Repository: celestiaorg/celestia-node
Pull request #1947 (2023-03-21 11:12:59)

chore: extract libs/header into go-header
Extracting code into [go-header](https://github.com/celestiaorg/go-header). Only imports changes

codecov-commenter: ## [Codecov](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg) Report
> Merging [#1947](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg) (54e42ba) into [main](https://codecov.io/gh/celestiaorg/celestia-node/commit/ab428b854247eb5a609aa6e0313241a6e52050f6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg) (ab428b8) will **decrease** coverage by `2.47%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #1947      +/-   ##
==========================================
- Coverage   57.80%   55.33%   -2.47%     
==========================================
  Files         245      212      -33     
  Lines       16187    13432    -2755     
==========================================
- Hits         9357     7433    -1924     
+ Misses       5881     5208     -673     
+ Partials      949      791     -158     
```


| [Impacted Files](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg) | Coverage Δ | |
|---|---|---|
| [core/exchange.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-Y29yZS9leGNoYW5nZS5nbw==) | `27.27% <ø> (ø)` | |
| [core/fetcher.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-Y29yZS9mZXRjaGVyLmdv) | `47.32% <ø> (ø)` | |
| [core/listener.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-Y29yZS9saXN0ZW5lci5nbw==) | `48.61% <ø> (-4.17%)` | :arrow_down: |
| [das/coordinator.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-ZGFzL2Nvb3JkaW5hdG9yLmdv) | `89.18% <ø> (ø)` | |
| [das/daser.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-ZGFzL2Rhc2VyLmdv) | `67.03% <ø> (ø)` | |
| [das/subscriber.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-ZGFzL3N1YnNjcmliZXIuZ28=) | `88.23% <ø> (ø)` | |
| [das/worker.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-ZGFzL3dvcmtlci5nbw==) | `96.33% <ø> (ø)` | |
| [header/header.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-aGVhZGVyL2hlYWRlci5nbw==) | `63.63% <ø> (ø)` | |
| [header/headertest/testing.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-aGVhZGVyL2hlYWRlcnRlc3QvdGVzdGluZy5nbw==) | `83.39% <ø> (ø)` | |
| [header/verify.go](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg#diff-aGVhZGVyL3ZlcmlmeS5nbw==) | `88.00% <ø> (ø)` | |
| ... and [20 more](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg) | |

... and [1 file with indirect coverage changes](https://codecov.io/gh/celestiaorg/celestia-node/pull/1947/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg)

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=celestiaorg)


Patch:
diff --git a/cmd/cel-shed/header.go b/cmd/cel-shed/header.go
index 1069e703..8216f196 100644
--- a/cmd/cel-shed/header.go
+++ b/cmd/cel-shed/header.go
@@ -7,8 +7,9 @@ import (
 
 	"github.com/spf13/cobra"
 
+	"github.com/celestiaorg/go-header/store"
+
 	"github.com/celestiaorg/celestia-node/header"
-	"github.com/celestiaorg/celestia-node/libs/header/store"
 	"github.com/celestiaorg/celestia-node/nodebuilder"
 	"github.com/celestiaorg/celestia-node/nodebuilder/node"
 )
diff --git a/core/exchange.go b/core/exchange.go
index 97eaf261..156742f0 100644
--- a/core/exchange.go
+++ b/core/exchange.go
@@ -5,8 +5,9 @@ import (
 	"context"
 	"fmt"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share/eds"
 )
 
diff --git a/core/fetcher.go b/core/fetcher.go
index a23a6da2..adf5bfa9 100644
--- a/core/fetcher.go
+++ b/core/fetcher.go
@@ -8,7 +8,7 @@ import (
 	coretypes "github.com/tendermint/tendermint/rpc/core/types"
 	"github.com/tendermint/tendermint/types"
 
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
+	libhead "github.com/celestiaorg/go-header"
 )
 
 const newBlockSubscriber = "NewBlock/Events"
diff --git a/core/listener.go b/core/listener.go
index 78a33f25..8f7bdfe8 100644
--- a/core/listener.go
+++ b/core/listener.go
@@ -7,8 +7,9 @@ import (
 	pubsub "github.com/libp2p/go-libp2p-pubsub"
 	"github.com/tendermint/tendermint/types"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share/eds"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"
 )
diff --git a/das/coordinator.go b/das/coordinator.go
index 2aca5483..cd41073f 100644
--- a/das/coordinator.go
+++ b/das/coordinator.go
@@ -5,8 +5,9 @@ import (
 	"sync"
 	"time"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"
 )
 
diff --git a/das/daser.go b/das/daser.go
index 96e81f96..128fd1a4 100644
--- a/das/daser.go
+++ b/das/daser.go
@@ -9,9 +9,10 @@ import (
 	"github.com/ipfs/go-datastore"
 	logging "github.com/ipfs/go-log/v2"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/libs/fraud"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share"
 	"github.com/celestiaorg/celestia-node/share/eds/byzantine"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"
diff --git a/das/subscriber.go b/das/subscriber.go
index 54f93a55..18cc891c 100644
--- a/das/subscriber.go
+++ b/das/subscriber.go
@@ -3,8 +3,9 @@ package das
 import (
 	"context"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 )
 
 // subscriber subscribes to notifications about new headers in the network to keep
diff --git a/das/worker.go b/das/worker.go
index 3cd61ea0..6cd70224 100644
--- a/das/worker.go
+++ b/das/worker.go
@@ -9,8 +9,9 @@ import (
 
 	"go.uber.org/multierr"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"
 )
 
diff --git a/go.mod b/go.mod
index ce1caab3..48136882 100644
--- a/go.mod
+++ b/go.mod
@@ -10,6 +10,7 @@ require (
 	github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921
 	github.com/benbjohnson/clock v1.3.0
 	github.com/celestiaorg/celestia-app v0.12.0
+	github.com/celestiaorg/go-header v0.2.0
 	github.com/celestiaorg/go-libp2p-messenger v0.2.0
 	github.com/celestiaorg/nmt v0.14.0
 	github.com/celestiaorg/rsmt2d v0.8.0
diff --git a/go.sum b/go.sum
index bf691cce..9cd71683 100644
--- a/go.sum
+++ b/go.sum
@@ -208,6 +208,8 @@ github.com/celestiaorg/cosmos-sdk v1.8.0-sdk-v0.46.7 h1:EADZy33ufskVIy6Rj6jbi3SO
 github.com/celestiaorg/cosmos-sdk v1.8.0-sdk-v0.46.7/go.mod h1:vg3Eza9adJJ5Mdx6boz5MpZsZcTZyrfTVYZHyi2zLm4=
 github.com/celestiaorg/dagstore v0.0.0-20221014072825-395797efb659 h1:f3205vw3GYBtMiNoS+qB6IuHSs50Iwqsm9lNIikLTCk=
 github.com/celestiaorg/dagstore v0.0.0-20221014072825-395797efb659/go.mod h1:ta/DlqIH10bvhwqJIw51Nq3QU4XVMp6pz3f0Deve9fM=
+github.com/celestiaorg/go-header v0.2.0 h1:UnufpDXQGLpP40SyiwfZLRT7alKLmo3lraPaJtsV8qI=
+github.com/celestiaorg/go-header v0.2.0/go.mod h1:6XKf0yhoEQqfKQTZnyTZjTjF5jH5Wq9uO9AvDMkdYbs=
 github.com/celestiaorg/go-libp2p-messenger v0.2.0 h1:/0MuPDcFamQMbw9xTZ73yImqgTO3jHV7wKHvWD/Irao=
 github.com/celestiaorg/go-libp2p-messenger v0.2.0/go.mod h1:s9PIhMi7ApOauIsfBcQwbr7m+HBzmVfDIS+QLdgzDSo=
 github.com/celestiaorg/go-verifcid v0.0.1-lazypatch h1:9TSe3w1cmJmbWlweCwCTIZkan7jV8M+KwglXpdD+UG8=
diff --git a/header/header.go b/header/header.go
index 1b1a0fd2..d1581890 100644
--- a/header/header.go
+++ b/header/header.go
@@ -11,9 +11,8 @@ import (
 	core "github.com/tendermint/tendermint/types"
 
 	"github.com/celestiaorg/celestia-app/pkg/da"
+	libhead "github.com/celestiaorg/go-header"
 	"github.com/celestiaorg/rsmt2d"
-
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 )
 
 // ConstructFn aliases a function that creates an ExtendedHeader.
diff --git a/header/verify.go b/header/verify.go
index eebb39c0..18e7f91e 100644
--- a/header/verify.go
+++ b/header/verify.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/tendermint/tendermint/light"
 
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
+	libhead "github.com/celestiaorg/go-header"
 )
 
 // Verify validates given untrusted Header against trusted ExtendedHeader.
diff --git a/libs/fraud/interface.go b/libs/fraud/interface.go
index 8df44c2b..1ae8b099 100644
--- a/libs/fraud/interface.go
+++ b/libs/fraud/interface.go
@@ -5,7 +5,7 @@ import (
 
 	logging "github.com/ipfs/go-log/v2"
 
-	"github.com/celestiaorg/celestia-node/libs/header"
+	"github.com/celestiaorg/go-header"
 )
 
 var log = logging.Logger("fraud")
diff --git a/libs/fraud/proof.go b/libs/fraud/proof.go
index c26e1b4e..d8d0a81c 100644
--- a/libs/fraud/proof.go
+++ b/libs/fraud/proof.go
@@ -8,7 +8,7 @@ import (
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric/global"
 
-	"github.com/celestiaorg/celestia-node/libs/header"
+	"github.com/celestiaorg/go-header"
 )
 
 var (
diff --git a/libs/header/doc.go b/libs/header/doc.go
deleted file mode 100644
index a2505a5c..00000000
--- a/libs/header/doc.go
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
-Package header contains all services related to generating, requesting, syncing and storing Headers.
-
-There are 4 main components in the header package:
- 1. p2p.Subscriber listens for new Headers from the P2P network (via the HeaderSub)
- 2. p2p.Exchange request Headers from other nodes
- 3. Syncer manages syncing of past and recent Headers from either the P2P network
- 4. Store manages storing Headers and making them available for access by other dependent services.
-*/
-package header
diff --git a/libs/header/errors.go b/libs/header/errors.go
deleted file mode 100644
index 8a04f6b6..00000000
--- a/libs/header/errors.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package header
-
-import "fmt"
-
-// VerifyError is thrown on during Verify if it fails.
-type VerifyError struct {
-	Reason error
-}
-
-func (vr *VerifyError) Error() string {
-	return fmt.Sprintf("header: verify: %s", vr.Reason.Error())
-}
diff --git a/libs/header/hash.go b/libs/header/hash.go
deleted file mode 100644
index 156cd2bb..00000000
--- a/libs/header/hash.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package header
-
-import (
-	"encoding/hex"
-	"fmt"
-	"strings"
-)
-
-// Hash represents cryptographic hash and provides basic serialization functions.
-type Hash []byte
-
-// String implements fmt.Stringer interface.
-func (h Hash) String() string {
-	return strings.ToUpper(hex.EncodeToString(h))
-}
-
-// MarshalJSON serializes Hash into valid JSON.
-func (h Hash) MarshalJSON() ([]byte, error) {
-	s := strings.ToUpper(hex.EncodeToString(h))
-	jbz := make([]byte, len(s)+2)
-	jbz[0] = '"'
-	copy(jbz[1:], s)
-	jbz[len(jbz)-1] = '"'
-	return jbz, nil
-}
-
-// UnmarshalJSON deserializes JSON representation of a Hash into object.
-func (h *Hash) UnmarshalJSON(data []byte) error {
-	if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
-		return fmt.Errorf("invalid hex string: %s", data)
-	}
-	bz2, err := hex.DecodeString(string(data[1 : len(data)-1]))
-	if err != nil {
-		return err
-	}
-	*h = bz2
-	return nil
-}
diff --git a/libs/header/header.go b/libs/header/header.go
deleted file mode 100644
index cdf27db4..00000000
--- a/libs/header/header.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package header
-
-import (
-	"encoding"
-	"time"
-)
-
-// Header abstracts all methods required to perform header sync.
-type Header interface {
-	// New creates new instance of a header.
-	New() Header
-	// IsZero reports whether Header is a zero value of it's concrete type.
-	IsZero() bool
-	// ChainID returns identifier of the chain (ChainID).
-	ChainID() string
-	// Hash returns hash of a header.
-	Hash() Hash
-	// Height returns the height of a header.
-	Height() int64
-	// LastHeader returns the hash of last header before this header (aka. previous header hash).
-	LastHeader() Hash
-	// Time returns time when header was created.
-	Time() time.Time
-	// Verify validates given untrusted Header against trusted Header.
-	Verify(Header) error
-	// Validate performs basic validation to check for missed/incorrect fields.
-	Validate() error
-
-	encoding.BinaryMarshaler
-	encoding.BinaryUnmarshaler
-}
diff --git a/libs/header/interface.go b/libs/header/interface.go
deleted file mode 100644
index 57fe1574..00000000
--- a/libs/header/interface.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package header
-
-import (
-	"context"
-	"errors"
-	"fmt"
-
-	pubsub "github.com/libp2p/go-libp2p-pubsub"
-)
-
-const (
-	// MaxRangeRequestSize defines the max amount of headers that can be handled/requested at once.
-	MaxRangeRequestSize uint64 = 512
-)
-
-// Subscriber encompasses the behavior necessary to
-// subscribe/unsubscribe from new Header events from the
-// network.
-type Subscriber[H Header] interface {
-	// Subscribe creates long-living Subscription for validated Headers.
-	// Multiple Subscriptions can be created.
-	Subscribe() (Subscription[H], error)
-	// AddValidator registers a Validator for all Subscriptions.
-	// Registered Validators screen Headers for their validity
-	// before they are sent through Subscriptions.
-	// Multiple validators can be registered.
-	AddValidator(func(context.Context, H) pubsub.ValidationResult) error
-}
-
-// Subscription can retrieve the next Header from the
-// network.
-type Subscription[H Header] interface {
-	// NextHeader returns the newest verified and valid Header
-	// in the network.
-	NextHeader(ctx context.Context) (H, error)
-	// Cancel cancels the subscription.
-	Cancel()
-}
-
-// Broadcaster broadcasts a Header to the network.
-type Broadcaster[H Header] 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 {
-	Getter[H]
-}
-
-var (
-	// ErrNotFound is returned when there is no requested header.
-	ErrNotFound = errors.New("header: not found")
-
-	// ErrNoHead is returned when Store is empty (does not contain any known header).
-	ErrNoHead = fmt.Errorf("header/store: no chain head")
-
-	// ErrHeadersLimitExceeded is returned when ExchangeServer receives header request for more
-	// than maxRequestSize headers.
-	ErrHeadersLimitExceeded = errors.New("header/p2p: header limit per 1 request exceeded")
-)
-
-// ErrNonAdjacent is returned when Store is appended with a header not adjacent to the stored head.
-type ErrNonAdjacent struct {
-	Head      int64
-	Attempted int64
-}
-
-func (ena *ErrNonAdjacent) Error() string {
-	return fmt.Sprintf("header/store: non-adjacent: head %d, attempted %d", ena.Head, ena.Attempted)
-}
-
-// Store encompasses the behavior necessary to store and retrieve Headers
-// from a node's local storage.
-type Store[H Header] interface {
-	// Start starts the store.
-	Start(context.Context) error
-
-	// Stop stops the store by preventing further writes
-	// and waiting till the ongoing ones are done.
-	Stop(context.Context) error
-
-	// Getter encompasses all getter methods for headers.
-	Getter[H]
-
-	// Init initializes Store with the given head, meaning it is initialized with the genesis header.
-	Init(context.Context, H) error
-
-	// Height reports current height of the chain head.
-	Height() uint64
-
-	// Has checks whether Header is already stored.
-	Has(context.Context, Hash) (bool, error)
-
-	// HasAt checks whether Header at the given height is already stored.
-	HasAt(context.Context, uint64) bool
-
-	// Append stores and verifies the given Header(s).
-	// It requires them to be adjacent and in ascending order,
-	// as it applies them contiguously on top of the current head height.
-	// It returns the amount of successfully applied headers,
-	// so caller can understand what given header was invalid, if any.
-	Append(context.Context, ...H) error
-}
-
-// Getter contains the behavior necessary for a component to retrieve
-// headers that have been processed during header sync.
-type Getter[H Header] interface {
-	Head[H]
-
-	// Get returns the Header corresponding to the given hash.
-	Get(context.Context, Hash) (H, error)
-
-	// GetByHeight returns the Header corresponding to the given block height.
-	GetByHeight(context.Context, uint64) (H, error)
-
-	// GetRangeByHeight returns the given range of Headers.
-	GetRangeByHeight(ctx context.Context, from, amount uint64) ([]H, error)
-
-	// GetVerifiedRange requests the header range from the provided Header and
-	// verifies that the returned headers are adjacent to each other.
-	GetVerifiedRange(ctx context.Context, from H, amount uint64) ([]H, error)
-}
-
-// 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 {
-	// Head returns the latest known header.
-	Head(context.Context) (H, error)
-}
diff --git a/libs/header/local/exchange.go b/libs/header/local/exchange.go
deleted file mode 100644
index 515e94fc..00000000
--- a/libs/header/local/exchange.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package local
-
-import (
-	"context"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// Exchange is a simple Exchange that reads Headers from Store without any networking.
-type Exchange[H header.Header] struct {
-	store header.Store[H]
-}
-
-// NewExchange creates a new local Exchange.
-func NewExchange[H header.Header](store header.Store[H]) header.Exchange[H] {
-	return &Exchange[H]{
-		store: store,
-	}
-}
-
-func (l *Exchange[H]) Start(context.Context) error {
-	return nil
-}
-
-func (l *Exchange[H]) Stop(context.Context) error {
-	return nil
-}
-
-func (l *Exchange[H]) Head(ctx context.Context) (H, error) {
-	return l.store.Head(ctx)
-}
-
-func (l *Exchange[H]) GetByHeight(ctx context.Context, height uint64) (H, error) {
-	return l.store.GetByHeight(ctx, height)
-}
-
-func (l *Exchange[H]) GetRangeByHeight(ctx context.Context, origin, amount uint64) ([]H, error) {
-	if amount == 0 {
-		return nil, nil
-	}
-	return l.store.GetRangeByHeight(ctx, origin, origin+amount)
-}
-
-func (l *Exchange[H]) GetVerifiedRange(ctx context.Context, from H, amount uint64,
-) ([]H, error) {
-	return l.store.GetVerifiedRange(ctx, from, uint64(from.Height())+amount+1)
-}
-
-func (l *Exchange[H]) Get(ctx context.Context, hash header.Hash) (H, error) {
-	return l.store.Get(ctx, hash)
-}
diff --git a/libs/header/metrics.go b/libs/header/metrics.go
deleted file mode 100644
index 6e9dcf37..00000000
--- a/libs/header/metrics.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package header
-
-import (
-	"context"
-	"time"
-
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/global"
-	"go.opentelemetry.io/otel/metric/instrument"
-	"go.opentelemetry.io/otel/metric/unit"
-)
-
-var meter = global.MeterProvider().Meter("header")
-
-// WithMetrics enables Otel metrics to monitor head and total amount of synced headers.
-func WithMetrics[H Header](store Store[H]) error {
-	headC, _ := meter.AsyncInt64().Counter(
-		"head",
-		instrument.WithUnit(unit.Dimensionless),
-		instrument.WithDescription("Subjective head of the node"),
-	)
-
-	err := meter.RegisterCallback(
-		[]instrument.Asynchronous{
-			headC,
-		},
-		func(ctx context.Context) {
-			// add timeout to limit the time it takes to get the head
-			// in case there is a deadlock
-			ctx, cancel := context.WithTimeout(ctx, time.Second)
-			defer cancel()
-
-			head, err := store.Head(ctx)
-			if err != nil {
-				headC.Observe(ctx, 0, attribute.String("err", err.Error()))
-				return
-			}
-
-			headC.Observe(
-				ctx,
-				head.Height(),
-			)
-		},
-	)
-	return err
-}
diff --git a/libs/header/mocks/store.go b/libs/header/mocks/store.go
deleted file mode 100644
index 8a047ea6..00000000
--- a/libs/header/mocks/store.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package mocks
-
-import (
-	"bytes"
-	"context"
-	"testing"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-type MockStore[H header.Header] struct {
-	Headers    map[int64]H
-	HeadHeight int64
-}
-
-// NewStore creates a mock store and adds several random
-// headers
-func NewStore[H header.Header](t *testing.T, gen test.Generator[H], numHeaders int) *MockStore[H] {
-	store := &MockStore[H]{
-		Headers:    make(map[int64]H),
-		HeadHeight: 0,
-	}
-
-	for i := 0; i < numHeaders; i++ {
-		header := gen.GetRandomHeader()
-		store.Headers[header.Height()] = header
-
-		if header.Height() > store.HeadHeight {
-			store.HeadHeight = header.Height()
-		}
-	}
-	return store
-}
-
-func (m *MockStore[H]) Init(context.Context, H) error { return nil }
-func (m *MockStore[H]) Start(context.Context) error   { return nil }
-func (m *MockStore[H]) Stop(context.Context) error    { return nil }
-
-func (m *MockStore[H]) Height() uint64 {
-	return uint64(m.HeadHeight)
-}
-
-func (m *MockStore[H]) Head(context.Context) (H, error) {
-	return m.Headers[m.HeadHeight], nil
-}
-
-func (m *MockStore[H]) Get(ctx context.Context, hash header.Hash) (H, error) {
-	for _, header := range m.Headers {
-		if bytes.Equal(header.Hash(), hash) {
-			return header, nil
-		}
-	}
-	var zero H
-	return zero, header.ErrNotFound
-}
-
-func (m *MockStore[H]) GetByHeight(ctx context.Context, height uint64) (H, error) {
-	return m.Headers[int64(height)], nil
-}
-
-func (m *MockStore[H]) GetRangeByHeight(ctx context.Context, from, to uint64) ([]H, error) {
-	headers := make([]H, to-from)
-	// As the requested range is [from; to),
-	// check that (to-1) height in request is less than
-	// the biggest header height in store.
-	if to-1 > m.Height() {
-		return nil, header.ErrNotFound
-	}
-	for i := range headers {
-		headers[i] = m.Headers[int64(from)]
-		from++
-	}
-	return headers, nil
-}
-
-func (m *MockStore[H]) GetVerifiedRange(
-	ctx context.Context,
-	h H,
-	to uint64,
-) ([]H, error) {
-	return m.GetRangeByHeight(ctx, uint64(h.Height())+1, to)
-}
-
-func (m *MockStore[H]) Has(context.Context, header.Hash) (bool, error) {
-	return false, nil
-}
-
-func (m *MockStore[H]) HasAt(_ context.Context, height uint64) bool {
-	return height != 0 && m.HeadHeight >= int64(height)
-}
-
-func (m *MockStore[H]) Append(ctx context.Context, headers ...H) error {
-	for _, header := range headers {
-		m.Headers[header.Height()] = header
-		// set head
-		if header.Height() > m.HeadHeight {
-			m.HeadHeight = header.Height()
-		}
-	}
-	return nil
-}
diff --git a/libs/header/p2p/exchange.go b/libs/header/p2p/exchange.go
deleted file mode 100644
index 289414ea..00000000
--- a/libs/header/p2p/exchange.go
+++ /dev/null
@@ -1,346 +0,0 @@
-package p2p
-
-import (
-	"bytes"
-	"context"
-	"errors"
-	"fmt"
-	"math/rand"
-	"sort"
-	"time"
-
-	logging "github.com/ipfs/go-log/v2"
-	"github.com/libp2p/go-libp2p/core/host"
-	"github.com/libp2p/go-libp2p/core/peer"
-	"github.com/libp2p/go-libp2p/core/protocol"
-	"github.com/libp2p/go-libp2p/p2p/net/conngater"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	p2p_pb "github.com/celestiaorg/celestia-node/libs/header/p2p/pb"
-)
-
-var log = logging.Logger("header/p2p")
-
-// the minimum number of headers of the same height received from trusted peers
-// to determine the network head. If all trusted header will return headers with
-// non-equal height, then the highest header will be chosen.
-const minTrustedHeadResponses = 2
-
-// Exchange enables sending outbound HeaderRequests to the network as well as
-// handling inbound HeaderRequests from the network.
-type Exchange[H header.Header] struct {
-	ctx    context.Context
-	cancel context.CancelFunc
-
-	protocolID protocol.ID
-	host       host.Host
-
-	trustedPeers func() peer.IDSlice
-	peerTracker  *peerTracker
-
-	Params ClientParameters
-
-	metrics *metrics
-}
-
-func NewExchange[H header.Header](
-	host host.Host,
-	peers peer.IDSlice,
-	connGater *conngater.BasicConnectionGater,
-	opts ...Option[ClientParameters],
-) (*Exchange[H], error) {
-	params := DefaultClientParameters()
-	for _, opt := range opts {
-		opt(&params)
-	}
-
-	err := params.Validate()
-	if err != nil {
-		return nil, err
-	}
-
-	ex := &Exchange[H]{
-		host:       host,
-		protocolID: protocolID(params.networkID),
-		peerTracker: newPeerTracker(
-			host,
-			connGater,
-		),
-		Params: params,
-	}
-
-	ex.trustedPeers = func() peer.IDSlice {
-		return shufflePeers(peers)
-	}
-	return ex, nil
-}
-
-func (ex *Exchange[H]) Start(context.Context) error {
-	ex.ctx, ex.cancel = context.WithCancel(context.Background())
-	log.Infow("client: starting client", "protocol ID", ex.protocolID)
-
-	trustedPeers := ex.trustedPeers()
-
-	for _, p := range trustedPeers {
-		// Try to pre-connect to trusted peers.
-		// We don't really care if we succeed at this point
-		// and just need any peers in the peerTracker asap
-		go func(p peer.ID) {
-			err := ex.host.Connect(ex.ctx, peer.AddrInfo{ID: p})
-			if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) {
-				log.Debugw("err connecting to a bootstrap peer", "err", err, "peer", p)
-			}
-		}(p)
-	}
-	go ex.peerTracker.gc()
-	go ex.peerTracker.track()
-	return nil
-}
-
-func (ex *Exchange[H]) Stop(context.Context) error {
-	// cancel the session if it exists
-	ex.cancel()
-	// stop the peerTracker
-	ex.peerTracker.stop()
-	return nil
-}
-
-// Head requests the latest Header. Note that the Header must be verified thereafter.
-// NOTE:
-// It is fine to continue handling head request if the timeout will be reached.
-// As we are requesting head from multiple trusted peers,
-// we may already have some headers when the timeout will be reached.
-func (ex *Exchange[H]) Head(ctx context.Context) (H, error) {
-	log.Debug("requesting head")
-	// create request
-	req := &p2p_pb.HeaderRequest{
-		Data:   &p2p_pb.HeaderRequest_Origin{Origin: uint64(0)},
-		Amount: 1,
-	}
-
-	var (
-		zero     H
-		headerCh = make(chan H)
-	)
-
-	trustedPeers := ex.trustedPeers()
-	// request head from each trusted peer
-	for _, from := range trustedPeers {
-		go func(from peer.ID) {
-			// request ensures that the result slice will have at least one Header
-			headers, err := ex.request(ctx, from, req)
-			if err != nil {
-				log.Errorw("head request to trusted peer failed", "trustedPeer", from, "err", err)
-				var zero H
-				headerCh <- zero
-				return
-			}
-			headerCh <- headers[0]
-		}(from)
-	}
-
-	result := make([]H, 0, len(trustedPeers))
-LOOP:
-	for range trustedPeers {
-		select {
-		case h := <-headerCh:
-			if !h.IsZero() {
-				result = append(result, h)
-			}
-		case <-ctx.Done():
-			break LOOP
-		case <-ex.ctx.Done():
-			return zero, ctx.Err()
-		}
-	}
-
-	return bestHead[H](result)
-}
-
-// GetByHeight performs a request for the Header at the given
-// height to the network. Note that the Header must be verified
-// thereafter.
-func (ex *Exchange[H]) GetByHeight(ctx context.Context, height uint64) (H, error) {
-	log.Debugw("requesting header", "height", height)
-	var zero H
-	// sanity check height
-	if height == 0 {
-		return zero, fmt.Errorf("specified request height must be greater than 0")
-	}
-	// create request
-	req := &p2p_pb.HeaderRequest{
-		Data:   &p2p_pb.HeaderRequest_Origin{Origin: height},
-		Amount: 1,
-	}
-	headers, err := ex.performRequest(ctx, req)
-	if err != nil {
-		return zero, err
-	}
-	return headers[0], nil
-}
-
-// GetRangeByHeight performs a request for the given range of Headers
-// to the network. Note that the Headers must be verified thereafter.
-func (ex *Exchange[H]) GetRangeByHeight(ctx context.Context, from, amount uint64) ([]H, error) {
-	if amount == 0 {
-		return make([]H, 0), nil
-	}
-	if amount > header.MaxRangeRequestSize {
-		return nil, header.ErrHeadersLimitExceeded
-	}
-	session := newSession[H](ex.ctx, ex.host, ex.peerTracker, ex.protocolID, ex.Params.RangeRequestTimeout)
-	defer session.close()
-	return session.getRangeByHeight(ctx, from, amount, ex.Params.MaxHeadersPerRangeRequest)
-}
-
-// GetVerifiedRange performs a request for the given range of Headers to the network and
-// ensures that returned headers are correct against the passed one.
-func (ex *Exchange[H]) GetVerifiedRange(
-	ctx context.Context,
-	from H,
-	amount uint64,
-) ([]H, error) {
-	if amount == 0 {
-		return make([]H, 0), nil
-	}
-	session := newSession[H](
-		ex.ctx, ex.host, ex.peerTracker, ex.protocolID, ex.Params.RangeRequestTimeout, withValidation(from),
-	)
-	defer session.close()
-	// we request the next header height that we don't have: `fromHead`+1
-	return session.getRangeByHeight(ctx, uint64(from.Height())+1, amount, ex.Params.MaxHeadersPerRangeRequest)
-}
-
-// Get performs a request for the Header by the given hash corresponding
-// to the RawHeader. Note that the Header must be verified thereafter.
-func (ex *Exchange[H]) Get(ctx context.Context, hash header.Hash) (H, error) {
-	log.Debugw("requesting header", "hash", hash.String())
-	var zero H
-	// create request
-	req := &p2p_pb.HeaderRequest{
-		Data:   &p2p_pb.HeaderRequest_Hash{Hash: hash},
-		Amount: 1,
-	}
-	headers, err := ex.performRequest(ctx, req)
-	if err != nil {
-		return zero, err
-	}
-
-	if !bytes.Equal(headers[0].Hash(), hash) {
-		return zero, fmt.Errorf("incorrect hash in header: expected %x, got %x", hash, headers[0].Hash())
-	}
-	return headers[0], nil
-}
-
-func (ex *Exchange[H]) performRequest(
-	ctx context.Context,
-	req *p2p_pb.HeaderRequest,
-) ([]H, error) {
-	if req.Amount == 0 {
-		return make([]H, 0), nil
-	}
-
-	trustedPeers := ex.trustedPeers()
-	// TODO: Move this check to constructor(#1671)
-	if len(trustedPeers) == 0 {
-		return nil, fmt.Errorf("no trusted peers")
-	}
-	var reqErr error
-
-	for _, peer := range trustedPeers {
-		ctx, cancel := context.WithTimeout(ctx, ex.Params.TrustedPeersRequestTimeout)
-		h, err := ex.request(ctx, peer, req)
-		cancel()
-		switch err {
-		default:
-			reqErr = err
-			log.Debugw("requesting header from trustedPeer failed",
-				"trustedPeer", peer, "err", err)
-			continue
-		case context.Canceled, context.DeadlineExceeded, nil:
-			return h, err
-		}
-	}
-	return nil, reqErr
-}
-
-// request sends the HeaderRequest to a remote peer.
-func (ex *Exchange[H]) request(
-	ctx context.Context,
-	to peer.ID,
-	req *p2p_pb.HeaderRequest,
-) ([]H, error) {
-	log.Debugw("requesting peer", "peer", to)
-	responses, size, duration, err := sendMessage(ctx, ex.host, to, ex.protocolID, req)
-	ex.metrics.observeResponse(ctx, size, duration, err)
-	if err != nil {
-		log.Debugw("err sending request", "peer", to, "err", err)
-		return nil, err
-	}
-
-	headers := make([]H, 0, len(responses))
-	for _, response := range responses {
-		if err = convertStatusCodeToError(response.StatusCode); err != nil {
-			return nil, err
-		}
-		var empty H
-		header := empty.New()
-		err := header.UnmarshalBinary(response.Body)
-		if err != nil {
-			return nil, err
-		}
-		err = validateChainID(ex.Params.chainID, header.(H).ChainID())
-		if err != nil {
-			return nil, err
-		}
-		headers = append(headers, header.(H))
-	}
-
-	if len(headers) == 0 {
-		return nil, header.ErrNotFound
-	}
-	return headers, nil
-}
-
-// shufflePeers changes the order of trusted peers.
-func shufflePeers(peers peer.IDSlice) peer.IDSlice {
-	tpeers := make(peer.IDSlice, len(peers))
-	copy(tpeers, peers)
-	//nolint:gosec // G404: Use of weak random number generator
-	rand.New(rand.NewSource(time.Now().UnixNano())).Shuffle(
-		len(tpeers),
-		func(i, j int) { tpeers[i], tpeers[j] = tpeers[j], tpeers[i] },
-	)
-	return tpeers
-}
-
-// bestHead chooses Header that matches the conditions:
-// * should have max height among received;
-// * 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) {
-	if len(result) == 0 {
-		var zero H
-		return zero, header.ErrNotFound
-	}
-	counter := make(map[string]int)
-	// go through all of Headers and count the number of headers with a specific hash
-	for _, res := range result {
-		counter[res.Hash().String()]++
-	}
-	// sort results in a decreasing order
-	sort.Slice(result, func(i, j int) bool {
-		return result[i].Height() > result[j].Height()
-	})
-
-	// try to find Header with the maximum height that was received at least from 2 peers
-	for _, res := range result {
-		if counter[res.Hash().String()] >= minTrustedHeadResponses {
-			return res, nil
-		}
-	}
-	log.Debug("could not find latest header received from at least two peers, returning header with the max height")
-	// otherwise return header with the max height
-	return result[0], nil
-}
diff --git a/libs/header/p2p/gossip_score.go b/libs/header/p2p/gossip_score.go
deleted file mode 100644
index b00ac024..00000000
--- a/libs/header/p2p/gossip_score.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package p2p
-
-import (
-	"time"
-
-	pubsub "github.com/libp2p/go-libp2p-pubsub"
-)
-
-// GossibSubScore provides a set of recommended parameters for header GossipSub topic, a.k.a
-// HeaderSub.
-var GossibSubScore = pubsub.TopicScoreParams{
-	// expected > 1 tx/second
-	TopicWeight: 0.1, // max cap is 5, single invalid message is -100
-
-	// 1 tick per second, maxes at 1 hour
-	TimeInMeshWeight:  0.0002778, // ~1/3600
-	TimeInMeshQuantum: time.Second,
-	TimeInMeshCap:     1,
-
-	// deliveries decay after 1 hour, cap at 100 blocks
-	FirstMessageDeliveriesWeight: 5, // max value is 500
-	FirstMessageDeliveriesDecay:  pubsub.ScoreParameterDecay(time.Hour),
-	FirstMessageDeliveriesCap:    100, // 100 blocks in an hour
-
-	// invalid messages decay after 1 hour
-	InvalidMessageDeliveriesWeight: -1000,
-	InvalidMessageDeliveriesDecay:  pubsub.ScoreParameterDecay(time.Hour),
-
-	// Mesh Delivery Failure is currently turned off for messages
-	// This is on purpose as the network is still too small, which results in
-	// asymmetries and potential unmeshing from negative scores.
-}
diff --git a/libs/header/p2p/helpers.go b/libs/header/p2p/helpers.go
deleted file mode 100644
index d703c1db..00000000
--- a/libs/header/p2p/helpers.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"fmt"
-	"io"
-	"strings"
-	"time"
-
-	"github.com/libp2p/go-libp2p/core/host"
-	"github.com/libp2p/go-libp2p/core/peer"
-	"github.com/libp2p/go-libp2p/core/protocol"
-
-	"github.com/celestiaorg/go-libp2p-messenger/serde"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	p2p_pb "github.com/celestiaorg/celestia-node/libs/header/p2p/pb"
-)
-
-func protocolID(networkID string) protocol.ID {
-	return protocol.ID(fmt.Sprintf("/%s/header-ex/v0.0.3", networkID))
-}
-
-func PubsubTopicID(networkID string) string {
-	return fmt.Sprintf("/%s/header-sub/v0.0.1", networkID)
-}
-
-func validateChainID(want, have string) error {
-	if want != "" && !strings.EqualFold(want, have) {
-		return fmt.Errorf("header with different chainID received.want=%s,have=%s",
-			want, have,
-		)
-	}
-	return nil
-}
-
-// sendMessage opens the stream to the given peers and sends HeaderRequest to fetch
-// Headers. As a result sendMessage returns HeaderResponse, the size of fetched
-// data, the duration of the request and an error.
-func sendMessage(
-	ctx context.Context,
-	host host.Host,
-	to peer.ID,
-	protocol protocol.ID,
-	req *p2p_pb.HeaderRequest,
-) ([]*p2p_pb.HeaderResponse, uint64, uint64, error) {
-	startTime := time.Now()
-	stream, err := host.NewStream(ctx, to, protocol)
-	if err != nil {
-		return nil, 0, 0, fmt.Errorf("header/p2p: failed to open a new stream: %w", err)
-	}
-
-	// set stream deadline from the context deadline.
-	// if it is empty, then we assume that it will
-	// hang until the server will close the stream by the timeout.
-	if dl, ok := ctx.Deadline(); ok {
-		if err = stream.SetDeadline(dl); err != nil {
-			log.Debugw("error setting deadline: %s", err)
-		}
-	}
-
-	// send request
-	_, err = serde.Write(stream, req)
-	if err != nil {
-		stream.Reset() //nolint:errcheck
-		return nil, 0, 0, fmt.Errorf("header/p2p: failed to write a request: %w", err)
-	}
-
-	err = stream.CloseWrite()
-	if err != nil {
-		return nil, 0, 0, err
-	}
-
-	headers := make([]*p2p_pb.HeaderResponse, 0)
-
-	var totalRespLn uint64
-	for i := 0; i < int(req.Amount); i++ {
-		resp := new(p2p_pb.HeaderResponse)
-		respLn, readErr := serde.Read(stream, resp)
-		if readErr != nil {
-			err = readErr
-			break
-		}
-
-		totalRespLn += uint64(respLn)
-		headers = append(headers, resp)
-	}
-
-	duration := time.Since(startTime).Milliseconds()
-
-	// we allow the server side to explicitly close the connection
-	// if it does not have the requested range.
-	// In this case, server side will send us a response with ErrNotFound status code inside
-	// and then will close the stream.
-	// If the server side will have a part of the requested range, then it will send this part
-	// and then will close the connection
-	if err == io.EOF {
-		err = nil
-	}
-
-	if err == nil {
-		if closeErr := stream.Close(); closeErr != nil {
-			log.Errorw("closing stream", "err", closeErr)
-		}
-	} else {
-		// reset stream in case of an error
-		stream.Reset() //nolint:errcheck
-	}
-	return headers, totalRespLn, uint64(duration), err
-}
-
-// convertStatusCodeToError converts passed status code into an error.
-func convertStatusCodeToError(code p2p_pb.StatusCode) error {
-	switch code {
-	case p2p_pb.StatusCode_OK:
-		return nil
-	case p2p_pb.StatusCode_NOT_FOUND:
-		return header.ErrNotFound
-	default:
-		return fmt.Errorf("unknown status code %d", code)
-	}
-}
diff --git a/libs/header/p2p/metrics.go b/libs/header/p2p/metrics.go
deleted file mode 100644
index f6fe1d69..00000000
--- a/libs/header/p2p/metrics.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package p2p
-
-import (
-	"context"
-
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/metric/global"
-	"go.opentelemetry.io/otel/metric/instrument"
-	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
-)
-
-type metrics struct {
-	responseSize     syncfloat64.Histogram
-	responseDuration syncfloat64.Histogram
-}
-
-var (
-	meter = global.MeterProvider().Meter("header/p2p")
-)
-
-func (ex *Exchange[H]) InitMetrics() error {
-	responseSize, err := meter.
-		SyncFloat64().
-		Histogram(
-			"header_p2p_headers_response_size",
-			instrument.WithDescription("Size of get headers response in bytes"),
-		)
-	if err != nil {
-		return err
-	}
-
-	responseDuration, err := meter.
-		SyncFloat64().
-		Histogram(
-			"header_p2p_headers_request_duration",
-			instrument.WithDescription("Duration of get headers request in seconds"),
-		)
-	if err != nil {
-		return err
-	}
-
-	ex.metrics = &metrics{
-		responseSize:     responseSize,
-		responseDuration: responseDuration,
-	}
-	return nil
-}
-
-func (m *metrics) observeResponse(ctx context.Context, size uint64, duration uint64, err error) {
-	if m == nil {
-		return
-	}
-	m.responseSize.Record(
-		ctx,
-		float64(size),
-		attribute.Bool("failed", err != nil),
-	)
-	m.responseDuration.Record(
-		ctx,
-		float64(duration),
-		attribute.Bool("failed", err != nil),
-	)
-}
diff --git a/libs/header/p2p/options.go b/libs/header/p2p/options.go
deleted file mode 100644
index 15c94d19..00000000
--- a/libs/header/p2p/options.go
+++ /dev/null
@@ -1,169 +0,0 @@
-package p2p
-
-import (
-	"fmt"
-	"time"
-)
-
-// parameters is an interface that encompasses all params needed for
-// client and server parameters to protect `optional functions` from this package.
-type parameters interface {
-	ServerParameters | ClientParameters
-}
-
-// Option is the functional option that is applied to the exchange instance
-// to configure parameters.
-type Option[T parameters] func(*T)
-
-// ServerParameters is the set of parameters that must be configured for the exchange.
-type ServerParameters struct {
-	// WriteDeadline sets the timeout for sending messages to the stream
-	WriteDeadline time.Duration
-	// ReadDeadline sets the timeout for reading messages from the stream
-	ReadDeadline time.Duration
-	// RangeRequestTimeout defines a timeout after which the session will try to re-request headers
-	// from another peer.
-	RangeRequestTimeout time.Duration
-	// networkID is a network that will be used to create a protocol.ID
-	// Is empty by default
-	networkID string
-}
-
-// DefaultServerParameters returns the default params to configure the store.
-func DefaultServerParameters() ServerParameters {
-	return ServerParameters{
-		WriteDeadline:       time.Second * 5,
-		ReadDeadline:        time.Minute,
-		RangeRequestTimeout: time.Second * 5,
-	}
-}
-
-func (p *ServerParameters) Validate() error {
-	if p.WriteDeadline == 0 {
-		return fmt.Errorf("invalid write time duration: %v", p.WriteDeadline)
-	}
-	if p.ReadDeadline == 0 {
-		return fmt.Errorf("invalid read time duration: %v", p.ReadDeadline)
-	}
-	if p.RangeRequestTimeout == 0 {
-		return fmt.Errorf("invalid request timeout for session: "+
-			"%s. %s: %v", greaterThenZero, providedSuffix, p.RangeRequestTimeout)
-	}
-	return nil
-}
-
-// WithWriteDeadline is a functional option that configures the
-// `WriteDeadline` parameter.
-func WithWriteDeadline[T ServerParameters](deadline time.Duration) Option[T] {
-	return func(p *T) {
-		switch t := any(p).(type) { //nolint:gocritic
-		case *ServerParameters:
-			t.WriteDeadline = deadline
-		}
-	}
-}
-
-// WithReadDeadline is a functional option that configures the
-// `WithReadDeadline` parameter.
-func WithReadDeadline[T ServerParameters](deadline time.Duration) Option[T] {
-	return func(p *T) {
-		switch t := any(p).(type) { //nolint:gocritic
-		case *ServerParameters:
-			t.ReadDeadline = deadline
-		}
-	}
-}
-
-// WithRangeRequestTimeout is a functional option that configures the
-// `RangeRequestTimeout` parameter.
-func WithRangeRequestTimeout[T parameters](duration time.Duration) Option[T] {
-	return func(p *T) {
-		switch t := any(p).(type) {
-		case *ClientParameters:
-			t.RangeRequestTimeout = duration
-		case *ServerParameters:
-			t.RangeRequestTimeout = duration
-		}
-	}
-}
-
-// WithNetworkID is a functional option that configures the
-// `networkID` parameter.
-func WithNetworkID[T parameters](networkID string) Option[T] {
-	return func(p *T) {
-		switch t := any(p).(type) {
-		case *ClientParameters:
-			t.networkID = networkID
-		case *ServerParameters:
-			t.networkID = networkID
-		}
-	}
-}
-
-// ClientParameters is the set of parameters that must be configured for the exchange.
-type ClientParameters struct {
-	// MaxHeadersPerRangeRequest defines the max amount of headers that can be requested per 1 request.
-	MaxHeadersPerRangeRequest uint64
-	// RangeRequestTimeout defines a timeout after which the session will try to re-request headers
-	// from another peer.
-	RangeRequestTimeout time.Duration
-	// TrustedPeersRequestTimeout a timeout for any request to a trusted peer.
-	TrustedPeersRequestTimeout time.Duration
-	// networkID is a network that will be used to create a protocol.ID
-	networkID string
-	// chainID is an identifier of the chain.
-	chainID string
-}
-
-// DefaultClientParameters returns the default params to configure the store.
-func DefaultClientParameters() ClientParameters {
-	return ClientParameters{
-		MaxHeadersPerRangeRequest:  64,
-		RangeRequestTimeout:        time.Second * 8,
-		TrustedPeersRequestTimeout: time.Millisecond * 300,
-	}
-}
-
-const (
-	greaterThenZero = "should be greater than 0"
-	providedSuffix  = "Provided value"
-)
-
-func (p *ClientParameters) Validate() error {
-	if p.MaxHeadersPerRangeRequest == 0 {
-		return fmt.Errorf("invalid MaxHeadersPerRangeRequest:%s. %s: %v",
-			greaterThenZero, providedSuffix, p.MaxHeadersPerRangeRequest)
-	}
-	if p.RangeRequestTimeout == 0 {
-		return fmt.Errorf("invalid request timeout for session: "+
-			"%s. %s: %v", greaterThenZero, providedSuffix, p.RangeRequestTimeout)
-	}
-	if p.TrustedPeersRequestTimeout == 0 {
-		return fmt.Errorf("invalid TrustedPeersRequestTimeout: "+
-			"%s. %s: %v", greaterThenZero, providedSuffix, p.TrustedPeersRequestTimeout)
-	}
-	return nil
-}
-
-// WithMaxHeadersPerRangeRequest is a functional option that configures the
-// // `MaxRangeRequestSize` parameter.
-func WithMaxHeadersPerRangeRequest[T ClientParameters](amount uint64) Option[T] {
-	return func(p *T) {
-		switch t := any(p).(type) { //nolint:gocritic
-		case *ClientParameters:
-			t.MaxHeadersPerRangeRequest = amount
-		}
-
-	}
-}
-
-// WithChainID is a functional option that configures the
-// `chainID` parameter.
-func WithChainID[T ClientParameters](chainID string) Option[T] {
-	return func(p *T) {
-		switch t := any(p).(type) { //nolint:gocritic
-		case *ClientParameters:
-			t.chainID = chainID
-		}
-	}
-}
diff --git a/libs/header/p2p/pb/header_request.pb.go b/libs/header/p2p/pb/header_request.pb.go
deleted file mode 100644
index e0b7e4f0..00000000
--- a/libs/header/p2p/pb/header_request.pb.go
+++ /dev/null
@@ -1,706 +0,0 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
-// source: libs/header/p2p/pb/header_request.proto
-
-package p2p_pb
-
-import (
-	fmt "fmt"
-	proto "github.com/gogo/protobuf/proto"
-	io "io"
-	math "math"
-	math_bits "math/bits"
-)
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
-
-type StatusCode int32
-
-const (
-	StatusCode_INVALID   StatusCode = 0
-	StatusCode_OK        StatusCode = 1
-	StatusCode_NOT_FOUND StatusCode = 2
-)
-
-var StatusCode_name = map[int32]string{
-	0: "INVALID",
-	1: "OK",
-	2: "NOT_FOUND",
-}
-
-var StatusCode_value = map[string]int32{
-	"INVALID":   0,
-	"OK":        1,
-	"NOT_FOUND": 2,
-}
-
-func (x StatusCode) String() string {
-	return proto.EnumName(StatusCode_name, int32(x))
-}
-
-func (StatusCode) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_43554822dc0b0806, []int{0}
-}
-
-type HeaderRequest struct {
-	// Types that are valid to be assigned to Data:
-	//	*HeaderRequest_Origin
-	//	*HeaderRequest_Hash
-	Data   isHeaderRequest_Data `protobuf_oneof:"data"`
-	Amount uint64               `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"`
-}
-
-func (m *HeaderRequest) Reset()         { *m = HeaderRequest{} }
-func (m *HeaderRequest) String() string { return proto.CompactTextString(m) }
-func (*HeaderRequest) ProtoMessage()    {}
-func (*HeaderRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_43554822dc0b0806, []int{0}
-}
-func (m *HeaderRequest) XXX_Unmarshal(b []byte) error {
-	return m.Unmarshal(b)
-}
-func (m *HeaderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	if deterministic {
-		return xxx_messageInfo_HeaderRequest.Marshal(b, m, deterministic)
-	} else {
-		b = b[:cap(b)]
-		n, err := m.MarshalToSizedBuffer(b)
-		if err != nil {
-			return nil, err
-		}
-		return b[:n], nil
-	}
-}
-func (m *HeaderRequest) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_HeaderRequest.Merge(m, src)
-}
-func (m *HeaderRequest) XXX_Size() int {
-	return m.Size()
-}
-func (m *HeaderRequest) XXX_DiscardUnknown() {
-	xxx_messageInfo_HeaderRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_HeaderRequest proto.InternalMessageInfo
-
-type isHeaderRequest_Data interface {
-	isHeaderRequest_Data()
-	MarshalTo([]byte) (int, error)
-	Size() int
-}
-
-type HeaderRequest_Origin struct {
-	Origin uint64 `protobuf:"varint,1,opt,name=origin,proto3,oneof" json:"origin,omitempty"`
-}
-type HeaderRequest_Hash struct {
-	Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3,oneof" json:"hash,omitempty"`
-}
-
-func (*HeaderRequest_Origin) isHeaderRequest_Data() {}
-func (*HeaderRequest_Hash) isHeaderRequest_Data()   {}
-
-func (m *HeaderRequest) GetData() isHeaderRequest_Data {
-	if m != nil {
-		return m.Data
-	}
-	return nil
-}
-
-func (m *HeaderRequest) GetOrigin() uint64 {
-	if x, ok := m.GetData().(*HeaderRequest_Origin); ok {
-		return x.Origin
-	}
-	return 0
-}
-
-func (m *HeaderRequest) GetHash() []byte {
-	if x, ok := m.GetData().(*HeaderRequest_Hash); ok {
-		return x.Hash
-	}
-	return nil
-}
-
-func (m *HeaderRequest) GetAmount() uint64 {
-	if m != nil {
-		return m.Amount
-	}
-	return 0
-}
-
-// XXX_OneofWrappers is for the internal use of the proto package.
-func (*HeaderRequest) XXX_OneofWrappers() []interface{} {
-	return []interface{}{
-		(*HeaderRequest_Origin)(nil),
-		(*HeaderRequest_Hash)(nil),
-	}
-}
-
-type HeaderResponse struct {
-	Body       []byte     `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
-	StatusCode StatusCode `protobuf:"varint,2,opt,name=statusCode,proto3,enum=p2p.pb.StatusCode" json:"statusCode,omitempty"`
-}
-
-func (m *HeaderResponse) Reset()         { *m = HeaderResponse{} }
-func (m *HeaderResponse) String() string { return proto.CompactTextString(m) }
-func (*HeaderResponse) ProtoMessage()    {}
-func (*HeaderResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_43554822dc0b0806, []int{1}
-}
-func (m *HeaderResponse) XXX_Unmarshal(b []byte) error {
-	return m.Unmarshal(b)
-}
-func (m *HeaderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	if deterministic {
-		return xxx_messageInfo_HeaderResponse.Marshal(b, m, deterministic)
-	} else {
-		b = b[:cap(b)]
-		n, err := m.MarshalToSizedBuffer(b)
-		if err != nil {
-			return nil, err
-		}
-		return b[:n], nil
-	}
-}
-func (m *HeaderResponse) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_HeaderResponse.Merge(m, src)
-}
-func (m *HeaderResponse) XXX_Size() int {
-	return m.Size()
-}
-func (m *HeaderResponse) XXX_DiscardUnknown() {
-	xxx_messageInfo_HeaderResponse.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_HeaderResponse proto.InternalMessageInfo
-
-func (m *HeaderResponse) GetBody() []byte {
-	if m != nil {
-		return m.Body
-	}
-	return nil
-}
-
-func (m *HeaderResponse) GetStatusCode() StatusCode {
-	if m != nil {
-		return m.StatusCode
-	}
-	return StatusCode_INVALID
-}
-
-func init() {
-	proto.RegisterEnum("p2p.pb.StatusCode", StatusCode_name, StatusCode_value)
-	proto.RegisterType((*HeaderRequest)(nil), "p2p.pb.HeaderRequest")
-	proto.RegisterType((*HeaderResponse)(nil), "p2p.pb.HeaderResponse")
-}
-
-func init() {
-	proto.RegisterFile("libs/header/p2p/pb/header_request.proto", fileDescriptor_43554822dc0b0806)
-}
-
-var fileDescriptor_43554822dc0b0806 = []byte{
-	// 271 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0xcd, 0x4a, 0xf3, 0x40,
-	0x14, 0x86, 0x67, 0xfa, 0x85, 0xf9, 0xf0, 0xd8, 0x96, 0x32, 0x88, 0x64, 0x35, 0x94, 0x6e, 0x2c,
-	0x2e, 0x12, 0x89, 0x57, 0x60, 0x2d, 0x92, 0xa2, 0x24, 0x30, 0xfe, 0xe0, 0x2e, 0x4c, 0xc8, 0x60,
-	0x02, 0x9a, 0x19, 0x33, 0x93, 0x85, 0x77, 0xe1, 0x65, 0xb9, 0xec, 0xd2, 0xa5, 0x24, 0x37, 0x22,
-	0x4e, 0xe3, 0xcf, 0xee, 0x3c, 0x3c, 0x2f, 0xe7, 0x3d, 0x1c, 0x38, 0x7a, 0xac, 0x72, 0x13, 0x96,
-	0x52, 0x14, 0xb2, 0x09, 0x75, 0xa4, 0x43, 0x9d, 0x0f, 0x94, 0x35, 0xf2, 0xb9, 0x95, 0xc6, 0x06,
-	0xba, 0x51, 0x56, 0x51, 0xa2, 0x23, 0x1d, 0xe8, 0x7c, 0x91, 0xc1, 0x24, 0x76, 0x9e, 0xef, 0x34,
-	0xf5, 0x81, 0xa8, 0xa6, 0x7a, 0xa8, 0x6a, 0x1f, 0xcf, 0xf1, 0xd2, 0x8b, 0x11, 0x1f, 0x98, 0x1e,
-	0x80, 0x57, 0x0a, 0x53, 0xfa, 0xa3, 0x39, 0x5e, 0x8e, 0x63, 0xc4, 0x1d, 0xd1, 0x43, 0x20, 0xe2,
-	0x49, 0xb5, 0xb5, 0xf5, 0xff, 0x7d, 0xe5, 0xf9, 0x40, 0x2b, 0x02, 0x5e, 0x21, 0xac, 0x58, 0xdc,
-	0xc3, 0xf4, 0xbb, 0xc0, 0x68, 0x55, 0x1b, 0x49, 0x29, 0x78, 0xb9, 0x2a, 0x5e, 0xdc, 0xfe, 0x31,
-	0x77, 0x33, 0x8d, 0x00, 0x8c, 0x15, 0xb6, 0x35, 0xe7, 0xaa, 0x90, 0xae, 0x61, 0x1a, 0xd1, 0x60,
-	0x77, 0x63, 0x70, 0xfd, 0x63, 0xf8, 0x9f, 0xd4, 0xf1, 0x09, 0xc0, 0xaf, 0xa1, 0xfb, 0xf0, 0x7f,
-	0x93, 0xdc, 0x9d, 0x5d, 0x6d, 0xd6, 0x33, 0x44, 0x09, 0x8c, 0xd2, 0xcb, 0x19, 0xa6, 0x13, 0xd8,
-	0x4b, 0xd2, 0x9b, 0xec, 0x22, 0xbd, 0x4d, 0xd6, 0xb3, 0xd1, 0xca, 0x7f, 0xeb, 0x18, 0xde, 0x76,
-	0x0c, 0x7f, 0x74, 0x0c, 0xbf, 0xf6, 0x0c, 0x6d, 0x7b, 0x86, 0xde, 0x7b, 0x86, 0x72, 0xe2, 0xbe,
-	0x72, 0xfa, 0x19, 0x00, 0x00, 0xff, 0xff, 0x04, 0xdd, 0x70, 0x54, 0x40, 0x01, 0x00, 0x00,
-}
-
-func (m *HeaderRequest) Marshal() (dAtA []byte, err error) {
-	size := m.Size()
-	dAtA = make([]byte, size)
-	n, err := m.MarshalToSizedBuffer(dAtA[:size])
-	if err != nil {
-		return nil, err
-	}
-	return dAtA[:n], nil
-}
-
-func (m *HeaderRequest) MarshalTo(dAtA []byte) (int, error) {
-	size := m.Size()
-	return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *HeaderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
-	i := len(dAtA)
-	_ = i
-	var l int
-	_ = l
-	if m.Amount != 0 {
-		i = encodeVarintHeaderRequest(dAtA, i, uint64(m.Amount))
-		i--
-		dAtA[i] = 0x18
-	}
-	if m.Data != nil {
-		{
-			size := m.Data.Size()
-			i -= size
-			if _, err := m.Data.MarshalTo(dAtA[i:]); err != nil {
-				return 0, err
-			}
-		}
-	}
-	return len(dAtA) - i, nil
-}
-
-func (m *HeaderRequest_Origin) MarshalTo(dAtA []byte) (int, error) {
-	size := m.Size()
-	return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *HeaderRequest_Origin) MarshalToSizedBuffer(dAtA []byte) (int, error) {
-	i := len(dAtA)
-	i = encodeVarintHeaderRequest(dAtA, i, uint64(m.Origin))
-	i--
-	dAtA[i] = 0x8
-	return len(dAtA) - i, nil
-}
-func (m *HeaderRequest_Hash) MarshalTo(dAtA []byte) (int, error) {
-	size := m.Size()
-	return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *HeaderRequest_Hash) MarshalToSizedBuffer(dAtA []byte) (int, error) {
-	i := len(dAtA)
-	if m.Hash != nil {
-		i -= len(m.Hash)
-		copy(dAtA[i:], m.Hash)
-		i = encodeVarintHeaderRequest(dAtA, i, uint64(len(m.Hash)))
-		i--
-		dAtA[i] = 0x12
-	}
-	return len(dAtA) - i, nil
-}
-func (m *HeaderResponse) Marshal() (dAtA []byte, err error) {
-	size := m.Size()
-	dAtA = make([]byte, size)
-	n, err := m.MarshalToSizedBuffer(dAtA[:size])
-	if err != nil {
-		return nil, err
-	}
-	return dAtA[:n], nil
-}
-
-func (m *HeaderResponse) MarshalTo(dAtA []byte) (int, error) {
-	size := m.Size()
-	return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *HeaderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
-	i := len(dAtA)
-	_ = i
-	var l int
-	_ = l
-	if m.StatusCode != 0 {
-		i = encodeVarintHeaderRequest(dAtA, i, uint64(m.StatusCode))
-		i--
-		dAtA[i] = 0x10
-	}
-	if len(m.Body) > 0 {
-		i -= len(m.Body)
-		copy(dAtA[i:], m.Body)
-		i = encodeVarintHeaderRequest(dAtA, i, uint64(len(m.Body)))
-		i--
-		dAtA[i] = 0xa
-	}
-	return len(dAtA) - i, nil
-}
-
-func encodeVarintHeaderRequest(dAtA []byte, offset int, v uint64) int {
-	offset -= sovHeaderRequest(v)
-	base := offset
-	for v >= 1<<7 {
-		dAtA[offset] = uint8(v&0x7f | 0x80)
-		v >>= 7
-		offset++
-	}
-	dAtA[offset] = uint8(v)
-	return base
-}
-func (m *HeaderRequest) Size() (n int) {
-	if m == nil {
-		return 0
-	}
-	var l int
-	_ = l
-	if m.Data != nil {
-		n += m.Data.Size()
-	}
-	if m.Amount != 0 {
-		n += 1 + sovHeaderRequest(uint64(m.Amount))
-	}
-	return n
-}
-
-func (m *HeaderRequest_Origin) Size() (n int) {
-	if m == nil {
-		return 0
-	}
-	var l int
-	_ = l
-	n += 1 + sovHeaderRequest(uint64(m.Origin))
-	return n
-}
-func (m *HeaderRequest_Hash) Size() (n int) {
-	if m == nil {
-		return 0
-	}
-	var l int
-	_ = l
-	if m.Hash != nil {
-		l = len(m.Hash)
-		n += 1 + l + sovHeaderRequest(uint64(l))
-	}
-	return n
-}
-func (m *HeaderResponse) Size() (n int) {
-	if m == nil {
-		return 0
-	}
-	var l int
-	_ = l
-	l = len(m.Body)
-	if l > 0 {
-		n += 1 + l + sovHeaderRequest(uint64(l))
-	}
-	if m.StatusCode != 0 {
-		n += 1 + sovHeaderRequest(uint64(m.StatusCode))
-	}
-	return n
-}
-
-func sovHeaderRequest(x uint64) (n int) {
-	return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozHeaderRequest(x uint64) (n int) {
-	return sovHeaderRequest(uint64((x << 1) ^ uint64((int64(x) >> 63))))
-}
-func (m *HeaderRequest) Unmarshal(dAtA []byte) error {
-	l := len(dAtA)
-	iNdEx := 0
-	for iNdEx < l {
-		preIndex := iNdEx
-		var wire uint64
-		for shift := uint(0); ; shift += 7 {
-			if shift >= 64 {
-				return ErrIntOverflowHeaderRequest
-			}
-			if iNdEx >= l {
-				return io.ErrUnexpectedEOF
-			}
-			b := dAtA[iNdEx]
-			iNdEx++
-			wire |= uint64(b&0x7F) << shift
-			if b < 0x80 {
-				break
-			}
-		}
-		fieldNum := int32(wire >> 3)
-		wireType := int(wire & 0x7)
-		if wireType == 4 {
-			return fmt.Errorf("proto: HeaderRequest: wiretype end group for non-group")
-		}
-		if fieldNum <= 0 {
-			return fmt.Errorf("proto: HeaderRequest: illegal tag %d (wire type %d)", fieldNum, wire)
-		}
-		switch fieldNum {
-		case 1:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Origin", wireType)
-			}
-			var v uint64
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowHeaderRequest
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				v |= uint64(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			m.Data = &HeaderRequest_Origin{v}
-		case 2:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
-			}
-			var byteLen int
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowHeaderRequest
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				byteLen |= int(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			if byteLen < 0 {
-				return ErrInvalidLengthHeaderRequest
-			}
-			postIndex := iNdEx + byteLen
-			if postIndex < 0 {
-				return ErrInvalidLengthHeaderRequest
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			v := make([]byte, postIndex-iNdEx)
-			copy(v, dAtA[iNdEx:postIndex])
-			m.Data = &HeaderRequest_Hash{v}
-			iNdEx = postIndex
-		case 3:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
-			}
-			m.Amount = 0
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowHeaderRequest
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				m.Amount |= uint64(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-		default:
-			iNdEx = preIndex
-			skippy, err := skipHeaderRequest(dAtA[iNdEx:])
-			if err != nil {
-				return err
-			}
-			if (skippy < 0) || (iNdEx+skippy) < 0 {
-				return ErrInvalidLengthHeaderRequest
-			}
-			if (iNdEx + skippy) > l {
-				return io.ErrUnexpectedEOF
-			}
-			iNdEx += skippy
-		}
-	}
-
-	if iNdEx > l {
-		return io.ErrUnexpectedEOF
-	}
-	return nil
-}
-func (m *HeaderResponse) Unmarshal(dAtA []byte) error {
-	l := len(dAtA)
-	iNdEx := 0
-	for iNdEx < l {
-		preIndex := iNdEx
-		var wire uint64
-		for shift := uint(0); ; shift += 7 {
-			if shift >= 64 {
-				return ErrIntOverflowHeaderRequest
-			}
-			if iNdEx >= l {
-				return io.ErrUnexpectedEOF
-			}
-			b := dAtA[iNdEx]
-			iNdEx++
-			wire |= uint64(b&0x7F) << shift
-			if b < 0x80 {
-				break
-			}
-		}
-		fieldNum := int32(wire >> 3)
-		wireType := int(wire & 0x7)
-		if wireType == 4 {
-			return fmt.Errorf("proto: HeaderResponse: wiretype end group for non-group")
-		}
-		if fieldNum <= 0 {
-			return fmt.Errorf("proto: HeaderResponse: illegal tag %d (wire type %d)", fieldNum, wire)
-		}
-		switch fieldNum {
-		case 1:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType)
-			}
-			var byteLen int
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowHeaderRequest
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				byteLen |= int(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			if byteLen < 0 {
-				return ErrInvalidLengthHeaderRequest
-			}
-			postIndex := iNdEx + byteLen
-			if postIndex < 0 {
-				return ErrInvalidLengthHeaderRequest
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.Body = append(m.Body[:0], dAtA[iNdEx:postIndex]...)
-			if m.Body == nil {
-				m.Body = []byte{}
-			}
-			iNdEx = postIndex
-		case 2:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field StatusCode", wireType)
-			}
-			m.StatusCode = 0
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowHeaderRequest
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				m.StatusCode |= StatusCode(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-		default:
-			iNdEx = preIndex
-			skippy, err := skipHeaderRequest(dAtA[iNdEx:])
-			if err != nil {
-				return err
-			}
-			if (skippy < 0) || (iNdEx+skippy) < 0 {
-				return ErrInvalidLengthHeaderRequest
-			}
-			if (iNdEx + skippy) > l {
-				return io.ErrUnexpectedEOF
-			}
-			iNdEx += skippy
-		}
-	}
-
-	if iNdEx > l {
-		return io.ErrUnexpectedEOF
-	}
-	return nil
-}
-func skipHeaderRequest(dAtA []byte) (n int, err error) {
-	l := len(dAtA)
-	iNdEx := 0
-	depth := 0
-	for iNdEx < l {
-		var wire uint64
-		for shift := uint(0); ; shift += 7 {
-			if shift >= 64 {
-				return 0, ErrIntOverflowHeaderRequest
-			}
-			if iNdEx >= l {
-				return 0, io.ErrUnexpectedEOF
-			}
-			b := dAtA[iNdEx]
-			iNdEx++
-			wire |= (uint64(b) & 0x7F) << shift
-			if b < 0x80 {
-				break
-			}
-		}
-		wireType := int(wire & 0x7)
-		switch wireType {
-		case 0:
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return 0, ErrIntOverflowHeaderRequest
-				}
-				if iNdEx >= l {
-					return 0, io.ErrUnexpectedEOF
-				}
-				iNdEx++
-				if dAtA[iNdEx-1] < 0x80 {
-					break
-				}
-			}
-		case 1:
-			iNdEx += 8
-		case 2:
-			var length int
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return 0, ErrIntOverflowHeaderRequest
-				}
-				if iNdEx >= l {
-					return 0, io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				length |= (int(b) & 0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			if length < 0 {
-				return 0, ErrInvalidLengthHeaderRequest
-			}
-			iNdEx += length
-		case 3:
-			depth++
-		case 4:
-			if depth == 0 {
-				return 0, ErrUnexpectedEndOfGroupHeaderRequest
-			}
-			depth--
-		case 5:
-			iNdEx += 4
-		default:
-			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
-		}
-		if iNdEx < 0 {
-			return 0, ErrInvalidLengthHeaderRequest
-		}
-		if depth == 0 {
-			return iNdEx, nil
-		}
-	}
-	return 0, io.ErrUnexpectedEOF
-}
-
-var (
-	ErrInvalidLengthHeaderRequest        = fmt.Errorf("proto: negative length found during unmarshaling")
-	ErrIntOverflowHeaderRequest          = fmt.Errorf("proto: integer overflow")
-	ErrUnexpectedEndOfGroupHeaderRequest = fmt.Errorf("proto: unexpected end of group")
-)
diff --git a/libs/header/p2p/pb/header_request.proto b/libs/header/p2p/pb/header_request.proto
deleted file mode 100644
index ee764c7a..00000000
--- a/libs/header/p2p/pb/header_request.proto
+++ /dev/null
@@ -1,22 +0,0 @@
-syntax = "proto3";
-
-package p2p.pb;
-
-message HeaderRequest {
-  oneof data {
-    uint64 origin = 1;
-    bytes hash = 2;
-  }
-  uint64 amount = 3;
-}
-
-enum StatusCode {
-  INVALID = 0;
-  OK = 1;
-  NOT_FOUND = 2;
-};
-
-message HeaderResponse {
-  bytes body = 1;
-  StatusCode statusCode = 2;
-}
diff --git a/libs/header/p2p/peer_stats.go b/libs/header/p2p/peer_stats.go
deleted file mode 100644
index ced05929..00000000
--- a/libs/header/p2p/peer_stats.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package p2p
-
-import (
-	"container/heap"
-	"context"
-	"sync"
-	"time"
-
-	"github.com/libp2p/go-libp2p/core/peer"
-)
-
-// peerStat represents a peer's average statistics.
-type peerStat struct {
-	sync.RWMutex
-	peerID peer.ID
-	// score is the average speed per single request
-	peerScore float32
-	// pruneDeadline specifies when disconnected peer will be removed if
-	// it does not return online.
-	pruneDeadline time.Time
-}
-
-// updateStats recalculates peer.score by averaging the last score
-// updateStats takes the total amount of bytes that were requested from the peer
-// and the total request duration(in milliseconds). The final score is calculated
-// by dividing the amount by time, so the result score will represent how many bytes
-// were retrieved in 1 millisecond. This value will then be averaged relative to the
-// previous peerScore.
-func (p *peerStat) updateStats(amount uint64, time uint64) {
-	p.Lock()
-	defer p.Unlock()
-	averageSpeed := float32(amount)
-	if time != 0 {
-		averageSpeed /= float32(time)
-	}
-	if p.peerScore == 0.0 {
-		p.peerScore = averageSpeed
-		return
-	}
-	p.peerScore = (p.peerScore + averageSpeed) / 2
-}
-
-// decreaseScore decreases peerScore by 20% of the peer that failed the request by any reason.
-// NOTE: decreasing peerScore in one session will not affect its position in queue in another
-// session(as we can have multiple sessions running concurrently).
-// TODO(vgonkivs): to figure out the better scoring increments/decrements
-func (p *peerStat) decreaseScore() {
-	p.Lock()
-	defer p.Unlock()
-
-	p.peerScore -= p.peerScore / 100 * 20
-}
-
-// score reads a peer's latest score from the queue
-func (p *peerStat) score() float32 {
-	p.RLock()
-	defer p.RUnlock()
-	return p.peerScore
-}
-
-// peerStats implements heap.Interface, so we can be sure that we are getting the peer
-// with the highest score, each time we call Pop.
-type peerStats []*peerStat
-
-func newPeerStats() peerStats {
-	ps := make(peerStats, 0)
-	heap.Init(&ps)
-	return ps
-}
-
-func (ps peerStats) Len() int { return len(ps) }
-
-// Less compares two peerScores.
-// Less is used by heap.Interface to build the queue in a decreasing order.
-func (ps peerStats) Less(i, j int) bool {
-	return ps[i].score() > ps[j].score()
-}
-
-func (ps peerStats) Swap(i, j int) {
-	ps[i], ps[j] = ps[j], ps[i]
-}
-
-// Push adds peerStat to the queue.
-func (ps *peerStats) Push(x any) {
-	item := x.(*peerStat)
-	*ps = append(*ps, item)
-}
-
-// Pop returns the peer with the highest score from the queue.
-func (ps *peerStats) Pop() any {
-	old := *ps
-	n := len(old)
-	item := old[n-1]
-	old[n-1] = nil
-	*ps = old[:n-1]
-	return item
-}
-
-// peerQueue wraps peerStats and guards it with the mutex.
-type peerQueue struct {
-	ctx context.Context
-
-	statsLk sync.RWMutex
-	stats   peerStats
-
-	havePeer chan struct{}
-}
-
-func newPeerQueue(ctx context.Context, stats []*peerStat) *peerQueue {
-	statsCh := make(chan struct{}, len(stats))
-	pq := &peerQueue{
-		ctx:      ctx,
-		stats:    newPeerStats(),
-		havePeer: statsCh,
-	}
-	for _, stat := range stats {
-		pq.push(stat)
-	}
-	return pq
-}
-
-// waitPop pops the peer with the biggest score.
-// in case if there are no peer available in current session, it blocks until
-// the peer will be pushed in.
-func (p *peerQueue) waitPop(ctx context.Context) *peerStat {
-	// TODO(vgonkivs): implement fallback solution for cases when peer queue is empty.
-	// As we discussed with @Wondertan there could be 2 possible solutions:
-	// * use libp2p.Discovery to find new peers outside peerTracker to request headers;
-	// * implement IWANT/IHAVE messaging system and start requesting ranges from the Peerstore;
-	select {
-	case <-ctx.Done():
-		return &peerStat{}
-	case <-p.ctx.Done():
-		return &peerStat{}
-	case <-p.havePeer:
-	}
-	p.statsLk.Lock()
-	defer p.statsLk.Unlock()
-	return heap.Pop(&p.stats).(*peerStat)
-}
-
-// push adds the peer to the queue.
-func (p *peerQueue) push(stat *peerStat) {
-	p.statsLk.Lock()
-	heap.Push(&p.stats, stat)
-	p.statsLk.Unlock()
-	// notify that the peer is available in the queue, so it can be popped out
-	p.havePeer <- struct{}{}
-}
diff --git a/libs/header/p2p/peer_tracker.go b/libs/header/p2p/peer_tracker.go
deleted file mode 100644
index 2b70c83a..00000000
--- a/libs/header/p2p/peer_tracker.go
+++ /dev/null
@@ -1,216 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"sync"
-	"time"
-
-	"github.com/libp2p/go-libp2p/core/event"
-	"github.com/libp2p/go-libp2p/core/host"
-	"github.com/libp2p/go-libp2p/core/network"
-	"github.com/libp2p/go-libp2p/core/peer"
-	"github.com/libp2p/go-libp2p/p2p/net/conngater"
-)
-
-const (
-	// defaultScore specifies the score for newly connected peers.
-	defaultScore float32 = 1
-	// maxTrackerSize specifies the max amount of peers that can be added to the peerTracker.
-	maxPeerTrackerSize = 100
-)
-
-var (
-	// maxAwaitingTime specifies the duration that gives to the disconnected peer to be back online,
-	// otherwise it will be removed on the next GC cycle.
-	maxAwaitingTime = time.Hour
-	// gcCycle defines the duration after which the peerTracker starts removing peers.
-	gcCycle = time.Minute * 30
-)
-
-type peerTracker struct {
-	host      host.Host
-	connGater *conngater.BasicConnectionGater
-
-	peerLk sync.RWMutex
-	// trackedPeers contains active peers that we can request to.
-	// we cache the peer once they disconnect,
-	// so we can guarantee that peerQueue will only contain active peers
-	trackedPeers map[peer.ID]*peerStat
-	// disconnectedPeers contains disconnected peers. In case if peer does not return
-	// online until pruneDeadline, it will be removed and its score will be lost.
-	disconnectedPeers map[peer.ID]*peerStat
-
-	ctx    context.Context
-	cancel context.CancelFunc
-	// done is used to gracefully stop the peerTracker.
-	// It allows to wait until track() and gc() will be stopped.
-	done chan struct{}
-}
-
-func newPeerTracker(
-	h host.Host,
-	connGater *conngater.BasicConnectionGater,
-) *peerTracker {
-	ctx, cancel := context.WithCancel(context.Background())
-	return &peerTracker{
-		host:              h,
-		connGater:         connGater,
-		disconnectedPeers: make(map[peer.ID]*peerStat),
-		trackedPeers:      make(map[peer.ID]*peerStat),
-		ctx:               ctx,
-		cancel:            cancel,
-		done:              make(chan struct{}, 2),
-	}
-}
-
-func (p *peerTracker) track() {
-	defer func() {
-		p.done <- struct{}{}
-	}()
-
-	// store peers that have been already connected
-	for _, c := range p.host.Network().Conns() {
-		p.connected(c.RemotePeer())
-	}
-
-	subs, err := p.host.EventBus().Subscribe(&event.EvtPeerConnectednessChanged{})
-	if err != nil {
-		log.Errorw("subscribing to EvtPeerConnectednessChanged", "err", err)
-		return
-	}
-
-	for {
-		select {
-		case <-p.ctx.Done():
-			err = subs.Close()
-			if err != nil {
-				log.Errorw("closing subscription", "err", err)
-			}
-			return
-		case subscription := <-subs.Out():
-			ev := subscription.(event.EvtPeerConnectednessChanged)
-			switch ev.Connectedness {
-			case network.Connected:
-				p.connected(ev.Peer)
-			case network.NotConnected:
-				p.disconnected(ev.Peer)
-			}
-		}
-	}
-}
-
-func (p *peerTracker) connected(pID peer.ID) {
-	if p.host.ID() == pID {
-		return
-	}
-
-	for _, c := range p.host.Network().ConnsToPeer(pID) {
-		// check if connection is short-termed and skip this peer
-		if c.Stat().Transient {
-			return
-		}
-	}
-
-	p.peerLk.Lock()
-	defer p.peerLk.Unlock()
-	// skip adding the peer to avoid overfilling of the peerTracker with unused peers if:
-	// peerTracker reaches the maxTrackerSize and there are more connected peers
-	// than disconnected peers.
-	if len(p.trackedPeers)+len(p.disconnectedPeers) > maxPeerTrackerSize &&
-		len(p.trackedPeers) > len(p.disconnectedPeers) {
-		return
-	}
-
-	// additional check in p.trackedPeers should be done,
-	// because libp2p does not emit multiple Connected events per 1 peer
-	stats, ok := p.disconnectedPeers[pID]
-	if !ok {
-		stats = &peerStat{peerID: pID, peerScore: defaultScore}
-	} else {
-		delete(p.disconnectedPeers, pID)
-	}
-	p.trackedPeers[pID] = stats
-}
-
-func (p *peerTracker) disconnected(pID peer.ID) {
-	p.peerLk.Lock()
-	defer p.peerLk.Unlock()
-	stats, ok := p.trackedPeers[pID]
-	if !ok {
-		return
-	}
-	stats.pruneDeadline = time.Now().Add(maxAwaitingTime)
-	p.disconnectedPeers[pID] = stats
-	delete(p.trackedPeers, pID)
-}
-
-func (p *peerTracker) peers() []*peerStat {
-	p.peerLk.RLock()
-	defer p.peerLk.RUnlock()
-	peers := make([]*peerStat, 0, len(p.trackedPeers))
-	for _, stat := range p.trackedPeers {
-		peers = append(peers, stat)
-	}
-	return peers
-}
-
-// gc goes through connected and disconnected peers once every gcPeriod
-// and removes:
-// * disconnected peers which have been disconnected for more than maxAwaitingTime;
-// * connected peers whose scores are less than or equal than defaultScore;
-func (p *peerTracker) gc() {
-	ticker := time.NewTicker(gcCycle)
-	for {
-		select {
-		case <-p.ctx.Done():
-			p.done <- struct{}{}
-			return
-		case <-ticker.C:
-			p.peerLk.Lock()
-			now := time.Now()
-			for id, peer := range p.disconnectedPeers {
-				if peer.pruneDeadline.Before(now) {
-					delete(p.disconnectedPeers, id)
-				}
-			}
-
-			for id, peer := range p.trackedPeers {
-				if peer.peerScore <= defaultScore {
-					delete(p.trackedPeers, id)
-				}
-			}
-			p.peerLk.Unlock()
-		}
-	}
-}
-
-// stop waits until all background routines will be finished.
-func (p *peerTracker) stop() {
-	p.cancel()
-
-	for i := 0; i < cap(p.done); i++ {
-		<-p.done
-	}
-}
-
-// blockPeer blocks a peer on the networking level and removes it from the local cache.
-func (p *peerTracker) blockPeer(pID peer.ID, reason error) {
-	// add peer to the blacklist, so we can't connect to it in the future.
-	err := p.connGater.BlockPeer(pID)
-	if err != nil {
-		log.Errorw("header/p2p: blocking peer failed", "pID", pID, "err", err)
-	}
-	// close connections to peer.
-	err = p.host.Network().ClosePeer(pID)
-	if err != nil {
-		log.Errorw("header/p2p: closing connection with peer failed", "pID", pID, "err", err)
-	}
-
-	log.Warnw("header/p2p: blocked peer", "pID", pID, "reason", reason)
-
-	p.peerLk.Lock()
-	defer p.peerLk.Unlock()
-	// remove peer from cache.
-	delete(p.trackedPeers, pID)
-	delete(p.disconnectedPeers, pID)
-}
diff --git a/libs/header/p2p/server.go b/libs/header/p2p/server.go
deleted file mode 100644
index 65c772c7..00000000
--- a/libs/header/p2p/server.go
+++ /dev/null
@@ -1,271 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"errors"
-	"time"
-
-	"github.com/libp2p/go-libp2p/core/host"
-	"github.com/libp2p/go-libp2p/core/network"
-	"github.com/libp2p/go-libp2p/core/protocol"
-	"go.opentelemetry.io/otel"
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/codes"
-	"go.opentelemetry.io/otel/trace"
-
-	"github.com/celestiaorg/go-libp2p-messenger/serde"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	p2p_pb "github.com/celestiaorg/celestia-node/libs/header/p2p/pb"
-)
-
-var (
-	tracer = otel.Tracer("header/server")
-)
-
-// ExchangeServer represents the server-side component for
-// responding to inbound header-related requests.
-type ExchangeServer[H header.Header] struct {
-	protocolID protocol.ID
-
-	host  host.Host
-	store header.Store[H]
-
-	ctx    context.Context
-	cancel context.CancelFunc
-
-	Params ServerParameters
-}
-
-// NewExchangeServer returns a new P2P server that handles inbound
-// header-related requests.
-func NewExchangeServer[H header.Header](
-	host host.Host,
-	store header.Store[H],
-	opts ...Option[ServerParameters],
-) (*ExchangeServer[H], error) {
-	params := DefaultServerParameters()
-	for _, opt := range opts {
-		opt(&params)
-	}
-	if err := params.Validate(); err != nil {
-		return nil, err
-	}
-
-	return &ExchangeServer[H]{
-		protocolID: protocolID(params.networkID),
-		host:       host,
-		store:      store,
-		Params:     params,
-	}, nil
-}
-
-// Start sets the stream handler for inbound header-related requests.
-func (serv *ExchangeServer[H]) Start(context.Context) error {
-	serv.ctx, serv.cancel = context.WithCancel(context.Background())
-	log.Infow("server: listening for inbound header requests", "protocol ID", serv.protocolID)
-
-	serv.host.SetStreamHandler(serv.protocolID, serv.requestHandler)
-
-	return nil
-}
-
-// Stop removes the stream handler for serving header-related requests.
-func (serv *ExchangeServer[H]) Stop(context.Context) error {
-	log.Info("server: stopping server")
-	serv.cancel()
-	serv.host.RemoveStreamHandler(serv.protocolID)
-	return nil
-}
-
-// requestHandler handles inbound HeaderRequests.
-func (serv *ExchangeServer[H]) requestHandler(stream network.Stream) {
-	err := stream.SetReadDeadline(time.Now().Add(serv.Params.ReadDeadline))
-	if err != nil {
-		log.Debugf("error setting deadline: %s", err)
-	}
-	// unmarshal request
-	pbreq := new(p2p_pb.HeaderRequest)
-	_, err = serde.Read(stream, pbreq)
-	if err != nil {
-		log.Errorw("server: reading header request from stream", "err", err)
-		stream.Reset() //nolint:errcheck
-		return
-	}
-	if err = stream.CloseRead(); err != nil {
-		log.Error(err)
-	}
-
-	var headers []H
-	// retrieve and write Headers
-	switch pbreq.Data.(type) {
-	case *p2p_pb.HeaderRequest_Hash:
-		headers, err = serv.handleRequestByHash(pbreq.GetHash())
-	case *p2p_pb.HeaderRequest_Origin:
-		headers, err = serv.handleRequest(pbreq.GetOrigin(), pbreq.GetOrigin()+pbreq.Amount)
-	default:
-		log.Error("server: invalid data type received")
-		stream.Reset() //nolint:errcheck
-		return
-	}
-	var code p2p_pb.StatusCode
-	switch err {
-	case nil:
-		code = p2p_pb.StatusCode_OK
-	case header.ErrNotFound:
-		code = p2p_pb.StatusCode_NOT_FOUND
-	default:
-		stream.Reset() //nolint:errcheck
-		return
-	}
-
-	// reallocate headers with 1 nil Header if code is not StatusCode_OK
-	if code != p2p_pb.StatusCode_OK {
-		headers = make([]H, 1)
-	}
-	// write all headers to stream
-	for _, h := range headers {
-		if err := stream.SetWriteDeadline(time.Now().Add(serv.Params.ReadDeadline)); err != nil {
-			log.Debugf("error setting deadline: %s", err)
-		}
-		var bin []byte
-		// if header is not nil, then marshal it to []byte.
-		// if header is nil, then error was received,so we will set empty []byte to proto.
-		if !h.IsZero() {
-			bin, err = h.MarshalBinary()
-			if err != nil {
-				log.Errorw("server: marshaling header to proto", "height", h.Height, "err", err)
-				stream.Reset() //nolint:errcheck
-				return
-			}
-		}
-		_, err = serde.Write(stream, &p2p_pb.HeaderResponse{Body: bin, StatusCode: code})
-		if err != nil {
-			log.Errorw("server: writing header to stream", "err", err)
-			stream.Reset() //nolint:errcheck
-			return
-		}
-	}
-
-	err = stream.Close()
-	if err != nil {
-		log.Errorw("while closing inbound stream", "err", err)
-	}
-}
-
-// handleRequestByHash returns the Header at the given hash
-// if it exists.
-func (serv *ExchangeServer[H]) handleRequestByHash(hash []byte) ([]H, error) {
-	log.Debugw("server: handling header request", "hash", header.Hash(hash).String())
-	ctx, cancel := context.WithTimeout(serv.ctx, serv.Params.RangeRequestTimeout)
-	defer cancel()
-	ctx, span := tracer.Start(ctx, "request-by-hash", trace.WithAttributes(
-		attribute.String("hash", header.Hash(hash).String()),
-	))
-	defer span.End()
-
-	h, err := serv.store.Get(ctx, hash)
-	if err != nil {
-		log.Errorw("server: getting header by hash", "hash", header.Hash(hash).String(), "err", err)
-		span.SetStatus(codes.Error, err.Error())
-		return nil, err
-	}
-
-	span.AddEvent("fetched-header-from-store", trace.WithAttributes(
-		attribute.String("hash", header.Hash(hash).String()),
-		attribute.Int64("height", h.Height())),
-	)
-	span.SetStatus(codes.Ok, "")
-	return []H{h}, nil
-}
-
-// handleRequest fetches the Header at the given origin and
-// writes it to the stream.
-func (serv *ExchangeServer[H]) handleRequest(from, to uint64) ([]H, error) {
-	if from == uint64(0) {
-		return serv.handleHeadRequest()
-	}
-
-	ctx, cancel := context.WithTimeout(serv.ctx, serv.Params.RangeRequestTimeout)
-	defer cancel()
-
-	ctx, span := tracer.Start(ctx, "request-range", trace.WithAttributes(
-		attribute.Int64("from", int64(from)),
-		attribute.Int64("to", int64(to))))
-	defer span.End()
-
-	if to-from > header.MaxRangeRequestSize {
-		log.Errorw("server: skip request for too many headers.", "amount", to-from)
-		span.SetStatus(codes.Error, header.ErrHeadersLimitExceeded.Error())
-		return nil, header.ErrHeadersLimitExceeded
-	}
-
-	log.Debugw("server: handling headers request", "from", from, "to", to)
-	// check that store has the requested height
-	if !serv.store.HasAt(ctx, to-1) {
-		head, err := serv.store.Head(ctx)
-		if err != nil {
-			span.SetStatus(codes.Error, err.Error())
-			log.Debugw("server: could not get current head", "err", err)
-			return nil, err
-		}
-
-		// might be a case when store hasn't synced yet to the requested range
-		if uint64(head.Height()) < from {
-			span.SetStatus(codes.Error, header.ErrNotFound.Error())
-			log.Debugw("server: requested headers not stored",
-				"from", from,
-				"to", to,
-				"currentHead",
-				head.Height(),
-			)
-			return nil, header.ErrNotFound
-		}
-
-		log.Debugw("server: serving partial range",
-			"prevMaxHeight", to,
-			"newMaxHeight", uint64(head.Height())+1,
-		)
-		// change `to` height to return a partial range
-		to = uint64(head.Height()) + 1
-	}
-
-	headersByRange, err := serv.store.GetRangeByHeight(ctx, from, to)
-	if err != nil {
-		span.SetStatus(codes.Error, err.Error())
-		if errors.Is(err, context.DeadlineExceeded) {
-			log.Warnw("server: requested headers not found", "from", from, "to", to)
-			return nil, header.ErrNotFound
-		}
-		log.Errorw("server: getting headers", "from", from, "to", to, "err", err)
-		return nil, err
-	}
-
-	span.AddEvent("fetched-range-of-headers", trace.WithAttributes(
-		attribute.Int("amount", len(headersByRange))))
-	span.SetStatus(codes.Ok, "")
-	return headersByRange, nil
-}
-
-// handleHeadRequest returns the latest stored head.
-func (serv *ExchangeServer[H]) handleHeadRequest() ([]H, error) {
-	log.Debug("server: handling head request")
-	ctx, cancel := context.WithTimeout(serv.ctx, serv.Params.RangeRequestTimeout)
-	defer cancel()
-	ctx, span := tracer.Start(ctx, "request-head")
-	defer span.End()
-
-	head, err := serv.store.Head(ctx)
-	if err != nil {
-		log.Errorw("server: getting head", "err", err)
-		span.SetStatus(codes.Error, err.Error())
-		return nil, err
-	}
-
-	span.AddEvent("fetched-head", trace.WithAttributes(
-		attribute.String("hash", head.Hash().String()),
-		attribute.Int64("height", head.Height())),
-	)
-	span.SetStatus(codes.Ok, "")
-	return []H{head}, nil
-}
diff --git a/libs/header/p2p/session.go b/libs/header/p2p/session.go
deleted file mode 100644
index d344508e..00000000
--- a/libs/header/p2p/session.go
+++ /dev/null
@@ -1,306 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"sort"
-	"time"
-
-	"github.com/libp2p/go-libp2p/core/host"
-	"github.com/libp2p/go-libp2p/core/protocol"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	p2p_pb "github.com/celestiaorg/celestia-node/libs/header/p2p/pb"
-)
-
-// errEmptyResponse means that server side closes the connection without sending at least 1
-// response.
-var errEmptyResponse = errors.New("empty response")
-
-type option[H header.Header] func(*session[H])
-
-func withValidation[H header.Header](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 {
-	host       host.Host
-	protocolID protocol.ID
-	queue      *peerQueue
-	// peerTracker contains discovered peers with records that describes their activity.
-	peerTracker *peerTracker
-
-	// `from` is set when additional validation for range is needed.
-	// Otherwise, it will be nil.
-	from           H
-	requestTimeout time.Duration
-
-	ctx    context.Context
-	cancel context.CancelFunc
-	reqCh  chan *p2p_pb.HeaderRequest
-}
-
-func newSession[H header.Header](
-	ctx context.Context,
-	h host.Host,
-	peerTracker *peerTracker,
-	protocolID protocol.ID,
-	requestTimeout time.Duration,
-	options ...option[H],
-) *session[H] {
-	ctx, cancel := context.WithCancel(ctx)
-	ses := &session[H]{
-		ctx:            ctx,
-		cancel:         cancel,
-		protocolID:     protocolID,
-		host:           h,
-		queue:          newPeerQueue(ctx, peerTracker.peers()),
-		peerTracker:    peerTracker,
-		requestTimeout: requestTimeout,
-	}
-
-	for _, opt := range options {
-		opt(ses)
-	}
-	return ses
-}
-
-// getRangeByHeight requests headers from different peers.
-func (s *session[H]) getRangeByHeight(
-	ctx context.Context,
-	from, amount, headersPerPeer uint64,
-) ([]H, error) {
-	log.Debugw("requesting headers", "from", from, "to", from+amount-1) // -1 need to exclude to+1 height
-
-	requests := prepareRequests(from, amount, headersPerPeer)
-	result := make(chan []H, len(requests))
-	s.reqCh = make(chan *p2p_pb.HeaderRequest, len(requests))
-
-	go s.handleOutgoingRequests(ctx, result)
-	for _, req := range requests {
-		s.reqCh <- req
-	}
-
-	headers := make([]H, 0, amount)
-LOOP:
-	for {
-		select {
-		case <-s.ctx.Done():
-			return nil, errors.New("header/p2p: exchange is closed")
-		case <-ctx.Done():
-			return nil, ctx.Err()
-		case res := <-result:
-			headers = append(headers, res...)
-			if uint64(len(headers)) == amount {
-				break LOOP
-			}
-		}
-	}
-
-	sort.Slice(headers, func(i, j int) bool {
-		return headers[i].Height() < headers[j].Height()
-	})
-
-	log.Debugw("received headers range",
-		"from", headers[0].Height(),
-		"to", headers[len(headers)-1].Height(),
-	)
-	return headers, nil
-}
-
-// close stops the session.
-func (s *session[H]) close() {
-	if s.cancel != nil {
-		s.cancel()
-		s.cancel = nil
-	}
-}
-
-// handleOutgoingRequests pops a peer from the queue and sends a prepared request to the peer.
-// Will exit via canceled session context or when all request are processed.
-func (s *session[H]) handleOutgoingRequests(ctx context.Context, result chan []H) {
-	for {
-		select {
-		case <-ctx.Done():
-			return
-		case <-s.ctx.Done():
-			return
-		case req := <-s.reqCh:
-			// select peer with the highest score among the available ones for the request
-			stats := s.queue.waitPop(ctx)
-			if stats.peerID == "" {
-				return
-			}
-			go s.doRequest(ctx, stats, req, result)
-		}
-	}
-}
-
-// doRequest chooses the best peer to fetch headers and sends a request in range of available
-// maxRetryAttempts.
-func (s *session[H]) doRequest(
-	ctx context.Context,
-	stat *peerStat,
-	req *p2p_pb.HeaderRequest,
-	headers chan []H,
-) {
-	ctx, cancel := context.WithTimeout(ctx, s.requestTimeout)
-	defer cancel()
-
-	r, size, duration, err := sendMessage(ctx, s.host, stat.peerID, s.protocolID, req)
-	if err != nil {
-		// we should not punish peer at this point and should try to parse responses, despite that error
-		// was received.
-		log.Debugw("requesting headers from peer failed", "peer", stat.peerID, "err", err)
-	}
-
-	h, err := s.processResponse(r)
-	if err != nil {
-		logFn := log.Errorw
-
-		switch err {
-		case header.ErrNotFound:
-			logFn = log.Debugw
-			fallthrough
-		case errEmptyResponse:
-			stat.decreaseScore()
-		default:
-			s.peerTracker.blockPeer(stat.peerID, err)
-		}
-
-		select {
-		case <-s.ctx.Done():
-			return
-		case s.reqCh <- req:
-		}
-		logFn("processing response",
-			"from", req.GetOrigin(),
-			"to", req.Amount+req.GetOrigin()-1,
-			"err", err,
-			"peer", stat.peerID,
-		)
-		return
-	}
-
-	log.Debugw("request headers from peer succeeded",
-		"peer", stat.peerID,
-		"receivedAmount", len(h),
-		"requestedAmount", req.Amount,
-	)
-
-	// update peer stats
-	stat.updateStats(size, duration)
-
-	responseLn := uint64(len(h))
-	// ensure that we received the correct amount of headers.
-	if responseLn < req.Amount {
-		from := uint64(h[responseLn-1].Height())
-		amount := req.Amount - responseLn
-
-		select {
-		case <-s.ctx.Done():
-			return
-		// create a new request with the remaining headers.
-		// prepareRequests will return a slice with 1 element at this point
-		case s.reqCh <- prepareRequests(from+1, amount, req.Amount)[0]:
-			log.Debugw("sending additional request to get remaining headers")
-		}
-	}
-
-	// send headers to the channel, return peer to the queue, so it can be
-	// re-used in case if there are other requests awaiting
-	headers <- h
-	s.queue.push(stat)
-}
-
-// processResponse converts HeaderResponse to Header.
-func (s *session[H]) processResponse(responses []*p2p_pb.HeaderResponse) ([]H, error) {
-	if len(responses) == 0 {
-		return nil, errEmptyResponse
-	}
-
-	headers := make([]H, 0)
-	for _, resp := range responses {
-		err := convertStatusCodeToError(resp.StatusCode)
-		if err != nil {
-			return nil, err
-		}
-
-		var empty H
-		header := empty.New()
-		err = header.UnmarshalBinary(resp.Body)
-		if err != nil {
-			return nil, err
-		}
-		headers = append(headers, header.(H))
-	}
-
-	if len(headers) == 0 {
-		return nil, header.ErrNotFound
-	}
-
-	err := s.validate(headers)
-	return headers, err
-}
-
-// validate checks that the received range of headers is adjacent and is valid against the provided
-// header.
-func (s *session[H]) validate(headers []H) error {
-	// if `s.from` is empty, then additional validation for the header`s range is not needed.
-	if s.from.IsZero() {
-		return nil
-	}
-
-	trusted := s.from
-	// verify that the whole range is valid and adjacent.
-	for _, untrusted := range headers {
-		err := trusted.Verify(untrusted)
-		if err != nil {
-			return err
-		}
-
-		// extra check for the adjacency should be performed only for the received range,
-		// because headers are received out of order and `s.from` can't be adjacent to them
-		if trusted.Height() != s.from.Height() {
-			if trusted.Height()+1 != untrusted.Height() {
-				// Exchange requires requested ranges to always consist of adjacent headers
-				return fmt.Errorf("peer sent valid but non-adjacent header. expected:%d, received:%d",
-					trusted.Height()+1,
-					untrusted.Height(),
-				)
-			}
-		}
-		// as `untrusted` was verified against previous trusted header, we can assume that it is valid
-		trusted = untrusted
-	}
-	return nil
-}
-
-// prepareRequests converts incoming range into separate HeaderRequest.
-func prepareRequests(from, amount, headersPerPeer uint64) []*p2p_pb.HeaderRequest {
-	requests := make([]*p2p_pb.HeaderRequest, 0, amount/headersPerPeer)
-	for amount > uint64(0) {
-		var requestSize uint64
-		request := &p2p_pb.HeaderRequest{
-			Data: &p2p_pb.HeaderRequest_Origin{Origin: from},
-		}
-
-		if amount < headersPerPeer {
-			requestSize = amount
-			amount = 0
-		} else {
-			amount -= headersPerPeer
-			from += headersPerPeer
-			requestSize = headersPerPeer
-		}
-
-		request.Amount = requestSize
-		requests = append(requests, request)
-	}
-	return requests
-}
diff --git a/libs/header/p2p/subscriber.go b/libs/header/p2p/subscriber.go
deleted file mode 100644
index 1917df94..00000000
--- a/libs/header/p2p/subscriber.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"fmt"
-
-	pubsub "github.com/libp2p/go-libp2p-pubsub"
-	"github.com/libp2p/go-libp2p/core/peer"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// Subscriber manages the lifecycle and relationship of header Module
-// with the "header-sub" gossipsub topic.
-type Subscriber[H header.Header] struct {
-	pubsubTopicID string
-
-	pubsub *pubsub.PubSub
-	topic  *pubsub.Topic
-	msgID  pubsub.MsgIdFunction
-}
-
-// NewSubscriber returns a Subscriber that manages the header Module's
-// relationship with the "header-sub" gossipsub topic.
-func NewSubscriber[H header.Header](
-	ps *pubsub.PubSub,
-	msgID pubsub.MsgIdFunction,
-	networkID string,
-) *Subscriber[H] {
-	return &Subscriber[H]{
-		pubsubTopicID: PubsubTopicID(networkID),
-		pubsub:        ps,
-		msgID:         msgID,
-	}
-}
-
-// Start starts the Subscriber, registering a topic validator for the "header-sub"
-// topic and joining it.
-func (p *Subscriber[H]) Start(context.Context) (err error) {
-	log.Infow("joining topic", "topic ID", p.pubsubTopicID)
-	p.topic, err = p.pubsub.Join(p.pubsubTopicID, pubsub.WithTopicMessageIdFn(p.msgID))
-	return err
-}
-
-// Stop closes the topic and unregisters its validator.
-func (p *Subscriber[H]) Stop(context.Context) error {
-	err := p.pubsub.UnregisterTopicValidator(p.pubsubTopicID)
-	if err != nil {
-		log.Warnf("unregistering validator: %s", err)
-	}
-
-	return p.topic.Close()
-}
-
-// AddValidator applies basic pubsub validator for the topic.
-func (p *Subscriber[H]) AddValidator(val func(context.Context, H) pubsub.ValidationResult) error {
-	pval := func(ctx context.Context, p peer.ID, msg *pubsub.Message) pubsub.ValidationResult {
-		var empty H
-		maybeHead := empty.New()
-		err := maybeHead.UnmarshalBinary(msg.Data)
-		if err != nil {
-			log.Errorw("unmarshalling header",
-				"from", p.ShortString(),
-				"err", err)
-			return pubsub.ValidationReject
-		}
-		msg.ValidatorData = maybeHead
-		return val(ctx, maybeHead.(H))
-	}
-	return p.pubsub.RegisterTopicValidator(p.pubsubTopicID, pval)
-}
-
-// Subscribe returns a new subscription to the Subscriber's
-// topic.
-func (p *Subscriber[H]) Subscribe() (header.Subscription[H], error) {
-	if p.topic == nil {
-		return nil, fmt.Errorf("header topic is not instantiated, service must be started before subscribing")
-	}
-
-	return newSubscription[H](p.topic)
-}
-
-// Broadcast broadcasts the given Header to the topic.
-func (p *Subscriber[H]) Broadcast(ctx context.Context, header H, opts ...pubsub.PubOpt) error {
-	bin, err := header.MarshalBinary()
-	if err != nil {
-		return err
-	}
-	return p.topic.Publish(ctx, bin, opts...)
-}
diff --git a/libs/header/p2p/subscription.go b/libs/header/p2p/subscription.go
deleted file mode 100644
index 0986434f..00000000
--- a/libs/header/p2p/subscription.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"fmt"
-	"reflect"
-
-	pubsub "github.com/libp2p/go-libp2p-pubsub"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// subscription handles retrieving Headers from the header pubsub topic.
-type subscription[H header.Header] 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) {
-	sub, err := topic.Subscribe()
-	if err != nil {
-		return nil, err
-	}
-
-	return &subscription[H]{
-		topic:        topic,
-		subscription: sub,
-	}, nil
-}
-
-// NextHeader returns the next (latest) verified Header from the network.
-func (s *subscription[H]) NextHeader(ctx context.Context) (H, error) {
-	msg, err := s.subscription.Next(ctx)
-	if err != nil {
-		var zero H
-		return zero, err
-	}
-	log.Debugw("received message", "topic", msg.Message.GetTopic(), "sender", msg.ReceivedFrom)
-
-	header, ok := msg.ValidatorData.(H)
-	if !ok {
-		panic(fmt.Sprintf("invalid type received %s", reflect.TypeOf(msg.ValidatorData)))
-	}
-
-	log.Debugw("received new Header", "height", header.Height(), "hash", header.Hash())
-	return header, nil
-}
-
-// Cancel cancels the subscription to new Headers from the network.
-func (s *subscription[H]) Cancel() {
-	s.subscription.Cancel()
-}
diff --git a/libs/header/store/batch.go b/libs/header/store/batch.go
deleted file mode 100644
index 89518be6..00000000
--- a/libs/header/store/batch.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package store
-
-import (
-	"sync"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// batch keeps an adjacent range of headers and loosely mimics the Store
-// interface. NOTE: Can fully implement Store for a use case.
-//
-// It keeps a mapping 'height -> header' and 'hash -> height'
-// 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 {
-	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] {
-	return &batch[H]{
-		heights: make(map[string]uint64, size),
-		headers: make([]H, 0, size),
-	}
-}
-
-// Len gives current length of the batch.
-func (b *batch[H]) Len() int {
-	b.lk.RLock()
-	defer b.lk.RUnlock()
-	return len(b.headers)
-}
-
-// GetAll returns a slice of all the headers in the batch.
-func (b *batch[H]) GetAll() []H {
-	b.lk.RLock()
-	defer b.lk.RUnlock()
-	return b.headers
-}
-
-// Get returns a header by its hash.
-func (b *batch[H]) Get(hash header.Hash) H {
-	b.lk.RLock()
-	defer b.lk.RUnlock()
-	height, ok := b.heights[hash.String()]
-	if !ok {
-		var zero H
-		return zero
-	}
-
-	return b.getByHeight(height)
-}
-
-// GetByHeight returns a header by its height.
-func (b *batch[H]) GetByHeight(height uint64) H {
-	b.lk.RLock()
-	defer b.lk.RUnlock()
-	return b.getByHeight(height)
-}
-
-func (b *batch[H]) getByHeight(height uint64) H {
-	var (
-		ln   = uint64(len(b.headers))
-		zero H
-	)
-	if ln == 0 {
-		return zero
-	}
-
-	head := uint64(b.headers[ln-1].Height())
-	base := head - ln
-	if height > head || height <= base {
-		return zero
-	}
-
-	return b.headers[height-base-1]
-}
-
-// Append appends new headers to the batch.
-func (b *batch[H]) Append(headers ...H) {
-	b.lk.Lock()
-	defer b.lk.Unlock()
-	for _, h := range headers {
-		b.headers = append(b.headers, h)
-		b.heights[h.Hash().String()] = uint64(h.Height())
-	}
-}
-
-// Has checks whether header by the hash is present in the batch.
-func (b *batch[H]) Has(hash header.Hash) bool {
-	b.lk.RLock()
-	defer b.lk.RUnlock()
-	_, ok := b.heights[hash.String()]
-	return ok
-}
-
-// Reset cleans references to batched headers.
-func (b *batch[H]) Reset() {
-	b.lk.Lock()
-	defer b.lk.Unlock()
-	b.headers = b.headers[:0]
-	for k := range b.heights {
-		delete(b.heights, k)
-	}
-}
diff --git a/libs/header/store/height_indexer.go b/libs/header/store/height_indexer.go
deleted file mode 100644
index fd0b1cb4..00000000
--- a/libs/header/store/height_indexer.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package store
-
-import (
-	"context"
-
-	lru "github.com/hashicorp/golang-lru"
-	"github.com/ipfs/go-datastore"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// 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 {
-	ds    datastore.Batching
-	cache *lru.ARCCache
-}
-
-// newHeightIndexer creates new heightIndexer.
-func newHeightIndexer[H header.Header](ds datastore.Batching, indexCacheSize int) (*heightIndexer[H], error) {
-	cache, err := lru.NewARC(indexCacheSize)
-	if err != nil {
-		return nil, err
-	}
-
-	return &heightIndexer[H]{
-		ds:    ds,
-		cache: cache,
-	}, nil
-}
-
-// HashByHeight loads a header hash corresponding to the given height.
-func (hi *heightIndexer[H]) HashByHeight(ctx context.Context, h uint64) (header.Hash, error) {
-	if v, ok := hi.cache.Get(h); ok {
-		return v.(header.Hash), nil
-	}
-
-	val, err := hi.ds.Get(ctx, heightKey(h))
-	if err != nil {
-		return nil, err
-	}
-
-	hi.cache.Add(h, header.Hash(val))
-	return val, nil
-}
-
-// IndexTo saves mapping between header Height and Hash to the given batch.
-func (hi *heightIndexer[H]) IndexTo(ctx context.Context, batch datastore.Batch, headers ...H) error {
-	for _, h := range headers {
-		err := batch.Put(ctx, heightKey(uint64(h.Height())), h.Hash())
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
diff --git a/libs/header/store/heightsub.go b/libs/header/store/heightsub.go
deleted file mode 100644
index 55cb63f5..00000000
--- a/libs/header/store/heightsub.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package store
-
-import (
-	"context"
-	"errors"
-	"sync"
-	"sync/atomic"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// errElapsedHeight is thrown when a requested height was already provided to heightSub.
-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 {
-	// height refers to the latest locally available header height
-	// that has been fully verified and inserted into the subjective chain
-	height       uint64 // atomic
-	heightReqsLk sync.Mutex
-	heightReqs   map[uint64][]chan H
-}
-
-// newHeightSub instantiates new heightSub.
-func newHeightSub[H header.Header]() *heightSub[H] {
-	return &heightSub[H]{
-		heightReqs: make(map[uint64][]chan H),
-	}
-}
-
-// Height reports current height.
-func (hs *heightSub[H]) Height() uint64 {
-	return atomic.LoadUint64(&hs.height)
-}
-
-// SetHeight sets the new head height for heightSub.
-func (hs *heightSub[H]) SetHeight(height uint64) {
-	atomic.StoreUint64(&hs.height, height)
-}
-
-// Sub subscribes for a header of a given height.
-// It can return errElapsedHeight, which means a requested header was already provided
-// and caller should get it elsewhere.
-func (hs *heightSub[H]) Sub(ctx context.Context, height uint64) (H, error) {
-	var zero H
-	if hs.Height() >= height {
-		return zero, errElapsedHeight
-	}
-
-	hs.heightReqsLk.Lock()
-	if hs.Height() >= height {
-		// This is a rare case we have to account for.
-		// The lock above can park a goroutine long enough for hs.height to change for a requested height,
-		// leaving the request never fulfilled and the goroutine deadlocked.
-		hs.heightReqsLk.Unlock()
-		return zero, errElapsedHeight
-	}
-	resp := make(chan H, 1)
-	hs.heightReqs[height] = append(hs.heightReqs[height], resp)
-	hs.heightReqsLk.Unlock()
-
-	select {
-	case resp := <-resp:
-		return resp, nil
-	case <-ctx.Done():
-		// no need to keep the request, if the op is canceled
-		hs.heightReqsLk.Lock()
-		delete(hs.heightReqs, height)
-		hs.heightReqsLk.Unlock()
-		return zero, ctx.Err()
-	}
-}
-
-// Pub processes all the outstanding subscriptions matching the given headers.
-// Pub is only safe when called from one goroutine.
-// For Pub to work correctly, heightSub has to be initialized with SetHeight
-// so that given headers are contiguous to the height on heightSub.
-func (hs *heightSub[H]) Pub(headers ...H) {
-	ln := len(headers)
-	if ln == 0 {
-		return
-	}
-
-	height := hs.Height()
-	from, to := uint64(headers[0].Height()), uint64(headers[ln-1].Height())
-	if height+1 != from {
-		log.Fatal("PLEASE FILE A BUG REPORT: headers given to the heightSub are in the wrong order")
-		return
-	}
-	hs.SetHeight(to)
-
-	hs.heightReqsLk.Lock()
-	defer hs.heightReqsLk.Unlock()
-
-	// there is a common case where we Pub only header
-	// in this case, we shouldn't loop over each heightReqs
-	// and instead read from the map directly
-	if ln == 1 {
-		reqs, ok := hs.heightReqs[from]
-		if ok {
-			for _, req := range reqs {
-				req <- headers[0] // reqs must always be buffered, so this won't block
-			}
-			delete(hs.heightReqs, from)
-		}
-		return
-	}
-
-	// instead of looping over each header in 'headers', we can loop over each request
-	// which will drastically decrease idle iterations, as there will be less requests than headers
-	for height, reqs := range hs.heightReqs {
-		// then we look if any of the requests match the given range of headers
-		if height >= from && height <= to {
-			// and if so, calculate its position and fulfill requests
-			h := headers[height-from]
-			for _, req := range reqs {
-				req <- h // reqs must always be buffered, so this won't block
-			}
-			delete(hs.heightReqs, height)
-		}
-	}
-}
diff --git a/libs/header/store/init.go b/libs/header/store/init.go
deleted file mode 100644
index b44eee1c..00000000
--- a/libs/header/store/init.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package store
-
-import (
-	"context"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// 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 {
-	_, err := store.Head(ctx)
-	switch err {
-	default:
-		return err
-	case header.ErrNoHead:
-		initial, err := ex.Get(ctx, hash)
-		if err != nil {
-			return err
-		}
-
-		return store.Init(ctx, initial)
-	}
-}
diff --git a/libs/header/store/keys.go b/libs/header/store/keys.go
deleted file mode 100644
index 087be71a..00000000
--- a/libs/header/store/keys.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package store
-
-import (
-	"strconv"
-
-	"github.com/ipfs/go-datastore"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-var (
-	storePrefix = datastore.NewKey("headers")
-	headKey     = datastore.NewKey("head")
-)
-
-func heightKey(h uint64) datastore.Key {
-	return datastore.NewKey(strconv.Itoa(int(h)))
-}
-
-func headerKey(h header.Header) datastore.Key {
-	return datastore.NewKey(h.Hash().String())
-}
diff --git a/libs/header/store/options.go b/libs/header/store/options.go
deleted file mode 100644
index 0f968f2f..00000000
--- a/libs/header/store/options.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package store
-
-import (
-	"fmt"
-)
-
-// Option is the functional option that is applied to the store instance
-// to configure store parameters.
-type Option func(*Parameters)
-
-// Parameters is the set of parameters that must be configured for the store.
-type Parameters struct {
-	// StoreCacheSize defines the maximum amount of entries in the Header Store cache.
-	StoreCacheSize int
-
-	// IndexCacheSize defines the maximum amount of entries in the Height to Hash index cache.
-	IndexCacheSize int
-
-	// WriteBatchSize defines the size of the batched header write.
-	// Headers are written in batches not to thrash the underlying Datastore with writes.
-	WriteBatchSize int
-}
-
-// DefaultParameters returns the default params to configure the store.
-func DefaultParameters() Parameters {
-	return Parameters{
-		StoreCacheSize: 4096,
-		IndexCacheSize: 16384,
-		WriteBatchSize: 2048,
-	}
-}
-
-const errSuffix = "value should be positive and non-zero"
-
-func (p *Parameters) Validate() error {
-	if p.StoreCacheSize <= 0 {
-		return fmt.Errorf("invalid store cache size:%s", errSuffix)
-	}
-	if p.IndexCacheSize <= 0 {
-		return fmt.Errorf("invalid indexer cache size:%s", errSuffix)
-	}
-	if p.WriteBatchSize <= 0 {
-		return fmt.Errorf("invalid batch size:%s", errSuffix)
-	}
-	return nil
-}
-
-// WithStoreCacheSize is a functional option that configures the
-// `StoreCacheSize` parameter.
-func WithStoreCacheSize(size int) Option {
-	return func(p *Parameters) {
-		p.StoreCacheSize = size
-	}
-}
-
-// WithIndexCacheSize is a functional option that configures the
-// `IndexCacheSize` parameter.
-func WithIndexCacheSize(size int) Option {
-	return func(p *Parameters) {
-		p.IndexCacheSize = size
-	}
-}
-
-// WithWriteBatchSize is a functional option that configures the
-// `WriteBatchSize` parameter.
-func WithWriteBatchSize(size int) Option {
-	return func(p *Parameters) {
-		p.WriteBatchSize = size
-	}
-}
diff --git a/libs/header/store/store.go b/libs/header/store/store.go
deleted file mode 100644
index 4c64c872..00000000
--- a/libs/header/store/store.go
+++ /dev/null
@@ -1,458 +0,0 @@
-package store
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"sync/atomic"
-
-	lru "github.com/hashicorp/golang-lru"
-	"github.com/ipfs/go-datastore"
-	"github.com/ipfs/go-datastore/namespace"
-	logging "github.com/ipfs/go-log/v2"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-var log = logging.Logger("header/store")
-
-var (
-	// errStoppedStore is returned for attempted operations on a stopped store
-	errStoppedStore = errors.New("stopped store")
-)
-
-// Store implements the Store interface for Headers over Datastore.
-type Store[H header.Header] struct {
-	// header storing
-	//
-	// underlying KV store
-	ds datastore.Batching
-	// adaptive replacement cache of headers
-	cache *lru.ARCCache
-
-	// header heights management
-	//
-	// maps heights to hashes
-	heightIndex *heightIndexer[H]
-	// manages current store read head height (1) and
-	// allows callers to wait until header for a height is stored (2)
-	heightSub *heightSub[H]
-
-	// writing to datastore
-	//
-	// queue of headers to be written
-	writes chan []H
-	// signals when writes are finished
-	writesDn chan struct{}
-	// writeHead maintains the current write head
-	writeHead atomic.Pointer[H]
-	// pending keeps headers pending to be written in one batch
-	pending *batch[H]
-
-	Params Parameters
-}
-
-// 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) {
-	return newStore[H](ds, opts...)
-}
-
-// NewStoreWithHead initiates a new Store and forcefully sets a given trusted header as head.
-func NewStoreWithHead[H header.Header](
-	ctx context.Context,
-	ds datastore.Batching,
-	head H,
-	opts ...Option,
-) (*Store[H], error) {
-	store, err := newStore[H](ds, opts...)
-	if err != nil {
-		return nil, err
-	}
-
-	return store, store.Init(ctx, head)
-}
-
-func newStore[H header.Header](ds datastore.Batching, opts ...Option) (*Store[H], error) {
-	params := DefaultParameters()
-	for _, opt := range opts {
-		opt(&params)
-	}
-
-	if err := params.Validate(); err != nil {
-		return nil, fmt.Errorf("header/store: store creation failed: %w", err)
-	}
-
-	cache, err := lru.NewARC(params.StoreCacheSize)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create index cache: %w", err)
-	}
-
-	wrappedStore := namespace.Wrap(ds, storePrefix)
-	index, err := newHeightIndexer[H](wrappedStore, params.IndexCacheSize)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create height indexer: %w", err)
-	}
-
-	return &Store[H]{
-		Params:      params,
-		ds:          wrappedStore,
-		heightSub:   newHeightSub[H](),
-		writes:      make(chan []H, 16),
-		writesDn:    make(chan struct{}),
-		cache:       cache,
-		heightIndex: index,
-		pending:     newBatch[H](params.WriteBatchSize),
-	}, nil
-}
-
-func (s *Store[H]) Init(ctx context.Context, initial H) error {
-	// trust the given header as the initial head
-	err := s.flush(ctx, initial)
-	if err != nil {
-		return err
-	}
-
-	log.Infow("initialized head", "height", initial.Height(), "hash", initial.Hash())
-	return nil
-}
-
-func (s *Store[H]) Start(context.Context) error {
-	go s.flushLoop()
-	return nil
-}
-
-func (s *Store[H]) Stop(ctx context.Context) error {
-	select {
-	case <-s.writesDn:
-		return errStoppedStore
-	default:
-	}
-	// signal to prevent further writes to Store
-	s.writes <- nil
-	select {
-	case <-s.writesDn: // wait till it is done writing
-	case <-ctx.Done():
-		return ctx.Err()
-	}
-
-	// cleanup caches
-	s.cache.Purge()
-	s.heightIndex.cache.Purge()
-	return nil
-}
-
-func (s *Store[H]) Height() uint64 {
-	return s.heightSub.Height()
-}
-
-func (s *Store[H]) Head(ctx context.Context) (H, error) {
-	head, err := s.GetByHeight(ctx, s.heightSub.Height())
-	if err == nil {
-		return head, nil
-	}
-
-	var zero H
-	head, err = s.readHead(ctx)
-	switch err {
-	default:
-		return zero, err
-	case datastore.ErrNotFound, header.ErrNotFound:
-		return zero, header.ErrNoHead
-	case nil:
-		s.heightSub.SetHeight(uint64(head.Height()))
-		log.Infow("loaded head", "height", head.Height(), "hash", head.Hash())
-		return head, nil
-	}
-}
-
-func (s *Store[H]) Get(ctx context.Context, hash header.Hash) (H, error) {
-	var zero H
-	if v, ok := s.cache.Get(hash.String()); ok {
-		return v.(H), nil
-	}
-	// check if the requested header is not yet written on disk
-	if h := s.pending.Get(hash); !h.IsZero() {
-		return h, nil
-	}
-
-	b, err := s.ds.Get(ctx, datastore.NewKey(hash.String()))
-	if err != nil {
-		if err == datastore.ErrNotFound {
-			return zero, header.ErrNotFound
-		}
-
-		return zero, err
-	}
-
-	var empty H
-	h := empty.New()
-	err = h.UnmarshalBinary(b)
-	if err != nil {
-		return zero, err
-	}
-
-	s.cache.Add(h.Hash().String(), h)
-	return h.(H), nil
-}
-
-func (s *Store[H]) GetByHeight(ctx context.Context, height uint64) (H, error) {
-	var zero H
-	if height == 0 {
-		return zero, fmt.Errorf("header/store: height must be bigger than zero")
-	}
-	// if the requested 'height' was not yet published
-	// we subscribe to it
-	h, err := s.heightSub.Sub(ctx, height)
-	if err != errElapsedHeight {
-		return h, err
-	}
-	// otherwise, the errElapsedHeight is thrown,
-	// which means the requested 'height' should be present
-	//
-	// check if the requested header is not yet written on disk
-	if h := s.pending.GetByHeight(height); !h.IsZero() {
-		return h, nil
-	}
-
-	hash, err := s.heightIndex.HashByHeight(ctx, height)
-	if err != nil {
-		if err == datastore.ErrNotFound {
-			return zero, header.ErrNotFound
-		}
-
-		return zero, err
-	}
-
-	return s.Get(ctx, hash)
-}
-
-func (s *Store[H]) GetRangeByHeight(ctx context.Context, from, to uint64) ([]H, error) {
-	h, err := s.GetByHeight(ctx, to-1)
-	if err != nil {
-		return nil, err
-	}
-
-	ln := to - from
-	headers := make([]H, ln)
-	for i := ln - 1; i > 0; i-- {
-		headers[i] = h
-		h, err = s.Get(ctx, h.LastHeader())
-		if err != nil {
-			return nil, err
-		}
-	}
-	headers[0] = h
-
-	return headers, nil
-}
-
-func (s *Store[H]) GetVerifiedRange(
-	ctx context.Context,
-	from H,
-	to uint64,
-) ([]H, error) {
-	if uint64(from.Height()) >= to {
-		return nil, fmt.Errorf("header/store: invalid range(%d,%d)", from.Height(), to)
-	}
-	headers, err := s.GetRangeByHeight(ctx, uint64(from.Height())+1, to)
-	if err != nil {
-		return nil, err
-	}
-
-	for _, h := range headers {
-		err := from.Verify(h)
-		if err != nil {
-			return nil, err
-		}
-		from = h
-	}
-	return headers, nil
-}
-
-func (s *Store[H]) Has(ctx context.Context, hash header.Hash) (bool, error) {
-	if ok := s.cache.Contains(hash.String()); ok {
-		return ok, nil
-	}
-	// check if the requested header is not yet written on disk
-	if ok := s.pending.Has(hash); ok {
-		return ok, nil
-	}
-
-	return s.ds.Has(ctx, datastore.NewKey(hash.String()))
-}
-
-func (s *Store[H]) HasAt(_ context.Context, height uint64) bool {
-	return height != uint64(0) && s.Height() >= height
-}
-
-func (s *Store[H]) Append(ctx context.Context, headers ...H) error {
-	lh := len(headers)
-	if lh == 0 {
-		return nil
-	}
-
-	var err error
-	// take current write head to verify headers against
-	var head H
-	headPtr := s.writeHead.Load()
-	if headPtr == nil {
-		head, err = s.Head(ctx)
-		if err != nil {
-			return err
-		}
-	} else {
-		head = *headPtr
-	}
-
-	// collect valid headers
-	verified := make([]H, 0, lh)
-	for i, h := range headers {
-		// currently store requires all headers to be appended sequentially and adjacently
-		// TODO(@Wondertan): Further pruning friendly Store design should reevaluate this requirement
-		if h.Height() != head.Height()+1 {
-			return &header.ErrNonAdjacent{
-				Head:      head.Height(),
-				Attempted: h.Height(),
-			}
-		}
-
-		err = head.Verify(h)
-		if err != nil {
-			var verErr *header.VerifyError
-			if errors.As(err, &verErr) {
-				log.Errorw("invalid header",
-					"height_of_head", head.Height(),
-					"hash_of_head", head.Hash(),
-					"height_of_invalid", h.Height(),
-					"hash_of_invalid", h.Hash(),
-					"reason", verErr.Reason)
-			}
-			// if the first header is invalid, no need to go further
-			if i == 0 {
-				// and simply return
-				return err
-			}
-			// otherwise, stop the loop and apply headers appeared to be valid
-			break
-		}
-		verified, head = append(verified, h), h
-	}
-
-	// queue headers to be written on disk
-	select {
-	case s.writes <- verified:
-		ln := len(verified)
-		s.writeHead.Store(&verified[ln-1])
-		wh := *s.writeHead.Load()
-		log.Infow("new head", "height", wh.Height(), "hash", wh.Hash())
-		// we return an error here after writing,
-		// as there might be an invalid header in between of a given range
-		return err
-	case <-s.writesDn:
-		return errStoppedStore
-	case <-ctx.Done():
-		return ctx.Err()
-	}
-}
-
-// flushLoop performs writing task to the underlying datastore in a separate routine
-// This way writes are controlled and manageable from one place allowing
-// (1) Appends not to be blocked on long disk IO writes and underlying DB compactions
-// (2) Batching header writes
-func (s *Store[H]) flushLoop() {
-	defer close(s.writesDn)
-	ctx := context.Background()
-	for headers := range s.writes {
-		// add headers to the pending and ensure they are accessible
-		s.pending.Append(headers...)
-		// and notify waiters if any + increase current read head height
-		// it is important to do Pub after updating pending
-		// so pending is consistent with atomic Height counter on the heightSub
-		s.heightSub.Pub(headers...)
-		// don't flush and continue if pending batch is not grown enough,
-		// and Store is not stopping(headers == nil)
-		if s.pending.Len() < s.Params.WriteBatchSize && headers != nil {
-			continue
-		}
-
-		err := s.flush(ctx, s.pending.GetAll()...)
-		if err != nil {
-			// TODO(@Wondertan): Should this be a fatal error case with os.Exit?
-			from, to := uint64(headers[0].Height()), uint64(headers[len(headers)-1].Height())
-			log.Errorw("writing header batch", "from", from, "to", to)
-			continue
-		}
-		// reset pending
-		s.pending.Reset()
-
-		if headers == nil {
-			// a signal to stop
-			return
-		}
-	}
-}
-
-// flush writes the given batch to datastore.
-func (s *Store[H]) flush(ctx context.Context, headers ...H) error {
-	ln := len(headers)
-	if ln == 0 {
-		return nil
-	}
-
-	batch, err := s.ds.Batch(ctx)
-	if err != nil {
-		return err
-	}
-
-	// collect all the headers in the batch to be written
-	for _, h := range headers {
-		b, err := h.MarshalBinary()
-		if err != nil {
-			return err
-		}
-
-		err = batch.Put(ctx, headerKey(h), b)
-		if err != nil {
-			return err
-		}
-	}
-
-	// marshal and add to batch reference to the new head
-	b, err := headers[ln-1].Hash().MarshalJSON()
-	if err != nil {
-		return err
-	}
-
-	err = batch.Put(ctx, headKey, b)
-	if err != nil {
-		return err
-	}
-
-	// write height indexes for headers as well
-	err = s.heightIndex.IndexTo(ctx, batch, headers...)
-	if err != nil {
-		return err
-	}
-
-	// finally, commit the batch on disk
-	return batch.Commit(ctx)
-}
-
-// readHead loads the head from the datastore.
-func (s *Store[H]) readHead(ctx context.Context) (H, error) {
-	var zero H
-	b, err := s.ds.Get(ctx, headKey)
-	if err != nil {
-		return zero, err
-	}
-
-	var head header.Hash
-	err = head.UnmarshalJSON(b)
-	if err != nil {
-		return zero, err
-	}
-
-	return s.Get(ctx, head)
-}
diff --git a/libs/header/sync/metrics.go b/libs/header/sync/metrics.go
deleted file mode 100644
index d76823b7..00000000
--- a/libs/header/sync/metrics.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package sync
-
-import (
-	"context"
-	"sync/atomic"
-
-	"go.opentelemetry.io/otel/metric/global"
-	"go.opentelemetry.io/otel/metric/instrument"
-)
-
-var meter = global.MeterProvider().Meter("header/sync")
-
-type metrics struct {
-	totalSynced int64
-}
-
-func (s *Syncer[H]) InitMetrics() error {
-	s.metrics = &metrics{}
-
-	totalSynced, err := meter.
-		AsyncFloat64().
-		Gauge(
-			"total_synced_headers",
-			instrument.WithDescription("total synced headers"),
-		)
-	if err != nil {
-		return err
-	}
-
-	return meter.RegisterCallback(
-		[]instrument.Asynchronous{
-			totalSynced,
-		},
-		func(ctx context.Context) {
-			totalSynced.Observe(ctx, float64(atomic.LoadInt64(&s.metrics.totalSynced)))
-		},
-	)
-}
-
-// recordTotalSynced records the total amount of synced headers.
-func (m *metrics) recordTotalSynced(totalSynced int) {
-	if m != nil {
-		atomic.AddInt64(&m.totalSynced, int64(totalSynced))
-	}
-}
diff --git a/libs/header/sync/options.go b/libs/header/sync/options.go
deleted file mode 100644
index d314003c..00000000
--- a/libs/header/sync/options.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package sync
-
-import (
-	"fmt"
-	"time"
-)
-
-type Options func(*Parameters)
-
-// Parameters is the set of parameters that must be configured for the syncer.
-type Parameters struct {
-	// TrustingPeriod is period through which we can trust a header's validators set.
-	//
-	// Should be significantly less than the unbonding period (e.g. unbonding
-	// period = 3 weeks, trusting period = 2 weeks).
-	//
-	// More specifically, trusting period + time needed to check headers + time
-	// needed to report and punish misbehavior should be less than the unbonding
-	// period.
-	TrustingPeriod time.Duration
-	// blockTime provides a reference point for the Syncer to determine
-	// whether its subjective head is outdated.
-	// Keeping it private, we don't want users to independently configure it.
-	// default value is set to 0 so syncer will constantly request networking head.
-	blockTime time.Duration
-}
-
-// DefaultParameters returns the default params to configure the syncer.
-func DefaultParameters() Parameters {
-	return Parameters{
-		TrustingPeriod: 168 * time.Hour,
-	}
-}
-
-func (p *Parameters) Validate() error {
-	if p.TrustingPeriod == 0 {
-		return fmt.Errorf("invalid trusting period duration: %v", p.TrustingPeriod)
-	}
-	return nil
-}
-
-// WithBlockTime is a functional option that configures the
-// `blockTime` parameter.
-func WithBlockTime(duration time.Duration) Options {
-	return func(p *Parameters) {
-		p.blockTime = duration
-	}
-}
-
-// WithTrustingPeriod is a functional option that configures the
-// `TrustingPeriod` parameter.
-func WithTrustingPeriod(duration time.Duration) Options {
-	return func(p *Parameters) {
-		p.TrustingPeriod = duration
-	}
-}
diff --git a/libs/header/sync/ranges.go b/libs/header/sync/ranges.go
deleted file mode 100644
index 86d54541..00000000
--- a/libs/header/sync/ranges.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package sync
-
-import (
-	"sync"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// 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 {
-	lk     sync.RWMutex
-	ranges []*headerRange[H]
-}
-
-// Head returns the highest Header in all ranges if any.
-func (rs *ranges[H]) Head() H {
-	rs.lk.RLock()
-	defer rs.lk.RUnlock()
-
-	return rs.head()
-}
-
-func (rs *ranges[H]) head() H {
-	ln := len(rs.ranges)
-	if ln == 0 {
-		var zero H
-		return zero
-	}
-
-	head := rs.ranges[ln-1]
-	return head.Head()
-}
-
-// Add appends the new Header to existing range or starts a new one.
-// It starts a new one if the new Header is not adjacent to any of existing ranges.
-func (rs *ranges[H]) Add(h H) {
-	rs.lk.Lock()
-	defer rs.lk.Unlock()
-
-	head := rs.head()
-
-	// short-circuit if header is from the past
-	if !head.IsZero() && head.Height() >= h.Height() {
-		// TODO(@Wondertan): Technically, we can still apply the header:
-		//  * Headers here are verified, so we can trust them
-		//  * PubSub does not guarantee the ordering of msgs
-		//    * So there might be a case where ordering is broken
-		//    * Even considering the delay(block time) with which new headers are generated
-		//    * But rarely
-		//  Would be still nice to implement
-		log.Warnf("rcvd headers in wrong order")
-		return
-	}
-
-	// if the new header is adjacent to head
-	if !head.IsZero() && h.Height() == head.Height()+1 {
-		// append it to the last known range
-		rs.ranges[len(rs.ranges)-1].Append(h)
-	} else {
-		// otherwise, start a new range
-		rs.ranges = append(rs.ranges, newRange[H](h))
-
-		// it is possible to miss a header or few from gossiping subsystem, due to quick disconnects
-		// or hibernation, so once we start rcving them again we save those in the new range
-	}
-}
-
-// First provides a first non-empty range, while cleaning up empty ones.
-func (rs *ranges[H]) First() (*headerRange[H], bool) {
-	rs.lk.Lock()
-	defer rs.lk.Unlock()
-
-	for {
-		if len(rs.ranges) == 0 {
-			return nil, false
-		}
-
-		out := rs.ranges[0]
-		if !out.Empty() {
-			return out, true
-		}
-
-		rs.ranges = rs.ranges[1:]
-	}
-}
-
-type headerRange[H header.Header] struct {
-	lk      sync.RWMutex
-	headers []H
-	start   uint64
-}
-
-func newRange[H header.Header](h H) *headerRange[H] {
-	return &headerRange[H]{
-		start:   uint64(h.Height()),
-		headers: []H{h},
-	}
-}
-
-// Append appends new headers.
-func (r *headerRange[H]) Append(h ...H) {
-	r.lk.Lock()
-	r.headers = append(r.headers, h...)
-	r.lk.Unlock()
-}
-
-// Empty reports if range is empty.
-func (r *headerRange[H]) Empty() bool {
-	r.lk.RLock()
-	defer r.lk.RUnlock()
-	return len(r.headers) == 0
-}
-
-// Head reports the head of range if any.
-func (r *headerRange[H]) Head() H {
-	r.lk.RLock()
-	defer r.lk.RUnlock()
-	ln := len(r.headers)
-	if ln == 0 {
-		var zero H
-		return zero
-	}
-	return r.headers[ln-1]
-}
-
-// Get returns headers within the range up to the specified 'end' height.
-func (r *headerRange[H]) Get(end uint64) []H {
-	r.lk.RLock()
-	defer r.lk.RUnlock()
-
-	amnt := r.rangeAmount(end)
-	return r.headers[:amnt]
-}
-
-// Remove removes all headers within the range up to the specified 'end' height.
-func (r *headerRange[H]) Remove(end uint64) {
-	r.lk.Lock()
-	defer r.lk.Unlock()
-
-	amnt := r.rangeAmount(end)
-	r.headers = r.headers[amnt:]
-	if len(r.headers) != 0 {
-		r.start = uint64(r.headers[0].Height())
-	}
-}
-
-// rangeAmount returns the number of headers to be removed or returned up to the specified 'end'
-// height.
-func (r *headerRange[H]) rangeAmount(end uint64) uint64 {
-	if r.start > end {
-		return 0
-	}
-
-	amnt := uint64(len(r.headers))
-	if r.start+amnt >= end {
-		amnt = end - r.start + 1 // + 1 to include 'end' as well
-	}
-
-	return amnt
-}
diff --git a/libs/header/sync/sync.go b/libs/header/sync/sync.go
deleted file mode 100644
index 50451545..00000000
--- a/libs/header/sync/sync.go
+++ /dev/null
@@ -1,324 +0,0 @@
-package sync
-
-import (
-	"context"
-	"errors"
-	"sync"
-	"time"
-
-	logging "github.com/ipfs/go-log/v2"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-var log = logging.Logger("header/sync")
-
-// Syncer implements efficient synchronization for headers.
-//
-// Subjective Head - the latest known local valid header and a sync target.
-// Network Head - the latest valid network-wide header. Becomes subjective once applied locally.
-//
-// There are two main processes running in Syncer:
-// - Main syncing loop(s.syncLoop)
-//   - Performs syncing from the latest stored header up to the latest known Subjective Head
-//   - Syncs by requesting missing headers from Exchange or
-//   - By accessing cache of pending headers
-//
-// - Receives every new Network Head from PubSub gossip subnetwork (s.incomingNetworkHead)
-//   - Validates against the latest known Subjective Head, is so
-//   - 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 {
-	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
-	metrics *metrics
-
-	// stateLk protects state which represents the current or latest sync
-	stateLk sync.RWMutex
-	state   State
-
-	// signals to start syncing
-	triggerSync chan struct{}
-	// pending keeps ranges of valid new network headers awaiting to be appended to store
-	pending ranges[H]
-
-	// controls lifecycle for syncLoop
-	ctx    context.Context
-	cancel context.CancelFunc
-
-	Params *Parameters
-}
-
-// NewSyncer creates a new instance of Syncer.
-func NewSyncer[H header.Header](
-	getter header.Getter[H],
-	store header.Store[H],
-	sub header.Subscriber[H],
-	opts ...Options,
-) (*Syncer[H], error) {
-	params := DefaultParameters()
-	for _, opt := range opts {
-		opt(&params)
-	}
-	if err := params.Validate(); err != nil {
-		return nil, err
-	}
-
-	return &Syncer[H]{
-		sub:         sub,
-		store:       syncStore[H]{Store: store},
-		getter:      syncGetter[H]{Getter: getter},
-		triggerSync: make(chan struct{}, 1), // should be buffered
-		Params:      &params,
-	}, nil
-}
-
-// Start starts the syncing routine.
-func (s *Syncer[H]) Start(ctx context.Context) error {
-	s.ctx, s.cancel = context.WithCancel(context.Background())
-	// register validator for header subscriptions
-	// syncer does not subscribe itself and syncs headers together with validation
-	err := s.sub.AddValidator(s.incomingNetworkHead)
-	if err != nil {
-		return err
-	}
-	// gets the latest head and kicks off syncing if necessary
-	_, err = s.Head(ctx)
-	if err != nil {
-		return err
-	}
-	// start syncLoop only if Start is errorless
-	go s.syncLoop()
-	return nil
-}
-
-// Stop stops Syncer.
-func (s *Syncer[H]) Stop(context.Context) error {
-	s.cancel()
-	return nil
-}
-
-// SyncWait blocks until ongoing sync is done.
-func (s *Syncer[H]) SyncWait(ctx context.Context) error {
-	state := s.State()
-	if state.Finished() {
-		return nil
-	}
-
-	// this store method blocks until header is available
-	_, err := s.store.GetByHeight(ctx, state.ToHeight)
-	return err
-}
-
-// State collects all the information about a sync.
-type State struct {
-	ID                   uint64 // incrementing ID of a sync
-	Height               uint64 // height at the moment when State is requested for a sync
-	FromHeight, ToHeight uint64 // the starting and the ending point of a sync
-	FromHash, ToHash     header.Hash
-	Start, End           time.Time
-	Error                error // the error that might happen within a sync
-}
-
-// Finished returns true if sync is done, false otherwise.
-func (s State) Finished() bool {
-	return s.ToHeight <= s.Height
-}
-
-// Duration returns the duration of the sync.
-func (s State) Duration() time.Duration {
-	return s.End.Sub(s.Start)
-}
-
-// State reports state of the current (if in progress), or last sync (if finished).
-// Note that throughout the whole Syncer lifetime there might an initial sync and multiple
-// catch-ups. All of them are treated as different syncs with different state IDs and other
-// information.
-func (s *Syncer[H]) State() State {
-	s.stateLk.RLock()
-	state := s.state
-	s.stateLk.RUnlock()
-
-	head, err := s.store.Head(s.ctx)
-	if err == nil {
-		state.Height = uint64(head.Height())
-	} else if state.Error == nil {
-		// don't ignore the error if we can show it in the state
-		state.Error = err
-	}
-
-	return state
-}
-
-// wantSync will trigger the syncing loop (non-blocking).
-func (s *Syncer[H]) wantSync() {
-	select {
-	case s.triggerSync <- struct{}{}:
-	default:
-	}
-}
-
-// syncLoop controls syncing process.
-func (s *Syncer[H]) syncLoop() {
-	for {
-		select {
-		case <-s.triggerSync:
-			s.sync(s.ctx)
-		case <-s.ctx.Done():
-			return
-		}
-	}
-}
-
-// sync ensures we are synced from the Store's head up to the new subjective head.
-func (s *Syncer[H]) sync(ctx context.Context) {
-	subjHead, err := s.subjectiveHead(ctx)
-	if err != nil {
-		log.Errorw("getting subjective head", "err", err)
-		return
-	}
-
-	storeHead, err := s.store.Head(ctx)
-	if err != nil {
-		log.Errorw("getting stored head", "err", err)
-		return
-	}
-
-	if storeHead.Height() >= subjHead.Height() {
-		log.Warnw("sync attempt to an already synced header",
-			"synced_height", storeHead.Height(),
-			"attempted_height", subjHead.Height(),
-		)
-		log.Warn("PLEASE REPORT THIS AS A BUG")
-		return // should never happen, but just in case
-	}
-
-	from := storeHead.Height() + 1
-	log.Infow("syncing headers",
-		"from", from,
-		"to", subjHead.Height())
-
-	err = s.doSync(ctx, storeHead, subjHead)
-	if err != nil {
-		if errors.Is(err, context.Canceled) {
-			// don't log this error as it is normal case of Syncer being stopped
-			return
-		}
-
-		log.Errorw("syncing headers",
-			"from", from,
-			"to", subjHead.Height(),
-			"err", err)
-		return
-	}
-
-	log.Infow("finished syncing headers",
-		"from", from,
-		"to", subjHead.Height(),
-		"elapsed time", s.state.End.Sub(s.state.Start))
-}
-
-// doSync performs actual syncing updating the internal State.
-func (s *Syncer[H]) doSync(ctx context.Context, fromHead, toHead H) (err error) {
-	s.stateLk.Lock()
-	s.state.ID++
-	s.state.FromHeight = uint64(fromHead.Height()) + 1
-	s.state.ToHeight = uint64(toHead.Height())
-	s.state.FromHash = fromHead.Hash()
-	s.state.ToHash = toHead.Hash()
-	s.state.Start = time.Now()
-	s.stateLk.Unlock()
-
-	err = s.processHeaders(ctx, fromHead, uint64(toHead.Height()))
-
-	s.stateLk.Lock()
-	s.state.End = time.Now()
-	s.state.Error = err
-	s.stateLk.Unlock()
-	return err
-}
-
-// processHeaders fetches and stores asked headers (from:to].
-// Checks headers in pending cache that apply to the requested range.
-// If some headers are missing, it starts requesting them from the network.
-func (s *Syncer[H]) processHeaders(
-	ctx context.Context,
-	fromHead H,
-	to uint64,
-) (err error) {
-	for {
-		headersRange, ok := s.pending.First()
-		if !ok {
-			break
-		}
-
-		headers := headersRange.Get(to)
-		if len(headers) == 0 {
-			break
-		}
-
-		// check if returned range is not adjacent to `fromHead`
-		if fromHead.Height()+1 != headers[0].Height() {
-			// if so - request missing ones
-			to := uint64(headers[0].Height() - 1)
-			if err = s.requestHeaders(ctx, fromHead, to); err != nil {
-				return err
-			}
-		}
-
-		// apply cached headers
-		if err = s.storeHeaders(ctx, headers...); err != nil {
-			return err
-		}
-
-		// cleanup range only after we stored the headers
-		headersRange.Remove(to)
-		// update fromHead for the next iteration
-		fromHead = headers[len(headers)-1]
-	}
-	return s.requestHeaders(ctx, fromHead, to)
-}
-
-// requestHeaders requests headers from the network -> (fromHeader.Height : to].
-func (s *Syncer[H]) requestHeaders(
-	ctx context.Context,
-	fromHead H,
-	to uint64,
-) error {
-	amount := to - uint64(fromHead.Height())
-	// start requesting headers until amount remaining will be 0
-	for amount > 0 {
-		size := header.MaxRangeRequestSize
-		if amount < size {
-			size = amount
-		}
-
-		headers, err := s.getter.GetVerifiedRange(ctx, fromHead, size)
-		if err != nil {
-			return err
-		}
-
-		if err := s.storeHeaders(ctx, headers...); err != nil {
-			return err
-		}
-
-		amount -= size // size == len(headers)
-		fromHead = headers[len(headers)-1]
-	}
-	return nil
-}
-
-// storeHeaders updates store with new headers and updates current syncStore's Head.
-func (s *Syncer[H]) storeHeaders(ctx context.Context, headers ...H) error {
-	// we don't expect any issues in storing right now, as all headers are now verified.
-	// So, we should return immediately in case an error appears.
-	err := s.store.Append(ctx, headers...)
-	if err != nil {
-		return err
-	}
-
-	s.metrics.recordTotalSynced(len(headers))
-	return nil
-}
diff --git a/libs/header/sync/sync_getter.go b/libs/header/sync/sync_getter.go
deleted file mode 100644
index ad364e7b..00000000
--- a/libs/header/sync/sync_getter.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package sync
-
-import (
-	"context"
-	"sync"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// syncGetter is a Getter wrapper that ensure only one Head call happens at the time
-type syncGetter[H header.Header] struct {
-	header.Getter[H]
-
-	headLk  sync.RWMutex
-	headErr error
-	head    H
-}
-
-func (se *syncGetter[H]) Head(ctx context.Context) (H, error) {
-	// the lock construction here ensures only one routine calling Head at a time
-	// while others wait via Rlock
-	if !se.headLk.TryLock() {
-		se.headLk.RLock()
-		defer se.headLk.RUnlock()
-		return se.head, se.headErr
-	}
-	defer se.headLk.Unlock()
-
-	se.head, se.headErr = se.Getter.Head(ctx)
-	return se.head, se.headErr
-}
diff --git a/libs/header/sync/sync_head.go b/libs/header/sync/sync_head.go
deleted file mode 100644
index 29ac310a..00000000
--- a/libs/header/sync/sync_head.go
+++ /dev/null
@@ -1,174 +0,0 @@
-package sync
-
-import (
-	"context"
-	"errors"
-	"time"
-
-	pubsub "github.com/libp2p/go-libp2p-pubsub"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// Head returns the Network Head.
-//
-// 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) (H, error) {
-	sbjHead, err := s.subjectiveHead(ctx)
-	if err != nil {
-		return sbjHead, err
-	}
-	// if subjective header is recent enough (relative to the network's block time) - just use it
-	if isRecent(sbjHead, s.Params.blockTime) {
-		return sbjHead, nil
-	}
-	// otherwise, request head from a trusted peer, as we assume it is fully synced
-	//
-	// TODO(@Wondertan): Here is another potential networking optimization:
-	//  * From sbjHead's timestamp and current time predict the time to the next header(TNH)
-	//  * If now >= TNH && now <= TNH + (THP) header propagation time
-	//    * Wait for header to arrive instead of requesting it
-	//  * This way we don't request as we know the new network header arrives exactly
-	netHead, err := s.getter.Head(ctx)
-	if err != nil {
-		return netHead, err
-	}
-	// process and validate netHead fetched from trusted peers
-	// NOTE: We could trust the netHead like we do during 'automatic subjective initialization'
-	// but in this case our subjective head is not expired, so we should verify netHead
-	// and only if it is valid, set it as new head
-	s.incomingNetworkHead(ctx, netHead)
-	// netHead was either accepted or rejected as the new subjective
-	// anyway return most current known subjective head
-	return s.subjectiveHead(ctx)
-}
-
-// subjectiveHead returns the latest known local header that is not expired(within trusting period).
-// If the header is expired, it is retrieved from a trusted peer without validation;
-// in other words, an automatic subjective initialization is performed.
-func (s *Syncer[H]) subjectiveHead(ctx context.Context) (H, error) {
-	// pending head is the latest known subjective head and sync target, so try to get it
-	// NOTES:
-	// * Empty when no sync is in progress
-	// * Pending cannot be expired, guaranteed
-	pendHead := s.pending.Head()
-	if !pendHead.IsZero() {
-		return pendHead, nil
-	}
-	// if pending is empty - get the latest stored/synced head
-	storeHead, err := s.store.Head(ctx)
-	if err != nil {
-		return storeHead, err
-	}
-	// check if the stored header is not expired and use it
-	if !isExpired(storeHead, s.Params.TrustingPeriod) {
-		return storeHead, nil
-	}
-	// otherwise, request head from a trusted peer
-	log.Infow("stored head header expired", "height", storeHead.Height())
-	trustHead, err := s.getter.Head(ctx)
-	if err != nil {
-		return trustHead, err
-	}
-	// and set it as the new subjective head without validation,
-	// or, in other words, do 'automatic subjective initialization'
-	// NOTE: we avoid validation as the head expired to prevent possibility of the Long-Range Attack
-	s.setSubjectiveHead(ctx, trustHead)
-	switch {
-	default:
-		log.Infow("subjective initialization finished", "height", trustHead.Height())
-		return trustHead, nil
-	case isExpired(trustHead, s.Params.TrustingPeriod):
-		log.Warnw("subjective initialization with an expired header", "height", trustHead.Height())
-	case !isRecent(trustHead, s.Params.blockTime):
-		log.Warnw("subjective initialization with an old header", "height", trustHead.Height())
-	}
-	log.Warn("trusted peer is out of sync")
-	return trustHead, nil
-}
-
-// setSubjectiveHead takes already validated head and sets it as the new sync target.
-func (s *Syncer[H]) setSubjectiveHead(ctx context.Context, netHead H) {
-	// TODO(@Wondertan): Right now, we can only store adjacent headers, instead we should:
-	//  * Allow storing any valid header here in Store
-	//  * Remove ErrNonAdjacent
-	//  * Remove writeHead from the canonical store implementation
-	err := s.storeHeaders(ctx, netHead)
-	var nonAdj *header.ErrNonAdjacent
-	if err != nil && !errors.As(err, &nonAdj) {
-		// might be a storage error or something else, but we can still try to continue processing netHead
-		log.Errorw("storing new network header",
-			"height", netHead.Height(),
-			"hash", netHead.Hash().String(),
-			"err", err)
-	}
-
-	storeHead, err := s.store.Head(ctx)
-	if err == nil && storeHead.Height() >= netHead.Height() {
-		// we already synced it up - do nothing
-		return
-	}
-	// and if valid, set it as new subjective head
-	s.pending.Add(netHead)
-	s.wantSync()
-	log.Infow("new network head", "height", netHead.Height(), "hash", netHead.Hash())
-}
-
-// incomingNetworkHead processes new potential network headers.
-// If the header valid, sets as new subjective header.
-func (s *Syncer[H]) incomingNetworkHead(ctx context.Context, netHead H) pubsub.ValidationResult {
-	// first of all, check the validity of the netHead
-	res := s.validateHead(ctx, netHead)
-	if res == pubsub.ValidationAccept {
-		// and set it if valid
-		s.setSubjectiveHead(ctx, netHead)
-	}
-	return res
-}
-
-// validateHead checks validity of the given header against the subjective head.
-func (s *Syncer[H]) validateHead(ctx context.Context, new H) pubsub.ValidationResult {
-	sbjHead, err := s.subjectiveHead(ctx)
-	if err != nil {
-		log.Errorw("getting subjective head during validation", "err", err)
-		return pubsub.ValidationIgnore // local error, so ignore
-	}
-	// ignore header if it's from the past
-	if new.Height() <= sbjHead.Height() {
-		log.Warnw("received known network header",
-			"current_height", sbjHead.Height(),
-			"header_height", new.Height(),
-			"header_hash", new.Hash())
-		return pubsub.ValidationIgnore
-	}
-	// perform verification
-	err = sbjHead.Verify(new)
-	var verErr *header.VerifyError
-	if errors.As(err, &verErr) {
-		log.Errorw("invalid network header",
-			"height_of_invalid", new.Height(),
-			"hash_of_invalid", new.Hash(),
-			"height_of_subjective", sbjHead.Height(),
-			"hash_of_subjective", sbjHead.Hash(),
-			"reason", verErr.Reason)
-		return pubsub.ValidationReject
-	}
-	// and accept if the header is good
-	return pubsub.ValidationAccept
-}
-
-// TODO(@Wondertan): We should request TrustingPeriod from the network's state params or
-//  listen for network params changes to always have a topical value.
-
-// isExpired checks if header is expired against trusting period.
-func isExpired(header header.Header, 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 {
-	return time.Since(header.Time()) <= blockTime+blockTime/2 // add half block time drift
-}
diff --git a/libs/header/sync/sync_store.go b/libs/header/sync/sync_store.go
deleted file mode 100644
index b2e31239..00000000
--- a/libs/header/sync/sync_store.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package sync
-
-import (
-	"context"
-	"sync/atomic"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-// 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 {
-	header.Store[H]
-
-	head atomic.Pointer[H]
-}
-
-func (s *syncStore[H]) Head(ctx context.Context) (H, error) {
-	if headPtr := s.head.Load(); headPtr != nil {
-		return *headPtr, nil
-	}
-
-	storeHead, err := s.Store.Head(ctx)
-	if err != nil {
-		return storeHead, err
-	}
-
-	s.head.Store(&storeHead)
-	return storeHead, nil
-}
-
-func (s *syncStore[H]) Append(ctx context.Context, headers ...H) error {
-	if err := s.Store.Append(ctx, headers...); err != nil {
-		return err
-	}
-
-	s.head.Store(&headers[len(headers)-1])
-	return nil
-}
diff --git a/nodebuilder/core/module.go b/nodebuilder/core/module.go
index ecb00289..243310da 100644
--- a/nodebuilder/core/module.go
+++ b/nodebuilder/core/module.go
@@ -5,10 +5,11 @@ import (
 
 	"go.uber.org/fx"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/core"
 	"github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/libs/fxutil"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/nodebuilder/node"
 	"github.com/celestiaorg/celestia-node/share/eds"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"
diff --git a/nodebuilder/das/config.go b/nodebuilder/das/config.go
index c08de959..eeaa382a 100644
--- a/nodebuilder/das/config.go
+++ b/nodebuilder/das/config.go
@@ -28,7 +28,8 @@ func DefaultConfig(tp node.Type) Config {
 		// Full node will primarily use shrex protocol for sampling, that is much more efficient and can
 		// fully utilize nodes bandwidth with lower amount of parallel sampling workers
 		cfg.ConcurrencyLimit = 6
-		// Full node uses shrex with fallback to ipld to sample, so need 2x amount of time in worst case scenario
+		// Full node uses shrex with fallback to ipld to sample, so need 2x amount of time in worst case
+		// scenario
 		cfg.SampleTimeout = 2 * modp2p.BlockTime * time.Duration(cfg.ConcurrencyLimit)
 	}
 	return Config(cfg)
diff --git a/nodebuilder/das/constructors.go b/nodebuilder/das/constructors.go
index 14298b27..d298c044 100644
--- a/nodebuilder/das/constructors.go
+++ b/nodebuilder/das/constructors.go
@@ -6,10 +6,11 @@ import (
 
 	"github.com/ipfs/go-datastore"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/das"
 	"github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/libs/fraud"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"
 )
diff --git a/nodebuilder/fraud/constructors.go b/nodebuilder/fraud/constructors.go
index 6f62fcb6..b6891d6d 100644
--- a/nodebuilder/fraud/constructors.go
+++ b/nodebuilder/fraud/constructors.go
@@ -8,9 +8,10 @@ import (
 	"github.com/libp2p/go-libp2p/core/host"
 	"go.uber.org/fx"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/libs/fraud"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/nodebuilder/p2p"
 )
 
diff --git a/nodebuilder/header/config.go b/nodebuilder/header/config.go
index 85a7fa5d..238fb39a 100644
--- a/nodebuilder/header/config.go
+++ b/nodebuilder/header/config.go
@@ -7,10 +7,11 @@ import (
 	"github.com/libp2p/go-libp2p/core/peer"
 	"github.com/multiformats/go-multiaddr"
 
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
-	p2p_exchange "github.com/celestiaorg/celestia-node/libs/header/p2p"
-	"github.com/celestiaorg/celestia-node/libs/header/store"
-	"github.com/celestiaorg/celestia-node/libs/header/sync"
+	libhead "github.com/celestiaorg/go-header"
+	p2p_exchange "github.com/celestiaorg/go-header/p2p"
+	"github.com/celestiaorg/go-header/store"
+	"github.com/celestiaorg/go-header/sync"
+
 	"github.com/celestiaorg/celestia-node/nodebuilder/node"
 	"github.com/celestiaorg/celestia-node/nodebuilder/p2p"
 )
diff --git a/nodebuilder/header/constructors.go b/nodebuilder/header/constructors.go
index f24c7d6d..4650ed77 100644
--- a/nodebuilder/header/constructors.go
+++ b/nodebuilder/header/constructors.go
@@ -9,11 +9,12 @@ import (
 	"github.com/libp2p/go-libp2p/p2p/net/conngater"
 	"go.uber.org/fx"
 
+	libhead "github.com/celestiaorg/go-header"
+	"github.com/celestiaorg/go-header/p2p"
+	"github.com/celestiaorg/go-header/store"
+	"github.com/celestiaorg/go-header/sync"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/p2p"
-	"github.com/celestiaorg/celestia-node/libs/header/store"
-	"github.com/celestiaorg/celestia-node/libs/header/sync"
 	modp2p "github.com/celestiaorg/celestia-node/nodebuilder/p2p"
 )
 
diff --git a/nodebuilder/header/header.go b/nodebuilder/header/header.go
index 36f06b8c..ba4a49dc 100644
--- a/nodebuilder/header/header.go
+++ b/nodebuilder/header/header.go
@@ -3,9 +3,10 @@ package header
 import (
 	"context"
 
+	libhead "github.com/celestiaorg/go-header"
+	"github.com/celestiaorg/go-header/sync"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/sync"
 )
 
 // Module exposes the functionality needed for querying headers from the network.
diff --git a/nodebuilder/header/mocks/api.go b/nodebuilder/header/mocks/api.go
index b2363677..6691aff1 100644
--- a/nodebuilder/header/mocks/api.go
+++ b/nodebuilder/header/mocks/api.go
@@ -11,8 +11,8 @@ import (
 	gomock "github.com/golang/mock/gomock"
 
 	header "github.com/celestiaorg/celestia-node/header"
-	header0 "github.com/celestiaorg/celestia-node/libs/header"
-	sync "github.com/celestiaorg/celestia-node/libs/header/sync"
+	header0 "github.com/celestiaorg/go-header"
+	sync "github.com/celestiaorg/go-header/sync"
 )
 
 // MockModule is a mock of Module interface.
diff --git a/nodebuilder/header/module.go b/nodebuilder/header/module.go
index 3f6bf4ed..5138305c 100644
--- a/nodebuilder/header/module.go
+++ b/nodebuilder/header/module.go
@@ -8,12 +8,13 @@ import (
 	pubsub "github.com/libp2p/go-libp2p-pubsub"
 	"go.uber.org/fx"
 
+	libhead "github.com/celestiaorg/go-header"
+	"github.com/celestiaorg/go-header/p2p"
+	"github.com/celestiaorg/go-header/store"
+	"github.com/celestiaorg/go-header/sync"
+
 	"github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/libs/fraud"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/p2p"
-	"github.com/celestiaorg/celestia-node/libs/header/store"
-	"github.com/celestiaorg/celestia-node/libs/header/sync"
 	modfraud "github.com/celestiaorg/celestia-node/nodebuilder/fraud"
 	"github.com/celestiaorg/celestia-node/nodebuilder/node"
 	modp2p "github.com/celestiaorg/celestia-node/nodebuilder/p2p"
diff --git a/nodebuilder/header/opts.go b/nodebuilder/header/opts.go
index 5663f059..decedcad 100644
--- a/nodebuilder/header/opts.go
+++ b/nodebuilder/header/opts.go
@@ -1,10 +1,11 @@
 package header
 
 import (
+	libhead "github.com/celestiaorg/go-header"
+	"github.com/celestiaorg/go-header/p2p"
+	"github.com/celestiaorg/go-header/sync"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/p2p"
-	"github.com/celestiaorg/celestia-node/libs/header/sync"
 )
 
 // WithMetrics provides sets `MetricsEnabled` to true on ClientParameters for the header exchange
diff --git a/nodebuilder/header/service.go b/nodebuilder/header/service.go
index 35e4d290..28e70fc0 100644
--- a/nodebuilder/header/service.go
+++ b/nodebuilder/header/service.go
@@ -3,10 +3,11 @@ package header
 import (
 	"context"
 
+	libhead "github.com/celestiaorg/go-header"
+	"github.com/celestiaorg/go-header/p2p"
+	"github.com/celestiaorg/go-header/sync"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/p2p"
-	"github.com/celestiaorg/celestia-node/libs/header/sync"
 )
 
 // Service represents the header Service that can be started / stopped on a node.
diff --git a/nodebuilder/p2p/pubsub.go b/nodebuilder/p2p/pubsub.go
index d8d302a5..6202aa9f 100644
--- a/nodebuilder/p2p/pubsub.go
+++ b/nodebuilder/p2p/pubsub.go
@@ -15,8 +15,9 @@ import (
 	"go.uber.org/fx"
 	"golang.org/x/crypto/blake2b"
 
+	headp2p "github.com/celestiaorg/go-header/p2p"
+
 	"github.com/celestiaorg/celestia-node/libs/fraud"
-	headp2p "github.com/celestiaorg/celestia-node/libs/header/p2p"
 )
 
 func init() {
diff --git a/nodebuilder/state/core.go b/nodebuilder/state/core.go
index e9f24007..052b6eef 100644
--- a/nodebuilder/state/core.go
+++ b/nodebuilder/state/core.go
@@ -2,9 +2,9 @@ package state
 
 import (
 	apptypes "github.com/celestiaorg/celestia-app/x/blob/types"
+	"github.com/celestiaorg/go-header/sync"
 
 	"github.com/celestiaorg/celestia-node/header"
-	"github.com/celestiaorg/celestia-node/libs/header/sync"
 	"github.com/celestiaorg/celestia-node/nodebuilder/core"
 	"github.com/celestiaorg/celestia-node/state"
 )
diff --git a/share/eds/byzantine/bad_encoding.go b/share/eds/byzantine/bad_encoding.go
index 6bee1ff4..288c14d3 100644
--- a/share/eds/byzantine/bad_encoding.go
+++ b/share/eds/byzantine/bad_encoding.go
@@ -7,11 +7,11 @@ import (
 
 	"github.com/celestiaorg/celestia-app/pkg/appconsts"
 	"github.com/celestiaorg/celestia-app/pkg/wrapper"
+	libhead "github.com/celestiaorg/go-header"
 	"github.com/celestiaorg/rsmt2d"
 
 	"github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/libs/fraud"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	pb "github.com/celestiaorg/celestia-node/share/eds/byzantine/pb"
 	"github.com/celestiaorg/celestia-node/share/ipld"
 )
diff --git a/share/p2p/peers/manager.go b/share/p2p/peers/manager.go
index 1956704e..f152b7fb 100644
--- a/share/p2p/peers/manager.go
+++ b/share/p2p/peers/manager.go
@@ -15,8 +15,9 @@ import (
 	"github.com/libp2p/go-libp2p/core/peer"
 	"github.com/libp2p/go-libp2p/p2p/net/conngater"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share"
 	"github.com/celestiaorg/celestia-node/share/availability/discovery"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"
diff --git a/state/core_access.go b/state/core_access.go
index 596c3ab7..1339c530 100644
--- a/state/core_access.go
+++ b/state/core_access.go
@@ -23,10 +23,10 @@ import (
 	"github.com/celestiaorg/celestia-app/pkg/appconsts"
 	"github.com/celestiaorg/celestia-app/x/blob"
 	apptypes "github.com/celestiaorg/celestia-app/x/blob/types"
+	libhead "github.com/celestiaorg/go-header"
 	"github.com/celestiaorg/nmt/namespace"
 
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 )
 
 var (


Test patch:
diff --git a/core/listener_test.go b/core/listener_test.go
index c0e62451..05f338f0 100644
--- a/core/listener_test.go
+++ b/core/listener_test.go
@@ -13,8 +13,9 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
+	"github.com/celestiaorg/go-header/p2p"
+
 	"github.com/celestiaorg/celestia-node/header"
-	"github.com/celestiaorg/celestia-node/libs/header/p2p"
 	"github.com/celestiaorg/celestia-node/share/eds"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"
 )
diff --git a/das/daser_test.go b/das/daser_test.go
index bf4dd34a..c85e656e 100644
--- a/das/daser_test.go
+++ b/das/daser_test.go
@@ -16,10 +16,11 @@ import (
 	"github.com/stretchr/testify/require"
 	"github.com/tendermint/tendermint/types"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/header/headertest"
 	"github.com/celestiaorg/celestia-node/libs/fraud"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share"
 	"github.com/celestiaorg/celestia-node/share/availability/full"
 	"github.com/celestiaorg/celestia-node/share/availability/light"
diff --git a/header/headertest/testing.go b/header/headertest/testing.go
index f446122e..9e2cbe50 100644
--- a/header/headertest/testing.go
+++ b/header/headertest/testing.go
@@ -22,11 +22,11 @@ import (
 	"golang.org/x/exp/rand"
 
 	"github.com/celestiaorg/celestia-app/pkg/da"
+	libhead "github.com/celestiaorg/go-header"
+	"github.com/celestiaorg/go-header/test"
 	"github.com/celestiaorg/rsmt2d"
 
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
 	"github.com/celestiaorg/celestia-node/share"
 )
 
diff --git a/header/headertest/verify_test.go b/header/headertest/verify_test.go
index 4dce036b..78da779a 100644
--- a/header/headertest/verify_test.go
+++ b/header/headertest/verify_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	tmrand "github.com/tendermint/tendermint/libs/rand"
 
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
+	libhead "github.com/celestiaorg/go-header"
 )
 
 func TestVerify(t *testing.T) {
diff --git a/libs/fraud/service_test.go b/libs/fraud/service_test.go
index 245eeaa5..2237454d 100644
--- a/libs/fraud/service_test.go
+++ b/libs/fraud/service_test.go
@@ -15,8 +15,8 @@ import (
 	mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
 	"github.com/stretchr/testify/require"
 
-	"github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
+	"github.com/celestiaorg/go-header"
+	"github.com/celestiaorg/go-header/test"
 )
 
 func TestService_Subscribe(t *testing.T) {
diff --git a/libs/fraud/testing.go b/libs/fraud/testing.go
index 24cfca0c..9e027dd4 100644
--- a/libs/fraud/testing.go
+++ b/libs/fraud/testing.go
@@ -14,8 +14,9 @@ import (
 	mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
 	"github.com/stretchr/testify/require"
 
+	"github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header/headertest"
-	"github.com/celestiaorg/celestia-node/libs/header"
 )
 
 type DummyService struct {
diff --git a/libs/header/p2p/exchange_test.go b/libs/header/p2p/exchange_test.go
deleted file mode 100644
index a6e32480..00000000
--- a/libs/header/p2p/exchange_test.go
+++ /dev/null
@@ -1,461 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"github.com/ipfs/go-datastore"
-	"github.com/ipfs/go-datastore/sync"
-	libhost "github.com/libp2p/go-libp2p/core/host"
-	"github.com/libp2p/go-libp2p/core/network"
-	"github.com/libp2p/go-libp2p/core/peer"
-	blankhost "github.com/libp2p/go-libp2p/p2p/host/blank"
-	"github.com/libp2p/go-libp2p/p2p/net/conngater"
-	mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
-	swarm "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/celestiaorg/go-libp2p-messenger/serde"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	headerMock "github.com/celestiaorg/celestia-node/libs/header/mocks"
-	p2p_pb "github.com/celestiaorg/celestia-node/libs/header/p2p/pb"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-const networkID = "private"
-
-func TestExchange_RequestHead(t *testing.T) {
-	hosts := createMocknet(t, 2)
-	exchg, store := createP2PExAndServer(t, hosts[0], hosts[1])
-	// perform header request
-	header, err := exchg.Head(context.Background())
-	require.NoError(t, err)
-
-	assert.Equal(t, store.Headers[store.HeadHeight].Height(), header.Height())
-	assert.Equal(t, store.Headers[store.HeadHeight].Hash(), header.Hash())
-}
-
-func TestExchange_RequestHeader(t *testing.T) {
-	hosts := createMocknet(t, 2)
-	exchg, store := createP2PExAndServer(t, hosts[0], hosts[1])
-	// perform expected request
-	header, err := exchg.GetByHeight(context.Background(), 5)
-	require.NoError(t, err)
-	assert.Equal(t, store.Headers[5].Height(), header.Height())
-	assert.Equal(t, store.Headers[5].Hash(), header.Hash())
-}
-
-func TestExchange_RequestHeaders(t *testing.T) {
-	hosts := createMocknet(t, 2)
-	exchg, store := createP2PExAndServer(t, hosts[0], hosts[1])
-	// perform expected request
-	gotHeaders, err := exchg.GetRangeByHeight(context.Background(), 1, 5)
-	require.NoError(t, err)
-	for _, got := range gotHeaders {
-		assert.Equal(t, store.Headers[got.Height()].Height(), got.Height())
-		assert.Equal(t, store.Headers[got.Height()].Hash(), got.Hash())
-	}
-}
-
-func TestExchange_RequestVerifiedHeaders(t *testing.T) {
-	hosts := createMocknet(t, 2)
-	exchg, store := createP2PExAndServer(t, hosts[0], hosts[1])
-	// perform expected request
-	h := store.Headers[1]
-	_, err := exchg.GetVerifiedRange(context.Background(), h, 3)
-	require.NoError(t, err)
-}
-
-func TestExchange_RequestVerifiedHeadersFails(t *testing.T) {
-	hosts := createMocknet(t, 2)
-	exchg, store := createP2PExAndServer(t, hosts[0], hosts[1])
-	store.Headers[2] = store.Headers[3]
-	// perform expected request
-	h := store.Headers[1]
-	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
-	t.Cleanup(cancel)
-	_, err := exchg.GetVerifiedRange(ctx, h, 3)
-	assert.Error(t, err)
-
-	// ensure that peer was added to the blacklist
-	peers := exchg.peerTracker.connGater.ListBlockedPeers()
-	require.Len(t, peers, 1)
-	require.True(t, hosts[1].ID() == peers[0])
-}
-
-// TestExchange_RequestFullRangeHeaders requests max amount of headers
-// to verify how session will parallelize all requests.
-func TestExchange_RequestFullRangeHeaders(t *testing.T) {
-	// create mocknet with 5 peers
-	hosts := createMocknet(t, 5)
-	store := headerMock.NewStore[*test.DummyHeader](t, test.NewTestSuite(t), int(header.MaxRangeRequestSize))
-	connGater, err := conngater.NewBasicConnectionGater(sync.MutexWrap(datastore.NewMapDatastore()))
-	require.NoError(t, err)
-	// create new exchange
-	exchange, err := NewExchange[*test.DummyHeader](hosts[len(hosts)-1], []peer.ID{hosts[4].ID()}, connGater,
-		WithNetworkID[ClientParameters](networkID),
-		WithChainID(networkID),
-	)
-	require.NoError(t, err)
-	exchange.ctx, exchange.cancel = context.WithCancel(context.Background())
-	t.Cleanup(exchange.cancel)
-	// amount of servers is len(hosts)-1 because one peer acts as a client
-	servers := make([]*ExchangeServer[*test.DummyHeader], len(hosts)-1)
-	for index := range servers {
-		servers[index], err = NewExchangeServer[*test.DummyHeader](
-			hosts[index],
-			store,
-			WithNetworkID[ServerParameters](networkID),
-		)
-		require.NoError(t, err)
-		servers[index].Start(context.Background()) //nolint:errcheck
-		exchange.peerTracker.peerLk.Lock()
-		exchange.peerTracker.trackedPeers[hosts[index].ID()] = &peerStat{peerID: hosts[index].ID()}
-		exchange.peerTracker.peerLk.Unlock()
-	}
-
-	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
-	t.Cleanup(cancel)
-	// request headers from 1 to totalAmount(80)
-	headers, err := exchange.GetRangeByHeight(ctx, 1, header.MaxRangeRequestSize)
-	require.NoError(t, err)
-	require.Len(t, headers, int(header.MaxRangeRequestSize))
-}
-
-// TestExchange_RequestHeadersLimitExceeded tests that the Exchange instance will return
-// header.ErrHeadersLimitExceeded if the requested range will be move than MaxRangeRequestSize.
-func TestExchange_RequestHeadersLimitExceeded(t *testing.T) {
-	hosts := createMocknet(t, 2)
-	exchg, _ := createP2PExAndServer(t, hosts[0], hosts[1])
-	_, err := exchg.GetRangeByHeight(context.Background(), 1, 600)
-	require.Error(t, err)
-	require.ErrorAs(t, err, &header.ErrHeadersLimitExceeded)
-}
-
-// TestExchange_RequestHeadersFromAnotherPeer tests that the Exchange instance will request range
-// from another peer with lower score after receiving header.ErrNotFound
-func TestExchange_RequestHeadersFromAnotherPeer(t *testing.T) {
-	hosts := createMocknet(t, 3)
-	// create client + server(it does not have needed headers)
-	exchg, _ := createP2PExAndServer(t, hosts[0], hosts[1])
-	// create one more server(with more headers in the store)
-	serverSideEx, err := NewExchangeServer[*test.DummyHeader](
-		hosts[2], headerMock.NewStore[*test.DummyHeader](t, test.NewTestSuite(t), 10),
-		WithNetworkID[ServerParameters](networkID),
-	)
-	require.NoError(t, err)
-	require.NoError(t, serverSideEx.Start(context.Background()))
-	t.Cleanup(func() {
-		serverSideEx.Stop(context.Background()) //nolint:errcheck
-	})
-	exchg.peerTracker.peerLk.Lock()
-	exchg.peerTracker.trackedPeers[hosts[2].ID()] = &peerStat{peerID: hosts[2].ID(), peerScore: 20}
-	exchg.peerTracker.peerLk.Unlock()
-	_, err = exchg.GetRangeByHeight(context.Background(), 5, 3)
-	require.NoError(t, err)
-	// ensure that peerScore for the second peer is changed
-	newPeerScore := exchg.peerTracker.trackedPeers[hosts[2].ID()].score()
-	require.NotEqual(t, 20, newPeerScore)
-}
-
-// TestExchange_RequestByHash tests that the Exchange instance can
-// respond to an HeaderRequest for a hash instead of a height.
-func TestExchange_RequestByHash(t *testing.T) {
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-
-	net, err := mocknet.FullMeshConnected(2)
-	require.NoError(t, err)
-	// get host and peer
-	host, peer := net.Hosts()[0], net.Hosts()[1]
-	// create and start the ExchangeServer
-	store := headerMock.NewStore[*test.DummyHeader](t, test.NewTestSuite(t), 5)
-	serv, err := NewExchangeServer[*test.DummyHeader](
-		host,
-		store,
-		WithNetworkID[ServerParameters](networkID),
-	)
-	require.NoError(t, err)
-	err = serv.Start(ctx)
-	require.NoError(t, err)
-	t.Cleanup(func() {
-		serv.Stop(context.Background()) //nolint:errcheck
-	})
-
-	// start a new stream via Peer to see if Host can handle inbound requests
-	stream, err := peer.NewStream(context.Background(), libhost.InfoFromHost(host).ID, protocolID(networkID))
-	require.NoError(t, err)
-	// create request for a header at a random height
-	reqHeight := store.HeadHeight - 2
-	req := &p2p_pb.HeaderRequest{
-		Data:   &p2p_pb.HeaderRequest_Hash{Hash: store.Headers[reqHeight].Hash()},
-		Amount: 1,
-	}
-	// send request
-	_, err = serde.Write(stream, req)
-	require.NoError(t, err)
-	// read resp
-	resp := new(p2p_pb.HeaderResponse)
-	_, err = serde.Read(stream, resp)
-	require.NoError(t, err)
-	// compare
-	var eh test.DummyHeader
-	err = eh.UnmarshalBinary(resp.Body)
-	require.NoError(t, err)
-
-	assert.Equal(t, store.Headers[reqHeight].Height(), eh.Height())
-	assert.Equal(t, store.Headers[reqHeight].Hash(), eh.Hash())
-}
-
-func Test_bestHead(t *testing.T) {
-	gen := func() []*test.DummyHeader {
-		suite := test.NewTestSuite(t)
-		res := make([]*test.DummyHeader, 0)
-		for i := 0; i < 3; i++ {
-			res = append(res, suite.GetRandomHeader())
-		}
-		return res
-	}
-	testCases := []struct {
-		precondition   func() []*test.DummyHeader
-		expectedHeight int64
-	}{
-		/*
-			Height -> Amount
-			headerHeight[0]=1 -> 1
-			headerHeight[1]=2 -> 1
-			headerHeight[2]=3 -> 1
-			result -> headerHeight[2]
-		*/
-		{
-			precondition:   gen,
-			expectedHeight: 3,
-		},
-		/*
-			Height -> Amount
-			headerHeight[0]=1 -> 2
-			headerHeight[1]=2 -> 1
-			headerHeight[2]=3 -> 1
-			result -> headerHeight[0]
-		*/
-		{
-			precondition: func() []*test.DummyHeader {
-				res := gen()
-				res = append(res, res[0])
-				return res
-			},
-			expectedHeight: 1,
-		},
-		/*
-			Height -> Amount
-			headerHeight[0]=1 -> 3
-			headerHeight[1]=2 -> 2
-			headerHeight[2]=3 -> 1
-			result -> headerHeight[1]
-		*/
-		{
-			precondition: func() []*test.DummyHeader {
-				res := gen()
-				res = append(res, res[0])
-				res = append(res, res[0])
-				res = append(res, res[1])
-				return res
-			},
-			expectedHeight: 2,
-		},
-	}
-	for _, tt := range testCases {
-		res := tt.precondition()
-		header, err := bestHead(res)
-		require.NoError(t, err)
-		require.True(t, header.Height() == tt.expectedHeight)
-	}
-}
-
-// TestExchange_RequestByHashFails tests that the Exchange instance can
-// respond with a StatusCode_NOT_FOUND if it will not have requested header.
-func TestExchange_RequestByHashFails(t *testing.T) {
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-
-	net, err := mocknet.FullMeshConnected(2)
-	require.NoError(t, err)
-	// get host and peer
-	host, peer := net.Hosts()[0], net.Hosts()[1]
-	serv, err := NewExchangeServer[*test.DummyHeader](
-		host, headerMock.NewStore[*test.DummyHeader](t, test.NewTestSuite(t), 0),
-		WithNetworkID[ServerParameters](networkID),
-	)
-	require.NoError(t, err)
-	err = serv.Start(ctx)
-	require.NoError(t, err)
-	t.Cleanup(func() {
-		serv.Stop(context.Background()) //nolint:errcheck
-	})
-
-	stream, err := peer.NewStream(context.Background(), libhost.InfoFromHost(host).ID, protocolID(networkID))
-	require.NoError(t, err)
-	req := &p2p_pb.HeaderRequest{
-		Data:   &p2p_pb.HeaderRequest_Hash{Hash: []byte("dummy_hash")},
-		Amount: 1,
-	}
-	// send request
-	_, err = serde.Write(stream, req)
-	require.NoError(t, err)
-	// read resp
-	resp := new(p2p_pb.HeaderResponse)
-	_, err = serde.Read(stream, resp)
-	require.NoError(t, err)
-	require.Equal(t, resp.StatusCode, p2p_pb.StatusCode_NOT_FOUND)
-}
-
-// TestExchange_HandleHeaderWithDifferentChainID ensures that headers with different
-// chainIDs will not be served and stored.
-func TestExchange_HandleHeaderWithDifferentChainID(t *testing.T) {
-	hosts := createMocknet(t, 2)
-	exchg, store := createP2PExAndServer(t, hosts[0], hosts[1])
-	exchg.Params.chainID = "test"
-
-	_, err := exchg.Head(context.Background())
-	require.Error(t, err)
-
-	_, err = exchg.GetByHeight(context.Background(), 1)
-	require.Error(t, err)
-
-	h, err := store.GetByHeight(context.Background(), 1)
-	require.NoError(t, err)
-	_, err = exchg.Get(context.Background(), h.Hash())
-	require.Error(t, err)
-}
-
-// TestExchange_RequestHeadersFromAnotherPeer tests that the Exchange instance will request range
-// from another peer with lower score after receiving header.ErrNotFound
-func TestExchange_RequestHeadersFromAnotherPeerWhenTimeout(t *testing.T) {
-	// create blankhost because mocknet does not support deadlines
-	swarm0 := swarm.GenSwarm(t)
-	host0 := blankhost.NewBlankHost(swarm0)
-	swarm1 := swarm.GenSwarm(t)
-	host1 := blankhost.NewBlankHost(swarm1)
-	swarm2 := swarm.GenSwarm(t)
-	host2 := blankhost.NewBlankHost(swarm2)
-	dial := func(a, b network.Network) {
-		swarm.DivulgeAddresses(b, a)
-		if _, err := a.DialPeer(context.Background(), b.LocalPeer()); err != nil {
-			t.Fatalf("Failed to dial: %s", err)
-		}
-	}
-	// dial peers
-	dial(swarm0, swarm1)
-	dial(swarm0, swarm2)
-	dial(swarm1, swarm2)
-
-	// create client + server(it does not have needed headers)
-	exchg, _ := createP2PExAndServer(t, host0, host1)
-	exchg.Params.RangeRequestTimeout = time.Millisecond * 100
-	// create one more server(with more headers in the store)
-	serverSideEx, err := NewExchangeServer[*test.DummyHeader](
-		host2, headerMock.NewStore[*test.DummyHeader](t, test.NewTestSuite(t), 10),
-		WithNetworkID[ServerParameters](networkID),
-	)
-	require.NoError(t, err)
-	// change store implementation
-	serverSideEx.store = &timedOutStore{timeout: exchg.Params.RangeRequestTimeout}
-	require.NoError(t, serverSideEx.Start(context.Background()))
-	t.Cleanup(func() {
-		serverSideEx.Stop(context.Background()) //nolint:errcheck
-	})
-	prevScore := exchg.peerTracker.trackedPeers[host1.ID()].score()
-	exchg.peerTracker.peerLk.Lock()
-	exchg.peerTracker.trackedPeers[host2.ID()] = &peerStat{peerID: host2.ID(), peerScore: 200}
-	exchg.peerTracker.peerLk.Unlock()
-	_, err = exchg.GetRangeByHeight(context.Background(), 1, 3)
-	require.NoError(t, err)
-	newPeerScore := exchg.peerTracker.trackedPeers[host1.ID()].score()
-	assert.NotEqual(t, newPeerScore, prevScore)
-}
-
-// TestExchange_RequestPartialRange enusres in case of receiving a partial response
-// from server, Exchange will re-request remaining headers from another peer
-func TestExchange_RequestPartialRange(t *testing.T) {
-	hosts := createMocknet(t, 3)
-	exchg, _ := createP2PExAndServer(t, hosts[0], hosts[1])
-
-	// create one more server(with more headers in the store)
-	serverSideEx, err := NewExchangeServer[*test.DummyHeader](
-		hosts[2], headerMock.NewStore[*test.DummyHeader](t, test.NewTestSuite(t), 10),
-		WithNetworkID[ServerParameters](networkID),
-	)
-	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
-	t.Cleanup(cancel)
-
-	require.NoError(t, err)
-	require.NoError(t, serverSideEx.Start(ctx))
-	exchg.peerTracker.peerLk.Lock()
-	prevScoreBefore1 := exchg.peerTracker.trackedPeers[hosts[1].ID()].peerScore
-	prevScoreBefore2 := 50
-	// reducing peerScore of the second server, so our exchange will request host[1] first.
-	exchg.peerTracker.trackedPeers[hosts[2].ID()] = &peerStat{peerID: hosts[2].ID(), peerScore: 50}
-	exchg.peerTracker.peerLk.Unlock()
-	h, err := exchg.GetRangeByHeight(ctx, 1, 8)
-	require.NotNil(t, h)
-	require.NoError(t, err)
-
-	exchg.peerTracker.peerLk.Lock()
-	prevScoreAfter1 := exchg.peerTracker.trackedPeers[hosts[1].ID()].peerScore
-	prevScoreAfter2 := exchg.peerTracker.trackedPeers[hosts[2].ID()].peerScore
-	exchg.peerTracker.peerLk.Unlock()
-
-	assert.NotEqual(t, prevScoreBefore1, prevScoreAfter1)
-	assert.NotEqual(t, prevScoreBefore2, prevScoreAfter2)
-}
-
-func createMocknet(t *testing.T, amount int) []libhost.Host {
-	net, err := mocknet.FullMeshConnected(amount)
-	require.NoError(t, err)
-	// get host and peer
-	return net.Hosts()
-}
-
-// createP2PExAndServer creates a Exchange with 5 headers already in its store.
-func createP2PExAndServer(
-	t *testing.T,
-	host, tpeer libhost.Host,
-) (*Exchange[*test.DummyHeader], *headerMock.MockStore[*test.DummyHeader]) {
-	store := headerMock.NewStore[*test.DummyHeader](t, test.NewTestSuite(t), 5)
-	serverSideEx, err := NewExchangeServer[*test.DummyHeader](tpeer, store,
-		WithNetworkID[ServerParameters](networkID),
-	)
-	require.NoError(t, err)
-	err = serverSideEx.Start(context.Background())
-	require.NoError(t, err)
-	connGater, err := conngater.NewBasicConnectionGater(sync.MutexWrap(datastore.NewMapDatastore()))
-	require.NoError(t, err)
-	ex, err := NewExchange[*test.DummyHeader](host, []peer.ID{tpeer.ID()}, connGater,
-		WithNetworkID[ClientParameters](networkID),
-		WithChainID(networkID),
-	)
-	require.NoError(t, err)
-	require.NoError(t, ex.Start(context.Background()))
-	time.Sleep(time.Millisecond * 100) // give peerTracker time to add a trusted peer
-	ex.peerTracker.peerLk.Lock()
-	ex.peerTracker.trackedPeers[tpeer.ID()] = &peerStat{peerID: tpeer.ID(), peerScore: 100.0}
-	ex.peerTracker.peerLk.Unlock()
-	t.Cleanup(func() {
-		serverSideEx.Stop(context.Background()) //nolint:errcheck
-		ex.Stop(context.Background())           //nolint:errcheck
-	})
-	return ex, store
-}
-
-type timedOutStore struct {
-	headerMock.MockStore[*test.DummyHeader]
-	timeout time.Duration
-}
-
-func (t *timedOutStore) HasAt(_ context.Context, _ uint64) bool {
-	time.Sleep(t.timeout + 1)
-	return true
-}
diff --git a/libs/header/p2p/peer_stats_test.go b/libs/header/p2p/peer_stats_test.go
deleted file mode 100644
index d5a875d1..00000000
--- a/libs/header/p2p/peer_stats_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package p2p
-
-import (
-	"container/heap"
-	"context"
-	"testing"
-
-	"github.com/libp2p/go-libp2p/core/peer"
-	"github.com/stretchr/testify/require"
-)
-
-func Test_PeerStatsPush(t *testing.T) {
-	pQueue := newPeerStats()
-	pQueue.Push(&peerStat{peerID: "peerID"})
-	require.True(t, pQueue.Len() == 1)
-}
-
-func Test_PeerStatsPop(t *testing.T) {
-	pQueue := newPeerStats()
-	pQueue.Push(&peerStat{peerID: "peerID"})
-	stats := heap.Pop(&pQueue).(*peerStat)
-	require.Equal(t, stats.peerID, peer.ID("peerID"))
-}
-
-func Test_PeerQueuePopBestPeer(t *testing.T) {
-	peersStat := peerStats{
-		{peerID: "peerID1", peerScore: 1},
-		{peerID: "peerID2", peerScore: 2},
-		{peerID: "peerID3", peerScore: 4},
-		{peerID: "peerID4"}, // score = 0
-	}
-	wantStat := peerStats{
-		{peerID: "peerID3", peerScore: 4},
-		{peerID: "peerID2", peerScore: 2},
-		{peerID: "peerID1", peerScore: 1},
-		{peerID: "peerID4"}, // score = 0
-	}
-
-	// we do not need timeout/cancel functionality here
-	pQueue := newPeerQueue(context.Background(), peersStat)
-	for index := 0; index < pQueue.stats.Len(); index++ {
-		stats := heap.Pop(&pQueue.stats).(*peerStat)
-		require.Equal(t, stats, wantStat[index])
-	}
-}
-
-func Test_PeerQueueRemovePeer(t *testing.T) {
-	peersStat := []*peerStat{
-		{peerID: "peerID1", peerScore: 1},
-		{peerID: "peerID2", peerScore: 2},
-		{peerID: "peerID3", peerScore: 4},
-		{peerID: "peerID4"}, // score = 0
-	}
-
-	// we do not need timeout/cancel functionality here
-	pQueue := newPeerQueue(context.Background(), peersStat)
-
-	_ = heap.Pop(&pQueue.stats)
-	stat := heap.Pop(&pQueue.stats).(*peerStat)
-	require.Equal(t, stat.peerID, peer.ID("peerID2"))
-}
-
-func Test_StatsUpdateStats(t *testing.T) {
-	// we do not need timeout/cancel functionality here
-	pQueue := newPeerQueue(context.Background(), []*peerStat{})
-	stat := &peerStat{peerID: "peerID", peerScore: 0}
-	heap.Push(&pQueue.stats, stat)
-	testCases := []struct {
-		inputTime   uint64
-		inputBytes  uint64
-		resultScore float32
-	}{
-		// common case, where time and bytes is not equal to 0
-		{
-			inputTime:   16,
-			inputBytes:  4,
-			resultScore: 4,
-		},
-		// in case if bytes is equal to 0,
-		// then the request was failed and previous score will be
-		// decreased
-		{
-			inputTime:   10,
-			inputBytes:  0,
-			resultScore: 2,
-		},
-		// testing case with time=0, to ensure that dividing by 0 is handled properly
-		{
-			inputTime:   0,
-			inputBytes:  0,
-			resultScore: 1,
-		},
-	}
-
-	for _, tt := range testCases {
-		stat.updateStats(tt.inputBytes, tt.inputTime)
-		updatedStat := heap.Pop(&pQueue.stats).(*peerStat)
-		require.Equal(t, updatedStat.score(), stat.score())
-		heap.Push(&pQueue.stats, updatedStat)
-	}
-}
-
-func Test_StatDecreaseScore(t *testing.T) {
-	pStats := &peerStat{
-		peerID:    peer.ID("test"),
-		peerScore: 100,
-	}
-	// will decrease score by 20%
-	pStats.decreaseScore()
-	require.Equal(t, pStats.score(), float32(80.0))
-}
diff --git a/libs/header/p2p/peer_tracker_test.go b/libs/header/p2p/peer_tracker_test.go
deleted file mode 100644
index 2645a241..00000000
--- a/libs/header/p2p/peer_tracker_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package p2p
-
-import (
-	"errors"
-	"testing"
-	"time"
-
-	"github.com/ipfs/go-datastore"
-	"github.com/ipfs/go-datastore/sync"
-	"github.com/libp2p/go-libp2p/core/peer"
-	"github.com/libp2p/go-libp2p/p2p/net/conngater"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func TestPeerTracker_GC(t *testing.T) {
-	h := createMocknet(t, 1)
-	gcCycle = time.Millisecond * 200
-	connGater, err := conngater.NewBasicConnectionGater(sync.MutexWrap(datastore.NewMapDatastore()))
-	require.NoError(t, err)
-	p := newPeerTracker(h[0], connGater)
-	maxAwaitingTime = time.Millisecond
-	pid1 := peer.ID("peer1")
-	pid2 := peer.ID("peer2")
-	pid3 := peer.ID("peer3")
-	pid4 := peer.ID("peer4")
-	p.trackedPeers[pid1] = &peerStat{peerID: pid1, peerScore: 0.5}
-	p.trackedPeers[pid2] = &peerStat{peerID: pid2, peerScore: 10}
-	p.disconnectedPeers[pid3] = &peerStat{peerID: pid3, pruneDeadline: time.Now()}
-	p.disconnectedPeers[pid4] = &peerStat{peerID: pid4, pruneDeadline: time.Now().Add(time.Minute * 10)}
-	assert.True(t, len(p.trackedPeers) > 0)
-	assert.True(t, len(p.disconnectedPeers) > 0)
-
-	go p.track()
-	go p.gc()
-	time.Sleep(time.Second * 1)
-	p.stop()
-	require.Nil(t, p.trackedPeers[pid1])
-	require.Nil(t, p.disconnectedPeers[pid3])
-}
-
-func TestPeerTracker_BlockPeer(t *testing.T) {
-	h := createMocknet(t, 2)
-	connGater, err := conngater.NewBasicConnectionGater(sync.MutexWrap(datastore.NewMapDatastore()))
-	require.NoError(t, err)
-	p := newPeerTracker(h[0], connGater)
-	maxAwaitingTime = time.Millisecond
-	p.blockPeer(h[1].ID(), errors.New("test"))
-	require.Len(t, connGater.ListBlockedPeers(), 1)
-	require.True(t, connGater.ListBlockedPeers()[0] == h[1].ID())
-}
diff --git a/libs/header/p2p/server_test.go b/libs/header/p2p/server_test.go
deleted file mode 100644
index cb123b36..00000000
--- a/libs/header/p2p/server_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"testing"
-
-	"github.com/ipfs/go-datastore"
-	"github.com/stretchr/testify/require"
-
-	"github.com/celestiaorg/celestia-node/libs/header/store"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func TestExchangeServer_handleRequestTimeout(t *testing.T) {
-	peer := createMocknet(t, 1)
-	s, err := store.NewStore[*test.DummyHeader](datastore.NewMapDatastore())
-	require.NoError(t, err)
-	server, err := NewExchangeServer[*test.DummyHeader](
-		peer[0],
-		s,
-		WithNetworkID[ServerParameters](networkID),
-	)
-	require.NoError(t, err)
-	err = server.Start(context.Background())
-	require.NoError(t, err)
-	t.Cleanup(func() {
-		server.Stop(context.Background()) //nolint:errcheck
-	})
-
-	_, err = server.handleRequest(1, 200)
-	require.Error(t, err)
-}
diff --git a/libs/header/p2p/session_test.go b/libs/header/p2p/session_test.go
deleted file mode 100644
index 9cd2df31..00000000
--- a/libs/header/p2p/session_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"github.com/libp2p/go-libp2p/core/peer"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func Test_PrepareRequests(t *testing.T) {
-	// from : 1, amount: 10, headersPerPeer: 5
-	// result -> {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}}
-	requests := prepareRequests(1, 10, 5)
-	require.Len(t, requests, 2)
-	require.Equal(t, requests[0].GetOrigin(), uint64(1))
-	require.Equal(t, requests[1].GetOrigin(), uint64(6))
-}
-
-// Test_Validate ensures that headers range is adjacent and valid.
-func Test_Validate(t *testing.T) {
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-	ses := newSession(
-		context.Background(),
-		nil,
-		&peerTracker{trackedPeers: make(map[peer.ID]*peerStat)},
-		"", time.Second,
-		withValidation(head),
-	)
-
-	headers := suite.GenDummyHeaders(5)
-	err := ses.validate(headers)
-	assert.NoError(t, err)
-}
-
-// Test_ValidateFails ensures that non-adjacent range will return an error.
-func Test_ValidateFails(t *testing.T) {
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-	ses := newSession(
-		context.Background(),
-		nil,
-		&peerTracker{trackedPeers: make(map[peer.ID]*peerStat)},
-		"", time.Second,
-		withValidation(head),
-	)
-
-	headers := suite.GenDummyHeaders(5)
-	// break adjacency
-	headers[2] = headers[4]
-	err := ses.validate(headers)
-	assert.Error(t, err)
-}
diff --git a/libs/header/p2p/subscription_test.go b/libs/header/p2p/subscription_test.go
deleted file mode 100644
index c95fe7bf..00000000
--- a/libs/header/p2p/subscription_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package p2p
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	pubsub "github.com/libp2p/go-libp2p-pubsub"
-	"github.com/libp2p/go-libp2p/core/event"
-	mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-// TestSubscriber tests the header Module's implementation of Subscriber.
-func TestSubscriber(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
-	defer cancel()
-
-	// create mock network
-	net, err := mocknet.FullMeshLinked(2)
-	require.NoError(t, err)
-
-	suite := test.NewTestSuite(t)
-
-	// get mock host and create new gossipsub on it
-	pubsub1, err := pubsub.NewGossipSub(ctx, net.Hosts()[0], pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign))
-	require.NoError(t, err)
-
-	// create sub-service lifecycles for header service 1
-	p2pSub1 := NewSubscriber[*test.DummyHeader](pubsub1, pubsub.DefaultMsgIdFn, networkID)
-	err = p2pSub1.Start(context.Background())
-	require.NoError(t, err)
-
-	// get mock host and create new gossipsub on it
-	pubsub2, err := pubsub.NewGossipSub(ctx, net.Hosts()[1],
-		pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign))
-	require.NoError(t, err)
-
-	// create sub-service lifecycles for header service 2
-	p2pSub2 := NewSubscriber[*test.DummyHeader](pubsub2, pubsub.DefaultMsgIdFn, networkID)
-	err = p2pSub2.Start(context.Background())
-	require.NoError(t, err)
-
-	sub0, err := net.Hosts()[0].EventBus().Subscribe(&event.EvtPeerIdentificationCompleted{})
-	require.NoError(t, err)
-	sub1, err := net.Hosts()[1].EventBus().Subscribe(&event.EvtPeerIdentificationCompleted{})
-	require.NoError(t, err)
-
-	err = net.ConnectAllButSelf()
-	require.NoError(t, err)
-
-	// wait on both peer identification events
-	for i := 0; i < 2; i++ {
-		select {
-		case <-sub0.Out():
-		case <-sub1.Out():
-		case <-ctx.Done():
-			assert.FailNow(t, "timeout waiting for peers to connect")
-		}
-	}
-
-	// subscribe
-	_, err = p2pSub2.Subscribe()
-	require.NoError(t, err)
-
-	p2pSub1.AddValidator(func(context.Context, *test.DummyHeader) pubsub.ValidationResult { //nolint:errcheck
-		return pubsub.ValidationAccept
-	})
-	subscription, err := p2pSub1.Subscribe()
-	require.NoError(t, err)
-
-	expectedHeader := suite.GenDummyHeaders(1)[0]
-	bin, err := expectedHeader.MarshalBinary()
-	require.NoError(t, err)
-
-	err = p2pSub2.topic.Publish(ctx, bin, pubsub.WithReadiness(pubsub.MinTopicSize(1)))
-	require.NoError(t, err)
-
-	// get next Header from network
-	header, err := subscription.NextHeader(ctx)
-	require.NoError(t, err)
-
-	assert.Equal(t, expectedHeader.Height(), header.Height())
-	assert.Equal(t, expectedHeader.Hash(), header.Hash())
-}
diff --git a/libs/header/store/heightsub_test.go b/libs/header/store/heightsub_test.go
deleted file mode 100644
index 990f4714..00000000
--- a/libs/header/store/heightsub_test.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package store
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func TestHeightSub(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	defer cancel()
-
-	hs := newHeightSub[*test.DummyHeader]()
-
-	// assert subscription returns nil for past heights
-	{
-		h := test.RandDummyHeader(t)
-		h.Raw.Height = 100
-		hs.SetHeight(99)
-		hs.Pub(h)
-
-		h, err := hs.Sub(ctx, 10)
-		assert.ErrorIs(t, err, errElapsedHeight)
-		assert.Nil(t, h)
-	}
-
-	// assert actual subscription works
-	{
-		go func() {
-			// fixes flakiness on CI
-			time.Sleep(time.Millisecond)
-
-			h1 := test.RandDummyHeader(t)
-			h1.Raw.Height = 101
-			h2 := test.RandDummyHeader(t)
-			h2.Raw.Height = 102
-			hs.Pub(h1, h2)
-		}()
-
-		h, err := hs.Sub(ctx, 101)
-		assert.NoError(t, err)
-		assert.NotNil(t, h)
-	}
-}
diff --git a/libs/header/store/init_test.go b/libs/header/store/init_test.go
deleted file mode 100644
index db800206..00000000
--- a/libs/header/store/init_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package store
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"github.com/ipfs/go-datastore"
-	"github.com/ipfs/go-datastore/sync"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/celestiaorg/celestia-node/libs/header/local"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func TestInitStore_NoReinit(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-	exchange := local.NewExchange(NewTestStore(ctx, t, head))
-
-	ds := sync.MutexWrap(datastore.NewMapDatastore())
-	store, err := NewStore[*test.DummyHeader](ds)
-	require.NoError(t, err)
-
-	err = Init[*test.DummyHeader](ctx, store, exchange, head.Hash())
-	assert.NoError(t, err)
-
-	err = store.Start(ctx)
-	require.NoError(t, err)
-
-	err = store.Append(ctx, suite.GenDummyHeaders(10)...)
-	require.NoError(t, err)
-
-	err = store.Stop(ctx)
-	require.NoError(t, err)
-
-	reopenedStore, err := NewStore[*test.DummyHeader](ds)
-	assert.NoError(t, err)
-
-	err = reopenedStore.Start(ctx)
-	require.NoError(t, err)
-
-	reopenedHead, err := reopenedStore.Head(ctx)
-	require.NoError(t, err)
-
-	// check that reopened head changed and the store wasn't reinitialized
-	assert.Equal(t, suite.Head().Height(), reopenedHead.Height())
-	assert.NotEqual(t, head.Height(), reopenedHead.Height())
-
-	err = reopenedStore.Stop(ctx)
-	require.NoError(t, err)
-}
diff --git a/libs/header/store/store_test.go b/libs/header/store/store_test.go
deleted file mode 100644
index 89388637..00000000
--- a/libs/header/store/store_test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package store
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"github.com/ipfs/go-datastore"
-	"github.com/ipfs/go-datastore/sync"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func TestStore(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-
-	ds := sync.MutexWrap(datastore.NewMapDatastore())
-	store, err := NewStoreWithHead(ctx, ds, suite.Head())
-	require.NoError(t, err)
-
-	err = store.Start(ctx)
-	require.NoError(t, err)
-
-	head, err := store.Head(ctx)
-	require.NoError(t, err)
-	assert.EqualValues(t, suite.Head().Hash(), head.Hash())
-
-	in := suite.GenDummyHeaders(10)
-	err = store.Append(ctx, in...)
-	require.NoError(t, err)
-
-	out, err := store.GetRangeByHeight(ctx, 2, 12)
-	require.NoError(t, err)
-	for i, h := range in {
-		assert.Equal(t, h.Hash(), out[i].Hash())
-	}
-
-	head, err = store.Head(ctx)
-	require.NoError(t, err)
-	assert.Equal(t, out[len(out)-1].Hash(), head.Hash())
-
-	ok, err := store.Has(ctx, in[5].Hash())
-	require.NoError(t, err)
-	assert.True(t, ok)
-
-	ok, err = store.Has(ctx, test.RandBytes(32))
-	require.NoError(t, err)
-	assert.False(t, ok)
-
-	go func() {
-		err := store.Append(ctx, suite.GenDummyHeaders(1)...)
-		require.NoError(t, err)
-	}()
-
-	h, err := store.GetByHeight(ctx, 12)
-	require.NoError(t, err)
-	assert.NotNil(t, h)
-
-	err = store.Stop(ctx)
-	require.NoError(t, err)
-
-	// check that the store can be successfully started after previous stop
-	// with all data being flushed.
-	store, err = NewStore[*test.DummyHeader](ds)
-	require.NoError(t, err)
-
-	err = store.Start(ctx)
-	require.NoError(t, err)
-
-	head, err = store.Head(ctx)
-	require.NoError(t, err)
-	assert.Equal(t, suite.Head().Hash(), head.Hash())
-
-	out, err = store.GetRangeByHeight(ctx, 1, 13)
-	require.NoError(t, err)
-	assert.Len(t, out, 12)
-
-	err = store.Stop(ctx)
-	require.NoError(t, err)
-}
-
-func TestStorePendingCacheMiss(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-
-	ds := sync.MutexWrap(datastore.NewMapDatastore())
-
-	store, err := NewStoreWithHead(ctx, ds, suite.Head(),
-		WithWriteBatchSize(100),
-		WithStoreCacheSize(100),
-	)
-	require.NoError(t, err)
-
-	err = store.Start(ctx)
-	require.NoError(t, err)
-	err = store.Append(ctx, suite.GenDummyHeaders(100)...)
-	require.NoError(t, err)
-
-	err = store.Append(ctx, suite.GenDummyHeaders(50)...)
-	require.NoError(t, err)
-
-	_, err = store.GetRangeByHeight(ctx, 1, 101)
-	require.NoError(t, err)
-
-	_, err = store.GetRangeByHeight(ctx, 101, 151)
-	require.NoError(t, err)
-}
diff --git a/libs/header/store/testing.go b/libs/header/store/testing.go
deleted file mode 100644
index a472193b..00000000
--- a/libs/header/store/testing.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package store
-
-import (
-	"context"
-	"testing"
-
-	"github.com/ipfs/go-datastore"
-	"github.com/ipfs/go-datastore/sync"
-	"github.com/stretchr/testify/require"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-// NewTestStore creates initialized and started in memory header Store which is useful for testing.
-func NewTestStore(ctx context.Context, t *testing.T, head *test.DummyHeader) header.Store[*test.DummyHeader] {
-	store, err := NewStoreWithHead(ctx, sync.MutexWrap(datastore.NewMapDatastore()), head)
-	require.NoError(t, err)
-
-	err = store.Start(ctx)
-	require.NoError(t, err)
-
-	t.Cleanup(func() {
-		err := store.Stop(ctx)
-		require.NoError(t, err)
-	})
-	return store
-}
diff --git a/libs/header/sync/ranges_test.go b/libs/header/sync/ranges_test.go
deleted file mode 100644
index ce5b123b..00000000
--- a/libs/header/sync/ranges_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package sync
-
-import (
-	"sync"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func TestAddParallel(t *testing.T) {
-	var pending ranges[*test.DummyHeader]
-
-	n := 500
-	suite := test.NewTestSuite(t)
-	headers := suite.GenDummyHeaders(n)
-
-	wg := &sync.WaitGroup{}
-	wg.Add(n)
-	for i := 0; i < n; i++ {
-		go func(i int) {
-			pending.Add(headers[i])
-			wg.Done()
-		}(i)
-	}
-	wg.Wait()
-
-	last := uint64(0)
-	for _, r := range pending.ranges {
-		assert.Greater(t, r.start, last)
-		last = r.start
-	}
-}
-
-func TestRangeGet(t *testing.T) {
-	n := 300
-	suite := test.NewTestSuite(t)
-	headers := suite.GenDummyHeaders(n)
-
-	r := newRange(headers[200])
-	r.Append(headers[201:]...)
-
-	truncated := r.Get(100)
-	assert.Len(t, truncated, 0)
-}
diff --git a/libs/header/sync/sync_getter_test.go b/libs/header/sync/sync_getter_test.go
deleted file mode 100644
index b89c6c57..00000000
--- a/libs/header/sync/sync_getter_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package sync
-
-import (
-	"context"
-	"fmt"
-	"sync"
-	"sync/atomic"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func TestSyncGetterHead(t *testing.T) {
-	ctx, cancel := context.WithCancel(context.Background())
-	t.Cleanup(cancel)
-
-	fex := &fakeGetter[*test.DummyHeader]{}
-	sex := &syncGetter[*test.DummyHeader]{Getter: fex}
-
-	var wg sync.WaitGroup
-	for i := 0; i < 100; i++ {
-		wg.Add(1)
-		go func() {
-			defer wg.Done()
-			h, err := sex.Head(ctx)
-			if h != nil || err != errFakeHead {
-				t.Fail()
-			}
-		}()
-	}
-	wg.Wait()
-
-	assert.EqualValues(t, 1, fex.hits.Load())
-}
-
-var errFakeHead = fmt.Errorf("head")
-
-type fakeGetter[H header.Header] struct {
-	hits atomic.Uint32
-}
-
-func (f *fakeGetter[H]) Head(ctx context.Context) (h H, err error) {
-	f.hits.Add(1)
-	select {
-	case <-time.After(time.Millisecond * 100):
-		err = errFakeHead
-	case <-ctx.Done():
-		err = ctx.Err()
-	}
-
-	return
-}
-
-func (f *fakeGetter[H]) Get(ctx context.Context, hash header.Hash) (H, error) {
-	panic("implement me")
-}
-
-func (f *fakeGetter[H]) GetByHeight(ctx context.Context, u uint64) (H, error) {
-	panic("implement me")
-}
-
-func (f *fakeGetter[H]) GetRangeByHeight(ctx context.Context, from, amount uint64) ([]H, error) {
-	panic("implement me")
-}
-
-func (f *fakeGetter[H]) GetVerifiedRange(ctx context.Context, from H, amount uint64) ([]H, error) {
-	panic("implement me")
-}
diff --git a/libs/header/sync/sync_store_test.go b/libs/header/sync/sync_store_test.go
deleted file mode 100644
index f2cbc1bb..00000000
--- a/libs/header/sync/sync_store_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package sync
-
-import (
-	"context"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/celestiaorg/celestia-node/libs/header/mocks"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func TestSyncStore(t *testing.T) {
-	ctx, cancel := context.WithCancel(context.Background())
-	t.Cleanup(cancel)
-
-	ts := test.NewTestSuite(t)
-	s := mocks.NewStore[*test.DummyHeader](t, ts, 100)
-	ss := syncStore[*test.DummyHeader]{Store: s}
-
-	h, err := ss.Head(ctx)
-	assert.NoError(t, err)
-	assert.Equal(t, ts.Head(), h)
-
-	err = ss.Append(ctx, ts.GetRandomHeader())
-	assert.NoError(t, err)
-
-	h, err = ss.Head(ctx)
-	assert.NoError(t, err)
-	assert.Equal(t, ts.Head(), h)
-}
diff --git a/libs/header/sync/sync_test.go b/libs/header/sync/sync_test.go
deleted file mode 100644
index 08473bf2..00000000
--- a/libs/header/sync/sync_test.go
+++ /dev/null
@@ -1,299 +0,0 @@
-package sync
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	pubsub "github.com/libp2p/go-libp2p-pubsub"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/local"
-	"github.com/celestiaorg/celestia-node/libs/header/store"
-	"github.com/celestiaorg/celestia-node/libs/header/test"
-)
-
-func TestSyncSimpleRequestingHead(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-
-	remoteStore := store.NewTestStore(ctx, t, head)
-	err := remoteStore.Append(ctx, suite.GenDummyHeaders(100)...)
-	require.NoError(t, err)
-
-	_, err = remoteStore.GetByHeight(ctx, 100)
-	require.NoError(t, err)
-
-	localStore := store.NewTestStore(ctx, t, head)
-	syncer, err := NewSyncer[*test.DummyHeader](
-		local.NewExchange(remoteStore),
-		localStore,
-		&test.DummySubscriber{},
-		WithBlockTime(time.Second*30),
-		WithTrustingPeriod(time.Microsecond),
-	)
-	require.NoError(t, err)
-	err = syncer.Start(ctx)
-	require.NoError(t, err)
-
-	time.Sleep(time.Millisecond * 10) // needs some to realize it is syncing
-	err = syncer.SyncWait(ctx)
-	require.NoError(t, err)
-
-	exp, err := remoteStore.Head(ctx)
-	require.NoError(t, err)
-
-	have, err := localStore.Head(ctx)
-	require.NoError(t, err)
-	assert.Equal(t, exp.Height(), have.Height())
-	assert.Empty(t, syncer.pending.Head())
-
-	state := syncer.State()
-	assert.Equal(t, uint64(exp.Height()), state.Height)
-	assert.Equal(t, uint64(2), state.FromHeight)
-	assert.Equal(t, uint64(exp.Height()), state.ToHeight)
-	assert.True(t, state.Finished(), state)
-}
-
-func TestDoSyncFullRangeFromExternalPeer(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-
-	remoteStore := store.NewTestStore(ctx, t, head)
-	localStore := store.NewTestStore(ctx, t, head)
-	syncer, err := NewSyncer[*test.DummyHeader](
-		local.NewExchange(remoteStore),
-		localStore,
-		&test.DummySubscriber{},
-	)
-	require.NoError(t, err)
-	require.NoError(t, syncer.Start(ctx))
-
-	err = remoteStore.Append(ctx, suite.GenDummyHeaders(int(header.MaxRangeRequestSize))...)
-	require.NoError(t, err)
-	// give store time to update heightSub index
-	time.Sleep(time.Millisecond * 100)
-
-	// trigger sync by calling Head
-	_, err = syncer.Head(ctx)
-	require.NoError(t, err)
-
-	// give store time to sync
-	time.Sleep(time.Millisecond * 100)
-
-	remoteHead, err := remoteStore.Head(ctx)
-	require.NoError(t, err)
-
-	newHead, err := localStore.Head(ctx)
-	require.NoError(t, err)
-	require.Equal(t, newHead.Height(), remoteHead.Height())
-}
-
-func TestSyncCatchUp(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-
-	remoteStore := store.NewTestStore(ctx, t, head)
-	localStore := store.NewTestStore(ctx, t, head)
-	syncer, err := NewSyncer[*test.DummyHeader](
-		local.NewExchange(remoteStore),
-		localStore,
-		&test.DummySubscriber{},
-		WithTrustingPeriod(time.Minute),
-	)
-	require.NoError(t, err)
-	// 1. Initial sync
-	err = syncer.Start(ctx)
-	require.NoError(t, err)
-
-	// 2. chain grows and syncer misses that
-	err = remoteStore.Append(ctx, suite.GenDummyHeaders(100)...)
-	require.NoError(t, err)
-
-	incomingHead := suite.GenDummyHeaders(1)[0]
-	// 3. syncer rcvs header from the future and starts catching-up
-	res := syncer.incomingNetworkHead(ctx, incomingHead)
-	assert.Equal(t, pubsub.ValidationAccept, res)
-
-	time.Sleep(time.Millisecond * 100) // needs some to realize it is syncing
-	err = syncer.SyncWait(ctx)
-	require.NoError(t, err)
-
-	exp, err := remoteStore.Head(ctx)
-	require.NoError(t, err)
-
-	// 4. assert syncer caught-up
-	have, err := localStore.Head(ctx)
-	require.NoError(t, err)
-
-	assert.Equal(t, have.Height(), incomingHead.Height())
-	assert.Equal(t, exp.Height()+1, have.Height()) // plus one as we didn't add last header to remoteStore
-	assert.Empty(t, syncer.pending.Head())
-
-	state := syncer.State()
-	assert.Equal(t, uint64(exp.Height()+1), state.Height)
-	assert.Equal(t, uint64(2), state.FromHeight)
-	assert.Equal(t, uint64(exp.Height()+1), state.ToHeight)
-	assert.True(t, state.Finished(), state)
-}
-
-func TestSyncPendingRangesWithMisses(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-
-	remoteStore := store.NewTestStore(ctx, t, head)
-	localStore := store.NewTestStore(ctx, t, head)
-	syncer, err := NewSyncer[*test.DummyHeader](
-		local.NewExchange(remoteStore),
-		localStore,
-		&test.DummySubscriber{},
-		WithTrustingPeriod(time.Minute),
-	)
-	require.NoError(t, err)
-	err = syncer.Start(ctx)
-	require.NoError(t, err)
-
-	// miss 1 (helps to test that Syncer properly requests missed Headers from Exchange)
-	err = remoteStore.Append(ctx, suite.GenDummyHeaders(1)...)
-	require.NoError(t, err)
-
-	range1 := suite.GenDummyHeaders(15)
-	err = remoteStore.Append(ctx, range1...)
-	require.NoError(t, err)
-
-	// miss 2
-	err = remoteStore.Append(ctx, suite.GenDummyHeaders(3)...)
-	require.NoError(t, err)
-
-	range2 := suite.GenDummyHeaders(23)
-	err = remoteStore.Append(ctx, range2...)
-	require.NoError(t, err)
-
-	// manually add to pending
-	for _, h := range append(range1, range2...) {
-		syncer.pending.Add(h)
-	}
-
-	// and fire up a sync
-	syncer.sync(ctx)
-
-	_, err = remoteStore.GetByHeight(ctx, 43)
-	require.NoError(t, err)
-	_, err = localStore.GetByHeight(ctx, 43)
-	require.NoError(t, err)
-
-	lastHead, err := syncer.store.Head(ctx)
-	require.NoError(t, err)
-	require.Equal(t, lastHead.Height(), int64(43))
-	exp, err := remoteStore.Head(ctx)
-	require.NoError(t, err)
-
-	have, err := localStore.Head(ctx)
-	require.NoError(t, err)
-
-	assert.Equal(t, exp.Height(), have.Height())
-	assert.Empty(t, syncer.pending.Head()) // assert all cache from pending is used
-}
-
-// TestSyncer_FindHeadersReturnsCorrectRange ensures that `findHeaders` returns
-// range [from;to]
-func TestSyncer_FindHeadersReturnsCorrectRange(t *testing.T) {
-	// Test consists of 3 steps:
-	// 1. get range of headers from pending; [2;11]
-	// 2. get headers from the remote store; [12;20]
-	// 3. apply last header from pending;
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-
-	remoteStore := store.NewTestStore(ctx, t, head)
-	localStore := store.NewTestStore(ctx, t, head)
-	syncer, err := NewSyncer[*test.DummyHeader](
-		local.NewExchange(remoteStore),
-		localStore,
-		&test.DummySubscriber{},
-	)
-	require.NoError(t, err)
-
-	range1 := suite.GenDummyHeaders(10)
-	// manually add to pending
-	for _, h := range range1 {
-		syncer.pending.Add(h)
-	}
-	err = remoteStore.Append(ctx, range1...)
-	require.NoError(t, err)
-	err = remoteStore.Append(ctx, suite.GenDummyHeaders(9)...)
-	require.NoError(t, err)
-
-	syncer.pending.Add(suite.GetRandomHeader())
-	require.NoError(t, err)
-	err = syncer.processHeaders(ctx, head, 21)
-	require.NoError(t, err)
-
-	head, err = syncer.store.Head(ctx)
-	require.NoError(t, err)
-	assert.Equal(t, head.Height(), int64(21))
-}
-
-func TestSyncerIncomingDuplicate(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
-	t.Cleanup(cancel)
-
-	suite := test.NewTestSuite(t)
-	head := suite.Head()
-
-	remoteStore := store.NewTestStore(ctx, t, head)
-	localStore := store.NewTestStore(ctx, t, head)
-	syncer, err := NewSyncer[*test.DummyHeader](
-		&delayedGetter[*test.DummyHeader]{Getter: local.NewExchange(remoteStore)},
-		localStore,
-		&test.DummySubscriber{},
-	)
-	require.NoError(t, err)
-	err = syncer.Start(ctx)
-	require.NoError(t, err)
-
-	range1 := suite.GenDummyHeaders(10)
-	err = remoteStore.Append(ctx, range1...)
-	require.NoError(t, err)
-
-	res := syncer.incomingNetworkHead(ctx, range1[len(range1)-1])
-	assert.Equal(t, pubsub.ValidationAccept, res)
-
-	time.Sleep(time.Millisecond * 10)
-
-	res = syncer.incomingNetworkHead(ctx, range1[len(range1)-1])
-	assert.Equal(t, pubsub.ValidationIgnore, res)
-
-	err = syncer.SyncWait(ctx)
-	require.NoError(t, err)
-}
-
-type delayedGetter[H header.Header] struct {
-	header.Getter[H]
-}
-
-func (d *delayedGetter[H]) GetVerifiedRange(ctx context.Context, from H, amount uint64) ([]H, error) {
-	select {
-	case <-time.After(time.Millisecond * 100):
-		return d.Getter.GetVerifiedRange(ctx, from, amount)
-	case <-ctx.Done():
-		return nil, ctx.Err()
-	}
-}
diff --git a/libs/header/test/testing.go b/libs/header/test/testing.go
deleted file mode 100644
index 5f0a3d4c..00000000
--- a/libs/header/test/testing.go
+++ /dev/null
@@ -1,269 +0,0 @@
-package test
-
-import (
-	"bytes"
-	"context"
-	"crypto/rand"
-	"encoding/binary"
-	"encoding/gob"
-	"fmt"
-	"math"
-	"testing"
-	"time"
-
-	pubsub "github.com/libp2p/go-libp2p-pubsub"
-	"golang.org/x/crypto/sha3"
-
-	"github.com/celestiaorg/celestia-node/libs/header"
-)
-
-type Raw struct {
-	ChainID      string
-	PreviousHash header.Hash
-
-	Height int64
-	Time   time.Time
-}
-
-type DummyHeader struct {
-	Raw
-
-	hash header.Hash
-}
-
-func (d *DummyHeader) New() header.Header {
-	return new(DummyHeader)
-}
-
-func (d *DummyHeader) IsZero() bool {
-	return d == nil
-}
-
-func (d *DummyHeader) ChainID() string {
-	return "private"
-}
-
-func (d *DummyHeader) Hash() header.Hash {
-	if len(d.hash) == 0 {
-		if err := d.rehash(); err != nil {
-			panic(err)
-		}
-	}
-	return d.hash
-}
-
-func (d *DummyHeader) rehash() error {
-	b, err := d.MarshalBinary()
-	if err != nil {
-		return err
-	}
-	hash := sha3.Sum512(b)
-	d.hash = hash[:]
-	return nil
-}
-
-func (d *DummyHeader) Height() int64 {
-	return d.Raw.Height
-}
-
-func (d *DummyHeader) LastHeader() header.Hash {
-	return d.Raw.PreviousHash
-}
-
-func (d *DummyHeader) Time() time.Time {
-	return d.Raw.Time
-}
-
-func (d *DummyHeader) IsRecent(blockTime time.Duration) bool {
-	return time.Since(d.Time()) <= blockTime
-}
-
-func (d *DummyHeader) IsExpired(period time.Duration) bool {
-	expirationTime := d.Time().Add(period)
-	return expirationTime.Before(time.Now())
-}
-
-func (d *DummyHeader) Verify(header header.Header) error {
-	epsilon := 10 * time.Second
-	if header.Time().After(time.Now().Add(epsilon)) {
-		return fmt.Errorf("header Time too far in the future")
-	}
-
-	if header.Height() <= d.Height() {
-		return fmt.Errorf("expected new header Height to be larger than old header Time")
-	}
-
-	if header.Time().Before(d.Time()) {
-		return fmt.Errorf("expected new header Time to be after old header Time")
-	}
-
-	return nil
-}
-
-func (d *DummyHeader) Validate() error {
-	return nil
-}
-
-func (d *DummyHeader) MarshalBinary() ([]byte, error) {
-	var buf bytes.Buffer
-	enc := gob.NewEncoder(&buf)
-	err := enc.Encode(d.Raw)
-	return buf.Bytes(), err
-}
-
-func (d *DummyHeader) UnmarshalBinary(data []byte) error {
-	dec := gob.NewDecoder(bytes.NewReader(data))
-	err := dec.Decode(&d.Raw)
-	if err != nil {
-		return err
-	}
-	err = d.rehash()
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// RandBytes returns slice of n-bytes, or nil in case of error
-func RandBytes(n int) []byte {
-	buf := make([]byte, n)
-
-	c, err := rand.Read(buf)
-	if err != nil || c != n {
-		return nil
-	}
-
-	return buf
-}
-
-func randInt63() int64 {
-	var buf [8]byte
-
-	_, err := rand.Read(buf[:])
-	if err != nil {
-		return math.MaxInt64
-	}
-
-	return int64(binary.BigEndian.Uint64(buf[:]) & math.MaxInt64)
-}
-
-func RandDummyHeader(t *testing.T) *DummyHeader {
-	t.Helper()
-	dh, err := randDummyHeader()
-	if err != nil {
-		t.Fatal(err)
-	}
-	return dh
-}
-
-func randDummyHeader() (*DummyHeader, error) {
-	dh := &DummyHeader{
-		Raw{
-			PreviousHash: RandBytes(32),
-			Height:       randInt63(),
-			Time:         time.Now().UTC(),
-		},
-		nil,
-	}
-	err := dh.rehash()
-	return dh, err
-}
-
-func mustRandDummyHeader() *DummyHeader {
-	dh, err := randDummyHeader()
-	if err != nil {
-		panic(err)
-	}
-	return dh
-}
-
-// Suite provides everything you need to test chain of Headers.
-// If not, please don't hesitate to extend it for your case.
-type Suite struct {
-	t *testing.T
-
-	head *DummyHeader
-}
-
-type Generator[H header.Header] interface {
-	GetRandomHeader() H
-}
-
-// NewTestSuite setups a new test suite.
-func NewTestSuite(t *testing.T) *Suite {
-	return &Suite{
-		t: t,
-	}
-}
-
-func (s *Suite) genesis() *DummyHeader {
-	return &DummyHeader{
-		hash: nil,
-		Raw: Raw{
-			PreviousHash: nil,
-			Height:       1,
-			Time:         time.Now().Add(-10 * time.Second).UTC(),
-		},
-	}
-}
-
-func (s *Suite) Head() *DummyHeader {
-	if s.head == nil {
-		s.head = s.genesis()
-	}
-	return s.head
-}
-
-func (s *Suite) GenDummyHeaders(num int) []*DummyHeader {
-	headers := make([]*DummyHeader, num)
-	for i := range headers {
-		headers[i] = s.GetRandomHeader()
-	}
-	return headers
-}
-
-func (s *Suite) GetRandomHeader() *DummyHeader {
-	if s.head == nil {
-		s.head = s.genesis()
-		return s.head
-	}
-
-	dh := mustRandDummyHeader()
-	dh.Raw.Height = s.head.Height() + 1
-	dh.Raw.PreviousHash = s.head.Hash()
-	_ = dh.rehash()
-	s.head = dh
-	return s.head
-}
-
-type DummySubscriber struct {
-	Headers []*DummyHeader
-}
-
-func (mhs *DummySubscriber) AddValidator(func(context.Context, *DummyHeader) pubsub.ValidationResult) error {
-	return nil
-}
-
-func (mhs *DummySubscriber) Subscribe() (header.Subscription[*DummyHeader], error) {
-	return mhs, nil
-}
-
-func (mhs *DummySubscriber) NextHeader(ctx context.Context) (*DummyHeader, error) {
-	defer func() {
-		if len(mhs.Headers) > 1 {
-			// pop the already-returned header
-			cp := mhs.Headers
-			mhs.Headers = cp[1:]
-		} else {
-			mhs.Headers = make([]*DummyHeader, 0)
-		}
-	}()
-	if len(mhs.Headers) == 0 {
-		return nil, context.Canceled
-	}
-	return mhs.Headers[0], nil
-}
-
-func (mhs *DummySubscriber) Stop(context.Context) error { return nil }
-func (mhs *DummySubscriber) Cancel()                    {}
diff --git a/nodebuilder/header/module_test.go b/nodebuilder/header/module_test.go
index 18d72eaf..00485b97 100644
--- a/nodebuilder/header/module_test.go
+++ b/nodebuilder/header/module_test.go
@@ -13,12 +13,13 @@ import (
 	"go.uber.org/fx"
 	"go.uber.org/fx/fxtest"
 
+	libhead "github.com/celestiaorg/go-header"
+	"github.com/celestiaorg/go-header/p2p"
+	"github.com/celestiaorg/go-header/store"
+	"github.com/celestiaorg/go-header/sync"
+
 	"github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/libs/fraud"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
-	"github.com/celestiaorg/celestia-node/libs/header/p2p"
-	"github.com/celestiaorg/celestia-node/libs/header/store"
-	"github.com/celestiaorg/celestia-node/libs/header/sync"
 	"github.com/celestiaorg/celestia-node/nodebuilder/node"
 	modp2p "github.com/celestiaorg/celestia-node/nodebuilder/p2p"
 )
diff --git a/nodebuilder/testing.go b/nodebuilder/testing.go
index 78063254..8c572415 100644
--- a/nodebuilder/testing.go
+++ b/nodebuilder/testing.go
@@ -9,12 +9,12 @@ import (
 	"go.uber.org/fx"
 
 	apptypes "github.com/celestiaorg/celestia-app/x/blob/types"
+	"github.com/celestiaorg/go-header/mocks"
 
 	"github.com/celestiaorg/celestia-node/core"
 	coreheader "github.com/celestiaorg/celestia-node/header"
 	"github.com/celestiaorg/celestia-node/header/headertest"
 	"github.com/celestiaorg/celestia-node/libs/fxutil"
-	"github.com/celestiaorg/celestia-node/libs/header/mocks"
 	"github.com/celestiaorg/celestia-node/nodebuilder/header"
 	"github.com/celestiaorg/celestia-node/nodebuilder/node"
 	"github.com/celestiaorg/celestia-node/nodebuilder/p2p"
diff --git a/nodebuilder/tests/swamp/swamp.go b/nodebuilder/tests/swamp/swamp.go
index ee188af2..884b39e6 100644
--- a/nodebuilder/tests/swamp/swamp.go
+++ b/nodebuilder/tests/swamp/swamp.go
@@ -18,10 +18,10 @@ import (
 	"go.uber.org/fx"
 
 	"github.com/celestiaorg/celestia-app/testutil/testnode"
+	libhead "github.com/celestiaorg/go-header"
 
 	"github.com/celestiaorg/celestia-node/core"
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/libs/keystore"
 	"github.com/celestiaorg/celestia-node/logs"
 	"github.com/celestiaorg/celestia-node/nodebuilder"
diff --git a/share/p2p/peers/manager_test.go b/share/p2p/peers/manager_test.go
index 21b0ba82..7f0c9819 100644
--- a/share/p2p/peers/manager_test.go
+++ b/share/p2p/peers/manager_test.go
@@ -18,8 +18,9 @@ import (
 	mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
 	"github.com/stretchr/testify/require"
 
+	libhead "github.com/celestiaorg/go-header"
+
 	"github.com/celestiaorg/celestia-node/header"
-	libhead "github.com/celestiaorg/celestia-node/libs/header"
 	"github.com/celestiaorg/celestia-node/share"
 	"github.com/celestiaorg/celestia-node/share/availability/discovery"
 	"github.com/celestiaorg/celestia-node/share/p2p/shrexsub"