最近、私はビットコインの魔法の世界に魅了されました 。 知識への渇望はとどまるところを知らず、Andreas Antonopoulos による素晴らしい本「Mastering Bitcoin」とビットコイン開発への完全な没入は、それを癒すのに役立ちました。 この本はビットコインの技術的基礎を詳細にカバーしていますが、練習のような新しいビジネスを学ぶのに何も助けになりません。
私の意見では、完全なビットコインノードを管理し、JSON-RPCインターフェースを介して通信するためのElixirのシンプルなアプリケーションは、素晴らしい「Hello、World!」です。 行こう!
完全なノードを取得する場所
完全なBitcoin Coreノードとの接続を確立するには、まずどこかで接続する必要があります。 JSON-RPCインターフェースが開いているパブリックノードは指で数えることができるため、ノードをローカルで起動するだけで十分です。
bitcoind
を bitcoind
し、 bitcoin.config
ファイルで構成します。
rpcuser=<username> rpcpassword=<password>
決定された値<username>
および<password>
は、ビットコインノードにリクエストを送信する際の認証に使用されます。
セットアップが完了したら、フルノードを開始します。
bitcoind -conf=<path to bitcoin.config> -daemon
起動すると、フルノードデーモンはピアへの接続を開始し、ブロック単位でトランザクションをロードして検証します。
すべてが正常に機能することを確認します。
bitcoin-cli getinfo
このコマンドは、ノードのバージョンや受信および検証済みブロックの数など、ノードに関する基本情報を返します。 ブロックチェーン全体のダウンロードと検証には数日かかる場合がありますが、現時点では、プロジェクトの作業を継続します。
JSON-RPCインターフェース
BitcoinノードはJSON-RPCインターフェースを介して機能します。これは、ブロックチェーンに関する情報を抽出し、ノードと対話するために使用できます。
ノードに関する情報を取得するために以前に使用したbitcoin-cli
がJSON-RPC APIの上で機能することは興味深いです。 ノードで可能なすべてのRPCコマンドのリストは、 bitcoin-cli help
呼び出すか、 Bitcoin Wikiを参照することで表示できます。
JSON-RPCプロトコルは、HTTPサーバーを介して着信コマンドを受信します。つまり、 bitcoin-cli
を使用せずにこれらのRPCコマンドを自分で作成できます。
たとえば、 getinfo
getinfoを getinfo
で getinfo
ます 。
curl --data-binary '{"jsonrpc":"1.0","method":"getinfo","params":[]}' \ http://<user>:<pass>@localhost:8332/
同様に、Elixir!など、HTTPクライアントを使用するプログラミング環境でこのようなコマンドを実行できます。
Elixirアプリケーション開発
完全なBitcoinノードと対話するための戦略を熟考して、Elixirアプリケーションを取り上げます。
新しいプロジェクトを作成し、 mix.exs
を更新して、JSONオブジェクトの暗号化と復号化に必要なpoison
ライブラリを追加しましょうhttpoison
は、Elixirの最高のHTTPクライアントの1つです。
defp deps do [ {:httpoison, "~> 0.13"}, {:poison, "~> 3.1"} ] end
コード生成を担当する部分が完了したので、ビットコインノードとの対話の実装に移りましょう。
HelloBitcoin
モジュールの操作を始めましょう。まず、 getinfo
関数のスタブをgetinfo
ます。
defmodule HelloBitcoin do def getinfo do raise "TODO: Implement getinfo" end end
簡単にするために、 iex -S mix
介してこのモジュールとやり取りします。 次のステップに進む前に、すべてが正しく機能することを確認しましょう。
HelloBitcoin.getinfo
スタブを呼び出すと、ランタイム例外が発生します。
iex(1)> HelloBitcoin.getinfo HelloBitcoin.getinfo ** (RuntimeError) TODO: Implement getinfo (hello_bitcoin) lib/hello_bitcoin.ex:4: HelloBitcoin.getinfo/0
素晴らしい。 エラー。 あるべきです。
GetInfo
構築する
getinfo関数getinfo
コンテンツを入力getinfo
ます。
繰り返しますPOST
メソッドを使用してHTTPリクエストをBitcoinノードのHTTPサーバーに送信し(通常はhttp://localhost:8332
リッスンしhttp://localhost:8332
)、 GetInfo
コマンドと必要なパラメーターを含むJSONオブジェクトを渡す必要があります。
httpoison
は、次の2つの方法でこのタスクに対処することが判明しました。
def getinfo do with url <- Application.get_env(:hello_bitcoin, :bitcoin_url), command <- %{jsonrpc: "1.0", method: "getinfo", params: []}, body <- Poison.encode!(command), headers <- [{"Content-Type", "application/json"}] do HTTPoison.post!(url, body, headers) end end
最初に、アプリケーション構成のbitcoin_url
キーからurl
を取得します。 アドレスはconfig/config.exs
あり、ローカルノードを指している必要があります。
config :hello_bitcoin, bitcoin_url: "http://<user>:<password>@localhost:8332"
次に、JSON-RPCコマンドを表す辞書を作成します。 この場合、 method
フィールドに"getinfo"
と書き込み、 params
フィールドは空のままにします。 そしてPoison.encode!
を使用してコマンドをJSON形式に変換して、リクエスト本文をPoison.encode!
しPoison.encode!
。
HelloBitcoin.getinfo
呼び出しは、ステータスコード200
のbitcoinノードから正常な応答と、JSON形式のgetinfo
結果をgetinfo
ます。
%HTTPoison.Response{ body: "{\"result\":{\"version\":140200,\"protocolversion\":70015,\"walletversion\":130000,\"balance\":0.00000000,\"blocks\":482864,\"timeoffset\":-1,\"connections\":8,\"proxy\":\"\",\"difficulty\":888171856257.3206,\"testnet\":false,\"keypoololdest\":1503512537,\"keypoolsize\":100,\"paytxfee\":0.00000000,\"relayfee\":0.00001000,\"errors\":\"\"},\"error\":null,\"id\":null}\n", headers: [{"Content-Type", "application/json"}, {"Date", "Thu, 31 Aug 2017 21:27:02 GMT"}, {"Content-Length", "328"}], request_url: "http://localhost:8332", status_code: 200 }
素晴らしい。
本文の結果のJSONテキストを解読し、結果を取得します。
HTTPoison.post!(url, body) |> Map.get(:body) |> Poison.decode!
これで、 HelloBitcoin.getinfo
から受信したHelloBitcoin.getinfo
呼び出しの結果がより便利な形式で表示されます。
%{"error" => nil, "id" => nil, "result" => %{"balance" => 0.0, "blocks" => 483001, "connections" => 8, "difficulty" => 888171856257.3206, "errors" => "", "keypoololdest" => 1503512537, "keypoolsize" => 100, "paytxfee" => 0.0, "protocolversion" => 70015, "proxy" => "", "relayfee" => 1.0e-5, "testnet" => false, "timeoffset" => -1, "version" => 140200, "walletversion" => 130000}}
必要なデータ( "result"
)は、リクエスト自体に関するメタデータを含む辞書にラップされている"result"
注意してください。 このメタデータには、エラーの可能性がある文字列とリクエスト識別子が含まれています。
getinfo
関数を書き換えて、エラー処理を含め、エラーのないクエリ実行の場合に実際のデータを返すようにします。
with url <- Application.get_env(:hello_bitcoin, :bitcoin_url), command <- %{jsonrpc: "1.0", method: "getinfo", params: []}, {:ok, body} <- Poison.encode(command), {:ok, response} <- HTTPoison.post(url, body), {:ok, metadata} <- Poison.decode(response.body), %{"error" => nil, "result" => result} <- metadata do result else %{"error" => reason} -> {:error, reason} error -> error end
エラーがなければ、 getinfo
関数はRPC呼び出しの結果を含むタプル{:ok, result}
を返します。そうでない場合は、エラーの説明を含むタプル{:error, reason}
を取得します。
チームの概要
同様に、他のブロックチェーンRPCコマンド、たとえばgetblockhash
実装できます。
def getblockhash(index) do with url <- Application.get_env(:hello_bitcoin, :bitcoin_url), command <- %{jsonrpc: "1.0", method: "getblockhash", params: [index]}, {:ok, body} <- Poison.encode(command), {:ok, response} <- HTTPoison.post(url, body), {:ok, metadata} <- Poison.decode(response.body), %{"error" => nil, "result" => result} <- metadata do {:ok, result} else %{"error" => reason} -> {:error, reason} error -> error end end
インデックス0でgetblockhash
を呼び出すgetblockhash
、チェーンの最初のブロックを取得します。
HelloBitcoin.getblockhash(0) {:ok, "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"}
getblockhash
関数は正しく機能し、 getinfo
関数とほとんど同じです。
コードの重複を避けるために、新しい補助関数bitcoin_rpc
共通の機能部分を強調します。
defp bitcoin_rpc(method, params \\ []) do with url <- Application.get_env(:hello_bitcoin, :bitcoin_url), command <- %{jsonrpc: "1.0", method: method, params: params}, {:ok, body} <- Poison.encode(command), {:ok, response} <- HTTPoison.post(url, body), {:ok, metadata} <- Poison.decode(response.body), %{"error" => nil, "result" => result} <- metadata do {:ok, result} else %{"error" => reason} -> {:error, reason} error -> error end end
次に、 getblockhash
関数に従ってgetinfo
およびgetblockhash
関数を再定義します。
def getinfo, do: bitcoin_rpc("getinfo") def getblockhash(index), do: bitcoin_rpc("getblockhash", [index])
bitcoin_rpc
はビットコイン用の本格的なRPCインターフェイスであり、任意のRPCコマンドを簡単に実行できることがbitcoin_rpc
ます。
上記のすべてをマシンに実装することに興味がある場合は、プロジェクトのソースコードがGitHubにあります 。
おわりに
さて、比較的単純なアイデアを説明するかなり長い記事が終わりました。 完全なBitcoinノードは、JSON-RPCインターフェイスを提供します。これには、任意の言語(Elixirなど)またはスタックを使用してアクセスできます。 ビットコインの開発は驚くほど面白く、さらに掘り下げてみるのも面白いです。
Elixirでのビットコインの操作に関する一連の記事の次の部分はこちらです。