Skip to content

Commit f8db801

Browse files
committed
test(sdk-coin-rune): add tests for wallet recovery
Ticket: COIN-2417
1 parent 6bcb1c6 commit f8db801

File tree

4 files changed

+138
-3
lines changed

4 files changed

+138
-3
lines changed

modules/sdk-coin-rune/src/rune.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class Rune extends CosmosCoin {
4242

4343
/** @inheritDoc **/
4444
protected getPublicNodeUrl(): string {
45-
return Environments[this.bitgo.getEnv()].coreumNodeUrl;
45+
return Environments[this.bitgo.getEnv()].runeNodeUrl;
4646
}
4747

4848
/** @inheritDoc **/

modules/sdk-coin-rune/test/resources/trune.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,15 @@ export const testnetCoinAmounts = {
9393
amount4: { amount: '-1', denom: 'rune' },
9494
amount5: { amount: '1000000000', denom: 'arune' },
9595
};
96+
97+
export const wrwUser = {
98+
senderAddress: 'sthor1gqek8kl5mr9c37rl8yp2mljne67jgaqm96jg2f',
99+
destinationAddress: 'sthor1tl46dxfh3qupqxgap5vtpah4pxwn35j5tnjzls',
100+
userPrivateKey:
101+
'{"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="}',
102+
backupPrivateKey:
103+
'{"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=="}',
104+
bitgoPublicKey:
105+
'02d754ffd645ba3d87fbdeb3578dbc22fc252ebb299416434a7059d9638f52d11e0fd153d9d dfb2a193531ac5b7d3b223188a957de1312d3ede35305ee70b76376',
106+
walletPassphrase: 'Ghghjkg!455544llll',
107+
};

modules/sdk-coin-rune/test/unit/rune.ts

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import { BitGoAPI } from '@bitgo/sdk-api';
22
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
3-
import { NetworkType } from '@bitgo/statics';
3+
import { coins, NetworkType } from '@bitgo/statics';
44
import BigNumber from 'bignumber.js';
55
import sinon from 'sinon';
66
import { Rune, Trune } from '../../src';
77
import { RuneUtils } from '../../src/lib/utils';
88
import { mainnetAddress } from '../resources/rune';
9-
import { TEST_SEND_TX, TEST_TX_WITH_MEMO, testnetAddress } from '../resources/trune';
9+
import { TEST_SEND_TX, TEST_TX_WITH_MEMO, testnetAddress, wrwUser } from '../resources/trune';
1010
const bech32 = require('bech32-buffer');
1111
import should = require('should');
12+
import { beforeEach } from 'mocha';
13+
import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos';
14+
import { GAS_AMOUNT } from '../../src/lib/constants';
1215

1316
describe('Rune', function () {
1417
let bitgo: TestBitGoAPI;
@@ -265,4 +268,121 @@ describe('Rune', function () {
265268
stub.restore();
266269
});
267270
});
271+
272+
describe('Recover transaction: success path', () => {
273+
const sandBox = sinon.createSandbox();
274+
const coin = coins.get('tthorchain:rune');
275+
const testBalance = '1500000';
276+
const testAccountNumber = '123';
277+
const testSequenceNumber = '0';
278+
const testChainId = 'thorchain-stagenet-2';
279+
280+
beforeEach(() => {
281+
const accountBalance = sandBox.stub(Trune.prototype, 'getAccountBalance' as keyof Trune);
282+
accountBalance.withArgs(wrwUser.senderAddress).resolves(testBalance);
283+
284+
const accountDetails = sandBox.stub(Trune.prototype, 'getAccountDetails' as keyof Trune);
285+
accountDetails.withArgs(wrwUser.senderAddress).resolves([testAccountNumber, testSequenceNumber]);
286+
287+
const chainId = sandBox.stub(Trune.prototype, 'getChainId' as keyof Trune);
288+
chainId.withArgs().resolves(testChainId);
289+
});
290+
291+
afterEach(() => {
292+
sandBox.restore();
293+
sinon.restore();
294+
});
295+
296+
it('should recover funds for non-bitgo recoveries', async function () {
297+
const res = await trune.recover({
298+
userKey: wrwUser.userPrivateKey,
299+
backupKey: wrwUser.backupPrivateKey,
300+
bitgoKey: wrwUser.bitgoPublicKey,
301+
walletPassphrase: wrwUser.walletPassphrase,
302+
recoveryDestination: wrwUser.destinationAddress,
303+
});
304+
res.should.not.be.empty();
305+
res.should.hasOwnProperty('serializedTx');
306+
sandBox.assert.calledOnce(trune.getAccountBalance);
307+
sandBox.assert.calledOnce(trune.getAccountDetails);
308+
sandBox.assert.calledOnce(trune.getChainId);
309+
310+
const truneTxn = new CosmosTransaction(coin, testnetUtils);
311+
truneTxn.enrichTransactionDetailsFromRawTransaction(res.serializedTx);
312+
const truneTxnJson = truneTxn.toJson();
313+
const sendMessage = truneTxnJson.sendMessages[0].value as SendMessage;
314+
const balance = new BigNumber(testBalance);
315+
const actualBalance = balance.minus(new BigNumber(GAS_AMOUNT));
316+
should.equal(sendMessage.amount[0].amount, actualBalance.toFixed());
317+
});
318+
});
319+
320+
describe('Recover transaction: failure path', () => {
321+
const sandBox = sinon.createSandbox();
322+
const testZeroBalance = '0';
323+
const testAccountNumber = '123';
324+
const testSequenceNumber = '0';
325+
const testChainId = 'thorchain-stagenet-2';
326+
327+
beforeEach(() => {
328+
const accountBalance = sandBox.stub(Trune.prototype, 'getAccountBalance' as keyof Trune);
329+
accountBalance.withArgs(wrwUser.senderAddress).resolves(testZeroBalance);
330+
331+
const accountDetails = sandBox.stub(Trune.prototype, 'getAccountDetails' as keyof Trune);
332+
accountDetails.withArgs(wrwUser.senderAddress).resolves([testAccountNumber, testSequenceNumber]);
333+
334+
const chainId = sandBox.stub(Trune.prototype, 'getChainId' as keyof Trune);
335+
chainId.withArgs().resolves(testChainId);
336+
});
337+
338+
afterEach(() => {
339+
sandBox.restore();
340+
sinon.restore();
341+
});
342+
343+
it('should throw error if backupkey is not present', async function () {
344+
await trune
345+
.recover({
346+
userKey: wrwUser.userPrivateKey,
347+
bitgoKey: wrwUser.bitgoPublicKey,
348+
walletPassphrase: wrwUser.walletPassphrase,
349+
recoveryDestination: wrwUser.destinationAddress,
350+
})
351+
.should.rejectedWith('missing backupKey');
352+
});
353+
354+
it('should throw error if userkey is not present', async function () {
355+
await trune
356+
.recover({
357+
backupKey: wrwUser.backupPrivateKey,
358+
bitgoKey: wrwUser.bitgoPublicKey,
359+
walletPassphrase: wrwUser.walletPassphrase,
360+
recoveryDestination: wrwUser.destinationAddress,
361+
})
362+
.should.rejectedWith('missing userKey');
363+
});
364+
365+
it('should throw error if wallet passphrase is not present', async function () {
366+
await trune
367+
.recover({
368+
userKey: wrwUser.userPrivateKey,
369+
backupKey: wrwUser.backupPrivateKey,
370+
bitgoKey: wrwUser.bitgoPublicKey,
371+
recoveryDestination: wrwUser.destinationAddress,
372+
})
373+
.should.rejectedWith('missing wallet passphrase');
374+
});
375+
376+
it('should throw error if there is no balance', async function () {
377+
await trune
378+
.recover({
379+
userKey: wrwUser.userPrivateKey,
380+
backupKey: wrwUser.backupPrivateKey,
381+
bitgoKey: wrwUser.bitgoPublicKey,
382+
walletPassphrase: wrwUser.walletPassphrase,
383+
recoveryDestination: wrwUser.destinationAddress,
384+
})
385+
.should.rejectedWith('Did not have enough funds to recover');
386+
});
387+
});
268388
});

