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 := ðpb.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 }