イーサリアムの開発に没頭。 パート2:Web3.jsとガス

前の記事では、Mistウォレットのユーザーインターフェイスを介したコントラクトの展開とそれらとの相互作用について説明しましたが、これは実際の開発には適していません。 ユーザーアプリケーションコードからブロックチェーンを操作できるライブラリが必要です。 この記事では、Web3.jsライブラリーを簡単に見て、Gethコンソールから感じます。 そして、おそらく開発者だけでなく潜在的な顧客にも関心がある別の重要なトピック-ブロックチェーンのトランザクションにはどれくらいの費用がかかりますか?









Mistから契約を展開する方法はすでに知っています。 しかし、Mistは主な機能へのグラフィカルインターフェイスを提供する単なるアプリケーションです。 Gethクライアントに基づいて動作します。 Gethは、EthereumプロトコルのGo実装の一部です。 Go実装は1つだけではありません。最も一般的な実装の1つは、たとえば、パリティ-さび実装です。 Parityでは、別のテストネットワークを使用できることに注意してください-Kovanは、Proof-of-Authorityを使用します-これは、Rokkebyのブロックの作成作業を分散するための同じアルゴリズムです(RopstenのProof-of-Workの代わりに)。 さらに、ParityはMistを使用せず、Webインターフェース上のウォレットを使用します。 ただし、当面はGoの実装に焦点を当てます。 Gethは、イーサリアムネットワークへのエントリポイントです。 その動作を示すために、提供されているコマンドラインを使用できます。 コマンドラインは通常のJavaScriptを解釈します。 前の記事でMistを使用して作成したものと同じコントラクトにアクセスしてみましょう。 まず、契約の住所とインターフェースをファイルに保存します。これらは後で必要になります。 MistはGethを使用しているため、競合が発生しないようにMistを閉じます(これは、MistのWindows実装には適用されません。



「こんにちはコマンドライン!」



コンソールでgethを実行します(gethのインストール方法はこちらで確認できます )。



$ geth console --testnet 2>>geth.log
      
      





--testnet



フラグは、私たちをRopstenテストネットワークに接続します。 Rinkebyに接続するには、 --rinkeby



フラグを--rinkeby



ます。 さらに、標準のgethストリーム(stderr)からgeth.logファイルにメッセージをリダイレクトします。そうしないと、コンソールで干渉します。

チームの結果として、ウェルカムメッセージが表示されます。 まず、次のコマンドを使用してウォレットの残高を調べてみましょう(必ずしも他のものを使用することはできませんが、アドレスがあります:コインベースと呼ばれる挨拶に表示されます)。



 > eth.getBalance(eth.coinbase)
      
      





結果の例: 22159430784000000000







エーテルではなく、wei-エーテルの量の可能な限り最小の測定単位で表示されるため、この数は非常に大きく、エーテルを操作するときにコードでも使用されます。 Mistウォレットのように、通常のエーテルで番号を表示するには、変換を追加できます。



 > web3.fromWei( eth.getBalance(eth.coinbase) ) 22.159430784
      
      





契約を開始します。 契約のアドレスを変数に割り当てます。



 > var address = "0x65cA73D13a2cc1dB6B92fd04eb4EBE4cEB70c5eC";
      
      





コントラクトインターフェイスを変数に割り当てます。



 > var abi = [ { "constant": false, "inputs": [ { "name": "newString", "type": "string" } ], "name": "setString", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "getString", "outputs": [ { "name": "", "type": "string", "value": "Hello World!" } ], "payable": false, "type": "function" } ];
      
      





契約オブジェクトを作成します。



 > var contract = web3.eth.contract(abi);
      
      





このオブジェクトを使用して、既存のコントラクトを開いたり、新しいコントラクトを展開したりできます。 この場合、最初のものが必要です。そのためにコマンドを実行します:



 > var stringHolder = contract.at(address)
      
      





各コマンドの出力はundefined



-注意しないでください。 アドレスとインターフェイスの値を置き換えます。サンプルを作成したときと同じテストネットワーク(Ropsten)にいる場合は、コントラクトを使用できます。 契約の機能を呼び出します。



 > stringHolder.getString() "Hello World!" > stringHolder.setString("Hello my baby, hello my honey!"); Error: invalid address at web3.js:3879:15 at web3.js:3705:20 at web3.js:4948:28 at map (<native code>) at web3.js:4947:12 at web3.js:4973:18 at web3.js:4998:23 at web3.js:4061:16 at apply (<native code>) at web3.js:4147:16
      
      





getString



メソッドが正しく機能し、 setString



がエラーを引き起こしたことがわかります。 この場合、航空で支払うことができる口座から取引を実行する必要があるため、このエラーが発生します。 次のコマンドを使用してアカウントのブロックを解除し(プライベートキーのパスワードを入力する必要があります)、トランザクションを実行するアカウントを設定する追加オプションを使用してsetString



再度実行します。



 > web3.personal.unlockAccount(eth.coinbase); Unlock account 0x<  > Passphrase: true > stringHolder.setString("Hello my baby, hello my honey!", {from: eth.coinbase}); "0x5f9c3a61c79df36776713f7373b902feea802cf6d3903195f8070ff2d376c669"
      
      





トランザクション番号が返されます。 この番号を使用して、etherscan Webサイトでトランザクションを追跡できます。Ropstenの場合はropsten.etherscan.io、Rinkebyの場合はrinkeby.etherscan.io 、検索でトランザクション番号を入力するか、コマンドを実行します。



 > web3.eth.getTransaction("0x5f9c3a61c79df36776713f7373b902feea802cf6d3903195f8070ff2d376c669");
      
      





私たちの代わりにあなたの取引番号を置き換えることを忘れないでください。



トランザクションの詳細を含む構造を参照してください。 これでgetString



を実行して、行が変更されたことを確認できます。



 > stringHolder.getString(); "Hello my baby, hello my honey!"
      
      





ガスはどのように消費されますか



前の記事で、私たちはすでにガスとは何かを書きました。 便宜上、以下を思い出してください。

エーテルは、データ変更操作に必要であり、いわゆるガスの代償となります。これは、トランザクションを完了するために必要な作業を評価するのに役立つ抽象的な測定単位です。 この評価をエーテルの現在の市場価値から独立させるために必要です。 トランザクションを送信するときに、ガスの各ユニットに対して支払うエーテルの量と、支払う意思のあるガスの最大量を指定できます。 割り当てる量が多いほど、潜在的なマイナーのトランザクションの優先度が高くなります。 実際、本質的に、ガス料金は、取引を完了して次のブロックに含めるための鉱夫の仕事の支払いです。 したがって、採掘時に、見つかったブロックの固定料金に加えて-執筆時点では5エーテルです-鉱夫もトランザクション料金(通常はエーテルの数百分の1)を受け取ります。 トランザクションあたりのガスの量は、データ操作の計算の複雑さに依存します。


トランザクション中に支払いとガス消費がどのように発生するかを明確に示すために、新しい契約を作成します。 同時に、別のコンパイルおよび展開方法を使用します-コマンドラインを使用します。



1.デモンストレーション用の契約の展開



次の契約を検討してください。



 pragma solidity ^0.4.10; contract UselessWorker { int public successfullyExecutedIterations = 0; function doWork(int _iterations) { successfullyExecutedIterations = _iterations; for (int i = 0; i < _iterations; i++) { keccak256(i); } } }
      
      





コントラクトには、1つのdoWork



関数のみが含まれます。この関数は、パラメーターとして反復回数int _iterations



、その後ループカウンターからのkeccak256



ハッシュを考慮します。 そのため、異なる作業量を与えて、必要なガス量がそれにどのように依存するかを確認できます。 コントラクトに格納されている唯一の変数-successfulExecutedIterations-は、最後の開始時に完了したサイクルの数を保存するために使用されます。 ガス流量を超えた場合に何が起こるかを示すために必要です。



契約のテキストをファイルUselessWorker.solに保存します。 コンパイルには、solc-solidityコンパイラを使用します(インストール手順はリンクにあります )。



 $ solc --bin --abi UselessWorker.sol
      
      





--abi



--abi



を使用して、バイナリコードとインターフェイスを生成するようコンパイラーに指示すると、次のような応答がコマンドに対して発行されます。



 ======= UselessWorker.sol:UselessWorker ======= Binary: Contract JSON ABI [{"constant":true,"inputs":[],"name":"successfullyExecutedIterations","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_iterations","type":"int256"}],"name":"doWork","outputs":[],"payable":false,"type":"function"}]
      
      





gethを実行して展開を実行します。



 $ geth console --testnet 2>>geth.log
      
      





最初に、コンパイラー出力から変数をコピーして、バイナリコードとインターフェイスを変数に割り当てます。 バイナリコードの前に0xを追加する必要があることに注意してください



 > var bin = ""; > var abi = [{"constant":true,"inputs":[],"name":"successfullyExecutedIterations","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_iterations","type":"int256"}],"name":"doWork","outputs":[],"payable":false,"type":"function"}];
      
      





開く場合と同様に、契約オブジェクトを作成します。



 > var contract = web3.eth.contract(abi);
      
      





契約の展開を実行しましょう。 デプロイメントは、特定のアカウントに代わって実行する必要があるトランザクションであり、ガスを消費し、待機する必要があります。 したがって、この場合は必要ではないコンストラクター引数とパラメーターの配列(from、data、gas)に加えて、コントラクトの展開はコールバック(この場合、エラーテキストまたはコントラクトのアドレスのいずれかを発行する最も単純なもの)を受け入れます。 ただし、最初にアカウントのロックを解除する必要があります。



 > web3.personal.unlockAccount(eth.coinbase);
      
      





次に、このアカウントから送信するトランザクションでコマンドを実行できます。



 > var uselessWorker = contract.new( {from: eth.coinbase, data: bin, gas: 1000000}, function(e, contract) { if (e) { console.log(e); } else { if (contract.address) { console.log ("mined " + contract.address); } } });
      
      