modules/sdk-core/src/bitgo/environments.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ interface EnvironmentTemplate {
4646
beraNodeUrl: string;
4747
zetaNodeUrl: string;
4848
coreumNodeUrl: string;
49+
runeNodeUrl: string;
4950
islmNodeUrl: string;
5051
dotNodeUrls: string[];
5152
tronNodes: {
@@ -142,6 +143,7 @@ const mainnetBase: EnvironmentTemplate = {
142143
beraNodeUrl: '', // TODO(WIN-693): update url when mainnet goes live
143144
zetaNodeUrl: 'https://zetachain.blockpi.network', // reference https://www.zetachain.com/docs/reference/api/
144145
coreumNodeUrl: 'https://full-node.mainnet-1.coreum.dev:1317',
146+
runeNodeUrl: 'https://thornode.ninerealms.com',
145147
islmNodeUrl: 'https://rest.cosmos.haqq.network',
146148
dotNodeUrls: ['wss://rpc.polkadot.io'],
147149
tronNodes: {
@@ -193,6 +195,7 @@ const testnetBase: EnvironmentTemplate = {
193195
beraNodeUrl: '', // TODO(WIN-693): update url when testnet goes live
194196
zetaNodeUrl: 'https://rest.nodejumper.io/zetachaintestnet', // reference : https://www.zetachain.com/docs/reference/api/
195197
coreumNodeUrl: 'https://full-node.testnet-1.coreum.dev:1317',
198+
runeNodeUrl: 'https://stagenet-thornode.ninerealms.com',
196199
islmNodeUrl: 'https://rest.cosmos.testedge2.haqq.network ',
197200
dotNodeUrls: ['wss://westend-rpc.polkadot.io'],
198201
tronNodes: {

0 commit comments

Comments
 (0)