diff --git a/rpcclient/chain.go b/rpcclient/chain.go index a5ec9e54..6e701b1c 100644 --- a/rpcclient/chain.go +++ b/rpcclient/chain.go @@ -52,14 +52,61 @@ func (c *Client) GetBestBlockHash() (*chainhash.Hash, error) { return c.GetBestBlockHashAsync().Receive() } +// legacyGetBlockRequest constructs and sends a legacy getblock request which +// contains two separate bools to denote verbosity, in contract to a single int +// parameter. +func (c *Client) legacyGetBlockRequest(hash string, verbose, + verboseTx bool) ([]byte, error) { + + hashJSON, err := json.Marshal(hash) + if err != nil { + return nil, err + } + verboseJSON, err := json.Marshal(btcjson.Bool(verbose)) + if err != nil { + return nil, err + } + verboseTxJSON, err := json.Marshal(btcjson.Bool(verboseTx)) + if err != nil { + return nil, err + } + return c.RawRequest("getblock", []json.RawMessage{ + hashJSON, verboseJSON, verboseTxJSON, + }) +} + +// waitForGetBlockRes waits for the response of a getblock request. If the +// response indicates an invalid parameter was provided, a legacy style of the +// request is resent and its response is returned instead. +func (c *Client) waitForGetBlockRes(respChan chan *response, hash string, + verbose, verboseTx bool) ([]byte, error) { + + res, err := receiveFuture(respChan) + + // If we receive an invalid parameter error, then we may be + // communicating with a btcd node which only understands the legacy + // request, so we'll try that. + if err, ok := err.(*btcjson.RPCError); ok && + err.Code == btcjson.ErrRPCInvalidParams.Code { + return c.legacyGetBlockRequest(hash, verbose, verboseTx) + } + + // Otherwise, we can return the response as is. + return res, err +} + // FutureGetBlockResult is a future promise to deliver the result of a // GetBlockAsync RPC invocation (or an applicable error). -type FutureGetBlockResult chan *response +type FutureGetBlockResult struct { + client *Client + hash string + Response chan *response +} // Receive waits for the response promised by the future and returns the raw // block requested from the server given its hash. func (r FutureGetBlockResult) Receive() (*wire.MsgBlock, error) { - res, err := receiveFuture(r) + res, err := r.client.waitForGetBlockRes(r.Response, r.hash, false, false) if err != nil { return nil, err } @@ -98,7 +145,11 @@ func (c *Client) GetBlockAsync(blockHash *chainhash.Hash) FutureGetBlockResult { } cmd := btcjson.NewGetBlockCmd(hash, btcjson.Int(0)) - return c.sendCmd(cmd) + return FutureGetBlockResult{ + client: c, + hash: hash, + Response: c.sendCmd(cmd), + } } // GetBlock returns a raw block from the server given its hash. @@ -111,12 +162,16 @@ func (c *Client) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) { // FutureGetBlockVerboseResult is a future promise to deliver the result of a // GetBlockVerboseAsync RPC invocation (or an applicable error). -type FutureGetBlockVerboseResult chan *response +type FutureGetBlockVerboseResult struct { + client *Client + hash string + Response 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 FutureGetBlockVerboseResult) Receive() (*btcjson.GetBlockVerboseResult, error) { - res, err := receiveFuture(r) + res, err := r.client.waitForGetBlockRes(r.Response, r.hash, true, false) if err != nil { return nil, err } @@ -143,7 +198,11 @@ func (c *Client) GetBlockVerboseAsync(blockHash *chainhash.Hash) FutureGetBlockV // 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) + return FutureGetBlockVerboseResult{ + client: c, + hash: hash, + Response: c.sendCmd(cmd), + } } // GetBlockVerbose returns a data structure from the server with information @@ -155,10 +214,18 @@ func (c *Client) GetBlockVerbose(blockHash *chainhash.Hash) (*btcjson.GetBlockVe return c.GetBlockVerboseAsync(blockHash).Receive() } -type FutureGetBlockVerboseTxResult chan *response +// FutureGetBlockVerboseTxResult is a future promise to deliver the result of a +// GetBlockVerboseTxResult RPC invocation (or an applicable error). +type FutureGetBlockVerboseTxResult struct { + client *Client + hash string + Response chan *response +} +// Receive waits for the response promised by the future and returns a verbose +// version of the block including detailed information about its transactions. func (r FutureGetBlockVerboseTxResult) Receive() (*btcjson.GetBlockVerboseTxResult, error) { - res, err := receiveFuture(r) + res, err := r.client.waitForGetBlockRes(r.Response, r.hash, true, true) if err != nil { return nil, err } @@ -182,11 +249,17 @@ func (c *Client) GetBlockVerboseTxAsync(blockHash *chainhash.Hash) FutureGetBloc 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)) - return c.sendCmd(cmd) + // 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)) + return FutureGetBlockVerboseTxResult{ + client: c, + hash: hash, + Response: c.sendCmd(cmd), + } } // GetBlockVerboseTx returns a data structure from the server with information