Repository: kaspanet/kaspad
Pull request #1003 (2020-11-04 15:37:00)

[NOD-1512] Implement utxo deserialization

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
ci-dag: Rebuild after update in master
ci-dag: Rebuild after update in master

Patch:
diff --git a/domain/consensus/factory.go b/domain/consensus/factory.go
index 8cc95952..20500265 100644
--- a/domain/consensus/factory.go
+++ b/domain/consensus/factory.go
@@ -136,7 +136,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 	)
 	consensusStateManager, err := consensusstatemanager.New(
 		dbManager,
-		dagParams,
+		dagParams.FinalityDepth(),
+		dagParams.PruningDepth(),
 		ghostdagManager,
 		dagTopologyManager,
 		dagTraversalManager,
@@ -183,6 +184,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
 		genesisHash,
 		dagTraversalManager,
 		dagTopologyManager,
+		consensusStateManager,
+
 		ghostdagDataStore,
 		blockStatusStore)
 
diff --git a/domain/consensus/model/selectedparentiterator.go b/domain/consensus/model/blockiterator.go
similarity index 50%
rename from domain/consensus/model/selectedparentiterator.go
rename to domain/consensus/model/blockiterator.go
index 520da27b..0b088c99 100644
--- a/domain/consensus/model/selectedparentiterator.go
+++ b/domain/consensus/model/blockiterator.go
@@ -2,9 +2,8 @@ package model
 
 import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 
-// SelectedParentIterator is an iterator over the selected parent
-// chain of some block
-type SelectedParentIterator interface {
+// BlockIterator is an iterator over blocks according to some order.
+type BlockIterator interface {
 	Next() bool
 	Get() *externalapi.DomainHash
 }
diff --git a/domain/consensus/model/interface_processes_consensusstatemanager.go b/domain/consensus/model/interface_processes_consensusstatemanager.go
index 9aed585a..cc36e856 100644
--- a/domain/consensus/model/interface_processes_consensusstatemanager.go
+++ b/domain/consensus/model/interface_processes_consensusstatemanager.go
@@ -8,4 +8,5 @@ type ConsensusStateManager interface {
 	PopulateTransactionWithUTXOEntries(transaction *externalapi.DomainTransaction) error
 	SetPruningPointUTXOSet(serializedUTXOSet []byte) error
 	RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (ReadOnlyUTXOSetIterator, error)
+	HeaderTipsPruningPoint() (*externalapi.DomainHash, error)
 }
diff --git a/domain/consensus/model/interface_processes_dagtraversalmanager.go b/domain/consensus/model/interface_processes_dagtraversalmanager.go
index 14819de5..550a0fc6 100644
--- a/domain/consensus/model/interface_processes_dagtraversalmanager.go
+++ b/domain/consensus/model/interface_processes_dagtraversalmanager.go
@@ -7,7 +7,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 type DAGTraversalManager interface {
 	HighestChainBlockBelowBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
 	LowestChainBlockAboveOrEqualToBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
-	SelectedParentIterator(highHash *externalapi.DomainHash) SelectedParentIterator
+	SelectedParentIterator(highHash *externalapi.DomainHash) BlockIterator
+	SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
+	AnticoneFromContext(context, lowHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
 	BlueWindow(highHash *externalapi.DomainHash, windowSize uint64) ([]*externalapi.DomainHash, error)
 	NewDownHeap() BlockHeap
 	NewUpHeap() BlockHeap
diff --git a/domain/consensus/model/readonlyutxoset.go b/domain/consensus/model/readonlyutxoset.go
index 4e36b8dc..cba7d9c7 100644
--- a/domain/consensus/model/readonlyutxoset.go
+++ b/domain/consensus/model/readonlyutxoset.go
@@ -12,5 +12,5 @@ type ReadOnlyUTXOSet interface {
 // ReadOnlyUTXOSet
 type ReadOnlyUTXOSetIterator interface {
 	Next() bool
-	Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry)
+	Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error)
 }
diff --git a/domain/consensus/processes/blockprocessor/buildblock.go b/domain/consensus/processes/blockprocessor/buildblock.go
index a43a8773..bf56b13d 100644
--- a/domain/consensus/processes/blockprocessor/buildblock.go
+++ b/domain/consensus/processes/blockprocessor/buildblock.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
 	"github.com/kaspanet/kaspad/util/mstime"
@@ -132,8 +132,8 @@ func (bp *blockProcessor) newBlockAcceptedIDMerkleRoot() (*externalapi.DomainHas
 		}
 	}
 	sort.Slice(acceptedTransactions, func(i, j int) bool {
-		acceptedTransactionIID := hashserialization.TransactionID(acceptedTransactions[i])
-		acceptedTransactionJID := hashserialization.TransactionID(acceptedTransactions[j])
+		acceptedTransactionIID := consensusserialization.TransactionID(acceptedTransactions[i])
+		acceptedTransactionJID := consensusserialization.TransactionID(acceptedTransactions[j])
 		return transactionid.Less(acceptedTransactionIID, acceptedTransactionJID)
 	})
 
diff --git a/domain/consensus/processes/blockprocessor/validateandinsertblock.go b/domain/consensus/processes/blockprocessor/validateandinsertblock.go
index 682d7588..f5bbcbbc 100644
--- a/domain/consensus/processes/blockprocessor/validateandinsertblock.go
+++ b/domain/consensus/processes/blockprocessor/validateandinsertblock.go
@@ -3,7 +3,7 @@ package blockprocessor
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/pkg/errors"
 )
 
@@ -17,7 +17,7 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
 		return errors.Errorf("cannot insert blocks while in %s mode", mode.State)
 	}
 
-	hash := hashserialization.HeaderHash(block.Header)
+	hash := consensusserialization.HeaderHash(block.Header)
 	if mode.State == externalapi.SyncStateHeadersFirst && len(block.Transactions) != 0 {
 		return errors.Errorf("block %s contains transactions while validating in header only mode", hash)
 	}
@@ -148,7 +148,7 @@ func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock, mode *ex
 		return err
 	}
 
