Implement 'getblockstats' JSON-RPC command

This commit is contained in:
Ivan Kuznetsov 2019-11-27 13:49:59 +07:00 committed by Jake Sylvestre
parent 8b1be46463
commit cfcf4fb762
4 changed files with 172 additions and 0 deletions

View file

@ -191,6 +191,50 @@ func NewGetBlockHeaderCmd(hash string, verbose *bool) *GetBlockHeaderCmd {
}
}
// HashOrHeight defines a type that can be used as hash_or_height value in JSON-RPC commands.
type HashOrHeight struct {
Value interface{}
}
// MarshalJSON implements the json.Marshaler interface
func (h HashOrHeight) MarshalJSON() ([]byte, error) {
return json.Marshal(h.Value)
}
// UnmarshalJSON implements the json.Unmarshaler interface
func (h *HashOrHeight) UnmarshalJSON(data []byte) error {
var unmarshalled interface{}
if err := json.Unmarshal(data, &unmarshalled); err != nil {
return err
}
switch v := unmarshalled.(type) {
case float64:
h.Value = int(v)
case string:
h.Value = v
default:
return fmt.Errorf("invalid hash_or_height value: %v", unmarshalled)
}
return nil
}
// GetBlockStatsCmd defines the getblockstats JSON-RPC command.
type GetBlockStatsCmd struct {
HashOrHeight HashOrHeight
Stats *[]string
}
// NewGetBlockStatsCmd returns a new instance which can be used to issue a
// getblockstats JSON-RPC command. Either height or hash must be specified.
func NewGetBlockStatsCmd(hashOrHeight HashOrHeight, stats *[]string) *GetBlockStatsCmd {
return &GetBlockStatsCmd{
HashOrHeight: hashOrHeight,
Stats: stats,
}
}
// TemplateRequest is a request object as defined in BIP22
// (https://en.bitcoin.it/wiki/BIP_0022), it is optionally provided as an
// pointer argument to GetBlockTemplateCmd.
@ -798,6 +842,7 @@ func init() {
MustRegisterCmd("getblockcount", (*GetBlockCountCmd)(nil), flags)
MustRegisterCmd("getblockhash", (*GetBlockHashCmd)(nil), flags)
MustRegisterCmd("getblockheader", (*GetBlockHeaderCmd)(nil), flags)
MustRegisterCmd("getblockstats", (*GetBlockStatsCmd)(nil), flags)
MustRegisterCmd("getblocktemplate", (*GetBlockTemplateCmd)(nil), flags)
MustRegisterCmd("getcfilter", (*GetCFilterCmd)(nil), flags)
MustRegisterCmd("getcfilterheader", (*GetCFilterHeaderCmd)(nil), flags)

View file

@ -228,6 +228,60 @@ func TestChainSvrCmds(t *testing.T) {
Verbose: btcjson.Bool(true),
},
},
{
name: "getblockstats height",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblockstats", btcjson.HashOrHeight{Value: 123})
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: 123}, nil)
},
marshalled: `{"jsonrpc":"1.0","method":"getblockstats","params":[123],"id":1}`,
unmarshalled: &btcjson.GetBlockStatsCmd{
HashOrHeight: btcjson.HashOrHeight{Value: 123},
},
},
{
name: "getblockstats hash",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblockstats", btcjson.HashOrHeight{Value: "deadbeef"})
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: "deadbeef"}, nil)
},
marshalled: `{"jsonrpc":"1.0","method":"getblockstats","params":["deadbeef"],"id":1}`,
unmarshalled: &btcjson.GetBlockStatsCmd{
HashOrHeight: btcjson.HashOrHeight{Value: "deadbeef"},
},
},
{
name: "getblockstats height optional stats",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblockstats", btcjson.HashOrHeight{Value: 123}, []string{"avgfee", "maxfee"})
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: 123}, &[]string{"avgfee", "maxfee"})
},
marshalled: `{"jsonrpc":"1.0","method":"getblockstats","params":[123,["avgfee","maxfee"]],"id":1}`,
unmarshalled: &btcjson.GetBlockStatsCmd{
HashOrHeight: btcjson.HashOrHeight{Value: 123},
Stats: &[]string{"avgfee", "maxfee"},
},
},
{
name: "getblockstats hash optional stats",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblockstats", btcjson.HashOrHeight{Value: "deadbeef"}, []string{"avgfee", "maxfee"})
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: "deadbeef"}, &[]string{"avgfee", "maxfee"})
},
marshalled: `{"jsonrpc":"1.0","method":"getblockstats","params":["deadbeef",["avgfee","maxfee"]],"id":1}`,
unmarshalled: &btcjson.GetBlockStatsCmd{
HashOrHeight: btcjson.HashOrHeight{Value: "deadbeef"},
Stats: &[]string{"avgfee", "maxfee"},
},
},
{
name: "getblocktemplate",
newCmd: func() (interface{}, error) {

View file

@ -24,6 +24,38 @@ type GetBlockHeaderVerboseResult struct {
NextHash string `json:"nextblockhash,omitempty"`
}
// GetBlockStatsResult models the data from the getblockstats command.
type GetBlockStatsResult struct {
AverageFee int64 `json:"avgfee"`
AverageFeeRate int64 `json:"avgfeerate"`
AverageTxSize int64 `json:"avgtxsize"`
FeeratePercentiles []int64 `json:"feerate_percentiles"`
Hash string `json:"blockhash"`
Height int64 `json:"height"`
Ins int64 `json:"ins"`
MaxFee int64 `json:"maxfee"`
MaxFeeRate int64 `json:"maxfeerate"`
MaxTxSize int64 `json:"maxtxsize"`
MedianFee int64 `json:"medianfee"`
MedianTime int64 `json:"mediantime"`
MedianTxSize int64 `json:"mediantxsize"`
MinFee int64 `json:"minfee"`
MinFeeRate int64 `json:"minfeerate"`
MinTxSize int64 `json:"mintxsize"`
Outs int64 `json:"outs"`
SegWitTotalSize int64 `json:"swtotal_size"`
SegWitTotalWeight int64 `json:"swtotal_weight"`
SegWitTxs int64 `json:"swtxs"`
Subsidy int64 `json:"subsidy"`
Time int64 `json:"time"`
TotalOut int64 `json:"total_out"`
TotalSize int64 `json:"total_size"`
TotalWeight int64 `json:"total_weight"`
Txs int64 `json:"txs"`
UTXOIncrease int64 `json:"utxo_increase"`
UTXOSizeIncrease int64 `json:"utxo_size_inc"`
}
// GetBlockVerboseResult models the data from the getblock command when the
// verbose flag is set to 1. When the verbose flag is set to 0, getblock returns a
// hex-encoded string. When the verbose flag is set to 1, getblock returns an object

View file

@ -1037,3 +1037,44 @@ func (c *Client) GetCFilterHeader(blockHash *chainhash.Hash,
filterType wire.FilterType) (*wire.MsgCFHeaders, error) {
return c.GetCFilterHeaderAsync(blockHash, filterType).Receive()
}
// FutureGetBlockStatsResult is a future promise to deliver the result of a
// GetBlockStatsAsync RPC invocation (or an applicable error).
type FutureGetBlockStatsResult chan *response
// Receive waits for the response promised by the future and returns statistics
// of a block at a certain height.
func (r FutureGetBlockStatsResult) Receive() (*btcjson.GetBlockStatsResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
var blockStats btcjson.GetBlockStatsResult
err = json.Unmarshal(res, &blockStats)
if err != nil {
return nil, err
}
return &blockStats, nil
}
// GetBlockStatsAsync returns an instance of a type that can be used to get
// the result of the RPC at some future time by invoking the Receive function on
// the returned instance.
//
// See GetBlockStats or the blocking version and more details.
func (c *Client) GetBlockStatsAsync(hashOrHeight interface{}, stats *[]string) FutureGetBlockStatsResult {
if hash, ok := hashOrHeight.(*chainhash.Hash); ok {
hashOrHeight = hash.String()
}
cmd := btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: hashOrHeight}, stats)
return c.sendCmd(cmd)
}
// GetBlockStats returns block statistics. First argument specifies height or hash of the target block.
// Second argument allows to select certain stats to return.
func (c *Client) GetBlockStats(hashOrHeight interface{}, stats *[]string) (*btcjson.GetBlockStatsResult, error) {
return c.GetBlockStatsAsync(hashOrHeight, stats).Receive()
}