112 lines
4.1 KiB
Nim
112 lines
4.1 KiB
Nim
|
# 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")
|