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:
Josh Rickmar 2014-04-09 11:07:09 -05:00
parent 6216012aac
commit b1a71d5f83
4 changed files with 197 additions and 197 deletions

103
rpc.go
View file

@ -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
}

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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,
} }