blockchain: Implement IntervalBlockHashes method.
This will be used to respond to getcfcheckpt queries.
This commit is contained in:
parent
07393c0dab
commit
185577f4c2
2 changed files with 111 additions and 0 deletions
|
@ -1364,6 +1364,45 @@ func (b *BlockChain) HeightToHashRange(startHeight int32,
|
||||||
return hashes, nil
|
return hashes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IntervalBlockHashes returns hashes for all blocks that are ancestors of
|
||||||
|
// endHash where the block height is a positive multiple of interval.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (b *BlockChain) IntervalBlockHashes(endHash *chainhash.Hash, interval int,
|
||||||
|
) ([]chainhash.Hash, error) {
|
||||||
|
|
||||||
|
endNode := b.index.LookupNode(endHash)
|
||||||
|
if endNode == nil {
|
||||||
|
return nil, fmt.Errorf("no known block header with hash %v", endHash)
|
||||||
|
}
|
||||||
|
if !b.index.NodeStatus(endNode).KnownValid() {
|
||||||
|
return nil, fmt.Errorf("block %v is not yet validated", endHash)
|
||||||
|
}
|
||||||
|
endHeight := endNode.height
|
||||||
|
|
||||||
|
resultsLength := int(endHeight) / interval
|
||||||
|
hashes := make([]chainhash.Hash, resultsLength)
|
||||||
|
|
||||||
|
b.bestChain.mtx.Lock()
|
||||||
|
defer b.bestChain.mtx.Unlock()
|
||||||
|
|
||||||
|
blockNode := endNode
|
||||||
|
for index := int(endHeight) / interval; index > 0; index-- {
|
||||||
|
// Use the bestChain chainView for faster lookups once lookup intersects
|
||||||
|
// the best chain.
|
||||||
|
blockHeight := int32(index * interval)
|
||||||
|
if b.bestChain.contains(blockNode) {
|
||||||
|
blockNode = b.bestChain.nodeByHeight(blockHeight)
|
||||||
|
} else {
|
||||||
|
blockNode = blockNode.Ancestor(blockHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashes[index-1] = blockNode.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashes, nil
|
||||||
|
}
|
||||||
|
|
||||||
// locateInventory returns the node of the block after the first known block in
|
// locateInventory returns the node of the block after the first known block in
|
||||||
// the locator along with the number of subsequent nodes needed to either reach
|
// the locator along with the number of subsequent nodes needed to either reach
|
||||||
// the provided stop hash or the provided max number of entries.
|
// the provided stop hash or the provided max number of entries.
|
||||||
|
|
|
@ -892,3 +892,75 @@ func TestHeightToHashRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestIntervalBlockHashes ensures that fetching block hashes at specified
|
||||||
|
// intervals by end hash works as expected.
|
||||||
|
func TestIntervalBlockHashes(t *testing.T) {
|
||||||
|
// Construct a synthetic block chain with a block index consisting of
|
||||||
|
// the following structure.
|
||||||
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||||
|
// \-> 16a -> 17a -> 18a (unvalidated)
|
||||||
|
tip := tstTip
|
||||||
|
chain := newFakeChain(&chaincfg.MainNetParams)
|
||||||
|
branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
|
||||||
|
branch1Nodes := chainedNodes(branch0Nodes[14], 3)
|
||||||
|
for _, node := range branch0Nodes {
|
||||||
|
chain.index.SetStatusFlags(node, statusValid)
|
||||||
|
chain.index.AddNode(node)
|
||||||
|
}
|
||||||
|
for _, node := range branch1Nodes {
|
||||||
|
if node.height < 18 {
|
||||||
|
chain.index.SetStatusFlags(node, statusValid)
|
||||||
|
}
|
||||||
|
chain.index.AddNode(node)
|
||||||
|
}
|
||||||
|
chain.bestChain.SetTip(tip(branch0Nodes))
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
endHash chainhash.Hash
|
||||||
|
interval int
|
||||||
|
hashes []chainhash.Hash
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "blocks on main chain",
|
||||||
|
endHash: branch0Nodes[17].hash,
|
||||||
|
interval: 8,
|
||||||
|
hashes: nodeHashes(branch0Nodes, 7, 15),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blocks on stale chain",
|
||||||
|
endHash: branch1Nodes[1].hash,
|
||||||
|
interval: 8,
|
||||||
|
hashes: append(nodeHashes(branch0Nodes, 7),
|
||||||
|
nodeHashes(branch1Nodes, 0)...),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no results",
|
||||||
|
endHash: branch0Nodes[17].hash,
|
||||||
|
interval: 20,
|
||||||
|
hashes: []chainhash.Hash{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unvalidated block",
|
||||||
|
endHash: branch1Nodes[2].hash,
|
||||||
|
interval: 8,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
hashes, err := chain.IntervalBlockHashes(&test.endHash, test.interval)
|
||||||
|
if err != nil {
|
||||||
|
if !test.expectError {
|
||||||
|
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(hashes, test.hashes) {
|
||||||
|
t.Errorf("%s: unxpected hashes -- got %v, want %v",
|
||||||
|
test.name, hashes, test.hashes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue