Add missing pieces of Electra (#14227)

* Add missing pieces of Electra

* update mock
This commit is contained in:
Radosław Kapka
2024-07-16 21:33:40 +02:00
committed by GitHub
parent fadff022a0
commit 637cbc88e8
9 changed files with 162 additions and 67 deletions

View File

@@ -323,7 +323,7 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
var attr payloadattribute.Attributer
switch st.Version() {
case version.Deneb:
case version.Deneb, version.Electra:
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")

View File

@@ -855,41 +855,63 @@ func Test_GetPayloadAttributeV2(t *testing.T) {
require.Equal(t, 0, len(a))
}
func Test_GetPayloadAttributeDeneb(t *testing.T) {
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
func Test_GetPayloadAttributeV3(t *testing.T) {
var testCases = []struct {
name string
st bstate.BeaconState
}{
{
name: "deneb",
st: func() bstate.BeaconState {
st, _ := util.DeterministicGenesisStateDeneb(t, 1)
return st
}(),
},
{
name: "electra",
st: func() bstate.BeaconState {
st, _ := util.DeterministicGenesisStateElectra(t, 1)
return st
}(),
},
}
st, _ := util.DeterministicGenesisStateDeneb(t, 1)
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, true, attr.IsEmpty())
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
// Cache hit, advance state, no fee recipient
slot := primitives.Slot(1)
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
a, err := attr.Withdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(a))
attr := service.getPayloadAttribute(ctx, test.st, 0, []byte{})
require.Equal(t, true, attr.IsEmpty())
// Cache hit, advance state, has fee recipient
suggestedAddr := common.HexToAddress("123")
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
a, err = attr.Withdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(a))
// Cache hit, advance state, no fee recipient
slot := primitives.Slot(1)
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, test.st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
a, err := attr.Withdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(a))
attrV3, err := attr.PbV3()
require.NoError(t, err)
hr := service.headRoot()
require.Equal(t, hr, [32]byte(attrV3.ParentBeaconBlockRoot))
// Cache hit, advance state, has fee recipient
suggestedAddr := common.HexToAddress("123")
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, test.st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
a, err = attr.Withdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(a))
attrV3, err := attr.PbV3()
require.NoError(t, err)
hr := service.headRoot()
require.Equal(t, hr, [32]byte(attrV3.ParentBeaconBlockRoot))
})
}
}
func Test_UpdateLastValidatedCheckpoint(t *testing.T) {

View File

@@ -68,11 +68,11 @@ func isSSZStorageFormat(obj interface{}) bool {
return true
case *ethpb.BeaconBlock:
return true
case *ethpb.Attestation:
case *ethpb.Attestation, *ethpb.AttestationElectra:
return true
case *ethpb.Deposit:
return true
case *ethpb.AttesterSlashing:
case *ethpb.AttesterSlashing, *ethpb.AttesterSlashingElectra:
return true
case *ethpb.ProposerSlashing:
return true

View File

@@ -368,13 +368,18 @@ func (s *Service) GetAttestationData(
return nil, &RpcError{Reason: BadRequest, Err: errors.Errorf("invalid request: %v", err)}
}
committeeIndex := primitives.CommitteeIndex(0)
if slots.ToEpoch(req.Slot) < params.BeaconConfig().ElectraForkEpoch {
committeeIndex = req.CommitteeIndex
}
s.AttestationCache.RLock()
res := s.AttestationCache.Get()
if res != nil && res.Slot == req.Slot {
s.AttestationCache.RUnlock()
return &ethpb.AttestationData{
Slot: res.Slot,
CommitteeIndex: req.CommitteeIndex,
CommitteeIndex: committeeIndex,
BeaconBlockRoot: res.HeadRoot,
Source: &ethpb.Checkpoint{
Epoch: res.Source.Epoch,
@@ -398,7 +403,7 @@ func (s *Service) GetAttestationData(
if res != nil && res.Slot == req.Slot {
return &ethpb.AttestationData{
Slot: res.Slot,
CommitteeIndex: req.CommitteeIndex,
CommitteeIndex: committeeIndex,
BeaconBlockRoot: res.HeadRoot,
Source: &ethpb.Checkpoint{
Epoch: res.Source.Epoch,
@@ -458,7 +463,7 @@ func (s *Service) GetAttestationData(
return &ethpb.AttestationData{
Slot: req.Slot,
CommitteeIndex: req.CommitteeIndex,
CommitteeIndex: committeeIndex,
BeaconBlockRoot: headRoot,
Source: &ethpb.Checkpoint{
Epoch: justifiedCheckpoint.Epoch,

View File

@@ -475,6 +475,74 @@ func TestGetAttestationData_SucceedsInFirstEpoch(t *testing.T) {
}
}
func TestGetAttestationData_CommitteeIndexIsZeroPostElectra(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.ElectraForkEpoch = 0
params.OverrideBeaconConfig(cfg)
block := util.NewBeaconBlock()
block.Block.Slot = 3*params.BeaconConfig().SlotsPerEpoch + 1
targetBlock := util.NewBeaconBlock()
targetBlock.Block.Slot = params.BeaconConfig().SlotsPerEpoch
targetRoot, err := targetBlock.Block.HashTreeRoot()
require.NoError(t, err)
justifiedBlock := util.NewBeaconBlock()
justifiedBlock.Block.Slot = 2 * params.BeaconConfig().SlotsPerEpoch
blockRoot, err := block.Block.HashTreeRoot()
require.NoError(t, err)
justifiedRoot, err := justifiedBlock.Block.HashTreeRoot()
require.NoError(t, err)
slot := 3*params.BeaconConfig().SlotsPerEpoch + 1
beaconState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, beaconState.SetSlot(slot))
justifiedCheckpoint := &ethpb.Checkpoint{
Epoch: 2,
Root: justifiedRoot[:],
}
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(justifiedCheckpoint))
offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
attesterServer := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: &mock.ChainService{Optimistic: false},
TimeFetcher: &mock.ChainService{Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second)},
CoreService: &core.Service{
HeadFetcher: &mock.ChainService{TargetRoot: targetRoot, Root: blockRoot[:], State: beaconState},
GenesisTimeFetcher: &mock.ChainService{
Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second),
},
FinalizedFetcher: &mock.ChainService{CurrentJustifiedCheckPoint: justifiedCheckpoint},
AttestationCache: cache.NewAttestationCache(),
OptimisticModeFetcher: &mock.ChainService{Optimistic: false},
},
}
req := &ethpb.AttestationDataRequest{
CommitteeIndex: 123, // set non-zero committee index
Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1,
}
res, err := attesterServer.GetAttestationData(context.Background(), req)
require.NoError(t, err)
expected := &ethpb.AttestationData{
Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1,
CommitteeIndex: 0,
BeaconBlockRoot: blockRoot[:],
Source: &ethpb.Checkpoint{
Epoch: 2,
Root: justifiedRoot[:],
},
Target: &ethpb.Checkpoint{
Epoch: 3,
Root: targetRoot[:],
},
}
assert.DeepEqual(t, expected, res)
}
func TestServer_SubscribeCommitteeSubnets_NoSlots(t *testing.T) {
attesterServer := &Server{
HeadFetcher: &mock.ChainService{},

View File

@@ -99,7 +99,7 @@ func (s *Store) AttestedPublicKeys(_ context.Context) ([][fieldparams.BLSPubkeyL
// If it is not, it updates the database.
func (s *Store) SlashableAttestationCheck(
ctx context.Context,
indexedAtt *ethpb.IndexedAttestation,
indexedAtt ethpb.IndexedAtt,
pubKey [fieldparams.BLSPubkeyLength]byte,
signingRoot32 [32]byte,
_ bool,
@@ -128,10 +128,10 @@ func (s *Store) SaveAttestationForPubKey(
_ context.Context,
pubkey [fieldparams.BLSPubkeyLength]byte,
_ [32]byte,
att *ethpb.IndexedAttestation,
att ethpb.IndexedAtt,
) error {
// If there is no attestation, return on error.
if att == nil || att.Data == nil || att.Data.Source == nil || att.Data.Target == nil {
if att == nil || att.GetData() == nil || att.GetData().Source == nil || att.GetData().Target == nil {
return errors.New("incoming attestation does not contain source and/or target epoch")
}
@@ -141,8 +141,8 @@ func (s *Store) SaveAttestationForPubKey(
return errors.Wrap(err, "could not get validator slashing protection")
}
incomingSourceEpochUInt64 := uint64(att.Data.Source.Epoch)
incomingTargetEpochUInt64 := uint64(att.Data.Target.Epoch)
incomingSourceEpochUInt64 := uint64(att.GetData().Source.Epoch)
incomingTargetEpochUInt64 := uint64(att.GetData().Target.Epoch)
if validatorSlashingProtection == nil {
// If there is no validator slashing protection, create one.
@@ -167,7 +167,7 @@ func (s *Store) SaveAttestationForPubKey(
if incomingSourceEpochUInt64 < savedSourceEpoch {
return errors.Errorf(
"could not sign attestation with source lower than recorded source epoch, %d < %d",
att.Data.Source.Epoch,
att.GetData().Source.Epoch,
validatorSlashingProtection.LastSignedAttestationSourceEpoch,
)
}
@@ -177,7 +177,7 @@ func (s *Store) SaveAttestationForPubKey(
if savedTargetEpoch != nil && incomingTargetEpochUInt64 <= *savedTargetEpoch {
return errors.Errorf(
"could not sign attestation with target lower than or equal to recorded target epoch, %d <= %d",
att.Data.Target.Epoch,
att.GetData().Target.Epoch,
*savedTargetEpoch,
)
}

View File

@@ -55,13 +55,13 @@ type ValidatorDB interface {
LowestSignedSourceEpoch(ctx context.Context, publicKey [fieldparams.BLSPubkeyLength]byte) (primitives.Epoch, bool, error)
AttestedPublicKeys(ctx context.Context) ([][fieldparams.BLSPubkeyLength]byte, error)
SlashableAttestationCheck(
ctx context.Context, indexedAtt *ethpb.IndexedAttestation, pubKey [fieldparams.BLSPubkeyLength]byte,
ctx context.Context, indexedAtt ethpb.IndexedAtt, pubKey [fieldparams.BLSPubkeyLength]byte,
signingRoot32 [32]byte,
emitAccountMetrics bool,
validatorAttestFailVec *prometheus.CounterVec,
) error
SaveAttestationForPubKey(
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoot [fieldparams.RootLength]byte, att *ethpb.IndexedAttestation,
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoot [fieldparams.RootLength]byte, att ethpb.IndexedAtt,
) error
SaveAttestationsForPubKey(
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoots [][]byte, atts []*ethpb.IndexedAttestation,

View File

@@ -139,7 +139,7 @@ func (s *Store) AttestationHistoryForPubKey(ctx context.Context, pubKey [fieldpa
// If it is not, it updates the database.
func (s *Store) SlashableAttestationCheck(
ctx context.Context,
indexedAtt *ethpb.IndexedAttestation,
indexedAtt ethpb.IndexedAtt,
pubKey [fieldparams.BLSPubkeyLength]byte,
signingRoot32 [32]byte,
emitAccountMetrics bool,
@@ -156,14 +156,14 @@ func (s *Store) SlashableAttestationCheck(
if err != nil {
return err
}
if exists && indexedAtt.Data.Source.Epoch < lowestSourceEpoch {
if exists && indexedAtt.GetData().Source.Epoch < lowestSourceEpoch {
return fmt.Errorf(
"could not sign attestation lower than lowest source epoch in db, %d < %d",
indexedAtt.Data.Source.Epoch,
indexedAtt.GetData().Source.Epoch,
lowestSourceEpoch,
)
}
existingSigningRoot, err := s.SigningRootAtTargetEpoch(ctx, pubKey, indexedAtt.Data.Target.Epoch)
existingSigningRoot, err := s.SigningRootAtTargetEpoch(ctx, pubKey, indexedAtt.GetData().Target.Epoch)
if err != nil {
return err
}
@@ -176,10 +176,10 @@ func (s *Store) SlashableAttestationCheck(
if err != nil {
return err
}
if signingRootsDiffer && exists && indexedAtt.Data.Target.Epoch <= lowestTargetEpoch {
if signingRootsDiffer && exists && indexedAtt.GetData().Target.Epoch <= lowestTargetEpoch {
return fmt.Errorf(
"could not sign attestation lower than or equal to lowest target epoch in db if signing roots differ, %d <= %d",
indexedAtt.Data.Target.Epoch,
indexedAtt.GetData().Target.Epoch,
lowestTargetEpoch,
)
}
@@ -210,7 +210,7 @@ func (s *Store) SlashableAttestationCheck(
// CheckSlashableAttestation verifies an incoming attestation is
// not a double vote for a validator public key nor a surround vote.
func (s *Store) CheckSlashableAttestation(
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoot []byte, att *ethpb.IndexedAttestation,
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoot []byte, att ethpb.IndexedAtt,
) (SlashingKind, error) {
ctx, span := trace.StartSpan(ctx, "Validator.CheckSlashableAttestation")
defer span.End()
@@ -228,14 +228,14 @@ func (s *Store) CheckSlashableAttestation(
// First we check for double votes.
signingRootsBucket := pkBucket.Bucket(attestationSigningRootsBucket)
if signingRootsBucket != nil {
targetEpochBytes := bytesutil.EpochToBytesBigEndian(att.Data.Target.Epoch)
targetEpochBytes := bytesutil.EpochToBytesBigEndian(att.GetData().Target.Epoch)
existingSigningRoot := signingRootsBucket.Get(targetEpochBytes)
// If a signing root exists in the database, and if this database signing root is empty => We consider the new attestation as a double vote.
// If a signing root exists in the database, and if this database signing differs from the signing root of the new attestation => We consider the new attestation as a double vote.
if existingSigningRoot != nil && (len(existingSigningRoot) == 0 || slashings.SigningRootsDiffer(existingSigningRoot, signingRoot)) {
slashKind = DoubleVote
return fmt.Errorf(doubleVoteMessage, att.Data.Target.Epoch, existingSigningRoot)
return fmt.Errorf(doubleVoteMessage, att.GetData().Target.Epoch, existingSigningRoot)
}
}
@@ -269,12 +269,12 @@ func (s *Store) CheckSlashableAttestation(
// Iterate from the back of the bucket since we are looking for target_epoch > att.target_epoch
func (*Store) checkSurroundedVote(
targetEpochsBucket *bolt.Bucket, att *ethpb.IndexedAttestation,
targetEpochsBucket *bolt.Bucket, att ethpb.IndexedAtt,
) (SlashingKind, error) {
c := targetEpochsBucket.Cursor()
for k, v := c.Last(); k != nil; k, v = c.Prev() {
existingTargetEpoch := bytesutil.BytesToEpochBigEndian(k)
if existingTargetEpoch <= att.Data.Target.Epoch {
if existingTargetEpoch <= att.GetData().Target.Epoch {
break
}
@@ -296,8 +296,8 @@ func (*Store) checkSurroundedVote(
if surrounded {
return SurroundedVote, fmt.Errorf(
surroundedVoteMessage,
att.Data.Source.Epoch,
att.Data.Target.Epoch,
att.GetData().Source.Epoch,
att.GetData().Target.Epoch,
existingSourceEpoch,
existingTargetEpoch,
)
@@ -309,12 +309,12 @@ func (*Store) checkSurroundedVote(
// Iterate from the back of the bucket since we are looking for source_epoch > att.source_epoch
func (*Store) checkSurroundingVote(
sourceEpochsBucket *bolt.Bucket, att *ethpb.IndexedAttestation,
sourceEpochsBucket *bolt.Bucket, att ethpb.IndexedAtt,
) (SlashingKind, error) {
c := sourceEpochsBucket.Cursor()
for k, v := c.Last(); k != nil; k, v = c.Prev() {
existingSourceEpoch := bytesutil.BytesToEpochBigEndian(k)
if existingSourceEpoch <= att.Data.Source.Epoch {
if existingSourceEpoch <= att.GetData().Source.Epoch {
break
}
@@ -336,8 +336,8 @@ func (*Store) checkSurroundingVote(
if surrounding {
return SurroundingVote, fmt.Errorf(
surroundingVoteMessage,
att.Data.Source.Epoch,
att.Data.Target.Epoch,
att.GetData().Source.Epoch,
att.GetData().Target.Epoch,
existingSourceEpoch,
existingTargetEpoch,
)
@@ -375,7 +375,7 @@ func (s *Store) SaveAttestationsForPubKey(
// SaveAttestationForPubKey saves an attestation for a validator public
// key for local validator slashing protection.
func (s *Store) SaveAttestationForPubKey(
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoot [fieldparams.RootLength]byte, att *ethpb.IndexedAttestation,
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoot [fieldparams.RootLength]byte, att ethpb.IndexedAtt,
) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveAttestationForPubKey")
defer span.End()
@@ -383,8 +383,8 @@ func (s *Store) SaveAttestationForPubKey(
ctx: ctx,
record: &common.AttestationRecord{
PubKey: pubKey,
Source: att.Data.Source.Epoch,
Target: att.Data.Target.Epoch,
Source: att.GetData().Source.Epoch,
Target: att.GetData().Target.Epoch,
SigningRoot: signingRoot[:],
},
}

View File

@@ -105,7 +105,7 @@ func (db *ValidatorDBMock) AttestedPublicKeys(ctx context.Context) ([][fieldpara
}
func (db *ValidatorDBMock) SlashableAttestationCheck(
ctx context.Context, indexedAtt *ethpb.IndexedAttestation, pubKey [fieldparams.BLSPubkeyLength]byte,
ctx context.Context, indexedAtt ethpb.IndexedAtt, pubKey [fieldparams.BLSPubkeyLength]byte,
signingRoot32 [32]byte,
emitAccountMetrics bool,
validatorAttestFailVec *prometheus.CounterVec,
@@ -114,7 +114,7 @@ func (db *ValidatorDBMock) SlashableAttestationCheck(
}
func (db *ValidatorDBMock) SaveAttestationForPubKey(
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoot [fieldparams.RootLength]byte, att *ethpb.IndexedAttestation,
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signingRoot [fieldparams.RootLength]byte, att ethpb.IndexedAtt,
) error {
panic("not implemented")
}