Blockchain
Ethereum
solidity
SmartContract
geth

Ethereum スマートコントラクト入門:geth のインストールから Hello World まで

前提知識

  • Blockchain、Ethereum、Smart Contract 等の概念的理解(参考図書
  • 基本的なプログラミングスキル

プライベートチェーンの立ち上げ

今回は、geth を使ってローカルマシン上に Ethereum のプライベートチェーン(自分だけがアクセス可能なブロックチェーン)を立ち上げます。

geth は Go言語で実装された Ethereumクライアントです。アカウントの作成からマイニングまで、Ethereum に関わる多くの機能を担います。

geth のインストール

macOS の場合

$ brew tap ethereum/ethereum
$ brew install ethereum

Ubuntu の場合

$ sudo add-apt-repository -y ppa:ethereum/ethereum
$ sudo apt update
$ sudo apt install ethereum

geth の起動

データを入れるためのディレクトリを任意の場所名前で作成し、ターミナル上でそのディレクトリ内に移動しておいてください。ここでは、ホームディレクトリ直下の my_eth_chain としました。

$ mkdir ~/my_eth_chain
$ cd ~/my_eth_chain

以下のオプションを付けて geth を起動します。

$ geth --dev --datadir .
--dev        : 開発用途のプライベートチェーン設定で起動する
--datadir .  : カレントディレクトリをデータディレクトリに指定する

そうしたら別のターミナルを開き、以下のコマンドで geth のコンソールを開きます。geth.ipc があるディレクトリ (datadir) の中で実行してください。

$ geth attach geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0

> 

このコンソールでは、Web3 API を通じて geth ノードとインタラクティブにやり取りすることができます。

アカウントの作成

まだアカウントが1つもない状態なので、コンソールからアカウントを1つ作成します。新規アカウント用のパスワードを尋ねられますが、今回は自分だけが使う開発用のプライベートチェーンなので、空でも構いません。

> personal.newAccount()
Passphrase: 
Repeat passphrase: 
...
"0x13118d9a345f83433506e20f8e9ae8cb0dddc5e9"

0x から始まる文字列が今作成したアカウントのアドレスです。このアカウントの秘密鍵は ~/my_eth_chain/keystore の中に作成されています。

もう1つアカウントを作っておきましょう。

> personal.newAccount()
...
"0x11cc1aeb1e7cbba8b64fa8dfd638f09f9188ea5f"

次のコマンドは、作成済みのアカウントのアドレスを配列で表示します。

> eth.accounts
["0x13118d9a345f83433506e20f8e9ae8cb0dddc5e9", "0x11cc1aeb1e7cbba8b64fa8dfd638f09f9188ea5f"]

このとき、1番目のアドレス (eth.accounts[0]) は、Coinbase (Etherbase) と呼ばれる特別なアカウントです。eth.coinbase でも Coinbase アドレスは確認できます。

> eth.coinbase
"0x13118d9a345f83433506e20f8e9ae8cb0dddc5e9"

ブロックをマイニングしたときの報酬 (Ether) は、その geth ノードの Coinbase アカウントが受け取ることになります。

Ether の取得(マイニング)

eth.getBalance() で Coinbase アカウントの現在の残高 (Balance) を確認してみると、まだ 0 であることが確認できると思います。

> eth.getBalance(eth.accounts[0])
0

では Ether を受け取るために、マイニングを開始してみましょう。

> miner.start()

geth が動いている方のターミナル画面を見てみると、次々とブロックが採掘されているのが確認できると思います。--dev オプション付きで起動したチェーンでは採掘難易度が低く設定されているので、短時間でマイニングを進めることができます。

そうしたら一旦マイニングを止めて、Coinbase アカウントの Ether 残高を再度確認してみると、残高が増えているはずです。

> miner.stop()
true
> eth.getBalance(eth.accounts[0])
55000000000000000000

大きな桁の数字が出ましたが、この数字の単位は ether ではなく wei になります。wei は Etherum における最小の単位で、1 ether = 1018 wei なので、上の場合は 55ether の報酬を受けたことになります(11ブロックを採掘した結果です)。

Ether の送金

次に送金のテストとして、先程作った2つ目のアカウントに対して、1つ目のアカウント (Coinbase) から 1ether (=1018wei) を送ってみましょう。

現在の残高は 0 です。

> eth.getBalance(eth.accounts[1])
0

eth.sendTransaction({ from: 送金元アドレス, to: 送金先アドレス, value: 送金額 (wei) }) が送金メソッドですが、これを実行するためには、先立って送金元アカウントのアンロックが必要です。

> personal.unlockAccount(eth.accounts[0])
Passphrase: 
true

では送金を実行します。

> eth.sendTransaction({ from: eth.accounts[0], to: eth.accounts[1], value: 1000000000000000000 })
...
"0x75cb60459676362efe9fb2973de9333ad5e2e64d41f68e0c3861ddf552ccdffb"

トランザクションの ID が表示されましたが、この段階ではまだ送金は完了していません。採掘して、このトランザクションがブロックチェーンに書き込まれる必要があります。

今回立ち上げたプライベートチェーンではノードは自分しかいないので、マイニングも自分でする必要があります。マイニングを開始して、1ブロック以上採掘してください。

> miner.start()

すると、eth.accounts[1] の残高が 1ether = 1018wei になっていることが確認できると思います。

> eth.getBalance(eth.accounts[1])
1000000000000000000

スマートコントラクトで Hello World

それでは、Ethereum の核心であるスマートコントラクトに移ります。スマートコントラクトの専用記述言語である Solidity を使ってプログラムを書き、先程立ち上げたプライベートチェーンにコードをデプロイするという流れになります。

solc のインストール

まず、Solidity のコンパイラである solc をインストールします。solc でコンパイルされたバイトコードは、Ethereum の EVM (Etherum Virtual Machine) という仮想マシンで実行されます。Java のバイトコードが JVM で実行されるのと一緒です。

macOS の場合

$ brew install solidity

Ubuntu の場合

$ sudo apt install solc

Solidity コードの記述

以下の Solidity コードを書いて、HellowWorld.sol という名前で任意の場所に保存してください。

HelloWorld.sol
pragma solidity ^0.4.13;

contract HelloWorld {
    string message;

    function setMessage(string _message) {
        message = _message;
    }

    function sayHello() returns (string) {
        return message;
    }
}

sayHello() メソッドを呼んだら、setMessage() で設定された文字列 (message) を返すだけの単純なコントラクトです。

contract HelloWorld は通常のプログラミング言語におけるクラスのようなものですが、contract の状態変数として宣言されたデータ(ここでは string message)は、ブロックチェーン上に永続的に記録されることになります。

Solidity コードのコンパイル

新しくターミナルを立ち上げて、HelloWorld.sol を保存したディレクトリ内で以下のコマンドを実行することで Solidity コードをコンパイルします。

$ solc --bin --abi HelloWorld.sol

すると、BinaryContract JSON ABI という2つの項目の結果が表示されます。

======= HelloWorld.sol:HelloWorld =======
Binary: 
6060604052341561000f57600080fd5b5b6102de8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063368b877214610049578063ef5fb05b146100a6575b600080fd5b341561005457600080fd5b6100a4600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610135565b005b34156100b157600080fd5b6100b9610150565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100fa5780820151818401525b6020810190506100de565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061014b9291906101f9565b505b50565b610158610279565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101ee5780601f106101c3576101008083540402835291602001916101ee565b820191906000526020600020905b8154815290600101906020018083116101d157829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023a57805160ff1916838001178555610268565b82800160010185558215610268579182015b8281111561026757825182559160200191906001019061024c565b5b509050610275919061028d565b5090565b602060405190810160405280600081525090565b6102af91905b808211156102ab576000816000905550600101610293565b5090565b905600a165627a7a7230582068aa6f9fa0b0611b69c6cdd9f024612fc651736ea66b3319e407b34d22b2ea5d0029
Contract JSON ABI 
[{"constant":false,"inputs":[{"name":"_message","type":"string"}],"name":"setMessage","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"sayHello","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}]

Binary が EVM のバイトコードで、ABI (Application Binary Interface) は、コントラクトのインタフェース情報になります。

プライベートチェーンへのデプロイ

上で得られた Binary と ABI を使って、ブロックチェーン上に HelloWorld コントラクトのデプロイ(コントラクトのアカウント作成)を行います。

起動中の geth のコンソールに戻って、以下のコマンドを実行してください。bin の方には、コンパイル結果の Binary の先頭に 0x を付けた上で文字列にして代入します。

> bin = "0x6060604052341561000f57600080fd5b5b6102de8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063368b877214610049578063ef5fb05b146100a6575b600080fd5b341561005457600080fd5b6100a4600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610135565b005b34156100b157600080fd5b6100b9610150565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100fa5780820151818401525b6020810190506100de565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061014b9291906101f9565b505b50565b610158610279565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101ee5780601f106101c3576101008083540402835291602001916101ee565b820191906000526020600020905b8154815290600101906020018083116101d157829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023a57805160ff1916838001178555610268565b82800160010185558215610268579182015b8281111561026757825182559160200191906001019061024c565b5b509050610275919061028d565b5090565b602060405190810160405280600081525090565b6102af91905b808211156102ab576000816000905550600101610293565b5090565b905600a165627a7a7230582068aa6f9fa0b0611b69c6cdd9f024612fc651736ea66b3319e407b34d22b2ea5d0029"

> abi = [{"constant":false,"inputs":[{"name":"_message","type":"string"}],"name":"setMessage","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"sayHello","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}]

ABI インタフェースから HelloWorld のコントラクトオブジェクトを作成します。

> contract = eth.contract(abi)

そして、コントラクトをチェーン上に登録するトランザクションを送信します。

> HelloWorld = contract.new({ from: eth.accounts[0], data: bin, gas: 1000000 })

既にマイニングしていれば、このトランザクションもすぐにブロックチェーンに取り込まれます。マイニングが止まっているのであれば、miner.start() をしてください。

> HelloWorld
{
  abi: [{
      constant: false,
      inputs: [{...}],
      name: "setMessage",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [],
      name: "sayHello",
      outputs: [{...}],
      payable: false,
      type: "function"
  }],
  address: "0x784c38f5fc8e2dcb9b3258272eb7a35a55e0ed52",
  transactionHash: "0x3a998bcefbe8097c363b2981390eca4e3e1c5f81b07281f8fabaaab60541ed9e",
  allEvents: function(),
  sayHello: function(),
  setMessage: function()
}

HelloWorld オブジェクトの address 部分にアドレスが入っていれば、このスマートコントラクトはブロックチェーン上に正しく書き込まれています。

スマートコントラクトの呼び出し

コンソール上の HelloWorld オブジェクトを使って、コントラクトのメソッドを呼び出してみましょう。

スマートコントラクトのメソッドの実行方法には、sendTransaction()call() との2種類があります。

sendTransaction()

sendTransaction() は、ブロックチェーン上に値を書き込むメソッドを呼び出すときに使用します。今回の例で言えば、setMessage() メソッドは、状態変数 message の値を更新するので sendTransaction を使う必要があります。

sendTransaction() は、マイナーに採掘される必要があり、そのための費用(手数料)がかかります。手数料は Gas という単位で支払われます。

call()

call() は、ブロックチェーンの状態を変化させない “Read Only” のメソッドの場合に使用します。今回の例で言えば、sayHello() メソッドは状態変数の更新を伴わないので、call() で呼び出せます。

マイナーによる新たなブロック生成は必要ないので、call() には手数料 (Gas) は発生しません。

Hello, world!

では実際にデプロイしたコントラクトを呼び出してみましょう。呼び出し方は、

コントラクトオブジェクト.メソッド名.call(引数)

コントラクトオブジェクト.メソッド名.sendTransaction(引数, { from: 送信元アドレス })

です。

最初に sayHello() を実行すると、まだ message の値はセットされていないので空文字が返ってきます。

> HelloWorld.sayHello.call()
""

では、"Hello, world!" という文字列を message に代入するために、以下の sendTransaction を実行します。

> HelloWorld.setMessage.sendTransaction("Hello, world!", { from: eth.accounts[0] })
"0xa1d4221d6859245ca79c9945b97bdfa941d6e5442472be38b88159a2124d7098"

トランザクションIDが表示され、マイニングしていればすぐにブロックチェーンに反映されます。

(このとき Error: authentication needed: password or unlock と怒られたら、personal.unlockAccount(eth.accounts[0]) で送金元アカウントをアンロックしてください)

そして、再度 HelloWorld.sayHello.call() を実行することで、めでたく "Hello, world!" と表示されました。

> HelloWorld.sayHello.call()
"Hello, world!"

おわりに

geth のインストールから、簡単なスマートコントラクトのデプロイ、実行までを行いました。Solidity による Ethereum スマートコントラクト開発の情報はまだまだ少ないですが、この記事がきっかけになってブロックチェーン/イーサリアム開発者が日本でも増えればいいなと思います。

参考図書

参考リンク