注意! イーサリアムのSはセキュリティの略です。 パート4.ツール







Solidity言語およびEthereumプラットフォーム全体のスマートコントラクトに固有の典型的な脆弱性、攻撃、および問題領域に専念するシリーズの第4部を紹介します。 ここでは、スマートコントラクトのセキュリティを分析するためのツールの存在と、それらが必要な(不要な)理由について説明します。







前半では、フロントランニング攻撃、さまざまな乱数生成アルゴリズム、および権限証明コンセンサスを使用したネットワークの復元力について説明しました。 2番目は、整数オーバーフロー、ABIエンコード/デコード、初期化されていないストレージポインター、タイプの混乱、およびバックドアの作り方について話しました。 第3部では、Solidityのいくつかの特徴的な機能に触れ、契約で見つかった論理的な脆弱性のいくつかを調べました。 このパートでは、スマートコントラクトを分析するための既存のツールの概要を提供します。







すべてのツールをいくつかのカテゴリに分類して、次のことができるようにしました。









次に、それぞれに触れます。 一部のツールはスマートコントラクトのソースコードでのみ機能し、他のツールでは機能しません。 すぐに、私たちは、パブリックドメインにあることがわかっており、私たちが協力したツールについてのみ話すことを予約します。 このトピックに関する他のツールの経験がある場合は、コメントしてください:)







デバッグ



現在、ソースコードでスマートコントラクトをデバッグする機会は1つ(または唯一の便利な) Remix IDEのみです。 また、バイトコードを理解する必要がある場合は、 Radare2またはMythrilTraceExplorerが役立ちます







Remix IDE



開発者の場合、ほとんどの場合、デバッグにRemixを使用します(ほとんどの場合、それを行います)。 ローカル変数(スタック)の状態、メモリ、ストレージ、消費ガスなどの状態を監視することは非常に便利です。







図1. Remix IDEインターフェース


[ストレージが完全にロードされました]タブの内容が不安な場合は、 ストレージデバイスを扱うことをお勧めします。 マイナス面は、JavaScript VMの定期的なバグと、アセンブラー挿入を使用する場合、行ごとのデバッグを期待できないことです。







Radare2



Remixが気に入らないがコンソールが好きなら、Rarere2を試してください。 これにより、コントラクトを分解およびデバッグできます。 トレースは、デバッグ(発生したトランザクションの記録)に使用されます。 rpcレーダーはノードに接続し( ganache-cliが便利)、トランザクションを繰り返すのに必要なデータを圧縮します。







グラフィカルモードでは、スタックとメモリの内容でタイルを開くことができます。







図2. Radare 2インターフェース


ブレークポイントを設定してメモリに書き込むことはできますが、ストレージを調べたり変更したりする方法は見つかりませんでした。







Positive Technologiesの社員には、Radarを使用して契約を分解およびデバッグする例があります。 レーダーは最も直感的なツールではありませんので、レーダーのコマンドを確認してください。







自動脆弱性スキャン



私たちの前に、3つの楽器の比較が既に行われています-ミスリル、マンティコア、オイエンテ。 この分析の結果によると、Mythrillはリードし、Oyenteは最悪の結果を出します。 テスト中に、著者はツールにコントラクトのソースコードを「供給」したため、結果は生のバイトコードをどれだけうまく処理しているかを反映していませんが、基本的に私たちの印象と一致します。







ミスリル



Mythrilには、スマートコントラクトのソースコードとバイトコードの両方の分析を含む幅広い機能があります。 チェーン上でも機能すること、つまり、オンラインでノードにオンラインで接続し、契約を収縮させて分析できることは注目に値します。 また、逆アセンブラーであり、適切なグラフを作成できます(ただし、注意してください。これはしばしば正しく行われないため、EthersplayまたはIDA-EVMを試してください)。 Mythrilは、MythrilTraceExplorerというツールの状態を生成することもできます。これにより、文字変数を使用してスマートコントラクトのバイトコードをデバッグできます。







