From 2f904ab61f230c11e6dcad02a87f0c74d6cb3c5f Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Fri, 27 May 2022 11:49:46 +0200 Subject: [PATCH] Util, Block: Removed 2-layer error structure, removed local package errors file, moved error codes to Util --- packages/block/src/errors.ts | 33 --------- packages/block/src/header/constructors.ts | 4 +- packages/block/src/header/header.ts | 82 ++++++++++++++--------- packages/util/src/errors.ts | 70 +++++++++++++++---- 4 files changed, 110 insertions(+), 79 deletions(-) delete mode 100644 packages/block/src/errors.ts diff --git a/packages/block/src/errors.ts b/packages/block/src/errors.ts deleted file mode 100644 index c12814bd59..0000000000 --- a/packages/block/src/errors.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { UsageError, ValidationError } from '@ethereumjs/util' - -import type { ValidationErrorType } from '@ethereumjs/util' - -/** - * Always define error codes on the generic Util - * error class level (e.g. associated to `ValidationError`) - */ -export enum ValidationErrorCode { - WRONG_TX_TRIE_LENGTH = 'WRONG_TX_TRIE_LENGTH', -} - -/** - * Additional types extending the generic Util - * error types (e.g. `ValidationErrorType`) - */ -export type HeaderValidationErrorType = - | { - block: string - received: string - } - | ValidationErrorType -export type HeaderUsageErrorType = { - block: string -} - -/** - * Dedicated error classes for the specific package, - * always to be subclassed from the generic Util error type - * classes (e.g. `ValidationError`, not: `EthereumJSError`) - */ -export class HeaderValidationError extends ValidationError {} -export class HeaderUsageError extends UsageError {} diff --git a/packages/block/src/header/constructors.ts b/packages/block/src/header/constructors.ts index 97222b970d..72ca874628 100644 --- a/packages/block/src/header/constructors.ts +++ b/packages/block/src/header/constructors.ts @@ -1,5 +1,5 @@ import { RLP } from '@ethereumjs/rlp' -import { bigIntToBytes, equalsBytes } from '@ethereumjs/util' +import { ErrorCode, ValueError, bigIntToBytes, equalsBytes } from '@ethereumjs/util' import { generateCliqueBlockExtraData } from '../consensus/clique.js' import { numberToHex, valuesArrayToHeaderData } from '../helpers.js' @@ -66,7 +66,7 @@ export function createBlockHeaderFromRLP( ) { const values = RLP.decode(serializedHeaderData) if (!Array.isArray(values)) { - throw new Error('Invalid serialized header input. Must be array') + throw new ValueError('Invalid serialized header input. Must be array', ErrorCode.INVALID_VALUE) } return createBlockHeaderFromBytesArray(values as Uint8Array[], opts) } diff --git a/packages/block/src/header/header.ts b/packages/block/src/header/header.ts index 1ec9669f55..a7f9f1a874 100644 --- a/packages/block/src/header/header.ts +++ b/packages/block/src/header/header.ts @@ -6,9 +6,12 @@ import { BIGINT_1, BIGINT_2, BIGINT_7, + ErrorCode, KECCAK256_RLP, KECCAK256_RLP_ARRAY, TypeOutput, + UsageError, + ValueError, bigIntToHex, bigIntToUnpaddedBytes, bytesToHex, @@ -26,7 +29,6 @@ import { CLIQUE_EXTRA_VANITY, cliqueIsEpochTransition, } from '../consensus/clique.js' -import { HeaderValidationError, ValidationErrorCode } from '../errors.js' import { fakeExponential } from '../helpers.js' import { paramsBlock } from '../params.js' @@ -77,10 +79,13 @@ export class BlockHeader { */ get prevRandao() { if (!this.common.isActivatedEIP(4399)) { - const msg = this._errorMsg( + throw new UsageError( 'The prevRandao parameter can only be accessed when EIP-4399 is activated', + ErrorCode.EIP_NOT_ACTIVATED, + { + objectContext: this.errorStr(), + }, ) - throw new Error(msg) } return this.mixHash } @@ -181,7 +186,10 @@ export class BlockHeader { toType(headerData.requestsRoot, TypeOutput.Uint8Array) ?? hardforkDefaults.requestsRoot if (!this.common.isActivatedEIP(1559) && baseFeePerGas !== undefined) { - throw new Error('A base fee for a block can only be set with EIP1559 being activated') + throw new UsageError( + 'A base fee for a block can only be set with EIP1559 being activated', + ErrorCode.EIP_NOT_ACTIVATED, + ) } if (!this.common.isActivatedEIP(4895) && withdrawalsRoot !== undefined) { @@ -260,38 +268,41 @@ export class BlockHeader { const { parentHash, stateRoot, transactionsTrie, receiptTrie, mixHash, nonce } = this if (parentHash.length !== 32) { - const msg = this._errorMsg(`parentHash must be 32 bytes, received ${parentHash.length} bytes`) - throw new Error(msg) + throw new ValueError(`parentHash must be 32 bytes`, ErrorCode.INVALID_VALUE_LENGTH, { + objectContext: this.errorStr(), + received: `${parentHash.length} bytes`, + }) } if (stateRoot.length !== 32) { - const msg = this._errorMsg(`stateRoot must be 32 bytes, received ${stateRoot.length} bytes`) - throw new Error(msg) + throw new ValueError(`stateRoot must be 32 bytes`, ErrorCode.INVALID_VALUE_LENGTH, { + objectContext: this.errorStr(), + received: `${stateRoot.length} bytes`, + }) } if (transactionsTrie.length !== 32) { - const e = new HeaderValidationError( - 'transactionsTrie must be 32 bytes', - ValidationErrorCode.WRONG_TX_TRIE_LENGTH, - { - block: this.errorStr(), - received: `${bytesToHex(transactionsTrie)} (${transactionsTrie.length} bytes)`, - }, - ) - throw e + throw new ValueError('transactionsTrie must be 32 bytes', ErrorCode.INVALID_VALUE_LENGTH, { + objectContext: this.errorStr(), + received: `${bytesToHex(transactionsTrie)} (${transactionsTrie.length} bytes)`, + }) } if (receiptTrie.length !== 32) { - const msg = this._errorMsg( - `receiptTrie must be 32 bytes, received ${receiptTrie.length} bytes`, - ) - throw new Error(msg) + throw new ValueError('receiptTrie must be 32 bytes', ErrorCode.INVALID_VALUE_LENGTH, { + objectContext: this.errorStr(), + received: `${bytesToHex(receiptTrie)} (${receiptTrie.length} bytes)`, + }) } if (mixHash.length !== 32) { - const msg = this._errorMsg(`mixHash must be 32 bytes, received ${mixHash.length} bytes`) - throw new Error(msg) + throw new ValueError('mixHash must be 32 bytes', ErrorCode.INVALID_VALUE_LENGTH, { + objectContext: this.errorStr(), + received: `${bytesToHex(mixHash)} (${mixHash.length} bytes)`, + }) } if (nonce.length !== 8) { - const msg = this._errorMsg(`nonce must be 8 bytes, received ${nonce.length} bytes`) - throw new Error(msg) + throw new ValueError('nonce must be 8 bytes', ErrorCode.INVALID_VALUE_LENGTH, { + objectContext: this.errorStr(), + received: `${bytesToHex(nonce)} (${nonce.length} bytes)`, + }) } // check if the block used too much gas @@ -436,8 +447,9 @@ export class BlockHeader { } } if (error) { - const msg = this._errorMsg(`Invalid PoS block: ${errorMsg}`) - throw new Error(msg) + throw new ValueError(`Invalid PoS block${errorMsg}`, ErrorCode.INVALID_OBJECT, { + objectContext: this.errorStr(), + }) } } } @@ -666,14 +678,22 @@ export class BlockHeader { */ ethashCanonicalDifficulty(parentBlockHeader: BlockHeader): bigint { if (this.common.consensusType() !== ConsensusType.ProofOfWork) { - const msg = this._errorMsg('difficulty calculation is only supported on PoW chains') - throw new Error(msg) + throw new UsageError( + 'difficulty calculation is only supported on PoW chains', + ErrorCode.INVALID_METHOD_CALL, + { + objectContext: this.errorStr(), + }, + ) } if (this.common.consensusAlgorithm() !== ConsensusAlgorithm.Ethash) { - const msg = this._errorMsg( + throw new UsageError( 'difficulty calculation currently only supports the ethash algorithm', + ErrorCode.INVALID_METHOD_CALL, + { + objectContext: this.errorStr(), + }, ) - throw new Error(msg) } const blockTs = this.timestamp const { timestamp: parentTs, difficulty: parentDif } = parentBlockHeader diff --git a/packages/util/src/errors.ts b/packages/util/src/errors.ts index a781f2cbf1..1eba2d31dc 100644 --- a/packages/util/src/errors.ts +++ b/packages/util/src/errors.ts @@ -4,18 +4,22 @@ * Kudos to https://github.com/ChainSafe/lodestar monorepo * for the inspiration :-) */ -export class EthereumJSError extends Error { +type EthereumJSErrorType = { + objectContext: string +} + +export class EthereumJSError extends Error { code: string - errorContext: T + context: T | {} - constructor(msg: string, code: string, errorContext: T) { + constructor(msg: string, code: string, context?: T) { super(msg) this.code = code - this.errorContext = errorContext + this.context = context ?? {} } - getErrorContext(): Record { - return { code: this.code, ...this.errorContext } + getContext(): Record { + return { code: this.code, ...this.context } } /** @@ -23,26 +27,66 @@ export class EthereumJSError extends Error { */ toObject(): Record { return { - ...this.getErrorContext(), + ...this.getContext(), stack: this.stack ?? '', } } } -export type ValidationErrorType = { - received: string +/** + * Error Codes + */ +export enum ErrorCode { + // Value Errors + INVALID_OBJECT = 'INVALID_OBJECT', + INVALID_VALUE = 'INVALID_VALUE', + INVALID_VALUE_LENGTH = 'INVALID_VALUE_LENGTH', + TOO_FEW_VALUES = 'TOO_FEW_VALUES', + TOO_MANY_VALUES = 'TOO_MANY_VALUES', + + // Usage Errors + EIP_NOT_ACTIVATED = 'EIP_NOT_ACTIVATED', + INCOMPATIBLE_LIBRARY_VERSION = 'INCOMPATIBLE_LIBRARY_VERSION', + INVALID_OPTION_USAGE = 'INVALID_OPTION_USAGE', + INVALID_METHOD_CALL = 'INVALID_METHOD_CALL', } /** - * Error along Object Validation + * Generic error types + */ + +/** + * Type flavors for ValueError + */ +export type ValueErrorType = + | { + received: string + } + | { + objectContext: string + received: string + } + | EthereumJSErrorType + +/** + * Type flavors for UsageError + */ +export type UsageErrorType = EthereumJSErrorType + +/** + * Generic Errors * - * Use directly or in a subclassed context for error comparison (`e instanceof ValidationError`) + * Use directly or in a subclassed context for error comparison (`e instanceof ValueError`) + */ + +/** + * Error along Object Value */ -export class ValidationError extends EthereumJSError {} +export class ValueError extends EthereumJSError {} /** * Error along API Usage * * Use directly or in a subclassed context for error comparison (`e instanceof UsageError`) */ -export class UsageError extends EthereumJSError {} +export class UsageError extends EthereumJSError {}