[rpc blockchain] Add support for mediantime, chainwork to RPC getblock.

This commit is contained in:
Jonathan Moody 2022-07-21 14:27:06 -04:00
parent 7f9fe4b970
commit 7df208eb88
4 changed files with 72 additions and 25 deletions

View file

@ -8,6 +8,7 @@ package blockchain
import (
"container/list"
"fmt"
"math/big"
"sync"
"time"
@ -1373,6 +1374,54 @@ func (b *BlockChain) BlockHashByHeight(blockHeight int32) (*chainhash.Hash, erro
return &node.hash, nil
}
// BlockAttributes desribes a Block in relation to others on the main chain.
type BlockAttributes struct {
Height int32
Confirmations int32
MedianTime time.Time
ChainWork *big.Int
PrevHash *chainhash.Hash
NextHash *chainhash.Hash
}
// BlockAttributesByHash returns BlockAttributes for the block with the given hash
// relative to other blocks in the main chain. A BestState snapshot describing
// the main chain is also returned for convenience.
//
// This function is safe for concurrent access.
func (b *BlockChain) BlockAttributesByHash(hash *chainhash.Hash, prevHash *chainhash.Hash) (
attrs *BlockAttributes, best *BestState, err error) {
best = b.BestSnapshot()
node := b.index.LookupNode(hash)
if node == nil || !b.bestChain.Contains(node) {
str := fmt.Sprintf("block %s is not in the main chain", hash)
return nil, best, errNotInMainChain(str)
}
attrs = &BlockAttributes{
Height: node.height,
Confirmations: 1 + best.Height - node.height,
MedianTime: node.CalcPastMedianTime(),
ChainWork: node.workSum,
}
// Populate prev block hash if there is one.
if node.height > 0 {
attrs.PrevHash = prevHash
}
// Populate next block hash if there is one.
if node.height < best.Height {
nextHash, err := b.BlockHashByHeight(node.height + 1)
if err != nil {
return nil, best, err
}
attrs.NextHash = nextHash
}
return attrs, best, nil
}
// HeightRange returns a range of block hashes for the given start and end
// heights. It is inclusive of the start height and exclusive of the end
// height. The end height will be limited to the current main chain height.

View file

@ -77,9 +77,11 @@ type GetBlockVerboseResultBase struct {
VersionHex string `json:"versionHex"`
MerkleRoot string `json:"merkleroot"`
Time int64 `json:"time"`
MedianTime int64 `json:"mediantime"`
Nonce uint32 `json:"nonce"`
Bits string `json:"bits"`
Difficulty float64 `json:"difficulty"`
ChainWork string `json:"chainwork"`
PreviousHash string `json:"previousblockhash,omitempty"`
NextHash string `json:"nextblockhash,omitempty"`

View file

@ -1209,31 +1209,23 @@ func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
return nil, internalRPCError(err.Error(), context)
}
// Get the block height from chain.
blockHeight, err := s.cfg.Chain.BlockHeightByHash(hash)
if err != nil {
context := "Failed to obtain block height"
return nil, internalRPCError(err.Error(), context)
}
blk.SetHeight(blockHeight)
best := s.cfg.Chain.BestSnapshot()
// Get next block hash unless there are none.
var nextHashString string
if blockHeight < best.Height {
nextHash, err := s.cfg.Chain.BlockHashByHeight(blockHeight + 1)
if err != nil {
context := "No next block"
return nil, internalRPCError(err.Error(), context)
}
nextHashString = nextHash.String()
}
params := s.cfg.ChainParams
blockHeader := &blk.MsgBlock().Header
// Get further details (height, confirmations, nexthash, mediantime, etc.) from chain.
attrs, best, err := s.cfg.Chain.BlockAttributesByHash(hash, &blockHeader.PrevBlock)
if err != nil {
context := "Failed to obtain block details"
return nil, internalRPCError(err.Error(), context)
}
var prevHashString string
if blockHeight > 0 {
prevHashString = blockHeader.PrevBlock.String()
if attrs.PrevHash != nil {
prevHashString = attrs.PrevHash.String()
}
var nextHashString string
if attrs.NextHash != nil {
nextHashString = attrs.NextHash.String()
}
base := btcjson.GetBlockVerboseResultBase{
@ -1244,13 +1236,15 @@ func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
PreviousHash: prevHashString,
Nonce: blockHeader.Nonce,
Time: blockHeader.Timestamp.Unix(),
Confirmations: int64(1 + best.Height - blockHeight),
Height: int64(blockHeight),
MedianTime: attrs.MedianTime.Unix(),
Confirmations: int64(attrs.Confirmations),
Height: int64(attrs.Height),
Size: int32(len(blkBytes)),
StrippedSize: int32(blk.MsgBlock().SerializeSizeStripped()),
Weight: int32(blockchain.GetBlockWeight(blk)),
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
Difficulty: getDifficultyRatio(blockHeader.Bits, params),
ChainWork: attrs.ChainWork.Text(16),
NextHash: nextHashString,
ClaimTrie: blockHeader.ClaimTrie.String(),
}
@ -1275,7 +1269,7 @@ func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
for i, tx := range txns {
rawTxn, err := createTxRawResult(params, tx.MsgTx(),
tx.Hash().String(), blockHeader, hash.String(),
blockHeight, best.Height)
attrs.Height, best.Height)
if err != nil {
return nil, err
}

View file

@ -260,9 +260,11 @@ var helpDescsEnUS = map[string]string{
"getblockverboseresult-tx": "The transaction hashes (only when verbosity=1)",
"getblockverboseresult-nTx": "The number of transactions (aka, count of TX)",
"getblockverboseresult-time": "The block time in seconds since 1 Jan 1970 GMT",
"getblockverboseresult-mediantime": "The median block time in seconds since 1 Jan 1970 GMT",
"getblockverboseresult-nonce": "The block nonce",
"getblockverboseresult-bits": "The bits which represent the block difficulty",
"getblockverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty",
"getblockverboseresult-chainwork": "Expected number of hashes required to produce the chain up to this block (in hex)",
"getblockverboseresult-previousblockhash": "The hash of the previous block",
"getblockverboseresult-nextblockhash": "The hash of the next block (only if there is one)",
"getblockverboseresult-strippedsize": "The size of the block without witness data",