Skip to content

Commit

Permalink
(rpcclient) add batch requests
Browse files Browse the repository at this point in the history
  • Loading branch information
jakesylvestre committed Dec 5, 2019
1 parent a5d1ff5 commit 6278ae4
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 103 deletions.
4 changes: 1 addition & 3 deletions btcjson/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ type Response struct {
ID *interface{} `json:"id"`
}


// NewResponse returns a new JSON-RPC response object given the provided rpc
// version, id, marshalled result, and RPC error. This function is only
// provided in case the caller wants to construct raw responses for some reason.
Expand All @@ -191,7 +190,6 @@ func NewResponse(rpcVersion string, id interface{}, marshalledResult []byte, rpc
}, nil
}


// MarshalResponse marshals the passed rpc version, id, result, and RPCError to
// a JSON-RPC response byte slice that is suitable for transmission to a
// JSON-RPC client.
Expand All @@ -209,4 +207,4 @@ func MarshalResponse(rpcVersion string, id interface{}, result interface{}, rpcE
return nil, err
}
return json.Marshal(&response)
}
}
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -911,7 +911,7 @@ func loadConfig() (*config, []string, error) {

// Only allow TLS to be disabled if the RPC is bound to localhost
// addresses.
if false && !cfg.DisableRPC && cfg.DisableTLS {
if !cfg.DisableRPC && cfg.DisableTLS {
allowedTLSListeners := map[string]struct{}{
"localhost": {},
"127.0.0.1": {},
Expand Down
2 changes: 1 addition & 1 deletion mempool/estimatefee.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const (

bytePerKb = 1000

btcPerSatoshi = 1E-8
btcPerSatoshi = 1e-8
)

var (
Expand Down
8 changes: 4 additions & 4 deletions rpcclient/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -985,9 +985,9 @@ func (c *Client) GetCFilterHeader(blockHash *chainhash.Hash,
type FutureGetBulkResult chan *response

type IndividualBulkResult struct {
Result interface{} `json:"result"`
Error string `json:"error"`
Id uint64 `json:"id"`
Result interface{} `json:"result"`
Error string `json:"error"`
Id uint64 `json:"id"`
}

type BulkResult = map[uint64]IndividualBulkResult
Expand All @@ -1008,4 +1008,4 @@ func (r FutureGetBulkResult) Receive() (BulkResult, error) {
}

return m, nil
}
}
28 changes: 8 additions & 20 deletions rpcclient/examples/bitcoincorehttp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@
package main

import (
"fmt"
"github.com/btcsuite/btcd/rpcclient"
"log"

"github.com/btcsuite/btcd/rpcclient"
)

func main() {
// Connect to local bitcoin core RPC server using HTTP POST mode.
connCfg := &rpcclient.ConnConfig{
Host: "127.0.0.1:8332",
Host: "localhost:8332",
User: "yourrpcuser",
Pass: "yourrpcpass",
DisableConnectOnNew: true,
HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode
DisableTLS: true, // Bitcoin core does not provide TLS by default
}
Expand All @@ -29,20 +28,9 @@ func main() {
defer client.Shutdown()

// Get the current block count.
batchClient := client.Batch()

// batch mode requires async requests
blockCount := batchClient.GetBlockCountAsync()
block1 := batchClient.GetBlockHashAsync(1)
batchClient.GetBlockHashAsync(2)
batchClient.GetBlockHashAsync(3)
block4 := batchClient.GetBlockHashAsync(4)
difficulty := batchClient.GetDifficultyAsync()

batchClient.Send()
//result, err
fmt.Println(blockCount.Receive())
fmt.Println(block1.Receive())
fmt.Println(block4.Receive())
fmt.Println(difficulty.Receive())
blockCount, err := client.GetBlockCount()
if err != nil {
log.Fatal(err)
}
log.Printf("Block count: %d", blockCount)
}
7 changes: 3 additions & 4 deletions rpcclient/examples/bitcoincorehttpbulk/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
Bitcoin Core HTTP POST Example
Bitcoin Core Batch HTTP POST Example
==============================

This example shows how to use the rpcclient package to connect to a Bitcoin
Core RPC server using HTTP POST mode with TLS disabled and gets the current
block count.
Core RPC server using HTTP POST mode with TLS disabled and uses the batch json rpc mode

## Running the Example

Expand All @@ -30,4 +29,4 @@ $ go run *.go

## License

This example is licensed under the [copyfree](http://copyfree.org) ISC License.
This example is licensed under the [copyfree](http://copyfree.org) ISC License.
95 changes: 39 additions & 56 deletions rpcclient/examples/bitcoincorehttpbulk/main.go
Original file line number Diff line number Diff line change
@@ -1,65 +1,48 @@
// Copyright (c) 2014-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package main

import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"github.com/btcsuite/btcd/rpcclient"
"log"
)

func main() {
const (
url = "http://localhost:8332"
rpcUser = "yourrpcuser"
rpcPass = "yourrpcpass"
)

// populate request set
reqs := []string{
`{}`,
`[]`,
`[1]`,
`[1,2,3]`,
`{"foo": "boo"}`, // should be an invalid request
`{"jsonrpc": "1.0", "foo": "boo", "id": "1"}`,
`{"jsonrpc": "1.0", "method": "getblockcount", "params": [], "id": "1"}`,
`{"jsonrpc": "1.0", "method": "getblockcount", "params": "a", "id": "1"}`, // should be invalid since params is neither an array nor a json object.
`[
{"jsonrpc": "2.0", "method": "getblockcount", "params": [], "id": "1"},
{"jsonrpc": "2.0", "method": "decodescript", "params": ["ac"]},
{"jsonrpc": "2.0", "method": "getbestblockhash", "params": [], "id": "2"},
{"foo": "boo"},
{"jsonrpc": "2.0", "method": "getblockcount", "id": "9"} /**/
]`, // should produce invalid request for the `{"foo": "boo"}`.
// Connect to local bitcoin core RPC server using HTTP POST mode.
connCfg := &rpcclient.ConnConfig{
Host: "localhost:8332",
User: "yourrpcuser",
Pass: "yourrpcpass",
DisableConnectOnNew: true,
HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode
DisableTLS: true, // Bitcoin core does not provide TLS by default
}

// Connect to local btcd RPC server using websockets.

client := http.Client{}

for _, jsonReq := range reqs {
bodyReader := bytes.NewReader([]byte(jsonReq))
httpReq, err := http.NewRequest("POST", url, bodyReader)
if err != nil {
fmt.Println(err)
return
}
httpReq.Close = true
httpReq.Header.Set("Content-Type", "application/json")
httpReq.SetBasicAuth(rpcUser, rpcPass)
resp, err := client.Do(httpReq)
if err != nil {
fmt.Println("request:", jsonReq, "response:", err)
return
}

respBytes, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Println(err)
return
}

fmt.Println("request:", jsonReq, "response:", string(respBytes))
// Notice the notification parameter is nil since notifications are
// not supported in HTTP POST mode.
client, err := rpcclient.New(connCfg, nil)
if err != nil {
log.Fatal(err)
}
}
defer client.Shutdown()

// Get the current block count.
batchClient := client.Batch()

// batch mode requires async requests
blockCount := batchClient.GetBlockCountAsync()
block1 := batchClient.GetBlockHashAsync(1)
batchClient.GetBlockHashAsync(2)
batchClient.GetBlockHashAsync(3)
block4 := batchClient.GetBlockHashAsync(4)
difficulty := batchClient.GetDifficultyAsync()

batchClient.Send()
//result, err
fmt.Println(blockCount.Receive())
fmt.Println(block1.Receive())
fmt.Println(block4.Receive())
fmt.Println(difficulty.Receive())
}
20 changes: 9 additions & 11 deletions rpcclient/infrastructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ type Client struct {
disconnected bool

// wether or not to batch requests, false unless changed by Bulk()
batch bool
batch bool
batchList *list.List

// retryCount holds the number of times the client has tried to
Expand Down Expand Up @@ -749,7 +749,7 @@ func (c *Client) handleSendPostMessage(details *sendPostDetails) {
var batchResponse json.RawMessage
if c.batch {
err = json.Unmarshal(respBytes, &batchResponse)
}else {
} else {
err = json.Unmarshal(respBytes, &resp)
}
if err != nil {
Expand All @@ -766,7 +766,7 @@ func (c *Client) handleSendPostMessage(details *sendPostDetails) {
// errors must be dealt with downstream since a whole request cannot
// "error out" other than through the status code error handled above
res, err = batchResponse, nil
}else {
} else {
res, err = resp.result()
}
jReq.responseChan <- &response{result: res, err: err}
Expand Down Expand Up @@ -883,7 +883,7 @@ func (c *Client) sendRequest(jReq *jsonRequest) {
// POST mode, the command is issued via an HTTP client. Otherwise,
// the command is issued via the asynchronous websocket channels.
if c.config.HTTPPostMode {
if c.batch{
if c.batch {
if err := c.addRequest(jReq); err != nil {
log.Warn(err)
}
Expand Down Expand Up @@ -946,7 +946,6 @@ func (c *Client) sendCmd(cmd interface{}) chan *response {
responseChan: responseChan,
}


c.sendRequest(jReq)

return responseChan
Expand Down Expand Up @@ -1313,8 +1312,8 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error
httpClient: httpClient,
requestMap: make(map[uint64]*list.Element),
requestList: list.New(),
batch: false,
batchList: list.New(),
batch: false,
batchList: list.New(),
ntfnHandlers: ntfnHandlers,
ntfnState: newNotificationState(),
sendChan: make(chan []byte, sendBufferSize),
Expand Down Expand Up @@ -1483,7 +1482,6 @@ func (c *Client) Batch() Client {
return *c
}


func (c *Client) sendAsync() FutureGetBulkResult {
// convert the array of marshalled json requests to a single request we can send
responseChan := make(chan *response, 1)
Expand All @@ -1509,10 +1507,10 @@ func (c *Client) sendAsync() FutureGetBulkResult {
}

// send batch requests
func (c *Client) Send() {
func (c *Client) Send() {
result, err := c.sendAsync().Receive()

if err != nil{
if err != nil {
log.Error(err)
}

Expand All @@ -1529,7 +1527,7 @@ func (c *Client) Send() {
requestError = errors.New(individualResult.Error)
}

result := response{
result := response{
result: fullResult,
err: requestError,
}
Expand Down
4 changes: 1 addition & 3 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3837,7 +3837,6 @@ type parsedRPCCmd struct {
err *btcjson.RPCError
}


// standardCmdResult checks that a parsed command is a standard Bitcoin JSON-RPC
// command and runs the appropriate handler to reply to the command. Any
// commands which are not recognized or not implemented will return an error
Expand Down Expand Up @@ -3915,7 +3914,6 @@ func createMarshalledReply(rpcVersion string, id interface{}, result interface{}
return btcjson.MarshalResponse(rpcVersion, id, result, jsonErr)
}


// processRequest determines the incoming request type (single or batched),
// parses it and returns a marshalled response.
func (s *rpcServer) processRequest(request *btcjson.Request, isAdmin bool, closeChan <-chan struct{}) []byte {
Expand All @@ -3924,7 +3922,7 @@ func (s *rpcServer) processRequest(request *btcjson.Request, isAdmin bool, close

if !isAdmin {
if _, ok := rpcLimited[request.Method]; !ok {
jsonErr = internalRPCError("limited user not " +
jsonErr = internalRPCError("limited user not "+
"authorized for this method", "")
}
}
Expand Down

0 comments on commit 6278ae4

Please sign in to comment.