From 160c388285f5ae4ccea484185b93b15d9e35a432 Mon Sep 17 00:00:00 2001 From: jalavosus Date: Fri, 31 Jan 2020 12:27:38 -0500 Subject: [PATCH 1/4] Refactor GetBlockCmd type and NewGetBlockCmd() function to follow the bitcoin json RPC verbosity format for getblock, which uses 0, 1, or 2 as parameters rather than a boolean true or false. --- btcjson/chainsvrcmds.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/btcjson/chainsvrcmds.go b/btcjson/chainsvrcmds.go index 406357bd06..031eafd606 100644 --- a/btcjson/chainsvrcmds.go +++ b/btcjson/chainsvrcmds.go @@ -130,8 +130,7 @@ func NewGetBestBlockHashCmd() *GetBestBlockHashCmd { // GetBlockCmd defines the getblock JSON-RPC command. type GetBlockCmd struct { Hash string - Verbose *bool `jsonrpcdefault:"true"` - VerboseTx *bool `jsonrpcdefault:"false"` + Verbosity *int `jsonrpcdefault:"0"` } // NewGetBlockCmd returns a new instance which can be used to issue a getblock @@ -139,11 +138,10 @@ type GetBlockCmd struct { // // 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 *int) *GetBlockCmd { return &GetBlockCmd{ Hash: hash, - Verbose: verbose, - VerboseTx: verboseTx, + Verbosity: verbosity, } } From 468154a05228669a4eddc4166c749a2b075aba1b Mon Sep 17 00:00:00 2001 From: jalavosus Date: Fri, 31 Jan 2020 12:29:20 -0500 Subject: [PATCH 2/4] Refactor GetBlockVerboseResult into two separate types: one type for getblock "hash" verbosity=1, and a second type for getblock "hash" verbosity=2. This is necessary due to how getblock returns a block's transaction data based on the provided verbosity parameter. If verbosity=1, then getblock.Tx is an array of a block's transaction ids (txids) as strings. If verbosity=2, then getblock.Tx is an array of raw transaction data. --- btcjson/chainsvrresults.go | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/btcjson/chainsvrresults.go b/btcjson/chainsvrresults.go index 7e6c710766..81d0517e42 100644 --- a/btcjson/chainsvrresults.go +++ b/btcjson/chainsvrresults.go @@ -25,8 +25,11 @@ type GetBlockHeaderVerboseResult struct { } // GetBlockVerboseResult models the data from the getblock command when the -// verbose flag is set. When the verbose flag is not set, getblock returns a -// hex-encoded string. +// 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 { Hash string `json:"hash"` Confirmations int64 `json:"confirmations"` @@ -38,7 +41,32 @@ type GetBlockVerboseResult struct { VersionHex string `json:"versionHex"` MerkleRoot string `json:"merkleroot"` Tx []string `json:"tx,omitempty"` - RawTx []TxRawResult `json:"rawtx,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. 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 { + Hash string `json:"hash"` + Confirmations int64 `json:"confirmations"` + StrippedSize int32 `json:"strippedsize"` + Size int32 `json:"size"` + Weight int32 `json:"weight"` + 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"` From 57cb8e4b11a76282166442ec149625712edb945f Mon Sep 17 00:00:00 2001 From: jalavosus Date: Fri, 31 Jan 2020 12:31:47 -0500 Subject: [PATCH 3/4] Refactor FutureGetBlockVerboseResult into two types: FutureGetBlockVerboseResult, and FutureGetBlockVerboseTxResult. Due to differences in how getblock returns data based on the provided verbosity parameter, it's necessary to have two separate return types based on verbosity. This necessitates a separate unmarshalling function (represented throughout rpcclient/chain.go as Result.Receive()) to ensure that data is correctly unmarshalled and returned to the user. --- btcjson/chainsvrcmds_test.go | 31 ++++++++++++------------------- btcjson/cmdinfo_test.go | 2 +- btcjson/example_test.go | 12 +++++------- rpcclient/chain.go | 32 ++++++++++++++++++++++++++------ rpcserver.go | 4 ++-- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/btcjson/chainsvrcmds_test.go b/btcjson/chainsvrcmds_test.go index 8cb4ee765a..a861af1aa7 100644 --- a/btcjson/chainsvrcmds_test.go +++ b/btcjson/chainsvrcmds_test.go @@ -142,50 +142,43 @@ func TestChainSvrCmds(t *testing.T) { { name: "getblock", newCmd: func() (interface{}, error) { - return btcjson.NewCmd("getblock", "123") + return btcjson.NewCmd("getblock", "123", btcjson.Int(0)) }, staticCmd: func() interface{} { - return btcjson.NewGetBlockCmd("123", nil, nil) + return btcjson.NewGetBlockCmd("123", btcjson.Int(0)) }, - marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123"],"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(false), + Verbosity: btcjson.Int(0), }, }, { 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.Int(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.Int(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.Bool(true), btcjson.Bool(true)) + return btcjson.NewGetBlockCmd("123", btcjson.Int(2)) }, - marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",true,true],"id":1}`, + marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",2],"id":1}`, unmarshalled: &btcjson.GetBlockCmd{ Hash: "123", - Verbose: btcjson.Bool(true), - VerboseTx: btcjson.Bool(true), + Verbosity: btcjson.Int(2), }, }, { diff --git a/btcjson/cmdinfo_test.go b/btcjson/cmdinfo_test.go index 044040279a..61a693e404 100644 --- a/btcjson/cmdinfo_test.go +++ b/btcjson/cmdinfo_test.go @@ -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)`, }, } diff --git a/btcjson/example_test.go b/btcjson/example_test.go index 527252c7fb..cbca495b38 100644 --- a/btcjson/example_test.go +++ b/btcjson/example_test.go @@ -21,7 +21,7 @@ func ExampleMarshalCmd() { // convenience function for creating a pointer out of a primitive for // optional parameters. blockHash := "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" - gbCmd := btcjson.NewGetBlockCmd(blockHash, btcjson.Bool(false), nil) + gbCmd := btcjson.NewGetBlockCmd(blockHash, btcjson.Int(1)) // Marshal the command to the format suitable for sending to the RPC // server. Typically the client would increment the id here which is @@ -38,7 +38,7 @@ func ExampleMarshalCmd() { fmt.Printf("%s\n", marshalledBytes) // Output: - // {"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",false],"id":1} + // {"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",1],"id":1} } // This example demonstrates how to unmarshal a JSON-RPC request and then @@ -46,7 +46,7 @@ func ExampleMarshalCmd() { func ExampleUnmarshalCmd() { // Ordinarily this would be read from the wire, but for this example, // it is hard coded here for clarity. - data := []byte(`{"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",false],"id":1}`) + data := []byte(`{"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",1],"id":1}`) // Unmarshal the raw bytes from the wire into a JSON-RPC request. var request btcjson.Request @@ -84,13 +84,11 @@ func ExampleUnmarshalCmd() { // Display the fields in the concrete command. fmt.Println("Hash:", gbCmd.Hash) - fmt.Println("Verbose:", *gbCmd.Verbose) - fmt.Println("VerboseTx:", *gbCmd.VerboseTx) + fmt.Println("Verbosity:", *gbCmd.Verbosity) // Output: // Hash: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f - // Verbose: false - // VerboseTx: false + // Verbosity: 0 } // This example demonstrates how to marshal a JSON-RPC response. diff --git a/rpcclient/chain.go b/rpcclient/chain.go index 996d80458c..6815e1fa78 100644 --- a/rpcclient/chain.go +++ b/rpcclient/chain.go @@ -97,7 +97,7 @@ func (c *Client) GetBlockAsync(blockHash *chainhash.Hash) FutureGetBlockResult { hash = blockHash.String() } - cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(false), nil) + cmd := btcjson.NewGetBlockCmd(hash, nil) return c.sendCmd(cmd) } @@ -140,8 +140,9 @@ func (c *Client) GetBlockVerboseAsync(blockHash *chainhash.Hash) FutureGetBlockV if blockHash != nil { hash = blockHash.String() } - - cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), nil) + // From the bitcoin-cli getblock documentation: + // "If verbosity is 1, returns an Object with information about block ." + cmd := btcjson.NewGetBlockCmd(hash, btcjson.Int(1)) return c.sendCmd(cmd) } @@ -154,18 +155,37 @@ func (c *Client) GetBlockVerbose(blockHash *chainhash.Hash) (*btcjson.GetBlockVe return c.GetBlockVerboseAsync(blockHash).Receive() } +type FutureGetBlockVerboseTxResult chan *response + +func (r FutureGetBlockVerboseTxResult) Receive() (*btcjson.GetBlockVerboseTxResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + var blockResult btcjson.GetBlockVerboseTxResult + err = json.Unmarshal(res, &blockResult) + if err != nil { + return nil, err + } + + return &blockResult, nil +} + // GetBlockVerboseTxAsync 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 GetBlockVerboseTx or the blocking version and more details. -func (c *Client) GetBlockVerboseTxAsync(blockHash *chainhash.Hash) FutureGetBlockVerboseResult { +func (c *Client) GetBlockVerboseTxAsync(blockHash *chainhash.Hash) FutureGetBlockVerboseTxResult { hash := "" if blockHash != nil { hash = blockHash.String() } + // From the bitcoin-cli getblock documentation: + // "If verbosity is 2, returns an Object with information about block and information about each transaction." + cmd := btcjson.NewGetBlockCmd(hash, btcjson.Int(2)) - cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), btcjson.Bool(true)) return c.sendCmd(cmd) } @@ -174,7 +194,7 @@ func (c *Client) GetBlockVerboseTxAsync(blockHash *chainhash.Hash) FutureGetBloc // // See GetBlockVerbose if only transaction hashes are preferred. // See GetBlock to retrieve a raw block instead. -func (c *Client) GetBlockVerboseTx(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseResult, error) { +func (c *Client) GetBlockVerboseTx(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseTxResult, error) { return c.GetBlockVerboseTxAsync(blockHash).Receive() } diff --git a/rpcserver.go b/rpcserver.go index e762cc1a9d..2da7e9f57e 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1084,7 +1084,7 @@ func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i // When the verbose flag isn't set, simply return the serialized block // as a hex-encoded string. - if c.Verbose != nil && !*c.Verbose { + if c.Verbosity != nil && *c.Verbosity == 0 { return hex.EncodeToString(blkBytes), nil } @@ -1137,7 +1137,7 @@ func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i NextHash: nextHashString, } - if c.VerboseTx == nil || !*c.VerboseTx { + if *c.Verbosity == 1 { transactions := blk.Transactions() txNames := make([]string, len(transactions)) for i, tx := range transactions { From a310aa6e7464e9624eb8078260e248c28c8f67bb Mon Sep 17 00:00:00 2001 From: jalavosus Date: Thu, 5 Mar 2020 06:35:34 -0500 Subject: [PATCH 4/4] All tests pass --- btcjson/cmdinfo_test.go | 2 +- btcjson/example_test.go | 6 +++--- rpcserverhelp.go | 7 +++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/btcjson/cmdinfo_test.go b/btcjson/cmdinfo_test.go index 61a693e404..471fccfc77 100644 --- a/btcjson/cmdinfo_test.go +++ b/btcjson/cmdinfo_test.go @@ -151,7 +151,7 @@ func TestMethodUsageText(t *testing.T) { { name: "getblock", method: "getblock", - expected: `getblock "hash" (verbosity=1)`, + expected: `getblock "hash" (verbosity=0)`, }, } diff --git a/btcjson/example_test.go b/btcjson/example_test.go index cbca495b38..73dd804073 100644 --- a/btcjson/example_test.go +++ b/btcjson/example_test.go @@ -21,7 +21,7 @@ func ExampleMarshalCmd() { // convenience function for creating a pointer out of a primitive for // optional parameters. blockHash := "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" - gbCmd := btcjson.NewGetBlockCmd(blockHash, btcjson.Int(1)) + gbCmd := btcjson.NewGetBlockCmd(blockHash, btcjson.Int(0)) // Marshal the command to the format suitable for sending to the RPC // server. Typically the client would increment the id here which is @@ -38,7 +38,7 @@ func ExampleMarshalCmd() { fmt.Printf("%s\n", marshalledBytes) // Output: - // {"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",1],"id":1} + // {"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",0],"id":1} } // This example demonstrates how to unmarshal a JSON-RPC request and then @@ -46,7 +46,7 @@ func ExampleMarshalCmd() { func ExampleUnmarshalCmd() { // Ordinarily this would be read from the wire, but for this example, // it is hard coded here for clarity. - data := []byte(`{"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",1],"id":1}`) + data := []byte(`{"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",0],"id":1}`) // Unmarshal the raw bytes from the wire into a JSON-RPC request. var request btcjson.Request diff --git a/rpcserverhelp.go b/rpcserverhelp.go index cc6935b512..cee2407566 100644 --- a/rpcserverhelp.go +++ b/rpcserverhelp.go @@ -162,10 +162,9 @@ var helpDescsEnUS = map[string]string{ // GetBlockCmd help. "getblock--synopsis": "Returns information about a block given its hash.", "getblock-hash": "The hash of the block", - "getblock-verbose": "Specifies the block is returned as a JSON object instead of hex-encoded string", - "getblock-verbosetx": "Specifies that each transaction is returned as a JSON object and only applies if the verbose flag is true (btcd extension)", - "getblock--condition0": "verbose=false", - "getblock--condition1": "verbose=true", + "getblock-verbosity": "Specifies whether the block data should be returned as a hex-encoded string (0), as parsed data with a slice of TXIDs (1), or as parsed data with parsed transaction data (2) ", + "getblock--condition0": "verbosity=0", + "getblock--condition1": "verbosity=1", "getblock--result0": "Hex-encoded bytes of the serialized block", // GetBlockChainInfoCmd help.