dcrd/blockchain/chainquery.go
Dave Collins 81c76dcb78
multi: Break blockchain dependency on dcrjson.
This modifies the ChainTips function in blockchain to return the results
using a type defined in the blockchain package itself instead of
directly returning a dcrjson type.  This is preferable since nothing
else in the module depends on dcrjson and therefore allows the
dependency on the dcrjson module to be broken.

It also updates the RPC server that makes use of the function to perform
the necessary conversions.

Finally, the associated module hierarchy documentation is updated to
properly remove the no longer required dependency.

It should be noted that this is a breaking change to the API and thus
will need a v2 major version bump of the blockchain module to be
published before the changes can be externally consumed.
2018-10-10 16:44:20 -05: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.KnownValid() {
result.Status = "valid-fork"
} else {
result.Status = "valid-headers"
}
}
return results
}