Ethereumの勉強: Hello, World! Contract by Solidity (macOS)
Hello, world!してみようと思います。
以下のリンクを参考にしました。
- Ethereum スマートコントラクト入門:geth のインストールから Hello World まで: 大変わかりやすく、チュートリアルを求めて私の記事を読もうと思っている方は、こちらの記事を読めばいいと思います。
- Ethereum入門: メンテがされていないのか少し古く、solidityのインストールでハマりました。基本概念や、プライベートネットワークの作り方が書いてあります。
- Wiki note on removed geth support for solidity compilation · Issue #14850 · ethereum/go-ethereum: geth で solidity を使うための方法が書かれたissue
- 追記: 2018/04/25: CryptoZombies とても勉強になります!
動機
geth が v1.6.0 から solidity のコンパイルをサポートしなくなったけれど、あまりそれについて触れているドキュメントがなかったので色々苦労したため、一応この時点ではこれでいけたよ!ということを残しておきます;
- 環境: macOS High Sierra 10.13
- 書いた日付: 2018/02/13
- geth: 1.7.3-stable
- solc(solidity): 0.4.19+commit.c4cbbb05.Darwin.appleclang
用語
- geth: go 言語で書かれた Ethereum クライアント
- コントラクト: Ethereum 上で動作するコード
- solidity: Ethereum 上でコントラクトを記述するための言語またはコンパイラ (solidity で書いたプログラムを実行環境である Ethereum Virtual Machine で解釈できるようにコンパイルする)
Ethereum について
ここで言う Ethereum はブロックチェーン上でチューリング完全なプログラムを動かせるプラットフォームのことらしいです。一般的に知られている仮想通貨の方は ether と読んで区別しています。ブロックチェーン上にコードが追加できて、そこに向けて GAS (コードが使う資源に応じた実行に必要な ether )や引数などを投げるとコードが実行される感じです。
プログラミングする観点から言うと、コードの書き方によって使用される GAS が異なる(コードを実行するための GAS を少なくする最適化スキルが重宝される)ことが重要な気がします。
Install geth
$ brew tap ethereum/ethereum $ brew install geth $ geth version
無事インストールできていることを確認できました。
Private チェーンの作成
新しく実験用のディレクトリを作って、そこで geth を起動します。 --dev
オプションによって、本来の Ethereum のチェーンではなく自分専用の開発用のチェーンで起動します。
$ mkdir path/to/somewhere $ cd path/to/somewhere $ geth --dev --datadir .
INFO がつらつらと出てきたら起動成功のようです。 WARN が出るかもしれないですが Block sealing failed
などは --dev
のせいなので気にしなくていいようです。
注意: 今回はコントラクトを作成して実行するだけが目的なのでひとまず気にしなくていいですが、--dev
で起動した場合、マイニングが行われるのはトランザクションが生成された時のみのようです # 。初めから大量の ether を持ったアカウントが一人登録されており、このアカウントが miner (マイニングを行うアカウント)の役割をふられています。パスワードは空です。
別コンソールから (JavaScript) 対話環境にログインします。基本的にこのインタフェースで作業していくようです。
$ ls … geth.ipc … $ geth attach geth.ipc
別コンソールで先ほど実行した geth --dev --datadir .
によって geth.ipc
と言うファイルが作成されていることを確認できました。geth attach geth.ipc
を実行することで対話環境に入れます。
> eth.accounts ["0x75eece28f8ce7b99af2af2324dbb17f5c1aab56e"]
とりあえず今のところやることはないですが、eth
、miner
、web3
などをいじって遊んでみるといいと思います。
Install solidity (solc コマンド)
コントラクトを書くために solidity をインストールします。
$ brew install solidity $ brew link solidity $ solc --version
結構時間がかかりました。適当なコマンドを打ってみて、ちゃんとインストールされていることが確認できました。
コントラクトの作成
Hello コントラクトを作成していきます。Hello, world! までの流れとしては以下の 3steps です;
- ソースをコンパイルして abi (Application Binary Interface), bin (Binary)を取得
- 対話環境で
eth.contract
にabi
を食わせてコントラクトの雛形を作成、コントラクトの雛形にコンストラクタ引数として bin と 送信者 と gas を与えてインスタンス化する - インスタンスに対してメソッドの呼び出し(今回はcall)を行う
step 1
solidity のコードサンプルがいくつかネットに落ちているのでそれを参考に Hello, world! プログラムを書きました。拡張子は sol が一般的なようです。ファイル名の命名規則などは後々でいいかのとか思いました。
hello.sol
pragma solidity ^0.4.0; contract Hello { function say() public pure returns (string) { return 'Hello, world!'; } }
コンパイルします。使いやすく出力フォーマット変えたりできないのかなとか思いますが、ターミナルで加工します。「コンパイル後のコードを json の形で吐き出した後、compilerOutput 変数に代入する」と言う js ファイルを作成します。
$ echo "var compilerOutput = `solc --optimize --combined-json abi,bin hello.sol`" > hello.js $ less hello.js var compilerOutput = {"contracts":{"hello.sol:Hello":{"abi":"[{\"constant\":true,\"inputs\":[],\"name\":\"say\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"}]","bin":"6060604052341561000f57600080fd5b61014e8061001e6000396000f3006060604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663954ab4b28114610045575b600080fd5b341561005057600080fd5b6100586100cf565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561009457808201518382015260200161007c565b50505050905090810190601f1680156100c15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100d7610110565b60408051908101604052600d81527f48656c6c6f2c20776f726c6421000000000000000000000000000000000000006020820152905090565b602060405190810160405260008152905600a165627a7a723058206803a6ff4f3b05c9ebd7f5f75edb032cfd89ce2b8177d8653269f91a932178720029"}},"version":"0.4.19+commit.c4cbbb05.Darwin.appleclang"}
solc のオプション abi, bin は 出力される json に含める内容です。
対話環境でコードをロードします。ここまでくればコンパイルは OK と見ていいのだろうか?
> loadScript('./hello.js') true > compilerOutput { contracts: { hello.sol:Hello: { abi: "[{\"constant\":true,\"inputs\":[],\"name\":\"say\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"}]", bin: "6060604052341561000f57600080fd5b61014e8061001e6000396000f3006060604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663954ab4b28114610045575b600080fd5b341561005057600080fd5b6100586100cf565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561009457808201518382015260200161007c565b50505050905090810190601f1680156100c15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100d7610110565b60408051908101604052600d81527f48656c6c6f2c20776f726c6421000000000000000000000000000000000000006020820152905090565b602060405190810160405260008152905600a165627a7a723058206803a6ff4f3b05c9ebd7f5f75edb032cfd89ce2b8177d8653269f91a932178720029" } }, version: "0.4.19+commit.c4cbbb05.Darwin.appleclang" }
abi, bin を含んだ感じのものが表示されました。
step 2
コントラクトを作成して実行してみます。abi は JSON.parse, bin は頭に '0x' がついてないので代入時につけてあげます。
> var abi = JSON.parse(compilerOutput.contracts['hello.sol:Hello'].abi) > var bin = '0x' + compilerOutput.contracts['hello.sol:Hello'].bin > var contract = eth.contract(abi) > Hello = contract.new({ from: eth.coinbase, data: bin, gas: 100000 }) > Hello { abi: [{ constant: true, inputs: [], name: "say", outputs: [{...}], payable: false, stateMutability: "pure", type: "function" }], address: "0x34f2deef0f113982608a8a943c85dc74293daa7a", transactionHash: "0xc81c82a8a3825a5cfef35122f06036ad220601a53f38e029a1a00ff132d3187b", allEvents: function(), say: function() }
addressが振られているのでOKみたいです。
注意: 実は何回か試行錯誤していて、アカウントのパスワード聞かれたり、Hello インスタンスに address が振られないという現象がありました。 eth.coinebase のアカウントはパスワードが空で、 address 振られないときは miner.stop() & miner.start() とマイニングを再起動させたりしたら動作するようになりました。(なぜだろう…)
step 3
Hello.say
に対して call
を実行します。
> Hello.say.call() "Hello, world!"
ようやく出ました!