Skip to content

Commit

Permalink
cleanly apply stem changes from gabriels commit
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech committed Apr 15, 2024
1 parent 1d44193 commit dccaca3
Show file tree
Hide file tree
Showing 16 changed files with 170 additions and 130 deletions.
23 changes: 16 additions & 7 deletions package-lock.json

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

3 changes: 1 addition & 2 deletions packages/client/src/execution/vmexecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,8 @@ export class VMExecution extends Execution {
if (this.verkleVM !== undefined) {
return
}

this.config.logger.info(`Setting up verkleVM`)
const stateManager = new StatelessVerkleStateManager()
const stateManager = await StatelessVerkleStateManager.create()
this.verkleVM = await VM.create({
common: this.config.execCommon,
blockchain: this.chain.blockchain,
Expand Down
2 changes: 1 addition & 1 deletion packages/client/test/rpc/engine/kaustinen6.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { BeaconPayloadJson } from '@ethereumjs/block'
import type { Common } from '@ethereumjs/common'
import type { HttpClient } from 'jayson/promise'
const genesisVerkleStateRoot = '0x382960711d9ccf58b9db20122e2253eb9bfa99d513f8c9d4e85b55971721f4de'
const genesisVerkleBlockHash = '0x086326f2922364dba375e7c9bed375d622845615c0974ffd1d3be0e34edbfbc3'
const genesisVerkleBlockHash = '0x2d4f1927f519051a71d6d9b1bf7066ded483b13c0f4a7bb9e5b388cdf0d496fd'

/**
* One can run this test in this format:
Expand Down
3 changes: 2 additions & 1 deletion packages/statemanager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
"debug": "^4.3.3",
"ethereum-cryptography": "^2.1.3",
"js-sdsl": "^4.1.4",
"lru-cache": "10.1.0"
"lru-cache": "10.1.0",
"verkle-cryptography-wasm": "^0.4.0"
},
"devDependencies": {
"@ethereumjs/block": "^5.2.0",
Expand Down
20 changes: 15 additions & 5 deletions packages/statemanager/src/accessWitness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getKey, getStem } from '@ethereumjs/verkle'
import debugDefault from 'debug'

import type { Address, PrefixedHexString } from '@ethereumjs/util'
import type { VerkleCrypto } from '@ethereumjs/verkle'

const { debug: createDebugLogger } = debugDefault
const debug = createDebugLogger('statemanager:verkle:aw')
Expand Down Expand Up @@ -72,13 +73,18 @@ export type AccessedStateWithAddress = AccessedState & {
export class AccessWitness {
stems: Map<PrefixedHexString, StemAccessEvent & StemMeta>
chunks: Map<PrefixedHexString, ChunkAccessEvent>

verkleCrypto: VerkleCrypto
constructor(
opts: {
verkleCrypto?: VerkleCrypto
stems?: Map<PrefixedHexString, StemAccessEvent & StemMeta>
chunks?: Map<PrefixedHexString, ChunkAccessEvent>
} = {}
) {
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required')
}
this.verkleCrypto = opts.verkleCrypto
this.stems = opts.stems ?? new Map<PrefixedHexString, StemAccessEvent & StemMeta>()
this.chunks = opts.chunks ?? new Map<PrefixedHexString, ChunkAccessEvent>()
}
Expand Down Expand Up @@ -164,7 +170,7 @@ export class AccessWitness {
return gas
}

touchCodeChunksRangeOnReadAndChargeGas(contact: Address, startPc: number, endPc: number) {
touchCodeChunksRangeOnReadAndChargeGas(contact: Address, startPc: number, endPc: number): bigint {
let gas = BIGINT_0
for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum)
Expand All @@ -173,7 +179,11 @@ export class AccessWitness {
return gas
}

touchCodeChunksRangeOnWriteAndChargeGas(contact: Address, startPc: number, endPc: number) {
touchCodeChunksRangeOnWriteAndChargeGas(
contact: Address,
startPc: number,
endPc: number
): bigint {
let gas = BIGINT_0
for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum)
Expand Down Expand Up @@ -251,7 +261,7 @@ export class AccessWitness {
// i.e. no fill cost is charged right now
const chunkFill = false

const accessedStemKey = getStem(address, treeIndex)
const accessedStemKey = getStem(this.verkleCrypto, address, treeIndex)
const accessedStemHex = bytesToHex(accessedStemKey)
let accessedStem = this.stems.get(accessedStemHex)
if (accessedStem === undefined) {
Expand Down Expand Up @@ -291,7 +301,7 @@ export class AccessWitness {

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

merge(accessWitness: AccessWitness): void {
Expand Down
85 changes: 47 additions & 38 deletions packages/statemanager/src/statelessVerkleStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import {
short,
toBytes,
} from '@ethereumjs/util'
import { getKey, getStem, verifyUpdate } from '@ethereumjs/verkle'
import { getKey, getStem } from '@ethereumjs/verkle'
import debugDefault from 'debug'
import { keccak256 } from 'ethereum-cryptography/keccak.js'
import { equalsBytes } from 'ethereum-cryptography/utils'
import { loadVerkleCrypto } from 'verkle-cryptography-wasm'

import {
AccessWitness,
Expand Down Expand Up @@ -45,6 +45,7 @@ import type {
StorageRange,
} from '@ethereumjs/common'
import type { Address, PrefixedHexString } from '@ethereumjs/util'
import type { VerkleCrypto } from '@ethereumjs/verkle'

const { debug: createDebugLogger } = debugDefault

Expand Down Expand Up @@ -113,6 +114,7 @@ export interface StatelessVerkleStateManagerOpts {
storageCacheOpts?: CacheOptions
codeCacheOpts?: CacheOptions
accesses?: AccessWitness
verkleCrypto?: VerkleCrypto
}

const PUSH_OFFSET = 95
Expand Down Expand Up @@ -141,6 +143,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {

originalStorageCache: OriginalStorageCache

verkleCrypto: VerkleCrypto
protected readonly _accountCacheSettings: CacheSettings
protected readonly _storageCacheSettings: CacheSettings
protected readonly _codeCacheSettings: CacheSettings
Expand Down Expand Up @@ -173,6 +176,17 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {

private keccakFunction: Function

/**
* Async static constructor for StatelessVerkleStateManager
* @param opts `StatelessVerkleStateManagerOpts`
* @returns a StatelessVerkleStateManager with initialized Verkle Crypto
*/
static create = async (opts: StatelessVerkleStateManagerOpts = {}) => {
if (opts.verkleCrypto === undefined) {
opts.verkleCrypto = await loadVerkleCrypto()
}
return new StatelessVerkleStateManager(opts)
}
/**
* Instantiate the StateManager interface.
*/
Expand Down Expand Up @@ -221,23 +235,15 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {

this.keccakFunction = opts.common?.customCrypto.keccak256 ?? keccak256

if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required')
}
this.verkleCrypto = opts.verkleCrypto

// Skip DEBUG calls unless 'ethjs' included in environmental DEBUG variables
// Additional window check is to prevent vite browser bundling (and potentially other) to break
this.DEBUG =
typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false

/*
* For a custom StateManager implementation adopt these
* callbacks passed to the `Cache` instantiated to perform
* the `get`, `put` and `delete` operations with the
* desired backend.
*/
// const getCb: get = async (address) => {
// return undefined
// }
// const putCb: put = async (keyBuf, accountRlp) => {}
// const deleteCb = async (keyBuf: Uint8Array) => {}
// this._cache = new Cache({ get, putCb, deleteCb })
}

async getTransitionStateRoot(_: DefaultStateManager, __: Uint8Array): Promise<Uint8Array> {
Expand All @@ -257,7 +263,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
}

this._executionWitness = executionWitness
this.accessWitness = accessWitness ?? new AccessWitness()
this.accessWitness = accessWitness ?? new AccessWitness({ verkleCrypto: this.verkleCrypto })

this._proof = executionWitness.verkleProof as unknown as Uint8Array

Expand Down Expand Up @@ -325,9 +331,9 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
return getKey(stem, CODE_SIZE_LEAF_KEY)
}

getTreeKeyForCodeChunk(address: Address, chunkId: number) {
async getTreeKeyForCodeChunk(address: Address, chunkId: number) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkId)
return getKey(getStem(address, treeIndex), toBytes(subIndex))
return getKey(getStem(this.verkleCrypto, address, treeIndex), toBytes(subIndex))
}