図3. MythrilTraceExplorerインターフェース


MythilおよびMythrilTraceExplorerオプションの完全なリストはドキュメントに記載されており、 ここにはない秘密のフラグを見つけることができます。







Consensysテストからeth_tx_order_dependence_minimal



コントラクトを取得します







ソースeth_tx_order_dependence_minimal
 contract Benchmark { address public owner; bool public claimed; uint public reward; function Benchmark() public { owner = msg.sender; } function setReward() public payable { require (!claimed); require(msg.sender == owner); owner.transfer(reward); reward = msg.value; } function claimReward(uint256 submission) { require (!claimed); require(submission < 10); msg.sender.transfer(reward); claimed = true; } }
      
      





Mythrilがどのようにソースコードを処理するかを見てください:







 user% myth -x eth_tx_order_dependence_minimal.sol ==== Ether send ==== Type: Warning Contract: Benchmark Function name: claimReward(uint256) PC address: 693 In the function 'claimReward(uint256)' a non-zero amount of Ether is sent to msg.sender. Call value is storage_1. There is a check on storage index 7. This storage slot can be written to by calling the function 'claimReward(uint256)'. --------------------
      
      





そして今それらなしで:







 user% myth -x -c 60806040523...5f9dc0029 The analysis was completed successfully. No issues were detected.
      
      





ご覧のとおり、Mythrilの生のバイトコードは分析できませんでした。







オエンテ



Pythonで記述された、スマートコントラクトの脆弱性を検出するための独自のシンボリック実行エンジンを備えたツール。 少し前まで、開発者は入力データに関する情報の出力を最終的に追加しました。これにより、問題がトリガーされます。 このツールは、ソースコードを使用して契約を分析する場合にのみ、ある程度適切な結果を生成します。 バイトコードに設定すると、その一部しかカバーされないため、ほとんどの場合、バグは見つかりません。







Mythrilとの類推により、Oyenteに経験を積んで、Oyenteがソースコードの有無にかかわらず脆弱性をどの程度見ているかを確認します。







 user% oyente -s eth_tx_order_dependence_minimal.sol INFO:root:contract eth_tx_order_dependence_minimal.sol:Benchmark: INFO:oyente.symExec: ============ Results =========== INFO:oyente.symExec: EVM Code Coverage: 98.3% INFO:oyente.symExec: Integer Underflow: False INFO:oyente.symExec: Integer Overflow: False INFO:oyente.symExec: Parity Multisig Bug 2: False INFO:oyente.symExec: Callstack Depth Attack Vulnerability: False INFO:oyente.symExec: Transaction-Ordering Dependence (TOD): True INFO:oyente.symExec: Timestamp Dependency: False INFO:oyente.symExec: Re-Entrancy Vulnerability: False INFO:oyente.symExec:Flow1 eth_tx_order_dependence_minimal.sol:14:9: Warning: Transaction-Ordering Dependency. owner.transfer(reward) Flow2 eth_tx_order_dependence_minimal.sol:22:9: Warning: Transaction-Ordering Dependency. msg.sender.transfer(reward) INFO:oyente.symExec: ====== Analysis Completed ======
      
      





次に、このコントラクトのバイトコードを設定します。







 user% oyente -b -s eth_tx_order_dependence_minimal.bin INFO:oyente.symExec: ============ Results =========== INFO:oyente.symExec: EVM Code Coverage: 18.0% INFO:oyente.symExec: Callstack Depth Attack Vulnerability: False INFO:oyente.symExec: Transaction-Ordering Dependence (TOD): False INFO:oyente.symExec: Timestamp Dependency: False INFO:oyente.symExec: Re-Entrancy Vulnerability: False INFO:oyente.symExec: ====== Analysis Completed ======
      
      





ご覧のとおり、カバレッジは大幅に減少し(98から18パーセント)、ツールは契約に関する問題を検出しません。







Oyenteの開発は、(まあ)非常に悪いコードのために簡単ではありません。 たとえば、パラメーターはグローバル変数を介してモジュールからモジュールに渡されます。 最も可能性が高いのは、このプロジェクトに取り組んでいる人が1.5人以下だからです。







