mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
223 lines
9.4 KiB
Go
223 lines
9.4 KiB
Go
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
|
|
}
|
|
|
|
// intersectionWithRemainderHavingDAAScoreInPlace calculates an intersection between two utxoCollections
|
|
// having same DAA score, puts it into result and into remainder from collection1
|
|
func intersectionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
|
|
for outpoint, utxoEntry := range collection1 {
|
|
if collection2.containsWithDAAScore(&outpoint, utxoEntry.BlockDAAScore()) {
|
|
result.add(&outpoint, utxoEntry)
|
|
} else {
|
|
remainder.add(&outpoint, utxoEntry)
|
|
}
|
|
}
|
|
}
|
|
|
|
// subtractionHavingDAAScoreInPlace calculates a subtraction between collection1 and collection2
|
|
// having same DAA score, puts it into result
|
|
func subtractionHavingDAAScoreInPlace(collection1, collection2, result utxoCollection) {
|
|
for outpoint, utxoEntry := range collection1 {
|
|
if !collection2.containsWithDAAScore(&outpoint, utxoEntry.BlockDAAScore()) {
|
|
result.add(&outpoint, utxoEntry)
|
|
}
|
|
}
|
|
}
|
|
|
|
// subtractionWithRemainderHavingDAAScoreInPlace calculates a subtraction between collection1 and collection2
|
|
// having same DAA score, puts it into result and into remainder from collection1
|
|
func subtractionWithRemainderHavingDAAScoreInPlace(collection1, collection2, result, remainder utxoCollection) {
|
|
for outpoint, utxoEntry := range collection1 {
|
|
if !collection2.containsWithDAAScore(&outpoint, utxoEntry.BlockDAAScore()) {
|
|
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 DAA scores AND utxoEntry is in this.toAdd and/or other.toRemove) -> Error
|
|
isNotAddedOutputRemovedWithDAAScore := func(outpoint *externalapi.DomainOutpoint, utxoEntry, diffEntry externalapi.UTXOEntry) bool {
|
|
return !(diffEntry.BlockDAAScore() != utxoEntry.BlockDAAScore() &&
|
|
(this.toAdd.containsWithDAAScore(outpoint, diffEntry.BlockDAAScore()) ||
|
|
other.toRemove.containsWithDAAScore(outpoint, utxoEntry.BlockDAAScore())))
|
|
}
|
|
|
|
if offendingOutpoint, ok :=
|
|
checkIntersectionWithRule(this.toRemove, other.toAdd, isNotAddedOutputRemovedWithDAAScore); ok {
|
|
return nil, errors.Errorf("diffFrom: outpoint %s both in this.toAdd and in other.toRemove", offendingOutpoint)
|
|
}
|
|
|
|
//check that NOT (entries with unequal DAA score AND utxoEntry is in this.toRemove and/or other.toAdd) -> Error
|
|
isNotRemovedOutputAddedWithDAAScore :=
|
|
func(outpoint *externalapi.DomainOutpoint, utxoEntry, diffEntry externalapi.UTXOEntry) bool {
|
|
|
|
return !(diffEntry.BlockDAAScore() != utxoEntry.BlockDAAScore() &&
|
|
(this.toRemove.containsWithDAAScore(outpoint, diffEntry.BlockDAAScore()) ||
|
|
other.toAdd.containsWithDAAScore(outpoint, utxoEntry.BlockDAAScore())))
|
|
}
|
|
|
|
if offendingOutpoint, ok :=
|
|
checkIntersectionWithRule(this.toAdd, other.toRemove, isNotRemovedOutputAddedWithDAAScore); 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 DAA 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.BlockDAAScore() != diffEntry.BlockDAAScore()
|
|
}); ok {
|
|
return nil, errors.Errorf("diffFrom: outpoint %s both in this.toRemove and other.toRemove with different "+
|
|
"DAA scores, with no corresponding entry in this.toAdd", offendingOutpoint)
|
|
}
|
|
|
|
result := &mutableUTXODiff{
|
|
toAdd: make(utxoCollection),
|
|
toRemove: make(utxoCollection),
|
|
}
|
|
|
|
// All transactions in this.toAdd:
|
|
// If they are not in other.toAdd - should be added in result.toRemove
|
|
inBothToAdd := make(utxoCollection)
|
|
subtractionWithRemainderHavingDAAScoreInPlace(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
|
|
subtractionHavingDAAScoreInPlace(other.toRemove, this.toRemove, result.toRemove)
|
|
|
|
// All transactions in this.toRemove:
|
|
// If they are not in other.toRemove - should be added in result.toAdd
|
|
subtractionHavingDAAScoreInPlace(this.toRemove, other.toRemove, result.toAdd)
|
|
|
|
// All transactions in other.toAdd:
|
|
// If they are not in this.toAdd - should be added in result.toAdd
|
|
subtractionHavingDAAScoreInPlace(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.containsWithDAAScore(outpoint, entryToAdd.BlockDAAScore())
|
|
}); 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.containsWithDAAScore(outpoint, existingEntry.BlockDAAScore())
|
|
}); ok {
|
|
return errors.Errorf(
|
|
"withDiffInPlace: outpoint %s both in this.toAdd and in other.toAdd", offendingOutpoint)
|
|
}
|
|
|
|
intersection := make(utxoCollection)
|
|
// If not exists neither in toAdd nor in toRemove - add to toRemove
|
|
intersectionWithRemainderHavingDAAScoreInPlace(other.toRemove, this.toAdd, intersection, this.toRemove)
|
|
// If already exists in toAdd with the same DAA score - remove from toAdd
|
|
this.toAdd.removeMultiple(intersection)
|
|
|
|
intersection = make(utxoCollection)
|
|
// If not exists neither in toAdd nor in toRemove, or exists in toRemove with different DAA score - add to toAdd
|
|
intersectionWithRemainderHavingDAAScoreInPlace(other.toAdd, this.toRemove, intersection, this.toAdd)
|
|
// If already exists in toRemove with the same DAA score - 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
|
|
}
|