Skip to content

Commit

Permalink
evm: verkle testing extraction (#3817)
Browse files Browse the repository at this point in the history
* evm: extract some verkle related improvements to a separate pr

* evm: extract more stuff

* Update packages/statemanager/src/statefulVerkleStateManager.ts

Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>

* chore: adjust evm params for 2935

* Fix step logging so dynamic gas doesn't include state accesses

* Revert unnecessary change

---------

Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>
  • Loading branch information
gabrocheleau and acolytec3 authored Dec 20, 2024
1 parent 4da1f03 commit cd4afd0
Show file tree
Hide file tree
Showing 20 changed files with 315 additions and 55 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions packages/common/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ export class Common {
for (const hfChanges of this.HARDFORK_CHANGES) {
// EIP-referencing HF config (e.g. for berlin)
if ('eips' in hfChanges[1]) {
const hfEIPs = hfChanges[1]['eips']
for (const eip of hfEIPs!) {
const hfEIPs = hfChanges[1]['eips'] ?? []
for (const eip of hfEIPs) {
this._mergeWithParamsCache(this._params[eip] ?? {})
}
}
Expand All @@ -337,7 +337,7 @@ export class Common {

for (const [name, hf] of this.HARDFORK_CHANGES) {
if (this.gteHardfork(name) && 'eips' in hf) {
this._activatedEIPsCache = this._activatedEIPsCache.concat(hf['eips'] as number[])
this._activatedEIPsCache = this._activatedEIPsCache.concat(hf.eips ?? [])
}
}
this._activatedEIPsCache = this._activatedEIPsCache.concat(this._eips)
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/hardforks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,6 @@ export const hardforksDict: HardforksDict = {
* Status : Final
*/
verkle: {
eips: [4762, 6800],
eips: [2935, 4762, 6800],
},
}
13 changes: 10 additions & 3 deletions packages/common/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,20 @@ export type VerkleAccessedStateWithAddress = VerkleAccessedState & {
export interface VerkleAccessWitnessInterface {
accesses(): Generator<VerkleAccessedStateWithAddress>
rawAccesses(): Generator<RawVerkleAccessedState>
debugWitnessCost(): void
touchAndChargeProofOfAbsence(address: Address): bigint
touchAndChargeMessageCall(address: Address): bigint
touchAndChargeValueTransfer(target: Address): bigint
touchAndChargeContractCreateInit(address: Address): bigint
touchAndChargeContractCreateCompleted(address: Address): bigint
touchTxOriginAndComputeGas(origin: Address): bigint
touchTxTargetAndComputeGas(target: Address, { sendsValue }: { sendsValue?: boolean }): bigint
touchCodeChunksRangeOnReadAndChargeGas(contact: Address, startPc: number, endPc: number): bigint
touchCodeChunksRangeOnWriteAndChargeGas(contact: Address, startPc: number, endPc: number): bigint
touchCodeChunksRangeOnReadAndComputeGas(contract: Address, startPc: number, endPc: number): bigint
touchCodeChunksRangeOnWriteAndComputeGas(
contract: Address,
startPc: number,
endPc: number,
): bigint
touchAddressOnWriteAndComputeGas(
address: Address,
treeIndex: number | bigint,
Expand All @@ -120,7 +125,7 @@ export interface VerkleAccessWitnessInterface {
treeIndex: number | bigint,
subIndex: number | Uint8Array,
): bigint
touchAddressAndChargeGas(
touchAddressAndComputeGas(
address: Address,
treeIndex: number | bigint,
subIndex: number | Uint8Array,
Expand All @@ -133,6 +138,8 @@ export interface VerkleAccessWitnessInterface {
{ isWrite }: { isWrite?: boolean },
): AccessEventFlags
merge(accessWitness: VerkleAccessWitnessInterface): void
commit(): void
revert(): void
}

/*
Expand Down
35 changes: 35 additions & 0 deletions packages/evm/src/chunkCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { ChunkAccessEvent } from './verkleAccessWitness.js'
import type { PrefixedHexString } from '@ethereumjs/util'
export class ChunkCache {
cache: Map<PrefixedHexString, ChunkAccessEvent>

constructor() {
this.cache = new Map<PrefixedHexString, ChunkAccessEvent>()
}

set(stemKey: PrefixedHexString, accessedStem: ChunkAccessEvent) {
this.cache.set(stemKey, accessedStem)
}

get(stemHex: PrefixedHexString): ChunkAccessEvent | undefined {
return this.cache.get(stemHex)
}

del(stemHex: PrefixedHexString): void {
this.cache.delete(stemHex)
}

commit(): [PrefixedHexString, ChunkAccessEvent][] {
const items: [PrefixedHexString, ChunkAccessEvent][] = Array.from(this.cache.entries())
this.clear()
return items
}

clear(): void {
this.cache.clear()
}

size() {
return this.cache.size
}
}
32 changes: 25 additions & 7 deletions packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,13 @@ export class EVM implements EVMInterface {
if (this.DEBUG) {
debugGas(`callAccessGas charged(${callAccessGas}) caused OOG (-> ${gasLimit})`)
}
message.accessWitness.revert()
return { execResult: OOGResult(message.gasLimit) }
} else {
if (this.DEBUG) {
debugGas(`callAccessGas used (${callAccessGas} gas (-> ${gasLimit}))`)
}
message.accessWitness.commit()
}
}

Expand Down Expand Up @@ -323,11 +325,13 @@ export class EVM implements EVMInterface {
`Proof of absence access charged(${absenceProofAccessGas}) caused OOG (-> ${gasLimit})`,
)
}
message.accessWitness?.revert()
return { execResult: OOGResult(message.gasLimit) }
} else {
if (this.DEBUG) {
debugGas(`Proof of absence access used (${absenceProofAccessGas} gas (-> ${gasLimit}))`)
}
message.accessWitness?.commit()
}
}
toAccount = new Account()
Expand Down Expand Up @@ -469,12 +473,14 @@ export class EVM implements EVMInterface {
debugGas(
`ContractCreateInit charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})`,
)
message.accessWitness?.revert()
}
return { execResult: OOGResult(message.gasLimit) }
} else {
if (this.DEBUG) {
debugGas(`ContractCreateInit charged (${contractCreateAccessGas} gas (-> ${gasLimit}))`)
}
message.accessWitness?.commit()
}
}

Expand Down Expand Up @@ -553,11 +559,15 @@ export class EVM implements EVMInterface {
`ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})`,
)
}
message.accessWitness?.revert()
return { execResult: OOGResult(message.gasLimit) }
} else {
debug(
`ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})`,
)
if (this.DEBUG) {
debug(
`ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})`,
)
}
message.accessWitness?.commit()
}
}

Expand Down Expand Up @@ -635,6 +645,7 @@ export class EVM implements EVMInterface {
if (this.DEBUG) {
debug(`Contract creation: out of gas`)
}
message.accessWitness?.revert()
result = { ...result, ...OOGResult(message.gasLimit) }
}
} else {
Expand All @@ -644,12 +655,14 @@ export class EVM implements EVMInterface {
if (this.DEBUG) {
debug(`Not enough gas to pay the code deposit fee (Frontier)`)
}
message.accessWitness?.revert()
result = { ...result, ...COOGResult(totalGas - returnFee) }
CodestoreOOG = true
} else {
if (this.DEBUG) {
debug(`Contract creation: out of gas`)
}
message.accessWitness?.revert()
result = { ...result, ...OOGResult(message.gasLimit) }
}
}
Expand All @@ -668,6 +681,7 @@ export class EVM implements EVMInterface {
`ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})`,
)
}
message.accessWitness?.revert()
result = { ...result, ...OOGResult(message.gasLimit) }
} else {
debug(
Expand All @@ -686,7 +700,7 @@ export class EVM implements EVMInterface {
// Add access charges for writing this code to the state
if (this.common.isActivatedEIP(6800)) {
const byteCodeWriteAccessfee =
message.accessWitness!.touchCodeChunksRangeOnWriteAndChargeGas(
message.accessWitness!.touchCodeChunksRangeOnWriteAndComputeGas(
message.to,
0,
result.returnValue.length - 1,
Expand All @@ -698,9 +712,11 @@ export class EVM implements EVMInterface {
`byteCodeWrite access gas (${byteCodeWriteAccessfee}) caused OOG (-> ${gasLimit})`,
)
}
message.accessWitness?.revert()
result = { ...result, ...OOGResult(message.gasLimit) }
} else {
debug(`byteCodeWrite access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})`)
message.accessWitness?.commit()
result.executionGasUsed += byteCodeWriteAccessfee
}
}
Expand Down Expand Up @@ -802,6 +818,7 @@ export class EVM implements EVMInterface {
}
}

message.accessWitness?.commit()
return {
...result,
runState: {
Expand Down Expand Up @@ -834,11 +851,11 @@ export class EVM implements EVMInterface {
let callerAccount
if (!message) {
this._block = opts.block ?? defaultBlock()
const caller = opts.caller ?? createZeroAddress()
this._tx = {
gasPrice: opts.gasPrice ?? BIGINT_0,
origin: opts.origin ?? opts.caller ?? createZeroAddress(),
origin: opts.origin ?? caller,
}
const caller = opts.caller ?? createZeroAddress()

const value = opts.value ?? BIGINT_0
if (opts.skipBalance === true) {
Expand Down Expand Up @@ -916,7 +933,7 @@ export class EVM implements EVMInterface {
result = await this._executeCall(message as MessageWithTo)
} else {
if (this.DEBUG) {
debug(`Message CREATE execution (to undefined)`)
debug(`Message CREATE execution (to: undefined)`)
}
result = await this._executeCreate(message)
}
Expand Down Expand Up @@ -962,6 +979,7 @@ export class EVM implements EVMInterface {
this.performanceLogger.stopTimer(timer!, 0)
}

message.accessWitness?.commit()
return result
}

Expand Down
18 changes: 11 additions & 7 deletions packages/evm/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ export class Interpreter {
this.performanceLogger.unpauseTimer(overheadTimer)
}
} catch (e: any) {
// Revert access witness changes if we revert - per EIP-4762
this._runState.env.accessWitness?.revert()
if (overheadTimer !== undefined) {
this.performanceLogger.unpauseTimer(overheadTimer)
}
Expand Down Expand Up @@ -375,10 +377,17 @@ export class Interpreter {
// It needs the base fee, for correct gas limit calculation for the CALL opcodes
gas = await opEntry.gasHandler(this._runState, gas, this.common)
}

if (this._evm.events.listenerCount('step') > 0 || this._evm.DEBUG) {
// Only run this stepHook function if there is an event listener (e.g. test runner)
// or if the vm is running in debug mode (to display opcode debug logs)
await this._runStepHook(gas, this.getGasLeft())
}

if (this.common.isActivatedEIP(6800) && this._env.chargeCodeAccesses === true) {
const contract = this._runState.interpreter.getAddress()
const statelessGas =
this._runState.env.accessWitness!.touchCodeChunksRangeOnReadAndChargeGas(
this._runState.env.accessWitness!.touchCodeChunksRangeOnReadAndComputeGas(
contract,
this._runState.programCounter,
this._runState.programCounter,
Expand All @@ -387,12 +396,6 @@ export class Interpreter {
debugGas(`codechunk accessed statelessGas=${statelessGas} (-> ${gas})`)
}

if (this._evm.events.listenerCount('step') > 0 || this._evm.DEBUG) {
// Only run this stepHook function if there is an event listener (e.g. test runner)
// or if the vm is running in debug mode (to display opcode debug logs)
await this._runStepHook(gas, this.getGasLeft())
}

// Check for invalid opcode
if (opInfo.isInvalid) {
throw new EvmError(ERROR.INVALID_OPCODE)
Expand All @@ -412,6 +415,7 @@ export class Interpreter {
} else {
opFn.apply(null, [this._runState, this.common])
}
this._runState.env.accessWitness?.commit()
} finally {
if (this.profilerOpts?.enabled === true) {
this.performanceLogger.stopTimer(
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/src/opcodes/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ export const handlers: Map<number, OpHandler> = new Map([
const contract = runState.interpreter.getAddress()
const startOffset = Math.min(runState.code.length, runState.programCounter + 1)
const endOffset = Math.min(runState.code.length, startOffset + numToPush - 1)
const statelessGas = runState.env.accessWitness!.touchCodeChunksRangeOnReadAndChargeGas(
const statelessGas = runState.env.accessWitness!.touchCodeChunksRangeOnReadAndComputeGas(
contract,
startOffset,
endOffset,
Expand Down
4 changes: 2 additions & 2 deletions packages/evm/src/opcodes/gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
codeEnd = codeSize
}

gas += runState.env.accessWitness!.touchCodeChunksRangeOnReadAndChargeGas(
gas += runState.env.accessWitness!.touchCodeChunksRangeOnReadAndComputeGas(
contract,
Number(_codeOffset),
Number(codeEnd),
Expand Down Expand Up @@ -242,7 +242,7 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
codeEnd = codeSize
}

gas += runState.env.accessWitness!.touchCodeChunksRangeOnReadAndChargeGas(
gas += runState.env.accessWitness!.touchCodeChunksRangeOnReadAndComputeGas(
address,
Number(_codeOffset),
Number(codeEnd),
Expand Down
1 change: 1 addition & 0 deletions packages/evm/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ export const paramsEVM: ParamsDict = {
// evm
historyStorageAddress: '0x0aae40965e6800cd9b1f4b05ff21581047e3f91e', // The address where the historical blockhashes are stored
historyServeWindow: 8192, // The amount of blocks to be served by the historical blockhash contract
systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address
},
/**
. * BASEFEE opcode
Expand Down
44 changes: 44 additions & 0 deletions packages/evm/src/stemCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { StemAccessEvent, StemMeta } from './verkleAccessWitness.js'
import type { PrefixedHexString } from '@ethereumjs/util'
export class StemCache {
cache: Map<PrefixedHexString, StemAccessEvent & StemMeta>

constructor() {
this.cache = new Map<PrefixedHexString, StemAccessEvent & StemMeta>()
}

set(stemKey: PrefixedHexString, accessedStem: StemAccessEvent & StemMeta) {
this.cache.set(stemKey, accessedStem)
}

get(stemHex: PrefixedHexString): (StemAccessEvent & StemMeta) | undefined {
return this.cache.get(stemHex)
}

del(stemHex: PrefixedHexString): void {
this.cache.delete(stemHex)
}

commit(): [PrefixedHexString, StemAccessEvent & StemMeta][] {
const items: [PrefixedHexString, StemAccessEvent & StemMeta][] = Array.from(
this.cache.entries(),
)
this.clear()
return items
}

/**
* Clear cache
*/
clear(): void {
this.cache.clear()
}

/**
* Returns the size of the cache
* @returns
*/
size() {
return this.cache.size
}
}
Loading

0 comments on commit cd4afd0

Please sign in to comment.