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
This commit is contained in:
Radosław Kapka
2024-05-28 22:56:36 +09:00
committed by GitHub
parent 2f2152e039
commit 2d15e53dab
35 changed files with 924 additions and 236 deletions

View File

@@ -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. // 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 { if err != nil {
return err return err
} }
indexedAtt, err := attestation.ConvertToIndexed(ctx, a, committee) indexedAtt, err := attestation.ConvertToIndexed(ctx, a, committees...)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,9 +7,11 @@ import (
"time" "time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition" "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" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/assert" "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) { func TestStore_OnAttestation_Ok_DoublyLinkedTree(t *testing.T) {
service, tr := minimalTestService(t) eval := func(ctx context.Context, service *Service, genesisState state.BeaconState, pks []bls.SecretKey) {
ctx := tr.ctx 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 := &ethpb.Checkpoint{Epoch: 0, Root: tRoot[:]}
ofc := &ethpb.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) t.Run("pre-Electra", func(t *testing.T) {
service.SetGenesisTime(time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0)) service, tr := minimalTestService(t)
require.NoError(t, service.saveGenesisData(ctx, genesisState)) ctx := tr.ctx
att, err := util.GenerateAttestations(genesisState, pks, 1, 0, false) genesisState, pks := util.DeterministicGenesisState(t, 64)
require.NoError(t, err) eval(ctx, service, genesisState, pks)
tRoot := bytesutil.ToBytes32(att[0].Data.Target.Root) })
copied := genesisState.Copy() t.Run("post-Electra", func(t *testing.T) {
copied, err = transition.ProcessSlots(ctx, copied, 1) service, tr := minimalTestService(t)
require.NoError(t, err) ctx := tr.ctx
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot)) genesisState, pks := util.DeterministicGenesisStateElectra(t, 64)
ojc := &ethpb.Checkpoint{Epoch: 0, Root: tRoot[:]} eval(ctx, service, genesisState, pks)
ofc := &ethpb.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))
} }
func TestService_GetRecentPreState(t *testing.T) { func TestService_GetRecentPreState(t *testing.T) {

View File

@@ -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 { func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock, st state.BeaconState) error {
// Feed in block's attestations to fork choice store. // Feed in block's attestations to fork choice store.
for _, a := range blk.Body().Attestations() { 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 { if err != nil {
return err return err
} }
indices, err := attestation.AttestingIndices(a, committee) indices, err := attestation.AttestingIndices(a, committees...)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1963,68 +1963,130 @@ func TestNoViableHead_Reboot(t *testing.T) {
} }
func TestOnBlock_HandleBlockAttestations(t *testing.T) { func TestOnBlock_HandleBlockAttestations(t *testing.T) {
service, tr := minimalTestService(t) t.Run("pre-Electra", func(t *testing.T) {
ctx := tr.ctx service, tr := minimalTestService(t)
ctx := tr.ctx
st, keys := util.DeterministicGenesisState(t, 64) st, keys := util.DeterministicGenesisState(t, 64)
stateRoot, err := st.HashTreeRoot(ctx) stateRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err, "Could not hash genesis state") 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[:]) genesis := blocks.NewGenesisBlock(stateRoot[:])
wsb, err := consensusblocks.NewSignedBeaconBlock(genesis) wsb, err := consensusblocks.NewSignedBeaconBlock(genesis)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block") require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block")
parentRoot, err := genesis.Block.HashTreeRoot() parentRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root") 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.SaveState(ctx, st, parentRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, 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) st, err = service.HeadState(ctx)
require.NoError(t, err) require.NoError(t, err)
b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 1) b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 1)
require.NoError(t, err) require.NoError(t, err)
wsb, err = consensusblocks.NewSignedBeaconBlock(b) wsb, err = consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err) require.NoError(t, err)
root, err := b.Block.HashTreeRoot() root, err := b.Block.HashTreeRoot()
require.NoError(t, err) require.NoError(t, err)
preState, err := service.getBlockPreState(ctx, wsb.Block()) preState, err := service.getBlockPreState(ctx, wsb.Block())
require.NoError(t, err) require.NoError(t, err)
postState, err := service.validateStateTransition(ctx, preState, wsb) postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState)) require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})) require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
st, err = service.HeadState(ctx) st, err = service.HeadState(ctx)
require.NoError(t, err) require.NoError(t, err)
b, err = util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 2) b, err = util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 2)
require.NoError(t, err) require.NoError(t, err)
wsb, err = consensusblocks.NewSignedBeaconBlock(b) wsb, err = consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err) require.NoError(t, err)
// prepare another block that is not inserted // prepare another block that is not inserted
st3, err := transition.ExecuteStateTransition(ctx, st, wsb) st3, err := transition.ExecuteStateTransition(ctx, st, wsb)
require.NoError(t, err) require.NoError(t, err)
b3, err := util.GenerateFullBlock(st3, keys, util.DefaultBlockGenConfig(), 3) b3, err := util.GenerateFullBlock(st3, keys, util.DefaultBlockGenConfig(), 3)
require.NoError(t, err) require.NoError(t, err)
wsb3, err := consensusblocks.NewSignedBeaconBlock(b3) wsb3, err := consensusblocks.NewSignedBeaconBlock(b3)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(wsb.Block().Body().Attestations())) require.Equal(t, 1, len(wsb.Block().Body().Attestations()))
a := wsb.Block().Body().Attestations()[0] a := wsb.Block().Body().Attestations()[0]
r := bytesutil.ToBytes32(a.GetData().BeaconBlockRoot) r := bytesutil.ToBytes32(a.GetData().BeaconBlockRoot)
require.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(r)) require.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(r))
require.Equal(t, 1, len(wsb.Block().Body().Attestations())) require.Equal(t, 1, len(wsb.Block().Body().Attestations()))
a3 := wsb3.Block().Body().Attestations()[0] a3 := wsb3.Block().Body().Attestations()[0]
r3 := bytesutil.ToBytes32(a3.GetData().BeaconBlockRoot) r3 := bytesutil.ToBytes32(a3.GetData().BeaconBlockRoot)
require.Equal(t, false, service.cfg.ForkChoiceStore.HasNode(r3)) 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.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.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.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.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) { func TestFillMissingBlockPayloadId_DiffSlotExitEarly(t *testing.T) {

View File

@@ -73,7 +73,7 @@ func TestProcessAttestations_Ok(t *testing.T) {
require.NoError(t, service.saveGenesisData(ctx, genesisState)) require.NoError(t, service.saveGenesisData(ctx, genesisState))
atts, err := util.GenerateAttestations(genesisState, pks, 1, 0, false) atts, err := util.GenerateAttestations(genesisState, pks, 1, 0, false)
require.NoError(t, err) require.NoError(t, err)
tRoot := bytesutil.ToBytes32(atts[0].Data.Target.Root) tRoot := bytesutil.ToBytes32(atts[0].GetData().Target.Root)
copied := genesisState.Copy() copied := genesisState.Copy()
copied, err = transition.ProcessSlots(ctx, copied, 1) copied, err = transition.ProcessSlots(ctx, copied, 1)
require.NoError(t, err) require.NoError(t, err)
@@ -131,8 +131,8 @@ func TestService_ProcessAttestationsAndUpdateHead(t *testing.T) {
} }
require.NoError(t, service.cfg.AttPool.SaveForkchoiceAttestations(attsToSave)) require.NoError(t, service.cfg.AttPool.SaveForkchoiceAttestations(attsToSave))
// Verify the target is in forkchoice // Verify the target is in forkchoice
require.Equal(t, true, fcs.HasNode(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].Data.BeaconBlockRoot)) require.Equal(t, tRoot, bytesutil.ToBytes32(atts[0].GetData().BeaconBlockRoot))
require.Equal(t, true, fcs.HasNode(service.originBlockRoot)) require.Equal(t, true, fcs.HasNode(service.originBlockRoot))
// Insert a new block to forkchoice // Insert a new block to forkchoice

View File

@@ -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. // is done in the background to avoid adding more load to this critical code path.
ctx := context.TODO() ctx := context.TODO()
for _, att := range signed.Block().Body().Attestations() { 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 { if err != nil {
log.WithError(err).Error("Could not get attestation committee") log.WithError(err).Error("Could not get attestation committees")
return return
} }
indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee) indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committees...)
if err != nil { if err != nil {
log.WithError(err).Error("Could not convert to indexed attestation") log.WithError(err).Error("Could not convert to indexed attestation")
return return

View File

@@ -13,6 +13,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db" "github.com/prysmaticlabs/prysm/v5/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem" "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem"
testDB "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing" 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" "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree" doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/attestations" "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()), WithTrackedValidatorsCache(cache.NewTrackedValidatorsCache()),
WithBlobStorage(filesystem.NewEphemeralBlobStorage(t)), WithBlobStorage(filesystem.NewEphemeralBlobStorage(t)),
WithSyncChecker(mock.MockChecker{}), WithSyncChecker(mock.MockChecker{}),
WithExecutionEngineCaller(&mockExecution.EngineClient{}),
} }
// append the variadic opts so they override the defaults by being processed afterwards // append the variadic opts so they override the defaults by being processed afterwards
opts = append(defOpts, opts...) opts = append(defOpts, opts...)

