2021-01-07 21:00:21 +00:00
|
|
|
package testutil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
|
|
|
|
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
|
|
|
"github.com/prysmaticlabs/go-bitfield"
|
|
|
|
"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/params"
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/rand"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewAttestation creates an attestation block with minimum marshalable fields.
|
|
|
|
func NewAttestation() *ethpb.Attestation {
|
|
|
|
return ðpb.Attestation{
|
|
|
|
AggregationBits: bitfield.Bitlist{0b1101},
|
|
|
|
Data: ðpb.AttestationData{
|
|
|
|
BeaconBlockRoot: make([]byte, 32),
|
|
|
|
Source: ðpb.Checkpoint{
|
|
|
|
Root: make([]byte, 32),
|
|
|
|
},
|
|
|
|
Target: ðpb.Checkpoint{
|
|
|
|
Root: make([]byte, 32),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Signature: make([]byte, 96),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenerateAttestations creates attestations that are entirely valid, for all
|
|
|
|
// the committees of the current state slot. This function expects attestations
|
|
|
|
// requested to be cleanly divisible by committees per slot. If there is 1 committee
|
|
|
|
// in the slot, and numToGen is set to 4, then it will return 4 attestations
|
|
|
|
// for the same data with their aggregation bits split uniformly.
|
|
|
|
//
|
|
|
|
// If you request 4 attestations, but there are 8 committees, you will get 4 fully aggregated attestations.
|
|
|
|
func GenerateAttestations(bState *stateTrie.BeaconState, privs []bls.SecretKey, numToGen, slot uint64, randomRoot bool) ([]*ethpb.Attestation, error) {
|
|
|
|
currentEpoch := helpers.SlotToEpoch(slot)
|
|
|
|
var attestations []*ethpb.Attestation
|
|
|
|
generateHeadState := false
|
|
|
|
bState = bState.Copy()
|
|
|
|
if slot > bState.Slot() {
|
|
|
|
// Going back a slot here so there's no inclusion delay issues.
|
|
|
|
slot--
|
|
|
|
generateHeadState = true
|
|
|
|
}
|
|
|
|
|
|
|
|
targetRoot := make([]byte, 32)
|
|
|
|
var headRoot []byte
|
|
|
|
var err error
|
|
|
|
// Only calculate head state if its an attestation for the current slot or future slot.
|
|
|
|
if generateHeadState || slot == bState.Slot() {
|
|
|
|
headState, err := stateTrie.InitializeFromProtoUnsafe(bState.CloneInnerState())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
headState, err = state.ProcessSlots(context.Background(), headState, slot+1)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
headRoot, err = helpers.BlockRootAtSlot(headState, slot)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
targetRoot, err = helpers.BlockRoot(headState, currentEpoch)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
headRoot, err = helpers.BlockRootAtSlot(bState, slot)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if randomRoot {
|
|
|
|
randGen := rand.NewDeterministicGenerator()
|
|
|
|
b := make([]byte, 32)
|
|
|
|
_, err := randGen.Read(b)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
headRoot = b
|
|
|
|
}
|
|
|
|
|
|
|
|
activeValidatorCount, err := helpers.ActiveValidatorCount(bState, currentEpoch)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
committeesPerSlot := helpers.SlotCommitteeCount(activeValidatorCount)
|
|
|
|
|
|
|
|
if numToGen < committeesPerSlot {
|
|
|
|
log.Printf(
|
|
|
|
"Warning: %d attestations requested is less than %d committees in current slot, not all validators will be attesting.",
|
|
|
|
numToGen,
|
|
|
|
committeesPerSlot,
|
|
|
|
)
|
|
|
|
} else if numToGen > committeesPerSlot {
|
|
|
|
log.Printf(
|
|
|
|
"Warning: %d attestations requested are more than %d committees in current slot, attestations will not be perfectly efficient.",
|
|
|
|
numToGen,
|
|
|
|
committeesPerSlot,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
attsPerCommittee := math.Max(float64(numToGen/committeesPerSlot), 1)
|
|
|
|
if math.Trunc(attsPerCommittee) != attsPerCommittee {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"requested attestations %d must be easily divisible by committees in slot %d, calculated %f",
|
|
|
|
numToGen,
|
|
|
|
committeesPerSlot,
|
|
|
|
attsPerCommittee,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
domain, err := helpers.Domain(bState.Fork(), currentEpoch, params.BeaconConfig().DomainBeaconAttester, bState.GenesisValidatorRoot())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for c := uint64(0); c < committeesPerSlot && c < numToGen; c++ {
|
|
|
|
committee, err := helpers.BeaconCommitteeFromState(bState, slot, c)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
attData := ðpb.AttestationData{
|
|
|
|
Slot: slot,
|
|
|
|
CommitteeIndex: c,
|
|
|
|
BeaconBlockRoot: headRoot,
|
|
|
|
Source: bState.CurrentJustifiedCheckpoint(),
|
|
|
|
Target: ðpb.Checkpoint{
|
|
|
|
Epoch: currentEpoch,
|
|
|
|
Root: targetRoot,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
dataRoot, err := helpers.ComputeSigningRoot(attData, domain)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
committeeSize := uint64(len(committee))
|
|
|
|
bitsPerAtt := committeeSize / uint64(attsPerCommittee)
|
|
|
|
for i := uint64(0); i < committeeSize; i += bitsPerAtt {
|
|
|
|
aggregationBits := bitfield.NewBitlist(committeeSize)
|
|
|
|
var sigs []bls.Signature
|
|
|
|
for b := i; b < i+bitsPerAtt; b++ {
|
|
|
|
aggregationBits.SetBitAt(b, true)
|
|
|
|
sigs = append(sigs, privs[committee[b]].Sign(dataRoot[:]))
|
|
|
|
}
|
|
|
|
|
|
|
|
// bls.AggregateSignatures will return nil if sigs is 0.
|
|
|
|
if len(sigs) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
att := ðpb.Attestation{
|
|
|
|
Data: attData,
|
|
|
|
AggregationBits: aggregationBits,
|
|
|
|
Signature: bls.AggregateSignatures(sigs).Marshal(),
|
|
|
|
}
|
|
|
|
attestations = append(attestations, att)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return attestations, nil
|
|
|
|
}
|
|
|
|
|
2021-01-13 22:05:57 +00:00
|
|
|
// HydrateAttestation hydrates an attestation object with correct field length sizes
|
|
|
|
// to comply with fssz marshalling and unmarshalling rules.
|
2021-01-07 21:00:21 +00:00
|
|
|
func HydrateAttestation(a *ethpb.Attestation) *ethpb.Attestation {
|
|
|
|
if a.Signature == nil {
|
|
|
|
a.Signature = make([]byte, 96)
|
|
|
|
}
|
|
|
|
if a.AggregationBits == nil {
|
|
|
|
a.AggregationBits = make([]byte, 1)
|
|
|
|
}
|
|
|
|
if a.Data == nil {
|
|
|
|
a.Data = ðpb.AttestationData{}
|
|
|
|
}
|
|
|
|
a.Data = HydrateAttestationData(a.Data)
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
2021-01-13 22:05:57 +00:00
|
|
|
// HydrateAttestationData hydrates an attestation data object with correct field length sizes
|
|
|
|
// to comply with fssz marshalling and unmarshalling rules.
|
2021-01-07 21:00:21 +00:00
|
|
|
func HydrateAttestationData(d *ethpb.AttestationData) *ethpb.AttestationData {
|
|
|
|
if d.BeaconBlockRoot == nil {
|
|
|
|
d.BeaconBlockRoot = make([]byte, 32)
|
|
|
|
}
|
|
|
|
if d.Target == nil {
|
|
|
|
d.Target = ðpb.Checkpoint{}
|
|
|
|
}
|
|
|
|
if d.Target.Root == nil {
|
|
|
|
d.Target.Root = make([]byte, 32)
|
|
|
|
}
|
|
|
|
if d.Source == nil {
|
|
|
|
d.Source = ðpb.Checkpoint{}
|
|
|
|
}
|
|
|
|
if d.Source.Root == nil {
|
|
|
|
d.Source.Root = make([]byte, 32)
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
}
|
2021-01-13 22:05:57 +00:00
|
|
|
|
|
|
|
// HydrateIndexedAttestation hydrates an indexed attestation with correct field length sizes
|
|
|
|
// to comply with fssz marshalling and unmarshalling rules.
|
|
|
|
func HydrateIndexedAttestation(a *ethpb.IndexedAttestation) *ethpb.IndexedAttestation {
|
|
|
|
if a.Signature == nil {
|
|
|
|
a.Signature = make([]byte, 96)
|
|
|
|
}
|
|
|
|
if a.Data == nil {
|
|
|
|
a.Data = ðpb.AttestationData{}
|
|
|
|
}
|
|
|
|
a.Data = HydrateAttestationData(a.Data)
|
|
|
|
return a
|
|
|
|
}
|