Ori Newman d748089a14
Update the virtual after overriding the virtual UTXO set (#1811)
* Update the virtual after overriding the virtual utxo set

* Put the updateVirtual inside importVirtualUTXOSetAndPruningPointUTXOSet

* Add pruningPoint to importVirtualUTXOSetAndPruningPointUTXOSet

* Remove sanity check
2021-08-02 17:02:15 +03:00

270 lines
6.9 KiB
Go

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/utxo"
"github.com/pkg/errors"
)
var utxoSetBucketName = []byte("virtual-utxo-set")
func (css *consensusStateStore) utxoKey(outpoint *externalapi.DomainOutpoint) (model.DBKey, error) {
serializedOutpoint, err := serializeOutpoint(outpoint)
if err != nil {
return nil, err
}
return css.utxoSetBucket.Key(serializedOutpoint), nil
}
func (css *consensusStateStore) StageVirtualUTXODiff(stagingArea *model.StagingArea, virtualUTXODiff externalapi.UTXODiff) {
stagingShard := css.stagingShard(stagingArea)
stagingShard.virtualUTXODiffStaging = virtualUTXODiff
}
func (csss *consensusStateStagingShard) commitVirtualUTXODiff(dbTx model.DBTransaction) error {
if csss.virtualUTXODiffStaging == nil {
return nil
}
toRemoveIterator := csss.virtualUTXODiffStaging.ToRemove().Iterator()
defer toRemoveIterator.Close()
for ok := toRemoveIterator.First(); ok; ok = toRemoveIterator.Next() {
toRemoveOutpoint, _, err := toRemoveIterator.Get()
if err != nil {
return err
}
csss.store.virtualUTXOSetCache.Remove(toRemoveOutpoint)
dbKey, err := csss.store.utxoKey(toRemoveOutpoint)
if err != nil {
return err
}
err = dbTx.Delete(dbKey)
if err != nil {
return err
}
}
toAddIterator := csss.virtualUTXODiffStaging.ToAdd().Iterator()
defer toAddIterator.Close()
for ok := toAddIterator.First(); ok; ok = toAddIterator.Next() {
toAddOutpoint, toAddEntry, err := toAddIterator.Get()
if err != nil {
return err
}
csss.store.virtualUTXOSetCache.Add(toAddOutpoint, toAddEntry)
dbKey, err := csss.store.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
}
}
// Note: we don't discard the staging here since that's
// being done at the end of Commit()
return nil
}
func (css *consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, stagingArea *model.StagingArea,
outpoint *externalapi.DomainOutpoint) (externalapi.UTXOEntry, error) {
stagingShard := css.stagingShard(stagingArea)
return css.utxoByOutpointFromStagedVirtualUTXODiff(dbContext, stagingShard, outpoint)
}
func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
stagingShard *consensusStateStagingShard, outpoint *externalapi.DomainOutpoint) (externalapi.UTXOEntry, error) {
if stagingShard.virtualUTXODiffStaging != nil {
if stagingShard.virtualUTXODiffStaging.ToRemove().Contains(outpoint) {
return nil, errors.Errorf("outpoint was not found")
}
if utxoEntry, ok := stagingShard.virtualUTXODiffStaging.ToAdd().Get(outpoint); ok {
return utxoEntry, nil
}
}
if entry, ok := css.virtualUTXOSetCache.Get(outpoint); ok {
return entry, nil
}
key, err := css.utxoKey(outpoint)
if err != nil {
return nil, err
}
serializedUTXOEntry, err := dbContext.Get(key)
if err != nil {
return nil, err
}
entry, err := deserializeUTXOEntry(serializedUTXOEntry)
if err != nil {
return nil, err
}
css.virtualUTXOSetCache.Add(outpoint, entry)
return entry, nil
}
func (css *consensusStateStore) HasUTXOByOutpoint(dbContext model.DBReader, stagingArea *model.StagingArea,
outpoint *externalapi.DomainOutpoint) (bool, error) {
stagingShard := css.stagingShard(stagingArea)
return css.hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext, stagingShard, outpoint)
}
func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
stagingShard *consensusStateStagingShard, outpoint *externalapi.DomainOutpoint) (bool, error) {
if stagingShard.virtualUTXODiffStaging != nil {
if stagingShard.virtualUTXODiffStaging.ToRemove().Contains(outpoint) {
return false, nil
}
if _, ok := stagingShard.virtualUTXODiffStaging.ToAdd().Get(outpoint); ok {
return true, nil
}
}
key, err := css.utxoKey(outpoint)
if err != nil {
return false, err
}
return dbContext.Has(key)
}
func (css *consensusStateStore) VirtualUTXOs(dbContext model.DBReader, fromOutpoint *externalapi.DomainOutpoint, limit int) (
[]*externalapi.OutpointAndUTXOEntryPair, error) {
cursor, err := dbContext.Cursor(css.utxoSetBucket)
if err != nil {
return nil, err
}
defer cursor.Close()
if fromOutpoint != nil {
serializedFromOutpoint, err := serializeOutpoint(fromOutpoint)
if err != nil {
return nil, err
}
seekKey := css.utxoSetBucket.Key(serializedFromOutpoint)
err = cursor.Seek(seekKey)
if err != nil {
return nil, err
}
}
iterator := newCursorUTXOSetIterator(cursor)
defer iterator.Close()
outpointAndUTXOEntryPairs := make([]*externalapi.OutpointAndUTXOEntryPair, 0, limit)
for len(outpointAndUTXOEntryPairs) < limit && iterator.Next() {
outpoint, utxoEntry, err := iterator.Get()
if err != nil {
return nil, err
}
outpointAndUTXOEntryPairs = append(outpointAndUTXOEntryPairs, &externalapi.OutpointAndUTXOEntryPair{
Outpoint: outpoint,
UTXOEntry: utxoEntry,
})
}
return outpointAndUTXOEntryPairs, nil
}
func (css *consensusStateStore) VirtualUTXOSetIterator(dbContext model.DBReader, stagingArea *model.StagingArea) (
externalapi.ReadOnlyUTXOSetIterator, error) {
stagingShard := css.stagingShard(stagingArea)
cursor, err := dbContext.Cursor(css.utxoSetBucket)
if err != nil {
return nil, err
}
mainIterator := newCursorUTXOSetIterator(cursor)
if stagingShard.virtualUTXODiffStaging != nil {
return utxo.IteratorWithDiff(mainIterator, stagingShard.virtualUTXODiffStaging)
}
return mainIterator, nil
}
type utxoSetIterator struct {
cursor model.DBCursor
isClosed bool
}
func newCursorUTXOSetIterator(cursor model.DBCursor) externalapi.ReadOnlyUTXOSetIterator {
return &utxoSetIterator{cursor: cursor}
}
func (u utxoSetIterator) First() bool {
if u.isClosed {
panic("Tried using a closed utxoSetIterator")
}
return u.cursor.First()
}
func (u utxoSetIterator) Next() bool {
if u.isClosed {
panic("Tried using a closed utxoSetIterator")
}
return u.cursor.Next()
}
func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry, err error) {
if u.isClosed {
return nil, nil, errors.New("Tried using a closed utxoSetIterator")
}
key, err := u.cursor.Key()
if err != nil {
panic(err)
}
utxoEntryBytes, err := u.cursor.Value()
if err != nil {
return nil, nil, err
}
outpoint, err = deserializeOutpoint(key.Suffix())
if err != nil {
return nil, nil, err
}
utxoEntry, err = deserializeUTXOEntry(utxoEntryBytes)
if err != nil {
return nil, nil, err
}
return outpoint, utxoEntry, nil
}
func (u utxoSetIterator) Close() error {
if u.isClosed {
return errors.New("Tried using a closed utxoSetIterator")
}
u.isClosed = true
err := u.cursor.Close()
if err != nil {
return err
}
u.cursor = nil
return nil
}