View File

@@ -66,11 +66,11 @@ func ProcessAttestationNoVerifySignature(
if err != nil { if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
indices, err := attestation.AttestingIndices(att, committee) indices, err := attestation.AttestingIndices(att, committees...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -195,47 +195,95 @@ func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) {
} }
func TestProcessAttestations_OK(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 := bitfield.NewBitlist(3)
aggBits.SetBitAt(0, true) aggBits.SetBitAt(0, true)
var mockRoot [32]byte var mockRoot [32]byte
copy(mockRoot[:], "hello-world") copy(mockRoot[:], "hello-world")
att := util.HydrateAttestation(&ethpb.Attestation{ att := util.HydrateAttestation(&ethpb.Attestation{
Data: &ethpb.AttestationData{ Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Root: mockRoot[:]}, Source: &ethpb.Checkpoint{Root: mockRoot[:]},
Target: &ethpb.Checkpoint{Root: mockRoot[:]}, Target: &ethpb.Checkpoint{Root: mockRoot[:]},
}, },
AggregationBits: aggBits, 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() aggBits := bitfield.NewBitlist(3)
cfc.Root = mockRoot[:] aggBits.SetBitAt(0, true)
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cfc)) committeeBits := primitives.NewAttestationCommitteeBits()
committeeBits.SetBitAt(0, true)
var mockRoot [32]byte
copy(mockRoot[:], "hello-world")
att := util.HydrateAttestationElectra(&ethpb.AttestationElectra{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Root: mockRoot[:]},
Target: &ethpb.Checkpoint{Root: mockRoot[:]},
},
AggregationBits: aggBits,
CommitteeBits: committeeBits,
})
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex) cfc := beaconState.CurrentJustifiedCheckpoint()
require.NoError(t, err) cfc.Root = mockRoot[:]
attestingIndices, err := attestation.AttestingIndices(att, committee) require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cfc))
require.NoError(t, err)
sigs := make([]bls.Signature, len(attestingIndices)) committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, 0)
for i, indice := range attestingIndices {
sb, err := signing.ComputeDomainAndSign(beaconState, 0, att.Data, params.BeaconConfig().DomainBeaconAttester, privKeys[indice])
require.NoError(t, err) require.NoError(t, err)
sig, err := bls.SignatureFromBytes(sb) attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err) require.NoError(t, err)
sigs[i] = sig sigs := make([]bls.Signature, len(attestingIndices))
} for i, indice := range attestingIndices {
att.Signature = bls.AggregateSignatures(sigs).Marshal() 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 := util.NewBeaconBlockElectra()
block.Block.Body.Attestations = []*ethpb.Attestation{att} block.Block.Body.Attestations = []*ethpb.AttestationElectra{att}
err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay) err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
require.NoError(t, err) require.NoError(t, err)
wsb, err := blocks.NewSignedBeaconBlock(block) wsb, err := blocks.NewSignedBeaconBlock(block)
require.NoError(t, err) require.NoError(t, err)
_, err = altair.ProcessAttestationsNoVerifySignature(context.Background(), beaconState, wsb.Block()) _, err = altair.ProcessAttestationsNoVerifySignature(context.Background(), beaconState, wsb.Block())
require.NoError(t, err) require.NoError(t, err)
})
} }
func TestProcessAttestationNoVerify_SourceTargetHead(t *testing.T) { func TestProcessAttestationNoVerify_SourceTargetHead(t *testing.T) {

View File

@@ -154,7 +154,7 @@ func TranslateParticipation(ctx context.Context, state state.BeaconState, atts [
if err != nil { if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }

View File

@@ -200,23 +200,6 @@ func ProcessAttestationNoVerifySignature(
return beaconState, nil 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. // VerifyIndexedAttestation determines the validity of an indexed attestation.
// //
// Spec pseudocode definition: // Spec pseudocode definition:

View File

@@ -578,53 +578,109 @@ func TestRetrieveAttestationSignatureSet_VerifiesMultipleAttestations(t *testing
} }
} }
st, err := util.NewBeaconState() t.Run("pre-Electra", func(t *testing.T) {
require.NoError(t, err) st, err := util.NewBeaconState()
require.NoError(t, st.SetSlot(5)) require.NoError(t, err)
require.NoError(t, st.SetValidators(validators)) require.NoError(t, st.SetSlot(5))
require.NoError(t, st.SetValidators(validators))
comm1, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 0 /*committeeIndex*/) comm1, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 0 /*committeeIndex*/)
require.NoError(t, err) require.NoError(t, err)
att1 := util.HydrateAttestation(&ethpb.Attestation{ att1 := util.HydrateAttestation(&ethpb.Attestation{
AggregationBits: bitfield.NewBitlist(uint64(len(comm1))), AggregationBits: bitfield.NewBitlist(uint64(len(comm1))),
Data: &ethpb.AttestationData{ Data: &ethpb.AttestationData{
Slot: 1, 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(&ethpb.Attestation{
AggregationBits: bitfield.NewBitlist(uint64(len(comm2))),
Data: &ethpb.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()) t.Run("post-Electra", func(t *testing.T) {
require.NoError(t, err) st, err := util.NewBeaconStateElectra()
root, err := signing.ComputeSigningRoot(att1.Data, domain) require.NoError(t, err)
require.NoError(t, err) require.NoError(t, st.SetSlot(5))
var sigs []bls.Signature require.NoError(t, st.SetValidators(validators))
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*/) comm1, err := helpers.BeaconCommitteeFromState(context.Background(), st, 1 /*slot*/, 0 /*committeeIndex*/)
require.NoError(t, err) require.NoError(t, err)
att2 := util.HydrateAttestation(&ethpb.Attestation{ commBits1 := primitives.NewAttestationCommitteeBits()
AggregationBits: bitfield.NewBitlist(uint64(len(comm2))), commBits1.SetBitAt(0, true)
Data: &ethpb.AttestationData{ att1 := util.HydrateAttestationElectra(&ethpb.AttestationElectra{
Slot: 1, AggregationBits: bitfield.NewBitlist(uint64(len(comm1))),
CommitteeIndex: 1, CommitteeBits: commBits1,
}, Data: &ethpb.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(&ethpb.AttestationElectra{
AggregationBits: bitfield.NewBitlist(uint64(len(comm2))),
CommitteeBits: commBits2,
Data: &ethpb.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) { func TestRetrieveAttestationSignatureSet_AcrossFork(t *testing.T) {

View File

@@ -297,21 +297,6 @@ func TestFuzzVerifyIndexedAttestationn_10000(t *testing.T) {
} }
} }
func TestFuzzVerifyAttestation_10000(t *testing.T) {
fuzzer := fuzz.NewWithSeed(0)
state := &ethpb.BeaconState{}
attestation := &ethpb.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) { func TestFuzzProcessDeposits_10000(t *testing.T) {
fuzzer := fuzz.NewWithSeed(0) fuzzer := fuzz.NewWithSeed(0)
state := &ethpb.BeaconState{} state := &ethpb.BeaconState{}

View File

@@ -163,7 +163,42 @@ func NewGenesisBlockForState(ctx context.Context, st state.BeaconState) (interfa
SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8), SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8),
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), 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(&ethpb.SignedBeaconBlockElectra{
Block: &ethpb.BeaconBlockElectra{
ParentRoot: params.BeaconConfig().ZeroHash[:],
StateRoot: root[:],
Body: &ethpb.BeaconBlockBodyElectra{
RandaoReveal: make([]byte, 96),
Eth1Data: &ethpb.Eth1Data{
DepositRoot: make([]byte, 32),
BlockHash: make([]byte, 32),
},
Graffiti: make([]byte, 32),
SyncAggregate: &ethpb.SyncAggregate{
SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8),
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
},
ExecutionPayload: &enginev1.ExecutionPayloadElectra{
ParentHash: make([]byte, 32), ParentHash: make([]byte, 32),
FeeRecipient: make([]byte, 20), FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, 32), StateRoot: make([]byte, 32),

View File

@@ -192,11 +192,11 @@ func createAttestationSignatureBatch(
descs := make([]string, len(atts)) descs := make([]string, len(atts))
for i, a := range atts { for i, a := range atts {
sigs[i] = a.GetSignature() 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 { if err != nil {
return nil, err return nil, err
} }
ia, err := attestation.ConvertToIndexed(ctx, a, c) ia, err := attestation.ConvertToIndexed(ctx, a, committees...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -470,7 +470,7 @@ func UnslashedAttestingIndices(ctx context.Context, state state.ReadOnlyBeaconSt
seen := make(map[uint64]bool) seen := make(map[uint64]bool)
for _, att := range atts { 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 { if err != nil {
return nil, err return nil, err
} }

View File

@@ -34,6 +34,7 @@ go_library(
"//encoding/bytesutil:go_default_library", "//encoding/bytesutil:go_default_library",
"//math:go_default_library", "//math:go_default_library",
"//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//time:go_default_library", "//time:go_default_library",
"//time/slots:go_default_library", "//time/slots:go_default_library",
"@com_github_pkg_errors//:go_default_library", "@com_github_pkg_errors//:go_default_library",

View File

@@ -21,6 +21,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/math" "github.com/prysmaticlabs/prysm/v5/math"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots" "github.com/prysmaticlabs/prysm/v5/time/slots"
) )
@@ -58,6 +59,29 @@ func SlotCommitteeCount(activeValidatorCount uint64) uint64 {
return committeesPerSlot 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 // 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 // is a spec implementation where state is used as an argument. In case of state retrieval
// becomes expensive, consider using BeaconCommittee below. // becomes expensive, consider using BeaconCommittee below.

View File

@@ -715,3 +715,37 @@ func TestCommitteeIndices(t *testing.T) {
indices := helpers.CommitteeIndices(bitfield) indices := helpers.CommitteeIndices(bitfield)
assert.DeepEqual(t, []primitives.CommitteeIndex{0, 1, 3}, indices) 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] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
state, err := state_native.InitializeFromProtoPhase0(&ethpb.BeaconState{
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
})
require.NoError(t, err)
t.Run("pre-Electra", func(t *testing.T) {
att := &ethpb.Attestation{Data: &ethpb.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 := &ethpb.AttestationElectra{CommitteeBits: bits, Data: &ethpb.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])))
})
}

View File

@@ -17,9 +17,9 @@ import (
func validAttesterSlashingForValIdx(t *testing.T, beaconState state.BeaconState, privs []bls.SecretKey, valIdx ...uint64) *ethpb.AttesterSlashing { func validAttesterSlashingForValIdx(t *testing.T, beaconState state.BeaconState, privs []bls.SecretKey, valIdx ...uint64) *ethpb.AttesterSlashing {
var slashings []*ethpb.AttesterSlashing var slashings []*ethpb.AttesterSlashing
for _, idx := range valIdx { 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) require.NoError(t, err)
slashings = append(slashings, slashing) slashings = append(slashings, generatedSlashing.(*ethpb.AttesterSlashing))
} }
var allSig1 []bls.Signature var allSig1 []bls.Signature
var allSig2 []bls.Signature var allSig2 []bls.Signature
@@ -78,12 +78,14 @@ func TestPool_InsertAttesterSlashing(t *testing.T) {
pendingSlashings := make([]*PendingAttesterSlashing, 20) pendingSlashings := make([]*PendingAttesterSlashing, 20)
slashings := make([]*ethpb.AttesterSlashing, 20) slashings := make([]*ethpb.AttesterSlashing, 20)
for i := 0; i < len(pendingSlashings); i++ { 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) require.NoError(t, err)
pendingSlashings[i] = &PendingAttesterSlashing{ pendingSlashings[i] = &PendingAttesterSlashing{
attesterSlashing: sl, attesterSlashing: generatedSl,
validatorToSlash: primitives.ValidatorIndex(i), validatorToSlash: primitives.ValidatorIndex(i),
} }
sl, ok := generatedSl.(*ethpb.AttesterSlashing)
require.Equal(t, true, ok, "Attester slashing has the wrong type (expected %T, got %T)", &ethpb.AttesterSlashing{}, generatedSl)
slashings[i] = sl slashings[i] = sl
} }
require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch)) require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch))
@@ -303,12 +305,16 @@ func TestPool_InsertAttesterSlashing_SigFailsVerify_ClearPool(t *testing.T) {
pendingSlashings := make([]*PendingAttesterSlashing, 2) pendingSlashings := make([]*PendingAttesterSlashing, 2)
slashings := make([]*ethpb.AttesterSlashing, 2) slashings := make([]*ethpb.AttesterSlashing, 2)
for i := 0; i < 2; i++ { 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) require.NoError(t, err)
pendingSlashings[i] = &PendingAttesterSlashing{ pendingSlashings[i] = &PendingAttesterSlashing{
attesterSlashing: sl, attesterSlashing: generatedSl,
validatorToSlash: primitives.ValidatorIndex(i), 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)", &ethpb.AttesterSlashing{}, generatedSl)
}
slashings[i] = sl slashings[i] = sl
} }
// We mess up the signature of the second slashing. // We mess up the signature of the second slashing.

View File

@@ -66,13 +66,13 @@ func TestServer_SubmitAttesterSlashing(t *testing.T) {
Broadcaster: mb, 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) require.NoError(t, err)
// We want the intersection of the slashing attesting indices // We want the intersection of the slashing attesting indices
// to be slashed, so we expect validators 2 and 3 to be in the response // to be slashed, so we expect validators 2 and 3 to be in the response
// slashed indices. // slashed indices.
_, err = bs.SubmitAttesterSlashing(ctx, slashing) _, err = bs.SubmitAttesterSlashing(ctx, generatedSlashing.(*ethpb.AttesterSlashing))
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, true, mb.BroadcastCalled.Load(), "Expected broadcast to be called when flag is set") 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, 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) require.NoError(t, err)
// We want the intersection of the slashing attesting indices // We want the intersection of the slashing attesting indices
@@ -153,17 +153,17 @@ func TestServer_SubmitAttesterSlashing_DontBroadcast(t *testing.T) {
wanted := &ethpb.SubmitSlashingResponse{ wanted := &ethpb.SubmitSlashingResponse{
SlashedIndices: []primitives.ValidatorIndex{2}, SlashedIndices: []primitives.ValidatorIndex{2},
} }
res, err := bs.SubmitAttesterSlashing(ctx, slashing) res, err := bs.SubmitAttesterSlashing(ctx, generatedSlashing.(*ethpb.AttesterSlashing))
require.NoError(t, err) require.NoError(t, err)
if !proto.Equal(wanted, res) { if !proto.Equal(wanted, res) {
t.Errorf("Wanted %v, received %v", 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") 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) require.NoError(t, err)
// If any of the attesting indices in the slashing object have already // 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. // 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") assert.NotNil(t, err, "Expected including a attester slashing for an already slashed validator to fail")
} }

View File

@@ -753,10 +753,12 @@ func injectSlashings(t *testing.T, st state.BeaconState, keys []bls.SecretKey, s
attSlashings := make([]*ethpb.AttesterSlashing, params.BeaconConfig().MaxAttesterSlashings) attSlashings := make([]*ethpb.AttesterSlashing, params.BeaconConfig().MaxAttesterSlashings)
for i := uint64(0); i < params.BeaconConfig().MaxAttesterSlashings; i++ { 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) require.NoError(t, err)
attesterSlashing, ok := generatedAttesterSlashing.(*ethpb.AttesterSlashing)
require.Equal(t, true, ok, "Attester slashing has the wrong type (expected %T, got %T)", &ethpb.AttesterSlashing{}, generatedAttesterSlashing)
attSlashings[i] = attesterSlashing 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) require.NoError(t, err)
} }
return proposerSlashings, attSlashings return proposerSlashings, attSlashings

View File

@@ -170,7 +170,7 @@ func TestSubscribe_ReceivesAttesterSlashing(t *testing.T) {
1, /* validator index */ 1, /* validator index */
) )
require.NoError(t, err, "Error generating attester slashing") 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) require.NoError(t, err)
p2pService.ReceivePubSub(topic, attesterSlashing) p2pService.ReceivePubSub(topic, attesterSlashing)

View File

@@ -3,6 +3,8 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"committee_bits_mainnet.go",
"committee_bits_minimal.go", # keep
"committee_index.go", "committee_index.go",
"domain.go", "domain.go",
"epoch.go", "epoch.go",
@@ -20,6 +22,7 @@ go_library(
deps = [ deps = [
"//math:go_default_library", "//math:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library", "@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
], ],
) )

View File

@@ -0,0 +1,9 @@
//go:build !minimal
package primitives
import "github.com/prysmaticlabs/go-bitfield"
func NewAttestationCommitteeBits() bitfield.Bitvector64 {
return bitfield.NewBitvector64()
}

View File

@@ -0,0 +1,9 @@
//go:build minimal
package primitives
import "github.com/prysmaticlabs/go-bitfield"
func NewAttestationCommitteeBits() bitfield.Bitvector4 {
return bitfield.NewBitvector4()
}

View File

@@ -17,6 +17,7 @@ go_library(
"deneb_state.go", "deneb_state.go",
"deposits.go", "deposits.go",
"electra.go", "electra.go",
"electra_block.go",
"electra_state.go", "electra_state.go",
"helpers.go", "helpers.go",
"merge.go", "merge.go",

View File

@@ -345,19 +345,35 @@ func GenerateFullBlockAltair(
numToGen = conf.NumAttesterSlashings numToGen = conf.NumAttesterSlashings
var aSlashings []*ethpb.AttesterSlashing var aSlashings []*ethpb.AttesterSlashing
if numToGen > 0 { if numToGen > 0 {
aSlashings, err = generateAttesterSlashings(bState, privs, numToGen) generated, err := generateAttesterSlashings(bState, privs, numToGen)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) 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)", &ethpb.AttesterSlashing{}, s)
}
}
} }
numToGen = conf.NumAttestations numToGen = conf.NumAttestations
var atts []*ethpb.Attestation var atts []*ethpb.Attestation
if numToGen > 0 { if numToGen > 0 {
atts, err = GenerateAttestations(bState, privs, numToGen, slot, false) generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) 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)", &ethpb.Attestation{}, a)
}
}
} }
numToGen = conf.NumDeposits numToGen = conf.NumDeposits

View File

@@ -2,7 +2,6 @@ package util
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"math" "math"
@@ -48,10 +47,8 @@ func NewAttestation() *ethpb.Attestation {
// for the same data with their aggregation bits split uniformly. // 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. // If you request 4 attestations, but there are 8 committees, you will get 4 fully aggregated attestations.
func GenerateAttestations( func GenerateAttestations(bState state.BeaconState, privs []bls.SecretKey, numToGen uint64, slot primitives.Slot, randomRoot bool) ([]ethpb.Att, error) { // nolint:gocognit
bState state.BeaconState, privs []bls.SecretKey, numToGen uint64, slot primitives.Slot, randomRoot bool, var attestations []ethpb.Att
) ([]*ethpb.Attestation, error) {
var attestations []*ethpb.Attestation
generateHeadState := false generateHeadState := false
bState = bState.Copy() bState = bState.Copy()
if slot > bState.Slot() { if slot > bState.Slot() {
@@ -108,8 +105,28 @@ func GenerateAttestations(
return nil, err return nil, err
} }
headState = genState 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: 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) headState, err = transition.ProcessSlots(context.Background(), headState, slot+1)
@@ -180,9 +197,14 @@ func GenerateAttestations(
return nil, err return nil, err
} }
ci := c
if bState.Version() >= version.Electra {
// committee index must be 0 post-Electra
ci = 0
}
attData := &ethpb.AttestationData{ attData := &ethpb.AttestationData{
Slot: slot, Slot: slot,
CommitteeIndex: c, CommitteeIndex: ci,
BeaconBlockRoot: headRoot, BeaconBlockRoot: headRoot,
Source: bState.CurrentJustifiedCheckpoint(), Source: bState.CurrentJustifiedCheckpoint(),
Target: &ethpb.Checkpoint{ Target: &ethpb.Checkpoint{
@@ -211,10 +233,22 @@ func GenerateAttestations(
continue continue
} }
att := &ethpb.Attestation{ var att ethpb.Att
Data: attData, if bState.Version() >= version.Electra {
AggregationBits: aggregationBits, cb := primitives.NewAttestationCommitteeBits()
Signature: bls.AggregateSignatures(sigs).Marshal(), cb.SetBitAt(uint64(c), true)
att = &ethpb.AttestationElectra{
Data: attData,
CommitteeBits: cb,
AggregationBits: aggregationBits,
Signature: bls.AggregateSignatures(sigs).Marshal(),
}
} else {
att = &ethpb.Attestation{
Data: attData,
AggregationBits: aggregationBits,
Signature: bls.AggregateSignatures(sigs).Marshal(),
}
} }
attestations = append(attestations, att) attestations = append(attestations, att)
} }
@@ -238,6 +272,25 @@ func HydrateAttestation(a *ethpb.Attestation) *ethpb.Attestation {
return a 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 = &ethpb.AttestationData{}
}
a.Data = HydrateAttestationData(a.Data)
return a
}
// HydrateV1Attestation hydrates a v1 attestation object with correct field length sizes // HydrateV1Attestation hydrates a v1 attestation object with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules. // to comply with fssz marshalling and unmarshalling rules.
func HydrateV1Attestation(a *attv1.Attestation) *attv1.Attestation { func HydrateV1Attestation(a *attv1.Attestation) *attv1.Attestation {

View File

@@ -56,19 +56,35 @@ func GenerateFullBlockBellatrix(
numToGen = conf.NumAttesterSlashings numToGen = conf.NumAttesterSlashings
var aSlashings []*ethpb.AttesterSlashing var aSlashings []*ethpb.AttesterSlashing
if numToGen > 0 { if numToGen > 0 {
aSlashings, err = generateAttesterSlashings(bState, privs, numToGen) generated, err := generateAttesterSlashings(bState, privs, numToGen)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) 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)", &ethpb.AttesterSlashing{}, s)
}
}
} }
numToGen = conf.NumAttestations numToGen = conf.NumAttestations
var atts []*ethpb.Attestation var atts []*ethpb.Attestation
if numToGen > 0 { if numToGen > 0 {
atts, err = GenerateAttestations(bState, privs, numToGen, slot, false) generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) 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)", &ethpb.Attestation{}, a)
}
}
} }
numToGen = conf.NumDeposits numToGen = conf.NumDeposits

View File

@@ -22,6 +22,7 @@ import (
v1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" v1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1"
v2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" v2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" 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/assertions"
"github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/require"
) )
@@ -109,19 +110,35 @@ func GenerateFullBlock(
numToGen = conf.NumAttesterSlashings numToGen = conf.NumAttesterSlashings
var aSlashings []*ethpb.AttesterSlashing var aSlashings []*ethpb.AttesterSlashing
if numToGen > 0 { if numToGen > 0 {
aSlashings, err = generateAttesterSlashings(bState, privs, numToGen) generated, err := generateAttesterSlashings(bState, privs, numToGen)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) 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)", &ethpb.AttesterSlashing{}, s)
}
}
} }
numToGen = conf.NumAttestations numToGen = conf.NumAttestations
var atts []*ethpb.Attestation var atts []*ethpb.Attestation
if numToGen > 0 { if numToGen > 0 {
atts, err = GenerateAttestations(bState, privs, numToGen, slot, false) generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) 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)", &ethpb.Attestation{}, a)
}
}
} }
numToGen = conf.NumDeposits numToGen = conf.NumDeposits
@@ -265,9 +282,59 @@ func GenerateAttesterSlashingForValidator(
bState state.BeaconState, bState state.BeaconState,
priv bls.SecretKey, priv bls.SecretKey,
idx primitives.ValidatorIndex, idx primitives.ValidatorIndex,
) (*ethpb.AttesterSlashing, error) { ) (ethpb.AttSlashing, error) {
currentEpoch := time.CurrentEpoch(bState) currentEpoch := time.CurrentEpoch(bState)
if bState.Version() >= version.Electra {
att1 := &ethpb.IndexedAttestationElectra{
Data: &ethpb.AttestationData{
Slot: bState.Slot(),
CommitteeIndex: 0,
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
Target: &ethpb.Checkpoint{
Epoch: currentEpoch,
Root: params.BeaconConfig().ZeroHash[:],
},
Source: &ethpb.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 := &ethpb.IndexedAttestationElectra{
Data: &ethpb.AttestationData{
Slot: bState.Slot(),
CommitteeIndex: 0,
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
Target: &ethpb.Checkpoint{
Epoch: currentEpoch,
Root: params.BeaconConfig().ZeroHash[:],
},
Source: &ethpb.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 &ethpb.AttesterSlashingElectra{
Attestation_1: att1,
Attestation_2: att2,
}, nil
}
att1 := &ethpb.IndexedAttestation{ att1 := &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{ Data: &ethpb.AttestationData{
Slot: bState.Slot(), Slot: bState.Slot(),
@@ -321,8 +388,8 @@ func generateAttesterSlashings(
bState state.BeaconState, bState state.BeaconState,
privs []bls.SecretKey, privs []bls.SecretKey,
numSlashings uint64, numSlashings uint64,
) ([]*ethpb.AttesterSlashing, error) { ) ([]ethpb.AttSlashing, error) {
attesterSlashings := make([]*ethpb.AttesterSlashing, numSlashings) attesterSlashings := make([]ethpb.AttSlashing, numSlashings)
randGen := rand.NewDeterministicGenerator() randGen := rand.NewDeterministicGenerator()
for i := uint64(0); i < numSlashings; i++ { for i := uint64(0); i < numSlashings; i++ {
committeeIndex := randGen.Uint64() % helpers.SlotCommitteeCount(uint64(bState.NumValidators())) committeeIndex := randGen.Uint64() % helpers.SlotCommitteeCount(uint64(bState.NumValidators()))

View File

@@ -24,7 +24,6 @@ import (
// GenerateFullBlockCapella generates a fully valid Capella block with the requested parameters. // GenerateFullBlockCapella generates a fully valid Capella block with the requested parameters.
// Use BlockGenConfig to declare the conditions you would like the block generated under. // Use BlockGenConfig to declare the conditions you would like the block generated under.
// This function modifies the passed state as follows: // This function modifies the passed state as follows:
func GenerateFullBlockCapella( func GenerateFullBlockCapella(
bState state.BeaconState, bState state.BeaconState,
privs []bls.SecretKey, privs []bls.SecretKey,
@@ -55,19 +54,35 @@ func GenerateFullBlockCapella(
numToGen = conf.NumAttesterSlashings numToGen = conf.NumAttesterSlashings
var aSlashings []*ethpb.AttesterSlashing var aSlashings []*ethpb.AttesterSlashing
if numToGen > 0 { if numToGen > 0 {
aSlashings, err = generateAttesterSlashings(bState, privs, numToGen) generated, err := generateAttesterSlashings(bState, privs, numToGen)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen) 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)", &ethpb.AttesterSlashing{}, s)
}
}
} }
numToGen = conf.NumAttestations numToGen = conf.NumAttestations
var atts []*ethpb.Attestation var atts []*ethpb.Attestation
if numToGen > 0 { if numToGen > 0 {
atts, err = GenerateAttestations(bState, privs, numToGen, slot, false) generatedAtts, err := GenerateAttestations(bState, privs, numToGen, slot, false)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen) 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)", &ethpb.Attestation{}, a)
}
}
} }
numToGen = conf.NumDeposits numToGen = conf.NumDeposits

View File

@@ -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)", &ethpb.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)", &ethpb.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 := &ethpb.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 := &ethpb.BeaconBlockElectra{
Slot: slot,
ParentRoot: parentRoot[:],
ProposerIndex: idx,
Body: &ethpb.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 &ethpb.SignedBeaconBlockElectra{Block: block, Signature: signature.Marshal()}, nil
}

View File

@@ -3,6 +3,7 @@ package util
import ( import (
"context" "context"
"encoding/binary" "encoding/binary"
"fmt"
"testing" "testing"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -54,8 +55,12 @@ func BlockSignature(
wsb, err = blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: b}) wsb, err = blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: b})
case *ethpb.BeaconBlockCapella: case *ethpb.BeaconBlockCapella:
wsb, err = blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockCapella{Block: b}) wsb, err = blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockCapella{Block: b})
case *ethpb.BeaconBlockDeneb:
wsb, err = blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockDeneb{Block: b})
case *ethpb.BeaconBlockElectra:
wsb, err = blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockElectra{Block: b})
default: default:
return nil, errors.New("unsupported block type") return nil, fmt.Errorf("unsupported block type %T", b)
} }
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not wrap block") return nil, errors.Wrap(err, "could not wrap block")
@@ -74,6 +79,10 @@ func BlockSignature(
b.StateRoot = s[:] b.StateRoot = s[:]
case *ethpb.BeaconBlockCapella: case *ethpb.BeaconBlockCapella:
b.StateRoot = s[:] 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 // Temporarily increasing the beacon state slot here since BeaconProposerIndex is a
@@ -88,6 +97,10 @@ func BlockSignature(
blockSlot = b.Slot blockSlot = b.Slot
case *ethpb.BeaconBlockCapella: case *ethpb.BeaconBlockCapella:
blockSlot = b.Slot blockSlot = b.Slot
case *ethpb.BeaconBlockDeneb:
blockSlot = b.Slot
case *ethpb.BeaconBlockElectra:
blockSlot = b.Slot
} }
// process slots to get the right fork // process slots to get the right fork
@@ -111,6 +124,10 @@ func BlockSignature(
blockRoot, err = signing.ComputeSigningRoot(b, domain) blockRoot, err = signing.ComputeSigningRoot(b, domain)
case *ethpb.BeaconBlockCapella: case *ethpb.BeaconBlockCapella:
blockRoot, err = signing.ComputeSigningRoot(b, domain) 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 { if err != nil {
return nil, err return nil, err

View File

@@ -122,10 +122,18 @@ func generateMarshalledFullStateAndBlock() error {
var atts []*ethpb.Attestation var atts []*ethpb.Attestation
for i := slotOffset + 1; i < slotsPerEpoch+slotOffset; i++ { 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 { if err != nil {
return err 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...) atts = append(atts, attsForSlot...)
} }