abbshr.github.io
abbshr.github.io copied to clipboard
Biter loggers 4:bitcoind JSON-RPC
Note:第一篇bitcoin日志中介绍了JSON-RPC, 同时记录了启动bitcoind的基本方法。
使用bitcoin-explorer获取block chain中的数据,加以分析处理。这一过程的最开始就利用了JSON-RPC。拿insight为例,其后台insight-api中的核心bitcore就是用来和bitcoind进程通信的,而通信的方式就是JSON-RPC:insight进程向bitcoind进程发送RPC指令,并在回调中得到想要的数据,然后呈现给上层应用做数据可视化。
JSON-RPC
JSON-RPC协议的本质仍是HTTP协议,也就是说JSON-RPC在HTTP基础上进行通信的。 在2013年更新了2.0版本,bitcoind使用的就是JSON-RPC 2.0
- 规定客户端发送JSON的格式:
jsonrpc: 用来指定JSON-RPC的版本,必须为2.0
method: 需要调用的方法名
params: 参数数组,
id: 由客户端建立的rpc标识符
- 服务器响应格式:
jsonrpc: 同上
result: 服务器返回的结果,如果出现错误,就没有这个字段
error: 一个JSON对象,如果调用成功则没有这个字段
id: 与请求的id相同(如果请求失败,id为null)
Batch
客户端在同一时刻发送多个请求对象,这些请求对象包装在一个数组中。这时服务器返回的也是个数组,包含对应请求对象的响应。
client => [
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
{"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
{"foo": "boo"},
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
{"jsonrpc": "2.0", "method": "get_data", "id": "9"}
]
server => [
{"jsonrpc": "2.0", "result": 7, "id": "1"},
{"jsonrpc": "2.0", "result": 19, "id": "2"},
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
{"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
]
bitcoind
它是中本聪编写的BTC客户端,也是第一个BTC客户端。实现了BTC的相关协议,并提供了RPC通信接口,允许本机进程或远程主机与其通信。
为了方便起见,把bitcoind添加到alias:
echo "alias btcrpc=$HOME/bitcoind -conf=$HOME/bitcoin.conf" >> .bashrc
bitcoind提供了一大堆RPC API,列表详见: https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list
通过help命令也可以获取:
btcrpc help
addmultisigaddress nrequired ["key",...] ( "account" )
addnode "node" "add|remove|onetry"
backupwallet "destination"
createmultisig nrequired ["key",...]
createrawtransaction [{"txid":"id","vout":n},...] {"address":amount,...}
decoderawtransaction "hexstring"
decodescript "hex"
dumpprivkey "bitcoinaddress"
dumpwallet "filename"
encryptwallet "passphrase"
getaccount "bitcoinaddress"
getaccountaddress "account"
getaddednodeinfo dns ( "node" )
getaddressesbyaccount "account"
getbalance ( "account" minconf )
getbestblockhash
getblock "hash" ( verbose )
getblockcount
getblockhash index
getblocktemplate ( "jsonrequestobject" )
getconnectioncount
getdifficulty
getgenerate
gethashespersec
getinfo
getmininginfo
getnettotals
getnetworkhashps ( blocks height )
getnewaddress ( "account" )
getpeerinfo
getrawchangeaddress
getrawmempool ( verbose )
getrawtransaction "txid" ( verbose )
getreceivedbyaccount "account" ( minconf )
getreceivedbyaddress "bitcoinaddress" ( minconf )
gettransaction "txid"
gettxout "txid" n ( includemempool )
gettxoutsetinfo
getunconfirmedbalance
getwork ( "data" )
help ( "command" )
importprivkey "bitcoinprivkey" ( "label" rescan )
importwallet "filename"
keypoolrefill ( newsize )
listaccounts ( minconf )
listaddressgroupings
listlockunspent
listreceivedbyaccount ( minconf includeempty )
listreceivedbyaddress ( minconf includeempty )
listsinceblock ( "blockhash" target-confirmations )
listtransactions ( "account" count from )
listunspent ( minconf maxconf ["address",...] )
lockunspent unlock [{"txid":"txid","vout":n},...]
move "fromaccount" "toaccount" amount ( minconf "comment" )
ping
sendfrom "fromaccount" "tobitcoinaddress" amount ( minconf "comment" "comment-to" )
sendmany "fromaccount" {"address":amount,...} ( minconf "comment" )
sendrawtransaction "hexstring" ( allowhighfees )
sendtoaddress "bitcoinaddress" amount ( "comment" "comment-to" )
setaccount "bitcoinaddress" "account"
setgenerate generate ( genproclimit )
settxfee amount
signmessage "bitcoinaddress" "message"
signrawtransaction "hexstring" ( [{"txid":"id","vout":n,"scriptPubKey":"hex","redeemScript":"hex"},...] ["privatekey1",...] sighashtype )
stop
submitblock "hexdata" ( "jsonparametersobject" )
validateaddress "bitcoinaddress"
verifychain ( checklevel numblocks )
verifymessage "bitcoinaddress" "signature" "message"
ex:
btcrpc getpeerinfo
[
{
"addr" : "127.0.0.1:35629",
"services" : "00000001",
"lastsend" : 1400043134,
"lastrecv" : 1400043134,
"bytessent" : 3357058,
"bytesrecv" : 147242,
"conntime" : 1400039657,
"pingtime" : 0.00000000,
"version" : 70000,
"subver" : "/BitcoinX:0.1/",
"inbound" : true,
"startingheight" : 0,
"banscore" : 0
},
{
"addr" : "50.184.166.246:8333",
"services" : "00000001",
"lastsend" : 1400043145,
"lastrecv" : 1400043133,
"bytessent" : 122888,
"bytesrecv" : 800499,
"conntime" : 1400039658,
"pingtime" : 0.00000000,
"version" : 70001,
"subver" : "/Satoshi:0.8.6/",
"inbound" : false,
"startingheight" : 300640,
"banscore" : 0,
"syncnode" : true
},
{
"addr" : "67.212.92.167:8333",
"services" : "00000001",
"lastsend" : 1400043145,
"lastrecv" : 1400043125,
"bytessent" : 124584,
"bytesrecv" : 749619,
"conntime" : 1400039658,
"pingtime" : 0.00000000,
"version" : 70002,
"subver" : "/Satoshi:0.9.1/",
"inbound" : false,
"startingheight" : 300640,
"banscore" : 0
},
{
"addr" : "174.78.248.214:8333",
"services" : "00000001",
"lastsend" : 1400043134,
"lastrecv" : 1400043144,
"bytessent" : 119650,
"bytesrecv" : 388564,
"conntime" : 1400039659,
"pingtime" : 0.00000000,
"version" : 70002,
"subver" : "/Satoshi:0.9.1/",
"inbound" : false,
"startingheight" : 300640,
"banscore" : 0
},
{
"addr" : "89.98.93.137:8333",
"services" : "00000001",
"lastsend" : 1400043144,
"lastrecv" : 1400043119,
"bytessent" : 130925,
"bytesrecv" : 1011629,
"conntime" : 1400039666,
"pingtime" : 0.00000000,
"version" : 70002,
"subver" : "/Satoshi:0.9.1/",
"inbound" : false,
"startingheight" : 300640,
"banscore" : 0
},
{
"addr" : "199.115.176.18:8333",
"services" : "00000001",
"lastsend" : 1400043135,
"lastrecv" : 1400043127,
"bytessent" : 345728,
"bytesrecv" : 25299,
"conntime" : 1400039675,
"pingtime" : 0.00000000,
"version" : 50000,
"subver" : "",
"inbound" : false,
"startingheight" : 277595,
"banscore" : 0
},
{
"addr" : "108.53.129.182:8333",
"services" : "00000001",
"lastsend" : 1400043134,
"lastrecv" : 1400043144,
"bytessent" : 115329,
"bytesrecv" : 359246,
"conntime" : 1400039691,
"pingtime" : 0.00000000,
"version" : 70002,
"subver" : "/Satoshi:0.9.1/",
"inbound" : false,
"startingheight" : 300640,
"banscore" : 0
},
{
"addr" : "107.170.211.129:8333",
"services" : "00000001",
"lastsend" : 1400043144,
"lastrecv" : 1400043144,
"bytessent" : 133227,
"bytesrecv" : 992178,
"conntime" : 1400039697,
"pingtime" : 0.00000000,
"version" : 70001,
"subver" : "/Satoshi:0.8.5/",
"inbound" : false,
"startingheight" : 300640,
"banscore" : 0
},
{
"addr" : "24.159.17.178:8333",
"services" : "00000001",
"lastsend" : 1400043145,
"lastrecv" : 1400043005,
"bytessent" : 36944879,
"bytesrecv" : 130539,
"conntime" : 1400040379,
"pingtime" : 0.00000000,
"version" : 70002,
"subver" : "/Satoshi:0.9.1/",
"inbound" : false,
"startingheight" : 181817,
"banscore" : 0
}
]
注意JSON-RPC不仅仅通信使用JSON格式,得到的结果当然也是JSON格式的啦。
其他方法
除了命令行的方式外,也有很多编程语言的wrapper以及curl工具:
curl
这是最Raw的方法,可以看到RPC交互的数据格式:
curl --user user --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getinfo", "params": [] }'
-H 'content-type: text/plain;' http://127.0.0.1:8332/
返回:
{"result":{"balance":0.000000000000000,"blocks":59952,"connections":48,"proxy":"","generate":false,
"genproclimit":-1,"difficulty":16.61907875185736,"error":null,"id":"curltest"}
JavaScript
自然有人写模块啦,node下有很多wrap,这个例子(bitcoin模块)是来自官网的:
var bitcoin = require('bitcoin');
var client = new bitcoin.Client({
host: 'localhost',
port: 8332,
user: 'user',
pass: 'pass'
});
client.getDifficulty(function(err, difficulty) {
if (err) {
return console.error(err);
}
console.log('Difficulty: ' + difficulty);
});