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

Move EEI into EVM #2604

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ffe2039
StateManager: added deactivateCache flag, adopted and expanded tests
holgerd77 Mar 13, 2023
d33ac35
Client/StateManager: use cacheless SM in client, fix StateManager.cop…
holgerd77 Mar 13, 2023
3c1ed3a
StateManager: reworked cache, access and diff cache separation (WIP)
holgerd77 Mar 15, 2023
85e590e
StateManager: continued cache reworking
holgerd77 Mar 15, 2023
683f134
StateManager -> Cache: logic fix in commit()
holgerd77 Mar 17, 2023
37835f7
Util: added storageRoot KECCAK256_RLP comparison for Account.isEmpty()
holgerd77 Mar 17, 2023
e215792
StateManager: Cache logic fixes, explicit checkpointing test suite
holgerd77 Mar 17, 2023
21fdc06
StateManager/VM: fixed VM API tests (account exists)
holgerd77 Mar 17, 2023
d9575e9
Lint fix
holgerd77 Mar 17, 2023
77d7c0f
StateManager -> Cache: Added cache clearing logic and options, added …
holgerd77 Mar 18, 2023
bda9d1a
StateManager/EVM/VM: added cache clearing options
holgerd77 Mar 18, 2023
4f9bf6c
Client: state cache integration, statistics output
holgerd77 Mar 18, 2023
9deda75
StateManager -> Cache: In-cache vs virtual improvements
holgerd77 Mar 18, 2023
d94582b
StateManager -> Cache: Refactor/rewrite with clear account-in-cache/a…
holgerd77 Mar 20, 2023
70a17a9
StateManager/EVM/VM: continues fixes, additional no-account exception…
holgerd77 Mar 20, 2023
c991f8d
Continued fixes
holgerd77 Mar 20, 2023
8ff1b52
Continued fixes
holgerd77 Mar 20, 2023
d303a1e
Lessen account restriction (early throws) on first round (too many te…
holgerd77 Mar 20, 2023
a20fe4d
Continued fixes and first-round loosening of restrictions
holgerd77 Mar 20, 2023
ad2185d
Continued fixes
holgerd77 Mar 20, 2023
d3dd1d1
EVM fix
holgerd77 Mar 21, 2023
a9d7fa4
Client: significantly increase state cache threshold from 500 to 2500…
holgerd77 Mar 21, 2023
1565efd
VM: fix DAO state changes not being committed early enough to disk (a…
holgerd77 Mar 22, 2023
18ae097
StateManager: added very simple code cache
holgerd77 Mar 22, 2023
1d34c50
VM/EVM test and example fixes
holgerd77 Mar 25, 2023
fbee320
Util: revert storageRoot check in Account.isEmpty()
holgerd77 Mar 25, 2023
09b7cbe
EVM: fix EXTCODEHASH bug
holgerd77 Mar 25, 2023
9ba59b8
vm: fix example [no ci]
jochem-brouwer Mar 26, 2023
d2c31da
vm: ensure touched accounts are cleared after generating genesis
jochem-brouwer Mar 26, 2023
b6f57fc
client: lint
jochem-brouwer Mar 26, 2023
bd6bca5
statemanager/common: extract state manager interface to common
jochem-brouwer Mar 27, 2023
713381d
evm: import eei
jochem-brouwer Mar 27, 2023
cf2f273
vm: update changes to evm eei
jochem-brouwer Mar 27, 2023
71dd0cd
evm: fix test syntax
jochem-brouwer Mar 27, 2023
d0820cf
evm: ensure default blockchain is loaded in tests
jochem-brouwer Mar 27, 2023
791c9f6
vm/evm: fix vm test such that they run [no ci]
jochem-brouwer Mar 27, 2023
349f9b5
vm: fix vm copy
jochem-brouwer Mar 27, 2023
f7ad1ee
client/vm: fix build
jochem-brouwer Mar 27, 2023
e31d14b
vm: fix test runner
jochem-brouwer Mar 28, 2023
b99a402
evm: fix example
jochem-brouwer Mar 28, 2023
88a9513
common: fix interface function sig
jochem-brouwer Mar 28, 2023
27ae76e
stateManager/vm: fix tests
jochem-brouwer Mar 28, 2023
eacee53
client: fix tests
jochem-brouwer Mar 28, 2023
572aaa4
client: fix tests
jochem-brouwer Mar 28, 2023
6416dd2
evm: fix example
jochem-brouwer Mar 28, 2023
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
53 changes: 51 additions & 2 deletions packages/client/lib/execution/vmexecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ export class VMExecution extends Execution {
/** Maximally tolerated block time before giving a warning on console */
private MAX_TOLERATED_BLOCK_TIME = 12

/**
* Delete cache items if not read or modfied by
* STATE_CACHE_THRESHOLD_NUM_BLOCKS number of blocks
*/
private STATE_CACHE_THRESHOLD_NUM_BLOCKS = 25000

/**
* Display state cache stats every num blocks
*/
private CACHE_STATS_NUM_BLOCKS = 500
private cacheStatsCount = 0

/**
* Create new VM execution module
*/
Expand All @@ -48,6 +60,7 @@ export class VMExecution extends Execution {

const stateManager = new DefaultStateManager({
trie,
deactivateCache: false,
})

this.vm = new (VM as any)({
Expand Down Expand Up @@ -280,6 +293,12 @@ export class VMExecution extends Execution {
if (!headBlock || reorg) {
const headBlock = await blockchain.getBlock(block.header.parentHash)
parentState = headBlock.header.stateRoot

if (reorg) {
this.config.logger.info(
`Chain reorg happened, set new head to block number=${headBlock.header.number}, clearing state cache for VM execution.`
)
}
}
// run block, update head if valid
try {
Expand Down Expand Up @@ -317,6 +336,7 @@ export class VMExecution extends Execution {
const result = await this.vm.runBlock({
block,
root: parentState,
cacheClearingOptions: this.cacheStatsAndOptions(this.vm, number, reorg),
skipBlockValidation,
skipHeaderValidation: true,
})
Expand Down Expand Up @@ -482,7 +502,7 @@ export class VMExecution extends Execution {
const block = await vm.blockchain.getBlock(blockNumber)
const parentBlock = await vm.blockchain.getBlock(block.header.parentHash)
// Set the correct state root
await vm.stateManager.setStateRoot(parentBlock.header.stateRoot)
const root = parentBlock.header.stateRoot
if (typeof vm.blockchain.getTotalDifficulty !== 'function') {
throw new Error('cannot get iterator head: blockchain has no getTotalDifficulty function')
}
Expand All @@ -493,7 +513,12 @@ export class VMExecution extends Execution {
// we are skipping header validation because the block has been picked from the
// blockchain and header should have already been validated while putBlock
const beforeTS = Date.now()
const res = await vm.runBlock({ block, skipHeaderValidation: true })
const res = await vm.runBlock({
block,
root,
cacheClearingOptions: this.cacheStatsAndOptions(vm, BigInt(blockNumber), false),
skipHeaderValidation: true,
})
const afterTS = Date.now()
const diffSec = Math.round((afterTS - beforeTS) / 1000)
const msg = `Executed block num=${blockNumber} hash=0x${block.hash().toString('hex')} txs=${
Expand Down Expand Up @@ -529,4 +554,28 @@ export class VMExecution extends Execution {
}
}
}

cacheStatsAndOptions(vm: VM, blockNumber: bigint, reorg: boolean) {
this.cacheStatsCount += 1
if (this.cacheStatsCount === this.CACHE_STATS_NUM_BLOCKS) {
const stats = vm.stateManager.cache!.stats()
this.config.logger.info(
`State cache stats size=${stats.cache.size} reads=${stats.cache.reads} hits=${stats.cache.hits} writes=${stats.cache.writes} | trie reads=${stats.trie.reads} writes=${stats.trie.writes}`
)
this.cacheStatsCount = 0
}
// Only apply cache threshold in selected block intervals
// for performance reasons (whole cache iteration needed)
let useThreshold
if (blockNumber % BigInt(100) === BigInt(0)) {
useThreshold = blockNumber - BigInt(this.STATE_CACHE_THRESHOLD_NUM_BLOCKS)
}

const cacheClearingOptions = {
clear: reorg ? true : false,
useThreshold,
comparand: blockNumber,
}
return cacheClearingOptions
}
}
9 changes: 7 additions & 2 deletions packages/client/lib/rpc/modules/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import type { Block, JsonRpcBlock } from '@ethereumjs/block'
import type { Log } from '@ethereumjs/evm'
import type { Proof } from '@ethereumjs/statemanager'
import type { FeeMarketEIP1559Transaction, Transaction, TypedTransaction } from '@ethereumjs/tx'
import type { Account } from '@ethereumjs/util'
import type { PostByzantiumTxReceipt, PreByzantiumTxReceipt, TxReceipt, VM } from '@ethereumjs/vm'

type GetLogsParams = {
Expand Down Expand Up @@ -514,6 +513,9 @@ export class Eth {
const vm = await this._vm.copy()
await vm.stateManager.setStateRoot(block.header.stateRoot)
const account = await vm.stateManager.getAccount(address)
if (account === undefined) {
throw new Error(`could not read account`)
}
return bigIntToHex(account.balance)
}

Expand Down Expand Up @@ -684,7 +686,10 @@ export class Eth {
await vm.stateManager.setStateRoot(block.header.stateRoot)

const address = Address.fromString(addressHex)
const account: Account = await vm.stateManager.getAccount(address)
const account = await vm.stateManager.getAccount(address)
if (account === undefined) {
throw new Error(`could not read account`)
}
return bigIntToHex(account.nonce)
}

Expand Down
13 changes: 10 additions & 3 deletions packages/client/lib/service/txpool.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BlobEIP4844Transaction, Capability } from '@ethereumjs/tx'
import { Address, bufferToHex } from '@ethereumjs/util'
import { Account, Address, bufferToHex } from '@ethereumjs/util'
import Heap = require('qheap')

import type { Config } from '../config'
Expand Down Expand Up @@ -303,7 +303,10 @@ export class TxPool {
const vmCopy = await this.vm.copy()
// Set state root to latest block so that account balance is correct when doing balance check
await vmCopy.stateManager.setStateRoot(block.stateRoot)
const account = await vmCopy.stateManager.getAccount(senderAddress)
let account = await vmCopy.stateManager.getAccount(senderAddress)
if (account === undefined) {
account = new Account()
}
if (account.nonce > tx.nonce) {
throw new Error(
`0x${sender} tries to send a tx with nonce ${tx.nonce}, but account has nonce ${account.nonce} (tx nonce too low)`
Expand Down Expand Up @@ -694,7 +697,11 @@ export class TxPool {
.map((obj) => obj.tx)
.sort((a, b) => Number(a.nonce - b.nonce))
// Check if the account nonce matches the lowest known tx nonce
const { nonce } = await vm.eei.getAccount(new Address(Buffer.from(address, 'hex')))
let account = await vm.eei.getAccount(new Address(Buffer.from(address, 'hex')))
if (account === undefined) {
account = new Account()
}
const { nonce } = account
if (txsSortedByNonce[0].nonce !== nonce) {
// Account nonce does not match the lowest known tx nonce,
// therefore no txs from this address are currently executable
Expand Down
7 changes: 4 additions & 3 deletions packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Hardfork } from '@ethereumjs/common'
import { DefaultStateManager } from '@ethereumjs/statemanager'
import { TransactionFactory, initKZG } from '@ethereumjs/tx'
import { Address } from '@ethereumjs/util'
import { Account, Address } from '@ethereumjs/util'
import * as kzg from 'c-kzg'
import * as tape from 'tape'

Expand Down Expand Up @@ -66,10 +66,11 @@ tape(`${method}: call with known payload`, async (t) => {
'hex'
)
const address = Address.fromPrivateKey(pkey)
await service.execution.vm.stateManager.putAccount(address, new Account())
const account = await service.execution.vm.stateManager.getAccount(address)

account.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account)
account!.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account!)
let req = params('engine_forkchoiceUpdatedV2', validPayload)
let payloadId
let expectRes = (res: any) => {
Expand Down
12 changes: 7 additions & 5 deletions packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Block, BlockHeader } from '@ethereumjs/block'
import { Hardfork } from '@ethereumjs/common'
import { DefaultStateManager } from '@ethereumjs/statemanager'
import { TransactionFactory } from '@ethereumjs/tx'
import { Address } from '@ethereumjs/util'
import { Account, Address } from '@ethereumjs/util'
import { randomBytes } from 'crypto'
import * as tape from 'tape'

Expand Down Expand Up @@ -47,10 +47,11 @@ tape(`${method}: call with valid parameters`, async (t) => {
'hex'
)
const address = Address.fromPrivateKey(pkey)
await service.execution.vm.stateManager.putAccount(address, new Account())
const account = await service.execution.vm.stateManager.getAccount(address)

account.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account)
account!.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account!)
const tx = TransactionFactory.fromTxData(
{
type: 0x01,
Expand Down Expand Up @@ -139,10 +140,11 @@ tape(`${method}: call with valid parameters on pre-Shanghai block`, async (t) =>
'hex'
)
const address = Address.fromPrivateKey(pkey)
await service.execution.vm.stateManager.putAccount(address, new Account())
const account = await service.execution.vm.stateManager.getAccount(address)

account.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account)
account!.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account!)
const tx = TransactionFactory.fromTxData(
{
type: 0x01,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Block, BlockHeader } from '@ethereumjs/block'
import { Hardfork } from '@ethereumjs/common'
import { DefaultStateManager } from '@ethereumjs/statemanager'
import { TransactionFactory } from '@ethereumjs/tx'
import { Address } from '@ethereumjs/util'
import { Account, Address } from '@ethereumjs/util'
import * as tape from 'tape'

import { INVALID_PARAMS, TOO_LARGE_REQUEST } from '../../../lib/rpc/error-code'
Expand Down Expand Up @@ -55,10 +55,11 @@ tape(`${method}: call with valid parameters`, async (t) => {
'hex'
)
const address = Address.fromPrivateKey(pkey)
await service.execution.vm.stateManager.putAccount(address, new Account())
const account = await service.execution.vm.stateManager.getAccount(address)

account.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account)
account!.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account!)
const tx = TransactionFactory.fromTxData(
{
type: 0x01,
Expand Down Expand Up @@ -150,10 +151,11 @@ tape(`${method}: call with valid parameters on pre-Shanghai hardfork`, async (t)
'hex'
)
const address = Address.fromPrivateKey(pkey)
await service.execution.vm.stateManager.putAccount(address, new Account())
const account = await service.execution.vm.stateManager.getAccount(address)

account.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account)
account!.balance = 0xfffffffffffffffn
await service.execution.vm.stateManager.putAccount(address, account!)
const tx = TransactionFactory.fromTxData(
{
type: 0x01,
Expand Down
17 changes: 10 additions & 7 deletions packages/client/test/rpc/eth/sendRawTransaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
commitmentsToVersionedHashes,
getBlobs,
} from '@ethereumjs/tx/dist/utils/blobHelpers'
import { toBuffer } from '@ethereumjs/util'
import { Account, toBuffer } from '@ethereumjs/util'
import * as kzg from 'c-kzg'
import { randomBytes } from 'crypto'
import * as tape from 'tape'
Expand Down Expand Up @@ -45,9 +45,10 @@ tape(`${method}: call with valid arguments`, async (t) => {
const address = transaction.getSenderAddress()
const vm = (client.services.find((s) => s.name === 'eth') as FullEthereumService).execution.vm

await vm.stateManager.putAccount(address, new Account())
const account = await vm.stateManager.getAccount(address)
account.balance = BigInt('40100000')
await vm.stateManager.putAccount(address, account)
account!.balance = BigInt('40100000')
await vm.stateManager.putAccount(address, account!)

const req = params(method, [txData])
const expectRes = (res: any) => {
Expand Down Expand Up @@ -189,9 +190,10 @@ tape(`${method}: call with no peers`, async (t) => {
const address = transaction.getSenderAddress()
const vm = (client.services.find((s) => s.name === 'eth') as FullEthereumService).execution.vm

await vm.stateManager.putAccount(address, new Account())
const account = await vm.stateManager.getAccount(address)
account.balance = BigInt('40100000')
await vm.stateManager.putAccount(address, account)
account!.balance = BigInt('40100000')
await vm.stateManager.putAccount(address, account!)

const req = params(method, [txData])

Expand Down Expand Up @@ -268,9 +270,10 @@ tape('blob EIP 4844 transaction', async (t) => {
{ common }
).sign(pk)
const vm = (client.services.find((s) => s.name === 'eth') as FullEthereumService).execution.vm
await vm.stateManager.putAccount(tx.getSenderAddress(), new Account())
const account = await vm.stateManager.getAccount(tx.getSenderAddress())
account.balance = BigInt(0xfffffffffffff)
await vm.stateManager.putAccount(tx.getSenderAddress(), account)
account!.balance = BigInt(0xfffffffffffff)
await vm.stateManager.putAccount(tx.getSenderAddress(), account!)

const req = params(method, ['0x' + tx.serializeNetworkWrapper().toString('hex')])
const req2 = params(method, ['0x' + replacementTx.serializeNetworkWrapper().toString('hex')])
Expand Down
Loading