[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 committed by Roy Lee
parent b147fe2a5b
commit daa3137dc4
4 changed files with 72 additions and 25 deletions

View file

@ -8,6 +8,7 @@ package blockchain
import ( import (
"container/list" "container/list"
"fmt" "fmt"
"math/big"
"sync" "sync"
"time" "time"
@ -1373,6 +1374,54 @@ func (b *BlockChain) BlockHashByHeight(blockHeight int32) (*chainhash.Hash, erro
return &node.hash, nil 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 // 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 // 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. // 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"` VersionHex string `json:"versionHex"`
MerkleRoot string `json:"merkleroot"` MerkleRoot string `json:"merkleroot"`
Time int64 `json:"time"` Time int64 `json:"time"`
MedianTime int64 `json:"mediantime"`
Nonce uint32 `json:"nonce"` Nonce uint32 `json:"nonce"`
Bits string `json:"bits"` Bits string `json:"bits"`
Difficulty float64 `json:"difficulty"` Difficulty float64 `json:"difficulty"`
ChainWork string `json:"chainwork"`
PreviousHash string `json:"previousblockhash,omitempty"` PreviousHash string `json:"previousblockhash,omitempty"`
NextHash string `json:"nextblockhash,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) 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 params := s.cfg.ChainParams
blockHeader := &blk.MsgBlock().Header 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 var prevHashString string
if blockHeight > 0 { if attrs.PrevHash != nil {
prevHashString = blockHeader.PrevBlock.String() prevHashString = attrs.PrevHash.String()
}
var nextHashString string
if attrs.NextHash != nil {
nextHashString = attrs.NextHash.String()
} }
base := btcjson.GetBlockVerboseResultBase{ base := btcjson.GetBlockVerboseResultBase{
@ -1244,13 +1236,15 @@ func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
PreviousHash: prevHashString, PreviousHash: prevHashString,
Nonce: blockHeader.Nonce, Nonce: blockHeader.Nonce,
Time: blockHeader.Timestamp.Unix(), Time: blockHeader.Timestamp.Unix(),
Confirmations: int64(1 + best.Height - blockHeight), MedianTime: attrs.MedianTime.Unix(),
Height: int64(blockHeight), Confirmations: int64(attrs.Confirmations),
Height: int64(attrs.Height),
Size: int32(len(blkBytes)), Size: int32(len(blkBytes)),
StrippedSize: int32(blk.MsgBlock().SerializeSizeStripped()), StrippedSize: int32(blk.MsgBlock().SerializeSizeStripped()),
Weight: int32(blockchain.GetBlockWeight(blk)), Weight: int32(blockchain.GetBlockWeight(blk)),
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
Difficulty: getDifficultyRatio(blockHeader.Bits, params), Difficulty: getDifficultyRatio(blockHeader.Bits, params),
ChainWork: attrs.ChainWork.Text(16),
NextHash: nextHashString, NextHash: nextHashString,
ClaimTrie: blockHeader.ClaimTrie.String(), ClaimTrie: blockHeader.ClaimTrie.String(),
} }
@ -1275,7 +1269,7 @@ func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
for i, tx := range txns { for i, tx := range txns {
rawTxn, err := createTxRawResult(params, tx.MsgTx(), rawTxn, err := createTxRawResult(params, tx.MsgTx(),
tx.Hash().String(), blockHeader, hash.String(), tx.Hash().String(), blockHeader, hash.String(),
blockHeight, best.Height) attrs.Height, best.Height)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -260,9 +260,11 @@ var helpDescsEnUS = map[string]string{
"getblockverboseresult-tx": "The transaction hashes (only when verbosity=1)", "getblockverboseresult-tx": "The transaction hashes (only when verbosity=1)",
"getblockverboseresult-nTx": "The number of transactions (aka, count of TX)", "getblockverboseresult-nTx": "The number of transactions (aka, count of TX)",
"getblockverboseresult-time": "The block time in seconds since 1 Jan 1970 GMT", "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-nonce": "The block nonce",
"getblockverboseresult-bits": "The bits which represent the block difficulty", "getblockverboseresult-bits": "The bits which represent the block difficulty",
"getblockverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum 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-previousblockhash": "The hash of the previous block",
"getblockverboseresult-nextblockhash": "The hash of the next block (only if there is one)", "getblockverboseresult-nextblockhash": "The hash of the next block (only if there is one)",
"getblockverboseresult-strippedsize": "The size of the block without witness data", "getblockverboseresult-strippedsize": "The size of the block without witness data",