dcrd/blockchain/chainquery.go
Dave Collins 2c1039d150
blockchain: Rename KnownValid to HasValidated.
This renames the KnownValid and associated status flag of statusValid to
HasValidated and statusValidated, respectively in order to better
clarify the behavior.

While this distinction doesn't currently make much difference the
intention in the future to decouple the block process and download logic
at which point the distinction will become more important.
2019-11-19 11:48:29 -06:00

132 lines
3.9 KiB
Go

// Copyright (c) 2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"bytes"
"sort"
"github.com/decred/dcrd/chaincfg/chainhash"
)
// nodeHeightSorter implements sort.Interface to allow a slice of nodes to
// be sorted by height in ascending order.
type nodeHeightSorter []*blockNode
// Len returns the number of nodes in the slice. It is part of the
// sort.Interface implementation.
func (s nodeHeightSorter) Len() int {
return len(s)
}
// Swap swaps the nodes at the passed indices. It is part of the
// sort.Interface implementation.
func (s nodeHeightSorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less returns whether the node with index i should sort before the node with
// index j. It is part of the sort.Interface implementation.
func (s nodeHeightSorter) Less(i, j int) bool {
// To ensure stable order when the heights are the same, fall back to
// sorting based on hash.
if s[i].height == s[j].height {
return bytes.Compare(s[i].hash[:], s[j].hash[:]) < 0
}
return s[i].height < s[j].height
}
// ChainTipInfo models information about a chain tip.
type ChainTipInfo struct {
// Height specifies the block height of the chain tip.
Height int64
// Hash specifies the block hash of the chain tip.
Hash chainhash.Hash
// BranchLen specifies the length of the branch that connects the chain tip
// to the main chain. It will be zero for the main chain tip.
BranchLen int64
// Status specifies the validation status of chain formed by the chain tip.
//
// active:
// The current best chain tip.
//
// invalid:
// The block or one of its ancestors is invalid.
//
// headers-only:
// The block or one of its ancestors does not have the full block data
// available which also means the block can't be validated or connected.
//
// valid-fork:
// The block is fully validated which implies it was probably part of the
// main chain at one point and was reorganized.
//
// valid-headers:
// The full block data is available and the header is valid, but the block
// was never validated which implies it was probably never part of the
// main chain.
Status string
}
// ChainTips returns information, in JSON-RPC format, about all of the currently
// known chain tips in the block index.
func (b *BlockChain) ChainTips() []ChainTipInfo {
b.index.RLock()
var chainTips []*blockNode
for _, nodes := range b.index.chainTips {
chainTips = append(chainTips, nodes...)
}
b.index.RUnlock()
// Generate the results sorted by descending height.
sort.Sort(sort.Reverse(nodeHeightSorter(chainTips)))
results := make([]ChainTipInfo, len(chainTips))
bestTip := b.bestChain.Tip()
for i, tip := range chainTips {
result := &results[i]
result.Height = tip.height
result.Hash = tip.hash
result.BranchLen = tip.height - b.bestChain.FindFork(tip).height
// Determine the status of the chain tip.
//
// active:
// The current best chain tip.
//
// invalid:
// The block or one of its ancestors is invalid.
//
// headers-only:
// The block or one of its ancestors does not have the full block data
// available which also means the block can't be validated or
// connected.
//
// valid-fork:
// The block is fully validated which implies it was probably part of
// main chain at one point and was reorganized.
//
// valid-headers:
// The full block data is available and the header is valid, but the
// block was never validated which implies it was probably never part
// of the main chain.
tipStatus := b.index.NodeStatus(tip)
if tip == bestTip {
result.Status = "active"
} else if tipStatus.KnownInvalid() {
result.Status = "invalid"
} else if !tipStatus.HaveData() {
result.Status = "headers-only"
} else if tipStatus.HasValidated() {
result.Status = "valid-fork"
} else {
result.Status = "valid-headers"
}
}
return results
}