Repository: celestiaorg/celestia-node
Pull request #159 (2021-10-28 22:17:19)

Add version command
The goal for this PR is to add the `version` subcommand of `celestia` command to get more information about the build binary. The new subcommand closes [#155](https://github.com/celestiaorg/celestia-node/issues/155).

### version command in action
- On Linux environment (Zorin OS 16)
<img width="550" alt="Screen Shot 2021-10-28 at 16 21 53" src="https://user-images.githubusercontent.com/34588445/139338366-265800a9-07f9-480b-bf78-8b1df518bee8.png">





Patch:
diff --git a/Makefile b/Makefile
index 121d974c..b0867948 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,9 @@
-#!/usr/bin/make -f
+SHELL=/usr/bin/env bash
 PROJECTNAME=$(shell basename "$(PWD)")
+BUILD_DATE=$(shell date)
+LAST_COMMIT=$(shell git rev-parse HEAD)
+# TODO (@OrlandoRomo) get version from git tags
+CELESTIA_VERSION="0.1.0"
 
 ## help: Get more info on make commands.
 help: Makefile
@@ -10,7 +14,7 @@ help: Makefile
 ## build: Build celesita-node binary.
 build:
 	@echo "--> Building Celestia"
-	@go build ./cmd/celestia
+	@go build -ldflags "-X 'main.buildTime=$(BUILD_DATE)' -X 'main.lastCommit=$(LAST_COMMIT)' -X 'main.semanticVersion=$(CELESTIA_VERSION)'" ./cmd/celestia 
 
 ## fmt: Formats only *.go (excluding *.pb.go *pb_test.go). Runs `gofmt & goimports` internally.
 fmt:
@@ -30,3 +34,18 @@ test:
 	@echo "--> Running tests"
 	@go test -v ./...
 .PHONY: test
+
+PB_PKGS=$(shell find . -name 'pb' -type d)
+PB_CORE=$(shell go list -f {{.Dir}} -m github.com/celestiaorg/celestia-core)
+PB_GOGO=$(shell go list -f {{.Dir}} -m github.com/gogo/protobuf)
+
+## pb-gen: Generate protobuf code for all /pb/*.proto files in the project.
+pb-gen:
+	@echo '--> Generating protobuf'
+	@for dir in $(PB_PKGS); \
+		do for file in `find $$dir -type f -name "*.proto"`; \
+			do protoc -I=. -I=${PB_CORE}/proto/ -I=${PB_GOGO}  --gogofaster_out . $$file; \
+			echo '-->' $$file; \
+		done; \
+	done;
+.PHONY: pb-gen
diff --git a/cmd/celestia/main.go b/cmd/celestia/main.go
index d95f696e..af197e90 100644
--- a/cmd/celestia/main.go
+++ b/cmd/celestia/main.go
@@ -12,6 +12,7 @@ func init() {
 		fullCmd,
 		lightCmd,
 		devCmd,
+		versionCmd,
 	)
 }
 
diff --git a/cmd/celestia/version.go b/cmd/celestia/version.go
new file mode 100644
index 00000000..ef1a016d
--- /dev/null
+++ b/cmd/celestia/version.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+	"fmt"
+	"runtime"
+
+	"github.com/spf13/cobra"
+)
+
+var (
+	buildTime       string
+	lastCommit      string
+	semanticVersion string
+)
+
+var versionCmd = &cobra.Command{
+	Use:   "version",
+	Short: "Show information about the current binary build",
+	Args:  cobra.NoArgs,
+	Run:   getBuildInfo,
+}
+
+func getBuildInfo(cmd *cobra.Command, args []string) {
+	fmt.Printf("Semantic version: %s\n", semanticVersion)
+	fmt.Printf("Commit: %s\n", lastCommit)
+	fmt.Printf("Build Date: %s\n", buildTime)
+	fmt.Printf("System version: %s/%s\n", runtime.GOARCH, runtime.GOOS)
+	fmt.Printf("Golang version: %s\n", runtime.Version())
+}
diff --git a/go.mod b/go.mod
index 0d25e861..5149de5a 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
 	github.com/celestiaorg/celestia-core v0.0.2-0.20210924001615-488ac31b4b3c
 	github.com/celestiaorg/nmt v0.7.0
 	github.com/celestiaorg/rsmt2d v0.3.0
