diff --git a/beacon-chain/blockchain/process_attestation.go b/beacon-chain/blockchain/process_attestation.go index d4bd636b7783..f04c9d8a2389 100644 --- a/beacon-chain/blockchain/process_attestation.go +++ b/beacon-chain/blockchain/process_attestation.go @@ -97,7 +97,7 @@ func (s *Service) OnAttestation(ctx context.Context, a ethpb.Att, disparity time // We assume trusted attestation in this function has verified signature. // Update forkchoice store with the new attestation for updating weight. - s.cfg.ForkChoiceStore.ProcessAttestation(ctx, indexedAtt.GetAttestingIndices(), bytesutil.ToBytes32(a.GetData().BeaconBlockRoot), a.GetData().Target.Epoch) + s.cfg.ForkChoiceStore.ProcessAttestation(ctx, indexedAtt.GetAttestingIndices(), bytesutil.ToBytes32(a.GetData().BeaconBlockRoot), a.GetData().Slot) return nil } diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index 454928b0a39d..2acc89132ea4 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -375,7 +375,7 @@ func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.Re } r := bytesutil.ToBytes32(a.GetData().BeaconBlockRoot) if s.cfg.ForkChoiceStore.HasNode(r) { - s.cfg.ForkChoiceStore.ProcessAttestation(ctx, indices, r, a.GetData().Target.Epoch) + s.cfg.ForkChoiceStore.ProcessAttestation(ctx, indices, r, a.GetData().Slot) } else if err := s.cfg.AttPool.SaveBlockAttestation(a); err != nil { return err } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go index 807e0d80f9b3..2ab19ecf01c6 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go @@ -80,7 +80,7 @@ func (f *ForkChoice) Head( // ProcessAttestation processes attestation for vote accounting, it iterates around validator indices // and update their votes accordingly. -func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []uint64, blockRoot [32]byte, targetEpoch primitives.Epoch) { +func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []uint64, blockRoot [32]byte, attSlot primitives.Slot) { _, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.ProcessAttestation") defer span.End() @@ -94,9 +94,9 @@ func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices [] newVote := f.votes[index].nextRoot == params.BeaconConfig().ZeroHash && f.votes[index].currentRoot == params.BeaconConfig().ZeroHash - // Vote gets updated if it's newly allocated or high target epoch. - if newVote || targetEpoch > f.votes[index].nextEpoch { - f.votes[index].nextEpoch = targetEpoch + // Vote gets updated if it's newly allocated or higher attestation slot. + if newVote || attSlot > f.votes[index].slot { + f.votes[index].slot = attSlot f.votes[index].nextRoot = blockRoot } } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go index 28b1be753596..acac229530ee 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go @@ -9,6 +9,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/testing/assert" "github.com/prysmaticlabs/prysm/v5/testing/require" + "github.com/prysmaticlabs/prysm/v5/time/slots" ) // Helper function to simulate the block being on time or delayed for proposer @@ -61,7 +62,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { ) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - f.ProcessAttestation(ctx, []uint64{0}, newRoot, fEpoch) + fSlot, err := slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, []uint64{0}, newRoot, fSlot) headRoot, err = f.Head(ctx) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 1") @@ -87,7 +90,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { ) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - f.ProcessAttestation(ctx, []uint64{1}, newRoot, fEpoch) + fSlot, err = slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, []uint64{1}, newRoot, fSlot) headRoot, err = f.Head(ctx) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 2") @@ -115,7 +120,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { ) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - f.ProcessAttestation(ctx, []uint64{2}, newRoot, fEpoch) + fSlot, err = slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, []uint64{2}, newRoot, fSlot) headRoot, err = f.Head(ctx) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 3") @@ -144,7 +151,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { ) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - f.ProcessAttestation(ctx, []uint64{3}, newRoot, fEpoch) + fSlot, err = slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, []uint64{3}, newRoot, fSlot) headRoot, err = f.Head(ctx) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 3") @@ -174,7 +183,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // Regression: process attestations for C, check that it // becomes head, we need two attestations to have C.weight = 30 > 24 = D.weight - f.ProcessAttestation(ctx, []uint64{4, 5}, indexToHash(3), fEpoch) + fSlot, err = slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, []uint64{4, 5}, indexToHash(3), fSlot) headRoot, err = f.Head(ctx) require.NoError(t, err) assert.Equal(t, indexToHash(3), headRoot, "Incorrect head for justified epoch at slot 4") @@ -235,10 +246,14 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // The maliciously withheld block has one vote. votes := []uint64{1} - f.ProcessAttestation(ctx, votes, maliciouslyWithheldBlock, fEpoch) + fSlot, err := slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, votes, maliciouslyWithheldBlock, fSlot) // The honest block has one vote. votes = []uint64{2} - f.ProcessAttestation(ctx, votes, honestBlock, fEpoch) + fSlot, err = slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, votes, honestBlock, fSlot) // Ensure the head is STILL C, the honest block, as the honest block had proposer boost. r, err = f.Head(ctx) @@ -304,7 +319,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // An attestation is received for B that has more voting power than C with the proposer boost, // allowing B to then become the head if their attestation has enough adversarial votes. votes := []uint64{1, 2} - f.ProcessAttestation(ctx, votes, maliciouslyWithheldBlock, fEpoch) + fSlot, err := slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, votes, maliciouslyWithheldBlock, fSlot) // Expect the head to have switched to B. r, err = f.Head(ctx) @@ -379,7 +396,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // An attestation for C is received at slot N+3. votes := []uint64{1} - f.ProcessAttestation(ctx, votes, c, fEpoch) + fSlot, err := slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, votes, c, fSlot) // A block D, building on B, is received at slot N+3. It should not be able to win without boosting. dSlot := primitives.Slot(3) @@ -419,7 +438,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) votes = []uint64{2} - f.ProcessAttestation(ctx, votes, d2, fEpoch) + fSlot, err = slots.EpochStart(fEpoch) + require.NoError(t, err) + f.ProcessAttestation(ctx, votes, d2, fSlot) // Ensure D becomes the head thanks to boosting. r, err = f.Head(ctx) require.NoError(t, err) diff --git a/beacon-chain/forkchoice/doubly-linked-tree/types.go b/beacon-chain/forkchoice/doubly-linked-tree/types.go index ad5bffa79d4c..a8877834a60f 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/types.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/types.go @@ -67,5 +67,5 @@ type Node struct { type Vote struct { currentRoot [fieldparams.RootLength]byte // current voting root. nextRoot [fieldparams.RootLength]byte // next voting root. - nextEpoch primitives.Epoch // epoch of next voting period. + slot primitives.Slot // slot of the last vote by this validator } diff --git a/beacon-chain/forkchoice/interfaces.go b/beacon-chain/forkchoice/interfaces.go index 9db82c6920a4..fc033bd184ee 100644 --- a/beacon-chain/forkchoice/interfaces.go +++ b/beacon-chain/forkchoice/interfaces.go @@ -47,7 +47,7 @@ type BlockProcessor interface { // AttestationProcessor processes the attestation that's used for accounting fork choice. type AttestationProcessor interface { - ProcessAttestation(context.Context, []uint64, [32]byte, primitives.Epoch) + ProcessAttestation(context.Context, []uint64, [32]byte, primitives.Slot) } // Getter returns fork choice related information.