mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-07 00:42:42 +00:00
Superstruct AggregateAndProof (#5715)
* Upgrade `superstruct` to `0.8.0` * superstruct `AggregateAndProof`
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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> {}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user