diff --git a/packages/ckb-sdk-utils/__tests__/address/fixtures.json b/packages/ckb-sdk-utils/__tests__/address/fixtures.json new file mode 100644 index 00000000..d06b6505 --- /dev/null +++ b/packages/ckb-sdk-utils/__tests__/address/fixtures.json @@ -0,0 +1,140 @@ +{ + "toAddressPayload": { + "basic": { + "params": ["0x36c329ed630d6ce750712a477543672adab57f4c"], + "expected": [1, 0, 54, 195, 41, 237, 99, 13, 108, 231, 80, 113, 42, 71, 117, 67, 103, 42, 218, 181, 127, 76] + } + }, + "fullPayloadToAddress": { + "data hash type": { + "params": [ + { + "args": "0x36c329ed630d6ce750712a477543672adab57f4c", + "type": "0x02", + "prefix": "ckt", + "codeHash": "0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176" + } + ], + "expected": "ckt1q2n9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5czshhac" + }, + "type hash type": { + "params": [ + { + "args": "0x36c329ed630d6ce750712a477543672adab57f4c", + "type": "0x04", + "prefix": "ckt", + "codeHash": "0x1892ea40d82b53c678ff88312450bbb17e164d7a3e0a90941aa58839f56f8df2" + } + ], + "expected": "ckt1qsvf96jqmq4483ncl7yrzfzshwchu9jd0glq4yy5r2jcsw04d7xlydkr98kkxrtvuag8z2j8w4pkw2k6k4l5c02auef" + }, + "default type = 0x02 and default prefix = ckt": { + "params": [ + { + "args": "0x36c329ed630d6ce750712a477543672adab57f4c", + "codeHash": "0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176" + } + ], + "expected": "ckt1q2n9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5czshhac" + } + }, + "bech32Address": { + "prefix = ckt": { + "params": ["0x36c329ed630d6ce750712a477543672adab57f4c", { "prefix": "ckt" }], + "expected": "ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83" + }, + "prefix = ckb": { + "params": ["0x36c329ed630d6ce750712a477543672adab57f4c", { "prefix": "ckb" }], + "expected": "ckb1qyqrdsefa43s6m882pcj53m4gdnj4k440axqdt9rtd" + }, + "default prefix = ckt": { + "params": ["0x36c329ed630d6ce750712a477543672adab57f4c", {}], + "expected": "ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83" + }, + "default option = { prefix: ckt, type: 0x01, codeHashIndex: 0x00 }": { + "params": ["0x36c329ed630d6ce750712a477543672adab57f4c"], + "expected": "ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83" + }, + "should throw an error when public key hash is not hex string": { + "params": ["36c329ed630d6ce750712a477543672adab57f4c"], + "exception": "Hex string 36c329ed630d6ce750712a477543672adab57f4c should start with 0x" + } + }, + "pubkeyToAddress": { + "with configuration of { prefix: ckt }": { + "params": [ + "0x024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01", + { + "prefix": "ckt" + } + ], + "expected": "ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83" + }, + "default prefix = ckt": { + "params": ["0x024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01", {}], + "expected": "ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83" + }, + "default option = { prefix: ckt }": { + "params": ["0x024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01"], + "expected": "ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83" + } + }, + "parseAddress": { + "output binary": { + "params": ["ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83", "binary"], + "expected": [1, 0, 54, 195, 41, 237, 99, 13, 108, 231, 80, 113, 42, 71, 117, 67, 103, 42, 218, 181, 127, 76] + }, + "output hex string": { + "params": ["ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83", "hex"], + "expected": "0x010036c329ed630d6ce750712a477543672adab57f4c" + }, + "default output type = binary": { + "params": ["ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83"], + "expected": [1, 0, 54, 195, 41, 237, 99, 13, 108, 231, 80, 113, 42, 71, 117, 67, 103, 42, 218, 181, 127, 76] + }, + "single sig address": { + "params": ["ckb1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jqfwyw5v"], + "expected": [1, 0, 179, 155, 188, 11, 54, 115, 199, 211, 100, 80, 188, 20, 207, 205, 173, 45, 85, 156, 108, 100] + }, + "multi sig address": { + "params": ["ckb1qyq5lv479ewscx3ms620sv34pgeuz6zagaaqklhtgg"], + "expected": [1, 1, 79, 178, 190, 46, 93, 12, 26, 59, 134, 148, 248, 50, 53, 10, 51, 193, 104, 93, 71, 122] + }, + "anyone can pay address": { + "params": ["ckt1qyprdsefa43s6m882pcj53m4gdnj4k440axq77ptun"], + "expected": [1, 2, 54, 195, 41, 237, 99, 13, 108, 231, 80, 113, 42, 71, 117, 67, 103, 42, 218, 181, 127, 76] + }, + "data hash type full version address": { + "params": ["ckb1q2da0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgdwd2q8"], + "expected": [ 2, 155, 215, 224, 111, 62, 207, 75, 224, 242, 252, 210, 24, 139, 35, 241, 185, 252, 200, 142, 93, 75, 101, 168, 99, 123, 23, 114, 59, 189, 163, 204, 232, 179, 155, 188, 11, 54, 115, 199, 211, 100, 80, 188, 20, 207, 205, 173, 45, 85, 156, 108, 100 ] + }, + "type hash type full version address": { + "params": ["ckb1qsvf96jqmq4483ncl7yrzfzshwchu9jd0glq4yy5r2jcsw04d7xlydkr98kkxrtvuag8z2j8w4pkw2k6k4l5czfy37k"], + "expected": [ 4, 24, 146, 234, 64, 216, 43, 83, 198, 120, 255, 136, 49, 36, 80, 187, 177, 126, 22, 77, 122, 62, 10, 144, 148, 26, 165, 136, 57, 245, 111, 141, 242, 54, 195, 41, 237, 99, 13, 108, 231, 80, 113, 42, 71, 117, 67, 103, 42, 218, 181, 127, 76 ] + }, + "should throw an error when short version address has invalid payload size": { + "params": ["ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqqm65l9j"], + "exception": "ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqqm65l9j is not a valid short version address" + }, + "should throw an error when anyone can pay address has invalid payload size": { + "params": ["ckt1qyprdsefa43s6m882pcj53m4gdnj4k440axqqfmyd9c"], + "exception": "ckt1qyprdsefa43s6m882pcj53m4gdnj4k440axqqfmyd9c is not a valid short version address" + }, + "should throw an error when address type is invalid": { + "params": ["ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2"], + "exception": "ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2 is not a valid address" + }, + "should throw an error when hash type is invalid": { + "params": ["ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2"], + "exception": "ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2 is not a valid address" + }, + "should throw an error when code hash index is invalid": { + "params": ["ckt1qyzndsefa43s6m882pcj53m4gdnj4k440axqcth0hp"], + "exception": "ckt1qyzndsefa43s6m882pcj53m4gdnj4k440axqcth0hp is not a valid short version address" + }, + "should throw an error when full version address has invalid size": { + "params": ["ckb1qsqcjt4ypkpt20r83lugxyj9pwa30cty6737p2gfgx493qul2cgvrxhw"], + "exception": "ckb1qsqcjt4ypkpt20r83lugxyj9pwa30cty6737p2gfgx493qul2cgvrxhw is not a valid full version address" + } + } +} diff --git a/packages/ckb-sdk-utils/__tests__/address/index.test.js b/packages/ckb-sdk-utils/__tests__/address/index.test.js new file mode 100644 index 00000000..b4d80799 --- /dev/null +++ b/packages/ckb-sdk-utils/__tests__/address/index.test.js @@ -0,0 +1,96 @@ +const ckbUtils = require('../..') +const fixtures = require('./fixtures.json') + +const { toAddressPayload, bech32Address, pubkeyToAddress, parseAddress, fullPayloadToAddress } = ckbUtils + +describe('Test address module', () => { + describe('toAddressPayload', () => { + const fixtureTable = Object.entries(fixtures.toAddressPayload).map(([title, { params, expected, exception }]) => [ + title, + params, + expected, + exception, + ]) + test.each(fixtureTable)(`%s`, (_title, params, expected, exception) => { + expect.assertions(1) + try { + const actual = toAddressPayload(...params) + expect(actual).toEqual(new Uint8Array(expected)) + } catch (err) { + expect(err).toEqual(new Error(exception)) + } + }) + }) + + describe('fullPayloadToAddress', () => { + const fixtureTable = Object.entries( + fixtures.fullPayloadToAddress, + ).map(([title, { params, expected, exception }]) => [title, params, expected, exception]) + test.each(fixtureTable)(`%s`, (_title, params, expected, exception) => { + expect.assertions(1) + try { + const actual = fullPayloadToAddress(...params) + expect(actual).toBe(expected) + } catch (err) { + expect(err).toEqual(new Error(exception)) + } + }) + }) + + describe('bech32Address', () => { + const fixtureTable = Object.entries(fixtures.bech32Address).map(([title, { params, expected, exception }]) => [ + title, + params, + expected, + exception, + ]) + + test.each(fixtureTable)(`%s`, (_title, params, expected, exception) => { + expect.assertions(1) + try { + const actual = bech32Address(...params) + expect(actual).toBe(expected) + } catch (err) { + expect(err).toEqual(new Error(exception)) + } + }) + }) + + describe('pubkeyToAddress', () => { + const fixtureTable = Object.entries(fixtures.pubkeyToAddress).map(([title, { params, expected, exception }]) => [ + title, + params, + expected, + exception, + ]) + + test.each(fixtureTable)(`%s`, (_title, params, expected, exception) => { + expect.assertions(1) + try { + const actual = pubkeyToAddress(...params) + expect(actual).toBe(expected) + } catch (err) { + expect(err).toEqual(new Error(exception)) + } + }) + }) + + describe('parseAddress', () => { + const fixtureTable = Object.entries(fixtures.parseAddress).map(([title, { params, expected, exception }]) => [ + title, + params, + expected, + exception, + ]) + + test.each(fixtureTable)(`%s`, (_title, params, expected, exception) => { + expect.assertions(1) + try { + const actual = parseAddress(...params) + expect(actual).toEqual(typeof expected === 'string' ? expected : new Uint8Array(expected)) + } catch (err) { + expect(err).toEqual(new Error(exception)) + } + }) + }) +}) diff --git a/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json b/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json index 86da6203..bc80792c 100644 --- a/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json +++ b/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json @@ -35,17 +35,17 @@ } }, "AddressPayloadException": { - "params": ["Invalid Payload"], + "params": ["Invalid Payload", "short"], "expected": { - "code": 101, - "message": "Invalid Payload is not a single-sig address payload" + "code": 104, + "message": "Invalid Payload is not a valid short version address payload" } }, "AddressException": { - "params": ["Invalid Address"], + "params": ["Invalid Address", "full"], "expected": { - "code": 101, - "message": "Invalid Address is not a single-sig address" + "code": 104, + "message": "Invalid Address is not a valid full version address" } }, "OutLenTooSmallException": { diff --git a/packages/ckb-sdk-utils/__tests__/utils/index.test.js b/packages/ckb-sdk-utils/__tests__/utils/index.test.js index 477a0ad6..4ed7acbf 100644 --- a/packages/ckb-sdk-utils/__tests__/utils/index.test.js +++ b/packages/ckb-sdk-utils/__tests__/utils/index.test.js @@ -1,4 +1,4 @@ -const ckbUtils = require('../../lib') +const ckbUtils = require('../..') const exceptions = require('../../lib/exceptions') const bech32Fixtures = require('./bech32.fixtures.json') const blake2bFixtures = require('./blake2b.fixtures.json') @@ -10,24 +10,16 @@ const { blake2b, blake160, bech32, - bech32Address, - toAddressPayload, privateKeyToPublicKey, privateKeyToAddress, - pubkeyToAddress, - parseAddress, - hexToBytes, - bytesToHex, scriptToHash, rawTransactionToHash, PERSONAL, - AddressType, - fullPayloadToAddress, calculateTransactionFee, calculateSerializedTxSizeInBlock, } = ckbUtils -const { ParameterRequiredException, HexStringWithout0xException } = exceptions +const { ParameterRequiredException } = exceptions describe('blake', () => { it('blake2b([]) with personal', () => { @@ -192,153 +184,6 @@ describe('privateKeyToAddress', () => { ).toBe(fixture.mainnetAddress) }) -describe('address', () => { - it('publicKeyHash to address payload', () => { - const fixture = { - publicKeyHash: '0x36c329ed630d6ce750712a477543672adab57f4c', - payload: '0x010036c329ed630d6ce750712a477543672adab57f4c', - } - const payload = bytesToHex(toAddressPayload(fixture.publicKeyHash)) - expect(payload).toBe(fixture.payload) - }) - - it('fullPayloadToAddress with hash type of Data', () => { - const fixture = { - params: { - args: '0x36c329ed630d6ce750712a477543672adab57f4c', - type: AddressType.DataCodeHash, - prefix: 'ckt', - codeHash: '0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176', - }, - expected: 'ckt1q2n9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5czshhac', - } - const address = fullPayloadToAddress(fixture.params) - expect(address).toBe(fixture.expected) - }) - - it('fullPayloadToAddress with hash type of Type', () => { - const fixture = { - params: { - args: '0x36c329ed630d6ce750712a477543672adab57f4c', - type: AddressType.TypeCodeHash, - prefix: 'ckt', - codeHash: '0x1892ea40d82b53c678ff88312450bbb17e164d7a3e0a90941aa58839f56f8df2', - }, - expected: 'ckt1qsvf96jqmq4483ncl7yrzfzshwchu9jd0glq4yy5r2jcsw04d7xlydkr98kkxrtvuag8z2j8w4pkw2k6k4l5c02auef', - } - const address = fullPayloadToAddress(fixture.params) - expect(address).toBe(fixture.expected) - }) - - it('fullPayloadToAddress with default params of type = AddressType.DataCodeHash and prefix = ckt', () => { - const fixture = { - params: { - args: '0x36c329ed630d6ce750712a477543672adab57f4c', - codeHash: '0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176', - }, - expected: 'ckt1q2n9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5czshhac', - } - const address = fullPayloadToAddress(fixture.params) - expect(address).toBe(fixture.expected) - }) - - it('publicKeyHash to address with prefix of ckt', () => { - const fixture = { - publicKeyHash: '0x36c329ed630d6ce750712a477543672adab57f4c', - prefix: 'ckt', - address: 'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83', - } - const address = bech32Address(fixture.publicKeyHash, { - prefix: fixture.prefix, - }) - expect(address).toBe(fixture.address) - }) - - it('publicKeyHash to address with prefix of ckb', () => { - const fixture = { - publicKeyHash: '0x36c329ed630d6ce750712a477543672adab57f4c', - prefix: 'ckb', - address: 'ckb1qyqrdsefa43s6m882pcj53m4gdnj4k440axqdt9rtd', - } - const address = bech32Address(fixture.publicKeyHash, { - prefix: fixture.prefix, - }) - expect(address).toBe(fixture.address) - }) - - it('publicKeyHash without 0x should throw an error', () => { - const publicKeyHash = '36c329ed630d6ce750712a477543672adab57f4c' - expect(() => toAddressPayload(publicKeyHash)).toThrow(new HexStringWithout0xException(publicKeyHash)) - }) - - it('bech32Address with empty options', () => { - const fixture = { - publicKeyHash: '0x36c329ed630d6ce750712a477543672adab57f4c', - address: 'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83', - } - const address = bech32Address(fixture.publicKeyHash, {}) - expect(address).toBe(fixture.address) - }) - - it('bech32Address with default options which should be prefix: ckb, type: binIndx, code hash index: 0x00', () => { - const fixture = { - publicKeyHash: '0x36c329ed630d6ce750712a477543672adab57f4c', - address: 'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83', - } - const address = bech32Address(fixture.publicKeyHash) - expect(address).toBe(fixture.address) - }) - - const pubkeyToAddressFixtures = { - 'with configuration of { prefix: ckt }': { - pubkey: '0x024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01', - config: { - prefix: 'ckt', - }, - address: 'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83', - }, - 'with empty configuration': { - pubkey: '0x024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01', - config: {}, - address: 'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83', - }, - 'with undefined configuration': { - pubkey: '0x024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01', - config: undefined, - address: 'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83', - }, - } - test.each(Object.keys(pubkeyToAddressFixtures))('%s', caseName => { - const fixture = pubkeyToAddressFixtures[caseName] - const address = pubkeyToAddress(hexToBytes(fixture.pubkey), fixture.config) - expect(address).toBe(fixture.address) - }) - - it('parse address', () => { - const fixture = { - addr: 'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83', - hrp: '0100', - blake160Pubkey: '36c329ed630d6ce750712a477543672adab57f4c', - } - const parsedHex = parseAddress(fixture.addr, 'hex') - expect(parsedHex).toBe(`0x${fixture.hrp}${fixture.blake160Pubkey}`) - const parsedBytes = parseAddress(fixture.addr, 'binary') - expect(bytesToHex(parsedBytes)).toBe(`0x${fixture.hrp}${fixture.blake160Pubkey}`) - }) - - it('parse address with default options encode: binary', () => { - const fixture = { - addr: 'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83', - hrp: '0100', - blake160Pubkey: '36c329ed630d6ce750712a477543672adab57f4c', - } - const parsedHex = bytesToHex(parseAddress(fixture.addr)) - expect(parsedHex).toBe(`0x${fixture.hrp}${fixture.blake160Pubkey}`) - const parsedBytes = parseAddress(fixture.addr) - expect(bytesToHex(parsedBytes)).toBe(`0x${fixture.hrp}${fixture.blake160Pubkey}`) - }) -}) - describe('transaction fee', () => { const fixtureTable = Object.entries( transactionFeeFixtures, diff --git a/packages/ckb-sdk-utils/__tests__/validators/index.test.js b/packages/ckb-sdk-utils/__tests__/validators/index.test.js index 9d43a034..949d7001 100644 --- a/packages/ckb-sdk-utils/__tests__/validators/index.test.js +++ b/packages/ckb-sdk-utils/__tests__/validators/index.test.js @@ -1,9 +1,4 @@ -const { - assertToBeHexString, - assertToBeHexStringOrBigint, - assertToBeAddress, - assertToBeAddressPayload, -} = require('../../lib/validators') +const { assertToBeHexString, assertToBeHexStringOrBigint } = require('../../lib/validators') const fixtures = require('./fixtures.json') describe('validators', () => { @@ -56,44 +51,4 @@ describe('validators', () => { ) }) }) - - describe('assert to be single-sig address', () => { - const { assertToBeSingleSigAddress: fixture } = fixtures - - it('single sig address should pass', () => { - expect(assertToBeAddress(fixture.singleSigAddress)).toBe(true) - }) - - it('address with invalid single sig payload should throw an error', () => { - expect(() => assertToBeAddress(fixture.addressWithInvalidSingleSigPayload)).toThrow( - new Error(`${fixture.addressWithInvalidSingleSigPayload} is not a single-sig address`), - ) - }) - - it('address has the incorrect size should throw an error', () => { - expect(() => assertToBeAddress(fixture.addressWithIncorrectSize)).toThrow( - new Error(`${fixture.addressWithIncorrectSize} is not a single-sig address`), - ) - }) - }) - - describe('assert to be single-sig address payload', () => { - const { assertToBeSignleSigAddressPayload: fixture } = fixtures - - it('single sig address payload should pass', () => { - expect(assertToBeAddressPayload(fixture.singleSigAddressPayload)).toBe(true) - }) - - it('payload not starts with 0x0100 should throw an error', () => { - expect(() => assertToBeAddressPayload(fixture.payloadNotStartsWith0x0100)).toThrow( - new Error(`${fixture.payloadNotStartsWith0x0100} is not a single-sig address payload`), - ) - }) - - it('payload has the incorrect size should throw an error', () => { - expect(() => assertToBeAddressPayload(fixture.paylaodWithIncorrectSize)).toThrow( - new Error(`${fixture.paylaodWithIncorrectSize} is not a single-sig address payload`), - ) - }) - }) }) diff --git a/packages/ckb-sdk-utils/src/address/index.ts b/packages/ckb-sdk-utils/src/address/index.ts index 7404c908..d16aced2 100644 --- a/packages/ckb-sdk-utils/src/address/index.ts +++ b/packages/ckb-sdk-utils/src/address/index.ts @@ -1,6 +1,6 @@ import { bech32, blake160 } from '..' import { hexToBytes, bytesToHex } from '../convertors' -import { HexStringWithout0xException } from '../exceptions' +import { HexStringWithout0xException, AddressException, AddressPayloadException } from '../exceptions' export enum AddressPrefix { Mainnet = 'ckb', @@ -109,6 +109,54 @@ export const pubkeyToAddress = ( }) } +const isValidShortVersionPayload = (payload: Uint8Array) => { + const [, index, ...data] = payload + /* eslint-disable indent */ + switch (index) { + case 0: // secp256k1 + blake160 + case 1: { + // secp256k1 + multisig + if (data.length !== 20) { + throw new AddressPayloadException(payload, 'short') + } + break + } + case 2: { + // anyone can pay + if (data.length === 20 || data.length === 22 || data.length === 24) { + break + } + throw new AddressPayloadException(payload, 'short') + } + default: { + throw new AddressPayloadException(payload, 'short') + } + } + /* eslint-enable indent */ +} + +const isValidPayload = (payload: Uint8Array) => { + const [type, ...data] = payload + /* eslint-disable indent */ + switch (type) { + case +AddressType.HashIdx: { + isValidShortVersionPayload(payload) + break + } + case +AddressType.DataCodeHash: + case +AddressType.TypeCodeHash: { + if (data.length < 32) { + throw new AddressPayloadException(payload, 'full') + } + break + } + default: { + throw new AddressPayloadException(payload) + } + } + /* eslint-enable indent */ +} + export declare interface ParseAddress { (address: string): Uint8Array (address: string, encode: 'binary'): Uint8Array @@ -121,6 +169,11 @@ export declare interface ParseAddress { */ export const parseAddress: ParseAddress = (address: string, encode: 'binary' | 'hex' = 'binary'): any => { const decoded = bech32.decode(address) - const data = bech32.fromWords(new Uint8Array(decoded.words)) - return encode === 'binary' ? data : bytesToHex(data) + const payload = bech32.fromWords(new Uint8Array(decoded.words)) + try { + isValidPayload(payload) + } catch (err) { + throw new AddressException(address, err.type) + } + return encode === 'binary' ? payload : bytesToHex(payload) } diff --git a/packages/ckb-sdk-utils/src/exceptions/ErrorCode.ts b/packages/ckb-sdk-utils/src/exceptions/ErrorCode.ts index e5bc9479..639b2811 100644 --- a/packages/ckb-sdk-utils/src/exceptions/ErrorCode.ts +++ b/packages/ckb-sdk-utils/src/exceptions/ErrorCode.ts @@ -2,6 +2,7 @@ export enum ErrorCode { ParameterInvalid = 101, ParameterRequired, SignMessageFailed, + AddressInvalid, } export default ErrorCode diff --git a/packages/ckb-sdk-utils/src/exceptions/address.ts b/packages/ckb-sdk-utils/src/exceptions/address.ts index 7283b2f5..145be75a 100644 --- a/packages/ckb-sdk-utils/src/exceptions/address.ts +++ b/packages/ckb-sdk-utils/src/exceptions/address.ts @@ -1,18 +1,24 @@ import ErrorCode from './ErrorCode' export class AddressPayloadException extends Error { - code = ErrorCode.ParameterInvalid + code = ErrorCode.AddressInvalid - constructor(payload: string) { - super(`${payload} is not a single-sig address payload`) + type: 'short' | 'full' | undefined + + constructor(payload: Uint8Array, type?: 'short' | 'full') { + super(`${payload} is not a valid ${type ? `${type} version ` : ''}address payload`) + this.type = type } } export class AddressException extends Error { - code = ErrorCode.ParameterInvalid + code = ErrorCode.AddressInvalid + + type: 'short' | 'full' | undefined - constructor(addr: string) { - super(`${addr} is not a single-sig address`) + constructor(addr: string, type?: 'short' | 'full') { + super(`${addr} is not a valid ${type ? `${type} version ` : ''}address`) + this.type = type } } diff --git a/packages/ckb-sdk-utils/src/validators.ts b/packages/ckb-sdk-utils/src/validators.ts index df5d70d9..c2f08b81 100644 --- a/packages/ckb-sdk-utils/src/validators.ts +++ b/packages/ckb-sdk-utils/src/validators.ts @@ -1,10 +1,4 @@ -import { - HexStringException, - HexStringWithout0xException, - AddressException, - AddressPayloadException, -} from './exceptions' -import { parseAddress } from './address' +import { HexStringException, HexStringWithout0xException } from './exceptions' export const assertToBeHexString = (value: string) => { if (typeof value !== 'string' || !value.startsWith('0x') || Number.isNaN(+value)) { @@ -26,29 +20,7 @@ export const assertToBeHexStringOrBigint = (value: string | bigint) => { throw new TypeError(`${value} should be type of string or bigint`) } -export const assertToBeAddressPayload = (payload: string) => { - if (!payload.startsWith('0x0100') || payload.length !== 46) { - throw new AddressPayloadException(payload) - } - return true -} - -export const assertToBeAddress = (address: string) => { - if (address.length !== 46) { - throw new AddressException(address) - } - try { - const payload = parseAddress(address, 'hex') - assertToBeAddressPayload(payload) - } catch (err) { - throw new AddressException(address) - } - return true -} - export default { assertToBeHexString, assertToBeHexStringOrBigint, - assertToBeAddressPayload, - assertToBeAddress, }