Skip to content

Commit

Permalink
Merge pull request #1577 from wpaulino/getblock-compat
Browse files Browse the repository at this point in the history
rpcclient: send legacy GetBlock request for backwards compatibility
  • Loading branch information
Roasbeef committed May 15, 2020
2 parents 9a88e1d + 742935e commit 9f0179f
Showing 1 changed file with 84 additions and 11 deletions.
95 changes: 84 additions & 11 deletions rpcclient/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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.
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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."
//
// 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)
return FutureGetBlockVerboseTxResult{
client: c,
hash: hash,
Response: c.sendCmd(cmd),
}
}

// GetBlockVerboseTx returns a data structure from the server with information
Expand Down

0 comments on commit 9f0179f

Please sign in to comment.