From 6db7c6267f5d12646ec450a2a29a352320a94ee8 Mon Sep 17 00:00:00 2001 From: lbqds Date: Sat, 11 May 2024 07:40:54 +0800 Subject: [PATCH] Encode using codec --- .../web3/src/codec/lockup-script-codec.ts | 2 +- packages/web3/src/utils/address.test.ts | 7 +- packages/web3/src/utils/address.ts | 67 ++++++++++--------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/packages/web3/src/codec/lockup-script-codec.ts b/packages/web3/src/codec/lockup-script-codec.ts index c98cfddfd..b705481c2 100644 --- a/packages/web3/src/codec/lockup-script-codec.ts +++ b/packages/web3/src/codec/lockup-script-codec.ts @@ -38,7 +38,7 @@ class PublicKeyHashCodec implements Codec { } const publicKeyHashCodec = new PublicKeyHashCodec() -const publicKeyHashesCodec = new ArrayCodec(publicKeyHashCodec) +export const publicKeyHashesCodec = new ArrayCodec(publicKeyHashCodec) const multiSigParser = Parser.start() .nest('publicKeyHashes', { type: publicKeyHashesCodec.parser }) .nest('m', { type: compactUnsignedIntCodec.parser }) diff --git a/packages/web3/src/utils/address.test.ts b/packages/web3/src/utils/address.test.ts index 6a74556df..e195cfe65 100644 --- a/packages/web3/src/utils/address.test.ts +++ b/packages/web3/src/utils/address.test.ts @@ -165,9 +165,6 @@ describe('address', function () { ).toEqual( '0303030f9f042a9410969f1886f85fa20f6e43176ae23fc5e64db15b3767c84c5db2dc03c83325bd2c0fe1464161c6d5f42699fc9dd799dda7f984f9fbf59b01b095be1903c0a849d8ab8633b45b45ea7f3bb3229e1083a13fd73e027aac2bc55e7f622172' ) - expect(() => - encodeMultisigPublicKeys(Array(32).fill('030f9f042a9410969f1886f85fa20f6e43176ae23fc5e64db15b3767c84c5db2dc'), 2) - ).toThrow('The length of public key array exceeds maximum limit') }) it('should compute address from public key', () => { @@ -183,7 +180,9 @@ describe('address', function () { '94438313828b1b17e5d7f2c9d773d44a81af6c3ef67446fbf350497ff3b06c3741' ] expect(() => addressFromPublicKey('0100', 'multisig')).toThrow('Invalid n in m-of-n multisig, m: 1, n: 0') - expect(() => addressFromPublicKey('0130', 'multisig')).toThrow('Invalid n in m-of-n multisig, m: 1, n: 48') + expect(() => addressFromPublicKey('013f', 'multisig')).toThrow('Invalid n in m-of-n multisig, m: 1, n: -1') + expect(() => addressFromPublicKey('0201', 'multisig')).toThrow('Invalid m in m-of-n multisig, m: 2, n: 1') + expect(() => addressFromPublicKey('3f02', 'multisig')).toThrow('Invalid m in m-of-n multisig, m: -1, n: 2') expect(() => addressFromPublicKey('04' + encodeMultisigPublicKeys(publicKeys, 3).slice(2), 'multisig')).toThrow( 'Invalid m in m-of-n multisig, m: 4, n: 3' ) diff --git a/packages/web3/src/utils/address.ts b/packages/web3/src/utils/address.ts index 98668d325..5e86eeb3c 100644 --- a/packages/web3/src/utils/address.ts +++ b/packages/web3/src/utils/address.ts @@ -24,10 +24,12 @@ import bs58 from './bs58' import djb2 from './djb2' import { binToHex, hexToBinUnsafe, isHexString } from './utils' import { KeyType } from '../signer' +import { compactSignedIntCodec } from '../codec' +import * as codec from '../codec' +import { Buffer } from 'buffer/' const ec = new EC('secp256k1') const PublicKeyBytesLength = 33 -const MaxKeySize = 32 export enum AddressType { P2PKH = 0x00, @@ -165,44 +167,46 @@ export function addressFromPublicKey(publicKey: string, _keyType?: KeyType): str } function multisigAddressFromPublicKey(publicKey: string): string { - try { - const bytes = hexToBinUnsafe(publicKey) - const m = bytes[0] - const n = bytes[1] - if (n <= 0 || n >= MaxKeySize) { - throw new Error(`Invalid n in m-of-n multisig, m: ${m}, n: ${n}`) - } - if (m <= 0 || m > n) { - throw new Error(`Invalid m in m-of-n multisig, m: ${m}, n: ${n}`) - } - if (bytes.length !== PublicKeyBytesLength * n + 2) { - throw new Error('Invalid public key size') - } + if (!isHexString(publicKey)) { + throw new Error(`Invalid public key ${publicKey}, expected a hex-string`) + } + const bytes = Buffer.from(hexToBinUnsafe(publicKey)) + const decodedM = compactSignedIntCodec.decode(bytes) + let index = decodedM.rest.length + 1 + const decodedN = compactSignedIntCodec.decode(bytes.slice(index)) + index += decodedN.rest.length + 1 + const m = compactSignedIntCodec.toI32(decodedM) + const n = compactSignedIntCodec.toI32(decodedN) + if (n <= 0) { + throw new Error(`Invalid n in m-of-n multisig, m: ${m}, n: ${n}`) + } + if (m <= 0 || m > n) { + throw new Error(`Invalid m in m-of-n multisig, m: ${m}, n: ${n}`) + } + if (bytes.length !== PublicKeyBytesLength * n + 2) { + throw new Error('Invalid public key size') + } - const publicKeyHashes: Uint8Array[] = [] - for (let i = 2; i < bytes.length; i += 33) { - const publicKey = bytes.slice(i, i + 33) - publicKeyHashes.push(blake.blake2b(publicKey, undefined, 32)) + const publicKeyHashes: codec.lockupScript.PublicKeyHash[] = [] + for (; index < bytes.length; index += 33) { + const publicKey = bytes.slice(index, index + 33) + publicKeyHashes.push({ publicKeyHash: Buffer.from(blake.blake2b(publicKey, undefined, 32)) }) + } + const lockupScript: codec.lockupScript.LockupScript = { + scriptType: AddressType.P2MPKH, + script: { + publicKeyHashes: codec.lockupScript.publicKeyHashesCodec.fromArray(publicKeyHashes), + m: compactSignedIntCodec.fromI32(m) } - const encoded = Buffer.concat([ - Buffer.from([AddressType.P2MPKH]), - Buffer.from([n]), - ...publicKeyHashes, - Buffer.from([m]) - ]) - return bs58.encode(encoded) - } catch (err) { - throw new Error(`Invalid multisig public key, error: ${err}`) } + const encoded = codec.lockupScript.lockupScriptCodec.encode(lockupScript) + return bs58.encode(encoded) } export function encodeMultisigPublicKeys(publicKeys: string[], m: number): string { if (publicKeys.length === 0) { throw new Error('Public key array is empty') } - if (publicKeys.length >= MaxKeySize) { - throw new Error('The length of public key array exceeds maximum limit') - } if (m <= 0 || m > publicKeys.length) { throw new Error(`Invalid m in m-of-n multisig, m: ${m}, n: ${publicKeys.length}`) } @@ -211,7 +215,8 @@ export function encodeMultisigPublicKeys(publicKeys: string[], m: number): strin throw new Error(`Invalid public key: ${publicKey}`) } }) - return Buffer.from([m, publicKeys.length]).toString('hex') + publicKeys.join('') + const prefix = Buffer.concat([compactSignedIntCodec.encodeI32(m), compactSignedIntCodec.encodeI32(publicKeys.length)]) + return prefix.toString('hex') + publicKeys.join('') } export function addressFromScript(script: Uint8Array): string {