-	blockHash := hashserialization.HeaderHash(block.Header)
+	blockHash := consensusserialization.HeaderHash(block.Header)
 	err = bp.blockValidator.ValidateProofOfWorkAndDifficulty(blockHash)
 	if err != nil {
 		return err
@@ -160,7 +160,7 @@ func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock, mode *ex
 	if err != nil {
 		if errors.As(err, &ruleerrors.RuleError{}) {
 			bp.discardAllChanges()
-			hash := hashserialization.HeaderHash(block.Header)
+			hash := consensusserialization.HeaderHash(block.Header)
 			bp.blockStatusStore.Stage(hash, externalapi.StatusInvalid)
 			commitErr := bp.commitAllChanges()
 			if commitErr != nil {
@@ -173,7 +173,7 @@ func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock, mode *ex
 }
 
 func (bp *blockProcessor) validatePreProofOfWork(block *externalapi.DomainBlock) error {
-	blockHash := hashserialization.HeaderHash(block.Header)
+	blockHash := consensusserialization.HeaderHash(block.Header)
 
 	hasHeader, err := bp.hasHeader(blockHash)
 	if err != nil {
@@ -192,7 +192,7 @@ func (bp *blockProcessor) validatePreProofOfWork(block *externalapi.DomainBlock)
 }
 
 func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock, mode *externalapi.SyncInfo) error {
-	blockHash := hashserialization.HeaderHash(block.Header)
+	blockHash := consensusserialization.HeaderHash(block.Header)
 
 	if mode.State != externalapi.SyncStateHeadersFirst {
 		bp.blockStore.Stage(blockHash, block)
diff --git a/domain/consensus/processes/blockvalidator/block_body_in_context.go b/domain/consensus/processes/blockvalidator/block_body_in_context.go
index 68286753..139efb47 100644
--- a/domain/consensus/processes/blockvalidator/block_body_in_context.go
+++ b/domain/consensus/processes/blockvalidator/block_body_in_context.go
@@ -3,7 +3,7 @@ package blockvalidator
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
 	"github.com/pkg/errors"
 	"math"
@@ -40,7 +40,7 @@ func (v *blockValidator) checkBlockTransactionsFinalized(blockHash *externalapi.
 	// Ensure all transactions in the block are finalized.
 	for _, tx := range block.Transactions {
 		if !v.isFinalizedTransaction(tx, ghostdagData.BlueScore, blockTime) {
-			txID := hashserialization.TransactionID(tx)
+			txID := consensusserialization.TransactionID(tx)
 			return errors.Wrapf(ruleerrors.ErrUnfinalizedTx, "block contains unfinalized "+
 				"transaction %s", txID)
 		}
diff --git a/domain/consensus/processes/blockvalidator/block_body_in_isolation.go b/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
index 4b42ff15..9ae3661b 100644
--- a/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
+++ b/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
@@ -3,9 +3,9 @@ package blockvalidator
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/estimatedsize"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
@@ -141,7 +141,7 @@ func (v *blockValidator) checkTransactionsInIsolation(block *externalapi.DomainB
 		err := v.transactionValidator.ValidateTransactionInIsolation(tx)
 		if err != nil {
 			return errors.Wrapf(err, "transaction %s failed isolation "+
-				"check", hashserialization.TransactionID(tx))
+				"check", consensusserialization.TransactionID(tx))
 		}
 	}
 
@@ -161,7 +161,7 @@ func (v *blockValidator) checkBlockHashMerkleRoot(block *externalapi.DomainBlock
 func (v *blockValidator) checkBlockDuplicateTransactions(block *externalapi.DomainBlock) error {
 	existingTxIDs := make(map[externalapi.DomainTransactionID]struct{})
 	for _, tx := range block.Transactions {
-		id := hashserialization.TransactionID(tx)
+		id := consensusserialization.TransactionID(tx)
 		if _, exists := existingTxIDs[*id]; exists {
 			return errors.Wrapf(ruleerrors.ErrDuplicateTx, "block contains duplicate "+
 				"transaction %s", id)
@@ -175,7 +175,7 @@ func (v *blockValidator) checkBlockDoubleSpends(block *externalapi.DomainBlock)
 	usedOutpoints := make(map[externalapi.DomainOutpoint]*externalapi.DomainTransactionID)
 	for _, tx := range block.Transactions {
 		for _, input := range tx.Inputs {
-			txID := hashserialization.TransactionID(tx)
+			txID := consensusserialization.TransactionID(tx)
 			if spendingTxID, exists := usedOutpoints[input.PreviousOutpoint]; exists {
 				return errors.Wrapf(ruleerrors.ErrDoubleSpendInSameBlock, "transaction %s spends "+
 					"outpoint %s that was already spent by "+
@@ -193,14 +193,14 @@ func (v *blockValidator) checkBlockHasNoChainedTransactions(block *externalapi.D
 	transactions := block.Transactions
 	transactionsSet := make(map[externalapi.DomainTransactionID]struct{}, len(transactions))
 	for _, transaction := range transactions {
-		txID := hashserialization.TransactionID(transaction)
+		txID := consensusserialization.TransactionID(transaction)
 		transactionsSet[*txID] = struct{}{}
 	}
 
 	for _, transaction := range transactions {
 		for i, transactionInput := range transaction.Inputs {
 			if _, ok := transactionsSet[transactionInput.PreviousOutpoint.TransactionID]; ok {
-				txID := hashserialization.TransactionID(transaction)
+				txID := consensusserialization.TransactionID(transaction)
 				return errors.Wrapf(ruleerrors.ErrChainedTransactions, "block contains chained "+
 					"transactions: Input %d of transaction %s spend "+
 					"an output of transaction %s", i, txID, transactionInput.PreviousOutpoint.TransactionID)
diff --git a/domain/consensus/processes/blockvalidator/block_header_in_context.go b/domain/consensus/processes/blockvalidator/block_header_in_context.go
index a30276a4..b3f00666 100644
--- a/domain/consensus/processes/blockvalidator/block_header_in_context.go
+++ b/domain/consensus/processes/blockvalidator/block_header_in_context.go
@@ -3,8 +3,8 @@ package blockvalidator
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
 	"github.com/pkg/errors"
 )
 
@@ -81,7 +81,7 @@ func (v *blockValidator) validateMedianTime(header *externalapi.DomainBlockHeade
 		return nil
 	}
 
-	hash := hashserialization.HeaderHash(header)
+	hash := consensusserialization.HeaderHash(header)
 	ghostdagData, err := v.ghostdagDataStore.Get(v.databaseContext, hash)
 	if err != nil {
 		return err
diff --git a/domain/consensus/processes/blockvalidator/block_header_in_isolation.go b/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
index 08801b4c..3c8455e5 100644
--- a/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
+++ b/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
@@ -5,9 +5,9 @@ import (
 
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
 	"github.com/pkg/errors"
 )
 
@@ -33,7 +33,7 @@ func (v *blockValidator) ValidateHeaderInIsolation(blockHash *externalapi.Domain
 }
 
 func (v *blockValidator) checkParentsLimit(header *externalapi.DomainBlockHeader) error {
-	hash := hashserialization.HeaderHash(header)
+	hash := consensusserialization.HeaderHash(header)
 	if len(header.ParentHashes) == 0 && *hash != *v.genesisHash {
 		return errors.Wrapf(ruleerrors.ErrNoParents, "block has no parents")
 	}
diff --git a/domain/consensus/processes/blockvalidator/proof_of_work.go b/domain/consensus/processes/blockvalidator/proof_of_work.go
index 26a69209..85471aef 100644
--- a/domain/consensus/processes/blockvalidator/proof_of_work.go
+++ b/domain/consensus/processes/blockvalidator/proof_of_work.go
@@ -3,8 +3,8 @@ package blockvalidator
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
 	"github.com/kaspanet/kaspad/util"
 	"github.com/pkg/errors"
 )
@@ -78,7 +78,7 @@ func (v *blockValidator) checkProofOfWork(header *externalapi.DomainBlockHeader)
 	// to avoid proof of work checks is set.
 	if !v.skipPoW {
 		// The block hash must be less than the claimed target.
-		hash := hashserialization.HeaderHash(header)
+		hash := consensusserialization.HeaderHash(header)
 		hashNum := hashes.ToBig(hash)
 		if hashNum.Cmp(target) > 0 {
 			return errors.Wrapf(ruleerrors.ErrUnexpectedDifficulty, "block hash of %064x is higher than "+
diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
index cdbf2337..27f81b4c 100644
--- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
+++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
@@ -179,7 +179,10 @@ func (csm *consensusStateManager) RestorePastUTXOSetIterator(blockHash *external
 
 	pastUTXO := model.NewUTXODiff()
 	for virtualUTXOSetIterator.Next() {
-		outpoint, utxoEntry := virtualUTXOSetIterator.Get()
+		outpoint, utxoEntry, err := virtualUTXOSetIterator.Get()
+		if err != nil {
+			return nil, err
+		}
 		pastUTXO.ToAdd[*outpoint] = utxoEntry
 	}
 
@@ -223,7 +226,7 @@ func (u utxoSetIterator) Next() bool {
 	return u.index != len(u.pairs)
 }
 
-func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry) {
+func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error) {
 	pair := u.pairs[u.index]
-	return &pair.outpoint, pair.entry
+	return &pair.outpoint, pair.entry, nil
 }
diff --git a/domain/consensus/processes/consensusstatemanager/consensusstatemanager.go b/domain/consensus/processes/consensusstatemanager/consensusstatemanager.go
index 332e25b1..cdd59f81 100644
--- a/domain/consensus/processes/consensusstatemanager/consensusstatemanager.go
+++ b/domain/consensus/processes/consensusstatemanager/consensusstatemanager.go
@@ -2,12 +2,12 @@ package consensusstatemanager
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
-	"github.com/kaspanet/kaspad/domain/dagconfig"
 )
 
 // consensusStateManager manages the node's consensus state
 type consensusStateManager struct {
-	dagParams       *dagconfig.Params
+	finalityDepth   uint64
+	pruningDepth    uint64
 	databaseContext model.DBManager
 
 	ghostdagManager       model.GHOSTDAGManager
@@ -37,7 +37,8 @@ type consensusStateManager struct {
 // New instantiates a new ConsensusStateManager
 func New(
 	databaseContext model.DBManager,
-	dagParams *dagconfig.Params,
+	finalityDepth uint64,
+	pruningDepth uint64,
 	ghostdagManager model.GHOSTDAGManager,
 	dagTopologyManager model.DAGTopologyManager,
 	dagTraversalManager model.DAGTraversalManager,
@@ -60,7 +61,8 @@ func New(
 	headerTipsStore model.HeaderTipsStore) (model.ConsensusStateManager, error) {
 
 	csm := &consensusStateManager{
-		dagParams:       dagParams,
+		finalityDepth:   finalityDepth,
+		pruningDepth:    pruningDepth,
 		databaseContext: databaseContext,
 
 		ghostdagManager:       ghostdagManager,
diff --git a/domain/consensus/processes/consensusstatemanager/finality.go b/domain/consensus/processes/consensusstatemanager/finality.go
index 00571f86..5db849ad 100644
--- a/domain/consensus/processes/consensusstatemanager/finality.go
+++ b/domain/consensus/processes/consensusstatemanager/finality.go
@@ -25,7 +25,7 @@ func (csm *consensusStateManager) virtualFinalityPoint(virtualGHOSTDAGData *mode
 	*externalapi.DomainHash, error) {
 
 	return csm.dagTraversalManager.HighestChainBlockBelowBlueScore(
-		model.VirtualBlockHash, virtualGHOSTDAGData.BlueScore-csm.dagParams.FinalityDepth())
+		model.VirtualBlockHash, virtualGHOSTDAGData.BlueScore-csm.finalityDepth)
 }
 
 func (csm *consensusStateManager) isViolatingFinality(
diff --git a/domain/consensus/processes/consensusstatemanager/multisets.go b/domain/consensus/processes/consensusstatemanager/multisets.go
index 0286b84a..08d808fd 100644
--- a/domain/consensus/processes/consensusstatemanager/multisets.go
+++ b/domain/consensus/processes/consensusstatemanager/multisets.go
@@ -3,8 +3,9 @@ package consensusstatemanager
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/multiset"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization"
 )
 
 func (csm *consensusStateManager) calculateMultiset(
@@ -47,7 +48,7 @@ func addTransactionToMultiset(multiset model.Multiset, transaction *externalapi.
 
 	for i, output := range transaction.Outputs {
 		outpoint := &externalapi.DomainOutpoint{
-			TransactionID: *hashserialization.TransactionID(transaction),
+			TransactionID: *consensusserialization.TransactionID(transaction),
 			Index:         uint32(i),
 		}
 		utxoEntry := &externalapi.UTXOEntry{
@@ -68,7 +69,7 @@ func addTransactionToMultiset(multiset model.Multiset, transaction *externalapi.
 func addUTXOToMultiset(multiset model.Multiset, entry *externalapi.UTXOEntry,
 	outpoint *externalapi.DomainOutpoint) error {
 
-	serializedUTXO, err := hashserialization.SerializeUTXO(entry, outpoint)
+	serializedUTXO, err := consensusserialization.SerializeUTXO(entry, outpoint)
 	if err != nil {
 		return err
 	}
@@ -80,7 +81,7 @@ func addUTXOToMultiset(multiset model.Multiset, entry *externalapi.UTXOEntry,
 func removeUTXOFromMultiset(multiset model.Multiset, entry *externalapi.UTXOEntry,
 	outpoint *externalapi.DomainOutpoint) error {
 
-	serializedUTXO, err := hashserialization.SerializeUTXO(entry, outpoint)
+	serializedUTXO, err := consensusserialization.SerializeUTXO(entry, outpoint)
 	if err != nil {
 		return err
 	}
@@ -89,14 +90,10 @@ func removeUTXOFromMultiset(multiset model.Multiset, entry *externalapi.UTXOEntr
 	return nil
 }
 
-func calcMultisetFromUTXOSetIterator(iterator model.ReadOnlyUTXOSetIterator) (model.Multiset, error) {
+func calcMultisetFromProtoUTXOSet(protoUTXOSet *utxoserialization.ProtoUTXOSet) (model.Multiset, error) {
 	ms := multiset.New()
-	for iterator.Next() {
-		entry, outpoint := iterator.Get()
-		err := addUTXOToMultiset(ms, outpoint, entry)
-		if err != nil {
-			return nil, err
-		}
+	for _, utxo := range protoUTXOSet.Utxos {
+		ms.Add(utxo.EntryOutpointPair)
 	}
 	return ms, nil
 }
diff --git a/domain/consensus/processes/consensusstatemanager/set_pruning_utxo_set.go b/domain/consensus/processes/consensusstatemanager/set_pruning_utxo_set.go
index 53856a77..f198ae58 100644
--- a/domain/consensus/processes/consensusstatemanager/set_pruning_utxo_set.go
+++ b/domain/consensus/processes/consensusstatemanager/set_pruning_utxo_set.go
@@ -1,9 +1,12 @@
 package consensusstatemanager
 
 import (
+	"github.com/golang/protobuf/proto"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization"
 	"github.com/kaspanet/kaspad/infrastructure/logger"
 	"github.com/pkg/errors"
 )
@@ -29,13 +32,18 @@ func (csm *consensusStateManager) SetPruningPointUTXOSet(serializedUTXOSet []byt
 }
 
 func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byte) error {
-	headerTipsPruningPoint, err := csm.headerTipsPruningPoint()
+	headerTipsPruningPoint, err := csm.HeaderTipsPruningPoint()
 	if err != nil {
 		return err
 	}
 
-	utxoSetIterator := deserializeUTXOSet(serializedUTXOSet)
-	utxoSetMultiSet, err := calcMultisetFromUTXOSetIterator(utxoSetIterator)
+	protoUTXOSet := &utxoserialization.ProtoUTXOSet{}
+	err = proto.Unmarshal(serializedUTXOSet, protoUTXOSet)
+	if err != nil {
+		return err
+	}
+
+	utxoSetMultiSet, err := calcMultisetFromProtoUTXOSet(protoUTXOSet)
 	if err != nil {
 		return err
 	}
@@ -56,7 +64,7 @@ func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byt
 		return err
 	}
 
-	csm.consensusStateStore.StageVirtualUTXOSet(utxoSetIterator)
+	csm.consensusStateStore.StageVirtualUTXOSet(protoUTXOSetToReadOnlyUTXOSetIterator(protoUTXOSet))
 
 	err = csm.ghostdagManager.GHOSTDAG(model.VirtualBlockHash)
 	if err != nil {
@@ -94,11 +102,33 @@ func (csm *consensusStateManager) commitSetPruningPointUTXOSetAll() error {
 	return dbTx.Commit()
 }
 
-func deserializeUTXOSet(serializedUTXOSet []byte) model.ReadOnlyUTXOSetIterator {
-	panic("implement me")
+type protoUTXOSetIterator struct {
+	utxoSet *utxoserialization.ProtoUTXOSet
+	index   int
+}
+
+func (p protoUTXOSetIterator) Next() bool {
+	p.index++
+	return p.index != len(p.utxoSet.Utxos)
+}
+
+func (p protoUTXOSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error) {
+	entry, outpoint, err := consensusserialization.DeserializeUTXO(p.utxoSet.Utxos[p.index].EntryOutpointPair)
+	if err != nil {
+		if consensusserialization.IsMalformedError(err) {
+			return nil, nil, errors.Wrap(ruleerrors.ErrMalformedUTXO, "malformed utxo")
+		}
+		return nil, nil, err
+	}
+
+	return outpoint, entry, nil
 }
 
-func (csm *consensusStateManager) headerTipsPruningPoint() (*externalapi.DomainHash, error) {
+func protoUTXOSetToReadOnlyUTXOSetIterator(protoUTXOSet *utxoserialization.ProtoUTXOSet) model.ReadOnlyUTXOSetIterator {
+	return &protoUTXOSetIterator{utxoSet: protoUTXOSet}
+}
+
+func (csm *consensusStateManager) HeaderTipsPruningPoint() (*externalapi.DomainHash, error) {
 	headerTips, err := csm.headerTipsStore.Tips(csm.databaseContext)
 	if err != nil {
 		return nil, err
@@ -120,9 +150,5 @@ func (csm *consensusStateManager) headerTipsPruningPoint() (*externalapi.DomainH
 		return nil, err
 	}
 
-	return csm.dagTraversalManager.HighestChainBlockBelowBlueScore(virtualHeaderHash, virtualHeaderGHOSTDAGData.BlueScore-pruningDepth())
-}
-
-func pruningDepth() uint64 {
-	panic("unimplemented")
+	return csm.dagTraversalManager.HighestChainBlockBelowBlueScore(virtualHeaderHash, virtualHeaderGHOSTDAGData.BlueScore-csm.pruningDepth)
 }
diff --git a/domain/consensus/processes/consensusstatemanager/utxoalgebra/diff_helpers.go b/domain/consensus/processes/consensusstatemanager/utxoalgebra/diff_helpers.go
index 56544f4b..bc3bd927 100644
--- a/domain/consensus/processes/consensusstatemanager/utxoalgebra/diff_helpers.go
+++ b/domain/consensus/processes/consensusstatemanager/utxoalgebra/diff_helpers.go
@@ -3,7 +3,7 @@ package utxoalgebra
 import (
 	"reflect"
 
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 
 	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
 
@@ -33,7 +33,7 @@ func DiffAddTransaction(utxoDiff *model.UTXODiff, transaction *externalapi.Domai
 	isCoinbase := transactionhelper.IsCoinBase(transaction)
 	for i, output := range transaction.Outputs {
 		outpoint := &externalapi.DomainOutpoint{
-			TransactionID: *hashserialization.TransactionID(transaction),
+			TransactionID: *consensusserialization.TransactionID(transaction),
 			Index:         uint32(i),
 		}
 		entry := &externalapi.UTXOEntry{
diff --git a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
index 2614cb26..44adb4ca 100644
--- a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
+++ b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
 
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 
 	"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
 
@@ -104,8 +104,8 @@ func calculateAcceptedIDMerkleRoot(multiblockAcceptanceData model.AcceptanceData
 	}
 	sort.Slice(acceptedTransactions, func(i, j int) bool {
 		return transactionid.Less(
-			hashserialization.TransactionID(acceptedTransactions[i]),
-			hashserialization.TransactionID(acceptedTransactions[j]))
+			consensusserialization.TransactionID(acceptedTransactions[i]),
+			consensusserialization.TransactionID(acceptedTransactions[j]))
 	})
 
 	return merkle.CalculateIDMerkleRoot(acceptedTransactions)
@@ -122,8 +122,8 @@ func (csm *consensusStateManager) validateCoinbaseTransaction(blockHash *externa
 		return err
 	}
 
-	coinbaseTransactionHash := hashserialization.TransactionHash(coinbaseTransaction)
-	expectedCoinbaseTransactionHash := hashserialization.TransactionHash(expectedCoinbaseTransaction)
+	coinbaseTransactionHash := consensusserialization.TransactionHash(coinbaseTransaction)
+	expectedCoinbaseTransactionHash := consensusserialization.TransactionHash(expectedCoinbaseTransaction)
 	if *coinbaseTransactionHash != *expectedCoinbaseTransactionHash {
 		return errors.Wrap(ruleerrors.ErrBadCoinbaseTransaction, "coinbase transaction is not built as expected")
 	}
diff --git a/domain/consensus/processes/dagtraversalmanager/anticone.go b/domain/consensus/processes/dagtraversalmanager/anticone.go
new file mode 100644
index 00000000..372b4fd6
--- /dev/null
+++ b/domain/consensus/processes/dagtraversalmanager/anticone.go
@@ -0,0 +1,53 @@
+package dagtraversalmanager
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
+)
+
+// AnticoneFromContext returns blocks in (context.Past ⋂ block.Anticone)
+func (dtm *dagTraversalManager) AnticoneFromContext(context, block *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
+	anticone := []*externalapi.DomainHash{}
+	queue := []*externalapi.DomainHash{context}
+	visited := hashset.New()
+
+	for len(queue) > 0 {
+		var current *externalapi.DomainHash
+		current, queue = queue[0], queue[1:]
+
+		if visited.Contains(current) {
+			continue
+		}
+
+		visited.Add(current)
+
+		currentIsAncestorOfBlock, err := dtm.dagTopologyManager.IsAncestorOf(current, block)
+		if err != nil {
+			return nil, err
+		}
+
+		if currentIsAncestorOfBlock {
+			continue
+		}
+
+		blockIsAncestorOfCurrent, err := dtm.dagTopologyManager.IsAncestorOf(block, current)
+		if err != nil {
+			return nil, err
+		}
+
+		if !blockIsAncestorOfCurrent {
+			anticone = append(anticone, current)
+		}
+
+		currentParents, err := dtm.dagTopologyManager.Parents(current)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, parent := range currentParents {
+			queue = append(queue, parent)
+		}
+	}
+
+	return anticone, nil
+}
diff --git a/domain/consensus/processes/dagtraversalmanager/dagtraversalmanager.go b/domain/consensus/processes/dagtraversalmanager/dagtraversalmanager.go
index bf9fd256..89190b0f 100644
--- a/domain/consensus/processes/dagtraversalmanager/dagtraversalmanager.go
+++ b/domain/consensus/processes/dagtraversalmanager/dagtraversalmanager.go
@@ -16,7 +16,7 @@ type dagTraversalManager struct {
 	ghostdagManager    model.GHOSTDAGManager
 }
 
-// selectedParentIterator implements the `model.SelectedParentIterator` API
+// selectedParentIterator implements the `model.BlockIterator` API
 type selectedParentIterator struct {
 	databaseContext   model.DBReader
 	ghostdagDataStore model.GHOSTDAGDataStore
@@ -55,7 +55,7 @@ func New(
 
 // SelectedParentIterator creates an iterator over the selected
 // parent chain of the given highHash
-func (dtm *dagTraversalManager) SelectedParentIterator(highHash *externalapi.DomainHash) model.SelectedParentIterator {
+func (dtm *dagTraversalManager) SelectedParentIterator(highHash *externalapi.DomainHash) model.BlockIterator {
 	return &selectedParentIterator{
 		databaseContext:   dtm.databaseContext,
 		ghostdagDataStore: dtm.ghostdagDataStore,
@@ -79,7 +79,7 @@ func (dtm *dagTraversalManager) HighestChainBlockBelowBlueScore(highHash *extern
 
 	requiredBlueScore := chainBlock.BlueScore - blueScore
 
-	// If we used `SelectedParentIterator` we'd need to do more calls to `ghostdagDataStore` so we can get the blueScore
+	// If we used `BlockIterator` we'd need to do more calls to `ghostdagDataStore` so we can get the blueScore
 	for chainBlock.BlueScore >= requiredBlueScore {
 		if chainBlock.SelectedParent == nil { // genesis
 			return blockHash, nil
diff --git a/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go b/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go
new file mode 100644
index 00000000..f69eef1c
--- /dev/null
+++ b/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go
@@ -0,0 +1,55 @@
+package dagtraversalmanager
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/pkg/errors"
+)
+
+type selectedChildIterator struct {
+	databaseContext    model.DBReader
+	dagTopologyManager model.DAGTopologyManager
+	highHash           *externalapi.DomainHash
+	current            *externalapi.DomainHash
+}
+
+func (s selectedChildIterator) Next() bool {
+	children, err := s.dagTopologyManager.Children(s.current)
+	if err != nil {
+		panic(err)
+	}
+
+	for _, child := range children {
+		isChildInSelectedParentChainOfHighHash, err := s.dagTopologyManager.IsInSelectedParentChainOf(child, s.highHash)
+		if err != nil {
+			panic(err)
+		}
+
+		if isChildInSelectedParentChainOfHighHash {
+			s.current = child
+			return true
+		}
+	}
+	return false
+}
+
+func (s selectedChildIterator) Get() *externalapi.DomainHash {
+	return s.current
+}
+
+func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (model.BlockIterator, error) {
+	isLowHashInSelectedParentChainOfHighHash, err := dtm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash)
+	if err != nil {
+		return nil, err
+	}
+
+	if !isLowHashInSelectedParentChainOfHighHash {
+		return nil, errors.Errorf("%s is not in the selected parent chain of %s", highHash, lowHash)
+	}
+	return &selectedChildIterator{
+		databaseContext:    dtm.databaseContext,
+		dagTopologyManager: dtm.dagTopologyManager,
+		highHash:           highHash,
+		current:            lowHash,
+	}, nil
+}
diff --git a/domain/consensus/processes/pruningmanager/pruningmanager.go b/domain/consensus/processes/pruningmanager/pruningmanager.go
index ade37b93..edc9766e 100644
--- a/domain/consensus/processes/pruningmanager/pruningmanager.go
+++ b/domain/consensus/processes/pruningmanager/pruningmanager.go
@@ -4,7 +4,7 @@ import (
 	"github.com/golang/protobuf/proto"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization"
 )
 
 // pruningManager resolves and manages the current pruning point
@@ -241,7 +241,7 @@ func (pm *pruningManager) deleteBlock(blockHash *externalapi.DomainHash) (alread
 }
 
 func serializeUTXOSetIterator(iter model.ReadOnlyUTXOSetIterator) ([]byte, error) {
-	serializedUtxo, err := hashserialization.ReadOnlyUTXOSetToProtoUTXOSet(iter)
+	serializedUtxo, err := utxoserialization.ReadOnlyUTXOSetToProtoUTXOSet(iter)
 	if err != nil {
 		return nil, err
 	}
diff --git a/domain/consensus/processes/syncmanager/antipast.go b/domain/consensus/processes/syncmanager/antipast.go
index f5add3a0..776fc294 100644
--- a/domain/consensus/processes/syncmanager/antipast.go
+++ b/domain/consensus/processes/syncmanager/antipast.go
@@ -99,7 +99,83 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
 }
 
 func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
-	panic("implement me")
+	headerTipsPruningPoint, err := sm.consensusStateManager.HeaderTipsPruningPoint()
+	if err != nil {
+		return nil, err
+	}
+
+	selectedChildIterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, headerTipsPruningPoint)
+	if err != nil {
+		return nil, err
+	}
+
+	lowHash := headerTipsPruningPoint
+	for selectedChildIterator.Next() {
+		lowHash = selectedChildIterator.Get()
+	}
+
+	hashesBetween, err := sm.antiPastHashesBetween(lowHash, highHash)
+	if err != nil {
+		return nil, err
+	}
+
+	lowHashAnticone, err := sm.dagTraversalManager.AnticoneFromContext(highHash, lowHash)
+	if err != nil {
+		return nil, err
+	}
+
+	blockToRemoveFromHashesBetween := hashset.New()
+	for _, blockHash := range lowHashAnticone {
+		isHeaderOnlyBlock, err := sm.isHeaderOnlyBlock(blockHash)
+		if err != nil {
+			return nil, err
+		}
+
+		if !isHeaderOnlyBlock {
+			blockToRemoveFromHashesBetween.Add(blockHash)
+		}
+	}
+
+	missingBlocks := make([]*externalapi.DomainHash, 0, len(hashesBetween)-len(lowHashAnticone))
+	for i, blockHash := range hashesBetween {
+		// If blockToRemoveFromHashesBetween is empty, no more blocks should be
+		// filtered, so we can copy the rest of hashesBetween into missingBlocks
+		if blockToRemoveFromHashesBetween.Length() == 0 {
+			missingBlocks = append(missingBlocks, hashesBetween[i:]...)
+			break
+		}
+
+		if blockToRemoveFromHashesBetween.Contains(blockHash) {
+			blockToRemoveFromHashesBetween.Remove(blockHash)
+			continue
+		}
+
+		missingBlocks = append(missingBlocks, blockHash)
+	}
+
+	if blockToRemoveFromHashesBetween.Length() != 0 {
+		return nil, errors.Errorf("blockToRemoveFromHashesBetween.Length() is expected to be 0")
+	}
+
+	return missingBlocks, nil
+}
+
+func (sm *syncManager) isHeaderOnlyBlock(blockHash *externalapi.DomainHash) (bool, error) {
+	exists, err := sm.blockStatusStore.Exists(sm.databaseContext, blockHash)
+	if err != nil {
+		return false, err
+	}
+
+	if !exists {
+		return false, nil
+	}
+
+	status, err := sm.blockStatusStore.Get(sm.databaseContext, blockHash)
+	if err != nil {
+		return false, err
+	}
+
+	return status == externalapi.StatusHeaderOnly, nil
 }
 
 func (sm *syncManager) isBlockInHeaderPruningPointFutureAndVirtualPast(blockHash *externalapi.DomainHash) (bool, error) {
diff --git a/domain/consensus/processes/syncmanager/syncmanager.go b/domain/consensus/processes/syncmanager/syncmanager.go
index a85b15ea..dd2d4930 100644
--- a/domain/consensus/processes/syncmanager/syncmanager.go
+++ b/domain/consensus/processes/syncmanager/syncmanager.go
@@ -10,8 +10,9 @@ type syncManager struct {
 	databaseContext  model.DBReader
 	genesisBlockHash *externalapi.DomainHash
 
-	dagTraversalManager model.DAGTraversalManager
-	dagTopologyManager  model.DAGTopologyManager
+	dagTraversalManager   model.DAGTraversalManager
+	dagTopologyManager    model.DAGTopologyManager
+	consensusStateManager model.ConsensusStateManager
 
 	ghostdagDataStore model.GHOSTDAGDataStore
 	blockStatusStore  model.BlockStatusStore
@@ -23,6 +24,8 @@ func New(
 	genesisBlockHash *externalapi.DomainHash,
 	dagTraversalManager model.DAGTraversalManager,
 	dagTopologyManager model.DAGTopologyManager,
+	consensusStateManager model.ConsensusStateManager,
+
 	ghostdagDataStore model.GHOSTDAGDataStore,
 	blockStatusStore model.BlockStatusStore) model.SyncManager {
 
@@ -30,8 +33,9 @@ func New(
 		databaseContext:  databaseContext,
 		genesisBlockHash: genesisBlockHash,
 
-		dagTraversalManager: dagTraversalManager,
-		dagTopologyManager:  dagTopologyManager,
+		dagTraversalManager:   dagTraversalManager,
+		dagTopologyManager:    dagTopologyManager,
+		consensusStateManager: consensusStateManager,
 
 		ghostdagDataStore: ghostdagDataStore,
 		blockStatusStore:  blockStatusStore,
diff --git a/domain/consensus/ruleerrors/rule_error.go b/domain/consensus/ruleerrors/rule_error.go
index ef2a45dc..b43a6291 100644
--- a/domain/consensus/ruleerrors/rule_error.go
+++ b/domain/consensus/ruleerrors/rule_error.go
@@ -3,7 +3,7 @@ package ruleerrors
 import (
 	"fmt"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/pkg/errors"
 )
 
@@ -233,6 +233,8 @@ var (
 	ErrBadPruningPointUTXOSet = newRuleError("ErrBadPruningPointUTXOSet")
 
 	ErrMissingBlockHeaderInIBD = newRuleError("ErrMissingBlockHeaderInIBD")
+
+	ErrMalformedUTXO = newRuleError("ErrMalformedUTXO")
 )
 
 // RuleError identifies a rule violation. It is used to indicate that
@@ -291,7 +293,7 @@ type InvalidTransaction struct {
 }
 
 func (invalid InvalidTransaction) String() string {
-	return fmt.Sprintf("(%v: %s)", hashserialization.TransactionID(invalid.Transaction), invalid.err)
+	return fmt.Sprintf("(%v: %s)", consensusserialization.TransactionID(invalid.Transaction), invalid.err)
 }
 
 // ErrInvalidTransactionsInNewBlock indicates that some transactions in a new block are invalid
diff --git a/domain/consensus/utils/hashserialization/common.go b/domain/consensus/utils/consensusserialization/common.go
similarity index 53%
rename from domain/consensus/utils/hashserialization/common.go
rename to domain/consensus/utils/consensusserialization/common.go
index a4fe9187..c2a941b4 100644
--- a/domain/consensus/utils/hashserialization/common.go
+++ b/domain/consensus/utils/consensusserialization/common.go
@@ -1,4 +1,4 @@
-package hashserialization
+package consensusserialization
 
 import (
 	"encoding/binary"
@@ -21,6 +21,8 @@ var (
 // errNoEncodingForType signifies that there's no encoding for the given type.
 var errNoEncodingForType = errors.New("there's no encoding for this type")
 
+var errMalformed = errors.New("errMalformed")
+
 // WriteElement writes the little endian representation of element to w.
 func WriteElement(w io.Writer, element interface{}) error {
 	// Attempt to write the element based on the concrete type via fast
@@ -102,3 +104,85 @@ func writeElements(w io.Writer, elements ...interface{}) error {
 	}
 	return nil
 }
+
+// readElement reads the next sequence of bytes from r using little endian
+// depending on the concrete type of element pointed to.
+func readElement(r io.Reader, element interface{}) error {
+	// Attempt to read the element based on the concrete type via fast
+	// type assertions first.
+	switch e := element.(type) {
+	case *int32:
+		rv, err := binaryserializer.Uint32(r, littleEndian)
+		if err != nil {
+			return err
+		}
+		*e = int32(rv)
+		return nil
+
+	case *uint32:
+		rv, err := binaryserializer.Uint32(r, littleEndian)
+		if err != nil {
+			return err
+		}
+		*e = rv
+		return nil
+
+	case *int64:
+		rv, err := binaryserializer.Uint64(r, littleEndian)
+		if err != nil {
+			return err
+		}
+		*e = int64(rv)
+		return nil
+
+	case *uint64:
+		rv, err := binaryserializer.Uint64(r, littleEndian)
+		if err != nil {
+			return err
+		}
+		*e = rv
+		return nil
+
+	case *uint8:
+		rv, err := binaryserializer.Uint8(r)
+		if err != nil {
+			return err
+		}
+		*e = rv
+		return nil
+
+	case *bool:
+		rv, err := binaryserializer.Uint8(r)
+		if err != nil {
+			return err
+		}
+		if rv == 0x00 {
+			*e = false
+		} else if rv == 0x01 {
+			*e = true
+		} else {
+			return errors.Wrapf(errMalformed, "in order to keep serialization canonical, true has to"+
+				" always be 0x01")
+		}
+		return nil
+	}
+
+	return errors.Wrapf(errNoEncodingForType, "couldn't find a way to read type %T", element)
+}
+
+// readElements reads multiple items from r. It is equivalent to multiple
+// calls to readElement.
+func readElements(r io.Reader, elements ...interface{}) error {
+	for _, element := range elements {
+		err := readElement(r, element)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// IsMalformedError returns whether the error indicates a malformed data source
+func IsMalformedError(err error) bool {
+	return errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, errMalformed)
+}
diff --git a/domain/consensus/utils/hashserialization/header.go b/domain/consensus/utils/consensusserialization/header.go
similarity index 97%
rename from domain/consensus/utils/hashserialization/header.go
rename to domain/consensus/utils/consensusserialization/header.go
index 07988810..8028b5c9 100644
--- a/domain/consensus/utils/hashserialization/header.go
+++ b/domain/consensus/utils/consensusserialization/header.go
@@ -1,4 +1,4 @@
-package hashserialization
+package consensusserialization
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
diff --git a/domain/consensus/utils/hashserialization/transaction.go b/domain/consensus/utils/consensusserialization/transaction.go
similarity index 99%
rename from domain/consensus/utils/hashserialization/transaction.go
rename to domain/consensus/utils/consensusserialization/transaction.go
index c5a920b0..651eb912 100644
--- a/domain/consensus/utils/hashserialization/transaction.go
+++ b/domain/consensus/utils/consensusserialization/transaction.go
@@ -1,4 +1,4 @@
-package hashserialization
+package consensusserialization
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
diff --git a/domain/consensus/utils/consensusserialization/utxo.go b/domain/consensus/utils/consensusserialization/utxo.go
new file mode 100644
index 00000000..3ebb4cb1
--- /dev/null
+++ b/domain/consensus/utils/consensusserialization/utxo.go
@@ -0,0 +1,131 @@
+package consensusserialization
+
+import (
+	"bytes"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
+	"io"
+
+	"github.com/pkg/errors"
+
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+)
+
+const uint32Size = 4
+
+// SerializeUTXO returns the byte-slice representation for given UTXOEntry-outpoint pair
+func SerializeUTXO(entry *externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint) ([]byte, error) {
+	w := &bytes.Buffer{}
+
+	err := serializeOutpoint(w, outpoint)
+	if err != nil {
+		return nil, err
+	}
+
+	err = serializeUTXOEntry(w, entry)
+	if err != nil {
+		return nil, err
+	}
+
+	return w.Bytes(), nil
+}
+
+// DeserializeUTXO deserializes the given byte slice to UTXOEntry-outpoint pair
+func DeserializeUTXO(utxoBytes []byte) (entry *externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint, err error) {
+	r := bytes.NewReader(utxoBytes)
+	outpoint, err = deserializeOutpoint(r)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	entry, err = deserializeUTXOEntry(r)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return entry, outpoint, nil
+}
+
+func serializeOutpoint(w io.Writer, outpoint *externalapi.DomainOutpoint) error {
+	_, err := w.Write(outpoint.TransactionID[:])
+	if err != nil {
+		return err
+	}
+
+	err = WriteElement(w, outpoint.Index)
+	if err != nil {
+		return errors.WithStack(err)
+	}
+
+	return nil
+}
+
+func deserializeOutpoint(r io.Reader) (*externalapi.DomainOutpoint, error) {
+	transactionIDBytes := make([]byte, externalapi.DomainHashSize)
+	_, err := io.ReadFull(r, transactionIDBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	transactionID, err := transactionid.FromBytes(transactionIDBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	indexBytes := make([]byte, uint32Size)
+	_, err = io.ReadFull(r, indexBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	var index uint32
+	err = readElement(r, &index)
+	if err != nil {
+		return nil, err
+	}
+
+	return &externalapi.DomainOutpoint{
+		TransactionID: *transactionID,
+		Index:         index,
+	}, nil
+}
+
+func serializeUTXOEntry(w io.Writer, entry *externalapi.UTXOEntry) error {
+	err := writeElements(w, entry.BlockBlueScore, entry.Amount, entry.IsCoinbase)
+	if err != nil {
+		return err
+	}
+
+	count := uint64(len(entry.ScriptPublicKey))
+	err = WriteElement(w, count)
+	if err != nil {
+		return err
+	}
+
+	_, err = w.Write(entry.ScriptPublicKey)
+	if err != nil {
+		return errors.WithStack(err)
+	}
+
+	return nil
+}
+
+func deserializeUTXOEntry(r io.Reader) (*externalapi.UTXOEntry, error) {
+	entry := &externalapi.UTXOEntry{}
+	err := readElements(r, entry.BlockBlueScore, entry.Amount, entry.IsCoinbase)
+	if err != nil {
+		return nil, err
+	}
+
+	count := uint64(len(entry.ScriptPublicKey))
+	err = readElement(r, count)
+	if err != nil {
+		return nil, err
+	}
+
+	_, err = r.Read(entry.ScriptPublicKey)
+	if err != nil {
+		return nil, errors.WithStack(err)
+	}
+
+	return entry, nil
+}
diff --git a/domain/consensus/utils/hashserialization/utxo.go b/domain/consensus/utils/hashserialization/utxo.go
deleted file mode 100644
index 4811331e..00000000
--- a/domain/consensus/utils/hashserialization/utxo.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package hashserialization
-
-import (
-	"bytes"
-	"encoding/binary"
-	"github.com/kaspanet/kaspad/domain/consensus/model"
-	"io"
-
-	"github.com/pkg/errors"
-
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-)
-
-// outpointIndexByteOrder is the byte order for serializing the outpoint index.
-// It uses big endian to ensure that when outpoint is used as database key, the
-// keys will be iterated in an ascending order by the outpoint index.
-var outpointIndexByteOrder = binary.BigEndian
-
-const (
-	outpointLen             = externalapi.DomainHashSize + 4
-	entryMinLen             = 8 + 1 + 8 + 8
-	averageScriptPubKeySize = 20
-)
-
-// ReadOnlyUTXOSetToProtoUTXOSet converts ReadOnlyUTXOSetIterator to ProtoUTXOSet
-func ReadOnlyUTXOSetToProtoUTXOSet(iter model.ReadOnlyUTXOSetIterator) (*ProtoUTXOSet, error) {
-	protoUTXOSet := &ProtoUTXOSet{
-		Utxos: []*ProtoUTXO{},
-	}
-
-	for iter.Next() {
-		outpoint, entry := iter.Get()
-
-		serializedOutpoint := bytes.NewBuffer(make([]byte, 0, outpointLen))
-		err := serializeOutpoint(serializedOutpoint, outpoint)
-		if err != nil {
-			return nil, err
-		}
-
-		serializedEntry := bytes.NewBuffer(make([]byte, 0, entryMinLen+averageScriptPubKeySize))
-		err = serializeUTXOEntry(serializedEntry, entry)
-		if err != nil {
-			return nil, err
-		}
-
-		protoUTXOSet.Utxos = append(protoUTXOSet.Utxos, &ProtoUTXO{
-			Entry:    serializedEntry.Bytes(),
-			Outpoint: serializedOutpoint.Bytes(),
-		})
-	}
-	return protoUTXOSet, nil
-}
-
-// SerializeUTXO returns the byte-slice representation for given UTXOEntry
-func SerializeUTXO(entry *externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint) ([]byte, error) {
-	w := &bytes.Buffer{}
-
-	err := serializeOutpoint(w, outpoint)
-	if err != nil {
-		return nil, err
-	}
-
-	err = serializeUTXOEntry(w, entry)
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
-}
-
-func serializeOutpoint(w io.Writer, outpoint *externalapi.DomainOutpoint) error {
-	_, err := w.Write(outpoint.TransactionID[:])
-	if err != nil {
-		return err
-	}
-
-	var buf [4]byte
-	outpointIndexByteOrder.PutUint32(buf[:], outpoint.Index)
-	_, err = w.Write(buf[:])
-	if err != nil {
-		return errors.WithStack(err)
-	}
-
-	return nil
-}
-
-func serializeUTXOEntry(w io.Writer, entry *externalapi.UTXOEntry) error {
-	err := writeElements(w, entry.BlockBlueScore, entry.Amount, entry.IsCoinbase)
-	if err != nil {
-		return err
-	}
-
-	count := uint64(len(entry.ScriptPublicKey))
-	err = WriteElement(w, count)
-	if err != nil {
-		return err
-	}
-
-	_, err = w.Write(entry.ScriptPublicKey)
-	if err != nil {
-		return errors.WithStack(err)
-	}
-
-	return nil
-}
diff --git a/domain/consensus/utils/hashset/hash_set.go b/domain/consensus/utils/hashset/hash_set.go
index 3181c503..c123ebc7 100644
--- a/domain/consensus/utils/hashset/hash_set.go
+++ b/domain/consensus/utils/hashset/hash_set.go
@@ -84,3 +84,8 @@ func (hs HashSet) ToSlice() []*externalapi.DomainHash {
 
 	return slice
 }
+
+// Length returns the length of this HashSet
+func (hs HashSet) Length() int {
+	return hs.Length()
+}
diff --git a/domain/consensus/utils/merkle/merkle.go b/domain/consensus/utils/merkle/merkle.go
index 584a3a9b..d7166b87 100644
--- a/domain/consensus/utils/merkle/merkle.go
+++ b/domain/consensus/utils/merkle/merkle.go
@@ -2,8 +2,8 @@ package merkle
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
 	"github.com/pkg/errors"
 	"math"
 )
@@ -47,7 +47,7 @@ func hashMerkleBranches(left, right *externalapi.DomainHash) *externalapi.Domain
 func CalculateHashMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
 	txHashes := make([]*externalapi.DomainHash, len(transactions))
 	for i, tx := range transactions {
-		txHashes[i] = hashserialization.TransactionHash(tx)
+		txHashes[i] = consensusserialization.TransactionHash(tx)
 	}
 	return merkleRoot(txHashes)
 }
@@ -57,7 +57,7 @@ func CalculateHashMerkleRoot(transactions []*externalapi.DomainTransaction) *ext
 func CalculateIDMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
 	txIDs := make([]*externalapi.DomainHash, len(transactions))
 	for i, tx := range transactions {
-		txIDs[i] = (*externalapi.DomainHash)(hashserialization.TransactionID(tx))
+		txIDs[i] = (*externalapi.DomainHash)(consensusserialization.TransactionID(tx))
 	}
 	return merkleRoot(txIDs)
 }
diff --git a/domain/consensus/utils/txscript/script.go b/domain/consensus/utils/txscript/script.go
index 1c12ab85..17f6a136 100644
--- a/domain/consensus/utils/txscript/script.go
+++ b/domain/consensus/utils/txscript/script.go
@@ -8,7 +8,7 @@ import (
 	"bytes"
 	"fmt"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/pkg/errors"
 )
 
@@ -362,7 +362,7 @@ func calcSignatureHash(script []parsedOpcode, hashType SigHashType, tx *external
 	// The final hash is the double sha256 of both the serialized modified
 	// transaction and the hash type (encoded as a 4-byte little-endian
 	// value) appended.
-	return hashserialization.TransactionHashForSigning(&txCopy, uint32(hashType)), nil
+	return consensusserialization.TransactionHashForSigning(&txCopy, uint32(hashType)), nil
 }
 
 // asSmallInt returns the passed opcode, which must be true according to
diff --git a/domain/consensus/utils/hashserialization/generate.go b/domain/consensus/utils/utxoserialization/generate.go
similarity index 83%
rename from domain/consensus/utils/hashserialization/generate.go
rename to domain/consensus/utils/utxoserialization/generate.go
index c8588e58..f58bfc47 100644
--- a/domain/consensus/utils/hashserialization/generate.go
+++ b/domain/consensus/utils/utxoserialization/generate.go
@@ -1,3 +1,3 @@
 //go:generate protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative utxo.proto
 
-package hashserialization
+package utxoserialization
diff --git a/domain/consensus/utils/utxoserialization/utxo.go b/domain/consensus/utils/utxoserialization/utxo.go
new file mode 100644
index 00000000..86008237
--- /dev/null
+++ b/domain/consensus/utils/utxoserialization/utxo.go
@@ -0,0 +1,30 @@
+package utxoserialization
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
+)
+
+// ReadOnlyUTXOSetToProtoUTXOSet converts ReadOnlyUTXOSetIterator to ProtoUTXOSet
+func ReadOnlyUTXOSetToProtoUTXOSet(iter model.ReadOnlyUTXOSetIterator) (*ProtoUTXOSet, error) {
+	protoUTXOSet := &ProtoUTXOSet{
+		Utxos: []*ProtoUTXO{},
+	}
+
+	for iter.Next() {
+		outpoint, entry, err := iter.Get()
+		if err != nil {
+			return nil, err
+		}
+
+		serializedUTXOBytes, err := consensusserialization.SerializeUTXO(entry, outpoint)
+		if err != nil {
+			return nil, err
+		}
+
+		protoUTXOSet.Utxos = append(protoUTXOSet.Utxos, &ProtoUTXO{
+			EntryOutpointPair: serializedUTXOBytes,
+		})
+	}
+	return protoUTXOSet, nil
+}
diff --git a/domain/consensus/utils/hashserialization/utxo.pb.go b/domain/consensus/utils/utxoserialization/utxo.pb.go
similarity index 72%
rename from domain/consensus/utils/hashserialization/utxo.pb.go
rename to domain/consensus/utils/utxoserialization/utxo.pb.go
index 75da3bf4..faa28b24 100644
--- a/domain/consensus/utils/hashserialization/utxo.pb.go
+++ b/domain/consensus/utils/utxoserialization/utxo.pb.go
@@ -1,10 +1,10 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.12.4
+// 	protoc        v3.12.3
 // source: utxo.proto
 
-package hashserialization
+package utxoserialization
 
 import (
 	proto "github.com/golang/protobuf/proto"
@@ -30,8 +30,7 @@ type ProtoUTXO struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Entry    []byte `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"`
-	Outpoint []byte `protobuf:"bytes,2,opt,name=outpoint,proto3" json:"outpoint,omitempty"`
+	EntryOutpointPair []byte `protobuf:"bytes,1,opt,name=entryOutpointPair,proto3" json:"entryOutpointPair,omitempty"`
 }
 
 func (x *ProtoUTXO) Reset() {
@@ -66,16 +65,9 @@ func (*ProtoUTXO) Descriptor() ([]byte, []int) {
 	return file_utxo_proto_rawDescGZIP(), []int{0}
 }
 
-func (x *ProtoUTXO) GetEntry() []byte {
+func (x *ProtoUTXO) GetEntryOutpointPair() []byte {
 	if x != nil {
-		return x.Entry
-	}
-	return nil
-}
-
-func (x *ProtoUTXO) GetOutpoint() []byte {
-	if x != nil {
-		return x.Outpoint
+		return x.EntryOutpointPair
 	}
 	return nil
 }
@@ -130,22 +122,21 @@ func (x *ProtoUTXOSet) GetUtxos() []*ProtoUTXO {
 var File_utxo_proto protoreflect.FileDescriptor
 
 var file_utxo_proto_rawDesc = []byte{
-	0x0a, 0x0a, 0x75, 0x74, 0x78, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x68, 0x61,
-	0x73, 0x68, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22,
-	0x3d, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x55, 0x54, 0x58, 0x4f, 0x12, 0x14, 0x0a, 0x05,
-	0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x6e, 0x74,
-	0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x42,
-	0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x55, 0x54, 0x58, 0x4f, 0x53, 0x65, 0x74, 0x12, 0x32,
-	0x0a, 0x05, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
-	0x68, 0x61, 0x73, 0x68, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
-	0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x55, 0x54, 0x58, 0x4f, 0x52, 0x05, 0x75, 0x74, 0x78,
-	0x6f, 0x73, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
-	0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64,
-	0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75,
-	0x73, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x73, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x73, 0x65, 0x72, 0x69,
-	0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x33,
+	0x0a, 0x0a, 0x75, 0x74, 0x78, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x75, 0x74,
+	0x78, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22,
+	0x39, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x55, 0x54, 0x58, 0x4f, 0x12, 0x2c, 0x0a, 0x11,
+	0x65, 0x6e, 0x74, 0x72, 0x79, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x50, 0x61, 0x69,
+	0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x4f, 0x75,
+	0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x50, 0x61, 0x69, 0x72, 0x22, 0x42, 0x0a, 0x0c, 0x50, 0x72,
+	0x6f, 0x74, 0x6f, 0x55, 0x54, 0x58, 0x4f, 0x53, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x75, 0x74,
+	0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x75, 0x74, 0x78, 0x6f,
+	0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x72,
+	0x6f, 0x74, 0x6f, 0x55, 0x54, 0x58, 0x4f, 0x52, 0x05, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x42, 0x45,
+	0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73,
+	0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x64, 0x6f, 0x6d,
+	0x61, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2f, 0x75, 0x74,
+	0x69, 0x6c, 0x73, 0x2f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -162,11 +153,11 @@ func file_utxo_proto_rawDescGZIP() []byte {
 
 var file_utxo_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
 var file_utxo_proto_goTypes = []interface{}{
-	(*ProtoUTXO)(nil),    // 0: hashserialization.ProtoUTXO
-	(*ProtoUTXOSet)(nil), // 1: hashserialization.ProtoUTXOSet
+	(*ProtoUTXO)(nil),    // 0: utxoserialization.ProtoUTXO
+	(*ProtoUTXOSet)(nil), // 1: utxoserialization.ProtoUTXOSet
 }
 var file_utxo_proto_depIdxs = []int32{
-	0, // 0: hashserialization.ProtoUTXOSet.utxos:type_name -> hashserialization.ProtoUTXO
+	0, // 0: utxoserialization.ProtoUTXOSet.utxos:type_name -> utxoserialization.ProtoUTXO
 	1, // [1:1] is the sub-list for method output_type
 	1, // [1:1] is the sub-list for method input_type
 	1, // [1:1] is the sub-list for extension type_name
diff --git a/domain/consensus/utils/hashserialization/utxo.proto b/domain/consensus/utils/utxoserialization/utxo.proto
similarity index 62%
rename from domain/consensus/utils/hashserialization/utxo.proto
rename to domain/consensus/utils/utxoserialization/utxo.proto
index ccafd6d8..01bbebe0 100644
--- a/domain/consensus/utils/hashserialization/utxo.proto
+++ b/domain/consensus/utils/utxoserialization/utxo.proto
@@ -1,11 +1,10 @@
 syntax = "proto3";
-package hashserialization;
+package utxoserialization;
 
-option go_package = "github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization";
+option go_package = "github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization";
 
 message ProtoUTXO {
-  bytes entry = 1;
-  bytes outpoint = 2;
+  bytes entryOutpointPair = 1;
 }
 
 message ProtoUTXOSet {
diff --git a/domain/miningmanager/blocktemplatebuilder/txselection.go b/domain/miningmanager/blocktemplatebuilder/txselection.go
index 3b3851de..1f8fc773 100644
--- a/domain/miningmanager/blocktemplatebuilder/txselection.go
+++ b/domain/miningmanager/blocktemplatebuilder/txselection.go
@@ -2,7 +2,7 @@ package blocktemplatebuilder
 
 import (
 	consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
 	"math"
 	"math/rand"
@@ -103,7 +103,7 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
 		if txsForBlockTemplate.totalMass+selectedTx.Mass < txsForBlockTemplate.totalMass ||
 			txsForBlockTemplate.totalMass+selectedTx.Mass > btb.policy.BlockMaxMass {
 			log.Tracef("Tx %s would exceed the max block mass. "+
-				"As such, stopping.", hashserialization.TransactionID(tx))
+				"As such, stopping.", consensusserialization.TransactionID(tx))
 			break
 		}
 
@@ -121,7 +121,7 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
 				log.Tracef("Tx %s would exceed the gas limit in "+
 					"subnetwork %s. Removing all remaining txs from this "+
 					"subnetwork.",
-					hashserialization.TransactionID(tx), subnetworkID)
+					consensusserialization.TransactionID(tx), subnetworkID)
 				for _, candidateTx := range candidateTxs {
 					// candidateTxs are ordered by subnetwork, so we can safely assume
 					// that transactions after subnetworkID will not be relevant.
@@ -146,7 +146,7 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
 		txsForBlockTemplate.totalFees += selectedTx.Fee
 
 		log.Tracef("Adding tx %s (feePerMegaGram %d)",
-			hashserialization.TransactionID(tx), selectedTx.Fee*1e6/selectedTx.Mass)
+			consensusserialization.TransactionID(tx), selectedTx.Fee*1e6/selectedTx.Mass)
 
 		markCandidateTxForDeletion(selectedTx)
 	}
diff --git a/domain/miningmanager/mempool/mempool.go b/domain/miningmanager/mempool/mempool.go
index 82935759..1157aa07 100644
--- a/domain/miningmanager/mempool/mempool.go
+++ b/domain/miningmanager/mempool/mempool.go
@@ -10,8 +10,8 @@ import (
 	"github.com/kaspanet/kaspad/domain/consensus"
 	consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/estimatedsize"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
 	miningmanagermodel "github.com/kaspanet/kaspad/domain/miningmanager/model"
 	"github.com/kaspanet/kaspad/infrastructure/logger"
 	"github.com/kaspanet/kaspad/util"
@@ -133,7 +133,7 @@ type orphanTx struct {
 // This function MUST be called with the mempool lock held (for writes).
 func (mp *mempool) removeOrphan(tx *consensusexternalapi.DomainTransaction, removeRedeemers bool) {
 	// Nothing to do if passed tx is not an orphan.
-	txID := hashserialization.TransactionID(tx)
+	txID := consensusserialization.TransactionID(tx)
 	otx, exists := mp.orphans[*txID]
 	if !exists {
 		return
@@ -234,7 +234,7 @@ func (mp *mempool) addOrphan(tx *consensusexternalapi.DomainTransaction) {
 	// This will periodically remove any expired orphans and evict a random
 	// orphan if space is still needed.
 	mp.limitNumOrphans()
-	txID := hashserialization.TransactionID(tx)
+	txID := consensusserialization.TransactionID(tx)
 	mp.orphans[*txID] = &orphanTx{
 		tx:         tx,
 		expiration: mstime.Now().Add(orphanTTL),
@@ -247,7 +247,7 @@ func (mp *mempool) addOrphan(tx *consensusexternalapi.DomainTransaction) {
 		mp.orphansByPrev[txIn.PreviousOutpoint][*txID] = tx
 	}
 
-	log.Debugf("Stored orphan transaction %s (total: %d)", hashserialization.TransactionID(tx),
+	log.Debugf("Stored orphan transaction %s (total: %d)", consensusserialization.TransactionID(tx),
 		len(mp.orphans))
 }
 
@@ -343,7 +343,7 @@ func (mp *mempool) haveTransaction(txID *consensusexternalapi.DomainTransactionI
 // This function MUST be called with the mempool lock held (for writes).
 func (mp *mempool) removeBlockTransactionsFromPool(txs []*consensusexternalapi.DomainTransaction) error {
 	for _, tx := range txs[util.CoinbaseTransactionIndex+1:] {
-		txID := hashserialization.TransactionID(tx)
+		txID := consensusserialization.TransactionID(tx)
 
 		if _, exists := mp.fetchTxDesc(txID); !exists {
 			continue
@@ -363,7 +363,7 @@ func (mp *mempool) removeBlockTransactionsFromPool(txs []*consensusexternalapi.D
 //
 // This function MUST be called with the mempool lock held (for writes).
 func (mp *mempool) removeTransactionAndItsChainedTransactions(tx *consensusexternalapi.DomainTransaction) error {
-	txID := hashserialization.TransactionID(tx)
+	txID := consensusserialization.TransactionID(tx)
 	// Remove any transactions which rely on this one.
 	for i := uint32(0); i < uint32(len(tx.Outputs)); i++ {
 		prevOut := consensusexternalapi.DomainOutpoint{TransactionID: *txID, Index: i}
@@ -397,7 +397,7 @@ func (mp *mempool) cleanTransactionFromSets(tx *consensusexternalapi.DomainTrans
 		return err
 	}
 
-	txID := hashserialization.TransactionID(tx)
+	txID := consensusserialization.TransactionID(tx)
 	delete(mp.pool, *txID)
 	delete(mp.chainedTransactions, *txID)
 
@@ -410,7 +410,7 @@ func (mp *mempool) cleanTransactionFromSets(tx *consensusexternalapi.DomainTrans
 // This function MUST be called with the mempool lock held (for writes).
 
 func (mp *mempool) updateBlockTransactionChainedTransactions(tx *consensusexternalapi.DomainTransaction) {
-	prevOut := consensusexternalapi.DomainOutpoint{TransactionID: *hashserialization.TransactionID(tx)}
+	prevOut := consensusexternalapi.DomainOutpoint{TransactionID: *consensusserialization.TransactionID(tx)}
 	for txOutIdx := range tx.Outputs {
 		// Skip to the next available output if there are none.
 		prevOut.Index = uint32(txOutIdx)
@@ -424,7 +424,7 @@ func (mp *mempool) updateBlockTransactionChainedTransactions(tx *consensusextern
 		if txDesc.depCount == 0 {
 			// Transaction may be already removed by recursive calls, if removeRedeemers is true.
 			// So avoid moving it into main pool
-			txDescID := hashserialization.TransactionID(txDesc.DomainTransaction)
+			txDescID := consensusserialization.TransactionID(txDesc.DomainTransaction)
 			if _, ok := mp.chainedTransactions[*txDescID]; ok {
 				delete(mp.chainedTransactions, *txDescID)
 				mp.pool[*txDescID] = txDesc
@@ -438,7 +438,7 @@ func (mp *mempool) updateBlockTransactionChainedTransactions(tx *consensusextern
 //
 // This function MUST be called with the mempool lock held (for writes).
 func (mp *mempool) removeChainTransaction(tx *consensusexternalapi.DomainTransaction) {
-	delete(mp.chainedTransactions, *hashserialization.TransactionID(tx))
+	delete(mp.chainedTransactions, *consensusserialization.TransactionID(tx))
 	for _, txIn := range tx.Inputs {
 		delete(mp.chainedTransactionByPreviousOutpoint, txIn.PreviousOutpoint)
 	}
@@ -452,10 +452,10 @@ func (mp *mempool) removeChainTransaction(tx *consensusexternalapi.DomainTransac
 //
 // This function MUST be called with the mempool lock held (for writes).
 func (mp *mempool) removeDoubleSpends(tx *consensusexternalapi.DomainTransaction) error {
-	txID := *hashserialization.TransactionID(tx)
+	txID := *consensusserialization.TransactionID(tx)
 	for _, txIn := range tx.Inputs {
 		if txRedeemer, ok := mp.mempoolUTXOSet.poolTransactionBySpendingOutpoint(txIn.PreviousOutpoint); ok {
-			if !(*hashserialization.TransactionID(txRedeemer) == txID) {
+			if !(*consensusserialization.TransactionID(txRedeemer) == txID) {
 				err := mp.removeTransactionAndItsChainedTransactions(txRedeemer)
 				if err != nil {
 					return err
@@ -478,7 +478,7 @@ func (mp *mempool) addTransaction(tx *consensusexternalapi.DomainTransaction, ma
 		DomainTransaction: tx,
 		depCount:          len(parentsInPool),
 	}
-	txID := *hashserialization.TransactionID(tx)
+	txID := *consensusserialization.TransactionID(tx)
 
 	if len(parentsInPool) == 0 {
 		mp.pool[txID] = txDescriptor
@@ -508,7 +508,7 @@ func (mp *mempool) checkPoolDoubleSpend(tx *consensusexternalapi.DomainTransacti
 		if txR, exists := mp.mempoolUTXOSet.poolTransactionBySpendingOutpoint(txIn.PreviousOutpoint); exists {
 			str := fmt.Sprintf("output %s already spent by "+
 				"transaction %s in the memory pool",
-				txIn.PreviousOutpoint, hashserialization.TransactionID(txR))
+				txIn.PreviousOutpoint, consensusserialization.TransactionID(txR))
 			return txRuleError(RejectDuplicate, str)
 		}
 	}
@@ -540,7 +540,7 @@ func (mp *mempool) fetchTxDesc(txID *consensusexternalapi.DomainTransactionID) (
 //
 // This function MUST be called with the mempool lock held (for writes).
 func (mp *mempool) maybeAcceptTransaction(tx *consensusexternalapi.DomainTransaction, rejectDupOrphans bool) ([]consensusexternalapi.DomainOutpoint, *txDescriptor, error) {
-	txID := hashserialization.TransactionID(tx)
+	txID := consensusserialization.TransactionID(tx)
 
 	// Don't accept the transaction if it already exists in the pool. This
 	// applies to orphan transactions as well when the reject duplicate
@@ -679,7 +679,7 @@ func (mp *mempool) processOrphans(acceptedTx *consensusexternalapi.DomainTransac
 		firstElement := processList.Remove(processList.Front())
 		processItem := firstElement.(*consensusexternalapi.DomainTransaction)
 
-		prevOut := consensusexternalapi.DomainOutpoint{TransactionID: *hashserialization.TransactionID(processItem)}
+		prevOut := consensusexternalapi.DomainOutpoint{TransactionID: *consensusserialization.TransactionID(processItem)}
 		for txOutIdx := range processItem.Outputs {
 			// Look up all orphans that redeem the output that is
 			// now available. This will typically only be one, but
@@ -759,7 +759,7 @@ func (mp *mempool) processOrphans(acceptedTx *consensusexternalapi.DomainTransac
 //
 // This function is safe for concurrent access.
 func (mp *mempool) ValidateAndInsertTransaction(tx *consensusexternalapi.DomainTransaction, allowOrphan bool) error {
-	log.Tracef("Processing transaction %s", hashserialization.TransactionID(tx))
+	log.Tracef("Processing transaction %s", consensusserialization.TransactionID(tx))
 
 	// Protect concurrent access.
 	mp.mtx.Lock()
@@ -801,7 +801,7 @@ func (mp *mempool) ValidateAndInsertTransaction(tx *consensusexternalapi.DomainT
 		// which is not really always the case.
 		str := fmt.Sprintf("orphan transaction %s references "+
 			"outputs of unknown or fully-spent "+
-			"transaction %s", hashserialization.TransactionID(tx), missingParents[0])
+			"transaction %s", consensusserialization.TransactionID(tx), missingParents[0])
 		return txRuleError(RejectDuplicate, str)
 	}
 
@@ -870,7 +870,7 @@ func (mp *mempool) HandleNewBlockTransactions(txs []*consensusexternalapi.Domain
 	for _, tx := range txs[util.CoinbaseTransactionIndex+1:] {
 		err := mp.removeDoubleSpends(tx)
 		if err != nil {
-			log.Infof("Failed removing tx from mempool: %s, '%s'", hashserialization.TransactionID(tx), err)
+			log.Infof("Failed removing tx from mempool: %s, '%s'", consensusserialization.TransactionID(tx), err)
 		}
 		mp.removeOrphan(tx, false)
 		acceptedOrphans := mp.processOrphans(tx)
@@ -888,7 +888,7 @@ func (mp *mempool) RemoveTransactions(txs []*consensusexternalapi.DomainTransact
 	for _, tx := range txs {
 		err := mp.removeDoubleSpends(tx)
 		if err != nil {
-			log.Infof("Failed removing tx from mempool: %s, '%s'", hashserialization.TransactionID(tx), err)
+			log.Infof("Failed removing tx from mempool: %s, '%s'", consensusserialization.TransactionID(tx), err)
 		}
 		mp.removeOrphan(tx, true)
 	}
diff --git a/domain/miningmanager/mempool/mempool_utxoset.go b/domain/miningmanager/mempool/mempool_utxoset.go
index 29b5f1e0..0b010280 100644
--- a/domain/miningmanager/mempool/mempool_utxoset.go
+++ b/domain/miningmanager/mempool/mempool_utxoset.go
@@ -3,7 +3,7 @@ package mempool
 import (
 	"github.com/kaspanet/kaspad/domain/blockdag"
 	consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
 	"github.com/pkg/errors"
 )
 
@@ -39,7 +39,7 @@ func (mpus *mempoolUTXOSet) checkExists(tx *consensusexternalapi.DomainTransacti
 	}
 
 	// Check if it creates an already existing UTXO
-	outpoint := consensusexternalapi.DomainOutpoint{TransactionID: *hashserialization.TransactionID(tx)}
+	outpoint := consensusexternalapi.DomainOutpoint{TransactionID: *consensusserialization.TransactionID(tx)}
 	for i := range tx.Outputs {
 		outpoint.Index = uint32(i)
 		if _, exists := mpus.poolUnspentOutputs[outpoint]; exists {
@@ -54,13 +54,13 @@ func (mpus *mempoolUTXOSet) checkExists(tx *consensusexternalapi.DomainTransacti
 func (mpus *mempoolUTXOSet) addTx(tx *consensusexternalapi.DomainTransaction) error {
 	for _, txIn := range tx.Inputs {
 		if existingTx, exists := mpus.transactionByPreviousOutpoint[txIn.PreviousOutpoint]; exists {
-			return errors.Errorf("outpoint %s is already used by %s", txIn.PreviousOutpoint, hashserialization.TransactionID(existingTx))
+			return errors.Errorf("outpoint %s is already used by %s", txIn.PreviousOutpoint, consensusserialization.TransactionID(existingTx))
 		}
 		mpus.transactionByPreviousOutpoint[txIn.PreviousOutpoint] = tx
 	}
 
 	for i, txOut := range tx.Outputs {
-		outpoint := consensusexternalapi.DomainOutpoint{TransactionID: *hashserialization.TransactionID(tx), Index: uint32(i)}
+		outpoint := consensusexternalapi.DomainOutpoint{TransactionID: *consensusserialization.TransactionID(tx), Index: uint32(i)}
 		if _, exists := mpus.poolUnspentOutputs[outpoint]; exists {
 			return errors.Errorf("outpoint %s already exists", outpoint)
 		}
@@ -84,7 +84,7 @@ func (mpus *mempoolUTXOSet) removeTx(tx *consensusexternalapi.DomainTransaction)
 		delete(mpus.transactionByPreviousOutpoint, txIn.PreviousOutpoint)
 	}
 
-	outpoint := consensusexternalapi.DomainOutpoint{TransactionID: *hashserialization.TransactionID(tx)}
+	outpoint := consensusexternalapi.DomainOutpoint{TransactionID: *consensusserialization.TransactionID(tx)}
 	for i := range tx.Outputs {
 		outpoint.Index = uint32(i)
 		if _, exists := mpus.poolUnspentOutputs[outpoint]; !exists {


Test patch:
diff --git a/domain/consensus/datastructures/consensusstatestore/utxo.go b/domain/consensus/datastructures/consensusstatestore/utxo.go
index 3b5a2d75..b5ace883 100644
--- a/domain/consensus/datastructures/consensusstatestore/utxo.go
+++ b/domain/consensus/datastructures/consensusstatestore/utxo.go
@@ -180,7 +180,7 @@ func (u utxoSetIterator) Next() bool {
 	return u.cursor.Next()
 }
 
-func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry) {
+func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error) {
 	key, err := u.cursor.Key()
 	if err != nil {
 		panic(err)
@@ -188,20 +188,20 @@ func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry
 
 	utxoEntryBytes, err := u.cursor.Value()
 	if err != nil {
-		panic(err)
+		return nil, nil, err
 	}
 
 	outpoint, err = deserializeOutpoint(key.Suffix())
 	if err != nil {
-		panic(err)
+		return nil, nil, err
 	}
 
 	utxoEntry, err = deserializeUTXOEntry(utxoEntryBytes)
 	if err != nil {
-		panic(err)
+		return nil, nil, err
 	}
 
-	return outpoint, utxoEntry
+	return outpoint, utxoEntry, nil
 }
 
 func (c consensusStateStore) StageVirtualUTXOSet(virtualUTXOSetIterator model.ReadOnlyUTXOSetIterator) error {
@@ -211,7 +211,11 @@ func (c consensusStateStore) StageVirtualUTXOSet(virtualUTXOSetIterator model.Re
 
 	c.stagedVirtualUTXOSet = make(model.UTXOCollection)
 	for virtualUTXOSetIterator.Next() {
-		outpoint, entry := virtualUTXOSetIterator.Get()
+		outpoint, entry, err := virtualUTXOSetIterator.Get()
+		if err != nil {
+			return err
+		}
+
 		if _, exists := c.stagedVirtualUTXOSet[*outpoint]; exists {
 			return errors.Errorf("outpoint %s is found more than once in the given iterator", outpoint)
 		}