mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-10 14:26:26 +00:00
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:
parent
3d829b8755
commit
c941dc801f
@ -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
|
||||
|
@ -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
|
||||
|
||||
ExecutionBlockHeader(
|
||||
parentHash : payload.parent_hash,
|
||||
ommersHash : EMPTY_UNCLE_HASH,
|
||||
coinbase : EthAddress payload.fee_recipient.data,
|
||||
stateRoot : payload.state_root,
|
||||
txRoot : EMPTY_ROOT_HASH,
|
||||
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,
|
||||
timestamp : fromUnix(int64.saturate payload.timestamp),
|
||||
extraData : payload.extra_data.asSeq,
|
||||
mixDigest : payload.prev_randao, # EIP-4399 redefine `mixDigest` -> `prevRandao`
|
||||
nonce : default(BlockNonce),
|
||||
fee : some payload.base_fee_per_gas
|
||||
)
|
||||
let
|
||||
txRoot = EMPTY_ROOT_HASH
|
||||
withdrawalsRoot =
|
||||
when payload is bellatrix.ExecutionPayload:
|
||||
none(Hash256)
|
||||
else:
|
||||
some payload.computeWithdrawalsTrieRoot()
|
||||
|
||||
func build_empty_execution_payload*[BS, EP](
|
||||
state: BS, feeRecipient: Eth1Address): EP =
|
||||
ExecutionBlockHeader(
|
||||
parentHash : payload.parent_hash,
|
||||
ommersHash : EMPTY_UNCLE_HASH,
|
||||
coinbase : EthAddress payload.fee_recipient.data,
|
||||
stateRoot : payload.state_root,
|
||||
txRoot : txRoot,
|
||||
receiptRoot : payload.receipts_root,
|
||||
bloom : payload.logs_bloom.data,
|
||||
difficulty : default(DifficultyInt),
|
||||
blockNumber : payload.block_number.u256,
|
||||
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 `mixDigest` -> `prevRandao`
|
||||
nonce : default(BlockNonce),
|
||||
fee : some payload.base_fee_per_gas,
|
||||
withdrawalsRoot: withdrawalsRoot)
|
||||
|
||||
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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
check payload.fee_recipient ==
|
||||
bellatrix.ExecutionAddress(data: distinctBase(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)]
|
||||
|
@ -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
2
vendor/nim-eth
vendored
@ -1 +1 @@
|
||||
Subproject commit 70b83a4efbb2912bd33d3a2acfb7aad9b40f4bc4
|
||||
Subproject commit 6499ee2bc5d264fdc68f5f08b647222a5c5252fa
|
Loading…
x
Reference in New Issue
Block a user