diff --git a/header.go b/header.go index 75054578..df9e5261 100644 --- a/header.go +++ b/header.go @@ -6,12 +6,12 @@ import ( ) // Header abstracts all methods required to perform header sync. -type Header interface { +type Header[H any] interface { // TODO: Ideally, this should be Header[H Header[H]], but GO does not support recursive type definitions. // New creates new instance of a header. // It exists to overcome limitation of Go's type system. // See: //https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#pointer-method-example - New() Header + New() H // IsZero reports whether Header is a zero value of it's concrete type. IsZero() bool // ChainID returns identifier of the chain. @@ -25,7 +25,7 @@ type Header interface { // Time returns time when header was created. Time() time.Time // Verify validates given untrusted Header against trusted Header. - Verify(Header) error + Verify(H) error // Validate performs stateless validation to check for missed/incorrect fields. Validate() error @@ -34,6 +34,6 @@ type Header interface { } // New is a generic Header constructor. -func New[H Header]() (h H) { - return h.New().(H) +func New[H Header[H]]() (h H) { + return h.New() } diff --git a/headertest/dummy_header.go b/headertest/dummy_header.go index c3dfe33b..a654d142 100644 --- a/headertest/dummy_header.go +++ b/headertest/dummy_header.go @@ -44,7 +44,7 @@ func RandDummyHeader(t *testing.T) *DummyHeader { return dh } -func (d *DummyHeader) New() header.Header { +func (d *DummyHeader) New() *DummyHeader { return new(DummyHeader) } @@ -96,8 +96,8 @@ func (d *DummyHeader) IsExpired(period time.Duration) bool { return expirationTime.Before(time.Now()) } -func (d *DummyHeader) Verify(header header.Header) error { - if dummy, _ := header.(*DummyHeader); dummy.VerifyFailure { +func (d *DummyHeader) Verify(header *DummyHeader) error { + if header.VerifyFailure { return ErrDummyVerify } diff --git a/headertest/store.go b/headertest/store.go index 0fe9d867..690cfc55 100644 --- a/headertest/store.go +++ b/headertest/store.go @@ -8,11 +8,11 @@ import ( "github.com/celestiaorg/go-header" ) -type Generator[H header.Header] interface { +type Generator[H header.Header[H]] interface { NextHeader() H } -type Store[H header.Header] struct { +type Store[H header.Header[H]] struct { Headers map[int64]H HeadHeight int64 } @@ -23,7 +23,7 @@ func NewDummyStore(t *testing.T) *Store[*DummyHeader] { } // NewStore creates a generic mock store supporting different type of Headers based on Generator. -func NewStore[H header.Header](t *testing.T, gen Generator[H], numHeaders int) *Store[H] { +func NewStore[H header.Header[H]](t *testing.T, gen Generator[H], numHeaders int) *Store[H] { store := &Store[H]{ Headers: make(map[int64]H), HeadHeight: 0, @@ -48,7 +48,7 @@ func (m *Store[H]) Height() uint64 { return uint64(m.HeadHeight) } -func (m *Store[H]) Head(context.Context, ...header.HeadOption) (H, error) { +func (m *Store[H]) Head(context.Context, ...header.HeadOption[H]) (H, error) { return m.Headers[m.HeadHeight], nil } diff --git a/headertest/subscriber.go b/headertest/subscriber.go index bfb427c6..64e92b98 100644 --- a/headertest/subscriber.go +++ b/headertest/subscriber.go @@ -6,7 +6,7 @@ import ( "github.com/celestiaorg/go-header" ) -type Subscriber[H header.Header] struct { +type Subscriber[H header.Header[H]] struct { Headers []H } diff --git a/interface.go b/interface.go index 9f323c1f..1d2d66fb 100644 --- a/interface.go +++ b/interface.go @@ -16,7 +16,7 @@ const ( // Subscriber encompasses the behavior necessary to // subscribe/unsubscribe from new Header events from the // network. -type Subscriber[H Header] interface { +type Subscriber[H Header[H]] interface { // Subscribe creates long-living Subscription for validated Headers. // Multiple Subscriptions can be created. Subscribe() (Subscription[H], error) @@ -28,7 +28,7 @@ type Subscriber[H Header] interface { } // Subscription listens for new Headers. -type Subscription[H Header] interface { +type Subscription[H Header[H]] interface { // NextHeader returns the newest verified and valid Header // in the network. NextHeader(ctx context.Context) (H, error) @@ -37,13 +37,13 @@ type Subscription[H Header] interface { } // Broadcaster broadcasts a Header to the network. -type Broadcaster[H Header] interface { +type Broadcaster[H Header[H]] interface { Broadcast(ctx context.Context, header H, opts ...pubsub.PubOpt) error } // Exchange encompasses the behavior necessary to request Headers // from the network. -type Exchange[H Header] interface { +type Exchange[H Header[H]] interface { Getter[H] } @@ -71,7 +71,7 @@ func (ena *ErrNonAdjacent) Error() string { // Store encompasses the behavior necessary to store and retrieve Headers // from a node's local storage. -type Store[H Header] interface { +type Store[H Header[H]] interface { // Start starts the store. Start(context.Context) error @@ -104,7 +104,7 @@ type Store[H Header] interface { // Getter contains the behavior necessary for a component to retrieve // headers that have been processed during header sync. -type Getter[H Header] interface { +type Getter[H Header[H]] interface { Head[H] // Get returns the Header corresponding to the given hash. @@ -124,7 +124,7 @@ type Getter[H Header] interface { // Head contains the behavior necessary for a component to retrieve // the chain head. Note that "chain head" is subjective to the component // reporting it. -type Head[H Header] interface { +type Head[H Header[H]] interface { // Head returns the latest known header. - Head(context.Context, ...HeadOption) (H, error) + Head(context.Context, ...HeadOption[H]) (H, error) } diff --git a/local/exchange.go b/local/exchange.go index df1858e3..c3d58488 100644 --- a/local/exchange.go +++ b/local/exchange.go @@ -7,12 +7,12 @@ import ( ) // Exchange is a simple Exchange that reads Headers from Store without any networking. -type Exchange[H header.Header] struct { +type Exchange[H header.Header[H]] struct { store header.Store[H] } // NewExchange creates a new local Exchange. -func NewExchange[H header.Header](store header.Store[H]) header.Exchange[H] { +func NewExchange[H header.Header[H]](store header.Store[H]) header.Exchange[H] { return &Exchange[H]{ store: store, } @@ -26,7 +26,7 @@ func (l *Exchange[H]) Stop(context.Context) error { return nil } -func (l *Exchange[H]) Head(ctx context.Context, _ ...header.HeadOption) (H, error) { +func (l *Exchange[H]) Head(ctx context.Context, _ ...header.HeadOption[H]) (H, error) { return l.store.Head(ctx) } diff --git a/metrics.go b/metrics.go index a1999c55..51290b6d 100644 --- a/metrics.go +++ b/metrics.go @@ -12,7 +12,7 @@ import ( var meter = otel.Meter("header") // WithMetrics enables Otel metrics to monitor head and total amount of synced headers. -func WithMetrics[H Header](store Store[H]) error { +func WithMetrics[H Header[H]](store Store[H]) error { headC, _ := meter.Int64ObservableCounter( "head", metric.WithDescription("Subjective head of the node"), diff --git a/opts.go b/opts.go index dcf04d8d..363b3076 100644 --- a/opts.go +++ b/opts.go @@ -1,17 +1,17 @@ package header -type HeadOption func(opts *HeadParams) +type HeadOption[H Header[H]] func(opts *HeadParams[H]) // HeadParams contains options to be used for Head interface methods -type HeadParams struct { +type HeadParams[H Header[H]] struct { // TrustedHead allows the caller of Head to specify a trusted header // against which the underlying implementation of Head can verify against. - TrustedHead Header + TrustedHead Header[H] } // WithTrustedHead sets the TrustedHead parameter to the given header. -func WithTrustedHead(verified Header) func(opts *HeadParams) { - return func(opts *HeadParams) { +func WithTrustedHead[H Header[H]] (verified Header[H]) func(opts *HeadParams[H]) { + return func(opts *HeadParams[H]) { opts.TrustedHead = verified } } diff --git a/p2p/exchange.go b/p2p/exchange.go index b88ce7ca..816ff7e8 100644 --- a/p2p/exchange.go +++ b/p2p/exchange.go @@ -32,7 +32,7 @@ var maxUntrustedHeadRequests = 4 // Exchange enables sending outbound HeaderRequests to the network as well as // handling inbound HeaderRequests from the network. -type Exchange[H header.Header] struct { +type Exchange[H header.Header[H]] struct { ctx context.Context cancel context.CancelFunc @@ -47,7 +47,7 @@ type Exchange[H header.Header] struct { metrics *metrics } -func NewExchange[H header.Header]( +func NewExchange[H header.Header[H]]( host host.Host, peers peer.IDSlice, gater *conngater.BasicConnectionGater, @@ -100,7 +100,7 @@ func (ex *Exchange[H]) Stop(ctx context.Context) error { // The Head must be verified thereafter where possible. // We request in parallel all the trusted peers, compare their response // and return the highest one. -func (ex *Exchange[H]) Head(ctx context.Context, opts ...header.HeadOption) (H, error) { +func (ex *Exchange[H]) Head(ctx context.Context, opts ...header.HeadOption[H]) (H, error) { log.Debug("requesting head") reqCtx := ctx @@ -115,7 +115,7 @@ func (ex *Exchange[H]) Head(ctx context.Context, opts ...header.HeadOption) (H, defer cancel() } - reqParams := header.HeadParams{} + reqParams := header.HeadParams[H]{} for _, opt := range opts { opt(&reqParams) } @@ -344,7 +344,7 @@ func shufflePeers(peers peer.IDSlice) peer.IDSlice { // * should be received at least from 2 peers; // If neither condition is met, then latest Header will be returned (header of the highest // height). -func bestHead[H header.Header](result []H) (H, error) { +func bestHead[H header.Header[H]](result []H) (H, error) { if len(result) == 0 { var zero H return zero, header.ErrNotFound diff --git a/p2p/exchange_test.go b/p2p/exchange_test.go index 47d6e60f..ca772d8d 100644 --- a/p2p/exchange_test.go +++ b/p2p/exchange_test.go @@ -52,7 +52,7 @@ func TestExchange_RequestHead(t *testing.T) { tests := []struct { requestFromTrusted bool - lastHeader header.Header + lastHeader header.Header[*headertest.DummyHeader] expectedHeight int64 expectedHash header.Hash }{ @@ -74,7 +74,7 @@ func TestExchange_RequestHead(t *testing.T) { for i, tt := range tests { t.Run(strconv.Itoa(i), func(t *testing.T) { - var opts []header.HeadOption + var opts []header.HeadOption[*headertest.DummyHeader] if !tt.requestFromTrusted { opts = append(opts, header.WithTrustedHead(tt.lastHeader)) } @@ -583,7 +583,7 @@ func (t *timedOutStore) HasAt(_ context.Context, _ uint64) bool { return true } -func (t *timedOutStore) Head(context.Context, ...header.HeadOption) (*headertest.DummyHeader, error) { +func (t *timedOutStore) Head(context.Context, ...header.HeadOption[*headertest.DummyHeader]) (*headertest.DummyHeader, error) { time.Sleep(t.timeout) return nil, header.ErrNoHead } diff --git a/p2p/server.go b/p2p/server.go index a33a08f7..3c3f9f82 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -25,7 +25,7 @@ var ( // ExchangeServer represents the server-side component for // responding to inbound header-related requests. -type ExchangeServer[H header.Header] struct { +type ExchangeServer[H header.Header[H]] struct { protocolID protocol.ID host host.Host @@ -39,7 +39,7 @@ type ExchangeServer[H header.Header] struct { // NewExchangeServer returns a new P2P server that handles inbound // header-related requests. -func NewExchangeServer[H header.Header]( +func NewExchangeServer[H header.Header[H]]( host host.Host, store header.Store[H], opts ...Option[ServerParameters], diff --git a/p2p/session.go b/p2p/session.go index 85ac3738..2ea21e55 100644 --- a/p2p/session.go +++ b/p2p/session.go @@ -18,9 +18,9 @@ import ( // response. var errEmptyResponse = errors.New("empty response") -type option[H header.Header] func(*session[H]) +type option[H header.Header[H]] func(*session[H]) -func withValidation[H header.Header](from H) option[H] { +func withValidation[H header.Header[H]](from H) option[H] { return func(s *session[H]) { s.from = from } @@ -28,7 +28,7 @@ func withValidation[H header.Header](from H) option[H] { // session aims to divide a range of headers // into several smaller requests among different peers. -type session[H header.Header] struct { +type session[H header.Header[H]] struct { host host.Host protocolID protocol.ID queue *peerQueue @@ -45,7 +45,7 @@ type session[H header.Header] struct { reqCh chan *p2p_pb.HeaderRequest } -func newSession[H header.Header]( +func newSession[H header.Header[H]]( ctx context.Context, h host.Host, peerTracker *peerTracker, @@ -284,7 +284,7 @@ func prepareRequests(from, amount, headersPerPeer uint64) []*p2p_pb.HeaderReques } // processResponses converts HeaderResponses to Headers -func processResponses[H header.Header](resps []*p2p_pb.HeaderResponse) ([]H, error) { +func processResponses[H header.Header[H]](resps []*p2p_pb.HeaderResponse) ([]H, error) { if len(resps) == 0 { return nil, errEmptyResponse } diff --git a/p2p/subscriber.go b/p2p/subscriber.go index 8f89caa6..6d80f10f 100644 --- a/p2p/subscriber.go +++ b/p2p/subscriber.go @@ -14,7 +14,7 @@ import ( // Subscriber manages the lifecycle and relationship of header Module // with the "header-sub" gossipsub topic. -type Subscriber[H header.Header] struct { +type Subscriber[H header.Header[H]] struct { pubsubTopicID string pubsub *pubsub.PubSub @@ -24,7 +24,7 @@ type Subscriber[H header.Header] struct { // NewSubscriber returns a Subscriber that manages the header Module's // relationship with the "header-sub" gossipsub topic. -func NewSubscriber[H header.Header]( +func NewSubscriber[H header.Header[H]]( ps *pubsub.PubSub, msgID pubsub.MsgIdFunction, networkID string, @@ -58,7 +58,8 @@ func (p *Subscriber[H]) Stop(context.Context) error { // Does not punish peers if *header.VerifyError is given with Uncertain set to true. func (p *Subscriber[H]) SetVerifier(val func(context.Context, H) error) error { pval := func(ctx context.Context, p peer.ID, msg *pubsub.Message) pubsub.ValidationResult { - hdr := header.New[H]() + var hdr H + hdr = hdr.New() err := hdr.UnmarshalBinary(msg.Data) if err != nil { log.Errorw("unmarshalling header", diff --git a/p2p/subscription.go b/p2p/subscription.go index 3dcd08bc..1f3a608a 100644 --- a/p2p/subscription.go +++ b/p2p/subscription.go @@ -11,14 +11,14 @@ import ( ) // subscription handles retrieving Headers from the header pubsub topic. -type subscription[H header.Header] struct { +type subscription[H header.Header[H]] struct { topic *pubsub.Topic subscription *pubsub.Subscription } // newSubscription creates a new Header event subscription // on the given host. -func newSubscription[H header.Header](topic *pubsub.Topic) (*subscription[H], error) { +func newSubscription[H header.Header[H]](topic *pubsub.Topic) (*subscription[H], error) { sub, err := topic.Subscribe() if err != nil { return nil, err diff --git a/store/batch.go b/store/batch.go index 6bde1d09..3d41f5fa 100644 --- a/store/batch.go +++ b/store/batch.go @@ -13,14 +13,14 @@ import ( // unlike the Store which keeps 'hash -> header' and 'height -> hash'. // The approach simplifies implementation for the batch and // makes it better optimized for the GetByHeight case which is what we need. -type batch[H header.Header] struct { +type batch[H header.Header[H]] struct { lk sync.RWMutex heights map[string]uint64 headers []H } // newBatch creates the batch with the given pre-allocated size. -func newBatch[H header.Header](size int) *batch[H] { +func newBatch[H header.Header[H]](size int) *batch[H] { return &batch[H]{ heights: make(map[string]uint64, size), headers: make([]H, 0, size), diff --git a/store/height_indexer.go b/store/height_indexer.go index 9a53c0a7..e8f25fe5 100644 --- a/store/height_indexer.go +++ b/store/height_indexer.go @@ -12,13 +12,13 @@ import ( // TODO(@Wondertan): There should be a more clever way to index heights, than just storing // HeightToHash pair... heightIndexer simply stores and cashes mappings between header Height and // Hash. -type heightIndexer[H header.Header] struct { +type heightIndexer[H header.Header[H]] struct { ds datastore.Batching cache *lru.ARCCache } // newHeightIndexer creates new heightIndexer. -func newHeightIndexer[H header.Header](ds datastore.Batching, indexCacheSize int) (*heightIndexer[H], error) { +func newHeightIndexer[H header.Header[H]](ds datastore.Batching, indexCacheSize int) (*heightIndexer[H], error) { cache, err := lru.NewARC(indexCacheSize) if err != nil { return nil, err diff --git a/store/heightsub.go b/store/heightsub.go index 6fb8b4b2..b75ef83c 100644 --- a/store/heightsub.go +++ b/store/heightsub.go @@ -13,7 +13,7 @@ import ( var errElapsedHeight = errors.New("elapsed height") // heightSub provides a minimalistic mechanism to wait till header for a height becomes available. -type heightSub[H header.Header] struct { +type heightSub[H header.Header[H]] struct { // height refers to the latest locally available header height // that has been fully verified and inserted into the subjective chain height atomic.Uint64 @@ -22,7 +22,7 @@ type heightSub[H header.Header] struct { } // newHeightSub instantiates new heightSub. -func newHeightSub[H header.Header]() *heightSub[H] { +func newHeightSub[H header.Header[H]]() *heightSub[H] { return &heightSub[H]{ heightReqs: make(map[uint64][]chan H), } diff --git a/store/init.go b/store/init.go index dff8b5ce..5d26a97b 100644 --- a/store/init.go +++ b/store/init.go @@ -9,7 +9,7 @@ import ( // Init ensures a Store is initialized. If it is not already initialized, // it initializes the Store by requesting the header with the given hash. -func Init[H header.Header](ctx context.Context, store header.Store[H], ex header.Exchange[H], hash header.Hash) error { +func Init[H header.Header[H]](ctx context.Context, store header.Store[H], ex header.Exchange[H], hash header.Hash) error { _, err := store.Head(ctx) switch { default: diff --git a/store/keys.go b/store/keys.go index e9c8aba3..c42b4c80 100644 --- a/store/keys.go +++ b/store/keys.go @@ -16,6 +16,6 @@ func heightKey(h uint64) datastore.Key { return datastore.NewKey(strconv.Itoa(int(h))) } -func headerKey(h header.Header) datastore.Key { +func headerKey[H header.Header[H]](h H) datastore.Key { return datastore.NewKey(h.Hash().String()) } diff --git a/store/store.go b/store/store.go index afaa61ec..4e76e8d5 100644 --- a/store/store.go +++ b/store/store.go @@ -25,7 +25,7 @@ var ( ) // Store implements the Store interface for Headers over Datastore. -type Store[H header.Header] struct { +type Store[H header.Header[H]] struct { // header storing // // underlying KV store @@ -58,12 +58,12 @@ type Store[H header.Header] struct { // NewStore constructs a Store over datastore. // The datastore must have a head there otherwise Start will error. // For first initialization of Store use NewStoreWithHead. -func NewStore[H header.Header](ds datastore.Batching, opts ...Option) (*Store[H], error) { +func NewStore[H header.Header[H]](ds datastore.Batching, opts ...Option) (*Store[H], error) { return newStore[H](ds, opts...) } // NewStoreWithHead initiates a new Store and forcefully sets a given trusted header as head. -func NewStoreWithHead[H header.Header]( +func NewStoreWithHead[H header.Header[H]]( ctx context.Context, ds datastore.Batching, head H, @@ -77,7 +77,7 @@ func NewStoreWithHead[H header.Header]( return store, store.Init(ctx, head) } -func newStore[H header.Header](ds datastore.Batching, opts ...Option) (*Store[H], error) { +func newStore[H header.Header[H]](ds datastore.Batching, opts ...Option) (*Store[H], error) { params := DefaultParameters() for _, opt := range opts { opt(¶ms) @@ -159,7 +159,7 @@ func (s *Store[H]) Height() uint64 { return s.heightSub.Height() } -func (s *Store[H]) Head(ctx context.Context, _ ...header.HeadOption) (H, error) { +func (s *Store[H]) Head(ctx context.Context, _ ...header.HeadOption[H]) (H, error) { head, err := s.GetByHeight(ctx, s.heightSub.Height()) if err == nil { return head, nil diff --git a/sync/ranges.go b/sync/ranges.go index 3f3805a1..4329ab0c 100644 --- a/sync/ranges.go +++ b/sync/ranges.go @@ -9,7 +9,7 @@ import ( // ranges keeps non-overlapping and non-adjacent header ranges which are used to cache headers (in // ascending order). This prevents unnecessary / duplicate network requests for additional headers // during sync. -type ranges[H header.Header] struct { +type ranges[H header.Header[H]] struct { lk sync.RWMutex ranges []*headerRange[H] } @@ -86,13 +86,13 @@ func (rs *ranges[H]) First() (*headerRange[H], bool) { } } -type headerRange[H header.Header] struct { +type headerRange[H header.Header[H]] struct { lk sync.RWMutex headers []H start uint64 } -func newRange[H header.Header](h H) *headerRange[H] { +func newRange[H header.Header[H]](h H) *headerRange[H] { return &headerRange[H]{ start: uint64(h.Height()), headers: []H{h}, diff --git a/sync/sync.go b/sync/sync.go index 548d1920..b0cfa705 100644 --- a/sync/sync.go +++ b/sync/sync.go @@ -30,7 +30,7 @@ var log = logging.Logger("header/sync") // - Sets as the new Subjective Head, which // - if there is a gap between the previous and the new Subjective Head // - Triggers s.syncLoop and saves the Subjective Head in the pending so s.syncLoop can access it -type Syncer[H header.Header] struct { +type Syncer[H header.Header[H]] struct { sub header.Subscriber[H] // to subscribe for new Network Heads store syncStore[H] // to store all the headers to getter syncGetter[H] // to fetch headers from @@ -55,7 +55,7 @@ type Syncer[H header.Header] struct { } // NewSyncer creates a new instance of Syncer. -func NewSyncer[H header.Header]( +func NewSyncer[H header.Header[H]]( getter header.Getter[H], store header.Store[H], sub header.Subscriber[H], diff --git a/sync/sync_getter.go b/sync/sync_getter.go index 2a14e0ed..267240c5 100644 --- a/sync/sync_getter.go +++ b/sync/sync_getter.go @@ -9,7 +9,7 @@ import ( ) // syncGetter is a Getter wrapper that ensure only one Head call happens at the time -type syncGetter[H header.Header] struct { +type syncGetter[H header.Header[H]] struct { getterLk sync.RWMutex isGetterLk atomic.Bool header.Getter[H] @@ -39,7 +39,7 @@ func (sg *syncGetter[H]) Unlock() { } // Head must be called with held Lock. -func (sg *syncGetter[H]) Head(ctx context.Context, opts ...header.HeadOption) (H, error) { +func (sg *syncGetter[H]) Head(ctx context.Context, opts ...header.HeadOption[H]) (H, error) { sg.checkLock("Head without preceding Lock on syncGetter") return sg.Getter.Head(ctx, opts...) } diff --git a/sync/sync_getter_test.go b/sync/sync_getter_test.go index e92dd555..59cc5692 100644 --- a/sync/sync_getter_test.go +++ b/sync/sync_getter_test.go @@ -43,11 +43,11 @@ func TestSyncGetterHead(t *testing.T) { var errFakeHead = fmt.Errorf("head") -type fakeGetter[H header.Header] struct { +type fakeGetter[H header.Header[H]] struct { hits atomic.Uint32 } -func (f *fakeGetter[H]) Head(ctx context.Context, _ ...header.HeadOption) (h H, err error) { +func (f *fakeGetter[H]) Head(ctx context.Context, _ ...header.HeadOption[H]) (h H, err error) { f.hits.Add(1) select { case <-time.After(time.Millisecond * 100): diff --git a/sync/sync_head.go b/sync/sync_head.go index 9fa3f066..7fa78d0d 100644 --- a/sync/sync_head.go +++ b/sync/sync_head.go @@ -13,7 +13,7 @@ import ( // Known subjective head is considered network head if it is recent enough(now-timestamp<=blocktime) // Otherwise, head is requested from a trusted peer and // set as the new subjective head, assuming that trusted peer is always fully synced. -func (s *Syncer[H]) Head(ctx context.Context, _ ...header.HeadOption) (H, error) { +func (s *Syncer[H]) Head(ctx context.Context, _ ...header.HeadOption[H]) (H, error) { sbjHead, err := s.subjectiveHead(ctx) if err != nil { return sbjHead, err @@ -38,7 +38,7 @@ func (s *Syncer[H]) Head(ctx context.Context, _ ...header.HeadOption) (H, error) return s.Head(ctx) } defer s.getter.Unlock() - netHead, err := s.getter.Head(ctx, header.WithTrustedHead(sbjHead)) + netHead, err := s.getter.Head(ctx, header.WithTrustedHead[H](sbjHead)) if err != nil { log.Warnw("failed to get recent head, returning current subjective", "sbjHead", sbjHead.Height(), "err", err) return s.subjectiveHead(ctx) @@ -184,12 +184,12 @@ func (s *Syncer[H]) verify(ctx context.Context, newHead H) (bool, error) { } // isExpired checks if header is expired against trusting period. -func isExpired(header header.Header, period time.Duration) bool { +func isExpired[H header.Header[H]](header H, period time.Duration) bool { expirationTime := header.Time().Add(period) return !expirationTime.After(time.Now()) } // isRecent checks if header is recent against the given blockTime. -func isRecent(header header.Header, blockTime time.Duration) bool { +func isRecent[H header.Header[H]](header H, blockTime time.Duration) bool { return time.Since(header.Time()) <= blockTime+blockTime/2 // add half block time drift } diff --git a/sync/sync_head_test.go b/sync/sync_head_test.go index 9ed6b1a3..4af2ed25 100644 --- a/sync/sync_head_test.go +++ b/sync/sync_head_test.go @@ -114,8 +114,8 @@ func newWrappedGetter(ex header.Exchange[*headertest.DummyHeader]) *wrappedGette } } -func (t *wrappedGetter) Head(ctx context.Context, options ...header.HeadOption) (*headertest.DummyHeader, error) { - params := header.HeadParams{} +func (t *wrappedGetter) Head(ctx context.Context, options ...header.HeadOption[*headertest.DummyHeader]) (*headertest.DummyHeader, error) { + params := header.HeadParams[*headertest.DummyHeader]{} for _, opt := range options { opt(¶ms) } diff --git a/sync/sync_store.go b/sync/sync_store.go index 770e7842..4ea8ecf7 100644 --- a/sync/sync_store.go +++ b/sync/sync_store.go @@ -10,7 +10,7 @@ import ( // syncStore is a Store wrapper that provides synchronization over writes and reads // for Head of underlying Store. Useful for Stores that do not guarantee synchrony between Append // and Head method. -type syncStore[H header.Header] struct { +type syncStore[H header.Header[H]] struct { header.Store[H] head atomic.Pointer[H] diff --git a/sync/sync_test.go b/sync/sync_test.go index 470b8367..cf2d2356 100644 --- a/sync/sync_test.go +++ b/sync/sync_test.go @@ -381,7 +381,7 @@ func TestSync_InvalidSyncTarget(t *testing.T) { require.Equal(t, expectedHead.Height(), storeHead.Height()) } -type delayedGetter[H header.Header] struct { +type delayedGetter[H header.Header[H]] struct { header.Getter[H] } diff --git a/sync/verify/verify.go b/sync/verify/verify.go index 32255def..1b4b4a97 100644 --- a/sync/verify/verify.go +++ b/sync/verify/verify.go @@ -40,7 +40,7 @@ func (vr *VerifyError) Unwrap() error { // // If heightThreshold is zero, uses DefaultHeightThreshold. // Always returns VerifyError. -func Verify[H header.Header](trstd, untrstd H, heightThreshold int64) error { +func Verify[H header.Header[H]](trstd, untrstd H, heightThreshold int64) error { // general mandatory verification err := verify[H](trstd, untrstd, heightThreshold) if err != nil { @@ -70,7 +70,7 @@ func Verify[H header.Header](trstd, untrstd H, heightThreshold int64) error { // verify is a little bro of Verify yet performs mandatory Header checks // for any Header implementation. -func verify[H header.Header](trstd, untrstd H, heightThreshold int64) error { +func verify[H header.Header[H]](trstd, untrstd H, heightThreshold int64) error { if heightThreshold == 0 { heightThreshold = DefaultHeightThreshold }