2021-09-23 18:53:46 +00:00
|
|
|
package util
|
2021-01-07 21:00:21 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-07-28 17:15:23 +00:00
|
|
|
"errors"
|
2021-01-07 21:00:21 +00:00
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"github.com/prysmaticlabs/go-bitfield"
|
2023-03-17 18:52:56 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
|
|
|
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
|
|
|
|
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/config/params"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/rand"
|
|
|
|
attv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
|
|
|
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
2021-01-07 21:00:21 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewAttestation creates an attestation block with minimum marshalable fields.
|
2022-07-19 12:41:15 +00:00
|
|
|
func NewAttestation() *ethpb.Attestation {
|
2021-01-07 21:00:21 +00:00
|
|
|
return ðpb.Attestation{
|
|
|
|
AggregationBits: bitfield.Bitlist{0b1101},
|
|
|
|
Data: ðpb.AttestationData{
|
2021-12-14 18:42:05 +00:00
|
|
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
2021-01-07 21:00:21 +00:00
|
|
|
Source: ðpb.Checkpoint{
|
2021-12-14 18:42:05 +00:00
|
|
|
Root: make([]byte, fieldparams.RootLength),
|
2021-01-07 21:00:21 +00:00
|
|
|
},
|
|
|
|
Target: ðpb.Checkpoint{
|
2021-12-14 18:42:05 +00:00
|
|
|
Root: make([]byte, fieldparams.RootLength),
|
2021-01-07 21:00:21 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
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.
|
2022-07-19 12:41:15 +00:00
|
|
|
func GenerateAttestations(
|
2023-01-26 14:40:12 +00:00
|
|
|
bState state.BeaconState, privs []bls.SecretKey, numToGen uint64, slot primitives.Slot, randomRoot bool,
|
2021-02-16 07:45:34 +00:00
|
|
|
) ([]*ethpb.Attestation, error) {
|
2021-01-07 21:00:21 +00:00
|
|
|
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
|
|
|
|
}
|
2021-10-01 20:17:57 +00:00
|
|
|
currentEpoch := slots.ToEpoch(slot)
|
2021-01-07 21:00:21 +00:00
|
|
|
|
2021-12-14 18:42:05 +00:00
|
|
|
targetRoot := make([]byte, fieldparams.RootLength)
|
2021-01-07 21:00:21 +00:00
|
|
|
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() {
|
2021-07-28 17:15:23 +00:00
|
|
|
var headState state.BeaconState
|
|
|
|
switch bState.Version() {
|
|
|
|
case version.Phase0:
|
2022-10-19 14:37:45 +00:00
|
|
|
pbState, err := state_native.ProtobufBeaconStatePhase0(bState.ToProto())
|
2021-07-28 17:15:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-09-16 22:17:46 +00:00
|
|
|
genState, err := state_native.InitializeFromProtoUnsafePhase0(pbState)
|
2021-07-28 17:15:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-19 04:38:04 +00:00
|
|
|
headState = genState
|
2021-07-28 17:15:23 +00:00
|
|
|
case version.Altair:
|
2022-10-19 14:37:45 +00:00
|
|
|
pbState, err := state_native.ProtobufBeaconStateAltair(bState.ToProto())
|
2021-07-28 17:15:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-09-16 22:17:46 +00:00
|
|
|
genState, err := state_native.InitializeFromProtoUnsafeAltair(pbState)
|
2021-07-28 17:15:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-19 04:38:04 +00:00
|
|
|
headState = genState
|
2022-08-02 14:55:05 +00:00
|
|
|
case version.Bellatrix:
|
2022-10-19 14:37:45 +00:00
|
|
|
pbState, err := state_native.ProtobufBeaconStateBellatrix(bState.ToProto())
|
2022-08-02 14:55:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-09-16 22:17:46 +00:00
|
|
|
genState, err := state_native.InitializeFromProtoUnsafeBellatrix(pbState)
|
2022-08-02 14:55:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
headState = genState
|
2023-01-01 18:22:01 +00:00
|
|
|
case version.Capella:
|
|
|
|
pbState, err := state_native.ProtobufBeaconStateCapella(bState.ToProto())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
genState, err := state_native.InitializeFromProtoUnsafeCapella(pbState)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
headState = genState
|
2021-07-28 17:15:23 +00:00
|
|
|
default:
|
|
|
|
return nil, errors.New("state type isn't supported")
|
2021-01-07 21:00:21 +00:00
|
|
|
}
|
2021-07-28 17:15:23 +00:00
|
|
|
|
2021-09-08 10:41:47 +00:00
|
|
|
headState, err = transition.ProcessSlots(context.Background(), headState, slot+1)
|
2021-01-07 21:00:21 +00:00
|
|
|
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()
|
2021-12-14 18:42:05 +00:00
|
|
|
b := make([]byte, fieldparams.RootLength)
|
2021-01-07 21:00:21 +00:00
|
|
|
_, err := randGen.Read(b)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
headRoot = b
|
|
|
|
}
|
|
|
|
|
2021-09-26 15:27:57 +00:00
|
|
|
activeValidatorCount, err := helpers.ActiveValidatorCount(context.Background(), bState, currentEpoch)
|
2021-01-07 21:00:21 +00:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-02-14 13:34:38 +00:00
|
|
|
domain, err := signing.Domain(bState.Fork(), currentEpoch, params.BeaconConfig().DomainBeaconAttester, bState.GenesisValidatorsRoot())
|
2021-01-07 21:00:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-01-26 14:40:12 +00:00
|
|
|
for c := primitives.CommitteeIndex(0); uint64(c) < committeesPerSlot && uint64(c) < numToGen; c++ {
|
2021-09-26 15:27:57 +00:00
|
|
|
committee, err := helpers.BeaconCommitteeFromState(context.Background(), bState, slot, c)
|
2021-01-07 21:00:21 +00:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-09-27 16:19:20 +00:00
|
|
|
dataRoot, err := signing.ComputeSigningRoot(attData, domain)
|
2021-01-07 21:00:21 +00:00
|
|
|
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.
|
2022-07-19 12:41:15 +00:00
|
|
|
func HydrateAttestation(a *ethpb.Attestation) *ethpb.Attestation {
|
2021-01-07 21:00:21 +00:00
|
|
|
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{}
|
|
|
|
}
|
2022-07-19 12:41:15 +00:00
|
|
|
a.Data = HydrateAttestationData(a.Data)
|
2021-01-07 21:00:21 +00:00
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
2021-08-04 09:01:38 +00:00
|
|
|
// HydrateV1Attestation hydrates a v1 attestation object with correct field length sizes
|
|
|
|
// to comply with fssz marshalling and unmarshalling rules.
|
2022-07-19 12:41:15 +00:00
|
|
|
func HydrateV1Attestation(a *attv1.Attestation) *attv1.Attestation {
|
2021-08-04 09:01:38 +00:00
|
|
|
if a.Signature == nil {
|
|
|
|
a.Signature = make([]byte, 96)
|
|
|
|
}
|
|
|
|
if a.AggregationBits == nil {
|
|
|
|
a.AggregationBits = make([]byte, 1)
|
|
|
|
}
|
|
|
|
if a.Data == nil {
|
|
|
|
a.Data = &attv1.AttestationData{}
|
|
|
|
}
|
2022-07-19 12:41:15 +00:00
|
|
|
a.Data = HydrateV1AttestationData(a.Data)
|
2021-08-04 09:01:38 +00:00
|
|
|
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.
|
2022-07-19 12:41:15 +00:00
|
|
|
func HydrateAttestationData(d *ethpb.AttestationData) *ethpb.AttestationData {
|
2021-01-07 21:00:21 +00:00
|
|
|
if d.BeaconBlockRoot == nil {
|
2021-12-14 18:42:05 +00:00
|
|
|
d.BeaconBlockRoot = make([]byte, fieldparams.RootLength)
|
2021-01-07 21:00:21 +00:00
|
|
|
}
|
|
|
|
if d.Target == nil {
|
|
|
|
d.Target = ðpb.Checkpoint{}
|
|
|
|
}
|
|
|
|
if d.Target.Root == nil {
|
2021-12-14 18:42:05 +00:00
|
|
|
d.Target.Root = make([]byte, fieldparams.RootLength)
|
2021-01-07 21:00:21 +00:00
|
|
|
}
|
|
|
|
if d.Source == nil {
|
|
|
|
d.Source = ðpb.Checkpoint{}
|
|
|
|
}
|
|
|
|
if d.Source.Root == nil {
|
2021-12-14 18:42:05 +00:00
|
|
|
d.Source.Root = make([]byte, fieldparams.RootLength)
|
2021-01-07 21:00:21 +00:00
|
|
|
}
|
|
|
|
return d
|
|
|
|
}
|
2021-01-13 22:05:57 +00:00
|
|
|
|
2021-08-04 09:01:38 +00:00
|
|
|
// HydrateV1AttestationData hydrates a v1 attestation data object with correct field length sizes
|
|
|
|
// to comply with fssz marshalling and unmarshalling rules.
|
2022-07-19 12:41:15 +00:00
|
|
|
func HydrateV1AttestationData(d *attv1.AttestationData) *attv1.AttestationData {
|
2021-08-04 09:01:38 +00:00
|
|
|
if d.BeaconBlockRoot == nil {
|
2021-12-14 18:42:05 +00:00
|
|
|
d.BeaconBlockRoot = make([]byte, fieldparams.RootLength)
|
2021-08-04 09:01:38 +00:00
|
|
|
}
|
|
|
|
if d.Target == nil {
|
|
|
|
d.Target = &attv1.Checkpoint{}
|
|
|
|
}
|
|
|
|
if d.Target.Root == nil {
|
2021-12-14 18:42:05 +00:00
|
|
|
d.Target.Root = make([]byte, fieldparams.RootLength)
|
2021-08-04 09:01:38 +00:00
|
|
|
}
|
|
|
|
if d.Source == nil {
|
|
|
|
d.Source = &attv1.Checkpoint{}
|
|
|
|
}
|
|
|
|
if d.Source.Root == nil {
|
2021-12-14 18:42:05 +00:00
|
|
|
d.Source.Root = make([]byte, fieldparams.RootLength)
|
2021-08-04 09:01:38 +00:00
|
|
|
}
|
|
|
|
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.
|
2022-07-19 12:41:15 +00:00
|
|
|
func HydrateIndexedAttestation(a *ethpb.IndexedAttestation) *ethpb.IndexedAttestation {
|
2021-01-13 22:05:57 +00:00
|
|
|
if a.Signature == nil {
|
|
|
|
a.Signature = make([]byte, 96)
|
|
|
|
}
|
|
|
|
if a.Data == nil {
|
|
|
|
a.Data = ðpb.AttestationData{}
|
|
|
|
}
|
2022-07-19 12:41:15 +00:00
|
|
|
a.Data = HydrateAttestationData(a.Data)
|
2021-01-13 22:05:57 +00:00
|
|
|
return a
|
|
|
|
}
|