bump nim-eth, extend empty block fallback for Capella (#4357)

Implements `emptyPayloadToBlockHeader` for Capella.
https://github.com/status-im/nim-eth/pull/562
This commit is contained in:
Etan Kissling 2022-11-28 14:41:25 +01:00 committed by GitHub
parent 3d829b8755
commit c941dc801f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 162 additions and 41 deletions

View File

@ -447,11 +447,12 @@ OK: 12/12 Fail: 0/12 Skip: 0/12
OK: 1/1 Fail: 0/1 Skip: 0/1
## Spec helpers
```diff
+ build_empty_execution_payload OK
+ build_empty_execution_payload - Bellatrix OK
+ build_empty_execution_payload - Capella OK
+ build_proof - BeaconState OK
+ integer_squareroot OK
```
OK: 3/3 Fail: 0/3 Skip: 0/3
OK: 4/4 Fail: 0/4 Skip: 0/4
## Specific field types
```diff
+ root update OK
@ -613,4 +614,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
OK: 9/9 Fail: 0/9 Skip: 0/9
---TOTAL---
OK: 338/343 Fail: 0/343 Skip: 5/343
OK: 339/344 Fail: 0/344 Skip: 5/344

View File

@ -19,6 +19,7 @@ import
stew/[bitops2, byteutils, endians2, objects, saturation_arith],
chronicles,
eth/eip1559, eth/common/[eth_types, eth_types_rlp],
eth/rlp, eth/trie/[db, hexary],
# Internal
./datatypes/[phase0, altair, bellatrix, capella],
"."/[eth2_merkleization, forks, ssz_codec]
@ -29,6 +30,7 @@ export
forks, eth2_merkleization, ssz_codec
type
ExecutionWithdrawal = eth_types.Withdrawal
ExecutionBlockHeader = eth_types.BlockHeader
FinalityCheckpoints* = object
@ -360,32 +362,71 @@ func compute_timestamp_at_slot*(state: ForkyBeaconState, slot: Slot): uint64 =
let slots_since_genesis = slot - GENESIS_SLOT
state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT
func gweiToWei*(gwei: Gwei): UInt256 =
gwei.u256 * 1_000_000_000.u256
func toExecutionWithdrawal*(
withdrawal: capella.Withdrawal): ExecutionWithdrawal =
ExecutionWithdrawal(
index: withdrawal.index,
validatorIndex: withdrawal.validatorIndex,
address: EthAddress withdrawal.address.data,
amount: gweiToWei withdrawal.amount)
# https://eips.ethereum.org/EIPS/eip-4895
proc computeWithdrawalsTrieRoot*(
payload: capella.ExecutionPayload): Hash256 =
if payload.withdrawals.len == 0:
return EMPTY_ROOT_HASH
var tr = initHexaryTrie(newMemoryDB())
for i, withdrawal in payload.withdrawals:
try:
tr.put(rlp.encode(i), rlp.encode(toExecutionWithdrawal(withdrawal)))
except RlpError as exc:
doAssert false, "HexaryTrie.put failed: " & $exc.msg
tr.rootHash()
proc emptyPayloadToBlockHeader*(
payload: bellatrix.ExecutionPayload): ExecutionBlockHeader =
payload: bellatrix.ExecutionPayload | capella.ExecutionPayload
): ExecutionBlockHeader =
static: # `GasInt` is signed. We only use it for hashing.
doAssert sizeof(GasInt) == sizeof(payload.gas_limit)
doAssert sizeof(GasInt) == sizeof(payload.gas_used)
## This function assumes that the payload is empty!
doAssert payload.transactions.len == 0
let
txRoot = EMPTY_ROOT_HASH
withdrawalsRoot =
when payload is bellatrix.ExecutionPayload:
none(Hash256)
else:
some payload.computeWithdrawalsTrieRoot()
ExecutionBlockHeader(
parentHash : payload.parent_hash,
ommersHash : EMPTY_UNCLE_HASH,
coinbase : EthAddress payload.fee_recipient.data,
stateRoot : payload.state_root,
txRoot : EMPTY_ROOT_HASH,
txRoot : txRoot,
receiptRoot : payload.receipts_root,
bloom : payload.logs_bloom.data,
difficulty : default(DifficultyInt),
blockNumber : payload.block_number.u256,
gasLimit : GasInt payload.gas_limit,
gasUsed : GasInt payload.gas_used,
gasLimit : cast[GasInt](payload.gas_limit),
gasUsed : cast[GasInt](payload.gas_used),
timestamp : fromUnix(int64.saturate payload.timestamp),
extraData : payload.extra_data.asSeq,
mixDigest : payload.prev_randao, # EIP-4399 redefine `mixDigest` -> `prevRandao`
mixDigest : payload.prev_randao, # EIP-4399 `mixDigest` -> `prevRandao`
nonce : default(BlockNonce),
fee : some payload.base_fee_per_gas
)
fee : some payload.base_fee_per_gas,
withdrawalsRoot: withdrawalsRoot)
func build_empty_execution_payload*[BS, EP](
state: BS, feeRecipient: Eth1Address): EP =
func build_empty_execution_payload*(
state: bellatrix.BeaconState,
feeRecipient: Eth1Address): bellatrix.ExecutionPayload =
## Assuming a pre-state of the same slot, build a valid ExecutionPayload
## without any transactions.
let
@ -396,7 +437,7 @@ func build_empty_execution_payload*[BS, EP](
GasInt.saturate latest.gas_used,
latest.base_fee_per_gas)
var payload = EP(
var payload = bellatrix.ExecutionPayload(
parent_hash: latest.block_hash,
fee_recipient: bellatrix.ExecutionAddress(data: distinctBase(feeRecipient)),
state_root: latest.state_root, # no changes to the state
@ -411,3 +452,35 @@ func build_empty_execution_payload*[BS, EP](
payload.block_hash = rlpHash emptyPayloadToBlockHeader(payload)
payload
proc build_empty_execution_payload*(
state: capella.BeaconState,
feeRecipient: Eth1Address,
expectedWithdrawals: seq[capella.Withdrawal]): capella.ExecutionPayload =
## Assuming a pre-state of the same slot, build a valid ExecutionPayload
## without any transactions.
let
latest = state.latest_execution_payload_header
timestamp = compute_timestamp_at_slot(state, state.slot)
randao_mix = get_randao_mix(state, get_current_epoch(state))
base_fee = calcEip1599BaseFee(GasInt.saturate latest.gas_limit,
GasInt.saturate latest.gas_used,
latest.base_fee_per_gas)
var payload = capella.ExecutionPayload(
parent_hash: latest.block_hash,
fee_recipient: bellatrix.ExecutionAddress(data: distinctBase(feeRecipient)),
state_root: latest.state_root, # no changes to the state
receipts_root: EMPTY_ROOT_HASH,
block_number: latest.block_number + 1,
prev_randao: randao_mix,
gas_limit: latest.gas_limit, # retain same limit
gas_used: 0, # empty block, 0 gas
timestamp: timestamp,
base_fee_per_gas: base_fee)
for withdrawal in expectedWithdrawals:
doAssert payload.withdrawals.add withdrawal
payload.block_hash = rlpHash emptyPayloadToBlockHeader(payload)
payload

View File

@ -388,9 +388,10 @@ proc getExecutionPayload[T](
template empty_execution_payload(): auto =
withState(proposalState[]):
when stateFork >= BeaconStateFork.Bellatrix:
build_empty_execution_payload[typeof forkyState.data, T](
forkyState.data, feeRecipient)
when stateFork >= BeaconStateFork.Capella:
raiseAssert $capellaImplementationMissing
elif stateFork >= BeaconStateFork.Bellatrix:
build_empty_execution_payload(forkyState.data, feeRecipient)
else:
default(T)

View File

@ -10,10 +10,11 @@
import
# Status libraries
stew/bitops2,
eth/common/eth_types as commonEthTypes, eth/common/eth_types_rlp,
web3/ethtypes,
# Beacon chain internals
../beacon_chain/spec/[forks, helpers, state_transition],
../beacon_chain/spec/datatypes/bellatrix,
../beacon_chain/spec/datatypes/[bellatrix, capella],
# Test utilities
./unittest2, mocking/mock_genesis
@ -61,20 +62,67 @@ suite "Spec helpers":
i += 1
process(state, state.numLeaves)
test "build_empty_execution_payload":
test "build_empty_execution_payload - Bellatrix":
var cfg = defaultRuntimeConfig
cfg.ALTAIR_FORK_EPOCH = GENESIS_EPOCH
cfg.BELLATRIX_FORK_EPOCH = GENESIS_EPOCH
let state = newClone(initGenesisState(cfg = cfg).bellatrixData)
template testCase(recipient: Eth1Address): untyped =
block:
let payload = build_empty_execution_payload[
typeof state[].data, bellatrix.ExecutionPayload](
state[].data, recipient)
proc testCase(recipient: Eth1Address) =
let payload = build_empty_execution_payload(state[].data, recipient)
check payload.fee_recipient ==
bellatrix.ExecutionAddress(data: distinctBase(recipient))
testCase default(Eth1Address)
testCase Eth1Address.fromHex("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
test "build_empty_execution_payload - Capella":
var cfg = defaultRuntimeConfig
cfg.ALTAIR_FORK_EPOCH = GENESIS_EPOCH
cfg.BELLATRIX_FORK_EPOCH = GENESIS_EPOCH
cfg.CAPELLA_FORK_EPOCH = GENESIS_EPOCH
let
state = newClone(initGenesisState(cfg = cfg).capellaData)
recipient = Eth1Address.fromHex(
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
proc testCase(withdrawals: seq[capella.Withdrawal]) =
let payload = build_empty_execution_payload(
state[].data, recipient, withdrawals)
check payload.fee_recipient ==
bellatrix.ExecutionAddress(data: distinctBase(recipient))
for i, withdrawal in withdrawals:
check payload.withdrawals[i] == withdrawal
let elHeader = emptyPayloadToBlockHeader(payload)
check elHeader.withdrawalsRoot.isSome
if withdrawals.len == 0:
check elHeader.withdrawalsRoot.get == EMPTY_ROOT_HASH
else:
check elHeader.withdrawalsRoot.get != EMPTY_ROOT_HASH
check elHeader.blockHash == payload.block_hash
var bellatrixHeader = elHeader
bellatrixHeader.withdrawalsRoot.reset()
check elHeader.blockHash != rlpHash bellatrixHeader
testCase @[]
testCase @[
capella.Withdrawal(
index: 42,
validatorIndex: 1337,
address: bellatrix.ExecutionAddress(data: distinctBase(recipient)),
amount: 25.Gwei)]
testCase @[
capella.Withdrawal(
index: 1,
validatorIndex: 1,
address: bellatrix.ExecutionAddress(data: distinctBase(recipient)),
amount: 1.Gwei),
capella.Withdrawal(
index: 2,
validatorIndex: 2,
address: bellatrix.ExecutionAddress(data: distinctBase(recipient)),
amount: 2.Gwei)]

View File

@ -146,9 +146,7 @@ proc addTestBlock*(
cfg.BELLATRIX_FORK_EPOCH * SLOTS_PER_EPOCH + 10:
if is_merge_transition_complete(forkyState.data):
const feeRecipient = default(Eth1Address)
build_empty_execution_payload[
bellatrix.BeaconState, bellatrix.ExecutionPayload](
forkyState.data, feeRecipient)
build_empty_execution_payload(forkyState.data, feeRecipient)
else:
build_empty_merge_execution_payload(forkyState.data)
else:

2
vendor/nim-eth vendored

@ -1 +1 @@
Subproject commit 70b83a4efbb2912bd33d3a2acfb7aad9b40f4bc4
Subproject commit 6499ee2bc5d264fdc68f5f08b647222a5c5252fa