prysm-pulse/testing/util/attestation.go

311 lines
9.4 KiB
Go
Raw Normal View History

package util
2021-01-07 21:00:21 +00:00
import (
"context"
"errors"
2021-01-07 21:00:21 +00:00
"fmt"
"math"
"github.com/prysmaticlabs/go-bitfield"
"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.
func NewAttestation() *ethpb.Attestation {
2021-01-07 21:00:21 +00:00
return &ethpb.Attestation{
AggregationBits: bitfield.Bitlist{0b1101},
Data: &ethpb.AttestationData{
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
2021-01-07 21:00:21 +00:00
Source: &ethpb.Checkpoint{
Root: make([]byte, fieldparams.RootLength),
2021-01-07 21:00:21 +00:00
},
Target: &ethpb.Checkpoint{
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.
func GenerateAttestations(
bState state.BeaconState, privs []bls.SecretKey, numToGen uint64, slot primitives.Slot, randomRoot bool,
) ([]*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
}
currentEpoch := slots.ToEpoch(slot)
2021-01-07 21:00:21 +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() {
var headState state.BeaconState
switch bState.Version() {
case version.Phase0:
pbState, err := state_native.ProtobufBeaconStatePhase0(bState.ToProto())
if err != nil {
return nil, err
}
Remove proto state (#11445) * Remove native state flag and use native state in spectests * remove feature from tests * use e2e config in slasher simulator * use params.BeaconConfig in testutil * use correct function * use minimal config in go_test * fix TestListValidators * parameterize sync committee bits and aggregation bits * Fix TestServer_ListIndexedAttestations_GenesisEpoch (cherry picked from commit 254ab623dde08ae8886b152facdbbd8889ed79db) * fix more tests * fix even more * moreeee * aaaand more * one more fix * one more * simplify TestGetAltairDuties_UnknownPubkey * comment out problematic test * one more fix * one more * aaaand one more * another * use fieldparams in HydrateBlindedBeaconBlockBodyBellatrix * create new package for mainnet tests * TestServer_GetBellatrixBeaconBlock * change slashed validator index * clear cache in reward_test.go * deprecate flag * create bazel mainnet target * move attester mainnet test to mainnet target * "fix" proposer tests * use minimal config in TestServer_circuitBreakBuilder * fix TestProposer_ProposeBlock_OK * more fixes in validator package * more fixes * more fixes * test code * move TestProposer_GetBeaconBlock_BellatrixEpoch to minimal * finally * remove proposer_bellatrix_mainnet_test.go * fix TestServer_GetBellatrixBeaconBlock_HappyCase * fix TestServer_GetBellatrixBeaconBlock_BuilderCase * Preston needs to fix this! * Revert "Preston needs to fix this!" This reverts commit b03d97a16e3080e254c7b19d7f193d3c600ca869. * remove proto state tests * fix migration tests * static analysis fix * review * remove proto state * swap state in tests * fix BUILD file in /proto/testing * remove metrics test with nil state
2022-09-16 22:17:46 +00:00
genState, err := state_native.InitializeFromProtoUnsafePhase0(pbState)
if err != nil {
return nil, err
}
headState = genState
case version.Altair:
pbState, err := state_native.ProtobufBeaconStateAltair(bState.ToProto())
if err != nil {
return nil, err
}
Remove proto state (#11445) * Remove native state flag and use native state in spectests * remove feature from tests * use e2e config in slasher simulator * use params.BeaconConfig in testutil * use correct function * use minimal config in go_test * fix TestListValidators * parameterize sync committee bits and aggregation bits * Fix TestServer_ListIndexedAttestations_GenesisEpoch (cherry picked from commit 254ab623dde08ae8886b152facdbbd8889ed79db) * fix more tests * fix even more * moreeee * aaaand more * one more fix * one more * simplify TestGetAltairDuties_UnknownPubkey * comment out problematic test * one more fix * one more * aaaand one more * another * use fieldparams in HydrateBlindedBeaconBlockBodyBellatrix * create new package for mainnet tests * TestServer_GetBellatrixBeaconBlock * change slashed validator index * clear cache in reward_test.go * deprecate flag * create bazel mainnet target * move attester mainnet test to mainnet target * "fix" proposer tests * use minimal config in TestServer_circuitBreakBuilder * fix TestProposer_ProposeBlock_OK * more fixes in validator package * more fixes * more fixes * test code * move TestProposer_GetBeaconBlock_BellatrixEpoch to minimal * finally * remove proposer_bellatrix_mainnet_test.go * fix TestServer_GetBellatrixBeaconBlock_HappyCase * fix TestServer_GetBellatrixBeaconBlock_BuilderCase * Preston needs to fix this! * Revert "Preston needs to fix this!" This reverts commit b03d97a16e3080e254c7b19d7f193d3c600ca869. * remove proto state tests * fix migration tests * static analysis fix * review * remove proto state * swap state in tests * fix BUILD file in /proto/testing * remove metrics test with nil state
2022-09-16 22:17:46 +00:00
genState, err := state_native.InitializeFromProtoUnsafeAltair(pbState)
if err != nil {
return nil, err
}
headState = genState
case version.Bellatrix:
pbState, err := state_native.ProtobufBeaconStateBellatrix(bState.ToProto())
if err != nil {
return nil, err
}
Remove proto state (#11445) * Remove native state flag and use native state in spectests * remove feature from tests * use e2e config in slasher simulator * use params.BeaconConfig in testutil * use correct function * use minimal config in go_test * fix TestListValidators * parameterize sync committee bits and aggregation bits * Fix TestServer_ListIndexedAttestations_GenesisEpoch (cherry picked from commit 254ab623dde08ae8886b152facdbbd8889ed79db) * fix more tests * fix even more * moreeee * aaaand more * one more fix * one more * simplify TestGetAltairDuties_UnknownPubkey * comment out problematic test * one more fix * one more * aaaand one more * another * use fieldparams in HydrateBlindedBeaconBlockBodyBellatrix * create new package for mainnet tests * TestServer_GetBellatrixBeaconBlock * change slashed validator index * clear cache in reward_test.go * deprecate flag * create bazel mainnet target * move attester mainnet test to mainnet target * "fix" proposer tests * use minimal config in TestServer_circuitBreakBuilder * fix TestProposer_ProposeBlock_OK * more fixes in validator package * more fixes * more fixes * test code * move TestProposer_GetBeaconBlock_BellatrixEpoch to minimal * finally * remove proposer_bellatrix_mainnet_test.go * fix TestServer_GetBellatrixBeaconBlock_HappyCase * fix TestServer_GetBellatrixBeaconBlock_BuilderCase * Preston needs to fix this! * Revert "Preston needs to fix this!" This reverts commit b03d97a16e3080e254c7b19d7f193d3c600ca869. * remove proto state tests * fix migration tests * static analysis fix * review * remove proto state * swap state in tests * fix BUILD file in /proto/testing * remove metrics test with nil state
2022-09-16 22:17:46 +00:00
genState, err := state_native.InitializeFromProtoUnsafeBellatrix(pbState)
if err != nil {
return nil, err
}
headState = genState
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
default:
return nil, errors.New("state type isn't supported")
2021-01-07 21:00:21 +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()
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
}
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,
)
}
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
}
for c := primitives.CommitteeIndex(0); uint64(c) < committeesPerSlot && uint64(c) < numToGen; c++ {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), bState, slot, c)
2021-01-07 21:00:21 +00:00
if err != nil {
return nil, err
}
attData := &ethpb.AttestationData{
Slot: slot,
CommitteeIndex: c,
BeaconBlockRoot: headRoot,
Source: bState.CurrentJustifiedCheckpoint(),
Target: &ethpb.Checkpoint{
Epoch: currentEpoch,
Root: targetRoot,
},
}
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 := &ethpb.Attestation{
Data: attData,
AggregationBits: aggregationBits,
Signature: bls.AggregateSignatures(sigs).Marshal(),
}
attestations = append(attestations, att)
}
}
return attestations, nil
}
// HydrateAttestation hydrates an attestation object with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
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 = &ethpb.AttestationData{}
}
a.Data = HydrateAttestationData(a.Data)
2021-01-07 21:00:21 +00:00
return a
}
// HydrateV1Attestation hydrates a v1 attestation object with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateV1Attestation(a *attv1.Attestation) *attv1.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 = &attv1.AttestationData{}
}
a.Data = HydrateV1AttestationData(a.Data)
return a
}
// HydrateAttestationData hydrates an attestation data object with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateAttestationData(d *ethpb.AttestationData) *ethpb.AttestationData {
2021-01-07 21:00:21 +00:00
if d.BeaconBlockRoot == nil {
d.BeaconBlockRoot = make([]byte, fieldparams.RootLength)
2021-01-07 21:00:21 +00:00
}
if d.Target == nil {
d.Target = &ethpb.Checkpoint{}
}
if d.Target.Root == nil {
d.Target.Root = make([]byte, fieldparams.RootLength)
2021-01-07 21:00:21 +00:00
}
if d.Source == nil {
d.Source = &ethpb.Checkpoint{}
}
if d.Source.Root == nil {
d.Source.Root = make([]byte, fieldparams.RootLength)
2021-01-07 21:00:21 +00:00
}
return d
}
// HydrateV1AttestationData hydrates a v1 attestation data object with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateV1AttestationData(d *attv1.AttestationData) *attv1.AttestationData {
if d.BeaconBlockRoot == nil {
d.BeaconBlockRoot = make([]byte, fieldparams.RootLength)
}
if d.Target == nil {
d.Target = &attv1.Checkpoint{}
}
if d.Target.Root == nil {
d.Target.Root = make([]byte, fieldparams.RootLength)
}
if d.Source == nil {
d.Source = &attv1.Checkpoint{}
}
if d.Source.Root == nil {
d.Source.Root = make([]byte, fieldparams.RootLength)
}
return d
}
// 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 = &ethpb.AttestationData{}
}
a.Data = HydrateAttestationData(a.Data)
return a
}