ビットコイン・インスティテュート

コマンドラインと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のようなスクリプト言語では、構文はローカル関数を呼び出すのと同じくらい自然だ。

The Madhatter 2010年2月24日02:29 UTC

ああ、ありがとう!

これがどれほど必要だったか、君には想像もつかないだろう。

すぐに支払いライブラリを作ってしまうよ。 😄

サトシ・ナカモトの投稿(2010年2月23日22:15 UTC)

Linuxではlibgtk2.0-0がインストールされている必要がある。

この要件はいずれ撤廃されるのだろうか? できればGTKは扱わずに済ませたい。

GtkはGUIに必要なので、同じバイナリを使いたい場合はリンクしなければなりません。別のバイナリを作る方法もありますが、どの程度のコード修正やifdefが必要かは分かりません。

The Madhatter 2010年2月24日05:00 UTC
  • madhatter2が鋭いヘックスエディターを取り出す
マイケル・マーカートの投稿(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のリンクを避けるためだけにwxBaseを使用するために、すべてをifdefで台無しにして、別のコンパイルとバイナリを用意するよりマシだ。

The Madhatter 2010年2月24日06:38 UTC

*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のリンクを避けるためだけにwxBaseを使用するために、すべてをifdefで台無しにして、別のコンパイルとバイナリを用意するよりマシだ。

Linux From Scratchを使っているので、GTKのような依存関係だらけのパッケージをインストールするのはかなり面倒だ。BitCoinが使わないのに、なぜ十数個のパッケージと数百メガバイトをシステムに追加しなければならないのか?

The Madhatter 2010年2月24日07:56 UTC

なぜインストールするかって? 今のところそうしなければならないからだ。その気持ちは分かる。私はミニマリストの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日16: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を使用していたのだろう。

Berkeley DB 4.5.20を使っている。

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をコメントアウトすることができる。

Cdecker 2010年2月27日03:21 UTC

ヘッドレスモードは非常に便利だ。ようやく夜間にサーバーで生成を始められる。😁

いいね、動いた! 😁 助けてくれてありがとう!

私は2.9.0を使っている。

(自分の方ではメモリーの問題はない。)

The Madhatter 2010年3月2日15:46 UTC

あのwcharの問題は、私がOSXビルドで行き詰まっていた部分だ。😁

解決されたのを見てうれしい。引き続きハックしていこう。

マルッティ・マルミの投稿(2010年2月24日18:17 UTC)

これはおかしいですね…64ビットLinuxサーバーでBitcoinをデーモンとして起動すると、残りの250MBのRAMと700MBのスワップをすべて食い尽くして、最終的にクラッシュします。32ビットのUbuntuデスクトップでは問題なく動作し、メモリー使用量は15MBに留まります。サーバーでは64ビットビルドのBitcoinを実行しています。ビルドに何か問題があるのかもしれません。

sirius-mがこれをデバッグした。64ビット関連の問題だった。

修正はSVNのutil.cppファイルで利用可能になった。

dwdollar 2010年4月6日07:07 UTC

シンプルな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
dwdollarの投稿(2010年4月6日07:07 UTC)

シンプルな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の辞書を返します。それくらい簡単ですよ 😉

dwdollar 2010年4月6日22:58 UTC
マルッティ・マルミの投稿(2010年4月6日22:06 UTC)

いやまあ、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の辞書を返します。それくらい簡単です

くそ、はは…まあ…少なくともsirius-mの腕前の一端を引き出せたわけだ。 😛

それを試してみる。

krepta3000 2011年6月26日14:47 UTC
サトシ・ナカモトの投稿(2010年2月23日22:15 UTC)

コマンドの一覧は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]);

まあ、全体にコメントが付いているのは良いことだ。スパゲッティコードは本当にうんざりするからな。

galaxyAbstractor 2011年6月27日12:16 UTC
krepta3000の投稿(2011年6月26日14:47 UTC)
サトシ・ナカモトの投稿(2010年2月23日22:15 UTC)

コマンドの一覧は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]);

まあ、全体にコメントが付いているのは良いことだ。スパゲッティコードは本当にうんざりするからな。

探しているのはこの一覧かな? https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list