diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index f072155b33..43d70fad64 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -61,6 +61,10 @@ OK: 4/4 Fail: 0/4 Skip: 0/4 + sanity check Electra blocks [Preset: mainnet] OK + sanity check Electra states [Preset: mainnet] OK + sanity check Electra states, reusing buffers [Preset: mainnet] OK ++ sanity check Fulu and cross-fork getState rollback [Preset: mainnet] OK ++ sanity check Fulu blocks [Preset: mainnet] OK ++ sanity check Fulu states [Preset: mainnet] OK ++ sanity check Fulu states, reusing buffers [Preset: mainnet] OK + sanity check blobs [Preset: mainnet] OK + sanity check genesis roundtrip [Preset: mainnet] OK + sanity check phase 0 blocks [Preset: mainnet] OK @@ -69,7 +73,7 @@ OK: 4/4 Fail: 0/4 Skip: 0/4 + sanity check phase 0 states, reusing buffers [Preset: mainnet] OK + sanity check state diff roundtrip [Preset: mainnet] OK ``` -OK: 29/29 Fail: 0/29 Skip: 0/29 +OK: 33/33 Fail: 0/33 Skip: 0/33 ## Beacon chain file test suite ```diff + Auto check/repair test (missing data) OK @@ -107,8 +111,9 @@ OK: 1/1 Fail: 0/1 Skip: 0/1 + Capella toSignedBlindedBeaconBlock OK + Deneb toSignedBlindedBeaconBlock OK + Electra toSignedBlindedBeaconBlock OK ++ Fulu toSignedBlindedBeaconBlock OK ``` -OK: 4/4 Fail: 0/4 Skip: 0/4 +OK: 5/5 Fail: 0/5 Skip: 0/5 ## Block pool altair processing [Preset: mainnet] ```diff + Invalid signatures [Preset: mainnet] OK @@ -1137,4 +1142,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 770/775 Fail: 0/775 Skip: 5/775 +OK: 775/780 Fail: 0/780 Skip: 5/780 diff --git a/beacon_chain/beacon_chain_db.nim b/beacon_chain/beacon_chain_db.nim index 143bc5995a..747777bf69 100644 --- a/beacon_chain/beacon_chain_db.nim +++ b/beacon_chain/beacon_chain_db.nim @@ -508,23 +508,43 @@ proc new*(T: type BeaconChainDB, # V1 - expected-to-be small rows get without rowid optimizations keyValues = kvStore db.openKvStore("key_values", true).expectDb() - blocks = [ + blocks = if cfg.FULU_FORK_EPOCH != FAR_FUTURE_EPOCH: [ kvStore db.openKvStore("blocks").expectDb(), 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("electra_blocks").expectDb()] + kvStore db.openKvStore("electra_blocks").expectDb(), + kvStore db.openKvStore("fulu_blocks").expectDb()] + + else: [ + kvStore db.openKvStore("blocks").expectDb(), + 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("electra_blocks").expectDb(), + kvStore db.openKvStore("").expectDb()] stateRoots = kvStore db.openKvStore("state_roots", true).expectDb() - statesNoVal = [ - kvStore db.openKvStore("state_no_validators2").expectDb(), - 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("electra_state_no_validator_pubkeys").expectDb()] + statesNoVal = if cfg.FULU_FORK_EPOCH != FAR_FUTURE_EPOCH: [ + kvStore db.openKvStore("state_no_validators").expectDb(), + 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("electra_state_no_validator_pubkeys").expectDb(), + kvStore db.openKvStore("fulu_state_no_validator_pubkeys").expectDb()] + + else: [ + kvStore db.openKvStore("state_no_validators").expectDb(), + 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("electra_state_no_validator_pubkeys").expectDb(), + kvStore db.openKvStore("").expectDb()] stateDiffs = kvStore db.openKvStore("state_diffs").expectDb() summaries = kvStore db.openKvStore("beacon_block_summaries", true).expectDb() @@ -805,7 +825,7 @@ proc putBlock*( db: BeaconChainDB, value: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | deneb.TrustedSignedBeaconBlock | - electra.TrustedSignedBeaconBlock) = + electra.TrustedSignedBeaconBlock | fulu.TrustedSignedBeaconBlock) = db.withManyWrites: db.blocks[type(value).kind].putSZSSZ(value.root.data, value) db.putBeaconBlockSummary(value.root, value.message.toBeaconBlockSummary()) @@ -859,6 +879,10 @@ template toBeaconStateNoImmutableValidators(state: electra.BeaconState): ElectraBeaconStateNoImmutableValidators = isomorphicCast[ElectraBeaconStateNoImmutableValidators](state) +template toBeaconStateNoImmutableValidators(state: fulu.BeaconState): + FuluBeaconStateNoImmutableValidators = + isomorphicCast[FuluBeaconStateNoImmutableValidators](state) + proc putState*( db: BeaconChainDB, key: Eth2Digest, value: phase0.BeaconState | altair.BeaconState) = @@ -869,7 +893,7 @@ proc putState*( proc putState*( db: BeaconChainDB, key: Eth2Digest, value: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | - electra.BeaconState) = + electra.BeaconState | fulu.BeaconState) = db.updateImmutableValidators(value.validators.asSeq()) db.statesNoVal[type(value).kind].putSZSSZ( key.data, toBeaconStateNoImmutableValidators(value)) @@ -998,7 +1022,8 @@ proc getBlock*( proc getBlock*[ X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | - deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock]( + deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock | + fulu.TrustedSignedBeaconBlock]( db: BeaconChainDB, key: Eth2Digest, T: type X): Opt[T] = # We only store blocks that we trust in the database @@ -1053,7 +1078,8 @@ proc getBlockSSZ*( proc getBlockSSZ*[ X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | - deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock]( + deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock | + fulu.TrustedSignedBeaconBlock]( db: BeaconChainDB, key: Eth2Digest, data: var seq[byte], T: type X): bool = let dataPtr = addr data # Short-lived var success = true @@ -1102,7 +1128,8 @@ proc getBlockSZ*( proc getBlockSZ*[ X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | - deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock]( + deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock | + fulu.TrustedSignedBeaconBlock]( db: BeaconChainDB, key: Eth2Digest, data: var seq[byte], T: type X): bool = let dataPtr = addr data # Short-lived func decode(data: openArray[byte]) = @@ -1200,7 +1227,8 @@ proc getStateOnlyMutableValidators( proc getStateOnlyMutableValidators( immutableValidators: openArray[ImmutableValidatorData2], store: KvStoreRef, key: openArray[byte], - output: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState), + output: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState), rollback: RollbackProc): bool = ## Load state into `output` - BeaconState is large so we want to avoid ## re-allocating it if possible @@ -1285,7 +1313,8 @@ proc getState*( proc getState*( db: BeaconChainDB, key: Eth2Digest, output: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState | electra.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState), rollback: RollbackProc): bool = ## Load state into `output` - BeaconState is large so we want to avoid ## re-allocating it if possible @@ -1365,7 +1394,7 @@ proc containsBlock*( proc containsBlock*[ X: altair.TrustedSignedBeaconBlock | bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | deneb.TrustedSignedBeaconBlock | - electra.TrustedSignedBeaconBlock]( + electra.TrustedSignedBeaconBlock | fulu.TrustedSignedBeaconBlock]( db: BeaconChainDB, key: Eth2Digest, T: type X): bool = db.blocks[X.kind].contains(key.data).expectDb() @@ -1506,7 +1535,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.Electra + static: doAssert ConsensusFork.high == ConsensusFork.Fulu while true: if db.v0.backend.getSnappySSZ( subkey(BeaconBlockSummary, res.root), res.summary) == GetResult.found: @@ -1523,6 +1552,8 @@ iterator getAncestorSummaries*(db: BeaconChainDB, root: Eth2Digest): res.summary = blck.get().message.toBeaconBlockSummary() elif (let blck = db.getBlock(res.root, electra.TrustedSignedBeaconBlock); blck.isSome()): res.summary = blck.get().message.toBeaconBlockSummary() + elif (let blck = db.getBlock(res.root, fulu.TrustedSignedBeaconBlock); blck.isSome()): + res.summary = blck.get().message.toBeaconBlockSummary() else: break diff --git a/beacon_chain/beacon_chain_db_immutable.nim b/beacon_chain/beacon_chain_db_immutable.nim index 63fe4a0cc4..d51d9f8bc9 100644 --- a/beacon_chain/beacon_chain_db_immutable.nim +++ b/beacon_chain/beacon_chain_db_immutable.nim @@ -17,6 +17,8 @@ from ./spec/datatypes/deneb import ExecutionPayloadHeader from ./spec/datatypes/electra import ExecutionPayloadHeader, PendingConsolidation, PendingDeposit, PendingPartialWithdrawal +from ./spec/datatypes/fulu import + ExecutionPayloadHeader type # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#beaconstate @@ -416,3 +418,87 @@ type pending_consolidations*: HashList[PendingConsolidation, Limit PENDING_CONSOLIDATIONS_LIMIT] ## [New in Electra:EIP7251] + + # Memory-representation-equivalent to a Fulu BeaconState for in-place SSZ + # reading and writing + FuluBeaconStateNoImmutableValidators* = object + # Versioning + genesis_time*: uint64 + genesis_validators_root*: Eth2Digest + slot*: Slot + fork*: Fork + + # History + latest_block_header*: BeaconBlockHeader + ## `latest_block_header.state_root == ZERO_HASH` temporarily + + block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] + ## Needed to process attestations, older to newer + + state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] + historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT] + ## Frozen in Capella, replaced by historical_summaries + + # Eth1 + eth1_data*: Eth1Data + eth1_data_votes*: + HashList[Eth1Data, Limit(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH)] + eth1_deposit_index*: uint64 + + # Registry + validators*: + HashList[ValidatorStatusCapella, Limit VALIDATOR_REGISTRY_LIMIT] + balances*: HashList[Gwei, Limit VALIDATOR_REGISTRY_LIMIT] + + # Randomness + randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest] + + # Slashings + slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei] + ## Per-epoch sums of slashed effective balances + + # Participation + previous_epoch_participation*: EpochParticipationFlags + current_epoch_participation*: EpochParticipationFlags + + # Finality + justification_bits*: JustificationBits + ## Bit set for every recent justified epoch + + previous_justified_checkpoint*: Checkpoint + current_justified_checkpoint*: Checkpoint + finalized_checkpoint*: Checkpoint + + # Inactivity + inactivity_scores*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT] + + # Light client sync committees + current_sync_committee*: SyncCommittee + next_sync_committee*: SyncCommittee + + # Execution + latest_execution_payload_header*: fulu.ExecutionPayloadHeader + + # Withdrawals + next_withdrawal_index*: WithdrawalIndex + next_withdrawal_validator_index*: uint64 + + # Deep history valid from Capella onwards + historical_summaries*: + HashList[HistoricalSummary, Limit HISTORICAL_ROOTS_LIMIT] + + deposit_requests_start_index*: uint64 # [New in Electra:EIP6110] + deposit_balance_to_consume*: Gwei # [New in Electra:EIP7251] + exit_balance_to_consume*: Gwei # [New in Electra:EIP7251] + earliest_exit_epoch*: Epoch # [New in Electra:EIP7251] + consolidation_balance_to_consume*: Gwei # [New in Electra:EIP7251] + earliest_consolidation_epoch*: Epoch # [New in Electra:EIP7251] + pending_deposits*: HashList[PendingDeposit, Limit PENDING_DEPOSITS_LIMIT] + ## [New in Electra:EIP7251] + + # [New in Electra:EIP7251] + pending_partial_withdrawals*: + HashList[PendingPartialWithdrawal, Limit PENDING_PARTIAL_WITHDRAWALS_LIMIT] + pending_consolidations*: + HashList[PendingConsolidation, Limit PENDING_CONSOLIDATIONS_LIMIT] + ## [New in Electra:EIP7251] diff --git a/beacon_chain/beacon_chain_file.nim b/beacon_chain/beacon_chain_file.nim index 4333c89d0e..f19d4cf84c 100644 --- a/beacon_chain/beacon_chain_file.nim +++ b/beacon_chain/beacon_chain_file.nim @@ -89,6 +89,8 @@ func getBlobForkCode(fork: ConsensusFork): uint64 = uint64(MaxForksCount) of ConsensusFork.Electra: uint64(MaxForksCount) + uint64(fork) - uint64(ConsensusFork.Deneb) + of ConsensusFork.Fulu: + uint64(MaxForksCount) + uint64(fork) - uint64(ConsensusFork.Electra) of ConsensusFork.Phase0 .. ConsensusFork.Capella: raiseAssert "Blobs are not supported for the fork" diff --git a/beacon_chain/consensus_object_pools/attestation_pool.nim b/beacon_chain/consensus_object_pools/attestation_pool.nim index 3becea2a31..ff70a1f437 100644 --- a/beacon_chain/consensus_object_pools/attestation_pool.nim +++ b/beacon_chain/consensus_object_pools/attestation_pool.nim @@ -652,7 +652,7 @@ func init( T: type AttestationCache, state: altair.HashedBeaconState | bellatrix.HashedBeaconState | capella.HashedBeaconState | deneb.HashedBeaconState | - electra.HashedBeaconState, + electra.HashedBeaconState | fulu.HashedBeaconState, cache: var StateCache): T = # Load attestations that are scheduled for being given rewards for let @@ -861,7 +861,8 @@ proc getAttestationsForBlock*(pool: var AttestationPool, default(seq[phase0.Attestation]) proc getElectraAttestationsForBlock*( - pool: var AttestationPool, state: electra.HashedBeaconState, + pool: var AttestationPool, + state: electra.HashedBeaconState | fulu.HashedBeaconState, cache: var StateCache): seq[electra.Attestation] = let newBlockSlot = state.data.slot.uint64 diff --git a/beacon_chain/consensus_object_pools/blob_quarantine.nim b/beacon_chain/consensus_object_pools/blob_quarantine.nim index d937035320..7bbffd11b6 100644 --- a/beacon_chain/consensus_object_pools/blob_quarantine.nim +++ b/beacon_chain/consensus_object_pools/blob_quarantine.nim @@ -72,7 +72,8 @@ func hasBlob*( func popBlobs*( quarantine: var BlobQuarantine, digest: Eth2Digest, - blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): + blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock | + fulu.SignedBeaconBlock): seq[ref BlobSidecar] = var r: seq[ref BlobSidecar] = @[] for idx, kzg_commitment in blck.message.body.blob_kzg_commitments: @@ -82,14 +83,18 @@ func popBlobs*( r func hasBlobs*(quarantine: BlobQuarantine, - blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): bool = + blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock | + fulu.SignedBeaconBlock): bool = + # Having a fulu SignedBeaconBlock is incorrect atm, but + # shall be fixed once data columns are rebased to fulu for idx, kzg_commitment in blck.message.body.blob_kzg_commitments: if (blck.root, BlobIndex idx, kzg_commitment) notin quarantine.blobs: return false true func blobFetchRecord*(quarantine: BlobQuarantine, - blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): BlobFetchRecord = + blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock | + fulu.SignedBeaconBlock): BlobFetchRecord = var indices: seq[BlobIndex] for i in 0..= ConsensusFork.Deneb: # https://github.com/ethereum/execution-apis/blob/90a46e9137c89d58e818e62fa33a0347bba50085/src/engine/prague.md # does not define any new forkchoiceUpdated, so reuse V3 from Dencun diff --git a/beacon_chain/consensus_object_pools/data_column_quarantine.nim b/beacon_chain/consensus_object_pools/data_column_quarantine.nim index 23cbf995eb..e073f10740 100644 --- a/beacon_chain/consensus_object_pools/data_column_quarantine.nim +++ b/beacon_chain/consensus_object_pools/data_column_quarantine.nim @@ -9,7 +9,7 @@ import std/tables, - ../spec/datatypes/eip7594, + ../spec/datatypes/fulu, ../spec/helpers from std/sequtils import mapIt diff --git a/beacon_chain/el/el_manager.nim b/beacon_chain/el/el_manager.nim index 2d327e6296..5b6884d5de 100644 --- a/beacon_chain/el/el_manager.nim +++ b/beacon_chain/el/el_manager.nim @@ -597,6 +597,9 @@ template EngineApiResponseType*(T: type deneb.ExecutionPayloadForSigning): type template EngineApiResponseType*(T: type electra.ExecutionPayloadForSigning): type = engine_api.GetPayloadV4Response +template EngineApiResponseType*(T: type fulu.ExecutionPayloadForSigning): type = + engine_api.GetPayloadV4Response + template toEngineWithdrawals*(withdrawals: seq[capella.Withdrawal]): seq[WithdrawalV1] = mapIt(withdrawals, toEngineWithdrawal(it)) @@ -719,8 +722,12 @@ proc getPayload*( requests.filterIt(not(it.finished())).mapIt(it.cancelAndWait()) await noCancel allFutures(pending) - if bestPayloadIdx.isSome(): - return ok(requests[bestPayloadIdx.get()].value().asConsensusType) + when PayloadType.kind == ConsensusFork.Fulu: + if bestPayloadIdx.isSome(): + return ok(requests[bestPayloadIdx.get()].value().asConsensusTypeFulu) + else: + if bestPayloadIdx.isSome(): + return ok(requests[bestPayloadIdx.get()].value().asConsensusType) if timeoutExceeded: break @@ -986,7 +993,7 @@ proc sendNewPayload*( let requests = m.elConnections.mapIt: let req = - when typeof(blck).kind == ConsensusFork.Electra: + when typeof(blck).kind >= ConsensusFork.Electra: # https://github.com/ethereum/execution-apis/blob/4140e528360fea53c34a766d86a000c6c039100e/src/engine/prague.md#engine_newpayloadv4 let versioned_hashes = mapIt( blck.body.blob_kzg_commitments, diff --git a/beacon_chain/el/engine_api_conversions.nim b/beacon_chain/el/engine_api_conversions.nim index 476fe84af0..39d5bc51f9 100644 --- a/beacon_chain/el/engine_api_conversions.nim +++ b/beacon_chain/el/engine_api_conversions.nim @@ -9,7 +9,7 @@ import kzg4844/[kzg_abi, kzg], - ../spec/datatypes/[bellatrix, capella, deneb, electra], + ../spec/datatypes/[bellatrix, capella, deneb, electra, fulu], web3/[engine_api, engine_api_types] from std/sequtils import mapIt @@ -156,6 +156,33 @@ func asElectraConsensusPayload(rpcExecutionPayload: ExecutionPayloadV3): blob_gas_used: rpcExecutionPayload.blobGasUsed.uint64, excess_blob_gas: rpcExecutionPayload.excessBlobGas.uint64) +func asFuluConsensusPayload(rpcExecutionPayload: ExecutionPayloadV3): + fulu.ExecutionPayload = + template getTransaction(tt: TypedTransaction): bellatrix.Transaction = + bellatrix.Transaction.init(tt.distinctBase) + + fulu.ExecutionPayload( + parent_hash: rpcExecutionPayload.parentHash.asEth2Digest, + feeRecipient: + ExecutionAddress(data: rpcExecutionPayload.feeRecipient.distinctBase), + state_root: rpcExecutionPayload.stateRoot.asEth2Digest, + receipts_root: rpcExecutionPayload.receiptsRoot.asEth2Digest, + logs_bloom: BloomLogs(data: rpcExecutionPayload.logsBloom.distinctBase), + prev_randao: rpcExecutionPayload.prevRandao.asEth2Digest, + block_number: rpcExecutionPayload.blockNumber.uint64, + gas_limit: rpcExecutionPayload.gasLimit.uint64, + gas_used: rpcExecutionPayload.gasUsed.uint64, + timestamp: rpcExecutionPayload.timestamp.uint64, + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(rpcExecutionPayload.extraData.data), + base_fee_per_gas: rpcExecutionPayload.baseFeePerGas, + block_hash: rpcExecutionPayload.blockHash.asEth2Digest, + transactions: List[bellatrix.Transaction, MAX_TRANSACTIONS_PER_PAYLOAD].init( + mapIt(rpcExecutionPayload.transactions, it.getTransaction)), + withdrawals: List[capella.Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD].init( + mapIt(rpcExecutionPayload.withdrawals, it.asConsensusWithdrawal)), + blob_gas_used: rpcExecutionPayload.blobGasUsed.uint64, + excess_blob_gas: rpcExecutionPayload.excessBlobGas.uint64) + func asConsensusType*(payload: engine_api.GetPayloadV3Response): deneb.ExecutionPayloadForSigning = deneb.ExecutionPayloadForSigning( @@ -196,6 +223,27 @@ func asConsensusType*( payload.blobsBundle.blobs.mapIt(it.data))), executionRequests: payload.executionRequests) +func asConsensusTypeFulu*( + payload: GetPayloadV4Response): + fulu.ExecutionPayloadForSigning = + fulu.ExecutionPayloadForSigning( + executionPayload: payload.executionPayload.asFuluConsensusPayload, + blockValue: payload.blockValue, + # TODO + # The `mapIt` calls below are necessary only because we use different distinct + # types for KZG commitments and Blobs in the `web3` and the `deneb` spec types. + # Both are defined as `array[N, byte]` under the hood. + blobsBundle: deneb.BlobsBundle( + commitments: KzgCommitments.init( + payload.blobsBundle.commitments.mapIt( + kzg_abi.KzgCommitment(bytes: it.data))), + proofs: KzgProofs.init( + payload.blobsBundle.proofs.mapIt( + kzg_abi.KzgProof(bytes: it.data))), + blobs: Blobs.init( + payload.blobsBundle.blobs.mapIt(it.data))), + executionRequests: payload.executionRequests) + func asEngineExecutionPayload*(blockBody: bellatrix.BeaconBlockBody): ExecutionPayloadV1 = template executionPayload(): untyped = blockBody.execution_payload @@ -252,7 +300,8 @@ func asEngineExecutionPayload*(blockBody: capella.BeaconBlockBody): withdrawals: mapIt(executionPayload.withdrawals, it.toEngineWithdrawal)) func asEngineExecutionPayload*( - blockBody: deneb.BeaconBlockBody | electra.BeaconBlockBody): + blockBody: deneb.BeaconBlockBody | electra.BeaconBlockBody | + fulu.BeaconBlockBody): ExecutionPayloadV3 = template executionPayload(): untyped = blockBody.execution_payload diff --git a/beacon_chain/era_db.nim b/beacon_chain/era_db.nim index 2a3477c391..cf43f8501f 100644 --- a/beacon_chain/era_db.nim +++ b/beacon_chain/era_db.nim @@ -442,7 +442,7 @@ iterator getBlockIds*( # `case` ensures we're on a fork for which the `PartialBeaconState` # definition is consistent case db.cfg.consensusForkAtEpoch(slot.epoch) - of ConsensusFork.Phase0 .. ConsensusFork.Electra: + of ConsensusFork.Phase0 .. ConsensusFork.Fulu: let stateSlot = (slot.era() + 1).start_slot() if not getPartialState( db, historical_roots, historical_summaries, stateSlot, state[]): diff --git a/beacon_chain/gossip_processing/block_processor.nim b/beacon_chain/gossip_processing/block_processor.nim index a1677162f7..c8a009c7e5 100644 --- a/beacon_chain/gossip_processing/block_processor.nim +++ b/beacon_chain/gossip_processing/block_processor.nim @@ -333,7 +333,8 @@ proc newExecutionPayload*( proc getExecutionValidity( elManager: ELManager, blck: bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock | - deneb.SignedBeaconBlock | electra.SignedBeaconBlock, + deneb.SignedBeaconBlock | electra.SignedBeaconBlock | + fulu.SignedBeaconBlock, deadlineObj: DeadlineObject, maxRetriesCount: int ): Future[NewPayloadStatus] {.async: (raises: [CancelledError]).} = @@ -371,7 +372,8 @@ proc getExecutionValidity( proc checkBloblessSignature( self: BlockProcessor, - signed_beacon_block: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): + signed_beacon_block: deneb.SignedBeaconBlock | electra.SignedBeaconBlock | + fulu.SignedBeaconBlock): Result[void, cstring] = let dag = self.consensusManager.dag let parent = dag.getBlockRef(signed_beacon_block.message.parent_root).valueOr: @@ -770,10 +772,11 @@ proc storeBlock( deadlineObj = deadlineObj, maxRetriesCount = getRetriesCount()) + debugFuluComment "We don't know yet if there'd be new PayloadAttributes version in Fulu." template callForkChoiceUpdated: auto = case self.consensusManager.dag.cfg.consensusForkAtEpoch( newHead.get.blck.bid.slot.epoch) - of ConsensusFork.Deneb, ConsensusFork.Electra: + of ConsensusFork.Deneb, ConsensusFork.Electra, ConsensusFork.Fulu: # https://github.com/ethereum/execution-apis/blob/90a46e9137c89d58e818e62fa33a0347bba50085/src/engine/prague.md # does not define any new forkchoiceUpdated, so reuse V3 from Dencun callExpectValidFCU(payloadAttributeType = PayloadAttributesV3) diff --git a/beacon_chain/gossip_processing/gossip_validation.nim b/beacon_chain/gossip_processing/gossip_validation.nim index 492b99ae93..d42d1c2be2 100644 --- a/beacon_chain/gossip_processing/gossip_validation.nim +++ b/beacon_chain/gossip_processing/gossip_validation.nim @@ -304,7 +304,8 @@ template validateBeaconBlockBellatrix( bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock | deneb.SignedBeaconBlock | - electra.SignedBeaconBlock, + electra.SignedBeaconBlock | + fulu.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 7d54d60654..9e4c2a7996 100644 --- a/beacon_chain/networking/eth2_network.nim +++ b/beacon_chain/networking/eth2_network.nim @@ -855,7 +855,8 @@ 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 or T is electra.SignedBeaconBlock: + T is deneb.SignedBeaconBlock or T is electra.SignedBeaconBlock or + T is fulu.SignedBeaconBlock: GOSSIP_MAX_SIZE # TODO https://github.com/status-im/nim-ssz-serialization/issues/20 for # Attestation, AttesterSlashing, and SignedAggregateAndProof, which all @@ -2682,6 +2683,12 @@ proc broadcastBeaconBlock*( let topic = getBeaconBlocksTopic(node.forkDigests.electra) node.broadcast(topic, blck) +proc broadcastBeaconBlock*( + node: Eth2Node, blck: fulu.SignedBeaconBlock): + Future[SendResult] {.async: (raises: [CancelledError], raw: true).} = + let topic = getBeaconBlocksTopic(node.forkDigests.fulu) + 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 86649b018d..7fd47d9660 100644 --- a/beacon_chain/networking/network_metadata.nim +++ b/beacon_chain/networking/network_metadata.nim @@ -268,8 +268,10 @@ when const_preset == "gnosis": for network in [gnosisMetadata, chiadoMetadata]: doAssert network.cfg.DENEB_FORK_EPOCH < FAR_FUTURE_EPOCH - doAssert network.cfg.ELECTRA_FORK_EPOCH == FAR_FUTURE_EPOCH - doAssert ConsensusFork.high == ConsensusFork.Electra + doAssert network.cfg.ELECTRA_FORK_EPOCH == FAR_FUTURE_EPOCH + doAssert network.cfg.FULU_FORK_EPOCH == FAR_FUTURE_EPOCH + doAssert ConsensusFork.high == ConsensusFork.Fulu + elif const_preset == "mainnet": when incbinEnabled: @@ -321,7 +323,8 @@ elif const_preset == "mainnet": for network in [mainnetMetadata, sepoliaMetadata, holeskyMetadata]: doAssert network.cfg.DENEB_FORK_EPOCH < FAR_FUTURE_EPOCH doAssert network.cfg.ELECTRA_FORK_EPOCH == FAR_FUTURE_EPOCH - doAssert ConsensusFork.high == ConsensusFork.Electra + doAssert network.cfg.FULU_FORK_EPOCH == FAR_FUTURE_EPOCH + doAssert ConsensusFork.high == ConsensusFork.Fulu 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 9c36d54d2b..1d6edc040c 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -177,6 +177,8 @@ func getVanityLogs(stdoutKind: StdoutLogKind): VanityLogs = func getVanityMascot(consensusFork: ConsensusFork): string = case consensusFork + of ConsensusFork.Fulu: + "not decided yet?" of ConsensusFork.Electra: "🦒" of ConsensusFork.Deneb: @@ -716,7 +718,7 @@ proc init*(T: type BeaconNode, exitQueue: newAsyncEventQueue[SignedVoluntaryExit](), blsToExecQueue: newAsyncEventQueue[SignedBLSToExecutionChange](), propSlashQueue: newAsyncEventQueue[ProposerSlashing](), - attSlashQueue: newAsyncEventQueue[AttesterSlashing](), + attSlashQueue: newAsyncEventQueue[phase0.AttesterSlashing](), blobSidecarQueue: newAsyncEventQueue[BlobSidecarInfoObject](), finalQueue: newAsyncEventQueue[FinalizationInfoObject](), reorgQueue: newAsyncEventQueue[ReorgInfoObject](), @@ -1085,7 +1087,8 @@ func forkDigests(node: BeaconNode): auto = node.dag.forkDigests.bellatrix, node.dag.forkDigests.capella, node.dag.forkDigests.deneb, - node.dag.forkDigests.electra] + node.dag.forkDigests.electra, + node.dag.forkDigests.fulu] forkDigestsArray # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/p2p-interface.md#attestation-subnet-subscription @@ -1277,6 +1280,10 @@ proc addElectraMessageHandlers( node: BeaconNode, forkDigest: ForkDigest, slot: Slot) = node.addDenebMessageHandlers(forkDigest, slot) +proc addFuluMessageHandlers( + node: BeaconNode, forkDigest: ForkDigest, slot: Slot) = + node.addElectraMessageHandlers(forkDigest, slot) + proc removeAltairMessageHandlers(node: BeaconNode, forkDigest: ForkDigest) = node.removePhase0MessageHandlers(forkDigest) @@ -1300,6 +1307,9 @@ proc removeDenebMessageHandlers(node: BeaconNode, forkDigest: ForkDigest) = proc removeElectraMessageHandlers(node: BeaconNode, forkDigest: ForkDigest) = node.removeDenebMessageHandlers(forkDigest) +proc removeFuluMessageHandlers(node: BeaconNode, forkDigest: ForkDigest) = + node.removeElectraMessageHandlers(forkDigest) + proc updateSyncCommitteeTopics(node: BeaconNode, slot: Slot) = template lastSyncUpdate: untyped = node.consensusManager[].actionTracker.lastSyncUpdate @@ -1450,7 +1460,7 @@ proc updateGossipStatus(node: BeaconNode, slot: Slot) {.async.} = TOPIC_SUBSCRIBE_THRESHOLD_SLOTS = 64 HYSTERESIS_BUFFER = 16 - static: doAssert high(ConsensusFork) == ConsensusFork.Electra + static: doAssert high(ConsensusFork) == ConsensusFork.Fulu let head = node.dag.head @@ -1529,7 +1539,8 @@ proc updateGossipStatus(node: BeaconNode, slot: Slot) {.async.} = removeAltairMessageHandlers, # bellatrix (altair handlers, different forkDigest) removeCapellaMessageHandlers, removeDenebMessageHandlers, - removeElectraMessageHandlers + removeElectraMessageHandlers, + removeFuluMessageHandlers ] for gossipFork in oldGossipForks: @@ -1541,7 +1552,8 @@ proc updateGossipStatus(node: BeaconNode, slot: Slot) {.async.} = addAltairMessageHandlers, # bellatrix (altair handlers, different forkDigest) addCapellaMessageHandlers, addDenebMessageHandlers, - addElectraMessageHandlers + addElectraMessageHandlers, + addFuluMessageHandlers ] for gossipFork in newGossipForks: diff --git a/beacon_chain/nimbus_signing_node.nim b/beacon_chain/nimbus_signing_node.nim index 503a5506c6..0c0cfca2aa 100644 --- a/beacon_chain/nimbus_signing_node.nim +++ b/beacon_chain/nimbus_signing_node.nim @@ -248,6 +248,8 @@ proc installApiHandlers*(node: SigningNodeRef) = (GeneralizedIndex(801), request.beaconBlockHeader.data) of ConsensusFork.Electra: (GeneralizedIndex(801), request.beaconBlockHeader.data) + of ConsensusFork.Fulu: + (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 c97a77048d..1457cf9819 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -1455,7 +1455,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = case consensusVersion.get(): of ConsensusFork.Phase0 .. ConsensusFork.Deneb: decodeAttestations(phase0.Attestation) - of ConsensusFork.Electra: + of ConsensusFork.Electra .. ConsensusFork.Fulu: decodeAttestations(electra.Attestation) let failures = @@ -1555,7 +1555,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = case consensusVersion.get(): of ConsensusFork.Phase0 .. ConsensusFork.Deneb: decodeAttesterSlashing(phase0.AttesterSlashing) - of ConsensusFork.Electra: + of ConsensusFork.Electra .. ConsensusFork.Fulu: decodeAttesterSlashing(electra.AttesterSlashing) # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolProposerSlashings diff --git a/beacon_chain/rpc/rest_validator_api.nim b/beacon_chain/rpc/rest_validator_api.nim index 7763f0509a..671ead2f84 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.Electra: + when consensusFork >= ConsensusFork.Fulu: + let blobsBundle = message.blobsBundleOpt.get() + fulu.BlockContents( + `block`: forkyBlck, + kzg_proofs: blobsBundle.proofs, + blobs: blobsBundle.blobs) + elif consensusFork >= ConsensusFork.Electra: let blobsBundle = message.blobsBundleOpt.get() electra.BlockContents( `block`: forkyBlck, @@ -922,7 +928,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = case consensusVersion.get(): of ConsensusFork.Phase0 .. ConsensusFork.Deneb: addDecodedProofs(phase0.SignedAggregateAndProof) - of ConsensusFork.Electra: + of ConsensusFork.Electra .. ConsensusFork.Fulu: addDecodedProofs(electra.SignedAggregateAndProof) await allFutures(proofs) diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 1973042d9b..48c2a7a212 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -86,7 +86,8 @@ func get_validator_from_deposit*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#deposits func get_validator_from_deposit*( - _: electra.BeaconState, pubkey: ValidatorPubKey, + _: electra.BeaconState | fulu.BeaconState, + pubkey: ValidatorPubKey, withdrawal_credentials: Eth2Digest, amount: Gwei): Validator = var validator = Validator( pubkeyData: HashedValidatorPubKey.init(pubkey), @@ -184,7 +185,9 @@ func get_state_exit_queue_info*( ExitQueueInfo( exit_queue_epoch: exit_queue_epoch, exit_queue_churn: exit_queue_churn) -func get_state_exit_queue_info*(state: electra.BeaconState): ExitQueueInfo = +func get_state_exit_queue_info*(state: electra.BeaconState | + fulu.BeaconState): + ExitQueueInfo = # Electra initiate_validator_exit doesn't have same quadratic aspect given # StateCache balance caching default(ExitQueueInfo) @@ -234,7 +237,8 @@ func get_total_active_balance*(state: ForkyBeaconState, cache: var StateCache): # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-get_balance_churn_limit func get_balance_churn_limit( - cfg: RuntimeConfig, state: electra.BeaconState, + cfg: RuntimeConfig, state: electra.BeaconState | + fulu.BeaconState, cache: var StateCache): Gwei = ## Return the churn limit for the current epoch. let churn = max( @@ -245,7 +249,7 @@ func get_balance_churn_limit( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.0/specs/electra/beacon-chain.md#new-get_activation_exit_churn_limit func get_activation_exit_churn_limit*( - cfg: RuntimeConfig, state: electra.BeaconState, cache: var StateCache): + cfg: RuntimeConfig, state: electra.BeaconState | fulu.BeaconState, cache: var StateCache): Gwei = ## Return the churn limit for the current epoch dedicated to activations and ## exits. @@ -255,14 +259,15 @@ func get_activation_exit_churn_limit*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.0/specs/electra/beacon-chain.md#new-get_consolidation_churn_limit func get_consolidation_churn_limit*( - cfg: RuntimeConfig, state: electra.BeaconState, cache: var StateCache): + cfg: RuntimeConfig, state: electra.BeaconState | fulu.BeaconState, cache: var StateCache): Gwei = get_balance_churn_limit(cfg, state, cache) - get_activation_exit_churn_limit(cfg, state, cache) # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.0/specs/electra/beacon-chain.md#new-compute_exit_epoch_and_update_churn func compute_exit_epoch_and_update_churn*( - cfg: RuntimeConfig, state: var electra.BeaconState, exit_balance: Gwei, + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), + exit_balance: Gwei, cache: var StateCache): Epoch = var earliest_exit_epoch = max(state.earliest_exit_epoch, compute_activation_exit_epoch(get_current_epoch(state))) @@ -291,7 +296,7 @@ func compute_exit_epoch_and_update_churn*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.0/specs/electra/beacon-chain.md#new-compute_consolidation_epoch_and_update_churn func compute_consolidation_epoch_and_update_churn*( - cfg: RuntimeConfig, state: var electra.BeaconState, + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), consolidation_balance: Gwei, cache: var StateCache): Epoch = var earliest_consolidation_epoch = max(state.earliest_consolidation_epoch, compute_activation_exit_epoch(get_current_epoch(state))) @@ -321,7 +326,7 @@ func compute_consolidation_epoch_and_update_churn*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.6/specs/electra/beacon-chain.md#modified-initiate_validator_exit func initiate_validator_exit*( - cfg: RuntimeConfig, state: var electra.BeaconState, + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), index: ValidatorIndex, exit_queue_info: ExitQueueInfo, cache: var StateCache): Result[ExitQueueInfo, cstring] = ## Initiate the exit of the validator with index ``index``. @@ -359,7 +364,8 @@ func get_slashing_penalty*( elif state is bellatrix.BeaconState or state is capella.BeaconState or state is deneb.BeaconState: validator_effective_balance div MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX - elif state is electra.BeaconState: + elif state is electra.BeaconState or + state is fulu.BeaconState: validator_effective_balance div MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA else: {.fatal: "invalid BeaconState type".} @@ -375,7 +381,8 @@ func get_whistleblower_reward*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/electra/beacon-chain.md#modified-slash_validator func get_whistleblower_reward*( - state: electra.BeaconState, validator_effective_balance: Gwei): Gwei = + state: electra.BeaconState | fulu.BeaconState, + validator_effective_balance: Gwei): Gwei = validator_effective_balance div WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#slash_validator @@ -386,7 +393,7 @@ func get_proposer_reward(state: ForkyBeaconState, whistleblower_reward: Gwei): G 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 or - state is electra.BeaconState: + state is electra.BeaconState or state is fulu.BeaconState: whistleblower_reward * PROPOSER_WEIGHT div WEIGHT_DENOMINATOR else: {.fatal: "invalid BeaconState type".} @@ -514,6 +521,17 @@ func get_initial_beacon_block*(state: electra.HashedBeaconState): electra.TrustedSignedBeaconBlock( message: message, root: hash_tree_root(message)) +func get_initial_beacon_block*(state: fulu.HashedBeaconState): + fulu.TrustedSignedBeaconBlock = + # The genesis block is implicitly trusted + let message = fulu.TrustedBeaconBlock( + slot: state.data.slot, + state_root: state.root) + # parent_root, randao_reveal, eth1_data, signature, and body automatically + # initialized to default values. + fulu.TrustedSignedBeaconBlock( + message: message, root: hash_tree_root(message)) + func get_initial_beacon_block*(state: ForkedHashedBeaconState): ForkedTrustedSignedBeaconBlock = withState(state): @@ -645,7 +663,7 @@ iterator get_attesting_indices_iter*(state: ForkyBeaconState, # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#modified-get_attesting_indices iterator get_attesting_indices_iter*( - state: electra.BeaconState, + state: electra.BeaconState | fulu.BeaconState, data: AttestationData, aggregation_bits: ElectraCommitteeValidatorsBits, committee_bits: auto, @@ -854,7 +872,7 @@ func get_attestation_participation_flag_indices( # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/deneb/beacon-chain.md#modified-get_attestation_participation_flag_indices func get_attestation_participation_flag_indices( - state: deneb.BeaconState | electra.BeaconState, + state: deneb.BeaconState | electra.BeaconState | fulu.BeaconState, data: AttestationData, inclusion_delay: uint64): set[TimelyFlag] = ## Return the flag indices that are satisfied by an attestation. let justified_checkpoint = @@ -920,7 +938,7 @@ func get_base_reward_per_increment*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/altair/beacon-chain.md#get_base_reward func get_base_reward( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.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``. @@ -965,7 +983,7 @@ proc check_attestation*( ok() proc check_attestation*( - state: electra.BeaconState, + state: electra.BeaconState | fulu.BeaconState, attestation: electra.Attestation | electra.TrustedAttestation, flags: UpdateFlags, cache: var StateCache, on_chain: static bool): Result[void, cstring] = ## Check that an attestation follows the rules of being included in the state @@ -1017,7 +1035,8 @@ proc check_attestation*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/capella/beacon-chain.md#new-process_bls_to_execution_change proc check_bls_to_execution_change*( genesisFork: Fork, - state: capella.BeaconState | deneb.BeaconState | electra.BeaconState, + state: capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState, signed_address_change: SignedBLSToExecutionChange, flags: UpdateFlags): Result[void, cstring] = let address_change = signed_address_change.message @@ -1178,7 +1197,7 @@ proc process_attestation*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#modified-get_next_sync_committee_indices func get_next_sync_committee_keys( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState): + deneb.BeaconState | electra.BeaconState | fulu.BeaconState): array[SYNC_COMMITTEE_SIZE, ValidatorPubKey] = ## Return the sequence of sync committee indices, with possible duplicates, ## for the next sync committee. @@ -1269,7 +1288,8 @@ func is_partially_withdrawable_validator( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-queue_excess_active_balance func queue_excess_active_balance( - state: var electra.BeaconState, index: uint64) = + state: var (electra.BeaconState | fulu.BeaconState), + index: uint64) = let balance = state.balances.item(index) if balance > static(MIN_ACTIVATION_BALANCE.Gwei): let excess_balance = balance - static(MIN_ACTIVATION_BALANCE.Gwei) @@ -1286,14 +1306,16 @@ func queue_excess_active_balance( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-switch_to_compounding_validator func switch_to_compounding_validator*( - state: var electra.BeaconState, index: ValidatorIndex) = + state: var (electra.BeaconState | fulu.BeaconState), + index: ValidatorIndex) = let validator = addr state.validators.mitem(index) validator.withdrawal_credentials.data[0] = COMPOUNDING_WITHDRAWAL_PREFIX queue_excess_active_balance(state, index.uint64) # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/electra/beacon-chain.md#new-get_pending_balance_to_withdraw func get_pending_balance_to_withdraw*( - state: electra.BeaconState, validator_index: ValidatorIndex): Gwei = + state: electra.BeaconState | fulu.BeaconState, + validator_index: ValidatorIndex): Gwei = var pending_balance: Gwei for withdrawal in state.pending_partial_withdrawals: if withdrawal.index == validator_index: @@ -1387,7 +1409,8 @@ func get_expected_withdrawals*( # to cleanly treat the results of get_expected_withdrawals as a seq[Withdrawal] # are valuable enough to make that the default version of this spec function. template get_expected_withdrawals_with_partial_count_aux*( - state: electra.BeaconState, epoch: Epoch, fetch_balance: untyped): + state: electra.BeaconState | fulu.BeaconState, + epoch: Epoch, fetch_balance: untyped): (seq[Withdrawal], uint64) = doAssert epoch - get_current_epoch(state) in [0'u64, 1'u64] @@ -1474,18 +1497,19 @@ template get_expected_withdrawals_with_partial_count_aux*( (withdrawals, partial_withdrawals_count) template get_expected_withdrawals_with_partial_count*( - state: electra.BeaconState): (seq[Withdrawal], uint64) = + state: electra.BeaconState | fulu.BeaconState): (seq[Withdrawal], uint64) = get_expected_withdrawals_with_partial_count_aux( state, get_current_epoch(state)) do: state.balances.item(validator_index) -func get_expected_withdrawals*(state: electra.BeaconState): seq[Withdrawal] = +func get_expected_withdrawals*(state: electra.BeaconState | fulu.BeaconState): + seq[Withdrawal] = get_expected_withdrawals_with_partial_count(state)[0] # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/altair/beacon-chain.md#get_next_sync_committee func get_next_sync_committee*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState): + deneb.BeaconState | electra.BeaconState | fulu.BeaconState): SyncCommittee = ## Return the next sync committee, with possible pubkey duplicates. var res: SyncCommittee @@ -2181,6 +2205,152 @@ func upgrade_to_electra*( post +func upgrade_to_fulu*( + cfg: RuntimeConfig, pre: electra.BeaconState, cache: var StateCache): + ref fulu.BeaconState = + let + epoch = get_current_epoch(pre) + latest_execution_payload_header = fulu.ExecutionPayloadHeader( + parent_hash: pre.latest_execution_payload_header.parent_hash, + fee_recipient: pre.latest_execution_payload_header.fee_recipient, + state_root: pre.latest_execution_payload_header.state_root, + receipts_root: pre.latest_execution_payload_header.receipts_root, + logs_bloom: pre.latest_execution_payload_header.logs_bloom, + prev_randao: pre.latest_execution_payload_header.prev_randao, + block_number: pre.latest_execution_payload_header.block_number, + gas_limit: pre.latest_execution_payload_header.gas_limit, + gas_used: pre.latest_execution_payload_header.gas_used, + timestamp: pre.latest_execution_payload_header.timestamp, + extra_data: pre.latest_execution_payload_header.extra_data, + base_fee_per_gas: pre.latest_execution_payload_header.base_fee_per_gas, + block_hash: pre.latest_execution_payload_header.block_hash, + transactions_root: pre.latest_execution_payload_header.transactions_root, + withdrawals_root: pre.latest_execution_payload_header.withdrawals_root, + blob_gas_used: pre.latest_execution_payload_header.blob_gas_used, + excess_blob_gas: pre.latest_execution_payload_header.excess_blob_gas) + + var max_exit_epoch = FAR_FUTURE_EPOCH + for v in pre.validators: + if v.exit_epoch != FAR_FUTURE_EPOCH: + max_exit_epoch = + if max_exit_epoch == FAR_FUTURE_EPOCH: + v.exit_epoch + else: + max(max_exit_epoch, v.exit_epoch) + if max_exit_epoch == FAR_FUTURE_EPOCH: + max_exit_epoch = get_current_epoch(pre) + let earliest_exit_epoch = max_exit_epoch + 1 + + let post = (ref fulu.BeaconState)( + # Versioning + genesis_time: pre.genesis_time, + genesis_validators_root: pre.genesis_validators_root, + slot: pre.slot, + fork: Fork( + previous_version: pre.fork.current_version, + current_version: cfg.FULU_FORK_VERSION, + epoch: epoch + ), + + # History + latest_block_header: pre.latest_block_header, + block_roots: pre.block_roots, + state_roots: pre.state_roots, + historical_roots: pre.historical_roots, + + # Eth1 + eth1_data: pre.eth1_data, + eth1_data_votes: pre.eth1_data_votes, + eth1_deposit_index: pre.eth1_deposit_index, + + # Registry + validators: pre.validators, + balances: pre.balances, + + # Randomness + randao_mixes: pre.randao_mixes, + + # Slashings + slashings: pre.slashings, + + # Participation + previous_epoch_participation: pre.previous_epoch_participation, + current_epoch_participation: pre.current_epoch_participation, + + # Finality + justification_bits: pre.justification_bits, + previous_justified_checkpoint: pre.previous_justified_checkpoint, + current_justified_checkpoint: pre.current_justified_checkpoint, + finalized_checkpoint: pre.finalized_checkpoint, + + # Inactivity + inactivity_scores: pre.inactivity_scores, + + # Sync + current_sync_committee: pre.current_sync_committee, + next_sync_committee: pre.next_sync_committee, + + # Execution-layer + latest_execution_payload_header: latest_execution_payload_header, + + # Withdrawals + next_withdrawal_index: pre.next_withdrawal_index, + next_withdrawal_validator_index: pre.next_withdrawal_validator_index, + + # Deep history valid from Capella onwards + historical_summaries: pre.historical_summaries, + + # [New in Electra:EIP6110] + deposit_requests_start_index: UNSET_DEPOSIT_REQUESTS_START_INDEX, + + # [New in Electra:EIP7251] + deposit_balance_to_consume: 0.Gwei, + exit_balance_to_consume: 0.Gwei, + earliest_exit_epoch: earliest_exit_epoch, + consolidation_balance_to_consume: 0.Gwei, + earliest_consolidation_epoch: + compute_activation_exit_epoch(get_current_epoch(pre)) + + # pending_balance_deposits, pending_partial_withdrawals, and + # pending_consolidations are default empty lists + ) + + post.exit_balance_to_consume = + get_activation_exit_churn_limit(cfg, post[], cache) + post.consolidation_balance_to_consume = + get_consolidation_churn_limit(cfg, post[], cache) + + # [New in Electra:EIP7251] + # add validators that are not yet active to pending balance deposits + var pre_activation: seq[(Epoch, uint64)] + for index, validator in post.validators: + if validator.activation_epoch == FAR_FUTURE_EPOCH: + pre_activation.add((validator.activation_eligibility_epoch, index.uint64)) + sort(pre_activation) + + for (_, index) in pre_activation: + let balance = post.balances.item(index) + post.balances[index] = 0.Gwei + let validator = addr post.validators.mitem(index) + validator[].effective_balance = 0.Gwei + validator[].activation_eligibility_epoch = FAR_FUTURE_EPOCH + # Use bls.G2_POINT_AT_INFINITY as a signature field placeholder and + # GENESIS_SLOT to distinguish from a pending deposit request + discard post.pending_deposits.add PendingDeposit( + pubkey: validator[].pubkey, + withdrawal_credentials: validator[].withdrawal_credentials, + amount: balance, + signature: ValidatorSig.infinity, + slot: GENESIS_SLOT) + + # Ensure early adopters of compounding credentials go through the activation + # churn + for index, validator in post.validators: + if has_compounding_withdrawal_credential(validator): + queue_excess_active_balance(post[], index.uint64) + + post + func latest_block_root(state: ForkyBeaconState, state_root: Eth2Digest): Eth2Digest = # The root of the last block that was successfully applied to this state - @@ -2212,7 +2382,7 @@ func latest_block_root*(state: ForkedHashedBeaconState): Eth2Digest = func get_sync_committee_cache*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.BeaconState, cache: var StateCache): SyncCommitteeCache = let period = state.slot.sync_committee_period() diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index 4f8849478d..01ce1cb703 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -960,7 +960,8 @@ func checkForkConsistency*(cfg: RuntimeConfig) = let forkVersions = [cfg.GENESIS_FORK_VERSION, cfg.ALTAIR_FORK_VERSION, cfg.BELLATRIX_FORK_VERSION, cfg.CAPELLA_FORK_VERSION, - cfg.DENEB_FORK_VERSION, cfg.ELECTRA_FORK_VERSION] + cfg.DENEB_FORK_VERSION, cfg.ELECTRA_FORK_VERSION, + cfg.FULU_FORK_VERSION] for i in 0 ..< forkVersions.len: for j in i+1 ..< forkVersions.len: @@ -979,6 +980,7 @@ func checkForkConsistency*(cfg: RuntimeConfig) = assertForkEpochOrder(cfg.BELLATRIX_FORK_EPOCH, cfg.CAPELLA_FORK_EPOCH) assertForkEpochOrder(cfg.CAPELLA_FORK_EPOCH, cfg.DENEB_FORK_EPOCH) assertForkEpochOrder(cfg.DENEB_FORK_EPOCH, cfg.ELECTRA_FORK_EPOCH) + assertForkEpochOrder(cfg.ELECTRA_FORK_EPOCH, cfg.FULU_FORK_EPOCH) func ofLen*[T, N](ListType: type List[T, N], n: int): ListType = if n < N: @@ -987,3 +989,6 @@ func ofLen*[T, N](ListType: type List[T, N], n: int): ListType = raise newException(SszSizeMismatchError) template debugComment*(s: string) = discard + +# Specifically has the `Fulu` naming, for easy debugging. +template debugFuluComment* (s: string) = discard diff --git a/beacon_chain/spec/datatypes/eip7594.nim b/beacon_chain/spec/datatypes/eip7594.nim deleted file mode 100644 index 7d664e7666..0000000000 --- a/beacon_chain/spec/datatypes/eip7594.nim +++ /dev/null @@ -1,114 +0,0 @@ -# beacon_chain -# Copyright (c) 2022-2024 Status Research & Development GmbH -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed except according to those terms. - -{.push raises: [].} - -import - std/[sequtils], - "."/[altair, base, deneb], - kzg4844/[kzg, kzg_abi] - -from std/strutils import join - -export base - -const - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/polynomial-commitments-sampling.md#cells - FIELD_ELEMENTS_PER_EXT_BLOB* = 2 * kzg_abi.FIELD_ELEMENTS_PER_BLOB - # Number of field elements in a Reed-Solomon extended blob | - FIELD_ELEMENTS_PER_CELL* = 64 # Number of field elements in a cell | - BYTES_PER_CELL* = FIELD_ELEMENTS_PER_CELL * kzg_abi.BYTES_PER_FIELD_ELEMENT - # The number of bytes in a cell | - CELLS_PER_EXT_BLOB* = FIELD_ELEMENTS_PER_EXT_BLOB div FIELD_ELEMENTS_PER_CELL - # The number of cells in an extended blob | - - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/p2p-interface.md#preset - KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH* = 4 - KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH_GINDEX* = 27 - -type - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.6/specs/_features/eip7594/polynomial-commitments-sampling.md#custom-types - BLSFieldElement* = KzgBytes32 - G2Point* = array[96, byte] - PolynomialCoeff* = List[BLSFieldElement, FIELD_ELEMENTS_PER_EXT_BLOB] - Coset* = array[FIELD_ELEMENTS_PER_CELL, BLSFieldElement] - CosetEvals* = array[FIELD_ELEMENTS_PER_CELL, BLSFieldElement] - Cell* = KzgCell - Cells* = KzgCells - CellsAndProofs* = KzgCellsAndKzgProofs - - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/das-core.md#custom-types - RowIndex* = uint64 - ColumnIndex* = uint64 - CellIndex* = uint64 - -const - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/das-core.md#data-size - NUMBER_OF_COLUMNS* = 128 - - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/das-core.md#networking - DATA_COLUMN_SIDECAR_SUBNET_COUNT* = 128 - - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/das-core.md#custody-setting - SAMPLES_PER_SLOT* = 8 - CUSTODY_REQUIREMENT* = 4 - -type - DataColumn* = List[KzgCell, Limit(MAX_BLOB_COMMITMENTS_PER_BLOCK)] - - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/_features/eip7594/das-core.md#datacolumnsidecar - DataColumnSidecar* = object - index*: ColumnIndex # Index of column in extended matrix - column*: DataColumn - kzg_commitments*: KzgCommitments - kzg_proofs*: KzgProofs - signed_block_header*: SignedBeaconBlockHeader - kzg_commitments_inclusion_proof*: - array[KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH, Eth2Digest] - - DataColumnSidecars* = seq[ref DataColumnSidecar] - - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/p2p-interface.md#datacolumnidentifier - DataColumnIdentifier* = object - block_root*: Eth2Digest - index*: ColumnIndex - - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.6/specs/_features/eip7594/das-core.md#matrixentry - MatrixEntry* = object - cell*: Cell - kzg_proof*: KzgProof - column_index*: ColumnIndex - row_index*: RowIndex - - # Not in spec, defined in order to compute custody subnets - CscBits* = BitArray[DATA_COLUMN_SIDECAR_SUBNET_COUNT] - - CscCount* = uint8 - - # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/p2p-interface.md#metadata - MetaData* = object - seq_number*: uint64 - attnets*: AttnetBits - syncnets*: SyncnetBits - custody_subnet_count*: CscCount - -func shortLog*(v: DataColumnSidecar): auto = - ( - index: v.index, - kzg_commitments: v.kzg_commitments.len, - kzg_proofs: v.kzg_proofs.len, - block_header: shortLog(v.signed_block_header.message), - ) - -func shortLog*(v: seq[DataColumnSidecar]): auto = - "[" & v.mapIt(shortLog(it)).join(", ") & "]" - -func shortLog*(x: seq[DataColumnIdentifier]): string = - "[" & x.mapIt(shortLog(it.block_root) & "/" & $it.index).join(", ") & "]" - -func shortLog*(x: seq[ColumnIndex]): string = - "<" & x.mapIt($it).join(", ") & ">" \ No newline at end of file diff --git a/beacon_chain/spec/datatypes/fulu.nim b/beacon_chain/spec/datatypes/fulu.nim new file mode 100644 index 0000000000..260dbeea2c --- /dev/null +++ b/beacon_chain/spec/datatypes/fulu.nim @@ -0,0 +1,687 @@ +# beacon_chain +# Copyright (c) 2022-2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +# Types specific to Fulu (i.e. known to have changed across hard forks) - see +# `base` for types and guidelines common across forks + +# TODO Careful, not nil analysis is broken / incomplete and the semantics will +# likely change in future versions of the language: +# https://github.com/nim-lang/RFCs/issues/250 +{.experimental: "notnil".} + +import + std/[sequtils, typetraits], + "."/[phase0, base, electra], + chronicles, + json_serialization, + ssz_serialization/[merkleization, proofs], + ssz_serialization/types as sszTypes, + ../digest, + kzg4844/[kzg, kzg_abi] + +from std/strutils import join +from stew/bitops2 import log2trunc +from stew/byteutils import to0xHex +from ./altair import + EpochParticipationFlags, InactivityScores, SyncAggregate, SyncCommittee, + TrustedSyncAggregate, SyncnetBits, num_active_participants +from ./bellatrix import BloomLogs, ExecutionAddress, Transaction +from ./capella import + ExecutionBranch, HistoricalSummary, SignedBLSToExecutionChange, + SignedBLSToExecutionChangeList, Withdrawal, EXECUTION_PAYLOAD_GINDEX +from ./deneb import Blobs, BlobsBundle, KzgCommitments, KzgProofs + +export json_serialization, base + +const + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/polynomial-commitments-sampling.md#cells + FIELD_ELEMENTS_PER_EXT_BLOB* = 2 * kzg_abi.FIELD_ELEMENTS_PER_BLOB + # Number of field elements in a Reed-Solomon extended blob | + FIELD_ELEMENTS_PER_CELL* = 64 # Number of field elements in a cell | + BYTES_PER_CELL* = FIELD_ELEMENTS_PER_CELL * kzg_abi.BYTES_PER_FIELD_ELEMENT + # The number of bytes in a cell | + CELLS_PER_EXT_BLOB* = FIELD_ELEMENTS_PER_EXT_BLOB div FIELD_ELEMENTS_PER_CELL + # The number of cells in an extended blob | + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/p2p-interface.md#preset + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH* = 4 + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH_GINDEX* = 27 + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/das-core.md#data-size + NUMBER_OF_COLUMNS* = 128 + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/das-core.md#networking + DATA_COLUMN_SIDECAR_SUBNET_COUNT* = 128 + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/das-core.md#custody-setting + SAMPLES_PER_SLOT* = 8 + CUSTODY_REQUIREMENT* = 4 + +type + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.6/specs/_features/eip7594/polynomial-commitments-sampling.md#custom-types + BLSFieldElement* = KzgBytes32 + G2Point* = array[96, byte] + PolynomialCoeff* = List[BLSFieldElement, FIELD_ELEMENTS_PER_EXT_BLOB] + Coset* = array[FIELD_ELEMENTS_PER_CELL, BLSFieldElement] + CosetEvals* = array[FIELD_ELEMENTS_PER_CELL, BLSFieldElement] + Cell* = KzgCell + Cells* = KzgCells + CellsAndProofs* = KzgCellsAndKzgProofs + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/das-core.md#custom-types + RowIndex* = uint64 + ColumnIndex* = uint64 + CellIndex* = uint64 + + +type + DataColumn* = List[KzgCell, Limit(MAX_BLOB_COMMITMENTS_PER_BLOCK)] + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/_features/eip7594/das-core.md#datacolumnsidecar + DataColumnSidecar* = object + index*: ColumnIndex # Index of column in extended matrix + column*: DataColumn + kzg_commitments*: KzgCommitments + kzg_proofs*: KzgProofs + signed_block_header*: SignedBeaconBlockHeader + kzg_commitments_inclusion_proof*: + array[KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH, Eth2Digest] + + DataColumnSidecars* = seq[ref DataColumnSidecar] + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/p2p-interface.md#datacolumnidentifier + DataColumnIdentifier* = object + block_root*: Eth2Digest + index*: ColumnIndex + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.6/specs/_features/eip7594/das-core.md#matrixentry + MatrixEntry* = object + cell*: Cell + kzg_proof*: KzgProof + column_index*: ColumnIndex + row_index*: RowIndex + + # Not in spec, defined in order to compute custody subnets + CscBits* = BitArray[DATA_COLUMN_SIDECAR_SUBNET_COUNT] + + CscCount* = uint8 + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/_features/eip7594/p2p-interface.md#metadata + MetaData* = object + seq_number*: uint64 + attnets*: AttnetBits + syncnets*: SyncnetBits + custody_subnet_count*: CscCount + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/deneb/beacon-chain.md#executionpayload + ExecutionPayload* = object + # Execution block header fields + parent_hash*: Eth2Digest + fee_recipient*: ExecutionAddress + ## 'beneficiary' in the yellow paper + state_root*: Eth2Digest + receipts_root*: Eth2Digest + logs_bloom*: BloomLogs + prev_randao*: Eth2Digest + ## 'difficulty' in the yellow paper + block_number*: uint64 + ## 'number' in the yellow paper + gas_limit*: uint64 + gas_used*: uint64 + timestamp*: uint64 + extra_data*: List[byte, MAX_EXTRA_DATA_BYTES] + base_fee_per_gas*: UInt256 + + # Extra payload fields + block_hash*: Eth2Digest # Hash of execution block + transactions*: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] + withdrawals*: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] + blob_gas_used*: uint64 + excess_blob_gas*: uint64 + + ExecutionPayloadForSigning* = object + executionPayload*: ExecutionPayload + blockValue*: Wei + blobsBundle*: BlobsBundle + executionRequests*: array[3, seq[byte]] + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/deneb/beacon-chain.md#executionpayloadheader + ExecutionPayloadHeader* = object + # Execution block header fields + parent_hash*: Eth2Digest + fee_recipient*: ExecutionAddress + state_root*: Eth2Digest + receipts_root*: Eth2Digest + logs_bloom*: BloomLogs + prev_randao*: Eth2Digest + block_number*: uint64 + gas_limit*: uint64 + gas_used*: uint64 + timestamp*: uint64 + extra_data*: List[byte, MAX_EXTRA_DATA_BYTES] + base_fee_per_gas*: UInt256 + + # Extra payload fields + block_hash*: Eth2Digest + ## Hash of execution block + transactions_root*: Eth2Digest + withdrawals_root*: Eth2Digest + blob_gas_used*: uint64 + excess_blob_gas*: uint64 + + ExecutePayload* = proc( + execution_payload: ExecutionPayload): bool {.gcsafe, raises: [].} + + FinalityBranch* = + array[log2trunc(FINALIZED_ROOT_GINDEX_ELECTRA), Eth2Digest] + + CurrentSyncCommitteeBranch* = + array[log2trunc(CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA), Eth2Digest] + + NextSyncCommitteeBranch* = + array[log2trunc(NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA), Eth2Digest] + + # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/capella/light-client/sync-protocol.md#modified-lightclientheader + LightClientHeader* = object + beacon*: BeaconBlockHeader + ## Beacon block header + + execution*: ExecutionPayloadHeader + ## Execution payload header corresponding to `beacon.body_root` (from Capella onward) + execution_branch*: capella.ExecutionBranch + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/altair/light-client/sync-protocol.md#lightclientbootstrap + LightClientBootstrap* = object + header*: LightClientHeader + ## Header matching the requested beacon block root + + current_sync_committee*: SyncCommittee + ## Current sync committee corresponding to `header.beacon.state_root` + current_sync_committee_branch*: CurrentSyncCommitteeBranch + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/altair/light-client/sync-protocol.md#lightclientupdate + LightClientUpdate* = object + attested_header*: LightClientHeader + ## Header attested to by the sync committee + + next_sync_committee*: SyncCommittee + ## Next sync committee corresponding to + ## `attested_header.beacon.state_root` + next_sync_committee_branch*: NextSyncCommitteeBranch + + # Finalized header corresponding to `attested_header.beacon.state_root` + finalized_header*: LightClientHeader + finality_branch*: FinalityBranch + + sync_aggregate*: SyncAggregate + ## Sync committee aggregate signature + signature_slot*: Slot + ## Slot at which the aggregate signature was created (untrusted) + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/altair/light-client/sync-protocol.md#lightclientfinalityupdate + LightClientFinalityUpdate* = object + # Header attested to by the sync committee + attested_header*: LightClientHeader + + # Finalized header corresponding to `attested_header.beacon.state_root` + finalized_header*: LightClientHeader + finality_branch*: FinalityBranch + + # Sync committee aggregate signature + sync_aggregate*: SyncAggregate + # Slot at which the aggregate signature was created (untrusted) + signature_slot*: Slot + + # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate + LightClientOptimisticUpdate* = object + # Header attested to by the sync committee + attested_header*: LightClientHeader + + # Sync committee aggregate signature + sync_aggregate*: SyncAggregate + # Slot at which the aggregate signature was created (untrusted) + signature_slot*: Slot + + SomeLightClientUpdateWithSyncCommittee* = + LightClientUpdate + + SomeLightClientUpdateWithFinality* = + LightClientUpdate | + LightClientFinalityUpdate + + SomeLightClientUpdate* = + LightClientUpdate | + LightClientFinalityUpdate | + LightClientOptimisticUpdate + + SomeLightClientObject* = + LightClientBootstrap | + SomeLightClientUpdate + + # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/altair/light-client/sync-protocol.md#lightclientstore + LightClientStore* = object + finalized_header*: LightClientHeader + ## Header that is finalized + + current_sync_committee*: SyncCommittee + ## Sync committees corresponding to the finalized header + next_sync_committee*: SyncCommittee + + best_valid_update*: Opt[LightClientUpdate] + ## Best available header to switch finalized head to + ## if we see nothing else + + optimistic_header*: LightClientHeader + ## Most recent available reasonably-safe header + + previous_max_active_participants*: uint64 + ## Max number of active participants in a sync committee + ## (used to compute safety threshold) + current_max_active_participants*: uint64 + + # https://github.com/ethereum/consensus-specs/blob/82133085a1295e93394ebdf71df8f2f6e0962588/specs/electra/beacon-chain.md#beaconstate + BeaconState* = object + # Versioning + genesis_time*: uint64 + genesis_validators_root*: Eth2Digest + slot*: Slot + fork*: Fork + + # History + latest_block_header*: BeaconBlockHeader + ## `latest_block_header.state_root == ZERO_HASH` temporarily + + block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] + ## Needed to process attestations, older to newer + + state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] + historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT] + ## Frozen in Capella, replaced by historical_summaries + + # Eth1 + eth1_data*: Eth1Data + eth1_data_votes*: + HashList[Eth1Data, Limit(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH)] + eth1_deposit_index*: uint64 + + # Registry + validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT] + balances*: HashList[Gwei, Limit VALIDATOR_REGISTRY_LIMIT] + + # Randomness + randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest] + + # Slashings + slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei] + ## Per-epoch sums of slashed effective balances + + # Participation + previous_epoch_participation*: EpochParticipationFlags + current_epoch_participation*: EpochParticipationFlags + + # Finality + justification_bits*: JustificationBits + ## Bit set for every recent justified epoch + + previous_justified_checkpoint*: Checkpoint + current_justified_checkpoint*: Checkpoint + finalized_checkpoint*: Checkpoint + + # Inactivity + inactivity_scores*: InactivityScores + + # Light client sync committees + current_sync_committee*: SyncCommittee + next_sync_committee*: SyncCommittee + + # Execution + latest_execution_payload_header*: ExecutionPayloadHeader + ## [Modified in Electra:EIP6110:EIP7002] + + # Withdrawals + next_withdrawal_index*: WithdrawalIndex + next_withdrawal_validator_index*: uint64 + + # Deep history valid from Capella onwards + historical_summaries*: + HashList[HistoricalSummary, Limit HISTORICAL_ROOTS_LIMIT] + + deposit_requests_start_index*: uint64 # [New in Electra:EIP6110] + deposit_balance_to_consume*: Gwei # [New in Electra:EIP7251] + exit_balance_to_consume*: Gwei # [New in Electra:EIP7251] + earliest_exit_epoch*: Epoch # [New in Electra:EIP7251] + consolidation_balance_to_consume*: Gwei # [New in Electra:EIP7251] + earliest_consolidation_epoch*: Epoch # [New in Electra:EIP7251] + pending_deposits*: HashList[PendingDeposit, Limit PENDING_DEPOSITS_LIMIT] + ## [New in Electra:EIP7251] + + # [New in Electra:EIP7251] + pending_partial_withdrawals*: + HashList[PendingPartialWithdrawal, Limit PENDING_PARTIAL_WITHDRAWALS_LIMIT] + pending_consolidations*: + HashList[PendingConsolidation, Limit PENDING_CONSOLIDATIONS_LIMIT] + ## [New in Electra:EIP7251] + + # TODO Careful, not nil analysis is broken / incomplete and the semantics will + # likely change in future versions of the language: + # https://github.com/nim-lang/RFCs/issues/250 + BeaconStateRef* = ref BeaconState not nil + NilableBeaconStateRef* = ref BeaconState + + # TODO: There should be only a single generic HashedBeaconState definition + HashedBeaconState* = object + data*: BeaconState + root*: Eth2Digest # hash_tree_root(data) + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/phase0/beacon-chain.md#beaconblock + BeaconBlock* = object + ## For each slot, a proposer is chosen from the validator pool to propose + ## a new block. Once the block as been proposed, it is transmitted to + ## validators that will have a chance to vote on it through attestations. + ## Each block collects attestations, or votes, on past blocks, thus a chain + ## is formed. + + slot*: Slot + proposer_index*: uint64 # `ValidatorIndex` after validation + + parent_root*: Eth2Digest + ## Root hash of the previous block + + state_root*: Eth2Digest + ## The state root, _after_ this block has been processed + + body*: BeaconBlockBody + + SigVerifiedBeaconBlock* = object + ## A BeaconBlock that contains verified signatures + ## but that has not been verified for state transition + + slot*: Slot + proposer_index*: uint64 # `ValidatorIndex` after validation + + parent_root*: Eth2Digest + ## Root hash of the previous block + + state_root*: Eth2Digest + ## The state root, _after_ this block has been processed + + body*: SigVerifiedBeaconBlockBody + + TrustedBeaconBlock* = object + ## When we receive blocks from outside sources, they are untrusted and go + ## through several layers of validation. Blocks that have gone through + ## validations can be trusted to be well-formed, with a correct signature, + ## having a parent and applying cleanly to the state that their parent + ## left them with. + ## + ## When loading such blocks from the database, to rewind states for example, + ## it is expensive to redo the validations (in particular, the signature + ## checks), thus `TrustedBlock` uses a `TrustedSig` type to mark that these + ## checks can be skipped. + ## + ## TODO this could probably be solved with some type trickery, but there + ## too many bugs in nim around generics handling, and we've used up + ## the trickery budget in the serialization library already. Until + ## then, the type must be manually kept compatible with its untrusted + ## cousin. + slot*: Slot + proposer_index*: uint64 # `ValidatorIndex` after validation + parent_root*: Eth2Digest + state_root*: Eth2Digest + body*: TrustedBeaconBlockBody + + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.6/specs/electra/beacon-chain.md#beaconblockbody + BeaconBlockBody* = object + randao_reveal*: ValidatorSig + eth1_data*: Eth1Data + ## Eth1 data vote + + graffiti*: GraffitiBytes + ## Arbitrary data + + # Operations + proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS] + attester_slashings*: + List[electra.AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS_ELECTRA] + ## [Modified in Electra:EIP7549] + attestations*: List[electra.Attestation, Limit MAX_ATTESTATIONS_ELECTRA] + ## [Modified in Electra:EIP7549] + deposits*: List[Deposit, Limit MAX_DEPOSITS] + voluntary_exits*: List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] + + sync_aggregate*: SyncAggregate + + # Execution + execution_payload*: fulu.ExecutionPayload # [Modified in Electra:EIP6110:EIP7002] + bls_to_execution_changes*: SignedBLSToExecutionChangeList + blob_kzg_commitments*: KzgCommitments + execution_requests*: ExecutionRequests # [New in Electra] + + SigVerifiedBeaconBlockBody* = object + ## A BeaconBlock body with signatures verified + ## including: + ## - Randao reveal + ## - Attestations + ## - ProposerSlashing (SignedBeaconBlockHeader) + ## - AttesterSlashing (IndexedAttestation) + ## - SignedVoluntaryExits + ## - SyncAggregate + ## + ## However: + ## - ETH1Data (Deposits) can contain invalid BLS signatures + ## + ## The block state transition has NOT been verified + randao_reveal*: TrustedSig + eth1_data*: Eth1Data + ## Eth1 data vote + + graffiti*: GraffitiBytes + ## Arbitrary data + + # Operations + proposer_slashings*: + List[TrustedProposerSlashing, Limit MAX_PROPOSER_SLASHINGS] + attester_slashings*: + List[electra.TrustedAttesterSlashing, Limit MAX_ATTESTER_SLASHINGS_ELECTRA] + ## [Modified in Electra:EIP7549] + attestations*: List[electra.TrustedAttestation, Limit MAX_ATTESTATIONS_ELECTRA] + ## [Modified in Electra:EIP7549] + deposits*: List[Deposit, Limit MAX_DEPOSITS] + voluntary_exits*: List[TrustedSignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] + + sync_aggregate*: TrustedSyncAggregate + + # Execution + execution_payload*: ExecutionPayload # [Modified in Electra:EIP6110:EIP7002] + bls_to_execution_changes*: SignedBLSToExecutionChangeList + blob_kzg_commitments*: KzgCommitments + execution_requests*: ExecutionRequests # [New in Electra] + + TrustedBeaconBlockBody* = object + ## A full verified block + randao_reveal*: TrustedSig + eth1_data*: Eth1Data + ## Eth1 data vote + + graffiti*: GraffitiBytes + ## Arbitrary data + + # Operations + proposer_slashings*: + List[TrustedProposerSlashing, Limit MAX_PROPOSER_SLASHINGS] + attester_slashings*: + List[electra.TrustedAttesterSlashing, Limit MAX_ATTESTER_SLASHINGS_ELECTRA] + ## [Modified in Electra:EIP7549] + attestations*: List[electra.TrustedAttestation, Limit MAX_ATTESTATIONS_ELECTRA] + ## [Modified in Electra:EIP7549] + deposits*: List[Deposit, Limit MAX_DEPOSITS] + voluntary_exits*: List[TrustedSignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] + + sync_aggregate*: TrustedSyncAggregate + + # Execution + execution_payload*: ExecutionPayload # [Modified in Electra:EIP6110:EIP7002] + bls_to_execution_changes*: SignedBLSToExecutionChangeList + blob_kzg_commitments*: KzgCommitments + execution_requests*: ExecutionRequests # [New in Electra] + + # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.6/specs/phase0/beacon-chain.md#signedbeaconblock + SignedBeaconBlock* = object + message*: BeaconBlock + signature*: ValidatorSig + + root* {.dontSerialize.}: Eth2Digest # cached root of signed beacon block + + SigVerifiedSignedBeaconBlock* = object + ## A SignedBeaconBlock with signatures verified + ## including: + ## - Block signature + ## - BeaconBlockBody + ## - Randao reveal + ## - Attestations + ## - ProposerSlashing (SignedBeaconBlockHeader) + ## - AttesterSlashing (IndexedAttestation) + ## - SignedVoluntaryExits + ## + ## - ETH1Data (Deposits) can contain invalid BLS signatures + ## + ## The block state transition has NOT been verified + message*: SigVerifiedBeaconBlock + signature*: TrustedSig + + root* {.dontSerialize.}: Eth2Digest # cached root of signed beacon block + + MsgTrustedSignedBeaconBlock* = object + message*: TrustedBeaconBlock + signature*: ValidatorSig + + root* {.dontSerialize.}: Eth2Digest # cached root of signed beacon block + + TrustedSignedBeaconBlock* = object + message*: TrustedBeaconBlock + signature*: TrustedSig + + root* {.dontSerialize.}: Eth2Digest # cached root of signed beacon block + + SomeSignedBeaconBlock* = + SignedBeaconBlock | + SigVerifiedSignedBeaconBlock | + MsgTrustedSignedBeaconBlock | + TrustedSignedBeaconBlock + SomeBeaconBlock* = + BeaconBlock | + SigVerifiedBeaconBlock | + TrustedBeaconBlock + SomeBeaconBlockBody* = + BeaconBlockBody | + SigVerifiedBeaconBlockBody | + TrustedBeaconBlockBody + + BlockContents* = object + `block`*: BeaconBlock + kzg_proofs*: KzgProofs + blobs*: Blobs + +func shortLog*(v: DataColumnSidecar): auto = + ( + index: v.index, + kzg_commitments: v.kzg_commitments.len, + kzg_proofs: v.kzg_proofs.len, + block_header: shortLog(v.signed_block_header.message), + ) + +func shortLog*(v: seq[DataColumnSidecar]): auto = + "[" & v.mapIt(shortLog(it)).join(", ") & "]" + +func shortLog*(x: seq[DataColumnIdentifier]): string = + "[" & x.mapIt(shortLog(it.block_root) & "/" & $it.index).join(", ") & "]" + +func shortLog*(x: seq[ColumnIndex]): string = + "<" & x.mapIt($it).join(", ") & ">" + +# TODO: There should be only a single generic HashedBeaconState definition +func initHashedBeaconState*(s: BeaconState): HashedBeaconState = + HashedBeaconState(data: s) + +func shortLog*(v: SomeBeaconBlock): auto = + ( + slot: shortLog(v.slot), + proposer_index: v.proposer_index, + parent_root: shortLog(v.parent_root), + state_root: shortLog(v.state_root), + eth1data: v.body.eth1_data, + graffiti: $v.body.graffiti, + proposer_slashings_len: v.body.proposer_slashings.len(), + attester_slashings_len: v.body.attester_slashings.len(), + attestations_len: v.body.attestations.len(), + deposits_len: v.body.deposits.len(), + voluntary_exits_len: v.body.voluntary_exits.len(), + sync_committee_participants: v.body.sync_aggregate.num_active_participants, + block_number: v.body.execution_payload.block_number, + # TODO checksum hex? shortlog? + block_hash: to0xHex(v.body.execution_payload.block_hash.data), + parent_hash: to0xHex(v.body.execution_payload.parent_hash.data), + fee_recipient: to0xHex(v.body.execution_payload.fee_recipient.data), + bls_to_execution_changes_len: v.body.bls_to_execution_changes.len(), + blob_kzg_commitments_len: v.body.blob_kzg_commitments.len(), + ) + +func shortLog*(v: SomeSignedBeaconBlock): auto = + ( + blck: shortLog(v.message), + signature: shortLog(v.signature) + ) + +func shortLog*(v: ExecutionPayload): auto = + ( + parent_hash: shortLog(v.parent_hash), + fee_recipient: $v.fee_recipient, + state_root: shortLog(v.state_root), + receipts_root: shortLog(v.receipts_root), + prev_randao: shortLog(v.prev_randao), + block_number: v.block_number, + gas_limit: v.gas_limit, + gas_used: v.gas_used, + timestamp: v.timestamp, + extra_data: toPrettyString(distinctBase v.extra_data), + base_fee_per_gas: $(v.base_fee_per_gas), + block_hash: shortLog(v.block_hash), + num_transactions: len(v.transactions), + num_withdrawals: len(v.withdrawals), + blob_gas_used: $(v.blob_gas_used), + excess_blob_gas: $(v.excess_blob_gas) + ) + +template asSigned*( + x: SigVerifiedSignedBeaconBlock | + MsgTrustedSignedBeaconBlock | + TrustedSignedBeaconBlock): SignedBeaconBlock = + isomorphicCast[SignedBeaconBlock](x) + +template asSigVerified*( + x: SignedBeaconBlock | + MsgTrustedSignedBeaconBlock | + TrustedSignedBeaconBlock): SigVerifiedSignedBeaconBlock = + isomorphicCast[SigVerifiedSignedBeaconBlock](x) + +template asSigVerified*( + x: BeaconBlock | TrustedBeaconBlock): SigVerifiedBeaconBlock = + isomorphicCast[SigVerifiedBeaconBlock](x) + +template asMsgTrusted*( + x: SignedBeaconBlock | + SigVerifiedSignedBeaconBlock | + TrustedSignedBeaconBlock): MsgTrustedSignedBeaconBlock = + isomorphicCast[MsgTrustedSignedBeaconBlock](x) + +template asTrusted*( + x: SignedBeaconBlock | + SigVerifiedSignedBeaconBlock | + MsgTrustedSignedBeaconBlock): TrustedSignedBeaconBlock = + isomorphicCast[TrustedSignedBeaconBlock](x) \ No newline at end of file diff --git a/beacon_chain/spec/eip7594_helpers.nim b/beacon_chain/spec/eip7594_helpers.nim index 3260ea601d..42c1bd8a11 100644 --- a/beacon_chain/spec/eip7594_helpers.nim +++ b/beacon_chain/spec/eip7594_helpers.nim @@ -18,11 +18,11 @@ import types], ./crypto, ./[helpers, digest], - ./datatypes/[eip7594] + ./datatypes/[fulu] type - CellBytes = array[eip7594.CELLS_PER_EXT_BLOB, Cell] - ProofBytes = array[eip7594.CELLS_PER_EXT_BLOB, KzgProof] + CellBytes = array[fulu.CELLS_PER_EXT_BLOB, Cell] + ProofBytes = array[fulu.CELLS_PER_EXT_BLOB, KzgProof] func sortedColumnIndices*(columnsPerSubnet: ColumnIndex, subnetIds: HashSet[uint64]): @@ -119,7 +119,7 @@ proc compute_matrix*(blobs: seq[KzgBlob]): Result[seq[MatrixEntry], cstring] = if cellsAndProofs.isErr: return err("Computing Extended Matrix: Issue computing cells and proofs") - for i in 0..")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) + ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck))) else: err(RestErrorMessage.init(Http415, "Invalid content type", [version, $body.contentType])) @@ -3042,6 +3108,23 @@ proc decodeBody*( except CatchableError as exc: return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, [version, $exc.msg])) + of ConsensusFork.Fulu: + try: + var res = RestJson.decode(body.data, FuluSignedBlockContents, + requireAllFields = true, + allowUnknownFields = true) + res.signed_block.root = hash_tree_root(res.signed_block.message) + RestPublishedSignedBlockContents( + kind: ConsensusFork.Fulu, fuluData: res) + except SerializationError as exc: + debug "Failed to decode JSON data", + err = exc.formatMsg(""), + data = string.fromBytes(body.data) + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(data) elif body.contentType == OctetStreamMediaType: @@ -3133,6 +3216,20 @@ proc decodeBody*( [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Electra, electraData: blckContents)) + of ConsensusFork.Fulu: + let blckContents = + try: + var res = SSZ.decode(body.data, FuluSignedBlockContents) + res.signed_block.root = hash_tree_root(res.signed_block.message) + res + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) + ok(RestPublishedSignedBlockContents( + kind: ConsensusFork.Fulu, fuluData: blckContents)) else: err(RestErrorMessage.init(Http415, "Invalid content type", [version, $body.contentType])) diff --git a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim index 20122f63af..2d4865d32f 100644 --- a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim +++ b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim @@ -26,7 +26,8 @@ type bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock | DenebSignedBlockContents | - ElectraSignedBlockContents + ElectraSignedBlockContents | + FuluSignedBlockContents proc getGenesis*(): RestResponse[GetGenesisResponse] {. rest, endpoint: "/eth/v1/beacon/genesis", @@ -163,6 +164,11 @@ proc publishBlock*(body: ElectraSignedBlockContents): RestPlainResponse {. meth: MethodPost.} ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock +proc publishBlock*(body: FuluSignedBlockContents): RestPlainResponse {. + rest, endpoint: "/eth/v1/beacon/blocks", + meth: MethodPost.} + ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock + proc publishSszBlock*( client: RestClientRef, blck: ForkySignedBeaconBlock @@ -217,6 +223,14 @@ proc publishBlockV2( meth: MethodPost.} ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2 +proc publishBlockV2( + broadcast_validation: Option[BroadcastValidationType], + body: FuluSignedBlockContents +): RestPlainResponse {.rest, endpoint: "/eth/v2/beacon/blocks", + meth: MethodPost.} + ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2 + + proc publishBlockV2*( client: RestClientRef, broadcast_validation: Option[BroadcastValidationType], @@ -229,6 +243,8 @@ proc publishBlockV2*( ConsensusFork.Deneb.toString() elif blck is ElectraSignedBlockContents: ConsensusFork.Electra.toString() + elif blck is FuluSignedBlockContents: + ConsensusFork.Fulu.toString() else: typeof(blck).kind.toString() client.publishBlockV2( @@ -248,6 +264,8 @@ proc publishSszBlockV2*( ConsensusFork.Deneb.toString() elif blck is ElectraSignedBlockContents: ConsensusFork.Electra.toString() + elif blck is FuluSignedBlockContents: + ConsensusFork.Fulu.toString() else: typeof(blck).kind.toString() client.publishBlockV2( @@ -290,6 +308,12 @@ proc publishBlindedBlock*(body: electra_mev.SignedBlindedBeaconBlock): meth: MethodPost.} ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock +proc publishBlindedBlock*(body: fulu_mev.SignedBlindedBeaconBlock): + RestPlainResponse {. + rest, endpoint: "/eth/v1/beacon/blinded_blocks", + meth: MethodPost.} + ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock + proc publishSszBlindedBlock*( client: RestClientRef, blck: ForkySignedBeaconBlock @@ -344,6 +368,13 @@ proc publishBlindedBlockV2*( meth: MethodPost.} ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock +proc publishBlindedBlockV2*( + broadcast_validation: Option[BroadcastValidationType], + body: fulu_mev.SignedBlindedBeaconBlock +): RestPlainResponse {.rest, endpoint: "/eth/v2/beacon/blinded_blocks", + meth: MethodPost.} + ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock + proc publishBlindedBlockV2*( client: RestClientRef, broadcast_validation: Option[BroadcastValidationType], diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index 9ba40f4f46..06182eb2b1 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, ".."/[deposit_snapshots, forks], - ".."/mev/deneb_mev + ".."/mev/[deneb_mev] export forks, phase0, altair, bellatrix, capella, deneb_mev, tables, httputils @@ -309,6 +309,11 @@ type kzg_proofs*: deneb.KzgProofs blobs*: deneb.Blobs + FuluSignedBlockContents* = object + signed_block*: fulu.SignedBeaconBlock + kzg_proofs*: deneb.KzgProofs + blobs*: deneb.Blobs + RestPublishedSignedBlockContents* = object case kind*: ConsensusFork of ConsensusFork.Phase0: phase0Data*: phase0.SignedBeaconBlock @@ -317,6 +322,7 @@ type of ConsensusFork.Capella: capellaData*: capella.SignedBeaconBlock of ConsensusFork.Deneb: denebData*: DenebSignedBlockContents of ConsensusFork.Electra: electraData*: ElectraSignedBlockContents + of ConsensusFork.Fulu: fuluData*: FuluSignedBlockContents ProduceBlockResponseV3* = ForkedMaybeBlindedBeaconBlock @@ -514,6 +520,7 @@ type GetGenesisResponse* = DataEnclosedObject[RestGenesis] GetHeaderResponseDeneb* = DataVersionEnclosedObject[deneb_mev.SignedBuilderBid] GetHeaderResponseElectra* = DataVersionEnclosedObject[electra_mev.SignedBuilderBid] + GetHeaderResponseFulu* = DataVersionEnclosedObject[fulu_mev.SignedBuilderBid] GetNetworkIdentityResponse* = DataEnclosedObject[RestNetworkIdentity] GetPeerCountResponse* = DataMetaEnclosedObject[RestPeerCount] GetPeerResponse* = DataMetaEnclosedObject[RestNodePeer] @@ -541,6 +548,7 @@ type ProduceSyncCommitteeContributionResponse* = DataEnclosedObject[SyncCommitteeContribution] SubmitBlindedBlockResponseDeneb* = DataEnclosedObject[deneb_mev.ExecutionPayloadAndBlobsBundle] SubmitBlindedBlockResponseElectra* = DataEnclosedObject[electra_mev.ExecutionPayloadAndBlobsBundle] + SubmitBlindedBlockResponseFulu* = DataEnclosedObject[fulu_mev.ExecutionPayloadAndBlobsBundle] GetValidatorsActivityResponse* = DataEnclosedObject[seq[RestActivityItem]] GetValidatorsLivenessResponse* = DataEnclosedObject[seq[RestLivenessItem]] SubmitBeaconCommitteeSelectionsResponse* = DataEnclosedObject[seq[RestBeaconCommitteeSelection]] @@ -597,6 +605,12 @@ func `==`*(a, b: RestValidatorIndex): bool {.borrow.} template withForkyBlck*( x: RestPublishedSignedBlockContents, body: untyped): untyped = case x.kind + of ConsensusFork.Fulu: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + template forkyBlck: untyped {.inject, used.} = x.fuluData.signed_block + template kzg_proofs: untyped {.inject, used.} = x.fuluData.kzg_proofs + template blobs: untyped {.inject, used.} = x.fuluData.blobs + body of ConsensusFork.Electra: const consensusFork {.inject, used.} = ConsensusFork.Electra template forkyBlck: untyped {.inject, used.} = x.electraData.signed_block @@ -642,6 +656,8 @@ func init*(T: type ForkedSignedBeaconBlock, ForkedSignedBeaconBlock.init(contents.denebData.signed_block) of ConsensusFork.Electra: ForkedSignedBeaconBlock.init(contents.electraData.signed_block) + of ConsensusFork.Fulu: + ForkedSignedBeaconBlock.init(contents.fuluData.signed_block) func init*(t: typedesc[RestPublishedSignedBlockContents], blck: phase0.BeaconBlock, root: Eth2Digest, @@ -715,6 +731,22 @@ func init*(t: typedesc[RestPublishedSignedBlockContents], ) ) +func init*(t: typedesc[RestPublishedSignedBlockContents], + contents: fulu.BlockContents, root: Eth2Digest, + signature: ValidatorSig): RestPublishedSignedBlockContents = + RestPublishedSignedBlockContents( + kind: ConsensusFork.Fulu, + fuluData: FuluSignedBlockContents( + signed_block: fulu.SignedBeaconBlock( + message: contents.`block`, + root: root, + signature: signature + ), + kzg_proofs: contents.kzg_proofs, + blobs: contents.blobs + ) + ) + func init*(t: typedesc[StateIdent], v: StateIdentType): StateIdent = StateIdent(kind: StateQueryKind.Named, value: v) diff --git a/beacon_chain/spec/eth2_merkleization.nim b/beacon_chain/spec/eth2_merkleization.nim index 6ae66c8337..f7cee32d66 100644 --- a/beacon_chain/spec/eth2_merkleization.nim +++ b/beacon_chain/spec/eth2_merkleization.nim @@ -22,6 +22,7 @@ from ./datatypes/bellatrix import HashedBeaconState, SignedBeaconBlock from ./datatypes/capella import HashedBeaconState, SignedBeaconBlock from ./datatypes/deneb import HashedBeaconState, SignedBeaconBlock from ./datatypes/electra import HashedBeaconState, SignedBeaconBlock +from ./datatypes/fulu import HashedBeaconState, SignedBeaconBlock export ssz_codec, merkleization, proofs @@ -33,13 +34,15 @@ type func hash_tree_root*( x: phase0.HashedBeaconState | altair.HashedBeaconState | bellatrix.HashedBeaconState | capella.HashedBeaconState | - deneb.HashedBeaconState | electra.SignedBeaconBlock) {. + deneb.HashedBeaconState | electra.SignedBeaconBlock | + fulu.SignedBeaconBlock) {. error: "HashedBeaconState should not be hashed".} func hash_tree_root*( x: phase0.SignedBeaconBlock | altair.SignedBeaconBlock | bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock | - deneb.SignedBeaconBlock | electra.SignedBeaconBlock) {. + deneb.SignedBeaconBlock | electra.SignedBeaconBlock | + fulu.SignedBeaconBlock) {. error: "SignedBeaconBlock should not be hashed".} func depositCountBytes*(x: uint64): array[32, byte] = diff --git a/beacon_chain/spec/eth2_ssz_serialization.nim b/beacon_chain/spec/eth2_ssz_serialization.nim index a3d3a1f8f5..4d7af53ca4 100644 --- a/beacon_chain/spec/eth2_ssz_serialization.nim +++ b/beacon_chain/spec/eth2_ssz_serialization.nim @@ -18,6 +18,7 @@ import from ./datatypes/deneb import SignedBeaconBlock, TrustedSignedBeaconBlock from ./datatypes/electra import SignedBeaconBlock, TrustedSignedBeaconBlock +from ./datatypes/fulu import SignedBeaconBlock, TrustedSignedBeaconBlock export phase0, altair, ssz_codec, ssz_serialization, eth2_merkleization @@ -67,6 +68,12 @@ template readSszBytes*( template readSszBytes*( data: openArray[byte], val: var electra.TrustedSignedBeaconBlock, updateRoot = true) = readAndUpdateRoot(data, val, updateRoot) +template readSszBytes*( + data: openArray[byte], val: var fulu.SignedBeaconBlock, updateRoot = true) = + readAndUpdateRoot(data, val, updateRoot) +template readSszBytes*( + data: openArray[byte], val: var fulu.TrustedSignedBeaconBlock, updateRoot = true) = + readAndUpdateRoot(data, val, updateRoot) template readSszBytes*( data: openArray[byte], val: var auto, updateRoot: bool) = diff --git a/beacon_chain/spec/forks.nim b/beacon_chain/spec/forks.nim index edffecff85..1b5a97b0f7 100644 --- a/beacon_chain/spec/forks.nim +++ b/beacon_chain/spec/forks.nim @@ -16,13 +16,13 @@ import "."/[ block_id, eth2_merkleization, eth2_ssz_serialization, forks_light_client, presets], - ./datatypes/[phase0, altair, bellatrix, capella, deneb, electra, eip7594], - ./mev/[bellatrix_mev, capella_mev, deneb_mev, electra_mev] + ./datatypes/[phase0, altair, bellatrix, capella, deneb, electra, fulu], + ./mev/[bellatrix_mev, capella_mev, deneb_mev, electra_mev, fulu_mev] export extras, block_id, phase0, altair, bellatrix, capella, deneb, electra, - eip7594, eth2_merkleization, eth2_ssz_serialization, forks_light_client, - presets, deneb_mev, electra_mev + fulu, eth2_merkleization, eth2_ssz_serialization, forks_light_client, + presets, deneb_mev, electra_mev, fulu_mev # This file contains helpers for dealing with forks - we have two ways we can # deal with forks: @@ -47,7 +47,8 @@ type Bellatrix, Capella, Deneb, - Electra + Electra, + Fulu ForkyBeaconState* = phase0.BeaconState | @@ -55,7 +56,8 @@ type bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | - electra.BeaconState + electra.BeaconState | + fulu.BeaconState ForkyHashedBeaconState* = phase0.HashedBeaconState | @@ -63,7 +65,8 @@ type bellatrix.HashedBeaconState | capella.HashedBeaconState | deneb.HashedBeaconState | - electra.HashedBeaconState + electra.HashedBeaconState | + fulu.HashedBeaconState ForkedHashedBeaconState* = object case kind*: ConsensusFork @@ -73,18 +76,21 @@ type of ConsensusFork.Capella: capellaData*: capella.HashedBeaconState of ConsensusFork.Deneb: denebData*: deneb.HashedBeaconState of ConsensusFork.Electra: electraData*: electra.HashedBeaconState + of ConsensusFork.Fulu: fuluData*: fulu.HashedBeaconState ForkyExecutionPayload* = bellatrix.ExecutionPayload | capella.ExecutionPayload | deneb.ExecutionPayload | - electra.ExecutionPayload + electra.ExecutionPayload | + fulu.ExecutionPayload ForkyExecutionPayloadHeader* = bellatrix.ExecutionPayloadHeader | capella.ExecutionPayloadHeader | deneb.ExecutionPayloadHeader | - electra.ExecutionPayloadHeader + electra.ExecutionPayloadHeader | + fulu.ExecutionPayloadHeader ForkyBeaconBlockBody* = phase0.BeaconBlockBody | @@ -92,7 +98,8 @@ type bellatrix.BeaconBlockBody | capella.BeaconBlockBody | deneb.BeaconBlockBody | - electra.BeaconBlockBody + electra.BeaconBlockBody | + fulu.BeaconBlockBody ForkySigVerifiedBeaconBlockBody* = phase0.SigVerifiedBeaconBlockBody | @@ -100,7 +107,8 @@ type bellatrix.SigVerifiedBeaconBlockBody | capella.SigVerifiedBeaconBlockBody | deneb.SigVerifiedBeaconBlockBody | - electra.SigVerifiedBeaconBlockBody + electra.SigVerifiedBeaconBlockBody | + fulu.SigVerifiedBeaconBlockBody ForkyTrustedBeaconBlockBody* = phase0.TrustedBeaconBlockBody | @@ -108,7 +116,8 @@ type bellatrix.TrustedBeaconBlockBody | capella.TrustedBeaconBlockBody | deneb.TrustedBeaconBlockBody | - electra.TrustedBeaconBlockBody + electra.TrustedBeaconBlockBody | + fulu.TrustedBeaconBlockBody SomeForkyBeaconBlockBody* = ForkyBeaconBlockBody | @@ -121,7 +130,8 @@ type bellatrix.BeaconBlock | capella.BeaconBlock | deneb.BeaconBlock | - electra.BeaconBlock + electra.BeaconBlock | + fulu.BeaconBlock ForkySigVerifiedBeaconBlock* = phase0.SigVerifiedBeaconBlock | @@ -129,7 +139,8 @@ type bellatrix.SigVerifiedBeaconBlock | capella.SigVerifiedBeaconBlock | deneb.SigVerifiedBeaconBlock | - electra.SigVerifiedBeaconBlock + electra.SigVerifiedBeaconBlock | + fulu.SigVerifiedBeaconBlock ForkyTrustedBeaconBlock* = phase0.TrustedBeaconBlock | @@ -137,7 +148,8 @@ type bellatrix.TrustedBeaconBlock | capella.TrustedBeaconBlock | deneb.TrustedBeaconBlock | - electra.TrustedBeaconBlock + electra.TrustedBeaconBlock | + fulu.TrustedBeaconBlock SomeForkyBeaconBlock* = ForkyBeaconBlock | @@ -148,11 +160,13 @@ type bellatrix.ExecutionPayloadForSigning | capella.ExecutionPayloadForSigning | deneb.ExecutionPayloadForSigning | - electra.ExecutionPayloadForSigning + electra.ExecutionPayloadForSigning | + fulu.ExecutionPayloadForSigning ForkyBlindedBeaconBlock* = deneb_mev.BlindedBeaconBlock | - electra_mev.BlindedBeaconBlock + electra_mev.BlindedBeaconBlock | + fulu_mev.BlindedBeaconBlock ForkyAggregateAndProof* = phase0.AggregateAndProof | @@ -174,6 +188,7 @@ type of ConsensusFork.Capella: capellaData*: phase0.Attestation of ConsensusFork.Deneb: denebData*: phase0.Attestation of ConsensusFork.Electra: electraData*: electra.Attestation + of ConsensusFork.Fulu: fuluData*: electra.Attestation ForkedAggregateAndProof* = object case kind*: ConsensusFork @@ -183,6 +198,7 @@ type of ConsensusFork.Capella: capellaData*: phase0.AggregateAndProof of ConsensusFork.Deneb: denebData*: phase0.AggregateAndProof of ConsensusFork.Electra: electraData*: electra.AggregateAndProof + of ConsensusFork.Fulu: fuluData*: electra.AggregateAndProof ForkedBeaconBlock* = object case kind*: ConsensusFork @@ -192,6 +208,7 @@ type of ConsensusFork.Capella: capellaData*: capella.BeaconBlock of ConsensusFork.Deneb: denebData*: deneb.BeaconBlock of ConsensusFork.Electra: electraData*: electra.BeaconBlock + of ConsensusFork.Fulu: fuluData*: fulu.BeaconBlock ForkedMaybeBlindedBeaconBlock* = object case kind*: ConsensusFork @@ -207,6 +224,8 @@ type denebData*: deneb_mev.MaybeBlindedBeaconBlock of ConsensusFork.Electra: electraData*: electra_mev.MaybeBlindedBeaconBlock + of ConsensusFork.Fulu: + fuluData*: fulu_mev.MaybeBlindedBeaconBlock consensusValue*: Opt[UInt256] executionValue*: Opt[UInt256] @@ -222,6 +241,7 @@ type of ConsensusFork.Capella: capellaData*: capella_mev.BlindedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb_mev.BlindedBeaconBlock of ConsensusFork.Electra: electraData*: electra_mev.BlindedBeaconBlock + of ConsensusFork.Fulu: fuluData*: fulu_mev.BlindedBeaconBlock ForkySignedBeaconBlock* = phase0.SignedBeaconBlock | @@ -229,7 +249,8 @@ type bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock | deneb.SignedBeaconBlock | - electra.SignedBeaconBlock + electra.SignedBeaconBlock | + fulu.SignedBeaconBlock ForkedSignedBeaconBlock* = object case kind*: ConsensusFork @@ -239,6 +260,7 @@ type of ConsensusFork.Capella: capellaData*: capella.SignedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb.SignedBeaconBlock of ConsensusFork.Electra: electraData*: electra.SignedBeaconBlock + of ConsensusFork.Fulu: fuluData*: fulu.SignedBeaconBlock ForkySignedBlindedBeaconBlock* = phase0.SignedBeaconBlock | @@ -246,7 +268,8 @@ type bellatrix_mev.SignedBlindedBeaconBlock | capella_mev.SignedBlindedBeaconBlock | deneb_mev.SignedBlindedBeaconBlock | - electra_mev.SignedBlindedBeaconBlock + electra_mev.SignedBlindedBeaconBlock | + fulu_mev.SignedBlindedBeaconBlock ForkedSignedBlindedBeaconBlock* = object case kind*: ConsensusFork @@ -256,6 +279,7 @@ type of ConsensusFork.Capella: capellaData*: capella_mev.SignedBlindedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb_mev.SignedBlindedBeaconBlock of ConsensusFork.Electra: electraData*: electra_mev.SignedBlindedBeaconBlock + of ConsensusFork.Fulu: fuluData*: fulu_mev.SignedBlindedBeaconBlock ForkySigVerifiedSignedBeaconBlock* = phase0.SigVerifiedSignedBeaconBlock | @@ -263,7 +287,8 @@ type bellatrix.SigVerifiedSignedBeaconBlock | capella.SigVerifiedSignedBeaconBlock | deneb.SigVerifiedSignedBeaconBlock | - electra.SigVerifiedSignedBeaconBlock + electra.SigVerifiedSignedBeaconBlock | + fulu.SigVerifiedSignedBeaconBlock ForkyMsgTrustedSignedBeaconBlock* = phase0.MsgTrustedSignedBeaconBlock | @@ -271,7 +296,8 @@ type bellatrix.MsgTrustedSignedBeaconBlock | capella.MsgTrustedSignedBeaconBlock | deneb.MsgTrustedSignedBeaconBlock | - electra.MsgTrustedSignedBeaconBlock + electra.MsgTrustedSignedBeaconBlock | + fulu.MsgTrustedSignedBeaconBlock ForkyTrustedSignedBeaconBlock* = phase0.TrustedSignedBeaconBlock | @@ -279,7 +305,8 @@ type bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | deneb.TrustedSignedBeaconBlock | - electra.TrustedSignedBeaconBlock + electra.TrustedSignedBeaconBlock | + fulu.TrustedSignedBeaconBlock ForkedMsgTrustedSignedBeaconBlock* = object case kind*: ConsensusFork @@ -289,6 +316,7 @@ type of ConsensusFork.Capella: capellaData*: capella.MsgTrustedSignedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb.MsgTrustedSignedBeaconBlock of ConsensusFork.Electra: electraData*: electra.MsgTrustedSignedBeaconBlock + of ConsensusFork.Fulu: fuluData*: fulu.MsgTrustedSignedBeaconBlock ForkedTrustedSignedBeaconBlock* = object case kind*: ConsensusFork @@ -298,6 +326,7 @@ type of ConsensusFork.Capella: capellaData*: capella.TrustedSignedBeaconBlock of ConsensusFork.Deneb: denebData*: deneb.TrustedSignedBeaconBlock of ConsensusFork.Electra: electraData*: electra.TrustedSignedBeaconBlock + of ConsensusFork.Fulu: fuluData*: fulu.TrustedSignedBeaconBlock SomeForkySignedBeaconBlock* = ForkySignedBeaconBlock | @@ -323,6 +352,7 @@ type capella*: ForkDigest deneb*: ForkDigest electra*: ForkDigest + fulu*: ForkDigest template kind*( x: typedesc[ @@ -436,8 +466,29 @@ template kind*( electra_mev.SignedBlindedBeaconBlock]): ConsensusFork = ConsensusFork.Electra +template kind*( + x: typedesc[ + fulu.BeaconState | + fulu.HashedBeaconState | + fulu.ExecutionPayload | + fulu.ExecutionPayloadForSigning | + fulu.ExecutionPayloadHeader | + fulu.BeaconBlock | + fulu.SignedBeaconBlock | + fulu.TrustedBeaconBlock | + fulu.BeaconBlockBody | + fulu.SigVerifiedBeaconBlockBody | + fulu.TrustedBeaconBlockBody | + fulu.SigVerifiedSignedBeaconBlock | + fulu.MsgTrustedSignedBeaconBlock | + fulu.TrustedSignedBeaconBlock | + fulu_mev.SignedBlindedBeaconBlock]): ConsensusFork = + ConsensusFork.Fulu + template BeaconState*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu.BeaconState] + elif kind == ConsensusFork.Electra: typedesc[electra.BeaconState] elif kind == ConsensusFork.Deneb: typedesc[deneb.BeaconState] @@ -453,7 +504,9 @@ template BeaconState*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template BeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu.BeaconBlock] + elif kind == ConsensusFork.Electra: typedesc[electra.BeaconBlock] elif kind == ConsensusFork.Deneb: typedesc[deneb.BeaconBlock] @@ -469,7 +522,9 @@ template BeaconBlock*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template BeaconBlockBody*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu.BeaconBlockBody] + elif kind == ConsensusFork.Electra: typedesc[electra.BeaconBlockBody] elif kind == ConsensusFork.Deneb: typedesc[deneb.BeaconBlockBody] @@ -485,7 +540,9 @@ template BeaconBlockBody*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template SignedBeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu.SignedBeaconBlock] + elif kind == ConsensusFork.Electra: typedesc[electra.SignedBeaconBlock] elif kind == ConsensusFork.Deneb: typedesc[deneb.SignedBeaconBlock] @@ -501,7 +558,9 @@ template SignedBeaconBlock*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template TrustedSignedBeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu.TrustedSignedBeaconBlock] + elif kind == ConsensusFork.Electra: typedesc[electra.TrustedSignedBeaconBlock] elif kind == ConsensusFork.Deneb: typedesc[deneb.TrustedSignedBeaconBlock] @@ -517,7 +576,9 @@ template TrustedSignedBeaconBlock*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template ExecutionPayloadForSigning*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu.ExecutionPayloadForSigning] + elif kind == ConsensusFork.Electra: typedesc[electra.ExecutionPayloadForSigning] elif kind == ConsensusFork.Deneb: typedesc[deneb.ExecutionPayloadForSigning] @@ -529,7 +590,9 @@ template ExecutionPayloadForSigning*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template BlindedBeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu_mev.BlindedBeaconBlock] + elif kind == ConsensusFork.Electra: typedesc[electra_mev.BlindedBeaconBlock] elif kind == ConsensusFork.Deneb: typedesc[deneb_mev.BlindedBeaconBlock] @@ -539,7 +602,9 @@ template BlindedBeaconBlock*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template MaybeBlindedBeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu_mev.MaybeBlindedBeaconBlock] + elif kind == ConsensusFork.Electra: typedesc[electra_mev.MaybeBlindedBeaconBlock] elif kind == ConsensusFork.Deneb: typedesc[deneb_mev.MaybeBlindedBeaconBlock] @@ -549,7 +614,9 @@ template MaybeBlindedBeaconBlock*(kind: static ConsensusFork): auto = static: raiseAssert "Unreachable" template SignedBlindedBeaconBlock*(kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu_mev.SignedBlindedBeaconBlock] + elif kind == ConsensusFork.Electra: typedesc[electra_mev.SignedBlindedBeaconBlock] elif kind == ConsensusFork.Deneb: typedesc[deneb_mev.SignedBlindedBeaconBlock] @@ -563,9 +630,37 @@ template Forky*( kind: static ConsensusFork): auto = kind.SignedBeaconBlock +# Workaround method used for tests that involve walking through +# `nim-eth2-scnarios`fork dirs, to be removed once Fulu is +# included in new release. +template withAllButFulu*( + x: typedesc[ConsensusFork], body: untyped): untyped = + static: doAssert ConsensusFork.high == ConsensusFork.Fulu + block: + const consensusFork {.inject, used.} = ConsensusFork.Electra + body + block: + const consensusFork {.inject, used.} = ConsensusFork.Deneb + body + block: + const consensusFork {.inject, used.} = ConsensusFork.Capella + body + block: + const consensusFork {.inject, used.} = ConsensusFork.Bellatrix + body + block: + const consensusFork {.inject, used.} = ConsensusFork.Altair + body + block: + const consensusFork {.inject, used.} = ConsensusFork.Phase0 + body + template withAll*( x: typedesc[ConsensusFork], body: untyped): untyped = - static: doAssert ConsensusFork.high == ConsensusFork.Electra + static: doAssert ConsensusFork.high == ConsensusFork.Fulu + block: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + body block: const consensusFork {.inject, used.} = ConsensusFork.Electra body @@ -588,6 +683,9 @@ template withAll*( template withConsensusFork*( x: ConsensusFork, body: untyped): untyped = case x + of ConsensusFork.Fulu: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + body of ConsensusFork.Electra: const consensusFork {.inject, used.} = ConsensusFork.Electra body @@ -609,7 +707,9 @@ template withConsensusFork*( template BlockContents*( kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu.BlockContents] + elif kind == ConsensusFork.Electra: typedesc[electra.BlockContents] elif kind == ConsensusFork.Deneb: typedesc[deneb.BlockContents] @@ -626,7 +726,9 @@ template BlockContents*( template BlindedBlockContents*( kind: static ConsensusFork): auto = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + typedesc[fulu_mev.BlindedBeaconBlock] + elif kind == ConsensusFork.Electra: typedesc[electra_mev.BlindedBeaconBlock] elif kind == ConsensusFork.Deneb: typedesc[deneb_mev.BlindedBeaconBlock] @@ -651,7 +753,7 @@ template PayloadAttributes*( {.error: "PayloadAttributes does not support " & $kind.} # `eth2_merkleization` cannot import `forks` (circular), so the check is here -static: doAssert ConsensusFork.high == ConsensusFork.Electra, +static: doAssert ConsensusFork.high == ConsensusFork.Fulu, "eth2_merkleization has been checked and `hash_tree_root` is up to date" # TODO when https://github.com/nim-lang/Nim/issues/21086 fixed, use return type @@ -680,6 +782,10 @@ 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))) +func new*(T: type ForkedHashedBeaconState, data: fulu.BeaconState): + ref ForkedHashedBeaconState = + (ref T)(kind: ConsensusFork.Fulu, fuluData: fulu.HashedBeaconState( + data: data, root: hash_tree_root(data))) template init*(T: type ForkedBeaconBlock, blck: phase0.BeaconBlock): T = T(kind: ConsensusFork.Phase0, phase0Data: blck) @@ -693,6 +799,8 @@ 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) +template init*(T: type ForkedBeaconBlock, blck: fulu.BeaconBlock): T = + T(kind: ConsensusFork.Fulu, fuluData: blck) template init*(T: type ForkedSignedBeaconBlock, blck: phase0.SignedBeaconBlock): T = T(kind: ConsensusFork.Phase0, phase0Data: blck) @@ -706,6 +814,8 @@ template init*(T: type ForkedSignedBeaconBlock, blck: deneb.SignedBeaconBlock): T(kind: ConsensusFork.Deneb, denebData: blck) template init*(T: type ForkedSignedBeaconBlock, blck: electra.SignedBeaconBlock): T = T(kind: ConsensusFork.Electra, electraData: blck) +template init*(T: type ForkedSignedBeaconBlock, blck: fulu.SignedBeaconBlock): T = + T(kind: ConsensusFork.Fulu, fuluData: blck) func init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock, blockRoot: Eth2Digest, signature: ValidatorSig): T = @@ -740,6 +850,11 @@ func init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock, electraData: electra.SignedBeaconBlock(message: forked.electraData, root: blockRoot, signature: signature)) + of ConsensusFork.Fulu: + T(kind: ConsensusFork.Fulu, + fuluData: fulu.SignedBeaconBlock(message: forked.fuluData, + root: blockRoot, + signature: signature)) func init*(T: type ForkedSignedBlindedBeaconBlock, forked: ForkedBlindedBeaconBlock, blockRoot: Eth2Digest, @@ -770,6 +885,10 @@ func init*(T: type ForkedSignedBlindedBeaconBlock, T(kind: ConsensusFork.Electra, electraData: electra_mev.SignedBlindedBeaconBlock(message: forked.electraData, signature: signature)) + of ConsensusFork.Fulu: + T(kind: ConsensusFork.Fulu, + fuluData: fulu_mev.SignedBlindedBeaconBlock(message: forked.fuluData, + signature: signature)) template init*(T: type ForkedSignedBlindedBeaconBlock, blck: capella_mev.BlindedBeaconBlock, blockRoot: Eth2Digest, @@ -792,6 +911,13 @@ template init*(T: type ForkedSignedBlindedBeaconBlock, electraData: electra_mev.SignedBlindedBeaconBlock( message: blck, signature: signature)) +template init*(T: type ForkedSignedBlindedBeaconBlock, + blck: fulu_mev.BlindedBeaconBlock, blockRoot: Eth2Digest, + signature: ValidatorSig): T = + T(kind: ConsensusFork.Fulu, + fuluData: fulu_mev.SignedBlindedBeaconBlock( + message: blck, signature: signature)) + template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: phase0.MsgTrustedSignedBeaconBlock): T = T(kind: ConsensusFork.Phase0, phase0Data: blck) template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: altair.MsgTrustedSignedBeaconBlock): T = @@ -815,6 +941,8 @@ template init*(T: type ForkedTrustedSignedBeaconBlock, blck: deneb.TrustedSigned T(kind: ConsensusFork.Deneb, denebData: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: electra.TrustedSignedBeaconBlock): T = T(kind: ConsensusFork.Electra, electraData: blck) +template init*(T: type ForkedTrustedSignedBeaconBlock, blck: fulu.TrustedSignedBeaconBlock): T = + T(kind: ConsensusFork.Fulu, fuluData: blck) template toString*(kind: ConsensusFork): string = case kind @@ -830,9 +958,13 @@ template toString*(kind: ConsensusFork): string = "deneb" of ConsensusFork.Electra: "electra" + of ConsensusFork.Fulu: + "fulu" template init*(T: typedesc[ConsensusFork], value: string): Opt[ConsensusFork] = case value + of "fulu": + Opt.some ConsensusFork.Fulu of "electra": Opt.some ConsensusFork.Electra of "deneb": @@ -859,6 +991,10 @@ template init*(T: type ForkedEpochInfo, info: altair.EpochInfo): T = template withState*(x: ForkedHashedBeaconState, body: untyped): untyped = case x.kind + of ConsensusFork.Fulu: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + template forkyState: untyped {.inject, used.} = x.fuluData + body of ConsensusFork.Electra: const consensusFork {.inject, used.} = ConsensusFork.Electra template forkyState: untyped {.inject, used.} = x.electraData @@ -889,7 +1025,9 @@ template forky*( ForkedBeaconBlock | ForkedHashedBeaconState, kind: static ConsensusFork): untyped = - when kind == ConsensusFork.Electra: + when kind == ConsensusFork.Fulu: + x.fuluData + elif kind == ConsensusFork.Electra: x.electraData elif kind == ConsensusFork.Deneb: x.denebData @@ -925,7 +1063,7 @@ template withEpochInfo*( template withEpochInfo*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.BeaconState, x: var ForkedEpochInfo, body: untyped): untyped = if x.kind != EpochInfoFork.Altair: # Rare, so efficiency not critical @@ -963,6 +1101,8 @@ func setStateRoot*(x: var ForkedHashedBeaconState, root: Eth2Digest) = func consensusForkEpoch*( cfg: RuntimeConfig, consensusFork: ConsensusFork): Epoch = case consensusFork + of ConsensusFork.Fulu: + cfg.FULU_FORK_EPOCH of ConsensusFork.Electra: cfg.ELECTRA_FORK_EPOCH of ConsensusFork.Deneb: @@ -979,7 +1119,8 @@ func consensusForkEpoch*( func consensusForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): ConsensusFork = ## Return the current fork for the given epoch. static: - doAssert high(ConsensusFork) == ConsensusFork.Electra + doAssert high(ConsensusFork) == ConsensusFork.Fulu + doAssert ConsensusFork.Fulu > ConsensusFork.Electra doAssert ConsensusFork.Electra > ConsensusFork.Deneb doAssert ConsensusFork.Deneb > ConsensusFork.Capella doAssert ConsensusFork.Capella > ConsensusFork.Bellatrix @@ -987,7 +1128,8 @@ func consensusForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): ConsensusFork = doAssert ConsensusFork.Altair > ConsensusFork.Phase0 doAssert GENESIS_EPOCH == 0 - if epoch >= cfg.ELECTRA_FORK_EPOCH: ConsensusFork.Electra + if epoch >= cfg.FULU_FORK_EPOCH: ConsensusFork.Fulu + elif 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 @@ -996,8 +1138,10 @@ func consensusForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): ConsensusFork = func consensusForkForDigest*( forkDigests: ForkDigests, forkDigest: ForkDigest): Opt[ConsensusFork] = - static: doAssert high(ConsensusFork) == ConsensusFork.Electra - if forkDigest == forkDigests.electra: + static: doAssert high(ConsensusFork) == ConsensusFork.Fulu + if forkDigest == forkDigests.fulu: + ok ConsensusFork.Fulu + elif forkDigest == forkDigests.electra: ok ConsensusFork.Electra elif forkDigest == forkDigests.deneb: ok ConsensusFork.Deneb @@ -1015,6 +1159,8 @@ func consensusForkForDigest*( func atConsensusFork*( forkDigests: ForkDigests, consensusFork: ConsensusFork): ForkDigest = case consensusFork + of ConsensusFork.Fulu: + forkDigests.fulu of ConsensusFork.Electra: forkDigests.electra of ConsensusFork.Deneb: @@ -1099,6 +1245,10 @@ template withBlck*( const consensusFork {.inject, used.} = ConsensusFork.Electra template forkyBlck: untyped {.inject, used.} = x.electraData body + of ConsensusFork.Fulu: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + template forkyBlck: untyped {.inject, used.} = x.fuluData + body func proposer_index*(x: ForkedBeaconBlock): uint64 = withBlck(x): forkyBlck.proposer_index @@ -1123,7 +1273,8 @@ template getForkedBlockField*( 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.Electra: unsafeAddr x.electraData.message.y)[] + of ConsensusFork.Electra: unsafeAddr x.electraData.message.y + of ConsensusFork.Fulu: unsafeAddr x.fuluData.message.y)[] template signature*(x: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | @@ -1161,6 +1312,18 @@ template withForkyMaybeBlindedBlck*( b: ForkedMaybeBlindedBeaconBlock, body: untyped): untyped = case b.kind + of ConsensusFork.Fulu: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + template d: untyped = b.fuluData + case d.isBlinded: + of true: + const isBlinded {.inject, used.} = true + template forkyMaybeBlindedBlck: untyped {.inject, used.} = d.blindedData + body + of false: + const isBlinded {.inject, used.} = false + template forkyMaybeBlindedBlck: untyped {.inject, used.} = d.data + body of ConsensusFork.Electra: const consensusFork {.inject, used.} = ConsensusFork.Electra template d: untyped = b.electraData @@ -1227,6 +1390,11 @@ template withStateAndBlck*( ForkedTrustedSignedBeaconBlock, body: untyped): untyped = case s.kind + of ConsensusFork.Fulu: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + template forkyState: untyped {.inject.} = s.fuluData + template forkyBlck: untyped {.inject.} = b.fuluData + body of ConsensusFork.Electra: const consensusFork {.inject, used.} = ConsensusFork.Electra template forkyState: untyped {.inject.} = s.electraData @@ -1260,6 +1428,10 @@ template withStateAndBlck*( template withAttestation*(a: ForkedAttestation, body: untyped): untyped = case a.kind + of ConsensusFork.Fulu: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + template forkyAttestation: untyped {.inject.} = a.fuluData + body of ConsensusFork.Electra: const consensusFork {.inject, used.} = ConsensusFork.Electra template forkyAttestation: untyped {.inject.} = a.electraData @@ -1288,6 +1460,10 @@ template withAttestation*(a: ForkedAttestation, body: untyped): untyped = template withAggregateAndProof*(a: ForkedAggregateAndProof, body: untyped): untyped = case a.kind + of ConsensusFork.Fulu: + const consensusFork {.inject, used.} = ConsensusFork.Fulu + template forkyProof: untyped {.inject.} = a.fuluData + body of ConsensusFork.Electra: const consensusFork {.inject, used.} = ConsensusFork.Electra template forkyProof: untyped {.inject.} = a.electraData @@ -1315,7 +1491,8 @@ template withAggregateAndProof*(a: ForkedAggregateAndProof, func toBeaconBlockHeader*( blck: SomeForkyBeaconBlock | deneb_mev.BlindedBeaconBlock | - electra_mev.BlindedBeaconBlock): BeaconBlockHeader = + electra_mev.BlindedBeaconBlock | fulu_mev.BlindedBeaconBlock): + BeaconBlockHeader = ## Reduce a given `BeaconBlock` to its `BeaconBlockHeader`. BeaconBlockHeader( slot: blck.slot, @@ -1379,8 +1556,15 @@ func electraFork*(cfg: RuntimeConfig): Fork = current_version: cfg.ELECTRA_FORK_VERSION, epoch: cfg.ELECTRA_FORK_EPOCH) +func fuluFork*(cfg: RuntimeConfig): Fork = + Fork( + previous_version: cfg.ELECTRA_FORK_VERSION, + current_version: cfg.FULU_FORK_VERSION, + epoch: cfg.FULU_FORK_EPOCH) + func forkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Fork = case cfg.consensusForkAtEpoch(epoch) + of ConsensusFork.Fulu: cfg.fuluFork of ConsensusFork.Electra: cfg.electraFork of ConsensusFork.Deneb: cfg.denebFork of ConsensusFork.Capella: cfg.capellaFork @@ -1390,6 +1574,7 @@ func forkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Fork = func forkVersionAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Version = case cfg.consensusForkAtEpoch(epoch) + of ConsensusFork.Fulu: cfg.FULU_FORK_VERSION of ConsensusFork.Electra: cfg.ELECTRA_FORK_VERSION of ConsensusFork.Deneb: cfg.DENEB_FORK_VERSION of ConsensusFork.Capella: cfg.CAPELLA_FORK_VERSION @@ -1399,7 +1584,8 @@ func forkVersionAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Version = func nextForkEpochAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Epoch = case cfg.consensusForkAtEpoch(epoch) - of ConsensusFork.Electra: FAR_FUTURE_EPOCH + of ConsensusFork.Fulu: FAR_FUTURE_EPOCH + of ConsensusFork.Electra: cfg.FULU_FORK_EPOCH of ConsensusFork.Deneb: cfg.ELECTRA_FORK_EPOCH of ConsensusFork.Capella: cfg.DENEB_FORK_EPOCH of ConsensusFork.Bellatrix: cfg.CAPELLA_FORK_EPOCH @@ -1414,6 +1600,7 @@ func forkVersion*(cfg: RuntimeConfig, consensusFork: ConsensusFork): Version = of ConsensusFork.Capella: cfg.CAPELLA_FORK_VERSION of ConsensusFork.Deneb: cfg.DENEB_FORK_VERSION of ConsensusFork.Electra: cfg.ELECTRA_FORK_VERSION + of ConsensusFork.Fulu: cfg.FULU_FORK_VERSION func lcDataForkAtConsensusFork*( consensusFork: ConsensusFork): LightClientDataFork = @@ -1429,15 +1616,15 @@ func lcDataForkAtConsensusFork*( else: LightClientDataFork.None -func getForkSchedule*(cfg: RuntimeConfig): array[6, Fork] = +func getForkSchedule*(cfg: RuntimeConfig): array[7, Fork] = ## This procedure returns list of known and/or scheduled forks. ## ## This procedure is used by HTTP REST framework and validator client. ## ## NOTE: Update this procedure when new fork will be scheduled. - static: doAssert high(ConsensusFork) == ConsensusFork.Electra + static: doAssert high(ConsensusFork) == ConsensusFork.Fulu [cfg.genesisFork(), cfg.altairFork(), cfg.bellatrixFork(), cfg.capellaFork(), - cfg.denebFork(), cfg.electraFork()] + cfg.denebFork(), cfg.electraFork(), cfg.fuluFork()] type # The first few fields of a state, shared across all forks @@ -1524,7 +1711,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.Electra + static: doAssert high(ConsensusFork) == ConsensusFork.Fulu T( phase0: compute_fork_digest(cfg.GENESIS_FORK_VERSION, genesis_validators_root), @@ -1537,7 +1724,9 @@ func init*(T: type ForkDigests, deneb: compute_fork_digest(cfg.DENEB_FORK_VERSION, genesis_validators_root), electra: - compute_fork_digest(cfg.ELECTRA_FORK_VERSION, genesis_validators_root) + compute_fork_digest(cfg.ELECTRA_FORK_VERSION, genesis_validators_root), + fulu: + compute_fork_digest(cfg.FULU_FORK_VERSION, genesis_validators_root) ) func toBlockId*(header: BeaconBlockHeader): BlockId = @@ -1633,6 +1822,28 @@ template init*(T: type ForkedMaybeBlindedBeaconBlock, consensusValue: cvalue, executionValue: evalue) +template init*(T: type ForkedMaybeBlindedBeaconBlock, + blck: fulu.BlockContents, + evalue: Opt[UInt256], cvalue: Opt[UInt256]): T = + ForkedMaybeBlindedBeaconBlock( + kind: ConsensusFork.Fulu, + fuluData: fulu_mev.MaybeBlindedBeaconBlock( + isBlinded: false, + data: blck), + consensusValue: cvalue, + executionValue: evalue) + +template init*(T: type ForkedMaybeBlindedBeaconBlock, + blck: fulu_mev.BlindedBeaconBlock, + evalue: Opt[UInt256], cvalue: Opt[UInt256]): T = + ForkedMaybeBlindedBeaconBlock( + kind: ConsensusFork.Fulu, + fuluData: fulu_mev.MaybeBlindedBeaconBlock( + isBlinded: true, + blindedData: blck), + consensusValue: cvalue, + executionValue: evalue) + func committee_index*( v: phase0.Attestation, on_chain: static bool = false): uint64 = v.data.index @@ -1659,6 +1870,8 @@ template init*(T: type ForkedAttestation, ForkedAttestation(kind: ConsensusFork.Deneb, denebData: attestation) of ConsensusFork.Electra: raiseAssert $fork & " fork should not be used for this type of attestation" + of ConsensusFork.Fulu: + raiseAssert $fork & " fork should not be used for this type of attestation" template init*(T: type ForkedAttestation, attestation: electra.Attestation, @@ -1668,6 +1881,8 @@ template init*(T: type ForkedAttestation, raiseAssert $fork & " fork should not be used for this type of attestation" of ConsensusFork.Electra: ForkedAttestation(kind: ConsensusFork.Electra, electraData: attestation) + of ConsensusFork.Fulu: + ForkedAttestation(kind: ConsensusFork.Fulu, fuluData: attestation) template init*(T: type ForkedAggregateAndProof, proof: phase0.AggregateAndProof, @@ -1686,6 +1901,9 @@ template init*(T: type ForkedAggregateAndProof, of ConsensusFork.Electra: raiseAssert $fork & " fork should not be used for this type of aggregate and proof" + of ConsensusFork.Fulu: + raiseAssert $fork & + " fork should not be used for this type of aggregate and proof" template init*(T: type ForkedAggregateAndProof, proof: electra.AggregateAndProof, @@ -1696,3 +1914,5 @@ template init*(T: type ForkedAggregateAndProof, " fork should not be used for this type of aggregate and proof" of ConsensusFork.Electra: ForkedAggregateAndProof(kind: ConsensusFork.Electra, electraData: proof) + of ConsensusFork.Fulu: + ForkedAggregateAndProof(kind: ConsensusFork.Fulu, fuluData: proof) diff --git a/beacon_chain/spec/forks_light_client.nim b/beacon_chain/spec/forks_light_client.nim index bd8f129b04..21b590bbb3 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, electra], + ./datatypes/[phase0, altair, bellatrix, capella, deneb, electra, fulu], ./eth2_merkleization type @@ -1207,7 +1207,8 @@ func toElectraLightClientHeader( func toElectraLightClientHeader( # `SomeSignedBeaconBlock`: https://github.com/nim-lang/Nim/issues/18095 blck: - electra.SignedBeaconBlock | electra.TrustedSignedBeaconBlock + electra.SignedBeaconBlock | electra.TrustedSignedBeaconBlock | + fulu.SignedBeaconBlock | fulu.TrustedSignedBeaconBlock ): electra.LightClientHeader = template payload: untyped = blck.message.body.execution_payload electra.LightClientHeader( @@ -1241,7 +1242,8 @@ func toLightClientHeader*( bellatrix.SignedBeaconBlock | bellatrix.TrustedSignedBeaconBlock | capella.SignedBeaconBlock | capella.TrustedSignedBeaconBlock | deneb.SignedBeaconBlock | deneb.TrustedSignedBeaconBlock | - electra.SignedBeaconBlock | electra.TrustedSignedBeaconBlock, + electra.SignedBeaconBlock | electra.TrustedSignedBeaconBlock | + fulu.SignedBeaconBlock | fulu.TrustedSignedBeaconBlock, kind: static LightClientDataFork): auto = when kind == LightClientDataFork.Electra: blck.toElectraLightClientHeader() diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index fb011ef418..d77005b61f 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -227,7 +227,8 @@ func verify_blob_sidecar_inclusion_proof*( ok() func create_blob_sidecars*( - forkyBlck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock, + forkyBlck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock | + fulu.SignedBeaconBlock, kzg_proofs: KzgProofs, blobs: Blobs): seq[BlobSidecar] = template kzg_commitments: untyped = @@ -382,7 +383,7 @@ func contextEpoch*(update: SomeForkyLightClientUpdate): Epoch = # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/bellatrix/beacon-chain.md#is_merge_transition_complete func is_merge_transition_complete*( state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | - electra.BeaconState): bool = + electra.BeaconState | fulu.BeaconState): bool = const defaultExecutionPayloadHeader = default(typeof(state.latest_execution_payload_header)) state.latest_execution_payload_header != defaultExecutionPayloadHeader @@ -399,7 +400,7 @@ func is_execution_block*(blck: SomeForkyBeaconBlock): bool = # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/bellatrix/beacon-chain.md#is_merge_transition_block func is_merge_transition_block( state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | - electra.BeaconState, + electra.BeaconState | fulu.BeaconState, body: bellatrix.BeaconBlockBody | bellatrix.TrustedBeaconBlockBody | bellatrix.SigVerifiedBeaconBlockBody | capella.BeaconBlockBody | capella.TrustedBeaconBlockBody | @@ -407,7 +408,9 @@ func is_merge_transition_block( deneb.BeaconBlockBody | deneb.TrustedBeaconBlockBody | deneb.SigVerifiedBeaconBlockBody | electra.BeaconBlockBody | electra.TrustedBeaconBlockBody | - electra.SigVerifiedBeaconBlockBody): bool = + electra.SigVerifiedBeaconBlockBody | + fulu.BeaconBlockBody | fulu.TrustedBeaconBlockBody | + fulu.SigVerifiedBeaconBlockBody): bool = const defaultExecutionPayload = default(typeof(body.execution_payload)) not is_merge_transition_complete(state) and body.execution_payload != defaultExecutionPayload @@ -415,7 +418,7 @@ func is_merge_transition_block( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/bellatrix/beacon-chain.md#is_execution_enabled func is_execution_enabled*( state: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | - electra.BeaconState, + electra.BeaconState | fulu.BeaconState, body: bellatrix.BeaconBlockBody | bellatrix.TrustedBeaconBlockBody | bellatrix.SigVerifiedBeaconBlockBody | capella.BeaconBlockBody | capella.TrustedBeaconBlockBody | @@ -423,7 +426,9 @@ func is_execution_enabled*( deneb.BeaconBlockBody | deneb.TrustedBeaconBlockBody | deneb.SigVerifiedBeaconBlockBody | electra.BeaconBlockBody | electra.TrustedBeaconBlockBody | - electra.SigVerifiedBeaconBlockBody): bool = + electra.SigVerifiedBeaconBlockBody | + fulu.BeaconBlockBody | fulu.TrustedBeaconBlockBody | + fulu.SigVerifiedBeaconBlockBody): bool = is_merge_transition_block(state, body) or is_merge_transition_complete(state) # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/bellatrix/beacon-chain.md#compute_timestamp_at_slot diff --git a/beacon_chain/spec/keystore.nim b/beacon_chain/spec/keystore.nim index 87ae2772bd..80dd1c7868 100644 --- a/beacon_chain/spec/keystore.nim +++ b/beacon_chain/spec/keystore.nim @@ -153,6 +153,7 @@ type capellaIndex*: Option[GeneralizedIndex] denebIndex*: Option[GeneralizedIndex] electraIndex*: Option[GeneralizedIndex] + fuluIndex*: Option[GeneralizedIndex] KeystoreData* = object version*: uint64 @@ -726,17 +727,20 @@ template writeValue*(w: var JsonWriter, func parseProvenBlockProperty*(propertyPath: string): Result[ProvenProperty, string] = if propertyPath == ".execution_payload.fee_recipient": + debugFuluComment "We don't know yet if `GeneralizedIndex` will stay same in Fulu yet." ok ProvenProperty( path: propertyPath, capellaIndex: some GeneralizedIndex(401), denebIndex: some GeneralizedIndex(801), - electraIndex: some GeneralizedIndex(801)) + electraIndex: some GeneralizedIndex(801), + fuluIndex: some GeneralizedIndex(801)) elif propertyPath == ".graffiti": ok ProvenProperty( path: propertyPath, capellaIndex: some GeneralizedIndex(18), denebIndex: some GeneralizedIndex(18), - electraIndex: some GeneralizedIndex(18)) + electraIndex: some GeneralizedIndex(18), + fuluIndex: some GeneralizedIndex(18)) else: err("Keystores with proven properties different than " & "`.execution_payload.fee_recipient` and `.graffiti` " & @@ -846,10 +850,12 @@ proc readValue*(reader: var JsonReader, value: var RemoteKeystore) prop.capellaIndex = some GeneralizedIndex(401) prop.denebIndex = some GeneralizedIndex(801) prop.electraIndex = some GeneralizedIndex(801) + prop.fuluIndex = some GeneralizedIndex(801) elif prop.path == ".graffiti": prop.capellaIndex = some GeneralizedIndex(18) prop.denebIndex = some GeneralizedIndex(18) prop.electraIndex = some GeneralizedIndex(801) + prop.fuluIndex = some GeneralizedIndex(801) else: reader.raiseUnexpectedValue("Keystores with proven properties different than " & "`.execution_payload.fee_recipient` and `.graffiti` " & diff --git a/beacon_chain/spec/mev/fulu_mev.nim b/beacon_chain/spec/mev/fulu_mev.nim new file mode 100644 index 0000000000..c6dd0cdd16 --- /dev/null +++ b/beacon_chain/spec/mev/fulu_mev.nim @@ -0,0 +1,152 @@ +# beacon_chain +# Copyright (c) 2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import ".."/datatypes/[altair, fulu] + +from stew/byteutils import to0xHex +from ".."/datatypes/phase0 import AttesterSlashing +from ../datatypes/bellatrix import ExecutionAddress +from ".."/datatypes/capella import SignedBLSToExecutionChange +from ".."/datatypes/deneb import BlobsBundle, KzgCommitments +from ".."/datatypes/electra import Attestation, AttesterSlashing, + ExecutionRequests +from ".."/eth2_merkleization import hash_tree_root + +type + BuilderBid* = object + header*: ExecutionPayloadHeader + blob_kzg_commitments*: KzgCommitments + value*: UInt256 + pubkey*: ValidatorPubKey + + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#signedbuilderbid + SignedBuilderBid* = object + message*: BuilderBid + signature*: ValidatorSig + + BlindedBeaconBlockBody* = object + randao_reveal*: ValidatorSig + eth1_data*: Eth1Data + graffiti*: GraffitiBytes + proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS] + attester_slashings*: + List[electra.AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS_ELECTRA] + attestations*: List[electra.Attestation, Limit MAX_ATTESTATIONS_ELECTRA] + deposits*: List[Deposit, Limit MAX_DEPOSITS] + voluntary_exits*: List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] + sync_aggregate*: SyncAggregate + execution_payload_header*: ExecutionPayloadHeader + bls_to_execution_changes*: + List[SignedBLSToExecutionChange, + Limit MAX_BLS_TO_EXECUTION_CHANGES] + blob_kzg_commitments*: KzgCommitments # [New in Deneb] + execution_requests*: ExecutionRequests # [New in Electra] + + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#blindedbeaconblock + BlindedBeaconBlock* = object + slot*: Slot + proposer_index*: uint64 + parent_root*: Eth2Digest + state_root*: Eth2Digest + body*: BlindedBeaconBlockBody # [Modified in Deneb] + + MaybeBlindedBeaconBlock* = object + case isBlinded*: bool + of false: + data*: fulu.BlockContents + of true: + blindedData*: BlindedBeaconBlock + + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#signedblindedbeaconblock + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/capella/builder.md#blindedbeaconblockbody + SignedBlindedBeaconBlock* = object + message*: BlindedBeaconBlock + signature*: ValidatorSig + + ExecutionPayloadAndBlobsBundle* = object + execution_payload*: ExecutionPayload + blobs_bundle*: BlobsBundle + + # Not spec, but suggested by spec + BlindedExecutionPayloadAndBlobsBundle* = object + execution_payload_header*: ExecutionPayloadHeader + blob_kzg_commitments*: KzgCommitments # [New in Deneb] + +func shortLog*(v: BlindedBeaconBlock): auto = + ( + slot: shortLog(v.slot), + proposer_index: v.proposer_index, + parent_root: shortLog(v.parent_root), + state_root: shortLog(v.state_root), + eth1data: v.body.eth1_data, + graffiti: $v.body.graffiti, + proposer_slashings_len: v.body.proposer_slashings.len(), + attester_slashings_len: v.body.attester_slashings.len(), + attestations_len: v.body.attestations.len(), + deposits_len: v.body.deposits.len(), + voluntary_exits_len: v.body.voluntary_exits.len(), + sync_committee_participants: v.body.sync_aggregate.num_active_participants, + block_number: v.body.execution_payload_header.block_number, + # TODO checksum hex? shortlog? + block_hash: to0xHex(v.body.execution_payload_header.block_hash.data), + parent_hash: to0xHex(v.body.execution_payload_header.parent_hash.data), + fee_recipient: to0xHex(v.body.execution_payload_header.fee_recipient.data), + bls_to_execution_changes_len: v.body.bls_to_execution_changes.len(), + blob_kzg_commitments_len: 0, # Deneb compat + ) + +func shortLog*(v: SignedBlindedBeaconBlock): auto = + ( + blck: shortLog(v.message), + signature: shortLog(v.signature) + ) + +func toSignedBlindedBeaconBlock*(blck: fulu.SignedBeaconBlock): + SignedBlindedBeaconBlock = + SignedBlindedBeaconBlock( + message: BlindedBeaconBlock( + slot: blck.message.slot, + proposer_index: blck.message.proposer_index, + parent_root: blck.message.parent_root, + state_root: blck.message.state_root, + body: BlindedBeaconBlockBody( + randao_reveal: blck.message.body.randao_reveal, + eth1_data: blck.message.body.eth1_data, + graffiti: blck.message.body.graffiti, + proposer_slashings: blck.message.body.proposer_slashings, + attester_slashings: blck.message.body.attester_slashings, + attestations: blck.message.body.attestations, + deposits: blck.message.body.deposits, + voluntary_exits: blck.message.body.voluntary_exits, + sync_aggregate: blck.message.body.sync_aggregate, + execution_payload_header: ExecutionPayloadHeader( + parent_hash: blck.message.body.execution_payload.parent_hash, + fee_recipient: blck.message.body.execution_payload.fee_recipient, + state_root: blck.message.body.execution_payload.state_root, + receipts_root: blck.message.body.execution_payload.receipts_root, + logs_bloom: blck.message.body.execution_payload.logs_bloom, + prev_randao: blck.message.body.execution_payload.prev_randao, + block_number: blck.message.body.execution_payload.block_number, + gas_limit: blck.message.body.execution_payload.gas_limit, + gas_used: blck.message.body.execution_payload.gas_used, + timestamp: blck.message.body.execution_payload.timestamp, + extra_data: blck.message.body.execution_payload.extra_data, + base_fee_per_gas: + blck.message.body.execution_payload.base_fee_per_gas, + block_hash: blck.message.body.execution_payload.block_hash, + transactions_root: + hash_tree_root(blck.message.body.execution_payload.transactions), + withdrawals_root: + hash_tree_root(blck.message.body.execution_payload.withdrawals), + blob_gas_used: blck.message.body.execution_payload.blob_gas_used, + excess_blob_gas: blck.message.body.execution_payload.excess_blob_gas), + bls_to_execution_changes: blck.message.body.bls_to_execution_changes, + blob_kzg_commitments: blck.message.body.blob_kzg_commitments, + execution_requests: blck.message.body.execution_requests)), + signature: blck.signature) diff --git a/beacon_chain/spec/mev/rest_fulu_mev_calls.nim b/beacon_chain/spec/mev/rest_fulu_mev_calls.nim new file mode 100644 index 0000000000..8be08ae3c5 --- /dev/null +++ b/beacon_chain/spec/mev/rest_fulu_mev_calls.nim @@ -0,0 +1,41 @@ +# beacon_chain +# Copyright (c) 2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import + chronos, presto/client, + ".."/eth2_apis/[rest_types, eth2_rest_serialization] + +export chronos, client, rest_types, eth2_rest_serialization + +proc getHeaderFulu*(slot: Slot, + parent_hash: Eth2Digest, + pubkey: ValidatorPubKey + ): RestPlainResponse {. + rest, endpoint: "/eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}", + meth: MethodGet, connection: {Dedicated, Close}.} + ## https://github.com/ethereum/builder-specs/blob/v0.4.0/apis/builder/header.yaml + +proc submitBlindedBlockPlain*( + body: fulu_mev.SignedBlindedBeaconBlock +): RestPlainResponse {. + rest, endpoint: "/eth/v1/builder/blinded_blocks", + meth: MethodPost, connection: {Dedicated, Close}.} + ## https://github.com/ethereum/builder-specs/blob/v0.4.0/apis/builder/blinded_blocks.yaml + +proc submitBlindedBlock*( + client: RestClientRef, + body: fulu_mev.SignedBlindedBeaconBlock +): Future[RestPlainResponse] {. + async: (raises: [CancelledError, RestEncodingError, RestDnsResolveError, + RestCommunicationError]).} = + ## https://github.com/ethereum/builder-specs/blob/v0.4.0/apis/builder/blinded_blocks.yaml + await client.submitBlindedBlockPlain( + body, + extraHeaders = @[("eth-consensus-version", toString(ConsensusFork.Fulu))] + ) diff --git a/beacon_chain/spec/presets.nim b/beacon_chain/spec/presets.nim index be87d58204..4706d29cb7 100644 --- a/beacon_chain/spec/presets.nim +++ b/beacon_chain/spec/presets.nim @@ -62,6 +62,8 @@ type DENEB_FORK_EPOCH*: Epoch ELECTRA_FORK_VERSION*: Version ELECTRA_FORK_EPOCH*: Epoch + FULU_FORK_VERSION*: Version + FULU_FORK_EPOCH*: Epoch # Time parameters # TODO SECONDS_PER_SLOT*: uint64 @@ -199,6 +201,9 @@ when const_preset == "mainnet": # Electra ELECTRA_FORK_VERSION: Version [byte 0x05, 0x00, 0x00, 0x00], ELECTRA_FORK_EPOCH: FAR_FUTURE_EPOCH, + # Fulu + FULU_FORK_VERSION: Version [byte 0x06, 0x00, 0x00, 0x00], + FULU_FORK_EPOCH: FAR_FUTURE_EPOCH, # Time parameters # --------------------------------------------------------------- @@ -348,7 +353,9 @@ elif const_preset == "gnosis": # Electra ELECTRA_FORK_VERSION: Version [byte 0x05, 0x00, 0x00, 0x64], ELECTRA_FORK_EPOCH: FAR_FUTURE_EPOCH, - + # Fulu + FULU_FORK_VERSION: Version [byte 0x06, 0x00, 0x00, 0x00], + FULU_FORK_EPOCH: FAR_FUTURE_EPOCH, # Time parameters # --------------------------------------------------------------- @@ -493,7 +500,9 @@ elif const_preset == "minimal": # Electra ELECTRA_FORK_VERSION: Version [byte 0x05, 0x00, 0x00, 0x01], ELECTRA_FORK_EPOCH: Epoch(uint64.high), - + # Fulu + FULU_FORK_VERSION: Version [byte 0x06, 0x00, 0x00, 0x01], + FULU_FORK_EPOCH: Epoch(uint64.high), # Time parameters # --------------------------------------------------------------- diff --git a/beacon_chain/spec/signatures.nim b/beacon_chain/spec/signatures.nim index 6ecd49b3a2..9197912b14 100644 --- a/beacon_chain/spec/signatures.nim +++ b/beacon_chain/spec/signatures.nim @@ -374,7 +374,7 @@ proc verify_contribution_and_proof_signature*( # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#signing func compute_builder_signing_root( fork: Fork, - msg: deneb_mev.BuilderBid | electra_mev.BuilderBid | + msg: deneb_mev.BuilderBid | electra_mev.BuilderBid | fulu_mev.BuilderBid | ValidatorRegistrationV1): Eth2Digest = # Uses genesis fork version regardless doAssert fork.current_version == fork.previous_version @@ -390,7 +390,8 @@ proc get_builder_signature*( blsSign(privkey, signing_root.data) proc verify_builder_signature*( - fork: Fork, msg: deneb_mev.BuilderBid | electra_mev.BuilderBid, + fork: Fork, msg: deneb_mev.BuilderBid | electra_mev.BuilderBid | + fulu_mev.BuilderBid, pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool = let signing_root = compute_builder_signing_root(fork, msg) blsVerify(pubkey, signing_root.data, signature) diff --git a/beacon_chain/spec/state_transition.nim b/beacon_chain/spec/state_transition.nim index ead1dc1a93..876379bbd4 100644 --- a/beacon_chain/spec/state_transition.nim +++ b/beacon_chain/spec/state_transition.nim @@ -168,6 +168,9 @@ func noRollback*(state: var deneb.HashedBeaconState) = func noRollback*(state: var electra.HashedBeaconState) = trace "Skipping rollback of broken Electra state" +func noRollback*(state: var fulu.HashedBeaconState) = + trace "Skipping rollback of broken Fulu state" + func maybeUpgradeStateToAltair( cfg: RuntimeConfig, state: var ForkedHashedBeaconState) = # Both process_slots() and state_transition_block() call this, so only run it @@ -229,6 +232,19 @@ func maybeUpgradeStateToElectra( electraData: electra.HashedBeaconState( root: hash_tree_root(newState[]), data: newState[]))[] +func maybeUpgradeStateToFulu( + cfg: RuntimeConfig, state: var ForkedHashedBeaconState, + cache: var StateCache) = + # Both process_slots() and state_transition_block() call this, so only run it + # once by checking for existing fork. + if getStateField(state, slot).epoch == cfg.FULU_FORK_EPOCH and + state.kind == ConsensusFork.Electra: + let newState = upgrade_to_fulu(cfg, state.electraData.data, cache) + state = (ref ForkedHashedBeaconState)( + kind: ConsensusFork.Fulu, + fuluData: fulu.HashedBeaconState( + root: hash_tree_root(newState[]), data: newState[]))[] + func maybeUpgradeState*( cfg: RuntimeConfig, state: var ForkedHashedBeaconState, cache: var StateCache) = @@ -237,6 +253,7 @@ func maybeUpgradeState*( cfg.maybeUpgradeStateToCapella(state) cfg.maybeUpgradeStateToDeneb(state) cfg.maybeUpgradeStateToElectra(state, cache) + cfg.maybeUpgradeStateToFulu(state, cache) proc process_slots*( cfg: RuntimeConfig, state: var ForkedHashedBeaconState, slot: Slot, @@ -402,7 +419,7 @@ func partialBeaconBlock*( func partialBeaconBlock*( cfg: RuntimeConfig, - state: var electra.HashedBeaconState, + state: var (electra.HashedBeaconState | fulu.HashedBeaconState), proposer_index: ValidatorIndex, randao_reveal: ValidatorSig, eth1_data: Eth1Data, @@ -539,6 +556,31 @@ proc makeBeaconBlockWithRewards*( ]) else: raiseAssert "Attempt to use non-Electra payload with post-Deneb state" + elif consensusFork == ConsensusFork.Fulu: + forkyState.data.latest_execution_payload_header.transactions_root = + transactions_root.get + + debugFuluComment "verify (again) that this is what builder API needs" + when executionPayload is fulu.ExecutionPayloadForSigning: + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#beaconblockbody + forkyState.data.latest_block_header.body_root = hash_tree_root( + [hash_tree_root(randao_reveal), + hash_tree_root(eth1_data), + hash_tree_root(graffiti), + hash_tree_root(validator_changes.proposer_slashings), + hash_tree_root(validator_changes.electra_attester_slashings), + hash_tree_root( + List[electra.Attestation, Limit MAX_ATTESTATIONS]( + attestations)), + hash_tree_root(List[Deposit, Limit MAX_DEPOSITS](deposits)), + hash_tree_root(validator_changes.voluntary_exits), + hash_tree_root(sync_aggregate), + execution_payload_root.get, + hash_tree_root(validator_changes.bls_to_execution_changes), + hash_tree_root(kzg_commitments.get) + ]) + else: + raiseAssert "Attempt to use non-Fulu payload with post-Electra state" else: static: raiseAssert "Unreachable" @@ -566,6 +608,10 @@ proc makeBeaconBlockWithRewards*( case state.kind of ConsensusFork.Electra: makeBeaconBlock(electra) else: raiseAssert "Attempt to use Electra payload with non-Electra state" + elif payloadFork == ConsensusFork.Fulu: + case state.kind + of ConsensusFork.Fulu: makeBeaconBlock(fulu) + else: raiseAssert "Attempt to use Electra payload with non-Fulu state" else: {.error: "Unsupported fork".} diff --git a/beacon_chain/spec/state_transition_block.nim b/beacon_chain/spec/state_transition_block.nim index 2936a9a6ab..b8e42ceb0b 100644 --- a/beacon_chain/spec/state_transition_block.nim +++ b/beacon_chain/spec/state_transition_block.nim @@ -364,7 +364,7 @@ proc process_deposit*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-process_deposit_request func process_deposit_request*( - cfg: RuntimeConfig, state: var electra.BeaconState, + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), deposit_request: DepositRequest, flags: UpdateFlags): Result[void, cstring] = # Set deposit request start index @@ -459,7 +459,8 @@ proc process_voluntary_exit*( proc process_bls_to_execution_change*( cfg: RuntimeConfig, - state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState), + state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState), signed_address_change: SignedBLSToExecutionChange): Result[void, cstring] = ? check_bls_to_execution_change( cfg.genesisFork, state, signed_address_change, {}) @@ -478,7 +479,7 @@ proc process_bls_to_execution_change*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#new-process_withdrawal_request func process_withdrawal_request*( - cfg: RuntimeConfig, state: var electra.BeaconState, + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), bucketSortedValidators: BucketSortedValidators, withdrawal_request: WithdrawalRequest, cache: var StateCache) = let @@ -562,7 +563,8 @@ func process_withdrawal_request*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-is_valid_switch_to_compounding_request func is_valid_switch_to_compounding_request( - state: electra.BeaconState, consolidation_request: ConsolidationRequest, + state: electra.BeaconState | fulu.BeaconState, + consolidation_request: ConsolidationRequest, source_validator: Validator): bool = # Switch to compounding requires source and target be equal if consolidation_request.source_pubkey != consolidation_request.target_pubkey: @@ -592,7 +594,7 @@ func is_valid_switch_to_compounding_request( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-process_consolidation_request func process_consolidation_request*( - cfg: RuntimeConfig, state: var electra.BeaconState, + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), bucketSortedValidators: BucketSortedValidators, consolidation_request: ConsolidationRequest, cache: var StateCache) = @@ -797,7 +799,8 @@ func get_proposer_reward*(participant_reward: Gwei): Gwei = # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/altair/beacon-chain.md#sync-aggregate-processing proc process_sync_aggregate*( state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState | electra.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState), sync_aggregate: SomeSyncAggregate, total_active_balance: Gwei, flags: UpdateFlags, cache: var StateCache): Result[Gwei, cstring] = if strictVerification in flags and state.slot > 1.Slot: @@ -1051,12 +1054,68 @@ proc process_execution_payload*( ok() +# copy of datatypes/fulu.nim +type SomeFuluBeaconBlockBody = + fulu.BeaconBlockBody | fulu.SigVerifiedBeaconBlockBody | + fulu.TrustedBeaconBlockBody + +# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#modified-process_execution_payload +proc process_execution_payload*( + state: var fulu.BeaconState, body: SomeFuluBeaconBlockBody, + notify_new_payload: fulu.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 = fulu.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.5.0-alpha.3/specs/capella/beacon-chain.md#new-process_withdrawals # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#updated-process_withdrawals func process_withdrawals*( - state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState), + state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState), payload: capella.ExecutionPayload | deneb.ExecutionPayload | - electra.ExecutionPayload): + electra.ExecutionPayload | fulu.ExecutionPayload): Result[void, cstring] = when typeof(state).kind >= ConsensusFork.Electra: let (expected_withdrawals, partial_withdrawals_count) = @@ -1311,3 +1370,36 @@ proc process_block*( state, blck.body.sync_aggregate, total_active_balance, flags, cache) ok(operations_rewards) + +type SomeFuluBlock = + fulu.BeaconBlock | fulu.SigVerifiedBeaconBlock | fulu.TrustedBeaconBlock +proc process_block*( + cfg: RuntimeConfig, + state: var fulu.BeaconState, blck: SomeFuluBlock, + flags: UpdateFlags, cache: var StateCache): Result[BlockRewards, 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(_: fulu.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) + var operations_rewards = ? process_operations( + cfg, state, blck.body, base_reward_per_increment, flags, cache) + operations_rewards.sync_aggregate = ? process_sync_aggregate( + state, blck.body.sync_aggregate, total_active_balance, flags, cache) + + ok(operations_rewards) diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index c3db703e49..20e4ff78b1 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -177,7 +177,7 @@ from ./datatypes/deneb import BeaconState # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/altair/beacon-chain.md#get_unslashed_participating_indices func get_unslashed_participating_balances*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState): + deneb.BeaconState | electra.BeaconState | fulu.BeaconState): UnslashedParticipatingBalances = let previous_epoch = get_previous_epoch(state) @@ -229,7 +229,7 @@ func get_unslashed_participating_balances*( func is_unslashed_participating_index( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.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 @@ -422,7 +422,8 @@ proc compute_unrealized_finality*( # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/altair/beacon-chain.md#justification-and-finalization proc process_justification_and_finalization*( state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState | electra.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState), balances: UnslashedParticipatingBalances, flags: UpdateFlags = {}) = # Initial FFG checkpoint values have a `0x00` stub for `root`. @@ -444,7 +445,7 @@ proc process_justification_and_finalization*( proc compute_unrealized_finality*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState): FinalityCheckpoints = + deneb.BeaconState | electra.BeaconState | fulu.BeaconState): FinalityCheckpoints = if get_current_epoch(state) <= GENESIS_EPOCH + 1: return FinalityCheckpoints( justified: state.current_justified_checkpoint, @@ -640,7 +641,7 @@ func get_attestation_deltas( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/altair/beacon-chain.md#get_base_reward func get_base_reward_increment*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.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``. @@ -652,7 +653,7 @@ func get_base_reward_increment*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/altair/beacon-chain.md#get_flag_index_deltas func get_flag_index_reward*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.BeaconState, base_reward: Gwei, active_increments: uint64, unslashed_participating_increments: uint64, @@ -681,7 +682,7 @@ func get_active_increments*( # Combines get_flag_index_deltas() and get_inactivity_penalty_deltas() template get_flag_and_inactivity_delta( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.BeaconState, base_reward_per_increment: Gwei, finality_delay: uint64, previous_epoch: Epoch, active_increments: uint64, penalty_denominator: uint64, @@ -736,7 +737,7 @@ template get_flag_and_inactivity_delta( iterator get_flag_and_inactivity_deltas*( cfg: RuntimeConfig, state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.BeaconState, base_reward_per_increment: Gwei, info: var altair.EpochInfo, finality_delay: uint64): (ValidatorIndex, Gwei, Gwei, Gwei, Gwei, Gwei, Gwei) = @@ -782,7 +783,7 @@ iterator get_flag_and_inactivity_deltas*( func get_flag_and_inactivity_delta_for_validator( cfg: RuntimeConfig, - state: deneb.BeaconState | electra.BeaconState, + state: deneb.BeaconState | electra.BeaconState | fulu.BeaconState, base_reward_per_increment: Gwei, info: var altair.EpochInfo, finality_delay: uint64, vidx: ValidatorIndex, inactivity_score: Gwei): Opt[(ValidatorIndex, Gwei, Gwei, Gwei, Gwei, Gwei, Gwei)] = @@ -846,7 +847,8 @@ func process_rewards_and_penalties*( func process_rewards_and_penalties*( cfg: RuntimeConfig, state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState | electra.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState), info: var altair.EpochInfo) = if get_current_epoch(state) == GENESIS_EPOCH: return @@ -949,8 +951,8 @@ func process_registry_updates*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.5/specs/electra/beacon-chain.md#modified-process_registry_updates func process_registry_updates*( - cfg: RuntimeConfig, state: var electra.BeaconState, cache: var StateCache): - Result[void, cstring] = + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), + cache: var StateCache): Result[void, cstring] = # Process activation eligibility and ejections for index in 0 ..< state.validators.len: let validator = state.validators.item(index) @@ -986,7 +988,8 @@ 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 or state is electra.BeaconState: + state is deneb.BeaconState or state is electra.BeaconState or + state is fulu.BeaconState: PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX else: {.fatal: "process_slashings: incorrect BeaconState type".} @@ -1020,6 +1023,14 @@ func get_slashing_penalty*( penalty_per_effective_balance_increment = adjusted_total_slashing_balance div (total_balance div increment) + # [Modified in Electra:EIP7251] + penalty_per_effective_balance_increment * effective_balance_increments + elif consensusFork == ConsensusFork.Fulu: + let + effective_balance_increments = validator.effective_balance div increment + penalty_per_effective_balance_increment = + adjusted_total_slashing_balance div (total_balance div increment) + # [Modified in Electra:EIP7251] penalty_per_effective_balance_increment * effective_balance_increments else: @@ -1133,7 +1144,7 @@ func process_participation_record_updates*(state: var phase0.BeaconState) = func process_participation_flag_updates*( state: var (altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | - electra.BeaconState)) = + electra.BeaconState | fulu.BeaconState)) = state.previous_epoch_participation = state.current_epoch_participation const zero = 0.ParticipationFlags @@ -1148,7 +1159,7 @@ func process_participation_flag_updates*( func process_sync_committee_updates*( state: var (altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState | - electra.BeaconState)) = + electra.BeaconState | fulu.BeaconState)) = let next_epoch = get_current_epoch(state) + 1 if next_epoch.is_sync_committee_period(): state.current_sync_committee = state.next_sync_committee @@ -1158,7 +1169,7 @@ func process_sync_committee_updates*( template compute_inactivity_update( cfg: RuntimeConfig, state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState, + deneb.BeaconState | electra.BeaconState | fulu.BeaconState, info: altair.EpochInfo, pre_inactivity_score: uint64): uint64 = let previous_epoch = get_previous_epoch(state) # get_eligible_validator_indices() @@ -1183,7 +1194,8 @@ template compute_inactivity_update( func process_inactivity_updates*( cfg: RuntimeConfig, state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState | electra.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState), info: altair.EpochInfo) = # Score updates based on previous epoch participation, skip genesis epoch if get_current_epoch(state) == GENESIS_EPOCH: @@ -1209,7 +1221,8 @@ func process_inactivity_updates*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/capella/beacon-chain.md#historical-summaries-updates func process_historical_summaries_update*( - state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState)): + state: var (capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.BeaconState)): Result[void, cstring] = # Set historical block root accumulator. let next_epoch = get_current_epoch(state) + 1 @@ -1229,8 +1242,9 @@ from ".."/validator_bucket_sort import # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-apply_pending_deposit func apply_pending_deposit( - cfg: RuntimeConfig, state: var electra.BeaconState, deposit: PendingDeposit, - validator_index: Opt[ValidatorIndex]): Result[void, cstring] = + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), + deposit: PendingDeposit, validator_index: Opt[ValidatorIndex]): + Result[void, cstring] = ## Applies ``deposit`` to the ``state``. if validator_index.isNone: # Verify the deposit signature (proof of possession) which is not checked by @@ -1249,7 +1263,7 @@ func apply_pending_deposit( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-process_pending_deposits func process_pending_deposits*( - cfg: RuntimeConfig, state: var electra.BeaconState, + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState) , cache: var StateCache): Result[void, cstring] = let next_epoch = get_current_epoch(state) + 1 @@ -1342,7 +1356,7 @@ func process_pending_deposits*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/electra/beacon-chain.md#new-process_pending_consolidations func process_pending_consolidations*( - cfg: RuntimeConfig, state: var electra.BeaconState): + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState) ): Result[void, cstring] = let next_epoch = get_current_epoch(state) + 1 var next_pending_consolidation = 0 @@ -1416,7 +1430,7 @@ proc process_epoch*( func init*( info: var altair.EpochInfo, state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState) = + deneb.BeaconState | electra.BeaconState | fulu.BeaconState) = # init participation, overwriting the full structure info.balances = get_unslashed_participating_balances(state) info.validators.setLen(state.validators.len()) @@ -1434,7 +1448,7 @@ func init*( func init*( T: type altair.EpochInfo, state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | - deneb.BeaconState | electra.BeaconState): T = + deneb.BeaconState | electra.BeaconState | fulu.BeaconState): T = init(result, state) # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/altair/beacon-chain.md#epoch-processing @@ -1527,7 +1541,7 @@ proc process_epoch*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.1/specs/electra/beacon-chain.md#epoch-processing proc process_epoch*( - cfg: RuntimeConfig, state: var electra.BeaconState, + cfg: RuntimeConfig, state: var (electra.BeaconState | fulu.BeaconState), flags: UpdateFlags, cache: var StateCache, info: var altair.EpochInfo): Result[void, cstring] = let epoch = get_current_epoch(state) @@ -1571,7 +1585,8 @@ proc process_epoch*( ok() proc get_validator_balance_after_epoch*( - cfg: RuntimeConfig, state: deneb.BeaconState | electra.BeaconState, + cfg: RuntimeConfig, state: deneb.BeaconState | electra.BeaconState | + fulu.BeaconState, cache: var StateCache, info: var altair.EpochInfo, index: ValidatorIndex): Gwei = # Run a subset of process_epoch() which affects an individual validator, diff --git a/beacon_chain/validator_client/api.nim b/beacon_chain/validator_client/api.nim index c4d5c3f4b3..e2416c7f61 100644 --- a/beacon_chain/validator_client/api.nim +++ b/beacon_chain/validator_client/api.nim @@ -2511,6 +2511,8 @@ proc publishBlockV2*( publishBlockV2(it, some(broadcast_validation), data.denebData) of ConsensusFork.Electra: publishBlockV2(it, some(broadcast_validation), data.electraData) + of ConsensusFork.Fulu: + publishBlockV2(it, some(broadcast_validation), data.fuluData) do: if apiResponse.isErr(): handleCommunicationError() @@ -2562,6 +2564,8 @@ proc publishBlockV2*( publishBlockV2(it, some(broadcast_validation), data.denebData) of ConsensusFork.Electra: publishBlockV2(it, some(broadcast_validation), data.electraData) + of ConsensusFork.Fulu: + publishBlockV2(it, some(broadcast_validation), data.fuluData) do: if apiResponse.isErr(): @@ -2627,6 +2631,8 @@ proc publishBlock*( publishBlock(it, data.denebData) of ConsensusFork.Electra: publishBlock(it, data.electraData) + of ConsensusFork.Fulu: + publishBlock(it, data.fuluData) do: if apiResponse.isErr(): handleCommunicationError() @@ -2675,6 +2681,8 @@ proc publishBlock*( publishBlock(it, data.denebData) of ConsensusFork.Electra: publishBlock(it, data.electraData) + of ConsensusFork.Fulu: + publishBlock(it, data.fuluData) do: if apiResponse.isErr(): @@ -2744,6 +2752,9 @@ proc publishBlindedBlockV2*( of ConsensusFork.Electra: publishBlindedBlockV2(it, some(broadcast_validation), data.electraData) + of ConsensusFork.Fulu: + publishBlindedBlockV2(it, some(broadcast_validation), + data.fuluData) do: if apiResponse.isErr(): handleCommunicationError() @@ -2800,6 +2811,9 @@ proc publishBlindedBlockV2*( of ConsensusFork.Electra: publishBlindedBlockV2(it, some(broadcast_validation), data.electraData) + of ConsensusFork.Fulu: + publishBlindedBlockV2(it, some(broadcast_validation), + data.fuluData) do: if apiResponse.isErr(): handleCommunicationError() @@ -2863,6 +2877,8 @@ proc publishBlindedBlock*( publishBlindedBlock(it, data.denebData) of ConsensusFork.Electra: publishBlindedBlock(it, data.electraData) + of ConsensusFork.Fulu: + publishBlindedBlock(it, data.fuluData) do: if apiResponse.isErr(): handleCommunicationError() @@ -2910,6 +2926,8 @@ proc publishBlindedBlock*( publishBlindedBlock(it, data.denebData) of ConsensusFork.Electra: publishBlindedBlock(it, data.electraData) + of ConsensusFork.Fulu: + publishBlindedBlock(it, data.fuluData) do: if apiResponse.isErr(): handleCommunicationError() diff --git a/beacon_chain/validator_client/common.nim b/beacon_chain/validator_client/common.nim index 55fab59a7c..06b340cd5f 100644 --- a/beacon_chain/validator_client/common.nim +++ b/beacon_chain/validator_client/common.nim @@ -46,10 +46,10 @@ const ZeroTimeDiff* = TimeDiff(nanoseconds: 0'i64) -static: doAssert(high(ConsensusFork) == ConsensusFork.Electra, +static: doAssert(high(ConsensusFork) == ConsensusFork.Fulu, "Update OptionalForks constant!") const - OptionalForks* = {ConsensusFork.Electra} + OptionalForks* = {ConsensusFork.Electra, ConsensusFork.Fulu} ## When a new ConsensusFork is added and before this fork is activated on ## `mainnet`, it should be part of `OptionalForks`. ## In this case, the client will ignore missing _VERSION diff --git a/beacon_chain/validators/beacon_validators.nim b/beacon_chain/validators/beacon_validators.nim index 8245bdeee8..770cc4ab7d 100644 --- a/beacon_chain/validators/beacon_validators.nim +++ b/beacon_chain/validators/beacon_validators.nim @@ -41,7 +41,7 @@ import ".."/[conf, beacon_clock, beacon_node], "."/[ keystore_management, slashing_protection, validator_duties, validator_pool], - ".."/spec/mev/[rest_deneb_mev_calls, rest_electra_mev_calls] + ".."/spec/mev/[rest_deneb_mev_calls, rest_electra_mev_calls, rest_fulu_mev_calls] from std/sequtils import countIt, foldl, mapIt from eth/async_utils import awaitWithTimeout @@ -522,7 +522,7 @@ proc makeBeaconBlockForHeadAndSlot*( let attestations = - when PayloadType.kind == ConsensusFork.Electra: + when PayloadType.kind >= ConsensusFork.Electra: node.attestationPool[].getElectraAttestationsForBlock(state[], cache) else: node.attestationPool[].getAttestationsForBlock(state[], cache) @@ -615,7 +615,8 @@ proc makeBeaconBlockForHeadAndSlot*( proc getBlindedExecutionPayload[ EPH: deneb_mev.BlindedExecutionPayloadAndBlobsBundle | - electra_mev.BlindedExecutionPayloadAndBlobsBundle]( + electra_mev.BlindedExecutionPayloadAndBlobsBundle | + fulu_mev.BlindedExecutionPayloadAndBlobsBundle]( node: BeaconNode, payloadBuilderClient: RestClientRef, slot: Slot, executionBlockHash: Eth2Digest, pubkey: ValidatorPubKey): Future[BlindedBlockResult[EPH]] {.async: (raises: [CancelledError, RestError]).} = @@ -653,6 +654,24 @@ proc getBlindedExecutionPayload[ "Unable to decode Electra blinded header: " & $res.error & " with HTTP status " & $response.status & ", Content-Type " & $response.contentType & " and content " & $response.data) + elif EPH is fulu_mev.BlindedExecutionPayloadAndBlobsBundle: + + debugFuluComment "Because electra MEV isn't working yet, this is a placeholder copy" + let + response = awaitWithTimeout( + payloadBuilderClient.getHeaderFulu( + slot, executionBlockHash, pubkey), + BUILDER_PROPOSAL_DELAY_TOLERANCE): + return err "Timeout obtaining Fulu blinded header from builder" + + res = decodeBytes( + GetHeaderResponseFulu, response.data, response.contentType) + + blindedHeader = res.valueOr: + return err( + "Unable to decode Fulu blinded header: " & $res.error & + " with HTTP status " & $response.status & ", Content-Type " & + $response.contentType & " and content " & $response.data) else: static: doAssert false @@ -697,9 +716,33 @@ func constructSignableBlindedBlock[T: deneb_mev.SignedBlindedBeaconBlock]( blindedBlock -func constructSignableBlindedBlock[T: electra_mev.SignedBlindedBeaconBlock]( - blck: electra.BeaconBlock, - blindedBundle: electra_mev.BlindedExecutionPayloadAndBlobsBundle): T = +func constructSignableBlindedBlock[T: electra_mev.SignedBlindedBeaconBlock | + fulu_mev.SignedBlindedBeaconBlock]( + blck: electra.BeaconBlock | fulu.BeaconBlock, + blindedBundle: electra_mev.BlindedExecutionPayloadAndBlobsBundle | + fulu_mev.BlindedExecutionPayloadAndBlobsBundle): T = + # Leaves signature field default, to be filled in by caller + const + blckFields = getFieldNames(typeof(blck)) + blckBodyFields = getFieldNames(typeof(blck.body)) + + var blindedBlock: T + + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/validator.md#block-proposal + copyFields(blindedBlock.message, blck, blckFields) + copyFields(blindedBlock.message.body, blck.body, blckBodyFields) + assign( + blindedBlock.message.body.execution_payload_header, + blindedBundle.execution_payload_header) + assign( + blindedBlock.message.body.blob_kzg_commitments, + blindedBundle.blob_kzg_commitments) + + blindedBlock + +func constructSignableBlindedBlock[T: fulu_mev.SignedBlindedBeaconBlock]( + blck: fulu.BeaconBlock, + blindedBundle: fulu_mev.BlindedExecutionPayloadAndBlobsBundle): T = # Leaves signature field default, to be filled in by caller const blckFields = getFieldNames(typeof(blck)) @@ -767,9 +810,34 @@ func constructPlainBlindedBlock[T: electra_mev.BlindedBeaconBlock]( blindedBlock +func constructPlainBlindedBlock[T: fulu_mev.BlindedBeaconBlock]( + blck: ForkyBeaconBlock, + blindedBundle: fulu_mev.BlindedExecutionPayloadAndBlobsBundle): T = + # https://github.com/nim-lang/Nim/issues/23020 workaround + static: doAssert T is fulu_mev.BlindedBeaconBlock + + const + blckFields = getFieldNames(typeof(blck)) + blckBodyFields = getFieldNames(typeof(blck.body)) + + var blindedBlock: T + + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/validator.md#block-proposal + copyFields(blindedBlock, blck, blckFields) + copyFields(blindedBlock.body, blck.body, blckBodyFields) + assign( + blindedBlock.body.execution_payload_header, + blindedBundle.execution_payload_header) + assign( + blindedBlock.body.blob_kzg_commitments, + blindedBundle.blob_kzg_commitments) + + blindedBlock + proc blindedBlockCheckSlashingAndSign[ T: deneb_mev.SignedBlindedBeaconBlock | - electra_mev.SignedBlindedBeaconBlock]( + electra_mev.SignedBlindedBeaconBlock | + fulu_mev.SignedBlindedBeaconBlock]( node: BeaconNode, slot: Slot, validator: AttachedValidator, validator_index: ValidatorIndex, nonsignedBlindedBlock: T): Future[Result[T, string]] {.async: (raises: [CancelledError]).} = @@ -803,11 +871,13 @@ proc blindedBlockCheckSlashingAndSign[ func getUnsignedBlindedBeaconBlock[ T: deneb_mev.SignedBlindedBeaconBlock | - electra_mev.SignedBlindedBeaconBlock]( + electra_mev.SignedBlindedBeaconBlock | + fulu_mev.SignedBlindedBeaconBlock]( node: BeaconNode, slot: Slot, validator_index: ValidatorIndex, forkedBlock: ForkedBeaconBlock, executionPayloadHeader: deneb_mev.BlindedExecutionPayloadAndBlobsBundle | - electra_mev.BlindedExecutionPayloadAndBlobsBundle): + electra_mev.BlindedExecutionPayloadAndBlobsBundle | + fulu_mev.BlindedExecutionPayloadAndBlobsBundle): Result[T, string] = withBlck(forkedBlock): when consensusFork >= ConsensusFork.Deneb: @@ -815,7 +885,9 @@ func getUnsignedBlindedBeaconBlock[ (T is deneb_mev.SignedBlindedBeaconBlock and consensusFork == ConsensusFork.Deneb) or (T is electra_mev.SignedBlindedBeaconBlock and - consensusFork == ConsensusFork.Electra)): + consensusFork == ConsensusFork.Electra) or + (T is fulu_mev.SignedBlindedBeaconBlock and + consensusFork == ConsensusFork.Fulu)): return err("getUnsignedBlindedBeaconBlock: mismatched block/payload types") else: return ok constructSignableBlindedBlock[T]( @@ -825,7 +897,8 @@ func getUnsignedBlindedBeaconBlock[ proc getBlindedBlockParts[ EPH: deneb_mev.BlindedExecutionPayloadAndBlobsBundle | - electra_mev.BlindedExecutionPayloadAndBlobsBundle]( + electra_mev.BlindedExecutionPayloadAndBlobsBundle | + fulu_mev.BlindedExecutionPayloadAndBlobsBundle]( node: BeaconNode, payloadBuilderClient: RestClientRef, head: BlockRef, pubkey: ValidatorPubKey, slot: Slot, randao: ValidatorSig, validator_index: ValidatorIndex, graffiti: GraffitiBytes): @@ -899,6 +972,21 @@ proc getBlindedBlockParts[ electra_mev.BlindedExecutionPayloadAndBlobsBundle.execution_payload_header copyFields( shimExecutionPayload.executionPayload, actualEPH, getFieldNames(ElectraEPH)) + elif EPH is fulu_mev.BlindedExecutionPayloadAndBlobsBundle: + debugFuluComment "verify (again, after change) this is what builder API needs" + type PayloadType = fulu.ExecutionPayloadForSigning + template actualEPH: untyped = + executionPayloadHeader.get.blindedBlckPart.execution_payload_header + let + withdrawals_root = Opt.some actualEPH.withdrawals_root + kzg_commitments = Opt.some( + executionPayloadHeader.get.blindedBlckPart.blob_kzg_commitments) + + var shimExecutionPayload: PayloadType + type FuluEPH = + fulu_mev.BlindedExecutionPayloadAndBlobsBundle.execution_payload_header + copyFields( + shimExecutionPayload.executionPayload, actualEPH, getFieldNames(FuluEPH)) else: static: doAssert false @@ -926,7 +1014,8 @@ proc getBlindedBlockParts[ proc getBuilderBid[ SBBB: deneb_mev.SignedBlindedBeaconBlock | - electra_mev.SignedBlindedBeaconBlock]( + electra_mev.SignedBlindedBeaconBlock | + fulu_mev.SignedBlindedBeaconBlock]( node: BeaconNode, payloadBuilderClient: RestClientRef, head: BlockRef, validator_pubkey: ValidatorPubKey, slot: Slot, randao: ValidatorSig, graffitiBytes: GraffitiBytes, validator_index: ValidatorIndex): @@ -937,6 +1026,8 @@ proc getBuilderBid[ type EPH = deneb_mev.BlindedExecutionPayloadAndBlobsBundle elif SBBB is electra_mev.SignedBlindedBeaconBlock: type EPH = electra_mev.BlindedExecutionPayloadAndBlobsBundle + elif SBBB is fulu_mev.SignedBlindedBeaconBlock: + type EPH = fulu_mev.BlindedExecutionPayloadAndBlobsBundle else: static: doAssert false @@ -969,7 +1060,8 @@ proc proposeBlockMEV( node: BeaconNode, payloadBuilderClient: RestClientRef, blindedBlock: deneb_mev.SignedBlindedBeaconBlock | - electra_mev.SignedBlindedBeaconBlock): + electra_mev.SignedBlindedBeaconBlock | + fulu_mev.SignedBlindedBeaconBlock): Future[Result[BlockRef, string]] {.async: (raises: [CancelledError]).} = let unblindedBlockRef = await node.unblindAndRouteBlockMEV( payloadBuilderClient, blindedBlock) @@ -1012,7 +1104,9 @@ proc makeBlindedBeaconBlockForHeadAndSlot*[BBB: ForkyBlindedBeaconBlock]( ## ## This function is used by the validator client, but not the beacon node for ## its own validators. - when BBB is electra_mev.BlindedBeaconBlock: + when BBB is fulu_mev.BlindedBeaconBlock: + type EPH = fulu_mev.BlindedExecutionPayloadAndBlobsBundle + elif BBB is electra_mev.BlindedBeaconBlock: type EPH = electra_mev.BlindedExecutionPayloadAndBlobsBundle elif BBB is deneb_mev.BlindedBeaconBlock: type EPH = deneb_mev.BlindedExecutionPayloadAndBlobsBundle @@ -1051,7 +1145,9 @@ proc makeBlindedBeaconBlockForHeadAndSlot*[BBB: ForkyBlindedBeaconBlock]( when ((consensusFork == ConsensusFork.Deneb and EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle) or (consensusFork == ConsensusFork.Electra and - EPH is electra_mev.BlindedExecutionPayloadAndBlobsBundle)): + EPH is electra_mev.BlindedExecutionPayloadAndBlobsBundle) or + (consensusFork == ConsensusFork.Fulu and + EPH is fulu_mev.BlindedExecutionPayloadAndBlobsBundle)): return ok( BuilderBid[BBB]( blindedBlckPart: diff --git a/beacon_chain/validators/message_router_mev.nim b/beacon_chain/validators/message_router_mev.nim index 71c22a037f..d8633e46c3 100644 --- a/beacon_chain/validators/message_router_mev.nim +++ b/beacon_chain/validators/message_router_mev.nim @@ -47,7 +47,8 @@ proc unblindAndRouteBlockMEV*( node: BeaconNode, payloadBuilderRestClient: RestClientRef, blindedBlock: deneb_mev.SignedBlindedBeaconBlock | - electra_mev.SignedBlindedBeaconBlock): + electra_mev.SignedBlindedBeaconBlock | + fulu_mev.SignedBlindedBeaconBlock): Future[Result[Opt[BlockRef], string]] {.async: (raises: [CancelledError]).} = const consensusFork = typeof(blindedBlock).kind @@ -91,6 +92,9 @@ proc unblindAndRouteBlockMEV*( elif blindedBlock is electra_mev.SignedBlindedBeaconBlock: let res = decodeBytes( SubmitBlindedBlockResponseElectra, response.data, response.contentType) + elif blindedBlock is fulu_mev.SignedBlindedBeaconBlock: + let res = decodeBytes( + SubmitBlindedBlockResponseFulu, response.data, response.contentType) else: static: doAssert false diff --git a/beacon_chain/validators/validator_pool.nim b/beacon_chain/validators/validator_pool.nim index 7e304fd9e8..b0e977e20a 100644 --- a/beacon_chain/validators/validator_pool.nim +++ b/beacon_chain/validators/validator_pool.nim @@ -533,7 +533,8 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, blck: ForkedBeaconBlock | ForkedBlindedBeaconBlock | ForkedMaybeBlindedBeaconBlock | deneb_mev.BlindedBeaconBlock | - electra_mev.BlindedBeaconBlock + electra_mev.BlindedBeaconBlock | + fulu_mev.BlindedBeaconBlock ): Future[SignatureResult] {.async: (raises: [CancelledError]).} = type SomeBlockBody = @@ -541,7 +542,9 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, deneb.BeaconBlockBody | deneb_mev.BlindedBeaconBlockBody | electra.BeaconBlockBody | - electra_mev.BlindedBeaconBlockBody + electra_mev.BlindedBeaconBlockBody | + fulu.BeaconBlockBody | + fulu_mev.BlindedBeaconBlockBody template blockPropertiesProofs(blockBody: SomeBlockBody, forkIndexField: untyped): seq[Web3SignerMerkleProof] = @@ -596,6 +599,19 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, Web3SignerForkedBeaconBlock(kind: ConsensusFork.Electra, data: blck.electraData.toBeaconBlockHeader), proofs) + of ConsensusFork.Fulu: + case v.data.remoteType + of RemoteSignerType.Web3Signer: + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: blck.fuluData.toBeaconBlockHeader)) + of RemoteSignerType.VerifyingWeb3Signer: + let proofs = blockPropertiesProofs( + blck.fuluData.body, fuluIndex) + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: blck.fuluData.toBeaconBlockHeader), + proofs) elif blck is deneb_mev.BlindedBeaconBlock: case v.data.remoteType of RemoteSignerType.Web3Signer: @@ -622,6 +638,19 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, Web3SignerForkedBeaconBlock(kind: ConsensusFork.Electra, data: blck.toBeaconBlockHeader), proofs) + elif blck is fulu_mev.BlindedBeaconBlock: + case v.data.remoteType + of RemoteSignerType.Web3Signer: + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: blck.toBeaconBlockHeader)) + of RemoteSignerType.VerifyingWeb3Signer: + let proofs = blockPropertiesProofs( + blck.body, fuluIndex) + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: blck.toBeaconBlockHeader), + proofs) elif blck is ForkedMaybeBlindedBeaconBlock: withForkyMaybeBlindedBlck(blck): # TODO why isn't this a case statement @@ -700,6 +729,34 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, Web3SignerForkedBeaconBlock(kind: ConsensusFork.Electra, data: forkyMaybeBlindedBlck.`block`.toBeaconBlockHeader), proofs) + elif consensusFork == ConsensusFork.Fulu: + when isBlinded: + case v.data.remoteType + of RemoteSignerType.Web3Signer: + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: forkyMaybeBlindedBlck.toBeaconBlockHeader)) + of RemoteSignerType.VerifyingWeb3Signer: + let proofs = + blockPropertiesProofs(forkyMaybeBlindedBlck.body, + fuluIndex) + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: forkyMaybeBlindedBlck.toBeaconBlockHeader), proofs) + else: + case v.data.remoteType + of RemoteSignerType.Web3Signer: + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: forkyMaybeBlindedBlck.`block`.toBeaconBlockHeader)) + of RemoteSignerType.VerifyingWeb3Signer: + let proofs = + blockPropertiesProofs(forkyMaybeBlindedBlck.`block`.body, + fuluIndex) + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: forkyMaybeBlindedBlck.`block`.toBeaconBlockHeader), + proofs) else: case blck.kind of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix: @@ -743,6 +800,19 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork, Web3SignerForkedBeaconBlock(kind: ConsensusFork.Electra, data: blck.electraData.toBeaconBlockHeader), proofs) + of ConsensusFork.Fulu: + case v.data.remoteType + of RemoteSignerType.Web3Signer: + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: blck.fuluData.toBeaconBlockHeader)) + of RemoteSignerType.VerifyingWeb3Signer: + let proofs = blockPropertiesProofs( + blck.fuluData.body, fuluIndex) + Web3SignerRequest.init(fork, genesis_validators_root, + Web3SignerForkedBeaconBlock(kind: ConsensusFork.Fulu, + data: blck.fuluData.toBeaconBlockHeader), + proofs) await v.signData(web3signerRequest) # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/validator.md#aggregate-signature diff --git a/ncli/ncli.nim b/ncli/ncli.nim index cbb675bf6a..b38d668c01 100644 --- a/ncli/ncli.nim +++ b/ncli/ncli.nim @@ -99,6 +99,7 @@ template saveSSZFile(filename: string, value: ForkedHashedBeaconState) = 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) + of ConsensusFork.Fulu: SSZ.saveFile(filename, value.fuluData.data) except IOError: raiseAssert "error saving SSZ file" @@ -240,18 +241,21 @@ proc doSSZ(conf: NcliConf) = of "capella_signed_block": printit(capella.SignedBeaconBlock) of "deneb_signed_block": printit(deneb.SignedBeaconBlock) of "electra_signed_block": printit(electra.SignedBeaconBlock) + of "fulu_signed_block": printit(fulu.SignedBeaconBlock) of "phase0_block": printit(phase0.BeaconBlock) of "altair_block": printit(altair.BeaconBlock) of "bellatrix_block": printit(bellatrix.BeaconBlock) of "capella_block": printit(capella.BeaconBlock) of "deneb_block": printit(deneb.BeaconBlock) of "electra_block": printit(electra.BeaconBlock) + of "fulu_block": printit(fulu.BeaconBlock) of "phase0_block_body": printit(phase0.BeaconBlockBody) of "altair_block_body": printit(altair.BeaconBlockBody) of "bellatrix_block_body": printit(bellatrix.BeaconBlockBody) of "capella_block_body": printit(capella.BeaconBlockBody) of "deneb_block_body": printit(deneb.BeaconBlockBody) of "electra_block_body": printit(electra.BeaconBlockBody) + of "fulu_block_body": printit(fulu.BeaconBlockBody) of "block_header": printit(BeaconBlockHeader) of "deposit": printit(Deposit) of "deposit_data": printit(DepositData) @@ -262,6 +266,7 @@ proc doSSZ(conf: NcliConf) = of "capella_state": printit(capella.BeaconState) of "deneb_state": printit(deneb.BeaconState) of "electra_state": printit(electra.BeaconState) + of "fulu_state": printit(fulu.BeaconState) of "proposer_slashing": printit(ProposerSlashing) of "voluntary_exit": printit(VoluntaryExit) diff --git a/ncli/ncli_common.nim b/ncli/ncli_common.nim index 0180ad0ebb..e4d6a7ee6e 100644 --- a/ncli/ncli_common.nim +++ b/ncli/ncli_common.nim @@ -275,7 +275,8 @@ proc collectEpochRewardsAndPenalties*( proc collectEpochRewardsAndPenalties*( rewardsAndPenalties: var seq[RewardsAndPenalties], state: var (altair.BeaconState | bellatrix.BeaconState | - capella.BeaconState | deneb.BeaconState | electra.BeaconState), + capella.BeaconState | deneb.BeaconState | electra.BeaconState | + fulu.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 14c0340c30..2f67345b04 100644 --- a/ncli/ncli_db.nim +++ b/ncli/ncli_db.nim @@ -248,7 +248,8 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = seq[bellatrix.TrustedSignedBeaconBlock], seq[capella.TrustedSignedBeaconBlock], seq[deneb.TrustedSignedBeaconBlock], - seq[electra.TrustedSignedBeaconBlock]) + seq[electra.TrustedSignedBeaconBlock], + seq[fulu.TrustedSignedBeaconBlock]) echo "Loaded head slot ", dag.head.slot, " selected ", blockRefs.len, " blocks" @@ -277,6 +278,9 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = of ConsensusFork.Electra: blocks[5].add dag.db.getBlock( blck.root, electra.TrustedSignedBeaconBlock).get() + of ConsensusFork.Fulu: + blocks[6].add dag.db.getBlock( + blck.root, fulu.TrustedSignedBeaconBlock).get() let stateData = newClone(dag.headState) @@ -289,7 +293,8 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = (ref bellatrix.HashedBeaconState)(), (ref capella.HashedBeaconState)(), (ref deneb.HashedBeaconState)(), - (ref electra.HashedBeaconState)()) + (ref electra.HashedBeaconState)(), + (ref fulu.HashedBeaconState)()) withTimer(timers[tLoadState]): doAssert dag.updateState( @@ -353,6 +358,9 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = of ConsensusFork.Electra: doAssert dbBenchmark.getState( forkyState.root, loadedState[5][].data, noRollback) + of ConsensusFork.Fulu: + doAssert dbBenchmark.getState( + forkyState.root, loadedState[6][].data, noRollback) if forkyState.data.slot.epoch mod 16 == 0: let loadedRoot = case consensusFork @@ -362,6 +370,7 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = 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) + of ConsensusFork.Fulu: hash_tree_root(loadedState[6][].data) doAssert hash_tree_root(forkyState.data) == loadedRoot processBlocks(blocks[0]) @@ -370,6 +379,7 @@ proc cmdBench(conf: DbConf, cfg: RuntimeConfig) = processBlocks(blocks[3]) processBlocks(blocks[4]) processBlocks(blocks[5]) + processBlocks(blocks[6]) printTimers(false, timers) @@ -384,6 +394,7 @@ proc cmdDumpState(conf: DbConf) = capellaState = (ref capella.HashedBeaconState)() denebState = (ref deneb.HashedBeaconState)() electraState = (ref electra.HashedBeaconState)() + fuluState = (ref fulu.HashedBeaconState)() for stateRoot in conf.stateRoot: if shouldShutDown: quit QuitSuccess @@ -403,6 +414,7 @@ proc cmdDumpState(conf: DbConf) = doit(capellaState[]) doit(denebState[]) doit(electraState[]) + doit(fuluState[]) echo "Couldn't load ", stateRoot diff --git a/ncli/ncli_testnet.nim b/ncli/ncli_testnet.nim index 8da0bd3583..36ab4df559 100644 --- a/ncli/ncli_testnet.nim +++ b/ncli/ncli_testnet.nim @@ -160,6 +160,11 @@ type desc: "The epoch of the Electra hard-fork" name: "electra-fork-epoch" .}: Epoch + fuluForkEpoch* {. + defaultValue: FAR_FUTURE_EPOCH + desc: "The epoch of the Fulu hard-fork" + name: "fulu-fork-epoch" .}: Epoch + outputGenesis* {. desc: "Output file where to write the initial state snapshot" name: "output-genesis" .}: OutFile @@ -318,6 +323,25 @@ func `as`(blk: BlockObject, T: type electra.ExecutionPayloadHeader): T = blob_gas_used: uint64 blk.blobGasUsed.getOrDefault(), excess_blob_gas: uint64 blk.excessBlobGas.getOrDefault()) +func `as`(blk: BlockObject, T: type fulu.ExecutionPayloadHeader): T = + T(parent_hash: blk.parentHash as Eth2Digest, + fee_recipient: blk.miner as ExecutionAddress, + state_root: blk.stateRoot as Eth2Digest, + receipts_root: blk.receiptsRoot as Eth2Digest, + logs_bloom: BloomLogs(data: distinctBase(blk.logsBloom)), + prev_randao: Eth2Digest(data: blk.difficulty.toByteArrayBE), + block_number: uint64 blk.number, + gas_limit: uint64 blk.gasLimit, + gas_used: uint64 blk.gasUsed, + timestamp: uint64 blk.timestamp, + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(blk.extraData.bytes), + base_fee_per_gas: blk.baseFeePerGas.getOrDefault(), + block_hash: blk.hash as Eth2Digest, + transactions_root: blk.transactionsRoot as Eth2Digest, + withdrawals_root: blk.withdrawalsRoot.getOrDefault() as Eth2Digest, + blob_gas_used: uint64 blk.blobGasUsed.getOrDefault(), + excess_blob_gas: uint64 blk.excessBlobGas.getOrDefault()) + func createDepositContractSnapshot( deposits: seq[DepositData], blockHash: Eth2Digest, @@ -460,7 +484,9 @@ proc doCreateTestnet*(config: CliConfig, initialState[].genesis_validators_root let genesisValidatorsRoot = - if config.electraForkEpoch == 0: + if config.fuluForkEpoch == 0: + createAndSaveState(genesisBlock as fulu.ExecutionPayloadHeader) + elif config.electraForkEpoch == 0: createAndSaveState(genesisBlock as electra.ExecutionPayloadHeader) elif config.denebForkEpoch == 0: createAndSaveState(genesisBlock as deneb.ExecutionPayloadHeader) diff --git a/research/block_sim.nim b/research/block_sim.nim index 6662a98494..af9ae5b7e6 100644 --- a/research/block_sim.nim +++ b/research/block_sim.nim @@ -144,6 +144,50 @@ proc makeSimulationBlock( ok(blck) +proc makeSimulationBlock( + cfg: RuntimeConfig, + state: var fulu.HashedBeaconState, + proposer_index: ValidatorIndex, + randao_reveal: ValidatorSig, + eth1_data: Eth1Data, + graffiti: GraffitiBytes, + attestations: seq[electra.Attestation], + deposits: seq[Deposit], + exits: BeaconBlockValidatorChanges, + sync_aggregate: SyncAggregate, + execution_payload: fulu.ExecutionPayloadForSigning, + bls_to_execution_changes: SignedBLSToExecutionChangeList, + rollback: RollbackHashedProc[fulu.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[fulu.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, + default(ExecutionRequests)) + + 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, @@ -369,6 +413,8 @@ cli do(slots = SLOTS_PER_EPOCH * 7, addr state.denebData elif T is electra.SignedBeaconBlock: addr state.electraData + elif T is fulu.SignedBeaconBlock: + addr state.fuluData else: static: doAssert false message = makeSimulationBlock( @@ -383,6 +429,8 @@ cli do(slots = SLOTS_PER_EPOCH * 7, default(GraffitiBytes), when T is electra.SignedBeaconBlock: attPool.getElectraAttestationsForBlock(state, cache) + elif T is fulu.SignedBeaconBlock: + attPool.getElectraAttestationsForBlock(state, cache) else: attPool.getAttestationsForBlock(state, cache), eth1ProposalData.deposits, @@ -390,6 +438,8 @@ cli do(slots = SLOTS_PER_EPOCH * 7, sync_aggregate, (when T is electra.SignedBeaconBlock: default(electra.ExecutionPayloadForSigning) + elif T is fulu.SignedBeaconBlock: + default(fulu.ExecutionPayloadForSigning) elif T is deneb.SignedBeaconBlock: default(deneb.ExecutionPayloadForSigning) else: @@ -461,6 +511,28 @@ cli do(slots = SLOTS_PER_EPOCH * 7, do: raiseAssert "withUpdatedState failed" + proc proposeFuluBlock(slot: Slot) = + if rand(r, 1.0) > blockRatio: + return + + dag.withUpdatedState(tmpState[], dag.getBlockIdAtSlot(slot).expect("block")) do: + let + newBlock = getNewBlock[fulu.SignedBeaconBlock](updatedState, slot, cache) + added = dag.addHeadBlock(verifier, newBlock) do ( + blckRef: BlockRef, signedBlock: fulu.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 @@ -501,6 +573,7 @@ cli do(slots = SLOTS_PER_EPOCH * 7, if blockRatio > 0.0: withTimer(timers[t]): case dag.cfg.consensusForkAtEpoch(slot.epoch) + of ConsensusFork.Fulu: proposeFuluBlock(slot) of ConsensusFork.Electra: proposeElectraBlock(slot) of ConsensusFork.Deneb: proposeDenebBlock(slot) of ConsensusFork.Phase0 .. ConsensusFork.Capella: diff --git a/research/wss_sim.nim b/research/wss_sim.nim index 8dadd671a7..72fddb6b75 100644 --- a/research/wss_sim.nim +++ b/research/wss_sim.nim @@ -285,7 +285,7 @@ cli do(validatorsDir: string, secretsDir: string, randao_reveal, forkyState.data.eth1_data, graffitiValue, - when typeof(payload).kind == ConsensusFork.Electra: + when typeof(payload).kind >= ConsensusFork.Electra: default(seq[electra.Attestation]) else: blockAggregates, diff --git a/tests/consensus_spec/eip7594/test_fixture_ssz_consensus_objects.nim b/tests/consensus_spec/eip7594/test_fixture_ssz_consensus_objects.nim index 3451ee5efa..03fae0ca2d 100644 --- a/tests/consensus_spec/eip7594/test_fixture_ssz_consensus_objects.nim +++ b/tests/consensus_spec/eip7594/test_fixture_ssz_consensus_objects.nim @@ -19,7 +19,7 @@ import ../../../beacon_chain/spec/datatypes/[ altair, deneb, - eip7594], + fulu], # Status libraries snappy, # Test utilities diff --git a/tests/consensus_spec/fixtures_utils.nim b/tests/consensus_spec/fixtures_utils.nim index 98ee032032..fbd999568e 100644 --- a/tests/consensus_spec/fixtures_utils.nim +++ b/tests/consensus_spec/fixtures_utils.nim @@ -47,6 +47,13 @@ func readValue*(r: var JsonReader, a: var seq[byte]) = func genesisTestRuntimeConfig*(consensusFork: ConsensusFork): RuntimeConfig = var res = defaultRuntimeConfig case consensusFork + of ConsensusFork.Fulu: + res.FULU_FORK_EPOCH = GENESIS_EPOCH + 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.Electra: res.ELECTRA_FORK_EPOCH = GENESIS_EPOCH res.DENEB_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 650028fdec..c1b5b7d2a3 100644 --- a/tests/consensus_spec/test_fixture_sanity_blocks.nim +++ b/tests/consensus_spec/test_fixture_sanity_blocks.nim @@ -99,5 +99,5 @@ template runForkBlockTests(consensusFork: static ConsensusFork) = "EF - " & forkHumanName & " - Random", RandomDir, suiteName, path) -withAll(ConsensusFork): +withAllButFulu(ConsensusFork): runForkBlockTests(consensusFork) \ No newline at end of file diff --git a/tests/test_beacon_chain_db.nim b/tests/test_beacon_chain_db.nim index 79bc8c91b1..772e05e093 100644 --- a/tests/test_beacon_chain_db.nim +++ b/tests/test_beacon_chain_db.nim @@ -75,6 +75,13 @@ proc getElectraStateRef(db: BeaconChainDB, root: Eth2Digest): if db.getState(root, res[], noRollback): return res +proc getFuluStateRef(db: BeaconChainDB, root: Eth2Digest): + fulu.NilableBeaconStateRef = + # load beaconstate the way the block pool does it - into an existence instance + let res = (fulu.BeaconStateRef)() + if db.getState(root, res[], noRollback): + return res + func withDigest(blck: phase0.TrustedBeaconBlock): phase0.TrustedSignedBeaconBlock = phase0.TrustedSignedBeaconBlock( @@ -117,6 +124,13 @@ func withDigest(blck: electra.TrustedBeaconBlock): root: hash_tree_root(blck) ) +func withDigest(blck: fulu.TrustedBeaconBlock): + fulu.TrustedSignedBeaconBlock = + fulu.TrustedSignedBeaconBlock( + message: blck, + root: hash_tree_root(blck) + ) + proc getTestStates(consensusFork: ConsensusFork): auto = let db = makeTestDB(SLOTS_PER_EPOCH) @@ -138,12 +152,15 @@ let testStatesCapella = getTestStates(ConsensusFork.Capella) testStatesDeneb = getTestStates(ConsensusFork.Deneb) testStatesElectra = getTestStates(ConsensusFork.Electra) + testStatesFulu = getTestStates(ConsensusFork.Fulu) + doAssert len(testStatesPhase0) > 8 doAssert len(testStatesAltair) > 8 doAssert len(testStatesBellatrix) > 8 doAssert len(testStatesCapella) > 8 doAssert len(testStatesDeneb) > 8 doAssert len(testStatesElectra) > 8 +doAssert len(testStatesFulu) > 8 suite "Beacon chain DB" & preset(): test "empty database" & preset(): @@ -187,6 +204,7 @@ suite "Beacon chain DB" & preset(): not db.containsBlock(root, capella.TrustedSignedBeaconBlock) not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) db.getBlock(root, phase0.TrustedSignedBeaconBlock).isErr() not db.getBlockSSZ(root, tmp, phase0.TrustedSignedBeaconBlock) not db.getBlockSZ(root, tmp2, phase0.TrustedSignedBeaconBlock) @@ -220,6 +238,7 @@ suite "Beacon chain DB" & preset(): not db.containsBlock(root, capella.TrustedSignedBeaconBlock) not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) db.getBlock(root, altair.TrustedSignedBeaconBlock).get() == signedBlock db.getBlockSSZ(root, tmp, altair.TrustedSignedBeaconBlock) db.getBlockSZ(root, tmp2, altair.TrustedSignedBeaconBlock) @@ -236,6 +255,7 @@ suite "Beacon chain DB" & preset(): not db.containsBlock(root, capella.TrustedSignedBeaconBlock) not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) db.getBlock(root, altair.TrustedSignedBeaconBlock).isErr() not db.getBlockSSZ(root, tmp, altair.TrustedSignedBeaconBlock) not db.getBlockSZ(root, tmp2, altair.TrustedSignedBeaconBlock) @@ -269,6 +289,7 @@ suite "Beacon chain DB" & preset(): not db.containsBlock(root, capella.TrustedSignedBeaconBlock) not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) db.getBlock(root, bellatrix.TrustedSignedBeaconBlock).get() == signedBlock db.getBlockSSZ(root, tmp, bellatrix.TrustedSignedBeaconBlock) db.getBlockSZ(root, tmp2, bellatrix.TrustedSignedBeaconBlock) @@ -285,6 +306,7 @@ suite "Beacon chain DB" & preset(): not db.containsBlock(root, capella.TrustedSignedBeaconBlock) not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) db.getBlock(root, bellatrix.TrustedSignedBeaconBlock).isErr() not db.getBlockSSZ(root, tmp, bellatrix.TrustedSignedBeaconBlock) not db.getBlockSZ(root, tmp2, bellatrix.TrustedSignedBeaconBlock) @@ -317,6 +339,7 @@ suite "Beacon chain DB" & preset(): not db.containsBlock(root, bellatrix.TrustedSignedBeaconBlock) not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) db.containsBlock(root, capella.TrustedSignedBeaconBlock) db.getBlock(root, capella.TrustedSignedBeaconBlock).get() == signedBlock db.getBlockSSZ(root, tmp, capella.TrustedSignedBeaconBlock) @@ -334,6 +357,7 @@ suite "Beacon chain DB" & preset(): not db.containsBlock(root, capella.TrustedSignedBeaconBlock) not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) db.getBlock(root, capella.TrustedSignedBeaconBlock).isErr() not db.getBlockSSZ(root, tmp, capella.TrustedSignedBeaconBlock) not db.getBlockSZ(root, tmp2, capella.TrustedSignedBeaconBlock) @@ -367,6 +391,7 @@ suite "Beacon chain DB" & preset(): not db.containsBlock(root, capella.TrustedSignedBeaconBlock) db.containsBlock(root, deneb.TrustedSignedBeaconBlock) not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) db.getBlock(root, deneb.TrustedSignedBeaconBlock).get() == signedBlock db.getBlockSSZ(root, tmp, deneb.TrustedSignedBeaconBlock) db.getBlockSZ(root, tmp2, deneb.TrustedSignedBeaconBlock) @@ -447,6 +472,57 @@ suite "Beacon chain DB" & preset(): db.close() + test "sanity check Fulu blocks" & preset(): + let db = BeaconChainDB.new("", inMemory = true) + + let + signedBlock = withDigest((fulu.TrustedBeaconBlock)()) + root = hash_tree_root(signedBlock.message) + + db.putBlock(signedBlock) + + var tmp, tmp2: seq[byte] + check: + db.containsBlock(root) + not db.containsBlock(root, phase0.TrustedSignedBeaconBlock) + not db.containsBlock(root, altair.TrustedSignedBeaconBlock) + not db.containsBlock(root, bellatrix.TrustedSignedBeaconBlock) + not db.containsBlock(root, capella.TrustedSignedBeaconBlock) + not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) + not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + db.containsBlock(root, fulu.TrustedSignedBeaconBlock) + db.getBlock(root, fulu.TrustedSignedBeaconBlock).get() == signedBlock + db.getBlockSSZ(root, tmp, fulu.TrustedSignedBeaconBlock) + db.getBlockSZ(root, tmp2, fulu.TrustedSignedBeaconBlock) + tmp == SSZ.encode(signedBlock) + tmp2 == encodeFramed(tmp) + uncompressedLenFramed(tmp2).isSome + + check: + db.delBlock(ConsensusFork.Fulu, root) + not db.containsBlock(root) + not db.containsBlock(root, phase0.TrustedSignedBeaconBlock) + not db.containsBlock(root, altair.TrustedSignedBeaconBlock) + not db.containsBlock(root, bellatrix.TrustedSignedBeaconBlock) + not db.containsBlock(root, capella.TrustedSignedBeaconBlock) + not db.containsBlock(root, deneb.TrustedSignedBeaconBlock) + not db.containsBlock(root, electra.TrustedSignedBeaconBlock) + not db.containsBlock(root, fulu.TrustedSignedBeaconBlock) + db.getBlock(root, fulu.TrustedSignedBeaconBlock).isErr() + not db.getBlockSSZ(root, tmp, fulu.TrustedSignedBeaconBlock) + not db.getBlockSZ(root, tmp2, fulu.TrustedSignedBeaconBlock) + + db.putStateRoot(root, signedBlock.message.slot, root) + var root2 = root + root2.data[0] = root.data[0] + 1 + db.putStateRoot(root, signedBlock.message.slot + 1, root2) + + check: + db.getStateRoot(root, signedBlock.message.slot).get() == root + db.getStateRoot(root, signedBlock.message.slot + 1).get() == root2 + + db.close() + test "sanity check phase 0 states" & preset(): let db = makeTestDB(SLOTS_PER_EPOCH) @@ -555,6 +631,24 @@ suite "Beacon chain DB" & preset(): db.close() + test "sanity check Fulu states" & preset(): + let db = makeTestDB(SLOTS_PER_EPOCH) + + for state in testStatesFulu: + let root = state[].fuluData.root + db.putState(root, state[].fuluData.data) + + check: + db.containsState(root) + hash_tree_root(db.getFuluStateRef(root)[]) == root + + db.delState(ConsensusFork.Fulu, root) + check: + not db.containsState(root) + db.getFuluStateRef(root).isNil + + db.close() + test "sanity check phase 0 states, reusing buffers" & preset(): let db = makeTestDB(SLOTS_PER_EPOCH) let stateBuffer = (phase0.BeaconStateRef)() @@ -675,6 +769,26 @@ suite "Beacon chain DB" & preset(): db.close() + test "sanity check Fulu states, reusing buffers" & preset(): + let db = makeTestDB(SLOTS_PER_EPOCH) + let stateBuffer = (fulu.BeaconStateRef)() + + for state in testStatesFulu: + let root = state[].fuluData.root + db.putState(root, state[].fuluData.data) + + check: + db.getState(root, stateBuffer[], noRollback) + db.containsState(root) + hash_tree_root(stateBuffer[]) == root + + db.delState(ConsensusFork.Fulu, root) + check: + not db.containsState(root) + not db.getState(root, stateBuffer[], noRollback) + + db.close() + test "sanity check phase 0 getState rollback" & preset(): var db = makeTestDB(SLOTS_PER_EPOCH) @@ -828,6 +942,32 @@ suite "Beacon chain DB" & preset(): state[].kind == ConsensusFork.Phase0 state[].phase0Data.data.slot != 10.Slot + test "sanity check Fulu and cross-fork getState rollback" & preset(): + var + db = makeTestDB(SLOTS_PER_EPOCH) + validatorMonitor = newClone(ValidatorMonitor.init()) + dag = init(ChainDAGRef, defaultRuntimeConfig, db, validatorMonitor, {}) + state = (ref ForkedHashedBeaconState)( + kind: ConsensusFork.Fulu, + fuluData: fulu.HashedBeaconState(data: fulu.BeaconState( + slot: 10.Slot))) + root = Eth2Digest() + + db.putCorruptState(ConsensusFork.Fulu, root) + + let restoreAddr = addr dag.headState + + func restore() = + assign(state[], restoreAddr[]) + + check: + state[].fuluData.data.slot == 10.Slot + not db.getState(root, state[].fuluData.data, restore) + + # assign() has switched the case object fork + state[].kind == ConsensusFork.Phase0 + state[].phase0Data.data.slot != 10.Slot + test "find ancestors" & preset(): var db = BeaconChainDB.new("", inMemory = true) diff --git a/tests/test_eip7594_helpers.nim b/tests/test_eip7594_helpers.nim index 6fa26d16ce..e535e663eb 100644 --- a/tests/test_eip7594_helpers.nim +++ b/tests/test_eip7594_helpers.nim @@ -15,7 +15,7 @@ import kzg4844/[kzg_abi, kzg], ./consensus_spec/[os_ops, fixtures_utils], ../beacon_chain/spec/[helpers, eip7594_helpers], - ../beacon_chain/spec/datatypes/[eip7594, deneb] + ../beacon_chain/spec/datatypes/[fulu, deneb] from std/strutils import rsplit diff --git a/tests/test_light_client.nim b/tests/test_light_client.nim index 7f3090d799..7ad26c1340 100644 --- a/tests/test_light_client.nim +++ b/tests/test_light_client.nim @@ -25,13 +25,14 @@ suite "Light client" & preset(): headPeriod = 4.SyncCommitteePeriod let cfg = block: # Fork schedule so that each `LightClientDataFork` is covered - static: doAssert ConsensusFork.high == ConsensusFork.Electra + static: doAssert ConsensusFork.high == ConsensusFork.Fulu 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 = (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * 3).Epoch + res.FULU_FORK_EPOCH = (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * 4).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 213ffaf112..5f9c060346 100644 --- a/tests/test_light_client_processor.nim +++ b/tests/test_light_client_processor.nim @@ -28,13 +28,14 @@ suite "Light client processor" & preset(): highPeriod = 6.SyncCommitteePeriod let cfg = block: # Fork schedule so that each `LightClientDataFork` is covered - static: doAssert ConsensusFork.high == ConsensusFork.Electra + static: doAssert ConsensusFork.high == ConsensusFork.Fulu 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 = (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * 3).Epoch + res.FULU_FORK_EPOCH = (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * 4).Epoch res const numValidators = SLOTS_PER_EPOCH diff --git a/tests/test_remote_keystore.nim b/tests/test_remote_keystore.nim index dec0cacd84..a635772fdd 100644 --- a/tests/test_remote_keystore.nim +++ b/tests/test_remote_keystore.nim @@ -138,6 +138,7 @@ suite "Remove keystore testing suite": check keystore.provenBlockProperties[0].capellaIndex == some GeneralizedIndex(401) check keystore.provenBlockProperties[0].denebIndex == some GeneralizedIndex(801) check keystore.provenBlockProperties[0].electraIndex == some GeneralizedIndex(801) + check keystore.provenBlockProperties[0].fuluIndex == some GeneralizedIndex(801) test "Verifying Signer / Many remotes": for version in [3]: @@ -186,4 +187,5 @@ suite "Remove keystore testing suite": check keystore.provenBlockProperties.len == 1 check keystore.provenBlockProperties[0].capellaIndex == some GeneralizedIndex(401) check keystore.provenBlockProperties[0].denebIndex == some GeneralizedIndex(801) - check keystore.provenBlockProperties[0].electraIndex == some GeneralizedIndex(801) \ No newline at end of file + check keystore.provenBlockProperties[0].electraIndex == some GeneralizedIndex(801) + check keystore.provenBlockProperties[0].fuluIndex == some GeneralizedIndex(801) \ No newline at end of file diff --git a/tests/test_signing_node.nim b/tests/test_signing_node.nim index 8b0541ee2c..e99d4ee8db 100644 --- a/tests/test_signing_node.nim +++ b/tests/test_signing_node.nim @@ -105,6 +105,9 @@ proc getBlock( of ConsensusFork.Electra: debugComment "electra test signing node getblock" raiseAssert "electra unsupported" + of ConsensusFork.Fulu: + debugFuluComment "electra test signing node getblock" + raiseAssert "fulu unsupported" except ValueError: # https://github.com/nim-lang/Nim/pull/23356 raiseAssert "Arguments match the format string" @@ -128,6 +131,10 @@ func init(t: typedesc[Web3SignerForkedBeaconBlock], Web3SignerForkedBeaconBlock( kind: ConsensusFork.Electra, data: forked.electraData.toBeaconBlockHeader) + of ConsensusFork.Fulu: + Web3SignerForkedBeaconBlock( + kind: ConsensusFork.Fulu, + data: forked.fuluData.toBeaconBlockHeader) proc createKeystore(dataDir, pubkey, store, password: string): Result[void, string] = @@ -260,6 +267,7 @@ func getRemoteKeystoreData(data: string, basePort: int, provenBlockProperties: @[ ProvenProperty( path: ".execution_payload.fee_recipient", + fuluIndex: some GeneralizedIndex(801), electraIndex: some GeneralizedIndex(801), denebIndex: some GeneralizedIndex(801), capellaIndex: some GeneralizedIndex(401) diff --git a/tests/test_toblindedblock.nim b/tests/test_toblindedblock.nim index 934e6db313..8e6b9e9156 100644 --- a/tests/test_toblindedblock.nim +++ b/tests/test_toblindedblock.nim @@ -12,7 +12,8 @@ import # Beacon chain internals ../beacon_chain/spec/helpers, ../beacon_chain/spec/datatypes/[bellatrix, capella], - ../beacon_chain/spec/mev/[bellatrix_mev, capella_mev, deneb_mev, electra_mev], + ../beacon_chain/spec/mev/[bellatrix_mev, capella_mev, deneb_mev, electra_mev, + fulu_mev], # Test utilities unittest2 @@ -130,6 +131,17 @@ template electra_steps() = default(ConsolidationRequest)) do_check +template fulu_steps() = + check: b.message.body.execution_requests.deposits.add( + default(DepositRequest)) + do_check + check: b.message.body.execution_requests.withdrawals.add( + default(WithdrawalRequest)) + do_check + check: b.message.body.execution_requests.consolidations.add( + default(ConsolidationRequest)) + do_check + suite "Blinded block conversions": withAll(ConsensusFork): when consensusFork >= ConsensusFork.Bellatrix: @@ -143,4 +155,4 @@ suite "Blinded block conversions": deneb_steps when consensusFork >= ConsensusFork.Electra: electra_steps - static: doAssert high(ConsensusFork) == ConsensusFork.Electra + static: doAssert high(ConsensusFork) == ConsensusFork.Fulu diff --git a/tests/test_validator_change_pool.nim b/tests/test_validator_change_pool.nim index 9beb636583..51c273d305 100644 --- a/tests/test_validator_change_pool.nim +++ b/tests/test_validator_change_pool.nim @@ -79,6 +79,7 @@ suite "Validator change pool testing suite": tmp.CAPELLA_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD) + 2 tmp.DENEB_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD) + 3 tmp.ELECTRA_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD) + 4 + tmp.FULU_FORK_EPOCH = Epoch(tmp.SHARD_COMMITTEE_PERIOD) + 5 tmp validatorMonitor = newClone(ValidatorMonitor.init()) diff --git a/tests/testblockutil.nim b/tests/testblockutil.nim index c541a3396c..21108bf8fe 100644 --- a/tests/testblockutil.nim +++ b/tests/testblockutil.nim @@ -219,6 +219,8 @@ proc addTestBlock*( graffiti, when consensusFork == ConsensusFork.Electra: electraAttestations + elif consensusFork == ConsensusFork.Fulu: + electraAttestations else: attestations, deposits, diff --git a/tests/testdbutil.nim b/tests/testdbutil.nim index 48df0f2226..7a7c39cf97 100644 --- a/tests/testdbutil.nim +++ b/tests/testdbutil.nim @@ -31,6 +31,11 @@ proc makeTestDB*( cfg.CAPELLA_FORK_EPOCH = 90000.Epoch if cfg.DENEB_FORK_EPOCH == FAR_FUTURE_EPOCH: cfg.DENEB_FORK_EPOCH = 100000.Epoch + if cfg.ELECTRA_FORK_EPOCH == FAR_FUTURE_EPOCH: + cfg.ELECTRA_FORK_EPOCH = 110000.Epoch + if cfg.FULU_FORK_EPOCH == FAR_FUTURE_EPOCH: + cfg.FULU_FORK_EPOCH = 120000.Epoch + var genState = (ref ForkedHashedBeaconState)( kind: ConsensusFork.Phase0, diff --git a/tests/teststateutil.nim b/tests/teststateutil.nim index 520da1a000..6e93147fe3 100644 --- a/tests/teststateutil.nim +++ b/tests/teststateutil.nim @@ -79,7 +79,7 @@ proc getTestStates*( info = ForkedEpochInfo() cfg = defaultRuntimeConfig - static: doAssert high(ConsensusFork) == ConsensusFork.Electra + static: doAssert high(ConsensusFork) == ConsensusFork.Fulu if consensusFork >= ConsensusFork.Altair: cfg.ALTAIR_FORK_EPOCH = 1.Epoch if consensusFork >= ConsensusFork.Bellatrix: @@ -90,6 +90,8 @@ proc getTestStates*( cfg.DENEB_FORK_EPOCH = 4.Epoch if consensusFork >= ConsensusFork.Electra: cfg.ELECTRA_FORK_EPOCH = 5.Epoch + if consensusFork >= ConsensusFork.Fulu: + cfg.FULU_FORK_EPOCH = 6.Epoch for i, epoch in stateEpochs: let slot = epoch.Epoch.start_slot @@ -109,7 +111,8 @@ from std/sequtils import allIt from ".."/beacon_chain/spec/beaconstate import get_expected_withdrawals proc checkPerValidatorBalanceCalc*( - state: deneb.BeaconState | electra.BeaconState): bool = + state: deneb.BeaconState | electra.BeaconState | + fulu.BeaconState): bool = var info: altair.EpochInfo cache: StateCache