Skip to content

Commit 895fd55

Browse files
Wp 6461 fix address verification during creation
2 parents da43c74 + 21302c3 commit 895fd55

File tree

2 files changed

+300
-1
lines changed

2 files changed

+300
-1
lines changed

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

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,6 +1299,302 @@ describe('ETH:', function () {
12991299
});
13001300
});
13011301

1302+
describe('Address Creation', function () {
1303+
it('should pass walletVersion 6 to isWalletAddress during address creation', async function () {
1304+
const bgUrl = common.Environments[bitgo.getEnv()].uri;
1305+
const ethCoin = bitgo.coin('hteth') as Hteth;
1306+
const walletDataV6 = {
1307+
id: '598f606cd8fc24710d2ebadb1d9459bb',
1308+
coinSpecific: {
1309+
baseAddress: '0xdf07117705a9f8dc4c2a78de66b7f1797dba9d4e',
1310+
walletVersion: 6,
1311+
},
1312+
coin: 'hteth',
1313+
keys: [
1314+
'598f606cd8fc24710d2ebad89dce86c2',
1315+
'598f606cc8e43aef09fcb785221d9dd2',
1316+
'5935d59cf660764331bafcade1855fd7',
1317+
],
1318+
receiveAddress: {
1319+
address: '0xdf07117705a9f8dc4c2a78de66b7f1797dba9d4e',
1320+
},
1321+
};
1322+
const ethWalletV6 = new Wallet(bitgo, ethCoin, walletDataV6);
1323+
const isWalletAddressSpy = sinon.spy(ethCoin, 'isWalletAddress');
1324+
1325+
// Mock keychain requests
1326+
nock(bgUrl).get(`/api/v2/hteth/key/598f606cd8fc24710d2ebad89dce86c2`).reply(200, {
1327+
id: '598f606cd8fc24710d2ebad89dce86c2',
1328+
pub: 'xpub661MyMwAqRbcFXDcWD2vxuebcT1ZpTF4Vke6qmMW8yzddwNYpAPjvYEEL5jLfyYXW2fuxtAxY8TgjPUJLcf1C8qz9N6VgZxArKX4EwB8rH5',
1329+
commonKeychain:
1330+
'033b02aac4f038fef5118350b77d302ec6202931ca2e7122aad88994ffefcbc70a6069e662436236abb1619195232c41580204cb202c22357ed8f53e69eac5c69e',
1331+
source: 'user',
1332+
type: 'tss',
1333+
});
1334+
nock(bgUrl).get(`/api/v2/hteth/key/598f606cc8e43aef09fcb785221d9dd2`).reply(200, {
1335+
id: '598f606cc8e43aef09fcb785221d9dd2',
1336+
pub: 'xpub661MyMwAqRbcGhSaXikpuTC9KU88Xx9LrjKSw1JKsvXNgabpTdgjy7LSovh9ZHhcqhAHQu7uthu7FguNGdcC4aXTKK5gqTcPe4WvLYRbCSG',
1337+
commonKeychain:
1338+
'033b02aac4f038fef5118350b77d302ec6202931ca2e7122aad88994ffefcbc70a6069e662436236abb1619195232c41580204cb202c22357ed8f53e69eac5c69e',
1339+
source: 'backup',
1340+
type: 'tss',
1341+
});
1342+
nock(bgUrl).get(`/api/v2/hteth/key/5935d59cf660764331bafcade1855fd7`).reply(200, {
1343+
id: '5935d59cf660764331bafcade1855fd7',
1344+
pub: 'xpub661MyMwAqRbcFsXShW8R3hJsHNTYTUwzcejnLkY7KCtaJbDqcGkcBF99BrEJSjNZHeHveiYUrsAdwnjUMGwpgmEbiKcZWRuVA9HxnRaA3r3',
1345+
commonKeychain:
1346+
'033b02aac4f038fef5118350b77d302ec6202931ca2e7122aad88994ffefcbc70a6069e662436236abb1619195232c41580204cb202c22357ed8f53e69eac5c69e',
1347+
source: 'bitgo',
1348+
type: 'tss',
1349+
});
1350+
1351+
// Mock address creation API
1352+
nock(bgUrl)
1353+
.post(`/api/v2/hteth/wallet/${ethWalletV6.id()}/address`)
1354+
.reply(200, {
1355+
id: '638a48c6c3dba40007a3497fa49a080c',
1356+
address: '0xc012041dac143a59fa491db3a2b67b69bd78b685',
1357+
chain: 0,
1358+
index: 0,
1359+
coin: 'hteth',
1360+
wallet: ethWalletV6.id(),
1361+
coinSpecific: {
1362+
forwarderVersion: 4,
1363+
salt: '0x0',
1364+
feeAddress: '0xb1e725186990b86ca8efed08a3ccda9c9f400f09',
1365+
},
1366+
});
1367+
1368+
await ethWalletV6.createAddress({ chain: 0 });
1369+
1370+
isWalletAddressSpy.calledOnce.should.be.true();
1371+
const calledParams = isWalletAddressSpy.firstCall.args[0];
1372+
calledParams.should.have.property('walletVersion', 6);
1373+
});
1374+
1375+
it('should pass walletVersion 5 to isWalletAddress during address creation', async function () {
1376+
const bgUrl = common.Environments[bitgo.getEnv()].uri;
1377+
const ethCoin = bitgo.coin('hteth') as Hteth;
1378+
const walletDataV5 = {
1379+
id: '598f606cd8fc24710d2ebadb1d9459bb',
1380+
coinSpecific: {
1381+
baseAddress: '0xf1e3d30798acdf3a12fa5beb5fad8efb23d5be11',
1382+
walletVersion: 5,
1383+
},
1384+
coin: 'hteth',
1385+
keys: [
1386+
'598f606cd8fc24710d2ebad89dce86c2',
1387+
'598f606cc8e43aef09fcb785221d9dd2',
1388+
'5935d59cf660764331bafcade1855fd7',
1389+
],
1390+
receiveAddress: {
1391+
address: '0xf1e3d30798acdf3a12fa5beb5fad8efb23d5be11',
1392+
},
1393+
};
1394+
const ethWalletV5 = new Wallet(bitgo, ethCoin, walletDataV5);
1395+
const isWalletAddressSpy = sinon.spy(ethCoin, 'isWalletAddress');
1396+
1397+
// Mock keychain requests
1398+
nock(bgUrl).get(`/api/v2/hteth/key/598f606cd8fc24710d2ebad89dce86c2`).reply(200, {
1399+
id: '598f606cd8fc24710d2ebad89dce86c2',
1400+
pub: 'xpub661MyMwAqRbcFXDcWD2vxuebcT1ZpTF4Vke6qmMW8yzddwNYpAPjvYEEL5jLfyYXW2fuxtAxY8TgjPUJLcf1C8qz9N6VgZxArKX4EwB8rH5',
1401+
commonKeychain:
1402+
'02c8a496b16abfe2567520a279e2154642fc3c0e08e629775cb4d845c0c5fbf55ab7ba153e886de65748ed18f4ff8f5cee2242e687399ea3297a1f5524fdefd56c',
1403+
source: 'user',
1404+
type: 'tss',
1405+
});
1406+
nock(bgUrl).get(`/api/v2/hteth/key/598f606cc8e43aef09fcb785221d9dd2`).reply(200, {
1407+
id: '598f606cc8e43aef09fcb785221d9dd2',
1408+
pub: 'xpub661MyMwAqRbcGhSaXikpuTC9KU88Xx9LrjKSw1JKsvXNgabpTdgjy7LSovh9ZHhcqhAHQu7uthu7FguNGdcC4aXTKK5gqTcPe4WvLYRbCSG',
1409+
commonKeychain:
1410+
'02c8a496b16abfe2567520a279e2154642fc3c0e08e629775cb4d845c0c5fbf55ab7ba153e886de65748ed18f4ff8f5cee2242e687399ea3297a1f5524fdefd56c',
1411+
source: 'backup',
1412+
type: 'tss',
1413+
});
1414+
nock(bgUrl).get(`/api/v2/hteth/key/5935d59cf660764331bafcade1855fd7`).reply(200, {
1415+
id: '5935d59cf660764331bafcade1855fd7',
1416+
pub: 'xpub661MyMwAqRbcFsXShW8R3hJsHNTYTUwzcejnLkY7KCtaJbDqcGkcBF99BrEJSjNZHeHveiYUrsAdwnjUMGwpgmEbiKcZWRuVA9HxnRaA3r3',
1417+
commonKeychain:
1418+
'02c8a496b16abfe2567520a279e2154642fc3c0e08e629775cb4d845c0c5fbf55ab7ba153e886de65748ed18f4ff8f5cee2242e687399ea3297a1f5524fdefd56c',
1419+
source: 'bitgo',
1420+
type: 'tss',
1421+
});
1422+
1423+
// Mock address creation API
1424+
nock(bgUrl)
1425+
.post(`/api/v2/hteth/wallet/${ethWalletV5.id()}/address`)
1426+
.reply(200, {
1427+
id: '638a48c6c3dba40007a3497fa49a080c',
1428+
address: '0xd63b5e2b8d1b4fba3625460508900bf2a0499a4d',
1429+
chain: 0,
1430+
index: 117,
1431+
coin: 'hteth',
1432+
wallet: ethWalletV5.id(),
1433+
coinSpecific: {
1434+
forwarderVersion: 4,
1435+
salt: '0x75',
1436+
feeAddress: '0xb1e725186990b86ca8efed08a3ccda9c9f400f09',
1437+
},
1438+
});
1439+
1440+
await ethWalletV5.createAddress({ chain: 0 });
1441+
1442+
// Verify isWalletAddress was called with walletVersion 5
1443+
isWalletAddressSpy.calledOnce.should.be.true();
1444+
const calledParams = isWalletAddressSpy.firstCall.args[0];
1445+
calledParams.should.have.property('walletVersion', 5);
1446+
});
1447+
1448+
it('should pass walletVersion 2 to isWalletAddress during address creation', async function () {
1449+
const bgUrl = common.Environments[bitgo.getEnv()].uri;
1450+
const ethCoin = bitgo.coin('hteth') as Hteth;
1451+
const walletDataV2 = {
1452+
id: '598f606cd8fc24710d2ebadb1d9459bb',
1453+
coinSpecific: {
1454+
baseAddress: '0xdc485da076ed4a2b19584e9a1fdbb974f89b60f4',
1455+
walletVersion: 2,
1456+
},
1457+
coin: 'hteth',
1458+
keys: [
1459+
'598f606cd8fc24710d2ebad89dce86c2',
1460+
'598f606cc8e43aef09fcb785221d9dd2',
1461+
'5935d59cf660764331bafcade1855fd7',
1462+
],
1463+
receiveAddress: {
1464+
address: '0xdc485da076ed4a2b19584e9a1fdbb974f89b60f4',
1465+
},
1466+
};
1467+
const ethWalletV2 = new Wallet(bitgo, ethCoin, walletDataV2);
1468+
const isWalletAddressSpy = sinon.spy(ethCoin, 'isWalletAddress');
1469+
1470+
// Mock keychain requests
1471+
nock(bgUrl).get(`/api/v2/hteth/key/598f606cd8fc24710d2ebad89dce86c2`).reply(200, {
1472+
id: '598f606cd8fc24710d2ebad89dce86c2',
1473+
pub: 'xpub661MyMwAqRbcGrCxCX39zb3TvYjTqfUGwEUZHjnraRFm1WeMw9gfCD1wwc2wUDmBBZ2TkccJMwf5eBTja8r3z6HMxoTZGW6nvyoJMQFsecv',
1474+
ethAddress: '0x9d16bb867b792c5e3bf636a0275f2db8601bd7d4',
1475+
source: 'user',
1476+
type: 'independent',
1477+
});
1478+
nock(bgUrl).get(`/api/v2/hteth/key/598f606cc8e43aef09fcb785221d9dd2`).reply(200, {
1479+
id: '598f606cc8e43aef09fcb785221d9dd2',
1480+
pub: 'xpub661MyMwAqRbcGKhdeC4nr1ta8d27xThtfFFHgbxWMrVb595meMS8i3fBMrTz8EdQMWBKHHKzxapGgheoMymVvRcQmaGDykRTBbtXqbiu9ps',
1481+
ethAddress: '0x2dfce5cfeb5c03fbe680cd39ac0d2b25399b7d22',
1482+
source: 'backup',
1483+
type: 'independent',
1484+
});
1485+
nock(bgUrl).get(`/api/v2/hteth/key/5935d59cf660764331bafcade1855fd7`).reply(200, {
1486+
id: '5935d59cf660764331bafcade1855fd7',
1487+
pub: 'xpub661MyMwAqRbcGzTn5eyNGDkb18R43nH79HokYLc5PXZM19V8UrbuLdVRaCQMs4EeCAjnqmoYXqfyusTU46WoZMDyLpmTzoUX66ZBwGFjt1a',
1488+
ethAddress: '0xb1e725186990b86ca8efed08a3ccda9c9f400f09',
1489+
source: 'bitgo',
1490+
type: 'independent',
1491+
});
1492+
1493+
// Mock address creation API
1494+
nock(bgUrl)
1495+
.post(`/api/v2/hteth/wallet/${ethWalletV2.id()}/address`)
1496+
.reply(200, {
1497+
id: '638a48c6c3dba40007a3497fa49a080c',
1498+
address: '0xf636ceddffe41d106586875c0e56dc8feb6268f7',
1499+
chain: 0,
1500+
index: 23,
1501+
coin: 'hteth',
1502+
wallet: ethWalletV2.id(),
1503+
coinSpecific: {
1504+
forwarderVersion: 2,
1505+
salt: '0x17',
1506+
},
1507+
});
1508+
1509+
await ethWalletV2.createAddress({ chain: 0 });
1510+
1511+
// Verify isWalletAddress was called with walletVersion 2
1512+
isWalletAddressSpy.calledOnce.should.be.true();
1513+
const calledParams = isWalletAddressSpy.firstCall.args[0];
1514+
calledParams.should.have.property('walletVersion', 2);
1515+
});
1516+
1517+
it('should fail v6 address creation if walletVersion was not passed (simulates old bug)', async function () {
1518+
const bgUrl = common.Environments[bitgo.getEnv()].uri;
1519+
const ethCoin = bitgo.coin('hteth') as Hteth;
1520+
const walletDataV6 = {
1521+
id: '598f606cd8fc24710d2ebadb1d9459bb',
1522+
coinSpecific: {
1523+
baseAddress: '0xdf07117705a9f8dc4c2a78de66b7f1797dba9d4e',
1524+
walletVersion: 6,
1525+
},
1526+
coin: 'hteth',
1527+
keys: [
1528+
'598f606cd8fc24710d2ebad89dce86c2',
1529+
'598f606cc8e43aef09fcb785221d9dd2',
1530+
'5935d59cf660764331bafcade1855fd7',
1531+
],
1532+
receiveAddress: {
1533+
address: '0xdf07117705a9f8dc4c2a78de66b7f1797dba9d4e',
1534+
},
1535+
};
1536+
const ethWalletV6 = new Wallet(bitgo, ethCoin, walletDataV6);
1537+
1538+
// Stub isWalletAddress to simulate the OLD bug where walletVersion was not passed
1539+
const originalIsWalletAddress = ethCoin.isWalletAddress.bind(ethCoin);
1540+
const isWalletAddressStub = sinon.stub(ethCoin, 'isWalletAddress').callsFake(async (params) => {
1541+
// Remove walletVersion to simulate the old bug
1542+
const paramsWithoutWalletVersion = { ...params };
1543+
delete (paramsWithoutWalletVersion as any).walletVersion;
1544+
return originalIsWalletAddress(paramsWithoutWalletVersion);
1545+
});
1546+
1547+
// Mock keychain requests
1548+
nock(bgUrl).get(`/api/v2/hteth/key/598f606cd8fc24710d2ebad89dce86c2`).reply(200, {
1549+
id: '598f606cd8fc24710d2ebad89dce86c2',
1550+
pub: 'xpub661MyMwAqRbcFXDcWD2vxuebcT1ZpTF4Vke6qmMW8yzddwNYpAPjvYEEL5jLfyYXW2fuxtAxY8TgjPUJLcf1C8qz9N6VgZxArKX4EwB8rH5',
1551+
commonKeychain:
1552+
'033b02aac4f038fef5118350b77d302ec6202931ca2e7122aad88994ffefcbc70a6069e662436236abb1619195232c41580204cb202c22357ed8f53e69eac5c69e',
1553+
source: 'user',
1554+
type: 'tss',
1555+
});
1556+
nock(bgUrl).get(`/api/v2/hteth/key/598f606cc8e43aef09fcb785221d9dd2`).reply(200, {
1557+
id: '598f606cc8e43aef09fcb785221d9dd2',
1558+
pub: 'xpub661MyMwAqRbcGhSaXikpuTC9KU88Xx9LrjKSw1JKsvXNgabpTdgjy7LSovh9ZHhcqhAHQu7uthu7FguNGdcC4aXTKK5gqTcPe4WvLYRbCSG',
1559+
commonKeychain:
1560+
'033b02aac4f038fef5118350b77d302ec6202931ca2e7122aad88994ffefcbc70a6069e662436236abb1619195232c41580204cb202c22357ed8f53e69eac5c69e',
1561+
source: 'backup',
1562+
type: 'tss',
1563+
});
1564+
nock(bgUrl).get(`/api/v2/hteth/key/5935d59cf660764331bafcade1855fd7`).reply(200, {
1565+
id: '5935d59cf660764331bafcade1855fd7',
1566+
pub: 'xpub661MyMwAqRbcFsXShW8R3hJsHNTYTUwzcejnLkY7KCtaJbDqcGkcBF99BrEJSjNZHeHveiYUrsAdwnjUMGwpgmEbiKcZWRuVA9HxnRaA3r3',
1567+
commonKeychain:
1568+
'033b02aac4f038fef5118350b77d302ec6202931ca2e7122aad88994ffefcbc70a6069e662436236abb1619195232c41580204cb202c22357ed8f53e69eac5c69e',
1569+
source: 'bitgo',
1570+
type: 'tss',
1571+
});
1572+
1573+
// Mock address creation API
1574+
nock(bgUrl)
1575+
.post(`/api/v2/hteth/wallet/${ethWalletV6.id()}/address`)
1576+
.reply(200, {
1577+
id: '638a48c6c3dba40007a3497fa49a080c',
1578+
address: '0xc012041dac143a59fa491db3a2b67b69bd78b685',
1579+
chain: 0,
1580+
index: 0,
1581+
coin: 'hteth',
1582+
wallet: ethWalletV6.id(),
1583+
coinSpecific: {
1584+
forwarderVersion: 4,
1585+
salt: '0x0',
1586+
feeAddress: '0xb1e725186990b86ca8efed08a3ccda9c9f400f09',
1587+
},
1588+
});
1589+
1590+
// Without walletVersion, address creation should fail because
1591+
// the code doesn't know to use TSS verification for v6 wallets
1592+
await assert.rejects(async () => ethWalletV6.createAddress({ chain: 0 }), UnexpectedAddressError);
1593+
1594+
isWalletAddressStub.restore();
1595+
});
1596+
});
1597+
13021598
describe('EVM Cross Chain Recovery', function () {
13031599
const baseUrl = common.Environments.test.etherscanBaseUrl as string;
13041600
it('should build a recovery transaction for hot wallet', async function () {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1380,7 +1380,10 @@ export class Wallet implements IWallet {
13801380
newAddress.baseAddress = baseAddress ?? _.get(this._wallet, 'coinSpecific.baseAddress');
13811381
newAddress.format = addressParams.format;
13821382

1383-
const verificationData: VerifyAddressOptions = _.merge({}, newAddress, { rootAddress });
1383+
const verificationData: VerifyAddressOptions = _.merge({}, newAddress, {
1384+
rootAddress,
1385+
walletVersion: _.get(this._wallet, 'coinSpecific.walletVersion'),
1386+
});
13841387

13851388
if (verificationData.error) {
13861389
throw new AddressGenerationError(verificationData.error);

0 commit comments

Comments
 (0)