Skip to content

Commit

Permalink
refactor!: type parametrized Header
Browse files Browse the repository at this point in the history
  • Loading branch information
Wondertan committed Aug 24, 2023
1 parent 0ad9816 commit e3862da
Show file tree
Hide file tree
Showing 29 changed files with 83 additions and 82 deletions.
10 changes: 5 additions & 5 deletions header.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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

Expand All @@ -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()
}
6 changes: 3 additions & 3 deletions headertest/dummy_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
}

Expand Down
8 changes: 4 additions & 4 deletions headertest/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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,
Expand All @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion headertest/subscriber.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
16 changes: 8 additions & 8 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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]
}

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand All @@ -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)
}
6 changes: 3 additions & 3 deletions local/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand All @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
10 changes: 5 additions & 5 deletions opts.go
Original file line number Diff line number Diff line change
@@ -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
}
}
10 changes: 5 additions & 5 deletions p2p/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions p2p/exchange_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}{
Expand All @@ -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))
}
Expand Down Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions p2p/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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],
Expand Down
10 changes: 5 additions & 5 deletions p2p/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ 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
}
}

// 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
Expand All @@ -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,
Expand Down Expand Up @@ -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
}
Expand Down
7 changes: 4 additions & 3 deletions p2p/subscriber.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions p2p/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit e3862da

Please sign in to comment.