From cb9d7e313d5744f934cde103667a0a926f303835 Mon Sep 17 00:00:00 2001
From: Ori Newman <orinewman1@gmail.com>
Date: Tue, 22 Dec 2020 17:38:54 +0200
Subject: [PATCH] Implement Clone and Equal for all model types (#1155)

* [NOD-1575] Implement Clone and Equal for all model types

* [NOD-1575] Add assertion for transaction ID equality

* [NOD-1575] Use DomainTransaction.Equal to compare to expected coinbase transaction

* [NOD-1575] Add TestDomainBlockHeader_Clone

* [NOD-1575] Don't clone nil values

* [NOD-1575] Add type assertions

* [NOD-1575] Don't clone nil values

* [NOD-1575] Add missing Equals

* [NOD-1575] Add length checks

* [NOD-1575] Update comment

* [NOD-1575] Check length for TransactionAcceptanceData

* [NOD-1575] Explicitly clone nils where needed

* [NOD-1575] Clone tx id

* [NOD-1575] Flip condition

* Nod 1576 make coverage tests for equal clone inside model externalapi (#1177)

* [NOD-1576] Make coverage tests for equal and clone inside model and externalapi

* Some formatting and naming fixes

* Made transactionToCompare type exported

* Added some tests and made some changes to the tests code

* No changes made

* Some formatting and naming changes made

* Made better test coverage for externalapi clone and equal functions

* Changed expected result for two cases

* Added equal and clone functions tests for ghostdag and utxodiff

* Added tests

* [NOD-1576] Implement reachabilitydata equal/clone unit tests

* [NOD-1576]  Full coverage of reachabilitydata equal/clone unit tests

* Made changes and handling panic to transaction_equal_clone_test.go and formating of utxodiff_equal_clone_test.go

* Added recoverForEqual2 for handling panic to transaction_equal_clone_test.go

* [NOD-1576]  Full coverage of transaction equal unit test

* [NOD-1576] Add expects panic

* [NOD-1576] Allow composites in go vet

* [NOD-1576] Code review fixes (#1223)

* [NOD-1576] Code review fixes

* [NOD-1576] Code review fixes part 2

* [NOD-1576] Fix wrong name

Co-authored-by: karim1king <karimkaspersky@yahoo.com>
Co-authored-by: Ori Newman <orinewman1@gmail.com>
Co-authored-by: Karim <karim1king@users.noreply.github.com>

* Fix merge errors

* Use Equal where possible

* Use Equal where possible

* Use Equal where possible

Co-authored-by: andrey-hash <74914043+andrey-hash@users.noreply.github.com>
Co-authored-by: karim1king <karimkaspersky@yahoo.com>
Co-authored-by: Karim <karim1king@users.noreply.github.com>
---
 app/appmessage/p2p_msgblock.go                |    2 +-
 app/appmessage/p2p_msgblock_test.go           |    2 +-
 app/appmessage/p2p_msgrequestheaders_test.go  |    2 +-
 app/appmessage/p2p_msgtx.go                   |    4 +-
 app/appmessage/p2p_msgtx_test.go              |   10 +-
 app/protocol/flowcontext/orphans.go           |    2 +-
 .../flows/blockrelay/handle_relay_invs.go     |    2 +-
 app/protocol/flows/blockrelay/ibd.go          |    2 +-
 .../flows/handshake/receiveversion.go         |    2 +-
 build_and_test.sh                             |    2 +-
 domain/consensus/consensus.go                 |    2 +-
 domain/consensus/finality_test.go             |   14 +-
 .../model/acceptancedata_equal_clone_test.go  |  809 ++++++++++++
 domain/consensus/model/blockrelations.go      |   25 +-
 .../model/blockrelations_equal_clone_test.go  |  115 ++
 .../model/externalapi/acceptancedata.go       |   78 +-
 domain/consensus/model/externalapi/block.go   |   81 +-
 .../externalapi/block_equal_clone_test.go     |  375 ++++++
 .../consensus/model/externalapi/blockinfo.go  |    9 +
 .../model/externalapi/blockinfo_clone_test.go |   45 +
 .../model/externalapi/blocklocator.go         |    5 +
 .../externalapi/blocklocator_clone_test.go    |   67 +
 .../model/externalapi/blockstatus.go          |    9 +
 .../blockstatus_equal_clone_test.go           |   87 ++
 .../consensus/model/externalapi/coinbase.go   |   14 +
 .../model/externalapi/coinbase_clone_test.go  |   59 +
 .../consensus/model/externalapi/equal_test.go |  238 ++++
 domain/consensus/model/externalapi/hash.go    |   31 +-
 .../externalapi/hash_clone_equal_test.go      |  119 ++
 .../model/externalapi/subnetworkid.go         |   17 +-
 .../subnetworkid_clone_equal_test.go          |   99 ++
 domain/consensus/model/externalapi/sync.go    |   39 +
 .../externalapi/sync_equal_clone_test.go      |  126 ++
 .../model/externalapi/transaction.go          |  175 ++-
 .../transaction_equal_clone_test.go           | 1133 +++++++++++++++++
 .../consensus/model/externalapi/utxoentry.go  |    1 +
 domain/consensus/model/reachabilitydata.go    |  100 +-
 .../reachabilitydata_equal_clone_test.go      |  296 +++++
 .../blockprocessor/validateandinsertblock.go  |    4 +-
 .../validateandinsertpruningpoint.go          |    2 +-
 .../blockvalidator/block_body_in_isolation.go |    2 +-
 .../blockvalidator/block_header_in_context.go |    2 +-
 .../block_header_in_isolation.go              |    2 +-
 .../add_block_to_virtual.go                   |    8 +-
 .../calculate_past_utxo.go                    |    2 +-
 .../calculate_past_utxo_test.go               |    2 +-
 .../check_finality_violation.go               |    2 +-
 .../find_selected_parent_chain_changes.go     |    2 +-
 ...find_selected_parent_chain_changes_test.go |   10 +-
 .../pick_virtual_parents.go                   |    2 +-
 .../resolve_block_status.go                   |    6 +-
 .../resolve_block_status_test.go              |    2 +-
 .../update_pruning_utxo_set.go                |    2 +-
 .../consensusstatemanager/update_virtual.go   |    4 +-
 .../consensusstatemanager/utxo_diffs.go       |    6 +-
 .../verify_and_build_utxo.go                  |    6 +-
 .../dagtopologymanager/dagtopologymanager.go  |    8 +-
 .../selected_child_iterator.go                |    2 +-
 .../finalitymanager/finality_manager.go       |    4 +-
 .../processes/ghostdag2/ghostdagimpl.go       |    6 +-
 .../ghostdagmanager/ghostdag_test.go          |    4 +-
 .../processes/ghostdagmanager/mergeset.go     |    2 +-
 .../headertipsmanager.go                      |    2 +-
 .../pruningmanager/pruningmanager.go          |    2 +-
 .../reachability_external_test.go             |   10 +-
 .../reachabilitymanager/reachability_test.go  |    4 +-
 .../processes/reachabilitymanager/tree.go     |   16 +-
 .../processes/syncmanager/syncinfo.go         |    2 +-
 .../transaction_in_isolation.go               |    4 +-
 domain/consensus/utils/hashes/to_big.go       |    2 +-
 domain/consensus/utils/subnetworks/compare.go |   11 -
 domain/consensus/utils/utxo/utxo_entry.go     |   38 +-
 .../consensus/utils/utxo/utxo_entry_test.go   |  113 ++
 domain/dagconfig/genesis_test.go              |    8 +-
 domain/dagconfig/params_test.go               |    2 +-
 domain/miningmanager/mempool/mempool.go       |    4 +-
 domain/utxoindex/store.go                     |    4 +-
 domain/utxoindex/utxoindex.go                 |    4 +-
 testing/integration/basic_sync_test.go        |    4 +-
 79 files changed, 4346 insertions(+), 174 deletions(-)
 create mode 100644 domain/consensus/model/acceptancedata_equal_clone_test.go
 create mode 100644 domain/consensus/model/blockrelations_equal_clone_test.go
 create mode 100644 domain/consensus/model/externalapi/block_equal_clone_test.go
 create mode 100644 domain/consensus/model/externalapi/blockinfo_clone_test.go
 create mode 100644 domain/consensus/model/externalapi/blocklocator_clone_test.go
 create mode 100644 domain/consensus/model/externalapi/blockstatus_equal_clone_test.go
 create mode 100644 domain/consensus/model/externalapi/coinbase_clone_test.go
 create mode 100644 domain/consensus/model/externalapi/equal_test.go
 create mode 100644 domain/consensus/model/externalapi/hash_clone_equal_test.go
 create mode 100644 domain/consensus/model/externalapi/subnetworkid_clone_equal_test.go
 create mode 100644 domain/consensus/model/externalapi/sync_equal_clone_test.go
 create mode 100644 domain/consensus/model/externalapi/transaction_equal_clone_test.go
 create mode 100644 domain/consensus/model/reachabilitydata_equal_clone_test.go
 create mode 100644 domain/consensus/utils/utxo/utxo_entry_test.go

diff --git a/app/appmessage/p2p_msgblock.go b/app/appmessage/p2p_msgblock.go
index 5d8d3de53..4436d40e8 100644
--- a/app/appmessage/p2p_msgblock.go
+++ b/app/appmessage/p2p_msgblock.go
@@ -71,7 +71,7 @@ func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 {
 // Note: this operation modifies the block in place.
 func (msg *MsgBlock) ConvertToPartial(subnetworkID *externalapi.DomainSubnetworkID) {
 	for _, tx := range msg.Transactions {
-		if tx.SubnetworkID != *subnetworkID {
+		if !tx.SubnetworkID.Equal(subnetworkID) {
 			tx.Payload = []byte{}
 		}
 	}
diff --git a/app/appmessage/p2p_msgblock_test.go b/app/appmessage/p2p_msgblock_test.go
index 39d02fca9..347b10556 100644
--- a/app/appmessage/p2p_msgblock_test.go
+++ b/app/appmessage/p2p_msgblock_test.go
@@ -110,7 +110,7 @@ func TestConvertToPartial(t *testing.T) {
 	for _, testTransaction := range transactions {
 		var subnetworkTx *MsgTx
 		for _, blockTransaction := range block.Transactions {
-			if blockTransaction.SubnetworkID == *testTransaction.subnetworkID {
+			if blockTransaction.SubnetworkID.Equal(testTransaction.subnetworkID) {
 				subnetworkTx = blockTransaction
 			}
 		}
diff --git a/app/appmessage/p2p_msgrequestheaders_test.go b/app/appmessage/p2p_msgrequestheaders_test.go
index 1d475b9d2..9d0a60cd3 100644
--- a/app/appmessage/p2p_msgrequestheaders_test.go
+++ b/app/appmessage/p2p_msgrequestheaders_test.go
@@ -26,7 +26,7 @@ func TestRequstIBDBlocks(t *testing.T) {
 
 	// Ensure we get the same data back out.
 	msg := NewMsgRequstHeaders(lowHash, highHash)
-	if *msg.HighHash != *highHash {
+	if !msg.HighHash.Equal(highHash) {
 		t.Errorf("NewMsgRequstHeaders: wrong high hash - got %v, want %v",
 			msg.HighHash, highHash)
 	}
diff --git a/app/appmessage/p2p_msgtx.go b/app/appmessage/p2p_msgtx.go
index 94661b7cf..1e65d91ea 100644
--- a/app/appmessage/p2p_msgtx.go
+++ b/app/appmessage/p2p_msgtx.go
@@ -256,8 +256,8 @@ func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
 // 3. The transaction's subnetwork
 func (msg *MsgTx) IsSubnetworkCompatible(subnetworkID *externalapi.DomainSubnetworkID) bool {
 	return subnetworkID == nil ||
-		*subnetworkID == subnetworks.SubnetworkIDNative ||
-		*subnetworkID == msg.SubnetworkID
+		subnetworkID.Equal(&subnetworks.SubnetworkIDNative) ||
+		subnetworkID.Equal(&msg.SubnetworkID)
 }
 
 // newMsgTx returns a new tx message that conforms to the Message interface.
diff --git a/app/appmessage/p2p_msgtx_test.go b/app/appmessage/p2p_msgtx_test.go
index e85a50601..c1dba1885 100644
--- a/app/appmessage/p2p_msgtx_test.go
+++ b/app/appmessage/p2p_msgtx_test.go
@@ -53,7 +53,7 @@ func TestTx(t *testing.T) {
 	// testing package functionality.
 	prevOutIndex := uint32(1)
 	prevOut := NewOutpoint(txID, prevOutIndex)
-	if prevOut.TxID != *txID {
+	if !prevOut.TxID.Equal(txID) {
 		t.Errorf("NewOutpoint: wrong ID - got %v, want %v",
 			spew.Sprint(&prevOut.TxID), spew.Sprint(txID))
 	}
@@ -179,7 +179,7 @@ func TestTxHashAndID(t *testing.T) {
 
 	// Ensure the TxID for coinbase transaction is the same as TxHash.
 	tx1ID := tx1.TxID()
-	if *tx1ID != *wantTxID1 {
+	if !tx1ID.Equal(wantTxID1) {
 		t.Errorf("TxID: wrong ID - got %v, want %v",
 			spew.Sprint(tx1ID), spew.Sprint(wantTxID1))
 	}
@@ -236,19 +236,19 @@ func TestTxHashAndID(t *testing.T) {
 
 	// Ensure the hash produced is expected.
 	tx2Hash := tx2.TxHash()
-	if *tx2Hash != *wantHash2 {
+	if !tx2Hash.Equal(wantHash2) {
 		t.Errorf("TxHash: wrong hash - got %v, want %v",
 			spew.Sprint(tx2Hash), spew.Sprint(wantHash2))
 	}
 
 	// Ensure the TxID for coinbase transaction is the same as TxHash.
 	tx2ID := tx2.TxID()
-	if *tx2ID != *wantID2 {
+	if !tx2ID.Equal(wantID2) {
 		t.Errorf("TxID: wrong ID - got %v, want %v",
 			spew.Sprint(tx2ID), spew.Sprint(wantID2))
 	}
 
-	if *tx2ID == (externalapi.DomainTransactionID)(*tx2Hash) {
+	if tx2ID.Equal((*externalapi.DomainTransactionID)(tx2Hash)) {
 		t.Errorf("tx2ID and tx2Hash shouldn't be the same for non-coinbase transaction with signature and/or payload")
 	}
 
diff --git a/app/protocol/flowcontext/orphans.go b/app/protocol/flowcontext/orphans.go
index c57afd95a..853462255 100644
--- a/app/protocol/flowcontext/orphans.go
+++ b/app/protocol/flowcontext/orphans.go
@@ -130,7 +130,7 @@ func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash)
 	var childOrphans []externalapi.DomainHash
 	for orphanHash, orphanBlock := range f.orphans {
 		for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
-			if *orphanBlockParentHash == *blockHash {
+			if orphanBlockParentHash.Equal(blockHash) {
 				childOrphans = append(childOrphans, orphanHash)
 				break
 			}
diff --git a/app/protocol/flows/blockrelay/handle_relay_invs.go b/app/protocol/flows/blockrelay/handle_relay_invs.go
index 26e335580..8b91c5788 100644
--- a/app/protocol/flows/blockrelay/handle_relay_invs.go
+++ b/app/protocol/flows/blockrelay/handle_relay_invs.go
@@ -171,7 +171,7 @@ func (flow *handleRelayInvsFlow) requestBlock(requestHash *externalapi.DomainHas
 
 	block := appmessage.MsgBlockToDomainBlock(msgBlock)
 	blockHash := consensushashing.BlockHash(block)
-	if *blockHash != *requestHash {
+	if !blockHash.Equal(requestHash) {
 		return nil, false, protocolerrors.Errorf(true, "got unrequested block %s", blockHash)
 	}
 
diff --git a/app/protocol/flows/blockrelay/ibd.go b/app/protocol/flows/blockrelay/ibd.go
index 41745bbab..a43c2e379 100644
--- a/app/protocol/flows/blockrelay/ibd.go
+++ b/app/protocol/flows/blockrelay/ibd.go
@@ -279,7 +279,7 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
 
 			block := appmessage.MsgBlockToDomainBlock(msgIBDBlock.MsgBlock)
 			blockHash := consensushashing.BlockHash(block)
-			if *expectedHash != *blockHash {
+			if !expectedHash.Equal(blockHash) {
 				return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
 			}
 
diff --git a/app/protocol/flows/handshake/receiveversion.go b/app/protocol/flows/handshake/receiveversion.go
index b0191b9b7..f3357dab1 100644
--- a/app/protocol/flows/handshake/receiveversion.go
+++ b/app/protocol/flows/handshake/receiveversion.go
@@ -90,7 +90,7 @@ func (flow *receiveVersionFlow) start() (*appmessage.NetAddress, error) {
 	isRemoteNodeFull := msgVersion.SubnetworkID == nil
 	isOutbound := flow.peer.Connection().IsOutbound()
 	if (isLocalNodeFull && !isRemoteNodeFull && isOutbound) ||
-		(!isLocalNodeFull && !isRemoteNodeFull && *msgVersion.SubnetworkID != *localSubnetworkID) {
+		(!isLocalNodeFull && !isRemoteNodeFull && !msgVersion.SubnetworkID.Equal(localSubnetworkID)) {
 
 		return nil, protocolerrors.New(false, "incompatible subnetworks")
 	}
diff --git a/build_and_test.sh b/build_and_test.sh
index 1f8fbc0a4..6fd9ac9ba 100755
--- a/build_and_test.sh
+++ b/build_and_test.sh
@@ -19,7 +19,7 @@ staticcheck -checks "\
     SA4022,SA4023,SA5000,SA5002,SA5004,SA5005,SA5007,SA5008,SA5009,SA5010,SA5011,SA5012,SA6001,SA6002,SA9001,SA9002, \
     SA9003,SA9004,SA9005,SA9006,ST1019" ./...
 
-go vet $FLAGS ./...
+go vet -composites=false $FLAGS ./...
 
 go build $FLAGS -o kaspad .
 
diff --git a/domain/consensus/consensus.go b/domain/consensus/consensus.go
index 6ad01c6a9..d4b01df9a 100644
--- a/domain/consensus/consensus.go
+++ b/domain/consensus/consensus.go
@@ -171,7 +171,7 @@ func (s *consensus) GetPruningPointUTXOSet(expectedPruningPointHash *externalapi
 		return nil, err
 	}
 
-	if *expectedPruningPointHash != *pruningPointHash {
+	if !expectedPruningPointHash.Equal(pruningPointHash) {
 		return nil, errors.Wrapf(ruleerrors.ErrWrongPruningPointHash, "expected pruning point %s but got %s",
 			expectedPruningPointHash,
 			pruningPointHash)
diff --git a/domain/consensus/finality_test.go b/domain/consensus/finality_test.go
index 0c5aa3189..4fc06b148 100644
--- a/domain/consensus/finality_test.go
+++ b/domain/consensus/finality_test.go
@@ -107,7 +107,7 @@ func TestFinality(t *testing.T) {
 		if err != nil {
 			t.Fatalf("TestFinality: Failed getting virtual selectedParent: %v", err)
 		}
-		if *consensushashing.BlockHash(selectedTip) != *sideChainTipHash {
+		if !consensushashing.BlockHash(selectedTip).Equal(sideChainTipHash) {
 			t.Fatalf("Overtaking block in side-chain is not selectedTip")
 		}
 
@@ -125,7 +125,7 @@ func TestFinality(t *testing.T) {
 			t.Fatalf("TestFinality: Failed getting the virtual's finality point: %v", err)
 		}
 
-		if *virtualFinality == *params.GenesisHash {
+		if virtualFinality.Equal(params.GenesisHash) {
 			t.Fatalf("virtual's finalityPoint is still genesis after adding finalityInterval + 1 blocks to the main chain")
 		}
 
@@ -309,7 +309,7 @@ func TestBoundedMergeDepth(t *testing.T) {
 		// Make sure it's actually blue
 		found := false
 		for _, blue := range virtualGhotDagData.MergeSetBlues() {
-			if *blue == *kosherizingBlockHash {
+			if blue.Equal(kosherizingBlockHash) {
 				found = true
 				break
 			}
@@ -329,7 +329,7 @@ func TestBoundedMergeDepth(t *testing.T) {
 			t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
 		}
 
-		if *consensushashing.BlockHash(virtualSelectedParent) != *consensushashing.BlockHash(pointAtBlueKosherizing) {
+		if !consensushashing.BlockHash(virtualSelectedParent).Equal(consensushashing.BlockHash(pointAtBlueKosherizing)) {
 			t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", consensushashing.BlockHash(pointAtBlueKosherizing), consensushashing.BlockHash(virtualSelectedParent))
 		}
 
@@ -346,7 +346,7 @@ func TestBoundedMergeDepth(t *testing.T) {
 			t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
 		}
 
-		if *consensushashing.BlockHash(virtualSelectedParent) != *tip {
+		if !consensushashing.BlockHash(virtualSelectedParent).Equal(tip) {
 			t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", tip, consensushashing.BlockHash(virtualSelectedParent))
 		}
 
@@ -357,7 +357,7 @@ func TestBoundedMergeDepth(t *testing.T) {
 		// Make sure it's actually blue
 		found = false
 		for _, blue := range virtualGhotDagData.MergeSetBlues() {
-			if *blue == *kosherizingBlockHash {
+			if blue.Equal(kosherizingBlockHash) {
 				found = true
 				break
 			}
@@ -382,7 +382,7 @@ func TestBoundedMergeDepth(t *testing.T) {
 			t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
 		}
 
-		if *consensushashing.BlockHash(virtualSelectedParent) != *consensushashing.BlockHash(transitiveBlueKosherizing) {
+		if !consensushashing.BlockHash(virtualSelectedParent).Equal(consensushashing.BlockHash(transitiveBlueKosherizing)) {
 			t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", consensushashing.BlockHash(transitiveBlueKosherizing), consensushashing.BlockHash(virtualSelectedParent))
 		}
 
diff --git a/domain/consensus/model/acceptancedata_equal_clone_test.go b/domain/consensus/model/acceptancedata_equal_clone_test.go
new file mode 100644
index 000000000..73bc29c9a
--- /dev/null
+++ b/domain/consensus/model/acceptancedata_equal_clone_test.go
@@ -0,0 +1,809 @@
+package model_test
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
+	"reflect"
+	"testing"
+)
+
+func initTestTransactionAcceptanceDataForClone() []*externalapi.TransactionAcceptanceData {
+
+	tests := []*externalapi.TransactionAcceptanceData{
+		{
+			&externalapi.DomainTransaction{
+				Version: 1,
+				Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}},
+					{uint64(0xFFFF),
+						[]byte{1, 3}}},
+				LockTime:     1,
+				SubnetworkID: externalapi.DomainSubnetworkID{0x01},
+				Gas:          1,
+				PayloadHash: externalapi.DomainHash{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, 0x00},
+				Payload: []byte{0x01},
+				Fee:     0,
+				Mass:    1,
+				ID: &externalapi.DomainTransactionID{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, 0x02},
+			},
+			1,
+			true,
+		},
+	}
+	return tests
+}
+
+type testTransactionAcceptanceDataToCompare struct {
+	transactionAcceptanceData *externalapi.TransactionAcceptanceData
+	expectedResult            bool
+}
+
+type testTransactionAcceptanceDataStruct struct {
+	baseTransactionAcceptanceData        *externalapi.TransactionAcceptanceData
+	transactionAcceptanceDataToCompareTo []testTransactionAcceptanceDataToCompare
+}
+
+func initTransactionAcceptanceDataForEqual() []testTransactionAcceptanceDataStruct {
+	var testTransactionAcceptanceDataBase = externalapi.TransactionAcceptanceData{
+
+		&externalapi.DomainTransaction{
+			Version: 1,
+			Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}},
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			LockTime:     1,
+			SubnetworkID: externalapi.DomainSubnetworkID{0x01},
+			Gas:          1,
+			PayloadHash: externalapi.DomainHash{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, 0x00},
+			Payload: []byte{0x01},
+			Fee:     0,
+			Mass:    1,
+			ID: &externalapi.DomainTransactionID{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, 0x02},
+		},
+		1,
+		true,
+	}
+
+	var testTransactionAcceptanceData1 = externalapi.TransactionAcceptanceData{
+		&externalapi.DomainTransaction{
+			Version: 1,
+			Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}},
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			LockTime:     1,
+			SubnetworkID: externalapi.DomainSubnetworkID{0x01},
+			Gas:          1,
+			PayloadHash: externalapi.DomainHash{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, 0x00},
+			Payload: []byte{0x01},
+			Fee:     0,
+			Mass:    1,
+			ID: &externalapi.DomainTransactionID{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, 0x02},
+		},
+		1,
+		true,
+	}
+	// test 2: different transactions
+	var testTransactionAcceptanceData2 = externalapi.TransactionAcceptanceData{
+		&externalapi.DomainTransaction{
+			Version: 2,
+			Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}},
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			LockTime:     1,
+			SubnetworkID: externalapi.DomainSubnetworkID{0x01},
+			Gas:          1,
+			PayloadHash: externalapi.DomainHash{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, 0x00},
+			Payload: []byte{0x01},
+			Fee:     0,
+			Mass:    1,
+			ID: &externalapi.DomainTransactionID{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, 0x02},
+		},
+		1,
+		true,
+	}
+	//test 3: different Fee
+	var testTransactionAcceptanceData3 = externalapi.TransactionAcceptanceData{
+		&externalapi.DomainTransaction{
+			Version: 1,
+			Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}},
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			LockTime:     1,
+			SubnetworkID: externalapi.DomainSubnetworkID{0x01},
+			Gas:          1,
+			PayloadHash: externalapi.DomainHash{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, 0x00},
+			Payload: []byte{0x01},
+			Fee:     0,
+			Mass:    1,
+			ID: &externalapi.DomainTransactionID{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, 0x02},
+		},
+		2,
+		true,
+	}
+	//test 4: different isAccepted
+	var testTransactionAcceptanceData4 = externalapi.TransactionAcceptanceData{
+		&externalapi.DomainTransaction{
+			Version: 1,
+			Inputs: []*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}},
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			LockTime:     1,
+			SubnetworkID: externalapi.DomainSubnetworkID{0x01},
+			Gas:          1,
+			PayloadHash: externalapi.DomainHash{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, 0x00},
+			Payload: []byte{0x01},
+			Fee:     0,
+			Mass:    1,
+			ID: &externalapi.DomainTransactionID{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, 0x02},
+		},
+		1,
+		false,
+	}
+
+	tests := []testTransactionAcceptanceDataStruct{
+		{
+			baseTransactionAcceptanceData: &testTransactionAcceptanceDataBase,
+			transactionAcceptanceDataToCompareTo: []testTransactionAcceptanceDataToCompare{
+				{
+					transactionAcceptanceData: &testTransactionAcceptanceData1,
+					expectedResult:            true,
+				}, {
+					transactionAcceptanceData: &testTransactionAcceptanceData2,
+					expectedResult:            false,
+				}, {
+					transactionAcceptanceData: &testTransactionAcceptanceData3,
+					expectedResult:            false,
+				}, {
+					transactionAcceptanceData: &testTransactionAcceptanceData4,
+					expectedResult:            false,
+				}, {
+					transactionAcceptanceData: nil,
+					expectedResult:            false,
+				},
+			},
+		}, {
+			baseTransactionAcceptanceData: nil,
+			transactionAcceptanceDataToCompareTo: []testTransactionAcceptanceDataToCompare{
+				{
+					transactionAcceptanceData: &testTransactionAcceptanceData1,
+					expectedResult:            false,
+				}, {
+					transactionAcceptanceData: nil,
+					expectedResult:            true,
+				},
+			},
+		},
+	}
+	return tests
+}
+
+func TestTransactionAcceptanceData_Equal(t *testing.T) {
+	acceptanceData := initTransactionAcceptanceDataForEqual()
+	for i, test := range acceptanceData {
+		for j, subTest := range test.transactionAcceptanceDataToCompareTo {
+			result1 := test.baseTransactionAcceptanceData.Equal(subTest.transactionAcceptanceData)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.transactionAcceptanceData.Equal(test.baseTransactionAcceptanceData)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestTransactionAcceptanceData_Clone(t *testing.T) {
+
+	testTransactionAcceptanceData := initTestTransactionAcceptanceDataForClone()
+	for i, transactionAcceptanceData := range testTransactionAcceptanceData {
+		transactionAcceptanceDataClone := transactionAcceptanceData.Clone()
+		if !transactionAcceptanceDataClone.Equal(transactionAcceptanceData) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(transactionAcceptanceData, transactionAcceptanceDataClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
+
+func initTestBlockAcceptanceDataForClone() []*externalapi.BlockAcceptanceData {
+
+	tests := []*externalapi.BlockAcceptanceData{{&externalapi.DomainHash{1},
+		[]*externalapi.TransactionAcceptanceData{
+			{
+				&externalapi.DomainTransaction{
+					1,
+					[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+						externalapi.DomainTransactionID{0x01}, 0xFFFF},
+						[]byte{1, 2, 3},
+						uint64(0xFFFFFFFF),
+						utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+					[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+						[]byte{1, 2}},
+						{uint64(0xFFFF),
+							[]byte{1, 3}}},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					1,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					&externalapi.DomainTransactionID{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, 0x02},
+				},
+				1,
+				true,
+			}},
+	},
+	}
+	return tests
+}
+
+type testBlockAcceptanceDataToCompare struct {
+	blockAcceptanceData *externalapi.BlockAcceptanceData
+	expectedResult      bool
+}
+
+type testBlockAcceptanceDataStruct struct {
+	baseBlockAcceptanceData        *externalapi.BlockAcceptanceData
+	blockAcceptanceDataToCompareTo []testBlockAcceptanceDataToCompare
+}
+
+func iniBlockAcceptanceDataForEqual() []testBlockAcceptanceDataStruct {
+	var testBlockAcceptanceDataBase = externalapi.BlockAcceptanceData{
+		&externalapi.DomainHash{1},
+		[]*externalapi.TransactionAcceptanceData{{
+			&externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{
+					externalapi.DomainOutpoint{
+						externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}},
+					{uint64(0xFFFF),
+						[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			1,
+			true,
+		}}}
+	//test 1: structs are equal
+	var testBlockAcceptanceData1 = externalapi.BlockAcceptanceData{
+		&externalapi.DomainHash{1},
+		[]*externalapi.TransactionAcceptanceData{{
+			&externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{
+					externalapi.DomainOutpoint{
+						externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}},
+					{uint64(0xFFFF),
+						[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			1,
+			true,
+		}}}
+	// test 2: different size
+	var testBlockAcceptanceData2 = externalapi.BlockAcceptanceData{
+		&externalapi.DomainHash{1},
+		[]*externalapi.TransactionAcceptanceData{{
+			&externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{
+					externalapi.DomainOutpoint{
+						externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}},
+					{uint64(0xFFFF),
+						[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			1,
+			true,
+		}, {}}}
+	//test 3: different transactions, same size
+	var testBlockAcceptanceData3 = externalapi.BlockAcceptanceData{
+		&externalapi.DomainHash{1},
+		[]*externalapi.TransactionAcceptanceData{{
+			&externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{
+					externalapi.DomainOutpoint{
+						externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}},
+					{uint64(0xFFFF),
+						[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			1,
+			false,
+		}}}
+
+	// test 4 - different block hash
+	var testBlockAcceptanceData4 = externalapi.BlockAcceptanceData{
+		&externalapi.DomainHash{2},
+		[]*externalapi.TransactionAcceptanceData{{
+			&externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{
+					externalapi.DomainOutpoint{
+						externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}},
+					{uint64(0xFFFF),
+						[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			1,
+			true,
+		}}}
+
+	tests := []testBlockAcceptanceDataStruct{
+		{
+			baseBlockAcceptanceData: &testBlockAcceptanceDataBase,
+			blockAcceptanceDataToCompareTo: []testBlockAcceptanceDataToCompare{
+				{
+					blockAcceptanceData: &testBlockAcceptanceData1,
+					expectedResult:      true,
+				}, {
+					blockAcceptanceData: &testBlockAcceptanceData2,
+					expectedResult:      false,
+				}, {
+					blockAcceptanceData: &testBlockAcceptanceData3,
+					expectedResult:      false,
+				}, {
+					blockAcceptanceData: nil,
+					expectedResult:      false,
+				},
+				{
+					blockAcceptanceData: &testBlockAcceptanceData4,
+					expectedResult:      false,
+				},
+			},
+		}, {
+			baseBlockAcceptanceData: nil,
+			blockAcceptanceDataToCompareTo: []testBlockAcceptanceDataToCompare{
+				{
+					blockAcceptanceData: &testBlockAcceptanceData1,
+					expectedResult:      false,
+				}, {
+					blockAcceptanceData: nil,
+					expectedResult:      true,
+				},
+			},
+		},
+	}
+	return tests
+}
+
+func TestBlockAcceptanceData_Equal(t *testing.T) {
+
+	blockAcceptances := iniBlockAcceptanceDataForEqual()
+	for i, test := range blockAcceptances {
+		for j, subTest := range test.blockAcceptanceDataToCompareTo {
+			result1 := test.baseBlockAcceptanceData.Equal(subTest.blockAcceptanceData)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.blockAcceptanceData.Equal(test.baseBlockAcceptanceData)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestBlockAcceptanceData_Clone(t *testing.T) {
+
+	testBlockAcceptanceData := initTestBlockAcceptanceDataForClone()
+	for i, blockAcceptanceData := range testBlockAcceptanceData {
+		blockAcceptanceDataClone := blockAcceptanceData.Clone()
+		if !blockAcceptanceDataClone.Equal(blockAcceptanceData) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(blockAcceptanceData, blockAcceptanceDataClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
+
+func initTestAcceptanceDataForClone() []externalapi.AcceptanceData {
+
+	test1 := []*externalapi.BlockAcceptanceData{{
+		&externalapi.DomainHash{1},
+		[]*externalapi.TransactionAcceptanceData{
+			{
+				&externalapi.DomainTransaction{
+					1,
+					[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+						externalapi.DomainTransactionID{0x01}, 0xFFFF},
+						[]byte{1, 2, 3},
+						uint64(0xFFFFFFFF),
+						utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+					[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+						[]byte{1, 2}},
+						{uint64(0xFFFF),
+							[]byte{1, 3}}},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					1,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					&externalapi.DomainTransactionID{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, 0x02},
+				},
+				1,
+				true,
+			}},
+	},
+	}
+	tests := []externalapi.AcceptanceData{test1, test1}
+	return tests
+}
+
+type testAcceptanceDataToCompare struct {
+	acceptanceData externalapi.AcceptanceData
+	expectedResult bool
+}
+
+type testAcceptanceDataStruct struct {
+	baseAcceptanceData        externalapi.AcceptanceData
+	acceptanceDataToCompareTo []testAcceptanceDataToCompare
+}
+
+func initAcceptanceDataForEqual() []testAcceptanceDataStruct {
+	var testAcceptanceDataBase = []*externalapi.BlockAcceptanceData{
+		{
+			&externalapi.DomainHash{1},
+			[]*externalapi.TransactionAcceptanceData{{
+				&externalapi.DomainTransaction{
+					1,
+					[]*externalapi.DomainTransactionInput{{
+						externalapi.DomainOutpoint{
+							externalapi.DomainTransactionID{0x01}, 0xFFFF},
+						[]byte{1, 2, 3},
+						uint64(0xFFFFFFFF),
+						utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+					[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+						[]byte{1, 2}},
+						{uint64(0xFFFF),
+							[]byte{1, 3}}},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					1,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					&externalapi.DomainTransactionID{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, 0x02},
+				},
+				1,
+				true,
+			}}}}
+	//test 1: structs are equal
+	var testAcceptanceData1 = []*externalapi.BlockAcceptanceData{
+		{&externalapi.DomainHash{1},
+			[]*externalapi.TransactionAcceptanceData{{
+				&externalapi.DomainTransaction{
+					1,
+					[]*externalapi.DomainTransactionInput{{
+						externalapi.DomainOutpoint{
+							externalapi.DomainTransactionID{0x01}, 0xFFFF},
+						[]byte{1, 2, 3},
+						uint64(0xFFFFFFFF),
+						utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+					[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+						[]byte{1, 2}},
+						{uint64(0xFFFF),
+							[]byte{1, 3}}},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					1,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					&externalapi.DomainTransactionID{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, 0x02},
+				},
+				1,
+				true,
+			}}}}
+	// test 2: different size
+	var testAcceptanceData2 = []*externalapi.BlockAcceptanceData{
+		{&externalapi.DomainHash{1},
+			[]*externalapi.TransactionAcceptanceData{{
+				&externalapi.DomainTransaction{
+					1,
+					[]*externalapi.DomainTransactionInput{{
+						externalapi.DomainOutpoint{
+							externalapi.DomainTransactionID{0x01}, 0xFFFF},
+						[]byte{1, 2, 3},
+						uint64(0xFFFFFFFF),
+						utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+					[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+						[]byte{1, 2}},
+						{uint64(0xFFFF),
+							[]byte{1, 3}}},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					1,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					&externalapi.DomainTransactionID{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, 0x02},
+				},
+				1,
+				true,
+			}}}, {}}
+	//test 3: different transactions, same size
+	var testAcceptanceData3 = []*externalapi.BlockAcceptanceData{
+		{&externalapi.DomainHash{1},
+			[]*externalapi.TransactionAcceptanceData{{
+				&externalapi.DomainTransaction{
+					2,
+					[]*externalapi.DomainTransactionInput{{
+						externalapi.DomainOutpoint{
+							externalapi.DomainTransactionID{0x01}, 0xFFFF},
+						[]byte{1, 2, 3},
+						uint64(0xFFFFFFFF),
+						utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+					[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+						[]byte{1, 2}},
+						{uint64(0xFFFF),
+							[]byte{1, 3}}},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					1,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					&externalapi.DomainTransactionID{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, 0x02},
+				},
+				1,
+				true,
+			}}}}
+
+	tests := []testAcceptanceDataStruct{
+		{
+			baseAcceptanceData: testAcceptanceDataBase,
+			acceptanceDataToCompareTo: []testAcceptanceDataToCompare{
+				{
+					acceptanceData: testAcceptanceData1,
+					expectedResult: true,
+				}, {
+					acceptanceData: testAcceptanceData2,
+					expectedResult: false,
+				}, {
+					acceptanceData: testAcceptanceData3,
+					expectedResult: false,
+				},
+			},
+		},
+	}
+	return tests
+}
+
+func TestAcceptanceData_Equal(t *testing.T) {
+
+	acceptances := initAcceptanceDataForEqual()
+	for i, test := range acceptances {
+		for j, subTest := range test.acceptanceDataToCompareTo {
+			result1 := test.baseAcceptanceData.Equal(subTest.acceptanceData)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.acceptanceData.Equal(test.baseAcceptanceData)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestAcceptanceData_Clone(t *testing.T) {
+
+	testAcceptanceData := initTestAcceptanceDataForClone()
+	for i, acceptanceData := range testAcceptanceData {
+		acceptanceDataClone := acceptanceData.Clone()
+		if !acceptanceDataClone.Equal(acceptanceData) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(acceptanceData, acceptanceDataClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/blockrelations.go b/domain/consensus/model/blockrelations.go
index 2ed402d46..face57b60 100644
--- a/domain/consensus/model/blockrelations.go
+++ b/domain/consensus/model/blockrelations.go
@@ -10,12 +10,29 @@ type BlockRelations struct {
 
 // Clone returns a clone of BlockRelations
 func (br *BlockRelations) Clone() *BlockRelations {
-	if br == nil {
-		return nil
-	}
-
 	return &BlockRelations{
 		Parents:  externalapi.CloneHashes(br.Parents),
 		Children: externalapi.CloneHashes(br.Children),
 	}
 }
+
+// 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 _ = &BlockRelations{[]*externalapi.DomainHash{}, []*externalapi.DomainHash{}}
+
+// Equal returns whether br equals to other
+func (br *BlockRelations) Equal(other *BlockRelations) bool {
+	if br == nil || other == nil {
+		return br == other
+	}
+
+	if !externalapi.HashesEqual(br.Parents, other.Parents) {
+		return false
+	}
+
+	if !externalapi.HashesEqual(br.Children, other.Children) {
+		return false
+	}
+
+	return true
+}
diff --git a/domain/consensus/model/blockrelations_equal_clone_test.go b/domain/consensus/model/blockrelations_equal_clone_test.go
new file mode 100644
index 000000000..39c05e8e3
--- /dev/null
+++ b/domain/consensus/model/blockrelations_equal_clone_test.go
@@ -0,0 +1,115 @@
+package model
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"reflect"
+	"testing"
+)
+
+func initTestBlockRelationsForClone() []*BlockRelations {
+
+	tests := []*BlockRelations{
+		{
+			[]*externalapi.DomainHash{{1}, {2}},
+			[]*externalapi.DomainHash{{3}, {4}},
+		},
+	}
+	return tests
+}
+
+type testBlockRelationsToCompare struct {
+	blockRelations *BlockRelations
+	expectedResult bool
+}
+
+type testBlockRelationsStruct struct {
+	baseBlockRelations        *BlockRelations
+	blockRelationsToCompareTo []testBlockRelationsToCompare
+}
+
+func initTestBlockRelationsForEqual() []testBlockRelationsStruct {
+
+	var testBlockRelationsBase = BlockRelations{
+		[]*externalapi.DomainHash{{1}, {2}},
+		[]*externalapi.DomainHash{{3}, {4}},
+	}
+	//First test: structs are equal
+	var testBlockRelations1 = BlockRelations{
+		[]*externalapi.DomainHash{{1}, {2}},
+		[]*externalapi.DomainHash{{3}, {4}},
+	}
+	//Second test: children changed
+	var testBlockRelations2 = BlockRelations{
+		[]*externalapi.DomainHash{{1}, {2}},
+		[]*externalapi.DomainHash{{3}, {5}},
+	}
+	//Third test: parents changed
+	var testBlockRelations3 = BlockRelations{
+		[]*externalapi.DomainHash{{6}, {2}},
+		[]*externalapi.DomainHash{{3}, {4}},
+	}
+
+	tests := []testBlockRelationsStruct{
+		{
+			baseBlockRelations: &testBlockRelationsBase,
+			blockRelationsToCompareTo: []testBlockRelationsToCompare{
+				{
+					blockRelations: &testBlockRelations1,
+					expectedResult: true,
+				}, {
+					blockRelations: &testBlockRelations2,
+					expectedResult: false,
+				}, {
+					blockRelations: &testBlockRelations3,
+					expectedResult: false,
+				}, {
+					blockRelations: nil,
+					expectedResult: false,
+				},
+			},
+		}, {
+			baseBlockRelations: nil,
+			blockRelationsToCompareTo: []testBlockRelationsToCompare{
+				{
+					blockRelations: &testBlockRelations1,
+					expectedResult: false,
+				}, {
+					blockRelations: nil,
+					expectedResult: true,
+				},
+			},
+		},
+	}
+	return tests
+}
+
+func TestBlockRelationsData_Equal(t *testing.T) {
+
+	blockRelationss := initTestBlockRelationsForEqual()
+	for i, test := range blockRelationss {
+		for j, subTest := range test.blockRelationsToCompareTo {
+			result1 := test.baseBlockRelations.Equal(subTest.blockRelations)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.blockRelations.Equal(test.baseBlockRelations)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestBlockRelations_Clone(t *testing.T) {
+
+	testBlockRelations := initTestBlockRelationsForClone()
+	for i, blockRelations := range testBlockRelations {
+		blockRelationsClone := blockRelations.Clone()
+		if !blockRelationsClone.Equal(blockRelations) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(blockRelations, blockRelationsClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/acceptancedata.go b/domain/consensus/model/externalapi/acceptancedata.go
index b3bde56b0..1cb4ae965 100644
--- a/domain/consensus/model/externalapi/acceptancedata.go
+++ b/domain/consensus/model/externalapi/acceptancedata.go
@@ -4,11 +4,27 @@ package externalapi
 // It's ordered in the same way as the block merge set blues.
 type AcceptanceData []*BlockAcceptanceData
 
+// 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 _ AcceptanceData = []*BlockAcceptanceData{}
+
+// Equal returns whether ad equals to other
+func (ad AcceptanceData) Equal(other AcceptanceData) bool {
+	if len(ad) != len(other) {
+		return false
+	}
+
+	for i, blockAcceptanceData := range ad {
+		if !blockAcceptanceData.Equal(other[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // Clone clones the AcceptanceData
 func (ad AcceptanceData) Clone() AcceptanceData {
-	if ad == nil {
-		return nil
-	}
 	clone := make(AcceptanceData, len(ad))
 	for i, blockAcceptanceData := range ad {
 		clone[i] = blockAcceptanceData.Clone()
@@ -24,6 +40,33 @@ type BlockAcceptanceData struct {
 	TransactionAcceptanceData []*TransactionAcceptanceData
 }
 
+// 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 _ = &BlockAcceptanceData{&DomainHash{}, []*TransactionAcceptanceData{}}
+
+// Equal returns whether bad equals to other
+func (bad *BlockAcceptanceData) Equal(other *BlockAcceptanceData) bool {
+	if bad == nil || other == nil {
+		return bad == other
+	}
+
+	if !bad.BlockHash.Equal(other.BlockHash) {
+		return false
+	}
+
+	if len(bad.TransactionAcceptanceData) != len(other.TransactionAcceptanceData) {
+		return false
+	}
+
+	for i, acceptanceData := range bad.TransactionAcceptanceData {
+		if !acceptanceData.Equal(other.TransactionAcceptanceData[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // Clone returns a clone of BlockAcceptanceData
 func (bad *BlockAcceptanceData) Clone() *BlockAcceptanceData {
 	if bad == nil {
@@ -49,12 +92,33 @@ type TransactionAcceptanceData struct {
 	IsAccepted  bool
 }
 
-// Clone returns a clone of TransactionAcceptanceData
-func (tad *TransactionAcceptanceData) Clone() *TransactionAcceptanceData {
-	if tad == nil {
-		return nil
+// 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 _ = &TransactionAcceptanceData{&DomainTransaction{}, 0, false}
+
+// Equal returns whether tad equals to other
+func (tad *TransactionAcceptanceData) Equal(other *TransactionAcceptanceData) bool {
+	if tad == nil || other == nil {
+		return tad == other
 	}
 
+	if !tad.Transaction.Equal(other.Transaction) {
+		return false
+	}
+
+	if tad.Fee != other.Fee {
+		return false
+	}
+
+	if tad.IsAccepted != other.IsAccepted {
+		return false
+	}
+
+	return true
+}
+
+// Clone returns a clone of TransactionAcceptanceData
+func (tad *TransactionAcceptanceData) Clone() *TransactionAcceptanceData {
 	return &TransactionAcceptanceData{
 		Transaction: tad.Transaction.Clone(),
 		Fee:         tad.Fee,
diff --git a/domain/consensus/model/externalapi/block.go b/domain/consensus/model/externalapi/block.go
index da4990620..07d0d5cbb 100644
--- a/domain/consensus/model/externalapi/block.go
+++ b/domain/consensus/model/externalapi/block.go
@@ -8,10 +8,6 @@ type DomainBlock struct {
 
 // Clone returns a clone of DomainBlock
 func (block *DomainBlock) Clone() *DomainBlock {
-	if block == nil {
-		return nil
-	}
-
 	transactionClone := make([]*DomainTransaction, len(block.Transactions))
 	for i, tx := range block.Transactions {
 		transactionClone[i] = tx.Clone()
@@ -23,6 +19,33 @@ func (block *DomainBlock) Clone() *DomainBlock {
 	}
 }
 
+// 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{}}
+
+// Equal returns whether block equals to other
+func (block *DomainBlock) Equal(other *DomainBlock) bool {
+	if block == nil || other == nil {
+		return block == other
+	}
+
+	if len(block.Transactions) != len(other.Transactions) {
+		return false
+	}
+
+	if !block.Header.Equal(other.Header) {
+		return false
+	}
+
+	for i, tx := range block.Transactions {
+		if !tx.Equal(other.Transactions[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // DomainBlockHeader represents the header part of a Kaspa block
 type DomainBlockHeader struct {
 	Version              int32
@@ -37,10 +60,6 @@ type DomainBlockHeader struct {
 
 // Clone returns a clone of DomainBlockHeader
 func (header *DomainBlockHeader) Clone() *DomainBlockHeader {
-	if header == nil {
-		return nil
-	}
-
 	return &DomainBlockHeader{
 		Version:              header.Version,
 		ParentHashes:         CloneHashes(header.ParentHashes),
@@ -52,3 +71,49 @@ func (header *DomainBlockHeader) Clone() *DomainBlockHeader {
 		Nonce:                header.Nonce,
 	}
 }
+
+// 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
+}
diff --git a/domain/consensus/model/externalapi/block_equal_clone_test.go b/domain/consensus/model/externalapi/block_equal_clone_test.go
new file mode 100644
index 000000000..9a032d847
--- /dev/null
+++ b/domain/consensus/model/externalapi/block_equal_clone_test.go
@@ -0,0 +1,375 @@
+package externalapi
+
+import (
+	"reflect"
+	"testing"
+)
+
+type blockToCompare struct {
+	block          *DomainBlock
+	expectedResult bool
+}
+
+type TestBlockStruct struct {
+	baseBlock         *DomainBlock
+	blocksToCompareTo []blockToCompare
+}
+
+func initTestBaseTransactions() []*DomainTransaction {
+
+	testTx := []*DomainTransaction{{
+		Version:      1,
+		Inputs:       []*DomainTransactionInput{},
+		Outputs:      []*DomainTransactionOutput{},
+		LockTime:     1,
+		SubnetworkID: DomainSubnetworkID{0x01},
+		Gas:          1,
+		PayloadHash: DomainHash{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, 0x00},
+		Payload: []byte{0x01},
+		Fee:     0,
+		Mass:    1,
+		ID: &DomainTransactionID{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, 0x02},
+	}}
+	return testTx
+}
+
+func initTestAnotherTransactions() []*DomainTransaction {
+
+	testTx := []*DomainTransaction{{
+		Version:      1,
+		Inputs:       []*DomainTransactionInput{},
+		Outputs:      []*DomainTransactionOutput{},
+		LockTime:     1,
+		SubnetworkID: DomainSubnetworkID{0x01},
+		Gas:          1,
+		PayloadHash: DomainHash{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},
+		Payload: []byte{0x01},
+		Fee:     0,
+		Mass:    1,
+		ID: &DomainTransactionID{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},
+	}}
+	return testTx
+}
+
+func initTestTwoTransactions() []*DomainTransaction {
+
+	testTx := []*DomainTransaction{{
+		Version:      1,
+		Inputs:       []*DomainTransactionInput{},
+		Outputs:      []*DomainTransactionOutput{},
+		LockTime:     1,
+		SubnetworkID: DomainSubnetworkID{0x01},
+		Gas:          1,
+		PayloadHash: DomainHash{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},
+		Payload: []byte{0x01},
+		Fee:     0,
+		Mass:    1,
+		ID: &DomainTransactionID{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{},
+		LockTime:     1,
+		SubnetworkID: DomainSubnetworkID{0x01},
+		Gas:          1,
+		PayloadHash: DomainHash{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},
+		Payload: []byte{0x01},
+		Fee:     0,
+		Mass:    1,
+		ID: &DomainTransactionID{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},
+	}}
+	return testTx
+}
+
+func initTestBlockStructsForClone() []*DomainBlock {
+
+	tests := []*DomainBlock{
+		{
+			&DomainBlockHeader{
+
+				0,
+				[]*DomainHash{{0}},
+				DomainHash{1},
+				DomainHash{2},
+				DomainHash{3},
+				4,
+				5,
+				6,
+			},
+			initTestBaseTransactions(),
+		}, {
+			&DomainBlockHeader{
+
+				0,
+				[]*DomainHash{},
+				DomainHash{1},
+				DomainHash{2},
+				DomainHash{3},
+				4,
+				5,
+				6,
+			},
+			initTestBaseTransactions(),
+		},
+	}
+
+	return tests
+}
+
+func initTestBlockStructsForEqual() *[]TestBlockStruct {
+	tests := []TestBlockStruct{
+		{
+			baseBlock: nil,
+			blocksToCompareTo: []blockToCompare{
+				{
+					block:          nil,
+					expectedResult: true,
+				},
+				{
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{0}},
+							DomainHash{1},
+							DomainHash{2},
+							DomainHash{3},
+							4,
+							5,
+							6,
+						},
+						initTestBaseTransactions()},
+					expectedResult: false,
+				},
+			},
+		}, {
+			baseBlock: &DomainBlock{
+				&DomainBlockHeader{
+					0,
+					[]*DomainHash{{1}},
+					DomainHash{2},
+					DomainHash{3},
+					DomainHash{4},
+					5,
+					6,
+					7,
+				},
+				initTestBaseTransactions(),
+			},
+			blocksToCompareTo: []blockToCompare{
+				{
+					block:          nil,
+					expectedResult: false,
+				},
+				{
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}},
+							DomainHash{2},
+							DomainHash{3},
+							DomainHash{4},
+							5,
+							6,
+							7,
+						},
+						initTestAnotherTransactions(),
+					},
+					expectedResult: false,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}},
+							DomainHash{2},
+							DomainHash{3},
+							DomainHash{4},
+							5,
+							6,
+							7,
+						},
+						initTestBaseTransactions(),
+					},
+					expectedResult: true,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}, {2}}, // Changed
+							DomainHash{2},
+							DomainHash{3},
+							DomainHash{4},
+							5,
+							6,
+							7,
+						},
+						initTestBaseTransactions(),
+					},
+					expectedResult: false,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{100}}, // Changed
+							DomainHash{2},
+							DomainHash{3},
+							DomainHash{4},
+							5,
+							6,
+							7,
+						},
+						initTestTwoTransactions(),
+					},
+					expectedResult: false,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}},
+							DomainHash{100}, // Changed
+							DomainHash{3},
+							DomainHash{4},
+							5,
+							6,
+							7,
+						},
+						initTestBaseTransactions(),
+					},
+					expectedResult: false,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}},
+							DomainHash{2},
+							DomainHash{100}, // Changed
+							DomainHash{4},
+							5,
+							6,
+							7,
+						},
+						initTestBaseTransactions(),
+					},
+					expectedResult: false,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}},
+							DomainHash{2},
+							DomainHash{3},
+							DomainHash{100}, // Changed
+							5,
+							6,
+							7,
+						},
+						initTestBaseTransactions(),
+					},
+					expectedResult: false,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}},
+							DomainHash{2},
+							DomainHash{3},
+							DomainHash{4},
+							100, // Changed
+							6,
+							7,
+						},
+						initTestBaseTransactions(),
+					},
+					expectedResult: false,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}},
+							DomainHash{2},
+							DomainHash{3},
+							DomainHash{4},
+							5,
+							100, // Changed
+							7,
+						},
+						initTestBaseTransactions(),
+					},
+					expectedResult: false,
+				}, {
+					block: &DomainBlock{
+						&DomainBlockHeader{
+							0,
+							[]*DomainHash{{1}},
+							DomainHash{2},
+							DomainHash{3},
+							DomainHash{4},
+							5,
+							6,
+							100, // Changed
+						},
+						initTestBaseTransactions(),
+					},
+					expectedResult: false,
+				},
+			},
+		},
+	}
+
+	return &tests
+}
+
+func TestDomainBlock_Equal(t *testing.T) {
+
+	blockTests := initTestBlockStructsForEqual()
+	for i, test := range *blockTests {
+		for j, subTest := range test.blocksToCompareTo {
+			result1 := test.baseBlock.Equal(subTest.block)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.block.Equal(test.baseBlock)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+
+}
+
+func TestDomainBlock_Clone(t *testing.T) {
+
+	blocks := initTestBlockStructsForClone()
+	for i, block := range blocks {
+		blockClone := block.Clone()
+		if !blockClone.Equal(block) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(block, blockClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/blockinfo.go b/domain/consensus/model/externalapi/blockinfo.go
index dcd7128de..72cd0d8f9 100644
--- a/domain/consensus/model/externalapi/blockinfo.go
+++ b/domain/consensus/model/externalapi/blockinfo.go
@@ -6,3 +6,12 @@ type BlockInfo struct {
 	BlockStatus BlockStatus
 	BlueScore   uint64
 }
+
+// Clone returns a clone of BlockInfo
+func (bi *BlockInfo) Clone() *BlockInfo {
+	return &BlockInfo{
+		Exists:      bi.Exists,
+		BlockStatus: bi.BlockStatus.Clone(),
+		BlueScore:   bi.BlueScore,
+	}
+}
diff --git a/domain/consensus/model/externalapi/blockinfo_clone_test.go b/domain/consensus/model/externalapi/blockinfo_clone_test.go
new file mode 100644
index 000000000..2ff7c2699
--- /dev/null
+++ b/domain/consensus/model/externalapi/blockinfo_clone_test.go
@@ -0,0 +1,45 @@
+package externalapi
+
+import (
+	"reflect"
+	"testing"
+)
+
+func initTestBlockInfoStructsForClone() []*BlockInfo {
+
+	tests := []*BlockInfo{
+		{
+			true,
+			BlockStatus(0x01),
+			0,
+		}, {
+			true,
+			BlockStatus(0x02),
+			0,
+		}, {
+			true,
+			1,
+			1,
+		}, {
+			true,
+			255,
+			2,
+		}, {
+			true,
+			0,
+			3,
+		},
+	}
+	return tests
+}
+
+func TestBlockInfo_Clone(t *testing.T) {
+
+	blockInfos := initTestBlockInfoStructsForClone()
+	for i, blockInfo := range blockInfos {
+		blockInfoClone := blockInfo.Clone()
+		if !reflect.DeepEqual(blockInfo, blockInfoClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/blocklocator.go b/domain/consensus/model/externalapi/blocklocator.go
index 92503d6a4..a730a4df5 100644
--- a/domain/consensus/model/externalapi/blocklocator.go
+++ b/domain/consensus/model/externalapi/blocklocator.go
@@ -15,3 +15,8 @@ package externalapi
 // The block locator for block 17 would be the hashes of blocks:
 //  [17 16 14 11 7 2 genesis]
 type BlockLocator []*DomainHash
+
+// Clone returns a clone of BlockLocator
+func (locator BlockLocator) Clone() BlockLocator {
+	return CloneHashes(locator)
+}
diff --git a/domain/consensus/model/externalapi/blocklocator_clone_test.go b/domain/consensus/model/externalapi/blocklocator_clone_test.go
new file mode 100644
index 000000000..149a51b2a
--- /dev/null
+++ b/domain/consensus/model/externalapi/blocklocator_clone_test.go
@@ -0,0 +1,67 @@
+package externalapi
+
+import (
+	"reflect"
+	"testing"
+)
+
+func initTestBlockLocatorForClone() []*BlockLocator {
+
+	tests := []*BlockLocator{{
+
+		{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},
+		{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, 0x02},
+		{0x01, 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, 0x03},
+		{0xFF, 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, 0x04},
+		{0xFF, 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, 0xFF},
+	}, {
+		{0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},
+		{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, 2},
+		{0x01, 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, 1},
+		{0xFF, 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, 1, 1},
+		{0xFF, 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, 2, 1},
+	},
+	}
+	return tests
+}
+
+func TestBlockLocator_Clone(t *testing.T) {
+
+	testBlockLocator := initTestBlockLocatorForClone()
+	for i, blockLocator := range testBlockLocator {
+		blockLocatorClone := blockLocator.Clone()
+		if !reflect.DeepEqual(blockLocator, &blockLocatorClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/blockstatus.go b/domain/consensus/model/externalapi/blockstatus.go
index 2c2fd408a..7358bafdd 100644
--- a/domain/consensus/model/externalapi/blockstatus.go
+++ b/domain/consensus/model/externalapi/blockstatus.go
@@ -8,6 +8,15 @@ func (bs BlockStatus) Clone() BlockStatus {
 	return bs
 }
 
+// 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 _ BlockStatus = 0
+
+// Equal returns whether bs equals to other
+func (bs BlockStatus) Equal(other BlockStatus) bool {
+	return bs == other
+}
+
 const (
 	// StatusInvalid indicates that the block is invalid.
 	StatusInvalid BlockStatus = iota
diff --git a/domain/consensus/model/externalapi/blockstatus_equal_clone_test.go b/domain/consensus/model/externalapi/blockstatus_equal_clone_test.go
new file mode 100644
index 000000000..7737296c1
--- /dev/null
+++ b/domain/consensus/model/externalapi/blockstatus_equal_clone_test.go
@@ -0,0 +1,87 @@
+package externalapi
+
+import (
+	"reflect"
+	"testing"
+)
+
+func initTestBlockStatusForClone() []BlockStatus {
+
+	tests := []BlockStatus{1, 2, 0xFF, 0}
+
+	return tests
+}
+
+type TestBlockStatusToCompare struct {
+	blockStatus    BlockStatus
+	expectedResult bool
+}
+
+type TestBlockStatusStruct struct {
+	baseBlockStatus          BlockStatus
+	blockStatusesToCompareTo []TestBlockStatusToCompare
+}
+
+func initTestBlockStatusForEqual() []TestBlockStatusStruct {
+	tests := []TestBlockStatusStruct{
+		{
+			baseBlockStatus: 0,
+			blockStatusesToCompareTo: []TestBlockStatusToCompare{
+				{
+					blockStatus:    1,
+					expectedResult: false,
+				},
+				{
+					blockStatus:    0,
+					expectedResult: true,
+				},
+			},
+		}, {
+			baseBlockStatus: 255,
+			blockStatusesToCompareTo: []TestBlockStatusToCompare{
+				{
+					blockStatus:    1,
+					expectedResult: false,
+				},
+				{
+					blockStatus:    255,
+					expectedResult: true,
+				},
+			},
+		},
+	}
+	return tests
+}
+
+func TestBlockStatus_Equal(t *testing.T) {
+
+	testBlockStatus := initTestBlockStatusForEqual()
+
+	for i, test := range testBlockStatus {
+		for j, subTest := range test.blockStatusesToCompareTo {
+			result1 := test.baseBlockStatus.Equal(subTest.blockStatus)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+
+			result2 := subTest.blockStatus.Equal(test.baseBlockStatus)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestBlockStatus_Clone(t *testing.T) {
+
+	testBlockStatus := initTestBlockStatusForClone()
+	for i, blockStatus := range testBlockStatus {
+		blockStatusClone := blockStatus.Clone()
+		if !blockStatusClone.Equal(blockStatus) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(blockStatus, blockStatusClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/coinbase.go b/domain/consensus/model/externalapi/coinbase.go
index a0ce7fead..5770ed48a 100644
--- a/domain/consensus/model/externalapi/coinbase.go
+++ b/domain/consensus/model/externalapi/coinbase.go
@@ -6,3 +6,17 @@ type DomainCoinbaseData struct {
 	ScriptPublicKey []byte
 	ExtraData       []byte
 }
+
+// Clone returns a clone of DomainCoinbaseData
+func (dcd *DomainCoinbaseData) Clone() *DomainCoinbaseData {
+	scriptPubKeyClone := make([]byte, len(dcd.ScriptPublicKey))
+	copy(scriptPubKeyClone, dcd.ScriptPublicKey)
+
+	extraDataClone := make([]byte, len(dcd.ExtraData))
+	copy(extraDataClone, dcd.ExtraData)
+
+	return &DomainCoinbaseData{
+		ScriptPublicKey: scriptPubKeyClone,
+		ExtraData:       extraDataClone,
+	}
+}
diff --git a/domain/consensus/model/externalapi/coinbase_clone_test.go b/domain/consensus/model/externalapi/coinbase_clone_test.go
new file mode 100644
index 000000000..b7c29bbc7
--- /dev/null
+++ b/domain/consensus/model/externalapi/coinbase_clone_test.go
@@ -0,0 +1,59 @@
+package externalapi
+
+import (
+	"reflect"
+	"testing"
+)
+
+func initTestCoinbaseDataStructsForClone() []*DomainCoinbaseData {
+
+	tests := []*DomainCoinbaseData{
+		{
+			[]byte{1, 2, 3, 4, 5, 6},
+			[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+		}, {
+			[]byte{0, 0, 0, 0, 55},
+			[]byte{0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF},
+		},
+	}
+	return tests
+}
+
+func TestDomainCoinbaseData_Clone(t *testing.T) {
+
+	coinbaseData := initTestCoinbaseDataStructsForClone()
+	for i, coinbase := range coinbaseData {
+		coinbaseClone := coinbase.Clone()
+		if !reflect.DeepEqual(coinbase, coinbaseClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/equal_test.go b/domain/consensus/model/externalapi/equal_test.go
new file mode 100644
index 000000000..4ccd24508
--- /dev/null
+++ b/domain/consensus/model/externalapi/equal_test.go
@@ -0,0 +1,238 @@
+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{{0}},
+						DomainHash{1},
+						DomainHash{2},
+						DomainHash{3},
+						4,
+						5,
+						6,
+					},
+					expectedResult: false,
+				},
+			},
+		},
+		{
+			baseHeader: &DomainBlockHeader{
+				0,
+				[]*DomainHash{{1}},
+				DomainHash{2},
+				DomainHash{3},
+				DomainHash{4},
+				5,
+				6,
+				7,
+			},
+			headersToCompareTo: []headerToCompare{
+				{
+					header:         nil,
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{1}},
+						DomainHash{2},
+						DomainHash{3},
+						DomainHash{4},
+						5,
+						6,
+						7,
+					},
+					expectedResult: true,
+				},
+				{
+					header: &DomainBlockHeader{
+						100,
+						[]*DomainHash{{1}},
+						DomainHash{2},
+						DomainHash{3},
+						DomainHash{4},
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{1}, {2}},
+						DomainHash{2},
+						DomainHash{3},
+						DomainHash{4},
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{100}},
+						DomainHash{2},
+						DomainHash{3},
+						DomainHash{4},
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{1}},
+						DomainHash{100},
+						DomainHash{3},
+						DomainHash{4},
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{1}},
+						DomainHash{2},
+						DomainHash{100},
+						DomainHash{4},
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{1}},
+						DomainHash{2},
+						DomainHash{3},
+						DomainHash{100},
+						5,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{1}},
+						DomainHash{2},
+						DomainHash{3},
+						DomainHash{4},
+						100,
+						6,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{1}},
+						DomainHash{2},
+						DomainHash{3},
+						DomainHash{4},
+						5,
+						100,
+						7,
+					},
+					expectedResult: false,
+				},
+				{
+					header: &DomainBlockHeader{
+						0,
+						[]*DomainHash{{1}},
+						DomainHash{2},
+						DomainHash{3},
+						DomainHash{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{{0}},
+			DomainHash{1},
+			DomainHash{2},
+			DomainHash{3},
+			4,
+			5,
+			6,
+		},
+		{
+			0,
+			[]*DomainHash{},
+			DomainHash{1},
+			DomainHash{2},
+			DomainHash{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/model/externalapi/hash.go b/domain/consensus/model/externalapi/hash.go
index 40446d6d1..e7080e1bc 100644
--- a/domain/consensus/model/externalapi/hash.go
+++ b/domain/consensus/model/externalapi/hash.go
@@ -19,14 +19,37 @@ func (hash DomainHash) String() string {
 
 // Clone clones the hash
 func (hash *DomainHash) Clone() *DomainHash {
-	if hash == nil {
-		return nil
-	}
-
 	hashClone := *hash
 	return &hashClone
 }
 
+// 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 _ DomainHash = [DomainHashSize]byte{}
+
+// Equal returns whether hash equals to other
+func (hash *DomainHash) Equal(other *DomainHash) bool {
+	if hash == nil || other == nil {
+		return hash == other
+	}
+
+	return *hash == *other
+}
+
+// HashesEqual returns whether the given hash slices are equal.
+func HashesEqual(a, b []*DomainHash) bool {
+	if len(a) != len(b) {
+		return false
+	}
+
+	for i, hash := range a {
+		if !hash.Equal(b[i]) {
+			return false
+		}
+	}
+	return true
+}
+
 // CloneHashes returns a clone of the given hashes slice
 func CloneHashes(hashes []*DomainHash) []*DomainHash {
 	clone := make([]*DomainHash, len(hashes))
diff --git a/domain/consensus/model/externalapi/hash_clone_equal_test.go b/domain/consensus/model/externalapi/hash_clone_equal_test.go
new file mode 100644
index 000000000..89e2c844e
--- /dev/null
+++ b/domain/consensus/model/externalapi/hash_clone_equal_test.go
@@ -0,0 +1,119 @@
+package externalapi
+
+import (
+	"reflect"
+	"testing"
+)
+
+func initTestDomainHashForClone() []*DomainHash {
+
+	tests := []*DomainHash{
+
+		{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, 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, 0x00, 0x01},
+		{0x01, 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},
+		{0xFF, 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},
+		{0xFF, 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, 0xFF},
+	}
+	return tests
+}
+
+type testHashToCompare struct {
+	hash           *DomainHash
+	expectedResult bool
+}
+
+type testHashStruct struct {
+	baseHash          *DomainHash
+	hashesToCompareTo []testHashToCompare
+}
+
+func initTestDomainHashForEqual() []*testHashStruct {
+	tests := []*testHashStruct{
+		{
+			baseHash: nil,
+			hashesToCompareTo: []testHashToCompare{
+				{
+					hash:           nil,
+					expectedResult: true,
+				}, {
+					hash: &DomainHash{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, 0x00},
+					expectedResult: false,
+				},
+			},
+		}, {
+			baseHash: &DomainHash{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, 0xFF},
+			hashesToCompareTo: []testHashToCompare{
+				{
+					hash:           nil,
+					expectedResult: false,
+				}, {
+					hash: &DomainHash{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, 0x00},
+					expectedResult: false,
+				}, {
+					hash: &DomainHash{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, 0xFF},
+					expectedResult: true,
+				},
+			},
+		},
+	}
+	return tests
+}
+
+func TestDomainHash_Equal(t *testing.T) {
+
+	hashTests := initTestDomainHashForEqual()
+	for i, test := range hashTests {
+		for j, subTest := range test.hashesToCompareTo {
+			result1 := test.baseHash.Equal(subTest.hash)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.hash.Equal(test.baseHash)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestDomainHash_Clone(t *testing.T) {
+
+	hashes := initTestDomainHashForClone()
+	for i, hash := range hashes {
+		hashClone := hash.Clone()
+		if !hashClone.Equal(hash) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(hash, hashClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/subnetworkid.go b/domain/consensus/model/externalapi/subnetworkid.go
index 8a948dff0..16f978eaa 100644
--- a/domain/consensus/model/externalapi/subnetworkid.go
+++ b/domain/consensus/model/externalapi/subnetworkid.go
@@ -18,10 +18,19 @@ func (id DomainSubnetworkID) String() string {
 
 // Clone returns a clone of DomainSubnetworkID
 func (id *DomainSubnetworkID) Clone() *DomainSubnetworkID {
-	if id == nil {
-		return nil
-	}
-
 	idClone := *id
 	return &idClone
 }
+
+// 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 _ DomainSubnetworkID = [DomainSubnetworkIDSize]byte{}
+
+// Equal returns whether id equals to other
+func (id *DomainSubnetworkID) Equal(other *DomainSubnetworkID) bool {
+	if id == nil || other == nil {
+		return id == other
+	}
+
+	return *id == *other
+}
diff --git a/domain/consensus/model/externalapi/subnetworkid_clone_equal_test.go b/domain/consensus/model/externalapi/subnetworkid_clone_equal_test.go
new file mode 100644
index 000000000..dc6d7c5f0
--- /dev/null
+++ b/domain/consensus/model/externalapi/subnetworkid_clone_equal_test.go
@@ -0,0 +1,99 @@
+package externalapi
+
+import (
+	"reflect"
+	"testing"
+)
+
+func initTestDomainSubnetworkIDForClone() []*DomainSubnetworkID {
+
+	tests := []*DomainSubnetworkID{{1, 0, 0xFF, 0}, {0, 1, 0xFF, 1},
+		{0, 1, 0xFF, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}
+	return tests
+}
+
+type testDomainSubnetworkIDToCompare struct {
+	domainSubnetworkID *DomainSubnetworkID
+	expectedResult     bool
+}
+
+type testDomainSubnetworkIDStruct struct {
+	baseDomainSubnetworkID        *DomainSubnetworkID
+	domainSubnetworkIDToCompareTo []testDomainSubnetworkIDToCompare
+}
+
+func initTestDomainSubnetworkIDForEqual() []testDomainSubnetworkIDStruct {
+	tests := []testDomainSubnetworkIDStruct{
+		{
+			baseDomainSubnetworkID: nil,
+			domainSubnetworkIDToCompareTo: []testDomainSubnetworkIDToCompare{
+				{
+					domainSubnetworkID: &DomainSubnetworkID{255, 255, 0xFF, 0},
+					expectedResult:     false,
+				},
+				{
+					domainSubnetworkID: nil,
+					expectedResult:     true,
+				},
+			},
+		}, {
+			baseDomainSubnetworkID: &DomainSubnetworkID{0},
+			domainSubnetworkIDToCompareTo: []testDomainSubnetworkIDToCompare{
+				{
+					domainSubnetworkID: &DomainSubnetworkID{255, 254, 0xFF, 0},
+					expectedResult:     false,
+				},
+				{
+					domainSubnetworkID: &DomainSubnetworkID{0},
+					expectedResult:     true,
+				},
+			},
+		}, {
+			baseDomainSubnetworkID: &DomainSubnetworkID{0, 1, 0xFF, 1, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+			domainSubnetworkIDToCompareTo: []testDomainSubnetworkIDToCompare{
+				{
+					domainSubnetworkID: &DomainSubnetworkID{0, 1, 0xFF, 1, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+						0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+					expectedResult: true,
+				},
+				{
+					domainSubnetworkID: &DomainSubnetworkID{0, 10, 0xFF, 0},
+					expectedResult:     false,
+				},
+			},
+		},
+	}
+	return tests
+}
+
+func TestDomainSubnetworkID_Equal(t *testing.T) {
+
+	domainSubnetworkIDs := initTestDomainSubnetworkIDForEqual()
+	for i, test := range domainSubnetworkIDs {
+		for j, subTest := range test.domainSubnetworkIDToCompareTo {
+			result1 := test.baseDomainSubnetworkID.Equal(subTest.domainSubnetworkID)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.domainSubnetworkID.Equal(test.baseDomainSubnetworkID)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestDomainSubnetworkID_Clone(t *testing.T) {
+
+	domainSubnetworkIDs := initTestDomainSubnetworkIDForClone()
+	for i, domainSubnetworkID := range domainSubnetworkIDs {
+		domainSubnetworkIDClone := domainSubnetworkID.Clone()
+		if !domainSubnetworkIDClone.Equal(domainSubnetworkID) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(domainSubnetworkID, domainSubnetworkIDClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/sync.go b/domain/consensus/model/externalapi/sync.go
index d5f11a499..d1b717857 100644
--- a/domain/consensus/model/externalapi/sync.go
+++ b/domain/consensus/model/externalapi/sync.go
@@ -7,3 +7,42 @@ type SyncInfo struct {
 	HeaderCount          uint64
 	BlockCount           uint64
 }
+
+// Clone returns a clone of SyncInfo
+func (si *SyncInfo) Clone() *SyncInfo {
+	return &SyncInfo{
+		IsAwaitingUTXOSet:    si.IsAwaitingUTXOSet,
+		IBDRootUTXOBlockHash: si.IBDRootUTXOBlockHash.Clone(),
+		HeaderCount:          si.HeaderCount,
+		BlockCount:           si.BlockCount,
+	}
+}
+
+// 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 _ = SyncInfo{false, &DomainHash{}, 0, 0}
+
+// Equal returns whether si equals to other
+func (si *SyncInfo) Equal(other *SyncInfo) bool {
+	if si == nil || other == nil {
+		return si == other
+	}
+
+	if si.IsAwaitingUTXOSet != other.IsAwaitingUTXOSet {
+		return false
+	}
+
+	if !si.IBDRootUTXOBlockHash.Equal(other.IBDRootUTXOBlockHash) {
+		return false
+	}
+
+	if si.HeaderCount != other.HeaderCount {
+		return false
+	}
+
+	if si.BlockCount != other.BlockCount {
+		return false
+	}
+
+	return true
+}
diff --git a/domain/consensus/model/externalapi/sync_equal_clone_test.go b/domain/consensus/model/externalapi/sync_equal_clone_test.go
new file mode 100644
index 000000000..4a216c564
--- /dev/null
+++ b/domain/consensus/model/externalapi/sync_equal_clone_test.go
@@ -0,0 +1,126 @@
+package externalapi
+
+import (
+	"reflect"
+	"testing"
+)
+
+func initTestSyncInfoForClone() []*SyncInfo {
+
+	tests := []*SyncInfo{{
+		false,
+		&DomainHash{1, 2},
+		0xF,
+		0xF}}
+	return tests
+}
+
+type testSyncInfoToCompare struct {
+	syncInfo       *SyncInfo
+	expectedResult bool
+}
+
+type testSyncInfoStruct struct {
+	baseSyncInfo        *SyncInfo
+	syncInfoToCompareTo []testSyncInfoToCompare
+}
+
+func initTestSyncInfoForEqual() []*testSyncInfoStruct {
+	tests := []*testSyncInfoStruct{
+		{
+			baseSyncInfo: nil,
+			syncInfoToCompareTo: []testSyncInfoToCompare{
+				{
+					syncInfo: &SyncInfo{
+						false,
+						&DomainHash{1, 2},
+						0xF,
+						0xF},
+					expectedResult: false,
+				}, {
+					syncInfo:       nil,
+					expectedResult: true,
+				},
+			}}, {
+			baseSyncInfo: &SyncInfo{
+				false,
+				&DomainHash{1, 2},
+				0xF,
+				0xF},
+			syncInfoToCompareTo: []testSyncInfoToCompare{
+				{
+					syncInfo: &SyncInfo{
+						false,
+						&DomainHash{1, 2},
+						0xF,
+						0xF},
+					expectedResult: true,
+				}, {
+					syncInfo: &SyncInfo{
+						true,
+						&DomainHash{1, 2},
+						0xF,
+						0xF},
+					expectedResult: false,
+				},
+				{
+					syncInfo: &SyncInfo{
+						false,
+						&DomainHash{1, 3},
+						0xF,
+						0xF},
+					expectedResult: false,
+				},
+				{
+					syncInfo: &SyncInfo{
+						false,
+						&DomainHash{1, 2},
+						0xF1,
+						0xF},
+					expectedResult: false,
+				}, {
+					syncInfo:       nil,
+					expectedResult: false,
+				}, {
+					syncInfo: &SyncInfo{
+						false,
+						&DomainHash{1, 2},
+						0xF,
+						0xF1},
+					expectedResult: false},
+			},
+		},
+	}
+	return tests
+}
+
+func TestSyncInfo_Equal(t *testing.T) {
+
+	testSyncState := initTestSyncInfoForEqual()
+	for i, test := range testSyncState {
+		for j, subTest := range test.syncInfoToCompareTo {
+			result1 := test.baseSyncInfo.Equal(subTest.syncInfo)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.syncInfo.Equal(test.baseSyncInfo)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestSyncInfo_Clone(t *testing.T) {
+
+	testSyncInfo := initTestSyncInfoForClone()
+	for i, syncInfo := range testSyncInfo {
+		syncStateClone := syncInfo.Clone()
+		if !syncStateClone.Equal(syncInfo) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(syncInfo, syncStateClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/transaction.go b/domain/consensus/model/externalapi/transaction.go
index 0ffdf0e48..d0410e9f3 100644
--- a/domain/consensus/model/externalapi/transaction.go
+++ b/domain/consensus/model/externalapi/transaction.go
@@ -1,7 +1,9 @@
 package externalapi
 
 import (
+	"bytes"
 	"fmt"
+	"github.com/pkg/errors"
 )
 
 // DomainTransaction represents a Kaspa transaction
@@ -25,10 +27,6 @@ type DomainTransaction struct {
 
 // Clone returns a clone of DomainTransaction
 func (tx *DomainTransaction) Clone() *DomainTransaction {
-	if tx == nil {
-		return nil
-	}
-
 	payloadClone := make([]byte, len(tx.Payload))
 	copy(payloadClone, tx.Payload)
 
@@ -42,6 +40,11 @@ func (tx *DomainTransaction) Clone() *DomainTransaction {
 		outputsClone[i] = output.Clone()
 	}
 
+	var idClone *DomainTransactionID
+	if tx.ID != nil {
+		idClone = tx.ID.Clone()
+	}
+
 	return &DomainTransaction{
 		Version:      tx.Version,
 		Inputs:       inputsClone,
@@ -53,9 +56,81 @@ func (tx *DomainTransaction) Clone() *DomainTransaction {
 		Payload:      payloadClone,
 		Fee:          tx.Fee,
 		Mass:         tx.Mass,
+		ID:           idClone,
 	}
 }
 
+// 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 _ = DomainTransaction{0, []*DomainTransactionInput{}, []*DomainTransactionOutput{}, 0,
+	DomainSubnetworkID{}, 0, DomainHash{}, []byte{}, 0, 0,
+	&DomainTransactionID{}}
+
+// Equal returns whether tx equals to other
+func (tx *DomainTransaction) Equal(other *DomainTransaction) bool {
+	if tx == nil || other == nil {
+		return tx == other
+	}
+
+	if tx.Version != other.Version {
+		return false
+	}
+
+	if len(tx.Inputs) != len(other.Inputs) {
+		return false
+	}
+
+	for i, input := range tx.Inputs {
+		if !input.Equal(other.Inputs[i]) {
+			return false
+		}
+	}
+
+	if len(tx.Outputs) != len(other.Outputs) {
+		return false
+	}
+
+	for i, output := range tx.Outputs {
+		if !output.Equal(other.Outputs[i]) {
+			return false
+		}
+	}
+
+	if tx.LockTime != other.LockTime {
+		return false
+	}
+
+	if !tx.SubnetworkID.Equal(&other.SubnetworkID) {
+		return false
+	}
+
+	if tx.Gas != other.Gas {
+		return false
+	}
+
+	if !tx.PayloadHash.Equal(&other.PayloadHash) {
+		return false
+	}
+
+	if !bytes.Equal(tx.Payload, other.Payload) {
+		return false
+	}
+
+	if tx.Fee != other.Fee {
+		return false
+	}
+
+	if tx.Mass != other.Mass {
+		return false
+	}
+
+	if tx.ID != nil && other.ID != nil && !tx.ID.Equal(other.ID) {
+		panic(errors.New("identical transactions should always have the same ID"))
+	}
+
+	return true
+}
+
 // DomainTransactionInput represents a Kaspa transaction input
 type DomainTransactionInput struct {
 	PreviousOutpoint DomainOutpoint
@@ -65,12 +140,37 @@ type DomainTransactionInput struct {
 	UTXOEntry UTXOEntry
 }
 
-// Clone returns a clone of DomainTransactionInput
-func (input *DomainTransactionInput) Clone() *DomainTransactionInput {
-	if input == nil {
-		return nil
+// 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 _ = &DomainTransactionInput{DomainOutpoint{}, []byte{}, 0, nil}
+
+// Equal returns whether input equals to other
+func (input *DomainTransactionInput) Equal(other *DomainTransactionInput) bool {
+	if input == nil || other == nil {
+		return input == other
 	}
 
+	if !input.PreviousOutpoint.Equal(&other.PreviousOutpoint) {
+		return false
+	}
+
+	if !bytes.Equal(input.SignatureScript, other.SignatureScript) {
+		return false
+	}
+
+	if input.Sequence != other.Sequence {
+		return false
+	}
+
+	if !input.UTXOEntry.Equal(other.UTXOEntry) {
+		return false
+	}
+
+	return true
+}
+
+// Clone returns a clone of DomainTransactionInput
+func (input *DomainTransactionInput) Clone() *DomainTransactionInput {
 	signatureScriptClone := make([]byte, len(input.SignatureScript))
 	copy(signatureScriptClone, input.SignatureScript)
 
@@ -88,12 +188,21 @@ type DomainOutpoint struct {
 	Index         uint32
 }
 
-// Clone returns a clone of DomainOutpoint
-func (op *DomainOutpoint) Clone() *DomainOutpoint {
-	if op == nil {
-		return nil
+// 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 _ = DomainOutpoint{DomainTransactionID{}, 0}
+
+// Equal returns whether op equals to other
+func (op *DomainOutpoint) Equal(other *DomainOutpoint) bool {
+	if op == nil || other == nil {
+		return op == other
 	}
 
+	return *op == *other
+}
+
+// Clone returns a clone of DomainOutpoint
+func (op *DomainOutpoint) Clone() *DomainOutpoint {
 	return &DomainOutpoint{
 		TransactionID: *op.TransactionID.Clone(),
 		Index:         op.Index,
@@ -119,12 +228,29 @@ type DomainTransactionOutput struct {
 	ScriptPublicKey []byte
 }
 
-// Clone returns a clone of DomainTransactionOutput
-func (output *DomainTransactionOutput) Clone() *DomainTransactionOutput {
-	if output == nil {
-		return nil
+// 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 _ = DomainTransactionOutput{0, []byte{}}
+
+// Equal returns whether output equals to other
+func (output *DomainTransactionOutput) Equal(other *DomainTransactionOutput) bool {
+	if output == nil || other == nil {
+		return output == other
 	}
 
+	if output.Value != other.Value {
+		return false
+	}
+
+	if !bytes.Equal(output.ScriptPublicKey, other.ScriptPublicKey) {
+		return false
+	}
+
+	return true
+}
+
+// Clone returns a clone of DomainTransactionOutput
+func (output *DomainTransactionOutput) Clone() *DomainTransactionOutput {
 	scriptPublicKeyClone := make([]byte, len(output.ScriptPublicKey))
 	copy(scriptPublicKeyClone, output.ScriptPublicKey)
 
@@ -144,10 +270,19 @@ func (id DomainTransactionID) String() string {
 
 // Clone returns a clone of DomainTransactionID
 func (id *DomainTransactionID) Clone() *DomainTransactionID {
-	if id == nil {
-		return nil
-	}
-
 	idClone := *id
 	return &idClone
 }
+
+// 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 _ DomainTransactionID = [DomainHashSize]byte{}
+
+// Equal returns whether id equals to other
+func (id *DomainTransactionID) Equal(other *DomainTransactionID) bool {
+	if id == nil || other == nil {
+		return id == other
+	}
+
+	return *id == *other
+}
diff --git a/domain/consensus/model/externalapi/transaction_equal_clone_test.go b/domain/consensus/model/externalapi/transaction_equal_clone_test.go
new file mode 100644
index 000000000..9a6c6e7b8
--- /dev/null
+++ b/domain/consensus/model/externalapi/transaction_equal_clone_test.go
@@ -0,0 +1,1133 @@
+package externalapi_test
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
+	"reflect"
+	"testing"
+)
+
+// Changed fields of a test struct compared to a base test struct marked as "changed" and
+// pointing in some cases name changed struct field
+
+type transactionToCompare struct {
+	tx             *externalapi.DomainTransaction
+	expectedResult bool
+	expectsPanic   bool
+}
+
+type testDomainTransactionStruct struct {
+	baseTx                 *externalapi.DomainTransaction
+	transactionToCompareTo []*transactionToCompare
+}
+
+type transactionInputToCompare struct {
+	tx             *externalapi.DomainTransactionInput
+	expectedResult bool
+}
+
+type testDomainTransactionInputStruct struct {
+	baseTx                      *externalapi.DomainTransactionInput
+	transactionInputToCompareTo []*transactionInputToCompare
+}
+
+type transactionOutputToCompare struct {
+	tx             *externalapi.DomainTransactionOutput
+	expectedResult bool
+}
+
+type testDomainTransactionOutputStruct struct {
+	baseTx                       *externalapi.DomainTransactionOutput
+	transactionOutputToCompareTo []*transactionOutputToCompare
+}
+
+type domainOutpointToCompare struct {
+	domainOutpoint *externalapi.DomainOutpoint
+	expectedResult bool
+}
+
+type testDomainOutpointStruct struct {
+	baseDomainOutpoint        *externalapi.DomainOutpoint
+	domainOutpointToCompareTo []*domainOutpointToCompare
+}
+
+type domainTransactionIDToCompare struct {
+	domainTransactionID *externalapi.DomainTransactionID
+	expectedResult      bool
+}
+
+type testDomainTransactionIDStruct struct {
+	baseDomainTransactionID        *externalapi.DomainTransactionID
+	domainTransactionIDToCompareTo []*domainTransactionIDToCompare
+}
+
+func initTestBaseTransaction() *externalapi.DomainTransaction {
+
+	testTx := &externalapi.DomainTransaction{
+		1,
+		[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+			externalapi.DomainTransactionID{0x01}, 0xFFFF},
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFFF),
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+		[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+			[]byte{1, 2}},
+			{uint64(0xFFFF),
+				[]byte{1, 3}}},
+		1,
+		externalapi.DomainSubnetworkID{0x01},
+		1,
+		externalapi.DomainHash{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, 0x00},
+		[]byte{0x01},
+		0,
+		1,
+		&externalapi.DomainTransactionID{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, 0x02},
+	}
+	return testTx
+}
+
+func initTestTransactionToCompare() []*transactionToCompare {
+
+	testTx := []*transactionToCompare{{
+		tx: &externalapi.DomainTransaction{
+			1,
+			[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}},
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			1,
+			externalapi.DomainSubnetworkID{0x01},
+			1,
+			externalapi.DomainHash{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}, //Changed
+			[]byte{0x01},
+			0,
+			1,
+			&externalapi.DomainTransactionID{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, 0x02},
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransaction{
+			1,
+			[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 3}}, //Changed
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			1,
+			externalapi.DomainSubnetworkID{0x01},
+			1,
+			externalapi.DomainHash{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, 0x00},
+			[]byte{0x01},
+			0,
+			1,
+			&externalapi.DomainTransactionID{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, 0x02},
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransaction{
+			1,
+			[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}},
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			1,
+			externalapi.DomainSubnetworkID{0x01, 0x02}, //Changed
+			1,
+			externalapi.DomainHash{0x01, 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},
+			[]byte{0x01},
+			0,
+			1,
+			&externalapi.DomainTransactionID{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, 0x02},
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransaction{
+			1,
+			[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}},
+				{uint64(0xFFFF),
+					[]byte{1, 3}}},
+			1,
+			externalapi.DomainSubnetworkID{0x01},
+			1,
+			externalapi.DomainHash{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, 0x00},
+			[]byte{0x01, 0x02}, //Changed
+			0,
+			1,
+			&externalapi.DomainTransactionID{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, 0x02},
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransaction{
+			1,
+			[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{0x01}, 0xFFFF},
+				[]byte{1, 2, 3},
+				uint64(0xFFFFFFFF),
+				utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+			[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}}, {uint64(0xFFFF),
+				[]byte{1, 3}}},
+			1,
+			externalapi.DomainSubnetworkID{0x01},
+			1,
+			externalapi.DomainHash{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, 0x00},
+			[]byte{0x01},
+			0,
+			1,
+			&externalapi.DomainTransactionID{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, 0x02},
+		},
+		expectedResult: true,
+	},
+		{
+			// ID changed
+			tx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}}, {uint64(0xFFFF),
+					[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x03},
+			},
+			expectsPanic: true,
+		},
+		{
+			tx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}},
+					{uint64(0xFFFF),
+						[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				1000000000, //Changed
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			expectedResult: false,
+		}, {
+			tx: &externalapi.DomainTransaction{
+				2, //Changed
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}}, {uint64(0xFFFF),
+					[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			}, //6
+			expectedResult: false,
+		}, {
+			tx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}}, {uint64(0xFFFF),
+					[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				2, //Changed
+				&externalapi.DomainTransactionID{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},
+			}, //7
+			expectedResult: false,
+		}, {
+			tx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}}, {uint64(0xFFFF),
+					[]byte{1, 3}}},
+				2, //Changed
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			expectedResult: false,
+		}, {
+			tx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)},
+					{externalapi.DomainOutpoint{
+						externalapi.DomainTransactionID{0x01}, 0xFFFF},
+						[]byte{1, 2, 3},
+						uint64(0xFFFFFFFF),
+						utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}}, {uint64(0xFFFF),
+					[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			expectedResult: false,
+		}, {
+			tx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}}, {uint64(0xFFFF),
+					[]byte{1, 3}}, {uint64(0xFFFFF),
+					[]byte{1, 2, 3}}}, //changed Outputs
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			expectedResult: false,
+		}, {
+			tx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}}, {uint64(0xFFFF),
+					[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				nil, //changed
+			},
+			expectedResult: true,
+		}, {
+			tx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3, 4}, true, 2)}},
+				[]*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+					[]byte{1, 2}}, {uint64(0xFFFF),
+					[]byte{1, 3}}},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			expectedResult: false,
+		},
+	}
+	return testTx
+}
+
+func initTestDomainTransactionForClone() []*externalapi.DomainTransaction {
+
+	tests := []*externalapi.DomainTransaction{
+		{
+			Version: 1,
+			Inputs: []*externalapi.DomainTransactionInput{
+				{externalapi.DomainOutpoint{
+					externalapi.DomainTransactionID{0x01}, 0xFFFF},
+					[]byte{1, 2, 3},
+					uint64(0xFFFFFFFF),
+					utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2)},
+			},
+			Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
+				[]byte{1, 2}}},
+			LockTime:     1,
+			SubnetworkID: externalapi.DomainSubnetworkID{0x01},
+			Gas:          1,
+			PayloadHash: externalapi.DomainHash{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},
+			Payload: []byte{0x01},
+			Fee:     5555555555,
+			Mass:    1,
+			ID: &externalapi.DomainTransactionID{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, 0x02},
+		}, {
+			Version:      1,
+			Inputs:       []*externalapi.DomainTransactionInput{},
+			Outputs:      []*externalapi.DomainTransactionOutput{},
+			LockTime:     1,
+			SubnetworkID: externalapi.DomainSubnetworkID{0x01},
+			Gas:          1,
+			PayloadHash: externalapi.DomainHash{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},
+			Payload: []byte{0x01},
+			Fee:     0,
+			Mass:    1,
+			ID:      &externalapi.DomainTransactionID{},
+		},
+	}
+	return tests
+}
+
+func initTestDomainTransactionForEqual() []testDomainTransactionStruct {
+
+	tests := []testDomainTransactionStruct{
+		{
+			baseTx:                 initTestBaseTransaction(),
+			transactionToCompareTo: initTestTransactionToCompare(),
+		},
+		{
+			baseTx: nil,
+			transactionToCompareTo: []*transactionToCompare{{
+				tx: &externalapi.DomainTransaction{
+					1,
+					[]*externalapi.DomainTransactionInput{},
+					[]*externalapi.DomainTransactionOutput{},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					1,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					&externalapi.DomainTransactionID{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, 0x02},
+				},
+				expectedResult: false,
+			}, {
+				tx:             nil,
+				expectedResult: true}},
+		}, {
+			baseTx: &externalapi.DomainTransaction{
+				1,
+				[]*externalapi.DomainTransactionInput{},
+				[]*externalapi.DomainTransactionOutput{},
+				1,
+				externalapi.DomainSubnetworkID{0x01},
+				1,
+				externalapi.DomainHash{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, 0x00},
+				[]byte{0x01},
+				0,
+				1,
+				&externalapi.DomainTransactionID{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, 0x02},
+			},
+			transactionToCompareTo: []*transactionToCompare{{
+				tx:             nil,
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransaction{
+					1,
+					[]*externalapi.DomainTransactionInput{},
+					[]*externalapi.DomainTransactionOutput{},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					0,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					nil,
+				},
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransaction{
+					1,
+					[]*externalapi.DomainTransactionInput{},
+					[]*externalapi.DomainTransactionOutput{},
+					1,
+					externalapi.DomainSubnetworkID{0x01},
+					1,
+					externalapi.DomainHash{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, 0x00},
+					[]byte{0x01},
+					0,
+					1,
+					nil,
+				},
+				expectedResult: true,
+			}},
+		},
+	}
+	return tests
+}
+
+func initTestBaseDomainTransactionInput() *externalapi.DomainTransactionInput {
+	basetxInput := &externalapi.DomainTransactionInput{
+		externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF},
+		[]byte{1, 2, 3},
+		uint64(0xFFFFFFFF),
+		utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2),
+	}
+	return basetxInput
+}
+
+func initTestDomainTxInputToCompare() []*transactionInputToCompare {
+	txInput := []*transactionInputToCompare{{
+		tx: &externalapi.DomainTransactionInput{
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF},
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFFF),
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2),
+		},
+		expectedResult: true,
+	}, {
+		tx: &externalapi.DomainTransactionInput{
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF},
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFFF),
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, false, 2), // Changed
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransactionInput{
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF},
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFF0), // Changed
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2),
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransactionInput{
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF},
+			[]byte{1, 2, 3, 4}, // Changed
+			uint64(0xFFFFFFFF),
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2),
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransactionInput{
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01, 0x02}, 0xFFFF}, // Changed
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFF0), // Changed
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2),
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransactionInput{
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01, 0x02}, 0xFFFF}, // Changed
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFF0), // Changed
+			utxo.NewUTXOEntry(2 /* Changed */, []byte{0, 1, 2, 3}, true, 2), // Changed
+		},
+		expectedResult: false,
+	}, {
+		tx: &externalapi.DomainTransactionInput{
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01, 0x02}, 0xFFFF}, // Changed
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFF0), // Changed
+			utxo.NewUTXOEntry(3 /* Changed */, []byte{0, 1, 2, 3}, true, 3), // Changed
+		},
+		expectedResult: false,
+	}, {
+		tx:             nil,
+		expectedResult: false,
+	}}
+	return txInput
+
+}
+
+func initTestDomainTransactionInputForClone() []*externalapi.DomainTransactionInput {
+	txInput := []*externalapi.DomainTransactionInput{
+		{
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF},
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFFF),
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2),
+		}, {
+
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF},
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFFF),
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2),
+		}, {
+
+			externalapi.DomainOutpoint{externalapi.DomainTransactionID{0x01}, 0xFFFF},
+			[]byte{1, 2, 3},
+			uint64(0xFFFFFFF0),
+			utxo.NewUTXOEntry(1, []byte{0, 1, 2, 3}, true, 2),
+		}}
+	return txInput
+}
+
+func initTestBaseDomainTransactionOutput() *externalapi.DomainTransactionOutput {
+	basetxOutput := &externalapi.DomainTransactionOutput{
+		0xFFFFFFFF,
+		[]byte{0xFF, 0xFF},
+	}
+	return basetxOutput
+}
+
+func initTestDomainTransactionOutputForClone() []*externalapi.DomainTransactionOutput {
+	txInput := []*externalapi.DomainTransactionOutput{
+		{
+			0xFFFFFFFF,
+			[]byte{0xF0, 0xFF},
+		}, {
+			0xFFFFFFF1,
+			[]byte{0xFF, 0xFF},
+		}}
+	return txInput
+}
+
+func initTestDomainTransactionOutputForEqual() []testDomainTransactionOutputStruct {
+	tests := []testDomainTransactionOutputStruct{
+		{
+			baseTx: initTestBaseDomainTransactionOutput(),
+			transactionOutputToCompareTo: []*transactionOutputToCompare{{
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFFF,
+					[]byte{0xFF, 0xFF}},
+				expectedResult: true,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFFF,
+					[]byte{0xF0, 0xFF}, // Changed
+				},
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFF0, // Changed
+					[]byte{0xFF, 0xFF},
+				},
+				expectedResult: false,
+			}, {
+				tx:             nil,
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFF0,               // Changed
+					[]byte{0xFF, 0xFF, 0x01}, // Changed
+				},
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFF0, // Changed
+					[]byte{},   // Changed
+				},
+				expectedResult: false,
+			}},
+		},
+		{
+			baseTx: nil,
+			transactionOutputToCompareTo: []*transactionOutputToCompare{{
+				tx:             nil,
+				expectedResult: true,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFFF,
+					[]byte{0xFF, 0xFF}},
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFFF,
+					[]byte{0xF0, 0xFF}, // Changed
+				},
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFF0, // Changed
+					[]byte{0xFF, 0xFF},
+				},
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFF0,
+					[]byte{0xFF, 0xFF, 0x01}, // Changed
+				},
+				expectedResult: false,
+			}, {
+				tx: &externalapi.DomainTransactionOutput{
+					0xFFFFFFF0,
+					[]byte{}, // Changed
+				},
+				expectedResult: false,
+			}},
+		},
+	}
+	return tests
+}
+
+func initTestDomainTransactionInputForEqual() []testDomainTransactionInputStruct {
+
+	tests := []testDomainTransactionInputStruct{
+		{
+			baseTx:                      initTestBaseDomainTransactionInput(),
+			transactionInputToCompareTo: initTestDomainTxInputToCompare(),
+		},
+	}
+	return tests
+}
+
+func TestDomainTransaction_Equal(t *testing.T) {
+
+	txTests := initTestDomainTransactionForEqual()
+	for i, test := range txTests {
+		for j, subTest := range test.transactionToCompareTo {
+			func() {
+				defer func() {
+					r := recover()
+					panicked := r != nil
+					if panicked != subTest.expectsPanic {
+						t.Fatalf("panicked expected to be %t but got %t", subTest.expectsPanic, panicked)
+					}
+				}()
+				result1 := test.baseTx.Equal(subTest.tx)
+				if result1 != subTest.expectedResult {
+					t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+				}
+			}()
+			func() {
+				defer func() {
+					r := recover()
+					panicked := r != nil
+					if panicked != subTest.expectsPanic {
+						t.Fatalf("panicked expected to be %t but got %t", subTest.expectsPanic, panicked)
+					}
+				}()
+				result2 := subTest.tx.Equal(test.baseTx)
+				if result2 != subTest.expectedResult {
+					t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+				}
+			}()
+		}
+	}
+}
+
+func TestDomainTransaction_Clone(t *testing.T) {
+
+	txs := initTestDomainTransactionForClone()
+	for i, tx := range txs {
+		txClone := tx.Clone()
+		if !txClone.Equal(tx) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(tx, txClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
+
+func TestDomainTransactionInput_Equal(t *testing.T) {
+
+	txTests := initTestDomainTransactionInputForEqual()
+	for i, test := range txTests {
+		for j, subTest := range test.transactionInputToCompareTo {
+			result1 := test.baseTx.Equal(subTest.tx)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.tx.Equal(test.baseTx)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestDomainTransactionInput_Clone(t *testing.T) {
+
+	txInputs := initTestDomainTransactionInputForClone()
+	for i, txInput := range txInputs {
+		txInputClone := txInput.Clone()
+		if !txInputClone.Equal(txInput) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(txInput, txInputClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
+
+func TestDomainTransactionOutput_Equal(t *testing.T) {
+
+	txTests := initTestDomainTransactionOutputForEqual()
+	for i, test := range txTests {
+		for j, subTest := range test.transactionOutputToCompareTo {
+			result1 := test.baseTx.Equal(subTest.tx)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.tx.Equal(test.baseTx)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestDomainTransactionOutput_Clone(t *testing.T) {
+
+	txInputs := initTestDomainTransactionOutputForClone()
+	for i, txOutput := range txInputs {
+		txOutputClone := txOutput.Clone()
+		if !txOutputClone.Equal(txOutput) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(txOutput, txOutputClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
+
+func initTestDomainOutpointForClone() []*externalapi.DomainOutpoint {
+	outpoint := []*externalapi.DomainOutpoint{{
+		externalapi.DomainTransactionID{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, 0x03},
+		1},
+	}
+	return outpoint
+}
+
+func initTestDomainOutpointForEqual() []testDomainOutpointStruct {
+
+	var outpoint = []*domainOutpointToCompare{{
+		domainOutpoint: &externalapi.DomainOutpoint{
+			externalapi.DomainTransactionID{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, 0x02},
+			1},
+		expectedResult: true,
+	}, {
+		domainOutpoint: &externalapi.DomainOutpoint{
+			externalapi.DomainTransactionID{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, 0x03},
+			1},
+		expectedResult: false,
+	}, {
+		domainOutpoint: &externalapi.DomainOutpoint{
+			externalapi.DomainTransactionID{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, 0x0},
+			2},
+		expectedResult: false,
+	}}
+	tests := []testDomainOutpointStruct{
+		{
+			baseDomainOutpoint: &externalapi.DomainOutpoint{
+				externalapi.DomainTransactionID{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, 0x02},
+				1},
+			domainOutpointToCompareTo: outpoint,
+		}, {baseDomainOutpoint: &externalapi.DomainOutpoint{
+			externalapi.DomainTransactionID{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, 0x02},
+			1},
+			domainOutpointToCompareTo: []*domainOutpointToCompare{{domainOutpoint: nil, expectedResult: false}},
+		}, {baseDomainOutpoint: nil,
+			domainOutpointToCompareTo: []*domainOutpointToCompare{{domainOutpoint: nil, expectedResult: true}},
+		},
+	}
+	return tests
+}
+
+func TestDomainOutpoint_Equal(t *testing.T) {
+
+	domainOutpoints := initTestDomainOutpointForEqual()
+	for i, test := range domainOutpoints {
+		for j, subTest := range test.domainOutpointToCompareTo {
+			result1 := test.baseDomainOutpoint.Equal(subTest.domainOutpoint)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.domainOutpoint.Equal(test.baseDomainOutpoint)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestDomainOutpoint_Clone(t *testing.T) {
+
+	domainOutpoints := initTestDomainOutpointForClone()
+	for i, outpoint := range domainOutpoints {
+		outpointClone := outpoint.Clone()
+		if !outpointClone.Equal(outpoint) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(outpoint, outpointClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
+
+func initTestDomainTransactionIDForClone() []*externalapi.DomainTransactionID {
+	outpoint := []*externalapi.DomainTransactionID{
+		{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, 0x03},
+		{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},
+	}
+
+	return outpoint
+}
+
+func initTestDomainTransactionIDForEqual() []testDomainTransactionIDStruct {
+
+	var outpoint = []*domainTransactionIDToCompare{{
+		domainTransactionID: &externalapi.DomainTransactionID{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, 0x02},
+		expectedResult: true,
+	}, {
+		domainTransactionID: &externalapi.DomainTransactionID{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, 0x03},
+		expectedResult: false,
+	}, {
+		domainTransactionID: &externalapi.DomainTransactionID{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, 0x0},
+		expectedResult: false,
+	}}
+	tests := []testDomainTransactionIDStruct{
+		{
+			baseDomainTransactionID: &externalapi.DomainTransactionID{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, 0x02},
+			domainTransactionIDToCompareTo: outpoint,
+		}, {
+			baseDomainTransactionID: nil,
+			domainTransactionIDToCompareTo: []*domainTransactionIDToCompare{{
+				domainTransactionID: &externalapi.DomainTransactionID{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, 0x03},
+				expectedResult: false,
+			}},
+		},
+	}
+	return tests
+}
+
+func TestDomainTransactionID_Equal(t *testing.T) {
+
+	domainDomainTransactionIDs := initTestDomainTransactionIDForEqual()
+	for i, test := range domainDomainTransactionIDs {
+		for j, subTest := range test.domainTransactionIDToCompareTo {
+			result1 := test.baseDomainTransactionID.Equal(subTest.domainTransactionID)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.domainTransactionID.Equal(test.baseDomainTransactionID)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestDomainTransactionID_Clone(t *testing.T) {
+
+	domainDomainTransactionIDs := initTestDomainTransactionIDForClone()
+	for i, domainTransactionID := range domainDomainTransactionIDs {
+		domainTransactionIDClone := domainTransactionID.Clone()
+		if !domainTransactionIDClone.Equal(domainTransactionID) {
+			t.Fatalf("Test #%d:[Equal] clone should be equal to the original", i)
+		}
+		if !reflect.DeepEqual(domainTransactionID, domainTransactionIDClone) {
+			t.Fatalf("Test #%d:[DeepEqual] clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/model/externalapi/utxoentry.go b/domain/consensus/model/externalapi/utxoentry.go
index 22f9498b8..efc803809 100644
--- a/domain/consensus/model/externalapi/utxoentry.go
+++ b/domain/consensus/model/externalapi/utxoentry.go
@@ -9,4 +9,5 @@ type UTXOEntry interface {
 	ScriptPublicKey() []byte // The public key script for the output.
 	BlockBlueScore() uint64  // Blue score of the block accepting the tx.
 	IsCoinbase() bool
+	Equal(other UTXOEntry) bool
 }
diff --git a/domain/consensus/model/reachabilitydata.go b/domain/consensus/model/reachabilitydata.go
index 93ffd3b20..f90c42e5a 100644
--- a/domain/consensus/model/reachabilitydata.go
+++ b/domain/consensus/model/reachabilitydata.go
@@ -12,15 +12,32 @@ type ReachabilityData struct {
 	FutureCoveringSet FutureCoveringTreeNodeSet
 }
 
-// Clone returns a clone of ReachabilityData
-func (rd *ReachabilityData) Clone() *ReachabilityData {
-	if rd == nil {
-		return nil
+// 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 _ = &ReachabilityData{&ReachabilityTreeNode{}, FutureCoveringTreeNodeSet{}}
+
+// Equal returns whether rd equals to other
+func (rd *ReachabilityData) Equal(other *ReachabilityData) bool {
+	if rd == nil || other == nil {
+		return rd == other
 	}
 
+	if !rd.TreeNode.Equal(other.TreeNode) {
+		return false
+	}
+
+	if !rd.FutureCoveringSet.Equal(other.FutureCoveringSet) {
+		return false
+	}
+
+	return true
+}
+
+// Clone returns a clone of ReachabilityData
+func (rd *ReachabilityData) Clone() *ReachabilityData {
 	return &ReachabilityData{
 		TreeNode:          rd.TreeNode.Clone(),
-		FutureCoveringSet: externalapi.CloneHashes(rd.FutureCoveringSet),
+		FutureCoveringSet: rd.FutureCoveringSet.Clone(),
 	}
 }
 
@@ -48,15 +65,43 @@ type ReachabilityTreeNode struct {
 	Interval *ReachabilityInterval
 }
 
+// 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 _ = &ReachabilityTreeNode{[]*externalapi.DomainHash{}, &externalapi.DomainHash{},
+	&ReachabilityInterval{}}
+
+// Equal returns whether rtn equals to other
+func (rtn *ReachabilityTreeNode) Equal(other *ReachabilityTreeNode) bool {
+	if rtn == nil || other == nil {
+		return rtn == other
+	}
+
+	if !externalapi.HashesEqual(rtn.Children, other.Children) {
+		return false
+	}
+
+	if !rtn.Parent.Equal(other.Parent) {
+		return false
+	}
+
+	if !rtn.Interval.Equal(other.Interval) {
+		return false
+	}
+
+	return true
+}
+
 // Clone returns a clone of ReachabilityTreeNode
 func (rtn *ReachabilityTreeNode) Clone() *ReachabilityTreeNode {
-	if rtn == nil {
-		return nil
+
+	var parentClone *externalapi.DomainHash
+	if rtn.Parent != nil {
+		parentClone = rtn.Parent.Clone()
 	}
 
 	return &ReachabilityTreeNode{
 		Children: externalapi.CloneHashes(rtn.Children),
-		Parent:   rtn.Parent.Clone(),
+		Parent:   parentClone,
 		Interval: rtn.Interval.Clone(),
 	}
 }
@@ -69,12 +114,29 @@ type ReachabilityInterval struct {
 	End   uint64
 }
 
-// Clone returns a clone of ReachabilityInterval
-func (ri *ReachabilityInterval) Clone() *ReachabilityInterval {
-	if ri == nil {
-		return nil
+// 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 _ = &ReachabilityInterval{0, 0}
+
+// Equal returns whether ri equals to other
+func (ri *ReachabilityInterval) Equal(other *ReachabilityInterval) bool {
+	if ri == nil || other == nil {
+		return ri == other
 	}
 
+	if ri.Start != other.Start {
+		return false
+	}
+
+	if ri.End != other.End {
+		return false
+	}
+
+	return true
+}
+
+// Clone returns a clone of ReachabilityInterval
+func (ri *ReachabilityInterval) Clone() *ReachabilityInterval {
 	return &ReachabilityInterval{
 		Start: ri.Start,
 		End:   ri.End,
@@ -98,3 +160,17 @@ func (ri *ReachabilityInterval) String() string {
 //
 // See insertNode, hasAncestorOf, and isInPast for further details.
 type FutureCoveringTreeNodeSet []*externalapi.DomainHash
+
+// Clone returns a clone of FutureCoveringTreeNodeSet
+func (fctns FutureCoveringTreeNodeSet) Clone() FutureCoveringTreeNodeSet {
+	return externalapi.CloneHashes(fctns)
+}
+
+// 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 _ FutureCoveringTreeNodeSet = []*externalapi.DomainHash{}
+
+// Equal returns whether fctns equals to other
+func (fctns FutureCoveringTreeNodeSet) Equal(other FutureCoveringTreeNodeSet) bool {
+	return externalapi.HashesEqual(fctns, other)
+}
diff --git a/domain/consensus/model/reachabilitydata_equal_clone_test.go b/domain/consensus/model/reachabilitydata_equal_clone_test.go
new file mode 100644
index 000000000..408981ec9
--- /dev/null
+++ b/domain/consensus/model/reachabilitydata_equal_clone_test.go
@@ -0,0 +1,296 @@
+package model
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"reflect"
+	"testing"
+)
+
+func TestReachabilityData_Equal(t *testing.T) {
+	type dataToCompare struct {
+		data           *ReachabilityData
+		expectedResult bool
+	}
+	tests := []struct {
+		baseData        *ReachabilityData
+		dataToCompareTo []dataToCompare
+	}{
+		// Test nil data
+		{
+			baseData:        nil,
+			dataToCompareTo: nil,
+		},
+		// Test empty data
+		{
+			baseData: &ReachabilityData{
+				&ReachabilityTreeNode{
+					[]*externalapi.DomainHash{},
+					&externalapi.DomainHash{},
+					&ReachabilityInterval{},
+				},
+				FutureCoveringTreeNodeSet{},
+			},
+			dataToCompareTo: []dataToCompare{
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{},
+							&externalapi.DomainHash{},
+							&ReachabilityInterval{},
+						},
+						FutureCoveringTreeNodeSet{},
+					},
+					expectedResult: true,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}}, // Changed
+							&externalapi.DomainHash{},
+							&ReachabilityInterval{},
+						},
+						FutureCoveringTreeNodeSet{},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{},
+							&externalapi.DomainHash{1}, // Changed
+							&ReachabilityInterval{},
+						},
+						FutureCoveringTreeNodeSet{},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{},
+							&externalapi.DomainHash{},
+							&ReachabilityInterval{100, 0}, // Changed start
+						},
+						FutureCoveringTreeNodeSet{},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{},
+							&externalapi.DomainHash{},
+							&ReachabilityInterval{0, 100}, // Changed end
+						},
+						FutureCoveringTreeNodeSet{},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{},
+							&externalapi.DomainHash{},
+							&ReachabilityInterval{},
+						},
+						FutureCoveringTreeNodeSet{{1}, {2}}, // Changed
+					},
+					expectedResult: false,
+				},
+			},
+		},
+		// Test filled data
+		{
+			baseData: &ReachabilityData{
+				&ReachabilityTreeNode{
+					[]*externalapi.DomainHash{{1}, {2}, {3}},
+					&externalapi.DomainHash{1},
+					&ReachabilityInterval{100, 200},
+				},
+				FutureCoveringTreeNodeSet{{1}, {2}},
+			},
+			dataToCompareTo: []dataToCompare{
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}, {3}},
+							&externalapi.DomainHash{1},
+							&ReachabilityInterval{100, 200},
+						},
+						FutureCoveringTreeNodeSet{{1}, {2}},
+					},
+					expectedResult: true,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}, {3}},
+							&externalapi.DomainHash{1},
+							&ReachabilityInterval{100, 200},
+						},
+						FutureCoveringTreeNodeSet{}, // Changed
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}, {3}},
+							&externalapi.DomainHash{1},
+							&ReachabilityInterval{200, 200}, // Changed start
+						},
+						FutureCoveringTreeNodeSet{{1}, {2}},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}, {3}},
+							&externalapi.DomainHash{1},
+							nil, //Changed
+						},
+						FutureCoveringTreeNodeSet{{1}, {2}},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}, {3}},
+							&externalapi.DomainHash{1},
+							&ReachabilityInterval{100, 100}, // Changed end
+						},
+						FutureCoveringTreeNodeSet{{1}, {2}},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{}, // Changed
+							&externalapi.DomainHash{1},
+							&ReachabilityInterval{100, 200},
+						},
+						FutureCoveringTreeNodeSet{{1}, {2}},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}},
+							&externalapi.DomainHash{}, // Changed
+							&ReachabilityInterval{100, 200},
+						},
+						FutureCoveringTreeNodeSet{{1}, {2}},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}},
+							&externalapi.DomainHash{1},
+							&ReachabilityInterval{}, // Changed
+						},
+						FutureCoveringTreeNodeSet{{1}, {2}},
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						&ReachabilityTreeNode{
+							[]*externalapi.DomainHash{{1}, {2}},
+							&externalapi.DomainHash{1},
+							&ReachabilityInterval{100, 200},
+						},
+						FutureCoveringTreeNodeSet{}, // Changed
+					},
+					expectedResult: false,
+				},
+				{
+					data: &ReachabilityData{
+						nil,
+						FutureCoveringTreeNodeSet{},
+					},
+					expectedResult: false,
+				},
+				{
+					data:           nil,
+					expectedResult: false,
+				},
+			},
+		},
+	}
+
+	for i, test := range tests {
+		for j, subTest := range test.dataToCompareTo {
+			result1 := test.baseData.Equal(subTest.data)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+
+			result2 := subTest.data.Equal(test.baseData)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
+
+func TestReachabilityData_Clone(t *testing.T) {
+	testData := []*ReachabilityData{
+		{
+			&ReachabilityTreeNode{
+				[]*externalapi.DomainHash{},
+				&externalapi.DomainHash{},
+				&ReachabilityInterval{},
+			},
+			FutureCoveringTreeNodeSet{},
+		},
+		{
+			&ReachabilityTreeNode{
+				[]*externalapi.DomainHash{{1}, {2}},
+				&externalapi.DomainHash{1},
+				&ReachabilityInterval{100, 200},
+			},
+			FutureCoveringTreeNodeSet{{1}, {2}},
+		},
+		{
+			&ReachabilityTreeNode{
+				[]*externalapi.DomainHash{},
+				&externalapi.DomainHash{1},
+				&ReachabilityInterval{100, 200},
+			},
+			FutureCoveringTreeNodeSet{{1}, {2}},
+		},
+		{
+			&ReachabilityTreeNode{
+				[]*externalapi.DomainHash{{1}, {2}},
+				&externalapi.DomainHash{1},
+				&ReachabilityInterval{},
+			},
+			FutureCoveringTreeNodeSet{{1}, {2}},
+		},
+		{
+			&ReachabilityTreeNode{
+				[]*externalapi.DomainHash{},
+				&externalapi.DomainHash{1},
+				&ReachabilityInterval{100, 200},
+			},
+			FutureCoveringTreeNodeSet{{1}, {2}},
+		},
+	}
+
+	for i, data := range testData {
+		clone := data.Clone()
+		if !clone.Equal(data) {
+			t.Fatalf("Test #%d: clone should be equal to the original", i)
+		}
+
+		if !reflect.DeepEqual(data, clone) {
+			t.Fatalf("Test #%d: clone should be equal to the original", i)
+		}
+	}
+}
diff --git a/domain/consensus/processes/blockprocessor/validateandinsertblock.go b/domain/consensus/processes/blockprocessor/validateandinsertblock.go
index 10ed66614..049388553 100644
--- a/domain/consensus/processes/blockprocessor/validateandinsertblock.go
+++ b/domain/consensus/processes/blockprocessor/validateandinsertblock.go
@@ -74,7 +74,7 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock,
 	}
 
 	var oldHeadersSelectedTip *externalapi.DomainHash
-	isGenesis := *blockHash == *bp.genesisHash
+	isGenesis := blockHash.Equal(bp.genesisHash)
 	if !isGenesis {
 		var err error
 		oldHeadersSelectedTip, err = bp.headersSelectedTipStore.HeadersSelectedTip(bp.databaseContext)
@@ -156,7 +156,7 @@ func (bp *blockProcessor) updateReachabilityReindexRoot(oldHeadersSelectedTip *e
 		return err
 	}
 
-	if *headersSelectedTip == *oldHeadersSelectedTip {
+	if headersSelectedTip.Equal(oldHeadersSelectedTip) {
 		return nil
 	}
 
diff --git a/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint.go b/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint.go
index c2e60cc01..826b6632b 100644
--- a/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint.go
+++ b/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint.go
@@ -17,7 +17,7 @@ func (bp *blockProcessor) validateAndInsertPruningPoint(newPruningPoint *externa
 
 	newPruningPointHash := consensushashing.BlockHash(newPruningPoint)
 
-	if *expectedNewPruningPointHash != *newPruningPointHash {
+	if !expectedNewPruningPointHash.Equal(newPruningPointHash) {
 		return errors.Wrapf(ruleerrors.ErrUnexpectedPruningPoint, "expected pruning point %s but got %s",
 			expectedNewPruningPointHash, newPruningPointHash)
 	}
diff --git a/domain/consensus/processes/blockvalidator/block_body_in_isolation.go b/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
index 5b9367cf1..4daa95b9f 100644
--- a/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
+++ b/domain/consensus/processes/blockvalidator/block_body_in_isolation.go
@@ -143,7 +143,7 @@ func (v *blockValidator) checkTransactionsInIsolation(block *externalapi.DomainB
 
 func (v *blockValidator) checkBlockHashMerkleRoot(block *externalapi.DomainBlock) error {
 	calculatedHashMerkleRoot := merkle.CalculateHashMerkleRoot(block.Transactions)
-	if block.Header.HashMerkleRoot != *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)
diff --git a/domain/consensus/processes/blockvalidator/block_header_in_context.go b/domain/consensus/processes/blockvalidator/block_header_in_context.go
index 19f92a2a1..63976e8bf 100644
--- a/domain/consensus/processes/blockvalidator/block_header_in_context.go
+++ b/domain/consensus/processes/blockvalidator/block_header_in_context.go
@@ -86,7 +86,7 @@ func (v *blockValidator) hasValidatedHeader(blockHash *externalapi.DomainHash) (
 func (v *blockValidator) checkParentsIncest(header *externalapi.DomainBlockHeader) error {
 	for _, parentA := range header.ParentHashes {
 		for _, parentB := range header.ParentHashes {
-			if *parentA == *parentB {
+			if parentA.Equal(parentB) {
 				continue
 			}
 
diff --git a/domain/consensus/processes/blockvalidator/block_header_in_isolation.go b/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
index c6402e6f9..31ca02e4c 100644
--- a/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
+++ b/domain/consensus/processes/blockvalidator/block_header_in_isolation.go
@@ -35,7 +35,7 @@ func (v *blockValidator) ValidateHeaderInIsolation(blockHash *externalapi.Domain
 
 func (v *blockValidator) checkParentsLimit(header *externalapi.DomainBlockHeader) error {
 	hash := consensushashing.HeaderHash(header)
-	if len(header.ParentHashes) == 0 && *hash != *v.genesisHash {
+	if len(header.ParentHashes) == 0 && !hash.Equal(v.genesisHash) {
 		return errors.Wrapf(ruleerrors.ErrNoParents, "block has no parents")
 	}
 
diff --git a/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go b/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go
index aab8210ae..01df31a28 100644
--- a/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go
+++ b/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go
@@ -68,7 +68,7 @@ func (csm *consensusStateManager) isCandidateToBeNextVirtualSelectedParent(block
 	log.Tracef("isCandidateToBeNextVirtualSelectedParent start for block %s", blockHash)
 	defer log.Tracef("isCandidateToBeNextVirtualSelectedParent end for block %s", blockHash)
 
-	if *blockHash == *csm.genesisHash {
+	if blockHash.Equal(csm.genesisHash) {
 		log.Tracef("Block %s is the genesis block, therefore it is "+
 			"the selected parent by definition", blockHash)
 		return true, nil
@@ -87,7 +87,7 @@ func (csm *consensusStateManager) isCandidateToBeNextVirtualSelectedParent(block
 	}
 	log.Tracef("The next selected parent is: %s", nextVirtualSelectedParent)
 
-	return *blockHash == *nextVirtualSelectedParent, nil
+	return blockHash.Equal(nextVirtualSelectedParent), nil
 }
 
 func (csm *consensusStateManager) addTip(newTipHash *externalapi.DomainHash) (newTips []*externalapi.DomainHash, err error) {
@@ -111,7 +111,7 @@ func (csm *consensusStateManager) calculateNewTips(newTipHash *externalapi.Domai
 	log.Tracef("calculateNewTips start for new tip %s", newTipHash)
 	defer log.Tracef("calculateNewTips end for new tip %s", newTipHash)
 
-	if *newTipHash == *csm.genesisHash {
+	if newTipHash.Equal(csm.genesisHash) {
 		log.Tracef("The new tip is the genesis block, therefore it is the only tip by definition")
 		return []*externalapi.DomainHash{newTipHash}, nil
 	}
@@ -133,7 +133,7 @@ func (csm *consensusStateManager) calculateNewTips(newTipHash *externalapi.Domai
 	for _, currentTip := range currentTips {
 		isCurrentTipInNewTipParents := false
 		for _, newTipParent := range newTipParents {
-			if *currentTip == *newTipParent {
+			if currentTip.Equal(newTipParent) {
 				isCurrentTipInNewTipParents = true
 				break
 			}
diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
index 42d214bbf..3b6c3c419 100644
--- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
+++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
@@ -18,7 +18,7 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
 	log.Tracef("CalculatePastUTXOAndAcceptanceData start for block %s", blockHash)
 	defer log.Tracef("CalculatePastUTXOAndAcceptanceData end for block %s", blockHash)
 
-	if *blockHash == *csm.genesisHash {
+	if blockHash.Equal(csm.genesisHash) {
 		log.Tracef("Block %s is the genesis. By definition, "+
 			"it has an empty UTXO diff, empty acceptance data, and a blank multiset", blockHash)
 		return utxo.NewUTXODiff(), externalapi.AcceptanceData{}, multiset.New(), nil
diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go
index a608340e7..ed38040db 100644
--- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go
+++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo_test.go
@@ -154,7 +154,7 @@ func TestPastUTXOMultiset(t *testing.T) {
 		secondMultisetHash := secondMultiset.Hash()
 
 		// Make sure the multiset hasn't changed
-		if *firstMultisetHash != *secondMultisetHash {
+		if !firstMultisetHash.Equal(secondMultisetHash) {
 			t.Fatalf("TestPastUTXOMultiSet: selectedParentMultiset appears to have changed!")
 		}
 	})
diff --git a/domain/consensus/processes/consensusstatemanager/check_finality_violation.go b/domain/consensus/processes/consensusstatemanager/check_finality_violation.go
index 9774276fe..4451c5cee 100644
--- a/domain/consensus/processes/consensusstatemanager/check_finality_violation.go
+++ b/domain/consensus/processes/consensusstatemanager/check_finality_violation.go
@@ -8,7 +8,7 @@ func (csm *consensusStateManager) isViolatingFinality(blockHash *externalapi.Dom
 	log.Tracef("isViolatingFinality start for block %s", blockHash)
 	defer log.Tracef("isViolatingFinality end for block %s", blockHash)
 
-	if *blockHash == *csm.genesisHash {
+	if blockHash.Equal(csm.genesisHash) {
 		log.Tracef("Block %s is the genesis block, "+
 			"and does not violate finality by definition", blockHash)
 		return false, false, nil
diff --git a/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes.go b/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes.go
index 965354283..d27b92e4c 100644
--- a/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes.go
+++ b/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes.go
@@ -51,7 +51,7 @@ func (csm *consensusStateManager) calculateSelectedParentChainChanges(
 	// Walk down from the toBlockHash to the common ancestor
 	var added []*externalapi.DomainHash
 	current = toBlockHash
-	for *current != *commonAncestor {
+	for !current.Equal(commonAncestor) {
 		added = append(added, current)
 		currentGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, current)
 		if err != nil {
diff --git a/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes_test.go b/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes_test.go
index f2306e142..c90ef0680 100644
--- a/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes_test.go
+++ b/domain/consensus/processes/consensusstatemanager/find_selected_parent_chain_changes_test.go
@@ -35,7 +35,7 @@ func TestCalculateSelectedParentChainChanges(t *testing.T) {
 			t.Fatalf("The `added` slice contains an unexpected amount of items after inserting block A. "+
 				"Want: %d, got: %d", 1, len(blockASelectedParentChainChanges.Added))
 		}
-		if *blockASelectedParentChainChanges.Added[0] != *blockAHash {
+		if !blockASelectedParentChainChanges.Added[0].Equal(blockAHash) {
 			t.Fatalf("The `added` slice contains an unexpected hash. Want: %s, got: %s",
 				blockAHash, blockASelectedParentChainChanges.Added[0])
 		}
@@ -53,7 +53,7 @@ func TestCalculateSelectedParentChainChanges(t *testing.T) {
 		}
 		virtualSelectedParent := virtualGHOSTDAGData.SelectedParent()
 		notVirtualSelectedParent := blockAHash
-		if *virtualSelectedParent == *blockAHash {
+		if virtualSelectedParent.Equal(blockAHash) {
 			notVirtualSelectedParent = blockBHash
 		}
 
@@ -71,7 +71,7 @@ func TestCalculateSelectedParentChainChanges(t *testing.T) {
 			t.Fatalf("The `removed` slice contains an unexpected amount of items after inserting block C. "+
 				"Want: %d, got: %d", 1, len(blockCSelectedParentChainChanges.Removed))
 		}
-		if *blockCSelectedParentChainChanges.Removed[0] != *virtualSelectedParent {
+		if !blockCSelectedParentChainChanges.Removed[0].Equal(virtualSelectedParent) {
 			t.Fatalf("The `removed` slice contains an unexpected hash. "+
 				"Want: %s, got: %s", virtualSelectedParent, blockCSelectedParentChainChanges.Removed[0])
 		}
@@ -82,11 +82,11 @@ func TestCalculateSelectedParentChainChanges(t *testing.T) {
 			t.Fatalf("The `added` slice contains an unexpected amount of items after inserting block C. "+
 				"Want: %d, got: %d", 2, len(blockCSelectedParentChainChanges.Added))
 		}
-		if *blockCSelectedParentChainChanges.Added[0] != *notVirtualSelectedParent {
+		if !blockCSelectedParentChainChanges.Added[0].Equal(notVirtualSelectedParent) {
 			t.Fatalf("The `added` slice contains an unexpected hash as the first item. "+
 				"Want: %s, got: %s", notVirtualSelectedParent, blockCSelectedParentChainChanges.Added[0])
 		}
-		if *blockCSelectedParentChainChanges.Added[1] != *blockCHash {
+		if !blockCSelectedParentChainChanges.Added[1].Equal(blockCHash) {
 			t.Fatalf("The `added` slice contains an unexpected hash as the second item. "+
 				"Want: %s, got: %s", blockCHash, blockCSelectedParentChainChanges.Added[1])
 		}
diff --git a/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go b/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
index 103da8975..3746f2422 100644
--- a/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
+++ b/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
@@ -109,7 +109,7 @@ func (csm *consensusStateManager) selectVirtualSelectedParent(
 
 			// remove virtual from parentChildren if it's there
 			for i, parentChild := range parentChildren {
-				if *parentChild == *model.VirtualBlockHash {
+				if parentChild.Equal(model.VirtualBlockHash) {
 					parentChildren = append(parentChildren[:i], parentChildren[i+1:]...)
 					break
 				}
diff --git a/domain/consensus/processes/consensusstatemanager/resolve_block_status.go b/domain/consensus/processes/consensusstatemanager/resolve_block_status.go
index 6f28d7312..8123aa690 100644
--- a/domain/consensus/processes/consensusstatemanager/resolve_block_status.go
+++ b/domain/consensus/processes/consensusstatemanager/resolve_block_status.go
@@ -70,7 +70,7 @@ func (csm *consensusStateManager) findSelectedParentStatus(unverifiedBlocks []*e
 	defer log.Tracef("findSelectedParentStatus end")
 
 	lastUnverifiedBlock := unverifiedBlocks[len(unverifiedBlocks)-1]
-	if *lastUnverifiedBlock == *csm.genesisHash {
+	if lastUnverifiedBlock.Equal(csm.genesisHash) {
 		log.Tracef("the most recent unverified block is the genesis block, "+
 			"which by definition has status: %s", externalapi.StatusUTXOValid)
 		return externalapi.StatusUTXOValid, nil
@@ -173,7 +173,7 @@ func (csm *consensusStateManager) removeAncestorsFromVirtualDiffParentsAndAssign
 	log.Tracef("removeAncestorsFromVirtualDiffParentsAndAssignDiffChild start for block %s", blockHash)
 	defer log.Tracef("removeAncestorsFromVirtualDiffParentsAndAssignDiffChild end for block %s", blockHash)
 
-	if *blockHash == *csm.genesisHash {
+	if blockHash.Equal(csm.genesisHash) {
 		log.Tracef("Genesis block doesn't have ancestors to remove from the virtual diff parents")
 		return nil
 	}
@@ -184,7 +184,7 @@ func (csm *consensusStateManager) removeAncestorsFromVirtualDiffParentsAndAssign
 	}
 
 	for _, virtualDiffParent := range virtualDiffParents {
-		if *virtualDiffParent == *blockHash {
+		if virtualDiffParent.Equal(blockHash) {
 			log.Tracef("Skipping updating virtual diff parent %s "+
 				"because it was updated before.", virtualDiffParent)
 			continue
diff --git a/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go b/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go
index 52dc97612..77f038e27 100644
--- a/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go
+++ b/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go
@@ -56,7 +56,7 @@ func TestDoubleSpends(t *testing.T) {
 		spendingTransaction2.Outputs[0].Value-- // tweak the value to create a different ID
 		spendingTransaction1ID := consensushashing.TransactionID(spendingTransaction1)
 		spendingTransaction2ID := consensushashing.TransactionID(spendingTransaction2)
-		if *spendingTransaction1ID == *spendingTransaction2ID {
+		if spendingTransaction1ID.Equal(spendingTransaction2ID) {
 			t.Fatalf("spendingTransaction1 and spendingTransaction2 ids are equal")
 		}
 
diff --git a/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go b/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go
index 87847d699..a01c05672 100644
--- a/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go
+++ b/domain/consensus/processes/consensusstatemanager/update_pruning_utxo_set.go
@@ -63,7 +63,7 @@ func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalap
 	log.Debugf("The UTXO commitment of the pruning point: %s",
 		newPruningPointHeader.UTXOCommitment)
 
-	if newPruningPointHeader.UTXOCommitment != *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())
 	}
diff --git a/domain/consensus/processes/consensusstatemanager/update_virtual.go b/domain/consensus/processes/consensusstatemanager/update_virtual.go
index 1d80d2458..9d50dc8e5 100644
--- a/domain/consensus/processes/consensusstatemanager/update_virtual.go
+++ b/domain/consensus/processes/consensusstatemanager/update_virtual.go
@@ -13,7 +13,7 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain
 
 	log.Tracef("Saving a reference to the GHOSTDAG data of the old virtual")
 	var oldVirtualSelectedParent *externalapi.DomainHash
-	if *newBlockHash != *csm.genesisHash {
+	if !newBlockHash.Equal(csm.genesisHash) {
 		oldVirtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, model.VirtualBlockHash)
 		if err != nil {
 			return nil, err
@@ -65,7 +65,7 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain
 
 	log.Tracef("Calculating selected parent chain changes")
 	var selectedParentChainChanges *externalapi.SelectedParentChainChanges
-	if *newBlockHash != *csm.genesisHash {
+	if !newBlockHash.Equal(csm.genesisHash) {
 		newVirtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, model.VirtualBlockHash)
 		if err != nil {
 			return nil, err
diff --git a/domain/consensus/processes/consensusstatemanager/utxo_diffs.go b/domain/consensus/processes/consensusstatemanager/utxo_diffs.go
index 564ca9fa2..4ba8d5325 100644
--- a/domain/consensus/processes/consensusstatemanager/utxo_diffs.go
+++ b/domain/consensus/processes/consensusstatemanager/utxo_diffs.go
@@ -29,7 +29,7 @@ func (csm *consensusStateManager) addToVirtualDiffParents(blockHash *externalapi
 	defer log.Tracef("addToVirtualDiffParents end for block %s", blockHash)
 
 	var oldVirtualDiffParents []*externalapi.DomainHash
-	if *blockHash != *csm.genesisHash {
+	if !blockHash.Equal(csm.genesisHash) {
 		var err error
 		oldVirtualDiffParents, err = csm.consensusStateStore.VirtualDiffParents(csm.databaseContext)
 		if err != nil {
@@ -39,7 +39,7 @@ func (csm *consensusStateManager) addToVirtualDiffParents(blockHash *externalapi
 
 	isInVirtualDiffParents := false
 	for _, diffParent := range oldVirtualDiffParents {
-		if *diffParent == *blockHash {
+		if diffParent.Equal(blockHash) {
 			isInVirtualDiffParents = true
 			break
 		}
@@ -67,7 +67,7 @@ func (csm *consensusStateManager) removeFromVirtualDiffParents(blockHash *extern
 
 	newVirtualDiffParents := make([]*externalapi.DomainHash, 0, len(oldVirtualDiffParents)-1)
 	for _, diffParent := range oldVirtualDiffParents {
-		if *diffParent != *blockHash {
+		if !diffParent.Equal(blockHash) {
 			newVirtualDiffParents = append(newVirtualDiffParents, diffParent)
 		}
 	}
diff --git a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
index c5a0eef2c..b4ecd098e 100644
--- a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
+++ b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
@@ -103,7 +103,7 @@ func (csm *consensusStateManager) validateAcceptedIDMerkleRoot(block *externalap
 	defer log.Tracef("validateAcceptedIDMerkleRoot end for block %s", blockHash)
 
 	calculatedAcceptedIDMerkleRoot := calculateAcceptedIDMerkleRoot(acceptanceData)
-	if block.Header.AcceptedIDMerkleRoot != *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)
@@ -119,7 +119,7 @@ func (csm *consensusStateManager) validateUTXOCommitment(
 	defer log.Tracef("validateUTXOCommitment end for block %s", blockHash)
 
 	multisetHash := multiset.Hash()
-	if block.Header.UTXOCommitment != *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)
 	}
@@ -173,7 +173,7 @@ func (csm *consensusStateManager) validateCoinbaseTransaction(blockHash *externa
 	log.Tracef("given coinbase hash: %s, expected coinbase hash: %s",
 		coinbaseTransactionHash, expectedCoinbaseTransactionHash)
 
-	if *coinbaseTransactionHash != *expectedCoinbaseTransactionHash {
+	if !coinbaseTransactionHash.Equal(expectedCoinbaseTransactionHash) {
 		return errors.Wrap(ruleerrors.ErrBadCoinbaseTransaction, "coinbase transaction is not built as expected")
 	}
 
diff --git a/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go b/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go
index 245fbfb13..e5c69cd6c 100644
--- a/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go
+++ b/domain/consensus/processes/dagtopologymanager/dagtopologymanager.go
@@ -99,7 +99,7 @@ func (dtm *dagTopologyManager) IsInSelectedParentChainOf(blockHashA *externalapi
 
 func isHashInSlice(hash *externalapi.DomainHash, hashes []*externalapi.DomainHash) bool {
 	for _, h := range hashes {
-		if *h == *hash {
+		if h.Equal(hash) {
 			return true
 		}
 	}
@@ -128,7 +128,7 @@ func (dtm *dagTopologyManager) SetParents(blockHash *externalapi.DomainHash, par
 				return err
 			}
 			for i, parentChild := range parentRelations.Children {
-				if *parentChild == *blockHash {
+				if parentChild.Equal(blockHash) {
 					parentRelations.Children = append(parentRelations.Children[:i], parentRelations.Children[i+1:]...)
 					dtm.blockRelationStore.StageBlockRelation(currentParent, parentRelations)
 					break
@@ -145,7 +145,7 @@ func (dtm *dagTopologyManager) SetParents(blockHash *externalapi.DomainHash, par
 		}
 		isBlockAlreadyInChildren := false
 		for _, parentChild := range parentRelations.Children {
-			if *parentChild == *blockHash {
+			if parentChild.Equal(blockHash) {
 				isBlockAlreadyInChildren = true
 				break
 			}
@@ -180,7 +180,7 @@ func (dtm *dagTopologyManager) ChildInSelectedParentChainOf(
 		selectedParent := ghostdagData.SelectedParent()
 
 		// In case where `blockHash` is an immediate parent of `highHash`
-		if *blockHash == *selectedParent {
+		if blockHash.Equal(selectedParent) {
 			return highHash, nil
 		}
 		highHash = selectedParent
diff --git a/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go b/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go
index cdce9fd6a..9db1f12b3 100644
--- a/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go
+++ b/domain/consensus/processes/dagtraversalmanager/selected_child_iterator.go
@@ -20,7 +20,7 @@ func (s *selectedChildIterator) Next() bool {
 	}
 
 	for _, child := range children {
-		if *child == *model.VirtualBlockHash {
+		if child.Equal(model.VirtualBlockHash) {
 			continue
 		}
 
diff --git a/domain/consensus/processes/finalitymanager/finality_manager.go b/domain/consensus/processes/finalitymanager/finality_manager.go
index b5fb1ba29..67cf5b811 100644
--- a/domain/consensus/processes/finalitymanager/finality_manager.go
+++ b/domain/consensus/processes/finalitymanager/finality_manager.go
@@ -51,7 +51,7 @@ func (fm *finalityManager) VirtualFinalityPoint() (*externalapi.DomainHash, erro
 func (fm *finalityManager) FinalityPoint(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
 	log.Tracef("FinalityPoint start")
 	defer log.Tracef("FinalityPoint end")
-	if *blockHash == *model.VirtualBlockHash {
+	if blockHash.Equal(model.VirtualBlockHash) {
 		return fm.VirtualFinalityPoint()
 	}
 	finalityPoint, err := fm.finalityStore.FinalityPoint(fm.databaseContext, blockHash)
@@ -88,7 +88,7 @@ func (fm *finalityManager) calculateFinalityPoint(blockHash *externalapi.DomainH
 	}
 
 	selectedParent := ghostdagData.SelectedParent()
-	if *selectedParent == *fm.genesisHash {
+	if selectedParent.Equal(fm.genesisHash) {
 		return fm.genesisHash, nil
 	}
 
diff --git a/domain/consensus/processes/ghostdag2/ghostdagimpl.go b/domain/consensus/processes/ghostdag2/ghostdagimpl.go
index 6a84f7b70..ec2ad1ef1 100644
--- a/domain/consensus/processes/ghostdag2/ghostdagimpl.go
+++ b/domain/consensus/processes/ghostdag2/ghostdagimpl.go
@@ -92,7 +92,7 @@ func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error
 	}
 
 	for _, mergeSetBlock := range mergeSetArr {
-		if *mergeSetBlock == *selectedParent {
+		if mergeSetBlock.Equal(selectedParent) {
 			if !contains(selectedParent, mergeSetBlues) {
 				mergeSetBlues = append(mergeSetBlues, selectedParent)
 				blueSet = append(blueSet, selectedParent)
@@ -252,7 +252,7 @@ func (gh *ghostdagHelper) validateKCluster(chain *externalapi.DomainHash, checke
 /*----------------contains-------------------------- */
 func contains(item *externalapi.DomainHash, items []*externalapi.DomainHash) bool {
 	for _, r := range items {
-		if *r == *item {
+		if r.Equal(item) {
 			return true
 		}
 	}
@@ -294,7 +294,7 @@ func (gh *ghostdagHelper) findMergeSet(parents []*externalapi.DomainHash, select
 	for len(blockQueue) > 0 {
 		block := blockQueue[0]
 		blockQueue = blockQueue[1:]
-		if *selectedParent == *block {
+		if selectedParent.Equal(block) {
 			if !contains(block, allMergeSet) {
 				allMergeSet = append(allMergeSet, block)
 			}
diff --git a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
index a2d248cd1..08f64b32c 100644
--- a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
+++ b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go
@@ -137,7 +137,7 @@ func TestGHOSTDAG(t *testing.T) {
 							factory.implName, info.Name(), testBlockData.ID, testBlockData.Score, ghostdagData.BlueScore())
 					}
 
-					if *StringToByte(testBlockData.SelectedParent) != *ghostdagData.SelectedParent() {
+					if !StringToByte(testBlockData.SelectedParent).Equal(ghostdagData.SelectedParent()) {
 						t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected selected parent %v but got %v.",
 							factory.implName, info.Name(), testBlockData.ID, testBlockData.SelectedParent, string(ghostdagData.SelectedParent()[:]))
 					}
@@ -267,7 +267,7 @@ func (dt *DAGTopologyManagerImpl) IsAncestorOf(hashBlockA *externalapi.DomainHas
 	}
 
 	for _, parentOfB := range blockBParents {
-		if *parentOfB == *hashBlockA {
+		if parentOfB.Equal(hashBlockA) {
 			return true, nil
 		}
 	}
diff --git a/domain/consensus/processes/ghostdagmanager/mergeset.go b/domain/consensus/processes/ghostdagmanager/mergeset.go
index ead66812f..4668eb127 100644
--- a/domain/consensus/processes/ghostdagmanager/mergeset.go
+++ b/domain/consensus/processes/ghostdagmanager/mergeset.go
@@ -14,7 +14,7 @@ func (gm *ghostdagManager) mergeSetWithoutSelectedParent(selecteParent *external
 	queue := []*externalapi.DomainHash{}
 	// Queueing all parents (other than the selected parent itself) for processing.
 	for _, parent := range blockParents {
-		if *parent == *selecteParent {
+		if parent.Equal(selecteParent) {
 			continue
 		}
 		mergeSetMap[*parent] = struct{}{}
diff --git a/domain/consensus/processes/headersselectedtipmanager/headertipsmanager.go b/domain/consensus/processes/headersselectedtipmanager/headertipsmanager.go
index 4ff750563..f4bb6f16b 100644
--- a/domain/consensus/processes/headersselectedtipmanager/headertipsmanager.go
+++ b/domain/consensus/processes/headersselectedtipmanager/headertipsmanager.go
@@ -46,7 +46,7 @@ func (h *headerTipsManager) AddHeaderTip(hash *externalapi.DomainHash) error {
 			return err
 		}
 
-		if *newHeadersSelectedTip != *headersSelectedTip {
+		if !newHeadersSelectedTip.Equal(headersSelectedTip) {
 			h.headersSelectedTipStore.Stage(newHeadersSelectedTip)
 		}
 	}
diff --git a/domain/consensus/processes/pruningmanager/pruningmanager.go b/domain/consensus/processes/pruningmanager/pruningmanager.go
index 11a0e7431..1c87d7a81 100644
--- a/domain/consensus/processes/pruningmanager/pruningmanager.go
+++ b/domain/consensus/processes/pruningmanager/pruningmanager.go
@@ -124,7 +124,7 @@ func (pm *pruningManager) UpdatePruningPointByVirtual() error {
 		return err
 	}
 
-	if *newPruningPoint != *currentP {
+	if !newPruningPoint.Equal(currentP) {
 		err = pm.savePruningPoint(newPruningPoint)
 		if err != nil {
 			return err
diff --git a/domain/consensus/processes/reachabilitymanager/reachability_external_test.go b/domain/consensus/processes/reachabilitymanager/reachability_external_test.go
index 8d2674955..01f0aaac6 100644
--- a/domain/consensus/processes/reachabilitymanager/reachability_external_test.go
+++ b/domain/consensus/processes/reachabilitymanager/reachability_external_test.go
@@ -26,7 +26,7 @@ func TestAddChildThatPointsDirectlyToTheSelectedParentChainBelowReindexRoot(t *t
 			t.Fatalf("ReachabilityReindexRoot: %s", err)
 		}
 
-		if *reindexRoot != *params.GenesisHash {
+		if !reindexRoot.Equal(params.GenesisHash) {
 			t.Fatalf("reindex root is expected to initially be genesis")
 		}
 
@@ -52,7 +52,7 @@ func TestAddChildThatPointsDirectlyToTheSelectedParentChainBelowReindexRoot(t *t
 			t.Fatalf("ReachabilityReindexRoot: %s", err)
 		}
 
-		if *newReindexRoot == *reindexRoot {
+		if newReindexRoot.Equal(reindexRoot) {
 			t.Fatalf("reindex root is expected to change")
 		}
 
@@ -114,7 +114,7 @@ func TestUpdateReindexRoot(t *testing.T) {
 				t.Fatalf("ReachabilityReindexRoot: %s", err)
 			}
 
-			if *reindexRoot != *params.GenesisHash {
+			if !reindexRoot.Equal(params.GenesisHash) {
 				t.Fatalf("reindex root unexpectedly moved")
 			}
 		}
@@ -131,7 +131,7 @@ func TestUpdateReindexRoot(t *testing.T) {
 			t.Fatalf("ReachabilityReindexRoot: %s", err)
 		}
 
-		if *reindexRoot != *chain1RootBlock {
+		if !reindexRoot.Equal(chain1RootBlock) {
 			t.Fatalf("chain1RootBlock is not the reindex root after reindex")
 		}
 
@@ -207,7 +207,7 @@ func TestReindexIntervalsEarlierThanReindexRoot(t *testing.T) {
 			t.Fatalf("ReachabilityReindexRoot: %s", err)
 		}
 
-		if *reindexRoot != *centerBlock {
+		if !reindexRoot.Equal(centerBlock) {
 			t.Fatalf("centerBlock is not the reindex root after reindex")
 		}
 
diff --git a/domain/consensus/processes/reachabilitymanager/reachability_test.go b/domain/consensus/processes/reachabilitymanager/reachability_test.go
index 448ebb031..0e7f36af7 100644
--- a/domain/consensus/processes/reachabilitymanager/reachability_test.go
+++ b/domain/consensus/processes/reachabilitymanager/reachability_test.go
@@ -950,7 +950,7 @@ func BenchmarkReindexInterval(b *testing.B) {
 			currentTreeNode = childTreeNode
 		}
 
-		originalRemainingInterval := *helper.remainingIntervalAfter(root)
+		originalRemainingInterval := helper.remainingIntervalAfter(root).Clone()
 		// After we added subTreeSize nodes, adding the next
 		// node should lead to a reindex from root.
 		fullReindexTriggeringNode := helper.newNode()
@@ -961,7 +961,7 @@ func BenchmarkReindexInterval(b *testing.B) {
 			b.Fatalf("addChild: %s", err)
 		}
 
-		if *helper.remainingIntervalAfter(root) == originalRemainingInterval {
+		if helper.remainingIntervalAfter(root).Equal(originalRemainingInterval) {
 			b.Fatal("Expected a reindex from root, but it didn't happen")
 		}
 	}
diff --git a/domain/consensus/processes/reachabilitymanager/tree.go b/domain/consensus/processes/reachabilitymanager/tree.go
index 054b3836e..2e2f3398c 100644
--- a/domain/consensus/processes/reachabilitymanager/tree.go
+++ b/domain/consensus/processes/reachabilitymanager/tree.go
@@ -317,7 +317,7 @@ func (rt *reachabilityManager) countSubtrees(node *externalapi.DomainHash, subTr
 
 		// We reached a leaf or a pre-calculated subtree.
 		// Push information up
-		for *current != *node {
+		for !current.Equal(node) {
 			current, err = rt.parent(current)
 			if err != nil {
 				return err
@@ -461,7 +461,7 @@ func (rt *reachabilityManager) reclaimIntervalBeforeChosenChild(rtn, commonAnces
 				return err
 			}
 
-			if currentHasSlackIntervalBefore || *current == *reindexRoot {
+			if currentHasSlackIntervalBefore || current.Equal(reindexRoot) {
 				break
 			}
 
@@ -471,7 +471,7 @@ func (rt *reachabilityManager) reclaimIntervalBeforeChosenChild(rtn, commonAnces
 			}
 		}
 
-		if *current == *reindexRoot {
+		if current.Equal(reindexRoot) {
 			// "Deallocate" an interval of slackReachabilityIntervalForReclaiming
 			// from this node. This is the interval that we'll use for the new
 			// node.
@@ -505,7 +505,7 @@ func (rt *reachabilityManager) reclaimIntervalBeforeChosenChild(rtn, commonAnces
 	// current node with an interval that is smaller by
 	// slackReachabilityIntervalForReclaiming. This is to make room
 	// for the new node.
-	for *current != *commonAncestor {
+	for !current.Equal(commonAncestor) {
 		currentInterval, err := rt.interval(current)
 		if err != nil {
 			return err
@@ -583,7 +583,7 @@ func (rt *reachabilityManager) reclaimIntervalAfterChosenChild(node, commonAnces
 				return err
 			}
 
-			if currentHasSlackIntervalAfter || *current == *reindexRoot {
+			if currentHasSlackIntervalAfter || current.Equal(reindexRoot) {
 				break
 			}
 
@@ -593,7 +593,7 @@ func (rt *reachabilityManager) reclaimIntervalAfterChosenChild(node, commonAnces
 			}
 		}
 
-		if *current == *reindexRoot {
+		if current.Equal(reindexRoot) {
 			// "Deallocate" an interval of slackReachabilityIntervalForReclaiming
 			// from this node. This is the interval that we'll use for the new
 			// node.
@@ -627,7 +627,7 @@ func (rt *reachabilityManager) reclaimIntervalAfterChosenChild(node, commonAnces
 	// current node with an interval that is smaller by
 	// slackReachabilityIntervalForReclaiming. This is to make room
 	// for the new node.
-	for *current != *commonAncestor {
+	for !current.Equal(commonAncestor) {
 		currentInterval, err := rt.interval(current)
 		if err != nil {
 			return err
@@ -883,7 +883,7 @@ func (rt *reachabilityManager) splitChildrenAroundChild(node, child *externalapi
 	}
 
 	for i, candidateChild := range nodeChildren {
-		if *candidateChild == *child {
+		if candidateChild.Equal(child) {
 			return nodeChildren[:i], nodeChildren[i+1:], nil
 		}
 	}
diff --git a/domain/consensus/processes/syncmanager/syncinfo.go b/domain/consensus/processes/syncmanager/syncinfo.go
index 90a5009d8..f62c10a96 100644
--- a/domain/consensus/processes/syncmanager/syncinfo.go
+++ b/domain/consensus/processes/syncmanager/syncinfo.go
@@ -36,7 +36,7 @@ func (sm *syncManager) isAwaitingUTXOSet() (isAwaitingUTXOSet bool, ibdRootUTXOB
 
 	// If the pruning point by headers is different from the current point
 	// it means we need to request the new pruning point UTXO set.
-	if *pruningPoint != *pruningPointByHeaders {
+	if !pruningPoint.Equal(pruningPointByHeaders) {
 		return true, pruningPointByHeaders, nil
 	}
 
diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go b/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go
index eee4d8168..9d0ecb65e 100644
--- a/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go
+++ b/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go
@@ -128,7 +128,7 @@ func (v *transactionValidator) checkCoinbaseLength(tx *externalapi.DomainTransac
 func (v *transactionValidator) checkTransactionPayloadHash(tx *externalapi.DomainTransaction) error {
 	if tx.SubnetworkID != subnetworks.SubnetworkIDNative {
 		payloadHash := hashes.PayloadHash(tx.Payload)
-		if tx.PayloadHash != *payloadHash {
+		if !tx.PayloadHash.Equal(payloadHash) {
 			return errors.Wrapf(ruleerrors.ErrInvalidPayloadHash, "invalid payload hash")
 		}
 	} else if tx.PayloadHash != (externalapi.DomainHash{}) {
@@ -177,7 +177,7 @@ func (v *transactionValidator) checkTransactionSubnetwork(tx *externalapi.Domain
 	// If we are a partial node, only transactions on built in subnetworks
 	// or our own subnetwork may have a payload
 	isLocalNodeFull := localNodeSubnetworkID == nil
-	shouldTxBeFull := subnetworks.IsBuiltIn(tx.SubnetworkID) || subnetworks.IsEqual(&tx.SubnetworkID, localNodeSubnetworkID)
+	shouldTxBeFull := subnetworks.IsBuiltIn(tx.SubnetworkID) || tx.SubnetworkID.Equal(localNodeSubnetworkID)
 	if !isLocalNodeFull && !shouldTxBeFull && len(tx.Payload) > 0 {
 		return errors.Wrapf(ruleerrors.ErrInvalidPayload,
 			"transaction that was expected to be partial has a payload "+
diff --git a/domain/consensus/utils/hashes/to_big.go b/domain/consensus/utils/hashes/to_big.go
index 510181660..25077c69a 100644
--- a/domain/consensus/utils/hashes/to_big.go
+++ b/domain/consensus/utils/hashes/to_big.go
@@ -10,7 +10,7 @@ import (
 func ToBig(hash *externalapi.DomainHash) *big.Int {
 	// A Hash is in little-endian, but the big package wants the bytes in
 	// big-endian, so reverse them.
-	buf := *hash
+	buf := hash.Clone()
 	blen := len(buf)
 	for i := 0; i < blen/2; i++ {
 		buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
diff --git a/domain/consensus/utils/subnetworks/compare.go b/domain/consensus/utils/subnetworks/compare.go
index af1a06358..12cda20da 100644
--- a/domain/consensus/utils/subnetworks/compare.go
+++ b/domain/consensus/utils/subnetworks/compare.go
@@ -21,14 +21,3 @@ func cmp(a, b externalapi.DomainSubnetworkID) int {
 func Less(a, b externalapi.DomainSubnetworkID) bool {
 	return cmp(a, b) < 0
 }
-
-// IsEqual returns true if a and b are equal or both nil
-func IsEqual(a, b *externalapi.DomainSubnetworkID) bool {
-	if a == nil && b == nil {
-		return true
-	}
-	if a == nil || b == nil {
-		return false
-	}
-	return *a == *b
-}
diff --git a/domain/consensus/utils/utxo/utxo_entry.go b/domain/consensus/utils/utxo/utxo_entry.go
index 68b63793e..b6cae4bab 100644
--- a/domain/consensus/utils/utxo/utxo_entry.go
+++ b/domain/consensus/utils/utxo/utxo_entry.go
@@ -1,6 +1,9 @@
 package utxo
 
-import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+import (
+	"bytes"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+)
 
 type utxoEntry struct {
 	amount          uint64
@@ -38,3 +41,36 @@ func (u *utxoEntry) BlockBlueScore() uint64 {
 func (u *utxoEntry) IsCoinbase() bool {
 	return u.isCoinbase
 }
+
+// Equal returns whether entry equals to other
+func (u *utxoEntry) Equal(other externalapi.UTXOEntry) bool {
+	if u == nil || other == nil {
+		return u == other
+	}
+
+	// If only the underlying value of other is nil it'll
+	// make `other == nil` return false, so we check it
+	// explicitly.
+	downcastedOther := other.(*utxoEntry)
+	if u == nil || downcastedOther == nil {
+		return u == downcastedOther
+	}
+
+	if u.Amount() != other.Amount() {
+		return false
+	}
+
+	if !bytes.Equal(u.ScriptPublicKey(), other.ScriptPublicKey()) {
+		return false
+	}
+
+	if u.BlockBlueScore() != other.BlockBlueScore() {
+		return false
+	}
+
+	if u.IsCoinbase() != other.IsCoinbase() {
+		return false
+	}
+
+	return true
+}
diff --git a/domain/consensus/utils/utxo/utxo_entry_test.go b/domain/consensus/utils/utxo/utxo_entry_test.go
new file mode 100644
index 000000000..69b90277b
--- /dev/null
+++ b/domain/consensus/utils/utxo/utxo_entry_test.go
@@ -0,0 +1,113 @@
+package utxo
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"testing"
+)
+
+func TestUTXOEntry_Equal(t *testing.T) {
+	type testUTXOEntryToCompare struct {
+		utxoEntry      *utxoEntry
+		expectedResult bool
+	}
+
+	tests := []struct {
+		baseUTXOEntry        *utxoEntry
+		UTXOEntryToCompareTo []testUTXOEntryToCompare
+	}{
+		{
+			baseUTXOEntry: nil,
+			UTXOEntryToCompareTo: []testUTXOEntryToCompare{
+				{
+					utxoEntry:      nil,
+					expectedResult: true,
+				},
+				{
+					utxoEntry: &utxoEntry{
+						0xFFFF,
+						[]byte{0xA1, 0xA2, 0xA3},
+						0xFFFF,
+						false,
+					},
+					expectedResult: false,
+				},
+			},
+		}, {
+			baseUTXOEntry: &utxoEntry{
+				0xFFFF,
+				[]byte{0xA1, 0xA2, 0xA3},
+				0xFFFF,
+				true,
+			},
+			UTXOEntryToCompareTo: []testUTXOEntryToCompare{
+				{
+					utxoEntry: &utxoEntry{
+						0xFFFF,
+						[]byte{0xA1, 0xA2, 0xA3},
+						0xFFFF,
+						true,
+					},
+					expectedResult: true,
+				},
+				{
+					utxoEntry:      nil,
+					expectedResult: false,
+				},
+				{
+					utxoEntry: &utxoEntry{
+						0xFFFF,
+						[]byte{0xA1, 0xA0, 0xA3}, // Changed
+						0xFFFF,
+						true,
+					},
+					expectedResult: false,
+				},
+				{
+					utxoEntry: &utxoEntry{
+						0xFFFF,
+						[]byte{0xA1, 0xA2, 0xA3},
+						0xFFFF,
+						false, // Changed
+					},
+					expectedResult: false,
+				},
+				{
+					utxoEntry: &utxoEntry{
+						0xFFFF,
+						[]byte{0xA1, 0xA2, 0xA3},
+						0xFFF0, // Changed
+						true,
+					},
+					expectedResult: false,
+				},
+				{
+					utxoEntry:      nil,
+					expectedResult: false,
+				},
+				{
+					utxoEntry: &utxoEntry{
+						0xFFF0, // Changed
+						[]byte{0xA1, 0xA2, 0xA3},
+						0xFFFF,
+						true,
+					},
+					expectedResult: false,
+				},
+			},
+		},
+	}
+
+	for i, test := range tests {
+		for j, subTest := range test.UTXOEntryToCompareTo {
+			var base externalapi.UTXOEntry = test.baseUTXOEntry
+			result1 := base.Equal(subTest.utxoEntry)
+			if result1 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result1)
+			}
+			result2 := subTest.utxoEntry.Equal(base)
+			if result2 != subTest.expectedResult {
+				t.Fatalf("Test #%d:%d: Expected %t but got %t", i, j, subTest.expectedResult, result2)
+			}
+		}
+	}
+}
diff --git a/domain/dagconfig/genesis_test.go b/domain/dagconfig/genesis_test.go
index dd6892757..580773546 100644
--- a/domain/dagconfig/genesis_test.go
+++ b/domain/dagconfig/genesis_test.go
@@ -15,7 +15,7 @@ import (
 func TestGenesisBlock(t *testing.T) {
 	// Check hash of the block against expected hash.
 	hash := consensushashing.BlockHash(MainnetParams.GenesisBlock)
-	if *MainnetParams.GenesisHash != *hash {
+	if !MainnetParams.GenesisHash.Equal(hash) {
 		t.Fatalf("TestGenesisBlock: Genesis block hash does "+
 			"not appear valid - got %v, want %v", hash, MainnetParams.GenesisHash)
 	}
@@ -26,7 +26,7 @@ func TestGenesisBlock(t *testing.T) {
 func TestTestnetGenesisBlock(t *testing.T) {
 	// Check hash of the block against expected hash.
 	hash := consensushashing.BlockHash(TestnetParams.GenesisBlock)
-	if *TestnetParams.GenesisHash != *hash {
+	if !TestnetParams.GenesisHash.Equal(hash) {
 		t.Fatalf("TestTestnetGenesisBlock: Genesis block hash does "+
 			"not appear valid - got %v, want %v", hash,
 			TestnetParams.GenesisHash)
@@ -38,7 +38,7 @@ func TestTestnetGenesisBlock(t *testing.T) {
 func TestSimnetGenesisBlock(t *testing.T) {
 	// Check hash of the block against expected hash.
 	hash := consensushashing.BlockHash(SimnetParams.GenesisBlock)
-	if *SimnetParams.GenesisHash != *hash {
+	if !SimnetParams.GenesisHash.Equal(hash) {
 		t.Fatalf("TestSimnetGenesisBlock: Genesis block hash does "+
 			"not appear valid - got %v, want %v", hash,
 			SimnetParams.GenesisHash)
@@ -50,7 +50,7 @@ func TestSimnetGenesisBlock(t *testing.T) {
 func TestDevnetGenesisBlock(t *testing.T) {
 	// Check hash of the block against expected hash.
 	hash := consensushashing.BlockHash(DevnetParams.GenesisBlock)
-	if *DevnetParams.GenesisHash != *hash {
+	if !DevnetParams.GenesisHash.Equal(hash) {
 		t.Fatalf("TestDevnetGenesisBlock: Genesis block hash does "+
 			"not appear valid - got %v, want %v", hash,
 			DevnetParams.GenesisHash)
diff --git a/domain/dagconfig/params_test.go b/domain/dagconfig/params_test.go
index ceb1219b8..3790ed75c 100644
--- a/domain/dagconfig/params_test.go
+++ b/domain/dagconfig/params_test.go
@@ -35,7 +35,7 @@ func TestNewHashFromStr(t *testing.T) {
 
 			result := newHashFromStr(test.hexStr)
 
-			if *result != *test.expectedHash {
+			if !result.Equal(test.expectedHash) {
 				t.Errorf("%s: Expected hash: %s, but got %s", test.hexStr, test.expectedHash, result)
 			}
 		}()
diff --git a/domain/miningmanager/mempool/mempool.go b/domain/miningmanager/mempool/mempool.go
index 0cba5cb1b..40dab8a9d 100644
--- a/domain/miningmanager/mempool/mempool.go
+++ b/domain/miningmanager/mempool/mempool.go
@@ -467,10 +467,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 := *consensushashing.TransactionID(tx)
+	txID := consensushashing.TransactionID(tx)
 	for _, txIn := range tx.Inputs {
 		if txRedeemer, ok := mp.mempoolUTXOSet.poolTransactionBySpendingOutpoint(txIn.PreviousOutpoint); ok {
-			if !(*consensushashing.TransactionID(txRedeemer) == txID) {
+			if !consensushashing.TransactionID(txRedeemer).Equal(txID) {
 				err := mp.removeTransactionAndItsChainedTransactions(txRedeemer)
 				if err != nil {
 					return err
diff --git a/domain/utxoindex/store.go b/domain/utxoindex/store.go
index e09a4b1be..67d985038 100644
--- a/domain/utxoindex/store.go
+++ b/domain/utxoindex/store.go
@@ -25,7 +25,7 @@ func newUTXOIndexStore(database database.Database) *utxoIndexStore {
 	}
 }
 
-func (uis *utxoIndexStore) add(scriptPublicKey []byte, outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry) error {
+func (uis *utxoIndexStore) add(scriptPublicKey []byte, outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry) error {
 	key := ConvertScriptPublicKeyToString(scriptPublicKey)
 	log.Tracef("Adding outpoint %s:%d to scriptPublicKey %s",
 		outpoint.TransactionID, outpoint.Index, key)
@@ -52,7 +52,7 @@ func (uis *utxoIndexStore) add(scriptPublicKey []byte, outpoint *externalapi.Dom
 		return errors.Errorf("cannot add outpoint %s because it's being added already", outpoint)
 	}
 
-	toAddPairsOfKey[*outpoint] = *utxoEntry
+	toAddPairsOfKey[*outpoint] = utxoEntry
 
 	log.Tracef("Added outpoint %s:%d to scriptPublicKey %s",
 		outpoint.TransactionID, outpoint.Index, key)
diff --git a/domain/utxoindex/utxoindex.go b/domain/utxoindex/utxoindex.go
index 8e2666fe5..defa4a6bb 100644
--- a/domain/utxoindex/utxoindex.go
+++ b/domain/utxoindex/utxoindex.go
@@ -126,7 +126,7 @@ func (ui *UTXOIndex) addTransaction(transaction *externalapi.DomainTransaction,
 		log.Tracef("Adding outpoint %s:%d to UTXO index", transactionID, index)
 		outpoint := externalapi.NewDomainOutpoint(transactionID, uint32(index))
 		utxoEntry := utxo.NewUTXOEntry(transactionOutput.Value, transactionOutput.ScriptPublicKey, isCoinbase, blockBlueScore)
-		err := ui.store.add(transactionOutput.ScriptPublicKey, outpoint, &utxoEntry)
+		err := ui.store.add(transactionOutput.ScriptPublicKey, outpoint, utxoEntry)
 		if err != nil {
 			return err
 		}
@@ -148,7 +148,7 @@ func (ui *UTXOIndex) removeTransaction(transaction *externalapi.DomainTransactio
 	for _, transactionInput := range transaction.Inputs {
 		log.Tracef("Adding outpoint %s:%d to UTXO index",
 			transactionInput.PreviousOutpoint.TransactionID, transactionInput.PreviousOutpoint.Index)
-		err := ui.store.add(transactionInput.UTXOEntry.ScriptPublicKey(), &transactionInput.PreviousOutpoint, &transactionInput.UTXOEntry)
+		err := ui.store.add(transactionInput.UTXOEntry.ScriptPublicKey(), &transactionInput.PreviousOutpoint, transactionInput.UTXOEntry)
 		if err != nil {
 			return err
 		}
diff --git a/testing/integration/basic_sync_test.go b/testing/integration/basic_sync_test.go
index 0050ebd8c..60eaecfe6 100644
--- a/testing/integration/basic_sync_test.go
+++ b/testing/integration/basic_sync_test.go
@@ -38,7 +38,7 @@ func TestIntegrationBasicSync(t *testing.T) {
 	}
 
 	blockHash := consensushashing.BlockHash(block)
-	if *header.BlockHash() != *blockHash {
+	if !header.BlockHash().Equal(blockHash) {
 		t.Errorf("Expected block with hash '%s', but got '%s'", blockHash, header.BlockHash())
 	}
 
@@ -49,7 +49,7 @@ func TestIntegrationBasicSync(t *testing.T) {
 	}
 
 	blockHash = consensushashing.BlockHash(block)
-	if *header.BlockHash() != *blockHash {
+	if !header.BlockHash().Equal(blockHash) {
 		t.Errorf("Expected block with hash '%s', but got '%s'", blockHash, header.BlockHash())
 	}
 }