Skip to content

Commit

Permalink
evm: migrate accessWitness file to evm package (#3770)
Browse files Browse the repository at this point in the history
* evm: migrate access witness file to evm package

* common: refactor VerkleAccessedStateType into common

* statemanager: remove accesswitness from sm package2

* evm: adjust evm for access witness

* statemanager: add evm state manager as devdependency for testing standalone state managers2

* statemanager: adjust state manager with refactored access witness

* common: add common accessWitness interfaces

* common: remove accessWitness from state manager interface

* statemanager: remove accessWitness from state manager

* evm: updates

* vm: updates

* vm: add verkle crypto to VmOpts

* evm: fix test and add accessWitness check

* statemanager: simplify access witness decoding

* evm: remove verklecrypto from evm

* evm: remove shallowCopy from accessWitness interface
  • Loading branch information
gabrocheleau authored Oct 28, 2024
1 parent 65ccf7c commit 88ef815
Show file tree
Hide file tree
Showing 17 changed files with 202 additions and 187 deletions.
41 changes: 34 additions & 7 deletions packages/common/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,38 @@ export type AccessEventFlags = {
*
* Experimental (do not implement)
*/
export interface AccessWitnessInterface {

export enum VerkleAccessedStateType {
BasicData = 'basicData',
CodeHash = 'codeHash',
Code = 'code',
Storage = 'storage',
}

export type RawVerkleAccessedState = {
address: Address
treeIndex: number | bigint
chunkIndex: number
chunkKey: PrefixedHexString
}

export type VerkleAccessedState =
| {
type: Exclude<
VerkleAccessedStateType,
VerkleAccessedStateType.Code | VerkleAccessedStateType.Storage
>
}
| { type: VerkleAccessedStateType.Code; codeOffset: number }
| { type: VerkleAccessedStateType.Storage; slot: bigint }

export type VerkleAccessedStateWithAddress = VerkleAccessedState & {
address: Address
chunkKey: PrefixedHexString
}
export interface VerkleAccessWitnessInterface {
accesses(): Generator<VerkleAccessedStateWithAddress>
rawAccesses(): Generator<RawVerkleAccessedState>
touchAndChargeProofOfAbsence(address: Address): bigint
touchAndChargeMessageCall(address: Address): bigint
touchAndChargeValueTransfer(target: Address): bigint
Expand Down Expand Up @@ -101,8 +132,7 @@ export interface AccessWitnessInterface {
subIndex: number | Uint8Array,
{ isWrite }: { isWrite?: boolean },
): AccessEventFlags
shallowCopy(): AccessWitnessInterface
merge(accessWitness: AccessWitnessInterface): void
merge(accessWitness: VerkleAccessWitnessInterface): void
}

/*
Expand Down Expand Up @@ -161,14 +191,11 @@ export interface StateManagerInterface {
clear(): void
}
generateCanonicalGenesis?(initState: any): Promise<void> // TODO make input more typesafe
// only Verkle/EIP-6800 (experimental)
accessWitness?: AccessWitnessInterface
initVerkleExecutionWitness?(
blockNum: bigint,
executionWitness?: VerkleExecutionWitness | null,
accessWitness?: AccessWitnessInterface,
): void
verifyPostState?(): Promise<boolean>
verifyPostState?(accessWitness: VerkleAccessWitnessInterface): Promise<boolean>
checkChunkWitnessPresent?(contract: Address, programCounter: number): Promise<boolean>
getAppliedKey?(address: Uint8Array): Uint8Array // only for preimages

Expand Down
16 changes: 11 additions & 5 deletions packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import type { MessageWithTo } from './message.js'
import type { AsyncDynamicGasHandler, SyncDynamicGasHandler } from './opcodes/gas.js'
import type { OpHandler, OpcodeList, OpcodeMap } from './opcodes/index.js'
import type { CustomPrecompile, PrecompileFunc } from './precompiles/index.js'
import type { VerkleAccessWitness } from './verkleAccessWitness.js'
import type { Common, StateManagerInterface } from '@ethereumjs/common'

const debug = debugDefault('evm:evm')
Expand Down Expand Up @@ -98,6 +99,7 @@ export class EVM implements EVMInterface {
public stateManager: StateManagerInterface
public blockchain: EVMMockBlockchainInterface
public journal: Journal
public verkleAccessWitness?: VerkleAccessWitness

public readonly transientStorage: TransientStorage

Expand Down Expand Up @@ -218,6 +220,7 @@ export class EVM implements EVMInterface {
this._bls = opts.bls ?? new NobleBLS()
this._bls.init?.()
}

this._bn254 = opts.bn254!

this._emit = async (topic: string, data: any): Promise<void> => {
Expand Down Expand Up @@ -259,20 +262,23 @@ export class EVM implements EVMInterface {
const fromAddress = message.caller

if (this.common.isActivatedEIP(6800)) {
if (message.accessWitness === undefined) {
throw new Error('accessWitness is required for EIP-6800')
}
const sendsValue = message.value !== BIGINT_0
if (message.depth === 0) {
const originAccessGas = message.accessWitness!.touchTxOriginAndComputeGas(fromAddress)
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(fromAddress)
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`)

const destAccessGas = message.accessWitness!.touchTxTargetAndComputeGas(message.to, {
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)
let callAccessGas = message.accessWitness.touchAndChargeMessageCall(message.to)
if (sendsValue) {
callAccessGas += message.accessWitness!.touchAndChargeValueTransfer(message.to)
callAccessGas += message.accessWitness.touchAndChargeValueTransfer(message.to)
}
gasLimit -= callAccessGas
if (gasLimit < BIGINT_0) {
Expand Down Expand Up @@ -860,7 +866,7 @@ export class EVM implements EVMInterface {
createdAddresses: opts.createdAddresses ?? new Set(),
delegatecall: opts.delegatecall,
blobVersionedHashes: opts.blobVersionedHashes,
accessWitness: opts.accessWitness,
accessWitness: this.verkleAccessWitness,
})
}

Expand Down
1 change: 1 addition & 0 deletions packages/evm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ export {

export * from './constructors.js'
export * from './params.js'
export * from './verkleAccessWitness.js'
8 changes: 6 additions & 2 deletions packages/evm/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ import type {
EVMResult,
Log,
} from './types.js'
import type { AccessWitnessInterface, Common, StateManagerInterface } from '@ethereumjs/common'
import type {
Common,
StateManagerInterface,
VerkleAccessWitnessInterface,
} from '@ethereumjs/common'
import type { Address, PrefixedHexString } from '@ethereumjs/util'

const debugGas = debugDefault('evm:gas')
Expand Down Expand Up @@ -78,7 +82,7 @@ export interface Env {
eof?: EOFEnv /* Optional EOF environment in case of EOF execution */
blobVersionedHashes: PrefixedHexString[] /** Versioned hashes for blob transactions */
createdAddresses?: Set<string>
accessWitness?: AccessWitnessInterface
accessWitness?: VerkleAccessWitnessInterface
chargeCodeAccesses?: boolean
}

Expand Down
6 changes: 3 additions & 3 deletions packages/evm/src/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BIGINT_0, createZeroAddress } from '@ethereumjs/util'

import type { PrecompileFunc } from './precompiles/index.js'
import type { EOFEnv } from './types.js'
import type { AccessWitnessInterface } from '@ethereumjs/common'
import type { VerkleAccessWitnessInterface } from '@ethereumjs/common'
import type { Address, PrefixedHexString } from '@ethereumjs/util'

const defaults = {
Expand Down Expand Up @@ -40,7 +40,7 @@ interface MessageOpts {
delegatecall?: boolean
gasRefund?: bigint
blobVersionedHashes?: PrefixedHexString[]
accessWitness?: AccessWitnessInterface
accessWitness?: VerkleAccessWitnessInterface
}

export class Message {
Expand Down Expand Up @@ -73,7 +73,7 @@ export class Message {
* List of versioned hashes if message is a blob transaction in the outer VM
*/
blobVersionedHashes?: PrefixedHexString[]
accessWitness?: AccessWitnessInterface
accessWitness?: VerkleAccessWitnessInterface

constructor(opts: MessageOpts) {
this.to = opts.to
Expand Down
6 changes: 4 additions & 2 deletions packages/evm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import type { OpHandler } from './opcodes/index.js'
import type { CustomPrecompile } from './precompiles/index.js'
import type { PrecompileFunc } from './precompiles/types.js'
import type {
AccessWitnessInterface,
Common,
ParamsDict,
StateManagerInterface,
VerkleAccessWitnessInterface,
} from '@ethereumjs/common'
import type { Account, Address, PrefixedHexString } from '@ethereumjs/util'
import type { EventEmitter } from 'eventemitter3'
Expand Down Expand Up @@ -128,7 +128,7 @@ export interface EVMRunCallOpts extends EVMRunOpts {
*/
message?: Message

accessWitness?: AccessWitnessInterface
accessWitness?: VerkleAccessWitnessInterface
}

interface NewContractEvent {
Expand Down Expand Up @@ -166,6 +166,7 @@ export interface EVMInterface {
runCall(opts: EVMRunCallOpts): Promise<EVMResult>
runCode(opts: EVMRunCodeOpts): Promise<ExecResult>
events?: EventEmitter<EVMEvent>
verkleAccessWitness?: VerkleAccessWitnessInterface
}

export type EVMProfilerOpts = {
Expand Down Expand Up @@ -212,6 +213,7 @@ export interface EVMOpts {
* - [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-6800](https://eips.ethereum.org/EIPS/eip-6800) - Verkle state tree (Experimental)
* - [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)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { VerkleAccessedStateType } from '@ethereumjs/common'
import {
BIGINT_0,
VERKLE_BASIC_DATA_LEAF_KEY,
Expand All @@ -14,7 +15,13 @@ import {
} from '@ethereumjs/util'
import debugDefault from 'debug'

import type { AccessEventFlags, AccessWitnessInterface } from '@ethereumjs/common'
import type {
AccessEventFlags,
RawVerkleAccessedState,
VerkleAccessWitnessInterface,
VerkleAccessedState,
VerkleAccessedStateWithAddress,
} from '@ethereumjs/common'
import type { Address, PrefixedHexString, VerkleCrypto } from '@ethereumjs/util'

const debug = debugDefault('statemanager:verkle:aw')
Expand All @@ -36,40 +43,16 @@ type ChunkAccessEvent = StemAccessEvent & { fill?: boolean }

// Since stem is pedersen hashed, it is useful to maintain the reverse relationship
type StemMeta = { address: Address; treeIndex: number | bigint }
type RawAccessedState = {
address: Address
treeIndex: number | bigint
chunkIndex: number
chunkKey: PrefixedHexString
}

export enum AccessedStateType {
BasicData = 'basicData',
CodeHash = 'codeHash',
Code = 'code',
Storage = 'storage',
}

type AccessedState =
| { type: Exclude<AccessedStateType, AccessedStateType.Code | AccessedStateType.Storage> }
| { type: AccessedStateType.Code; codeOffset: number }
| { type: AccessedStateType.Storage; slot: bigint }
export type AccessedStateWithAddress = AccessedState & {
address: Address
chunkKey: PrefixedHexString
}

export class AccessWitness implements AccessWitnessInterface {
export class VerkleAccessWitness implements VerkleAccessWitnessInterface {
stems: Map<PrefixedHexString, StemAccessEvent & StemMeta>
chunks: Map<PrefixedHexString, ChunkAccessEvent>
verkleCrypto: VerkleCrypto
constructor(
opts: {
verkleCrypto?: VerkleCrypto
stems?: Map<PrefixedHexString, StemAccessEvent & StemMeta>
chunks?: Map<PrefixedHexString, ChunkAccessEvent>
} = {},
) {
constructor(opts: {
verkleCrypto: VerkleCrypto
stems?: Map<PrefixedHexString, StemAccessEvent & StemMeta>
chunks?: Map<PrefixedHexString, ChunkAccessEvent>
}) {
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required')
}
Expand Down Expand Up @@ -275,12 +258,7 @@ export class AccessWitness implements AccessWitnessInterface {
return { stemRead, stemWrite, chunkRead, chunkWrite, chunkFill }
}

/**Create a shallow copy, could clone some caches in future for optimizations */
shallowCopy(): AccessWitness {
return new AccessWitness({ verkleCrypto: this.verkleCrypto })
}

merge(accessWitness: AccessWitness): void {
merge(accessWitness: VerkleAccessWitness): void {
for (const [chunkKey, chunkValue] of accessWitness.chunks.entries()) {
const stemKey = chunkKey.slice(0, chunkKey.length - 2) as PrefixedHexString
const stem = accessWitness.stems.get(stemKey)
Expand All @@ -305,7 +283,7 @@ export class AccessWitness implements AccessWitnessInterface {
}
}

*rawAccesses(): Generator<RawAccessedState> {
*rawAccesses(): Generator<RawVerkleAccessedState> {
for (const chunkKey of this.chunks.keys()) {
// drop the last byte
const stemKey = chunkKey.slice(0, chunkKey.length - 2) as PrefixedHexString
Expand All @@ -320,7 +298,7 @@ export class AccessWitness implements AccessWitnessInterface {
}
}

*accesses(): Generator<AccessedStateWithAddress> {
*accesses(): Generator<VerkleAccessedStateWithAddress> {
for (const rawAccess of this.rawAccesses()) {
const { address, treeIndex, chunkIndex, chunkKey } = rawAccess
const accessedState = decodeAccessedState(treeIndex, chunkIndex)
Expand All @@ -329,46 +307,34 @@ export class AccessWitness implements AccessWitnessInterface {
}
}

export function decodeAccessedState(treeIndex: number | bigint, chunkIndex: number): AccessedState {
export function decodeAccessedState(
treeIndex: number | bigint,
chunkIndex: number,
): VerkleAccessedState {
const position = BigInt(treeIndex) * BigInt(VERKLE_NODE_WIDTH) + BigInt(chunkIndex)
switch (position) {
case BigInt(0):
return { type: AccessedStateType.BasicData }
return { type: VerkleAccessedStateType.BasicData }
case BigInt(1):
return { type: AccessedStateType.CodeHash }
return { type: VerkleAccessedStateType.CodeHash }
default:
if (position < VERKLE_HEADER_STORAGE_OFFSET) {
throw Error(`No attribute yet stored >=2 and <${VERKLE_HEADER_STORAGE_OFFSET}`)
}

if (position >= VERKLE_HEADER_STORAGE_OFFSET && position < VERKLE_CODE_OFFSET) {
const slot = position - BigInt(VERKLE_HEADER_STORAGE_OFFSET)
return { type: AccessedStateType.Storage, slot }
return { type: VerkleAccessedStateType.Storage, slot }
} else if (position >= VERKLE_CODE_OFFSET && position < VERKLE_MAIN_STORAGE_OFFSET) {
const codeChunkIdx = Number(position) - VERKLE_CODE_OFFSET
return { type: AccessedStateType.Code, codeOffset: codeChunkIdx * 31 }
return { type: VerkleAccessedStateType.Code, codeOffset: codeChunkIdx * 31 }
} else if (position >= VERKLE_MAIN_STORAGE_OFFSET) {
const slot = BigInt(position - VERKLE_MAIN_STORAGE_OFFSET)
return { type: AccessedStateType.Storage, slot }
return { type: VerkleAccessedStateType.Storage, slot }
} else {
throw Error(
`Invalid treeIndex=${treeIndex} chunkIndex=${chunkIndex} for verkle tree access`,
)
}
}
}

export function decodeValue(type: AccessedStateType, value: PrefixedHexString | null): string {
if (value === null) {
return ''
}

switch (type) {
case AccessedStateType.BasicData:
case AccessedStateType.CodeHash:
case AccessedStateType.Code:
case AccessedStateType.Storage: {
return value
}
}
}
Loading

0 comments on commit 88ef815

Please sign in to comment.