[NOD-1063] Fix a bug in which a block is pointing directly to a block in the selected parent chain below the reindex root

commit e303efef4209b8d62f1aac2cb57ac79829411556
Author: stasatdaglabs <stas@daglabs.com>
Date:   Mon Jun 29 11:59:36 2020 +0300

    [NOD-1063] Rename a test.

commit bfecd57470ec8aeb0a1b0ef82c051dde364c536e
Author: stasatdaglabs <stas@daglabs.com>
Date:   Mon Jun 29 11:57:36 2020 +0300

    [NOD-1063] Fix a comment.

commit b969e5922da16a3734806c03075fe2e45e64958b
Author: stasatdaglabs <stas@daglabs.com>
Date:   Sun Jun 28 18:14:44 2020 +0300

    [NOD-1063] Convert modifiedTreeNode to an out param.

commit 170f9872f432b2f7177cb099149c9959031e4f1e
Author: stasatdaglabs <stas@daglabs.com>
Date:   Sun Jun 28 17:05:01 2020 +0300

    [NOD-1063] Fix a bug in which a block is added to the selected parent chain below the reindex root.
This commit is contained in:
Mike Zak 2020-06-29 12:16:47 +03:00
parent ad096f9781
commit e87d00c9cf
2 changed files with 166 additions and 148 deletions

View File