念のため、ツールボックスにOyenteを保持する価値はありますが、Oyenteが開発および改善されるかどうかはわかりません。







気孔率



おそらく最も誇張された逆コンパイラ。 その主な機能は、機能しないことです。







この機能を実証するために、おもちゃのコントラクトを逆コンパイルしましょう。これは、実験の純度のために、2017年7月1日から比較的古いバージョンのsolc'a 0.4.12を収集します。 Porosityが2018年1月に最後に更新されたため、ツールが当社の契約に対応できると計算できることに注意してください。







オリジナル:







 contract sample { uint y; function foobar (uint x) { if (x == 0) { y = 0x1337; } else { y = 0xb33f; } } }
      
      





ユニークな逆コンパイラ出力:







 .\porosity.exe --code $binRuntime --decompile function func_14ba3f12 { if (!msg.value) { } if (arg_4 != 0x0) { store[var_xrpMC] = 0xB33F; } store[var_zdGc8] = 0x1337; return; return; }
      
      





ツールを簡単にするためにabiも渡そうとすると、Porosityがクラッシュします。







Porosityのアプリケーションを見つけることができませんでした。使用する試みはすべて、意味のない結論を受け取るか、ツールをクラッシュさせることで終了しました。







マンティコア



プログラムのシンボリック実行のためのツール。 少し前に、EVMサポートが追加され、開発されています(そして、 永続的な開発者が登場すれば、事態はさらに速くなります)。 多くのチップが搭載されていますが、テストされておらず、ほとんどの場合機能しません。 プロジェクトにはかなり悪いドキュメントもありますが、コミュニティがこの欠点をすぐに修正することを願っています。







Manticoreの使用例を見る前に、指で「シンボリック実行」を説明する価値があります。また、Michael Hicksによるプレゼンテーション「バグを見つけるためのシンボリック実行」のスライドが役立ちます。







画像
図4.バグを見つけるためのシンボリック実行。 マイケル・ヒックス


シンボリック実行を使用すると、特定のポイントに関数/プログラムを与えるために必要な入力を理解できます。 大まかに言えば、シンボリック実行中に、特定のポイントへの実行方法に関するすべての制限(条件)が収集され、これらの制限から式が作成され、必要な入力を取得します。







デモンストレーションのために、Positive Hack Days 8で開催されたEtherHackコンテストの「The Lock」タスクを解決します。参加者にはソースコードは渡されず、契約のアドレスのみが渡されました。 タスクを解決するには、正しいPINコードで契約を呼び出す必要がありました(その検証はフォールバックで実装されます)。 ソリューションには、ピンコード検証アルゴリズムを理解するためのリバースエンジニアリングが含まれていました。







この問題の解決策を自動化しましょう。 ただし、契約の逆アセンブルされたコードを見て、参加者が有効なPINコードを送信した場合に何が起こる(または起こらない)かを調べる必要があります。 つまり、プログラムにはsstore命令が1つしかないことに注意する価値があり、チェストのキーが正しく選択された場合に実行されると安全に想定できます。







残念ながら、Manticoreのバグは問題の正直な解決策を提供しません(バグレポートを開発者に送信しました。できればすぐにエラーを修正することを願っています)。出て行かなければなりません。乗算(現在のManticoreのバグにより、シンボリックベースでべき乗をエミュレートできません)。 もちろん、コンテスト中にこのようなひねくれた方法でタスクを解決することは無意味ですが、ツールをデモンストレーションするだけであることを忘れないでください! また、ソースコードが存在しても、シンボリック実行はバイトコードではなくシンボリック実行では機能するため、作業は単純化されません。







十分な言い訳と保護がありますので、スクリプトを見てみましょう。







