github.com/prysmaticlabs/prysm@v1.4.4/shared/attestationutil/attestation_utils.go (about)

     1  // Package attestationutil contains useful helpers for converting
     2  // attestations into indexed form.
     3  package attestationutil
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"fmt"
     9  	"sort"
    10  
    11  	"github.com/pkg/errors"
    12  	types "github.com/prysmaticlabs/eth2-types"
    13  	"github.com/prysmaticlabs/go-bitfield"
    14  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    15  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    16  	"github.com/prysmaticlabs/prysm/shared/bls"
    17  	"github.com/prysmaticlabs/prysm/shared/params"
    18  	"go.opencensus.io/trace"
    19  )
    20  
    21  // ConvertToIndexed converts attestation to (almost) indexed-verifiable form.
    22  //
    23  // Note about spec pseudocode definition. The state was used by get_attesting_indices to determine
    24  // the attestation committee. Now that we provide this as an argument, we no longer need to provide
    25  // a state.
    26  //
    27  // Spec pseudocode definition:
    28  //   def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation:
    29  //    """
    30  //    Return the indexed attestation corresponding to ``attestation``.
    31  //    """
    32  //    attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits)
    33  //
    34  //    return IndexedAttestation(
    35  //        attesting_indices=sorted(attesting_indices),
    36  //        data=attestation.data,
    37  //        signature=attestation.signature,
    38  //    )
    39  func ConvertToIndexed(ctx context.Context, attestation *ethpb.Attestation, committee []types.ValidatorIndex) (*ethpb.IndexedAttestation, error) {
    40  	ctx, span := trace.StartSpan(ctx, "attestationutil.ConvertToIndexed")
    41  	defer span.End()
    42  
    43  	attIndices, err := AttestingIndices(attestation.AggregationBits, committee)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	sort.Slice(attIndices, func(i, j int) bool {
    49  		return attIndices[i] < attIndices[j]
    50  	})
    51  	inAtt := &ethpb.IndexedAttestation{
    52  		Data:             attestation.Data,
    53  		Signature:        attestation.Signature,
    54  		AttestingIndices: attIndices,
    55  	}
    56  	return inAtt, err
    57  }
    58  
    59  // AttestingIndices returns the attesting participants indices from the attestation data. The
    60  // committee is provided as an argument rather than a imported implementation from the spec definition.
    61  // Having the committee as an argument allows for re-use of beacon committees when possible.
    62  //
    63  // Spec pseudocode definition:
    64  //   def get_attesting_indices(state: BeaconState,
    65  //                          data: AttestationData,
    66  //                          bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]:
    67  //    """
    68  //    Return the set of attesting indices corresponding to ``data`` and ``bits``.
    69  //    """
    70  //    committee = get_beacon_committee(state, data.slot, data.index)
    71  //    return set(index for i, index in enumerate(committee) if bits[i])
    72  func AttestingIndices(bf bitfield.Bitfield, committee []types.ValidatorIndex) ([]uint64, error) {
    73  	if bf.Len() != uint64(len(committee)) {
    74  		return nil, fmt.Errorf("bitfield length %d is not equal to committee length %d", bf.Len(), len(committee))
    75  	}
    76  	indices := make([]uint64, 0, bf.Count())
    77  	for _, idx := range bf.BitIndices() {
    78  		if idx < len(committee) {
    79  			indices = append(indices, uint64(committee[idx]))
    80  		}
    81  	}
    82  	return indices, nil
    83  }
    84  
    85  // VerifyIndexedAttestationSig this helper function performs the last part of the
    86  // spec indexed attestation validation starting at Verify aggregate signature
    87  // comment.
    88  //
    89  // Spec pseudocode definition:
    90  //   def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
    91  //    """
    92  //    Check if ``indexed_attestation`` is not empty, has sorted and unique indices and has a valid aggregate signature.
    93  //    """
    94  //    # Verify indices are sorted and unique
    95  //    indices = indexed_attestation.attesting_indices
    96  //    if len(indices) == 0 or not indices == sorted(set(indices)):
    97  //        return False
    98  //    # Verify aggregate signature
    99  //    pubkeys = [state.validators[i].pubkey for i in indices]
   100  //    domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
   101  //    signing_root = compute_signing_root(indexed_attestation.data, domain)
   102  //    return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature)
   103  func VerifyIndexedAttestationSig(ctx context.Context, indexedAtt *ethpb.IndexedAttestation, pubKeys []bls.PublicKey, domain []byte) error {
   104  	ctx, span := trace.StartSpan(ctx, "attestationutil.VerifyIndexedAttestationSig")
   105  	defer span.End()
   106  	indices := indexedAtt.AttestingIndices
   107  	messageHash, err := helpers.ComputeSigningRoot(indexedAtt.Data, domain)
   108  	if err != nil {
   109  		return errors.Wrap(err, "could not get signing root of object")
   110  	}
   111  
   112  	sig, err := bls.SignatureFromBytes(indexedAtt.Signature)
   113  	if err != nil {
   114  		return errors.Wrap(err, "could not convert bytes to signature")
   115  	}
   116  
   117  	voted := len(indices) > 0
   118  	if voted && !sig.FastAggregateVerify(pubKeys, messageHash) {
   119  		return helpers.ErrSigFailedToVerify
   120  	}
   121  	return nil
   122  }
   123  
   124  // IsValidAttestationIndices this helper function performs the first part of the
   125  // spec indexed attestation validation starting at Check if ``indexed_attestation``
   126  // comment and ends at Verify aggregate signature comment.
   127  //
   128  // Spec pseudocode definition:
   129  //  def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
   130  //    """
   131  //    Check if ``indexed_attestation`` is not empty, has sorted and unique indices and has a valid aggregate signature.
   132  //    """
   133  //    # Verify indices are sorted and unique
   134  //    indices = indexed_attestation.attesting_indices
   135  //    if len(indices) == 0 or not indices == sorted(set(indices)):
   136  //        return False
   137  //    # Verify aggregate signature
   138  //    pubkeys = [state.validators[i].pubkey for i in indices]
   139  //    domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
   140  //    signing_root = compute_signing_root(indexed_attestation.data, domain)
   141  //    return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature)
   142  func IsValidAttestationIndices(ctx context.Context, indexedAttestation *ethpb.IndexedAttestation) error {
   143  	ctx, span := trace.StartSpan(ctx, "attestationutil.IsValidAttestationIndices")
   144  	defer span.End()
   145  
   146  	if indexedAttestation == nil || indexedAttestation.Data == nil || indexedAttestation.Data.Target == nil || indexedAttestation.AttestingIndices == nil {
   147  		return errors.New("nil or missing indexed attestation data")
   148  	}
   149  	indices := indexedAttestation.AttestingIndices
   150  	if len(indices) == 0 {
   151  		return errors.New("expected non-empty attesting indices")
   152  	}
   153  	if uint64(len(indices)) > params.BeaconConfig().MaxValidatorsPerCommittee {
   154  		return fmt.Errorf("validator indices count exceeds MAX_VALIDATORS_PER_COMMITTEE, %d > %d", len(indices), params.BeaconConfig().MaxValidatorsPerCommittee)
   155  	}
   156  	for i := 1; i < len(indices); i++ {
   157  		if indices[i-1] >= indices[i] {
   158  			return errors.New("attesting indices is not uniquely sorted")
   159  		}
   160  	}
   161  	return nil
   162  }
   163  
   164  // AttDataIsEqual this function performs an equality check between 2 attestation data, if they're unequal, it will return false.
   165  func AttDataIsEqual(attData1, attData2 *ethpb.AttestationData) bool {
   166  	if attData1.Slot != attData2.Slot {
   167  		return false
   168  	}
   169  	if attData1.CommitteeIndex != attData2.CommitteeIndex {
   170  		return false
   171  	}
   172  	if !bytes.Equal(attData1.BeaconBlockRoot, attData2.BeaconBlockRoot) {
   173  		return false
   174  	}
   175  	if attData1.Source.Epoch != attData2.Source.Epoch {
   176  		return false
   177  	}
   178  	if !bytes.Equal(attData1.Source.Root, attData2.Source.Root) {
   179  		return false
   180  	}
   181  	if attData1.Target.Epoch != attData2.Target.Epoch {
   182  		return false
   183  	}
   184  	if !bytes.Equal(attData1.Target.Root, attData2.Target.Root) {
   185  		return false
   186  	}
   187  	return true
   188  }
   189  
   190  // CheckPointIsEqual performs an equality check between 2 check points, returns false if unequal.
   191  func CheckPointIsEqual(checkPt1, checkPt2 *ethpb.Checkpoint) bool {
   192  	if checkPt1.Epoch != checkPt2.Epoch {
   193  		return false
   194  	}
   195  	if !bytes.Equal(checkPt1.Root, checkPt2.Root) {
   196  		return false
   197  	}
   198  	return true
   199  }