Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

前提知識

  • 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 スマートコントラクト開発の情報はまだまだ少ないですが、この記事がきっかけになってブロックチェーン/イーサリアム開発者が日本でも増えればいいなと思います。

参考図書

参考リンク

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away