blockchain: Add IsAncestor method to blockNode

IsAncestor() provides functionality for testing if a block node is
an ancestor of anther block node.
This commit is contained in:
Calvin Kim 2022-12-19 14:57:33 +09:00
parent c9c8795160
commit bc6396ddfd
2 changed files with 178 additions and 0 deletions

View File

@ -274,6 +274,28 @@ func (node *blockNode) RelativeAncestorCtx(distance int32) HeaderCtx {
return ancestor
}
// IsAncestor returns if the other node is an ancestor of this block node.
func (node *blockNode) IsAncestor(otherNode *blockNode) bool {
// Return early as false if the otherNode is nil.
if otherNode == nil {
return false
}
ancestor := node.Ancestor(otherNode.height)
if ancestor == nil {
return false
}
// If the otherNode has the same height as me, then the returned
// ancestor will be me. Return false since I'm not an ancestor of me.
if node.height == ancestor.height {
return false
}
// Return true if the fetched ancestor is other node.
return ancestor.Equals(otherNode)
}
// RelativeAncestor returns the ancestor block node a relative 'distance' blocks
// before this node. This is equivalent to calling Ancestor with the node's
// height minus provided distance.

View File

@ -1155,3 +1155,159 @@ func TestChainTips(t *testing.T) {
}
}
}
func TestIsAncestor(t *testing.T) {
// Construct a synthetic block chain with a block index consisting of
// the following structure.
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
tip := tstTip
chain := newFakeChain(&chaincfg.MainNetParams)
branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 3)
for _, node := range branch0Nodes {
chain.index.SetStatusFlags(node, statusDataStored)
chain.index.SetStatusFlags(node, statusValid)
chain.index.AddNode(node)
}
chain.bestChain.SetTip(tip(branch0Nodes))
branch1Nodes := chainedNodes(chain.bestChain.Genesis(), 1)
for _, node := range branch1Nodes {
chain.index.SetStatusFlags(node, statusDataStored)
chain.index.SetStatusFlags(node, statusValid)
chain.index.AddNode(node)
}
branch2Nodes := chainedNodes(chain.bestChain.Genesis(), 1)
for _, node := range branch2Nodes {
chain.index.SetStatusFlags(node, statusDataStored)
chain.index.SetStatusFlags(node, statusValidateFailed)
chain.index.AddNode(node)
}
// Is 1 an ancestor of 3?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeTrue := branch0Nodes[2].IsAncestor(branch0Nodes[0])
if !shouldBeTrue {
t.Errorf("TestIsAncestor fail. Node %s is an ancestor of node %s but got false",
branch0Nodes[0].hash.String(), branch0Nodes[2].hash.String())
}
// Is 1 an ancestor of 2?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeTrue = branch0Nodes[1].IsAncestor(branch0Nodes[0])
if !shouldBeTrue {
t.Errorf("TestIsAncestor fail. Node %s is an ancestor of node %s but got false",
branch0Nodes[0].hash.String(), branch0Nodes[1].hash.String())
}
// Is the genesis an ancestor of 1?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeTrue = branch0Nodes[0].IsAncestor(chain.bestChain.Genesis())
if !shouldBeTrue {
t.Errorf("TestIsAncestor fail. The genesis block is an ancestor of all blocks "+
"but got false for node %s",
branch0Nodes[0].hash.String())
}
// Is the genesis an ancestor of 1a?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeTrue = branch1Nodes[0].IsAncestor(chain.bestChain.Genesis())
if !shouldBeTrue {
t.Errorf("TestIsAncestor fail. The genesis block is an ancestor of all blocks "+
"but got false for node %s",
branch1Nodes[0].hash.String())
}
// Is the genesis an ancestor of 1b?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeTrue = branch2Nodes[0].IsAncestor(chain.bestChain.Genesis())
if !shouldBeTrue {
t.Errorf("TestIsAncestor fail. The genesis block is an ancestor of all blocks "+
"but got false for node %s",
branch2Nodes[0].hash.String())
}
// Is 1 an ancestor of 1a?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeFalse := branch1Nodes[0].IsAncestor(branch0Nodes[0])
if shouldBeFalse {
t.Errorf("TestIsAncestor fail. Node %s is in a different branch than "+
"node %s but got true", branch1Nodes[0].hash.String(),
branch0Nodes[0].hash.String())
}
// Is 1 an ancestor of 1b?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeFalse = branch2Nodes[0].IsAncestor(branch0Nodes[0])
if shouldBeFalse {
t.Errorf("TestIsAncestor fail. Node %s is in a different branch than "+
"node %s but got true", branch2Nodes[0].hash.String(),
branch0Nodes[0].hash.String())
}
// Is 1a an ancestor of 1b?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeFalse = branch2Nodes[0].IsAncestor(branch1Nodes[0])
if shouldBeFalse {
t.Errorf("TestIsAncestor fail. Node %s is in a different branch than "+
"node %s but got true", branch2Nodes[0].hash.String(),
branch1Nodes[0].hash.String())
}
// Is 1 an ancestor of 1?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeFalse = branch0Nodes[0].IsAncestor(branch0Nodes[0])
if shouldBeFalse {
t.Errorf("TestIsAncestor fail. Node is not an ancestor of itself but got true for node %s",
branch0Nodes[0].hash.String())
}
// Is the geneis an ancestor of genesis?
//
// genesis -> 1 -> 2 -> 3 (active)
// \ -> 1a (valid-fork)
// \ -> 1b (invalid)
shouldBeFalse = chain.bestChain.Genesis().IsAncestor(chain.bestChain.Genesis())
if shouldBeFalse {
t.Errorf("TestIsAncestor fail. Node is not an ancestor of itself but got true for node %s",
chain.bestChain.Genesis().hash.String())
}
// Is a block from another chain an ancestor of 1b?
fakeChain := newFakeChain(&chaincfg.TestNet3Params)
shouldBeFalse = branch2Nodes[0].IsAncestor(fakeChain.bestChain.Genesis())
if shouldBeFalse {
t.Errorf("TestIsAncestor fail. Node %s is in a different chain than "+
"node %s but got true", fakeChain.bestChain.Genesis().hash.String(),
branch2Nodes[0].hash.String())
}
}