From 2d15e53dabbea44ade23ae3ef4b16477811c7fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Tue, 28 May 2024 22:56:36 +0900 Subject: [PATCH] Eip 7549 core (#14037) * interfaces move * build fix * remove annoying warning * more build fixes * review * core code * tests part 1 * tests part 2 * TranslateParticipation doesn't need Electra * remove unused function * pending atts don't need Electra * tests part 3 * build fixes * review * remove newline * review * fix test --- .../blockchain/process_attestation.go | 4 +- .../blockchain/process_attestation_test.go | 49 ++-- beacon-chain/blockchain/process_block.go | 4 +- beacon-chain/blockchain/process_block_test.go | 168 ++++++++----- .../blockchain/receive_attestation_test.go | 6 +- beacon-chain/blockchain/receive_block.go | 6 +- beacon-chain/blockchain/setup_test.go | 2 + beacon-chain/core/altair/attestation.go | 4 +- beacon-chain/core/altair/attestation_test.go | 114 ++++++--- beacon-chain/core/altair/upgrade.go | 2 +- beacon-chain/core/blocks/attestation.go | 17 -- beacon-chain/core/blocks/attestation_test.go | 142 +++++++---- .../core/blocks/block_operations_fuzz_test.go | 15 -- beacon-chain/core/blocks/genesis.go | 37 ++- beacon-chain/core/blocks/signature.go | 4 +- beacon-chain/core/epoch/epoch_processing.go | 2 +- beacon-chain/core/helpers/BUILD.bazel | 1 + beacon-chain/core/helpers/beacon_committee.go | 24 ++ .../core/helpers/beacon_committee_test.go | 34 +++ .../slashings/service_attester_test.go | 18 +- .../prysm/v1alpha1/beacon/slashings_test.go | 12 +- .../prysm/v1alpha1/validator/proposer_test.go | 6 +- beacon-chain/sync/subscriber_test.go | 2 +- consensus-types/primitives/BUILD.bazel | 3 + .../primitives/committee_bits_mainnet.go | 9 + .../primitives/committee_bits_minimal.go | 9 + testing/util/BUILD.bazel | 1 + testing/util/altair.go | 20 +- testing/util/attestation.go | 75 +++++- testing/util/bellatrix.go | 20 +- testing/util/block.go | 77 +++++- testing/util/capella_block.go | 21 +- testing/util/electra_block.go | 223 ++++++++++++++++++ testing/util/helpers.go | 19 +- tools/benchmark-files-gen/main.go | 10 +- 35 files changed, 924 insertions(+), 236 deletions(-) create mode 100644 consensus-types/primitives/committee_bits_mainnet.go create mode 100644 consensus-types/primitives/committee_bits_minimal.go create mode 100644 testing/util/electra_block.go diff --git a/beacon-chain/blockchain/process_attestation.go b/beacon-chain/blockchain/process_attestation.go index a0949e8699..19d2abd46c 100644 --- a/beacon-chain/blockchain/process_attestation.go +++ b/beacon-chain/blockchain/process_attestation.go @@ -80,11 +80,11 @@ func (s *Service) OnAttestation(ctx context.Context, a ethpb.Att, disparity time } // Use the target state to verify attesting indices are valid. - committee, err := helpers.BeaconCommitteeFromState(ctx, baseState, a.GetData().Slot, a.GetData().CommitteeIndex) + committees, err := helpers.AttestationCommittees(ctx, baseState, a) if err != nil { return err } - indexedAtt, err := attestation.ConvertToIndexed(ctx, a, committee) + indexedAtt, err := attestation.ConvertToIndexed(ctx, a, committees...) if err != nil { return err } diff --git a/beacon-chain/blockchain/process_attestation_test.go b/beacon-chain/blockchain/process_attestation_test.go index 47e411eacc..f24db1f61a 100644 --- a/beacon-chain/blockchain/process_attestation_test.go +++ b/beacon-chain/blockchain/process_attestation_test.go @@ -7,9 +7,11 @@ import ( "time" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v5/crypto/bls" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/assert" @@ -125,25 +127,36 @@ func TestStore_OnAttestation_ErrorConditions(t *testing.T) { } func TestStore_OnAttestation_Ok_DoublyLinkedTree(t *testing.T) { - service, tr := minimalTestService(t) - ctx := tr.ctx + eval := func(ctx context.Context, service *Service, genesisState state.BeaconState, pks []bls.SecretKey) { + service.SetGenesisTime(time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0)) + require.NoError(t, service.saveGenesisData(ctx, genesisState)) + att, err := util.GenerateAttestations(genesisState, pks, 1, 0, false) + require.NoError(t, err) + tRoot := bytesutil.ToBytes32(att[0].GetData().Target.Root) + copied := genesisState.Copy() + copied, err = transition.ProcessSlots(ctx, copied, 1) + require.NoError(t, err) + require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot)) + ojc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]} + ofc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]} + state, blkRoot, err := prepareForkchoiceState(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, ojc, ofc) + require.NoError(t, err) + require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) + require.NoError(t, service.OnAttestation(ctx, att[0], 0)) + } - genesisState, pks := util.DeterministicGenesisState(t, 64) - service.SetGenesisTime(time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0)) - require.NoError(t, service.saveGenesisData(ctx, genesisState)) - att, err := util.GenerateAttestations(genesisState, pks, 1, 0, false) - require.NoError(t, err) - tRoot := bytesutil.ToBytes32(att[0].Data.Target.Root) - copied := genesisState.Copy() - copied, err = transition.ProcessSlots(ctx, copied, 1) - require.NoError(t, err) - require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot)) - ojc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]} - ofc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]} - state, blkRoot, err := prepareForkchoiceState(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, ojc, ofc) - require.NoError(t, err) - require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) - require.NoError(t, service.OnAttestation(ctx, att[0], 0)) + t.Run("pre-Electra", func(t *testing.T) { + service, tr := minimalTestService(t) + ctx := tr.ctx + genesisState, pks := util.DeterministicGenesisState(t, 64) + eval(ctx, service, genesisState, pks) + }) + t.Run("post-Electra", func(t *testing.T) { + service, tr := minimalTestService(t) + ctx := tr.ctx + genesisState, pks := util.DeterministicGenesisStateElectra(t, 64) + eval(ctx, service, genesisState, pks) + }) } func TestService_GetRecentPreState(t *testing.T) { diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index 575b1f2529..1ea9e28a5d 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -366,11 +366,11 @@ func (s *Service) handleEpochBoundary(ctx context.Context, slot primitives.Slot, func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock, st state.BeaconState) error { // Feed in block's attestations to fork choice store. for _, a := range blk.Body().Attestations() { - committee, err := helpers.BeaconCommitteeFromState(ctx, st, a.GetData().Slot, a.GetData().CommitteeIndex) + committees, err := helpers.AttestationCommittees(ctx, st, a) if err != nil { return err } - indices, err := attestation.AttestingIndices(a, committee) + indices, err := attestation.AttestingIndices(a, committees...) if err != nil { return err } diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index d4ca7a9ba6..d1d1f606ea 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -1963,68 +1963,130 @@ func TestNoViableHead_Reboot(t *testing.T) { } func TestOnBlock_HandleBlockAttestations(t *testing.T) { - service, tr := minimalTestService(t) - ctx := tr.ctx + t.Run("pre-Electra", func(t *testing.T) { + service, tr := minimalTestService(t) + ctx := tr.ctx - st, keys := util.DeterministicGenesisState(t, 64) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(t, err, "Could not hash genesis state") + st, keys := util.DeterministicGenesisState(t, 64) + stateRoot, err := st.HashTreeRoot(ctx) + require.NoError(t, err, "Could not hash genesis state") - require.NoError(t, service.saveGenesisData(ctx, st)) + require.NoError(t, service.saveGenesisData(ctx, st)) - genesis := blocks.NewGenesisBlock(stateRoot[:]) - wsb, err := consensusblocks.NewSignedBeaconBlock(genesis) - require.NoError(t, err) - require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block") - parentRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err, "Could not get signing root") - require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, parentRoot), "Could not save genesis state") - require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state") + genesis := blocks.NewGenesisBlock(stateRoot[:]) + wsb, err := consensusblocks.NewSignedBeaconBlock(genesis) + require.NoError(t, err) + require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block") + parentRoot, err := genesis.Block.HashTreeRoot() + require.NoError(t, err, "Could not get signing root") + require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, parentRoot), "Could not save genesis state") + require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state") - st, err = service.HeadState(ctx) - require.NoError(t, err) - b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 1) - require.NoError(t, err) - wsb, err = consensusblocks.NewSignedBeaconBlock(b) - require.NoError(t, err) - root, err := b.Block.HashTreeRoot() - require.NoError(t, err) - preState, err := service.getBlockPreState(ctx, wsb.Block()) - require.NoError(t, err) - postState, err := service.validateStateTransition(ctx, preState, wsb) - require.NoError(t, err) - require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) - require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + st, err = service.HeadState(ctx) + require.NoError(t, err) + b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 1) + require.NoError(t, err) + wsb, err = consensusblocks.NewSignedBeaconBlock(b) + require.NoError(t, err) + root, err := b.Block.HashTreeRoot() + require.NoError(t, err) + preState, err := service.getBlockPreState(ctx, wsb.Block()) + require.NoError(t, err) + postState, err := service.validateStateTransition(ctx, preState, wsb) + require.NoError(t, err) + require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) - st, err = service.HeadState(ctx) - require.NoError(t, err) - b, err = util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 2) - require.NoError(t, err) - wsb, err = consensusblocks.NewSignedBeaconBlock(b) - require.NoError(t, err) + st, err = service.HeadState(ctx) + require.NoError(t, err) + b, err = util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 2) + require.NoError(t, err) + wsb, err = consensusblocks.NewSignedBeaconBlock(b) + require.NoError(t, err) - // prepare another block that is not inserted - st3, err := transition.ExecuteStateTransition(ctx, st, wsb) - require.NoError(t, err) - b3, err := util.GenerateFullBlock(st3, keys, util.DefaultBlockGenConfig(), 3) - require.NoError(t, err) - wsb3, err := consensusblocks.NewSignedBeaconBlock(b3) - require.NoError(t, err) + // prepare another block that is not inserted + st3, err := transition.ExecuteStateTransition(ctx, st, wsb) + require.NoError(t, err) + b3, err := util.GenerateFullBlock(st3, keys, util.DefaultBlockGenConfig(), 3) + require.NoError(t, err) + wsb3, err := consensusblocks.NewSignedBeaconBlock(b3) + require.NoError(t, err) - require.Equal(t, 1, len(wsb.Block().Body().Attestations())) - a := wsb.Block().Body().Attestations()[0] - r := bytesutil.ToBytes32(a.GetData().BeaconBlockRoot) - require.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(r)) + require.Equal(t, 1, len(wsb.Block().Body().Attestations())) + a := wsb.Block().Body().Attestations()[0] + r := bytesutil.ToBytes32(a.GetData().BeaconBlockRoot) + require.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(r)) - require.Equal(t, 1, len(wsb.Block().Body().Attestations())) - a3 := wsb3.Block().Body().Attestations()[0] - r3 := bytesutil.ToBytes32(a3.GetData().BeaconBlockRoot) - require.Equal(t, false, service.cfg.ForkChoiceStore.HasNode(r3)) + require.Equal(t, 1, len(wsb.Block().Body().Attestations())) + a3 := wsb3.Block().Body().Attestations()[0] + r3 := bytesutil.ToBytes32(a3.GetData().BeaconBlockRoot) + require.Equal(t, false, service.cfg.ForkChoiceStore.HasNode(r3)) - require.NoError(t, service.handleBlockAttestations(ctx, wsb.Block(), st)) // fine to use the same committee as st - require.Equal(t, 0, service.cfg.AttPool.ForkchoiceAttestationCount()) - require.NoError(t, service.handleBlockAttestations(ctx, wsb3.Block(), st3)) // fine to use the same committee as st - require.Equal(t, 1, len(service.cfg.AttPool.BlockAttestations())) + require.NoError(t, service.handleBlockAttestations(ctx, wsb.Block(), st)) // fine to use the same committee as st + require.Equal(t, 0, service.cfg.AttPool.ForkchoiceAttestationCount()) + require.NoError(t, service.handleBlockAttestations(ctx, wsb3.Block(), st3)) // fine to use the same committee as st + require.Equal(t, 1, len(service.cfg.AttPool.BlockAttestations())) + }) + t.Run("post-Electra", func(t *testing.T) { + service, tr := minimalTestService(t) + ctx := tr.ctx + + st, keys := util.DeterministicGenesisStateElectra(t, 64) + require.NoError(t, service.saveGenesisData(ctx, st)) + + genesis, err := blocks.NewGenesisBlockForState(ctx, st) + require.NoError(t, err) + require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, genesis), "Could not save genesis block") + parentRoot, err := genesis.Block().HashTreeRoot() + require.NoError(t, err, "Could not get signing root") + require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, parentRoot), "Could not save genesis state") + require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state") + + st, err = service.HeadState(ctx) + require.NoError(t, err) + b, err := util.GenerateFullBlockElectra(st, keys, util.DefaultBlockGenConfig(), 1) + require.NoError(t, err) + wsb, err := consensusblocks.NewSignedBeaconBlock(b) + require.NoError(t, err) + root, err := b.Block.HashTreeRoot() + require.NoError(t, err) + preState, err := service.getBlockPreState(ctx, wsb.Block()) + require.NoError(t, err) + postState, err := service.validateStateTransition(ctx, preState, wsb) + require.NoError(t, err) + require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) + require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) + + st, err = service.HeadState(ctx) + require.NoError(t, err) + b, err = util.GenerateFullBlockElectra(st, keys, util.DefaultBlockGenConfig(), 2) + require.NoError(t, err) + wsb, err = consensusblocks.NewSignedBeaconBlock(b) + require.NoError(t, err) + + // prepare another block that is not inserted + st3, err := transition.ExecuteStateTransition(ctx, st, wsb) + require.NoError(t, err) + b3, err := util.GenerateFullBlockElectra(st3, keys, util.DefaultBlockGenConfig(), 3) + require.NoError(t, err) + wsb3, err := consensusblocks.NewSignedBeaconBlock(b3) + require.NoError(t, err) + + require.Equal(t, 1, len(wsb.Block().Body().Attestations())) + a := wsb.Block().Body().Attestations()[0] + r := bytesutil.ToBytes32(a.GetData().BeaconBlockRoot) + require.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(r)) + + require.Equal(t, 1, len(wsb.Block().Body().Attestations())) + a3 := wsb3.Block().Body().Attestations()[0] + r3 := bytesutil.ToBytes32(a3.GetData().BeaconBlockRoot) + require.Equal(t, false, service.cfg.ForkChoiceStore.HasNode(r3)) + + require.NoError(t, service.handleBlockAttestations(ctx, wsb.Block(), st)) // fine to use the same committee as st + require.Equal(t, 0, service.cfg.AttPool.ForkchoiceAttestationCount()) + require.NoError(t, service.handleBlockAttestations(ctx, wsb3.Block(), st3)) // fine to use the same committee as st + require.Equal(t, 1, len(service.cfg.AttPool.BlockAttestations())) + }) } func TestFillMissingBlockPayloadId_DiffSlotExitEarly(t *testing.T) { diff --git a/beacon-chain/blockchain/receive_attestation_test.go b/beacon-chain/blockchain/receive_attestation_test.go index 94c27f1fb8..d0df3e1b23 100644 --- a/beacon-chain/blockchain/receive_attestation_test.go +++ b/beacon-chain/blockchain/receive_attestation_test.go @@ -73,7 +73,7 @@ func TestProcessAttestations_Ok(t *testing.T) { require.NoError(t, service.saveGenesisData(ctx, genesisState)) atts, err := util.GenerateAttestations(genesisState, pks, 1, 0, false) require.NoError(t, err) - tRoot := bytesutil.ToBytes32(atts[0].Data.Target.Root) + tRoot := bytesutil.ToBytes32(atts[0].GetData().Target.Root) copied := genesisState.Copy() copied, err = transition.ProcessSlots(ctx, copied, 1) require.NoError(t, err) @@ -131,8 +131,8 @@ func TestService_ProcessAttestationsAndUpdateHead(t *testing.T) { } require.NoError(t, service.cfg.AttPool.SaveForkchoiceAttestations(attsToSave)) // Verify the target is in forkchoice - require.Equal(t, true, fcs.HasNode(bytesutil.ToBytes32(atts[0].Data.BeaconBlockRoot))) - require.Equal(t, tRoot, bytesutil.ToBytes32(atts[0].Data.BeaconBlockRoot)) + require.Equal(t, true, fcs.HasNode(bytesutil.ToBytes32(atts[0].GetData().BeaconBlockRoot))) + require.Equal(t, tRoot, bytesutil.ToBytes32(atts[0].GetData().BeaconBlockRoot)) require.Equal(t, true, fcs.HasNode(service.originBlockRoot)) // Insert a new block to forkchoice diff --git a/beacon-chain/blockchain/receive_block.go b/beacon-chain/blockchain/receive_block.go index 884e257f32..db2de5f1ff 100644 --- a/beacon-chain/blockchain/receive_block.go +++ b/beacon-chain/blockchain/receive_block.go @@ -479,12 +479,12 @@ func (s *Service) sendBlockAttestationsToSlasher(signed interfaces.ReadOnlySigne // is done in the background to avoid adding more load to this critical code path. ctx := context.TODO() for _, att := range signed.Block().Body().Attestations() { - committee, err := helpers.BeaconCommitteeFromState(ctx, preState, att.GetData().Slot, att.GetData().CommitteeIndex) + committees, err := helpers.AttestationCommittees(ctx, preState, att) if err != nil { - log.WithError(err).Error("Could not get attestation committee") + log.WithError(err).Error("Could not get attestation committees") return } - indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee) + indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committees...) if err != nil { log.WithError(err).Error("Could not convert to indexed attestation") return diff --git a/beacon-chain/blockchain/setup_test.go b/beacon-chain/blockchain/setup_test.go index f305ea6e95..22acd22147 100644 --- a/beacon-chain/blockchain/setup_test.go +++ b/beacon-chain/blockchain/setup_test.go @@ -13,6 +13,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/db" "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem" testDB "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing" + mockExecution "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice" doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree" "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/attestations" @@ -120,6 +121,7 @@ func minimalTestService(t *testing.T, opts ...Option) (*Service, *testServiceReq WithTrackedValidatorsCache(cache.NewTrackedValidatorsCache()), WithBlobStorage(filesystem.NewEphemeralBlobStorage(t)), WithSyncChecker(mock.MockChecker{}), + WithExecutionEngineCaller(&mockExecution.EngineClient{}), } // append the variadic opts so they override the defaults by being processed afterwards opts = append(defOpts, opts...) diff --git a/beacon-chain/core/altair/attestation.go b/beacon-chain/core/altair/attestation.go index e2d6305a4b..6c9c251e5f 100644 --- a/beacon-chain/core/altair/attestation.go +++ b/beacon-chain/core/altair/attestation.go @@ -66,11 +66,11 @@ func ProcessAttestationNoVerifySignature( if err != nil { return nil, err } - committee, err := helpers.BeaconCommitteeFromState(ctx, beaconState, att.GetData().Slot, att.GetData().CommitteeIndex) + committees, err := helpers.AttestationCommittees(ctx, beaconState, att) if err != nil { return nil, err } - indices, err := attestation.AttestingIndices(att, committee) + indices, err := attestation.AttestingIndices(att, committees...) if err != nil { return nil, err } diff --git a/beacon-chain/core/altair/attestation_test.go b/beacon-chain/core/altair/attestation_test.go index 2665edac85..6b8367c07f 100644 --- a/beacon-chain/core/altair/attestation_test.go +++ b/beacon-chain/core/altair/attestation_test.go @@ -195,47 +195,95 @@ func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) { } func TestProcessAttestations_OK(t *testing.T) { - beaconState, privKeys := util.DeterministicGenesisStateAltair(t, 100) + t.Run("pre-Electra", func(t *testing.T) { + beaconState, privKeys := util.DeterministicGenesisStateAltair(t, 100) - aggBits := bitfield.NewBitlist(3) - aggBits.SetBitAt(0, true) - var mockRoot [32]byte - copy(mockRoot[:], "hello-world") - att := util.HydrateAttestation(ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Root: mockRoot[:]}, - Target: ðpb.Checkpoint{Root: mockRoot[:]}, - }, - AggregationBits: aggBits, + aggBits := bitfield.NewBitlist(3) + aggBits.SetBitAt(0, true) + var mockRoot [32]byte + copy(mockRoot[:], "hello-world") + att := util.HydrateAttestation(ðpb.Attestation{ + Data: ðpb.AttestationData{ + Source: ðpb.Checkpoint{Root: mockRoot[:]}, + Target: ðpb.Checkpoint{Root: mockRoot[:]}, + }, + AggregationBits: aggBits, + }) + + cfc := beaconState.CurrentJustifiedCheckpoint() + cfc.Root = mockRoot[:] + require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cfc)) + + committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, 0) + require.NoError(t, err) + attestingIndices, err := attestation.AttestingIndices(att, committee) + require.NoError(t, err) + sigs := make([]bls.Signature, len(attestingIndices)) + for i, indice := range attestingIndices { + sb, err := signing.ComputeDomainAndSign(beaconState, 0, att.Data, params.BeaconConfig().DomainBeaconAttester, privKeys[indice]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + sigs[i] = sig + } + att.Signature = bls.AggregateSignatures(sigs).Marshal() + + block := util.NewBeaconBlockAltair() + block.Block.Body.Attestations = []*ethpb.Attestation{att} + + err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay) + require.NoError(t, err) + wsb, err := blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) + _, err = altair.ProcessAttestationsNoVerifySignature(context.Background(), beaconState, wsb.Block()) + require.NoError(t, err) }) + t.Run("post-Electra", func(t *testing.T) { + beaconState, privKeys := util.DeterministicGenesisStateElectra(t, 100) - cfc := beaconState.CurrentJustifiedCheckpoint() - cfc.Root = mockRoot[:] - require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cfc)) + aggBits := bitfield.NewBitlist(3) + aggBits.SetBitAt(0, true) + committeeBits := primitives.NewAttestationCommitteeBits() + committeeBits.SetBitAt(0, true) + var mockRoot [32]byte + copy(mockRoot[:], "hello-world") + att := util.HydrateAttestationElectra(ðpb.AttestationElectra{ + Data: ðpb.AttestationData{ + Source: ðpb.Checkpoint{Root: mockRoot[:]}, + Target: ðpb.Checkpoint{Root: mockRoot[:]}, + }, + AggregationBits: aggBits, + CommitteeBits: committeeBits, + }) - committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex) - require.NoError(t, err) - attestingIndices, err := attestation.AttestingIndices(att, committee) - require.NoError(t, err) - sigs := make([]bls.Signature, len(attestingIndices)) - for i, indice := range attestingIndices { - sb, err := signing.ComputeDomainAndSign(beaconState, 0, att.Data, params.BeaconConfig().DomainBeaconAttester, privKeys[indice]) + cfc := beaconState.CurrentJustifiedCheckpoint() + cfc.Root = mockRoot[:] + require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cfc)) + + committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, 0) require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) + attestingIndices, err := attestation.AttestingIndices(att, committee) require.NoError(t, err) - sigs[i] = sig - } - att.Signature = bls.AggregateSignatures(sigs).Marshal() + sigs := make([]bls.Signature, len(attestingIndices)) + for i, indice := range attestingIndices { + sb, err := signing.ComputeDomainAndSign(beaconState, 0, att.Data, params.BeaconConfig().DomainBeaconAttester, privKeys[indice]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + sigs[i] = sig + } + att.Signature = bls.AggregateSignatures(sigs).Marshal() - block := util.NewBeaconBlockAltair() - block.Block.Body.Attestations = []*ethpb.Attestation{att} + block := util.NewBeaconBlockElectra() + block.Block.Body.Attestations = []*ethpb.AttestationElectra{att} - err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay) - require.NoError(t, err) - wsb, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) - _, err = altair.ProcessAttestationsNoVerifySignature(context.Background(), beaconState, wsb.Block()) - require.NoError(t, err) + err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay) + require.NoError(t, err) + wsb, err := blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) + _, err = altair.ProcessAttestationsNoVerifySignature(context.Background(), beaconState, wsb.Block()) + require.NoError(t, err) + }) } func TestProcessAttestationNoVerify_SourceTargetHead(t *testing.T) { diff --git a/beacon-chain/core/altair/upgrade.go b/beacon-chain/core/altair/upgrade.go index 3b3e33ce89..6a4c0354b1 100644 --- a/beacon-chain/core/altair/upgrade.go +++ b/beacon-chain/core/altair/upgrade.go @@ -154,7 +154,7 @@ func TranslateParticipation(ctx context.Context, state state.BeaconState, atts [ if err != nil { return nil, err } - committee, err := helpers.BeaconCommitteeFromState(ctx, state, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(ctx, state, att.GetData().Slot, att.GetData().CommitteeIndex) if err != nil { return nil, err } diff --git a/beacon-chain/core/blocks/attestation.go b/beacon-chain/core/blocks/attestation.go index 5b9c0c83ef..fe71802126 100644 --- a/beacon-chain/core/blocks/attestation.go +++ b/beacon-chain/core/blocks/attestation.go @@ -200,23 +200,6 @@ func ProcessAttestationNoVerifySignature( return beaconState, nil } -// VerifyAttestationSignature converts and attestation into an indexed attestation and verifies -// the signature in that attestation. -func VerifyAttestationSignature(ctx context.Context, beaconState state.ReadOnlyBeaconState, att ethpb.Att) error { - if err := helpers.ValidateNilAttestation(att); err != nil { - return err - } - committee, err := helpers.BeaconCommitteeFromState(ctx, beaconState, att.GetData().Slot, att.GetData().CommitteeIndex) - if err != nil { - return err - } - indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee) - if err != nil { - return err - } - return VerifyIndexedAttestation(ctx, beaconState, indexedAtt) -} - // VerifyIndexedAttestation determines the validity of an indexed attestation. // // Spec pseudocode definition: diff --git a/beacon-chain/core/blocks/attestation_test.go b/beacon-chain/core/blocks/attestation_test.go index 2ebc36a231..361be04b5c 100644 --- a/beacon-chain/core/blocks/attestation_test.go +++ b/beacon-chain/core/blocks/attestation_test.go @@ -578,53 +578,109 @@ func TestRetrieveAttestationSignatureSet_VerifiesMultipleAttestations(t *testing } } - st, err := util.NewBeaconState() - require.NoError(t, err) - require.NoError(t, st.SetSlot(5)) - require.NoError(t, st.SetValidators(validators)) + t.Run("pre-Electra", func(t *testing.T) { + st, err := util.NewBeaconState() + require.NoError(t, err) + require.NoError(t, st.SetSlot(5)) + require.NoError(t, st.SetValidators(validators)) - comm1, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 0 /*committeeIndex*/) - require.NoError(t, err) - att1 := util.HydrateAttestation(ðpb.Attestation{ - AggregationBits: bitfield.NewBitlist(uint64(len(comm1))), - Data: ðpb.AttestationData{ - Slot: 1, - }, + comm1, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 0 /*committeeIndex*/) + require.NoError(t, err) + att1 := util.HydrateAttestation(ðpb.Attestation{ + AggregationBits: bitfield.NewBitlist(uint64(len(comm1))), + Data: ðpb.AttestationData{ + Slot: 1, + }, + }) + domain, err := signing.Domain(st.Fork(), st.Fork().Epoch, params.BeaconConfig().DomainBeaconAttester, st.GenesisValidatorsRoot()) + require.NoError(t, err) + root, err := signing.ComputeSigningRoot(att1.Data, domain) + require.NoError(t, err) + var sigs []bls.Signature + for i, u := range comm1 { + att1.AggregationBits.SetBitAt(uint64(i), true) + sigs = append(sigs, keys[u].Sign(root[:])) + } + att1.Signature = bls.AggregateSignatures(sigs).Marshal() + + comm2, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 1 /*committeeIndex*/) + require.NoError(t, err) + att2 := util.HydrateAttestation(ðpb.Attestation{ + AggregationBits: bitfield.NewBitlist(uint64(len(comm2))), + Data: ðpb.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + }, + }) + root, err = signing.ComputeSigningRoot(att2.Data, domain) + require.NoError(t, err) + sigs = nil + for i, u := range comm2 { + att2.AggregationBits.SetBitAt(uint64(i), true) + sigs = append(sigs, keys[u].Sign(root[:])) + } + att2.Signature = bls.AggregateSignatures(sigs).Marshal() + + set, err := blocks.AttestationSignatureBatch(ctx, st, []ethpb.Att{att1, att2}) + require.NoError(t, err) + verified, err := set.Verify() + require.NoError(t, err) + assert.Equal(t, true, verified, "Multiple signatures were unable to be verified.") }) - domain, err := signing.Domain(st.Fork(), st.Fork().Epoch, params.BeaconConfig().DomainBeaconAttester, st.GenesisValidatorsRoot()) - require.NoError(t, err) - root, err := signing.ComputeSigningRoot(att1.Data, domain) - require.NoError(t, err) - var sigs []bls.Signature - for i, u := range comm1 { - att1.AggregationBits.SetBitAt(uint64(i), true) - sigs = append(sigs, keys[u].Sign(root[:])) - } - att1.Signature = bls.AggregateSignatures(sigs).Marshal() + t.Run("post-Electra", func(t *testing.T) { + st, err := util.NewBeaconStateElectra() + require.NoError(t, err) + require.NoError(t, st.SetSlot(5)) + require.NoError(t, st.SetValidators(validators)) - comm2, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 1 /*committeeIndex*/) - require.NoError(t, err) - att2 := util.HydrateAttestation(ðpb.Attestation{ - AggregationBits: bitfield.NewBitlist(uint64(len(comm2))), - Data: ðpb.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - }, + comm1, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 0 /*committeeIndex*/) + require.NoError(t, err) + commBits1 := primitives.NewAttestationCommitteeBits() + commBits1.SetBitAt(0, true) + att1 := util.HydrateAttestationElectra(ðpb.AttestationElectra{ + AggregationBits: bitfield.NewBitlist(uint64(len(comm1))), + CommitteeBits: commBits1, + Data: ðpb.AttestationData{ + Slot: 1, + }, + }) + domain, err := signing.Domain(st.Fork(), st.Fork().Epoch, params.BeaconConfig().DomainBeaconAttester, st.GenesisValidatorsRoot()) + require.NoError(t, err) + root, err := signing.ComputeSigningRoot(att1.Data, domain) + require.NoError(t, err) + var sigs []bls.Signature + for i, u := range comm1 { + att1.AggregationBits.SetBitAt(uint64(i), true) + sigs = append(sigs, keys[u].Sign(root[:])) + } + att1.Signature = bls.AggregateSignatures(sigs).Marshal() + + comm2, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 1 /*committeeIndex*/) + require.NoError(t, err) + commBits2 := primitives.NewAttestationCommitteeBits() + commBits2.SetBitAt(1, true) + att2 := util.HydrateAttestationElectra(ðpb.AttestationElectra{ + AggregationBits: bitfield.NewBitlist(uint64(len(comm2))), + CommitteeBits: commBits2, + Data: ðpb.AttestationData{ + Slot: 1, + }, + }) + root, err = signing.ComputeSigningRoot(att2.Data, domain) + require.NoError(t, err) + sigs = nil + for i, u := range comm2 { + att2.AggregationBits.SetBitAt(uint64(i), true) + sigs = append(sigs, keys[u].Sign(root[:])) + } + att2.Signature = bls.AggregateSignatures(sigs).Marshal() + + set, err := blocks.AttestationSignatureBatch(ctx, st, []ethpb.Att{att1, att2}) + require.NoError(t, err) + verified, err := set.Verify() + require.NoError(t, err) + assert.Equal(t, true, verified, "Multiple signatures were unable to be verified.") }) - root, err = signing.ComputeSigningRoot(att2.Data, domain) - require.NoError(t, err) - sigs = nil - for i, u := range comm2 { - att2.AggregationBits.SetBitAt(uint64(i), true) - sigs = append(sigs, keys[u].Sign(root[:])) - } - att2.Signature = bls.AggregateSignatures(sigs).Marshal() - - set, err := blocks.AttestationSignatureBatch(ctx, st, []ethpb.Att{att1, att2}) - require.NoError(t, err) - verified, err := set.Verify() - require.NoError(t, err) - assert.Equal(t, true, verified, "Multiple signatures were unable to be verified.") } func TestRetrieveAttestationSignatureSet_AcrossFork(t *testing.T) { diff --git a/beacon-chain/core/blocks/block_operations_fuzz_test.go b/beacon-chain/core/blocks/block_operations_fuzz_test.go index 28db7d8c4f..23adc4144a 100644 --- a/beacon-chain/core/blocks/block_operations_fuzz_test.go +++ b/beacon-chain/core/blocks/block_operations_fuzz_test.go @@ -297,21 +297,6 @@ func TestFuzzVerifyIndexedAttestationn_10000(t *testing.T) { } } -func TestFuzzVerifyAttestation_10000(t *testing.T) { - fuzzer := fuzz.NewWithSeed(0) - state := ðpb.BeaconState{} - attestation := ðpb.Attestation{} - ctx := context.Background() - for i := 0; i < 10000; i++ { - fuzzer.Fuzz(state) - fuzzer.Fuzz(attestation) - s, err := state_native.InitializeFromProtoUnsafePhase0(state) - require.NoError(t, err) - err = VerifyAttestationSignature(ctx, s, attestation) - _ = err - } -} - func TestFuzzProcessDeposits_10000(t *testing.T) { fuzzer := fuzz.NewWithSeed(0) state := ðpb.BeaconState{} diff --git a/beacon-chain/core/blocks/genesis.go b/beacon-chain/core/blocks/genesis.go index 466bc8bd9c..1099085cc2 100644 --- a/beacon-chain/core/blocks/genesis.go +++ b/beacon-chain/core/blocks/genesis.go @@ -163,7 +163,42 @@ func NewGenesisBlockForState(ctx context.Context, st state.BeaconState) (interfa SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8), SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), }, - ExecutionPayload: &enginev1.ExecutionPayloadDeneb{ // Deneb difference. + ExecutionPayload: &enginev1.ExecutionPayloadDeneb{ + ParentHash: make([]byte, 32), + FeeRecipient: make([]byte, 20), + StateRoot: make([]byte, 32), + ReceiptsRoot: make([]byte, 32), + LogsBloom: make([]byte, 256), + PrevRandao: make([]byte, 32), + ExtraData: make([]byte, 0), + BaseFeePerGas: make([]byte, 32), + BlockHash: make([]byte, 32), + Transactions: make([][]byte, 0), + Withdrawals: make([]*enginev1.Withdrawal, 0), + }, + BlsToExecutionChanges: make([]*ethpb.SignedBLSToExecutionChange, 0), + BlobKzgCommitments: make([][]byte, 0), + }, + }, + Signature: params.BeaconConfig().EmptySignature[:], + }) + case *ethpb.BeaconStateElectra: + return blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockElectra{ + Block: ðpb.BeaconBlockElectra{ + ParentRoot: params.BeaconConfig().ZeroHash[:], + StateRoot: root[:], + Body: ðpb.BeaconBlockBodyElectra{ + RandaoReveal: make([]byte, 96), + Eth1Data: ðpb.Eth1Data{ + DepositRoot: make([]byte, 32), + BlockHash: make([]byte, 32), + }, + Graffiti: make([]byte, 32), + SyncAggregate: ðpb.SyncAggregate{ + SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8), + SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), + }, + ExecutionPayload: &enginev1.ExecutionPayloadElectra{ ParentHash: make([]byte, 32), FeeRecipient: make([]byte, 20), StateRoot: make([]byte, 32), diff --git a/beacon-chain/core/blocks/signature.go b/beacon-chain/core/blocks/signature.go index b142441605..c0f9741589 100644 --- a/beacon-chain/core/blocks/signature.go +++ b/beacon-chain/core/blocks/signature.go @@ -192,11 +192,11 @@ func createAttestationSignatureBatch( descs := make([]string, len(atts)) for i, a := range atts { sigs[i] = a.GetSignature() - c, err := helpers.BeaconCommitteeFromState(ctx, beaconState, a.GetData().Slot, a.GetData().CommitteeIndex) + committees, err := helpers.AttestationCommittees(ctx, beaconState, a) if err != nil { return nil, err } - ia, err := attestation.ConvertToIndexed(ctx, a, c) + ia, err := attestation.ConvertToIndexed(ctx, a, committees...) if err != nil { return nil, err } diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index 5119e5059f..e941997179 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -470,7 +470,7 @@ func UnslashedAttestingIndices(ctx context.Context, state state.ReadOnlyBeaconSt seen := make(map[uint64]bool) for _, att := range atts { - committee, err := helpers.BeaconCommitteeFromState(ctx, state, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(ctx, state, att.GetData().Slot, att.GetData().CommitteeIndex) if err != nil { return nil, err } diff --git a/beacon-chain/core/helpers/BUILD.bazel b/beacon-chain/core/helpers/BUILD.bazel index c8b45a534c..4e2a799415 100644 --- a/beacon-chain/core/helpers/BUILD.bazel +++ b/beacon-chain/core/helpers/BUILD.bazel @@ -34,6 +34,7 @@ go_library( "//encoding/bytesutil:go_default_library", "//math:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//runtime/version:go_default_library", "//time:go_default_library", "//time/slots:go_default_library", "@com_github_pkg_errors//:go_default_library", diff --git a/beacon-chain/core/helpers/beacon_committee.go b/beacon-chain/core/helpers/beacon_committee.go index 2980c51091..91d6c5a863 100644 --- a/beacon-chain/core/helpers/beacon_committee.go +++ b/beacon-chain/core/helpers/beacon_committee.go @@ -21,6 +21,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/math" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" ) @@ -58,6 +59,29 @@ func SlotCommitteeCount(activeValidatorCount uint64) uint64 { return committeesPerSlot } +// AttestationCommittees returns beacon state committees that reflect attestation's committee indices. +func AttestationCommittees(ctx context.Context, st state.ReadOnlyBeaconState, att ethpb.Att) ([][]primitives.ValidatorIndex, error) { + var committees [][]primitives.ValidatorIndex + if att.Version() >= version.Electra { + committeeIndices := att.CommitteeBitsVal().BitIndices() + committees = make([][]primitives.ValidatorIndex, len(committeeIndices)) + for i, ci := range committeeIndices { + committee, err := BeaconCommitteeFromState(ctx, st, att.GetData().Slot, primitives.CommitteeIndex(ci)) + if err != nil { + return nil, err + } + committees[i] = committee + } + } else { + committee, err := BeaconCommitteeFromState(ctx, st, att.GetData().Slot, att.GetData().CommitteeIndex) + if err != nil { + return nil, err + } + committees = [][]primitives.ValidatorIndex{committee} + } + return committees, nil +} + // BeaconCommitteeFromState returns the crosslink committee of a given slot and committee index. This // is a spec implementation where state is used as an argument. In case of state retrieval // becomes expensive, consider using BeaconCommittee below. diff --git a/beacon-chain/core/helpers/beacon_committee_test.go b/beacon-chain/core/helpers/beacon_committee_test.go index fd3cc25523..2bbafdd0fa 100644 --- a/beacon-chain/core/helpers/beacon_committee_test.go +++ b/beacon-chain/core/helpers/beacon_committee_test.go @@ -715,3 +715,37 @@ func TestCommitteeIndices(t *testing.T) { indices := helpers.CommitteeIndices(bitfield) assert.DeepEqual(t, []primitives.CommitteeIndex{0, 1, 3}, indices) } + +func TestAttestationCommittees(t *testing.T) { + validators := make([]*ethpb.Validator, params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().TargetCommitteeSize)) + for i := 0; i < len(validators); i++ { + validators[i] = ðpb.Validator{ + ExitEpoch: params.BeaconConfig().FarFutureEpoch, + } + } + + state, err := state_native.InitializeFromProtoPhase0(ðpb.BeaconState{ + Validators: validators, + RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), + }) + require.NoError(t, err) + + t.Run("pre-Electra", func(t *testing.T) { + att := ðpb.Attestation{Data: ðpb.AttestationData{CommitteeIndex: 0}} + committees, err := helpers.AttestationCommittees(context.Background(), state, att) + require.NoError(t, err) + require.Equal(t, 1, len(committees)) + assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0]))) + }) + t.Run("post-Electra", func(t *testing.T) { + bits := primitives.NewAttestationCommitteeBits() + bits.SetBitAt(0, true) + bits.SetBitAt(1, true) + att := ðpb.AttestationElectra{CommitteeBits: bits, Data: ðpb.AttestationData{}} + committees, err := helpers.AttestationCommittees(context.Background(), state, att) + require.NoError(t, err) + require.Equal(t, 2, len(committees)) + assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0]))) + assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[1]))) + }) +} diff --git a/beacon-chain/operations/slashings/service_attester_test.go b/beacon-chain/operations/slashings/service_attester_test.go index 50f4ab63d6..2dab3f1076 100644 --- a/beacon-chain/operations/slashings/service_attester_test.go +++ b/beacon-chain/operations/slashings/service_attester_test.go @@ -17,9 +17,9 @@ import ( func validAttesterSlashingForValIdx(t *testing.T, beaconState state.BeaconState, privs []bls.SecretKey, valIdx ...uint64) *ethpb.AttesterSlashing { var slashings []*ethpb.AttesterSlashing for _, idx := range valIdx { - slashing, err := util.GenerateAttesterSlashingForValidator(beaconState, privs[idx], primitives.ValidatorIndex(idx)) + generatedSlashing, err := util.GenerateAttesterSlashingForValidator(beaconState, privs[idx], primitives.ValidatorIndex(idx)) require.NoError(t, err) - slashings = append(slashings, slashing) + slashings = append(slashings, generatedSlashing.(*ethpb.AttesterSlashing)) } var allSig1 []bls.Signature var allSig2 []bls.Signature @@ -78,12 +78,14 @@ func TestPool_InsertAttesterSlashing(t *testing.T) { pendingSlashings := make([]*PendingAttesterSlashing, 20) slashings := make([]*ethpb.AttesterSlashing, 20) for i := 0; i < len(pendingSlashings); i++ { - sl, err := util.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], primitives.ValidatorIndex(i)) + generatedSl, err := util.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], primitives.ValidatorIndex(i)) require.NoError(t, err) pendingSlashings[i] = &PendingAttesterSlashing{ - attesterSlashing: sl, + attesterSlashing: generatedSl, validatorToSlash: primitives.ValidatorIndex(i), } + sl, ok := generatedSl.(*ethpb.AttesterSlashing) + require.Equal(t, true, ok, "Attester slashing has the wrong type (expected %T, got %T)", ðpb.AttesterSlashing{}, generatedSl) slashings[i] = sl } require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch)) @@ -303,12 +305,16 @@ func TestPool_InsertAttesterSlashing_SigFailsVerify_ClearPool(t *testing.T) { pendingSlashings := make([]*PendingAttesterSlashing, 2) slashings := make([]*ethpb.AttesterSlashing, 2) for i := 0; i < 2; i++ { - sl, err := util.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], primitives.ValidatorIndex(i)) + generatedSl, err := util.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], primitives.ValidatorIndex(i)) require.NoError(t, err) pendingSlashings[i] = &PendingAttesterSlashing{ - attesterSlashing: sl, + attesterSlashing: generatedSl, validatorToSlash: primitives.ValidatorIndex(i), } + sl, ok := generatedSl.(*ethpb.AttesterSlashing) + if !ok { + require.Equal(t, true, ok, "Attester slashing has the wrong type (expected %T, got %T)", ðpb.AttesterSlashing{}, generatedSl) + } slashings[i] = sl } // We mess up the signature of the second slashing. diff --git a/beacon-chain/rpc/prysm/v1alpha1/beacon/slashings_test.go b/beacon-chain/rpc/prysm/v1alpha1/beacon/slashings_test.go index 78d6455b73..a69459168b 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/beacon/slashings_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/beacon/slashings_test.go @@ -66,13 +66,13 @@ func TestServer_SubmitAttesterSlashing(t *testing.T) { Broadcaster: mb, } - slashing, err := util.GenerateAttesterSlashingForValidator(st, privs[2], primitives.ValidatorIndex(2)) + generatedSlashing, err := util.GenerateAttesterSlashingForValidator(st, privs[2], primitives.ValidatorIndex(2)) require.NoError(t, err) // We want the intersection of the slashing attesting indices // to be slashed, so we expect validators 2 and 3 to be in the response // slashed indices. - _, err = bs.SubmitAttesterSlashing(ctx, slashing) + _, err = bs.SubmitAttesterSlashing(ctx, generatedSlashing.(*ethpb.AttesterSlashing)) require.NoError(t, err) assert.Equal(t, true, mb.BroadcastCalled.Load(), "Expected broadcast to be called when flag is set") } @@ -144,7 +144,7 @@ func TestServer_SubmitAttesterSlashing_DontBroadcast(t *testing.T) { Broadcaster: mb, } - slashing, err := util.GenerateAttesterSlashingForValidator(st, privs[2], primitives.ValidatorIndex(2)) + generatedSlashing, err := util.GenerateAttesterSlashingForValidator(st, privs[2], primitives.ValidatorIndex(2)) require.NoError(t, err) // We want the intersection of the slashing attesting indices @@ -153,17 +153,17 @@ func TestServer_SubmitAttesterSlashing_DontBroadcast(t *testing.T) { wanted := ðpb.SubmitSlashingResponse{ SlashedIndices: []primitives.ValidatorIndex{2}, } - res, err := bs.SubmitAttesterSlashing(ctx, slashing) + res, err := bs.SubmitAttesterSlashing(ctx, generatedSlashing.(*ethpb.AttesterSlashing)) require.NoError(t, err) if !proto.Equal(wanted, res) { t.Errorf("Wanted %v, received %v", wanted, res) } assert.Equal(t, false, mb.BroadcastCalled.Load(), "Expected broadcast not to be called by default") - slashing, err = util.GenerateAttesterSlashingForValidator(st, privs[5], primitives.ValidatorIndex(5)) + generatedSlashing, err = util.GenerateAttesterSlashingForValidator(st, privs[5], primitives.ValidatorIndex(5)) require.NoError(t, err) // If any of the attesting indices in the slashing object have already // been slashed, we should fail to insert properly into the attester slashing pool. - _, err = bs.SubmitAttesterSlashing(ctx, slashing) + _, err = bs.SubmitAttesterSlashing(ctx, generatedSlashing.(*ethpb.AttesterSlashing)) assert.NotNil(t, err, "Expected including a attester slashing for an already slashed validator to fail") } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go index 108f54eb81..fcecd22b0f 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go @@ -753,10 +753,12 @@ func injectSlashings(t *testing.T, st state.BeaconState, keys []bls.SecretKey, s attSlashings := make([]*ethpb.AttesterSlashing, params.BeaconConfig().MaxAttesterSlashings) for i := uint64(0); i < params.BeaconConfig().MaxAttesterSlashings; i++ { - attesterSlashing, err := util.GenerateAttesterSlashingForValidator(st, keys[i+params.BeaconConfig().MaxProposerSlashings], primitives.ValidatorIndex(i+params.BeaconConfig().MaxProposerSlashings) /* validator index */) + generatedAttesterSlashing, err := util.GenerateAttesterSlashingForValidator(st, keys[i+params.BeaconConfig().MaxProposerSlashings], primitives.ValidatorIndex(i+params.BeaconConfig().MaxProposerSlashings) /* validator index */) require.NoError(t, err) + attesterSlashing, ok := generatedAttesterSlashing.(*ethpb.AttesterSlashing) + require.Equal(t, true, ok, "Attester slashing has the wrong type (expected %T, got %T)", ðpb.AttesterSlashing{}, generatedAttesterSlashing) attSlashings[i] = attesterSlashing - err = server.SlashingsPool.InsertAttesterSlashing(context.Background(), st, attesterSlashing) + err = server.SlashingsPool.InsertAttesterSlashing(context.Background(), st, generatedAttesterSlashing.(*ethpb.AttesterSlashing)) require.NoError(t, err) } return proposerSlashings, attSlashings diff --git a/beacon-chain/sync/subscriber_test.go b/beacon-chain/sync/subscriber_test.go index c0f7472eeb..d0bae7fe4a 100644 --- a/beacon-chain/sync/subscriber_test.go +++ b/beacon-chain/sync/subscriber_test.go @@ -170,7 +170,7 @@ func TestSubscribe_ReceivesAttesterSlashing(t *testing.T) { 1, /* validator index */ ) require.NoError(t, err, "Error generating attester slashing") - err = r.cfg.beaconDB.SaveState(ctx, beaconState, bytesutil.ToBytes32(attesterSlashing.Attestation_1.Data.BeaconBlockRoot)) + err = r.cfg.beaconDB.SaveState(ctx, beaconState, bytesutil.ToBytes32(attesterSlashing.FirstAttestation().GetData().BeaconBlockRoot)) require.NoError(t, err) p2pService.ReceivePubSub(topic, attesterSlashing) diff --git a/consensus-types/primitives/BUILD.bazel b/consensus-types/primitives/BUILD.bazel index 10ddde9c73..96506f9315 100644 --- a/consensus-types/primitives/BUILD.bazel +++ b/consensus-types/primitives/BUILD.bazel @@ -3,6 +3,8 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ + "committee_bits_mainnet.go", + "committee_bits_minimal.go", # keep "committee_index.go", "domain.go", "epoch.go", @@ -20,6 +22,7 @@ go_library( deps = [ "//math:go_default_library", "@com_github_prysmaticlabs_fastssz//:go_default_library", + "@com_github_prysmaticlabs_go_bitfield//:go_default_library", ], ) diff --git a/consensus-types/primitives/committee_bits_mainnet.go b/consensus-types/primitives/committee_bits_mainnet.go new file mode 100644 index 0000000000..b815dd12c3 --- /dev/null +++ b/consensus-types/primitives/committee_bits_mainnet.go @@ -0,0 +1,9 @@ +//go:build !minimal + +package primitives + +import "github.com/prysmaticlabs/go-bitfield" + +func NewAttestationCommitteeBits() bitfield.Bitvector64 { + return bitfield.NewBitvector64() +} diff --git a/consensus-types/primitives/committee_bits_minimal.go b/consensus-types/primitives/committee_bits_minimal.go new file mode 100644 index 0000000000..caec3090a8 --- /dev/null +++ b/consensus-types/primitives/committee_bits_minimal.go @@ -0,0 +1,9 @@ +//go:build minimal + +package primitives + +import "github.com/prysmaticlabs/go-bitfield" + +func NewAttestationCommitteeBits() bitfield.Bitvector4 { + return bitfield.NewBitvector4() +} diff --git a/testing/util/BUILD.bazel b/testing/util/BUILD.bazel index 675ea1fb6c..8456b8d178 100644 --- a/testing/util/BUILD.bazel +++ b/testing/util/BUILD.bazel @@ -17,6 +17,7 @@ go_library( "deneb_state.go", "deposits.go", "electra.go", + "electra_block.go", "electra_state.go", "helpers.go", "merge.go", diff --git a/testing/util/altair.go b/testing/util/altair.go index bef3d08b90..902c039337 100644 --- a/testing/util/altair.go +++ b/testing/util/altair.go @@ -345,19 +345,35 @@ func GenerateFullBlockAltair( numToGen = conf.NumAttesterSlashings var aSlashings []*ethpb.AttesterSlashing if numToGen > 0 { - aSlashings, err = generateAttesterSlashings(bState, privs, numToGen) + generated, err := generateAttesterSlashings(bState, privs, numToGen) if err != nil { return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) } + aSlashings = make([]*ethpb.AttesterSlashing, len(generated)) + var ok bool + for i, s := range generated { + aSlashings[i], ok = s.(*ethpb.AttesterSlashing) + if !ok { + return nil, fmt.Errorf("attester slashing has wrong type (expected %T, got %T)", ðpb.AttesterSlashing{}, s) + } + } } numToGen = conf.NumAttestations var atts []*ethpb.Attestation if numToGen > 0 { - atts, err = GenerateAttestations(bState, privs, numToGen, slot, false) + generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false) if err != nil { return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) } + atts = make([]*ethpb.Attestation, len(generatedAtts)) + var ok bool + for i, a := range generatedAtts { + atts[i], ok = a.(*ethpb.Attestation) + if !ok { + return nil, fmt.Errorf("attestation has the wrong type (expected %T, got %T)", ðpb.Attestation{}, a) + } + } } numToGen = conf.NumDeposits diff --git a/testing/util/attestation.go b/testing/util/attestation.go index 0c62520b3d..2daabe9715 100644 --- a/testing/util/attestation.go +++ b/testing/util/attestation.go @@ -2,7 +2,6 @@ package util import ( "context" - "errors" "fmt" "math" @@ -48,10 +47,8 @@ func NewAttestation() *ethpb.Attestation { // 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) { - var attestations []*ethpb.Attestation +func GenerateAttestations(bState state.BeaconState, privs []bls.SecretKey, numToGen uint64, slot primitives.Slot, randomRoot bool) ([]ethpb.Att, error) { // nolint:gocognit + var attestations []ethpb.Att generateHeadState := false bState = bState.Copy() if slot > bState.Slot() { @@ -108,8 +105,28 @@ func GenerateAttestations( return nil, err } headState = genState + case version.Deneb: + pbState, err := state_native.ProtobufBeaconStateDeneb(bState.ToProto()) + if err != nil { + return nil, err + } + genState, err := state_native.InitializeFromProtoUnsafeDeneb(pbState) + if err != nil { + return nil, err + } + headState = genState + case version.Electra: + pbState, err := state_native.ProtobufBeaconStateElectra(bState.ToProto()) + if err != nil { + return nil, err + } + genState, err := state_native.InitializeFromProtoUnsafeElectra(pbState) + if err != nil { + return nil, err + } + headState = genState default: - return nil, errors.New("state type isn't supported") + return nil, fmt.Errorf("state version %s isn't supported", version.String(bState.Version())) } headState, err = transition.ProcessSlots(context.Background(), headState, slot+1) @@ -180,9 +197,14 @@ func GenerateAttestations( return nil, err } + ci := c + if bState.Version() >= version.Electra { + // committee index must be 0 post-Electra + ci = 0 + } attData := ðpb.AttestationData{ Slot: slot, - CommitteeIndex: c, + CommitteeIndex: ci, BeaconBlockRoot: headRoot, Source: bState.CurrentJustifiedCheckpoint(), Target: ðpb.Checkpoint{ @@ -211,10 +233,22 @@ func GenerateAttestations( continue } - att := ðpb.Attestation{ - Data: attData, - AggregationBits: aggregationBits, - Signature: bls.AggregateSignatures(sigs).Marshal(), + var att ethpb.Att + if bState.Version() >= version.Electra { + cb := primitives.NewAttestationCommitteeBits() + cb.SetBitAt(uint64(c), true) + att = ðpb.AttestationElectra{ + Data: attData, + CommitteeBits: cb, + AggregationBits: aggregationBits, + Signature: bls.AggregateSignatures(sigs).Marshal(), + } + } else { + att = ðpb.Attestation{ + Data: attData, + AggregationBits: aggregationBits, + Signature: bls.AggregateSignatures(sigs).Marshal(), + } } attestations = append(attestations, att) } @@ -238,6 +272,25 @@ func HydrateAttestation(a *ethpb.Attestation) *ethpb.Attestation { return a } +// HydrateAttestationElectra hydrates an attestation object with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateAttestationElectra(a *ethpb.AttestationElectra) *ethpb.AttestationElectra { + if a.Signature == nil { + a.Signature = make([]byte, 96) + } + if a.AggregationBits == nil { + a.AggregationBits = make([]byte, 1) + } + if a.CommitteeBits == nil { + a.CommitteeBits = primitives.NewAttestationCommitteeBits() + } + if a.Data == nil { + a.Data = ðpb.AttestationData{} + } + a.Data = HydrateAttestationData(a.Data) + 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 { diff --git a/testing/util/bellatrix.go b/testing/util/bellatrix.go index 09137d5193..c860bbace8 100644 --- a/testing/util/bellatrix.go +++ b/testing/util/bellatrix.go @@ -56,19 +56,35 @@ func GenerateFullBlockBellatrix( numToGen = conf.NumAttesterSlashings var aSlashings []*ethpb.AttesterSlashing if numToGen > 0 { - aSlashings, err = generateAttesterSlashings(bState, privs, numToGen) + generated, err := generateAttesterSlashings(bState, privs, numToGen) if err != nil { return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) } + aSlashings = make([]*ethpb.AttesterSlashing, len(generated)) + var ok bool + for i, s := range generated { + aSlashings[i], ok = s.(*ethpb.AttesterSlashing) + if !ok { + return nil, fmt.Errorf("attester slashing has wrong type (expected %T, got %T)", ðpb.AttesterSlashing{}, s) + } + } } numToGen = conf.NumAttestations var atts []*ethpb.Attestation if numToGen > 0 { - atts, err = GenerateAttestations(bState, privs, numToGen, slot, false) + generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false) if err != nil { return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) } + atts = make([]*ethpb.Attestation, len(generatedAtts)) + var ok bool + for i, a := range generatedAtts { + atts[i], ok = a.(*ethpb.Attestation) + if !ok { + return nil, fmt.Errorf("attestation has the wrong type (expected %T, got %T)", ðpb.Attestation{}, a) + } + } } numToGen = conf.NumDeposits diff --git a/testing/util/block.go b/testing/util/block.go index d9d8fb97ce..e6f9d8e6b4 100644 --- a/testing/util/block.go +++ b/testing/util/block.go @@ -22,6 +22,7 @@ import ( v1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" v2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/assertions" "github.com/prysmaticlabs/prysm/v5/testing/require" ) @@ -109,19 +110,35 @@ func GenerateFullBlock( numToGen = conf.NumAttesterSlashings var aSlashings []*ethpb.AttesterSlashing if numToGen > 0 { - aSlashings, err = generateAttesterSlashings(bState, privs, numToGen) + generated, err := generateAttesterSlashings(bState, privs, numToGen) if err != nil { return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) } + aSlashings = make([]*ethpb.AttesterSlashing, len(generated)) + var ok bool + for i, s := range generated { + aSlashings[i], ok = s.(*ethpb.AttesterSlashing) + if !ok { + return nil, fmt.Errorf("attester slashing has the wrong type (expected %T, got %T)", ðpb.AttesterSlashing{}, s) + } + } } numToGen = conf.NumAttestations var atts []*ethpb.Attestation if numToGen > 0 { - atts, err = GenerateAttestations(bState, privs, numToGen, slot, false) + generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false) if err != nil { return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) } + atts = make([]*ethpb.Attestation, len(generatedAtts)) + var ok bool + for i, a := range generatedAtts { + atts[i], ok = a.(*ethpb.Attestation) + if !ok { + return nil, fmt.Errorf("attestation has the wrong type (expected %T, got %T)", ðpb.Attestation{}, a) + } + } } numToGen = conf.NumDeposits @@ -265,9 +282,59 @@ func GenerateAttesterSlashingForValidator( bState state.BeaconState, priv bls.SecretKey, idx primitives.ValidatorIndex, -) (*ethpb.AttesterSlashing, error) { +) (ethpb.AttSlashing, error) { currentEpoch := time.CurrentEpoch(bState) + if bState.Version() >= version.Electra { + att1 := ðpb.IndexedAttestationElectra{ + Data: ðpb.AttestationData{ + Slot: bState.Slot(), + CommitteeIndex: 0, + BeaconBlockRoot: make([]byte, fieldparams.RootLength), + Target: ðpb.Checkpoint{ + Epoch: currentEpoch, + Root: params.BeaconConfig().ZeroHash[:], + }, + Source: ðpb.Checkpoint{ + Epoch: currentEpoch + 1, + Root: params.BeaconConfig().ZeroHash[:], + }, + }, + AttestingIndices: []uint64{uint64(idx)}, + } + var err error + att1.Signature, err = signing.ComputeDomainAndSign(bState, currentEpoch, att1.Data, params.BeaconConfig().DomainBeaconAttester, priv) + if err != nil { + return nil, err + } + + att2 := ðpb.IndexedAttestationElectra{ + Data: ðpb.AttestationData{ + Slot: bState.Slot(), + CommitteeIndex: 0, + BeaconBlockRoot: make([]byte, fieldparams.RootLength), + Target: ðpb.Checkpoint{ + Epoch: currentEpoch, + Root: params.BeaconConfig().ZeroHash[:], + }, + Source: ðpb.Checkpoint{ + Epoch: currentEpoch, + Root: params.BeaconConfig().ZeroHash[:], + }, + }, + AttestingIndices: []uint64{uint64(idx)}, + } + att2.Signature, err = signing.ComputeDomainAndSign(bState, currentEpoch, att2.Data, params.BeaconConfig().DomainBeaconAttester, priv) + if err != nil { + return nil, err + } + + return ðpb.AttesterSlashingElectra{ + Attestation_1: att1, + Attestation_2: att2, + }, nil + } + att1 := ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ Slot: bState.Slot(), @@ -321,8 +388,8 @@ func generateAttesterSlashings( bState state.BeaconState, privs []bls.SecretKey, numSlashings uint64, -) ([]*ethpb.AttesterSlashing, error) { - attesterSlashings := make([]*ethpb.AttesterSlashing, numSlashings) +) ([]ethpb.AttSlashing, error) { + attesterSlashings := make([]ethpb.AttSlashing, numSlashings) randGen := rand.NewDeterministicGenerator() for i := uint64(0); i < numSlashings; i++ { committeeIndex := randGen.Uint64() % helpers.SlotCommitteeCount(uint64(bState.NumValidators())) diff --git a/testing/util/capella_block.go b/testing/util/capella_block.go index 0002fce546..1ceeb70693 100644 --- a/testing/util/capella_block.go +++ b/testing/util/capella_block.go @@ -24,7 +24,6 @@ import ( // GenerateFullBlockCapella generates a fully valid Capella block with the requested parameters. // Use BlockGenConfig to declare the conditions you would like the block generated under. // This function modifies the passed state as follows: - func GenerateFullBlockCapella( bState state.BeaconState, privs []bls.SecretKey, @@ -55,19 +54,35 @@ func GenerateFullBlockCapella( numToGen = conf.NumAttesterSlashings var aSlashings []*ethpb.AttesterSlashing if numToGen > 0 { - aSlashings, err = generateAttesterSlashings(bState, privs, numToGen) + generated, err := generateAttesterSlashings(bState, privs, numToGen) if err != nil { return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) } + aSlashings = make([]*ethpb.AttesterSlashing, len(generated)) + var ok bool + for i, s := range generated { + aSlashings[i], ok = s.(*ethpb.AttesterSlashing) + if !ok { + return nil, fmt.Errorf("attester slashing has the wrong type (expected %T, got %T)", ðpb.AttesterSlashing{}, s) + } + } } numToGen = conf.NumAttestations var atts []*ethpb.Attestation if numToGen > 0 { - atts, err = GenerateAttestations(bState, privs, numToGen, slot, false) + generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false) if err != nil { return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) } + atts = make([]*ethpb.Attestation, len(generatedAtts)) + var ok bool + for i, a := range generatedAtts { + atts[i], ok = a.(*ethpb.Attestation) + if !ok { + return nil, fmt.Errorf("attestation has the wrong type (expected %T, got %T)", ðpb.Attestation{}, a) + } + } } numToGen = conf.NumDeposits diff --git a/testing/util/electra_block.go b/testing/util/electra_block.go new file mode 100644 index 0000000000..e285fdfc2a --- /dev/null +++ b/testing/util/electra_block.go @@ -0,0 +1,223 @@ +package util + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" + fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" + "github.com/prysmaticlabs/prysm/v5/config/params" + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v5/crypto/bls" + "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" + v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" + ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/time/slots" +) + +// GenerateFullBlockElectra generates a fully valid Electra block with the requested parameters. +// Use BlockGenConfig to declare the conditions you would like the block generated under. +// This function modifies the passed state as follows: +func GenerateFullBlockElectra( + bState state.BeaconState, + privs []bls.SecretKey, + conf *BlockGenConfig, + slot primitives.Slot, +) (*ethpb.SignedBeaconBlockElectra, error) { + ctx := context.Background() + currentSlot := bState.Slot() + if currentSlot > slot { + return nil, fmt.Errorf("current slot in state is larger than given slot. %d > %d", currentSlot, slot) + } + bState = bState.Copy() + + if conf == nil { + conf = &BlockGenConfig{} + } + + var err error + var pSlashings []*ethpb.ProposerSlashing + numToGen := conf.NumProposerSlashings + if numToGen > 0 { + pSlashings, err = generateProposerSlashings(bState, privs, numToGen) + if err != nil { + return nil, errors.Wrapf(err, "failed generating %d proposer slashings:", numToGen) + } + } + + numToGen = conf.NumAttesterSlashings + var aSlashings []*ethpb.AttesterSlashingElectra + if numToGen > 0 { + generated, err := generateAttesterSlashings(bState, privs, numToGen) + if err != nil { + return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) + } + aSlashings = make([]*ethpb.AttesterSlashingElectra, len(generated)) + var ok bool + for i, s := range generated { + aSlashings[i], ok = s.(*ethpb.AttesterSlashingElectra) + if !ok { + return nil, fmt.Errorf("attester slashing has the wrong type (expected %T, got %T)", ðpb.AttesterSlashingElectra{}, s) + } + } + } + + numToGen = conf.NumAttestations + var atts []*ethpb.AttestationElectra + if numToGen > 0 { + generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false) + if err != nil { + return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) + } + atts = make([]*ethpb.AttestationElectra, len(generatedAtts)) + var ok bool + for i, a := range generatedAtts { + atts[i], ok = a.(*ethpb.AttestationElectra) + if !ok { + return nil, fmt.Errorf("attestation has the wrong type (expected %T, got %T)", ðpb.AttestationElectra{}, a) + } + } + } + + numToGen = conf.NumDeposits + var newDeposits []*ethpb.Deposit + eth1Data := bState.Eth1Data() + if numToGen > 0 { + newDeposits, eth1Data, err = generateDepositsAndEth1Data(bState, numToGen) + if err != nil { + return nil, errors.Wrapf(err, "failed generating %d deposits:", numToGen) + } + } + + numToGen = conf.NumVoluntaryExits + var exits []*ethpb.SignedVoluntaryExit + if numToGen > 0 { + exits, err = generateVoluntaryExits(bState, privs, numToGen) + if err != nil { + return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) + } + } + + numToGen = conf.NumTransactions + newTransactions := make([][]byte, numToGen) + for i := uint64(0); i < numToGen; i++ { + newTransactions[i] = bytesutil.Uint64ToBytesLittleEndian(i) + } + newWithdrawals := make([]*v1.Withdrawal, 0) + + random, err := helpers.RandaoMix(bState, time.CurrentEpoch(bState)) + if err != nil { + return nil, errors.Wrap(err, "could not process randao mix") + } + + timestamp, err := slots.ToTime(bState.GenesisTime(), slot) + if err != nil { + return nil, errors.Wrap(err, "could not get current timestamp") + } + + stCopy := bState.Copy() + stCopy, err = transition.ProcessSlots(context.Background(), stCopy, slot) + if err != nil { + return nil, err + } + + parentExecution, err := stCopy.LatestExecutionPayloadHeader() + if err != nil { + return nil, err + } + blockHash := indexToHash(uint64(slot)) + newExecutionPayloadCapella := &v1.ExecutionPayloadElectra{ + ParentHash: parentExecution.BlockHash(), + FeeRecipient: make([]byte, 20), + StateRoot: params.BeaconConfig().ZeroHash[:], + ReceiptsRoot: params.BeaconConfig().ZeroHash[:], + LogsBloom: make([]byte, 256), + PrevRandao: random, + BlockNumber: uint64(slot), + ExtraData: params.BeaconConfig().ZeroHash[:], + BaseFeePerGas: params.BeaconConfig().ZeroHash[:], + BlockHash: blockHash[:], + Timestamp: uint64(timestamp.Unix()), + Transactions: newTransactions, + Withdrawals: newWithdrawals, + } + var syncCommitteeBits []byte + currSize := new(ethpb.SyncAggregate).SyncCommitteeBits.Len() + switch currSize { + case 512: + syncCommitteeBits = bitfield.NewBitvector512() + case 32: + syncCommitteeBits = bitfield.NewBitvector32() + default: + return nil, errors.New("invalid bit vector size") + } + newSyncAggregate := ðpb.SyncAggregate{ + SyncCommitteeBits: syncCommitteeBits, + SyncCommitteeSignature: append([]byte{0xC0}, make([]byte, 95)...), + } + + newHeader := bState.LatestBlockHeader() + prevStateRoot, err := bState.HashTreeRoot(ctx) + if err != nil { + return nil, errors.Wrap(err, "could not hash state") + } + newHeader.StateRoot = prevStateRoot[:] + parentRoot, err := newHeader.HashTreeRoot() + if err != nil { + return nil, errors.Wrap(err, "could not hash the new header") + } + + if slot == currentSlot { + slot = currentSlot + 1 + } + + reveal, err := RandaoReveal(stCopy, time.CurrentEpoch(stCopy), privs) + if err != nil { + return nil, errors.Wrap(err, "could not compute randao reveal") + } + + idx, err := helpers.BeaconProposerIndex(ctx, stCopy) + if err != nil { + return nil, errors.Wrap(err, "could not compute beacon proposer index") + } + + changes := make([]*ethpb.SignedBLSToExecutionChange, conf.NumBLSChanges) + for i := uint64(0); i < conf.NumBLSChanges; i++ { + changes[i], err = GenerateBLSToExecutionChange(bState, privs[i+1], primitives.ValidatorIndex(i)) + if err != nil { + return nil, err + } + } + + block := ðpb.BeaconBlockElectra{ + Slot: slot, + ParentRoot: parentRoot[:], + ProposerIndex: idx, + Body: ðpb.BeaconBlockBodyElectra{ + Eth1Data: eth1Data, + RandaoReveal: reveal, + ProposerSlashings: pSlashings, + AttesterSlashings: aSlashings, + Attestations: atts, + VoluntaryExits: exits, + Deposits: newDeposits, + Graffiti: make([]byte, fieldparams.RootLength), + SyncAggregate: newSyncAggregate, + ExecutionPayload: newExecutionPayloadCapella, + BlsToExecutionChanges: changes, + }, + } + + // The fork can change after processing the state + signature, err := BlockSignature(bState, block, privs) + if err != nil { + return nil, errors.Wrap(err, "could not compute block signature") + } + + return ðpb.SignedBeaconBlockElectra{Block: block, Signature: signature.Marshal()}, nil +} diff --git a/testing/util/helpers.go b/testing/util/helpers.go index e72b5a9317..b207813a08 100644 --- a/testing/util/helpers.go +++ b/testing/util/helpers.go @@ -3,6 +3,7 @@ package util import ( "context" "encoding/binary" + "fmt" "testing" "github.com/pkg/errors" @@ -54,8 +55,12 @@ func BlockSignature( wsb, err = blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{Block: b}) case *ethpb.BeaconBlockCapella: wsb, err = blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockCapella{Block: b}) + case *ethpb.BeaconBlockDeneb: + wsb, err = blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockDeneb{Block: b}) + case *ethpb.BeaconBlockElectra: + wsb, err = blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockElectra{Block: b}) default: - return nil, errors.New("unsupported block type") + return nil, fmt.Errorf("unsupported block type %T", b) } if err != nil { return nil, errors.Wrap(err, "could not wrap block") @@ -74,6 +79,10 @@ func BlockSignature( b.StateRoot = s[:] case *ethpb.BeaconBlockCapella: b.StateRoot = s[:] + case *ethpb.BeaconBlockDeneb: + b.StateRoot = s[:] + case *ethpb.BeaconBlockElectra: + b.StateRoot = s[:] } // Temporarily increasing the beacon state slot here since BeaconProposerIndex is a @@ -88,6 +97,10 @@ func BlockSignature( blockSlot = b.Slot case *ethpb.BeaconBlockCapella: blockSlot = b.Slot + case *ethpb.BeaconBlockDeneb: + blockSlot = b.Slot + case *ethpb.BeaconBlockElectra: + blockSlot = b.Slot } // process slots to get the right fork @@ -111,6 +124,10 @@ func BlockSignature( blockRoot, err = signing.ComputeSigningRoot(b, domain) case *ethpb.BeaconBlockCapella: blockRoot, err = signing.ComputeSigningRoot(b, domain) + case *ethpb.BeaconBlockDeneb: + blockRoot, err = signing.ComputeSigningRoot(b, domain) + case *ethpb.BeaconBlockElectra: + blockRoot, err = signing.ComputeSigningRoot(b, domain) } if err != nil { return nil, err diff --git a/tools/benchmark-files-gen/main.go b/tools/benchmark-files-gen/main.go index 14556bf9fc..a845288414 100644 --- a/tools/benchmark-files-gen/main.go +++ b/tools/benchmark-files-gen/main.go @@ -122,10 +122,18 @@ func generateMarshalledFullStateAndBlock() error { var atts []*ethpb.Attestation for i := slotOffset + 1; i < slotsPerEpoch+slotOffset; i++ { - attsForSlot, err := util.GenerateAttestations(beaconState, privs, attConfig.NumAttestations, i, false) + generatedAttsForSlot, err := util.GenerateAttestations(beaconState, privs, attConfig.NumAttestations, i, false) if err != nil { return err } + attsForSlot := make([]*ethpb.Attestation, len(generatedAttsForSlot)) + for j, att := range generatedAttsForSlot { + a, ok := att.(*ethpb.Attestation) + if !ok { + return errors.New("attestation is not of type *ethpb.Attestation") + } + attsForSlot[j] = a + } atts = append(atts, attsForSlot...) }