Skip to content
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
12 changes: 8 additions & 4 deletions modules/sdk-coin-dot/src/dot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import {
Environments,
ExplanationResult,
KeyPair,
MethodNotImplementedError,
MPCAlgorithm,
ParsedTransaction,
ParseTransactionOptions,
SignedTransaction,
SignTransactionOptions as BaseSignTransactionOptions,
UnsignedTransaction,
VerifyAddressOptions,
VerifyTransactionOptions,
TssVerifyAddressOptions,
EDDSAMethods,
EDDSAMethodTypes,
MPCTx,
Expand All @@ -29,6 +28,7 @@ import {
MultisigType,
multisigTypes,
AuditDecryptedKeyParams,
verifyEddsaTssWalletAddress,
} from '@bitgo/sdk-core';
import { BaseCoin as StaticsBaseCoin, coins, PolkadotSpecNameType } from '@bitgo/statics';
import { Interface, KeyPair as DotKeyPair, Transaction, TransactionBuilderFactory, Utils } from './lib';
Expand Down Expand Up @@ -642,8 +642,12 @@ export class Dot extends BaseCoin {
return {};
}

async isWalletAddress(params: VerifyAddressOptions): Promise<boolean> {
throw new MethodNotImplementedError();
async isWalletAddress(params: TssVerifyAddressOptions): Promise<boolean> {
return verifyEddsaTssWalletAddress(
params,
(address) => this.isValidAddress(address),
(publicKey) => this.getAddressFromPublicKey(publicKey)
);
}

async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
Expand Down
68 changes: 68 additions & 0 deletions modules/sdk-coin-dot/test/unit/dot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BitGoAPI } from '@bitgo/sdk-api';
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { randomBytes } from 'crypto';
import should = require('should');
import assert = require('assert');
import { Dot, Tdot, KeyPair } from '../../src';
import * as testData from '../fixtures';
import { chainName, txVersion, genesisHash, specVersion } from '../resources';
Expand Down Expand Up @@ -670,4 +671,71 @@ describe('DOT:', function () {
);
});
});

describe('isWalletAddress', () => {
it('should verify valid wallet address with correct keychain and index', async function () {
const address = '5DxD9nT16GQLrU6aB5pSS5VtxoZbVju3NHUCcawxZyZCTf74';
const commonKeychain =
'6d2d5150f6e435dfd9b4f225f2cc29d95ec3b61b34e8bec98693b1a7ffe44cd764f99ee5058838d785c73360ad4f24d78e0255ab2c368c09060b29a9b27f040e';
const index = '3';
const keychains = [{ id: '1', type: 'tss' as const, commonKeychain }];

const result = await basecoin.isWalletAddress({ keychains, address, index });
result.should.equal(true);
});

it('should return false for address with incorrect keychain', async function () {
const address = '5DxD9nT16GQLrU6aB5pSS5VtxoZbVju3NHUCcawxZyZCTf74';
const wrongKeychain =
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
const index = '3';
const keychains = [{ id: '1', type: 'tss' as const, commonKeychain: wrongKeychain }];

const result = await basecoin.isWalletAddress({ keychains, address, index });
result.should.equal(false);
});

it('should return false for address with incorrect index', async function () {
const address = '5DxD9nT16GQLrU6aB5pSS5VtxoZbVju3NHUCcawxZyZCTf74';
const commonKeychain =
'6d2d5150f6e435dfd9b4f225f2cc29d95ec3b61b34e8bec98693b1a7ffe44cd764f99ee5058838d785c73360ad4f24d78e0255ab2c368c09060b29a9b27f040e';
const wrongIndex = '999';
const keychains = [{ id: '1', type: 'tss' as const, commonKeychain }];

const result = await basecoin.isWalletAddress({ keychains, address, index: wrongIndex });
result.should.equal(false);
});

it('should throw error for invalid address', async function () {
const invalidAddress = 'invalidaddress';
const commonKeychain =
'6d2d5150f6e435dfd9b4f225f2cc29d95ec3b61b34e8bec98693b1a7ffe44cd764f99ee5058838d785c73360ad4f24d78e0255ab2c368c09060b29a9b27f040e';
const index = '3';
const keychains = [{ id: '1', type: 'tss' as const, commonKeychain }];

await assert.rejects(async () => await basecoin.isWalletAddress({ keychains, address: invalidAddress, index }), {
message: `invalid address: ${invalidAddress}`,
});
});
});

