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

Porting https://github.com/reinerRubin/btcd bug fix #1506

Closed
wants to merge 1 commit into from
Closed
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
8 changes: 3 additions & 5 deletions btcjson/chainsvrcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,20 +130,18 @@ func NewGetBestBlockHashCmd() *GetBestBlockHashCmd {
// GetBlockCmd defines the getblock JSON-RPC command.
type GetBlockCmd struct {
Hash string
Verbose *bool `jsonrpcdefault:"true"`
VerboseTx *bool `jsonrpcdefault:"false"`
Verbosity *uint32 `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 *uint32) *GetBlockCmd {
return &GetBlockCmd{
Hash: hash,
Verbose: verbose,
VerboseTx: verboseTx,
Verbosity: verbosity,
}
}

Expand Down
43 changes: 27 additions & 16 deletions btcjson/chainsvrcmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,15 @@ func TestChainSvrCmds(t *testing.T) {
{
name: "getblock",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123")
return btcjson.NewCmd("getblock", "123", 0)
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", nil, nil)
return btcjson.NewGetBlockCmd("123", btcjson.Uint32(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.Uint32(0),
},
},
{
Expand All @@ -160,32 +159,44 @@ func TestChainSvrCmds(t *testing.T) {
// 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)
verbosityPtr := btcjson.Uint32(1)
return btcjson.NewCmd("getblock", "123", &verbosityPtr)
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Bool(true), nil)
return btcjson.NewGetBlockCmd("123", btcjson.Uint32(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.Uint32(1),
},
},
{
name: "getblock required optional2",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123", true, true)
return btcjson.NewCmd("getblock", "123", 2)
},
staticCmd: func() interface{} {
return btcjson.NewGetBlockCmd("123", btcjson.Bool(true), btcjson.Bool(true))
return btcjson.NewGetBlockCmd("123", btcjson.Uint32(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",
Verbosity: btcjson.Uint32(2),
},
},
{
name: "getblock; default verbose level must be 1",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getblock", "123")
},
staticCmd: func() interface{} {
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(true),
Verbosity: btcjson.Uint32(1),
},
},
{
Expand Down
50 changes: 31 additions & 19 deletions btcjson/chainsvrresults.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,39 @@ 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"`
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"`
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. When the verbose flag is not set, getblock returns a
// hex-encoded string.
// verbose flag is set to 1 (default).
type GetBlockVerboseResult 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 []string `json:"tx,omitempty"`
RawTx []TxRawResult `json:"rawtx,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"`
*GetBlockBaseVerboseResult
Tx []string `json:"tx,omitempty"`
}

// GetBlockVerboseTxResult models the data from the getblock command when the
// verbose flag is set to 2.
type GetBlockVerboseTxResult struct {
*GetBlockBaseVerboseResult
Tx []TxRawResult `json:"tx,omitempty"`
}

// CreateMultiSigResult models the data returned from the createmultisig
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
29 changes: 14 additions & 15 deletions btcjson/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ import (
"github.com/btcsuite/btcd/btcjson"
)

// This example demonstrates how to create and marshal a command into a JSON-RPC
// request.
// Create a new getblock command. Notice the call to btcjson.Uint32 which is a
// convenience function for creating a pointer out of a primitive for
// optional parameters. Notice the nil parameter indicates
// to use the default parameter for that fields. This is a common
// pattern used in all of the New<Foo>Cmdr functions in this package for
// optional fields.
func ExampleMarshalCmd() {
// Create a new getblock command. Notice the nil parameter indicates
// to use the default parameter for that fields. This is a common
// pattern used in all of the New<Foo>Cmd functions in this package for
// optional fields. Also, notice the call to btcjson.Bool which is a
// convenience function for creating a pointer out of a primitive for
// optional parameters.
// Create a new getblock command.
blockHash := "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
gbCmd := btcjson.NewGetBlockCmd(blockHash, btcjson.Bool(false), nil)
gbCmd := btcjson.NewGetBlockCmd(blockHash, btcjson.Uint32(2))
// or
// gbCmd := btcjson.NewGetBlockCmd(blockHash, nil)

// Marshal the command to the format suitable for sending to the RPC
// server. Typically the client would increment the id here which is
Expand All @@ -38,15 +39,15 @@ 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",2],"id":1}
}

// This example demonstrates how to unmarshal a JSON-RPC request and then
// unmarshal the concrete request into a concrete command.
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",0],"id":1}`)

// Unmarshal the raw bytes from the wire into a JSON-RPC request.
var request btcjson.Request
Expand Down Expand Up @@ -84,13 +85,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.
Expand Down
37 changes: 23 additions & 14 deletions btcjson/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ func resultStructHelp(xT descLookupFunc, rt reflect.Type, indentLevel int) []str
fieldType := reflectTypeToJSONType(xT, rtfType)
fieldDescKey := typeName + "-" + fieldName
fieldExamples, isComplex := reflectTypeToJSONExample(xT,
rtfType, indentLevel, fieldDescKey)
if isComplex {
rtfType, indentLevel, fieldDescKey, rtf.Anonymous)
if isComplex && !rtf.Anonymous {
var brace string
kind := rtfType.Kind()
if kind == reflect.Array || kind == reflect.Slice {
Expand All @@ -123,6 +123,8 @@ func resultStructHelp(xT descLookupFunc, rt reflect.Type, indentLevel int) []str
fieldName, brace, fieldType, xT(fieldDescKey))
results = append(results, result)
results = append(results, fieldExamples...)
} else if isComplex && rtf.Anonymous {
results = append(results, fieldExamples...)
} else {
result := fmt.Sprintf("%s\"%s\": %s,\t(%s)\t%s", indent,
fieldName, fieldExamples[0], fieldType,
Expand All @@ -140,7 +142,7 @@ func resultStructHelp(xT descLookupFunc, rt reflect.Type, indentLevel int) []str
// a tab writer. A bool is also returned which specifies whether or not the
// type results in a complex JSON object since they need to be handled
// differently.
func reflectTypeToJSONExample(xT descLookupFunc, rt reflect.Type, indentLevel int, fieldDescKey string) ([]string, bool) {
func reflectTypeToJSONExample(xT descLookupFunc, rt reflect.Type, indentLevel int, fieldDescKey string, embeddedStruct bool) ([]string, bool) {
// Indirect pointer if needed.
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
Expand All @@ -163,7 +165,12 @@ func reflectTypeToJSONExample(xT descLookupFunc, rt reflect.Type, indentLevel in

case reflect.Struct:
indent := strings.Repeat(" ", indentLevel)
results := resultStructHelp(xT, rt, indentLevel+1)
nextIndentLevel := indentLevel

if !embeddedStruct {
nextIndentLevel++
}
results := resultStructHelp(xT, rt, nextIndentLevel)

// An opening brace is needed for the first indent level. For
// all others, it will be included as a part of the previous
Expand All @@ -174,20 +181,22 @@ func reflectTypeToJSONExample(xT descLookupFunc, rt reflect.Type, indentLevel in
copy(newResults[1:], results)
results = newResults
}

// The closing brace has a comma after it except for the first
// indent level. The final tabs are necessary so the tab writer
// lines things up properly.
closingBrace := indent + "}"
if indentLevel > 0 {
closingBrace += ","
if !embeddedStruct {
// The closing brace has a comma after it except for the first
// indent level. The final tabs are necessary so the tab writer
// lines things up properly.
closingBrace := indent + "}"
if indentLevel > 0 {
closingBrace += ","
}
results = append(results, closingBrace+"\t\t")
}
results = append(results, closingBrace+"\t\t")

return results, true

case reflect.Array, reflect.Slice:
results, isComplex := reflectTypeToJSONExample(xT, rt.Elem(),
indentLevel, fieldDescKey)
indentLevel, fieldDescKey, false)

// When the result is complex, it is because this is an array of
// objects.
Expand Down Expand Up @@ -251,7 +260,7 @@ func reflectTypeToJSONExample(xT descLookupFunc, rt reflect.Type, indentLevel in
// type.
func resultTypeHelp(xT descLookupFunc, rt reflect.Type, fieldDescKey string) string {
// Generate the JSON example for the result type.
results, isComplex := reflectTypeToJSONExample(xT, rt, 0, fieldDescKey)
results, isComplex := reflectTypeToJSONExample(xT, rt, 0, fieldDescKey, false)

// When this is a primitive type, add the associated JSON type and
// result description into the final string, format it accordingly,
Expand Down
2 changes: 1 addition & 1 deletion btcjson/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func TestHelpReflectInternals(t *testing.T) {

// Ensure the generated example is as expected.
examples, isComplex := btcjson.TstReflectTypeToJSONExample(xT,
test.reflectType, test.indentLevel, "fdk")
test.reflectType, test.indentLevel, "fdk", false)
if isComplex != test.isComplex {
t.Errorf("Test #%d (%s) unexpected isComplex - got: %v, "+
"want: %v", i, test.name, isComplex,
Expand Down
33 changes: 28 additions & 5 deletions rpcclient/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, btcjson.Uint32(0))
return c.sendCmd(cmd)
}

Expand Down Expand Up @@ -141,7 +141,7 @@ func (c *Client) GetBlockVerboseAsync(blockHash *chainhash.Hash) FutureGetBlockV
hash = blockHash.String()
}

cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), nil)
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Uint32(1))
return c.sendCmd(cmd)
}

Expand All @@ -154,18 +154,41 @@ func (c *Client) GetBlockVerbose(blockHash *chainhash.Hash) (*btcjson.GetBlockVe
return c.GetBlockVerboseAsync(blockHash).Receive()
}

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

// Receive waits for the response promised by the future and returns the data
// structure from the server with information about the requested block.
func (r FutureGetBlockVerboseTxResult) Receive() (*btcjson.GetBlockVerboseTxResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}

// Unmarshal the raw result into a BlockResult.
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()
}

cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), btcjson.Bool(true))
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Uint32(2))

return c.sendCmd(cmd)
}

Expand All @@ -174,7 +197,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()
}

Expand Down
Loading