beacon/light: bubble up getSyncCommittee errors

This commit is contained in:
Zsolt Felfoldi 2023-09-08 15:59:43 +02:00 committed by zsfelfoldi
parent 7128f9e3cb
commit 3a46f01060
2 changed files with 31 additions and 23 deletions

View file

@ -141,10 +141,15 @@ func NewCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signer
} }
// roll back invalid updates (might be necessary if forks have been changed since last time) // roll back invalid updates (might be necessary if forks have been changed since last time)
for !s.updates.periods.IsEmpty() { for !s.updates.periods.IsEmpty() {
if update, ok := s.updates.get(s.updates.periods.Next - 1); !ok || s.verifyUpdate(update) { update, ok := s.updates.get(s.updates.periods.Next - 1)
if update == nil { if !ok {
log.Error("Sync committee update missing", "period", s.updates.periods.Next-1) log.Error("Sync committee update missing", "period", s.updates.periods.Next-1)
} s.Reset()
break
}
if valid, err := s.verifyUpdate(update); err != nil {
log.Error("Error validating update", "period", s.updates.periods.Next-1, "error", err)
} else if valid {
break break
} }
if err := s.rollback(s.updates.periods.Next); err != nil { if err := s.rollback(s.updates.periods.Next); err != nil {
@ -349,7 +354,9 @@ func (s *CommitteeChain) InsertUpdate(update *types.LightClientUpdate, nextCommi
if s.fixedRoots.periods.Includes(period+1) && reorg { if s.fixedRoots.periods.Includes(period+1) && reorg {
return ErrCannotReorg return ErrCannotReorg
} }
if !s.verifyUpdate(update) { if ok, err := s.verifyUpdate(update); err != nil {
return err
} else if !ok {
return ErrInvalidUpdate return ErrInvalidUpdate
} }
addCommittee := !s.committees.periods.Includes(period+1) || reorg addCommittee := !s.committees.periods.Includes(period+1) || reorg
@ -439,21 +446,19 @@ func (s *CommitteeChain) getCommitteeRoot(period uint64) common.Hash {
} }
// getSyncCommittee returns the deserialized sync committee at the given period. // getSyncCommittee returns the deserialized sync committee at the given period.
func (s *CommitteeChain) getSyncCommittee(period uint64) syncCommittee { func (s *CommitteeChain) getSyncCommittee(period uint64) (syncCommittee, error) {
if c, ok := s.committeeCache.Get(period); ok { if c, ok := s.committeeCache.Get(period); ok {
return c return c, nil
} }
if sc, ok := s.committees.get(period); ok { if sc, ok := s.committees.get(period); ok {
c, err := s.sigVerifier.deserializeSyncCommittee(sc) c, err := s.sigVerifier.deserializeSyncCommittee(sc)
if err != nil { if err != nil {
log.Error("Sync committee deserialization error", "error", err) return nil, fmt.Errorf("Sync committee #%d deserialization error: %v", period, err)
return nil
} }
s.committeeCache.Add(period, c) s.committeeCache.Add(period, c)
return c return c, nil
} }
log.Error("Missing serialized sync committee", "period", period) return nil, fmt.Errorf("Missing serialized sync committee #%d", period)
return nil
} }
// VerifySignedHeader returns true if the given signed header has a valid signature // VerifySignedHeader returns true if the given signed header has a valid signature
@ -463,14 +468,14 @@ func (s *CommitteeChain) getSyncCommittee(period uint64) syncCommittee {
// The age of the header is also returned (the time elapsed since the beginning // The age of the header is also returned (the time elapsed since the beginning
// of the given slot, according to the local system clock). If enforceTime is // of the given slot, according to the local system clock). If enforceTime is
// true then negative age (future) headers are rejected. // true then negative age (future) headers are rejected.
func (s *CommitteeChain) VerifySignedHeader(head types.SignedHeader) (bool, time.Duration) { func (s *CommitteeChain) VerifySignedHeader(head types.SignedHeader) (bool, time.Duration, error) {
s.chainmu.RLock() s.chainmu.RLock()
defer s.chainmu.RUnlock() defer s.chainmu.RUnlock()
return s.verifySignedHeader(head) return s.verifySignedHeader(head)
} }
func (s *CommitteeChain) verifySignedHeader(head types.SignedHeader) (bool, time.Duration) { func (s *CommitteeChain) verifySignedHeader(head types.SignedHeader) (bool, time.Duration, error) {
var age time.Duration var age time.Duration
now := s.unixNano() now := s.unixNano()
if head.Header.Slot < (uint64(now-math.MinInt64)/uint64(time.Second)-s.config.GenesisTime)/12 { if head.Header.Slot < (uint64(now-math.MinInt64)/uint64(time.Second)-s.config.GenesisTime)/12 {
@ -479,31 +484,34 @@ func (s *CommitteeChain) verifySignedHeader(head types.SignedHeader) (bool, time
age = time.Duration(math.MinInt64) age = time.Duration(math.MinInt64)
} }
if s.enforceTime && age < 0 { if s.enforceTime && age < 0 {
return false, age return false, age, nil
}
committee, err := s.getSyncCommittee(types.SyncPeriod(head.SignatureSlot))
if err != nil {
return false, 0, err
} }
committee := s.getSyncCommittee(types.SyncPeriod(head.SignatureSlot))
if committee == nil { if committee == nil {
return false, age return false, age, nil
} }
if signingRoot, err := s.config.Forks.SigningRoot(head.Header); err == nil { if signingRoot, err := s.config.Forks.SigningRoot(head.Header); err == nil {
return s.sigVerifier.verifySignature(committee, signingRoot, &head.Signature), age return s.sigVerifier.verifySignature(committee, signingRoot, &head.Signature), age, nil
} }
return false, age return false, age, nil
} }
// verifyUpdate checks whether the header signature is correct and the update // verifyUpdate checks whether the header signature is correct and the update
// fits into the specified constraints (assumes that the update has been // fits into the specified constraints (assumes that the update has been
// successfully validated previously) // successfully validated previously)
func (s *CommitteeChain) verifyUpdate(update *types.LightClientUpdate) bool { func (s *CommitteeChain) verifyUpdate(update *types.LightClientUpdate) (bool, error) {
// Note: SignatureSlot determines the sync period of the committee used for signature // Note: SignatureSlot determines the sync period of the committee used for signature
// verification. Though in reality SignatureSlot is always bigger than update.Header.Slot, // verification. Though in reality SignatureSlot is always bigger than update.Header.Slot,
// setting them as equal here enforces the rule that they have to be in the same sync // setting them as equal here enforces the rule that they have to be in the same sync
// period in order for the light client update proof to be meaningful. // period in order for the light client update proof to be meaningful.
ok, age := s.verifySignedHeader(update.AttestedHeader) ok, age, err := s.verifySignedHeader(update.AttestedHeader)
if age < 0 { if age < 0 {
log.Warn("Future committee update received", "age", age) log.Warn("Future committee update received", "age", age)
} }
return ok return ok, err
} }
// canonicalStore stores instances of the given type in a database and caches // canonicalStore stores instances of the given type in a database and caches

View file

@ -283,7 +283,7 @@ func (c *committeeChainTest) insertUpdate(tc *testCommitteeChain, period uint64,
func (c *committeeChainTest) verifySignedHeader(tc *testCommitteeChain, period float64, expOk bool) { func (c *committeeChainTest) verifySignedHeader(tc *testCommitteeChain, period float64, expOk bool) {
slot := uint64(period * float64(params.SyncPeriodLength)) slot := uint64(period * float64(params.SyncPeriodLength))
signedHead := GenerateTestSignedHeader(types.Header{Slot: slot}, &tc.config, tc.periods[types.SyncPeriod(slot)].committee, slot+1, 400) signedHead := GenerateTestSignedHeader(types.Header{Slot: slot}, &tc.config, tc.periods[types.SyncPeriod(slot)].committee, slot+1, 400)
if ok, _ := c.chain.VerifySignedHeader(signedHead); ok != expOk { if ok, _, _ := c.chain.VerifySignedHeader(signedHead); ok != expOk {
c.t.Errorf("Incorrect output from VerifySignedHeader at period %f (expected %v, got %v)", period, expOk, ok) c.t.Errorf("Incorrect output from VerifySignedHeader at period %f (expected %v, got %v)", period, expOk, ok)
} }
} }