github.com/prysmaticlabs/prysm@v1.4.4/shared/testutil/attestation.go (about) 1 package testutil 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 8 types "github.com/prysmaticlabs/eth2-types" 9 "github.com/prysmaticlabs/go-bitfield" 10 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 11 "github.com/prysmaticlabs/prysm/beacon-chain/core/state" 12 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 13 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 14 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 15 "github.com/prysmaticlabs/prysm/shared/bls" 16 "github.com/prysmaticlabs/prysm/shared/params" 17 "github.com/prysmaticlabs/prysm/shared/rand" 18 log "github.com/sirupsen/logrus" 19 ) 20 21 // NewAttestation creates an attestation block with minimum marshalable fields. 22 func NewAttestation() *ethpb.Attestation { 23 return ðpb.Attestation{ 24 AggregationBits: bitfield.Bitlist{0b1101}, 25 Data: ðpb.AttestationData{ 26 BeaconBlockRoot: make([]byte, 32), 27 Source: ðpb.Checkpoint{ 28 Root: make([]byte, 32), 29 }, 30 Target: ðpb.Checkpoint{ 31 Root: make([]byte, 32), 32 }, 33 }, 34 Signature: make([]byte, 96), 35 } 36 } 37 38 // GenerateAttestations creates attestations that are entirely valid, for all 39 // the committees of the current state slot. This function expects attestations 40 // requested to be cleanly divisible by committees per slot. If there is 1 committee 41 // in the slot, and numToGen is set to 4, then it will return 4 attestations 42 // for the same data with their aggregation bits split uniformly. 43 // 44 // If you request 4 attestations, but there are 8 committees, you will get 4 fully aggregated attestations. 45 func GenerateAttestations( 46 bState iface.BeaconState, privs []bls.SecretKey, numToGen uint64, slot types.Slot, randomRoot bool, 47 ) ([]*ethpb.Attestation, error) { 48 var attestations []*ethpb.Attestation 49 generateHeadState := false 50 bState = bState.Copy() 51 if slot > bState.Slot() { 52 // Going back a slot here so there's no inclusion delay issues. 53 slot-- 54 generateHeadState = true 55 } 56 currentEpoch := helpers.SlotToEpoch(slot) 57 58 targetRoot := make([]byte, 32) 59 var headRoot []byte 60 var err error 61 // Only calculate head state if its an attestation for the current slot or future slot. 62 if generateHeadState || slot == bState.Slot() { 63 pbState, err := v1.ProtobufBeaconState(bState.CloneInnerState()) 64 if err != nil { 65 return nil, err 66 } 67 genState, err := v1.InitializeFromProtoUnsafe(pbState) 68 if err != nil { 69 return nil, err 70 } 71 headState := iface.BeaconState(genState) 72 headState, err = state.ProcessSlots(context.Background(), headState, slot+1) 73 if err != nil { 74 return nil, err 75 } 76 headRoot, err = helpers.BlockRootAtSlot(headState, slot) 77 if err != nil { 78 return nil, err 79 } 80 targetRoot, err = helpers.BlockRoot(headState, currentEpoch) 81 if err != nil { 82 return nil, err 83 } 84 } else { 85 headRoot, err = helpers.BlockRootAtSlot(bState, slot) 86 if err != nil { 87 return nil, err 88 } 89 } 90 if randomRoot { 91 randGen := rand.NewDeterministicGenerator() 92 b := make([]byte, 32) 93 _, err := randGen.Read(b) 94 if err != nil { 95 return nil, err 96 } 97 headRoot = b 98 } 99 100 activeValidatorCount, err := helpers.ActiveValidatorCount(bState, currentEpoch) 101 if err != nil { 102 return nil, err 103 } 104 committeesPerSlot := helpers.SlotCommitteeCount(activeValidatorCount) 105 106 if numToGen < committeesPerSlot { 107 log.Printf( 108 "Warning: %d attestations requested is less than %d committees in current slot, not all validators will be attesting.", 109 numToGen, 110 committeesPerSlot, 111 ) 112 } else if numToGen > committeesPerSlot { 113 log.Printf( 114 "Warning: %d attestations requested are more than %d committees in current slot, attestations will not be perfectly efficient.", 115 numToGen, 116 committeesPerSlot, 117 ) 118 } 119 120 attsPerCommittee := math.Max(float64(numToGen/committeesPerSlot), 1) 121 if math.Trunc(attsPerCommittee) != attsPerCommittee { 122 return nil, fmt.Errorf( 123 "requested attestations %d must be easily divisible by committees in slot %d, calculated %f", 124 numToGen, 125 committeesPerSlot, 126 attsPerCommittee, 127 ) 128 } 129 130 domain, err := helpers.Domain(bState.Fork(), currentEpoch, params.BeaconConfig().DomainBeaconAttester, bState.GenesisValidatorRoot()) 131 if err != nil { 132 return nil, err 133 } 134 for c := types.CommitteeIndex(0); uint64(c) < committeesPerSlot && uint64(c) < numToGen; c++ { 135 committee, err := helpers.BeaconCommitteeFromState(bState, slot, c) 136 if err != nil { 137 return nil, err 138 } 139 140 attData := ðpb.AttestationData{ 141 Slot: slot, 142 CommitteeIndex: c, 143 BeaconBlockRoot: headRoot, 144 Source: bState.CurrentJustifiedCheckpoint(), 145 Target: ðpb.Checkpoint{ 146 Epoch: currentEpoch, 147 Root: targetRoot, 148 }, 149 } 150 151 dataRoot, err := helpers.ComputeSigningRoot(attData, domain) 152 if err != nil { 153 return nil, err 154 } 155 156 committeeSize := uint64(len(committee)) 157 bitsPerAtt := committeeSize / uint64(attsPerCommittee) 158 for i := uint64(0); i < committeeSize; i += bitsPerAtt { 159 aggregationBits := bitfield.NewBitlist(committeeSize) 160 var sigs []bls.Signature 161 for b := i; b < i+bitsPerAtt; b++ { 162 aggregationBits.SetBitAt(b, true) 163 sigs = append(sigs, privs[committee[b]].Sign(dataRoot[:])) 164 } 165 166 // bls.AggregateSignatures will return nil if sigs is 0. 167 if len(sigs) == 0 { 168 continue 169 } 170 171 att := ðpb.Attestation{ 172 Data: attData, 173 AggregationBits: aggregationBits, 174 Signature: bls.AggregateSignatures(sigs).Marshal(), 175 } 176 attestations = append(attestations, att) 177 } 178 } 179 return attestations, nil 180 } 181 182 // HydrateAttestation hydrates an attestation object with correct field length sizes 183 // to comply with fssz marshalling and unmarshalling rules. 184 func HydrateAttestation(a *ethpb.Attestation) *ethpb.Attestation { 185 if a.Signature == nil { 186 a.Signature = make([]byte, 96) 187 } 188 if a.AggregationBits == nil { 189 a.AggregationBits = make([]byte, 1) 190 } 191 if a.Data == nil { 192 a.Data = ðpb.AttestationData{} 193 } 194 a.Data = HydrateAttestationData(a.Data) 195 return a 196 } 197 198 // HydrateAttestationData hydrates an attestation data object with correct field length sizes 199 // to comply with fssz marshalling and unmarshalling rules. 200 func HydrateAttestationData(d *ethpb.AttestationData) *ethpb.AttestationData { 201 if d.BeaconBlockRoot == nil { 202 d.BeaconBlockRoot = make([]byte, 32) 203 } 204 if d.Target == nil { 205 d.Target = ðpb.Checkpoint{} 206 } 207 if d.Target.Root == nil { 208 d.Target.Root = make([]byte, 32) 209 } 210 if d.Source == nil { 211 d.Source = ðpb.Checkpoint{} 212 } 213 if d.Source.Root == nil { 214 d.Source.Root = make([]byte, 32) 215 } 216 return d 217 } 218 219 // HydrateIndexedAttestation hydrates an indexed attestation with correct field length sizes 220 // to comply with fssz marshalling and unmarshalling rules. 221 func HydrateIndexedAttestation(a *ethpb.IndexedAttestation) *ethpb.IndexedAttestation { 222 if a.Signature == nil { 223 a.Signature = make([]byte, 96) 224 } 225 if a.Data == nil { 226 a.Data = ðpb.AttestationData{} 227 } 228 a.Data = HydrateAttestationData(a.Data) 229 return a 230 }