package helpers import ( "github.com/gogo/protobuf/proto" "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" "github.com/prysmaticlabs/prysm/shared/bls" ) var ( // ErrAttestationDataSlotNilState is returned when a nil state argument // is provided to AttestationDataSlot. ErrAttestationDataSlotNilState = errors.New("nil state provided for AttestationDataSlot") // ErrAttestationDataSlotNilData is returned when a nil attestation data // argument is provided to AttestationDataSlot. ErrAttestationDataSlotNilData = errors.New("nil data provided for AttestationDataSlot") // ErrAttestationAggregationBitsOverlap is returned when two attestations aggregation // bits overlap with each other. ErrAttestationAggregationBitsOverlap = errors.New("overlapping aggregation bits") ) // AggregateAttestations such that the minimal number of attestations are returned. // Note: this is currently a naive implementation to the order of O(n^2). func AggregateAttestations(atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) { if len(atts) <= 1 { return atts, nil } // Naive aggregation. O(n^2) time. for i, a := range atts { if i >= len(atts) { break } for j := i + 1; j < len(atts); j++ { b := atts[j] if !a.AggregationBits.Overlaps(b.AggregationBits) { var err error a, err = AggregateAttestation(a, b) if err != nil { return nil, err } // Delete b atts = append(atts[:j], atts[j+1:]...) j-- atts[i] = a } } } // Naive deduplication of identical aggregations. O(n^2) time. for i, a := range atts { for j := i + 1; j < len(atts); j++ { b := atts[j] if a.AggregationBits.Contains(b.AggregationBits) { // If b is fully contained in a, then b can be removed. atts = append(atts[:j], atts[j+1:]...) j-- } else if b.AggregationBits.Contains(a.AggregationBits) { // if a is fully contained in b, then a can be removed. atts = append(atts[:i], atts[i+1:]...) i-- break // Stop the inner loop, advance a. } } } return atts, nil } // BLS aggregate signature aliases for testing / benchmark substitution. These methods are // significantly more expensive than the inner logic of AggregateAttestations so they must be // substituted for benchmarks which analyze AggregateAttestations. var aggregateSignatures = bls.AggregateSignatures var signatureFromBytes = bls.SignatureFromBytes // AggregateAttestation aggregates attestations a1 and a2 together. func AggregateAttestation(a1 *ethpb.Attestation, a2 *ethpb.Attestation) (*ethpb.Attestation, error) { if a1.AggregationBits.Overlaps(a2.AggregationBits) { return nil, ErrAttestationAggregationBitsOverlap } baseAtt := proto.Clone(a1).(*ethpb.Attestation) newAtt := proto.Clone(a2).(*ethpb.Attestation) if newAtt.AggregationBits.Count() > baseAtt.AggregationBits.Count() { baseAtt, newAtt = newAtt, baseAtt } if baseAtt.AggregationBits.Contains(newAtt.AggregationBits) { return baseAtt, nil } newBits := baseAtt.AggregationBits.Or(newAtt.AggregationBits) newSig, err := signatureFromBytes(newAtt.Signature) if err != nil { return nil, err } baseSig, err := signatureFromBytes(baseAtt.Signature) if err != nil { return nil, err } aggregatedSig := aggregateSignatures([]*bls.Signature{baseSig, newSig}) baseAtt.Signature = aggregatedSig.Marshal() baseAtt.AggregationBits = newBits return baseAtt, nil }