Repository: kaspanet/kaspad
Pull request #992 (2020-11-02 14:06:27)

[NOD-1498] Consensus State Store


Patch:
diff --git a/domain/consensus/database/serialization/utxo_collection.go b/domain/consensus/database/serialization/utxo_collection.go
index 41ad7429..c8030ea1 100644
--- a/domain/consensus/database/serialization/utxo_collection.go
+++ b/domain/consensus/database/serialization/utxo_collection.go
@@ -10,7 +10,7 @@ func utxoCollectionToDBUTXOCollection(utxoCollection model.UTXOCollection) []*Db
 	for outpoint, entry := range utxoCollection {
 		items[i] = &DbUtxoCollectionItem{
 			Outpoint:  DomainOutpointToDbOutpoint(&outpoint),
-			UtxoEntry: utxoEntryToDBUTXOEntry(entry),
+			UtxoEntry: UTXOEntryToDBUTXOEntry(entry),
 		}
 		i++
 	}
@@ -26,7 +26,7 @@ func dbUTXOCollectionToUTXOCollection(items []*DbUtxoCollectionItem) (model.UTXO
 			return nil, err
 		}
 
-		collection[*outpoint] = dbUTXOEntryToUTXOEntry(item.UtxoEntry)
+		collection[*outpoint] = DBUTXOEntryToUTXOEntry(item.UtxoEntry)
 	}
 	return collection, nil
 }
diff --git a/domain/consensus/database/serialization/utxo_entry.go b/domain/consensus/database/serialization/utxo_entry.go
index 73931f21..4a17f6ee 100644
--- a/domain/consensus/database/serialization/utxo_entry.go
+++ b/domain/consensus/database/serialization/utxo_entry.go
@@ -4,7 +4,8 @@ import (
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 )
 
