mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-16 12:28:24 +00:00
Merge branch 'unstable' into refactor-mock-builder
This commit is contained in:
@@ -12,8 +12,8 @@ use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
use super::{
|
||||
AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey,
|
||||
Signature, SignedRoot,
|
||||
AggregateSignature, AttestationData, BitList, ChainSpec, CommitteeIndex, Domain, EthSpec, Fork,
|
||||
SecretKey, Signature, SignedRoot,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -24,6 +24,10 @@ pub enum Error {
|
||||
IncorrectStateVariant,
|
||||
InvalidCommitteeLength,
|
||||
InvalidCommitteeIndex,
|
||||
AttesterNotInCommittee(usize),
|
||||
InvalidCommittee,
|
||||
MissingCommittee,
|
||||
NoCommitteeForSlotAndIndex { slot: Slot, index: CommitteeIndex },
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for Error {
|
||||
@@ -231,6 +235,16 @@ impl<E: EthSpec> Attestation<E> {
|
||||
Attestation::Electra(att) => att.aggregation_bits.get(index),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_single_attestation_with_attester_index(
|
||||
&self,
|
||||
attester_index: usize,
|
||||
) -> Result<SingleAttestation, Error> {
|
||||
match self {
|
||||
Self::Base(_) => Err(Error::IncorrectStateVariant),
|
||||
Self::Electra(attn) => attn.to_single_attestation_with_attester_index(attester_index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> AttestationRef<'_, E> {
|
||||
@@ -287,6 +301,14 @@ impl<E: EthSpec> AttestationElectra<E> {
|
||||
self.get_committee_indices().first().cloned()
|
||||
}
|
||||
|
||||
pub fn get_aggregation_bits(&self) -> Vec<u64> {
|
||||
self.aggregation_bits
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, bit)| if bit { Some(index as u64) } else { None })
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_committee_indices(&self) -> Vec<u64> {
|
||||
self.committee_bits
|
||||
.iter()
|
||||
@@ -350,6 +372,22 @@ impl<E: EthSpec> AttestationElectra<E> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_single_attestation_with_attester_index(
|
||||
&self,
|
||||
attester_index: usize,
|
||||
) -> Result<SingleAttestation, Error> {
|
||||
let Some(committee_index) = self.committee_index() else {
|
||||
return Err(Error::InvalidCommitteeIndex);
|
||||
};
|
||||
|
||||
Ok(SingleAttestation {
|
||||
committee_index: committee_index as usize,
|
||||
attester_index,
|
||||
data: self.data.clone(),
|
||||
signature: self.signature.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> AttestationBase<E> {
|
||||
@@ -527,6 +565,58 @@ impl<E: EthSpec> ForkVersionDeserialize for Vec<Attestation<E>> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Decode,
|
||||
Encode,
|
||||
TestRandom,
|
||||
Derivative,
|
||||
arbitrary::Arbitrary,
|
||||
TreeHash,
|
||||
PartialEq,
|
||||
)]
|
||||
pub struct SingleAttestation {
|
||||
pub committee_index: usize,
|
||||
pub attester_index: usize,
|
||||
pub data: AttestationData,
|
||||
pub signature: AggregateSignature,
|
||||
}
|
||||
|
||||
impl SingleAttestation {
|
||||
pub fn to_attestation<E: EthSpec>(&self, committee: &[usize]) -> Result<Attestation<E>, Error> {
|
||||
let aggregation_bit = committee
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, &validator_index)| {
|
||||
if self.attester_index == validator_index {
|
||||
return Some(i);
|
||||
}
|
||||
None
|
||||
})
|
||||
.ok_or(Error::AttesterNotInCommittee(self.attester_index))?;
|
||||
|
||||
let mut committee_bits: BitVector<E::MaxCommitteesPerSlot> = BitVector::default();
|
||||
committee_bits
|
||||
.set(self.committee_index, true)
|
||||
.map_err(|_| Error::InvalidCommitteeIndex)?;
|
||||
|
||||
let mut aggregation_bits =
|
||||
BitList::with_capacity(committee.len()).map_err(|_| Error::InvalidCommitteeLength)?;
|
||||
|
||||
aggregation_bits.set(aggregation_bit, true)?;
|
||||
|
||||
Ok(Attestation::Electra(AttestationElectra {
|
||||
aggregation_bits,
|
||||
committee_bits,
|
||||
data: self.data.clone(),
|
||||
signature: self.signature.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -12,7 +12,7 @@ use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
use self::indexed_attestation::{IndexedAttestationBase, IndexedAttestationElectra};
|
||||
use self::indexed_attestation::IndexedAttestationBase;
|
||||
|
||||
/// A block of the `BeaconChain`.
|
||||
#[superstruct(
|
||||
@@ -499,52 +499,6 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockBell
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockCapella<E, Payload> {
|
||||
/// Return a Capella block where the block has maximum size.
|
||||
pub fn full(spec: &ChainSpec) -> Self {
|
||||
let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
|
||||
let bls_to_execution_changes = vec![
|
||||
SignedBlsToExecutionChange {
|
||||
message: BlsToExecutionChange {
|
||||
validator_index: 0,
|
||||
from_bls_pubkey: PublicKeyBytes::empty(),
|
||||
to_execution_address: Address::ZERO,
|
||||
},
|
||||
signature: Signature::empty()
|
||||
};
|
||||
E::max_bls_to_execution_changes()
|
||||
]
|
||||
.into();
|
||||
let sync_aggregate = SyncAggregate {
|
||||
sync_committee_signature: AggregateSignature::empty(),
|
||||
sync_committee_bits: BitVector::default(),
|
||||
};
|
||||
BeaconBlockCapella {
|
||||
slot: spec.genesis_slot,
|
||||
proposer_index: 0,
|
||||
parent_root: Hash256::zero(),
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBodyCapella {
|
||||
proposer_slashings: base_block.body.proposer_slashings,
|
||||
attester_slashings: base_block.body.attester_slashings,
|
||||
attestations: base_block.body.attestations,
|
||||
deposits: base_block.body.deposits,
|
||||
voluntary_exits: base_block.body.voluntary_exits,
|
||||
bls_to_execution_changes,
|
||||
sync_aggregate,
|
||||
randao_reveal: Signature::empty(),
|
||||
eth1_data: Eth1Data {
|
||||
deposit_root: Hash256::zero(),
|
||||
block_hash: Hash256::zero(),
|
||||
deposit_count: 0,
|
||||
},
|
||||
graffiti: Graffiti::default(),
|
||||
execution_payload: Payload::Capella::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockCapella<E, Payload> {
|
||||
/// Returns an empty Capella block to be used during genesis.
|
||||
fn empty(spec: &ChainSpec) -> Self {
|
||||
@@ -604,79 +558,6 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockDene
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockElectra<E, Payload> {
|
||||
/// Return a Electra block where the block has maximum size.
|
||||
pub fn full(spec: &ChainSpec) -> Self {
|
||||
let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
|
||||
let indexed_attestation: IndexedAttestationElectra<E> = IndexedAttestationElectra {
|
||||
attesting_indices: VariableList::new(vec![0_u64; E::MaxValidatorsPerSlot::to_usize()])
|
||||
.unwrap(),
|
||||
data: AttestationData::default(),
|
||||
signature: AggregateSignature::empty(),
|
||||
};
|
||||
let attester_slashings = vec![
|
||||
AttesterSlashingElectra {
|
||||
attestation_1: indexed_attestation.clone(),
|
||||
attestation_2: indexed_attestation,
|
||||
};
|
||||
E::max_attester_slashings_electra()
|
||||
]
|
||||
.into();
|
||||
let attestation = AttestationElectra {
|
||||
aggregation_bits: BitList::with_capacity(E::MaxValidatorsPerSlot::to_usize()).unwrap(),
|
||||
data: AttestationData::default(),
|
||||
signature: AggregateSignature::empty(),
|
||||
committee_bits: BitVector::new(),
|
||||
};
|
||||
let mut attestations_electra = vec![];
|
||||
for _ in 0..E::MaxAttestationsElectra::to_usize() {
|
||||
attestations_electra.push(attestation.clone());
|
||||
}
|
||||
|
||||
let bls_to_execution_changes = vec![
|
||||
SignedBlsToExecutionChange {
|
||||
message: BlsToExecutionChange {
|
||||
validator_index: 0,
|
||||
from_bls_pubkey: PublicKeyBytes::empty(),
|
||||
to_execution_address: Address::ZERO,
|
||||
},
|
||||
signature: Signature::empty()
|
||||
};
|
||||
E::max_bls_to_execution_changes()
|
||||
]
|
||||
.into();
|
||||
let sync_aggregate = SyncAggregate {
|
||||
sync_committee_signature: AggregateSignature::empty(),
|
||||
sync_committee_bits: BitVector::default(),
|
||||
};
|
||||
BeaconBlockElectra {
|
||||
slot: spec.genesis_slot,
|
||||
proposer_index: 0,
|
||||
parent_root: Hash256::zero(),
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBodyElectra {
|
||||
proposer_slashings: base_block.body.proposer_slashings,
|
||||
attester_slashings,
|
||||
attestations: attestations_electra.into(),
|
||||
deposits: base_block.body.deposits,
|
||||
voluntary_exits: base_block.body.voluntary_exits,
|
||||
bls_to_execution_changes,
|
||||
sync_aggregate,
|
||||
randao_reveal: Signature::empty(),
|
||||
eth1_data: Eth1Data {
|
||||
deposit_root: Hash256::zero(),
|
||||
block_hash: Hash256::zero(),
|
||||
deposit_count: 0,
|
||||
},
|
||||
graffiti: Graffiti::default(),
|
||||
execution_payload: Payload::Electra::default(),
|
||||
blob_kzg_commitments: VariableList::empty(),
|
||||
execution_requests: ExecutionRequests::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockElectra<E, Payload> {
|
||||
/// Returns an empty Electra block to be used during genesis.
|
||||
fn empty(spec: &ChainSpec) -> Self {
|
||||
@@ -708,79 +589,6 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockElec
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockFulu<E, Payload> {
|
||||
/// Return a Fulu block where the block has maximum size.
|
||||
pub fn full(spec: &ChainSpec) -> Self {
|
||||
let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
|
||||
let indexed_attestation: IndexedAttestationElectra<E> = IndexedAttestationElectra {
|
||||
attesting_indices: VariableList::new(vec![0_u64; E::MaxValidatorsPerSlot::to_usize()])
|
||||
.unwrap(),
|
||||
data: AttestationData::default(),
|
||||
signature: AggregateSignature::empty(),
|
||||
};
|
||||
let attester_slashings = vec![
|
||||
AttesterSlashingElectra {
|
||||
attestation_1: indexed_attestation.clone(),
|
||||
attestation_2: indexed_attestation,
|
||||
};
|
||||
E::max_attester_slashings_electra()
|
||||
]
|
||||
.into();
|
||||
let attestation = AttestationElectra {
|
||||
aggregation_bits: BitList::with_capacity(E::MaxValidatorsPerSlot::to_usize()).unwrap(),
|
||||
data: AttestationData::default(),
|
||||
signature: AggregateSignature::empty(),
|
||||
committee_bits: BitVector::new(),
|
||||
};
|
||||
let mut attestations_electra = vec![];
|
||||
for _ in 0..E::MaxAttestationsElectra::to_usize() {
|
||||
attestations_electra.push(attestation.clone());
|
||||
}
|
||||
|
||||
let bls_to_execution_changes = vec![
|
||||
SignedBlsToExecutionChange {
|
||||
message: BlsToExecutionChange {
|
||||
validator_index: 0,
|
||||
from_bls_pubkey: PublicKeyBytes::empty(),
|
||||
to_execution_address: Address::ZERO,
|
||||
},
|
||||
signature: Signature::empty()
|
||||
};
|
||||
E::max_bls_to_execution_changes()
|
||||
]
|
||||
.into();
|
||||
let sync_aggregate = SyncAggregate {
|
||||
sync_committee_signature: AggregateSignature::empty(),
|
||||
sync_committee_bits: BitVector::default(),
|
||||
};
|
||||
BeaconBlockFulu {
|
||||
slot: spec.genesis_slot,
|
||||
proposer_index: 0,
|
||||
parent_root: Hash256::zero(),
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBodyFulu {
|
||||
proposer_slashings: base_block.body.proposer_slashings,
|
||||
attester_slashings,
|
||||
attestations: attestations_electra.into(),
|
||||
deposits: base_block.body.deposits,
|
||||
voluntary_exits: base_block.body.voluntary_exits,
|
||||
bls_to_execution_changes,
|
||||
sync_aggregate,
|
||||
randao_reveal: Signature::empty(),
|
||||
eth1_data: Eth1Data {
|
||||
deposit_root: Hash256::zero(),
|
||||
block_hash: Hash256::zero(),
|
||||
deposit_count: 0,
|
||||
},
|
||||
graffiti: Graffiti::default(),
|
||||
execution_payload: Payload::Fulu::default(),
|
||||
blob_kzg_commitments: VariableList::empty(),
|
||||
execution_requests: ExecutionRequests::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> EmptyBlock for BeaconBlockFulu<E, Payload> {
|
||||
/// Returns an empty Fulu block to be used during genesis.
|
||||
fn empty(spec: &ChainSpec) -> Self {
|
||||
|
||||
@@ -191,7 +191,6 @@ pub struct ChainSpec {
|
||||
pub max_pending_partials_per_withdrawals_sweep: u64,
|
||||
pub min_per_epoch_churn_limit_electra: u64,
|
||||
pub max_per_epoch_activation_exit_churn_limit: u64,
|
||||
pub max_blobs_per_block_electra: u64,
|
||||
|
||||
/*
|
||||
* Fulu hard fork params
|
||||
@@ -205,10 +204,11 @@ pub struct ChainSpec {
|
||||
* DAS params
|
||||
*/
|
||||
pub eip7594_fork_epoch: Option<Epoch>,
|
||||
pub custody_requirement: u64,
|
||||
pub number_of_columns: u64,
|
||||
pub number_of_custody_groups: u64,
|
||||
pub data_column_sidecar_subnet_count: u64,
|
||||
pub number_of_columns: usize,
|
||||
pub samples_per_slot: u64,
|
||||
pub custody_requirement: u64,
|
||||
|
||||
/*
|
||||
* Networking
|
||||
@@ -238,7 +238,14 @@ pub struct ChainSpec {
|
||||
pub max_request_data_column_sidecars: u64,
|
||||
pub min_epochs_for_blob_sidecars_requests: u64,
|
||||
pub blob_sidecar_subnet_count: u64,
|
||||
max_blobs_per_block: u64,
|
||||
pub max_blobs_per_block: u64,
|
||||
|
||||
/*
|
||||
* Networking Electra
|
||||
*/
|
||||
max_blobs_per_block_electra: u64,
|
||||
pub blob_sidecar_subnet_count_electra: u64,
|
||||
pub max_request_blob_sidecars_electra: u64,
|
||||
|
||||
/*
|
||||
* Networking Derived
|
||||
@@ -618,6 +625,14 @@ impl ChainSpec {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_request_blob_sidecars(&self, fork_name: ForkName) -> usize {
|
||||
if fork_name.electra_enabled() {
|
||||
self.max_request_blob_sidecars_electra as usize
|
||||
} else {
|
||||
self.max_request_blob_sidecars as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the value of `MAX_BLOBS_PER_BLOCK` appropriate for the fork at `epoch`.
|
||||
pub fn max_blobs_per_block(&self, epoch: Epoch) -> u64 {
|
||||
self.max_blobs_per_block_by_fork(self.fork_name_at_epoch(epoch))
|
||||
@@ -632,10 +647,33 @@ impl ChainSpec {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data_columns_per_subnet(&self) -> usize {
|
||||
/// Returns the number of data columns per custody group.
|
||||
pub fn data_columns_per_group(&self) -> u64 {
|
||||
self.number_of_columns
|
||||
.safe_div(self.data_column_sidecar_subnet_count as usize)
|
||||
.expect("Subnet count must be greater than 0")
|
||||
.safe_div(self.number_of_custody_groups)
|
||||
.expect("Custody group count must be greater than 0")
|
||||
}
|
||||
|
||||
/// Returns the number of column sidecars to sample per slot.
|
||||
pub fn sampling_size(&self, custody_group_count: u64) -> Result<u64, String> {
|
||||
let columns_per_custody_group = self
|
||||
.number_of_columns
|
||||
.safe_div(self.number_of_custody_groups)
|
||||
.map_err(|_| "number_of_custody_groups must be greater than 0")?;
|
||||
|
||||
let custody_column_count = columns_per_custody_group
|
||||
.safe_mul(custody_group_count)
|
||||
.map_err(|_| "Computing sampling size should not overflow")?;
|
||||
|
||||
Ok(std::cmp::max(custody_column_count, self.samples_per_slot))
|
||||
}
|
||||
|
||||
pub fn custody_group_count(&self, is_supernode: bool) -> u64 {
|
||||
if is_supernode {
|
||||
self.number_of_custody_groups
|
||||
} else {
|
||||
self.custody_requirement
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
|
||||
@@ -830,7 +868,6 @@ impl ChainSpec {
|
||||
u64::checked_pow(2, 8)?.checked_mul(u64::checked_pow(10, 9)?)
|
||||
})
|
||||
.expect("calculation does not overflow"),
|
||||
max_blobs_per_block_electra: default_max_blobs_per_block_electra(),
|
||||
|
||||
/*
|
||||
* Fulu hard fork params
|
||||
@@ -843,10 +880,11 @@ impl ChainSpec {
|
||||
* DAS params
|
||||
*/
|
||||
eip7594_fork_epoch: None,
|
||||
custody_requirement: 4,
|
||||
data_column_sidecar_subnet_count: 128,
|
||||
number_of_columns: 128,
|
||||
number_of_custody_groups: 128,
|
||||
data_column_sidecar_subnet_count: 128,
|
||||
samples_per_slot: 8,
|
||||
custody_requirement: 4,
|
||||
|
||||
/*
|
||||
* Network specific
|
||||
@@ -886,6 +924,13 @@ impl ChainSpec {
|
||||
max_blobs_by_root_request: default_max_blobs_by_root_request(),
|
||||
max_data_columns_by_root_request: default_data_columns_by_root_request(),
|
||||
|
||||
/*
|
||||
* Networking Electra specific
|
||||
*/
|
||||
max_blobs_per_block_electra: default_max_blobs_per_block_electra(),
|
||||
blob_sidecar_subnet_count_electra: default_blob_sidecar_subnet_count_electra(),
|
||||
max_request_blob_sidecars_electra: default_max_request_blob_sidecars_electra(),
|
||||
|
||||
/*
|
||||
* Application specific
|
||||
*/
|
||||
@@ -1161,7 +1206,6 @@ impl ChainSpec {
|
||||
u64::checked_pow(2, 8)?.checked_mul(u64::checked_pow(10, 9)?)
|
||||
})
|
||||
.expect("calculation does not overflow"),
|
||||
max_blobs_per_block_electra: default_max_blobs_per_block_electra(),
|
||||
|
||||
/*
|
||||
* Fulu hard fork params
|
||||
@@ -1174,10 +1218,12 @@ impl ChainSpec {
|
||||
* DAS params
|
||||
*/
|
||||
eip7594_fork_epoch: None,
|
||||
custody_requirement: 4,
|
||||
data_column_sidecar_subnet_count: 128,
|
||||
number_of_columns: 128,
|
||||
number_of_custody_groups: 128,
|
||||
data_column_sidecar_subnet_count: 128,
|
||||
samples_per_slot: 8,
|
||||
custody_requirement: 4,
|
||||
|
||||
/*
|
||||
* Network specific
|
||||
*/
|
||||
@@ -1216,6 +1262,13 @@ impl ChainSpec {
|
||||
max_blobs_by_root_request: default_max_blobs_by_root_request(),
|
||||
max_data_columns_by_root_request: default_data_columns_by_root_request(),
|
||||
|
||||
/*
|
||||
* Networking Electra specific
|
||||
*/
|
||||
max_blobs_per_block_electra: default_max_blobs_per_block_electra(),
|
||||
blob_sidecar_subnet_count_electra: default_blob_sidecar_subnet_count_electra(),
|
||||
max_request_blob_sidecars_electra: default_max_request_blob_sidecars_electra(),
|
||||
|
||||
/*
|
||||
* Application specific
|
||||
*/
|
||||
@@ -1421,19 +1474,28 @@ pub struct Config {
|
||||
#[serde(default = "default_max_blobs_per_block_electra")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
max_blobs_per_block_electra: u64,
|
||||
#[serde(default = "default_blob_sidecar_subnet_count_electra")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub blob_sidecar_subnet_count_electra: u64,
|
||||
#[serde(default = "default_max_request_blob_sidecars_electra")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
max_request_blob_sidecars_electra: u64,
|
||||
|
||||
#[serde(default = "default_custody_requirement")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
custody_requirement: u64,
|
||||
#[serde(default = "default_data_column_sidecar_subnet_count")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
data_column_sidecar_subnet_count: u64,
|
||||
#[serde(default = "default_number_of_columns")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
number_of_columns: u64,
|
||||
#[serde(default = "default_number_of_custody_groups")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
number_of_custody_groups: u64,
|
||||
#[serde(default = "default_data_column_sidecar_subnet_count")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
data_column_sidecar_subnet_count: u64,
|
||||
#[serde(default = "default_samples_per_slot")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
samples_per_slot: u64,
|
||||
#[serde(default = "default_custody_requirement")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
custody_requirement: u64,
|
||||
}
|
||||
|
||||
fn default_bellatrix_fork_version() -> [u8; 4] {
|
||||
@@ -1555,6 +1617,14 @@ const fn default_max_blobs_per_block() -> u64 {
|
||||
6
|
||||
}
|
||||
|
||||
const fn default_blob_sidecar_subnet_count_electra() -> u64 {
|
||||
9
|
||||
}
|
||||
|
||||
const fn default_max_request_blob_sidecars_electra() -> u64 {
|
||||
1152
|
||||
}
|
||||
|
||||
const fn default_min_per_epoch_churn_limit_electra() -> u64 {
|
||||
128_000_000_000
|
||||
}
|
||||
@@ -1587,6 +1657,10 @@ const fn default_number_of_columns() -> u64 {
|
||||
128
|
||||
}
|
||||
|
||||
const fn default_number_of_custody_groups() -> u64 {
|
||||
128
|
||||
}
|
||||
|
||||
const fn default_samples_per_slot() -> u64 {
|
||||
8
|
||||
}
|
||||
@@ -1787,11 +1861,14 @@ impl Config {
|
||||
max_per_epoch_activation_exit_churn_limit: spec
|
||||
.max_per_epoch_activation_exit_churn_limit,
|
||||
max_blobs_per_block_electra: spec.max_blobs_per_block_electra,
|
||||
blob_sidecar_subnet_count_electra: spec.blob_sidecar_subnet_count_electra,
|
||||
max_request_blob_sidecars_electra: spec.max_request_blob_sidecars_electra,
|
||||
|
||||
custody_requirement: spec.custody_requirement,
|
||||
number_of_columns: spec.number_of_columns,
|
||||
number_of_custody_groups: spec.number_of_custody_groups,
|
||||
data_column_sidecar_subnet_count: spec.data_column_sidecar_subnet_count,
|
||||
number_of_columns: spec.number_of_columns as u64,
|
||||
samples_per_slot: spec.samples_per_slot,
|
||||
custody_requirement: spec.custody_requirement,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1865,10 +1942,13 @@ impl Config {
|
||||
min_per_epoch_churn_limit_electra,
|
||||
max_per_epoch_activation_exit_churn_limit,
|
||||
max_blobs_per_block_electra,
|
||||
custody_requirement,
|
||||
data_column_sidecar_subnet_count,
|
||||
blob_sidecar_subnet_count_electra,
|
||||
max_request_blob_sidecars_electra,
|
||||
number_of_columns,
|
||||
number_of_custody_groups,
|
||||
data_column_sidecar_subnet_count,
|
||||
samples_per_slot,
|
||||
custody_requirement,
|
||||
} = self;
|
||||
|
||||
if preset_base != E::spec_name().to_string().as_str() {
|
||||
@@ -1935,6 +2015,8 @@ impl Config {
|
||||
min_per_epoch_churn_limit_electra,
|
||||
max_per_epoch_activation_exit_churn_limit,
|
||||
max_blobs_per_block_electra,
|
||||
max_request_blob_sidecars_electra,
|
||||
blob_sidecar_subnet_count_electra,
|
||||
|
||||
// We need to re-derive any values that might have changed in the config.
|
||||
max_blocks_by_root_request: max_blocks_by_root_request_common(max_request_blocks),
|
||||
@@ -1946,10 +2028,11 @@ impl Config {
|
||||
max_request_data_column_sidecars,
|
||||
),
|
||||
|
||||
custody_requirement,
|
||||
number_of_columns,
|
||||
number_of_custody_groups,
|
||||
data_column_sidecar_subnet_count,
|
||||
number_of_columns: number_of_columns as usize,
|
||||
samples_per_slot,
|
||||
custody_requirement,
|
||||
|
||||
..chain_spec.clone()
|
||||
})
|
||||
|
||||
142
consensus/types/src/data_column_custody_group.rs
Normal file
142
consensus/types/src/data_column_custody_group.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use crate::{ChainSpec, ColumnIndex, DataColumnSubnetId};
|
||||
use alloy_primitives::U256;
|
||||
use itertools::Itertools;
|
||||
use maplit::hashset;
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub type CustodyIndex = u64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DataColumnCustodyGroupError {
|
||||
InvalidCustodyGroup(CustodyIndex),
|
||||
InvalidCustodyGroupCount(u64),
|
||||
ArithError(ArithError),
|
||||
}
|
||||
|
||||
/// The `get_custody_groups` function is used to determine the custody groups that a node is
|
||||
/// assigned to.
|
||||
///
|
||||
/// spec: https://github.com/ethereum/consensus-specs/blob/8e0d0d48e81d6c7c5a8253ab61340f5ea5bac66a/specs/fulu/das-core.md#get_custody_groups
|
||||
pub fn get_custody_groups(
|
||||
raw_node_id: [u8; 32],
|
||||
custody_group_count: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<HashSet<CustodyIndex>, DataColumnCustodyGroupError> {
|
||||
if custody_group_count > spec.number_of_custody_groups {
|
||||
return Err(DataColumnCustodyGroupError::InvalidCustodyGroupCount(
|
||||
custody_group_count,
|
||||
));
|
||||
}
|
||||
|
||||
let mut custody_groups: HashSet<u64> = hashset![];
|
||||
let mut current_id = U256::from_be_slice(&raw_node_id);
|
||||
while custody_groups.len() < custody_group_count as usize {
|
||||
let mut node_id_bytes = [0u8; 32];
|
||||
node_id_bytes.copy_from_slice(current_id.as_le_slice());
|
||||
let hash = ethereum_hashing::hash_fixed(&node_id_bytes);
|
||||
let hash_prefix: [u8; 8] = hash[0..8]
|
||||
.try_into()
|
||||
.expect("hash_fixed produces a 32 byte array");
|
||||
let hash_prefix_u64 = u64::from_le_bytes(hash_prefix);
|
||||
let custody_group = hash_prefix_u64
|
||||
.safe_rem(spec.number_of_custody_groups)
|
||||
.expect("spec.number_of_custody_groups must not be zero");
|
||||
custody_groups.insert(custody_group);
|
||||
|
||||
current_id = current_id.wrapping_add(U256::from(1u64));
|
||||
}
|
||||
|
||||
Ok(custody_groups)
|
||||
}
|
||||
|
||||
/// Returns the columns that are associated with a given custody group.
|
||||
///
|
||||
/// spec: https://github.com/ethereum/consensus-specs/blob/8e0d0d48e81d6c7c5a8253ab61340f5ea5bac66a/specs/fulu/das-core.md#compute_columns_for_custody_group
|
||||
pub fn compute_columns_for_custody_group(
|
||||
custody_group: CustodyIndex,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<impl Iterator<Item = ColumnIndex>, DataColumnCustodyGroupError> {
|
||||
let number_of_custody_groups = spec.number_of_custody_groups;
|
||||
if custody_group >= number_of_custody_groups {
|
||||
return Err(DataColumnCustodyGroupError::InvalidCustodyGroup(
|
||||
custody_group,
|
||||
));
|
||||
}
|
||||
|
||||
let mut columns = Vec::new();
|
||||
for i in 0..spec.data_columns_per_group() {
|
||||
let column = number_of_custody_groups
|
||||
.safe_mul(i)
|
||||
.and_then(|v| v.safe_add(custody_group))
|
||||
.map_err(DataColumnCustodyGroupError::ArithError)?;
|
||||
columns.push(column);
|
||||
}
|
||||
|
||||
Ok(columns.into_iter())
|
||||
}
|
||||
|
||||
pub fn compute_subnets_for_node(
|
||||
raw_node_id: [u8; 32],
|
||||
custody_group_count: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<HashSet<DataColumnSubnetId>, DataColumnCustodyGroupError> {
|
||||
let custody_groups = get_custody_groups(raw_node_id, custody_group_count, spec)?;
|
||||
let mut subnets = HashSet::new();
|
||||
|
||||
for custody_group in custody_groups {
|
||||
let custody_group_subnets = compute_subnets_from_custody_group(custody_group, spec)?;
|
||||
subnets.extend(custody_group_subnets);
|
||||
}
|
||||
|
||||
Ok(subnets)
|
||||
}
|
||||
|
||||
/// Returns the subnets that are associated with a given custody group.
|
||||
pub fn compute_subnets_from_custody_group(
|
||||
custody_group: CustodyIndex,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<impl Iterator<Item = DataColumnSubnetId> + '_, DataColumnCustodyGroupError> {
|
||||
let result = compute_columns_for_custody_group(custody_group, spec)?
|
||||
.map(|column_index| DataColumnSubnetId::from_column_index(column_index, spec))
|
||||
.unique();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_compute_columns_for_custody_group() {
|
||||
let mut spec = ChainSpec::mainnet();
|
||||
spec.number_of_custody_groups = 64;
|
||||
spec.number_of_columns = 128;
|
||||
let columns_per_custody_group = spec.number_of_columns / spec.number_of_custody_groups;
|
||||
|
||||
for custody_group in 0..spec.number_of_custody_groups {
|
||||
let columns = compute_columns_for_custody_group(custody_group, &spec)
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(columns.len(), columns_per_custody_group as usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_subnets_from_custody_group() {
|
||||
let mut spec = ChainSpec::mainnet();
|
||||
spec.number_of_custody_groups = 64;
|
||||
spec.number_of_columns = 256;
|
||||
spec.data_column_sidecar_subnet_count = 128;
|
||||
|
||||
let subnets_per_custody_group =
|
||||
spec.data_column_sidecar_subnet_count / spec.number_of_custody_groups;
|
||||
|
||||
for custody_group in 0..spec.number_of_custody_groups {
|
||||
let subnets = compute_subnets_from_custody_group(custody_group, &spec)
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(subnets.len(), subnets_per_custody_group as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
//! Identifies each data column subnet by an integer identifier.
|
||||
use crate::data_column_sidecar::ColumnIndex;
|
||||
use crate::{ChainSpec, EthSpec};
|
||||
use alloy_primitives::U256;
|
||||
use itertools::Itertools;
|
||||
use crate::ChainSpec;
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{self, Display};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
@@ -18,76 +15,14 @@ impl DataColumnSubnetId {
|
||||
id.into()
|
||||
}
|
||||
|
||||
pub fn from_column_index<E: EthSpec>(column_index: usize, spec: &ChainSpec) -> Self {
|
||||
(column_index
|
||||
.safe_rem(spec.data_column_sidecar_subnet_count as usize)
|
||||
pub fn from_column_index(column_index: ColumnIndex, spec: &ChainSpec) -> Self {
|
||||
column_index
|
||||
.safe_rem(spec.data_column_sidecar_subnet_count)
|
||||
.expect(
|
||||
"data_column_sidecar_subnet_count should never be zero if this function is called",
|
||||
) as u64)
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
pub fn columns<E: EthSpec>(&self, spec: &ChainSpec) -> impl Iterator<Item = ColumnIndex> {
|
||||
let subnet = self.0;
|
||||
let data_column_sidecar_subnet = spec.data_column_sidecar_subnet_count;
|
||||
let columns_per_subnet = spec.data_columns_per_subnet() as u64;
|
||||
(0..columns_per_subnet).map(move |i| data_column_sidecar_subnet * i + subnet)
|
||||
}
|
||||
|
||||
/// Compute required subnets to subscribe to given the node id.
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
pub fn compute_custody_subnets<E: EthSpec>(
|
||||
raw_node_id: [u8; 32],
|
||||
custody_subnet_count: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<impl Iterator<Item = DataColumnSubnetId>, Error> {
|
||||
if custody_subnet_count > spec.data_column_sidecar_subnet_count {
|
||||
return Err(Error::InvalidCustodySubnetCount(custody_subnet_count));
|
||||
}
|
||||
|
||||
let mut subnets: HashSet<u64> = HashSet::new();
|
||||
let mut current_id = U256::from_be_slice(&raw_node_id);
|
||||
while (subnets.len() as u64) < custody_subnet_count {
|
||||
let mut node_id_bytes = [0u8; 32];
|
||||
node_id_bytes.copy_from_slice(current_id.as_le_slice());
|
||||
let hash = ethereum_hashing::hash_fixed(&node_id_bytes);
|
||||
let hash_prefix: [u8; 8] = hash[0..8]
|
||||
.try_into()
|
||||
.expect("hash_fixed produces a 32 byte array");
|
||||
let hash_prefix_u64 = u64::from_le_bytes(hash_prefix);
|
||||
let subnet = hash_prefix_u64 % spec.data_column_sidecar_subnet_count;
|
||||
|
||||
if !subnets.contains(&subnet) {
|
||||
subnets.insert(subnet);
|
||||
}
|
||||
|
||||
if current_id == U256::MAX {
|
||||
current_id = U256::ZERO
|
||||
}
|
||||
current_id += U256::from(1u64)
|
||||
}
|
||||
Ok(subnets.into_iter().map(DataColumnSubnetId::new))
|
||||
}
|
||||
|
||||
/// Compute the custody subnets for a given node id with the default `custody_requirement`.
|
||||
/// This operation should be infallable, and empty iterator is returned if it fails unexpectedly.
|
||||
pub fn compute_custody_requirement_subnets<E: EthSpec>(
|
||||
node_id: [u8; 32],
|
||||
spec: &ChainSpec,
|
||||
) -> impl Iterator<Item = DataColumnSubnetId> {
|
||||
Self::compute_custody_subnets::<E>(node_id, spec.custody_requirement, spec)
|
||||
.expect("should compute default custody subnets")
|
||||
}
|
||||
|
||||
pub fn compute_custody_columns<E: EthSpec>(
|
||||
raw_node_id: [u8; 32],
|
||||
custody_subnet_count: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<impl Iterator<Item = ColumnIndex>, Error> {
|
||||
Self::compute_custody_subnets::<E>(raw_node_id, custody_subnet_count, spec)
|
||||
.map(|subnet| subnet.flat_map(|subnet| subnet.columns::<E>(spec)).sorted())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DataColumnSubnetId {
|
||||
@@ -139,88 +74,3 @@ impl From<ArithError> for Error {
|
||||
Error::ArithError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::data_column_subnet_id::DataColumnSubnetId;
|
||||
use crate::MainnetEthSpec;
|
||||
use crate::Uint256;
|
||||
use crate::{EthSpec, GnosisEthSpec, MinimalEthSpec};
|
||||
|
||||
type E = MainnetEthSpec;
|
||||
|
||||
#[test]
|
||||
fn test_compute_subnets_for_data_column() {
|
||||
let spec = E::default_spec();
|
||||
let node_ids = [
|
||||
"0",
|
||||
"88752428858350697756262172400162263450541348766581994718383409852729519486397",
|
||||
"18732750322395381632951253735273868184515463718109267674920115648614659369468",
|
||||
"27726842142488109545414954493849224833670205008410190955613662332153332462900",
|
||||
"39755236029158558527862903296867805548949739810920318269566095185775868999998",
|
||||
"31899136003441886988955119620035330314647133604576220223892254902004850516297",
|
||||
"58579998103852084482416614330746509727562027284701078483890722833654510444626",
|
||||
"28248042035542126088870192155378394518950310811868093527036637864276176517397",
|
||||
"60930578857433095740782970114409273483106482059893286066493409689627770333527",
|
||||
"103822458477361691467064888613019442068586830412598673713899771287914656699997",
|
||||
]
|
||||
.into_iter()
|
||||
.map(|v| Uint256::from_str_radix(v, 10).unwrap().to_be_bytes::<32>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let custody_requirement = 4;
|
||||
for node_id in node_ids {
|
||||
let computed_subnets = DataColumnSubnetId::compute_custody_subnets::<E>(
|
||||
node_id,
|
||||
custody_requirement,
|
||||
&spec,
|
||||
)
|
||||
.unwrap();
|
||||
let computed_subnets: Vec<_> = computed_subnets.collect();
|
||||
|
||||
// the number of subnets is equal to the custody requirement
|
||||
assert_eq!(computed_subnets.len() as u64, custody_requirement);
|
||||
|
||||
let subnet_count = spec.data_column_sidecar_subnet_count;
|
||||
for subnet in computed_subnets {
|
||||
let columns: Vec<_> = subnet.columns::<E>(&spec).collect();
|
||||
// the number of columns is equal to the specified number of columns per subnet
|
||||
assert_eq!(columns.len(), spec.data_columns_per_subnet());
|
||||
|
||||
for pair in columns.windows(2) {
|
||||
// each successive column index is offset by the number of subnets
|
||||
assert_eq!(pair[1] - pair[0], subnet_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_custody_requirement_subnets_never_panics() {
|
||||
let node_id = [1u8; 32];
|
||||
test_compute_custody_requirement_subnets_with_spec::<MainnetEthSpec>(node_id);
|
||||
test_compute_custody_requirement_subnets_with_spec::<MinimalEthSpec>(node_id);
|
||||
test_compute_custody_requirement_subnets_with_spec::<GnosisEthSpec>(node_id);
|
||||
}
|
||||
|
||||
fn test_compute_custody_requirement_subnets_with_spec<E: EthSpec>(node_id: [u8; 32]) {
|
||||
let _ = DataColumnSubnetId::compute_custody_requirement_subnets::<E>(
|
||||
node_id,
|
||||
&E::default_spec(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_columns_subnet_conversion() {
|
||||
let spec = E::default_spec();
|
||||
for subnet in 0..spec.data_column_sidecar_subnet_count {
|
||||
let subnet_id = DataColumnSubnetId::new(subnet);
|
||||
for column_index in subnet_id.columns::<E>(&spec) {
|
||||
assert_eq!(
|
||||
subnet_id,
|
||||
DataColumnSubnetId::from_column_index::<E>(column_index as usize, &spec)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,58 +128,6 @@ impl<E: EthSpec> ExecutionPayload<E> {
|
||||
// Max size of variable length `transactions` field
|
||||
+ (E::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + E::max_bytes_per_transaction()))
|
||||
}
|
||||
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
/// Returns the maximum size of an execution payload.
|
||||
pub fn max_execution_payload_capella_size() -> usize {
|
||||
// Fixed part
|
||||
ExecutionPayloadCapella::<E>::default().as_ssz_bytes().len()
|
||||
// Max size of variable length `extra_data` field
|
||||
+ (E::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len())
|
||||
// Max size of variable length `transactions` field
|
||||
+ (E::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + E::max_bytes_per_transaction()))
|
||||
// Max size of variable length `withdrawals` field
|
||||
+ (E::max_withdrawals_per_payload() * <Withdrawal as Encode>::ssz_fixed_len())
|
||||
}
|
||||
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
/// Returns the maximum size of an execution payload.
|
||||
pub fn max_execution_payload_deneb_size() -> usize {
|
||||
// Fixed part
|
||||
ExecutionPayloadDeneb::<E>::default().as_ssz_bytes().len()
|
||||
// Max size of variable length `extra_data` field
|
||||
+ (E::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len())
|
||||
// Max size of variable length `transactions` field
|
||||
+ (E::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + E::max_bytes_per_transaction()))
|
||||
// Max size of variable length `withdrawals` field
|
||||
+ (E::max_withdrawals_per_payload() * <Withdrawal as Encode>::ssz_fixed_len())
|
||||
}
|
||||
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
/// Returns the maximum size of an execution payload.
|
||||
pub fn max_execution_payload_electra_size() -> usize {
|
||||
// Fixed part
|
||||
ExecutionPayloadElectra::<E>::default().as_ssz_bytes().len()
|
||||
// Max size of variable length `extra_data` field
|
||||
+ (E::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len())
|
||||
// Max size of variable length `transactions` field
|
||||
+ (E::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + E::max_bytes_per_transaction()))
|
||||
// Max size of variable length `withdrawals` field
|
||||
+ (E::max_withdrawals_per_payload() * <Withdrawal as Encode>::ssz_fixed_len())
|
||||
}
|
||||
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
/// Returns the maximum size of an execution payload.
|
||||
pub fn max_execution_payload_fulu_size() -> usize {
|
||||
// Fixed part
|
||||
ExecutionPayloadFulu::<E>::default().as_ssz_bytes().len()
|
||||
// Max size of variable length `extra_data` field
|
||||
+ (E::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len())
|
||||
// Max size of variable length `transactions` field
|
||||
+ (E::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + E::max_bytes_per_transaction()))
|
||||
// Max size of variable length `withdrawals` field
|
||||
+ (E::max_withdrawals_per_payload() * <Withdrawal as Encode>::ssz_fixed_len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> ForkVersionDeserialize for ExecutionPayload<E> {
|
||||
|
||||
@@ -104,6 +104,7 @@ pub mod slot_data;
|
||||
pub mod sqlite;
|
||||
|
||||
pub mod blob_sidecar;
|
||||
pub mod data_column_custody_group;
|
||||
pub mod data_column_sidecar;
|
||||
pub mod data_column_subnet_id;
|
||||
pub mod light_client_header;
|
||||
@@ -117,7 +118,7 @@ pub use crate::aggregate_and_proof::{
|
||||
};
|
||||
pub use crate::attestation::{
|
||||
Attestation, AttestationBase, AttestationElectra, AttestationRef, AttestationRefMut,
|
||||
Error as AttestationError,
|
||||
Error as AttestationError, SingleAttestation,
|
||||
};
|
||||
pub use crate::attestation_data::AttestationData;
|
||||
pub use crate::attestation_duty::AttestationDuty;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Identifies each shard by an integer identifier.
|
||||
use crate::SingleAttestation;
|
||||
use crate::{AttestationRef, ChainSpec, CommitteeIndex, EthSpec, Slot};
|
||||
use alloy_primitives::{bytes::Buf, U256};
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
@@ -57,6 +58,21 @@ impl SubnetId {
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the subnet for an attestation where each slot in the
|
||||
/// attestation epoch contains `committee_count_per_slot` committees.
|
||||
pub fn compute_subnet_for_single_attestation<E: EthSpec>(
|
||||
attestation: &SingleAttestation,
|
||||
committee_count_per_slot: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<SubnetId, ArithError> {
|
||||
Self::compute_subnet::<E>(
|
||||
attestation.data.slot,
|
||||
attestation.committee_index as u64,
|
||||
committee_count_per_slot,
|
||||
spec,
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the subnet for an attestation with `attestation.data.slot == slot` and
|
||||
/// `attestation.data.index == committee_index` where each slot in the attestation epoch
|
||||
/// contains `committee_count_at_slot` committees.
|
||||
|
||||
Reference in New Issue
Block a user