Files
prysm/beacon-chain/sync/pending_attestations_queue_bucket_test.go
Bastin 92bd211e4d upgrade v6 to v7 (#15989)
* upgrade v6 to v7

* changelog

* update-go-ssz
2025-11-06 16:16:23 +00:00

418 lines
11 KiB
Go

package sync
import (
"context"
"testing"
mockChain "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/assert"
"github.com/OffchainLabs/prysm/v7/testing/require"
"github.com/OffchainLabs/prysm/v7/testing/util"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func TestProcessAttestationBucket(t *testing.T) {
t.Run("EmptyBucket", func(t *testing.T) {
hook := logTest.NewGlobal()
s := &Service{}
s.processAttestationBucket(context.Background(), nil)
emptyBucket := &attestationBucket{
attestations: []ethpb.Att{},
}
s.processAttestationBucket(context.Background(), emptyBucket)
require.Equal(t, 0, len(hook.Entries), "Should not log any messages for empty buckets")
})
t.Run("ForkchoiceFailure", func(t *testing.T) {
hook := logTest.NewGlobal()
chainService := &mockChain.ChainService{
NotFinalized: true, // This makes InForkchoice return false
}
s := &Service{
cfg: &config{
chain: chainService,
},
}
attData := &ethpb.AttestationData{
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot"), 32),
}
bucket := &attestationBucket{
data: attData,
attestations: []ethpb.Att{util.NewAttestation()},
}
s.processAttestationBucket(context.Background(), bucket)
require.Equal(t, 1, len(hook.Entries))
assert.StringContains(t, "Failed forkchoice check for bucket", hook.LastEntry().Message)
require.NotNil(t, hook.LastEntry().Data["error"])
})
t.Run("CommitteeFailure", func(t *testing.T) {
hook := logTest.NewGlobal()
beaconState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, beaconState.SetSlot(1))
chainService := &mockChain.ChainService{
State: beaconState,
ValidAttestation: true,
}
s := &Service{
cfg: &config{
chain: chainService,
},
}
attData := &ethpb.AttestationData{
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot"), 32),
Target: &ethpb.Checkpoint{
Epoch: 1,
Root: bytesutil.PadTo([]byte("blockroot"), 32),
},
CommitteeIndex: 999999,
}
att := util.NewAttestation()
att.Data = attData
bucket := &attestationBucket{
data: attData,
attestations: []ethpb.Att{att},
}
s.processAttestationBucket(context.Background(), bucket)
require.Equal(t, 1, len(hook.Entries))
assert.StringContains(t, "Failed to get committee from state", hook.LastEntry().Message)
})
t.Run("FFGConsistencyFailure", func(t *testing.T) {
hook := logTest.NewGlobal()
validators := make([]*ethpb.Validator, 64)
for i := range validators {
validators[i] = &ethpb.Validator{
ExitEpoch: 1000000,
EffectiveBalance: 32000000000,
}
}
beaconState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, beaconState.SetSlot(1))
require.NoError(t, beaconState.SetValidators(validators))
chainService := &mockChain.ChainService{
State: beaconState,
}
s := &Service{
cfg: &config{
chain: chainService,
},
}
attData := &ethpb.AttestationData{
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot"), 32),
Target: &ethpb.Checkpoint{
Epoch: 1,
Root: bytesutil.PadTo([]byte("different_target"), 32), // Different from BeaconBlockRoot to trigger FFG failure
},
}
att := util.NewAttestation()
att.Data = attData
bucket := &attestationBucket{
data: attData,
attestations: []ethpb.Att{att},
}
s.processAttestationBucket(context.Background(), bucket)
require.Equal(t, 1, len(hook.Entries))
assert.StringContains(t, "Failed FFG consistency check for bucket", hook.LastEntry().Message)
})
t.Run("ProcessingSuccess", func(t *testing.T) {
hook := logTest.NewGlobal()
validators := make([]*ethpb.Validator, 64)
for i := range validators {
validators[i] = &ethpb.Validator{
ExitEpoch: 1000000,
EffectiveBalance: 32000000000,
}
}
beaconState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, beaconState.SetSlot(1))
require.NoError(t, beaconState.SetValidators(validators))
chainService := &mockChain.ChainService{
State: beaconState,
ValidAttestation: true,
}
s := &Service{
cfg: &config{
chain: chainService,
},
}
// Test with Phase0 attestation
t.Run("Phase0_NoError", func(t *testing.T) {
hook.Reset() // Reset logs before test
phase0Att := util.NewAttestation()
phase0Att.Data.Slot = 1
phase0Att.Data.CommitteeIndex = 0
bucket := &attestationBucket{
data: phase0Att.GetData(),
attestations: []ethpb.Att{phase0Att},
}
s.processAttestationBucket(context.Background(), bucket)
})
// Test with SingleAttestation
t.Run("Electra_NoError", func(t *testing.T) {
hook.Reset() // Reset logs before test
attData := &ethpb.AttestationData{
Slot: 1,
CommitteeIndex: 0,
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot"), 32),
Source: &ethpb.Checkpoint{
Epoch: 0,
Root: bytesutil.PadTo([]byte("source"), 32),
},
Target: &ethpb.Checkpoint{
Epoch: 1,
Root: bytesutil.PadTo([]byte("blockroot"), 32), // Same as BeaconBlockRoot for LMD/FFG consistency
},
}
singleAtt := &ethpb.SingleAttestation{
CommitteeId: 0,
AttesterIndex: 0,
Data: attData,
Signature: make([]byte, 96),
}
bucket := &attestationBucket{
data: singleAtt.GetData(),
attestations: []ethpb.Att{singleAtt},
}
s.processAttestationBucket(context.Background(), bucket)
})
})
}
func TestBucketAttestationsByData(t *testing.T) {
t.Run("EmptyInput", func(t *testing.T) {
hook := logTest.NewGlobal()
buckets := bucketAttestationsByData(nil)
require.Equal(t, 0, len(buckets))
require.Equal(t, 0, len(hook.Entries))
buckets = bucketAttestationsByData([]ethpb.Att{})
require.Equal(t, 0, len(buckets))
require.Equal(t, 0, len(hook.Entries))
})
t.Run("SingleAttestation", func(t *testing.T) {
hook := logTest.NewGlobal()
att := util.NewAttestation()
att.Data.Slot = 1
att.Data.CommitteeIndex = 0
buckets := bucketAttestationsByData([]ethpb.Att{att})
require.Equal(t, 1, len(buckets))
var bucket *attestationBucket
for _, b := range buckets {
bucket = b
break
}
require.NotNil(t, bucket)
require.Equal(t, 1, len(bucket.attestations))
require.Equal(t, att, bucket.attestations[0])
require.Equal(t, att.GetData(), bucket.data)
require.Equal(t, 0, len(hook.Entries))
})
t.Run("MultipleAttestationsSameData", func(t *testing.T) {
hook := logTest.NewGlobal()
att1 := util.NewAttestation()
att1.Data.Slot = 1
att1.Data.CommitteeIndex = 0
att2 := util.NewAttestation()
att2.Data = att1.Data // Same data
att2.Signature = make([]byte, 96) // Different signature
buckets := bucketAttestationsByData([]ethpb.Att{att1, att2})
require.Equal(t, 1, len(buckets), "Should have one bucket for same data")
var bucket *attestationBucket
for _, b := range buckets {
bucket = b
break
}
require.NotNil(t, bucket)
require.Equal(t, 2, len(bucket.attestations), "Should have both attestations in one bucket")
require.Equal(t, att1.GetData(), bucket.data)
require.Equal(t, 0, len(hook.Entries))
})
t.Run("MultipleAttestationsDifferentData", func(t *testing.T) {
hook := logTest.NewGlobal()
att1 := util.NewAttestation()
att1.Data.Slot = 1
att1.Data.CommitteeIndex = 0
att2 := util.NewAttestation()
att2.Data.Slot = 2 // Different slot
att2.Data.CommitteeIndex = 1
buckets := bucketAttestationsByData([]ethpb.Att{att1, att2})
require.Equal(t, 2, len(buckets), "Should have two buckets for different data")
bucketCount := 0
for _, bucket := range buckets {
require.Equal(t, 1, len(bucket.attestations), "Each bucket should have one attestation")
bucketCount++
}
require.Equal(t, 2, bucketCount, "Should have exactly two buckets")
require.Equal(t, 0, len(hook.Entries))
})
t.Run("MixedAttestationTypes", func(t *testing.T) {
hook := logTest.NewGlobal()
// Create Phase0 attestation
phase0Att := util.NewAttestation()
phase0Att.Data.Slot = 1
phase0Att.Data.CommitteeIndex = 0
electraAtt := &ethpb.SingleAttestation{
CommitteeId: 0,
AttesterIndex: 1,
Data: phase0Att.Data, // Same data
Signature: make([]byte, 96),
}
buckets := bucketAttestationsByData([]ethpb.Att{phase0Att, electraAtt})
require.Equal(t, 1, len(buckets), "Should have one bucket for same data")
var bucket *attestationBucket
for _, b := range buckets {
bucket = b
break
}
require.NotNil(t, bucket)
require.Equal(t, 2, len(bucket.attestations), "Should have both attestations in one bucket")
require.Equal(t, phase0Att.GetData(), bucket.data)
require.Equal(t, 0, len(hook.Entries))
})
}
func TestBatchVerifyAttestationSignatures(t *testing.T) {
t.Run("EmptyInput", func(t *testing.T) {
s := &Service{}
beaconState, err := util.NewBeaconState()
require.NoError(t, err)
result := s.batchVerifyAttestationSignatures(context.Background(), []ethpb.Att{}, beaconState)
// Empty input should return empty output
require.Equal(t, 0, len(result))
})
t.Run("BatchVerificationWithState", func(t *testing.T) {
hook := logTest.NewGlobal()
validators := make([]*ethpb.Validator, 64)
for i := range validators {
validators[i] = &ethpb.Validator{
ExitEpoch: 1000000,
EffectiveBalance: 32000000000,
}
}
beaconState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, beaconState.SetSlot(1))
require.NoError(t, beaconState.SetValidators(validators))
s := &Service{}
att := util.NewAttestation()
att.Data.Slot = 1
attestations := []ethpb.Att{att}
result := s.batchVerifyAttestationSignatures(context.Background(), attestations, beaconState)
require.NotNil(t, result)
if len(result) == 0 && len(hook.Entries) > 0 {
_ = false // Check if fallback message is logged
for _, entry := range hook.Entries {
if entry.Message == "batch verification failed, using individual checks" {
_ = true // Found the fallback message
break
}
}
// It's OK if fallback message is logged - this means the function is working correctly
}
})
t.Run("BatchVerificationFailureFallbackToIndividual", func(t *testing.T) {
hook := logTest.NewGlobal()
beaconState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, beaconState.SetSlot(1))
chainService := &mockChain.ChainService{
State: beaconState,
ValidAttestation: false, // This will cause verification to fail
}
s := &Service{
cfg: &config{
chain: chainService,
},
}
att := util.NewAttestation()
att.Data.Slot = 1
attestations := []ethpb.Att{att}
result := s.batchVerifyAttestationSignatures(context.Background(), attestations, beaconState)
require.Equal(t, 0, len(result))
require.NotEqual(t, 0, len(hook.Entries), "Should have log entries")
found := false
for _, entry := range hook.Entries {
if entry.Message == "batch verification failed, using individual checks" {
found = true
break
}
}
require.Equal(t, true, found, "Should log fallback message")
})
}