9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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

環境構築がまだの場合は、
Ethereum入門(1) - スマートコントラクトの開発環境を構築する から、環境構築を済ませてください。

スマートコントラクトを実施するための、ディレクトリの作成から入ります。この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コマンドの使い方

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に、web3solcが追加されているのが確認出来ます。このように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を通す

自分は、.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 コマンドは、イーサリウムのエミュレート環境を提供します。また、新規に10のアカウントを自動的に追記してくれます。
そしてそれぞれのアカウントに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'
9
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?