-
Notifications
You must be signed in to change notification settings - Fork 302
Expand file tree
/
Copy pathaddressVerification.ts
More file actions
92 lines (80 loc) · 4.07 KB
/
addressVerification.ts
File metadata and controls
92 lines (80 loc) · 4.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { getDerivationPath } from '@bitgo/sdk-lib-mpc';
import { Ecdsa } from '../../../account-lib/mpc';
import { TssVerifyAddressOptions } from '../../baseCoin/iBaseCoin';
import { InvalidAddressError } from '../../errors';
import { EDDSAMethods } from '../../tss';
/**
* Extracts and validates the commonKeychain from keychains array.
* For MPC wallets, all keychains should have the same commonKeychain.
*
* @param keychains - Array of keychains containing commonKeychain
* @returns The validated commonKeychain
* @throws {Error} if keychains are missing, empty, or have mismatched commonKeychains
*/
export function extractCommonKeychain(keychains: TssVerifyAddressOptions['keychains']): string {
if (!keychains?.length) {
throw new Error('missing required param keychains');
}
const commonKeychain = keychains[0].commonKeychain;
if (!commonKeychain) {
throw new Error('missing required param commonKeychain');
}
// Verify all keychains have the same commonKeychain
if (keychains.find((kc) => kc.commonKeychain !== commonKeychain))
throw new Error('all keychains must have the same commonKeychain for MPC coins');
return commonKeychain;
}
/**
* Verifies if an address belongs to a wallet using EdDSA TSS MPC derivation.
* This is a common implementation for EdDSA-based MPC coins (SOL, DOT, SUI, TON, IOTA, etc.)
*
* @param params - Verification options including keychains, address, and derivation index
* @param isValidAddress - Coin-specific function to validate address format
* @param getAddressFromPublicKey - Coin-specific function to convert public key to address
* @returns true if the address matches the derived address, false otherwise
* @throws {InvalidAddressError} if the address is invalid
* @throws {Error} if required parameters are missing or invalid
*/
export async function verifyEddsaTssWalletAddress(
params: TssVerifyAddressOptions,
isValidAddress: (address: string) => boolean,
getAddressFromPublicKey: (publicKey: string) => string
): Promise<boolean> {
return verifyMPCWalletAddress({ ...params, keyCurve: 'ed25519' }, isValidAddress, getAddressFromPublicKey);
}
/**
* Verifies if an address belongs to a wallet using ECDSA TSS MPC derivation.
* This is a common implementation for ECDSA-based MPC coins (ETH, BTC, etc.)
*
* @param params - Verification options including keychains, address, and derivation index
* @param isValidAddress - Coin-specific function to validate address format
* @param getAddressFromPublicKey - Coin-specific function to convert public key to address
* @returns true if the address matches the derived address, false otherwise
* @throws {InvalidAddressError} if the address is invalid
* @throws {Error} if required parameters are missing or invalid
*/
export async function verifyMPCWalletAddress(
params: TssVerifyAddressOptions & {
keyCurve: 'secp256k1' | 'ed25519';
},
isValidAddress: (address: string) => boolean,
getAddressFromPublicKey: (publicKey: string) => string
): Promise<boolean> {
const { keychains, address, index, derivedFromParentWithSeed } = params;
if (!isValidAddress(address)) {
throw new InvalidAddressError(`invalid address: ${address}`);
}
const MPC = params.keyCurve === 'secp256k1' ? new Ecdsa() : await EDDSAMethods.getInitializedMpcInstance();
const commonKeychain = extractCommonKeychain(keychains);
// Compute derivation path:
// - For SMC wallets with derivedFromParentWithSeed, compute prefix and use: {prefix}/{index}
// - For other wallets, use simple path: m/{index}
const prefix = derivedFromParentWithSeed ? getDerivationPath(derivedFromParentWithSeed.toString()) : undefined;
const derivationPath = prefix ? `${prefix}/${index}` : `m/${index}`;
const derivedPublicKey = MPC.deriveUnhardened(commonKeychain, derivationPath);
// secp256k1 expects 33 bytes; ed25519 expects 32 bytes
const publicKeySize = params.keyCurve === 'secp256k1' ? 33 : 32;
const publicKeyOnly = Buffer.from(derivedPublicKey, 'hex').subarray(0, publicKeySize).toString('hex');
const expectedAddress = getAddressFromPublicKey(publicKeyOnly);
return address === expectedAddress;
}