mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 18:32:42 +00:00
Use bulk verification for sync_aggregate signature (#2415)
## Proposed Changes Add the `sync_aggregate` from `BeaconBlock` to the bulk signature verifier for blocks. This necessitates a new signature set constructor for the sync aggregate, which is different from the others due to the use of [`eth2_fast_aggregate_verify`](https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.7/specs/altair/bls.md#eth2_fast_aggregate_verify) for sync aggregates, per [`process_sync_aggregate`](https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.7/specs/altair/beacon-chain.md#sync-aggregate-processing). I made the choice to return an optional signature set, with `None` representing the case where the signature is valid on account of being the point at infinity (requires no further checking). To "dogfood" the changes and prevent duplication, the consensus logic now uses the signature set approach as well whenever it is required to verify signatures (which should only be in testing AFAIK). The EF tests pass with the code as it exists currently, but failed before I adapted the `eth2_fast_aggregate_verify` changes (which is good). As a result of this change Altair block processing should be a little faster, and importantly, we will no longer accidentally verify signatures when replaying blocks, e.g. when replaying blocks from the database.
This commit is contained in:
@@ -11,7 +11,7 @@ use types::{
|
||||
DepositData, Domain, Epoch, EthSpec, Fork, Hash256, InconsistentFork, IndexedAttestation,
|
||||
ProposerSlashing, PublicKey, PublicKeyBytes, Signature, SignedAggregateAndProof,
|
||||
SignedBeaconBlock, SignedBeaconBlockHeader, SignedContributionAndProof, SignedRoot,
|
||||
SignedVoluntaryExit, SigningData, SyncAggregatorSelectionData, Unsigned,
|
||||
SignedVoluntaryExit, SigningData, Slot, SyncAggregate, SyncAggregatorSelectionData, Unsigned,
|
||||
};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -36,6 +36,8 @@ pub enum Error {
|
||||
/// The public keys supplied do not match the number of objects requiring keys. Block validity
|
||||
/// was not determined.
|
||||
MismatchedPublicKeyLen { pubkey_len: usize, other_len: usize },
|
||||
/// Pubkey decompression failed. The block is invalid.
|
||||
PublicKeyDecompressionFailed,
|
||||
/// The public key bytes stored in the `BeaconState` were not valid. This is a serious internal
|
||||
/// error.
|
||||
BadBlsBytes { validator_index: u64 },
|
||||
@@ -517,3 +519,73 @@ where
|
||||
|
||||
Ok(SignatureSet::single_pubkey(signature, pubkey, message))
|
||||
}
|
||||
|
||||
/// Signature set verifier for a block's `sync_aggregate` (Altair and later).
|
||||
///
|
||||
/// The `slot` should be the slot of the block that the sync aggregate is included in, which may be
|
||||
/// different from `state.slot()`. The `block_root` should be the block root that the sync aggregate
|
||||
/// signs over. It's passed in rather than extracted from the `state` because when verifying a batch
|
||||
/// of blocks the `state` will not yet have had the blocks applied.
|
||||
///
|
||||
/// Returns `Ok(None)` in the case where `sync_aggregate` has 0 signatures. The spec
|
||||
/// uses a separate function `eth2_fast_aggregate_verify` for this, but we can equivalently
|
||||
/// check the exceptional case eagerly and do a `fast_aggregate_verify` in the case where the
|
||||
/// check fails (by returning `Some(signature_set)`).
|
||||
pub fn sync_aggregate_signature_set<'a, T, D>(
|
||||
decompressor: D,
|
||||
sync_aggregate: &'a SyncAggregate<T>,
|
||||
slot: Slot,
|
||||
block_root: Hash256,
|
||||
state: &'a BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Option<SignatureSet<'a>>>
|
||||
where
|
||||
T: EthSpec,
|
||||
D: Fn(&'a PublicKeyBytes) -> Option<Cow<'a, PublicKey>>,
|
||||
{
|
||||
// Allow the point at infinity to count as a signature for 0 validators as per
|
||||
// `eth2_fast_aggregate_verify` from the spec.
|
||||
if sync_aggregate.sync_committee_bits.is_zero()
|
||||
&& sync_aggregate.sync_committee_signature.is_infinity()
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let committee_pubkeys = &state
|
||||
.get_built_sync_committee(slot.epoch(T::slots_per_epoch()), spec)?
|
||||
.pubkeys;
|
||||
|
||||
let participant_pubkeys = committee_pubkeys
|
||||
.iter()
|
||||
.zip(sync_aggregate.sync_committee_bits.iter())
|
||||
.filter_map(|(pubkey, bit)| {
|
||||
if bit {
|
||||
Some(decompressor(pubkey))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.ok_or(Error::PublicKeyDecompressionFailed)?;
|
||||
|
||||
let previous_slot = slot.saturating_sub(1u64);
|
||||
|
||||
let domain = spec.get_domain(
|
||||
previous_slot.epoch(T::slots_per_epoch()),
|
||||
Domain::SyncCommittee,
|
||||
&state.fork(),
|
||||
state.genesis_validators_root(),
|
||||
);
|
||||
|
||||
let message = SigningData {
|
||||
object_root: block_root,
|
||||
domain,
|
||||
}
|
||||
.tree_hash_root();
|
||||
|
||||
Ok(Some(SignatureSet::multiple_pubkeys(
|
||||
&sync_aggregate.sync_committee_signature,
|
||||
participant_pubkeys,
|
||||
message,
|
||||
)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user