mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[NOD-1417] Implement reachability (#964)
* [NOD-1417] Implement reachability * [NOD-1417] Rename package name * [NOD-1417] Add UpdateReindexRoot to interface api * [NOD-1417] Remove redundant type * [NOD-1417] Rename reachabilityTreeManager/reachabilityTree to reachabilityManager * [NOD-1417] Fix typo * [NOD-1417] Remove redundant copyright message * [NOD-1417] Fix comment
This commit is contained in:
parent
a132f55302
commit
a436b30ebf
@ -25,7 +25,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/pastmediantimemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/pruningmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/reachabilitytree"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/reachabilitymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/transactionvalidator"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
@ -56,12 +56,14 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db database.Database
|
||||
dbManager := dbmanager.New(db)
|
||||
|
||||
// Processes
|
||||
reachabilityTree := reachabilitytree.New(
|
||||
reachabilityManager := reachabilitymanager.New(
|
||||
dbManager,
|
||||
ghostdagDataStore,
|
||||
blockRelationStore,
|
||||
reachabilityDataStore)
|
||||
dagTopologyManager := dagtopologymanager.New(
|
||||
dbManager,
|
||||
reachabilityTree,
|
||||
reachabilityManager,
|
||||
blockRelationStore)
|
||||
ghostdagManager := ghostdagmanager.New(
|
||||
dbManager,
|
||||
@ -137,7 +139,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db database.Database
|
||||
pruningManager,
|
||||
blockValidator,
|
||||
dagTopologyManager,
|
||||
reachabilityTree,
|
||||
reachabilityManager,
|
||||
difficultyManager,
|
||||
pastMedianTimeManager,
|
||||
ghostdagManager,
|
||||
|
@ -8,4 +8,5 @@ type ReachabilityTree interface {
|
||||
AddBlock(blockHash *externalapi.DomainHash) error
|
||||
IsReachabilityTreeAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
|
||||
IsDAGAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
|
||||
UpdateReindexRoot(selectedTip *externalapi.DomainHash) error
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// ReachabilityData holds the set of data required to answer
|
||||
// reachability queries
|
||||
type ReachabilityData struct {
|
||||
@ -23,8 +25,8 @@ type ReachabilityData struct {
|
||||
// case, and so reindexing should always succeed unless more than
|
||||
// 2^64 blocks are added to the DAG/tree.
|
||||
type ReachabilityTreeNode struct {
|
||||
Children []*ReachabilityTreeNode
|
||||
Parent *ReachabilityTreeNode
|
||||
Children []*externalapi.DomainHash
|
||||
Parent *externalapi.DomainHash
|
||||
|
||||
// interval is the index interval containing all intervals of
|
||||
// blocks in this node's subtree
|
||||
@ -39,12 +41,6 @@ type ReachabilityInterval struct {
|
||||
End uint64
|
||||
}
|
||||
|
||||
// OrderedTreeNodeSet is an ordered set of reachabilityTreeNodes
|
||||
// Note that this type does not validate order validity. It's the
|
||||
// responsibility of the caller to construct instances of this
|
||||
// type properly.
|
||||
type OrderedTreeNodeSet []*ReachabilityTreeNode
|
||||
|
||||
// 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
|
||||
@ -58,4 +54,4 @@ type OrderedTreeNodeSet []*ReachabilityTreeNode
|
||||
//
|
||||
// See insertNode, hasAncestorOf, and reachabilityTree.isInPast for further
|
||||
// details.
|
||||
type FutureCoveringTreeNodeSet OrderedTreeNodeSet
|
||||
type FutureCoveringTreeNodeSet []*externalapi.DomainHash
|
||||
|
59
domain/consensus/processes/reachabilitymanager/fetch.go
Normal file
59
domain/consensus/processes/reachabilitymanager/fetch.go
Normal file
@ -0,0 +1,59 @@
|
||||
package reachabilitymanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
func (rt *reachabilityManager) data(blockHash *externalapi.DomainHash) (*model.ReachabilityData, error) {
|
||||
return rt.reachabilityDataStore.ReachabilityData(rt.databaseContext, blockHash)
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) futureCoveringSet(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
data, err := rt.data(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.FutureCoveringSet, nil
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) treeNode(blockHash *externalapi.DomainHash) (*model.ReachabilityTreeNode, error) {
|
||||
data, err := rt.data(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.TreeNode, nil
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) interval(blockHash *externalapi.DomainHash) (*model.ReachabilityInterval, error) {
|
||||
treeNode, err := rt.treeNode(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return treeNode.Interval, nil
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) children(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
data, err := rt.data(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.TreeNode.Children, nil
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) parent(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
|
||||
data, err := rt.data(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.TreeNode.Parent, nil
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) reindexRoot() (*externalapi.DomainHash, error) {
|
||||
return rt.reachabilityDataStore.ReachabilityReindexRoot(rt.databaseContext)
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
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 reachabilityTree.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
|
||||
}
|
123
domain/consensus/processes/reachabilitymanager/interval.go
Normal file
123
domain/consensus/processes/reachabilitymanager/interval.go
Normal file
@ -0,0 +1,123 @@
|
||||
package reachabilitymanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/pkg/errors"
|
||||
"math"
|
||||
)
|
||||
|
||||
func newReachabilityInterval(start uint64, end uint64) *model.ReachabilityInterval {
|
||||
return &model.ReachabilityInterval{Start: start, End: end}
|
||||
}
|
||||
|
||||
// intervalSize returns the size of this interval. Note that intervals are
|
||||
// inclusive from both sides.
|
||||
func intervalSize(ri *model.ReachabilityInterval) uint64 {
|
||||
return ri.End - ri.Start + 1
|
||||
}
|
||||
|
||||
// intervalSplitInHalf splits this interval by a fraction of 0.5.
|
||||
// See splitFraction for further details.
|
||||
func intervalSplitInHalf(ri *model.ReachabilityInterval) (
|
||||
left *model.ReachabilityInterval, right *model.ReachabilityInterval, err error) {
|
||||
|
||||
return intervalSplitFraction(ri, 0.5)
|
||||
}
|
||||
|
||||
// intervalSplitFraction splits this interval to two parts such that their
|
||||
// union is equal to the original interval and the first (left) part
|
||||
// contains the given fraction of the original interval's size.
|
||||
// Note: if the split results in fractional parts, this method rounds
|
||||
// the first part up and the last part down.
|
||||
func intervalSplitFraction(ri *model.ReachabilityInterval, fraction float64) (
|
||||
left *model.ReachabilityInterval, right *model.ReachabilityInterval, err error) {
|
||||
|
||||
if fraction < 0 || fraction > 1 {
|
||||
return nil, nil, errors.Errorf("fraction must be between 0 and 1")
|
||||
}
|
||||
if intervalSize(ri) == 0 {
|
||||
return nil, nil, errors.Errorf("cannot split an empty interval")
|
||||
}
|
||||
|
||||
allocationSize := uint64(math.Ceil(float64(intervalSize(ri)) * fraction))
|
||||
left = newReachabilityInterval(ri.Start, ri.Start+allocationSize-1)
|
||||
right = newReachabilityInterval(ri.Start+allocationSize, ri.End)
|
||||
return left, right, nil
|
||||
}
|
||||
|
||||
// intervalSplitExact splits this interval to exactly |sizes| parts where
|
||||
// |part_i| = sizes[i]. This method expects sum(sizes) to be exactly
|
||||
// equal to the interval's size.
|
||||
func intervalSplitExact(ri *model.ReachabilityInterval, sizes []uint64) ([]*model.ReachabilityInterval, error) {
|
||||
sizesSum := uint64(0)
|
||||
for _, size := range sizes {
|
||||
sizesSum += size
|
||||
}
|
||||
if sizesSum != intervalSize(ri) {
|
||||
return nil, errors.Errorf("sum of sizes must be equal to the interval's size")
|
||||
}
|
||||
|
||||
intervals := make([]*model.ReachabilityInterval, len(sizes))
|
||||
start := ri.Start
|
||||
for i, size := range sizes {
|
||||
intervals[i] = newReachabilityInterval(start, start+size-1)
|
||||
start += size
|
||||
}
|
||||
return intervals, nil
|
||||
}
|
||||
|
||||
// intervalSplitWithExponentialBias splits this interval to |sizes| parts
|
||||
// by the allocation rule described below. This method expects sum(sizes)
|
||||
// to be smaller or equal to the interval's size. Every part_i is
|
||||
// allocated at least sizes[i] capacity. The remaining budget is
|
||||
// split by an exponentially biased rule described below.
|
||||
//
|
||||
// This rule follows the GHOSTDAG protocol behavior where the child
|
||||
// with the largest subtree is expected to dominate the competition
|
||||
// for new blocks and thus grow the most. However, we may need to
|
||||
// add slack for non-largest subtrees in order to make CPU reindexing
|
||||
// attacks unworthy.
|
||||
func intervalSplitWithExponentialBias(ri *model.ReachabilityInterval, sizes []uint64) ([]*model.ReachabilityInterval, error) {
|
||||
intervalSize := intervalSize(ri)
|
||||
sizesSum := uint64(0)
|
||||
for _, size := range sizes {
|
||||
sizesSum += size
|
||||
}
|
||||
if sizesSum > intervalSize {
|
||||
return nil, errors.Errorf("sum of sizes must be less than or equal to the interval's size")
|
||||
}
|
||||
if sizesSum == intervalSize {
|
||||
return intervalSplitExact(ri, sizes)
|
||||
}
|
||||
|
||||
// Add a fractional bias to every size in the given sizes
|
||||
totalBias := intervalSize - sizesSum
|
||||
remainingBias := totalBias
|
||||
biasedSizes := make([]uint64, len(sizes))
|
||||
fractions := exponentialFractions(sizes)
|
||||
for i, fraction := range fractions {
|
||||
var bias uint64
|
||||
if i == len(fractions)-1 {
|
||||
bias = remainingBias
|
||||
} else {
|
||||
bias = uint64(math.Round(float64(totalBias) * fraction))
|
||||
if bias > remainingBias {
|
||||
bias = remainingBias
|
||||
}
|
||||
}
|
||||
biasedSizes[i] = sizes[i] + bias
|
||||
remainingBias -= bias
|
||||
}
|
||||
return intervalSplitExact(ri, biasedSizes)
|
||||
}
|
||||
|
||||
// intervalContains returns true if ri contains other.
|
||||
func intervalContains(ri *model.ReachabilityInterval, other *model.ReachabilityInterval) bool {
|
||||
return ri.Start <= other.Start && other.End <= ri.End
|
||||
}
|
||||
|
||||
// intervalString returns a string representation of the interval.
|
||||
func intervalString(ri *model.ReachabilityInterval) string {
|
||||
return fmt.Sprintf("[%d,%d]", ri.Start, ri.End)
|
||||
}
|
7
domain/consensus/processes/reachabilitymanager/log.go
Normal file
7
domain/consensus/processes/reachabilitymanager/log.go
Normal file
@ -0,0 +1,7 @@
|
||||
package reachabilitymanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.REAC)
|
@ -0,0 +1,79 @@
|
||||
package reachabilitymanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// orderedTreeNodeSet is an ordered set of model.ReachabilityTreeNodes
|
||||
// Note that this type does not validate order validity. It's the
|
||||
// responsibility of the caller to construct instances of this
|
||||
// type properly.
|
||||
type orderedTreeNodeSet []*externalapi.DomainHash
|
||||
|
||||
// findAncestorOfNode finds the reachability tree ancestor of `node`
|
||||
// among the nodes in `tns`.
|
||||
func (rt *reachabilityManager) findAncestorOfNode(tns orderedTreeNodeSet, node *externalapi.DomainHash) (*externalapi.DomainHash, bool) {
|
||||
ancestorIndex, ok, err := rt.findAncestorIndexOfNode(tns, node)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return tns[ancestorIndex], true
|
||||
}
|
||||
|
||||
// findAncestorIndexOfNode finds the index of the reachability tree
|
||||
// ancestor of `node` among the nodes in `tns`. It does so by finding
|
||||
// the index of the block with the maximum start that is below the
|
||||
// given block.
|
||||
func (rt *reachabilityManager) findAncestorIndexOfNode(tns orderedTreeNodeSet, node *externalapi.DomainHash) (int, bool, error) {
|
||||
treeNode, err := rt.treeNode(node)
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
blockInterval := treeNode.Interval
|
||||
end := blockInterval.End
|
||||
|
||||
low := 0
|
||||
high := len(tns)
|
||||
for low < high {
|
||||
middle := (low + high) / 2
|
||||
middleInterval, err := rt.interval(tns[middle])
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
if end < middleInterval.Start {
|
||||
high = middle
|
||||
} else {
|
||||
low = middle + 1
|
||||
}
|
||||
}
|
||||
|
||||
if low == 0 {
|
||||
return 0, false, nil
|
||||
}
|
||||
return low - 1, true, nil
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) propagateIntervals(tns orderedTreeNodeSet, intervals []*model.ReachabilityInterval,
|
||||
subtreeSizeMaps []map[externalapi.DomainHash]uint64) error {
|
||||
|
||||
for i, node := range tns {
|
||||
err := rt.stageInterval(node, intervals[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subtreeSizeMap := subtreeSizeMaps[i]
|
||||
err = rt.propagateInterval(node, subtreeSizeMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package reachabilitymanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// IsDAGAncestorOf returns true if blockHashA is an ancestor of
|
||||
// blockHashB in the DAG.
|
||||
//
|
||||
// Note: this method will return true if blockHashA == blockHashB
|
||||
// The complexity of this method is O(log(|this.futureCoveringTreeNodeSet|))
|
||||
func (rt *reachabilityManager) IsDAGAncestorOf(blockHashA, blockHashB *externalapi.DomainHash) (bool, error) {
|
||||
// Check if this node is a reachability tree ancestor of the
|
||||
// other node
|
||||
isReachabilityTreeAncestor, err := rt.IsReachabilityTreeAncestorOf(blockHashA, blockHashB)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if isReachabilityTreeAncestor {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Otherwise, use previously registered future blocks to complete the
|
||||
// reachability test
|
||||
return rt.futureCoveringSetHasAncestorOf(blockHashA, blockHashB)
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) UpdateReindexRoot(selectedTip *externalapi.DomainHash) error {
|
||||
return rt.updateReindexRoot(selectedTip)
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package reachabilitymanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// reachabilityManager maintains a structure that allows to answer
|
||||
// reachability queries in sub-linear time
|
||||
type reachabilityManager struct {
|
||||
databaseContext model.DBReader
|
||||
blockRelationStore model.BlockRelationStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
}
|
||||
|
||||
// New instantiates a new reachabilityManager
|
||||
func New(
|
||||
databaseContext model.DBReader,
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
blockRelationStore model.BlockRelationStore,
|
||||
reachabilityDataStore model.ReachabilityDataStore,
|
||||
) model.ReachabilityTree {
|
||||
return &reachabilityManager{
|
||||
databaseContext: databaseContext,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
}
|
||||
}
|
||||
|
||||
// AddBlock adds the block with the given blockHash into the reachability tree.
|
||||
func (rt *reachabilityManager) AddBlock(blockHash *externalapi.DomainHash) error {
|
||||
// Allocate a new reachability tree node
|
||||
newTreeNode := newReachabilityTreeNode()
|
||||
err := rt.stageTreeNode(blockHash, newTreeNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ghostdagData, err := rt.ghostdagDataStore.Get(rt.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If this is the genesis node, simply initialize it and return
|
||||
if ghostdagData.SelectedParent == nil {
|
||||
rt.stageReindexRoot(blockHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
reindexRoot, err := rt.reindexRoot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Insert the node into the selected parent's reachability tree
|
||||
err = rt.addChild(ghostdagData.SelectedParent, blockHash, reindexRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add the block to the futureCoveringSets of all the blocks
|
||||
// in the merget set
|
||||
mergeSet := make([]*externalapi.DomainHash, len(ghostdagData.MergeSetBlues)+len(ghostdagData.MergeSetReds))
|
||||
copy(mergeSet, ghostdagData.MergeSetBlues)
|
||||
copy(mergeSet[len(ghostdagData.MergeSetBlues):], ghostdagData.MergeSetReds)
|
||||
|
||||
for _, current := range mergeSet {
|
||||
err = rt.insertToFutureCoveringSet(current, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
66
domain/consensus/processes/reachabilitymanager/stage.go
Normal file
66
domain/consensus/processes/reachabilitymanager/stage.go
Normal file
@ -0,0 +1,66 @@
|
||||
package reachabilitymanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
func (rt *reachabilityManager) stageData(blockHash *externalapi.DomainHash, data *model.ReachabilityData) {
|
||||
rt.reachabilityDataStore.StageReachabilityData(blockHash, data)
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) stageFutureCoveringSet(blockHash *externalapi.DomainHash, set model.FutureCoveringTreeNodeSet) error {
|
||||
data, err := rt.data(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data.FutureCoveringSet = set
|
||||
rt.reachabilityDataStore.StageReachabilityData(blockHash, data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) stageTreeNode(blockHash *externalapi.DomainHash, node *model.ReachabilityTreeNode) error {
|
||||
data, err := rt.data(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data.TreeNode = node
|
||||
rt.reachabilityDataStore.StageReachabilityData(blockHash, data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) stageReindexRoot(blockHash *externalapi.DomainHash) {
|
||||
rt.reachabilityDataStore.StageReachabilityReindexRoot(blockHash)
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) addChildAndStage(node, child *externalapi.DomainHash) error {
|
||||
nodeData, err := rt.data(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nodeData.TreeNode.Children = append(nodeData.TreeNode.Children, child)
|
||||
return rt.stageTreeNode(node, nodeData.TreeNode)
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) stageParent(node, parent *externalapi.DomainHash) error {
|
||||
treeNode, err := rt.treeNode(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
treeNode.Parent = parent
|
||||
return rt.stageTreeNode(node, treeNode)
|
||||
}
|
||||
|
||||
func (rt *reachabilityManager) stageInterval(node *externalapi.DomainHash, interval *model.ReachabilityInterval) error {
|
||||
treeNode, err := rt.treeNode(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
treeNode.Interval = interval
|
||||
return rt.stageTreeNode(node, treeNode)
|
||||
}
|
1039
domain/consensus/processes/reachabilitymanager/tree.go
Normal file
1039
domain/consensus/processes/reachabilitymanager/tree.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,41 +0,0 @@
|
||||
package reachabilitytree
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// reachabilityTree maintains a structure that allows to answer
|
||||
// reachability queries in sub-linear time
|
||||
type reachabilityTree struct {
|
||||
blockRelationStore model.BlockRelationStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
}
|
||||
|
||||
// New instantiates a new ReachabilityTree
|
||||
func New(
|
||||
blockRelationStore model.BlockRelationStore,
|
||||
reachabilityDataStore model.ReachabilityDataStore) model.ReachabilityTree {
|
||||
return &reachabilityTree{
|
||||
blockRelationStore: blockRelationStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
}
|
||||
}
|
||||
|
||||
// AddBlock adds the block with the given blockHash into the reachability tree.
|
||||
func (rt *reachabilityTree) AddBlock(blockHash *externalapi.DomainHash) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsReachabilityTreeAncestorOf returns true if blockHashA is an
|
||||
// ancestor of blockHashB in the reachability tree. Note that this
|
||||
// does not necessarily mean that it isn't its ancestor in the DAG.
|
||||
func (rt *reachabilityTree) IsReachabilityTreeAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsDAGAncestorOf returns true if blockHashA is an ancestor of
|
||||
// blockHashB in the DAG.
|
||||
func (rt *reachabilityTree) IsDAGAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) {
|
||||
return false, nil
|
||||
}
|
@ -54,6 +54,7 @@ var (
|
||||
snvrLog = BackendLog.Logger("SNVR")
|
||||
ibdsLog = BackendLog.Logger("IBDS")
|
||||
wsvcLog = BackendLog.Logger("WSVC")
|
||||
reacLog = BackendLog.Logger("REAC")
|
||||
)
|
||||
|
||||
// SubsystemTags is an enum of all sub system tags
|
||||
@ -85,7 +86,8 @@ var SubsystemTags = struct {
|
||||
DNSS,
|
||||
SNVR,
|
||||
IBDS,
|
||||
WSVC string
|
||||
WSVC,
|
||||
REAC string
|
||||
}{
|
||||
ADXR: "ADXR",
|
||||
AMGR: "AMGR",
|
||||
@ -115,6 +117,7 @@ var SubsystemTags = struct {
|
||||
SNVR: "SNVR",
|
||||
IBDS: "IBDS",
|
||||
WSVC: "WSVC",
|
||||
REAC: "REAC",
|
||||
}
|
||||
|
||||
// subsystemLoggers maps each subsystem identifier to its associated logger.
|
||||
@ -147,6 +150,7 @@ var subsystemLoggers = map[string]*Logger{
|
||||
SubsystemTags.SNVR: snvrLog,
|
||||
SubsystemTags.IBDS: ibdsLog,
|
||||
SubsystemTags.WSVC: wsvcLog,
|
||||
SubsystemTags.REAC: reacLog,
|
||||
}
|
||||
|
||||
// InitLog attaches log file and error log file to the backend log.
|
||||
|
Loading…
x
Reference in New Issue
Block a user