ロックソルバー
 from manticore.ethereum import ManticoreEVM from manticore.core.smtlib import Operators from struct import pack m = ManticoreEVM() m.verbosity(0) contract_source_code = ''' contract lock { uint public unlocked; function unlock(bytes4 cPincode) payable { uint digitPowers = 0; uint iPincode = 0; for (uint i = 0; i < 4; i++) { if (cPincode[i] >= 0x30 && cPincode[i] <= 0x39) { // manticore can't handle pow with symbolic input at the moment uint digit = (uint(cPincode[i]) - 0x30); digit *= digit; digit *= digit; digitPowers += digit; iPincode += (uint(cPincode[i]) - 0x30) * 10**(3-i); } else { revert(); } } if (uint(iPincode) == 0 || uint(iPincode) == 1) { revert(); } if (digitPowers == uint(iPincode)) { unlocked = 0x31337; } } } ''' user_account = m.create_account(balance=10**18) m.world.set_balance(user_account, 10**18) contract_account = m.solidity_create_contract(contract_source_code, owner=user_account) print "[+] Created a contract account 0x%x" % contract_account print '[+] Sending transaction with symbolic input' symbolic_data = m.make_symbolic_buffer(36) m.transaction(caller=user_account, address=contract_account, data=symbolic_data, value=10**18) pincodes = None for state in m.all_states: world = state.platform for where, what in world.get_storage_items(int(contract_account)): _input = Operators.CONCAT(8*4, *world.human_transactions[-1].data[4:8]) # getting 4 bytes after function signature print '[+] Storage write 0x%x' % state.solve_one(what) pincodes = state.solve_n(_input, 10) print '[+] Found %i pincodes:' % len(pincodes) for pincode in sorted(pincodes): print ' "%s"' % pack('>I', pincode) break if pincodes is not None: break
      
      





実行すると、次の結論が得られます。







 [+] Created a contract account 0x1bfa530d5d685155e98cd7d9dd23f7b6a801cfef [+] Sending transaction with symbolic input [+] Storage write 0x31337 [+] Found 3 pincodes: "1634" "8208" "9474"
      
      





私たちは何が起こっているのかを扱います。 まず、契約コードを見てください。 ロック解除関数は、入力に4バイトを使用します。この入力では、4桁の10進数のテキスト表現が表示されます。 それらのピンコードのみが適切であり、4番目の次数の数字の合計は、ピンコード自体と等しく、0と1を除きます。 確かに









14+64+34+44=$163







ピンコードがこれらの条件を満たしている場合、ストレージにあるunlocked



されていunlocked



変数に数値0x31337が書き込まれます。

それでは、スクリプトを理解しましょう。 最初の部分で何が起こるかは簡単に理解できます。ユーザーアカウントが作成され、次に契約が作成されます。 トランザクションは、シンボリック入力で開始されます。 このアクションを使用すると、シンボリック実行がアクティブになり、数分かかります。 完了したら、すべての最終状態を調べて、何かがストレージに書き込まれているかどうかを確認し、書き込まれている場合は、トランザクションが行われた入力を調べます。 呼び出された関数の署名の4バイト後に、それらからPINコードを抽出します。 問題を解決し、契約が予想するPINコードの桁数がわからない場合、4バイトではなく32バイトすべて(EVMのメモリセルのデフォルトサイズ)が得られます。 次に、画面上に同様の数字が表示されます。







 0x3136333434343434343434343434343434343434343434343434343434343434 0x3832303800000000000000000000000000000000000000000000000000000000
      
      





Manticoreの分析結果を使用して、おもちゃのパズルを解くだけでなく、おもちゃ契約のおもちゃのバグを探すこともできます。「契約を含むすべてのブロードキャストが住所に届くように、契約に何を送信すればよいですか?」







EthCC 2018ワークショップの安全でないウォレットの例を見てみましょう:







