Migrate to Engine API spec version v1.0.0-alpha.5; More progress towards working M1
This commit is contained in:
parent
990846135a
commit
3cbb920406
|
@ -62,6 +62,9 @@ if not defined(macosx):
|
|||
# light-weight stack traces using libbacktrace and libunwind
|
||||
--define:nimStackTraceOverride
|
||||
switch("import", "libbacktrace")
|
||||
else:
|
||||
--stacktrace:on
|
||||
--linetrace:on
|
||||
|
||||
--define:nimOldCaseObjects # https://github.com/status-im/nim-confutils/issues/9
|
||||
# libnimbus.so needs position-independent code
|
||||
|
|
|
@ -103,6 +103,7 @@ const
|
|||
defaultEthWsPort = 8546
|
||||
defaultEthGraphqlPort = 8547
|
||||
defaultEngineApiPort = 8550
|
||||
defaultEngineApiWsPort = 8551
|
||||
defaultListenAddress = (static ValidIpAddress.init("0.0.0.0"))
|
||||
defaultAdminListenAddress = (static ValidIpAddress.init("127.0.0.1"))
|
||||
defaultListenAddressDesc = $defaultListenAddress & ", meaning all network interfaces"
|
||||
|
@ -328,6 +329,23 @@ type
|
|||
defaultValueDesc: defaultAdminListenAddressDesc
|
||||
name: "engine-api-address" .}: ValidIpAddress
|
||||
|
||||
engineApiWsEnabled* {.
|
||||
desc: "Enable the WebSocket Engine API"
|
||||
defaultValue: false
|
||||
name: "engine-api-ws" .}: bool
|
||||
|
||||
engineApiWsPort* {.
|
||||
desc: "Listening port for the WebSocket Engine API"
|
||||
defaultValue: defaultEngineApiWsPort
|
||||
defaultValueDesc: $defaultEngineApiWsPort
|
||||
name: "engine-api-ws-port" .}: Port
|
||||
|
||||
engineApiWsAddress* {.
|
||||
desc: "Listening address for the WebSocket Engine API"
|
||||
defaultValue: defaultAdminListenAddress
|
||||
defaultValueDesc: defaultAdminListenAddressDesc
|
||||
name: "engine-api-ws-address" .}: ValidIpAddress
|
||||
|
||||
nodeKeyHex* {.
|
||||
desc: "P2P node private key (as 32 bytes hex string)"
|
||||
defaultValue: ""
|
||||
|
|
|
@ -40,6 +40,7 @@ type
|
|||
NimbusNode = ref object
|
||||
rpcServer: RpcHttpServer
|
||||
engineApiServer: RpcHttpServer
|
||||
engineApiWsServer: RpcWebSocketServer
|
||||
ethNode: EthereumNode
|
||||
state: NimbusState
|
||||
graphqlServer: GraphqlHttpServerRef
|
||||
|
@ -157,7 +158,8 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf,
|
|||
|
||||
# Enable RPC APIs based on RPC flags and protocol flags
|
||||
let rpcFlags = conf.getRpcFlags()
|
||||
if RpcFlag.Eth in rpcFlags and ProtocolFlag.Eth in protocols:
|
||||
if (RpcFlag.Eth in rpcFlags and ProtocolFlag.Eth in protocols) or
|
||||
(conf.engineApiPort == conf.rpcPort):
|
||||
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.rpcServer)
|
||||
if RpcFlag.Debug in rpcFlags:
|
||||
setupDebugRpc(chainDB, nimbus.rpcServer)
|
||||
|
@ -176,7 +178,8 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf,
|
|||
|
||||
# Enable Websocket RPC APIs based on RPC flags and protocol flags
|
||||
let wsFlags = conf.getWsFlags()
|
||||
if RpcFlag.Eth in wsFlags and ProtocolFlag.Eth in protocols:
|
||||
if (RpcFlag.Eth in wsFlags and ProtocolFlag.Eth in protocols) or
|
||||
(conf.engineApiWsPort == conf.wsPort):
|
||||
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.wsRpcServer)
|
||||
if RpcFlag.Debug in wsFlags:
|
||||
setupDebugRpc(chainDB, nimbus.wsRpcServer)
|
||||
|
@ -219,13 +222,30 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf,
|
|||
nimbus.sealingEngine.start()
|
||||
|
||||
if conf.engineApiEnabled:
|
||||
nimbus.engineApiServer = newRpcHttpServer([
|
||||
initTAddress(conf.engineApiAddress, conf.engineApiPort)
|
||||
])
|
||||
setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiServer)
|
||||
nimbus.engineAPiServer.start()
|
||||
if conf.engineApiPort != conf.rpcPort:
|
||||
nimbus.engineApiServer = newRpcHttpServer([
|
||||
initTAddress(conf.engineApiAddress, conf.engineApiPort)
|
||||
])
|
||||
setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiServer)
|
||||
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.engineApiServer)
|
||||
nimbus.engineApiServer.start()
|
||||
else:
|
||||
setupEngineAPI(nimbus.sealingEngine, nimbus.rpcServer)
|
||||
|
||||
info "Starting engine API server", port = conf.engineApiPort
|
||||
|
||||
if conf.engineApiWsEnabled:
|
||||
if conf.engineApiWsPort != conf.wsPort:
|
||||
nimbus.engineApiWsServer = newRpcWebSocketServer(
|
||||
initTAddress(conf.engineApiWsAddress, conf.engineApiWsPort))
|
||||
setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiWsServer)
|
||||
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.engineApiWsServer)
|
||||
nimbus.engineApiWsServer.start()
|
||||
else:
|
||||
setupEngineAPI(nimbus.sealingEngine, nimbus.wsRpcServer)
|
||||
|
||||
info "Starting WebSocket engine API server", port = conf.engineApiWsPort
|
||||
|
||||
# metrics server
|
||||
if conf.metricsEnabled:
|
||||
info "Starting metrics HTTP server", address = conf.metricsAddress, port = conf.metricsPort
|
||||
|
@ -283,6 +303,8 @@ proc stop*(nimbus: NimbusNode, conf: NimbusConf) {.async, gcsafe.} =
|
|||
await nimbus.engineAPiServer.stop()
|
||||
if conf.wsEnabled:
|
||||
nimbus.wsRpcServer.stop()
|
||||
if conf.engineApiWsEnabled:
|
||||
nimbus.engineApiWsServer.stop()
|
||||
if conf.graphqlEnabled:
|
||||
await nimbus.graphqlServer.stop()
|
||||
if conf.engineSigner != ZERO_ADDRESS:
|
||||
|
|
|
@ -80,8 +80,9 @@ func toNextFork(n: BlockNumber): uint64 =
|
|||
else:
|
||||
result = n.truncate(uint64)
|
||||
|
||||
func isBlockAfterTtd*(c: Chain, blockNum: BlockNumber): bool =
|
||||
c.ttdReachedAt.isSome and blockNum > c.ttdReachedAt.get
|
||||
func isBlockAfterTtd*(c: Chain, blockHeader: BlockHeader): bool =
|
||||
# TODO: This should be fork aware
|
||||
c.ttdReachedAt.isSome and blockHeader.blockNumber > c.ttdReachedAt.get
|
||||
|
||||
func getNextFork(c: ChainConfig, fork: ChainFork): uint64 =
|
||||
let next: array[ChainFork, uint64] = [
|
||||
|
|
|
@ -74,7 +74,8 @@ proc persistBlocksImpl(c: Chain; headers: openarray[BlockHeader];
|
|||
return validationResult
|
||||
|
||||
if c.extraValidation and c.verifyFrom <= header.blockNumber:
|
||||
if c.db.config.poaEngine:
|
||||
let isBlockAfterTtd = c.isBlockAfterTtd(header)
|
||||
if c.db.config.poaEngine and not isBlockAfterTtd:
|
||||
var parent = if 0 < i: @[headers[i-1]] else: @[]
|
||||
let rc = c.clique.cliqueVerify(header,parent)
|
||||
if rc.isOK:
|
||||
|
@ -90,7 +91,7 @@ proc persistBlocksImpl(c: Chain; headers: openarray[BlockHeader];
|
|||
header,
|
||||
body,
|
||||
checkSealOK = false, # TODO: how to checkseal from here
|
||||
ttdReached = c.isBlockAfterTtd(header.blockNumber),
|
||||
ttdReached = isBlockAfterTtd,
|
||||
pow = c.pow)
|
||||
if res.isErr:
|
||||
debug "block validation error",
|
||||
|
|
|
@ -34,7 +34,7 @@ export
|
|||
{.push raises: [Defect].}
|
||||
|
||||
const
|
||||
daoForkBlockExtraData =
|
||||
daoForkBlockExtraData* =
|
||||
byteutils.hexToByteArray[13](DAOForkBlockExtra).toSeq
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -40,7 +40,7 @@ proc calcRootHashRlp*(items: openArray[seq[byte]]): Hash256 =
|
|||
tr.put(rlp.encode(i), t)
|
||||
return tr.rootHash()
|
||||
|
||||
proc toBlockHeader(payload: ExecutionPayload): eth_types.BlockHeader =
|
||||
proc toBlockHeader(payload: ExecutionPayloadV1): eth_types.BlockHeader =
|
||||
discard payload.random # TODO: What should this be used for?
|
||||
|
||||
let transactions = seq[seq[byte]](payload.transactions)
|
||||
|
@ -49,10 +49,10 @@ proc toBlockHeader(payload: ExecutionPayload): eth_types.BlockHeader =
|
|||
EthBlockHeader(
|
||||
parentHash : payload.parentHash.asEthHash,
|
||||
ommersHash : EMPTY_UNCLE_HASH,
|
||||
coinbase : EthAddress payload.coinbase,
|
||||
coinbase : EthAddress payload.feeRecipient,
|
||||
stateRoot : payload.stateRoot.asEthHash,
|
||||
txRoot : txRoot,
|
||||
receiptRoot : payload.receiptRoot.asEthHash,
|
||||
receiptRoot : payload.receiptsRoot.asEthHash,
|
||||
bloom : distinctBase(payload.logsBloom),
|
||||
difficulty : default(DifficultyInt),
|
||||
blockNumber : payload.blockNumber.distinctBase.u256,
|
||||
|
@ -65,40 +65,53 @@ proc toBlockHeader(payload: ExecutionPayload): eth_types.BlockHeader =
|
|||
fee : some payload.baseFeePerGas
|
||||
)
|
||||
|
||||
proc toBlockBody(payload: ExecutionPayload): BlockBody =
|
||||
proc toBlockBody(payload: ExecutionPayloadV1): BlockBody =
|
||||
# TODO the transactions from the payload have to be converted here
|
||||
discard payload.transactions
|
||||
|
||||
proc setupEngineAPI*(sealingEngine: SealingEngineRef, server: RpcServer) =
|
||||
proc setupEngineAPI*(
|
||||
sealingEngine: SealingEngineRef,
|
||||
server: RpcServer) =
|
||||
|
||||
var payloadsInstance = newClone(newSeq[ExecutionPayload]())
|
||||
var payloadsInstance = newClone(newSeq[ExecutionPayloadV1]())
|
||||
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")
|
||||
# https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.5/src/engine/specification.md#engine_getpayloadv1
|
||||
server.rpc("engine_getPayloadV1") do(payloadIdBytes: FixedBytes[8]) -> ExecutionPayloadV1:
|
||||
let payloadId = uint64.fromBytesBE(distinctBase payloadIdBytes)
|
||||
if payloadId > payloads.len.uint64:
|
||||
raise (ref InvalidRequest)(code: engineApiUnknownPayload, msg: "Unknown payload")
|
||||
return payloads[int payloadId]
|
||||
|
||||
server.rpc("engine_executePayload") do(payload: ExecutionPayload) -> ExecutePayloadResponse:
|
||||
# https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.5/src/engine/specification.md#engine_executepayloadv1
|
||||
server.rpc("engine_executePayloadV1") do(payload: ExecutionPayloadV1) -> 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)
|
||||
return ExecutePayloadResponse(status: PayloadExecutionStatus.syncing)
|
||||
|
||||
# TODO check whether we are syncing
|
||||
|
||||
let
|
||||
headers = [payload.toBlockHeader]
|
||||
bodies = [payload.toBlockBody]
|
||||
|
||||
if rlpHash(headers[0]) != payload.blockHash.asEthHash:
|
||||
return ExecutePayloadResponse(status: $PayloadExecutionStatus.invalid)
|
||||
return ExecutePayloadResponse(status: PayloadExecutionStatus.invalid,
|
||||
validationError: some "payload root doesn't match its contents")
|
||||
|
||||
if sealingEngine.chain.persistBlocks(headers, bodies) != ValidationResult.OK:
|
||||
return ExecutePayloadResponse(status: $PayloadExecutionStatus.invalid)
|
||||
# TODO Provide validationError and latestValidHash
|
||||
return ExecutePayloadResponse(status: PayloadExecutionStatus.invalid)
|
||||
|
||||
return ExecutePayloadResponse(status: $PayloadExecutionStatus.valid)
|
||||
return ExecutePayloadResponse(status: PayloadExecutionStatus.valid,
|
||||
latestValidHash: some payload.blockHash)
|
||||
|
||||
server.rpc("engine_forkchoiceUpdated") do(update: ForkChoiceUpdate):
|
||||
# https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.5/src/engine/specification.md#engine_forkchoiceupdatedv1
|
||||
server.rpc("engine_forkchoiceUpdatedV1") do(
|
||||
update: ForkchoiceStateV1,
|
||||
payloadAttributes: Option[PayloadAttributesV1]) -> ForkchoiceUpdatedResponse:
|
||||
let
|
||||
db = sealingEngine.chain.db
|
||||
newHead = update.headBlockHash.asEthHash
|
||||
|
@ -107,5 +120,24 @@ proc setupEngineAPI*(sealingEngine: SealingEngineRef, server: RpcServer) =
|
|||
# histories that are no longer relevant
|
||||
discard update.finalizedBlockHash
|
||||
|
||||
# TODO Check whether we are syncing
|
||||
|
||||
if not db.setHead(newHead):
|
||||
raise (ref InvalidRequest)(code: UNKNOWN_HEADER, msg: "Uknown head block hash")
|
||||
return ForkchoiceUpdatedResponse(status: ForkchoiceUpdatedStatus.syncing)
|
||||
|
||||
if payloadAttributes.isSome:
|
||||
let payloadId = uint64 payloads.len
|
||||
|
||||
var payload: ExecutionPayloadV1
|
||||
let generatePayloadRes = sealingEngine.generateExecutionPayload(
|
||||
payloadAttributes.get,
|
||||
payload)
|
||||
if generatePayloadRes.isErr:
|
||||
raise newException(CatchableError, generatePayloadRes.error)
|
||||
|
||||
payloads.add payload
|
||||
|
||||
return ForkchoiceUpdatedResponse(status: ForkchoiceUpdatedStatus.success,
|
||||
payloadId: some payloadId.toBytesBE.PayloadID)
|
||||
else:
|
||||
return ForkchoiceUpdatedResponse(status: ForkchoiceUpdatedStatus.success)
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
]#
|
||||
|
||||
import
|
||||
stint, stew/byteutils, eth/[keys, rlp], eth/common/eth_types
|
||||
stint, stew/byteutils, eth/[keys, rlp],
|
||||
eth/common/eth_types,
|
||||
json_serialization
|
||||
|
||||
type
|
||||
HexQuantityStr* = distinct string
|
||||
|
@ -300,3 +302,15 @@ proc fromJson*(n: JsonNode, argName: string, result: var Hash256) =
|
|||
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var JsonNode) =
|
||||
result = n
|
||||
|
||||
proc writeValue*(writer: var JsonWriter, value: HexQuantityStr) =
|
||||
writeValue(writer, string value)
|
||||
|
||||
proc writeValue*(writer: var JsonWriter, value: HexDataStr) =
|
||||
writeValue(writer, string value)
|
||||
|
||||
proc writeValue*(writer: var JsonWriter, value: EthAddressStr) =
|
||||
writeValue(writer, string value)
|
||||
|
||||
proc writeValue*(writer: var JsonWriter, value: EthHashStr) =
|
||||
writeValue(writer, string value)
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
# those terms.
|
||||
|
||||
import
|
||||
times, options, tables,
|
||||
times, tables,
|
||||
json_rpc/rpcserver, hexstrings, stint, stew/byteutils,
|
||||
json_serialization, web3/conversions, json_serialization/std/options,
|
||||
eth/common/eth_types_json_serialization,
|
||||
eth/[common, keys, rlp, p2p], nimcrypto,
|
||||
".."/[transaction, vm_state, constants, utils, context],
|
||||
../db/[db_chain, state_db],
|
||||
|
@ -297,7 +299,9 @@ proc setupEthRpc*(node: EthereumNode, ctx: EthContext, chain: BaseChainDB, txPoo
|
|||
hash = data.toHash
|
||||
|
||||
if chain.getBlockHeader(hash, header):
|
||||
result = some(populateBlockObject(header, chain, fullTransactions))
|
||||
result = some populateBlockObject(header, chain, fullTransactions)
|
||||
else:
|
||||
result = none BlockObject
|
||||
|
||||
server.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> Option[BlockObject]:
|
||||
## Returns information about a block by block number.
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import
|
||||
hexstrings, options, eth/[common, keys, rlp], json
|
||||
|
||||
from
|
||||
web3/ethtypes import FixedBytes
|
||||
|
||||
export FixedBytes
|
||||
|
||||
#[
|
||||
Notes:
|
||||
* Some of the types suppose 'null' when there is no appropriate value.
|
||||
|
@ -49,7 +54,7 @@ type
|
|||
parentHash*: Hash256 # hash of the parent block.
|
||||
nonce*: Option[HexDataStr] # hash of the generated proof-of-work. null when its pending block.
|
||||
sha3Uncles*: Hash256 # SHA3 of the uncles data in the block.
|
||||
logsBloom*: Option[BloomFilter] # the bloom filter for the logs of the block. null when its pending block.
|
||||
logsBloom*: FixedBytes[256] # the bloom filter for the logs of the block. null when its pending block.
|
||||
transactionsRoot*: Hash256 # the root of the transaction trie of the block.
|
||||
stateRoot*: Hash256 # the root of the final state trie of the block.
|
||||
receiptsRoot*: Hash256 # the root of the receipts trie of the block.
|
||||
|
@ -107,7 +112,7 @@ type
|
|||
gasUsed*: HexQuantityStr # the amount of gas used by this specific transaction alone.
|
||||
contractAddress*: Option[EthAddress] # the contract address created, if the transaction was a contract creation, otherwise null.
|
||||
logs*: seq[Log] # list of log objects which this transaction generated.
|
||||
logsBloom*: BloomFilter # bloom filter for light clients to quickly retrieve related logs.
|
||||
logsBloom*: FixedBytes[256] # bloom filter for light clients to quickly retrieve related logs.
|
||||
root*: Option[Hash256] # post-transaction stateroot (pre Byzantium).
|
||||
status*: Option[int] # 1 = success, 0 = failure.
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ proc populateBlockObject*(header: BlockHeader, chain: BaseChainDB, fullTx: bool,
|
|||
result.parentHash = header.parentHash
|
||||
result.nonce = some(hexDataStr(header.nonce))
|
||||
result.sha3Uncles = header.ommersHash
|
||||
result.logsBloom = some(header.bloom)
|
||||
result.logsBloom = FixedBytes[256] header.bloom
|
||||
result.transactionsRoot = header.txRoot
|
||||
result.stateRoot = header.stateRoot
|
||||
result.receiptsRoot = header.receiptRoot
|
||||
|
@ -183,7 +183,7 @@ proc populateReceipt*(receipt: Receipt, gasUsed: GasInt, tx: Transaction, txInde
|
|||
result.contractAddress = some(contractAddress)
|
||||
|
||||
result.logs = receipt.logs
|
||||
result.logsBloom = receipt.bloom
|
||||
result.logsBloom = FixedBytes[256] receipt.bloom
|
||||
|
||||
# post-transaction stateroot (pre Byzantium).
|
||||
if receipt.hasStateRoot:
|
||||
|
|
|
@ -16,12 +16,12 @@ import
|
|||
clique_desc,
|
||||
clique_cfg,
|
||||
clique_sealer],
|
||||
./p2p/gaslimit,
|
||||
./p2p/[gaslimit, validate],
|
||||
"."/[chain_config, utils, context],
|
||||
"."/utils/tx_pool
|
||||
|
||||
from web3/ethtypes as web3types import nil
|
||||
from web3/engine_api_types import ExecutionPayload, PayloadAttributes
|
||||
from web3/engine_api_types import ExecutionPayloadV1, PayloadAttributesV1
|
||||
|
||||
type
|
||||
EngineState* = enum
|
||||
|
@ -85,8 +85,7 @@ proc prepareHeader(engine: SealingEngineRef,
|
|||
parent.gasLimit,
|
||||
gasFloor = DEFAULT_GAS_LIMIT,
|
||||
gasCeil = DEFAULT_GAS_LIMIT),
|
||||
# TODO: extraData can be configured via cli
|
||||
#extraData : engine.extra,
|
||||
extraData : daoForkBlockExtraData,
|
||||
coinbase : coinbase,
|
||||
timestamp : timestamp,
|
||||
ommersHash : EMPTY_UNCLE_HASH,
|
||||
|
@ -106,30 +105,29 @@ proc prepareHeader(engine: SealingEngineRef,
|
|||
# TODO: desiredLimit can be configured by user, gasCeil
|
||||
header.gasLimit = calcGasLimit1559(parentGasLimit, desiredLimit = DEFAULT_GAS_LIMIT)
|
||||
|
||||
# TODO Post merge, clique should not be executing
|
||||
let clique = engine.chain.clique
|
||||
let res = clique.prepare(parent, header)
|
||||
if res.isErr:
|
||||
return err($res.error)
|
||||
|
||||
if engine.chain.isBlockAfterTtd(header.blockNumber):
|
||||
if engine.chain.isBlockAfterTtd(header):
|
||||
header.difficulty = DifficultyInt.zero
|
||||
header.mixDigest = default(Hash256)
|
||||
header.nonce = default(BlockNonce)
|
||||
else:
|
||||
let res = engine.chain.clique.prepare(parent, header)
|
||||
if res.isErr:
|
||||
return err($res.error)
|
||||
|
||||
ok(header)
|
||||
|
||||
proc generateBlock(engine: SealingEngineRef,
|
||||
coinbase: EthAddress,
|
||||
parentBlockHeader: BlockHeader,
|
||||
outBlock: var EthBlock): Result[void, string] =
|
||||
outBlock: var EthBlock,
|
||||
timestamp = getTime()): Result[void, string] =
|
||||
# deviation from standard block generator
|
||||
# - no local and remote transactions inclusion(need tx pool)
|
||||
# - no receipts from tx
|
||||
# - no DAO hard fork
|
||||
# - no local and remote uncles inclusion
|
||||
|
||||
let clique = engine.chain.clique
|
||||
let time = getTime()
|
||||
let res = prepareHeader(engine, coinbase, parentBlockHeader, time)
|
||||
let res = prepareHeader(engine, coinbase, parentBlockHeader, timestamp)
|
||||
if res.isErr:
|
||||
return err("error prepare header")
|
||||
|
||||
|
@ -137,10 +135,11 @@ proc generateBlock(engine: SealingEngineRef,
|
|||
header: res.get()
|
||||
)
|
||||
|
||||
# TODO Post merge, Clique should not be executing
|
||||
let sealRes = clique.seal(outBlock)
|
||||
if sealRes.isErr:
|
||||
return err("error sealing block header: " & $sealRes.error)
|
||||
if not engine.chain.isBlockAfterTtd(outBlock.header):
|
||||
# TODO Post merge, Clique should not be executing
|
||||
let sealRes = engine.chain.clique.seal(outBlock)
|
||||
if sealRes.isErr:
|
||||
return err("error sealing block header: " & $sealRes.error)
|
||||
|
||||
debug "generated block",
|
||||
blockNumber = outBlock.header.blockNumber,
|
||||
|
@ -151,10 +150,11 @@ proc generateBlock(engine: SealingEngineRef,
|
|||
proc generateBlock(engine: SealingEngineRef,
|
||||
coinbase: EthAddress,
|
||||
parentHash: Hash256,
|
||||
outBlock: var EthBlock): Result[void, string] =
|
||||
outBlock: var EthBlock,
|
||||
timestamp = getTime()): Result[void, string] =
|
||||
var parentBlockHeader: BlockHeader
|
||||
if engine.chain.db.getBlockHeader(parentHash, parentBlockHeader):
|
||||
generateBlock(engine, coinbase, parentBlockHeader, outBlock)
|
||||
generateBlock(engine, coinbase, parentBlockHeader, outBlock, timestamp)
|
||||
else:
|
||||
# TODO:
|
||||
# This hack shouldn't be necessary if the database can find
|
||||
|
@ -167,8 +167,10 @@ proc generateBlock(engine: SealingEngineRef,
|
|||
|
||||
proc generateBlock(engine: SealingEngineRef,
|
||||
coinbase: EthAddress,
|
||||
outBlock: var EthBlock): Result[void, string] =
|
||||
generateBlock(engine, coinbase, engine.chain.currentBlock(), outBlock)
|
||||
outBlock: var EthBlock,
|
||||
timestamp = getTime()): Result[void, string] =
|
||||
generateBlock(engine, coinbase, engine.chain.currentBlock(),
|
||||
outBlock, timestamp)
|
||||
|
||||
proc sealingLoop(engine: SealingEngineRef): Future[void] {.async.} =
|
||||
let clique = engine.chain.clique
|
||||
|
@ -217,13 +219,22 @@ proc sealingLoop(engine: SealingEngineRef): Future[void] {.async.} =
|
|||
|
||||
info "block generated", number=blk.header.blockNumber
|
||||
|
||||
template unsafeQuantityToInt64(q: web3types.Quantity): int64 =
|
||||
int64 q
|
||||
|
||||
proc generateExecutionPayload*(engine: SealingEngineRef,
|
||||
payloadAttrs: PayloadAttributes,
|
||||
payloadRes: var ExecutionPayload): Result[void, string] =
|
||||
payloadAttrs: PayloadAttributesV1,
|
||||
payloadRes: var ExecutionPayloadV1): Result[void, string] =
|
||||
let headBlock = try: engine.chain.db.getCanonicalHead()
|
||||
except CatchableError: return err "No head block in database"
|
||||
|
||||
var blk: EthBlock
|
||||
let blkRes = engine.generateBlock(EthAddress payloadAttrs.feeRecipient,
|
||||
payloadAttrs.parentHash.asEthHash,
|
||||
blk)
|
||||
let blkRes = engine.generateBlock(
|
||||
EthAddress payloadAttrs.suggestedFeeRecipient,
|
||||
headBlock,
|
||||
blk,
|
||||
fromUnix(payloadAttrs.timestamp.unsafeQuantityToInt64))
|
||||
|
||||
if blkRes.isErr:
|
||||
error "sealing engine generateBlock error", msg = blkRes.error
|
||||
return blkRes
|
||||
|
@ -233,10 +244,12 @@ proc generateExecutionPayload*(engine: SealingEngineRef,
|
|||
])
|
||||
|
||||
payloadRes.parentHash = Web3BlockHash blk.header.parentHash.data
|
||||
payloadRes.coinbase = Web3Address blk.header.coinbase
|
||||
payloadRes.feeRecipient = Web3Address blk.header.coinbase
|
||||
payloadRes.stateRoot = Web3BlockHash blk.header.stateRoot.data
|
||||
payloadRes.receiptRoot = Web3BlockHash blk.header.receiptRoot.data
|
||||
payloadRes.receiptsRoot = Web3BlockHash blk.header.receiptRoot.data
|
||||
payloadRes.logsBloom = Web3Bloom blk.header.bloom
|
||||
# TODO Check the extra data length here
|
||||
# payloadres.extraData = web3types.DynamicBytes[256] blk.header.extraData
|
||||
payloadRes.random = web3types.FixedBytes[32](payloadAttrs.random)
|
||||
payloadRes.blockNumber = Web3Quantity blk.header.blockNumber.truncate(uint64)
|
||||
payloadRes.gasLimit = Web3Quantity blk.header.gasLimit
|
||||
|
|
|
@ -13,20 +13,20 @@ set -Eeuo pipefail
|
|||
# eth/catalyst: fix random in payload, payloadid as hexutil
|
||||
|
||||
# Get the payload
|
||||
resp_get_payload=$(curl -sX POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_getPayload","params":["0x0"],"id":67}' http://localhost:8550)
|
||||
echo "engine_getPayload response: ${resp_get_payload}"
|
||||
resp_get_payload=$(curl -sX POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_getPayloadV1","params":["0x0"],"id":67}' http://localhost:8550)
|
||||
echo "engine_getPayloadV1 response: ${resp_get_payload}"
|
||||
|
||||
expected_resp_get_payload='{"jsonrpc":"2.0","id":67,"result":{"blockHash":"0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174","parentHash":"0xa0513a503d5bd6e89a144c3268e5b7e9da9dbf63df125a360e3950a7d0d67131","coinbase":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b","stateRoot":"0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45","receiptRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","random":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","gasLimit":"0x989680","gasUsed":"0x0","timestamp":"0x5","extraData":"0x","baseFeePerGas":"0x0","transactions":[]}}'
|
||||
empirical_resp_get_payload='{"jsonrpc":"2.0","id":67,"result":{"blockHash":"0x7a694c5e6e372e6f865b073c101c2fba01f899f16480eb13f7e333a3b7e015bc","parentHash":"0xa0513a503d5bd6e89a144c3268e5b7e9da9dbf63df125a360e3950a7d0d67131","coinbase":"0x0000000000000000000000000000000000000000","stateRoot":"0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45","receiptRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","random":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","gasLimit":"0x989680","gasUsed":"0x0","timestamp":"0x5","extraData":"0x","baseFeePerGas":"0x0","transactions":[]}}'
|
||||
[[ ${resp_get_payload} == ${expected_resp_get_payload} ]] || [[ ${resp_get_payload} == ${empirical_resp_get_payload} ]] || (echo "Unexpected response to engine_getPayload"; false)
|
||||
[[ ${resp_get_payload} == ${expected_resp_get_payload} ]] || [[ ${resp_get_payload} == ${empirical_resp_get_payload} ]] || (echo "Unexpected response to engine_getPayloadV1"; false)
|
||||
|
||||
# Execute the payload
|
||||
# Needed two tweaks vs upstream note: (a) add blockNumber field and (b) switch receiptRoots to receiptRoot
|
||||
resp_execute_payload=$(curl -sX POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_executePayload","params":[{"blockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858","parentHash":"0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a","coinbase":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b","stateRoot":"0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45","receiptRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","random":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x5","extraData":"0x","baseFeePerGas":"0x7","transactions":[]}],"id":67}' http://localhost:8550)
|
||||
[[ ${resp_execute_payload} == '{"jsonrpc":"2.0","id":67,"result":{"status":"VALID"}}' ]] || (echo "Unexpected response to engine_executePayload"; false)
|
||||
resp_execute_payload=$(curl -sX POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_executePayloadV1","params":[{"blockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858","parentHash":"0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a","coinbase":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b","stateRoot":"0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45","receiptRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","random":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x5","extraData":"0x","baseFeePerGas":"0x7","transactions":[]}],"id":67}' http://localhost:8550)
|
||||
[[ ${resp_execute_payload} == '{"jsonrpc":"2.0","id":67,"result":{"status":"VALID"}}' ]] || (echo "Unexpected response to engine_executePayloadV1"; false)
|
||||
|
||||
# Update the fork choice
|
||||
resp_fork_choice_updated=$(curl -sX POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_forkchoiceUpdated","params":[{"headBlockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", "finalizedBlockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858"}],"id":67}' http://localhost:8550)
|
||||
[[ ${resp_consensus_validated} == '{"jsonrpc":"2.0","id":67,"result":null}' ]] || (echo "Unexpected response to engine_forkChoiceUpdated"; false)
|
||||
resp_fork_choice_updated=$(curl -sX POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_forkchoiceUpdatedV1","params":[{"headBlockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", "finalizedBlockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858"}],"id":67}' http://localhost:8550)
|
||||
[[ ${resp_consensus_validated} == '{"jsonrpc":"2.0","id":67,"result":null}' ]] || (echo "Unexpected response to engine_forkchoiceUpdatedV1"; false)
|
||||
|
||||
echo "Execution test vectors for Merge passed"
|
||||
|
|
|
@ -20,6 +20,7 @@ $SCRIPT_DIR/../../build/nimbus \
|
|||
--custom-network:"$SCRIPT_DIR/amphora-interop-genesis-m1.json" \
|
||||
--network:0 \
|
||||
--engine-api \
|
||||
--engine-api-port:8545 \
|
||||
--rpc \
|
||||
--nat:none --discovery:none \
|
||||
--import-key:"$SCRIPT_DIR/signer-key.txt" \
|
||||
|
|
|
@ -9,7 +9,7 @@ import
|
|||
asynctest, json, strformat, strutils, options, tables, os,
|
||||
nimcrypto, stew/byteutils, times,
|
||||
json_rpc/[rpcserver, rpcclient], eth/common as eth_common,
|
||||
eth/[rlp, keys, trie/db, p2p/private/p2p_types],
|
||||
eth/[rlp, keys, trie/db, p2p/private/p2p_types], web3/conversions,
|
||||
../nimbus/rpc/[common, p2p, hexstrings, rpc_types, rpc_utils],
|
||||
../nimbus/[constants, config, genesis, utils, transaction,
|
||||
vm_state, vm_types],
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 16ae908e49dad3dd1be1d536798a2b0ae39250df
|
||||
Subproject commit 7daafe7b9cdfc3743c4b924eca6621dc928cf138
|
Loading…
Reference in New Issue