rpcserver: Convert to make use of new btcjson.
This commit converts the RPC server over to use the new features available in the latest version of btcjson and improve a few things along the way. This following summarizes the changes: - All btcjson imports have been updated to the latest package version - The help has been significantly improved - Invoking help with no command specified now provides an alphabetized list of all supported commands along with one-line usage - The help for each command is automatically generated and provides much more explicit information such as the type of each parameter, whether or not it's optional or required, etc - The websocket-specific commands are now provided when accessing the help when connected via websockets - Help has been added for all websocket-specific commands and is only accessible when connected via websockets - The error returns and handling of both the standard and websocket handlers has been made consistent - All RPC errors have been converted to the new RPCError type - Various variables have been renamed for consistency - Several RPC errors have been improved - The commands that are marked as unimplemented have been moved into the separate map where they belong - Several comments have been improved - An unnecessary check has been removed from the createrawtransaction handler - The command parsing has been restructured a bit to pave the way for JSON-RPC 2.0 batching support
This commit is contained in:
parent
c0c48e0efd
commit
637fbcadec
6 changed files with 1519 additions and 691 deletions
|
@ -417,7 +417,7 @@ func NewGetRawTransactionCmd(txHash string, verbose *int) *GetRawTransactionCmd
|
||||||
// GetTxOutCmd defines the gettxout JSON-RPC command.
|
// GetTxOutCmd defines the gettxout JSON-RPC command.
|
||||||
type GetTxOutCmd struct {
|
type GetTxOutCmd struct {
|
||||||
Txid string
|
Txid string
|
||||||
Vout int
|
Vout uint32
|
||||||
IncludeMempool *bool `jsonrpcdefault:"true"`
|
IncludeMempool *bool `jsonrpcdefault:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,7 +426,7 @@ type GetTxOutCmd struct {
|
||||||
//
|
//
|
||||||
// The parameters which are pointers indicate they are optional. Passing nil
|
// The parameters which are pointers indicate they are optional. Passing nil
|
||||||
// for optional parameters will use the default value.
|
// for optional parameters will use the default value.
|
||||||
func NewGetTxOutCmd(txHash string, vout int, includeMempool *bool) *GetTxOutCmd {
|
func NewGetTxOutCmd(txHash string, vout uint32, includeMempool *bool) *GetTxOutCmd {
|
||||||
return &GetTxOutCmd{
|
return &GetTxOutCmd{
|
||||||
Txid: txHash,
|
Txid: txHash,
|
||||||
Vout: vout,
|
Vout: vout,
|
||||||
|
|
1129
rpcserver.go
1129
rpcserver.go
File diff suppressed because it is too large
Load diff
621
rpcserverhelp.go
Normal file
621
rpcserverhelp.go
Normal file
|
@ -0,0 +1,621 @@
|
||||||
|
// 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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// helpDescsEnUS defines the English descriptions used for the help strings.
|
||||||
|
var helpDescsEnUS = map[string]string{
|
||||||
|
// DebugLevelCmd help.
|
||||||
|
"debuglevel--synopsis": "Dynamically changes the debug logging level.\n" +
|
||||||
|
"The levelspec can either a debug level or of the form:\n" +
|
||||||
|
"<subsystem>=<level>,<subsystem2>=<level2>,...\n" +
|
||||||
|
"The valid debug levels are trace, debug, info, warn, error, and critical.\n" +
|
||||||
|
"The valid subsystems are AMGR, ADXR, BCDB, BMGR, BTCD, CHAN, DISC, PEER, RPCS, SCRP, SRVR, and TXMP.\n" +
|
||||||
|
"Finally the keyword 'show' will return a list of the available subsystems.",
|
||||||
|
"debuglevel-levelspec": "The debug level(s) to use or the keyword 'show'",
|
||||||
|
"debuglevel--condition0": "levelspec!=show",
|
||||||
|
"debuglevel--condition1": "levelspec=show",
|
||||||
|
"debuglevel--result0": "The string 'Done.'",
|
||||||
|
"debuglevel--result1": "The list of subsystems",
|
||||||
|
|
||||||
|
// AddNodeCmd help.
|
||||||
|
"addnode--synopsis": "Attempts to add or remove a persistent peer.",
|
||||||
|
"addnode-addr": "IP address and port of the peer to operate on",
|
||||||
|
"addnode-subcmd": "'add' to add a persistent peer, 'remove' to remove a persistent peer, or 'onetry' to try a single connection to a peer",
|
||||||
|
|
||||||
|
// TransactionInput help.
|
||||||
|
"transactioninput-txid": "The hash of the input transaction",
|
||||||
|
"transactioninput-vout": "The specific output of the input transaction to redeem",
|
||||||
|
|
||||||
|
// CreateRawTransactionCmd help.
|
||||||
|
"createrawtransaction--synopsis": "Returns a new transaction spending the provided inputs and sending to the provided addresses.\n" +
|
||||||
|
"The transaction inputs are not signed in the created transaction.\n" +
|
||||||
|
"The signrawtransaction RPC command provided by wallet must be used to sign the resulting transaction.",
|
||||||
|
"createrawtransaction-inputs": "The inputs to the transaction",
|
||||||
|
"createrawtransaction-amounts": "JSON object with the destination addresses as keys and amounts as values",
|
||||||
|
"createrawtransaction-amounts--key": "address",
|
||||||
|
"createrawtransaction-amounts--value": "n.nnn",
|
||||||
|
"createrawtransaction-amounts--desc": "The destination address as the key and the amount in BTC as the value",
|
||||||
|
"createrawtransaction--result0": "Hex-encoded bytes of the serialized transaction",
|
||||||
|
|
||||||
|
// ScriptSig help.
|
||||||
|
"scriptsig-asm": "Disassembly of the script",
|
||||||
|
"scriptsig-hex": "Hex-encoded bytes of the script",
|
||||||
|
|
||||||
|
// Vin help.
|
||||||
|
"vin-coinbase": "The hex-encoded bytes of the signature script (coinbase txns only)",
|
||||||
|
"vin-txid": "The hash of the origin transaction (non-coinbase txns only)",
|
||||||
|
"vin-vout": "The index of the output being redeemed from the origin transaction (non-coinbase txns only)",
|
||||||
|
"vin-scriptSig": "The signature script used to redeem the origin transaction as a JSON object (non-coinbase txns only)",
|
||||||
|
"vin-sequence": "The script sequence number",
|
||||||
|
|
||||||
|
// ScriptPubKeyResult help.
|
||||||
|
"scriptpubkeyresult-asm": "Disassembly of the script",
|
||||||
|
"scriptpubkeyresult-hex": "Hex-encoded bytes of the script",
|
||||||
|
"scriptpubkeyresult-reqSigs": "The number of required signatures",
|
||||||
|
"scriptpubkeyresult-type": "The type of the script (e.g. 'pubkeyhash')",
|
||||||
|
"scriptpubkeyresult-addresses": "The bitcoin addresses associated with this script",
|
||||||
|
|
||||||
|
// Vout help.
|
||||||
|
"vout-value": "The amount in BTC",
|
||||||
|
"vout-n": "The index of this transaction output",
|
||||||
|
"vout-scriptPubKey": "The public key script used to pay coins as a JSON object",
|
||||||
|
|
||||||
|
// TxRawDecodeResult help.
|
||||||
|
"txrawdecoderesult-txid": "The hash of the transaction",
|
||||||
|
"txrawdecoderesult-version": "The transaction version",
|
||||||
|
"txrawdecoderesult-locktime": "The transaction lock time",
|
||||||
|
"txrawdecoderesult-vin": "The transaction inputs as JSON objects",
|
||||||
|
"txrawdecoderesult-vout": "The transaction outputs as JSON objects",
|
||||||
|
|
||||||
|
// DecodeRawTransactionCmd help.
|
||||||
|
"decoderawtransaction--synopsis": "Returns a JSON object representing the provided serialized, hex-encoded transaction.",
|
||||||
|
"decoderawtransaction-hextx": "Serialized, hex-encoded transaction",
|
||||||
|
|
||||||
|
// DecodeScriptResult help.
|
||||||
|
"decodescriptresult-asm": "Disassembly of the script",
|
||||||
|
"decodescriptresult-reqSigs": "The number of required signatures",
|
||||||
|
"decodescriptresult-type": "The type of the script (e.g. 'pubkeyhash')",
|
||||||
|
"decodescriptresult-addresses": "The bitcoin addresses associated with this script",
|
||||||
|
"decodescriptresult-p2sh": "The script hash for use in pay-to-script-hash transactions",
|
||||||
|
|
||||||
|
// DecodeScriptCmd help.
|
||||||
|
"decodescript--synopsis": "Returns a JSON object with information about the provided hex-encoded script.",
|
||||||
|
"decodescript-hexscript": "Hex-encoded script",
|
||||||
|
|
||||||
|
// GetAddedNodeInfoResultAddr help.
|
||||||
|
"getaddednodeinforesultaddr-address": "The ip address for this DNS entry",
|
||||||
|
"getaddednodeinforesultaddr-connected": "The connection 'direction' (inbound/outbound/false)",
|
||||||
|
|
||||||
|
// GetAddedNodeInfoResult help.
|
||||||
|
"getaddednodeinforesult-addednode": "The ip address or domain of the added peer",
|
||||||
|
"getaddednodeinforesult-connected": "Whether or not the peer is currently connected",
|
||||||
|
"getaddednodeinforesult-addresses": "DNS lookup and connection information about the peer",
|
||||||
|
|
||||||
|
// GetAddedNodeInfo help.
|
||||||
|
"getaddednodeinfo--synopsis": "Returns information about manually added (persistent) peers.",
|
||||||
|
"getaddednodeinfo-dns": "Specifies whether the returned data is a JSON object including DNS and connection information, or just a list of added peers",
|
||||||
|
"getaddednodeinfo-node": "Only return information about this specific peer instead of all added peers",
|
||||||
|
"getaddednodeinfo--condition0": "dns=false",
|
||||||
|
"getaddednodeinfo--condition1": "dns=true",
|
||||||
|
"getaddednodeinfo--result0": "List of added peers",
|
||||||
|
|
||||||
|
// GetBestBlockResult help.
|
||||||
|
"getbestblockresult-hash": "Hex-encoded bytes of the best block hash",
|
||||||
|
"getbestblockresult-height": "Height of the best block",
|
||||||
|
|
||||||
|
// GetBestBlockCmd help.
|
||||||
|
"getbestblock--synopsis": "Get block height and hash of best block in the main chain.",
|
||||||
|
"getbestblock--result0": "Get block height and hash of best block in the main chain.",
|
||||||
|
|
||||||
|
// GetBestBlockHashCmd help.
|
||||||
|
"getbestblockhash--synopsis": "Returns the hash of the of the best (most recent) block in the longest block chain.",
|
||||||
|
"getbestblockhash--result0": "The hex-encoded block hash",
|
||||||
|
|
||||||
|
// GetBlockCmd help.
|
||||||
|
"getblock--synopsis": "Returns information about a block given its hash.",
|
||||||
|
"getblock-hash": "The hash of the block",
|
||||||
|
"getblock-verbose": "Specifies the block is returned as a JSON object instead of hex-encoded string",
|
||||||
|
"getblock-verbosetx": "Specifies that each transaction is returned as a JSON object and only applies if the verbose flag is true (btcd extension)",
|
||||||
|
"getblock--condition0": "verbose=false",
|
||||||
|
"getblock--condition1": "verbose=true",
|
||||||
|
"getblock--result0": "Hex-encoded bytes of the serialized block",
|
||||||
|
|
||||||
|
// TxRawResult help.
|
||||||
|
"txrawresult-hex": "Hex-encoded transaction",
|
||||||
|
"txrawresult-txid": "The hash of the transaction",
|
||||||
|
"txrawresult-version": "The transaction version",
|
||||||
|
"txrawresult-locktime": "The transaction lock time",
|
||||||
|
"txrawresult-vin": "The transaction inputs as JSON objects",
|
||||||
|
"txrawresult-vout": "The transaction outputs as JSON objects",
|
||||||
|
"txrawresult-blockhash": "Hash of the block the transaction is part of",
|
||||||
|
"txrawresult-confirmations": "Number of confirmations of the block",
|
||||||
|
"txrawresult-time": "Transaction time in seconds since 1 Jan 1970 GMT",
|
||||||
|
"txrawresult-blocktime": "Block time in seconds since the 1 Jan 1970 GMT",
|
||||||
|
|
||||||
|
// GetBlockVerboseResult help.
|
||||||
|
"getblockverboseresult-hash": "The hash of the block (same as provided)",
|
||||||
|
"getblockverboseresult-confirmations": "The number of confirmations",
|
||||||
|
"getblockverboseresult-size": "The size of the block",
|
||||||
|
"getblockverboseresult-height": "The height of the block in the block chain",
|
||||||
|
"getblockverboseresult-version": "The block version",
|
||||||
|
"getblockverboseresult-merkleroot": "Root hash of the merkle tree",
|
||||||
|
"getblockverboseresult-tx": "The transaction hashes (only when verbosetx=false)",
|
||||||
|
"getblockverboseresult-rawtx": "The transactions as JSON objects (only when verbosetx=true)",
|
||||||
|
"getblockverboseresult-time": "The block time in seconds since 1 Jan 1970 GMT",
|
||||||
|
"getblockverboseresult-nonce": "The block nonce",
|
||||||
|
"getblockverboseresult-bits": "The bits which represent the block difficulty",
|
||||||
|
"getblockverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty",
|
||||||
|
"getblockverboseresult-previousblockhash": "The hash of the previous block",
|
||||||
|
"getblockverboseresult-nextblockhash": "The hash of the next block",
|
||||||
|
|
||||||
|
// GetBlockCountCmd help.
|
||||||
|
"getblockcount--synopsis": "Returns the number of blocks in the longest block chain.",
|
||||||
|
"getblockcount--result0": "The current block count",
|
||||||
|
|
||||||
|
// GetBlockHashCmd help.
|
||||||
|
"getblockhash--synopsis": "Returns hash of the block in best block chain at the given height.",
|
||||||
|
"getblockhash-index": "The block height",
|
||||||
|
"getblockhash--result0": "The block hash",
|
||||||
|
|
||||||
|
// TemplateRequest help.
|
||||||
|
"templaterequest-mode": "This is 'template', 'proposal', or omitted",
|
||||||
|
"templaterequest-capabilities": "List of capabilities",
|
||||||
|
"templaterequest-longpollid": "The long poll ID of a job to monitor for expiration; required and valid only for long poll requests ",
|
||||||
|
"templaterequest-sigoplimit": "Number of signature operations allowed in blocks (this parameter is ignored)",
|
||||||
|
"templaterequest-sizelimit": "Number of bytes allowed in blocks (this parameter is ignored)",
|
||||||
|
"templaterequest-maxversion": "Highest supported block version number (this parameter is ignored)",
|
||||||
|
"templaterequest-target": "The desired target for the block template (this parameter is ignored)",
|
||||||
|
"templaterequest-data": "Hex-encoded block data (only for mode=proposal)",
|
||||||
|
"templaterequest-workid": "The server provided workid if provided in block template (not applicable)",
|
||||||
|
|
||||||
|
// GetBlockTemplateResultTx help.
|
||||||
|
"getblocktemplateresulttx-data": "Hex-encoded transaction data (byte-for-byte)",
|
||||||
|
"getblocktemplateresulttx-hash": "Hex-encoded transaction hash (little endian if treated as a 256-bit number)",
|
||||||
|
"getblocktemplateresulttx-depends": "Other transactions before this one (by 1-based index in the 'transactions' list) that must be present in the final block if this one is",
|
||||||
|
"getblocktemplateresulttx-fee": "Difference in value between transaction inputs and outputs (in Satoshi)",
|
||||||
|
"getblocktemplateresulttx-sigops": "Total number of signature operations as counted for purposes of block limits",
|
||||||
|
|
||||||
|
// GetBlockTemplateResultAux help.
|
||||||
|
"getblocktemplateresultaux-flags": "Hex-encoded byte-for-byte data to include in the coinbase signature script",
|
||||||
|
|
||||||
|
// GetBlockTemplateResult help.
|
||||||
|
"getblocktemplateresult-bits": "Hex-encoded compressed difficulty",
|
||||||
|
"getblocktemplateresult-curtime": "Current time as seen by the server (recommended for block time); must fall within mintime/maxtime rules",
|
||||||
|
"getblocktemplateresult-height": "Height of the block to be solved",
|
||||||
|
"getblocktemplateresult-previousblockhash": "Hex-encoded big-endian hash of the previous block",
|
||||||
|
"getblocktemplateresult-sigoplimit": "Number of sigops allowed in blocks ",
|
||||||
|
"getblocktemplateresult-sizelimit": "Number of bytes allowed in blocks",
|
||||||
|
"getblocktemplateresult-transactions": "Array of transactions as JSON objects",
|
||||||
|
"getblocktemplateresult-version": "The block version",
|
||||||
|
"getblocktemplateresult-coinbaseaux": "Data that should be included in the coinbase signature script",
|
||||||
|
"getblocktemplateresult-coinbasetxn": "Information about the coinbase transaction",
|
||||||
|
"getblocktemplateresult-coinbasevalue": "Total amount available for the coinbase in Satoshi",
|
||||||
|
"getblocktemplateresult-workid": "This value must be returned with result if provided (not provided)",
|
||||||
|
"getblocktemplateresult-longpollid": "Identifier for long poll request which allows monitoring for expiration",
|
||||||
|
"getblocktemplateresult-longpolluri": "An alternate URI to use for long poll requests if provided (not provided)",
|
||||||
|
"getblocktemplateresult-submitold": "Not applicable",
|
||||||
|
"getblocktemplateresult-target": "Hex-encoded big-endian number which valid results must be less than",
|
||||||
|
"getblocktemplateresult-expires": "Maximum number of seconds (starting from when the server sent the response) this work is valid for",
|
||||||
|
"getblocktemplateresult-maxtime": "Maximum allowed time",
|
||||||
|
"getblocktemplateresult-mintime": "Minimum allowed time",
|
||||||
|
"getblocktemplateresult-mutable": "List of mutations the server explicitly allows",
|
||||||
|
"getblocktemplateresult-noncerange": "Two concatenated hex-encoded big-endian 32-bit integers which represent the valid ranges of nonces the miner may scan",
|
||||||
|
"getblocktemplateresult-capabilities": "List of server capabilities including 'proposal' to indicate support for block proposals",
|
||||||
|
"getblocktemplateresult-reject-reason": "Reason the proposal was invalid as-is (only applies to proposal responses)",
|
||||||
|
|
||||||
|
// GetBlockTemplateCmd help.
|
||||||
|
"getblocktemplate--synopsis": "Returns a JSON object with information necessary to construct a block to mine or accepts a proposal to validate.\n" +
|
||||||
|
"See BIP0022 and BIP0023 for the full specification.",
|
||||||
|
"getblocktemplate-request": "Request object which controls the mode and several parameters",
|
||||||
|
"getblocktemplate--condition0": "mode=template",
|
||||||
|
"getblocktemplate--condition1": "mode=proposal, rejected",
|
||||||
|
"getblocktemplate--condition2": "mode=proposal, accepted",
|
||||||
|
"getblocktemplate--result1": "An error string which represents why the proposal was rejected or nothing if accepted",
|
||||||
|
|
||||||
|
// GetConnectionCountCmd help.
|
||||||
|
"getconnectioncount--synopsis": "Returns the number of active connections to other peers.",
|
||||||
|
"getconnectioncount--result0": "The number of connections",
|
||||||
|
|
||||||
|
// GetCurrentNetCmd help.
|
||||||
|
"getcurrentnet--synopsis": "Get bitcoin network the server is running on.",
|
||||||
|
"getcurrentnet--result0": "The network identifer",
|
||||||
|
|
||||||
|
// GetDifficultyCmd help.
|
||||||
|
"getdifficulty--synopsis": "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.",
|
||||||
|
"getdifficulty--result0": "The difficulty",
|
||||||
|
|
||||||
|
// GetGenerateCmd help.
|
||||||
|
"getgenerate--synopsis": "Returns if the server is set to generate coins (mine) or not.",
|
||||||
|
"getgenerate--result0": "True if mining, false if not",
|
||||||
|
|
||||||
|
// GetHashesPerSecCmd help.
|
||||||
|
"gethashespersec--synopsis": "Returns a recent hashes per second performance measurement while generating coins (mining).",
|
||||||
|
"gethashespersec--result0": "The number of hashes per second",
|
||||||
|
|
||||||
|
// InfoChainResult help.
|
||||||
|
"infochainresult-version": "The version of the server",
|
||||||
|
"infochainresult-protocolversion": "The latest supported protocol version",
|
||||||
|
"infochainresult-blocks": "The number of blocks processed",
|
||||||
|
"infochainresult-timeoffset": "The time offset",
|
||||||
|
"infochainresult-connections": "The number of connected peers",
|
||||||
|
"infochainresult-proxy": "The proxy used by the server",
|
||||||
|
"infochainresult-difficulty": "The current target difficulty",
|
||||||
|
"infochainresult-testnet": "Whether or not server is using testnet",
|
||||||
|
"infochainresult-relayfee": "The minimum relay fee for non-free transactions in BTC/KB",
|
||||||
|
"infochainresult-errors": "Any current errors",
|
||||||
|
|
||||||
|
// InfoWalletResult help.
|
||||||
|
"infowalletresult-version": "The version of the server",
|
||||||
|
"infowalletresult-protocolversion": "The latest supported protocol version",
|
||||||
|
"infowalletresult-walletversion": "The version of the wallet server",
|
||||||
|
"infowalletresult-balance": "The total bitcoin balance of the wallet",
|
||||||
|
"infowalletresult-blocks": "The number of blocks processed",
|
||||||
|
"infowalletresult-timeoffset": "The time offset",
|
||||||
|
"infowalletresult-connections": "The number of connected peers",
|
||||||
|
"infowalletresult-proxy": "The proxy used by the server",
|
||||||
|
"infowalletresult-difficulty": "The current target difficulty",
|
||||||
|
"infowalletresult-testnet": "Whether or not server is using testnet",
|
||||||
|
"infowalletresult-keypoololdest": "Seconds since 1 Jan 1970 GMT of the oldest pre-generated key in the key pool",
|
||||||
|
"infowalletresult-keypoolsize": "The number of new keys that are pre-generated",
|
||||||
|
"infowalletresult-unlocked_until": "The timestamp in seconds since 1 Jan 1970 GMT that the wallet is unlocked for transfers, or 0 if the wallet is locked",
|
||||||
|
"infowalletresult-paytxfee": "The transaction fee set in BTC/KB",
|
||||||
|
"infowalletresult-relayfee": "The minimum relay fee for non-free transactions in BTC/KB",
|
||||||
|
"infowalletresult-errors": "Any current errors",
|
||||||
|
|
||||||
|
// GetInfoCmd help.
|
||||||
|
"getinfo--synopsis": "Returns a JSON object containing various state info.",
|
||||||
|
|
||||||
|
// GetMiningInfoResult help.
|
||||||
|
"getmininginforesult-blocks": "Height of the latest best block",
|
||||||
|
"getmininginforesult-currentblocksize": "Size of the latest best block",
|
||||||
|
"getmininginforesult-currentblocktx": "Number of transactions in the latest best block",
|
||||||
|
"getmininginforesult-difficulty": "Current target difficulty",
|
||||||
|
"getmininginforesult-errors": "Any current errors",
|
||||||
|
"getmininginforesult-generate": "Whether or not server is set to generate coins",
|
||||||
|
"getmininginforesult-genproclimit": "Number of processors to use for coin generation (-1 when disabled)",
|
||||||
|
"getmininginforesult-hashespersec": "Recent hashes per second performance measurement while generating coins",
|
||||||
|
"getmininginforesult-networkhashps": "Estimated network hashes per second for the most recent blocks",
|
||||||
|
"getmininginforesult-pooledtx": "Number of transactions in the memory pool",
|
||||||
|
"getmininginforesult-testnet": "Whether or not server is using testnet",
|
||||||
|
|
||||||
|
// GetMiningInfoCmd help.
|
||||||
|
"getmininginfo--synopsis": "Returns a JSON object containing mining-related information.",
|
||||||
|
|
||||||
|
// GetNetworkHashPSCmd help.
|
||||||
|
"getnetworkhashps--synopsis": "Returns the estimated network hashes per second for the block heights provided by the parameters.",
|
||||||
|
"getnetworkhashps-blocks": "The number of blocks, or -1 for blocks since last difficulty change",
|
||||||
|
"getnetworkhashps-height": "Perform estimate ending with this height or -1 for current best chain block height",
|
||||||
|
"getnetworkhashps--result0": "Estimated hashes per second",
|
||||||
|
|
||||||
|
// GetNetTotalsCmd help.
|
||||||
|
"getnettotals--synopsis": "Returns a JSON object containing network traffic statistics.",
|
||||||
|
|
||||||
|
// GetNetTotalsResult help.
|
||||||
|
"getnettotalsresult-totalbytesrecv": "Total bytes received",
|
||||||
|
"getnettotalsresult-totalbytessent": "Total bytes sent",
|
||||||
|
"getnettotalsresult-timemillis": "Number of milliseconds since 1 Jan 1970 GMT",
|
||||||
|
|
||||||
|
// GetPeerInfoResult help.
|
||||||
|
"getpeerinforesult-addr": "The ip address and port of the peer",
|
||||||
|
"getpeerinforesult-addrlocal": "Local address",
|
||||||
|
"getpeerinforesult-services": "Services bitmask which represents the services supported by the peer",
|
||||||
|
"getpeerinforesult-lastsend": "Time the last message was received in seconds since 1 Jan 1970 GMT",
|
||||||
|
"getpeerinforesult-lastrecv": "Time the last message was sent in seconds since 1 Jan 1970 GMT",
|
||||||
|
"getpeerinforesult-bytessent": "Total bytes sent",
|
||||||
|
"getpeerinforesult-bytesrecv": "Total bytes received",
|
||||||
|
"getpeerinforesult-conntime": "Time the connection was made in seconds since 1 Jan 1970 GMT",
|
||||||
|
"getpeerinforesult-pingtime": "Number of microseconds the last ping took",
|
||||||
|
"getpeerinforesult-pingwait": "Number of microseconds a queued ping has been waiting for a response",
|
||||||
|
"getpeerinforesult-version": "The protocol version of the peer",
|
||||||
|
"getpeerinforesult-subver": "The user agent of the peer",
|
||||||
|
"getpeerinforesult-inbound": "Whether or not the peer is an inbound connection",
|
||||||
|
"getpeerinforesult-startingheight": "The latest block height the peer knew about when the connection was established",
|
||||||
|
"getpeerinforesult-currentheight": "The current height of the peer",
|
||||||
|
"getpeerinforesult-banscore": "The ban score",
|
||||||
|
"getpeerinforesult-syncnode": "Whether or not the peer is the sync peer",
|
||||||
|
|
||||||
|
// GetPeerInfoCmd help.
|
||||||
|
"getpeerinfo--synopsis": "Returns data about each connected network peer as an array of json objects.",
|
||||||
|
|
||||||
|
// GetRawMempoolVerboseResult help.
|
||||||
|
"getrawmempoolverboseresult-size": "Transaction size in bytes",
|
||||||
|
"getrawmempoolverboseresult-fee": "Transaction fee in bitcoins",
|
||||||
|
"getrawmempoolverboseresult-time": "Local time transaction entered pool in seconds since 1 Jan 1970 GMT",
|
||||||
|
"getrawmempoolverboseresult-height": "Block height when transaction entered the pool",
|
||||||
|
"getrawmempoolverboseresult-startingpriority": "Priority when transaction entered the pool",
|
||||||
|
"getrawmempoolverboseresult-currentpriority": "Current priority",
|
||||||
|
"getrawmempoolverboseresult-depends": "Unconfirmed transactions used as inputs for this transaction",
|
||||||
|
|
||||||
|
// GetRawMempoolCmd help.
|
||||||
|
"getrawmempool--synopsis": "Returns information about all of the transactions currently in the memory pool.",
|
||||||
|
"getrawmempool-verbose": "Returns JSON object when true or an array of transaction hashes when false",
|
||||||
|
"getrawmempool--condition0": "verbose=false",
|
||||||
|
"getrawmempool--condition1": "verbose=true",
|
||||||
|
"getrawmempool--result0": "Array of transaction hashes",
|
||||||
|
|
||||||
|
// GetRawTransactionCmd help.
|
||||||
|
"getrawtransaction--synopsis": "Returns information about a transaction given its hash.",
|
||||||
|
"getrawtransaction-txid": "The hash of the transaction",
|
||||||
|
"getrawtransaction-verbose": "Specifies the transaction is returned as a JSON object instead of a hex-encoded string",
|
||||||
|
"getrawtransaction--condition0": "verbose=false",
|
||||||
|
"getrawtransaction--condition1": "verbose=true",
|
||||||
|
"getrawtransaction--result0": "Hex-encoded bytes of the serialized transaction",
|
||||||
|
|
||||||
|
// GetTxOutResult help.
|
||||||
|
"gettxoutresult-bestblock": "The block hash that contains the transaction output",
|
||||||
|
"gettxoutresult-confirmations": "The number of confirmations",
|
||||||
|
"gettxoutresult-value": "The transaction amount in BTC",
|
||||||
|
"gettxoutresult-scriptPubKey": "The public key script used to pay coins as a JSON object",
|
||||||
|
"gettxoutresult-version": "The transaction version",
|
||||||
|
"gettxoutresult-coinbase": "Whether or not the transaction is a coinbase",
|
||||||
|
|
||||||
|
// GetTxOutCmd help.
|
||||||
|
"gettxout--synopsis": "Returns information about an unspent transaction output..",
|
||||||
|
"gettxout-txid": "The hash of the transaction",
|
||||||
|
"gettxout-vout": "The index of the output",
|
||||||
|
"gettxout-includemempool": "Include the mempool when true",
|
||||||
|
|
||||||
|
// GetWorkResult help.
|
||||||
|
"getworkresult-data": "Hex-encoded block data",
|
||||||
|
"getworkresult-hash1": "(DEPRECATED) Hex-encoded formatted hash buffer",
|
||||||
|
"getworkresult-midstate": "(DEPRECATED) Hex-encoded precomputed hash state after hashing first half of the data",
|
||||||
|
"getworkresult-target": "Hex-encoded little-endian hash target",
|
||||||
|
|
||||||
|
// GetWorkCmd help.
|
||||||
|
"getwork--synopsis": "(DEPRECATED - Use getblocktemplate instead) Returns formatted hash data to work on or checks and submits solved data.",
|
||||||
|
"getwork-data": "Hex-encoded data to check",
|
||||||
|
"getwork--condition0": "no data provided",
|
||||||
|
"getwork--condition1": "data provided",
|
||||||
|
"getwork--result1": "Whether or not the solved data is valid and was added to the chain",
|
||||||
|
|
||||||
|
// HelpCmd help.
|
||||||
|
"help--synopsis": "Returns a list of all commands or help for a specified command.",
|
||||||
|
"help-command": "The command to retrieve help for",
|
||||||
|
"help--condition0": "no command provided",
|
||||||
|
"help--condition1": "command specified",
|
||||||
|
"help--result0": "List of commands",
|
||||||
|
"help--result1": "Help for specified command",
|
||||||
|
|
||||||
|
// PingCmd help.
|
||||||
|
"ping--synopsis": "Queues a ping to be sent to each connected peer.\n" +
|
||||||
|
"Ping times are provided by getpeerinfo via the pingtime and pingwait fields.",
|
||||||
|
|
||||||
|
// SearchRawTransactionsCmd help.
|
||||||
|
"searchrawtransactions--synopsis": "Returns raw data for transactions involving the passed address.\n" +
|
||||||
|
"Returned transactions are pulled from both the database, and transactions currently in the mempool.\n" +
|
||||||
|
"Transactions pulled from the mempool will have the 'confirmations' field set to 0.\n" +
|
||||||
|
"Usage of this RPC requires the optional --addrindex flag to be activated, otherwise all responses will simply return with an error stating the address index has not yet been built.\n" +
|
||||||
|
"Similarly, until the address index has caught up with the current best height, all requests will return an error response in order to avoid serving stale data.",
|
||||||
|
"searchrawtransactions-address": "The Bitcoin address to search for",
|
||||||
|
"searchrawtransactions-verbose": "Specifies the transaction is returned as a JSON object instead of hex-encoded string",
|
||||||
|
"searchrawtransactions-skip": "The number of leading transactions to leave out of the final response",
|
||||||
|
"searchrawtransactions-count": "The maximum number of transactions to return",
|
||||||
|
"searchrawtransactions--condition0": "verbose=0",
|
||||||
|
"searchrawtransactions--condition1": "verbose=1",
|
||||||
|
"searchrawtransactions--result0": "Hex-encoded serialized transaction",
|
||||||
|
|
||||||
|
// SendRawTransactionCmd help.
|
||||||
|
"sendrawtransaction--synopsis": "Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.",
|
||||||
|
"sendrawtransaction-hextx": "Serialized, hex-encoded signed transaction",
|
||||||
|
"sendrawtransaction-allowhighfees": "Whether or not to allow insanely high fees (btcd does not yet implement this parameter, so it has no effect)",
|
||||||
|
"sendrawtransaction--result0": "The hash of the transaction",
|
||||||
|
|
||||||
|
// SetGenerateCmd help.
|
||||||
|
"setgenerate--synopsis": "Set the server to generate coins (mine) or not.",
|
||||||
|
"setgenerate-generate": "Use true to enable generation, false to disable it",
|
||||||
|
"setgenerate-genproclimit": "The number of processors (cores) to limit generation to or -1 for default",
|
||||||
|
|
||||||
|
// StopCmd help.
|
||||||
|
"stop--synopsis": "Shutdown btcd.",
|
||||||
|
"stop--result0": "The string 'btcd stopping.'",
|
||||||
|
|
||||||
|
// SubmitBlockOptions help.
|
||||||
|
"submitblockoptions-workid": "This parameter is currently ignored",
|
||||||
|
|
||||||
|
// SubmitBlockCmd help.
|
||||||
|
"submitblock--synopsis": "Attempts to submit a new serialized, hex-encoded block to the network.",
|
||||||
|
"submitblock-hexblock": "Serialized, hex-encoded block",
|
||||||
|
"submitblock-options": "This parameter is currently ignored",
|
||||||
|
"submitblock--condition0": "Block successfully submitted",
|
||||||
|
"submitblock--condition1": "Block rejected",
|
||||||
|
"submitblock--result1": "The reason the block was rejected",
|
||||||
|
|
||||||
|
// ValidateAddressResult help.
|
||||||
|
"validateaddresschainresult-isvalid": "Whether or not the address is valid",
|
||||||
|
"validateaddresschainresult-address": "The bitcoin address (only when isvalid is true)",
|
||||||
|
|
||||||
|
// ValidateAddressCmd help.
|
||||||
|
"validateaddress--synopsis": "Verify an address is valid.",
|
||||||
|
"validateaddress-address": "Bitcoin address to validate",
|
||||||
|
|
||||||
|
// VerifyChainCmd help.
|
||||||
|
"verifychain--synopsis": "Verifies the block chain database.\n" +
|
||||||
|
"The actual checks performed by the checklevel parameter are implementation specific.\n" +
|
||||||
|
"For btcd this is:\n" +
|
||||||
|
"checklevel=0 - Look up each block and ensure it can be loaded from the database.\n" +
|
||||||
|
"checklevel=1 - Perform basic context-free sanity checks on each block.",
|
||||||
|
"verifychain-checklevel": "How thorough the block verification is",
|
||||||
|
"verifychain-checkdepth": "The number of blocks to check",
|
||||||
|
"verifychain--result0": "Whether or not the chain verified",
|
||||||
|
|
||||||
|
// VerifyMessageCmd help.
|
||||||
|
"verifymessage--synopsis": "Verify a signed message.",
|
||||||
|
"verifymessage-address": "The bitcoin address to use for the signature",
|
||||||
|
"verifymessage-signature": "The base-64 encoded signature provided by the signer",
|
||||||
|
"verifymessage-message": "The signed message",
|
||||||
|
"verifymessage--result0": "Whether or not the signature verified",
|
||||||
|
|
||||||
|
// -------- Websocket-specific help --------
|
||||||
|
|
||||||
|
// NotifyBlocksCmd help.
|
||||||
|
"notifyblocks--synopsis": "Request notifications for whenever a block is connected or disconnected from the main (best) chain.",
|
||||||
|
|
||||||
|
// NotifyNewTransactionsCmd help.
|
||||||
|
"notifynewtransactions--synopsis": "Send either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.",
|
||||||
|
"notifynewtransactions-verbose": "Specifies which type of notification to receive. If verbose is true, then the caller receives txacceptedverbose, otherwise the caller receives txaccepted",
|
||||||
|
|
||||||
|
// NotifyReceivedCmd help.
|
||||||
|
"notifyreceived--synopsis": "Send a recvtx notification when a transaction added to mempool or appears in a newly-attached block contains a txout pkScript sending to any of the passed addresses.\n" +
|
||||||
|
"Matching outpoints are automatically registered for redeemingtx notifications.",
|
||||||
|
"notifyreceived-addresses": "List of address to receive notifications about",
|
||||||
|
|
||||||
|
// OutPoint help.
|
||||||
|
"outpoint-hash": "The hex-encoded bytes of the outpoint hash",
|
||||||
|
"outpoint-index": "The index of the outpoint",
|
||||||
|
|
||||||
|
// NotifySpentCmd help.
|
||||||
|
"notifyspent--synopsis": "Send a redeemingtx notification when a transaction spending an outpoint appears in mempool (if relayed to this btcd instance) and when such a transaction first appears in a newly-attached block.",
|
||||||
|
"notifyspent-outpoints": "List of transaction outpoints to monitor.",
|
||||||
|
|
||||||
|
// Rescan help.
|
||||||
|
"rescan--synopsis": "Rescan block chain for transactions to addresses.\n" +
|
||||||
|
"When the endblock parameter is omitted, the rescan continues through the best block in the main chain.\n" +
|
||||||
|
"Rescan results are sent as recvtx and redeemingtx notifications.\n" +
|
||||||
|
"This call returns once the rescan completes.",
|
||||||
|
"rescan-beginblock": "Hash of the first block to begin rescanning",
|
||||||
|
"rescan-addresses": "List of addresses to include in the rescan",
|
||||||
|
"rescan-outpoints": "List of transaction outpoints to include in the rescan",
|
||||||
|
"rescan-endblock": "Hash of final block to rescan",
|
||||||
|
}
|
||||||
|
|
||||||
|
// rpcResultTypes specifies the result types that each RPC command can return.
|
||||||
|
// This information is used to generate the help. Each result type must be a
|
||||||
|
// pointer to the type (or nil to indicate no return value).
|
||||||
|
var rpcResultTypes = map[string][]interface{}{
|
||||||
|
"addnode": nil,
|
||||||
|
"createrawtransaction": []interface{}{(*string)(nil)},
|
||||||
|
"debuglevel": []interface{}{(*string)(nil), (*string)(nil)},
|
||||||
|
"decoderawtransaction": []interface{}{(*btcjson.TxRawDecodeResult)(nil)},
|
||||||
|
"decodescript": []interface{}{(*btcjson.DecodeScriptResult)(nil)},
|
||||||
|
"getaddednodeinfo": []interface{}{(*[]string)(nil), (*[]btcjson.GetAddedNodeInfoResult)(nil)},
|
||||||
|
"getbestblock": []interface{}{(*btcjson.GetBestBlockResult)(nil)},
|
||||||
|
"getbestblockhash": []interface{}{(*string)(nil)},
|
||||||
|
"getblock": []interface{}{(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)},
|
||||||
|
"getblockcount": []interface{}{(*int64)(nil)},
|
||||||
|
"getblockhash": []interface{}{(*string)(nil)},
|
||||||
|
"getblocktemplate": []interface{}{(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
|
||||||
|
"getconnectioncount": []interface{}{(*int32)(nil)},
|
||||||
|
"getcurrentnet": []interface{}{(*uint32)(nil)},
|
||||||
|
"getdifficulty": []interface{}{(*float64)(nil)},
|
||||||
|
"getgenerate": []interface{}{(*bool)(nil)},
|
||||||
|
"gethashespersec": []interface{}{(*float64)(nil)},
|
||||||
|
"getinfo": []interface{}{(*btcjson.InfoChainResult)(nil)},
|
||||||
|
"getmininginfo": []interface{}{(*btcjson.GetMiningInfoResult)(nil)},
|
||||||
|
"getnettotals": []interface{}{(*btcjson.GetNetTotalsResult)(nil)},
|
||||||
|
"getnetworkhashps": []interface{}{(*int64)(nil)},
|
||||||
|
"getpeerinfo": []interface{}{(*[]btcjson.GetPeerInfoResult)(nil)},
|
||||||
|
"getrawmempool": []interface{}{(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)},
|
||||||
|
"getrawtransaction": []interface{}{(*string)(nil), (*btcjson.TxRawResult)(nil)},
|
||||||
|
"gettxout": []interface{}{(*btcjson.GetTxOutResult)(nil)},
|
||||||
|
"getwork": []interface{}{(*btcjson.GetWorkResult)(nil), (*bool)(nil)},
|
||||||
|
"help": []interface{}{(*string)(nil), (*string)(nil)},
|
||||||
|
"ping": nil,
|
||||||
|
"searchrawtransactions": []interface{}{(*string)(nil), (*[]btcjson.TxRawResult)(nil)},
|
||||||
|
"sendrawtransaction": []interface{}{(*string)(nil)},
|
||||||
|
"setgenerate": nil,
|
||||||
|
"stop": []interface{}{(*string)(nil)},
|
||||||
|
"submitblock": []interface{}{nil, (*string)(nil)},
|
||||||
|
"validateaddress": []interface{}{(*btcjson.ValidateAddressChainResult)(nil)},
|
||||||
|
"verifychain": []interface{}{(*bool)(nil)},
|
||||||
|
"verifymessage": []interface{}{(*bool)(nil)},
|
||||||
|
|
||||||
|
// Websocket commands.
|
||||||
|
"notifyblocks": nil,
|
||||||
|
"notifynewtransactions": nil,
|
||||||
|
"notifyreceived": nil,
|
||||||
|
"notifyspent": nil,
|
||||||
|
"rescan": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// helpCacher provides a concurrent safe type that provides help and usage for
|
||||||
|
// the RPC server commands and caches the results for future calls.
|
||||||
|
type helpCacher struct {
|
||||||
|
sync.Mutex
|
||||||
|
usage string
|
||||||
|
methodHelp map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// rpcMethodHelp returns an RPC help string for the provided method.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (c *helpCacher) rpcMethodHelp(method string) (string, error) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
// Return the cached method help if it exists.
|
||||||
|
if help, exists := c.methodHelp[method]; exists {
|
||||||
|
return help, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up the result types for the method.
|
||||||
|
resultTypes, ok := rpcResultTypes[method]
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("no result types specified for method " +
|
||||||
|
method)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate, cache, and return the help.
|
||||||
|
help, err := btcjson.GenerateHelp(method, helpDescsEnUS, resultTypes...)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
c.methodHelp[method] = help
|
||||||
|
return help, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// rpcUsage returns one-line usage for all support RPC commands.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (c *helpCacher) rpcUsage(includeWebsockets bool) (string, error) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
// Return the cached usage if it is available.
|
||||||
|
if c.usage != "" {
|
||||||
|
return c.usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a list of one-line usage for every command.
|
||||||
|
usageTexts := make([]string, 0, len(rpcHandlers))
|
||||||
|
for k := range rpcHandlers {
|
||||||
|
usage, err := btcjson.MethodUsageText(k)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
usageTexts = append(usageTexts, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include websockets commands if requested.
|
||||||
|
if includeWebsockets {
|
||||||
|
for k := range wsHandlers {
|
||||||
|
usage, err := btcjson.MethodUsageText(k)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
usageTexts = append(usageTexts, usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(sort.StringSlice(usageTexts))
|
||||||
|
c.usage = strings.Join(usageTexts, "\n")
|
||||||
|
return c.usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHelpCacher returns a new instance of a help cacher which provides help and
|
||||||
|
// usage for the RPC server commands and caches the results for future calls.
|
||||||
|
func newHelpCacher() *helpCacher {
|
||||||
|
return &helpCacher{
|
||||||
|
methodHelp: make(map[string]string),
|
||||||
|
}
|
||||||
|
}
|
65
rpcserverhelp_test.go
Normal file
65
rpcserverhelp_test.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// 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 main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// TestHelp ensures the help is reasonably accurate by checking that every
|
||||||
|
// command specified also has result types defined and the one-line usage and
|
||||||
|
// help text can be generated for them.
|
||||||
|
func TestHelp(t *testing.T) {
|
||||||
|
// Ensure there are result types specified for every handler.
|
||||||
|
for k := range rpcHandlers {
|
||||||
|
if _, ok := rpcResultTypes[k]; !ok {
|
||||||
|
t.Errorf("RPC handler defined for method '%v' without "+
|
||||||
|
"also specifying result types", k)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
for k := range wsHandlers {
|
||||||
|
if _, ok := rpcResultTypes[k]; !ok {
|
||||||
|
t.Errorf("RPC handler defined for method '%v' without "+
|
||||||
|
"also specifying result types", k)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the usage for every command can be generated without errors.
|
||||||
|
helpCacher := newHelpCacher()
|
||||||
|
if _, err := helpCacher.rpcUsage(true); err != nil {
|
||||||
|
t.Fatalf("Failed to generate one-line usage: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := helpCacher.rpcUsage(true); err != nil {
|
||||||
|
t.Fatalf("Failed to generate one-line usage (cached): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the help for every command can be generated without errors.
|
||||||
|
for k := range rpcHandlers {
|
||||||
|
if _, err := helpCacher.rpcMethodHelp(k); err != nil {
|
||||||
|
t.Errorf("Failed to generate help for method '%v': %v",
|
||||||
|
k, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := helpCacher.rpcMethodHelp(k); err != nil {
|
||||||
|
t.Errorf("Failed to generate help for method '%v'"+
|
||||||
|
"(cached): %v", k, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k := range wsHandlers {
|
||||||
|
if _, err := helpCacher.rpcMethodHelp(k); err != nil {
|
||||||
|
t.Errorf("Failed to generate help for method '%v': %v",
|
||||||
|
k, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := helpCacher.rpcMethodHelp(k); err != nil {
|
||||||
|
t.Errorf("Failed to generate help for method '%v'"+
|
||||||
|
"(cached): %v", k, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
389
rpcwebsocket.go
389
rpcwebsocket.go
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
// Copyright (c) 2013-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.
|
||||||
|
|
||||||
|
@ -19,8 +19,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/ripemd160"
|
"golang.org/x/crypto/ripemd160"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/btcsuite/btcd/btcjson/v2/btcjson"
|
||||||
"github.com/btcsuite/btcd/btcjson/btcws"
|
|
||||||
"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"
|
||||||
|
@ -44,11 +43,14 @@ var timeZeroVal time.Time
|
||||||
|
|
||||||
// wsCommandHandler describes a callback function used to handle a specific
|
// wsCommandHandler describes a callback function used to handle a specific
|
||||||
// command.
|
// command.
|
||||||
type wsCommandHandler func(*wsClient, btcjson.Cmd) (interface{}, *btcjson.Error)
|
type wsCommandHandler func(*wsClient, interface{}) (interface{}, error)
|
||||||
|
|
||||||
// wsHandlers maps RPC command strings to appropriate websocket handler
|
// wsHandlers maps RPC command strings to appropriate websocket handler
|
||||||
// functions.
|
// functions. This is set by init because help references wsHandlers and thus
|
||||||
var wsHandlers = map[string]wsCommandHandler{
|
// causes a dependency loop.
|
||||||
|
var wsHandlers map[string]wsCommandHandler
|
||||||
|
var wsHandlersBeforeInit = map[string]wsCommandHandler{
|
||||||
|
"help": handleWebsocketHelp,
|
||||||
"notifyblocks": handleNotifyBlocks,
|
"notifyblocks": handleNotifyBlocks,
|
||||||
"notifynewtransactions": handleNotifyNewTransactions,
|
"notifynewtransactions": handleNotifyNewTransactions,
|
||||||
"notifyreceived": handleNotifyReceived,
|
"notifyreceived": handleNotifyReceived,
|
||||||
|
@ -412,8 +414,8 @@ func (*wsNotificationManager) notifyBlockConnected(clients map[chan struct{}]*ws
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify interested websocket clients about the connected block.
|
// Notify interested websocket clients about the connected block.
|
||||||
ntfn := btcws.NewBlockConnectedNtfn(hash.String(), int32(block.Height()))
|
ntfn := btcjson.NewBlockConnectedNtfn(hash.String(), int32(block.Height()))
|
||||||
marshalledJSON, err := json.Marshal(ntfn)
|
marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Error("Failed to marshal block connected notification: "+
|
rpcsLog.Error("Failed to marshal block connected notification: "+
|
||||||
"%v", err)
|
"%v", err)
|
||||||
|
@ -442,9 +444,9 @@ func (*wsNotificationManager) notifyBlockDisconnected(clients map[chan struct{}]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify interested websocket clients about the disconnected block.
|
// Notify interested websocket clients about the disconnected block.
|
||||||
ntfn := btcws.NewBlockDisconnectedNtfn(hash.String(),
|
ntfn := btcjson.NewBlockDisconnectedNtfn(hash.String(),
|
||||||
int32(block.Height()))
|
int32(block.Height()))
|
||||||
marshalledJSON, err := json.Marshal(ntfn)
|
marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Error("Failed to marshal block disconnected "+
|
rpcsLog.Error("Failed to marshal block disconnected "+
|
||||||
"notification: %v", err)
|
"notification: %v", err)
|
||||||
|
@ -478,31 +480,36 @@ func (m *wsNotificationManager) notifyForNewTx(clients map[chan struct{}]*wsClie
|
||||||
amount += txOut.Value
|
amount += txOut.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
ntfn := btcws.NewTxAcceptedNtfn(txShaStr, amount)
|
ntfn := btcjson.NewTxAcceptedNtfn(txShaStr, btcutil.Amount(amount).ToBTC())
|
||||||
marshalledJSON, err := json.Marshal(ntfn)
|
marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal tx notification: %s", err.Error())
|
rpcsLog.Errorf("Failed to marshal tx notification: %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var verboseNtfn *btcws.TxAcceptedVerboseNtfn
|
var verboseNtfn *btcjson.TxAcceptedVerboseNtfn
|
||||||
var marshalledJSONVerbose []byte
|
var marshalledJSONVerbose []byte
|
||||||
for _, wsc := range clients {
|
for _, wsc := range clients {
|
||||||
if wsc.verboseTxUpdates {
|
if wsc.verboseTxUpdates {
|
||||||
if verboseNtfn == nil {
|
if marshalledJSONVerbose != nil {
|
||||||
net := m.server.server.chainParams
|
wsc.QueueNotification(marshalledJSONVerbose)
|
||||||
rawTx, err := createTxRawResult(net, txShaStr,
|
continue
|
||||||
mtx, nil, 0, nil)
|
}
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
verboseNtfn = btcws.NewTxAcceptedVerboseNtfn(rawTx)
|
|
||||||
marshalledJSONVerbose, err = json.Marshal(verboseNtfn)
|
|
||||||
if err != nil {
|
|
||||||
rpcsLog.Errorf("Failed to marshal verbose tx notification: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
net := m.server.server.chainParams
|
||||||
|
rawTx, err := createTxRawResult(net, txShaStr, mtx, nil,
|
||||||
|
0, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
verboseNtfn = btcjson.NewTxAcceptedVerboseNtfn(*rawTx)
|
||||||
|
marshalledJSONVerbose, err = btcjson.MarshalCmd(nil,
|
||||||
|
verboseNtfn)
|
||||||
|
if err != nil {
|
||||||
|
rpcsLog.Errorf("Failed to marshal verbose tx "+
|
||||||
|
"notification: %s", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
wsc.QueueNotification(marshalledJSONVerbose)
|
wsc.QueueNotification(marshalledJSONVerbose)
|
||||||
} else {
|
} else {
|
||||||
|
@ -590,12 +597,12 @@ func txHexString(tx *btcutil.Tx) string {
|
||||||
|
|
||||||
// blockDetails creates a BlockDetails struct to include in btcws notifications
|
// blockDetails creates a BlockDetails struct to include in btcws notifications
|
||||||
// from a block and a transaction's block index.
|
// from a block and a transaction's block index.
|
||||||
func blockDetails(block *btcutil.Block, txIndex int) *btcws.BlockDetails {
|
func blockDetails(block *btcutil.Block, txIndex int) *btcjson.BlockDetails {
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
blockSha, _ := block.Sha() // never errors
|
blockSha, _ := block.Sha() // never errors
|
||||||
return &btcws.BlockDetails{
|
return &btcjson.BlockDetails{
|
||||||
Height: int32(block.Height()),
|
Height: int32(block.Height()),
|
||||||
Hash: blockSha.String(),
|
Hash: blockSha.String(),
|
||||||
Index: txIndex,
|
Index: txIndex,
|
||||||
|
@ -607,8 +614,8 @@ func blockDetails(block *btcutil.Block, txIndex int) *btcws.BlockDetails {
|
||||||
// with the passed parameters.
|
// with the passed parameters.
|
||||||
func newRedeemingTxNotification(txHex string, index int, block *btcutil.Block) ([]byte, error) {
|
func newRedeemingTxNotification(txHex string, index int, block *btcutil.Block) ([]byte, error) {
|
||||||
// Create and marshal the notification.
|
// Create and marshal the notification.
|
||||||
ntfn := btcws.NewRedeemingTxNtfn(txHex, blockDetails(block, index))
|
ntfn := btcjson.NewRedeemingTxNtfn(txHex, *blockDetails(block, index))
|
||||||
return json.Marshal(ntfn)
|
return btcjson.MarshalCmd(nil, ntfn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// notifyForTxOuts examines each transaction output, notifying interested
|
// notifyForTxOuts examines each transaction output, notifying interested
|
||||||
|
@ -641,9 +648,10 @@ func (m *wsNotificationManager) notifyForTxOuts(ops map[wire.OutPoint]map[chan s
|
||||||
if txHex == "" {
|
if txHex == "" {
|
||||||
txHex = txHexString(tx)
|
txHex = txHexString(tx)
|
||||||
}
|
}
|
||||||
ntfn := btcws.NewRecvTxNtfn(txHex, blockDetails(block, tx.Index()))
|
ntfn := btcjson.NewRecvTxNtfn(txHex, *blockDetails(block,
|
||||||
|
tx.Index()))
|
||||||
|
|
||||||
marshalledJSON, err := json.Marshal(ntfn)
|
marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal processedtx notification: %v", err)
|
rpcsLog.Errorf("Failed to marshal processedtx notification: %v", err)
|
||||||
continue
|
continue
|
||||||
|
@ -833,36 +841,6 @@ type wsResponse struct {
|
||||||
doneChan chan bool
|
doneChan chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMarshalledReply returns a new marshalled btcjson.Reply given the
|
|
||||||
// passed parameters. It will automatically convert errors that are not of
|
|
||||||
// the type *btcjson.Error to the appropriate type as needed.
|
|
||||||
func createMarshalledReply(id, result interface{}, replyErr error) ([]byte, error) {
|
|
||||||
var jsonErr *btcjson.Error
|
|
||||||
if replyErr != nil {
|
|
||||||
if jErr, ok := replyErr.(*btcjson.Error); ok {
|
|
||||||
jsonErr = jErr
|
|
||||||
} else {
|
|
||||||
jsonErr = &btcjson.Error{
|
|
||||||
Code: btcjson.ErrInternal.Code,
|
|
||||||
Message: replyErr.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response := btcjson.Reply{
|
|
||||||
Id: &id,
|
|
||||||
Result: result,
|
|
||||||
Error: jsonErr,
|
|
||||||
}
|
|
||||||
|
|
||||||
marshalledJSON, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return marshalledJSON, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// wsClient provides an abstraction for handling a websocket client. The
|
// wsClient provides an abstraction for handling a websocket client. The
|
||||||
// overall data flow is split into 3 main goroutines, a possible 4th goroutine
|
// overall data flow is split into 3 main goroutines, a possible 4th goroutine
|
||||||
// for long-running operations (only started if request is made), and a
|
// for long-running operations (only started if request is made), and a
|
||||||
|
@ -914,7 +892,7 @@ type wsClient struct {
|
||||||
|
|
||||||
// Networking infrastructure.
|
// Networking infrastructure.
|
||||||
asyncStarted bool
|
asyncStarted bool
|
||||||
asyncChan chan btcjson.Cmd
|
asyncChan chan *parsedRPCCmd
|
||||||
ntfnChan chan []byte
|
ntfnChan chan []byte
|
||||||
sendChan chan wsResponse
|
sendChan chan wsResponse
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
|
@ -923,22 +901,27 @@ type wsClient struct {
|
||||||
|
|
||||||
// handleMessage is the main handler for incoming requests. It enforces
|
// handleMessage is the main handler for incoming requests. It enforces
|
||||||
// authentication, parses the incoming json, looks up and executes handlers
|
// authentication, parses the incoming json, looks up and executes handlers
|
||||||
// (including pass through for standard RPC commands), sends the appropriate
|
// (including pass through for standard RPC commands), and sends the appropriate
|
||||||
// response. It also detects commands which are marked as long-running and
|
// response. It also detects commands which are marked as long-running and
|
||||||
// sends them off to the asyncHander for processing.
|
// sends them off to the asyncHander for processing.
|
||||||
func (c *wsClient) handleMessage(msg []byte) {
|
func (c *wsClient) handleMessage(msg []byte) {
|
||||||
if !c.authenticated {
|
if !c.authenticated {
|
||||||
// Disconnect immediately if the provided command fails to
|
// Disconnect immediately if the provided command fails to
|
||||||
// parse when the client is not already authenticated.
|
// parse when the client is not already authenticated.
|
||||||
cmd, jsonErr := parseCmd(msg)
|
var request btcjson.Request
|
||||||
if jsonErr != nil {
|
if err := json.Unmarshal(msg, &request); err != nil {
|
||||||
|
c.Disconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parsedCmd := parseCmd(&request)
|
||||||
|
if parsedCmd.err != nil {
|
||||||
c.Disconnect()
|
c.Disconnect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect immediately if the first command is not
|
// Disconnect immediately if the first command is not
|
||||||
// authenticate when not already authenticated.
|
// authenticate when not already authenticated.
|
||||||
authCmd, ok := cmd.(*btcws.AuthenticateCmd)
|
authCmd, ok := parsedCmd.cmd.(*btcjson.AuthenticateCmd)
|
||||||
if !ok {
|
if !ok {
|
||||||
rpcsLog.Warnf("Unauthenticated websocket message " +
|
rpcsLog.Warnf("Unauthenticated websocket message " +
|
||||||
"received")
|
"received")
|
||||||
|
@ -959,7 +942,7 @@ func (c *wsClient) handleMessage(msg []byte) {
|
||||||
c.authenticated = true
|
c.authenticated = true
|
||||||
|
|
||||||
// Marshal and send response.
|
// Marshal and send response.
|
||||||
reply, err := createMarshalledReply(authCmd.Id(), nil, nil)
|
reply, err := createMarshalledReply(parsedCmd.id, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal authenticate reply: "+
|
rpcsLog.Errorf("Failed to marshal authenticate reply: "+
|
||||||
"%v", err.Error())
|
"%v", err.Error())
|
||||||
|
@ -969,21 +952,16 @@ func (c *wsClient) handleMessage(msg []byte) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attmpt to parse the raw json into a known btcjson.Cmd.
|
// Attempt to parse the raw message into a JSON-RPC request.
|
||||||
cmd, jsonErr := parseCmd(msg)
|
var request btcjson.Request
|
||||||
if jsonErr != nil {
|
if err := json.Unmarshal(msg, &request); err != nil {
|
||||||
// Use the provided id for errors when a valid JSON-RPC message
|
jsonErr := &btcjson.RPCError{
|
||||||
// was parsed. Requests with no IDs are ignored.
|
Code: btcjson.ErrRPCParse.Code,
|
||||||
var id interface{}
|
Message: "Failed to parse request: " + err.Error(),
|
||||||
if cmd != nil {
|
|
||||||
id = cmd.Id()
|
|
||||||
if id == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal and send response.
|
// Marshal and send response.
|
||||||
reply, err := createMarshalledReply(id, nil, jsonErr)
|
reply, err := createMarshalledReply(nil, nil, jsonErr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal parse failure "+
|
rpcsLog.Errorf("Failed to marshal parse failure "+
|
||||||
"reply: %v", err)
|
"reply: %v", err)
|
||||||
|
@ -992,11 +970,30 @@ func (c *wsClient) handleMessage(msg []byte) {
|
||||||
c.SendMessage(reply, nil)
|
c.SendMessage(reply, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rpcsLog.Debugf("Received command <%s> from %s", cmd.Method(), c.addr)
|
// Requests with no ID (notifications) must not have a response per the
|
||||||
|
// JSON-RPC spec.
|
||||||
|
if request.ID == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse the JSON-RPC request into a known concrete command.
|
||||||
|
cmd := parseCmd(&request)
|
||||||
|
if cmd.err != nil {
|
||||||
|
// Marshal and send response.
|
||||||
|
reply, err := createMarshalledReply(cmd.id, nil, cmd.err)
|
||||||
|
if err != nil {
|
||||||
|
rpcsLog.Errorf("Failed to marshal parse failure "+
|
||||||
|
"reply: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SendMessage(reply, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rpcsLog.Debugf("Received command <%s> from %s", cmd.method, c.addr)
|
||||||
|
|
||||||
// Disconnect if already authenticated and another authenticate command
|
// Disconnect if already authenticated and another authenticate command
|
||||||
// is received.
|
// is received.
|
||||||
if _, ok := cmd.(*btcws.AuthenticateCmd); ok {
|
if _, ok := cmd.cmd.(*btcjson.AuthenticateCmd); ok {
|
||||||
rpcsLog.Warnf("Websocket client %s is already authenticated",
|
rpcsLog.Warnf("Websocket client %s is already authenticated",
|
||||||
c.addr)
|
c.addr)
|
||||||
c.Disconnect()
|
c.Disconnect()
|
||||||
|
@ -1005,7 +1002,7 @@ func (c *wsClient) handleMessage(msg []byte) {
|
||||||
|
|
||||||
// When the command is marked as a long-running command, send it off
|
// When the command is marked as a long-running command, send it off
|
||||||
// to the asyncHander goroutine for processing.
|
// to the asyncHander goroutine for processing.
|
||||||
if _, ok := wsAsyncHandlers[cmd.Method()]; ok {
|
if _, ok := wsAsyncHandlers[cmd.method]; ok {
|
||||||
// Start up the async goroutine for handling long-running
|
// Start up the async goroutine for handling long-running
|
||||||
// requests asynchonrously if needed.
|
// requests asynchonrously if needed.
|
||||||
if !c.asyncStarted {
|
if !c.asyncStarted {
|
||||||
|
@ -1020,28 +1017,28 @@ func (c *wsClient) handleMessage(msg []byte) {
|
||||||
|
|
||||||
// Lookup the websocket extension for the command and if it doesn't
|
// Lookup the websocket extension for the command and if it doesn't
|
||||||
// exist fallback to handling the command as a standard command.
|
// exist fallback to handling the command as a standard command.
|
||||||
wsHandler, ok := wsHandlers[cmd.Method()]
|
wsHandler, ok := wsHandlers[cmd.method]
|
||||||
if !ok {
|
if !ok {
|
||||||
// No websocket-specific handler so handle like a legacy
|
// No websocket-specific handler so handle like a legacy
|
||||||
// RPC connection.
|
// RPC connection.
|
||||||
response := c.server.standardCmdReply(cmd, nil)
|
result, jsonErr := c.server.standardCmdResult(cmd, nil)
|
||||||
reply, err := json.Marshal(response)
|
reply, err := createMarshalledReply(cmd.id, result, jsonErr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal reply for <%s> "+
|
rpcsLog.Errorf("Failed to marshal reply for <%s> "+
|
||||||
"command: %v", cmd.Method(), err)
|
"command: %v", cmd.method, err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.SendMessage(reply, nil)
|
c.SendMessage(reply, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke the handler and marshal and send response.
|
// Invoke the handler and marshal and send response.
|
||||||
result, jsonErr := wsHandler(c, cmd)
|
result, jsonErr := wsHandler(c, cmd.cmd)
|
||||||
reply, err := createMarshalledReply(cmd.Id(), result, jsonErr)
|
reply, err := createMarshalledReply(cmd.id, result, jsonErr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal reply for <%s> command: %v",
|
rpcsLog.Errorf("Failed to marshal reply for <%s> command: %v",
|
||||||
cmd.Method(), err)
|
cmd.method, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.SendMessage(reply, nil)
|
c.SendMessage(reply, nil)
|
||||||
|
@ -1207,20 +1204,21 @@ func (c *wsClient) asyncHandler() {
|
||||||
|
|
||||||
// runHandler runs the handler for the passed command and sends the
|
// runHandler runs the handler for the passed command and sends the
|
||||||
// reply.
|
// reply.
|
||||||
runHandler := func(cmd btcjson.Cmd) {
|
runHandler := func(parsedCmd *parsedRPCCmd) {
|
||||||
wsHandler, ok := wsHandlers[cmd.Method()]
|
wsHandler, ok := wsHandlers[parsedCmd.method]
|
||||||
if !ok {
|
if !ok {
|
||||||
rpcsLog.Warnf("No handler for command <%s>",
|
rpcsLog.Warnf("No handler for command <%s>",
|
||||||
cmd.Method())
|
parsedCmd.method)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke the handler and marshal and send response.
|
// Invoke the handler and marshal and send response.
|
||||||
result, jsonErr := wsHandler(c, cmd)
|
result, jsonErr := wsHandler(c, parsedCmd.cmd)
|
||||||
reply, err := createMarshalledReply(cmd.Id(), result, jsonErr)
|
reply, err := createMarshalledReply(parsedCmd.id, result,
|
||||||
|
jsonErr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal reply for <%s> "+
|
rpcsLog.Errorf("Failed to marshal reply for <%s> "+
|
||||||
"command: %v", cmd.Method(), err)
|
"command: %v", parsedCmd.method, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.SendMessage(reply, nil)
|
c.SendMessage(reply, nil)
|
||||||
|
@ -1232,7 +1230,7 @@ out:
|
||||||
case cmd := <-c.asyncChan:
|
case cmd := <-c.asyncChan:
|
||||||
if !waiting {
|
if !waiting {
|
||||||
c.wg.Add(1)
|
c.wg.Add(1)
|
||||||
go func(cmd btcjson.Cmd) {
|
go func(cmd *parsedRPCCmd) {
|
||||||
runHandler(cmd)
|
runHandler(cmd)
|
||||||
asyncHandlerDoneChan <- struct{}{}
|
asyncHandlerDoneChan <- struct{}{}
|
||||||
c.wg.Done()
|
c.wg.Done()
|
||||||
|
@ -1255,11 +1253,11 @@ out:
|
||||||
// asynchronously send.
|
// asynchronously send.
|
||||||
element := pendingCmds.Remove(next)
|
element := pendingCmds.Remove(next)
|
||||||
c.wg.Add(1)
|
c.wg.Add(1)
|
||||||
go func(cmd btcjson.Cmd) {
|
go func(cmd *parsedRPCCmd) {
|
||||||
runHandler(cmd)
|
runHandler(cmd)
|
||||||
asyncHandlerDoneChan <- struct{}{}
|
asyncHandlerDoneChan <- struct{}{}
|
||||||
c.wg.Done()
|
c.wg.Done()
|
||||||
}(element.(btcjson.Cmd))
|
}(element.(*parsedRPCCmd))
|
||||||
|
|
||||||
case <-c.quit:
|
case <-c.quit:
|
||||||
break out
|
break out
|
||||||
|
@ -1380,36 +1378,80 @@ func newWebsocketClient(server *rpcServer, conn *websocket.Conn,
|
||||||
server: server,
|
server: server,
|
||||||
addrRequests: make(map[string]struct{}),
|
addrRequests: make(map[string]struct{}),
|
||||||
spentRequests: make(map[wire.OutPoint]struct{}),
|
spentRequests: make(map[wire.OutPoint]struct{}),
|
||||||
ntfnChan: make(chan []byte, 1), // nonblocking sync
|
ntfnChan: make(chan []byte, 1), // nonblocking sync
|
||||||
asyncChan: make(chan btcjson.Cmd, 1), // nonblocking sync
|
asyncChan: make(chan *parsedRPCCmd, 1), // nonblocking sync
|
||||||
sendChan: make(chan wsResponse, websocketSendBufferSize),
|
sendChan: make(chan wsResponse, websocketSendBufferSize),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleWebsocketHelp implements the help command for websocket connections.
|
||||||
|
func handleWebsocketHelp(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
||||||
|
cmd, ok := icmd.(*btcjson.HelpCmd)
|
||||||
|
if !ok {
|
||||||
|
return nil, btcjson.ErrRPCInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide a usage overview of all commands when no specific command
|
||||||
|
// was specified.
|
||||||
|
var command string
|
||||||
|
if cmd.Command != nil {
|
||||||
|
command = *cmd.Command
|
||||||
|
}
|
||||||
|
if command == "" {
|
||||||
|
usage, err := wsc.server.helpCacher.rpcUsage(true)
|
||||||
|
if err != nil {
|
||||||
|
context := "Failed to generate RPC usage"
|
||||||
|
return nil, internalRPCError(err.Error(), context)
|
||||||
|
}
|
||||||
|
return usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the command asked for is supported and implemented.
|
||||||
|
// Search the list of websocket handlers as well as the main list of
|
||||||
|
// handlers since help should only be provided for those cases.
|
||||||
|
valid := true
|
||||||
|
if _, ok := rpcHandlers[command]; !ok {
|
||||||
|
if _, ok := wsHandlers[command]; !ok {
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCInvalidParameter,
|
||||||
|
Message: "Unknown command: " + command,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the help for the command.
|
||||||
|
help, err := wsc.server.helpCacher.rpcMethodHelp(command)
|
||||||
|
if err != nil {
|
||||||
|
context := "Failed to generate help"
|
||||||
|
return nil, internalRPCError(err.Error(), context)
|
||||||
|
}
|
||||||
|
return help, nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleNotifyBlocks implements the notifyblocks command extension for
|
// handleNotifyBlocks implements the notifyblocks command extension for
|
||||||
// websocket connections.
|
// websocket connections.
|
||||||
func handleNotifyBlocks(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
func handleNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
||||||
wsc.server.ntfnMgr.RegisterBlockUpdates(wsc)
|
wsc.server.ntfnMgr.RegisterBlockUpdates(wsc)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleNotifySpent implements the notifyspent command extension for
|
// handleNotifySpent implements the notifyspent command extension for
|
||||||
// websocket connections.
|
// websocket connections.
|
||||||
func handleNotifySpent(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
func handleNotifySpent(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
||||||
cmd, ok := icmd.(*btcws.NotifySpentCmd)
|
cmd, ok := icmd.(*btcjson.NotifySpentCmd)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, &btcjson.ErrInternal
|
return nil, btcjson.ErrRPCInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
outpoints := make([]*wire.OutPoint, 0, len(cmd.OutPoints))
|
outpoints := make([]*wire.OutPoint, 0, len(cmd.OutPoints))
|
||||||
for i := range cmd.OutPoints {
|
for i := range cmd.OutPoints {
|
||||||
blockHash, err := wire.NewShaHashFromStr(cmd.OutPoints[i].Hash)
|
blockHash, err := wire.NewShaHashFromStr(cmd.OutPoints[i].Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &btcjson.Error{
|
return nil, rpcDecodeHexError(cmd.OutPoints[i].Hash)
|
||||||
Code: btcjson.ErrParse.Code,
|
|
||||||
Message: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
index := cmd.OutPoints[i].Index
|
index := cmd.OutPoints[i].Index
|
||||||
outpoints = append(outpoints, wire.NewOutPoint(blockHash, index))
|
outpoints = append(outpoints, wire.NewOutPoint(blockHash, index))
|
||||||
|
@ -1420,23 +1462,23 @@ func handleNotifySpent(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.E
|
||||||
|
|
||||||
// handleNotifyNewTransations implements the notifynewtransactions command
|
// handleNotifyNewTransations implements the notifynewtransactions command
|
||||||
// extension for websocket connections.
|
// extension for websocket connections.
|
||||||
func handleNotifyNewTransactions(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
func handleNotifyNewTransactions(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
||||||
cmd, ok := icmd.(*btcws.NotifyNewTransactionsCmd)
|
cmd, ok := icmd.(*btcjson.NotifyNewTransactionsCmd)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, &btcjson.ErrInternal
|
return nil, btcjson.ErrRPCInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
wsc.verboseTxUpdates = cmd.Verbose
|
wsc.verboseTxUpdates = cmd.Verbose != nil && *cmd.Verbose
|
||||||
wsc.server.ntfnMgr.RegisterNewMempoolTxsUpdates(wsc)
|
wsc.server.ntfnMgr.RegisterNewMempoolTxsUpdates(wsc)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleNotifyReceived implements the notifyreceived command extension for
|
// handleNotifyReceived implements the notifyreceived command extension for
|
||||||
// websocket connections.
|
// websocket connections.
|
||||||
func handleNotifyReceived(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
func handleNotifyReceived(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
||||||
cmd, ok := icmd.(*btcws.NotifyReceivedCmd)
|
cmd, ok := icmd.(*btcjson.NotifyReceivedCmd)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, &btcjson.ErrInternal
|
return nil, btcjson.ErrRPCInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode addresses to validate input, but the strings slice is used
|
// Decode addresses to validate input, but the strings slice is used
|
||||||
|
@ -1444,11 +1486,11 @@ func handleNotifyReceived(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjso
|
||||||
for _, addr := range cmd.Addresses {
|
for _, addr := range cmd.Addresses {
|
||||||
_, err := btcutil.DecodeAddress(addr, activeNetParams.Params)
|
_, err := btcutil.DecodeAddress(addr, activeNetParams.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := btcjson.Error{
|
return nil, &btcjson.RPCError{
|
||||||
Code: btcjson.ErrInvalidAddressOrKey.Code,
|
Code: btcjson.ErrRPCInvalidAddressOrKey,
|
||||||
Message: fmt.Sprintf("Invalid address or key: %v", addr),
|
Message: fmt.Sprintf("Invalid address or key: %v",
|
||||||
|
addr),
|
||||||
}
|
}
|
||||||
return nil, &e
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wsc.server.ntfnMgr.RegisterTxOutAddressRequests(wsc, cmd.Addresses)
|
wsc.server.ntfnMgr.RegisterTxOutAddressRequests(wsc, cmd.Addresses)
|
||||||
|
@ -1478,8 +1520,8 @@ func (r *rescanKeys) unspentSlice() []*wire.OutPoint {
|
||||||
|
|
||||||
// ErrRescanReorg defines the error that is returned when an unrecoverable
|
// ErrRescanReorg defines the error that is returned when an unrecoverable
|
||||||
// reorganize is detected during a rescan.
|
// reorganize is detected during a rescan.
|
||||||
var ErrRescanReorg = btcjson.Error{
|
var ErrRescanReorg = btcjson.RPCError{
|
||||||
Code: btcjson.ErrDatabase.Code,
|
Code: btcjson.ErrRPCDatabase,
|
||||||
Message: "Reorganize",
|
Message: "Reorganize",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1596,9 +1638,10 @@ func rescanBlock(wsc *wsClient, lookups *rescanKeys, blk *btcutil.Block) {
|
||||||
if txHex == "" {
|
if txHex == "" {
|
||||||
txHex = txHexString(tx)
|
txHex = txHexString(tx)
|
||||||
}
|
}
|
||||||
ntfn := btcws.NewRecvTxNtfn(txHex, blockDetails(blk, tx.Index()))
|
ntfn := btcjson.NewRecvTxNtfn(txHex,
|
||||||
|
*blockDetails(blk, tx.Index()))
|
||||||
|
|
||||||
marshalledJSON, err := json.Marshal(ntfn)
|
marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal recvtx notification: %v", err)
|
rpcsLog.Errorf("Failed to marshal recvtx notification: %v", err)
|
||||||
return
|
return
|
||||||
|
@ -1622,12 +1665,15 @@ func rescanBlock(wsc *wsClient, lookups *rescanKeys, blk *btcutil.Block) {
|
||||||
// range of blocks. If this condition does not hold true, the JSON-RPC error
|
// range of blocks. If this condition does not hold true, the JSON-RPC error
|
||||||
// for an unrecoverable reorganize is returned.
|
// for an unrecoverable reorganize is returned.
|
||||||
func recoverFromReorg(db database.Db, minBlock, maxBlock int64,
|
func recoverFromReorg(db database.Db, minBlock, maxBlock int64,
|
||||||
lastBlock *wire.ShaHash) ([]wire.ShaHash, *btcjson.Error) {
|
lastBlock *wire.ShaHash) ([]wire.ShaHash, error) {
|
||||||
|
|
||||||
hashList, err := db.FetchHeightRange(minBlock, maxBlock)
|
hashList, err := db.FetchHeightRange(minBlock, maxBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Error looking up block range: %v", err)
|
rpcsLog.Errorf("Error looking up block range: %v", err)
|
||||||
return nil, &btcjson.ErrDatabase
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCDatabase,
|
||||||
|
Message: "Database error: " + err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if lastBlock == nil || len(hashList) == 0 {
|
if lastBlock == nil || len(hashList) == 0 {
|
||||||
return hashList, nil
|
return hashList, nil
|
||||||
|
@ -1636,7 +1682,10 @@ func recoverFromReorg(db database.Db, minBlock, maxBlock int64,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Error looking up possibly reorged block: %v",
|
rpcsLog.Errorf("Error looking up possibly reorged block: %v",
|
||||||
err)
|
err)
|
||||||
return nil, &btcjson.ErrDatabase
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCDatabase,
|
||||||
|
Message: "Database error: " + err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
jsonErr := descendantBlock(lastBlock, blk)
|
jsonErr := descendantBlock(lastBlock, blk)
|
||||||
if jsonErr != nil {
|
if jsonErr != nil {
|
||||||
|
@ -1647,7 +1696,7 @@ func recoverFromReorg(db database.Db, minBlock, maxBlock int64,
|
||||||
|
|
||||||
// descendantBlock returns the appropiate JSON-RPC error if a current block
|
// descendantBlock returns the appropiate JSON-RPC error if a current block
|
||||||
// fetched during a reorganize is not a direct child of the parent block hash.
|
// fetched during a reorganize is not a direct child of the parent block hash.
|
||||||
func descendantBlock(prevHash *wire.ShaHash, curBlock *btcutil.Block) *btcjson.Error {
|
func descendantBlock(prevHash *wire.ShaHash, curBlock *btcutil.Block) error {
|
||||||
curHash := &curBlock.MsgBlock().Header.PrevBlock
|
curHash := &curBlock.MsgBlock().Header.PrevBlock
|
||||||
if !prevHash.IsEqual(curHash) {
|
if !prevHash.IsEqual(curHash) {
|
||||||
rpcsLog.Errorf("Stopping rescan for reorged block %v "+
|
rpcsLog.Errorf("Stopping rescan for reorged block %v "+
|
||||||
|
@ -1667,20 +1716,17 @@ func descendantBlock(prevHash *wire.ShaHash, curBlock *btcutil.Block) *btcjson.E
|
||||||
// handler erroring. Clients must handle this by finding a block still in
|
// handler erroring. Clients must handle this by finding a block still in
|
||||||
// the chain (perhaps from a rescanprogress notification) to resume their
|
// the chain (perhaps from a rescanprogress notification) to resume their
|
||||||
// rescan.
|
// rescan.
|
||||||
func handleRescan(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
func handleRescan(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
||||||
cmd, ok := icmd.(*btcws.RescanCmd)
|
cmd, ok := icmd.(*btcjson.RescanCmd)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, &btcjson.ErrInternal
|
return nil, btcjson.ErrRPCInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
outpoints := make([]*wire.OutPoint, 0, len(cmd.OutPoints))
|
outpoints := make([]*wire.OutPoint, 0, len(cmd.OutPoints))
|
||||||
for i := range cmd.OutPoints {
|
for i := range cmd.OutPoints {
|
||||||
blockHash, err := wire.NewShaHashFromStr(cmd.OutPoints[i].Hash)
|
blockHash, err := wire.NewShaHashFromStr(cmd.OutPoints[i].Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &btcjson.Error{
|
return nil, rpcDecodeHexError(cmd.OutPoints[i].Hash)
|
||||||
Code: btcjson.ErrParse.Code,
|
|
||||||
Message: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
index := cmd.OutPoints[i].Index
|
index := cmd.OutPoints[i].Index
|
||||||
outpoints = append(outpoints, wire.NewOutPoint(blockHash, index))
|
outpoints = append(outpoints, wire.NewOutPoint(blockHash, index))
|
||||||
|
@ -1707,9 +1753,10 @@ func handleRescan(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error)
|
||||||
for _, addrStr := range cmd.Addresses {
|
for _, addrStr := range cmd.Addresses {
|
||||||
addr, err := btcutil.DecodeAddress(addrStr, activeNetParams.Params)
|
addr, err := btcutil.DecodeAddress(addrStr, activeNetParams.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonErr := btcjson.Error{
|
jsonErr := btcjson.RPCError{
|
||||||
Code: btcjson.ErrInvalidAddressOrKey.Code,
|
Code: btcjson.ErrRPCInvalidAddressOrKey,
|
||||||
Message: "Rescan address " + addrStr + ": " + err.Error(),
|
Message: "Rescan address " + addrStr + ": " +
|
||||||
|
err.Error(),
|
||||||
}
|
}
|
||||||
return nil, &jsonErr
|
return nil, &jsonErr
|
||||||
}
|
}
|
||||||
|
@ -1732,8 +1779,8 @@ func handleRescan(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error)
|
||||||
lookups.uncompressedPubkeys[uncompressedPubkey] = struct{}{}
|
lookups.uncompressedPubkeys[uncompressedPubkey] = struct{}{}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
jsonErr := btcjson.Error{
|
jsonErr := btcjson.RPCError{
|
||||||
Code: btcjson.ErrInvalidAddressOrKey.Code,
|
Code: btcjson.ErrRPCInvalidAddressOrKey,
|
||||||
Message: "Pubkey " + addrStr + " is of unknown length",
|
Message: "Pubkey " + addrStr + " is of unknown length",
|
||||||
}
|
}
|
||||||
return nil, &jsonErr
|
return nil, &jsonErr
|
||||||
|
@ -1754,22 +1801,28 @@ func handleRescan(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error)
|
||||||
|
|
||||||
minBlockSha, err := wire.NewShaHashFromStr(cmd.BeginBlock)
|
minBlockSha, err := wire.NewShaHashFromStr(cmd.BeginBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &btcjson.ErrDecodeHexString
|
return nil, rpcDecodeHexError(cmd.BeginBlock)
|
||||||
}
|
}
|
||||||
minBlock, err := db.FetchBlockHeightBySha(minBlockSha)
|
minBlock, err := db.FetchBlockHeightBySha(minBlockSha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &btcjson.ErrBlockNotFound
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCBlockNotFound,
|
||||||
|
Message: "Error getting block: " + err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maxBlock := database.AllShas
|
maxBlock := database.AllShas
|
||||||
if cmd.EndBlock != "" {
|
if cmd.EndBlock != nil {
|
||||||
maxBlockSha, err := wire.NewShaHashFromStr(cmd.EndBlock)
|
maxBlockSha, err := wire.NewShaHashFromStr(*cmd.EndBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &btcjson.ErrDecodeHexString
|
return nil, rpcDecodeHexError(*cmd.EndBlock)
|
||||||
}
|
}
|
||||||
maxBlock, err = db.FetchBlockHeightBySha(maxBlockSha)
|
maxBlock, err = db.FetchBlockHeightBySha(maxBlockSha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &btcjson.ErrBlockNotFound
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCBlockNotFound,
|
||||||
|
Message: "Error getting block: " + err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1790,7 +1843,10 @@ fetchRange:
|
||||||
hashList, err := db.FetchHeightRange(minBlock, maxBlock)
|
hashList, err := db.FetchHeightRange(minBlock, maxBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Error looking up block range: %v", err)
|
rpcsLog.Errorf("Error looking up block range: %v", err)
|
||||||
return nil, &btcjson.ErrDatabase
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCDatabase,
|
||||||
|
Message: "Database error: " + err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(hashList) == 0 {
|
if len(hashList) == 0 {
|
||||||
// The rescan is finished if no blocks hashes for this
|
// The rescan is finished if no blocks hashes for this
|
||||||
|
@ -1825,7 +1881,11 @@ fetchRange:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Error fetching best block "+
|
rpcsLog.Errorf("Error fetching best block "+
|
||||||
"hash: %v", err)
|
"hash: %v", err)
|
||||||
return nil, &btcjson.ErrDatabase
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCDatabase,
|
||||||
|
Message: "Database error: " +
|
||||||
|
err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if again {
|
if again {
|
||||||
continue
|
continue
|
||||||
|
@ -1842,7 +1902,11 @@ fetchRange:
|
||||||
if err != database.ErrBlockShaMissing {
|
if err != database.ErrBlockShaMissing {
|
||||||
rpcsLog.Errorf("Error looking up "+
|
rpcsLog.Errorf("Error looking up "+
|
||||||
"block: %v", err)
|
"block: %v", err)
|
||||||
return nil, &btcjson.ErrDatabase
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCDatabase,
|
||||||
|
Message: "Database error: " +
|
||||||
|
err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an absolute max block was specified, don't
|
// If an absolute max block was specified, don't
|
||||||
|
@ -1865,11 +1929,10 @@ fetchRange:
|
||||||
// before the range was evaluated, as it must be
|
// before the range was evaluated, as it must be
|
||||||
// reevaluated for the new hashList.
|
// reevaluated for the new hashList.
|
||||||
minBlock += int64(i)
|
minBlock += int64(i)
|
||||||
var jsonErr *btcjson.Error
|
hashList, err = recoverFromReorg(db, minBlock,
|
||||||
hashList, jsonErr = recoverFromReorg(db, minBlock,
|
|
||||||
maxBlock, lastBlockHash)
|
maxBlock, lastBlockHash)
|
||||||
if jsonErr != nil {
|
if err != nil {
|
||||||
return nil, jsonErr
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(hashList) == 0 {
|
if len(hashList) == 0 {
|
||||||
break fetchRange
|
break fetchRange
|
||||||
|
@ -1897,9 +1960,9 @@ fetchRange:
|
||||||
lastBlock = blk
|
lastBlock = blk
|
||||||
lastBlockHash, err = blk.Sha()
|
lastBlockHash, err = blk.Sha()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Unknown problem creating "+
|
context := "Failed to create block hash"
|
||||||
"block sha: %v", err)
|
return nil, internalRPCError(err.Error(),
|
||||||
return nil, &btcjson.ErrInternal
|
context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1912,10 +1975,10 @@ fetchRange:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
n := btcws.NewRescanProgressNtfn(hashList[i].String(),
|
n := btcjson.NewRescanProgressNtfn(hashList[i].String(),
|
||||||
int32(blk.Height()),
|
int32(blk.Height()),
|
||||||
blk.MsgBlock().Header.Timestamp.Unix())
|
blk.MsgBlock().Header.Timestamp.Unix())
|
||||||
mn, err := n.MarshalJSON()
|
mn, err := btcjson.MarshalCmd(nil, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal rescan "+
|
rpcsLog.Errorf("Failed to marshal rescan "+
|
||||||
"progress notification: %v", err)
|
"progress notification: %v", err)
|
||||||
|
@ -1940,10 +2003,10 @@ fetchRange:
|
||||||
// received before the rescan RPC returns. Therefore, another method
|
// received before the rescan RPC returns. Therefore, another method
|
||||||
// is needed to safely inform clients that all rescan notifications have
|
// is needed to safely inform clients that all rescan notifications have
|
||||||
// been sent.
|
// been sent.
|
||||||
n := btcws.NewRescanFinishedNtfn(lastBlockHash.String(),
|
n := btcjson.NewRescanFinishedNtfn(lastBlockHash.String(),
|
||||||
int32(lastBlock.Height()),
|
int32(lastBlock.Height()),
|
||||||
lastBlock.MsgBlock().Header.Timestamp.Unix())
|
lastBlock.MsgBlock().Header.Timestamp.Unix())
|
||||||
if mn, err := n.MarshalJSON(); err != nil {
|
if mn, err := btcjson.MarshalCmd(nil, n); err != nil {
|
||||||
rpcsLog.Errorf("Failed to marshal rescan finished "+
|
rpcsLog.Errorf("Failed to marshal rescan finished "+
|
||||||
"notification: %v", err)
|
"notification: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1955,3 +2018,7 @@ fetchRange:
|
||||||
rpcsLog.Info("Finished rescan")
|
rpcsLog.Info("Finished rescan")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
wsHandlers = wsHandlersBeforeInit
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
"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/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
|
Loading…
Reference in a new issue