次に、同様の答えを期待します。



 mined 0xaad3bf6443621f24099ee4f51a22c8d7e9f63548
      
      





これは、契約が修正されたことを意味し、この契約の機能を呼び出すことができます。



 > uselessWorker.successfullyExecutedIterations(); 0
      
      





契約ではそのような機能を説明していませんが、 public



フィールドごとに自動的に作成されることに注意してください。



2.ガス消費実験



関数を呼び出すときに必要となるおおよそのガスの量を計算することから始めましょう。これを行うには、対象のメソッドのパラメータがパラメータとしてguessGasに渡されている間に、対象のコントラクトメソッドでestimateGas()



メソッドを呼び出します。 この場合、次のように呼び出すことができます。



 > uselessWorker.doWork.estimateGas(1); 41801 > uselessWorker.doWork.estimateGas(2); 41914 > uselessWorker.doWork.estimateGas(3); 42027
      
      





計算によると、各サイクルは113ガスを消費するはずです。 放送料はいくらですか? これを行うには、使用するガスの価格を知る必要があります。 トランザクションの送信時に指定されていない場合、デフォルト値が表示されます(すぐにエーテルに変換されます):



 > web3.fromWei( eth.gasPrice ); 1e-7
      
      





これは、この場合、デフォルトで1ガスのコストが0.0000001エーテルであることを意味します。 この価格は、現在のコマンドをどのように実行したかの過程でも固定されたり変更されたりすることはありません。 したがって、値は異なる可能性があります。 この方法での取引手数料は、ガスの価格にガスの量を乗じたものに等しくなります。 1サイクルの価格を計算します(価格にはサイクル自体だけでなく、トランザクションを受け入れるための初期価格も含まれます)。



 > web3.fromWei( eth.gasPrice * uselessWorker.doWork.estimateGas(1) ); "0.0041801"
      
      





