Repository: kaspanet/kaspad
Pull request #1167 (2020-12-01 06:32:46)

[NOD-1551] Make UTXODiff immutable + skip cloning it in datastore

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

Patch:
diff --git a/domain/consensus/database/serialization/utxo_collection.go b/domain/consensus/database/serialization/utxo_collection.go
index 81e51496..703364f7 100644
--- a/domain/consensus/database/serialization/utxo_collection.go
+++ b/domain/consensus/database/serialization/utxo_collection.go
@@ -2,32 +2,39 @@ package serialization
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
 )
 
-func utxoCollectionToDBUTXOCollection(utxoCollection model.UTXOCollection) []*DbUtxoCollectionItem {
-	items := make([]*DbUtxoCollectionItem, len(utxoCollection))
+func utxoCollectionToDBUTXOCollection(utxoCollection model.UTXOCollection) ([]*DbUtxoCollectionItem, error) {
+	items := make([]*DbUtxoCollectionItem, utxoCollection.Len())
 	i := 0
-	for outpoint, entry := range utxoCollection {
-		outpointCopy := outpoint
+	utxoIterator := utxoCollection.Iterator()
+	for utxoIterator.Next() {
+		outpoint, entry, err := utxoIterator.Get()
+		if err != nil {
+			return nil, err
+		}
+
 		items[i] = &DbUtxoCollectionItem{
-			Outpoint:  DomainOutpointToDbOutpoint(&outpointCopy),
+			Outpoint:  DomainOutpointToDbOutpoint(outpoint),
 			UtxoEntry: UTXOEntryToDBUTXOEntry(entry),
 		}
 		i++
 	}
 
-	return items
+	return items, nil
 }
 
 func dbUTXOCollectionToUTXOCollection(items []*DbUtxoCollectionItem) (model.UTXOCollection, error) {
-	collection := make(model.UTXOCollection)
+	utxoMap := make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry, len(items))
 	for _, item := range items {
 		outpoint, err := DbOutpointToDomainOutpoint(item.Outpoint)
 		if err != nil {
 			return nil, err
 		}
 
-		collection[*outpoint] = DBUTXOEntryToUTXOEntry(item.UtxoEntry)
+		utxoMap[*outpoint] = DBUTXOEntryToUTXOEntry(item.UtxoEntry)
 	}
-	return collection, nil
+	return utxo.NewUTXOCollection(utxoMap), nil
 }
diff --git a/domain/consensus/database/serialization/utxo_diff.go b/domain/consensus/database/serialization/utxo_diff.go
index 0d395fa7..ec2cd858 100644
--- a/domain/consensus/database/serialization/utxo_diff.go
+++ b/domain/consensus/database/serialization/utxo_diff.go
@@ -2,18 +2,29 @@ package serialization
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
 )
 
 // UTXODiffToDBUTXODiff converts UTXODiff to DbUtxoDiff
