mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-22 16:58:21 +00:00
Redesign of BaseVMState descriptor (#923)
* Redesign of BaseVMState descriptor why: BaseVMState provides an environment for executing transactions. The current descriptor also provides data that cannot generally be known within the execution environment, e.g. the total gasUsed which is available not before after all transactions have finished. Also, the BaseVMState constructor has been replaced by a constructor that does not need pre-initialised input of the account database. also: Previous constructor and some fields are provided with a deprecated annotation (producing a lot of noise.) * Replace legacy directives in production sources * Replace legacy directives in unit test sources * fix CI (missing premix update) * Remove legacy directives * chase CI problem * rebased * Re-introduce 'AccountsCache' constructor optimisation for 'BaseVmState' re-initialisation why: Constructing a new 'AccountsCache' descriptor can be avoided sometimes when the current state root is properly positioned already. Such a feature existed already as the update function 'initStateDB()' for the 'BaseChanDB' where the accounts cache was linked into this desctiptor. The function 'initStateDB()' was removed and re-implemented into the 'BaseVmState' constructor without optimisation. The old version was of restricted use as a wrong accounts cache state would unconditionally throw an exception rather than conceptually ask for a remedy. The optimised 'BaseVmState' re-initialisation has been implemented for the 'persistBlocks()' function. also: moved some test helpers to 'test/replay' folder * Remove unused & undocumented fields from Chain descriptor why: Reduces attack surface in general & improves reading the code.
This commit is contained in:
parent
14dd763900
commit
261c0b51a7
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -215,7 +215,9 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: external/dlls-${{ matrix.target.cpu }}
|
||||
key: 'dlls-v2-${{ matrix.target.cpu }}'
|
||||
# according to docu, idle caches are kept for up to 7 days
|
||||
# so change dlls# to force new cache contents (for some number #)
|
||||
key: dlls0-${{ matrix.target.cpu }}
|
||||
|
||||
- name: Install DLLs dependencies (Windows)
|
||||
if: >
|
||||
|
@ -82,6 +82,10 @@ proc rootHash*(ac: AccountsCache): KeccakHash =
|
||||
doAssert(ac.isDirty == false)
|
||||
ac.trie.rootHash
|
||||
|
||||
proc isTopLevelClean*(ac: AccountsCache): bool =
|
||||
## Getter, returns `true` if all pending data have been commited.
|
||||
not ac.isDirty and ac.savePoint.parentSavePoint.isNil
|
||||
|
||||
proc beginSavepoint*(ac: var AccountsCache): SavePoint =
|
||||
new result
|
||||
result.cache = initTable[EthAddress, RefAccount]()
|
||||
@ -371,28 +375,28 @@ proc isDeadAccount*(ac: AccountsCache, address: EthAddress): bool =
|
||||
else:
|
||||
result = acc.isEmpty()
|
||||
|
||||
proc setBalance*(ac: var AccountsCache, address: EthAddress, balance: UInt256) =
|
||||
proc setBalance*(ac: AccountsCache, address: EthAddress, balance: UInt256) =
|
||||
let acc = ac.getAccount(address)
|
||||
acc.flags.incl {IsTouched, IsAlive}
|
||||
if acc.account.balance != balance:
|
||||
ac.makeDirty(address).account.balance = balance
|
||||
|
||||
proc addBalance*(ac: var AccountsCache, address: EthAddress, delta: UInt256) {.inline.} =
|
||||
proc addBalance*(ac: AccountsCache, address: EthAddress, delta: UInt256) {.inline.} =
|
||||
ac.setBalance(address, ac.getBalance(address) + delta)
|
||||
|
||||
proc subBalance*(ac: var AccountsCache, address: EthAddress, delta: UInt256) {.inline.} =
|
||||
proc subBalance*(ac: AccountsCache, address: EthAddress, delta: UInt256) {.inline.} =
|
||||
ac.setBalance(address, ac.getBalance(address) - delta)
|
||||
|
||||
proc setNonce*(ac: var AccountsCache, address: EthAddress, nonce: AccountNonce) =
|
||||
proc setNonce*(ac: AccountsCache, address: EthAddress, nonce: AccountNonce) =
|
||||
let acc = ac.getAccount(address)
|
||||
acc.flags.incl {IsTouched, IsAlive}
|
||||
if acc.account.nonce != nonce:
|
||||
ac.makeDirty(address).account.nonce = nonce
|
||||
|
||||
proc incNonce*(ac: var AccountsCache, address: EthAddress) {.inline.} =
|
||||
proc incNonce*(ac: AccountsCache, address: EthAddress) {.inline.} =
|
||||
ac.setNonce(address, ac.getNonce(address) + 1)
|
||||
|
||||
proc setCode*(ac: var AccountsCache, address: EthAddress, code: seq[byte]) =
|
||||
proc setCode*(ac: AccountsCache, address: EthAddress, code: seq[byte]) =
|
||||
let acc = ac.getAccount(address)
|
||||
acc.flags.incl {IsTouched, IsAlive}
|
||||
let codeHash = keccakHash(code)
|
||||
@ -402,7 +406,7 @@ proc setCode*(ac: var AccountsCache, address: EthAddress, code: seq[byte]) =
|
||||
acc.code = code
|
||||
acc.flags.incl CodeChanged
|
||||
|
||||
proc setStorage*(ac: var AccountsCache, address: EthAddress, slot, value: UInt256) =
|
||||
proc setStorage*(ac: AccountsCache, address: EthAddress, slot, value: UInt256) =
|
||||
let acc = ac.getAccount(address)
|
||||
acc.flags.incl {IsTouched, IsAlive}
|
||||
let oldValue = acc.storageValue(slot, ac.db)
|
||||
@ -411,20 +415,20 @@ proc setStorage*(ac: var AccountsCache, address: EthAddress, slot, value: UInt25
|
||||
acc.overlayStorage[slot] = value
|
||||
acc.flags.incl StorageChanged
|
||||
|
||||
proc clearStorage*(ac: var AccountsCache, address: EthAddress) =
|
||||
proc clearStorage*(ac: AccountsCache, address: EthAddress) =
|
||||
let acc = ac.getAccount(address)
|
||||
acc.flags.incl {IsTouched, IsAlive}
|
||||
if acc.account.storageRoot != emptyRlpHash:
|
||||
# there is no point to clone the storage since we want to remove it
|
||||
ac.makeDirty(address, cloneStorage = false).account.storageRoot = emptyRlpHash
|
||||
|
||||
proc deleteAccount*(ac: var AccountsCache, address: EthAddress) =
|
||||
proc deleteAccount*(ac: AccountsCache, address: EthAddress) =
|
||||
# make sure all savepoints already committed
|
||||
doAssert(ac.savePoint.parentSavePoint.isNil)
|
||||
let acc = ac.getAccount(address)
|
||||
acc.kill()
|
||||
|
||||
proc persist*(ac: var AccountsCache, clearCache: bool = true) =
|
||||
proc persist*(ac: AccountsCache, clearCache: bool = true) =
|
||||
# make sure all savepoint already committed
|
||||
doAssert(ac.savePoint.parentSavePoint.isNil)
|
||||
var cleanAccounts = initHashSet[EthAddress]()
|
||||
|
@ -10,7 +10,7 @@ import
|
||||
stew/[byteutils], eth/trie/[hexary, db],
|
||||
eth/[common, rlp, p2p], chronicles,
|
||||
".."/[errors, constants, utils, chain_config],
|
||||
"."/[storage_types, accounts_cache]
|
||||
"."/storage_types
|
||||
|
||||
type
|
||||
BaseChainDB* = ref object
|
||||
@ -19,7 +19,6 @@ type
|
||||
networkId*: NetworkId
|
||||
config* : ChainConfig
|
||||
genesis* : Genesis
|
||||
stateDB* : AccountsCache
|
||||
|
||||
# startingBlock, currentBlock, and highestBlock
|
||||
# are progress indicator
|
||||
@ -50,11 +49,6 @@ proc `$`*(db: BaseChainDB): string =
|
||||
proc networkParams*(db: BaseChainDB): NetworkParams =
|
||||
NetworkParams(config: db.config, genesis: db.genesis)
|
||||
|
||||
proc initStateDB*(db: BaseChainDB, stateRoot: Hash256) =
|
||||
if db.stateDB.isNil.not and db.stateDB.rootHash == stateRoot:
|
||||
return
|
||||
db.stateDB = AccountsCache.init(db.db, stateRoot, db.pruneTrie)
|
||||
|
||||
proc exists*(self: BaseChainDB, hash: Hash256): bool =
|
||||
self.db.contains(hash.data)
|
||||
|
||||
|
@ -65,3 +65,6 @@ type
|
||||
|
||||
StaticContextError* = object of VMError
|
||||
## State changes not allowed in static call context
|
||||
|
||||
VmStateError* = object of VMError
|
||||
## VM state error relay
|
||||
|
@ -44,9 +44,9 @@ type
|
||||
Chain* = ref object of AbstractChainDB
|
||||
db: BaseChainDB
|
||||
forkIds: array[ChainFork, ForkID]
|
||||
blockZeroHash: KeccakHash
|
||||
lastBlockHash: KeccakHash
|
||||
parentStateRoot: KeccakHash
|
||||
|
||||
blockZeroHash: KeccakHash ##\
|
||||
## Overload cache for `genesisHash()` method
|
||||
|
||||
extraValidation: bool ##\
|
||||
## Trigger extra validation, currently within `persistBlocks()`
|
||||
@ -228,14 +228,6 @@ proc verifyFrom*(c: Chain): BlockNumber =
|
||||
## Getter
|
||||
c.verifyFrom
|
||||
|
||||
proc lastBlockHash*(c: Chain): KeccakHash =
|
||||
## Getter
|
||||
c.lastBlockHash
|
||||
|
||||
proc parentStateRoot*(c: Chain): KeccakHash =
|
||||
## Getter
|
||||
c.parentStateRoot
|
||||
|
||||
proc currentBlock*(c: Chain): BlockHeader
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## currentBlock retrieves the current head block of the canonical chain.
|
||||
@ -262,14 +254,6 @@ proc `verifyFrom=`*(c: Chain; verifyFrom: uint64) =
|
||||
## Variant of `verifyFrom=`
|
||||
c.verifyFrom = verifyFrom.u256
|
||||
|
||||
proc `lastBlockHash=`*(c: Chain; blockHash: KeccakHash) =
|
||||
## Setter.
|
||||
c.lastBlockHash = blockHash
|
||||
|
||||
proc `parentStateRoot=`*(c: Chain; stateRoot: KeccakHash) =
|
||||
## Setter.
|
||||
c.parentStateRoot = stateRoot
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -11,6 +11,7 @@
|
||||
import
|
||||
../../db/db_chain,
|
||||
../../vm_state,
|
||||
../../vm_types,
|
||||
../clique,
|
||||
../executor,
|
||||
../validate,
|
||||
@ -47,19 +48,20 @@ proc persistBlocksImpl(c: Chain; headers: openarray[BlockHeader];
|
||||
var cliqueState = c.clique.cliqueSave
|
||||
defer: c.clique.cliqueRestore(cliqueState)
|
||||
|
||||
# Note that `0 < headers.len`, assured when called from `persistBlocks()`
|
||||
var vmState = BaseVMState.new(headers[0], c.db)
|
||||
|
||||
for i in 0 ..< headers.len:
|
||||
let (header, body) = (headers[i], bodies[i])
|
||||
let
|
||||
(header, body) = (headers[i], bodies[i])
|
||||
|
||||
if header.parentHash != c.lastBlockHash:
|
||||
let parent = c.db.getBlockHeader(header.parentHash)
|
||||
c.parentStateRoot = parent.stateRoot
|
||||
|
||||
# initStateDB will return the last known state
|
||||
# if the stateRoot is match
|
||||
c.db.initStateDB(c.parentStateRoot)
|
||||
if not vmState.reinit(header):
|
||||
debug "Cannot update VmState",
|
||||
blockNumber = header.blockNumber,
|
||||
item = i
|
||||
return ValidationResult.Error
|
||||
|
||||
let
|
||||
vmState = newBaseVMState(c.db.stateDB, header, c.db)
|
||||
let
|
||||
validationResult = vmState.processBlock(c.clique, header, body)
|
||||
|
||||
when not defined(release):
|
||||
@ -102,8 +104,6 @@ proc persistBlocksImpl(c: Chain; headers: openarray[BlockHeader];
|
||||
# so the rpc return consistent result
|
||||
# between eth_blockNumber and eth_syncing
|
||||
c.db.currentBlock = header.blockNumber
|
||||
c.lastBlockHash = header.blockHash
|
||||
c.parentStateRoot = header.stateRoot
|
||||
|
||||
transaction.commit()
|
||||
|
||||
|
@ -145,24 +145,6 @@ proc processTransaction*(
|
||||
fork = vmState.getForkUnsafe
|
||||
vmState.processTransaction(tx, sender, header, fork)
|
||||
|
||||
#[
|
||||
proc processTransaction*(tx: Transaction; sender: EthAddress;
|
||||
vmState: BaseVMState; fork: Fork):
|
||||
Result[GasInt,void]
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Legacy variant of `processTransaction()` with `*header* derived
|
||||
## from the `vmState` argument.
|
||||
vmState.processTransaction(tx, sender, vmState.blockHeader, fork)
|
||||
|
||||
proc processTransaction*(tx: Transaction; sender: EthAddress;
|
||||
vmState: BaseVMState):
|
||||
Result[GasInt,void]
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Legacy variant of `processTransaction()` with `*header* and *fork* derived
|
||||
## from the `vmState` argument.
|
||||
vmState.processTransaction(tx, sender, vmState.blockHeader)
|
||||
#]#
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -89,10 +89,8 @@ proc traceTransaction*(chainDB: BaseChainDB, header: BlockHeader,
|
||||
captureTrieDB = trieDB captureDB
|
||||
networkParams = chainDB.networkParams
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false, chainDB.networkId, networkParams) # prune or not prune?
|
||||
|
||||
captureChainDB.initStateDB(parent.stateRoot)
|
||||
let
|
||||
vmState = newBaseVMState(captureChainDB.stateDB, header, captureChainDB, tracerFlags + {EnableAccount})
|
||||
captureFlags = tracerFlags + {EnableAccount}
|
||||
vmState = BaseVMState.new(header, captureChainDB, captureFlags)
|
||||
|
||||
var stateDb = vmState.stateDB
|
||||
|
||||
@ -162,10 +160,8 @@ proc dumpBlockState*(db: BaseChainDB, header: BlockHeader, body: BlockBody, dump
|
||||
networkParams = db.networkParams
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false, db.networkId, networkParams)
|
||||
# we only need stack dump if we want to scan for internal transaction address
|
||||
|
||||
captureChainDB.initStateDB(parent.stateRoot)
|
||||
let
|
||||
vmState = newBaseVMState(captureChainDB.stateDB, header, captureChainDB, {EnableTracing, DisableMemory, DisableStorage, EnableAccount})
|
||||
captureFlags = {EnableTracing, DisableMemory, DisableStorage, EnableAccount}
|
||||
vmState = BaseVMState.new(header, captureChainDB, captureFlags)
|
||||
miner = vmState.coinbase()
|
||||
|
||||
var
|
||||
@ -222,10 +218,8 @@ proc traceBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, tra
|
||||
captureTrieDB = trieDB captureDB
|
||||
networkParams = chainDB.networkParams
|
||||
captureChainDB = newBaseChainDB(captureTrieDB, false, chainDB.networkId, networkParams)
|
||||
|
||||
captureChainDB.initStateDB(parent.stateRoot)
|
||||
let
|
||||
vmState = newBaseVMState(captureChainDB.stateDB, header, captureChainDB, tracerFlags + {EnableTracing})
|
||||
captureFlags = tracerFlags + {EnableTracing}
|
||||
vmState = BaseVMState.new(header, captureChainDB, captureFlags)
|
||||
|
||||
if header.txRoot == BLANK_ROOT_HASH: return newJNull()
|
||||
doAssert(body.transactions.calcTxRoot == header.txRoot)
|
||||
|
@ -7,6 +7,7 @@
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
std/times,
|
||||
eth/common/eth_types, stint, options, stew/byteutils, chronicles,
|
||||
".."/[vm_types, vm_state, vm_gas_costs, forks, constants],
|
||||
".."/[db/db_chain, db/accounts_cache, transaction], eth/trie/db,
|
||||
@ -72,8 +73,12 @@ proc toCallParams(vmState: BaseVMState, cd: RpcCallData,
|
||||
|
||||
proc rpcCallEvm*(call: RpcCallData, header: BlockHeader, chainDB: BaseChainDB): CallResult =
|
||||
const globalGasCap = 0 # TODO: globalGasCap should configurable by user
|
||||
let stateDB = AccountsCache.init(chainDB.db, header.stateRoot)
|
||||
let vmState = newBaseVMState(stateDB, header, chainDB)
|
||||
let topHeader = BlockHeader(
|
||||
parentHash: header.blockHash,
|
||||
timestamp: getTime().utc.toTime,
|
||||
gasLimit: 0.GasInt, ## ???
|
||||
fee: Uint256.none()) ## ???
|
||||
let vmState = BaseVMState.new(topHeader, chainDB)
|
||||
let params = toCallParams(vmState, call, globalGasCap, header.fee)
|
||||
|
||||
var dbTx = chainDB.db.beginTransaction()
|
||||
@ -83,8 +88,12 @@ proc rpcCallEvm*(call: RpcCallData, header: BlockHeader, chainDB: BaseChainDB):
|
||||
|
||||
proc rpcEstimateGas*(cd: RpcCallData, header: BlockHeader, chainDB: BaseChainDB, gasCap: GasInt): GasInt =
|
||||
# Binary search the gas requirement, as it may be higher than the amount used
|
||||
let stateDB = AccountsCache.init(chainDB.db, header.stateRoot)
|
||||
let vmState = newBaseVMState(stateDB, header, chainDB)
|
||||
let topHeader = BlockHeader(
|
||||
parentHash: header.blockHash,
|
||||
timestamp: getTime().utc.toTime,
|
||||
gasLimit: 0.GasInt, ## ???
|
||||
fee: Uint256.none()) ## ???
|
||||
let vmState = BaseVMState.new(topHeader, chainDB)
|
||||
let fork = chainDB.config.toFork(header.blockNumber)
|
||||
let txGas = gasFees[fork][GasTransaction] # txGas always 21000, use constants?
|
||||
var params = toCallParams(vmState, cd, gasCap, header.fee)
|
||||
|
@ -54,16 +54,16 @@ proc setupTxContext(host: TransactionHost) =
|
||||
# vmState.coinbase now unused
|
||||
host.txContext.block_coinbase = vmState.minerAddress.toEvmc
|
||||
# vmState.blockNumber now unused
|
||||
host.txContext.block_number = (vmState.blockHeader.blockNumber
|
||||
host.txContext.block_number = (vmState.blockNumber
|
||||
.truncate(typeof(host.txContext.block_number)))
|
||||
# vmState.timestamp now unused
|
||||
host.txContext.block_timestamp = vmState.blockHeader.timestamp.toUnix
|
||||
host.txContext.block_timestamp = vmState.timestamp.toUnix
|
||||
# vmState.gasLimit now unused
|
||||
host.txContext.block_gas_limit = vmState.blockHeader.gasLimit
|
||||
host.txContext.block_gas_limit = vmState.gasLimit
|
||||
# vmState.difficulty now unused
|
||||
host.txContext.block_difficulty = vmState.blockHeader.difficulty.toEvmc
|
||||
host.txContext.block_difficulty = vmState.difficulty.toEvmc
|
||||
host.txContext.chain_id = vmState.chaindb.config.chainId.uint.u256.toEvmc
|
||||
host.txContext.block_base_fee = vmState.blockHeader.baseFee.toEvmc
|
||||
host.txContext.block_base_fee = vmState.baseFee.toEvmc
|
||||
|
||||
# Most host functions do `flip256` in `evmc_host_glue`, but due to this
|
||||
# result being cached, it's better to do `flip256` when filling the cache.
|
||||
|
@ -229,7 +229,8 @@ proc setError*(c: Computation, msg: string, burnsGas = false) {.inline.} =
|
||||
proc writeContract*(c: Computation) =
|
||||
template withExtra(tracer: untyped, args: varargs[untyped]) =
|
||||
tracer args, newContract=($c.msg.contractAddress),
|
||||
blockNumber=c.vmState.blockNumber, blockHash=($c.vmState.blockHash)
|
||||
blockNumber=c.vmState.blockNumber,
|
||||
parentHash=($c.vmState.parent.blockHash)
|
||||
|
||||
# In each check below, they are guarded by `len > 0`. This includes writing
|
||||
# out the code, because the account already has zero-length code to handle
|
||||
|
@ -15,10 +15,10 @@ proc hostGetTxContextImpl(ctx: Computation): nimbus_tx_context {.cdecl.} =
|
||||
result.block_coinbase = vmstate.coinbase
|
||||
result.block_number = vmstate.blockNumber.truncate(int64)
|
||||
result.block_timestamp = vmstate.timestamp.toUnix()
|
||||
result.block_gas_limit = int64(vmstate.blockHeader.gasLimit)
|
||||
result.block_gas_limit = int64(vmstate.gasLimit)
|
||||
result.block_difficulty = toEvmc(vmstate.difficulty)
|
||||
result.chain_id = toEvmc(vmstate.chaindb.config.chainId.uint.u256)
|
||||
result.block_base_fee = toEvmc(vmstate.blockHeader.baseFee)
|
||||
result.block_base_fee = toEvmc(vmstate.baseFee)
|
||||
|
||||
proc hostGetBlockHashImpl(ctx: Computation, number: int64): Hash256 {.cdecl.} =
|
||||
ctx.vmState.getAncestorHash(number.u256)
|
||||
|
@ -16,50 +16,260 @@ import
|
||||
../db/[db_chain, accounts_cache],
|
||||
../errors,
|
||||
../forks,
|
||||
../utils,
|
||||
../utils/ec_recover,
|
||||
../utils/[difficulty, ec_recover],
|
||||
./interpreter/gas_costs,
|
||||
./transaction_tracer,
|
||||
./types,
|
||||
eth/[common, keys]
|
||||
|
||||
# Forward declaration
|
||||
proc consensusEnginePoA*(vmState: BaseVMState): bool
|
||||
{.push raises: [Defect].}
|
||||
|
||||
proc getMinerAddress(vmState: BaseVMState): EthAddress =
|
||||
if not vmState.consensusEnginePoA:
|
||||
return vmState.blockHeader.coinbase
|
||||
const
|
||||
nilHash = block:
|
||||
var rc: Hash256
|
||||
rc
|
||||
|
||||
let account = vmState.blockHeader.ecRecover
|
||||
template safeExecutor(info: string; code: untyped) =
|
||||
try:
|
||||
code
|
||||
except CatchableError as e:
|
||||
raise (ref CatchableError)(msg: e.msg)
|
||||
except Defect as e:
|
||||
raise (ref Defect)(msg: e.msg)
|
||||
except:
|
||||
let e = getCurrentException()
|
||||
raise newException(VmStateError, info & "(): " & $e.name & " -- " & e.msg)
|
||||
|
||||
proc getMinerAddress(chainDB: BaseChainDB; header: BlockHeader): EthAddress
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
if not chainDB.config.poaEngine:
|
||||
return header.coinbase
|
||||
|
||||
let account = header.ecRecover
|
||||
if account.isErr:
|
||||
let msg = "Could not recover account address: " & $account.error
|
||||
raise newException(ValidationError, msg)
|
||||
|
||||
account.value
|
||||
|
||||
proc `$`*(vmState: BaseVMState): string =
|
||||
if vmState.isNil:
|
||||
result = "nil"
|
||||
else:
|
||||
result = &"VMState {vmState.name}:\n header: {vmState.blockHeader}\n chaindb: {vmState.chaindb}"
|
||||
|
||||
proc init*(self: BaseVMState, ac: AccountsCache, header: BlockHeader,
|
||||
chainDB: BaseChainDB, tracerFlags: set[TracerFlags] = {}) =
|
||||
proc init(
|
||||
self: BaseVMState;
|
||||
ac: AccountsCache;
|
||||
parent: BlockHeader;
|
||||
timestamp: EthTime;
|
||||
gasLimit: GasInt;
|
||||
fee: Option[Uint256];
|
||||
miner: EthAddress;
|
||||
chainDB: BaseChainDB;
|
||||
tracer: TransactionTracer)
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Initialisation helper
|
||||
self.prevHeaders = @[]
|
||||
self.name = "BaseVM"
|
||||
self.blockHeader = header
|
||||
self.parent = parent
|
||||
self.timestamp = timestamp
|
||||
self.gasLimit = gasLimit
|
||||
self.fee = fee
|
||||
self.chaindb = chainDB
|
||||
self.tracer.initTracer(tracerFlags)
|
||||
self.tracer = tracer
|
||||
self.logEntries = @[]
|
||||
self.stateDB = ac
|
||||
self.touchedAccounts = initHashSet[EthAddress]()
|
||||
{.gcsafe.}:
|
||||
self.minerAddress = self.getMinerAddress()
|
||||
self.minerAddress = miner
|
||||
|
||||
proc newBaseVMState*(ac: AccountsCache, header: BlockHeader,
|
||||
chainDB: BaseChainDB, tracerFlags: set[TracerFlags] = {}): BaseVMState =
|
||||
proc init(
|
||||
self: BaseVMState;
|
||||
ac: AccountsCache;
|
||||
parent: BlockHeader;
|
||||
timestamp: EthTime;
|
||||
gasLimit: GasInt;
|
||||
fee: Option[Uint256];
|
||||
miner: EthAddress;
|
||||
chainDB: BaseChainDB;
|
||||
tracerFlags: set[TracerFlags])
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
var tracer: TransactionTracer
|
||||
tracer.initTracer(tracerFlags)
|
||||
self.init(
|
||||
ac = ac,
|
||||
parent = parent,
|
||||
timestamp = timestamp,
|
||||
gasLimit = gasLimit,
|
||||
fee = fee,
|
||||
miner = miner,
|
||||
chainDB = chainDB,
|
||||
tracer = tracer)
|
||||
|
||||
# --------------
|
||||
|
||||
proc `$`*(vmState: BaseVMState): string
|
||||
{.gcsafe, raises: [Defect,ValueError].} =
|
||||
if vmState.isNil:
|
||||
result = "nil"
|
||||
else:
|
||||
result = &"VMState {vmState.name}:"&
|
||||
&"\n blockNumber: {vmState.parent.blockNumber + 1}"&
|
||||
&"\n chaindb: {vmState.chaindb}"
|
||||
|
||||
proc new*(
|
||||
T: type BaseVMState;
|
||||
parent: BlockHeader; ## parent header, account sync position
|
||||
timestamp: EthTime; ## tx env: time stamp
|
||||
gasLimit: GasInt; ## tx env: gas limit
|
||||
fee: Option[Uint256]; ## tx env: optional base fee
|
||||
miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA)
|
||||
chainDB: BaseChainDB; ## block chain database
|
||||
tracerFlags: set[TracerFlags] = {};
|
||||
pruneTrie: bool = true): T
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Create a new `BaseVMState` descriptor from a parent block header. This
|
||||
## function internally constructs a new account state cache rooted at
|
||||
## `parent.stateRoot`
|
||||
##
|
||||
## This `new()` constructor and its variants (see below) provide a save
|
||||
## `BaseVMState` environment where the account state cache is synchronised
|
||||
## with the `parent` block header.
|
||||
new result
|
||||
result.init(ac, header, chainDB, tracerFlags)
|
||||
result.init(
|
||||
ac = AccountsCache.init(chainDB.db, parent.stateRoot, pruneTrie),
|
||||
parent = parent,
|
||||
timestamp = timestamp,
|
||||
gasLimit = gasLimit,
|
||||
fee = fee,
|
||||
miner = miner,
|
||||
chainDB = chainDB,
|
||||
tracerFlags = tracerFlags)
|
||||
|
||||
proc reinit*(self: BaseVMState; ## Object descriptor
|
||||
parent: BlockHeader; ## parent header, account sync pos.
|
||||
timestamp: EthTime; ## tx env: time stamp
|
||||
gasLimit: GasInt; ## tx env: gas limit
|
||||
fee: Option[Uint256]; ## tx env: optional base fee
|
||||
miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA)
|
||||
pruneTrie: bool = true): bool
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Re-initialise state descriptor. The `AccountsCache` database is
|
||||
## re-initilaise only if its `rootHash` doe not point to `parent.stateRoot`,
|
||||
## already. Accumulated state data are reset.
|
||||
##
|
||||
## This function returns `true` unless the `AccountsCache` database could be
|
||||
## queries about its `rootHash`, i.e. `isTopLevelClean` evaluated `true`. If
|
||||
## this function returns `false`, the function argument `self` is left
|
||||
## untouched.
|
||||
if self.stateDB.isTopLevelClean:
|
||||
let
|
||||
tracer = self.tracer
|
||||
db = self.chainDB
|
||||
ac = if self.stateDB.rootHash == parent.stateRoot: self.stateDB
|
||||
else: AccountsCache.init(db.db, parent.stateRoot, pruneTrie)
|
||||
self[].reset
|
||||
self.init(
|
||||
ac = ac,
|
||||
parent = parent,
|
||||
timestamp = timestamp,
|
||||
gasLimit = gasLimit,
|
||||
fee = fee,
|
||||
miner = miner,
|
||||
chainDB = db,
|
||||
tracer = tracer)
|
||||
return true
|
||||
# else: false
|
||||
|
||||
proc reinit*(self: BaseVMState; ## Object descriptor
|
||||
parent: BlockHeader; ## parent header, account sync pos.
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
pruneTrie: bool = true): bool
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Variant of `reinit()`. The `parent` argument is used to sync the accounts
|
||||
## cache and the `header` is used as a container to pass the `timestamp`,
|
||||
## `gasLimit`, and `fee` values.
|
||||
##
|
||||
## It requires the `header` argument properly initalised so that for PoA
|
||||
## networks, the miner address is retrievable via `ecRecover()`.
|
||||
self.reinit(
|
||||
parent = parent,
|
||||
timestamp = header.timestamp,
|
||||
gasLimit = header.gasLimit,
|
||||
fee = header.fee,
|
||||
miner = self.chainDB.getMinerAddress(header),
|
||||
pruneTrie = pruneTrie)
|
||||
|
||||
proc reinit*(self: BaseVMState; ## Object descriptor
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
pruneTrie: bool = true): bool
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## This is a variant of the `reinit()` function above where the field
|
||||
## `header.parentHash`, is used to fetch the `parent` BlockHeader to be
|
||||
## used in the `update()` variant, above.
|
||||
self.reinit(
|
||||
parent = self.chainDB.getBlockHeader(header.parentHash),
|
||||
header = header,
|
||||
pruneTrie = pruneTrie)
|
||||
|
||||
|
||||
proc init*(
|
||||
self: BaseVMState; ## Object descriptor
|
||||
parent: BlockHeader; ## parent header, account sync position
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
chainDB: BaseChainDB; ## block chain database
|
||||
tracerFlags: set[TracerFlags] = {},
|
||||
pruneTrie: bool = true)
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Variant of `new()` constructor above for in-place initalisation. The
|
||||
## `parent` argument is used to sync the accounts cache and the `header`
|
||||
## is used as a container to pass the `timestamp`, `gasLimit`, and `fee`
|
||||
## values.
|
||||
##
|
||||
## It requires the `header` argument properly initalised so that for PoA
|
||||
## networks, the miner address is retrievable via `ecRecover()`.
|
||||
self.init(AccountsCache.init(chainDB.db, parent.stateRoot, pruneTrie),
|
||||
parent,
|
||||
header.timestamp,
|
||||
header.gasLimit,
|
||||
header.fee,
|
||||
chainDB.getMinerAddress(header),
|
||||
chainDB,
|
||||
tracerFlags)
|
||||
|
||||
proc new*(
|
||||
T: type BaseVMState;
|
||||
parent: BlockHeader; ## parent header, account sync position
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
chainDB: BaseChainDB; ## block chain database
|
||||
tracerFlags: set[TracerFlags] = {},
|
||||
pruneTrie: bool = true): T
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## This is a variant of the `new()` constructor above where the `parent`
|
||||
## argument is used to sync the accounts cache and the `header` is used
|
||||
## as a container to pass the `timestamp`, `gasLimit`, and `fee` values.
|
||||
##
|
||||
## It requires the `header` argument properly initalised so that for PoA
|
||||
## networks, the miner address is retrievable via `ecRecover()`.
|
||||
new result
|
||||
result.init(
|
||||
parent = parent,
|
||||
header = header,
|
||||
chainDB = chainDB,
|
||||
tracerFlags = tracerFlags,
|
||||
pruneTrie = pruneTrie)
|
||||
|
||||
proc new*(
|
||||
T: type BaseVMState;
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
chainDB: BaseChainDB; ## block chain database
|
||||
tracerFlags: set[TracerFlags] = {};
|
||||
pruneTrie: bool = true): T
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## This is a variant of the `new()` constructor above where the field
|
||||
## `header.parentHash`, is used to fetch the `parent` BlockHeader to be
|
||||
## used in the `new()` variant, above.
|
||||
BaseVMState.new(
|
||||
parent = chainDB.getBlockHeader(header.parentHash),
|
||||
header = header,
|
||||
chainDB = chainDB,
|
||||
tracerFlags = tracerFlags,
|
||||
pruneTrie = pruneTrie)
|
||||
|
||||
|
||||
proc setupTxContext*(vmState: BaseVMState, origin: EthAddress, gasPrice: GasInt, forkOverride=none(Fork)) =
|
||||
## this proc will be called each time a new transaction
|
||||
@ -70,7 +280,7 @@ proc setupTxContext*(vmState: BaseVMState, origin: EthAddress, gasPrice: GasInt,
|
||||
if forkOverride.isSome:
|
||||
forkOverride.get
|
||||
else:
|
||||
vmState.chainDB.config.toFork(vmState.blockHeader.blockNumber)
|
||||
vmState.chainDB.config.toFork(vmState.parent.blockNumber + 1)
|
||||
vmState.gasCosts = vmState.fork.forkToSchedule
|
||||
|
||||
proc consensusEnginePoA*(vmState: BaseVMState): bool =
|
||||
@ -79,48 +289,31 @@ proc consensusEnginePoA*(vmState: BaseVMState): bool =
|
||||
# using `real` engine configuration
|
||||
vmState.chainDB.config.poaEngine
|
||||
|
||||
proc updateBlockHeader*(vmState: BaseVMState, header: BlockHeader) =
|
||||
vmState.blockHeader = header
|
||||
vmState.touchedAccounts.clear()
|
||||
vmState.selfDestructs.clear()
|
||||
if EnableTracing in vmState.tracer.flags:
|
||||
vmState.tracer.initTracer(vmState.tracer.flags)
|
||||
vmState.logEntries = @[]
|
||||
vmState.receipts = @[]
|
||||
vmState.minerAddress = vmState.getMinerAddress()
|
||||
vmState.cumulativeGasUsed = 0.GasInt
|
||||
|
||||
method blockhash*(vmState: BaseVMState): Hash256 {.base, gcsafe.} =
|
||||
vmState.blockHeader.hash
|
||||
|
||||
method coinbase*(vmState: BaseVMState): EthAddress {.base, gcsafe.} =
|
||||
vmState.minerAddress
|
||||
|
||||
method timestamp*(vmState: BaseVMState): EthTime {.base, gcsafe.} =
|
||||
vmState.blockHeader.timestamp
|
||||
|
||||
method blockNumber*(vmState: BaseVMState): BlockNumber {.base, gcsafe.} =
|
||||
# it should return current block number
|
||||
# and not head.blockNumber
|
||||
vmState.blockHeader.blockNumber
|
||||
vmState.parent.blockNumber + 1
|
||||
|
||||
method difficulty*(vmState: BaseVMState): UInt256 {.base, gcsafe.} =
|
||||
vmState.blockHeader.difficulty
|
||||
|
||||
method gasLimit*(vmState: BaseVMState): GasInt {.base, gcsafe.} =
|
||||
vmState.blockHeader.gasLimit
|
||||
vmState.chainDB.config.calcDifficulty(vmState.timestamp, vmState.parent)
|
||||
|
||||
method baseFee*(vmState: BaseVMState): UInt256 {.base, gcsafe.} =
|
||||
vmState.blockHeader.baseFee
|
||||
if vmState.fee.isSome:
|
||||
vmState.fee.get
|
||||
else:
|
||||
0.u256
|
||||
|
||||
when defined(geth):
|
||||
import db/geth_db
|
||||
|
||||
method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256 {.base, gcsafe.} =
|
||||
var ancestorDepth = vmState.blockHeader.blockNumber - blockNumber - 1
|
||||
method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256 {.base, gcsafe, raises: [Defect,CatchableError].} =
|
||||
var ancestorDepth = vmState.blockNumber - blockNumber - 1
|
||||
if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH:
|
||||
return
|
||||
if blockNumber >= vmState.blockHeader.blockNumber:
|
||||
if blockNumber >= vmState.blockNumber:
|
||||
return
|
||||
|
||||
when defined(geth):
|
||||
@ -154,10 +347,10 @@ proc getAndClearLogEntries*(vmState: BaseVMState): seq[Log] =
|
||||
shallowCopy(result, vmState.logEntries)
|
||||
vmState.logEntries = @[]
|
||||
|
||||
proc enableTracing*(vmState: BaseVMState) {.inline.} =
|
||||
proc enableTracing*(vmState: BaseVMState) =
|
||||
vmState.tracer.flags.incl EnableTracing
|
||||
|
||||
proc disableTracing*(vmState: BaseVMState) {.inline.} =
|
||||
proc disableTracing*(vmState: BaseVMState) =
|
||||
vmState.tracer.flags.excl EnableTracing
|
||||
|
||||
iterator tracedAccounts*(vmState: BaseVMState): EthAddress =
|
||||
@ -174,25 +367,27 @@ proc removeTracedAccounts*(vmState: BaseVMState, accounts: varargs[EthAddress])
|
||||
for acc in accounts:
|
||||
vmState.tracer.accounts.excl acc
|
||||
|
||||
proc status*(vmState: BaseVMState): bool {.inline.} =
|
||||
proc status*(vmState: BaseVMState): bool =
|
||||
ExecutionOK in vmState.flags
|
||||
|
||||
proc `status=`*(vmState: BaseVMState, status: bool) =
|
||||
if status: vmState.flags.incl ExecutionOK
|
||||
else: vmState.flags.excl ExecutionOK
|
||||
|
||||
proc generateWitness*(vmState: BaseVMState): bool {.inline.} =
|
||||
proc generateWitness*(vmState: BaseVMState): bool =
|
||||
GenerateWitness in vmState.flags
|
||||
|
||||
proc `generateWitness=`*(vmState: BaseVMState, status: bool) =
|
||||
if status: vmState.flags.incl GenerateWitness
|
||||
else: vmState.flags.excl GenerateWitness
|
||||
|
||||
proc buildWitness*(vmState: BaseVMState): seq[byte] =
|
||||
proc buildWitness*(vmState: BaseVMState): seq[byte]
|
||||
{.raises: [Defect, CatchableError].} =
|
||||
let rootHash = vmState.stateDB.rootHash
|
||||
let mkeys = vmState.stateDB.makeMultiKeys()
|
||||
let flags = if vmState.fork >= FKSpurious: {wfEIP170} else: {}
|
||||
|
||||
# build witness from tree
|
||||
var wb = initWitnessBuilder(vmState.chainDB.db, rootHash, flags)
|
||||
result = wb.buildWitness(mkeys)
|
||||
safeExecutor("buildWitness"):
|
||||
result = wb.buildWitness(mkeys)
|
||||
|
@ -34,7 +34,10 @@ type
|
||||
BaseVMState* = ref object of RootObj
|
||||
prevHeaders* : seq[BlockHeader]
|
||||
chaindb* : BaseChainDB
|
||||
blockHeader* : BlockHeader
|
||||
parent* : BlockHeader
|
||||
timestamp* : EthTime
|
||||
gasLimit* : GasInt
|
||||
fee* : Option[Uint256]
|
||||
name* : string
|
||||
flags* : set[VMFlag]
|
||||
tracer* : TransactionTracer
|
||||
|
@ -166,7 +166,8 @@ proc setError*(c: Computation, msg: string, burnsGas = false) {.inline.} =
|
||||
proc writeContract*(c: Computation) =
|
||||
template withExtra(tracer: untyped, args: varargs[untyped]) =
|
||||
tracer args, newContract=($c.msg.contractAddress),
|
||||
blockNumber=c.vmState.blockNumber, blockHash=($c.vmState.blockHash)
|
||||
blockNumber=c.vmState.blockNumber,
|
||||
parentHash=($c.vmState.parent.blockHash)
|
||||
|
||||
# In each check below, they are guarded by `len > 0`. This includes writing
|
||||
# out the code, because the account already has zero-length code to handle
|
||||
|
@ -16,49 +16,259 @@ import
|
||||
../db/[db_chain, accounts_cache],
|
||||
../errors,
|
||||
../forks,
|
||||
../utils,
|
||||
../utils/ec_recover,
|
||||
../utils/[difficulty, ec_recover],
|
||||
./transaction_tracer,
|
||||
./types,
|
||||
eth/[common, keys]
|
||||
|
||||
# Forward declaration
|
||||
proc consensusEnginePoA*(vmState: BaseVMState): bool
|
||||
{.push raises: [Defect].}
|
||||
|
||||
proc getMinerAddress(vmState: BaseVMState): EthAddress =
|
||||
if not vmState.consensusEnginePoA:
|
||||
return vmState.blockHeader.coinbase
|
||||
const
|
||||
nilHash = block:
|
||||
var rc: Hash256
|
||||
rc
|
||||
|
||||
let account = vmState.blockHeader.ecRecover
|
||||
template safeExecutor(info: string; code: untyped) =
|
||||
try:
|
||||
code
|
||||
except CatchableError as e:
|
||||
raise (ref CatchableError)(msg: e.msg)
|
||||
except Defect as e:
|
||||
raise (ref Defect)(msg: e.msg)
|
||||
except:
|
||||
let e = getCurrentException()
|
||||
raise newException(VmStateError, info & "(): " & $e.name & " -- " & e.msg)
|
||||
|
||||
proc getMinerAddress(chainDB: BaseChainDB; header: BlockHeader): EthAddress
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
if not chainDB.config.poaEngine:
|
||||
return header.coinbase
|
||||
|
||||
let account = header.ecRecover
|
||||
if account.isErr:
|
||||
let msg = "Could not recover account address: " & $account.error
|
||||
raise newException(ValidationError, msg)
|
||||
|
||||
account.value
|
||||
|
||||
proc `$`*(vmState: BaseVMState): string =
|
||||
if vmState.isNil:
|
||||
result = "nil"
|
||||
else:
|
||||
result = &"VMState {vmState.name}:\n header: {vmState.blockHeader}\n chaindb: {vmState.chaindb}"
|
||||
|
||||
proc init*(self: BaseVMState, ac: AccountsCache, header: BlockHeader,
|
||||
chainDB: BaseChainDB, tracerFlags: set[TracerFlags] = {}) =
|
||||
proc init(
|
||||
self: BaseVMState;
|
||||
ac: AccountsCache;
|
||||
parent: BlockHeader;
|
||||
timestamp: EthTime;
|
||||
gasLimit: GasInt;
|
||||
fee: Option[Uint256];
|
||||
miner: EthAddress;
|
||||
chainDB: BaseChainDB;
|
||||
tracer: TransactionTracer)
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Initialisation helper
|
||||
self.prevHeaders = @[]
|
||||
self.name = "BaseVM"
|
||||
self.blockHeader = header
|
||||
self.parent = parent
|
||||
self.timestamp = timestamp
|
||||
self.gasLimit = gasLimit
|
||||
self.fee = fee
|
||||
self.chaindb = chainDB
|
||||
self.tracer.initTracer(tracerFlags)
|
||||
self.tracer = tracer
|
||||
self.logEntries = @[]
|
||||
self.stateDB = ac
|
||||
self.touchedAccounts = initHashSet[EthAddress]()
|
||||
{.gcsafe.}:
|
||||
self.minerAddress = self.getMinerAddress()
|
||||
self.minerAddress = miner
|
||||
|
||||
proc newBaseVMState*(ac: AccountsCache, header: BlockHeader,
|
||||
chainDB: BaseChainDB, tracerFlags: set[TracerFlags] = {}): BaseVMState =
|
||||
proc init(
|
||||
self: BaseVMState;
|
||||
ac: AccountsCache;
|
||||
parent: BlockHeader;
|
||||
timestamp: EthTime;
|
||||
gasLimit: GasInt;
|
||||
fee: Option[Uint256];
|
||||
miner: EthAddress;
|
||||
chainDB: BaseChainDB;
|
||||
tracerFlags: set[TracerFlags])
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
var tracer: TransactionTracer
|
||||
tracer.initTracer(tracerFlags)
|
||||
self.init(
|
||||
ac = ac,
|
||||
parent = parent,
|
||||
timestamp = timestamp,
|
||||
gasLimit = gasLimit,
|
||||
fee = fee,
|
||||
miner = miner,
|
||||
chainDB = chainDB,
|
||||
tracer = tracer)
|
||||
|
||||
# --------------
|
||||
|
||||
proc `$`*(vmState: BaseVMState): string
|
||||
{.gcsafe, raises: [Defect,ValueError].} =
|
||||
if vmState.isNil:
|
||||
result = "nil"
|
||||
else:
|
||||
result = &"VMState {vmState.name}:"&
|
||||
&"\n blockNumber: {vmState.parent.blockNumber + 1}"&
|
||||
&"\n chaindb: {vmState.chaindb}"
|
||||
|
||||
proc new*(
|
||||
T: type BaseVMState;
|
||||
parent: BlockHeader; ## parent header, account sync position
|
||||
timestamp: EthTime; ## tx env: time stamp
|
||||
gasLimit: GasInt; ## tx env: gas limit
|
||||
fee: Option[Uint256]; ## tx env: optional base fee
|
||||
miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA)
|
||||
chainDB: BaseChainDB; ## block chain database
|
||||
tracerFlags: set[TracerFlags] = {};
|
||||
pruneTrie: bool = true): T
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Create a new `BaseVMState` descriptor from a parent block header. This
|
||||
## function internally constructs a new account state cache rooted at
|
||||
## `parent.stateRoot`
|
||||
##
|
||||
## This `new()` constructor and its variants (see below) provide a save
|
||||
## `BaseVMState` environment where the account state cache is synchronised
|
||||
## with the `parent` block header.
|
||||
new result
|
||||
result.init(ac, header, chainDB, tracerFlags)
|
||||
result.init(
|
||||
ac = AccountsCache.init(chainDB.db, parent.stateRoot, pruneTrie),
|
||||
parent = parent,
|
||||
timestamp = timestamp,
|
||||
gasLimit = gasLimit,
|
||||
fee = fee,
|
||||
miner = miner,
|
||||
chainDB = chainDB,
|
||||
tracerFlags = tracerFlags)
|
||||
|
||||
proc reinit*(self: BaseVMState; ## Object descriptor
|
||||
parent: BlockHeader; ## parent header, account sync pos.
|
||||
timestamp: EthTime; ## tx env: time stamp
|
||||
gasLimit: GasInt; ## tx env: gas limit
|
||||
fee: Option[Uint256]; ## tx env: optional base fee
|
||||
miner: EthAddress; ## tx env: coinbase(PoW) or signer(PoA)
|
||||
pruneTrie: bool = true): bool
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Re-initialise state descriptor. The `AccountsCache` database is
|
||||
## re-initilaise only if its `rootHash` doe not point to `parent.stateRoot`,
|
||||
## already. Accumulated state data are reset.
|
||||
##
|
||||
## This function returns `true` unless the `AccountsCache` database could be
|
||||
## queries about its `rootHash`, i.e. `isTopLevelClean` evaluated `true`. If
|
||||
## this function returns `false`, the function argument `self` is left
|
||||
## untouched.
|
||||
if self.stateDB.isTopLevelClean:
|
||||
let
|
||||
tracer = self.tracer
|
||||
db = self.chainDB
|
||||
ac = if self.stateDB.rootHash == parent.stateRoot: self.stateDB
|
||||
else: AccountsCache.init(db.db, parent.stateRoot, pruneTrie)
|
||||
self[].reset
|
||||
self.init(
|
||||
ac = ac,
|
||||
parent = parent,
|
||||
timestamp = timestamp,
|
||||
gasLimit = gasLimit,
|
||||
fee = fee,
|
||||
miner = miner,
|
||||
chainDB = db,
|
||||
tracer = tracer)
|
||||
return true
|
||||
# else: false
|
||||
|
||||
proc reinit*(self: BaseVMState; ## Object descriptor
|
||||
parent: BlockHeader; ## parent header, account sync pos.
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
pruneTrie: bool = true): bool
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Variant of `reinit()`. The `parent` argument is used to sync the accounts
|
||||
## cache and the `header` is used as a container to pass the `timestamp`,
|
||||
## `gasLimit`, and `fee` values.
|
||||
##
|
||||
## It requires the `header` argument properly initalised so that for PoA
|
||||
## networks, the miner address is retrievable via `ecRecover()`.
|
||||
self.reinit(
|
||||
parent = parent,
|
||||
timestamp = header.timestamp,
|
||||
gasLimit = header.gasLimit,
|
||||
fee = header.fee,
|
||||
miner = self.chainDB.getMinerAddress(header),
|
||||
pruneTrie = pruneTrie)
|
||||
|
||||
proc reinit*(self: BaseVMState; ## Object descriptor
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
pruneTrie: bool = true): bool
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## This is a variant of the `reinit()` function above where the field
|
||||
## `header.parentHash`, is used to fetch the `parent` BlockHeader to be
|
||||
## used in the `update()` variant, above.
|
||||
self.reinit(
|
||||
parent = self.chainDB.getBlockHeader(header.parentHash),
|
||||
header = header,
|
||||
pruneTrie = pruneTrie)
|
||||
|
||||
|
||||
proc init*(
|
||||
self: BaseVMState; ## Object descriptor
|
||||
parent: BlockHeader; ## parent header, account sync position
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
chainDB: BaseChainDB; ## block chain database
|
||||
tracerFlags: set[TracerFlags] = {},
|
||||
pruneTrie: bool = true)
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## Variant of `new()` constructor above for in-place initalisation. The
|
||||
## `parent` argument is used to sync the accounts cache and the `header`
|
||||
## is used as a container to pass the `timestamp`, `gasLimit`, and `fee`
|
||||
## values.
|
||||
##
|
||||
## It requires the `header` argument properly initalised so that for PoA
|
||||
## networks, the miner address is retrievable via `ecRecover()`.
|
||||
self.init(AccountsCache.init(chainDB.db, parent.stateRoot, pruneTrie),
|
||||
parent,
|
||||
header.timestamp,
|
||||
header.gasLimit,
|
||||
header.fee,
|
||||
chainDB.getMinerAddress(header),
|
||||
chainDB,
|
||||
tracerFlags)
|
||||
|
||||
proc new*(
|
||||
T: type BaseVMState;
|
||||
parent: BlockHeader; ## parent header, account sync position
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
chainDB: BaseChainDB; ## block chain database
|
||||
tracerFlags: set[TracerFlags] = {},
|
||||
pruneTrie: bool = true): T
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## This is a variant of the `new()` constructor above where the `parent`
|
||||
## argument is used to sync the accounts cache and the `header` is used
|
||||
## as a container to pass the `timestamp`, `gasLimit`, and `fee` values.
|
||||
##
|
||||
## It requires the `header` argument properly initalised so that for PoA
|
||||
## networks, the miner address is retrievable via `ecRecover()`.
|
||||
new result
|
||||
result.init(
|
||||
parent = parent,
|
||||
header = header,
|
||||
chainDB = chainDB,
|
||||
tracerFlags = tracerFlags,
|
||||
pruneTrie = pruneTrie)
|
||||
|
||||
proc new*(
|
||||
T: type BaseVMState;
|
||||
header: BlockHeader; ## header with tx environment data fields
|
||||
chainDB: BaseChainDB; ## block chain database
|
||||
tracerFlags: set[TracerFlags] = {};
|
||||
pruneTrie: bool = true): T
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
## This is a variant of the `new()` constructor above where the field
|
||||
## `header.parentHash`, is used to fetch the `parent` BlockHeader to be
|
||||
## used in the `new()` variant, above.
|
||||
BaseVMState.new(
|
||||
parent = chainDB.getBlockHeader(header.parentHash),
|
||||
header = header,
|
||||
chainDB = chainDB,
|
||||
tracerFlags = tracerFlags,
|
||||
pruneTrie = pruneTrie)
|
||||
|
||||
|
||||
proc consensusEnginePoA*(vmState: BaseVMState): bool =
|
||||
# PoA consensus engine have no reward for miner
|
||||
@ -66,48 +276,31 @@ proc consensusEnginePoA*(vmState: BaseVMState): bool =
|
||||
# using `real` engine configuration
|
||||
vmState.chainDB.config.poaEngine
|
||||
|
||||
proc updateBlockHeader*(vmState: BaseVMState, header: BlockHeader) =
|
||||
vmState.blockHeader = header
|
||||
vmState.touchedAccounts.clear()
|
||||
vmState.selfDestructs.clear()
|
||||
if EnableTracing in vmState.tracer.flags:
|
||||
vmState.tracer.initTracer(vmState.tracer.flags)
|
||||
vmState.logEntries = @[]
|
||||
vmState.receipts = @[]
|
||||
vmState.minerAddress = vmState.getMinerAddress()
|
||||
vmState.cumulativeGasUsed = 0.GasInt
|
||||
|
||||
method blockhash*(vmState: BaseVMState): Hash256 {.base, gcsafe.} =
|
||||
vmState.blockHeader.hash
|
||||
|
||||
method coinbase*(vmState: BaseVMState): EthAddress {.base, gcsafe.} =
|
||||
vmState.minerAddress
|
||||
|
||||
method timestamp*(vmState: BaseVMState): EthTime {.base, gcsafe.} =
|
||||
vmState.blockHeader.timestamp
|
||||
|
||||
method blockNumber*(vmState: BaseVMState): BlockNumber {.base, gcsafe.} =
|
||||
# it should return current block number
|
||||
# and not head.blockNumber
|
||||
vmState.blockHeader.blockNumber
|
||||
vmState.parent.blockNumber + 1
|
||||
|
||||
method difficulty*(vmState: BaseVMState): UInt256 {.base, gcsafe.} =
|
||||
vmState.blockHeader.difficulty
|
||||
|
||||
method gasLimit*(vmState: BaseVMState): GasInt {.base, gcsafe.} =
|
||||
vmState.blockHeader.gasLimit
|
||||
vmState.chainDB.config.calcDifficulty(vmState.timestamp, vmState.parent)
|
||||
|
||||
method baseFee*(vmState: BaseVMState): UInt256 {.base, gcsafe.} =
|
||||
vmState.blockHeader.baseFee
|
||||
if vmState.fee.isSome:
|
||||
vmState.fee.get
|
||||
else:
|
||||
0.u256
|
||||
|
||||
when defined(geth):
|
||||
import db/geth_db
|
||||
|
||||
method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256 {.base, gcsafe.} =
|
||||
var ancestorDepth = vmState.blockHeader.blockNumber - blockNumber - 1
|
||||
method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256 {.base, gcsafe, raises: [Defect,CatchableError].} =
|
||||
var ancestorDepth = vmState.blockNumber - blockNumber - 1
|
||||
if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH:
|
||||
return
|
||||
if blockNumber >= vmState.blockHeader.blockNumber:
|
||||
if blockNumber >= vmState.blockNumber:
|
||||
return
|
||||
|
||||
when defined(geth):
|
||||
@ -141,10 +334,10 @@ proc getAndClearLogEntries*(vmState: BaseVMState): seq[Log] =
|
||||
shallowCopy(result, vmState.logEntries)
|
||||
vmState.logEntries = @[]
|
||||
|
||||
proc enableTracing*(vmState: BaseVMState) {.inline.} =
|
||||
proc enableTracing*(vmState: BaseVMState) =
|
||||
vmState.tracer.flags.incl EnableTracing
|
||||
|
||||
proc disableTracing*(vmState: BaseVMState) {.inline.} =
|
||||
proc disableTracing*(vmState: BaseVMState) =
|
||||
vmState.tracer.flags.excl EnableTracing
|
||||
|
||||
iterator tracedAccounts*(vmState: BaseVMState): EthAddress =
|
||||
@ -161,25 +354,27 @@ proc removeTracedAccounts*(vmState: BaseVMState, accounts: varargs[EthAddress])
|
||||
for acc in accounts:
|
||||
vmState.tracer.accounts.excl acc
|
||||
|
||||
proc status*(vmState: BaseVMState): bool {.inline.} =
|
||||
proc status*(vmState: BaseVMState): bool =
|
||||
ExecutionOK in vmState.flags
|
||||
|
||||
proc `status=`*(vmState: BaseVMState, status: bool) =
|
||||
if status: vmState.flags.incl ExecutionOK
|
||||
else: vmState.flags.excl ExecutionOK
|
||||
|
||||
proc generateWitness*(vmState: BaseVMState): bool {.inline.} =
|
||||
proc generateWitness*(vmState: BaseVMState): bool =
|
||||
GenerateWitness in vmState.flags
|
||||
|
||||
proc `generateWitness=`*(vmState: BaseVMState, status: bool) =
|
||||
if status: vmState.flags.incl GenerateWitness
|
||||
else: vmState.flags.excl GenerateWitness
|
||||
|
||||
proc buildWitness*(vmState: BaseVMState): seq[byte] =
|
||||
proc buildWitness*(vmState: BaseVMState): seq[byte]
|
||||
{.raises: [Defect, CatchableError].} =
|
||||
let rootHash = vmState.stateDB.rootHash
|
||||
let mkeys = vmState.stateDB.makeMultiKeys()
|
||||
let flags = if vmState.fork >= FKSpurious: {wfEIP170} else: {}
|
||||
|
||||
# build witness from tree
|
||||
var wb = initWitnessBuilder(vmState.chainDB.db, rootHash, flags)
|
||||
result = wb.buildWitness(mkeys)
|
||||
safeExecutor("buildWitness"):
|
||||
result = wb.buildWitness(mkeys)
|
||||
|
@ -45,7 +45,7 @@ proc setupTxContext*(vmState: BaseVMState, origin: EthAddress, gasPrice: GasInt,
|
||||
if forkOverride.isSome:
|
||||
forkOverride.get
|
||||
else:
|
||||
vmState.chainDB.config.toFork(vmState.blockHeader.blockNumber)
|
||||
vmState.chainDB.config.toFork(vmState.blockNumber)
|
||||
vmState.gasCosts = vmState.fork.forkToSchedule
|
||||
|
||||
|
||||
|
@ -25,7 +25,10 @@ type
|
||||
BaseVMState* = ref object of RootObj
|
||||
prevHeaders* : seq[BlockHeader]
|
||||
chaindb* : BaseChainDB
|
||||
blockHeader* : BlockHeader
|
||||
parent* : BlockHeader
|
||||
timestamp* : EthTime
|
||||
gasLimit* : GasInt
|
||||
fee* : Option[Uint256]
|
||||
name* : string
|
||||
flags* : set[VMFlag]
|
||||
tracer* : TransactionTracer
|
||||
|
@ -24,14 +24,12 @@ else:
|
||||
export
|
||||
vms.`$`,
|
||||
vms.blockNumber,
|
||||
vms.blockhash,
|
||||
vms.buildWitness,
|
||||
vms.coinbase,
|
||||
vms.consensusEnginePoA,
|
||||
vms.difficulty,
|
||||
vms.disableTracing,
|
||||
vms.enableTracing,
|
||||
vms.gasLimit,
|
||||
vms.baseFee,
|
||||
vms.generateWitness,
|
||||
vms.`generateWitness=`,
|
||||
@ -40,14 +38,13 @@ export
|
||||
vms.getTracingResult,
|
||||
vms.init,
|
||||
vms.mutateStateDB,
|
||||
vms.newBaseVMState,
|
||||
vms.new,
|
||||
vms.reinit,
|
||||
vms.readOnlyStateDB,
|
||||
vms.removeTracedAccounts,
|
||||
vms.status,
|
||||
vms.`status=`,
|
||||
vms.timestamp,
|
||||
vms.tracedAccounts,
|
||||
vms.tracedAccountsPairs,
|
||||
vms.updateBlockHeader
|
||||
vms.tracedAccountsPairs
|
||||
|
||||
# End
|
||||
|
@ -1,6 +1,6 @@
|
||||
import
|
||||
json, os, stint, eth/trie/db, stew/byteutils, eth/common,
|
||||
../nimbus/db/[db_chain], chronicles, ../nimbus/vm_state,
|
||||
../nimbus/db/[db_chain], chronicles, ../nimbus/[vm_state, vm_types],
|
||||
../nimbus/p2p/executor, premixcore, prestate, ../nimbus/tracer
|
||||
|
||||
proc prepareBlockEnv(node: JsonNode, memoryDB: TrieDatabaseRef) =
|
||||
@ -22,9 +22,8 @@ proc executeBlock(blockEnv: JsonNode, memoryDB: TrieDatabaseRef, blockNumber: Ui
|
||||
let transaction = memoryDB.beginTransaction()
|
||||
defer: transaction.dispose()
|
||||
|
||||
chainDB.initStateDB(parent.stateRoot)
|
||||
let
|
||||
vmState = newBaseVMState(chainDB.stateDB, header, chainDB)
|
||||
vmState = BaseVMState.new(parent, header, chainDB)
|
||||
validationResult = vmState.processBlockNotPoA(header, body)
|
||||
|
||||
if validationResult != ValidationResult.OK:
|
||||
|
@ -7,7 +7,7 @@ import
|
||||
configuration, stint, eth/common,
|
||||
../nimbus/db/[db_chain, select_backend, capturedb],
|
||||
eth/trie/[hexary, db], ../nimbus/p2p/executor,
|
||||
../nimbus/[tracer, vm_state]
|
||||
../nimbus/[tracer, vm_state, vm_types]
|
||||
|
||||
proc dumpDebug(chainDB: BaseChainDB, blockNumber: Uint256) =
|
||||
var
|
||||
@ -26,10 +26,7 @@ proc dumpDebug(chainDB: BaseChainDB, blockNumber: Uint256) =
|
||||
header = captureChainDB.getBlockHeader(blockNumber)
|
||||
headerHash = header.blockHash
|
||||
body = captureChainDB.getBlockBody(headerHash)
|
||||
|
||||
captureChainDB.initStateDB(parent.stateRoot)
|
||||
let
|
||||
vmState = newBaseVMState(captureChainDB.stateDB, header, captureChainDB)
|
||||
vmState = BaseVMState.new(parent, header, captureChainDB)
|
||||
|
||||
captureChainDB.setHead(parent, true)
|
||||
discard vmState.processBlockNotPoA(header, body)
|
||||
|
@ -5,7 +5,7 @@ import
|
||||
stint, stew/byteutils, chronicles,
|
||||
|
||||
../nimbus/[tracer, vm_state, utils, vm_types],
|
||||
../nimbus/db/[db_chain, state_db, accounts_cache],
|
||||
../nimbus/db/[db_chain, state_db],
|
||||
../nimbus/p2p/executor, premixcore,
|
||||
"."/configuration, downloader, parser
|
||||
|
||||
@ -66,9 +66,9 @@ type
|
||||
proc hash*(x: Uint256): Hash =
|
||||
result = hash(x.toByteArrayBE)
|
||||
|
||||
proc newHunterVMState(ac: AccountsCache, header: BlockHeader, chainDB: BaseChainDB): HunterVMState =
|
||||
proc new(T: type HunterVMState; parent, header: BlockHeader, chainDB: BaseChainDB): T =
|
||||
new result
|
||||
result.init(ac, header, chainDB)
|
||||
result.init(parent, header, chainDB)
|
||||
result.headers = initTable[BlockNumber, BlockHeader]()
|
||||
|
||||
method getAncestorHash*(vmState: HunterVMState, blockNumber: BlockNumber): Hash256 {.gcsafe.} =
|
||||
@ -96,12 +96,11 @@ proc huntProblematicBlock(blockNumber: Uint256): ValidationResult =
|
||||
chainDB = newBaseChainDB(memoryDB, false)
|
||||
|
||||
chainDB.setHead(parentBlock.header, true)
|
||||
chainDB.initStateDB(parentBlock.header.stateRoot)
|
||||
|
||||
let transaction = memoryDB.beginTransaction()
|
||||
defer: transaction.dispose()
|
||||
let
|
||||
vmState = newHunterVMState(chainDB.stateDB, thisBlock.header, chainDB)
|
||||
vmState = HunterVMState.new(parentBlock.header, thisBlock.header, chainDB)
|
||||
validationResult = vmState.processBlockNotPoA(thisBlock.header, thisBlock.body)
|
||||
|
||||
if validationResult != ValidationResult.OK:
|
||||
|
@ -5,7 +5,7 @@ import
|
||||
|
||||
import
|
||||
../nimbus/db/[db_chain, select_backend],
|
||||
../nimbus/vm_state,
|
||||
../nimbus/[vm_state, vm_types],
|
||||
../nimbus/p2p/executor
|
||||
|
||||
const
|
||||
@ -26,13 +26,12 @@ proc validateBlock(chainDB: BaseChainDB, blockNumber: BlockNumber): BlockNumber
|
||||
let transaction = chainDB.db.beginTransaction()
|
||||
defer: transaction.dispose()
|
||||
|
||||
chainDB.initStateDB(parent.stateRoot)
|
||||
for i in 0 ..< numBlocks:
|
||||
stdout.write blockNumber + i.u256
|
||||
stdout.write "\r"
|
||||
|
||||
let
|
||||
vmState = newBaseVMState(chainDB.stateDB, headers[i], chainDB)
|
||||
vmState = BaseVMState.new(parent, headers[i], chainDB)
|
||||
validationResult = vmState.processBlockNotPoA(headers[i], bodies[i])
|
||||
|
||||
if validationResult != ValidationResult.OK:
|
||||
|
@ -213,9 +213,7 @@ proc initDatabase*(networkId = MainNet): (BaseVMState, BaseChainDB) =
|
||||
difficulty: db.config.calcDifficulty(timestamp, parent),
|
||||
gasLimit: 100_000
|
||||
)
|
||||
|
||||
db.initStateDB(parent.stateRoot)
|
||||
let vmState = newBaseVMState(db.stateDB, header, db)
|
||||
vmState = BaseVMState.new(header, db)
|
||||
|
||||
(vmState, db)
|
||||
|
||||
|
Binary file not shown.
@ -13,6 +13,10 @@ import
|
||||
stew/results,
|
||||
zlib
|
||||
|
||||
const
|
||||
lineBufStrLen = 512
|
||||
outBufSize = 2048
|
||||
|
||||
type
|
||||
GUnzip = object
|
||||
mz: ZStream
|
||||
@ -20,7 +24,7 @@ type
|
||||
# fields used in explode()
|
||||
inCache: string
|
||||
inCount: uint
|
||||
outBuf: array[4096,char]
|
||||
outBuf: array[outBufSize,char]
|
||||
outCount: uint
|
||||
outDoneOK: bool
|
||||
|
||||
@ -107,7 +111,7 @@ proc open*(state: var GUnzip; fileName: string):
|
||||
state.reset
|
||||
|
||||
var
|
||||
strBuf = 1024.newString
|
||||
strBuf = lineBufStrLen.newString
|
||||
start = 10
|
||||
rc = state.mz.inflateInit2(Z_RAW_DEFLATE)
|
||||
doAssert rc == Z_OK
|
@ -63,6 +63,17 @@ proc dumpGroupBeginNl*(db: BaseChainDB;
|
||||
|
||||
proc dumpGroupNl*(db: BaseChainDB; headers: openArray[BlockHeader];
|
||||
bodies: openArray[BlockBody]): string =
|
||||
## Add this below the line `transaction.commit()` in the function
|
||||
## `p2p/chain.persist_blocks.persistBlocksImpl()`:
|
||||
## ::
|
||||
## dumpStream.write c.db.dumpGroupNl(headers,bodies)
|
||||
##
|
||||
## where `dumpStream` is some stream (think of `stdout`) of type `File`
|
||||
## that could be initialised with
|
||||
## ::
|
||||
## var dumpStream: File
|
||||
## dumpStream.open("./dump-stream.out", fmWrite)
|
||||
##
|
||||
db.dumpGroupBeginNl(headers) &
|
||||
toSeq(countup(0, headers.len-1))
|
||||
.mapIt(dumpGroupBlockNl(headers[it], bodies[it]))
|
@ -16,7 +16,7 @@ import
|
||||
../nimbus/transaction,
|
||||
../nimbus/vm_state,
|
||||
../nimbus/vm_types,
|
||||
./test_clique/undump,
|
||||
./replay/undump,
|
||||
eth/[common, p2p, trie/db],
|
||||
unittest2
|
||||
|
||||
@ -33,8 +33,7 @@ const
|
||||
|
||||
goerliCapture: CaptureSpecs = (
|
||||
network: GoerliNet,
|
||||
# file: "goerli68161.txt.gz",
|
||||
file: "goerli51840.txt.gz",
|
||||
file: "goerli68161.txt.gz",
|
||||
numBlocks: 5500, # unconditionally load blocks
|
||||
numTxs: 10) # txs following (not in block chain)
|
||||
|
||||
@ -96,10 +95,7 @@ proc importBlocks(cdb: BaseChainDB; h: seq[BlockHeader]; b: seq[BlockBody]) =
|
||||
raiseAssert "persistBlocks() failed at block #" & $h[0].blockNumber
|
||||
|
||||
proc getVmState(cdb: BaseChainDB; number: BlockNumber): BaseVMState =
|
||||
let
|
||||
topHeader = cdb.getBlockHeader(number)
|
||||
accounts = AccountsCache.init(cdb.db, topHeader.stateRoot, cdb.pruneTrie)
|
||||
result = accounts.newBaseVMState(topHeader, cdb)
|
||||
BaseVMState.new(cdb.getBlockHeader(number), cdb)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Crash test function, finding out about how the transaction framework works ..
|
||||
|
@ -240,11 +240,13 @@ proc importBlock(tester: var Tester, chainDB: BaseChainDB,
|
||||
)
|
||||
|
||||
deepCopy(result, preminedBlock)
|
||||
# we need to reinit the state if the stateRoot appear to be different
|
||||
# with the one in stateDB
|
||||
chainDB.initStateDB(parentHeader.stateRoot)
|
||||
let tracerFlags: set[TracerFlags] = if tester.trace: {TracerFlags.EnableTracing} else : {}
|
||||
tester.vmState = newBaseVMState(chainDB.stateDB, baseHeaderForImport, chainDB, tracerFlags)
|
||||
|
||||
tester.vmState = BaseVMState.new(
|
||||
parentHeader,
|
||||
baseHeaderForImport,
|
||||
chainDB,
|
||||
(if tester.trace: {TracerFlags.EnableTracing} else: {}),
|
||||
chainDB.pruneTrie)
|
||||
|
||||
let body = BlockBody(
|
||||
transactions: result.txs,
|
||||
@ -386,12 +388,16 @@ proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = fal
|
||||
continue
|
||||
|
||||
var tester = parseTester(fixture, testStatusIMPL)
|
||||
var chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = test_config.getConfiguration().pruning)
|
||||
chainDB.initStateDB(emptyRlpHash)
|
||||
setupStateDB(fixture["pre"], chainDB.stateDB)
|
||||
chainDB.stateDB.persist()
|
||||
|
||||
check chainDB.stateDB.rootHash == tester.genesisHeader.stateRoot
|
||||
let
|
||||
pruneTrie = test_config.getConfiguration().pruning
|
||||
chainDB = newBaseChainDB(newMemoryDb(), pruneTrie)
|
||||
stateDB = AccountsCache.init(chainDB.db, emptyRlpHash, chainDB.pruneTrie)
|
||||
|
||||
setupStateDB(fixture["pre"], stateDB)
|
||||
stateDB.persist()
|
||||
|
||||
check stateDB.rootHash == tester.genesisHeader.stateRoot
|
||||
|
||||
tester.debugMode = debugMode
|
||||
tester.trace = trace
|
||||
|
@ -20,13 +20,17 @@ import
|
||||
],
|
||||
../nimbus/utils/ec_recover,
|
||||
../nimbus/[config, utils, constants, context],
|
||||
./test_clique/[pool, undump],
|
||||
./test_clique/pool,
|
||||
./replay/undump,
|
||||
eth/[common, keys],
|
||||
stint, stew/byteutils,
|
||||
stint,
|
||||
unittest2
|
||||
|
||||
const
|
||||
goerliCapture = "test_clique" / "goerli51840.txt.gz"
|
||||
baseDir = [".", "tests", ".." / "tests", $DirSep] # path containg repo
|
||||
repoDir = ["test_clique", "replay", "status"] # alternative repos
|
||||
|
||||
goerliCapture = "goerli68161.txt.gz"
|
||||
groupReplayTransactions = 7
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@ -50,6 +54,14 @@ proc ppRow(elapsed: Duration): string =
|
||||
let ms = elapsed.inMilliSeconds + 500
|
||||
"x".repeat(ms div 1000)
|
||||
|
||||
proc findFilePath(file: string): string =
|
||||
result = "?unknown?" / file
|
||||
for dir in baseDir:
|
||||
for repo in repoDir:
|
||||
let path = dir / repo / file
|
||||
if path.fileExists:
|
||||
return path
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Test Runners
|
||||
# ------------------------------------------------------------------------------
|
||||
@ -114,7 +126,7 @@ proc runCliqueSnapshot(noisy = true; postProcessOk = false; testId: int) =
|
||||
|
||||
|
||||
proc runGoerliReplay(noisy = true; showElapsed = false,
|
||||
dir = "tests"; captureFile = goerliCapture,
|
||||
captureFile = goerliCapture,
|
||||
startAtBlock = 0u64; stopAfterBlock = 0u64) =
|
||||
var
|
||||
pool = newVoterPool()
|
||||
@ -122,15 +134,19 @@ proc runGoerliReplay(noisy = true; showElapsed = false,
|
||||
cInx = 0
|
||||
stoppedOk = false
|
||||
|
||||
let
|
||||
fileInfo = captureFile.splitFile.name.split(".")[0]
|
||||
filePath = captureFile.findFilePath
|
||||
|
||||
pool.debug = noisy
|
||||
pool.verifyFrom = startAtBlock
|
||||
|
||||
let stopThreshold = if stopAfterBlock == 0u64: uint64.high.u256
|
||||
else: stopAfterBlock.u256
|
||||
|
||||
suite "Replay Goerli Chain":
|
||||
suite &"Replay Goerli chain from {fileInfo} capture":
|
||||
|
||||
for w in (dir / captureFile).undumpNextGroup:
|
||||
for w in filePath.undumpNextGroup:
|
||||
|
||||
if w[0][0].blockNumber == 0.u256:
|
||||
# Verify Genesis
|
||||
@ -197,7 +213,7 @@ proc runGoerliReplay(noisy = true; showElapsed = false,
|
||||
|
||||
|
||||
proc runGoerliBaybySteps(noisy = true;
|
||||
dir = "tests"; captureFile = goerliCapture,
|
||||
captureFile = goerliCapture,
|
||||
stopAfterBlock = 0u64) =
|
||||
var
|
||||
pool = newVoterPool()
|
||||
@ -205,12 +221,15 @@ proc runGoerliBaybySteps(noisy = true;
|
||||
|
||||
pool.debug = noisy
|
||||
|
||||
let stopThreshold = if stopAfterBlock == 0u64: 20.u256
|
||||
else: stopAfterBlock.u256
|
||||
let
|
||||
fileInfo = captureFile.splitFile.name.split(".")[0]
|
||||
filePath = captureFile.findFilePath
|
||||
stopThreshold = if stopAfterBlock == 0u64: 20.u256
|
||||
else: stopAfterBlock.u256
|
||||
|
||||
suite "Replay Goerli Chain Transactions Single Blockwise":
|
||||
suite &"Replay Goerli chain from {fileInfo} capture, single blockwise":
|
||||
|
||||
for w in (dir / captureFile).undumpNextGroup:
|
||||
for w in filePath.undumpNextGroup:
|
||||
if stoppedOk:
|
||||
break
|
||||
if w[0][0].blockNumber == 0.u256:
|
||||
@ -238,13 +257,14 @@ proc runGoerliBaybySteps(noisy = true;
|
||||
discard
|
||||
|
||||
proc cliqueMiscTests() =
|
||||
let
|
||||
prvKeyFile = "private.key".findFilePath
|
||||
|
||||
suite "clique misc":
|
||||
test "signer func":
|
||||
const
|
||||
engineSigner = "658bdf435d810c91414ec09147daa6db62406379"
|
||||
privateKey = "tests" / "test_clique" / "private.key"
|
||||
|
||||
let
|
||||
engineSigner = "658bdf435d810c91414ec09147daa6db62406379"
|
||||
privateKey = prvKeyFile
|
||||
conf = makeConfig(@["--engine-signer:" & engineSigner, "--import-key:" & privateKey])
|
||||
ctx = newEthContext()
|
||||
|
||||
@ -292,24 +312,32 @@ when isMainModule:
|
||||
# `test_clique/indiump.dumpGroupNl()`
|
||||
# placed at the end of
|
||||
# `p2p/chain/persist_blocks.persistBlocks()`.
|
||||
captureFile = "goerli504192.txt.gz"
|
||||
captureFile = goerliCapture
|
||||
#captureFile = "dump-stream.out.gz"
|
||||
|
||||
proc goerliReplay(noisy = true; showElapsed = true;
|
||||
dir = "/status"; captureFile = captureFile;
|
||||
startAtBlock = 0u64; stopAfterBlock = 0u64) =
|
||||
proc goerliReplay(noisy = true;
|
||||
showElapsed = true;
|
||||
captureFile = captureFile;
|
||||
startAtBlock = 0u64;
|
||||
stopAfterBlock = 0u64) =
|
||||
runGoerliReplay(
|
||||
noisy = noisy, showElapsed = showElapsed,
|
||||
dir = dir, captureFile = captureFile,
|
||||
startAtBlock = startAtBlock, stopAfterBlock = stopAfterBlock)
|
||||
noisy = noisy,
|
||||
showElapsed = showElapsed,
|
||||
captureFile = captureFile,
|
||||
startAtBlock = startAtBlock,
|
||||
stopAfterBlock = stopAfterBlock)
|
||||
|
||||
# local path is: nimbus-eth1/tests
|
||||
let noisy = defined(debug)
|
||||
|
||||
#[let noisy = defined(debug)
|
||||
noisy.runCliqueSnapshot(true)
|
||||
noisy.runCliqueSnapshot(false)
|
||||
noisy.runGoerliBaybySteps(dir = ".")
|
||||
noisy.runGoerliReplay(dir = ".", startAtBlock = 31100u64)]#
|
||||
noisy.runGoerliBaybySteps
|
||||
noisy.runGoerliReplay(startAtBlock = 31100u64)
|
||||
|
||||
#noisy.goerliReplay(startAtBlock = 31100u64)
|
||||
#noisy.goerliReplay(startAtBlock = 194881u64, stopAfterBlock = 198912u64)
|
||||
|
||||
cliqueMiscTests()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -33,17 +33,11 @@ type
|
||||
trace: bool
|
||||
index: int
|
||||
|
||||
GST_VMState = ref object of BaseVMState
|
||||
|
||||
proc toBytes(x: string): seq[byte] =
|
||||
result = newSeq[byte](x.len)
|
||||
for i in 0..<x.len: result[i] = x[i].byte
|
||||
|
||||
proc newGST_VMState(ac: AccountsCache, header: BlockHeader, chainDB: BaseChainDB, tracerFlags: set[TracerFlags]): GST_VMState =
|
||||
new result
|
||||
result.init(ac, header, chainDB, tracerFlags)
|
||||
|
||||
method getAncestorHash*(vmState: GST_VMState, blockNumber: BlockNumber): Hash256 {.gcsafe.} =
|
||||
method getAncestorHash*(vmState: BaseVMState; blockNumber: BlockNumber): Hash256 {.gcsafe.} =
|
||||
if blockNumber >= vmState.blockNumber:
|
||||
return
|
||||
elif blockNumber < 0:
|
||||
@ -90,11 +84,15 @@ proc dumpDebugData(tester: Tester, vmState: BaseVMState, sender: EthAddress, gas
|
||||
writeFile("debug_" & tester.name & "_" & $tester.index & status & ".json", debugData.pretty())
|
||||
|
||||
proc testFixtureIndexes(tester: Tester, testStatusIMPL: var TestStatus) =
|
||||
var tracerFlags: set[TracerFlags] = if tester.trace: {TracerFlags.EnableTracing} else : {}
|
||||
let
|
||||
chainDB = newBaseChainDB(newMemoryDb(), getConfiguration().pruning)
|
||||
vmState = BaseVMState.new(
|
||||
parent = BlockHeader(stateRoot: emptyRlpHash),
|
||||
header = tester.header,
|
||||
chainDB = chainDB,
|
||||
tracerFlags = (if tester.trace: {TracerFlags.EnableTracing} else: {}),
|
||||
pruneTrie = chainDB.pruneTrie)
|
||||
|
||||
var chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = getConfiguration().pruning)
|
||||
chainDB.initStateDB(emptyRlpHash)
|
||||
var vmState = newGST_VMState(chainDB.stateDB, tester.header, chainDB, tracerFlags)
|
||||
var gasUsed: GasInt
|
||||
let sender = tester.tx.getSender()
|
||||
|
||||
|
@ -143,7 +143,7 @@ func getHexadecimalInt*(j: JsonNode): int64 =
|
||||
data = fromHex(StUInt[64], j.getStr)
|
||||
result = cast[int64](data)
|
||||
|
||||
proc setupStateDB*(wantedState: JsonNode, stateDB: var AccountsCache) =
|
||||
proc setupStateDB*(wantedState: JsonNode, stateDB: AccountsCache) =
|
||||
for ac, accountData in wantedState:
|
||||
let account = ethAddressFromHex(ac)
|
||||
for slot, value in accountData{"storage"}:
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
import
|
||||
std/[os, sequtils, strformat, strutils, times],
|
||||
./test_clique/gunzip,
|
||||
./replay/gunzip,
|
||||
../nimbus/utils/[pow, pow/pow_cache, pow/pow_dataset],
|
||||
eth/[common],
|
||||
unittest2
|
||||
|
@ -12,6 +12,7 @@ import
|
||||
|
||||
../nimbus/[vm_computation,
|
||||
vm_state,
|
||||
vm_types,
|
||||
forks,
|
||||
constants,
|
||||
vm_precompiles,
|
||||
@ -24,7 +25,7 @@ import
|
||||
|
||||
proc initAddress(i: byte): EthAddress = result[19] = i
|
||||
|
||||
template doTest(fixture: JsonNode, fork: Fork, address: PrecompileAddresses): untyped =
|
||||
template doTest(fixture: JsonNode; vmState: BaseVMState; fork: Fork, address: PrecompileAddresses): untyped =
|
||||
for test in fixture:
|
||||
let
|
||||
expectedErr = test.hasKey("ExpectedError")
|
||||
@ -63,33 +64,32 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
||||
fork = parseEnum[Fork](fixtures["fork"].getStr.toLowerAscii)
|
||||
data = fixtures["data"]
|
||||
privateKey = PrivateKey.fromHex("7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d")[]
|
||||
header = BlockHeader(blockNumber: 1.u256)
|
||||
chainDB = newBaseChainDB(newMemoryDb())
|
||||
|
||||
chainDB.initStateDB(header.stateRoot)
|
||||
let vmState = newBaseVMState(chainDB.stateDB, header, chainDB)
|
||||
vmState = BaseVMState.new(
|
||||
BlockHeader(blockNumber: 1.u256),
|
||||
BlockHeader(),
|
||||
newBaseChainDB(newMemoryDb()))
|
||||
|
||||
case toLowerAscii(label)
|
||||
of "ecrecover": data.doTest(fork, paEcRecover)
|
||||
of "sha256" : data.doTest(fork, paSha256)
|
||||
of "ripemd" : data.doTest(fork, paRipeMd160)
|
||||
of "identity" : data.doTest(fork, paIdentity)
|
||||
of "modexp" : data.doTest(fork, paModExp)
|
||||
of "bn256add" : data.doTest(fork, paEcAdd)
|
||||
of "bn256mul" : data.doTest(fork, paEcMul)
|
||||
of "ecpairing": data.doTest(fork, paPairing)
|
||||
of "blake2f" : data.doTest(fork, paBlake2bf)
|
||||
of "ecrecover": data.doTest(vmState, fork, paEcRecover)
|
||||
of "sha256" : data.doTest(vmState, fork, paSha256)
|
||||
of "ripemd" : data.doTest(vmState, fork, paRipeMd160)
|
||||
of "identity" : data.doTest(vmState, fork, paIdentity)
|
||||
of "modexp" : data.doTest(vmState, fork, paModExp)
|
||||
of "bn256add" : data.doTest(vmState, fork, paEcAdd)
|
||||
of "bn256mul" : data.doTest(vmState, fork, paEcMul)
|
||||
of "ecpairing": data.doTest(vmState, fork, paPairing)
|
||||
of "blake2f" : data.doTest(vmState, fork, paBlake2bf)
|
||||
# EIP 2537: disabled
|
||||
# reason: not included in berlin
|
||||
#of "blsg1add" : data.doTest(fork, paBlsG1Add)
|
||||
#of "blsg1mul" : data.doTest(fork, paBlsG1Mul)
|
||||
#of "blsg1multiexp" : data.doTest(fork, paBlsG1MultiExp)
|
||||
#of "blsg2add" : data.doTest(fork, paBlsG2Add)
|
||||
#of "blsg2mul" : data.doTest(fork, paBlsG2Mul)
|
||||
#of "blsg2multiexp": data.doTest(fork, paBlsG2MultiExp)
|
||||
#of "blspairing": data.doTest(fork, paBlsPairing)
|
||||
#of "blsmapg1": data.doTest(fork, paBlsMapG1)
|
||||
#of "blsmapg2": data.doTest(fork, paBlsMapG2)
|
||||
#of "blsg1add" : data.doTest(vmState, fork, paBlsG1Add)
|
||||
#of "blsg1mul" : data.doTest(vmState, fork, paBlsG1Mul)
|
||||
#of "blsg1multiexp" : data.doTest(vmState, fork, paBlsG1MultiExp)
|
||||
#of "blsg2add" : data.doTest(vmState, fork, paBlsG2Add)
|
||||
#of "blsg2mul" : data.doTest(vmState, fork, paBlsG2Mul)
|
||||
#of "blsg2multiexp": data.doTest(vmState, fork, paBlsG2MultiExp)
|
||||
#of "blspairing": data.doTest(vmState, fork, paBlsPairing)
|
||||
#of "blsmapg1": data.doTest(vmState, fork, paBlsMapG1)
|
||||
#of "blsmapg2": data.doTest(vmState, fork, paBlsMapG2)
|
||||
else:
|
||||
echo "Unknown test vector '" & $label & "'"
|
||||
testStatusIMPL = SKIPPED
|
||||
|
@ -11,7 +11,8 @@ import
|
||||
json_rpc/[rpcserver, rpcclient], eth/common as eth_common,
|
||||
eth/[rlp, keys, trie/db, p2p/private/p2p_types],
|
||||
../nimbus/rpc/[common, p2p, hexstrings, rpc_types, rpc_utils],
|
||||
../nimbus/[constants, vm_state, config, genesis, utils, transaction],
|
||||
../nimbus/[constants, config, genesis, utils, transaction,
|
||||
vm_state, vm_types],
|
||||
../nimbus/db/[accounts_cache, db_chain],
|
||||
../nimbus/p2p/[chain, executor, executor/executor_helpers],
|
||||
../nimbus/sync/protocol_eth65,
|
||||
@ -31,6 +32,11 @@ template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0]
|
||||
const sigPath = &"{sourceDir}{DirSep}rpcclient{DirSep}ethcallsigs.nim"
|
||||
createRpcSigs(RpcSocketClient, sigPath)
|
||||
|
||||
const
|
||||
zeroAddress = block:
|
||||
var rc: EthAddress
|
||||
rc
|
||||
|
||||
type
|
||||
TestEnv = object
|
||||
txHash: Hash256
|
||||
@ -51,12 +57,16 @@ proc setupEnv(chainDB: BaseChainDB, signer, ks2: EthAddress, ctx: EthContext): T
|
||||
PUSH1 "0x1C" # RETURN OFFSET at 28
|
||||
RETURN
|
||||
|
||||
chainDB.initStateDB(parent.stateRoot)
|
||||
chainDB.stateDB.setCode(ks2, code)
|
||||
chainDB.stateDB.addBalance(signer, 9_000_000_000.u256)
|
||||
var
|
||||
vmState = newBaseVMState(chainDB.stateDB, BlockHeader(parentHash: parentHash), chainDB)
|
||||
zeroAddress: EthAddress
|
||||
let
|
||||
vmHeader = BlockHeader(parentHash: parentHash)
|
||||
vmState = BaseVMState.new(
|
||||
parent = BlockHeader(stateRoot: parent.stateRoot),
|
||||
header = vmHeader,
|
||||
chainDB = chainDB,
|
||||
pruneTrie = chainDB.pruneTrie)
|
||||
|
||||
vmState.stateDB.setCode(ks2, code)
|
||||
vmState.stateDB.addBalance(signer, 9_000_000_000.u256)
|
||||
|
||||
let
|
||||
unsignedTx1 = Transaction(
|
||||
@ -85,7 +95,7 @@ proc setupEnv(chainDB: BaseChainDB, signer, ks2: EthAddress, ctx: EthContext): T
|
||||
vmState.cumulativeGasUsed = 0
|
||||
for txIndex, tx in txs:
|
||||
let sender = tx.getSender()
|
||||
discard vmState.processTransaction(tx, sender, vmState.blockHeader)
|
||||
discard vmState.processTransaction(tx, sender, vmHeader)
|
||||
vmState.receipts[txIndex] = makeReceipt(vmState, tx.txType)
|
||||
|
||||
let
|
||||
|
Loading…
x
Reference in New Issue
Block a user