この価格では、約10,000サイクルでエーテルの10分の1の領域で手数料が発生します。 チェック:



 > web3.fromWei( eth.gasPrice * uselessWorker.doWork.estimateGas(10000) ); "0.1171752"
      
      





本当に。 しかし、10000ではなく、たとえば1,000,000を指定するとどうなりますか? 結果は次のようになります。



 > web3.fromWei( eth.gasPrice * uselessWorker.doWork.estimateGas(1000000) ); "0.4704624"
      
      





この不均衡は、消費できるガスの最大量もあるという事実から生じます。 この数量にもデフォルト値がありますが、この場合は表示できません。 支払う準備ができているガス制限を明示的に設定するとどうなるか見てみましょう。 ただし、まず、価格が変更されたかどうかを確認します。



 > web3.fromWei( eth.gasPrice ); 2.8e-7
      
      





それはほぼ3倍に成長しました(私たちの国ではこれは30分以内に起こりました)。 デフォルト価格は市場価格とみなされるため、動的に変化します。 最初に使用したのと同じ価格の見積もりを保持するために、ガスの価格(weiで)を変数に割り当てます。



 > var fixedGasPrice = 100000000000;
      
      





価格で10万サイクルの価格を計算します(100万サイクルには時間がかかりすぎる場合があります)が、ガス制限(パラメーターgas)を変更します。



 > web3.fromWei( fixedGasPrice * uselessWorker.doWork.estimateGas(100000, {gas: 100000000}) ); "1.1341816"
      
      





ガスの量を1000に制限します。



 > web3.fromWei( fixedGasPrice * uselessWorker.doWork.estimateGas(100000, {gas: 1000}) ); "0.0001"
      
      





この場合、提供されたすべてのガスが消費されます。 変更はブロックチェーンに保存されませんが、鉱夫はガスがなくなるまで作業を行ったため、支払いを受け取ります。 したがって、ガス制限を正しく示すことが重要です。 実行はブロックチェーンの現在の状態に基づいているため、 estimateGas



関数は常に信頼できるデータを提供できるわけではありません。これは、このトランザクションの実行中に異なる場合があり、異なるガス消費を伴います。



メソッドの実際の実行と、予測値との比較に移りましょう。 まず、アカウントの残高を見て覚えてみましょう。



 > initialBalance = eth.getBalance(eth.coinbase); 5006820644000000000
      
      





10サイクルを完了するとします。 この呼び出しに必要なガス量を計算します。



 > uselessWorker.doWork.estimateGas( 10 ); 42818
      
      





この操作にはfixedGasPrice



価格を使用しますが、ガスの最大量を42000に設定します(予測値よりも低いため、これはおそらく十分ではありません)。 したがって、提供されたすべてのガスの消費を考慮した支払いは、weiで行う必要があります。



 > predictedCost = 42000 * fixedGasPrice; 4200000000000000
      
      





何が放送されますか:



 > web3.fromWei(predictedCost); "0.0042"
      
      





制限と価格のタスクでトランザクションを完了します(ただし、その前にアカウントのロックを解除します)。



 > web3.personal.unlockAccount(eth.coinbase); > transaction = uselessWorker.doWork( 10, { from: eth.coinbase, gas: 42000, gasPrice: fixedGasPrice } ); "0xc0590a2cf39c3e4339253ecf11d124177b75502cea368adcf30d1b7d6933ef5a"
      
      





トランザクション番号によって、次の操作を行うことでステータスを追跡できます。



 > result = web3.eth.getTransactionReceipt(transaction);
      
      





