Ethereum入門(2) - スマートコントラクトでHello world!

スマートコントラクトで、`helloworld`を実行します。ここでは、スマートコントラクトの革新的なところである、ある処理（この場合、`helloworld`）をした際に、トランザクションが発生し、コストがかかると言うことを体現することが出来ます。

環境構築がまだの場合は、
[Ethereum入門(1) - スマートコントラクトの開発環境を構築する](https://qiita.com/takeoworks/items/d4c3c098ae1e256b4f44) から、環境構築を済ませてください。

スマートコントラクトを実施するための、ディレクトリの作成から入ります。このhelloworldディレクトリ配下に作成していきます。

```
$ mkdir -p /Users/test/applications/blockchain/helloworld
$ cd helloworld
```

# nodejs用のNPM 管理パッケージのインストール

ここで、管理を簡単にするために、`npm` を導入します。`npm` は、`node.js` で使われるパッケージ管理ツールです。`npm` のリポジトリに、ツールが追加されていきます。スマートコントラクトを扱う際は、`node.js` を使うため、パッケージの管理に `npm` を使用していきます。管理したいディレクトリ（パッケージディレクトリ）で `npm init` コマンドを打ち込むことでパッケージを初期化します。初期化中に色々と聞かれますが、デフォルトでエンターキーを叩いて大丈夫です。

```
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (helloworld) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /Users/takeo/Applications/blockchain/helloworld/package.json:

{
  "name": "helloworld",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes) yes
```

`tree` で作成されたファイルを見てみます。`npm` パッケージが `package.json` を生成し、このファイルを使用して、プロジェクトが管理されます。

```
$ tree
.
└── package.json

0 directories, 1 file
```

npmのチートシートとして、分かりやすかったエントリがあったので、リンクを記載しておきます。ありがとうございます！
[npmコマンドの使い方](https://qiita.com/yoh-nak/items/8446bf12094c729d00fe)


# Helloworld アプリに必要なオブジェクトのインストール
`helloword`アプリケーションに必要な、依存関係のあるパッケージをインストールしていきます。
イーサリアムノードの`RPC`エンドポイントのラッパーで、スマートコントラクトと連携する `web3` と スマートコントラクトを記述する`solidity`の コンパイラである `solc` をインストールします。`npm install web3@0.20.0 solc` コマンドでインストールすることになります。`web3` はまだ開発中とのことらしいのですが、比較的安定している0.20 バージョンを使用していきます。

```
$ npm install web3@0.20.0 solc
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN helloworld@1.0.0 No description
npm WARN helloworld@1.0.0 No repository field.

+ web3@0.20.0
+ solc@0.4.18
added 71 packages in 10.616s
```

ここで、`package.json` ファイルの中身を見てみましょう。`npm init` したものに加えて、`dependencies`に、`web3`と`solc`が追加されているのが確認出来ます。このようにNPMは、パッケージを管理します。後にディレクトリごと別の環境に持っていった際に、npm install とすることで、インストール作業を簡単にしてくれます。

```
$ cat package.json
{
  "name": "helloworld",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "solc": "^0.4.18",
    "web3": "^0.20.0"
  }
}
```
tree を打ち込んでみると、インストールされた多くのパッケージが確認できたと思います。これは、web3 solc に依存する全てのパッケージがインストールされたからです。このように、インストールされる依存関係も拾って、一括でインストールしてくれます。

# Helloworld スマートコントラクトの実装
簡単なスマートコントラクトを記述します。getHelloworld()関数で、メッセージを表示し、setHelloworld()関数では、出力するメッセージがセット出来るようにっています。setHelloworld()関数を実行することで、トランザクションが実行され、ブロックチェーン上に記載されることになり、またコストが発生することになります。

olidity のコンパイラーが、0.4.18 となっていることに気をつけてください。コントラクターの名前は、helloworld です。function helloworld() は、コンストラクターで、ブロックチェーン上に配置される際に、一度だけ呼ばれます。新たに配置する際は、別のインスタンスを作成することになるので、新しいアドレスが割当てられます。

```helloworld.sol
pragma solidity ^0.4.18;

contract helloworld {
  string message;
  function helloworld() public {
    message = "Hello World！";
  }

  function setHelloworld(string _message) public {
    message = _message;
  }

  function getHelloworld() constant returns (string) {
    return message;
  }
}
```

# イーサリアムノードへの配置
では、イーサリアムノードに配置していきます。配置すると入っても、実際のイーサリアムノードではなく、TestRPCに配置します。TestRPCは、イーサリアムのエミュレーターでした。


## テスト用エミュレーターの起動
実際のメインネットでは試すことが出来ないので、testRPCを立ち上げて、プライベートノードのエミュレーター上でスマートコントラクトをテストします。

```
$ testrpc
```

-bash: testrpc: command not found
testrpc と打ち込んで、上記のようなメッセージが出た場合、testrpc インストールされていないか、パスが通っていない可能性があります。

```
$ sudo npm install -g ethereumjs-testrpc
Password:
/usr/local/Cellar/node/8.9.1/bin/testrpc -> /usr/local/Cellar/node/8.9.1/lib/node_modules/ethereumjs-testrpc/build/cli.node.js

> fsevents@1.1.3 install /usr/local/Cellar/node/8.9.1/lib/node_modules/ethereumjs-testrpc/node_modules/fsevents
> node install

[fsevents] Success: "/usr/local/Cellar/node/8.9.1/lib/node_modules/ethereumjs-testrpc/node_modules/fsevents/lib/binding/Release/node-v57-darwin-x64/fse.node" already installed
Pass --update-binary to reinstall or --build-from-source to recompile
+ ethereumjs-testrpc@6.0.1
```
例えば、上記のように打ち込むと、インストール先がわかります。私の環境では、`/usr/local/Cellar/node/8.9.1/bin/`に`testRPC`がインストールされていました。


● MacでPATHを通す際は、以下のエントリが参考になります。
[MacでPATHを通す](https://qiita.com/nbkn/items/01a11392921119fa0153)


自分は、`.bash_profile` にパスを追記したくなかったので、少し面倒ですが、毎回打ち込むことにします。

```
$ export PATH=$PATH:/usr/local/Cellar/node/8.9.1/bin/
$ testrpc
EthereumJS TestRPC v6.0.1 (ganache-core: 2.0.0)

Available Accounts
==================
(0) 0x691fd5c90f2b79102d0262310db33ecbd22df62b
(1) 0xc3433b538dbce2fe9a5c2d0672fc0d2377b6b5d3
(2) 0x23b7e1e4c3f378154bd494908ef42dd0d6dab3b7
(3) 0x011d8641cd48371ec13bd00e5bd5f7e540c2afa6
(4) 0xd808a917657d6d5285f215b3bdb9a5eb282c4370
(5) 0x67adac0c3d9fe6db78bb326eab1aabd4e6138d46
(6) 0x06ca468857e64672b76ba1d4538e3ebf75e3c0b1
(7) 0x017fd6ca50e6eb86ab727580909dc64767346d65
(8) 0xe2f2254fe5fbca79a16eab3ef57b1de0aa0b841e
(9) 0x2c890ad8fbdcb0b24dad404596a7d566db4f7845

Private Keys
==================
(0) 3883239aa6bc3ce77c62be94a5c2bd371c8fc3cddb38a882bc92c0e8c716ecc3
(1) 3baa7861144e63a2e6e11d7f329995e5cfa3f98d3f386ff59a825b13a2188e7d
(2) d58fb5b2722633a94aa72121a37a14f08dee9db9e4e5f4312fe901b68aa37a24
(3) f8ba7e37429eb3c9090c66441d123e3f569c4a77f8ce07f2707c208628a50bf7
(4) c3bcac430d9355fdf34890793dd090b8eed3532ce621b58f6ed06c57d45d31c2
(5) d93d6ae585a42efb5062e0bfdf2f0d619e3a258f7c1dee5ef0c86a5a781a355d
(6) b5f6a46abae1449814ba2ef817959af3aa19b1d2a5459b128e1d6e470aa0b243
(7) 7650a62f412395051c58246c653ef0161b1e913e799336e59fff90d873daf77c
(8) a4fbcfa06bd7926a085027ebab7a79796caa3744a0d0df82be3da40fb4002e7b
(9) 2eeb0e27d283e830fa907d6e4319828200107f257c4e83a4c07ae8ab5cec9792

HD Wallet
==================
Mnemonic:      summer glad second two coin mouse glad hero accuse manage wild have
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545
```

このtestrpc コマンドは、イーサリウムのエミュレート環境を提供します。また、新規に１０のアカウントを自動的に追記してくれます。
そしてそれぞれのアカウントに100ETH割り当ててくれます。また、イーサリアムのノードに wsで、localhost:8545 からアクセスすることが出来ます。

# nodejsを使ってのコントラクト配置の準備とコンパイル

て、これでノードがスタートし、ノードに対して読み書きができるようになりました。先程作った`helloworld`コントラクトを`nodejs`を使ってコンパイルし、配置していきます。まず別のコンソールを立上げ、プロジェクトフォルダに移動します。`node`コマンドを使って、`nodejs`コンソールに移動します。

```
$ node
>
```
オブジェクトをインポートします。

```
> Web3 = require('web3')
{ [Function: Web3]
  providers: 
   { HttpProvider: [Function: HttpProvider],
     IpcProvider: [Function: IpcProvider] } }
```

Web3オブジェクトのインスタンスを生成します。

```
> web3_instance = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
```

実際に作成されたかを確認してみましょう。

```
> web3_instance.eth.accounts
[ '0x691fd5c90f2b79102d0262310db33ecbd22df62b',
  '0xc3433b538dbce2fe9a5c2d0672fc0d2377b6b5d3',
  '0x23b7e1e4c3f378154bd494908ef42dd0d6dab3b7',
  '0x011d8641cd48371ec13bd00e5bd5f7e540c2afa6',
  '0xd808a917657d6d5285f215b3bdb9a5eb282c4370',
  '0x67adac0c3d9fe6db78bb326eab1aabd4e6138d46',
  '0x06ca468857e64672b76ba1d4538e3ebf75e3c0b1',
  '0x017fd6ca50e6eb86ab727580909dc64767346d65',
  '0xe2f2254fe5fbca79a16eab3ef57b1de0aa0b841e',
  '0x2c890ad8fbdcb0b24dad404596a7d566db4f7845' ]
```

## solidity のコンパイル
インスタンスが確認できたところで、solidityをコンパイルしていきます。

```
> solc = require('solc')
```

ソースコードを確認します。fs は、デフォルトで読み込まれているライブラリです。

```
sourceCode = fs.readFileSync('helloworld.sol').toString() 
```
コンパイルします。

```
> compiledCode = solc.compile(sourceCode)
```
このコマンドを実行すると、`bytecode` で示されるバイナリデータが含まれていますが、大きくは、`json`ファイルであり、一見するとエラーのようですが、コンパイルは正常に通っています。この`json`データを使って、`helloworld`コントラクトをデプロイしていきます。

まずは、`ABI`を取得します。`ABI`は、`helloword` コントラクトとやり取りするための`API`のようなものですが、コントラクトでやり取りするので、`API`とは分けて、`ABI`というのが一般的なようです。

```
> contractABI = JSON.parse(compiledCode.contracts[':helloworld'].interface)
```
これで、オブジェクトを操作するためのABIが取得できました。次にデプロイする為のバイトコードを取得します。

```
> byteCode = compiledCode.contracts[':helloworld'].bytecode
```
これで、実際に動作する対象であるバイトコードと、バイトコードを操作するためのABIを取得できました。

次にデプロイするための、インスタンスを取得します。

```
> helloworldContract = web3_instance.eth.contract(contractABI)
```

# Helloworldスマートコントラクトのデプロイ
前回のコマンドで、インスタンスが取得できたので、これを`new`でデプロイします。`from:`で記載しているのは、accounts[0]からデプロイし、`gas`のコストを支払うという意味のようです。（ここ少し曖昧です） これで、ブロックチェーン上のトランザクションにHelloworldコントラクトがデプロイされました。

```
> helloworldDeploy = helloworldContract.new({data: byteCode, from: web3_instance.eth.accounts[0], gas: 4700000})
```


testrpcを見てみると、このコマンドによる結果を見ることが出来ます。

```
HD Wallet
==================
Mnemonic:      summer glad second two coin mouse glad hero accuse manage wild have
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545
eth_accounts
eth_accounts
eth_sendTransaction

  Transaction: 0x280ba1f31bd1aaaa4efd0b55e89a663d6bdfadfd05fa5ed2472782cba282ef1b
  Contract created: 0xa3adb5859aa22874438aae3c4a3a1092f362d3fc
  Gas usage: 284092
  Block Number: 1
  Block Time: Sat Nov 11 2017 21:02:59 GMT+0900 (JST)

eth_newBlockFilter
eth_getFilterChanges
eth_getTransactionReceipt
eth_getCode
eth_uninstallFilter
```

Contract created を見るとアドレスが記載されています。デプロイされたアドレスを確認したい場合は、以下のコマンドを打ち込みます。

```
> helloworldDeploy.address
```

次のこのコントラクトのアドレスから、コントラクトのインスタンスを取得してみます。このようにアドレスがわかればインスタンスを取得できます。

```
> helloworld_Instance = helloworldContract.at(helloworldDeploy.address)
```

Helloworldコントラクトに記載した関数を使ってみます。

```
> helloworld_Instance.getHelloworld()
'Hello world!'
```


# トランザクションの発生
`setHelloworld()`関数を使い、メッセージを変えてみます。ここでトランザクションが起動することになります。スマートコントラクトの核心的であり革新的な部分でもあります。関数を呼び出すことで、トランザクションフィーを発生させることが出来るのです。accounts[0] は、トランザクションフィーを支払って、`setHelloworld`()関数を呼び出すこととになります。

```
> helloworld_Instance.setHelloworld("The world is changed", {from:web3_instance.eth.accounts[0]})
'0xb3b64dcb3aee87cc68d1c3de787e8934c3498a3f067e6ea719b423516ab29772'
```

トランザクションを特定するハッシュキーが発行されました。

testrpcを見てみると、トランザクションが追加されているのがわかります。

```
  Transaction: 0xb3b64dcb3aee87cc68d1c3de787e8934c3498a3f067e6ea719b423516ab29772
  Gas usage: 33352
  Block Number: 2
  Block Time: Sat Nov 11 2017 21:16:33 GMT+0900 (JST)
```

getHelloworld()関数を実行するとメッセージが変わっていることがわかります。

```
> helloworld_Instance.getHelloworld()
'The world is changed'
```