chunkifyCode(code: Uint8Array) {
Expand All @@ -340,15 +346,15 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
throw new Error('Not implemented')
}

getTreeKeyForStorageSlot(address: Address, storageKey: bigint) {
async getTreeKeyForStorageSlot(address: Address, storageKey: bigint) {
const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(storageKey)

return getKey(getStem(address, treeIndex), toBytes(subIndex))
return getKey(getStem(this.verkleCrypto, address, treeIndex), toBytes(subIndex))
}

checkChunkWitnessPresent(address: Address, codeOffset: number) {
const chunkId = Math.floor(codeOffset / 31)
const chunkKey = bytesToHex(this.getTreeKeyForCodeChunk(address, chunkId))
async checkChunkWitnessPresent(address: Address, codeOffset: number) {
const chunkId = codeOffset / 31
const chunkKey = bytesToHex(await this.getTreeKeyForCodeChunk(address, chunkId))
return this._state[chunkKey] !== undefined
}

Expand All @@ -358,7 +364,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
* checkpoints were reverted.
*/
shallowCopy(): EVMStateManagerInterface {
const stateManager = new StatelessVerkleStateManager()
const stateManager = new StatelessVerkleStateManager({ verkleCrypto: this.verkleCrypto })
stateManager.initVerkleExecutionWitness(this._blockNum, this._executionWitness!)
return stateManager
}
Expand Down Expand Up @@ -419,7 +425,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {

const chunks = Math.floor(codeSize / 31) + 1
for (let chunkId = 0; chunkId < chunks; chunkId++) {
const chunkKey = bytesToHex(this.getTreeKeyForCodeChunk(address, chunkId))
const chunkKey = bytesToHex(await this.getTreeKeyForCodeChunk(address, chunkId))
const codeChunk = this._state[chunkKey]
if (codeChunk === null) {
const errorMsg = `Invalid access to a non existent code chunk with chunkKey=${chunkKey}`
Expand Down Expand Up @@ -486,7 +492,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
}
}

const storageKey = this.getTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)))
const storageKey = await this.getTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)))
const storageValue = toBytes(this._state[bytesToHex(storageKey)])

