Skip to content

Commit 69f3583

Browse files
committed
feat: address verification for apt
TICKET: WP-7081
1 parent 77e100a commit 69f3583

File tree

2 files changed

+62
-48
lines changed

2 files changed

+62
-48
lines changed

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

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import {
1414
PrebuildTransactionWithIntentOptions,
1515
SignedTransaction,
1616
SignTransactionOptions,
17-
VerifyAddressOptions,
17+
TssVerifyAddressOptions,
1818
VerifyTransactionOptions,
19+
verifyMPCWalletAddress,
1920
} from '@bitgo/sdk-core';
2021
import { BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics';
2122
import { KeyPair as AptKeyPair, TransactionBuilderFactory } from './lib';
@@ -30,6 +31,11 @@ export interface AptParseTransactionOptions extends ParseTransactionOptions {
3031
txHex: string;
3132
}
3233

34+
export interface TssVerifyAptAddressOptions extends TssVerifyAddressOptions {
35+
address: string;
36+
rootAddress?: string;
37+
}
38+
3339
export class Apt extends BaseCoin {
3440
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
3541
protected constructor(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>) {
@@ -120,12 +126,38 @@ export class Apt extends BaseCoin {
120126
return true;
121127
}
122128

123-
async isWalletAddress(params: VerifyAddressOptions): Promise<boolean> {
124-
const { address: newAddress } = params;
129+
/**
130+
* Verify that an address belongs to this wallet.
131+
*
132+
* @param {TssVerifyAptAddressOptions} params - Verification parameters
133+
* @returns {Promise<boolean>} True if address belongs to wallet
134+
* @throws {InvalidAddressError} If address format is invalid or doesn't match derived address
135+
*/
136+
async isWalletAddress(params: TssVerifyAptAddressOptions): Promise<boolean> {
137+
const { address, rootAddress } = params;
125138

126-
if (!this.isValidAddress(newAddress)) {
127-
throw new InvalidAddressError(`invalid address: ${newAddress}`);
139+
if (!this.isValidAddress(address)) {
140+
throw new InvalidAddressError(`invalid address: ${address}`);
128141
}
142+
143+
const isVerifyingRootAddress = rootAddress && address.toLowerCase() === rootAddress.toLowerCase();
144+
if (isVerifyingRootAddress) {
145+
const index = typeof params.index === 'string' ? parseInt(params.index, 10) : params.index;
146+
if (index !== 0) {
147+
throw new Error(`Root address verification requires index 0, but got index ${params.index}.`);
148+
}
149+
}
150+
151+
const result = await verifyMPCWalletAddress(
152+
{ ...params, keyCurve: 'ed25519' },
153+
this.isValidAddress.bind(this),
154+
(pubKey) => utils.getAddressFromPublicKey(pubKey)
155+
);
156+
157+
if (!result) {
158+
throw new InvalidAddressError(`invalid address: ${address}`);
159+
}
160+
129161
return true;
130162
}
131163

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

Lines changed: 25 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -280,42 +280,20 @@ describe('APT:', function () {
280280
});
281281

282282
describe('Address Validation', () => {
283-
let keychains;
284-
let commonKeychain;
285-
286-
before(function () {
287-
commonKeychain =
288-
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781';
289-
keychains = [
290-
{
291-
id: '6424c353eaf78d000766e95949868468',
292-
source: 'user',
293-
type: 'tss',
294-
commonKeychain:
295-
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
296-
encryptedPrv:
297-
'{"iv":"cZd5i7L4RxtwrALW2rK7UA==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"5zgoH1Bd3Fw=","ct":"9vVlnXFRtrM9FVEo+d2chbGHlM9lFZemueBuAs3BIkPo33Fo7jzwwNK/kIWkEyg+NmEBd5IaqAS157nvvvwzzsmMWlQdUz9qbmXNv3pg987cXFR08exS+4uhwP1YNOjJTRvRNcO9ZqHb46d4fmyJ/yC9/susCge7r/EsbaN5C3afv1dzybuq912FwaQElZLYYp5BICudFOMZ9k0UDMfKM/PMDkH7WexoGHr9GKq/bgCH2B39TZZyHKU6Uy47lXep2s6h0DrMwHOrnmiL3DZjOj88Ynvphlzxuo4eOlD2UHia2+nvIaISYs29Pr0DAvREutchvcBpExj1kWWPv7hQYrv8F0NAdatsbWl3w+xKyfiMKo1USlrwyJviypGtQtXOJyw0XPN0rv2+L5lW8BbjpzHfYYN13fJTedlGTFhhkzVtbbPAKE02kx7zCJcjYaiexdSTsrDLScYNT9/Jhdt27KpsooehwVohLfSKz4vbFfRu2MPZw3/+c/hfiJNgtz6esWbnxGrcE8U2IwPYCaK+Ghk4DcqWNIni59RI5B5kAsQOToII40qPN510uTgxBSPO7q7MHgkxdd4CqBq+ojr9j0P7oao8E5Y+CBDJrojDoCh1oCCDW9vo2dXlVcD8SIbw7U/9AfvEbA4xyE/5md1M7CIwLnWs2Ynv0YtaKoqhdS9x6FmHlMDhN/DKHinrwmowtrTT82fOkpO5g9saSmgU7Qy3gLt8t+VwdEyeFeQUKRSyci8qgqXQaZIg4+aXgaSOnlCFMtmB8ekYxEhTY5uzRfrNgS4s1QeqFBpNtUF+Ydi297pbVXnJoXAN+SVWd80GCx+yI2dpVC89k3rOWK9WeyqlnzuLJWp2RIOB9cdW8GFv/fN+QAJpYeVxOE4+nZDsKnsj8nKcg9t4Dlx1G6gLM1/Vq9YxNLbuzuRC0asUYvdMnoMvszmpm++TxndYisgNYscpZSoz7wvcazJNEPfhPVjEkd6tUUuN4GM35H0DmKCUQNT+a6B6hmHlTZvjxiyGAg5bY59hdjvJ+22QduazlEEC6LI3HrA7uK0TpplWzS1tCIFvTMUhj65DEZmNJ2+ZY9bQ4vsMf+DRR3OOG4t+DMlNfjOd3zNv3QoY95BjfWpryFwPzDq7bCP67JDsoj7j2TY5FRSrRkD77H0Ewlux2cWfjRTwcMHcdQxxuV0OP0aNjGDjybFN"}',
298-
},
299-
{
300-
id: '6424c353eaf78d000766e96137d4404b',
301-
source: 'backup',
302-
type: 'tss',
303-
commonKeychain:
304-
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
305-
encryptedPrv:
306-
'{"iv":"vi0dPef/Rx7kG/pRySQi6Q==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"9efhQsiEvVs=","ct":"Gw6atvf6gxKzsjtl3xseipO3rAxp1mAz7Yu1ihFsi5/lf2vMZegApgZx+pyILFS9KKLHbNF3U6WgSYdrr2t4vzdLsXkH1WIxfHS+cd2C5N59yADZDnPJBT6pv/IRvaYelP0Ck3nIYQ2hSMm8op+VOWC/SzHeh7slYDqwEHTGan0Wigfvk1yRd7CCJTaEAomnc/4eFi2NY3X3gt/3opy9IAgknnwUFohn96EWpEQ0F6pbzH/Z8VF6gF+DUcrrByAxExUPnHQZiFk3YHU/vVV4FxBU/mVAE8xBsBn5ul5e5SUMPfc7TBuJWv4BByTNg9xDShF/91Yx2nbfUm5d9QmM8lpKgzzQvcK8POAPk87gRCuKnsGh5vNS0UppkHc+ocfzRQlGA6jze7QyyQO0rMj5Ly8kWjwk2vISvKYHYS1NR7VU549UIXo7NXjatunKSc3+IreoRUHIshiaLg6hl+pxCCuc0qQ43V0mdIfCjTN8gkGWLNk8R7tAGPz9jyapQPcPEGHgEz0ATIi6yMNWCsibS2eLiE1uVEJONoM4lk6FPl3Q2CHbW2MeEbqjY8hbaw18mNb2xSBH/Fwpiial+Tvi2imqgnCO4ZpO9bllKftZPcQy0stN+eGBlb5ufyflKkDSiChHYroGjEpmiFicdde48cJszF52uKNnf1q67fA9/S2FAHQab3EXojxH2Gbk+kkV2h/TYKFFZSWC3vi4e8mO+vjMUcR0AdsgPFyEIz0SCGuba3CnTLNdEuZwsauAeHkx2vUTnRgJPVgNeeuXmsVG76Sy2ggJHuals0Hj8U2Xda0qO1RuFfoCWfss9wn6HGRwPPkhSB/8oNguAqmRVGKkd8Zwt3IvrTd9fk0/rFFDJKGz7WyNHkYgUmNiGcItD12v0jx7FZ52EJzl3Av1RyJUQK18+8EYPh3SGiU9dt7VX0aF0uo6JouKhOeldUvMP+AugQz8fUclwTQsbboVg27Yxo0DyATVwThW5a56R6Qf5ZiQJluFuzs5y98rq0S5q046lE6o3vVmJpEdwjeSCJoET5CL4nTgkXyWvhm4eB8u/e66l3o0qbaSx8q9YYmT9EpRcl5TP4ThLBKETYdzVvg4exjQfektMatk5EyUpEIhZPXh5vXpJZesdfO9LJ8zTaHBsBjDPU7cdNgQMbebpataRi8A0el2/IJXl+E+olgAz5zC4i2O1Q=="}',
307-
},
308-
{
309-
id: '6424c353eaf78d000766e9510b125fba',
310-
source: 'bitgo',
311-
type: 'tss',
312-
commonKeychain:
313-
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
314-
verifiedVssProof: true,
315-
isBitGo: true,
316-
},
317-
];
318-
});
283+
// Real test data from tapt wallet 692f53f35a6e3f25a18c4eb8af016fdd
284+
const testData = {
285+
commonKeychain:
286+
'6a724c11eafea4209704c35e6ee3e1fba80d2a40860d873bbe5981de636c9cf6ade77e6fdd4388889ee93d7eaa737ab584edb57cc0cc15b2899380348d6e482c',
287+
rootAddress: '0x0598b31aa77176dbaba25306404aa8131218068df58bf0b7eec13f57053fd5a7',
288+
receiveAddress: '0xdc169725dd6d9a07ee255b17087b4079e8a80850c895c65b62c0ef6de740d37a',
289+
receiveAddressIndex: 1,
290+
};
291+
292+
const keychains = [
293+
{ commonKeychain: testData.commonKeychain },
294+
{ commonKeychain: testData.commonKeychain },
295+
{ commonKeychain: testData.commonKeychain },
296+
];
319297

320298
it('should return true when validating a well formatted address prefixed with 0x', async function () {
321299
const address = '0xf941ae3cbe5645dccc15da8346b533f7f91f202089a5521653c062b2ff10b304';
@@ -332,27 +310,31 @@ describe('APT:', function () {
332310
basecoin.isValidAddress(address).should.equal(false);
333311
});
334312

335-
it('should return true for isWalletAddress with valid address for index 4', async function () {
336-
const newAddress = '0x8b3c7807730d75792dd6c49732cf9f014d6984a9c77d386bdb1072a9e537d8d8';
337-
const index = 4;
313+
it('should verify a valid receive address', async function () {
314+
const params = {
315+
address: testData.receiveAddress,
316+
rootAddress: testData.rootAddress,
317+
keychains,
318+
index: testData.receiveAddressIndex,
319+
};
338320

339-
const params = { commonKeychain, address: newAddress, index, keychains };
340-
(await basecoin.isWalletAddress(params)).should.equal(true);
321+
const result = await basecoin.isWalletAddress(params);
322+
result.should.equal(true);
341323
});
342324

343325
it('should throw error for isWalletAddress when keychains is missing', async function () {
344326
const address = '0x2959bfc3fdb7dc23fed8deba2fafb70f3e606a59';
345327
const index = 0;
346328

347-
const params = { commonKeychain, address, index };
329+
const params = { commonKeychain: testData.commonKeychain, address, index };
348330
await assert.rejects(async () => basecoin.isWalletAddress(params));
349331
});
350332

351333
it('should throw error for isWalletAddress when new address is invalid', async function () {
352334
const wrongAddress = 'badAddress';
353335
const index = 0;
354336

355-
const params = { commonKeychain, address: wrongAddress, index };
337+
const params = { commonKeychain: testData.commonKeychain, address: wrongAddress, index };
356338
await assert.rejects(async () => basecoin.isWalletAddress(params), {
357339
message: `invalid address: ${wrongAddress}`,
358340
});

0 commit comments

Comments
 (0)