Migrate to Engine API spec version v1.0.0-alpha.5; More progress towards working M1

This commit is contained in:
Zahary Karadjov 2022-01-24 15:08:33 +02:00
parent 990846135a
commit 3cbb920406
No known key found for this signature in database
GPG Key ID: C8936F8A3073D609
16 changed files with 189 additions and 75 deletions

View File

@ -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

View File

@ -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: ""

View File

@ -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:
if conf.engineApiPort != conf.rpcPort:
nimbus.engineApiServer = newRpcHttpServer([
initTAddress(conf.engineApiAddress, conf.engineApiPort)
])
setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiServer)
nimbus.engineAPiServer.start()
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:

View File

@ -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] = [

View File

@ -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",

View File

@ -34,7 +34,7 @@ export
{.push raises: [Defect].}
const
daoForkBlockExtraData =
daoForkBlockExtraData* =
byteutils.hexToByteArray[13](DAOForkBlockExtra).toSeq
# ------------------------------------------------------------------------------

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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:

View File

@ -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 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)
if engine.chain.isBlockAfterTtd(header.blockNumber):
header.difficulty = DifficultyInt.zero
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,8 +135,9 @@ proc generateBlock(engine: SealingEngineRef,
header: res.get()
)
if not engine.chain.isBlockAfterTtd(outBlock.header):
# TODO Post merge, Clique should not be executing
let sealRes = clique.seal(outBlock)
let sealRes = engine.chain.clique.seal(outBlock)
if sealRes.isErr:
return err("error sealing block header: " & $sealRes.error)
@ -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

View File

@ -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"

View File

@ -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" \

View File

@ -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],

2
vendor/nim-web3 vendored

@ -1 +1 @@
Subproject commit 16ae908e49dad3dd1be1d536798a2b0ae39250df
Subproject commit 7daafe7b9cdfc3743c4b924eca6621dc928cf138