Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Chore] Backport GetBlockVerbosity Fixes & Add getbalances RPC client command #389

Merged
merged 15 commits into from
Jul 22, 2020
Merged
28 changes: 23 additions & 5 deletions btcjson/chainsvrcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package btcjson

import (
"encoding/json"
"errors"
"fmt"

"github.com/gcash/bchd/wire"
Expand Down Expand Up @@ -127,23 +128,40 @@ func NewGetBestBlockHashCmd() *GetBestBlockHashCmd {
return &GetBestBlockHashCmd{}
}

// VerbosityLevel is a type that can unmarshal a bool or an int into an int field!
// This allows the raw API to receive either an int or a bool.
type VerbosityLevel int

// UnmarshalJSON allows the VerbosityLevel to unmarshal either bool or int.
func (v *VerbosityLevel) UnmarshalJSON(dat []byte) error {
switch string(dat) {
case "0", "false":
*v = 0
case "1", "true":
*v = 1
case "2":
*v = 2
default:
return errors.New("invalid VerbosityLevel value")
}
return nil
}

// GetBlockCmd defines the getblock JSON-RPC command.
type GetBlockCmd struct {
Hash string
Verbose *bool `jsonrpcdefault:"true"`
VerboseTx *bool `jsonrpcdefault:"false"`
Verbosity *VerbosityLevel `jsonrpcdefault:"1"`
}

// NewGetBlockCmd returns a new instance which can be used to issue a getblock
// JSON-RPC command.
//
// The parameters which are pointers indicate they are optional. Passing nil
// for optional parameters will use the default value.
func NewGetBlockCmd(hash string, verbose, verboseTx *bool) *GetBlockCmd {
func NewGetBlockCmd(hash string, verbosity *VerbosityLevel) *GetBlockCmd {
return &GetBlockCmd{
Hash: hash,
Verbose: verbose,
VerboseTx: verboseTx,
Verbosity: verbosity,
}
}

Expand Down
97 changes: 80 additions & 17 deletions btcjson/chainsvrcmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,51 +141,114 @@ func TestChainSvrCmds(t *testing.T) {
},
{
name: "getblock",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123", btcjson.Int(0))
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Vlevel(0))
},
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",0],"id":1}`,
unmarshalled: &btcjson.GetBlockCmd{
Hash: "123",
Verbosity: btcjson.Vlevel(0),
},
},
{
name: "getblock default verbosity",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123")
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", nil, nil)
return btcjson.NewGetBlockCmd("123", nil)
},
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123"],"id":1}`,
unmarshalled: &btcjson.GetBlockCmd{
Hash: "123",
Verbose: btcjson.Bool(true),
VerboseTx: btcjson.Bool(false),
Verbosity: btcjson.Vlevel(1),
},
},
{
name: "getblock required optional1",
newCmd: func() (interface{}, error) {
// Intentionally use a source param that is
// more pointers than the destination to
// exercise that path.
verbosePtr := btcjson.Bool(true)
return btcjson.NewCmd("getblock", "123", &verbosePtr)
return btcjson.NewCmd("getblock", "123", btcjson.Int(1))
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Bool(true), nil)
return btcjson.NewGetBlockCmd("123", btcjson.Vlevel(1))
},
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",true],"id":1}`,
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",1],"id":1}`,
unmarshalled: &btcjson.GetBlockCmd{
Hash: "123",
Verbose: btcjson.Bool(true),
VerboseTx: btcjson.Bool(false),
Verbosity: btcjson.Vlevel(1),
},
},
{
name: "getblock required optional2",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123", true, true)
return btcjson.NewCmd("getblock", "123", btcjson.Int(2))
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Vlevel(2))
},
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",2],"id":1}`,
unmarshalled: &btcjson.GetBlockCmd{
Hash: "123",
Verbosity: btcjson.Vlevel(2),
},
},
{
name: "getblock required optional string true",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123", btcjson.String("true"))
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Vlevel(1))
},
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",1],"id":1}`,
unmarshalled: &btcjson.GetBlockCmd{
Hash: "123",
Verbosity: btcjson.Vlevel(1),
},
},
{
name: "getblock required optional string false",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123", btcjson.String("false"))
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Vlevel(0))
},
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",0],"id":1}`,
unmarshalled: &btcjson.GetBlockCmd{
Hash: "123",
Verbosity: btcjson.Vlevel(0),
},
},
{
name: "getblock required optional true",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123", btcjson.Bool(true))
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Vlevel(1))
},
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",1],"id":1}`,
unmarshalled: &btcjson.GetBlockCmd{
Hash: "123",
Verbosity: btcjson.Vlevel(1),
},
},
{
name: "getblock required optional false",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123", btcjson.Bool(false))
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Bool(true), btcjson.Bool(true))
return btcjson.NewGetBlockCmd("123", btcjson.Vlevel(0))
},
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",true,true],"id":1}`,
marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",0],"id":1}`,
unmarshalled: &btcjson.GetBlockCmd{
Hash: "123",
Verbose: btcjson.Bool(true),
VerboseTx: btcjson.Bool(true),
Verbosity: btcjson.Vlevel(0),
},
},
{
Expand Down
66 changes: 41 additions & 25 deletions btcjson/chainsvrresults.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,53 @@ type GetBlockHeaderVerboseResult struct {
NextHash string `json:"nextblockhash,omitempty"`
}

// GetBlockBaseVerboseResult models the common data from the getblock command when
// verbose flag set to 1 or 2. When the verbose flag is not set, getblock
// returns a hex-encoded string.
type GetBlockBaseVerboseResult struct {
Hash string `json:"hash"`
Confirmations int64 `json:"confirmations"`
Size int32 `json:"size"`
Height int64 `json:"height"`
Version int32 `json:"version"`
VersionHex string `json:"versionHex"`
MerkleRoot string `json:"merkleroot"`
Time int64 `json:"time"`
Nonce uint32 `json:"nonce"`
Bits string `json:"bits"`
Difficulty float64 `json:"difficulty"`
PreviousHash string `json:"previousblockhash"`
NextHash string `json:"nextblockhash,omitempty"`
}

// GetBlockVerboseResult models the data from the getblock command when the
// verbose flag is set to 1 (default).
// 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
// whose tx field is an array of transaction hashes. When the verbose flag is set to 2,
// getblock returns an object whose tx field is an array of raw transactions.
// Use GetBlockVerboseTxResult to unmarshal data received from passing verbose=2 to getblock.
type GetBlockVerboseResult struct {
*GetBlockBaseVerboseResult
Tx []string `json:"tx,omitempty"`
Hash string `json:"hash"`
Confirmations int64 `json:"confirmations"`
StrippedSize int32 `json:"strippedsize"`
Size int32 `json:"size"`
Height int64 `json:"height"`
Version int32 `json:"version"`
VersionHex string `json:"versionHex"`
MerkleRoot string `json:"merkleroot"`
Tx []string `json:"tx,omitempty"`
RawTx []TxRawResult `json:"rawtx,omitempty"` // Note: this field is always empty when verbose != 2.
Time int64 `json:"time"`
Nonce uint32 `json:"nonce"`
Bits string `json:"bits"`
Difficulty float64 `json:"difficulty"`
PreviousHash string `json:"previousblockhash"`
NextHash string `json:"nextblockhash,omitempty"`
}

// GetBlockVerboseTxResult models the data from the getblock command when the
// verbose flag is set to 2.
// verbose flag is set to 2. 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
// whose tx field is an array of transaction hashes. When the verbose flag is set to 2,
// getblock returns an object whose tx field is an array of raw transactions.
// Use GetBlockVerboseResult to unmarshal data received from passing verbose=1 to getblock.
type GetBlockVerboseTxResult struct {
*GetBlockBaseVerboseResult
Tx []TxRawResult `json:"tx,omitempty"`
Hash string `json:"hash"`
Confirmations int64 `json:"confirmations"`
StrippedSize int32 `json:"strippedsize"`
Size int32 `json:"size"`
Height int64 `json:"height"`
Version int32 `json:"version"`
VersionHex string `json:"versionHex"`
MerkleRoot string `json:"merkleroot"`
Tx []TxRawResult `json:"tx,omitempty"`
Time int64 `json:"time"`
Nonce uint32 `json:"nonce"`
Bits string `json:"bits"`
Difficulty float64 `json:"difficulty"`
PreviousHash string `json:"previousblockhash"`
NextHash string `json:"nextblockhash,omitempty"`
}

// AddMultisigAddressResult models the data returned from the addmultisigaddress
Expand Down
2 changes: 1 addition & 1 deletion btcjson/cmdinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func TestMethodUsageText(t *testing.T) {
{
name: "getblock",
method: "getblock",
expected: `getblock "hash" (verbose=true verbosetx=false)`,
expected: `getblock "hash" (verbosity=1)`,
},
}

Expand Down
53 changes: 51 additions & 2 deletions btcjson/cmdparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ func typesMaybeCompatible(dest reflect.Type, src reflect.Type) bool {
return true
}

if srcKind == reflect.Bool {
if isNumeric(destKind) {
return true
}
}

if srcKind == reflect.String {
// Strings can potentially be converted to numeric types.
if isNumeric(destKind) {
Expand Down Expand Up @@ -289,6 +295,39 @@ func assignField(paramNum int, fieldName string, dest reflect.Value, src reflect

// Perform supported type conversions.
switch src.Kind() {
case reflect.Bool:
switch dest.Kind() {
// Destination is a signed integer of various magnitude.
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64:

srcBool := src.Bool()

switch srcBool {
case true:
dest.SetInt(1)
case false:
dest.SetInt(0)
}

case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64:

srcBool := src.Bool()

switch srcBool {
case true:
dest.SetUint(1)
case false:
dest.SetUint(0)
}

default:
str := fmt.Sprintf("parameter #%d '%s' must be type "+
"%v (got %v)", paramNum, fieldName, destBaseType,
srcBaseType)
return makeError(ErrInvalidType, str)
}
// Source value is a signed integer of various magnitude.
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64:
Expand Down Expand Up @@ -409,7 +448,17 @@ func assignField(paramNum int, fieldName string, dest reflect.Value, src reflect
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64:

srcInt, err := strconv.ParseInt(src.String(), 0, 0)
// Support coercing booleans into integers.
strVal := src.String()

switch strVal {
case "false":
strVal = "0"
case "true":
strVal = "1"
}

srcInt, err := strconv.ParseInt(strVal, 0, 0)
if err != nil {
str := fmt.Sprintf("parameter #%d '%s' must "+
"parse to a %v", paramNum, fieldName,
Expand Down Expand Up @@ -471,7 +520,7 @@ func assignField(paramNum int, fieldName string, dest reflect.Value, src reflect
err := json.Unmarshal([]byte(src.String()), &concreteVal)
if err != nil {
str := fmt.Sprintf("parameter #%d '%s' must "+
"be valid JSON which unsmarshals to a %v",
"be valid JSON which unmarshals to a %v",
paramNum, fieldName, destBaseType)
return makeError(ErrInvalidType, str)
}
Expand Down
Loading