+	github.com/gogo/protobuf v1.3.2
 	github.com/ipfs/go-bitswap v0.3.4
 	github.com/ipfs/go-block-format v0.0.3
 	github.com/ipfs/go-blockservice v0.1.7
diff --git a/node/init.go b/node/init.go
index 03cb8263..9d58ac90 100644
--- a/node/init.go
+++ b/node/init.go
@@ -1,6 +1,7 @@
 package node
 
 import (
+	"errors"
 	"os"
 	"path/filepath"
 
@@ -48,6 +49,10 @@ func InitWith(path string, tp Type, cfg *Config) error {
 		return err
 	}
 
+	if cfg == nil {
+		return errors.New("configuration is missing for the node's initialisation")
+	}
+
 	cfgPath := configPath(path)
 	if !utils.Exists(cfgPath) {
 		err = SaveConfig(cfgPath, cfg)
diff --git a/service/header/header.go b/service/header/header.go
new file mode 100644
index 00000000..98edcc98
--- /dev/null
+++ b/service/header/header.go
@@ -0,0 +1,43 @@
+package header
+
+import (
+	"fmt"
+
+	"github.com/celestiaorg/celestia-core/pkg/da"
+	core "github.com/celestiaorg/celestia-core/types"
+)
+
+// RawHeader is an alias to core.Header. It is
+// "raw" because it is not yet wrapped to include
+// the DataAvailabilityHeader.
+type RawHeader = core.Header
+
+// ExtendedHeader represents a wrapped "raw" header that includes
+// information necessary for Celestia Nodes to be notified of new
+// block headers and perform Data Availability Sampling.
+type ExtendedHeader struct {
+	RawHeader    `json:"header"`
+	Commit       *core.Commit               `json:"commit"`
+	ValidatorSet *core.ValidatorSet         `json:"validator_set"`
+	DAH          *da.DataAvailabilityHeader `json:"dah"`
+}
+
+// MarshalBinary marshals ExtendedHeader to binary.
+func (eh *ExtendedHeader) MarshalBinary() ([]byte, error) {
+	return MarshalExtendedHeader(eh)
+}
+
+// MarshalBinary unmarshals ExtendedHeader from binary.
+func (eh *ExtendedHeader) UnmarshalBinary(data []byte) error {
+	if eh == nil {
+		return fmt.Errorf("header: cannot UnmarshalBinary - nil ExtendedHeader")
+	}
+
+	out, err := UnmarshalExtendedHeader(data)
+	if err != nil {
+		return err
+	}
+
+	*eh = *out
+	return nil
+}
diff --git a/service/header/header_serde.go b/service/header/header_serde.go
new file mode 100644
index 00000000..931b963e
--- /dev/null
+++ b/service/header/header_serde.go
@@ -0,0 +1,62 @@
+package header
+
+import (
+	"github.com/celestiaorg/celestia-core/pkg/da"
+	core "github.com/celestiaorg/celestia-core/types"
+
+	header_pb "github.com/celestiaorg/celestia-node/service/header/pb"
+)
+
+// MarshalExtendedHeader serializes given ExtendedHeader to bytes using protobuf.
+// Paired with UnmarshalExtendedHeader.
+func MarshalExtendedHeader(in *ExtendedHeader) (_ []byte, err error) {
+	out := &header_pb.ExtendedHeader{
+		Header: in.RawHeader.ToProto(),
+		Commit: in.Commit.ToProto(),
+	}
+
+	out.ValidatorSet, err = in.ValidatorSet.ToProto()
+	if err != nil {
+		return nil, err
+	}
+
+	out.Dah, err = in.DAH.ToProto()
+	if err != nil {
+		return nil, err
+	}
+
+	return out.Marshal()
+}
+
+// MarshalExtendedHeader deserializes given data into a new ExtendedHeader using protobuf.
+// Paired with MarshalExtendedHeader.
+func UnmarshalExtendedHeader(data []byte) (*ExtendedHeader, error) {
+	in := &header_pb.ExtendedHeader{}
+	err := in.Unmarshal(data)
+	if err != nil {
+		return nil, err
+	}
+
+	out := &ExtendedHeader{}
+	out.RawHeader, err = core.HeaderFromProto(in.Header)
+	if err != nil {
+		return nil, err
+	}
+
+	out.Commit, err = core.CommitFromProto(in.Commit)
+	if err != nil {
+		return nil, err
+	}
+
+	out.ValidatorSet, err = core.ValidatorSetFromProto(in.ValidatorSet)
+	if err != nil {
+		return nil, err
+	}
+
+	out.DAH, err = da.DataAvailabilityHeaderFromProto(in.Dah)
+	if err != nil {
+		return nil, err
+	}
+
+	return out, nil
+}
diff --git a/service/header/pb/extended_header.pb.go b/service/header/pb/extended_header.pb.go
new file mode 100644
index 00000000..4157526d
--- /dev/null
+++ b/service/header/pb/extended_header.pb.go
@@ -0,0 +1,514 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: service/header/pb/extended_header.proto
+
+package header_pb
+
+import (
+	fmt "fmt"
+	da "github.com/celestiaorg/celestia-core/proto/tendermint/da"
+	types "github.com/celestiaorg/celestia-core/proto/tendermint/types"
+	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 ExtendedHeader struct {
+	Header       *types.Header              `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+	Commit       *types.Commit              `protobuf:"bytes,2,opt,name=commit,proto3" json:"commit,omitempty"`
+	ValidatorSet *types.ValidatorSet        `protobuf:"bytes,3,opt,name=validator_set,json=validatorSet,proto3" json:"validator_set,omitempty"`
+	Dah          *da.DataAvailabilityHeader `protobuf:"bytes,4,opt,name=dah,proto3" json:"dah,omitempty"`
+}
+
+func (m *ExtendedHeader) Reset()         { *m = ExtendedHeader{} }
+func (m *ExtendedHeader) String() string { return proto.CompactTextString(m) }
+func (*ExtendedHeader) ProtoMessage()    {}
+func (*ExtendedHeader) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ae8fd759b6756a2e, []int{0}
+}
+func (m *ExtendedHeader) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ExtendedHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ExtendedHeader.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 *ExtendedHeader) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ExtendedHeader.Merge(m, src)
+}
+func (m *ExtendedHeader) XXX_Size() int {
+	return m.Size()
+}
+func (m *ExtendedHeader) XXX_DiscardUnknown() {
+	xxx_messageInfo_ExtendedHeader.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ExtendedHeader proto.InternalMessageInfo
+
+func (m *ExtendedHeader) GetHeader() *types.Header {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *ExtendedHeader) GetCommit() *types.Commit {
+	if m != nil {
+		return m.Commit
+	}
+	return nil
+}
+
+func (m *ExtendedHeader) GetValidatorSet() *types.ValidatorSet {
+	if m != nil {
+		return m.ValidatorSet
+	}
+	return nil
+}
+
+func (m *ExtendedHeader) GetDah() *da.DataAvailabilityHeader {
+	if m != nil {
+		return m.Dah
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*ExtendedHeader)(nil), "header.pb.ExtendedHeader")
+}
+
+func init() {
+	proto.RegisterFile("service/header/pb/extended_header.proto", fileDescriptor_ae8fd759b6756a2e)
+}
+
+var fileDescriptor_ae8fd759b6756a2e = []byte{
+	// 269 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2f, 0x4e, 0x2d, 0x2a,
+	0xcb, 0x4c, 0x4e, 0xd5, 0xcf, 0x48, 0x4d, 0x4c, 0x49, 0x2d, 0xd2, 0x2f, 0x48, 0xd2, 0x4f, 0xad,
+	0x28, 0x49, 0xcd, 0x4b, 0x49, 0x4d, 0x89, 0x87, 0x08, 0xe9, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b,
+	0x71, 0xc2, 0x78, 0x49, 0x52, 0x32, 0x60, 0xf9, 0xa2, 0xdc, 0xcc, 0xbc, 0x12, 0xfd, 0x92, 0xca,
+	0x82, 0xd4, 0x62, 0x08, 0x09, 0x51, 0x28, 0xa5, 0x80, 0x21, 0x5b, 0x96, 0x98, 0x93, 0x99, 0x92,
+	0x58, 0x92, 0x0f, 0x35, 0x4a, 0x4a, 0x07, 0x49, 0x45, 0x4a, 0xa2, 0x7e, 0x4a, 0x62, 0x49, 0x62,
+	0x7c, 0x62, 0x59, 0x62, 0x66, 0x4e, 0x62, 0x52, 0x66, 0x4e, 0x66, 0x49, 0x25, 0x8a, 0xc5, 0x4a,
+	0x9f, 0x18, 0xb9, 0xf8, 0x5c, 0xa1, 0x4e, 0xf2, 0x00, 0x4b, 0x08, 0x19, 0x70, 0xb1, 0x41, 0x94,
+	0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0x49, 0xe8, 0x21, 0x4c, 0xd4, 0x83, 0xb8, 0x05, 0xa2,
+	0x32, 0x08, 0xaa, 0x0e, 0xa4, 0x23, 0x39, 0x3f, 0x37, 0x37, 0xb3, 0x44, 0x82, 0x09, 0x97, 0x0e,
+	0x67, 0xb0, 0x7c, 0x10, 0x54, 0x9d, 0x90, 0x33, 0x17, 0x2f, 0xdc, 0xdd, 0xf1, 0xc5, 0xa9, 0x25,
+	0x12, 0xcc, 0x60, 0x8d, 0x72, 0x98, 0x1a, 0xc3, 0x60, 0xca, 0x82, 0x53, 0x4b, 0x82, 0x78, 0xca,
+	0x90, 0x78, 0x42, 0xe6, 0x5c, 0xcc, 0x29, 0x89, 0x19, 0x12, 0x2c, 0x60, 0xad, 0xaa, 0xc8, 0x5a,
+	0x53, 0x12, 0xf5, 0x5c, 0x12, 0x4b, 0x12, 0x1d, 0x91, 0xbc, 0x0d, 0x75, 0x32, 0x48, 0x87, 0x93,
+	0xc4, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1,
+	0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x24, 0xb1, 0x81, 0x43, 0xc5, 0x18,
+	0x10, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x6d, 0x32, 0x42, 0xb9, 0x01, 0x00, 0x00,
+}
+
+func (m *ExtendedHeader) 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 *ExtendedHeader) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ExtendedHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.Dah != nil {
+		{
+			size, err := m.Dah.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintExtendedHeader(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x22
+	}
+	if m.ValidatorSet != nil {
+		{
+			size, err := m.ValidatorSet.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintExtendedHeader(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.Commit != nil {
+		{
+			size, err := m.Commit.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintExtendedHeader(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x12
+	}
+	if m.Header != nil {
+		{
+			size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintExtendedHeader(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintExtendedHeader(dAtA []byte, offset int, v uint64) int {
+	offset -= sovExtendedHeader(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *ExtendedHeader) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Header != nil {
+		l = m.Header.Size()
+		n += 1 + l + sovExtendedHeader(uint64(l))
+	}
+	if m.Commit != nil {
+		l = m.Commit.Size()
+		n += 1 + l + sovExtendedHeader(uint64(l))
+	}
+	if m.ValidatorSet != nil {
+		l = m.ValidatorSet.Size()
+		n += 1 + l + sovExtendedHeader(uint64(l))
+	}
+	if m.Dah != nil {
+		l = m.Dah.Size()
+		n += 1 + l + sovExtendedHeader(uint64(l))
+	}
+	return n
+}
+
+func sovExtendedHeader(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozExtendedHeader(x uint64) (n int) {
+	return sovExtendedHeader(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *ExtendedHeader) 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 ErrIntOverflowExtendedHeader
+			}
+			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: ExtendedHeader: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ExtendedHeader: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowExtendedHeader
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Header == nil {
+				m.Header = &types.Header{}
+			}
+			if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Commit", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowExtendedHeader
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Commit == nil {
+				m.Commit = &types.Commit{}
+			}
+			if err := m.Commit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSet", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowExtendedHeader
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.ValidatorSet == nil {
+				m.ValidatorSet = &types.ValidatorSet{}
+			}
+			if err := m.ValidatorSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Dah", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowExtendedHeader
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Dah == nil {
+				m.Dah = &da.DataAvailabilityHeader{}
+			}
+			if err := m.Dah.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipExtendedHeader(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthExtendedHeader
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipExtendedHeader(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, ErrIntOverflowExtendedHeader
+			}
+			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, ErrIntOverflowExtendedHeader
+				}
+				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, ErrIntOverflowExtendedHeader
+				}
+				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, ErrInvalidLengthExtendedHeader
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupExtendedHeader
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthExtendedHeader
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthExtendedHeader        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowExtendedHeader          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupExtendedHeader = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/service/header/pb/extended_header.proto b/service/header/pb/extended_header.proto
new file mode 100644
index 00000000..6376a2f6
--- /dev/null
+++ b/service/header/pb/extended_header.proto
@@ -0,0 +1,17 @@
+syntax = "proto3";
+
+package header.pb;
+
+import "tendermint/types/types.proto";
+import "tendermint/types/validator.proto";
+import "tendermint/da/data_availability_header.proto";
+
+message ExtendedHeader {
+  tendermint.types.Header header = 1;
+  tendermint.types.Commit commit = 2;
+  tendermint.types.ValidatorSet validator_set = 3;
+  tendermint.da.DataAvailabilityHeader dah = 4;
+}
+
+// Generated with:
+// protoc -I=. -I=$(go list -f {{.Dir}} -m github.com/celestiaorg/celestia-core)/proto/ -I=$(go list -f {{.Dir}} -m github.com/gogo/protobuf)  --gogofaster_out . ./extended_header.proto
diff --git a/service/header/types.go b/service/header/types.go
deleted file mode 100644
index 6f7fbd5d..00000000
--- a/service/header/types.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package header
-
-import (
-	"github.com/celestiaorg/celestia-core/pkg/da"
-	core "github.com/celestiaorg/celestia-core/types"
-)
-
-// ExtendedHeader represents a wrapped "raw" header that includes
-// information necessary for Celestia Nodes to be notified of new
-// block headers and perform Data Availability Sampling.
-type ExtendedHeader struct {
-	*RawHeader
-	DAH *da.DataAvailabilityHeader
-}
-
-// RawHeader is an alias to core.Header. It is
-// "raw" because it is not yet wrapped to include
-// the DataAvailabilityHeader.
-type RawHeader = core.Header


Test patch:
diff --git a/node/init_test.go b/node/init_test.go
index 8d704df9..2ed02873 100644
--- a/node/init_test.go
+++ b/node/init_test.go
@@ -1,27 +1,77 @@
 package node
 
 import (
+	"os"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+
+	"github.com/celestiaorg/celestia-node/libs/fslock"
 )
 
-// TODO(@Bidon15): We need more test coverage for Init part
-// Tests could include invalid paths/configs and custom ones
-// For more info visit #89
-func TestInitFull(t *testing.T) {
+func TestInit(t *testing.T) {
 	dir := t.TempDir()
-	err := Init(dir, Full)
+	nodes := []Type{Light, Full}
+
+	for _, node := range nodes {
+		require.NoError(t, Init(dir, node))
+		assert.True(t, IsInit(dir, node))
+	}
+}
+
+func TestInitErrForInvalidPath(t *testing.T) {
+	path := "/invalid_path"
+	nodes := []Type{Light, Full}
+
+	for _, node := range nodes {
+		require.Error(t, Init(path, node))
+	}
+}
+
+func TestInitWithNilConfig(t *testing.T) {
+	dir := t.TempDir()
+	nodes := []Type{Light, Full}
+
+	for _, node := range nodes {
+		require.Error(t, InitWith(dir, node, nil))
+	}
+}
+
+func TestIsInitWithBrokenConfig(t *testing.T) {
+	dir := t.TempDir()
+	f, err := os.Create(configPath(dir))
 	require.NoError(t, err)
-	ok := IsInit(dir, Full)
-	assert.True(t, ok)
+	defer f.Close()
+	//nolint:errcheck
+	f.Write([]byte(`
+		[P2P]
+		  ListenAddresses = [/ip4/0.0.0.0/tcp/2121]
+    `))
+	nodes := []Type{Light, Full}
+
+	for _, node := range nodes {
+		assert.False(t, IsInit(dir, node))
+	}
 }
 
-func TestInitLight(t *testing.T) {
+func TestIsInitForNonExistDir(t *testing.T) {
+	path := "/invalid_path"
+	nodes := []Type{Light, Full}
+
+	for _, node := range nodes {
+		assert.False(t, IsInit(path, node))
+	}
+}
+
+func TestInitErrForLockedDir(t *testing.T) {
 	dir := t.TempDir()
-	err := Init(dir, Light)
+	flock, err := fslock.Lock(lockPath(dir))
 	require.NoError(t, err)
-	ok := IsInit(dir, Light)
-	assert.True(t, ok)
+	defer flock.Unlock() //nolint:errcheck
+	nodes := []Type{Light, Full}
+
+	for _, node := range nodes {
+		require.Error(t, Init(dir, node))
+	}
 }
diff --git a/service/header/header_serde_test.go b/service/header/header_serde_test.go
new file mode 100644
index 00000000..36c0301e
--- /dev/null
+++ b/service/header/header_serde_test.go
@@ -0,0 +1,23 @@
+package header
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestMarshalUnmarshalExtendedHeader(t *testing.T) {
+	in := RandExtendedHeader(t)
+	data, err := in.MarshalBinary()
+	require.NoError(t, err)
+
+	out := &ExtendedHeader{}
+	err = out.UnmarshalBinary(data)
+	require.NoError(t, err)
+	assert.Equal(t, in.ValidatorSet, out.ValidatorSet)
+	assert.True(t, in.DAH.Equals(out.DAH))
+	// not the check for equality as time.Time is not serialized exactly 1:1
+	assert.NotZero(t, out.RawHeader)
+	assert.NotNil(t, out.Commit)
+}
diff --git a/service/header/testing.go b/service/header/testing.go
new file mode 100644
index 00000000..d62793b7
--- /dev/null
+++ b/service/header/testing.go
@@ -0,0 +1,66 @@
+package header
+
+import (
+	mrand "math/rand"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/celestiaorg/celestia-core/pkg/da"
+
+	tmrand "github.com/celestiaorg/celestia-core/libs/rand"
+	tmproto "github.com/celestiaorg/celestia-core/proto/tendermint/types"
+	"github.com/celestiaorg/celestia-core/proto/tendermint/version"
+	core "github.com/celestiaorg/celestia-core/types"
+)
+
+// RandExtendedHeader provides an ExtendedHeader fixture.
+func RandExtendedHeader(t *testing.T) *ExtendedHeader {
+	rh := RandRawHeader(t)
+	valSet, vals := core.RandValidatorSet(5, 1)
+	voteSet := core.NewVoteSet(rh.ChainID, rh.Height, 0, tmproto.PrecommitType, valSet)
+	commit, err := core.MakeCommit(RandBlockID(t), rh.Height, 0, voteSet, vals, time.Now())
+	require.NoError(t, err)
+	dah := da.MinDataAvailabilityHeader()
+	return &ExtendedHeader{
+		RawHeader:    *rh,
+		Commit:       commit,
+		ValidatorSet: valSet,
+		DAH:          &dah,
+	}
+}
+
+// RandRawHeader provides a RawHeader fixture.
+func RandRawHeader(t *testing.T) *RawHeader {
+	return &RawHeader{
+		Version:            version.Consensus{Block: 11, App: 1},
+		ChainID:            "test",
+		Height:             mrand.Int63(), //nolint:gosec
+		Time:               time.Now(),
+		LastBlockID:        RandBlockID(t),
+		LastCommitHash:     tmrand.Bytes(32),
+		DataHash:           tmrand.Bytes(32),
+		ValidatorsHash:     tmrand.Bytes(32),
+		NextValidatorsHash: tmrand.Bytes(32),
+		ConsensusHash:      tmrand.Bytes(32),
+		AppHash:            tmrand.Bytes(32),
+		LastResultsHash:    tmrand.Bytes(32),
+		EvidenceHash:       tmrand.Bytes(32),
+		ProposerAddress:    tmrand.Bytes(20),
+	}
+}
+
+// RandBlockID provides a BlockID fixture.
+func RandBlockID(t *testing.T) core.BlockID {
+	bid := core.BlockID{
+		Hash: make([]byte, 32),
+		PartSetHeader: core.PartSetHeader{
+			Total: 123,
+			Hash:  make([]byte, 32),
+		},
+	}
+	mrand.Read(bid.Hash)               //nolint:gosec
+	mrand.Read(bid.PartSetHeader.Hash) //nolint:gosec
+	return bid
+}