From 74e978fac7cfb63c7cf19f527b3db133b0594256 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Sun, 15 Jan 2023 15:05:44 +0100 Subject: [PATCH] support EIP4844 LC data format Implements the proposed light client data format for EIP4844: - https://github.com/ethereum/consensus-specs/pull/3151 --- ConsensusSpecPreset-mainnet.md | 10 +- ConsensusSpecPreset-minimal.md | 10 +- beacon_chain/light_client.nim | 10 - beacon_chain/nimbus_beacon_node.nim | 14 +- beacon_chain/nimbus_light_client.nim | 16 +- beacon_chain/spec/datatypes/eip4844.nim | 337 +++++++++++++++++- beacon_chain/spec/forks.nim | 6 +- beacon_chain/spec/forks_light_client.nim | 249 +++++++++++-- .../test_fixture_ssz_consensus_objects.nim | 10 +- 9 files changed, 605 insertions(+), 57 deletions(-) diff --git a/ConsensusSpecPreset-mainnet.md b/ConsensusSpecPreset-mainnet.md index 5bb0247af7..3d9302a7f1 100644 --- a/ConsensusSpecPreset-mainnet.md +++ b/ConsensusSpecPreset-mainnet.md @@ -2023,6 +2023,14 @@ OK: 1/1 Fail: 0/1 Skip: 0/1 + Testing Withdrawal OK ``` OK: 48/48 Fail: 0/48 Skip: 0/48 +## EF - EIP4844 - Unittests - Light client - Sync protocol [Preset: mainnet] +```diff ++ process_light_client_update_finality_updated OK ++ process_light_client_update_timeout OK ++ test_process_light_client_update_at_period_boundary OK ++ test_process_light_client_update_not_timeout OK +``` +OK: 4/4 Fail: 0/4 Skip: 0/4 ## EF - Phase 0 - Epoch Processing - Effective balance updates [Preset: mainnet] ```diff + Effective balance updates - effective_balance_hysteresis [Preset: mainnet] OK @@ -2588,4 +2596,4 @@ OK: 63/63 Fail: 0/63 Skip: 0/63 OK: 100/100 Fail: 0/100 Skip: 0/100 ---TOTAL--- -OK: 2289/2298 Fail: 0/2298 Skip: 9/2298 +OK: 2293/2302 Fail: 0/2302 Skip: 9/2302 diff --git a/ConsensusSpecPreset-minimal.md b/ConsensusSpecPreset-minimal.md index 449f422a81..9799831c8a 100644 --- a/ConsensusSpecPreset-minimal.md +++ b/ConsensusSpecPreset-minimal.md @@ -2187,6 +2187,14 @@ OK: 5/5 Fail: 0/5 Skip: 0/5 + Testing Withdrawal OK ``` OK: 48/48 Fail: 0/48 Skip: 0/48 +## EF - EIP4844 - Unittests - Light client - Sync protocol [Preset: minimal] +```diff ++ process_light_client_update_finality_updated OK ++ process_light_client_update_timeout OK ++ test_process_light_client_update_at_period_boundary OK ++ test_process_light_client_update_not_timeout OK +``` +OK: 4/4 Fail: 0/4 Skip: 0/4 ## EF - Phase 0 - Epoch Processing - Effective balance updates [Preset: minimal] ```diff + Effective balance updates - effective_balance_hysteresis [Preset: minimal] OK @@ -2755,4 +2763,4 @@ OK: 68/68 Fail: 0/68 Skip: 0/68 OK: 102/102 Fail: 0/102 Skip: 0/102 ---TOTAL--- -OK: 2440/2449 Fail: 0/2449 Skip: 9/2449 +OK: 2444/2453 Fail: 0/2453 Skip: 9/2453 diff --git a/beacon_chain/light_client.nim b/beacon_chain/light_client.nim index 9edff8866c..1a4152908a 100644 --- a/beacon_chain/light_client.nim +++ b/beacon_chain/light_client.nim @@ -347,10 +347,6 @@ proc installMessageValidators*( let forkDigests = lightClient.forkDigests for stateFork in BeaconStateFork: - if stateFork >= BeaconStateFork.EIP4844: - # Format is still in development, do not use Gossip at this time. - continue - withLcDataFork(lcDataForkAtStateFork(stateFork)): when lcDataFork > LightClientDataFork.None: let @@ -420,9 +416,6 @@ proc updateGossipStatus*( for gossipFork in oldGossipForks: if gossipFork >= BeaconStateFork.Altair: - if gossipFork >= BeaconStateFork.EIP4844: - # Format is still in development, do not use Gossip at this time. - continue let forkDigest = lightClient.forkDigests[].atStateFork(gossipFork) lightClient.network.unsubscribe( getLightClientFinalityUpdateTopic(forkDigest)) @@ -431,9 +424,6 @@ proc updateGossipStatus*( for gossipFork in newGossipForks: if gossipFork >= BeaconStateFork.Altair: - if gossipFork >= BeaconStateFork.EIP4844: - # Format is still in development, do not use Gossip at this time. - continue let forkDigest = lightClient.forkDigests[].atStateFork(gossipFork) lightClient.network.subscribe( getLightClientFinalityUpdateTopic(forkDigest), diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 5d49c4eaa8..27acdc270e 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -1430,7 +1430,7 @@ proc installMessageValidators(node: BeaconNode) = installPhase0Validators(forkDigests.altair) installPhase0Validators(forkDigests.bellatrix) installPhase0Validators(forkDigests.capella) - if node.dag.cfg.EIP4844_FORK_EPOCH != FAR_FUTURE_EPOCH: + if node.dag.cfg.EIP4844_FORK_EPOCH != FAR_FUTURE_EPOCH: installPhase0Validators(forkDigests.eip4844) node.network.addValidator( @@ -1463,11 +1463,17 @@ proc installMessageValidators(node: BeaconNode) = toValidationResult(node.processor[].processSignedBeaconBlock( MsgSource.gossip, signedBlock))) - if node.dag.cfg.EIP4844_FORK_EPOCH != FAR_FUTURE_EPOCH: + if node.dag.cfg.EIP4844_FORK_EPOCH != FAR_FUTURE_EPOCH: node.network.addValidator( getBeaconBlockAndBlobsSidecarTopic(forkDigests.eip4844), - proc (signedBlock: eip4844.SignedBeaconBlockAndBlobsSidecar): ValidationResult = - # TODO: take into account node.shouldSyncOptimistically(node.currentSlot) + proc ( + signedBlock: eip4844.SignedBeaconBlockAndBlobsSidecar + ): ValidationResult = + if node.shouldSyncOptimistically(node.currentSlot): + toValidationResult( + node.optimisticProcessor.processSignedBeaconBlock( + signedBlock.beacon_block)) + else: toValidationResult(node.processor[].processSignedBeaconBlock( MsgSource.gossip, signedBlock))) diff --git a/beacon_chain/nimbus_light_client.nim b/beacon_chain/nimbus_light_client.nim index 0cce10ab36..205663ad87 100644 --- a/beacon_chain/nimbus_light_client.nim +++ b/beacon_chain/nimbus_light_client.nim @@ -13,7 +13,7 @@ import ./gossip_processing/optimistic_processor, ./networking/topic_params, ./spec/beaconstate, - ./spec/datatypes/[phase0, altair, bellatrix, capella], + ./spec/datatypes/[phase0, altair, bellatrix, capella, eip4844], "."/[filepath, light_client, light_client_db, nimbus_binary_common, version] from ./consensus_object_pools/consensus_manager import runForkchoiceUpdated @@ -159,6 +159,14 @@ programMain: proc (signedBlock: capella.SignedBeaconBlock): ValidationResult = toValidationResult( optimisticProcessor.processSignedBeaconBlock(signedBlock))) + network.addValidator( + getBeaconBlockAndBlobsSidecarTopic(forkDigests.eip4844), + proc ( + signedBlock: eip4844.SignedBeaconBlockAndBlobsSidecar + ): ValidationResult = + toValidationResult( + optimisticProcessor.processSignedBeaconBlock( + signedBlock.beacon_block))) lightClient.installMessageValidators() waitFor network.startListening() waitFor network.start() @@ -255,16 +263,10 @@ programMain: oldGossipForks = currentGossipState - targetGossipState for gossipFork in oldGossipForks: - if gossipFork >= BeaconStateFork.EIP4844: - # Format is still in development, do not use Gossip at this time. - continue let forkDigest = forkDigests[].atStateFork(gossipFork) network.unsubscribe(getBeaconBlocksTopic(forkDigest)) for gossipFork in newGossipForks: - if gossipFork >= BeaconStateFork.EIP4844: - # Format is still in development, do not use Gossip at this time. - continue let forkDigest = forkDigests[].atStateFork(gossipFork) network.subscribe( getBeaconBlocksTopic(forkDigest), blocksTopicParams, diff --git a/beacon_chain/spec/datatypes/eip4844.nim b/beacon_chain/spec/datatypes/eip4844.nim index 6f880bc442..b5261f903f 100644 --- a/beacon_chain/spec/datatypes/eip4844.nim +++ b/beacon_chain/spec/datatypes/eip4844.nim @@ -19,8 +19,10 @@ else: {.push raises: [].} import - stew/byteutils, + chronicles, + stew/[bitops2, byteutils], json_serialization, + ssz_serialization/[merkleization, proofs], ssz_serialization/types as sszTypes, ../digest, "."/[base, phase0, altair, bellatrix, capella] @@ -108,6 +110,102 @@ type ExecutePayload* = proc( execution_payload: ExecutionPayload): bool {.gcsafe, raises: [Defect].} + # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/capella/light-client/sync-protocol.md#lightclientheader + LightClientHeader* = object + beacon*: BeaconBlockHeader + ## Beacon block header + + execution*: ExecutionPayloadHeader + ## Execution payload header corresponding to `beacon.body_root` (from Capella onward) + execution_branch*: capella.ExecutionBranch + + # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/altair/light-client/sync-protocol.md#lightclientbootstrap + LightClientBootstrap* = object + header*: LightClientHeader + ## Header matching the requested beacon block root + + current_sync_committee*: SyncCommittee + ## Current sync committee corresponding to `header.beacon.state_root` + current_sync_committee_branch*: altair.CurrentSyncCommitteeBranch + + # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/altair/light-client/sync-protocol.md#lightclientupdate + LightClientUpdate* = object + attested_header*: LightClientHeader + ## Header attested to by the sync committee + + next_sync_committee*: SyncCommittee + ## Next sync committee corresponding to `attested_header.beacon.state_root` + next_sync_committee_branch*: altair.NextSyncCommitteeBranch + + # Finalized header corresponding to `attested_header.beacon.state_root` + finalized_header*: LightClientHeader + finality_branch*: altair.FinalityBranch + + sync_aggregate*: SyncAggregate + ## Sync committee aggregate signature + signature_slot*: Slot + ## Slot at which the aggregate signature was created (untrusted) + + # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/altair/light-client/sync-protocol.md#lightclientfinalityupdate + LightClientFinalityUpdate* = object + # Header attested to by the sync committee + attested_header*: LightClientHeader + + # Finalized header corresponding to `attested_header.beacon.state_root` + finalized_header*: LightClientHeader + finality_branch*: altair.FinalityBranch + + # Sync committee aggregate signature + sync_aggregate*: SyncAggregate + # Slot at which the aggregate signature was created (untrusted) + signature_slot*: Slot + + # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate + LightClientOptimisticUpdate* = object + # Header attested to by the sync committee + attested_header*: LightClientHeader + + # Sync committee aggregate signature + sync_aggregate*: SyncAggregate + # Slot at which the aggregate signature was created (untrusted) + signature_slot*: Slot + + SomeLightClientUpdateWithSyncCommittee* = + LightClientUpdate + + SomeLightClientUpdateWithFinality* = + LightClientUpdate | + LightClientFinalityUpdate + + SomeLightClientUpdate* = + LightClientUpdate | + LightClientFinalityUpdate | + LightClientOptimisticUpdate + + SomeLightClientObject* = + LightClientBootstrap | + SomeLightClientUpdate + + # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/altair/light-client/sync-protocol.md#lightclientstore + LightClientStore* = object + finalized_header*: LightClientHeader + ## Header that is finalized + + current_sync_committee*: SyncCommittee + ## Sync committees corresponding to the finalized header + next_sync_committee*: SyncCommittee + + best_valid_update*: Opt[LightClientUpdate] + ## Best available header to switch finalized head to if we see nothing else + + optimistic_header*: LightClientHeader + ## Most recent available reasonably-safe header + + previous_max_active_participants*: uint64 + ## Max number of active participants in a sync committee (used to compute + ## safety threshold) + current_max_active_participants*: uint64 + # https://github.com/ethereum/consensus-specs/blob/v1.3.0-alpha.1/specs/capella/beacon-chain.md#beaconstate # changes indirectly via ExecutionPayloadHeader BeaconState* = object @@ -403,6 +501,243 @@ func shortLog*(v: SomeSignedBeaconBlock): auto = blck: shortLog(v.message), signature: shortLog(v.signature) ) + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/sync-protocol.md#get_lc_execution_root +func get_lc_execution_root*( + header: LightClientHeader, cfg: RuntimeConfig): Eth2Digest = + let epoch = header.beacon.slot.epoch + + if epoch >= cfg.EIP4844_FORK_EPOCH: + return hash_tree_root(header.execution) + + if epoch >= cfg.CAPELLA_FORK_EPOCH: + let execution_header = capella.ExecutionPayloadHeader( + parent_hash: header.execution.parent_hash, + fee_recipient: header.execution.fee_recipient, + state_root: header.execution.state_root, + receipts_root: header.execution.receipts_root, + logs_bloom: header.execution.logs_bloom, + prev_randao: header.execution.prev_randao, + block_number: header.execution.block_number, + gas_limit: header.execution.gas_limit, + gas_used: header.execution.gas_used, + timestamp: header.execution.timestamp, + extra_data: header.execution.extra_data, + base_fee_per_gas: header.execution.base_fee_per_gas, + block_hash: header.execution.block_hash, + transactions_root: header.execution.transactions_root, + withdrawals_root: header.execution.withdrawals_root) + return hash_tree_root(execution_header) + + ZERO_HASH + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/sync-protocol.md#is_valid_light_client_header +func is_valid_light_client_header*( + header: LightClientHeader, cfg: RuntimeConfig): bool = + let epoch = header.beacon.slot.epoch + + if epoch < cfg.EIP4844_FORK_EPOCH: + if header.execution.excess_data_gas != 0.u256: + return false + + if epoch < cfg.CAPELLA_FORK_EPOCH: + return + header.execution == default(ExecutionPayloadHeader) and + header.execution_branch == default(ExecutionBranch) + + is_valid_merkle_branch( + get_lc_execution_root(header, cfg), + header.execution_branch, + log2trunc(EXECUTION_PAYLOAD_INDEX), + get_subtree_index(EXECUTION_PAYLOAD_INDEX), + header.beacon.body_root) + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/fork.md#upgrade_lc_header_to_eip4844 +func upgrade_lc_header_to_eip4844*( + pre: capella.LightClientHeader): LightClientHeader = + LightClientHeader( + beacon: pre.beacon, + execution: ExecutionPayloadHeader( + parent_hash: pre.execution.parent_hash, + fee_recipient: pre.execution.fee_recipient, + state_root: pre.execution.state_root, + receipts_root: pre.execution.receipts_root, + logs_bloom: pre.execution.logs_bloom, + prev_randao: pre.execution.prev_randao, + block_number: pre.execution.block_number, + gas_limit: pre.execution.gas_limit, + gas_used: pre.execution.gas_used, + timestamp: pre.execution.timestamp, + extra_data: pre.execution.extra_data, + base_fee_per_gas: pre.execution.base_fee_per_gas, + block_hash: pre.execution.block_hash, + transactions_root: pre.execution.transactions_root, + withdrawals_root: pre.execution.withdrawals_root), + execution_branch: pre.execution_branch) + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/fork.md#upgrade_lc_bootstrap_to_eip4844 +func upgrade_lc_bootstrap_to_eip4844*( + pre: capella.LightClientBootstrap): LightClientBootstrap = + LightClientBootstrap( + header: upgrade_lc_header_to_eip4844(pre.header), + current_sync_committee: pre.current_sync_committee, + current_sync_committee_branch: pre.current_sync_committee_branch) + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/fork.md#upgrade_lc_update_to_eip4844 +func upgrade_lc_update_to_eip4844*( + pre: capella.LightClientUpdate): LightClientUpdate = + LightClientUpdate( + attested_header: upgrade_lc_header_to_eip4844(pre.attested_header), + next_sync_committee: pre.next_sync_committee, + next_sync_committee_branch: pre.next_sync_committee_branch, + finalized_header: upgrade_lc_header_to_eip4844(pre.finalized_header), + finality_branch: pre.finality_branch, + sync_aggregate: pre.sync_aggregate, + signature_slot: pre.signature_slot) + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/fork.md#upgrade_lc_finality_update_to_eip4844 +func upgrade_lc_finality_update_to_eip4844*( + pre: capella.LightClientFinalityUpdate): LightClientFinalityUpdate = + LightClientFinalityUpdate( + attested_header: upgrade_lc_header_to_eip4844(pre.attested_header), + finalized_header: upgrade_lc_header_to_eip4844(pre.finalized_header), + finality_branch: pre.finality_branch, + sync_aggregate: pre.sync_aggregate, + signature_slot: pre.signature_slot) + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/fork.md#upgrade_lc_optimistic_update_to_eip4844 +func upgrade_lc_optimistic_update_to_eip4844*( + pre: capella.LightClientOptimisticUpdate): LightClientOptimisticUpdate = + LightClientOptimisticUpdate( + attested_header: upgrade_lc_header_to_eip4844(pre.attested_header), + sync_aggregate: pre.sync_aggregate, + signature_slot: pre.signature_slot) + +func shortLog*(v: LightClientHeader): auto = + ( + beacon: shortLog(v.beacon), + execution: ( + block_hash: v.execution.block_hash, + block_number: v.execution.block_number) + ) + +func shortLog*(v: LightClientBootstrap): auto = + ( + header: shortLog(v.header) + ) + +func shortLog*(v: LightClientUpdate): auto = + ( + attested: shortLog(v.attested_header), + has_next_sync_committee: + v.next_sync_committee != default(typeof(v.next_sync_committee)), + finalized: shortLog(v.finalized_header), + num_active_participants: v.sync_aggregate.num_active_participants, + signature_slot: v.signature_slot + ) + +func shortLog*(v: LightClientFinalityUpdate): auto = + ( + attested: shortLog(v.attested_header), + finalized: shortLog(v.finalized_header), + num_active_participants: v.sync_aggregate.num_active_participants, + signature_slot: v.signature_slot + ) + +func shortLog*(v: LightClientOptimisticUpdate): auto = + ( + attested: shortLog(v.attested_header), + num_active_participants: v.sync_aggregate.num_active_participants, + signature_slot: v.signature_slot, + ) + +chronicles.formatIt LightClientBootstrap: shortLog(it) +chronicles.formatIt LightClientUpdate: shortLog(it) +chronicles.formatIt LightClientFinalityUpdate: shortLog(it) +chronicles.formatIt LightClientOptimisticUpdate: shortLog(it) + +template toFull*( + update: SomeLightClientUpdate): LightClientUpdate = + when update is LightClientUpdate: + update + elif update is SomeLightClientUpdateWithFinality: + LightClientUpdate( + attested_header: update.attested_header, + finalized_header: update.finalized_header, + finality_branch: update.finality_branch, + sync_aggregate: update.sync_aggregate, + signature_slot: update.signature_slot) + else: + LightClientUpdate( + attested_header: update.attested_header, + sync_aggregate: update.sync_aggregate, + signature_slot: update.signature_slot) + +template toFinality*( + update: SomeLightClientUpdate): LightClientFinalityUpdate = + when update is LightClientFinalityUpdate: + update + elif update is SomeLightClientUpdateWithFinality: + LightClientFinalityUpdate( + attested_header: update.attested_header, + finalized_header: update.finalized_header, + finality_branch: update.finality_branch, + sync_aggregate: update.sync_aggregate, + signature_slot: update.signature_slot) + else: + LightClientFinalityUpdate( + attested_header: update.attested_header, + sync_aggregate: update.sync_aggregate, + signature_slot: update.signature_slot) + +template toOptimistic*( + update: SomeLightClientUpdate): LightClientOptimisticUpdate = + when update is LightClientOptimisticUpdate: + update + else: + LightClientOptimisticUpdate( + attested_header: update.attested_header, + sync_aggregate: update.sync_aggregate, + signature_slot: update.signature_slot) + +func matches*[A, B: SomeLightClientUpdate](a: A, b: B): bool = + if a.attested_header != b.attested_header: + return false + when a is SomeLightClientUpdateWithSyncCommittee and + b is SomeLightClientUpdateWithSyncCommittee: + if a.next_sync_committee != b.next_sync_committee: + return false + if a.next_sync_committee_branch != b.next_sync_committee_branch: + return false + when a is SomeLightClientUpdateWithFinality and + b is SomeLightClientUpdateWithFinality: + if a.finalized_header != b.finalized_header: + return false + if a.finality_branch != b.finality_branch: + return false + if a.sync_aggregate != b.sync_aggregate: + return false + if a.signature_slot != b.signature_slot: + return false + true + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/fork.md#upgrade_lc_store_to_eip4844 +func upgrade_lc_store_to_eip4844*( + pre: capella.LightClientStore): LightClientStore = + let best_valid_update = + if pre.best_valid_update.isNone: + Opt.none(LightClientUpdate) + else: + Opt.some upgrade_lc_update_to_eip4844(pre.best_valid_update.get) + LightClientStore( + finalized_header: upgrade_lc_header_to_eip4844(pre.finalized_header), + current_sync_committee: pre.current_sync_committee, + next_sync_committee: pre.next_sync_committee, + best_valid_update: best_valid_update, + optimistic_header: upgrade_lc_header_to_eip4844(pre.optimistic_header), + previous_max_active_participants: pre.previous_max_active_participants, + current_max_active_participants: pre.current_max_active_participants) + template asSigned*( x: SigVerifiedSignedBeaconBlock | MsgTrustedSignedBeaconBlock | diff --git a/beacon_chain/spec/forks.nim b/beacon_chain/spec/forks.nim index c174153f79..9f4089eae3 100644 --- a/beacon_chain/spec/forks.nim +++ b/beacon_chain/spec/forks.nim @@ -927,8 +927,10 @@ func nextForkEpochAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Epoch = of BeaconStateFork.Phase0: cfg.ALTAIR_FORK_EPOCH func lcDataForkAtStateFork*(stateFork: BeaconStateFork): LightClientDataFork = - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella - if stateFork >= BeaconStateFork.Capella: + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 + if stateFork >= BeaconStateFork.EIP4844: + LightClientDataFork.EIP4844 + elif stateFork >= BeaconStateFork.Capella: LightClientDataFork.Capella elif stateFork >= BeaconStateFork.Altair: LightClientDataFork.Altair diff --git a/beacon_chain/spec/forks_light_client.nim b/beacon_chain/spec/forks_light_client.nim index 547c6954a0..1e31834e28 100644 --- a/beacon_chain/spec/forks_light_client.nim +++ b/beacon_chain/spec/forks_light_client.nim @@ -18,27 +18,33 @@ type LightClientDataFork* {.pure.} = enum # Append only, used in DB data! None = 0, # only use non-0 in DB to detect accidentally uninitialized data Altair = 1, - Capella = 2 + Capella = 2, + EIP4844 = 3 ForkyLightClientHeader* = altair.LightClientHeader | - capella.LightClientHeader + capella.LightClientHeader | + eip4844.LightClientHeader ForkyLightClientBootstrap* = altair.LightClientBootstrap | - capella.LightClientBootstrap + capella.LightClientBootstrap | + eip4844.LightClientBootstrap ForkyLightClientUpdate* = altair.LightClientUpdate | - capella.LightClientUpdate + capella.LightClientUpdate | + eip4844.LightClientUpdate ForkyLightClientFinalityUpdate* = altair.LightClientFinalityUpdate | - capella.LightClientFinalityUpdate + capella.LightClientFinalityUpdate | + eip4844.LightClientFinalityUpdate ForkyLightClientOptimisticUpdate* = altair.LightClientOptimisticUpdate | - capella.LightClientOptimisticUpdate + capella.LightClientOptimisticUpdate | + eip4844.LightClientOptimisticUpdate SomeForkyLightClientUpdateWithSyncCommittee* = ForkyLightClientUpdate @@ -58,7 +64,8 @@ type ForkyLightClientStore* = altair.LightClientStore | - capella.LightClientStore + capella.LightClientStore | + eip4844.LightClientStore ForkedLightClientHeader* = object case kind*: LightClientDataFork @@ -68,6 +75,8 @@ type altairData*: altair.LightClientHeader of LightClientDataFork.Capella: capellaData*: capella.LightClientHeader + of LightClientDataFork.EIP4844: + eip4844Data*: eip4844.LightClientHeader ForkedLightClientBootstrap* = object case kind*: LightClientDataFork @@ -77,6 +86,8 @@ type altairData*: altair.LightClientBootstrap of LightClientDataFork.Capella: capellaData*: capella.LightClientBootstrap + of LightClientDataFork.EIP4844: + eip4844Data*: eip4844.LightClientBootstrap ForkedLightClientUpdate* = object case kind*: LightClientDataFork @@ -86,6 +97,8 @@ type altairData*: altair.LightClientUpdate of LightClientDataFork.Capella: capellaData*: capella.LightClientUpdate + of LightClientDataFork.EIP4844: + eip4844Data*: eip4844.LightClientUpdate ForkedLightClientFinalityUpdate* = object case kind*: LightClientDataFork @@ -95,6 +108,8 @@ type altairData*: altair.LightClientFinalityUpdate of LightClientDataFork.Capella: capellaData*: capella.LightClientFinalityUpdate + of LightClientDataFork.EIP4844: + eip4844Data*: eip4844.LightClientFinalityUpdate ForkedLightClientOptimisticUpdate* = object case kind*: LightClientDataFork @@ -104,6 +119,8 @@ type altairData*: altair.LightClientOptimisticUpdate of LightClientDataFork.Capella: capellaData*: capella.LightClientOptimisticUpdate + of LightClientDataFork.EIP4844: + eip4844Data*: eip4844.LightClientOptimisticUpdate SomeForkedLightClientUpdateWithSyncCommittee* = ForkedLightClientUpdate @@ -129,11 +146,15 @@ type altairData*: altair.LightClientStore of LightClientDataFork.Capella: capellaData*: capella.LightClientStore + of LightClientDataFork.EIP4844: + eip4844Data*: eip4844.LightClientStore func lcDataForkAtEpoch*( cfg: RuntimeConfig, epoch: Epoch): LightClientDataFork = - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella - if epoch >= cfg.CAPELLA_FORK_EPOCH: + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 + if epoch >= cfg.EIP4844_FORK_EPOCH: + LightClientDataFork.EIP4844 + elif epoch >= cfg.CAPELLA_FORK_EPOCH: LightClientDataFork.Capella elif epoch >= cfg.ALTAIR_FORK_EPOCH: LightClientDataFork.Altair @@ -160,8 +181,20 @@ template kind*( capella.LightClientStore]): LightClientDataFork = LightClientDataFork.Capella +template kind*( + x: typedesc[ # `SomeLightClientObject` doesn't work here (Nim 1.6) + eip4844.LightClientHeader | + eip4844.LightClientBootstrap | + eip4844.LightClientUpdate | + eip4844.LightClientFinalityUpdate | + eip4844.LightClientOptimisticUpdate | + eip4844.LightClientStore]): LightClientDataFork = + LightClientDataFork.EIP4844 + template LightClientHeader*(kind: static LightClientDataFork): auto = - when kind == LightClientDataFork.Capella: + when kind == LightClientDataFork.EIP4844: + typedesc[eip4844.LightClientHeader] + elif kind == LightClientDataFork.Capella: typedesc[capella.LightClientHeader] elif kind == LightClientDataFork.Altair: typedesc[altair.LightClientHeader] @@ -169,7 +202,9 @@ template LightClientHeader*(kind: static LightClientDataFork): auto = static: raiseAssert "Unreachable" template LightClientBootstrap*(kind: static LightClientDataFork): auto = - when kind == LightClientDataFork.Capella: + when kind == LightClientDataFork.EIP4844: + typedesc[eip4844.LightClientBootstrap] + elif kind == LightClientDataFork.Capella: typedesc[capella.LightClientBootstrap] elif kind == LightClientDataFork.Altair: typedesc[altair.LightClientBootstrap] @@ -177,7 +212,9 @@ template LightClientBootstrap*(kind: static LightClientDataFork): auto = static: raiseAssert "Unreachable" template LightClientUpdate*(kind: static LightClientDataFork): auto = - when kind == LightClientDataFork.Capella: + when kind == LightClientDataFork.EIP4844: + typedesc[eip4844.LightClientUpdate] + elif kind == LightClientDataFork.Capella: typedesc[capella.LightClientUpdate] elif kind == LightClientDataFork.Altair: typedesc[altair.LightClientUpdate] @@ -185,7 +222,9 @@ template LightClientUpdate*(kind: static LightClientDataFork): auto = static: raiseAssert "Unreachable" template LightClientFinalityUpdate*(kind: static LightClientDataFork): auto = - when kind == LightClientDataFork.Capella: + when kind == LightClientDataFork.EIP4844: + typedesc[eip4844.LightClientFinalityUpdate] + elif kind == LightClientDataFork.Capella: typedesc[capella.LightClientFinalityUpdate] elif kind == LightClientDataFork.Altair: typedesc[altair.LightClientFinalityUpdate] @@ -193,7 +232,9 @@ template LightClientFinalityUpdate*(kind: static LightClientDataFork): auto = static: raiseAssert "Unreachable" template LightClientOptimisticUpdate*(kind: static LightClientDataFork): auto = - when kind == LightClientDataFork.Capella: + when kind == LightClientDataFork.EIP4844: + typedesc[eip4844.LightClientOptimisticUpdate] + elif kind == LightClientDataFork.Capella: typedesc[capella.LightClientOptimisticUpdate] elif kind == LightClientDataFork.Altair: typedesc[altair.LightClientOptimisticUpdate] @@ -201,7 +242,9 @@ template LightClientOptimisticUpdate*(kind: static LightClientDataFork): auto = static: raiseAssert "Unreachable" template LightClientStore*(kind: static LightClientDataFork): auto = - when kind == LightClientDataFork.Capella: + when kind == LightClientDataFork.EIP4844: + typedesc[eip4844.LightClientStore] + elif kind == LightClientDataFork.Capella: typedesc[capella.LightClientStore] elif kind == LightClientDataFork.Altair: typedesc[altair.LightClientStore] @@ -258,7 +301,10 @@ template Forked*(x: typedesc[ForkyLightClientStore]): auto = template withAll*( x: typedesc[LightClientDataFork], body: untyped): untyped = - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 + block: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + body block: const lcDataFork {.inject, used.} = LightClientDataFork.Capella body @@ -272,6 +318,9 @@ template withAll*( template withLcDataFork*( x: LightClientDataFork, body: untyped): untyped = case x + of LightClientDataFork.EIP4844: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + body of LightClientDataFork.Capella: const lcDataFork {.inject, used.} = LightClientDataFork.Capella body @@ -285,6 +334,10 @@ template withLcDataFork*( template withForkyHeader*( x: ForkedLightClientHeader, body: untyped): untyped = case x.kind + of LightClientDataFork.EIP4844: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + template forkyHeader: untyped {.inject, used.} = x.eip4844Data + body of LightClientDataFork.Capella: const lcDataFork {.inject, used.} = LightClientDataFork.Capella template forkyHeader: untyped {.inject, used.} = x.capellaData @@ -300,6 +353,10 @@ template withForkyHeader*( template withForkyBootstrap*( x: ForkedLightClientBootstrap, body: untyped): untyped = case x.kind + of LightClientDataFork.EIP4844: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + template forkyBootstrap: untyped {.inject, used.} = x.eip4844Data + body of LightClientDataFork.Capella: const lcDataFork {.inject, used.} = LightClientDataFork.Capella template forkyBootstrap: untyped {.inject, used.} = x.capellaData @@ -315,6 +372,10 @@ template withForkyBootstrap*( template withForkyUpdate*( x: ForkedLightClientUpdate, body: untyped): untyped = case x.kind + of LightClientDataFork.EIP4844: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + template forkyUpdate: untyped {.inject, used.} = x.eip4844Data + body of LightClientDataFork.Capella: const lcDataFork {.inject, used.} = LightClientDataFork.Capella template forkyUpdate: untyped {.inject, used.} = x.capellaData @@ -330,6 +391,10 @@ template withForkyUpdate*( template withForkyFinalityUpdate*( x: ForkedLightClientFinalityUpdate, body: untyped): untyped = case x.kind + of LightClientDataFork.EIP4844: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + template forkyFinalityUpdate: untyped {.inject, used.} = x.eip4844Data + body of LightClientDataFork.Capella: const lcDataFork {.inject, used.} = LightClientDataFork.Capella template forkyFinalityUpdate: untyped {.inject, used.} = x.capellaData @@ -345,6 +410,10 @@ template withForkyFinalityUpdate*( template withForkyOptimisticUpdate*( x: ForkedLightClientOptimisticUpdate, body: untyped): untyped = case x.kind + of LightClientDataFork.EIP4844: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + template forkyOptimisticUpdate: untyped {.inject, used.} = x.eip4844Data + body of LightClientDataFork.Capella: const lcDataFork {.inject, used.} = LightClientDataFork.Capella template forkyOptimisticUpdate: untyped {.inject, used.} = x.capellaData @@ -360,6 +429,10 @@ template withForkyOptimisticUpdate*( template withForkyObject*( x: SomeForkedLightClientObject, body: untyped): untyped = case x.kind + of LightClientDataFork.EIP4844: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + template forkyObject: untyped {.inject, used.} = x.eip4844Data + body of LightClientDataFork.Capella: const lcDataFork {.inject, used.} = LightClientDataFork.Capella template forkyObject: untyped {.inject, used.} = x.capellaData @@ -375,6 +448,10 @@ template withForkyObject*( template withForkyStore*( x: ForkedLightClientStore, body: untyped): untyped = case x.kind + of LightClientDataFork.EIP4844: + const lcDataFork {.inject, used.} = LightClientDataFork.EIP4844 + template forkyStore: untyped {.inject, used.} = x.eip4844Data + body of LightClientDataFork.Capella: const lcDataFork {.inject, used.} = LightClientDataFork.Capella template forkyStore: untyped {.inject, used.} = x.capellaData @@ -444,7 +521,9 @@ template forky*( SomeForkedLightClientObject | ForkedLightClientStore, kind: static LightClientDataFork): untyped = - when kind == LightClientDataFork.Capella: + when kind == LightClientDataFork.EIP4844: + x.eip4844Data + elif kind == LightClientDataFork.Capella: x.capellaData elif kind == LightClientDataFork.Altair: x.altairData @@ -475,7 +554,15 @@ func migrateToDataFork*( capellaData: upgrade_lc_header_to_capella( x.forky(LightClientDataFork.Altair))) - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella + # Upgrade to EIP4844 + when newKind >= LightClientDataFork.EIP4844: + if x.kind == LightClientDataFork.Capella: + x = ForkedLightClientHeader( + kind: LightClientDataFork.EIP4844, + eip4844Data: upgrade_lc_header_to_eip4844( + x.forky(LightClientDataFork.Capella))) + + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 doAssert x.kind == newKind func migrateToDataFork*( @@ -502,7 +589,15 @@ func migrateToDataFork*( capellaData: upgrade_lc_bootstrap_to_capella( x.forky(LightClientDataFork.Altair))) - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella + # Upgrade to EIP4844 + when newKind >= LightClientDataFork.EIP4844: + if x.kind == LightClientDataFork.Capella: + x = ForkedLightClientBootstrap( + kind: LightClientDataFork.EIP4844, + eip4844Data: upgrade_lc_bootstrap_to_eip4844( + x.forky(LightClientDataFork.Capella))) + + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 doAssert x.kind == newKind func migrateToDataFork*( @@ -529,7 +624,15 @@ func migrateToDataFork*( capellaData: upgrade_lc_update_to_capella( x.forky(LightClientDataFork.Altair))) - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella + # Upgrade to EIP4844 + when newKind >= LightClientDataFork.EIP4844: + if x.kind == LightClientDataFork.Capella: + x = ForkedLightClientUpdate( + kind: LightClientDataFork.EIP4844, + eip4844Data: upgrade_lc_update_to_eip4844( + x.forky(LightClientDataFork.Capella))) + + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 doAssert x.kind == newKind func migrateToDataFork*( @@ -556,7 +659,15 @@ func migrateToDataFork*( capellaData: upgrade_lc_finality_update_to_capella( x.forky(LightClientDataFork.Altair))) - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella + # Upgrade to EIP4844 + when newKind >= LightClientDataFork.EIP4844: + if x.kind == LightClientDataFork.Capella: + x = ForkedLightClientFinalityUpdate( + kind: LightClientDataFork.EIP4844, + eip4844Data: upgrade_lc_finality_update_to_eip4844( + x.forky(LightClientDataFork.Capella))) + + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 doAssert x.kind == newKind func migrateToDataFork*( @@ -583,7 +694,15 @@ func migrateToDataFork*( capellaData: upgrade_lc_optimistic_update_to_capella( x.forky(LightClientDataFork.Altair))) - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella + # Upgrade to EIP4844 + when newKind >= LightClientDataFork.EIP4844: + if x.kind == LightClientDataFork.Capella: + x = ForkedLightClientOptimisticUpdate( + kind: LightClientDataFork.EIP4844, + eip4844Data: upgrade_lc_optimistic_update_to_eip4844( + x.forky(LightClientDataFork.Capella))) + + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 doAssert x.kind == newKind func migrateToDataFork*( @@ -610,7 +729,15 @@ func migrateToDataFork*( capellaData: upgrade_lc_store_to_capella( x.forky(LightClientDataFork.Altair))) - static: doAssert LightClientDataFork.high == LightClientDataFork.Capella + # Upgrade to EIP4844 + when newKind >= LightClientDataFork.EIP4844: + if x.kind == LightClientDataFork.Capella: + x = ForkedLightClientStore( + kind: LightClientDataFork.EIP4844, + eip4844Data: upgrade_lc_store_to_eip4844( + x.forky(LightClientDataFork.Capella))) + + static: doAssert LightClientDataFork.high == LightClientDataFork.EIP4844 doAssert x.kind == newKind func migratingToDataFork*[ @@ -651,8 +778,7 @@ func toCapellaLightClientHeader( func toCapellaLightClientHeader( blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6) - capella.SignedBeaconBlock | capella.TrustedSignedBeaconBlock | - eip4844.SignedBeaconBlock | eip4844.TrustedSignedBeaconBlock + capella.SignedBeaconBlock | capella.TrustedSignedBeaconBlock ): capella.LightClientHeader = template payload: untyped = blck.message.body.execution_payload capella.LightClientHeader( @@ -676,6 +802,75 @@ func toCapellaLightClientHeader( execution_branch: blck.message.body.build_proof( capella.EXECUTION_PAYLOAD_INDEX).get) +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/eip4844/light-client/full-node.md#block_to_light_client_header +func toEIP4844LightClientHeader( + blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6) + phase0.SignedBeaconBlock | phase0.TrustedSignedBeaconBlock | + altair.SignedBeaconBlock | altair.TrustedSignedBeaconBlock | + bellatrix.SignedBeaconBlock | bellatrix.TrustedSignedBeaconBlock +): eip4844.LightClientHeader = + # Note that during fork transitions, `finalized_header` may still + # point to earlier forks. While Bellatrix blocks also contain an + # `ExecutionPayload` (minus `withdrawals_root`), it was not included + # in the corresponding light client data. To ensure compatibility + # with legacy data going through `upgrade_lc_header_to_capella`, + # leave out execution data. + eip4844.LightClientHeader( + beacon: blck.message.toBeaconBlockHeader()) + +func toEIP4844LightClientHeader( + blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6) + capella.SignedBeaconBlock | capella.TrustedSignedBeaconBlock +): eip4844.LightClientHeader = + template payload: untyped = blck.message.body.execution_payload + eip4844.LightClientHeader( + beacon: blck.message.toBeaconBlockHeader(), + execution: eip4844.ExecutionPayloadHeader( + parent_hash: payload.parent_hash, + fee_recipient: payload.fee_recipient, + state_root: payload.state_root, + receipts_root: payload.receipts_root, + logs_bloom: payload.logs_bloom, + prev_randao: payload.prev_randao, + block_number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + extra_data: payload.extra_data, + base_fee_per_gas: payload.base_fee_per_gas, + block_hash: payload.block_hash, + transactions_root: hash_tree_root(payload.transactions), + withdrawals_root: hash_tree_root(payload.withdrawals)), + execution_branch: blck.message.body.build_proof( + capella.EXECUTION_PAYLOAD_INDEX).get) + +func toEIP4844LightClientHeader( + blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6) + eip4844.SignedBeaconBlock | eip4844.TrustedSignedBeaconBlock +): eip4844.LightClientHeader = + template payload: untyped = blck.message.body.execution_payload + eip4844.LightClientHeader( + beacon: blck.message.toBeaconBlockHeader(), + execution: eip4844.ExecutionPayloadHeader( + parent_hash: payload.parent_hash, + fee_recipient: payload.fee_recipient, + state_root: payload.state_root, + receipts_root: payload.receipts_root, + logs_bloom: payload.logs_bloom, + prev_randao: payload.prev_randao, + block_number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + extra_data: payload.extra_data, + base_fee_per_gas: payload.base_fee_per_gas, + excess_data_gas: payload.excess_data_gas, + block_hash: payload.block_hash, + transactions_root: hash_tree_root(payload.transactions), + withdrawals_root: hash_tree_root(payload.withdrawals)), + execution_branch: blck.message.body.build_proof( + capella.EXECUTION_PAYLOAD_INDEX).get) + func toLightClientHeader*( blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6) phase0.SignedBeaconBlock | phase0.TrustedSignedBeaconBlock | @@ -684,7 +879,9 @@ func toLightClientHeader*( capella.SignedBeaconBlock | capella.TrustedSignedBeaconBlock | eip4844.SignedBeaconBlock | eip4844.TrustedSignedBeaconBlock, kind: static LightClientDataFork): auto = - when kind == LightClientDataFork.Capella: + when kind == LightClientDataFork.EIP4844: + blck.toEIP4844LightClientHeader() + elif kind == LightClientDataFork.Capella: blck.toCapellaLightClientHeader() elif kind == LightClientDataFork.Altair: blck.toAltairLightClientHeader() diff --git a/tests/consensus_spec/eip4844/test_fixture_ssz_consensus_objects.nim b/tests/consensus_spec/eip4844/test_fixture_ssz_consensus_objects.nim index e5a03adbd5..af7c4c56a3 100644 --- a/tests/consensus_spec/eip4844/test_fixture_ssz_consensus_objects.nim +++ b/tests/consensus_spec/eip4844/test_fixture_ssz_consensus_objects.nim @@ -126,15 +126,15 @@ suite "EF - EIP4844 - SSZ consensus objects " & preset(): of "HistoricalSummary": checkSSZ(HistoricalSummary, path, hash) of "IndexedAttestation": checkSSZ(IndexedAttestation, path, hash) of "LightClientBootstrap": - discard # checkSSZ(capella.LightClientBootstrap, path, hash) + discard # checkSSZ(eip4844.LightClientBootstrap, path, hash) of "LightClientHeader": - discard # checkSSZ(capella.LightClientHeader, path, hash) + discard # checkSSZ(eip4844.LightClientHeader, path, hash) of "LightClientUpdate": - discard # checkSSZ(capella.LightClientUpdate, path, hash) + discard # checkSSZ(eip4844.LightClientUpdate, path, hash) of "LightClientFinalityUpdate": - discard # checkSSZ(capella.LightClientFinalityUpdate, path, hash) + discard # checkSSZ(eip4844.LightClientFinalityUpdate, path, hash) of "LightClientOptimisticUpdate": - discard # checkSSZ(capella.LightClientOptimisticUpdate, path, hash) + discard # checkSSZ(eip4844.LightClientOptimisticUpdate, path, hash) of "PendingAttestation": checkSSZ(PendingAttestation, path, hash) of "PowBlock": checkSSZ(PowBlock, path, hash) of "ProposerSlashing": checkSSZ(ProposerSlashing, path, hash)