Elixirでビットコインを使用します

最近、私はビットコインの魔法の世界魅了されました 。 知識への渇望はとどまるところを知らず、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でのビットコインの操作に関する一連の記事の次の部分はこちらです。



Source: https://habr.com/ru/post/J341072/


All Articles