if (!this._storageCacheSettings.deactivate) {
Expand All @@ -508,7 +514,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
this._storageCache!.put(address, key, value)
} else {
// TODO: Consider refactoring this in a writeContractStorage function? Like in stateManager.ts
const storageKey = this.getTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)))
const storageKey = await this.getTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)))
this._state[bytesToHex(storageKey)] = bytesToHex(setLengthRight(value, 32))
}
}
Expand All @@ -518,7 +524,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
* @param address - Address to clear the storage of
*/
async clearContractStorage(address: Address): Promise<void> {
const stem = getStem(address, 0)
const stem = getStem(this.verkleCrypto, address, 0)
const codeHashKey = this.getTreeKeyForCodeHash(stem)
this._storageCache?.clearContractStorage(address)
// Update codeHash to `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`
Expand All @@ -537,7 +543,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
}
}

const stem = getStem(address, 0)
const stem = getStem(this.verkleCrypto, address, 0)
const versionKey = this.getTreeKeyForVersion(stem)
const balanceKey = this.getTreeKeyForBalance(stem)
const nonceKey = this.getTreeKeyForNonce(stem)
Expand Down Expand Up @@ -624,7 +630,7 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
}

if (this._accountCacheSettings.deactivate) {
const stem = getStem(address, 0)
const stem = getStem(this.verkleCrypto, address, 0)
const balanceKey = this.getTreeKeyForBalance(stem)
const nonceKey = this.getTreeKeyForNonce(stem)
const codeHashKey = this.getTreeKeyForCodeHash(stem)
Expand Down Expand Up @@ -678,7 +684,8 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
throw new Error('Not implemented yet')
}

async verifyProof(parentVerkleRoot: Uint8Array): Promise<boolean> {
// TODO: Re-implement this method once we have working verifyUpdate and the testnets have been updated to provide ingestible data
async verifyProof(_: Uint8Array): Promise<boolean> {
// Implementation: https://github.com/crate-crypto/rust-verkle-wasm/blob/master/src/lib.rs#L45
// The root is the root of the current (un-updated) trie
// The proof is proof of membership of all of the accessed values
Expand All @@ -687,17 +694,19 @@ export class StatelessVerkleStateManager implements EVMStateManagerInterface {
//
// This function returns the new root when all of the updated values are applied

const updatedStateRoot: Uint8Array = verifyUpdate(
parentVerkleRoot,
this._proof!, // TODO: Convert this into a Uint8Array ingestible by the method
new Map() // TODO: Generate the keys_values map from the old to the updated value
)
// const updatedStateRoot: Uint8Array = verifyUpdate(
// parentVerkleRoot,
// this._proof!, // TODO: Convert this into a Uint8Array ingestible by the method
// new Map() // TODO: Generate the keys_values map from the old to the updated value
// )

// TODO: Not sure if this should return the updated state Root (current block) or the un-updated one (parent block)
const verkleRoot = await this.getStateRoot()
// const verkleRoot = await this.getStateRoot()

// Verify that updatedStateRoot matches the state root of the block
return equalsBytes(updatedStateRoot, verkleRoot)
// return equalsBytes(updatedStateRoot, verkleRoot)

return true
}

// Verifies that the witness post-state matches the computed post-state
Expand Down
Loading

0 comments on commit dccaca3

Please sign in to comment.