From fa3c2ec8a0de34147c237b28de467bc6734ff2a2 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 7 Aug 2024 14:46:35 -0400 Subject: [PATCH 01/23] Revert "statemanager: refactor logic into capabilities (#3554)" This reverts commit d7d1dabd98c8a4ebe1bd8702d179e1b980cf37b6. --- packages/client/src/execution/vmexecution.ts | 8 +- packages/client/src/rpc/modules/eth.ts | 2 +- packages/common/src/interfaces.ts | 31 ------- packages/statemanager/src/cache/account.ts | 2 +- packages/statemanager/src/cache/code.ts | 2 +- packages/statemanager/src/cache/storage.ts | 2 +- packages/statemanager/src/cache/types.ts | 5 +- packages/statemanager/src/capabilities.ts | 88 ------------------ packages/statemanager/src/rpcStateManager.ts | 29 +++--- .../statemanager/src/simpleStateManager.ts | 11 ++- packages/statemanager/src/stateManager.ts | 92 +++++++++++++++---- .../src/statelessVerkleStateManager.ts | 90 ++++++++++++++---- packages/statemanager/src/types.ts | 21 ++++- .../statemanager/test/cache/account.spec.ts | 3 +- packages/statemanager/test/cache/code.spec.ts | 3 +- .../statemanager/test/cache/storage.spec.ts | 3 +- .../statemanager/test/stateManager.spec.ts | 3 +- .../test/statelessVerkleStateManager.spec.ts | 4 +- 18 files changed, 211 insertions(+), 188 deletions(-) delete mode 100644 packages/statemanager/src/capabilities.ts diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index 8b45786f67..0ddbe76e6c 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -4,10 +4,14 @@ import { DBSetHashToNumber, DBSetTD, } from '@ethereumjs/blockchain' -import { CacheType, ConsensusType, Hardfork } from '@ethereumjs/common' +import { ConsensusType, Hardfork } from '@ethereumjs/common' import { MCLBLS } from '@ethereumjs/evm' import { getGenesis } from '@ethereumjs/genesis' -import { DefaultStateManager, StatelessVerkleStateManager } from '@ethereumjs/statemanager' +import { + CacheType, + DefaultStateManager, + StatelessVerkleStateManager, +} from '@ethereumjs/statemanager' import { createTrie } from '@ethereumjs/trie' import { BIGINT_0, diff --git a/packages/client/src/rpc/modules/eth.ts b/packages/client/src/rpc/modules/eth.ts index 6cdb852b77..36da556bd4 100644 --- a/packages/client/src/rpc/modules/eth.ts +++ b/packages/client/src/rpc/modules/eth.ts @@ -45,8 +45,8 @@ import type { EthProtocol } from '../../net/protocol/index.js' import type { FullEthereumService, Service } from '../../service/index.js' import type { RpcTx } from '../types.js' import type { Block, JsonRpcBlock } from '@ethereumjs/block' -import type { Proof } from '@ethereumjs/common' import type { Log } from '@ethereumjs/evm' +import type { Proof } from '@ethereumjs/statemanager' import type { FeeMarketEIP1559Transaction, LegacyTransaction, diff --git a/packages/common/src/interfaces.ts b/packages/common/src/interfaces.ts index 29c7b259df..1768e417b1 100644 --- a/packages/common/src/interfaces.ts +++ b/packages/common/src/interfaces.ts @@ -209,35 +209,4 @@ export interface StateManagerInterface { */ clearCaches(): void shallowCopy(downlevelCaches?: boolean): StateManagerInterface - - /* - * Cache properties - */ - _accountCache?: Cache - _storageCache?: Cache - _codeCache?: Cache - - _accountCacheSettings?: CacheSettings - _storageCacheSettings?: CacheSettings - _codeCacheSettings?: CacheSettings -} - -/** - * Cache related - */ -export enum CacheType { - LRU = 'lru', - ORDERED_MAP = 'ordered_map', -} - -export type CacheSettings = { - deactivate: boolean - type: CacheType - size: number -} - -interface Cache { - checkpoint(): void - commit(): void - revert(): void } diff --git a/packages/statemanager/src/cache/account.ts b/packages/statemanager/src/cache/account.ts index eedb7ef5c5..216abeb54d 100644 --- a/packages/statemanager/src/cache/account.ts +++ b/packages/statemanager/src/cache/account.ts @@ -1,10 +1,10 @@ -import { CacheType } from '@ethereumjs/common' import { bytesToUnprefixedHex } from '@ethereumjs/util' import { OrderedMap } from '@js-sdsl/ordered-map' import debugDefault from 'debug' import { LRUCache } from 'lru-cache' import { Cache } from './cache.js' +import { CacheType } from './types.js' import type { CacheOpts } from './types.js' import type { Account, Address } from '@ethereumjs/util' diff --git a/packages/statemanager/src/cache/code.ts b/packages/statemanager/src/cache/code.ts index 61a957de62..3aaeb25db8 100644 --- a/packages/statemanager/src/cache/code.ts +++ b/packages/statemanager/src/cache/code.ts @@ -1,10 +1,10 @@ -import { CacheType } from '@ethereumjs/common' import { bytesToUnprefixedHex } from '@ethereumjs/util' import { OrderedMap } from '@js-sdsl/ordered-map' import debugDefault from 'debug' import { LRUCache } from 'lru-cache' import { Cache } from './cache.js' +import { CacheType } from './types.js' import type { CacheOpts } from './types.js' import type { Address } from '@ethereumjs/util' diff --git a/packages/statemanager/src/cache/storage.ts b/packages/statemanager/src/cache/storage.ts index 6c9c153e14..345e74ad3b 100644 --- a/packages/statemanager/src/cache/storage.ts +++ b/packages/statemanager/src/cache/storage.ts @@ -1,10 +1,10 @@ -import { CacheType } from '@ethereumjs/common' import { bytesToUnprefixedHex, hexToBytes } from '@ethereumjs/util' import { OrderedMap } from '@js-sdsl/ordered-map' import debugDefault from 'debug' import { LRUCache } from 'lru-cache' import { Cache } from './cache.js' +import { CacheType } from './types.js' import type { CacheOpts } from './types.js' import type { Address } from '@ethereumjs/util' diff --git a/packages/statemanager/src/cache/types.ts b/packages/statemanager/src/cache/types.ts index 7f283d7c65..0e5b3d40f6 100644 --- a/packages/statemanager/src/cache/types.ts +++ b/packages/statemanager/src/cache/types.ts @@ -1,4 +1,7 @@ -import type { CacheType } from '@ethereumjs/common' +export enum CacheType { + LRU = 'lru', + ORDERED_MAP = 'ordered_map', +} export interface CacheOpts { size: number diff --git a/packages/statemanager/src/capabilities.ts b/packages/statemanager/src/capabilities.ts deleted file mode 100644 index 9b48cd0468..0000000000 --- a/packages/statemanager/src/capabilities.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { type AccountFields, CacheType, type StateManagerInterface } from '@ethereumjs/common' -import { Account } from '@ethereumjs/util' - -import { AccountCache, CodeCache, OriginalStorageCache, StorageCache } from './cache/index.js' - -import type { CacheStateManagerOpts } from './types.js' -import type { Address } from '@ethereumjs/util' - -export function checkpointCaches(stateManager: StateManagerInterface): void { - stateManager._accountCache?.checkpoint() - stateManager._storageCache?.checkpoint() - stateManager._codeCache?.checkpoint() -} - -export function commitCaches(stateManager: StateManagerInterface): void { - stateManager._accountCache?.commit() - stateManager._storageCache?.commit() - stateManager._codeCache?.commit() -} - -export function initializeCaches( - stateManager: StateManagerInterface, - options: CacheStateManagerOpts, -): void { - stateManager.originalStorageCache = new OriginalStorageCache( - stateManager.getStorage.bind(stateManager), - ) - - stateManager._accountCacheSettings = { - deactivate: options.accountCacheOpts?.deactivate ?? false, - type: options.accountCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: options.accountCacheOpts?.size ?? 100000, - } - - if (stateManager._accountCacheSettings.deactivate === false) { - stateManager._accountCache = new AccountCache({ - size: stateManager._accountCacheSettings.size, - type: stateManager._accountCacheSettings.type, - }) - } - - stateManager._storageCacheSettings = { - deactivate: options.storageCacheOpts?.deactivate ?? false, - type: options.storageCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: options.storageCacheOpts?.size ?? 20000, - } - - if (stateManager._storageCacheSettings.deactivate === false) { - stateManager._storageCache = new StorageCache({ - size: stateManager._storageCacheSettings.size, - type: stateManager._storageCacheSettings.type, - }) - } - - stateManager._codeCacheSettings = { - deactivate: - (options.codeCacheOpts?.deactivate === true || options.codeCacheOpts?.size === 0) ?? false, - type: options.codeCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: options.codeCacheOpts?.size ?? 20000, - } - - if (stateManager._codeCacheSettings.deactivate === false) { - stateManager._codeCache = new CodeCache({ - size: stateManager._codeCacheSettings.size, - type: stateManager._codeCacheSettings.type, - }) - } -} - -export async function modifyAccountFields( - stateManager: StateManagerInterface, - address: Address, - accountFields: AccountFields, -): Promise { - const account = (await stateManager.getAccount(address)) ?? new Account() - - account.nonce = accountFields.nonce ?? account.nonce - account.balance = accountFields.balance ?? account.balance - account.storageRoot = accountFields.storageRoot ?? account.storageRoot - account.codeHash = accountFields.codeHash ?? account.codeHash - await stateManager.putAccount(address, account) -} - -export function revertCaches(stateManager: StateManagerInterface): void { - stateManager._accountCache?.revert() - stateManager._storageCache?.revert() - stateManager._codeCache?.revert() -} diff --git a/packages/statemanager/src/rpcStateManager.ts b/packages/statemanager/src/rpcStateManager.ts index 2c6016aac4..198dd25a94 100644 --- a/packages/statemanager/src/rpcStateManager.ts +++ b/packages/statemanager/src/rpcStateManager.ts @@ -1,4 +1,4 @@ -import { CacheType, Common, Mainnet } from '@ethereumjs/common' +import { Common, Mainnet } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { verifyTrieProof } from '@ethereumjs/trie' import { @@ -16,11 +16,10 @@ import { import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import { AccountCache, OriginalStorageCache, StorageCache } from './cache/index.js' -import * as Capabilities from './capabilities.js' +import { AccountCache, CacheType, OriginalStorageCache, StorageCache } from './cache/index.js' -import type { RPCStateManagerOpts } from './index.js' -import type { AccountFields, Proof, StateManagerInterface, StorageDump } from '@ethereumjs/common' +import type { Proof, RPCStateManagerOpts } from './index.js' +import type { AccountFields, StateManagerInterface, StorageDump } from '@ethereumjs/common' import type { Address, PrefixedHexString } from '@ethereumjs/util' import type { Debugger } from 'debug' @@ -28,14 +27,12 @@ const KECCAK256_RLP_EMPTY_ACCOUNT = RLP.encode(new Account().serialize()).slice( export class RPCStateManager implements StateManagerInterface { protected _provider: string + protected _contractCache: Map + protected _storageCache: StorageCache protected _blockTag: string - - _accountCache: AccountCache - _storageCache: StorageCache - _contractCache: Map - + protected _accountCache: AccountCache originalStorageCache: OriginalStorageCache - _debug: Debugger + protected _debug: Debugger protected DEBUG: boolean private keccakFunction: Function public readonly common: Common @@ -320,7 +317,15 @@ export class RPCStateManager implements StateManagerInterface { ), ) } - await Capabilities.modifyAccountFields(this, address, accountFields) + let account = await this.getAccount(address) + if (!account) { + account = new Account() + } + account.nonce = accountFields.nonce ?? account.nonce + account.balance = accountFields.balance ?? account.balance + account.storageRoot = accountFields.storageRoot ?? account.storageRoot + account.codeHash = accountFields.codeHash ?? account.codeHash + await this.putAccount(address, account) } /** diff --git a/packages/statemanager/src/simpleStateManager.ts b/packages/statemanager/src/simpleStateManager.ts index 58d6977a96..8c58c616ba 100644 --- a/packages/statemanager/src/simpleStateManager.ts +++ b/packages/statemanager/src/simpleStateManager.ts @@ -2,7 +2,6 @@ import { Account, bytesToHex } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { OriginalStorageCache } from './cache/originalStorageCache.js' -import * as Capabilities from './capabilities.js' import type { SimpleStateManagerOpts } from './index.js' import type { AccountFields, Common, StateManagerInterface } from '@ethereumjs/common' @@ -79,7 +78,15 @@ export class SimpleStateManager implements StateManagerInterface { } async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - await Capabilities.modifyAccountFields(this, address, accountFields) + let account = await this.getAccount(address) + if (!account) { + account = new Account() + } + account.nonce = accountFields.nonce ?? account.nonce + account.balance = accountFields.balance ?? account.balance + account.storageRoot = accountFields.storageRoot ?? account.storageRoot + account.codeHash = accountFields.codeHash ?? account.codeHash + await this.putAccount(address, account) } async getCode(address: Address): Promise { diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 7daf7e1917..f07ca072d1 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -1,4 +1,4 @@ -import { CacheType, Common, Mainnet } from '@ethereumjs/common' +import { Common, Mainnet } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { Trie, @@ -31,17 +31,22 @@ import { import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import * as Capabilities from './capabilities.js' +import { + AccountCache, + CacheType, + CodeCache, + OriginalStorageCache, + StorageCache, +} from './cache/index.js' import { CODEHASH_PREFIX, type CacheSettings, type DefaultStateManagerOpts } from './index.js' -import type { AccountCache, CodeCache, OriginalStorageCache, StorageCache } from './cache/index.js' +import type { StorageProof } from './index.js' import type { AccountFields, Proof, StateManagerInterface, StorageDump, - StorageProof, StorageRange, } from '@ethereumjs/common' import type { Address, DB, PrefixedHexString } from '@ethereumjs/util' @@ -64,21 +69,20 @@ import type { Debugger } from 'debug' */ export class DefaultStateManager implements StateManagerInterface { protected _debug: Debugger - _accountCache?: AccountCache - _storageCache?: StorageCache - _codeCache?: CodeCache + protected _accountCache?: AccountCache + protected _storageCache?: StorageCache + protected _codeCache?: CodeCache + + originalStorageCache: OriginalStorageCache protected _trie: Trie protected _storageTries: { [key: string]: Trie } protected readonly _prefixCodeHashes: boolean protected readonly _prefixStorageTrieKeys: boolean - - // Non-null assertion necessary to inform TypeScript that these properties are set in the constructor through a helper function - originalStorageCache!: OriginalStorageCache - readonly _accountCacheSettings!: CacheSettings - readonly _storageCacheSettings!: CacheSettings - readonly _codeCacheSettings!: CacheSettings + protected readonly _accountCacheSettings: CacheSettings + protected readonly _storageCacheSettings: CacheSettings + protected readonly _codeCacheSettings: CacheSettings public readonly common: Common @@ -116,10 +120,48 @@ export class DefaultStateManager implements StateManagerInterface { this.keccakFunction = opts.common?.customCrypto.keccak256 ?? keccak256 + this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this)) + this._prefixCodeHashes = opts.prefixCodeHashes ?? true this._prefixStorageTrieKeys = opts.prefixStorageTrieKeys ?? false + this._accountCacheSettings = { + deactivate: + (opts.accountCacheOpts?.deactivate === true || opts.accountCacheOpts?.size === 0) ?? false, + type: opts.accountCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.accountCacheOpts?.size ?? 100000, + } + if (!this._accountCacheSettings.deactivate) { + this._accountCache = new AccountCache({ + size: this._accountCacheSettings.size, + type: this._accountCacheSettings.type, + }) + } - Capabilities.initializeCaches(this, opts) + this._storageCacheSettings = { + deactivate: + (opts.storageCacheOpts?.deactivate === true || opts.storageCacheOpts?.size === 0) ?? false, + type: opts.storageCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.storageCacheOpts?.size ?? 20000, + } + if (!this._storageCacheSettings.deactivate) { + this._storageCache = new StorageCache({ + size: this._storageCacheSettings.size, + type: this._storageCacheSettings.type, + }) + } + + this._codeCacheSettings = { + deactivate: + (opts.codeCacheOpts?.deactivate === true || opts.codeCacheOpts?.size === 0) ?? false, + type: opts.codeCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.codeCacheOpts?.size ?? 20000, + } + if (!this._codeCacheSettings.deactivate) { + this._codeCache = new CodeCache({ + size: this._codeCacheSettings.size, + type: this._codeCacheSettings.type, + }) + } } /** @@ -182,7 +224,15 @@ export class DefaultStateManager implements StateManagerInterface { * @param accountFields - Object containing account fields and values to modify */ async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - await Capabilities.modifyAccountFields(this, address, accountFields) + let account = await this.getAccount(address) + if (!account) { + account = new Account() + } + account.nonce = accountFields.nonce ?? account.nonce + account.balance = accountFields.balance ?? account.balance + account.storageRoot = accountFields.storageRoot ?? account.storageRoot + account.codeHash = accountFields.codeHash ?? account.codeHash + await this.putAccount(address, account) } /** @@ -462,7 +512,9 @@ export class DefaultStateManager implements StateManagerInterface { */ async checkpoint(): Promise { this._trie.checkpoint() - Capabilities.checkpointCaches(this) + this._storageCache?.checkpoint() + this._accountCache?.checkpoint() + this._codeCache?.checkpoint() this._checkpointCount++ } @@ -473,7 +525,9 @@ export class DefaultStateManager implements StateManagerInterface { async commit(): Promise { // setup trie checkpointing await this._trie.commit() - Capabilities.commitCaches(this) + this._storageCache?.commit() + this._accountCache?.commit() + this._codeCache?.commit() this._checkpointCount-- if (this._checkpointCount === 0) { @@ -493,7 +547,9 @@ export class DefaultStateManager implements StateManagerInterface { async revert(): Promise { // setup trie checkpointing await this._trie.revert() - Capabilities.revertCaches(this) + this._storageCache?.revert() + this._accountCache?.revert() + this._codeCache?.revert() this._storageTries = {} diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index cc9b93d15f..4fc1d33211 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -25,16 +25,16 @@ import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { AccessWitness, AccessedStateType, decodeValue } from './accessWitness.js' -import * as Capabilities from './capabilities.js' - import { - type CacheSettings, - type StatelessVerkleStateManagerOpts, - type VerkleState, -} from './index.js' + AccountCache, + CacheType, + CodeCache, + OriginalStorageCache, + StorageCache, +} from './cache/index.js' import type { AccessedStateWithAddress } from './accessWitness.js' -import type { AccountCache, CodeCache, OriginalStorageCache, StorageCache } from './cache/index.js' +import type { CacheSettings, StatelessVerkleStateManagerOpts, VerkleState } from './index.js' import type { DefaultStateManager } from './stateManager.js' import type { AccountFields, Proof, StateManagerInterface } from '@ethereumjs/common' import type { @@ -72,13 +72,12 @@ export class StatelessVerkleStateManager implements StateManagerInterface { _codeCache?: CodeCache _cachedStateRoot?: Uint8Array - verkleCrypto: VerkleCrypto + originalStorageCache: OriginalStorageCache - // Non-null assertion necessary to inform TypeScript that these properties are set in the constructor through a helper function - originalStorageCache!: OriginalStorageCache - readonly _accountCacheSettings!: CacheSettings - readonly _storageCacheSettings!: CacheSettings - readonly _codeCacheSettings!: CacheSettings + verkleCrypto: VerkleCrypto + protected readonly _accountCacheSettings: CacheSettings + protected readonly _storageCacheSettings: CacheSettings + protected readonly _codeCacheSettings: CacheSettings /** * StateManager is run in DEBUG mode (default: false) @@ -113,7 +112,47 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * Instantiate the StateManager interface. */ constructor(opts: StatelessVerkleStateManagerOpts) { - Capabilities.initializeCaches(this, opts) + this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this)) + + this._accountCacheSettings = { + deactivate: opts.accountCacheOpts?.deactivate ?? false, + type: opts.accountCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.accountCacheOpts?.size ?? 100000, + } + + if (!this._accountCacheSettings.deactivate) { + this._accountCache = new AccountCache({ + size: this._accountCacheSettings.size, + type: this._accountCacheSettings.type, + }) + } + + this._storageCacheSettings = { + deactivate: opts.storageCacheOpts?.deactivate ?? false, + type: opts.storageCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.storageCacheOpts?.size ?? 20000, + } + + if (!this._storageCacheSettings.deactivate) { + this._storageCache = new StorageCache({ + size: this._storageCacheSettings.size, + type: this._storageCacheSettings.type, + }) + } + + this._codeCacheSettings = { + deactivate: + (opts.codeCacheOpts?.deactivate === true || opts.codeCacheOpts?.size === 0) ?? false, + type: opts.codeCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.codeCacheOpts?.size ?? 20000, + } + + if (!this._codeCacheSettings.deactivate) { + this._codeCache = new CodeCache({ + size: this._codeCacheSettings.size, + type: this._codeCacheSettings.type, + }) + } this._cachedStateRoot = opts.initialStateRoot @@ -532,7 +571,16 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - await Capabilities.modifyAccountFields(this, address, accountFields) + let account = await this.getAccount(address) + if (!account) { + account = new Account() + } + + account._nonce = accountFields.nonce ?? account._nonce + account._balance = accountFields.balance ?? account._balance + account._storageRoot = accountFields.storageRoot ?? account._storageRoot + account._codeHash = accountFields.codeHash ?? account._codeHash + await this.putAccount(address, account) } getProof(_: Address, __: Uint8Array[] = []): Promise { @@ -733,7 +781,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { */ async checkpoint(): Promise { this._checkpoints.push(this._state) - Capabilities.checkpointCaches(this) + this._accountCache?.checkpoint() + this._storageCache?.checkpoint() + this._codeCache?.checkpoint() } /** @@ -742,7 +792,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { */ async commit(): Promise { this._checkpoints.pop() - Capabilities.commitCaches(this) + this._accountCache!.commit() + this._storageCache?.commit() + this._codeCache?.commit() } // TODO @@ -757,7 +809,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { async revert(): Promise { // setup trie checkpointing this._checkpoints.pop() - Capabilities.revertCaches(this) + this._accountCache?.revert() + this._storageCache?.revert() + this._codeCache?.revert() } /** diff --git a/packages/statemanager/src/types.ts b/packages/statemanager/src/types.ts index 340a21eb3c..da35fe4a85 100644 --- a/packages/statemanager/src/types.ts +++ b/packages/statemanager/src/types.ts @@ -1,7 +1,8 @@ import { type PrefixedHexString, utf8ToBytes } from '@ethereumjs/util' +import type { CacheType } from './cache/index.js' import type { AccessWitness } from './index.js' -import type { CacheType, Common } from '@ethereumjs/common' +import type { Common } from '@ethereumjs/common' import type { Trie } from '@ethereumjs/trie' import type { VerkleCrypto } from '@ethereumjs/util' @@ -61,7 +62,7 @@ interface BaseStateManagerOpts { /** * Cache state manager options (not to be used directly) */ -export interface CacheStateManagerOpts { +interface CacheStateManagerOpts { accountCacheOpts?: CacheOptions storageCacheOpts?: CacheOptions codeCacheOpts?: CacheOptions @@ -136,3 +137,19 @@ export interface EncodedVerkleProof { * misbehaviour in the underlying trie library. */ export const CODEHASH_PREFIX = utf8ToBytes('c') + +export type StorageProof = { + key: PrefixedHexString + proof: PrefixedHexString[] + value: PrefixedHexString +} + +export type Proof = { + address: PrefixedHexString + balance: PrefixedHexString + codeHash: PrefixedHexString + nonce: PrefixedHexString + storageHash: PrefixedHexString + accountProof: PrefixedHexString[] + storageProof: StorageProof[] +} diff --git a/packages/statemanager/test/cache/account.spec.ts b/packages/statemanager/test/cache/account.spec.ts index 4c69073eb8..533f62d095 100644 --- a/packages/statemanager/test/cache/account.spec.ts +++ b/packages/statemanager/test/cache/account.spec.ts @@ -1,8 +1,7 @@ -import { CacheType } from '@ethereumjs/common' import { Account, Address, equalsBytes, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { AccountCache } from '../../src/cache/index.js' +import { AccountCache, CacheType } from '../../src/cache/index.js' import { createAccountWithDefaults } from '../util.js' describe('Account Cache: initialization', () => { diff --git a/packages/statemanager/test/cache/code.spec.ts b/packages/statemanager/test/cache/code.spec.ts index cc1aac0b40..2bbe3eaea7 100644 --- a/packages/statemanager/test/cache/code.spec.ts +++ b/packages/statemanager/test/cache/code.spec.ts @@ -1,8 +1,7 @@ -import { CacheType } from '@ethereumjs/common' import { Address, equalsBytes, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { CodeCache } from '../../src/cache/index.js' +import { CacheType, CodeCache } from '../../src/cache/index.js' describe('Code Cache: initialization', () => { for (const type of [CacheType.LRU, CacheType.ORDERED_MAP]) { diff --git a/packages/statemanager/test/cache/storage.spec.ts b/packages/statemanager/test/cache/storage.spec.ts index dbd9914767..3fcb30d5f6 100644 --- a/packages/statemanager/test/cache/storage.spec.ts +++ b/packages/statemanager/test/cache/storage.spec.ts @@ -1,8 +1,7 @@ -import { CacheType } from '@ethereumjs/common' import { Address, equalsBytes, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { StorageCache } from '../../src/cache/index.js' +import { CacheType, StorageCache } from '../../src/cache/index.js' describe('Storage Cache: initialization', () => { for (const type of [CacheType.LRU, CacheType.ORDERED_MAP]) { diff --git a/packages/statemanager/test/stateManager.spec.ts b/packages/statemanager/test/stateManager.spec.ts index 6aca9047bb..1744973e3b 100644 --- a/packages/statemanager/test/stateManager.spec.ts +++ b/packages/statemanager/test/stateManager.spec.ts @@ -1,4 +1,3 @@ -import { CacheType } from '@ethereumjs/common' import { Trie, createTrie, createTrieFromProof } from '@ethereumjs/trie' import { Account, @@ -15,7 +14,7 @@ import { } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { DefaultStateManager } from '../src/index.js' +import { CacheType, DefaultStateManager } from '../src/index.js' import type { PrefixedHexString } from '@ethereumjs/util' diff --git a/packages/statemanager/test/statelessVerkleStateManager.spec.ts b/packages/statemanager/test/statelessVerkleStateManager.spec.ts index 310fcebd7b..489d594616 100644 --- a/packages/statemanager/test/statelessVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statelessVerkleStateManager.spec.ts @@ -1,5 +1,5 @@ import { createBlock } from '@ethereumjs/block' -import { CacheType, createCommonFromGethGenesis } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { createTxFromSerializedData } from '@ethereumjs/tx' import { Address, @@ -16,7 +16,7 @@ import { import { loadVerkleCrypto } from 'verkle-cryptography-wasm' import { assert, beforeAll, describe, it, test } from 'vitest' -import { StatelessVerkleStateManager } from '../src/index.js' +import { CacheType, StatelessVerkleStateManager } from '../src/index.js' import * as testnetVerkleKaustinen from './testdata/testnetVerkleKaustinen.json' import * as verkleBlockJSON from './testdata/verkleKaustinen6Block72.json' From bd1f237d6a6224d6c22f13ade921485125acabf8 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 7 Aug 2024 14:53:36 -0400 Subject: [PATCH 02/23] statemanager: refactor modifyAccountFields --- packages/statemanager/src/rpcStateManager.ts | 11 ++--------- .../statemanager/src/simpleStateManager.ts | 11 ++--------- packages/statemanager/src/stateManager.ts | 11 ++--------- .../src/statelessVerkleStateManager.ts | 12 ++---------- packages/statemanager/src/util.ts | 18 ++++++++++++++++++ 5 files changed, 26 insertions(+), 37 deletions(-) create mode 100644 packages/statemanager/src/util.ts diff --git a/packages/statemanager/src/rpcStateManager.ts b/packages/statemanager/src/rpcStateManager.ts index 198dd25a94..98de1aca5b 100644 --- a/packages/statemanager/src/rpcStateManager.ts +++ b/packages/statemanager/src/rpcStateManager.ts @@ -17,6 +17,7 @@ import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { AccountCache, CacheType, OriginalStorageCache, StorageCache } from './cache/index.js' +import { modifyAccountFields } from './util.js' import type { Proof, RPCStateManagerOpts } from './index.js' import type { AccountFields, StateManagerInterface, StorageDump } from '@ethereumjs/common' @@ -317,15 +318,7 @@ export class RPCStateManager implements StateManagerInterface { ), ) } - let account = await this.getAccount(address) - if (!account) { - account = new Account() - } - account.nonce = accountFields.nonce ?? account.nonce - account.balance = accountFields.balance ?? account.balance - account.storageRoot = accountFields.storageRoot ?? account.storageRoot - account.codeHash = accountFields.codeHash ?? account.codeHash - await this.putAccount(address, account) + return modifyAccountFields(this, address, accountFields) } /** diff --git a/packages/statemanager/src/simpleStateManager.ts b/packages/statemanager/src/simpleStateManager.ts index 8c58c616ba..e663d62648 100644 --- a/packages/statemanager/src/simpleStateManager.ts +++ b/packages/statemanager/src/simpleStateManager.ts @@ -2,6 +2,7 @@ import { Account, bytesToHex } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { OriginalStorageCache } from './cache/originalStorageCache.js' +import { modifyAccountFields } from './util.js' import type { SimpleStateManagerOpts } from './index.js' import type { AccountFields, Common, StateManagerInterface } from '@ethereumjs/common' @@ -78,15 +79,7 @@ export class SimpleStateManager implements StateManagerInterface { } async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - let account = await this.getAccount(address) - if (!account) { - account = new Account() - } - account.nonce = accountFields.nonce ?? account.nonce - account.balance = accountFields.balance ?? account.balance - account.storageRoot = accountFields.storageRoot ?? account.storageRoot - account.codeHash = accountFields.codeHash ?? account.codeHash - await this.putAccount(address, account) + return modifyAccountFields(this, address, accountFields) } async getCode(address: Address): Promise { diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index f07ca072d1..0417a0287a 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -38,6 +38,7 @@ import { OriginalStorageCache, StorageCache, } from './cache/index.js' +import { modifyAccountFields } from './util.js' import { CODEHASH_PREFIX, type CacheSettings, type DefaultStateManagerOpts } from './index.js' @@ -224,15 +225,7 @@ export class DefaultStateManager implements StateManagerInterface { * @param accountFields - Object containing account fields and values to modify */ async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - let account = await this.getAccount(address) - if (!account) { - account = new Account() - } - account.nonce = accountFields.nonce ?? account.nonce - account.balance = accountFields.balance ?? account.balance - account.storageRoot = accountFields.storageRoot ?? account.storageRoot - account.codeHash = accountFields.codeHash ?? account.codeHash - await this.putAccount(address, account) + return modifyAccountFields(this, address, accountFields) } /** diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 4fc1d33211..361c69f9d4 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -32,6 +32,7 @@ import { OriginalStorageCache, StorageCache, } from './cache/index.js' +import { modifyAccountFields } from './util.js' import type { AccessedStateWithAddress } from './accessWitness.js' import type { CacheSettings, StatelessVerkleStateManagerOpts, VerkleState } from './index.js' @@ -571,16 +572,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - let account = await this.getAccount(address) - if (!account) { - account = new Account() - } - - account._nonce = accountFields.nonce ?? account._nonce - account._balance = accountFields.balance ?? account._balance - account._storageRoot = accountFields.storageRoot ?? account._storageRoot - account._codeHash = accountFields.codeHash ?? account._codeHash - await this.putAccount(address, account) + return modifyAccountFields(this, address, accountFields) } getProof(_: Address, __: Uint8Array[] = []): Promise { diff --git a/packages/statemanager/src/util.ts b/packages/statemanager/src/util.ts new file mode 100644 index 0000000000..3628ab842d --- /dev/null +++ b/packages/statemanager/src/util.ts @@ -0,0 +1,18 @@ +import { Account } from '@ethereumjs/util' + +import type { AccountFields, StateManagerInterface } from '@ethereumjs/common' +import type { Address } from '@ethereumjs/util' + +export async function modifyAccountFields( + stateManager: StateManagerInterface, + address: Address, + accountFields: AccountFields, +): Promise { + const account = (await stateManager.getAccount(address)) ?? new Account() + + account.nonce = accountFields.nonce ?? account.nonce + account.balance = accountFields.balance ?? account.balance + account.storageRoot = accountFields.storageRoot ?? account.storageRoot + account.codeHash = accountFields.codeHash ?? account.codeHash + await stateManager.putAccount(address, account) +} From d5aaf300025c130157e04fa04840b5bec70772b8 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Wed, 7 Aug 2024 14:55:43 -0400 Subject: [PATCH 03/23] statemanager: adjust return statements --- packages/statemanager/src/rpcStateManager.ts | 2 +- packages/statemanager/src/simpleStateManager.ts | 2 +- packages/statemanager/src/stateManager.ts | 2 +- packages/statemanager/src/statelessVerkleStateManager.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/statemanager/src/rpcStateManager.ts b/packages/statemanager/src/rpcStateManager.ts index 98de1aca5b..d551f3dfda 100644 --- a/packages/statemanager/src/rpcStateManager.ts +++ b/packages/statemanager/src/rpcStateManager.ts @@ -318,7 +318,7 @@ export class RPCStateManager implements StateManagerInterface { ), ) } - return modifyAccountFields(this, address, accountFields) + await modifyAccountFields(this, address, accountFields) } /** diff --git a/packages/statemanager/src/simpleStateManager.ts b/packages/statemanager/src/simpleStateManager.ts index e663d62648..30bf009641 100644 --- a/packages/statemanager/src/simpleStateManager.ts +++ b/packages/statemanager/src/simpleStateManager.ts @@ -79,7 +79,7 @@ export class SimpleStateManager implements StateManagerInterface { } async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - return modifyAccountFields(this, address, accountFields) + await modifyAccountFields(this, address, accountFields) } async getCode(address: Address): Promise { diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 0417a0287a..90e353a310 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -225,7 +225,7 @@ export class DefaultStateManager implements StateManagerInterface { * @param accountFields - Object containing account fields and values to modify */ async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - return modifyAccountFields(this, address, accountFields) + await modifyAccountFields(this, address, accountFields) } /** diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 361c69f9d4..263e60ca45 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -572,7 +572,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { - return modifyAccountFields(this, address, accountFields) + await modifyAccountFields(this, address, accountFields) } getProof(_: Address, __: Uint8Array[] = []): Promise { From 533fa8ca7c4e5841aa190c692384fc983090dfea Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 06:14:25 -0400 Subject: [PATCH 04/23] statemanager: refactor separate caches into caches class --- packages/statemanager/src/cache/account.ts | 4 +- packages/statemanager/src/cache/caches.ts | 61 +++++++ packages/statemanager/src/cache/index.ts | 1 + packages/statemanager/src/cache/types.ts | 49 ++++++ packages/statemanager/src/stateManager.ts | 161 +++++++----------- .../src/statelessVerkleStateManager.ts | 148 ++++++---------- packages/statemanager/src/types.ts | 70 ++------ 7 files changed, 233 insertions(+), 261 deletions(-) create mode 100644 packages/statemanager/src/cache/caches.ts diff --git a/packages/statemanager/src/cache/account.ts b/packages/statemanager/src/cache/account.ts index 216abeb54d..9837178873 100644 --- a/packages/statemanager/src/cache/account.ts +++ b/packages/statemanager/src/cache/account.ts @@ -68,14 +68,14 @@ export class AccountCache extends Cache { put( address: Address, account: Account | undefined, - couldBeParitalAccount: boolean = false, + couldBePartialAccount: boolean = false, ): void { const addressHex = bytesToUnprefixedHex(address.bytes) this._saveCachePreState(addressHex) const elem = { accountRLP: account !== undefined - ? couldBeParitalAccount + ? couldBePartialAccount ? account.serializeWithPartialInfo() : account.serialize() : undefined, diff --git a/packages/statemanager/src/cache/caches.ts b/packages/statemanager/src/cache/caches.ts new file mode 100644 index 0000000000..5295079663 --- /dev/null +++ b/packages/statemanager/src/cache/caches.ts @@ -0,0 +1,61 @@ +import { AccountCache } from './account.js' +import { CodeCache } from './code.js' +import { StorageCache } from './storage.js' +import { type CacheSettings, CacheType, type CachesStateManagerOpts } from './types.js' + +export class Caches { + account?: AccountCache + code?: CodeCache + storage?: StorageCache + + settings: Record<'account' | 'code' | 'storage', CacheSettings> + + constructor(opts: CachesStateManagerOpts = {}) { + const accountSettings = { + deactivate: + (opts.accountCacheOpts?.deactivate === true || opts.accountCacheOpts?.size === 0) ?? false, + type: opts.accountCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.accountCacheOpts?.size ?? 100000, + } + const storageSettings = { + deactivate: + (opts.storageCacheOpts?.deactivate === true || opts.storageCacheOpts?.size === 0) ?? false, + type: opts.storageCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.storageCacheOpts?.size ?? 20000, + } + + const codeSettings = { + deactivate: + (opts.codeCacheOpts?.deactivate === true || opts.codeCacheOpts?.size === 0) ?? false, + type: opts.codeCacheOpts?.type ?? CacheType.ORDERED_MAP, + size: opts.codeCacheOpts?.size ?? 20000, + } + + this.settings = { + account: accountSettings, + code: codeSettings, + storage: storageSettings, + } + + if (this.settings.account.deactivate === false) { + this.account = new AccountCache({ + size: this.settings.account.size, + type: this.settings.account.type, + }) + } + + if (this.settings.storage.deactivate === false) { + this.storage = new StorageCache({ + size: this.settings.storage.size, + type: this.settings.storage.type, + }) + } + + if (this.settings.code.deactivate === false) { + this.code = new CodeCache({ + size: this.settings.code.size, + type: this.settings.code.type, + }) + } + } +} diff --git a/packages/statemanager/src/cache/index.ts b/packages/statemanager/src/cache/index.ts index e19c3405e7..799b350c1b 100644 --- a/packages/statemanager/src/cache/index.ts +++ b/packages/statemanager/src/cache/index.ts @@ -1,4 +1,5 @@ export * from './account.js' +export * from './caches.js' export * from './code.js' export * from './originalStorageCache.js' export * from './storage.js' diff --git a/packages/statemanager/src/cache/types.ts b/packages/statemanager/src/cache/types.ts index 0e5b3d40f6..2208d109be 100644 --- a/packages/statemanager/src/cache/types.ts +++ b/packages/statemanager/src/cache/types.ts @@ -7,3 +7,52 @@ export interface CacheOpts { size: number type: CacheType } + +export type CacheSettings = { + deactivate: boolean + type: CacheType + size: number +} + +export interface CacheStateManagerOpts { + /** + * Allows for cache deactivation + * + * Depending on the use case and underlying datastore (and eventual concurrent cache + * mechanisms there), usage with or without cache can be faster + * + * Default: false + */ + deactivate?: boolean + + /** + * Cache type to use. + * + * Available options: + * + * ORDERED_MAP: Cache with no fixed upper bound and dynamic allocation, + * use for dynamic setups like testing or similar. + * + * LRU: LRU cache with pre-allocation of memory and a fixed size. + * Use for larger and more persistent caches. + */ + type?: CacheType + + /** + * Size of the cache (only for LRU cache) + * + * Default: 100000 (account cache) / 20000 (storage cache) / 20000 (code cache) + * + * Note: the cache/trie interplay mechanism is designed in a way that + * the theoretical number of max modified accounts between two flush operations + * should be smaller than the cache size, otherwise the cache will "forget" the + * old modifications resulting in an incomplete set of trie-flushed accounts. + */ + size?: number +} + +export interface CachesStateManagerOpts { + accountCacheOpts?: CacheStateManagerOpts + codeCacheOpts?: CacheStateManagerOpts + storageCacheOpts?: CacheStateManagerOpts +} diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 90e353a310..a87f63a49f 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -31,16 +31,10 @@ import { import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import { - AccountCache, - CacheType, - CodeCache, - OriginalStorageCache, - StorageCache, -} from './cache/index.js' +import { CacheType, OriginalStorageCache } from './cache/index.js' import { modifyAccountFields } from './util.js' -import { CODEHASH_PREFIX, type CacheSettings, type DefaultStateManagerOpts } from './index.js' +import { CODEHASH_PREFIX, Caches, type DefaultStateManagerOpts } from './index.js' import type { StorageProof } from './index.js' import type { @@ -70,9 +64,7 @@ import type { Debugger } from 'debug' */ export class DefaultStateManager implements StateManagerInterface { protected _debug: Debugger - protected _accountCache?: AccountCache - protected _storageCache?: StorageCache - protected _codeCache?: CodeCache + protected _caches: Caches originalStorageCache: OriginalStorageCache @@ -81,9 +73,6 @@ export class DefaultStateManager implements StateManagerInterface { protected readonly _prefixCodeHashes: boolean protected readonly _prefixStorageTrieKeys: boolean - protected readonly _accountCacheSettings: CacheSettings - protected readonly _storageCacheSettings: CacheSettings - protected readonly _codeCacheSettings: CacheSettings public readonly common: Common @@ -125,44 +114,8 @@ export class DefaultStateManager implements StateManagerInterface { this._prefixCodeHashes = opts.prefixCodeHashes ?? true this._prefixStorageTrieKeys = opts.prefixStorageTrieKeys ?? false - this._accountCacheSettings = { - deactivate: - (opts.accountCacheOpts?.deactivate === true || opts.accountCacheOpts?.size === 0) ?? false, - type: opts.accountCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.accountCacheOpts?.size ?? 100000, - } - if (!this._accountCacheSettings.deactivate) { - this._accountCache = new AccountCache({ - size: this._accountCacheSettings.size, - type: this._accountCacheSettings.type, - }) - } - this._storageCacheSettings = { - deactivate: - (opts.storageCacheOpts?.deactivate === true || opts.storageCacheOpts?.size === 0) ?? false, - type: opts.storageCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.storageCacheOpts?.size ?? 20000, - } - if (!this._storageCacheSettings.deactivate) { - this._storageCache = new StorageCache({ - size: this._storageCacheSettings.size, - type: this._storageCacheSettings.type, - }) - } - - this._codeCacheSettings = { - deactivate: - (opts.codeCacheOpts?.deactivate === true || opts.codeCacheOpts?.size === 0) ?? false, - type: opts.codeCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.codeCacheOpts?.size ?? 20000, - } - if (!this._codeCacheSettings.deactivate) { - this._codeCache = new CodeCache({ - size: this._codeCacheSettings.size, - type: this._codeCacheSettings.type, - }) - } + this._caches = new Caches(opts.cachesOpts) } /** @@ -170,8 +123,8 @@ export class DefaultStateManager implements StateManagerInterface { * @param address - Address of the `account` to get */ async getAccount(address: Address): Promise { - if (!this._accountCacheSettings.deactivate) { - const elem = this._accountCache!.get(address) + if (!this._caches.settings.account.deactivate) { + const elem = this._caches.account!.get(address) if (elem !== undefined) { return elem.accountRLP !== undefined ? createAccountFromRLP(elem.accountRLP) : undefined } @@ -182,7 +135,7 @@ export class DefaultStateManager implements StateManagerInterface { if (this.DEBUG) { this._debug(`Get account ${address} from DB (${account ? 'exists' : 'non-existent'})`) } - this._accountCache?.put(address, account) + this._caches.account?.put(address, account) return account } @@ -201,7 +154,7 @@ export class DefaultStateManager implements StateManagerInterface { }`, ) } - if (this._accountCacheSettings.deactivate) { + if (this._caches.settings.account.deactivate) { const trie = this._trie if (account !== undefined) { await trie.put(address.bytes, account.serialize()) @@ -210,9 +163,9 @@ export class DefaultStateManager implements StateManagerInterface { } } else { if (account !== undefined) { - this._accountCache!.put(address, account) + this._caches.account!.put(address, account) } else { - this._accountCache!.del(address) + this._caches.account!.del(address) } } } @@ -237,15 +190,15 @@ export class DefaultStateManager implements StateManagerInterface { this._debug(`Delete account ${address}`) } - this._codeCache?.del(address) + this._caches.code?.del(address) - if (this._accountCacheSettings.deactivate) { + if (this._caches.settings.account.deactivate) { await this._trie.del(address.bytes) } else { - this._accountCache!.del(address) + this._caches.account!.del(address) } - if (!this._storageCacheSettings.deactivate) { - this._storageCache?.clearStorage(address) + if (!this._caches.settings.storage.deactivate) { + this._caches.storage?.clearStorage(address) } } @@ -256,7 +209,7 @@ export class DefaultStateManager implements StateManagerInterface { * @param value - The value of the `code` */ async putCode(address: Address, value: Uint8Array): Promise { - this._codeCache?.put(address, value) + this._caches.code?.put(address, value) const codeHash = this.keccakFunction(value) if (this.DEBUG) { @@ -276,8 +229,8 @@ export class DefaultStateManager implements StateManagerInterface { * Returns an empty `Uint8Array` if the account has no associated code. */ async getCode(address: Address): Promise { - if (!this._codeCacheSettings.deactivate) { - const elem = this._codeCache?.get(address) + if (!this._caches.settings.code.deactivate) { + const elem = this._caches.code?.get(address) if (elem !== undefined) { return elem.code ?? new Uint8Array(0) } @@ -294,8 +247,8 @@ export class DefaultStateManager implements StateManagerInterface { : account.codeHash const code = (await this._trie.database().get(key)) ?? new Uint8Array(0) - if (!this._codeCacheSettings.deactivate) { - this._codeCache!.put(address, code) + if (!this._caches.settings.code.deactivate) { + this._caches.code!.put(address, code) } return code } @@ -376,8 +329,8 @@ export class DefaultStateManager implements StateManagerInterface { if (key.length !== 32) { throw new Error('Storage key must be 32 bytes long') } - if (!this._storageCacheSettings.deactivate) { - const value = this._storageCache!.get(address, key) + if (!this._caches.settings.storage.deactivate) { + const value = this._caches.storage!.get(address, key) if (value !== undefined) { const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array return decoded @@ -390,8 +343,8 @@ export class DefaultStateManager implements StateManagerInterface { } const trie = this._getStorageTrie(address, account) const value = await trie.get(key) - if (!this._storageCacheSettings.deactivate) { - this._storageCache?.put(address, key, value ?? hexToBytes('0x80')) + if (!this._caches.settings.storage.deactivate) { + this._caches.storage?.put(address, key, value ?? hexToBytes('0x80')) } const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array return decoded @@ -474,9 +427,9 @@ export class DefaultStateManager implements StateManagerInterface { } value = unpadBytes(value) - if (!this._storageCacheSettings.deactivate) { + if (!this._caches.settings.storage.deactivate) { const encodedValue = RLP.encode(value) - this._storageCache!.put(address, key, encodedValue) + this._caches.storage!.put(address, key, encodedValue) } else { await this._writeContractStorage(address, account, key, value) } @@ -491,7 +444,7 @@ export class DefaultStateManager implements StateManagerInterface { if (!account) { account = new Account() } - this._storageCache?.clearStorage(address) + this._caches.storage?.clearStorage(address) await this._modifyContractStorage(address, account, (storageTrie, done) => { storageTrie.root(storageTrie.EMPTY_TRIE_ROOT) done() @@ -505,9 +458,9 @@ export class DefaultStateManager implements StateManagerInterface { */ async checkpoint(): Promise { this._trie.checkpoint() - this._storageCache?.checkpoint() - this._accountCache?.checkpoint() - this._codeCache?.checkpoint() + this._caches.storage?.checkpoint() + this._caches.account?.checkpoint() + this._caches.code?.checkpoint() this._checkpointCount++ } @@ -518,9 +471,9 @@ export class DefaultStateManager implements StateManagerInterface { async commit(): Promise { // setup trie checkpointing await this._trie.commit() - this._storageCache?.commit() - this._accountCache?.commit() - this._codeCache?.commit() + this._caches.storage?.commit() + this._caches.account?.commit() + this._caches.code?.commit() this._checkpointCount-- if (this._checkpointCount === 0) { @@ -540,9 +493,9 @@ export class DefaultStateManager implements StateManagerInterface { async revert(): Promise { // setup trie checkpointing await this._trie.revert() - this._storageCache?.revert() - this._accountCache?.revert() - this._codeCache?.revert() + this._caches.storage?.revert() + this._caches.account?.revert() + this._caches.code?.revert() this._storageTries = {} @@ -558,8 +511,8 @@ export class DefaultStateManager implements StateManagerInterface { * Writes all cache items to the trie */ async flush(): Promise { - if (!this._codeCacheSettings.deactivate) { - const items = this._codeCache!.flush() + if (!this._caches.settings.code.deactivate) { + const items = this._caches.code!.flush() for (const item of items) { const addr = createAddressFromString(`0x${item[0]}`) @@ -580,8 +533,8 @@ export class DefaultStateManager implements StateManagerInterface { await this.modifyAccountFields(addr, { codeHash }) } } - if (!this._storageCacheSettings.deactivate) { - const items = this._storageCache!.flush() + if (!this._caches.settings.storage.deactivate) { + const items = this._caches.storage!.flush() for (const item of items) { const address = createAddressFromString(`0x${item[0]}`) const keyHex = item[1] @@ -595,8 +548,8 @@ export class DefaultStateManager implements StateManagerInterface { } } } - if (!this._accountCacheSettings.deactivate) { - const items = this._accountCache!.flush() + if (!this._caches.settings.account.deactivate) { + const items = this._caches.account!.flush() for (const item of items) { const addressHex = item[0] const addressBytes = unprefixedHexToBytes(addressHex) @@ -857,14 +810,14 @@ export class DefaultStateManager implements StateManagerInterface { } this._trie.root(stateRoot) - if (this._accountCache !== undefined && clearCache) { - this._accountCache.clear() + if (this._caches.account !== undefined && clearCache) { + this._caches.account.clear() } - if (this._storageCache !== undefined && clearCache) { - this._storageCache.clear() + if (this._caches.storage !== undefined && clearCache) { + this._caches.storage.clear() } - if (this._codeCache !== undefined && clearCache) { - this._codeCache!.clear() + if (this._caches.code !== undefined && clearCache) { + this._caches.code!.clear() } this._storageTries = {} } @@ -1004,16 +957,16 @@ export class DefaultStateManager implements StateManagerInterface { const trie = this._trie.shallowCopy(false, { cacheSize }) const prefixCodeHashes = this._prefixCodeHashes const prefixStorageTrieKeys = this._prefixStorageTrieKeys - let accountCacheOpts = { ...this._accountCacheSettings } - if (downlevelCaches && !this._accountCacheSettings.deactivate) { + let accountCacheOpts = { ...this._caches.settings.account } + if (downlevelCaches && !this._caches.settings.account.deactivate) { accountCacheOpts = { ...accountCacheOpts, type: CacheType.ORDERED_MAP } } - let storageCacheOpts = { ...this._storageCacheSettings } - if (downlevelCaches && !this._storageCacheSettings.deactivate) { + let storageCacheOpts = { ...this._caches.settings.storage } + if (downlevelCaches && !this._caches.settings.storage.deactivate) { storageCacheOpts = { ...storageCacheOpts, type: CacheType.ORDERED_MAP } } - let codeCacheOpts = { ...this._codeCacheSettings } - if (!this._codeCacheSettings.deactivate) { + let codeCacheOpts = { ...this._caches.settings.code } + if (!this._caches.settings.code.deactivate) { codeCacheOpts = { ...codeCacheOpts, type: CacheType.ORDERED_MAP } } @@ -1032,9 +985,9 @@ export class DefaultStateManager implements StateManagerInterface { * Clears all underlying caches */ clearCaches() { - this._accountCache?.clear() - this._storageCache?.clear() - this._codeCache?.clear() + this._caches.account?.clear() + this._caches.storage?.clear() + this._caches.code?.clear() } /** diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 263e60ca45..1f7bd58228 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -25,17 +25,11 @@ import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { AccessWitness, AccessedStateType, decodeValue } from './accessWitness.js' -import { - AccountCache, - CacheType, - CodeCache, - OriginalStorageCache, - StorageCache, -} from './cache/index.js' +import { Caches, OriginalStorageCache } from './cache/index.js' import { modifyAccountFields } from './util.js' import type { AccessedStateWithAddress } from './accessWitness.js' -import type { CacheSettings, StatelessVerkleStateManagerOpts, VerkleState } from './index.js' +import type { StatelessVerkleStateManagerOpts, VerkleState } from './index.js' import type { DefaultStateManager } from './stateManager.js' import type { AccountFields, Proof, StateManagerInterface } from '@ethereumjs/common' import type { @@ -68,17 +62,13 @@ const ZEROVALUE = '0x00000000000000000000000000000000000000000000000000000000000 * */ export class StatelessVerkleStateManager implements StateManagerInterface { - _accountCache?: AccountCache - _storageCache?: StorageCache - _codeCache?: CodeCache _cachedStateRoot?: Uint8Array originalStorageCache: OriginalStorageCache verkleCrypto: VerkleCrypto - protected readonly _accountCacheSettings: CacheSettings - protected readonly _storageCacheSettings: CacheSettings - protected readonly _codeCacheSettings: CacheSettings + + protected _caches: Caches /** * StateManager is run in DEBUG mode (default: false) @@ -115,45 +105,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { constructor(opts: StatelessVerkleStateManagerOpts) { this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this)) - this._accountCacheSettings = { - deactivate: opts.accountCacheOpts?.deactivate ?? false, - type: opts.accountCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.accountCacheOpts?.size ?? 100000, - } - - if (!this._accountCacheSettings.deactivate) { - this._accountCache = new AccountCache({ - size: this._accountCacheSettings.size, - type: this._accountCacheSettings.type, - }) - } - - this._storageCacheSettings = { - deactivate: opts.storageCacheOpts?.deactivate ?? false, - type: opts.storageCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.storageCacheOpts?.size ?? 20000, - } - - if (!this._storageCacheSettings.deactivate) { - this._storageCache = new StorageCache({ - size: this._storageCacheSettings.size, - type: this._storageCacheSettings.type, - }) - } - - this._codeCacheSettings = { - deactivate: - (opts.codeCacheOpts?.deactivate === true || opts.codeCacheOpts?.size === 0) ?? false, - type: opts.codeCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.codeCacheOpts?.size ?? 20000, - } - - if (!this._codeCacheSettings.deactivate) { - this._codeCache = new CodeCache({ - size: this._codeCacheSettings.size, - type: this._codeCacheSettings.type, - }) - } + this._caches = new Caches(opts.cachesOpts) this._cachedStateRoot = opts.initialStateRoot @@ -270,7 +222,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`putCode address=${address.toString()} value=${short(value)}`) } - this._codeCache?.put(address, value) + this._caches.code?.put(address, value) const codeHash = keccak256(value) if (KECCAK256_NULL === codeHash) { // If the code hash is the null hash, no code has to be stored @@ -294,8 +246,8 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`getCode address=${address.toString()}`) } - if (!this._codeCacheSettings.deactivate) { - const elem = this._codeCache?.get(address) + if (!this._caches.settings.code.deactivate) { + const elem = this._caches.code?.get(address) if (elem !== undefined) { return elem.code ?? new Uint8Array(0) } @@ -341,16 +293,16 @@ export class StatelessVerkleStateManager implements StateManagerInterface { // Return accessedCode where only accessed code has been copied const contactCode = accessedCode.slice(0, codeSize) - if (!this._codeCacheSettings.deactivate) { - this._codeCache?.put(address, contactCode) + if (!this._caches.settings.code.deactivate) { + this._caches.code?.put(address, contactCode) } return contactCode } async getCodeSize(address: Address): Promise { - if (!this._accountCacheSettings.deactivate) { - const elem = this._accountCache!.get(address) + if (!this._caches.settings.account.deactivate) { + const elem = this._caches.account!.get(address) if (elem !== undefined) { const account = elem.accountRLP !== undefined ? createPartialAccountFromRLP(elem.accountRLP) : undefined @@ -381,8 +333,8 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * If this does not exist an empty `Uint8Array` is returned. */ async getStorage(address: Address, key: Uint8Array): Promise { - if (!this._storageCacheSettings.deactivate) { - const value = this._storageCache!.get(address, key) + if (!this._caches.settings.storage.deactivate) { + const value = this._caches.storage!.get(address, key) if (value !== undefined) { return value } @@ -395,8 +347,8 @@ export class StatelessVerkleStateManager implements StateManagerInterface { ) const storageValue = toBytes(this._state[bytesToHex(storageKey)]) - if (!this._storageCacheSettings.deactivate) { - this._storageCache?.put(address, key, storageValue ?? hexToBytes('0x80')) + if (!this._caches.settings.storage.deactivate) { + this._caches.storage?.put(address, key, storageValue ?? hexToBytes('0x80')) } return storageValue @@ -410,8 +362,8 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * @param value - Value to set at `key` for account corresponding to `address`. Cannot be more than 32 bytes. Leading zeros are stripped. If it is a empty or filled with zeros, deletes the value. */ async putStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise { - if (!this._storageCacheSettings.deactivate) { - this._storageCache!.put(address, key, value) + if (!this._caches.settings.storage.deactivate) { + this._caches.storage!.put(address, key, value) } else { // TODO: Consider refactoring this in a writeContractStorage function? Like in stateManager.ts const storageKey = await getVerkleTreeKeyForStorageSlot( @@ -432,14 +384,14 @@ export class StatelessVerkleStateManager implements StateManagerInterface { async clearStorage(address: Address): Promise { const stem = getVerkleStem(this.verkleCrypto, address, 0) const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash) - this._storageCache?.clearStorage(address) + this._caches.storage?.clearStorage(address) // Update codeHash to `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` this._state[bytesToHex(codeHashKey)] = KECCAK256_NULL_S } async getAccount(address: Address): Promise { - if (!this._accountCacheSettings.deactivate) { - const elem = this._accountCache!.get(address) + if (!this._caches.settings.account.deactivate) { + const elem = this._caches.account!.get(address) if (elem !== undefined) { return elem.accountRLP !== undefined ? createPartialAccountFromRLP(elem.accountRLP) @@ -521,8 +473,8 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`getAccount address=${address.toString()} stem=${short(stem)}`) } - if (!this._accountCacheSettings.deactivate) { - this._accountCache?.put(address, account, true) + if (!this._caches.settings.account.deactivate) { + this._caches.account?.put(address, account, true) } return account @@ -533,7 +485,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`putAccount address=${address.toString()}`) } - if (this._accountCacheSettings.deactivate) { + if (this._caches.settings.account.deactivate) { const stem = getVerkleStem(this.verkleCrypto, address, 0) const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance) const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce) @@ -547,9 +499,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { this._state[bytesToHex(codeHashKey)] = bytesToHex(account.codeHash) } else { if (account !== undefined) { - this._accountCache!.put(address, account, true) + this._caches.account!.put(address, account, true) } else { - this._accountCache!.del(address) + this._caches.account!.del(address) } } } @@ -563,11 +515,11 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`Delete account ${address}`) } - this._codeCache?.del(address) - this._accountCache!.del(address) + this._caches.code?.del(address) + this._caches.account!.del(address) - if (!this._storageCacheSettings.deactivate) { - this._storageCache?.clearStorage(address) + if (!this._caches.settings.storage.deactivate) { + this._caches.storage?.clearStorage(address) } } @@ -682,7 +634,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { const { address, type } = accessedState switch (type) { case AccessedStateType.Version: { - const encodedAccount = this._accountCache?.get(address)?.accountRLP + const encodedAccount = this._caches.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -691,7 +643,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { return ZEROVALUE } case AccessedStateType.Balance: { - const encodedAccount = this._accountCache?.get(address)?.accountRLP + const encodedAccount = this._caches.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -701,7 +653,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } case AccessedStateType.Nonce: { - const encodedAccount = this._accountCache?.get(address)?.accountRLP + const encodedAccount = this._caches.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -710,7 +662,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } case AccessedStateType.CodeHash: { - const encodedAccount = this._accountCache?.get(address)?.accountRLP + const encodedAccount = this._caches.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -718,10 +670,10 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } case AccessedStateType.CodeSize: { - const codeSize = this._codeCache?.get(address)?.code?.length + const codeSize = this._caches.code?.get(address)?.code?.length if (codeSize === undefined) { // it could be an EOA lets check for that - const encodedAccount = this._accountCache?.get(address)?.accountRLP + const encodedAccount = this._caches.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -741,7 +693,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { case AccessedStateType.Code: { const { codeOffset } = accessedState - const code = this._codeCache?.get(address)?.code + const code = this._caches.code?.get(address)?.code if (code === undefined) { return null } @@ -757,7 +709,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { const { slot } = accessedState const key = setLengthLeft(bigIntToBytes(slot), 32) - const storage = this._storageCache?.get(address, key) + const storage = this._caches.storage?.get(address, key) if (storage === undefined) { return null } @@ -773,9 +725,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { */ async checkpoint(): Promise { this._checkpoints.push(this._state) - this._accountCache?.checkpoint() - this._storageCache?.checkpoint() - this._codeCache?.checkpoint() + this._caches.account?.checkpoint() + this._caches.storage?.checkpoint() + this._caches.code?.checkpoint() } /** @@ -784,9 +736,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { */ async commit(): Promise { this._checkpoints.pop() - this._accountCache!.commit() - this._storageCache?.commit() - this._codeCache?.commit() + this._caches.account!.commit() + this._caches.storage?.commit() + this._caches.code?.commit() } // TODO @@ -801,9 +753,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { async revert(): Promise { // setup trie checkpointing this._checkpoints.pop() - this._accountCache?.revert() - this._storageCache?.revert() - this._codeCache?.revert() + this._caches.account?.revert() + this._caches.storage?.revert() + this._caches.code?.revert() } /** @@ -837,9 +789,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * Clears all underlying caches */ clearCaches() { - this._accountCache?.clear() - this._codeCache?.clear() - this._storageCache?.clear() + this._caches.account?.clear() + this._caches.code?.clear() + this._caches.storage?.clear() } // TODO: Removing this causes a Kaustinen6 test in client to fail diff --git a/packages/statemanager/src/types.ts b/packages/statemanager/src/types.ts index da35fe4a85..58ab9f991d 100644 --- a/packages/statemanager/src/types.ts +++ b/packages/statemanager/src/types.ts @@ -1,54 +1,11 @@ import { type PrefixedHexString, utf8ToBytes } from '@ethereumjs/util' -import type { CacheType } from './cache/index.js' +import type { CachesStateManagerOpts } from './cache/types.js' import type { AccessWitness } from './index.js' import type { Common } from '@ethereumjs/common' import type { Trie } from '@ethereumjs/trie' import type { VerkleCrypto } from '@ethereumjs/util' -type CacheOptions = { - /** - * Allows for cache deactivation - * - * Depending on the use case and underlying datastore (and eventual concurrent cache - * mechanisms there), usage with or without cache can be faster - * - * Default: false - */ - deactivate?: boolean - - /** - * Cache type to use. - * - * Available options: - * - * ORDERED_MAP: Cache with no fixed upper bound and dynamic allocation, - * use for dynamic setups like testing or similar. - * - * LRU: LRU cache with pre-allocation of memory and a fixed size. - * Use for larger and more persistent caches. - */ - type?: CacheType - - /** - * Size of the cache (only for LRU cache) - * - * Default: 100000 (account cache) / 20000 (storage cache) / 20000 (code cache) - * - * Note: the cache/trie interplay mechanism is designed in a way that - * the theoretical number of max modified accounts between two flush operations - * should be smaller than the cache size, otherwise the cache will "forget" the - * old modifications resulting in an incomplete set of trie-flushed accounts. - */ - size?: number -} - -export type CacheSettings = { - deactivate: boolean - type: CacheType - size: number -} - /** * Basic state manager options (not to be used directly) */ @@ -59,15 +16,6 @@ interface BaseStateManagerOpts { common?: Common } -/** - * Cache state manager options (not to be used directly) - */ -interface CacheStateManagerOpts { - accountCacheOpts?: CacheOptions - storageCacheOpts?: CacheOptions - codeCacheOpts?: CacheOptions -} - /** * Options for constructing a {@link SimpleStateManager}. */ @@ -83,7 +31,7 @@ export interface RPCStateManagerOpts extends BaseStateManagerOpts { /** * Options for constructing a {@link StateManager}. */ -export interface DefaultStateManagerOpts extends BaseStateManagerOpts, CacheStateManagerOpts { +export interface DefaultStateManagerOpts extends BaseStateManagerOpts, CachesStateManagerOpts { /** * A {@link Trie} instance */ @@ -107,17 +55,25 @@ export interface DefaultStateManagerOpts extends BaseStateManagerOpts, CacheStat * Default: false (for backwards compatibility reasons) */ prefixStorageTrieKeys?: boolean + + /** + * Options to enable and configure the use of a cache account, code and storage + * This can be useful for speeding up reads, especially when the trie is large. + * The cache is only used for reading from the trie and is not used for writing to the trie. + * + * Default: false + */ + cachesOpts?: CachesStateManagerOpts } /** * Options dictionary. */ -export interface StatelessVerkleStateManagerOpts - extends BaseStateManagerOpts, - CacheStateManagerOpts { +export interface StatelessVerkleStateManagerOpts extends BaseStateManagerOpts { accesses?: AccessWitness verkleCrypto: VerkleCrypto initialStateRoot?: Uint8Array + cachesOpts?: CachesStateManagerOpts } export interface VerkleState { From bba928b97fd15301f64657952fa0c4dd7d9f4eb3 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 06:28:59 -0400 Subject: [PATCH 05/23] statemanager: fix shallow copy logic --- packages/client/src/execution/vmexecution.ts | 2 +- packages/statemanager/src/stateManager.ts | 4 +--- packages/statemanager/src/types.ts | 2 +- .../statemanager/test/stateManager.spec.ts | 20 ++++++++++--------- .../test/statelessVerkleStateManager.spec.ts | 16 ++++++++------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index fdecb17486..c47c0673a1 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -4,7 +4,7 @@ import { DBSetHashToNumber, DBSetTD, } from '@ethereumjs/blockchain' -import { CacheType, ConsensusType, Hardfork } from '@ethereumjs/common' +import { ConsensusType, Hardfork } from '@ethereumjs/common' import { MCLBLS, RustBN254 } from '@ethereumjs/evm' import { getGenesis } from '@ethereumjs/genesis' import { diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index a87f63a49f..c2aadad6c0 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -975,9 +975,7 @@ export class DefaultStateManager implements StateManagerInterface { trie, prefixStorageTrieKeys, prefixCodeHashes, - accountCacheOpts, - storageCacheOpts, - codeCacheOpts, + cachesOpts: { accountCacheOpts, storageCacheOpts, codeCacheOpts }, }) } diff --git a/packages/statemanager/src/types.ts b/packages/statemanager/src/types.ts index 58ab9f991d..a7a56aa447 100644 --- a/packages/statemanager/src/types.ts +++ b/packages/statemanager/src/types.ts @@ -31,7 +31,7 @@ export interface RPCStateManagerOpts extends BaseStateManagerOpts { /** * Options for constructing a {@link StateManager}. */ -export interface DefaultStateManagerOpts extends BaseStateManagerOpts, CachesStateManagerOpts { +export interface DefaultStateManagerOpts extends BaseStateManagerOpts { /** * A {@link Trie} instance */ diff --git a/packages/statemanager/test/stateManager.spec.ts b/packages/statemanager/test/stateManager.spec.ts index 1744973e3b..8d88c14533 100644 --- a/packages/statemanager/test/stateManager.spec.ts +++ b/packages/statemanager/test/stateManager.spec.ts @@ -82,22 +82,24 @@ describe('StateManager -> General', () => { sm = new DefaultStateManager({ trie, - accountCacheOpts: { - type: CacheType.LRU, - }, - storageCacheOpts: { - type: CacheType.LRU, + cachesOpts: { + accountCacheOpts: { + type: CacheType.LRU, + }, + storageCacheOpts: { + type: CacheType.LRU, + }, }, }) smCopy = sm.shallowCopy() assert.equal( - smCopy['_accountCacheSettings'].type, + smCopy['_caches'].settings.account.type, CacheType.ORDERED_MAP, 'should switch to ORDERED_MAP account cache on copy()', ) assert.equal( - smCopy['_storageCacheSettings'].type, + smCopy['_caches'].settings.storage.type, CacheType.ORDERED_MAP, 'should switch to ORDERED_MAP storage cache on copy()', ) @@ -105,12 +107,12 @@ describe('StateManager -> General', () => { smCopy = sm.shallowCopy(false) assert.equal( - smCopy['_accountCacheSettings'].type, + smCopy['_caches'].settings.account.type, CacheType.LRU, 'should retain account cache type when deactivate cache downleveling', ) assert.equal( - smCopy['_storageCacheSettings'].type, + smCopy['_caches'].settings.storage.type, CacheType.LRU, 'should retain storage cache type when deactivate cache downleveling', ) diff --git a/packages/statemanager/test/statelessVerkleStateManager.spec.ts b/packages/statemanager/test/statelessVerkleStateManager.spec.ts index 489d594616..c45c9b7b3a 100644 --- a/packages/statemanager/test/statelessVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statelessVerkleStateManager.spec.ts @@ -144,11 +144,13 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { it(`copy()`, async () => { const stateManager = new StatelessVerkleStateManager({ - accountCacheOpts: { - type: CacheType.ORDERED_MAP, - }, - storageCacheOpts: { - type: CacheType.ORDERED_MAP, + cachesOpts: { + accountCacheOpts: { + type: CacheType.ORDERED_MAP, + }, + storageCacheOpts: { + type: CacheType.ORDERED_MAP, + }, }, common, verkleCrypto, @@ -158,12 +160,12 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { const stateManagerCopy = stateManager.shallowCopy() as StatelessVerkleStateManager assert.equal( - (stateManagerCopy as any)['_accountCacheSettings'].type, + stateManagerCopy['_caches'].settings.account.type, CacheType.ORDERED_MAP, 'should switch to ORDERED_MAP account cache on copy()', ) assert.equal( - (stateManagerCopy as any)['_storageCacheSettings'].type, + stateManagerCopy['_caches'].settings.storage.type, CacheType.ORDERED_MAP, 'should switch to ORDERED_MAP storage cache on copy()', ) From a04158d9427c49c8cbbd21c2274422b5fb34e46e Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 06:35:57 -0400 Subject: [PATCH 06/23] client: fix vmexecution statemanager cache opts --- packages/client/src/execution/vmexecution.ts | 30 +++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index c47c0673a1..dcdf1e86ca 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -164,20 +164,22 @@ export class VMExecution extends Execution { const stateManager = new DefaultStateManager({ trie, prefixStorageTrieKeys: this.config.prefixStorageTrieKeys, - accountCacheOpts: { - deactivate: false, - type: CacheType.LRU, - size: this.config.accountCache, - }, - storageCacheOpts: { - deactivate: false, - type: CacheType.LRU, - size: this.config.storageCache, - }, - codeCacheOpts: { - deactivate: false, - type: CacheType.LRU, - size: this.config.codeCache, + cachesOpts: { + accountCacheOpts: { + deactivate: false, + type: CacheType.LRU, + size: this.config.accountCache, + }, + storageCacheOpts: { + deactivate: false, + type: CacheType.LRU, + size: this.config.storageCache, + }, + codeCacheOpts: { + deactivate: false, + type: CacheType.LRU, + size: this.config.codeCache, + }, }, common: this.config.chainCommon, }) From 24315945568dc6a84bbfe5282ea747191e7644bb Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 07:16:05 -0400 Subject: [PATCH 07/23] statemanager: refactor some capabilities to caches methods --- packages/statemanager/src/cache/caches.ts | 32 +++++++++++++++++ packages/statemanager/src/stateManager.ts | 35 +++++-------------- .../src/statelessVerkleStateManager.ts | 12 ++----- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/packages/statemanager/src/cache/caches.ts b/packages/statemanager/src/cache/caches.ts index 5295079663..80270eefca 100644 --- a/packages/statemanager/src/cache/caches.ts +++ b/packages/statemanager/src/cache/caches.ts @@ -3,6 +3,8 @@ import { CodeCache } from './code.js' import { StorageCache } from './storage.js' import { type CacheSettings, CacheType, type CachesStateManagerOpts } from './types.js' +import type { Address } from '@ethereumjs/util' + export class Caches { account?: AccountCache code?: CodeCache @@ -58,4 +60,34 @@ export class Caches { }) } } + + checkpoint() { + this.account?.checkpoint() + this.storage?.checkpoint() + this.code?.checkpoint() + } + + clear() { + this.account?.clear() + this.storage?.clear() + this.code?.clear() + } + + commit() { + this.account?.commit() + this.storage?.commit() + this.code?.commit() + } + + deleteAccount(address: Address) { + this.code?.del(address) + this.account?.del(address) + this.storage?.clearStorage(address) + } + + revert() { + this.account?.revert() + this.storage?.revert() + this.code?.revert() + } } diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index c2aadad6c0..8d30ba68bc 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -190,15 +190,10 @@ export class DefaultStateManager implements StateManagerInterface { this._debug(`Delete account ${address}`) } - this._caches.code?.del(address) + this._caches.deleteAccount(address) - if (this._caches.settings.account.deactivate) { + if (this._caches.settings.account.deactivate === true) { await this._trie.del(address.bytes) - } else { - this._caches.account!.del(address) - } - if (!this._caches.settings.storage.deactivate) { - this._caches.storage?.clearStorage(address) } } @@ -458,9 +453,7 @@ export class DefaultStateManager implements StateManagerInterface { */ async checkpoint(): Promise { this._trie.checkpoint() - this._caches.storage?.checkpoint() - this._caches.account?.checkpoint() - this._caches.code?.checkpoint() + this._caches.checkpoint() this._checkpointCount++ } @@ -471,9 +464,7 @@ export class DefaultStateManager implements StateManagerInterface { async commit(): Promise { // setup trie checkpointing await this._trie.commit() - this._caches.storage?.commit() - this._caches.account?.commit() - this._caches.code?.commit() + this._caches.commit() this._checkpointCount-- if (this._checkpointCount === 0) { @@ -493,9 +484,7 @@ export class DefaultStateManager implements StateManagerInterface { async revert(): Promise { // setup trie checkpointing await this._trie.revert() - this._caches.storage?.revert() - this._caches.account?.revert() - this._caches.code?.revert() + this._caches.revert() this._storageTries = {} @@ -810,14 +799,8 @@ export class DefaultStateManager implements StateManagerInterface { } this._trie.root(stateRoot) - if (this._caches.account !== undefined && clearCache) { - this._caches.account.clear() - } - if (this._caches.storage !== undefined && clearCache) { - this._caches.storage.clear() - } - if (this._caches.code !== undefined && clearCache) { - this._caches.code!.clear() + if (clearCache) { + this._caches.clear() } this._storageTries = {} } @@ -983,9 +966,7 @@ export class DefaultStateManager implements StateManagerInterface { * Clears all underlying caches */ clearCaches() { - this._caches.account?.clear() - this._caches.storage?.clear() - this._caches.code?.clear() + this._caches.clear() } /** diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 1f7bd58228..f161e99070 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -725,9 +725,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { */ async checkpoint(): Promise { this._checkpoints.push(this._state) - this._caches.account?.checkpoint() - this._caches.storage?.checkpoint() - this._caches.code?.checkpoint() + this._caches.checkpoint() } /** @@ -736,9 +734,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { */ async commit(): Promise { this._checkpoints.pop() - this._caches.account!.commit() - this._caches.storage?.commit() - this._caches.code?.commit() + this._caches.commit() } // TODO @@ -753,9 +749,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { async revert(): Promise { // setup trie checkpointing this._checkpoints.pop() - this._caches.account?.revert() - this._caches.storage?.revert() - this._caches.code?.revert() + this._caches.revert() } /** From 814b9f7814f434bba71b5ca1cd83a71a510a56ca Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 07:16:16 -0400 Subject: [PATCH 08/23] statemanager: fix tests --- .../test/stateManager.account.spec.ts | 14 ++++---- .../test/stateManager.code.spec.ts | 12 +++---- .../test/stateManager.storage.spec.ts | 35 +++++++++++++++---- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/statemanager/test/stateManager.account.spec.ts b/packages/statemanager/test/stateManager.account.spec.ts index ef97c11ce7..f13ee916e1 100644 --- a/packages/statemanager/test/stateManager.account.spec.ts +++ b/packages/statemanager/test/stateManager.account.spec.ts @@ -12,7 +12,7 @@ describe('StateManager -> General/Account', () => { { deactivate: false, size: 0 }, ]) { it(`should set the state root to empty`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) assert.ok(equalsBytes(stateManager['_trie'].root(), KECCAK256_RLP), 'it has default root') // commit some data to the trie @@ -32,7 +32,7 @@ describe('StateManager -> General/Account', () => { }) it(`should clear the cache when the state root is set`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -75,7 +75,7 @@ describe('StateManager -> General/Account', () => { }) it('should put and get account, and add to the underlying cache if the account is not found', async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const account = createAccountWithDefaults() const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) @@ -86,7 +86,7 @@ describe('StateManager -> General/Account', () => { assert.equal(res1!.balance, BigInt(0xfff384)) await stateManager.flush() - stateManager['_accountCache']?.clear() + stateManager['_caches'].account?.clear() const res2 = await stateManager.getAccount(address) @@ -94,7 +94,7 @@ describe('StateManager -> General/Account', () => { }) it(`should return undefined for a non-existent account`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const res = (await stateManager.getAccount(address)) === undefined @@ -103,7 +103,7 @@ describe('StateManager -> General/Account', () => { }) it(`should return undefined for an existent account`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const account = createAccountWithDefaults(BigInt(0x1), BigInt(0x1)) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) @@ -115,7 +115,7 @@ describe('StateManager -> General/Account', () => { }) it(`should modify account fields correctly`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const account = createAccountWithDefaults() const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) await stateManager.putAccount(address, account) diff --git a/packages/statemanager/test/stateManager.code.spec.ts b/packages/statemanager/test/stateManager.code.spec.ts index ee7dc802ac..0882bfbda7 100644 --- a/packages/statemanager/test/stateManager.code.spec.ts +++ b/packages/statemanager/test/stateManager.code.spec.ts @@ -31,8 +31,8 @@ describe('StateManager -> Code', () => { */ // Setup - const stateManager = new DefaultStateManager({ accountCacheOpts }) - const codeStateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const codeStateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const address1 = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() const key1 = hexToBytes(`0x${'00'.repeat(32)}`) @@ -87,7 +87,7 @@ describe('StateManager -> Code', () => { }) it(`should set and get code`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const code = hexToBytes( '0x73095e7baea6a6c7c4c2dfeb977efac326af552d873173095e7baea6a6c7c4c2dfeb977efac326af552d873157', @@ -105,7 +105,7 @@ describe('StateManager -> Code', () => { }) it(`should not get code if is not contract`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const raw: AccountData = { nonce: '0x0', @@ -118,7 +118,7 @@ describe('StateManager -> Code', () => { }) it(`should set empty code`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const raw: AccountData = { nonce: '0x0', @@ -133,7 +133,7 @@ describe('StateManager -> Code', () => { }) it(`should prefix codehashes by default`, async () => { - const stateManager = new DefaultStateManager({ accountCacheOpts }) + const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const code = hexToBytes('0x80') await stateManager.putCode(address, code) diff --git a/packages/statemanager/test/stateManager.storage.spec.ts b/packages/statemanager/test/stateManager.storage.spec.ts index ed65a30f8f..3b5de84a5f 100644 --- a/packages/statemanager/test/stateManager.storage.spec.ts +++ b/packages/statemanager/test/stateManager.storage.spec.ts @@ -24,7 +24,10 @@ describe('StateManager -> Storage', () => { ]) { for (const prefixStorageTrieKeys of [false, true]) { it.skipIf(isBrowser() === true)(`should dump storage`, async () => { - const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, storageCacheOpts }) + const stateManager = new DefaultStateManager({ + prefixStorageTrieKeys, + cachesOpts: { storageCacheOpts }, + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -40,7 +43,10 @@ describe('StateManager -> Storage', () => { }) it("should validate the key's length when modifying a contract's storage", async () => { - const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, storageCacheOpts }) + const stateManager = new DefaultStateManager({ + prefixStorageTrieKeys, + cachesOpts: { storageCacheOpts }, + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() await stateManager.putAccount(address, account) @@ -56,7 +62,10 @@ describe('StateManager -> Storage', () => { }) it("should validate the key's length when reading a contract's storage", async () => { - const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, storageCacheOpts }) + const stateManager = new DefaultStateManager({ + prefixStorageTrieKeys, + cachesOpts: { storageCacheOpts }, + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() await stateManager.putAccount(address, account) @@ -72,7 +81,10 @@ describe('StateManager -> Storage', () => { }) it(`should throw on storage values larger than 32 bytes`, async () => { - const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, storageCacheOpts }) + const stateManager = new DefaultStateManager({ + prefixStorageTrieKeys, + cachesOpts: { storageCacheOpts }, + }) const address = createZeroAddress() const account = createAccountWithDefaults() await stateManager.putAccount(address, account) @@ -88,7 +100,10 @@ describe('StateManager -> Storage', () => { }) it(`should strip zeros of storage values`, async () => { - const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, storageCacheOpts }) + const stateManager = new DefaultStateManager({ + prefixStorageTrieKeys, + cachesOpts: { storageCacheOpts }, + }) const address = createZeroAddress() const account = createAccountWithDefaults() await stateManager.putAccount(address, account) @@ -118,7 +133,10 @@ describe('StateManager -> Storage', () => { const zeroLengths = [0, 1, 31, 32] // checks for arbitrary-length zeros for (const length of zeroLengths) { - const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, storageCacheOpts }) + const stateManager = new DefaultStateManager({ + prefixStorageTrieKeys, + cachesOpts: { storageCacheOpts }, + }) const account = createAccountWithDefaults() await stateManager.putAccount(address, account) @@ -138,7 +156,10 @@ describe('StateManager -> Storage', () => { }) it(`should not strip trailing zeros`, async () => { - const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, storageCacheOpts }) + const stateManager = new DefaultStateManager({ + prefixStorageTrieKeys, + cachesOpts: { storageCacheOpts }, + }) const address = createZeroAddress() const account = createAccountWithDefaults() await stateManager.putAccount(address, account) From 2439219be9fe00800c6c337e031a8e7196cee4f8 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 14:33:00 -0400 Subject: [PATCH 09/23] statemanager: refactor caches into optional opt passed in directly to the sm --- packages/statemanager/src/stateManager.ts | 66 +++++++++-------- .../src/statelessVerkleStateManager.ts | 72 +++++++++---------- packages/statemanager/src/types.ts | 7 +- 3 files changed, 74 insertions(+), 71 deletions(-) diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 8d30ba68bc..29e7cc248a 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -64,7 +64,7 @@ import type { Debugger } from 'debug' */ export class DefaultStateManager implements StateManagerInterface { protected _debug: Debugger - protected _caches: Caches + protected _caches?: Caches originalStorageCache: OriginalStorageCache @@ -115,7 +115,7 @@ export class DefaultStateManager implements StateManagerInterface { this._prefixCodeHashes = opts.prefixCodeHashes ?? true this._prefixStorageTrieKeys = opts.prefixStorageTrieKeys ?? false - this._caches = new Caches(opts.cachesOpts) + this._caches = opts.caches } /** @@ -123,7 +123,7 @@ export class DefaultStateManager implements StateManagerInterface { * @param address - Address of the `account` to get */ async getAccount(address: Address): Promise { - if (!this._caches.settings.account.deactivate) { + if (this._caches !== undefined && !this._caches.settings.account.deactivate) { const elem = this._caches.account!.get(address) if (elem !== undefined) { return elem.accountRLP !== undefined ? createAccountFromRLP(elem.accountRLP) : undefined @@ -135,7 +135,7 @@ export class DefaultStateManager implements StateManagerInterface { if (this.DEBUG) { this._debug(`Get account ${address} from DB (${account ? 'exists' : 'non-existent'})`) } - this._caches.account?.put(address, account) + this._caches?.account?.put(address, account) return account } @@ -154,7 +154,7 @@ export class DefaultStateManager implements StateManagerInterface { }`, ) } - if (this._caches.settings.account.deactivate) { + if (this._caches === undefined || this._caches.settings.account.deactivate) { const trie = this._trie if (account !== undefined) { await trie.put(address.bytes, account.serialize()) @@ -190,9 +190,9 @@ export class DefaultStateManager implements StateManagerInterface { this._debug(`Delete account ${address}`) } - this._caches.deleteAccount(address) + this._caches?.deleteAccount(address) - if (this._caches.settings.account.deactivate === true) { + if (this._caches === undefined || this._caches.settings.account.deactivate === true) { await this._trie.del(address.bytes) } } @@ -204,7 +204,7 @@ export class DefaultStateManager implements StateManagerInterface { * @param value - The value of the `code` */ async putCode(address: Address, value: Uint8Array): Promise { - this._caches.code?.put(address, value) + this._caches?.code?.put(address, value) const codeHash = this.keccakFunction(value) if (this.DEBUG) { @@ -224,7 +224,7 @@ export class DefaultStateManager implements StateManagerInterface { * Returns an empty `Uint8Array` if the account has no associated code. */ async getCode(address: Address): Promise { - if (!this._caches.settings.code.deactivate) { + if (this._caches !== undefined && !this._caches.settings.code.deactivate) { const elem = this._caches.code?.get(address) if (elem !== undefined) { return elem.code ?? new Uint8Array(0) @@ -242,7 +242,7 @@ export class DefaultStateManager implements StateManagerInterface { : account.codeHash const code = (await this._trie.database().get(key)) ?? new Uint8Array(0) - if (!this._caches.settings.code.deactivate) { + if (this._caches !== undefined && !this._caches.settings.code.deactivate) { this._caches.code!.put(address, code) } return code @@ -324,7 +324,7 @@ export class DefaultStateManager implements StateManagerInterface { if (key.length !== 32) { throw new Error('Storage key must be 32 bytes long') } - if (!this._caches.settings.storage.deactivate) { + if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { const value = this._caches.storage!.get(address, key) if (value !== undefined) { const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array @@ -338,7 +338,7 @@ export class DefaultStateManager implements StateManagerInterface { } const trie = this._getStorageTrie(address, account) const value = await trie.get(key) - if (!this._caches.settings.storage.deactivate) { + if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { this._caches.storage?.put(address, key, value ?? hexToBytes('0x80')) } const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array @@ -422,7 +422,7 @@ export class DefaultStateManager implements StateManagerInterface { } value = unpadBytes(value) - if (!this._caches.settings.storage.deactivate) { + if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { const encodedValue = RLP.encode(value) this._caches.storage!.put(address, key, encodedValue) } else { @@ -439,7 +439,7 @@ export class DefaultStateManager implements StateManagerInterface { if (!account) { account = new Account() } - this._caches.storage?.clearStorage(address) + this._caches?.storage?.clearStorage(address) await this._modifyContractStorage(address, account, (storageTrie, done) => { storageTrie.root(storageTrie.EMPTY_TRIE_ROOT) done() @@ -453,7 +453,7 @@ export class DefaultStateManager implements StateManagerInterface { */ async checkpoint(): Promise { this._trie.checkpoint() - this._caches.checkpoint() + this._caches?.checkpoint() this._checkpointCount++ } @@ -464,7 +464,7 @@ export class DefaultStateManager implements StateManagerInterface { async commit(): Promise { // setup trie checkpointing await this._trie.commit() - this._caches.commit() + this._caches?.commit() this._checkpointCount-- if (this._checkpointCount === 0) { @@ -484,7 +484,7 @@ export class DefaultStateManager implements StateManagerInterface { async revert(): Promise { // setup trie checkpointing await this._trie.revert() - this._caches.revert() + this._caches?.revert() this._storageTries = {} @@ -500,7 +500,7 @@ export class DefaultStateManager implements StateManagerInterface { * Writes all cache items to the trie */ async flush(): Promise { - if (!this._caches.settings.code.deactivate) { + if (this._caches !== undefined && !this._caches.settings.code.deactivate) { const items = this._caches.code!.flush() for (const item of items) { const addr = createAddressFromString(`0x${item[0]}`) @@ -522,7 +522,7 @@ export class DefaultStateManager implements StateManagerInterface { await this.modifyAccountFields(addr, { codeHash }) } } - if (!this._caches.settings.storage.deactivate) { + if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { const items = this._caches.storage!.flush() for (const item of items) { const address = createAddressFromString(`0x${item[0]}`) @@ -537,7 +537,7 @@ export class DefaultStateManager implements StateManagerInterface { } } } - if (!this._caches.settings.account.deactivate) { + if (this._caches !== undefined && !this._caches.settings.account.deactivate) { const items = this._caches.account!.flush() for (const item of items) { const addressHex = item[0] @@ -800,7 +800,7 @@ export class DefaultStateManager implements StateManagerInterface { this._trie.root(stateRoot) if (clearCache) { - this._caches.clear() + this._caches?.clear() } this._storageTries = {} } @@ -940,16 +940,24 @@ export class DefaultStateManager implements StateManagerInterface { const trie = this._trie.shallowCopy(false, { cacheSize }) const prefixCodeHashes = this._prefixCodeHashes const prefixStorageTrieKeys = this._prefixStorageTrieKeys - let accountCacheOpts = { ...this._caches.settings.account } - if (downlevelCaches && !this._caches.settings.account.deactivate) { + let accountCacheOpts = { ...this._caches?.settings.account } + if ( + downlevelCaches && + this._caches !== undefined && + !this._caches.settings.account.deactivate + ) { accountCacheOpts = { ...accountCacheOpts, type: CacheType.ORDERED_MAP } } - let storageCacheOpts = { ...this._caches.settings.storage } - if (downlevelCaches && !this._caches.settings.storage.deactivate) { + let storageCacheOpts = { ...this._caches?.settings.storage } + if ( + downlevelCaches && + this._caches !== undefined && + !this._caches.settings.storage.deactivate + ) { storageCacheOpts = { ...storageCacheOpts, type: CacheType.ORDERED_MAP } } - let codeCacheOpts = { ...this._caches.settings.code } - if (!this._caches.settings.code.deactivate) { + let codeCacheOpts = { ...this._caches?.settings.code } + if (this._caches !== undefined && !this._caches.settings.code.deactivate) { codeCacheOpts = { ...codeCacheOpts, type: CacheType.ORDERED_MAP } } @@ -958,7 +966,7 @@ export class DefaultStateManager implements StateManagerInterface { trie, prefixStorageTrieKeys, prefixCodeHashes, - cachesOpts: { accountCacheOpts, storageCacheOpts, codeCacheOpts }, + caches: new Caches({ accountCacheOpts, storageCacheOpts, codeCacheOpts }), }) } @@ -966,7 +974,7 @@ export class DefaultStateManager implements StateManagerInterface { * Clears all underlying caches */ clearCaches() { - this._caches.clear() + this._caches?.clear() } /** diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index f161e99070..8b38d9ee7b 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -68,7 +68,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { verkleCrypto: VerkleCrypto - protected _caches: Caches + protected _caches?: Caches /** * StateManager is run in DEBUG mode (default: false) @@ -105,7 +105,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { constructor(opts: StatelessVerkleStateManagerOpts) { this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this)) - this._caches = new Caches(opts.cachesOpts) + this._caches = opts.caches this._cachedStateRoot = opts.initialStateRoot @@ -206,8 +206,11 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * at the last fully committed point, i.e. as if all current * checkpoints were reverted. */ - shallowCopy(): StateManagerInterface { - const stateManager = new StatelessVerkleStateManager({ verkleCrypto: this.verkleCrypto }) + shallowCopy(): StatelessVerkleStateManager { + const stateManager = new StatelessVerkleStateManager({ + caches: this._caches !== undefined ? new Caches() : undefined, + verkleCrypto: this.verkleCrypto, + }) return stateManager } @@ -222,7 +225,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`putCode address=${address.toString()} value=${short(value)}`) } - this._caches.code?.put(address, value) + this._caches?.code?.put(address, value) const codeHash = keccak256(value) if (KECCAK256_NULL === codeHash) { // If the code hash is the null hash, no code has to be stored @@ -246,7 +249,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`getCode address=${address.toString()}`) } - if (!this._caches.settings.code.deactivate) { + if (this._caches !== undefined && !this._caches.settings.code.deactivate) { const elem = this._caches.code?.get(address) if (elem !== undefined) { return elem.code ?? new Uint8Array(0) @@ -293,7 +296,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { // Return accessedCode where only accessed code has been copied const contactCode = accessedCode.slice(0, codeSize) - if (!this._caches.settings.code.deactivate) { + if (this._caches !== undefined && !this._caches.settings.code.deactivate) { this._caches.code?.put(address, contactCode) } @@ -301,7 +304,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } async getCodeSize(address: Address): Promise { - if (!this._caches.settings.account.deactivate) { + if (this._caches !== undefined && !this._caches.settings.account.deactivate) { const elem = this._caches.account!.get(address) if (elem !== undefined) { const account = @@ -333,7 +336,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * If this does not exist an empty `Uint8Array` is returned. */ async getStorage(address: Address, key: Uint8Array): Promise { - if (!this._caches.settings.storage.deactivate) { + if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { const value = this._caches.storage!.get(address, key) if (value !== undefined) { return value @@ -347,7 +350,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { ) const storageValue = toBytes(this._state[bytesToHex(storageKey)]) - if (!this._caches.settings.storage.deactivate) { + if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { this._caches.storage?.put(address, key, storageValue ?? hexToBytes('0x80')) } @@ -362,7 +365,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * @param value - Value to set at `key` for account corresponding to `address`. Cannot be more than 32 bytes. Leading zeros are stripped. If it is a empty or filled with zeros, deletes the value. */ async putStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise { - if (!this._caches.settings.storage.deactivate) { + if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { this._caches.storage!.put(address, key, value) } else { // TODO: Consider refactoring this in a writeContractStorage function? Like in stateManager.ts @@ -384,13 +387,13 @@ export class StatelessVerkleStateManager implements StateManagerInterface { async clearStorage(address: Address): Promise { const stem = getVerkleStem(this.verkleCrypto, address, 0) const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash) - this._caches.storage?.clearStorage(address) + this._caches?.storage?.clearStorage(address) // Update codeHash to `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` this._state[bytesToHex(codeHashKey)] = KECCAK256_NULL_S } async getAccount(address: Address): Promise { - if (!this._caches.settings.account.deactivate) { + if (this._caches !== undefined && !this._caches.settings.account.deactivate) { const elem = this._caches.account!.get(address) if (elem !== undefined) { return elem.accountRLP !== undefined @@ -473,7 +476,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`getAccount address=${address.toString()} stem=${short(stem)}`) } - if (!this._caches.settings.account.deactivate) { + if (this._caches !== undefined && !this._caches.settings.account.deactivate) { this._caches.account?.put(address, account, true) } @@ -485,7 +488,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`putAccount address=${address.toString()}`) } - if (this._caches.settings.account.deactivate) { + if (this._caches !== undefined && this._caches.settings.account.deactivate) { const stem = getVerkleStem(this.verkleCrypto, address, 0) const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance) const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce) @@ -499,9 +502,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { this._state[bytesToHex(codeHashKey)] = bytesToHex(account.codeHash) } else { if (account !== undefined) { - this._caches.account!.put(address, account, true) + this._caches?.account?.put(address, account, true) } else { - this._caches.account!.del(address) + this._caches?.account?.del(address) } } } @@ -515,12 +518,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`Delete account ${address}`) } - this._caches.code?.del(address) - this._caches.account!.del(address) - - if (!this._caches.settings.storage.deactivate) { - this._caches.storage?.clearStorage(address) - } + this._caches?.deleteAccount(address) } async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { @@ -547,7 +545,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { // Verifies that the witness post-state matches the computed post-state verifyPostState(): boolean { // track what all chunks were accessed so as to compare in the end if any chunks were missed - // in access while comparising against the provided poststate in the execution witness + // in access while comparing against the provided poststate in the execution witness const accessedChunks = new Map() // switch to false if postVerify fails let postFailures = 0 @@ -634,7 +632,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { const { address, type } = accessedState switch (type) { case AccessedStateType.Version: { - const encodedAccount = this._caches.account?.get(address)?.accountRLP + const encodedAccount = this._caches?.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -643,7 +641,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { return ZEROVALUE } case AccessedStateType.Balance: { - const encodedAccount = this._caches.account?.get(address)?.accountRLP + const encodedAccount = this._caches?.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -653,7 +651,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } case AccessedStateType.Nonce: { - const encodedAccount = this._caches.account?.get(address)?.accountRLP + const encodedAccount = this._caches?.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -662,7 +660,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } case AccessedStateType.CodeHash: { - const encodedAccount = this._caches.account?.get(address)?.accountRLP + const encodedAccount = this._caches?.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -670,10 +668,10 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } case AccessedStateType.CodeSize: { - const codeSize = this._caches.code?.get(address)?.code?.length + const codeSize = this._caches?.code?.get(address)?.code?.length if (codeSize === undefined) { // it could be an EOA lets check for that - const encodedAccount = this._caches.account?.get(address)?.accountRLP + const encodedAccount = this._caches?.account?.get(address)?.accountRLP if (encodedAccount === undefined) { return null } @@ -693,7 +691,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { case AccessedStateType.Code: { const { codeOffset } = accessedState - const code = this._caches.code?.get(address)?.code + const code = this._caches?.code?.get(address)?.code if (code === undefined) { return null } @@ -709,7 +707,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { const { slot } = accessedState const key = setLengthLeft(bigIntToBytes(slot), 32) - const storage = this._caches.storage?.get(address, key) + const storage = this._caches?.storage?.get(address, key) if (storage === undefined) { return null } @@ -725,7 +723,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { */ async checkpoint(): Promise { this._checkpoints.push(this._state) - this._caches.checkpoint() + this._caches?.checkpoint() } /** @@ -734,7 +732,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { */ async commit(): Promise { this._checkpoints.pop() - this._caches.commit() + this._caches?.commit() } // TODO @@ -749,7 +747,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { async revert(): Promise { // setup trie checkpointing this._checkpoints.pop() - this._caches.revert() + this._caches?.revert() } /** @@ -783,9 +781,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * Clears all underlying caches */ clearCaches() { - this._caches.account?.clear() - this._caches.code?.clear() - this._caches.storage?.clear() + this._caches?.clear() } // TODO: Removing this causes a Kaustinen6 test in client to fail diff --git a/packages/statemanager/src/types.ts b/packages/statemanager/src/types.ts index a7a56aa447..a9051bc2e0 100644 --- a/packages/statemanager/src/types.ts +++ b/packages/statemanager/src/types.ts @@ -1,7 +1,6 @@ import { type PrefixedHexString, utf8ToBytes } from '@ethereumjs/util' -import type { CachesStateManagerOpts } from './cache/types.js' -import type { AccessWitness } from './index.js' +import type { AccessWitness, Caches } from './index.js' import type { Common } from '@ethereumjs/common' import type { Trie } from '@ethereumjs/trie' import type { VerkleCrypto } from '@ethereumjs/util' @@ -63,7 +62,7 @@ export interface DefaultStateManagerOpts extends BaseStateManagerOpts { * * Default: false */ - cachesOpts?: CachesStateManagerOpts + caches?: Caches } /** @@ -73,7 +72,7 @@ export interface StatelessVerkleStateManagerOpts extends BaseStateManagerOpts { accesses?: AccessWitness verkleCrypto: VerkleCrypto initialStateRoot?: Uint8Array - cachesOpts?: CachesStateManagerOpts + caches?: Caches } export interface VerkleState { From 4c6240faf576d15df4f1bcea2fbc7a21474b9e8c Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 14:33:16 -0400 Subject: [PATCH 10/23] statemanager: adjust tests with refactored caches --- .../test/checkpointing.code.spec.ts | 136 +++++++++++++++--- .../test/stateManager.account.spec.ts | 16 +-- .../test/stateManager.code.spec.ts | 14 +- .../statemanager/test/stateManager.spec.ts | 14 +- .../test/stateManager.storage.spec.ts | 16 +-- .../test/statelessVerkleStateManager.spec.ts | 18 ++- 6 files changed, 160 insertions(+), 54 deletions(-) diff --git a/packages/statemanager/test/checkpointing.code.spec.ts b/packages/statemanager/test/checkpointing.code.spec.ts index 12e8815dd4..180c8b8274 100644 --- a/packages/statemanager/test/checkpointing.code.spec.ts +++ b/packages/statemanager/test/checkpointing.code.spec.ts @@ -2,7 +2,7 @@ import { type StateManagerInterface } from '@ethereumjs/common' import { Account, Address, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { DefaultStateManager, SimpleStateManager } from '../src/index.js' +import { Caches, DefaultStateManager, SimpleStateManager } from '../src/index.js' const codeEval = async ( sm: StateManagerInterface, @@ -93,7 +93,20 @@ describe('StateManager -> Code Checkpointing', () => { for (const SM of stateManagers) { for (const c of codeSets) { it(`No CP -> C1 -> Flush() (-> C1)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + + // let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -107,7 +120,12 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`CP -> C1.1 -> Commit -> Flush() (-> C1.1)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } await sm.putAccount(address, new Account()) await sm.checkpoint() @@ -121,7 +139,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`CP -> C1.1 -> Revert -> Flush() (-> Undefined)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.checkpoint() @@ -137,7 +161,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`C1.1 -> CP -> Commit -> Flush() (-> C1.1)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -151,7 +181,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`C1.1 -> CP -> Revert -> Flush() (-> C1.1)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -165,7 +201,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`C1.1 -> CP -> C1.2 -> Commit -> Flush() (-> C1.2)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -180,7 +222,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`C1.1 -> CP -> C1.2 -> Commit -> C1.3 -> Flush() (-> C1.3)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -196,7 +244,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`C1.1 -> CP -> C1.2 -> C1.3 -> Commit -> Flush() (-> C1.3)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -212,7 +266,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`CP -> C1.1 -> C1.2 -> Commit -> Flush() (-> C1.2)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.checkpoint() @@ -227,7 +287,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`CP -> C1.1 -> C1.2 -> Revert -> Flush() (-> Undefined)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.checkpoint() @@ -243,7 +309,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it(`C1.1 -> CP -> C1.2 -> Revert -> Flush() (-> C1.1)`, async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -258,7 +330,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Commit -> Commit -> Flush() (-> C1.3)', async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -276,7 +354,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Commit -> Revert -> Flush() (-> C1.1)', async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -294,7 +378,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> Commit -> Flush() (-> C1.2)', async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -312,7 +402,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> C1.4 -> Commit -> Flush() (-> C1.4)', async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) @@ -331,7 +427,13 @@ describe('StateManager -> Code Checkpointing', () => { }) it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> C1.4 -> CP -> C1.5 -> Commit -> Commit -> Flush() (-> C1.5)', async () => { - const sm = new SM() + let sm: DefaultStateManager | SimpleStateManager + if (SM === DefaultStateManager) { + sm = new SM({ caches: new Caches() }) + } else { + sm = new SM() + } + await sm.putAccount(address, new Account()) await sm.putCode(address, c.c1.value) diff --git a/packages/statemanager/test/stateManager.account.spec.ts b/packages/statemanager/test/stateManager.account.spec.ts index f13ee916e1..ed2c4d18f9 100644 --- a/packages/statemanager/test/stateManager.account.spec.ts +++ b/packages/statemanager/test/stateManager.account.spec.ts @@ -1,7 +1,7 @@ import { Address, KECCAK256_RLP, bytesToHex, equalsBytes, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { DefaultStateManager } from '../src/index.js' +import { Caches, DefaultStateManager } from '../src/index.js' import { createAccountWithDefaults } from './util.js' @@ -12,7 +12,7 @@ describe('StateManager -> General/Account', () => { { deactivate: false, size: 0 }, ]) { it(`should set the state root to empty`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) assert.ok(equalsBytes(stateManager['_trie'].root(), KECCAK256_RLP), 'it has default root') // commit some data to the trie @@ -32,7 +32,7 @@ describe('StateManager -> General/Account', () => { }) it(`should clear the cache when the state root is set`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -75,7 +75,7 @@ describe('StateManager -> General/Account', () => { }) it('should put and get account, and add to the underlying cache if the account is not found', async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const account = createAccountWithDefaults() const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) @@ -86,7 +86,7 @@ describe('StateManager -> General/Account', () => { assert.equal(res1!.balance, BigInt(0xfff384)) await stateManager.flush() - stateManager['_caches'].account?.clear() + stateManager['_caches']?.account?.clear() const res2 = await stateManager.getAccount(address) @@ -94,7 +94,7 @@ describe('StateManager -> General/Account', () => { }) it(`should return undefined for a non-existent account`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const res = (await stateManager.getAccount(address)) === undefined @@ -103,7 +103,7 @@ describe('StateManager -> General/Account', () => { }) it(`should return undefined for an existent account`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const account = createAccountWithDefaults(BigInt(0x1), BigInt(0x1)) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) @@ -115,7 +115,7 @@ describe('StateManager -> General/Account', () => { }) it(`should modify account fields correctly`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const account = createAccountWithDefaults() const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) await stateManager.putAccount(address, account) diff --git a/packages/statemanager/test/stateManager.code.spec.ts b/packages/statemanager/test/stateManager.code.spec.ts index 0882bfbda7..023afef9d2 100644 --- a/packages/statemanager/test/stateManager.code.spec.ts +++ b/packages/statemanager/test/stateManager.code.spec.ts @@ -7,7 +7,7 @@ import { } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { DefaultStateManager } from '../src/index.js' +import { Caches, DefaultStateManager } from '../src/index.js' import { createAccountWithDefaults } from './util.js' @@ -31,8 +31,8 @@ describe('StateManager -> Code', () => { */ // Setup - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) - const codeStateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const codeStateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const address1 = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() const key1 = hexToBytes(`0x${'00'.repeat(32)}`) @@ -87,7 +87,7 @@ describe('StateManager -> Code', () => { }) it(`should set and get code`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const code = hexToBytes( '0x73095e7baea6a6c7c4c2dfeb977efac326af552d873173095e7baea6a6c7c4c2dfeb977efac326af552d873157', @@ -105,7 +105,7 @@ describe('StateManager -> Code', () => { }) it(`should not get code if is not contract`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const raw: AccountData = { nonce: '0x0', @@ -118,7 +118,7 @@ describe('StateManager -> Code', () => { }) it(`should set empty code`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const raw: AccountData = { nonce: '0x0', @@ -133,7 +133,7 @@ describe('StateManager -> Code', () => { }) it(`should prefix codehashes by default`, async () => { - const stateManager = new DefaultStateManager({ cachesOpts: { accountCacheOpts } }) + const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const code = hexToBytes('0x80') await stateManager.putCode(address, code) diff --git a/packages/statemanager/test/stateManager.spec.ts b/packages/statemanager/test/stateManager.spec.ts index 8d88c14533..5193adbdd5 100644 --- a/packages/statemanager/test/stateManager.spec.ts +++ b/packages/statemanager/test/stateManager.spec.ts @@ -14,7 +14,7 @@ import { } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { CacheType, DefaultStateManager } from '../src/index.js' +import { CacheType, Caches, DefaultStateManager } from '../src/index.js' import type { PrefixedHexString } from '@ethereumjs/util' @@ -82,24 +82,24 @@ describe('StateManager -> General', () => { sm = new DefaultStateManager({ trie, - cachesOpts: { + caches: new Caches({ accountCacheOpts: { type: CacheType.LRU, }, storageCacheOpts: { type: CacheType.LRU, }, - }, + }), }) smCopy = sm.shallowCopy() assert.equal( - smCopy['_caches'].settings.account.type, + smCopy['_caches']?.settings.account.type, CacheType.ORDERED_MAP, 'should switch to ORDERED_MAP account cache on copy()', ) assert.equal( - smCopy['_caches'].settings.storage.type, + smCopy['_caches']?.settings.storage.type, CacheType.ORDERED_MAP, 'should switch to ORDERED_MAP storage cache on copy()', ) @@ -107,12 +107,12 @@ describe('StateManager -> General', () => { smCopy = sm.shallowCopy(false) assert.equal( - smCopy['_caches'].settings.account.type, + smCopy['_caches']?.settings.account.type, CacheType.LRU, 'should retain account cache type when deactivate cache downleveling', ) assert.equal( - smCopy['_caches'].settings.storage.type, + smCopy['_caches']?.settings.storage.type, CacheType.LRU, 'should retain storage cache type when deactivate cache downleveling', ) diff --git a/packages/statemanager/test/stateManager.storage.spec.ts b/packages/statemanager/test/stateManager.storage.spec.ts index 3b5de84a5f..32d4d3e11c 100644 --- a/packages/statemanager/test/stateManager.storage.spec.ts +++ b/packages/statemanager/test/stateManager.storage.spec.ts @@ -11,7 +11,7 @@ import { import { keccak256 } from 'ethereum-cryptography/keccak.js' import { assert, describe, it } from 'vitest' -import { DefaultStateManager } from '../src/index.js' +import { Caches, DefaultStateManager } from '../src/index.js' import { createAccountWithDefaults } from './util.js' @@ -26,7 +26,7 @@ describe('StateManager -> Storage', () => { it.skipIf(isBrowser() === true)(`should dump storage`, async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - cachesOpts: { storageCacheOpts }, + caches: new Caches({ storageCacheOpts }), }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -45,7 +45,7 @@ describe('StateManager -> Storage', () => { it("should validate the key's length when modifying a contract's storage", async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - cachesOpts: { storageCacheOpts }, + caches: new Caches({ storageCacheOpts }), }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -64,7 +64,7 @@ describe('StateManager -> Storage', () => { it("should validate the key's length when reading a contract's storage", async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - cachesOpts: { storageCacheOpts }, + caches: new Caches({ storageCacheOpts }), }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -83,7 +83,7 @@ describe('StateManager -> Storage', () => { it(`should throw on storage values larger than 32 bytes`, async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - cachesOpts: { storageCacheOpts }, + caches: new Caches({ storageCacheOpts }), }) const address = createZeroAddress() const account = createAccountWithDefaults() @@ -102,7 +102,7 @@ describe('StateManager -> Storage', () => { it(`should strip zeros of storage values`, async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - cachesOpts: { storageCacheOpts }, + caches: new Caches({ storageCacheOpts }), }) const address = createZeroAddress() const account = createAccountWithDefaults() @@ -135,7 +135,7 @@ describe('StateManager -> Storage', () => { for (const length of zeroLengths) { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - cachesOpts: { storageCacheOpts }, + caches: new Caches({ storageCacheOpts }), }) const account = createAccountWithDefaults() await stateManager.putAccount(address, account) @@ -158,7 +158,7 @@ describe('StateManager -> Storage', () => { it(`should not strip trailing zeros`, async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - cachesOpts: { storageCacheOpts }, + caches: new Caches({ storageCacheOpts }), }) const address = createZeroAddress() const account = createAccountWithDefaults() diff --git a/packages/statemanager/test/statelessVerkleStateManager.spec.ts b/packages/statemanager/test/statelessVerkleStateManager.spec.ts index c45c9b7b3a..6a4ff6e2a9 100644 --- a/packages/statemanager/test/statelessVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statelessVerkleStateManager.spec.ts @@ -16,7 +16,7 @@ import { import { loadVerkleCrypto } from 'verkle-cryptography-wasm' import { assert, beforeAll, describe, it, test } from 'vitest' -import { CacheType, StatelessVerkleStateManager } from '../src/index.js' +import { CacheType, Caches, StatelessVerkleStateManager } from '../src/index.js' import * as testnetVerkleKaustinen from './testdata/testnetVerkleKaustinen.json' import * as verkleBlockJSON from './testdata/verkleKaustinen6Block72.json' @@ -67,7 +67,11 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { }) it('put/delete/modify account', async () => { - const stateManager = new StatelessVerkleStateManager({ common, verkleCrypto }) + const stateManager = new StatelessVerkleStateManager({ + common, + caches: new Caches(), + verkleCrypto, + }) stateManager.initVerkleExecutionWitness(block.header.number, block.executionWitness) const address = new Address(randomBytes(20)) @@ -144,28 +148,28 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { it(`copy()`, async () => { const stateManager = new StatelessVerkleStateManager({ - cachesOpts: { + caches: new Caches({ accountCacheOpts: { type: CacheType.ORDERED_MAP, }, storageCacheOpts: { type: CacheType.ORDERED_MAP, }, - }, + }), common, verkleCrypto, }) stateManager.initVerkleExecutionWitness(block.header.number, block.executionWitness) - const stateManagerCopy = stateManager.shallowCopy() as StatelessVerkleStateManager + const stateManagerCopy = stateManager.shallowCopy() assert.equal( - stateManagerCopy['_caches'].settings.account.type, + stateManagerCopy['_caches']?.settings.account.type, CacheType.ORDERED_MAP, 'should switch to ORDERED_MAP account cache on copy()', ) assert.equal( - stateManagerCopy['_caches'].settings.storage.type, + stateManagerCopy['_caches']?.settings.storage.type, CacheType.ORDERED_MAP, 'should switch to ORDERED_MAP storage cache on copy()', ) From 5e1666d97d424be8c9bf27b2793dcd269098c90c Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 14:39:59 -0400 Subject: [PATCH 11/23] client: adjust vm execution with update cache --- packages/client/src/execution/vmexecution.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index dcdf1e86ca..bc391dc897 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -9,6 +9,7 @@ import { MCLBLS, RustBN254 } from '@ethereumjs/evm' import { getGenesis } from '@ethereumjs/genesis' import { CacheType, + Caches, DefaultStateManager, StatelessVerkleStateManager, } from '@ethereumjs/statemanager' @@ -164,7 +165,7 @@ export class VMExecution extends Execution { const stateManager = new DefaultStateManager({ trie, prefixStorageTrieKeys: this.config.prefixStorageTrieKeys, - cachesOpts: { + caches: new Caches({ accountCacheOpts: { deactivate: false, type: CacheType.LRU, @@ -180,7 +181,7 @@ export class VMExecution extends Execution { type: CacheType.LRU, size: this.config.codeCache, }, - }, + }), common: this.config.chainCommon, }) From c754eaca47892dadac2a43bb7ad02882d9b9924f Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 14:54:55 -0400 Subject: [PATCH 12/23] vm: fix vm tests --- packages/vm/src/vm.ts | 7 +++++-- packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/vm/src/vm.ts b/packages/vm/src/vm.ts index a0af8bb891..d523f1ac28 100644 --- a/packages/vm/src/vm.ts +++ b/packages/vm/src/vm.ts @@ -1,7 +1,7 @@ import { createBlockchain } from '@ethereumjs/blockchain' import { Common, Mainnet } from '@ethereumjs/common' import { createEVM, getActivePrecompiles } from '@ethereumjs/evm' -import { DefaultStateManager } from '@ethereumjs/statemanager' +import { Caches, DefaultStateManager } from '@ethereumjs/statemanager' import { Account, Address, @@ -82,7 +82,10 @@ export class VM { } if (opts.stateManager === undefined) { - opts.stateManager = new DefaultStateManager({ common: opts.common }) + opts.stateManager = new DefaultStateManager({ + caches: new Caches(), + common: opts.common, + }) } if (opts.blockchain === undefined) { diff --git a/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts b/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts index 2c3cbe5aa4..1a60eef6db 100644 --- a/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts +++ b/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts @@ -1,7 +1,7 @@ import { createBlock } from '@ethereumjs/block' import { Hardfork, Mainnet, createCustomCommon } from '@ethereumjs/common' import { createEVM } from '@ethereumjs/evm' -import { StatelessVerkleStateManager } from '@ethereumjs/statemanager' +import { Caches, StatelessVerkleStateManager } from '@ethereumjs/statemanager' import { createTxFromSerializedData } from '@ethereumjs/tx' import { hexToBytes } from '@ethereumjs/util' import { loadVerkleCrypto } from 'verkle-cryptography-wasm' @@ -33,7 +33,11 @@ const block = createBlock({ ...verkleBlockJSON, transactions: decodedTxs } as Bl describe('EIP 6800 tests', () => { it('successfully run transactions statelessly using the block witness', async () => { const verkleCrypto = await loadVerkleCrypto() - const verkleStateManager = new StatelessVerkleStateManager({ common, verkleCrypto }) + const verkleStateManager = new StatelessVerkleStateManager({ + caches: new Caches(), + common, + verkleCrypto, + }) const evm = await createEVM({ common, stateManager: verkleStateManager }) const vm = await VM.create({ common, From 5d4895c1ff867f3fcf5a1c90b1e0ce16657cac2f Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 15:07:26 -0400 Subject: [PATCH 13/23] vm: adjust test runners with non-default caches --- packages/vm/test/tester/runners/BlockchainTestsRunner.ts | 3 ++- packages/vm/test/tester/runners/GeneralStateTestsRunner.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index 92e20239fd..a33b167960 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -3,7 +3,7 @@ import { EthashConsensus, createBlockchain } from '@ethereumjs/blockchain' import { ConsensusAlgorithm } from '@ethereumjs/common' import { Ethash } from '@ethereumjs/ethash' import { RLP } from '@ethereumjs/rlp' -import { DefaultStateManager } from '@ethereumjs/statemanager' +import { Caches, DefaultStateManager } from '@ethereumjs/statemanager' import { Trie } from '@ethereumjs/trie' import { createTxFromSerializedData } from '@ethereumjs/tx' import { @@ -49,6 +49,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes let cacheDB = new MapDB() let state = new Trie({ useKeyHashing: true, common }) let stateManager = new DefaultStateManager({ + caches: new Caches(), trie: state, common, }) diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index 992cc6c11c..fe715a1cbd 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -1,7 +1,7 @@ import { Block } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' import { type InterpreterStep } from '@ethereumjs/evm' -import { DefaultStateManager } from '@ethereumjs/statemanager' +import { Caches, DefaultStateManager } from '@ethereumjs/statemanager' import { Trie } from '@ethereumjs/trie' import { Account, @@ -81,6 +81,7 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { const blockchain = await createBlockchain({ genesisBlock, common }) const state = new Trie({ useKeyHashing: true, common }) const stateManager = new DefaultStateManager({ + caches: new Caches(), trie: state, common, }) From 888b20eee8e757198367fef34454c9fdd8530ec6 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 16:58:16 -0400 Subject: [PATCH 14/23] statemanager: simplify handling of deactivate --- packages/statemanager/src/stateManager.ts | 133 ++++++++---------- .../src/statelessVerkleStateManager.ts | 66 ++++----- 2 files changed, 83 insertions(+), 116 deletions(-) diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 29e7cc248a..12f5b89de0 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -123,11 +123,9 @@ export class DefaultStateManager implements StateManagerInterface { * @param address - Address of the `account` to get */ async getAccount(address: Address): Promise { - if (this._caches !== undefined && !this._caches.settings.account.deactivate) { - const elem = this._caches.account!.get(address) - if (elem !== undefined) { - return elem.accountRLP !== undefined ? createAccountFromRLP(elem.accountRLP) : undefined - } + const elem = this._caches?.account?.get(address) + if (elem !== undefined) { + return elem.accountRLP !== undefined ? createAccountFromRLP(elem.accountRLP) : undefined } const rlp = await this._trie.get(address.bytes) @@ -154,7 +152,7 @@ export class DefaultStateManager implements StateManagerInterface { }`, ) } - if (this._caches === undefined || this._caches.settings.account.deactivate) { + if (this._caches?.account === undefined) { const trie = this._trie if (account !== undefined) { await trie.put(address.bytes, account.serialize()) @@ -163,9 +161,9 @@ export class DefaultStateManager implements StateManagerInterface { } } else { if (account !== undefined) { - this._caches.account!.put(address, account) + this._caches.account?.put(address, account) } else { - this._caches.account!.del(address) + this._caches.account?.del(address) } } } @@ -192,7 +190,7 @@ export class DefaultStateManager implements StateManagerInterface { this._caches?.deleteAccount(address) - if (this._caches === undefined || this._caches.settings.account.deactivate === true) { + if (this._caches?.account === undefined) { await this._trie.del(address.bytes) } } @@ -224,11 +222,9 @@ export class DefaultStateManager implements StateManagerInterface { * Returns an empty `Uint8Array` if the account has no associated code. */ async getCode(address: Address): Promise { - if (this._caches !== undefined && !this._caches.settings.code.deactivate) { - const elem = this._caches.code?.get(address) - if (elem !== undefined) { - return elem.code ?? new Uint8Array(0) - } + const elem = this._caches?.code?.get(address) + if (elem !== undefined) { + return elem.code ?? new Uint8Array(0) } const account = await this.getAccount(address) if (!account) { @@ -242,9 +238,7 @@ export class DefaultStateManager implements StateManagerInterface { : account.codeHash const code = (await this._trie.database().get(key)) ?? new Uint8Array(0) - if (this._caches !== undefined && !this._caches.settings.code.deactivate) { - this._caches.code!.put(address, code) - } + this._caches?.code?.put(address, code) return code } @@ -324,12 +318,10 @@ export class DefaultStateManager implements StateManagerInterface { if (key.length !== 32) { throw new Error('Storage key must be 32 bytes long') } - if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { - const value = this._caches.storage!.get(address, key) - if (value !== undefined) { - const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array - return decoded - } + const cachedValue = this._caches?.storage?.get(address, key) + if (cachedValue !== undefined) { + const decoded = RLP.decode(cachedValue ?? new Uint8Array(0)) as Uint8Array + return decoded } const account = await this.getAccount(address) @@ -338,9 +330,7 @@ export class DefaultStateManager implements StateManagerInterface { } const trie = this._getStorageTrie(address, account) const value = await trie.get(key) - if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { - this._caches.storage?.put(address, key, value ?? hexToBytes('0x80')) - } + this._caches?.storage?.put(address, key, value ?? hexToBytes('0x80')) const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array return decoded } @@ -422,12 +412,8 @@ export class DefaultStateManager implements StateManagerInterface { } value = unpadBytes(value) - if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { - const encodedValue = RLP.encode(value) - this._caches.storage!.put(address, key, encodedValue) - } else { - await this._writeContractStorage(address, account, key, value) - } + this._caches?.storage?.put(address, key, RLP.encode(value)) ?? + (await this._writeContractStorage(address, account, key, value)) } /** @@ -500,56 +486,51 @@ export class DefaultStateManager implements StateManagerInterface { * Writes all cache items to the trie */ async flush(): Promise { - if (this._caches !== undefined && !this._caches.settings.code.deactivate) { - const items = this._caches.code!.flush() - for (const item of items) { - const addr = createAddressFromString(`0x${item[0]}`) + const codeItems = this._caches?.code?.flush() ?? [] + for (const item of codeItems) { + const addr = createAddressFromString(`0x${item[0]}`) - const code = item[1].code - if (code === undefined) { - continue - } + const code = item[1].code + if (code === undefined) { + continue + } - // update code in database - const codeHash = this.keccakFunction(code) - const key = this._prefixCodeHashes ? concatBytes(CODEHASH_PREFIX, codeHash) : codeHash - await this._getCodeDB().put(key, code) + // update code in database + const codeHash = this.keccakFunction(code) + const key = this._prefixCodeHashes ? concatBytes(CODEHASH_PREFIX, codeHash) : codeHash + await this._getCodeDB().put(key, code) - // update code root of associated account - if ((await this.getAccount(addr)) === undefined) { - await this.putAccount(addr, new Account()) - } - await this.modifyAccountFields(addr, { codeHash }) + // update code root of associated account + if ((await this.getAccount(addr)) === undefined) { + await this.putAccount(addr, new Account()) } - } - if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { - const items = this._caches.storage!.flush() - for (const item of items) { - const address = createAddressFromString(`0x${item[0]}`) - const keyHex = item[1] - const keyBytes = unprefixedHexToBytes(keyHex) - const value = item[2] - - const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array - const account = await this.getAccount(address) - if (account) { - await this._writeContractStorage(address, account, keyBytes, decoded) - } + await this.modifyAccountFields(addr, { codeHash }) + } + const storageItems = this._caches?.storage?.flush() ?? [] + for (const item of storageItems) { + const address = createAddressFromString(`0x${item[0]}`) + const keyHex = item[1] + const keyBytes = unprefixedHexToBytes(keyHex) + const value = item[2] + + const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array + const account = await this.getAccount(address) + if (account) { + await this._writeContractStorage(address, account, keyBytes, decoded) } } - if (this._caches !== undefined && !this._caches.settings.account.deactivate) { - const items = this._caches.account!.flush() - for (const item of items) { - const addressHex = item[0] - const addressBytes = unprefixedHexToBytes(addressHex) - const elem = item[1] - if (elem.accountRLP === undefined) { - const trie = this._trie - await trie.del(addressBytes) - } else { - const trie = this._trie - await trie.put(addressBytes, elem.accountRLP) - } + + const accountItems = this._caches?.account?.flush() ?? [] + for (const item of accountItems) { + const addressHex = item[0] + const addressBytes = unprefixedHexToBytes(addressHex) + const elem = item[1] + if (elem.accountRLP === undefined) { + const trie = this._trie + await trie.del(addressBytes) + } else { + const trie = this._trie + await trie.put(addressBytes, elem.accountRLP) } } } diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 8b38d9ee7b..89213c09f8 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -249,11 +249,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`getCode address=${address.toString()}`) } - if (this._caches !== undefined && !this._caches.settings.code.deactivate) { - const elem = this._caches.code?.get(address) - if (elem !== undefined) { - return elem.code ?? new Uint8Array(0) - } + const elem = this._caches?.code?.get(address) + if (elem !== undefined) { + return elem.code ?? new Uint8Array(0) } const account = await this.getAccount(address) @@ -296,26 +294,22 @@ export class StatelessVerkleStateManager implements StateManagerInterface { // Return accessedCode where only accessed code has been copied const contactCode = accessedCode.slice(0, codeSize) - if (this._caches !== undefined && !this._caches.settings.code.deactivate) { - this._caches.code?.put(address, contactCode) - } + this._caches?.code?.put(address, contactCode) return contactCode } async getCodeSize(address: Address): Promise { - if (this._caches !== undefined && !this._caches.settings.account.deactivate) { - const elem = this._caches.account!.get(address) - if (elem !== undefined) { - const account = - elem.accountRLP !== undefined ? createPartialAccountFromRLP(elem.accountRLP) : undefined - if (account === undefined) { - const errorMsg = `account=${account} in cache` - debug(errorMsg) - throw Error(errorMsg) - } - return account.codeSize + const elem = this._caches?.account?.get(address) + if (elem !== undefined) { + const account = + elem.accountRLP !== undefined ? createPartialAccountFromRLP(elem.accountRLP) : undefined + if (account === undefined) { + const errorMsg = `account=${account} in cache` + debug(errorMsg) + throw Error(errorMsg) } + return account.codeSize } // load the account basic fields and codeSize should be in it @@ -336,11 +330,9 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * If this does not exist an empty `Uint8Array` is returned. */ async getStorage(address: Address, key: Uint8Array): Promise { - if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { - const value = this._caches.storage!.get(address, key) - if (value !== undefined) { - return value - } + const value = this._caches?.storage?.get(address, key) + if (value !== undefined) { + return value } const storageKey = await getVerkleTreeKeyForStorageSlot( @@ -350,9 +342,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { ) const storageValue = toBytes(this._state[bytesToHex(storageKey)]) - if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { - this._caches.storage?.put(address, key, storageValue ?? hexToBytes('0x80')) - } + this._caches?.storage?.put(address, key, storageValue ?? hexToBytes('0x80')) return storageValue } @@ -365,8 +355,8 @@ export class StatelessVerkleStateManager implements StateManagerInterface { * @param value - Value to set at `key` for account corresponding to `address`. Cannot be more than 32 bytes. Leading zeros are stripped. If it is a empty or filled with zeros, deletes the value. */ async putStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise { - if (this._caches !== undefined && !this._caches.settings.storage.deactivate) { - this._caches.storage!.put(address, key, value) + if (this._caches?.storage !== undefined) { + this._caches.storage.put(address, key, value) } else { // TODO: Consider refactoring this in a writeContractStorage function? Like in stateManager.ts const storageKey = await getVerkleTreeKeyForStorageSlot( @@ -393,13 +383,11 @@ export class StatelessVerkleStateManager implements StateManagerInterface { } async getAccount(address: Address): Promise { - if (this._caches !== undefined && !this._caches.settings.account.deactivate) { - const elem = this._caches.account!.get(address) - if (elem !== undefined) { - return elem.accountRLP !== undefined - ? createPartialAccountFromRLP(elem.accountRLP) - : undefined - } + const elem = this._caches?.account?.get(address) + if (elem !== undefined) { + return elem.accountRLP !== undefined + ? createPartialAccountFromRLP(elem.accountRLP) + : undefined } const stem = getVerkleStem(this.verkleCrypto, address, 0) @@ -476,9 +464,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`getAccount address=${address.toString()} stem=${short(stem)}`) } - if (this._caches !== undefined && !this._caches.settings.account.deactivate) { - this._caches.account?.put(address, account, true) - } + this._caches?.account?.put(address, account, true) return account } @@ -488,7 +474,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { debug(`putAccount address=${address.toString()}`) } - if (this._caches !== undefined && this._caches.settings.account.deactivate) { + if (this._caches?.account === undefined) { const stem = getVerkleStem(this.verkleCrypto, address, 0) const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance) const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce) From 905a52e0cf03ac86d1a51fadeca756430a0bac0b Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 17:26:13 -0400 Subject: [PATCH 15/23] statemanager: remove redundant checks --- packages/statemanager/src/stateManager.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 12f5b89de0..0115c69199 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -922,23 +922,15 @@ export class DefaultStateManager implements StateManagerInterface { const prefixCodeHashes = this._prefixCodeHashes const prefixStorageTrieKeys = this._prefixStorageTrieKeys let accountCacheOpts = { ...this._caches?.settings.account } - if ( - downlevelCaches && - this._caches !== undefined && - !this._caches.settings.account.deactivate - ) { + if (downlevelCaches && this._caches?.settings.account.deactivate === false) { accountCacheOpts = { ...accountCacheOpts, type: CacheType.ORDERED_MAP } } let storageCacheOpts = { ...this._caches?.settings.storage } - if ( - downlevelCaches && - this._caches !== undefined && - !this._caches.settings.storage.deactivate - ) { + if (downlevelCaches && this._caches?.settings.storage.deactivate === false) { storageCacheOpts = { ...storageCacheOpts, type: CacheType.ORDERED_MAP } } let codeCacheOpts = { ...this._caches?.settings.code } - if (this._caches !== undefined && !this._caches.settings.code.deactivate) { + if (this._caches?.settings.code.deactivate === false) { codeCacheOpts = { ...codeCacheOpts, type: CacheType.ORDERED_MAP } } @@ -947,7 +939,7 @@ export class DefaultStateManager implements StateManagerInterface { trie, prefixStorageTrieKeys, prefixCodeHashes, - caches: new Caches({ accountCacheOpts, storageCacheOpts, codeCacheOpts }), + caches: new Caches({ accountCacheOpts, codeCacheOpts, storageCacheOpts }), }) } From f30288abed070f80058532916e51c24e4c15d875 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Fri, 9 Aug 2024 17:36:46 -0400 Subject: [PATCH 16/23] statemanager: remove non null asesertion --- packages/statemanager/src/statelessVerkleStateManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index 89213c09f8..0b19a3bad6 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -536,7 +536,7 @@ export class StatelessVerkleStateManager implements StateManagerInterface { // switch to false if postVerify fails let postFailures = 0 - for (const accessedState of this.accessWitness!.accesses()) { + for (const accessedState of this.accessWitness?.accesses() ?? []) { const { address, type } = accessedState let extraMeta = '' if (accessedState.type === AccessedStateType.Code) { From ba00837f70404e68e0544f529f6d6c72c51512c4 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sat, 10 Aug 2024 09:45:12 -0400 Subject: [PATCH 17/23] statemanager: remove cache opt from key naming --- packages/client/src/execution/vmexecution.ts | 6 ++--- packages/statemanager/src/cache/caches.ts | 21 +++++++--------- packages/statemanager/src/cache/types.ts | 6 ++--- packages/statemanager/src/stateManager.ts | 6 ++++- .../test/stateManager.account.spec.ts | 24 ++++++++++++++----- .../test/stateManager.code.spec.ts | 24 ++++++++++++++----- .../statemanager/test/stateManager.spec.ts | 4 ++-- .../test/stateManager.storage.spec.ts | 14 +++++------ .../test/statelessVerkleStateManager.spec.ts | 4 ++-- 9 files changed, 67 insertions(+), 42 deletions(-) diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index bc391dc897..07635aa376 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -166,17 +166,17 @@ export class VMExecution extends Execution { trie, prefixStorageTrieKeys: this.config.prefixStorageTrieKeys, caches: new Caches({ - accountCacheOpts: { + account: { deactivate: false, type: CacheType.LRU, size: this.config.accountCache, }, - storageCacheOpts: { + storage: { deactivate: false, type: CacheType.LRU, size: this.config.storageCache, }, - codeCacheOpts: { + code: { deactivate: false, type: CacheType.LRU, size: this.config.codeCache, diff --git a/packages/statemanager/src/cache/caches.ts b/packages/statemanager/src/cache/caches.ts index 80270eefca..5aedfb34de 100644 --- a/packages/statemanager/src/cache/caches.ts +++ b/packages/statemanager/src/cache/caches.ts @@ -14,23 +14,20 @@ export class Caches { constructor(opts: CachesStateManagerOpts = {}) { const accountSettings = { - deactivate: - (opts.accountCacheOpts?.deactivate === true || opts.accountCacheOpts?.size === 0) ?? false, - type: opts.accountCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.accountCacheOpts?.size ?? 100000, + deactivate: (opts.account?.deactivate === true || opts.account?.size === 0) ?? false, + type: opts.account?.type ?? CacheType.ORDERED_MAP, + size: opts.account?.size ?? 100000, } const storageSettings = { - deactivate: - (opts.storageCacheOpts?.deactivate === true || opts.storageCacheOpts?.size === 0) ?? false, - type: opts.storageCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.storageCacheOpts?.size ?? 20000, + deactivate: (opts.storage?.deactivate === true || opts.storage?.size === 0) ?? false, + type: opts.storage?.type ?? CacheType.ORDERED_MAP, + size: opts.storage?.size ?? 20000, } const codeSettings = { - deactivate: - (opts.codeCacheOpts?.deactivate === true || opts.codeCacheOpts?.size === 0) ?? false, - type: opts.codeCacheOpts?.type ?? CacheType.ORDERED_MAP, - size: opts.codeCacheOpts?.size ?? 20000, + deactivate: (opts.code?.deactivate === true || opts.code?.size === 0) ?? false, + type: opts.code?.type ?? CacheType.ORDERED_MAP, + size: opts.code?.size ?? 20000, } this.settings = { diff --git a/packages/statemanager/src/cache/types.ts b/packages/statemanager/src/cache/types.ts index 2208d109be..909e5c68dc 100644 --- a/packages/statemanager/src/cache/types.ts +++ b/packages/statemanager/src/cache/types.ts @@ -52,7 +52,7 @@ export interface CacheStateManagerOpts { } export interface CachesStateManagerOpts { - accountCacheOpts?: CacheStateManagerOpts - codeCacheOpts?: CacheStateManagerOpts - storageCacheOpts?: CacheStateManagerOpts + account?: CacheStateManagerOpts + code?: CacheStateManagerOpts + storage?: CacheStateManagerOpts } diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 0115c69199..23af18d808 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -939,7 +939,11 @@ export class DefaultStateManager implements StateManagerInterface { trie, prefixStorageTrieKeys, prefixCodeHashes, - caches: new Caches({ accountCacheOpts, codeCacheOpts, storageCacheOpts }), + caches: new Caches({ + account: accountCacheOpts, + code: codeCacheOpts, + storage: storageCacheOpts, + }), }) } diff --git a/packages/statemanager/test/stateManager.account.spec.ts b/packages/statemanager/test/stateManager.account.spec.ts index ed2c4d18f9..11dd50a840 100644 --- a/packages/statemanager/test/stateManager.account.spec.ts +++ b/packages/statemanager/test/stateManager.account.spec.ts @@ -12,7 +12,9 @@ describe('StateManager -> General/Account', () => { { deactivate: false, size: 0 }, ]) { it(`should set the state root to empty`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) assert.ok(equalsBytes(stateManager['_trie'].root(), KECCAK256_RLP), 'it has default root') // commit some data to the trie @@ -32,7 +34,9 @@ describe('StateManager -> General/Account', () => { }) it(`should clear the cache when the state root is set`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -75,7 +79,9 @@ describe('StateManager -> General/Account', () => { }) it('should put and get account, and add to the underlying cache if the account is not found', async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const account = createAccountWithDefaults() const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) @@ -94,7 +100,9 @@ describe('StateManager -> General/Account', () => { }) it(`should return undefined for a non-existent account`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const res = (await stateManager.getAccount(address)) === undefined @@ -103,7 +111,9 @@ describe('StateManager -> General/Account', () => { }) it(`should return undefined for an existent account`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const account = createAccountWithDefaults(BigInt(0x1), BigInt(0x1)) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) @@ -115,7 +125,9 @@ describe('StateManager -> General/Account', () => { }) it(`should modify account fields correctly`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const account = createAccountWithDefaults() const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) await stateManager.putAccount(address, account) diff --git a/packages/statemanager/test/stateManager.code.spec.ts b/packages/statemanager/test/stateManager.code.spec.ts index 023afef9d2..146464e7a0 100644 --- a/packages/statemanager/test/stateManager.code.spec.ts +++ b/packages/statemanager/test/stateManager.code.spec.ts @@ -31,8 +31,12 @@ describe('StateManager -> Code', () => { */ // Setup - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) - const codeStateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) + const codeStateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const address1 = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() const key1 = hexToBytes(`0x${'00'.repeat(32)}`) @@ -87,7 +91,9 @@ describe('StateManager -> Code', () => { }) it(`should set and get code`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const code = hexToBytes( '0x73095e7baea6a6c7c4c2dfeb977efac326af552d873173095e7baea6a6c7c4c2dfeb977efac326af552d873157', @@ -105,7 +111,9 @@ describe('StateManager -> Code', () => { }) it(`should not get code if is not contract`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const raw: AccountData = { nonce: '0x0', @@ -118,7 +126,9 @@ describe('StateManager -> Code', () => { }) it(`should set empty code`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const raw: AccountData = { nonce: '0x0', @@ -133,7 +143,9 @@ describe('StateManager -> Code', () => { }) it(`should prefix codehashes by default`, async () => { - const stateManager = new DefaultStateManager({ caches: new Caches({ accountCacheOpts }) }) + const stateManager = new DefaultStateManager({ + caches: new Caches({ account: accountCacheOpts }), + }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const code = hexToBytes('0x80') await stateManager.putCode(address, code) diff --git a/packages/statemanager/test/stateManager.spec.ts b/packages/statemanager/test/stateManager.spec.ts index 5193adbdd5..2ec8bbb856 100644 --- a/packages/statemanager/test/stateManager.spec.ts +++ b/packages/statemanager/test/stateManager.spec.ts @@ -83,10 +83,10 @@ describe('StateManager -> General', () => { sm = new DefaultStateManager({ trie, caches: new Caches({ - accountCacheOpts: { + account: { type: CacheType.LRU, }, - storageCacheOpts: { + storage: { type: CacheType.LRU, }, }), diff --git a/packages/statemanager/test/stateManager.storage.spec.ts b/packages/statemanager/test/stateManager.storage.spec.ts index 32d4d3e11c..35a81336bb 100644 --- a/packages/statemanager/test/stateManager.storage.spec.ts +++ b/packages/statemanager/test/stateManager.storage.spec.ts @@ -26,7 +26,7 @@ describe('StateManager -> Storage', () => { it.skipIf(isBrowser() === true)(`should dump storage`, async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - caches: new Caches({ storageCacheOpts }), + caches: new Caches({ storage: storageCacheOpts }), }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -45,7 +45,7 @@ describe('StateManager -> Storage', () => { it("should validate the key's length when modifying a contract's storage", async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - caches: new Caches({ storageCacheOpts }), + caches: new Caches({ storage: storageCacheOpts }), }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -64,7 +64,7 @@ describe('StateManager -> Storage', () => { it("should validate the key's length when reading a contract's storage", async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - caches: new Caches({ storageCacheOpts }), + caches: new Caches({ storage: storageCacheOpts }), }) const address = new Address(hexToBytes('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccountWithDefaults() @@ -83,7 +83,7 @@ describe('StateManager -> Storage', () => { it(`should throw on storage values larger than 32 bytes`, async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - caches: new Caches({ storageCacheOpts }), + caches: new Caches({ storage: storageCacheOpts }), }) const address = createZeroAddress() const account = createAccountWithDefaults() @@ -102,7 +102,7 @@ describe('StateManager -> Storage', () => { it(`should strip zeros of storage values`, async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - caches: new Caches({ storageCacheOpts }), + caches: new Caches({ storage: storageCacheOpts }), }) const address = createZeroAddress() const account = createAccountWithDefaults() @@ -135,7 +135,7 @@ describe('StateManager -> Storage', () => { for (const length of zeroLengths) { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - caches: new Caches({ storageCacheOpts }), + caches: new Caches({ storage: storageCacheOpts }), }) const account = createAccountWithDefaults() await stateManager.putAccount(address, account) @@ -158,7 +158,7 @@ describe('StateManager -> Storage', () => { it(`should not strip trailing zeros`, async () => { const stateManager = new DefaultStateManager({ prefixStorageTrieKeys, - caches: new Caches({ storageCacheOpts }), + caches: new Caches({ storage: storageCacheOpts }), }) const address = createZeroAddress() const account = createAccountWithDefaults() diff --git a/packages/statemanager/test/statelessVerkleStateManager.spec.ts b/packages/statemanager/test/statelessVerkleStateManager.spec.ts index 6a4ff6e2a9..6fc153efae 100644 --- a/packages/statemanager/test/statelessVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statelessVerkleStateManager.spec.ts @@ -149,10 +149,10 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { it(`copy()`, async () => { const stateManager = new StatelessVerkleStateManager({ caches: new Caches({ - accountCacheOpts: { + account: { type: CacheType.ORDERED_MAP, }, - storageCacheOpts: { + storage: { type: CacheType.ORDERED_MAP, }, }), From c037ae21cd65d314c1f9070e455a48b38bd90ac5 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 11 Aug 2024 08:37:17 -0400 Subject: [PATCH 18/23] statemanager: refactor rpc state manager to use caches --- packages/statemanager/src/rpcStateManager.ts | 73 ++++++++------------ 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/packages/statemanager/src/rpcStateManager.ts b/packages/statemanager/src/rpcStateManager.ts index d551f3dfda..6fd3be8985 100644 --- a/packages/statemanager/src/rpcStateManager.ts +++ b/packages/statemanager/src/rpcStateManager.ts @@ -16,7 +16,7 @@ import { import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import { AccountCache, CacheType, OriginalStorageCache, StorageCache } from './cache/index.js' +import { Caches, OriginalStorageCache } from './cache/index.js' import { modifyAccountFields } from './util.js' import type { Proof, RPCStateManagerOpts } from './index.js' @@ -28,10 +28,8 @@ const KECCAK256_RLP_EMPTY_ACCOUNT = RLP.encode(new Account().serialize()).slice( export class RPCStateManager implements StateManagerInterface { protected _provider: string - protected _contractCache: Map - protected _storageCache: StorageCache + protected _caches: Caches protected _blockTag: string - protected _accountCache: AccountCache originalStorageCache: OriginalStorageCache protected _debug: Debugger protected DEBUG: boolean @@ -53,9 +51,11 @@ export class RPCStateManager implements StateManagerInterface { this._blockTag = opts.blockTag === 'earliest' ? opts.blockTag : bigIntToHex(opts.blockTag) - this._contractCache = new Map() - this._storageCache = new StorageCache({ size: 100000, type: CacheType.ORDERED_MAP }) - this._accountCache = new AccountCache({ size: 100000, type: CacheType.ORDERED_MAP }) + this._caches = new Caches({ storage: { size: 100000 }, code: { size: 100000 } }) + + // this._contractCache = new Map() + // this._storageCache = new StorageCache({ size: 100000, type: CacheType.ORDERED_MAP }) + // this._accountCache = new AccountCache({ size: 100000, type: CacheType.ORDERED_MAP }) this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this)) this.common = opts.common ?? new Common({ chain: Mainnet }) @@ -72,15 +72,8 @@ export class RPCStateManager implements StateManagerInterface { provider: this._provider, blockTag: BigInt(this._blockTag), }) - newState._contractCache = new Map(this._contractCache) - newState._storageCache = new StorageCache({ - size: 100000, - type: CacheType.ORDERED_MAP, - }) - newState._accountCache = new AccountCache({ - size: 100000, - type: CacheType.ORDERED_MAP, - }) + newState._caches = new Caches({ storage: { size: 100000 } }) + return newState } @@ -100,9 +93,7 @@ export class RPCStateManager implements StateManagerInterface { * initially be retrieved from the provider */ clearCaches(): void { - this._contractCache.clear() - this._storageCache.clear() - this._accountCache.clear() + this._caches.clear() } /** @@ -112,14 +103,14 @@ export class RPCStateManager implements StateManagerInterface { * Returns an empty `Uint8Array` if the account has no associated code. */ async getCode(address: Address): Promise { - let codeBytes = this._contractCache.get(address.toString()) + let codeBytes = this._caches.code?.get(address)?.code if (codeBytes !== undefined) return codeBytes const code = await fetchFromProvider(this._provider, { method: 'eth_getCode', params: [address.toString(), this._blockTag], }) codeBytes = toBytes(code) - this._contractCache.set(address.toString(), codeBytes) + this._caches.code?.put(address, codeBytes) return codeBytes } @@ -136,7 +127,7 @@ export class RPCStateManager implements StateManagerInterface { */ async putCode(address: Address, value: Uint8Array): Promise { // Store contract code in the cache - this._contractCache.set(address.toString(), value) + this._caches.code?.put(address, value) } /** @@ -154,7 +145,7 @@ export class RPCStateManager implements StateManagerInterface { throw new Error('Storage key must be 32 bytes long') } - let value = this._storageCache!.get(address, key) + let value = this._caches.storage?.get(address, key) if (value !== undefined) { return value } @@ -180,7 +171,7 @@ export class RPCStateManager implements StateManagerInterface { * If it is empty or filled with zeros, deletes the value. */ async putStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise { - this._storageCache.put(address, key, value) + this._caches.storage?.put(address, key, value) } /** @@ -188,7 +179,7 @@ export class RPCStateManager implements StateManagerInterface { * @param address - Address to clear the storage of */ async clearStorage(address: Address): Promise { - this._storageCache.clearStorage(address) + this._caches.storage?.clearStorage(address) } /** @@ -199,7 +190,7 @@ export class RPCStateManager implements StateManagerInterface { * Both are represented as `0x` prefixed hex strings. */ dumpStorage(address: Address): Promise { - const storageMap = this._storageCache.dump(address) + const storageMap = this._caches.storage?.dump(address) const dump: StorageDump = {} if (storageMap !== undefined) { for (const slot of storageMap) { @@ -216,7 +207,7 @@ export class RPCStateManager implements StateManagerInterface { async accountExists(address: Address): Promise { if (this.DEBUG) this._debug?.(`verify if ${address.toString()} exists`) - const localAccount = this._accountCache.get(address) + const localAccount = this._caches.account?.get(address) if (localAccount !== undefined) return true // Get merkle proof for `address` from provider const proof = await fetchFromProvider(this._provider, { @@ -238,7 +229,7 @@ export class RPCStateManager implements StateManagerInterface { * @param address - Address of the `account` to get */ async getAccount(address: Address): Promise { - const elem = this._accountCache?.get(address) + const elem = this._caches.account?.get(address) if (elem !== undefined) { return elem.accountRLP !== undefined ? createAccountFromRLP(elem.accountRLP) : undefined } @@ -250,7 +241,7 @@ export class RPCStateManager implements StateManagerInterface { ? undefined : createAccountFromRLP(accountFromProvider.serialize()) - this._accountCache?.put(address, account) + this._caches.account?.put(address, account) return account } @@ -291,9 +282,9 @@ export class RPCStateManager implements StateManagerInterface { ) } if (account !== undefined) { - this._accountCache!.put(address, account) + this._caches.account!.put(address, account) } else { - this._accountCache!.del(address) + this._caches.account!.del(address) } } @@ -329,7 +320,7 @@ export class RPCStateManager implements StateManagerInterface { if (this.DEBUG) { this._debug(`deleting account corresponding to ${address.toString()}`) } - this._accountCache.del(address) + this._caches.account?.del(address) } /** @@ -344,9 +335,10 @@ export class RPCStateManager implements StateManagerInterface { method: 'eth_getProof', params: [ address.toString(), - [storageSlots.map((slot) => bytesToHex(slot))], + // TODO: Investigate why this works (either the type should be adjusted, or this is broken) + [storageSlots.map((slot) => bytesToHex(slot))] as any, this._blockTag, - ] as any, + ], }) return proof @@ -366,12 +358,9 @@ export class RPCStateManager implements StateManagerInterface { * Checkpoints the current state of the StateManager instance. * State changes that follow can then be committed by calling * `commit` or `reverted` by calling rollback. - * - * Partial implementation, called from the subclass. */ async checkpoint(): Promise { - this._accountCache.checkpoint() - this._storageCache.checkpoint() + this._caches.checkpoint() } /** @@ -382,7 +371,7 @@ export class RPCStateManager implements StateManagerInterface { */ async commit(): Promise { // setup cache checkpointing - this._accountCache.commit() + this._caches.account?.commit() } /** @@ -392,13 +381,11 @@ export class RPCStateManager implements StateManagerInterface { * Partial implementation , called from the subclass. */ async revert(): Promise { - this._accountCache.revert() - this._storageCache.revert() - this._contractCache.clear() + this._caches.revert() } async flush(): Promise { - this._accountCache.flush() + this._caches.account?.flush() } /** From fdcdb36523ef31746e6bd8ae5cc3a09d5b52250d Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 11 Aug 2024 08:42:23 -0400 Subject: [PATCH 19/23] statemanager: fix rpc state manager tests --- .../statemanager/test/rpcStateManager.spec.ts | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/packages/statemanager/test/rpcStateManager.spec.ts b/packages/statemanager/test/rpcStateManager.spec.ts index 5dc0ea890f..1213ed2ec6 100644 --- a/packages/statemanager/test/rpcStateManager.spec.ts +++ b/packages/statemanager/test/rpcStateManager.spec.ts @@ -47,18 +47,10 @@ describe('RPC State Manager initialization tests', async () => { it('should work', () => { let state = new RPCStateManager({ provider, blockTag: 1n }) assert.ok(state instanceof RPCStateManager, 'was able to instantiate state manager') - assert.equal( - (state as any)._blockTag, - '0x1', - 'State manager starts with default block tag of 1', - ) + assert.equal(state['_blockTag'], '0x1', 'State manager starts with default block tag of 1') state = new RPCStateManager({ provider, blockTag: 1n }) - assert.equal( - (state as any)._blockTag, - '0x1', - 'State Manager instantiated with predefined blocktag', - ) + assert.equal(state['_blockTag'], '0x1', 'State Manager instantiated with predefined blocktag') state = new RPCStateManager({ provider: 'https://google.com', blockTag: 1n }) assert.ok( @@ -87,7 +79,7 @@ describe('RPC State Manager API tests', () => { await state.putAccount(vitalikDotEth, account!) const retrievedVitalikAccount = createAccountFromRLP( - (state as any)._accountCache.get(vitalikDotEth)!.accountRLP, + state['_caches'].account?.get(vitalikDotEth)?.accountRLP!, ) assert.ok(retrievedVitalikAccount.nonce > 0n, 'Vitalik.eth is stored in cache') @@ -106,7 +98,7 @@ describe('RPC State Manager API tests', () => { await state.putCode(UNIerc20ContractAddress, UNIContractCode) assert.ok( - typeof (state as any)._contractCache.get(UNIerc20ContractAddress.toString()) !== 'undefined', + state['_caches'].code?.get(UNIerc20ContractAddress) !== undefined, 'UNI ERC20 contract code was found in cache', ) @@ -219,16 +211,16 @@ describe('RPC State Manager API tests', () => { } assert.equal( - (state as any)._contractCache.get(UNIerc20ContractAddress), + state['_caches'].account?.get(UNIerc20ContractAddress), undefined, 'should not have any code for contract after cache is reverted', ) - assert.equal((state as any)._blockTag, '0x1', 'blockTag defaults to 1') + assert.equal(state['_blockTag'], '0x1', 'blockTag defaults to 1') state.setBlockTag(5n) - assert.equal((state as any)._blockTag, '0x5', 'blockTag set to 0x5') + assert.equal(state['_blockTag'], '0x5', 'blockTag set to 0x5') state.setBlockTag('earliest') - assert.equal((state as any)._blockTag, 'earliest', 'blockTag set to earliest') + assert.equal(state['_blockTag'], 'earliest', 'blockTag set to earliest') await state.checkpoint() }) From 3d93587e125bc9eaba74fa0501efd54f3cc9bc3c Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 11 Aug 2024 10:15:44 -0400 Subject: [PATCH 20/23] client: vmexecution cache stats refactor --- packages/client/src/execution/vmexecution.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index 07635aa376..2f2ca4d7ad 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -40,7 +40,6 @@ import { ReceiptsManager } from './receipt.js' import type { ExecutionOptions } from './execution.js' import type { Block } from '@ethereumjs/block' -import type { Trie } from '@ethereumjs/trie' import type { PrefixedHexString } from '@ethereumjs/util' import type { RunBlockOpts, TxReceipt } from '@ethereumjs/vm' @@ -1066,25 +1065,22 @@ export class VMExecution extends Execution { stats() { if (this._statsVM instanceof DefaultStateManager) { - const sm = this._statsVM.stateManager as any - const disactivatedStats = { size: 0, reads: 0, hits: 0, writes: 0 } + const sm = this._statsVM.stateManager as DefaultStateManager + const deactivatedStats = { size: 0, reads: 0, hits: 0, writes: 0 } let stats - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - stats = !sm._accountCacheSettings.deactivate ? sm._accountCache.stats() : disactivatedStats + stats = sm['_caches']?.account?.stats() ?? deactivatedStats this.config.logger.info( `Account cache stats size=${stats.size} reads=${stats.reads} hits=${stats.hits} writes=${stats.writes}`, ) - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - stats = !sm._storageCacheSettings.deactivate ? sm._storageCache.stats() : disactivatedStats + stats = sm['_caches']?.storage?.stats() ?? deactivatedStats this.config.logger.info( `Storage cache stats size=${stats.size} reads=${stats.reads} hits=${stats.hits} writes=${stats.writes}`, ) - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - stats = !sm._codeCacheSettings.deactivate ? sm._codeCache.stats() : disactivatedStats + stats = sm['_caches']?.code?.stats() ?? deactivatedStats this.config.logger.info( `Code cache stats size=${stats.size} reads=${stats.reads} hits=${stats.hits} writes=${stats.writes}`, ) - const tStats = (sm._trie as Trie).database().stats() + const tStats = sm['_trie'].database().stats() this.config.logger.info( `Trie cache stats size=${tStats.size} reads=${tStats.cache.reads} hits=${tStats.cache.hits} ` + `writes=${tStats.cache.writes} readsDB=${tStats.db.reads} hitsDB=${tStats.db.hits} writesDB=${tStats.db.writes}`, From 5f4c253b56e0b8aeee552d541de8e2ecd782d27c Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Mon, 12 Aug 2024 11:45:28 -0400 Subject: [PATCH 21/23] statemanageR: updategetproof json-rpc call format --- packages/statemanager/src/rpcStateManager.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/statemanager/src/rpcStateManager.ts b/packages/statemanager/src/rpcStateManager.ts index 6fd3be8985..f00f2f5b28 100644 --- a/packages/statemanager/src/rpcStateManager.ts +++ b/packages/statemanager/src/rpcStateManager.ts @@ -333,12 +333,7 @@ export class RPCStateManager implements StateManagerInterface { if (this.DEBUG) this._debug(`retrieving proof from provider for ${address.toString()}`) const proof = await fetchFromProvider(this._provider, { method: 'eth_getProof', - params: [ - address.toString(), - // TODO: Investigate why this works (either the type should be adjusted, or this is broken) - [storageSlots.map((slot) => bytesToHex(slot))] as any, - this._blockTag, - ], + params: [address.toString(), storageSlots.map(bytesToHex).join(','), this._blockTag], }) return proof From 7fe21fd8037c4a5491a2871733f9547d7cdbe7de Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Mon, 12 Aug 2024 11:47:10 -0400 Subject: [PATCH 22/23] statemanager: remove deactivate from caches --- packages/statemanager/src/cache/caches.ts | 35 +++++++-------- packages/statemanager/src/cache/types.ts | 44 +++++-------------- packages/statemanager/src/stateManager.ts | 6 +-- .../test/checkpointing.code.spec.ts | 1 - .../test/stateManager.account.spec.ts | 6 +-- .../test/stateManager.code.spec.ts | 6 +-- .../test/stateManager.storage.spec.ts | 6 +-- 7 files changed, 34 insertions(+), 70 deletions(-) diff --git a/packages/statemanager/src/cache/caches.ts b/packages/statemanager/src/cache/caches.ts index 5aedfb34de..1373d02f16 100644 --- a/packages/statemanager/src/cache/caches.ts +++ b/packages/statemanager/src/cache/caches.ts @@ -1,8 +1,9 @@ import { AccountCache } from './account.js' import { CodeCache } from './code.js' import { StorageCache } from './storage.js' -import { type CacheSettings, CacheType, type CachesStateManagerOpts } from './types.js' +import { CacheType, type CachesStateManagerOpts } from './types.js' +import type { CacheOpts } from './types.js' import type { Address } from '@ethereumjs/util' export class Caches { @@ -10,52 +11,50 @@ export class Caches { code?: CodeCache storage?: StorageCache - settings: Record<'account' | 'code' | 'storage', CacheSettings> + settings: Record<'account' | 'code' | 'storage', CacheOpts> constructor(opts: CachesStateManagerOpts = {}) { const accountSettings = { - deactivate: (opts.account?.deactivate === true || opts.account?.size === 0) ?? false, type: opts.account?.type ?? CacheType.ORDERED_MAP, size: opts.account?.size ?? 100000, } - const storageSettings = { - deactivate: (opts.storage?.deactivate === true || opts.storage?.size === 0) ?? false, - type: opts.storage?.type ?? CacheType.ORDERED_MAP, - size: opts.storage?.size ?? 20000, - } const codeSettings = { - deactivate: (opts.code?.deactivate === true || opts.code?.size === 0) ?? false, type: opts.code?.type ?? CacheType.ORDERED_MAP, size: opts.code?.size ?? 20000, } + const storageSettings = { + type: opts.storage?.type ?? CacheType.ORDERED_MAP, + size: opts.storage?.size ?? 20000, + } + this.settings = { account: accountSettings, code: codeSettings, storage: storageSettings, } - if (this.settings.account.deactivate === false) { + if (this.settings.account.size !== 0) { this.account = new AccountCache({ size: this.settings.account.size, type: this.settings.account.type, }) } - if (this.settings.storage.deactivate === false) { - this.storage = new StorageCache({ - size: this.settings.storage.size, - type: this.settings.storage.type, - }) - } - - if (this.settings.code.deactivate === false) { + if (this.settings.code.size !== 0) { this.code = new CodeCache({ size: this.settings.code.size, type: this.settings.code.type, }) } + + if (this.settings.storage.size !== 0) { + this.storage = new StorageCache({ + size: this.settings.storage.size, + type: this.settings.storage.type, + }) + } } checkpoint() { diff --git a/packages/statemanager/src/cache/types.ts b/packages/statemanager/src/cache/types.ts index 909e5c68dc..e0819913e0 100644 --- a/packages/statemanager/src/cache/types.ts +++ b/packages/statemanager/src/cache/types.ts @@ -4,27 +4,17 @@ export enum CacheType { } export interface CacheOpts { - size: number - type: CacheType -} - -export type CacheSettings = { - deactivate: boolean - type: CacheType - size: number -} - -export interface CacheStateManagerOpts { /** - * Allows for cache deactivation + * Size of the cache (only for LRU cache) * - * Depending on the use case and underlying datastore (and eventual concurrent cache - * mechanisms there), usage with or without cache can be faster + * Default: 100000 (account cache) / 20000 (storage cache) / 20000 (code cache) * - * Default: false + * Note: the cache/trie interplay mechanism is designed in a way that + * the theoretical number of max modified accounts between two flush operations + * should be smaller than the cache size, otherwise the cache will "forget" the + * old modifications resulting in an incomplete set of trie-flushed accounts. */ - deactivate?: boolean - + size: number /** * Cache type to use. * @@ -36,23 +26,11 @@ export interface CacheStateManagerOpts { * LRU: LRU cache with pre-allocation of memory and a fixed size. * Use for larger and more persistent caches. */ - type?: CacheType - - /** - * Size of the cache (only for LRU cache) - * - * Default: 100000 (account cache) / 20000 (storage cache) / 20000 (code cache) - * - * Note: the cache/trie interplay mechanism is designed in a way that - * the theoretical number of max modified accounts between two flush operations - * should be smaller than the cache size, otherwise the cache will "forget" the - * old modifications resulting in an incomplete set of trie-flushed accounts. - */ - size?: number + type: CacheType } export interface CachesStateManagerOpts { - account?: CacheStateManagerOpts - code?: CacheStateManagerOpts - storage?: CacheStateManagerOpts + account?: Partial + code?: Partial + storage?: Partial } diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 23af18d808..e973137b95 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -922,15 +922,15 @@ export class DefaultStateManager implements StateManagerInterface { const prefixCodeHashes = this._prefixCodeHashes const prefixStorageTrieKeys = this._prefixStorageTrieKeys let accountCacheOpts = { ...this._caches?.settings.account } - if (downlevelCaches && this._caches?.settings.account.deactivate === false) { + if (downlevelCaches && this._caches?.settings.account.size !== 0) { accountCacheOpts = { ...accountCacheOpts, type: CacheType.ORDERED_MAP } } let storageCacheOpts = { ...this._caches?.settings.storage } - if (downlevelCaches && this._caches?.settings.storage.deactivate === false) { + if (downlevelCaches && this._caches?.settings.storage.size !== 0) { storageCacheOpts = { ...storageCacheOpts, type: CacheType.ORDERED_MAP } } let codeCacheOpts = { ...this._caches?.settings.code } - if (this._caches?.settings.code.deactivate === false) { + if (this._caches?.settings.code.size !== 0) { codeCacheOpts = { ...codeCacheOpts, type: CacheType.ORDERED_MAP } } diff --git a/packages/statemanager/test/checkpointing.code.spec.ts b/packages/statemanager/test/checkpointing.code.spec.ts index 180c8b8274..e53b39c109 100644 --- a/packages/statemanager/test/checkpointing.code.spec.ts +++ b/packages/statemanager/test/checkpointing.code.spec.ts @@ -100,7 +100,6 @@ describe('StateManager -> Code Checkpointing', () => { sm = new SM() } - // let sm: DefaultStateManager | SimpleStateManager if (SM === DefaultStateManager) { sm = new SM({ caches: new Caches() }) } else { diff --git a/packages/statemanager/test/stateManager.account.spec.ts b/packages/statemanager/test/stateManager.account.spec.ts index 11dd50a840..6d0e010253 100644 --- a/packages/statemanager/test/stateManager.account.spec.ts +++ b/packages/statemanager/test/stateManager.account.spec.ts @@ -6,11 +6,7 @@ import { Caches, DefaultStateManager } from '../src/index.js' import { createAccountWithDefaults } from './util.js' describe('StateManager -> General/Account', () => { - for (const accountCacheOpts of [ - { deactivate: false }, - { deactivate: true }, - { deactivate: false, size: 0 }, - ]) { + for (const accountCacheOpts of [{ size: 1000 }, { size: 0 }]) { it(`should set the state root to empty`, async () => { const stateManager = new DefaultStateManager({ caches: new Caches({ account: accountCacheOpts }), diff --git a/packages/statemanager/test/stateManager.code.spec.ts b/packages/statemanager/test/stateManager.code.spec.ts index 146464e7a0..a31026d662 100644 --- a/packages/statemanager/test/stateManager.code.spec.ts +++ b/packages/statemanager/test/stateManager.code.spec.ts @@ -14,11 +14,7 @@ import { createAccountWithDefaults } from './util.js' import type { AccountData } from '@ethereumjs/util' describe('StateManager -> Code', () => { - for (const accountCacheOpts of [ - { deactivate: false }, - { deactivate: true }, - { deactivate: false, size: 0 }, - ]) { + for (const accountCacheOpts of [{ size: 1000 }, { size: 0 }]) { it(`should store codehashes using a prefix`, async () => { /* This test is mostly an example of why a code prefix is necessary diff --git a/packages/statemanager/test/stateManager.storage.spec.ts b/packages/statemanager/test/stateManager.storage.spec.ts index 35a81336bb..f8a1fb48cc 100644 --- a/packages/statemanager/test/stateManager.storage.spec.ts +++ b/packages/statemanager/test/stateManager.storage.spec.ts @@ -17,11 +17,7 @@ import { createAccountWithDefaults } from './util.js' const isBrowser = new Function('try {return this===window;}catch(e){ return false;}') describe('StateManager -> Storage', () => { - for (const storageCacheOpts of [ - { deactivate: false }, - { deactivate: true }, - { deactivate: false, size: 0 }, - ]) { + for (const storageCacheOpts of [{ size: 1000 }, { size: 0 }]) { for (const prefixStorageTrieKeys of [false, true]) { it.skipIf(isBrowser() === true)(`should dump storage`, async () => { const stateManager = new DefaultStateManager({ From 486438350daefe83d22ebd7c1dfd3c1926a55067 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Mon, 12 Aug 2024 11:47:23 -0400 Subject: [PATCH 23/23] client: remove deactivate from vm execution instantiation --- packages/client/src/execution/vmexecution.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index 2f2ca4d7ad..460b049a83 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -166,17 +166,14 @@ export class VMExecution extends Execution { prefixStorageTrieKeys: this.config.prefixStorageTrieKeys, caches: new Caches({ account: { - deactivate: false, type: CacheType.LRU, size: this.config.accountCache, }, storage: { - deactivate: false, type: CacheType.LRU, size: this.config.storageCache, }, code: { - deactivate: false, type: CacheType.LRU, size: this.config.codeCache, },