Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(sdk-coin-rune): add tests for wallet recovery #5339

Merged
merged 1 commit into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/sdk-coin-rune/src/rune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class Rune extends CosmosCoin {

/** @inheritDoc **/
protected getPublicNodeUrl(): string {
return Environments[this.bitgo.getEnv()].coreumNodeUrl;
return Environments[this.bitgo.getEnv()].runeNodeUrl;
}

/** @inheritDoc **/
Expand Down
12 changes: 12 additions & 0 deletions modules/sdk-coin-rune/test/resources/trune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,15 @@ export const testnetCoinAmounts = {
amount4: { amount: '-1', denom: 'rune' },
amount5: { amount: '1000000000', denom: 'arune' },
};

export const wrwUser = {
senderAddress: 'sthor1gqek8kl5mr9c37rl8yp2mljne67jgaqm96jg2f',
destinationAddress: 'sthor1tl46dxfh3qupqxgap5vtpah4pxwn35j5tnjzls',
userPrivateKey:
'{"iv":"9rcHMytmmnHjG1llBoc49g==","v":1,"iter":10000,"ks":256,"ts":64,"mode" :"ccm","adata":"","cipher":"aes","salt":"8lFAwTrcCzQ=","ct":"1gMRC60nu01IYr jgir9GCuQNLMZC2vt/pFjgL+tV3K7/rviSguDsua133PkNEeuty1mZxNH2S1TKu1KxZJMlB+MAs vo7k9dIyCgQvkX/nbJFFaRrnLL55mcQe0PhCdVNRHx9doXMngRF+UqEMLD8F0HNE1+ZPGzsathH DLFPxLCRMrwA/Ss/LQowGiXo4WnzVK3MQcX6wdmZvfV+S3xswRjHkLvolVY3rVGP5PkU86Wwrmy e1CKM4hSGA8FModa718Svk7C+LHr0yOTuuPftXtH0fAPPo9KH7f+EbBWMJPQgjK8DW/z1xnDgLH dzhveyNPevqUqb3FEyutjow+KBkxl+xHqJ4gKcU9MLsghPMCy0zSq2kBbhlqWJ7XwduhHVGv2nT rlsEfh7Z0fbqZTX1RQ5AKZhSBFEqmq1hKxRlhVcr9KqBw814tIfdFD7nMgV7rz+3X8cJaK2tDXH 0QaY8bJGwxwH38FsfNevs8Qf2bx5VrD/Vw3wo0G5hJ7+TdPCrib8UWyxPGuruH7b8hwCUTZaq97 zkDp7c79BDWyH8ycTxQXjLPwb1CwpUiRDc6NHD2p3iGVaztMdQOMXmPeeMhBNGVI0MPAfSdcDip 2E5Ek0fwnI1mEuFjPMkc+VOlsVhlWfV/9Xpoh3NadPhqg3i9Cr2uNT/PSyHd/jpIqG5DlaaEvVD f7COqNvXX5Qqmlr9dL3R2cf0zP8TPImAlRfef4D8zatHtylKkImMc0JcdcXTzwU/5tf21o/Y701 DLYjwUUgqQ57ObHHNlSvmdIuO1rD5LAn96NAYNRStrENqmbibE5MsYrp9nPAprCNFnZR6UNpXwD hvq9gvxRG+PjmXGj0nuyR7cshxURLy0/p4tq7LkGjk4Gg31Mgk41lcYUW2tLuUbTYmpteu3Cdwe DFuLsfI1qZxKyr6V5Nse3ZAvnJLTZnQXiHOnc2y5/e53+MAVupXR7HZzx7SIJ+fqxpjxvccmgJC sWaPo+Lsx6KaSNkQGFMGuyZ87IEvm7vk2GyIP+FQUn4nmTdEDLzg7nNo+zf5WT4A6Yz3GNAa3Nh 2QHZSFGWcubOXDawh6lAjZ78GNK9yJDp22dJTmfZ+1TsVORpbZw="}',
backupPrivateKey:
'{"iv":"0EDqGU1yBE2PM4ry6TxARA==","v":1,"iter":10000,"ks":256,"ts":64,"mode" :"ccm","adata":"","cipher":"aes","salt":"a8T0fVVhTz0=","ct":"syCrJ+RD8oHZ4z jltLmyNS/VVmSLXLMAdnVnoPNEfFX9mIMAycYCajMR297l09fPsWWqDcDs0cSI0xNbnMjyW30b/ JOvQ33eaBSiyBr3vbmhaEFduZTUgOCNg/Js0cAkEkeE/KDkfOUB0HzfSEQb6m0eXmWgGSnySUOd TlZZ/thT7j2oPHVpPE52h6Yi3LS1Jkt3S7ySStXvG/4iurhcL32a32Fx3IQxs/bg8JBIGlRG644 aLttCJ+uPihaSISUGn1hh0iEz2ZAaCoCkq7oNPrK+pxHHOmbjxp6XpV67EmQ/JRfSt47eW3jC7A D+xaLLHspjQifp6ClZeHT2pBrkcWrZfr1uL5w3CKrXQeJ0pXUoRmJwGys4hm5Bzu4imANo+jd1+ wg73ByYQ5VwRhV1qhb82vUrQ0i4k5Gt2l6U2LZ/DMQiUbq7FdjYWqfZmk18GIZ6g3IPJtGjnA6x o8RTvkytMMv8btsx/aa6jE6EMeFtGzlhhJJunpcS4QG4IkIuyAjPafwOklEwXklZCLDpKdVYgTR RE0XHC0VXOGQwGUARzMZwQO5OaRyQkxtZ5/CawI5jrwMZP+bpC9dKzsGOXfSvvFsJWnkboXyy09 bFwTuKHx0ZILdA/8Xq5689YEmFljHTeQ0Es/q5y5kU+XzT4sjjgxz1qqhUJVh7wDwQevRDWTwGJ Lfi2AeLsv2f0PKiGTYJzQGSXUerhLMwhg3rXNfXGkrWSmEhJH/RsvF6CdaRKWxVd+k8K5ULLwK+ sdujA5murZ24KI5z0Yr9qW2TSsJRG3PS+ZH1q3VGYaz6k912QVzyvzds8+hfaHNtr6OGBWahGZo JchgnQHSfAZxOUz7K7WNzh934HK5+e11/lF/Fxssumz2OStFWI18oBB7/8YBi6PEp3sFddOQVSA BXRGuStolGna43nly4bavOic7XIoNHs61hynMeR6NcthsSrj5xZ6q36EqLdfkDImQvu+3BXClT7 Q4bdEfXm0t7Bgmid4NeJ1I3grXZMOmVbPgcypfxp2jrkjme/R0/eJL4B38WU2xjxDg+WlBGy8dA xg8ll+n2NkA7lvJz5NBDxbGHRAhtyVTF8YA+Oxps3rumIw=="}',
bitgoPublicKey:
'02d754ffd645ba3d87fbdeb3578dbc22fc252ebb299416434a7059d9638f52d11e0fd153d9d dfb2a193531ac5b7d3b223188a957de1312d3ede35305ee70b76376',
walletPassphrase: 'Ghghjkg!455544llll',
};
124 changes: 122 additions & 2 deletions modules/sdk-coin-rune/test/unit/rune.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { BitGoAPI } from '@bitgo/sdk-api';
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { NetworkType } from '@bitgo/statics';
import { coins, NetworkType } from '@bitgo/statics';
import BigNumber from 'bignumber.js';
import sinon from 'sinon';
import { Rune, Trune } from '../../src';
import { RuneUtils } from '../../src/lib/utils';
import { mainnetAddress } from '../resources/rune';
import { TEST_SEND_TX, TEST_TX_WITH_MEMO, testnetAddress } from '../resources/trune';
import { TEST_SEND_TX, TEST_TX_WITH_MEMO, testnetAddress, wrwUser } from '../resources/trune';
const bech32 = require('bech32-buffer');
import should = require('should');
import { beforeEach } from 'mocha';
import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos';
import { GAS_AMOUNT } from '../../src/lib/constants';

describe('Rune', function () {
let bitgo: TestBitGoAPI;
Expand Down Expand Up @@ -265,4 +268,121 @@ describe('Rune', function () {
stub.restore();
});
});

describe('Recover transaction: success path', () => {
const sandBox = sinon.createSandbox();
const coin = coins.get('tthorchain:rune');
const testBalance = '1500000';
const testAccountNumber = '123';
const testSequenceNumber = '0';
const testChainId = 'thorchain-stagenet-2';

beforeEach(() => {
const accountBalance = sandBox.stub(Trune.prototype, 'getAccountBalance' as keyof Trune);
accountBalance.withArgs(wrwUser.senderAddress).resolves(testBalance);

const accountDetails = sandBox.stub(Trune.prototype, 'getAccountDetails' as keyof Trune);
accountDetails.withArgs(wrwUser.senderAddress).resolves([testAccountNumber, testSequenceNumber]);

const chainId = sandBox.stub(Trune.prototype, 'getChainId' as keyof Trune);
chainId.withArgs().resolves(testChainId);
});

afterEach(() => {
sandBox.restore();
sinon.restore();
});

it('should recover funds for non-bitgo recoveries', async function () {
const res = await trune.recover({
userKey: wrwUser.userPrivateKey,
backupKey: wrwUser.backupPrivateKey,
bitgoKey: wrwUser.bitgoPublicKey,
walletPassphrase: wrwUser.walletPassphrase,
recoveryDestination: wrwUser.destinationAddress,
});
res.should.not.be.empty();
res.should.hasOwnProperty('serializedTx');
sandBox.assert.calledOnce(trune.getAccountBalance);
sandBox.assert.calledOnce(trune.getAccountDetails);
sandBox.assert.calledOnce(trune.getChainId);

const truneTxn = new CosmosTransaction(coin, testnetUtils);
truneTxn.enrichTransactionDetailsFromRawTransaction(res.serializedTx);
const truneTxnJson = truneTxn.toJson();
const sendMessage = truneTxnJson.sendMessages[0].value as SendMessage;
const balance = new BigNumber(testBalance);
const actualBalance = balance.minus(new BigNumber(GAS_AMOUNT));
should.equal(sendMessage.amount[0].amount, actualBalance.toFixed());
});
});

describe('Recover transaction: failure path', () => {
const sandBox = sinon.createSandbox();
const testZeroBalance = '0';
const testAccountNumber = '123';
const testSequenceNumber = '0';
const testChainId = 'thorchain-stagenet-2';

beforeEach(() => {
const accountBalance = sandBox.stub(Trune.prototype, 'getAccountBalance' as keyof Trune);
accountBalance.withArgs(wrwUser.senderAddress).resolves(testZeroBalance);

const accountDetails = sandBox.stub(Trune.prototype, 'getAccountDetails' as keyof Trune);
accountDetails.withArgs(wrwUser.senderAddress).resolves([testAccountNumber, testSequenceNumber]);

const chainId = sandBox.stub(Trune.prototype, 'getChainId' as keyof Trune);
chainId.withArgs().resolves(testChainId);
});

afterEach(() => {
sandBox.restore();
sinon.restore();
});

it('should throw error if backupkey is not present', async function () {
await trune
.recover({
userKey: wrwUser.userPrivateKey,
bitgoKey: wrwUser.bitgoPublicKey,
walletPassphrase: wrwUser.walletPassphrase,
recoveryDestination: wrwUser.destinationAddress,
})
.should.rejectedWith('missing backupKey');
});

it('should throw error if userkey is not present', async function () {
await trune
.recover({
backupKey: wrwUser.backupPrivateKey,
bitgoKey: wrwUser.bitgoPublicKey,
walletPassphrase: wrwUser.walletPassphrase,
recoveryDestination: wrwUser.destinationAddress,
})
.should.rejectedWith('missing userKey');
});

it('should throw error if wallet passphrase is not present', async function () {
await trune
.recover({
userKey: wrwUser.userPrivateKey,
backupKey: wrwUser.backupPrivateKey,
bitgoKey: wrwUser.bitgoPublicKey,
recoveryDestination: wrwUser.destinationAddress,
})
.should.rejectedWith('missing wallet passphrase');
});

it('should throw error if there is no balance', async function () {
await trune
.recover({
userKey: wrwUser.userPrivateKey,
backupKey: wrwUser.backupPrivateKey,
bitgoKey: wrwUser.bitgoPublicKey,
walletPassphrase: wrwUser.walletPassphrase,
recoveryDestination: wrwUser.destinationAddress,
})
.should.rejectedWith('Did not have enough funds to recover');
});
});
});
3 changes: 3 additions & 0 deletions modules/sdk-core/src/bitgo/environments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ interface EnvironmentTemplate {
beraNodeUrl: string;
zetaNodeUrl: string;
coreumNodeUrl: string;
runeNodeUrl: string;
islmNodeUrl: string;
dotNodeUrls: string[];
tronNodes: {
Expand Down Expand Up @@ -142,6 +143,7 @@ const mainnetBase: EnvironmentTemplate = {
beraNodeUrl: '', // TODO(WIN-693): update url when mainnet goes live
zetaNodeUrl: 'https://zetachain.blockpi.network', // reference https://www.zetachain.com/docs/reference/api/
coreumNodeUrl: 'https://full-node.mainnet-1.coreum.dev:1317',
runeNodeUrl: 'https://thornode.ninerealms.com',
islmNodeUrl: 'https://rest.cosmos.haqq.network',
dotNodeUrls: ['wss://rpc.polkadot.io'],
tronNodes: {
Expand Down Expand Up @@ -193,6 +195,7 @@ const testnetBase: EnvironmentTemplate = {
beraNodeUrl: '', // TODO(WIN-693): update url when testnet goes live
zetaNodeUrl: 'https://rest.nodejumper.io/zetachaintestnet', // reference : https://www.zetachain.com/docs/reference/api/
coreumNodeUrl: 'https://full-node.testnet-1.coreum.dev:1317',
runeNodeUrl: 'https://stagenet-thornode.ninerealms.com',
islmNodeUrl: 'https://rest.cosmos.testedge2.haqq.network ',
dotNodeUrls: ['wss://westend-rpc.polkadot.io'],
tronNodes: {
Expand Down
Loading