Delay JSON unmarshaling until needed.
This change takes advantage of the RawMessage type in the encoding/json package to defer unmarshaling of all JSON-RPC values until absolutely necessary. This is particularly important for request passthrough when btcwallet must ask btcd to handle a chain request for a wallet client. In the previous code, during the marshal and unmarshal dance to set the original client's request id in the btcd response, large JSON numbers were being mangled to use (scientific) E notation even when they could be represented as a integer without any loss of precision.
This commit is contained in:
parent
6216012aac
commit
b1a71d5f83
4 changed files with 197 additions and 197 deletions
103
rpc.go
103
rpc.go
|
@ -17,14 +17,41 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"github.com/conformal/btcjson"
|
"github.com/conformal/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RPCResponse is an interface type covering both server
|
// RawRPCResponse is a response to a JSON-RPC request with delayed
|
||||||
// (frontend <-> btcwallet) and client (btcwallet <-> btcd) responses.
|
// unmarshaling.
|
||||||
type RPCResponse interface {
|
type RawRPCResponse struct {
|
||||||
Result() interface{}
|
Id *uint64
|
||||||
Error() *btcjson.Error
|
Result *json.RawMessage `json:"result"`
|
||||||
|
Error *json.RawMessage `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RawRPCResponse) FinishUnmarshal(v interface{}) (interface{}, *btcjson.Error) {
|
||||||
|
// JSON-RPC spec makes this handling easier-ish because both result and
|
||||||
|
// error cannot be non-nil.
|
||||||
|
var jsonErr *btcjson.Error
|
||||||
|
if r.Error != nil {
|
||||||
|
if err := json.Unmarshal([]byte(*r.Error), &jsonErr); err != nil {
|
||||||
|
return nil, &btcjson.Error{
|
||||||
|
Code: btcjson.ErrParse.Code,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, jsonErr
|
||||||
|
}
|
||||||
|
if r.Result != nil {
|
||||||
|
if err := json.Unmarshal([]byte(*r.Result), &v); err != nil {
|
||||||
|
return nil, &btcjson.Error{
|
||||||
|
Code: btcjson.ErrParse.Code,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientRequest is a type holding a bitcoin client's request and
|
// ClientRequest is a type holding a bitcoin client's request and
|
||||||
|
@ -32,7 +59,7 @@ type RPCResponse interface {
|
||||||
type ClientRequest struct {
|
type ClientRequest struct {
|
||||||
ws bool
|
ws bool
|
||||||
request btcjson.Cmd
|
request btcjson.Cmd
|
||||||
response chan RPCResponse
|
response chan RawRPCResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientRequest creates a new ClientRequest from a btcjson.Cmd.
|
// NewClientRequest creates a new ClientRequest from a btcjson.Cmd.
|
||||||
|
@ -40,78 +67,28 @@ func NewClientRequest(request btcjson.Cmd, ws bool) *ClientRequest {
|
||||||
return &ClientRequest{
|
return &ClientRequest{
|
||||||
ws: ws,
|
ws: ws,
|
||||||
request: request,
|
request: request,
|
||||||
response: make(chan RPCResponse),
|
response: make(chan RawRPCResponse),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle sends a client request to the RPC gateway for processing,
|
// Handle sends a client request to the RPC gateway for processing,
|
||||||
// and returns the result when handling is finished.
|
// and returns the result when handling is finished.
|
||||||
func (r *ClientRequest) Handle() (interface{}, *btcjson.Error) {
|
func (r *ClientRequest) Handle() RawRPCResponse {
|
||||||
clientRequests <- r
|
clientRequests <- r
|
||||||
resp := <-r.response
|
return <-r.response
|
||||||
return resp.Result(), resp.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientResponse holds a result and error returned from handling a
|
|
||||||
// client's request.
|
|
||||||
type ClientResponse struct {
|
|
||||||
result interface{}
|
|
||||||
err *btcjson.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result returns the result of a response to a client.
|
|
||||||
func (r *ClientResponse) Result() interface{} {
|
|
||||||
return r.result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the error of a response to a client, or nil if
|
|
||||||
// there is no error.
|
|
||||||
func (r *ClientResponse) Error() *btcjson.Error {
|
|
||||||
return r.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerRequest is a type responsible for handling requests to a bitcoin
|
// ServerRequest is a type responsible for handling requests to a bitcoin
|
||||||
// server and providing a method to access the response.
|
// server and providing a method to access the response.
|
||||||
type ServerRequest struct {
|
type ServerRequest struct {
|
||||||
request btcjson.Cmd
|
request btcjson.Cmd
|
||||||
result interface{}
|
response chan RawRPCResponse
|
||||||
response chan RPCResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServerRequest creates a new ServerRequest from a btcjson.Cmd. request
|
// NewServerRequest creates a new ServerRequest from a btcjson.Cmd.
|
||||||
// may be nil to create a new var for the result (with types determined by
|
func NewServerRequest(request btcjson.Cmd) *ServerRequest {
|
||||||
// the unmarshaling rules described in the json package), or set to a var
|
|
||||||
// with an expected type (i.e. *btcjson.BlockResult) to directly unmarshal
|
|
||||||
// the response's result into a convenient type.
|
|
||||||
func NewServerRequest(request btcjson.Cmd, result interface{}) *ServerRequest {
|
|
||||||
return &ServerRequest{
|
return &ServerRequest{
|
||||||
request: request,
|
request: request,
|
||||||
result: result,
|
response: make(chan RawRPCResponse, 1),
|
||||||
response: make(chan RPCResponse, 1),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerResponse holds a response's result and error returned from sending a
|
|
||||||
// ServerRequest.
|
|
||||||
type ServerResponse struct {
|
|
||||||
// Result will be set to a concrete type (i.e. *btcjson.BlockResult)
|
|
||||||
// and may be type asserted to that type if a non-nil result was used
|
|
||||||
// to create the originating ServerRequest. Otherwise, Result will be
|
|
||||||
// set to new memory allocated by json.Unmarshal, and the type rules
|
|
||||||
// for unmarshaling described in the json package should be followed
|
|
||||||
// when type asserting Result.
|
|
||||||
result interface{}
|
|
||||||
|
|
||||||
// Err points to an unmarshaled error, or nil if result is valid.
|
|
||||||
err *btcjson.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result returns the result of a server's RPC response.
|
|
||||||
func (r *ServerResponse) Result() interface{} {
|
|
||||||
return r.result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result returns the error of a server's RPC response.
|
|
||||||
func (r *ServerResponse) Error() *btcjson.Error {
|
|
||||||
return r.err
|
|
||||||
}
|
|
||||||
|
|
209
rpcclient.go
209
rpcclient.go
|
@ -37,7 +37,7 @@ type ServerConn interface {
|
||||||
// SendRequest sends a bitcoin RPC request, returning a channel to
|
// SendRequest sends a bitcoin RPC request, returning a channel to
|
||||||
// read the reply. A channel is used so both synchronous and
|
// read the reply. A channel is used so both synchronous and
|
||||||
// asynchronous RPC can be supported.
|
// asynchronous RPC can be supported.
|
||||||
SendRequest(request *ServerRequest) chan RPCResponse
|
SendRequest(request *ServerRequest) chan RawRPCResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrBtcdDisconnected describes an error where an operation cannot
|
// ErrBtcdDisconnected describes an error where an operation cannot
|
||||||
|
@ -48,6 +48,9 @@ var ErrBtcdDisconnected = btcjson.Error{
|
||||||
Message: "btcd disconnected",
|
Message: "btcd disconnected",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrBtcdDisconnectedRaw is the raw JSON encoding of ErrBtcdDisconnected.
|
||||||
|
var ErrBtcdDisconnectedRaw = json.RawMessage(`{"code":-1,"message":"btcd disconnected"}`)
|
||||||
|
|
||||||
// BtcdRPCConn is a type managing a client connection to a btcd RPC server
|
// BtcdRPCConn is a type managing a client connection to a btcd RPC server
|
||||||
// over websockets.
|
// over websockets.
|
||||||
type BtcdRPCConn struct {
|
type BtcdRPCConn struct {
|
||||||
|
@ -72,23 +75,20 @@ func NewBtcdRPCConn(ws *websocket.Conn) *BtcdRPCConn {
|
||||||
|
|
||||||
// SendRequest sends an RPC request and returns a channel to read the response's
|
// SendRequest sends an RPC request and returns a channel to read the response's
|
||||||
// result and error. Part of the RPCConn interface.
|
// result and error. Part of the RPCConn interface.
|
||||||
func (btcd *BtcdRPCConn) SendRequest(request *ServerRequest) chan RPCResponse {
|
func (btcd *BtcdRPCConn) SendRequest(request *ServerRequest) chan RawRPCResponse {
|
||||||
select {
|
select {
|
||||||
case <-btcd.closed:
|
case <-btcd.closed:
|
||||||
// The connection has closed, so instead of adding and sending
|
// The connection has closed, so instead of adding and sending
|
||||||
// a request, return a channel that just replies with the
|
// a request, return a channel that just replies with the
|
||||||
// error for a disconnected btcd.
|
// error for a disconnected btcd.
|
||||||
responseChan := make(chan RPCResponse, 1)
|
responseChan := make(chan RawRPCResponse, 1)
|
||||||
response := &ServerResponse{
|
responseChan <- RawRPCResponse{Error: &ErrBtcdDisconnectedRaw}
|
||||||
err: &ErrBtcdDisconnected,
|
|
||||||
}
|
|
||||||
responseChan <- response
|
|
||||||
return responseChan
|
return responseChan
|
||||||
|
|
||||||
default:
|
default:
|
||||||
addRequest := &AddRPCRequest{
|
addRequest := &AddRPCRequest{
|
||||||
Request: request,
|
Request: request,
|
||||||
ResponseChan: make(chan chan RPCResponse, 1),
|
ResponseChan: make(chan chan RawRPCResponse, 1),
|
||||||
}
|
}
|
||||||
btcd.addRequest <- addRequest
|
btcd.addRequest <- addRequest
|
||||||
return <-addRequest.ResponseChan
|
return <-addRequest.ResponseChan
|
||||||
|
@ -124,7 +124,7 @@ func (btcd *BtcdRPCConn) Close() {
|
||||||
// being manaaged by a btcd RPC connection.
|
// being manaaged by a btcd RPC connection.
|
||||||
type AddRPCRequest struct {
|
type AddRPCRequest struct {
|
||||||
Request *ServerRequest
|
Request *ServerRequest
|
||||||
ResponseChan chan chan RPCResponse
|
ResponseChan chan chan RawRPCResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
// send performs the actual send of the marshaled request over the btcd
|
// send performs the actual send of the marshaled request over the btcd
|
||||||
|
@ -136,17 +136,11 @@ func (btcd *BtcdRPCConn) send(rpcrequest *ServerRequest) error {
|
||||||
return websocket.Message.Send(btcd.ws, mrequest)
|
return websocket.Message.Send(btcd.ws, mrequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
type receivedResponse struct {
|
|
||||||
id uint64
|
|
||||||
raw string
|
|
||||||
reply *btcjson.Reply
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start starts the goroutines required to send RPC requests and listen for
|
// Start starts the goroutines required to send RPC requests and listen for
|
||||||
// replies.
|
// replies.
|
||||||
func (btcd *BtcdRPCConn) Start() {
|
func (btcd *BtcdRPCConn) Start() {
|
||||||
done := btcd.closed
|
done := btcd.closed
|
||||||
responses := make(chan *receivedResponse)
|
responses := make(chan RawRPCResponse)
|
||||||
|
|
||||||
// Maintain a map of JSON IDs to RPCRequests currently being waited on.
|
// Maintain a map of JSON IDs to RPCRequests currently being waited on.
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -167,49 +161,61 @@ func (btcd *BtcdRPCConn) Start() {
|
||||||
|
|
||||||
addrequest.ResponseChan <- rpcrequest.response
|
addrequest.ResponseChan <- rpcrequest.response
|
||||||
|
|
||||||
case recvResponse, ok := <-responses:
|
case rawResponse, ok := <-responses:
|
||||||
if !ok {
|
if !ok {
|
||||||
responses = nil
|
responses = nil
|
||||||
close(done)
|
close(done)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
rpcrequest, ok := m[recvResponse.id]
|
rpcrequest, ok := m[*rawResponse.Id]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warnf("Received unexpected btcd response")
|
log.Warnf("Received unexpected btcd response")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
delete(m, recvResponse.id)
|
delete(m, *rawResponse.Id)
|
||||||
|
|
||||||
// If no result var was set, create and send
|
rpcrequest.response <- rawResponse
|
||||||
// send the response unmarshaled by the json
|
|
||||||
// package.
|
/*
|
||||||
if rpcrequest.result == nil {
|
//rpcrequest.result
|
||||||
|
var jsonErr *btcjson.Error
|
||||||
|
if rawResponse.result != nil {
|
||||||
|
}
|
||||||
|
err := json.Unmarshal([]byte(*rawResponse.result), &result)
|
||||||
|
err := json.Unmarshal([]byte(*rawResponse.error), &error)
|
||||||
|
|
||||||
|
rawResult := recvResponse
|
||||||
|
rawError := recvResponse
|
||||||
response := &ServerResponse{
|
response := &ServerResponse{
|
||||||
result: recvResponse.reply.Result,
|
result: r.Result,
|
||||||
err: recvResponse.reply.Error,
|
err: jsonErr
|
||||||
}
|
}
|
||||||
rpcrequest.response <- response
|
rpcrequest.response <- response
|
||||||
continue
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
// A return var was set, so unmarshal again
|
/*
|
||||||
// into the var before sending the response.
|
// If no result var was set, create and send
|
||||||
r := &btcjson.Reply{
|
// send the response unmarshaled by the json
|
||||||
Result: rpcrequest.result,
|
// package.
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(recvResponse.raw), &r)
|
|
||||||
response := &ServerResponse{
|
if rpcrequest.result == nil {
|
||||||
result: r.Result,
|
response := &ServerResponse{
|
||||||
err: r.Error,
|
result: recvResponse.reply.Result,
|
||||||
}
|
err: recvResponse.reply.Error,
|
||||||
rpcrequest.response <- response
|
}
|
||||||
|
rpcrequest.response <- response
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// A return var was set, so unmarshal again
|
||||||
|
// into the var before sending the response.
|
||||||
|
*/
|
||||||
|
|
||||||
case <-done:
|
case <-done:
|
||||||
response := &ServerResponse{
|
resp := RawRPCResponse{Error: &ErrBtcdDisconnectedRaw}
|
||||||
err: &ErrBtcdDisconnected,
|
|
||||||
}
|
|
||||||
for _, request := range m {
|
for _, request := range m {
|
||||||
request.response <- response
|
request.response <- resp
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -256,28 +262,18 @@ func (btcd *BtcdRPCConn) Start() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarshalResponse attempts to unmarshal a marshaled JSON-RPC
|
// unmarshalResponse attempts to unmarshal a marshaled JSON-RPC response.
|
||||||
// response.
|
func unmarshalResponse(s string) (RawRPCResponse, error) {
|
||||||
func unmarshalResponse(s string) (*receivedResponse, error) {
|
var r RawRPCResponse
|
||||||
var r btcjson.Reply
|
|
||||||
if err := json.Unmarshal([]byte(s), &r); err != nil {
|
if err := json.Unmarshal([]byte(s), &r); err != nil {
|
||||||
return nil, err
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a valid ID.
|
// Check for a valid ID.
|
||||||
if r.Id == nil {
|
if r.Id == nil {
|
||||||
return nil, errors.New("id is nil")
|
return r, errors.New("id is null")
|
||||||
}
|
}
|
||||||
fid, ok := (*r.Id).(float64)
|
return r, nil
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("id is not a number")
|
|
||||||
}
|
|
||||||
response := &receivedResponse{
|
|
||||||
id: uint64(fid),
|
|
||||||
raw: s,
|
|
||||||
reply: &r,
|
|
||||||
}
|
|
||||||
return response, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarshalNotification attempts to unmarshal a marshaled JSON-RPC
|
// unmarshalNotification attempts to unmarshal a marshaled JSON-RPC
|
||||||
|
@ -307,61 +303,67 @@ type GetBestBlockResult struct {
|
||||||
// in the main chain.
|
// in the main chain.
|
||||||
func GetBestBlock(rpc ServerConn) (*GetBestBlockResult, *btcjson.Error) {
|
func GetBestBlock(rpc ServerConn) (*GetBestBlockResult, *btcjson.Error) {
|
||||||
cmd := btcws.NewGetBestBlockCmd(<-NewJSONID)
|
cmd := btcws.NewGetBestBlockCmd(<-NewJSONID)
|
||||||
request := NewServerRequest(cmd, new(GetBestBlockResult))
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
|
||||||
if response.Error() != nil {
|
var resultData GetBestBlockResult
|
||||||
return nil, response.Error()
|
_, jsonErr := response.FinishUnmarshal(&resultData)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return nil, jsonErr
|
||||||
}
|
}
|
||||||
return response.Result().(*GetBestBlockResult), nil
|
return &resultData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlock requests details about a block with the given hash.
|
// GetBlock requests details about a block with the given hash.
|
||||||
func GetBlock(rpc ServerConn, blockHash string) (*btcjson.BlockResult, *btcjson.Error) {
|
func GetBlock(rpc ServerConn, blockHash string) (*btcjson.BlockResult, *btcjson.Error) {
|
||||||
// NewGetBlockCmd cannot fail with no optargs, so omit the check.
|
// NewGetBlockCmd cannot fail with no optargs, so omit the check.
|
||||||
cmd, _ := btcjson.NewGetBlockCmd(<-NewJSONID, blockHash)
|
cmd, _ := btcjson.NewGetBlockCmd(<-NewJSONID, blockHash)
|
||||||
request := NewServerRequest(cmd, new(btcjson.BlockResult))
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
|
||||||
if response.Error() != nil {
|
var resultData btcjson.BlockResult
|
||||||
return nil, response.Error()
|
_, jsonErr := response.FinishUnmarshal(&resultData)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return nil, jsonErr
|
||||||
}
|
}
|
||||||
return response.Result().(*btcjson.BlockResult), nil
|
return &resultData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentNet requests the network a bitcoin RPC server is running on.
|
// GetCurrentNet requests the network a bitcoin RPC server is running on.
|
||||||
func GetCurrentNet(rpc ServerConn) (btcwire.BitcoinNet, *btcjson.Error) {
|
func GetCurrentNet(rpc ServerConn) (btcwire.BitcoinNet, *btcjson.Error) {
|
||||||
cmd := btcws.NewGetCurrentNetCmd(<-NewJSONID)
|
cmd := btcws.NewGetCurrentNetCmd(<-NewJSONID)
|
||||||
request := NewServerRequest(cmd, nil)
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
|
||||||
if response.Error() != nil {
|
var resultData uint32
|
||||||
return 0, response.Error()
|
_, jsonErr := response.FinishUnmarshal(&resultData)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return 0, jsonErr
|
||||||
}
|
}
|
||||||
return btcwire.BitcoinNet(uint32(response.Result().(float64))), nil
|
return btcwire.BitcoinNet(resultData), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyBlocks requests blockconnected and blockdisconnected notifications.
|
// NotifyBlocks requests blockconnected and blockdisconnected notifications.
|
||||||
func NotifyBlocks(rpc ServerConn) *btcjson.Error {
|
func NotifyBlocks(rpc ServerConn) *btcjson.Error {
|
||||||
cmd := btcws.NewNotifyBlocksCmd(<-NewJSONID)
|
cmd := btcws.NewNotifyBlocksCmd(<-NewJSONID)
|
||||||
request := NewServerRequest(cmd, nil)
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
_, jsonErr := response.FinishUnmarshal(nil)
|
||||||
return response.Error()
|
return jsonErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyNewTXs requests notifications for new transactions that spend
|
// NotifyNewTXs requests notifications for new transactions that spend
|
||||||
// to any of the addresses in addrs.
|
// to any of the addresses in addrs.
|
||||||
func NotifyNewTXs(rpc ServerConn, addrs []string) *btcjson.Error {
|
func NotifyNewTXs(rpc ServerConn, addrs []string) *btcjson.Error {
|
||||||
cmd := btcws.NewNotifyNewTXsCmd(<-NewJSONID, addrs)
|
cmd := btcws.NewNotifyNewTXsCmd(<-NewJSONID, addrs)
|
||||||
request := NewServerRequest(cmd, nil)
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
_, jsonErr := response.FinishUnmarshal(nil)
|
||||||
return response.Error()
|
return jsonErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifySpent requests notifications for when a transaction is processed which
|
// NotifySpent requests notifications for when a transaction is processed which
|
||||||
// spends op.
|
// spends op.
|
||||||
func NotifySpent(rpc ServerConn, op *btcwire.OutPoint) *btcjson.Error {
|
func NotifySpent(rpc ServerConn, op *btcwire.OutPoint) *btcjson.Error {
|
||||||
cmd := btcws.NewNotifySpentCmd(<-NewJSONID, op)
|
cmd := btcws.NewNotifySpentCmd(<-NewJSONID, op)
|
||||||
request := NewServerRequest(cmd, nil)
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
_, jsonErr := response.FinishUnmarshal(nil)
|
||||||
return response.Error()
|
return jsonErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rescan requests a blockchain rescan for transactions to any number of
|
// Rescan requests a blockchain rescan for transactions to any number of
|
||||||
|
@ -371,21 +373,23 @@ func Rescan(rpc ServerConn, beginBlock int32, addrs []string,
|
||||||
|
|
||||||
// NewRescanCmd cannot fail with no optargs, so omit the check.
|
// NewRescanCmd cannot fail with no optargs, so omit the check.
|
||||||
cmd, _ := btcws.NewRescanCmd(<-NewJSONID, beginBlock, addrs, outpoints)
|
cmd, _ := btcws.NewRescanCmd(<-NewJSONID, beginBlock, addrs, outpoints)
|
||||||
request := NewServerRequest(cmd, nil)
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
_, jsonErr := response.FinishUnmarshal(nil)
|
||||||
return response.Error()
|
return jsonErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRawTransaction sends a hex-encoded transaction for relay.
|
// SendRawTransaction sends a hex-encoded transaction for relay.
|
||||||
func SendRawTransaction(rpc ServerConn, hextx string) (txid string, error *btcjson.Error) {
|
func SendRawTransaction(rpc ServerConn, hextx string) (txid string, error *btcjson.Error) {
|
||||||
// NewSendRawTransactionCmd cannot fail, so omit the check.
|
// NewSendRawTransactionCmd cannot fail, so omit the check.
|
||||||
cmd, _ := btcjson.NewSendRawTransactionCmd(<-NewJSONID, hextx)
|
cmd, _ := btcjson.NewSendRawTransactionCmd(<-NewJSONID, hextx)
|
||||||
request := NewServerRequest(cmd, new(string))
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
|
||||||
if response.Error() != nil {
|
var resultData string
|
||||||
return "", response.Error()
|
_, jsonErr := response.FinishUnmarshal(&resultData)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return "", jsonErr
|
||||||
}
|
}
|
||||||
return *response.Result().(*string), nil
|
return resultData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRawTransaction sends the non-verbose version of a getrawtransaction
|
// GetRawTransaction sends the non-verbose version of a getrawtransaction
|
||||||
|
@ -394,13 +398,14 @@ func SendRawTransaction(rpc ServerConn, hextx string) (txid string, error *btcjs
|
||||||
func GetRawTransaction(rpc ServerConn, txsha *btcwire.ShaHash) (*btcutil.Tx, *btcjson.Error) {
|
func GetRawTransaction(rpc ServerConn, txsha *btcwire.ShaHash) (*btcutil.Tx, *btcjson.Error) {
|
||||||
// NewGetRawTransactionCmd cannot fail with no optargs.
|
// NewGetRawTransactionCmd cannot fail with no optargs.
|
||||||
cmd, _ := btcjson.NewGetRawTransactionCmd(<-NewJSONID, txsha.String())
|
cmd, _ := btcjson.NewGetRawTransactionCmd(<-NewJSONID, txsha.String())
|
||||||
request := NewServerRequest(cmd, new(string))
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
|
||||||
if response.Error() != nil {
|
var resultData string
|
||||||
return nil, response.Error()
|
_, jsonErr := response.FinishUnmarshal(&resultData)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return nil, jsonErr
|
||||||
}
|
}
|
||||||
hextx := *response.Result().(*string)
|
serializedTx, err := hex.DecodeString(resultData)
|
||||||
serializedTx, err := hex.DecodeString(hextx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &btcjson.ErrDecodeHexString
|
return nil, &btcjson.ErrDecodeHexString
|
||||||
}
|
}
|
||||||
|
@ -416,10 +421,12 @@ func GetRawTransaction(rpc ServerConn, txsha *btcwire.ShaHash) (*btcutil.Tx, *bt
|
||||||
func VerboseGetRawTransaction(rpc ServerConn, txsha *btcwire.ShaHash) (*btcjson.TxRawResult, *btcjson.Error) {
|
func VerboseGetRawTransaction(rpc ServerConn, txsha *btcwire.ShaHash) (*btcjson.TxRawResult, *btcjson.Error) {
|
||||||
// NewGetRawTransactionCmd cannot fail with a single optarg.
|
// NewGetRawTransactionCmd cannot fail with a single optarg.
|
||||||
cmd, _ := btcjson.NewGetRawTransactionCmd(<-NewJSONID, txsha.String(), 1)
|
cmd, _ := btcjson.NewGetRawTransactionCmd(<-NewJSONID, txsha.String(), 1)
|
||||||
request := NewServerRequest(cmd, new(btcjson.TxRawResult))
|
response := <-rpc.SendRequest(NewServerRequest(cmd))
|
||||||
response := <-rpc.SendRequest(request)
|
|
||||||
if response.Error() != nil {
|
var resultData btcjson.TxRawResult
|
||||||
return nil, response.Error()
|
_, jsonErr := response.FinishUnmarshal(&resultData)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return nil, jsonErr
|
||||||
}
|
}
|
||||||
return response.Result().(*btcjson.TxRawResult), nil
|
return &resultData, nil
|
||||||
}
|
}
|
||||||
|
|
64
rpcserver.go
64
rpcserver.go
|
@ -20,6 +20,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"github.com/conformal/btcec"
|
"github.com/conformal/btcec"
|
||||||
"github.com/conformal/btcjson"
|
"github.com/conformal/btcjson"
|
||||||
"github.com/conformal/btcscript"
|
"github.com/conformal/btcscript"
|
||||||
|
@ -118,6 +119,9 @@ var ErrServerBusy = btcjson.Error{
|
||||||
Message: "Server busy",
|
Message: "Server busy",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrServerBusyRaw is the raw JSON encoding of ErrServerBusy.
|
||||||
|
var ErrServerBusyRaw = json.RawMessage(`{"code":-32000,"message":"Server busy"}`)
|
||||||
|
|
||||||
// RPCGateway is the common entry point for all client RPC requests and
|
// RPCGateway is the common entry point for all client RPC requests and
|
||||||
// server notifications. If a request needs to be handled by btcwallet,
|
// server notifications. If a request needs to be handled by btcwallet,
|
||||||
// it is sent to WalletRequestProcessor's request queue, or dropped if the
|
// it is sent to WalletRequestProcessor's request queue, or dropped if the
|
||||||
|
@ -149,16 +153,15 @@ func RPCGateway() {
|
||||||
case requestQueue <- r:
|
case requestQueue <- r:
|
||||||
default:
|
default:
|
||||||
// Server busy with too many requests.
|
// Server busy with too many requests.
|
||||||
resp := ClientResponse{
|
resp := RawRPCResponse{
|
||||||
err: &ErrServerBusy,
|
Error: &ErrServerBusyRaw,
|
||||||
}
|
}
|
||||||
r.response <- &resp
|
r.response <- resp
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
r.request.SetId(<-NewJSONID)
|
r.request.SetId(<-NewJSONID)
|
||||||
request := &ServerRequest{
|
request := &ServerRequest{
|
||||||
request: r.request,
|
request: r.request,
|
||||||
result: nil,
|
|
||||||
response: r.response,
|
response: r.response,
|
||||||
}
|
}
|
||||||
CurrentServerConn().SendRequest(request)
|
CurrentServerConn().SendRequest(request)
|
||||||
|
@ -197,9 +200,16 @@ func WalletRequestProcessor() {
|
||||||
result, jsonErr := f(r.request)
|
result, jsonErr := f(r.request)
|
||||||
AcctMgr.Release()
|
AcctMgr.Release()
|
||||||
|
|
||||||
r.response <- &ClientResponse{
|
if jsonErr != nil {
|
||||||
result: result,
|
b, _ := json.Marshal(jsonErr)
|
||||||
err: jsonErr,
|
r.response <- RawRPCResponse{
|
||||||
|
Error: (*json.RawMessage)(&b),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b, _ := json.Marshal(result)
|
||||||
|
r.response <- RawRPCResponse{
|
||||||
|
Result: (*json.RawMessage)(&b),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case n := <-handleNtfn:
|
case n := <-handleNtfn:
|
||||||
|
@ -563,34 +573,35 @@ func GetInfo(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
||||||
// Call down to btcd for all of the information in this command known
|
// Call down to btcd for all of the information in this command known
|
||||||
// by them. This call can not realistically ever fail.
|
// by them. This call can not realistically ever fail.
|
||||||
gicmd, _ := btcjson.NewGetInfoCmd(<-NewJSONID)
|
gicmd, _ := btcjson.NewGetInfoCmd(<-NewJSONID)
|
||||||
req := NewServerRequest(gicmd, make(map[string]interface{}))
|
response := <-CurrentServerConn().SendRequest(NewServerRequest(gicmd))
|
||||||
response := <-CurrentServerConn().SendRequest(req)
|
|
||||||
if response.Error() != nil {
|
var info btcjson.InfoResult
|
||||||
return nil, response.Error()
|
_, jsonErr := response.FinishUnmarshal(&info)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return nil, jsonErr
|
||||||
}
|
}
|
||||||
ret := response.Result().(map[string]interface{})
|
|
||||||
|
|
||||||
balance := float64(0.0)
|
balance := float64(0.0)
|
||||||
accounts := AcctMgr.ListAccounts(1)
|
accounts := AcctMgr.ListAccounts(1)
|
||||||
for _, v := range accounts {
|
for _, v := range accounts {
|
||||||
balance += v
|
balance += v
|
||||||
}
|
}
|
||||||
ret["walletversion"] = wallet.VersCurrent.Uint32()
|
info.WalletVersion = int(wallet.VersCurrent.Uint32())
|
||||||
ret["balance"] = balance
|
info.Balance = balance
|
||||||
// Keypool times are not tracked. set to current time.
|
// Keypool times are not tracked. set to current time.
|
||||||
ret["keypoololdest"] = time.Now().Unix()
|
info.KeypoolOldest = time.Now().Unix()
|
||||||
ret["keypoolsize"] = cfg.KeypoolSize
|
info.KeypoolSize = int(cfg.KeypoolSize)
|
||||||
TxFeeIncrement.Lock()
|
TxFeeIncrement.Lock()
|
||||||
ret["paytxfee"] = TxFeeIncrement.i
|
info.PaytxFee = float64(TxFeeIncrement.i) / float64(btcutil.SatoshiPerBitcoin)
|
||||||
TxFeeIncrement.Unlock()
|
TxFeeIncrement.Unlock()
|
||||||
/*
|
/*
|
||||||
* We don't set the following since they don't make much sense in the
|
* We don't set the following since they don't make much sense in the
|
||||||
* wallet architecture:
|
* wallet architecture:
|
||||||
* ret["unlocked_until"]
|
* - unlocked_until
|
||||||
* ret["errors"]
|
* - errors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return ret, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccount handles a getaccount request by returning the account name
|
// GetAccount handles a getaccount request by returning the account name
|
||||||
|
@ -1070,8 +1081,7 @@ func ListSinceBlock(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req := NewServerRequest(gbh, new(string))
|
bhChan := CurrentServerConn().SendRequest(NewServerRequest(gbh))
|
||||||
bhChan := CurrentServerConn().SendRequest(req)
|
|
||||||
|
|
||||||
txInfoList, err := AcctMgr.ListSinceBlock(height, bs.Height,
|
txInfoList, err := AcctMgr.ListSinceBlock(height, bs.Height,
|
||||||
cmd.TargetConfirmations)
|
cmd.TargetConfirmations)
|
||||||
|
@ -1084,15 +1094,15 @@ func ListSinceBlock(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
||||||
|
|
||||||
// Done with work, get the response.
|
// Done with work, get the response.
|
||||||
response := <-bhChan
|
response := <-bhChan
|
||||||
if response.Error() != nil {
|
var hash string
|
||||||
return nil, response.Error()
|
_, jsonErr := response.FinishUnmarshal(&hash)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return nil, jsonErr
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := response.Result().(*string)
|
|
||||||
|
|
||||||
res := make(map[string]interface{})
|
res := make(map[string]interface{})
|
||||||
res["transactions"] = txInfoList
|
res["transactions"] = txInfoList
|
||||||
res["lastblock"] = *hash
|
res["lastblock"] = hash
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
18
sockets.go
18
sockets.go
|
@ -263,17 +263,23 @@ func (s *server) ReplyToFrontend(msg []byte, ws, authenticated bool) ([]byte, er
|
||||||
}
|
}
|
||||||
|
|
||||||
cReq := NewClientRequest(cmd, ws)
|
cReq := NewClientRequest(cmd, ws)
|
||||||
result, jsonErr := cReq.Handle()
|
rawResp := cReq.Handle()
|
||||||
|
|
||||||
response := btcjson.Reply{
|
response := struct {
|
||||||
Id: &id,
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
Result: result,
|
Id interface{} `json:"id"`
|
||||||
Error: jsonErr,
|
Result *json.RawMessage `json:"result"`
|
||||||
|
Error *json.RawMessage `json:"error"`
|
||||||
|
}{
|
||||||
|
Jsonrpc: "1.0",
|
||||||
|
Id: id,
|
||||||
|
Result: rawResp.Result,
|
||||||
|
Error: rawResp.Error,
|
||||||
}
|
}
|
||||||
mresponse, err := json.Marshal(response)
|
mresponse, err := json.Marshal(response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Cannot marhal response: %v", err)
|
log.Errorf("Cannot marhal response: %v", err)
|
||||||
response = btcjson.Reply{
|
response := btcjson.Reply{
|
||||||
Id: &id,
|
Id: &id,
|
||||||
Error: &btcjson.ErrInternal,
|
Error: &btcjson.ErrInternal,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue