remove capella and deneb empty execution payload fallbacks (#4613)

This commit is contained in:
tersec 2023-02-13 18:15:16 +01:00 committed by GitHub
parent aee19fec6b
commit 07ccd3fa6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 259 deletions

View File

@ -449,12 +449,10 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
## Spec helpers ## Spec helpers
```diff ```diff
+ build_empty_execution_payload - Bellatrix OK + build_empty_execution_payload - Bellatrix OK
+ build_empty_execution_payload - Capella OK
+ build_empty_execution_payload - EIP4844 OK
+ build_proof - BeaconState OK + build_proof - BeaconState OK
+ integer_squareroot OK + integer_squareroot OK
``` ```
OK: 5/5 Fail: 0/5 Skip: 0/5 OK: 3/3 Fail: 0/3 Skip: 0/3
## Specific field types ## Specific field types
```diff ```diff
+ root update OK + root update OK
@ -621,4 +619,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
OK: 9/9 Fail: 0/9 Skip: 0/9 OK: 9/9 Fail: 0/9 Skip: 0/9
---TOTAL--- ---TOTAL---
OK: 346/351 Fail: 0/351 Skip: 5/351 OK: 344/349 Fail: 0/349 Skip: 5/349

View File

@ -20,9 +20,11 @@ import
"."/[eth2_merkleization, forks, ssz_codec] "."/[eth2_merkleization, forks, ssz_codec]
# TODO although eth2_merkleization already exports ssz_codec, *sometimes* code # TODO although eth2_merkleization already exports ssz_codec, *sometimes* code
# fails to compile if the export is not done here also # fails to compile if the export is not done here also. Exporting rlp avoids a
# generics sandwich where rlp/writer.append() is not seen, by a caller outside
# this module via compute_execution_block_hash() called from block_processor.
export export
forks, eth2_merkleization, ssz_codec eth2_merkleization, forks, rlp, ssz_codec
type type
ExecutionWithdrawal = eth_types.Withdrawal ExecutionWithdrawal = eth_types.Withdrawal
@ -202,7 +204,7 @@ func has_flag*(flags: ParticipationFlags, flag_index: int): bool =
let flag = ParticipationFlags(1'u8 shl flag_index) let flag = ParticipationFlags(1'u8 shl flag_index)
(flags and flag) == flag (flags and flag) == flag
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#is_sync_committee_update # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/altair/light-client/sync-protocol.md#is_sync_committee_update
template is_sync_committee_update*(update: SomeForkyLightClientUpdate): bool = template is_sync_committee_update*(update: SomeForkyLightClientUpdate): bool =
when update is SomeForkyLightClientUpdateWithSyncCommittee: when update is SomeForkyLightClientUpdateWithSyncCommittee:
update.next_sync_committee_branch != update.next_sync_committee_branch !=
@ -217,11 +219,11 @@ template is_finality_update*(update: SomeForkyLightClientUpdate): bool =
else: else:
false false
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#is_next_sync_committee_known # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/altair/light-client/sync-protocol.md#is_next_sync_committee_known
template is_next_sync_committee_known*(store: ForkyLightClientStore): bool = template is_next_sync_committee_known*(store: ForkyLightClientStore): bool =
store.next_sync_committee != default(typeof(store.next_sync_committee)) store.next_sync_committee != default(typeof(store.next_sync_committee))
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#get_safety_threshold # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/altair/light-client/sync-protocol.md#get_safety_threshold
func get_safety_threshold*(store: ForkyLightClientStore): uint64 = func get_safety_threshold*(store: ForkyLightClientStore): uint64 =
max( max(
store.previous_max_active_participants, store.previous_max_active_participants,
@ -319,13 +321,13 @@ template is_better_update*[
new_update: A, old_update: B): bool = new_update: A, old_update: B): bool =
is_better_data(toMeta(new_update), toMeta(old_update)) is_better_data(toMeta(new_update), toMeta(old_update))
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-alpha.1/specs/altair/light-client/p2p-interface.md#getlightclientbootstrap # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/altair/light-client/p2p-interface.md#getlightclientbootstrap
func contextEpoch*(bootstrap: ForkyLightClientBootstrap): Epoch = func contextEpoch*(bootstrap: ForkyLightClientBootstrap): Epoch =
bootstrap.header.beacon.slot.epoch bootstrap.header.beacon.slot.epoch
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/p2p-interface.md#lightclientupdatesbyrange # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/altair/light-client/p2p-interface.md#lightclientupdatesbyrange
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/altair/light-client/p2p-interface.md#getlightclientfinalityupdate # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/altair/light-client/p2p-interface.md#getlightclientfinalityupdate
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/p2p-interface.md#getlightclientoptimisticupdate # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/altair/light-client/p2p-interface.md#getlightclientoptimisticupdate
func contextEpoch*(update: SomeForkyLightClientUpdate): Epoch = func contextEpoch*(update: SomeForkyLightClientUpdate): Epoch =
update.attested_header.beacon.slot.epoch update.attested_header.beacon.slot.epoch
@ -481,85 +483,3 @@ proc build_empty_execution_payload*(
payload.block_hash = payload.compute_execution_block_hash() payload.block_hash = payload.compute_execution_block_hash()
payload payload
proc build_empty_execution_payload*(
state: capella.BeaconState,
feeRecipient: Eth1Address,
expectedWithdrawals = newSeq[capella.Withdrawal](0)
): 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 = payload.compute_execution_block_hash()
payload
# https://eips.ethereum.org/EIPS/eip-4844#parameters
const
TARGET_DATA_GAS_PER_BLOCK* = 1.u256 shl 18
DATA_GAS_PER_BLOB* = 1.u256 shl 17
# https://eips.ethereum.org/EIPS/eip-4844#header-extension
func calc_excess_data_gas*(
parent: eip4844.ExecutionPayloadHeader, new_blobs: uint): UInt256 =
let consumed_data_gas = new_blobs.u256 * DATA_GAS_PER_BLOB
return
if parent.excess_data_gas + consumed_data_gas < TARGET_DATA_GAS_PER_BLOCK:
0.u256
else:
parent.excess_data_gas + consumed_data_gas - TARGET_DATA_GAS_PER_BLOCK
proc build_empty_execution_payload*(
state: eip4844.BeaconState,
feeRecipient: Eth1Address,
expectedWithdrawals = newSeq[capella.Withdrawal](0)
): eip4844.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 = eip4844.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,
excess_data_gas: latest.calc_excess_data_gas(new_blobs = 0))
for withdrawal in expectedWithdrawals:
doAssert payload.withdrawals.add withdrawal
payload.block_hash = payload.compute_execution_block_hash()
payload

View File

@ -300,19 +300,19 @@ proc forkchoice_updated(
proc get_execution_payload[EP]( proc get_execution_payload[EP](
payload_id: Option[bellatrix.PayloadID], execution_engine: Eth1Monitor): payload_id: Option[bellatrix.PayloadID], execution_engine: Eth1Monitor):
Future[EP] {.async.} = Future[Opt[EP]] {.async.} =
return if payload_id.isNone(): return if payload_id.isNone():
# Pre-merge, empty payload # Pre-merge, empty payload
default(EP) Opt.some default(EP)
else: else:
when EP is bellatrix.ExecutionPayload: when EP is bellatrix.ExecutionPayload:
asConsensusExecutionPayload( Opt.some asConsensusExecutionPayload(
await execution_engine.getPayloadV1(payload_id.get)) await execution_engine.getPayloadV1(payload_id.get))
elif EP is capella.ExecutionPayload: elif EP is capella.ExecutionPayload:
asConsensusExecutionPayload( Opt.some asConsensusExecutionPayload(
await execution_engine.getPayloadV2(payload_id.get)) await execution_engine.getPayloadV2(payload_id.get))
elif EP is eip4844.ExecutionPayload: elif EP is eip4844.ExecutionPayload:
asConsensusExecutionPayload( Opt.some asConsensusExecutionPayload(
await execution_engine.getPayloadV3(payload_id.get)) await execution_engine.getPayloadV3(payload_id.get))
else: else:
static: doAssert "unknown execution payload type" static: doAssert "unknown execution payload type"
@ -345,22 +345,25 @@ proc getExecutionPayload[T](
# transmit this information through the Forked types, so this has to # transmit this information through the Forked types, so this has to
# be re-proven here. # be re-proven here.
withState(proposalState[]): withState(proposalState[]):
when (stateFork == ConsensusFork.EIP4844 and when stateFork >= ConsensusFork.Capella:
T is eip4844.ExecutionPayload) or # As of Capella, because EL state root changes in way more difficult to
(stateFork == ConsensusFork.Capella and # compute way from CL due to incorporation of withdrawals into EL state
T is capella.ExecutionPayload) or # cannot use fake-EL fallback. Unlike transactions, withdrawals are not
(stateFork == ConsensusFork.Bellatrix and # optional, so one cannot avoid this by not including any withdrawals.
Opt.none T
elif (stateFork == ConsensusFork.Bellatrix and
T is bellatrix.ExecutionPayload): T is bellatrix.ExecutionPayload):
build_empty_execution_payload(forkyState.data, feeRecipient) Opt.some build_empty_execution_payload(forkyState.data, feeRecipient)
elif stateFork >= ConsensusFork.Bellatrix: elif stateFork == ConsensusFork.Bellatrix:
raiseAssert "getExecutionPayload: mismatched proposalState and ExecutionPayload fork" raiseAssert "getExecutionPayload: mismatched proposalState and ExecutionPayload fork"
else: else:
default(T) # Vacuously -- these are pre-Bellatrix and not used.
Opt.some default(T)
if node.eth1Monitor.isNil: if node.eth1Monitor.isNil:
beacon_block_payload_errors.inc() beacon_block_payload_errors.inc()
warn "getExecutionPayload: eth1Monitor not initialized; using empty execution payload" warn "getExecutionPayload: eth1Monitor not initialized; using empty execution payload"
return Opt.some empty_execution_payload return empty_execution_payload
try: try:
# Minimize window for Eth1 monitor to shut down connection # Minimize window for Eth1 monitor to shut down connection
@ -422,12 +425,12 @@ proc getExecutionPayload[T](
payload_id, err = err.msg payload_id, err = err.msg
empty_execution_payload empty_execution_payload
return Opt.some payload return payload
except CatchableError as err: except CatchableError as err:
beacon_block_payload_errors.inc() beacon_block_payload_errors.inc()
error "Error creating non-empty execution payload; using empty execution payload", error "Error creating non-empty execution payload; using empty execution payload",
msg = err.msg msg = err.msg
return Opt.some empty_execution_payload return empty_execution_payload
proc getBlobsBundle( proc getBlobsBundle(
node: BeaconNode, epoch: Epoch, validator_index: ValidatorIndex, node: BeaconNode, epoch: Epoch, validator_index: ValidatorIndex,

View File

@ -1,5 +1,5 @@
# beacon_chain # beacon_chain
# Copyright (c) 2018-2022 Status Research & Development GmbH # Copyright (c) 2018-2023 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -76,79 +76,3 @@ suite "Spec helpers":
testCase default(Eth1Address) testCase default(Eth1Address)
testCase Eth1Address.fromHex("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b") 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 = payloadToBlockHeader(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)]
test "build_empty_execution_payload - EIP4844":
var cfg = defaultRuntimeConfig
cfg.ALTAIR_FORK_EPOCH = GENESIS_EPOCH
cfg.BELLATRIX_FORK_EPOCH = GENESIS_EPOCH
cfg.CAPELLA_FORK_EPOCH = GENESIS_EPOCH
cfg.EIP4844_FORK_EPOCH = GENESIS_EPOCH
let
state = newClone(initGenesisState(cfg = cfg).eip4844Data)
recipient = Eth1Address.fromHex(
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
withdrawals = @[
capella.Withdrawal(
index: 42,
validatorIndex: 1337,
address: bellatrix.ExecutionAddress(data: distinctBase(recipient)),
amount: 25.Gwei)]
payload = build_empty_execution_payload(
state[].data, recipient, withdrawals)
check:
payload.fee_recipient ==
bellatrix.ExecutionAddress(data: distinctBase(recipient))
payload.withdrawals[0] == withdrawals[0]
payload.excess_data_gas == 0.u256

View File

@ -78,9 +78,6 @@ func signBlock(
ValidatorSig() ValidatorSig()
ForkedSignedBeaconBlock.init(forked, root, signature) ForkedSignedBeaconBlock.init(forked, root, signature)
from ../beacon_chain/spec/datatypes/capella import
BeaconState, ExecutionPayload, SignedBLSToExecutionChangeList
proc build_empty_merge_execution_payload(state: bellatrix.BeaconState): proc build_empty_merge_execution_payload(state: bellatrix.BeaconState):
bellatrix.ExecutionPayload = bellatrix.ExecutionPayload =
## Assuming a pre-state of the same slot, build a valid ExecutionPayload ## Assuming a pre-state of the same slot, build a valid ExecutionPayload
@ -108,44 +105,17 @@ proc build_empty_merge_execution_payload(state: bellatrix.BeaconState):
payload payload
proc build_empty_merge_execution_payload(state: capella.BeaconState): proc addTestBlock*(
capella.ExecutionPayload =
## Assuming a pre-state of the same slot, build a valid ExecutionPayload
## without any transactions from a non-merged block.
doAssert not is_merge_transition_complete(state)
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))
var payload = capella.ExecutionPayload(
parent_hash: latest.block_hash,
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: 30000000, # retain same limit
gas_used: 0, # empty block, 0 gas
timestamp: timestamp,
base_fee_per_gas: EIP1559_INITIAL_BASE_FEE)
payload.block_hash = rlpHash payloadToBlockHeader(payload)
payload
proc addTestBlockAux[EP: bellatrix.ExecutionPayload | capella.ExecutionPayload](
state: var ForkedHashedBeaconState, state: var ForkedHashedBeaconState,
cache: var StateCache, cache: var StateCache,
eth1_data: Eth1Data, eth1_data: Eth1Data = Eth1Data(),
attestations: seq[Attestation], attestations: seq[Attestation] = newSeq[Attestation](),
deposits: seq[Deposit], deposits: seq[Deposit] = newSeq[Deposit](),
sync_aggregate: SyncAggregate, sync_aggregate: SyncAggregate = SyncAggregate.init(),
graffiti: GraffitiBytes, graffiti: GraffitiBytes = default(GraffitiBytes),
flags: set[UpdateFlag], flags: set[UpdateFlag] = {},
nextSlot: bool, nextSlot: bool = true,
cfg: RuntimeConfig): ForkedSignedBeaconBlock = cfg: RuntimeConfig = defaultRuntimeConfig): ForkedSignedBeaconBlock =
# Create and add a block to state - state will advance by one slot! # Create and add a block to state - state will advance by one slot!
if nextSlot: if nextSlot:
var info = ForkedEpochInfo() var info = ForkedEpochInfo()
@ -167,23 +137,25 @@ proc addTestBlockAux[EP: bellatrix.ExecutionPayload | capella.ExecutionPayload](
ValidatorSig() ValidatorSig()
let execution_payload = let execution_payload =
withState(state): if cfg.CAPELLA_FORK_EPOCH != FAR_FUTURE_EPOCH:
when (stateFork == ConsensusFork.Bellatrix and # Can't keep correctly doing this once Capella happens, but LVH search
EP is bellatrix.ExecutionPayload) or # test relies on merging. So, merge only if no Capella transition.
(stateFork == ConsensusFork.Capella and default(bellatrix.ExecutionPayload)
EP is capella.ExecutionPayload): else:
# Merge shortly after Bellatrix withState(state):
if forkyState.data.slot > when stateFork == ConsensusFork.Bellatrix:
cfg.BELLATRIX_FORK_EPOCH * SLOTS_PER_EPOCH + 10: # Merge shortly after Bellatrix
if is_merge_transition_complete(forkyState.data): if forkyState.data.slot >
const feeRecipient = default(Eth1Address) cfg.BELLATRIX_FORK_EPOCH * SLOTS_PER_EPOCH + 10:
build_empty_execution_payload(forkyState.data, feeRecipient) if is_merge_transition_complete(forkyState.data):
const feeRecipient = default(Eth1Address)
build_empty_execution_payload(forkyState.data, feeRecipient)
else:
build_empty_merge_execution_payload(forkyState.data)
else: else:
build_empty_merge_execution_payload(forkyState.data) default(bellatrix.ExecutionPayload)
else: else:
default(EP) default(bellatrix.ExecutionPayload)
else:
default(EP)
let let
message = makeBeaconBlock( message = makeBeaconBlock(
@ -218,24 +190,6 @@ proc addTestBlockAux[EP: bellatrix.ExecutionPayload | capella.ExecutionPayload](
new_block new_block
proc addTestBlock*(
state: var ForkedHashedBeaconState, cache: var StateCache,
eth1_data = Eth1Data(), attestations = newSeq[Attestation](),
deposits = newSeq[Deposit](), sync_aggregate = SyncAggregate.init(),
graffiti = default(GraffitiBytes), flags: set[UpdateFlag] = {},
nextSlot = true, cfg = defaultRuntimeConfig): ForkedSignedBeaconBlock =
case state.kind
of ConsensusFork.Phase0, ConsensusFork.Altair, ConsensusFork.Bellatrix:
addTestBlockAux[bellatrix.ExecutionPayload](
state, cache, eth1_data, attestations, deposits, sync_aggregate,
graffiti, flags, nextSlot, cfg)
of ConsensusFork.Capella:
addTestBlockAux[capella.ExecutionPayload](
state, cache, eth1_data, attestations, deposits, sync_aggregate,
graffiti, flags, nextSlot, cfg)
of ConsensusFork.EIP4844:
raiseAssert $eip4844ImplementationMissing & ": tests/testblockutil.nim addTestBlock"
proc makeTestBlock*( proc makeTestBlock*(
state: ForkedHashedBeaconState, state: ForkedHashedBeaconState,
cache: var StateCache, cache: var StateCache,