Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

statemanager: refactor logic into capabilities #3554

Merged
merged 6 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions packages/client/src/execution/vmexecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@ import {
DBSetHashToNumber,
DBSetTD,
} from '@ethereumjs/blockchain'
import { ConsensusType, Hardfork } from '@ethereumjs/common'
import { CacheType, ConsensusType, Hardfork } from '@ethereumjs/common'
import { MCLBLS } from '@ethereumjs/evm'
import { getGenesis } from '@ethereumjs/genesis'
import {
CacheType,
DefaultStateManager,
StatelessVerkleStateManager,
} from '@ethereumjs/statemanager'
import { DefaultStateManager, StatelessVerkleStateManager } from '@ethereumjs/statemanager'
import { createTrie } from '@ethereumjs/trie'
import {
BIGINT_0,
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/rpc/modules/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
31 changes: 31 additions & 0 deletions packages/common/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,35 @@ export interface StateManagerInterface {
*/
clearCaches(): void
shallowCopy(downlevelCaches?: boolean): StateManagerInterface

/*
* Cache properties
*/
_accountCache?: Cache
_storageCache?: Cache
_codeCache?: Cache

_accountCacheSettings?: CacheSettings
_storageCacheSettings?: CacheSettings
_codeCacheSettings?: CacheSettings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yeah, and the StateManager interface should generally not be bloated in any way, that was one big reason for the refactor of the interface we did 1-2 weeks ago.

This should remain minimal (also regarding optional properties, though it's a bit less urgent here), so that third-party users are not burdened with the internals of state manager.

So all these properties should definitely go out again.

}

/**
* 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
Copy link
Member

@holgerd77 holgerd77 Aug 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Common interfaces should really only be used for the "3 big defining interfaces" (like StateManager) which are important beyond the scope of a single package and therefore should get some solid visibility and the possibility to re-use.

(so generally it's already too crowded in here and some interfaces do not really belong here)

So these Cache interfaces should definitely not be placed here and should go back if it's not absolutely structurally necessary (I guess it's not?).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(maybe this should be stated better at the top of this file)

}
2 changes: 1 addition & 1 deletion packages/statemanager/src/cache/account.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
2 changes: 1 addition & 1 deletion packages/statemanager/src/cache/code.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
2 changes: 1 addition & 1 deletion packages/statemanager/src/cache/storage.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
5 changes: 1 addition & 4 deletions packages/statemanager/src/cache/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
export enum CacheType {
LRU = 'lru',
ORDERED_MAP = 'ordered_map',
}
import type { CacheType } from '@ethereumjs/common'

export interface CacheOpts {
size: number
Expand Down
88 changes: 88 additions & 0 deletions packages/statemanager/src/capabilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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<void> {
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()
}
29 changes: 12 additions & 17 deletions packages/statemanager/src/rpcStateManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Common, Mainnet } from '@ethereumjs/common'
import { CacheType, Common, Mainnet } from '@ethereumjs/common'
import { RLP } from '@ethereumjs/rlp'
import { verifyTrieProof } from '@ethereumjs/trie'
import {
Expand All @@ -16,23 +16,26 @@ import {
import debugDefault from 'debug'
import { keccak256 } from 'ethereum-cryptography/keccak.js'

import { AccountCache, CacheType, OriginalStorageCache, StorageCache } from './cache/index.js'
import { AccountCache, OriginalStorageCache, StorageCache } from './cache/index.js'
import * as Capabilities from './capabilities.js'

import type { Proof, RPCStateManagerOpts } from './index.js'
import type { AccountFields, StateManagerInterface, StorageDump } from '@ethereumjs/common'
import type { RPCStateManagerOpts } from './index.js'
import type { AccountFields, Proof, StateManagerInterface, StorageDump } from '@ethereumjs/common'
import type { Address, PrefixedHexString } from '@ethereumjs/util'
import type { Debugger } from 'debug'

const KECCAK256_RLP_EMPTY_ACCOUNT = RLP.encode(new Account().serialize()).slice(2)

export class RPCStateManager implements StateManagerInterface {
protected _provider: string
protected _contractCache: Map<string, Uint8Array>
protected _storageCache: StorageCache
protected _blockTag: string
protected _accountCache: AccountCache

_accountCache: AccountCache
_storageCache: StorageCache
_contractCache: Map<string, Uint8Array>

originalStorageCache: OriginalStorageCache
protected _debug: Debugger
_debug: Debugger
protected DEBUG: boolean
private keccakFunction: Function
public readonly common: Common
Expand Down Expand Up @@ -317,15 +320,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)
await Capabilities.modifyAccountFields(this, address, accountFields)
}

/**
Expand Down
11 changes: 2 additions & 9 deletions packages/statemanager/src/simpleStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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'
Expand Down Expand Up @@ -78,15 +79,7 @@ export class SimpleStateManager implements StateManagerInterface {
}

async modifyAccountFields(address: Address, accountFields: AccountFields): Promise<void> {
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)
await Capabilities.modifyAccountFields(this, address, accountFields)
}

async getCode(address: Address): Promise<Uint8Array> {
Expand Down
Loading
Loading