btcjson: Replace btcjson with version 2.
This commit removes the old and deprecated btcjsonv1 package, moves the new version 2 package into its place, and updates all imports accordingly.
This commit is contained in:
parent
1e98e23d1f
commit
d8a4423b90
63 changed files with 166 additions and 16401 deletions
|
@ -7,12 +7,11 @@ btcjson
|
||||||
|
|
||||||
Package btcjson implements concrete types for marshalling to and from the
|
Package btcjson implements concrete types for marshalling to and from the
|
||||||
bitcoin JSON-RPC API. A comprehensive suite of tests is provided to ensure
|
bitcoin JSON-RPC API. A comprehensive suite of tests is provided to ensure
|
||||||
proper functionality. Package btcjson is licensed under the copyfree ISC
|
proper functionality.
|
||||||
license.
|
|
||||||
|
|
||||||
Although this package was primarily written for btcd, it has intentionally been
|
Although this package was primarily written for the btcsuite, it has
|
||||||
designed so it can be used as a standalone package for any projects needing to
|
intentionally been designed so it can be used as a standalone package for any
|
||||||
marshal to and from bitcoin JSON-RPC requests and responses.
|
projects needing to marshal to and from bitcoin JSON-RPC requests and responses.
|
||||||
|
|
||||||
Note that although it's possible to use this package directly to implement an
|
Note that although it's possible to use this package directly to implement an
|
||||||
RPC client, it is not recommended since it is only intended as an infrastructure
|
RPC client, it is not recommended since it is only intended as an infrastructure
|
||||||
|
@ -23,42 +22,6 @@ management, websocket support, automatic notification re-registration on
|
||||||
reconnect, and conversion from the raw underlying RPC types (strings, floats,
|
reconnect, and conversion from the raw underlying RPC types (strings, floats,
|
||||||
ints, etc) to higher-level types with many nice and useful properties.
|
ints, etc) to higher-level types with many nice and useful properties.
|
||||||
|
|
||||||
## JSON RPC
|
|
||||||
|
|
||||||
Bitcoin provides an extensive API call list to control the chain and wallet
|
|
||||||
servers through JSON-RPC. These can be used to get information from the server
|
|
||||||
or to cause the server to perform some action.
|
|
||||||
|
|
||||||
The general form of the commands are:
|
|
||||||
|
|
||||||
```JSON
|
|
||||||
{"jsonrpc": "1.0", "id":"test", "method": "getinfo", "params": []}
|
|
||||||
```
|
|
||||||
|
|
||||||
btcjson provides code to easily create these commands from go (as some of the
|
|
||||||
commands can be fairly complex), to send the commands to a running bitcoin RPC
|
|
||||||
server, and to handle the replies (putting them in useful Go data structures).
|
|
||||||
|
|
||||||
## Sample Use
|
|
||||||
|
|
||||||
```Go
|
|
||||||
// Create a new command.
|
|
||||||
cmd, err := btcjson.NewGetBlockCountCmd()
|
|
||||||
if err != nil {
|
|
||||||
// Handle error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal the command to a JSON-RPC formatted byte slice.
|
|
||||||
marshalled, err := btcjson.MarshalCmd(id, cmd)
|
|
||||||
if err != nil {
|
|
||||||
// Handle error
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point marshalled contains the raw bytes that are ready to send
|
|
||||||
// to the RPC server to issue the command.
|
|
||||||
fmt.Printf("%s\n", marshalled)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
|
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
|
||||||
|
@ -78,6 +41,26 @@ http://localhost:6060/pkg/github.com/btcsuite/btcd/btcjson
|
||||||
$ go get github.com/btcsuite/btcd/btcjson
|
$ go get github.com/btcsuite/btcd/btcjson
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
* [Marshal Command]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/btcjson#example-MarshalCmd)
|
||||||
|
Demonstrates how to create and marshal a command into a JSON-RPC request.
|
||||||
|
|
||||||
|
* [Unmarshal Command]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/btcjson#example-UnmarshalCmd)
|
||||||
|
Demonstrates how to unmarshal a JSON-RPC request and then unmarshal the
|
||||||
|
concrete request into a concrete command.
|
||||||
|
|
||||||
|
* [Marshal Response]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/btcjson#example-MarshalResponse)
|
||||||
|
Demonstrates how to marshal a JSON-RPC response.
|
||||||
|
|
||||||
|
* [Unmarshal Response]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/btcjson#example-package--UnmarshalResponse)
|
||||||
|
Demonstrates how to unmarshal a JSON-RPC response and then unmarshal the
|
||||||
|
result field in the response to a concrete type.
|
||||||
|
|
||||||
## GPG Verification Key
|
## GPG Verification Key
|
||||||
|
|
||||||
All official release tags are signed by Conformal so users can ensure the code
|
All official release tags are signed by Conformal so users can ensure the code
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestBtcdExtCmds tests all of the btcd extended commands marshal and unmarshal
|
// TestBtcdExtCmds tests all of the btcd extended commands marshal and unmarshal
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestBtcWalletExtCmds tests all of the btcwallet extended commands marshal and
|
// TestBtcWalletExtCmds tests all of the btcwallet extended commands marshal and
|
|
@ -1,88 +0,0 @@
|
||||||
btcws
|
|
||||||
=====
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)]
|
|
||||||
(https://travis-ci.org/btcsuite/btcws)
|
|
||||||
|
|
||||||
Package btcws implements extensions to the standard bitcoind JSON-RPC
|
|
||||||
API for the btcd suite of programs (btcd, btcwallet, and btcgui).
|
|
||||||
Importing this package registers all implemented custom requests with
|
|
||||||
btcjson (using btcjson.RegisterCustomCmd).
|
|
||||||
|
|
||||||
## Sample Use
|
|
||||||
```Go
|
|
||||||
// Client Side
|
|
||||||
import (
|
|
||||||
"golang.org/x/net/websocket"
|
|
||||||
"github.com/btcsuite/btcd/btcjson/btcws"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create rescan command.
|
|
||||||
id := 0
|
|
||||||
addrs := map[string]struct{}{
|
|
||||||
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH": struct{},
|
|
||||||
}
|
|
||||||
cmd, err := btcws.NewRescanCmd(id, 270000, addrs)
|
|
||||||
|
|
||||||
// Set up a handler for a reply with id 0.
|
|
||||||
AddReplyHandler(id, func(reply map[string]interface{}) {
|
|
||||||
// Deal with reply.
|
|
||||||
})
|
|
||||||
|
|
||||||
// JSON marshal and send rescan request to websocket connection.
|
|
||||||
websocket.JSON.Send(btcdWSConn, cmd)
|
|
||||||
|
|
||||||
|
|
||||||
// Server Side
|
|
||||||
import (
|
|
||||||
"golang.org/x/net/websocket"
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
"github.com/btcsuite/btcd/btcjson/btcws"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get marshaled request.
|
|
||||||
var b []byte
|
|
||||||
err := websocket.Message.Receive(clientWSConn, &b)
|
|
||||||
|
|
||||||
// Parse marshaled command.
|
|
||||||
cmd, err := btcjson.ParseMarshaledCmd(b)
|
|
||||||
|
|
||||||
// If this is a rescan command, handle and reply.
|
|
||||||
rcmd, ok := cmd.(*btcws.RescanCmd)
|
|
||||||
if ok {
|
|
||||||
// Do stuff
|
|
||||||
var reply []byte
|
|
||||||
err := websocket.Message.Send(clientWSConn, reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go get github.com/btcsuite/btcd/btcjson/btcws
|
|
||||||
```
|
|
||||||
|
|
||||||
## GPG Verification Key
|
|
||||||
|
|
||||||
All official release tags are signed by Conformal so users can ensure the code
|
|
||||||
has not been tampered with and is coming from Conformal. To verify the
|
|
||||||
signature perform the following:
|
|
||||||
|
|
||||||
- Download the public key from the Conformal website at
|
|
||||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
|
||||||
|
|
||||||
- Import the public key into your GPG keyring:
|
|
||||||
```bash
|
|
||||||
gpg --import GIT-GPG-KEY-conformal.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
|
||||||
placeholder for the specific tag:
|
|
||||||
```bash
|
|
||||||
git tag -v TAG_NAME
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Package btcws is licensed under the liberal ISC License.
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,364 +0,0 @@
|
||||||
// Copyright (c) 2013 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// this has to be in the real package so we can mock up structs
|
|
||||||
package btcws
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
|
||||||
|
|
||||||
var testAccount = "account"
|
|
||||||
|
|
||||||
var cmdtests = []struct {
|
|
||||||
name string
|
|
||||||
f func() (btcjson.Cmd, error)
|
|
||||||
result btcjson.Cmd // after marshal and unmarshal
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "createencryptedwallet",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewCreateEncryptedWalletCmd(
|
|
||||||
float64(1),
|
|
||||||
"banana"), nil
|
|
||||||
},
|
|
||||||
result: &CreateEncryptedWalletCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Passphrase: "banana",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "createnewaccount",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewCreateNewAccountCmd(
|
|
||||||
float64(1),
|
|
||||||
"account"), nil
|
|
||||||
},
|
|
||||||
result: &CreateNewAccountCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: "account",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getbestblock",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewGetBestBlockCmd(float64(1)), nil
|
|
||||||
},
|
|
||||||
result: &GetBestBlockCmd{
|
|
||||||
id: float64(1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getcurrentnet",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewGetCurrentNetCmd(float64(1)), nil
|
|
||||||
},
|
|
||||||
result: &GetCurrentNetCmd{
|
|
||||||
id: float64(1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getunconfirmedbalance no optargs",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewGetUnconfirmedBalanceCmd(float64(1))
|
|
||||||
},
|
|
||||||
result: &GetUnconfirmedBalanceCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getunconfirmedbalance one optarg",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewGetUnconfirmedBalanceCmd(float64(1),
|
|
||||||
testAccount)
|
|
||||||
},
|
|
||||||
result: &GetUnconfirmedBalanceCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: testAccount,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "listaddresstransactions no optargs",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
addrs := []string{
|
|
||||||
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH",
|
|
||||||
}
|
|
||||||
return NewListAddressTransactionsCmd(
|
|
||||||
float64(1),
|
|
||||||
addrs)
|
|
||||||
},
|
|
||||||
result: &ListAddressTransactionsCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: "",
|
|
||||||
Addresses: []string{
|
|
||||||
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "listaddresstransactions one optarg",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
addrs := []string{
|
|
||||||
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH",
|
|
||||||
}
|
|
||||||
return NewListAddressTransactionsCmd(
|
|
||||||
float64(1),
|
|
||||||
addrs,
|
|
||||||
testAccount)
|
|
||||||
},
|
|
||||||
result: &ListAddressTransactionsCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: testAccount,
|
|
||||||
Addresses: []string{
|
|
||||||
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "listalltransactions no optargs",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewListAllTransactionsCmd(float64(1))
|
|
||||||
},
|
|
||||||
result: &ListAllTransactionsCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "listalltransactions one optarg",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewListAllTransactionsCmd(
|
|
||||||
float64(1),
|
|
||||||
testAccount)
|
|
||||||
},
|
|
||||||
result: &ListAllTransactionsCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: &testAccount,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "notifyreceived",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
addrs := []string{
|
|
||||||
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH",
|
|
||||||
}
|
|
||||||
return NewNotifyReceivedCmd(
|
|
||||||
float64(1),
|
|
||||||
addrs), nil
|
|
||||||
},
|
|
||||||
result: &NotifyReceivedCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Addresses: []string{
|
|
||||||
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "notifynewtransactions",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewNotifyNewTransactionsCmd(
|
|
||||||
float64(1),
|
|
||||||
true)
|
|
||||||
},
|
|
||||||
result: &NotifyNewTransactionsCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Verbose: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "notifyspent",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
ops := []OutPoint{
|
|
||||||
{
|
|
||||||
Hash: "000102030405060708091011121314" +
|
|
||||||
"1516171819202122232425262728" +
|
|
||||||
"293031",
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return NewNotifySpentCmd(float64(1), ops), nil
|
|
||||||
},
|
|
||||||
result: &NotifySpentCmd{
|
|
||||||
id: float64(1),
|
|
||||||
OutPoints: []OutPoint{
|
|
||||||
{
|
|
||||||
Hash: "000102030405060708091011121314" +
|
|
||||||
"1516171819202122232425262728" +
|
|
||||||
"293031",
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "renameaccount",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewRenameAccountCmd(
|
|
||||||
float64(1),
|
|
||||||
"old",
|
|
||||||
"new"), nil
|
|
||||||
},
|
|
||||||
result: &RenameAccountCmd{
|
|
||||||
id: float64(1),
|
|
||||||
OldAccount: "old",
|
|
||||||
NewAccount: "new",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "rescan no optargs",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
addrs := []string{"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH"}
|
|
||||||
ops := []OutPoint{
|
|
||||||
{
|
|
||||||
Hash: "000102030405060708091011121314" +
|
|
||||||
"1516171819202122232425262728" +
|
|
||||||
"293031",
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return NewRescanCmd(
|
|
||||||
float64(1),
|
|
||||||
"0000000000000002a775aec59dc6a9e4bb1c025cf1b8c2195dd9dc3998c827c5",
|
|
||||||
addrs,
|
|
||||||
ops)
|
|
||||||
},
|
|
||||||
result: &RescanCmd{
|
|
||||||
id: float64(1),
|
|
||||||
BeginBlock: "0000000000000002a775aec59dc6a9e4bb1c025cf1b8c2195dd9dc3998c827c5",
|
|
||||||
Addresses: []string{"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH"},
|
|
||||||
OutPoints: []OutPoint{
|
|
||||||
{
|
|
||||||
Hash: "000102030405060708091011121314" +
|
|
||||||
"1516171819202122232425262728" +
|
|
||||||
"293031",
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
EndBlock: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "rescan one optarg",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
addrs := []string{"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH"}
|
|
||||||
ops := []OutPoint{
|
|
||||||
{
|
|
||||||
Hash: "000102030405060708091011121314" +
|
|
||||||
"1516171819202122232425262728" +
|
|
||||||
"293031",
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return NewRescanCmd(
|
|
||||||
float64(1),
|
|
||||||
"0000000000000002a775aec59dc6a9e4bb1c025cf1b8c2195dd9dc3998c827c5",
|
|
||||||
addrs,
|
|
||||||
ops,
|
|
||||||
"0000000000000001c091ada69f444dc0282ecaabe4808ddbb2532e5555db0c03")
|
|
||||||
},
|
|
||||||
result: &RescanCmd{
|
|
||||||
id: float64(1),
|
|
||||||
BeginBlock: "0000000000000002a775aec59dc6a9e4bb1c025cf1b8c2195dd9dc3998c827c5",
|
|
||||||
Addresses: []string{"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH"},
|
|
||||||
OutPoints: []OutPoint{
|
|
||||||
{
|
|
||||||
Hash: "000102030405060708091011121314" +
|
|
||||||
"1516171819202122232425262728" +
|
|
||||||
"293031",
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
EndBlock: "0000000000000001c091ada69f444dc0282ecaabe4808ddbb2532e5555db0c03",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "walletislocked no optargs",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewWalletIsLockedCmd(float64(1))
|
|
||||||
},
|
|
||||||
result: &WalletIsLockedCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "walletislocked one optarg",
|
|
||||||
f: func() (btcjson.Cmd, error) {
|
|
||||||
return NewWalletIsLockedCmd(
|
|
||||||
float64(1),
|
|
||||||
testAccount)
|
|
||||||
},
|
|
||||||
result: &WalletIsLockedCmd{
|
|
||||||
id: float64(1),
|
|
||||||
Account: testAccount,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCmds(t *testing.T) {
|
|
||||||
for _, test := range cmdtests {
|
|
||||||
c, err := test.f()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: failed to run func: %v",
|
|
||||||
test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
mc, err := c.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: failed to marshal cmd: %v",
|
|
||||||
test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
c2, err := btcjson.ParseMarshaledCmd(mc)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: failed to ummarshal cmd: %v",
|
|
||||||
test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(test.result, c2) {
|
|
||||||
t.Errorf("%s: unmarshal not as expected. "+
|
|
||||||
"got %v wanted %v", test.name, spew.Sdump(c2),
|
|
||||||
spew.Sdump(test.result))
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(c, c2) {
|
|
||||||
t.Errorf("%s: unmarshal not as we started with. "+
|
|
||||||
"got %v wanted %v", test.name, spew.Sdump(c2),
|
|
||||||
spew.Sdump(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
// id from Id func must match result.
|
|
||||||
if c.Id() != test.result.Id() {
|
|
||||||
t.Errorf("%s: Id returned incorrect id. "+
|
|
||||||
"got %v wanted %v", test.name, c.Id(),
|
|
||||||
test.result.Id())
|
|
||||||
}
|
|
||||||
|
|
||||||
// method from Method func must match result.
|
|
||||||
if c.Method() != test.result.Method() {
|
|
||||||
t.Errorf("%s: Method returned incorrect method. "+
|
|
||||||
"got %v wanted %v", test.name, c.Method(),
|
|
||||||
test.result.Method())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read marshaled command back into c. Should still
|
|
||||||
// match result.
|
|
||||||
if err := c.UnmarshalJSON(mc); err != nil {
|
|
||||||
t.Errorf("%s: error while unmarshalling: %v", test.name,
|
|
||||||
err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(test.result, c) {
|
|
||||||
t.Errorf("%s: unmarshal not as expected. "+
|
|
||||||
"got %v wanted %v", test.name, spew.Sdump(c),
|
|
||||||
spew.Sdump(test.result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,290 +0,0 @@
|
||||||
// Copyright (c) 2013 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcws_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
"github.com/btcsuite/btcd/btcjson/btcws"
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ntfntests = []struct {
|
|
||||||
name string
|
|
||||||
f func() btcjson.Cmd
|
|
||||||
result btcjson.Cmd // after marshal and unmarshal
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "accountbalance",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewAccountBalanceNtfn("abcde", 1.2345, true)
|
|
||||||
},
|
|
||||||
result: &btcws.AccountBalanceNtfn{
|
|
||||||
Account: "abcde",
|
|
||||||
Balance: 1.2345,
|
|
||||||
Confirmed: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "blockconnected",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewBlockConnectedNtfn(
|
|
||||||
"000000004811dda1c320ad5d0ea184a20a53acd92292c5f1cb926c3ee82abf70",
|
|
||||||
153469)
|
|
||||||
},
|
|
||||||
result: &btcws.BlockConnectedNtfn{
|
|
||||||
Hash: "000000004811dda1c320ad5d0ea184a20a53acd92292c5f1cb926c3ee82abf70",
|
|
||||||
Height: 153469,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "blockdisconnected",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewBlockDisconnectedNtfn(
|
|
||||||
"000000004811dda1c320ad5d0ea184a20a53acd92292c5f1cb926c3ee82abf70",
|
|
||||||
153469)
|
|
||||||
},
|
|
||||||
result: &btcws.BlockDisconnectedNtfn{
|
|
||||||
Hash: "000000004811dda1c320ad5d0ea184a20a53acd92292c5f1cb926c3ee82abf70",
|
|
||||||
Height: 153469,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "btcdconnected",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewBtcdConnectedNtfn(true)
|
|
||||||
},
|
|
||||||
result: &btcws.BtcdConnectedNtfn{
|
|
||||||
Connected: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "recvtx no block",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewRecvTxNtfn("lalala the hex tx", nil)
|
|
||||||
},
|
|
||||||
result: &btcws.RecvTxNtfn{
|
|
||||||
HexTx: "lalala the hex tx",
|
|
||||||
Block: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "recvtx with block",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
block := &btcws.BlockDetails{
|
|
||||||
Height: 153469,
|
|
||||||
Hash: "000000004811dda1c320ad5d0ea184a20a53acd92292c5f1cb926c3ee82abf70",
|
|
||||||
Index: 1,
|
|
||||||
Time: 1386944019,
|
|
||||||
}
|
|
||||||
return btcws.NewRecvTxNtfn("lalala the hex tx", block)
|
|
||||||
},
|
|
||||||
result: &btcws.RecvTxNtfn{
|
|
||||||
HexTx: "lalala the hex tx",
|
|
||||||
Block: &btcws.BlockDetails{
|
|
||||||
Height: 153469,
|
|
||||||
Hash: "000000004811dda1c320ad5d0ea184a20a53acd92292c5f1cb926c3ee82abf70",
|
|
||||||
Index: 1,
|
|
||||||
Time: 1386944019,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "redeemingtx",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewRedeemingTxNtfn("lalala the hex tx", nil)
|
|
||||||
},
|
|
||||||
result: &btcws.RedeemingTxNtfn{
|
|
||||||
HexTx: "lalala the hex tx",
|
|
||||||
Block: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "redeemingtx with block",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
block := &btcws.BlockDetails{
|
|
||||||
Height: 153469,
|
|
||||||
Hash: "000000004811dda1c320ad5d0ea184a20a53acd92292c5f1cb926c3ee82abf70",
|
|
||||||
Index: 1,
|
|
||||||
Time: 1386944019,
|
|
||||||
}
|
|
||||||
return btcws.NewRedeemingTxNtfn("lalala the hex tx", block)
|
|
||||||
},
|
|
||||||
result: &btcws.RedeemingTxNtfn{
|
|
||||||
HexTx: "lalala the hex tx",
|
|
||||||
Block: &btcws.BlockDetails{
|
|
||||||
Height: 153469,
|
|
||||||
Hash: "000000004811dda1c320ad5d0ea184a20a53acd92292c5f1cb926c3ee82abf70",
|
|
||||||
Index: 1,
|
|
||||||
Time: 1386944019,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "rescanfinished",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewRescanFinishedNtfn(
|
|
||||||
"00000000b8980ec1fe96bc1b4425788ddc88dd36699521a448ebca2020b38699",
|
|
||||||
12345, 1240784732)
|
|
||||||
},
|
|
||||||
result: &btcws.RescanFinishedNtfn{
|
|
||||||
Hash: "00000000b8980ec1fe96bc1b4425788ddc88dd36699521a448ebca2020b38699",
|
|
||||||
Height: 12345,
|
|
||||||
Time: 1240784732,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "rescanprogress",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewRescanProgressNtfn(
|
|
||||||
"00000000b8980ec1fe96bc1b4425788ddc88dd36699521a448ebca2020b38699",
|
|
||||||
12345, 1240784732)
|
|
||||||
},
|
|
||||||
result: &btcws.RescanProgressNtfn{
|
|
||||||
Hash: "00000000b8980ec1fe96bc1b4425788ddc88dd36699521a448ebca2020b38699",
|
|
||||||
Height: 12345,
|
|
||||||
Time: 1240784732,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "newtx",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
details := &btcjson.ListTransactionsResult{
|
|
||||||
Account: "original",
|
|
||||||
Address: "mnSsMBY8j4AhQzbR6XqawND7NPTECVdtLd",
|
|
||||||
Category: "receive",
|
|
||||||
Amount: 100,
|
|
||||||
Fee: 0.0,
|
|
||||||
Confirmations: 6707,
|
|
||||||
Generated: false,
|
|
||||||
BlockHash: "000000000b20bf5fe8e25b19f9ec340744cda321a17ade12af9838530a75098b",
|
|
||||||
BlockIndex: 2,
|
|
||||||
BlockTime: 1397079345,
|
|
||||||
TxID: "cb082a63b29f446551829d03fa8bac02d3825a18994d5feec564f14101fc5fad",
|
|
||||||
WalletConflicts: []string{},
|
|
||||||
Time: 123123123,
|
|
||||||
TimeReceived: 1397079169,
|
|
||||||
Comment: "comment",
|
|
||||||
OtherAccount: "",
|
|
||||||
}
|
|
||||||
return btcws.NewTxNtfn("abcde", details)
|
|
||||||
},
|
|
||||||
result: &btcws.TxNtfn{
|
|
||||||
Account: "abcde",
|
|
||||||
Details: &btcjson.ListTransactionsResult{
|
|
||||||
Account: "original",
|
|
||||||
Address: "mnSsMBY8j4AhQzbR6XqawND7NPTECVdtLd",
|
|
||||||
Category: "receive",
|
|
||||||
Amount: 100,
|
|
||||||
Fee: 0.0,
|
|
||||||
Confirmations: 6707,
|
|
||||||
Generated: false,
|
|
||||||
BlockHash: "000000000b20bf5fe8e25b19f9ec340744cda321a17ade12af9838530a75098b",
|
|
||||||
BlockIndex: 2,
|
|
||||||
BlockTime: 1397079345,
|
|
||||||
TxID: "cb082a63b29f446551829d03fa8bac02d3825a18994d5feec564f14101fc5fad",
|
|
||||||
WalletConflicts: []string{},
|
|
||||||
Time: 123123123,
|
|
||||||
TimeReceived: 1397079169,
|
|
||||||
Comment: "comment",
|
|
||||||
OtherAccount: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "walletlockstate",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewWalletLockStateNtfn("abcde", true)
|
|
||||||
},
|
|
||||||
result: &btcws.WalletLockStateNtfn{
|
|
||||||
Account: "abcde",
|
|
||||||
Locked: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "txaccepted",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewTxAcceptedNtfn(
|
|
||||||
"062f2b5f7d28c787e0f3aee382132241cd590efb7b83bd2c7f506de5aa4ef275",
|
|
||||||
34567765)
|
|
||||||
},
|
|
||||||
result: &btcws.TxAcceptedNtfn{
|
|
||||||
TxID: "062f2b5f7d28c787e0f3aee382132241cd590efb7b83bd2c7f506de5aa4ef275",
|
|
||||||
Amount: 34567765,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "txacceptedverbose",
|
|
||||||
f: func() btcjson.Cmd {
|
|
||||||
return btcws.NewTxAcceptedVerboseNtfn(&btcjson.TxRawResult{
|
|
||||||
Hex: "01000000010cdf900074a3622499a2f28f44a94476f27a8900a2bdd60e042754b6cab09741000000008a473044022012e11012fad1eb21ba1c82deb8da98778b08e714b72f281293064528343fae0502204294d7520f469f9673087a55395de0ce0e9074dce236db9fe7f30013b5fd00b90141047b6ff7832b4a763666e5481a0bd9eedb656d9f882d215c16fe9563d7b191cd67b2a41601a853a9f9d92773ae6f912ef451a089148e510623759cf55c408efdefffffffff02f4063f00000000001976a914b269e0ceec5d5b5e192cf580ae42341e0f79b0b588aca8c84b02000000001976a91439233c0d43a1411e547c60bad8985bae3530b6af88ac00000000",
|
|
||||||
Txid: "0cfeb968fb5d0f6b9a2a1de37c0607a1964dd3e335f203377cec90e03b20869e",
|
|
||||||
Version: 0x1,
|
|
||||||
LockTime: 0x0,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
result: &btcws.TxAcceptedVerboseNtfn{
|
|
||||||
RawTx: &btcjson.TxRawResult{
|
|
||||||
Hex: "01000000010cdf900074a3622499a2f28f44a94476f27a8900a2bdd60e042754b6cab09741000000008a473044022012e11012fad1eb21ba1c82deb8da98778b08e714b72f281293064528343fae0502204294d7520f469f9673087a55395de0ce0e9074dce236db9fe7f30013b5fd00b90141047b6ff7832b4a763666e5481a0bd9eedb656d9f882d215c16fe9563d7b191cd67b2a41601a853a9f9d92773ae6f912ef451a089148e510623759cf55c408efdefffffffff02f4063f00000000001976a914b269e0ceec5d5b5e192cf580ae42341e0f79b0b588aca8c84b02000000001976a91439233c0d43a1411e547c60bad8985bae3530b6af88ac00000000",
|
|
||||||
Txid: "0cfeb968fb5d0f6b9a2a1de37c0607a1964dd3e335f203377cec90e03b20869e",
|
|
||||||
Version: 0x1,
|
|
||||||
LockTime: 0x0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNtfns(t *testing.T) {
|
|
||||||
for _, test := range ntfntests {
|
|
||||||
// create notification.
|
|
||||||
n := test.f()
|
|
||||||
|
|
||||||
// verify that id is nil.
|
|
||||||
if n.Id() != nil {
|
|
||||||
t.Errorf("%s: notification should not have non-nil id %v",
|
|
||||||
test.name, n.Id())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
mn, err := n.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: failed to marshal notification: %v",
|
|
||||||
test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
n2, err := btcjson.ParseMarshaledCmd(mn)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: failed to ummarshal cmd: %v",
|
|
||||||
test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(test.result, n2) {
|
|
||||||
t.Errorf("%s: unmarshal not as expected. "+
|
|
||||||
"got %v wanted %v", test.name, spew.Sdump(n2),
|
|
||||||
spew.Sdump(test.result))
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(n, n2) {
|
|
||||||
t.Errorf("%s: unmarshal not as we started with. "+
|
|
||||||
"got %v wanted %v", test.name, spew.Sdump(n2),
|
|
||||||
spew.Sdump(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read marshaled notification back into n. Should still
|
|
||||||
// match result.
|
|
||||||
if err := n.UnmarshalJSON(mn); err != nil {
|
|
||||||
t.Errorf("%s: unmarshal failed: %v", test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(test.result, n) {
|
|
||||||
t.Errorf("%s: unmarshal not as expected. "+
|
|
||||||
"got %v wanted %v", test.name, spew.Sdump(n),
|
|
||||||
spew.Sdump(test.result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrCmds tests all of the chain server commands marshal and unmarshal
|
// TestChainSvrCmds tests all of the chain server commands marshal and unmarshal
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrCustomResults ensures any results that have custom marshalling
|
// TestChainSvrCustomResults ensures any results that have custom marshalling
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrWsCmds tests all of the chain server websocket-specific commands
|
// TestChainSvrWsCmds tests all of the chain server websocket-specific commands
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrWsNtfns tests all of the chain server websocket-specific
|
// TestChainSvrWsNtfns tests all of the chain server websocket-specific
|
|
@ -1,828 +0,0 @@
|
||||||
// Copyright (c) 2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaultHelpStrings contains the help text for all commands that are supported
|
|
||||||
// by btcjson by default.
|
|
||||||
var defaultHelpStrings = map[string]string{
|
|
||||||
"addmultisigaddress": `addmultisigaddress nrequired ["key",...] ("account" )
|
|
||||||
Add a multisignature address to the wallet where 'nrequired' signatures are
|
|
||||||
required for spending. Each key is an address of publickey. Optionally, account
|
|
||||||
may be provided to assign the address to that account.`,
|
|
||||||
|
|
||||||
"addnode": `addnode "node" "{add|remove|onetry}"
|
|
||||||
Add or remove a node from the list of hosts we connect to. If mode is "onetry"
|
|
||||||
then the host will be connected to once only, otherwise the node will be retried
|
|
||||||
upon disconnection.`,
|
|
||||||
|
|
||||||
"backupwallet": `backupwallet "destination"
|
|
||||||
Safely copies the wallet file to the destination provided, either a directory or
|
|
||||||
a filename.`,
|
|
||||||
|
|
||||||
"createmultisig": `createmultisig nrequired ["key", ...]
|
|
||||||
Creates a multi-signature address with m keys where "nrequired" signatures are
|
|
||||||
required from those m. A JSON object is returned containing the address and
|
|
||||||
redemption script:
|
|
||||||
{
|
|
||||||
"address":"address", # the value of the new address.
|
|
||||||
redeemScript":"script" # The stringified hex-encoded redemption script.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"createrawtransaction": `createrawtransaction [{"txid":"id", "vout":n},...] {"address":amount,...}
|
|
||||||
Creates a transaction spending the given inputs and outputting to the
|
|
||||||
given addresses. The return is a hex encoded string of the raw
|
|
||||||
transaction with *unsigned* inputs. The transaction is not stored in any
|
|
||||||
wallet.`,
|
|
||||||
|
|
||||||
// TODO(oga) this should be external since it is nonstandard.
|
|
||||||
"debuglevel": `debuglevel "levelspec"
|
|
||||||
Dynamically changes the debug logging level. Levelspec must be either a debug
|
|
||||||
level, one of the following:
|
|
||||||
trace,
|
|
||||||
debug,
|
|
||||||
info,
|
|
||||||
warn,
|
|
||||||
error,
|
|
||||||
critical.
|
|
||||||
Alternatively levelspec may be a specification of the form:
|
|
||||||
<subsystem>=<level>,<subsystem2>=<level2>
|
|
||||||
Where the valid subsystem names are:
|
|
||||||
AMGR,
|
|
||||||
ADXR,
|
|
||||||
BCDB,
|
|
||||||
BMGR,
|
|
||||||
BTCD,
|
|
||||||
CHAN,
|
|
||||||
DISC,
|
|
||||||
PEER,
|
|
||||||
RPCS,
|
|
||||||
SCRP,
|
|
||||||
SRVR,
|
|
||||||
TXMP.
|
|
||||||
Finally the keyword "show" will return a list of the available subsystems.
|
|
||||||
The command returns a string which will be "Done." if the command was sucessful,
|
|
||||||
or the list of subsystems if "show" was specified.`,
|
|
||||||
|
|
||||||
"decoderawtransaction": `decoderawtransaction "hexstring"
|
|
||||||
Decodes the seralized, hex-encoded transaction in hexstring and returns a JSON
|
|
||||||
object representing it:
|
|
||||||
{
|
|
||||||
"hex":"hex", # String of the hex encoded transaction provided.
|
|
||||||
"txid":"id", # The sha id of the transaction as a hex string
|
|
||||||
"version":n, # The version of the transaction as a number.
|
|
||||||
"locktime":t, # Locktime of the tx (number).
|
|
||||||
"vin": [ # Array of objects for inputs.
|
|
||||||
{
|
|
||||||
"txid":"id", # Txid that is spent.
|
|
||||||
"vout":n, # Output number.
|
|
||||||
"scriptSig": {
|
|
||||||
"asm":"asm", # Disasembled script as a string.
|
|
||||||
"hex":"hex", # Hex string of script.
|
|
||||||
},
|
|
||||||
"sequence":n, # Sequence number of script.
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vout": [ # Array of objects for outputs.
|
|
||||||
{
|
|
||||||
"value":x.xxx, # Value in BTC.
|
|
||||||
"n":n, # Numeric index.
|
|
||||||
"scriptPubkey": { # Object representing script.
|
|
||||||
"asm":"asm", # Disassembled script as string.
|
|
||||||
"hex":"hex", # Hex string of script.
|
|
||||||
"reqSigs":n, # Number of required signatures.
|
|
||||||
"type":"type" # Type as string, e.g. pubkeyhash.
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"blockhash":"hash", # The block hash. as a string.
|
|
||||||
"confirmations":n # Number of confirmations in blockchain.
|
|
||||||
"time":t, # Transaction time in seconds since the epoch.
|
|
||||||
"blocktime":t, # Block time in seconds since the epoch.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"decodescript": `decodescript "hex"
|
|
||||||
Decodes the hex encoded script passed as a string and returns a JSON object:
|
|
||||||
{
|
|
||||||
"asm":"asm", # disassembled string of the script.
|
|
||||||
"hex":"hex", # hex string of the script.
|
|
||||||
"type":"type", # type of script as a string.
|
|
||||||
"reqSigs":n, # number of required signatures.
|
|
||||||
"addresses": [ # JSON array of address strings.
|
|
||||||
"address", # bitcoin address as a string.
|
|
||||||
],
|
|
||||||
"p2sh","address" # script address as a string.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"dumpprivkey": `dumpprivkey "bitcoinaddress"
|
|
||||||
Returns the private key corresponding to the provided address in a format
|
|
||||||
compatible with importprivkey.`,
|
|
||||||
|
|
||||||
"dumpwallet": `dumpwallet "filename"
|
|
||||||
Dumps all wallet keys into "filename" in a human readable format.`,
|
|
||||||
|
|
||||||
"encryptwallet": `encryptwallet "passphrase"
|
|
||||||
Encrypts the wallet with "passphrase". This command is for the initial
|
|
||||||
encryption of an otherwise unencrypted wallet, changing a passphrase
|
|
||||||
should use walletpassphrasechange.`,
|
|
||||||
|
|
||||||
"estimatefee": `estimatefee "numblocks"
|
|
||||||
Estimates the approximate fee per kilobyte needed for a transaction to
|
|
||||||
get confirmed within 'numblocks' blocks.`,
|
|
||||||
|
|
||||||
"estimatepriority": `estimatepriority "numblocks"
|
|
||||||
Estimates the approximate priority a zero-fee transaction needs to get
|
|
||||||
confirmed within 'numblocks' blocks.`,
|
|
||||||
|
|
||||||
"getaccount": `getaccount "address"
|
|
||||||
Returns the account associated with the given "address" as a string.`,
|
|
||||||
|
|
||||||
"getaccountaddress": `getaccountaddress "account"
|
|
||||||
Returns the current address used for receiving payments in this given account.`,
|
|
||||||
|
|
||||||
"getaddednodeinfo": `getaddednodeinfo dns ( "node" )
|
|
||||||
Returns a list of JSON objects with information about the local list of
|
|
||||||
permanent nodes. If dns is false, only a list of permanent nodes will
|
|
||||||
be provided, otherwise connected information will also be provided. If
|
|
||||||
node is not provided then all nodes will be detailed. The JSON return
|
|
||||||
format is as follows:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"addednode":"1.2.3.4", # node ip address as a string
|
|
||||||
"connected":true|false # boolean connectionstate.
|
|
||||||
"addresses": [
|
|
||||||
"address":"1.2.3.4:5678" # Bitcoin server host and port as a string.
|
|
||||||
"connected":"inbound", # The string "inbound" or "outbound".
|
|
||||||
],
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]`,
|
|
||||||
|
|
||||||
"getaddressesbyaccount": `getaddressesbyaccount "account"
|
|
||||||
Returns the list of addresses for the given account:
|
|
||||||
[
|
|
||||||
"address", # Bitcoin address associated with the given account.
|
|
||||||
...
|
|
||||||
]`,
|
|
||||||
|
|
||||||
"getbalance": `getbalance ("account" "minconf")
|
|
||||||
Returns the balance for an account. If "account" is not specified this is the
|
|
||||||
total balance for the server. if "minconf" is provided then only transactions
|
|
||||||
with at least "minconf" confirmations will be counted for the balance. The
|
|
||||||
result is a JSON numeric type.`,
|
|
||||||
|
|
||||||
"getbestblockhash": `getbestblockhash
|
|
||||||
Returns the hash of the last block in the longest blockchain known to
|
|
||||||
the server as a hex encoded string.`,
|
|
||||||
|
|
||||||
"getblock": `getblock "hash" ( verbose verbosetx=false)
|
|
||||||
Returns data about the block with hash "hash". If verbose is false a
|
|
||||||
string of hex-encoded data for the block is returned. If verbose is true
|
|
||||||
a JSON object is provided with the following:
|
|
||||||
{
|
|
||||||
"hash":"hash", # The block hash (same as argument).
|
|
||||||
"confirmations":n, # Number of confirmations as numeric.
|
|
||||||
"size":n, # Block size as numeric.
|
|
||||||
"height":n, # Block height as numeric.
|
|
||||||
"version":n, # Block version as numeric.
|
|
||||||
"merkelroot":"...", # The merkle root of the block.
|
|
||||||
"tx" : [ # the transactions in the block as an array of strings.
|
|
||||||
"transactionid",
|
|
||||||
...
|
|
||||||
],
|
|
||||||
"time":t, # The block time in seconds since the epoch.
|
|
||||||
"nonce":n, # The nonce of the block as a number.
|
|
||||||
"bits":"1d00ffff", # the compact representation of the difficulty as bits.
|
|
||||||
"difficulty":n, # the difficulty of the block as a number.
|
|
||||||
"previousblockhash":"hash", # hash of the previous block as a string.
|
|
||||||
"nextblockhash":""hash", # hash of the next block as a string.
|
|
||||||
}
|
|
||||||
If "verbosetx" is true, the returned object will contain a "rawtx" member
|
|
||||||
instead of the "tx" member; It will contain objects representing the
|
|
||||||
transactions in the block in format used by getrawtransaction.
|
|
||||||
Please note that verbosetx is a btcd/btcjson extension.`,
|
|
||||||
|
|
||||||
"getblockcount": `getblockcount
|
|
||||||
Returns a numeric for the number of blocks in the longest block chain.`,
|
|
||||||
|
|
||||||
"getblockhash": `getblockhash index
|
|
||||||
Returns the hash of the block (as a string) at the given index in the
|
|
||||||
current blockchain.`,
|
|
||||||
|
|
||||||
"getblocktemplate": `getblocktemplate ( jsonrequestobject )
|
|
||||||
Returns a block template for external mining purposes. The optional
|
|
||||||
request object follows the following format:
|
|
||||||
{
|
|
||||||
"mode":"template" # Optional, "template" or omitted.
|
|
||||||
"capabilities": [ # List of strings, optional.
|
|
||||||
"support", # Client side features supported. one of:
|
|
||||||
... # "longpoll", "coinbasetxn", "coinbasevalue",
|
|
||||||
... # "proposal", "serverlist", "workid".
|
|
||||||
]
|
|
||||||
}
|
|
||||||
The result object is of the following format:
|
|
||||||
{
|
|
||||||
"version":n, # Numeric block version.
|
|
||||||
"previousblockhash":"string" # Hash of the current tip of the blocktrain.
|
|
||||||
"transactions":[
|
|
||||||
"data" # String of hex-encoded serialized transaction.
|
|
||||||
"hash" # Hex encoded hash of the tx.
|
|
||||||
"depends": [ # Array of numbers representing the transactions
|
|
||||||
n, # that must be included in the final block if
|
|
||||||
..., # this one is. A 1 based index into the
|
|
||||||
..., # transactions list.
|
|
||||||
]
|
|
||||||
"fee":n # Numeric transaction fee in satoshi. This is calculated by the diffrence between the sum of inputs and outputs. For coinbase transaction this is a negative number of total collected block fees. If not present fee is unknown; clients must not assume that there is no fee in this case.
|
|
||||||
"sigops":n # Number of total signature operations calculated for purposes of block limits. If not present the count is unknown but clients must not assume it is zero.
|
|
||||||
"required":true|false # If provided and true this transaction *must* be in the final block.
|
|
||||||
],
|
|
||||||
"coinbaseaux": { # Object of data to be included in coinbase's scriptSig.
|
|
||||||
"flags":"flags" # String of flags.
|
|
||||||
}
|
|
||||||
"coinbasevalue" # Numeric value in satoshi for maximum allowable input to coinbase transaction, including transaction fees and mining aware.
|
|
||||||
"coinbasetxn":{} # Object contining information for coinbase transaction.
|
|
||||||
"target":"target", # The hash target as a string.
|
|
||||||
"mintime":t, # Minimum timestamp appropriate for next block in seconds since the epoch.
|
|
||||||
"mutable":[ # Array of ways the template may be modified.
|
|
||||||
"value" # e.g. "time", "transactions", "prevblock", etc
|
|
||||||
]
|
|
||||||
"noncerange":"00000000ffffffff" # Wtring representing the range of valid nonces.
|
|
||||||
"sigopliit" # Numeric limit for max sigops in block.
|
|
||||||
"sizelimit" # Numeric limit of block size.
|
|
||||||
"curtime":t # Current timestamp in seconds since the epoch.
|
|
||||||
"bits":"xxx", # Compressed target for next block as string.
|
|
||||||
"height":n, # Numeric height of the next block.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"getconnectioncount": `getconnectioncount
|
|
||||||
Returns the number of connections to other nodes currently active as a JSON
|
|
||||||
number.`,
|
|
||||||
|
|
||||||
"getdifficulty": `getdifficulty
|
|
||||||
Returns the proof-of-work difficulty as a JSON number. The result is a
|
|
||||||
multiple of the minimum difficulty.`,
|
|
||||||
|
|
||||||
"getgenerate": `getgenerate
|
|
||||||
Returns JSON boolean whether or not the server is set to "mine" coins or not.`,
|
|
||||||
|
|
||||||
"gethashespersec": `gethashespersec
|
|
||||||
Returns a JSON number representing a recent measurement of hashes-per-second
|
|
||||||
during mining.`,
|
|
||||||
|
|
||||||
"getinfo": `getinfo
|
|
||||||
Returns a JSON object containing state information about the service:
|
|
||||||
{
|
|
||||||
"version":n, # Numeric server version.
|
|
||||||
"protocolversion":n, # Numeric protocol version.
|
|
||||||
"walletversion":n, # Numeric wallet version.
|
|
||||||
"balance":n, # Total balance in the wallet as a number.
|
|
||||||
"blocks":n, # Numeric detailing current number of blocks.
|
|
||||||
"timeoffset":n, # Numeric server time offset.
|
|
||||||
"proxy":"host:port" # Optional string detailing the proxy in use.
|
|
||||||
"difficulty":n, # Current blockchain difficulty as a number.
|
|
||||||
"testnet":true|false # Boolean if the server is testnet.
|
|
||||||
"keypoololdest":t, # Oldest timstamp for pre generated keys. (in seconds since the epoch).
|
|
||||||
"keypoolsize":n, # Numeric size of the wallet keypool.
|
|
||||||
"paytxfee":n, # Numeric transaction fee that has been set.
|
|
||||||
"unlocked_until":t, # Numeric time the wallet is unlocked for in seconds since epoch.
|
|
||||||
"errors":"..." # Any error messages as a string.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"getmininginfo": `getmininginfo
|
|
||||||
Returns a JSON object containing information related to mining:
|
|
||||||
{
|
|
||||||
"blocks":n, # Numeric current block
|
|
||||||
"currentblocksize":n, # Numeric last block size.
|
|
||||||
currentblocktx":n, # Numeric last block transaction
|
|
||||||
"difficulty":n, # Numeric current difficulty.
|
|
||||||
"errors":"...", # Current error string.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"getnettotals": `getnettotals
|
|
||||||
Returns JSON object containing network traffic statistics:
|
|
||||||
{
|
|
||||||
"totalbytesrecv":n, # Numeric total bytes received.
|
|
||||||
"totalbytessent":n, # Numeric total bytes sent.
|
|
||||||
"timemilis",t, # Total numeric of milliseconds since epoch.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"getnetworkhashps": `getnetworkhashps ( blocks=120 height=-1 )
|
|
||||||
Returns the estimated network hash rate per second based on the last
|
|
||||||
"blocks" blocks. If "blocks" is -1 then the number of blocks since the
|
|
||||||
last difficulty change will be used. If "height" is set then the
|
|
||||||
calculation will be carried out for the given block height instead of
|
|
||||||
the block tip. A JSON number is returned with the hashes per second
|
|
||||||
estimate.`,
|
|
||||||
|
|
||||||
"getnewaddress": `getnewaddress ( "account" )
|
|
||||||
Returns a string for a new Bitcoin address for receiving payments. In the case
|
|
||||||
that "account" is specified then the address will be for "account", else the
|
|
||||||
default account will be used.`,
|
|
||||||
|
|
||||||
"getpeerinfo": `getpeerinfo
|
|
||||||
Returns a list of JSON objects containing information about each connected
|
|
||||||
network node. The objects have the following format:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id":n, # Unique node ID.
|
|
||||||
"addr":"host:port" # IP and port of the peer as a string.
|
|
||||||
"addrlocal":"ip:port" # Local address as a string.
|
|
||||||
"services":"00000001", # Services bitmask as a string.
|
|
||||||
"lastsend":t, # Time in seconds since epoch since last send.
|
|
||||||
"lastrecv":t, # Time in seconds since epoch since last received message.
|
|
||||||
"bytessent":n, # Total number of bytes sent.
|
|
||||||
"bytesrecv":n, # Total number of bytes received.
|
|
||||||
"conntime":t, # Connection time in seconds since epoch.
|
|
||||||
"timeoffset":n, # Peer time offset.
|
|
||||||
"pingtime":n, # Ping time
|
|
||||||
"pingwait":n, # Ping wait.
|
|
||||||
"version":n, # The numeric peer version.
|
|
||||||
"subver":"/btcd:0.1/" # The peer useragent string.
|
|
||||||
"inbound":true|false, # True or false whether peer is inbound.
|
|
||||||
"startingheight":n, # Numeric block heght of peer at connect time.
|
|
||||||
"banscore":n, # The numeric ban score.
|
|
||||||
"syncnode":true|false, # Boolean if the peer is the current sync node.
|
|
||||||
}
|
|
||||||
]`,
|
|
||||||
"getrawchangeaddress": `getrawchangeaddress
|
|
||||||
Returns a string containing a new Bitcoin addres for receiving change.
|
|
||||||
This rpc call is for use with raw transactions only.`,
|
|
||||||
|
|
||||||
"getrawmempool": `getrawmempool ( verbose )
|
|
||||||
Returns the contents of the transaction memory pool as a JSON array. If
|
|
||||||
verbose is false just the transaction ids will be returned as strings.
|
|
||||||
If it is true then objects in the following format will be returned:
|
|
||||||
[
|
|
||||||
"transactionid": {
|
|
||||||
"size":n, # Numeric transaction size in bytes.
|
|
||||||
"fee":n, # Numeric transqaction fee in btc.
|
|
||||||
"time":t, # Time transaction entered pool in seconds since the epoch.
|
|
||||||
"height":n, # Numeric block height when the transaction entered pool.
|
|
||||||
"startingpriority:n, # Numeric transaction priority when it entered the pool.
|
|
||||||
"currentpriority":n, # Numeric transaction priority.
|
|
||||||
"depends":[ # Unconfirmed transactions used as inputs for this one. As an array of strings.
|
|
||||||
"transactionid", # Parent transaction id.
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]`,
|
|
||||||
|
|
||||||
"getrawtransaction": `getrawtransaction "txid" ( verbose )
|
|
||||||
Returns raw data related to "txid". If verbose is false, a string containing
|
|
||||||
hex-encoded serialized data for txid. If verbose is true a JSON object with the
|
|
||||||
following information about txid is returned:
|
|
||||||
{
|
|
||||||
"hex":"data", # String of serialized, hex encoded data for txid.
|
|
||||||
"txid":"id", # String containing the transaction id (same as "txid" parameter)
|
|
||||||
"version":n # Numeric tx version number.
|
|
||||||
"locktime":t, # Transaction locktime.
|
|
||||||
"vin":[ # Array of objects representing transaction inputs.
|
|
||||||
{
|
|
||||||
"txid":"id", # Spent transaction id as a string.
|
|
||||||
"vout""n, # Spent transaction output no.
|
|
||||||
"scriptSig":{ # Signature script as an object.
|
|
||||||
"asm":"asm", # Disassembled script string.
|
|
||||||
"hex":"hex", # Hex serialized string.
|
|
||||||
},
|
|
||||||
"sequence":n, # Script sequence number.
|
|
||||||
},
|
|
||||||
...
|
|
||||||
],
|
|
||||||
vout:[ # Array of objects representing transaction outputs.
|
|
||||||
{
|
|
||||||
"value":n, # Numeric value of output in btc.
|
|
||||||
"n", n, # Numeric output index.
|
|
||||||
"scriptPubKey":{ # Object representing pubkey script.
|
|
||||||
"asm":"asm" # Disassembled string of script.
|
|
||||||
"hex":"hex" # Hex serialized string.
|
|
||||||
"reqSigs":n, # Number of required signatures.
|
|
||||||
"type":"pubkey", # Type of scirpt. e.g. pubkeyhash" or "pubkey".
|
|
||||||
"addresses":[ # Array of address strings.
|
|
||||||
"address", # Bitcoin address.
|
|
||||||
...
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"blockhash":"hash" # Hash of the block the transaction is part of.
|
|
||||||
"confirmations":n, # Number of numeric confirmations of block.
|
|
||||||
"time":t, # Transaction time in seconds since the epoch.
|
|
||||||
"blocktime":t, # Block time in seconds since the epoch.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"getreceivedbyaccount": `getreceivedbyaccount "account" ( minconf=1 )
|
|
||||||
Returns the total amount of BTC received by addresses related to "account".
|
|
||||||
Only transactions with at least "minconf" confirmations are considered.`,
|
|
||||||
|
|
||||||
"getreceivedbyaddress": `getreceivedbyaddress "address" ( minconf=1 )
|
|
||||||
Returns the total amount of BTC received by the given address. Only transactions
|
|
||||||
with "minconf" confirmations will be used for the total.`,
|
|
||||||
|
|
||||||
"gettransaction": `gettransaction "txid"
|
|
||||||
Returns a JSON object containing detailed information about the in-wallet
|
|
||||||
transaction "txid". The object follows the following format:
|
|
||||||
{
|
|
||||||
"amount":n, # Transaction amount in BTC.
|
|
||||||
"confirmations":n, # Number of confirmations for transaction.
|
|
||||||
"blockhash":"hash", # Hash of block transaction is part of.
|
|
||||||
"blockindex":n, # Index of block transaction is part of.
|
|
||||||
"blocktime":t, # Time of block transaction is part of.
|
|
||||||
"txid":"id", # Transaction id.
|
|
||||||
"time":t, # Transaction time in seconds since epoch.
|
|
||||||
"timereceived":t, # Time transaction was received in seconds since epoch.
|
|
||||||
"details":[
|
|
||||||
{
|
|
||||||
"account":"name", # The acount name involvedi n the transaction. "" means the default.
|
|
||||||
"address":"address", # The address involved in the transaction as a string.
|
|
||||||
"category":"send|receive", # Category - either send or receive.
|
|
||||||
"amount":n, # numeric amount in BTC.
|
|
||||||
}
|
|
||||||
...
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"gettxout": `gettxout "txid" n ( includemempool )
|
|
||||||
Returns an object containing detauls about an unspent transaction output
|
|
||||||
the "n"th output of "txid":
|
|
||||||
{
|
|
||||||
"bestblock":"hash", # Block has containing transaction.
|
|
||||||
"confirmations":n, # Number of confirmations for block.
|
|
||||||
"value":n # Transaction value in BTC.
|
|
||||||
scriptPubkey" {
|
|
||||||
"asm":"asm", # Disassembled string of script.
|
|
||||||
"hex":"hex", # String script serialized and hex encoded.
|
|
||||||
"reqSigs" # Numeric required signatures.
|
|
||||||
"type":"pubkeyhash" # Type of transaction. e.g. pubkeyhas
|
|
||||||
"addresses":[ # Array of strings containing addresses.
|
|
||||||
"address",
|
|
||||||
...
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"gettxoutsetinfo": `gettxoutsetinfo
|
|
||||||
Returns an object containing startstics about the unspent transaction
|
|
||||||
output set:
|
|
||||||
{
|
|
||||||
"height":n, # Numeric current block height.
|
|
||||||
"bestblock":"hex" # Hex string of best block hash.
|
|
||||||
"transactions":n, # Numeric count of transactions.
|
|
||||||
"txouts":n # Numeric count of transaction outputs.
|
|
||||||
"bytes_serialized":n # Numeric serialized size.
|
|
||||||
"hash_serialized" # String of serialized hash.
|
|
||||||
"total_amount":n, # Numeric total amount in BTC.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"getwork": `getwork ( "data" )
|
|
||||||
If "data" is present it is a hex encoded block datastruture that has been byte
|
|
||||||
reversed, if this is the case then the server will try to solve the
|
|
||||||
block and return true upon success, false upon failure. If data is not
|
|
||||||
specified the following object is returned to describe the work to be
|
|
||||||
solved:
|
|
||||||
{
|
|
||||||
"midstate":"xxx", # String of precomputed hash state for the first half of the data (deprecated).
|
|
||||||
"data":"...", # Block data as string.
|
|
||||||
"hash1":"..." # Hash buffer for second hash as string. (deprecated).
|
|
||||||
"target":"...", # Byte reversed hash target.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"help": `help ( "command" )
|
|
||||||
With no arguemnts, lists the supported commands. If "command" is
|
|
||||||
specified then help text for that command is returned as a string.`,
|
|
||||||
|
|
||||||
"importprivkey": `importprivkey "privkey" ( "label" rescan=true )
|
|
||||||
Adds a private key (in the same format as dumpprivkey) to the wallet. If label
|
|
||||||
is provided this is used as a label for the key. If rescan is true then the
|
|
||||||
blockchain will be scanned for transaction.`,
|
|
||||||
|
|
||||||
"importwallet": `importwallet "filename"
|
|
||||||
Imports keys from the wallet dump file in "filename".`,
|
|
||||||
|
|
||||||
"invalidateblock": `invalidateblock "hash"
|
|
||||||
Mark block specified by "hash" as invalid.`,
|
|
||||||
|
|
||||||
"keypoolrefill": `keypoolrefill ( newsize=100 )
|
|
||||||
Refills the wallet pregenerated key pool to a size of "newsize"`,
|
|
||||||
|
|
||||||
"listaccounts": `listaccounts ( minconf=1)
|
|
||||||
Returns a JSON object mapping account names to account balances:
|
|
||||||
{
|
|
||||||
"accountname": n # Account name to numeric balance in BTC.
|
|
||||||
...
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"listaddressgroupings": `listaddressgroupings
|
|
||||||
Returns a JSON array of array of addreses which have had their relation to each
|
|
||||||
other made public by common use as inputs or in the change address. The data
|
|
||||||
takes the following format:
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"address", # Bitcoin address.
|
|
||||||
amount, # Amount in BTC.
|
|
||||||
"account" # Optional account name string.
|
|
||||||
],
|
|
||||||
...
|
|
||||||
],
|
|
||||||
...
|
|
||||||
]`,
|
|
||||||
|
|
||||||
"listlockunspent": `listlockunspent
|
|
||||||
Returns a JSON array of objects detailing transaction outputs that are
|
|
||||||
temporarily unspendable due to being processed by the lockunspent call.
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"txid":"txid" # Id of locked transaction as a string.
|
|
||||||
"vout":n, # Numeric index of locked output.
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]`,
|
|
||||||
|
|
||||||
"listreceivedbyaccount": `listreceivedbyaccount ( minconf=1 includeempty=false )
|
|
||||||
Returns a JSON array containing objects for each account detailing their
|
|
||||||
balances. Only transaction with at least "minconf" confirmations will be
|
|
||||||
included. If "includeempty" is true then accounts who have received no payments
|
|
||||||
will also be included, else they will be elided. The format is as follows:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"account":"name", # Name of the receiving account.
|
|
||||||
"amount":n, # Total received amount in BTC.
|
|
||||||
"confirmations":n, # Total confirmations for most recent transaction.
|
|
||||||
}
|
|
||||||
]`,
|
|
||||||
|
|
||||||
"listreceivedbyaddress": `listreceivedbyaddress ( minconf=1 includeempty=false )
|
|
||||||
Returns a JSON array containing objects for each address detailing their
|
|
||||||
balances. Only transaction with at least "minconf" confirmations will be
|
|
||||||
included. If "includeempty" is true then adresses who have received no payments
|
|
||||||
will also be included, else they will be elided. The format is as follows:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"account":"name", # Name of the receiving account.
|
|
||||||
"amount":n, # Total received amount in BTC.
|
|
||||||
"confirmations":n, # Total confirmations for most recent transaction.
|
|
||||||
}
|
|
||||||
]`,
|
|
||||||
|
|
||||||
"listsinceblock": `listsinceblock ("blockhash" minconf=1)
|
|
||||||
Gets all wallet transactions in block since "blockhash", if blockhash is
|
|
||||||
omitted then all transactions are provided. If present the only transactions
|
|
||||||
with "minconf" confirmations are listed.
|
|
||||||
{
|
|
||||||
"transactions":[
|
|
||||||
"account" # String of account related to the transaction.
|
|
||||||
"address" # String of related address of transaction.
|
|
||||||
"category":"send|receive" # String detailing whether transaction was a send or receive of funds.
|
|
||||||
"amount":n, # Numeric value of transaction. Negative if transaction category was "send"
|
|
||||||
"fee":n, # Numeric value of transaction fee in BTC.
|
|
||||||
"confirmations":n, # Number of transaction confirmations
|
|
||||||
"blockhash":"hash" # String of hash of block transaction is part of.
|
|
||||||
"blockindex":n, # Numeric index of block transaction is part of.
|
|
||||||
"blocktime":t, # Block time in seconds since the epoch.
|
|
||||||
"txid":"id", # Transaction id.
|
|
||||||
"time":t, # Transaction time in second since the epoch.
|
|
||||||
"timereceived":t, # Time transaction received in seconds since the epoch.
|
|
||||||
"comment":"...", # String of the comment associated with the transaction.
|
|
||||||
"to":"...", # String of "to" comment of the transaction.
|
|
||||||
]
|
|
||||||
"lastblock":"lastblockhash" # Hash of the last block as a string.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"listtransactions": `listtransactions ( "account" count=10 from=0 )
|
|
||||||
Returns up to "count" most recent wallet transactions for "account" (or all
|
|
||||||
accounts if none specified) skipping the first "from" transactions.
|
|
||||||
{
|
|
||||||
"transactions":[
|
|
||||||
"account" # String of account related to the transaction.
|
|
||||||
"address" # String of related address of transaction.
|
|
||||||
"category":"send|receive|move" # String detailing whether transaction was a send or receive of funds.Move is a local move between accounts and doesnt touch the blockchain.
|
|
||||||
"amount":n, # Numeric value of transaction. Negative if transaction category was "send"
|
|
||||||
"fee":n, # Numeric value of transaction fee in BTC.
|
|
||||||
"confirmations":n, # Number of transaction confirmations
|
|
||||||
"blockhash":"hash" # String of hash of block transaction is part of.
|
|
||||||
"blockindex":n, # Numeric index of block transaction is part of.
|
|
||||||
"blocktime":t, # Block time in seconds since the epoch.
|
|
||||||
"txid":"id", # Transaction id.
|
|
||||||
"time":t, # Transaction time in second since the epoch.
|
|
||||||
"timereceived":t, # Time transaction received in seconds since the epoch.
|
|
||||||
"comment":"...", # String of the comment associated with the transaction.
|
|
||||||
"to":"...", # String of "to" comment of the transaction.
|
|
||||||
]
|
|
||||||
"lastblock":"lastblockhash" # Hash of the last block as a string.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"listunspent": `listunspent (minconf=1 maxconf=9999999 ["address",...]
|
|
||||||
Returns a JSON array of objects representing unspent transaction outputs with
|
|
||||||
between minconf and maxconf confirmations (inclusive). If the array of addresses
|
|
||||||
is present then only txouts paid to the addresses listed will be considered. The
|
|
||||||
objects take the following format:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"txid":"id", # The transaction id as a string.
|
|
||||||
"vout":n, # The output number of the tx.
|
|
||||||
"address":"add", # String of the transaction address.
|
|
||||||
"account":"acc", # The associated account, "" for default.
|
|
||||||
"scriptPubkey":"key" # The pubkeyscript as a string.
|
|
||||||
"amount":n, # The value of the transaction in BTC.
|
|
||||||
"confirmations":n, # The numer of confirmations.
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]`,
|
|
||||||
|
|
||||||
"lockunspent": `lockunspent unlock [{"txid":id", "vout":n},...]
|
|
||||||
Changes the llist of temporarily unspendable transaction outputs. The
|
|
||||||
transacion outputs in the list of objects provided will be marked as
|
|
||||||
locked (if unlock is false) or unlocked (unlock is true). A locked
|
|
||||||
transaction will not be chosen automatically to be spent when a
|
|
||||||
tranaction is created. Boolean is returned whether the command succeeded.`,
|
|
||||||
|
|
||||||
"move": `move "fromaccount" "toaccount" amount ( minconf=1 "comment" )
|
|
||||||
Moves a specifies amount from "fromaccount" to "toaccount". Only funds
|
|
||||||
with minconf confirmations are used. If comment is present this comment
|
|
||||||
will be stored in the wallet with the transaction. Boolean is returned
|
|
||||||
to denode success.`,
|
|
||||||
|
|
||||||
"ping": `ping
|
|
||||||
Queues a ping to be sent to each connected peer. Ping times are provided in
|
|
||||||
getpeerinfo.`,
|
|
||||||
|
|
||||||
"reconsiderblock": `reconsiderblock "hash"
|
|
||||||
Remove invalid mark from block specified by "hash" so it is considered again.`,
|
|
||||||
|
|
||||||
"searchrawtransactions": `searchrawtransactions "address" (verbose=1 skip=0 count=100)
|
|
||||||
Returns raw tx data related to credits or debits to "address". Skip indicates
|
|
||||||
the number of leading transactions to leave out of the final result. Count
|
|
||||||
represents the max number of transactions to return. If verbose is false, a
|
|
||||||
string containing hex-encoded serialized data for txid. If verbose is true a
|
|
||||||
JSON object with the following information about txid is returned:
|
|
||||||
{
|
|
||||||
"hex":"data", # String of serialized, hex encoded data for txid.
|
|
||||||
"txid":"id", # String containing the transaction id (same as "txid" parameter)
|
|
||||||
"version":n # Numeric tx version number.
|
|
||||||
"locktime":t, # Transaction locktime.
|
|
||||||
"vin":[ # Array of objects representing transaction inputs.
|
|
||||||
{
|
|
||||||
"txid":"id", # Spent transaction id as a string.
|
|
||||||
"vout""n, # Spent transaction output no.
|
|
||||||
"scriptSig":{ # Signature script as an object.
|
|
||||||
"asm":"asm", # Disassembled script string.
|
|
||||||
"hex":"hex", # Hex serialized string.
|
|
||||||
},
|
|
||||||
"sequence":n, # Script sequence number.
|
|
||||||
},
|
|
||||||
...
|
|
||||||
],
|
|
||||||
vout:[ # Array of objects representing transaction outputs.
|
|
||||||
{
|
|
||||||
"value":n, # Numeric value of output in btc.
|
|
||||||
"n", n, # Numeric output index.
|
|
||||||
"scriptPubKey":{ # Object representing pubkey script.
|
|
||||||
"asm":"asm" # Disassembled string of script.
|
|
||||||
"hex":"hex" # Hex serialized string.
|
|
||||||
"reqSigs":n, # Number of required signatures.
|
|
||||||
"type":"pubkey", # Type of scirpt. e.g. pubkeyhash" or "pubkey".
|
|
||||||
"addresses":[ # Array of address strings.
|
|
||||||
"address", # Bitcoin address.
|
|
||||||
...
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"blockhash":"hash" # Hash of the block the transaction is part of.
|
|
||||||
"confirmations":n, # Number of numeric confirmations of block.
|
|
||||||
"time":t, # Transaction time in seconds since the epoch.
|
|
||||||
"blocktime":t, # Block time in seconds since the epoch.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"sendfrom": `sendfrom "fromaccount" "tobitcoinaddress" amount ( minconf=1 "comment" "comment-to" )
|
|
||||||
Sends "amount" (rounded to the nearest 0.00000001) to
|
|
||||||
"tobitcoindaddress" from "fromaccount". Only funds with at least
|
|
||||||
"minconf" confirmations will be considered. If "comment" is present this
|
|
||||||
will be store the purpose of the transaction. If "comment-to" is present
|
|
||||||
this comment will be recorded locally to store the recipient of the
|
|
||||||
transaction.`,
|
|
||||||
|
|
||||||
"sendmany": `sendmany "fromaccount" {"address":amount,...} ( minconf=1 "comment" )
|
|
||||||
Sends funds to multiple recipients. Funds from "fromaccount" are send to the
|
|
||||||
address/amount pairs specified. Only funds with minconf confirmations are
|
|
||||||
considered. "comment" if present is recorded locally as the purpose of the
|
|
||||||
transaction.`,
|
|
||||||
|
|
||||||
"sendrawtransaction": `sendrawtransaction "hexstring" (allowhighfees=false)
|
|
||||||
Submits the hex-encoded transaction in "hexstring" to the bitcoin network via
|
|
||||||
the local node. If allowhighfees is true then high fees will be allowed.`,
|
|
||||||
|
|
||||||
"sendtoaddress": `sendtoaddress "bitcoindaddress" amount ( "comment" "comment-to")
|
|
||||||
Send "amount" BTC (rounded to the nearest 0.00000001) to "bitcoinaddress". If
|
|
||||||
comment is set, it will be stored locally to describe the purpose of the
|
|
||||||
transaction. If comment-to" is set it will be stored locally to describe the
|
|
||||||
recipient of the transaction. A string containing the transaction id is
|
|
||||||
returned if successful.`,
|
|
||||||
|
|
||||||
"setaccount": `setaccount "address" "account"
|
|
||||||
Sets the account associated with "address" to "account".`,
|
|
||||||
|
|
||||||
"setgenerate": `setgenerate generate ( genproclimit )
|
|
||||||
Sets the current mining state to "generate". Up to "genproclimit" processors
|
|
||||||
will be used, if genproclimit is -1 then it is unlimited.`,
|
|
||||||
|
|
||||||
"settxfee": `settxfee amount
|
|
||||||
Sets the current transaction fee.`,
|
|
||||||
|
|
||||||
"signmessage": `signmessage "address" "message"
|
|
||||||
Returns a signature for "message" with the private key of "address"`,
|
|
||||||
|
|
||||||
"signrawtransaction": `signrawtransaction "hexstring" ( prevtxs ["privatekey",...] sighashtype="ALL" )
|
|
||||||
Signs the inputs for the serialized and hex encoded transaction in
|
|
||||||
"hexstring". "prevtxs" optionally is an is an array of objects
|
|
||||||
representing the previous tx outputs that are spent here but may not yet
|
|
||||||
be in the blockchain, it takes the following format:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"txid":id:, # String of transaction id.
|
|
||||||
"vout":n, # Output number.
|
|
||||||
"scriptPubKey":"hex", # Hex encoded string of pubkey script from transaction.
|
|
||||||
"redemScript":"hex" # Hex encoded redemption script.
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
If the third argument is provided these base58-encoded private keys in
|
|
||||||
the list will be the only keys used to sign the transaction. sighashtype
|
|
||||||
optionally denoes the signature hash type to be used. is must be one of the
|
|
||||||
following:
|
|
||||||
"ALL",
|
|
||||||
"NONE",
|
|
||||||
"SINGLE",
|
|
||||||
"ALL|ANYONECANPAY",
|
|
||||||
"NONE|ANYONECANPAY",
|
|
||||||
"SINGLE|ANYONECANPAY".
|
|
||||||
The return value takes the following format:
|
|
||||||
{
|
|
||||||
"hex":"value" # Hex string of raw transactino with signatures applied.
|
|
||||||
"complete":n, # If the transaction has a complete set of signatures. 0 if false.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"stop": `stop
|
|
||||||
Stop the server.`,
|
|
||||||
|
|
||||||
"submitblock": `submitblock "data" ( optionalparameterobject )
|
|
||||||
Will attempt to submit the block serialized in "data" to the bitcoin network.
|
|
||||||
optionalparametersobject takes the following format:
|
|
||||||
{
|
|
||||||
"workid":"id" # If getblocktemplate provided a workid it must be included with submissions.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"validateaddress": `validateaddress "address
|
|
||||||
Returns a JSON objects containing information about the provided "address".
|
|
||||||
Format:
|
|
||||||
{
|
|
||||||
"isvalid":true|false, # If the address is valid. If false this is the only property returned.
|
|
||||||
"address:"address", # The address that was validated.
|
|
||||||
"ismine":true|false, # If the address belongs to the server.
|
|
||||||
"isscript:true|false, # If the address is a script address.
|
|
||||||
"pubkey":"pk", # The hex value of the public key associated with the address.
|
|
||||||
"iscompressed":true|false, # If the address is compresssed.
|
|
||||||
"account":"account", # The related account. "" is the default.
|
|
||||||
}`,
|
|
||||||
|
|
||||||
"verifychain": `verifychain ( checklevel=3 numblocks=288 )
|
|
||||||
Verifies the stored blockchain database. "checklevel" denotes how thorough the
|
|
||||||
check is and "numblocks" how many blocks from the tip will be verified.`,
|
|
||||||
|
|
||||||
"verifymessage": `verifymessage "bitcoinaddress" "signature" "message"
|
|
||||||
Verifies the signature "signature" for "message" from the key related
|
|
||||||
to "bitcoinaddress". The return is true or false if the signature
|
|
||||||
verified correctly.`,
|
|
||||||
|
|
||||||
"walletlock": `walletlock
|
|
||||||
Removes any encryption key for the wallet from memory, unlocking the wallet.
|
|
||||||
In order to use wallet functionality again the wallet must be unlocked via
|
|
||||||
walletpassphrase.`,
|
|
||||||
|
|
||||||
"walletpassphrase": `walletpassphrase "passphrase" timeout
|
|
||||||
Decrypts the wallet for "timeout" seconds with "passphrase". This is required
|
|
||||||
before using wallet functionality.`,
|
|
||||||
|
|
||||||
"walletpassphrasechange": `walletpassphrasechange "oldpassphrase" "newpassphrase"
|
|
||||||
Changes the wallet passphrase from "oldpassphrase" to "newpassphrase".`,
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHelpString returns a string containing help text for "cmdName". If
|
|
||||||
// cmdName is unknown to btcjson - either via the default command list or those
|
|
||||||
// registered using RegisterCustomCmd - an error will be returned.
|
|
||||||
func GetHelpString(cmdName string) (string, error) {
|
|
||||||
helpstr := ""
|
|
||||||
|
|
||||||
if help, ok := defaultHelpStrings[cmdName]; ok {
|
|
||||||
return help, nil
|
|
||||||
}
|
|
||||||
if c, ok := customCmds[cmdName]; ok {
|
|
||||||
return c.helpString, nil
|
|
||||||
}
|
|
||||||
return helpstr, errors.New("invalid command specified")
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestCmdMethod tests the CmdMethod function to ensure it retuns the expected
|
// TestCmdMethod tests the CmdMethod function to ensure it retuns the expected
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestAssignField tests the assignField function handles supported combinations
|
// TestAssignField tests the assignField function handles supported combinations
|
204
btcjson/doc.go
204
btcjson/doc.go
|
@ -1,116 +1,146 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
// Copyright (c) 2015 Conformal Systems LLC.
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package btcjson implements the bitcoin JSON-RPC API.
|
Package btcjson provides primitives for working with the bitcoin JSON-RPC API.
|
||||||
|
|
||||||
A complete description of the JSON-RPC protocol as used by bitcoin can
|
Overview
|
||||||
be found on the official wiki at
|
|
||||||
https://en.bitcoin.it/wiki/API_reference_%28JSON-RPC%29 with a list of
|
|
||||||
all the supported calls at
|
|
||||||
https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list.
|
|
||||||
|
|
||||||
This package provides data structures and code for marshalling and
|
When communicating via the JSON-RPC protocol, all of the commands need to be
|
||||||
unmarshalling json for communicating with a running instance of btcd
|
marshalled to and from the the wire in the appropriate format. This package
|
||||||
or bitcoind/bitcoin-qt. It also provides code for sending those
|
provides data structures and primitives to ease this process.
|
||||||
messages. It does not provide any code for the client to actually deal
|
|
||||||
with the messages. Although it is meant primarily for btcd, it is
|
|
||||||
possible to use this code elsewhere for interacting with a bitcoin
|
|
||||||
client programatically.
|
|
||||||
|
|
||||||
Protocol
|
In addition, it also provides some additional features such as custom command
|
||||||
|
registration, command categorization, and reflection-based help generation.
|
||||||
|
|
||||||
All messages to bitcoin are of the form:
|
JSON-RPC Protocol Overview
|
||||||
|
|
||||||
{"jsonrpc":"1.0","id":"SOMEID","method":"SOMEMETHOD","params":SOMEPARAMS}
|
This information is not necessary in order to use this package, but it does
|
||||||
|
provide some intuition into what the marshalling and unmarshalling that is
|
||||||
|
discussed below is doing under the hood.
|
||||||
|
|
||||||
The params field can vary in what it contains depending on the
|
As defined by the JSON-RPC spec, there are effectively two forms of messages on
|
||||||
different method (or command) being sent.
|
the wire:
|
||||||
|
|
||||||
Replies will vary in form for the different commands. The basic form is:
|
- Request Objects
|
||||||
|
{"jsonrpc":"1.0","id":"SOMEID","method":"SOMEMETHOD","params":[SOMEPARAMS]}
|
||||||
|
NOTE: Notifications are the same format except the id field is null.
|
||||||
|
|
||||||
{"result":SOMETHING,"error":null,"id":"SOMEID"}
|
- Response Objects
|
||||||
|
{"result":SOMETHING,"error":null,"id":"SOMEID"}
|
||||||
|
{"result":null,"error":{"code":SOMEINT,"message":SOMESTRING},"id":"SOMEID"}
|
||||||
|
|
||||||
The result field can be as simple as an int, or a complex structure
|
For requests, the params field can vary in what it contains depending on the
|
||||||
containing many nested fields. For cases where we have already worked
|
method (a.k.a. command) being sent. Each parameter can be as simple as an int
|
||||||
out the possible types of reply, result is unmarshalled into a
|
or a complex structure containing many nested fields. The id field is used to
|
||||||
structure that matches the command. For others, an interface is
|
identify a request and will be included in the associated response.
|
||||||
returned. An interface is not as convenient as one needs to do a type
|
|
||||||
assertion first before using the value, but it means we can handle
|
|
||||||
arbitrary replies.
|
|
||||||
|
|
||||||
The error field is null when there is no error. When there is an
|
When working with asynchronous transports, such as websockets, spontaneous
|
||||||
error it will return a numeric error code as well as a message
|
notifications are also possible. As indicated, they are the same as a request
|
||||||
describing the error.
|
object, except they have the id field set to null. Therefore, servers will
|
||||||
|
ignore requests with the id field set to null, while clients can choose to
|
||||||
|
consume or ignore them.
|
||||||
|
|
||||||
id is simply the id of the requester.
|
Unfortunately, the original Bitcoin JSON-RPC API (and hence anything compatible
|
||||||
|
with it) doesn't always follow the spec and will sometimes return an error
|
||||||
|
string in the result field with a null error for certain commands. However,
|
||||||
|
for the most part, the error field will be set as described on failure.
|
||||||
|
|
||||||
RPC Server Authentication
|
Marshalling and Unmarshalling
|
||||||
|
|
||||||
All RPC calls must be authenticated with a username and password. The specifics
|
Based upon the discussion above, it should be easy to see how the types of this
|
||||||
on how to configure the RPC server varies depending on the specific bitcoin
|
package map into the required parts of the protocol
|
||||||
implementation. For bitcoind, this is accomplished by setting rpcuser and
|
|
||||||
rpcpassword in the file ~/.bitcoin/bitcoin.conf. For btcd, this is accomplished
|
|
||||||
by setting rpcuser and rpcpass in the file ~/.btcd/btcd.conf. The default local
|
|
||||||
address of bitcoind is 127.0.0.1:8332 and the default local address of btcd is
|
|
||||||
127.0.0.1:8334
|
|
||||||
|
|
||||||
Usage
|
- Request Objects (type Request)
|
||||||
|
- Commands (type <Foo>Cmd)
|
||||||
|
- Notifications (type <Foo>Ntfn)
|
||||||
|
- Response Objects (type Response)
|
||||||
|
- Result (type <Foo>Result)
|
||||||
|
|
||||||
The general pattern to use this package consists of generating a message (see
|
To simplify the marshalling of the requests and responses, the MarshalCmd and
|
||||||
the full list on the official bitcoin wiki), sending the message, and handling
|
MarshalResponse functions are provided. They return the raw bytes ready to be
|
||||||
the result after asserting its type.
|
sent across the wire.
|
||||||
|
|
||||||
For commands where the reply structure is known, such as getinfo, one can
|
Unmarshalling a received Request object is a two step process:
|
||||||
directly access the fields in the Reply structure by type asserting the
|
1) Unmarshal the raw bytes into a Request struct instance via json.Unmarshal
|
||||||
reply to the appropriate concrete type.
|
2) Use UnmarshalCmd on the Result field of the unmarshalled Request to create
|
||||||
|
a concrete command or notification instance with all struct fields set
|
||||||
|
accordingly
|
||||||
|
|
||||||
// Create a getinfo command.
|
This approach is used since it provides the caller with access to the additional
|
||||||
id := 1
|
fields in the request that are not part of the command such as the ID.
|
||||||
cmd, err := btcjson.NewGetInfoCmd(id)
|
|
||||||
if err != nil {
|
|
||||||
// Log and handle error.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the message to server using the appropriate username and
|
Unmarshalling a received Response object is also a two step process:
|
||||||
// password.
|
1) Unmarhsal the raw bytes into a Response struct instance via json.Unmarshal
|
||||||
reply, err := btcjson.RpcSend(user, password, server, cmd)
|
2) Depending on the ID, unmarshal the Result field of the unmarshalled
|
||||||
if err != nil {
|
Response to create a concrete type instance
|
||||||
fmt.Println(err)
|
|
||||||
// Log and handle error.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure there is a result and type assert it to a btcjson.InfoResult.
|
As above, this approach is used since it provides the caller with access to the
|
||||||
if reply.Result != nil {
|
fields in the response such as the ID and Error.
|
||||||
if info, ok := reply.Result.(*btcjson.InfoResult); ok {
|
|
||||||
fmt.Println("balance =", info.Balance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
For other commands where this package does not yet provide a concrete
|
Command Creation
|
||||||
implementation for the reply, such as getrawmempool, the reply uses a generic
|
|
||||||
interface so one can access individual items as follows:
|
|
||||||
|
|
||||||
// Create a getrawmempool command.
|
This package provides two approaches for creating a new command. This first,
|
||||||
id := 1
|
and preferred, method is to use one of the New<Foo>Cmd functions. This allows
|
||||||
cmd, err := btcjson.NewGetRawMempoolCmd(id)
|
static compile-time checking to help ensure the parameters stay in sync with
|
||||||
if err != nil {
|
the struct definitions.
|
||||||
// Log and handle error.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the message to server using the appropriate username and
|
The second approach is the NewCmd function which takes a method (command) name
|
||||||
// password.
|
and variable arguments. The function includes full checking to ensure the
|
||||||
reply, err := btcjson.RpcSend(user, password, server, cmd)
|
parameters are accurate according to provided method, however these checks are,
|
||||||
if err != nil {
|
obviously, run-time which means any mistakes won't be found until the code is
|
||||||
// Log and handle error.
|
actually executed. However, it is quite useful for user-supplied commands
|
||||||
}
|
that are intentionally dynamic.
|
||||||
|
|
||||||
// Ensure there is a result and type assert it to a string slice.
|
Custom Command Registration
|
||||||
if reply.Result != nil {
|
|
||||||
if mempool, ok := reply.Result.([]string); ok {
|
The command handling of this package is built around the concept of registered
|
||||||
fmt.Println("num mempool entries =", len(mempool))
|
commands. This is true for the wide variety of commands already provided by the
|
||||||
}
|
package, but it also means caller can easily provide custom commands with all
|
||||||
}
|
of the same functionality as the built-in commands. Use the RegisterCmd
|
||||||
|
function for this purpose.
|
||||||
|
|
||||||
|
A list of all registered methods can be obtained with the RegisteredCmdMethods
|
||||||
|
function.
|
||||||
|
|
||||||
|
Command Inspection
|
||||||
|
|
||||||
|
All registered commands are registered with flags that identify information such
|
||||||
|
as whether the command applies to a chain server, wallet server, or is a
|
||||||
|
notification along with the method name to use. These flags can be obtained
|
||||||
|
with the MethodUsageFlags flags, and the method can be obtained with the
|
||||||
|
CmdMethod function.
|
||||||
|
|
||||||
|
Help Generation
|
||||||
|
|
||||||
|
To facilitate providing consistent help to users of the RPC server, this package
|
||||||
|
exposes the GenerateHelp and function which uses reflection on registered
|
||||||
|
commands or notifications, as well as the provided expected result types, to
|
||||||
|
generate the final help text.
|
||||||
|
|
||||||
|
In addition, the MethodUsageText function is provided to generate consistent
|
||||||
|
one-line usage for registered commands and notifications using reflection.
|
||||||
|
|
||||||
|
Errors
|
||||||
|
|
||||||
|
There are 2 distinct type of errors supported by this package:
|
||||||
|
|
||||||
|
- General errors related to marshalling or unmarshalling or improper use of
|
||||||
|
the package (type Error)
|
||||||
|
- RPC errors which are intended to be returned across the wire as a part of
|
||||||
|
the JSON-RPC response (type RPCError)
|
||||||
|
|
||||||
|
The first category of errors (type Error) typically indicates a programmer error
|
||||||
|
and can be avoided by properly using the API. Errors of this type will be
|
||||||
|
returned from the various functions available in this package. They identify
|
||||||
|
issues such as unsupported field types, attempts to register malformed commands,
|
||||||
|
and attempting to create a new command with an improper number of parameters.
|
||||||
|
The specific reason for the error can be detected by type asserting it to a
|
||||||
|
*btcjson.Error and accessing the ErrorCode field.
|
||||||
|
|
||||||
|
The second category of errors (type RPCError), on the other hand, are useful for
|
||||||
|
returning errors to RPC clients. Consequently, they are used in the previously
|
||||||
|
described Response type.
|
||||||
*/
|
*/
|
||||||
package btcjson
|
package btcjson
|
||||||
|
|
|
@ -7,7 +7,7 @@ package btcjson_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestErrorCodeStringer tests the stringized output for the ErrorCode type.
|
// TestErrorCodeStringer tests the stringized output for the ErrorCode type.
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This example demonstrates how to create and marshal a command into a JSON-RPC
|
// This example demonstrates how to create and marshal a command into a JSON-RPC
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestHelpReflectInternals ensures the various help functions which deal with
|
// TestHelpReflectInternals ensures the various help functions which deal with
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestHelpers tests the various helper functions which create pointers to
|
// TestHelpers tests the various helper functions which create pointers to
|
|
@ -1,59 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
This test file is part of the btcjson package rather than than the
|
|
||||||
btcjson_test package so it can bridge access to the internals to properly test
|
|
||||||
cases which are either not possible or can't reliably be tested via the public
|
|
||||||
interface. The functions are only exported while the tests are being run.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TestJsonWIthArgs tests jsonWithArgs to ensure that it can generate a json
|
|
||||||
// command as well as sending it something that cannot be marshalled to json
|
|
||||||
// (a channel) to test the failure paths.
|
|
||||||
func TestJsonWithArgs(t *testing.T) {
|
|
||||||
cmd := "list"
|
|
||||||
var args interface{}
|
|
||||||
_, err := jsonWithArgs(cmd, "test", args)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Could not make json with no args. %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
channel := make(chan int)
|
|
||||||
_, err = jsonWithArgs(cmd, "test", channel)
|
|
||||||
if _, ok := err.(*json.UnsupportedTypeError); !ok {
|
|
||||||
t.Errorf("Message with channel should fail. %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var comp complex128
|
|
||||||
_, err = jsonWithArgs(cmd, "test", comp)
|
|
||||||
if _, ok := err.(*json.UnsupportedTypeError); !ok {
|
|
||||||
t.Errorf("Message with complex part should fail. %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestJsonRPCSend tests jsonRPCSend which actually send the rpc command.
|
|
||||||
// This currently a negative test only until we setup a fake http server to
|
|
||||||
// test the actually connection code.
|
|
||||||
func TestJsonRPCSend(t *testing.T) {
|
|
||||||
// Just negative test right now.
|
|
||||||
user := "something"
|
|
||||||
password := "something"
|
|
||||||
server := "invalid"
|
|
||||||
var message []byte
|
|
||||||
_, err := jsonRPCSend(user, password, server, message, false, nil, false)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should fail when it cannot connect.")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,821 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BadStatusCode describes a HTTP error when a response has non-200 status code
|
|
||||||
type BadStatusCode int
|
|
||||||
|
|
||||||
func (e BadStatusCode) Error() string {
|
|
||||||
status := int(e)
|
|
||||||
return fmt.Sprintf("http bad status: %d %s", status, http.StatusText(status))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrIncorrectArgTypes describes an error where the wrong argument types
|
|
||||||
// are present.
|
|
||||||
var ErrIncorrectArgTypes = errors.New("incorrect arguement types")
|
|
||||||
|
|
||||||
// Message contains a message to be sent to the bitcoin client.
|
|
||||||
type Message struct {
|
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
|
||||||
Id interface{} `json:"id,omitempty"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Params interface{} `json:"params"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reply is the general form of the reply from the bitcoin client.
|
|
||||||
// The form of the Result part varies from one command to the next so it
|
|
||||||
// is currently implemented as an interface.
|
|
||||||
type Reply struct {
|
|
||||||
Result interface{} `json:"result"`
|
|
||||||
Error *Error `json:"error"`
|
|
||||||
// This has to be a pointer for go to put a null in it when empty.
|
|
||||||
Id *interface{} `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error models the error field of the json returned by a bitcoin client. When
|
|
||||||
// there is no error, this should be a nil pointer to produce the null in the
|
|
||||||
// json that bitcoind produces.
|
|
||||||
type Error struct {
|
|
||||||
Code int `json:"code,omitempty"`
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guarantee Error satisifies the builtin error interface
|
|
||||||
var _, _ error = Error{}, &Error{}
|
|
||||||
|
|
||||||
// Error returns a string describing the btcjson error. This
|
|
||||||
// satisifies the builtin error interface.
|
|
||||||
func (e Error) Error() string {
|
|
||||||
return fmt.Sprintf("%d: %s", e.Code, e.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// jsonWithArgs takes a command, an id, and an interface which contains an
|
|
||||||
// array of the arguments for that command. It knows NOTHING about the commands
|
|
||||||
// so all error checking of the arguments must happen before it is called.
|
|
||||||
func jsonWithArgs(command string, id interface{}, args interface{}) ([]byte, error) {
|
|
||||||
rawMessage := Message{"1.0", id, command, args}
|
|
||||||
finalMessage, err := json.Marshal(rawMessage)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return finalMessage, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMessage takes a string and the optional arguments for it. Then,
|
|
||||||
// if it is a recognized bitcoin json message, generates the json message ready
|
|
||||||
// to send off to the daemon or server.
|
|
||||||
// It is capable of handeling all of the commands from the standard client,
|
|
||||||
// described at:
|
|
||||||
// https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list
|
|
||||||
//
|
|
||||||
// WARNING: This method is deprecated and may be removed in a future version.
|
|
||||||
// Do NOT use this as it does not work for all commands. Instead, use one of
|
|
||||||
// the New<command>Cmd functions to create a specific command.
|
|
||||||
func CreateMessage(message string, args ...interface{}) ([]byte, error) {
|
|
||||||
finalMessage, err := CreateMessageWithId(message, "btcd", args...)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMessageWithId takes a string, an id, and the optional arguments for
|
|
||||||
// it. Then, if it is a recognized bitcoin json message, generates the json
|
|
||||||
// message ready to send off to the daemon or server. It is capable of handling
|
|
||||||
// all of the commands from the standard client, described at:
|
|
||||||
// https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list
|
|
||||||
//
|
|
||||||
// WARNING: This method is deprecated and may be removed in a future version.
|
|
||||||
// Do NOT use this as it does not work for all commands. Instead, use one of
|
|
||||||
// the New<command>Cmd functions to create a specific command.
|
|
||||||
func CreateMessageWithId(message string, id interface{}, args ...interface{}) ([]byte, error) {
|
|
||||||
var finalMessage []byte
|
|
||||||
var err error
|
|
||||||
// Different commands have different required and optional arguments.
|
|
||||||
// Need to handle them based on that.
|
|
||||||
switch message {
|
|
||||||
// No args
|
|
||||||
case "getblockcount", "getblocknumber", "getconnectioncount",
|
|
||||||
"getdifficulty", "getgenerate", "gethashespersec", "getinfo",
|
|
||||||
"getmininginfo", "getpeerinfo", "getrawmempool",
|
|
||||||
"keypoolrefill", "listaddressgroupings", "listlockunspent",
|
|
||||||
"stop", "walletlock", "getbestblockhash", "getblockchaininfo",
|
|
||||||
"getnetworkinfo":
|
|
||||||
if len(args) > 0 {
|
|
||||||
err = fmt.Errorf("too many arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One optional int
|
|
||||||
case "listaccounts":
|
|
||||||
if len(args) > 1 {
|
|
||||||
err = fmt.Errorf("too many arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
if len(args) == 1 {
|
|
||||||
_, ok := args[0].(int)
|
|
||||||
if !ok {
|
|
||||||
err = fmt.Errorf("argument must be int for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required int
|
|
||||||
case "getblockhash", "estimatefee", "estimatepriority":
|
|
||||||
if len(args) != 1 {
|
|
||||||
err = fmt.Errorf("missing argument for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok := args[0].(int)
|
|
||||||
if !ok {
|
|
||||||
err = fmt.Errorf("argument must be int for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required float
|
|
||||||
case "settxfee":
|
|
||||||
if len(args) != 1 {
|
|
||||||
err = fmt.Errorf("missing argument for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok := args[0].(float64)
|
|
||||||
if !ok {
|
|
||||||
err = fmt.Errorf("argument must be float64 for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One optional string
|
|
||||||
case "getmemorypool", "getnewaddress", "getwork", "help",
|
|
||||||
"getrawchangeaddress":
|
|
||||||
if len(args) > 1 {
|
|
||||||
err = fmt.Errorf("too many arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
if len(args) == 1 {
|
|
||||||
_, ok := args[0].(string)
|
|
||||||
if !ok {
|
|
||||||
err = fmt.Errorf("optional argument must be string for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required string
|
|
||||||
case "backupwallet", "decoderawtransaction", "dumpprivkey",
|
|
||||||
"encryptwallet", "getaccount", "getaccountaddress",
|
|
||||||
"getaddressesbyaccount", "getblock",
|
|
||||||
"gettransaction", "sendrawtransaction", "validateaddress",
|
|
||||||
"invalidateblock", "reconsiderblock":
|
|
||||||
if len(args) != 1 {
|
|
||||||
err = fmt.Errorf("%s requires one argument", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok := args[0].(string)
|
|
||||||
if !ok {
|
|
||||||
err = fmt.Errorf("argument must be string for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// Two required strings
|
|
||||||
case "setaccount", "signmessage", "walletpassphrasechange", "addnode":
|
|
||||||
if len(args) != 2 {
|
|
||||||
err = fmt.Errorf("missing arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
_, ok2 := args[1].(string)
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("arguments must be string for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required string, one required int
|
|
||||||
case "walletpassphrase":
|
|
||||||
if len(args) != 2 {
|
|
||||||
err = fmt.Errorf("missing arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
_, ok2 := args[1].(int)
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("arguments must be string and int for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// Three required strings
|
|
||||||
case "verifymessage":
|
|
||||||
if len(args) != 3 {
|
|
||||||
err = fmt.Errorf("three arguments required for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
_, ok2 := args[1].(string)
|
|
||||||
_, ok3 := args[2].(string)
|
|
||||||
if !ok1 || !ok2 || !ok3 {
|
|
||||||
err = fmt.Errorf("arguments must be string for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required bool, one optional string
|
|
||||||
case "getaddednodeinfo":
|
|
||||||
if len(args) > 2 || len(args) == 0 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(bool)
|
|
||||||
ok2 := true
|
|
||||||
if len(args) == 2 {
|
|
||||||
_, ok2 = args[1].(string)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("arguments must be bool and optionally string for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required bool, one optional int
|
|
||||||
case "setgenerate":
|
|
||||||
if len(args) > 2 || len(args) == 0 {
|
|
||||||
err = fmt.Errorf("wrong number of argument for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(bool)
|
|
||||||
ok2 := true
|
|
||||||
if len(args) == 2 {
|
|
||||||
_, ok2 = args[1].(int)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("arguments must be bool and optionally int for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One optional string, one optional int
|
|
||||||
case "getbalance", "getreceivedbyaccount":
|
|
||||||
if len(args) > 2 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
ok1 := true
|
|
||||||
ok2 := true
|
|
||||||
if len(args) >= 1 {
|
|
||||||
_, ok1 = args[0].(string)
|
|
||||||
}
|
|
||||||
if len(args) == 2 {
|
|
||||||
_, ok2 = args[1].(int)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("optional arguments must be string and int for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required string, one optional int
|
|
||||||
case "getrawtransaction", "getreceivedbyaddress":
|
|
||||||
if len(args) > 2 || len(args) == 0 {
|
|
||||||
err = fmt.Errorf("wrong number of argument for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
ok2 := true
|
|
||||||
if len(args) == 2 {
|
|
||||||
_, ok2 = args[1].(int)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("arguments must be string and optionally int for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required string, one optional string
|
|
||||||
// Strictly, the optional arg for submit block is an object, but
|
|
||||||
// bitcoind ignores it for now, so best to just allow string until
|
|
||||||
// support for it is complete.
|
|
||||||
case "submitblock":
|
|
||||||
if len(args) > 2 || len(args) == 0 {
|
|
||||||
err = fmt.Errorf("wrong number of argument for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
ok2 := true
|
|
||||||
if len(args) == 2 {
|
|
||||||
_, ok2 = args[1].(string)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("arguments must be string and optionally string for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One optional int, one optional bool
|
|
||||||
case "listreceivedbyaccount", "listreceivedbyaddress":
|
|
||||||
if len(args) > 2 {
|
|
||||||
err = fmt.Errorf("wrong number of argument for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
ok1 := true
|
|
||||||
ok2 := true
|
|
||||||
if len(args) >= 1 {
|
|
||||||
_, ok1 = args[0].(int)
|
|
||||||
}
|
|
||||||
if len(args) == 2 {
|
|
||||||
_, ok2 = args[1].(bool)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("optional arguments must be int and bool for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One optional string, two optional ints
|
|
||||||
case "listtransactions":
|
|
||||||
if len(args) > 3 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
ok1 := true
|
|
||||||
ok2 := true
|
|
||||||
ok3 := true
|
|
||||||
if len(args) >= 1 {
|
|
||||||
_, ok1 = args[0].(string)
|
|
||||||
}
|
|
||||||
if len(args) > 1 {
|
|
||||||
_, ok2 = args[1].(int)
|
|
||||||
}
|
|
||||||
if len(args) == 3 {
|
|
||||||
_, ok3 = args[2].(int)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 || !ok3 {
|
|
||||||
err = fmt.Errorf("optional arguments must be string and up to two ints for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required string, one optional string, one optional bool
|
|
||||||
case "importprivkey":
|
|
||||||
if len(args) > 3 || len(args) == 0 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
ok2 := true
|
|
||||||
ok3 := true
|
|
||||||
if len(args) > 1 {
|
|
||||||
_, ok2 = args[1].(string)
|
|
||||||
}
|
|
||||||
if len(args) == 3 {
|
|
||||||
_, ok3 = args[2].(bool)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 || !ok3 {
|
|
||||||
err = fmt.Errorf("arguments must be string and optionally string and bool for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// Two optional ints
|
|
||||||
case "listunspent":
|
|
||||||
if len(args) > 2 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
ok1 := true
|
|
||||||
ok2 := true
|
|
||||||
if len(args) >= 1 {
|
|
||||||
_, ok1 = args[0].(int)
|
|
||||||
}
|
|
||||||
if len(args) == 2 {
|
|
||||||
_, ok2 = args[1].(int)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("optional arguments must be ints for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// Two optional strings
|
|
||||||
case "listsinceblock":
|
|
||||||
if len(args) > 2 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
ok1 := true
|
|
||||||
ok2 := true
|
|
||||||
if len(args) >= 1 {
|
|
||||||
_, ok1 = args[0].(string)
|
|
||||||
}
|
|
||||||
if len(args) == 2 {
|
|
||||||
_, ok2 = args[1].(string)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
err = fmt.Errorf("optional arguments must be strings for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
|
|
||||||
// Two required strings, one required float, one optional int,
|
|
||||||
// two optional strings.
|
|
||||||
case "sendfrom":
|
|
||||||
if len(args) > 6 || len(args) < 3 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
_, ok2 := args[1].(string)
|
|
||||||
_, ok3 := args[2].(float64)
|
|
||||||
ok4 := true
|
|
||||||
ok5 := true
|
|
||||||
ok6 := true
|
|
||||||
if len(args) >= 4 {
|
|
||||||
_, ok4 = args[3].(int)
|
|
||||||
}
|
|
||||||
if len(args) >= 5 {
|
|
||||||
_, ok5 = args[4].(string)
|
|
||||||
}
|
|
||||||
if len(args) == 6 {
|
|
||||||
_, ok6 = args[5].(string)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 || !ok6 {
|
|
||||||
err = fmt.Errorf("arguments must be string, string, float64 and optionally int and two strings for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// Two required strings, one required float, one optional int,
|
|
||||||
// one optional string.
|
|
||||||
case "move":
|
|
||||||
if len(args) > 5 || len(args) < 3 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
_, ok2 := args[1].(string)
|
|
||||||
_, ok3 := args[2].(float64)
|
|
||||||
ok4 := true
|
|
||||||
ok5 := true
|
|
||||||
if len(args) >= 4 {
|
|
||||||
_, ok4 = args[3].(int)
|
|
||||||
}
|
|
||||||
if len(args) == 5 {
|
|
||||||
_, ok5 = args[4].(string)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 {
|
|
||||||
err = fmt.Errorf("arguments must be string, string, float64 and optionally int and string for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// One required strings, one required float, two optional strings
|
|
||||||
case "sendtoaddress":
|
|
||||||
if len(args) > 4 || len(args) < 2 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
_, ok2 := args[1].(float64)
|
|
||||||
ok3 := true
|
|
||||||
ok4 := true
|
|
||||||
if len(args) >= 3 {
|
|
||||||
_, ok3 = args[2].(string)
|
|
||||||
}
|
|
||||||
if len(args) == 4 {
|
|
||||||
_, ok4 = args[3].(string)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 || !ok3 || !ok4 {
|
|
||||||
err = fmt.Errorf("arguments must be string, float64 and optionally two strings for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// required int, required pair of keys (string), optional string
|
|
||||||
case "addmultisignaddress":
|
|
||||||
if len(args) > 4 || len(args) < 3 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(int)
|
|
||||||
_, ok2 := args[1].(string)
|
|
||||||
_, ok3 := args[2].(string)
|
|
||||||
ok4 := true
|
|
||||||
if len(args) == 4 {
|
|
||||||
_, ok4 = args[2].(string)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 || !ok3 || !ok4 {
|
|
||||||
err = fmt.Errorf("arguments must be int, two string and optionally one for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// Must be a set of string, int, string, float (any number of those).
|
|
||||||
case "createrawtransaction":
|
|
||||||
if len(args)%4 != 0 || len(args) == 0 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
type vlist struct {
|
|
||||||
Txid string `json:"txid"`
|
|
||||||
Vout uint32 `json:"vout"`
|
|
||||||
}
|
|
||||||
vList := make([]vlist, len(args)/4)
|
|
||||||
addresses := make(map[string]float64)
|
|
||||||
for i := 0; i < len(args)/4; i++ {
|
|
||||||
txid, ok1 := args[(i*4)+0].(string)
|
|
||||||
vout, ok2 := args[(i*4)+1].(uint32)
|
|
||||||
add, ok3 := args[(i*4)+2].(string)
|
|
||||||
amt, ok4 := args[(i*4)+3].(float64)
|
|
||||||
if !ok1 || !ok2 || !ok3 || !ok4 {
|
|
||||||
return finalMessage, ErrIncorrectArgTypes
|
|
||||||
}
|
|
||||||
vList[i].Txid = txid
|
|
||||||
vList[i].Vout = vout
|
|
||||||
addresses[add] = amt
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, []interface{}{vList, addresses})
|
|
||||||
// string, string/float pairs, optional int, and string
|
|
||||||
case "sendmany":
|
|
||||||
if len(args) < 3 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
var minconf int
|
|
||||||
var comment string
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
if !ok1 {
|
|
||||||
return finalMessage, ErrIncorrectArgTypes
|
|
||||||
}
|
|
||||||
addresses := make(map[string]float64)
|
|
||||||
for i := 1; i < len(args); i += 2 {
|
|
||||||
add, ok1 := args[i].(string)
|
|
||||||
if ok1 {
|
|
||||||
if len(args) > i+1 {
|
|
||||||
amt, ok2 := args[i+1].(float64)
|
|
||||||
if !ok2 {
|
|
||||||
return finalMessage, ErrIncorrectArgTypes
|
|
||||||
}
|
|
||||||
// Put a single pair into addresses
|
|
||||||
addresses[add] = amt
|
|
||||||
} else {
|
|
||||||
comment = add
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, ok := args[i].(int); ok {
|
|
||||||
minconf = args[i].(int)
|
|
||||||
}
|
|
||||||
if len(args)-1 > i {
|
|
||||||
if _, ok := args[i+1].(string); ok {
|
|
||||||
comment = args[i+1].(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, []interface{}{args[0].(string), addresses, minconf, comment})
|
|
||||||
// bool and an array of stuff
|
|
||||||
case "lockunspent":
|
|
||||||
if len(args) < 2 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(bool)
|
|
||||||
if !ok1 {
|
|
||||||
return finalMessage, ErrIncorrectArgTypes
|
|
||||||
}
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, args)
|
|
||||||
// one required string (hex) and optional sets of one string, one int,
|
|
||||||
// and one string along with another optional string.
|
|
||||||
case "signrawtransaction":
|
|
||||||
if len(args) < 1 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
if !ok1 {
|
|
||||||
return finalMessage, ErrIncorrectArgTypes
|
|
||||||
}
|
|
||||||
type txlist struct {
|
|
||||||
Txid string `json:"txid"`
|
|
||||||
Vout uint32 `json:"vout"`
|
|
||||||
ScriptPubKey string `json:"scriptPubKey"`
|
|
||||||
}
|
|
||||||
txList := make([]txlist, 1)
|
|
||||||
|
|
||||||
if len(args) > 1 {
|
|
||||||
txid, ok2 := args[1].(string)
|
|
||||||
vout, ok3 := args[2].(uint32)
|
|
||||||
spkey, ok4 := args[3].(string)
|
|
||||||
if !ok1 || !ok2 || !ok3 || !ok4 {
|
|
||||||
return finalMessage, ErrIncorrectArgTypes
|
|
||||||
}
|
|
||||||
txList[0].Txid = txid
|
|
||||||
txList[0].Vout = vout
|
|
||||||
txList[0].ScriptPubKey = spkey
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
pkeyList := make([]string, (len(args)-1)/4)
|
|
||||||
for i := 0; i < len(args)/4; i += 1 {
|
|
||||||
fmt.Println(args[(i*4)+4])
|
|
||||||
txid, ok1 := args[(i*4)+1].(string)
|
|
||||||
vout, ok2 := args[(i*4)+2].(int)
|
|
||||||
spkey, ok3 := args[(i*4)+3].(string)
|
|
||||||
pkey, ok4 := args[(i*4)+4].(string)
|
|
||||||
if !ok1 || !ok2 || !ok3 || !ok4 {
|
|
||||||
return finalMessage, ErrIncorrectArgTypes
|
|
||||||
}
|
|
||||||
txList[i].Txid = txid
|
|
||||||
txList[i].Vout = vout
|
|
||||||
txList[i].ScriptPubKey = spkey
|
|
||||||
pkeyList[i] = pkey
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
finalMessage, err = jsonWithArgs(message, id, []interface{}{args[0].(string), txList})
|
|
||||||
// one required string (address), one optional bool, two optional ints.
|
|
||||||
case "searchrawtransactions":
|
|
||||||
if len(args) > 4 || len(args) == 0 {
|
|
||||||
err = fmt.Errorf("wrong number of arguments for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
_, ok1 := args[0].(string)
|
|
||||||
ok2 := true
|
|
||||||
ok3 := true
|
|
||||||
ok4 := true
|
|
||||||
if len(args) >= 2 {
|
|
||||||
_, ok2 = args[1].(int)
|
|
||||||
}
|
|
||||||
if len(args) >= 3 {
|
|
||||||
_, ok3 = args[2].(int)
|
|
||||||
}
|
|
||||||
if len(args) == 4 {
|
|
||||||
_, ok4 = args[3].(int)
|
|
||||||
}
|
|
||||||
if !ok1 || !ok2 || !ok3 || !ok4 {
|
|
||||||
err = fmt.Errorf("arguments must be string, one optional "+
|
|
||||||
"bool, and two optional ints for %s", message)
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
// Any other message
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("not a valid command: %s", message)
|
|
||||||
}
|
|
||||||
return finalMessage, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONGetMethod takes a message and tries to find the bitcoin command that it
|
|
||||||
// is in reply to so it can be processed further.
|
|
||||||
func JSONGetMethod(message []byte) (string, error) {
|
|
||||||
var obj struct {
|
|
||||||
Method string `json:"method"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(message, &obj)
|
|
||||||
return obj.Method, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TlsRpcCommand takes a message generated from one of the routines above
|
|
||||||
// along with the login/server information and any relavent PEM encoded
|
|
||||||
// certificates chains. It sends the command via https and returns a go struct
|
|
||||||
// with the result.
|
|
||||||
func TlsRpcCommand(user string, password string, server string, message []byte,
|
|
||||||
certificates []byte, skipverify bool) (Reply, error) {
|
|
||||||
return rpcCommand(user, password, server, message, true, certificates,
|
|
||||||
skipverify)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RpcCommand takes a message generated from one of the routines above
|
|
||||||
// along with the login/server info, sends it, and gets a reply, returning
|
|
||||||
// a go struct with the result.
|
|
||||||
func RpcCommand(user string, password string, server string, message []byte) (Reply, error) {
|
|
||||||
return rpcCommand(user, password, server, message, false, nil, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rpcCommand(user string, password string, server string, message []byte,
|
|
||||||
https bool, certificates []byte, skipverify bool) (Reply, error) {
|
|
||||||
var result Reply
|
|
||||||
method, err := JSONGetMethod(message)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
body, err := rpcRawCommand(user, password, server, message, https,
|
|
||||||
certificates, skipverify)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("error getting json reply: %v", err)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
result, err = ReadResultCmd(method, body)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("error reading json message: %v", err)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TlsRpcRawCommand takes a message generated from one of the routines above
|
|
||||||
// along with the login,server info and PEM encoded certificate chains for the
|
|
||||||
// server sends it, and gets a reply, returning
|
|
||||||
// the raw []byte response for use with ReadResultCmd.
|
|
||||||
func TlsRpcRawCommand(user string, password string, server string,
|
|
||||||
message []byte, certificates []byte, skipverify bool) ([]byte, error) {
|
|
||||||
return rpcRawCommand(user, password, server, message, true,
|
|
||||||
certificates, skipverify)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RpcRawCommand takes a message generated from one of the routines above
|
|
||||||
// along with the login/server info, sends it, and gets a reply, returning
|
|
||||||
// the raw []byte response for use with ReadResultCmd.
|
|
||||||
func RpcRawCommand(user string, password string, server string, message []byte) ([]byte, error) {
|
|
||||||
return rpcRawCommand(user, password, server, message, false, nil, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// rpcRawCommand is a helper function for the above two functions.
|
|
||||||
func rpcRawCommand(user string, password string, server string,
|
|
||||||
message []byte, https bool, certificates []byte, skipverify bool) ([]byte, error) {
|
|
||||||
var result []byte
|
|
||||||
var msg interface{}
|
|
||||||
err := json.Unmarshal(message, &msg)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("error, message does not appear to be valid json: %v", err)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
resp, err := jsonRPCSend(user, password, server, message, https,
|
|
||||||
certificates, skipverify)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("error sending json message: " + err.Error())
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, BadStatusCode(resp.StatusCode)
|
|
||||||
}
|
|
||||||
result, err = GetRaw(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("error getting json reply: %v", err)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RpcSend sends the passed command to the provided server using the provided
|
|
||||||
// authentication details, waits for a reply, and returns a Go struct with the
|
|
||||||
// result.
|
|
||||||
func RpcSend(user string, password string, server string, cmd Cmd) (Reply, error) {
|
|
||||||
msg, err := cmd.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
return Reply{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return RpcCommand(user, password, server, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TlsRpcSend sends the passed command to the provided server using the provided
|
|
||||||
// authentication details and PEM encoded certificate chain, waits for a reply,
|
|
||||||
// and returns a Go struct with the result.
|
|
||||||
func TlsRpcSend(user string, password string, server string, cmd Cmd,
|
|
||||||
certificates []byte, skipVerify bool) (Reply, error) {
|
|
||||||
|
|
||||||
msg, err := cmd.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
return Reply{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return TlsRpcCommand(user, password, server, msg, certificates,
|
|
||||||
skipVerify)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValidIdType checks that the Id field (which can go in any of the json
|
|
||||||
// messages) is valid. json rpc 1.0 allows any (json) type, but we still need
|
|
||||||
// to prevent values that cannot be marshalled from going in. json rpc 2.0
|
|
||||||
// (which bitcoind follows for some parts) only allows string, number, or null,
|
|
||||||
// so we restrict to that list. Ths is only necessary if you manually marshal
|
|
||||||
// a message. The normal btcjson functions only use string ids.
|
|
||||||
func IsValidIdType(id interface{}) bool {
|
|
||||||
switch id.(type) {
|
|
||||||
case int, int8, int16, int32, int64,
|
|
||||||
uint, uint8, uint16, uint32, uint64,
|
|
||||||
float32, float64,
|
|
||||||
string,
|
|
||||||
nil:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONToAmount Safely converts a floating point value to an int.
|
|
||||||
// Clearly not all floating point numbers can be converted to ints (there
|
|
||||||
// is no one-to-one mapping), but bitcoin's json api returns most numbers as
|
|
||||||
// floats which are not safe to use when handling money. Since bitcoins can
|
|
||||||
// only be divided in a limited way, this methods works for the amounts returned
|
|
||||||
// by the json api. It is not for general use.
|
|
||||||
// This follows the method described at:
|
|
||||||
// https://en.bitcoin.it/wiki/Proper_Money_Handling_%28JSON-RPC%29
|
|
||||||
func JSONToAmount(jsonAmount float64) (int64, error) {
|
|
||||||
var amount int64
|
|
||||||
var err error
|
|
||||||
if jsonAmount > 1.797693134862315708145274237317043567981e+300 {
|
|
||||||
err := fmt.Errorf("error %v is too large to convert", jsonAmount)
|
|
||||||
return amount, err
|
|
||||||
}
|
|
||||||
if jsonAmount < -1.797693134862315708145274237317043567981e+300 {
|
|
||||||
err := fmt.Errorf("error %v is too small to convert", jsonAmount)
|
|
||||||
return amount, err
|
|
||||||
}
|
|
||||||
tempVal := 1e8 * jsonAmount
|
|
||||||
// So we round properly. float won't == 0 and if it did, that
|
|
||||||
// would be converted fine anyway.
|
|
||||||
if tempVal < 0 {
|
|
||||||
tempVal = tempVal - 0.5
|
|
||||||
}
|
|
||||||
if tempVal > 0 {
|
|
||||||
tempVal = tempVal + 0.5
|
|
||||||
}
|
|
||||||
// Then just rely on the integer truncating
|
|
||||||
amount = int64(tempVal)
|
|
||||||
return amount, err
|
|
||||||
}
|
|
|
@ -1,395 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
// cmdtests is a table of all the possible commands and a list of inputs,
|
|
||||||
// some of which should work, some of which should not (indicated by the
|
|
||||||
// pass variable). This mainly checks the type and number of the arguments,
|
|
||||||
// it does not actually check to make sure the values are correct (i.e., that
|
|
||||||
// addresses are reasonable) as the bitcoin client must be able to deal with
|
|
||||||
// that.
|
|
||||||
var cmdtests = []struct {
|
|
||||||
cmd string
|
|
||||||
args []interface{}
|
|
||||||
pass bool
|
|
||||||
}{
|
|
||||||
{"createmultisig", nil, false},
|
|
||||||
{"createmultisig", []interface{}{1}, false},
|
|
||||||
{"getinfo", nil, true},
|
|
||||||
{"getinfo", []interface{}{1}, false},
|
|
||||||
{"listaccounts", nil, true},
|
|
||||||
{"listaccounts", []interface{}{1}, true},
|
|
||||||
{"listaccounts", []interface{}{"test"}, false},
|
|
||||||
{"listaccounts", []interface{}{1, 2}, false},
|
|
||||||
{"estimatefee", nil, false},
|
|
||||||
{"estimatefee", []interface{}{1}, true},
|
|
||||||
{"estimatefee", []interface{}{1, 2}, false},
|
|
||||||
{"estimatefee", []interface{}{1.3}, false},
|
|
||||||
{"estimatepriority", nil, false},
|
|
||||||
{"estimatepriority", []interface{}{1}, true},
|
|
||||||
{"estimatepriority", []interface{}{1, 2}, false},
|
|
||||||
{"estimatepriority", []interface{}{1.3}, false},
|
|
||||||
{"getblockhash", nil, false},
|
|
||||||
{"getblockhash", []interface{}{1}, true},
|
|
||||||
{"getblockhash", []interface{}{1, 2}, false},
|
|
||||||
{"getblockhash", []interface{}{1.1}, false},
|
|
||||||
{"settxfee", nil, false},
|
|
||||||
{"settxfee", []interface{}{1.0}, true},
|
|
||||||
{"settxfee", []interface{}{1.0, 2.0}, false},
|
|
||||||
{"settxfee", []interface{}{1}, false},
|
|
||||||
{"getmemorypool", nil, true},
|
|
||||||
{"getmemorypool", []interface{}{"test"}, true},
|
|
||||||
{"getmemorypool", []interface{}{1}, false},
|
|
||||||
{"getmemorypool", []interface{}{"test", 2}, false},
|
|
||||||
{"backupwallet", nil, false},
|
|
||||||
{"backupwallet", []interface{}{1, 2}, false},
|
|
||||||
{"backupwallet", []interface{}{1}, false},
|
|
||||||
{"backupwallet", []interface{}{"testpath"}, true},
|
|
||||||
{"invalidateblock", nil, false},
|
|
||||||
{"invalidateblock", []interface{}{1, 2}, false},
|
|
||||||
{"invalidateblock", []interface{}{1}, false},
|
|
||||||
{"invalidateblock", []interface{}{"testhash"}, true},
|
|
||||||
{"reconsiderblock", nil, false},
|
|
||||||
{"reconsiderblock", []interface{}{1, 2}, false},
|
|
||||||
{"reconsiderblock", []interface{}{1}, false},
|
|
||||||
{"reconsiderblock", []interface{}{"testhash"}, true},
|
|
||||||
{"setaccount", nil, false},
|
|
||||||
{"setaccount", []interface{}{1}, false},
|
|
||||||
{"setaccount", []interface{}{1, 2, 3}, false},
|
|
||||||
{"setaccount", []interface{}{1, "test"}, false},
|
|
||||||
{"setaccount", []interface{}{"test", "test"}, true},
|
|
||||||
{"verifymessage", nil, false},
|
|
||||||
{"verifymessage", []interface{}{1}, false},
|
|
||||||
{"verifymessage", []interface{}{1, 2}, false},
|
|
||||||
{"verifymessage", []interface{}{1, 2, 3, 4}, false},
|
|
||||||
{"verifymessage", []interface{}{"test", "test", "test"}, true},
|
|
||||||
{"verifymessage", []interface{}{"test", "test", 1}, false},
|
|
||||||
{"getaddednodeinfo", nil, false},
|
|
||||||
{"getaddednodeinfo", []interface{}{1}, false},
|
|
||||||
{"getaddednodeinfo", []interface{}{true}, true},
|
|
||||||
{"getaddednodeinfo", []interface{}{true, 1}, false},
|
|
||||||
{"getaddednodeinfo", []interface{}{true, "test"}, true},
|
|
||||||
{"setgenerate", nil, false},
|
|
||||||
{"setgenerate", []interface{}{1, 2, 3}, false},
|
|
||||||
{"setgenerate", []interface{}{true}, true},
|
|
||||||
{"setgenerate", []interface{}{true, 1}, true},
|
|
||||||
{"setgenerate", []interface{}{true, 1.1}, false},
|
|
||||||
{"setgenerate", []interface{}{"true", 1}, false},
|
|
||||||
{"getbalance", nil, true},
|
|
||||||
{"getbalance", []interface{}{"test"}, true},
|
|
||||||
{"getbalance", []interface{}{"test", 1}, true},
|
|
||||||
{"getbalance", []interface{}{"test", 1.0}, false},
|
|
||||||
{"getbalance", []interface{}{1, 1}, false},
|
|
||||||
{"getbalance", []interface{}{"test", 1, 2}, false},
|
|
||||||
{"getbalance", []interface{}{1}, false},
|
|
||||||
{"addnode", nil, false},
|
|
||||||
{"addnode", []interface{}{1, 2, 3}, false},
|
|
||||||
{"addnode", []interface{}{"test", "test"}, true},
|
|
||||||
{"addnode", []interface{}{1}, false},
|
|
||||||
{"addnode", []interface{}{"test", 1}, false},
|
|
||||||
{"addnode", []interface{}{"test", 1.0}, false},
|
|
||||||
{"listreceivedbyaccount", nil, true},
|
|
||||||
{"listreceivedbyaccount", []interface{}{1, 2, 3}, false},
|
|
||||||
{"listreceivedbyaccount", []interface{}{1}, true},
|
|
||||||
{"listreceivedbyaccount", []interface{}{1.0}, false},
|
|
||||||
{"listreceivedbyaccount", []interface{}{1, false}, true},
|
|
||||||
{"listreceivedbyaccount", []interface{}{1, "false"}, false},
|
|
||||||
{"listtransactions", nil, true},
|
|
||||||
{"listtransactions", []interface{}{"test"}, true},
|
|
||||||
{"listtransactions", []interface{}{"test", 1}, true},
|
|
||||||
{"listtransactions", []interface{}{"test", 1, 2}, true},
|
|
||||||
{"listtransactions", []interface{}{"test", 1, 2, 3}, false},
|
|
||||||
{"listtransactions", []interface{}{1}, false},
|
|
||||||
{"listtransactions", []interface{}{"test", 1.0}, false},
|
|
||||||
{"listtransactions", []interface{}{"test", 1, "test"}, false},
|
|
||||||
{"importprivkey", nil, false},
|
|
||||||
{"importprivkey", []interface{}{"test"}, true},
|
|
||||||
{"importprivkey", []interface{}{1}, false},
|
|
||||||
{"importprivkey", []interface{}{"test", "test"}, true},
|
|
||||||
{"importprivkey", []interface{}{"test", "test", true}, true},
|
|
||||||
{"importprivkey", []interface{}{"test", "test", true, 1}, false},
|
|
||||||
{"importprivkey", []interface{}{"test", 1.0, true}, false},
|
|
||||||
{"importprivkey", []interface{}{"test", "test", "true"}, false},
|
|
||||||
{"listunspent", nil, true},
|
|
||||||
{"listunspent", []interface{}{1}, true},
|
|
||||||
{"listunspent", []interface{}{1, 2}, true},
|
|
||||||
{"listunspent", []interface{}{1, 2, 3}, false},
|
|
||||||
{"listunspent", []interface{}{1.0}, false},
|
|
||||||
{"listunspent", []interface{}{1, 2.0}, false},
|
|
||||||
{"sendfrom", nil, false},
|
|
||||||
{"sendfrom", []interface{}{"test"}, false},
|
|
||||||
{"sendfrom", []interface{}{"test", "test"}, false},
|
|
||||||
{"sendfrom", []interface{}{"test", "test", 1.0}, true},
|
|
||||||
{"sendfrom", []interface{}{"test", 1, 1.0}, false},
|
|
||||||
{"sendfrom", []interface{}{1, "test", 1.0}, false},
|
|
||||||
{"sendfrom", []interface{}{"test", "test", 1}, false},
|
|
||||||
{"sendfrom", []interface{}{"test", "test", 1.0, 1}, true},
|
|
||||||
{"sendfrom", []interface{}{"test", "test", 1.0, 1, "test"}, true},
|
|
||||||
{"sendfrom", []interface{}{"test", "test", 1.0, 1, "test", "test"}, true},
|
|
||||||
{"move", nil, false},
|
|
||||||
{"move", []interface{}{1, 2, 3, 4, 5, 6}, false},
|
|
||||||
{"move", []interface{}{1, 2}, false},
|
|
||||||
{"move", []interface{}{"test", "test", 1.0}, true},
|
|
||||||
{"move", []interface{}{"test", "test", 1.0, 1, "test"}, true},
|
|
||||||
{"move", []interface{}{"test", "test", 1.0, 1}, true},
|
|
||||||
{"move", []interface{}{1, "test", 1.0}, false},
|
|
||||||
{"move", []interface{}{"test", 1, 1.0}, false},
|
|
||||||
{"move", []interface{}{"test", "test", 1}, false},
|
|
||||||
{"move", []interface{}{"test", "test", 1.0, 1.0, "test"}, false},
|
|
||||||
{"move", []interface{}{"test", "test", 1.0, 1, true}, false},
|
|
||||||
{"sendtoaddress", nil, false},
|
|
||||||
{"sendtoaddress", []interface{}{"test"}, false},
|
|
||||||
{"sendtoaddress", []interface{}{"test", 1.0}, true},
|
|
||||||
{"sendtoaddress", []interface{}{"test", 1.0, "test"}, true},
|
|
||||||
{"sendtoaddress", []interface{}{"test", 1.0, "test", "test"}, true},
|
|
||||||
{"sendtoaddress", []interface{}{1, 1.0, "test", "test"}, false},
|
|
||||||
{"sendtoaddress", []interface{}{"test", 1, "test", "test"}, false},
|
|
||||||
{"sendtoaddress", []interface{}{"test", 1.0, 1.0, "test"}, false},
|
|
||||||
{"sendtoaddress", []interface{}{"test", 1.0, "test", 1.0}, false},
|
|
||||||
{"sendtoaddress", []interface{}{"test", 1.0, "test", "test", 1}, false},
|
|
||||||
{"addmultisignaddress", []interface{}{1, "test", "test"}, true},
|
|
||||||
{"addmultisignaddress", []interface{}{1, "test"}, false},
|
|
||||||
{"addmultisignaddress", []interface{}{1, 1.0, "test"}, false},
|
|
||||||
{"addmultisignaddress", []interface{}{1, "test", "test", "test"}, true},
|
|
||||||
{"createrawtransaction", []interface{}{"in1", uint32(0), "a1", 1.0}, true},
|
|
||||||
{"createrawtransaction", []interface{}{"in1", "out1", "a1", 1.0, "test"}, false},
|
|
||||||
{"createrawtransaction", []interface{}{}, false},
|
|
||||||
{"createrawtransaction", []interface{}{"in1", 1.0, "a1", 1.0}, false},
|
|
||||||
{"sendmany", []interface{}{"in1", "out1", 1.0, 1, "comment"}, true},
|
|
||||||
{"sendmany", []interface{}{"in1", "out1", 1.0, "comment"}, true},
|
|
||||||
{"sendmany", []interface{}{"in1", "out1"}, false},
|
|
||||||
{"sendmany", []interface{}{true, "out1", 1.0, 1, "comment"}, false},
|
|
||||||
{"sendmany", []interface{}{"in1", "out1", "test", 1, "comment"}, false},
|
|
||||||
{"lockunspent", []interface{}{true, "something"}, true},
|
|
||||||
{"lockunspent", []interface{}{true}, false},
|
|
||||||
{"lockunspent", []interface{}{1.0, "something"}, false},
|
|
||||||
{"signrawtransaction", []interface{}{"hexstring", "test", uint32(1), "test"}, true},
|
|
||||||
{"signrawtransaction", []interface{}{"hexstring", "test", "test2", "test3", "test4"}, false},
|
|
||||||
{"signrawtransaction", []interface{}{"hexstring", "test", "test2", "test3"}, false},
|
|
||||||
{"signrawtransaction", []interface{}{1.2, "test", "test2", "test3", "test4"}, false},
|
|
||||||
{"signrawtransaction", []interface{}{"hexstring", 1, "test2", "test3", "test4"}, false},
|
|
||||||
{"signrawtransaction", []interface{}{"hexstring", "test", "test2", 3, "test4"}, false},
|
|
||||||
{"searchrawtransactions", []interface{}{"someaddr"}, true},
|
|
||||||
{"searchrawtransactions", []interface{}{"someaddr", 1}, true},
|
|
||||||
{"searchrawtransactions", []interface{}{"someaddr", 0, 1}, true},
|
|
||||||
{"searchrawtransactions", []interface{}{"someaddr", 1, 5, 500}, true},
|
|
||||||
{"searchrawtransactions", []interface{}{"someaddr", 1, 5, "test"}, false},
|
|
||||||
{"searchrawtransactions", []interface{}{}, false},
|
|
||||||
{"listsinceblock", []interface{}{"test", "test"}, true},
|
|
||||||
{"listsinceblock", []interface{}{"test", "test", "test"}, false},
|
|
||||||
{"listsinceblock", []interface{}{"test"}, true},
|
|
||||||
{"listsinceblock", []interface{}{}, true},
|
|
||||||
{"listsinceblock", []interface{}{1, "test"}, false},
|
|
||||||
{"walletpassphrase", []interface{}{"test", 1}, true},
|
|
||||||
{"walletpassphrase", []interface{}{"test"}, false},
|
|
||||||
{"walletpassphrase", []interface{}{"test", "test"}, false},
|
|
||||||
{"getrawchangeaddress", []interface{}{}, true},
|
|
||||||
{"getrawchangeaddress", []interface{}{"something"}, true},
|
|
||||||
{"getrawchangeaddress", []interface{}{"something", "test"}, false},
|
|
||||||
{"getbestblockhash", []interface{}{}, true},
|
|
||||||
{"getbestblockhash", []interface{}{"something"}, false},
|
|
||||||
{"getblockchaininfo", []interface{}{}, true},
|
|
||||||
{"getblockchaininfo", []interface{}{"something"}, false},
|
|
||||||
{"getnetworkinfo", []interface{}{}, true},
|
|
||||||
{"getnetworkinfo", []interface{}{"something"}, false},
|
|
||||||
{"submitblock", []interface{}{}, false},
|
|
||||||
{"submitblock", []interface{}{"something"}, true},
|
|
||||||
{"submitblock", []interface{}{"something", "something else"}, true},
|
|
||||||
{"submitblock", []interface{}{"something", "something else", "more"}, false},
|
|
||||||
{"submitblock", []interface{}{"something", 1}, false},
|
|
||||||
{"fakecommand", nil, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRpcCreateMessage tests CreateMessage using the table of messages
|
|
||||||
// in cmdtests.
|
|
||||||
func TestRpcCreateMessage(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
for i, tt := range cmdtests {
|
|
||||||
if tt.args == nil {
|
|
||||||
_, err = btcjson.CreateMessage(tt.cmd)
|
|
||||||
} else {
|
|
||||||
_, err = btcjson.CreateMessage(tt.cmd, tt.args...)
|
|
||||||
}
|
|
||||||
if tt.pass {
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Could not create command %d: %s %v.", i, tt.cmd, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should create command. %d: %s", i, tt.cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRpcCommand tests RpcCommand by generating some commands and
|
|
||||||
// trying to send them off.
|
|
||||||
func TestRpcCommand(t *testing.T) {
|
|
||||||
user := "something"
|
|
||||||
pass := "something"
|
|
||||||
server := "invalid"
|
|
||||||
var msg []byte
|
|
||||||
_, err := btcjson.RpcCommand(user, pass, server, msg)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should fail.")
|
|
||||||
}
|
|
||||||
msg, err = btcjson.CreateMessage("getinfo")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Cannot create valid json message")
|
|
||||||
}
|
|
||||||
_, err = btcjson.RpcCommand(user, pass, server, msg)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should not connect to server.")
|
|
||||||
}
|
|
||||||
|
|
||||||
badMsg := []byte("{\"jsonrpc\":\"1.0\",\"id\":\"btcd\",\"method\":\"\"}")
|
|
||||||
_, err = btcjson.RpcCommand(user, pass, server, badMsg)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Cannot have no method in msg..")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FailingReadClose is a type used for testing so we can get something that
|
|
||||||
// fails past Go's type system.
|
|
||||||
type FailingReadCloser struct{}
|
|
||||||
|
|
||||||
func (f *FailingReadCloser) Close() error {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FailingReadCloser) Read(p []byte) (n int, err error) {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRpcReply tests JsonGetRaw by sending both a good and a bad buffer
|
|
||||||
// to it.
|
|
||||||
func TestRpcReply(t *testing.T) {
|
|
||||||
buffer := new(bytes.Buffer)
|
|
||||||
buffer2 := ioutil.NopCloser(buffer)
|
|
||||||
_, err := btcjson.GetRaw(buffer2)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error reading rpc reply.")
|
|
||||||
}
|
|
||||||
failBuf := &FailingReadCloser{}
|
|
||||||
_, err = btcjson.GetRaw(failBuf)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Error, this should fail.")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var idtests = []struct {
|
|
||||||
testID []interface{}
|
|
||||||
pass bool
|
|
||||||
}{
|
|
||||||
{[]interface{}{"string test"}, true},
|
|
||||||
{[]interface{}{1}, true},
|
|
||||||
{[]interface{}{1.0}, true},
|
|
||||||
{[]interface{}{nil}, true},
|
|
||||||
{[]interface{}{make(chan int)}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestIsValidIdType tests that IsValidIdType allows (and disallows the correct
|
|
||||||
// types).
|
|
||||||
func TestIsValidIdType(t *testing.T) {
|
|
||||||
for _, tt := range idtests {
|
|
||||||
res := btcjson.IsValidIdType(tt.testID[0])
|
|
||||||
if res != tt.pass {
|
|
||||||
t.Errorf("Incorrect type result %v.", tt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var floattests = []struct {
|
|
||||||
in float64
|
|
||||||
out int64
|
|
||||||
pass bool
|
|
||||||
}{
|
|
||||||
{1.0, 100000000, true},
|
|
||||||
{-1.0, -100000000, true},
|
|
||||||
{0.0, 0, true},
|
|
||||||
{0.00000001, 1, true},
|
|
||||||
{-0.00000001, -1, true},
|
|
||||||
{-1.0e307, 0, false},
|
|
||||||
{1.0e307, 0, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestJSONtoAmount tests that JSONtoAmount returns the proper values.
|
|
||||||
func TestJSONtoAmount(t *testing.T) {
|
|
||||||
for _, tt := range floattests {
|
|
||||||
res, err := btcjson.JSONToAmount(tt.in)
|
|
||||||
if tt.pass {
|
|
||||||
if res != tt.out || err != nil {
|
|
||||||
t.Errorf("Should not fail: %v", tt.in)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should not pass: %v", tt.in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestErrorInterface tests that the Error type satisifies the builtin
|
|
||||||
// error interface and tests that the error string is created in the form
|
|
||||||
// "code: message".
|
|
||||||
func TestErrorInterface(t *testing.T) {
|
|
||||||
codes := []int{
|
|
||||||
-1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
}
|
|
||||||
messages := []string{
|
|
||||||
"parse error",
|
|
||||||
"error getting field",
|
|
||||||
"method not found",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an Error and check that both Error and *Error can be used
|
|
||||||
// as an error.
|
|
||||||
var jsonError btcjson.Error
|
|
||||||
var iface interface{} = jsonError
|
|
||||||
var ifacep interface{} = &jsonError
|
|
||||||
if _, ok := iface.(error); !ok {
|
|
||||||
t.Error("cannot type assert Error as error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, ok := ifacep.(error); !ok {
|
|
||||||
t.Error("cannot type assert *Error as error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify jsonError is converted to the expected string using a few
|
|
||||||
// combinations of codes and messages.
|
|
||||||
for _, code := range codes {
|
|
||||||
for _, message := range messages {
|
|
||||||
// Create Error
|
|
||||||
jsonError := btcjson.Error{
|
|
||||||
Code: code,
|
|
||||||
Message: message,
|
|
||||||
}
|
|
||||||
|
|
||||||
exp := fmt.Sprintf("%d: %s", jsonError.Code, jsonError.Message)
|
|
||||||
res := fmt.Sprintf("%v", jsonError)
|
|
||||||
if exp != res {
|
|
||||||
t.Errorf("error string '%s' differs from expected '%v'", res, exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
7283
btcjson/jsoncmd.go
7283
btcjson/jsoncmd.go
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,175 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson
|
|
||||||
|
|
||||||
// Standard JSON-RPC 2.0 errors
|
|
||||||
var (
|
|
||||||
ErrInvalidRequest = Error{
|
|
||||||
Code: -32600,
|
|
||||||
Message: "Invalid request",
|
|
||||||
}
|
|
||||||
ErrMethodNotFound = Error{
|
|
||||||
Code: -32601,
|
|
||||||
Message: "Method not found",
|
|
||||||
}
|
|
||||||
ErrInvalidParams = Error{
|
|
||||||
Code: -32602,
|
|
||||||
Message: "Invalid paramaters",
|
|
||||||
}
|
|
||||||
ErrInternal = Error{
|
|
||||||
Code: -32603,
|
|
||||||
Message: "Internal error",
|
|
||||||
}
|
|
||||||
ErrParse = Error{
|
|
||||||
Code: -32700,
|
|
||||||
Message: "Parse error",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// General application defined JSON errors
|
|
||||||
var (
|
|
||||||
ErrMisc = Error{
|
|
||||||
Code: -1,
|
|
||||||
Message: "Miscellaneous error",
|
|
||||||
}
|
|
||||||
ErrForbiddenBySafeMode = Error{
|
|
||||||
Code: -2,
|
|
||||||
Message: "Server is in safe mode, and command is not allowed in safe mode",
|
|
||||||
}
|
|
||||||
ErrType = Error{
|
|
||||||
Code: -3,
|
|
||||||
Message: "Unexpected type was passed as parameter",
|
|
||||||
}
|
|
||||||
ErrInvalidAddressOrKey = Error{
|
|
||||||
Code: -5,
|
|
||||||
Message: "Invalid address or key",
|
|
||||||
}
|
|
||||||
ErrOutOfMemory = Error{
|
|
||||||
Code: -7,
|
|
||||||
Message: "Ran out of memory during operation",
|
|
||||||
}
|
|
||||||
ErrInvalidParameter = Error{
|
|
||||||
Code: -8,
|
|
||||||
Message: "Invalid, missing or duplicate parameter",
|
|
||||||
}
|
|
||||||
ErrDatabase = Error{
|
|
||||||
Code: -20,
|
|
||||||
Message: "Database error",
|
|
||||||
}
|
|
||||||
ErrDeserialization = Error{
|
|
||||||
Code: -22,
|
|
||||||
Message: "Error parsing or validating structure in raw format",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Peer-to-peer client errors
|
|
||||||
var (
|
|
||||||
ErrClientNotConnected = Error{
|
|
||||||
Code: -9,
|
|
||||||
Message: "Bitcoin is not connected",
|
|
||||||
}
|
|
||||||
ErrClientInInitialDownload = Error{
|
|
||||||
Code: -10,
|
|
||||||
Message: "Bitcoin is downloading blocks...",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wallet JSON errors
|
|
||||||
var (
|
|
||||||
ErrWallet = Error{
|
|
||||||
Code: -4,
|
|
||||||
Message: "Unspecified problem with wallet",
|
|
||||||
}
|
|
||||||
ErrWalletInsufficientFunds = Error{
|
|
||||||
Code: -6,
|
|
||||||
Message: "Not enough funds in wallet or account",
|
|
||||||
}
|
|
||||||
ErrWalletInvalidAccountName = Error{
|
|
||||||
Code: -11,
|
|
||||||
Message: "Invalid account name",
|
|
||||||
}
|
|
||||||
ErrWalletKeypoolRanOut = Error{
|
|
||||||
Code: -12,
|
|
||||||
Message: "Keypool ran out, call keypoolrefill first",
|
|
||||||
}
|
|
||||||
ErrWalletUnlockNeeded = Error{
|
|
||||||
Code: -13,
|
|
||||||
Message: "Enter the wallet passphrase with walletpassphrase first",
|
|
||||||
}
|
|
||||||
ErrWalletPassphraseIncorrect = Error{
|
|
||||||
Code: -14,
|
|
||||||
Message: "The wallet passphrase entered was incorrect",
|
|
||||||
}
|
|
||||||
ErrWalletWrongEncState = Error{
|
|
||||||
Code: -15,
|
|
||||||
Message: "Command given in wrong wallet encryption state",
|
|
||||||
}
|
|
||||||
ErrWalletEncryptionFailed = Error{
|
|
||||||
Code: -16,
|
|
||||||
Message: "Failed to encrypt the wallet",
|
|
||||||
}
|
|
||||||
ErrWalletAlreadyUnlocked = Error{
|
|
||||||
Code: -17,
|
|
||||||
Message: "Wallet is already unlocked",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Specific Errors related to commands. These are the ones a user of the rpc
|
|
||||||
// server are most likely to see. Generally, the codes should match one of the
|
|
||||||
// more general errors above.
|
|
||||||
var (
|
|
||||||
ErrBlockNotFound = Error{
|
|
||||||
Code: -5,
|
|
||||||
Message: "Block not found",
|
|
||||||
}
|
|
||||||
ErrBlockCount = Error{
|
|
||||||
Code: -5,
|
|
||||||
Message: "Error getting block count",
|
|
||||||
}
|
|
||||||
ErrBestBlockHash = Error{
|
|
||||||
Code: -5,
|
|
||||||
Message: "Error getting best block hash",
|
|
||||||
}
|
|
||||||
ErrDifficulty = Error{
|
|
||||||
Code: -5,
|
|
||||||
Message: "Error getting difficulty",
|
|
||||||
}
|
|
||||||
ErrOutOfRange = Error{
|
|
||||||
Code: -1,
|
|
||||||
Message: "Block number out of range",
|
|
||||||
}
|
|
||||||
ErrNoTxInfo = Error{
|
|
||||||
Code: -5,
|
|
||||||
Message: "No information available about transaction",
|
|
||||||
}
|
|
||||||
ErrNoNewestBlockInfo = Error{
|
|
||||||
Code: -5,
|
|
||||||
Message: "No information about newest block",
|
|
||||||
}
|
|
||||||
ErrInvalidTxVout = Error{
|
|
||||||
Code: -5,
|
|
||||||
Message: "Ouput index number (vout) does not exist for transaction.",
|
|
||||||
}
|
|
||||||
ErrRawTxString = Error{
|
|
||||||
Code: -32602,
|
|
||||||
Message: "Raw tx is not a string",
|
|
||||||
}
|
|
||||||
ErrDecodeHexString = Error{
|
|
||||||
Code: -22,
|
|
||||||
Message: "Unable to decode hex string",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Errors that are specific to btcd.
|
|
||||||
var (
|
|
||||||
ErrNoWallet = Error{
|
|
||||||
Code: -1,
|
|
||||||
Message: "This implementation does not implement wallet commands",
|
|
||||||
}
|
|
||||||
ErrUnimplemented = Error{
|
|
||||||
Code: -1,
|
|
||||||
Message: "Command unimplemented",
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,79 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
// Need to import this size it registers hash we need.
|
|
||||||
_ "crypto/sha512"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MarshallAndSend takes the reply structure, marshalls it to json, and
|
|
||||||
// sends it back to the io.Writer (most likely an http.ResponseWriter).
|
|
||||||
// returning a log message and an error if there is one.
|
|
||||||
func MarshallAndSend(rawReply Reply, w io.Writer) (string, error) {
|
|
||||||
finalReply, err := json.Marshal(rawReply)
|
|
||||||
if err != nil {
|
|
||||||
msg := fmt.Sprintf("[RPCS] Error Marshalling reply: %v", err)
|
|
||||||
return msg, err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s\n", finalReply)
|
|
||||||
msg := fmt.Sprintf("[RPCS] reply: %v", rawReply)
|
|
||||||
return msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// jsonRPCSend connects to the daemon with the specified username, password,
|
|
||||||
// and ip/port and then send the supplied message. This uses net/http rather
|
|
||||||
// than net/rpc/jsonrpc since that one doesn't support http connections and is
|
|
||||||
// therefore useless.
|
|
||||||
func jsonRPCSend(user string, password string, server string, message []byte,
|
|
||||||
https bool, certificates []byte, skipverify bool) (*http.Response, error) {
|
|
||||||
client := &http.Client{}
|
|
||||||
protocol := "http"
|
|
||||||
if https {
|
|
||||||
pool := x509.NewCertPool()
|
|
||||||
pool.AppendCertsFromPEM(certificates)
|
|
||||||
|
|
||||||
config := &tls.Config{
|
|
||||||
InsecureSkipVerify: skipverify,
|
|
||||||
RootCAs: pool,
|
|
||||||
}
|
|
||||||
transport := &http.Transport{TLSClientConfig: config}
|
|
||||||
client.Transport = transport
|
|
||||||
protocol = "https"
|
|
||||||
}
|
|
||||||
credentials := url.UserPassword(user, password).String()
|
|
||||||
resp, err := client.Post(protocol+"://"+credentials+"@"+server,
|
|
||||||
"application/json", bytes.NewReader(message))
|
|
||||||
if err != nil {
|
|
||||||
// We do not want to log the username/password in the errors.
|
|
||||||
replaceStr := "<username>:<password>"
|
|
||||||
str := strings.Replace(err.Error(), credentials, replaceStr, -1)
|
|
||||||
err = fmt.Errorf("%v", str)
|
|
||||||
}
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRaw should be called after JsonRpcSend. It reads and returns
|
|
||||||
// the reply (which you can then call ReadResultCmd() on) and closes the
|
|
||||||
// connection.
|
|
||||||
func GetRaw(resp io.ReadCloser) ([]byte, error) {
|
|
||||||
body, err := ioutil.ReadAll(resp)
|
|
||||||
resp.Close()
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error reading json reply: %v", err)
|
|
||||||
return body, err
|
|
||||||
}
|
|
||||||
return body, nil
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestMarshallAndSend tests the MarshallAndSend function to make sure it can
|
|
||||||
// create a json message to write to the io.Writerr and to make sure
|
|
||||||
// it fails properly in cases where it cannot generate json.
|
|
||||||
func TestMarshallAndSend(t *testing.T) {
|
|
||||||
jsonError := btcjson.Error{
|
|
||||||
Code: -32700,
|
|
||||||
Message: "Parse error",
|
|
||||||
}
|
|
||||||
// json.Marshal cannot handle channels so this is a good way to get a
|
|
||||||
// marshal failure.
|
|
||||||
badRes := make(chan interface{})
|
|
||||||
rawReply := btcjson.Reply{
|
|
||||||
Result: badRes,
|
|
||||||
Error: &jsonError,
|
|
||||||
Id: nil,
|
|
||||||
}
|
|
||||||
var w bytes.Buffer
|
|
||||||
|
|
||||||
msg, err := btcjson.MarshallAndSend(rawReply, &w)
|
|
||||||
if fmt.Sprintf("%s", err) != "json: unsupported type: chan interface {}" {
|
|
||||||
t.Error("Should not be able to unmarshall channel")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use something simple so we can compare the reply.
|
|
||||||
rawReply = btcjson.Reply{
|
|
||||||
Result: nil,
|
|
||||||
Error: nil,
|
|
||||||
Id: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err = btcjson.MarshallAndSend(rawReply, &w)
|
|
||||||
if msg != "[RPCS] reply: {<nil> <nil> <nil>}" {
|
|
||||||
t.Error("Incorrect reply:", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
expBuf := "{\"result\":null,\"error\":null,\"id\":null}\n"
|
|
||||||
|
|
||||||
if w.String() != expBuf {
|
|
||||||
t.Error("Incorrect data in buffer:", w.String())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,750 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BlockResult models the data from the getblock command when the verbose flag
|
|
||||||
// is set. When the verbose flag is not set, getblock return a hex-encoded
|
|
||||||
// string.
|
|
||||||
type BlockResult struct {
|
|
||||||
Hash string `json:"hash"`
|
|
||||||
Confirmations uint64 `json:"confirmations"`
|
|
||||||
Size int32 `json:"size"`
|
|
||||||
Height int64 `json:"height"`
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
MerkleRoot string `json:"merkleroot"`
|
|
||||||
Tx []string `json:"tx,omitempty"`
|
|
||||||
RawTx []TxRawResult `json:"rawtx,omitempty"`
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
Nonce uint32 `json:"nonce"`
|
|
||||||
Bits string `json:"bits"`
|
|
||||||
Difficulty float64 `json:"difficulty"`
|
|
||||||
PreviousHash string `json:"previousblockhash"`
|
|
||||||
NextHash string `json:"nextblockhash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMultiSigResult models the data returned from the createmultisig command.
|
|
||||||
type CreateMultiSigResult struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
RedeemScript string `json:"redeemScript"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeScriptResult models the data returned from the decodescript command.
|
|
||||||
type DecodeScriptResult struct {
|
|
||||||
Asm string `json:"asm"`
|
|
||||||
ReqSigs int32 `json:"reqSigs,omitempty"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Addresses []string `json:"addresses,omitempty"`
|
|
||||||
P2sh string `json:"p2sh"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAddedNodeInfoResultAddr models the data of the addresses portion of the
|
|
||||||
// getaddednodeinfo command.
|
|
||||||
type GetAddedNodeInfoResultAddr struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
Connected string `json:"connected"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAddedNodeInfoResult models the data from the getaddednodeinfo command.
|
|
||||||
type GetAddedNodeInfoResult struct {
|
|
||||||
AddedNode string `json:"addednode"`
|
|
||||||
Connected *bool `json:"connected,omitempty"`
|
|
||||||
Addresses *[]GetAddedNodeInfoResultAddr `json:"addresses,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockChainInfoResult models the data returned from the getblockchaininfo
|
|
||||||
// command.
|
|
||||||
type GetBlockChainInfoResult struct {
|
|
||||||
Chain string `json:"chain"`
|
|
||||||
Blocks int32 `json:"blocks"`
|
|
||||||
BestBlockHash string `json:"bestblockhash"`
|
|
||||||
Difficulty float64 `json:"difficulty"`
|
|
||||||
VerificationProgress float64 `json:"verificationprogress"`
|
|
||||||
ChainWork string `json:"chainwork"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockTemplateResultTx models the transactions field of the
|
|
||||||
// getblocktemplate command.
|
|
||||||
type GetBlockTemplateResultTx struct {
|
|
||||||
Data string `json:"data"`
|
|
||||||
Hash string `json:"hash"`
|
|
||||||
Depends []int64 `json:"depends"`
|
|
||||||
Fee int64 `json:"fee"`
|
|
||||||
SigOps int64 `json:"sigops"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockTemplateResultAux models the coinbaseaux field of the
|
|
||||||
// getblocktemplate command.
|
|
||||||
type GetBlockTemplateResultAux struct {
|
|
||||||
Flags string `json:"flags"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockTemplateResult models the data returned from the getblocktemplate
|
|
||||||
// command.
|
|
||||||
type GetBlockTemplateResult struct {
|
|
||||||
// Base fields from BIP 0022. CoinbaseAux is optional. One of
|
|
||||||
// CoinbaseTxn or CoinbaseValue must be specified, but not both.
|
|
||||||
Bits string `json:"bits"`
|
|
||||||
CurTime int64 `json:"curtime"`
|
|
||||||
Height int64 `json:"height"`
|
|
||||||
PreviousHash string `json:"previousblockhash"`
|
|
||||||
SigOpLimit int64 `json:"sigoplimit,omitempty"`
|
|
||||||
SizeLimit int64 `json:"sizelimit,omitempty"`
|
|
||||||
Transactions []GetBlockTemplateResultTx `json:"transactions"`
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
CoinbaseAux *GetBlockTemplateResultAux `json:"coinbaseaux,omitempty"`
|
|
||||||
CoinbaseTxn *GetBlockTemplateResultTx `json:"coinbasetxn,omitempty"`
|
|
||||||
CoinbaseValue *int64 `json:"coinbasevalue,omitempty"`
|
|
||||||
WorkID string `json:"workid,omitempty"`
|
|
||||||
|
|
||||||
// Optional long polling from BIP 0022.
|
|
||||||
LongPollID string `json:"longpollid,omitempty"`
|
|
||||||
LongPollURI string `json:"longpolluri,omitempty"`
|
|
||||||
SubmitOld *bool `json:"submitold,omitempty"`
|
|
||||||
|
|
||||||
// Basic pool extension from BIP 0023.
|
|
||||||
Target string `json:"target,omitempty"`
|
|
||||||
Expires int64 `json:"expires,omitempty"`
|
|
||||||
|
|
||||||
// Mutations from BIP 0023.
|
|
||||||
MaxTime int64 `json:"maxtime,omitempty"`
|
|
||||||
MinTime int64 `json:"mintime,omitempty"`
|
|
||||||
Mutable []string `json:"mutable,omitempty"`
|
|
||||||
NonceRange string `json:"noncerange,omitempty"`
|
|
||||||
|
|
||||||
// Block proposal from BIP 0023.
|
|
||||||
Capabilities []string `json:"capabilities,omitempty"`
|
|
||||||
RejectReasion string `json:"reject-reason,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNetworkInfoResult models the data returned from the getnetworkinfo command.
|
|
||||||
type GetNetworkInfoResult struct {
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
ProtocolVersion int32 `json:"protocolversion"`
|
|
||||||
TimeOffset int64 `json:"timeoffset"`
|
|
||||||
Connections int32 `json:"connections"`
|
|
||||||
Networks []NetworksResult `json:"networks"`
|
|
||||||
RelayFee float64 `json:"relayfee"`
|
|
||||||
LocalAddresses []LocalAddressesResult `json:"localaddresses"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPeerInfoResult models the data returned from the getpeerinfo command.
|
|
||||||
type GetPeerInfoResult struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Addr string `json:"addr"`
|
|
||||||
AddrLocal string `json:"addrlocal,omitempty"`
|
|
||||||
Services string `json:"services"`
|
|
||||||
LastSend int64 `json:"lastsend"`
|
|
||||||
LastRecv int64 `json:"lastrecv"`
|
|
||||||
BytesSent uint64 `json:"bytessent"`
|
|
||||||
BytesRecv uint64 `json:"bytesrecv"`
|
|
||||||
ConnTime int64 `json:"conntime"`
|
|
||||||
TimeOffset int64 `json:"timeoffset"`
|
|
||||||
PingTime float64 `json:"pingtime"`
|
|
||||||
PingWait float64 `json:"pingwait,omitempty"`
|
|
||||||
Version uint32 `json:"version"`
|
|
||||||
SubVer string `json:"subver"`
|
|
||||||
Inbound bool `json:"inbound"`
|
|
||||||
StartingHeight int32 `json:"startingheight"`
|
|
||||||
CurrentHeight int32 `json:"currentheight,omitempty"`
|
|
||||||
BanScore int32 `json:"banscore,omitempty"`
|
|
||||||
SyncNode bool `json:"syncnode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRawMempoolResult models the data returned from the getrawmempool command.
|
|
||||||
type GetRawMempoolResult struct {
|
|
||||||
Size int32 `json:"size"`
|
|
||||||
Fee float64 `json:"fee"`
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
Height int64 `json:"height"`
|
|
||||||
StartingPriority float64 `json:"startingpriority"`
|
|
||||||
CurrentPriority float64 `json:"currentpriority"`
|
|
||||||
Depends []string `json:"depends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTransactionDetailsResult models the details data from the gettransaction command.
|
|
||||||
type GetTransactionDetailsResult struct {
|
|
||||||
Account string `json:"account"`
|
|
||||||
Address string `json:"address,omitempty"`
|
|
||||||
Category string `json:"category"`
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
Fee float64 `json:"fee,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTransactionResult models the data from the gettransaction command.
|
|
||||||
type GetTransactionResult struct {
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
Fee float64 `json:"fee,omitempty"`
|
|
||||||
Confirmations int64 `json:"confirmations"`
|
|
||||||
BlockHash string `json:"blockhash"`
|
|
||||||
BlockIndex int64 `json:"blockindex"`
|
|
||||||
BlockTime int64 `json:"blocktime"`
|
|
||||||
TxID string `json:"txid"`
|
|
||||||
WalletConflicts []string `json:"walletconflicts"`
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
TimeReceived int64 `json:"timereceived"`
|
|
||||||
Details []GetTransactionDetailsResult `json:"details"`
|
|
||||||
Hex string `json:"hex"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTxOutResult models the data from the gettxout command.
|
|
||||||
type GetTxOutResult struct {
|
|
||||||
BestBlock string `json:"bestblock"`
|
|
||||||
Confirmations int64 `json:"confirmations"`
|
|
||||||
Value float64 `json:"value"`
|
|
||||||
ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"`
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
Coinbase bool `json:"coinbase"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNetTotalsResult models the data returned from the getnettotals command.
|
|
||||||
type GetNetTotalsResult struct {
|
|
||||||
TotalBytesRecv uint64 `json:"totalbytesrecv"`
|
|
||||||
TotalBytesSent uint64 `json:"totalbytessent"`
|
|
||||||
TimeMillis int64 `json:"timemillis"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScriptSig models a signature script. It is defined seperately since it only
|
|
||||||
// applies to non-coinbase. Therefore the field in the Vin structure needs
|
|
||||||
// to be a pointer.
|
|
||||||
type ScriptSig struct {
|
|
||||||
Asm string `json:"asm"`
|
|
||||||
Hex string `json:"hex"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vin models parts of the tx data. It is defined seperately since both
|
|
||||||
// getrawtransaction, sendrawtransaction, and decoderawtransaction use the
|
|
||||||
// same structure.
|
|
||||||
type Vin struct {
|
|
||||||
Coinbase string `json:"coinbase"`
|
|
||||||
Txid string `json:"txid"`
|
|
||||||
Vout uint32 `json:"vout"`
|
|
||||||
ScriptSig *ScriptSig `json:"scriptSig"`
|
|
||||||
Sequence uint32 `json:"sequence"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCoinBase returns a bool to show if a Vin is a Coinbase one or not.
|
|
||||||
func (v *Vin) IsCoinBase() bool {
|
|
||||||
return len(v.Coinbase) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON provides a custom Marshal method for Vin.
|
|
||||||
func (v *Vin) MarshalJSON() ([]byte, error) {
|
|
||||||
if v.IsCoinBase() {
|
|
||||||
coinbaseStruct := struct {
|
|
||||||
Coinbase string `json:"coinbase"`
|
|
||||||
Sequence uint32 `json:"sequence"`
|
|
||||||
}{
|
|
||||||
Coinbase: v.Coinbase,
|
|
||||||
Sequence: v.Sequence,
|
|
||||||
}
|
|
||||||
return json.Marshal(coinbaseStruct)
|
|
||||||
}
|
|
||||||
|
|
||||||
txStruct := struct {
|
|
||||||
Txid string `json:"txid"`
|
|
||||||
Vout uint32 `json:"vout"`
|
|
||||||
ScriptSig *ScriptSig `json:"scriptSig"`
|
|
||||||
Sequence uint32 `json:"sequence"`
|
|
||||||
}{
|
|
||||||
Txid: v.Txid,
|
|
||||||
Vout: v.Vout,
|
|
||||||
ScriptSig: v.ScriptSig,
|
|
||||||
Sequence: v.Sequence,
|
|
||||||
}
|
|
||||||
return json.Marshal(txStruct)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScriptPubKeyResult models the scriptPubKey data of a tx script. It is
|
|
||||||
// defined separately since it is used by multiple commands.
|
|
||||||
type ScriptPubKeyResult struct {
|
|
||||||
Asm string `json:"asm"`
|
|
||||||
Hex string `json:"hex,omitempty"`
|
|
||||||
ReqSigs int32 `json:"reqSigs,omitempty"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Addresses []string `json:"addresses,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vout models parts of the tx data. It is defined seperately since both
|
|
||||||
// getrawtransaction, sendrawtransaction, and decoderawtransaction use the same
|
|
||||||
// structure.
|
|
||||||
type Vout struct {
|
|
||||||
Value float64 `json:"value"`
|
|
||||||
N uint32 `json:"n"`
|
|
||||||
ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMiningInfoResult models the data from the getmininginfo command.
|
|
||||||
type GetMiningInfoResult struct {
|
|
||||||
Blocks int64 `json:"blocks"`
|
|
||||||
CurrentBlockSize uint64 `json:"currentblocksize"`
|
|
||||||
CurrentBlockTx uint64 `json:"currentblocktx"`
|
|
||||||
Difficulty float64 `json:"difficulty"`
|
|
||||||
Errors string `json:"errors"`
|
|
||||||
Generate bool `json:"generate"`
|
|
||||||
GenProcLimit int32 `json:"genproclimit"`
|
|
||||||
HashesPerSec int64 `json:"hashespersec"`
|
|
||||||
NetworkHashPS int64 `json:"networkhashps"`
|
|
||||||
PooledTx uint64 `json:"pooledtx"`
|
|
||||||
TestNet bool `json:"testnet"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWorkResult models the data from the getwork command.
|
|
||||||
type GetWorkResult struct {
|
|
||||||
Data string `json:"data"`
|
|
||||||
Hash1 string `json:"hash1"`
|
|
||||||
Midstate string `json:"midstate"`
|
|
||||||
Target string `json:"target"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InfoResult contains the data returned by the getinfo command.
|
|
||||||
type InfoResult struct {
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
ProtocolVersion int32 `json:"protocolversion"`
|
|
||||||
WalletVersion int32 `json:"walletversion,omitempty"`
|
|
||||||
Balance float64 `json:"balance,omitempty"`
|
|
||||||
Blocks int32 `json:"blocks"`
|
|
||||||
TimeOffset int64 `json:"timeoffset"`
|
|
||||||
Connections int32 `json:"connections"`
|
|
||||||
Proxy string `json:"proxy"`
|
|
||||||
Difficulty float64 `json:"difficulty"`
|
|
||||||
TestNet bool `json:"testnet"`
|
|
||||||
KeypoolOldest int64 `json:"keypoololdest,omitempty"`
|
|
||||||
KeypoolSize int32 `json:"keypoolsize,omitempty"`
|
|
||||||
UnlockedUntil int64 `json:"unlocked_until,omitempty"`
|
|
||||||
PaytxFee float64 `json:"paytxfee,omitempty"`
|
|
||||||
RelayFee float64 `json:"relayfee"`
|
|
||||||
Errors string `json:"errors"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListTransactionsResult models the data from the listtransactions command.
|
|
||||||
type ListTransactionsResult struct {
|
|
||||||
Account string `json:"account"`
|
|
||||||
Address string `json:"address,omitempty"`
|
|
||||||
Category string `json:"category"`
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
Fee float64 `json:"fee"`
|
|
||||||
Confirmations int64 `json:"confirmations"`
|
|
||||||
Generated bool `json:"generated,omitempty"`
|
|
||||||
BlockHash string `json:"blockhash,omitempty"`
|
|
||||||
BlockIndex int64 `json:"blockindex,omitempty"`
|
|
||||||
BlockTime int64 `json:"blocktime,omitempty"`
|
|
||||||
TxID string `json:"txid"`
|
|
||||||
WalletConflicts []string `json:"walletconflicts"`
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
TimeReceived int64 `json:"timereceived"`
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
OtherAccount string `json:"otheraccount"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalAddressesResult models the localaddresses data from the getnetworkinfo command.
|
|
||||||
type LocalAddressesResult struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
Port uint16 `json:"port"`
|
|
||||||
Score int32 `json:"score"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListReceivedByAccountResult models the data from the listreceivedbyaccount
|
|
||||||
// command.
|
|
||||||
type ListReceivedByAccountResult struct {
|
|
||||||
Account string `json:"account"`
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
Confirmations uint64 `json:"confirmations"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListReceivedByAddressResult models the data from the listreceivedbyaddress
|
|
||||||
// command.
|
|
||||||
type ListReceivedByAddressResult struct {
|
|
||||||
Account string `json:"account"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
Confirmations uint64 `json:"confirmations"`
|
|
||||||
TxIDs []string `json:"txids,omitempty"`
|
|
||||||
InvolvesWatchonly bool `json:"involvesWatchonly,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListSinceBlockResult models the data from the listsinceblock command.
|
|
||||||
type ListSinceBlockResult struct {
|
|
||||||
Transactions []ListTransactionsResult `json:"transactions"`
|
|
||||||
LastBlock string `json:"lastblock"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListUnspentResult models a successful response from the listunspent request.
|
|
||||||
type ListUnspentResult struct {
|
|
||||||
TxId string `json:"txid"`
|
|
||||||
Vout uint32 `json:"vout"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
Account string `json:"account"`
|
|
||||||
ScriptPubKey string `json:"scriptPubKey"`
|
|
||||||
RedeemScript string `json:"redeemScript,omitempty"`
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
Confirmations int64 `json:"confirmations"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworksResult models the networks data from the getnetworkinfo command.
|
|
||||||
type NetworksResult struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Limited bool `json:"limited"`
|
|
||||||
Reachable bool `json:"reachable"`
|
|
||||||
Proxy string `json:"proxy"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignRawTransactionResult models the data from the signrawtransaction
|
|
||||||
// command.
|
|
||||||
type SignRawTransactionResult struct {
|
|
||||||
Hex string `json:"hex"`
|
|
||||||
Complete bool `json:"complete"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxRawResult models the data from the getrawtransaction and sendrawtransaction
|
|
||||||
// commands
|
|
||||||
type TxRawResult struct {
|
|
||||||
Hex string `json:"hex"`
|
|
||||||
Txid string `json:"txid"`
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
LockTime uint32 `json:"locktime"`
|
|
||||||
Vin []Vin `json:"vin"`
|
|
||||||
Vout []Vout `json:"vout"`
|
|
||||||
BlockHash string `json:"blockhash,omitempty"`
|
|
||||||
Confirmations uint64 `json:"confirmations"`
|
|
||||||
Time int64 `json:"time,omitempty"`
|
|
||||||
Blocktime int64 `json:"blocktime,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxRawDecodeResult models the data from the decoderawtransaction command.
|
|
||||||
type TxRawDecodeResult struct {
|
|
||||||
Txid string `json:"txid"`
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
Locktime uint32 `json:"locktime"`
|
|
||||||
Vin []Vin `json:"vin"`
|
|
||||||
Vout []Vout `json:"vout"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateAddressResult models the data from the validateaddress command.
|
|
||||||
type ValidateAddressResult struct {
|
|
||||||
IsValid bool `json:"isvalid"`
|
|
||||||
Address string `json:"address,omitempty"`
|
|
||||||
IsMine bool `json:"ismine,omitempty"`
|
|
||||||
IsWatchOnly bool `json:"iswatchonly,omitempty"`
|
|
||||||
IsScript bool `json:"isscript,omitempty"`
|
|
||||||
PubKey string `json:"pubkey,omitempty"`
|
|
||||||
IsCompressed bool `json:"iscompressed,omitempty"`
|
|
||||||
Account string `json:"account,omitempty"`
|
|
||||||
Addresses []string `json:"addresses,omitempty"`
|
|
||||||
Hex string `json:"hex,omitempty"`
|
|
||||||
Script string `json:"script,omitempty"`
|
|
||||||
SigsRequired int32 `json:"sigsrequired,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadResultCmd unmarshalls the json reply with data struct for specific
|
|
||||||
// commands or an interface if it is not a command where we already have a
|
|
||||||
// struct ready.
|
|
||||||
func ReadResultCmd(cmd string, message []byte) (Reply, error) {
|
|
||||||
var result Reply
|
|
||||||
var err error
|
|
||||||
var objmap map[string]json.RawMessage
|
|
||||||
err = json.Unmarshal(message, &objmap)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
// Take care of the parts that are the same for all replies.
|
|
||||||
var jsonErr Error
|
|
||||||
var id interface{}
|
|
||||||
err = json.Unmarshal(objmap["error"], &jsonErr)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error unmarshalling json reply: %v", err)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(objmap["id"], &id)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error unmarshalling json reply: %v", err)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it is a command where we have already worked out the reply,
|
|
||||||
// generate put the results in the proper structure.
|
|
||||||
// We handle the error condition after the switch statement.
|
|
||||||
switch cmd {
|
|
||||||
case "createmultisig":
|
|
||||||
var res *CreateMultiSigResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "decodescript":
|
|
||||||
var res *DecodeScriptResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getaddednodeinfo":
|
|
||||||
// getaddednodeinfo can either return a JSON object or a
|
|
||||||
// slice of strings depending on the verbose flag. Choose the
|
|
||||||
// right form accordingly.
|
|
||||||
if bytes.IndexByte(objmap["result"], '{') > -1 {
|
|
||||||
var res []GetAddedNodeInfoResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var res []string
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "getinfo":
|
|
||||||
var res *InfoResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getblock":
|
|
||||||
// getblock can either return a JSON object or a hex-encoded
|
|
||||||
// string depending on the verbose flag. Choose the right form
|
|
||||||
// accordingly.
|
|
||||||
if bytes.IndexByte(objmap["result"], '{') > -1 {
|
|
||||||
var res *BlockResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var res string
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "getblockchaininfo":
|
|
||||||
var res *GetBlockChainInfoResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getnettotals":
|
|
||||||
var res *GetNetTotalsResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getnetworkhashps":
|
|
||||||
var res int64
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getpeerinfo":
|
|
||||||
var res []GetPeerInfoResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getrawtransaction":
|
|
||||||
// getrawtransaction can either return a JSON object or a
|
|
||||||
// hex-encoded string depending on the verbose flag. Choose the
|
|
||||||
// right form accordingly.
|
|
||||||
if bytes.IndexByte(objmap["result"], '{') > -1 {
|
|
||||||
var res *TxRawResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var res string
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "decoderawtransaction":
|
|
||||||
var res *TxRawDecodeResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getaddressesbyaccount":
|
|
||||||
var res []string
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getmininginfo":
|
|
||||||
var res *GetMiningInfoResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getnetworkinfo":
|
|
||||||
var res *GetNetworkInfoResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
if res != nil && res.LocalAddresses == nil {
|
|
||||||
res.LocalAddresses = []LocalAddressesResult{}
|
|
||||||
}
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getrawmempool":
|
|
||||||
// getrawmempool can either return a map of JSON objects or
|
|
||||||
// an array of strings depending on the verbose flag. Choose
|
|
||||||
// the right form accordingly.
|
|
||||||
if bytes.IndexByte(objmap["result"], '{') > -1 {
|
|
||||||
var res map[string]GetRawMempoolResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var res []string
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "gettransaction":
|
|
||||||
var res *GetTransactionResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "gettxout":
|
|
||||||
var res *GetTxOutResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if res != nil && err == nil {
|
|
||||||
if res.ScriptPubKey.Addresses == nil {
|
|
||||||
res.ScriptPubKey.Addresses = []string{}
|
|
||||||
}
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "getwork":
|
|
||||||
// getwork can either return a JSON object or a boolean
|
|
||||||
// depending on whether or not data was provided. Choose the
|
|
||||||
// right form accordingly.
|
|
||||||
if bytes.IndexByte(objmap["result"], '{') > -1 {
|
|
||||||
var res *GetWorkResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var res bool
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "validateaddress":
|
|
||||||
var res *ValidateAddressResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "signrawtransaction":
|
|
||||||
var res *SignRawTransactionResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "listaccounts":
|
|
||||||
var res map[string]float64
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "listreceivedbyaccount":
|
|
||||||
var res []ListReceivedByAccountResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "listreceivedbyaddress":
|
|
||||||
var res []ListReceivedByAddressResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "listsinceblock":
|
|
||||||
var res *ListSinceBlockResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
if res.Transactions == nil {
|
|
||||||
res.Transactions = []ListTransactionsResult{}
|
|
||||||
}
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "listtransactions":
|
|
||||||
var res []ListTransactionsResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
case "listunspent":
|
|
||||||
var res []ListUnspentResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
|
|
||||||
case "searchrawtransactions":
|
|
||||||
// searchrawtransactions can either return a list of JSON objects
|
|
||||||
// or a list of hex-encoded strings depending on the verbose flag.
|
|
||||||
// Choose the right form accordingly.
|
|
||||||
if bytes.IndexByte(objmap["result"], '{') > -1 {
|
|
||||||
var res []*TxRawResult
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var res []string
|
|
||||||
err = json.Unmarshal(objmap["result"], &res)
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// For commands that return a single item (or no items), we get it with
|
|
||||||
// the correct concrete type for free (but treat them separately
|
|
||||||
// for clarity).
|
|
||||||
case "getblockcount", "getbalance", "getblockhash", "getgenerate",
|
|
||||||
"getconnectioncount", "getdifficulty", "gethashespersec",
|
|
||||||
"setgenerate", "stop", "settxfee", "getaccount",
|
|
||||||
"getnewaddress", "sendtoaddress", "createrawtransaction",
|
|
||||||
"sendrawtransaction", "getbestblockhash", "getrawchangeaddress",
|
|
||||||
"sendfrom", "sendmany", "addmultisigaddress", "getunconfirmedbalance",
|
|
||||||
"getaccountaddress", "estimatefee", "estimatepriority":
|
|
||||||
err = json.Unmarshal(message, &result)
|
|
||||||
default:
|
|
||||||
// None of the standard Bitcoin RPC methods matched. Try
|
|
||||||
// registered custom command reply parsers.
|
|
||||||
if c, ok := customCmds[cmd]; ok && c.replyParser != nil {
|
|
||||||
var res interface{}
|
|
||||||
res, err = c.replyParser(objmap["result"])
|
|
||||||
if err == nil {
|
|
||||||
result.Result = res
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// For anything else put it in an interface. All the
|
|
||||||
// data is still there, just a little less convenient
|
|
||||||
// to deal with.
|
|
||||||
err = json.Unmarshal(message, &result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error unmarshalling json reply: %v", err)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
// Only want the error field when there is an actual error to report.
|
|
||||||
if jsonErr.Code != 0 {
|
|
||||||
result.Error = &jsonErr
|
|
||||||
}
|
|
||||||
result.Id = &id
|
|
||||||
return result, err
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
var resulttests = []struct {
|
|
||||||
cmd string
|
|
||||||
msg []byte
|
|
||||||
comp bool
|
|
||||||
pass bool
|
|
||||||
}{
|
|
||||||
// Generate a fake message to make sure we can encode and decode it and
|
|
||||||
// get the same thing back.
|
|
||||||
{"getblockcount",
|
|
||||||
[]byte(`{"result":226790,"error":{"code":1,"message":"No Error"},"id":"btcd"}`),
|
|
||||||
true, true},
|
|
||||||
// Generate a fake message to make sure we don't make a command from it.
|
|
||||||
{"anycommand", []byte(`{"result":"test","id":1}`), false, false},
|
|
||||||
{"anycommand", []byte(`{some junk}`), false, false},
|
|
||||||
{"anycommand", []byte(`{"error":null,"result":null,"id":"test"}`), false, true},
|
|
||||||
{"createmultisig", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"createmultisig", []byte(`{"error":null,"id":1,"result":{"address":"something","redeemScript":"else"}}`), false, true},
|
|
||||||
{"decodescript", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"decodescript", []byte(`{"error":null,"id":1,"result":{"Asm":"something"}}`), false, true},
|
|
||||||
{"getinfo", []byte(`{"error":null,"result":null,"id":"test"}`), false, true},
|
|
||||||
{"getinfo", []byte(`{"error":null,"result":null}`), false, false},
|
|
||||||
{"getinfo", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"getblock", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"getblock", []byte(`{"result":{"hash":"000000","confirmations":16007,"size":325648},"error":null,"id":1}`), false, true},
|
|
||||||
{"getblockchaininfo", []byte(`{"result":{"chain":"hex","blocks":250000,"bestblockhash":"hash"},"error":null,"id":1}`), false, true},
|
|
||||||
{"getblockchaininfo", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"getnetworkinfo", []byte(`{"result":{"version":100,"protocolversion":70002},"error":null,"id":1}`), false, true},
|
|
||||||
{"getnetworkinfo", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"getrawtransaction", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"getrawtransaction", []byte(`{"error":null,"id":1,"result":{"hex":"somejunk","version":1}}`), false, true},
|
|
||||||
{"gettransaction", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"gettransaction", []byte(`{"error":null,"id":1,"result":{"Amount":0.0}}`), false, true},
|
|
||||||
{"decoderawtransaction", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"decoderawtransaction", []byte(`{"error":null,"id":1,"result":{"Txid":"something"}}`), false, true},
|
|
||||||
{"getaddressesbyaccount", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"getaddressesbyaccount", []byte(`{"error":null,"id":1,"result":["test"]}`), false, true},
|
|
||||||
{"getmininginfo", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"getmininginfo", []byte(`{"error":null,"id":1,"result":{"generate":true}}`), false, true},
|
|
||||||
{"gettxout", []byte(`{"error":null,"id":1,"result":{"bestblock":"a","value":1.0}}`), false, true},
|
|
||||||
{"listreceivedbyaddress", []byte(`{"error":null,"id":1,"result":[{"a"}]}`), false, false},
|
|
||||||
{"listreceivedbyaddress", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, true},
|
|
||||||
{"listsinceblock", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
||||||
{"listsinceblock", []byte(`{"error":null,"id":1,"result":{"lastblock":"something"}}`), false, true},
|
|
||||||
{"validateaddress", []byte(`{"error":null,"id":1,"result":{"isvalid":false}}`), false, true},
|
|
||||||
{"validateaddress", []byte(`{"error":null,"id":1,"result":{false}}`), false, false},
|
|
||||||
{"signrawtransaction", []byte(`{"error":null,"id":1,"result":{"hex":"something","complete":false}}`), false, true},
|
|
||||||
{"signrawtransaction", []byte(`{"error":null,"id":1,"result":{false}}`), false, false},
|
|
||||||
{"listunspent", []byte(`{"error":null,"id":1,"result":[{"txid":"something"}]}`), false, true},
|
|
||||||
{"listunspent", []byte(`{"error":null,"id":1,"result":[{"txid"}]}`), false, false},
|
|
||||||
{"searchrawtransactions", []byte(`{"error":null,"id":1,"result":{"a":"b"}}`), false, false},
|
|
||||||
{"searchrawtransactions", []byte(`{"error":null,"id":1,"result":["sometxhex"]}`), false, true},
|
|
||||||
{"searchrawtransactions", []byte(`{"error":null,"id":1,"result":[{"hex":"somejunk","version":1}]}`), false, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestReadResultCmd tests that readResultCmd can properly unmarshall the
|
|
||||||
// returned []byte that contains a json reply for both known and unknown
|
|
||||||
// messages.
|
|
||||||
func TestReadResultCmd(t *testing.T) {
|
|
||||||
for i, tt := range resulttests {
|
|
||||||
result, err := btcjson.ReadResultCmd(tt.cmd, tt.msg)
|
|
||||||
if tt.pass {
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Should read result: %d %v", i, err)
|
|
||||||
}
|
|
||||||
// Due to the pointer for the Error and other structs,
|
|
||||||
// we can't always guarantee byte for byte comparison.
|
|
||||||
if tt.comp {
|
|
||||||
msg2, err := json.Marshal(result)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Should unmarshal result: %d %v", i, err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(tt.msg, msg2) {
|
|
||||||
t.Errorf("json byte arrays differ. %d %v %v", i, tt.msg, msg2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should fail: %d, %s", i, tt.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestIsValidIDType ensures the IsValidIDType function behaves as expected.
|
// TestIsValidIDType ensures the IsValidIDType function behaves as expected.
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestUsageFlagStringer tests the stringized output for the UsageFlag type.
|
// TestUsageFlagStringer tests the stringized output for the UsageFlag type.
|
|
@ -1,87 +0,0 @@
|
||||||
btcjson
|
|
||||||
=======
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)]
|
|
||||||
(https://travis-ci.org/btcsuite/btcd) [![ISC License]
|
|
||||||
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
|
||||||
|
|
||||||
Package btcjson implements concrete types for marshalling to and from the
|
|
||||||
bitcoin JSON-RPC API. A comprehensive suite of tests is provided to ensure
|
|
||||||
proper functionality.
|
|
||||||
|
|
||||||
Although this package was primarily written for the btcsuite, it has
|
|
||||||
intentionally been designed so it can be used as a standalone package for any
|
|
||||||
projects needing to marshal to and from bitcoin JSON-RPC requests and responses.
|
|
||||||
|
|
||||||
Note that although it's possible to use this package directly to implement an
|
|
||||||
RPC client, it is not recommended since it is only intended as an infrastructure
|
|
||||||
package. Instead, RPC clients should use the
|
|
||||||
[btcrpcclient](https://github.com/btcsuite/btcrpcclient) package which provides
|
|
||||||
a full blown RPC client with many features such as automatic connection
|
|
||||||
management, websocket support, automatic notification re-registration on
|
|
||||||
reconnect, and conversion from the raw underlying RPC types (strings, floats,
|
|
||||||
ints, etc) to higher-level types with many nice and useful properties.
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
|
|
||||||
(http://godoc.org/github.com/btcsuite/btcd/btcjson/v2/btcjson)
|
|
||||||
|
|
||||||
Full `go doc` style documentation for the project can be viewed online without
|
|
||||||
installing this package by using the GoDoc site
|
|
||||||
[here](http://godoc.org/github.com/btcsuite/btcd/btcjson/v2/btcjson).
|
|
||||||
|
|
||||||
You can also view the documentation locally once the package is installed with
|
|
||||||
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
|
|
||||||
http://localhost:6060/pkg/github.com/btcsuite/btcd/btcjson/v2/btcjson
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go get github.com/btcsuite/btcd/btcjson/v2/btcjson
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
* [Marshal Command]
|
|
||||||
(http://godoc.org/github.com/btcsuite/btcd/btcjson/v2/btcjson#example-MarshalCmd)
|
|
||||||
Demonstrates how to create and marshal a command into a JSON-RPC request.
|
|
||||||
|
|
||||||
* [Unmarshal Command]
|
|
||||||
(http://godoc.org/github.com/btcsuite/btcd/btcjson/v2/btcjson#example-UnmarshalCmd)
|
|
||||||
Demonstrates how to unmarshal a JSON-RPC request and then unmarshal the
|
|
||||||
concrete request into a concrete command.
|
|
||||||
|
|
||||||
* [Marshal Response]
|
|
||||||
(http://godoc.org/github.com/btcsuite/btcd/btcjson/v2/btcjson#example-MarshalResponse)
|
|
||||||
Demonstrates how to marshal a JSON-RPC response.
|
|
||||||
|
|
||||||
* [Unmarshal Response]
|
|
||||||
(http://godoc.org/github.com/btcsuite/btcd/btcjson/v2/btcjson#example-package--UnmarshalResponse)
|
|
||||||
Demonstrates how to unmarshal a JSON-RPC response and then unmarshal the
|
|
||||||
result field in the response to a concrete type.
|
|
||||||
|
|
||||||
## GPG Verification Key
|
|
||||||
|
|
||||||
All official release tags are signed by Conformal so users can ensure the code
|
|
||||||
has not been tampered with and is coming from Conformal. To verify the
|
|
||||||
signature perform the following:
|
|
||||||
|
|
||||||
- Download the public key from the Conformal website at
|
|
||||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
|
||||||
|
|
||||||
- Import the public key into your GPG keyring:
|
|
||||||
```bash
|
|
||||||
gpg --import GIT-GPG-KEY-conformal.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
|
||||||
placeholder for the specific tag:
|
|
||||||
```bash
|
|
||||||
git tag -v TAG_NAME
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Package btcjson is licensed under the [copyfree](http://copyfree.org) ISC
|
|
||||||
License.
|
|
|
@ -1,146 +0,0 @@
|
||||||
// Copyright (c) 2015 Conformal Systems LLC.
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package btcjson provides primitives for working with the bitcoin JSON-RPC API.
|
|
||||||
|
|
||||||
Overview
|
|
||||||
|
|
||||||
When communicating via the JSON-RPC protocol, all of the commands need to be
|
|
||||||
marshalled to and from the the wire in the appropriate format. This package
|
|
||||||
provides data structures and primitives to ease this process.
|
|
||||||
|
|
||||||
In addition, it also provides some additional features such as custom command
|
|
||||||
registration, command categorization, and reflection-based help generation.
|
|
||||||
|
|
||||||
JSON-RPC Protocol Overview
|
|
||||||
|
|
||||||
This information is not necessary in order to use this package, but it does
|
|
||||||
provide some intuition into what the marshalling and unmarshalling that is
|
|
||||||
discussed below is doing under the hood.
|
|
||||||
|
|
||||||
As defined by the JSON-RPC spec, there are effectively two forms of messages on
|
|
||||||
the wire:
|
|
||||||
|
|
||||||
- Request Objects
|
|
||||||
{"jsonrpc":"1.0","id":"SOMEID","method":"SOMEMETHOD","params":[SOMEPARAMS]}
|
|
||||||
NOTE: Notifications are the same format except the id field is null.
|
|
||||||
|
|
||||||
- Response Objects
|
|
||||||
{"result":SOMETHING,"error":null,"id":"SOMEID"}
|
|
||||||
{"result":null,"error":{"code":SOMEINT,"message":SOMESTRING},"id":"SOMEID"}
|
|
||||||
|
|
||||||
For requests, the params field can vary in what it contains depending on the
|
|
||||||
method (a.k.a. command) being sent. Each parameter can be as simple as an int
|
|
||||||
or a complex structure containing many nested fields. The id field is used to
|
|
||||||
identify a request and will be included in the associated response.
|
|
||||||
|
|
||||||
When working with asynchronous transports, such as websockets, spontaneous
|
|
||||||
notifications are also possible. As indicated, they are the same as a request
|
|
||||||
object, except they have the id field set to null. Therefore, servers will
|
|
||||||
ignore requests with the id field set to null, while clients can choose to
|
|
||||||
consume or ignore them.
|
|
||||||
|
|
||||||
Unfortunately, the original Bitcoin JSON-RPC API (and hence anything compatible
|
|
||||||
with it) doesn't always follow the spec and will sometimes return an error
|
|
||||||
string in the result field with a null error for certain commands. However,
|
|
||||||
for the most part, the error field will be set as described on failure.
|
|
||||||
|
|
||||||
Marshalling and Unmarshalling
|
|
||||||
|
|
||||||
Based upon the discussion above, it should be easy to see how the types of this
|
|
||||||
package map into the required parts of the protocol
|
|
||||||
|
|
||||||
- Request Objects (type Request)
|
|
||||||
- Commands (type <Foo>Cmd)
|
|
||||||
- Notifications (type <Foo>Ntfn)
|
|
||||||
- Response Objects (type Response)
|
|
||||||
- Result (type <Foo>Result)
|
|
||||||
|
|
||||||
To simplify the marshalling of the requests and responses, the MarshalCmd and
|
|
||||||
MarshalResponse functions are provided. They return the raw bytes ready to be
|
|
||||||
sent across the wire.
|
|
||||||
|
|
||||||
Unmarshalling a received Request object is a two step process:
|
|
||||||
1) Unmarshal the raw bytes into a Request struct instance via json.Unmarshal
|
|
||||||
2) Use UnmarshalCmd on the Result field of the unmarshalled Request to create
|
|
||||||
a concrete command or notification instance with all struct fields set
|
|
||||||
accordingly
|
|
||||||
|
|
||||||
This approach is used since it provides the caller with access to the additional
|
|
||||||
fields in the request that are not part of the command such as the ID.
|
|
||||||
|
|
||||||
Unmarshalling a received Response object is also a two step process:
|
|
||||||
1) Unmarshal the raw bytes into a Response struct instance via json.Unmarshal
|
|
||||||
2) Depending on the ID, unmarshal the Result field of the unmarshalled
|
|
||||||
Response to create a concrete type instance
|
|
||||||
|
|
||||||
As above, this approach is used since it provides the caller with access to the
|
|
||||||
fields in the response such as the ID and Error.
|
|
||||||
|
|
||||||
Command Creation
|
|
||||||
|
|
||||||
This package provides two approaches for creating a new command. This first,
|
|
||||||
and preferred, method is to use one of the New<Foo>Cmd functions. This allows
|
|
||||||
static compile-time checking to help ensure the parameters stay in sync with
|
|
||||||
the struct definitions.
|
|
||||||
|
|
||||||
The second approach is the NewCmd function which takes a method (command) name
|
|
||||||
and variable arguments. The function includes full checking to ensure the
|
|
||||||
parameters are accurate according to provided method, however these checks are,
|
|
||||||
obviously, run-time which means any mistakes won't be found until the code is
|
|
||||||
actually executed. However, it is quite useful for user-supplied commands
|
|
||||||
that are intentionally dynamic.
|
|
||||||
|
|
||||||
Custom Command Registration
|
|
||||||
|
|
||||||
The command handling of this package is built around the concept of registered
|
|
||||||
commands. This is true for the wide variety of commands already provided by the
|
|
||||||
package, but it also means caller can easily provide custom commands with all
|
|
||||||
of the same functionality as the built-in commands. Use the RegisterCmd
|
|
||||||
function for this purpose.
|
|
||||||
|
|
||||||
A list of all registered methods can be obtained with the RegisteredCmdMethods
|
|
||||||
function.
|
|
||||||
|
|
||||||
Command Inspection
|
|
||||||
|
|
||||||
All registered commands are registered with flags that identify information such
|
|
||||||
as whether the command applies to a chain server, wallet server, or is a
|
|
||||||
notification along with the method name to use. These flags can be obtained
|
|
||||||
with the MethodUsageFlags flags, and the method can be obtained with the
|
|
||||||
CmdMethod function.
|
|
||||||
|
|
||||||
Help Generation
|
|
||||||
|
|
||||||
To facilitate providing consistent help to users of the RPC server, this package
|
|
||||||
exposes the GenerateHelp and function which uses reflection on registered
|
|
||||||
commands or notifications, as well as the provided expected result types, to
|
|
||||||
generate the final help text.
|
|
||||||
|
|
||||||
In addition, the MethodUsageText function is provided to generate consistent
|
|
||||||
one-line usage for registered commands and notifications using reflection.
|
|
||||||
|
|
||||||
Errors
|
|
||||||
|
|
||||||
There are 2 distinct type of errors supported by this package:
|
|
||||||
|
|
||||||
- General errors related to marshalling or unmarshalling or improper use of
|
|
||||||
the package (type Error)
|
|
||||||
- RPC errors which are intended to be returned across the wire as a part of
|
|
||||||
the JSON-RPC response (type RPCError)
|
|
||||||
|
|
||||||
The first category of errors (type Error) typically indicates a programmer error
|
|
||||||
and can be avoided by properly using the API. Errors of this type will be
|
|
||||||
returned from the various functions available in this package. They identify
|
|
||||||
issues such as unsupported field types, attempts to register malformed commands,
|
|
||||||
and attempting to create a new command with an improper number of parameters.
|
|
||||||
The specific reason for the error can be detected by type asserting it to a
|
|
||||||
*btcjson.Error and accessing the ErrorCode field.
|
|
||||||
|
|
||||||
The second category of errors (type RPCError), on the other hand, are useful for
|
|
||||||
returning errors to RPC clients. Consequently, they are used in the previously
|
|
||||||
described Response type.
|
|
||||||
*/
|
|
||||||
package btcjson
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWalletSvrCmds tests all of the wallet server commands marshal and
|
// TestWalletSvrCmds tests all of the wallet server commands marshal and
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWalletSvrWsCmds tests all of the wallet server websocket-specific
|
// TestWalletSvrWsCmds tests all of the wallet server websocket-specific
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWalletSvrWsNtfns tests all of the chain server websocket-specific
|
// TestWalletSvrWsNtfns tests all of the chain server websocket-specific
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
flags "github.com/btcsuite/go-flags"
|
flags "github.com/btcsuite/go-flags"
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
"github.com/btcsuite/go-socks/socks"
|
"github.com/btcsuite/go-socks/socks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,7 @@ import (
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/btcsuite/btcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcd/btcjson/btcws"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/btcsuite/btcd/database"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
@ -880,7 +879,7 @@ func handleGetBestBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &btcws.GetBestBlockResult{
|
result := &btcjson.GetBestBlockResult{
|
||||||
Hash: sha.String(),
|
Hash: sha.String(),
|
||||||
Height: int32(height),
|
Height: int32(height),
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// helpDescsEnUS defines the English descriptions used for the help strings.
|
// helpDescsEnUS defines the English descriptions used for the help strings.
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/btcsuite/btcd/database"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/addrmgr"
|
"github.com/btcsuite/btcd/addrmgr"
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/btcsuite/btcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/btcsuite/btcd/database"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
|
Loading…
Reference in a new issue