-func utxoEntryToDBUTXOEntry(utxoEntry *externalapi.UTXOEntry) *DbUtxoEntry {
+// UTXOEntryToDBUTXOEntry converts UTXOEntry to DbUtxoEntry
+func UTXOEntryToDBUTXOEntry(utxoEntry *externalapi.UTXOEntry) *DbUtxoEntry {
 	return &DbUtxoEntry{
 		Amount:          utxoEntry.Amount,
 		ScriptPublicKey: utxoEntry.ScriptPublicKey,
@@ -13,7 +14,8 @@ func utxoEntryToDBUTXOEntry(utxoEntry *externalapi.UTXOEntry) *DbUtxoEntry {
 	}
 }
 
-func dbUTXOEntryToUTXOEntry(dbUtxoEntry *DbUtxoEntry) *externalapi.UTXOEntry {
+// DBUTXOEntryToUTXOEntry convert DbUtxoEntry ro UTXOEntry
+func DBUTXOEntryToUTXOEntry(dbUtxoEntry *DbUtxoEntry) *externalapi.UTXOEntry {
 	return &externalapi.UTXOEntry{
 		Amount:          dbUtxoEntry.Amount,
 		ScriptPublicKey: dbUtxoEntry.ScriptPublicKey,
diff --git a/domain/consensus/model/interface_processes_consensusstatemanager.go b/domain/consensus/model/interface_processes_consensusstatemanager.go
index ce4cae7d..074c353d 100644
--- a/domain/consensus/model/interface_processes_consensusstatemanager.go
+++ b/domain/consensus/model/interface_processes_consensusstatemanager.go
@@ -7,4 +7,5 @@ type ConsensusStateManager interface {
 	AddBlockToVirtual(blockHash *externalapi.DomainHash) error
 	PopulateTransactionWithUTXOEntries(transaction *externalapi.DomainTransaction) error
 	SetPruningPointUTXOSet(pruningPoint *externalapi.DomainHash, serializedUTXOSet []byte) error
+	RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (ReadOnlyUTXOSetIterator, error)
 }
diff --git a/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go b/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go
index 16253d88..6dc90068 100644
--- a/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go
+++ b/domain/consensus/processes/consensusstatemanager/add_block_to_virtual.go
@@ -85,10 +85,7 @@ func (csm *consensusStateManager) addTip(newTipHash *externalapi.DomainHash) (ne
 		}
 	}
 
-	err = csm.consensusStateStore.StageTips(newTips)
-	if err != nil {
-		return nil, err
-	}
+	csm.consensusStateStore.StageTips(newTips)
 
 	return newTips, nil
 }
diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
index 2c1950ec..cdbf2337 100644
--- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
+++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go
@@ -165,3 +165,65 @@ func (csm *consensusStateManager) checkTransactionMass(
 
 	return true, accumulatedMassAfter
 }
+
+func (csm *consensusStateManager) RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (model.ReadOnlyUTXOSetIterator, error) {
+	diff, _, _, err := csm.calculatePastUTXOAndAcceptanceData(blockHash)
+	if err != nil {
+		return nil, err
+	}
+
+	virtualUTXOSetIterator, err := csm.consensusStateStore.VirtualUTXOSetIterator(csm.databaseContext)
+	if err != nil {
+		return nil, err
+	}
+
+	pastUTXO := model.NewUTXODiff()
+	for virtualUTXOSetIterator.Next() {
+		outpoint, utxoEntry := virtualUTXOSetIterator.Get()
+		pastUTXO.ToAdd[*outpoint] = utxoEntry
+	}
+
+	diff, err = utxoalgebra.WithDiff(pastUTXO, diff)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(diff.ToRemove) > 0 {
+		return nil, errors.New("diff.ToRemove is expected to be empty")
+	}
+
+	return newUTXOSetIterator(diff.ToAdd), nil
+}
+
+type utxoOutpointEntryPair struct {
+	outpoint externalapi.DomainOutpoint
+	entry    *externalapi.UTXOEntry
+}
+
+type utxoSetIterator struct {
+	index int
+	pairs []utxoOutpointEntryPair
+}
+
+func newUTXOSetIterator(collection model.UTXOCollection) *utxoSetIterator {
+	pairs := make([]utxoOutpointEntryPair, len(collection))
+	i := 0
+	for outpoint, entry := range collection {
+		pairs[i] = utxoOutpointEntryPair{
+			outpoint: outpoint,
+			entry:    entry,
+		}
+		i++
+	}
+	return &utxoSetIterator{index: 0, pairs: pairs}
+}
+
+func (u utxoSetIterator) Next() bool {
+	u.index++
+	return u.index != len(u.pairs)
+}
+
+func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry) {
+	pair := u.pairs[u.index]
+	return &pair.outpoint, pair.entry
+}
diff --git a/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go b/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
index c5b083d3..da0959f9 100644
--- a/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
+++ b/domain/consensus/processes/consensusstatemanager/pick_virtual_parents.go
@@ -137,8 +137,12 @@ func (csm *consensusStateManager) mergeSetIncrease(
 
 func (csm *consensusStateManager) boundedMergeBreakingParents(parents []*externalapi.DomainHash) (hashset.HashSet, error) {
 	// Temporarily set virtual to all parents, so that we can run ghostdag on it
-	csm.dagTopologyManager.SetParents(model.VirtualBlockHash, parents)
-	err := csm.ghostdagManager.GHOSTDAG(model.VirtualBlockHash)
+	err := csm.dagTopologyManager.SetParents(model.VirtualBlockHash, parents)
+	if err != nil {
+		return nil, err
+	}
+
+	err = csm.ghostdagManager.GHOSTDAG(model.VirtualBlockHash)
 	if err != nil {
 		return nil, err
 	}
diff --git a/domain/consensus/processes/consensusstatemanager/update_virtual.go b/domain/consensus/processes/consensusstatemanager/update_virtual.go
index 662cde07..6cb28817 100644
--- a/domain/consensus/processes/consensusstatemanager/update_virtual.go
+++ b/domain/consensus/processes/consensusstatemanager/update_virtual.go
@@ -13,7 +13,10 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain
 		return err
 	}
 
-	csm.dagTopologyManager.SetParents(model.VirtualBlockHash, virtualParents)
+	err = csm.dagTopologyManager.SetParents(model.VirtualBlockHash, virtualParents)
+	if err != nil {
+		return err
+	}
 
 	err = csm.ghostdagManager.GHOSTDAG(model.VirtualBlockHash)
 	if err != nil {
@@ -66,5 +69,7 @@ func (csm *consensusStateManager) updateVirtualDiffParents(
 		}
 	}
 
-	return csm.consensusStateStore.StageVirtualDiffParents(newVirtualDiffParents)
+	csm.consensusStateStore.StageVirtualDiffParents(newVirtualDiffParents)
+
+	return nil
 }
diff --git a/domain/consensus/utils/hashes/hash_array.go b/domain/consensus/utils/hashes/hash_array.go
new file mode 100644
index 00000000..0e491df1
--- /dev/null
+++ b/domain/consensus/utils/hashes/hash_array.go
@@ -0,0 +1,41 @@
+package hashes
+
+import (
+	"bytes"
+
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/pkg/errors"
+)
+
+// DeserializeHashSlice decodes an array of DomainHashes from byte-slice representation
+func DeserializeHashSlice(hashesBytes []byte) ([]*externalapi.DomainHash, error) {
+	if len(hashesBytes)%externalapi.DomainHashSize != 0 {
+		return nil, errors.Errorf("serialized hashes length is %d bytes, while it should be a multiple of %d",
+			len(hashesBytes), externalapi.DomainHashSize)
+	}
+
+	hashes := make([]*externalapi.DomainHash, 0, len(hashesBytes)/externalapi.DomainHashSize)
+
+	for i := 0; i < len(hashesBytes); i += externalapi.DomainHashSize {
+		hashBytes := hashesBytes[i : i+externalapi.DomainHashSize]
+		hash, err := FromBytes(hashBytes)
+		if err != nil {
+			return nil, err
+		}
+
+		hashes = append(hashes, hash)
+	}
+
+	return hashes, nil
+}
+
+// SerializeHashSlice encodes a slice of DomainHashes into a byte slice
+func SerializeHashSlice(hashes []*externalapi.DomainHash) []byte {
+	hashesBytes := make([][]byte, 0, len(hashes))
+
+	for _, hash := range hashes {
+		hashesBytes = append(hashesBytes, hash[:])
+	}
+
+	return bytes.Join(hashesBytes, []byte{})
+}
diff --git a/domain/consensus/utils/hashserialization/utxo.go b/domain/consensus/utils/hashserialization/utxo.go
index f8a6b232..d011f1e2 100644
--- a/domain/consensus/utils/hashserialization/utxo.go
+++ b/domain/consensus/utils/hashserialization/utxo.go
@@ -49,17 +49,9 @@ func serializeOutpoint(w io.Writer, outpoint *externalapi.DomainOutpoint) error
 }
 
 func serializeUTXOEntry(w io.Writer, entry *externalapi.UTXOEntry) error {
-	buf := [8 + 1 + 8]byte{}
-	// Encode the blueScore.
-	binary.LittleEndian.PutUint64(buf[:8], entry.BlockBlueScore)
-
-	buf[8] = serializeUTXOEntryFlags(entry)
-
-	binary.LittleEndian.PutUint64(buf[9:], entry.Amount)
-
-	_, err := w.Write(buf[:])
+	err := writeElements(w, entry.BlockBlueScore, entry.Amount, entry.IsCoinbase)
 	if err != nil {
-		return errors.WithStack(err)
+		return err
 	}
 
 	count := uint64(len(entry.ScriptPublicKey))
@@ -75,13 +67,3 @@ func serializeUTXOEntry(w io.Writer, entry *externalapi.UTXOEntry) error {
 
 	return nil
 }
-
-func serializeUTXOEntryFlags(entry *externalapi.UTXOEntry) uint8 {
-	var serializedFlags uint8 = 0
-
-	if entry.IsCoinbase {
-		serializedFlags |= 1
-	}
-
-	return serializedFlags
-}


Test patch:
diff --git a/domain/consensus/datastructures/consensusstatestore/consensusstatestore.go b/domain/consensus/datastructures/consensusstatestore/consensusstatestore.go
index a0593f0d..03a9007c 100644
--- a/domain/consensus/datastructures/consensusstatestore/consensusstatestore.go
+++ b/domain/consensus/datastructures/consensusstatestore/consensusstatestore.go
@@ -7,6 +7,9 @@ import (
 
 // consensusStateStore represents a store for the current consensus state
 type consensusStateStore struct {
+	stagedTips               []*externalapi.DomainHash
+	stagedVirtualDiffParents []*externalapi.DomainHash
+	stagedVirtualUTXODiff    *model.UTXODiff
 }
 
 // New instantiates a new ConsensusStateStore
@@ -15,41 +18,34 @@ func New() model.ConsensusStateStore {
 }
 
 func (c consensusStateStore) Discard() {
-	panic("implement me")
+	c.stagedTips = nil
+	c.stagedVirtualUTXODiff = nil
+	c.stagedVirtualDiffParents = nil
 }
 
 func (c consensusStateStore) Commit(dbTx model.DBTransaction) error {
-	panic("implement me")
+	err := c.commitTips(dbTx)
+	if err != nil {
+		return err
+	}
+	err = c.commitVirtualDiffParents(dbTx)
+	if err != nil {
+		return err
+	}
+	err = c.commitVirtualUTXODiff(dbTx)
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 func (c consensusStateStore) IsStaged() bool {
-	panic("implement me")
-}
-
-func (c consensusStateStore) StageVirtualUTXODiff(virtualUTXODiff *model.UTXODiff) {
-	panic("implement me")
-}
-
-func (c consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (*externalapi.UTXOEntry, error) {
-	panic("implement me")
-}
-
-func (c consensusStateStore) HasUTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (bool, error) {
-	panic("implement me")
-}
-
-func (c consensusStateStore) StageVirtualDiffParents(virtualDiffParents []*externalapi.DomainHash) error {
-	panic("implement me")
-}
-
-func (c consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
-	panic("implement me")
-}
-
-func (c consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
-	panic("implement me")
+	return c.stagedTips != nil ||
+		c.stagedVirtualDiffParents != nil ||
+		c.stagedVirtualUTXODiff != nil
 }
 
-func (c consensusStateStore) StageTips(tipHashes []*externalapi.DomainHash) error {
+func (c consensusStateStore) VirtualUTXOSetIterator(dbContext model.DBReader) (model.ReadOnlyUTXOSetIterator, error) {
 	panic("implement me")
 }
diff --git a/domain/consensus/datastructures/consensusstatestore/tips.go b/domain/consensus/datastructures/consensusstatestore/tips.go
new file mode 100644
index 00000000..a8cb146c
--- /dev/null
+++ b/domain/consensus/datastructures/consensusstatestore/tips.go
@@ -0,0 +1,39 @@
+package consensusstatestore
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
+)
+
+var tipsKey = dbkeys.MakeBucket().Key([]byte("tips"))
+
+func (c consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
+	if c.stagedTips != nil {
+		return c.stagedTips, nil
+	}
+
+	tipsBytes, err := dbContext.Get(tipsKey)
+	if err != nil {
+		return nil, err
+	}
+
+	return hashes.DeserializeHashSlice(tipsBytes)
+}
+
+func (c consensusStateStore) StageTips(tipHashes []*externalapi.DomainHash) {
+	c.stagedTips = tipHashes
+}
+
+func (c consensusStateStore) commitTips(dbTx model.DBTransaction) error {
+	tipsBytes := hashes.SerializeHashSlice(c.stagedTips)
+
+	err := dbTx.Put(tipsKey, tipsBytes)
+	if err != nil {
+		return err
+	}
+
+	c.stagedTips = nil
+	return nil
+}
diff --git a/domain/consensus/datastructures/consensusstatestore/utxo.go b/domain/consensus/datastructures/consensusstatestore/utxo.go
new file mode 100644
index 00000000..1cd4711d
--- /dev/null
+++ b/domain/consensus/datastructures/consensusstatestore/utxo.go
@@ -0,0 +1,94 @@
+package consensusstatestore
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
+	"github.com/kaspanet/kaspad/infrastructure/db/database"
+)
+
+var utxoSetBucket = dbkeys.MakeBucket([]byte("virtual-utxo-set"))
+
+func utxoKey(outpoint *externalapi.DomainOutpoint) (model.DBKey, error) {
+	serializedOutpoint, err := serializeOutpoint(outpoint)
+	if err != nil {
+		return nil, err
+	}
+
+	return utxoSetBucket.Key(serializedOutpoint), nil
+}
+
+func (c consensusStateStore) StageVirtualUTXODiff(virtualUTXODiff *model.UTXODiff) {
+	c.stagedVirtualUTXODiff = virtualUTXODiff
+}
+
+func (c consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) error {
+	for toRemoveOutpoint := range c.stagedVirtualUTXODiff.ToRemove {
+		dbKey, err := utxoKey(&toRemoveOutpoint)
+		if err != nil {
+			return err
+		}
+		err = dbTx.Delete(dbKey)
+		if err != nil {
+			return err
+		}
+	}
+
+	for toAddOutpoint, toAddEntry := range c.stagedVirtualUTXODiff.ToAdd {
+		dbKey, err := utxoKey(&toAddOutpoint)
+		if err != nil {
+			return err
+		}
+		serializedEntry, err := serializeUTXOEntry(toAddEntry)
+		if err != nil {
+			return err
+		}
+		err = dbTx.Put(dbKey, serializedEntry)
+		if err != nil {
+			return err
+		}
+	}
+
+	c.stagedVirtualUTXODiff = nil
+
+	return nil
+}
+
+func (c consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (
+	*externalapi.UTXOEntry, error) {
+
+	if _, ok := c.stagedVirtualUTXODiff.ToRemove[*outpoint]; ok {
+		return nil, database.ErrNotFound
+	}
+	if utxoEntry, ok := c.stagedVirtualUTXODiff.ToAdd[*outpoint]; ok {
+		return utxoEntry, nil
+	}
+
+	key, err := utxoKey(outpoint)
+	if err != nil {
+		return nil, err
+	}
+
+	serializedUTXOEntry, err := dbContext.Get(key)
+	if err != nil {
+		return nil, err
+	}
+
+	return deserializeUTXOEntry(serializedUTXOEntry)
+}
+
+func (c consensusStateStore) HasUTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (bool, error) {
+	if _, ok := c.stagedVirtualUTXODiff.ToRemove[*outpoint]; ok {
+		return false, database.ErrNotFound
+	}
+	if _, ok := c.stagedVirtualUTXODiff.ToAdd[*outpoint]; ok {
+		return true, nil
+	}
+
+	key, err := utxoKey(outpoint)
+	if err != nil {
+		return false, err
+	}
+
+	return dbContext.Has(key)
+}
diff --git a/domain/consensus/datastructures/consensusstatestore/utxo_serialization.go b/domain/consensus/datastructures/consensusstatestore/utxo_serialization.go
new file mode 100644
index 00000000..f4853f04
--- /dev/null
+++ b/domain/consensus/datastructures/consensusstatestore/utxo_serialization.go
@@ -0,0 +1,35 @@
+package consensusstatestore
+
+import (
+	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+)
+
+func serializeOutpoint(outpoint *externalapi.DomainOutpoint) ([]byte, error) {
+	return proto.Marshal(serialization.DomainOutpointToDbOutpoint(outpoint))
+}
+
+func serializeUTXOEntry(entry *externalapi.UTXOEntry) ([]byte, error) {
+	return proto.Marshal(serialization.UTXOEntryToDBUTXOEntry(entry))
+}
+
+func deserializeOutpoint(outpointBytes []byte) (*externalapi.DomainOutpoint, error) {
+	dbOutpoint := &serialization.DbOutpoint{}
+	err := proto.Unmarshal(outpointBytes, dbOutpoint)
+	if err != nil {
+		return nil, err
+	}
+
+	return serialization.DbOutpointToDomainOutpoint(dbOutpoint)
+}
+
+func deserializeUTXOEntry(entryBytes []byte) (*externalapi.UTXOEntry, error) {
+	dbEntry := &serialization.DbUtxoEntry{}
+	err := proto.Unmarshal(entryBytes, dbEntry)
+	if err != nil {
+		return nil, err
+	}
+
+	return serialization.DBUTXOEntryToUTXOEntry(dbEntry), nil
+}
diff --git a/domain/consensus/datastructures/consensusstatestore/virtual_diff_parents.go b/domain/consensus/datastructures/consensusstatestore/virtual_diff_parents.go
new file mode 100644
index 00000000..93c0ed2c
--- /dev/null
+++ b/domain/consensus/datastructures/consensusstatestore/virtual_diff_parents.go
@@ -0,0 +1,39 @@
+package consensusstatestore
+
+import (
+	"github.com/kaspanet/kaspad/domain/consensus/model"
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
+)
+
+var virtualDiffParentsKey = dbkeys.MakeBucket().Key([]byte("virtual-diff-parents"))
+
+func (c consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
+	if c.stagedVirtualDiffParents != nil {
+		return c.stagedVirtualDiffParents, nil
+	}
+
+	virtualDiffParentsBytes, err := dbContext.Get(virtualDiffParentsKey)
+	if err != nil {
+		return nil, err
+	}
+
+	return hashes.DeserializeHashSlice(virtualDiffParentsBytes)
+}
+
+func (c consensusStateStore) StageVirtualDiffParents(tipHashes []*externalapi.DomainHash) {
+	c.stagedVirtualDiffParents = tipHashes
+}
+
+func (c consensusStateStore) commitVirtualDiffParents(dbTx model.DBTransaction) error {
+	virtualDiffParentsBytes := hashes.SerializeHashSlice(c.stagedVirtualDiffParents)
+
+	err := dbTx.Put(virtualDiffParentsKey, virtualDiffParentsBytes)
+	if err != nil {
+		return err
+	}
+
+	c.stagedVirtualDiffParents = nil
+	return nil
+}
diff --git a/domain/consensus/model/interface_datastructures_consensusstatestore.go b/domain/consensus/model/interface_datastructures_consensusstatestore.go
index 18573b9d..845ed7f3 100644
--- a/domain/consensus/model/interface_datastructures_consensusstatestore.go
+++ b/domain/consensus/model/interface_datastructures_consensusstatestore.go
@@ -10,10 +10,11 @@ type ConsensusStateStore interface {
 	StageVirtualUTXODiff(virtualUTXODiff *UTXODiff)
 	UTXOByOutpoint(dbContext DBReader, outpoint *externalapi.DomainOutpoint) (*externalapi.UTXOEntry, error)
 	HasUTXOByOutpoint(dbContext DBReader, outpoint *externalapi.DomainOutpoint) (bool, error)
+	VirtualUTXOSetIterator(dbContext DBReader) (ReadOnlyUTXOSetIterator, error)
 
-	StageVirtualDiffParents(virtualDiffParents []*externalapi.DomainHash) error
+	StageVirtualDiffParents(virtualDiffParents []*externalapi.DomainHash)
 	VirtualDiffParents(dbContext DBReader) ([]*externalapi.DomainHash, error)
 
+	StageTips(tipHashes []*externalapi.DomainHash)
 	Tips(dbContext DBReader) ([]*externalapi.DomainHash, error)
-	StageTips(tipHashes []*externalapi.DomainHash) error
 }