保護されていない財布
 from manticore.ethereum import ManticoreEVM from manticore.core.smtlib import solver m = ManticoreEVM() # initiate the blockchain source_code = ''' contract UnprotectedWallet{ address public owner; modifier onlyowner { require(msg.sender==owner); _; } function UnprotectedWallet() public { owner = msg.sender; } // this function should be protected function changeOwner(address _newOwner) public { owner = _newOwner; } function deposit() payable public { } function withdraw() onlyowner public { msg.sender.transfer(this.balance); } } ''' # Generate the accounts. Creator has 10 ethers; attacker 0 creator_account = m.create_account(balance=10*10**18) attacker_account = m.create_account(balance=0) contract_account = m.solidity_create_contract(source_code, owner=creator_account) print "Creator account: 0x%x" % creator_account print "Attacker account: 0x%x" % attacker_account # Deposit 1 ether, from the creator contract_account.deposit(caller=creator_account, value=10**18) # Two raw transactions from the attacker symbolic_data = m.make_symbolic_buffer(320) m.transaction(caller=attacker_account, address=contract_account, data=symbolic_data, value=0) symbolic_data = m.make_symbolic_buffer(320) m.transaction(caller=attacker_account, address=contract_account, data=symbolic_data, value=0) for state in m.running_states: # Check if the attacker can ends with some ether balance = state.platform.get_balance(attacker_account) state.constrain(balance > 1) if solver.check(state.constraints): print "Attacker can steal the ether! see %s"%m.workspace m.generate_testcase(state, 'WalletHack')
      
      





明らかに、開発者はchangeOwnerに属性onlyOwnerを与えることを忘れていました。 このため、2つのトランザクションの攻撃者が資金を得ることができます。 いつものように、最も興味深いのはスクリプトの最後です。 状態を列挙して、「攻撃者はどれにコインを持っていますか?」という質問をします。 そして、マンティコアは彼に答えることができます。 WalletHack_00000000.txファイルのmcore_XXXフォルダーで回答を探します。







画像
図5. Manticoreの作業の結果。


彼らがManticoreを放棄しなければ、スマートコントラクトの柔軟な分析のための優れたツールに成長しますが、現実の世界と実際のタスクに直面する準備はまだ整っていません。







エキドナ (段階的)



エキドナは、スマートコントラクトをファジングするために設計された実験的なツールです。 しかし、これはあなたが考えることができるファズではありません。 Echidnaがスマートコントラクトの機能をファジングするには、特別な方法でコントラクトを準備する必要があります。







 //  contract EchidnaTest { function sensitiveFunc(uint num, bool stop) { if (num > 5 && !stop) { selfdestruct(msg.sender); } } }
      
      





 contract EchidnaTest { uint num; bool stop; function num_setter(uint _num) public { num = _num; } function stop_setter(bool _stop) public { stop = _stop; } function echidna_sensitiveFunc() public returns (bool) { if (num > 5 && stop) { selfdestruct(msg.sender); } return true; } }
      
      





つまり、プロファイリングする機能を「ねじ解除」する必要があります。 Echidnaは、 num_setter



およびstop_setter



を呼び出してからnum_setter



を呼び出します。 また、Echidnaはすべてが順調に進んだときに関数がtrue



を返すことを期待していることに注意してください(ただし、明示的には伝えません)。 したがって、 false



revert()



selfdestruct(0)



などをselfdestruct(0)



revert()



により、「間違った」動作をマークできます。







 p4lex@ubuntu:~/tools/echidna$ echidna-test echid.sol ━━━ echid.sol ━━━ ✗ "echidna_sensitiveFunc" failed after 23 tests and 51 shrinks. │ Call sequence: num_setter(6); │ stop_setter(false); ✗ 1 failed.
      
      





また、エキドナが変更する特定の入力データを設定できないことにも注意してください。したがって、複雑なタスクの場合、このようなファジングの有効性には多くのことが望まれます。 しかし、プロファジングする必要がある各関数を書き直さなければならないという事実は、おそらく気づいたでしょう。 公平に言えば、Haskellで記述できる場合でも、可能な値のセットから特定のドメインを提供するように入力値ジェネレーターを設定できることに注意してください(たとえば、 num



は5以上でなければならないことを知っています)。







