139 lines
6.1 KiB
Nim
139 lines
6.1 KiB
Nim
|
import
|
||
|
chronos,
|
||
|
options,
|
||
|
sequtils,
|
||
|
times,
|
||
|
nimcrypto,
|
||
|
os,
|
||
|
stew/byteutils,
|
||
|
stew/results,
|
||
|
json_rpc/rpcclient,
|
||
|
eth/[rlp, common/eth_types, p2p],
|
||
|
core/chain/[chain_desc, persist_blocks],
|
||
|
core/executor/process_block,
|
||
|
db/[db_chain, select_backend, storage_types, distinct_tries, incomplete_db, accounts_cache],
|
||
|
eth/trie/[db, trie_defs],
|
||
|
rpc/rpc_utils,
|
||
|
evm/async/[data_sources, operations, data_sources/json_rpc_data_source],
|
||
|
./vm_state, ./vm_types,
|
||
|
./sync/stateless,
|
||
|
chronicles
|
||
|
|
||
|
from strutils import parseInt, startsWith
|
||
|
from common/chain_config import MainNet, networkParams
|
||
|
from common/common import initializeEmptyDb
|
||
|
|
||
|
|
||
|
proc coinbasesOfThisBlockAndUncles(header: BlockHeader, body: BlockBody): seq[EthAddress] =
|
||
|
result.add header.coinbase
|
||
|
for uncle in body.uncles:
|
||
|
result.add(uncle.coinbase)
|
||
|
|
||
|
proc createVmStateForStatelessMode*(com: CommonRef, header: BlockHeader, body: BlockBody,
|
||
|
parentHeader: BlockHeader, asyncFactory: AsyncOperationFactory): Result[BaseVMState, string]
|
||
|
{.inline.} =
|
||
|
let vmState = BaseVMState()
|
||
|
if not vmState.statelessInit(parentHeader, header, com, asyncFactory, {}):
|
||
|
return err("Cannot initialise VmState for block number " & $(header.blockNumber))
|
||
|
waitFor(ifNecessaryGetAccounts(vmState, coinbasesOfThisBlockAndUncles(header, body)))
|
||
|
ok(vmState)
|
||
|
|
||
|
|
||
|
|
||
|
proc statelesslyRunBlock*(asyncDataSource: AsyncDataSource, com: CommonRef, header: BlockHeader, body: BlockBody): Result[Hash256, string] =
|
||
|
try:
|
||
|
let t0 = now()
|
||
|
|
||
|
# FIXME-Adam: this doesn't feel like the right place for this; where should it go?
|
||
|
com.db.db.put(emptyRlpHash.data, emptyRlp)
|
||
|
|
||
|
let blockHash: Hash256 = header.blockHash
|
||
|
|
||
|
let asyncFactory = AsyncOperationFactory(maybeDataSource: some(asyncDataSource))
|
||
|
|
||
|
let parentHeader = waitFor(asyncDataSource.fetchBlockHeaderWithHash(header.parentHash))
|
||
|
com.db.persistHeaderToDbWithoutSetHeadOrScore(parentHeader)
|
||
|
|
||
|
info("statelessly running block", blockNumber=header.blockNumber, blockHash=blockHash, parentHash=header.parentHash, parentStateRoot=parentHeader.stateRoot, desiredNewStateRoot=header.stateRoot)
|
||
|
|
||
|
let vmState = createVmStateForStatelessMode(com, header, body, parentHeader, asyncFactory).get
|
||
|
let vres = processBlockNotPoA(vmState, header, body)
|
||
|
|
||
|
let elapsedTime = now() - t0
|
||
|
|
||
|
let headerStateRoot = header.stateRoot
|
||
|
let vmStateRoot = rootHash(vmState.stateDB)
|
||
|
info("finished statelessly running the block", vres=vres, elapsedTime=elapsedTime, durationSpentDoingFetches=durationSpentDoingFetches, fetchCounter=fetchCounter, headerStateRoot=headerStateRoot, vmStateRoot=vmStateRoot)
|
||
|
if headerStateRoot != vmStateRoot:
|
||
|
return err("State roots do not match: header says " & $(headerStateRoot) & ", vmState says " & $(vmStateRoot))
|
||
|
else:
|
||
|
if vres == ValidationResult.OK:
|
||
|
return ok(blockHash)
|
||
|
else:
|
||
|
return err("Error while statelessly running a block")
|
||
|
except:
|
||
|
let ex = getCurrentException()
|
||
|
echo getStackTrace(ex)
|
||
|
error "Got an exception while statelessly running a block", exMsg = ex.msg
|
||
|
return err("Error while statelessly running a block: " & $(ex.msg))
|
||
|
|
||
|
proc statelesslyRunBlock*(asyncDataSource: AsyncDataSource, com: CommonRef, blockHash: Hash256): Result[Hash256, string] =
|
||
|
let (header, body) = waitFor(asyncDataSource.fetchBlockHeaderAndBodyWithHash(blockHash))
|
||
|
let r = statelesslyRunBlock(asyncDataSource, com, header, body)
|
||
|
if r.isErr:
|
||
|
error("stateless execution failed", hash=blockHash, error=r.error)
|
||
|
else:
|
||
|
info("stateless execution succeeded", hash=blockHash, resultingHash=r.value)
|
||
|
return r
|
||
|
|
||
|
proc fetchBlockHeaderAndBodyForHashOrNumber(asyncDataSource: AsyncDataSource, hashOrNum: string): Future[(BlockHeader, BlockBody)] {.async.} =
|
||
|
if hashOrNum.startsWith("0x"):
|
||
|
return await asyncDataSource.fetchBlockHeaderAndBodyWithHash(hashOrNum.toHash)
|
||
|
else:
|
||
|
return await asyncDataSource.fetchBlockHeaderAndBodyWithNumber(u256(parseInt(hashOrNum)))
|
||
|
|
||
|
proc statelesslyRunSequentialBlocks*(asyncDataSource: AsyncDataSource, com: CommonRef, initialBlockNumber: BlockNumber): Result[Hash256, string] =
|
||
|
info("sequential stateless execution beginning", initialBlockNumber=initialBlockNumber)
|
||
|
var n = initialBlockNumber
|
||
|
while true:
|
||
|
let (header, body) = waitFor(asyncDataSource.fetchBlockHeaderAndBodyWithNumber(n))
|
||
|
let r = statelesslyRunBlock(asyncDataSource, com, header, body)
|
||
|
if r.isErr:
|
||
|
error("stateless execution failed", n=n, h=header.blockHash, error=r.error)
|
||
|
return r
|
||
|
else:
|
||
|
info("stateless execution succeeded", n=n, h=header.blockHash, resultingHash=r.value)
|
||
|
n = n + 1
|
||
|
|
||
|
proc statelesslyRunBlock*(asyncDataSource: AsyncDataSource, com: CommonRef, hashOrNum: string): Result[Hash256, string] =
|
||
|
let (header, body) = waitFor(fetchBlockHeaderAndBodyForHashOrNumber(asyncDataSource, hashOrNum))
|
||
|
return statelesslyRunBlock(asyncDataSource, com, header, body)
|
||
|
|
||
|
|
||
|
proc statelesslyRunTransaction*(asyncDataSource: AsyncDataSource, com: CommonRef, headerHash: Hash256, tx: Transaction) =
|
||
|
let t0 = now()
|
||
|
|
||
|
let (header, body) = waitFor(asyncDataSource.fetchBlockHeaderAndBodyWithHash(headerHash))
|
||
|
|
||
|
# FIXME-Adam: this doesn't feel like the right place for this; where should it go?
|
||
|
com.db.db.put(emptyRlpHash.data, emptyRlp)
|
||
|
|
||
|
let blockHash: Hash256 = header.blockHash
|
||
|
|
||
|
let transaction = com.db.db.beginTransaction()
|
||
|
defer: transaction.rollback() # intentionally throwing away the result of this execution
|
||
|
|
||
|
let asyncFactory = AsyncOperationFactory(maybeDataSource: some(asyncDataSource))
|
||
|
let parentHeader = waitFor(asyncDataSource.fetchBlockHeaderWithHash(header.parentHash))
|
||
|
com.db.persistHeaderToDbWithoutSetHeadOrScore(parentHeader)
|
||
|
|
||
|
let vmState = createVmStateForStatelessMode(com, header, body, parentHeader, asyncFactory).get
|
||
|
|
||
|
let r = processTransactions(vmState, header, @[tx])
|
||
|
if r.isErr:
|
||
|
error("error statelessly running tx", tx=tx, error=r.error)
|
||
|
else:
|
||
|
let elapsedTime = now() - t0
|
||
|
let gasUsed = vmState.cumulativeGasUsed
|
||
|
info("finished statelessly running the tx", elapsedTime=elapsedTime, gasUsed=gasUsed)
|