From 54d9c8d67bc2a314b796bdd1abc4f6abd2945a18 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 14 Jul 2020 22:33:00 +0800 Subject: [PATCH] refactor: refactor internal exceptions add detailed error codes and messages --- packages/ckb-sdk-core/src/index.ts | 16 +-- packages/ckb-sdk-core/src/loadCells.ts | 6 +- packages/ckb-sdk-core/src/signWitnesses.ts | 8 +- .../ckb-sdk-rpc/__tests__/ckb-rpc.test.js | 6 +- .../__tests__/exceptions/fixtures.json | 74 ++++++++++ .../__tests__/exceptions/index.test.js | 18 +++ .../__tests__/formatters/params.fixtures.json | 12 +- packages/ckb-sdk-rpc/__tests__/method.test.js | 4 +- .../ckb-sdk-rpc/src/exceptions/ErrorCode.ts | 8 ++ packages/ckb-sdk-rpc/src/exceptions/batch.ts | 39 ++++++ .../ckb-sdk-rpc/src/exceptions/formatter.ts | 49 +++++++ packages/ckb-sdk-rpc/src/exceptions/index.ts | 4 + packages/ckb-sdk-rpc/src/exceptions/rpc.ts | 18 +++ packages/ckb-sdk-rpc/src/index.ts | 8 +- packages/ckb-sdk-rpc/src/method.ts | 5 +- packages/ckb-sdk-rpc/src/paramsFormatter.ts | 23 +++- .../__tests__/convertors/index.test.js | 10 +- .../__tests__/ecpair/index.test.js | 6 +- .../__tests__/exceptions/fixtures.json | 128 ++++++++++++++++++ .../__tests__/exceptions/index.test.js | 15 ++ .../__tests__/utils/index.test.js | 8 +- packages/ckb-sdk-utils/src/address/index.ts | 4 +- .../ckb-sdk-utils/src/convertors/index.ts | 8 +- packages/ckb-sdk-utils/src/crypto/blake2b.ts | 35 +++-- packages/ckb-sdk-utils/src/ecpair.ts | 17 ++- .../ckb-sdk-utils/src/exceptions/ErrorCode.ts | 7 + .../ckb-sdk-utils/src/exceptions/address.ts | 22 +++ .../src/exceptions/argumentRequired.ts | 5 - .../ckb-sdk-utils/src/exceptions/blake2b.ts | 103 ++++++++++++++ .../ckb-sdk-utils/src/exceptions/common.ts | 22 +++ .../ckb-sdk-utils/src/exceptions/index.ts | 19 +-- .../src/exceptions/invalidAddress.ts | 16 --- .../src/exceptions/invalidHexString.ts | 16 --- .../src/exceptions/privateKey.ts | 12 ++ .../ckb-sdk-utils/src/exceptions/string.ts | 22 +++ packages/ckb-sdk-utils/src/index.ts | 6 +- .../ckb-sdk-utils/src/serialization/script.ts | 4 +- packages/ckb-sdk-utils/src/validators.ts | 17 ++- 38 files changed, 666 insertions(+), 134 deletions(-) create mode 100644 packages/ckb-sdk-rpc/__tests__/exceptions/fixtures.json create mode 100644 packages/ckb-sdk-rpc/__tests__/exceptions/index.test.js create mode 100644 packages/ckb-sdk-rpc/src/exceptions/ErrorCode.ts create mode 100644 packages/ckb-sdk-rpc/src/exceptions/batch.ts create mode 100644 packages/ckb-sdk-rpc/src/exceptions/formatter.ts create mode 100644 packages/ckb-sdk-rpc/src/exceptions/index.ts create mode 100644 packages/ckb-sdk-rpc/src/exceptions/rpc.ts create mode 100644 packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json create mode 100644 packages/ckb-sdk-utils/__tests__/exceptions/index.test.js create mode 100644 packages/ckb-sdk-utils/src/exceptions/ErrorCode.ts create mode 100644 packages/ckb-sdk-utils/src/exceptions/address.ts delete mode 100644 packages/ckb-sdk-utils/src/exceptions/argumentRequired.ts create mode 100644 packages/ckb-sdk-utils/src/exceptions/blake2b.ts create mode 100644 packages/ckb-sdk-utils/src/exceptions/common.ts delete mode 100644 packages/ckb-sdk-utils/src/exceptions/invalidAddress.ts delete mode 100644 packages/ckb-sdk-utils/src/exceptions/invalidHexString.ts create mode 100644 packages/ckb-sdk-utils/src/exceptions/privateKey.ts create mode 100644 packages/ckb-sdk-utils/src/exceptions/string.ts diff --git a/packages/ckb-sdk-core/src/index.ts b/packages/ckb-sdk-core/src/index.ts index 3a3b7d9e..1900e9ab 100644 --- a/packages/ckb-sdk-core/src/index.ts +++ b/packages/ckb-sdk-core/src/index.ts @@ -1,7 +1,7 @@ /// import RPC from '@nervosnetwork/ckb-sdk-rpc' -import { ArgumentRequired } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' +import { ParameterRequiredException } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' import * as utils from '@nervosnetwork/ckb-sdk-utils' import generateRawTransaction, { Cell, RawTransactionParamsBase } from './generateRawTransaction' @@ -99,7 +99,7 @@ class CKB { deps: Omit | undefined = this.config.secp256k1Dep, ) => { if (!deps) { - throw new ArgumentRequired('deps') + throw new ParameterRequiredException('deps') } return this.utils.scriptToHash({ @@ -199,7 +199,7 @@ class CKB { transaction: CKBComponents.RawTransactionToSign, cells: CachedCell[], ) => { - if (!key) throw new ArgumentRequired('Private key or address object') + if (!key) throw new ParameterRequiredException('Private key or address object') this.#validateTransactionToSign(transaction) const transactionHash = this.utils.rawTransactionToHash(transaction) @@ -449,20 +449,20 @@ class CKB { #secp256k1DepsShouldBeReady = () => { if (!this.config.secp256k1Dep) { - throw new ArgumentRequired('Secp256k1 dep') + throw new ParameterRequiredException('Secp256k1 dep') } } #DAODepsShouldBeReady = () => { if (!this.config.daoDep) { - throw new ArgumentRequired('Dao dep') + throw new ParameterRequiredException('Dao dep') } } #validateTransactionToSign = (transaction: CKBComponents.RawTransactionToSign) => { - if (!transaction) throw new ArgumentRequired('Transaction') - if (!transaction.witnesses) throw new ArgumentRequired('Witnesses') - if (!transaction.outputsData) throw new ArgumentRequired('OutputsData') + if (!transaction) throw new ParameterRequiredException('Transaction') + if (!transaction.witnesses) throw new ParameterRequiredException('Witnesses') + if (!transaction.outputsData) throw new ParameterRequiredException('OutputsData') if (transaction.outputsData.length < transaction.outputs.length) throw new Error('Invalid count of outputsData') } diff --git a/packages/ckb-sdk-core/src/loadCells.ts b/packages/ckb-sdk-core/src/loadCells.ts index 081df144..76060fe3 100644 --- a/packages/ckb-sdk-core/src/loadCells.ts +++ b/packages/ckb-sdk-core/src/loadCells.ts @@ -1,7 +1,7 @@ import RPC from '@nervosnetwork/ckb-sdk-rpc' import { assertToBeHexStringOrBigint } from '@nervosnetwork/ckb-sdk-utils/lib/validators' import { JSBI } from '@nervosnetwork/ckb-sdk-utils' -import { ArgumentRequired } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' +import { ParameterRequiredException } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' const getMinBigInt = (x: JSBI, y: JSBI) => { return JSBI.greaterThan(x, y) ? y : x @@ -18,10 +18,10 @@ interface LoadCellsProps { const loadCells = async ({ lockHash, start = '0x0', end, STEP = '0x64', rpc }: LoadCellsProps) => { console.warn(`This method is only for demo, don't use it in production`) if (!lockHash) { - throw new ArgumentRequired('lockHash') + throw new ParameterRequiredException('lockHash') } if (!rpc) { - throw new ArgumentRequired('RPC object') + throw new ParameterRequiredException('RPC object') } assertToBeHexStringOrBigint(start) diff --git a/packages/ckb-sdk-core/src/signWitnesses.ts b/packages/ckb-sdk-core/src/signWitnesses.ts index c3256299..f2196c96 100644 --- a/packages/ckb-sdk-core/src/signWitnesses.ts +++ b/packages/ckb-sdk-core/src/signWitnesses.ts @@ -1,4 +1,4 @@ -import { ArgumentRequired } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' +import { ParameterRequiredException } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' import signWitnessGroup from './signWitnessGroup' import groupScripts from './groupScripts' @@ -41,8 +41,8 @@ const signWitnesses: SignWitnesses = (key: SignatureProvider | Map { - if (!key) throw new ArgumentRequired('Signature provider') - if (!transactionHash) throw new ArgumentRequired('Transaction hash') + if (!key) throw new ParameterRequiredException('Signature provider') + if (!transactionHash) throw new ParameterRequiredException('Transaction hash') if (!witnesses.length) throw new Error('Witnesses is empty') if (isMap(key)) { @@ -60,7 +60,7 @@ const signWitnesses: SignWitnesses = (key: SignatureProvider | Map witnesses[idx]), ...restWitnesses] + const ws = [...indices.map(idx => witnesses[idx]), ...restWitnesses] const witnessIncludeSignature = signWitnessGroup(sk, transactionHash, ws)[0] rawWitnesses[indices[0]] = witnessIncludeSignature diff --git a/packages/ckb-sdk-rpc/__tests__/ckb-rpc.test.js b/packages/ckb-sdk-rpc/__tests__/ckb-rpc.test.js index 26c66a87..bd50d0bc 100644 --- a/packages/ckb-sdk-rpc/__tests__/ckb-rpc.test.js +++ b/packages/ckb-sdk-rpc/__tests__/ckb-rpc.test.js @@ -1451,7 +1451,7 @@ describe('Test with mock', () => { describe('ckb-rpc errors', () => { it('throw raw error', async () => { - expect(() => rpc.getBlock(0)).toThrow('Hash 0 should be type of string') + expect(() => rpc.getBlock(0)).toThrow('Expect hash to be string, but 0 received') }) describe('batch request', () => { @@ -1465,7 +1465,7 @@ describe('Test with mock', () => { const batch = rpc.createBatchRequest([['getBlock', [0]]]) batch .exec() - .catch(err => expect(err).toEqual(new Error(`[Batch Request 0]: Hash 0 should be type of string`))) + .catch(err => expect(err).toEqual(new Error(`[Batch Request 0]: Expect hash to be string, but 0 received`))) }) it('should return an error of mismatched json rpc id', async () => { @@ -1491,7 +1491,7 @@ describe('Test with mock', () => { ], }) const res = await batch.exec() - expect(res[0]).toEqual(new Error(`[Batch Request 0]: JSONRPC id not matched`)) + expect(res[0]).toEqual(new Error(`[Batch Request 0]: Expect json rpc id to be 10000, but 10001 received`)) }) }) }) diff --git a/packages/ckb-sdk-rpc/__tests__/exceptions/fixtures.json b/packages/ckb-sdk-rpc/__tests__/exceptions/fixtures.json new file mode 100644 index 00000000..75fd2bbc --- /dev/null +++ b/packages/ckb-sdk-rpc/__tests__/exceptions/fixtures.json @@ -0,0 +1,74 @@ +{ + "IdNotMatchException": { + "params": [10000, 10001], + "expected": { + "code": 201, + "message": "Expect json rpc id to be 10000, but 10001 received" + } + }, + "ResponseException": { + "params": ["Response_Err_Message"], + "expected": { + "code": 204, + "message": "Response_Err_Message" + } + }, + "PageSizeTooLargeException": { + "params": [51, 50], + "expected": { + "code": 101, + "message": "Expect page size to be at most 50, but 51 received" + } + }, + "PageSizeTooSmallException": { + "params": [-1, 0], + "expected": { + "code": 101, + "message": "Expect page size to be at least 0, but -1 received" + } + }, + "OutputsValidatorTypeException": { + "params": [], + "expected": { + "code": 101, + "message": "Expect outputs validator to be 'default' or 'passthrough'" + } + }, + "BigintOrHexStringTypeException": { + "params": ["ab"], + "expected": { + "code": 101, + "message": "Expect number to be bigint or hex string, but ab received" + } + }, + "StringHashTypeException": { + "params": [1], + "expected": { + "code": 101, + "message": "Expect hash to be string, but 1 received" + } + }, + "MethodInBatchNotFoundException": { + "params": ["Method_Name"], + "expected": { + "code": 202, + "message": "[Batch Request]: Method Method_Name is not found" + } + }, + "PayloadInBatchException": { + "params": [1, "Payload_Err_Message"], + "expected": { + "code": 203, + "index": 1, + "message": "[Batch Request 1]: Payload_Err_Message" + } + }, + "IdNotMatchedInBatchException": { + "params": [1, 10000, 10001], + "expected": { + "code": 201, + "index": 1, + "message": "[Batch Request 1]: Expect json rpc id to be 10000, but 10001 received" + } + } +} diff --git a/packages/ckb-sdk-rpc/__tests__/exceptions/index.test.js b/packages/ckb-sdk-rpc/__tests__/exceptions/index.test.js new file mode 100644 index 00000000..c0a83f50 --- /dev/null +++ b/packages/ckb-sdk-rpc/__tests__/exceptions/index.test.js @@ -0,0 +1,18 @@ +const exceptions = require('../../lib/exceptions') +const fixtures = require('./fixtures.json') + +describe('Test exceptions', () => { + const fixtureTable = Object.entries(fixtures).map(([exceptionName, { params, expected }]) => [ + exceptionName, + params, + expected, + ]) + test.each(fixtureTable)(`%s`, (exceptionName, params, expected) => { + const err = new exceptions[exceptionName](...params) + expect(err.code).toBe(expected.code) + expect(err.message).toBe(expected.message) + if (err.index) { + expect(err.index).toBe(expected.index) + } + }) +}) diff --git a/packages/ckb-sdk-rpc/__tests__/formatters/params.fixtures.json b/packages/ckb-sdk-rpc/__tests__/formatters/params.fixtures.json index f60ee4ae..a1ac9c6f 100644 --- a/packages/ckb-sdk-rpc/__tests__/formatters/params.fixtures.json +++ b/packages/ckb-sdk-rpc/__tests__/formatters/params.fixtures.json @@ -6,11 +6,11 @@ }, { "param": 20, - "exception": "The number 20 should be a bigint or a hex string" + "exception": "Expect number to be bigint or hex string, but 20 received" }, { "param": null, - "exception": "The number null should be a bigint or a hex string" + "exception": "Expect number to be bigint or hex string, but null received" }, { "param": "1", @@ -28,7 +28,7 @@ }, { "param": 1, - "exception": "Hash 1 should be type of string" + "exception": "Expect hash to be string, but 1 received" } ], "toOutPoint": [ @@ -345,7 +345,7 @@ }, { "param": -1, - "exception": "Page size is expected to be non-negative" + "exception": "Expect page size to be at least 0, but -1 received" }, { "param": 50, @@ -353,7 +353,7 @@ }, { "param": 51, - "exception": "Page size is up to 50" + "exception": "Expect page size to be at most 50, but 51 received" } ], "toReverseOrder": [ @@ -388,7 +388,7 @@ }, { "param": "unknown", - "exception": "Outputs validator should be default or passthrough" + "exception": "Expect outputs validator to be 'default' or 'passthrough'" } ] } diff --git a/packages/ckb-sdk-rpc/__tests__/method.test.js b/packages/ckb-sdk-rpc/__tests__/method.test.js index 0786366c..0d7744f3 100644 --- a/packages/ckb-sdk-rpc/__tests__/method.test.js +++ b/packages/ckb-sdk-rpc/__tests__/method.test.js @@ -37,7 +37,9 @@ describe('Test Method', () => { result: null, }, }) - await method.call().catch(err => expect(err).toEqual(new Error(`JSONRPC id not matched`))) + await method + .call() + .catch(err => expect(err).toEqual(new Error(`Expect json rpc id to be 10000, but 10001 received`))) }) it('returns with error', async () => { diff --git a/packages/ckb-sdk-rpc/src/exceptions/ErrorCode.ts b/packages/ckb-sdk-rpc/src/exceptions/ErrorCode.ts new file mode 100644 index 00000000..83956337 --- /dev/null +++ b/packages/ckb-sdk-rpc/src/exceptions/ErrorCode.ts @@ -0,0 +1,8 @@ +export enum ErrorCode { + IdNotMatch = 201, + MethodNotFound = 202, + PayloadMessage = 203, + ResponseMessage = 204, +} + +export default ErrorCode diff --git a/packages/ckb-sdk-rpc/src/exceptions/batch.ts b/packages/ckb-sdk-rpc/src/exceptions/batch.ts new file mode 100644 index 00000000..ef3a92c3 --- /dev/null +++ b/packages/ckb-sdk-rpc/src/exceptions/batch.ts @@ -0,0 +1,39 @@ +import ErrorCode from './ErrorCode' +import { IdNotMatchException } from './rpc' + +const ERROR_LABEL = 'Batch Request' + +export class MethodInBatchNotFoundException extends Error { + code = ErrorCode.MethodNotFound + + constructor(name: string) { + super(`[${ERROR_LABEL}]: Method ${name} is not found`) + } +} + +export class PayloadInBatchException extends Error { + code = ErrorCode.PayloadMessage + + index: number | undefined + + constructor(index: number, message: string) { + super(`[${ERROR_LABEL} ${index}]: ${message}`) + this.index = index + } +} + +export class IdNotMatchedInBatchException extends IdNotMatchException { + index: number | undefined + + constructor(index: number, requestId: number, responseId: number) { + super(requestId, responseId) + this.message = `[${ERROR_LABEL} ${index}]: ${this.message}` + this.index = index + } +} + +export default { + MethodInBatchNotFoundException, + PayloadInBatchException, + IdNotMatchedInBatchException, +} diff --git a/packages/ckb-sdk-rpc/src/exceptions/formatter.ts b/packages/ckb-sdk-rpc/src/exceptions/formatter.ts new file mode 100644 index 00000000..f251c99f --- /dev/null +++ b/packages/ckb-sdk-rpc/src/exceptions/formatter.ts @@ -0,0 +1,49 @@ +import { ErrorCode } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' + +export class PageSizeTooLargeException extends RangeError { + code = ErrorCode.ParameterInvalid + + constructor(pageSize: bigint | string, maxSize: number) { + super(`Expect page size to be at most ${maxSize}, but ${pageSize} received`) + } +} + +export class PageSizeTooSmallException extends RangeError { + code = ErrorCode.ParameterInvalid + + constructor(pageSize: bigint | string, minSize: number) { + super(`Expect page size to be at least ${minSize}, but ${pageSize} received`) + } +} + +export class OutputsValidatorTypeException extends TypeError { + code = ErrorCode.ParameterInvalid + + constructor() { + super(`Expect outputs validator to be 'default' or 'passthrough'`) + } +} + +export class BigintOrHexStringTypeException extends TypeError { + code = ErrorCode.ParameterInvalid + + constructor(value: any) { + super(`Expect number to be bigint or hex string, but ${value} received`) + } +} + +export class StringHashTypeException extends TypeError { + code = ErrorCode.ParameterInvalid + + constructor(hash: any) { + super(`Expect hash to be string, but ${hash} received`) + } +} + +export default { + PageSizeTooLargeException, + PageSizeTooSmallException, + OutputsValidatorTypeException, + BigintOrHexStringTypeException, + StringHashTypeException, +} diff --git a/packages/ckb-sdk-rpc/src/exceptions/index.ts b/packages/ckb-sdk-rpc/src/exceptions/index.ts new file mode 100644 index 00000000..6546c01a --- /dev/null +++ b/packages/ckb-sdk-rpc/src/exceptions/index.ts @@ -0,0 +1,4 @@ +export { HexStringWithout0xException } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' +export * from './formatter' +export * from './rpc' +export * from './batch' diff --git a/packages/ckb-sdk-rpc/src/exceptions/rpc.ts b/packages/ckb-sdk-rpc/src/exceptions/rpc.ts new file mode 100644 index 00000000..dd3e753d --- /dev/null +++ b/packages/ckb-sdk-rpc/src/exceptions/rpc.ts @@ -0,0 +1,18 @@ +import ErrorCode from './ErrorCode' + +export class IdNotMatchException extends Error { + code = ErrorCode.IdNotMatch + + constructor(requestId: number, responseId: number) { + super(`Expect json rpc id to be ${requestId}, but ${responseId} received`) + } +} + +export class ResponseException extends Error { + code = ErrorCode.ResponseMessage +} + +export default { + IdNotMatchException, + ResponseException, +} diff --git a/packages/ckb-sdk-rpc/src/index.ts b/packages/ckb-sdk-rpc/src/index.ts index b04bdaef..89c3b55d 100644 --- a/packages/ckb-sdk-rpc/src/index.ts +++ b/packages/ckb-sdk-rpc/src/index.ts @@ -6,6 +6,7 @@ import Method from './method' import paramsFormatter from './paramsFormatter' import resultFormatter from './resultFormatter' +import { MethodInBatchNotFoundException, PayloadInBatchException, IdNotMatchedInBatchException } from './exceptions' class CKBRPC extends Base { #node: CKBComponents.Node = { @@ -61,7 +62,6 @@ class CKBRPC extends Base { params: [N, P][] = [], ) => { const ctx = this - const ERROR_LABEL = 'Batch Request' const proxied: [N, P][] = new Proxy([], { set(...p) { @@ -69,7 +69,7 @@ class CKBRPC extends Base { if (p[1] !== 'length') { const name = p?.[2]?.[0] if (methods.indexOf(name) === -1) { - throw new Error(`[${ERROR_LABEL}]: Method ${name} is not found`) + throw new MethodInBatchNotFoundException(name) } } return Reflect.set(...p) @@ -96,7 +96,7 @@ class CKBRPC extends Base { const method = new Method(ctx.node, { ...ctx.rpcProperties[f], name: f }) return method.getPayload(...p) } catch (err) { - throw new Error(`[${ERROR_LABEL} ${i}]: ${err.message}`) + throw new PayloadInBatchException(i, err.message) } }) @@ -111,7 +111,7 @@ class CKBRPC extends Base { return batchRes.data.map((res: any, i: number) => { if (res.id !== payload[i].id) { - return new Error(`[${ERROR_LABEL} ${i}]: JSONRPC id not matched`) + return new IdNotMatchedInBatchException(i, payload[i].id, res.id) } return ctx.rpcProperties[proxied[i][0]].resultFormatters?.(res.result) ?? res.result }) diff --git a/packages/ckb-sdk-rpc/src/method.ts b/packages/ckb-sdk-rpc/src/method.ts index 7c0e0ed6..5a21aaa5 100644 --- a/packages/ckb-sdk-rpc/src/method.ts +++ b/packages/ckb-sdk-rpc/src/method.ts @@ -1,4 +1,5 @@ import axios from 'axios' +import { IdNotMatchException, ResponseException } from './exceptions' class Method { #name: string @@ -36,10 +37,10 @@ class Method { httpsAgent: this.#node.httpsAgent, }).then(res => { if (res.data.id !== payload.id) { - throw new Error('JSONRPC id not matched') + throw new IdNotMatchException(payload.id, res.data.id) } if (res.data.error) { - throw new Error(JSON.stringify(res.data.error)) + throw new ResponseException(JSON.stringify(res.data.error)) } return this.#options.resultFormatters?.(res.data.result) ?? res.data.result }) diff --git a/packages/ckb-sdk-rpc/src/paramsFormatter.ts b/packages/ckb-sdk-rpc/src/paramsFormatter.ts index ed878a75..91c39582 100644 --- a/packages/ckb-sdk-rpc/src/paramsFormatter.ts +++ b/packages/ckb-sdk-rpc/src/paramsFormatter.ts @@ -1,5 +1,12 @@ -import { HexStringShouldStartWith0x } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions' import { JSBI } from '@nervosnetwork/ckb-sdk-utils' +import { + PageSizeTooLargeException, + PageSizeTooSmallException, + OutputsValidatorTypeException, + BigintOrHexStringTypeException, + StringHashTypeException, + HexStringWithout0xException, +} from './exceptions' /* eslint-disable camelcase */ const formatter = { @@ -11,7 +18,7 @@ const formatter = { }, toHash: (hash: string): RPC.Hash256 => { if (typeof hash !== 'string') { - throw new TypeError(`Hash ${hash} should be type of string`) + throw new StringHashTypeException(hash) } return hash.startsWith('0x') ? hash : `0x${hash}` }, @@ -21,10 +28,10 @@ const formatter = { return `0x${number.toString(16)}` } if (typeof number !== 'string') { - throw new TypeError(`The number ${number} should be a bigint or a hex string`) + throw new BigintOrHexStringTypeException(number) } if (!number.startsWith('0x')) { - throw new HexStringShouldStartWith0x(number) + throw new HexStringWithout0xException(number) } return number }, @@ -107,8 +114,10 @@ const formatter = { toPageNumber: (pageNo: string | bigint = '0x1') => formatter.toNumber(pageNo), toPageSize: (pageSize: string | bigint = '0x32') => { const size = JSBI.BigInt(`${pageSize}`) - if (JSBI.greaterThan(size, JSBI.BigInt(50))) throw new Error('Page size is up to 50') - if (JSBI.lessThan(size, JSBI.BigInt(0))) throw new Error('Page size is expected to be non-negative') + const MAX_SIZE = 50 + const MIN_SIZE = 0 + if (JSBI.greaterThan(size, JSBI.BigInt(MAX_SIZE))) throw new PageSizeTooLargeException(pageSize, MAX_SIZE) + if (JSBI.lessThan(size, JSBI.BigInt(MIN_SIZE))) throw new PageSizeTooSmallException(pageSize, MIN_SIZE) return formatter.toNumber(`0x${size.toString(16)}`) }, toReverseOrder: (reverse: boolean = false) => !!reverse, @@ -118,7 +127,7 @@ const formatter = { if (VALIDATORS.indexOf(outputsValidator) > -1) { return outputsValidator } - throw new TypeError('Outputs validator should be default or passthrough') + throw new OutputsValidatorTypeException() }, } diff --git a/packages/ckb-sdk-utils/__tests__/convertors/index.test.js b/packages/ckb-sdk-utils/__tests__/convertors/index.test.js index 65927e10..d902efeb 100644 --- a/packages/ckb-sdk-utils/__tests__/convertors/index.test.js +++ b/packages/ckb-sdk-utils/__tests__/convertors/index.test.js @@ -8,7 +8,7 @@ const { hexToUtf8, toHexInLittleEndian, } = require('../../lib/convertors') -const { HexStringShouldStartWith0x } = require('../../lib/exceptions') +const { HexStringWithout0xException } = require('../../lib/exceptions') const { uint16Le: uint16LeFixture, @@ -70,7 +70,7 @@ describe('hex to bytes', () => { }) it('hex string without 0x should throw an error', () => { - expect(() => hexToBytes('abcd12')).toThrow(new HexStringShouldStartWith0x('abcd12')) + expect(() => hexToBytes('abcd12')).toThrow(new HexStringWithout0xException('abcd12')) }) }) @@ -95,7 +95,7 @@ describe('hex to utf8', () => { }) it('hex string without 0x should throw an error', () => { - expect(() => hexToBytes('abcd')).toThrow(new HexStringShouldStartWith0x('abcd')) + expect(() => hexToBytes('abcd')).toThrow(new HexStringWithout0xException('abcd')) }) }) @@ -108,9 +108,9 @@ describe('Test toHexInLittleEndian', () => { expect(toHexInLittleEndian(value)).toBe(expected) }) it('hex string without 0x should throw an error', () => { - expect(() => toHexInLittleEndian('123')).toThrow(new HexStringShouldStartWith0x('123')) + expect(() => toHexInLittleEndian('123')).toThrow(new HexStringWithout0xException('123')) }) it('throw an error when received a input unable to be converted into a number', () => { - expect(() => toHexInLittleEndian('invalid number')).toThrow(new HexStringShouldStartWith0x('invalid number')) + expect(() => toHexInLittleEndian('invalid number')).toThrow(new HexStringWithout0xException('invalid number')) }) }) diff --git a/packages/ckb-sdk-utils/__tests__/ecpair/index.test.js b/packages/ckb-sdk-utils/__tests__/ecpair/index.test.js index ac4d8102..68b8f062 100644 --- a/packages/ckb-sdk-utils/__tests__/ecpair/index.test.js +++ b/packages/ckb-sdk-utils/__tests__/ecpair/index.test.js @@ -1,6 +1,6 @@ const { hexToBytes } = require('../..') const { default: ECPair } = require('../../lib/ecpair') -const { ArgumentRequired, HexStringShouldStartWith0x } = require('../../lib/exceptions') +const { ParameterRequiredException, HexStringWithout0xException } = require('../../lib/exceptions') const { sigFixtures, signRecoverableFixtures } = require('./signature.fixtures.json') const { instantiate: instantiateFixtures } = require('./ecpare.fixtures.json') @@ -39,12 +39,12 @@ describe('ECPair', () => { }) it('Instantiate with an empty private key should throw an error', () => { - expect(() => new ECPair()).toThrow(new ArgumentRequired('Private key')) + expect(() => new ECPair()).toThrow(new ParameterRequiredException('Private key')) }) it('Instantiate with a private key without 0x should throw an error', () => { const privateKey = 'e79f3207ea4980b7fed79956d5934249ceac4751a4fae01a0f7c4a96884bc4e3' - expect(() => new ECPair(privateKey, {})).toThrow(new HexStringShouldStartWith0x(privateKey)) + expect(() => new ECPair(privateKey, {})).toThrow(new HexStringWithout0xException(privateKey)) }) it('shoule throw an error if private key has invalid length', () => { diff --git a/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json b/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json new file mode 100644 index 00000000..86da6203 --- /dev/null +++ b/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json @@ -0,0 +1,128 @@ +{ + "ParameterRequiredException": { + "params": ["Field Name"], + "expected": { + "code": 102, + "message": "Field Name is required" + } + }, + "SignMessageException": { + "params": [], + "expected": { + "code": 103, + "message": "Fail to sign the message" + } + }, + "PrivateKeyLenException": { + "params": [], + "expected": { + "code": 101, + "message": "Private key has invalid length" + } + }, + "HexStringException": { + "params": ["Invalid hex string"], + "expected": { + "code": 101, + "message": "Invalid hex string is an invalid hex string" + } + }, + "HexStringWithout0xException": { + "params": ["abc"], + "expected": { + "code": 101, + "message": "Hex string abc should start with 0x" + } + }, + "AddressPayloadException": { + "params": ["Invalid Payload"], + "expected": { + "code": 101, + "message": "Invalid Payload is not a single-sig address payload" + } + }, + "AddressException": { + "params": ["Invalid Address"], + "expected": { + "code": 101, + "message": "Invalid Address is not a single-sig address" + } + }, + "OutLenTooSmallException": { + "params": [16, 32], + "expected": { + "code": 101, + "message": "Expect outlen to be at least 32, but 16 received" + } + }, + "OutLenTooLargeException": { + "params": [32, 16], + "expected": { + "code": 101, + "message": "Expect outlen to be at most 16, but 32 received" + } + }, + "KeyLenTooSmallException": { + "params": [16, 32], + "expected": { + "code": 101, + "message": "Expect key length to be at least 32, but 16 received" + } + }, + "KeyLenTooLargeException": { + "params": [32, 16], + "expected": { + "code": 101, + "message": "Expect key length to be at most 16, but 32 received" + } + }, + "OutTypeException": { + "params": [], + "expected": { + "code": 101, + "message": "Expect out to be \"binary\", \"hex\", Uint8Array, or Buffer" + } + }, + "SaltTypeException": { + "params": [], + "expected": { + "code": 101, + "message": "Expect salt to be Uint8Array or Buffer" + } + }, + "SaltLenException": { + "params": [16, 32], + "expected": { + "code": 101, + "message": "Expect salt length to be 32, but 16 received" + } + }, + "InputTypeException": { + "params": [], + "expected": { + "code": 101, + "message": "Expect input to be Uint8Array or Buffer" + } + }, + "KeyTypeException": { + "params": [], + "expected": { + "code": 101, + "message": "Expect key to be Uint8Array or Buffer" + } + }, + "PersonalTypeException": { + "params": [], + "expected": { + "code": 101, + "message": "Expect PERSONAL to be Uint8Array or Buffer" + } + }, + "PersonalLenException": { + "params": [32, 16], + "expected": { + "code": 101, + "message": "Expect PERSONAL length to be 16, but 32 received" + } + } +} diff --git a/packages/ckb-sdk-utils/__tests__/exceptions/index.test.js b/packages/ckb-sdk-utils/__tests__/exceptions/index.test.js new file mode 100644 index 00000000..e2cd38c2 --- /dev/null +++ b/packages/ckb-sdk-utils/__tests__/exceptions/index.test.js @@ -0,0 +1,15 @@ +const exceptions = require('../../lib/exceptions') +const fixtures = require('./fixtures.json') + +describe.only('Test exceptions', () => { + const fixtureTable = Object.entries(fixtures).map(([exceptionName, { params, expected }]) => [ + exceptionName, + params, + expected, + ]) + test.each(fixtureTable)(`%s`, (exceptionName, params, expected) => { + const err = new exceptions[exceptionName](...params) + expect(err.code).toBe(expected.code) + expect(err.message).toBe(expected.message) + }) +}) diff --git a/packages/ckb-sdk-utils/__tests__/utils/index.test.js b/packages/ckb-sdk-utils/__tests__/utils/index.test.js index 1ed15feb..473f6f58 100644 --- a/packages/ckb-sdk-utils/__tests__/utils/index.test.js +++ b/packages/ckb-sdk-utils/__tests__/utils/index.test.js @@ -28,7 +28,7 @@ const { calculateSerializedTxSizeInBlock, } = ckbUtils -const { ArgumentRequired, HexStringShouldStartWith0x } = exceptions +const { ParameterRequiredException, HexStringWithout0xException } = exceptions describe('parse epoch', () => { const fixture = { @@ -75,7 +75,7 @@ describe('blake', () => { salt ? Buffer.from(salt, 'hex') : null, personal ? Buffer.from(personal, 'hex') : null, ) - }).toThrowError(`outlen must be at least 16, was given ${outlen}`) + }).toThrowError(`Expect outlen to be at least 16, but ${outlen} received`) } else { const s = blake2b( outlen, @@ -166,7 +166,7 @@ describe('scriptToHash', () => { }) it('empty input should throw an error', () => { - expect(() => scriptToHash()).toThrow(new ArgumentRequired('Script')) + expect(() => scriptToHash()).toThrow(new ParameterRequiredException('Script')) }) }) @@ -282,7 +282,7 @@ describe('address', () => { it('publicKeyHash without 0x should throw an error', () => { const publicKeyHash = '36c329ed630d6ce750712a477543672adab57f4c' - expect(() => toAddressPayload(publicKeyHash)).toThrow(new HexStringShouldStartWith0x(publicKeyHash)) + expect(() => toAddressPayload(publicKeyHash)).toThrow(new HexStringWithout0xException(publicKeyHash)) }) it('bech32Address with empty options', () => { diff --git a/packages/ckb-sdk-utils/src/address/index.ts b/packages/ckb-sdk-utils/src/address/index.ts index 58b2d0bc..7404c908 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 { HexStringShouldStartWith0x } from '../exceptions' +import { HexStringWithout0xException } from '../exceptions' export enum AddressPrefix { Mainnet = 'ckb', @@ -43,7 +43,7 @@ export const toAddressPayload = ( ): Uint8Array => { if (typeof args === 'string') { if (!args.startsWith('0x')) { - throw new HexStringShouldStartWith0x(args) + throw new HexStringWithout0xException(args) } return new Uint8Array([...hexToBytes(type), ...hexToBytes(codeHashOrCodeHashIndex), ...hexToBytes(args)]) } diff --git a/packages/ckb-sdk-utils/src/convertors/index.ts b/packages/ckb-sdk-utils/src/convertors/index.ts index b0b2735d..6a85c45f 100644 --- a/packages/ckb-sdk-utils/src/convertors/index.ts +++ b/packages/ckb-sdk-utils/src/convertors/index.ts @@ -1,7 +1,7 @@ import { TextDecoder, TextEncoder, deprecate } from 'util' import JSBI from 'jsbi' import { assertToBeHexStringOrBigint } from '../validators' -import { HexStringShouldStartWith0x } from '../exceptions' +import { HexStringWithout0xException } from '../exceptions' /** * Converts an uint16 into a hex string in little endian @@ -49,7 +49,7 @@ export const toUint64Le = (uint64: string | bigint) => { export const hexToBytes = (rawhex: string | number | bigint) => { if (rawhex === '') return new Uint8Array() if (typeof rawhex === 'string' && !rawhex.startsWith('0x')) { - throw new HexStringShouldStartWith0x(rawhex) + throw new HexStringWithout0xException(rawhex) } let hex = rawhex.toString(16).replace(/^0x/i, '') @@ -64,7 +64,7 @@ export const hexToBytes = (rawhex: string | number | bigint) => { } export const bytesToHex = (bytes: Uint8Array): string => - `0x${[...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')}` + `0x${[...bytes].map(b => b.toString(16).padStart(2, '0')).join('')}` export const bytesToUtf8 = (bytes: Uint8Array) => new TextDecoder().decode(bytes) @@ -87,7 +87,7 @@ export const toHexInLittleEndian = deprecate((int: string | bigint, paddingBytes const reversedHex = reverseString(hex) const frags = reversedHex.match(/\w{1,2}/g) || [] const hexInLittleEndian = frags - .map((frag) => reverseString(frag.padEnd(2, '0'))) + .map(frag => reverseString(frag.padEnd(2, '0'))) .join('') .padEnd(paddingBytes * 2, '0') return `0x${hexInLittleEndian}` diff --git a/packages/ckb-sdk-utils/src/crypto/blake2b.ts b/packages/ckb-sdk-utils/src/crypto/blake2b.ts index 8c850245..4304f8e6 100644 --- a/packages/ckb-sdk-utils/src/crypto/blake2b.ts +++ b/packages/ckb-sdk-utils/src/crypto/blake2b.ts @@ -1,5 +1,18 @@ /* eslint-disable no-param-reassign */ const b2wasm = require('blake2b-wasm') +const { + OutLenTooSmallException, + OutLenTooLargeException, + KeyLenTooSmallException, + KeyLenTooLargeException, + OutTypeException, + SaltTypeException, + SaltLenException, + InputTypeException, + KeyTypeException, + PersonalTypeException, + PersonalLenException, +} = require('../exceptions') const BYTES_MIN = 16 const BYTES_MAX = 64 @@ -293,7 +306,7 @@ export class Blake2b { update = (input: Uint8Array) => { if (!(input instanceof Uint8Array)) { - throw new TypeError('input must be Uint8Array or Buffer') + throw new InputTypeException() } blake2bUpdate(this, input) return this @@ -302,7 +315,7 @@ export class Blake2b { digest = (out: 'binary' | 'hex') => { const buf = !out || out === 'binary' || out === 'hex' ? new Uint8Array(this.outlen) : out if (!(buf instanceof Uint8Array)) { - throw new TypeError('out must be "binary", "hex", Uint8Array, or Buffer') + throw new OutTypeException() } if (buf.length < this.outlen) { throw new Error('out must have at least outlen bytes of space') @@ -324,36 +337,36 @@ export const blake2b = ( ) => { if (noAssert !== true) { if (outlen < BYTES_MIN) { - throw new Error(`outlen must be at least ${BYTES_MIN}, was given ${outlen}`) + throw new OutLenTooSmallException(outlen, BYTES_MIN) } if (outlen > BYTES_MAX) { - throw new Error(`outlen must be at most ${BYTES_MAX}, was given ${outlen}`) + throw new OutLenTooLargeException(outlen, BYTES_MAX) } if (key !== null) { if (!(key instanceof Uint8Array)) { - throw new TypeError('key must be Uint8Array or Buffer') + throw new KeyTypeException() } if (key.length < KEYBYTES_MIN) { - throw new Error(`key must be at least ${KEYBYTES_MIN}, was given ${key.length}`) + throw new KeyLenTooSmallException(key.length, KEYBYTES_MIN) } if (key.length > KEYBYTES_MAX) { - throw new Error(`key must be at most ${KEYBYTES_MAX}, was given ${key.length}`) + throw new KeyLenTooLargeException(key.length, KEYBYTES_MAX) } } if (salt !== null) { if (!(salt instanceof Uint8Array)) { - throw new TypeError('salt must be Uint8Array or Buffer') + throw new SaltTypeException() } if (salt.length !== SALTBYTES) { - throw new Error(`salt must be exactly ${SALTBYTES}, was given ${salt.length}`) + throw new SaltLenException(salt.length, SALTBYTES) } } if (personal !== null) { if (!(personal instanceof Uint8Array)) { - throw new TypeError('personal must be Uint8Array or Buffer') + throw new PersonalTypeException() } if (personal.length !== PERSONALBYTES) { - throw new TypeError(`personal must be exactly ${PERSONALBYTES}, was given ${personal.length}`) + throw new PersonalLenException(personal.length, PERSONALBYTES) } } } diff --git a/packages/ckb-sdk-utils/src/ecpair.ts b/packages/ckb-sdk-utils/src/ecpair.ts index 9f3aec27..1a42618b 100644 --- a/packages/ckb-sdk-utils/src/ecpair.ts +++ b/packages/ckb-sdk-utils/src/ecpair.ts @@ -1,7 +1,12 @@ import { ec as EC } from 'elliptic' import { hexToBytes } from './convertors' -import { HexStringShouldStartWith0x, ArgumentRequired } from './exceptions' +import { + HexStringWithout0xException, + ParameterRequiredException, + PrivateKeyLenException, + SignMessageException, +} from './exceptions' const ec = new EC('secp256k1') @@ -20,18 +25,18 @@ class ECPair { compressed: true, }, ) { - if (sk === undefined) throw new ArgumentRequired('Private key') + if (sk === undefined) throw new ParameterRequiredException('Private key') if (typeof sk === 'string' && !sk.startsWith('0x')) { - throw new HexStringShouldStartWith0x(sk) + throw new HexStringWithout0xException(sk) } if (typeof sk === 'string' && sk.length !== 66) { - throw new Error('Private key has invalid length') + throw new PrivateKeyLenException() } if (typeof sk === 'object' && sk.byteLength !== 32) { - throw new Error('Private key has invalid length') + throw new PrivateKeyLenException() } this.key = ec.keyFromPrivate(typeof sk === 'string' ? sk.replace(/^0x/, '') : sk) @@ -80,7 +85,7 @@ class ECPair { const { r, s, recoveryParam } = this.key.sign(msg, { canonical: true, }) - if (recoveryParam === null) throw new Error('Fail to sign the message') + if (recoveryParam === null) throw new SignMessageException() const fmtR = r.toString(16).padStart(64, '0') const fmtS = s.toString(16).padStart(64, '0') return `0x${fmtR}${fmtS}0${recoveryParam}` diff --git a/packages/ckb-sdk-utils/src/exceptions/ErrorCode.ts b/packages/ckb-sdk-utils/src/exceptions/ErrorCode.ts new file mode 100644 index 00000000..e5bc9479 --- /dev/null +++ b/packages/ckb-sdk-utils/src/exceptions/ErrorCode.ts @@ -0,0 +1,7 @@ +export enum ErrorCode { + ParameterInvalid = 101, + ParameterRequired, + SignMessageFailed, +} + +export default ErrorCode diff --git a/packages/ckb-sdk-utils/src/exceptions/address.ts b/packages/ckb-sdk-utils/src/exceptions/address.ts new file mode 100644 index 00000000..7283b2f5 --- /dev/null +++ b/packages/ckb-sdk-utils/src/exceptions/address.ts @@ -0,0 +1,22 @@ +import ErrorCode from './ErrorCode' + +export class AddressPayloadException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(payload: string) { + super(`${payload} is not a single-sig address payload`) + } +} + +export class AddressException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(addr: string) { + super(`${addr} is not a single-sig address`) + } +} + +export default { + AddressPayloadException, + AddressException, +} diff --git a/packages/ckb-sdk-utils/src/exceptions/argumentRequired.ts b/packages/ckb-sdk-utils/src/exceptions/argumentRequired.ts deleted file mode 100644 index 3d69b648..00000000 --- a/packages/ckb-sdk-utils/src/exceptions/argumentRequired.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default class extends Error { - constructor(name: string) { - super(`${name} is required`) - } -} diff --git a/packages/ckb-sdk-utils/src/exceptions/blake2b.ts b/packages/ckb-sdk-utils/src/exceptions/blake2b.ts new file mode 100644 index 00000000..33288e8d --- /dev/null +++ b/packages/ckb-sdk-utils/src/exceptions/blake2b.ts @@ -0,0 +1,103 @@ +import ErrorCode from './ErrorCode' + +export class OutLenTooSmallException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(outlen: number, minLen: number) { + super(`Expect outlen to be at least ${minLen}, but ${outlen} received`) + } +} + +export class OutLenTooLargeException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(outlen: number, maxLen: number) { + super(`Expect outlen to be at most ${maxLen}, but ${outlen} received`) + } +} + +export class KeyLenTooSmallException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(keyLen: number, minLen: number) { + super(`Expect key length to be at least ${minLen}, but ${keyLen} received`) + } +} + +export class KeyLenTooLargeException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(keyLen: number, maxLen: number) { + super(`Expect key length to be at most ${maxLen}, but ${keyLen} received`) + } +} + +export class OutTypeException extends TypeError { + code = ErrorCode.ParameterInvalid + + constructor() { + super(`Expect out to be "binary", "hex", Uint8Array, or Buffer`) + } +} + +export class SaltTypeException extends TypeError { + code = ErrorCode.ParameterInvalid + + constructor() { + super(`Expect salt to be Uint8Array or Buffer`) + } +} + +export class SaltLenException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(saltLen: number, requiredLen: number) { + super(`Expect salt length to be ${requiredLen}, but ${saltLen} received`) + } +} + +export class InputTypeException extends TypeError { + code = ErrorCode.ParameterInvalid + + constructor() { + super(`Expect input to be Uint8Array or Buffer`) + } +} + +export class KeyTypeException extends TypeError { + code = ErrorCode.ParameterInvalid + + constructor() { + super(`Expect key to be Uint8Array or Buffer`) + } +} + +export class PersonalTypeException extends TypeError { + code = ErrorCode.ParameterInvalid + + constructor() { + super(`Expect PERSONAL to be Uint8Array or Buffer`) + } +} + +export class PersonalLenException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(personalLen: number, requiredLen: number) { + super(`Expect PERSONAL length to be ${requiredLen}, but ${personalLen} received`) + } +} + +export default { + OutLenTooSmallException, + OutLenTooLargeException, + KeyLenTooSmallException, + KeyLenTooLargeException, + OutTypeException, + SaltTypeException, + SaltLenException, + InputTypeException, + KeyTypeException, + PersonalTypeException, + PersonalLenException, +} diff --git a/packages/ckb-sdk-utils/src/exceptions/common.ts b/packages/ckb-sdk-utils/src/exceptions/common.ts new file mode 100644 index 00000000..93afacd4 --- /dev/null +++ b/packages/ckb-sdk-utils/src/exceptions/common.ts @@ -0,0 +1,22 @@ +import ErrorCode from './ErrorCode' + +export class ParameterRequiredException extends Error { + code = ErrorCode.ParameterRequired + + constructor(name: string) { + super(`${name} is required`) + } +} + +export class SignMessageException extends Error { + code = ErrorCode.SignMessageFailed + + constructor() { + super('Fail to sign the message') + } +} + +export default { + ParameterRequiredException, + SignMessageException, +} diff --git a/packages/ckb-sdk-utils/src/exceptions/index.ts b/packages/ckb-sdk-utils/src/exceptions/index.ts index 38a78c33..a94d51e3 100644 --- a/packages/ckb-sdk-utils/src/exceptions/index.ts +++ b/packages/ckb-sdk-utils/src/exceptions/index.ts @@ -1,13 +1,6 @@ -import ArgumentRequired from './argumentRequired' -import { HexStringShouldStartWith0x, InvalidHexString } from './invalidHexString' -import { InvalidAddress, InvalidAddressPayload } from './invalidAddress' - -export { HexStringShouldStartWith0x, ArgumentRequired, InvalidHexString, InvalidAddress, InvalidAddressPayload } - -export default { - HexStringShouldStartWith0x, - ArgumentRequired, - InvalidHexString, - InvalidAddress, - InvalidAddressPayload, -} +export * from './ErrorCode' +export * from './common' +export * from './string' +export * from './address' +export * from './blake2b' +export * from './privateKey' diff --git a/packages/ckb-sdk-utils/src/exceptions/invalidAddress.ts b/packages/ckb-sdk-utils/src/exceptions/invalidAddress.ts deleted file mode 100644 index 514f4d9b..00000000 --- a/packages/ckb-sdk-utils/src/exceptions/invalidAddress.ts +++ /dev/null @@ -1,16 +0,0 @@ -export class InvalidAddressPayload extends Error { - constructor(payload: string) { - super(`${payload} is not a single-sig address payload`) - } -} - -export class InvalidAddress extends Error { - constructor(addr: string) { - super(`${addr} is not a single-sig address`) - } -} - -export default { - InvalidAddressPayload, - InvalidAddress, -} diff --git a/packages/ckb-sdk-utils/src/exceptions/invalidHexString.ts b/packages/ckb-sdk-utils/src/exceptions/invalidHexString.ts deleted file mode 100644 index 043cb293..00000000 --- a/packages/ckb-sdk-utils/src/exceptions/invalidHexString.ts +++ /dev/null @@ -1,16 +0,0 @@ -export class InvalidHexString extends Error { - constructor(hex: string) { - super(`${hex} is an invalid hex string`) - } -} - -export class HexStringShouldStartWith0x extends Error { - constructor(hex: string) { - super(`Hex string ${hex} should start with 0x`) - } -} - -export default { - InvalidHexString, - HexStringShouldStartWith0x, -} diff --git a/packages/ckb-sdk-utils/src/exceptions/privateKey.ts b/packages/ckb-sdk-utils/src/exceptions/privateKey.ts new file mode 100644 index 00000000..5bef976e --- /dev/null +++ b/packages/ckb-sdk-utils/src/exceptions/privateKey.ts @@ -0,0 +1,12 @@ +import ErrorCode from './ErrorCode' + +export class PrivateKeyLenException extends Error { + code = ErrorCode.ParameterInvalid + + constructor() { + super('Private key has invalid length') + } +} +export default { + PrivateKeyLenException, +} diff --git a/packages/ckb-sdk-utils/src/exceptions/string.ts b/packages/ckb-sdk-utils/src/exceptions/string.ts new file mode 100644 index 00000000..bc55e26e --- /dev/null +++ b/packages/ckb-sdk-utils/src/exceptions/string.ts @@ -0,0 +1,22 @@ +import ErrorCode from './ErrorCode' + +export class HexStringException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(hex: string) { + super(`${hex} is an invalid hex string`) + } +} + +export class HexStringWithout0xException extends Error { + code = ErrorCode.ParameterInvalid + + constructor(hex: string) { + super(`Hex string ${hex} should start with 0x`) + } +} + +export default { + HexStringException, + HexStringWithout0xException, +} diff --git a/packages/ckb-sdk-utils/src/index.ts b/packages/ckb-sdk-utils/src/index.ts index 9b1cbef8..77e817ea 100644 --- a/packages/ckb-sdk-utils/src/index.ts +++ b/packages/ckb-sdk-utils/src/index.ts @@ -3,7 +3,7 @@ import ECPair from './ecpair' import { hexToBytes } from './convertors' import { pubkeyToAddress, AddressOptions } from './address' import { assertToBeHexStringOrBigint } from './validators' -import { ArgumentRequired } from './exceptions' +import { ParameterRequiredException } from './exceptions' import crypto from './crypto' import { serializeScript } from './serialization/script' import { serializeRawTransaction, serializeTransaction, serializeWitnessArgs } from './serialization/transaction' @@ -17,7 +17,7 @@ export { serializeScript, serializeRawTransaction, serializeTransaction, seriali export const { blake2b, bech32, blake160 } = crypto export const scriptToHash = (script: CKBComponents.Script) => { - if (!script) throw new ArgumentRequired('Script') + if (!script) throw new ParameterRequiredException('Script') const serializedScript = serializeScript(script) const s = blake2b(32, null, null, PERSONAL) s.update(hexToBytes(serializedScript)) @@ -26,7 +26,7 @@ export const scriptToHash = (script: CKBComponents.Script) => { } export const rawTransactionToHash = (rawTransaction: Omit) => { - if (!rawTransaction) throw new ArgumentRequired('Raw transaction') + if (!rawTransaction) throw new ParameterRequiredException('Raw transaction') const serializedRawTransaction = serializeRawTransaction(rawTransaction) const s = blake2b(32, null, null, PERSONAL) s.update(hexToBytes(serializedRawTransaction)) diff --git a/packages/ckb-sdk-utils/src/serialization/script.ts b/packages/ckb-sdk-utils/src/serialization/script.ts index b34278af..5caf4ecd 100644 --- a/packages/ckb-sdk-utils/src/serialization/script.ts +++ b/packages/ckb-sdk-utils/src/serialization/script.ts @@ -1,5 +1,5 @@ import { serializeArray, serializeTable, serializeFixVec } from './basic' -import { ArgumentRequired } from '../exceptions' +import { ParameterRequiredException } from '../exceptions' export const serializeCodeHash = (codeHash: CKBComponents.Hash256) => serializeArray(codeHash) @@ -12,7 +12,7 @@ export const serializeHashType = (hashType: CKBComponents.ScriptHashType) => { export const serializeArgs = (args: string) => serializeFixVec(args) export const serializeScript = (script: CKBComponents.Script) => { - if (!script) throw new ArgumentRequired('Script') + if (!script) throw new ParameterRequiredException('Script') const { codeHash = '', hashType, args = '' } = script const serializedCodeHash = serializeCodeHash(codeHash) const serializedHashType = serializeHashType(hashType) diff --git a/packages/ckb-sdk-utils/src/validators.ts b/packages/ckb-sdk-utils/src/validators.ts index 035c76b4..df5d70d9 100644 --- a/packages/ckb-sdk-utils/src/validators.ts +++ b/packages/ckb-sdk-utils/src/validators.ts @@ -1,9 +1,14 @@ -import { InvalidHexString, HexStringShouldStartWith0x, InvalidAddress, InvalidAddressPayload } from './exceptions' +import { + HexStringException, + HexStringWithout0xException, + AddressException, + AddressPayloadException, +} from './exceptions' import { parseAddress } from './address' export const assertToBeHexString = (value: string) => { if (typeof value !== 'string' || !value.startsWith('0x') || Number.isNaN(+value)) { - throw new InvalidHexString(value) + throw new HexStringException(value) } return true } @@ -14,7 +19,7 @@ export const assertToBeHexStringOrBigint = (value: string | bigint) => { } if (typeof value === 'string') { if (!value.startsWith('0x')) { - throw new HexStringShouldStartWith0x(value) + throw new HexStringWithout0xException(value) } return true } @@ -23,20 +28,20 @@ export const assertToBeHexStringOrBigint = (value: string | bigint) => { export const assertToBeAddressPayload = (payload: string) => { if (!payload.startsWith('0x0100') || payload.length !== 46) { - throw new InvalidAddressPayload(payload) + throw new AddressPayloadException(payload) } return true } export const assertToBeAddress = (address: string) => { if (address.length !== 46) { - throw new InvalidAddress(address) + throw new AddressException(address) } try { const payload = parseAddress(address, 'hex') assertToBeAddressPayload(payload) } catch (err) { - throw new InvalidAddress(address) + throw new AddressException(address) } return true }