-func UTXODiffToDBUTXODiff(diff *model.UTXODiff) *DbUtxoDiff {
-	return &DbUtxoDiff{
-		ToAdd:    utxoCollectionToDBUTXOCollection(diff.ToAdd),
-		ToRemove: utxoCollectionToDBUTXOCollection(diff.ToRemove),
+func UTXODiffToDBUTXODiff(diff model.UTXODiff) (*DbUtxoDiff, error) {
+	toAdd, err := utxoCollectionToDBUTXOCollection(diff.ToAdd())
+	if err != nil {
+		return nil, err
+	}
+
+	toRemove, err := utxoCollectionToDBUTXOCollection(diff.ToRemove())
+	if err != nil {
+		return nil, err
 	}
+
+	return &DbUtxoDiff{
+		ToAdd:    toAdd,
+		ToRemove: toRemove,
+	}, nil
 }
 
 // DBUTXODiffToUTXODiff converts DbUtxoDiff to UTXODiff
-func DBUTXODiffToUTXODiff(diff *DbUtxoDiff) (*model.UTXODiff, error) {
+func DBUTXODiffToUTXODiff(diff *DbUtxoDiff) (model.UTXODiff, error) {
 	toAdd, err := dbUTXOCollectionToUTXOCollection(diff.ToAdd)
 	if err != nil {
 		return nil, err
@@ -24,8 +35,5 @@ func DBUTXODiffToUTXODiff(diff *DbUtxoDiff) (*model.UTXODiff, error) {
 		return nil, err
 	}
 
-	return &model.UTXODiff{
-		ToAdd:    toAdd,
-		ToRemove: toRemove,
-	}, nil
+	return utxo.NewUTXODiffFromCollections(toAdd, toRemove)
 }
diff --git a/domain/consensus/database/serialization/utxo_entry.go b/domain/consensus/database/serialization/utxo_entry.go
index 4a17f6ee..f8ec8587 100644
--- a/domain/consensus/database/serialization/utxo_entry.go
+++ b/domain/consensus/database/serialization/utxo_entry.go
@@ -2,24 +2,20 @@ package serialization
 
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
 )
 
 // UTXOEntryToDBUTXOEntry converts UTXOEntry to DbUtxoEntry
-func UTXOEntryToDBUTXOEntry(utxoEntry *externalapi.UTXOEntry) *DbUtxoEntry {
+func UTXOEntryToDBUTXOEntry(utxoEntry externalapi.UTXOEntry) *DbUtxoEntry {
 	return &DbUtxoEntry{
-		Amount:          utxoEntry.Amount,
-		ScriptPublicKey: utxoEntry.ScriptPublicKey,
-		BlockBlueScore:  utxoEntry.BlockBlueScore,
-		IsCoinbase:      utxoEntry.IsCoinbase,
+		Amount:          utxoEntry.Amount(),
+		ScriptPublicKey: utxoEntry.ScriptPublicKey(),
+		BlockBlueScore:  utxoEntry.BlockBlueScore(),
+		IsCoinbase:      utxoEntry.IsCoinbase(),
 	}
 }
 
 // DBUTXOEntryToUTXOEntry convert DbUtxoEntry ro UTXOEntry
-func DBUTXOEntryToUTXOEntry(dbUtxoEntry *DbUtxoEntry) *externalapi.UTXOEntry {
-	return &externalapi.UTXOEntry{
-		Amount:          dbUtxoEntry.Amount,
-		ScriptPublicKey: dbUtxoEntry.ScriptPublicKey,
-		BlockBlueScore:  dbUtxoEntry.BlockBlueScore,
-		IsCoinbase:      dbUtxoEntry.IsCoinbase,
-	}
+func DBUTXOEntryToUTXOEntry(dbUtxoEntry *DbUtxoEntry) externalapi.UTXOEntry {
+	return utxo.NewUTXOEntry(dbUtxoEntry.Amount, dbUtxoEntry.ScriptPublicKey, dbUtxoEntry.IsCoinbase, dbUtxoEntry.BlockBlueScore)
 }
diff --git a/domain/consensus/datastructures/utxodiffstore/utxodiffstore.go b/domain/consensus/datastructures/utxodiffstore/utxodiffstore.go
index ea0b14dc..60d5e55e 100644
--- a/domain/consensus/datastructures/utxodiffstore/utxodiffstore.go
+++ b/domain/consensus/datastructures/utxodiffstore/utxodiffstore.go
@@ -15,7 +15,7 @@ var utxoDiffChildBucket = dbkeys.MakeBucket([]byte("utxo-diff-children"))
 
 // utxoDiffStore represents a store of UTXODiffs
 type utxoDiffStore struct {
-	utxoDiffStaging      map[externalapi.DomainHash]*model.UTXODiff
+	utxoDiffStaging      map[externalapi.DomainHash]model.UTXODiff
 	utxoDiffChildStaging map[externalapi.DomainHash]*externalapi.DomainHash
 	toDelete             map[externalapi.DomainHash]struct{}
 	utxoDiffCache        *lrucache.LRUCache
@@ -25,7 +25,7 @@ type utxoDiffStore struct {
 // New instantiates a new UTXODiffStore
 func New(cacheSize int) model.UTXODiffStore {
 	return &utxoDiffStore{
-		utxoDiffStaging:      make(map[externalapi.DomainHash]*model.UTXODiff),
+		utxoDiffStaging:      make(map[externalapi.DomainHash]model.UTXODiff),
 		utxoDiffChildStaging: make(map[externalapi.DomainHash]*externalapi.DomainHash),
 		toDelete:             make(map[externalapi.DomainHash]struct{}),
 		utxoDiffCache:        lrucache.New(cacheSize),
@@ -34,8 +34,8 @@ func New(cacheSize int) model.UTXODiffStore {
 }
 
 // Stage stages the given utxoDiff for the given blockHash
-func (uds *utxoDiffStore) Stage(blockHash *externalapi.DomainHash, utxoDiff *model.UTXODiff, utxoDiffChild *externalapi.DomainHash) {
-	uds.utxoDiffStaging[*blockHash] = utxoDiff.Clone()
+func (uds *utxoDiffStore) Stage(blockHash *externalapi.DomainHash, utxoDiff model.UTXODiff, utxoDiffChild *externalapi.DomainHash) {
+	uds.utxoDiffStaging[*blockHash] = utxoDiff
 
 	if utxoDiffChild != nil {
 		uds.utxoDiffChildStaging[*blockHash] = utxoDiffChild.Clone()
@@ -55,7 +55,7 @@ func (uds *utxoDiffStore) IsBlockHashStaged(blockHash *externalapi.DomainHash) b
 }
 
 func (uds *utxoDiffStore) Discard() {
-	uds.utxoDiffStaging = make(map[externalapi.DomainHash]*model.UTXODiff)
+	uds.utxoDiffStaging = make(map[externalapi.DomainHash]model.UTXODiff)
 	uds.utxoDiffChildStaging = make(map[externalapi.DomainHash]*externalapi.DomainHash)
 	uds.toDelete = make(map[externalapi.DomainHash]struct{})
 }
@@ -107,13 +107,13 @@ func (uds *utxoDiffStore) Commit(dbTx model.DBTransaction) error {
 }
 
 // UTXODiff gets the utxoDiff associated with the given blockHash
-func (uds *utxoDiffStore) UTXODiff(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.UTXODiff, error) {
+func (uds *utxoDiffStore) UTXODiff(dbContext model.DBReader, blockHash *externalapi.DomainHash) (model.UTXODiff, error) {
 	if utxoDiff, ok := uds.utxoDiffStaging[*blockHash]; ok {
-		return utxoDiff.Clone(), nil
+		return utxoDiff, nil
 	}
 
 	if utxoDiff, ok := uds.utxoDiffCache.Get(blockHash); ok {
-		return utxoDiff.(*model.UTXODiff).Clone(), nil
+		return utxoDiff.(model.UTXODiff), nil
 	}
 
 	utxoDiffBytes, err := dbContext.Get(uds.utxoDiffHashAsKey(blockHash))
@@ -126,7 +126,7 @@ func (uds *utxoDiffStore) UTXODiff(dbContext model.DBReader, blockHash *external
 		return nil, err
 	}
 	uds.utxoDiffCache.Add(blockHash, utxoDiff)
-	return utxoDiff.Clone(), nil
+	return utxoDiff, nil
 }
 
 // UTXODiffChild gets the utxoDiff child associated with the given blockHash
@@ -187,16 +187,21 @@ func (uds *utxoDiffStore) utxoDiffChildHashAsKey(hash *externalapi.DomainHash) m
 	return utxoDiffChildBucket.Key(hash[:])
 }
 
-func (uds *utxoDiffStore) serializeUTXODiff(utxoDiff *model.UTXODiff) ([]byte, error) {
-	dbUtxoDiff := serialization.UTXODiffToDBUTXODiff(utxoDiff)
+func (uds *utxoDiffStore) serializeUTXODiff(utxoDiff model.UTXODiff) ([]byte, error) {
+	dbUtxoDiff, err := serialization.UTXODiffToDBUTXODiff(utxoDiff)
+	if err != nil {
+		return nil, err
+	}
+
 	bytes, err := proto.Marshal(dbUtxoDiff)
 	if err != nil {
 		return nil, errors.WithStack(err)
 	}
+
 	return bytes, nil
 }
 
-func (uds *utxoDiffStore) deserializeUTXODiff(utxoDiffBytes []byte) (*model.UTXODiff, error) {
+func (uds *utxoDiffStore) deserializeUTXODiff(utxoDiffBytes []byte) (model.UTXODiff, error) {
 	dbUTXODiff := &serialization.DbUtxoDiff{}
 	err := proto.Unmarshal(utxoDiffBytes, dbUTXODiff)
 	if err != nil {
diff --git a/domain/consensus/model/externalapi/transaction.go b/domain/consensus/model/externalapi/transaction.go
index 83300d2c..0ffdf0e4 100644
--- a/domain/consensus/model/externalapi/transaction.go
+++ b/domain/consensus/model/externalapi/transaction.go
@@ -62,7 +62,7 @@ type DomainTransactionInput struct {
 	SignatureScript  []byte
 	Sequence         uint64
 
-	UTXOEntry *UTXOEntry
+	UTXOEntry UTXOEntry
 }
 
 // Clone returns a clone of DomainTransactionInput
@@ -78,7 +78,7 @@ func (input *DomainTransactionInput) Clone() *DomainTransactionInput {
 		PreviousOutpoint: *input.PreviousOutpoint.Clone(),
 		SignatureScript:  signatureScriptClone,
 		Sequence:         input.Sequence,
-		UTXOEntry:        input.UTXOEntry.Clone(),
+		UTXOEntry:        input.UTXOEntry,
 	}
 }
 
diff --git a/domain/consensus/model/externalapi/utxoentry.go b/domain/consensus/model/externalapi/utxoentry.go
index 69f5a475..22f9498b 100644
--- a/domain/consensus/model/externalapi/utxoentry.go
+++ b/domain/consensus/model/externalapi/utxoentry.go
@@ -4,36 +4,9 @@ package externalapi
 // set such as whether or not it was contained in a coinbase tx, the blue
 // score of the block that accepts the tx, its public key script, and how
 // much it pays.
-type UTXOEntry struct {
-	Amount          uint64
-	ScriptPublicKey []byte // The public key script for the output.
-	BlockBlueScore  uint64 // Blue score of the block accepting the tx.
-	IsCoinbase      bool
-}
-
-// Clone returns a clone of UTXOEntry
-func (entry *UTXOEntry) Clone() *UTXOEntry {
-	if entry == nil {
-		return nil
-	}
-
-	scriptPublicKeyClone := make([]byte, len(entry.ScriptPublicKey))
-	copy(scriptPublicKeyClone, entry.ScriptPublicKey)
-
-	return &UTXOEntry{
-		Amount:          entry.Amount,
-		ScriptPublicKey: scriptPublicKeyClone,
-		BlockBlueScore:  entry.BlockBlueScore,
-		IsCoinbase:      entry.IsCoinbase,
-	}
-}
-
-// NewUTXOEntry creates a new utxoEntry representing the given txOut
-func NewUTXOEntry(amount uint64, scriptPubKey []byte, isCoinbase bool, blockBlueScore uint64) *UTXOEntry {
-	return &UTXOEntry{
-		Amount:          amount,
-		ScriptPublicKey: scriptPubKey,
-		BlockBlueScore:  blockBlueScore,
-		IsCoinbase:      isCoinbase,
-	}
+type UTXOEntry interface {
+	Amount() uint64
+	ScriptPublicKey() []byte // The public key script for the output.
+	BlockBlueScore() uint64  // Blue score of the block accepting the tx.
+	IsCoinbase() bool
 }
diff --git a/domain/consensus/model/interface_datastructures_utxodiffstore.go b/domain/consensus/model/interface_datastructures_utxodiffstore.go
index 4d7ab310..a774c55e 100644
--- a/domain/consensus/model/interface_datastructures_utxodiffstore.go
+++ b/domain/consensus/model/interface_datastructures_utxodiffstore.go
@@ -5,9 +5,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 // UTXODiffStore represents a store of UTXODiffs
 type UTXODiffStore interface {
 	Store
-	Stage(blockHash *externalapi.DomainHash, utxoDiff *UTXODiff, utxoDiffChild *externalapi.DomainHash)
+	Stage(blockHash *externalapi.DomainHash, utxoDiff UTXODiff, utxoDiffChild *externalapi.DomainHash)
 	IsStaged() bool
-	UTXODiff(dbContext DBReader, blockHash *externalapi.DomainHash) (*UTXODiff, error)
+	UTXODiff(dbContext DBReader, blockHash *externalapi.DomainHash) (UTXODiff, error)
 	UTXODiffChild(dbContext DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
 	HasUTXODiffChild(dbContext DBReader, blockHash *externalapi.DomainHash) (bool, error)
 	Delete(blockHash *externalapi.DomainHash)
diff --git a/domain/consensus/model/interface_processes_consensusstatemanager.go b/domain/consensus/model/interface_processes_consensusstatemanager.go
index 5f4dc783..208eef3e 100644
--- a/domain/consensus/model/interface_processes_consensusstatemanager.go
+++ b/domain/consensus/model/interface_processes_consensusstatemanager.go
@@ -9,5 +9,5 @@ type ConsensusStateManager interface {
 	SetPruningPointUTXOSet(serializedUTXOSet []byte) error
 	RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (ReadOnlyUTXOSetIterator, error)
 	HeaderTipsPruningPoint() (*externalapi.DomainHash, error)
-	CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (*UTXODiff, AcceptanceData, Multiset, error)
+	CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (UTXODiff, AcceptanceData, Multiset, error)
 }
diff --git a/domain/consensus/model/readonlyutxoset.go b/domain/consensus/model/readonlyutxoset.go
index cba7d9c7..17f8c293 100644
--- a/domain/consensus/model/readonlyutxoset.go
+++ b/domain/consensus/model/readonlyutxoset.go
@@ -5,12 +5,12 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 // ReadOnlyUTXOSet represents a UTXOSet that can only be read from
 type ReadOnlyUTXOSet interface {
 	Iterator() ReadOnlyUTXOSetIterator
-	Entry(outpoint *externalapi.DomainOutpoint) *externalapi.UTXOEntry
+	Entry(outpoint *externalapi.DomainOutpoint) externalapi.UTXOEntry
 }
 
 // ReadOnlyUTXOSetIterator is an iterator over all entries in a
 // ReadOnlyUTXOSet
 type ReadOnlyUTXOSetIterator interface {
 	Next() bool
-	Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error)
+	Get() (outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry, err error)
 }
diff --git a/domain/consensus/model/utxodiff.go b/domain/consensus/model/utxodiff.go
index 7448f1c0..6de4e2ce 100644
--- a/domain/consensus/model/utxodiff.go
+++ b/domain/consensus/model/utxodiff.go
@@ -1,72 +1,33 @@
 package model
 
-import (
-	"fmt"
-	"sort"
-	"strings"
-
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-)
-
-// UTXOCollection represents a set of UTXOs indexed by their outpoints
-type UTXOCollection map[externalapi.DomainOutpoint]*externalapi.UTXOEntry
-
-// Clone returns a clone of UTXOCollection
-func (uc UTXOCollection) Clone() UTXOCollection {
-	if uc == nil {
-		return nil
-	}
-
-	clone := make(UTXOCollection, len(uc))
-	for outpoint, entry := range uc {
-		clone[outpoint] = entry.Clone()
-	}
-
-	return clone
+import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+
+// UTXOCollection represents a collection of UTXO entries, indexed by their outpoint
+type UTXOCollection interface {
+	Iterator() ReadOnlyUTXOSetIterator
+	Get(outpoint *externalapi.DomainOutpoint) (externalapi.UTXOEntry, bool)
+	Contains(outpoint *externalapi.DomainOutpoint) bool
+	Len() int
 }
 
-func (uc UTXOCollection) String() string {
-	utxoStrings := make([]string, len(uc))
-
-	i := 0
-	for outpoint, utxoEntry := range uc {
-		utxoStrings[i] = fmt.Sprintf("(%s, %d) => %d, blueScore: %d",
-			outpoint.TransactionID, outpoint.Index, utxoEntry.Amount, utxoEntry.BlockBlueScore)
-		i++
-	}
-
-	// Sort strings for determinism.
-	sort.Strings(utxoStrings)
-
-	return fmt.Sprintf("[ %s ]", strings.Join(utxoStrings, ", "))
-}
-
-// UTXODiff represents a diff between two UTXO Sets.
-type UTXODiff struct {
-	ToAdd    UTXOCollection
-	ToRemove UTXOCollection
+// UTXODiff represents the diff between two UTXO sets
+type UTXODiff interface {
+	ToAdd() UTXOCollection
+	ToRemove() UTXOCollection
+	WithDiff(other UTXODiff) (UTXODiff, error)
+	DiffFrom(other UTXODiff) (UTXODiff, error)
+	CloneMutable() MutableUTXODiff
 }
 
-// Clone returns a clone of UTXODiff
-func (d *UTXODiff) Clone() *UTXODiff {
-	if d == nil {
-		return nil
-	}
+// MutableUTXODiff represents a UTXO-Diff that can be mutated
+type MutableUTXODiff interface {
+	ToImmutable() UTXODiff
 
-	return &UTXODiff{
-		ToAdd:    d.ToAdd.Clone(),
-		ToRemove: d.ToRemove.Clone(),
-	}
-}
-
-func (d UTXODiff) String() string {
-	return fmt.Sprintf("ToAdd: %s; ToRemove: %s", d.ToAdd, d.ToRemove)
-}
+	WithDiff(other UTXODiff) (UTXODiff, error)
+	DiffFrom(other UTXODiff) (UTXODiff, error)
+	ToAdd() UTXOCollection
+	ToRemove() UTXOCollection
 
-// NewUTXODiff instantiates an empty UTXODiff
-func NewUTXODiff() *UTXODiff {
-	return &UTXODiff{
-		ToAdd:    UTXOCollection{},
-		ToRemove: UTXOCollection{},
-	}
+	WithDiffInPlace(other UTXODiff) error
+	AddTransaction(transaction *externalapi.DomainTransaction, blockBlueScore uint64) error
 }
diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
index bd01ca56..d5577b00 100644
--- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
+++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
@@ -11,11 +11,10 @@ import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo/utxoalgebra"
 )
 
 func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (
-	*model.UTXODiff, model.AcceptanceData, model.Multiset, error) {
+	model.UTXODiff, model.AcceptanceData, model.Multiset, error) {
 
 	log.Tracef("CalculatePastUTXOAndAcceptanceData start for block %s", blockHash)
 	defer log.Tracef("CalculatePastUTXOAndAcceptanceData end for block %s", blockHash)
@@ -23,7 +22,7 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
 	if *blockHash == *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 &model.UTXODiff{}, model.AcceptanceData{}, multiset.New(), nil
+		return utxo.NewUTXODiff(), model.AcceptanceData{}, multiset.New(), nil
 	}
 
 	blockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, blockHash)
@@ -51,17 +50,17 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
 	}
 	log.Tracef("The multiset of block %s resolved to: %s", blockHash, multiset.Hash())
 
-	return utxoDiff, acceptanceData, multiset, nil
+	return utxoDiff.ToImmutable(), acceptanceData, multiset, nil
 }
 
-func (csm *consensusStateManager) restorePastUTXO(blockHash *externalapi.DomainHash) (*model.UTXODiff, error) {
+func (csm *consensusStateManager) restorePastUTXO(blockHash *externalapi.DomainHash) (model.MutableUTXODiff, error) {
 	log.Tracef("restorePastUTXO start for block %s", blockHash)
 	defer log.Tracef("restorePastUTXO end for block %s", blockHash)
 
 	var err error
 
 	log.Tracef("Collecting UTXO diffs for block %s", blockHash)
-	var utxoDiffs []*model.UTXODiff
+	var utxoDiffs []model.UTXODiff
 	nextBlockHash := blockHash
 	for {
 		log.Tracef("Collecting UTXO diff for block %s", nextBlockHash)
@@ -97,9 +96,9 @@ func (csm *consensusStateManager) restorePastUTXO(blockHash *externalapi.DomainH
 
 	// apply the diffs in reverse order
 	log.Tracef("Applying the collected UTXO diffs for block %s in reverse order", blockHash)
-	accumulatedDiff := model.NewUTXODiff()
+	accumulatedDiff := utxo.NewMutableUTXODiff()
 	for i := len(utxoDiffs) - 1; i >= 0; i-- {
-		accumulatedDiff, err = utxoalgebra.WithDiff(accumulatedDiff, utxoDiffs[i])
+		err = accumulatedDiff.WithDiffInPlace(utxoDiffs[i])
 		if err != nil {
 			return nil, err
 		}
@@ -110,8 +109,8 @@ func (csm *consensusStateManager) restorePastUTXO(blockHash *externalapi.DomainH
 }
 
 func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainHash,
-	selectedParentPastUTXODiff *model.UTXODiff, ghostdagData *model.BlockGHOSTDAGData) (
-	model.AcceptanceData, *model.UTXODiff, error) {
+	selectedParentPastUTXODiff model.MutableUTXODiff, ghostdagData *model.BlockGHOSTDAGData) (
+	model.AcceptanceData, model.MutableUTXODiff, error) {
 
 	log.Tracef("applyBlueBlocks start for block %s", blockHash)
 	defer log.Tracef("applyBlueBlocks end for block %s", blockHash)
@@ -128,7 +127,7 @@ func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainH
 	log.Tracef("The past median time for block %s is: %d", blockHash, selectedParentMedianTime)
 
 	multiblockAcceptanceData := make(model.AcceptanceData, len(blueBlocks))
-	accumulatedUTXODiff := selectedParentPastUTXODiff.Clone()
+	accumulatedUTXODiff := selectedParentPastUTXODiff
 	accumulatedMass := uint64(0)
 
 	for i, blueBlock := range blueBlocks {
@@ -168,7 +167,7 @@ func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainH
 }
 
 func (csm *consensusStateManager) maybeAcceptTransaction(transaction *externalapi.DomainTransaction,
-	blockHash *externalapi.DomainHash, isSelectedParent bool, accumulatedUTXODiff *model.UTXODiff,
+	blockHash *externalapi.DomainHash, isSelectedParent bool, accumulatedUTXODiff model.MutableUTXODiff,
 	accumulatedMassBefore uint64, selectedParentPastMedianTime int64, blockBlueScore uint64) (
 	isAccepted bool, accumulatedMassAfter uint64, err error) {
 
@@ -177,7 +176,7 @@ func (csm *consensusStateManager) maybeAcceptTransaction(transaction *externalap
 	defer log.Tracef("maybeAcceptTransaction end for transaction %s in block %s", transactionID, blockHash)
 
 	log.Tracef("Populating transaction %s with UTXO entries", transactionID)
-	err = csm.populateTransactionWithUTXOEntriesFromVirtualOrDiff(transaction, accumulatedUTXODiff)
+	err = csm.populateTransactionWithUTXOEntriesFromVirtualOrDiff(transaction, accumulatedUTXODiff.ToImmutable())
 	if err != nil {
 		if !errors.As(err, &(ruleerrors.RuleError{})) {
 			return false, 0, err
@@ -220,7 +219,7 @@ func (csm *consensusStateManager) maybeAcceptTransaction(transaction *externalap
 	}
 
 	log.Tracef("Adding transaction %s in block %s to the accumulated diff", transactionID, blockHash)
-	err = utxoalgebra.DiffAddTransaction(accumulatedUTXODiff, transaction, blockBlueScore)
+	err = accumulatedUTXODiff.AddTransaction(transaction, blockBlueScore)
 	if err != nil {
 		return false, 0, err
 	}
@@ -278,5 +277,5 @@ func (csm *consensusStateManager) RestorePastUTXOSetIterator(blockHash *external
 		return nil, err
 	}
 
-	return utxo.IteratorWithDiff(virtualUTXOSetIterator, blockDiff)
+	return utxo.IteratorWithDiff(virtualUTXOSetIterator, blockDiff.ToImmutable())
 }
diff --git a/domain/consensus/processes/consensusstatemanager/multisets.go b/domain/consensus/processes/consensusstatemanager/multisets.go
index c3b1e733..7786b499 100644
--- a/domain/consensus/processes/consensusstatemanager/multisets.go
+++ b/domain/consensus/processes/consensusstatemanager/multisets.go
@@ -72,12 +72,8 @@ func addTransactionToMultiset(multiset model.Multiset, transaction *externalapi.
 			TransactionID: *transactionID,
 			Index:         uint32(i),
 		}
-		utxoEntry := &externalapi.UTXOEntry{
-			Amount:          output.Value,
-			ScriptPublicKey: output.ScriptPublicKey,
-			BlockBlueScore:  blockBlueScore,
-			IsCoinbase:      isCoinbase,
-		}
+		utxoEntry := utxo.NewUTXOEntry(output.Value, output.ScriptPublicKey, isCoinbase, blockBlueScore)
+
 		log.Tracef("Adding input %s at index %d from the multiset", transactionID, i)
 		err := addUTXOToMultiset(multiset, utxoEntry, outpoint)
 		if err != nil {
@@ -88,7 +84,7 @@ func addTransactionToMultiset(multiset model.Multiset, transaction *externalapi.
 	return nil
 }
 
-func addUTXOToMultiset(multiset model.Multiset, entry *externalapi.UTXOEntry,
+func addUTXOToMultiset(multiset model.Multiset, entry externalapi.UTXOEntry,
 	outpoint *externalapi.DomainOutpoint) error {
 
 	serializedUTXO, err := utxo.SerializeUTXO(entry, outpoint)
@@ -100,7 +96,7 @@ func addUTXOToMultiset(multiset model.Multiset, entry *externalapi.UTXOEntry,
 	return nil
 }
 
-func removeUTXOFromMultiset(multiset model.Multiset, entry *externalapi.UTXOEntry,
+func removeUTXOFromMultiset(multiset model.Multiset, entry externalapi.UTXOEntry,
 	outpoint *externalapi.DomainOutpoint) error {
 
 	serializedUTXO, err := utxo.SerializeUTXO(entry, outpoint)
diff --git a/domain/consensus/processes/consensusstatemanager/populate_tx_with_utxo_entries.go b/domain/consensus/processes/consensusstatemanager/populate_tx_with_utxo_entries.go
index 1b6e38d7..3b5c6ce0 100644
--- a/domain/consensus/processes/consensusstatemanager/populate_tx_with_utxo_entries.go
+++ b/domain/consensus/processes/consensusstatemanager/populate_tx_with_utxo_entries.go
@@ -5,7 +5,6 @@ import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo/utxoalgebra"
 )
 
 // PopulateTransactionWithUTXOEntries populates the transaction UTXO entries with data from the virtual's UTXO set.
@@ -17,7 +16,7 @@ func (csm *consensusStateManager) PopulateTransactionWithUTXOEntries(transaction
 // from the virtual's UTXO set combined with the provided utxoDiff.
 // If utxoDiff == nil UTXO entries are taken from the virtual's UTXO set only
 func (csm *consensusStateManager) populateTransactionWithUTXOEntriesFromVirtualOrDiff(
-	transaction *externalapi.DomainTransaction, utxoDiff *model.UTXODiff) error {
+	transaction *externalapi.DomainTransaction, utxoDiff model.UTXODiff) error {
 
 	transactionID := consensushashing.TransactionID(transaction)
 	log.Tracef("populateTransactionWithUTXOEntriesFromVirtualOrDiff start for transaction %s", transactionID)
@@ -34,14 +33,14 @@ func (csm *consensusStateManager) populateTransactionWithUTXOEntriesFromVirtualO
 
 		// check if utxoDiff says anything about the input's outpoint
 		if utxoDiff != nil {
-			if utxoEntry, ok := utxoalgebra.CollectionGet(utxoDiff.ToAdd, &transactionInput.PreviousOutpoint); ok {
+			if utxoEntry, ok := utxoDiff.ToAdd().Get(&transactionInput.PreviousOutpoint); ok {
 				log.Tracef("Populating outpoint %s:%d from the given utxoDiff",
 					transactionInput.PreviousOutpoint.TransactionID, transactionInput.PreviousOutpoint.Index)
 				transactionInput.UTXOEntry = utxoEntry
 				continue
 			}
 
-			if utxoalgebra.CollectionContains(utxoDiff.ToRemove, &transactionInput.PreviousOutpoint) {
+			if utxoDiff.ToRemove().Contains(&transactionInput.PreviousOutpoint) {
 				log.Tracef("Outpoint %s:%d is missing in the given utxoDiff",
 					transactionInput.PreviousOutpoint.TransactionID, transactionInput.PreviousOutpoint.Index)
 				missingOutpoints = append(missingOutpoints, &transactionInput.PreviousOutpoint)
diff --git a/domain/consensus/processes/consensusstatemanager/resolve_block_status.go b/domain/consensus/processes/consensusstatemanager/resolve_block_status.go
index bd54054c..3a2447aa 100644
--- a/domain/consensus/processes/consensusstatemanager/resolve_block_status.go
+++ b/domain/consensus/processes/consensusstatemanager/resolve_block_status.go
@@ -4,7 +4,6 @@ import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo/utxoalgebra"
 	"github.com/pkg/errors"
 )
 
@@ -169,7 +168,7 @@ func (csm *consensusStateManager) resolveSingleBlockStatus(blockHash *externalap
 }
 
 func (csm *consensusStateManager) removeAncestorsFromVirtualDiffParentsAndAssignDiffChild(
-	blockHash *externalapi.DomainHash, pastUTXODiff *model.UTXODiff) error {
+	blockHash *externalapi.DomainHash, pastUTXODiff model.UTXODiff) error {
 
 	log.Tracef("removeAncestorsFromVirtualDiffParentsAndAssignDiffChild start for block %s", blockHash)
 	defer log.Tracef("removeAncestorsFromVirtualDiffParentsAndAssignDiffChild end for block %s", blockHash)
@@ -209,7 +208,7 @@ func (csm *consensusStateManager) removeAncestorsFromVirtualDiffParentsAndAssign
 		if err != nil {
 			return err
 		}
-		newDiff, err := utxoalgebra.DiffFrom(pastUTXODiff, currentDiff)
+		newDiff, err := pastUTXODiff.DiffFrom(currentDiff)
 		if err != nil {
 			return err
 		}
diff --git a/domain/consensus/processes/consensusstatemanager/set_pruning_utxo_set.go b/domain/consensus/processes/consensusstatemanager/set_pruning_utxo_set.go
index 7c6b667a..fd211c9b 100644
--- a/domain/consensus/processes/consensusstatemanager/set_pruning_utxo_set.go
+++ b/domain/consensus/processes/consensusstatemanager/set_pruning_utxo_set.go
@@ -88,7 +88,7 @@ func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byt
 	}
 
 	log.Tracef("Updating the header tips pruning point diff parents with an empty UTXO diff")
-	err = csm.updateVirtualDiffParents(model.NewUTXODiff())
+	err = csm.updateVirtualDiffParents(utxo.NewUTXODiff())
 	if err != nil {
 		return err
 	}
@@ -130,7 +130,7 @@ func (p protoUTXOSetIterator) Next() bool {
 	return p.index < len(p.utxoSet.Utxos)
 }
 
-func (p protoUTXOSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error) {
+func (p protoUTXOSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry, err error) {
 	entry, outpoint, err := utxo.DeserializeUTXO(p.utxoSet.Utxos[p.index].EntryOutpointPair)
 	if err != nil {
 		if serialization.IsMalformedError(err) {
diff --git a/domain/consensus/processes/consensusstatemanager/update_virtual.go b/domain/consensus/processes/consensusstatemanager/update_virtual.go
index f93bbe10..c87aed31 100644
--- a/domain/consensus/processes/consensusstatemanager/update_virtual.go
+++ b/domain/consensus/processes/consensusstatemanager/update_virtual.go
@@ -3,7 +3,6 @@ package consensusstatemanager
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo/utxoalgebra"
 )
 
 func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.DomainHash, tips []*externalapi.DomainHash) error {
@@ -55,7 +54,7 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain
 	return nil
 }
 
-func (csm *consensusStateManager) updateVirtualDiffParents(virtualUTXODiff *model.UTXODiff) error {
+func (csm *consensusStateManager) updateVirtualDiffParents(virtualUTXODiff model.UTXODiff) error {
 	log.Tracef("updateVirtualDiffParents start")
 	defer log.Tracef("updateVirtualDiffParents end")
 
@@ -70,7 +69,7 @@ func (csm *consensusStateManager) updateVirtualDiffParents(virtualUTXODiff *mode
 		if err != nil {
 			return err
 		}
-		newDiff, err := utxoalgebra.DiffFrom(virtualUTXODiff, virtualDiffParentUTXODiff)
+		newDiff, err := virtualUTXODiff.DiffFrom(virtualDiffParentUTXODiff)
 		if err != nil {
 			return err
 		}
diff --git a/domain/consensus/processes/consensusstatemanager/utxo_diffs.go b/domain/consensus/processes/consensusstatemanager/utxo_diffs.go
index 1f55fb33..564ca9fa 100644
--- a/domain/consensus/processes/consensusstatemanager/utxo_diffs.go
+++ b/domain/consensus/processes/consensusstatemanager/utxo_diffs.go
@@ -7,7 +7,7 @@ import (
 )
 
 func (csm *consensusStateManager) stageDiff(blockHash *externalapi.DomainHash,
-	utxoDiff *model.UTXODiff, utxoDiffChild *externalapi.DomainHash) error {
+	utxoDiff model.UTXODiff, utxoDiffChild *externalapi.DomainHash) error {
 
 	log.Tracef("stageDiff start for block %s", blockHash)
 	defer log.Tracef("stageDiff end for block %s", blockHash)
diff --git a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
index 17226e86..36914518 100644
--- a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
+++ b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go
@@ -19,7 +19,7 @@ import (
 )
 
 func (csm *consensusStateManager) verifyUTXO(block *externalapi.DomainBlock, blockHash *externalapi.DomainHash,
-	pastUTXODiff *model.UTXODiff, acceptanceData model.AcceptanceData, multiset model.Multiset) error {
+	pastUTXODiff model.UTXODiff, acceptanceData model.AcceptanceData, multiset model.Multiset) error {
 
 	log.Tracef("verifyUTXO start for block %s", blockHash)
 	defer log.Tracef("verifyUTXO end for block %s", blockHash)
@@ -58,7 +58,7 @@ func (csm *consensusStateManager) verifyUTXO(block *externalapi.DomainBlock, blo
 }
 
 func (csm *consensusStateManager) validateBlockTransactionsAgainstPastUTXO(block *externalapi.DomainBlock,
-	blockHash *externalapi.DomainHash, pastUTXODiff *model.UTXODiff) error {
+	blockHash *externalapi.DomainHash, pastUTXODiff model.UTXODiff) error {
 
 	log.Tracef("validateBlockTransactionsAgainstPastUTXO start for block %s", blockHash)
 	defer log.Tracef("validateBlockTransactionsAgainstPastUTXO end for block %s", blockHash)
diff --git a/domain/consensus/processes/transactionvalidator/mass.go b/domain/consensus/processes/transactionvalidator/mass.go
index 56206219..6b5d15da 100644
--- a/domain/consensus/processes/transactionvalidator/mass.go
+++ b/domain/consensus/processes/transactionvalidator/mass.go
@@ -37,8 +37,8 @@ func (v *transactionValidator) transactionMass(tx *externalapi.DomainTransaction
 		// Count the precise number of signature operations in the
 		// referenced public key script.
 		sigScript := input.SignatureScript
-		isP2SH := txscript.IsPayToScriptHash(utxoEntry.ScriptPublicKey)
-		sigOpsCount += uint64(txscript.GetPreciseSigOpCount(sigScript, utxoEntry.ScriptPublicKey, isP2SH))
+		isP2SH := txscript.IsPayToScriptHash(utxoEntry.ScriptPublicKey())
+		sigOpsCount += uint64(txscript.GetPreciseSigOpCount(sigScript, utxoEntry.ScriptPublicKey(), isP2SH))
 	}
 	if len(missingOutpoints) > 0 {
 		return 0, ruleerrors.NewErrMissingTxOut(missingOutpoints)
diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_context.go b/domain/consensus/processes/transactionvalidator/transaction_in_context.go
index bc8dd7af..9f40f999 100644
--- a/domain/consensus/processes/transactionvalidator/transaction_in_context.go
+++ b/domain/consensus/processes/transactionvalidator/transaction_in_context.go
@@ -65,8 +65,8 @@ func (v *transactionValidator) checkTransactionCoinbaseMaturity(
 		utxoEntry := input.UTXOEntry
 		if utxoEntry == nil {
 			missingOutpoints = append(missingOutpoints, &input.PreviousOutpoint)
-		} else if utxoEntry.IsCoinbase {
-			originBlueScore := utxoEntry.BlockBlueScore
+		} else if utxoEntry.IsCoinbase() {
+			originBlueScore := utxoEntry.BlockBlueScore()
 			blueScoreSincePrev := txBlueScore - originBlueScore
 			if blueScoreSincePrev < v.blockCoinbaseMaturity {
 				return errors.Wrapf(ruleerrors.ErrImmatureSpend, "tried to spend coinbase "+
@@ -116,11 +116,11 @@ func (v *transactionValidator) checkTransactionInputAmounts(tx *externalapi.Doma
 	return totalSompiIn, nil
 }
 
-func (v *transactionValidator) checkEntryAmounts(entry *externalapi.UTXOEntry, totalSompiInBefore uint64) (totalSompiInAfter uint64, err error) {
+func (v *transactionValidator) checkEntryAmounts(entry externalapi.UTXOEntry, totalSompiInBefore uint64) (totalSompiInAfter uint64, err error) {
 	// The total of all outputs must not be more than the max
 	// allowed per transaction. Also, we could potentially overflow
 	// the accumulator so check for overflow.
-	originTxSompi := entry.Amount
+	originTxSompi := entry.Amount()
 	totalSompiInAfter = totalSompiInBefore + originTxSompi
 	if totalSompiInAfter < totalSompiInBefore ||
 		totalSompiInAfter > constants.MaxSompi {
@@ -187,7 +187,7 @@ func (v *transactionValidator) validateTransactionScripts(tx *externalapi.Domain
 			continue
 		}
 
-		scriptPubKey := utxoEntry.ScriptPublicKey
+		scriptPubKey := utxoEntry.ScriptPublicKey()
 		vm, err := txscript.NewEngine(scriptPubKey, tx,
 			i, txscript.ScriptNoFlags, v.sigCache)
 		if err != nil {
@@ -241,7 +241,7 @@ func (v *transactionValidator) calcTxSequenceLockFromReferencedUTXOEntries(
 		// If the input blue score is set to the mempool blue score, then we
 		// assume the transaction makes it into the next block when
 		// evaluating its sequence blocks.
-		inputBlueScore := utxoEntry.BlockBlueScore
+		inputBlueScore := utxoEntry.BlockBlueScore()
 
 		// Given a sequence number, we apply the relative time lock
 		// mask in order to obtain the time lock delta required before
diff --git a/domain/consensus/utils/utxo/diff_algebra.go b/domain/consensus/utils/utxo/diff_algebra.go
new file mode 100644
index 00000000..876ba809
--- /dev/null
+++ b/domain/consensus/utils/utxo/diff_algebra.go
@@ -0,0 +1,252 @@
+package utxo
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/pkg/errors"
+)
+
+// checkIntersection checks if there is an intersection between two utxoCollections
+func checkIntersection(collection1 utxoCollection, collection2 utxoCollection) bool {
+	for outpoint := range collection1 {
+		if collection2.Contains(&outpoint) {
+			return true
+		}
+	}
+
+	return false
+}
+
+// checkIntersectionWithRule checks if there is an intersection between two utxoCollections satisfying arbitrary rule
+// returns the first outpoint in the two collections' intersection satsifying the rule, and a boolean indicating whether
+// such outpoint exists
+func checkIntersectionWithRule(collection1 utxoCollection, collection2 utxoCollection,
+	extraRule func(*externalapi.DomainOutpoint, externalapi.UTXOEntry, externalapi.UTXOEntry) bool) (
+	*externalapi.DomainOutpoint, bool) {
+
+	for outpoint, utxoEntry := range collection1 {
+		if diffEntry, ok := collection2.Get(&outpoint); ok {
+			if extraRule(&outpoint, utxoEntry, diffEntry) {
+				return &outpoint, true
+			}
+		}
+	}
+
+	return nil, false
+}
+
+// minInt returns the smaller of x or y integer values
+func minInt(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}
+
+// intersectionWithRemainderHavingBlueScore calculates an intersection between two utxoCollections
+// having same blue score, returns the result and the remainder from collection1
+func intersectionWithRemainderHavingBlueScore(collection1, collection2 utxoCollection) (result, remainder utxoCollection) {
+	result = make(utxoCollection, minInt(len(collection1), len(collection2)))
+	remainder = make(utxoCollection, len(collection1))
+	intersectionWithRemainderHavingBlueScoreInPlace(collection1, collection2, result, remainder)
+	return
+}
+
+// intersectionWithRemainderHavingBlueScoreInPlace calculates an intersection between two utxoCollections
+// having same blue score, puts it into result and into remainder from collection1
+func intersectionWithRemainderHavingBlueScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
+	for outpoint, utxoEntry := range collection1 {
+		if collection2.containsWithBlueScore(&outpoint, utxoEntry.BlockBlueScore()) {
+			result.add(&outpoint, utxoEntry)
+		} else {
+			remainder.add(&outpoint, utxoEntry)
+		}
+	}
+}
+
+// subtractionHavingBlueScore calculates a subtraction between collection1 and collection2
+// having same blue score, returns the result
+func subtractionHavingBlueScore(collection1, collection2 utxoCollection) (result utxoCollection) {
+	result = make(utxoCollection, len(collection1))
+
+	subtractionHavingBlueScoreInPlace(collection1, collection2, result)
+	return
+}
+
+// subtractionHavingBlueScoreInPlace calculates a subtraction between collection1 and collection2
+// having same blue score, puts it into result
+func subtractionHavingBlueScoreInPlace(collection1, collection2, result utxoCollection) {
+	for outpoint, utxoEntry := range collection1 {
+		if !collection2.containsWithBlueScore(&outpoint, utxoEntry.BlockBlueScore()) {
+			result.add(&outpoint, utxoEntry)
+		}
+	}
+}
+
+// subtractionWithRemainderHavingBlueScore calculates a subtraction between collection1 and collection2
+// having same blue score, returns the result and the remainder from collection1
+func subtractionWithRemainderHavingBlueScore(collection1, collection2 utxoCollection) (result, remainder utxoCollection) {
+	result = make(utxoCollection, len(collection1))
+	remainder = make(utxoCollection, len(collection1))
+
+	subtractionWithRemainderHavingBlueScoreInPlace(collection1, collection2, result, remainder)
+	return
+}
+
+// subtractionWithRemainderHavingBlueScoreInPlace calculates a subtraction between collection1 and collection2
+// having same blue score, puts it into result and into remainder from collection1
+func subtractionWithRemainderHavingBlueScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
+	for outpoint, utxoEntry := range collection1 {
+		if !collection2.containsWithBlueScore(&outpoint, utxoEntry.BlockBlueScore()) {
+			result.add(&outpoint, utxoEntry)
+		} else {
+			remainder.add(&outpoint, utxoEntry)
+		}
+	}
+}
+
+// DiffFrom returns a new mutableUTXODiff with the difference between this mutableUTXODiff and another
+// Assumes that:
+// Both mutableUTXODiffs are from the same base
+// If a txOut exists in both mutableUTXODiffs, its underlying values would be the same
+//
+// diffFrom follows a set of rules represented by the following 3 by 3 table:
+//
+//          |           | this      |           |
+// ---------+-----------+-----------+-----------+-----------
+//          |           | toAdd     | toRemove  | None
+// ---------+-----------+-----------+-----------+-----------
+// other    | toAdd     | -         | X         | toAdd
+// ---------+-----------+-----------+-----------+-----------
+//          | toRemove  | X         | -         | toRemove
+// ---------+-----------+-----------+-----------+-----------
+//          | None      | toRemove  | toAdd     | -
+//
+// Key:
+// -		Don't add anything to the result
+// X		Return an error
+// toAdd	Add the UTXO into the toAdd collection of the result
+// toRemove	Add the UTXO into the toRemove collection of the result
+//
+// Examples:
+// 1. This diff contains a UTXO in toAdd, and the other diff contains it in toRemove
+//    diffFrom results in an error
+// 2. This diff contains a UTXO in toRemove, and the other diff does not contain it
+//    diffFrom results in the UTXO being added to toAdd
+func diffFrom(this, other *mutableUTXODiff) (*mutableUTXODiff, error) {
+	// Note that the following cases are not accounted for, as they are impossible
+	// as long as the base utxoSet is the same:
+	// - if utxoEntry is in this.toAdd and other.toRemove
+	// - if utxoEntry is in this.toRemove and other.toAdd
+
+	// check that NOT (entries with unequal blue scores AND utxoEntry is in this.toAdd and/or other.toRemove) -> Error
+	isNotAddedOutputRemovedWithBlueScore := func(outpoint *externalapi.DomainOutpoint, utxoEntry, diffEntry externalapi.UTXOEntry) bool {
+		return !(diffEntry.BlockBlueScore() != utxoEntry.BlockBlueScore() &&
+			(this.toAdd.containsWithBlueScore(outpoint, diffEntry.BlockBlueScore()) ||
+				other.toRemove.containsWithBlueScore(outpoint, utxoEntry.BlockBlueScore())))
+	}
+
+	if offendingOutpoint, ok :=
+		checkIntersectionWithRule(this.toRemove, other.toAdd, isNotAddedOutputRemovedWithBlueScore); ok {
+		return nil, errors.Errorf("diffFrom: outpoint %s both in this.toAdd and in other.toRemove", offendingOutpoint)
+	}
+
+	//check that NOT (entries with unequal blue score AND utxoEntry is in this.toRemove and/or other.toAdd) -> Error
+	isNotRemovedOutputAddedWithBlueScore :=
+		func(outpoint *externalapi.DomainOutpoint, utxoEntry, diffEntry externalapi.UTXOEntry) bool {
+
+			return !(diffEntry.BlockBlueScore() != utxoEntry.BlockBlueScore() &&
+				(this.toRemove.containsWithBlueScore(outpoint, diffEntry.BlockBlueScore()) ||
+					other.toAdd.containsWithBlueScore(outpoint, utxoEntry.BlockBlueScore())))
+		}
+
+	if offendingOutpoint, ok :=
+		checkIntersectionWithRule(this.toAdd, other.toRemove, isNotRemovedOutputAddedWithBlueScore); ok {
+		return nil, errors.Errorf("diffFrom: outpoint %s both in this.toRemove and in other.toAdd", offendingOutpoint)
+	}
+
+	// if have the same entry in this.toRemove and other.toRemove
+	// and existing entry is with different blue score, in this case - this is an error
+	if offendingOutpoint, ok := checkIntersectionWithRule(this.toRemove, other.toRemove,
+		func(outpoint *externalapi.DomainOutpoint, utxoEntry, diffEntry externalapi.UTXOEntry) bool {
+			return utxoEntry.BlockBlueScore() != diffEntry.BlockBlueScore()
+		}); ok {
+		return nil, errors.Errorf("diffFrom: outpoint %s both in this.toRemove and other.toRemove with different "+
+			"blue scores, with no corresponding entry in this.toAdd", offendingOutpoint)
+	}
+
+	result := &mutableUTXODiff{
+		toAdd:    make(utxoCollection, len(this.toRemove)+len(other.toAdd)),
+		toRemove: make(utxoCollection, len(this.toAdd)+len(other.toRemove)),
+	}
+
+	// All transactions in this.toAdd:
+	// If they are not in other.toAdd - should be added in result.toRemove
+	inBothToAdd := make(utxoCollection, len(this.toAdd))
+	subtractionWithRemainderHavingBlueScoreInPlace(this.toAdd, other.toAdd, result.toRemove, inBothToAdd)
+	// If they are in other.toRemove - base utxoSet is not the same
+	if checkIntersection(inBothToAdd, this.toRemove) != checkIntersection(inBothToAdd, other.toRemove) {
+		return nil, errors.New(
+			"diffFrom: outpoint both in this.toAdd, other.toAdd, and only one of this.toRemove and other.toRemove")
+	}
+
+	// All transactions in other.toRemove:
+	// If they are not in this.toRemove - should be added in result.toRemove
+	subtractionHavingBlueScoreInPlace(other.toRemove, this.toRemove, result.toRemove)
+
+	// All transactions in this.toRemove:
+	// If they are not in other.toRemove - should be added in result.toAdd
+	subtractionHavingBlueScoreInPlace(this.toRemove, other.toRemove, result.toAdd)
+
+	// All transactions in other.toAdd:
+	// If they are not in this.toAdd - should be added in result.toAdd
+	subtractionHavingBlueScoreInPlace(other.toAdd, this.toAdd, result.toAdd)
+
+	return result, nil
+}
+
+// WithDiffInPlace applies provided diff to this diff in-place, that would be the result if
+// first d, and than diff were applied to the same base
+func withDiffInPlace(this *mutableUTXODiff, other *mutableUTXODiff) error {
+	if offendingOutpoint, ok := checkIntersectionWithRule(other.toRemove, this.toRemove,
+		func(outpoint *externalapi.DomainOutpoint, entryToAdd, existingEntry externalapi.UTXOEntry) bool {
+			return !this.toAdd.containsWithBlueScore(outpoint, entryToAdd.BlockBlueScore())
+		}); ok {
+		return errors.Errorf(
+			"withDiffInPlace: outpoint %s both in this.toRemove and in other.toRemove", offendingOutpoint)
+	}
+
+	if offendingOutpoint, ok := checkIntersectionWithRule(other.toAdd, this.toAdd,
+		func(outpoint *externalapi.DomainOutpoint, entryToAdd, existingEntry externalapi.UTXOEntry) bool {
+			return !other.toRemove.containsWithBlueScore(outpoint, existingEntry.BlockBlueScore())
+		}); ok {
+		return errors.Errorf(
+			"withDiffInPlace: outpoint %s both in this.toAdd and in other.toAdd", offendingOutpoint)
+	}
+
+	intersection := make(utxoCollection, minInt(len(other.toRemove), len(this.toAdd)))
+	// If not exists neither in toAdd nor in toRemove - add to toRemove
+	intersectionWithRemainderHavingBlueScoreInPlace(other.toRemove, this.toAdd, intersection, this.toRemove)
+	// If already exists in toAdd with the same blueScore - remove from toAdd
+	this.toAdd.removeMultiple(intersection)
+
+	intersection = make(utxoCollection, minInt(len(other.toAdd), len(this.toRemove)))
+	// If not exists neither in toAdd nor in toRemove, or exists in toRemove with different blueScore - add to toAdd
+	intersectionWithRemainderHavingBlueScoreInPlace(other.toAdd, this.toRemove, intersection, this.toAdd)
+	// If already exists in toRemove with the same blueScore - remove from toRemove
+	this.toRemove.removeMultiple(intersection)
+
+	return nil
+}
+
+// WithDiff applies provided diff to this diff, creating a new mutableUTXODiff, that would be the result if
+// first d, and than diff were applied to some base
+func withDiff(this *mutableUTXODiff, diff *mutableUTXODiff) (*mutableUTXODiff, error) {
+	clone := this.clone()
+
+	err := withDiffInPlace(clone, diff)
+	if err != nil {
+		return nil, err
+	}
+
+	return clone, nil
+}
diff --git a/domain/consensus/utils/utxo/immutable_utxo_diff.go b/domain/consensus/utils/utxo/immutable_utxo_diff.go
new file mode 100644
index 00000000..3bc7a4a6
--- /dev/null
+++ b/domain/consensus/utils/utxo/immutable_utxo_diff.go
@@ -0,0 +1,86 @@
+package utxo
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/pkg/errors"
+)
+
+type immutableUTXODiff struct {
+	mutableUTXODiff *mutableUTXODiff
+
+	isInvalidated bool
+}
+
+func (iud *immutableUTXODiff) ToAdd() model.UTXOCollection {
+	if iud.isInvalidated {
+		panic("Attempt to read from an invalidated UTXODiff")
+	}
+
+	return iud.mutableUTXODiff.ToAdd()
+}
+
+func (iud *immutableUTXODiff) ToRemove() model.UTXOCollection {
+	if iud.isInvalidated {
+		panic("Attempt to read from an invalidated UTXODiff")
+	}
+
+	return iud.mutableUTXODiff.ToRemove()
+}
+
+func (iud *immutableUTXODiff) WithDiff(other model.UTXODiff) (model.UTXODiff, error) {
+	if iud.isInvalidated {
+		panic("Attempt to read from an invalidated UTXODiff")
+	}
+
+	return iud.mutableUTXODiff.WithDiff(other)
+}
+
+func (iud *immutableUTXODiff) DiffFrom(other model.UTXODiff) (model.UTXODiff, error) {
+	if iud.isInvalidated {
+		panic("Attempt to read from an invalidated UTXODiff")
+	}
+
+	return iud.mutableUTXODiff.DiffFrom(other)
+}
+
+// NewUTXODiff creates an empty UTXODiff
+func NewUTXODiff() model.UTXODiff {
+	return newUTXODiff()
+}
+
+func newUTXODiff() *immutableUTXODiff {
+	return &immutableUTXODiff{
+		mutableUTXODiff: newMutableUTXODiff(),
+		isInvalidated:   false,
+	}
+}
+
+// NewUTXODiffFromCollections returns a new UTXODiff with the given toAdd and toRemove collections
+func NewUTXODiffFromCollections(toAdd, toRemove model.UTXOCollection) (model.UTXODiff, error) {
+	add, ok := toAdd.(utxoCollection)
+	if !ok {
+		return nil, errors.New("toAdd is not of type utxoCollection")
+	}
+	remove, ok := toRemove.(utxoCollection)
+	if !ok {
+		return nil, errors.New("toRemove is not of type utxoCollection")
+	}
+	return &immutableUTXODiff{
+		mutableUTXODiff: &mutableUTXODiff{
+			toAdd:    add,
+			toRemove: remove,
+		},
+	}, nil
+}
+
+func (iud *immutableUTXODiff) CloneMutable() model.MutableUTXODiff {
+	return iud.cloneMutable()
+}
+
+func (iud *immutableUTXODiff) cloneMutable() *mutableUTXODiff {
+	if iud == nil {
+		return nil
+	}
+
+	return iud.mutableUTXODiff.clone()
+}
diff --git a/domain/consensus/utils/utxo/mutable_utxo_diff.go b/domain/consensus/utils/utxo/mutable_utxo_diff.go
new file mode 100644
index 00000000..cae0b645
--- /dev/null
+++ b/domain/consensus/utils/utxo/mutable_utxo_diff.go
@@ -0,0 +1,160 @@
+package utxo
+
+import (
+	"fmt"
+
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
+	"github.com/pkg/errors"
+)
+
+type mutableUTXODiff struct {
+	toAdd    utxoCollection
+	toRemove utxoCollection
+
+	immutableReferences []*immutableUTXODiff
+}
+
+// NewMutableUTXODiff creates an empty mutable UTXO-Diff
+func NewMutableUTXODiff() model.MutableUTXODiff {
+	return newMutableUTXODiff()
+}
+
+func newMutableUTXODiff() *mutableUTXODiff {
+	return &mutableUTXODiff{
+		toAdd:    utxoCollection{},
+		toRemove: utxoCollection{},
+	}
+}
+
+func (mud *mutableUTXODiff) ToImmutable() model.UTXODiff {
+	immutableReference := &immutableUTXODiff{
+		mutableUTXODiff: mud,
+		isInvalidated:   false,
+	}
+
+	mud.immutableReferences = append(mud.immutableReferences, immutableReference)
+
+	return immutableReference
+}
+
+func (mud *mutableUTXODiff) invalidateImmutableReferences() {
+	for _, immutableReference := range mud.immutableReferences {
+		immutableReference.isInvalidated = true
+	}
+
+	mud.immutableReferences = nil
+}
+
+func (mud *mutableUTXODiff) WithDiff(other model.UTXODiff) (model.UTXODiff, error) {
+	o, ok := other.(*immutableUTXODiff)
+	if !ok {
+		return nil, errors.New("other is not of type *immutableUTXODiff")
+	}
+
+	result, err := withDiff(mud, o.mutableUTXODiff)
+	if err != nil {
+		return nil, err
+	}
+
+	return result.ToImmutable(), nil
+}
+
+func (mud *mutableUTXODiff) WithDiffInPlace(other model.UTXODiff) error {
+	o, ok := other.(*immutableUTXODiff)
+	if !ok {
+		return errors.New("other is not of type *immutableUTXODiff")
+	}
+
+	mud.invalidateImmutableReferences()
+
+	return withDiffInPlace(mud, o.mutableUTXODiff)
+}
+
+func (mud *mutableUTXODiff) DiffFrom(other model.UTXODiff) (model.UTXODiff, error) {
+	o, ok := other.(*immutableUTXODiff)
+	if !ok {
+		return nil, errors.New("other is not of type *immutableUTXODiff")
+	}
+
+	result, err := diffFrom(mud, o.mutableUTXODiff)
+	if err != nil {
+		return nil, err
+	}
+
+	return result.ToImmutable(), nil
+}
+
+func (mud *mutableUTXODiff) ToAdd() model.UTXOCollection {
+	return mud.toAdd
+}
+
+func (mud *mutableUTXODiff) ToRemove() model.UTXOCollection {
+	return mud.toRemove
+}
+func (mud *mutableUTXODiff) AddTransaction(transaction *externalapi.DomainTransaction, blockBlueScore uint64) error {
+	mud.invalidateImmutableReferences()
+
+	for _, input := range transaction.Inputs {
+		err := mud.removeEntry(&input.PreviousOutpoint, input.UTXOEntry)
+		if err != nil {
+			return err
+		}
+	}
+
+	isCoinbase := transactionhelper.IsCoinBase(transaction)
+	transactionID := *consensushashing.TransactionID(transaction)
+	for i, output := range transaction.Outputs {
+		outpoint := &externalapi.DomainOutpoint{
+			TransactionID: transactionID,
+			Index:         uint32(i),
+		}
+		entry := NewUTXOEntry(output.Value, output.ScriptPublicKey, isCoinbase, blockBlueScore)
+
+		err := mud.addEntry(outpoint, entry)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (mud *mutableUTXODiff) addEntry(outpoint *externalapi.DomainOutpoint, entry externalapi.UTXOEntry) error {
+	if mud.toRemove.containsWithBlueScore(outpoint, entry.BlockBlueScore()) {
+		mud.toRemove.remove(outpoint)
+	} else if mud.toAdd.Contains(outpoint) {
+		return errors.Errorf("AddEntry: Cannot add outpoint %s twice", outpoint)
+	} else {
+		mud.toAdd.add(outpoint, entry)
+	}
+	return nil
+}
+
+func (mud *mutableUTXODiff) removeEntry(outpoint *externalapi.DomainOutpoint, entry externalapi.UTXOEntry) error {
+	if mud.toAdd.containsWithBlueScore(outpoint, entry.BlockBlueScore()) {
+		mud.toAdd.remove(outpoint)
+	} else if mud.toRemove.Contains(outpoint) {
+		return errors.Errorf("removeEntry: Cannot remove outpoint %s twice", outpoint)
+	} else {
+		mud.toRemove.add(outpoint, entry)
+	}
+	return nil
+}
+
+func (mud *mutableUTXODiff) clone() *mutableUTXODiff {
+	if mud == nil {
+		return nil
+	}
+
+	return &mutableUTXODiff{
+		toAdd:    mud.toAdd.Clone(),
+		toRemove: mud.toRemove.Clone(),
+	}
+}
+
+func (mud *mutableUTXODiff) String() string {
+	return fmt.Sprintf("toAdd: %s; toRemove: %s", mud.toAdd, mud.toRemove)
+}
diff --git a/domain/consensus/utils/utxo/serialization.go b/domain/consensus/utils/utxo/serialization.go
index c7367da3..5ce29c77 100644
--- a/domain/consensus/utils/utxo/serialization.go
+++ b/domain/consensus/utils/utxo/serialization.go
@@ -4,19 +4,16 @@ import (
 	"bytes"
 	"io"
 
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/serialization"
-
 	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
-
 	"github.com/pkg/errors"
-
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 )
 
 const uint32Size = 4
 
 // SerializeUTXO returns the byte-slice representation for given UTXOEntry-outpoint pair
-func SerializeUTXO(entry *externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint) ([]byte, error) {
+func SerializeUTXO(entry externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint) ([]byte, error) {
 	w := &bytes.Buffer{}
 
 	err := serializeOutpoint(w, outpoint)
@@ -33,7 +30,7 @@ func SerializeUTXO(entry *externalapi.UTXOEntry, outpoint *externalapi.DomainOut
 }
 
 // DeserializeUTXO deserializes the given byte slice to UTXOEntry-outpoint pair
-func DeserializeUTXO(utxoBytes []byte) (entry *externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint, err error) {
+func DeserializeUTXO(utxoBytes []byte) (entry externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint, err error) {
 	r := bytes.NewReader(utxoBytes)
 	outpoint, err = deserializeOutpoint(r)
 	if err != nil {
@@ -92,19 +89,19 @@ func deserializeOutpoint(r io.Reader) (*externalapi.DomainOutpoint, error) {
 	}, nil
 }
 
-func serializeUTXOEntry(w io.Writer, entry *externalapi.UTXOEntry) error {
-	err := serialization.WriteElements(w, entry.BlockBlueScore, entry.Amount, entry.IsCoinbase)
+func serializeUTXOEntry(w io.Writer, entry externalapi.UTXOEntry) error {
+	err := serialization.WriteElements(w, entry.BlockBlueScore(), entry.Amount(), entry.IsCoinbase())
 	if err != nil {
 		return err
 	}
 
-	count := uint64(len(entry.ScriptPublicKey))
+	count := uint64(len(entry.ScriptPublicKey()))
 	err = serialization.WriteElement(w, count)
 	if err != nil {
 		return err
 	}
 
-	_, err = w.Write(entry.ScriptPublicKey)
+	_, err = w.Write(entry.ScriptPublicKey())
 	if err != nil {
 		return errors.WithStack(err)
 	}
@@ -112,23 +109,26 @@ func serializeUTXOEntry(w io.Writer, entry *externalapi.UTXOEntry) error {
 	return nil
 }
 
-func deserializeUTXOEntry(r io.Reader) (*externalapi.UTXOEntry, error) {
-	entry := &externalapi.UTXOEntry{}
-	err := serialization.ReadElements(r, entry.BlockBlueScore, entry.Amount, entry.IsCoinbase)
+func deserializeUTXOEntry(r io.Reader) (externalapi.UTXOEntry, error) {
+	var blockBlueScore uint64
+	var amount uint64
+	var isCoinbase bool
+	err := serialization.ReadElements(r, blockBlueScore, amount, isCoinbase)
 	if err != nil {
 		return nil, err
 	}
 
-	count := uint64(len(entry.ScriptPublicKey))
-	err = serialization.ReadElement(r, count)
+	var scriptPubKeyLen int
+	err = serialization.ReadElement(r, scriptPubKeyLen)
 	if err != nil {
 		return nil, err
 	}
 
-	_, err = r.Read(entry.ScriptPublicKey)
+	scriptPubKey := make([]byte, scriptPubKeyLen)
+	_, err = r.Read(scriptPubKey)
 	if err != nil {
 		return nil, errors.WithStack(err)
 	}
 
-	return entry, nil
+	return NewUTXOEntry(amount, scriptPubKey, isCoinbase, blockBlueScore), nil
 }
diff --git a/domain/consensus/utils/utxo/utxo_collection.go b/domain/consensus/utils/utxo/utxo_collection.go
new file mode 100644
index 00000000..4e409123
--- /dev/null
+++ b/domain/consensus/utils/utxo/utxo_collection.go
@@ -0,0 +1,95 @@
+package utxo
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+)
+
+type utxoCollection map[externalapi.DomainOutpoint]externalapi.UTXOEntry
+
+// NewUTXOCollection creates a UTXO-Collection from the given map from outpoint to UTXOEntry
+func NewUTXOCollection(utxoMap map[externalapi.DomainOutpoint]externalapi.UTXOEntry) model.UTXOCollection {
+	return utxoCollection(utxoMap)
+}
+
+// Get returns the model.UTXOEntry represented by provided outpoint,
+// and a boolean value indicating if said model.UTXOEntry is in the set or not
+func (uc utxoCollection) Get(outpoint *externalapi.DomainOutpoint) (externalapi.UTXOEntry, bool) {
+	entry, ok := uc[*outpoint]
+	return entry, ok
+}
+
+// Contains returns a boolean value indicating whether a UTXO entry is in the set
+func (uc utxoCollection) Contains(outpoint *externalapi.DomainOutpoint) bool {
+	_, ok := uc[*outpoint]
+	return ok
+}
+
+func (uc utxoCollection) Len() int {
+	return len(uc)
+}
+
+func (uc utxoCollection) Clone() utxoCollection {
+	if uc == nil {
+		return nil
+	}
+
+	clone := make(utxoCollection, len(uc))
+	for outpoint, entry := range uc {
+		clone[outpoint] = entry
+	}
+
+	return clone
+}
+
+func (uc utxoCollection) String() string {
+	utxoStrings := make([]string, len(uc))
+
+	i := 0
+	for outpoint, utxoEntry := range uc {
+		utxoStrings[i] = fmt.Sprintf("(%s, %d) => %d, blueScore: %d",
+			outpoint.TransactionID, outpoint.Index, utxoEntry.Amount(), utxoEntry.BlockBlueScore())
+		i++
+	}
+
+	// Sort strings for determinism.
+	sort.Strings(utxoStrings)
+
+	return fmt.Sprintf("[ %s ]", strings.Join(utxoStrings, ", "))
+}
+
+// add adds a new UTXO entry to this collection
+func (uc utxoCollection) add(outpoint *externalapi.DomainOutpoint, entry externalapi.UTXOEntry) {
+	uc[*outpoint] = entry
+}
+
+// addMultiple adds multiple UTXO entries to this collection
+func (uc utxoCollection) addMultiple(collectionToAdd utxoCollection) {
+	for outpoint, entry := range collectionToAdd {
+		uc[outpoint] = entry
+	}
+}
+
+// remove removes a UTXO entry from this collection if it exists
+func (uc utxoCollection) remove(outpoint *externalapi.DomainOutpoint) {
+	delete(uc, *outpoint)
+}
+
+// removeMultiple removes multiple UTXO entries from this collection if it exists
+func (uc utxoCollection) removeMultiple(collectionToRemove utxoCollection) {
+	for outpoint := range collectionToRemove {
+		delete(uc, outpoint)
+	}
+}
+
+// containsWithBlueScore returns a boolean value indicating whether a model.UTXOEntry
+// is in the set and its blue score is equal to the given blue score.
+func (uc utxoCollection) containsWithBlueScore(outpoint *externalapi.DomainOutpoint, blueScore uint64) bool {
+	entry, ok := uc.Get(outpoint)
+	return ok && entry.BlockBlueScore() == blueScore
+}
diff --git a/domain/consensus/utils/utxo/utxo_entry.go b/domain/consensus/utils/utxo/utxo_entry.go
new file mode 100644
index 00000000..68b63793
--- /dev/null
+++ b/domain/consensus/utils/utxo/utxo_entry.go
@@ -0,0 +1,40 @@
+package utxo
+
+import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+
+type utxoEntry struct {
+	amount          uint64
+	scriptPublicKey []byte
+	blockBlueScore  uint64
+	isCoinbase      bool
+}
+
+// NewUTXOEntry creates a new utxoEntry representing the given txOut
+func NewUTXOEntry(amount uint64, scriptPubKey []byte, isCoinbase bool, blockBlueScore uint64) externalapi.UTXOEntry {
+	scriptPubKeyClone := make([]byte, len(scriptPubKey))
+	copy(scriptPubKeyClone, scriptPubKey)
+	return &utxoEntry{
+		amount:          amount,
+		scriptPublicKey: scriptPubKeyClone,
+		blockBlueScore:  blockBlueScore,
+		isCoinbase:      isCoinbase,
+	}
+}
+
+func (u *utxoEntry) Amount() uint64 {
+	return u.amount
+}
+
+func (u *utxoEntry) ScriptPublicKey() []byte {
+	clone := make([]byte, len(u.scriptPublicKey))
+	copy(clone, u.scriptPublicKey)
+	return clone
+}
+
+func (u *utxoEntry) BlockBlueScore() uint64 {
+	return u.blockBlueScore
+}
+
+func (u *utxoEntry) IsCoinbase() bool {
+	return u.isCoinbase
+}
diff --git a/domain/consensus/utils/utxo/utxo_iterator.go b/domain/consensus/utils/utxo/utxo_iterator.go
index d75b33c5..b78154ba 100644
--- a/domain/consensus/utils/utxo/utxo_iterator.go
+++ b/domain/consensus/utils/utxo/utxo_iterator.go
@@ -3,11 +3,12 @@ package utxo
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/pkg/errors"
 )
 
 type utxoOutpointEntryPair struct {
 	outpoint externalapi.DomainOutpoint
-	entry    *externalapi.UTXOEntry
+	entry    externalapi.UTXOEntry
 }
 
 type utxoCollectionIterator struct {
@@ -15,11 +16,10 @@ type utxoCollectionIterator struct {
 	pairs []utxoOutpointEntryPair
 }
 
-// CollectionIterator creates a utxo iterator from give UTXO collection
-func CollectionIterator(collection model.UTXOCollection) model.ReadOnlyUTXOSetIterator {
-	pairs := make([]utxoOutpointEntryPair, len(collection))
+func (uc utxoCollection) Iterator() model.ReadOnlyUTXOSetIterator {
+	pairs := make([]utxoOutpointEntryPair, len(uc))
 	i := 0
-	for outpoint, entry := range collection {
+	for outpoint, entry := range uc {
 		pairs[i] = utxoOutpointEntryPair{
 			outpoint: outpoint,
 			entry:    entry,
@@ -29,12 +29,25 @@ func CollectionIterator(collection model.UTXOCollection) model.ReadOnlyUTXOSetIt
 	return &utxoCollectionIterator{index: -1, pairs: pairs}
 }
 
-func (u *utxoCollectionIterator) Next() bool {
-	u.index++
-	return u.index < len(u.pairs)
+func (uci *utxoCollectionIterator) Next() bool {
+	uci.index++
+	return uci.index < len(uci.pairs)
 }
 
-func (u *utxoCollectionIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error) {
-	pair := u.pairs[u.index]
+func (uci *utxoCollectionIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry, err error) {
+	pair := uci.pairs[uci.index]
 	return &pair.outpoint, pair.entry, nil
 }
+
+func (uci *utxoCollectionIterator) WithDiff(diff model.UTXODiff) (model.ReadOnlyUTXOSetIterator, error) {
+	d, ok := diff.(*immutableUTXODiff)
+	if !ok {
+		return nil, errors.New("diff is not of type *immutableUTXODiff")
+	}
+
+	return &readOnlyUTXOIteratorWithDiff{
+		baseIterator:  uci,
+		diff:          d,
+		toAddIterator: diff.ToAdd().Iterator(),
+	}, nil
+}
diff --git a/domain/consensus/utils/utxo/utxo_iterator_with_diff.go b/domain/consensus/utils/utxo/utxo_iterator_with_diff.go
index f8b0da03..1a92aea0 100644
--- a/domain/consensus/utils/utxo/utxo_iterator_with_diff.go
+++ b/domain/consensus/utils/utxo/utxo_iterator_with_diff.go
@@ -3,24 +3,29 @@ package utxo
 import (
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo/utxoalgebra"
+	"github.com/pkg/errors"
 )
 
 type readOnlyUTXOIteratorWithDiff struct {
 	baseIterator model.ReadOnlyUTXOSetIterator
-	diff         *model.UTXODiff
+	diff         *immutableUTXODiff
 
 	currentOutpoint  *externalapi.DomainOutpoint
-	currentUTXOEntry *externalapi.UTXOEntry
+	currentUTXOEntry externalapi.UTXOEntry
 	currentErr       error
 
 	toAddIterator model.ReadOnlyUTXOSetIterator
 }
 
 // IteratorWithDiff applies a UTXODiff to given utxo iterator
-func IteratorWithDiff(iterator model.ReadOnlyUTXOSetIterator, diff *model.UTXODiff) (model.ReadOnlyUTXOSetIterator, error) {
+func IteratorWithDiff(iterator model.ReadOnlyUTXOSetIterator, diff model.UTXODiff) (model.ReadOnlyUTXOSetIterator, error) {
+	d, ok := diff.(*immutableUTXODiff)
+	if !ok {
+		return nil, errors.New("diff is not of type *immutableUTXODiff")
+	}
+
 	if iteratorWithDiff, ok := iterator.(*readOnlyUTXOIteratorWithDiff); ok {
-		combinedDiff, err := utxoalgebra.WithDiff(iteratorWithDiff.diff, diff)
+		combinedDiff, err := iteratorWithDiff.diff.WithDiff(d)
 		if err != nil {
 			return nil, err
 		}
@@ -30,15 +35,15 @@ func IteratorWithDiff(iterator model.ReadOnlyUTXOSetIterator, diff *model.UTXODi
 
 	return &readOnlyUTXOIteratorWithDiff{
 		baseIterator:  iterator,
-		diff:          diff,
-		toAddIterator: CollectionIterator(diff.ToAdd),
+		diff:          d,
+		toAddIterator: d.mutableUTXODiff.toAdd.Iterator(),
 	}, nil
 }
 
 func (r *readOnlyUTXOIteratorWithDiff) Next() bool {
-	for r.baseIterator.Next() { // keep looping until we reach an outpoint/entry pair that is not in r.diff.ToRemove
+	for r.baseIterator.Next() { // keep looping until we reach an outpoint/entry pair that is not in r.diff.toRemove
 		r.currentOutpoint, r.currentUTXOEntry, r.currentErr = r.baseIterator.Get()
-		if !utxoalgebra.CollectionContainsWithBlueScore(r.diff.ToRemove, r.currentOutpoint, r.currentUTXOEntry.BlockBlueScore) {
+		if !r.diff.mutableUTXODiff.toRemove.containsWithBlueScore(r.currentOutpoint, r.currentUTXOEntry.BlockBlueScore()) {
 			return true
 		}
 	}
@@ -51,6 +56,6 @@ func (r *readOnlyUTXOIteratorWithDiff) Next() bool {
 	return false
 }
 
-func (r *readOnlyUTXOIteratorWithDiff) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error) {
+func (r *readOnlyUTXOIteratorWithDiff) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry, err error) {
 	return r.currentOutpoint, r.currentUTXOEntry, r.currentErr
 }
diff --git a/domain/consensus/utils/utxo/utxoalgebra/collection_helpers.go b/domain/consensus/utils/utxo/utxoalgebra/collection_helpers.go
deleted file mode 100644
index f0e969cf..00000000
--- a/domain/consensus/utils/utxo/utxoalgebra/collection_helpers.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package utxoalgebra
-
-import (
-	"github.com/kaspanet/kaspad/domain/consensus/model"
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-)
-
-// add adds a new UTXO entry to this collection
-func collectionAdd(collection model.UTXOCollection, outpoint *externalapi.DomainOutpoint, entry *externalapi.UTXOEntry) {
-	collection[*outpoint] = entry
-}
-
-// addMultiple adds multiple UTXO entries to this collection
-func collectionAddMultiple(collection model.UTXOCollection, collectionToAdd model.UTXOCollection) {
-	for outpoint, entry := range collectionToAdd {
-		collection[outpoint] = entry
-	}
-}
-
-// remove removes a UTXO entry from this collection if it exists
-func collectionRemove(collection model.UTXOCollection, outpoint *externalapi.DomainOutpoint) {
-	delete(collection, *outpoint)
-}
-
-// removeMultiple removes multiple UTXO entries from this collection if it exists
-func collectionRemoveMultiple(collection model.UTXOCollection, collectionToRemove model.UTXOCollection) {
-	for outpoint := range collectionToRemove {
-		delete(collection, outpoint)
-	}
-}
-
-// CollectionGet returns the model.UTXOEntry represented by provided outpoint,
-// and a boolean value indicating if said model.UTXOEntry is in the set or not
-func CollectionGet(collection model.UTXOCollection, outpoint *externalapi.DomainOutpoint) (*externalapi.UTXOEntry, bool) {
-	entry, ok := collection[*outpoint]
-	return entry, ok
-}
-
-// CollectionContains returns a boolean value indicating whether a UTXO entry is in the set
-func CollectionContains(collection model.UTXOCollection, outpoint *externalapi.DomainOutpoint) bool {
-	_, ok := collection[*outpoint]
-	return ok
-}
-
-// CollectionContainsWithBlueScore returns a boolean value indicating whether a model.UTXOEntry
-// is in the set and its blue score is equal to the given blue score.
-func CollectionContainsWithBlueScore(collection model.UTXOCollection, outpoint *externalapi.DomainOutpoint, blueScore uint64) bool {
-	entry, ok := CollectionGet(collection, outpoint)
-	return ok && entry.BlockBlueScore == blueScore
-}
diff --git a/domain/consensus/utils/utxo/utxoalgebra/diff_algebra.go b/domain/consensus/utils/utxo/utxoalgebra/diff_algebra.go
deleted file mode 100644
index ad186c74..00000000
--- a/domain/consensus/utils/utxo/utxoalgebra/diff_algebra.go
+++ /dev/null
@@ -1,253 +0,0 @@
-package utxoalgebra
-
-import (
-	"github.com/kaspanet/kaspad/domain/consensus/model"
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/pkg/errors"
-)
-
-// checkIntersection checks if there is an intersection between two model.UTXOCollections
-func checkIntersection(collection1 model.UTXOCollection, collection2 model.UTXOCollection) bool {
-	for outpoint := range collection1 {
-		if CollectionContains(collection2, &outpoint) {
-			return true
-		}
-	}
-
-	return false
-}
-
-// checkIntersectionWithRule checks if there is an intersection between two model.UTXOCollections satisfying arbitrary rule
-// returns the first outpoint in the two collections' intersection satsifying the rule, and a boolean indicating whether
-// such outpoint exists
-func checkIntersectionWithRule(collection1 model.UTXOCollection, collection2 model.UTXOCollection,
-	extraRule func(*externalapi.DomainOutpoint, *externalapi.UTXOEntry, *externalapi.UTXOEntry) bool) (
-	*externalapi.DomainOutpoint, bool) {
-
-	for outpoint, utxoEntry := range collection1 {
-		if diffEntry, ok := CollectionGet(collection2, &outpoint); ok {
-			if extraRule(&outpoint, utxoEntry, diffEntry) {
-				return &outpoint, true
-			}
-		}
-	}
-
-	return nil, false
-}
-
-// minInt returns the smaller of x or y integer values
-func minInt(a, b int) int {
-	if a < b {
-		return a
-	}
-	return b
-}
-
-// intersectionWithRemainderHavingBlueScore calculates an intersection between two model.UTXOCollections
-// having same blue score, returns the result and the remainder from collection1
-func intersectionWithRemainderHavingBlueScore(collection1, collection2 model.UTXOCollection) (result, remainder model.UTXOCollection) {
-	result = make(model.UTXOCollection, minInt(len(collection1), len(collection2)))
-	remainder = make(model.UTXOCollection, len(collection1))
-	intersectionWithRemainderHavingBlueScoreInPlace(collection1, collection2, result, remainder)
-	return
-}
-
-// intersectionWithRemainderHavingBlueScoreInPlace calculates an intersection between two model.UTXOCollections
-// having same blue score, puts it into result and into remainder from collection1
-func intersectionWithRemainderHavingBlueScoreInPlace(collection1, collection2, result, remainder model.UTXOCollection) {
-	for outpoint, utxoEntry := range collection1 {
-		if CollectionContainsWithBlueScore(collection2, &outpoint, utxoEntry.BlockBlueScore) {
-			collectionAdd(result, &outpoint, utxoEntry)
-		} else {
-			collectionAdd(remainder, &outpoint, utxoEntry)
-		}
-	}
-}
-
-// subtractionHavingBlueScore calculates a subtraction between collection1 and collection2
-// having same blue score, returns the result
-func subtractionHavingBlueScore(collection1, collection2 model.UTXOCollection) (result model.UTXOCollection) {
-	result = make(model.UTXOCollection, len(collection1))
-
-	subtractionHavingBlueScoreInPlace(collection1, collection2, result)
-	return
-}
-
-// subtractionHavingBlueScoreInPlace calculates a subtraction between collection1 and collection2
-// having same blue score, puts it into result
-func subtractionHavingBlueScoreInPlace(collection1, collection2, result model.UTXOCollection) {
-	for outpoint, utxoEntry := range collection1 {
-		if !CollectionContainsWithBlueScore(collection2, &outpoint, utxoEntry.BlockBlueScore) {
-			collectionAdd(result, &outpoint, utxoEntry)
-		}
-	}
-}
-
-// subtractionWithRemainderHavingBlueScore calculates a subtraction between collection1 and collection2
-// having same blue score, returns the result and the remainder from collection1
-func subtractionWithRemainderHavingBlueScore(collection1, collection2 model.UTXOCollection) (result, remainder model.UTXOCollection) {
-	result = make(model.UTXOCollection, len(collection1))
-	remainder = make(model.UTXOCollection, len(collection1))
-
-	subtractionWithRemainderHavingBlueScoreInPlace(collection1, collection2, result, remainder)
-	return
-}
-
-// subtractionWithRemainderHavingBlueScoreInPlace calculates a subtraction between collection1 and collection2
-// having same blue score, puts it into result and into remainder from collection1
-func subtractionWithRemainderHavingBlueScoreInPlace(collection1, collection2, result, remainder model.UTXOCollection) {
-	for outpoint, utxoEntry := range collection1 {
-		if !CollectionContainsWithBlueScore(collection2, &outpoint, utxoEntry.BlockBlueScore) {
-			collectionAdd(result, &outpoint, utxoEntry)
-		} else {
-			collectionAdd(remainder, &outpoint, utxoEntry)
-		}
-	}
-}
-
-// DiffFrom returns a new utxoDiff with the difference between this utxoDiff and another
-// Assumes that:
-// Both utxoDiffs are from the same base
-// If a txOut exists in both utxoDiffs, its underlying values would be the same
-//
-// diffFrom follows a set of rules represented by the following 3 by 3 table:
-//
-//          |           | this      |           |
-// ---------+-----------+-----------+-----------+-----------
-//          |           | ToAdd     | ToRemove  | None
-// ---------+-----------+-----------+-----------+-----------
-// other    | ToAdd     | -         | X         | ToAdd
-// ---------+-----------+-----------+-----------+-----------
-//          | ToRemove  | X         | -         | ToRemove
-// ---------+-----------+-----------+-----------+-----------
-//          | None      | ToRemove  | ToAdd     | -
-//
-// Key:
-// -		Don't add anything to the result
-// X		Return an error
-// ToAdd	Add the UTXO into the ToAdd collection of the result
-// ToRemove	Add the UTXO into the ToRemove collection of the result
-//
-// Examples:
-// 1. This diff contains a UTXO in ToAdd, and the other diff contains it in ToRemove
-//    diffFrom results in an error
-// 2. This diff contains a UTXO in ToRemove, and the other diff does not contain it
-//    diffFrom results in the UTXO being added to ToAdd
-func DiffFrom(this, other *model.UTXODiff) (*model.UTXODiff, error) {
-	// Note that the following cases are not accounted for, as they are impossible
-	// as long as the base utxoSet is the same:
-	// - if utxoEntry is in this.ToAdd and other.ToRemove
-	// - if utxoEntry is in this.ToRemove and other.ToAdd
-
-	// check that NOT (entries with unequal blue scores AND utxoEntry is in this.ToAdd and/or other.ToRemove) -> Error
-	isNotAddedOutputRemovedWithBlueScore := func(outpoint *externalapi.DomainOutpoint, utxoEntry, diffEntry *externalapi.UTXOEntry) bool {
-		return !(diffEntry.BlockBlueScore != utxoEntry.BlockBlueScore &&
-			(CollectionContainsWithBlueScore(this.ToAdd, outpoint, diffEntry.BlockBlueScore) ||
-				CollectionContainsWithBlueScore(other.ToRemove, outpoint, utxoEntry.BlockBlueScore)))
-	}
-
-	if offendingOutpoint, ok :=
-		checkIntersectionWithRule(this.ToRemove, other.ToAdd, isNotAddedOutputRemovedWithBlueScore); ok {
-		return nil, errors.Errorf("diffFrom: outpoint %s both in this.ToAdd and in other.ToRemove", offendingOutpoint)
-	}
-
-	//check that NOT (entries with unequal blue score AND utxoEntry is in this.ToRemove and/or other.ToAdd) -> Error
-	isNotRemovedOutputAddedWithBlueScore :=
-		func(outpoint *externalapi.DomainOutpoint, utxoEntry, diffEntry *externalapi.UTXOEntry) bool {
-
-			return !(diffEntry.BlockBlueScore != utxoEntry.BlockBlueScore &&
-				(CollectionContainsWithBlueScore(this.ToRemove, outpoint, diffEntry.BlockBlueScore) ||
-					CollectionContainsWithBlueScore(other.ToAdd, outpoint, utxoEntry.BlockBlueScore)))
-		}
-
-	if offendingOutpoint, ok :=
-		checkIntersectionWithRule(this.ToAdd, other.ToRemove, isNotRemovedOutputAddedWithBlueScore); ok {
-		return nil, errors.Errorf("diffFrom: outpoint %s both in this.ToRemove and in other.ToAdd", offendingOutpoint)
-	}
-
-	// if have the same entry in this.ToRemove and other.ToRemove
-	// and existing entry is with different blue score, in this case - this is an error
-	if offendingOutpoint, ok := checkIntersectionWithRule(this.ToRemove, other.ToRemove,
-		func(outpoint *externalapi.DomainOutpoint, utxoEntry, diffEntry *externalapi.UTXOEntry) bool {
-			return utxoEntry.BlockBlueScore != diffEntry.BlockBlueScore
-		}); ok {
-		return nil, errors.Errorf("diffFrom: outpoint %s both in this.ToRemove and other.ToRemove with different "+
-			"blue scores, with no corresponding entry in this.ToAdd", offendingOutpoint)
-	}
-
-	result := model.UTXODiff{
-		ToAdd:    make(model.UTXOCollection, len(this.ToRemove)+len(other.ToAdd)),
-		ToRemove: make(model.UTXOCollection, len(this.ToAdd)+len(other.ToRemove)),
-	}
-
-	// All transactions in this.ToAdd:
-	// If they are not in other.ToAdd - should be added in result.ToRemove
-	inBothToAdd := make(model.UTXOCollection, len(this.ToAdd))
-	subtractionWithRemainderHavingBlueScoreInPlace(this.ToAdd, other.ToAdd, result.ToRemove, inBothToAdd)
-	// If they are in other.ToRemove - base utxoSet is not the same
-	if checkIntersection(inBothToAdd, this.ToRemove) != checkIntersection(inBothToAdd, other.ToRemove) {
-		return nil, errors.New(
-			"diffFrom: outpoint both in this.ToAdd, other.ToAdd, and only one of this.ToRemove and other.ToRemove")
-	}
-
-	// All transactions in other.ToRemove:
-	// If they are not in this.ToRemove - should be added in result.ToRemove
-	subtractionHavingBlueScoreInPlace(other.ToRemove, this.ToRemove, result.ToRemove)
-
-	// All transactions in this.ToRemove:
-	// If they are not in other.ToRemove - should be added in result.ToAdd
-	subtractionHavingBlueScoreInPlace(this.ToRemove, other.ToRemove, result.ToAdd)
-
-	// All transactions in other.ToAdd:
-	// If they are not in this.ToAdd - should be added in result.ToAdd
-	subtractionHavingBlueScoreInPlace(other.ToAdd, this.ToAdd, result.ToAdd)
-
-	return &result, nil
-}
-
-// WithDiffInPlace applies provided diff to this diff in-place, that would be the result if
-// first d, and than diff were applied to the same base
-func WithDiffInPlace(this *model.UTXODiff, diff *model.UTXODiff) error {
-	if offendingOutpoint, ok := checkIntersectionWithRule(diff.ToRemove, this.ToRemove,
-		func(outpoint *externalapi.DomainOutpoint, entryToAdd, existingEntry *externalapi.UTXOEntry) bool {
-			return !CollectionContainsWithBlueScore(this.ToAdd, outpoint, entryToAdd.BlockBlueScore)
-		}); ok {
-		return errors.Errorf(
-			"withDiffInPlace: outpoint %s both in this.ToRemove and in diff.ToRemove", offendingOutpoint)
-	}
-
-	if offendingOutpoint, ok := checkIntersectionWithRule(diff.ToAdd, this.ToAdd,
-		func(outpoint *externalapi.DomainOutpoint, entryToAdd, existingEntry *externalapi.UTXOEntry) bool {
-			return !CollectionContainsWithBlueScore(diff.ToRemove, outpoint, existingEntry.BlockBlueScore)
-		}); ok {
-		return errors.Errorf(
-			"withDiffInPlace: outpoint %s both in this.ToAdd and in diff.ToAdd", offendingOutpoint)
-	}
-
-	intersection := make(model.UTXOCollection, minInt(len(diff.ToRemove), len(this.ToAdd)))
-	// If not exists neither in ToAdd nor in ToRemove - add to ToRemove
-	intersectionWithRemainderHavingBlueScoreInPlace(diff.ToRemove, this.ToAdd, intersection, this.ToRemove)
-	// If already exists in ToAdd with the same blueScore - remove from ToAdd
-	collectionRemoveMultiple(this.ToAdd, intersection)
-
-	intersection = make(model.UTXOCollection, minInt(len(diff.ToAdd), len(this.ToRemove)))
-	// If not exists neither in ToAdd nor in ToRemove, or exists in ToRemove with different blueScore - add to ToAdd
-	intersectionWithRemainderHavingBlueScoreInPlace(diff.ToAdd, this.ToRemove, intersection, this.ToAdd)
-	// If already exists in ToRemove with the same blueScore - remove from ToRemove
-	collectionRemoveMultiple(this.ToRemove, intersection)
-
-	return nil
-}
-
-// WithDiff applies provided diff to this diff, creating a new utxoDiff, that would be the result if
-// first d, and than diff were applied to some base
-func WithDiff(this *model.UTXODiff, diff *model.UTXODiff) (*model.UTXODiff, error) {
-	clone := this.Clone()
-
-	err := WithDiffInPlace(clone, diff)
-	if err != nil {
-		return nil, err
-	}
-
-	return clone, nil
-}
diff --git a/domain/consensus/utils/utxo/utxoalgebra/diff_helpers.go b/domain/consensus/utils/utxo/utxoalgebra/diff_helpers.go
deleted file mode 100644
index 9ad87038..00000000
--- a/domain/consensus/utils/utxo/utxoalgebra/diff_helpers.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package utxoalgebra
-
-import (
-	"reflect"
-
-	"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
-
-	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
-
-	"github.com/kaspanet/kaspad/domain/consensus/model"
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/pkg/errors"
-)
-
-// DiffAddTransaction modifies the provided utxoDiff with provided transaction.
-func DiffAddTransaction(utxoDiff *model.UTXODiff, transaction *externalapi.DomainTransaction, blockBlueScore uint64) error {
-	for _, input := range transaction.Inputs {
-		err := diffRemoveEntry(utxoDiff, &input.PreviousOutpoint, input.UTXOEntry)
-		if err != nil {
-			return err
-		}
-	}
-
-	isCoinbase := transactionhelper.IsCoinBase(transaction)
-	transactionID := *consensushashing.TransactionID(transaction)
-	for i, output := range transaction.Outputs {
-		outpoint := &externalapi.DomainOutpoint{
-			TransactionID: transactionID,
-			Index:         uint32(i),
-		}
-		entry := &externalapi.UTXOEntry{
-			Amount:          output.Value,
-			ScriptPublicKey: output.ScriptPublicKey,
-			BlockBlueScore:  blockBlueScore,
-			IsCoinbase:      isCoinbase,
-		}
-
-		err := diffAddEntry(utxoDiff, outpoint, entry)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func diffAddEntry(diff *model.UTXODiff, outpoint *externalapi.DomainOutpoint, entry *externalapi.UTXOEntry) error {
-	if CollectionContainsWithBlueScore(diff.ToRemove, outpoint, entry.BlockBlueScore) {
-		collectionRemove(diff.ToRemove, outpoint)
-	} else if _, exists := diff.ToAdd[*outpoint]; exists {
-		return errors.Errorf("AddEntry: Cannot add outpoint %s twice", outpoint)
-	} else {
-		collectionAdd(diff.ToAdd, outpoint, entry)
-	}
-	return nil
-}
-
-func diffRemoveEntry(diff *model.UTXODiff, outpoint *externalapi.DomainOutpoint, entry *externalapi.UTXOEntry) error {
-	if CollectionContainsWithBlueScore(diff.ToAdd, outpoint, entry.BlockBlueScore) {
-		collectionRemove(diff.ToAdd, outpoint)
-	} else if _, exists := diff.ToRemove[*outpoint]; exists {
-		return errors.Errorf("removeEntry: Cannot remove outpoint %s twice", outpoint)
-	} else {
-		collectionAdd(diff.ToRemove, outpoint, entry)
-	}
-	return nil
-}
-
-func diffEqual(this *model.UTXODiff, other *model.UTXODiff) bool {
-	if this == nil || other == nil {
-		return this == other
-	}
-
-	return reflect.DeepEqual(this.ToAdd, other.ToAdd) &&
-		reflect.DeepEqual(this.ToRemove, other.ToRemove)
-}
diff --git a/domain/miningmanager/mempool/mempool_utxoset.go b/domain/miningmanager/mempool/mempool_utxoset.go
index e13c71c4..830b8b51 100644
--- a/domain/miningmanager/mempool/mempool_utxoset.go
+++ b/domain/miningmanager/mempool/mempool_utxoset.go
@@ -3,6 +3,8 @@ package mempool
 import (
 	"math"
 
+	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
+
 	consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
 	"github.com/pkg/errors"
@@ -13,13 +15,13 @@ const unacceptedBlueScore = math.MaxUint64
 func newMempoolUTXOSet() *mempoolUTXOSet {
 	return &mempoolUTXOSet{
 		transactionByPreviousOutpoint: make(map[consensusexternalapi.DomainOutpoint]*consensusexternalapi.DomainTransaction),
-		poolUnspentOutputs:            make(map[consensusexternalapi.DomainOutpoint]*consensusexternalapi.UTXOEntry),
+		poolUnspentOutputs:            make(map[consensusexternalapi.DomainOutpoint]consensusexternalapi.UTXOEntry),
 	}
 }
 
 type mempoolUTXOSet struct {
 	transactionByPreviousOutpoint map[consensusexternalapi.DomainOutpoint]*consensusexternalapi.DomainTransaction
-	poolUnspentOutputs            map[consensusexternalapi.DomainOutpoint]*consensusexternalapi.UTXOEntry
+	poolUnspentOutputs            map[consensusexternalapi.DomainOutpoint]consensusexternalapi.UTXOEntry
 }
 
 // Populate UTXO Entries in the transaction, to allow chained txs.
@@ -67,12 +69,8 @@ func (mpus *mempoolUTXOSet) addTx(tx *consensusexternalapi.DomainTransaction) er
 		if _, exists := mpus.poolUnspentOutputs[outpoint]; exists {
 			return errors.Errorf("outpoint %s already exists", outpoint)
 		}
-		mpus.poolUnspentOutputs[outpoint] = &consensusexternalapi.UTXOEntry{
-			Amount:          txOut.Value,
-			ScriptPublicKey: txOut.ScriptPublicKey,
-			BlockBlueScore:  unacceptedBlueScore,
-			IsCoinbase:      false,
-		}
+		mpus.poolUnspentOutputs[outpoint] =
+			utxo.NewUTXOEntry(txOut.Value, txOut.ScriptPublicKey, false, unacceptedBlueScore)
 	}
 	return nil
 }
diff --git a/domain/miningmanager/mempool/policy.go b/domain/miningmanager/mempool/policy.go
index ddbae216..4e679f33 100644
--- a/domain/miningmanager/mempool/policy.go
+++ b/domain/miningmanager/mempool/policy.go
@@ -88,7 +88,7 @@ func checkInputsStandard(tx *consensusexternalapi.DomainTransaction) error {
 		// they have already been checked prior to calling this
 		// function.
 		entry := txIn.UTXOEntry
-		originScriptPubKey := entry.ScriptPublicKey
+		originScriptPubKey := entry.ScriptPublicKey()
 		switch txscript.GetScriptClass(originScriptPubKey) {
 		case txscript.ScriptHashTy:
 			numSigOps := txscript.GetPreciseSigOpCount(


Test patch:
diff --git a/domain/consensus/datastructures/consensusstatestore/consensusstatestore.go b/domain/consensus/datastructures/consensusstatestore/consensusstatestore.go
index c0ea148e..b3a37a78 100644
--- a/domain/consensus/datastructures/consensusstatestore/consensusstatestore.go
+++ b/domain/consensus/datastructures/consensusstatestore/consensusstatestore.go
@@ -9,7 +9,7 @@ import (
 type consensusStateStore struct {
 	tipsStaging               []*externalapi.DomainHash
 	virtualDiffParentsStaging []*externalapi.DomainHash
-	virtualUTXODiffStaging    *model.UTXODiff
+	virtualUTXODiffStaging    model.UTXODiff
 	virtualUTXOSetStaging     model.UTXOCollection
 
 	tipsCache               []*externalapi.DomainHash
diff --git a/domain/consensus/datastructures/consensusstatestore/utxo.go b/domain/consensus/datastructures/consensusstatestore/utxo.go
index 42ad6e02..6bc76113 100644
--- a/domain/consensus/datastructures/consensusstatestore/utxo.go
+++ b/domain/consensus/datastructures/consensusstatestore/utxo.go
@@ -19,12 +19,12 @@ func utxoKey(outpoint *externalapi.DomainOutpoint) (model.DBKey, error) {
 	return utxoSetBucket.Key(serializedOutpoint), nil
 }
 
-func (css *consensusStateStore) StageVirtualUTXODiff(virtualUTXODiff *model.UTXODiff) error {
+func (css *consensusStateStore) StageVirtualUTXODiff(virtualUTXODiff model.UTXODiff) error {
 	if css.virtualUTXOSetStaging != nil {
 		return errors.New("cannot stage virtual UTXO diff while virtual UTXO set is staged")
 	}
 
-	css.virtualUTXODiffStaging = virtualUTXODiff.Clone()
+	css.virtualUTXODiffStaging = virtualUTXODiff
 	return nil
 }
 
@@ -33,8 +33,13 @@ func (css *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction)
 		return nil
 	}
 
-	for toRemoveOutpoint := range css.virtualUTXODiffStaging.ToRemove {
-		dbKey, err := utxoKey(&toRemoveOutpoint)
+	toRemoveIterator := css.virtualUTXODiffStaging.ToRemove().Iterator()
+	for toRemoveIterator.Next() {
+		toRemoveOutpoint, _, err := toRemoveIterator.Get()
+		if err != nil {
+			return err
+		}
+		dbKey, err := utxoKey(toRemoveOutpoint)
 		if err != nil {
 			return err
 		}
@@ -44,8 +49,13 @@ func (css *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction)
 		}
 	}
 
-	for toAddOutpoint, toAddEntry := range css.virtualUTXODiffStaging.ToAdd {
-		dbKey, err := utxoKey(&toAddOutpoint)
+	toAddIterator := css.virtualUTXODiffStaging.ToAdd().Iterator()
+	for toAddIterator.Next() {
+		toAddOutpoint, toAddEntry, err := toAddIterator.Get()
+		if err != nil {
+			return err
+		}
+		dbKey, err := utxoKey(toAddOutpoint)
 		if err != nil {
 			return err
 		}
@@ -69,8 +79,13 @@ func (css *consensusStateStore) commitVirtualUTXOSet(dbTx model.DBTransaction) e
 		return nil
 	}
 
-	for outpoint, utxoEntry := range css.virtualUTXOSetStaging {
-		dbKey, err := utxoKey(&outpoint)
+	iterator := css.virtualUTXOSetStaging.Iterator()
+	for iterator.Next() {
+		outpoint, utxoEntry, err := iterator.Get()
+		if err != nil {
+			return err
+		}
+		dbKey, err := utxoKey(outpoint)
 		if err != nil {
 			return err
 		}
@@ -90,7 +105,7 @@ func (css *consensusStateStore) commitVirtualUTXOSet(dbTx model.DBTransaction) e
 }
 
 func (css *consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (
-	*externalapi.UTXOEntry, error) {
+	externalapi.UTXOEntry, error) {
 
 	if css.virtualUTXOSetStaging != nil {
 		return css.utxoByOutpointFromStagedVirtualUTXOSet(outpoint)
@@ -101,13 +116,13 @@ func (css *consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, outpoin
 
 func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
 	outpoint *externalapi.DomainOutpoint) (
-	*externalapi.UTXOEntry, error) {
+	externalapi.UTXOEntry, error) {
 
 	if css.virtualUTXODiffStaging != nil {
-		if _, ok := css.virtualUTXODiffStaging.ToRemove[*outpoint]; ok {
+		if css.virtualUTXODiffStaging.ToRemove().Contains(outpoint) {
 			return nil, errors.Errorf("outpoint was not found")
 		}
-		if utxoEntry, ok := css.virtualUTXODiffStaging.ToAdd[*outpoint]; ok {
+		if utxoEntry, ok := css.virtualUTXODiffStaging.ToAdd().Get(outpoint); ok {
 			return utxoEntry, nil
 		}
 	}
@@ -126,8 +141,8 @@ func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContex
 }
 
 func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) (
-	*externalapi.UTXOEntry, error) {
-	if utxoEntry, ok := css.virtualUTXOSetStaging[*outpoint]; ok {
+	externalapi.UTXOEntry, error) {
+	if utxoEntry, ok := css.virtualUTXOSetStaging.Get(outpoint); ok {
 		return utxoEntry, nil
 	}
 
@@ -146,10 +161,10 @@ func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbCon
 	outpoint *externalapi.DomainOutpoint) (bool, error) {
 
 	if css.virtualUTXODiffStaging != nil {
-		if _, ok := css.virtualUTXODiffStaging.ToRemove[*outpoint]; ok {
+		if css.virtualUTXODiffStaging.ToRemove().Contains(outpoint) {
 			return false, nil
 		}
-		if _, ok := css.virtualUTXODiffStaging.ToAdd[*outpoint]; ok {
+		if _, ok := css.virtualUTXODiffStaging.ToAdd().Get(outpoint); ok {
 			return true, nil
 		}
 	}
@@ -163,8 +178,7 @@ func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbCon
 }
 
 func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) bool {
-	_, ok := css.virtualUTXOSetStaging[*outpoint]
-	return ok
+	return css.virtualUTXOSetStaging.Contains(outpoint)
 }
 
 func (css *consensusStateStore) VirtualUTXOSetIterator(dbContext model.DBReader) (model.ReadOnlyUTXOSetIterator, error) {
@@ -193,7 +207,7 @@ func (u utxoSetIterator) Next() bool {
 	return u.cursor.Next()
 }
 
-func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error) {
+func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry, err error) {
 	key, err := u.cursor.Key()
 	if err != nil {
 		panic(err)
@@ -222,18 +236,19 @@ func (css *consensusStateStore) StageVirtualUTXOSet(virtualUTXOSetIterator model
 		return errors.New("cannot stage virtual UTXO set while virtual UTXO diff is staged")
 	}
 
-	css.virtualUTXOSetStaging = make(model.UTXOCollection)
+	utxoMap := make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry)
 	for virtualUTXOSetIterator.Next() {
 		outpoint, entry, err := virtualUTXOSetIterator.Get()
 		if err != nil {
 			return err
 		}
 
-		if _, exists := css.virtualUTXOSetStaging[*outpoint]; exists {
+		if _, exists := utxoMap[*outpoint]; exists {
 			return errors.Errorf("outpoint %s is found more than once in the given iterator", outpoint)
 		}
-		css.virtualUTXOSetStaging[*outpoint] = entry
+		utxoMap[*outpoint] = entry
 	}
+	css.virtualUTXOSetStaging = utxo.NewUTXOCollection(utxoMap)
 
 	return nil
 }
diff --git a/domain/consensus/datastructures/consensusstatestore/utxo_serialization.go b/domain/consensus/datastructures/consensusstatestore/utxo_serialization.go
index f4853f04..6da8fab3 100644
--- a/domain/consensus/datastructures/consensusstatestore/utxo_serialization.go
+++ b/domain/consensus/datastructures/consensusstatestore/utxo_serialization.go
@@ -10,7 +10,7 @@ func serializeOutpoint(outpoint *externalapi.DomainOutpoint) ([]byte, error) {
 	return proto.Marshal(serialization.DomainOutpointToDbOutpoint(outpoint))
 }
 
-func serializeUTXOEntry(entry *externalapi.UTXOEntry) ([]byte, error) {
+func serializeUTXOEntry(entry externalapi.UTXOEntry) ([]byte, error) {
 	return proto.Marshal(serialization.UTXOEntryToDBUTXOEntry(entry))
 }
 
@@ -24,7 +24,7 @@ func deserializeOutpoint(outpointBytes []byte) (*externalapi.DomainOutpoint, err
 	return serialization.DbOutpointToDomainOutpoint(dbOutpoint)
 }
 
-func deserializeUTXOEntry(entryBytes []byte) (*externalapi.UTXOEntry, error) {
+func deserializeUTXOEntry(entryBytes []byte) (externalapi.UTXOEntry, error) {
 	dbEntry := &serialization.DbUtxoEntry{}
 	err := proto.Unmarshal(entryBytes, dbEntry)
 	if err != nil {
diff --git a/domain/consensus/model/interface_datastructures_consensusstatestore.go b/domain/consensus/model/interface_datastructures_consensusstatestore.go
index 022f8c54..df8fd215 100644
--- a/domain/consensus/model/interface_datastructures_consensusstatestore.go
+++ b/domain/consensus/model/interface_datastructures_consensusstatestore.go
@@ -7,9 +7,9 @@ type ConsensusStateStore interface {
 	Store
 	IsStaged() bool
 
-	StageVirtualUTXODiff(virtualUTXODiff *UTXODiff) error
+	StageVirtualUTXODiff(virtualUTXODiff UTXODiff) error
 	StageVirtualUTXOSet(virtualUTXOSetIterator ReadOnlyUTXOSetIterator) error
-	UTXOByOutpoint(dbContext DBReader, outpoint *externalapi.DomainOutpoint) (*externalapi.UTXOEntry, error)
+	UTXOByOutpoint(dbContext DBReader, outpoint *externalapi.DomainOutpoint) (externalapi.UTXOEntry, error)
 	HasUTXOByOutpoint(dbContext DBReader, outpoint *externalapi.DomainOutpoint) (bool, error)
 	VirtualUTXOSetIterator(dbContext DBReader) (ReadOnlyUTXOSetIterator, error)
 
diff --git a/domain/consensus/model/testapi/test_block_builder.go b/domain/consensus/model/testapi/test_block_builder.go
index 129d975b..ce2e4d9b 100644
--- a/domain/consensus/model/testapi/test_block_builder.go
+++ b/domain/consensus/model/testapi/test_block_builder.go
@@ -12,5 +12,5 @@ type TestBlockBuilder interface {
 	// BuildBlockWithParents builds a block with provided parents, coinbaseData and transactions,
 	// and returns the block together with its past UTXO-diff from the virtual.
 	BuildBlockWithParents(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData,
-		transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, *model.UTXODiff, error)
+		transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, model.UTXODiff, error)
 }
diff --git a/domain/consensus/model/testapi/test_consensus.go b/domain/consensus/model/testapi/test_consensus.go
index a90804da..f745d48d 100644
--- a/domain/consensus/model/testapi/test_consensus.go
+++ b/domain/consensus/model/testapi/test_consensus.go
@@ -11,7 +11,8 @@ type TestConsensus interface {
 
 	DatabaseContext() model.DBReader
 
-	BuildBlockWithParents(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, *model.UTXODiff, error)
+	BuildBlockWithParents(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData,
+		transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, model.UTXODiff, error)
 
 	// AddBlock builds a block with given information, solves it, and adds to the DAG.
 	// Returns the hash of the added block
diff --git a/domain/consensus/model/testapi/test_consensus_state_manager.go b/domain/consensus/model/testapi/test_consensus_state_manager.go
index bf112cbb..251eae8a 100644
--- a/domain/consensus/model/testapi/test_consensus_state_manager.go
+++ b/domain/consensus/model/testapi/test_consensus_state_manager.go
@@ -8,7 +8,7 @@ import (
 // TestConsensusStateManager  adds to the main ConsensusStateManager methods required by tests
 type TestConsensusStateManager interface {
 	model.ConsensusStateManager
-	AddUTXOToMultiset(multiset model.Multiset, entry *externalapi.UTXOEntry,
+	AddUTXOToMultiset(multiset model.Multiset, entry externalapi.UTXOEntry,
 		outpoint *externalapi.DomainOutpoint) error
 	ResolveBlockStatus(blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error)
 	VirtualFinalityPoint() (*externalapi.DomainHash, error)
diff --git a/domain/consensus/processes/blockbuilder/test_block_builder.go b/domain/consensus/processes/blockbuilder/test_block_builder.go
index c9284d7f..967a1e4d 100644
--- a/domain/consensus/processes/blockbuilder/test_block_builder.go
+++ b/domain/consensus/processes/blockbuilder/test_block_builder.go
@@ -31,7 +31,7 @@ func NewTestBlockBuilder(baseBlockBuilder model.BlockBuilder, testConsensus test
 // and returns the block together with its past UTXO-diff from the virtual.
 func (bb *testBlockBuilder) BuildBlockWithParents(parentHashes []*externalapi.DomainHash,
 	coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (
-	*externalapi.DomainBlock, *model.UTXODiff, error) {
+	*externalapi.DomainBlock, model.UTXODiff, error) {
 
 	onEnd := logger.LogAndMeasureExecutionTime(log, "BuildBlockWithParents")
 	defer onEnd()
@@ -72,7 +72,7 @@ func (bb *testBlockBuilder) buildHeaderWithParents(parentHashes []*externalapi.D
 	}, nil
 }
 
-func (bb *testBlockBuilder) buildBlockWithParents(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, *model.UTXODiff, error) {
+func (bb *testBlockBuilder) buildBlockWithParents(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, model.UTXODiff, error) {
 
 	defer bb.testConsensus.DiscardAllStores()
 
diff --git a/domain/consensus/processes/consensusstatemanager/test_consensus_state_manager.go b/domain/consensus/processes/consensusstatemanager/test_consensus_state_manager.go
index b6314cc7..e10edc82 100644
--- a/domain/consensus/processes/consensusstatemanager/test_consensus_state_manager.go
+++ b/domain/consensus/processes/consensusstatemanager/test_consensus_state_manager.go
@@ -16,7 +16,7 @@ func NewTestConsensusStateManager(baseConsensusStateManager model.ConsensusState
 }
 
 func (csm testConsensusStateManager) AddUTXOToMultiset(
-	multiset model.Multiset, entry *externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint) error {
+	multiset model.Multiset, entry externalapi.UTXOEntry, outpoint *externalapi.DomainOutpoint) error {
 
 	return addUTXOToMultiset(multiset, entry, outpoint)
 }
diff --git a/domain/consensus/test_consensus.go b/domain/consensus/test_consensus.go
index 216befae..927739f1 100644
--- a/domain/consensus/test_consensus.go
+++ b/domain/consensus/test_consensus.go
@@ -18,7 +18,7 @@ type testConsensus struct {
 
 func (tc *testConsensus) BuildBlockWithParents(parentHashes []*externalapi.DomainHash,
 	coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (
-	*externalapi.DomainBlock, *model.UTXODiff, error) {
+	*externalapi.DomainBlock, model.UTXODiff, error) {
 
 	// Require write lock because BuildBlockWithParents stages temporary data
 	tc.lock.Lock()
diff --git a/domain/consensus/utils/utxo/diff_algebra_test.go b/domain/consensus/utils/utxo/diff_algebra_test.go
new file mode 100644
index 00000000..97b7efc4
--- /dev/null
+++ b/domain/consensus/utils/utxo/diff_algebra_test.go
@@ -0,0 +1,616 @@
+package utxo
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
+
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+)
+
+func (mud *mutableUTXODiff) equal(other *mutableUTXODiff) bool {
+	if mud == nil || other == nil {
+		return mud == other
+	}
+
+	return reflect.DeepEqual(mud.toAdd, other.toAdd) &&
+		reflect.DeepEqual(mud.toRemove, other.toRemove)
+}
+
+// TestUTXOCollection makes sure that utxoCollection cloning and string representations work as expected.
+func TestUTXOCollection(t *testing.T) {
+	txID0, _ := transactionid.FromString("0000000000000000000000000000000000000000000000000000000000000000")
+	txID1, _ := transactionid.FromString("1111111111111111111111111111111111111111111111111111111111111111")
+	outpoint0 := externalapi.NewDomainOutpoint(txID0, 0)
+	outpoint1 := externalapi.NewDomainOutpoint(txID1, 0)
+	utxoEntry0 := NewUTXOEntry(10, []byte{}, true, 0)
+	utxoEntry1 := NewUTXOEntry(20, []byte{}, false, 1)
+
+	// For each of the following test cases, we will:
+	// .String() the given collection and compare it to expectedStringWithMultiset
+	// .clone() the given collection and compare its value to itself (expected: equals) and its reference to itself (expected: not equal)
+	tests := []struct {
+		name           string
+		collection     utxoCollection
+		expectedString string
+	}{
+		{
+			name:           "empty collection",
+			collection:     utxoCollection{},
+			expectedString: "[  ]",
+		},
+		{
+			name: "one member",
+			collection: utxoCollection{
+				*outpoint0: utxoEntry1,
+			},
+			expectedString: "[ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 20, blueScore: 1 ]",
+		},
+		{
+			name: "two members",
+			collection: utxoCollection{
+				*outpoint0: utxoEntry0,
+				*outpoint1: utxoEntry1,
+			},
+			expectedString: "[ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0, (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]",
+		},
+	}
+
+	for _, test := range tests {
+		// Test utxoCollection string representation
+		collectionString := test.collection.String()
+		if collectionString != test.expectedString {
+			t.Errorf("unexpected string in test \"%s\". "+
+				"Expected: \"%s\", got: \"%s\".", test.name, test.expectedString, collectionString)
+		}
+
+		// Test utxoCollection cloning
+		collectionClone := test.collection.Clone()
+		if reflect.ValueOf(collectionClone).Pointer() == reflect.ValueOf(test.collection).Pointer() {
+			t.Errorf("collection is reference-equal to its clone in test \"%s\". ", test.name)
+		}
+		if !reflect.DeepEqual(test.collection, collectionClone) {
+			t.Errorf("collection is not equal to its clone in test \"%s\". "+
+				"Expected: \"%s\", got: \"%s\".", test.name, collectionString, collectionClone.String())
+		}
+	}
+}
+
+// TestutxoDiff makes sure that mutableUTXODiff creation, cloning, and string representations work as expected.
+func TestUTXODiff(t *testing.T) {
+	txID0, _ := transactionid.FromString("0000000000000000000000000000000000000000000000000000000000000000")
+	txID1, _ := transactionid.FromString("1111111111111111111111111111111111111111111111111111111111111111")
+	outpoint0 := externalapi.NewDomainOutpoint(txID0, 0)
+	outpoint1 := externalapi.NewDomainOutpoint(txID1, 0)
+	utxoEntry0 := NewUTXOEntry(10, []byte{}, true, 0)
+	utxoEntry1 := NewUTXOEntry(20, []byte{}, false, 1)
+
+	diff := newMutableUTXODiff()
+
+	if len(diff.toAdd) != 0 || len(diff.toRemove) != 0 {
+		t.Errorf("new diff is not empty")
+	}
+
+	err := diff.addEntry(outpoint0, utxoEntry0)
+	if err != nil {
+		t.Fatalf("error adding entry to utxo diff: %s", err)
+	}
+
+	err = diff.removeEntry(outpoint1, utxoEntry1)
+	if err != nil {
+		t.Fatalf("error adding entry to utxo diff: %s", err)
+	}
+
+	// Test mutableUTXODiff cloning
+	clonedDiff := diff.clone()
+	if clonedDiff == diff {
+		t.Errorf("cloned diff is reference-equal to the original")
+	}
+	if !reflect.DeepEqual(clonedDiff, diff) {
+		t.Errorf("cloned diff not equal to the original"+
+			"Original: \"%v\", cloned: \"%v\".", diff, clonedDiff)
+	}
+
+	// Test mutableUTXODiff string representation
+	expectedDiffString := "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]"
+	diffString := clonedDiff.String()
+	if diffString != expectedDiffString {
+		t.Errorf("unexpected diff string. "+
+			"Expected: \"%s\", got: \"%s\".", expectedDiffString, diffString)
+	}
+}
+
+// TestutxoDiffRules makes sure that all diffFrom and WithDiff rules are followed.
+// Each test case represents a cell in the two tables outlined in the documentation for mutableUTXODiff.
+func TestUTXODiffRules(t *testing.T) {
+	txID0, _ := transactionid.FromString("0000000000000000000000000000000000000000000000000000000000000000")
+	outpoint0 := externalapi.NewDomainOutpoint(txID0, 0)
+	utxoEntry1 := NewUTXOEntry(10, []byte{}, true, 0)
+	utxoEntry2 := NewUTXOEntry(20, []byte{}, true, 1)
+
+	// For each of the following test cases, we will:
+	// this.diffFrom(other) and compare it to expectedDiffFromResult
+	// this.WithDiff(other) and compare it to expectedWithDiffResult
+	// this.withDiffInPlace(other) and compare it to expectedWithDiffResult
+	//
+	// Note: an expected nil result means that we expect the respective operation to fail
+	// See the following spreadsheet for a summary of all test-cases:
+	// https://docs.google.com/spreadsheets/d/1E8G3mp5y1-yifouwLLXRLueSRfXdDRwRKFieYE07buY/edit?usp=sharing
+	tests := []struct {
+		name                   string
+		this                   *mutableUTXODiff
+		other                  *mutableUTXODiff
+		expectedDiffFromResult *mutableUTXODiff
+		expectedWithDiffResult *mutableUTXODiff
+	}{
+		{
+			name: "first toAdd in this, first toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toAdd in this, second in toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toAdd in this, second in toRemove in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+		},
+		{
+			name: "first in toAdd in this and other, second in toRemove in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toAdd in this and toRemove in other, second in toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{},
+			},
+		},
+		{
+			name: "first in toAdd in this, empty other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+		},
+		{
+			name: "first in toRemove in this and in toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+		},
+		{
+			name: "first in toRemove in this, second in toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+		},
+		{
+			name: "first in toRemove in this and other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toRemove in this, second in toRemove in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toRemove in this and toAdd in other, second in toRemove in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toRemove in this and other, second in toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{},
+			},
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toRemove in this, empty other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+		},
+		{
+			name: "first in toAdd in this and other, second in toRemove in this",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toAdd in this, second in toRemove in this and toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toAdd in this and toRemove in other, second in toRemove in this",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+		},
+		{
+			name: "first in toAdd in this, second in toRemove in this and in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toAdd and second in toRemove in both this and other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			expectedWithDiffResult: nil,
+		},
+		{
+			name: "first in toAdd in this and toRemove in other, second in toRemove in this and toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedDiffFromResult: nil,
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+		},
+		{
+			name: "first in toAdd and second in toRemove in this, empty other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry2},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+		},
+		{
+			name: "empty this, first in toAdd in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{},
+			},
+		},
+		{
+			name: "empty this, first in toRemove in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{*outpoint0: utxoEntry1},
+			},
+		},
+		{
+			name: "empty this, first in toAdd and second in toRemove in other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{*outpoint0: utxoEntry1},
+				toRemove: utxoCollection{*outpoint0: utxoEntry2},
+			},
+		},
+		{
+			name: "empty this, empty other",
+			this: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			other: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			expectedDiffFromResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+			expectedWithDiffResult: &mutableUTXODiff{
+				toAdd:    utxoCollection{},
+				toRemove: utxoCollection{},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		// diffFrom from test.this to test.other
+		diffResult, err := diffFrom(test.this, test.other)
+
+		// Test whether diffFrom returned an error
+		isDiffFromOk := err == nil
+		expectedIsDiffFromOk := test.expectedDiffFromResult != nil
+		if isDiffFromOk != expectedIsDiffFromOk {
+			t.Errorf("unexpected diffFrom error in test \"%s\". "+
+				"Expected: \"%t\", got: \"%t\".", test.name, expectedIsDiffFromOk, isDiffFromOk)
+		}
+
+		// If not error, test the diffFrom result
+		if isDiffFromOk && !test.expectedDiffFromResult.equal(diffResult) {
+			t.Errorf("unexpected diffFrom result in test \"%s\". "+
+				"Expected: \"%v\", got: \"%v\".", test.name, test.expectedDiffFromResult, diffResult)
+		}
+
+		// Make sure that WithDiff after diffFrom results in the original test.other
+		if isDiffFromOk {
+			otherResult, err := withDiff(test.this, diffResult)
+			if err != nil {
+				t.Errorf("WithDiff unexpectedly failed in test \"%s\": %s", test.name, err)
+			}
+			if !test.other.equal(otherResult) {
+				t.Errorf("unexpected WithDiff result in test \"%s\". "+
+					"Expected: \"%v\", got: \"%v\".", test.name, test.other, otherResult)
+			}
+		}
+
+		// WithDiff from test.this to test.other
+		withDiffResult, err := withDiff(test.this, test.other)
+
+		// Test whether WithDiff returned an error
+		isWithDiffOk := err == nil
+		expectedIsWithDiffOk := test.expectedWithDiffResult != nil
+		if isWithDiffOk != expectedIsWithDiffOk {
+			t.Errorf("unexpected WithDiff error in test \"%s\". "+
+				"Expected: \"%t\", got: \"%t\".", test.name, expectedIsWithDiffOk, isWithDiffOk)
+		}
+
+		// If not error, test the WithDiff result
+		if isWithDiffOk && !withDiffResult.equal(test.expectedWithDiffResult) {
+			t.Errorf("unexpected WithDiff result in test \"%s\". "+
+				"Expected: \"%v\", got: \"%v\".", test.name, test.expectedWithDiffResult, withDiffResult)
+		}
+
+		// Repeat WithDiff check test.this time using withDiffInPlace
+		thisClone := test.this.clone()
+		err = withDiffInPlace(thisClone, test.other)
+
+		// Test whether withDiffInPlace returned an error
+		isWithDiffInPlaceOk := err == nil
+		expectedIsWithDiffInPlaceOk := test.expectedWithDiffResult != nil
+		if isWithDiffInPlaceOk != expectedIsWithDiffInPlaceOk {
+			t.Errorf("unexpected withDiffInPlace error in test \"%s\". "+
+				"Expected: \"%t\", got: \"%t\".", test.name, expectedIsWithDiffInPlaceOk, isWithDiffInPlaceOk)
+		}
+
+		// If not error, test the withDiffInPlace result
+		if isWithDiffInPlaceOk && !thisClone.equal(test.expectedWithDiffResult) {
+			t.Errorf("unexpected withDiffInPlace result in test \"%s\". "+
+				"Expected: \"%v\", got: \"%v\".", test.name, test.expectedWithDiffResult, thisClone)
+		}
+
+		// Make sure that diffFrom after WithDiff results in the original test.other
+		if isWithDiffOk {
+			otherResult, err := diffFrom(test.this, withDiffResult)
+			if err != nil {
+				t.Errorf("diffFrom unexpectedly failed in test \"%s\": %s", test.name, err)
+			}
+			if !test.other.equal(otherResult) {
+				t.Errorf("unexpected diffFrom result in test \"%s\". "+
+					"Expected: \"%v\", got: \"%v\".", test.name, test.other, otherResult)
+			}
+		}
+	}
+}
diff --git a/domain/consensus/utils/utxo/serialization_test.go b/domain/consensus/utils/utxo/serialization_test.go
index 3f8808c5..cb4c1a78 100644
--- a/domain/consensus/utils/utxo/serialization_test.go
+++ b/domain/consensus/utils/utxo/serialization_test.go
@@ -12,12 +12,7 @@ func Benchmark_serializeUTXO(b *testing.B) {
 	if err != nil {
 		b.Fatalf("Error decoding scriptPublicKey string: %s", err)
 	}
-	entry := &externalapi.UTXOEntry{
-		Amount:          5000000000,
-		ScriptPublicKey: scriptPublicKey, // p2pkh
-		BlockBlueScore:  1432432,
-		IsCoinbase:      false,
-	}
+	entry := NewUTXOEntry(5000000000, scriptPublicKey, false, 1432432)
 	outpoint := &externalapi.DomainOutpoint{
 		TransactionID: externalapi.DomainTransactionID{
 			0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95,
diff --git a/domain/consensus/utils/utxo/utxoalgebra/diff_algebra_test.go b/domain/consensus/utils/utxo/utxoalgebra/diff_algebra_test.go
deleted file mode 100644
index e42e9514..00000000
--- a/domain/consensus/utils/utxo/utxoalgebra/diff_algebra_test.go
+++ /dev/null
@@ -1,608 +0,0 @@
-package utxoalgebra
-
-import (
-	"reflect"
-	"testing"
-
-	"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
-
-	"github.com/kaspanet/kaspad/domain/consensus/model"
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-)
-
-// TestUTXOCollection makes sure that model.UTXOCollection cloning and string representations work as expected.
-func TestUTXOCollection(t *testing.T) {
-	txID0, _ := transactionid.FromString("0000000000000000000000000000000000000000000000000000000000000000")
-	txID1, _ := transactionid.FromString("1111111111111111111111111111111111111111111111111111111111111111")
-	outpoint0 := externalapi.NewDomainOutpoint(txID0, 0)
-	outpoint1 := externalapi.NewDomainOutpoint(txID1, 0)
-	utxoEntry0 := externalapi.NewUTXOEntry(10, []byte{}, true, 0)
-	utxoEntry1 := externalapi.NewUTXOEntry(20, []byte{}, false, 1)
-
-	// For each of the following test cases, we will:
-	// .String() the given collection and compare it to expectedStringWithMultiset
-	// .clone() the given collection and compare its value to itself (expected: equals) and its reference to itself (expected: not equal)
-	tests := []struct {
-		name           string
-		collection     model.UTXOCollection
-		expectedString string
-	}{
-		{
-			name:           "empty collection",
-			collection:     model.UTXOCollection{},
-			expectedString: "[  ]",
-		},
-		{
-			name: "one member",
-			collection: model.UTXOCollection{
-				*outpoint0: utxoEntry1,
-			},
-			expectedString: "[ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 20, blueScore: 1 ]",
-		},
-		{
-			name: "two members",
-			collection: model.UTXOCollection{
-				*outpoint0: utxoEntry0,
-				*outpoint1: utxoEntry1,
-			},
-			expectedString: "[ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0, (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]",
-		},
-	}
-
-	for _, test := range tests {
-		// Test model.UTXOCollection string representation
-		collectionString := test.collection.String()
-		if collectionString != test.expectedString {
-			t.Errorf("unexpected string in test \"%s\". "+
-				"Expected: \"%s\", got: \"%s\".", test.name, test.expectedString, collectionString)
-		}
-
-		// Test model.UTXOCollection cloning
-		collectionClone := test.collection.Clone()
-		if reflect.ValueOf(collectionClone).Pointer() == reflect.ValueOf(test.collection).Pointer() {
-			t.Errorf("collection is reference-equal to its clone in test \"%s\". ", test.name)
-		}
-		if !reflect.DeepEqual(test.collection, collectionClone) {
-			t.Errorf("collection is not equal to its clone in test \"%s\". "+
-				"Expected: \"%s\", got: \"%s\".", test.name, collectionString, collectionClone.String())
-		}
-	}
-}
-
-// Testmodel.UTXODiff makes sure that utxoDiff creation, cloning, and string representations work as expected.
-func TestUTXODiff(t *testing.T) {
-	txID0, _ := transactionid.FromString("0000000000000000000000000000000000000000000000000000000000000000")
-	txID1, _ := transactionid.FromString("1111111111111111111111111111111111111111111111111111111111111111")
-	outpoint0 := externalapi.NewDomainOutpoint(txID0, 0)
-	outpoint1 := externalapi.NewDomainOutpoint(txID1, 0)
-	utxoEntry0 := externalapi.NewUTXOEntry(10, []byte{}, true, 0)
-	utxoEntry1 := externalapi.NewUTXOEntry(20, []byte{}, false, 1)
-
-	diff := model.NewUTXODiff()
-
-	if len(diff.ToAdd) != 0 || len(diff.ToRemove) != 0 {
-		t.Errorf("new diff is not empty")
-	}
-
-	err := diffAddEntry(diff, outpoint0, utxoEntry0)
-	if err != nil {
-		t.Fatalf("error adding entry to utxo diff: %s", err)
-	}
-
-	err = diffRemoveEntry(diff, outpoint1, utxoEntry1)
-	if err != nil {
-		t.Fatalf("error adding entry to utxo diff: %s", err)
-	}
-
-	// Test utxoDiff cloning
-	clonedDiff := diff.Clone()
-	if clonedDiff == diff {
-		t.Errorf("cloned diff is reference-equal to the original")
-	}
-	if !reflect.DeepEqual(clonedDiff, diff) {
-		t.Errorf("cloned diff not equal to the original"+
-			"Original: \"%v\", cloned: \"%v\".", diff, clonedDiff)
-	}
-
-	// Test utxoDiff string representation
-	expectedDiffString := "ToAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; ToRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]"
-	diffString := clonedDiff.String()
-	if diffString != expectedDiffString {
-		t.Errorf("unexpected diff string. "+
-			"Expected: \"%s\", got: \"%s\".", expectedDiffString, diffString)
-	}
-}
-
-// Testmodel.UTXODiffRules makes sure that all diffFrom and WithDiff rules are followed.
-// Each test case represents a cell in the two tables outlined in the documentation for utxoDiff.
-func TestUTXODiffRules(t *testing.T) {
-	txID0, _ := transactionid.FromString("0000000000000000000000000000000000000000000000000000000000000000")
-	outpoint0 := externalapi.NewDomainOutpoint(txID0, 0)
-	utxoEntry1 := externalapi.NewUTXOEntry(10, []byte{}, true, 0)
-	utxoEntry2 := externalapi.NewUTXOEntry(20, []byte{}, true, 1)
-
-	// For each of the following test cases, we will:
-	// this.diffFrom(other) and compare it to expectedDiffFromResult
-	// this.WithDiff(other) and compare it to expectedWithDiffResult
-	// this.withDiffInPlace(other) and compare it to expectedWithDiffResult
-	//
-	// Note: an expected nil result means that we expect the respective operation to fail
-	// See the following spreadsheet for a summary of all test-cases:
-	// https://docs.google.com/spreadsheets/d/1E8G3mp5y1-yifouwLLXRLueSRfXdDRwRKFieYE07buY/edit?usp=sharing
-	tests := []struct {
-		name                   string
-		this                   *model.UTXODiff
-		other                  *model.UTXODiff
-		expectedDiffFromResult *model.UTXODiff
-		expectedWithDiffResult *model.UTXODiff
-	}{
-		{
-			name: "first ToAdd in this, first ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToAdd in this, second in ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToAdd in this, second in ToRemove in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-		},
-		{
-			name: "first in ToAdd in this and other, second in ToRemove in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToAdd in this and ToRemove in other, second in ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{},
-			},
-		},
-		{
-			name: "first in ToAdd in this, empty other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-		},
-		{
-			name: "first in ToRemove in this and in ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-		},
-		{
-			name: "first in ToRemove in this, second in ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-		},
-		{
-			name: "first in ToRemove in this and other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToRemove in this, second in ToRemove in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToRemove in this and ToAdd in other, second in ToRemove in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToRemove in this and other, second in ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToRemove in this, empty other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-		},
-		{
-			name: "first in ToAdd in this and other, second in ToRemove in this",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToAdd in this, second in ToRemove in this and ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToAdd in this and ToRemove in other, second in ToRemove in this",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-		},
-		{
-			name: "first in ToAdd in this, second in ToRemove in this and in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToAdd and second in ToRemove in both this and other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedWithDiffResult: nil,
-		},
-		{
-			name: "first in ToAdd in this and ToRemove in other, second in ToRemove in this and ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedDiffFromResult: nil,
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-		},
-		{
-			name: "first in ToAdd and second in ToRemove in this, empty other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry2},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-		},
-		{
-			name: "empty this, first in ToAdd in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{},
-			},
-		},
-		{
-			name: "empty this, first in ToRemove in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry1},
-			},
-		},
-		{
-			name: "empty this, first in ToAdd and second in ToRemove in other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{*outpoint0: utxoEntry1},
-				ToRemove: model.UTXOCollection{*outpoint0: utxoEntry2},
-			},
-		},
-		{
-			name: "empty this, empty other",
-			this: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			other: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedDiffFromResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-			expectedWithDiffResult: &model.UTXODiff{
-				ToAdd:    model.UTXOCollection{},
-				ToRemove: model.UTXOCollection{},
-			},
-		},
-	}
-
-	for _, test := range tests {
-		// diffFrom from test.this to test.other
-		diffResult, err := DiffFrom(test.this, test.other)
-
-		// Test whether diffFrom returned an error
-		isDiffFromOk := err == nil
-		expectedIsDiffFromOk := test.expectedDiffFromResult != nil
-		if isDiffFromOk != expectedIsDiffFromOk {
-			t.Errorf("unexpected diffFrom error in test \"%s\". "+
-				"Expected: \"%t\", got: \"%t\".", test.name, expectedIsDiffFromOk, isDiffFromOk)
-		}
-
-		// If not error, test the diffFrom result
-		if isDiffFromOk && !diffEqual(test.expectedDiffFromResult, diffResult) {
-			t.Errorf("unexpected diffFrom result in test \"%s\". "+
-				"Expected: \"%v\", got: \"%v\".", test.name, test.expectedDiffFromResult, diffResult)
-		}
-
-		// Make sure that WithDiff after diffFrom results in the original test.other
-		if isDiffFromOk {
-			otherResult, err := WithDiff(test.this, diffResult)
-			if err != nil {
-				t.Errorf("WithDiff unexpectedly failed in test \"%s\": %s", test.name, err)
-			}
-			if !diffEqual(test.other, otherResult) {
-				t.Errorf("unexpected WithDiff result in test \"%s\". "+
-					"Expected: \"%v\", got: \"%v\".", test.name, test.other, otherResult)
-			}
-		}
-
-		// WithDiff from test.this to test.other
-		withDiffResult, err := WithDiff(test.this, test.other)
-
-		// Test whether WithDiff returned an error
-		isWithDiffOk := err == nil
-		expectedIsWithDiffOk := test.expectedWithDiffResult != nil
-		if isWithDiffOk != expectedIsWithDiffOk {
-			t.Errorf("unexpected WithDiff error in test \"%s\". "+
-				"Expected: \"%t\", got: \"%t\".", test.name, expectedIsWithDiffOk, isWithDiffOk)
-		}
-
-		// If not error, test the WithDiff result
-		if isWithDiffOk && !diffEqual(withDiffResult, test.expectedWithDiffResult) {
-			t.Errorf("unexpected WithDiff result in test \"%s\". "+
-				"Expected: \"%v\", got: \"%v\".", test.name, test.expectedWithDiffResult, withDiffResult)
-		}
-
-		// Repeat WithDiff check test.this time using withDiffInPlace
-		thisClone := test.this.Clone()
-		err = WithDiffInPlace(thisClone, test.other)
-
-		// Test whether withDiffInPlace returned an error
-		isWithDiffInPlaceOk := err == nil
-		expectedIsWithDiffInPlaceOk := test.expectedWithDiffResult != nil
-		if isWithDiffInPlaceOk != expectedIsWithDiffInPlaceOk {
-			t.Errorf("unexpected withDiffInPlace error in test \"%s\". "+
-				"Expected: \"%t\", got: \"%t\".", test.name, expectedIsWithDiffInPlaceOk, isWithDiffInPlaceOk)
-		}
-
-		// If not error, test the withDiffInPlace result
-		if isWithDiffInPlaceOk && !diffEqual(thisClone, test.expectedWithDiffResult) {
-			t.Errorf("unexpected withDiffInPlace result in test \"%s\". "+
-				"Expected: \"%v\", got: \"%v\".", test.name, test.expectedWithDiffResult, thisClone)
-		}
-
-		// Make sure that diffFrom after WithDiff results in the original test.other
-		if isWithDiffOk {
-			otherResult, err := DiffFrom(test.this, withDiffResult)
-			if err != nil {
-				t.Errorf("diffFrom unexpectedly failed in test \"%s\": %s", test.name, err)
-			}
-			if !diffEqual(test.other, otherResult) {
-				t.Errorf("unexpected diffFrom result in test \"%s\". "+
-					"Expected: \"%v\", got: \"%v\".", test.name, test.other, otherResult)
-			}
-		}
-	}
-}