kaspad/domain/consensus/utils/utxo/mutable_utxo_diff.go
Svarog dfd8b3423d
Implement new mechanism for updating UTXO Diffs (#1671)
* Use selectedParent instead of selectedTip for non-selectedTip blocks in restoreSingleBlockStatus

* Cache the selectedParent for re-use in a resolveSingleBlockStatus chain

* Implement and use reverseUTXOSet

* Reverse blocks in correct order

* Support resolveBlockStatus without separate stagingAreas for usage of testConsensus

* Handle the case where the tip of the resolved block is not the next selectedTip

* Unify isResolveTip

* Some minor fixes and cleanup

* Add full finality window re-org test to stability-slow

* rename: useSeparateStagingAreasPerBlock -> useSeparateStagingAreaPerBlock

* Better logs in resolveSingleBlockStatus

* A few retouches to reverseUTXODiffs

* TEMPORARY COMMIT: EXTRAT ALL DIFFFROMS TO SEPARATE METHODS

* TEMPORARY COMMIT: REMOVE DIFFICULTY CHECKS IN DEVNET

* Don't pre-allocate in utxo-algebra, since the numbers are not known ahead-of-time

* Add some logs to reverseUTXODiffs

* Revert "TEMPORARY COMMIT: REMOVE DIFFICULTY CHECKS IN DEVNET"

This reverts commit c0af9dc6ade78a914c970e11bc63c34605565f57.

* Revert "TEMPORARY COMMIT: EXTRAT ALL DIFFFROMS TO SEPARATE METHODS"

This reverts commit 4fcca1b48c3a1183598833a355b9bfaf169edba1.

* Remove redundant paranthesis

* Revise some logs messages

* Rename:oneBlockBeforeCurrentUTXOSet -> lastResolvedBlockUTXOSet

* Don't break if the block was resolved as invalid

* rename unverifiedBlocks to recentlyVerifiedBlcks in reverseUTXODiffs

* Add errors.New to the panic, for a stack trace

* Reverse the UTXODiffs after the main block has been commited

* Use the correct value for previousUTXODiff

* Add test for ReverseUTXODiff

* Fix some names and comments

* Update TestReverseUTXODiffs to use consensus.Config

* Fix comments mentioning 'oneBlockBeforeTip'
2021-04-20 10:26:55 +03:00

168 lines
4.3 KiB
Go

package utxo
import (
"fmt"
"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() externalapi.MutableUTXODiff {
return newMutableUTXODiff()
}
func newMutableUTXODiff() *mutableUTXODiff {
return &mutableUTXODiff{
toAdd: utxoCollection{},
toRemove: utxoCollection{},
}
}
func (mud *mutableUTXODiff) ToImmutable() externalapi.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 externalapi.UTXODiff) (externalapi.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 externalapi.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 externalapi.UTXODiff) (externalapi.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() externalapi.UTXOCollection {
return mud.toAdd
}
func (mud *mutableUTXODiff) ToRemove() externalapi.UTXOCollection {
return mud.toRemove
}
func (mud *mutableUTXODiff) AddTransaction(transaction *externalapi.DomainTransaction, blockDAAScore 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, blockDAAScore)
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.containsWithDAAScore(outpoint, entry.BlockDAAScore()) {
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.containsWithDAAScore(outpoint, entry.BlockDAAScore()) {
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)
}
func (mud *mutableUTXODiff) Reversed() *mutableUTXODiff {
return &mutableUTXODiff{
toAdd: mud.toRemove,
toRemove: mud.toAdd,
immutableReferences: mud.immutableReferences,
}
}