nimbus-eth1/nimbus/rpc/engine_api.nim

112 lines
4.1 KiB
Nim
Raw Normal View History

# Nimbus
# Copyright (c) 2018 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.
import
std/[typetraits, times],
stew/[objects, results],
json_rpc/[rpcserver, errors],
web3/[conversions, engine_api_types],
eth/[trie, rlp, common, trie/db],
".."/db/db_chain,
".."/p2p/chain/[chain_desc, persist_blocks],
".."/[sealer, utils, constants]
import eth/common/eth_types except BlockHeader
type EthBlockHeader = eth_types.BlockHeader
# TODO move this to stew/objects
template newClone*[T: not ref](x: T): ref T =
# TODO not nil in return type: https://github.com/nim-lang/Nim/issues/14146
# TODO use only when x is a function call that returns a new instance!
let res = new typeof(x) # TODO safe to do noinit here?
res[] = x
res
template asEthHash*(hash: engine_api_types.BlockHash): Hash256 =
Hash256(data: distinctBase(hash))
template unsafeQuantityToInt64(q: Quantity): int64 =
int64 q
proc calcRootHashRlp*(items: openArray[seq[byte]]): Hash256 =
var tr = initHexaryTrie(newMemoryDB())
for i, t in items:
tr.put(rlp.encode(i), t)
return tr.rootHash()
proc toBlockHeader(payload: ExecutionPayload): eth_types.BlockHeader =
discard payload.random # TODO: What should this be used for?
let transactions = seq[seq[byte]](payload.transactions)
let txRoot = calcRootHashRlp(transactions)
EthBlockHeader(
parentHash : payload.parentHash.asEthHash,
ommersHash : EMPTY_UNCLE_HASH,
coinbase : EthAddress payload.coinbase,
stateRoot : payload.stateRoot.asEthHash,
txRoot : txRoot,
receiptRoot : payload.receiptRoot.asEthHash,
bloom : distinctBase(payload.logsBloom),
difficulty : default(DifficultyInt),
blockNumber : payload.blockNumber.distinctBase.u256,
gasLimit : payload.gasLimit.unsafeQuantityToInt64,
gasUsed : payload.gasUsed.unsafeQuantityToInt64,
timestamp : fromUnix payload.timestamp.unsafeQuantityToInt64,
extraData : distinctBase payload.extraData,
mixDigest : default(Hash256),
nonce : default(BlockNonce),
fee : some payload.baseFeePerGas
)
proc toBlockBody(payload: ExecutionPayload): BlockBody =
# TODO the transactions from the payload have to be converted here
discard payload.transactions
proc setupEngineAPI*(sealingEngine: SealingEngineRef, server: RpcServer) =
var payloadsInstance = newClone(newSeq[ExecutionPayload]())
template payloads: auto = payloadsInstance[]
server.rpc("engine_getPayload") do(payloadId: Quantity) -> ExecutionPayload:
if payloadId.uint64 > high(int).uint64 or
int(payloadId) >= payloads.len:
raise (ref InvalidRequest)(code: UNKNOWN_PAYLOAD, msg: "Unknown payload")
return payloads[int payloadId]
server.rpc("engine_executePayload") do(payload: ExecutionPayload) -> ExecutePayloadResponse:
# TODO
if payload.transactions.len > 0:
# Give us a break, a block with transcations? instructions to execute?
# Nah, we are syncing!
return ExecutePayloadResponse(status: $PayloadExecutionStatus.syncing)
let
headers = [payload.toBlockHeader]
bodies = [payload.toBlockBody]
if rlpHash(headers[0]) != payload.blockHash.asEthHash:
return ExecutePayloadResponse(status: $PayloadExecutionStatus.invalid)
if sealingEngine.chain.persistBlocks(headers, bodies) != ValidationResult.OK:
return ExecutePayloadResponse(status: $PayloadExecutionStatus.invalid)
return ExecutePayloadResponse(status: $PayloadExecutionStatus.valid)
server.rpc("engine_forkchoiceUpdated") do(update: ForkChoiceUpdate):
let
db = sealingEngine.chain.db
newHead = update.headBlockHash.asEthHash
# TODO Use the finalized block information to prune any alterantive
# histories that are no longer relevant
discard update.finalizedBlockHash
if not db.setHead(newHead):
raise (ref InvalidRequest)(code: UNKNOWN_HEADER, msg: "Uknown head block hash")