diff --git a/Cargo.lock b/Cargo.lock index 753763fe96..90196ea5b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7653,6 +7653,7 @@ dependencies = [ "safe_arith", "serde", "slog", + "ssz_types", "strum", "tempfile", "tree_hash", diff --git a/beacon_node/beacon_chain/src/attestation_verification.rs b/beacon_node/beacon_chain/src/attestation_verification.rs index 32c33f3e61..06fba937d8 100644 --- a/beacon_node/beacon_chain/src/attestation_verification.rs +++ b/beacon_node/beacon_chain/src/attestation_verification.rs @@ -61,10 +61,9 @@ use std::borrow::Cow; use strum::AsRefStr; use tree_hash::TreeHash; use types::{ - Attestation, AttestationRef, BeaconCommittee, - BeaconStateError::{self, NoCommitteeFound}, - ChainSpec, CommitteeIndex, Epoch, EthSpec, ForkName, Hash256, IndexedAttestation, - SelectionProof, SignedAggregateAndProof, Slot, SubnetId, + Attestation, AttestationRef, BeaconCommittee, BeaconStateError::NoCommitteeFound, ChainSpec, + CommitteeIndex, Epoch, EthSpec, Hash256, IndexedAttestation, SelectionProof, + SignedAggregateAndProof, Slot, SubnetId, }; pub use batch::{batch_verify_aggregated_attestations, batch_verify_unaggregated_attestations}; @@ -266,30 +265,9 @@ pub enum Error { BeaconChainError(BeaconChainError), } -// TODO(electra) the error conversion changes here are to get a test case to pass -// this could easily be cleaned up impl From for Error { fn from(e: BeaconChainError) -> Self { - match &e { - BeaconChainError::BeaconStateError(beacon_state_error) => { - if let BeaconStateError::AggregatorNotInCommittee { aggregator_index } = - beacon_state_error - { - Self::AggregatorNotInCommittee { - aggregator_index: *aggregator_index, - } - } else if let BeaconStateError::InvalidSelectionProof { aggregator_index } = - beacon_state_error - { - Self::InvalidSelectionProof { - aggregator_index: *aggregator_index, - } - } else { - Error::BeaconChainError(e) - } - } - _ => Error::BeaconChainError(e), - } + Self::BeaconChainError(e) } } @@ -1169,7 +1147,7 @@ pub fn verify_propagation_slot_range( let current_fork = spec.fork_name_at_slot::(slot_clock.now().ok_or(BeaconChainError::UnableToReadSlot)?); - let earliest_permissible_slot = if current_fork < ForkName::Deneb { + let earliest_permissible_slot = if !current_fork.deneb_enabled() { one_epoch_prior // EIP-7045 } else { @@ -1414,11 +1392,11 @@ pub fn obtain_indexed_attestation_and_committees_per_slot( /// Runs the `map_fn` with the committee and committee count per slot for the given `attestation`. /// /// This function exists in this odd "map" pattern because efficiently obtaining the committees for -/// an attestations slot can be complex. It might involve reading straight from the +/// an attestation's slot can be complex. It might involve reading straight from the /// `beacon_chain.shuffling_cache` or it might involve reading it from a state from the DB. Due to /// the complexities of `RwLock`s on the shuffling cache, a simple `Cow` isn't suitable here. /// -/// If the committees for an `attestation`'s slot isn't found in the `shuffling_cache`, we will read a state +/// If the committees for an `attestation`'s slot aren't found in the `shuffling_cache`, we will read a state /// from disk and then update the `shuffling_cache`. /// /// Committees are sorted by ascending index order 0..committees_per_slot diff --git a/beacon_node/beacon_chain/tests/block_verification.rs b/beacon_node/beacon_chain/tests/block_verification.rs index 4c7a499697..d9c9a3b6a7 100644 --- a/beacon_node/beacon_chain/tests/block_verification.rs +++ b/beacon_node/beacon_chain/tests/block_verification.rs @@ -2,7 +2,9 @@ use beacon_chain::block_verification_types::{AsBlock, ExecutedBlock, RpcBlock}; use beacon_chain::{ - test_utils::{AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType}, + test_utils::{ + test_spec, AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType, + }, AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes, ExecutionPendingBlock, }; use beacon_chain::{ @@ -1210,8 +1212,14 @@ async fn block_gossip_verification() { #[tokio::test] async fn verify_block_for_gossip_slashing_detection() { let slasher_dir = tempdir().unwrap(); + let spec = Arc::new(test_spec::()); let slasher = Arc::new( - Slasher::open(SlasherConfig::new(slasher_dir.path().into()), test_logger()).unwrap(), + Slasher::open( + SlasherConfig::new(slasher_dir.path().into()), + spec, + test_logger(), + ) + .unwrap(), ); let inner_slasher = slasher.clone(); diff --git a/beacon_node/operation_pool/src/attestation.rs b/beacon_node/operation_pool/src/attestation.rs index c6ed6eb7f6..97d0583e34 100644 --- a/beacon_node/operation_pool/src/attestation.rs +++ b/beacon_node/operation_pool/src/attestation.rs @@ -184,8 +184,8 @@ pub fn earliest_attestation_validators( // Bitfield of validators whose attestations are new/fresh. let mut new_validators = match attestation.indexed { CompactIndexedAttestation::Base(indexed_att) => indexed_att.aggregation_bits.clone(), - // TODO(electra) per the comments above, this code path is obsolete post altair fork, so maybe we should just return an empty bitlist here? - CompactIndexedAttestation::Electra(_) => todo!(), + // This code path is obsolete post altair fork, so we just return an empty bitlist here. + CompactIndexedAttestation::Electra(_) => return BitList::with_capacity(0).unwrap(), }; let state_attestations = if attestation.checkpoint.target_epoch == state.current_epoch() { diff --git a/beacon_node/operation_pool/src/attestation_storage.rs b/beacon_node/operation_pool/src/attestation_storage.rs index ad4ffe6d3c..4de9d351f3 100644 --- a/beacon_node/operation_pool/src/attestation_storage.rs +++ b/beacon_node/operation_pool/src/attestation_storage.rs @@ -165,22 +165,22 @@ impl CompactIndexedAttestation { CompactIndexedAttestation::Electra(this), CompactIndexedAttestation::Electra(other), ) => this.should_aggregate(other), - // TODO(electra) is a mix of electra and base compact indexed attestations an edge case we need to deal with? _ => false, } } - pub fn aggregate(&mut self, other: &Self) -> Option<()> { + /// Returns `true` if aggregated, otherwise `false`. + pub fn aggregate(&mut self, other: &Self) -> bool { match (self, other) { (CompactIndexedAttestation::Base(this), CompactIndexedAttestation::Base(other)) => { - this.aggregate(other) + this.aggregate(other); + true } ( CompactIndexedAttestation::Electra(this), CompactIndexedAttestation::Electra(other), ) => this.aggregate_same_committee(other), - // TODO(electra) is a mix of electra and base compact indexed attestations an edge case we need to deal with? - _ => None, + _ => false, } } } @@ -192,7 +192,7 @@ impl CompactIndexedAttestationBase { .is_zero() } - pub fn aggregate(&mut self, other: &Self) -> Option<()> { + pub fn aggregate(&mut self, other: &Self) { self.attesting_indices = self .attesting_indices .drain(..) @@ -201,8 +201,6 @@ impl CompactIndexedAttestationBase { .collect(); self.aggregation_bits = self.aggregation_bits.union(&other.aggregation_bits); self.signature.add_assign_aggregate(&other.signature); - - Some(()) } } @@ -216,9 +214,10 @@ impl CompactIndexedAttestationElectra { .is_zero() } - pub fn aggregate_same_committee(&mut self, other: &Self) -> Option<()> { + /// Returns `true` if aggregated, otherwise `false`. + pub fn aggregate_same_committee(&mut self, other: &Self) -> bool { if self.committee_bits != other.committee_bits { - return None; + return false; } self.aggregation_bits = self.aggregation_bits.union(&other.aggregation_bits); self.attesting_indices = self @@ -228,7 +227,7 @@ impl CompactIndexedAttestationElectra { .dedup() .collect(); self.signature.add_assign_aggregate(&other.signature); - Some(()) + true } pub fn aggregate_with_disjoint_committees(&mut self, other: &Self) -> Option<()> { @@ -318,8 +317,7 @@ impl AttestationMap { for existing_attestation in attestations.iter_mut() { if existing_attestation.should_aggregate(&indexed) { - existing_attestation.aggregate(&indexed); - aggregated = true; + aggregated = existing_attestation.aggregate(&indexed); } else if *existing_attestation == indexed { aggregated = true; } diff --git a/beacon_node/operation_pool/src/lib.rs b/beacon_node/operation_pool/src/lib.rs index 49fbed5686..a1c9ada03a 100644 --- a/beacon_node/operation_pool/src/lib.rs +++ b/beacon_node/operation_pool/src/lib.rs @@ -39,7 +39,7 @@ use std::ptr; use types::{ sync_aggregate::Error as SyncAggregateError, typenum::Unsigned, AbstractExecPayload, Attestation, AttestationData, AttesterSlashing, BeaconState, BeaconStateError, ChainSpec, - Epoch, EthSpec, ForkName, ProposerSlashing, SignedBeaconBlock, SignedBlsToExecutionChange, + Epoch, EthSpec, ProposerSlashing, SignedBeaconBlock, SignedBlsToExecutionChange, SignedVoluntaryExit, Slot, SyncAggregate, SyncCommitteeContribution, Validator, }; @@ -316,10 +316,10 @@ impl OperationPool { ) .inspect(|_| num_curr_valid += 1); - let curr_epoch_limit = if fork_name < ForkName::Electra { - E::MaxAttestations::to_usize() - } else { + let curr_epoch_limit = if fork_name.electra_enabled() { E::MaxAttestationsElectra::to_usize() + } else { + E::MaxAttestations::to_usize() }; let prev_epoch_limit = if let BeaconState::Base(base_state) = state { std::cmp::min( diff --git a/beacon_node/src/lib.rs b/beacon_node/src/lib.rs index ab400d2e73..385da5b4fe 100644 --- a/beacon_node/src/lib.rs +++ b/beacon_node/src/lib.rs @@ -80,7 +80,7 @@ impl ProductionBeaconNode { let builder = ClientBuilder::new(context.eth_spec_instance.clone()) .runtime_context(context) - .chain_spec(spec) + .chain_spec(spec.clone()) .beacon_processor(client_config.beacon_processor.clone()) .http_api_config(client_config.http_api.clone()) .disk_store( @@ -113,8 +113,12 @@ impl ProductionBeaconNode { _ => {} } let slasher = Arc::new( - Slasher::open(slasher_config, log.new(slog::o!("service" => "slasher"))) - .map_err(|e| format!("Slasher open error: {:?}", e))?, + Slasher::open( + slasher_config, + Arc::new(spec), + log.new(slog::o!("service" => "slasher")), + ) + .map_err(|e| format!("Slasher open error: {:?}", e))?, ); builder.slasher(slasher) } else { diff --git a/book/src/checkpoint-sync.md b/book/src/checkpoint-sync.md index 63d96874c3..2bf028acfe 100644 --- a/book/src/checkpoint-sync.md +++ b/book/src/checkpoint-sync.md @@ -146,8 +146,19 @@ For more information on historic state storage see the To manually specify a checkpoint use the following two flags: -* `--checkpoint-state`: accepts an SSZ-encoded `BeaconState` blob -* `--checkpoint-block`: accepts an SSZ-encoded `SignedBeaconBlock` blob +* `--checkpoint-state`: accepts an SSZ-encoded `BeaconState` file +* `--checkpoint-block`: accepts an SSZ-encoded `SignedBeaconBlock` file +* `--checkpoint-blobs`: accepts an SSZ-encoded `Blobs` file + +The command is as following: + +```bash +curl -H "Accept: application/octet-stream" "http://localhost:5052/eth/v2/debug/beacon/states/$SLOT" > state.ssz +curl -H "Accept: application/octet-stream" "http://localhost:5052/eth/v2/beacon/blocks/$SLOT" > block.ssz +curl -H "Accept: application/octet-stream" "http://localhost:5052/eth/v1/beacon/blob_sidecars/$SLOT" > blobs.ssz +``` + +where `$SLOT` is the slot number. It can be specified as `head` or `finalized` as well. _Both_ the state and block must be provided and the state **must** match the block. The state may be from the same slot as the block (unadvanced), or advanced to an epoch boundary, diff --git a/book/src/database-migrations.md b/book/src/database-migrations.md index a81acd7794..611c61cb9c 100644 --- a/book/src/database-migrations.md +++ b/book/src/database-migrations.md @@ -16,6 +16,7 @@ validator client or the slasher**. | Lighthouse version | Release date | Schema version | Downgrade available? | |--------------------|--------------|----------------|----------------------| +| v5.2.0 | Jun 2024 | v19 | yes before Deneb | | v5.1.0 | Mar 2024 | v19 | yes before Deneb | | v5.0.0 | Feb 2024 | v19 | yes before Deneb | | v4.6.0 | Dec 2023 | v19 | yes before Deneb | diff --git a/book/src/faq.md b/book/src/faq.md index c7fdb6b32f..2de7841343 100644 --- a/book/src/faq.md +++ b/book/src/faq.md @@ -15,6 +15,7 @@ - [My beacon node logs `WARN Error signalling fork choice waiter`, what should I do?](#bn-fork-choice) - [My beacon node logs `ERRO Aggregate attestation queue full`, what should I do?](#bn-queue-full) - [My beacon node logs `WARN Failed to finalize deposit cache`, what should I do?](#bn-deposit-cache) +- [My beacon node logs `WARN Could not verify blob sidecar for gossip`, what does it mean?](#bn-blob) ## [Validator](#validator-1) @@ -214,6 +215,16 @@ This suggests that the computer resources are being overwhelmed. It could be due This is a known [bug](https://github.com/sigp/lighthouse/issues/3707) that will fix by itself. +### My beacon node logs `WARN Could not verify blob sidecar for gossip`, what does it mean? + +An example of the full log is shown below: + +```text +Jun 07 23:05:12.170 WARN Could not verify blob sidecar for gossip. Ignoring the blob sidecar, commitment: 0xaa97…6f54, index: 1, root: 0x93b8…c47c, slot: 9248017, error: PastFinalizedSlot { blob_slot: Slot(9248017), finalized_slot: Slot(9248032) }, module: network::network_beacon_processor::gossip_methods:720 +``` + +The `PastFinalizedSlot` indicates that the time at which the node received the blob has past the finalization period. This could be due to a peer sending an earlier blob. The log will be gone when Lighthouse eventually drops the peer. + ## Validator ### Why does it take so long for a validator to be activated? @@ -327,13 +338,24 @@ The first thing is to ensure both consensus and execution clients are synced wit You can see more information on the [Ethstaker KB](https://ethstaker.gitbook.io/ethstaker-knowledge-base/help/missed-attestations). -Another cause for missing attestations is delays during block processing. When this happens, the debug logs will show (debug logs can be found under `$datadir/beacon/logs`): +Another cause for missing attestations is the block arriving late, or there are delays during block processing. + +An example of the log: (debug logs can be found under `$datadir/beacon/logs`): ```text -DEBG Delayed head block set_as_head_delay: Some(93.579425ms), imported_delay: Some(1.460405278s), observed_delay: Some(2.540811921s), block_delay: 4.094796624s, slot: 6837344, proposer_index: 211108, block_root: 0x2c52231c0a5a117401f5231585de8aa5dd963bc7cbc00c544e681342eedd1700, service: beacon +Delayed head block, set_as_head_time_ms: 27, imported_time_ms: 168, attestable_delay_ms: 4209, available_delay_ms: 4186, execution_time_ms: 201, blob_delay_ms: 3815, observed_delay_ms: 3984, total_delay_ms: 4381, slot: 1886014, proposer_index: 733, block_root: 0xa7390baac88d50f1cbb5ad81691915f6402385a12521a670bbbd4cd5f8bf3934, service: beacon, module: beacon_chain::canonical_head:1441 ``` -The fields to look for are `imported_delay > 1s` and `observed_delay < 3s`. The `imported_delay` is how long the node took to process the block. The `imported_delay` of larger than 1 second suggests that there is slowness in processing the block. It could be due to high CPU usage, high I/O disk usage or the clients are doing some background maintenance processes. The `observed_delay` is determined mostly by the proposer and partly by your networking setup (e.g., how long it took for the node to receive the block). The `observed_delay` of less than 3 seconds means that the block is not arriving late from the block proposer. Combining the above, this implies that the validator should have been able to attest to the block, but failed due to slowness in the node processing the block. +The field to look for is `attestable_delay`, which defines the time when a block is ready for the validator to attest. If the `attestable_delay` is greater than 4s which has past the window of attestation, the attestation wil fail. In the above example, the delay is mostly caused by late block observed by the node, as shown in `observed_delay`. The `observed_delay` is determined mostly by the proposer and partly by your networking setup (e.g., how long it took for the node to receive the block). Ideally, `observed_delay` should be less than 3 seconds. In this example, the validator failed to attest the block due to the block arriving late. + +Another example of log: + +``` +DEBG Delayed head block, set_as_head_time_ms: 22, imported_time_ms: 312, attestable_delay_ms: 7052, available_delay_ms: 6874, execution_time_ms: 4694, blob_delay_ms: 2159, observed_delay_ms: 2179, total_delay_ms: 7209, slot: 1885922, proposer_index: 606896, block_root: 0x9966df24d24e722d7133068186f0caa098428696e9f441ac416d0aca70cc0a23, service: beacon, module: beacon_chain::canonical_head:1441 +/159.69.68.247/tcp/9000, service: libp2p, module: lighthouse_network::service:1811 +``` + +In this example, we see that the `execution_time_ms` is 4694ms. The `execution_time_ms` is how long the node took to process the block. The `execution_time_ms` of larger than 1 second suggests that there is slowness in processing the block. If the `execution_time_ms` is high, it could be due to high CPU usage, high I/O disk usage or the clients are doing some background maintenance processes. ### Sometimes I miss the attestation head vote, resulting in penalty. Is this normal? @@ -514,21 +536,23 @@ If you would still like to subscribe to all subnets, you can use the flag `subsc ### How to know how many of my peers are connected via QUIC? -With `--metrics` enabled in the beacon node, you can find the number of peers connected via QUIC using: +With `--metrics` enabled in the beacon node, the [Grafana Network dashboard](https://github.com/sigp/lighthouse-metrics/blob/master/dashboards/Network.json) displays the connected by transport, which will show the number of peers connected via QUIC. + +Alternatively, you can find the number of peers connected via QUIC manually using: ```bash - curl -s "http://localhost:5054/metrics" | grep libp2p_quic_peers + curl -s "http://localhost:5054/metrics" | grep 'transport="quic"' ``` A response example is: ```text -# HELP libp2p_quic_peers Count of libp2p peers currently connected via QUIC -# TYPE libp2p_quic_peers gauge -libp2p_quic_peers 4 +libp2p_peers_multi{direction="inbound",transport="quic"} 27 +libp2p_peers_multi{direction="none",transport="quic"} 0 +libp2p_peers_multi{direction="outbound",transport="quic"} 9 ``` -which shows that there are 4 peers connected via QUIC. +which shows that there are a total of 36 peers connected via QUIC. ## Miscellaneous diff --git a/book/src/slasher.md b/book/src/slasher.md index 5098fe6eda..3310f6c9ef 100644 --- a/book/src/slasher.md +++ b/book/src/slasher.md @@ -114,13 +114,13 @@ changed after initialization. * Flag: `--slasher-max-db-size GIGABYTES` * Argument: maximum size of the database in gigabytes -* Default: 256 GB +* Default: 512 GB Both database backends LMDB and MDBX place a hard limit on the size of the database file. You can use the `--slasher-max-db-size` flag to set this limit. It can be adjusted after initialization if the limit is reached. -By default the limit is set to accommodate the default history length and around 600K validators (with about 30% headroom) but +By default the limit is set to accommodate the default history length and around 1 million validators but you can set it lower if running with a reduced history length. The space required scales approximately linearly in validator count and history length, i.e. if you halve either you can halve the space required. diff --git a/book/src/slashing-protection.md b/book/src/slashing-protection.md index 88e2bb955c..2d580f1c31 100644 --- a/book/src/slashing-protection.md +++ b/book/src/slashing-protection.md @@ -75,7 +75,7 @@ Once you have the slashing protection database from your existing client, you ca using this command: ```bash -lighthouse account validator slashing-protection import +lighthouse account validator slashing-protection import filename.json ``` When importing an interchange file, you still need to import the validator keystores themselves @@ -86,7 +86,7 @@ separately, using the instructions for [import validator keys](./mainnet-validat You can export Lighthouse's database for use with another client with this command: ``` -lighthouse account validator slashing-protection export +lighthouse account validator slashing-protection export filename.json ``` The validator client needs to be stopped in order to export, to guarantee that the data exported is diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index 05621d188b..e7655b453a 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -626,6 +626,7 @@ pub fn process_withdrawals>( // Update pending partial withdrawals [New in Electra:EIP7251] if let Some(partial_withdrawals_count) = partial_withdrawals_count { + // TODO(electra): Use efficient pop_front after milhouse release https://github.com/sigp/milhouse/pull/38 let new_partial_withdrawals = state .pending_partial_withdrawals()? .iter_from(partial_withdrawals_count)? diff --git a/consensus/state_processing/src/verify_operation.rs b/consensus/state_processing/src/verify_operation.rs index f218b806d2..c4b7c6a026 100644 --- a/consensus/state_processing/src/verify_operation.rs +++ b/consensus/state_processing/src/verify_operation.rs @@ -48,7 +48,6 @@ pub trait TransformPersist { pub struct SigVerifiedOp { op: T, verified_against: VerifiedAgainst, - //#[ssz(skip_serializing, skip_deserializing)] _phantom: PhantomData, } diff --git a/consensus/types/src/aggregate_and_proof.rs b/consensus/types/src/aggregate_and_proof.rs index 6ae0df4ad7..223b12e768 100644 --- a/consensus/types/src/aggregate_and_proof.rs +++ b/consensus/types/src/aggregate_and_proof.rs @@ -33,7 +33,8 @@ use tree_hash_derive::TreeHash; derive(Debug, PartialEq, TreeHash, Serialize,), serde(untagged, bound = "E: EthSpec"), tree_hash(enum_behaviour = "transparent") - ) + ), + map_ref_into(AttestationRef) )] #[derive( arbitrary::Arbitrary, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash, @@ -59,19 +60,17 @@ pub struct AggregateAndProof { impl<'a, E: EthSpec> AggregateAndProofRef<'a, E> { /// Returns `true` if `validator_pubkey` signed over `self.aggregate.data.slot`. pub fn aggregate(self) -> AttestationRef<'a, E> { - match self { - AggregateAndProofRef::Base(a) => AttestationRef::Base(&a.aggregate), - AggregateAndProofRef::Electra(a) => AttestationRef::Electra(&a.aggregate), - } + map_aggregate_and_proof_ref_into_attestation_ref!(&'a _, self, |inner, cons| { + cons(&inner.aggregate) + }) } } impl AggregateAndProof { /// Returns `true` if `validator_pubkey` signed over `self.aggregate.data.slot`. - pub fn aggregate(&self) -> AttestationRef { - match self { - AggregateAndProof::Base(a) => AttestationRef::Base(&a.aggregate), - AggregateAndProof::Electra(a) => AttestationRef::Electra(&a.aggregate), - } + pub fn aggregate<'a>(&'a self) -> AttestationRef<'a, E> { + map_aggregate_and_proof_ref_into_attestation_ref!(&'a _, self.to_ref(), |inner, cons| { + cons(&inner.aggregate) + }) } } diff --git a/consensus/types/src/attestation.rs b/consensus/types/src/attestation.rs index c8d1c3fb9b..88993267a9 100644 --- a/consensus/types/src/attestation.rs +++ b/consensus/types/src/attestation.rs @@ -1,6 +1,6 @@ use crate::slot_data::SlotData; +use crate::Checkpoint; use crate::{test_utils::TestRandom, Hash256, Slot}; -use crate::{Checkpoint, ForkName}; use derivative::Derivative; use safe_arith::ArithError; use serde::{Deserialize, Serialize}; @@ -99,7 +99,7 @@ impl Attestation { target: Checkpoint, spec: &ChainSpec, ) -> Result { - if spec.fork_name_at_slot::(slot) >= ForkName::Electra { + if spec.fork_name_at_slot::(slot).electra_enabled() { let mut committee_bits: BitVector = BitVector::default(); committee_bits .set(committee_index as usize, true) @@ -277,16 +277,6 @@ impl<'a, E: EthSpec> AttestationRef<'a, E> { } impl AttestationElectra { - /// Are the aggregation bitfields of these attestations disjoint? - // TODO(electra): check whether the definition from CompactIndexedAttestation::should_aggregate - // is useful where this is used, i.e. only consider attestations disjoint when their committees - // match AND their aggregation bits do not intersect. - pub fn signers_disjoint_from(&self, other: &Self) -> bool { - self.aggregation_bits - .intersection(&other.aggregation_bits) - .is_zero() - } - pub fn committee_index(&self) -> Option { self.get_committee_indices().first().cloned() } @@ -304,7 +294,6 @@ impl AttestationElectra { /// The aggregation bitfields must be disjoint, and the data must be the same. pub fn aggregate(&mut self, other: &Self) { debug_assert_eq!(self.data, other.data); - debug_assert!(self.signers_disjoint_from(other)); self.aggregation_bits = self.aggregation_bits.union(&other.aggregation_bits); self.signature.add_assign_aggregate(&other.signature); } @@ -358,19 +347,11 @@ impl AttestationElectra { } impl AttestationBase { - /// Are the aggregation bitfields of these attestations disjoint? - pub fn signers_disjoint_from(&self, other: &Self) -> bool { - self.aggregation_bits - .intersection(&other.aggregation_bits) - .is_zero() - } - /// Aggregate another Attestation into this one. /// /// The aggregation bitfields must be disjoint, and the data must be the same. pub fn aggregate(&mut self, other: &Self) { debug_assert_eq!(self.data, other.data); - debug_assert!(self.signers_disjoint_from(other)); self.aggregation_bits = self.aggregation_bits.union(&other.aggregation_bits); self.signature.add_assign_aggregate(&other.signature); } diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index a3df69652c..b8d0c8c2b0 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -118,12 +118,8 @@ impl ExecutionPayloadHeader { pub fn ssz_max_var_len_for_fork(fork_name: ForkName) -> usize { // Matching here in case variable fields are added in future forks. match fork_name { - ForkName::Base - | ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { + ForkName::Base | ForkName::Altair => 0, + ForkName::Bellatrix | ForkName::Capella | ForkName::Deneb | ForkName::Electra => { // Max size of variable length `extra_data` field E::max_extra_data_bytes() * ::ssz_fixed_len() } diff --git a/consensus/types/src/fork_name.rs b/consensus/types/src/fork_name.rs index f0810e2bdb..96f206d454 100644 --- a/consensus/types/src/fork_name.rs +++ b/consensus/types/src/fork_name.rs @@ -120,6 +120,10 @@ impl ForkName { } } + pub fn deneb_enabled(self) -> bool { + self >= ForkName::Deneb + } + pub fn electra_enabled(self) -> bool { self >= ForkName::Electra } diff --git a/consensus/types/src/indexed_attestation.rs b/consensus/types/src/indexed_attestation.rs index 900c905938..19d1501075 100644 --- a/consensus/types/src/indexed_attestation.rs +++ b/consensus/types/src/indexed_attestation.rs @@ -240,38 +240,6 @@ mod quoted_variable_list_u64 { } } -#[derive(Debug, Clone, Encode, Decode, PartialEq)] -#[ssz(enum_behaviour = "union")] -pub enum IndexedAttestationOnDisk { - Base(IndexedAttestationBase), - Electra(IndexedAttestationElectra), -} - -#[derive(Debug, Clone, Encode, PartialEq)] -#[ssz(enum_behaviour = "union")] -pub enum IndexedAttestationRefOnDisk<'a, E: EthSpec> { - Base(&'a IndexedAttestationBase), - Electra(&'a IndexedAttestationElectra), -} - -impl<'a, E: EthSpec> From<&'a IndexedAttestation> for IndexedAttestationRefOnDisk<'a, E> { - fn from(attestation: &'a IndexedAttestation) -> Self { - match attestation { - IndexedAttestation::Base(attestation) => Self::Base(attestation), - IndexedAttestation::Electra(attestation) => Self::Electra(attestation), - } - } -} - -impl From> for IndexedAttestation { - fn from(attestation: IndexedAttestationOnDisk) -> Self { - match attestation { - IndexedAttestationOnDisk::Base(attestation) => Self::Base(attestation), - IndexedAttestationOnDisk::Electra(attestation) => Self::Electra(attestation), - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 2a73ff0f37..6a98d7ade9 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -176,27 +176,28 @@ pub use crate::fork_versioned_response::{ForkVersionDeserialize, ForkVersionedRe pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN}; pub use crate::historical_batch::HistoricalBatch; pub use crate::indexed_attestation::{ - IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, - IndexedAttestationOnDisk, IndexedAttestationRef, IndexedAttestationRefOnDisk, + IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef, }; pub use crate::light_client_bootstrap::{ LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella, - LightClientBootstrapDeneb, + LightClientBootstrapDeneb, LightClientBootstrapElectra, }; pub use crate::light_client_finality_update::{ LightClientFinalityUpdate, LightClientFinalityUpdateAltair, LightClientFinalityUpdateCapella, - LightClientFinalityUpdateDeneb, + LightClientFinalityUpdateDeneb, LightClientFinalityUpdateElectra, }; pub use crate::light_client_header::{ LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, + LightClientHeaderElectra, }; pub use crate::light_client_optimistic_update::{ LightClientOptimisticUpdate, LightClientOptimisticUpdateAltair, LightClientOptimisticUpdateCapella, LightClientOptimisticUpdateDeneb, + LightClientOptimisticUpdateElectra, }; pub use crate::light_client_update::{ Error as LightClientError, LightClientUpdate, LightClientUpdateAltair, - LightClientUpdateCapella, LightClientUpdateDeneb, + LightClientUpdateCapella, LightClientUpdateDeneb, LightClientUpdateElectra, }; pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_list::ParticipationList; diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client_bootstrap.rs index 61da0e1b11..e3a85744de 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client_bootstrap.rs @@ -1,7 +1,8 @@ use crate::{ light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec, EthSpec, FixedVector, ForkName, ForkVersionDeserialize, Hash256, LightClientHeader, LightClientHeaderAltair, - LightClientHeaderCapella, LightClientHeaderDeneb, SignedBeaconBlock, Slot, SyncCommittee, + LightClientHeaderCapella, LightClientHeaderDeneb, LightClientHeaderElectra, SignedBeaconBlock, + Slot, SyncCommittee, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -16,7 +17,7 @@ use tree_hash_derive::TreeHash; /// A LightClientBootstrap is the initializer we send over to light_client nodes /// that are trying to generate their basic storage when booting up. #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -51,6 +52,8 @@ pub struct LightClientBootstrap { pub header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "header_deneb"))] pub header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "header_electra"))] + pub header: LightClientHeaderElectra, /// The `SyncCommittee` used in the requested period. pub current_sync_committee: Arc>, /// Merkle proof for sync committee @@ -66,6 +69,7 @@ impl LightClientBootstrap { Self::Altair(_) => func(ForkName::Altair), Self::Capella(_) => func(ForkName::Capella), Self::Deneb(_) => func(ForkName::Deneb), + Self::Electra(_) => func(ForkName::Electra), } } @@ -82,9 +86,8 @@ impl LightClientBootstrap { Self::Altair(LightClientBootstrapAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => Self::Capella(LightClientBootstrapCapella::from_ssz_bytes(bytes)?), - ForkName::Deneb | ForkName::Electra => { - Self::Deneb(LightClientBootstrapDeneb::from_ssz_bytes(bytes)?) - } + ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb::from_ssz_bytes(bytes)?), + ForkName::Electra => Self::Electra(LightClientBootstrapElectra::from_ssz_bytes(bytes)?), ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientBootstrap decoding for {fork_name} not implemented" @@ -97,18 +100,16 @@ impl LightClientBootstrap { #[allow(clippy::arithmetic_side_effects)] pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { - // TODO(electra): review electra changes - match fork_name { + let fixed_len = match fork_name { ForkName::Base => 0, - ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { + ForkName::Altair | ForkName::Bellatrix => { as Encode>::ssz_fixed_len() - + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } - } + ForkName::Capella => as Encode>::ssz_fixed_len(), + ForkName::Deneb => as Encode>::ssz_fixed_len(), + ForkName::Electra => as Encode>::ssz_fixed_len(), + }; + fixed_len + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } pub fn from_beacon_state( @@ -138,11 +139,16 @@ impl LightClientBootstrap { current_sync_committee, current_sync_committee_branch, }), - ForkName::Deneb | ForkName::Electra => Self::Deneb(LightClientBootstrapDeneb { + ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb { header: LightClientHeaderDeneb::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch, }), + ForkName::Electra => Self::Electra(LightClientBootstrapElectra { + header: LightClientHeaderElectra::block_to_light_client_header(block)?, + current_sync_committee, + current_sync_committee_branch, + }), }; Ok(light_client_bootstrap) diff --git a/consensus/types/src/light_client_finality_update.rs b/consensus/types/src/light_client_finality_update.rs index 29c526e291..a9e24e03db 100644 --- a/consensus/types/src/light_client_finality_update.rs +++ b/consensus/types/src/light_client_finality_update.rs @@ -2,7 +2,8 @@ use super::{EthSpec, FixedVector, Hash256, LightClientHeader, Slot, SyncAggregat use crate::ChainSpec; use crate::{ light_client_update::*, test_utils::TestRandom, ForkName, ForkVersionDeserialize, - LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, SignedBeaconBlock, + LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, + LightClientHeaderElectra, SignedBeaconBlock, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -15,7 +16,7 @@ use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -50,6 +51,8 @@ pub struct LightClientFinalityUpdate { pub attested_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))] pub attested_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "attested_header_electra"))] + pub attested_header: LightClientHeaderElectra, /// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch). #[superstruct(only(Altair), partial_getter(rename = "finalized_header_altair"))] pub finalized_header: LightClientHeaderAltair, @@ -57,6 +60,8 @@ pub struct LightClientFinalityUpdate { pub finalized_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "finalized_header_deneb"))] pub finalized_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "finalized_header_electra"))] + pub finalized_header: LightClientHeaderElectra, /// Merkle proof attesting finalized header. #[test_random(default)] pub finality_branch: FixedVector, @@ -80,7 +85,7 @@ impl LightClientFinalityUpdate { .map_err(|_| Error::InconsistentFork)? { ForkName::Altair | ForkName::Bellatrix => { - let finality_update = LightClientFinalityUpdateAltair { + Self::Altair(LightClientFinalityUpdateAltair { attested_header: LightClientHeaderAltair::block_to_light_client_header( attested_block, )?, @@ -90,37 +95,42 @@ impl LightClientFinalityUpdate { finality_branch, sync_aggregate, signature_slot, - }; - Self::Altair(finality_update) - } - ForkName::Capella => { - let finality_update = LightClientFinalityUpdateCapella { - attested_header: LightClientHeaderCapella::block_to_light_client_header( - attested_block, - )?, - finalized_header: LightClientHeaderCapella::block_to_light_client_header( - finalized_block, - )?, - finality_branch, - sync_aggregate, - signature_slot, - }; - Self::Capella(finality_update) - } - ForkName::Deneb | ForkName::Electra => { - let finality_update = LightClientFinalityUpdateDeneb { - attested_header: LightClientHeaderDeneb::block_to_light_client_header( - attested_block, - )?, - finalized_header: LightClientHeaderDeneb::block_to_light_client_header( - finalized_block, - )?, - finality_branch, - sync_aggregate, - signature_slot, - }; - Self::Deneb(finality_update) + }) } + ForkName::Capella => Self::Capella(LightClientFinalityUpdateCapella { + attested_header: LightClientHeaderCapella::block_to_light_client_header( + attested_block, + )?, + finalized_header: LightClientHeaderCapella::block_to_light_client_header( + finalized_block, + )?, + finality_branch, + sync_aggregate, + signature_slot, + }), + ForkName::Deneb => Self::Deneb(LightClientFinalityUpdateDeneb { + attested_header: LightClientHeaderDeneb::block_to_light_client_header( + attested_block, + )?, + finalized_header: LightClientHeaderDeneb::block_to_light_client_header( + finalized_block, + )?, + finality_branch, + sync_aggregate, + signature_slot, + }), + ForkName::Electra => Self::Electra(LightClientFinalityUpdateElectra { + attested_header: LightClientHeaderElectra::block_to_light_client_header( + attested_block, + )?, + finalized_header: LightClientHeaderElectra::block_to_light_client_header( + finalized_block, + )?, + finality_branch, + sync_aggregate, + signature_slot, + }), + ForkName::Base => return Err(Error::AltairForkNotActive), }; @@ -135,6 +145,7 @@ impl LightClientFinalityUpdate { Self::Altair(_) => func(ForkName::Altair), Self::Capella(_) => func(ForkName::Capella), Self::Deneb(_) => func(ForkName::Deneb), + Self::Electra(_) => func(ForkName::Electra), } } @@ -153,8 +164,9 @@ impl LightClientFinalityUpdate { ForkName::Capella => { Self::Capella(LightClientFinalityUpdateCapella::from_ssz_bytes(bytes)?) } - ForkName::Deneb | ForkName::Electra => { - Self::Deneb(LightClientFinalityUpdateDeneb::from_ssz_bytes(bytes)?) + ForkName::Deneb => Self::Deneb(LightClientFinalityUpdateDeneb::from_ssz_bytes(bytes)?), + ForkName::Electra => { + Self::Electra(LightClientFinalityUpdateElectra::from_ssz_bytes(bytes)?) } ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( @@ -168,18 +180,17 @@ impl LightClientFinalityUpdate { #[allow(clippy::arithmetic_side_effects)] pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { - // TODO(electra): review electra changes - match fork_name { + let fixed_size = match fork_name { ForkName::Base => 0, - ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { + ForkName::Altair | ForkName::Bellatrix => { as Encode>::ssz_fixed_len() - + 2 * LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } - } + ForkName::Capella => as Encode>::ssz_fixed_len(), + ForkName::Deneb => as Encode>::ssz_fixed_len(), + ForkName::Electra => as Encode>::ssz_fixed_len(), + }; + // `2 *` because there are two headers in the update + fixed_size + 2 * LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } } diff --git a/consensus/types/src/light_client_header.rs b/consensus/types/src/light_client_header.rs index 213ec90f95..1d6432ed6f 100644 --- a/consensus/types/src/light_client_header.rs +++ b/consensus/types/src/light_client_header.rs @@ -4,7 +4,7 @@ use crate::ForkVersionDeserialize; use crate::{light_client_update::*, BeaconBlockBody}; use crate::{ test_utils::TestRandom, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - FixedVector, Hash256, SignedBeaconBlock, + ExecutionPayloadHeaderElectra, FixedVector, Hash256, SignedBeaconBlock, }; use crate::{BeaconBlockHeader, ExecutionPayloadHeader}; use derivative::Derivative; @@ -17,7 +17,7 @@ use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -54,8 +54,13 @@ pub struct LightClientHeader { pub execution: ExecutionPayloadHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_header_deneb"))] pub execution: ExecutionPayloadHeaderDeneb, + #[superstruct( + only(Electra), + partial_getter(rename = "execution_payload_header_electra") + )] + pub execution: ExecutionPayloadHeaderElectra, - #[superstruct(only(Capella, Deneb))] + #[superstruct(only(Capella, Deneb, Electra))] pub execution_branch: FixedVector, #[ssz(skip_serializing, skip_deserializing)] @@ -81,9 +86,12 @@ impl LightClientHeader { ForkName::Capella => LightClientHeader::Capella( LightClientHeaderCapella::block_to_light_client_header(block)?, ), - ForkName::Deneb | ForkName::Electra => LightClientHeader::Deneb( + ForkName::Deneb => LightClientHeader::Deneb( LightClientHeaderDeneb::block_to_light_client_header(block)?, ), + ForkName::Electra => LightClientHeader::Electra( + LightClientHeaderElectra::block_to_light_client_header(block)?, + ), }; Ok(header) } @@ -96,9 +104,12 @@ impl LightClientHeader { ForkName::Capella => { LightClientHeader::Capella(LightClientHeaderCapella::from_ssz_bytes(bytes)?) } - ForkName::Deneb | ForkName::Electra => { + ForkName::Deneb => { LightClientHeader::Deneb(LightClientHeaderDeneb::from_ssz_bytes(bytes)?) } + ForkName::Electra => { + LightClientHeader::Electra(LightClientHeaderElectra::from_ssz_bytes(bytes)?) + } ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientHeader decoding for {fork_name} not implemented" @@ -192,6 +203,34 @@ impl LightClientHeaderDeneb { } } +impl LightClientHeaderElectra { + pub fn block_to_light_client_header(block: &SignedBeaconBlock) -> Result { + let payload = block + .message() + .execution_payload()? + .execution_payload_electra()?; + + let header = ExecutionPayloadHeaderElectra::from(payload); + let beacon_block_body = BeaconBlockBody::from( + block + .message() + .body_electra() + .map_err(|_| Error::BeaconBlockBodyError)? + .to_owned(), + ); + + let execution_branch = + beacon_block_body.block_body_merkle_proof(EXECUTION_PAYLOAD_INDEX)?; + + Ok(LightClientHeaderElectra { + beacon: block.message().block_header(), + execution: header, + execution_branch: FixedVector::new(execution_branch)?, + _phantom_data: PhantomData, + }) + } +} + impl ForkVersionDeserialize for LightClientHeader { fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>( value: serde_json::value::Value, @@ -204,9 +243,12 @@ impl ForkVersionDeserialize for LightClientHeader { ForkName::Capella => serde_json::from_value(value) .map(|light_client_header| Self::Capella(light_client_header)) .map_err(serde::de::Error::custom), - ForkName::Deneb | ForkName::Electra => serde_json::from_value(value) + ForkName::Deneb => serde_json::from_value(value) .map(|light_client_header| Self::Deneb(light_client_header)) .map_err(serde::de::Error::custom), + ForkName::Electra => serde_json::from_value(value) + .map(|light_client_header| Self::Electra(light_client_header)) + .map_err(serde::de::Error::custom), ForkName::Base => Err(serde::de::Error::custom(format!( "LightClientHeader deserialization for {fork_name} not implemented" ))), diff --git a/consensus/types/src/light_client_optimistic_update.rs b/consensus/types/src/light_client_optimistic_update.rs index 4727673f6c..708f24e770 100644 --- a/consensus/types/src/light_client_optimistic_update.rs +++ b/consensus/types/src/light_client_optimistic_update.rs @@ -2,7 +2,7 @@ use super::{EthSpec, ForkName, ForkVersionDeserialize, LightClientHeader, Slot, use crate::test_utils::TestRandom; use crate::{ light_client_update::*, ChainSpec, LightClientHeaderAltair, LightClientHeaderCapella, - LightClientHeaderDeneb, SignedBeaconBlock, + LightClientHeaderDeneb, LightClientHeaderElectra, SignedBeaconBlock, }; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; @@ -18,7 +18,7 @@ use tree_hash_derive::TreeHash; /// A LightClientOptimisticUpdate is the update we send on each slot, /// it is based off the current unfinalized epoch is verified only against BLS signature. #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -53,6 +53,8 @@ pub struct LightClientOptimisticUpdate { pub attested_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))] pub attested_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "attested_header_electra"))] + pub attested_header: LightClientHeaderElectra, /// current sync aggregate pub sync_aggregate: SyncAggregate, /// Slot of the sync aggregated signature @@ -86,13 +88,20 @@ impl LightClientOptimisticUpdate { sync_aggregate, signature_slot, }), - ForkName::Deneb | ForkName::Electra => Self::Deneb(LightClientOptimisticUpdateDeneb { + ForkName::Deneb => Self::Deneb(LightClientOptimisticUpdateDeneb { attested_header: LightClientHeaderDeneb::block_to_light_client_header( attested_block, )?, sync_aggregate, signature_slot, }), + ForkName::Electra => Self::Electra(LightClientOptimisticUpdateElectra { + attested_header: LightClientHeaderElectra::block_to_light_client_header( + attested_block, + )?, + sync_aggregate, + signature_slot, + }), ForkName::Base => return Err(Error::AltairForkNotActive), }; @@ -107,6 +116,7 @@ impl LightClientOptimisticUpdate { Self::Altair(_) => func(ForkName::Altair), Self::Capella(_) => func(ForkName::Capella), Self::Deneb(_) => func(ForkName::Deneb), + Self::Electra(_) => func(ForkName::Electra), } } @@ -139,9 +149,12 @@ impl LightClientOptimisticUpdate { ForkName::Capella => { Self::Capella(LightClientOptimisticUpdateCapella::from_ssz_bytes(bytes)?) } - ForkName::Deneb | ForkName::Electra => { + ForkName::Deneb => { Self::Deneb(LightClientOptimisticUpdateDeneb::from_ssz_bytes(bytes)?) } + ForkName::Electra => { + Self::Electra(LightClientOptimisticUpdateElectra::from_ssz_bytes(bytes)?) + } ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientOptimisticUpdate decoding for {fork_name} not implemented" @@ -154,18 +167,16 @@ impl LightClientOptimisticUpdate { #[allow(clippy::arithmetic_side_effects)] pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { - // TODO(electra): review electra changes - match fork_name { + let fixed_len = match fork_name { ForkName::Base => 0, - ForkName::Altair - | ForkName::Bellatrix - | ForkName::Capella - | ForkName::Deneb - | ForkName::Electra => { + ForkName::Altair | ForkName::Bellatrix => { as Encode>::ssz_fixed_len() - + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } - } + ForkName::Capella => as Encode>::ssz_fixed_len(), + ForkName::Deneb => as Encode>::ssz_fixed_len(), + ForkName::Electra => as Encode>::ssz_fixed_len(), + }; + fixed_len + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) } } diff --git a/consensus/types/src/light_client_update.rs b/consensus/types/src/light_client_update.rs index 002fbea2d3..76ad568988 100644 --- a/consensus/types/src/light_client_update.rs +++ b/consensus/types/src/light_client_update.rs @@ -1,4 +1,5 @@ use super::{EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee}; +use crate::light_client_header::LightClientHeaderElectra; use crate::{ beacon_state, test_utils::TestRandom, BeaconBlock, BeaconBlockHeader, BeaconState, ChainSpec, ForkName, ForkVersionDeserialize, LightClientHeaderAltair, LightClientHeaderCapella, @@ -76,7 +77,7 @@ impl From for Error { /// or to sync up to the last committee period, we need to have one ready for each ALTAIR period /// we go over, note: there is no need to keep all of the updates from [ALTAIR_PERIOD, CURRENT_PERIOD]. #[superstruct( - variants(Altair, Capella, Deneb), + variants(Altair, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -111,6 +112,8 @@ pub struct LightClientUpdate { pub attested_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))] pub attested_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "attested_header_electra"))] + pub attested_header: LightClientHeaderElectra, /// The `SyncCommittee` used in the next period. pub next_sync_committee: Arc>, /// Merkle proof for next sync committee @@ -122,6 +125,8 @@ pub struct LightClientUpdate { pub finalized_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "finalized_header_deneb"))] pub finalized_header: LightClientHeaderDeneb, + #[superstruct(only(Electra), partial_getter(rename = "finalized_header_electra"))] + pub finalized_header: LightClientHeaderElectra, /// Merkle proof attesting finalized header. pub finality_branch: FixedVector, /// current sync aggreggate @@ -221,7 +226,7 @@ impl LightClientUpdate { signature_slot: block.slot(), }) } - ForkName::Deneb | ForkName::Electra => { + ForkName::Deneb => { let attested_header = LightClientHeaderDeneb::block_to_light_client_header(attested_block)?; let finalized_header = @@ -236,6 +241,23 @@ impl LightClientUpdate { signature_slot: block.slot(), }) } + ForkName::Electra => { + let attested_header = + LightClientHeaderElectra::block_to_light_client_header(attested_block)?; + let finalized_header = + LightClientHeaderElectra::block_to_light_client_header(finalized_block)?; + Self::Electra(LightClientUpdateElectra { + attested_header, + next_sync_committee: attested_state.next_sync_committee()?.clone(), + next_sync_committee_branch: FixedVector::new(next_sync_committee_branch)?, + finalized_header, + finality_branch: FixedVector::new(finality_branch)?, + sync_aggregate: sync_aggregate.clone(), + signature_slot: block.slot(), + }) + } // To add a new fork, just append the new fork variant on the latest fork. Forks that + // have a distinct execution header will need a new LightClientUdpate variant only + // if you need to test or support lightclient usages }; Ok(light_client_update) @@ -247,9 +269,8 @@ impl LightClientUpdate { Self::Altair(LightClientUpdateAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => Self::Capella(LightClientUpdateCapella::from_ssz_bytes(bytes)?), - ForkName::Deneb | ForkName::Electra => { - Self::Deneb(LightClientUpdateDeneb::from_ssz_bytes(bytes)?) - } + ForkName::Deneb => Self::Deneb(LightClientUpdateDeneb::from_ssz_bytes(bytes)?), + ForkName::Electra => Self::Electra(LightClientUpdateElectra::from_ssz_bytes(bytes)?), ForkName::Base => { return Err(ssz::DecodeError::BytesInvalid(format!( "LightClientUpdate decoding for {fork_name} not implemented" diff --git a/consensus/types/src/signed_aggregate_and_proof.rs b/consensus/types/src/signed_aggregate_and_proof.rs index d339cecaae..26eca19bf1 100644 --- a/consensus/types/src/signed_aggregate_and_proof.rs +++ b/consensus/types/src/signed_aggregate_and_proof.rs @@ -34,7 +34,9 @@ use tree_hash_derive::TreeHash; ), serde(bound = "E: EthSpec"), arbitrary(bound = "E: EthSpec"), - ) + ), + map_into(Attestation), + map_ref_into(AggregateAndProofRef) )] #[derive( arbitrary::Arbitrary, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash, @@ -102,19 +104,17 @@ impl SignedAggregateAndProof { } } - pub fn message(&self) -> AggregateAndProofRef { - match self { - SignedAggregateAndProof::Base(message) => AggregateAndProofRef::Base(&message.message), - SignedAggregateAndProof::Electra(message) => { - AggregateAndProofRef::Electra(&message.message) - } - } + pub fn message<'a>(&'a self) -> AggregateAndProofRef<'a, E> { + map_signed_aggregate_and_proof_ref_into_aggregate_and_proof_ref!( + &'a _, + self.to_ref(), + |inner, cons| { cons(&inner.message) } + ) } pub fn into_attestation(self) -> Attestation { - match self { - Self::Base(att) => Attestation::Base(att.message.aggregate), - Self::Electra(att) => Attestation::Electra(att.message.aggregate), - } + map_signed_aggregate_and_proof_into_attestation!(self, |inner, cons| { + cons(inner.message.aggregate) + }) } } diff --git a/consensus/types/src/sync_committee_contribution.rs b/consensus/types/src/sync_committee_contribution.rs index 81de83ba3b..c348c3e8be 100644 --- a/consensus/types/src/sync_committee_contribution.rs +++ b/consensus/types/src/sync_committee_contribution.rs @@ -63,13 +63,6 @@ impl SyncCommitteeContribution { }) } - /// Are the aggregation bitfields of these sync contribution disjoint? - pub fn signers_disjoint_from(&self, other: &Self) -> bool { - self.aggregation_bits - .intersection(&other.aggregation_bits) - .is_zero() - } - /// Aggregate another `SyncCommitteeContribution` into this one. /// /// The aggregation bitfields must be disjoint, and the data must be the same. @@ -77,7 +70,6 @@ impl SyncCommitteeContribution { debug_assert_eq!(self.slot, other.slot); debug_assert_eq!(self.beacon_block_root, other.beacon_block_root); debug_assert_eq!(self.subcommittee_index, other.subcommittee_index); - debug_assert!(self.signers_disjoint_from(other)); self.aggregation_bits = self.aggregation_bits.union(&other.aggregation_bits); self.signature.add_assign_aggregate(&other.signature); diff --git a/slasher/Cargo.toml b/slasher/Cargo.toml index ef5cb8249e..563c4599d8 100644 --- a/slasher/Cargo.toml +++ b/slasher/Cargo.toml @@ -29,6 +29,7 @@ tree_hash = { workspace = true } tree_hash_derive = { workspace = true } types = { workspace = true } strum = { workspace = true } +ssz_types = { workspace = true } # MDBX is pinned at the last version with Windows and macOS support. mdbx = { package = "libmdbx", git = "https://github.com/sigp/libmdbx-rs", tag = "v0.1.4", optional = true } diff --git a/slasher/src/config.rs b/slasher/src/config.rs index 26ac884153..1851e2e441 100644 --- a/slasher/src/config.rs +++ b/slasher/src/config.rs @@ -11,7 +11,7 @@ pub const DEFAULT_VALIDATOR_CHUNK_SIZE: usize = 256; pub const DEFAULT_HISTORY_LENGTH: usize = 4096; pub const DEFAULT_UPDATE_PERIOD: u64 = 12; pub const DEFAULT_SLOT_OFFSET: f64 = 10.5; -pub const DEFAULT_MAX_DB_SIZE: usize = 256 * 1024; // 256 GiB +pub const DEFAULT_MAX_DB_SIZE: usize = 512 * 1024; // 512 GiB pub const DEFAULT_ATTESTATION_ROOT_CACHE_SIZE: NonZeroUsize = new_non_zero_usize(100_000); pub const DEFAULT_BROADCAST: bool = false; diff --git a/slasher/src/database.rs b/slasher/src/database.rs index 16089706a0..801abe9283 100644 --- a/slasher/src/database.rs +++ b/slasher/src/database.rs @@ -13,17 +13,19 @@ use parking_lot::Mutex; use serde::de::DeserializeOwned; use slog::{info, Logger}; use ssz::{Decode, Encode}; +use ssz_derive::{Decode, Encode}; use std::borrow::{Borrow, Cow}; use std::marker::PhantomData; use std::sync::Arc; use tree_hash::TreeHash; use types::{ - Epoch, EthSpec, Hash256, IndexedAttestation, IndexedAttestationOnDisk, - IndexedAttestationRefOnDisk, ProposerSlashing, SignedBeaconBlockHeader, Slot, + AggregateSignature, AttestationData, ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, + IndexedAttestationBase, IndexedAttestationElectra, ProposerSlashing, SignedBeaconBlockHeader, + Slot, VariableList, }; /// Current database schema version, to check compatibility of on-disk DB with software. -pub const CURRENT_SCHEMA_VERSION: u64 = 4; +pub const CURRENT_SCHEMA_VERSION: u64 = 3; /// Metadata about the slashing database itself. const METADATA_DB: &str = "metadata"; @@ -70,6 +72,7 @@ pub struct SlasherDB { /// LRU cache mapping indexed attestation IDs to their attestation data roots. attestation_root_cache: Mutex>, pub(crate) config: Arc, + pub(crate) spec: Arc, _phantom: PhantomData, } @@ -236,6 +239,43 @@ impl AsRef<[u8]> for IndexedAttestationId { } } +/// Indexed attestation that abstracts over Phase0 and Electra variants by using a plain `Vec` for +/// the attesting indices. +/// +/// This allows us to avoid rewriting the entire indexed attestation database at Electra, which +/// saves a lot of execution time. The bytes that it encodes to are the same as the bytes that a +/// regular IndexedAttestation encodes to, because SSZ doesn't care about the length-bound. +#[derive(Debug, PartialEq, Decode, Encode)] +pub struct IndexedAttestationOnDisk { + attesting_indices: Vec, + data: AttestationData, + signature: AggregateSignature, +} + +impl IndexedAttestationOnDisk { + fn into_indexed_attestation( + self, + spec: &ChainSpec, + ) -> Result, Error> { + let fork_at_target_epoch = spec.fork_name_at_epoch(self.data.target.epoch); + if fork_at_target_epoch.electra_enabled() { + let attesting_indices = VariableList::new(self.attesting_indices)?; + Ok(IndexedAttestation::Electra(IndexedAttestationElectra { + attesting_indices, + data: self.data, + signature: self.signature, + })) + } else { + let attesting_indices = VariableList::new(self.attesting_indices)?; + Ok(IndexedAttestation::Base(IndexedAttestationBase { + attesting_indices, + data: self.data, + signature: self.signature, + })) + } + } +} + /// Bincode deserialization specialised to `Cow<[u8]>`. fn bincode_deserialize(bytes: Cow<[u8]>) -> Result { Ok(bincode::deserialize(bytes.borrow())?) @@ -246,7 +286,7 @@ fn ssz_decode(bytes: Cow<[u8]>) -> Result { } impl SlasherDB { - pub fn open(config: Arc, log: Logger) -> Result { + pub fn open(config: Arc, spec: Arc, log: Logger) -> Result { info!(log, "Opening slasher database"; "backend" => %config.backend); std::fs::create_dir_all(&config.database_path)?; @@ -269,6 +309,7 @@ impl SlasherDB { databases, attestation_root_cache, config, + spec, _phantom: PhantomData, }; @@ -458,9 +499,8 @@ impl SlasherDB { }; let attestation_key = IndexedAttestationId::new(indexed_att_id); - let indexed_attestation_on_disk: IndexedAttestationRefOnDisk = - indexed_attestation.into(); - let data = indexed_attestation_on_disk.as_ssz_bytes(); + // IndexedAttestationOnDisk and IndexedAttestation have compatible encodings. + let data = indexed_attestation.as_ssz_bytes(); cursor.put(attestation_key.as_ref(), &data)?; drop(cursor); @@ -484,8 +524,8 @@ impl SlasherDB { .ok_or(Error::MissingIndexedAttestation { id: indexed_attestation_id.as_u64(), })?; - let indexed_attestation: IndexedAttestationOnDisk = ssz_decode(bytes)?; - Ok(indexed_attestation.into()) + let indexed_attestation_on_disk: IndexedAttestationOnDisk = ssz_decode(bytes)?; + indexed_attestation_on_disk.into_indexed_attestation(&self.spec) } fn get_attestation_data_root( @@ -775,3 +815,93 @@ impl SlasherDB { Ok(()) } } + +#[cfg(test)] +mod test { + use super::*; + use types::{Checkpoint, ForkName, MainnetEthSpec, Unsigned}; + + type E = MainnetEthSpec; + + fn indexed_attestation_on_disk_roundtrip_test( + spec: &ChainSpec, + make_attestation: fn( + Vec, + AttestationData, + AggregateSignature, + ) -> IndexedAttestation, + committee_len: u64, + ) { + let attestation_data = AttestationData { + slot: Slot::new(1000), + index: 0, + beacon_block_root: Hash256::repeat_byte(0xaa), + source: Checkpoint { + epoch: Epoch::new(0), + root: Hash256::repeat_byte(0xbb), + }, + target: Checkpoint { + epoch: Epoch::new(31), + root: Hash256::repeat_byte(0xcc), + }, + }; + + let attesting_indices = (0..committee_len).collect::>(); + let signature = AggregateSignature::infinity(); + + let fork_attestation = make_attestation( + attesting_indices.clone(), + attestation_data.clone(), + signature.clone(), + ); + + let on_disk = IndexedAttestationOnDisk { + attesting_indices, + data: attestation_data, + signature, + }; + let encoded = on_disk.as_ssz_bytes(); + assert_eq!(encoded, fork_attestation.as_ssz_bytes()); + + let decoded_on_disk = IndexedAttestationOnDisk::from_ssz_bytes(&encoded).unwrap(); + assert_eq!(decoded_on_disk, on_disk); + + let decoded = on_disk.into_indexed_attestation(spec).unwrap(); + assert_eq!(decoded, fork_attestation); + } + + /// Check that `IndexedAttestationOnDisk` and `IndexedAttestation` have compatible encodings. + #[test] + fn indexed_attestation_on_disk_roundtrip_base() { + let spec = ForkName::Base.make_genesis_spec(E::default_spec()); + let make_attestation = |attesting_indices, data, signature| { + IndexedAttestation::::Base(IndexedAttestationBase { + attesting_indices: VariableList::new(attesting_indices).unwrap(), + data, + signature, + }) + }; + indexed_attestation_on_disk_roundtrip_test( + &spec, + make_attestation, + ::MaxValidatorsPerCommittee::to_u64(), + ) + } + + #[test] + fn indexed_attestation_on_disk_roundtrip_electra() { + let spec = ForkName::Electra.make_genesis_spec(E::default_spec()); + let make_attestation = |attesting_indices, data, signature| { + IndexedAttestation::::Electra(IndexedAttestationElectra { + attesting_indices: VariableList::new(attesting_indices).unwrap(), + data, + signature, + }) + }; + indexed_attestation_on_disk_roundtrip_test( + &spec, + make_attestation, + ::MaxValidatorsPerSlot::to_u64(), + ) + } +} diff --git a/slasher/src/error.rs b/slasher/src/error.rs index b939c281e9..8d3295b22a 100644 --- a/slasher/src/error.rs +++ b/slasher/src/error.rs @@ -13,6 +13,7 @@ pub enum Error { DatabaseIOError(io::Error), DatabasePermissionsError(filesystem::Error), SszDecodeError(ssz::DecodeError), + SszTypesError(ssz_types::Error), BincodeError(bincode::Error), ArithError(safe_arith::ArithError), ChunkIndexOutOfBounds(usize), @@ -100,6 +101,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: ssz_types::Error) -> Self { + Error::SszTypesError(e) + } +} + impl From for Error { fn from(e: bincode::Error) -> Self { Error::BincodeError(e) diff --git a/slasher/src/migrate.rs b/slasher/src/migrate.rs index ce298caaf2..674ab9c132 100644 --- a/slasher/src/migrate.rs +++ b/slasher/src/migrate.rs @@ -17,10 +17,6 @@ impl SlasherDB { software_schema_version: CURRENT_SCHEMA_VERSION, }), (x, y) if x == y => Ok(self), - (3, 4) => { - // TODO(electra): db migration due to `IndexedAttestationOnDisk` - Ok(self) - } (_, _) => Err(Error::IncompatibleSchemaVersion { database_schema_version: schema_version, software_schema_version: CURRENT_SCHEMA_VERSION, diff --git a/slasher/src/slasher.rs b/slasher/src/slasher.rs index fc8e8453c8..0bb7c9c3ff 100644 --- a/slasher/src/slasher.rs +++ b/slasher/src/slasher.rs @@ -13,7 +13,8 @@ use slog::{debug, error, info, Logger}; use std::collections::HashSet; use std::sync::Arc; use types::{ - AttesterSlashing, Epoch, EthSpec, IndexedAttestation, ProposerSlashing, SignedBeaconBlockHeader, + AttesterSlashing, ChainSpec, Epoch, EthSpec, IndexedAttestation, ProposerSlashing, + SignedBeaconBlockHeader, }; #[derive(Debug)] @@ -28,10 +29,10 @@ pub struct Slasher { } impl Slasher { - pub fn open(config: Config, log: Logger) -> Result { + pub fn open(config: Config, spec: Arc, log: Logger) -> Result { config.validate()?; let config = Arc::new(config); - let db = SlasherDB::open(config.clone(), log.clone())?; + let db = SlasherDB::open(config.clone(), spec, log.clone())?; let attester_slashings = Mutex::new(HashSet::new()); let proposer_slashings = Mutex::new(HashSet::new()); let attestation_queue = AttestationQueue::default(); diff --git a/slasher/src/test_utils.rs b/slasher/src/test_utils.rs index 7019a8aed7..453d0e6667 100644 --- a/slasher/src/test_utils.rs +++ b/slasher/src/test_utils.rs @@ -1,9 +1,10 @@ use std::collections::HashSet; +use std::sync::Arc; use types::{ indexed_attestation::{IndexedAttestationBase, IndexedAttestationElectra}, AggregateSignature, AttestationData, AttesterSlashing, AttesterSlashingBase, - AttesterSlashingElectra, BeaconBlockHeader, Checkpoint, Epoch, Hash256, IndexedAttestation, - MainnetEthSpec, Signature, SignedBeaconBlockHeader, Slot, + AttesterSlashingElectra, BeaconBlockHeader, ChainSpec, Checkpoint, Epoch, EthSpec, Hash256, + IndexedAttestation, MainnetEthSpec, Signature, SignedBeaconBlockHeader, Slot, }; pub type E = MainnetEthSpec; @@ -145,3 +146,7 @@ pub fn block(slot: u64, proposer_index: u64, block_root: u64) -> SignedBeaconBlo signature: Signature::empty(), } } + +pub fn chain_spec() -> Arc { + Arc::new(E::default_spec()) +} diff --git a/slasher/tests/attester_slashings.rs b/slasher/tests/attester_slashings.rs index b74e491e4b..902141d971 100644 --- a/slasher/tests/attester_slashings.rs +++ b/slasher/tests/attester_slashings.rs @@ -6,7 +6,8 @@ use rayon::prelude::*; use slasher::{ config::DEFAULT_CHUNK_SIZE, test_utils::{ - att_slashing, indexed_att, indexed_att_electra, slashed_validators_from_slashings, E, + att_slashing, chain_spec, indexed_att, indexed_att_electra, + slashed_validators_from_slashings, E, }, Config, Slasher, }; @@ -270,7 +271,8 @@ fn slasher_test( ) { let tempdir = tempdir().unwrap(); let config = Config::new(tempdir.path().into()); - let slasher = Slasher::open(config, test_logger()).unwrap(); + let spec = chain_spec(); + let slasher = Slasher::open(config, spec, test_logger()).unwrap(); let current_epoch = Epoch::new(current_epoch); for (i, attestation) in attestations.iter().enumerate() { @@ -299,7 +301,8 @@ fn parallel_slasher_test( ) { let tempdir = tempdir().unwrap(); let config = Config::new(tempdir.path().into()); - let slasher = Slasher::open(config, test_logger()).unwrap(); + let spec = chain_spec(); + let slasher = Slasher::open(config, spec, test_logger()).unwrap(); let current_epoch = Epoch::new(current_epoch); attestations diff --git a/slasher/tests/proposer_slashings.rs b/slasher/tests/proposer_slashings.rs index 3b7b8ed583..2d2738087d 100644 --- a/slasher/tests/proposer_slashings.rs +++ b/slasher/tests/proposer_slashings.rs @@ -2,7 +2,7 @@ use logging::test_logger; use slasher::{ - test_utils::{block as test_block, E}, + test_utils::{block as test_block, chain_spec, E}, Config, Slasher, }; use tempfile::tempdir; @@ -12,7 +12,8 @@ use types::{Epoch, EthSpec}; fn empty_pruning() { let tempdir = tempdir().unwrap(); let config = Config::new(tempdir.path().into()); - let slasher = Slasher::::open(config, test_logger()).unwrap(); + let spec = chain_spec(); + let slasher = Slasher::::open(config, spec, test_logger()).unwrap(); slasher.prune_database(Epoch::new(0)).unwrap(); } @@ -24,8 +25,9 @@ fn block_pruning() { let mut config = Config::new(tempdir.path().into()); config.chunk_size = 2; config.history_length = 2; + let spec = chain_spec(); - let slasher = Slasher::::open(config.clone(), test_logger()).unwrap(); + let slasher = Slasher::::open(config.clone(), spec, test_logger()).unwrap(); let current_epoch = Epoch::from(2 * config.history_length); // Pruning the empty database should be safe. diff --git a/slasher/tests/random.rs b/slasher/tests/random.rs index ce0e42df1d..ebfe0ef4e9 100644 --- a/slasher/tests/random.rs +++ b/slasher/tests/random.rs @@ -4,7 +4,7 @@ use logging::test_logger; use rand::prelude::*; use slasher::{ test_utils::{ - block, indexed_att, slashed_validators_from_attestations, + block, chain_spec, indexed_att, slashed_validators_from_attestations, slashed_validators_from_slashings, E, }, Config, Slasher, @@ -49,7 +49,9 @@ fn random_test(seed: u64, test_config: TestConfig) { config.chunk_size = 1 << chunk_size_exponent; config.history_length = 1 << rng.gen_range(chunk_size_exponent..chunk_size_exponent + 3); - let slasher = Slasher::::open(config.clone(), test_logger()).unwrap(); + let spec = chain_spec(); + + let slasher = Slasher::::open(config.clone(), spec, test_logger()).unwrap(); let validators = (0..num_validators as u64).collect::>(); diff --git a/slasher/tests/wrap_around.rs b/slasher/tests/wrap_around.rs index d2c876d363..9a42aeb60b 100644 --- a/slasher/tests/wrap_around.rs +++ b/slasher/tests/wrap_around.rs @@ -1,7 +1,10 @@ #![cfg(any(feature = "mdbx", feature = "lmdb"))] use logging::test_logger; -use slasher::{test_utils::indexed_att, Config, Slasher}; +use slasher::{ + test_utils::{chain_spec, indexed_att}, + Config, Slasher, +}; use tempfile::tempdir; use types::Epoch; @@ -9,11 +12,12 @@ use types::Epoch; fn attestation_pruning_empty_wrap_around() { let tempdir = tempdir().unwrap(); let mut config = Config::new(tempdir.path().into()); + let spec = chain_spec(); config.validator_chunk_size = 1; config.chunk_size = 16; config.history_length = 16; - let slasher = Slasher::open(config.clone(), test_logger()).unwrap(); + let slasher = Slasher::open(config.clone(), spec, test_logger()).unwrap(); let v = vec![0]; let history_length = config.history_length as u64; diff --git a/testing/ef_tests/check_all_files_accessed.py b/testing/ef_tests/check_all_files_accessed.py index 7629d61827..5f425ab98b 100755 --- a/testing/ef_tests/check_all_files_accessed.py +++ b/testing/ef_tests/check_all_files_accessed.py @@ -42,7 +42,9 @@ excluded_paths = [ "bls12-381-tests/deserialization_G2", "bls12-381-tests/hash_to_G2", "tests/.*/eip6110", - "tests/.*/whisk" + "tests/.*/whisk", + # TODO(electra): re-enable in https://github.com/sigp/lighthouse/pull/5758 + "tests/.*/.*/ssz_static/IndexedAttestation" ] diff --git a/testing/ef_tests/src/type_name.rs b/testing/ef_tests/src/type_name.rs index cbea78dabf..f886431b40 100644 --- a/testing/ef_tests/src/type_name.rs +++ b/testing/ef_tests/src/type_name.rs @@ -41,6 +41,8 @@ type_name_generic!(AggregateAndProof); type_name_generic!(AggregateAndProofBase, "AggregateAndProof"); type_name_generic!(AggregateAndProofElectra, "AggregateAndProof"); type_name_generic!(Attestation); +type_name_generic!(AttestationBase, "Attestation"); +type_name_generic!(AttestationElectra, "Attestation"); type_name!(AttestationData); type_name_generic!(AttesterSlashing); type_name_generic!(AttesterSlashingBase, "AttesterSlashing"); @@ -76,6 +78,8 @@ type_name!(Fork); type_name!(ForkData); type_name_generic!(HistoricalBatch); type_name_generic!(IndexedAttestation); +type_name_generic!(IndexedAttestationBase, "IndexedAttestation"); +type_name_generic!(IndexedAttestationElectra, "IndexedAttestation"); type_name_generic!(LightClientBootstrap); type_name_generic!(LightClientBootstrapAltair, "LightClientBootstrap"); type_name_generic!(LightClientBootstrapCapella, "LightClientBootstrap"); diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index 9e8b375b2c..6068514010 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -232,7 +232,6 @@ mod ssz_static { ssz_static_test!(fork, Fork); ssz_static_test!(fork_data, ForkData); ssz_static_test!(historical_batch, HistoricalBatch<_>); - ssz_static_test!(indexed_attestation, IndexedAttestation<_>); ssz_static_test!(pending_attestation, PendingAttestation<_>); ssz_static_test!(proposer_slashing, ProposerSlashing); ssz_static_test!(