結果は次のような構造になります。



 { blockHash: "0x91b63b43856e62fd26ad7f401bfe556cc100e8adf4b5ac510261e91adb9953a3", blockNumber: 1375978, contractAddress: null, cumulativeGasUsed: 740323, from: "0x334731990b420d7fe77347545c45a689becfca08", gasUsed: 42000, logs: [], logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", root: "0x07dc7178ac2abaa2460cd94d5eb7bf6d7ed9ed09e3e74a4b53321b5c210932c0", to: "0xaad3bf6443621f24099ee4f51a22c8d7e9f63548", transactionHash: "0xc0590a2cf39c3e4339253ecf11d124177b75502cea368adcf30d1b7d6933ef5a", transactionIndex: 1 }
      
      





結果がnil



場合、トランザクションはまだブロックに追加されていないため、待機してコマンドを再試行する必要があります。



この構造では、予想通り、 gasUsed



ガスgasUsed



が42,000であることがgasUsed



。 残高が変更されたかどうかを確認します。



 > web3.fromWei( initialBalance - eth.getBalance(eth.coinbase) ); "0.0042"
      
      





予想どおり、消費量は0.0042エーテルでした。 契約のデータが変更されたかどうかを確認します。



 > uselessWorker.successfullyExecutedIterations(); 0
      
      





コントラクトの割り当てがループの前に実行されたという事実にもかかわらず、変数には何も保存されませんでした。 したがって、ガス不足の場合、変更は完全にロールバックされることがわかります。 ただし、作業が完了しているため、このトランザクションはブロックに追加されたままであるため、ブロック番号と他のすべての情報を確認できます。 ただし、情報では、すべてのガスが消費されたことを除いて、他のエラーの兆候は見られません。 すべてのガスが使い果たされたが、もう必要ではなくなった、まれだが可能性のあるケースでトランザクションのステータスを判断する方法 残念ながら、サイトを調べる(たとえば、 ここにトランザクションのリンクがあります。他のトランザクションを検索し、エラーで完了したかどうかを確認できます)、またはデバッグツールを使用して同じトランザクションの送信を再シミュレートするよりも簡単な方法はまだありません。



ここで、予想よりわずかに多く制限を指定した場合に何が起こるかを確認しましょう。 初期バランスも維持します。



 > initialBalance = eth.getBalance(eth.coinbase); 5002620644000000000
      
      





今回は43,000のガスを割り当て、バランスの変化が期待できます



 > 43000 * fixedGasPrice; 4300000000000000
      
      





新しい制限でトランザクションを完了しましょう:



 > web3.personal.unlockAccount(eth.coinbase); > transaction = uselessWorker.doWork( 10, { from: eth.coinbase, gas: 43000, gasPrice: fixedGasPrice } );
      
      





そして、その実行の結果を取得します。



 > result = web3.eth.getTransactionReceipt(transaction);
      
      





 { blockHash: "0xe9793206faf5e923042488d4312d542db2c5189d25a0014d894179fce222705d", blockNumber: 1376028, contractAddress: null, cumulativeGasUsed: 131780, from: "0x334731990b420d7fe77347545c45a689becfca08", gasUsed: 42817, logs: [], logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", root: "0x51fd139db608c2fa833e5de205497368362853b8e778787e85180e1cde206151", to: "0xaad3bf6443621f24099ee4f51a22c8d7e9f63548", transactionHash: "0xd69e46cbb9c139cc2c56ae4860b2e73e4581a97fc016c2848dc6bd399e9b5196", transactionIndex: 2 }
      
      





使用済み42817ガス(予測より1少ない)。

エーテル消費:



 > web3.fromWei( initialBalance - eth.getBalance(eth.coinbase) ); "0.0042817"
      
      





使用済みガスの支払いに必要なだけ正確に。



契約の変更は保存されますか?



 > uselessWorker.successfullyExecutedIterations(); 10
      
      





はい、保存しました。



続く



今のところすべてです。 Gethのドキュメントはここで見ることができます



堅牢性ドキュメント。



Gethはweb3.jsライブラリを使用します。そのドキュメントはこちらでご覧いただけます

これは、イーサリアムブロックチェーンに接続するための最も一般的なライブラリの1つです。 Ruby on Railsでの開発を専門としているため、適切なrubyインターフェースを見つけるという目標を設定しました。 次の記事では、それがどうなるかを説明します。



パート1へのリンク



All Articles