電通国際情報サービス オープン イノベーション ラボの比嘉です。 今回のテーマは、スマート コントラ クト。スマート コントラ クトとは ブロックチェーン 上にデプロイされるプログラムのことで、今回は、 Gethはじめました の続きになります。 まだ、上記の記事をやってない方は、お手数をおかけしますが先に上記の記事をやってください。GethはEthereumクライアントの中で公式に推奨されているクライアントです。 Gethの再起動 Solidityのインストール macOSの場合 Windowsの場合 スマートコントラクトのコード solc スマートコントラクトのデプロイ まとめ Gethの再起動 上記記事で、Gethのメインプロセスを終了させた方は、もう一度再起動しましょう。 $ cd private_net $ geth --networkid 1203 --nodiscover --datadir . 別のターミナルを立ち上げて、メインプロセスにコンソールで接続します。 $ cd private_net $ geth attach geth.ipc 採掘が止まっている場合は、採掘を開始しましょう。 > eth.mining false > miner.start() null Solidityのインストール Ethereumでスマート コントラ クトを書くための プログラミング言語 としては、Solidity, Vyper, Fe, lllなどがありますが、よく使われているのはSolidityです。そこで、今回はSolidityをインストールします。 Solidityをインストールするための 公式サイトはこちら macOS の場合 Homebrewを事前にインストールし、次のコマンドを実行しましょう。 $ brew update $ brew upgrade $ brew tap ethereum/ethereum $ brew install solidity $ brew linkapps solidity brew update を実行するときに Error: homebrew-core is a shallow clone. のエラーが、起きる場合があります。その場合はメッセージに、書いてあるとおりに、gitコマンドを実行しましょう。 インストールがうまくいっていれば、次のコマンドが成功します。 $ solc --version Windows の場合 ダウンロードサイト から、 solc-windows.exe を落としてインストールしましょう。 スマート コントラ クトのコード 最初のスマート コントラ クトとして、数値をスマート コントラ クトの状態変数に格納したり、取り出したりするプログラムを書いてみましょう。 NumberRegister.sol pragma solidity ^ 0.8 . 10 ; contract NumberRegister { uint num; function set( uint _num) public { num = _num; } function get() public view returns ( uint ) { return num; } } Solidityの細かい文法については、今後の記事で取り上げるので、細かいことは気にしなくて大丈夫です。この NumberRegiseter.sol をprivate_netの ディレクト リに保存しましょう。 solc SolidityのプログラムをEthereumで実行できるようにするには、 solc で コンパイル します。 コンパイル とは、 NumberRegiseter.sol のような人間が見て理解しやすいコードをコンピューターが実行できるように変更することです。 もう一つ、ターミナルを立ち上げて、 solc を実行しましょう。 $ cd private_net $ solc --abi --bin NumberRegister.sol 次のような出力が表示されたはずです。 ======= NumberRegister.sol:NumberRegister ======= Binary: 608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220348a0bef0ed2915311a28c4b28b351b950a27275e260ed0a915108c3b0257d2c64736f6c634300080a0033 Contract JSON ABI [{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_num","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}] Binaryのところに書いてある 6080 ではじまり 0033 で終わる文字列の先頭に0xを付加した上で、シングルクォートでくくり、Gethのコンソールに貼り付けます。BinaryはEVM(Ethereumの 仮想マシン )が理解できる バイトコード です。 > bin = '0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220348a0bef0ed2915311a28c4b28b351b950a27275e260ed0a915108c3b0257d2c64736f6c634300080a0033' 同様に Contract JSON ABI もコンソールに貼り付けましょう。ABIはスマート コントラ クトの入出力の仕様になります。 > abi = [{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_num","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}] ここで定義した bin と abi は、この後で使うので覚えておいてください。binはEVM(Ethereumの 仮想マシン )が理解できる バイトコード 、abiはスマート コントラ クトの入出力の仕様です。 スマート コントラ クトのデプロイ 先ほどのスマート コントラ クトをEthereumネットワークにデプロイしましょう。 > cnt = eth.contract(abi).new({from: eth.accounts[0], data: bin}) Error: authentication needed: password or unlock at web3.js:6347:37(47) at web3.js:5081:62(37) at web3.js:3021:48(134) at <eval>:1:28(16) Error: authentication needed: password or unlock が発生した場合は、アカウントをunlockAcount()しましょう。 トランザクション を実行する場合は、アカウントをunlockAccount()する必要があります。このエラーは何度も見ることになるので、どう対応すれば良いか体で覚えましょう。 > personal.unlockAccount(eth.accounts[0]) Unlock account 0x29faad1bb68151278c47df617766bf045c9b2b00 Passphrase: true > cnt = eth.contract(abi).new({from: eth.accounts[0], data: bin}) { abi: [{ inputs: [], name: "get", outputs: [{...}], stateMutability: "view", type: "function" }, { inputs: [{...}], name: "set", outputs: [], stateMutability: "nonpayable", type: "function" }], address: undefined, transactionHash: "0x13658235f5bff4afefd57b504a048f5c2bc37241bacfd9ce58177627aa1882b2" } cntの中身のaddressがundefinedになっていますね。これは、このスマート コントラ クトのデプロイがまだ終わっていないことを表しています。Gethのメインプロセスのログで採掘がきちんと行われていることを確認したら、cnt.addressを確認しましょう。 > cnt.address "0x102118ad7f8bede0158d2353660f63ea5780f966" スマート コントラ クトに、アクセスするにはABI(スマート コントラ クトの入出力の仕様)とaddressが必要です。それでは、NumberRegister コントラ クトを作りましょう。 > cnt2 = eth.contract(abi).at(cnt.address) { abi: [{ inputs: [], name: "get", outputs: [{...}], stateMutability: "view", type: "function" }, { inputs: [{...}], name: "set", outputs: [], stateMutability: "nonpayable", type: "function" }], address: "0x102118ad7f8bede0158d2353660f63ea5780f966", transactionHash: null, allEvents: function(), get: function(), set: function() } スマート コントラ クトの状態を変更しない場合は、 トランザクション なしで実行できます。それでは、NumberRegister.get()メソッドを呼び出してみましょう。 コントラクトオブジェクト.メソッド名.call() で呼びだすことができます。 > cnt2.get.call() 0 スマート コントラ クトの状態を変更する場合、 コントラクトオブジェクト.メソッド名.sendTransaction() を呼び出します。それでは、NumberRegister.set()を呼び出してみましょう。 > cnt2.set.sendTransaction(1, {from: eth.accounts[0]}) Error: authentication needed: password or unlock at web3.js:6347:37(47) at web3.js:5081:62(37) at web3.js:4137:41(57) at <eval>:1:36(10) エラーが出ていない人は気にしなくて大丈夫です。上記のエラーが出た人は、unlockAccount()を呼び出しましょう。 > personal.unlockAccount(eth.accounts[0]) Unlock account 0x29faad1bb68151278c47df617766bf045c9b2b00 Passphrase: true > txid = cnt2.set.sendTransaction(1, {from: eth.accounts[0]}) "0xf12e076acec31684bade23f3ca12c14396160a86464ce1298bbe10e795824f2e" Gethのメインプロセスのログで採掘が動いていることを確認し、NumberRegister.get()を呼び出してみましょう。 > cnt2.get.call() 1 cnt2.set.sendTransaction() の第一引数で指定した値が、設定されていることが分かります。 まとめ スマート コントラ クトを コンパイル すると、ABI(入出力の仕様)とバイナリー(EVMが理解できる バイトコード )が出力されます。 バイナリーをEthereumネットワークにデプロイするとaddressが付加されます。 ABIとaddressを使って コントラ クトオブジェクトを作成して、スマートコンタクトにアクセスします。 スマート コントラ クトの状態を変更しない場合、 コントラクトオブジェクト.メソッド名.call() を呼び出します。 スマート コントラ クトの状態を変更する場合、 コントラクトオブジェクト.メソッド名.sendTransaction() を呼び出します。 今回はここまで。Gethを止めておきましょう。コンソールはCTL-D。メインプロセスはCTL-Cで止めます。正直、GethのコンソールはNode.jsのモジュールも使えないなど、かなり不便です。次回からは、開発環境として Hardhat を使います。 僕の書いたNFT関連の記事 Gethはじめました NFT入門 執筆: @higa 、レビュー: @sato.taichi ( Shodo で執筆されました )