describe('getAddressFromPublicKey', () => {
it('should convert public key to SS58 address for testnet', function () {
const publicKey = '53845d7b6a6e4a666fa2a0f500b88849b02926da5590993731d2b428b7643690';
const expectedAddress = '5DxD9nT16GQLrU6aB5pSS5VtxoZbVju3NHUCcawxZyZCTf74';

const address = basecoin.getAddressFromPublicKey(publicKey);
address.should.equal(expectedAddress);
});

it('should convert public key to SS58 address for mainnet', function () {
const publicKey = '53845d7b6a6e4a666fa2a0f500b88849b02926da5590993731d2b428b7643690';
// Mainnet uses different SS58 prefix (0) vs testnet (42)
const address = prodCoin.getAddressFromPublicKey(publicKey);
address.should.be.type('string');
address.length.should.be.greaterThan(0);
// Should be different from testnet address
address.should.not.equal('5DxD9nT16GQLrU6aB5pSS5VtxoZbVju3NHUCcawxZyZCTf74');
});
});
});
31 changes: 6 additions & 25 deletions modules/sdk-coin-iota/src/iota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import {
MultisigType,
multisigTypes,
MPCAlgorithm,
InvalidAddressError,
EDDSAMethods,
TssVerifyAddressOptions,
MPCType,
verifyEddsaTssWalletAddress,
} from '@bitgo/sdk-core';
import { BaseCoin as StaticsBaseCoin, CoinFamily } from '@bitgo/statics';
import utils from './lib/utils';
Expand Down Expand Up @@ -92,29 +91,11 @@ export class Iota extends BaseCoin {
* @param params
*/
async isWalletAddress(params: TssVerifyAddressOptions): Promise<boolean> {
const { keychains, address, index } = params;

if (!this.isValidAddress(address)) {
throw new InvalidAddressError(`invalid address: ${address}`);
}

if (!keychains) {
throw new Error('missing required param keychains');
}

for (const keychain of keychains) {
const MPC = await EDDSAMethods.getInitializedMpcInstance();
const commonKeychain = keychain.commonKeychain as string;

const derivationPath = 'm/' + index;
const derivedPublicKey = MPC.deriveUnhardened(commonKeychain, derivationPath).slice(0, 64);
const expectedAddress = utils.getAddressFromPublicKey(derivedPublicKey);

if (address !== expectedAddress) {
return false;
}
}
return true;
return verifyEddsaTssWalletAddress(
params,
(address) => this.isValidAddress(address),
(publicKey) => utils.getAddressFromPublicKey(publicKey)
);
}

/**
Expand Down
22 changes: 18 additions & 4 deletions modules/sdk-coin-sol/src/sol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
ITokenEnablement,
KeyPair,
Memo,
MethodNotImplementedError,
MPCAlgorithm,
MPCConsolidationRecoveryOptions,
MPCRecoveryOptions,
Expand All @@ -50,8 +49,9 @@ import {
TransactionExplanation,
TransactionParams,
TransactionRecipient,
VerifyAddressOptions,
VerifyTransactionOptions,
TssVerifyAddressOptions,
verifyEddsaTssWalletAddress,
} from '@bitgo/sdk-core';
import { auditEddsaPrivateKey, getDerivationPath } from '@bitgo/sdk-lib-mpc';
import { BaseNetwork, CoinFamily, coins, SolCoin, BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
Expand Down Expand Up @@ -560,8 +560,22 @@ export class Sol extends BaseCoin {
return true;
}

async isWalletAddress(params: VerifyAddressOptions): Promise<boolean> {
throw new MethodNotImplementedError();
async isWalletAddress(params: TssVerifyAddressOptions): Promise<boolean> {
return verifyEddsaTssWalletAddress(
params,
(address) => this.isValidAddress(address),
(publicKey) => this.getAddressFromPublicKey(publicKey)
);
}

/**
* Converts a Solana public key to an address
* @param publicKey Hex-encoded public key (64 hex characters = 32 bytes)
* @returns Base58-encoded Solana address
*/
getAddressFromPublicKey(publicKey: string): string {
const publicKeyBuffer = Buffer.from(publicKey, 'hex');
return base58.encode(publicKeyBuffer);
}

/**
Expand Down
57 changes: 57 additions & 0 deletions modules/sdk-coin-sol/test/unit/sol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3353,4 +3353,61 @@ describe('SOL:', function () {
);
});
});

describe('isWalletAddress', () => {
it('should verify valid wallet address with correct keychain and index', async function () {
const address = '7YAesfwPk41VChUgr65bm8FEep7ymWqLSW5rpYB5zZPY';
const commonKeychain =
'8ea32ecacfc83effbd2e2790ee44fa7c59b4d86c29a12f09fb613d8195f93f4e21875cad3b98adada40c040c54c3569467df41a020881a6184096378701862bd';
const index = '1';
const keychains = [{ id: '1', type: 'tss' as const, commonKeychain }];

const result = await basecoin.isWalletAddress({ keychains, address, index });
result.should.equal(true);
});

it('should return false for address with incorrect keychain', async function () {
const address = '7YAesfwPk41VChUgr65bm8FEep7ymWqLSW5rpYB5zZPY';
const wrongKeychain =
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
const index = '1';
const keychains = [{ id: '1', type: 'tss' as const, commonKeychain: wrongKeychain }];

const result = await basecoin.isWalletAddress({ keychains, address, index });
result.should.equal(false);
});

it('should return false for address with incorrect index', async function () {
const address = '7YAesfwPk41VChUgr65bm8FEep7ymWqLSW5rpYB5zZPY';
const commonKeychain =
'8ea32ecacfc83effbd2e2790ee44fa7c59b4d86c29a12f09fb613d8195f93f4e21875cad3b98adada40c040c54c3569467df41a020881a6184096378701862bd';
const wrongIndex = '999';
const keychains = [{ id: '1', type: 'tss' as const, commonKeychain }];

const result = await basecoin.isWalletAddress({ keychains, address, index: wrongIndex });
result.should.equal(false);
});

it('should throw error for invalid address', async function () {
const invalidAddress = 'invalidaddress';
const commonKeychain =
'8ea32ecacfc83effbd2e2790ee44fa7c59b4d86c29a12f09fb613d8195f93f4e21875cad3b98adada40c040c54c3569467df41a020881a6184096378701862bd';
const index = '1';
const keychains = [{ id: '1', type: 'tss' as const, commonKeychain }];

await assert.rejects(async () => await basecoin.isWalletAddress({ keychains, address: invalidAddress, index }), {
message: `invalid address: ${invalidAddress}`,
});
});
});

describe('getAddressFromPublicKey', () => {
it('should convert public key to base58 address', function () {
const publicKey = '61220a9394802b1d1df37b35f7a3197970f48081092cee011fc98f7b71b2bd43';
const expectedAddress = '7YAesfwPk41VChUgr65bm8FEep7ymWqLSW5rpYB5zZPY';

const address = basecoin.getAddressFromPublicKey(publicKey);
address.should.equal(expectedAddress);
});
});
});
13 changes: 6 additions & 7 deletions modules/sdk-coin-sui/src/sui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
EDDSAMethods,
EDDSAMethodTypes,
Environments,
InvalidAddressError,
KeyPair,
MPCAlgorithm,
MPCRecoveryOptions,
Expand All @@ -30,6 +29,7 @@ import {
MultisigType,
multisigTypes,
AuditDecryptedKeyParams,
verifyEddsaTssWalletAddress,
} from '@bitgo/sdk-core';
import { BaseCoin as StaticsBaseCoin, BaseNetwork, coins, SuiCoin } from '@bitgo/statics';
import BigNumber from 'bignumber.js';
Expand Down Expand Up @@ -188,12 +188,11 @@ export class Sui extends BaseCoin {
}

async isWalletAddress(params: TssVerifyAddressOptions): Promise<boolean> {
const { address: newAddress } = params;

if (!this.isValidAddress(newAddress)) {
throw new InvalidAddressError(`invalid address: ${newAddress}`);
}
return true;
return verifyEddsaTssWalletAddress(
params,
(address) => this.isValidAddress(address),
(publicKey) => this.getAddressFromPublicKey(publicKey)
);
}

async parseTransaction(params: SuiParseTransactionOptions): Promise<SuiParsedTransaction> {
Expand Down
31 changes: 12 additions & 19 deletions modules/sdk-coin-ton/src/ton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
MPCTxs,
MPCSweepRecoveryOptions,
AuditDecryptedKeyParams,
extractCommonKeychain,
} from '@bitgo/sdk-core';
import { auditEddsaPrivateKey, getDerivationPath } from '@bitgo/sdk-lib-mpc';
import { BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics';
Expand Down Expand Up @@ -159,29 +160,21 @@ export class Ton extends BaseCoin {
throw new InvalidAddressError(`invalid address: ${newAddress}`);
}

if (!keychains) {
throw new Error('missing required param keychains');
}

for (const keychain of keychains) {
const [address, memoId] = newAddress.split('?memoId=');
const MPC = await EDDSAMethods.getInitializedMpcInstance();
const commonKeychain = keychain.commonKeychain as string;
const [address, memoId] = newAddress.split('?memoId=');

const derivationPath = 'm/' + index;
const derivedPublicKey = MPC.deriveUnhardened(commonKeychain, derivationPath).slice(0, 64);
const expectedAddress = await Utils.default.getAddressFromPublicKey(derivedPublicKey);
// TON supports memoId for address tagging - verify it matches the index
if (memoId) {
return memoId === `${index}`;
}

if (memoId) {
return memoId === `${index}`;
}
const commonKeychain = extractCommonKeychain(keychains);

if (address !== expectedAddress) {
return false;
}
}
const MPC = await EDDSAMethods.getInitializedMpcInstance();
const derivationPath = 'm/' + index;
const derivedPublicKey = MPC.deriveUnhardened(commonKeychain, derivationPath).slice(0, 64);
const expectedAddress = await Utils.default.getAddressFromPublicKey(derivedPublicKey);

return true;
return address === expectedAddress;
}

async parseTransaction(params: TonParseTransactionOptions): Promise<ParsedTransaction> {
Expand Down
3 changes: 2 additions & 1 deletion modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
VerifyTransactionOptions,
AuditKeyParams,
AuditDecryptedKeyParams,
TssVerifyAddressOptions,
} from './iBaseCoin';
import { IInscriptionBuilder } from '../inscriptionBuilder';
import {
Expand Down Expand Up @@ -346,7 +347,7 @@ export abstract class BaseCoin implements IBaseCoin {
* @param params
* @return true iff address is a wallet address. Must return false if address is outside wallet.
*/
abstract isWalletAddress(params: VerifyAddressOptions): Promise<boolean>;
abstract isWalletAddress(params: VerifyAddressOptions | TssVerifyAddressOptions): Promise<boolean>;

/**
* convert address into desired address format.
Expand Down
Loading