Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions blockchain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package blockchain
import (
"container/list"
"fmt"
"math/big"
"sync"
"time"

Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions btcjson/chainsvrresults.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`

Expand Down
40 changes: 17 additions & 23 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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 height"
context := "Failed to obtain block details"
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
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{
Expand All @@ -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(),
}
Expand All @@ -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
}
Expand Down
2 changes: 2 additions & 0 deletions rpcserverhelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down