@ethereumjs/evm
Advanced tools
Comparing version 3.0.0 to 3.1.0
@@ -13,3 +13,3 @@ import { Common, Hardfork } from '@ethereumjs/common'; | ||
import type { CustomPrecompile, PrecompileFunc } from './precompiles/index.js'; | ||
import type { Block, Blockchain, CustomOpcode, EVMEvents, EVMInterface, EVMOpts, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, bn128 } from './types.js'; | ||
import type { Block, Blockchain, CustomOpcode, EVMBLSInterface, EVMEvents, EVMInterface, EVMOpts, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, bn128 } from './types.js'; | ||
import type { EVMStateManagerInterface } from '@ethereumjs/common'; | ||
@@ -48,2 +48,3 @@ /** | ||
get opcodes(): OpcodeList; | ||
protected readonly _bls?: EVMBLSInterface; | ||
/** | ||
@@ -50,0 +51,0 @@ * EVM is run in DEBUG mode (default: false) |
@@ -19,6 +19,5 @@ "use strict"; | ||
const types_js_1 = require("./types.js"); | ||
const { debug: createDebugLogger } = debug_1.default; | ||
const debug = createDebugLogger('evm:evm'); | ||
const debugGas = createDebugLogger('evm:gas'); | ||
const debugPrecompiles = createDebugLogger('evm:precompiles'); | ||
const debug = (0, debug_1.default)('evm:evm'); | ||
const debugGas = (0, debug_1.default)('evm:gas'); | ||
const debugPrecompiles = (0, debug_1.default)('evm:precompiles'); | ||
let initializedRustBN = undefined; | ||
@@ -61,4 +60,5 @@ /** | ||
const supportedEIPs = [ | ||
1153, 1559, 2315, 2565, 2718, 2929, 2930, 2935, 3074, 3198, 3529, 3540, 3541, 3607, 3651, | ||
3670, 3855, 3860, 4399, 4895, 4788, 4844, 5133, 5656, 6780, 6800, 7516, | ||
1153, 1559, 2537, 2565, 2718, 2929, 2930, 2935, 3074, 3198, 3529, 3540, 3541, 3607, 3651, | ||
3670, 3855, 3860, 4399, 4895, 4788, 4844, 5133, 5656, 6110, 6780, 6800, 7002, 7251, 7516, | ||
7685, 7702, 7709, | ||
]; | ||
@@ -86,2 +86,6 @@ for (const eip of this.common.eips()) { | ||
this._precompiles = (0, index_js_2.getActivePrecompiles)(this.common, this._customPrecompiles); | ||
if (this.common.isActivatedEIP(2537)) { | ||
this._bls = opts.bls ?? new index_js_2.NobleBLS(); | ||
this._bls.init?.(); | ||
} | ||
this._emit = async (topic, data) => { | ||
@@ -138,20 +142,31 @@ return new Promise((resolve) => this.events.emit(topic, data, resolve)); | ||
let gasLimit = message.gasLimit; | ||
const fromAddress = message.authcallOrigin ?? message.caller; | ||
if (this.common.isActivatedEIP(6800)) { | ||
const sendsValue = message.value !== util_1.BIGINT_0; | ||
if (message.depth === 0) { | ||
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(message.authcallOrigin ?? message.caller); | ||
gasLimit -= originAccessGas; | ||
if (gasLimit < util_1.BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access charged(${originAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(fromAddress); | ||
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`); | ||
const destAccessGas = message.accessWitness.touchTxTargetAndComputeGas(message.to, { | ||
sendsValue, | ||
}); | ||
debugGas(`destAccessGas=${destAccessGas} waived off for target at depth=0`); | ||
} | ||
let callAccessGas = message.accessWitness.touchAndChargeMessageCall(message.to); | ||
if (sendsValue) { | ||
callAccessGas += message.accessWitness.touchAndChargeValueTransfer(fromAddress, message.to); | ||
} | ||
gasLimit -= callAccessGas; | ||
if (gasLimit < util_1.BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`callAccessGas charged(${callAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
else { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access used (${originAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
} | ||
else { | ||
if (this.DEBUG) { | ||
debugGas(`callAccessGas used (${callAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
} | ||
} | ||
let account = await this.stateManager.getAccount(message.authcallOrigin ?? message.caller); | ||
let account = await this.stateManager.getAccount(fromAddress); | ||
if (!account) { | ||
@@ -170,22 +185,2 @@ account = new util_1.Account(); | ||
} | ||
if (this.common.isActivatedEIP(6800)) { | ||
if (message.depth === 0) { | ||
const sendsValue = message.value !== util_1.BIGINT_0; | ||
const destAccessGas = message.accessWitness.touchTxExistingAndComputeGas(message.to, { | ||
sendsValue, | ||
}); | ||
gasLimit -= destAccessGas; | ||
if (gasLimit < util_1.BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Destination access charged(${destAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
} | ||
else { | ||
if (this.DEBUG) { | ||
debugGas(`Destination access used (${destAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
} | ||
} | ||
} | ||
// Load `to` account | ||
@@ -284,16 +279,8 @@ let toAccount = await this.stateManager.getAccount(message.to); | ||
let gasLimit = message.gasLimit; | ||
const fromAddress = message.authcallOrigin ?? message.caller; | ||
if (this.common.isActivatedEIP(6800)) { | ||
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(message.caller); | ||
gasLimit -= originAccessGas; | ||
if (gasLimit < util_1.BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access charged(${originAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
if (message.depth === 0) { | ||
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(fromAddress); | ||
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`); | ||
} | ||
else { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access used (${originAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
} | ||
} | ||
@@ -333,8 +320,7 @@ let account = await this.stateManager.getAccount(message.caller); | ||
if (this.common.isActivatedEIP(6800)) { | ||
const sendsValue = message.value !== util_1.BIGINT_0; | ||
const contractCreateAccessGas = message.accessWitness.touchAndChargeContractCreateInit(message.to, { sendsValue }); | ||
const contractCreateAccessGas = message.accessWitness.touchAndChargeContractCreateInit(message.to); | ||
gasLimit -= contractCreateAccessGas; | ||
if (gasLimit < util_1.BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Contract create (sendsValue=${sendsValue}) charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})`); | ||
debugGas(`ContractCreateInit charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
@@ -345,3 +331,3 @@ return { execResult: OOGResult(message.gasLimit) }; | ||
if (this.DEBUG) { | ||
debugGas(`Contract create (sendsValue=${sendsValue}) charged (${contractCreateAccessGas} gas (-> ${gasLimit}))`); | ||
debugGas(`ContractCreateInit charged (${contractCreateAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
@@ -352,3 +338,5 @@ } | ||
if ((toAccount.nonce && toAccount.nonce > util_1.BIGINT_0) || | ||
!((0, util_1.equalsBytes)(toAccount.codeHash, util_1.KECCAK256_NULL) === true)) { | ||
!((0, util_1.equalsBytes)(toAccount.codeHash, util_1.KECCAK256_NULL) === true) || | ||
// See EIP 7610 and the discussion `https://ethereum-magicians.org/t/eip-7610-revert-creation-in-case-of-non-empty-storage` | ||
!((0, util_1.equalsBytes)(toAccount.storageRoot, util_1.KECCAK256_RLP) === true)) { | ||
if (this.DEBUG) { | ||
@@ -404,2 +392,15 @@ debug(`Returning on address collision`); | ||
if (exit) { | ||
if (this.common.isActivatedEIP(6800)) { | ||
const createCompleteAccessGas = message.accessWitness.touchAndChargeContractCreateCompleted(message.to); | ||
gasLimit -= createCompleteAccessGas; | ||
if (gasLimit < util_1.BIGINT_0) { | ||
if (this.DEBUG) { | ||
debug(`ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
} | ||
else { | ||
debug(`ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})`); | ||
} | ||
} | ||
return { | ||
@@ -424,3 +425,3 @@ createdAddress: message.to, | ||
let returnFee = util_1.BIGINT_0; | ||
if (!result.exceptionError) { | ||
if (!result.exceptionError && !this.common.isActivatedEIP(6800)) { | ||
returnFee = | ||
@@ -532,3 +533,3 @@ BigInt(result.returnValue.length) * BigInt(this.common.param('gasPrices', 'createData')); | ||
if (this.common.isActivatedEIP(6800)) { | ||
const byteCodeWriteAccessfee = message.accessWitness.touchCodeChunksRangeOnWriteAndChargeGas(message.to, 0, message.code.length - 1); | ||
const byteCodeWriteAccessfee = message.accessWitness.touchCodeChunksRangeOnWriteAndChargeGas(message.to, 0, result.returnValue.length - 1); | ||
gasLimit -= byteCodeWriteAccessfee; | ||
@@ -542,3 +543,3 @@ if (gasLimit < util_1.BIGINT_0) { | ||
else { | ||
debug(`ContractCreateComplete access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})`); | ||
debug(`byteCodeWrite access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})`); | ||
result.executionGasUsed += byteCodeWriteAccessfee; | ||
@@ -697,3 +698,3 @@ } | ||
await this._emit('beforeMessage', message); | ||
if (!message.to && this.common.isActivatedEIP(2929) === true) { | ||
if (!message.to && this.common.isActivatedEIP(2929)) { | ||
message.code = message.data; | ||
@@ -927,2 +928,3 @@ this.journal.addWarmedAddress((await this._generateAddress(message)).bytes); | ||
common_1.Hardfork.Prague, | ||
common_1.Hardfork.Osaka, | ||
]; | ||
@@ -929,0 +931,0 @@ function OOGResult(gasLimit) { |
@@ -26,4 +26,2 @@ export declare enum ERROR { | ||
AUTHCALL_UNSET = "attempting to AUTHCALL without AUTH set", | ||
AUTHCALL_NONZERO_VALUEEXT = "attempting to execute AUTHCALL with nonzero external value", | ||
AUTH_INVALID_S = "invalid Signature: s-values greater than secp256k1n/2 are considered invalid", | ||
BLS_12_381_INVALID_INPUT_LENGTH = "invalid input length", | ||
@@ -30,0 +28,0 @@ BLS_12_381_POINT_NOT_ON_CURVE = "point not on curve", |
@@ -30,4 +30,2 @@ "use strict"; | ||
ERROR["AUTHCALL_UNSET"] = "attempting to AUTHCALL without AUTH set"; | ||
ERROR["AUTHCALL_NONZERO_VALUEEXT"] = "attempting to execute AUTHCALL with nonzero external value"; | ||
ERROR["AUTH_INVALID_S"] = "invalid Signature: s-values greater than secp256k1n/2 are considered invalid"; | ||
// BLS errors | ||
@@ -34,0 +32,0 @@ ERROR["BLS_12_381_INVALID_INPUT_LENGTH"] = "invalid input length"; |
import { EOF } from './eof.js'; | ||
import { EVM } from './evm.js'; | ||
import { ERROR as EVMErrorMessage, EvmError } from './exceptions.js'; | ||
import { InterpreterStep } from './interpreter.js'; | ||
import { Message } from './message.js'; | ||
import { getOpcodesForHF } from './opcodes/index.js'; | ||
import { PrecompileInput, getActivePrecompiles } from './precompiles/index.js'; | ||
import { EVMInterface, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, Log, bn128 } from './types.js'; | ||
export { bn128, EOF, EVM, EvmError, EVMErrorMessage, EVMInterface, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, getActivePrecompiles, getOpcodesForHF, InterpreterStep, Log, Message, PrecompileInput, }; | ||
import { MCLBLS, NobleBLS, type PrecompileInput, getActivePrecompiles } from './precompiles/index.js'; | ||
import type { InterpreterStep } from './interpreter.js'; | ||
import type { EVMInterface, EVMOpts, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, Log, bn128 } from './types.js'; | ||
export * from './logger.js'; | ||
export type { bn128, EVMInterface, EVMOpts, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, InterpreterStep, Log, PrecompileInput, }; | ||
export { EOF, EVM, EvmError, EVMErrorMessage, getActivePrecompiles, getOpcodesForHF, MCLBLS, Message, NobleBLS, }; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Message = exports.getOpcodesForHF = exports.getActivePrecompiles = exports.EVMErrorMessage = exports.EvmError = exports.EVM = exports.EOF = void 0; | ||
exports.NobleBLS = exports.Message = exports.MCLBLS = exports.getOpcodesForHF = exports.getActivePrecompiles = exports.EVMErrorMessage = exports.EvmError = exports.EVM = exports.EOF = void 0; | ||
const eof_js_1 = require("./eof.js"); | ||
@@ -16,3 +30,6 @@ Object.defineProperty(exports, "EOF", { enumerable: true, get: function () { return eof_js_1.EOF; } }); | ||
const index_js_2 = require("./precompiles/index.js"); | ||
Object.defineProperty(exports, "MCLBLS", { enumerable: true, get: function () { return index_js_2.MCLBLS; } }); | ||
Object.defineProperty(exports, "NobleBLS", { enumerable: true, get: function () { return index_js_2.NobleBLS; } }); | ||
Object.defineProperty(exports, "getActivePrecompiles", { enumerable: true, get: function () { return index_js_2.getActivePrecompiles; } }); | ||
__exportStar(require("./logger.js"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -11,5 +11,4 @@ import { Account } from '@ethereumjs/util'; | ||
import type { Block, Blockchain, EVMProfilerOpts, Log } from './types.js'; | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { AccessWitness } from '@ethereumjs/statemanager'; | ||
import type { Address } from '@ethereumjs/util'; | ||
import type { AccessWitnessInterface, Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { Address, PrefixedHexString } from '@ethereumjs/util'; | ||
export interface InterpreterOpts { | ||
@@ -27,7 +26,7 @@ pc?: number; | ||
*/ | ||
selfdestruct: Set<string>; | ||
selfdestruct: Set<PrefixedHexString>; | ||
/** | ||
* A map which tracks which addresses were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
} | ||
@@ -51,3 +50,3 @@ export interface Env { | ||
createdAddresses?: Set<string>; | ||
accessWitness?: AccessWitness; | ||
accessWitness?: AccessWitnessInterface; | ||
chargeCodeAccesses?: boolean; | ||
@@ -62,3 +61,2 @@ } | ||
stack: Stack; | ||
returnStack: Stack; | ||
code: Uint8Array; | ||
@@ -89,3 +87,2 @@ shouldDoJumpAnalysis: boolean; | ||
stack: bigint[]; | ||
returnStack: bigint[]; | ||
pc: number; | ||
@@ -92,0 +89,0 @@ depth: number; |
@@ -5,3 +5,2 @@ "use strict"; | ||
const common_1 = require("@ethereumjs/common"); | ||
const statemanager_1 = require("@ethereumjs/statemanager"); | ||
const util_1 = require("@ethereumjs/util"); | ||
@@ -15,4 +14,3 @@ const debug_1 = require("debug"); | ||
const stack_js_1 = require("./stack.js"); | ||
const { debug: createDebugLogger } = debug_1.default; | ||
const debugGas = createDebugLogger('evm:gas'); | ||
const debugGas = (0, debug_1.default)('evm:gas'); | ||
/** | ||
@@ -36,3 +34,2 @@ * Parses and executes EVM bytecode. | ||
stack: new stack_js_1.Stack(), | ||
returnStack: new stack_js_1.Stack(1023), | ||
code: new Uint8Array(0), | ||
@@ -138,5 +135,6 @@ validJumps: Uint8Array.from([]), | ||
this.common.isActivatedEIP(6800) && | ||
this._runState.stateManager instanceof statemanager_1.StatelessVerkleStateManager) { | ||
// is this a code loaded from state using witnesses | ||
this._runState.env.chargeCodeAccesses === true) { | ||
const contract = this._runState.interpreter.getAddress(); | ||
if (!this._runState.stateManager.checkChunkWitnessPresent(contract, programCounter)) { | ||
if (!(await this._runState.stateManager.checkChunkWitnessPresent(contract, programCounter))) { | ||
throw Error(`Invalid witness with missing codeChunk for pc=${programCounter}`); | ||
@@ -251,3 +249,2 @@ } | ||
stack: this._runState.stack.getStack(), | ||
returnStack: this._runState.returnStack.getStack(), | ||
depth: this._env.depth, | ||
@@ -277,3 +274,3 @@ address: this._env.address, | ||
if (!(name in this.opDebuggers)) { | ||
this.opDebuggers[name] = createDebugLogger(`evm:ops:${name}`); | ||
this.opDebuggers[name] = (0, debug_1.default)(`evm:ops:${name}`); | ||
} | ||
@@ -327,6 +324,2 @@ this.opDebuggers[name](JSON.stringify(opTrace)); | ||
} | ||
else if (opcode === 0x5c) { | ||
// Define a BEGINSUB as a 2 in the valid jumps array | ||
jumps[i] = 2; | ||
} | ||
} | ||
@@ -647,2 +640,3 @@ } | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -665,2 +659,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -683,2 +678,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -702,2 +698,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -722,2 +719,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -812,2 +810,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -814,0 +813,0 @@ let createdAddresses; |
import { Address } from '@ethereumjs/util'; | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { Account } from '@ethereumjs/util'; | ||
import type { Account, PrefixedHexString } from '@ethereumjs/util'; | ||
declare type AddressString = string; | ||
declare type HashString = string; | ||
declare type SlotString = string; | ||
@@ -18,3 +17,3 @@ export declare class Journal { | ||
accessList?: Map<AddressString, Set<SlotString>>; | ||
preimages?: Map<HashString, Uint8Array>; | ||
preimages?: Map<PrefixedHexString, Uint8Array>; | ||
constructor(stateManager: EVMStateManagerInterface, common: Common); | ||
@@ -21,0 +20,0 @@ /** |
@@ -7,3 +7,3 @@ "use strict"; | ||
const debug_1 = require("debug"); | ||
const { debug: createDebugLogger } = debug_1.default; | ||
const utils_1 = require("ethereum-cryptography/utils"); | ||
class Journal { | ||
@@ -15,3 +15,3 @@ constructor(stateManager, common) { | ||
typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false; | ||
this._debug = createDebugLogger('statemanager:statemanager'); | ||
this._debug = (0, debug_1.default)('statemanager:statemanager'); | ||
// TODO maybe call into this.clearJournal | ||
@@ -132,14 +132,7 @@ this.cleanJournal(); | ||
async cleanup() { | ||
if (this.common.gteHardfork(common_1.Hardfork.SpuriousDragon) === true) { | ||
if (this.common.gteHardfork(common_1.Hardfork.SpuriousDragon)) { | ||
for (const addressHex of this.touched) { | ||
const address = new util_1.Address((0, util_1.toBytes)('0x' + addressHex)); | ||
const address = new util_1.Address((0, utils_1.hexToBytes)(`0x${addressHex}`)); | ||
const account = await this.stateManager.getAccount(address); | ||
if (account === undefined || account.isEmpty()) { | ||
if (this.common.isActivatedEIP(2935)) { | ||
// The history storage address is exempt of state clearing by EIP-158 if the EIP is activated | ||
const addr = (0, util_1.bigIntToHex)(this.common.param('vm', 'historyStorageAddress')).slice(2); | ||
if (addressHex === addr) { | ||
continue; | ||
} | ||
} | ||
await this.deleteAccount(address); | ||
@@ -146,0 +139,0 @@ if (this.DEBUG) { |
import { Address } from '@ethereumjs/util'; | ||
import type { PrecompileFunc } from './precompiles/index.js'; | ||
import type { AccessWitness } from '@ethereumjs/statemanager'; | ||
import type { AccessWitnessInterface } from '@ethereumjs/common'; | ||
import type { PrefixedHexString } from '@ethereumjs/util'; | ||
interface MessageOpts { | ||
@@ -19,7 +20,7 @@ to?: Address; | ||
*/ | ||
selfdestruct?: Set<string>; | ||
selfdestruct?: Set<PrefixedHexString>; | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
delegatecall?: boolean; | ||
@@ -29,3 +30,3 @@ authcallOrigin?: Address; | ||
blobVersionedHashes?: Uint8Array[]; | ||
accessWitness?: AccessWitness; | ||
accessWitness?: AccessWitnessInterface; | ||
} | ||
@@ -49,7 +50,7 @@ export declare class Message { | ||
*/ | ||
selfdestruct?: Set<string>; | ||
selfdestruct?: Set<PrefixedHexString>; | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
delegatecall: boolean; | ||
@@ -66,3 +67,3 @@ /** | ||
blobVersionedHashes?: Uint8Array[]; | ||
accessWitness?: AccessWitness; | ||
accessWitness?: AccessWitnessInterface; | ||
constructor(opts: MessageOpts); | ||
@@ -69,0 +70,0 @@ /** |
@@ -233,10 +233,2 @@ "use strict"; | ||
{ | ||
eip: 2315, | ||
opcodes: { | ||
0x5c: { name: 'BEGINSUB', isAsync: false, dynamicGas: false }, | ||
0x5d: { name: 'RETURNSUB', isAsync: false, dynamicGas: false }, | ||
0x5e: { name: 'JUMPSUB', isAsync: false, dynamicGas: false }, | ||
}, | ||
}, | ||
{ | ||
eip: 3198, | ||
@@ -243,0 +235,0 @@ opcodes: { |
@@ -22,3 +22,3 @@ import type { RunState } from '../interpreter.js'; | ||
*/ | ||
export declare function accessStorageEIP2929(runState: RunState, key: Uint8Array, isSstore: boolean, common: Common): bigint; | ||
export declare function accessStorageEIP2929(runState: RunState, key: Uint8Array, isSstore: boolean, common: Common, chargeGas?: boolean): bigint; | ||
/** | ||
@@ -25,0 +25,0 @@ * Adjusts cost of SSTORE_RESET_GAS or SLOAD (aka sstorenoop) (EIP-2200) downward when storage |
@@ -16,3 +16,3 @@ "use strict"; | ||
function accessAddressEIP2929(runState, address, common, chargeGas = true, isSelfdestructOrAuthcall = false) { | ||
if (common.isActivatedEIP(2929) === false) | ||
if (!common.isActivatedEIP(2929)) | ||
return util_1.BIGINT_0; | ||
@@ -24,3 +24,4 @@ // Cold | ||
// selfdestruct beneficiary address reads are charged an *additional* cold access | ||
if (chargeGas) { | ||
// if verkle not activated | ||
if (chargeGas && !common.isActivatedEIP(6800)) { | ||
return common.param('gasPrices', 'coldaccountaccess'); | ||
@@ -44,4 +45,4 @@ } | ||
*/ | ||
function accessStorageEIP2929(runState, key, isSstore, common) { | ||
if (common.isActivatedEIP(2929) === false) | ||
function accessStorageEIP2929(runState, key, isSstore, common, chargeGas = true) { | ||
if (!common.isActivatedEIP(2929)) | ||
return util_1.BIGINT_0; | ||
@@ -53,5 +54,7 @@ const address = runState.interpreter.getAddress().bytes; | ||
runState.interpreter.journal.addWarmedStorage(address, key); | ||
return common.param('gasPrices', 'coldsload'); | ||
if (chargeGas && !common.isActivatedEIP(6800)) { | ||
return common.param('gasPrices', 'coldsload'); | ||
} | ||
} | ||
else if (!isSstore) { | ||
else if (chargeGas && (!isSstore || common.isActivatedEIP(6800))) { | ||
return common.param('gasPrices', 'warmstorageread'); | ||
@@ -73,3 +76,3 @@ } | ||
function adjustSstoreGasEIP2929(runState, key, defaultCost, costName, common) { | ||
if (common.isActivatedEIP(2929) === false) | ||
if (!common.isActivatedEIP(2929)) | ||
return defaultCost; | ||
@@ -76,0 +79,0 @@ const address = runState.interpreter.getAddress().bytes; |
@@ -8,3 +8,3 @@ "use strict"; | ||
const util_js_1 = require("./util.js"); | ||
const EIP3074MAGIC = (0, util_1.hexToBytes)('0x03'); | ||
const EIP3074MAGIC = (0, util_1.hexToBytes)('0x04'); | ||
// the opcode functions | ||
@@ -464,4 +464,10 @@ exports.handlers = new Map([ | ||
const addressBigInt = runState.stack.pop(); | ||
const size = BigInt((await runState.stateManager.getContractCode(new util_1.Address((0, util_js_1.addresstoBytes)(addressBigInt)))) | ||
.length); | ||
let size; | ||
if (typeof runState.stateManager.getContractCodeSize === 'function') { | ||
size = BigInt(await runState.stateManager.getContractCodeSize(new util_1.Address((0, util_js_1.addresstoBytes)(addressBigInt)))); | ||
} | ||
else { | ||
size = BigInt((await runState.stateManager.getContractCode(new util_1.Address((0, util_js_1.addresstoBytes)(addressBigInt)))) | ||
.length); | ||
} | ||
runState.stack.push(size); | ||
@@ -531,3 +537,3 @@ }, | ||
const number = runState.stack.pop(); | ||
if (common.isActivatedEIP(2935)) { | ||
if (common.isActivatedEIP(7709)) { | ||
if (number >= runState.interpreter.getBlockNumber()) { | ||
@@ -538,10 +544,17 @@ runState.stack.push(util_1.BIGINT_0); | ||
const diff = runState.interpreter.getBlockNumber() - number; | ||
const historyServeWindow = common.param('vm', 'historyServeWindow'); | ||
// block lookups must be within the `historyServeWindow` | ||
if (diff > historyServeWindow || diff <= util_1.BIGINT_0) { | ||
// block lookups must be within the original window even if historyStorageAddress's | ||
// historyServeWindow is much greater than 256 | ||
if (diff > util_1.BIGINT_256 || diff <= util_1.BIGINT_0) { | ||
runState.stack.push(util_1.BIGINT_0); | ||
return; | ||
} | ||
const historyAddress = util_1.Address.fromString((0, util_1.bigIntToHex)(common.param('vm', 'historyStorageAddress'))); | ||
const historyAddress = new util_1.Address((0, util_1.bigIntToAddressBytes)(common.param('vm', 'historyStorageAddress'))); | ||
const historyServeWindow = common.param('vm', 'historyServeWindow'); | ||
const key = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(number % historyServeWindow), 32); | ||
if (common.isActivatedEIP(6800)) { | ||
const { treeIndex, subIndex } = (0, util_1.getVerkleTreeIndexesForStorageSlot)(number); | ||
// create witnesses and charge gas | ||
const statelessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas(historyAddress, treeIndex, subIndex); | ||
runState.interpreter.useGas(statelessGas, `BLOCKHASH`); | ||
} | ||
const storage = await runState.stateManager.getContractStorage(historyAddress, key); | ||
@@ -763,74 +776,41 @@ runState.stack.push((0, util_1.bytesToBigInt)(storage)); | ||
[0x5b, function () { }], | ||
// 0x5c: BEGINSUB (EIP 2315) / TLOAD (EIP 1153) | ||
// 0x5c: TLOAD (EIP 1153) | ||
[ | ||
0x5c, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// BEGINSUB | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.INVALID_BEGINSUB + ' at ' + (0, util_js_1.describeLocation)(runState)); | ||
} | ||
else if (common.isActivatedEIP(1153)) { | ||
// TLOAD | ||
const key = runState.stack.pop(); | ||
const keyBuf = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(key), 32); | ||
const value = runState.interpreter.transientStorageLoad(keyBuf); | ||
const valueBN = value.length ? (0, util_1.bytesToBigInt)(value) : util_1.BIGINT_0; | ||
runState.stack.push(valueBN); | ||
} | ||
function (runState) { | ||
const key = runState.stack.pop(); | ||
const keyBuf = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(key), 32); | ||
const value = runState.interpreter.transientStorageLoad(keyBuf); | ||
const valueBN = value.length ? (0, util_1.bytesToBigInt)(value) : util_1.BIGINT_0; | ||
runState.stack.push(valueBN); | ||
}, | ||
], | ||
// 0x5d: RETURNSUB (EIP 2315) / TSTORE (EIP 1153) | ||
// 0x5d: TSTORE (EIP 1153) | ||
[ | ||
0x5d, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// RETURNSUB | ||
if (runState.returnStack.length < 1) { | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.INVALID_RETURNSUB); | ||
} | ||
const dest = runState.returnStack.pop(); | ||
runState.programCounter = Number(dest); | ||
function (runState) { | ||
// TSTORE | ||
if (runState.interpreter.isStatic()) { | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.STATIC_STATE_CHANGE); | ||
} | ||
else if (common.isActivatedEIP(1153)) { | ||
// TSTORE | ||
if (runState.interpreter.isStatic()) { | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.STATIC_STATE_CHANGE); | ||
} | ||
const [key, val] = runState.stack.popN(2); | ||
const keyBuf = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(key), 32); | ||
// NOTE: this should be the shortest representation | ||
let value; | ||
if (val === util_1.BIGINT_0) { | ||
value = Uint8Array.from([]); | ||
} | ||
else { | ||
value = (0, util_1.bigIntToBytes)(val); | ||
} | ||
runState.interpreter.transientStorageStore(keyBuf, value); | ||
const [key, val] = runState.stack.popN(2); | ||
const keyBuf = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(key), 32); | ||
// NOTE: this should be the shortest representation | ||
let value; | ||
if (val === util_1.BIGINT_0) { | ||
value = Uint8Array.from([]); | ||
} | ||
else { | ||
value = (0, util_1.bigIntToBytes)(val); | ||
} | ||
runState.interpreter.transientStorageStore(keyBuf, value); | ||
}, | ||
], | ||
// 0x5e: JUMPSUB (2315) / MCOPY (5656) | ||
// 0x5e: MCOPY (5656) | ||
[ | ||
0x5e, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// JUMPSUB | ||
const dest = runState.stack.pop(); | ||
if (dest > runState.interpreter.getCodeSize()) { | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.INVALID_JUMPSUB + ' at ' + (0, util_js_1.describeLocation)(runState)); | ||
} | ||
const destNum = Number(dest); | ||
if (!(0, util_js_1.jumpSubIsValid)(runState, destNum)) { | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.INVALID_JUMPSUB + ' at ' + (0, util_js_1.describeLocation)(runState)); | ||
} | ||
runState.returnStack.push(BigInt(runState.programCounter)); | ||
runState.programCounter = destNum + 1; | ||
} | ||
else if (common.isActivatedEIP(5656)) { | ||
// MCOPY | ||
const [dst, src, length] = runState.stack.popN(3); | ||
const data = runState.memory.read(Number(src), Number(length), true); | ||
runState.memory.write(Number(dst), Number(length), data); | ||
} | ||
function (runState) { | ||
const [dst, src, length] = runState.stack.popN(3); | ||
const data = runState.memory.read(Number(src), Number(length), true); | ||
runState.memory.write(Number(dst), Number(length), data); | ||
}, | ||
@@ -1020,19 +1000,37 @@ ], | ||
let [authority, memOffset, memLength] = runState.stack.popN(3); | ||
if (memLength > util_1.BIGINT_128) { | ||
memLength = util_1.BIGINT_128; | ||
if (memLength > BigInt(97)) { | ||
memLength = BigInt(97); | ||
} | ||
let mem = runState.memory.read(Number(memOffset), Number(memLength)); | ||
if (mem.length < 128) { | ||
mem = (0, util_1.setLengthRight)(mem, 128); | ||
if (mem.length < 97) { | ||
mem = (0, util_1.setLengthRight)(mem, 97); | ||
} | ||
const yParity = BigInt(mem[31]); | ||
const r = mem.subarray(32, 64); | ||
const s = mem.subarray(64, 96); | ||
const commit = mem.subarray(96, 128); | ||
const yParity = BigInt(mem[0]); | ||
const r = mem.subarray(1, 33); | ||
const s = mem.subarray(33, 65); | ||
const commit = mem.subarray(65, 97); | ||
if ((0, util_1.bytesToBigInt)(s) > util_1.SECP256K1_ORDER_DIV_2) { | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.AUTH_INVALID_S); | ||
runState.stack.push(util_1.BIGINT_0); | ||
runState.auth = undefined; | ||
return; | ||
} | ||
const paddedInvokerAddress = (0, util_1.setLengthLeft)(runState.interpreter._env.address.bytes, 32); | ||
if (yParity > util_1.BIGINT_1) { | ||
runState.stack.push(util_1.BIGINT_0); | ||
runState.auth = undefined; | ||
return; | ||
} | ||
// we don't want strick check here on authority being in address range just last 20 bytes | ||
const expectedAddress = new util_1.Address((0, util_1.bigIntToAddressBytes)(authority, false)); | ||
const account = (await runState.stateManager.getAccount(expectedAddress)) ?? new util_1.Account(); | ||
if (account.isContract()) { | ||
// EXTCODESIZE > 0 | ||
runState.stack.push(util_1.BIGINT_0); | ||
runState.auth = undefined; | ||
return; | ||
} | ||
const accountNonce = account.nonce; | ||
const invokedAddress = (0, util_1.setLengthLeft)(runState.interpreter._env.address.bytes, 32); | ||
const chainId = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(runState.interpreter.getChainId()), 32); | ||
const message = (0, util_1.concatBytes)(EIP3074MAGIC, chainId, paddedInvokerAddress, commit); | ||
const nonce = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(accountNonce), 32); | ||
const message = (0, util_1.concatBytes)(EIP3074MAGIC, chainId, nonce, invokedAddress, commit); | ||
const keccakFunction = runState.interpreter._evm.common.customCrypto.keccak256 ?? keccak_js_1.keccak256; | ||
@@ -1054,3 +1052,2 @@ const msgHash = keccakFunction(message); | ||
runState.auth = address; | ||
const expectedAddress = new util_1.Address((0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(authority), 20)); | ||
if (!expectedAddress.equals(address)) { | ||
@@ -1070,3 +1067,3 @@ // expected address does not equal the recovered address, clear auth variable | ||
async function (runState) { | ||
const [_currentGasLimit, addr, value, _valueExt, argsOffset, argsLength, retOffset, retLength,] = runState.stack.popN(8); | ||
const [_currentGasLimit, addr, value, argsOffset, argsLength, retOffset, retLength] = runState.stack.popN(7); | ||
const toAddress = new util_1.Address((0, util_js_1.addresstoBytes)(addr)); | ||
@@ -1073,0 +1070,0 @@ const gasLimit = runState.messageGasLimit; |
@@ -5,3 +5,2 @@ "use strict"; | ||
const common_1 = require("@ethereumjs/common"); | ||
const statemanager_1 = require("@ethereumjs/statemanager"); | ||
const util_1 = require("@ethereumjs/util"); | ||
@@ -48,6 +47,13 @@ const exceptions_js_1 = require("../exceptions.js"); | ||
async function (runState, gas, common) { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0]; | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(address), common); | ||
const address = (0, util_js_1.addresstoBytes)(runState.stack.peek()[0]); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const balanceAddress = new util_1.Address(address); | ||
const coldAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas(balanceAddress, 0, util_1.VERKLE_BALANCE_LEAF_KEY); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, address, common, charge2929Gas); | ||
} | ||
return gas; | ||
@@ -76,3 +82,3 @@ }, | ||
gas += common.param('gasPrices', 'copy') * (0, util_js_1.divCeil)(dataLength, util_1.BIGINT_32); | ||
if (common.isActivatedEIP(6800)) { | ||
if (common.isActivatedEIP(6800) && runState.env.chargeCodeAccesses === true) { | ||
const contract = runState.interpreter.getAddress(); | ||
@@ -94,9 +100,16 @@ let codeEnd = _codeOffset + dataLength; | ||
async function (runState, gas, common) { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0]; | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(address), common); | ||
const addressBytes = (0, util_js_1.addresstoBytes)(runState.stack.peek()[0]); | ||
const address = new util_1.Address(addressBytes); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(address) === undefined) { | ||
let coldAccessGas = util_1.BIGINT_0; | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_VERSION_LEAF_KEY); | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_CODE_SIZE_LEAF_KEY); | ||
gas += coldAccessGas; | ||
// if cold access gas has been charged 2929 gas shouldn't be charged | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(6800) === true) { | ||
const address = new util_1.Address((0, util_js_1.addresstoBytes)(runState.stack.peek()[0])); | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, statemanager_1.CODE_SIZE_LEAF_KEY); | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, addressBytes, common, charge2929Gas); | ||
} | ||
@@ -110,17 +123,28 @@ return gas; | ||
async function (runState, gas, common) { | ||
const [address, memOffset, _codeOffset, dataLength] = runState.stack.peek(4); | ||
const [addressBigInt, memOffset, _codeOffset, dataLength] = runState.stack.peek(4); | ||
const addressBytes = (0, util_js_1.addresstoBytes)(addressBigInt); | ||
const address = new util_1.Address(addressBytes); | ||
gas += (0, util_js_1.subMemUsage)(runState, memOffset, dataLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(address), common); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(address) === undefined) { | ||
let coldAccessGas = util_1.BIGINT_0; | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_VERSION_LEAF_KEY); | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_CODE_SIZE_LEAF_KEY); | ||
gas += coldAccessGas; | ||
// if cold access gas has been charged 2929 gas shouldn't be charged | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, addressBytes, common, charge2929Gas); | ||
} | ||
if (dataLength !== util_1.BIGINT_0) { | ||
gas += common.param('gasPrices', 'copy') * (0, util_js_1.divCeil)(dataLength, util_1.BIGINT_32); | ||
if (common.isActivatedEIP(6800)) { | ||
const contract = new util_1.Address((0, util_js_1.addresstoBytes)(address)); | ||
let codeEnd = _codeOffset + dataLength; | ||
const codeSize = BigInt((await runState.stateManager.getContractCode(contract)).length); | ||
const codeSize = BigInt((await runState.stateManager.getContractCode(address)).length); | ||
if (codeEnd > codeSize) { | ||
codeEnd = codeSize; | ||
} | ||
gas += runState.env.accessWitness.touchCodeChunksRangeOnReadAndChargeGas(contract, Number(_codeOffset), Number(codeEnd)); | ||
gas += runState.env.accessWitness.touchCodeChunksRangeOnReadAndChargeGas(address, Number(_codeOffset), Number(codeEnd)); | ||
} | ||
@@ -150,6 +174,14 @@ } | ||
async function (runState, gas, common) { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0]; | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(address), common); | ||
const address = (0, util_js_1.addresstoBytes)(runState.stack.peek()[0]); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const codeAddress = new util_1.Address(address); | ||
let coldAccessGas = util_1.BIGINT_0; | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(codeAddress, 0, util_1.VERKLE_CODE_HASH_LEAF_KEY); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, address, common, charge2929Gas); | ||
} | ||
return gas; | ||
@@ -191,10 +223,13 @@ }, | ||
const keyBuf = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(key), 32); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += (0, EIP2929_js_1.accessStorageEIP2929)(runState, keyBuf, false, common); | ||
} | ||
if (common.isActivatedEIP(6800) === true) { | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const address = runState.interpreter.getAddress(); | ||
const { treeIndex, subIndex } = (0, statemanager_1.getTreeIndexesForStorageSlot)(key); | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, treeIndex, subIndex); | ||
const { treeIndex, subIndex } = (0, util_1.getVerkleTreeIndexesForStorageSlot)(key); | ||
const coldAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, treeIndex, subIndex); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessStorageEIP2929)(runState, keyBuf, false, common, charge2929Gas); | ||
} | ||
return gas; | ||
@@ -226,3 +261,5 @@ }, | ||
else if (common.gteHardfork(common_1.Hardfork.Istanbul)) { | ||
gas += (0, EIP2200_js_1.updateSstoreGasEIP2200)(runState, currentStorage, originalStorage, (0, util_js_1.setLengthLeftStorage)(value), keyBytes, common); | ||
if (!common.isActivatedEIP(6800)) { | ||
gas += (0, EIP2200_js_1.updateSstoreGasEIP2200)(runState, currentStorage, originalStorage, (0, util_js_1.setLengthLeftStorage)(value), keyBytes, common); | ||
} | ||
} | ||
@@ -232,13 +269,16 @@ else { | ||
} | ||
if (common.isActivatedEIP(2929) === true) { | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const contract = runState.interpreter.getAddress(); | ||
const { treeIndex, subIndex } = (0, util_1.getVerkleTreeIndexesForStorageSlot)(key); | ||
const coldAccessGas = runState.env.accessWitness.touchAddressOnWriteAndComputeGas(contract, treeIndex, subIndex); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
// We have to do this after the Istanbul (EIP2200) checks. | ||
// Otherwise, we might run out of gas, due to "sentry check" of 2300 gas, | ||
// if we deduct extra gas first. | ||
gas += (0, EIP2929_js_1.accessStorageEIP2929)(runState, keyBytes, true, common); | ||
gas += (0, EIP2929_js_1.accessStorageEIP2929)(runState, keyBytes, true, common, charge2929Gas); | ||
} | ||
if (common.isActivatedEIP(6800) === true) { | ||
const contract = runState.interpreter.getAddress(); | ||
const { treeIndex, subIndex } = (0, statemanager_1.getTreeIndexesForStorageSlot)(key); | ||
gas += runState.env.accessWitness.touchAddressOnWriteAndComputeGas(contract, treeIndex, subIndex); | ||
} | ||
return gas; | ||
@@ -286,6 +326,6 @@ }, | ||
const [_value, offset, length] = runState.stack.peek(3); | ||
if (common.isActivatedEIP(2929) === true) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, runState.interpreter.getAddress().bytes, common, false); | ||
} | ||
if (common.isActivatedEIP(3860) === true) { | ||
if (common.isActivatedEIP(3860)) { | ||
gas += ((length + util_1.BIGINT_31) / util_1.BIGINT_32) * common.param('gasPrices', 'initCodeWordCost'); | ||
@@ -311,6 +351,18 @@ } | ||
gas += (0, util_js_1.subMemUsage)(runState, outOffset, outLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, toAddress.bytes, common); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined) { | ||
// TODO: add check if toAddress is not a precompile | ||
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
if (value !== util_1.BIGINT_0) { | ||
const contractAddress = runState.interpreter.getAddress(); | ||
gas += runState.env.accessWitness.touchAndChargeValueTransfer(contractAddress, toAddress); | ||
} | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (value !== util_1.BIGINT_0) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, toAddress.bytes, common, charge2929Gas); | ||
} | ||
if (value !== util_1.BIGINT_0 && !common.isActivatedEIP(6800)) { | ||
gas += common.param('gasPrices', 'callValueTransfer'); | ||
@@ -335,10 +387,2 @@ } | ||
} | ||
if (common.isActivatedEIP(6800)) { | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
if (value !== util_1.BIGINT_0) { | ||
const contractAddress = runState.interpreter.getAddress(); | ||
gas += runState.env.accessWitness.touchAndChargeValueTransfer(contractAddress, toAddress); | ||
} | ||
} | ||
const gasLimit = (0, util_js_1.maxCallGas)(currentGasLimit, runState.interpreter.getGasLeft() - gas, runState, common); | ||
@@ -362,15 +406,18 @@ // note that TangerineWhistle or later this cannot happen | ||
const [currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.peek(7); | ||
const toAddress = new util_1.Address((0, util_js_1.addresstoBytes)(toAddr)); | ||
gas += (0, util_js_1.subMemUsage)(runState, inOffset, inLength, common); | ||
gas += (0, util_js_1.subMemUsage)(runState, outOffset, outLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(toAddr), common); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined) { | ||
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(toAddr), common, charge2929Gas); | ||
} | ||
if (value !== util_1.BIGINT_0) { | ||
gas += common.param('gasPrices', 'callValueTransfer'); | ||
} | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new util_1.Address((0, util_js_1.addresstoBytes)(toAddr)); | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
} | ||
const gasLimit = (0, util_js_1.maxCallGas)(currentGasLimit, runState.interpreter.getGasLeft() - gas, runState, common); | ||
@@ -400,12 +447,16 @@ // note that TangerineWhistle or later this cannot happen | ||
const [currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] = runState.stack.peek(6); | ||
const toAddress = new util_1.Address((0, util_js_1.addresstoBytes)(toAddr)); | ||
gas += (0, util_js_1.subMemUsage)(runState, inOffset, inLength, common); | ||
gas += (0, util_js_1.subMemUsage)(runState, outOffset, outLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(toAddr), common); | ||
} | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new util_1.Address((0, util_js_1.addresstoBytes)(toAddr)); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined) { | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(toAddr), common, charge2929Gas); | ||
} | ||
const gasLimit = (0, util_js_1.maxCallGas)(currentGasLimit, runState.interpreter.getGasLeft() - gas, runState, common); | ||
@@ -430,6 +481,6 @@ // note that TangerineWhistle or later this cannot happen | ||
gas += (0, util_js_1.subMemUsage)(runState, offset, length, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, runState.interpreter.getAddress().bytes, common, false); | ||
} | ||
if (common.isActivatedEIP(3860) === true) { | ||
if (common.isActivatedEIP(3860)) { | ||
gas += ((length + util_1.BIGINT_31) / util_1.BIGINT_32) * common.param('gasPrices', 'initCodeWordCost'); | ||
@@ -448,3 +499,6 @@ } | ||
async function (runState, gas, common) { | ||
const [_address, memOffset, memLength] = runState.stack.peek(3); | ||
const [address, memOffset, memLength] = runState.stack.peek(3); | ||
// Note: 2929 is always active if AUTH can be reached, | ||
// since it needs London as minimum hardfork | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_1.bigIntToBytes)(address), common); | ||
gas += (0, util_js_1.subMemUsage)(runState, memOffset, memLength, common); | ||
@@ -461,6 +515,3 @@ return gas; | ||
} | ||
const [currentGasLimit, addr, value, valueExt, argsOffset, argsLength, retOffset, retLength,] = runState.stack.peek(8); | ||
if (valueExt !== util_1.BIGINT_0) { | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.AUTHCALL_NONZERO_VALUEEXT); | ||
} | ||
const [currentGasLimit, addr, value, argsOffset, argsLength, retOffset, retLength] = runState.stack.peek(7); | ||
const toAddress = new util_1.Address((0, util_js_1.addresstoBytes)(addr)); | ||
@@ -486,2 +537,13 @@ gas += common.param('gasPrices', 'warmstorageread'); | ||
runState.messageGasLimit = gasLimit; | ||
if (value > util_1.BIGINT_0) { | ||
const account = (await runState.stateManager.getAccount(runState.auth)) ?? new util_1.Account(); | ||
if (account.balance < value) { | ||
(0, util_js_1.trap)(exceptions_js_1.ERROR.OUT_OF_GAS); | ||
} | ||
account.balance -= value; | ||
const toAddr = new util_1.Address((0, util_js_1.addresstoBytes)(addr)); | ||
const target = (await runState.stateManager.getAccount(toAddr)) ?? new util_1.Account(); | ||
target.balance += value; | ||
await runState.stateManager.putAccount(toAddr, target); | ||
} | ||
return gas; | ||
@@ -497,10 +559,13 @@ }, | ||
gas += (0, util_js_1.subMemUsage)(runState, outOffset, outLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(toAddr), common); | ||
} | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new util_1.Address((0, util_js_1.addresstoBytes)(toAddr)); | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, (0, util_js_1.addresstoBytes)(toAddr), common, charge2929Gas); | ||
} | ||
const gasLimit = (0, util_js_1.maxCallGas)(currentGasLimit, runState.interpreter.getGasLeft() - gas, runState, common); // we set TangerineWhistle or later to true here, as STATICCALL was available from Byzantium (which is after TangerineWhistle) | ||
@@ -529,6 +594,7 @@ runState.messageGasLimit = gasLimit; | ||
const selfdestructToAddress = new util_1.Address((0, util_js_1.addresstoBytes)(selfdestructToaddressBigInt)); | ||
const contractAddress = runState.interpreter.getAddress(); | ||
let deductGas = false; | ||
const balance = await runState.interpreter.getExternalBalance(contractAddress); | ||
if (common.gteHardfork(common_1.Hardfork.SpuriousDragon)) { | ||
// EIP-161: State Trie Clearing | ||
const balance = await runState.interpreter.getExternalBalance(runState.interpreter.getAddress()); | ||
if (balance > util_1.BIGINT_0) { | ||
@@ -552,5 +618,24 @@ // This technically checks if account is empty or non-existent | ||
} | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, selfdestructToAddress.bytes, common, true, true); | ||
let selfDestructToCharge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
// read accesses for version and code size | ||
if (runState.interpreter._evm.getPrecompile(contractAddress) === undefined) { | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(contractAddress, 0, util_1.VERKLE_VERSION_LEAF_KEY); | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(contractAddress, 0, util_1.VERKLE_CODE_SIZE_LEAF_KEY); | ||
} | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(contractAddress, 0, util_1.VERKLE_BALANCE_LEAF_KEY); | ||
if (balance > util_1.BIGINT_0) { | ||
gas += runState.env.accessWitness.touchAddressOnWriteAndComputeGas(contractAddress, 0, util_1.VERKLE_BALANCE_LEAF_KEY); | ||
} | ||
let selfDestructToColdAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas(selfdestructToAddress, 0, util_1.VERKLE_BALANCE_LEAF_KEY); | ||
if (balance > util_1.BIGINT_0) { | ||
selfDestructToColdAccessGas += | ||
runState.env.accessWitness.touchAddressOnWriteAndComputeGas(selfdestructToAddress, 0, util_1.VERKLE_BALANCE_LEAF_KEY); | ||
} | ||
gas += selfDestructToColdAccessGas; | ||
selfDestructToCharge2929Gas = selfDestructToColdAccessGas === util_1.BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += (0, EIP2929_js_1.accessAddressEIP2929)(runState, selfdestructToAddress.bytes, common, selfDestructToCharge2929Gas, true); | ||
} | ||
return gas; | ||
@@ -557,0 +642,0 @@ }, |
@@ -47,6 +47,2 @@ import type { RunState } from '../interpreter.js'; | ||
/** | ||
* Checks if a jumpsub is valid given a destination (defined as a 2 in the validJumps array) | ||
*/ | ||
export declare function jumpSubIsValid(runState: RunState, dest: number): boolean; | ||
/** | ||
* Returns an overflow-safe slice of an array. It right-pads | ||
@@ -53,0 +49,0 @@ * the data with zeros to `length`. |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.exponentiation = exports.abs = exports.toTwos = exports.fromTwos = exports.mod = exports.updateSstoreGas = exports.writeCallOutput = exports.subMemUsage = exports.maxCallGas = exports.jumpSubIsValid = exports.jumpIsValid = exports.getFullname = exports.getDataSlice = exports.divCeil = exports.describeLocation = exports.addresstoBytes = exports.trap = exports.setLengthLeftStorage = void 0; | ||
exports.exponentiation = exports.abs = exports.toTwos = exports.fromTwos = exports.mod = exports.updateSstoreGas = exports.writeCallOutput = exports.subMemUsage = exports.maxCallGas = exports.jumpIsValid = exports.getFullname = exports.getDataSlice = exports.divCeil = exports.describeLocation = exports.addresstoBytes = exports.trap = exports.setLengthLeftStorage = void 0; | ||
const common_1 = require("@ethereumjs/common"); | ||
@@ -121,9 +121,2 @@ const util_1 = require("@ethereumjs/util"); | ||
/** | ||
* Checks if a jumpsub is valid given a destination (defined as a 2 in the validJumps array) | ||
*/ | ||
function jumpSubIsValid(runState, dest) { | ||
return runState.validJumps[dest] === 2; | ||
} | ||
exports.jumpSubIsValid = jumpSubIsValid; | ||
/** | ||
* Returns an overflow-safe slice of an array. It right-pads | ||
@@ -130,0 +123,0 @@ * the data with zeros to `length`. |
import { type Address } from '@ethereumjs/util'; | ||
import { MCLBLS, NobleBLS } from './bls12_381/index.js'; | ||
import type { PrecompileFunc, PrecompileInput } from './types.js'; | ||
@@ -39,4 +40,4 @@ import type { Common } from '@ethereumjs/common'; | ||
declare function getPrecompileName(addressUnprefixedStr: string): string | undefined; | ||
export { getActivePrecompiles, getPrecompileName, precompileEntries, precompiles, ripemdPrecompileAddress, }; | ||
export { getActivePrecompiles, getPrecompileName, MCLBLS, NobleBLS, precompileEntries, precompiles, ripemdPrecompileAddress, }; | ||
export type { AddPrecompile, CustomPrecompile, DeletePrecompile, PrecompileFunc, PrecompileInput }; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ripemdPrecompileAddress = exports.precompiles = exports.precompileEntries = exports.getPrecompileName = exports.getActivePrecompiles = void 0; | ||
exports.ripemdPrecompileAddress = exports.precompiles = exports.precompileEntries = exports.NobleBLS = exports.MCLBLS = exports.getPrecompileName = exports.getActivePrecompiles = void 0; | ||
const common_1 = require("@ethereumjs/common"); | ||
@@ -16,2 +16,14 @@ const util_1 = require("@ethereumjs/util"); | ||
const _0a_kzg_point_evaluation_js_1 = require("./0a-kzg-point-evaluation.js"); | ||
const _0b_bls12_g1add_js_1 = require("./0b-bls12-g1add.js"); | ||
const _0c_bls12_g1mul_js_1 = require("./0c-bls12-g1mul.js"); | ||
const _0d_bls12_g1msm_js_1 = require("./0d-bls12-g1msm.js"); | ||
const _0e_bls12_g2add_js_1 = require("./0e-bls12-g2add.js"); | ||
const _0f_bls12_g2mul_js_1 = require("./0f-bls12-g2mul.js"); | ||
const _10_bls12_g2msm_js_1 = require("./10-bls12-g2msm.js"); | ||
const _11_bls12_pairing_js_1 = require("./11-bls12-pairing.js"); | ||
const _12_bls12_map_fp_to_g1_js_1 = require("./12-bls12-map-fp-to-g1.js"); | ||
const _13_bls12_map_fp2_to_g2_js_1 = require("./13-bls12-map-fp2-to-g2.js"); | ||
const index_js_1 = require("./bls12_381/index.js"); | ||
Object.defineProperty(exports, "MCLBLS", { enumerable: true, get: function () { return index_js_1.MCLBLS; } }); | ||
Object.defineProperty(exports, "NobleBLS", { enumerable: true, get: function () { return index_js_1.NobleBLS; } }); | ||
var PrecompileAvailabilityCheck; | ||
@@ -115,2 +127,83 @@ (function (PrecompileAvailabilityCheck) { | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000b', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _0b_bls12_g1add_js_1.precompile0b, | ||
name: 'BLS12_G1ADD', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000c', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _0c_bls12_g1mul_js_1.precompile0c, | ||
name: 'BLS12_G1MUL', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000d', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _0d_bls12_g1msm_js_1.precompile0d, | ||
name: 'BLS12_G1MSM', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000e', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _0e_bls12_g2add_js_1.precompile0e, | ||
name: 'BLS12_G2ADD', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000f', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _0f_bls12_g2mul_js_1.precompile0f, | ||
name: 'BLS12_G2MUL', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000010', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _10_bls12_g2msm_js_1.precompile10, | ||
name: 'BLS12_G2MSM', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000011', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _11_bls12_pairing_js_1.precompile11, | ||
name: 'BLS12_PAIRING', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000012', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _12_bls12_map_fp_to_g1_js_1.precompile12, | ||
name: 'BLS12_MAP_FP_TO_G1', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000013', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: _13_bls12_map_fp2_to_g2_js_1.precompile13, | ||
name: 'BLS12_MAP_FP2_TO_G2', | ||
}, | ||
]; | ||
@@ -129,2 +222,11 @@ exports.precompileEntries = precompileEntries; | ||
'000000000000000000000000000000000000000a': _0a_kzg_point_evaluation_js_1.precompile0a, | ||
'000000000000000000000000000000000000000b': _0b_bls12_g1add_js_1.precompile0b, | ||
'000000000000000000000000000000000000000c': _0c_bls12_g1mul_js_1.precompile0c, | ||
'000000000000000000000000000000000000000d': _0d_bls12_g1msm_js_1.precompile0d, | ||
'000000000000000000000000000000000000000e': _0e_bls12_g2add_js_1.precompile0e, | ||
'000000000000000000000000000000000000000f': _0f_bls12_g2mul_js_1.precompile0f, | ||
'0000000000000000000000000000000000000010': _10_bls12_g2msm_js_1.precompile10, | ||
'0000000000000000000000000000000000000011': _11_bls12_pairing_js_1.precompile11, | ||
'0000000000000000000000000000000000000012': _12_bls12_map_fp_to_g1_js_1.precompile12, | ||
'0000000000000000000000000000000000000013': _13_bls12_map_fp2_to_g2_js_1.precompile13, | ||
}; | ||
@@ -131,0 +233,0 @@ exports.precompiles = precompiles; |
@@ -8,5 +8,4 @@ import type { EvmError } from './exceptions.js'; | ||
import type { PrecompileFunc } from './precompiles/types.js'; | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { AccessWitness } from '@ethereumjs/statemanager'; | ||
import type { Account, Address, AsyncEventEmitter } from '@ethereumjs/util'; | ||
import type { AccessWitnessInterface, Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { Account, Address, AsyncEventEmitter, PrefixedHexString } from '@ethereumjs/util'; | ||
export declare type DeleteOpcode = { | ||
@@ -70,3 +69,3 @@ opcode: number; | ||
*/ | ||
selfdestruct?: Set<string>; | ||
selfdestruct?: Set<PrefixedHexString>; | ||
/** | ||
@@ -99,3 +98,3 @@ * The address of the account that is executing this code (`address(this)`). Defaults to the zero address. | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
/** | ||
@@ -118,3 +117,3 @@ * Skip balance checks if true. If caller balance is less than message value, | ||
message?: Message; | ||
accessWitness?: AccessWitness; | ||
accessWitness?: AccessWitnessInterface; | ||
} | ||
@@ -141,3 +140,3 @@ interface NewContractEvent { | ||
accessList?: Map<string, Set<string>>; | ||
preimages?: Map<string, Uint8Array>; | ||
preimages?: Map<PrefixedHexString, Uint8Array>; | ||
addAlwaysWarmAddress(address: string, addToAccessList?: boolean): void; | ||
@@ -168,7 +167,6 @@ addAlwaysWarmSlot(address: string, slot: string, addToAccessList?: boolean): void; | ||
* - [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - Fee market change for ETH 1.0 chain | ||
* - [EIP-2315](https://eips.ethereum.org/EIPS/eip-2315) - Simple subroutines for the EVM (`outdated`) | ||
* - [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) - BLS precompiles (removed in v4.0.0, see latest v3 release) | ||
* - [EIP-2565](https://eips.ethereum.org/EIPS/eip-2565) - ModExp gas cost | ||
* - [EIP-2718](https://eips.ethereum.org/EIPS/eip-2565) - Transaction Types | ||
* - [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) - Save historical block hashes in state (`experimental`) | ||
* - [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) - Serve historical block hashes from state (Prague) | ||
* - [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) - gas cost increases for state access opcodes | ||
@@ -192,7 +190,13 @@ * - [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) - Optional access list tx type | ||
* - [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) - Shard Blob Transactions (Cancun) | ||
* - [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) - EOA code transactions (Prague) (`outdated`) | ||
* - [EIP-7709](https://eips.ethereum.org/EIPS/eip-7709) - Read BLOCKHASH from storage and update cost (Osaka) | ||
* - [EIP-4895](https://eips.ethereum.org/EIPS/eip-4895) - Beacon chain push withdrawals as operations (Shanghai) | ||
* - [EIP-5133](https://eips.ethereum.org/EIPS/eip-5133) - Delaying Difficulty Bomb to mid-September 2022 (Gray Glacier) | ||
* - [EIP-5656](https://eips.ethereum.org/EIPS/eip-5656) - MCOPY - Memory copying instruction (Cancun) | ||
* - [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110) - Supply validator deposits on chain (Prague) | ||
* - [EIP-6780](https://eips.ethereum.org/EIPS/eip-6780) - SELFDESTRUCT only in same transaction (Cancun) | ||
* - [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) - Execution layer triggerable withdrawals (Prague) | ||
* - [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) - Execution layer triggerable validator consolidations (Prague) | ||
* - [EIP-7516](https://eips.ethereum.org/EIPS/eip-7516) - BLOBBASEFEE opcode (Cancun) | ||
* - [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) - General purpose execution layer requests (Prague) | ||
* | ||
@@ -238,2 +242,23 @@ * *Annotations:* | ||
customPrecompiles?: CustomPrecompile[]; | ||
/** | ||
* For the EIP-2537 BLS Precompiles, the native JS `@noble/curves` | ||
* https://github.com/paulmillr/noble-curves BLS12-381 curve implementation | ||
* is used (see `noble.ts` file in the `precompiles` folder). | ||
* | ||
* To use an alternative implementation this option can be used by passing | ||
* in a wrapper implementation integrating the desired library and adhering | ||
* to the `EVMBLSInterface` specification. | ||
* | ||
* An interface for the MCL WASM implementation https://github.com/herumi/mcl-wasm | ||
* is shipped with this library which can be used as follows (with `mcl-wasm` being | ||
* explicitly added to the set of dependencies): | ||
* | ||
* ```ts | ||
* import * as mcl from 'mcl-wasm' | ||
* | ||
* await mcl.init(mcl.BLS12_381) | ||
* const evm = await EVM.create({ bls: new MCLBLS(mcl) }) | ||
* ``` | ||
*/ | ||
bls?: EVMBLSInterface; | ||
stateManager?: EVMStateManagerInterface; | ||
@@ -290,7 +315,7 @@ /** | ||
*/ | ||
selfdestruct?: Set<string>; | ||
selfdestruct?: Set<PrefixedHexString>; | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
/** | ||
@@ -305,2 +330,14 @@ * The gas refund counter | ||
} | ||
export declare type EVMBLSInterface = { | ||
init?(): void; | ||
addG1(input: Uint8Array): Uint8Array; | ||
mulG1(input: Uint8Array): Uint8Array; | ||
addG2(input: Uint8Array): Uint8Array; | ||
mulG2(input: Uint8Array): Uint8Array; | ||
mapFPtoG1(input: Uint8Array): Uint8Array; | ||
mapFP2toG2(input: Uint8Array): Uint8Array; | ||
msmG1(input: Uint8Array): Uint8Array; | ||
msmG2(input: Uint8Array): Uint8Array; | ||
pairingCheck(input: Uint8Array): Uint8Array; | ||
}; | ||
/** | ||
@@ -353,7 +390,7 @@ * Log that the contract emits. | ||
export interface bn128 { | ||
ec_pairing: (input_str: string) => string; | ||
ec_add: (input_str: string) => string; | ||
ec_mul: (input_hex: string) => string; | ||
ec_pairing: (input_str: string) => PrefixedHexString; | ||
ec_add: (input_str: string) => PrefixedHexString; | ||
ec_mul: (input_hex: string) => PrefixedHexString; | ||
} | ||
export {}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -13,3 +13,3 @@ import { Common, Hardfork } from '@ethereumjs/common'; | ||
import type { CustomPrecompile, PrecompileFunc } from './precompiles/index.js'; | ||
import type { Block, Blockchain, CustomOpcode, EVMEvents, EVMInterface, EVMOpts, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, bn128 } from './types.js'; | ||
import type { Block, Blockchain, CustomOpcode, EVMBLSInterface, EVMEvents, EVMInterface, EVMOpts, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, bn128 } from './types.js'; | ||
import type { EVMStateManagerInterface } from '@ethereumjs/common'; | ||
@@ -48,2 +48,3 @@ /** | ||
get opcodes(): OpcodeList; | ||
protected readonly _bls?: EVMBLSInterface; | ||
/** | ||
@@ -50,0 +51,0 @@ * EVM is run in DEBUG mode (default: false) |
import { Chain, Common, Hardfork } from '@ethereumjs/common'; | ||
import { DefaultStateManager } from '@ethereumjs/statemanager'; | ||
import { Account, Address, AsyncEventEmitter, BIGINT_0, BIGINT_1, KECCAK256_NULL, MAX_INTEGER, bigIntToBytes, bytesToUnprefixedHex, equalsBytes, generateAddress, generateAddress2, short, zeros, } from '@ethereumjs/util'; | ||
import { Account, Address, AsyncEventEmitter, BIGINT_0, BIGINT_1, KECCAK256_NULL, KECCAK256_RLP, MAX_INTEGER, bigIntToBytes, bytesToUnprefixedHex, equalsBytes, generateAddress, generateAddress2, short, zeros, } from '@ethereumjs/util'; | ||
import debugDefault from 'debug'; | ||
@@ -13,9 +13,8 @@ import { initRustBN } from 'rustbn-wasm'; | ||
import { getOpcodesForHF } from './opcodes/index.js'; | ||
import { getActivePrecompiles, getPrecompileName } from './precompiles/index.js'; | ||
import { NobleBLS, getActivePrecompiles, getPrecompileName } from './precompiles/index.js'; | ||
import { TransientStorage } from './transientStorage.js'; | ||
import { DefaultBlockchain } from './types.js'; | ||
const { debug: createDebugLogger } = debugDefault; | ||
const debug = createDebugLogger('evm:evm'); | ||
const debugGas = createDebugLogger('evm:gas'); | ||
const debugPrecompiles = createDebugLogger('evm:precompiles'); | ||
const debug = debugDefault('evm:evm'); | ||
const debugGas = debugDefault('evm:gas'); | ||
const debugPrecompiles = debugDefault('evm:precompiles'); | ||
let initializedRustBN = undefined; | ||
@@ -58,4 +57,5 @@ /** | ||
const supportedEIPs = [ | ||
1153, 1559, 2315, 2565, 2718, 2929, 2930, 2935, 3074, 3198, 3529, 3540, 3541, 3607, 3651, | ||
3670, 3855, 3860, 4399, 4895, 4788, 4844, 5133, 5656, 6780, 6800, 7516, | ||
1153, 1559, 2537, 2565, 2718, 2929, 2930, 2935, 3074, 3198, 3529, 3540, 3541, 3607, 3651, | ||
3670, 3855, 3860, 4399, 4895, 4788, 4844, 5133, 5656, 6110, 6780, 6800, 7002, 7251, 7516, | ||
7685, 7702, 7709, | ||
]; | ||
@@ -83,2 +83,6 @@ for (const eip of this.common.eips()) { | ||
this._precompiles = getActivePrecompiles(this.common, this._customPrecompiles); | ||
if (this.common.isActivatedEIP(2537)) { | ||
this._bls = opts.bls ?? new NobleBLS(); | ||
this._bls.init?.(); | ||
} | ||
this._emit = async (topic, data) => { | ||
@@ -135,20 +139,31 @@ return new Promise((resolve) => this.events.emit(topic, data, resolve)); | ||
let gasLimit = message.gasLimit; | ||
const fromAddress = message.authcallOrigin ?? message.caller; | ||
if (this.common.isActivatedEIP(6800)) { | ||
const sendsValue = message.value !== BIGINT_0; | ||
if (message.depth === 0) { | ||
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(message.authcallOrigin ?? message.caller); | ||
gasLimit -= originAccessGas; | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access charged(${originAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(fromAddress); | ||
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`); | ||
const destAccessGas = message.accessWitness.touchTxTargetAndComputeGas(message.to, { | ||
sendsValue, | ||
}); | ||
debugGas(`destAccessGas=${destAccessGas} waived off for target at depth=0`); | ||
} | ||
let callAccessGas = message.accessWitness.touchAndChargeMessageCall(message.to); | ||
if (sendsValue) { | ||
callAccessGas += message.accessWitness.touchAndChargeValueTransfer(fromAddress, message.to); | ||
} | ||
gasLimit -= callAccessGas; | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`callAccessGas charged(${callAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
else { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access used (${originAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
} | ||
else { | ||
if (this.DEBUG) { | ||
debugGas(`callAccessGas used (${callAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
} | ||
} | ||
let account = await this.stateManager.getAccount(message.authcallOrigin ?? message.caller); | ||
let account = await this.stateManager.getAccount(fromAddress); | ||
if (!account) { | ||
@@ -167,22 +182,2 @@ account = new Account(); | ||
} | ||
if (this.common.isActivatedEIP(6800)) { | ||
if (message.depth === 0) { | ||
const sendsValue = message.value !== BIGINT_0; | ||
const destAccessGas = message.accessWitness.touchTxExistingAndComputeGas(message.to, { | ||
sendsValue, | ||
}); | ||
gasLimit -= destAccessGas; | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Destination access charged(${destAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
} | ||
else { | ||
if (this.DEBUG) { | ||
debugGas(`Destination access used (${destAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
} | ||
} | ||
} | ||
// Load `to` account | ||
@@ -281,16 +276,8 @@ let toAccount = await this.stateManager.getAccount(message.to); | ||
let gasLimit = message.gasLimit; | ||
const fromAddress = message.authcallOrigin ?? message.caller; | ||
if (this.common.isActivatedEIP(6800)) { | ||
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(message.caller); | ||
gasLimit -= originAccessGas; | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access charged(${originAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
if (message.depth === 0) { | ||
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(fromAddress); | ||
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`); | ||
} | ||
else { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access used (${originAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
} | ||
} | ||
@@ -330,8 +317,7 @@ let account = await this.stateManager.getAccount(message.caller); | ||
if (this.common.isActivatedEIP(6800)) { | ||
const sendsValue = message.value !== BIGINT_0; | ||
const contractCreateAccessGas = message.accessWitness.touchAndChargeContractCreateInit(message.to, { sendsValue }); | ||
const contractCreateAccessGas = message.accessWitness.touchAndChargeContractCreateInit(message.to); | ||
gasLimit -= contractCreateAccessGas; | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Contract create (sendsValue=${sendsValue}) charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})`); | ||
debugGas(`ContractCreateInit charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
@@ -342,3 +328,3 @@ return { execResult: OOGResult(message.gasLimit) }; | ||
if (this.DEBUG) { | ||
debugGas(`Contract create (sendsValue=${sendsValue}) charged (${contractCreateAccessGas} gas (-> ${gasLimit}))`); | ||
debugGas(`ContractCreateInit charged (${contractCreateAccessGas} gas (-> ${gasLimit}))`); | ||
} | ||
@@ -349,3 +335,5 @@ } | ||
if ((toAccount.nonce && toAccount.nonce > BIGINT_0) || | ||
!(equalsBytes(toAccount.codeHash, KECCAK256_NULL) === true)) { | ||
!(equalsBytes(toAccount.codeHash, KECCAK256_NULL) === true) || | ||
// See EIP 7610 and the discussion `https://ethereum-magicians.org/t/eip-7610-revert-creation-in-case-of-non-empty-storage` | ||
!(equalsBytes(toAccount.storageRoot, KECCAK256_RLP) === true)) { | ||
if (this.DEBUG) { | ||
@@ -401,2 +389,15 @@ debug(`Returning on address collision`); | ||
if (exit) { | ||
if (this.common.isActivatedEIP(6800)) { | ||
const createCompleteAccessGas = message.accessWitness.touchAndChargeContractCreateCompleted(message.to); | ||
gasLimit -= createCompleteAccessGas; | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debug(`ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})`); | ||
} | ||
return { execResult: OOGResult(message.gasLimit) }; | ||
} | ||
else { | ||
debug(`ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})`); | ||
} | ||
} | ||
return { | ||
@@ -421,3 +422,3 @@ createdAddress: message.to, | ||
let returnFee = BIGINT_0; | ||
if (!result.exceptionError) { | ||
if (!result.exceptionError && !this.common.isActivatedEIP(6800)) { | ||
returnFee = | ||
@@ -529,3 +530,3 @@ BigInt(result.returnValue.length) * BigInt(this.common.param('gasPrices', 'createData')); | ||
if (this.common.isActivatedEIP(6800)) { | ||
const byteCodeWriteAccessfee = message.accessWitness.touchCodeChunksRangeOnWriteAndChargeGas(message.to, 0, message.code.length - 1); | ||
const byteCodeWriteAccessfee = message.accessWitness.touchCodeChunksRangeOnWriteAndChargeGas(message.to, 0, result.returnValue.length - 1); | ||
gasLimit -= byteCodeWriteAccessfee; | ||
@@ -539,3 +540,3 @@ if (gasLimit < BIGINT_0) { | ||
else { | ||
debug(`ContractCreateComplete access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})`); | ||
debug(`byteCodeWrite access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})`); | ||
result.executionGasUsed += byteCodeWriteAccessfee; | ||
@@ -694,3 +695,3 @@ } | ||
await this._emit('beforeMessage', message); | ||
if (!message.to && this.common.isActivatedEIP(2929) === true) { | ||
if (!message.to && this.common.isActivatedEIP(2929)) { | ||
message.code = message.data; | ||
@@ -923,2 +924,3 @@ this.journal.addWarmedAddress((await this._generateAddress(message)).bytes); | ||
Hardfork.Prague, | ||
Hardfork.Osaka, | ||
]; | ||
@@ -925,0 +927,0 @@ export function OOGResult(gasLimit) { |
@@ -26,4 +26,2 @@ export declare enum ERROR { | ||
AUTHCALL_UNSET = "attempting to AUTHCALL without AUTH set", | ||
AUTHCALL_NONZERO_VALUEEXT = "attempting to execute AUTHCALL with nonzero external value", | ||
AUTH_INVALID_S = "invalid Signature: s-values greater than secp256k1n/2 are considered invalid", | ||
BLS_12_381_INVALID_INPUT_LENGTH = "invalid input length", | ||
@@ -30,0 +28,0 @@ BLS_12_381_POINT_NOT_ON_CURVE = "point not on curve", |
@@ -27,4 +27,2 @@ export var ERROR; | ||
ERROR["AUTHCALL_UNSET"] = "attempting to AUTHCALL without AUTH set"; | ||
ERROR["AUTHCALL_NONZERO_VALUEEXT"] = "attempting to execute AUTHCALL with nonzero external value"; | ||
ERROR["AUTH_INVALID_S"] = "invalid Signature: s-values greater than secp256k1n/2 are considered invalid"; | ||
// BLS errors | ||
@@ -31,0 +29,0 @@ ERROR["BLS_12_381_INVALID_INPUT_LENGTH"] = "invalid input length"; |
import { EOF } from './eof.js'; | ||
import { EVM } from './evm.js'; | ||
import { ERROR as EVMErrorMessage, EvmError } from './exceptions.js'; | ||
import { InterpreterStep } from './interpreter.js'; | ||
import { Message } from './message.js'; | ||
import { getOpcodesForHF } from './opcodes/index.js'; | ||
import { PrecompileInput, getActivePrecompiles } from './precompiles/index.js'; | ||
import { EVMInterface, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, Log, bn128 } from './types.js'; | ||
export { bn128, EOF, EVM, EvmError, EVMErrorMessage, EVMInterface, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, getActivePrecompiles, getOpcodesForHF, InterpreterStep, Log, Message, PrecompileInput, }; | ||
import { MCLBLS, NobleBLS, type PrecompileInput, getActivePrecompiles } from './precompiles/index.js'; | ||
import type { InterpreterStep } from './interpreter.js'; | ||
import type { EVMInterface, EVMOpts, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, Log, bn128 } from './types.js'; | ||
export * from './logger.js'; | ||
export type { bn128, EVMInterface, EVMOpts, EVMResult, EVMRunCallOpts, EVMRunCodeOpts, ExecResult, InterpreterStep, Log, PrecompileInput, }; | ||
export { EOF, EVM, EvmError, EVMErrorMessage, getActivePrecompiles, getOpcodesForHF, MCLBLS, Message, NobleBLS, }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -6,4 +6,5 @@ import { EOF } from './eof.js'; | ||
import { getOpcodesForHF } from './opcodes/index.js'; | ||
import { getActivePrecompiles } from './precompiles/index.js'; | ||
export { EOF, EVM, EvmError, EVMErrorMessage, getActivePrecompiles, getOpcodesForHF, Message, }; | ||
import { MCLBLS, NobleBLS, getActivePrecompiles, } from './precompiles/index.js'; | ||
export * from './logger.js'; | ||
export { EOF, EVM, EvmError, EVMErrorMessage, getActivePrecompiles, getOpcodesForHF, MCLBLS, Message, NobleBLS, }; | ||
//# sourceMappingURL=index.js.map |
@@ -11,5 +11,4 @@ import { Account } from '@ethereumjs/util'; | ||
import type { Block, Blockchain, EVMProfilerOpts, Log } from './types.js'; | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { AccessWitness } from '@ethereumjs/statemanager'; | ||
import type { Address } from '@ethereumjs/util'; | ||
import type { AccessWitnessInterface, Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { Address, PrefixedHexString } from '@ethereumjs/util'; | ||
export interface InterpreterOpts { | ||
@@ -27,7 +26,7 @@ pc?: number; | ||
*/ | ||
selfdestruct: Set<string>; | ||
selfdestruct: Set<PrefixedHexString>; | ||
/** | ||
* A map which tracks which addresses were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
} | ||
@@ -51,3 +50,3 @@ export interface Env { | ||
createdAddresses?: Set<string>; | ||
accessWitness?: AccessWitness; | ||
accessWitness?: AccessWitnessInterface; | ||
chargeCodeAccesses?: boolean; | ||
@@ -62,3 +61,2 @@ } | ||
stack: Stack; | ||
returnStack: Stack; | ||
code: Uint8Array; | ||
@@ -89,3 +87,2 @@ shouldDoJumpAnalysis: boolean; | ||
stack: bigint[]; | ||
returnStack: bigint[]; | ||
pc: number; | ||
@@ -92,0 +89,0 @@ depth: number; |
import { ConsensusAlgorithm } from '@ethereumjs/common'; | ||
import { StatelessVerkleStateManager } from '@ethereumjs/statemanager'; | ||
import { Account, BIGINT_0, BIGINT_1, MAX_UINT64, bigIntToHex, bytesToBigInt, bytesToHex, equalsBytes, } from '@ethereumjs/util'; | ||
@@ -11,4 +10,3 @@ import debugDefault from 'debug'; | ||
import { Stack } from './stack.js'; | ||
const { debug: createDebugLogger } = debugDefault; | ||
const debugGas = createDebugLogger('evm:gas'); | ||
const debugGas = debugDefault('evm:gas'); | ||
/** | ||
@@ -32,3 +30,2 @@ * Parses and executes EVM bytecode. | ||
stack: new Stack(), | ||
returnStack: new Stack(1023), | ||
code: new Uint8Array(0), | ||
@@ -134,5 +131,6 @@ validJumps: Uint8Array.from([]), | ||
this.common.isActivatedEIP(6800) && | ||
this._runState.stateManager instanceof StatelessVerkleStateManager) { | ||
// is this a code loaded from state using witnesses | ||
this._runState.env.chargeCodeAccesses === true) { | ||
const contract = this._runState.interpreter.getAddress(); | ||
if (!this._runState.stateManager.checkChunkWitnessPresent(contract, programCounter)) { | ||
if (!(await this._runState.stateManager.checkChunkWitnessPresent(contract, programCounter))) { | ||
throw Error(`Invalid witness with missing codeChunk for pc=${programCounter}`); | ||
@@ -247,3 +245,2 @@ } | ||
stack: this._runState.stack.getStack(), | ||
returnStack: this._runState.returnStack.getStack(), | ||
depth: this._env.depth, | ||
@@ -273,3 +270,3 @@ address: this._env.address, | ||
if (!(name in this.opDebuggers)) { | ||
this.opDebuggers[name] = createDebugLogger(`evm:ops:${name}`); | ||
this.opDebuggers[name] = debugDefault(`evm:ops:${name}`); | ||
} | ||
@@ -323,6 +320,2 @@ this.opDebuggers[name](JSON.stringify(opTrace)); | ||
} | ||
else if (opcode === 0x5c) { | ||
// Define a BEGINSUB as a 2 in the valid jumps array | ||
jumps[i] = 2; | ||
} | ||
} | ||
@@ -643,2 +636,3 @@ } | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -661,2 +655,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -679,2 +674,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -698,2 +694,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -718,2 +715,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -808,2 +806,3 @@ return this._baseCall(msg); | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}); | ||
@@ -810,0 +809,0 @@ let createdAddresses; |
import { Address } from '@ethereumjs/util'; | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { Account } from '@ethereumjs/util'; | ||
import type { Account, PrefixedHexString } from '@ethereumjs/util'; | ||
declare type AddressString = string; | ||
declare type HashString = string; | ||
declare type SlotString = string; | ||
@@ -18,3 +17,3 @@ export declare class Journal { | ||
accessList?: Map<AddressString, Set<SlotString>>; | ||
preimages?: Map<HashString, Uint8Array>; | ||
preimages?: Map<PrefixedHexString, Uint8Array>; | ||
constructor(stateManager: EVMStateManagerInterface, common: Common); | ||
@@ -21,0 +20,0 @@ /** |
import { Hardfork } from '@ethereumjs/common'; | ||
import { Address, RIPEMD160_ADDRESS_STRING, bigIntToHex, bytesToHex, bytesToUnprefixedHex, stripHexPrefix, toBytes, unprefixedHexToBytes, } from '@ethereumjs/util'; | ||
import { Address, RIPEMD160_ADDRESS_STRING, bytesToHex, bytesToUnprefixedHex, stripHexPrefix, unprefixedHexToBytes, } from '@ethereumjs/util'; | ||
import debugDefault from 'debug'; | ||
const { debug: createDebugLogger } = debugDefault; | ||
import { hexToBytes } from 'ethereum-cryptography/utils'; | ||
export class Journal { | ||
@@ -11,3 +11,3 @@ constructor(stateManager, common) { | ||
typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false; | ||
this._debug = createDebugLogger('statemanager:statemanager'); | ||
this._debug = debugDefault('statemanager:statemanager'); | ||
// TODO maybe call into this.clearJournal | ||
@@ -128,14 +128,7 @@ this.cleanJournal(); | ||
async cleanup() { | ||
if (this.common.gteHardfork(Hardfork.SpuriousDragon) === true) { | ||
if (this.common.gteHardfork(Hardfork.SpuriousDragon)) { | ||
for (const addressHex of this.touched) { | ||
const address = new Address(toBytes('0x' + addressHex)); | ||
const address = new Address(hexToBytes(`0x${addressHex}`)); | ||
const account = await this.stateManager.getAccount(address); | ||
if (account === undefined || account.isEmpty()) { | ||
if (this.common.isActivatedEIP(2935)) { | ||
// The history storage address is exempt of state clearing by EIP-158 if the EIP is activated | ||
const addr = bigIntToHex(this.common.param('vm', 'historyStorageAddress')).slice(2); | ||
if (addressHex === addr) { | ||
continue; | ||
} | ||
} | ||
await this.deleteAccount(address); | ||
@@ -142,0 +135,0 @@ if (this.DEBUG) { |
import { Address } from '@ethereumjs/util'; | ||
import type { PrecompileFunc } from './precompiles/index.js'; | ||
import type { AccessWitness } from '@ethereumjs/statemanager'; | ||
import type { AccessWitnessInterface } from '@ethereumjs/common'; | ||
import type { PrefixedHexString } from '@ethereumjs/util'; | ||
interface MessageOpts { | ||
@@ -19,7 +20,7 @@ to?: Address; | ||
*/ | ||
selfdestruct?: Set<string>; | ||
selfdestruct?: Set<PrefixedHexString>; | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
delegatecall?: boolean; | ||
@@ -29,3 +30,3 @@ authcallOrigin?: Address; | ||
blobVersionedHashes?: Uint8Array[]; | ||
accessWitness?: AccessWitness; | ||
accessWitness?: AccessWitnessInterface; | ||
} | ||
@@ -49,7 +50,7 @@ export declare class Message { | ||
*/ | ||
selfdestruct?: Set<string>; | ||
selfdestruct?: Set<PrefixedHexString>; | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
delegatecall: boolean; | ||
@@ -66,3 +67,3 @@ /** | ||
blobVersionedHashes?: Uint8Array[]; | ||
accessWitness?: AccessWitness; | ||
accessWitness?: AccessWitnessInterface; | ||
constructor(opts: MessageOpts); | ||
@@ -69,0 +70,0 @@ /** |
@@ -229,10 +229,2 @@ import { Hardfork } from '@ethereumjs/common'; | ||
{ | ||
eip: 2315, | ||
opcodes: { | ||
0x5c: { name: 'BEGINSUB', isAsync: false, dynamicGas: false }, | ||
0x5d: { name: 'RETURNSUB', isAsync: false, dynamicGas: false }, | ||
0x5e: { name: 'JUMPSUB', isAsync: false, dynamicGas: false }, | ||
}, | ||
}, | ||
{ | ||
eip: 3198, | ||
@@ -239,0 +231,0 @@ opcodes: { |
@@ -22,3 +22,3 @@ import type { RunState } from '../interpreter.js'; | ||
*/ | ||
export declare function accessStorageEIP2929(runState: RunState, key: Uint8Array, isSstore: boolean, common: Common): bigint; | ||
export declare function accessStorageEIP2929(runState: RunState, key: Uint8Array, isSstore: boolean, common: Common, chargeGas?: boolean): bigint; | ||
/** | ||
@@ -25,0 +25,0 @@ * Adjusts cost of SSTORE_RESET_GAS or SLOAD (aka sstorenoop) (EIP-2200) downward when storage |
@@ -13,3 +13,3 @@ import { BIGINT_0 } from '@ethereumjs/util'; | ||
export function accessAddressEIP2929(runState, address, common, chargeGas = true, isSelfdestructOrAuthcall = false) { | ||
if (common.isActivatedEIP(2929) === false) | ||
if (!common.isActivatedEIP(2929)) | ||
return BIGINT_0; | ||
@@ -21,3 +21,4 @@ // Cold | ||
// selfdestruct beneficiary address reads are charged an *additional* cold access | ||
if (chargeGas) { | ||
// if verkle not activated | ||
if (chargeGas && !common.isActivatedEIP(6800)) { | ||
return common.param('gasPrices', 'coldaccountaccess'); | ||
@@ -40,4 +41,4 @@ } | ||
*/ | ||
export function accessStorageEIP2929(runState, key, isSstore, common) { | ||
if (common.isActivatedEIP(2929) === false) | ||
export function accessStorageEIP2929(runState, key, isSstore, common, chargeGas = true) { | ||
if (!common.isActivatedEIP(2929)) | ||
return BIGINT_0; | ||
@@ -49,5 +50,7 @@ const address = runState.interpreter.getAddress().bytes; | ||
runState.interpreter.journal.addWarmedStorage(address, key); | ||
return common.param('gasPrices', 'coldsload'); | ||
if (chargeGas && !common.isActivatedEIP(6800)) { | ||
return common.param('gasPrices', 'coldsload'); | ||
} | ||
} | ||
else if (!isSstore) { | ||
else if (chargeGas && (!isSstore || common.isActivatedEIP(6800))) { | ||
return common.param('gasPrices', 'warmstorageread'); | ||
@@ -68,3 +71,3 @@ } | ||
export function adjustSstoreGasEIP2929(runState, key, defaultCost, costName, common) { | ||
if (common.isActivatedEIP(2929) === false) | ||
if (!common.isActivatedEIP(2929)) | ||
return defaultCost; | ||
@@ -71,0 +74,0 @@ const address = runState.interpreter.getAddress().bytes; |
@@ -1,6 +0,6 @@ | ||
import { Address, BIGINT_0, BIGINT_1, BIGINT_128, BIGINT_160, BIGINT_2, BIGINT_224, BIGINT_255, BIGINT_256, BIGINT_27, BIGINT_2EXP160, BIGINT_2EXP224, BIGINT_2EXP96, BIGINT_31, BIGINT_32, BIGINT_7, BIGINT_8, BIGINT_96, MAX_INTEGER_BIGINT, SECP256K1_ORDER_DIV_2, TWO_POW256, bigIntToBytes, bigIntToHex, bytesToBigInt, bytesToHex, concatBytes, ecrecover, hexToBytes, publicToAddress, setLengthLeft, setLengthRight, } from '@ethereumjs/util'; | ||
import { Account, Address, BIGINT_0, BIGINT_1, BIGINT_160, BIGINT_2, BIGINT_224, BIGINT_255, BIGINT_256, BIGINT_27, BIGINT_2EXP160, BIGINT_2EXP224, BIGINT_2EXP96, BIGINT_31, BIGINT_32, BIGINT_7, BIGINT_8, BIGINT_96, MAX_INTEGER_BIGINT, SECP256K1_ORDER_DIV_2, TWO_POW256, bigIntToAddressBytes, bigIntToBytes, bytesToBigInt, bytesToHex, concatBytes, ecrecover, getVerkleTreeIndexesForStorageSlot, hexToBytes, publicToAddress, setLengthLeft, setLengthRight, } from '@ethereumjs/util'; | ||
import { keccak256 } from 'ethereum-cryptography/keccak.js'; | ||
import { ERROR } from '../exceptions.js'; | ||
import { addresstoBytes, describeLocation, exponentiation, fromTwos, getDataSlice, jumpIsValid, jumpSubIsValid, mod, toTwos, trap, writeCallOutput, } from './util.js'; | ||
const EIP3074MAGIC = hexToBytes('0x03'); | ||
import { addresstoBytes, describeLocation, exponentiation, fromTwos, getDataSlice, jumpIsValid, mod, toTwos, trap, writeCallOutput, } from './util.js'; | ||
const EIP3074MAGIC = hexToBytes('0x04'); | ||
// the opcode functions | ||
@@ -460,4 +460,10 @@ export const handlers = new Map([ | ||
const addressBigInt = runState.stack.pop(); | ||
const size = BigInt((await runState.stateManager.getContractCode(new Address(addresstoBytes(addressBigInt)))) | ||
.length); | ||
let size; | ||
if (typeof runState.stateManager.getContractCodeSize === 'function') { | ||
size = BigInt(await runState.stateManager.getContractCodeSize(new Address(addresstoBytes(addressBigInt)))); | ||
} | ||
else { | ||
size = BigInt((await runState.stateManager.getContractCode(new Address(addresstoBytes(addressBigInt)))) | ||
.length); | ||
} | ||
runState.stack.push(size); | ||
@@ -527,3 +533,3 @@ }, | ||
const number = runState.stack.pop(); | ||
if (common.isActivatedEIP(2935)) { | ||
if (common.isActivatedEIP(7709)) { | ||
if (number >= runState.interpreter.getBlockNumber()) { | ||
@@ -534,10 +540,17 @@ runState.stack.push(BIGINT_0); | ||
const diff = runState.interpreter.getBlockNumber() - number; | ||
const historyServeWindow = common.param('vm', 'historyServeWindow'); | ||
// block lookups must be within the `historyServeWindow` | ||
if (diff > historyServeWindow || diff <= BIGINT_0) { | ||
// block lookups must be within the original window even if historyStorageAddress's | ||
// historyServeWindow is much greater than 256 | ||
if (diff > BIGINT_256 || diff <= BIGINT_0) { | ||
runState.stack.push(BIGINT_0); | ||
return; | ||
} | ||
const historyAddress = Address.fromString(bigIntToHex(common.param('vm', 'historyStorageAddress'))); | ||
const historyAddress = new Address(bigIntToAddressBytes(common.param('vm', 'historyStorageAddress'))); | ||
const historyServeWindow = common.param('vm', 'historyServeWindow'); | ||
const key = setLengthLeft(bigIntToBytes(number % historyServeWindow), 32); | ||
if (common.isActivatedEIP(6800)) { | ||
const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(number); | ||
// create witnesses and charge gas | ||
const statelessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas(historyAddress, treeIndex, subIndex); | ||
runState.interpreter.useGas(statelessGas, `BLOCKHASH`); | ||
} | ||
const storage = await runState.stateManager.getContractStorage(historyAddress, key); | ||
@@ -759,74 +772,41 @@ runState.stack.push(bytesToBigInt(storage)); | ||
[0x5b, function () { }], | ||
// 0x5c: BEGINSUB (EIP 2315) / TLOAD (EIP 1153) | ||
// 0x5c: TLOAD (EIP 1153) | ||
[ | ||
0x5c, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// BEGINSUB | ||
trap(ERROR.INVALID_BEGINSUB + ' at ' + describeLocation(runState)); | ||
} | ||
else if (common.isActivatedEIP(1153)) { | ||
// TLOAD | ||
const key = runState.stack.pop(); | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | ||
const value = runState.interpreter.transientStorageLoad(keyBuf); | ||
const valueBN = value.length ? bytesToBigInt(value) : BIGINT_0; | ||
runState.stack.push(valueBN); | ||
} | ||
function (runState) { | ||
const key = runState.stack.pop(); | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | ||
const value = runState.interpreter.transientStorageLoad(keyBuf); | ||
const valueBN = value.length ? bytesToBigInt(value) : BIGINT_0; | ||
runState.stack.push(valueBN); | ||
}, | ||
], | ||
// 0x5d: RETURNSUB (EIP 2315) / TSTORE (EIP 1153) | ||
// 0x5d: TSTORE (EIP 1153) | ||
[ | ||
0x5d, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// RETURNSUB | ||
if (runState.returnStack.length < 1) { | ||
trap(ERROR.INVALID_RETURNSUB); | ||
} | ||
const dest = runState.returnStack.pop(); | ||
runState.programCounter = Number(dest); | ||
function (runState) { | ||
// TSTORE | ||
if (runState.interpreter.isStatic()) { | ||
trap(ERROR.STATIC_STATE_CHANGE); | ||
} | ||
else if (common.isActivatedEIP(1153)) { | ||
// TSTORE | ||
if (runState.interpreter.isStatic()) { | ||
trap(ERROR.STATIC_STATE_CHANGE); | ||
} | ||
const [key, val] = runState.stack.popN(2); | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | ||
// NOTE: this should be the shortest representation | ||
let value; | ||
if (val === BIGINT_0) { | ||
value = Uint8Array.from([]); | ||
} | ||
else { | ||
value = bigIntToBytes(val); | ||
} | ||
runState.interpreter.transientStorageStore(keyBuf, value); | ||
const [key, val] = runState.stack.popN(2); | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | ||
// NOTE: this should be the shortest representation | ||
let value; | ||
if (val === BIGINT_0) { | ||
value = Uint8Array.from([]); | ||
} | ||
else { | ||
value = bigIntToBytes(val); | ||
} | ||
runState.interpreter.transientStorageStore(keyBuf, value); | ||
}, | ||
], | ||
// 0x5e: JUMPSUB (2315) / MCOPY (5656) | ||
// 0x5e: MCOPY (5656) | ||
[ | ||
0x5e, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// JUMPSUB | ||
const dest = runState.stack.pop(); | ||
if (dest > runState.interpreter.getCodeSize()) { | ||
trap(ERROR.INVALID_JUMPSUB + ' at ' + describeLocation(runState)); | ||
} | ||
const destNum = Number(dest); | ||
if (!jumpSubIsValid(runState, destNum)) { | ||
trap(ERROR.INVALID_JUMPSUB + ' at ' + describeLocation(runState)); | ||
} | ||
runState.returnStack.push(BigInt(runState.programCounter)); | ||
runState.programCounter = destNum + 1; | ||
} | ||
else if (common.isActivatedEIP(5656)) { | ||
// MCOPY | ||
const [dst, src, length] = runState.stack.popN(3); | ||
const data = runState.memory.read(Number(src), Number(length), true); | ||
runState.memory.write(Number(dst), Number(length), data); | ||
} | ||
function (runState) { | ||
const [dst, src, length] = runState.stack.popN(3); | ||
const data = runState.memory.read(Number(src), Number(length), true); | ||
runState.memory.write(Number(dst), Number(length), data); | ||
}, | ||
@@ -1016,19 +996,37 @@ ], | ||
let [authority, memOffset, memLength] = runState.stack.popN(3); | ||
if (memLength > BIGINT_128) { | ||
memLength = BIGINT_128; | ||
if (memLength > BigInt(97)) { | ||
memLength = BigInt(97); | ||
} | ||
let mem = runState.memory.read(Number(memOffset), Number(memLength)); | ||
if (mem.length < 128) { | ||
mem = setLengthRight(mem, 128); | ||
if (mem.length < 97) { | ||
mem = setLengthRight(mem, 97); | ||
} | ||
const yParity = BigInt(mem[31]); | ||
const r = mem.subarray(32, 64); | ||
const s = mem.subarray(64, 96); | ||
const commit = mem.subarray(96, 128); | ||
const yParity = BigInt(mem[0]); | ||
const r = mem.subarray(1, 33); | ||
const s = mem.subarray(33, 65); | ||
const commit = mem.subarray(65, 97); | ||
if (bytesToBigInt(s) > SECP256K1_ORDER_DIV_2) { | ||
trap(ERROR.AUTH_INVALID_S); | ||
runState.stack.push(BIGINT_0); | ||
runState.auth = undefined; | ||
return; | ||
} | ||
const paddedInvokerAddress = setLengthLeft(runState.interpreter._env.address.bytes, 32); | ||
if (yParity > BIGINT_1) { | ||
runState.stack.push(BIGINT_0); | ||
runState.auth = undefined; | ||
return; | ||
} | ||
// we don't want strick check here on authority being in address range just last 20 bytes | ||
const expectedAddress = new Address(bigIntToAddressBytes(authority, false)); | ||
const account = (await runState.stateManager.getAccount(expectedAddress)) ?? new Account(); | ||
if (account.isContract()) { | ||
// EXTCODESIZE > 0 | ||
runState.stack.push(BIGINT_0); | ||
runState.auth = undefined; | ||
return; | ||
} | ||
const accountNonce = account.nonce; | ||
const invokedAddress = setLengthLeft(runState.interpreter._env.address.bytes, 32); | ||
const chainId = setLengthLeft(bigIntToBytes(runState.interpreter.getChainId()), 32); | ||
const message = concatBytes(EIP3074MAGIC, chainId, paddedInvokerAddress, commit); | ||
const nonce = setLengthLeft(bigIntToBytes(accountNonce), 32); | ||
const message = concatBytes(EIP3074MAGIC, chainId, nonce, invokedAddress, commit); | ||
const keccakFunction = runState.interpreter._evm.common.customCrypto.keccak256 ?? keccak256; | ||
@@ -1050,3 +1048,2 @@ const msgHash = keccakFunction(message); | ||
runState.auth = address; | ||
const expectedAddress = new Address(setLengthLeft(bigIntToBytes(authority), 20)); | ||
if (!expectedAddress.equals(address)) { | ||
@@ -1066,3 +1063,3 @@ // expected address does not equal the recovered address, clear auth variable | ||
async function (runState) { | ||
const [_currentGasLimit, addr, value, _valueExt, argsOffset, argsLength, retOffset, retLength,] = runState.stack.popN(8); | ||
const [_currentGasLimit, addr, value, argsOffset, argsLength, retOffset, retLength] = runState.stack.popN(7); | ||
const toAddress = new Address(addresstoBytes(addr)); | ||
@@ -1069,0 +1066,0 @@ const gasLimit = runState.messageGasLimit; |
import { Hardfork } from '@ethereumjs/common'; | ||
import { CODE_SIZE_LEAF_KEY, getTreeIndexesForStorageSlot } from '@ethereumjs/statemanager'; | ||
import { Address, BIGINT_0, BIGINT_1, BIGINT_3, BIGINT_31, BIGINT_32, bigIntToBytes, setLengthLeft, } from '@ethereumjs/util'; | ||
import { Account, Address, BIGINT_0, BIGINT_1, BIGINT_3, BIGINT_31, BIGINT_32, VERKLE_BALANCE_LEAF_KEY, VERKLE_CODE_HASH_LEAF_KEY, VERKLE_CODE_SIZE_LEAF_KEY, VERKLE_VERSION_LEAF_KEY, bigIntToBytes, getVerkleTreeIndexesForStorageSlot, setLengthLeft, } from '@ethereumjs/util'; | ||
import { ERROR } from '../exceptions.js'; | ||
@@ -44,6 +43,13 @@ import { updateSstoreGasEIP1283 } from './EIP1283.js'; | ||
async function (runState, gas, common) { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0]; | ||
gas += accessAddressEIP2929(runState, addresstoBytes(address), common); | ||
const address = addresstoBytes(runState.stack.peek()[0]); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const balanceAddress = new Address(address); | ||
const coldAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas(balanceAddress, 0, VERKLE_BALANCE_LEAF_KEY); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, address, common, charge2929Gas); | ||
} | ||
return gas; | ||
@@ -72,3 +78,3 @@ }, | ||
gas += common.param('gasPrices', 'copy') * divCeil(dataLength, BIGINT_32); | ||
if (common.isActivatedEIP(6800)) { | ||
if (common.isActivatedEIP(6800) && runState.env.chargeCodeAccesses === true) { | ||
const contract = runState.interpreter.getAddress(); | ||
@@ -90,9 +96,16 @@ let codeEnd = _codeOffset + dataLength; | ||
async function (runState, gas, common) { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0]; | ||
gas += accessAddressEIP2929(runState, addresstoBytes(address), common); | ||
const addressBytes = addresstoBytes(runState.stack.peek()[0]); | ||
const address = new Address(addressBytes); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(address) === undefined) { | ||
let coldAccessGas = BIGINT_0; | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY); | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY); | ||
gas += coldAccessGas; | ||
// if cold access gas has been charged 2929 gas shouldn't be charged | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(6800) === true) { | ||
const address = new Address(addresstoBytes(runState.stack.peek()[0])); | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY); | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addressBytes, common, charge2929Gas); | ||
} | ||
@@ -106,17 +119,28 @@ return gas; | ||
async function (runState, gas, common) { | ||
const [address, memOffset, _codeOffset, dataLength] = runState.stack.peek(4); | ||
const [addressBigInt, memOffset, _codeOffset, dataLength] = runState.stack.peek(4); | ||
const addressBytes = addresstoBytes(addressBigInt); | ||
const address = new Address(addressBytes); | ||
gas += subMemUsage(runState, memOffset, dataLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(address), common); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(address) === undefined) { | ||
let coldAccessGas = BIGINT_0; | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY); | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY); | ||
gas += coldAccessGas; | ||
// if cold access gas has been charged 2929 gas shouldn't be charged | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addressBytes, common, charge2929Gas); | ||
} | ||
if (dataLength !== BIGINT_0) { | ||
gas += common.param('gasPrices', 'copy') * divCeil(dataLength, BIGINT_32); | ||
if (common.isActivatedEIP(6800)) { | ||
const contract = new Address(addresstoBytes(address)); | ||
let codeEnd = _codeOffset + dataLength; | ||
const codeSize = BigInt((await runState.stateManager.getContractCode(contract)).length); | ||
const codeSize = BigInt((await runState.stateManager.getContractCode(address)).length); | ||
if (codeEnd > codeSize) { | ||
codeEnd = codeSize; | ||
} | ||
gas += runState.env.accessWitness.touchCodeChunksRangeOnReadAndChargeGas(contract, Number(_codeOffset), Number(codeEnd)); | ||
gas += runState.env.accessWitness.touchCodeChunksRangeOnReadAndChargeGas(address, Number(_codeOffset), Number(codeEnd)); | ||
} | ||
@@ -146,6 +170,14 @@ } | ||
async function (runState, gas, common) { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0]; | ||
gas += accessAddressEIP2929(runState, addresstoBytes(address), common); | ||
const address = addresstoBytes(runState.stack.peek()[0]); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const codeAddress = new Address(address); | ||
let coldAccessGas = BIGINT_0; | ||
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(codeAddress, 0, VERKLE_CODE_HASH_LEAF_KEY); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, address, common, charge2929Gas); | ||
} | ||
return gas; | ||
@@ -187,10 +219,13 @@ }, | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessStorageEIP2929(runState, keyBuf, false, common); | ||
} | ||
if (common.isActivatedEIP(6800) === true) { | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const address = runState.interpreter.getAddress(); | ||
const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(key); | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, treeIndex, subIndex); | ||
const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(key); | ||
const coldAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas(address, treeIndex, subIndex); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessStorageEIP2929(runState, keyBuf, false, common, charge2929Gas); | ||
} | ||
return gas; | ||
@@ -222,3 +257,5 @@ }, | ||
else if (common.gteHardfork(Hardfork.Istanbul)) { | ||
gas += updateSstoreGasEIP2200(runState, currentStorage, originalStorage, setLengthLeftStorage(value), keyBytes, common); | ||
if (!common.isActivatedEIP(6800)) { | ||
gas += updateSstoreGasEIP2200(runState, currentStorage, originalStorage, setLengthLeftStorage(value), keyBytes, common); | ||
} | ||
} | ||
@@ -228,13 +265,16 @@ else { | ||
} | ||
if (common.isActivatedEIP(2929) === true) { | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const contract = runState.interpreter.getAddress(); | ||
const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(key); | ||
const coldAccessGas = runState.env.accessWitness.touchAddressOnWriteAndComputeGas(contract, treeIndex, subIndex); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
// We have to do this after the Istanbul (EIP2200) checks. | ||
// Otherwise, we might run out of gas, due to "sentry check" of 2300 gas, | ||
// if we deduct extra gas first. | ||
gas += accessStorageEIP2929(runState, keyBytes, true, common); | ||
gas += accessStorageEIP2929(runState, keyBytes, true, common, charge2929Gas); | ||
} | ||
if (common.isActivatedEIP(6800) === true) { | ||
const contract = runState.interpreter.getAddress(); | ||
const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(key); | ||
gas += runState.env.accessWitness.touchAddressOnWriteAndComputeGas(contract, treeIndex, subIndex); | ||
} | ||
return gas; | ||
@@ -282,6 +322,6 @@ }, | ||
const [_value, offset, length] = runState.stack.peek(3); | ||
if (common.isActivatedEIP(2929) === true) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, runState.interpreter.getAddress().bytes, common, false); | ||
} | ||
if (common.isActivatedEIP(3860) === true) { | ||
if (common.isActivatedEIP(3860)) { | ||
gas += ((length + BIGINT_31) / BIGINT_32) * common.param('gasPrices', 'initCodeWordCost'); | ||
@@ -307,6 +347,18 @@ } | ||
gas += subMemUsage(runState, outOffset, outLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, toAddress.bytes, common); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined) { | ||
// TODO: add check if toAddress is not a precompile | ||
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
if (value !== BIGINT_0) { | ||
const contractAddress = runState.interpreter.getAddress(); | ||
gas += runState.env.accessWitness.touchAndChargeValueTransfer(contractAddress, toAddress); | ||
} | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (value !== BIGINT_0) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, toAddress.bytes, common, charge2929Gas); | ||
} | ||
if (value !== BIGINT_0 && !common.isActivatedEIP(6800)) { | ||
gas += common.param('gasPrices', 'callValueTransfer'); | ||
@@ -331,10 +383,2 @@ } | ||
} | ||
if (common.isActivatedEIP(6800)) { | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
if (value !== BIGINT_0) { | ||
const contractAddress = runState.interpreter.getAddress(); | ||
gas += runState.env.accessWitness.touchAndChargeValueTransfer(contractAddress, toAddress); | ||
} | ||
} | ||
const gasLimit = maxCallGas(currentGasLimit, runState.interpreter.getGasLeft() - gas, runState, common); | ||
@@ -358,15 +402,18 @@ // note that TangerineWhistle or later this cannot happen | ||
const [currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.peek(7); | ||
const toAddress = new Address(addresstoBytes(toAddr)); | ||
gas += subMemUsage(runState, inOffset, inLength, common); | ||
gas += subMemUsage(runState, outOffset, outLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined) { | ||
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common, charge2929Gas); | ||
} | ||
if (value !== BIGINT_0) { | ||
gas += common.param('gasPrices', 'callValueTransfer'); | ||
} | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new Address(addresstoBytes(toAddr)); | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
} | ||
const gasLimit = maxCallGas(currentGasLimit, runState.interpreter.getGasLeft() - gas, runState, common); | ||
@@ -396,12 +443,16 @@ // note that TangerineWhistle or later this cannot happen | ||
const [currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] = runState.stack.peek(6); | ||
const toAddress = new Address(addresstoBytes(toAddr)); | ||
gas += subMemUsage(runState, inOffset, inLength, common); | ||
gas += subMemUsage(runState, outOffset, outLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common); | ||
} | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new Address(addresstoBytes(toAddr)); | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined) { | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common, charge2929Gas); | ||
} | ||
const gasLimit = maxCallGas(currentGasLimit, runState.interpreter.getGasLeft() - gas, runState, common); | ||
@@ -426,6 +477,6 @@ // note that TangerineWhistle or later this cannot happen | ||
gas += subMemUsage(runState, offset, length, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, runState.interpreter.getAddress().bytes, common, false); | ||
} | ||
if (common.isActivatedEIP(3860) === true) { | ||
if (common.isActivatedEIP(3860)) { | ||
gas += ((length + BIGINT_31) / BIGINT_32) * common.param('gasPrices', 'initCodeWordCost'); | ||
@@ -444,3 +495,6 @@ } | ||
async function (runState, gas, common) { | ||
const [_address, memOffset, memLength] = runState.stack.peek(3); | ||
const [address, memOffset, memLength] = runState.stack.peek(3); | ||
// Note: 2929 is always active if AUTH can be reached, | ||
// since it needs London as minimum hardfork | ||
gas += accessAddressEIP2929(runState, bigIntToBytes(address), common); | ||
gas += subMemUsage(runState, memOffset, memLength, common); | ||
@@ -457,6 +511,3 @@ return gas; | ||
} | ||
const [currentGasLimit, addr, value, valueExt, argsOffset, argsLength, retOffset, retLength,] = runState.stack.peek(8); | ||
if (valueExt !== BIGINT_0) { | ||
trap(ERROR.AUTHCALL_NONZERO_VALUEEXT); | ||
} | ||
const [currentGasLimit, addr, value, argsOffset, argsLength, retOffset, retLength] = runState.stack.peek(7); | ||
const toAddress = new Address(addresstoBytes(addr)); | ||
@@ -482,2 +533,13 @@ gas += common.param('gasPrices', 'warmstorageread'); | ||
runState.messageGasLimit = gasLimit; | ||
if (value > BIGINT_0) { | ||
const account = (await runState.stateManager.getAccount(runState.auth)) ?? new Account(); | ||
if (account.balance < value) { | ||
trap(ERROR.OUT_OF_GAS); | ||
} | ||
account.balance -= value; | ||
const toAddr = new Address(addresstoBytes(addr)); | ||
const target = (await runState.stateManager.getAccount(toAddr)) ?? new Account(); | ||
target.balance += value; | ||
await runState.stateManager.putAccount(toAddr, target); | ||
} | ||
return gas; | ||
@@ -493,10 +555,13 @@ }, | ||
gas += subMemUsage(runState, outOffset, outLength, common); | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common); | ||
} | ||
let charge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new Address(addresstoBytes(toAddr)); | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | ||
gas += coldAccessGas; | ||
charge2929Gas = coldAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common, charge2929Gas); | ||
} | ||
const gasLimit = maxCallGas(currentGasLimit, runState.interpreter.getGasLeft() - gas, runState, common); // we set TangerineWhistle or later to true here, as STATICCALL was available from Byzantium (which is after TangerineWhistle) | ||
@@ -525,6 +590,7 @@ runState.messageGasLimit = gasLimit; | ||
const selfdestructToAddress = new Address(addresstoBytes(selfdestructToaddressBigInt)); | ||
const contractAddress = runState.interpreter.getAddress(); | ||
let deductGas = false; | ||
const balance = await runState.interpreter.getExternalBalance(contractAddress); | ||
if (common.gteHardfork(Hardfork.SpuriousDragon)) { | ||
// EIP-161: State Trie Clearing | ||
const balance = await runState.interpreter.getExternalBalance(runState.interpreter.getAddress()); | ||
if (balance > BIGINT_0) { | ||
@@ -548,5 +614,24 @@ // This technically checks if account is empty or non-existent | ||
} | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, selfdestructToAddress.bytes, common, true, true); | ||
let selfDestructToCharge2929Gas = true; | ||
if (common.isActivatedEIP(6800)) { | ||
// read accesses for version and code size | ||
if (runState.interpreter._evm.getPrecompile(contractAddress) === undefined) { | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(contractAddress, 0, VERKLE_VERSION_LEAF_KEY); | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(contractAddress, 0, VERKLE_CODE_SIZE_LEAF_KEY); | ||
} | ||
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas(contractAddress, 0, VERKLE_BALANCE_LEAF_KEY); | ||
if (balance > BIGINT_0) { | ||
gas += runState.env.accessWitness.touchAddressOnWriteAndComputeGas(contractAddress, 0, VERKLE_BALANCE_LEAF_KEY); | ||
} | ||
let selfDestructToColdAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas(selfdestructToAddress, 0, VERKLE_BALANCE_LEAF_KEY); | ||
if (balance > BIGINT_0) { | ||
selfDestructToColdAccessGas += | ||
runState.env.accessWitness.touchAddressOnWriteAndComputeGas(selfdestructToAddress, 0, VERKLE_BALANCE_LEAF_KEY); | ||
} | ||
gas += selfDestructToColdAccessGas; | ||
selfDestructToCharge2929Gas = selfDestructToColdAccessGas === BIGINT_0; | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, selfdestructToAddress.bytes, common, selfDestructToCharge2929Gas, true); | ||
} | ||
return gas; | ||
@@ -553,0 +638,0 @@ }, |
@@ -47,6 +47,2 @@ import type { RunState } from '../interpreter.js'; | ||
/** | ||
* Checks if a jumpsub is valid given a destination (defined as a 2 in the validJumps array) | ||
*/ | ||
export declare function jumpSubIsValid(runState: RunState, dest: number): boolean; | ||
/** | ||
* Returns an overflow-safe slice of an array. It right-pads | ||
@@ -53,0 +49,0 @@ * the data with zeros to `length`. |
@@ -110,8 +110,2 @@ import { Hardfork } from '@ethereumjs/common'; | ||
/** | ||
* Checks if a jumpsub is valid given a destination (defined as a 2 in the validJumps array) | ||
*/ | ||
export function jumpSubIsValid(runState, dest) { | ||
return runState.validJumps[dest] === 2; | ||
} | ||
/** | ||
* Returns an overflow-safe slice of an array. It right-pads | ||
@@ -118,0 +112,0 @@ * the data with zeros to `length`. |
import { type Address } from '@ethereumjs/util'; | ||
import { MCLBLS, NobleBLS } from './bls12_381/index.js'; | ||
import type { PrecompileFunc, PrecompileInput } from './types.js'; | ||
@@ -39,4 +40,4 @@ import type { Common } from '@ethereumjs/common'; | ||
declare function getPrecompileName(addressUnprefixedStr: string): string | undefined; | ||
export { getActivePrecompiles, getPrecompileName, precompileEntries, precompiles, ripemdPrecompileAddress, }; | ||
export { getActivePrecompiles, getPrecompileName, MCLBLS, NobleBLS, precompileEntries, precompiles, ripemdPrecompileAddress, }; | ||
export type { AddPrecompile, CustomPrecompile, DeletePrecompile, PrecompileFunc, PrecompileInput }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -13,2 +13,12 @@ import { Hardfork } from '@ethereumjs/common'; | ||
import { precompile0a } from './0a-kzg-point-evaluation.js'; | ||
import { precompile0b } from './0b-bls12-g1add.js'; | ||
import { precompile0c } from './0c-bls12-g1mul.js'; | ||
import { precompile0d } from './0d-bls12-g1msm.js'; | ||
import { precompile0e } from './0e-bls12-g2add.js'; | ||
import { precompile0f } from './0f-bls12-g2mul.js'; | ||
import { precompile10 } from './10-bls12-g2msm.js'; | ||
import { precompile11 } from './11-bls12-pairing.js'; | ||
import { precompile12 } from './12-bls12-map-fp-to-g1.js'; | ||
import { precompile13 } from './13-bls12-map-fp2-to-g2.js'; | ||
import { MCLBLS, NobleBLS } from './bls12_381/index.js'; | ||
var PrecompileAvailabilityCheck; | ||
@@ -111,2 +121,83 @@ (function (PrecompileAvailabilityCheck) { | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000b', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0b, | ||
name: 'BLS12_G1ADD', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000c', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0c, | ||
name: 'BLS12_G1MUL', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000d', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0d, | ||
name: 'BLS12_G1MSM', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000e', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0e, | ||
name: 'BLS12_G2ADD', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000f', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0f, | ||
name: 'BLS12_G2MUL', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000010', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile10, | ||
name: 'BLS12_G2MSM', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000011', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile11, | ||
name: 'BLS12_PAIRING', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000012', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile12, | ||
name: 'BLS12_MAP_FP_TO_G1', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000013', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile13, | ||
name: 'BLS12_MAP_FP2_TO_G2', | ||
}, | ||
]; | ||
@@ -124,2 +215,11 @@ const precompiles = { | ||
'000000000000000000000000000000000000000a': precompile0a, | ||
'000000000000000000000000000000000000000b': precompile0b, | ||
'000000000000000000000000000000000000000c': precompile0c, | ||
'000000000000000000000000000000000000000d': precompile0d, | ||
'000000000000000000000000000000000000000e': precompile0e, | ||
'000000000000000000000000000000000000000f': precompile0f, | ||
'0000000000000000000000000000000000000010': precompile10, | ||
'0000000000000000000000000000000000000011': precompile11, | ||
'0000000000000000000000000000000000000012': precompile12, | ||
'0000000000000000000000000000000000000013': precompile13, | ||
}; | ||
@@ -153,3 +253,3 @@ function getActivePrecompiles(common, customPrecompiles) { | ||
} | ||
export { getActivePrecompiles, getPrecompileName, precompileEntries, precompiles, ripemdPrecompileAddress, }; | ||
export { getActivePrecompiles, getPrecompileName, MCLBLS, NobleBLS, precompileEntries, precompiles, ripemdPrecompileAddress, }; | ||
//# sourceMappingURL=index.js.map |
@@ -8,5 +8,4 @@ import type { EvmError } from './exceptions.js'; | ||
import type { PrecompileFunc } from './precompiles/types.js'; | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { AccessWitness } from '@ethereumjs/statemanager'; | ||
import type { Account, Address, AsyncEventEmitter } from '@ethereumjs/util'; | ||
import type { AccessWitnessInterface, Common, EVMStateManagerInterface } from '@ethereumjs/common'; | ||
import type { Account, Address, AsyncEventEmitter, PrefixedHexString } from '@ethereumjs/util'; | ||
export declare type DeleteOpcode = { | ||
@@ -70,3 +69,3 @@ opcode: number; | ||
*/ | ||
selfdestruct?: Set<string>; | ||
selfdestruct?: Set<PrefixedHexString>; | ||
/** | ||
@@ -99,3 +98,3 @@ * The address of the account that is executing this code (`address(this)`). Defaults to the zero address. | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
/** | ||
@@ -118,3 +117,3 @@ * Skip balance checks if true. If caller balance is less than message value, | ||
message?: Message; | ||
accessWitness?: AccessWitness; | ||
accessWitness?: AccessWitnessInterface; | ||
} | ||
@@ -141,3 +140,3 @@ interface NewContractEvent { | ||
accessList?: Map<string, Set<string>>; | ||
preimages?: Map<string, Uint8Array>; | ||
preimages?: Map<PrefixedHexString, Uint8Array>; | ||
addAlwaysWarmAddress(address: string, addToAccessList?: boolean): void; | ||
@@ -168,7 +167,6 @@ addAlwaysWarmSlot(address: string, slot: string, addToAccessList?: boolean): void; | ||
* - [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - Fee market change for ETH 1.0 chain | ||
* - [EIP-2315](https://eips.ethereum.org/EIPS/eip-2315) - Simple subroutines for the EVM (`outdated`) | ||
* - [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) - BLS precompiles (removed in v4.0.0, see latest v3 release) | ||
* - [EIP-2565](https://eips.ethereum.org/EIPS/eip-2565) - ModExp gas cost | ||
* - [EIP-2718](https://eips.ethereum.org/EIPS/eip-2565) - Transaction Types | ||
* - [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) - Save historical block hashes in state (`experimental`) | ||
* - [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) - Serve historical block hashes from state (Prague) | ||
* - [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) - gas cost increases for state access opcodes | ||
@@ -192,7 +190,13 @@ * - [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) - Optional access list tx type | ||
* - [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) - Shard Blob Transactions (Cancun) | ||
* - [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) - EOA code transactions (Prague) (`outdated`) | ||
* - [EIP-7709](https://eips.ethereum.org/EIPS/eip-7709) - Read BLOCKHASH from storage and update cost (Osaka) | ||
* - [EIP-4895](https://eips.ethereum.org/EIPS/eip-4895) - Beacon chain push withdrawals as operations (Shanghai) | ||
* - [EIP-5133](https://eips.ethereum.org/EIPS/eip-5133) - Delaying Difficulty Bomb to mid-September 2022 (Gray Glacier) | ||
* - [EIP-5656](https://eips.ethereum.org/EIPS/eip-5656) - MCOPY - Memory copying instruction (Cancun) | ||
* - [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110) - Supply validator deposits on chain (Prague) | ||
* - [EIP-6780](https://eips.ethereum.org/EIPS/eip-6780) - SELFDESTRUCT only in same transaction (Cancun) | ||
* - [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) - Execution layer triggerable withdrawals (Prague) | ||
* - [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) - Execution layer triggerable validator consolidations (Prague) | ||
* - [EIP-7516](https://eips.ethereum.org/EIPS/eip-7516) - BLOBBASEFEE opcode (Cancun) | ||
* - [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) - General purpose execution layer requests (Prague) | ||
* | ||
@@ -238,2 +242,23 @@ * *Annotations:* | ||
customPrecompiles?: CustomPrecompile[]; | ||
/** | ||
* For the EIP-2537 BLS Precompiles, the native JS `@noble/curves` | ||
* https://github.com/paulmillr/noble-curves BLS12-381 curve implementation | ||
* is used (see `noble.ts` file in the `precompiles` folder). | ||
* | ||
* To use an alternative implementation this option can be used by passing | ||
* in a wrapper implementation integrating the desired library and adhering | ||
* to the `EVMBLSInterface` specification. | ||
* | ||
* An interface for the MCL WASM implementation https://github.com/herumi/mcl-wasm | ||
* is shipped with this library which can be used as follows (with `mcl-wasm` being | ||
* explicitly added to the set of dependencies): | ||
* | ||
* ```ts | ||
* import * as mcl from 'mcl-wasm' | ||
* | ||
* await mcl.init(mcl.BLS12_381) | ||
* const evm = await EVM.create({ bls: new MCLBLS(mcl) }) | ||
* ``` | ||
*/ | ||
bls?: EVMBLSInterface; | ||
stateManager?: EVMStateManagerInterface; | ||
@@ -290,7 +315,7 @@ /** | ||
*/ | ||
selfdestruct?: Set<string>; | ||
selfdestruct?: Set<PrefixedHexString>; | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string>; | ||
createdAddresses?: Set<PrefixedHexString>; | ||
/** | ||
@@ -305,2 +330,14 @@ * The gas refund counter | ||
} | ||
export declare type EVMBLSInterface = { | ||
init?(): void; | ||
addG1(input: Uint8Array): Uint8Array; | ||
mulG1(input: Uint8Array): Uint8Array; | ||
addG2(input: Uint8Array): Uint8Array; | ||
mulG2(input: Uint8Array): Uint8Array; | ||
mapFPtoG1(input: Uint8Array): Uint8Array; | ||
mapFP2toG2(input: Uint8Array): Uint8Array; | ||
msmG1(input: Uint8Array): Uint8Array; | ||
msmG2(input: Uint8Array): Uint8Array; | ||
pairingCheck(input: Uint8Array): Uint8Array; | ||
}; | ||
/** | ||
@@ -353,7 +390,7 @@ * Log that the contract emits. | ||
export interface bn128 { | ||
ec_pairing: (input_str: string) => string; | ||
ec_add: (input_str: string) => string; | ||
ec_mul: (input_hex: string) => string; | ||
ec_pairing: (input_str: string) => PrefixedHexString; | ||
ec_add: (input_str: string) => PrefixedHexString; | ||
ec_mul: (input_hex: string) => PrefixedHexString; | ||
} | ||
export {}; | ||
//# sourceMappingURL=types.d.ts.map |
{ | ||
"name": "@ethereumjs/evm", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "JavaScript Ethereum Virtual Machine (EVM) implementation", | ||
@@ -50,15 +50,17 @@ "keywords": [ | ||
"profiling": "0x ./benchmarks/run.js profiling", | ||
"test": "npm run test:node && npm run test:browser", | ||
"test:browser": "npx vitest run -c=vitest.config.browser.ts --browser.provider=playwright --browser.name=webkit --browser.headless", | ||
"test": "npm run test:node", | ||
"test:browser": "npx vitest run --config=./vitest.config.browser.mts", | ||
"test:node": "npx vitest run", | ||
"tsc": "../../config/cli/ts-compile.sh" | ||
"tsc": "../../config/cli/ts-compile.sh", | ||
"visualize:bundle": "npx vite build --config=./vite.config.bundler.ts --emptyOutDir=false --outDir ." | ||
}, | ||
"dependencies": { | ||
"@ethereumjs/common": "^4.3.0", | ||
"@ethereumjs/statemanager": "^2.3.0", | ||
"@ethereumjs/tx": "^5.3.0", | ||
"@ethereumjs/util": "^9.0.3", | ||
"@ethereumjs/common": "^4.4.0", | ||
"@ethereumjs/statemanager": "^2.4.0", | ||
"@ethereumjs/tx": "^5.4.0", | ||
"@ethereumjs/util": "^9.1.0", | ||
"@types/debug": "^4.1.9", | ||
"debug": "^4.3.3", | ||
"ethereum-cryptography": "^2.1.3", | ||
"ethereum-cryptography": "^2.2.1", | ||
"@noble/curves": "^1.4.2", | ||
"rustbn-wasm": "^0.4.0" | ||
@@ -72,8 +74,11 @@ }, | ||
"@types/node-dir": "^0.0.34", | ||
"@types/rollup-plugin-visualizer": "^4.2.4", | ||
"benchmark": "^2.1.4", | ||
"kzg-wasm": "^0.3.1", | ||
"kzg-wasm": "^0.4.0", | ||
"level": "^8.0.0", | ||
"mcl-wasm": "^1.5.0", | ||
"memory-level": "^1.0.0", | ||
"minimist": "^1.2.5", | ||
"node-dir": "^0.1.17", | ||
"rollup-plugin-visualizer": "^5.12.0", | ||
"solc": "^0.8.1" | ||
@@ -80,0 +85,0 @@ }, |
@@ -100,2 +100,55 @@ # @ethereumjs/evm | ||
### Precompiles | ||
This library support all EVM precompiles up to the `Prague` hardfork. | ||
The following code allows to run precompiles in isolation, e.g. for testing purposes: | ||
```ts | ||
// ./examples/precompile.ts | ||
import { Chain, Common, Hardfork } from '@ethereumjs/common' | ||
import { EVM, getActivePrecompiles } from '@ethereumjs/evm' | ||
import { bytesToHex, hexToBytes } from '@ethereumjs/util' | ||
const main = async () => { | ||
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Prague }) | ||
// Taken from test/eips/precompiles/bls/add_G1_bls.json | ||
const data = hexToBytes( | ||
'0x0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e100000000000000000000000000000000112b98340eee2777cc3c14163dea3ec97977ac3dc5c70da32e6e87578f44912e902ccef9efe28d4a78b8999dfbca942600000000000000000000000000000000186b28d92356c4dfec4b5201ad099dbdede3781f8998ddf929b4cd7756192185ca7b8f4ef7088f813270ac3d48868a21' | ||
) | ||
const gasLimit = BigInt(5000000) | ||
const evm = await EVM.create({ common }) | ||
const precompile = getActivePrecompiles(common).get('000000000000000000000000000000000000000b')! | ||
const callData = { | ||
data, | ||
gasLimit, | ||
common, | ||
_EVM: evm, | ||
} | ||
const result = await precompile(callData) | ||
console.log(`Precompile result:${bytesToHex(result.returnValue)}`) | ||
} | ||
main() | ||
``` | ||
### EIP-2537 BLS Precompiles (Prague) | ||
Starting with `v3.1.0` the EVM support the BLS precompiles introduced with [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537). These precompiles run natively using the [@noble/curves](https://github.com/paulmillr/noble-curves) library (❤️ to `@paulmillr`!). | ||
An alternative WASM implementation (using [bls-wasm](https://github.com/herumi/bls-wasm)) can be optionally used like this if needed for performance reasons: | ||
```ts | ||
import { EVM, MCLBLS } from '@ethereumjs/evm' | ||
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Prague }) | ||
await mcl.init(mcl.BLS12_381) | ||
const mclbls = new MCLBLS(mcl) | ||
const evm = await EVM.create({ common, bls }) | ||
``` | ||
## Examples | ||
@@ -222,7 +275,6 @@ | ||
- [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - Fee market change for ETH 1.0 chain | ||
- [EIP-2315](https://eips.ethereum.org/EIPS/eip-2315) - Simple subroutines for the EVM (`outdated`) | ||
- [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) - BLS precompiles (removed in v4.0.0, see latest v3 release) | ||
- [EIP-2565](https://eips.ethereum.org/EIPS/eip-2565) - ModExp gas cost | ||
- [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) - Transaction Types | ||
- [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) - Save historical block hashes in state (`experimental`) | ||
- [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) - Serve historical block hashes from state (Prague) | ||
- [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) - gas cost increases for state access opcodes | ||
@@ -249,4 +301,10 @@ - [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) - Optional access list tx type | ||
- [EIP-5656](https://eips.ethereum.org/EIPS/eip-5656) - MCOPY - Memory copying instruction (Cancun) | ||
- [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110) - Supply validator deposits on chain (Prague) | ||
- [EIP-6780](https://eips.ethereum.org/EIPS/eip-6780) - SELFDESTRUCT only in same transaction (Cancun) | ||
- [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) - Execution layer triggerable withdrawals (Prague) | ||
- [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) - Execution layer triggerable validator consolidations (Prague) | ||
- [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) - EOA code transactions (Prague) (`outdated`) | ||
- [EIP-7709](https://eips.ethereum.org/EIPS/eip-7709) - Read BLOCKHASH from storage and update cost (Osaka) | ||
- [EIP-7516](https://eips.ethereum.org/EIPS/eip-7516) - BLOBBASEFEE opcode (Cancun) | ||
- [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) - General purpose execution layer requests (Prague) | ||
@@ -253,0 +311,0 @@ ### WASM Crypto Support |
143
src/evm.ts
@@ -10,2 +10,3 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' | ||
KECCAK256_NULL, | ||
KECCAK256_RLP, | ||
MAX_INTEGER, | ||
@@ -30,3 +31,3 @@ bigIntToBytes, | ||
import { getOpcodesForHF } from './opcodes/index.js' | ||
import { getActivePrecompiles, getPrecompileName } from './precompiles/index.js' | ||
import { NobleBLS, getActivePrecompiles, getPrecompileName } from './precompiles/index.js' | ||
import { TransientStorage } from './transientStorage.js' | ||
@@ -45,2 +46,3 @@ import { DefaultBlockchain } from './types.js' | ||
CustomOpcode, | ||
EVMBLSInterface, | ||
EVMEvents, | ||
@@ -56,7 +58,6 @@ EVMInterface, | ||
import type { EVMStateManagerInterface } from '@ethereumjs/common' | ||
const { debug: createDebugLogger } = debugDefault | ||
const debug = createDebugLogger('evm:evm') | ||
const debugGas = createDebugLogger('evm:gas') | ||
const debugPrecompiles = createDebugLogger('evm:precompiles') | ||
const debug = debugDefault('evm:evm') | ||
const debugGas = debugDefault('evm:gas') | ||
const debugPrecompiles = debugDefault('evm:precompiles') | ||
@@ -92,2 +93,3 @@ let initializedRustBN: bn128 | undefined = undefined | ||
Hardfork.Prague, | ||
Hardfork.Osaka, | ||
] | ||
@@ -137,2 +139,4 @@ protected _tx?: { | ||
protected readonly _bls?: EVMBLSInterface | ||
/** | ||
@@ -161,3 +165,3 @@ * EVM is run in DEBUG mode (default: false) | ||
const opts = createOpts ?? ({} as EVMOpts) | ||
const bn128 = initializedRustBN ?? (await initRustBN()) | ||
const bn128 = initializedRustBN ?? ((await initRustBN()) as bn128) | ||
initializedRustBN = bn128 | ||
@@ -202,4 +206,5 @@ | ||
const supportedEIPs = [ | ||
1153, 1559, 2315, 2565, 2718, 2929, 2930, 2935, 3074, 3198, 3529, 3540, 3541, 3607, 3651, | ||
3670, 3855, 3860, 4399, 4895, 4788, 4844, 5133, 5656, 6780, 6800, 7516, | ||
1153, 1559, 2537, 2565, 2718, 2929, 2930, 2935, 3074, 3198, 3529, 3540, 3541, 3607, 3651, | ||
3670, 3855, 3860, 4399, 4895, 4788, 4844, 5133, 5656, 6110, 6780, 6800, 7002, 7251, 7516, | ||
7685, 7702, 7709, | ||
] | ||
@@ -236,2 +241,7 @@ | ||
if (this.common.isActivatedEIP(2537)) { | ||
this._bls = opts.bls ?? new NobleBLS() | ||
this._bls.init?.() | ||
} | ||
this._emit = async (topic: string, data: any): Promise<void> => { | ||
@@ -264,23 +274,34 @@ return new Promise((resolve) => this.events.emit(topic as keyof EVMEvents, data, resolve)) | ||
let gasLimit = message.gasLimit | ||
const fromAddress = message.authcallOrigin ?? message.caller | ||
if (this.common.isActivatedEIP(6800)) { | ||
const sendsValue = message.value !== BIGINT_0 | ||
if (message.depth === 0) { | ||
const originAccessGas = message.accessWitness!.touchTxOriginAndComputeGas( | ||
message.authcallOrigin ?? message.caller | ||
) | ||
gasLimit -= originAccessGas | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access charged(${originAccessGas}) caused OOG (-> ${gasLimit})`) | ||
} | ||
return { execResult: OOGResult(message.gasLimit) } | ||
} else { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access used (${originAccessGas} gas (-> ${gasLimit}))`) | ||
} | ||
const originAccessGas = message.accessWitness!.touchTxOriginAndComputeGas(fromAddress) | ||
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`) | ||
const destAccessGas = message.accessWitness!.touchTxTargetAndComputeGas(message.to, { | ||
sendsValue, | ||
}) | ||
debugGas(`destAccessGas=${destAccessGas} waived off for target at depth=0`) | ||
} | ||
let callAccessGas = message.accessWitness!.touchAndChargeMessageCall(message.to) | ||
if (sendsValue) { | ||
callAccessGas += message.accessWitness!.touchAndChargeValueTransfer(fromAddress, message.to) | ||
} | ||
gasLimit -= callAccessGas | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`callAccessGas charged(${callAccessGas}) caused OOG (-> ${gasLimit})`) | ||
} | ||
return { execResult: OOGResult(message.gasLimit) } | ||
} else { | ||
if (this.DEBUG) { | ||
debugGas(`callAccessGas used (${callAccessGas} gas (-> ${gasLimit}))`) | ||
} | ||
} | ||
} | ||
let account = await this.stateManager.getAccount(message.authcallOrigin ?? message.caller) | ||
let account = await this.stateManager.getAccount(fromAddress) | ||
if (!account) { | ||
@@ -299,22 +320,2 @@ account = new Account() | ||
if (this.common.isActivatedEIP(6800)) { | ||
if (message.depth === 0) { | ||
const sendsValue = message.value !== BIGINT_0 | ||
const destAccessGas = message.accessWitness!.touchTxExistingAndComputeGas(message.to, { | ||
sendsValue, | ||
}) | ||
gasLimit -= destAccessGas | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Destination access charged(${destAccessGas}) caused OOG (-> ${gasLimit})`) | ||
} | ||
return { execResult: OOGResult(message.gasLimit) } | ||
} else { | ||
if (this.DEBUG) { | ||
debugGas(`Destination access used (${destAccessGas} gas (-> ${gasLimit}))`) | ||
} | ||
} | ||
} | ||
} | ||
// Load `to` account | ||
@@ -421,15 +422,8 @@ let toAccount = await this.stateManager.getAccount(message.to) | ||
let gasLimit = message.gasLimit | ||
const fromAddress = message.authcallOrigin ?? message.caller | ||
if (this.common.isActivatedEIP(6800)) { | ||
const originAccessGas = message.accessWitness!.touchTxOriginAndComputeGas(message.caller) | ||
gasLimit -= originAccessGas | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access charged(${originAccessGas}) caused OOG (-> ${gasLimit})`) | ||
} | ||
return { execResult: OOGResult(message.gasLimit) } | ||
} else { | ||
if (this.DEBUG) { | ||
debugGas(`Origin access used (${originAccessGas} gas (-> ${gasLimit}))`) | ||
} | ||
if (message.depth === 0) { | ||
const originAccessGas = message.accessWitness!.touchTxOriginAndComputeGas(fromAddress) | ||
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`) | ||
} | ||
@@ -478,6 +472,4 @@ } | ||
if (this.common.isActivatedEIP(6800)) { | ||
const sendsValue = message.value !== BIGINT_0 | ||
const contractCreateAccessGas = message.accessWitness!.touchAndChargeContractCreateInit( | ||
message.to, | ||
{ sendsValue } | ||
message.to | ||
) | ||
@@ -488,3 +480,3 @@ gasLimit -= contractCreateAccessGas | ||
debugGas( | ||
`Contract create (sendsValue=${sendsValue}) charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})` | ||
`ContractCreateInit charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})` | ||
) | ||
@@ -495,5 +487,3 @@ } | ||
if (this.DEBUG) { | ||
debugGas( | ||
`Contract create (sendsValue=${sendsValue}) charged (${contractCreateAccessGas} gas (-> ${gasLimit}))` | ||
) | ||
debugGas(`ContractCreateInit charged (${contractCreateAccessGas} gas (-> ${gasLimit}))`) | ||
} | ||
@@ -506,3 +496,5 @@ } | ||
(toAccount.nonce && toAccount.nonce > BIGINT_0) || | ||
!(equalsBytes(toAccount.codeHash, KECCAK256_NULL) === true) | ||
!(equalsBytes(toAccount.codeHash, KECCAK256_NULL) === true) || | ||
// See EIP 7610 and the discussion `https://ethereum-magicians.org/t/eip-7610-revert-creation-in-case-of-non-empty-storage` | ||
!(equalsBytes(toAccount.storageRoot, KECCAK256_RLP) === true) | ||
) { | ||
@@ -565,3 +557,22 @@ if (this.DEBUG) { | ||
} | ||
if (exit) { | ||
if (this.common.isActivatedEIP(6800)) { | ||
const createCompleteAccessGas = | ||
message.accessWitness!.touchAndChargeContractCreateCompleted(message.to) | ||
gasLimit -= createCompleteAccessGas | ||
if (gasLimit < BIGINT_0) { | ||
if (this.DEBUG) { | ||
debug( | ||
`ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})` | ||
) | ||
} | ||
return { execResult: OOGResult(message.gasLimit) } | ||
} else { | ||
debug( | ||
`ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})` | ||
) | ||
} | ||
} | ||
return { | ||
@@ -589,3 +600,3 @@ createdAddress: message.to, | ||
let returnFee = BIGINT_0 | ||
if (!result.exceptionError) { | ||
if (!result.exceptionError && !this.common.isActivatedEIP(6800)) { | ||
returnFee = | ||
@@ -711,3 +722,3 @@ BigInt(result.returnValue.length) * BigInt(this.common.param('gasPrices', 'createData')) | ||
0, | ||
message.code!.length - 1 | ||
result.returnValue.length - 1 | ||
) | ||
@@ -723,5 +734,3 @@ gasLimit -= byteCodeWriteAccessfee | ||
} else { | ||
debug( | ||
`ContractCreateComplete access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})` | ||
) | ||
debug(`byteCodeWrite access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})`) | ||
result.executionGasUsed += byteCodeWriteAccessfee | ||
@@ -910,3 +919,3 @@ } | ||
if (!message.to && this.common.isActivatedEIP(2929) === true) { | ||
if (!message.to && this.common.isActivatedEIP(2929)) { | ||
message.code = message.data | ||
@@ -913,0 +922,0 @@ this.journal.addWarmedAddress((await this._generateAddress(message)).bytes) |
@@ -27,4 +27,2 @@ export enum ERROR { | ||
AUTHCALL_UNSET = 'attempting to AUTHCALL without AUTH set', | ||
AUTHCALL_NONZERO_VALUEEXT = 'attempting to execute AUTHCALL with nonzero external value', | ||
AUTH_INVALID_S = 'invalid Signature: s-values greater than secp256k1n/2 are considered invalid', | ||
@@ -31,0 +29,0 @@ // BLS errors |
import { EOF } from './eof.js' | ||
import { EVM } from './evm.js' | ||
import { ERROR as EVMErrorMessage, EvmError } from './exceptions.js' | ||
import { InterpreterStep } from './interpreter.js' | ||
import { Message } from './message.js' | ||
import { getOpcodesForHF } from './opcodes/index.js' | ||
import { PrecompileInput, getActivePrecompiles } from './precompiles/index.js' | ||
import { | ||
MCLBLS, | ||
NobleBLS, | ||
type PrecompileInput, | ||
getActivePrecompiles, | ||
} from './precompiles/index.js' | ||
import type { InterpreterStep } from './interpreter.js' | ||
import type { | ||
EVMInterface, | ||
EVMOpts, | ||
EVMResult, | ||
@@ -17,9 +24,8 @@ EVMRunCallOpts, | ||
} from './types.js' | ||
export { | ||
export * from './logger.js' | ||
export type { | ||
bn128, | ||
EOF, | ||
EVM, | ||
EvmError, | ||
EVMErrorMessage, | ||
EVMInterface, | ||
EVMOpts, | ||
EVMResult, | ||
@@ -29,8 +35,17 @@ EVMRunCallOpts, | ||
ExecResult, | ||
getActivePrecompiles, | ||
getOpcodesForHF, | ||
InterpreterStep, | ||
Log, | ||
Message, | ||
PrecompileInput, | ||
} | ||
export { | ||
EOF, | ||
EVM, | ||
EvmError, | ||
EVMErrorMessage, | ||
getActivePrecompiles, | ||
getOpcodesForHF, | ||
MCLBLS, | ||
Message, | ||
NobleBLS, | ||
} |
import { ConsensusAlgorithm } from '@ethereumjs/common' | ||
import { StatelessVerkleStateManager } from '@ethereumjs/statemanager' | ||
import { | ||
@@ -27,8 +26,6 @@ Account, | ||
import type { Block, Blockchain, EVMProfilerOpts, EVMResult, Log } from './types.js' | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common' | ||
import type { AccessWitness } from '@ethereumjs/statemanager' | ||
import type { Address } from '@ethereumjs/util' | ||
const { debug: createDebugLogger } = debugDefault | ||
import type { AccessWitnessInterface, Common, EVMStateManagerInterface } from '@ethereumjs/common' | ||
import type { Address, PrefixedHexString } from '@ethereumjs/util' | ||
const debugGas = createDebugLogger('evm:gas') | ||
const debugGas = debugDefault('evm:gas') | ||
@@ -48,3 +45,3 @@ export interface InterpreterOpts { | ||
*/ | ||
selfdestruct: Set<string> | ||
selfdestruct: Set<PrefixedHexString> | ||
@@ -54,3 +51,3 @@ /** | ||
*/ | ||
createdAddresses?: Set<string> | ||
createdAddresses?: Set<PrefixedHexString> | ||
} | ||
@@ -75,3 +72,3 @@ | ||
createdAddresses?: Set<string> | ||
accessWitness?: AccessWitness | ||
accessWitness?: AccessWitnessInterface | ||
chargeCodeAccesses?: boolean | ||
@@ -87,3 +84,2 @@ } | ||
stack: Stack | ||
returnStack: Stack | ||
code: Uint8Array | ||
@@ -114,3 +110,2 @@ shouldDoJumpAnalysis: boolean | ||
stack: bigint[] | ||
returnStack: bigint[] | ||
pc: number | ||
@@ -174,3 +169,2 @@ depth: number | ||
stack: new Stack(), | ||
returnStack: new Stack(1023), // 1023 return stack height limit per EIP 2315 spec | ||
code: new Uint8Array(0), | ||
@@ -280,10 +274,9 @@ validJumps: Uint8Array.from([]), | ||
this.common.isActivatedEIP(6800) && | ||
this._runState.stateManager instanceof StatelessVerkleStateManager | ||
// is this a code loaded from state using witnesses | ||
this._runState.env.chargeCodeAccesses === true | ||
) { | ||
const contract = this._runState.interpreter.getAddress() | ||
if ( | ||
!(this._runState.stateManager as StatelessVerkleStateManager).checkChunkWitnessPresent( | ||
contract, | ||
programCounter | ||
) | ||
!(await this._runState.stateManager.checkChunkWitnessPresent!(contract, programCounter)) | ||
) { | ||
@@ -424,3 +417,2 @@ throw Error(`Invalid witness with missing codeChunk for pc=${programCounter}`) | ||
stack: this._runState.stack.getStack(), | ||
returnStack: this._runState.returnStack.getStack(), | ||
depth: this._env.depth, | ||
@@ -454,3 +446,3 @@ address: this._env.address, | ||
if (!(name in this.opDebuggers)) { | ||
this.opDebuggers[name] = createDebugLogger(`evm:ops:${name}`) | ||
this.opDebuggers[name] = debugDefault(`evm:ops:${name}`) | ||
} | ||
@@ -506,5 +498,2 @@ this.opDebuggers[name](JSON.stringify(opTrace)) | ||
jumps[i] = 1 | ||
} else if (opcode === 0x5c) { | ||
// Define a BEGINSUB as a 2 in the valid jumps array | ||
jumps[i] = 2 | ||
} | ||
@@ -867,2 +856,3 @@ } | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}) | ||
@@ -892,2 +882,3 @@ | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}) | ||
@@ -917,2 +908,3 @@ | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}) | ||
@@ -943,2 +935,3 @@ | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}) | ||
@@ -970,2 +963,3 @@ | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}) | ||
@@ -983,3 +977,3 @@ | ||
this._runState.returnBytes = new Uint8Array(0) | ||
let createdAddresses: Set<string> | ||
let createdAddresses: Set<PrefixedHexString> | ||
if (this.common.isActivatedEIP(6780)) { | ||
@@ -1092,5 +1086,6 @@ createdAddresses = new Set(this._result.createdAddresses) | ||
blobVersionedHashes: this._env.blobVersionedHashes, | ||
accessWitness: this._env.accessWitness, | ||
}) | ||
let createdAddresses: Set<string> | ||
let createdAddresses: Set<PrefixedHexString> | ||
if (this.common.isActivatedEIP(6780)) { | ||
@@ -1097,0 +1092,0 @@ createdAddresses = new Set(this._result.createdAddresses) |
@@ -5,18 +5,15 @@ import { Hardfork } from '@ethereumjs/common' | ||
RIPEMD160_ADDRESS_STRING, | ||
bigIntToHex, | ||
bytesToHex, | ||
bytesToUnprefixedHex, | ||
stripHexPrefix, | ||
toBytes, | ||
unprefixedHexToBytes, | ||
} from '@ethereumjs/util' | ||
import debugDefault from 'debug' | ||
import { hexToBytes } from 'ethereum-cryptography/utils' | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common' | ||
import type { Account } from '@ethereumjs/util' | ||
import type { Account, PrefixedHexString } from '@ethereumjs/util' | ||
import type { Debugger } from 'debug' | ||
const { debug: createDebugLogger } = debugDefault | ||
type AddressString = string | ||
type HashString = string | ||
type SlotString = string | ||
@@ -52,3 +49,3 @@ type WarmSlots = Set<SlotString> | ||
public accessList?: Map<AddressString, Set<SlotString>> | ||
public preimages?: Map<HashString, Uint8Array> | ||
public preimages?: Map<PrefixedHexString, Uint8Array> | ||
@@ -61,3 +58,3 @@ constructor(stateManager: EVMStateManagerInterface, common: Common) { | ||
this._debug = createDebugLogger('statemanager:statemanager') | ||
this._debug = debugDefault('statemanager:statemanager') | ||
@@ -200,14 +197,7 @@ // TODO maybe call into this.clearJournal | ||
async cleanup(): Promise<void> { | ||
if (this.common.gteHardfork(Hardfork.SpuriousDragon) === true) { | ||
if (this.common.gteHardfork(Hardfork.SpuriousDragon)) { | ||
for (const addressHex of this.touched) { | ||
const address = new Address(toBytes('0x' + addressHex)) | ||
const address = new Address(hexToBytes(`0x${addressHex}`)) | ||
const account = await this.stateManager.getAccount(address) | ||
if (account === undefined || account.isEmpty()) { | ||
if (this.common.isActivatedEIP(2935)) { | ||
// The history storage address is exempt of state clearing by EIP-158 if the EIP is activated | ||
const addr = bigIntToHex(this.common.param('vm', 'historyStorageAddress')).slice(2) | ||
if (addressHex === addr) { | ||
continue | ||
} | ||
} | ||
await this.deleteAccount(address) | ||
@@ -214,0 +204,0 @@ if (this.DEBUG) { |
import { Address, BIGINT_0 } from '@ethereumjs/util' | ||
import type { PrecompileFunc } from './precompiles/index.js' | ||
import type { AccessWitness } from '@ethereumjs/statemanager' | ||
import type { AccessWitnessInterface } from '@ethereumjs/common' | ||
import type { PrefixedHexString } from '@ethereumjs/util' | ||
@@ -32,7 +33,7 @@ const defaults = { | ||
*/ | ||
selfdestruct?: Set<string> | ||
selfdestruct?: Set<PrefixedHexString> | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string> | ||
createdAddresses?: Set<PrefixedHexString> | ||
delegatecall?: boolean | ||
@@ -42,3 +43,3 @@ authcallOrigin?: Address | ||
blobVersionedHashes?: Uint8Array[] | ||
accessWitness?: AccessWitness | ||
accessWitness?: AccessWitnessInterface | ||
} | ||
@@ -63,7 +64,7 @@ | ||
*/ | ||
selfdestruct?: Set<string> | ||
selfdestruct?: Set<PrefixedHexString> | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string> | ||
createdAddresses?: Set<PrefixedHexString> | ||
delegatecall: boolean | ||
@@ -80,3 +81,3 @@ /** | ||
blobVersionedHashes?: Uint8Array[] | ||
accessWitness?: AccessWitness | ||
accessWitness?: AccessWitnessInterface | ||
@@ -83,0 +84,0 @@ constructor(opts: MessageOpts) { |
@@ -278,10 +278,2 @@ import { Hardfork } from '@ethereumjs/common' | ||
{ | ||
eip: 2315, | ||
opcodes: { | ||
0x5c: { name: 'BEGINSUB', isAsync: false, dynamicGas: false }, | ||
0x5d: { name: 'RETURNSUB', isAsync: false, dynamicGas: false }, | ||
0x5e: { name: 'JUMPSUB', isAsync: false, dynamicGas: false }, | ||
}, | ||
}, | ||
{ | ||
eip: 3198, | ||
@@ -288,0 +280,0 @@ opcodes: { |
@@ -23,3 +23,3 @@ import { BIGINT_0 } from '@ethereumjs/util' | ||
): bigint { | ||
if (common.isActivatedEIP(2929) === false) return BIGINT_0 | ||
if (!common.isActivatedEIP(2929)) return BIGINT_0 | ||
@@ -32,3 +32,4 @@ // Cold | ||
// selfdestruct beneficiary address reads are charged an *additional* cold access | ||
if (chargeGas) { | ||
// if verkle not activated | ||
if (chargeGas && !common.isActivatedEIP(6800)) { | ||
return common.param('gasPrices', 'coldaccountaccess') | ||
@@ -55,5 +56,6 @@ } | ||
isSstore: boolean, | ||
common: Common | ||
common: Common, | ||
chargeGas = true | ||
): bigint { | ||
if (common.isActivatedEIP(2929) === false) return BIGINT_0 | ||
if (!common.isActivatedEIP(2929)) return BIGINT_0 | ||
@@ -66,4 +68,6 @@ const address = runState.interpreter.getAddress().bytes | ||
runState.interpreter.journal.addWarmedStorage(address, key) | ||
return common.param('gasPrices', 'coldsload') | ||
} else if (!isSstore) { | ||
if (chargeGas && !common.isActivatedEIP(6800)) { | ||
return common.param('gasPrices', 'coldsload') | ||
} | ||
} else if (chargeGas && (!isSstore || common.isActivatedEIP(6800))) { | ||
return common.param('gasPrices', 'warmstorageread') | ||
@@ -91,3 +95,3 @@ } | ||
): bigint { | ||
if (common.isActivatedEIP(2929) === false) return defaultCost | ||
if (!common.isActivatedEIP(2929)) return defaultCost | ||
@@ -94,0 +98,0 @@ const address = runState.interpreter.getAddress().bytes |
import { | ||
Account, | ||
Address, | ||
BIGINT_0, | ||
BIGINT_1, | ||
BIGINT_128, | ||
BIGINT_160, | ||
@@ -23,4 +23,4 @@ BIGINT_2, | ||
TWO_POW256, | ||
bigIntToAddressBytes, | ||
bigIntToBytes, | ||
bigIntToHex, | ||
bytesToBigInt, | ||
@@ -30,2 +30,3 @@ bytesToHex, | ||
ecrecover, | ||
getVerkleTreeIndexesForStorageSlot, | ||
hexToBytes, | ||
@@ -47,3 +48,2 @@ publicToAddress, | ||
jumpIsValid, | ||
jumpSubIsValid, | ||
mod, | ||
@@ -58,3 +58,3 @@ toTwos, | ||
const EIP3074MAGIC = hexToBytes('0x03') | ||
const EIP3074MAGIC = hexToBytes('0x04') | ||
@@ -525,6 +525,17 @@ export interface SyncOpHandler { | ||
const addressBigInt = runState.stack.pop() | ||
const size = BigInt( | ||
(await runState.stateManager.getContractCode(new Address(addresstoBytes(addressBigInt)))) | ||
.length | ||
) | ||
let size | ||
if (typeof runState.stateManager.getContractCodeSize === 'function') { | ||
size = BigInt( | ||
await runState.stateManager.getContractCodeSize( | ||
new Address(addresstoBytes(addressBigInt)) | ||
) | ||
) | ||
} else { | ||
size = BigInt( | ||
(await runState.stateManager.getContractCode(new Address(addresstoBytes(addressBigInt)))) | ||
.length | ||
) | ||
} | ||
runState.stack.push(size) | ||
@@ -605,3 +616,3 @@ }, | ||
if (common.isActivatedEIP(2935)) { | ||
if (common.isActivatedEIP(7709)) { | ||
if (number >= runState.interpreter.getBlockNumber()) { | ||
@@ -613,5 +624,5 @@ runState.stack.push(BIGINT_0) | ||
const diff = runState.interpreter.getBlockNumber() - number | ||
const historyServeWindow = common.param('vm', 'historyServeWindow') | ||
// block lookups must be within the `historyServeWindow` | ||
if (diff > historyServeWindow || diff <= BIGINT_0) { | ||
// block lookups must be within the original window even if historyStorageAddress's | ||
// historyServeWindow is much greater than 256 | ||
if (diff > BIGINT_256 || diff <= BIGINT_0) { | ||
runState.stack.push(BIGINT_0) | ||
@@ -621,7 +632,18 @@ return | ||
const historyAddress = Address.fromString( | ||
bigIntToHex(common.param('vm', 'historyStorageAddress')) | ||
const historyAddress = new Address( | ||
bigIntToAddressBytes(common.param('vm', 'historyStorageAddress')) | ||
) | ||
const historyServeWindow = common.param('vm', 'historyServeWindow') | ||
const key = setLengthLeft(bigIntToBytes(number % historyServeWindow), 32) | ||
if (common.isActivatedEIP(6800)) { | ||
const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(number) | ||
// create witnesses and charge gas | ||
const statelessGas = runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
historyAddress, | ||
treeIndex, | ||
subIndex | ||
) | ||
runState.interpreter.useGas(statelessGas, `BLOCKHASH`) | ||
} | ||
const storage = await runState.stateManager.getContractStorage(historyAddress, key) | ||
@@ -851,77 +873,42 @@ | ||
[0x5b, function () {}], | ||
// 0x5c: BEGINSUB (EIP 2315) / TLOAD (EIP 1153) | ||
// 0x5c: TLOAD (EIP 1153) | ||
[ | ||
0x5c, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// BEGINSUB | ||
trap(ERROR.INVALID_BEGINSUB + ' at ' + describeLocation(runState)) | ||
} else if (common.isActivatedEIP(1153)) { | ||
// TLOAD | ||
const key = runState.stack.pop() | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32) | ||
const value = runState.interpreter.transientStorageLoad(keyBuf) | ||
const valueBN = value.length ? bytesToBigInt(value) : BIGINT_0 | ||
runState.stack.push(valueBN) | ||
} | ||
function (runState) { | ||
const key = runState.stack.pop() | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32) | ||
const value = runState.interpreter.transientStorageLoad(keyBuf) | ||
const valueBN = value.length ? bytesToBigInt(value) : BIGINT_0 | ||
runState.stack.push(valueBN) | ||
}, | ||
], | ||
// 0x5d: RETURNSUB (EIP 2315) / TSTORE (EIP 1153) | ||
// 0x5d: TSTORE (EIP 1153) | ||
[ | ||
0x5d, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// RETURNSUB | ||
if (runState.returnStack.length < 1) { | ||
trap(ERROR.INVALID_RETURNSUB) | ||
} | ||
function (runState) { | ||
// TSTORE | ||
if (runState.interpreter.isStatic()) { | ||
trap(ERROR.STATIC_STATE_CHANGE) | ||
} | ||
const [key, val] = runState.stack.popN(2) | ||
const dest = runState.returnStack.pop() | ||
runState.programCounter = Number(dest) | ||
} else if (common.isActivatedEIP(1153)) { | ||
// TSTORE | ||
if (runState.interpreter.isStatic()) { | ||
trap(ERROR.STATIC_STATE_CHANGE) | ||
} | ||
const [key, val] = runState.stack.popN(2) | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32) | ||
// NOTE: this should be the shortest representation | ||
let value | ||
if (val === BIGINT_0) { | ||
value = Uint8Array.from([]) | ||
} else { | ||
value = bigIntToBytes(val) | ||
} | ||
const keyBuf = setLengthLeft(bigIntToBytes(key), 32) | ||
// NOTE: this should be the shortest representation | ||
let value | ||
if (val === BIGINT_0) { | ||
value = Uint8Array.from([]) | ||
} else { | ||
value = bigIntToBytes(val) | ||
} | ||
runState.interpreter.transientStorageStore(keyBuf, value) | ||
} | ||
runState.interpreter.transientStorageStore(keyBuf, value) | ||
}, | ||
], | ||
// 0x5e: JUMPSUB (2315) / MCOPY (5656) | ||
// 0x5e: MCOPY (5656) | ||
[ | ||
0x5e, | ||
function (runState, common) { | ||
if (common.isActivatedEIP(2315)) { | ||
// JUMPSUB | ||
const dest = runState.stack.pop() | ||
if (dest > runState.interpreter.getCodeSize()) { | ||
trap(ERROR.INVALID_JUMPSUB + ' at ' + describeLocation(runState)) | ||
} | ||
const destNum = Number(dest) | ||
if (!jumpSubIsValid(runState, destNum)) { | ||
trap(ERROR.INVALID_JUMPSUB + ' at ' + describeLocation(runState)) | ||
} | ||
runState.returnStack.push(BigInt(runState.programCounter)) | ||
runState.programCounter = destNum + 1 | ||
} else if (common.isActivatedEIP(5656)) { | ||
// MCOPY | ||
const [dst, src, length] = runState.stack.popN(3) | ||
const data = runState.memory.read(Number(src), Number(length), true) | ||
runState.memory.write(Number(dst), Number(length), data) | ||
} | ||
function (runState) { | ||
const [dst, src, length] = runState.stack.popN(3) | ||
const data = runState.memory.read(Number(src), Number(length), true) | ||
runState.memory.write(Number(dst), Number(length), data) | ||
}, | ||
@@ -1157,23 +1144,44 @@ ], | ||
if (memLength > BIGINT_128) { | ||
memLength = BIGINT_128 | ||
if (memLength > BigInt(97)) { | ||
memLength = BigInt(97) | ||
} | ||
let mem = runState.memory.read(Number(memOffset), Number(memLength)) | ||
if (mem.length < 128) { | ||
mem = setLengthRight(mem, 128) | ||
if (mem.length < 97) { | ||
mem = setLengthRight(mem, 97) | ||
} | ||
const yParity = BigInt(mem[31]) | ||
const r = mem.subarray(32, 64) | ||
const s = mem.subarray(64, 96) | ||
const commit = mem.subarray(96, 128) | ||
const yParity = BigInt(mem[0]) | ||
const r = mem.subarray(1, 33) | ||
const s = mem.subarray(33, 65) | ||
const commit = mem.subarray(65, 97) | ||
if (bytesToBigInt(s) > SECP256K1_ORDER_DIV_2) { | ||
trap(ERROR.AUTH_INVALID_S) | ||
runState.stack.push(BIGINT_0) | ||
runState.auth = undefined | ||
return | ||
} | ||
if (yParity > BIGINT_1) { | ||
runState.stack.push(BIGINT_0) | ||
runState.auth = undefined | ||
return | ||
} | ||
const paddedInvokerAddress = setLengthLeft(runState.interpreter._env.address.bytes, 32) | ||
// we don't want strick check here on authority being in address range just last 20 bytes | ||
const expectedAddress = new Address(bigIntToAddressBytes(authority, false)) | ||
const account = (await runState.stateManager.getAccount(expectedAddress)) ?? new Account() | ||
if (account.isContract()) { | ||
// EXTCODESIZE > 0 | ||
runState.stack.push(BIGINT_0) | ||
runState.auth = undefined | ||
return | ||
} | ||
const accountNonce = account.nonce | ||
const invokedAddress = setLengthLeft(runState.interpreter._env.address.bytes, 32) | ||
const chainId = setLengthLeft(bigIntToBytes(runState.interpreter.getChainId()), 32) | ||
const message = concatBytes(EIP3074MAGIC, chainId, paddedInvokerAddress, commit) | ||
const nonce = setLengthLeft(bigIntToBytes(accountNonce), 32) | ||
const message = concatBytes(EIP3074MAGIC, chainId, nonce, invokedAddress, commit) | ||
@@ -1198,4 +1206,2 @@ const keccakFunction = runState.interpreter._evm.common.customCrypto.keccak256 ?? keccak256 | ||
const expectedAddress = new Address(setLengthLeft(bigIntToBytes(authority), 20)) | ||
if (!expectedAddress.equals(address)) { | ||
@@ -1216,12 +1222,4 @@ // expected address does not equal the recovered address, clear auth variable | ||
async function (runState) { | ||
const [ | ||
_currentGasLimit, | ||
addr, | ||
value, | ||
_valueExt, | ||
argsOffset, | ||
argsLength, | ||
retOffset, | ||
retLength, | ||
] = runState.stack.popN(8) | ||
const [_currentGasLimit, addr, value, argsOffset, argsLength, retOffset, retLength] = | ||
runState.stack.popN(7) | ||
@@ -1228,0 +1226,0 @@ const toAddress = new Address(addresstoBytes(addr)) |
import { Hardfork } from '@ethereumjs/common' | ||
import { CODE_SIZE_LEAF_KEY, getTreeIndexesForStorageSlot } from '@ethereumjs/statemanager' | ||
import { | ||
Account, | ||
Address, | ||
@@ -10,3 +10,8 @@ BIGINT_0, | ||
BIGINT_32, | ||
VERKLE_BALANCE_LEAF_KEY, | ||
VERKLE_CODE_HASH_LEAF_KEY, | ||
VERKLE_CODE_SIZE_LEAF_KEY, | ||
VERKLE_VERSION_LEAF_KEY, | ||
bigIntToBytes, | ||
getVerkleTreeIndexesForStorageSlot, | ||
setLengthLeft, | ||
@@ -86,6 +91,20 @@ } from '@ethereumjs/util' | ||
async function (runState, gas, common): Promise<bigint> { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0] | ||
gas += accessAddressEIP2929(runState, addresstoBytes(address), common) | ||
const address = addresstoBytes(runState.stack.peek()[0]) | ||
let charge2929Gas = true | ||
if (common.isActivatedEIP(6800)) { | ||
const balanceAddress = new Address(address) | ||
const coldAccessGas = runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
balanceAddress, | ||
0, | ||
VERKLE_BALANCE_LEAF_KEY | ||
) | ||
gas += coldAccessGas | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, address, common, charge2929Gas) | ||
} | ||
return gas | ||
@@ -117,3 +136,3 @@ }, | ||
if (common.isActivatedEIP(6800)) { | ||
if (common.isActivatedEIP(6800) && runState.env.chargeCodeAccesses === true) { | ||
const contract = runState.interpreter.getAddress() | ||
@@ -140,16 +159,31 @@ let codeEnd = _codeOffset + dataLength | ||
async function (runState, gas, common): Promise<bigint> { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0] | ||
gas += accessAddressEIP2929(runState, addresstoBytes(address), common) | ||
} | ||
const addressBytes = addresstoBytes(runState.stack.peek()[0]) | ||
const address = new Address(addressBytes) | ||
if (common.isActivatedEIP(6800) === true) { | ||
const address = new Address(addresstoBytes(runState.stack.peek()[0])) | ||
gas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
let charge2929Gas = true | ||
if ( | ||
common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(address) === undefined | ||
) { | ||
let coldAccessGas = BIGINT_0 | ||
coldAccessGas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
address, | ||
0, | ||
CODE_SIZE_LEAF_KEY | ||
VERKLE_VERSION_LEAF_KEY | ||
) | ||
coldAccessGas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
address, | ||
0, | ||
VERKLE_CODE_SIZE_LEAF_KEY | ||
) | ||
gas += coldAccessGas | ||
// if cold access gas has been charged 2929 gas shouldn't be charged | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addressBytes, common, charge2929Gas) | ||
} | ||
return gas | ||
@@ -162,10 +196,34 @@ }, | ||
async function (runState, gas, common): Promise<bigint> { | ||
const [address, memOffset, _codeOffset, dataLength] = runState.stack.peek(4) | ||
const [addressBigInt, memOffset, _codeOffset, dataLength] = runState.stack.peek(4) | ||
const addressBytes = addresstoBytes(addressBigInt) | ||
const address = new Address(addressBytes) | ||
gas += subMemUsage(runState, memOffset, dataLength, common) | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(address), common) | ||
let charge2929Gas = true | ||
if ( | ||
common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(address) === undefined | ||
) { | ||
let coldAccessGas = BIGINT_0 | ||
coldAccessGas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
address, | ||
0, | ||
VERKLE_VERSION_LEAF_KEY | ||
) | ||
coldAccessGas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
address, | ||
0, | ||
VERKLE_CODE_SIZE_LEAF_KEY | ||
) | ||
gas += coldAccessGas | ||
// if cold access gas has been charged 2929 gas shouldn't be charged | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addressBytes, common, charge2929Gas) | ||
} | ||
if (dataLength !== BIGINT_0) { | ||
@@ -175,5 +233,4 @@ gas += common.param('gasPrices', 'copy') * divCeil(dataLength, BIGINT_32) | ||
if (common.isActivatedEIP(6800)) { | ||
const contract = new Address(addresstoBytes(address)) | ||
let codeEnd = _codeOffset + dataLength | ||
const codeSize = BigInt((await runState.stateManager.getContractCode(contract)).length) | ||
const codeSize = BigInt((await runState.stateManager.getContractCode(address)).length) | ||
if (codeEnd > codeSize) { | ||
@@ -184,3 +241,3 @@ codeEnd = codeSize | ||
gas += runState.env.accessWitness!.touchCodeChunksRangeOnReadAndChargeGas( | ||
contract, | ||
address, | ||
Number(_codeOffset), | ||
@@ -216,6 +273,23 @@ Number(codeEnd) | ||
async function (runState, gas, common): Promise<bigint> { | ||
if (common.isActivatedEIP(2929) === true) { | ||
const address = runState.stack.peek()[0] | ||
gas += accessAddressEIP2929(runState, addresstoBytes(address), common) | ||
const address = addresstoBytes(runState.stack.peek()[0]) | ||
let charge2929Gas = true | ||
if (common.isActivatedEIP(6800)) { | ||
const codeAddress = new Address(address) | ||
let coldAccessGas = BIGINT_0 | ||
coldAccessGas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
codeAddress, | ||
0, | ||
VERKLE_CODE_HASH_LEAF_KEY | ||
) | ||
gas += coldAccessGas | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, address, common, charge2929Gas) | ||
} | ||
return gas | ||
@@ -258,10 +332,7 @@ }, | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessStorageEIP2929(runState, keyBuf, false, common) | ||
} | ||
if (common.isActivatedEIP(6800) === true) { | ||
let charge2929Gas = true | ||
if (common.isActivatedEIP(6800)) { | ||
const address = runState.interpreter.getAddress() | ||
const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(key) | ||
gas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(key) | ||
const coldAccessGas = runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
address, | ||
@@ -271,3 +342,11 @@ treeIndex, | ||
) | ||
gas += coldAccessGas | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessStorageEIP2929(runState, keyBuf, false, common, charge2929Gas) | ||
} | ||
return gas | ||
@@ -309,10 +388,12 @@ }, | ||
} else if (common.gteHardfork(Hardfork.Istanbul)) { | ||
gas += updateSstoreGasEIP2200( | ||
runState, | ||
currentStorage, | ||
originalStorage, | ||
setLengthLeftStorage(value), | ||
keyBytes, | ||
common | ||
) | ||
if (!common.isActivatedEIP(6800)) { | ||
gas += updateSstoreGasEIP2200( | ||
runState, | ||
currentStorage, | ||
originalStorage, | ||
setLengthLeftStorage(value), | ||
keyBytes, | ||
common | ||
) | ||
} | ||
} else { | ||
@@ -322,13 +403,7 @@ gas += updateSstoreGas(runState, currentStorage, setLengthLeftStorage(value), common) | ||
if (common.isActivatedEIP(2929) === true) { | ||
// We have to do this after the Istanbul (EIP2200) checks. | ||
// Otherwise, we might run out of gas, due to "sentry check" of 2300 gas, | ||
// if we deduct extra gas first. | ||
gas += accessStorageEIP2929(runState, keyBytes, true, common) | ||
} | ||
if (common.isActivatedEIP(6800) === true) { | ||
let charge2929Gas = true | ||
if (common.isActivatedEIP(6800)) { | ||
const contract = runState.interpreter.getAddress() | ||
const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(key) | ||
gas += runState.env.accessWitness!.touchAddressOnWriteAndComputeGas( | ||
const { treeIndex, subIndex } = getVerkleTreeIndexesForStorageSlot(key) | ||
const coldAccessGas = runState.env.accessWitness!.touchAddressOnWriteAndComputeGas( | ||
contract, | ||
@@ -338,3 +413,14 @@ treeIndex, | ||
) | ||
gas += coldAccessGas | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
// We have to do this after the Istanbul (EIP2200) checks. | ||
// Otherwise, we might run out of gas, due to "sentry check" of 2300 gas, | ||
// if we deduct extra gas first. | ||
gas += accessStorageEIP2929(runState, keyBytes, true, common, charge2929Gas) | ||
} | ||
return gas | ||
@@ -387,3 +473,3 @@ }, | ||
if (common.isActivatedEIP(2929) === true) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929( | ||
@@ -397,3 +483,3 @@ runState, | ||
if (common.isActivatedEIP(3860) === true) { | ||
if (common.isActivatedEIP(3860)) { | ||
gas += ((length + BIGINT_31) / BIGINT_32) * common.param('gasPrices', 'initCodeWordCost') | ||
@@ -424,7 +510,27 @@ } | ||
gas += subMemUsage(runState, outOffset, outLength, common) | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, toAddress.bytes, common) | ||
let charge2929Gas = true | ||
if ( | ||
common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined | ||
) { | ||
// TODO: add check if toAddress is not a precompile | ||
const coldAccessGas = runState.env.accessWitness!.touchAndChargeMessageCall(toAddress) | ||
if (value !== BIGINT_0) { | ||
const contractAddress = runState.interpreter.getAddress() | ||
gas += runState.env.accessWitness!.touchAndChargeValueTransfer( | ||
contractAddress, | ||
toAddress | ||
) | ||
} | ||
gas += coldAccessGas | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (value !== BIGINT_0) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, toAddress.bytes, common, charge2929Gas) | ||
} | ||
if (value !== BIGINT_0 && !common.isActivatedEIP(6800)) { | ||
gas += common.param('gasPrices', 'callValueTransfer') | ||
@@ -452,14 +558,2 @@ } | ||
if (common.isActivatedEIP(6800)) { | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness!.touchAndChargeMessageCall(toAddress) | ||
if (value !== BIGINT_0) { | ||
const contractAddress = runState.interpreter.getAddress() | ||
gas += runState.env.accessWitness!.touchAndChargeValueTransfer( | ||
contractAddress, | ||
toAddress | ||
) | ||
} | ||
} | ||
const gasLimit = maxCallGas( | ||
@@ -491,2 +585,3 @@ currentGasLimit, | ||
runState.stack.peek(7) | ||
const toAddress = new Address(addresstoBytes(toAddr)) | ||
@@ -496,6 +591,17 @@ gas += subMemUsage(runState, inOffset, inLength, common) | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common) | ||
let charge2929Gas = true | ||
if ( | ||
common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined | ||
) { | ||
const coldAccessGas = runState.env.accessWitness!.touchAndChargeMessageCall(toAddress) | ||
gas += coldAccessGas | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common, charge2929Gas) | ||
} | ||
if (value !== BIGINT_0) { | ||
@@ -505,8 +611,2 @@ gas += common.param('gasPrices', 'callValueTransfer') | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new Address(addresstoBytes(toAddr)) | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness!.touchAndChargeMessageCall(toAddress) | ||
} | ||
const gasLimit = maxCallGas( | ||
@@ -543,2 +643,3 @@ currentGasLimit, | ||
runState.stack.peek(6) | ||
const toAddress = new Address(addresstoBytes(toAddr)) | ||
@@ -548,10 +649,16 @@ gas += subMemUsage(runState, inOffset, inLength, common) | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common) | ||
let charge2929Gas = true | ||
if ( | ||
common.isActivatedEIP(6800) && | ||
runState.interpreter._evm.getPrecompile(toAddress) === undefined | ||
) { | ||
// TODO: add check if toAddress is not a precompile | ||
const coldAccessGas = runState.env.accessWitness!.touchAndChargeMessageCall(toAddress) | ||
gas += coldAccessGas | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new Address(addresstoBytes(toAddr)) | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness!.touchAndChargeMessageCall(toAddress) | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common, charge2929Gas) | ||
} | ||
@@ -587,3 +694,3 @@ | ||
if (common.isActivatedEIP(2929) === true) { | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929( | ||
@@ -597,3 +704,3 @@ runState, | ||
if (common.isActivatedEIP(3860) === true) { | ||
if (common.isActivatedEIP(3860)) { | ||
gas += ((length + BIGINT_31) / BIGINT_32) * common.param('gasPrices', 'initCodeWordCost') | ||
@@ -613,3 +720,6 @@ } | ||
async function (runState, gas, common): Promise<bigint> { | ||
const [_address, memOffset, memLength] = runState.stack.peek(3) | ||
const [address, memOffset, memLength] = runState.stack.peek(3) | ||
// Note: 2929 is always active if AUTH can be reached, | ||
// since it needs London as minimum hardfork | ||
gas += accessAddressEIP2929(runState, bigIntToBytes(address), common) | ||
gas += subMemUsage(runState, memOffset, memLength, common) | ||
@@ -627,17 +737,5 @@ return gas | ||
const [ | ||
currentGasLimit, | ||
addr, | ||
value, | ||
valueExt, | ||
argsOffset, | ||
argsLength, | ||
retOffset, | ||
retLength, | ||
] = runState.stack.peek(8) | ||
const [currentGasLimit, addr, value, argsOffset, argsLength, retOffset, retLength] = | ||
runState.stack.peek(7) | ||
if (valueExt !== BIGINT_0) { | ||
trap(ERROR.AUTHCALL_NONZERO_VALUEEXT) | ||
} | ||
const toAddress = new Address(addresstoBytes(addr)) | ||
@@ -674,2 +772,17 @@ | ||
runState.messageGasLimit = gasLimit | ||
if (value > BIGINT_0) { | ||
const account = (await runState.stateManager.getAccount(runState.auth!)) ?? new Account() | ||
if (account.balance < value) { | ||
trap(ERROR.OUT_OF_GAS) | ||
} | ||
account.balance -= value | ||
const toAddr = new Address(addresstoBytes(addr)) | ||
const target = (await runState.stateManager.getAccount(toAddr)) ?? new Account() | ||
target.balance += value | ||
await runState.stateManager.putAccount(toAddr, target) | ||
} | ||
return gas | ||
@@ -688,12 +801,16 @@ }, | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common) | ||
} | ||
let charge2929Gas = true | ||
if (common.isActivatedEIP(6800)) { | ||
const toAddress = new Address(addresstoBytes(toAddr)) | ||
// TODO: add check if toAddress is not a precompile | ||
gas += runState.env.accessWitness!.touchAndChargeMessageCall(toAddress) | ||
const coldAccessGas = runState.env.accessWitness!.touchAndChargeMessageCall(toAddress) | ||
gas += coldAccessGas | ||
charge2929Gas = coldAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929(runState, addresstoBytes(toAddr), common, charge2929Gas) | ||
} | ||
const gasLimit = maxCallGas( | ||
@@ -729,8 +846,9 @@ currentGasLimit, | ||
const selfdestructToAddress = new Address(addresstoBytes(selfdestructToaddressBigInt)) | ||
const contractAddress = runState.interpreter.getAddress() | ||
let deductGas = false | ||
const balance = await runState.interpreter.getExternalBalance(contractAddress) | ||
if (common.gteHardfork(Hardfork.SpuriousDragon)) { | ||
// EIP-161: State Trie Clearing | ||
const balance = await runState.interpreter.getExternalBalance( | ||
runState.interpreter.getAddress() | ||
) | ||
if (balance > BIGINT_0) { | ||
@@ -755,5 +873,60 @@ // This technically checks if account is empty or non-existent | ||
if (common.isActivatedEIP(2929) === true) { | ||
gas += accessAddressEIP2929(runState, selfdestructToAddress.bytes, common, true, true) | ||
let selfDestructToCharge2929Gas = true | ||
if (common.isActivatedEIP(6800)) { | ||
// read accesses for version and code size | ||
if (runState.interpreter._evm.getPrecompile(contractAddress) === undefined) { | ||
gas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
contractAddress, | ||
0, | ||
VERKLE_VERSION_LEAF_KEY | ||
) | ||
gas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
contractAddress, | ||
0, | ||
VERKLE_CODE_SIZE_LEAF_KEY | ||
) | ||
} | ||
gas += runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
contractAddress, | ||
0, | ||
VERKLE_BALANCE_LEAF_KEY | ||
) | ||
if (balance > BIGINT_0) { | ||
gas += runState.env.accessWitness!.touchAddressOnWriteAndComputeGas( | ||
contractAddress, | ||
0, | ||
VERKLE_BALANCE_LEAF_KEY | ||
) | ||
} | ||
let selfDestructToColdAccessGas = | ||
runState.env.accessWitness!.touchAddressOnReadAndComputeGas( | ||
selfdestructToAddress, | ||
0, | ||
VERKLE_BALANCE_LEAF_KEY | ||
) | ||
if (balance > BIGINT_0) { | ||
selfDestructToColdAccessGas += | ||
runState.env.accessWitness!.touchAddressOnWriteAndComputeGas( | ||
selfdestructToAddress, | ||
0, | ||
VERKLE_BALANCE_LEAF_KEY | ||
) | ||
} | ||
gas += selfDestructToColdAccessGas | ||
selfDestructToCharge2929Gas = selfDestructToColdAccessGas === BIGINT_0 | ||
} | ||
if (common.isActivatedEIP(2929)) { | ||
gas += accessAddressEIP2929( | ||
runState, | ||
selfdestructToAddress.bytes, | ||
common, | ||
selfDestructToCharge2929Gas, | ||
true | ||
) | ||
} | ||
return gas | ||
@@ -760,0 +933,0 @@ }, |
@@ -140,9 +140,2 @@ import { Hardfork } from '@ethereumjs/common' | ||
/** | ||
* Checks if a jumpsub is valid given a destination (defined as a 2 in the validJumps array) | ||
*/ | ||
export function jumpSubIsValid(runState: RunState, dest: number): boolean { | ||
return runState.validJumps[dest] === 2 | ||
} | ||
/** | ||
* Returns an overflow-safe slice of an array. It right-pads | ||
@@ -149,0 +142,0 @@ * the data with zeros to `length`. |
@@ -14,2 +14,12 @@ import { Hardfork } from '@ethereumjs/common' | ||
import { precompile0a } from './0a-kzg-point-evaluation.js' | ||
import { precompile0b } from './0b-bls12-g1add.js' | ||
import { precompile0c } from './0c-bls12-g1mul.js' | ||
import { precompile0d } from './0d-bls12-g1msm.js' | ||
import { precompile0e } from './0e-bls12-g2add.js' | ||
import { precompile0f } from './0f-bls12-g2mul.js' | ||
import { precompile10 } from './10-bls12-g2msm.js' | ||
import { precompile11 } from './11-bls12-pairing.js' | ||
import { precompile12 } from './12-bls12-map-fp-to-g1.js' | ||
import { precompile13 } from './13-bls12-map-fp2-to-g2.js' | ||
import { MCLBLS, NobleBLS } from './bls12_381/index.js' | ||
@@ -142,2 +152,83 @@ import type { PrecompileFunc, PrecompileInput } from './types.js' | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000b', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0b, | ||
name: 'BLS12_G1ADD', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000c', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0c, | ||
name: 'BLS12_G1MUL', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000d', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0d, | ||
name: 'BLS12_G1MSM', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000e', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0e, | ||
name: 'BLS12_G2ADD', | ||
}, | ||
{ | ||
address: '000000000000000000000000000000000000000f', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile0f, | ||
name: 'BLS12_G2MUL', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000010', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile10, | ||
name: 'BLS12_G2MSM', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000011', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile11, | ||
name: 'BLS12_PAIRING', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000012', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile12, | ||
name: 'BLS12_MAP_FP_TO_G1', | ||
}, | ||
{ | ||
address: '0000000000000000000000000000000000000013', | ||
check: { | ||
type: PrecompileAvailabilityCheck.EIP, | ||
param: 2537, | ||
}, | ||
precompile: precompile13, | ||
name: 'BLS12_MAP_FP2_TO_G2', | ||
}, | ||
] | ||
@@ -156,2 +247,11 @@ | ||
'000000000000000000000000000000000000000a': precompile0a, | ||
'000000000000000000000000000000000000000b': precompile0b, | ||
'000000000000000000000000000000000000000c': precompile0c, | ||
'000000000000000000000000000000000000000d': precompile0d, | ||
'000000000000000000000000000000000000000e': precompile0e, | ||
'000000000000000000000000000000000000000f': precompile0f, | ||
'0000000000000000000000000000000000000010': precompile10, | ||
'0000000000000000000000000000000000000011': precompile11, | ||
'0000000000000000000000000000000000000012': precompile12, | ||
'0000000000000000000000000000000000000013': precompile13, | ||
} | ||
@@ -211,2 +311,4 @@ | ||
getPrecompileName, | ||
MCLBLS, | ||
NobleBLS, | ||
precompileEntries, | ||
@@ -213,0 +315,0 @@ precompiles, |
@@ -10,5 +10,4 @@ import { zeros } from '@ethereumjs/util' | ||
import type { PrecompileFunc } from './precompiles/types.js' | ||
import type { Common, EVMStateManagerInterface } from '@ethereumjs/common' | ||
import type { AccessWitness } from '@ethereumjs/statemanager' | ||
import type { Account, Address, AsyncEventEmitter } from '@ethereumjs/util' | ||
import type { AccessWitnessInterface, Common, EVMStateManagerInterface } from '@ethereumjs/common' | ||
import type { Account, Address, AsyncEventEmitter, PrefixedHexString } from '@ethereumjs/util' | ||
@@ -76,3 +75,3 @@ export type DeleteOpcode = { | ||
*/ | ||
selfdestruct?: Set<string> | ||
selfdestruct?: Set<PrefixedHexString> | ||
/** | ||
@@ -110,3 +109,3 @@ * The address of the account that is executing this code (`address(this)`). Defaults to the zero address. | ||
*/ | ||
createdAddresses?: Set<string> | ||
createdAddresses?: Set<PrefixedHexString> | ||
/** | ||
@@ -130,3 +129,3 @@ * Skip balance checks if true. If caller balance is less than message value, | ||
accessWitness?: AccessWitness | ||
accessWitness?: AccessWitnessInterface | ||
} | ||
@@ -157,3 +156,3 @@ | ||
accessList?: Map<string, Set<string>> | ||
preimages?: Map<string, Uint8Array> | ||
preimages?: Map<PrefixedHexString, Uint8Array> | ||
addAlwaysWarmAddress(address: string, addToAccessList?: boolean): void | ||
@@ -187,7 +186,6 @@ addAlwaysWarmSlot(address: string, slot: string, addToAccessList?: boolean): void | ||
* - [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - Fee market change for ETH 1.0 chain | ||
* - [EIP-2315](https://eips.ethereum.org/EIPS/eip-2315) - Simple subroutines for the EVM (`outdated`) | ||
* - [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) - BLS precompiles (removed in v4.0.0, see latest v3 release) | ||
* - [EIP-2565](https://eips.ethereum.org/EIPS/eip-2565) - ModExp gas cost | ||
* - [EIP-2718](https://eips.ethereum.org/EIPS/eip-2565) - Transaction Types | ||
* - [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) - Save historical block hashes in state (`experimental`) | ||
* - [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) - Serve historical block hashes from state (Prague) | ||
* - [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) - gas cost increases for state access opcodes | ||
@@ -211,7 +209,13 @@ * - [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) - Optional access list tx type | ||
* - [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) - Shard Blob Transactions (Cancun) | ||
* - [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) - EOA code transactions (Prague) (`outdated`) | ||
* - [EIP-7709](https://eips.ethereum.org/EIPS/eip-7709) - Read BLOCKHASH from storage and update cost (Osaka) | ||
* - [EIP-4895](https://eips.ethereum.org/EIPS/eip-4895) - Beacon chain push withdrawals as operations (Shanghai) | ||
* - [EIP-5133](https://eips.ethereum.org/EIPS/eip-5133) - Delaying Difficulty Bomb to mid-September 2022 (Gray Glacier) | ||
* - [EIP-5656](https://eips.ethereum.org/EIPS/eip-5656) - MCOPY - Memory copying instruction (Cancun) | ||
* - [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110) - Supply validator deposits on chain (Prague) | ||
* - [EIP-6780](https://eips.ethereum.org/EIPS/eip-6780) - SELFDESTRUCT only in same transaction (Cancun) | ||
* - [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) - Execution layer triggerable withdrawals (Prague) | ||
* - [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) - Execution layer triggerable validator consolidations (Prague) | ||
* - [EIP-7516](https://eips.ethereum.org/EIPS/eip-7516) - BLOBBASEFEE opcode (Cancun) | ||
* - [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) - General purpose execution layer requests (Prague) | ||
* | ||
@@ -268,2 +272,24 @@ * *Annotations:* | ||
/** | ||
* For the EIP-2537 BLS Precompiles, the native JS `@noble/curves` | ||
* https://github.com/paulmillr/noble-curves BLS12-381 curve implementation | ||
* is used (see `noble.ts` file in the `precompiles` folder). | ||
* | ||
* To use an alternative implementation this option can be used by passing | ||
* in a wrapper implementation integrating the desired library and adhering | ||
* to the `EVMBLSInterface` specification. | ||
* | ||
* An interface for the MCL WASM implementation https://github.com/herumi/mcl-wasm | ||
* is shipped with this library which can be used as follows (with `mcl-wasm` being | ||
* explicitly added to the set of dependencies): | ||
* | ||
* ```ts | ||
* import * as mcl from 'mcl-wasm' | ||
* | ||
* await mcl.init(mcl.BLS12_381) | ||
* const evm = await EVM.create({ bls: new MCLBLS(mcl) }) | ||
* ``` | ||
*/ | ||
bls?: EVMBLSInterface | ||
/* | ||
@@ -327,7 +353,7 @@ * The StateManager which is used to update the trie | ||
*/ | ||
selfdestruct?: Set<string> | ||
selfdestruct?: Set<PrefixedHexString> | ||
/** | ||
* Map of addresses which were created (used in EIP 6780) | ||
*/ | ||
createdAddresses?: Set<string> | ||
createdAddresses?: Set<PrefixedHexString> | ||
/** | ||
@@ -343,2 +369,15 @@ * The gas refund counter | ||
export type EVMBLSInterface = { | ||
init?(): void | ||
addG1(input: Uint8Array): Uint8Array | ||
mulG1(input: Uint8Array): Uint8Array | ||
addG2(input: Uint8Array): Uint8Array | ||
mulG2(input: Uint8Array): Uint8Array | ||
mapFPtoG1(input: Uint8Array): Uint8Array | ||
mapFP2toG2(input: Uint8Array): Uint8Array | ||
msmG1(input: Uint8Array): Uint8Array | ||
msmG2(input: Uint8Array): Uint8Array | ||
pairingCheck(input: Uint8Array): Uint8Array | ||
} | ||
/** | ||
@@ -399,5 +438,5 @@ * Log that the contract emits. | ||
export interface bn128 { | ||
ec_pairing: (input_str: string) => string | ||
ec_add: (input_str: string) => string | ||
ec_mul: (input_hex: string) => string | ||
ec_pairing: (input_str: string) => PrefixedHexString | ||
ec_add: (input_str: string) => PrefixedHexString | ||
ec_mul: (input_hex: string) => PrefixedHexString | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1785011
421
27380
502
9
15
+ Added@noble/curves@^1.4.2
+ Added@noble/curves@1.6.0(transitive)
+ Added@noble/hashes@1.5.0(transitive)
Updated@ethereumjs/common@^4.4.0
Updated@ethereumjs/tx@^5.4.0
Updated@ethereumjs/util@^9.1.0
Updatedethereum-cryptography@^2.2.1