コマンドラインと JSON-RPC
SVN 上のバージョン 0.2.6 はデーモンとして実行でき、コマンドラインまたは JSON-RPC で制御できるようになった。
Linux では libgtk2.0-0 がインストールされている必要があるが、GUI が実行されている必要はない。うまくいけば、ウィンドウシステムがインストールされていなくても GTK をインストールできるだろう。
デーモンとして起動するコマンド: bitcoin -daemon [スイッチ…]
または、通常通り UI を実行しつつコマンドラインや JSON-RPC からも制御可能にするには、-server スイッチを使用する。
bitcoin -server [スイッチ…]
どちらのスイッチでも、127.0.0.1:8332 でローカルソケット接続を受け付ける HTTP JSON-RPC サーバーが実行される。ポートはループバックにバインドされ、ローカルマシンからのみアクセスできるが、実行中のユーザーだけでなくどのアカウントからでもアクセスできる。
コマンドラインから制御するには、インターフェースはスイッチなしのコマンド名の後にパラメーター(ある場合)を続ける。 bitcoin <コマンド> [パラメーター…]
例:
bitcoin getinfo
bitcoin getdifficulty
bitcoin setgenerate true
bitcoin stop
これはシンプルな JSON-RPC クライアントで、JSON の結果を表示する。コマンドのリストは rpc.cpp を参照してほしい。
Web アプリや自動化されたものは通常、コマンドラインではなく JSON-RPC を直接使用する。すべての主要言語に JSON-RPC ライブラリがある。PHP や Python のようなスクリプト言語では、構文はローカル関数を呼び出すのと同じくらい自然だ。
ああ、ありがとう!
これがどれほど必要だったか、君には想像もつかないだろう。
すぐに支払いライブラリを作り上げてみせる。 😄
SVN上のバージョン0.2.6はデーモンとして実行でき、コマンドラインまたはJSON-RPCで制御できるようになった。
この要件はいずれ撤廃されるのか? GTK を扱うのは面倒だ。
Gtk は GUI に必要なので、同じバイナリを使いたい場合はリンクしなければなりません。別のバイナリを作る方法もありますが、どの程度のコード修正や ifdef が必要かは分かりません。
- madhatter2 が鋭いヘックスエディターを取り出す
サトシ・ナカモトの投稿(2010年2月23日 13:15 UTC)SVN上のバージョン0.2.6はデーモンとして実行でき、コマンドラインまたはJSON-RPCで制御できるようになった。
この要件はいつか解消されますか?GTKを扱いたくないのですが。
GTK を「扱う」のに実際どれくらいの手間がかかるのだろうか?「sudo apt-get install libgtk2.0-0」をして、いくつかの余分なライブラリが置いてあるだけの問題ではないか?GTK は何もする必要はなく、ただそこにあれば Bitcoin が起動時にリンクでき、GUI がないため gtk-init-check の呼び出しが失敗して、それで終わりだ。
GTK のリンクを避けるためだけに wxBase を使用するために、すべてを ifdef で台無しにして、別のコンパイルとバイナリを用意するよりマシだ。
*NIX 系の人々はある種の「ピューリスト」だ。本来必要のないライブラリで OS のインストールを汚されたくないのだ。
何百万もの ifdef も答えではない。うーん…これにはもう少し考察が必要かもしれない。
サトシ・ナカモトの投稿(2010年2月24日 06:17 UTC)マイケル・マーカートの投稿(2010年2月24日 03:07 UTC)サトシ・ナカモトの投稿(2010年2月23日 22:15 UTC)Linuxではlibgtk2.0-0のインストールが必要だ
この要件はいずれ取り除かれますか? GTKを相手にしたくないので。
GTKを「相手にする」のに、実際どれだけ手間がかかるのか? 「sudo apt-get install libgtk2.0-0」を実行して、ライブラリがいくつか余分に置かれるだけではないのか? GTKは何かをする必要はない、bitcoinが起動時にリンクするためにそこに在りさえすればいい。GUIがないのでgtk-init-check呼び出しが失敗する、それで終わりだ。
こうすれば、GTKのリンクを避けるためだけにifdefだらけにし、wxBaseを使う別個のコンパイルとバイナリを用意する、という形で全体を切り刻まずに済む。
Linux From Scratch を使っているので、GTK のような依存関係だらけのパッケージをインストールするのはかなり面倒だ。BitCoin が使わないのに、なぜ十数個のパッケージと数百メガバイトをシステムに追加しなければならないのか?
なぜインストールするかって? 今のところそうしなければならないからだ。その気持ちは分かる。俺はミニマリストの FreeBSD サーバーを運用しているが、X ライブラリでごちゃごちゃにするのは面倒だ。
しかし、X ワークステーションにインストールするのはまったく問題ない。通常、正しい依存関係がすでに揃っているからだ。😛
これはおかしいですね…64 ビット Linux サーバーで Bitcoin をデーモンとして起動すると、残りの 250MB の RAM と 700MB のスワップをすべて食い尽くして、最終的にクラッシュします。32 ビットの Ubuntu デスクトップでは問題なく動作し、メモリー使用量は 15MB に留まります。サーバーでは 64 ビットビルドの Bitcoin を実行しています。ビルドに何か問題があるのかもしれません。
メモリー使用量はいつ、どのくらいの速さで増加したか?すぐに、長時間かけてゆっくりと、それとも何かの後のイベントから始まったか?
Ubuntu 9.10 64 ビットで -daemon を実行しており、メモリー使用量は安定している。
サーバーでの違いは 64 ビット以外に何かあるはずだ。GUI がないことによる何らかの不具合かもしれない。メモリーリークのデバッグツールが手がかりを与えてくれるかもしれない。
すぐに増加し始めました。valgrind で調べてみます。
OK、wxBase のみをリンクし GTK をリンクしないビルドターゲット bitcoind を作成した。SVN 上のバージョン 0.2.7 だ。
ui.cpp から初期化とシャットダウンの処理を init.cpp に分離したので、ui.cpp は純粋な UI のみになった。ui.h は wxUSE_GUI=0 の場合にインラインスタブを提供する。ノードから UI へのインターフェース関数は 4 つだけだ。bitcoind ビルドでは、ui.o や uibase.o はリンクしない。
マルッティ・マルミの投稿(2010年2月25日 07:32 UTC)すぐに増加し始めました。valgrindで調べてみます。
何か UI の処理が失敗したか、正しく初期化されなかったために、wxWidgets 内で無限にリトライしているような感じがする。初期化失敗を無視して実行を続けるハックは、未知の領域に入ることを意味する。このモードでは wx をほとんど使用しないという事実に頼っている。wxGetTranslation や wxMutex など、いくつかは引き続き使用している。
別のデバッグ方法として、gdb で実行し、すべてが静かになりすべてのスレッドがアイドルになるのを待ち、ブレークして、どのスレッドが忙しく何かをしているか、何をしているかを確認する方法がある。
bitcoind はおそらく問題なく動作すると思うが、問題のデバッグをしてもらえると助かる。
wxBase だけでコンパイルしようとするとエラーが出る。
g++ -c -O0 -Wno-invalid-offsetof -Wformat -g -D__WXDEBUG__ -D__WXGTK__ -DNOPCH -I"/opt/tdep/include" -I"/usr/include" -DwxUSE_GUI=0 -o obj/nogui/util.o util.cpp
In file included from util.cpp:5:
headers.h:22:24: error: wx/clipbrd.h: No such file or directory
In file included from headers.h:100,
from util.cpp:5:
db.h: In member function 'bool CDB::Exists(const K&)':
db.h:140: error: 'class Db' has no member named 'exists'
make: *** [obj/nogui/util.o] Error 1
clipbrd.h は wxBase にはインストールされない。wxWidgets-2.9.0/include/wx/clipbrd.h を include ディレクトリに移動しても、「no such file」の 2 行が消えるだけだ。
wx/clipbrd.h は使用されていないので、#if wxUSE_GUI の中に移動してほしい。
SVN の headers.h を更新した。
すまない、wxbase にリンクしたが、コンピューターにはフルの wxWidgets があった。
db.h:140 のクラス Db にメンバー「exisits」がないというのは変だ。pdb->get、pdb->put、pdb->del はその前にコンパイルできていた。Berkeley DB のバージョン 4.7.25 を使っているか?
Db::exists()
http://www.oracle.com/technology/documentation/berkeley-db/db/api_reference/CXX/frame_main.html
http://www.oracle.com/technology/documentation/berkeley-db/db/api_reference/CXX/dbexists.html
おそらく最近 exists が追加されたのかもしれない。それ以前は get を使用していたのだろう。
DB-4.7.25 を使ったらその問題は解決した。
しかし、今度はこのエラーが出ている:
g++ -O0 -Wno-invalid-offsetof -Wformat -g -D__WXDEBUG__ -D__WXGTK__ -DNOPCH -I"/usr/include" -I"/opt/tdep/include" -o bitcoind -L"/usr/lib" -L"/usr/local/lib" -L"/opt/tdep/lib" obj/nogui/util.o obj/nogui/script.o obj/nogui/db.o obj/nogui/net.o obj/nogui/irc.o obj/nogui/main.o obj/nogui/rpc.o obj/nogui/init.o obj/sha.o -l wx_baseu-2.9 -Wl,-Bstatic -l boost_system -l boost_filesystem -l db_cxx -Wl,-Bdynamic -l crypto -l gthread-2.0
obj/nogui/init.o: In function `wxArrayString::Item(unsigned int) const':
init.cpp:(.text._ZNK13wxArrayString4ItemEj[wxArrayString::Item(unsigned int) const]+0x7): undefined reference to `wxTheAssertHandler'
init.cpp:(.text._ZNK13wxArrayString4ItemEj[wxArrayString::Item(unsigned int) const]+0x42): undefined reference to `wxOnAssert(char const*, int, char const*, char const*, wchar_t const*)'
collect2: ld returned 1 exit status
make: *** [bitcoind] Error 1 wxWidgets 2.9.0 を使用しているか?2.9.0 以外の使用は推奨しない。
wx ヘッダー(arrstr.h)に wxBase 外の何かへの参照があるようだ。
Bitcoin の makefile から D__WXDEBUG__を削除すれば、おそらく解決するだろう。
それでも動作せず、とにかく動かしたい場合は、wxWidgets の include/wx/arrstr.h、167 行目を編集して wxASSERT_MSG をコメントアウトすることができる。
あの wchar の問題は、俺が OSX ビルドで行き詰まっていた部分だ。😁
解決されたのを見てうれしい。引き続きハックしていこう。
これはおかしいですね…64ビットLinuxサーバーでBitcoinをデーモンとして起動すると、残りの250MBのRAMと700MBのスワップをすべて食い尽くして、最終的にクラッシュします。32ビットのUbuntuデスクトップでは問題なく動作し、メモリー使用量は15MBに留まります。サーバーでは64ビットビルドのBitcoinを実行しています。ビルドに何か問題があるのかもしれません。
sirius-m がこれをデバッグした。64 ビット関連の問題だった。
修正は SVN の util.cpp ファイルで利用可能になった。
シンプルな Python API のコードを置いておく。各メソッドはサーバーに接続し、リクエストを送り、レスポンスを受け取って、JSON に相当する Python の辞書を返す。標準の Python モジュールしか使っていない。エラーチェックは一切しておらず、rpc.cpp からは 3 つの関数しか実装していない。需要があればもっと書くこともできる。
使い方としては、Python のコードはこんな感じになる… access = BitcoinAPI() access.getInfo() access.getAmountReceived(“1JyEmxiMso2RsFVfBcCa616npBvGgxiBX”) access.sendToAddress(“1JyEmxiMso2RsFVfBcCa616npBvGgxiBX”, 100.00) # 自分のアドレスに 100 ビットコイン送る 😁
これが自分のサイトでの自動トランザクションの土台になる。何か質問や懸念があれば言ってくれ。ひどく間違っている点があれば、遠慮なく教えてくれ。
import httplib, simplejson
class BitcoinAPI(object):
def __init__(self, host = "127.0.0.1", port = 8332, timeout = 3):
self.host = host
self.port = port
self.timeout = timeout
self.httpHeader = {"Content-type": "application/json"} # I don't know what needs to be in the header, but this works
return
def connect(self):
self.connection = httplib.HTTPConnection(self.host, self.port, timeout = self.timeout)
return
def disconnect(self):
self.connection.close()
return
# Functions return a python dictionary which should be equivalent to the JSON syntax received from the server
# ident or "id" is a constant 1, but can be overridden when calling. E.g. getAmountReceived(address, ident = 23)
def getInfo(self, ident = 1):
self.connect()
params = simplejson.dumps({"method": "getinfo", "params": ], "id": ident})
self.connection.request("POST", "/", params, self.httpHeader)
response = self.connection.getresponse()
#print response.status, response.reason # Use for troubleshooting
dictionary = simplejson.loads(response.read())
self.disconnect()
return dictionary
def getAmountReceived(self, address, ident = 1):
self.connect()
params = simplejson.dumps({"method": "getamountreceived", "params": [address], "id": ident})
self.connection.request("POST", "/", params, self.httpHeader)
response = self.connection.getresponse()
#print response.status, response.reason # Use for troubleshooting
dictionary = simplejson.loads(response.read())
self.disconnect()
return dictionary
def sendToAddress(self, address, amount, ident = 1):
self.connect()
params = simplejson.dumps({"method": "sendtoaddress", "params": [address, amount], "id": ident})
self.connection.request("POST", "/", params, self.httpHeader)
response = self.connection.getresponse()
#print response.status, response.reason # Use for troubleshooting
dictionary = simplejson.loads(response.read())
self.disconnect()
return dictionary シンプルな Python API のコードを置いておく。各メソッドはサーバーに接続し、リクエストを送り、レスポンスを受け取って、JSON に相当する Python の辞書を返す。標準の Python モジュールしか使っていない。エラーチェックは一切しておらず、rpc.cpp からは 3 つの関数しか実装していない。需要があればもっと書くこともできる。
いやまあ、python-json-rpc (http://json-rpc.org/wiki/python-json-rpc) をインストールしてこうするだけでもいいですよ:
from jsonrpc import ServiceProxy
s = ServiceProxy("http://127.0.0.1:8332")
s.getinfo()
python の辞書を返します。それくらい簡単ですよ 😉
いやまあ、python-json-rpc (
http://json-rpc.org/wiki/python-json-rpc) をインストールしてこうするだけでもいいですよ:Code:from jsonrpc import ServiceProxy s = ServiceProxy(“
http://127.0.0.1:8332”) s.getinfo()pythonの辞書を返します。それくらい簡単です
くそ、はは…まあ…少なくとも sirius-m の秘伝の一端を引き出せたわけだ。 😛
それを試してみる。
コマンドの一覧はrpc.cppを見てくれ。
本気か? つまり、自分以外みんな C++が分かるってことか? このファイルのどこにコマンド一覧があるのか分からない。ため息 何年も C++を理解しようとしてきたが、どういうわけか頭にまるで入ってこない。あんたが言っているのはこれのことか?
//
// Special case non-string parameter types
//
if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
if (strMethod == "getamountreceived" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
if (strMethod == "getallreceived" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
if (strMethod == "getallreceived" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo<bool>(params[1]); // deprecated
if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "sendmany" && n > 1)
{
string s = params[1].get_str();
Value v;
if (!read_string(s, v) || v.type() != obj_type)
throw runtime_error("type mismatch");
params[1] = v.get_obj();
}
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
まあ、全体にコメントが付いているのは良いことだ。スパゲッティコードは本当にうんざりするからな。
サトシ・ナカモトの投稿(2010年2月23日 22:15 UTC)コマンドの一覧はrpc.cppを見てくれ。
本気か? つまり、自分以外みんなC++が分かるってことか? このファイルのどこにコマンド一覧があるのか分からない。ため息 何年もC++を理解しようとしてきたが、どういうわけか頭にまるで入ってこない。あんたが言っているのはこれのことか? Code: // // Special case non-string parameter types // if (strMethod == “setgenerate” && n > 0) ConvertTo(params[0]); if (strMethod == “setgenerate” && n > 1) ConvertTo(params[1]); if (strMethod == “sendtoaddress” && n > 1) ConvertTo(params[1]); if (strMethod == “settxfee” && n > 0) ConvertTo(params[0]); if (strMethod == “getamountreceived” && n > 1) ConvertTo(params[1]); // deprecated if (strMethod == “getreceivedbyaddress” && n > 1) ConvertTo(params[1]); if (strMethod == “getreceivedbyaccount” && n > 1) ConvertTo(params[1]); if (strMethod == “getreceivedbylabel” && n > 1) ConvertTo(params[1]); // deprecated if (strMethod == “getallreceived” && n > 0) ConvertTo(params[0]); // deprecated if (strMethod == “getallreceived” && n > 1) ConvertTo(params[1]); if (strMethod == “listreceivedbyaddress” && n > 0) ConvertTo(params[0]); if (strMethod == “listreceivedbyaddress” && n > 1) ConvertTo(params[1]); if (strMethod == “listreceivedbyaccount” && n > 0) ConvertTo(params[0]); if (strMethod == “listreceivedbyaccount” && n > 1) ConvertTo(params[1]); if (strMethod == “listreceivedbylabel” && n > 0) ConvertTo(params[0]); // deprecated if (strMethod == “listreceivedbylabel” && n > 1) ConvertTo(params[1]); // deprecated if (strMethod == “getbalance” && n > 1) ConvertTo(params[1]); if (strMethod == “move” && n > 2) ConvertTo(params[2]); if (strMethod == “move” && n > 3) ConvertTo(params[3]); if (strMethod == “sendfrom” && n > 2) ConvertTo(params[2]); if (strMethod == “sendfrom” && n > 3) ConvertTo(params[3]); if (strMethod == “listtransactions” && n > 1) ConvertTo(params[1]); if (strMethod == “listtransactions” && n > 2) ConvertTo(params[2]); if (strMethod == “listaccounts” && n > 0) ConvertTo(params[0]); if (strMethod == “sendmany” && n > 1) { string s = params[1].get_str(); Value v; if (!read_string(s, v) || v.type() != obj_type) throw runtime_error(“type mismatch”); params[1] = v.get_obj(); } if (strMethod == “sendmany” && n > 2) ConvertTo(params[2]);
まあ、全体にコメントが付いているのは良いことだ。スパゲッティコードは本当にうんざりするからな。
探しているのはこの一覧か? https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list