一般に、ファジングは実を結ぶことができます。 脆弱性が長いアクションチェーンの後にのみ現れる場合、シンボリック実行にはファジングとは対照的にCPUとメモリの大きな消費が必要になります(ただし、ある程度の確率でのみこの脆弱性を検出します)。 エキドナ使用されているアプローチの詳細については、 こちらをご覧ください







スマートチェック



再入可能性はどこにでもあるように思える、非常に実験的なクローズドソースのツールです。 気持が良いから:契約を送信フォームに貼り付けるか、githubへのリンクを指定できます。 また、静的コード分析が使用されるため、契約をコンパイルする必要さえありません。 次のようになります。







図6. SmartCheckインターフェース


この例では、Ether転送後に残高がリセットされます。これはSmartCheckの署名です。 ただし、 send



機能とtransfer



機能のみを使用すると再入可能性が防止されるため( 第3部でこれについて説明しました)、上のスクリーンショットでは誤検出が見られます。







リバースエンジニアリング



現在、オープンアクセスにはオープンデコンパイラが存在しないため、このロジックまたはそのコントラクトが実装するロジックを理解するために、逆コンパイラを使用する必要があります。 一般に、逆コンパイラが存在する可能性があります。 最初の試みはPorosityで行われましたが、おそらく解決策は機能しますが、残念なことに、RET2の人たち閉じたものを持っています。 それまでは、バイトコードだけを残して、できることを見てみましょう。







EVMdis



最も古く、最も便利な逆アセンブラー。 美しいインターフェースを自慢することはできませんが、他のツールにはないチップがあります:まず、シーケンスの代わりに命令を折りたたみ、もう1つの読みやすい式を表示し、次に各ベースユニットについてEVMdisが入力でスタックを表示します記号化された変数を含むブロックに。 EVMはスタックされたマシンであるため、このデータがなければスタックを関数の最初から復元する必要があるため、これによりコードのロジックを理解するプロセスが大幅に高速化されます。







例:







 # Stack: [] 0x33 PUSH(0xFFFFFFFF & CALLDATALOAD(0x0) / 0x100000000000000000000000000000000000000000000000000000000) 0x34 DUP1 0x3E JUMPI(:label5, 0x2F54BF6E == POP()) ... :label36 # Stack: [@0x420 @0x408 0x0 @0x2A1 @0x282 :label22 @0x33] 0x43F POP() 0x440 POP() 0x443 PUSH(MLOAD(0x40)) ...
      
      





0x33では、値がcalldataから返され、スタックにプッシュされます。 この値は不明であるため、記号化されています。 さらに、他のシンボリックおよび特定の値(番号および遷移アドレス)の中で、ベースブロックラベル36のスタックでそれを観察できます。







私たちの意見では、これは契約の逆展開のための最も便利なツールです。







Ethersplay



EVMアーキテクチャのサポートを追加するバイナリニンジャプラグイン。 EVMはスタックされたマシンであるため、忍者の中間表現(LLILおよびMLIL)は機能しません。したがって、ベアアセンブラーの指示を読む必要があります。 気持が良いから:プラグインには{ : }



ペアを持つ辞書があるため、すぐに多くの有名な関数を定義できます(たとえば、ERC標準の異なるインターフェースを実装する)。







画像
図7. Ethersplayプラグインを使用したバイナリNinjiaインターフェース


プラグインには、各命令のスタックの状態を示す機能もあります。







画像
図8. Ethersplayで表示されるスタックステータス


ただし、すべてのシンボリック変数をUnknown



という単語に置き換えますが、EVMdisは代わりに、このシンボリック変数がスタック上で出現したアドレスを書き込みます。







Ethersplayは私たちの兵器庫に定着していませんが、おそらく有用なツールになる段階まで開発されるでしょう。







アイダエヴム



IDA Proがコントラクトを分解できるようにするプロセッサモジュール。 また、Ethersplayのような関数プロトタイプの辞書もあります。







画像
図9. IDA-EVMプロセッサモジュールを備えたIDAインターフェイス


