Repository: kaspanet/kaspad
Pull request #1305 (2020-12-28 13:57:19)

Add mutable and immutable header interfaces
In order to save many copies, I added immutable interface to block header so block headers will be able to be moved safely.

ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master

Patch:
diff --git a/app/appmessage/domainconverters.go b/app/appmessage/domainconverters.go
index 46f52f9a..7496623d 100644
--- a/app/appmessage/domainconverters.go
+++ b/app/appmessage/domainconverters.go
@@ -2,6 +2,7 @@ package appmessage
 
 import (
 	"encoding/hex"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
 
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
@@ -21,17 +22,17 @@ func DomainBlockToMsgBlock(domainBlock *externalapi.DomainBlock) *MsgBlock {
 	}
 }
 
-// DomainBlockHeaderToBlockHeader converts an externalapi.DomainBlockHeader to MsgBlockHeader
-func DomainBlockHeaderToBlockHeader(domainBlockHeader *externalapi.DomainBlockHeader) *MsgBlockHeader {
+// DomainBlockHeaderToBlockHeader converts an externalapi.BlockHeader to MsgBlockHeader
+func DomainBlockHeaderToBlockHeader(domainBlockHeader externalapi.BlockHeader) *MsgBlockHeader {
 	return &MsgBlockHeader{
-		Version:              domainBlockHeader.Version,
-		ParentHashes:         domainBlockHeader.ParentHashes,
-		HashMerkleRoot:       &domainBlockHeader.HashMerkleRoot,
-		AcceptedIDMerkleRoot: &domainBlockHeader.AcceptedIDMerkleRoot,
-		UTXOCommitment:       &domainBlockHeader.UTXOCommitment,
-		Timestamp:            mstime.UnixMilliseconds(domainBlockHeader.TimeInMilliseconds),
-		Bits:                 domainBlockHeader.Bits,
-		Nonce:                domainBlockHeader.Nonce,
+		Version:              domainBlockHeader.Version(),
+		ParentHashes:         domainBlockHeader.ParentHashes(),
+		HashMerkleRoot:       domainBlockHeader.HashMerkleRoot(),
+		AcceptedIDMerkleRoot: domainBlockHeader.AcceptedIDMerkleRoot(),
+		UTXOCommitment:       domainBlockHeader.UTXOCommitment(),
+		Timestamp:            mstime.UnixMilliseconds(domainBlockHeader.TimeInMilliseconds()),
+		Bits:                 domainBlockHeader.Bits(),
+		Nonce:                domainBlockHeader.Nonce(),
 	}
 }
 
@@ -48,18 +49,18 @@ func MsgBlockToDomainBlock(msgBlock *MsgBlock) *externalapi.DomainBlock {
 	}
 }
 
