diff --git a/packages/vm/src/evm/evm.ts b/packages/vm/src/evm/evm.ts index c0362e44a4..afe0341a8c 100644 --- a/packages/vm/src/evm/evm.ts +++ b/packages/vm/src/evm/evm.ts @@ -143,10 +143,6 @@ export interface EVMResult { * Contains the results from running the code, if any, as described in {@link runCode} */ execResult: ExecResult - /** - * Total amount of gas to be refunded from all nested calls. - */ - gasRefund?: bigint } /** @@ -178,6 +174,10 @@ export interface ExecResult { * A map from the accounts that have self-destructed to the addresses to send their funds to */ selfdestruct?: { [k: string]: Buffer } + /** + * The gas refund counter + */ + gasRefund?: bigint } export interface NewContractEvent { @@ -238,7 +238,6 @@ export default class EVM extends AsyncEventEmitter { /** * Amount of gas to refund from deleting storage values */ - _refund: bigint _transientStorage: TransientStorage _common: Common @@ -306,7 +305,6 @@ export default class EVM extends AsyncEventEmitter { this.eei = opts.eei - this._refund = BigInt(0) this._transientStorage = new TransientStorage() if (opts.common) { @@ -719,6 +717,7 @@ export default class EVM extends AsyncEventEmitter { exceptionError: interpreterRes.exceptionError, gas: interpreter._gasLeft, gasUsed, + gasRefund: interpreterRes.runState!.gasRefund, returnValue: result.returnValue ? result.returnValue : Buffer.alloc(0), } } @@ -769,8 +768,6 @@ export default class EVM extends AsyncEventEmitter { this.eei.state.addWarmedAddress((await this._generateAddress(message)).buf) } - const oldRefund = this._refund - await this.eei.state.checkpoint() this._transientStorage.checkpoint() if (this.DEBUG) { @@ -803,17 +800,16 @@ export default class EVM extends AsyncEventEmitter { debug( `Received message execResult: [ gasUsed=${gasUsed} exceptionError=${ exceptionError ? `'${exceptionError.error}'` : 'none' - } returnValue=0x${short(returnValue)} gasRefund=${result.gasRefund ?? 0} ]` + } returnValue=0x${short(returnValue)} gasRefund=${result.execResult.gasRefund ?? 0} ]` ) } const err = result.execResult.exceptionError // This clause captures any error which happened during execution - // If that is the case, then set the _refund tracker to the old refund value + // If that is the case, then all refunds are forfeited if (err) { - this._refund = oldRefund result.execResult.selfdestruct = {} + result.execResult.gasRefund = BigInt(0) } - result.gasRefund = this._refund if (err) { if (this._common.gteHardfork(Hardfork.Homestead) || err.error != ERROR.CODESTORE_OUT_OF_GAS) { result.execResult.logs = [] diff --git a/packages/vm/src/evm/interpreter.ts b/packages/vm/src/evm/interpreter.ts index 1c1fde8ca8..81996a5d72 100644 --- a/packages/vm/src/evm/interpreter.ts +++ b/packages/vm/src/evm/interpreter.ts @@ -71,6 +71,7 @@ export interface RunState { env: Env messageGasLimit?: bigint // Cache value from `gas.ts` to save gas limit for a message call interpreter: Interpreter + gasRefund: bigint // Tracks the current refund } export interface InterpreterResult { @@ -147,6 +148,7 @@ export default class Interpreter { env: env, shouldDoJumpAnalysis: true, interpreter: this, + gasRefund: BigInt(0), } this._env = env this._lastReturned = Buffer.alloc(0) @@ -302,7 +304,7 @@ export default class Interpreter { const eventObj: InterpreterStep = { pc: this._runState.programCounter, gasLeft, - gasRefund: this._evm._refund, + gasRefund: this._runState.gasRefund, opcode: { name: opcode.fullName, fee: opcode.fee, @@ -420,9 +422,11 @@ export default class Interpreter { */ refundGas(amount: bigint, context?: string): void { if (this._evm.DEBUG) { - debugGas(`${context ? context + ': ' : ''}refund ${amount} gas (-> ${this._evm._refund})`) + debugGas( + `${context ? context + ': ' : ''}refund ${amount} gas (-> ${this._runState.gasRefund})` + ) } - this._evm._refund += amount + this._runState.gasRefund += amount } /** @@ -432,11 +436,13 @@ export default class Interpreter { */ subRefund(amount: bigint, context?: string): void { if (this._evm.DEBUG) { - debugGas(`${context ? context + ': ' : ''}sub gas refund ${amount} (-> ${this._evm._refund})`) + debugGas( + `${context ? context + ': ' : ''}sub gas refund ${amount} (-> ${this._runState.gasRefund})` + ) } - this._evm._refund -= amount - if (this._evm._refund < BigInt(0)) { - this._evm._refund = BigInt(0) + this._runState.gasRefund -= amount + if (this._runState.gasRefund < BigInt(0)) { + this._runState.gasRefund = BigInt(0) trap(ERROR.REFUND_EXHAUSTED) } } @@ -835,6 +841,7 @@ export default class Interpreter { // update stateRoot on current contract const account = await this._eei.state.getAccount(this._env.address) this._env.contract = account + this._runState.gasRefund += results.execResult.gasRefund ?? BigInt(0) } return this._getReturnCode(results) diff --git a/packages/vm/src/evm/types.ts b/packages/vm/src/evm/types.ts index ee7660d224..008eec16d3 100644 --- a/packages/vm/src/evm/types.ts +++ b/packages/vm/src/evm/types.ts @@ -92,6 +92,10 @@ export interface RunCallOpts { * If the call is a DELEGATECALL. Defaults to false. */ delegatecall?: boolean + /** + * Refund counter. Defaults to `0` + */ + gasRefund?: bigint /** * Optionally pass in an already-built message. */ diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index ed70b13e29..adea04a61f 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -443,7 +443,7 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { } // Process any gas refund - let gasRefund = results.gasRefund ?? BigInt(0) + let gasRefund = results.execResult.gasRefund ?? BigInt(0) const maxRefundQuotient = this._common.param('gasConfig', 'maxRefundQuotient') if (gasRefund !== BigInt(0)) { const maxRefund = results.gasUsed / maxRefundQuotient @@ -508,7 +508,7 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { } } } - this.evm._refund = BigInt(0) + await state.cleanupTouchedAccounts() state.clearOriginalStorageCache() if (this._common.isActivatedEIP(1153)) this.evm._transientStorage.clear()