ただし、これは、プログラム全体のスタックの状態を手動で解決する必要があるため、コントラクトの便利なリバース開発には十分ではありません。 私たちの意見では、このツールは開発を受けることはほとんどなく、現在の形では役に立たないため、このツールは注目に値しません。







テスト中



このセクションでは、実際にあなたの契約から安全な契約をする傾向があるもの、すなわちテストについてお話したいと思います。 実践が示すように、テストで少なくとも95%がカバーされるスマートコントラクトでは、深刻な脆弱性の可能性はわずかです。 テストを作成するプロセスでは、多くの場合、必要なチェックが多く追加されますが、平気ではありません。経験豊富なセキュリティ監査員または開発者のみが語ることができます。 たとえば、契約が実装するサブジェクトエリアの0day脆弱性、またはその他の論理エラーを見つけるか、特定の契約メカニズム(DApp)をより効率的にする方法を提案します。







トリュフ



Truffleを使用して「その場で」テストを実行するのが最も便利です(ほとんどのスマートコントラクトが開発されています)。 テストフォルダー( truffle init



後に表示されtruffle init



)で、テストを作成し、 truffle test



コマンドを使用して実行できます。 ただし、公式ドキュメントよりもトリュフを使用したテストについて詳しく説明できる人はほとんどいません。 また、 ソリッドカバレッジツールを使用したテストでカバレッジを測定することを忘れないでください。







web3.js



何らかの理由(多くの場合)でTruffleがあなたに合わない場合、web3ライブラリを使用してテストを書くことができます(それらは異なる言語で利用可能です)。 記述されたテストコードは、ロジックの一部として将来のDAppに簡単に転送できるため、web3.jsを使用するのが最も便利です。 唯一の注意点は、web3.jsバージョン^ 1.0.0を使用することです。 未来は彼女のものであり、Googleの最初の行となるバージョン0.14.0では互換性がありません。 例を考えてみましょう:







ソースコード
 pragma solidity ^0.4.23; contract Testable { address public human; uint private counter; constructor() { human = tx.origin; } event CallmeLog(); function callme(uint times) public { counter++; if (times > 1) { callme(--times); } else { emit CallmeLog(); } } }
      
      





テスト自体に進む前に、ネットワークに新しいコントラクトをアップロードする関数を作成する必要があります。これは一度だけ行う必要があるためです:)







テスト
 const solc = require('solc'); const fs = require('fs'); const Web3 = require('web3'); const web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545')); const code = fs.readFileSync('./testable.sol'); async function main() { // when use unlocked account from node let defaultAccs = await web3.eth.getAccounts(); web3.eth.defaultAccount = defaultAccs[0]; // this is for known private key // web3.eth.accounts.wallet.add(privKey.key); let Testable = await deploy(); human_test(Testable); callme_tx_status_test(Testable, 3); callme_event_test(Testable, 3); // let's got new instance Testable = await deploy(); callme_storage_test(Testable, 5); } async function deploy() { let output = solc.compile(code.toString(), 1); let TestableABI = output.contracts[Object.keys(output.contracts)[0]].interface; let TestableBytecode = output.contracts[Object.keys(output.contracts)[0]].bytecode; let Testable = new web3.eth.Contract(JSON.parse(TestableABI),{ from: web3.eth.defaultAccount }); let gasCount = await Testable.deploy({ data: TestableBytecode }).estimateGas(); Testable = await Testable.deploy({ data: TestableBytecode }).send({ gas: gasCount }); return Testable; } async function human_test(instance) { // let's test that contract has owner (human) // use "call" for public variables and constant methods let humanAddress = await instance.methods.human().call(); if (humanAddress !== web3.eth.defaultAccount) { throw "✕ human address is wrong" } else { console.log(" human_test passed"); } } ...
      
      





, ( ) . web3_utilz/Testing .









, , , , , , CTF.







(reverse engineering), , . , , - ( cryptoKittes ). , reverse engineering. , , , .







, , p4lex Igor1024 .








All Articles