Skip to content

Commit

Permalink
Add getchaintxstats JSON-RPC client command
Browse files Browse the repository at this point in the history
Introduce Int32OrNil for feeding null parameter from command line.
This is useful for getchaintxstats to be able to define both params
while using the server-side calculated default value for the
first param.

Stopped panicking when providing null input from comand line to
array/slice/struct/map.
  • Loading branch information
lindlof committed May 25, 2020
1 parent 9f0179f commit b36d63c
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 1 deletion.
54 changes: 54 additions & 0 deletions btcjson/chainsvrcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,59 @@ func NewGetChainTipsCmd() *GetChainTipsCmd {
return &GetChainTipsCmd{}
}

// Int32OrNil defines a type for representing nullable numeric JSON-RPC parameters.
// This is a way to explicitly marshall a null param as they are dropped by default.
//
// Useful when default value is unknown client-side and null param
// needs to be included to define following params.
type Int32OrNil struct {
Value *int32
}

// MarshalJSON implements the json.Marshaler interface
func (n Int32OrNil) MarshalJSON() ([]byte, error) {
return json.Marshal(n.Value)
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (n *Int32OrNil) UnmarshalJSON(data []byte) error {
var unmarshalled interface{}
if err := json.Unmarshal(data, &unmarshalled); err != nil {
return err
}

switch v := unmarshalled.(type) {
case float64:
n.Value = Int32(int32(v))
default:
return fmt.Errorf("invalid int64 value: %v", unmarshalled)
}

return nil
}

// GetChainTxStatsCmd defines the getchaintxstats JSON-RPC command.
type GetChainTxStatsCmd struct {
NBlocks *Int32OrNil `jsonrpcusage:"nblocks|null"`
BlockHash *string
}

// NewGetChainTxStatsCmd returns a new instance which can be used to issue a
// getchaintxstats JSON-RPC command.
//
// The parameters which are pointers indicate they are optional. Passing nil
// for optional parameters will use the default value.
func NewGetChainTxStatsCmd(nBlocks *Int32OrNil, blockHash *string) *GetChainTxStatsCmd {
// Omit null nblock in params unless null value is required to define blockhash
if nBlocks.Value == nil && blockHash == nil {
nBlocks = nil
}
return &GetChainTxStatsCmd{
NBlocks: nBlocks,
BlockHash: blockHash,
}
}

// GetConnectionCountCmd defines the getconnectioncount JSON-RPC command.
type GetConnectionCountCmd struct{}

Expand Down Expand Up @@ -847,6 +900,7 @@ func init() {
MustRegisterCmd("getcfilter", (*GetCFilterCmd)(nil), flags)
MustRegisterCmd("getcfilterheader", (*GetCFilterHeaderCmd)(nil), flags)
MustRegisterCmd("getchaintips", (*GetChainTipsCmd)(nil), flags)
MustRegisterCmd("getchaintxstats", (*GetChainTxStatsCmd)(nil), flags)
MustRegisterCmd("getconnectioncount", (*GetConnectionCountCmd)(nil), flags)
MustRegisterCmd("getdifficulty", (*GetDifficultyCmd)(nil), flags)
MustRegisterCmd("getgenerate", (*GetGenerateCmd)(nil), flags)
Expand Down
52 changes: 52 additions & 0 deletions btcjson/chainsvrcmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,58 @@ func TestChainSvrCmds(t *testing.T) {
marshalled: `{"jsonrpc":"1.0","method":"getchaintips","params":[],"id":1}`,
unmarshalled: &btcjson.GetChainTipsCmd{},
},
{
name: "getchaintxstats",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getchaintxstats")
},
staticCmd: func() interface{} {
return btcjson.NewGetChainTxStatsCmd(&btcjson.Int32OrNil{Value: nil}, nil)
},
marshalled: `{"jsonrpc":"1.0","method":"getchaintxstats","params":[],"id":1}`,
unmarshalled: &btcjson.GetChainTxStatsCmd{},
},
{
name: "getchaintxstats optional nblocks",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getchaintxstats", btcjson.Int32OrNil{Value: btcjson.Int32(1000)})
},
staticCmd: func() interface{} {
return btcjson.NewGetChainTxStatsCmd(&btcjson.Int32OrNil{Value: btcjson.Int32(1000)}, nil)
},
marshalled: `{"jsonrpc":"1.0","method":"getchaintxstats","params":[1000],"id":1}`,
unmarshalled: &btcjson.GetChainTxStatsCmd{
NBlocks: &btcjson.Int32OrNil{Value: btcjson.Int32(1000)},
},
},
{
name: "getchaintxstats optional blockhash",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getchaintxstats", btcjson.Int32OrNil{Value: nil}, btcjson.String("0000afaf"))
},
staticCmd: func() interface{} {
return btcjson.NewGetChainTxStatsCmd(&btcjson.Int32OrNil{Value: nil}, btcjson.String("0000afaf"))
},
marshalled: `{"jsonrpc":"1.0","method":"getchaintxstats","params":[null,"0000afaf"],"id":1}`,
unmarshalled: &btcjson.GetChainTxStatsCmd{
NBlocks: nil,
BlockHash: btcjson.String("0000afaf"),
},
},
{
name: "getchaintxstats optional nblocks and blockhash",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getchaintxstats", btcjson.Int32OrNil{Value: btcjson.Int32(1000)}, btcjson.String("0000afaf"))
},
staticCmd: func() interface{} {
return btcjson.NewGetChainTxStatsCmd(&btcjson.Int32OrNil{Value: btcjson.Int32(1000)}, btcjson.String("0000afaf"))
},
marshalled: `{"jsonrpc":"1.0","method":"getchaintxstats","params":[1000,"0000afaf"],"id":1}`,
unmarshalled: &btcjson.GetChainTxStatsCmd{
NBlocks: &btcjson.Int32OrNil{Value: btcjson.Int32(1000)},
BlockHash: btcjson.String("0000afaf"),
},
},
{
name: "getconnectioncount",
newCmd: func() (interface{}, error) {
Expand Down
12 changes: 12 additions & 0 deletions btcjson/chainsvrresults.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ type GetBlockVerboseTxResult struct {
NextHash string `json:"nextblockhash,omitempty"`
}

// GetChainTxStatsResult models the data from the getchaintxstats command.
type GetChainTxStatsResult struct {
Time int64 `json:"time"`
TxCount int64 `json:"txcount"`
WindowFinalBlockHash string `json:"window_final_block_hash"`
WindowFinalBlockHeight int32 `json:"window_final_block_height"`
WindowBlockCount int32 `json:"window_block_count"`
WindowTxCount int32 `json:"window_tx_count"`
WindowInterval int32 `json:"window_interval"`
TxRate float64 `json:"txrate"`
}

// CreateMultiSigResult models the data returned from the createmultisig
// command.
type CreateMultiSigResult struct {
Expand Down
4 changes: 3 additions & 1 deletion btcjson/cmdparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,9 @@ func assignField(paramNum int, fieldName string, dest reflect.Value, src reflect
paramNum, fieldName, destBaseType)
return makeError(ErrInvalidType, str)
}
dest.Set(reflect.ValueOf(concreteVal).Elem())
if concreteVal != nil {
dest.Set(reflect.ValueOf(concreteVal).Elem())
}
}
}

