From d00b7bf92cf88fb39a1667b70209e87d0ac666b2 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 1 Oct 2024 13:46:08 +0200 Subject: [PATCH 1/7] evm/util: error overhaul --- packages/evm/src/{exceptions.ts => errors.ts} | 49 +++- packages/evm/src/evm.ts | 76 ++++-- packages/evm/src/index.ts | 11 +- packages/evm/src/interpreter.ts | 72 ++++-- packages/evm/src/opcodes/EIP2200.ts | 4 +- packages/evm/src/opcodes/functions.ts | 217 +++++++++++++++--- packages/evm/src/opcodes/gas.ts | 121 ++++++++-- packages/evm/src/opcodes/util.ts | 8 +- .../evm/src/precompiles/08-bn254-pairing.ts | 10 +- packages/evm/src/precompiles/09-blake2f.ts | 12 +- .../precompiles/0a-kzg-point-evaluation.ts | 42 +++- .../evm/src/precompiles/0b-bls12-g1add.ts | 18 +- .../evm/src/precompiles/0c-bls12-g1mul.ts | 18 +- .../evm/src/precompiles/0d-bls12-g1msm.ts | 34 ++- .../evm/src/precompiles/0e-bls12-g2add.ts | 18 +- .../evm/src/precompiles/0f-bls12-g2mul.ts | 18 +- .../evm/src/precompiles/10-bls12-g2msm.ts | 26 ++- .../evm/src/precompiles/11-bls12-pairing.ts | 26 ++- .../src/precompiles/12-bls12-map-fp-to-g1.ts | 18 +- .../src/precompiles/13-bls12-map-fp2-to-g2.ts | 18 +- packages/evm/src/precompiles/bls12_381/mcl.ts | 37 ++- .../evm/src/precompiles/bls12_381/noble.ts | 27 ++- packages/evm/src/precompiles/bn254/noble.ts | 17 +- packages/evm/src/stack.ts | 42 +++- packages/evm/src/types.ts | 2 +- packages/util/src/errors.ts | 44 ++++ packages/util/src/index.ts | 5 + 27 files changed, 812 insertions(+), 178 deletions(-) rename packages/evm/src/{exceptions.ts => errors.ts} (54%) create mode 100644 packages/util/src/errors.ts diff --git a/packages/evm/src/exceptions.ts b/packages/evm/src/errors.ts similarity index 54% rename from packages/evm/src/exceptions.ts rename to packages/evm/src/errors.ts index 9063f818ed5..32f385f78f8 100644 --- a/packages/evm/src/exceptions.ts +++ b/packages/evm/src/errors.ts @@ -1,4 +1,9 @@ -export enum ERROR { +import { EthereumJSError } from '@ethereumjs/util' + +import type { EOFError } from './eof/errors.js' + +// TODO: merge EOF errors in here +export enum RuntimeErrorMessage { OUT_OF_GAS = 'out of gas', CODESTORE_OUT_OF_GAS = 'code store out of gas', CODESIZE_EXCEEDS_MAXIMUM = 'code size to deposit exceeds maximum code size', @@ -15,13 +20,11 @@ export enum ERROR { REFUND_EXHAUSTED = 'refund exhausted', VALUE_OVERFLOW = 'value overflow', INSUFFICIENT_BALANCE = 'insufficient balance', - INVALID_BEGINSUB = 'invalid BEGINSUB', - INVALID_RETURNSUB = 'invalid RETURNSUB', - INVALID_JUMPSUB = 'invalid JUMPSUB', INVALID_BYTECODE_RESULT = 'invalid bytecode deployed', INITCODE_SIZE_VIOLATION = 'initcode exceeds max initcode size', INVALID_INPUT_LENGTH = 'invalid input length', INVALID_EOF_FORMAT = 'invalid EOF format', + INVALID_PRECOMPILE = 'invalid precompile', // BLS errors BLS_12_381_INVALID_INPUT_LENGTH = 'invalid input length', @@ -38,12 +41,38 @@ export enum ERROR { INVALID_PROOF = 'kzg proof invalid', } -export class EvmError { - error: ERROR - errorType: string +export enum EvmErrorCode { + UNSUPPORTED_FEATURE = 'EVM_ERROR_UNSUPPORTED_FEATURE', + RUNTIME_ERROR = 'EVM_ERROR_RUNTIME_ERROR', +} + +type EvmRuntimeErrorType = { + code: EvmErrorCode.RUNTIME_ERROR + reason: RuntimeErrorMessage | EOFError +} & ( + | { reason: RuntimeErrorMessage.REVERT; revertBytes: Uint8Array } + | { reason: Exclude | EOFError } +) + +export type EvmErrorType = { code: EvmErrorCode.UNSUPPORTED_FEATURE } | EvmRuntimeErrorType - constructor(error: ERROR) { - this.error = error - this.errorType = 'EvmError' +export class EvmError extends EthereumJSError { + constructor(type: EvmErrorType, message?: string) { + super(type, message) } } + +export function getRuntimeError(error: EvmError): RuntimeErrorMessage | EOFError | undefined { + if (error.type.code === EvmErrorCode.RUNTIME_ERROR) { + return error.type.reason + } +} + +/* +throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.REVERT +}) + + +*/ diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 869b479db77..9766f23d72e 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -20,7 +20,7 @@ import debugDefault from 'debug' import { FORMAT } from './eof/constants.js' import { isEOF } from './eof/util.js' -import { ERROR, EvmError } from './exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage, getRuntimeError } from './errors.js' import { Interpreter } from './interpreter.js' import { Journal } from './journal.js' import { EVMPerformanceLogger } from './logger.js' @@ -184,12 +184,18 @@ export class EVM implements EVMInterface { for (const eip of this.common.eips()) { if (!supportedEIPs.includes(eip)) { - throw new Error(`EIP-${eip} is not supported by the EVM`) + throw new EvmError( + { code: EvmErrorCode.UNSUPPORTED_FEATURE }, + `EIP-${eip} is not supported by the EVM`, + ) } } if (!EVM.supportedHardforks.includes(this.common.hardfork() as Hardfork)) { - throw new Error( + throw new EvmError( + { + code: EvmErrorCode.UNSUPPORTED_FEATURE, + }, `Hardfork ${this.common.hardfork()} not set as supported in supportedHardforks`, ) } @@ -418,7 +424,10 @@ export class EVM implements EVMInterface { createdAddress: message.to, execResult: { returnValue: new Uint8Array(0), - exceptionError: new EvmError(ERROR.INITCODE_SIZE_VIOLATION), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INITCODE_SIZE_VIOLATION, + }), executionGasUsed: message.gasLimit, }, } @@ -475,7 +484,10 @@ export class EVM implements EVMInterface { createdAddress: message.to, execResult: { returnValue: new Uint8Array(0), - exceptionError: new EvmError(ERROR.CREATE_COLLISION), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.CREATE_COLLISION, + }), executionGasUsed: message.gasLimit, }, } @@ -770,8 +782,8 @@ export class EVM implements EVMInterface { let gasUsed = message.gasLimit - interpreterRes.runState!.gasLeft if (interpreterRes.exceptionError) { if ( - interpreterRes.exceptionError.error !== ERROR.REVERT && - interpreterRes.exceptionError.error !== ERROR.INVALID_EOF_FORMAT + getRuntimeError(interpreterRes.exceptionError) !== RuntimeErrorMessage.REVERT && + getRuntimeError(interpreterRes.exceptionError) !== RuntimeErrorMessage.INVALID_EOF_FORMAT ) { gasUsed = message.gasLimit } @@ -907,7 +919,7 @@ export class EVM implements EVMInterface { const { executionGasUsed, exceptionError, returnValue } = result.execResult debug( `Received message execResult: [ gasUsed=${executionGasUsed} exceptionError=${ - exceptionError ? `'${exceptionError.error}'` : 'none' + exceptionError ? `'${getRuntimeError(exceptionError)}'` : 'none' } returnValue=${short(returnValue)} gasRefund=${result.execResult.gasRefund ?? 0} ]`, ) } @@ -917,14 +929,17 @@ export class EVM implements EVMInterface { // There is one exception: if the CODESTORE_OUT_OF_GAS error is thrown // (this only happens the Frontier/Chainstart fork) // then the error is dismissed - if (err && err.error !== ERROR.CODESTORE_OUT_OF_GAS) { + if (err && getRuntimeError(err) !== RuntimeErrorMessage.CODESTORE_OUT_OF_GAS) { result.execResult.selfdestruct = new Set() result.execResult.createdAddresses = new Set() result.execResult.gasRefund = BIGINT_0 } if ( err && - !(this.common.hardfork() === Hardfork.Chainstart && err.error === ERROR.CODESTORE_OUT_OF_GAS) + !( + this.common.hardfork() === Hardfork.Chainstart && + getRuntimeError(err) === RuntimeErrorMessage.CODESTORE_OUT_OF_GAS + ) ) { result.execResult.logs = [] await this.journal.revert() @@ -993,7 +1008,10 @@ export class EVM implements EVMInterface { gasLimit: bigint, ): Promise | ExecResult { if (typeof code !== 'function') { - throw new Error('Invalid precompile') + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_PRECOMPILE, + }) } const opts = { @@ -1053,7 +1071,10 @@ export class EVM implements EVMInterface { protected async _reduceSenderBalance(account: Account, message: Message): Promise { account.balance -= message.value if (account.balance < BIGINT_0) { - throw new EvmError(ERROR.INSUFFICIENT_BALANCE) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INSUFFICIENT_BALANCE, + }) } const result = this.journal.putAccount(message.caller, account) if (this.DEBUG) { @@ -1065,7 +1086,10 @@ export class EVM implements EVMInterface { protected async _addToBalance(toAccount: Account, message: MessageWithTo): Promise { const newBalance = toAccount.balance + message.value if (newBalance > MAX_INTEGER) { - throw new EvmError(ERROR.VALUE_OVERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.VALUE_OVERFLOW, + }) } toAccount.balance = newBalance // putAccount as the nonce may have changed for contract creation @@ -1114,11 +1138,15 @@ export class EVM implements EVMInterface { } } +// TODO clean me up export function OOGResult(gasLimit: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasLimit, - exceptionError: new EvmError(ERROR.OUT_OF_GAS), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_GAS, + }), } } // CodeDeposit OOG Result @@ -1126,7 +1154,10 @@ export function COOGResult(gasUsedCreateCode: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasUsedCreateCode, - exceptionError: new EvmError(ERROR.CODESTORE_OUT_OF_GAS), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_GAS, + }), } } @@ -1134,7 +1165,10 @@ export function INVALID_BYTECODE_RESULT(gasLimit: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasLimit, - exceptionError: new EvmError(ERROR.INVALID_BYTECODE_RESULT), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_BYTECODE_RESULT, + }), } } @@ -1142,7 +1176,10 @@ export function INVALID_EOF_RESULT(gasLimit: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasLimit, - exceptionError: new EvmError(ERROR.INVALID_EOF_FORMAT), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_EOF_FORMAT, + }), } } @@ -1150,7 +1187,10 @@ export function CodesizeExceedsMaximumError(gasUsed: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasUsed, - exceptionError: new EvmError(ERROR.CODESIZE_EXCEEDS_MAXIMUM), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.CODESIZE_EXCEEDS_MAXIMUM, + }), } } diff --git a/packages/evm/src/index.ts b/packages/evm/src/index.ts index 2386c948def..0431c3aa607 100644 --- a/packages/evm/src/index.ts +++ b/packages/evm/src/index.ts @@ -1,6 +1,11 @@ import { EOFContainer, validateEOF } from './eof/container.js' +import { + RuntimeErrorMessage as EVMRuntimeErrorMessage, + EvmError, + EvmErrorCode, + EvmErrorType, +} from './errors.js' import { EVM } from './evm.js' -import { ERROR as EVMErrorMessage, EvmError } from './exceptions.js' import { Message } from './message.js' import { getOpcodesForHF } from './opcodes/index.js' import { @@ -47,8 +52,10 @@ export { EOFContainer, EVM, EvmError, - EVMErrorMessage, + EvmErrorCode, + EvmErrorType, EVMMockBlockchain, + EVMRuntimeErrorMessage, getActivePrecompiles, getOpcodesForHF, MCLBLS, diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index f3619e9648b..eb41703c690 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -16,7 +16,7 @@ import { FORMAT, MAGIC, VERSION } from './eof/constants.js' import { EOFContainerMode, validateEOF } from './eof/container.js' import { setupEOF } from './eof/setup.js' import { ContainerSectionType } from './eof/verify.js' -import { ERROR, EvmError } from './exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage, getRuntimeError } from './errors.js' import { type EVMPerformanceLogger, type Timer } from './logger.js' import { Memory } from './memory.js' import { Message } from './message.js' @@ -212,14 +212,20 @@ export class Interpreter { // Bytecode contains invalid EOF magic byte return { runState: this._runState, - exceptionError: new EvmError(ERROR.INVALID_BYTECODE_RESULT), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_BYTECODE_RESULT, + }), } } if (code[2] !== VERSION) { // Bytecode contains invalid EOF version number return { runState: this._runState, - exceptionError: new EvmError(ERROR.INVALID_EOF_FORMAT), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_EOF_FORMAT, + }), } } this._runState.code = code @@ -232,7 +238,10 @@ export class Interpreter { } catch (e) { return { runState: this._runState, - exceptionError: new EvmError(ERROR.INVALID_EOF_FORMAT), // TODO: verify if all gas should be consumed + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_EOF_FORMAT, + }), // TODO: verify if all gas should be consumed } } @@ -249,7 +258,10 @@ export class Interpreter { // Trying to deploy an invalid EOF container return { runState: this._runState, - exceptionError: new EvmError(ERROR.INVALID_EOF_FORMAT), // TODO: verify if all gas should be consumed + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_EOF_FORMAT, + }), // TODO: verify if all gas should be consumed } } } @@ -331,7 +343,7 @@ export class Interpreter { throw e } // STOP is not an exception - if (e.error !== ERROR.STOP) { + if (getRuntimeError(e) !== RuntimeErrorMessage.STOP) { err = e } break @@ -391,7 +403,10 @@ export class Interpreter { // Check for invalid opcode if (opInfo.isInvalid) { - throw new EvmError(ERROR.INVALID_OPCODE) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }) } // Reduce opcode's base fee @@ -546,7 +561,9 @@ export class Interpreter { } if (this._runState.gasLeft < BIGINT_0) { this._runState.gasLeft = BIGINT_0 - trap(ERROR.OUT_OF_GAS) + trap( + new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.OUT_OF_GAS }), + ) } } @@ -582,7 +599,12 @@ export class Interpreter { this._runState.gasRefund -= amount if (this._runState.gasRefund < BIGINT_0) { this._runState.gasRefund = BIGINT_0 - trap(ERROR.REFUND_EXHAUSTED) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.REFUND_EXHAUSTED, + }), + ) } } @@ -664,7 +686,7 @@ export class Interpreter { */ finish(returnData: Uint8Array): void { this._result.returnValue = returnData - trap(ERROR.STOP) + trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.STOP })) } /** @@ -674,7 +696,13 @@ export class Interpreter { */ revert(returnData: Uint8Array): void { this._result.returnValue = returnData - trap(ERROR.REVERT) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.REVERT, + revertBytes: returnData, + }), + ) } /** @@ -999,7 +1027,7 @@ export class Interpreter { if ( results.execResult.returnValue !== undefined && (!results.execResult.exceptionError || - results.execResult.exceptionError.error === ERROR.REVERT) + getRuntimeError(results.execResult.exceptionError) === RuntimeErrorMessage.REVERT) ) { this._runState.returnBytes = results.execResult.returnValue } @@ -1100,14 +1128,15 @@ export class Interpreter { // Set return buffer in case revert happened if ( results.execResult.exceptionError && - results.execResult.exceptionError.error === ERROR.REVERT + getRuntimeError(results.execResult.exceptionError) === RuntimeErrorMessage.REVERT ) { this._runState.returnBytes = results.execResult.returnValue } if ( !results.execResult.exceptionError || - results.execResult.exceptionError.error === ERROR.CODESTORE_OUT_OF_GAS + getRuntimeError(results.execResult.exceptionError) === + RuntimeErrorMessage.CODESTORE_OUT_OF_GAS ) { for (const addressToSelfdestructHex of selfdestruct) { this._result.selfdestruct.add(addressToSelfdestructHex) @@ -1213,7 +1242,7 @@ export class Interpreter { }) } - trap(ERROR.STOP) + trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.STOP })) } /** @@ -1221,11 +1250,18 @@ export class Interpreter { */ log(data: Uint8Array, numberOfTopics: number, topics: Uint8Array[]): void { if (numberOfTopics < 0 || numberOfTopics > 4) { - trap(ERROR.OUT_OF_RANGE) + trap( + new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.OUT_OF_GAS }), + ) } if (topics.length !== numberOfTopics) { - trap(ERROR.INTERNAL_ERROR) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INTERNAL_ERROR, + }), + ) } const log: Log = [this._env.address.bytes, topics, data] @@ -1242,7 +1278,7 @@ export class Interpreter { } else { // EOF mode, call was either EXTCALL / EXTDELEGATECALL / EXTSTATICCALL if (results.execResult.exceptionError !== undefined) { - if (results.execResult.exceptionError.error === ERROR.REVERT) { + if (getRuntimeError(results.execResult.exceptionError) === RuntimeErrorMessage.REVERT) { // Revert return BIGINT_1 } else { diff --git a/packages/evm/src/opcodes/EIP2200.ts b/packages/evm/src/opcodes/EIP2200.ts index 5f08305d093..46226877d07 100644 --- a/packages/evm/src/opcodes/EIP2200.ts +++ b/packages/evm/src/opcodes/EIP2200.ts @@ -1,6 +1,6 @@ import { equalsBytes } from '@ethereumjs/util' -import { ERROR } from '../exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { adjustSstoreGasEIP2929 } from './EIP2929.js' import { trap } from './util.js' @@ -27,7 +27,7 @@ export function updateSstoreGasEIP2200( ) { // Fail if not enough gas is left if (runState.interpreter.getGasLeft() <= common.param('sstoreSentryEIP2200Gas')) { - trap(ERROR.OUT_OF_GAS) + trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.OUT_OF_GAS })) } // Noop diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 6b7ef888524..b87147c7b94 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -32,7 +32,7 @@ import { keccak256 } from 'ethereum-cryptography/keccak.js' import { EOFContainer, EOFContainerMode } from '../eof/container.js' import { EOFError } from '../eof/errors.js' import { EOFBYTES, EOFHASH, isEOF } from '../eof/util.js' -import { ERROR } from '../exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { DELEGATION_7702_FLAG } from '../types.js' import { @@ -82,7 +82,7 @@ export const handlers: Map = new Map([ [ 0x00, function () { - trap(ERROR.STOP) + trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.STOP })) }, ], // 0x01: ADD @@ -848,13 +848,23 @@ export const handlers: Map = new Map([ function (runState) { const dest = runState.stack.pop() if (dest > runState.interpreter.getCodeSize()) { - trap(ERROR.INVALID_JUMP + ' at ' + describeLocation(runState)) + trap( + new EvmError( + { code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.INVALID_JUMP }, + RuntimeErrorMessage.INVALID_JUMP + ' at ' + describeLocation(runState), + ), + ) } const destNum = Number(dest) if (!jumpIsValid(runState, destNum)) { - trap(ERROR.INVALID_JUMP + ' at ' + describeLocation(runState)) + trap( + new EvmError( + { code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.INVALID_JUMP }, + RuntimeErrorMessage.INVALID_JUMP + ' at ' + describeLocation(runState), + ), + ) } runState.programCounter = destNum @@ -867,13 +877,23 @@ export const handlers: Map = new Map([ const [dest, cond] = runState.stack.popN(2) if (cond !== BIGINT_0) { if (dest > runState.interpreter.getCodeSize()) { - trap(ERROR.INVALID_JUMP + ' at ' + describeLocation(runState)) + trap( + new EvmError( + { code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.INVALID_JUMP }, + RuntimeErrorMessage.INVALID_JUMP + ' at ' + describeLocation(runState), + ), + ) } const destNum = Number(dest) if (!jumpIsValid(runState, destNum)) { - trap(ERROR.INVALID_JUMP + ' at ' + describeLocation(runState)) + trap( + new EvmError( + { code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.INVALID_JUMP }, + RuntimeErrorMessage.INVALID_JUMP + ' at ' + describeLocation(runState), + ), + ) } runState.programCounter = destNum @@ -920,7 +940,12 @@ export const handlers: Map = new Map([ function (runState) { // TSTORE if (runState.interpreter.isStatic()) { - trap(ERROR.STATIC_STATE_CHANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + }), + ) } const [key, val] = runState.stack.popN(2) @@ -961,7 +986,12 @@ export const handlers: Map = new Map([ runState.programCounter + numToPush > runState.code.length && common.isActivatedEIP(3540) ) { - trap(ERROR.OUT_OF_RANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_RANGE, + }), + ) } if (common.isActivatedEIP(6800) && runState.env.chargeCodeAccesses === true) { @@ -1031,7 +1061,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const pos = runState.stack.pop() if (pos > runState.env.eof!.container.body.dataSection.length) { @@ -1056,7 +1091,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const toLoad = Number( bytesToBigInt(runState.code.subarray(runState.programCounter, runState.programCounter + 2)), @@ -1074,7 +1114,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } runState.stack.push(BigInt(runState.env.eof!.container.body.dataSection.length)) }, @@ -1085,7 +1130,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const [memOffset, offset, size] = runState.stack.popN(3) if (size !== BIGINT_0) { @@ -1102,7 +1152,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } else { const code = runState.env.code const rjumpDest = new DataView(code.buffer).getInt16(runState.programCounter) @@ -1116,7 +1171,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } else { const cond = runState.stack.pop() // Move PC to the PC post instruction @@ -1136,7 +1196,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } else { const code = runState.env.code const jumptableEntries = code[runState.programCounter] @@ -1163,7 +1228,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const sectionTarget = bytesToInt( runState.code.slice(runState.programCounter, runState.programCounter + 2), @@ -1171,10 +1241,12 @@ export const handlers: Map = new Map([ const stackItems = runState.stack.length const typeSection = runState.env.eof!.container.body.typeSections[sectionTarget] if (1024 < stackItems + typeSection?.inputs - typeSection?.maxStackHeight) { - trap(EOFError.StackOverflow) + trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: EOFError.StackOverflow })) } if (runState.env.eof!.eofRunState.returnStack.length >= 1024) { - trap(EOFError.ReturnStackOverflow) + trap( + new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: EOFError.ReturnStackOverflow }), + ) } runState.env.eof?.eofRunState.returnStack.push(runState.programCounter + 2) @@ -1188,12 +1260,17 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const newPc = runState.env.eof!.eofRunState.returnStack.pop() if (newPc === undefined) { // This should NEVER happen since it is validated that functions either terminate (the call frame) or return - trap(EOFError.RetfNoReturn) + trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: EOFError.RetfNoReturn })) } runState.programCounter = newPc! }, @@ -1204,7 +1281,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } // NOTE: (and also TODO) this code is exactly the same as CALLF, except pushing to the return stack is now skipped // (and also the return stack overflow check) @@ -1215,7 +1297,7 @@ export const handlers: Map = new Map([ const stackItems = runState.stack.length const typeSection = runState.env.eof!.container.body.typeSections[sectionTarget] if (1024 < stackItems + typeSection?.inputs - typeSection?.maxStackHeight) { - trap(EOFError.StackOverflow) + trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: EOFError.StackOverflow })) } /*if (runState.env.eof!.eofRunState.returnStack.length >= 1024) { trap(EOFError.ReturnStackOverflow) @@ -1232,7 +1314,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const toDup = Number( @@ -1250,7 +1337,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const toSwap = Number( @@ -1268,7 +1360,12 @@ export const handlers: Map = new Map([ function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const toExchange = Number( bytesToBigInt(runState.code.subarray(runState.programCounter, runState.programCounter + 1)), @@ -1285,7 +1382,12 @@ export const handlers: Map = new Map([ async function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } else { // Read container index const containerIndex = runState.env.code[runState.programCounter] @@ -1321,7 +1423,12 @@ export const handlers: Map = new Map([ async function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } else { // Read container index const containerIndex = runState.env.code[runState.programCounter] @@ -1343,13 +1450,23 @@ export const handlers: Map = new Map([ const actualSectionSize = preDeployDataSectionSize + Number(auxDataSize) if (actualSectionSize < originalDataSize) { - trap(EOFError.InvalidReturnContractDataSize) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: EOFError.InvalidReturnContractDataSize, + }), + ) } if (actualSectionSize > 0xffff) { // Data section size is now larger than the max data section size // Temp: trap OOG? - trap(ERROR.OUT_OF_GAS) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } const newSize = setLengthLeft(bigIntToBytes(BigInt(actualSectionSize)), 2) @@ -1377,7 +1494,12 @@ export const handlers: Map = new Map([ length > Number(common.param('maxInitCodeSize')) && !runState.interpreter._evm.allowUnlimitedInitCodeSize ) { - trap(ERROR.INITCODE_SIZE_VIOLATION) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INITCODE_SIZE_VIOLATION, + }), + ) } const gasLimit = runState.messageGasLimit! @@ -1403,7 +1525,12 @@ export const handlers: Map = new Map([ 0xf5, async function (runState, common) { if (runState.interpreter.isStatic()) { - trap(ERROR.STATIC_STATE_CHANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + }), + ) } const [value, offset, length, salt] = runState.stack.popN(4) @@ -1413,7 +1540,12 @@ export const handlers: Map = new Map([ length > Number(common.param('maxInitCodeSize')) && !runState.interpreter._evm.allowUnlimitedInitCodeSize ) { - trap(ERROR.INITCODE_SIZE_VIOLATION) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INITCODE_SIZE_VIOLATION, + }), + ) } const gasLimit = runState.messageGasLimit! @@ -1524,7 +1656,12 @@ export const handlers: Map = new Map([ async function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } else { const [toAddr, inOffset, inLength, value] = runState.stack.popN(4) @@ -1558,7 +1695,12 @@ export const handlers: Map = new Map([ async function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } else { const value = runState.interpreter.getCallValue() const [toAddr, inOffset, inLength] = runState.stack.popN(3) @@ -1622,7 +1764,12 @@ export const handlers: Map = new Map([ async function (runState, _common) { if (runState.env.eof === undefined) { // Opcode not available in legacy contracts - trap(ERROR.INVALID_OPCODE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_OPCODE, + }), + ) } else { const value = BIGINT_0 const [toAddr, inOffset, inLength] = runState.stack.popN(3) diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index ad842a971ba..33685228d22 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -15,7 +15,7 @@ import { } from '@ethereumjs/util' import { EOFError } from '../eof/errors.js' -import { ERROR } from '../exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { DELEGATION_7702_FLAG } from '../types.js' import { updateSstoreGasEIP1283 } from './EIP1283.js' @@ -82,7 +82,12 @@ export const dynamicGasHandlers: Map 32) { - trap(ERROR.OUT_OF_RANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_RANGE, + }), + ) } const expPricePerByte = common.param('expByteGas') gas += BigInt(byteLength) * expPricePerByte @@ -262,7 +267,12 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { - trap(ERROR.STATIC_STATE_CHANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + }), + ) } const [key, val] = runState.stack.peek(2) @@ -447,7 +462,12 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { - trap(ERROR.STATIC_STATE_CHANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + }), + ) } const [memOffset, memLength] = runState.stack.peek(2) @@ -455,7 +475,12 @@ export const dynamicGasHandlers: Map 4) { - trap(ERROR.OUT_OF_RANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_RANGE, + }), + ) } gas += subMemUsage(runState, memOffset, memLength, common) @@ -538,7 +563,12 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { - trap(ERROR.STATIC_STATE_CHANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + }), + ) } const [_value, offset, length] = runState.stack.peek(3) @@ -573,7 +603,12 @@ export const dynamicGasHandlers: Map runState.interpreter.getGasLeft() - gas) { - trap(ERROR.OUT_OF_GAS) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_GAS, + }), + ) } if (gas > runState.interpreter.getGasLeft()) { - trap(ERROR.OUT_OF_GAS) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_GAS, + }), + ) } runState.messageGasLimit = gasLimit @@ -696,7 +741,12 @@ export const dynamicGasHandlers: Map runState.interpreter.getGasLeft() - gas) { - trap(ERROR.OUT_OF_GAS) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_GAS, + }), + ) } runState.messageGasLimit = gasLimit @@ -757,7 +807,12 @@ export const dynamicGasHandlers: Map runState.interpreter.getGasLeft() - gas) { - trap(ERROR.OUT_OF_GAS) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_GAS, + }), + ) } runState.messageGasLimit = gasLimit @@ -769,7 +824,12 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { - trap(ERROR.STATIC_STATE_CHANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + }), + ) } const [_value, offset, length, _salt] = runState.stack.peek(4) @@ -807,7 +867,12 @@ export const dynamicGasHandlers: Map 0, charge CALL_VALUE_COST @@ -817,7 +882,12 @@ export const dynamicGasHandlers: Map 20 bytes if (toAddr > EXTCALL_TARGET_MAX) { - trap(EOFError.InvalidExtcallTarget) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: EOFError.InvalidExtcallTarget, + }), + ) } // Charge for memory expansion @@ -880,7 +950,12 @@ export const dynamicGasHandlers: Map 20 bytes if (toAddr > EXTCALL_TARGET_MAX) { - trap(EOFError.InvalidExtcallTarget) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: EOFError.InvalidExtcallTarget, + }), + ) } // Charge for memory expansion @@ -978,7 +1053,12 @@ export const dynamicGasHandlers: Map 20 bytes if (toAddr > EXTCALL_TARGET_MAX) { - trap(EOFError.InvalidExtcallTarget) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: EOFError.InvalidExtcallTarget, + }), + ) } // Charge for memory expansion @@ -1030,7 +1110,12 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { - trap(ERROR.STATIC_STATE_CHANGE) + trap( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + }), + ) } const selfdestructToaddressBigInt = runState.stack.peek()[0] diff --git a/packages/evm/src/opcodes/util.ts b/packages/evm/src/opcodes/util.ts index 1dec494e1e9..a18d7d852c4 100644 --- a/packages/evm/src/opcodes/util.ts +++ b/packages/evm/src/opcodes/util.ts @@ -15,9 +15,7 @@ import { } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import { EvmError } from '../exceptions.js' - -import type { ERROR } from '../exceptions.js' +import type { EvmError } from '../errors.js' import type { RunState } from '../interpreter.js' import type { Common } from '@ethereumjs/common' import type { Address } from '@ethereumjs/util' @@ -51,9 +49,9 @@ export function setLengthLeftStorage(value: Uint8Array) { /** * Wraps error message as EvmError */ -export function trap(err: string) { +export function trap(err: EvmError) { // TODO: facilitate extra data along with errors - throw new EvmError(err as ERROR) + throw err } /** diff --git a/packages/evm/src/precompiles/08-bn254-pairing.ts b/packages/evm/src/precompiles/08-bn254-pairing.ts index 2d1e67ed3a0..953d45a8235 100644 --- a/packages/evm/src/precompiles/08-bn254-pairing.ts +++ b/packages/evm/src/precompiles/08-bn254-pairing.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { gasLimitCheck, moduloLengthCheck } from './util.js' @@ -14,7 +14,13 @@ import type { PrecompileInput } from './types.js' export function precompile08(opts: PrecompileInput): ExecResult { const pName = getPrecompileName('08') if (!moduloLengthCheck(opts, 192, pName)) { - return EvmErrorResult(new EvmError(ERROR.INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } const inputDataSize = BigInt(Math.floor(opts.data.length / 192)) diff --git a/packages/evm/src/precompiles/09-blake2f.ts b/packages/evm/src/precompiles/09-blake2f.ts index 8a5943389f5..d43edc95a2d 100644 --- a/packages/evm/src/precompiles/09-blake2f.ts +++ b/packages/evm/src/precompiles/09-blake2f.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { gasLimitCheck } from './util.js' @@ -171,7 +171,10 @@ export function precompile09(opts: PrecompileInput): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: opts.gasLimit, - exceptionError: new EvmError(ERROR.OUT_OF_RANGE), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_RANGE, + }), } } const lastByte = data.subarray(212, 213)[0] @@ -182,7 +185,10 @@ export function precompile09(opts: PrecompileInput): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: opts.gasLimit, - exceptionError: new EvmError(ERROR.OUT_OF_RANGE), + exceptionError: new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.OUT_OF_RANGE, + }), } } diff --git a/packages/evm/src/precompiles/0a-kzg-point-evaluation.ts b/packages/evm/src/precompiles/0a-kzg-point-evaluation.ts index 990fe37b2e3..88b1cf94d14 100644 --- a/packages/evm/src/precompiles/0a-kzg-point-evaluation.ts +++ b/packages/evm/src/precompiles/0a-kzg-point-evaluation.ts @@ -6,8 +6,8 @@ import { setLengthLeft, } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { gasLimitCheck } from './util.js' @@ -33,7 +33,13 @@ export async function precompile0a(opts: PrecompileInput): Promise { } if (opts.data.length !== 192) { - return EvmErrorResult(new EvmError(ERROR.INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } const version = Number(opts.common.param('blobCommitmentVersionKzg')) @@ -48,7 +54,13 @@ export async function precompile0a(opts: PrecompileInput): Promise { if (opts._debug !== undefined) { opts._debug(`${pName} failed: INVALID_COMMITMENT`) } - return EvmErrorResult(new EvmError(ERROR.INVALID_COMMITMENT), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_COMMITMENT, + }), + opts.gasLimit, + ) } if (opts._debug !== undefined) { @@ -61,19 +73,37 @@ export async function precompile0a(opts: PrecompileInput): Promise { try { const res = opts.common.customCrypto?.kzg?.verifyProof(commitment, z, y, kzgProof) if (res === false) { - return EvmErrorResult(new EvmError(ERROR.INVALID_PROOF), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_PROOF, + }), + opts.gasLimit, + ) } } catch (err: any) { if (err.message.includes('C_KZG_BADARGS') === true) { if (opts._debug !== undefined) { opts._debug(`${pName} failed: INVALID_INPUTS`) } - return EvmErrorResult(new EvmError(ERROR.INVALID_INPUTS), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_INPUTS, + }), + opts.gasLimit, + ) } if (opts._debug !== undefined) { opts._debug(`${pName} failed: Unknown error - ${err.message}`) } - return EvmErrorResult(new EvmError(ERROR.REVERT), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INTERNAL_ERROR, + }), + opts.gasLimit, + ) } // Return value - FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values diff --git a/packages/evm/src/precompiles/0b-bls12-g1add.ts b/packages/evm/src/precompiles/0b-bls12-g1add.ts index 9fd91e3b4ba..e13a35fea85 100644 --- a/packages/evm/src/precompiles/0b-bls12-g1add.ts +++ b/packages/evm/src/precompiles/0b-bls12-g1add.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' import { equalityLengthCheck, gasLimitCheck } from './util.js' @@ -22,7 +22,13 @@ export async function precompile0b(opts: PrecompileInput): Promise { } if (!equalityLengthCheck(opts, 256, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } // check if some parts of input are zero bytes. @@ -33,7 +39,13 @@ export async function precompile0b(opts: PrecompileInput): Promise { [192, 208], ] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } let returnValue diff --git a/packages/evm/src/precompiles/0c-bls12-g1mul.ts b/packages/evm/src/precompiles/0c-bls12-g1mul.ts index aedb5664050..b1416c86127 100644 --- a/packages/evm/src/precompiles/0c-bls12-g1mul.ts +++ b/packages/evm/src/precompiles/0c-bls12-g1mul.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' import { equalityLengthCheck, gasLimitCheck } from './util.js' @@ -22,7 +22,13 @@ export async function precompile0c(opts: PrecompileInput): Promise { } if (!equalityLengthCheck(opts, 160, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } // check if some parts of input are zero bytes. @@ -31,7 +37,13 @@ export async function precompile0c(opts: PrecompileInput): Promise { [64, 80], ] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } let returnValue diff --git a/packages/evm/src/precompiles/0d-bls12-g1msm.ts b/packages/evm/src/precompiles/0d-bls12-g1msm.ts index ac67b5be487..ea841d10389 100644 --- a/packages/evm/src/precompiles/0d-bls12-g1msm.ts +++ b/packages/evm/src/precompiles/0d-bls12-g1msm.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck, msmGasUsed } from './bls12_381/index.js' import { gasLimitCheck, moduloLengthCheck } from './util.js' @@ -21,7 +21,13 @@ export async function precompile0d(opts: PrecompileInput): Promise { if (opts._debug !== undefined) { opts._debug(`${pName} failed: Empty input`) } - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit) // follow Geth's implementation + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INPUT_EMPTY, + }), + opts.gasLimit, + ) // follow Geth's implementation } // TODO: Double-check respectively confirm that this order is really correct that the gas check @@ -39,10 +45,22 @@ export async function precompile0d(opts: PrecompileInput): Promise { if (opts._debug !== undefined) { opts._debug(`${pName} failed: Invalid input length length=${inputData.length}`) } - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } if (!moduloLengthCheck(opts, 160, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } // prepare pairing list and check for mandatory zero bytes @@ -55,7 +73,13 @@ export async function precompile0d(opts: PrecompileInput): Promise { // zero bytes check const pairStart = 160 * k if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } } diff --git a/packages/evm/src/precompiles/0e-bls12-g2add.ts b/packages/evm/src/precompiles/0e-bls12-g2add.ts index d1bc0e48077..41b4c3f3922 100644 --- a/packages/evm/src/precompiles/0e-bls12-g2add.ts +++ b/packages/evm/src/precompiles/0e-bls12-g2add.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' import { equalityLengthCheck, gasLimitCheck } from './util.js' @@ -22,7 +22,13 @@ export async function precompile0e(opts: PrecompileInput): Promise { } if (!equalityLengthCheck(opts, 512, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } // check if some parts of input are zero bytes. @@ -37,7 +43,13 @@ export async function precompile0e(opts: PrecompileInput): Promise { [448, 464], ] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } // TODO: verify that point is on G2 diff --git a/packages/evm/src/precompiles/0f-bls12-g2mul.ts b/packages/evm/src/precompiles/0f-bls12-g2mul.ts index 3e6e16d6eba..ecb3fd128aa 100644 --- a/packages/evm/src/precompiles/0f-bls12-g2mul.ts +++ b/packages/evm/src/precompiles/0f-bls12-g2mul.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' import { equalityLengthCheck, gasLimitCheck } from './util.js' @@ -22,7 +22,13 @@ export async function precompile0f(opts: PrecompileInput): Promise { } if (!equalityLengthCheck(opts, 288, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } // check if some parts of input are zero bytes. @@ -33,7 +39,13 @@ export async function precompile0f(opts: PrecompileInput): Promise { [192, 208], ] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } // TODO: verify that point is on G2 diff --git a/packages/evm/src/precompiles/10-bls12-g2msm.ts b/packages/evm/src/precompiles/10-bls12-g2msm.ts index b327e3ca6f5..a9d120df5d7 100644 --- a/packages/evm/src/precompiles/10-bls12-g2msm.ts +++ b/packages/evm/src/precompiles/10-bls12-g2msm.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck, msmGasUsed } from './bls12_381/index.js' import { gasLimitCheck, moduloLengthCheck } from './util.js' @@ -19,7 +19,13 @@ export async function precompile10(opts: PrecompileInput): Promise { if (opts._debug !== undefined) { opts._debug(`${pName} failed: Empty input`) } - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit) // follow Geth's implementation + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INPUT_EMPTY, + }), + opts.gasLimit, + ) // follow Geth's implementation } const numPairs = Math.floor(opts.data.length / 288) @@ -31,7 +37,13 @@ export async function precompile10(opts: PrecompileInput): Promise { } if (!moduloLengthCheck(opts, 288, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } // prepare pairing list and check for mandatory zero bytes @@ -46,7 +58,13 @@ export async function precompile10(opts: PrecompileInput): Promise { // zero bytes check const pairStart = 288 * k if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } } diff --git a/packages/evm/src/precompiles/11-bls12-pairing.ts b/packages/evm/src/precompiles/11-bls12-pairing.ts index fa8a03ecc95..cffeeb97607 100644 --- a/packages/evm/src/precompiles/11-bls12-pairing.ts +++ b/packages/evm/src/precompiles/11-bls12-pairing.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' import { gasLimitCheck, moduloLengthCheck } from './util.js' @@ -22,7 +22,13 @@ export async function precompile11(opts: PrecompileInput): Promise { if (opts._debug !== undefined) { opts._debug(`${pName} failed: Empty input`) } - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INPUT_EMPTY, + }), + opts.gasLimit, + ) } const gasUsedPerPair = opts.common.paramByEIP('bls12381PairingPerPairGas', 2537) ?? BigInt(0) @@ -31,7 +37,13 @@ export async function precompile11(opts: PrecompileInput): Promise { // gas check. I will keep it there to not side-change the existing implementation, but we should // check (respectively Jochem can maybe have a word) if this is something intended or not if (!moduloLengthCheck(opts, 384, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } const gasUsed = baseGas + gasUsedPerPair * BigInt(Math.floor(opts.data.length / 384)) @@ -52,7 +64,13 @@ export async function precompile11(opts: PrecompileInput): Promise { // zero bytes check const pairStart = 384 * k if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } } diff --git a/packages/evm/src/precompiles/12-bls12-map-fp-to-g1.ts b/packages/evm/src/precompiles/12-bls12-map-fp-to-g1.ts index 93381090db7..7bc27c48fd2 100644 --- a/packages/evm/src/precompiles/12-bls12-map-fp-to-g1.ts +++ b/packages/evm/src/precompiles/12-bls12-map-fp-to-g1.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' import { equalityLengthCheck, gasLimitCheck } from './util.js' @@ -22,13 +22,25 @@ export async function precompile12(opts: PrecompileInput): Promise { } if (!equalityLengthCheck(opts, 64, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } // check if some parts of input are zero bytes. const zeroByteRanges = [[0, 16]] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } let returnValue diff --git a/packages/evm/src/precompiles/13-bls12-map-fp2-to-g2.ts b/packages/evm/src/precompiles/13-bls12-map-fp2-to-g2.ts index ff4a60f3c46..aa5ae64f658 100644 --- a/packages/evm/src/precompiles/13-bls12-map-fp2-to-g2.ts +++ b/packages/evm/src/precompiles/13-bls12-map-fp2-to-g2.ts @@ -1,7 +1,7 @@ import { bytesToHex } from '@ethereumjs/util' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' import { equalityLengthCheck, gasLimitCheck } from './util.js' @@ -22,7 +22,13 @@ export async function precompile13(opts: PrecompileInput): Promise { } if (!equalityLengthCheck(opts, 128, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.INVALID_INPUT_LENGTH, + }), + opts.gasLimit, + ) } // check if some parts of input are zero bytes. @@ -31,7 +37,13 @@ export async function precompile13(opts: PrecompileInput): Promise { [64, 80], ] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) + return EvmErrorResult( + new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }), + opts.gasLimit, + ) } let returnValue diff --git a/packages/evm/src/precompiles/bls12_381/mcl.ts b/packages/evm/src/precompiles/bls12_381/mcl.ts index 133ef18cbed..f92cadc4faf 100644 --- a/packages/evm/src/precompiles/bls12_381/mcl.ts +++ b/packages/evm/src/precompiles/bls12_381/mcl.ts @@ -7,7 +7,7 @@ import { unprefixedHexToBytes, } from '@ethereumjs/util' -import { ERROR, EvmError } from '../../exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../../errors.js' import { BLS_FIELD_MODULUS, @@ -53,12 +53,18 @@ function BLS12_381_ToG1Point(input: Uint8Array, mcl: any, verifyOrder = true): a mcl.verifyOrderG1(verifyOrder) if (verifyOrder && G1.isValidOrder() === false) { - throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }) } // Check if these coordinates are actually on the curve. if (G1.isValid() === false) { - throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }) } return G1 @@ -121,11 +127,17 @@ function BLS12_381_ToG2Point(input: Uint8Array, mcl: any, verifyOrder = true): a mcl.verifyOrderG2(verifyOrder) if (verifyOrder && p.isValidOrder() === false) { - throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }) } if (p.isValid() === false) { - throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }) } return p @@ -165,7 +177,10 @@ function BLS12_381_ToFrPoint(input: Uint8Array, mcl: any): any { function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array, mcl: any): any { // check if point is in field if (bytesToBigInt(fpCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + }) } const fp = new mcl.Fp() @@ -181,10 +196,16 @@ function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array, mcl: any): any { function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array, mcl: any): any { // check if the coordinates are in the field if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + }) } if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + }) } const fp_x = new mcl.Fp() diff --git a/packages/evm/src/precompiles/bls12_381/noble.ts b/packages/evm/src/precompiles/bls12_381/noble.ts index ff6576ceb31..9fd8d10fb87 100644 --- a/packages/evm/src/precompiles/bls12_381/noble.ts +++ b/packages/evm/src/precompiles/bls12_381/noble.ts @@ -8,7 +8,7 @@ import { } from '@ethereumjs/util' import { bls12_381 } from 'ethereum-cryptography/bls.js' -import { ERROR, EvmError } from '../../exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../../errors.js' import { BLS_FIELD_MODULUS, @@ -59,7 +59,10 @@ function BLS12_381_ToG1Point(input: Uint8Array, verifyOrder = true) { G1.assertValidity() } catch (e) { if (verifyOrder || (e as Error).message !== 'bad point: not in prime-order subgroup') - throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }) } return G1 @@ -102,7 +105,10 @@ function BLS12_381_ToG2Point(input: Uint8Array, verifyOrder = true) { pG2.assertValidity() } catch (e) { if (verifyOrder || (e as Error).message !== 'bad point: not in prime-order subgroup') - throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + }) } return pG2 @@ -153,7 +159,10 @@ function BLS12_381_ToFrPoint(input: Uint8Array): bigint { function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array) { // check if point is in field if (bytesToBigInt(fpCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + }) } const FP = bls12_381.fields.Fp.fromBytes(fpCoordinate.slice(16)) return FP @@ -162,10 +171,16 @@ function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array) { function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array) { // check if the coordinates are in the field if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + }) } if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + }) } const fpBytes = concatBytes(fpXCoordinate.subarray(16), fpYCoordinate.subarray(16)) diff --git a/packages/evm/src/precompiles/bn254/noble.ts b/packages/evm/src/precompiles/bn254/noble.ts index d82745d8025..05561b826cd 100644 --- a/packages/evm/src/precompiles/bn254/noble.ts +++ b/packages/evm/src/precompiles/bn254/noble.ts @@ -9,7 +9,7 @@ import { } from '@ethereumjs/util' import { bn254 } from 'ethereum-cryptography/bn.js' -import { ERROR, EvmError } from '../../exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../../errors.js' import type { EVMBN254Interface } from '../../types.js' @@ -87,7 +87,10 @@ function toG2Point(input: Uint8Array) { for (const p of [p_x_1, p_x_2, p_y_1, p_y_2]) { const pB = bytesToBigInt(p) if (bn254.fields.Fp.create(pB) !== pB) { - throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BN254_FP_NOT_IN_FIELD, + }) } } @@ -106,10 +109,16 @@ function toG2Point(input: Uint8Array) { function toFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array) { if (bytesToBigInt(fpXCoordinate) >= bn254.fields.Fp2.ORDER) { - throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BN254_FP_NOT_IN_FIELD, + }) } if (bytesToBigInt(fpYCoordinate) >= bn254.fields.Fp2.ORDER) { - throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.BN254_FP_NOT_IN_FIELD, + }) } const fpBytes = concatBytes(fpXCoordinate, fpYCoordinate) diff --git a/packages/evm/src/stack.ts b/packages/evm/src/stack.ts index d8ff351cb24..4229f856a6c 100644 --- a/packages/evm/src/stack.ts +++ b/packages/evm/src/stack.ts @@ -1,4 +1,4 @@ -import { ERROR, EvmError } from './exceptions.js' +import { EvmError, EvmErrorCode, RuntimeErrorMessage } from './errors.js' /** * Implementation of the stack used in evm. @@ -23,7 +23,10 @@ export class Stack { push(value: bigint) { if (this._len >= this._maxHeight) { - throw new EvmError(ERROR.STACK_OVERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STACK_OVERFLOW, + }) } // Read current length, set `_store` to value, and then increase the length @@ -32,7 +35,10 @@ export class Stack { pop(): bigint { if (this._len < 1) { - throw new EvmError(ERROR.STACK_UNDERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STACK_UNDERFLOW, + }) } // Length is checked above, so pop shouldn't return undefined @@ -49,7 +55,10 @@ export class Stack { */ popN(num: number = 1): bigint[] { if (this._len < num) { - throw new EvmError(ERROR.STACK_UNDERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STACK_UNDERFLOW, + }) } if (num === 0) { @@ -79,7 +88,10 @@ export class Stack { for (let peek = 0; peek < num; peek++) { const index = --start if (index < 0) { - throw new EvmError(ERROR.STACK_UNDERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STACK_UNDERFLOW, + }) } peekArray[peek] = this._store[index] } @@ -92,7 +104,10 @@ export class Stack { */ swap(position: number) { if (this._len <= position) { - throw new EvmError(ERROR.STACK_UNDERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STACK_UNDERFLOW, + }) } const head = this._len - 1 @@ -115,12 +130,18 @@ export class Stack { dup(position: number) { const len = this._len if (len < position) { - throw new EvmError(ERROR.STACK_UNDERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STACK_UNDERFLOW, + }) } // Note: this code is borrowed from `push()` (avoids a call) if (len >= this._maxHeight) { - throw new EvmError(ERROR.STACK_OVERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STACK_OVERFLOW, + }) } const i = len - position @@ -139,7 +160,10 @@ export class Stack { // Stack underflow is not possible in EOF if (exchangeIndex1 < 0 || exchangeIndex2 < 0) { - throw new EvmError(ERROR.STACK_UNDERFLOW) + throw new EvmError({ + code: EvmErrorCode.RUNTIME_ERROR, + reason: RuntimeErrorMessage.STACK_UNDERFLOW, + }) } const cache = this._store[exchangeIndex2] diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index 1601ab77701..199fcec66ed 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -1,5 +1,5 @@ import type { EOFContainer } from './eof/container.js' -import type { EvmError } from './exceptions.js' +import type { EvmError } from './errors.js' import type { InterpreterStep, RunState } from './interpreter.js' import type { Message } from './message.js' import type { AsyncDynamicGasHandler, SyncDynamicGasHandler } from './opcodes/gas.js' diff --git a/packages/util/src/errors.ts b/packages/util/src/errors.ts new file mode 100644 index 00000000000..2a9e4deed79 --- /dev/null +++ b/packages/util/src/errors.ts @@ -0,0 +1,44 @@ +/** + * Generic EthereumJS error class with metadata attached + * + * Kudos to https://github.com/ChainSafe/lodestar monorepo + * for the inspiration :-) + * See: https://github.com/ChainSafe/lodestar/blob/unstable/packages/utils/src/errors.ts + */ +export type EthereumJSErrorMetaData = Record +export type EthereumJSErrorObject = { + message: string + stack: string + className: string + type: EthereumJSErrorMetaData +} + +/** + * Generic EthereumJS error with attached metadata + */ +export class EthereumJSError extends Error { + type: T + code: string + constructor(type: T, message?: string, stack?: string) { + super(message ?? type.code) + this.type = type + this.code = type.code + if (stack !== undefined) this.stack = stack + } + + getMetadata(): EthereumJSErrorMetaData { + return this.type + } + + /** + * Get the metadata and the stacktrace for the error. + */ + toObject(): EthereumJSErrorObject { + return { + type: this.getMetadata(), + message: this.message ?? '', + stack: this.stack ?? '', + className: this.constructor.name, + } + } +} diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index d1f6dd95e73..2776c03d037 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -28,6 +28,11 @@ export * from './db.js' */ export * from './withdrawal.js' +/** + * EthereumJS Extended Errors + */ +export * from './errors.js' + /** * ECDSA signature */ From 71e77dc695e5ad700513ab91626cdc1980f2b353 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 1 Oct 2024 13:57:38 +0200 Subject: [PATCH 2/7] evm: fix err handling --- packages/evm/src/interpreter.ts | 4 ++-- packages/util/src/errors.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index eb41703c690..cd13af00d59 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -338,8 +338,8 @@ export class Interpreter { if (overheadTimer !== undefined) { this.performanceLogger.unpauseTimer(overheadTimer) } - // re-throw on non-VM errors - if (!('errorType' in e && e.errorType === 'EvmError')) { + // re-throw on non-VM-runtime errors + if (getRuntimeError(e) === undefined) { throw e } // STOP is not an exception diff --git a/packages/util/src/errors.ts b/packages/util/src/errors.ts index 2a9e4deed79..2ef188bfd4d 100644 --- a/packages/util/src/errors.ts +++ b/packages/util/src/errors.ts @@ -18,7 +18,7 @@ export type EthereumJSErrorObject = { */ export class EthereumJSError extends Error { type: T - code: string + code: string // TODO likely remove this and for error inspection inspect `error.type.code` (like Lodestar) constructor(type: T, message?: string, stack?: string) { super(message ?? type.code) this.type = type From ec01f1bc7552bb3cfedf9a8e086fff9e525a2081 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 10 Feb 2025 14:37:24 +0100 Subject: [PATCH 3/7] util: update base error class --- packages/util/src/errors.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/util/src/errors.ts b/packages/util/src/errors.ts index 2ef188bfd4d..b94e0e52315 100644 --- a/packages/util/src/errors.ts +++ b/packages/util/src/errors.ts @@ -18,11 +18,9 @@ export type EthereumJSErrorObject = { */ export class EthereumJSError extends Error { type: T - code: string // TODO likely remove this and for error inspection inspect `error.type.code` (like Lodestar) constructor(type: T, message?: string, stack?: string) { super(message ?? type.code) this.type = type - this.code = type.code if (stack !== undefined) this.stack = stack } From 16d1b38464bcbc482a3087054181ec440e3ae213 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 10 Feb 2025 15:40:57 +0100 Subject: [PATCH 4/7] evm/util: update to new, simpler error format --- packages/evm/src/errors.ts | 40 +---- packages/evm/src/evm.ts | 76 ++++---- packages/evm/src/index.ts | 13 +- packages/evm/src/interpreter.ts | 69 +++---- packages/evm/src/opcodes/EIP2200.ts | 4 +- packages/evm/src/opcodes/functions.ts | 168 ++++++++---------- packages/evm/src/opcodes/gas.ts | 112 +++++------- packages/evm/src/opcodes/util.ts | 5 +- .../evm/src/precompiles/08-bn254-pairing.ts | 7 +- packages/evm/src/precompiles/09-blake2f.ts | 12 +- .../precompiles/0a-kzg-point-evaluation.ts | 27 ++- .../evm/src/precompiles/0b-bls12-g1add.ts | 12 +- .../evm/src/precompiles/0c-bls12-g1msm.ts | 22 +-- .../evm/src/precompiles/0d-bls12-g2add.ts | 12 +- .../evm/src/precompiles/0e-bls12-g2msm.ts | 17 +- .../evm/src/precompiles/0f-bls12-pairing.ts | 17 +- .../src/precompiles/10-bls12-map-fp-to-g1.ts | 12 +- .../src/precompiles/11-bls12-map-fp2-to-g2.ts | 12 +- packages/evm/src/precompiles/bls12_381/mcl.ts | 37 ++-- .../evm/src/precompiles/bls12_381/noble.ts | 27 ++- packages/evm/src/precompiles/bn254/noble.ts | 17 +- packages/evm/src/stack.ts | 42 ++--- packages/evm/src/types.ts | 4 +- packages/util/src/errors.ts | 11 ++ 24 files changed, 319 insertions(+), 456 deletions(-) diff --git a/packages/evm/src/errors.ts b/packages/evm/src/errors.ts index 32f385f78f8..c3fc3e67d4e 100644 --- a/packages/evm/src/errors.ts +++ b/packages/evm/src/errors.ts @@ -3,7 +3,7 @@ import { EthereumJSError } from '@ethereumjs/util' import type { EOFError } from './eof/errors.js' // TODO: merge EOF errors in here -export enum RuntimeErrorMessage { +export enum EVMErrorCode { OUT_OF_GAS = 'out of gas', CODESTORE_OUT_OF_GAS = 'code store out of gas', CODESIZE_EXCEEDS_MAXIMUM = 'code size to deposit exceeds maximum code size', @@ -41,38 +41,14 @@ export enum RuntimeErrorMessage { INVALID_PROOF = 'kzg proof invalid', } -export enum EvmErrorCode { - UNSUPPORTED_FEATURE = 'EVM_ERROR_UNSUPPORTED_FEATURE', - RUNTIME_ERROR = 'EVM_ERROR_RUNTIME_ERROR', -} - -type EvmRuntimeErrorType = { - code: EvmErrorCode.RUNTIME_ERROR - reason: RuntimeErrorMessage | EOFError -} & ( - | { reason: RuntimeErrorMessage.REVERT; revertBytes: Uint8Array } - | { reason: Exclude | EOFError } -) - -export type EvmErrorType = { code: EvmErrorCode.UNSUPPORTED_FEATURE } | EvmRuntimeErrorType +type EVMErrorType = + | { + code: EVMErrorCode | EOFError + } + | { code: EVMErrorCode.REVERT; revertBytes: Uint8Array } -export class EvmError extends EthereumJSError { - constructor(type: EvmErrorType, message?: string) { +export class EVMError extends EthereumJSError { + constructor(type: EVMErrorType, message?: string) { super(type, message) } } - -export function getRuntimeError(error: EvmError): RuntimeErrorMessage | EOFError | undefined { - if (error.type.code === EvmErrorCode.RUNTIME_ERROR) { - return error.type.reason - } -} - -/* -throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.REVERT -}) - - -*/ diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 97e3fc53568..aec0f60f957 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -8,6 +8,8 @@ import { KECCAK256_NULL, KECCAK256_RLP, MAX_INTEGER, + UsageError, + UsageErrorType, bigIntToBytes, bytesToUnprefixedHex, createZeroAddress, @@ -21,7 +23,7 @@ import { EventEmitter } from 'eventemitter3' import { FORMAT } from './eof/constants.js' import { isEOF } from './eof/util.js' -import { EvmError, EvmErrorCode, RuntimeErrorMessage, getRuntimeError } from './errors.js' +import { EVMError, EVMErrorCode } from './errors.js' import { Interpreter } from './interpreter.js' import { Journal } from './journal.js' import { EVMPerformanceLogger } from './logger.js' @@ -189,17 +191,19 @@ export class EVM implements EVMInterface { for (const eip of this.common.eips()) { if (!supportedEIPs.includes(eip)) { - throw new EvmError( - { code: EvmErrorCode.UNSUPPORTED_FEATURE }, + throw new UsageError( + { + code: UsageErrorType.UNSUPPORTED_FEATURE, + }, `EIP-${eip} is not supported by the EVM`, ) } } if (!EVM.supportedHardforks.includes(this.common.hardfork() as Hardfork)) { - throw new EvmError( + throw new UsageError( { - code: EvmErrorCode.UNSUPPORTED_FEATURE, + code: UsageErrorType.UNSUPPORTED_FEATURE, }, `Hardfork ${this.common.hardfork()} not set as supported in supportedHardforks`, ) @@ -448,9 +452,8 @@ export class EVM implements EVMInterface { createdAddress: message.to, execResult: { returnValue: new Uint8Array(0), - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INITCODE_SIZE_VIOLATION, + exceptionError: new EVMError({ + code: EVMErrorCode.INITCODE_SIZE_VIOLATION, }), executionGasUsed: message.gasLimit, }, @@ -510,9 +513,8 @@ export class EVM implements EVMInterface { createdAddress: message.to, execResult: { returnValue: new Uint8Array(0), - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.CREATE_COLLISION, + exceptionError: new EVMError({ + code: EVMErrorCode.CREATE_COLLISION, }), executionGasUsed: message.gasLimit, }, @@ -814,8 +816,8 @@ export class EVM implements EVMInterface { let gasUsed = message.gasLimit - interpreterRes.runState!.gasLeft if (interpreterRes.exceptionError) { if ( - getRuntimeError(interpreterRes.exceptionError) !== RuntimeErrorMessage.REVERT && - getRuntimeError(interpreterRes.exceptionError) !== RuntimeErrorMessage.INVALID_EOF_FORMAT + interpreterRes.exceptionError.type.code !== EVMErrorCode.REVERT && + interpreterRes.exceptionError.type.code !== EVMErrorCode.INVALID_EOF_FORMAT ) { gasUsed = message.gasLimit } @@ -952,7 +954,7 @@ export class EVM implements EVMInterface { const { executionGasUsed, exceptionError, returnValue } = result.execResult debug( `Received message execResult: [ gasUsed=${executionGasUsed} exceptionError=${ - exceptionError ? `'${getRuntimeError(exceptionError)}'` : 'none' + exceptionError ? `'${exceptionError.type.code}'` : 'none' } returnValue=${short(returnValue)} gasRefund=${result.execResult.gasRefund ?? 0} ]`, ) } @@ -962,7 +964,7 @@ export class EVM implements EVMInterface { // There is one exception: if the CODESTORE_OUT_OF_GAS error is thrown // (this only happens the Frontier/Chainstart fork) // then the error is dismissed - if (err && getRuntimeError(err) !== RuntimeErrorMessage.CODESTORE_OUT_OF_GAS) { + if (err && err.type.code !== EVMErrorCode.CODESTORE_OUT_OF_GAS) { result.execResult.selfdestruct = new Set() result.execResult.createdAddresses = new Set() result.execResult.gasRefund = BIGINT_0 @@ -971,7 +973,7 @@ export class EVM implements EVMInterface { err && !( this.common.hardfork() === Hardfork.Chainstart && - getRuntimeError(err) === RuntimeErrorMessage.CODESTORE_OUT_OF_GAS + err.type.code === EVMErrorCode.CODESTORE_OUT_OF_GAS ) ) { result.execResult.logs = [] @@ -1042,9 +1044,8 @@ export class EVM implements EVMInterface { gasLimit: bigint, ): Promise | ExecResult { if (typeof code !== 'function') { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_PRECOMPILE, + throw new EVMError({ + code: EVMErrorCode.INVALID_PRECOMPILE, }) } @@ -1105,9 +1106,8 @@ export class EVM implements EVMInterface { protected async _reduceSenderBalance(account: Account, message: Message): Promise { account.balance -= message.value if (account.balance < BIGINT_0) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INSUFFICIENT_BALANCE, + throw new EVMError({ + code: EVMErrorCode.INSUFFICIENT_BALANCE, }) } const result = this.journal.putAccount(message.caller, account) @@ -1120,9 +1120,8 @@ export class EVM implements EVMInterface { protected async _addToBalance(toAccount: Account, message: MessageWithTo): Promise { const newBalance = toAccount.balance + message.value if (newBalance > MAX_INTEGER) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.VALUE_OVERFLOW, + throw new EVMError({ + code: EVMErrorCode.VALUE_OVERFLOW, }) } toAccount.balance = newBalance @@ -1176,9 +1175,8 @@ export function OOGResult(gasLimit: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasLimit, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_GAS, + exceptionError: new EVMError({ + code: EVMErrorCode.OUT_OF_GAS, }), } } @@ -1187,9 +1185,8 @@ export function COOGResult(gasUsedCreateCode: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasUsedCreateCode, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_GAS, + exceptionError: new EVMError({ + code: EVMErrorCode.OUT_OF_GAS, }), } } @@ -1198,9 +1195,8 @@ export function INVALID_BYTECODE_RESULT(gasLimit: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasLimit, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_BYTECODE_RESULT, + exceptionError: new EVMError({ + code: EVMErrorCode.INVALID_BYTECODE_RESULT, }), } } @@ -1209,9 +1205,8 @@ export function INVALID_EOF_RESULT(gasLimit: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasLimit, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_EOF_FORMAT, + exceptionError: new EVMError({ + code: EVMErrorCode.INVALID_EOF_FORMAT, }), } } @@ -1220,14 +1215,13 @@ export function CodesizeExceedsMaximumError(gasUsed: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasUsed, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.CODESIZE_EXCEEDS_MAXIMUM, + exceptionError: new EVMError({ + code: EVMErrorCode.CODESIZE_EXCEEDS_MAXIMUM, }), } } -export function EvmErrorResult(error: EvmError, gasUsed: bigint): ExecResult { +export function EvmErrorResult(error: EVMError, gasUsed: bigint): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: gasUsed, diff --git a/packages/evm/src/index.ts b/packages/evm/src/index.ts index 50e8c890be1..02604c2bea4 100644 --- a/packages/evm/src/index.ts +++ b/packages/evm/src/index.ts @@ -1,10 +1,5 @@ import { EOFContainer, validateEOF } from './eof/container.js' -import { - RuntimeErrorMessage as EVMRuntimeErrorMessage, - EvmError, - EvmErrorCode, - EvmErrorType, -} from './errors.js' +import { EVMError, EVMErrorCode } from './errors.js' import { EVM } from './evm.js' import { Message } from './message.js' import { getOpcodesForHF } from './opcodes/index.js' @@ -51,11 +46,9 @@ export type { export { EOFContainer, EVM, - EvmError, - EvmErrorCode, - EvmErrorType, + EVMError, + EVMErrorCode, EVMMockBlockchain, - EVMRuntimeErrorMessage, getActivePrecompiles, getOpcodesForHF, MCLBLS, diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index 2d55ca2b16a..3baede6b97d 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -16,7 +16,7 @@ import { FORMAT, MAGIC, VERSION } from './eof/constants.js' import { EOFContainerMode, validateEOF } from './eof/container.js' import { setupEOF } from './eof/setup.js' import { ContainerSectionType } from './eof/verify.js' -import { EvmError, EvmErrorCode, RuntimeErrorMessage, getRuntimeError } from './errors.js' +import { EVMError, EVMErrorCode } from './errors.js' import { type EVMPerformanceLogger, type Timer } from './logger.js' import { Memory } from './memory.js' import { Message } from './message.js' @@ -109,7 +109,7 @@ export interface RunState { export interface InterpreterResult { runState: RunState - exceptionError?: EvmError + exceptionError?: EVMError } export interface InterpreterStep { @@ -216,9 +216,8 @@ export class Interpreter { // Bytecode contains invalid EOF magic byte return { runState: this._runState, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_BYTECODE_RESULT, + exceptionError: new EVMError({ + code: EVMErrorCode.INVALID_BYTECODE_RESULT, }), } } @@ -226,9 +225,8 @@ export class Interpreter { // Bytecode contains invalid EOF version number return { runState: this._runState, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_EOF_FORMAT, + exceptionError: new EVMError({ + code: EVMErrorCode.INVALID_EOF_FORMAT, }), } } @@ -242,9 +240,8 @@ export class Interpreter { } catch (e) { return { runState: this._runState, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_EOF_FORMAT, + exceptionError: new EVMError({ + code: EVMErrorCode.INVALID_EOF_FORMAT, }), // TODO: verify if all gas should be consumed } } @@ -262,9 +259,8 @@ export class Interpreter { // Trying to deploy an invalid EOF container return { runState: this._runState, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_EOF_FORMAT, + exceptionError: new EVMError({ + code: EVMErrorCode.INVALID_EOF_FORMAT, }), // TODO: verify if all gas should be consumed } } @@ -345,11 +341,11 @@ export class Interpreter { this.performanceLogger.unpauseTimer(overheadTimer) } // re-throw on non-VM-runtime errors - if (getRuntimeError(e) === undefined) { + if (!(e instanceof EVMError)) { throw e } // STOP is not an exception - if (getRuntimeError(e) !== RuntimeErrorMessage.STOP) { + if (e.type.code !== EVMErrorCode.STOP) { err = e } break @@ -409,9 +405,8 @@ export class Interpreter { // Check for invalid opcode if (opInfo.isInvalid) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + throw new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }) } @@ -568,9 +563,7 @@ export class Interpreter { } if (this._runState.gasLeft < BIGINT_0) { this._runState.gasLeft = BIGINT_0 - trap( - new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.OUT_OF_GAS }), - ) + trap(new EVMError({ code: EVMErrorCode.OUT_OF_GAS })) } } @@ -607,9 +600,8 @@ export class Interpreter { if (this._runState.gasRefund < BIGINT_0) { this._runState.gasRefund = BIGINT_0 trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.REFUND_EXHAUSTED, + new EVMError({ + code: EVMErrorCode.REFUND_EXHAUSTED, }), ) } @@ -693,7 +685,7 @@ export class Interpreter { */ finish(returnData: Uint8Array): void { this._result.returnValue = returnData - trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.STOP })) + trap(new EVMError({ code: EVMErrorCode.STOP })) } /** @@ -704,9 +696,8 @@ export class Interpreter { revert(returnData: Uint8Array): void { this._result.returnValue = returnData trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.REVERT, + new EVMError({ + code: EVMErrorCode.REVERT, revertBytes: returnData, }), ) @@ -1034,7 +1025,7 @@ export class Interpreter { if ( results.execResult.returnValue !== undefined && (!results.execResult.exceptionError || - getRuntimeError(results.execResult.exceptionError) === RuntimeErrorMessage.REVERT) + results.execResult.exceptionError.type.code === EVMErrorCode.REVERT) ) { this._runState.returnBytes = results.execResult.returnValue } @@ -1135,15 +1126,14 @@ export class Interpreter { // Set return buffer in case revert happened if ( results.execResult.exceptionError && - getRuntimeError(results.execResult.exceptionError) === RuntimeErrorMessage.REVERT + results.execResult.exceptionError.type.code === EVMErrorCode.REVERT ) { this._runState.returnBytes = results.execResult.returnValue } if ( !results.execResult.exceptionError || - getRuntimeError(results.execResult.exceptionError) === - RuntimeErrorMessage.CODESTORE_OUT_OF_GAS + results.execResult.exceptionError.type.code === EVMErrorCode.CODESTORE_OUT_OF_GAS ) { for (const addressToSelfdestructHex of selfdestruct) { this._result.selfdestruct.add(addressToSelfdestructHex) @@ -1249,7 +1239,7 @@ export class Interpreter { }) } - trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.STOP })) + trap(new EVMError({ code: EVMErrorCode.STOP })) } /** @@ -1257,16 +1247,13 @@ export class Interpreter { */ log(data: Uint8Array, numberOfTopics: number, topics: Uint8Array[]): void { if (numberOfTopics < 0 || numberOfTopics > 4) { - trap( - new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.OUT_OF_GAS }), - ) + trap(new EVMError({ code: EVMErrorCode.OUT_OF_GAS })) } if (topics.length !== numberOfTopics) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INTERNAL_ERROR, + new EVMError({ + code: EVMErrorCode.INTERNAL_ERROR, }), ) } @@ -1285,7 +1272,7 @@ export class Interpreter { } else { // EOF mode, call was either EXTCALL / EXTDELEGATECALL / EXTSTATICCALL if (results.execResult.exceptionError !== undefined) { - if (getRuntimeError(results.execResult.exceptionError) === RuntimeErrorMessage.REVERT) { + if (results.execResult.exceptionError.type.code === EVMErrorCode.REVERT) { // Revert return BIGINT_1 } else { diff --git a/packages/evm/src/opcodes/EIP2200.ts b/packages/evm/src/opcodes/EIP2200.ts index 46226877d07..f50d1bf0f2c 100644 --- a/packages/evm/src/opcodes/EIP2200.ts +++ b/packages/evm/src/opcodes/EIP2200.ts @@ -1,6 +1,6 @@ import { equalsBytes } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { adjustSstoreGasEIP2929 } from './EIP2929.js' import { trap } from './util.js' @@ -27,7 +27,7 @@ export function updateSstoreGasEIP2200( ) { // Fail if not enough gas is left if (runState.interpreter.getGasLeft() <= common.param('sstoreSentryEIP2200Gas')) { - trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.OUT_OF_GAS })) + trap(new EVMError({ code: EVMErrorCode.OUT_OF_GAS })) } // Noop diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 542bdc9d525..dda782347db 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -30,7 +30,7 @@ import { keccak256 } from 'ethereum-cryptography/keccak.js' import { EOFContainer, EOFContainerMode } from '../eof/container.js' import { EOFError } from '../eof/errors.js' import { EOFBYTES, EOFHASH, isEOF } from '../eof/util.js' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { createAddressFromStackBigInt, @@ -64,7 +64,7 @@ export const handlers: Map = new Map([ [ 0x00, function () { - trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.STOP })) + trap(new EVMError({ code: EVMErrorCode.STOP })) }, ], // 0x01: ADD @@ -810,9 +810,9 @@ export const handlers: Map = new Map([ const dest = runState.stack.pop() if (dest > runState.interpreter.getCodeSize()) { trap( - new EvmError( - { code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.INVALID_JUMP }, - RuntimeErrorMessage.INVALID_JUMP + ' at ' + describeLocation(runState), + new EVMError( + { code: EVMErrorCode.INVALID_JUMP }, + EVMErrorCode.INVALID_JUMP + ' at ' + describeLocation(runState), ), ) } @@ -821,9 +821,9 @@ export const handlers: Map = new Map([ if (!jumpIsValid(runState, destNum)) { trap( - new EvmError( - { code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.INVALID_JUMP }, - RuntimeErrorMessage.INVALID_JUMP + ' at ' + describeLocation(runState), + new EVMError( + { code: EVMErrorCode.INVALID_JUMP }, + EVMErrorCode.INVALID_JUMP + ' at ' + describeLocation(runState), ), ) } @@ -839,9 +839,9 @@ export const handlers: Map = new Map([ if (cond !== BIGINT_0) { if (dest > runState.interpreter.getCodeSize()) { trap( - new EvmError( - { code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.INVALID_JUMP }, - RuntimeErrorMessage.INVALID_JUMP + ' at ' + describeLocation(runState), + new EVMError( + { code: EVMErrorCode.INVALID_JUMP }, + EVMErrorCode.INVALID_JUMP + ' at ' + describeLocation(runState), ), ) } @@ -850,9 +850,9 @@ export const handlers: Map = new Map([ if (!jumpIsValid(runState, destNum)) { trap( - new EvmError( - { code: EvmErrorCode.RUNTIME_ERROR, reason: RuntimeErrorMessage.INVALID_JUMP }, - RuntimeErrorMessage.INVALID_JUMP + ' at ' + describeLocation(runState), + new EVMError( + { code: EVMErrorCode.INVALID_JUMP }, + EVMErrorCode.INVALID_JUMP + ' at ' + describeLocation(runState), ), ) } @@ -902,9 +902,8 @@ export const handlers: Map = new Map([ // TSTORE if (runState.interpreter.isStatic()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + new EVMError({ + code: EVMErrorCode.STATIC_STATE_CHANGE, }), ) } @@ -1012,9 +1011,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1042,9 +1040,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1065,9 +1062,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1081,9 +1077,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1103,9 +1098,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } else { @@ -1122,9 +1116,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } else { @@ -1147,9 +1140,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } else { @@ -1179,9 +1171,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1191,12 +1182,10 @@ export const handlers: Map = new Map([ const stackItems = runState.stack.length const typeSection = runState.env.eof!.container.body.typeSections[sectionTarget] if (stackItems > 1024 - typeSection.maxStackHeight + typeSection.inputs) { - trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: EOFError.StackOverflow })) + trap(new EVMError({ code: EOFError.StackOverflow })) } if (runState.env.eof!.eofRunState.returnStack.length >= 1024) { - trap( - new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: EOFError.ReturnStackOverflow }), - ) + trap(new EVMError({ code: EOFError.ReturnStackOverflow })) } runState.env.eof?.eofRunState.returnStack.push(runState.programCounter + 2) @@ -1211,16 +1200,15 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } const newPc = runState.env.eof!.eofRunState.returnStack.pop() if (newPc === undefined) { // This should NEVER happen since it is validated that functions either terminate (the call frame) or return - trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: EOFError.RetfNoReturn })) + trap(new EVMError({ code: EOFError.RetfNoReturn })) } runState.programCounter = newPc! }, @@ -1232,9 +1220,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1247,7 +1234,7 @@ export const handlers: Map = new Map([ const stackItems = runState.stack.length const typeSection = runState.env.eof!.container.body.typeSections[sectionTarget] if (stackItems > 1024 - typeSection.maxStackHeight + typeSection.inputs) { - trap(new EvmError({ code: EvmErrorCode.RUNTIME_ERROR, reason: EOFError.StackOverflow })) + trap(new EVMError({ code: EOFError.StackOverflow })) } /*if (runState.env.eof!.eofRunState.returnStack.length >= 1024) { trap(EOFError.ReturnStackOverflow) @@ -1265,9 +1252,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1288,9 +1274,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1311,9 +1296,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1333,17 +1317,15 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } else { if (runState.interpreter.isStatic()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + new EVMError({ + code: EVMErrorCode.STATIC_STATE_CHANGE, }), ) } @@ -1382,9 +1364,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } else { @@ -1409,9 +1390,8 @@ export const handlers: Map = new Map([ if (actualSectionSize < originalDataSize) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: EOFError.InvalidReturnContractDataSize, + new EVMError({ + code: EOFError.InvalidReturnContractDataSize, }), ) } @@ -1420,9 +1400,8 @@ export const handlers: Map = new Map([ // Data section size is now larger than the max data section size // Temp: trap OOG? trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1453,9 +1432,8 @@ export const handlers: Map = new Map([ !runState.interpreter._evm.allowUnlimitedInitCodeSize ) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INITCODE_SIZE_VIOLATION, + new EVMError({ + code: EVMErrorCode.INITCODE_SIZE_VIOLATION, }), ) } @@ -1484,9 +1462,8 @@ export const handlers: Map = new Map([ async function (runState, common) { if (runState.interpreter.isStatic()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + new EVMError({ + code: EVMErrorCode.STATIC_STATE_CHANGE, }), ) } @@ -1499,9 +1476,8 @@ export const handlers: Map = new Map([ !runState.interpreter._evm.allowUnlimitedInitCodeSize ) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INITCODE_SIZE_VIOLATION, + new EVMError({ + code: EVMErrorCode.INITCODE_SIZE_VIOLATION, }), ) } @@ -1615,9 +1591,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } @@ -1644,9 +1619,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } else { @@ -1683,9 +1657,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } else { @@ -1753,9 +1726,8 @@ export const handlers: Map = new Map([ if (runState.env.eof === undefined) { // Opcode not available in legacy contracts trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_OPCODE, + new EVMError({ + code: EVMErrorCode.INVALID_OPCODE, }), ) } else { diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index 6d98738c97f..e5c44346f65 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -12,7 +12,7 @@ import { } from '@ethereumjs/util' import { EOFError } from '../eof/errors.js' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { DELEGATION_7702_FLAG } from '../types.js' import { updateSstoreGasEIP1283 } from './EIP1283.js' @@ -80,9 +80,8 @@ export const dynamicGasHandlers: Map 32) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_RANGE, + new EVMError({ + code: EVMErrorCode.OUT_OF_RANGE, }), ) } @@ -247,9 +246,8 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + new EVMError({ + code: EVMErrorCode.STATIC_STATE_CHANGE, }), ) } @@ -428,9 +425,8 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + new EVMError({ + code: EVMErrorCode.STATIC_STATE_CHANGE, }), ) } @@ -441,9 +437,8 @@ export const dynamicGasHandlers: Map 4) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_RANGE, + new EVMError({ + code: EVMErrorCode.OUT_OF_RANGE, }), ) } @@ -461,9 +456,8 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + new EVMError({ + code: EVMErrorCode.STATIC_STATE_CHANGE, }), ) } @@ -587,9 +579,8 @@ export const dynamicGasHandlers: Map runState.interpreter.getGasLeft() - gas) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_GAS, + new EVMError({ + code: EVMErrorCode.OUT_OF_GAS, }), ) } if (gas > runState.interpreter.getGasLeft()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_GAS, + new EVMError({ + code: EVMErrorCode.OUT_OF_GAS, }), ) } @@ -722,9 +711,8 @@ export const dynamicGasHandlers: Map runState.interpreter.getGasLeft() - gas) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_GAS, + new EVMError({ + code: EVMErrorCode.OUT_OF_GAS, }), ) } @@ -787,9 +775,8 @@ export const dynamicGasHandlers: Map runState.interpreter.getGasLeft() - gas) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_GAS, + new EVMError({ + code: EVMErrorCode.OUT_OF_GAS, }), ) } @@ -804,9 +791,8 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + new EVMError({ + code: EVMErrorCode.STATIC_STATE_CHANGE, }), ) } @@ -842,9 +828,8 @@ export const dynamicGasHandlers: Map 20 bytes if (toAddr > EXTCALL_TARGET_MAX) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: EOFError.InvalidExtcallTarget, + new EVMError({ + code: EOFError.InvalidExtcallTarget, }), ) } @@ -934,9 +917,8 @@ export const dynamicGasHandlers: Map 20 bytes if (toAddr > EXTCALL_TARGET_MAX) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: EOFError.InvalidExtcallTarget, + new EVMError({ + code: EOFError.InvalidExtcallTarget, }), ) } @@ -1048,9 +1029,8 @@ export const dynamicGasHandlers: Map 20 bytes if (toAddr > EXTCALL_TARGET_MAX) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: EOFError.InvalidExtcallTarget, + new EVMError({ + code: EOFError.InvalidExtcallTarget, }), ) } @@ -1119,9 +1098,8 @@ export const dynamicGasHandlers: Map { if (runState.interpreter.isStatic()) { trap( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STATIC_STATE_CHANGE, + new EVMError({ + code: EVMErrorCode.STATIC_STATE_CHANGE, }), ) } diff --git a/packages/evm/src/opcodes/util.ts b/packages/evm/src/opcodes/util.ts index b8972ceea7c..bfd278c08b9 100644 --- a/packages/evm/src/opcodes/util.ts +++ b/packages/evm/src/opcodes/util.ts @@ -15,7 +15,7 @@ import { } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import type { EvmError } from '../errors.js' +import type { EVMError } from '../errors.js' import type { RunState } from '../interpreter.js' import type { Common } from '@ethereumjs/common' import type { Address } from '@ethereumjs/util' @@ -85,7 +85,8 @@ export function setLengthLeftStorage(value: Uint8Array) { /** * Wraps error message as EvmError */ -export function trap(err: EvmError) { +export function trap(err: EVMError) { + // TODO: accept EVMError constructor args instead of the error // TODO: facilitate extra data along with errors throw err } diff --git a/packages/evm/src/precompiles/08-bn254-pairing.ts b/packages/evm/src/precompiles/08-bn254-pairing.ts index 953d45a8235..cd6e9498af6 100644 --- a/packages/evm/src/precompiles/08-bn254-pairing.ts +++ b/packages/evm/src/precompiles/08-bn254-pairing.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { gasLimitCheck, moduloLengthCheck } from './util.js' @@ -15,9 +15,8 @@ export function precompile08(opts: PrecompileInput): ExecResult { const pName = getPrecompileName('08') if (!moduloLengthCheck(opts, 192, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_INPUT_LENGTH, + new EVMError({ + code: EVMErrorCode.INVALID_INPUT_LENGTH, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/09-blake2f.ts b/packages/evm/src/precompiles/09-blake2f.ts index b9477ffab65..e2f99a2ce64 100644 --- a/packages/evm/src/precompiles/09-blake2f.ts +++ b/packages/evm/src/precompiles/09-blake2f.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { OOGResult } from '../evm.js' import { gasLimitCheck } from './util.js' @@ -182,9 +182,8 @@ export function precompile09(opts: PrecompileInput): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: opts.gasLimit, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_RANGE, + exceptionError: new EVMError({ + code: EVMErrorCode.OUT_OF_RANGE, }), } } @@ -196,9 +195,8 @@ export function precompile09(opts: PrecompileInput): ExecResult { return { returnValue: new Uint8Array(0), executionGasUsed: opts.gasLimit, - exceptionError: new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.OUT_OF_RANGE, + exceptionError: new EVMError({ + code: EVMErrorCode.OUT_OF_RANGE, }), } } diff --git a/packages/evm/src/precompiles/0a-kzg-point-evaluation.ts b/packages/evm/src/precompiles/0a-kzg-point-evaluation.ts index 88b1cf94d14..89d290c647e 100644 --- a/packages/evm/src/precompiles/0a-kzg-point-evaluation.ts +++ b/packages/evm/src/precompiles/0a-kzg-point-evaluation.ts @@ -6,7 +6,7 @@ import { setLengthLeft, } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { gasLimitCheck } from './util.js' @@ -34,9 +34,8 @@ export async function precompile0a(opts: PrecompileInput): Promise { if (opts.data.length !== 192) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_INPUT_LENGTH, + new EVMError({ + code: EVMErrorCode.INVALID_INPUT_LENGTH, }), opts.gasLimit, ) @@ -55,9 +54,8 @@ export async function precompile0a(opts: PrecompileInput): Promise { opts._debug(`${pName} failed: INVALID_COMMITMENT`) } return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_COMMITMENT, + new EVMError({ + code: EVMErrorCode.INVALID_COMMITMENT, }), opts.gasLimit, ) @@ -74,9 +72,8 @@ export async function precompile0a(opts: PrecompileInput): Promise { const res = opts.common.customCrypto?.kzg?.verifyProof(commitment, z, y, kzgProof) if (res === false) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_PROOF, + new EVMError({ + code: EVMErrorCode.INVALID_PROOF, }), opts.gasLimit, ) @@ -87,9 +84,8 @@ export async function precompile0a(opts: PrecompileInput): Promise { opts._debug(`${pName} failed: INVALID_INPUTS`) } return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_INPUTS, + new EVMError({ + code: EVMErrorCode.INVALID_INPUTS, }), opts.gasLimit, ) @@ -98,9 +94,8 @@ export async function precompile0a(opts: PrecompileInput): Promise { opts._debug(`${pName} failed: Unknown error - ${err.message}`) } return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INTERNAL_ERROR, + new EVMError({ + code: EVMErrorCode.INTERNAL_ERROR, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/0b-bls12-g1add.ts b/packages/evm/src/precompiles/0b-bls12-g1add.ts index 2b4e4975ed1..3d6750a2e14 100644 --- a/packages/evm/src/precompiles/0b-bls12-g1add.ts +++ b/packages/evm/src/precompiles/0b-bls12-g1add.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' @@ -23,9 +23,8 @@ export async function precompile0b(opts: PrecompileInput): Promise { if (!equalityLengthCheck(opts, 256, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + new EVMError({ + code: EVMErrorCode.BLS_12_381_INVALID_INPUT_LENGTH, }), opts.gasLimit, ) @@ -40,9 +39,8 @@ export async function precompile0b(opts: PrecompileInput): Promise { ] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/0c-bls12-g1msm.ts b/packages/evm/src/precompiles/0c-bls12-g1msm.ts index 6a36008dc11..5f268b04808 100644 --- a/packages/evm/src/precompiles/0c-bls12-g1msm.ts +++ b/packages/evm/src/precompiles/0c-bls12-g1msm.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { @@ -26,9 +26,8 @@ export async function precompile0c(opts: PrecompileInput): Promise { opts._debug(`${pName} failed: Empty input`) } return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_INPUT_EMPTY, + new EVMError({ + code: EVMErrorCode.BLS_12_381_INPUT_EMPTY, }), opts.gasLimit, ) // follow Geth's implementation @@ -50,18 +49,16 @@ export async function precompile0c(opts: PrecompileInput): Promise { opts._debug(`${pName} failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) } if (!moduloLengthCheck(opts, 160, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) @@ -78,9 +75,8 @@ export async function precompile0c(opts: PrecompileInput): Promise { const pairStart = 160 * k if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/0d-bls12-g2add.ts b/packages/evm/src/precompiles/0d-bls12-g2add.ts index 204f9dd6a05..a93e1c7bcd9 100644 --- a/packages/evm/src/precompiles/0d-bls12-g2add.ts +++ b/packages/evm/src/precompiles/0d-bls12-g2add.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' @@ -23,9 +23,8 @@ export async function precompile0d(opts: PrecompileInput): Promise { if (!equalityLengthCheck(opts, 512, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + new EVMError({ + code: EVMErrorCode.BLS_12_381_INVALID_INPUT_LENGTH, }), opts.gasLimit, ) @@ -44,9 +43,8 @@ export async function precompile0d(opts: PrecompileInput): Promise { ] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/0e-bls12-g2msm.ts b/packages/evm/src/precompiles/0e-bls12-g2msm.ts index 17dcd2ad324..9aed6396855 100644 --- a/packages/evm/src/precompiles/0e-bls12-g2msm.ts +++ b/packages/evm/src/precompiles/0e-bls12-g2msm.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { @@ -24,9 +24,8 @@ export async function precompile0e(opts: PrecompileInput): Promise { opts._debug(`${pName} failed: Empty input`) } return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_INPUT_EMPTY, + new EVMError({ + code: EVMErrorCode.BLS_12_381_INPUT_EMPTY, }), opts.gasLimit, ) // follow Geth's implementation @@ -42,9 +41,8 @@ export async function precompile0e(opts: PrecompileInput): Promise { if (!moduloLengthCheck(opts, 288, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + new EVMError({ + code: EVMErrorCode.BLS_12_381_INVALID_INPUT_LENGTH, }), opts.gasLimit, ) @@ -63,9 +61,8 @@ export async function precompile0e(opts: PrecompileInput): Promise { const pairStart = 288 * k if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/0f-bls12-pairing.ts b/packages/evm/src/precompiles/0f-bls12-pairing.ts index c31778021f0..015a3fc2aaa 100644 --- a/packages/evm/src/precompiles/0f-bls12-pairing.ts +++ b/packages/evm/src/precompiles/0f-bls12-pairing.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' @@ -23,9 +23,8 @@ export async function precompile0f(opts: PrecompileInput): Promise { opts._debug(`${pName} failed: Empty input`) } return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_INPUT_EMPTY, + new EVMError({ + code: EVMErrorCode.BLS_12_381_INPUT_EMPTY, }), opts.gasLimit, ) @@ -38,9 +37,8 @@ export async function precompile0f(opts: PrecompileInput): Promise { // check (respectively Jochem can maybe have a word) if this is something intended or not if (!moduloLengthCheck(opts, 384, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + new EVMError({ + code: EVMErrorCode.BLS_12_381_INVALID_INPUT_LENGTH, }), opts.gasLimit, ) @@ -65,9 +63,8 @@ export async function precompile0f(opts: PrecompileInput): Promise { const pairStart = 384 * k if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/10-bls12-map-fp-to-g1.ts b/packages/evm/src/precompiles/10-bls12-map-fp-to-g1.ts index b5cd6c30b9e..20004d2408f 100644 --- a/packages/evm/src/precompiles/10-bls12-map-fp-to-g1.ts +++ b/packages/evm/src/precompiles/10-bls12-map-fp-to-g1.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' @@ -23,9 +23,8 @@ export async function precompile10(opts: PrecompileInput): Promise { if (!equalityLengthCheck(opts, 64, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_INVALID_INPUT_LENGTH, + new EVMError({ + code: EVMErrorCode.BLS_12_381_INVALID_INPUT_LENGTH, }), opts.gasLimit, ) @@ -35,9 +34,8 @@ export async function precompile10(opts: PrecompileInput): Promise { const zeroByteRanges = [[0, 16]] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/11-bls12-map-fp2-to-g2.ts b/packages/evm/src/precompiles/11-bls12-map-fp2-to-g2.ts index e2e0f4ac028..93bed970274 100644 --- a/packages/evm/src/precompiles/11-bls12-map-fp2-to-g2.ts +++ b/packages/evm/src/precompiles/11-bls12-map-fp2-to-g2.ts @@ -1,6 +1,6 @@ import { bytesToHex } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../errors.js' +import { EVMError, EVMErrorCode } from '../errors.js' import { EvmErrorResult, OOGResult } from '../evm.js' import { leading16ZeroBytesCheck } from './bls12_381/index.js' @@ -23,9 +23,8 @@ export async function precompile11(opts: PrecompileInput): Promise { if (!equalityLengthCheck(opts, 128, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.INVALID_INPUT_LENGTH, + new EVMError({ + code: EVMErrorCode.INVALID_INPUT_LENGTH, }), opts.gasLimit, ) @@ -38,9 +37,8 @@ export async function precompile11(opts: PrecompileInput): Promise { ] if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { return EvmErrorResult( - new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }), opts.gasLimit, ) diff --git a/packages/evm/src/precompiles/bls12_381/mcl.ts b/packages/evm/src/precompiles/bls12_381/mcl.ts index 76ebd9588aa..65632d2c625 100644 --- a/packages/evm/src/precompiles/bls12_381/mcl.ts +++ b/packages/evm/src/precompiles/bls12_381/mcl.ts @@ -7,7 +7,7 @@ import { unprefixedHexToBytes, } from '@ethereumjs/util' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../../errors.js' +import { EVMError, EVMErrorCode } from '../../errors.js' import { BLS_FIELD_MODULUS, @@ -53,17 +53,15 @@ function BLS12_381_ToG1Point(input: Uint8Array, mcl: any, verifyOrder = true): a mcl.verifyOrderG1(verifyOrder) if (verifyOrder && G1.isValidOrder() === false) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }) } // Check if these coordinates are actually on the curve. if (G1.isValid() === false) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }) } @@ -93,15 +91,13 @@ function BLS12_381_FromG1Point(input: any): Uint8Array { function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array, mcl: any): any { // check if the coordinates are in the field if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_FP_NOT_IN_FIELD, }) } if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_FP_NOT_IN_FIELD, }) } @@ -158,16 +154,14 @@ function BLS12_381_ToG2Point(input: Uint8Array, mcl: any, verifyOrder = true): a mcl.verifyOrderG2(verifyOrder) if (verifyOrder && p.isValidOrder() === false) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }) } if (p.isValid() === false) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }) } @@ -208,9 +202,8 @@ function BLS12_381_ToFrPoint(input: Uint8Array, mcl: any): any { function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array, mcl: any): any { // check if point is in field if (bytesToBigInt(fpCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_FP_NOT_IN_FIELD, }) } diff --git a/packages/evm/src/precompiles/bls12_381/noble.ts b/packages/evm/src/precompiles/bls12_381/noble.ts index 6706b9952ae..7893d188080 100644 --- a/packages/evm/src/precompiles/bls12_381/noble.ts +++ b/packages/evm/src/precompiles/bls12_381/noble.ts @@ -8,7 +8,7 @@ import { } from '@ethereumjs/util' import { bls12_381 } from '@noble/curves/bls12-381' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../../errors.js' +import { EVMError, EVMErrorCode } from '../../errors.js' import { BLS_FIELD_MODULUS, @@ -31,15 +31,13 @@ function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Arr // check if the coordinates are in the field // check if the coordinates are in the field if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_FP_NOT_IN_FIELD, }) } if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_FP_NOT_IN_FIELD, }) } @@ -72,9 +70,8 @@ function BLS12_381_ToG1Point(input: Uint8Array, verifyOrder = true) { G1.assertValidity() } catch (e) { if (verifyOrder || (e as Error).message !== 'bad point: not in prime-order subgroup') - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }) } @@ -118,9 +115,8 @@ function BLS12_381_ToG2Point(input: Uint8Array, verifyOrder = true) { pG2.assertValidity() } catch (e) { if (verifyOrder || (e as Error).message !== 'bad point: not in prime-order subgroup') - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_POINT_NOT_ON_CURVE, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_POINT_NOT_ON_CURVE, }) } @@ -172,9 +168,8 @@ function BLS12_381_ToFrPoint(input: Uint8Array): bigint { function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array) { // check if point is in field if (bytesToBigInt(fpCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BLS_12_381_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BLS_12_381_FP_NOT_IN_FIELD, }) } const FP = bls12_381.fields.Fp.fromBytes(fpCoordinate.slice(16)) diff --git a/packages/evm/src/precompiles/bn254/noble.ts b/packages/evm/src/precompiles/bn254/noble.ts index af1906e0b01..c3b72a3576f 100644 --- a/packages/evm/src/precompiles/bn254/noble.ts +++ b/packages/evm/src/precompiles/bn254/noble.ts @@ -9,7 +9,7 @@ import { } from '@ethereumjs/util' import { bn254 } from '@noble/curves/bn254' -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from '../../errors.js' +import { EVMError, EVMErrorCode } from '../../errors.js' import type { EVMBN254Interface } from '../../types.js' import type { AffinePoint } from '@noble/curves/abstract/weierstrass' @@ -64,15 +64,13 @@ function toFrPoint(input: Uint8Array): bigint { function toFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array) { if (bytesToBigInt(fpXCoordinate) >= bn254.fields.Fp2.ORDER) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BN254_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BN254_FP_NOT_IN_FIELD, }) } if (bytesToBigInt(fpYCoordinate) >= bn254.fields.Fp2.ORDER) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BN254_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BN254_FP_NOT_IN_FIELD, }) } @@ -102,9 +100,8 @@ function toG2Point(input: Uint8Array) { for (const p of [p_x_1, p_x_2, p_y_1, p_y_2]) { const pB = bytesToBigInt(p) if (bn254.fields.Fp.create(pB) !== pB) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.BN254_FP_NOT_IN_FIELD, + throw new EVMError({ + code: EVMErrorCode.BN254_FP_NOT_IN_FIELD, }) } } diff --git a/packages/evm/src/stack.ts b/packages/evm/src/stack.ts index 4229f856a6c..75520a67cf4 100644 --- a/packages/evm/src/stack.ts +++ b/packages/evm/src/stack.ts @@ -1,4 +1,4 @@ -import { EvmError, EvmErrorCode, RuntimeErrorMessage } from './errors.js' +import { EVMError, EVMErrorCode } from './errors.js' /** * Implementation of the stack used in evm. @@ -23,9 +23,8 @@ export class Stack { push(value: bigint) { if (this._len >= this._maxHeight) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STACK_OVERFLOW, + throw new EVMError({ + code: EVMErrorCode.STACK_OVERFLOW, }) } @@ -35,9 +34,8 @@ export class Stack { pop(): bigint { if (this._len < 1) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STACK_UNDERFLOW, + throw new EVMError({ + code: EVMErrorCode.STACK_UNDERFLOW, }) } @@ -55,9 +53,8 @@ export class Stack { */ popN(num: number = 1): bigint[] { if (this._len < num) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STACK_UNDERFLOW, + throw new EVMError({ + code: EVMErrorCode.STACK_UNDERFLOW, }) } @@ -88,9 +85,8 @@ export class Stack { for (let peek = 0; peek < num; peek++) { const index = --start if (index < 0) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STACK_UNDERFLOW, + throw new EVMError({ + code: EVMErrorCode.STACK_UNDERFLOW, }) } peekArray[peek] = this._store[index] @@ -104,9 +100,8 @@ export class Stack { */ swap(position: number) { if (this._len <= position) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STACK_UNDERFLOW, + throw new EVMError({ + code: EVMErrorCode.STACK_UNDERFLOW, }) } @@ -130,17 +125,15 @@ export class Stack { dup(position: number) { const len = this._len if (len < position) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STACK_UNDERFLOW, + throw new EVMError({ + code: EVMErrorCode.STACK_UNDERFLOW, }) } // Note: this code is borrowed from `push()` (avoids a call) if (len >= this._maxHeight) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STACK_OVERFLOW, + throw new EVMError({ + code: EVMErrorCode.STACK_OVERFLOW, }) } @@ -160,9 +153,8 @@ export class Stack { // Stack underflow is not possible in EOF if (exchangeIndex1 < 0 || exchangeIndex2 < 0) { - throw new EvmError({ - code: EvmErrorCode.RUNTIME_ERROR, - reason: RuntimeErrorMessage.STACK_UNDERFLOW, + throw new EVMError({ + code: EVMErrorCode.STACK_UNDERFLOW, }) } diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index b85ea4329e9..1ec8b8c12cb 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -1,5 +1,5 @@ import type { EOFContainer } from './eof/container.js' -import type { EvmError } from './errors.js' +import type { EVMError } from './errors.js' import type { InterpreterStep, RunState } from './interpreter.js' import type { Message } from './message.js' import type { AsyncDynamicGasHandler, SyncDynamicGasHandler } from './opcodes/gas.js' @@ -388,7 +388,7 @@ export interface ExecResult { /** * Description of the exception, if any occurred */ - exceptionError?: EvmError + exceptionError?: EVMError /** * Amount of gas left */ diff --git a/packages/util/src/errors.ts b/packages/util/src/errors.ts index b94e0e52315..019b8c16971 100644 --- a/packages/util/src/errors.ts +++ b/packages/util/src/errors.ts @@ -40,3 +40,14 @@ export class EthereumJSError extends Error { } } } + +export enum UsageErrorType { + UNSUPPORTED_FEATURE = 'unsupported feature', +} + +/** + * Error along API Usage + * + * Use directly or in a subclassed context for error comparison (`e instanceof UsageError`) + */ +export class UsageError extends EthereumJSError<{ code: UsageErrorType }> {} From aa3702badef1c32ede0281b2fc68580d1d820545 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 10 Feb 2025 16:04:41 +0100 Subject: [PATCH 5/7] vm/client: fix build --- packages/client/src/rpc/modules/eth.ts | 2 +- packages/evm/src/errors.ts | 2 ++ packages/vm/src/runTx.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/src/rpc/modules/eth.ts b/packages/client/src/rpc/modules/eth.ts index 5eb5070897e..509ee29bed0 100644 --- a/packages/client/src/rpc/modules/eth.ts +++ b/packages/client/src/rpc/modules/eth.ts @@ -520,7 +520,7 @@ export class Eth { throw { code: 3, data: bytesToHex(execResult.returnValue), - message: execResult.exceptionError.error, + message: execResult.exceptionError.type.code, } } return bytesToHex(execResult.returnValue) diff --git a/packages/evm/src/errors.ts b/packages/evm/src/errors.ts index c3fc3e67d4e..488454d352c 100644 --- a/packages/evm/src/errors.ts +++ b/packages/evm/src/errors.ts @@ -51,4 +51,6 @@ export class EVMError extends EthereumJSError { constructor(type: EVMErrorType, message?: string) { super(type, message) } + + // TODO: add helper method to format the error in a human readable way } diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index fa4d6565a7c..f4179c86342 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -608,7 +608,7 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { debug('-'.repeat(100)) debug( `Received tx execResult: [ executionGasUsed=${executionGasUsed} exceptionError=${ - exceptionError !== undefined ? `'${exceptionError.error}'` : 'none' + exceptionError !== undefined ? `'${exceptionError.type.code}'` : 'none' } returnValue=${short(returnValue)} gasRefund=${results.gasRefund ?? 0} ]`, ) } From a8cd9e15d451453cbaa2110747f24617d6a05cb5 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 24 Feb 2025 05:44:21 +0100 Subject: [PATCH 6/7] util: introduce new temp error with unset error code --- packages/util/src/errors.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/util/src/errors.ts b/packages/util/src/errors.ts index 019b8c16971..c3f211cdf5c 100644 --- a/packages/util/src/errors.ts +++ b/packages/util/src/errors.ts @@ -13,6 +13,11 @@ export type EthereumJSErrorObject = { type: EthereumJSErrorMetaData } +// In order to update all our errors to use `EthereumJSError`, temporarily include the +// unset error code. All errors throwing this code should be updated to use the relevant +// error code. +export const UNSET_ERROR_CODE = 'ETHEREUMJS_UNSET_ERROR_CODE' + /** * Generic EthereumJS error with attached metadata */ @@ -41,6 +46,18 @@ export class EthereumJSError extends Error { } } +/** + * @deprecated Use `EthereumJSError` with a set error code instead + * @param message Optional error message + * @param stack Optional stack trace + * @returns + */ +export function EthereumJSErrorUnsetCode(message?: string, stack?: string) { + return new EthereumJSError({ code: UNSET_ERROR_CODE }, message, stack) +} + +// Below here: specific monorepo-wide errors + export enum UsageErrorType { UNSUPPORTED_FEATURE = 'unsupported feature', } From 85aa5fad700307ccae5ccfd868eacc429539856c Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 24 Feb 2025 05:57:39 +0100 Subject: [PATCH 7/7] lint: add rule to disallow `new Error` throwing --- config/eslint.cjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/config/eslint.cjs b/config/eslint.cjs index 050d7d2af89..410bef1dbc1 100644 --- a/config/eslint.cjs +++ b/config/eslint.cjs @@ -116,7 +116,13 @@ module.exports = { 'simple-import-sort/exports': 'error', 'sort-imports': ['error', { ignoreDeclarationSort: true }], 'ethereumjs/noBuffer': 'error', - 'no-restricted-syntax': 'off', + 'no-restricted-syntax': [ + 'error', + { + selector: "ThrowStatement > NewExpression[callee.name='Error']", + message: "Throwing default JS Errors is not allowed. It is only possible to throw `EthereumJSError` (see the util package)", + } + ] }, parserOptions: { extraFileExtensions: ['.json'],