Skip to content

Commit

Permalink
feat(sync): bifurcation for syncTarget
Browse files Browse the repository at this point in the history
  • Loading branch information
cristaloleg committed Sep 24, 2024
1 parent 4b626c3 commit fac1640
Showing 1 changed file with 64 additions and 11 deletions.
75 changes: 64 additions & 11 deletions sync/sync_head.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sync
import (
"context"
"errors"
"fmt"
"time"

"github.com/celestiaorg/go-header"
Expand Down Expand Up @@ -173,22 +174,74 @@ func (s *Syncer[H]) verify(ctx context.Context, newHead H) (bool, error) {
}

var verErr *header.VerifyError
if errors.As(err, &verErr) && !verErr.SoftFailure {
logF := log.Warnw
if errors.Is(err, header.ErrKnownHeader) {
logF = log.Debugw
}
logF("invalid network header",
"height_of_invalid", newHead.Height(),
"hash_of_invalid", newHead.Hash(),
"height_of_subjective", sbjHead.Height(),
"hash_of_subjective", sbjHead.Hash(),
"reason", verErr.Reason)
if !errors.As(err, &verErr) {
return false, nil
}

if verErr.SoftFailure {
err := s.verifySkipping(ctx, sbjHead.Height(), newHead)
return false, err
}

logF := log.Warnw
if errors.Is(err, header.ErrKnownHeader) {
logF = log.Debugw
}
logF("invalid network header",
"height_of_invalid", newHead.Height(),
"hash_of_invalid", newHead.Hash(),
"height_of_subjective", sbjHead.Height(),
"hash_of_subjective", sbjHead.Hash(),
"reason", verErr.Reason,
)

return verErr.SoftFailure, err
}

/*
Subjective head is 500, network head is 1000.
Header at height 1000 does not have sufficient validator set overlap,
so the client downloads height 750 (which does have enough sufficient overlap),
verifies it against 500 and advances the subjective head to 750.
Client tries to apply height 1000 against 750 and if there is sufficient overlap,
it applies 1000 as the subjective head.
If not, it downloads the halfway point and retries the process.
*/
func (s *Syncer[H]) verifySkipping(ctx context.Context, subjHeight uint64, networkHeader H) error {
diff := networkHeader.Height() - subjHeight
if diff <= 0 {
panic(fmt.Sprintf("implementation bug: diff is %d", diff))
}

for diff > 0 {
diff = diff / 2
subjHeight += diff

subjHeader, err := s.getter.GetByHeight(ctx, subjHeight)
if err != nil {
return err
}

if err := header.Verify(subjHeader, networkHeader); err == nil {
return nil
}
}
return &NewValidatorSetCantBeTrustedError{
NetHeadHeight: networkHeader.Height(),
NetHeadHash: networkHeader.Hash(),
}
}

type NewValidatorSetCantBeTrustedError struct {
NetHeadHeight uint64
NetHeadHash []byte
}

func (e *NewValidatorSetCantBeTrustedError) Error() string {
return fmt.Sprintf("sync: new validator set cant be trusted: head %d, attempted %s", e.NetHeadHeight, e.NetHeadHash)
}

// isExpired checks if header is expired against trusting period.
func isExpired[H header.Header[H]](header H, period time.Duration) bool {
expirationTime := header.Time().Add(period)
Expand Down

0 comments on commit fac1640

Please sign in to comment.