Expand Down
43 changes: 43 additions & 0 deletions rpcclient/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,49 @@ func (c *Client) GetBlockCount() (int64, error) {
return c.GetBlockCountAsync().Receive()
}

// FutureGetChainTxStatsResult is a future promise to deliver the result of a
// GetChainTxStatsAsync RPC invocation (or an applicable error).
type FutureGetChainTxStatsResult chan *response

// Receive waits for the response promised by the future and returns transaction statistics
func (r FutureGetChainTxStatsResult) Receive() (*btcjson.GetChainTxStatsResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}

var chainTxStats btcjson.GetChainTxStatsResult
err = json.Unmarshal(res, &chainTxStats)
if err != nil {
return nil, err
}

return &chainTxStats, nil
}

// GetChainTxStatsAsync 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 GetChainTxStats for the blocking version and more details.
func (c *Client) GetChainTxStatsAsync(nBlocks *int32, blockHash *chainhash.Hash) FutureGetChainTxStatsResult {
var hash *string
if blockHash != nil {
hash = btcjson.String(blockHash.String())
}

cmd := btcjson.NewGetChainTxStatsCmd(&btcjson.Int32OrNil{Value: nBlocks}, hash)
return c.sendCmd(cmd)
}

// GetChainTxStats returns statistics about the total number and rate of transactions in the chain.
//
// First argument specifies size of the window in number of blocks. It's one month by default.
// Second argument is the hash of the block that ends the window.
func (c *Client) GetChainTxStats(nBlocks *int32, blockHash *chainhash.Hash) (*btcjson.GetChainTxStatsResult, error) {
return c.GetChainTxStatsAsync(nBlocks, blockHash).Receive()
}

// FutureGetDifficultyResult is a future promise to deliver the result of a
// GetDifficultyAsync RPC invocation (or an applicable error).
type FutureGetDifficultyResult chan *response
Expand Down

0 comments on commit b36d63c

Please sign in to comment.