-// BlockHeaderToDomainBlockHeader converts a MsgBlockHeader to externalapi.DomainBlockHeader
-func BlockHeaderToDomainBlockHeader(blockHeader *MsgBlockHeader) *externalapi.DomainBlockHeader {
-	return &externalapi.DomainBlockHeader{
-		Version:              blockHeader.Version,
-		ParentHashes:         blockHeader.ParentHashes,
-		HashMerkleRoot:       *blockHeader.HashMerkleRoot,
-		AcceptedIDMerkleRoot: *blockHeader.AcceptedIDMerkleRoot,
-		UTXOCommitment:       *blockHeader.UTXOCommitment,
-		TimeInMilliseconds:   blockHeader.Timestamp.UnixMilliseconds(),
-		Bits:                 blockHeader.Bits,
-		Nonce:                blockHeader.Nonce,
-	}
+// BlockHeaderToDomainBlockHeader converts a MsgBlockHeader to externalapi.BlockHeader
+func BlockHeaderToDomainBlockHeader(blockHeader *MsgBlockHeader) externalapi.BlockHeader {
+	return blockheader.NewImmutableBlockHeader(
+		blockHeader.Version,
+		blockHeader.ParentHashes,
+		blockHeader.HashMerkleRoot,
+		blockHeader.AcceptedIDMerkleRoot,
+		blockHeader.UTXOCommitment,
+		blockHeader.Timestamp.UnixMilliseconds(),
+		blockHeader.Bits,
+		blockHeader.Nonce,
+	)
 }
 
 // DomainTransactionToMsgTx converts an externalapi.DomainTransaction into an MsgTx
diff --git a/app/protocol/blocklogger/blocklogger.go b/app/protocol/blocklogger/blocklogger.go
index 81713aba..d17001b0 100644
--- a/app/protocol/blocklogger/blocklogger.go
+++ b/app/protocol/blocklogger/blocklogger.go
@@ -50,7 +50,7 @@ func LogBlock(block *externalapi.DomainBlock) {
 
 	log.Infof("Processed %d %s in the last %s (%d %s, %s)",
 		receivedLogBlocks, blockStr, tDuration, receivedLogTx,
-		txStr, mstime.UnixMilliseconds(block.Header.TimeInMilliseconds))
+		txStr, mstime.UnixMilliseconds(block.Header.TimeInMilliseconds()))
 
 	receivedLogBlocks = 0
 	receivedLogTx = 0
diff --git a/app/protocol/flowcontext/orphans.go b/app/protocol/flowcontext/orphans.go
index 9c563ba2..f655c4bc 100644
--- a/app/protocol/flowcontext/orphans.go
+++ b/app/protocol/flowcontext/orphans.go
@@ -71,10 +71,10 @@ func (f *FlowContext) UnorphanBlocks(rootBlock *externalapi.DomainBlock) ([]*Uno
 		orphanBlock := f.orphans[orphanHash]
 
 		log.Debugf("Considering to unorphan block %s with parents %s",
-			orphanHash, orphanBlock.Header.ParentHashes)
+			orphanHash, orphanBlock.Header.ParentHashes())
 
 		canBeUnorphaned := true
-		for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
+		for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes() {
 			orphanBlockParentInfo, err := f.domain.Consensus().GetBlockInfo(orphanBlockParentHash)
 			if err != nil {
 				return nil, err
@@ -131,7 +131,7 @@ func (f *FlowContext) addChildOrphansToProcessQueue(blockHash *externalapi.Domai
 func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash) []externalapi.DomainHash {
 	var childOrphans []externalapi.DomainHash
 	for orphanHash, orphanBlock := range f.orphans {
-		for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
+		for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes() {
 			if orphanBlockParentHash.Equal(blockHash) {
 				childOrphans = append(childOrphans, orphanHash)
 				break
diff --git a/app/protocol/flowcontext/sync_rate.go b/app/protocol/flowcontext/sync_rate.go
index b96036c6..b7c0f2d2 100644
--- a/app/protocol/flowcontext/sync_rate.go
+++ b/app/protocol/flowcontext/sync_rate.go
@@ -71,13 +71,13 @@ func (f *FlowContext) ShouldMine() (bool, error) {
 	}
 
 	now := mstime.Now().UnixMilliseconds()
-	if now-virtualSelectedParentHeader.TimeInMilliseconds < maxSelectedParentTimeDiffToAllowMiningInMilliSeconds {
+	if now-virtualSelectedParentHeader.TimeInMilliseconds() < maxSelectedParentTimeDiffToAllowMiningInMilliSeconds {
 		log.Debugf("The selected tip timestamp is recent (%d), so ShouldMine returns true",
-			virtualSelectedParentHeader.TimeInMilliseconds)
+			virtualSelectedParentHeader.TimeInMilliseconds())
 		return true, nil
 	}
 
 	log.Debugf("The selected tip timestamp is old (%d), so ShouldMine returns false",
-		virtualSelectedParentHeader.TimeInMilliseconds)
+		virtualSelectedParentHeader.TimeInMilliseconds())
 	return false, nil
 }
diff --git a/app/rpc/rpccontext/verbosedata.go b/app/rpc/rpccontext/verbosedata.go
index b18738d7..574e9ee9 100644
--- a/app/rpc/rpccontext/verbosedata.go
+++ b/app/rpc/rpccontext/verbosedata.go
@@ -23,8 +23,7 @@ import (
 )
 
 // BuildBlockVerboseData builds a BlockVerboseData from the given block.
-// This method must be called with the DAG lock held for reads
-func (ctx *Context) BuildBlockVerboseData(blockHeader *externalapi.DomainBlockHeader, includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
+func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
 	hash := consensushashing.HeaderHash(blockHeader)
 
 	blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(hash)
@@ -33,16 +32,16 @@ func (ctx *Context) BuildBlockVerboseData(blockHeader *externalapi.DomainBlockHe
 	}
 	result := &appmessage.BlockVerboseData{
 		Hash:                 hash.String(),
-		Version:              blockHeader.Version,
-		VersionHex:           fmt.Sprintf("%08x", blockHeader.Version),
-		HashMerkleRoot:       blockHeader.HashMerkleRoot.String(),
-		AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(),
-		UTXOCommitment:       blockHeader.UTXOCommitment.String(),
-		ParentHashes:         hashes.ToStrings(blockHeader.ParentHashes),
-		Nonce:                blockHeader.Nonce,
-		Time:                 blockHeader.TimeInMilliseconds,
-		Bits:                 strconv.FormatInt(int64(blockHeader.Bits), 16),
-		Difficulty:           ctx.GetDifficultyRatio(blockHeader.Bits, ctx.Config.ActiveNetParams),
+		Version:              blockHeader.Version(),
+		VersionHex:           fmt.Sprintf("%08x", blockHeader.Version()),
+		HashMerkleRoot:       blockHeader.HashMerkleRoot().String(),
+		AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot().String(),
+		UTXOCommitment:       blockHeader.UTXOCommitment().String(),
+		ParentHashes:         hashes.ToStrings(blockHeader.ParentHashes()),
+		Nonce:                blockHeader.Nonce(),
+		Time:                 blockHeader.TimeInMilliseconds(),
+		Bits:                 strconv.FormatInt(int64(blockHeader.Bits()), 16),
+		Difficulty:           ctx.GetDifficultyRatio(blockHeader.Bits(), ctx.Config.ActiveNetParams),
 		BlueScore:            blockInfo.BlueScore,
 		IsHeaderOnly:         blockInfo.BlockStatus == externalapi.StatusHeaderOnly,
 	}
@@ -97,7 +96,7 @@ func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) fl
 // BuildTransactionVerboseData builds a TransactionVerboseData from
 // the given parameters
 func (ctx *Context) BuildTransactionVerboseData(tx *externalapi.DomainTransaction, txID string,
-	blockHeader *externalapi.DomainBlockHeader, blockHash string) (
+	blockHeader externalapi.BlockHeader, blockHash string) (
 	*appmessage.TransactionVerboseData, error) {
 
 	var payloadHash string
@@ -120,8 +119,8 @@ func (ctx *Context) BuildTransactionVerboseData(tx *externalapi.DomainTransactio
 	}
 
 	if blockHeader != nil {
-		txReply.Time = uint64(blockHeader.TimeInMilliseconds)
-		txReply.BlockTime = uint64(blockHeader.TimeInMilliseconds)
+		txReply.Time = uint64(blockHeader.TimeInMilliseconds())
+		txReply.BlockTime = uint64(blockHeader.TimeInMilliseconds())
 		txReply.BlockHash = blockHash
 	}
 
diff --git a/cmd/kaspaminer/mineloop.go b/cmd/kaspaminer/mineloop.go
index 3d67bf67..96d244d5 100644
--- a/cmd/kaspaminer/mineloop.go
+++ b/cmd/kaspaminer/mineloop.go
@@ -95,7 +95,7 @@ func mineNextBlock(client *minerClient, miningAddr util.Address, foundBlock chan
 
 func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error {
 	blockHash := consensushashing.BlockHash(block)
-	log.Infof("Found block %s with parents %s. Submitting to %s", blockHash, block.Header.ParentHashes, client.Address())
+	log.Infof("Found block %s with parents %s. Submitting to %s", blockHash, block.Header.ParentHashes(), client.Address())
 
 	err := client.SubmitBlock(block)
 	if err != nil {
@@ -105,16 +105,18 @@ func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error
 }
 
 func solveBlock(block *externalapi.DomainBlock, stopChan chan struct{}, foundBlock chan *externalapi.DomainBlock) {
-	targetDifficulty := util.CompactToBig(block.Header.Bits)
+	targetDifficulty := util.CompactToBig(block.Header.Bits())
+	headerForMining := block.Header.ToMutable()
 	initialNonce := random.Uint64()
 	for i := initialNonce; i != initialNonce-1; i++ {
 		select {
 		case <-stopChan:
 			return
 		default:
-			block.Header.Nonce = i
+			headerForMining.SetNonce(i)
 			atomic.AddUint64(&hashesTried, 1)
-			if pow.CheckProofOfWorkWithTarget(block.Header, targetDifficulty) {
+			if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
+				block.Header = headerForMining.ToImmutable()
 				foundBlock <- block
 				return
 			}
diff --git a/domain/consensus/consensus.go b/domain/consensus/consensus.go
index 7bff7e69..8a01b263 100644
--- a/domain/consensus/consensus.go
+++ b/domain/consensus/consensus.go
@@ -103,7 +103,7 @@ func (s *consensus) GetBlock(blockHash *externalapi.DomainHash) (*externalapi.Do
 	return s.blockStore.Block(s.databaseContext, blockHash)
 }
 
-func (s *consensus) GetBlockHeader(blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
+func (s *consensus) GetBlockHeader(blockHash *externalapi.DomainHash) (externalapi.BlockHeader, error) {
 	s.lock.Lock()
 	defer s.lock.Unlock()
 
diff --git a/domain/consensus/database/serialization/blockheader.go b/domain/consensus/database/serialization/blockheader.go
index e8981daa..5a1bce9a 100644
--- a/domain/consensus/database/serialization/blockheader.go
+++ b/domain/consensus/database/serialization/blockheader.go
@@ -2,24 +2,25 @@ package serialization
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
 )
 
-// DomainBlockHeaderToDbBlockHeader converts DomainBlockHeader to DbBlockHeader
-func DomainBlockHeaderToDbBlockHeader(domainBlockHeader *externalapi.DomainBlockHeader) *DbBlockHeader {
+// DomainBlockHeaderToDbBlockHeader converts BlockHeader to DbBlockHeader
+func DomainBlockHeaderToDbBlockHeader(domainBlockHeader externalapi.BlockHeader) *DbBlockHeader {
 	return &DbBlockHeader{
-		Version:              domainBlockHeader.Version,
-		ParentHashes:         DomainHashesToDbHashes(domainBlockHeader.ParentHashes),
-		HashMerkleRoot:       DomainHashToDbHash(&domainBlockHeader.HashMerkleRoot),
-		AcceptedIDMerkleRoot: DomainHashToDbHash(&domainBlockHeader.AcceptedIDMerkleRoot),
-		UtxoCommitment:       DomainHashToDbHash(&domainBlockHeader.UTXOCommitment),
-		TimeInMilliseconds:   domainBlockHeader.TimeInMilliseconds,
-		Bits:                 domainBlockHeader.Bits,
-		Nonce:                domainBlockHeader.Nonce,
+		Version:              domainBlockHeader.Version(),
+		ParentHashes:         DomainHashesToDbHashes(domainBlockHeader.ParentHashes()),
+		HashMerkleRoot:       DomainHashToDbHash(domainBlockHeader.HashMerkleRoot()),
+		AcceptedIDMerkleRoot: DomainHashToDbHash(domainBlockHeader.AcceptedIDMerkleRoot()),
+		UtxoCommitment:       DomainHashToDbHash(domainBlockHeader.UTXOCommitment()),
+		TimeInMilliseconds:   domainBlockHeader.TimeInMilliseconds(),
+		Bits:                 domainBlockHeader.Bits(),
+		Nonce:                domainBlockHeader.Nonce(),
 	}
 }
 
-// DbBlockHeaderToDomainBlockHeader converts DbBlockHeader to DomainBlockHeader
-func DbBlockHeaderToDomainBlockHeader(dbBlockHeader *DbBlockHeader) (*externalapi.DomainBlockHeader, error) {
+// DbBlockHeaderToDomainBlockHeader converts DbBlockHeader to BlockHeader
+func DbBlockHeaderToDomainBlockHeader(dbBlockHeader *DbBlockHeader) (externalapi.BlockHeader, error) {
 	parentHashes, err := DbHashesToDomainHashes(dbBlockHeader.ParentHashes)
 	if err != nil {
 		return nil, err
@@ -37,14 +38,14 @@ func DbBlockHeaderToDomainBlockHeader(dbBlockHeader *DbBlockHeader) (*externalap
 		return nil, err
 	}
 
-	return &externalapi.DomainBlockHeader{
-		Version:              dbBlockHeader.Version,
-		ParentHashes:         parentHashes,
-		HashMerkleRoot:       *hashMerkleRoot,
-		AcceptedIDMerkleRoot: *acceptedIDMerkleRoot,
-		UTXOCommitment:       *utxoCommitment,
-		TimeInMilliseconds:   dbBlockHeader.TimeInMilliseconds,
-		Bits:                 dbBlockHeader.Bits,
-		Nonce:                dbBlockHeader.Nonce,
-	}, nil
+	return blockheader.NewImmutableBlockHeader(
+		dbBlockHeader.Version,
+		parentHashes,
+		hashMerkleRoot,
+		acceptedIDMerkleRoot,
+		utxoCommitment,
+		dbBlockHeader.TimeInMilliseconds,
+		dbBlockHeader.Bits,
+		dbBlockHeader.Nonce,
+	), nil
 }
diff --git a/domain/consensus/datastructures/blockheaderstore/blockheaderstore.go b/domain/consensus/datastructures/blockheaderstore/blockheaderstore.go
index ce80f707..e0169fec 100644
--- a/domain/consensus/datastructures/blockheaderstore/blockheaderstore.go
+++ b/domain/consensus/datastructures/blockheaderstore/blockheaderstore.go
@@ -14,7 +14,7 @@ var countKey = dbkeys.MakeBucket().Key([]byte("block-headers-count"))
 
 // blockHeaderStore represents a store of blocks
 type blockHeaderStore struct {
-	staging  map[externalapi.DomainHash]*externalapi.DomainBlockHeader
+	staging  map[externalapi.DomainHash]externalapi.BlockHeader
 	toDelete map[externalapi.DomainHash]struct{}
 	cache    *lrucache.LRUCache
 	count    uint64
@@ -23,7 +23,7 @@ type blockHeaderStore struct {
 // New instantiates a new BlockHeaderStore
 func New(dbContext model.DBReader, cacheSize int) (model.BlockHeaderStore, error) {
 	blockHeaderStore := &blockHeaderStore{
-		staging:  make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader),
+		staging:  make(map[externalapi.DomainHash]externalapi.BlockHeader),
 		toDelete: make(map[externalapi.DomainHash]struct{}),
 		cache:    lrucache.New(cacheSize),
 	}
@@ -57,8 +57,8 @@ func (bhs *blockHeaderStore) initializeCount(dbContext model.DBReader) error {
 }
 
 // Stage stages the given block header for the given blockHash
-func (bhs *blockHeaderStore) Stage(blockHash *externalapi.DomainHash, blockHeader *externalapi.DomainBlockHeader) {
-	bhs.staging[*blockHash] = blockHeader.Clone()
+func (bhs *blockHeaderStore) Stage(blockHash *externalapi.DomainHash, blockHeader externalapi.BlockHeader) {
+	bhs.staging[*blockHash] = blockHeader
 }
 
 func (bhs *blockHeaderStore) IsStaged() bool {
@@ -66,7 +66,7 @@ func (bhs *blockHeaderStore) IsStaged() bool {
 }
 
 func (bhs *blockHeaderStore) Discard() {
-	bhs.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader)
+	bhs.staging = make(map[externalapi.DomainHash]externalapi.BlockHeader)
 	bhs.toDelete = make(map[externalapi.DomainHash]struct{})
 }
 
@@ -101,13 +101,13 @@ func (bhs *blockHeaderStore) Commit(dbTx model.DBTransaction) error {
 }
 
 // BlockHeader gets the block header associated with the given blockHash
-func (bhs *blockHeaderStore) BlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
+func (bhs *blockHeaderStore) BlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (externalapi.BlockHeader, error) {
 	if header, ok := bhs.staging[*blockHash]; ok {
-		return header.Clone(), nil
+		return header, nil
 	}
 
 	if header, ok := bhs.cache.Get(blockHash); ok {
-		return header.(*externalapi.DomainBlockHeader).Clone(), nil
+		return header.(externalapi.BlockHeader), nil
 	}
 
 	headerBytes, err := dbContext.Get(bhs.hashAsKey(blockHash))
@@ -120,7 +120,7 @@ func (bhs *blockHeaderStore) BlockHeader(dbContext model.DBReader, blockHash *ex
 		return nil, err
 	}
 	bhs.cache.Add(blockHash, header)
-	return header.Clone(), nil
+	return header, nil
 }
 
 // HasBlock returns whether a block header with a given hash exists in the store.
@@ -142,8 +142,8 @@ func (bhs *blockHeaderStore) HasBlockHeader(dbContext model.DBReader, blockHash
 }
 
 // BlockHeaders gets the block headers associated with the given blockHashes
-func (bhs *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error) {
-	headers := make([]*externalapi.DomainBlockHeader, len(blockHashes))
+func (bhs *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]externalapi.BlockHeader, error) {
+	headers := make([]externalapi.BlockHeader, len(blockHashes))
 	for i, hash := range blockHashes {
 		var err error
 		headers[i], err = bhs.BlockHeader(dbContext, hash)
@@ -167,12 +167,12 @@ func (bhs *blockHeaderStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey
 	return bucket.Key(hash.ByteSlice())
 }
 
-func (bhs *blockHeaderStore) serializeHeader(header *externalapi.DomainBlockHeader) ([]byte, error) {
+func (bhs *blockHeaderStore) serializeHeader(header externalapi.BlockHeader) ([]byte, error) {
 	dbBlockHeader := serialization.DomainBlockHeaderToDbBlockHeader(header)
 	return proto.Marshal(dbBlockHeader)
 }
 
-func (bhs *blockHeaderStore) deserializeHeader(headerBytes []byte) (*externalapi.DomainBlockHeader, error) {
+func (bhs *blockHeaderStore) deserializeHeader(headerBytes []byte) (externalapi.BlockHeader, error) {
 	dbBlockHeader := &serialization.DbBlockHeader{}
 	err := proto.Unmarshal(headerBytes, dbBlockHeader)
 	if err != nil {
diff --git a/domain/consensus/model/externalapi/block.go b/domain/consensus/model/externalapi/block.go
index 1ff82a4c..c0244e6a 100644
--- a/domain/consensus/model/externalapi/block.go
+++ b/domain/consensus/model/externalapi/block.go
@@ -2,7 +2,7 @@ package externalapi
 
 // DomainBlock represents a Kaspa block
 type DomainBlock struct {
-	Header       *DomainBlockHeader
+	Header       BlockHeader
 	Transactions []*DomainTransaction
 }
 
@@ -14,14 +14,14 @@ func (block *DomainBlock) Clone() *DomainBlock {
 	}
 
 	return &DomainBlock{
-		Header:       block.Header.Clone(),
+		Header:       block.Header,
 		Transactions: transactionClone,
 	}
 }
 
 // If this doesn't compile, it means the type definition has been changed, so it's
 // an indication to update Equal and Clone accordingly.
-var _ = DomainBlock{&DomainBlockHeader{}, []*DomainTransaction{}}
+var _ = DomainBlock{nil, []*DomainTransaction{}}
 
 // Equal returns whether block equals to other
 func (block *DomainBlock) Equal(other *DomainBlock) bool {
@@ -46,74 +46,30 @@ func (block *DomainBlock) Equal(other *DomainBlock) bool {
 	return true
 }
 
-// DomainBlockHeader represents the header part of a Kaspa block
-type DomainBlockHeader struct {
-	Version              int32
-	ParentHashes         []*DomainHash
-	HashMerkleRoot       DomainHash
-	AcceptedIDMerkleRoot DomainHash
-	UTXOCommitment       DomainHash
-	TimeInMilliseconds   int64
-	Bits                 uint32
-	Nonce                uint64
+// BlockHeader represents an immutable block header.
+type BlockHeader interface {
+	BaseBlockHeader
+	ToMutable() MutableBlockHeader
 }
 
-// Clone returns a clone of DomainBlockHeader
-func (header *DomainBlockHeader) Clone() *DomainBlockHeader {
-	return &DomainBlockHeader{
-		Version:              header.Version,
-		ParentHashes:         CloneHashes(header.ParentHashes),
-		HashMerkleRoot:       header.HashMerkleRoot,
-		AcceptedIDMerkleRoot: header.AcceptedIDMerkleRoot,
-		UTXOCommitment:       header.UTXOCommitment,
-		TimeInMilliseconds:   header.TimeInMilliseconds,
-		Bits:                 header.Bits,
-		Nonce:                header.Nonce,
-	}
+// BaseBlockHeader represents the header part of a Kaspa block
+type BaseBlockHeader interface {
+	Version() int32
+	ParentHashes() []*DomainHash
+	HashMerkleRoot() *DomainHash
+	AcceptedIDMerkleRoot() *DomainHash
+	UTXOCommitment() *DomainHash
+	TimeInMilliseconds() int64
+	Bits() uint32
+	Nonce() uint64
+	Equal(other BaseBlockHeader) bool
 }
 
-// If this doesn't compile, it means the type definition has been changed, so it's
-// an indication to update Equal and Clone accordingly.
-var _ = &DomainBlockHeader{0, []*DomainHash{}, DomainHash{},
-	DomainHash{}, DomainHash{}, 0, 0, 0}
-
-// Equal returns whether header equals to other
-func (header *DomainBlockHeader) Equal(other *DomainBlockHeader) bool {
-	if header == nil || other == nil {
-		return header == other
-	}
-
-	if header.Version != other.Version {
-		return false
-	}
-
-	if !HashesEqual(header.ParentHashes, other.ParentHashes) {
-		return false
-	}
-
-	if !header.HashMerkleRoot.Equal(&other.HashMerkleRoot) {
-		return false
-	}
-
-	if !header.AcceptedIDMerkleRoot.Equal(&other.AcceptedIDMerkleRoot) {
-		return false
-	}
-
-	if !header.UTXOCommitment.Equal(&other.UTXOCommitment) {
-		return false
-	}
-
-	if header.TimeInMilliseconds != other.TimeInMilliseconds {
-		return false
-	}
-
-	if header.Bits != other.Bits {
-		return false
-	}
-
-	if header.Nonce != other.Nonce {
-		return false
-	}
-
-	return true
+// MutableBlockHeader represents a block header that can be mutated, but only
+// the fields that are relevant to mining (Nonce and TimeInMilliseconds).
+type MutableBlockHeader interface {
+	BaseBlockHeader
+	ToImmutable() BlockHeader
+	SetNonce(nonce uint64)
+	SetTimeInMilliseconds(timeInMilliseconds int64)
 }
diff --git a/domain/consensus/model/externalapi/consensus.go b/domain/consensus/model/externalapi/consensus.go
index 7ac38e21..45ea17e5 100644
--- a/domain/consensus/model/externalapi/consensus.go
+++ b/domain/consensus/model/externalapi/consensus.go
@@ -7,7 +7,7 @@ type Consensus interface {
 	ValidateTransactionAndPopulateWithConsensusData(transaction *DomainTransaction) error
 
 	GetBlock(blockHash *DomainHash) (*DomainBlock, error)
-	GetBlockHeader(blockHash *DomainHash) (*DomainBlockHeader, error)
+	GetBlockHeader(blockHash *DomainHash) (BlockHeader, error)
 	GetBlockInfo(blockHash *DomainHash) (*BlockInfo, error)
 	GetBlockAcceptanceData(blockHash *DomainHash) (AcceptanceData, error)
 
diff --git a/domain/consensus/model/interface_datastructures_blockheaderstore.go b/domain/consensus/model/interface_datastructures_blockheaderstore.go
index 20edf9c0..017ae628 100644
--- a/domain/consensus/model/interface_datastructures_blockheaderstore.go
+++ b/domain/consensus/model/interface_datastructures_blockheaderstore.go
@@ -5,11 +5,11 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 // BlockHeaderStore represents a store of block headers
 type BlockHeaderStore interface {
 	Store
-	Stage(blockHash *externalapi.DomainHash, blockHeader *externalapi.DomainBlockHeader)
+	Stage(blockHash *externalapi.DomainHash, blockHeader externalapi.BlockHeader)
 	IsStaged() bool
-	BlockHeader(dbContext DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error)
+	BlockHeader(dbContext DBReader, blockHash *externalapi.DomainHash) (externalapi.BlockHeader, error)
 	HasBlockHeader(dbContext DBReader, blockHash *externalapi.DomainHash) (bool, error)
-	BlockHeaders(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error)
+	BlockHeaders(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]externalapi.BlockHeader, error)
 	Delete(blockHash *externalapi.DomainHash)
 	Count() uint64
 }
diff --git a/domain/consensus/model/pow/pow.go b/domain/consensus/model/pow/pow.go
index 323fdb93..c584cc62 100644
--- a/domain/consensus/model/pow/pow.go
+++ b/domain/consensus/model/pow/pow.go
@@ -13,7 +13,7 @@ import (
 
 // CheckProofOfWorkWithTarget check's if the block has a valid PoW according to the provided target
 // it does not check if the difficulty itself is valid or less than the maximum for the appropriate network
-func CheckProofOfWorkWithTarget(header *externalapi.DomainBlockHeader, target *big.Int) bool {
+func CheckProofOfWorkWithTarget(header externalapi.MutableBlockHeader, target *big.Int) bool {
 	// The block pow must be less than the claimed target
 	powNum := calcPowValue(header)
 
@@ -23,17 +23,19 @@ func CheckProofOfWorkWithTarget(header *externalapi.DomainBlockHeader, target *b
 
 // CheckProofOfWorkByBits check's if the block has a valid PoW according to its Bits field
 // it does not check if the difficulty itself is valid or less than the maximum for the appropriate network
-func CheckProofOfWorkByBits(header *externalapi.DomainBlockHeader) bool {
-	return CheckProofOfWorkWithTarget(header, util.CompactToBig(header.Bits))
+func CheckProofOfWorkByBits(header externalapi.MutableBlockHeader) bool {
+	return CheckProofOfWorkWithTarget(header, util.CompactToBig(header.Bits()))
 }
 
-func calcPowValue(header *externalapi.DomainBlockHeader) *big.Int {
+func calcPowValue(header externalapi.MutableBlockHeader) *big.Int {
 	// Zero out the time and nonce.
-	timestamp, nonce := header.TimeInMilliseconds, header.Nonce
-	header.TimeInMilliseconds, header.Nonce = 0, 0
+	timestamp, nonce := header.TimeInMilliseconds(), header.Nonce()
+	header.SetTimeInMilliseconds(0)
+	header.SetNonce(0)
 
 	prePowHash := consensushashing.HeaderHash(header)
-	header.TimeInMilliseconds, header.Nonce = timestamp, nonce
+	header.SetTimeInMilliseconds(timestamp)
+	header.SetNonce(nonce)
 
 	// PRE_POW_HASH || TIME || 32 zero byte padding || NONCE
 	writer := hashes.NewPoWHashWriter()
diff --git a/domain/consensus/processes/blockbuilder/block_builder.go b/domain/consensus/processes/blockbuilder/block_builder.go
index 78b4bb11..1246e9bd 100644
--- a/domain/consensus/processes/blockbuilder/block_builder.go
+++ b/domain/consensus/processes/blockbuilder/block_builder.go
@@ -1,6 +1,7 @@
 package blockbuilder
 
 import (
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
 	"sort"
 
 	"github.com/kaspanet/kaspad/domain/consensus/model"
@@ -95,7 +96,7 @@ func (bb *blockBuilder) newBlockCoinbaseTransaction(
 	return bb.coinbaseManager.ExpectedCoinbaseTransaction(model.VirtualBlockHash, coinbaseData)
 }
 
-func (bb *blockBuilder) buildHeader(transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlockHeader, error) {
+func (bb *blockBuilder) buildHeader(transactions []*externalapi.DomainTransaction) (externalapi.BlockHeader, error) {
 	parentHashes, err := bb.newBlockParentHashes()
 	if err != nil {
 		return nil, err
@@ -118,15 +119,16 @@ func (bb *blockBuilder) buildHeader(transactions []*externalapi.DomainTransactio
 		return nil, err
 	}
 
-	return &externalapi.DomainBlockHeader{
-		Version:              constants.BlockVersion,
-		ParentHashes:         parentHashes,
-		HashMerkleRoot:       *hashMerkleRoot,
-		AcceptedIDMerkleRoot: *acceptedIDMerkleRoot,
-		UTXOCommitment:       *utxoCommitment,
-		TimeInMilliseconds:   timeInMilliseconds,
-		Bits:                 bits,
-	}, nil
+	return blockheader.NewImmutableBlockHeader(
+		constants.BlockVersion,
+		parentHashes,
+		hashMerkleRoot,
+		acceptedIDMerkleRoot,
+		utxoCommitment,
+		timeInMilliseconds,
+		bits,
+		0,
+	), nil
 }
 
 func (bb *blockBuilder) newBlockParentHashes() ([]*externalapi.DomainHash, error) {
diff --git a/domain/consensus/processes/blockvalidator/block_body_in_context.go b/domain/consensus/processes/blockvalidator/block_body_in_context.go
index d16421e9..ca8c3a74 100644
--- a/domain/consensus/processes/blockvalidator/block_body_in_context.go
+++ b/domain/consensus/processes/blockvalidator/block_body_in_context.go
@@ -37,7 +37,7 @@ func (v *blockValidator) checkParentBlockBodiesExist(blockHash *externalapi.Doma
 	if err != nil {
 		return err
 	}
-	for _, parent := range header.ParentHashes {
+	for _, parent := range header.ParentHashes() {
 		hasBlock, err := v.blockStore.HasBlock(v.databaseContext, parent)
 		if err != nil {
 			return err
@@ -84,7 +84,7 @@ func (v *blockValidator) checkBlockTransactionsFinalized(blockHash *externalapi.
 		return err
 	}
 
-	blockTime := block.Header.TimeInMilliseconds
+	blockTime := block.Header.TimeInMilliseconds()
 
 	ghostdagData, err := v.ghostdagDataStore.Get(v.databaseContext, blockHash)
 	if err != nil {
@@ -92,7 +92,7 @@ func (v *blockValidator) checkBlockTransactionsFinalized(blockHash *externalapi.
 	}
 
 	// If it's not genesis
-	if len(block.Header.ParentHashes) != 0 {
+	if len(block.Header.ParentHashes()) != 0 {
 		blockTime, err = v.pastMedianTimeManager.PastMedianTime(blockHash)
 		if err != nil {
 			return err
diff --git a/domain/consensus/processes/blockvalidator/block_body_in_isolation.go b/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
index 4daa95b9..aa0f6d45 100644
--- a/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
+++ b/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
@@ -143,10 +143,10 @@ func (v *blockValidator) checkTransactionsInIsolation(block *externalapi.DomainB
 
 func (v *blockValidator) checkBlockHashMerkleRoot(block *externalapi.DomainBlock) error {
 	calculatedHashMerkleRoot := merkle.CalculateHashMerkleRoot(block.Transactions)
-	if !block.Header.HashMerkleRoot.Equal(calculatedHashMerkleRoot) {
+	if !block.Header.HashMerkleRoot().Equal(calculatedHashMerkleRoot) {
 		return errors.Wrapf(ruleerrors.ErrBadMerkleRoot, "block hash merkle root is invalid - block "+
 			"header indicates %s, but calculated value is %s",
-			block.Header.HashMerkleRoot, calculatedHashMerkleRoot)
+			block.Header.HashMerkleRoot(), calculatedHashMerkleRoot)
 	}
 	return nil
 }
diff --git a/domain/consensus/processes/blockvalidator/block_header_in_context.go b/domain/consensus/processes/blockvalidator/block_header_in_context.go
index 63976e8b..dccc30b9 100644
--- a/domain/consensus/processes/blockvalidator/block_header_in_context.go
+++ b/domain/consensus/processes/blockvalidator/block_header_in_context.go
@@ -83,9 +83,9 @@ func (v *blockValidator) hasValidatedHeader(blockHash *externalapi.DomainHash) (
 }
 
 // checkParentsIncest validates that no parent is an ancestor of another parent
-func (v *blockValidator) checkParentsIncest(header *externalapi.DomainBlockHeader) error {
-	for _, parentA := range header.ParentHashes {
-		for _, parentB := range header.ParentHashes {
+func (v *blockValidator) checkParentsIncest(header externalapi.BlockHeader) error {
+	for _, parentA := range header.ParentHashes() {
+		for _, parentB := range header.ParentHashes() {
 			if parentA.Equal(parentB) {
 				continue
 			}
@@ -107,8 +107,8 @@ func (v *blockValidator) checkParentsIncest(header *externalapi.DomainBlockHeade
 	return nil
 }
 
-func (v *blockValidator) validateMedianTime(header *externalapi.DomainBlockHeader) error {
-	if len(header.ParentHashes) == 0 {
+func (v *blockValidator) validateMedianTime(header externalapi.BlockHeader) error {
+	if len(header.ParentHashes()) == 0 {
 		return nil
 	}
 
@@ -120,9 +120,9 @@ func (v *blockValidator) validateMedianTime(header *externalapi.DomainBlockHeade
 		return err
 	}
 
-	if header.TimeInMilliseconds <= pastMedianTime {
+	if header.TimeInMilliseconds() <= pastMedianTime {
 		return errors.Wrapf(ruleerrors.ErrTimeTooOld, "block timestamp of %d is not after expected %d",
-			header.TimeInMilliseconds, pastMedianTime)
+			header.TimeInMilliseconds(), pastMedianTime)
 	}
 
 	return nil
diff --git a/domain/consensus/processes/blockvalidator/block_header_in_isolation.go b/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
index 31ca02e4..f6de3184 100644
--- a/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
+++ b/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
@@ -33,21 +33,21 @@ func (v *blockValidator) ValidateHeaderInIsolation(blockHash *externalapi.Domain
 	return nil
 }
 
-func (v *blockValidator) checkParentsLimit(header *externalapi.DomainBlockHeader) error {
+func (v *blockValidator) checkParentsLimit(header externalapi.BlockHeader) error {
 	hash := consensushashing.HeaderHash(header)
-	if len(header.ParentHashes) == 0 && !hash.Equal(v.genesisHash) {
+	if len(header.ParentHashes()) == 0 && !hash.Equal(v.genesisHash) {
 		return errors.Wrapf(ruleerrors.ErrNoParents, "block has no parents")
 	}
 
-	if uint64(len(header.ParentHashes)) > uint64(v.maxBlockParents) {
+	if uint64(len(header.ParentHashes())) > uint64(v.maxBlockParents) {
 		return errors.Wrapf(ruleerrors.ErrTooManyParents, "block header has %d parents, but the maximum allowed amount "+
-			"is %d", len(header.ParentHashes), v.maxBlockParents)
+			"is %d", len(header.ParentHashes()), v.maxBlockParents)
 	}
 	return nil
 }
 
-func (v *blockValidator) checkBlockTimestampInIsolation(header *externalapi.DomainBlockHeader) error {
-	blockTimestamp := header.TimeInMilliseconds
+func (v *blockValidator) checkBlockTimestampInIsolation(header externalapi.BlockHeader) error {
+	blockTimestamp := header.TimeInMilliseconds()
 	now := mstime.Now().UnixMilliseconds()
 	maxCurrentTime := now + int64(v.timestampDeviationTolerance)*v.targetTimePerBlock.Milliseconds()
 	if blockTimestamp > maxCurrentTime {
diff --git a/domain/consensus/processes/blockvalidator/header_estimated_size.go b/domain/consensus/processes/blockvalidator/header_estimated_size.go
index 428bad74..025197ad 100644
--- a/domain/consensus/processes/blockvalidator/header_estimated_size.go
+++ b/domain/consensus/processes/blockvalidator/header_estimated_size.go
@@ -7,12 +7,12 @@ import (
 // headerEstimatedSerializedSize is the estimated size of a block header in some
 // serialization. This has to be deterministic, but not necessarily accurate, since
 // it's only used to check block size limit violation.
-func (v *blockValidator) headerEstimatedSerializedSize(header *externalapi.DomainBlockHeader) uint64 {
+func (v *blockValidator) headerEstimatedSerializedSize(header externalapi.BlockHeader) uint64 {
 	size := uint64(0)
 	size += 4 // Version (int32)
 
-	size += 8                                                             // number of parents (uint64)
-	size += uint64(externalapi.DomainHashSize * len(header.ParentHashes)) // parents
+	size += 8                                                               // number of parents (uint64)
+	size += uint64(externalapi.DomainHashSize * len(header.ParentHashes())) // parents
 
 	size += externalapi.DomainHashSize // HashMerkleRoot
 	size += externalapi.DomainHashSize // AcceptedIDMerkleRoot
diff --git a/domain/consensus/processes/blockvalidator/proof_of_work.go b/domain/consensus/processes/blockvalidator/proof_of_work.go
index 08b4548f..fa96c528 100644
--- a/domain/consensus/processes/blockvalidator/proof_of_work.go
+++ b/domain/consensus/processes/blockvalidator/proof_of_work.go
@@ -39,7 +39,7 @@ func (v *blockValidator) ValidatePruningPointViolationAndProofOfWorkAndDifficult
 		return err
 	}
 
-	err = v.dagTopologyManager.SetParents(blockHash, header.ParentHashes)
+	err = v.dagTopologyManager.SetParents(blockHash, header.ParentHashes())
 	if err != nil {
 		return err
 	}
@@ -65,8 +65,8 @@ func (v *blockValidator) validateDifficulty(blockHash *externalapi.DomainHash) e
 	if err != nil {
 		return err
 	}
-	if header.Bits != expectedBits {
-		return errors.Wrapf(ruleerrors.ErrUnexpectedDifficulty, "block difficulty of %d is not the expected value of %d", header.Bits, expectedBits)
+	if header.Bits() != expectedBits {
+		return errors.Wrapf(ruleerrors.ErrUnexpectedDifficulty, "block difficulty of %d is not the expected value of %d", header.Bits(), expectedBits)
 	}
 
 	return nil
@@ -79,9 +79,9 @@ func (v *blockValidator) validateDifficulty(blockHash *externalapi.DomainHash) e
 // The flags modify the behavior of this function as follows:
 //  - BFNoPoWCheck: The check to ensure the block hash is less than the target
 //    difficulty is not performed.
-func (v *blockValidator) checkProofOfWork(header *externalapi.DomainBlockHeader) error {
+func (v *blockValidator) checkProofOfWork(header externalapi.BlockHeader) error {
 	// The target difficulty must be larger than zero.
-	target := util.CompactToBig(header.Bits)
+	target := util.CompactToBig(header.Bits())
 	if target.Sign() <= 0 {
 		return errors.Wrapf(ruleerrors.ErrUnexpectedDifficulty, "block target difficulty of %064x is too low",
 			target)
@@ -95,7 +95,7 @@ func (v *blockValidator) checkProofOfWork(header *externalapi.DomainBlockHeader)
 
 	// The block pow must be valid unless the flag to avoid proof of work checks is set.
 	if !v.skipPoW {
-		valid := pow.CheckProofOfWorkWithTarget(header, target)
+		valid := pow.CheckProofOfWorkWithTarget(header.ToMutable(), target)
 		if !valid {
 			return errors.Wrap(ruleerrors.ErrInvalidPoW, "block has invalid proof of work")
 		}
@@ -103,9 +103,9 @@ func (v *blockValidator) checkProofOfWork(header *externalapi.DomainBlockHeader)
 	return nil
 }
 
-func (v *blockValidator) checkParentHeadersExist(header *externalapi.DomainBlockHeader) error {
+func (v *blockValidator) checkParentHeadersExist(header externalapi.BlockHeader) error {
 	missingParentHashes := []*externalapi.DomainHash{}
-	for _, parent := range header.ParentHashes {
+	for _, parent := range header.ParentHashes() {
 		parentHeaderExists, err := v.blockHeaderStore.HasBlockHeader(v.databaseContext, parent)
 		if err != nil {
 			return err
@@ -131,7 +131,7 @@ func (v *blockValidator) checkParentHeadersExist(header *externalapi.DomainBlock
 
 	return nil
 }
-func (v *blockValidator) checkPruningPointViolation(header *externalapi.DomainBlockHeader) error {
+func (v *blockValidator) checkPruningPointViolation(header externalapi.BlockHeader) error {
 	// check if the pruning point is on past of at least one parent of the header's parents.
 
 	hasPruningPoint, err := v.pruningStore.HasPruningPoint(v.databaseContext)
@@ -149,7 +149,7 @@ func (v *blockValidator) checkPruningPointViolation(header *externalapi.DomainBl
 		return err
 	}
 
-	isAncestorOfAny, err := v.dagTopologyManager.IsAncestorOfAny(pruningPoint, header.ParentHashes)
+	isAncestorOfAny, err := v.dagTopologyManager.IsAncestorOfAny(pruningPoint, header.ParentHashes())
 	if err != nil {
 		return err
 	}
diff --git a/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go b/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go
index 61f5d255..12f02844 100644
--- a/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go
+++ b/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go
@@ -62,11 +62,11 @@ func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalap
 		return err
 	}
 	log.Debugf("The UTXO commitment of the pruning point: %s",
-		newPruningPointHeader.UTXOCommitment)
+		newPruningPointHeader.UTXOCommitment())
 
-	if !newPruningPointHeader.UTXOCommitment.Equal(utxoSetMultiSet.Hash()) {
+	if !newPruningPointHeader.UTXOCommitment().Equal(utxoSetMultiSet.Hash()) {
 		return errors.Wrapf(ruleerrors.ErrBadPruningPointUTXOSet, "the expected multiset hash of the pruning "+
-			"point UTXO set is %s but got %s", newPruningPointHeader.UTXOCommitment, *utxoSetMultiSet.Hash())
+			"point UTXO set is %s but got %s", newPruningPointHeader.UTXOCommitment(), *utxoSetMultiSet.Hash())
 	}
 	log.Debugf("The new pruning point UTXO commitment validation passed")
 
diff --git a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
index 7dd6b299..b3d0b63c 100644
--- a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
+++ b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
@@ -103,10 +103,10 @@ func (csm *consensusStateManager) validateAcceptedIDMerkleRoot(block *externalap
 	defer log.Tracef("validateAcceptedIDMerkleRoot end for block %s", blockHash)
 
 	calculatedAcceptedIDMerkleRoot := calculateAcceptedIDMerkleRoot(acceptanceData)
-	if !block.Header.AcceptedIDMerkleRoot.Equal(calculatedAcceptedIDMerkleRoot) {
+	if !block.Header.AcceptedIDMerkleRoot().Equal(calculatedAcceptedIDMerkleRoot) {
 		return errors.Wrapf(ruleerrors.ErrBadMerkleRoot, "block %s accepted ID merkle root is invalid - block "+
 			"header indicates %s, but calculated value is %s",
-			blockHash, &block.Header.UTXOCommitment, calculatedAcceptedIDMerkleRoot)
+			blockHash, block.Header.UTXOCommitment(), calculatedAcceptedIDMerkleRoot)
 	}
 
 	return nil
@@ -119,9 +119,9 @@ func (csm *consensusStateManager) validateUTXOCommitment(
 	defer log.Tracef("validateUTXOCommitment end for block %s", blockHash)
 
 	multisetHash := multiset.Hash()
-	if !block.Header.UTXOCommitment.Equal(multisetHash) {
+	if !block.Header.UTXOCommitment().Equal(multisetHash) {
 		return errors.Wrapf(ruleerrors.ErrBadUTXOCommitment, "block %s UTXO commitment is invalid - block "+
-			"header indicates %s, but calculated value is %s", blockHash, &block.Header.UTXOCommitment, multisetHash)
+			"header indicates %s, but calculated value is %s", blockHash, block.Header.UTXOCommitment(), multisetHash)
 	}
 
 	return nil
diff --git a/domain/consensus/processes/difficultymanager/blockwindow.go b/domain/consensus/processes/difficultymanager/blockwindow.go
index 2fd45825..3f795f14 100644
--- a/domain/consensus/processes/difficultymanager/blockwindow.go
+++ b/domain/consensus/processes/difficultymanager/blockwindow.go
@@ -23,8 +23,8 @@ func (dm *difficultyManager) getDifficultyBlock(blockHash *externalapi.DomainHas
 		return difficultyBlock{}, err
 	}
 	return difficultyBlock{
-		timeInMilliseconds: header.TimeInMilliseconds,
-		Bits:               header.Bits,
+		timeInMilliseconds: header.TimeInMilliseconds(),
+		Bits:               header.Bits(),
 	}, nil
 }
 
diff --git a/domain/consensus/processes/difficultymanager/difficultymanager.go b/domain/consensus/processes/difficultymanager/difficultymanager.go
index 3d75318c..87939a8d 100644
--- a/domain/consensus/processes/difficultymanager/difficultymanager.go
+++ b/domain/consensus/processes/difficultymanager/difficultymanager.go
@@ -59,7 +59,7 @@ func (dm *difficultyManager) genesisBits() (uint32, error) {
 		return 0, err
 	}
 
-	return header.Bits, nil
+	return header.Bits(), nil
 }
 
 // RequiredDifficulty returns the difficulty required for some block
diff --git a/domain/consensus/processes/ghostdag2/ghostdagimpl.go b/domain/consensus/processes/ghostdag2/ghostdagimpl.go
index 05a3030f..23773708 100644
--- a/domain/consensus/processes/ghostdag2/ghostdagimpl.go
+++ b/domain/consensus/processes/ghostdag2/ghostdagimpl.go
@@ -113,7 +113,7 @@ func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error
 		if err != nil {
 			return err
 		}
-		myWork.Add(myWork, util.CalcWork(header.Bits))
+		myWork.Add(myWork, util.CalcWork(header.Bits()))
 	}
 
 	e := ghostdagmanager.NewBlockGHOSTDAGData(myScore, myWork, selectedParent, mergeSetBlues, mergeSetReds, nil)
diff --git a/domain/consensus/processes/ghostdagmanager/ghostdag.go b/domain/consensus/processes/ghostdagmanager/ghostdag.go
index f82fceb3..6eb056b6 100644
--- a/domain/consensus/processes/ghostdagmanager/ghostdag.go
+++ b/domain/consensus/processes/ghostdagmanager/ghostdag.go
@@ -88,7 +88,7 @@ func (gm *ghostdagManager) GHOSTDAG(blockHash *externalapi.DomainHash) error {
 			if err != nil {
 				return err
 			}
-			newBlockData.blueWork.Add(newBlockData.blueWork, util.CalcWork(header.Bits))
+			newBlockData.blueWork.Add(newBlockData.blueWork, util.CalcWork(header.Bits()))
 		}
 	} else {
 		// Genesis's blue score is defined to be 0.
diff --git a/domain/consensus/processes/pastmediantimemanager/pastmediantimemanager.go b/domain/consensus/processes/pastmediantimemanager/pastmediantimemanager.go
index fc496bbf..a8b3e94b 100644
--- a/domain/consensus/processes/pastmediantimemanager/pastmediantimemanager.go
+++ b/domain/consensus/processes/pastmediantimemanager/pastmediantimemanager.go
@@ -54,7 +54,7 @@ func (pmtm *pastMedianTimeManager) PastMedianTime(blockHash *externalapi.DomainH
 			return 0, err
 		}
 
-		return header.TimeInMilliseconds, nil
+		return header.TimeInMilliseconds(), nil
 	}
 
 	window, err := pmtm.dagTraversalManager.BlueWindow(selectedParentHash, 2*pmtm.timestampDeviationTolerance-1)
@@ -76,7 +76,7 @@ func (pmtm *pastMedianTimeManager) windowMedianTimestamp(window []*externalapi.D
 		if err != nil {
 			return 0, err
 		}
-		timestamps[i] = blockHeader.TimeInMilliseconds
+		timestamps[i] = blockHeader.TimeInMilliseconds()
 	}
 
 	sort.Slice(timestamps, func(i, j int) bool {
diff --git a/domain/consensus/utils/blockheader/blockheader.go b/domain/consensus/utils/blockheader/blockheader.go
new file mode 100644
index 00000000..0ad3776c
--- /dev/null
+++ b/domain/consensus/utils/blockheader/blockheader.go
@@ -0,0 +1,146 @@
+package blockheader
+
+import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+
+type blockHeader struct {
+	version              int32
+	parentHashes         []*externalapi.DomainHash
+	hashMerkleRoot       *externalapi.DomainHash
+	acceptedIDMerkleRoot *externalapi.DomainHash
+	utxoCommitment       *externalapi.DomainHash
+	timeInMilliseconds   int64
+	bits                 uint32
+	nonce                uint64
+}
+
+func (bh *blockHeader) ToImmutable() externalapi.BlockHeader {
+	return bh.clone()
+}
+
+func (bh *blockHeader) SetNonce(nonce uint64) {
+	bh.nonce = nonce
+}
+
+func (bh *blockHeader) SetTimeInMilliseconds(timeInMilliseconds int64) {
+	bh.timeInMilliseconds = timeInMilliseconds
+}
+
+func (bh *blockHeader) Version() int32 {
+	return bh.version
+}
+
+func (bh *blockHeader) ParentHashes() []*externalapi.DomainHash {
+	return bh.parentHashes
+}
+
+func (bh *blockHeader) HashMerkleRoot() *externalapi.DomainHash {
+	return bh.hashMerkleRoot
+}
+
+func (bh *blockHeader) AcceptedIDMerkleRoot() *externalapi.DomainHash {
+	return bh.acceptedIDMerkleRoot
+}
+
+func (bh *blockHeader) UTXOCommitment() *externalapi.DomainHash {
+	return bh.utxoCommitment
+}
+
+func (bh *blockHeader) TimeInMilliseconds() int64 {
+	return bh.timeInMilliseconds
+}
+
+func (bh *blockHeader) Bits() uint32 {
+	return bh.bits
+}
+
+func (bh *blockHeader) Nonce() uint64 {
+	return bh.nonce
+}
+
+func (bh *blockHeader) Equal(other externalapi.BaseBlockHeader) bool {
+	if bh == nil || other == nil {
+		return bh == other
+	}
+
+	// If only the underlying value of other is nil it'll
+	// make `other == nil` return false, so we check it
+	// explicitly.
+	downcastedOther := other.(*blockHeader)
+	if bh == nil || downcastedOther == nil {
+		return bh == downcastedOther
+	}
+
+	if bh.version != other.Version() {
+		return false
+	}
+
+	if !externalapi.HashesEqual(bh.parentHashes, other.ParentHashes()) {
+		return false
+	}
+
+	if !bh.hashMerkleRoot.Equal(other.HashMerkleRoot()) {
+		return false
+	}
+
+	if !bh.acceptedIDMerkleRoot.Equal(other.AcceptedIDMerkleRoot()) {
+		return false
+	}
+
+	if !bh.utxoCommitment.Equal(other.UTXOCommitment()) {
+		return false
+	}
+
+	if bh.timeInMilliseconds != other.TimeInMilliseconds() {
+		return false
+	}
+
+	if bh.bits != other.Bits() {
+		return false
+	}
+
+	if bh.nonce != other.Nonce() {
+		return false
+	}
+
+	return true
+}
+
+func (bh *blockHeader) clone() *blockHeader {
+	return &blockHeader{
+		version:              bh.version,
+		parentHashes:         externalapi.CloneHashes(bh.parentHashes),
+		hashMerkleRoot:       bh.hashMerkleRoot,
+		acceptedIDMerkleRoot: bh.acceptedIDMerkleRoot,
+		utxoCommitment:       bh.utxoCommitment,
+		timeInMilliseconds:   bh.timeInMilliseconds,
+		bits:                 bh.bits,
+		nonce:                bh.nonce,
+	}
+}
+
+func (bh *blockHeader) ToMutable() externalapi.MutableBlockHeader {
+	return bh.clone()
+}
+
+// NewImmutableBlockHeader returns a new immutable header
+func NewImmutableBlockHeader(
+	version int32,
+	parentHashes []*externalapi.DomainHash,
+	hashMerkleRoot *externalapi.DomainHash,
+	acceptedIDMerkleRoot *externalapi.DomainHash,
+	utxoCommitment *externalapi.DomainHash,
+	timeInMilliseconds int64,
+	bits uint32,
+	nonce uint64,
+) externalapi.BlockHeader {
+	return &blockHeader{
+		version:              version,
+		parentHashes:         parentHashes,
+		hashMerkleRoot:       hashMerkleRoot,
+		acceptedIDMerkleRoot: acceptedIDMerkleRoot,
+		utxoCommitment:       utxoCommitment,
+		timeInMilliseconds:   timeInMilliseconds,
+		bits:                 bits,
+		nonce:                nonce,
+	}
+}
diff --git a/domain/consensus/utils/consensushashing/block.go b/domain/consensus/utils/consensushashing/block.go
index 83c5d5ed..ea6b1026 100644
--- a/domain/consensus/utils/consensushashing/block.go
+++ b/domain/consensus/utils/consensushashing/block.go
@@ -16,7 +16,7 @@ func BlockHash(block *externalapi.DomainBlock) *externalapi.DomainHash {
 }
 
 // HeaderHash returns the given header's hash
-func HeaderHash(header *externalapi.DomainBlockHeader) *externalapi.DomainHash {
+func HeaderHash(header externalapi.BaseBlockHeader) *externalapi.DomainHash {
 	// Encode the header and hash everything prior to the number of
 	// transactions.
 	writer := hashes.NewBlockHashWriter()
@@ -31,18 +31,18 @@ func HeaderHash(header *externalapi.DomainBlockHeader) *externalapi.DomainHash {
 	return writer.Finalize()
 }
 
-func serializeHeader(w io.Writer, header *externalapi.DomainBlockHeader) error {
-	timestamp := header.TimeInMilliseconds
+func serializeHeader(w io.Writer, header externalapi.BaseBlockHeader) error {
+	timestamp := header.TimeInMilliseconds()
 
-	numParents := len(header.ParentHashes)
-	if err := serialization.WriteElements(w, header.Version, uint64(numParents)); err != nil {
+	numParents := len(header.ParentHashes())
+	if err := serialization.WriteElements(w, header.Version(), uint64(numParents)); err != nil {
 		return err
 	}
-	for _, hash := range header.ParentHashes {
+	for _, hash := range header.ParentHashes() {
 		if err := serialization.WriteElement(w, hash); err != nil {
 			return err
 		}
 	}
-	return serialization.WriteElements(w, &header.HashMerkleRoot, &header.AcceptedIDMerkleRoot, &header.UTXOCommitment, timestamp,
-		header.Bits, header.Nonce)
+	return serialization.WriteElements(w, header.HashMerkleRoot(), header.AcceptedIDMerkleRoot(), header.UTXOCommitment(), timestamp,
+		header.Bits(), header.Nonce())
 }
diff --git a/domain/consensus/utils/mining/solve.go b/domain/consensus/utils/mining/solve.go
index f5274f7b..2188f8a3 100644
--- a/domain/consensus/utils/mining/solve.go
+++ b/domain/consensus/utils/mining/solve.go
@@ -12,11 +12,12 @@ import (
 
 // SolveBlock increments the given block's nonce until it matches the difficulty requirements in its bits field
 func SolveBlock(block *externalapi.DomainBlock, rd *rand.Rand) {
-	targetDifficulty := utilsMath.CompactToBig(block.Header.Bits)
-
+	targetDifficulty := utilsMath.CompactToBig(block.Header.Bits())
+	headerForMining := block.Header.ToMutable()
 	for i := rd.Uint64(); i < math.MaxUint64; i++ {
-		block.Header.Nonce = i
-		if pow.CheckProofOfWorkWithTarget(block.Header, targetDifficulty) {
+		headerForMining.SetNonce(i)
+		if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
+			block.Header = headerForMining.ToImmutable()
 			return
 		}
 	}
diff --git a/domain/dagconfig/genesis.go b/domain/dagconfig/genesis.go
index 22362e4e..088f03e9 100644
--- a/domain/dagconfig/genesis.go
+++ b/domain/dagconfig/genesis.go
@@ -6,6 +6,7 @@ package dagconfig
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
 )
@@ -46,16 +47,16 @@ var genesisMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externalapi.Dom
 // genesisBlock defines the genesis block of the block DAG which serves as the
 // public transaction ledger for the main network.
 var genesisBlock = externalapi.DomainBlock{
-	Header: &externalapi.DomainBlockHeader{
-		Version:              1,
-		ParentHashes:         []*externalapi.DomainHash{},
-		HashMerkleRoot:       *genesisMerkleRoot,
-		AcceptedIDMerkleRoot: externalapi.DomainHash{},
-		UTXOCommitment:       externalapi.DomainHash{},
-		TimeInMilliseconds:   0x1763db5c4a9,
-		Bits:                 0x207fffff,
-		Nonce:                0x1,
-	},
+	Header: blockheader.NewImmutableBlockHeader(
+		1,
+		[]*externalapi.DomainHash{},
+		genesisMerkleRoot,
+		&externalapi.DomainHash{},
+		&externalapi.DomainHash{},
+		0x1763db5c4a9,
+		0x207fffff,
+		0x1,
+	),
 	Transactions: []*externalapi.DomainTransaction{genesisCoinbaseTx},
 }
 
@@ -97,16 +98,16 @@ var devnetGenesisMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externala
 // devnetGenesisBlock defines the genesis block of the block DAG which serves as the
 // public transaction ledger for the development network.
 var devnetGenesisBlock = externalapi.DomainBlock{
-	Header: &externalapi.DomainBlockHeader{
-		Version:              1,
-		ParentHashes:         []*externalapi.DomainHash{},
-		HashMerkleRoot:       *devnetGenesisMerkleRoot,
-		AcceptedIDMerkleRoot: externalapi.DomainHash{},
-		UTXOCommitment:       externalapi.DomainHash{},
-		TimeInMilliseconds:   0x1763db5c4a9,
-		Bits:                 0x1e7fffff,
-		Nonce:                0xb6c8,
-	},
+	Header: blockheader.NewImmutableBlockHeader(
+		1,
+		[]*externalapi.DomainHash{},
+		devnetGenesisMerkleRoot,
+		&externalapi.DomainHash{},
+		&externalapi.DomainHash{},
+		0x1763db5c4a9,
+		0x1e7fffff,
+		0xb6c8,
+	),
 	Transactions: []*externalapi.DomainTransaction{devnetGenesisCoinbaseTx},
 }
 
@@ -147,16 +148,16 @@ var simnetGenesisMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externala
 // simnetGenesisBlock defines the genesis block of the block DAG which serves as the
 // public transaction ledger for the development network.
 var simnetGenesisBlock = externalapi.DomainBlock{
-	Header: &externalapi.DomainBlockHeader{
-		Version:              1,
-		ParentHashes:         []*externalapi.DomainHash{},
-		HashMerkleRoot:       *simnetGenesisMerkleRoot,
-		AcceptedIDMerkleRoot: externalapi.DomainHash{},
-		UTXOCommitment:       externalapi.DomainHash{},
-		TimeInMilliseconds:   0x1763db5c4a9,
-		Bits:                 0x207fffff,
-		Nonce:                0x0,
-	},
+	Header: blockheader.NewImmutableBlockHeader(
+		1,
+		[]*externalapi.DomainHash{},
+		simnetGenesisMerkleRoot,
+		&externalapi.DomainHash{},
+		&externalapi.DomainHash{},
+		0x1763db5c4a9,
+		0x207fffff,
+		0x0,
+	),
 	Transactions: []*externalapi.DomainTransaction{simnetGenesisCoinbaseTx},
 }
 
@@ -195,15 +196,15 @@ var testnetGenesisMerkleRoot = externalapi.NewDomainHashFromByteArray(&[external
 // testnetGenesisBlock defines the genesis block of the block DAG which serves as the
 // public transaction ledger for testnet.
 var testnetGenesisBlock = externalapi.DomainBlock{
-	Header: &externalapi.DomainBlockHeader{
-		Version:              1,
-		ParentHashes:         []*externalapi.DomainHash{},
-		HashMerkleRoot:       *testnetGenesisMerkleRoot,
-		AcceptedIDMerkleRoot: externalapi.DomainHash{},
-		UTXOCommitment:       externalapi.DomainHash{},
-		TimeInMilliseconds:   0x1763db5c4a9,
-		Bits:                 0x1e7fffff,
-		Nonce:                0x493d,
-	},
+	Header: blockheader.NewImmutableBlockHeader(
+		1,
+		[]*externalapi.DomainHash{},
+		testnetGenesisMerkleRoot,
+		&externalapi.DomainHash{},
+		&externalapi.DomainHash{},
+		0x1763db5c4a9,
+		0x1e7fffff,
+		0x493d,
+	),
 	Transactions: []*externalapi.DomainTransaction{testnetGenesisCoinbaseTx},
 }
diff --git a/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go b/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go
index 1f216f33..55906375 100644
--- a/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go
+++ b/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go
@@ -148,7 +148,7 @@ func (btb *blockTemplateBuilder) GetBlockTemplate(coinbaseData *consensusexterna
 	}
 
 	log.Debugf("Created new block template (%d transactions, %d in fees, %d mass, target difficulty %064x)",
-		len(blk.Transactions), blockTxs.totalFees, blockTxs.totalMass, util.CompactToBig(blk.Header.Bits))
+		len(blk.Transactions), blockTxs.totalFees, blockTxs.totalMass, util.CompactToBig(blk.Header.Bits()))
 
 	return blk, nil
 }
diff --git a/infrastructure/config/network.go b/infrastructure/config/network.go
index a2b49033..edaadb66 100644
--- a/infrastructure/config/network.go
+++ b/infrastructure/config/network.go
@@ -165,7 +165,7 @@ func (networkFlags *NetworkFlags) overrideDAGParams() error {
 			return errors.Errorf("couldn't convert %s to big int", *config.PowMax)
 		}
 
-		genesisTarget := math.CompactToBig(networkFlags.ActiveNetParams.GenesisBlock.Header.Bits)
+		genesisTarget := math.CompactToBig(networkFlags.ActiveNetParams.GenesisBlock.Header.Bits())
 		if powMax.Cmp(genesisTarget) > 0 {
 			return errors.Errorf("powMax (%s) is smaller than genesis's target (%s)", powMax.Text(16),
 				genesisTarget.Text(16))


Test patch:
diff --git a/domain/consensus/consensus_test.go b/domain/consensus/consensus_test.go
index 021811a1..6632d124 100644
--- a/domain/consensus/consensus_test.go
+++ b/domain/consensus/consensus_test.go
@@ -24,7 +24,9 @@ func TestConsensus_GetBlockInfo(t *testing.T) {
 		if err != nil {
 			t.Fatal(err)
 		}
-		invalidBlock.Header.TimeInMilliseconds = 0
+		newHeader := invalidBlock.Header.ToMutable()
+		newHeader.SetTimeInMilliseconds(0)
+		invalidBlock.Header = newHeader.ToImmutable()
 		_, err = consensus.ValidateAndInsertBlock(invalidBlock)
 		if !errors.Is(err, ruleerrors.ErrTimeTooOld) {
 			t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrTimeTooOld, err)
diff --git a/domain/consensus/model/externalapi/block_equal_clone_test.go b/domain/consensus/model/externalapi/block_equal_clone_test.go
index 35dd5030..9ef4890c 100644
--- a/domain/consensus/model/externalapi/block_equal_clone_test.go
+++ b/domain/consensus/model/externalapi/block_equal_clone_test.go
@@ -1,30 +1,32 @@
-package externalapi
+package externalapi_test
 
 import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
 	"reflect"
 	"testing"
 )
 
 type blockToCompare struct {
-	block          *DomainBlock
+	block          *externalapi.DomainBlock
 	expectedResult bool
 }
 
 type TestBlockStruct struct {
-	baseBlock         *DomainBlock
+	baseBlock         *externalapi.DomainBlock
 	blocksToCompareTo []blockToCompare
 }
 
-func initTestBaseTransactions() []*DomainTransaction {
+func initTestBaseTransactions() []*externalapi.DomainTransaction {
 
-	testTx := []*DomainTransaction{{
+	testTx := []*externalapi.DomainTransaction{{
 		Version:      1,
-		Inputs:       []*DomainTransactionInput{},
-		Outputs:      []*DomainTransactionOutput{},
+		Inputs:       []*externalapi.DomainTransactionInput{},
+		Outputs:      []*externalapi.DomainTransactionOutput{},
 		LockTime:     1,
-		SubnetworkID: DomainSubnetworkID{0x01},
+		SubnetworkID: externalapi.DomainSubnetworkID{0x01},
 		Gas:          1,
-		PayloadHash: *NewDomainHashFromByteArray(&[DomainHashSize]byte{
+		PayloadHash: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -32,7 +34,7 @@ func initTestBaseTransactions() []*DomainTransaction {
 		Payload: []byte{0x01},
 		Fee:     0,
 		Mass:    1,
-		ID: NewDomainTransactionIDFromByteArray(&[DomainHashSize]byte{
+		ID: externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -41,16 +43,16 @@ func initTestBaseTransactions() []*DomainTransaction {
 	return testTx
 }
 
-func initTestAnotherTransactions() []*DomainTransaction {
+func initTestAnotherTransactions() []*externalapi.DomainTransaction {
 
-	testTx := []*DomainTransaction{{
+	testTx := []*externalapi.DomainTransaction{{
 		Version:      1,
-		Inputs:       []*DomainTransactionInput{},
-		Outputs:      []*DomainTransactionOutput{},
+		Inputs:       []*externalapi.DomainTransactionInput{},
+		Outputs:      []*externalapi.DomainTransactionOutput{},
 		LockTime:     1,
-		SubnetworkID: DomainSubnetworkID{0x01},
+		SubnetworkID: externalapi.DomainSubnetworkID{0x01},
 		Gas:          1,
-		PayloadHash: *NewDomainHashFromByteArray(&[DomainHashSize]byte{
+		PayloadHash: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -58,7 +60,7 @@ func initTestAnotherTransactions() []*DomainTransaction {
 		Payload: []byte{0x01},
 		Fee:     0,
 		Mass:    1,
-		ID: NewDomainTransactionIDFromByteArray(&[DomainHashSize]byte{
+		ID: externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -67,16 +69,16 @@ func initTestAnotherTransactions() []*DomainTransaction {
 	return testTx
 }
 
-func initTestTwoTransactions() []*DomainTransaction {
+func initTestTwoTransactions() []*externalapi.DomainTransaction {
 
-	testTx := []*DomainTransaction{{
+	testTx := []*externalapi.DomainTransaction{{
 		Version:      1,
-		Inputs:       []*DomainTransactionInput{},
-		Outputs:      []*DomainTransactionOutput{},
+		Inputs:       []*externalapi.DomainTransactionInput{},
+		Outputs:      []*externalapi.DomainTransactionOutput{},
 		LockTime:     1,
-		SubnetworkID: DomainSubnetworkID{0x01},
+		SubnetworkID: externalapi.DomainSubnetworkID{0x01},
 		Gas:          1,
-		PayloadHash: *NewDomainHashFromByteArray(&[DomainHashSize]byte{
+		PayloadHash: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -84,19 +86,19 @@ func initTestTwoTransactions() []*DomainTransaction {
 		Payload: []byte{0x01},
 		Fee:     0,
 		Mass:    1,
-		ID: NewDomainTransactionIDFromByteArray(&[DomainHashSize]byte{
+		ID: externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}),
 	}, {
 		Version:      1,
-		Inputs:       []*DomainTransactionInput{},
-		Outputs:      []*DomainTransactionOutput{},
+		Inputs:       []*externalapi.DomainTransactionInput{},
+		Outputs:      []*externalapi.DomainTransactionOutput{},
 		LockTime:     1,
-		SubnetworkID: DomainSubnetworkID{0x01},
+		SubnetworkID: externalapi.DomainSubnetworkID{0x01},
 		Gas:          1,
-		PayloadHash: *NewDomainHashFromByteArray(&[DomainHashSize]byte{
+		PayloadHash: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -104,7 +106,7 @@ func initTestTwoTransactions() []*DomainTransaction {
 		Payload: []byte{0x01},
 		Fee:     0,
 		Mass:    1,
-		ID: NewDomainTransactionIDFromByteArray(&[DomainHashSize]byte{
+		ID: externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -113,34 +115,34 @@ func initTestTwoTransactions() []*DomainTransaction {
 	return testTx
 }
 
-func initTestBlockStructsForClone() []*DomainBlock {
+func initTestBlockStructsForClone() []*externalapi.DomainBlock {
 
-	tests := []*DomainBlock{
+	tests := []*externalapi.DomainBlock{
 		{
-			&DomainBlockHeader{
+			blockheader.NewImmutableBlockHeader(
 
 				0,
-				[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{0})},
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{1}),
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
+				[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{0})},
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1}),
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
 				4,
 				5,
 				6,
-			},
+			),
 			initTestBaseTransactions(),
 		}, {
-			&DomainBlockHeader{
+			blockheader.NewImmutableBlockHeader(
 
 				0,
-				[]*DomainHash{},
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{1}),
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
+				[]*externalapi.DomainHash{},
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1}),
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
 				4,
 				5,
 				6,
-			},
+			),
 			initTestBaseTransactions(),
 		},
 	}
@@ -158,33 +160,33 @@ func initTestBlockStructsForEqual() *[]TestBlockStruct {
 					expectedResult: true,
 				},
 				{
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{0})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{1}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{0})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
 							4,
 							5,
 							6,
-						},
+						),
 						initTestBaseTransactions()},
 					expectedResult: false,
 				},
 			},
 		}, {
-			baseBlock: &DomainBlock{
-				&DomainBlockHeader{
+			baseBlock: &externalapi.DomainBlock{
+				blockheader.NewImmutableBlockHeader(
 					0,
-					[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-					*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-					*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-					*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+					[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+					externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+					externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+					externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 					5,
 					6,
 					7,
-				},
+				),
 				initTestBaseTransactions(),
 			},
 			blocksToCompareTo: []blockToCompare{
@@ -193,155 +195,155 @@ func initTestBlockStructsForEqual() *[]TestBlockStruct {
 					expectedResult: false,
 				},
 				{
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							5,
 							6,
 							7,
-						},
+						),
 						initTestAnotherTransactions(),
 					},
 					expectedResult: false,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							5,
 							6,
 							7,
-						},
+						),
 						initTestBaseTransactions(),
 					},
 					expectedResult: true,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{
-								NewDomainHashFromByteArray(&[DomainHashSize]byte{1}),
-								NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
+							[]*externalapi.DomainHash{
+								externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1}),
+								externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
 							},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							5,
 							6,
 							7,
-						},
+						),
 						initTestBaseTransactions(),
 					},
 					expectedResult: false,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{100})}, // Changed
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100})}, // Changed
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							5,
 							6,
 							7,
-						},
+						),
 						initTestTwoTransactions(),
 					},
 					expectedResult: false,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{100}), // Changed
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100}), // Changed
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							5,
 							6,
 							7,
-						},
+						),
 						initTestBaseTransactions(),
 					},
 					expectedResult: false,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{100}), // Changed
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100}), // Changed
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							5,
 							6,
 							7,
-						},
+						),
 						initTestBaseTransactions(),
 					},
 					expectedResult: false,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{100}), // Changed
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100}), // Changed
 							5,
 							6,
 							7,
-						},
+						),
 						initTestBaseTransactions(),
 					},
 					expectedResult: false,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							100, // Changed
 							6,
 							7,
-						},
+						),
 						initTestBaseTransactions(),
 					},
 					expectedResult: false,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							5,
 							100, // Changed
 							7,
-						},
+						),
 						initTestBaseTransactions(),
 					},
 					expectedResult: false,
 				}, {
-					block: &DomainBlock{
-						&DomainBlockHeader{
+					block: &externalapi.DomainBlock{
+						blockheader.NewImmutableBlockHeader(
 							0,
-							[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-							*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
+							[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
 							5,
 							6,
 							100, // Changed
-						},
+						),
 						initTestBaseTransactions(),
 					},
 					expectedResult: false,
diff --git a/domain/consensus/model/externalapi/equal_test.go b/domain/consensus/model/externalapi/equal_test.go
deleted file mode 100644
index cc4797a0..00000000
--- a/domain/consensus/model/externalapi/equal_test.go
+++ /dev/null
@@ -1,241 +0,0 @@
-package externalapi
-
-import (
-	"reflect"
-	"testing"
-)
-
-func TestDomainBlockHeader_Equal(t *testing.T) {
-	type headerToCompare struct {
-		header         *DomainBlockHeader
-		expectedResult bool
-	}
-	tests := []struct {
-		baseHeader         *DomainBlockHeader
-		headersToCompareTo []headerToCompare
-	}{
-		{
-			baseHeader: nil,
-			headersToCompareTo: []headerToCompare{
-				{
-					header:         nil,
-					expectedResult: true,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{0})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{1}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						4,
-						5,
-						6,
-					},
-					expectedResult: false,
-				},
-			},
-		},
-		{
-			baseHeader: &DomainBlockHeader{
-				0,
-				[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-				*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-				5,
-				6,
-				7,
-			},
-			headersToCompareTo: []headerToCompare{
-				{
-					header:         nil,
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						5,
-						6,
-						7,
-					},
-					expectedResult: true,
-				},
-				{
-					header: &DomainBlockHeader{
-						100,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						5,
-						6,
-						7,
-					},
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						// []*DomainHash{{1}, {2}},
-						[]*DomainHash{
-							NewDomainHashFromByteArray(&[DomainHashSize]byte{1}),
-							NewDomainHashFromByteArray(&[DomainHashSize]byte{2})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						5,
-						6,
-						7,
-					},
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{100})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						5,
-						6,
-						7,
-					},
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{100}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						5,
-						6,
-						7,
-					},
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{100}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						5,
-						6,
-						7,
-					},
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{100}),
-						5,
-						6,
-						7,
-					},
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						100,
-						6,
-						7,
-					},
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						5,
-						100,
-						7,
-					},
-					expectedResult: false,
-				},
-				{
-					header: &DomainBlockHeader{
-						0,
-						[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{1})},
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-						*NewDomainHashFromByteArray(&[DomainHashSize]byte{4}),
-						5,
-						6,
-						100,
-					},
-					expectedResult: false,
-				},
-			},
-		},
-	}
-
-	for i, test := range tests {
-		for j, subTest := range test.headersToCompareTo {
-			result1 := test.baseHeader.Equal(subTest.header)
-			if result1 != subTest.expectedResult {
-				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
-			}
-
-			result2 := subTest.header.Equal(test.baseHeader)
-			if result2 != subTest.expectedResult {
-				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
-			}
-		}
-	}
-}
-
-func TestDomainBlockHeader_Clone(t *testing.T) {
-	headers := []*DomainBlockHeader{
-		{
-			0,
-			[]*DomainHash{NewDomainHashFromByteArray(&[DomainHashSize]byte{0})},
-			*NewDomainHashFromByteArray(&[DomainHashSize]byte{1}),
-			*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-			*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-			4,
-			5,
-			6,
-		},
-		{
-			0,
-			[]*DomainHash{},
-			*NewDomainHashFromByteArray(&[DomainHashSize]byte{1}),
-			*NewDomainHashFromByteArray(&[DomainHashSize]byte{2}),
-			*NewDomainHashFromByteArray(&[DomainHashSize]byte{3}),
-			4,
-			5,
-			6,
-		},
-	}
-
-	for i, header := range headers {
-		clone := header.Clone()
-		if !clone.Equal(header) {
-			t.Fatalf("Test #%d: clone should be equal to the original", i)
-		}
-
-		if !reflect.DeepEqual(header, clone) {
-			t.Fatalf("Test #%d: clone should be equal to the original", i)
-		}
-	}
-}
diff --git a/domain/consensus/processes/blockbuilder/test_block_builder.go b/domain/consensus/processes/blockbuilder/test_block_builder.go
index 3e3fa750..31eeea62 100644
--- a/domain/consensus/processes/blockbuilder/test_block_builder.go
+++ b/domain/consensus/processes/blockbuilder/test_block_builder.go
@@ -4,6 +4,7 @@ import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
 	"github.com/kaspanet/kaspad/infrastructure/logger"
@@ -41,7 +42,7 @@ func (bb *testBlockBuilder) BuildBlockWithParents(parentHashes []*externalapi.Do
 
 func (bb *testBlockBuilder) buildHeaderWithParents(parentHashes []*externalapi.DomainHash,
 	transactions []*externalapi.DomainTransaction, acceptanceData externalapi.AcceptanceData, multiset model.Multiset) (
-	*externalapi.DomainBlockHeader, error) {
+	externalapi.BlockHeader, error) {
 
 	timeInMilliseconds, err := bb.minBlockTime(tempBlockHash)
 	if err != nil {
@@ -60,16 +61,16 @@ func (bb *testBlockBuilder) buildHeaderWithParents(parentHashes []*externalapi.D
 	utxoCommitment := multiset.Hash()
 
 	bb.nonceCounter++
-	return &externalapi.DomainBlockHeader{
-		Version:              constants.BlockVersion,
-		ParentHashes:         parentHashes,
-		HashMerkleRoot:       *hashMerkleRoot,
-		AcceptedIDMerkleRoot: *acceptedIDMerkleRoot,
-		UTXOCommitment:       *utxoCommitment,
-		TimeInMilliseconds:   timeInMilliseconds,
-		Bits:                 bits,
-		Nonce:                bb.nonceCounter,
-	}, nil
+	return blockheader.NewImmutableBlockHeader(
+		constants.BlockVersion,
+		parentHashes,
+		hashMerkleRoot,
+		acceptedIDMerkleRoot,
+		utxoCommitment,
+		timeInMilliseconds,
+		bits,
+		bb.nonceCounter,
+	), nil
 }
 
 func (bb *testBlockBuilder) buildBlockWithParents(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, model.UTXODiff, error) {
diff --git a/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go b/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go
index 929312b9..6171cd32 100644
--- a/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go
+++ b/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go
@@ -1,6 +1,7 @@
 package blockvalidator_test
 
 import (
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
 	"math"
 	"testing"
 
@@ -119,9 +120,9 @@ func TestCheckBlockSanity(t *testing.T) {
 }
 
 var unOrderedParentsBlock = externalapi.DomainBlock{
-	Header: &externalapi.DomainBlockHeader{
-		Version: 0x10000000,
-		ParentHashes: []*externalapi.DomainHash{
+	Header: blockheader.NewImmutableBlockHeader(
+		0x10000000,
+		[]*externalapi.DomainHash{
 			externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 				0x4b, 0xb0, 0x75, 0x35, 0xdf, 0xd5, 0x8e, 0x0b,
 				0x3c, 0xd6, 0x4f, 0xd7, 0x15, 0x52, 0x80, 0x87,
@@ -135,28 +136,28 @@ var unOrderedParentsBlock = externalapi.DomainBlock{
 				0x7a, 0x4e, 0xde, 0xfe, 0x2c, 0x00, 0x00, 0x00,
 			}),
 		},
-		HashMerkleRoot: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0xf8, 0x55, 0x7b, 0xd0, 0xda, 0xf2, 0x06, 0x8b,
 			0x3b, 0xb1, 0x93, 0x5a, 0x2c, 0x52, 0x43, 0xf0,
 			0x02, 0xf2, 0xb1, 0x40, 0x81, 0x2c, 0x0c, 0x15,
 			0x8d, 0x04, 0x3d, 0xe2, 0x23, 0x54, 0x98, 0x88,
 		}),
-		AcceptedIDMerkleRoot: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x80, 0xf7, 0x00, 0xe3, 0x16, 0x3d, 0x04, 0x95,
 			0x5b, 0x7e, 0xaf, 0x84, 0x7e, 0x1b, 0x6b, 0x06,
 			0x4e, 0x06, 0xba, 0x64, 0xd7, 0x61, 0xda, 0x25,
 			0x1a, 0x0e, 0x21, 0xd4, 0x64, 0x49, 0x02, 0xa2,
 		}),
-		UTXOCommitment: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x80, 0xf7, 0x00, 0xe3, 0x16, 0x3d, 0x04, 0x95,
 			0x5b, 0x7e, 0xaf, 0x84, 0x7e, 0x1b, 0x6b, 0x06,
 			0x4e, 0x06, 0xba, 0x64, 0xd7, 0x61, 0xda, 0x25,
 			0x1a, 0x0e, 0x21, 0xd4, 0x64, 0x49, 0x02, 0xa2,
 		}),
-		TimeInMilliseconds: 0x5cd18053000,
-		Bits:               0x207fffff,
-		Nonce:              0x1,
-	},
+		0x5cd18053000,
+		0x207fffff,
+		0x1,
+	),
 	Transactions: []*externalapi.DomainTransaction{
 		{
 			Version: 1,
@@ -393,9 +394,9 @@ var unOrderedParentsBlock = externalapi.DomainBlock{
 
 // exampleValidBlock defines a sample valid block
 var exampleValidBlock = externalapi.DomainBlock{
-	Header: &externalapi.DomainBlockHeader{
-		Version: 0x10000000,
-		ParentHashes: []*externalapi.DomainHash{
+	Header: blockheader.NewImmutableBlockHeader(
+		0x10000000,
+		[]*externalapi.DomainHash{
 			externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 				0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95,
 				0xd9, 0xc6, 0x41, 0xf3, 0xb8, 0xee, 0xc2, 0xf3,
@@ -409,23 +410,23 @@ var exampleValidBlock = externalapi.DomainBlock{
 				0x6a, 0xce, 0x0e, 0x38, 0xc6, 0x00, 0x00, 0x00,
 			}),
 		},
-		HashMerkleRoot: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x33, 0x70, 0xa7, 0x40, 0x9f, 0x2d, 0x87, 0xe1,
 			0x26, 0xaf, 0x0f, 0x5c, 0x7e, 0xc3, 0x84, 0x5e,
 			0x4f, 0x68, 0x42, 0x0a, 0xbf, 0x90, 0xcd, 0xef,
 			0x94, 0x9b, 0xe1, 0x9a, 0xf7, 0xdd, 0xb0, 0xb5,
 		}),
-		AcceptedIDMerkleRoot: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x8a, 0xb7, 0xd6, 0x73, 0x1b, 0xe6, 0xc5, 0xd3,
 			0x5d, 0x4e, 0x2c, 0xc9, 0x57, 0x88, 0x30, 0x65,
 			0x81, 0xb8, 0xa0, 0x68, 0x77, 0xc4, 0x02, 0x1e,
 			0x3c, 0xb1, 0x16, 0x8f, 0x5f, 0x6b, 0x45, 0x87,
 		}),
-		UTXOCommitment:     *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{}),
-		TimeInMilliseconds: 0x17305aa654a,
-		Bits:               0x207fffff,
-		Nonce:              1,
-	},
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{}),
+		0x17305aa654a,
+		0x207fffff,
+		1,
+	),
 	Transactions: []*externalapi.DomainTransaction{
 		{
 			Version: 1,
@@ -695,9 +696,9 @@ var exampleValidBlock = externalapi.DomainBlock{
 
 // blockWithWrongTxOrder defines invalid block 100,000 of the block DAG.
 var blockWithWrongTxOrder = externalapi.DomainBlock{
-	Header: &externalapi.DomainBlockHeader{
-		Version: 1,
-		ParentHashes: []*externalapi.DomainHash{
+	Header: blockheader.NewImmutableBlockHeader(
+		1,
+		[]*externalapi.DomainHash{
 			externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 				0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95,
 				0xd9, 0xc6, 0x41, 0xf3, 0xb8, 0xee, 0xc2, 0xf3,
@@ -711,28 +712,28 @@ var blockWithWrongTxOrder = externalapi.DomainBlock{
 				0x6a, 0xce, 0x0e, 0x38, 0xc6, 0x00, 0x00, 0x00,
 			}),
 		},
-		HashMerkleRoot: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0xac, 0xa4, 0x21, 0xe1, 0xa6, 0xc3, 0xbe, 0x5d,
 			0x52, 0x66, 0xf3, 0x0b, 0x21, 0x87, 0xbc, 0xf3,
 			0xf3, 0x2d, 0xd1, 0x05, 0x64, 0xb5, 0x16, 0x76,
 			0xe4, 0x66, 0x7d, 0x51, 0x53, 0x18, 0x6d, 0xb1,
 		}),
-		AcceptedIDMerkleRoot: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0xa0, 0x69, 0x2d, 0x16, 0xb5, 0xd7, 0xe4, 0xf3,
 			0xcd, 0xc7, 0xc9, 0xaf, 0xfb, 0xd2, 0x1b, 0x85,
 			0x0b, 0x79, 0xf5, 0x29, 0x6d, 0x1c, 0xaa, 0x90,
 			0x2f, 0x01, 0xd4, 0x83, 0x9b, 0x2a, 0x04, 0x5e,
 		}),
-		UTXOCommitment: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
+		externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
 			0x00, 0x69, 0x2d, 0x16, 0xb5, 0xd7, 0xe4, 0xf3,
 			0xcd, 0xc7, 0xc9, 0xaf, 0xfb, 0xd2, 0x1b, 0x85,
 			0x0b, 0x79, 0xf5, 0x29, 0x6d, 0x1c, 0xaa, 0x90,
 			0x2f, 0x01, 0xd4, 0x83, 0x9b, 0x2a, 0x04, 0x5e,
 		}),
-		TimeInMilliseconds: 0x5cd16eaa000,
-		Bits:               0x207fffff,
-		Nonce:              1,
-	},
+		0x5cd16eaa000,
+		0x207fffff,
+		1,
+	),
 	Transactions: []*externalapi.DomainTransaction{
 		{
 			Version: 1,
diff --git a/domain/consensus/processes/blockvalidator/block_header_in_context_test.go b/domain/consensus/processes/blockvalidator/block_header_in_context_test.go
index 165b42df..4791e7e1 100644
--- a/domain/consensus/processes/blockvalidator/block_header_in_context_test.go
+++ b/domain/consensus/processes/blockvalidator/block_header_in_context_test.go
@@ -2,6 +2,7 @@ package blockvalidator_test
 
 import (
 	"errors"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
 	"testing"
 
 	"github.com/kaspanet/kaspad/domain/consensus"
@@ -28,7 +29,9 @@ func TestValidateMedianTime(t *testing.T) {
 				t.Fatalf("BuildBlockWithParents: %+v", err)
 			}
 
-			block.Header.TimeInMilliseconds = blockTime
+			newHeader := block.Header.ToMutable()
+			newHeader.SetTimeInMilliseconds(blockTime)
+			block.Header = newHeader.ToImmutable()
 			_, err = tc.ValidateAndInsertBlock(block)
 			if !errors.Is(err, expectedErr) {
 				t.Fatalf("expected error %s but got %+v", expectedErr, err)
@@ -62,7 +65,7 @@ func TestValidateMedianTime(t *testing.T) {
 		tip := params.GenesisBlock
 		tipHash := params.GenesisHash
 
-		blockTime := tip.Header.TimeInMilliseconds
+		blockTime := tip.Header.TimeInMilliseconds()
 
 		for i := 0; i < 100; i++ {
 			blockTime += 1000
@@ -105,16 +108,16 @@ func TestCheckParentsIncest(t *testing.T) {
 		}
 
 		directParentsRelationBlock := &externalapi.DomainBlock{
-			Header: &externalapi.DomainBlockHeader{
-				Version:              0,
-				ParentHashes:         []*externalapi.DomainHash{a, b},
-				HashMerkleRoot:       externalapi.DomainHash{},
-				AcceptedIDMerkleRoot: externalapi.DomainHash{},
-				UTXOCommitment:       externalapi.DomainHash{},
-				TimeInMilliseconds:   0,
-				Bits:                 0,
-				Nonce:                0,
-			},
+			Header: blockheader.NewImmutableBlockHeader(
+				0,
+				[]*externalapi.DomainHash{a, b},
+				&externalapi.DomainHash{},
+				&externalapi.DomainHash{},
+				&externalapi.DomainHash{},
+				0,
+				0,
+				0,
+			),
 			Transactions: nil,
 		}
 
@@ -124,16 +127,16 @@ func TestCheckParentsIncest(t *testing.T) {
 		}
 
 		indirectParentsRelationBlock := &externalapi.DomainBlock{
-			Header: &externalapi.DomainBlockHeader{
-				Version:              0,
-				ParentHashes:         []*externalapi.DomainHash{params.GenesisHash, b},
-				HashMerkleRoot:       externalapi.DomainHash{},
-				AcceptedIDMerkleRoot: externalapi.DomainHash{},
-				UTXOCommitment:       externalapi.DomainHash{},
-				TimeInMilliseconds:   0,
-				Bits:                 0,
-				Nonce:                0,
-			},
+			Header: blockheader.NewImmutableBlockHeader(
+				0,
+				[]*externalapi.DomainHash{params.GenesisHash, b},
+				&externalapi.DomainHash{},
+				&externalapi.DomainHash{},
+				&externalapi.DomainHash{},
+				0,
+				0,
+				0,
+			),
 			Transactions: nil,
 		}
 
diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go
index ed38040d..db0b6481 100644
--- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go
+++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go
@@ -104,10 +104,10 @@ func checkBlockUTXOCommitment(t *testing.T, consensus testapi.TestConsensus, blo
 	utxoCommitment := ms.Hash()
 
 	// Make sure that the two commitments are equal
-	if *utxoCommitment != block.Header.UTXOCommitment {
+	if !utxoCommitment.Equal(block.Header.UTXOCommitment()) {
 		t.Fatalf("TestUTXOCommitment: calculated UTXO commitment for block %s and "+
 			"actual UTXO commitment don't match. Want: %s, got: %s", blockName,
-			utxoCommitment, block.Header.UTXOCommitment)
+			utxoCommitment, block.Header.UTXOCommitment())
 	}
 }
 
diff --git a/domain/consensus/processes/difficultymanager/difficultymanager_test.go b/domain/consensus/processes/difficultymanager/difficultymanager_test.go
index eef999e2..46e9b8cf 100644
--- a/domain/consensus/processes/difficultymanager/difficultymanager_test.go
+++ b/domain/consensus/processes/difficultymanager/difficultymanager_test.go
@@ -25,7 +25,7 @@ func TestDifficulty(t *testing.T) {
 		// a bit less then an hour of timestamps.
 		// To prevent rejected blocks due to timestamps in the future, the following safeguard makes sure
 		// the genesis block is at least 1 hour in the past.
-		if params.GenesisBlock.Header.TimeInMilliseconds > mstime.ToMSTime(time.Now().Add(-time.Hour)).UnixMilliseconds() {
+		if params.GenesisBlock.Header.TimeInMilliseconds() > mstime.ToMSTime(time.Now().Add(-time.Hour)).UnixMilliseconds() {
 			t.Fatalf("TestDifficulty requires the GenesisBlock to be at least 1 hour old to pass")
 		}
 
@@ -51,7 +51,7 @@ func TestDifficulty(t *testing.T) {
 					t.Fatalf("BlockHeader: %+v", err)
 				}
 
-				blockTime = header.TimeInMilliseconds + params.TargetTimePerBlock.Milliseconds()
+				blockTime = header.TimeInMilliseconds() + params.TargetTimePerBlock.Milliseconds()
 			}
 
 			block, _, err := tc.BuildBlockWithParents(parents, nil, nil)
@@ -59,7 +59,9 @@ func TestDifficulty(t *testing.T) {
 				t.Fatalf("BuildBlockWithParents: %+v", err)
 			}
 
-			block.Header.TimeInMilliseconds = blockTime
+			newHeader := block.Header.ToMutable()
+			newHeader.SetTimeInMilliseconds(blockTime)
+			block.Header = newHeader.ToImmutable()
 			_, err = tc.ValidateAndInsertBlock(block)
 			if err != nil {
 				t.Fatalf("ValidateAndInsertBlock: %+v", err)
@@ -99,31 +101,31 @@ func TestDifficulty(t *testing.T) {
 		tip := params.GenesisBlock
 		for i := 0; i < params.DifficultyAdjustmentWindowSize; i++ {
 			tip, tipHash = addBlock(0, tipHash)
-			if tip.Header.Bits != params.GenesisBlock.Header.Bits {
+			if tip.Header.Bits() != params.GenesisBlock.Header.Bits() {
 				t.Fatalf("As long as the bluest parent's blue score is less then the difficulty adjustment " +
 					"window size, the difficulty should be the same as genesis'")
 			}
 		}
 		for i := 0; i < params.DifficultyAdjustmentWindowSize+100; i++ {
 			tip, tipHash = addBlock(0, tipHash)
-			if tip.Header.Bits != params.GenesisBlock.Header.Bits {
+			if tip.Header.Bits() != params.GenesisBlock.Header.Bits() {
 				t.Fatalf("As long as the block rate remains the same, the difficulty shouldn't change")
 			}
 		}
 
 		blockInThePast, tipHash := addBlockWithMinimumTime(tipHash)
-		if blockInThePast.Header.Bits != tip.Header.Bits {
+		if blockInThePast.Header.Bits() != tip.Header.Bits() {
 			t.Fatalf("The difficulty should only change when blockInThePast is in the past of a block bluest parent")
 		}
 		tip = blockInThePast
 
 		tip, tipHash = addBlock(0, tipHash)
-		if tip.Header.Bits != blockInThePast.Header.Bits {
+		if tip.Header.Bits() != blockInThePast.Header.Bits() {
 			t.Fatalf("The difficulty should only change when blockInThePast is in the past of a block bluest parent")
 		}
 
 		tip, tipHash = addBlock(0, tipHash)
-		if compareBits(tip.Header.Bits, blockInThePast.Header.Bits) >= 0 {
+		if compareBits(tip.Header.Bits(), blockInThePast.Header.Bits()) >= 0 {
 			t.Fatalf("tip.bits should be smaller than blockInThePast.bits because blockInThePast increased the " +
 				"block rate, so the difficulty should increase as well")
 		}
@@ -136,8 +138,8 @@ func TestDifficulty(t *testing.T) {
 			expectedBits = uint32(0x207f83df)
 		}
 
-		if tip.Header.Bits != expectedBits {
-			t.Errorf("tip.bits was expected to be %x but got %x", expectedBits, tip.Header.Bits)
+		if tip.Header.Bits() != expectedBits {
+			t.Errorf("tip.bits was expected to be %x but got %x", expectedBits, tip.Header.Bits())
 		}
 
 		// Increase block rate to increase difficulty
@@ -154,38 +156,38 @@ func TestDifficulty(t *testing.T) {
 				t.Fatalf("BlockHeader: %+v", err)
 			}
 
-			if compareBits(tip.Header.Bits, selectedParentHeader.Bits) > 0 {
+			if compareBits(tip.Header.Bits(), selectedParentHeader.Bits()) > 0 {
 				t.Fatalf("Because we're increasing the block rate, the difficulty can't decrease")
 			}
 		}
 
 		// Add blocks until difficulty stabilizes
-		lastBits := tip.Header.Bits
+		lastBits := tip.Header.Bits()
 		sameBitsCount := 0
 		for sameBitsCount < params.DifficultyAdjustmentWindowSize+1 {
 			tip, tipHash = addBlock(0, tipHash)
-			if tip.Header.Bits == lastBits {
+			if tip.Header.Bits() == lastBits {
 				sameBitsCount++
 			} else {
-				lastBits = tip.Header.Bits
+				lastBits = tip.Header.Bits()
 				sameBitsCount = 0
 			}
 		}
 
-		slowBlockTime := tip.Header.TimeInMilliseconds + params.TargetTimePerBlock.Milliseconds() + 1000
+		slowBlockTime := tip.Header.TimeInMilliseconds() + params.TargetTimePerBlock.Milliseconds() + 1000
 		slowBlock, tipHash := addBlock(slowBlockTime, tipHash)
-		if slowBlock.Header.Bits != tip.Header.Bits {
+		if slowBlock.Header.Bits() != tip.Header.Bits() {
 			t.Fatalf("The difficulty should only change when slowBlock is in the past of a block bluest parent")
 		}
 
 		tip = slowBlock
 
 		tip, tipHash = addBlock(0, tipHash)
-		if tip.Header.Bits != slowBlock.Header.Bits {
+		if tip.Header.Bits() != slowBlock.Header.Bits() {
 			t.Fatalf("The difficulty should only change when slowBlock is in the past of a block bluest parent")
 		}
 		tip, tipHash = addBlock(0, tipHash)
-		if compareBits(tip.Header.Bits, slowBlock.Header.Bits) <= 0 {
+		if compareBits(tip.Header.Bits(), slowBlock.Header.Bits()) <= 0 {
 			t.Fatalf("tip.bits should be smaller than slowBlock.bits because slowBlock decreased the block" +
 				" rate, so the difficulty should decrease as well")
 		}
@@ -203,7 +205,7 @@ func TestDifficulty(t *testing.T) {
 		}
 		tipWithRedPast, _ := addBlock(0, redChainTipHash, blueTipHash)
 		tipWithoutRedPast, _ := addBlock(0, blueTipHash)
-		if tipWithoutRedPast.Header.Bits != tipWithRedPast.Header.Bits {
+		if tipWithoutRedPast.Header.Bits() != tipWithRedPast.Header.Bits() {
 			t.Fatalf("tipWithoutRedPast.bits should be the same as tipWithRedPast.bits because red blocks" +
 				" shouldn't affect the difficulty")
 		}
diff --git a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
index a05a6a29..9bc5dc8f 100644
--- a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
+++ b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
@@ -2,6 +2,8 @@ package ghostdagmanager_test
 
 import (
 	"encoding/json"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
 	"math/big"
 	"os"
 	"path/filepath"
@@ -65,12 +67,12 @@ func TestGHOSTDAG(t *testing.T) {
 		}
 
 		blockHeadersStore := &blockHeadersStore{
-			dagMap: make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader),
+			dagMap: make(map[externalapi.DomainHash]externalapi.BlockHeader),
 		}
 
 		blockGHOSTDAGDataGenesis := ghostdagmanager.NewBlockGHOSTDAGData(0, new(big.Int), nil, nil, nil, nil)
 		genesisHeader := params.GenesisBlock.Header
-		genesisWork := util.CalcWork(genesisHeader.Bits)
+		genesisWork := util.CalcWork(genesisHeader.Bits())
 
 		var testsCounter int
 		err := filepath.Walk("../../testdata/dags", func(path string, info os.FileInfo, err error) error {
@@ -108,10 +110,16 @@ func TestGHOSTDAG(t *testing.T) {
 
 					blockID := StringToDomainHash(testBlockData.ID)
 					dagTopology.parentsMap[*blockID] = StringToDomainHashSlice(testBlockData.Parents)
-					blockHeadersStore.dagMap[*blockID] = &externalapi.DomainBlockHeader{
-						ParentHashes: StringToDomainHashSlice(testBlockData.Parents),
-						Bits:         genesisHeader.Bits,
-					}
+					blockHeadersStore.dagMap[*blockID] = blockheader.NewImmutableBlockHeader(
+						constants.BlockVersion,
+						StringToDomainHashSlice(testBlockData.Parents),
+						nil,
+						nil,
+						nil,
+						0,
+						genesisHeader.Bits(),
+						0,
+					)
 
 					err := g.GHOSTDAG(blockID)
 					if err != nil {
@@ -154,7 +162,7 @@ func TestGHOSTDAG(t *testing.T) {
 				dagTopology.parentsMap[genesisHash] = nil
 				ghostdagDataStore.dagMap = make(map[externalapi.DomainHash]model.BlockGHOSTDAGData)
 				ghostdagDataStore.dagMap[genesisHash] = blockGHOSTDAGDataGenesis
-				blockHeadersStore.dagMap = make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader)
+				blockHeadersStore.dagMap = make(map[externalapi.DomainHash]externalapi.BlockHeader)
 				blockHeadersStore.dagMap[genesisHash] = genesisHeader
 			}
 
@@ -299,20 +307,20 @@ func (dt *DAGTopologyManagerImpl) SetParents(blockHash *externalapi.DomainHash,
 }
 
 type blockHeadersStore struct {
-	dagMap map[externalapi.DomainHash]*externalapi.DomainBlockHeader
+	dagMap map[externalapi.DomainHash]externalapi.BlockHeader
 }
 
 func (b *blockHeadersStore) Discard() { panic("unimplemented") }
 
 func (b *blockHeadersStore) Commit(_ model.DBTransaction) error { panic("unimplemented") }
 
-func (b *blockHeadersStore) Stage(blockHash *externalapi.DomainHash, blockHeader *externalapi.DomainBlockHeader) {
+func (b *blockHeadersStore) Stage(blockHash *externalapi.DomainHash, blockHeader externalapi.BlockHeader) {
 	b.dagMap[*blockHash] = blockHeader
 }
 
 func (b *blockHeadersStore) IsStaged() bool { panic("unimplemented") }
 
-func (b *blockHeadersStore) BlockHeader(_ model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
+func (b *blockHeadersStore) BlockHeader(_ model.DBReader, blockHash *externalapi.DomainHash) (externalapi.BlockHeader, error) {
 	header, ok := b.dagMap[*blockHash]
 	if ok {
 		return header, nil
@@ -325,8 +333,8 @@ func (b *blockHeadersStore) HasBlockHeader(_ model.DBReader, blockHash *external
 	return ok, nil
 }
 
-func (b *blockHeadersStore) BlockHeaders(_ model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error) {
-	res := make([]*externalapi.DomainBlockHeader, 0, len(blockHashes))
+func (b *blockHeadersStore) BlockHeaders(_ model.DBReader, blockHashes []*externalapi.DomainHash) ([]externalapi.BlockHeader, error) {
+	res := make([]externalapi.BlockHeader, 0, len(blockHashes))
 	for _, hash := range blockHashes {
 		header, err := b.BlockHeader(nil, hash)
 		if err != nil {
diff --git a/domain/consensus/processes/pastmediantimemanager/pastmediantimemanager_test.go b/domain/consensus/processes/pastmediantimemanager/pastmediantimemanager_test.go
index 938bfe88..c7769750 100644
--- a/domain/consensus/processes/pastmediantimemanager/pastmediantimemanager_test.go
+++ b/domain/consensus/processes/pastmediantimemanager/pastmediantimemanager_test.go
@@ -22,7 +22,7 @@ func TestPastMedianTime(t *testing.T) {
 		numBlocks := uint32(300)
 		blockHashes := make([]*externalapi.DomainHash, numBlocks)
 		blockHashes[0] = params.GenesisHash
-		blockTime := params.GenesisBlock.Header.TimeInMilliseconds
+		blockTime := params.GenesisBlock.Header.TimeInMilliseconds()
 		for i := uint32(1); i < numBlocks; i++ {
 			blockTime += 1000
 			block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{blockHashes[i-1]}, nil, nil)
@@ -30,7 +30,9 @@ func TestPastMedianTime(t *testing.T) {
 				t.Fatalf("BuildBlockWithParents: %s", err)
 			}
 
-			block.Header.TimeInMilliseconds = blockTime
+			newHeader := block.Header.ToMutable()
+			newHeader.SetTimeInMilliseconds(blockTime)
+			block.Header = newHeader.ToImmutable()
 			_, err = tc.ValidateAndInsertBlock(block)
 			if err != nil {
 				t.Fatalf("ValidateAndInsertBlock: %+v", err)
@@ -68,7 +70,7 @@ func TestPastMedianTime(t *testing.T) {
 			}
 
 			millisecondsSinceGenesis := pastMedianTime -
-				params.GenesisBlock.Header.TimeInMilliseconds
+				params.GenesisBlock.Header.TimeInMilliseconds()
 
 			if millisecondsSinceGenesis != test.expectedMillisecondsSinceGenesis {
 				t.Errorf("TestCalcPastMedianTime: expected past median time of block %v to be %v milliseconds "+
diff --git a/domain/consensus/utils/blockheader/blockheader_test.go b/domain/consensus/utils/blockheader/blockheader_test.go
new file mode 100644
index 00000000..02524a87
--- /dev/null
+++ b/domain/consensus/utils/blockheader/blockheader_test.go
@@ -0,0 +1,205 @@
+package blockheader
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"testing"
+)
+
+func TestDomainBlockHeader_Equal(t *testing.T) {
+	type headerToCompare struct {
+		header         *blockHeader
+		expectedResult bool
+	}
+	tests := []struct {
+		baseHeader         *blockHeader
+		headersToCompareTo []headerToCompare
+	}{
+		{
+			baseHeader: nil,
+			headersToCompareTo: []headerToCompare{
+				{
+					header:         nil,
+					expectedResult: true,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{0})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						4,
+						5,
+						6,
+					},
+					expectedResult: false,
+				},
+			},
+		},
+		{
+			baseHeader: &blockHeader{
+				0,
+				[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+				externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+				5,
+				6,
+				7,
+			},
+			headersToCompareTo: []headerToCompare{
+				{
+					header:         nil,
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						5,
+						6,
+						7,
+					},
+					expectedResult: true,
+				},
+				{
+					header: &blockHeader{
+						100,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						// []*externalapi.DomainHash{{1}, {2}},
+						[]*externalapi.DomainHash{
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1}),
+							externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100}),
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						100,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						5,
+						100,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &blockHeader{
+						0,
+						[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3}),
+						externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4}),
+						5,
+						6,
+						100,
+					},
+					expectedResult: false,
+				},
+			},
+		},
+	}
+
+	for i, test := range tests {
+		for j, subTest := range test.headersToCompareTo {
+			result1 := test.baseHeader.Equal(subTest.header)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+
+			result2 := subTest.header.Equal(test.baseHeader)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
diff --git a/testing/integration/mining_test.go b/testing/integration/mining_test.go
index 1fecb839..0f9d7774 100644
--- a/testing/integration/mining_test.go
+++ b/testing/integration/mining_test.go
@@ -12,11 +12,13 @@ import (
 )
 
 func solveBlock(block *externalapi.DomainBlock) *externalapi.DomainBlock {
-	targetDifficulty := util.CompactToBig(block.Header.Bits)
+	targetDifficulty := util.CompactToBig(block.Header.Bits())
+	headerForMining := block.Header.ToMutable()
 	initialNonce := rand.Uint64()
 	for i := initialNonce; i != initialNonce-1; i++ {
-		block.Header.Nonce = i
-		if pow.CheckProofOfWorkWithTarget(block.Header, targetDifficulty) {
+		headerForMining.SetNonce(i)
+		if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
+			block.Header = headerForMining.ToImmutable()
 			return block
 		}
 	}