WIP: next hard fork #5
4 changed files with 159 additions and 0 deletions
123
blockchain/chainquery.go
Normal file
123
blockchain/chainquery.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package blockchain
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
)
|
||||
|
||||
type ChainTip struct { // duplicate of btcjson.GetChainTipsResult to avoid circular reference
|
||||
Height int64
|
||||
Hash string
|
||||
BranchLen int64
|
||||
Status string
|
||||
}
|
||||
|
||||
// nodeHeightSorter implements sort.Interface to allow a slice of nodes to
|
||||
// be sorted by height in ascending order.
|
||||
type nodeHeightSorter []ChainTip
|
||||
|
||||
// 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 strings.Compare(s[i].Hash, s[j].Hash) < 0
|
||||
}
|
||||
return s[i].Height < s[j].Height
|
||||
}
|
||||
|
||||
// ChainTips returns information, in JSON-RPC format, about all the currently
|
||||
// known chain tips in the block index.
|
||||
func (b *BlockChain) ChainTips() []ChainTip {
|
||||
// we need our current tip
|
||||
// we also need all of our orphans that aren't in the prevOrphans
|
||||
var results []ChainTip
|
||||
|
||||
tip := b.bestChain.Tip()
|
||||
results = append(results, ChainTip{
|
||||
Height: int64(tip.height),
|
||||
Hash: tip.hash.String(),
|
||||
BranchLen: 0,
|
||||
Status: "active",
|
||||
})
|
||||
|
||||
b.orphanLock.RLock()
|
||||
defer b.orphanLock.RUnlock()
|
||||
|
||||
notInBestChain := func(block *btcutil.Block) bool {
|
||||
node := b.bestChain.NodeByHeight(block.Height())
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
return node.hash.IsEqual(block.Hash())
|
||||
}
|
||||
|
||||
for hash, orphan := range b.orphans {
|
||||
if len(b.prevOrphans[hash]) > 0 {
|
||||
continue
|
||||
}
|
||||
fork := orphan.block
|
||||
for fork != nil && notInBestChain(fork) {
|
||||
fork = b.orphans[*fork.Hash()].block
|
||||
}
|
||||
|
||||
result := ChainTip{
|
||||
Height: int64(orphan.block.Height()),
|
||||
Hash: hash.String(),
|
||||
BranchLen: int64(orphan.block.Height() - fork.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.LookupNode(&hash).status
|
||||
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"
|
||||
}
|
||||
|
||||
results = append(results, result)
|
||||
}
|
||||
|
||||
// Generate the results sorted by descending height.
|
||||
sort.Sort(sort.Reverse(nodeHeightSorter(results)))
|
||||
return results
|
||||
}
|
|
@ -325,6 +325,14 @@ type GetMempoolEntryResult struct {
|
|||
Depends []string `json:"depends"`
|
||||
}
|
||||
|
||||
// GetChainTipsResult models the data returns from the getchaintips command.
|
||||
type GetChainTipsResult struct {
|
||||
Height int64 `json:"height"`
|
||||
Hash string `json:"hash"`
|
||||
BranchLen int64 `json:"branchlen"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// GetMempoolInfoResult models the data returned from the getmempoolinfo
|
||||
// command.
|
||||
type GetMempoolInfoResult struct {
|
||||
|
|
11
rpcserver.go
11
rpcserver.go
|
@ -147,6 +147,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
|
|||
"getblockcount": handleGetBlockCount,
|
||||
"getblockhash": handleGetBlockHash,
|
||||
"getblockheader": handleGetBlockHeader,
|
||||
"getchaintips": handleGetChainTips,
|
||||
"getblocktemplate": handleGetBlockTemplate,
|
||||
"getcfilter": handleGetCFilter,
|
||||
"getcfilterheader": handleGetCFilterHeader,
|
||||
|
@ -1471,6 +1472,16 @@ func handleGetBlockHeader(s *rpcServer, cmd interface{}, closeChan <-chan struct
|
|||
return blockHeaderReply, nil
|
||||
}
|
||||
|
||||
// handleGetChainTips implements the getchaintips command.
|
||||
func handleGetChainTips(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||
tips := s.cfg.Chain.ChainTips()
|
||||
results := make([]btcjson.GetChainTipsResult, 0, len(tips))
|
||||
for _, tip := range tips {
|
||||
results = append(results, btcjson.GetChainTipsResult(tip))
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// encodeTemplateID encodes the passed details into an ID that can be used to
|
||||
// uniquely identify a block template.
|
||||
func encodeTemplateID(prevHash *chainhash.Hash, lastGenerated time.Time) string {
|
||||
|
|
|
@ -353,6 +353,22 @@ var helpDescsEnUS = map[string]string{
|
|||
"getblocktemplate--condition2": "mode=proposal, accepted",
|
||||
"getblocktemplate--result1": "An error string which represents why the proposal was rejected or nothing if accepted",
|
||||
|
||||
// GetChainTips help.
|
||||
"getchaintips--synopsis": "Returns information about all known chain tips the in the block tree.\n\n" +
|
||||
"The statuses in the result have the following meanings:\n" +
|
||||
"active: The current best chain tip.\n" +
|
||||
"invalid: The block or one of its ancestors is invalid.\n" +
|
||||
"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.\n" +
|
||||
"valid-fork: The block is fully validated which implies it was probably part of the main chain at one point and was reorganized.\n" +
|
||||
"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.",
|
||||
|
||||
// GetChainTipsResult help.
|
||||
"getchaintipsresult-height": "The height of the chain tip",
|
||||
"getchaintipsresult-hash": "The block hash of the chain tip",
|
||||
"getchaintipsresult-branchlen": "The length of the branch that connects the tip to the main chain (0 for the main chain tip)",
|
||||
"getchaintipsresult-status": "The status of the chain (active, invalid, headers-only, valid-fork, valid-headers)",
|
||||
"getchaintipsresults--result0": "test",
|
||||
|
||||
// GetCFilterCmd help.
|
||||
"getcfilter--synopsis": "Returns a block's committed filter given its hash.",
|
||||
"getcfilter-filtertype": "The type of filter to return (0=regular)",
|
||||
|
@ -837,6 +853,7 @@ var rpcResultTypes = map[string][]interface{}{
|
|||
"getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)},
|
||||
"getcfilter": {(*string)(nil)},
|
||||
"getcfilterheader": {(*string)(nil)},
|
||||
"getchaintips": {(*[]btcjson.GetChainTipsResult)(nil)},
|
||||
"getconnectioncount": {(*int32)(nil)},
|
||||
"getcurrentnet": {(*uint32)(nil)},
|
||||
"getdifficulty": {(*float64)(nil)},
|
||||
|
|
Loading…
Reference in a new issue