Superstruct AggregateAndProof (#5715)

* Upgrade `superstruct` to `0.8.0`

* superstruct `AggregateAndProof`
This commit is contained in:
ethDreamer
2024-05-06 10:09:22 -05:00
committed by GitHub
parent 7c6526d978
commit 19a9479234
28 changed files with 410 additions and 225 deletions

View File

@@ -425,7 +425,7 @@ where
E: EthSpec,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let slot = signed_aggregate_and_proof.message.aggregate.data().slot;
let slot = signed_aggregate_and_proof.message().aggregate().data().slot;
let domain = spec.get_domain(
slot.epoch(E::slots_per_epoch()),
@@ -434,8 +434,8 @@ where
genesis_validators_root,
);
let message = slot.signing_root(domain);
let signature = &signed_aggregate_and_proof.message.selection_proof;
let validator_index = signed_aggregate_and_proof.message.aggregator_index;
let signature = signed_aggregate_and_proof.message().selection_proof();
let validator_index = signed_aggregate_and_proof.message().aggregator_index();
Ok(SignatureSet::single_pubkey(
signature,
@@ -456,8 +456,8 @@ where
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let target_epoch = signed_aggregate_and_proof
.message
.aggregate
.message()
.aggregate()
.data()
.target
.epoch;
@@ -468,9 +468,9 @@ where
fork,
genesis_validators_root,
);
let message = signed_aggregate_and_proof.message.signing_root(domain);
let signature = &signed_aggregate_and_proof.signature;
let validator_index = signed_aggregate_and_proof.message.aggregator_index;
let message = signed_aggregate_and_proof.message().signing_root(domain);
let signature = signed_aggregate_and_proof.signature();
let validator_index = signed_aggregate_and_proof.message().aggregator_index();
Ok(SignatureSet::single_pubkey(
signature,

View File

@@ -1,41 +1,79 @@
use super::{Attestation, AttestationBase, AttestationElectra, AttestationRef};
use super::{
Attestation, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, SecretKey, SelectionProof,
Signature, SignedRoot,
ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, SecretKey, SelectionProof, Signature,
SignedRoot,
};
use crate::test_utils::TestRandom;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
/// A Validators aggregate attestation and selection proof.
///
/// Spec v0.12.1
#[derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
#[superstruct(
variants(Base, Electra),
variant_attributes(
derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
),
serde(bound = "E: EthSpec"),
arbitrary(bound = "E: EthSpec"),
),
ref_attributes(
derive(Debug, PartialEq, TreeHash, Serialize,),
serde(bound = "E: EthSpec"),
tree_hash(enum_behaviour = "transparent")
)
)]
#[serde(bound = "E: EthSpec")]
#[derive(
arbitrary::Arbitrary, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash,
)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
#[arbitrary(bound = "E: EthSpec")]
pub struct AggregateAndProof<E: EthSpec> {
/// The index of the validator that created the attestation.
#[serde(with = "serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub aggregator_index: u64,
/// The aggregate attestation.
#[superstruct(flatten)]
pub aggregate: Attestation<E>,
/// A proof provided by the validator that permits them to publish on the
/// `beacon_aggregate_and_proof` gossipsub topic.
pub selection_proof: Signature,
}
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),
}
}
}
impl<E: EthSpec> AggregateAndProof<E> {
/// Returns `true` if `validator_pubkey` signed over `self.aggregate.data.slot`.
pub fn aggregate(&self) -> AttestationRef<E> {
match self {
AggregateAndProof::Base(a) => AttestationRef::Base(&a.aggregate),
AggregateAndProof::Electra(a) => AttestationRef::Electra(&a.aggregate),
}
}
}
impl<E: EthSpec> AggregateAndProof<E> {
/// Produces a new `AggregateAndProof` with a `selection_proof` generated by signing
/// `aggregate.data.slot` with `secret_key`.
@@ -62,10 +100,17 @@ impl<E: EthSpec> AggregateAndProof<E> {
})
.into();
Self {
aggregator_index,
aggregate,
selection_proof,
match aggregate {
Attestation::Base(attestation) => Self::Base(AggregateAndProofBase {
aggregator_index,
aggregate: attestation,
selection_proof,
}),
Attestation::Electra(attestation) => Self::Electra(AggregateAndProofElectra {
aggregator_index,
aggregate: attestation,
selection_proof,
}),
}
}
@@ -77,16 +122,17 @@ impl<E: EthSpec> AggregateAndProof<E> {
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> bool {
let target_epoch = self.aggregate.data().slot.epoch(E::slots_per_epoch());
let target_epoch = self.aggregate().data().slot.epoch(E::slots_per_epoch());
let domain = spec.get_domain(
target_epoch,
Domain::SelectionProof,
fork,
genesis_validators_root,
);
let message = self.aggregate.data().slot.signing_root(domain);
self.selection_proof.verify(validator_pubkey, message)
let message = self.aggregate().data().slot.signing_root(domain);
self.selection_proof().verify(validator_pubkey, message)
}
}
impl<E: EthSpec> SignedRoot for AggregateAndProof<E> {}
impl<'a, E: EthSpec> SignedRoot for AggregateAndProofRef<'a, E> {}

View File

@@ -42,7 +42,8 @@ pub enum Error {
derivative(PartialEq, Hash(bound = "E: EthSpec")),
serde(bound = "E: EthSpec", deny_unknown_fields),
arbitrary(bound = "E: EthSpec"),
)
),
ref_attributes(derive(TreeHash), tree_hash(enum_behaviour = "transparent"))
)]
#[derive(
Debug,
@@ -123,22 +124,24 @@ impl<E: EthSpec> Attestation<E> {
/// 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) {
pub fn aggregate(&mut self, other: AttestationRef<E>) {
match self {
Attestation::Base(att) => {
debug_assert!(other.as_base().is_ok());
if let Ok(other) = other.as_base() {
att.aggregate(other)
Attestation::Base(att) => match other {
AttestationRef::Base(oth) => {
att.aggregate(oth);
}
}
Attestation::Electra(att) => {
debug_assert!(other.as_electra().is_ok());
if let Ok(other) = other.as_electra() {
att.aggregate(other)
AttestationRef::Electra(_) => {
debug_assert!(false, "Cannot aggregate base and electra attestations");
}
}
},
Attestation::Electra(att) => match other {
AttestationRef::Base(_) => {
debug_assert!(false, "Cannot aggregate base and electra attestations");
}
AttestationRef::Electra(oth) => {
att.aggregate(oth);
}
},
}
}
@@ -215,8 +218,22 @@ impl<E: EthSpec> Attestation<E> {
impl<'a, E: EthSpec> AttestationRef<'a, E> {
pub fn clone_as_attestation(self) -> Attestation<E> {
match self {
AttestationRef::Base(att) => Attestation::Base(att.clone()),
AttestationRef::Electra(att) => Attestation::Electra(att.clone()),
Self::Base(att) => Attestation::Base(att.clone()),
Self::Electra(att) => Attestation::Electra(att.clone()),
}
}
pub fn is_aggregation_bits_zero(self) -> bool {
match self {
Self::Base(att) => att.aggregation_bits.is_zero(),
Self::Electra(att) => att.aggregation_bits.is_zero(),
}
}
pub fn num_set_aggregation_bits(&self) -> usize {
match self {
Self::Base(att) => att.aggregation_bits.num_set_bits(),
Self::Electra(att) => att.aggregation_bits.num_set_bits(),
}
}
}

View File

@@ -39,15 +39,10 @@ use tree_hash_derive::TreeHash;
#[ssz(enum_behaviour = "transparent")]
#[tree_hash(enum_behaviour = "transparent")]
pub struct AttesterSlashing<E: EthSpec> {
// TODO(electra) change this to `#[superstruct(flatten)]` when 0.8 is out..
#[superstruct(only(Base), partial_getter(rename = "attestation_1_base"))]
pub attestation_1: IndexedAttestationBase<E>,
#[superstruct(only(Electra), partial_getter(rename = "attestation_1_electra"))]
pub attestation_1: IndexedAttestationElectra<E>,
#[superstruct(only(Base), partial_getter(rename = "attestation_2_base"))]
pub attestation_2: IndexedAttestationBase<E>,
#[superstruct(only(Electra), partial_getter(rename = "attestation_2_electra"))]
pub attestation_2: IndexedAttestationElectra<E>,
#[superstruct(flatten)]
pub attestation_1: IndexedAttestation<E>,
#[superstruct(flatten)]
pub attestation_2: IndexedAttestation<E>,
}
/// This is a copy of the `AttesterSlashing` enum but with `Encode` and `Decode` derived

View File

@@ -77,16 +77,13 @@ pub struct ExecutionPayloadHeader<E: EthSpec> {
pub block_hash: ExecutionBlockHash,
#[superstruct(getter(copy))]
pub transactions_root: Hash256,
#[superstruct(only(Capella, Deneb, Electra))]
#[superstruct(getter(copy))]
#[superstruct(only(Capella, Deneb, Electra), partial_getter(copy))]
pub withdrawals_root: Hash256,
#[superstruct(only(Deneb, Electra))]
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub blob_gas_used: u64,
#[superstruct(only(Deneb, Electra))]
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
#[serde(with = "serde_utils::quoted_u64")]
#[superstruct(getter(copy))]
pub excess_blob_gas: u64,
#[superstruct(only(Electra), partial_getter(copy))]
pub deposit_receipts_root: Hash256,

View File

@@ -114,7 +114,9 @@ pub mod runtime_var_list;
use ethereum_types::{H160, H256};
pub use crate::activation_queue::ActivationQueue;
pub use crate::aggregate_and_proof::AggregateAndProof;
pub use crate::aggregate_and_proof::{
AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef,
};
pub use crate::attestation::{
Attestation, AttestationBase, AttestationElectra, AttestationRef, AttestationRefMut,
Error as AttestationError,
@@ -218,7 +220,9 @@ pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
pub use crate::runtime_var_list::RuntimeVariableList;
pub use crate::selection_proof::SelectionProof;
pub use crate::shuffling_id::AttestationShufflingId;
pub use crate::signed_aggregate_and_proof::SignedAggregateAndProof;
pub use crate::signed_aggregate_and_proof::{
SignedAggregateAndProof, SignedAggregateAndProofBase, SignedAggregateAndProofElectra,
};
pub use crate::signed_beacon_block::{
ssz_tagged_signed_beacon_block, ssz_tagged_signed_beacon_block_arc, SignedBeaconBlock,
SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix,

View File

@@ -1,10 +1,14 @@
use super::{
AggregateAndProof, Attestation, ChainSpec, Domain, EthSpec, Fork, Hash256, SecretKey,
SelectionProof, Signature, SignedRoot,
AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef,
};
use super::{
Attestation, ChainSpec, Domain, EthSpec, Fork, Hash256, SecretKey, SelectionProof, Signature,
SignedRoot,
};
use crate::test_utils::TestRandom;
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
@@ -12,22 +16,36 @@ use tree_hash_derive::TreeHash;
/// gossipsub topic.
///
/// Spec v0.12.1
#[derive(
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
arbitrary::Arbitrary,
#[superstruct(
variants(Base, Electra),
variant_attributes(
derive(
arbitrary::Arbitrary,
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TestRandom,
TreeHash,
),
serde(bound = "E: EthSpec"),
arbitrary(bound = "E: EthSpec"),
)
)]
#[serde(bound = "E: EthSpec")]
#[derive(
arbitrary::Arbitrary, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, TreeHash,
)]
#[serde(untagged)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
#[serde(bound = "E: EthSpec", deny_unknown_fields)]
#[arbitrary(bound = "E: EthSpec")]
pub struct SignedAggregateAndProof<E: EthSpec> {
/// The `AggregateAndProof` that was signed.
#[superstruct(flatten)]
pub message: AggregateAndProof<E>,
/// The aggregate attestation.
pub signature: Signature,
@@ -57,7 +75,7 @@ impl<E: EthSpec> SignedAggregateAndProof<E> {
spec,
);
let target_epoch = message.aggregate.data().slot.epoch(E::slots_per_epoch());
let target_epoch = message.aggregate().data().slot.epoch(E::slots_per_epoch());
let domain = spec.get_domain(
target_epoch,
Domain::AggregateAndProof,
@@ -66,9 +84,28 @@ impl<E: EthSpec> SignedAggregateAndProof<E> {
);
let signing_message = message.signing_root(domain);
SignedAggregateAndProof {
message,
signature: secret_key.sign(signing_message),
match message {
AggregateAndProof::Base(message) => {
SignedAggregateAndProof::Base(SignedAggregateAndProofBase {
message,
signature: secret_key.sign(signing_message),
})
}
AggregateAndProof::Electra(message) => {
SignedAggregateAndProof::Electra(SignedAggregateAndProofElectra {
message,
signature: secret_key.sign(signing_message),
})
}
}
}
pub fn message(&self) -> AggregateAndProofRef<E> {
match self {
SignedAggregateAndProof::Base(message) => AggregateAndProofRef::Base(&message.message),
SignedAggregateAndProof::Electra(message) => {
AggregateAndProofRef::Electra(&message.message)
}
}
}
}