From d09bf3b587bc4f0c91b8e2f58884665a0ae80e34 Mon Sep 17 00:00:00 2001 From: tersec Date: Sat, 24 Feb 2024 13:44:15 +0000 Subject: [PATCH] initial Electra support skeleton (#5946) --- beacon_chain/beacon_chain_db.nim | 32 ++-- beacon_chain/beacon_node_light_client.nim | 2 + .../attestation_pool.nim | 3 +- .../blob_quarantine.nim | 8 +- .../consensus_object_pools/block_dag.nim | 3 +- .../block_pools_types.nim | 7 +- .../block_quarantine.nim | 9 +- .../consensus_object_pools/blockchain_dag.nim | 24 ++- .../consensus_manager.nim | 11 +- beacon_chain/el/el_manager.nim | 40 ++++- beacon_chain/era_db.nim | 2 + .../gossip_processing/block_processor.nim | 12 +- .../gossip_processing/gossip_validation.nim | 3 +- beacon_chain/networking/eth2_network.nim | 8 +- beacon_chain/networking/network_metadata.nim | 6 +- beacon_chain/nimbus_beacon_node.nim | 12 +- beacon_chain/nimbus_signing_node.nim | 3 + beacon_chain/rpc/rest_beacon_api.nim | 25 ++- beacon_chain/rpc/rest_validator_api.nim | 25 ++- beacon_chain/spec/beaconstate.nim | 42 +++-- beacon_chain/spec/datatypes/deneb.nim | 1 + .../eth2_apis/eth2_rest_serialization.nim | 113 ++++++++++++-- beacon_chain/spec/eth2_apis/rest_types.nim | 22 ++- beacon_chain/spec/forks.nim | 144 +++++++++++++++--- beacon_chain/spec/forks_light_client.nim | 11 +- beacon_chain/spec/helpers.nim | 23 ++- beacon_chain/spec/state_transition.nim | 16 +- beacon_chain/spec/state_transition_block.nim | 101 +++++++++++- beacon_chain/spec/state_transition_epoch.nim | 35 +++-- beacon_chain/validator_client/api.nim | 12 ++ .../validator_client/block_service.nim | 16 +- beacon_chain/validators/beacon_validators.nim | 5 +- beacon_chain/validators/validator_pool.nim | 9 ++ ncli/ncli.nim | 1 + ncli/ncli_common.nim | 2 +- ncli/ncli_db.nim | 17 ++- research/block_sim.nim | 72 ++++++++- research/wss_sim.nim | 9 ++ tests/consensus_spec/fixtures_utils.nim | 6 + .../test_fixture_sanity_blocks.nim | 3 +- tests/test_light_client.nim | 4 +- tests/test_light_client_processor.nim | 4 +- tests/test_signing_node.nim | 43 ++++-- tests/teststateutil.nim | 4 +- 44 files changed, 792 insertions(+), 158 deletions(-) diff --git a/beacon_chain/beacon_chain_db.nim b/beacon_chain/beacon_chain_db.nim index d31417398..71d86e05e 100644 --- a/beacon_chain/beacon_chain_db.nim +++ b/beacon_chain/beacon_chain_db.nim @@ -508,7 +508,8 @@ proc new*(T: type BeaconChainDB, kvStore db.openKvStore("altair_blocks").expectDb(), kvStore db.openKvStore("bellatrix_blocks").expectDb(), kvStore db.openKvStore("capella_blocks").expectDb(), - kvStore db.openKvStore("deneb_blocks").expectDb()] + kvStore db.openKvStore("deneb_blocks").expectDb(), + kvStore db.openKvStore("electra_blocks").expectDb()] stateRoots = kvStore db.openKvStore("state_roots", true).expectDb() @@ -517,7 +518,8 @@ proc new*(T: type BeaconChainDB, kvStore db.openKvStore("altair_state_no_validators").expectDb(), kvStore db.openKvStore("bellatrix_state_no_validators").expectDb(), kvStore db.openKvStore("capella_state_no_validator_pubkeys").expectDb(), - kvStore db.openKvStore("deneb_state_no_validator_pubkeys").expectDb()] + kvStore db.openKvStore("deneb_state_no_validator_pubkeys").expectDb(), + kvStore db.openKvStore("electra_state_no_validator_pubkeys").expectDb()] stateDiffs = kvStore db.openKvStore("state_diffs").expectDb() summaries = kvStore db.openKvStore("beacon_block_summaries", true).expectDb() @@ -789,7 +791,8 @@ proc putBlock*( proc putBlock*( db: BeaconChainDB, value: bellatrix.TrustedSignedBeaconBlock | - capella.TrustedSignedBeaconBlock | deneb.TrustedSignedBeaconBlock) = + capella.TrustedSignedBeaconBlock | deneb.TrustedSignedBeaconBlock | + electra.TrustedSignedBeaconBlock) = db.withManyWrites: db.blocks[type(value).kind].putSZSSZ(value.root.data, value) db.putBeaconBlockSummary(value.root, value.message.toBeaconBlockSummary()) @@ -839,6 +842,10 @@ template toBeaconStateNoImmutableValidators(state: deneb.BeaconState): DenebBeaconStateNoImmutableValidators = isomorphicCast[DenebBeaconStateNoImmutableValidators](state) +template toBeaconStateNoImmutableValidators(state: electra.BeaconState): + ElectraBeaconStateNoImmutableValidators = + isomorphicCast[ElectraBeaconStateNoImmutableValidators](state) + proc putState*( db: BeaconChainDB, key: Eth2Digest, value: phase0.BeaconState | altair.BeaconState) = @@ -848,7 +855,8 @@ proc putState*( proc putState*( db: BeaconChainDB, key: Eth2Digest, - value: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState) = + value: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | + electra.BeaconState) = db.updateImmutableValidators(value.validators.asSeq()) db.statesNoVal[type(value).kind].putSZSSZ( key.data, toBeaconStateNoImmutableValidators(value)) @@ -976,7 +984,7 @@ proc getBlock*( proc getBlock*[ X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | - deneb.TrustedSignedBeaconBlock]( + deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock]( db: BeaconChainDB, key: Eth2Digest, T: type X): Opt[T] = # We only store blocks that we trust in the database @@ -1031,7 +1039,7 @@ proc getBlockSSZ*( proc getBlockSSZ*[ X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | - deneb.TrustedSignedBeaconBlock]( + deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock]( db: BeaconChainDB, key: Eth2Digest, data: var seq[byte], T: type X): bool = let dataPtr = addr data # Short-lived var success = true @@ -1080,7 +1088,7 @@ proc getBlockSZ*( proc getBlockSZ*[ X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | - deneb.TrustedSignedBeaconBlock]( + deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock]( db: BeaconChainDB, key: Eth2Digest, data: var seq[byte], T: type X): bool = let dataPtr = addr data # Short-lived func decode(data: openArray[byte]) = @@ -1178,7 +1186,8 @@ proc getStateOnlyMutableValidators( proc getStateOnlyMutableValidators( immutableValidators: openArray[ImmutableValidatorData2], store: KvStoreRef, key: openArray[byte], - output: var (capella.BeaconState | deneb.BeaconState), + output: var (capella.BeaconState | deneb.BeaconState | + electra.BeaconState), rollback: RollbackProc): bool = ## Load state into `output` - BeaconState is large so we want to avoid ## re-allocating it if possible @@ -1263,7 +1272,8 @@ proc getState*( proc getState*( db: BeaconChainDB, key: Eth2Digest, output: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState), + capella.BeaconState | deneb.BeaconState | + electra.BeaconState), rollback: RollbackProc): bool = ## Load state into `output` - BeaconState is large so we want to avoid ## re-allocating it if possible @@ -1483,7 +1493,7 @@ iterator getAncestorSummaries*(db: BeaconChainDB, root: Eth2Digest): # Backwards compat for reading old databases, or those that for whatever # reason lost a summary along the way.. - static: doAssert ConsensusFork.high == ConsensusFork.Deneb + static: doAssert ConsensusFork.high == ConsensusFork.Electra while true: if db.v0.backend.getSnappySSZ( subkey(BeaconBlockSummary, res.root), res.summary) == GetResult.found: @@ -1498,6 +1508,8 @@ iterator getAncestorSummaries*(db: BeaconChainDB, root: Eth2Digest): res.summary = blck.get().message.toBeaconBlockSummary() elif (let blck = db.getBlock(res.root, deneb.TrustedSignedBeaconBlock); blck.isSome()): res.summary = blck.get().message.toBeaconBlockSummary() + elif (let blck = db.getBlock(res.root, electra.TrustedSignedBeaconBlock); blck.isSome()): + res.summary = blck.get().message.toBeaconBlockSummary() else: break diff --git a/beacon_chain/beacon_node_light_client.nim b/beacon_chain/beacon_node_light_client.nim index 3f0af21e2..66d78735e 100644 --- a/beacon_chain/beacon_node_light_client.nim +++ b/beacon_chain/beacon_node_light_client.nim @@ -77,6 +77,8 @@ proc initLightClient*( case node.dag.cfg.consensusForkAtEpoch( forkyBlck.message.slot.epoch) + of ConsensusFork.Electra: + debugRaiseAssert "initLightClient" of ConsensusFork.Deneb: callForkchoiceUpdated(PayloadAttributesV3) of ConsensusFork.Capella: diff --git a/beacon_chain/consensus_object_pools/attestation_pool.nim b/beacon_chain/consensus_object_pools/attestation_pool.nim index 4278b3894..e7b9d93cd 100644 --- a/beacon_chain/consensus_object_pools/attestation_pool.nim +++ b/beacon_chain/consensus_object_pools/attestation_pool.nim @@ -483,7 +483,8 @@ func init( func init( T: type AttestationCache, state: altair.HashedBeaconState | bellatrix.HashedBeaconState | - capella.HashedBeaconState | deneb.HashedBeaconState, + capella.HashedBeaconState | deneb.HashedBeaconState | + electra.HashedBeaconState, cache: var StateCache): T = # Load attestations that are scheduled for being given rewards for let diff --git a/beacon_chain/consensus_object_pools/blob_quarantine.nim b/beacon_chain/consensus_object_pools/blob_quarantine.nim index 1178f5edd..ffc35c99b 100644 --- a/beacon_chain/consensus_object_pools/blob_quarantine.nim +++ b/beacon_chain/consensus_object_pools/blob_quarantine.nim @@ -69,7 +69,8 @@ func hasBlob*( func popBlobs*( quarantine: var BlobQuarantine, digest: Eth2Digest, - blck: deneb.SignedBeaconBlock): seq[ref BlobSidecar] = + blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): + seq[ref BlobSidecar] = var r: seq[ref BlobSidecar] = @[] for idx, kzg_commitment in blck.message.body.blob_kzg_commitments: var b: ref BlobSidecar @@ -77,8 +78,9 @@ func popBlobs*( r.add(b) r -func hasBlobs*(quarantine: BlobQuarantine, blck: deneb.SignedBeaconBlock): - bool = +func hasBlobs*( + quarantine: BlobQuarantine, + blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): bool = for idx, kzg_commitment in blck.message.body.blob_kzg_commitments: if (blck.root, BlobIndex idx, kzg_commitment) notin quarantine.blobs: return false diff --git a/beacon_chain/consensus_object_pools/block_dag.nim b/beacon_chain/consensus_object_pools/block_dag.nim index 6dd2bc0eb..307a02f12 100644 --- a/beacon_chain/consensus_object_pools/block_dag.nim +++ b/beacon_chain/consensus_object_pools/block_dag.nim @@ -73,7 +73,8 @@ func init*( T: type BlockRef, root: Eth2Digest, executionValid: bool, blck: bellatrix.SomeBeaconBlock | bellatrix.TrustedBeaconBlock | capella.SomeBeaconBlock | capella.TrustedBeaconBlock | - deneb.SomeBeaconBlock | deneb.TrustedBeaconBlock): BlockRef = + deneb.SomeBeaconBlock | deneb.TrustedBeaconBlock | + electra.SomeBeaconBlock | electra.TrustedBeaconBlock): BlockRef = BlockRef.init( root, Opt.some Eth2Digest(blck.body.execution_payload.block_hash), executionValid = diff --git a/beacon_chain/consensus_object_pools/block_pools_types.nim b/beacon_chain/consensus_object_pools/block_pools_types.nim index 0a450325a..e092b446a 100644 --- a/beacon_chain/consensus_object_pools/block_pools_types.nim +++ b/beacon_chain/consensus_object_pools/block_pools_types.nim @@ -293,10 +293,11 @@ type OnBellatrixBlockAdded* = OnBlockAdded[bellatrix.TrustedSignedBeaconBlock] OnCapellaBlockAdded* = OnBlockAdded[capella.TrustedSignedBeaconBlock] OnDenebBlockAdded* = OnBlockAdded[deneb.TrustedSignedBeaconBlock] + OnElectraBlockAdded* = OnBlockAdded[electra.TrustedSignedBeaconBlock] OnForkyBlockAdded* = OnPhase0BlockAdded | OnAltairBlockAdded | OnBellatrixBlockAdded | - OnCapellaBlockAdded | OnDenebBlockAdded + OnCapellaBlockAdded | OnDenebBlockAdded | OnElectraBlockAdded HeadChangeInfoObject* = object slot*: Slot @@ -328,7 +329,9 @@ type optimistic* {.serializedFieldName: "execution_optimistic".}: Option[bool] template OnBlockAddedCallback*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Deneb: + when kind == ConsensusFork.Electra: + typedesc[OnElectraBlockAdded] + elif kind == ConsensusFork.Deneb: typedesc[OnDenebBlockAdded] elif kind == ConsensusFork.Capella: typedesc[OnCapellaBlockAdded] diff --git a/beacon_chain/consensus_object_pools/block_quarantine.nim b/beacon_chain/consensus_object_pools/block_quarantine.nim index d6da6d24b..1efe4ddf7 100644 --- a/beacon_chain/consensus_object_pools/block_quarantine.nim +++ b/beacon_chain/consensus_object_pools/block_quarantine.nim @@ -294,8 +294,7 @@ iterator pop*(quarantine: var Quarantine, root: Eth2Digest): proc addBlobless*( quarantine: var Quarantine, finalizedSlot: Slot, - signedBlock: deneb.SignedBeaconBlock): bool = - + signedBlock: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): bool = if not isViable(finalizedSlot, signedBlock.message.slot): quarantine.addUnviable(signedBlock.root) return false @@ -306,8 +305,10 @@ proc addBlobless*( return true debug "block quarantine: Adding blobless", blck = shortLog(signedBlock) - quarantine.blobless[signedBlock.root] = signedBlock - quarantine.missing.del(signedBlock.root) + debugRaiseAssert "addBlobless; needs consideration how to handle deneb and electra" + when not (signedBlock is electra.SignedBeaconBlock): + quarantine.blobless[signedBlock.root] = signedBlock + quarantine.missing.del(signedBlock.root) true func popBlobless*(quarantine: var Quarantine, root: Eth2Digest): diff --git a/beacon_chain/consensus_object_pools/blockchain_dag.nim b/beacon_chain/consensus_object_pools/blockchain_dag.nim index 5ad19ad52..05f581022 100644 --- a/beacon_chain/consensus_object_pools/blockchain_dag.nim +++ b/beacon_chain/consensus_object_pools/blockchain_dag.nim @@ -268,8 +268,11 @@ proc getForkedBlock*(db: BeaconChainDB, root: Eth2Digest): Opt[ForkedTrustedSignedBeaconBlock] = # When we only have a digest, we don't know which fork it's from so we try # them one by one - this should be used sparingly - static: doAssert high(ConsensusFork) == ConsensusFork.Deneb - if (let blck = db.getBlock(root, deneb.TrustedSignedBeaconBlock); + static: doAssert high(ConsensusFork) == ConsensusFork.Electra + if (let blck = db.getBlock(root, electra.TrustedSignedBeaconBlock); + blck.isSome()): + ok(ForkedTrustedSignedBeaconBlock.init(blck.get())) + elif (let blck = db.getBlock(root, deneb.TrustedSignedBeaconBlock); blck.isSome()): ok(ForkedTrustedSignedBeaconBlock.init(blck.get())) elif (let blck = db.getBlock(root, capella.TrustedSignedBeaconBlock); @@ -1002,6 +1005,12 @@ proc applyBlock( state_transition( dag.cfg, state, data, cache, info, dag.updateFlags + {slotProcessed}, noRollback) + of ConsensusFork.Electra: + let data = getBlock(dag, bid, electra.TrustedSignedBeaconBlock).valueOr: + return err("Block load failed") + state_transition( + dag.cfg, state, data, cache, info, + dag.updateFlags + {slotProcessed}, noRollback) proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB, validatorMonitor: ref ValidatorMonitor, updateFlags: UpdateFlags, @@ -1155,11 +1164,12 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB, let configFork = case dag.headState.kind - of ConsensusFork.Phase0: genesisFork(cfg) - of ConsensusFork.Altair: altairFork(cfg) + of ConsensusFork.Phase0: genesisFork(cfg) + of ConsensusFork.Altair: altairFork(cfg) of ConsensusFork.Bellatrix: bellatrixFork(cfg) - of ConsensusFork.Capella: capellaFork(cfg) - of ConsensusFork.Deneb: denebFork(cfg) + of ConsensusFork.Capella: capellaFork(cfg) + of ConsensusFork.Deneb: denebFork(cfg) + of ConsensusFork.Electra: electraFork(cfg) stateFork = getStateField(dag.headState, fork) # Here, we check only the `current_version` field because the spec @@ -2398,6 +2408,8 @@ proc updateHead*( of ConsensusFork.Deneb: if dag.vanityLogs.onUpgradeToDeneb != nil: dag.vanityLogs.onUpgradeToDeneb() + of ConsensusFork.Electra: + debugRaiseAssert "updateHead" if dag.vanityLogs.onKnownBlsToExecutionChange != nil and checkBlsToExecutionChanges( diff --git a/beacon_chain/consensus_object_pools/consensus_manager.nim b/beacon_chain/consensus_object_pools/consensus_manager.nim index acb63c14a..909185aee 100644 --- a/beacon_chain/consensus_object_pools/consensus_manager.nim +++ b/beacon_chain/consensus_object_pools/consensus_manager.nim @@ -379,7 +379,16 @@ proc runProposalForkchoiceUpdated*( payloadAttributes = some fcPayloadAttributes) debug "Fork-choice updated for proposal", status - static: doAssert high(ConsensusFork) == ConsensusFork.Deneb + static: doAssert high(ConsensusFork) == ConsensusFork.Electra + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "runProposalForkchoiceUpdated, probably will be a new payload attributes type here" + callForkchoiceUpdated(PayloadAttributesV3( + timestamp: Quantity timestamp, + prevRandao: FixedBytes[32] randomData, + suggestedFeeRecipient: feeRecipient, + withdrawals: + toEngineWithdrawals get_expected_withdrawals(forkyState.data), + parentBeaconBlockRoot: beaconHead.blck.bid.root.asBlockHash)) when consensusFork >= ConsensusFork.Deneb: callForkchoiceUpdated(PayloadAttributesV3( timestamp: Quantity timestamp, diff --git a/beacon_chain/el/el_manager.nim b/beacon_chain/el/el_manager.nim index 093465fcb..295a50739 100644 --- a/beacon_chain/el/el_manager.nim +++ b/beacon_chain/el/el_manager.nim @@ -561,6 +561,32 @@ func asEngineExecutionPayload*(executionPayload: deneb.ExecutionPayload): blobGasUsed: Quantity(executionPayload.blob_gas_used), excessBlobGas: Quantity(executionPayload.excess_blob_gas)) +func asEngineExecutionPayload*(executionPayload: electra.ExecutionPayload): + ExecutionPayloadV3 = + debugRaiseAssert "asEngineExecutionPayload for electra.ExecutionPayload probably won't use ExecutionPayloadV3" + template getTypedTransaction(tt: bellatrix.Transaction): TypedTransaction = + TypedTransaction(tt.distinctBase) + + engine_api.ExecutionPayloadV3( + parentHash: executionPayload.parent_hash.asBlockHash, + feeRecipient: Address(executionPayload.fee_recipient.data), + stateRoot: executionPayload.state_root.asBlockHash, + receiptsRoot: executionPayload.receipts_root.asBlockHash, + logsBloom: + FixedBytes[BYTES_PER_LOGS_BLOOM](executionPayload.logs_bloom.data), + prevRandao: executionPayload.prev_randao.asBlockHash, + blockNumber: Quantity(executionPayload.block_number), + gasLimit: Quantity(executionPayload.gas_limit), + gasUsed: Quantity(executionPayload.gas_used), + timestamp: Quantity(executionPayload.timestamp), + extraData: DynamicBytes[0, MAX_EXTRA_DATA_BYTES](executionPayload.extra_data), + baseFeePerGas: executionPayload.base_fee_per_gas, + blockHash: executionPayload.block_hash.asBlockHash, + transactions: mapIt(executionPayload.transactions, it.getTypedTransaction), + withdrawals: mapIt(executionPayload.withdrawals, it.asEngineWithdrawal), + blobGasUsed: Quantity(executionPayload.blob_gas_used), + excessBlobGas: Quantity(executionPayload.excess_blob_gas)) + func isConnected(connection: ELConnection): bool = connection.web3.isSome @@ -752,6 +778,10 @@ template EngineApiResponseType*(T: type capella.ExecutionPayloadForSigning): typ template EngineApiResponseType*(T: type deneb.ExecutionPayloadForSigning): type = engine_api.GetPayloadV3Response +template EngineApiResponseType*(T: type electra.ExecutionPayloadForSigning): type = + debugRaiseAssert "EngineApiResponseType electra.ExecutionPayloadForSigning; presumably will be a GetPayloadV4Response" + engine_api.GetPayloadV3Response + template toEngineWithdrawals*(withdrawals: seq[capella.Withdrawal]): seq[WithdrawalV1] = mapIt(withdrawals, toEngineWithdrawal(it)) @@ -850,9 +880,15 @@ proc getPayload*(m: ELManager, deadline.cancelSoon() - if bestPayloadIdx.isSome: - return ok requests[bestPayloadIdx.get].value().asConsensusType + when PayloadType.kind != ConsensusFork.Electra: + if bestPayloadIdx.isSome: + return ok requests[bestPayloadIdx.get].value().asConsensusType + else: + return err() else: + # right now, asConsensusType is confused by Deneb and Electra sharing a + # Payload type. this will probably be resolved in time naturally. + debugRaiseAssert "getPayload ForkyExecutionPayloadForSigning" return err() proc waitELToSyncDeposits(connection: ELConnection, diff --git a/beacon_chain/era_db.nim b/beacon_chain/era_db.nim index 3f3c71877..8b6e3bf35 100644 --- a/beacon_chain/era_db.nim +++ b/beacon_chain/era_db.nim @@ -447,6 +447,8 @@ iterator getBlockIds*( if not getPartialState( db, historical_roots, historical_summaries, stateSlot, state[]): state = nil # No `return` in iterators + of ConsensusFork.Electra: + debugRaiseAssert "getBlockIds" if state == nil: break diff --git a/beacon_chain/gossip_processing/block_processor.nim b/beacon_chain/gossip_processing/block_processor.nim index a190dc895..b449e2ac3 100644 --- a/beacon_chain/gossip_processing/block_processor.nim +++ b/beacon_chain/gossip_processing/block_processor.nim @@ -328,7 +328,7 @@ proc newExecutionPayload*( proc getExecutionValidity( elManager: ELManager, blck: bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock | - deneb.SignedBeaconBlock): + deneb.SignedBeaconBlock | electra.SignedBeaconBlock): Future[NewPayloadStatus] {.async: (raises: [CancelledError]).} = if not blck.message.is_execution_block: return NewPayloadStatus.valid # vacuously @@ -361,9 +361,10 @@ proc getExecutionValidity( blck = shortLog(blck) return NewPayloadStatus.noResponse -proc checkBloblessSignature(self: BlockProcessor, - signed_beacon_block: deneb.SignedBeaconBlock): - Result[void, cstring] = +proc checkBloblessSignature( + self: BlockProcessor, + signed_beacon_block: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): + Result[void, cstring] = let dag = self.consensusManager.dag let parent = dag.getBlockRef(signed_beacon_block.message.parent_root).valueOr: return err("checkBloblessSignature called with orphan block") @@ -674,6 +675,9 @@ proc storeBlock( template callForkChoiceUpdated: auto = case self.consensusManager.dag.cfg.consensusForkAtEpoch( newHead.get.blck.bid.slot.epoch) + of ConsensusFork.Electra: + debugRaiseAssert "storeBlock, probably will become PayloadAttributesV3" + callExpectValidFCU(payloadAttributeType = PayloadAttributesV3) of ConsensusFork.Deneb: callExpectValidFCU(payloadAttributeType = PayloadAttributesV3) of ConsensusFork.Capella: diff --git a/beacon_chain/gossip_processing/gossip_validation.nim b/beacon_chain/gossip_processing/gossip_validation.nim index c827d6fe4..562876c87 100644 --- a/beacon_chain/gossip_processing/gossip_validation.nim +++ b/beacon_chain/gossip_processing/gossip_validation.nim @@ -268,7 +268,8 @@ template validateBeaconBlockBellatrix( signed_beacon_block: bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock | - deneb.SignedBeaconBlock, + deneb.SignedBeaconBlock | + electra.SignedBeaconBlock, parent: BlockRef): untyped = # If the execution is enabled for the block -- i.e. # is_execution_enabled(state, block.body) then validate the following: diff --git a/beacon_chain/networking/eth2_network.nim b/beacon_chain/networking/eth2_network.nim index fc97278dd..251b1c30b 100644 --- a/beacon_chain/networking/eth2_network.nim +++ b/beacon_chain/networking/eth2_network.nim @@ -830,7 +830,7 @@ template gossipMaxSize(T: untyped): uint32 = when isFixedSize(T): fixedPortionSize(T).uint32 elif T is bellatrix.SignedBeaconBlock or T is capella.SignedBeaconBlock or - T is deneb.SignedBeaconBlock: + T is deneb.SignedBeaconBlock or T is electra.SignedBeaconBlock: GOSSIP_MAX_SIZE # TODO https://github.com/status-im/nim-ssz-serialization/issues/20 for # Attestation, AttesterSlashing, and SignedAggregateAndProof, which all @@ -2637,6 +2637,12 @@ proc broadcastBeaconBlock*( let topic = getBeaconBlocksTopic(node.forkDigests.deneb) node.broadcast(topic, blck) +proc broadcastBeaconBlock*( + node: Eth2Node, blck: electra.SignedBeaconBlock): + Future[SendResult] {.async: (raises: [CancelledError], raw: true).} = + let topic = getBeaconBlocksTopic(node.forkDigests.electra) + node.broadcast(topic, blck) + proc broadcastBlobSidecar*( node: Eth2Node, subnet_id: BlobId, blob: deneb.BlobSidecar): Future[SendResult] {.async: (raises: [CancelledError], raw: true).} = diff --git a/beacon_chain/networking/network_metadata.nim b/beacon_chain/networking/network_metadata.nim index 28045ea3b..8e0a6ec33 100644 --- a/beacon_chain/networking/network_metadata.nim +++ b/beacon_chain/networking/network_metadata.nim @@ -275,7 +275,8 @@ when const_preset == "gnosis": doAssert network.cfg.BELLATRIX_FORK_EPOCH < FAR_FUTURE_EPOCH doAssert network.cfg.CAPELLA_FORK_EPOCH < FAR_FUTURE_EPOCH doAssert network.cfg.DENEB_FORK_EPOCH < FAR_FUTURE_EPOCH - static: doAssert ConsensusFork.high == ConsensusFork.Deneb + doAssert network.cfg.ELECTRA_FORK_EPOCH == FAR_FUTURE_EPOCH + static: doAssert ConsensusFork.high == ConsensusFork.Electra elif const_preset == "mainnet": when incbinEnabled: @@ -340,7 +341,8 @@ elif const_preset == "mainnet": doAssert network.cfg.BELLATRIX_FORK_EPOCH < FAR_FUTURE_EPOCH doAssert network.cfg.CAPELLA_FORK_EPOCH < FAR_FUTURE_EPOCH doAssert network.cfg.DENEB_FORK_EPOCH < FAR_FUTURE_EPOCH - static: doAssert ConsensusFork.high == ConsensusFork.Deneb + doAssert network.cfg.ELECTRA_FORK_EPOCH == FAR_FUTURE_EPOCH + static: doAssert ConsensusFork.high == ConsensusFork.Electra proc getMetadataForNetwork*(networkName: string): Eth2NetworkMetadata = template loadRuntimeMetadata(): auto = diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 364ade0e7..549d71e70 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -137,6 +137,9 @@ func getVanityLogs(stdoutKind: StdoutLogKind): VanityLogs = func getVanityMascot(consensusFork: ConsensusFork): string = case consensusFork + of ConsensusFork.Electra: + debugRaiseAssert "getVanityMascot" + " " of ConsensusFork.Deneb: "🐟" of ConsensusFork.Capella: @@ -860,7 +863,8 @@ func forkDigests(node: BeaconNode): auto = node.dag.forkDigests.altair, node.dag.forkDigests.bellatrix, node.dag.forkDigests.capella, - node.dag.forkDigests.deneb] + node.dag.forkDigests.deneb, + node.dag.forkDigests.electra] forkDigestsArray # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/p2p-interface.md#attestation-subnet-subscription @@ -1293,7 +1297,8 @@ proc updateGossipStatus(node: BeaconNode, slot: Slot) {.async.} = removeAltairMessageHandlers, removeAltairMessageHandlers, # bellatrix (altair handlers, different forkDigest) removeCapellaMessageHandlers, - removeDenebMessageHandlers + removeDenebMessageHandlers, + removeDenebMessageHandlers # Electra (Deneb handler, different forkDigest) ] for gossipFork in oldGossipForks: @@ -1304,7 +1309,8 @@ proc updateGossipStatus(node: BeaconNode, slot: Slot) {.async.} = addAltairMessageHandlers, addAltairMessageHandlers, # bellatrix (altair handlers, different forkDigest) addCapellaMessageHandlers, - addDenebMessageHandlers + addDenebMessageHandlers, + addDenebMessageHandlers # Electra (Deneb handler, different forkDigest) ] for gossipFork in newGossipForks: diff --git a/beacon_chain/nimbus_signing_node.nim b/beacon_chain/nimbus_signing_node.nim index c8f884fd8..4d75ac3b4 100644 --- a/beacon_chain/nimbus_signing_node.nim +++ b/beacon_chain/nimbus_signing_node.nim @@ -237,6 +237,9 @@ proc installApiHandlers*(node: SigningNodeRef) = (GeneralizedIndex(401), request.beaconBlockHeader.data) of ConsensusFork.Deneb: (GeneralizedIndex(801), request.beaconBlockHeader.data) + of ConsensusFork.Electra: + debugRaiseAssert "/api/v1/eth2/sign/{validator_key} TODO verify correctness" + (GeneralizedIndex(801), request.beaconBlockHeader.data) if request.proofs.isNone() or len(request.proofs.get()) == 0: return errorResponse(Http400, MissingMerkleProofError) diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index 6569b2c2d..a46303df4 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -929,6 +929,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = await node.router.routeSignedBeaconBlock( blck, Opt.some(blck.create_blob_sidecars( restBlock.denebData.kzg_proofs, restBlock.denebData.blobs))) + of ConsensusFork.Electra: + debugRaiseAssert "/eth/v1/beacon/blocks POST; can't reach here unless in Electra per check above, but this keeps return value consistent" + var blck = restBlock.denebData.signed_block + blck.root = hash_tree_root(blck.message) + await node.router.routeSignedBeaconBlock( + blck, Opt.some(blck.create_blob_sidecars( + restBlock.denebData.kzg_proofs, restBlock.denebData.blobs))) if res.isErr(): return RestApiResponse.jsonError( @@ -1005,6 +1012,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = await node.router.routeSignedBeaconBlock( blck, Opt.some(blck.create_blob_sidecars( restBlock.denebData.kzg_proofs, restBlock.denebData.blobs))) + of ConsensusFork.Electra: + debugRaiseAssert "electra missing; /eth/v2/beacon/blocks will only trigger this codepath when in Electra" + var blck = restBlock.denebData.signed_block + blck.root = hash_tree_root(blck.message) + await node.router.routeSignedBeaconBlock( + blck, Opt.some(blck.create_blob_sidecars( + restBlock.denebData.kzg_proofs, restBlock.denebData.blobs))) if res.isErr(): return RestApiResponse.jsonError( @@ -1051,7 +1065,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = RestApiResponse.jsonError(Http500, InvalidAcceptError) withBlck(bdata.asSigned()): - when consensusFork <= ConsensusFork.Altair: + when consensusFork == ConsensusFork.Electra: + debugRaiseAssert "/eth/v1/beacon/blinded_block POST" + RestApiResponse.jsonError(Http500, "electra missing") + elif consensusFork <= ConsensusFork.Altair: respondSszOrJson(forkyBlck, consensusFork) else: respondSszOrJson(toSignedBlindedBeaconBlock(forkyBlck), consensusFork) @@ -1082,7 +1099,11 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = return RestApiResponse.jsonError(Http400, BlockIncorrectFork) withConsensusFork(currentEpochFork): - when consensusFork >= ConsensusFork.Capella: + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "/eth/v1/beacon/blinded_blocks POST" + return RestApiResponse.jsonError( + Http400, $consensusFork & " builder API unsupported") + elif consensusFork >= ConsensusFork.Capella: let restBlock = decodeBodyJsonOrSsz( consensusFork.SignedBlindedBeaconBlock, body).valueOr: diff --git a/beacon_chain/rpc/rest_validator_api.nim b/beacon_chain/rpc/rest_validator_api.nim index 64643a43f..f4ea5d818 100644 --- a/beacon_chain/rpc/rest_validator_api.nim +++ b/beacon_chain/rpc/rest_validator_api.nim @@ -408,7 +408,13 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = return withBlck(message.blck): let data = - when consensusFork >= ConsensusFork.Deneb: + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "/eth/v2/validator/blocks/{slot} GET" + let blobsBundle = message.blobsBundleOpt.get() + deneb.BlockContents( + kzg_proofs: blobsBundle.proofs, + blobs: blobsBundle.blobs) + elif consensusFork >= ConsensusFork.Deneb: let blobsBundle = message.blobsBundleOpt.get() deneb.BlockContents( `block`: forkyBlck, @@ -522,7 +528,11 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = contextFork = node.dag.cfg.consensusForkAtEpoch(node.currentSlot.epoch) withConsensusFork(contextFork): - when consensusFork >= ConsensusFork.Capella: + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "/eth/v1/validator/blinded_blocks/{slot} GET 1" + return RestApiResponse.jsonError( + Http400, "Electra builder API not yet supported") + elif consensusFork >= ConsensusFork.Capella: let res = await makeBlindedBeaconBlockForHeadAndSlot[ consensusFork.BlindedBeaconBlock]( node, payloadBuilderClient, qrandao, @@ -541,7 +551,11 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = if res.isErr(): return RestApiResponse.jsonError(Http400, res.error()) withBlck(res.get().blck): - return responseVersioned(forkyBlck, contextFork) + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "/eth/v1/validator/blinded_blocks/{slot} GET 2" + return RestApiResponse.jsonError(Http400, "") + else: + return responseVersioned(forkyBlck, contextFork) func getMaybeBlindedHeaders( consensusFork: ConsensusFork, @@ -632,7 +646,10 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = return RestApiResponse.jsonError(Http400, InvalidRandaoRevealValue) withConsensusFork(node.dag.cfg.consensusForkAtEpoch(qslot.epoch)): - when consensusFork >= ConsensusFork.Capella: + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "/eth/v3/validator/blocks/{slot} GET 1" + return RestApiResponse.jsonError(Http500, "") + elif consensusFork >= ConsensusFork.Capella: let message = (await node.makeMaybeBlindedBeaconBlockForHeadAndSlot( consensusFork, qrandao, qgraffiti, qhead, qslot)).valueOr: diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 39d8fe5af..8ea33cfed 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -84,8 +84,8 @@ func get_validator_churn_limit*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/deneb/beacon-chain.md#new-get_validator_activation_churn_limit func get_validator_activation_churn_limit*( - cfg: RuntimeConfig, state: deneb.BeaconState, cache: var StateCache): - uint64 = + cfg: RuntimeConfig, state: deneb.BeaconState | electra.BeaconState, + cache: var StateCache): uint64 = ## Return the validator activation churn limit for the current epoch. min( cfg.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, @@ -154,7 +154,7 @@ func get_slashing_penalty*(state: ForkyBeaconState, elif state is altair.BeaconState: validator_effective_balance div MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR elif state is bellatrix.BeaconState or state is capella.BeaconState or - state is deneb.BeaconState: + state is deneb.BeaconState or state is electra.BeaconState: validator_effective_balance div MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX else: {.fatal: "invalid BeaconState type".} @@ -172,7 +172,8 @@ func get_proposer_reward(state: ForkyBeaconState, whistleblower_reward: Gwei): G when state is phase0.BeaconState: whistleblower_reward div PROPOSER_REWARD_QUOTIENT elif state is altair.BeaconState or state is bellatrix.BeaconState or - state is capella.BeaconState or state is deneb.BeaconState: + state is capella.BeaconState or state is deneb.BeaconState or + state is electra.BeaconState: whistleblower_reward * PROPOSER_WEIGHT div WEIGHT_DENOMINATOR else: {.fatal: "invalid BeaconState type".} @@ -292,6 +293,20 @@ func get_initial_beacon_block*(state: deneb.HashedBeaconState): deneb.TrustedSignedBeaconBlock( message: message, root: hash_tree_root(message)) +from ./datatypes/electra import HashedBeaconState, TrustedSignedBeaconBlock + +# TODO spec link here when it exists +func get_initial_beacon_block*(state: electra.HashedBeaconState): + electra.TrustedSignedBeaconBlock = + # The genesis block is implicitly trusted + let message = electra.TrustedBeaconBlock( + slot: state.data.slot, + state_root: state.root) + # parent_root, randao_reveal, eth1_data, signature, and body automatically + # initialized to default values. + electra.TrustedSignedBeaconBlock( + message: message, root: hash_tree_root(message)) + func get_initial_beacon_block*(state: ForkedHashedBeaconState): ForkedTrustedSignedBeaconBlock = withState(state): @@ -549,7 +564,7 @@ func get_attestation_participation_flag_indices( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/deneb/beacon-chain.md#modified-get_attestation_participation_flag_indices func get_attestation_participation_flag_indices( - state: deneb.BeaconState, + state: deneb.BeaconState | electra.BeaconState, data: AttestationData, inclusion_delay: uint64): set[TimelyFlag] = ## Return the flag indices that are satisfied by an attestation. let justified_checkpoint = @@ -613,7 +628,7 @@ func get_base_reward_per_increment*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#get_base_reward func get_base_reward( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState, + deneb.BeaconState | electra.BeaconState, index: ValidatorIndex, base_reward_per_increment: Gwei): Gwei = ## Return the base reward for the validator defined by ``index`` with respect ## to the current ``state``. @@ -658,7 +673,8 @@ proc check_attestation*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/capella/beacon-chain.md#new-process_bls_to_execution_change proc check_bls_to_execution_change*( - genesisFork: Fork, state: capella.BeaconState | deneb.BeaconState, + genesisFork: Fork, + state: capella.BeaconState | deneb.BeaconState | electra.BeaconState, signed_address_change: SignedBLSToExecutionChange, flags: UpdateFlags): Result[void, cstring] = let address_change = signed_address_change.message @@ -746,7 +762,8 @@ proc process_attestation*( else: addPendingAttestation(state.previous_epoch_attestations) elif state is altair.BeaconState or state is bellatrix.BeaconState or - state is capella.BeaconState or state is deneb.BeaconState: + state is capella.BeaconState or state is deneb.BeaconState or + state is electra.BeaconState: template updateParticipationFlags(epoch_participation: untyped) = let proposer_reward = get_proposer_reward( state, attestation, base_reward_per_increment, cache, epoch_participation) @@ -765,7 +782,7 @@ proc process_attestation*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#get_next_sync_committee_indices func get_next_sync_committee_keys( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState): + deneb.BeaconState | electra.BeaconState): array[SYNC_COMMITTEE_SIZE, ValidatorPubKey] = ## Return the sequence of sync committee indices, with possible duplicates, ## for the next sync committee. @@ -825,7 +842,8 @@ func is_partially_withdrawable_validator( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/capella/beacon-chain.md#new-get_expected_withdrawals func get_expected_withdrawals*( - state: capella.BeaconState | deneb.BeaconState): seq[Withdrawal] = + state: capella.BeaconState | deneb.BeaconState | electra.BeaconState): + seq[Withdrawal] = let epoch = get_current_epoch(state) num_validators = lenu64(state.validators) @@ -862,7 +880,7 @@ func get_expected_withdrawals*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#get_next_sync_committee func get_next_sync_committee*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState): + deneb.BeaconState | electra.BeaconState): SyncCommittee = ## Return the next sync committee, with possible pubkey duplicates. var res: SyncCommittee @@ -1436,7 +1454,7 @@ func latest_block_root*(state: ForkedHashedBeaconState): Eth2Digest = func get_sync_committee_cache*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState, + deneb.BeaconState | electra.BeaconState, cache: var StateCache): SyncCommitteeCache = let period = state.slot.sync_committee_period() diff --git a/beacon_chain/spec/datatypes/deneb.nim b/beacon_chain/spec/datatypes/deneb.nim index 1d6acd658..a8ea50335 100644 --- a/beacon_chain/spec/datatypes/deneb.nim +++ b/beacon_chain/spec/datatypes/deneb.nim @@ -603,6 +603,7 @@ func kzg_commitment_inclusion_proof_gindex*( # The first member (`randao_reveal`) is 16, subsequent members +1 each. # If there are ever more than 16 members in `BeaconBlockBody`, indices change! # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/ssz/merkle-proofs.md + debugRaiseAssert "kzg_commitment_inclusion_proof_gindex; ensure still applies to Electra after whatever changes happen" const # blob_kzg_commitments BLOB_KZG_COMMITMENTS_GINDEX = diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index c8296b490..4ee6e5869 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -608,7 +608,9 @@ proc jsonResponseBlock*(t: typedesc[RestApiResponse], writer.writeField("execution_optimistic", execOpt.get()) writer.writeField("finalized", finalized) withBlck(data): - writer.writeField("data", forkyBlck) + debugRaiseAssert "jsonResponseBlock: ForkedSignedBeaconBlock" + when consensusFork != ConsensusFork.Electra: + writer.writeField("data", forkyBlck) writer.endRecord() stream.getOutput(seq[byte]) except SerializationError: @@ -633,7 +635,9 @@ proc jsonResponseState*(t: typedesc[RestApiResponse], if execOpt.isSome(): writer.writeField("execution_optimistic", execOpt.get()) withState(data): - writer.writeField("data", forkyState.data) + debugRaiseAssert "jsonResponseBlock: ForkedHashedBeaconState" + when consensusFork != ConsensusFork.Electra: + writer.writeField("data", forkyState.data) writer.endRecord() stream.getOutput(seq[byte]) except SerializationError: @@ -1559,6 +1563,9 @@ proc readValue*[BlockType: ProduceBlockResponseV2]( reader.raiseUnexpectedValue("Incorrect deneb block format") value = ProduceBlockResponseV2(kind: ConsensusFork.Deneb, denebData: res.get()) + of ConsensusFork.Electra: + debugRaiseAssert "readValue ProduceBlockResponseV2" + reader.raiseUnexpectedValue("Incorrect electra block format") proc readValue*[BlockType: ForkedBlindedBeaconBlock]( reader: var JsonReader[RestJson], @@ -1625,6 +1632,9 @@ proc readValue*[BlockType: ForkedBlindedBeaconBlock]( exc.formatMsg("BlindedBlock") & "]") value = ForkedBlindedBeaconBlock(kind: ConsensusFork.Deneb, denebData: res) + of ConsensusFork.Electra: + debugRaiseAssert "readValue ForkedBlindedBeaconBlock" + reader.raiseUnexpectedValue("Electra blinded block format unsupported") proc readValue*[BlockType: Web3SignerForkedBeaconBlock]( reader: var JsonReader[RestJson], @@ -1927,6 +1937,8 @@ proc readValue*(reader: var JsonReader[RestJson], assign( value.denebBody.execution_payload.excess_blob_gas, ep_src.excess_blob_gas.get()) + of ConsensusFork.Electra: + debugRaiseAssert "readValue" ## RestPublishedBeaconBlock proc readValue*(reader: var JsonReader[RestJson], @@ -2033,6 +2045,16 @@ proc readValue*(reader: var JsonReader[RestJson], body: body.denebBody ) ) + of ConsensusFork.Electra: + ForkedBeaconBlock.init( + electra.BeaconBlock( + slot: slot.get(), + proposer_index: proposer_index.get(), + parent_root: parent_root.get(), + state_root: state_root.get(), + body: body.electraBody + ) + ) ) ## RestPublishedSignedBeaconBlock @@ -2099,6 +2121,13 @@ proc readValue*(reader: var JsonReader[RestJson], signature: signature.get() ) ) + of ConsensusFork.Electra: + ForkedSignedBeaconBlock.init( + electra.SignedBeaconBlock( + message: blck.electraData, + signature: signature.get() + ) + ) ) proc readValue*(reader: var JsonReader[RestJson], @@ -2156,6 +2185,8 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedValue("Incorrect signed_block format") of ConsensusFork.Deneb: ForkedBeaconBlock.init(blck.denebData.message) + of ConsensusFork.Electra: + ForkedBeaconBlock.init(blck.electraData.message) )) of "kzg_proofs": if kzg_proofs.isSome(): @@ -2243,6 +2274,16 @@ proc readValue*(reader: var JsonReader[RestJson], blobs: blobs.get() ) ) + of ConsensusFork.Electra: + value = RestPublishedSignedBlockContents( + kind: ConsensusFork.Electra, + electraData: ElectraSignedBlockContents( + # Constructed to be internally consistent + signed_block: signed_message.get().distinctBase.electraData, + kzg_proofs: kzg_proofs.get(), + blobs: blobs.get() + ) + ) ## ForkedSignedBeaconBlock proc readValue*(reader: var JsonReader[RestJson], @@ -2346,6 +2387,9 @@ proc readValue*(reader: var JsonReader[RestJson], if res.isNone(): reader.raiseUnexpectedValue("Incorrect deneb block format") value = ForkedSignedBeaconBlock.init(res.get()) + of ConsensusFork.Electra: + debugRaiseAssert "readValue 3" + reader.raiseUnexpectedValue("Incorrect electra block format") withBlck(value): forkyBlck.root = hash_tree_root(forkyBlck.message) @@ -2369,6 +2413,10 @@ proc writeValue*( of ConsensusFork.Deneb: writer.writeField("version", "deneb") writer.writeField("data", value.denebData) + of ConsensusFork.Electra: + writer.writeField("version", "electra") + debugRaiseAssert "writeValue ForkedSignedBeaconBlock" + #writer.writeField("data", value.electraData) writer.endRecord() # ForkedHashedBeaconState is used where a `ForkedBeaconState` normally would @@ -2472,6 +2520,9 @@ proc readValue*(reader: var JsonReader[RestJson], except SerializationError: reader.raiseUnexpectedValue("Incorrect deneb beacon state format") toValue(denebData) + of ConsensusFork.Electra: + debugRaiseAssert "readValue ForkedHashedBeaconState" + reader.raiseUnexpectedValue("Incorrect electra beacon state format") proc writeValue*( writer: var JsonWriter[RestJson], value: ForkedHashedBeaconState @@ -2493,6 +2544,10 @@ proc writeValue*( of ConsensusFork.Deneb: writer.writeField("version", "deneb") writer.writeField("data", value.denebData.data) + of ConsensusFork.Electra: + writer.writeField("version", "electra") + debugRaiseAssert "writeValue ForkedHashedBeaconState" + #writer.writeField("data", value.electraData.data) writer.endRecord() ## SomeForkedLightClientObject @@ -3405,18 +3460,20 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: ProduceBlockResponseV3) {.raises: [IOError].} = writer.beginRecord() withForkyMaybeBlindedBlck(value): - writer.writeField("version", consensusFork.toString()) - when isBlinded: - writer.writeField("execution_payload_blinded", "true") - else: - writer.writeField("execution_payload_blinded", "false") - if value.executionValue.isSome(): - writer.writeField("execution_payload_value", - $(value.executionValue.get())) - if value.consensusValue.isSome(): - writer.writeField("consensus_block_value", - $(value.consensusValue.get())) - writer.writeField("data", forkyMaybeBlindedBlck) + debugRaiseAssert "writeValue ProduceBlockResponseV3" + when consensusFork != ConsensusFork.Electra: + writer.writeField("version", consensusFork.toString()) + when isBlinded: + writer.writeField("execution_payload_blinded", "true") + else: + writer.writeField("execution_payload_blinded", "false") + if value.executionValue.isSome(): + writer.writeField("execution_payload_value", + $(value.executionValue.get())) + if value.consensusValue.isSome(): + writer.writeField("consensus_block_value", + $(value.consensusValue.get())) + writer.writeField("data", forkyMaybeBlindedBlck) writer.endRecord() proc readValue*(reader: var JsonReader[RestJson], @@ -3442,7 +3499,9 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedValue("Field `data` is missing") withConsensusFork(version.get): - when consensusFork >= ConsensusFork.Capella: + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "readValue ProduceBlockResponseV3" + elif consensusFork >= ConsensusFork.Capella: if blinded.get: value = ForkedMaybeBlindedBeaconBlock.init( RestJson.decode( @@ -3560,6 +3619,10 @@ proc decodeBody*( return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, [version, $exc.msg])) ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck))) + of ConsensusFork.Electra: + debugRaiseAssert "decodeBody RestPublishedSignedBeaconBlock" + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version])) else: err(RestErrorMessage.init(Http415, "Invalid content type", [version, $body.contentType])) @@ -3650,6 +3713,10 @@ proc decodeBody*( [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Deneb, denebData: blckContents)) + of ConsensusFork.Electra: + debugRaiseAssert "decodeBody RestPublishedSignedBlockContents" + return err(RestErrorMessage.init( + Http400, "electra missing", [version])) else: err(RestErrorMessage.init(Http415, "Invalid content type", [version, $body.contentType])) @@ -3779,6 +3846,11 @@ proc decodeBodyJsonOrSsz*( [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Deneb, denebData: blckContents)) + of ConsensusFork.Electra: + debugRaiseAssert "decodeBodyJsonOrSsz RestPublishedSignedBlockContents" + return err( + RestErrorMessage.init(Http400, "electra missing", + [version])) else: err(RestErrorMessage.init(Http415, "Invalid content type", [version, $body.contentType])) @@ -3922,6 +3994,9 @@ proc decodeBytes*[T: DecodeConsensysTypes]( let fork = ConsensusFork.decodeString(consensusVersion).valueOr: return err("Invalid or Unsupported consensus version") case fork + of ConsensusFork.Electra: + debugRaiseAssert "decodeBytes, DecodeConsensysTypes" + return err("Invalid or Unsupported consensus version") of ConsensusFork.Deneb: let blckContents = ? readSszResBytes(deneb.BlockContents, value) ok(ProduceBlockResponseV2(kind: ConsensusFork.Deneb, @@ -3946,6 +4021,9 @@ proc decodeBytes*[T: DecodeConsensysTypes]( let fork = ConsensusFork.decodeString(consensusVersion).valueOr: return err("Invalid or Unsupported consensus version") case fork + of ConsensusFork.Electra: + debugRaiseAssert "decodeBytes DecodeConsensysTypes" + err("Unable to decode blinded block for Electra fork") of ConsensusFork.Deneb: let blck = ? readSszResBytes(deneb_mev.BlindedBeaconBlock, value) @@ -4018,7 +4096,10 @@ proc decodeBytes*[T: ProduceBlockResponseV3]( except ValueError: return err("Incorrect `Eth-Consensus-Block-Value` header value") withConsensusFork(fork): - when consensusFork >= ConsensusFork.Capella: + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "decodeBytes ProduceBlockV3" + return err("decodeBytes ProduceBlockV3") + elif consensusFork >= ConsensusFork.Capella: if blinded: let contents = ? readSszResBytes(consensusFork.BlindedBlockContents, value) diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index 2e21856c6..7f3a81156 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -17,7 +17,7 @@ import std/[json, tables], stew/base10, web3/primitives, httputils, ".."/forks, - ".."/datatypes/[phase0, altair, bellatrix, deneb], + ".."/datatypes/[phase0, altair, bellatrix, deneb, electra], ".."/mev/[capella_mev, deneb_mev] from ".."/datatypes/capella import BeaconBlockBody @@ -331,6 +331,11 @@ type kzg_proofs*: deneb.KzgProofs blobs*: deneb.Blobs + ElectraSignedBlockContents* = object + signed_block*: electra.SignedBeaconBlock + kzg_proofs*: deneb.KzgProofs + blobs*: deneb.Blobs + RestPublishedSignedBlockContents* = object case kind*: ConsensusFork of ConsensusFork.Phase0: phase0Data*: phase0.SignedBeaconBlock @@ -338,6 +343,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.SignedBeaconBlock of ConsensusFork.Capella: capellaData*: capella.SignedBeaconBlock of ConsensusFork.Deneb: denebData*: DenebSignedBlockContents + of ConsensusFork.Electra: electraData*: ElectraSignedBlockContents RestPublishedBeaconBlock* = distinct ForkedBeaconBlock @@ -348,6 +354,7 @@ type of ConsensusFork.Bellatrix: bellatrixBody*: bellatrix.BeaconBlockBody of ConsensusFork.Capella: capellaBody*: capella.BeaconBlockBody of ConsensusFork.Deneb: denebBody*: deneb.BeaconBlockBody + of ConsensusFork.Electra: electraBody*: electra.BeaconBlockBody ProduceBlockResponseV2* = object case kind*: ConsensusFork @@ -356,6 +363,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.BeaconBlock of ConsensusFork.Capella: capellaData*: capella.BeaconBlock of ConsensusFork.Deneb: denebData*: deneb.BlockContents + of ConsensusFork.Electra: electraData*: electra.BlockContents ProduceBlockResponseV3* = ForkedMaybeBlindedBeaconBlock @@ -632,6 +640,8 @@ func init*(T: type ForkedSignedBeaconBlock, ForkedSignedBeaconBlock.init(contents.capellaData) of ConsensusFork.Deneb: ForkedSignedBeaconBlock.init(contents.denebData.signed_block) + of ConsensusFork.Electra: + ForkedSignedBeaconBlock.init(contents.electraData.signed_block) func init*(t: typedesc[RestPublishedSignedBlockContents], blck: phase0.BeaconBlock, root: Eth2Digest, @@ -689,6 +699,12 @@ func init*(t: typedesc[RestPublishedSignedBlockContents], ) ) +func init*(t: typedesc[RestPublishedSignedBlockContents], + contents: electra.BeaconBlock, root: Eth2Digest, + signature: ValidatorSig): RestPublishedSignedBlockContents = + debugRaiseAssert "init*(t: typedesc[RestPublishedSignedBlockContents]," + default(RestPublishedSignedBlockContents) + func init*(t: typedesc[StateIdent], v: StateIdentType): StateIdent = StateIdent(kind: StateQueryKind.Named, value: v) @@ -1025,3 +1041,7 @@ template withBlck*(x: ProduceBlockResponseV2, const consensusFork {.inject, used.} = ConsensusFork.Deneb template blck: untyped {.inject.} = x.denebData.blck body + of ConsensusFork.Electra: + const consensusFork {.inject, used.} = ConsensusFork.Electra + template blck: untyped {.inject.} = x.electraData.blck + body diff --git a/beacon_chain/spec/forks.nim b/beacon_chain/spec/forks.nim index 2fb8c9910..f1ae668d9 100644 --- a/beacon_chain/spec/forks.nim +++ b/beacon_chain/spec/forks.nim @@ -46,7 +46,8 @@ type Altair, Bellatrix, Capella, - Deneb + Deneb, + Electra ForkyBeaconState* = phase0.BeaconState | @@ -71,6 +72,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.HashedBeaconState of ConsensusFork.Capella: capellaData*: capella.HashedBeaconState of ConsensusFork.Deneb: denebData*: deneb.HashedBeaconState + of ConsensusFork.Electra: electraData*: electra.HashedBeaconState ForkyExecutionPayload* = bellatrix.ExecutionPayload | @@ -158,6 +160,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.BeaconBlock of ConsensusFork.Capella: capellaData*: capella.BeaconBlock of ConsensusFork.Deneb: denebData*: deneb.BeaconBlock + of ConsensusFork.Electra: electraData*: electra.BeaconBlock ForkedMaybeBlindedBeaconBlock* = object case kind*: ConsensusFork @@ -171,6 +174,8 @@ type capellaData*: capella_mev.MaybeBlindedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb_mev.MaybeBlindedBeaconBlock + of ConsensusFork.Electra: + electraData*: electra.BeaconBlock consensusValue*: Opt[UInt256] executionValue*: Opt[UInt256] @@ -185,6 +190,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix_mev.BlindedBeaconBlock of ConsensusFork.Capella: capellaData*: capella_mev.BlindedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb_mev.BlindedBeaconBlock + of ConsensusFork.Electra: electraData*: electra.BeaconBlock ForkedTrustedBeaconBlock* = object case kind*: ConsensusFork @@ -193,6 +199,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.TrustedBeaconBlock of ConsensusFork.Capella: capellaData*: capella.TrustedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb.TrustedBeaconBlock + of ConsensusFork.Electra: electraData*: electra.TrustedBeaconBlock ForkySignedBeaconBlock* = phase0.SignedBeaconBlock | @@ -209,6 +216,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.SignedBeaconBlock of ConsensusFork.Capella: capellaData*: capella.SignedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb.SignedBeaconBlock + of ConsensusFork.Electra: electraData*: electra.SignedBeaconBlock ForkySignedBlindedBeaconBlock* = phase0.SignedBeaconBlock | @@ -224,6 +232,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix_mev.SignedBlindedBeaconBlock of ConsensusFork.Capella: capellaData*: capella_mev.SignedBlindedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb_mev.SignedBlindedBeaconBlock + of ConsensusFork.Electra: electraData*: electra.SignedBeaconBlock ForkySigVerifiedSignedBeaconBlock* = phase0.SigVerifiedSignedBeaconBlock | @@ -256,6 +265,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.MsgTrustedSignedBeaconBlock of ConsensusFork.Capella: capellaData*: capella.MsgTrustedSignedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb.MsgTrustedSignedBeaconBlock + of ConsensusFork.Electra: electraData*: electra.MsgTrustedSignedBeaconBlock ForkedTrustedSignedBeaconBlock* = object case kind*: ConsensusFork @@ -264,6 +274,7 @@ type of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.TrustedSignedBeaconBlock of ConsensusFork.Capella: capellaData*: capella.TrustedSignedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb.TrustedSignedBeaconBlock + of ConsensusFork.Electra: electraData*: electra.TrustedSignedBeaconBlock SomeForkySignedBeaconBlock* = ForkySignedBeaconBlock | @@ -377,8 +388,28 @@ template kind*( deneb_mev.SignedBlindedBeaconBlock]): ConsensusFork = ConsensusFork.Deneb +template kind*( + x: typedesc[ + electra.BeaconState | + electra.HashedBeaconState | + electra.ExecutionPayload | + electra.ExecutionPayloadForSigning | + electra.ExecutionPayloadHeader | + electra.BeaconBlock | + electra.SignedBeaconBlock | + electra.TrustedBeaconBlock | + electra.BeaconBlockBody | + electra.SigVerifiedBeaconBlockBody | + electra.TrustedBeaconBlockBody | + electra.SigVerifiedSignedBeaconBlock | + electra.MsgTrustedSignedBeaconBlock | + electra.TrustedSignedBeaconBlock]): ConsensusFork = + ConsensusFork.Electra + template BeaconState*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Deneb: + when kind == ConsensusFork.Electra: + typedesc[electra.BeaconState] + elif kind == ConsensusFork.Deneb: typedesc[deneb.BeaconState] elif kind == ConsensusFork.Capella: typedesc[capella.BeaconState] @@ -392,7 +423,9 @@ template BeaconState*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template BeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Deneb: + when kind == ConsensusFork.Electra: + typedesc[electra.BeaconBlock] + elif kind == ConsensusFork.Deneb: typedesc[deneb.BeaconBlock] elif kind == ConsensusFork.Capella: typedesc[capella.BeaconBlock] @@ -406,7 +439,9 @@ template BeaconBlock*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template BeaconBlockBody*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Deneb: + when kind == ConsensusFork.Electra: + typedesc[electra.BeaconBlockBody] + elif kind == ConsensusFork.Deneb: typedesc[deneb.BeaconBlockBody] elif kind == ConsensusFork.Capella: typedesc[capella.BeaconBlockBody] @@ -420,7 +455,9 @@ template BeaconBlockBody*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template SignedBeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Deneb: + when kind == ConsensusFork.Electra: + typedesc[electra.SignedBeaconBlock] + elif kind == ConsensusFork.Deneb: typedesc[deneb.SignedBeaconBlock] elif kind == ConsensusFork.Capella: typedesc[capella.SignedBeaconBlock] @@ -434,7 +471,9 @@ template SignedBeaconBlock*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template TrustedSignedBeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Deneb: + when kind == ConsensusFork.Electra: + typedesc[electra.TrustedSignedBeaconBlock] + elif kind == ConsensusFork.Deneb: typedesc[deneb.TrustedSignedBeaconBlock] elif kind == ConsensusFork.Capella: typedesc[capella.TrustedSignedBeaconBlock] @@ -448,7 +487,9 @@ template TrustedSignedBeaconBlock*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template ExecutionPayloadForSigning*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Deneb: + when kind == ConsensusFork.Electra: + typedesc[electra.ExecutionPayloadForSigning] + elif kind == ConsensusFork.Deneb: typedesc[deneb.ExecutionPayloadForSigning] elif kind == ConsensusFork.Capella: typedesc[capella.ExecutionPayloadForSigning] @@ -489,7 +530,10 @@ template SignedBlindedBeaconBlock*(kind: static ConsensusFork): auto = template withAll*( x: typedesc[ConsensusFork], body: untyped): untyped = - static: doAssert ConsensusFork.high == ConsensusFork.Deneb + static: doAssert ConsensusFork.high == ConsensusFork.Electra + block: + const consensusFork {.inject, used.} = ConsensusFork.Electra + body block: const consensusFork {.inject, used.} = ConsensusFork.Deneb body @@ -509,6 +553,9 @@ template withAll*( template withConsensusFork*( x: ConsensusFork, body: untyped): untyped = case x + of ConsensusFork.Electra: + const consensusFork {.inject, used.} = ConsensusFork.Electra + body of ConsensusFork.Deneb: const consensusFork {.inject, used.} = ConsensusFork.Deneb body @@ -567,8 +614,9 @@ template PayloadAttributes*( {.error: "PayloadAttributes does not support " & $kind.} # `eth2_merkleization` cannot import `forks` (circular), so the check is here -static: doAssert ConsensusFork.high == ConsensusFork.Deneb, - "eth2_merkleization has been checked and `hash_tree_root` is up to date" +debugRaiseAssert "eth2_merkleization has been checked and `hash_tree_root` is up to date" +static: doAssert ConsensusFork.high == ConsensusFork.Electra, + "eth2_merkleization has been checked and `hash_tree_root` is up to date" # TODO # TODO when https://github.com/nim-lang/Nim/issues/21086 fixed, use return type # `ref T` @@ -592,6 +640,10 @@ func new*(T: type ForkedHashedBeaconState, data: deneb.BeaconState): ref ForkedHashedBeaconState = (ref T)(kind: ConsensusFork.Deneb, denebData: deneb.HashedBeaconState( data: data, root: hash_tree_root(data))) +func new*(T: type ForkedHashedBeaconState, data: electra.BeaconState): + ref ForkedHashedBeaconState = + (ref T)(kind: ConsensusFork.Electra, electraData: electra.HashedBeaconState( + data: data, root: hash_tree_root(data))) template init*(T: type ForkedBeaconBlock, blck: phase0.BeaconBlock): T = T(kind: ConsensusFork.Phase0, phase0Data: blck) @@ -603,7 +655,10 @@ template init*(T: type ForkedBeaconBlock, blck: capella.BeaconBlock): T = T(kind: ConsensusFork.Capella, capellaData: blck) template init*(T: type ForkedBeaconBlock, blck: deneb.BeaconBlock): T = T(kind: ConsensusFork.Deneb, denebData: blck) +template init*(T: type ForkedBeaconBlock, blck: electra.BeaconBlock): T = + T(kind: ConsensusFork.Electra, electraData: blck) +# TODO are these still used? template init*(T: type ForkedTrustedBeaconBlock, blck: phase0.TrustedBeaconBlock): T = T(kind: ConsensusFork.Phase0, phase0Data: blck) template init*(T: type ForkedTrustedBeaconBlock, blck: altair.TrustedBeaconBlock): T = @@ -623,6 +678,8 @@ template init*(T: type ForkedSignedBeaconBlock, blck: capella.SignedBeaconBlock) T(kind: ConsensusFork.Capella, capellaData: blck) template init*(T: type ForkedSignedBeaconBlock, blck: deneb.SignedBeaconBlock): T = T(kind: ConsensusFork.Deneb, denebData: blck) +template init*(T: type ForkedSignedBeaconBlock, blck: electra.SignedBeaconBlock): T = + T(kind: ConsensusFork.Electra, electraData: blck) func init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock, blockRoot: Eth2Digest, signature: ValidatorSig): T = @@ -652,6 +709,11 @@ func init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock, denebData: deneb.SignedBeaconBlock(message: forked.denebData, root: blockRoot, signature: signature)) + of ConsensusFork.Electra: + T(kind: ConsensusFork.Electra, + electraData: electra.SignedBeaconBlock(message: forked.electraData, + root: blockRoot, + signature: signature)) func init*(T: type ForkedSignedBlindedBeaconBlock, forked: ForkedBlindedBeaconBlock, blockRoot: Eth2Digest, @@ -678,6 +740,9 @@ func init*(T: type ForkedSignedBlindedBeaconBlock, T(kind: ConsensusFork.Deneb, denebData: deneb_mev.SignedBlindedBeaconBlock(message: forked.denebData, signature: signature)) + of ConsensusFork.Electra: + debugRaiseAssert "init*(T: type ForkedSignedBlindedBeaconBlock" + T(kind: ConsensusFork.Electra) template init*(T: type ForkedSignedBlindedBeaconBlock, blck: capella_mev.BlindedBeaconBlock, blockRoot: Eth2Digest, @@ -703,6 +768,8 @@ template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: capella.MsgTrust T(kind: ConsensusFork.Capella, capellaData: blck) template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: deneb.MsgTrustedSignedBeaconBlock): T = T(kind: ConsensusFork.Deneb, denebData: blck) +template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: electra.MsgTrustedSignedBeaconBlock): T = + T(kind: ConsensusFork.Electra, electraData: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: phase0.TrustedSignedBeaconBlock): T = T(kind: ConsensusFork.Phase0, phase0Data: blck) @@ -714,6 +781,8 @@ template init*(T: type ForkedTrustedSignedBeaconBlock, blck: capella.TrustedSign T(kind: ConsensusFork.Capella, capellaData: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: deneb.TrustedSignedBeaconBlock): T = T(kind: ConsensusFork.Deneb, denebData: blck) +template init*(T: type ForkedTrustedSignedBeaconBlock, blck: electra.TrustedSignedBeaconBlock): T = + T(kind: ConsensusFork.Electra, electraData: blck) template toString*(kind: ConsensusFork): string = case kind @@ -727,9 +796,13 @@ template toString*(kind: ConsensusFork): string = "capella" of ConsensusFork.Deneb: "deneb" + of ConsensusFork.Electra: + "electra" template init*(T: typedesc[ConsensusFork], value: string): Opt[ConsensusFork] = case value + of "electra": + Opt.some ConsensusFork.Electra of "deneb": Opt.some ConsensusFork.Deneb of "capella": @@ -754,6 +827,10 @@ template init*(T: type ForkedEpochInfo, info: altair.EpochInfo): T = template withState*(x: ForkedHashedBeaconState, body: untyped): untyped = case x.kind + of ConsensusFork.Electra: + const consensusFork {.inject, used.} = ConsensusFork.Electra + template forkyState: untyped {.inject, used.} = x.electraData + body of ConsensusFork.Deneb: const consensusFork {.inject, used.} = ConsensusFork.Deneb template forkyState: untyped {.inject, used.} = x.denebData @@ -780,7 +857,9 @@ template forky*( ForkedBeaconBlock | ForkedHashedBeaconState, kind: static ConsensusFork): untyped = - when kind == ConsensusFork.Deneb: + when kind == ConsensusFork.Electra: + x.electraData + elif kind == ConsensusFork.Deneb: x.denebData elif kind == ConsensusFork.Capella: x.capellaData @@ -855,6 +934,8 @@ func setStateRoot*(x: var ForkedHashedBeaconState, root: Eth2Digest) = func consensusForkEpoch*( cfg: RuntimeConfig, consensusFork: ConsensusFork): Epoch = case consensusFork + of ConsensusFork.Electra: + cfg.ELECTRA_FORK_EPOCH of ConsensusFork.Deneb: cfg.DENEB_FORK_EPOCH of ConsensusFork.Capella: @@ -869,14 +950,16 @@ func consensusForkEpoch*( func consensusForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): ConsensusFork = ## Return the current fork for the given epoch. static: - doAssert high(ConsensusFork) == ConsensusFork.Deneb + doAssert high(ConsensusFork) == ConsensusFork.Electra + doAssert ConsensusFork.Electra > ConsensusFork.Deneb doAssert ConsensusFork.Deneb > ConsensusFork.Capella doAssert ConsensusFork.Capella > ConsensusFork.Bellatrix doAssert ConsensusFork.Bellatrix > ConsensusFork.Altair doAssert ConsensusFork.Altair > ConsensusFork.Phase0 doAssert GENESIS_EPOCH == 0 - if epoch >= cfg.DENEB_FORK_EPOCH: ConsensusFork.Deneb + if epoch >= cfg.ELECTRA_FORK_EPOCH: ConsensusFork.Electra + elif epoch >= cfg.DENEB_FORK_EPOCH: ConsensusFork.Deneb elif epoch >= cfg.CAPELLA_FORK_EPOCH: ConsensusFork.Capella elif epoch >= cfg.BELLATRIX_FORK_EPOCH: ConsensusFork.Bellatrix elif epoch >= cfg.ALTAIR_FORK_EPOCH: ConsensusFork.Altair @@ -884,8 +967,10 @@ func consensusForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): ConsensusFork = func consensusForkForDigest*( forkDigests: ForkDigests, forkDigest: ForkDigest): Opt[ConsensusFork] = - static: doAssert high(ConsensusFork) == ConsensusFork.Deneb - if forkDigest == forkDigests.deneb: + static: doAssert high(ConsensusFork) == ConsensusFork.Electra + if forkDigest == forkDigests.electra: + ok ConsensusFork.Electra + elif forkDigest == forkDigests.deneb: ok ConsensusFork.Deneb elif forkDigest == forkDigests.capella: ok ConsensusFork.Capella @@ -901,6 +986,8 @@ func consensusForkForDigest*( func atConsensusFork*( forkDigests: ForkDigests, consensusFork: ConsensusFork): ForkDigest = case consensusFork + of ConsensusFork.Electra: + forkDigests.electra of ConsensusFork.Deneb: forkDigests.deneb of ConsensusFork.Capella: @@ -979,6 +1066,10 @@ template withBlck*( const consensusFork {.inject, used.} = ConsensusFork.Deneb template forkyBlck: untyped {.inject, used.} = x.denebData body + of ConsensusFork.Electra: + const consensusFork {.inject, used.} = ConsensusFork.Electra + template forkyBlck: untyped {.inject, used.} = x.electraData + body func proposer_index*(x: ForkedBeaconBlock): uint64 = withBlck(x): forkyBlck.proposer_index @@ -1002,7 +1093,8 @@ template getForkedBlockField*( of ConsensusFork.Altair: unsafeAddr x.altairData.message.y of ConsensusFork.Bellatrix: unsafeAddr x.bellatrixData.message.y of ConsensusFork.Capella: unsafeAddr x.capellaData.message.y - of ConsensusFork.Deneb: unsafeAddr x.denebData.message.y)[] + of ConsensusFork.Deneb: unsafeAddr x.denebData.message.y + of ConsensusFork.Electra: unsafeAddr x.electraData.message.y)[] template signature*(x: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | @@ -1040,6 +1132,12 @@ template withForkyMaybeBlindedBlck*( b: ForkedMaybeBlindedBeaconBlock, body: untyped): untyped = case b.kind + of ConsensusFork.Electra: + const + consensusFork {.inject, used.} = ConsensusFork.Electra + isBlinded {.inject, used.} = false + template forkyMaybeBlindedBlck: untyped {.inject, used.} = b.electraData + body of ConsensusFork.Deneb: const consensusFork {.inject, used.} = ConsensusFork.Deneb template d: untyped = b.denebData @@ -1090,6 +1188,11 @@ template withStateAndBlck*( ForkedTrustedSignedBeaconBlock, body: untyped): untyped = case s.kind + of ConsensusFork.Electra: + const consensusFork {.inject, used.} = ConsensusFork.Electra + template forkyState: untyped {.inject.} = s.electraData + template forkyBlck: untyped {.inject.} = b.electraData + body of ConsensusFork.Deneb: const consensusFork {.inject, used.} = ConsensusFork.Deneb template forkyState: untyped {.inject.} = s.denebData @@ -1188,6 +1291,7 @@ func electraFork*(cfg: RuntimeConfig): Fork = func forkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Fork = case cfg.consensusForkAtEpoch(epoch) + of ConsensusFork.Electra: cfg.electraFork of ConsensusFork.Deneb: cfg.denebFork of ConsensusFork.Capella: cfg.capellaFork of ConsensusFork.Bellatrix: cfg.bellatrixFork @@ -1196,6 +1300,7 @@ func forkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Fork = func forkVersionAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Version = case cfg.consensusForkAtEpoch(epoch) + of ConsensusFork.Electra: cfg.ELECTRA_FORK_VERSION of ConsensusFork.Deneb: cfg.DENEB_FORK_VERSION of ConsensusFork.Capella: cfg.CAPELLA_FORK_VERSION of ConsensusFork.Bellatrix: cfg.BELLATRIX_FORK_VERSION @@ -1203,9 +1308,9 @@ func forkVersionAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Version = of ConsensusFork.Phase0: cfg.GENESIS_FORK_VERSION func nextForkEpochAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Epoch = - static: doAssert high(ConsensusFork) == ConsensusFork.Deneb case cfg.consensusForkAtEpoch(epoch) - of ConsensusFork.Deneb: FAR_FUTURE_EPOCH + of ConsensusFork.Electra: FAR_FUTURE_EPOCH + of ConsensusFork.Deneb: cfg.ELECTRA_FORK_EPOCH of ConsensusFork.Capella: cfg.DENEB_FORK_EPOCH of ConsensusFork.Bellatrix: cfg.CAPELLA_FORK_EPOCH of ConsensusFork.Altair: cfg.BELLATRIX_FORK_EPOCH @@ -1218,6 +1323,7 @@ func forkVersion*(cfg: RuntimeConfig, consensusFork: ConsensusFork): Version = of ConsensusFork.Bellatrix: cfg.BELLATRIX_FORK_VERSION of ConsensusFork.Capella: cfg.CAPELLA_FORK_VERSION of ConsensusFork.Deneb: cfg.DENEB_FORK_VERSION + of ConsensusFork.Electra: cfg.ELECTRA_FORK_VERSION func lcDataForkAtConsensusFork*( consensusFork: ConsensusFork): LightClientDataFork = @@ -1325,7 +1431,7 @@ func compute_fork_digest*(current_version: Version, func init*(T: type ForkDigests, cfg: RuntimeConfig, genesis_validators_root: Eth2Digest): T = - static: doAssert high(ConsensusFork) == ConsensusFork.Deneb + static: doAssert high(ConsensusFork) == ConsensusFork.Electra T( phase0: compute_fork_digest(cfg.GENESIS_FORK_VERSION, genesis_validators_root), diff --git a/beacon_chain/spec/forks_light_client.nim b/beacon_chain/spec/forks_light_client.nim index 09e32c596..db5958ba5 100644 --- a/beacon_chain/spec/forks_light_client.nim +++ b/beacon_chain/spec/forks_light_client.nim @@ -8,7 +8,7 @@ {.push raises: [].} import - ./datatypes/[phase0, altair, bellatrix, capella, deneb], + ./datatypes/[phase0, altair, bellatrix, capella, deneb, electra], ./eth2_merkleization type @@ -957,9 +957,14 @@ func toLightClientHeader*( altair.SignedBeaconBlock | altair.TrustedSignedBeaconBlock | bellatrix.SignedBeaconBlock | bellatrix.TrustedSignedBeaconBlock | capella.SignedBeaconBlock | capella.TrustedSignedBeaconBlock | - deneb.SignedBeaconBlock | deneb.TrustedSignedBeaconBlock, + deneb.SignedBeaconBlock | deneb.TrustedSignedBeaconBlock | + electra.SignedBeaconBlock | electra.TrustedSignedBeaconBlock, kind: static LightClientDataFork): auto = - when kind == LightClientDataFork.Deneb: + when blck is electra.SignedBeaconBlock or + blck is electra.TrustedSignedBeaconBlock: + debugRaiseAssert "toLightClientHeader" + default(deneb.LightClientHeader) + elif kind == LightClientDataFork.Deneb: blck.toDenebLightClientHeader() elif kind == LightClientDataFork.Capella: blck.toCapellaLightClientHeader() diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index e8abe8138..425310648 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -228,7 +228,7 @@ func verify_blob_sidecar_inclusion_proof*( ok() func create_blob_sidecars*( - forkyBlck: deneb.SignedBeaconBlock, + forkyBlck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock, kzg_proofs: KzgProofs, blobs: Blobs): seq[BlobSidecar] = template kzg_commitments: untyped = @@ -382,7 +382,8 @@ func contextEpoch*(update: SomeForkyLightClientUpdate): Epoch = # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/bellatrix/beacon-chain.md#is_merge_transition_complete func is_merge_transition_complete*( - state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState): bool = + state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | + electra.BeaconState): bool = const defaultExecutionPayloadHeader = default(typeof(state.latest_execution_payload_header)) state.latest_execution_payload_header != defaultExecutionPayloadHeader @@ -398,26 +399,32 @@ func is_execution_block*(blck: SomeForkyBeaconBlock): bool = # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/bellatrix/beacon-chain.md#is_merge_transition_block func is_merge_transition_block( - state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState, + state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | + electra.BeaconState, body: bellatrix.BeaconBlockBody | bellatrix.TrustedBeaconBlockBody | bellatrix.SigVerifiedBeaconBlockBody | capella.BeaconBlockBody | capella.TrustedBeaconBlockBody | capella.SigVerifiedBeaconBlockBody | deneb.BeaconBlockBody | deneb.TrustedBeaconBlockBody | - deneb.SigVerifiedBeaconBlockBody): bool = + deneb.SigVerifiedBeaconBlockBody | + electra.BeaconBlockBody | electra.TrustedBeaconBlockBody | + electra.SigVerifiedBeaconBlockBody): bool = const defaultExecutionPayload = default(typeof(body.execution_payload)) not is_merge_transition_complete(state) and body.execution_payload != defaultExecutionPayload # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/bellatrix/beacon-chain.md#is_execution_enabled func is_execution_enabled*( - state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState, + state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | + electra.BeaconState, body: bellatrix.BeaconBlockBody | bellatrix.TrustedBeaconBlockBody | bellatrix.SigVerifiedBeaconBlockBody | capella.BeaconBlockBody | capella.TrustedBeaconBlockBody | capella.SigVerifiedBeaconBlockBody | deneb.BeaconBlockBody | deneb.TrustedBeaconBlockBody | - deneb.SigVerifiedBeaconBlockBody): bool = + deneb.SigVerifiedBeaconBlockBody | + electra.BeaconBlockBody | electra.TrustedBeaconBlockBody | + electra.SigVerifiedBeaconBlockBody): bool = is_merge_transition_block(state, body) or is_merge_transition_complete(state) # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/bellatrix/beacon-chain.md#compute_timestamp_at_slot @@ -449,8 +456,8 @@ func toExecutionWithdrawal*( # https://eips.ethereum.org/EIPS/eip-4895 proc computeWithdrawalsTrieRoot*( - payload: capella.ExecutionPayload | deneb.ExecutionPayload -): ExecutionHash256 = + payload: capella.ExecutionPayload | deneb.ExecutionPayload | + electra.ExecutionPayload): ExecutionHash256 = if payload.withdrawals.len == 0: return EMPTY_ROOT_HASH diff --git a/beacon_chain/spec/state_transition.nim b/beacon_chain/spec/state_transition.nim index 09d4d1eeb..0bee911a6 100644 --- a/beacon_chain/spec/state_transition.nim +++ b/beacon_chain/spec/state_transition.nim @@ -159,18 +159,15 @@ func noRollback*(state: var altair.HashedBeaconState) = func noRollback*(state: var bellatrix.HashedBeaconState) = trace "Skipping rollback of broken Bellatrix state" -from ./datatypes/capella import - ExecutionPayload, HashedBeaconState, SignedBLSToExecutionChangeList, - asSigVerified - func noRollback*(state: var capella.HashedBeaconState) = trace "Skipping rollback of broken Capella state" -from ./datatypes/deneb import HashedBeaconState - func noRollback*(state: var deneb.HashedBeaconState) = trace "Skipping rollback of broken Deneb state" +func noRollback*(state: var electra.HashedBeaconState) = + trace "Skipping rollback of broken Electra state" + func maybeUpgradeStateToAltair( cfg: RuntimeConfig, state: var ForkedHashedBeaconState) = # Both process_slots() and state_transition_block() call this, so only run it @@ -225,6 +222,7 @@ func maybeUpgradeState*( cfg.maybeUpgradeStateToBellatrix(state) cfg.maybeUpgradeStateToCapella(state) cfg.maybeUpgradeStateToDeneb(state) + # TODO cfg.maybeUpgradeStateToElectra proc process_slots*( cfg: RuntimeConfig, state: var ForkedHashedBeaconState, slot: Slot, @@ -477,6 +475,8 @@ proc makeBeaconBlock*( ]) else: raiseAssert "Attempt to use non-Deneb payload with post-Deneb state" + elif consensusFork == ConsensusFork.Electra: + debugRaiseAssert "makeBeaconBlock" else: static: raiseAssert "Unreachable" @@ -501,6 +501,10 @@ proc makeBeaconBlock*( case state.kind of ConsensusFork.Deneb: makeBeaconBlock(deneb) else: raiseAssert "Attempt to use Deneb payload with non-Deneb state" + elif payloadFork == ConsensusFork.Electra: + case state.kind + of ConsensusFork.Electra: makeBeaconBlock(electra) + else: raiseAssert "Attempt to use Electra payload with non-Electra state" else: {.error: "Unsupported fork".} diff --git a/beacon_chain/spec/state_transition_block.nim b/beacon_chain/spec/state_transition_block.nim index adf48ee71..4a574074f 100644 --- a/beacon_chain/spec/state_transition_block.nim +++ b/beacon_chain/spec/state_transition_block.nim @@ -415,7 +415,8 @@ proc process_voluntary_exit*( ok() proc process_bls_to_execution_change*( - cfg: RuntimeConfig, state: var (capella.BeaconState | deneb.BeaconState), + cfg: RuntimeConfig, + state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState), signed_address_change: SignedBLSToExecutionChange): Result[void, cstring] = ? check_bls_to_execution_change( cfg.genesisFork, state, signed_address_change, {}) @@ -486,7 +487,7 @@ func get_proposer_reward*(participant_reward: Gwei): Gwei = # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#sync-aggregate-processing proc process_sync_aggregate*( state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState), sync_aggregate: SomeSyncAggregate, total_active_balance: Gwei, flags: UpdateFlags, cache: var StateCache): @@ -687,10 +688,67 @@ proc process_execution_payload*( ok() +# TODO workaround for https://github.com/nim-lang/Nim/issues/18095 +# copy of datatypes/electra.nim +type SomeElectraBeaconBlockBody = + electra.BeaconBlockBody | electra.SigVerifiedBeaconBlockBody | + electra.TrustedBeaconBlockBody + +# TODO spec ref URL when available +proc process_execution_payload*( + state: var electra.BeaconState, body: SomeElectraBeaconBlockBody, + notify_new_payload: electra.ExecutePayload): Result[void, cstring] = + template payload: auto = body.execution_payload + + # Verify consistency of the parent hash with respect to the previous + # execution payload header + if not (payload.parent_hash == + state.latest_execution_payload_header.block_hash): + return err("process_execution_payload: payload and state parent hash mismatch") + + # Verify prev_randao + if not (payload.prev_randao == get_randao_mix(state, get_current_epoch(state))): + return err("process_execution_payload: payload and state randomness mismatch") + + # Verify timestamp + if not (payload.timestamp == compute_timestamp_at_slot(state, state.slot)): + return err("process_execution_payload: invalid timestamp") + + # [New in Deneb] Verify commitments are under limit + if not (lenu64(body.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK): + return err("process_execution_payload: too many KZG commitments") + + # Verify the execution payload is valid + if not notify_new_payload(payload): + return err("process_execution_payload: execution payload invalid") + + # Cache execution payload header + state.latest_execution_payload_header = electra.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, + base_fee_per_gas: payload.base_fee_per_gas, + block_hash: payload.block_hash, + extra_data: payload.extra_data, + transactions_root: hash_tree_root(payload.transactions), + withdrawals_root: hash_tree_root(payload.withdrawals), + blob_gas_used: payload.blob_gas_used, + excess_blob_gas: payload.excess_blob_gas) + + ok() + # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/capella/beacon-chain.md#new-process_withdrawals func process_withdrawals*( - state: var (capella.BeaconState | deneb.BeaconState), - payload: capella.ExecutionPayload | deneb.ExecutionPayload): + state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState), + payload: capella.ExecutionPayload | deneb.ExecutionPayload | + electra.ExecutionPayload): Result[void, cstring] = let expected_withdrawals = get_expected_withdrawals(state) @@ -902,3 +960,38 @@ proc process_block*( state, blck.body.sync_aggregate, total_active_balance, flags, cache) ok() + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/deneb/beacon-chain.md#block-processing +# TODO workaround for https://github.com/nim-lang/Nim/issues/18095 +type SomeElectraBlock = + electra.BeaconBlock | electra.SigVerifiedBeaconBlock | electra.TrustedBeaconBlock +proc process_block*( + cfg: RuntimeConfig, + state: var electra.BeaconState, blck: SomeElectraBlock, + flags: UpdateFlags, cache: var StateCache): Result[void, cstring]= + ## When there's a new block, we need to verify that the block is sane and + ## update the state accordingly - the state is left in an unknown state when + ## block application fails (!) + + ? process_block_header(state, blck, flags, cache) + + # Consensus specs v1.4.0 unconditionally assume is_execution_enabled is + # true, but intentionally keep such a check. + if is_execution_enabled(state, blck.body): + ? process_withdrawals(state, blck.body.execution_payload) + ? process_execution_payload( + state, blck.body, + func(_: electra.ExecutionPayload): bool = true) + ? process_randao(state, blck.body, flags, cache) + ? process_eth1_data(state, blck.body) + + let + total_active_balance = get_total_active_balance(state, cache) + base_reward_per_increment = + get_base_reward_per_increment(total_active_balance) + ? process_operations( + cfg, state, blck.body, base_reward_per_increment, flags, cache) + ? process_sync_aggregate( + state, blck.body.sync_aggregate, total_active_balance, flags, cache) + + ok() diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index d592c5877..7fd0d80c4 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -177,7 +177,8 @@ from ./datatypes/deneb import BeaconState # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#get_unslashed_participating_indices func get_unslashed_participating_balances*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState): UnslashedParticipatingBalances = + deneb.BeaconState | electra.BeaconState): + UnslashedParticipatingBalances = let previous_epoch = get_previous_epoch(state) current_epoch = get_current_epoch(state) @@ -228,7 +229,7 @@ func get_unslashed_participating_balances*( func is_unslashed_participating_index( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState, + deneb.BeaconState | electra.BeaconState, flag_index: TimelyFlag, epoch: Epoch, validator_index: ValidatorIndex): bool = doAssert epoch in [get_previous_epoch(state), get_current_epoch(state)] # TODO hoist this conditional @@ -445,7 +446,7 @@ proc compute_unrealized_finality*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#justification-and-finalization proc process_justification_and_finalization*( state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState), balances: UnslashedParticipatingBalances, flags: UpdateFlags = {}) = # Initial FFG checkpoint values have a `0x00` stub for `root`. @@ -467,7 +468,7 @@ proc process_justification_and_finalization*( proc compute_unrealized_finality*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState): FinalityCheckpoints = + deneb.BeaconState | electra.BeaconState): FinalityCheckpoints = if get_current_epoch(state) <= GENESIS_EPOCH + 1: return FinalityCheckpoints( justified: state.current_justified_checkpoint, @@ -658,7 +659,7 @@ func get_attestation_deltas( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#get_base_reward func get_base_reward_increment*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState, + deneb.BeaconState | electra.BeaconState, index: ValidatorIndex, base_reward_per_increment: Gwei): Gwei = ## Return the base reward for the validator defined by ``index`` with respect ## to the current ``state``. @@ -669,7 +670,7 @@ func get_base_reward_increment*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#get_flag_index_deltas func get_flag_index_reward*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState, + deneb.BeaconState | electra.BeaconState, base_reward: Gwei, active_increments: Gwei, unslashed_participating_increments: Gwei, weight, finality_delay: uint64): Gwei = @@ -697,7 +698,7 @@ func get_active_increments*( iterator get_flag_and_inactivity_deltas*( cfg: RuntimeConfig, state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState, + deneb.BeaconState | electra.BeaconState, base_reward_per_increment: Gwei, info: var altair.EpochInfo, finality_delay: uint64): (ValidatorIndex, Gwei, Gwei, Gwei, Gwei, Gwei, Gwei) = @@ -807,7 +808,7 @@ func process_rewards_and_penalties*( func process_rewards_and_penalties*( cfg: RuntimeConfig, state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState), info: var altair.EpochInfo) = if get_current_epoch(state) == GENESIS_EPOCH: return @@ -914,7 +915,7 @@ func get_adjusted_total_slashing_balance*( elif state is altair.BeaconState: PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR elif state is bellatrix.BeaconState or state is capella.BeaconState or - state is deneb.BeaconState: + state is deneb.BeaconState or state is electra.BeaconState: PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX else: {.fatal: "process_slashings: incorrect BeaconState type".} @@ -1037,7 +1038,8 @@ func process_participation_record_updates*(state: var phase0.BeaconState) = # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#participation-flags-updates func process_participation_flag_updates*( state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState)) = + capella.BeaconState | deneb.BeaconState | + electra.BeaconState)) = state.previous_epoch_participation = state.current_epoch_participation const zero = 0.ParticipationFlags @@ -1051,7 +1053,8 @@ func process_participation_flag_updates*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#sync-committee-updates func process_sync_committee_updates*( state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState)) = + capella.BeaconState | deneb.BeaconState | + electra.BeaconState)) = let next_epoch = get_current_epoch(state) + 1 if next_epoch.is_sync_committee_period(): state.current_sync_committee = state.next_sync_committee @@ -1060,7 +1063,7 @@ func process_sync_committee_updates*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/altair/beacon-chain.md#inactivity-scores template compute_inactivity_update( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState, + deneb.BeaconState | electra.BeaconState, info: altair.EpochInfo, pre_inactivity_score: Gwei): Gwei = if not is_eligible_validator(info.validators[index]): continue @@ -1086,7 +1089,7 @@ template compute_inactivity_update( func process_inactivity_updates*( cfg: RuntimeConfig, state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState), info: altair.EpochInfo) = # Score updates based on previous epoch participation, skip genesis epoch if get_current_epoch(state) == GENESIS_EPOCH: @@ -1108,7 +1111,7 @@ func process_inactivity_updates*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/capella/beacon-chain.md#historical-summaries-updates func process_historical_summaries_update*( - state: var (capella.BeaconState | deneb.BeaconState)): + state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState)): Result[void, cstring] = # Set historical block root accumulator. let next_epoch = get_current_epoch(state) + 1 @@ -1159,7 +1162,7 @@ proc process_epoch*( func init*( info: var altair.EpochInfo, state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState) = + deneb.BeaconState | electra.BeaconState) = # init participation, overwriting the full structure info.balances = get_unslashed_participating_balances(state) info.validators.setLen(state.validators.len()) @@ -1229,7 +1232,7 @@ proc process_epoch*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/capella/beacon-chain.md#epoch-processing proc process_epoch*( cfg: RuntimeConfig, - state: var (capella.BeaconState | deneb.BeaconState), + state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState), flags: UpdateFlags, cache: var StateCache, info: var altair.EpochInfo): Result[void, cstring] = let epoch = get_current_epoch(state) diff --git a/beacon_chain/validator_client/api.nim b/beacon_chain/validator_client/api.nim index e455f9855..1519abeae 100644 --- a/beacon_chain/validator_client/api.nim +++ b/beacon_chain/validator_client/api.nim @@ -2259,6 +2259,9 @@ proc publishBlock*( publishBlock(it, data.capellaData) of ConsensusFork.Deneb: publishBlock(it, data.denebData) + of ConsensusFork.Electra: + debugRaiseAssert "publishBlock RestPublishedSignedBlockContents; denebData will assert via mismatched case object discriminator" + publishBlock(it, data.denebData) do: if apiResponse.isErr(): handleCommunicationError() @@ -2305,6 +2308,9 @@ proc publishBlock*( publishBlock(it, data.capellaData) of ConsensusFork.Deneb: publishBlock(it, data.denebData) + of ConsensusFork.Electra: + debugRaiseAssert "publishBlock RestPublishedSignedBlockContents; denebData will create invalid case discriminator" + publishBlock(it, data.denebData) do: if apiResponse.isErr(): @@ -2461,6 +2467,9 @@ proc publishBlindedBlock*( publishBlindedBlock(it, data.capellaData) of ConsensusFork.Deneb: publishBlindedBlock(it, data.denebData) + of ConsensusFork.Electra: + debugRaiseAssert "publishBlindedBlock ForkedSignedBlindedBeaconBlock; denebData mismatches discriminator" + publishBlindedBlock(it, data.denebData) do: if apiResponse.isErr(): handleCommunicationError() @@ -2506,6 +2515,9 @@ proc publishBlindedBlock*( publishBlindedBlock(it, data.capellaData) of ConsensusFork.Deneb: publishBlindedBlock(it, data.denebData) + of ConsensusFork.Electra: + debugRaiseAssert "publishBlindedBlock ForkedSignedBlindedBeaconBlock; denebData mismatches discriminator" + publishBlindedBlock(it, data.denebData) do: if apiResponse.isErr(): handleCommunicationError() diff --git a/beacon_chain/validator_client/block_service.nim b/beacon_chain/validator_client/block_service.nim index 15baf6859..2bf9c26cd 100644 --- a/beacon_chain/validator_client/block_service.nim +++ b/beacon_chain/validator_client/block_service.nim @@ -96,6 +96,9 @@ proc produceBlock( data: ForkedBeaconBlock.init(blck), kzgProofsOpt: Opt.some(kzgProofs), blobsOpt: Opt.some(blobs))) + of ConsensusFork.Electra: + debugRaiseAssert "produceBlock in block_service.nim" + return Opt.none(PreparedBeaconBlock) proc produceBlindedBlock( vc: ValidatorClientRef, @@ -305,8 +308,11 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, blockRoot = hash_tree_root( when consensusFork < ConsensusFork.Deneb: forkyMaybeBlindedBlck - else: + elif consensusFork < ConsensusFork.Electra: forkyMaybeBlindedBlck.`block` + else: + debugRaiseAssert "publishBlockV3 1" + default(Attestation) ) signingRoot = compute_block_signing_root(fork, genesisRoot, slot, blockRoot) @@ -318,8 +324,11 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, blck = shortLog( when consensusFork < ConsensusFork.Deneb: forkyMaybeBlindedBlck - else: + elif consensusFork < ConsensusFork.Electra: forkyMaybeBlindedBlck.`block` + else: + debugRaiseAssert "publishBlockV3 2" + default(bellatrix.BeaconBlock) ) block_root = shortLog(blockRoot) signing_root = shortLog(signingRoot) @@ -572,6 +581,9 @@ proc publishBlockV2(vc: ValidatorClientRef, currentSlot, slot: Slot, signature: signature), kzg_proofs: preparedBlock.kzgProofsOpt.get, blobs: preparedBlock.blobsOpt.get)) + of ConsensusFork.Electra: + debugRaiseAssert "publishBlockV2 2" + default(RestPublishedSignedBlockContents) res = try: diff --git a/beacon_chain/validators/beacon_validators.nim b/beacon_chain/validators/beacon_validators.nim index 8fa4ae330..eeb0ac468 100644 --- a/beacon_chain/validators/beacon_validators.nim +++ b/beacon_chain/validators/beacon_validators.nim @@ -1316,7 +1316,10 @@ proc proposeBlock(node: BeaconNode, genesis_validators_root, node.config.localBlockValueBoost) return withConsensusFork(node.dag.cfg.consensusForkAtEpoch(slot.epoch)): - when consensusFork >= ConsensusFork.Capella: + when consensusFork >= ConsensusFork.Electra: + debugRaiseAssert "proposeBlock; fill in Electra support" + default(BlockRef) + elif consensusFork >= ConsensusFork.Capella: proposeBlockContinuation( consensusFork.SignedBlindedBeaconBlock, consensusFork.ExecutionPayloadForSigning) diff --git a/beacon_chain/validators/validator_pool.nim b/beacon_chain/validators/validator_pool.nim index 19ddbc5b1..7bacb4c58 100644 --- a/beacon_chain/validators/validator_pool.nim +++ b/beacon_chain/validators/validator_pool.nim @@ -565,6 +565,9 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, Web3SignerForkedBeaconBlock(kind: ConsensusFork.Deneb, data: blck.denebData.toBeaconBlockHeader), proofs) + of ConsensusFork.Electra: + debugRaiseAssert "getBlockSignature 2" + default(Web3SignerRequest) elif blck is capella_mev.BlindedBeaconBlock: case v.data.remoteType of RemoteSignerType.Web3Signer: @@ -668,6 +671,9 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, Web3SignerForkedBeaconBlock(kind: ConsensusFork.Deneb, data: forkyMaybeBlindedBlck.`block`.toBeaconBlockHeader), proofs) + elif consensusFork == ConsensusFork.Electra: + debugRaiseAssert "getBlockSignature 1" + default(Web3SignerRequest) else: case blck.kind of ConsensusFork.Phase0, ConsensusFork.Altair: @@ -711,6 +717,9 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, Web3SignerForkedBeaconBlock(kind: ConsensusFork.Deneb, data: blck.denebData.toBeaconBlockHeader), proofs) + of ConsensusFork.Electra: + debugRaiseAssert "getBlockSignature" + return SignatureResult.err("Invalid beacon block fork version") await v.signData(web3signerRequest) # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.7/specs/phase0/validator.md#aggregate-signature diff --git a/ncli/ncli.nim b/ncli/ncli.nim index 2bec6d5f1..fd8c8e25a 100644 --- a/ncli/ncli.nim +++ b/ncli/ncli.nim @@ -98,6 +98,7 @@ template saveSSZFile(filename: string, value: ForkedHashedBeaconState) = of ConsensusFork.Bellatrix: SSZ.saveFile(filename, value.bellatrixData.data) of ConsensusFork.Capella: SSZ.saveFile(filename, value.capellaData.data) of ConsensusFork.Deneb: SSZ.saveFile(filename, value.denebData.data) + of ConsensusFork.Electra: SSZ.saveFile(filename, value.electraData.data) except IOError: raiseAssert "error saving SSZ file" diff --git a/ncli/ncli_common.nim b/ncli/ncli_common.nim index 87fcc7b23..597cd734e 100644 --- a/ncli/ncli_common.nim +++ b/ncli/ncli_common.nim @@ -274,7 +274,7 @@ proc collectEpochRewardsAndPenalties*( proc collectEpochRewardsAndPenalties*( rewardsAndPenalties: var seq[RewardsAndPenalties], state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState), cache: var StateCache, cfg: RuntimeConfig, flags: UpdateFlags) = if get_current_epoch(state) == GENESIS_EPOCH: return diff --git a/ncli/ncli_db.nim b/ncli/ncli_db.nim index 0dcfc48ac..7a3c62ac2 100644 --- a/ncli/ncli_db.nim +++ b/ncli/ncli_db.nim @@ -240,7 +240,8 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = seq[altair.TrustedSignedBeaconBlock], seq[bellatrix.TrustedSignedBeaconBlock], seq[capella.TrustedSignedBeaconBlock], - seq[deneb.TrustedSignedBeaconBlock]) + seq[deneb.TrustedSignedBeaconBlock], + seq[electra.TrustedSignedBeaconBlock]) echo "Loaded head slot ", dag.head.slot, " selected ", blockRefs.len, " blocks" @@ -266,6 +267,9 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = of ConsensusFork.Deneb: blocks[4].add dag.db.getBlock( blck.root, deneb.TrustedSignedBeaconBlock).get() + of ConsensusFork.Electra: + blocks[5].add dag.db.getBlock( + blck.root, electra.TrustedSignedBeaconBlock).get() let stateData = newClone(dag.headState) @@ -277,7 +281,8 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = (ref altair.HashedBeaconState)(), (ref bellatrix.HashedBeaconState)(), (ref capella.HashedBeaconState)(), - (ref deneb.HashedBeaconState)()) + (ref deneb.HashedBeaconState)(), + (ref electra.HashedBeaconState)()) withTimer(timers[tLoadState]): doAssert dag.updateState( @@ -338,6 +343,9 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = of ConsensusFork.Deneb: doAssert dbBenchmark.getState( forkyState.root, loadedState[4][].data, noRollback) + of ConsensusFork.Electra: + doAssert dbBenchmark.getState( + forkyState.root, loadedState[5][].data, noRollback) if forkyState.data.slot.epoch mod 16 == 0: let loadedRoot = case consensusFork @@ -346,6 +354,7 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = of ConsensusFork.Bellatrix: hash_tree_root(loadedState[2][].data) of ConsensusFork.Capella: hash_tree_root(loadedState[3][].data) of ConsensusFork.Deneb: hash_tree_root(loadedState[4][].data) + of ConsensusFork.Electra: hash_tree_root(loadedState[5][].data) doAssert hash_tree_root(forkyState.data) == loadedRoot processBlocks(blocks[0]) @@ -353,6 +362,7 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = processBlocks(blocks[2]) processBlocks(blocks[3]) processBlocks(blocks[4]) + processBlocks(blocks[5]) printTimers(false, timers) @@ -366,6 +376,7 @@ proc cmdDumpState(conf: DbConf) = bellatrixState = (ref bellatrix.HashedBeaconState)() capellaState = (ref capella.HashedBeaconState)() denebState = (ref deneb.HashedBeaconState)() + electraState = (ref electra.HashedBeaconState)() for stateRoot in conf.stateRoot: if shouldShutDown: quit QuitSuccess @@ -428,6 +439,8 @@ proc cmdDumpBlock(conf: DbConf) = dump("./", blck.get()) elif (let blck = db.getBlock(root, deneb.TrustedSignedBeaconBlock); blck.isSome): dump("./", blck.get()) + elif (let blck = db.getBlock(root, electra.TrustedSignedBeaconBlock); blck.isSome): + dump("./", blck.get()) else: echo "Couldn't load ", blockRoot except CatchableError as e: diff --git a/research/block_sim.nim b/research/block_sim.nim index 7decd40e0..0ad239214 100644 --- a/research/block_sim.nim +++ b/research/block_sim.nim @@ -142,6 +142,49 @@ proc makeSimulationBlock( ok(blck) +proc makeSimulationBlock( + cfg: RuntimeConfig, + state: var electra.HashedBeaconState, + proposer_index: ValidatorIndex, + randao_reveal: ValidatorSig, + eth1_data: Eth1Data, + graffiti: GraffitiBytes, + attestations: seq[Attestation], + deposits: seq[Deposit], + exits: BeaconBlockValidatorChanges, + sync_aggregate: SyncAggregate, + execution_payload: electra.ExecutionPayloadForSigning, + bls_to_execution_changes: SignedBLSToExecutionChangeList, + rollback: RollbackHashedProc[electra.HashedBeaconState], + cache: var StateCache, + # TODO: + # `verificationFlags` is needed only in tests and can be + # removed if we don't use invalid signatures there + verificationFlags: UpdateFlags = {}): Result[electra.BeaconBlock, cstring] = + ## Create a block for the given state. The latest block applied to it will + ## be used for the parent_root value, and the slot will be take from + ## state.slot meaning process_slots must be called up to the slot for which + ## the block is to be created. + + # To create a block, we'll first apply a partial block to the state, skipping + # some validations. + + var blck = partialBeaconBlock( + cfg, state, proposer_index, randao_reveal, eth1_data, graffiti, + attestations, deposits, exits, sync_aggregate, execution_payload) + + let res = process_block( + cfg, state.data, blck.asSigVerified(), verificationFlags, cache) + + if res.isErr: + rollback(state) + return err(res.error()) + + state.root = hash_tree_root(state.data) + blck.state_root = state.root + + ok(blck) + # TODO confutils is an impenetrable black box. how can a help text be added here? cli do(slots = SLOTS_PER_EPOCH * 7, validators = SLOTS_PER_EPOCH * 500, @@ -343,6 +386,8 @@ cli do(slots = SLOTS_PER_EPOCH * 7, addr state.capellaData elif T is deneb.SignedBeaconBlock: addr state.denebData + elif T is electra.SignedBeaconBlock: + addr state.electraData else: static: doAssert false message = makeSimulationBlock( @@ -359,7 +404,9 @@ cli do(slots = SLOTS_PER_EPOCH * 7, eth1ProposalData.deposits, BeaconBlockValidatorChanges(), sync_aggregate, - when T is deneb.SignedBeaconBlock: + when T is electra.SignedBeaconBlock: + default(electra.ExecutionPayloadForSigning) + elif T is deneb.SignedBeaconBlock: default(deneb.ExecutionPayloadForSigning) elif T is capella.SignedBeaconBlock: default(capella.ExecutionPayloadForSigning) @@ -432,6 +479,28 @@ cli do(slots = SLOTS_PER_EPOCH * 7, do: raiseAssert "withUpdatedState failed" + proc proposeElectraBlock(slot: Slot) = + if rand(r, 1.0) > blockRatio: + return + + dag.withUpdatedState(tmpState[], dag.getBlockIdAtSlot(slot).expect("block")) do: + let + newBlock = getNewBlock[electra.SignedBeaconBlock](updatedState, slot, cache) + added = dag.addHeadBlock(verifier, newBlock) do ( + blckRef: BlockRef, signedBlock: electra.TrustedSignedBeaconBlock, + epochRef: EpochRef, unrealized: FinalityCheckpoints): + # Callback add to fork choice if valid + attPool.addForkChoice( + epochRef, blckRef, unrealized, signedBlock.message, + blckRef.slot.start_beacon_time) + + dag.updateHead(added[], quarantine[], []) + if dag.needStateCachesAndForkChoicePruning(): + dag.pruneStateCachesDAG() + attPool.prune() + do: + raiseAssert "withUpdatedState failed" + var lastEth1BlockAt = genesisTime eth1BlockNum = 1000 @@ -472,6 +541,7 @@ cli do(slots = SLOTS_PER_EPOCH * 7, if blockRatio > 0.0: withTimer(timers[t]): case dag.cfg.consensusForkAtEpoch(slot.epoch) + of ConsensusFork.Electra: proposeElectraBlock(slot) of ConsensusFork.Deneb: proposeDenebBlock(slot) of ConsensusFork.Capella: proposeCapellaBlock(slot) of ConsensusFork.Bellatrix, ConsensusFork.Altair, ConsensusFork.Phase0: diff --git a/research/wss_sim.nim b/research/wss_sim.nim index fcdc78114..c57bb2e21 100644 --- a/research/wss_sim.nim +++ b/research/wss_sim.nim @@ -221,6 +221,15 @@ cli do(validatorsDir: string, secretsDir: string, fork, genesis_validators_root, slot, blockRoot, validators[proposer]).toValidatorSig()) dump(".", signedBlock) + of ConsensusFork.Electra: + blockRoot = hash_tree_root(message.electraData) + let signedBlock = electra.SignedBeaconBlock( + message: message.electraData, + root: blockRoot, + signature: get_block_signature( + fork, genesis_validators_root, slot, blockRoot, + validators[proposer]).toValidatorSig()) + dump(".", signedBlock) except CatchableError: raiseAssert "unreachable" notice "Block proposed", message, blockRoot diff --git a/tests/consensus_spec/fixtures_utils.nim b/tests/consensus_spec/fixtures_utils.nim index 8143e2bc8..27da45442 100644 --- a/tests/consensus_spec/fixtures_utils.nim +++ b/tests/consensus_spec/fixtures_utils.nim @@ -47,6 +47,12 @@ func readValue*(r: var JsonReader, a: var seq[byte]) = func genesisTestRuntimeConfig*(consensusFork: ConsensusFork): RuntimeConfig = var res = defaultRuntimeConfig case consensusFork + of ConsensusFork.Electra: + res.ELECTRA_FORK_EPOCH = GENESIS_EPOCH + res.DENEB_FORK_EPOCH = GENESIS_EPOCH + res.CAPELLA_FORK_EPOCH = GENESIS_EPOCH + res.BELLATRIX_FORK_EPOCH = GENESIS_EPOCH + res.ALTAIR_FORK_EPOCH = GENESIS_EPOCH of ConsensusFork.Deneb: res.DENEB_FORK_EPOCH = GENESIS_EPOCH res.CAPELLA_FORK_EPOCH = GENESIS_EPOCH diff --git a/tests/consensus_spec/test_fixture_sanity_blocks.nim b/tests/consensus_spec/test_fixture_sanity_blocks.nim index a9a3f1f35..6d92300ef 100644 --- a/tests/consensus_spec/test_fixture_sanity_blocks.nim +++ b/tests/consensus_spec/test_fixture_sanity_blocks.nim @@ -94,4 +94,5 @@ template runForkBlockTests(consensusFork: static ConsensusFork) = RandomDir, suiteName, path) withAll(ConsensusFork): - runForkBlockTests(consensusFork) + when consensusFork != ConsensusFork.Electra: + runForkBlockTests(consensusFork) diff --git a/tests/test_light_client.nim b/tests/test_light_client.nim index 9a9e0ac0f..d7ebeacad 100644 --- a/tests/test_light_client.nim +++ b/tests/test_light_client.nim @@ -25,12 +25,14 @@ suite "Light client" & preset(): headPeriod = 3.SyncCommitteePeriod let cfg = block: # Fork schedule so that each `LightClientDataFork` is covered - static: doAssert ConsensusFork.high == ConsensusFork.Deneb + static: doAssert ConsensusFork.high == ConsensusFork.Electra var res = defaultRuntimeConfig res.ALTAIR_FORK_EPOCH = 1.Epoch res.BELLATRIX_FORK_EPOCH = 2.Epoch res.CAPELLA_FORK_EPOCH = (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * 1).Epoch res.DENEB_FORK_EPOCH = (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * 2).Epoch + debugRaiseAssert "light client test; when this is tested, enable Electra" + res.ELECTRA_FORK_EPOCH = FAR_FUTURE_EPOCH res altairStartSlot = cfg.ALTAIR_FORK_EPOCH.start_slot diff --git a/tests/test_light_client_processor.nim b/tests/test_light_client_processor.nim index f0e437b4a..37c9b5b8b 100644 --- a/tests/test_light_client_processor.nim +++ b/tests/test_light_client_processor.nim @@ -28,12 +28,14 @@ suite "Light client processor" & preset(): highPeriod = 5.SyncCommitteePeriod let cfg = block: # Fork schedule so that each `LightClientDataFork` is covered - static: doAssert ConsensusFork.high == ConsensusFork.Deneb + static: doAssert ConsensusFork.high == ConsensusFork.Electra var res = defaultRuntimeConfig res.ALTAIR_FORK_EPOCH = 1.Epoch res.BELLATRIX_FORK_EPOCH = 2.Epoch res.CAPELLA_FORK_EPOCH = (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * 1).Epoch res.DENEB_FORK_EPOCH = (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * 2).Epoch + res.ELECTRA_FORK_EPOCH = FAR_FUTURE_EPOCH + debugRaiseAssert "Light client processor test, once define/works add Electra to tests" res const numValidators = SLOTS_PER_EPOCH diff --git a/tests/test_signing_node.nim b/tests/test_signing_node.nim index bb069f2f3..41564750d 100644 --- a/tests/test_signing_node.nim +++ b/tests/test_signing_node.nim @@ -17,6 +17,7 @@ import from std/os import getEnv, osErrorMsg +{.push raises: [].} {.used.} const @@ -97,26 +98,33 @@ func init(T: type ForkedBeaconBlock, contents: ProduceBlockResponseV2): T = return ForkedBeaconBlock.init(contents.capellaData) of ConsensusFork.Deneb: return ForkedBeaconBlock.init(contents.denebData.`block`) + of ConsensusFork.Electra: + return ForkedBeaconBlock.init(contents.electraData.`block`) proc getBlock(fork: ConsensusFork, feeRecipient = SigningExpectedFeeRecipient): ForkedBeaconBlock = - let - blckData = - case fork - of ConsensusFork.Phase0: Phase0Block - of ConsensusFork.Altair: AltairBlock - of ConsensusFork.Bellatrix: BellatrixBlock % [feeRecipient] - of ConsensusFork.Capella: CapellaBlock % [feeRecipient] - of ConsensusFork.Deneb: DenebBlockContents % [feeRecipient] - contentType = ContentTypeData( - mediaType: MediaType.init("application/json")) + try: + debugRaiseAssert "getBlock; ConsensusFork.Electra shouldn't use DenebBlockContents, but not tested, so do that together" + let + blckData = + case fork + of ConsensusFork.Phase0: Phase0Block + of ConsensusFork.Altair: AltairBlock + of ConsensusFork.Bellatrix: BellatrixBlock % [feeRecipient] + of ConsensusFork.Capella: CapellaBlock % [feeRecipient] + of ConsensusFork.Deneb: DenebBlockContents % [feeRecipient] + of ConsensusFork.Electra: DenebBlockContents % [feeRecipient] + contentType = ContentTypeData( + mediaType: MediaType.init("application/json")) - let b = decodeBytes(ProduceBlockResponseV2, - blckData.toOpenArrayByte(0, len(blckData) - 1), - Opt.some(contentType), - $fork).tryGet() - ForkedBeaconBlock.init(b) + let b = decodeBytes(ProduceBlockResponseV2, + blckData.toOpenArrayByte(0, len(blckData) - 1), + Opt.some(contentType), + $fork).tryGet() + ForkedBeaconBlock.init(b) + except ValueError: + raiseAssert "unreachable" func init(t: typedesc[Web3SignerForkedBeaconBlock], forked: ForkedBeaconBlock): Web3SignerForkedBeaconBlock = @@ -135,6 +143,11 @@ func init(t: typedesc[Web3SignerForkedBeaconBlock], Web3SignerForkedBeaconBlock( kind: ConsensusFork.Deneb, data: forked.denebData.toBeaconBlockHeader) + of ConsensusFork.Electra: + debugRaiseAssert "init typedesc[Web3SignerForkedBeaconBlock]" + Web3SignerForkedBeaconBlock( + kind: ConsensusFork.Deneb, + data: forked.electraData.toBeaconBlockHeader) proc createKeystore(dataDir, pubkey, store, password: string): Result[void, string] = diff --git a/tests/teststateutil.nim b/tests/teststateutil.nim index 24ffd70c2..86172937c 100644 --- a/tests/teststateutil.nim +++ b/tests/teststateutil.nim @@ -64,7 +64,7 @@ proc getTestStates*( info = ForkedEpochInfo() cfg = defaultRuntimeConfig - static: doAssert high(ConsensusFork) == ConsensusFork.Deneb + static: doAssert high(ConsensusFork) == ConsensusFork.Electra if consensusFork >= ConsensusFork.Altair: cfg.ALTAIR_FORK_EPOCH = 1.Epoch if consensusFork >= ConsensusFork.Bellatrix: @@ -73,6 +73,8 @@ proc getTestStates*( cfg.CAPELLA_FORK_EPOCH = 3.Epoch if consensusFork >= ConsensusFork.Deneb: cfg.DENEB_FORK_EPOCH = 4.Epoch + if consensusFork >= ConsensusFork.Electra: + cfg.DENEB_FORK_EPOCH = 5.Epoch for i, epoch in stateEpochs: let slot = epoch.Epoch.start_slot