2023-01-31 12:38:08 +00:00
|
|
|
# Nimbus
|
|
|
|
# Copyright (c) 2022-2023 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.
|
|
|
|
|
2022-02-22 08:55:04 +00:00
|
|
|
import
|
2023-03-09 23:40:55 +00:00
|
|
|
std/[typetraits, times, strutils, sequtils],
|
2022-02-22 08:55:04 +00:00
|
|
|
nimcrypto/[hash, sha2],
|
|
|
|
web3/engine_api_types,
|
2022-07-04 05:34:10 +00:00
|
|
|
json_rpc/errors,
|
2023-03-09 23:40:55 +00:00
|
|
|
eth/[trie, rlp, common, common/eth_types, trie/db],
|
2023-01-31 12:38:08 +00:00
|
|
|
stew/[results, byteutils],
|
2022-12-02 04:35:41 +00:00
|
|
|
../../constants,
|
2022-02-22 08:55:04 +00:00
|
|
|
./mergetypes
|
|
|
|
|
2023-03-09 23:40:55 +00:00
|
|
|
type Hash256 = eth_types.Hash256
|
|
|
|
|
|
|
|
proc computePayloadId*(headBlockHash: Hash256, params: PayloadAttributesV1 | PayloadAttributesV2): PayloadID =
|
2022-02-22 08:55:04 +00:00
|
|
|
var dest: Hash256
|
|
|
|
var ctx: sha256
|
|
|
|
ctx.init()
|
|
|
|
ctx.update(headBlockHash.data)
|
|
|
|
ctx.update(toBytesBE distinctBase params.timestamp)
|
|
|
|
ctx.update(distinctBase params.prevRandao)
|
|
|
|
ctx.update(distinctBase params.suggestedFeeRecipient)
|
2023-03-09 23:40:55 +00:00
|
|
|
# FIXME-Adam: Do we need to include the withdrawals in this calculation?
|
|
|
|
# https://github.com/ethereum/go-ethereum/pull/25838#discussion_r1024340383
|
|
|
|
# "The execution api specs define that this ID can be completely random. It
|
|
|
|
# used to be derived from payload attributes in the past, but maybe it's
|
|
|
|
# time to use a randomized ID to not break it with any changes to the
|
|
|
|
# attributes?"
|
2022-02-22 08:55:04 +00:00
|
|
|
ctx.finish dest.data
|
|
|
|
ctx.clear()
|
|
|
|
(distinctBase result)[0..7] = dest.data[0..7]
|
|
|
|
|
2023-03-09 23:40:55 +00:00
|
|
|
proc append*(w: var RlpWriter, q: Quantity) =
|
|
|
|
w.append(uint64(q))
|
|
|
|
|
|
|
|
proc append*(w: var RlpWriter, a: Address) =
|
|
|
|
w.append(distinctBase(a))
|
|
|
|
|
2022-02-22 08:55:04 +00:00
|
|
|
template unsafeQuantityToInt64(q: Quantity): int64 =
|
|
|
|
int64 q
|
|
|
|
|
|
|
|
template asEthHash*(hash: engine_api_types.BlockHash): Hash256 =
|
|
|
|
Hash256(data: distinctBase(hash))
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
2023-03-09 23:40:55 +00:00
|
|
|
proc calcWithdrawalsRoot(withdrawals: seq[WithdrawalV1]): Hash256 =
|
|
|
|
calcRootHashRlp(withdrawals.map(writer.encode))
|
|
|
|
|
|
|
|
func maybeWithdrawals*(payload: ExecutionPayloadV1 | ExecutionPayloadV2): Option[seq[WithdrawalV1]] =
|
|
|
|
when payload is ExecutionPayloadV1:
|
|
|
|
none[seq[WithdrawalV1]]()
|
|
|
|
else:
|
|
|
|
some(payload.withdrawals)
|
|
|
|
|
|
|
|
proc toBlockHeader*(payload: ExecutionPayloadV1 | ExecutionPayloadV2): EthBlockHeader =
|
2022-02-22 08:55:04 +00:00
|
|
|
let transactions = seq[seq[byte]](payload.transactions)
|
|
|
|
let txRoot = calcRootHashRlp(transactions)
|
2023-03-09 23:40:55 +00:00
|
|
|
|
2022-02-22 08:55:04 +00:00
|
|
|
EthBlockHeader(
|
2023-03-09 23:40:55 +00:00
|
|
|
parentHash : payload.parentHash.asEthHash,
|
|
|
|
ommersHash : EMPTY_UNCLE_HASH,
|
|
|
|
coinbase : EthAddress payload.feeRecipient,
|
|
|
|
stateRoot : payload.stateRoot.asEthHash,
|
|
|
|
txRoot : txRoot,
|
|
|
|
receiptRoot : payload.receiptsRoot.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 : bytes payload.extraData,
|
|
|
|
mixDigest : payload.prevRandao.asEthHash, # EIP-4399 redefine `mixDigest` -> `prevRandao`
|
|
|
|
nonce : default(BlockNonce),
|
|
|
|
fee : some payload.baseFeePerGas,
|
|
|
|
withdrawalsRoot: payload.maybeWithdrawals.map(calcWithdrawalsRoot) # EIP-4895
|
2022-02-22 08:55:04 +00:00
|
|
|
)
|
|
|
|
|
2023-03-09 23:40:55 +00:00
|
|
|
proc toWithdrawal*(w: WithdrawalV1): Withdrawal =
|
|
|
|
Withdrawal(
|
|
|
|
index: uint64(w.index),
|
|
|
|
validatorIndex: uint64(w.validatorIndex),
|
|
|
|
address: distinctBase(w.address),
|
2023-04-24 20:59:38 +00:00
|
|
|
amount: uint64(w.amount) # AARDVARK: is this wei or gwei or what?
|
2023-03-09 23:40:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
proc toWithdrawalV1*(w: Withdrawal): WithdrawalV1 =
|
|
|
|
WithdrawalV1(
|
|
|
|
index: Quantity(w.index),
|
|
|
|
validatorIndex: Quantity(w.validatorIndex),
|
|
|
|
address: Address(w.address),
|
2023-04-24 20:59:38 +00:00
|
|
|
amount: Quantity(w.amount) # AARDVARK: is this wei or gwei or what?
|
2023-03-09 23:40:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
proc toTypedTransaction*(tx: Transaction): TypedTransaction =
|
|
|
|
TypedTransaction(rlp.encode(tx))
|
|
|
|
|
|
|
|
proc toBlockBody*(payload: ExecutionPayloadV1 | ExecutionPayloadV2): BlockBody =
|
2022-07-04 05:34:10 +00:00
|
|
|
result.transactions.setLen(payload.transactions.len)
|
|
|
|
for i, tx in payload.transactions:
|
|
|
|
result.transactions[i] = rlp.decode(distinctBase tx, Transaction)
|
2023-03-09 23:40:55 +00:00
|
|
|
when payload is ExecutionPayloadV2:
|
|
|
|
let ws = payload.maybeWithdrawals
|
|
|
|
result.withdrawals =
|
|
|
|
if ws.isSome:
|
|
|
|
some(ws.get.map(toWithdrawal))
|
|
|
|
else:
|
|
|
|
none[seq[Withdrawal]]()
|
2022-07-04 05:34:10 +00:00
|
|
|
|
|
|
|
proc `$`*(x: BlockHash): string =
|
|
|
|
toHex(x)
|
|
|
|
|
|
|
|
template toValidHash*(x: Hash256): Option[BlockHash] =
|
2022-02-22 08:55:04 +00:00
|
|
|
some(BlockHash(x.data))
|
|
|
|
|
2022-07-04 05:34:10 +00:00
|
|
|
proc validateBlockHash*(header: EthBlockHeader, gotHash: Hash256): Result[void, PayloadStatusV1] =
|
2022-02-22 08:55:04 +00:00
|
|
|
let wantHash = header.blockHash
|
|
|
|
if wantHash != gotHash:
|
2022-07-04 05:34:10 +00:00
|
|
|
let status = PayloadStatusV1(
|
2023-03-09 23:40:55 +00:00
|
|
|
# This used to say invalid_block_hash, but see here:
|
|
|
|
# https://github.com/ethereum/execution-apis/blob/main/src/engine/shanghai.md#engine_newpayloadv2
|
|
|
|
# "INVALID_BLOCK_HASH status value is supplanted by INVALID."
|
|
|
|
status: PayloadExecutionStatus.invalid,
|
2022-07-04 05:34:10 +00:00
|
|
|
validationError: some("blockhash mismatch, want $1, got $2" % [$wantHash, $gotHash])
|
|
|
|
)
|
|
|
|
return err(status)
|
2022-02-22 08:55:04 +00:00
|
|
|
|
|
|
|
return ok()
|
|
|
|
|
|
|
|
proc simpleFCU*(status: PayloadExecutionStatus): ForkchoiceUpdatedResponse =
|
|
|
|
ForkchoiceUpdatedResponse(payloadStatus: PayloadStatusV1(status: status))
|
|
|
|
|
|
|
|
proc simpleFCU*(status: PayloadExecutionStatus, msg: string): ForkchoiceUpdatedResponse =
|
2022-07-04 05:34:10 +00:00
|
|
|
ForkchoiceUpdatedResponse(
|
|
|
|
payloadStatus: PayloadStatusV1(
|
|
|
|
status: status,
|
|
|
|
validationError: some(msg)
|
|
|
|
)
|
|
|
|
)
|
2022-02-22 08:55:04 +00:00
|
|
|
|
2022-06-17 00:53:33 +00:00
|
|
|
proc invalidFCU*(hash: Hash256 = Hash256()): ForkchoiceUpdatedResponse =
|
|
|
|
ForkchoiceUpdatedResponse(payloadStatus:
|
|
|
|
PayloadStatusV1(
|
|
|
|
status: PayloadExecutionStatus.invalid,
|
2022-07-04 05:34:10 +00:00
|
|
|
latestValidHash: toValidHash(hash)
|
2022-06-17 00:53:33 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
proc validFCU*(id: Option[PayloadID], validHash: Hash256): ForkchoiceUpdatedResponse =
|
2022-02-22 08:55:04 +00:00
|
|
|
ForkchoiceUpdatedResponse(
|
|
|
|
payloadStatus: PayloadStatusV1(
|
|
|
|
status: PayloadExecutionStatus.valid,
|
2022-07-04 05:34:10 +00:00
|
|
|
latestValidHash: toValidHash(validHash)
|
2022-02-22 08:55:04 +00:00
|
|
|
),
|
|
|
|
payloadId: id
|
|
|
|
)
|
|
|
|
|
|
|
|
proc invalidStatus*(validHash: Hash256, msg: string): PayloadStatusV1 =
|
|
|
|
PayloadStatusV1(
|
|
|
|
status: PayloadExecutionStatus.invalid,
|
2022-07-04 05:34:10 +00:00
|
|
|
latestValidHash: toValidHash(validHash),
|
2022-02-22 08:55:04 +00:00
|
|
|
validationError: some(msg)
|
|
|
|
)
|
|
|
|
|
2022-06-17 00:53:33 +00:00
|
|
|
proc invalidStatus*(validHash: Hash256 = Hash256()): PayloadStatusV1 =
|
|
|
|
PayloadStatusV1(
|
|
|
|
status: PayloadExecutionStatus.invalid,
|
2022-07-04 05:34:10 +00:00
|
|
|
latestValidHash: toValidHash(validHash)
|
2022-06-17 00:53:33 +00:00
|
|
|
)
|
|
|
|
|
2022-06-30 07:34:55 +00:00
|
|
|
proc acceptedStatus*(validHash: Hash256): PayloadStatusV1 =
|
|
|
|
PayloadStatusV1(
|
|
|
|
status: PayloadExecutionStatus.accepted,
|
2022-07-04 05:34:10 +00:00
|
|
|
latestValidHash: toValidHash(validHash)
|
2022-06-30 07:34:55 +00:00
|
|
|
)
|
|
|
|
|
2022-07-04 05:34:10 +00:00
|
|
|
proc acceptedStatus*(): PayloadStatusV1 =
|
|
|
|
PayloadStatusV1(
|
|
|
|
status: PayloadExecutionStatus.accepted
|
|
|
|
)
|
|
|
|
|
|
|
|
proc validStatus*(validHash: Hash256): PayloadStatusV1 =
|
|
|
|
PayloadStatusV1(
|
|
|
|
status: PayloadExecutionStatus.valid,
|
|
|
|
latestValidHash: toValidHash(validHash)
|
|
|
|
)
|
|
|
|
|
|
|
|
proc invalidParams*(msg: string): ref InvalidRequest =
|
|
|
|
(ref InvalidRequest)(
|
|
|
|
code: engineApiInvalidParams,
|
|
|
|
msg: msg
|
|
|
|
)
|
|
|
|
|
|
|
|
proc unknownPayload*(msg: string): ref InvalidRequest =
|
|
|
|
(ref InvalidRequest)(
|
|
|
|
code: engineApiUnknownPayload,
|
|
|
|
msg: msg
|
|
|
|
)
|
|
|
|
|
|
|
|
proc invalidAttr*(msg: string): ref InvalidRequest =
|
|
|
|
(ref InvalidRequest)(
|
|
|
|
code: engineApiInvalidPayloadAttributes,
|
|
|
|
msg: msg
|
|
|
|
)
|