Skip to content

Commit

Permalink
Batch verify aggregated attestation signatures (#7744)
Browse files Browse the repository at this point in the history
* First take. Got benchmark numbers

* Remove benchmark test

* Final clean up

* Failing to verify aggregator index should be reject
  • Loading branch information
terencechain committed Nov 10, 2020
1 parent be40e1a commit 1b9911c
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 53 deletions.
98 changes: 77 additions & 21 deletions beacon-chain/sync/validate_aggregate_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/traceutil"
Expand Down Expand Up @@ -131,21 +132,34 @@ func (s *Service) validateAggregatedAtt(ctx context.Context, signed *ethpb.Signe
return pubsub.ValidationReject
}

// Verify selection proof reflects to the right validator and signature is valid.
if err := validateSelection(ctx, bs, signed.Message.Aggregate.Data, signed.Message.AggregatorIndex, signed.Message.SelectionProof); err != nil {
// Verify selection proof reflects to the right validator.
selectionSigSet, err := validateSelectionIndex(ctx, bs, signed.Message.Aggregate.Data, signed.Message.AggregatorIndex, signed.Message.SelectionProof)
if err != nil {
traceutil.AnnotateError(span, errors.Wrapf(err, "Could not validate selection for validator %d", signed.Message.AggregatorIndex))
return pubsub.ValidationReject
}

// Verify the aggregator's signature is valid.
if err := validateAggregatorSignature(bs, signed); err != nil {
// Verify selection signature, aggregator signature and attestation signature are valid.
// We use batch verify here to save compute.
aggregatorSigSet, err := aggSigSet(bs, signed)
if err != nil {
traceutil.AnnotateError(span, errors.Wrapf(err, "Could not get aggregator sig set %d", signed.Message.AggregatorIndex))
return pubsub.ValidationIgnore
}
attSigSet, err := blocks.AttestationSignatureSet(ctx, bs, []*ethpb.Attestation{signed.Message.Aggregate})
if err != nil {
traceutil.AnnotateError(span, errors.Wrapf(err, "Could not verify aggregator signature %d", signed.Message.AggregatorIndex))
return pubsub.ValidationReject
return pubsub.ValidationIgnore
}

// Verify aggregated attestation has a valid signature.
if err := blocks.VerifyAttestationSignature(ctx, bs, signed.Message.Aggregate); err != nil {
traceutil.AnnotateError(span, err)
set := bls.NewSet()
set.Join(selectionSigSet).Join(aggregatorSigSet).Join(attSigSet)
valid, err := set.Verify()
if err != nil {
traceutil.AnnotateError(span, errors.Errorf("Could not join signature set"))
return pubsub.ValidationIgnore
}
if !valid {
traceutil.AnnotateError(span, errors.Errorf("Could not verify selection or aggregator or attestation signature"))
return pubsub.ValidationReject
}

Expand Down Expand Up @@ -210,32 +224,74 @@ func validateIndexInCommittee(ctx context.Context, bs *stateTrie.BeaconState, a
return nil
}

// This validates selection proof by validating it's from the correct validator index of the slot and selection
// proof is a valid signature.
func validateSelection(ctx context.Context, bs *stateTrie.BeaconState, data *ethpb.AttestationData, validatorIndex uint64, proof []byte) error {
_, span := trace.StartSpan(ctx, "sync.validateSelection")
// This validates selection proof by validating it's from the correct validator index of the slot.
// It does not verify the selection proof, it returns the signature set of selection proof which can be used for batch verify.
func validateSelectionIndex(ctx context.Context, bs *stateTrie.BeaconState, data *ethpb.AttestationData, validatorIndex uint64, proof []byte) (*bls.SignatureSet, error) {
_, span := trace.StartSpan(ctx, "sync.validateSelectionIndex")
defer span.End()

committee, err := helpers.BeaconCommitteeFromState(bs, data.Slot, data.CommitteeIndex)
if err != nil {
return err
return nil, err
}
aggregator, err := helpers.IsAggregator(uint64(len(committee)), proof)
if err != nil {
return err
return nil, err
}
if !aggregator {
return fmt.Errorf("validator is not an aggregator for slot %d", data.Slot)
return nil, fmt.Errorf("validator is not an aggregator for slot %d", data.Slot)
}

domain := params.BeaconConfig().DomainSelectionProof
epoch := helpers.SlotToEpoch(data.Slot)
return helpers.ComputeDomainVerifySigningRoot(bs, validatorIndex, epoch, data.Slot, domain, proof)

v, err := bs.ValidatorAtIndex(validatorIndex)
if err != nil {
return nil, err
}
publicKey, err := bls.PublicKeyFromBytes(v.PublicKey)
if err != nil {
return nil, err
}

d, err := helpers.Domain(bs.Fork(), epoch, domain, bs.GenesisValidatorRoot())
if err != nil {
return nil, err
}
root, err := helpers.ComputeSigningRoot(data.Slot, d)
if err != nil {
return nil, err
}
return &bls.SignatureSet{
Signatures: [][]byte{proof},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
}, nil
}

// This verifies aggregator signature over the signed aggregate and proof object.
func validateAggregatorSignature(s *stateTrie.BeaconState, a *ethpb.SignedAggregateAttestationAndProof) error {
return helpers.ComputeDomainVerifySigningRoot(s, a.Message.AggregatorIndex,
helpers.SlotToEpoch(a.Message.Aggregate.Data.Slot), a.Message, params.BeaconConfig().DomainAggregateAndProof, a.Signature)
// This returns aggregator signature set which can be used to batch verify.
func aggSigSet(s *stateTrie.BeaconState, a *ethpb.SignedAggregateAttestationAndProof) (*bls.SignatureSet, error) {
v, err := s.ValidatorAtIndex(a.Message.AggregatorIndex)
if err != nil {
return nil, err
}
publicKey, err := bls.PublicKeyFromBytes(v.PublicKey)
if err != nil {
return nil, err
}

epoch := helpers.SlotToEpoch(a.Message.Aggregate.Data.Slot)
d, err := helpers.Domain(s.Fork(), epoch, params.BeaconConfig().DomainAggregateAndProof, s.GenesisValidatorRoot())
if err != nil {
return nil, err
}
root, err := helpers.ComputeSigningRoot(a.Message, d)
if err != nil {
return nil, err
}
return &bls.SignatureSet{
Signatures: [][]byte{a.Signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
}, nil
}
34 changes: 2 additions & 32 deletions beacon-chain/sync/validate_aggregate_proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,39 +90,9 @@ func TestVerifySelection_NotAnAggregator(t *testing.T) {
Source: &ethpb.Checkpoint{Root: make([]byte, 32)},
}

_, err := validateSelectionIndex(ctx, beaconState, data, 0, sig.Marshal())
wanted := "validator is not an aggregator for slot"
assert.ErrorContains(t, wanted, validateSelection(ctx, beaconState, data, 0, sig.Marshal()))
}

func TestVerifySelection_BadSignature(t *testing.T) {
ctx := context.Background()
validators := uint64(256)
beaconState, privKeys := testutil.DeterministicGenesisState(t, validators)

sig := privKeys[0].Sign([]byte{'A'})
data := &ethpb.AttestationData{
BeaconBlockRoot: make([]byte, 32),
Target: &ethpb.Checkpoint{Root: make([]byte, 32)},
Source: &ethpb.Checkpoint{Root: make([]byte, 32)},
}

wanted := "signature did not verify"
assert.ErrorContains(t, wanted, validateSelection(ctx, beaconState, data, 0, sig.Marshal()))
}

func TestVerifySelection_CanVerify(t *testing.T) {
ctx := context.Background()
validators := uint64(256)
beaconState, privKeys := testutil.DeterministicGenesisState(t, validators)

data := &ethpb.AttestationData{
BeaconBlockRoot: make([]byte, 32),
Target: &ethpb.Checkpoint{Root: make([]byte, 32)},
Source: &ethpb.Checkpoint{Root: make([]byte, 32)},
}
sig, err := helpers.ComputeDomainAndSign(beaconState, 0, data.Slot, params.BeaconConfig().DomainSelectionProof, privKeys[0])
require.NoError(t, err)
require.NoError(t, validateSelection(ctx, beaconState, data, 0, sig))
assert.ErrorContains(t, wanted, err)
}

func TestValidateAggregateAndProof_NoBlock(t *testing.T) {
Expand Down

0 comments on commit 1b9911c

Please sign in to comment.