@ -40,14 +40,6 @@ func newModifiedTreeNodes(nodes ...*reachabilityTreeNode) modifiedTreeNodes {
return modifiedNodes
}
// addAll adds all the reachabilityTreeNodes in `other`
// into `mtn`. Note that `other` is not affected.
func (mtn modifiedTreeNodes) addAll(other modifiedTreeNodes) {
for node := range other {
mtn[node] = struct{}{}
}
}
// reachabilityInterval represents an interval to be used within the
// tree reachability algorithm. See reachabilityTreeNode for further
// details.
@ -267,8 +259,8 @@ func (rtn *reachabilityTreeNode) hasSlackIntervalAfter() bool {
// remaining interval to allocate, a reindexing is triggered.
// This method returns a list of reachabilityTreeNodes modified
// by it.
func (rtn *reachabilityTreeNode) addChild(child *reachabilityTreeNode, reindexRoot *reachabilityTreeNode) (
modifiedTreeNodes, error) {
func (rtn *reachabilityTreeNode) addChild(child *reachabilityTreeNode, reindexRoot *reachabilityTreeNode,
modifiedNodes modifiedTreeNodes) error {
remaining := rtn.remainingIntervalAfter()
@ -281,9 +273,9 @@ func (rtn *reachabilityTreeNode) addChild(child *reachabilityTreeNode, reindexRo
// at this point we don't yet know child's interval.
if !reindexRoot.isAncestorOf(rtn) {
reindexStartTime := time.Now()
modifiedNodes, err := rtn.reindexIntervalsEarlierThanReindexRoot(reindexRoot)
err := rtn.reindexIntervalsEarlierThanReindexRoot(reindexRoot, modifiedNodes)
if err != nil {
return nil, err
return err
}
reindexTimeElapsed := time.Since(reindexStartTime)
log.Debugf("Reachability reindex triggered for "+
@ -291,30 +283,32 @@ func (rtn *reachabilityTreeNode) addChild(child *reachabilityTreeNode, reindexRo
"reindex root %s. Modified %d tree nodes and took %dms.",
rtn.blockNode.hash, reindexRoot.blockNode.hash,
len(modifiedNodes), reindexTimeElapsed.Milliseconds())
return modifiedNodes, nil
return nil
}
// No allocation space left -- reindex
if remaining.size() == 0 {
reindexStartTime := time.Now()
modifiedNodes, err := rtn.reindexIntervals()
err := rtn.reindexIntervals(modifiedNodes)
if err != nil {
return nil, err
return err
}
reindexTimeElapsed := time.Since(reindexStartTime)
log.Debugf("Reachability reindex triggered for "+
"block %s. Modified %d tree nodes and took %dms.",
rtn.blockNode.hash, len(modifiedNodes), reindexTimeElapsed.Milliseconds())
return modifiedNodes, nil
return nil
}
// Allocate from the remaining space
allocated, _, err := remaining.splitInHalf()
if err != nil {
return nil, err
return err
}
child.interval = allocated
return newModifiedTreeNodes(rtn, child), nil
modifiedNodes[rtn] = struct{}{}
modifiedNodes[child] = struct{}{}
return nil
}
// reindexIntervals traverses the reachability subtree that's
@ -324,7 +318,7 @@ func (rtn *reachabilityTreeNode) addChild(child *reachabilityTreeNode, reindexRo
// tree until it finds a node with a subreeSize that's greater than
// its interval size. See propagateInterval for further details.
// This method returns a list of reachabilityTreeNodes modified by it.
func (rtn *reachabilityTreeNode) reindexIntervals() (modifiedTreeNodes, error) {
func (rtn *reachabilityTreeNode) reindexIntervals(modifiedNodes modifiedTreeNodes) error {
current := rtn
// Initial interval and subtree sizes
@ -338,7 +332,7 @@ func (rtn *reachabilityTreeNode) reindexIntervals() (modifiedTreeNodes, error) {
if current.parent == nil {
// If we ended up here it means that there are more
// than 2^64 blocks, which shouldn't ever happen.
return nil, errors.Errorf("missing tree " +
return errors.Errorf("missing tree " +
"parent during reindexing. Theoretically, this " +
"should only ever happen if there are more " +
"than 2^64 blocks in the DAG.")
@ -350,7 +344,7 @@ func (rtn *reachabilityTreeNode) reindexIntervals() (modifiedTreeNodes, error) {
}
// Propagate the interval to the subtree
return current.propagateInterval(subTreeSizeMap)
return current.propagateInterval(subTreeSizeMap, modifiedNodes)
}
// countSubtrees counts the size of each subtree under this node,
@ -415,10 +409,9 @@ func (rtn *reachabilityTreeNode) countSubtrees(subTreeSizeMap map[*reachabilityT
// Subtree intervals are recursively allocated according to subtree sizes and
// the allocation rule in splitWithExponentialBias. This method returns
// a list of reachabilityTreeNodes modified by it.
func (rtn *reachabilityTreeNode) propagateInterval(subTreeSizeMap map[*reachabilityTreeNode]uint64) (
modifiedTreeNodes, error) {
func (rtn *reachabilityTreeNode) propagateInterval(subTreeSizeMap map[*reachabilityTreeNode]uint64,
modifiedNodes modifiedTreeNodes) error {
allModifiedTreeNodes := newModifiedTreeNodes()
queue := []*reachabilityTreeNode{rtn}
for len(queue) > 0 {
var current *reachabilityTreeNode
@ -430,7 +423,7 @@ func (rtn *reachabilityTreeNode) propagateInterval(subTreeSizeMap map[*reachabil
}
intervals, err := current.intervalRangeForChildAllocation().splitWithExponentialBias(sizes)
if err != nil {
return nil, err
return err
}
for i, child := range current.children {
childInterval := intervals[i]
@ -439,13 +432,13 @@ func (rtn *reachabilityTreeNode) propagateInterval(subTreeSizeMap map[*reachabil
}
}
allModifiedTreeNodes[current] = struct{}{}
modifiedNodes[current] = struct{}{}
}
return allModifiedTreeNodes, nil
return nil
}
func (rtn *reachabilityTreeNode) reindexIntervalsEarlierThanReindexRoot(
reindexRoot *reachabilityTreeNode) (modifiedTreeNodes, error) {
reindexRoot *reachabilityTreeNode, modifiedNodes modifiedTreeNodes) error {
// Find the common ancestor for both rtn and the reindex root
commonAncestor := rtn.findCommonAncestorWithReindexRoot(reindexRoot)
@ -455,25 +448,27 @@ func (rtn *reachabilityTreeNode) reindexIntervalsEarlierThanReindexRoot(
// b. A reachability tree ancestor of `reindexRoot`
commonAncestorChosenChild, err := commonAncestor.findAncestorAmongChildren(reindexRoot)
if err != nil {
return nil, err
return err
}
if rtn.interval.end < commonAncestorChosenChild.interval.start {
// rtn is in the subtree before the chosen child
return rtn.reclaimIntervalBeforeChosenChild(commonAncestor, commonAncestorChosenChild, reindexRoot)
return rtn.reclaimIntervalBeforeChosenChild(commonAncestor,
commonAncestorChosenChild, reindexRoot, modifiedNodes)
}
if commonAncestorChosenChild.interval.end < rtn.interval.start {
// rtn is in the subtree after the chosen child
return rtn.reclaimIntervalAfterChosenChild(commonAncestor, commonAncestorChosenChild, reindexRoot)
}
return nil, errors.Errorf("rtn is in the chosen child's subtree")
// rtn is either:
// * in the subtree after the chosen child
// * the common ancestor
// In both cases we reclaim from the "after" subtree. In the
// latter case this is arbitrary
return rtn.reclaimIntervalAfterChosenChild(commonAncestor,
commonAncestorChosenChild, reindexRoot, modifiedNodes)
}
func (rtn *reachabilityTreeNode) reclaimIntervalBeforeChosenChild(
commonAncestor *reachabilityTreeNode, commonAncestorChosenChild *reachabilityTreeNode, reindexRoot *reachabilityTreeNode) (
modifiedTreeNodes, error) {
allModifiedTreeNodes := newModifiedTreeNodes()
commonAncestor *reachabilityTreeNode, commonAncestorChosenChild *reachabilityTreeNode,
reindexRoot *reachabilityTreeNode, modifiedNodes modifiedTreeNodes) error {
current := commonAncestorChosenChild
if !commonAncestorChosenChild.hasSlackIntervalBefore() {
@ -484,7 +479,7 @@ func (rtn *reachabilityTreeNode) reclaimIntervalBeforeChosenChild(
var err error
current, err = current.findAncestorAmongChildren(reindexRoot)
if err != nil {
return nil, err
return err
}
}
@ -497,11 +492,10 @@ func (rtn *reachabilityTreeNode) reclaimIntervalBeforeChosenChild(
current.interval.start+slackReachabilityIntervalForReclaiming,
current.interval.end,
)
modifiedNodes, err := current.countSubtreesAndPropagateInterval()
err := current.countSubtreesAndPropagateInterval(modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodes)
current.interval = originalInterval
}
}
@ -516,25 +510,24 @@ func (rtn *reachabilityTreeNode) reclaimIntervalBeforeChosenChild(
current.interval.start+slackReachabilityIntervalForReclaiming,
current.interval.end,
)
modifiedNodes, err := current.parent.reindexIntervalsBeforeNode(current)
err := current.parent.reindexIntervalsBeforeNode(current, modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodes)
current = current.parent
}
return allModifiedTreeNodes, nil
return nil
}
// reindexIntervalsBeforeNode applies a tight interval to the reachability
// subtree before `node`. Note that `node` itself is unaffected.
func (rtn *reachabilityTreeNode) reindexIntervalsBeforeNode(node *reachabilityTreeNode) (
modifiedTreeNodes, error) {
func (rtn *reachabilityTreeNode) reindexIntervalsBeforeNode(node *reachabilityTreeNode,
modifiedNodes modifiedTreeNodes) error {
childrenBeforeNode, _, err := rtn.splitChildrenAroundChild(node)
if err != nil {
return nil, err
return err
}
childrenBeforeNodeSizes, childrenBeforeNodeSubtreeSizeMaps, childrenBeforeNodeSizesSum :=
@ -545,16 +538,15 @@ func (rtn *reachabilityTreeNode) reindexIntervalsBeforeNode(node *reachabilityTr
newInterval := newReachabilityInterval(newIntervalEnd-childrenBeforeNodeSizesSum+1, newIntervalEnd)
intervals, err := newInterval.splitExact(childrenBeforeNodeSizes)
if err != nil {
return nil, err
return err
}
return orderedTreeNodeSet(childrenBeforeNode).propagateIntervals(intervals, childrenBeforeNodeSubtreeSizeMaps)
return orderedTreeNodeSet(childrenBeforeNode).
propagateIntervals(intervals, childrenBeforeNodeSubtreeSizeMaps, modifiedNodes)
}
func (rtn *reachabilityTreeNode) reclaimIntervalAfterChosenChild(
commonAncestor *reachabilityTreeNode, commonAncestorChosenChild *reachabilityTreeNode, reindexRoot *reachabilityTreeNode) (
modifiedTreeNodes, error) {
allModifiedTreeNodes := newModifiedTreeNodes()
commonAncestor *reachabilityTreeNode, commonAncestorChosenChild *reachabilityTreeNode,
reindexRoot *reachabilityTreeNode, modifiedNodes modifiedTreeNodes) error {
current := commonAncestorChosenChild
if !commonAncestorChosenChild.hasSlackIntervalAfter() {
@ -565,7 +557,7 @@ func (rtn *reachabilityTreeNode) reclaimIntervalAfterChosenChild(
var err error
current, err = current.findAncestorAmongChildren(reindexRoot)
if err != nil {
return nil, err
return err
}
}
@ -578,11 +570,12 @@ func (rtn *reachabilityTreeNode) reclaimIntervalAfterChosenChild(
current.interval.start,
current.interval.end-slackReachabilityIntervalForReclaiming,
)
modifiedNodes, err := current.countSubtreesAndPropagateInterval()
modifiedNodes[current] = struct{}{}
err := current.countSubtreesAndPropagateInterval(modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodes)
current.interval = originalInterval
}
}
@ -597,25 +590,26 @@ func (rtn *reachabilityTreeNode) reclaimIntervalAfterChosenChild(
current.interval.start,
current.interval.end-slackReachabilityIntervalForReclaiming,
)
modifiedNodes, err := current.parent.reindexIntervalsAfterNode(current)
modifiedNodes[current] = struct{}{}
err := current.parent.reindexIntervalsAfterNode(current, modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodes)
current = current.parent
}
return allModifiedTreeNodes, nil
return nil
}
// reindexIntervalsAfterNode applies a tight interval to the reachability
// subtree after `node`. Note that `node` itself is unaffected.
func (rtn *reachabilityTreeNode) reindexIntervalsAfterNode(node *reachabilityTreeNode) (
modifiedTreeNodes, error) {
func (rtn *reachabilityTreeNode) reindexIntervalsAfterNode(node *reachabilityTreeNode,
modifiedNodes modifiedTreeNodes) error {
_, childrenAfterNode, err := rtn.splitChildrenAroundChild(node)
if err != nil {
return nil, err
return err
}
childrenAfterNodeSizes, childrenAfterNodeSubtreeSizeMaps, childrenAfterNodeSizesSum :=
@ -626,25 +620,24 @@ func (rtn *reachabilityTreeNode) reindexIntervalsAfterNode(node *reachabilityTre
newInterval := newReachabilityInterval(newIntervalStart, newIntervalStart+childrenAfterNodeSizesSum-1)
intervals, err := newInterval.splitExact(childrenAfterNodeSizes)
if err != nil {
return nil, err
return err
}
return orderedTreeNodeSet(childrenAfterNode).propagateIntervals(intervals, childrenAfterNodeSubtreeSizeMaps)
return orderedTreeNodeSet(childrenAfterNode).
propagateIntervals(intervals, childrenAfterNodeSubtreeSizeMaps, modifiedNodes)
}
func (tns orderedTreeNodeSet) propagateIntervals(intervals []*reachabilityInterval,
subtreeSizeMaps []map[*reachabilityTreeNode]uint64) (modifiedTreeNodes, error) {
subtreeSizeMaps []map[*reachabilityTreeNode]uint64, modifiedNodes modifiedTreeNodes) error {
allModifiedTreeNodes := newModifiedTreeNodes()
for i, node := range tns {
node.interval = intervals[i]
subtreeSizeMap := subtreeSizeMaps[i]
modifiedNodes, err := node.propagateInterval(subtreeSizeMap)
err := node.propagateInterval(subtreeSizeMap, modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodes)
}
return allModifiedTreeNodes, nil
return nil
}
// isAncestorOf checks if this node is a reachability tree ancestor
@ -832,7 +825,8 @@ func (rt *reachabilityTree) addBlock(node *blockNode, selectedParentAnticone []*
if err != nil {
return err
}
modifiedNodes, err := selectedParentTreeNode.addChild(newTreeNode, rt.reindexRoot)
modifiedNodes := newModifiedTreeNodes()
err = selectedParentTreeNode.addChild(newTreeNode, rt.reindexRoot, modifiedNodes)
if err != nil {
return err
}
@ -861,7 +855,8 @@ func (rt *reachabilityTree) addBlock(node *blockNode, selectedParentAnticone []*
// at this stage the virtual had not yet been updated.
if node.blueScore > rt.dag.SelectedTipBlueScore() {
updateStartTime := time.Now()
modifiedNodes, err := rt.updateReindexRoot(newTreeNode)
modifiedNodes := newModifiedTreeNodes()
err := rt.updateReindexRoot(newTreeNode, modifiedNodes)
if err != nil {
return err
}
@ -941,48 +936,47 @@ func (rt *reachabilityTree) storeState(dbTx *dbaccess.TxContext) error {
return nil
}
func (rt *reachabilityTree) updateReindexRoot(newTreeNode *reachabilityTreeNode) (modifiedTreeNodes, error) {
allModifiedTreeNodes := newModifiedTreeNodes()
func (rt *reachabilityTree) updateReindexRoot(newTreeNode *reachabilityTreeNode,
modifiedNodes modifiedTreeNodes) error {
nextReindexRoot := rt.reindexRoot
for {
candidateReindexRoot, modifiedNodes, found, err := rt.maybeMoveReindexRoot(nextReindexRoot, newTreeNode)
candidateReindexRoot, found, err := rt.maybeMoveReindexRoot(nextReindexRoot, newTreeNode, modifiedNodes)
if err != nil {
return nil, err
return err
}
if !found {
break
}
allModifiedTreeNodes.addAll(modifiedNodes)
nextReindexRoot = candidateReindexRoot
}
rt.reindexRoot = nextReindexRoot
return allModifiedTreeNodes, nil
return nil
}
func (rt *reachabilityTree) maybeMoveReindexRoot(
reindexRoot *reachabilityTreeNode, newTreeNode *reachabilityTreeNode) (
newReindexRoot *reachabilityTreeNode, modifiedNodes modifiedTreeNodes, found bool, err error) {
reindexRoot *reachabilityTreeNode, newTreeNode *reachabilityTreeNode, modifiedNodes modifiedTreeNodes) (
newReindexRoot *reachabilityTreeNode, found bool, err error) {
if !reindexRoot.isAncestorOf(newTreeNode) {
commonAncestor := newTreeNode.findCommonAncestorWithReindexRoot(reindexRoot)
return commonAncestor, nil, true, nil
return commonAncestor, true, nil
}
reindexRootChosenChild, err := reindexRoot.findAncestorAmongChildren(newTreeNode)
if err != nil {
return nil, nil, false, err
return nil, false, err
}
if newTreeNode.blockNode.blueScore-reindexRootChosenChild.blockNode.blueScore < reachabilityReindexWindow {
return nil, nil, false, nil
return nil, false, nil
}
modifiedNodes, err = rt.concentrateIntervalAroundReindexRootChosenChild(reindexRoot, reindexRootChosenChild)
err = rt.concentrateIntervalAroundReindexRootChosenChild(reindexRoot, reindexRootChosenChild, modifiedNodes)
if err != nil {
return nil, nil, false, err
return nil, false, err
}
return reindexRootChosenChild, modifiedNodes, true, nil
return reindexRootChosenChild, true, nil
}
// findAncestorAmongChildren finds the reachability tree child
@ -997,39 +991,34 @@ func (rtn *reachabilityTreeNode) findAncestorAmongChildren(node *reachabilityTre
}
func (rt *reachabilityTree) concentrateIntervalAroundReindexRootChosenChild(
reindexRoot *reachabilityTreeNode, reindexRootChosenChild *reachabilityTreeNode) (
modifiedTreeNodes, error) {
allModifiedTreeNodes := newModifiedTreeNodes()
reindexRoot *reachabilityTreeNode, reindexRootChosenChild *reachabilityTreeNode,
modifiedNodes modifiedTreeNodes) error {
reindexRootChildNodesBeforeChosen, reindexRootChildNodesAfterChosen, err :=
reindexRoot.splitChildrenAroundChild(reindexRootChosenChild)
if err != nil {
return nil, err
return err
}
reindexRootChildNodesBeforeChosenSizesSum, modifiedNodesBeforeChosen, err :=
rt.tightenIntervalsBeforeReindexRootChosenChild(reindexRoot, reindexRootChildNodesBeforeChosen)
reindexRootChildNodesBeforeChosenSizesSum, err :=
rt.tightenIntervalsBeforeReindexRootChosenChild(reindexRoot, reindexRootChildNodesBeforeChosen, modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodesBeforeChosen)
reindexRootChildNodesAfterChosenSizesSum, modifiedNodesAfterChosen, err :=
rt.tightenIntervalsAfterReindexRootChosenChild(reindexRoot, reindexRootChildNodesAfterChosen)
reindexRootChildNodesAfterChosenSizesSum, err :=
rt.tightenIntervalsAfterReindexRootChosenChild(reindexRoot, reindexRootChildNodesAfterChosen, modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodesAfterChosen)
modifiedNodesForReindexRootExpansion, err := rt.expandIntervalInReindexRootChosenChild(
reindexRoot, reindexRootChosenChild, reindexRootChildNodesBeforeChosenSizesSum, reindexRootChildNodesAfterChosenSizesSum)
err = rt.expandIntervalInReindexRootChosenChild(reindexRoot, reindexRootChosenChild,
reindexRootChildNodesBeforeChosenSizesSum, reindexRootChildNodesAfterChosenSizesSum, modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodesForReindexRootExpansion)
return allModifiedTreeNodes, nil
return nil
}
// splitChildrenAroundChild splits `rtn` into two slices: the nodes that are before
@ -1046,8 +1035,8 @@ func (rtn *reachabilityTreeNode) splitChildrenAroundChild(child *reachabilityTre
}
func (rt *reachabilityTree) tightenIntervalsBeforeReindexRootChosenChild(
reindexRoot *reachabilityTreeNode, reindexRootChildNodesBeforeChosen []*reachabilityTreeNode) (
reindexRootChildNodesBeforeChosenSizesSum uint64, modifiedNodes modifiedTreeNodes, err error) {
reindexRoot *reachabilityTreeNode, reindexRootChildNodesBeforeChosen []*reachabilityTreeNode,
modifiedNodes modifiedTreeNodes) (reindexRootChildNodesBeforeChosenSizesSum uint64, err error) {
reindexRootChildNodesBeforeChosenSizes, reindexRootChildNodesBeforeChosenSubtreeSizeMaps, reindexRootChildNodesBeforeChosenSizesSum :=
calcReachabilityTreeNodeSizes(reindexRootChildNodesBeforeChosen)
@ -1057,17 +1046,17 @@ func (rt *reachabilityTree) tightenIntervalsBeforeReindexRootChosenChild(
reindexRoot.interval.start+reachabilityReindexSlack+reindexRootChildNodesBeforeChosenSizesSum-1,
)
modifiedNodes, err = rt.propagateChildIntervals(intervalBeforeReindexRootStart, reindexRootChildNodesBeforeChosen,
reindexRootChildNodesBeforeChosenSizes, reindexRootChildNodesBeforeChosenSubtreeSizeMaps)
err = rt.propagateChildIntervals(intervalBeforeReindexRootStart, reindexRootChildNodesBeforeChosen,
reindexRootChildNodesBeforeChosenSizes, reindexRootChildNodesBeforeChosenSubtreeSizeMaps, modifiedNodes)
if err != nil {
return 0, nil, err
return 0, err
}
return reindexRootChildNodesBeforeChosenSizesSum, modifiedNodes, nil
return reindexRootChildNodesBeforeChosenSizesSum, nil
}
func (rt *reachabilityTree) tightenIntervalsAfterReindexRootChosenChild(
reindexRoot *reachabilityTreeNode, reindexRootChildNodesAfterChosen []*reachabilityTreeNode) (
reindexRootChildNodesAfterChosenSizesSum uint64, modifiedNodes modifiedTreeNodes, err error) {
reindexRoot *reachabilityTreeNode, reindexRootChildNodesAfterChosen []*reachabilityTreeNode,
modifiedNodes modifiedTreeNodes) (reindexRootChildNodesAfterChosenSizesSum uint64, err error) {
reindexRootChildNodesAfterChosenSizes, reindexRootChildNodesAfterChosenSubtreeSizeMaps, reindexRootChildNodesAfterChosenSizesSum :=
calcReachabilityTreeNodeSizes(reindexRootChildNodesAfterChosen)
@ -1077,19 +1066,17 @@ func (rt *reachabilityTree) tightenIntervalsAfterReindexRootChosenChild(
reindexRoot.interval.end-reachabilityReindexSlack-1,
)
modifiedNodes, err = rt.propagateChildIntervals(intervalAfterReindexRootEnd, reindexRootChildNodesAfterChosen,
reindexRootChildNodesAfterChosenSizes, reindexRootChildNodesAfterChosenSubtreeSizeMaps)
err = rt.propagateChildIntervals(intervalAfterReindexRootEnd, reindexRootChildNodesAfterChosen,
reindexRootChildNodesAfterChosenSizes, reindexRootChildNodesAfterChosenSubtreeSizeMaps, modifiedNodes)
if err != nil {
return 0, nil, err
return 0, err
}
return reindexRootChildNodesAfterChosenSizesSum, modifiedNodes, nil
return reindexRootChildNodesAfterChosenSizesSum, nil
}
func (rt *reachabilityTree) expandIntervalInReindexRootChosenChild(reindexRoot *reachabilityTreeNode,
reindexRootChosenChild *reachabilityTreeNode, reindexRootChildNodesBeforeChosenSizesSum uint64,
reindexRootChildNodesAfterChosenSizesSum uint64) (modifiedTreeNodes, error) {
allModifiedTreeNodes := newModifiedTreeNodes()
reindexRootChildNodesAfterChosenSizesSum uint64, modifiedNodes modifiedTreeNodes) error {
newReindexRootChildInterval := newReachabilityInterval(
reindexRoot.interval.start+reindexRootChildNodesBeforeChosenSizesSum+reachabilityReindexSlack,
@ -1110,22 +1097,21 @@ func (rt *reachabilityTree) expandIntervalInReindexRootChosenChild(reindexRoot *
newReindexRootChildInterval.start+reachabilityReindexSlack,
newReindexRootChildInterval.end-reachabilityReindexSlack,
)
modifiedNodes, err := reindexRootChosenChild.countSubtreesAndPropagateInterval()
err := reindexRootChosenChild.countSubtreesAndPropagateInterval(modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodes)
}
reindexRootChosenChild.interval = newReindexRootChildInterval
allModifiedTreeNodes[reindexRootChosenChild] = struct{}{}
return allModifiedTreeNodes, nil
modifiedNodes[reindexRootChosenChild] = struct{}{}
return nil
}
func (rtn *reachabilityTreeNode) countSubtreesAndPropagateInterval() (modifiedTreeNodes, error) {
func (rtn *reachabilityTreeNode) countSubtreesAndPropagateInterval(modifiedNodes modifiedTreeNodes) error {
subtreeSizeMap := make(map[*reachabilityTreeNode]uint64)
rtn.countSubtrees(subtreeSizeMap)
return rtn.propagateInterval(subtreeSizeMap)
return rtn.propagateInterval(subtreeSizeMap, modifiedNodes)
}
func calcReachabilityTreeNodeSizes(treeNodes []*reachabilityTreeNode) (
@ -1146,14 +1132,12 @@ func calcReachabilityTreeNodeSizes(treeNodes []*reachabilityTreeNode) (
}
func (rt *reachabilityTree) propagateChildIntervals(interval *reachabilityInterval,
childNodes []*reachabilityTreeNode, sizes []uint64, subtreeSizeMaps []map[*reachabilityTreeNode]uint64) (
modifiedTreeNodes, error) {
allModifiedTreeNodes := newModifiedTreeNodes()
childNodes []*reachabilityTreeNode, sizes []uint64, subtreeSizeMaps []map[*reachabilityTreeNode]uint64,
modifiedNodes modifiedTreeNodes) error {
childIntervalSizes, err := interval.splitExact(sizes)
if err != nil {
return nil, err
return err
}
for i, child := range childNodes {
@ -1161,14 +1145,13 @@ func (rt *reachabilityTree) propagateChildIntervals(interval *reachabilityInterv
child.interval = childInterval
childSubtreeSizeMap := subtreeSizeMaps[i]
modifiedNodes, err := child.propagateInterval(childSubtreeSizeMap)
err := child.propagateInterval(childSubtreeSizeMap, modifiedNodes)
if err != nil {
return nil, err
return err
}
allModifiedTreeNodes.addAll(modifiedNodes)
}
return allModifiedTreeNodes, nil
return nil
}
// isInPast returns true if `this` is in the past (exclusive) of `other`

View File

@ -19,7 +19,8 @@ func TestAddChild(t *testing.T) {
currentTip := root
for i := 0; i < 6; i++ {
node := newReachabilityTreeNode(&blockNode{})
modifiedNodes, err := currentTip.addChild(node, root)
modifiedNodes := newModifiedTreeNodes()
err := currentTip.addChild(node, root, modifiedNodes)
if err != nil {
t.Fatalf("TestAddChild: addChild failed: %s", err)
}
@ -36,7 +37,8 @@ func TestAddChild(t *testing.T) {
// Add another node to the tip of the chain to trigger a reindex (100 < 2^7=128)
lastChild := newReachabilityTreeNode(&blockNode{})
modifiedNodes, err := currentTip.addChild(lastChild, root)
modifiedNodes := newModifiedTreeNodes()
err := currentTip.addChild(lastChild, root, modifiedNodes)
if err != nil {
t.Fatalf("TestAddChild: addChild failed: %s", err)
}
@ -80,7 +82,8 @@ func TestAddChild(t *testing.T) {
childNodes := make([]*reachabilityTreeNode, 6)
for i := 0; i < len(childNodes); i++ {
childNodes[i] = newReachabilityTreeNode(&blockNode{})
modifiedNodes, err := root.addChild(childNodes[i], root)
modifiedNodes := newModifiedTreeNodes()
err := root.addChild(childNodes[i], root, modifiedNodes)
if err != nil {
t.Fatalf("TestAddChild: addChild failed: %s", err)
}
@ -95,7 +98,8 @@ func TestAddChild(t *testing.T) {
// Add another node to the root to trigger a reindex (100 < 2^7=128)
lastChild = newReachabilityTreeNode(&blockNode{})
modifiedNodes, err = root.addChild(lastChild, root)
modifiedNodes = newModifiedTreeNodes()
err = root.addChild(lastChild, root, modifiedNodes)
if err != nil {
t.Fatalf("TestAddChild: addChild failed: %s", err)
}
@ -135,7 +139,7 @@ func TestReachabilityTreeNodeIsAncestorOf(t *testing.T) {
descendants := make([]*reachabilityTreeNode, numberOfDescendants)
for i := 0; i < numberOfDescendants; i++ {
node := newReachabilityTreeNode(&blockNode{})
_, err := currentTip.addChild(node, root)
err := currentTip.addChild(node, root, newModifiedTreeNodes())
if err != nil {
t.Fatalf("TestReachabilityTreeNodeIsAncestorOf: addChild failed: %s", err)
}
@ -682,7 +686,7 @@ func TestReindexIntervalErrors(t *testing.T) {
currentTreeNode := treeNode
for i := 0; i < 100; i++ {
childTreeNode := newReachabilityTreeNode(&blockNode{})
_, err = currentTreeNode.addChild(childTreeNode, treeNode)
err = currentTreeNode.addChild(childTreeNode, treeNode, newModifiedTreeNodes())
if err != nil {
break
}
@ -719,7 +723,7 @@ func BenchmarkReindexInterval(b *testing.B) {
currentTreeNode := root
for i := 0; i < subTreeSize; i++ {
childTreeNode := newReachabilityTreeNode(&blockNode{})
_, err := currentTreeNode.addChild(childTreeNode, root)
err := currentTreeNode.addChild(childTreeNode, root, newModifiedTreeNodes())
if err != nil {
b.Fatalf("addChild: %s", err)
}
@ -732,7 +736,7 @@ func BenchmarkReindexInterval(b *testing.B) {
// node should lead to a reindex from root.
fullReindexTriggeringNode := newReachabilityTreeNode(&blockNode{})
b.StartTimer()
_, err := currentTreeNode.addChild(fullReindexTriggeringNode, root)
err := currentTreeNode.addChild(fullReindexTriggeringNode, root, newModifiedTreeNodes())
b.StopTimer()
if err != nil {
b.Fatalf("addChild: %s", err)
@ -818,6 +822,37 @@ func TestIsInPast(t *testing.T) {
}
}
func TestAddChildThatPointsDirectlyToTheSelectedParentChainBelowReindexRoot(t *testing.T) {
// Create a new database and DAG instance to run tests against.
dag, teardownFunc, err := DAGSetup("TestAddChildThatPointsDirectlyToTheSelectedParentChainBelowReindexRoot",
true, Config{DAGParams: &dagconfig.SimnetParams})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
// Set the reindex window to a low number to make this test run fast
originalReachabilityReindexWindow := reachabilityReindexWindow
reachabilityReindexWindow = 10
defer func() {
reachabilityReindexWindow = originalReachabilityReindexWindow
}()
// Add a block on top of the genesis block
chainRootBlock := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{dag.genesis.hash}, nil)
// Add chain of reachabilityReindexWindow blocks above chainRootBlock.
// This should move the reindex root
chainRootBlockTipHash := chainRootBlock.BlockHash()
for i := uint64(0); i < reachabilityReindexWindow; i++ {
chainBlock := PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{chainRootBlockTipHash}, nil)
chainRootBlockTipHash = chainBlock.BlockHash()
}
// Add another block over genesis
PrepareAndProcessBlockForTest(t, dag, []*daghash.Hash{dag.genesis.hash}, nil)
}
func TestUpdateReindexRoot(t *testing.T) {
// Create a new database and DAG instance to run tests against.
dag, teardownFunc, err := DAGSetup("TestUpdateReindexRoot", true, Config{