Skip to content

Commit 72d6506

Browse files
authored
feat(sync, store)!: Remove adjacency requirement from Store.Append (#186)
## Overview Things that gonna change after we drop adjacency requirement from `Store.Append`: - Obviously dropping `ErrNonAdjacent` (breaking change). - `Store.Append` comment is **already** outdated (looks like there were 2 return params) - `store.Store.Append` will not verify for adjacency (obviously) - `Syncer.setSubjectiveHead` will not verify for `ErrNonAdjacent` - adjacency check is happening in `sync` instead of `store`. Fixes #32
1 parent 5872766 commit 72d6506

File tree

4 files changed

+34
-25
lines changed

4 files changed

+34
-25
lines changed

interface.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package header
33
import (
44
"context"
55
"errors"
6-
"fmt"
76

87
pubsub "github.com/libp2p/go-libp2p-pubsub"
98
)
@@ -59,16 +58,6 @@ var (
5958
ErrHeadersLimitExceeded = errors.New("header/p2p: header limit per 1 request exceeded")
6059
)
6160

62-
// ErrNonAdjacent is returned when Store is appended with a header not adjacent to the stored head.
63-
type ErrNonAdjacent struct {
64-
Head uint64
65-
Attempted uint64
66-
}
67-
68-
func (ena *ErrNonAdjacent) Error() string {
69-
return fmt.Sprintf("header/store: non-adjacent: head %d, attempted %d", ena.Head, ena.Attempted)
70-
}
71-
7261
// Store encompasses the behavior necessary to store and retrieve Headers
7362
// from a node's local storage.
7463
type Store[H Header[H]] interface {
@@ -88,10 +77,6 @@ type Store[H Header[H]] interface {
8877
HasAt(context.Context, uint64) bool
8978

9079
// Append stores and verifies the given Header(s).
91-
// It requires them to be adjacent and in ascending order,
92-
// as it applies them contiguously on top of the current head height.
93-
// It returns the amount of successfully applied headers,
94-
// so caller can understand what given header was invalid, if any.
9580
Append(context.Context, ...H) error
9681

9782
// GetRange returns the range [from:to).

store/store.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -322,14 +322,6 @@ func (s *Store[H]) Append(ctx context.Context, headers ...H) error {
322322
// collect valid headers
323323
verified := make([]H, 0, lh)
324324
for i, h := range headers {
325-
// currently store requires all headers to be appended sequentially and adjacently
326-
// TODO(@Wondertan): Further pruning friendly Store design should reevaluate this requirement
327-
if h.Height() != head.Height()+1 {
328-
return &header.ErrNonAdjacent{
329-
Head: head.Height(),
330-
Attempted: h.Height(),
331-
}
332-
}
333325

334326
err = head.Verify(h)
335327
if err != nil {
@@ -350,7 +342,8 @@ func (s *Store[H]) Append(ctx context.Context, headers ...H) error {
350342
// otherwise, stop the loop and apply headers appeared to be valid
351343
break
352344
}
353-
verified, head = append(verified, h), h
345+
verified = append(verified, h)
346+
head = h
354347
}
355348

356349
onWrite := func() {

sync/sync_head.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (s *Syncer[H]) setSubjectiveHead(ctx context.Context, netHead H) {
119119
// * Remove ErrNonAdjacent
120120
// * Remove writeHead from the canonical store implementation
121121
err := s.store.Append(ctx, netHead)
122-
var nonAdj *header.ErrNonAdjacent
122+
var nonAdj *errNonAdjacent
123123
if err != nil && !errors.As(err, &nonAdj) {
124124
// might be a storage error or something else, but we can still try to continue processing netHead
125125
log.Errorw("storing new network header",

sync/sync_store.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,23 @@ package sync
22

33
import (
44
"context"
5+
"errors"
6+
"fmt"
57
"sync/atomic"
68

79
"github.com/celestiaorg/go-header"
810
)
911

12+
// errNonAdjacent is returned when syncer is appended with a header not adjacent to the stored head.
13+
type errNonAdjacent struct {
14+
Head uint64
15+
Attempted uint64
16+
}
17+
18+
func (ena *errNonAdjacent) Error() string {
19+
return fmt.Sprintf("sync: non-adjacent: head %d, attempted %d", ena.Head, ena.Attempted)
20+
}
21+
1022
// syncStore is a Store wrapper that provides synchronization over writes and reads
1123
// for Head of underlying Store. Useful for Stores that do not guarantee synchrony between Append
1224
// and Head method.
@@ -31,6 +43,25 @@ func (s *syncStore[H]) Head(ctx context.Context) (H, error) {
3143
}
3244

3345
func (s *syncStore[H]) Append(ctx context.Context, headers ...H) error {
46+
if len(headers) == 0 {
47+
return nil
48+
}
49+
50+
head, err := s.Head(ctx)
51+
if err != nil && !errors.Is(err, context.Canceled) {
52+
panic(err)
53+
}
54+
55+
for _, h := range headers {
56+
if h.Height() != head.Height()+1 {
57+
return &errNonAdjacent{
58+
Head: head.Height(),
59+
Attempted: h.Height(),
60+
}
61+
}
62+
head = h
63+
}
64+
3465
if err := s.Store.Append(ctx, headers...); err != nil {
3566
return err
3667
}

0 commit comments

Comments
 (0)