nimbus-eth1/nimbus/rpc/experimental.nim

133 lines
4.7 KiB
Nim

# Nimbus
# Copyright (c) 2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
{.push raises: [].}
import
std/[typetraits],
json_rpc/rpcserver, stint, web3/conversions,
eth/p2p,
../[transaction, vm_state, constants, vm_types],
../db/state_db,
rpc_types, rpc_utils,
../core/tx_pool,
../common/common,
../utils/utils,
../beacon/web3_eth_conv,
./filters,
../core/executor/process_block,
../db/ledger,
../../stateless/[witness_verification, witness_types],
./p2p
type
BlockHeader = eth_types.BlockHeader
ReadOnlyStateDB = state_db.ReadOnlyStateDB
proc getBlockWitness*(
com: CommonRef,
blockHeader: BlockHeader,
statePostExecution: bool): (KeccakHash, BlockWitness, WitnessFlags)
{.raises: [RlpError, BlockNotFound, ValueError, CatchableError].} =
let
chainDB = com.db
blockHash = chainDB.getBlockHash(blockHeader.blockNumber)
blockBody = chainDB.getBlockBody(blockHash)
# Initializing the VM will throw a Defect if the state doesn't exist.
# Once we enable pruning we will need to check if the block state has been pruned
# before trying to initialize the VM as we do here.
vmState = BaseVMState.new(blockHeader, com)
flags = if vmState.fork >= FKSpurious: {wfEIP170} else: {}
vmState.generateWitness = true # Enable saving witness data
var dbTx = vmState.com.db.beginTransaction()
defer: dbTx.dispose()
# Execute the block of transactions and collect the keys of the touched account state
let processBlockResult = processBlock(vmState, blockHeader, blockBody)
doAssert processBlockResult == ValidationResult.OK
let mkeys = vmState.stateDB.makeMultiKeys()
if statePostExecution:
result = (vmState.stateDB.rootHash, vmState.buildWitness(mkeys), flags)
else:
# Reset state to what it was before executing the block of transactions
let initialState = BaseVMState.new(blockHeader, com)
result = (initialState.stateDB.rootHash, initialState.buildWitness(mkeys), flags)
dbTx.rollback()
proc getBlockProofs*(
accDB: ReadOnlyStateDB,
witnessRoot: KeccakHash,
witness: BlockWitness,
flags: WitnessFlags): seq[ProofResponse] {.raises: [RlpError].} =
if witness.len() == 0:
return @[]
let verifyWitnessResult = verifyWitness(witnessRoot, witness, flags)
doAssert verifyWitnessResult.isOk()
var blockProofs = newSeqOfCap[ProofResponse](verifyWitnessResult.value().len())
for address, account in verifyWitnessResult.value():
var slots = newSeqOfCap[UInt256](account.storage.len())
for slotKey, _ in account.storage:
slots.add(slotKey)
blockProofs.add(getProof(accDB, address, slots))
return blockProofs
proc setupExpRpc*(com: CommonRef, server: RpcServer) =
let chainDB = com.db
proc getStateDB(header: BlockHeader): ReadOnlyStateDB =
## Retrieves the account db from canonical head
# we don't use accounst_cache here because it's only read operations
let ac = newAccountStateDB(chainDB, header.stateRoot, com.pruneTrie)
result = ReadOnlyStateDB(ac)
server.rpc("exp_getWitnessByBlockNumber") do(quantityTag: BlockTag, statePostExecution: bool) -> seq[byte]:
## Returns the block witness for a block by block number or tag.
##
## quantityTag: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
## statePostExecution: bool which indicates whether to return the witness based on the state before or after executing the block.
## Returns seq[byte]
let
blockHeader = chainDB.headerFromTag(quantityTag)
(_, witness, _) = getBlockWitness(com, blockHeader, statePostExecution)
return witness
server.rpc("exp_getProofsByBlockNumber") do(quantityTag: BlockTag, statePostExecution: bool) -> seq[ProofResponse]:
## Returns the block proofs for a block by block number or tag.
##
## quantityTag: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
## statePostExecution: bool which indicates whether to return the proofs based on the state before or after executing the block.
## Returns seq[ProofResponse]
let
blockHeader = chainDB.headerFromTag(quantityTag)
(witnessRoot, witness, flags) = getBlockWitness(com, blockHeader, statePostExecution)
let accDB = if statePostExecution:
getStateDB(blockHeader)
else:
getStateDB(chainDB.getBlockHeader(blockHeader.parentHash))
return getBlockProofs(accDB, witnessRoot, witness, flags)