diff --git a/anchor/signature_collector/src/lib.rs b/anchor/signature_collector/src/lib.rs index 24de51699..830982924 100644 --- a/anchor/signature_collector/src/lib.rs +++ b/anchor/signature_collector/src/lib.rs @@ -103,7 +103,7 @@ impl SignatureCollectorManager { self: &Arc, metadata: SignatureMetadata, requester: SignatureRequester, - signing_data: SigningData, + validator_signing_data: ValidatorSigningData, ) -> Result, CollectionError> { let Some(signer) = self.operator_id.get() else { return Err(CollectionError::OwnOperatorIdUnknown); @@ -114,8 +114,8 @@ impl SignatureCollectorManager { debug!( ?metadata, ?requester, - root=?signing_data.root, - index=?signing_data.index, + root=?validator_signing_data.root, + index=?validator_signing_data.index, "sign_and_collect called", ); @@ -125,8 +125,8 @@ impl SignatureCollectorManager { self.processor.permitless.send_immediate( move |drop_on_finish| { let sender = manager.get_or_spawn( - signing_data.root, - signing_data.index, + validator_signing_data.root, + validator_signing_data.index, cloned_metadata.slot, ); let _ = sender.send(CollectorMessage { @@ -144,21 +144,21 @@ impl SignatureCollectorManager { let manager = self.clone(); self.processor.urgent_consensus.send_blocking( move || { - trace!(root = ?signing_data.root, "Signing..."); + trace!(root = ?validator_signing_data.root, "Signing..."); // If we have no share, we can not actually sign the message, because we are running // in impostor mode. - let partial_signature = if let Some(share) = &signing_data.share { - share.sign(signing_data.root) + let partial_signature = if let Some(share) = &validator_signing_data.share { + share.sign(validator_signing_data.root) } else { Signature::empty() }; - trace!(root = ?signing_data.root, "Signed"); + trace!(root = ?validator_signing_data.root, "Signed"); let message = PartialSignatureMessage { partial_signature, - signing_root: signing_data.root, + signing_root: validator_signing_data.root, signer, - validator_index: signing_data.index, + validator_index: validator_signing_data.index, }; match requester { SignatureRequester::SingleValidator { pubkey } => { @@ -178,12 +178,13 @@ impl SignatureCollectorManager { } SignatureRequester::Committee { num_signatures_to_collect, + base_hash, } => { // We have to collect all signatures from the given validators. // To check this create or get an entry from the `committee_signatures` map. let mut entry = match manager .committee_signatures - .entry((signing_data.root, metadata.committee_id)) + .entry((base_hash, metadata.committee_id)) { Entry::Occupied(occupied) => occupied, Entry::Vacant(vacant) => vacant.insert_entry(CommitteeSignatures { @@ -224,7 +225,7 @@ impl SignatureCollectorManager { // Finally, make the local instance aware of the partial signature, if it is a real // signature. - if signing_data.share.is_some() { + if validator_signing_data.share.is_some() { let _ = manager.receive_partial_signature(message, metadata.slot); } }, @@ -391,11 +392,16 @@ pub enum SignatureRequester { Committee { /// The number of signatures we have to wait for. num_signatures_to_collect: usize, + /// A hash that identifies what we are signing. We wait with sending the message until we + /// have created enough signatures with this `base_hash`. We need this to differentiate + /// "groups" of signatures. We cannot use the signing root, as we need to group signatures + /// with differing signing roots. + base_hash: Hash256, }, } #[derive(Clone)] -pub struct SigningData { +pub struct ValidatorSigningData { pub root: Hash256, pub index: ValidatorIndex, pub share: Option, diff --git a/anchor/validator_store/src/lib.rs b/anchor/validator_store/src/lib.rs index 5df05f016..00e94e6f0 100644 --- a/anchor/validator_store/src/lib.rs +++ b/anchor/validator_store/src/lib.rs @@ -24,7 +24,8 @@ use qbft_manager::{ }; use safe_arith::{ArithError, SafeArith}; use signature_collector::{ - CollectionError, SignatureCollectorManager, SignatureMetadata, SignatureRequester, SigningData, + CollectionError, SignatureCollectorManager, SignatureMetadata, SignatureRequester, + ValidatorSigningData, }; use slashing_protection::{NotSafe, Safe, SlashingDatabase}; use slot_clock::SlotClock; @@ -32,8 +33,8 @@ use ssv_types::{ Cluster, CommitteeId, ValidatorIndex, ValidatorMetadata, consensus::{ BEACON_ROLE_AGGREGATOR, BEACON_ROLE_PROPOSER, BEACON_ROLE_SYNC_COMMITTEE_CONTRIBUTION, - BeaconVote, Contribution, ContributionWrapper, Contributions, ValidatorConsensusData, - ValidatorDuty, + BeaconVote, Contribution, ContributionWrapper, Contributions, QbftData, + ValidatorConsensusData, ValidatorDuty, }, msgid::Role, partial_sig::PartialSignatureKind, @@ -335,6 +336,7 @@ impl AnchorValidatorStore { &self, signature_kind: PartialSignatureKind, role: Role, + base_hash: Option, validator: InitializedValidator, signing_root: Hash256, slot: Slot, @@ -353,7 +355,7 @@ impl AnchorValidatorStore { committee_id, }; - let requester = if role == Role::Committee { + let requester = if let Some(base_hash) = base_hash { let metadata = self.get_slot_metadata(slot).await?; SignatureRequester::Committee { num_signatures_to_collect: self @@ -375,6 +377,7 @@ impl AnchorValidatorStore { .sum() }) .unwrap_or_default(), + base_hash, } } else { SignatureRequester::SingleValidator { @@ -382,7 +385,7 @@ impl AnchorValidatorStore { } }; - let signing_data = SigningData { + let signing_data = ValidatorSigningData { root: signing_root, index: validator .metadata @@ -514,6 +517,7 @@ impl AnchorValidatorStore { .collect_signature( PartialSignatureKind::PostConsensus, Role::Proposer, + None, self.validator(validator_pubkey)?, signing_root, header.slot, @@ -616,6 +620,7 @@ impl AnchorValidatorStore { .collect_signature( PartialSignatureKind::VoluntaryExit, Role::VoluntaryExit, + None, self.validator(validator_pubkey)?, signing_root, slot, @@ -835,6 +840,7 @@ impl ValidatorStore for AnchorValidatorStore { self.collect_signature( PartialSignatureKind::RandaoPartialSig, Role::Proposer, + None, self.validator(validator_pubkey)?, signing_root, self.slot_clock.now().ok_or(SpecificError::SlotClock)?, @@ -972,6 +978,7 @@ impl ValidatorStore for AnchorValidatorStore { Completed::TimedOut => return Err(Error::SpecificError(SpecificError::Timeout)), Completed::Success(data) => data, }; + let data_hash = data.hash(); attestation.data_mut().beacon_block_root = data.block_root; attestation.data_mut().source = data.source; attestation.data_mut().target = data.target; @@ -992,6 +999,7 @@ impl ValidatorStore for AnchorValidatorStore { .collect_signature( PartialSignatureKind::PostConsensus, Role::Committee, + Some(data_hash), validator, signing_root, attestation.data().slot, @@ -1036,6 +1044,7 @@ impl ValidatorStore for AnchorValidatorStore { .collect_signature( PartialSignatureKind::ValidatorRegistration, Role::ValidatorRegistration, + None, self.validator(validator_registration_data.pubkey)?, signing_root, validity_slot, @@ -1149,6 +1158,7 @@ impl ValidatorStore for AnchorValidatorStore { .collect_signature( PartialSignatureKind::PostConsensus, Role::Aggregator, + None, validator, signing_root, message.aggregate().get_slot(), @@ -1190,6 +1200,7 @@ impl ValidatorStore for AnchorValidatorStore { self.collect_signature( PartialSignatureKind::SelectionProofPartialSig, Role::Aggregator, + None, self.validator(validator_pubkey)?, signing_root, slot, @@ -1234,6 +1245,7 @@ impl ValidatorStore for AnchorValidatorStore { self.collect_signature( PartialSignatureKind::ContributionProofs, Role::SyncCommittee, + None, self.validator(*validator_pubkey)?, signing_root, slot, @@ -1294,6 +1306,7 @@ impl ValidatorStore for AnchorValidatorStore { .collect_signature( PartialSignatureKind::PostConsensus, Role::Committee, + Some(data.hash()), validator, signing_root, slot, @@ -1441,6 +1454,7 @@ impl ValidatorStore for AnchorValidatorStore { self.collect_signature( PartialSignatureKind::PostConsensus, Role::SyncCommittee, + None, validator, signing_root, slot,