Svarog c5707f64dc
[NOD-1420] Implement consensusStateManager (#985)
* [NOD-1420] Start working on ConsensusStateManager. Might be redundant due to recent changes

* [NOD-1420] Convert model to externalapi in utxo_algerbra helpers

* [NOD-1420] Add UTXO-diff algebra

* [NOD-1420] Prepare skeleton of calculateAcceptanceDataAndMultiset

* [NOD-1420] Added skeleton for AddBlockToVirtual

* [NOD-1420] Implement PopulateTransactionWithUTXOEntries

* [NOD-1420] Implement restorePastUTXO

* [NOD-1420] Implement finality check

* [NOD-1420] Move handling of tips to consensusStateManager

* [NOD-1420] Implement calculateAcceptanceDataAndMultiset

* [NOD-1420] Start implementing resolveBlockStatus

* [NOD-1420] Implement resolveBlockStatus

* [NOD-1420] Update related fields in end of resolveSingleBlockStatus

* [NOD-1420] Start working on selectVirtualParents

* [NOD-1420] Implemented BlockHeap

* [NOD-1420] Implement selectVirtualParents

* [NOD-1420] Implement updateVirtual

* [NOD-1420] Added comments where they were missing

* [NOD-1420] Place all consensusStateManager functions in correct files

* [NOD-1420] Return the missing outpoints from populateTransactionWithUTXOEntriesFromVirtualOrDiff

* [NOD-1420] Outpoint.ID -> TransactionID

* [NOD-1420] Fix Stringer tests

* [NOD-1420] Copy hash.FromString into utils

* [NOD-1420] SetParents should return an error

* [NOD-1420] Remove all reachabilityManager references from consensusStateManager

* [NOD-1420] Remove VirtualData. Get the info from the stores where needed

* [NOD-1420] Invert parameters to IsAncestorOf

* [NOD-1420] Use model.AcceptanceData

* [NOD-1420] Don't return accumulatedMassBefore in error cases

* [NOD-1420] Don't expect store functions to return nil when the requested data was found - instead add HasXXX functions

* [NOD-1420] addTransactionToMultiset sets isCoinbase properly

* [NOD-1420] expected hash string length is externalapi.DomainHashSize * 2

* [NOD-1420] Rename reachabilityTree -> reachabilityManager + updateReindexRoot if isNextVirtualSelectedParent

* [NOD-1420] ValidateCoinbaseTransaction in csm.verifyAndBuildUTXO

* [NOD-1420] Re-write HAsUTXODiffChild

* [NOD-1420] delete past_utxo.go.bak

* [NOD-1420] Implement validateCoinbaseTransaction in CSM

* [NOD-1420] Imlemented missing functionality in ValidateTransactionAndPopulateWithConsensusData

* [NOD-1420] Moved merge depth logic to MergeDepthManager

* [NOD-1420] Add logs
2020-11-02 16:18:53 +02:00

133 lines
4.5 KiB
Go

package reachabilitymanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// futureCoveringTreeNodeSet represents a collection of blocks in the future of
// a certain block. Once a block B is added to the DAG, every block A_i in
// B's selected parent anticone must register B in its futureCoveringTreeNodeSet. This allows
// to relatively quickly (O(log(|futureCoveringTreeNodeSet|))) query whether B
// is a descendent (is in the "future") of any block that previously
// registered it.
//
// Note that futureCoveringTreeNodeSet is meant to be queried only if B is not
// a reachability tree descendant of the block in question, as reachability
// tree queries are always O(1).
//
// See insertNode, hasAncestorOf, and isInPast for further
// details.
type futureCoveringTreeNodeSet orderedTreeNodeSet
// insertToFutureCoveringSet inserts the given block into this node's FutureCoveringSet
// while keeping it ordered by interval.
// If a block B ∈ node.FutureCoveringSet exists such that its interval
// contains block's interval, block need not be added. If block's
// interval contains B's interval, it replaces it.
//
// Notes:
// * Intervals never intersect unless one contains the other
// (this follows from the tree structure and the indexing rule).
// * Since node.FutureCoveringSet is kept ordered, a binary search can be
// used for insertion/queries.
// * Although reindexing may change a block's interval, the
// is-superset relation will by definition
// be always preserved.
func (rt *reachabilityManager) insertToFutureCoveringSet(node, futureNode *externalapi.DomainHash) error {
futureCoveringSet, err := rt.futureCoveringSet(node)
if err != nil {
return err
}
ancestorIndex, ok, err := rt.findAncestorIndexOfNode(futureCoveringSet, futureNode)
if err != nil {
return err
}
if !ok {
newSet := append([]*externalapi.DomainHash{futureNode}, futureCoveringSet...)
err := rt.stageFutureCoveringSet(node, newSet)
if err != nil {
return err
}
return nil
}
candidate := futureCoveringSet[ancestorIndex]
candidateIsAncestorOfFutureNode, err := rt.IsReachabilityTreeAncestorOf(candidate, futureNode)
if err != nil {
return err
}
if candidateIsAncestorOfFutureNode {
// candidate is an ancestor of futureNode, no need to insert
return nil
}
futureNodeIsAncestorOfCandidate, err := rt.IsReachabilityTreeAncestorOf(futureNode, candidate)
if err != nil {
return err
}
if futureNodeIsAncestorOfCandidate {
// futureNode is an ancestor of candidate, and can thus replace it
newSet := make([]*externalapi.DomainHash, len(futureCoveringSet))
copy(newSet, futureCoveringSet)
newSet[ancestorIndex] = futureNode
return rt.stageFutureCoveringSet(node, newSet)
}
// Insert futureNode in the correct index to maintain futureCoveringTreeNodeSet as
// a sorted-by-interval list.
// Note that ancestorIndex might be equal to len(futureCoveringTreeNodeSet)
left := futureCoveringSet[:ancestorIndex+1]
right := append([]*externalapi.DomainHash{futureNode}, futureCoveringSet[ancestorIndex+1:]...)
newSet := append(left, right...)
return rt.stageFutureCoveringSet(node, newSet)
}
// futureCoveringSetHasAncestorOf resolves whether the given node `other` is in the subtree of
// any node in this.FutureCoveringSet.
// See insertNode method for the complementary insertion behavior.
//
// Like the insert method, this method also relies on the fact that
// this.FutureCoveringSet is kept ordered by interval to efficiently perform a
// binary search over this.FutureCoveringSet and answer the query in
// O(log(|futureCoveringTreeNodeSet|)).
func (rt *reachabilityManager) futureCoveringSetHasAncestorOf(this, other *externalapi.DomainHash) (bool, error) {
futureCoveringSet, err := rt.futureCoveringSet(this)
if err != nil {
return false, err
}
ancestorIndex, ok, err := rt.findAncestorIndexOfNode(futureCoveringSet, other)
if err != nil {
return false, err
}
if !ok {
// No candidate to contain other
return false, nil
}
candidate := futureCoveringSet[ancestorIndex]
return rt.IsReachabilityTreeAncestorOf(candidate, other)
}
// futureCoveringSetString returns a string representation of the intervals in this futureCoveringSet.
func (rt *reachabilityManager) futureCoveringSetString(futureCoveringSet []*externalapi.DomainHash) (string, error) {
intervalsString := ""
for _, node := range futureCoveringSet {
nodeInterval, err := rt.interval(node)
if err != nil {
return "", err
}
intervalsString += intervalString(nodeInterval)
}
return intervalsString, nil
}