mirror of
https://github.com/btcsuite/btcd.git
synced 2025-02-23 14:40:44 +01:00
Merge pull request #2153 from kcalvinalvin/2024-04-02-add-is-ancestor
blockchain: Add IsAncestor method to blockNode
This commit is contained in:
commit
5d50f7c83e
2 changed files with 192 additions and 0 deletions
|
@ -135,6 +135,20 @@ func newBlockNode(blockHeader *wire.BlockHeader, parent *blockNode) *blockNode {
|
||||||
return &node
|
return &node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equals compares all the fields of the block node except for the parent and
|
||||||
|
// ancestor and returns true if they're equal.
|
||||||
|
func (node *blockNode) Equals(other *blockNode) bool {
|
||||||
|
return node.hash == other.hash &&
|
||||||
|
node.workSum.Cmp(other.workSum) == 0 &&
|
||||||
|
node.height == other.height &&
|
||||||
|
node.version == other.version &&
|
||||||
|
node.bits == other.bits &&
|
||||||
|
node.nonce == other.nonce &&
|
||||||
|
node.timestamp == other.timestamp &&
|
||||||
|
node.merkleRoot == other.merkleRoot &&
|
||||||
|
node.status == other.status
|
||||||
|
}
|
||||||
|
|
||||||
// Header constructs a block header from the node and returns it.
|
// Header constructs a block header from the node and returns it.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
|
@ -260,6 +274,28 @@ func (node *blockNode) RelativeAncestorCtx(distance int32) HeaderCtx {
|
||||||
return ancestor
|
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
|
// RelativeAncestor returns the ancestor block node a relative 'distance' blocks
|
||||||
// before this node. This is equivalent to calling Ancestor with the node's
|
// before this node. This is equivalent to calling Ancestor with the node's
|
||||||
// height minus provided distance.
|
// height minus provided distance.
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue