UNPKG

@ethereumjs/statemanager

Version:
155 lines (131 loc) 5.5 kB
import { Account, EthereumJSErrorWithoutCode, bytesToHex } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { OriginalStorageCache } from './cache/originalStorageCache.ts' import { modifyAccountFields } from './util.ts' import type { AccountFields, Common, StateManagerInterface } from '@ethereumjs/common' import type { Address, PrefixedHexString } from '@ethereumjs/util' import type { SimpleStateManagerOpts } from './index.ts' /** * Simple and dependency-free state manager for basic state access use cases * where a merkle-patricia or verkle tree backed state manager is too heavy-weight. * * This state manager comes with the basic state access logic for * accounts, storage and code (put* and get* methods) as well as a simple * implementation of checkpointing but lacks methods implementations of * state root related logic as well as some other non-core functions. * * Functionality provided is sufficient to be used for simple EVM use * cases and the state manager is used as default there. * * For a more full fledged and MPT-backed state manager implementation * have a look at the [`@ethereumjs/statemanager` package docs](https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/statemanager/docs/README.md). */ export class SimpleStateManager implements StateManagerInterface { public accountStack: Map<PrefixedHexString, Account | undefined>[] = [] public codeStack: Map<PrefixedHexString, Uint8Array>[] = [] public storageStack: Map<string, Uint8Array>[] = [] originalStorageCache: { get(address: Address, key: Uint8Array): Promise<Uint8Array> clear(): void } public readonly common?: Common constructor(opts: SimpleStateManagerOpts = {}) { this.checkpointSync() this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this)) this.common = opts.common } protected topAccountStack() { return this.accountStack[this.accountStack.length - 1] } protected topCodeStack() { return this.codeStack[this.codeStack.length - 1] } protected topStorageStack() { return this.storageStack[this.storageStack.length - 1] } // Synchronous version of checkpoint() to allow to call from constructor protected checkpointSync() { const newTopA = new Map(this.topAccountStack()) for (const [address, account] of newTopA) { const accountCopy = account !== undefined ? Object.assign(Object.create(Object.getPrototypeOf(account)), account) : undefined newTopA.set(address, accountCopy) } this.accountStack.push(newTopA) this.codeStack.push(new Map(this.topCodeStack())) this.storageStack.push(new Map(this.topStorageStack())) } async getAccount(address: Address): Promise<Account | undefined> { return this.topAccountStack().get(address.toString()) } async putAccount(address: Address, account?: Account | undefined): Promise<void> { this.topAccountStack().set(address.toString(), account) } async deleteAccount(address: Address): Promise<void> { this.topAccountStack().set(address.toString(), undefined) } async modifyAccountFields(address: Address, accountFields: AccountFields): Promise<void> { await modifyAccountFields(this, address, accountFields) } async getCode(address: Address): Promise<Uint8Array> { return this.topCodeStack().get(address.toString()) ?? new Uint8Array(0) } async putCode(address: Address, value: Uint8Array): Promise<void> { this.topCodeStack().set(address.toString(), value) if ((await this.getAccount(address)) === undefined) { await this.putAccount(address, new Account()) } await this.modifyAccountFields(address, { codeHash: (this.common?.customCrypto.keccak256 ?? keccak256)(value), }) } async getCodeSize(address: Address): Promise<number> { const contractCode = await this.getCode(address) return contractCode.length } async getStorage(address: Address, key: Uint8Array): Promise<Uint8Array> { return ( this.topStorageStack().get(`${address.toString()}_${bytesToHex(key)}`) ?? new Uint8Array(0) ) } async putStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise<void> { this.topStorageStack().set(`${address.toString()}_${bytesToHex(key)}`, value) } async clearStorage(): Promise<void> {} async checkpoint(): Promise<void> { this.checkpointSync() } async commit(): Promise<void> { this.accountStack.splice(-2, 1) this.codeStack.splice(-2, 1) this.storageStack.splice(-2, 1) } async revert(): Promise<void> { this.accountStack.pop() this.codeStack.pop() this.storageStack.pop() } async flush(): Promise<void> {} clearCaches(): void {} shallowCopy(): StateManagerInterface { const copy = new SimpleStateManager({ common: this.common }) for (let i = 0; i < this.accountStack.length; i++) { copy.accountStack.push(new Map(this.accountStack[i])) copy.codeStack.push(new Map(this.codeStack[i])) copy.storageStack.push(new Map(this.storageStack[i])) } return copy } // State root functionality not implemented getStateRoot(): Promise<Uint8Array> { throw EthereumJSErrorWithoutCode('Method not implemented.') } setStateRoot(): Promise<void> { throw EthereumJSErrorWithoutCode('Method not implemented.') } hasStateRoot(): Promise<boolean> { throw EthereumJSErrorWithoutCode('Method not implemented.') } }