前提知識
- 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
という名前で任意の場所に保存してください。
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
すると、Binary
と Contract 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 スマートコントラクト開発の情報はまだまだ少ないですが、この記事がきっかけになってブロックチェーン/イーサリアム開発者が日本でも増えればいいなと思います。
参考図書
-
『いまさら聞けない ビットコインとブロックチェーン』
- ゼロからブロックチェーンのことを学びたい人向け
-
『スマートコントラクト本格入門―FinTechとブロックチェーンが作り出す近未来がわかる』
- スマートコントラクトの概念を学びたい人向け
-
『ブロックチェーン イーサリアムへの入り口』/[『はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門』]
(https://www.amazon.co.jp/dp/B07416W2PY/)- geth や Solidity の具体的な技術入門書
参考リンク
-
Ethereum.org
- Ethereum Project のページ
-
geth
- Go言語製の Ethereum クライアント
-
Solidity
- 開発言語 Solidity のドキュメント
-
Web3 JavaScript API
- Ethereum の JavaScript API
-
JSON RPC API
- Ethereum の JSON RPC API
-
Truffle
- Ethereum 開発フレームワーク
- The DAO