Etherum Browser-Solidityを利用したコントラクト開発・デプロイ・実行


【本文】

Ethereumを使用するメリットの一つはSmart Contract(※以下、コントラクト)をブロックチェーン上で共有し様々な契約を自動実行する点にあります。

今回は、コントラクトを作成後、デプロイしブロックチェーン上に組み込む処理をBrowser-Solidityを使って実施する方法について概観します。
また、デプロイ後のコントラクトを、Browser-SolidityのRemix画面上に表示された情報を利用して、gethコンソール上で実行させる方法を説明します。
(※Browser-Solidityとgethを連携させたコントラクト開発についての説明がweb上で意外と見つからないので、敢えて投稿しました。)

【前提条件】

go-ethereum/gethがOS上にインストール済みであることを前提にしています。
(※Solidity自体はインストールしていなくても問題ありません。)
OSはUbuntu(※正確にはLubuntu(Ubuntuの軽量版)を使用しています。

【Browser-Solidityによるコントラクトの開発・デプロイ・実行】

1.GitHubよりBrowser-Solidityのアーカイブ(zip)をローカル上にダウンロード
  ( https://github.com/ethereum/browser-solidity/archive/gh-pages.zip

2.アーカイブファイルをローカル上の任意の場所で解凍。
  解凍後、「browser-solidity-gh-pages」と言う名前のディレクトリ内に
  index.htmlがあるので、任意のブラウザで開く。
  (※「Remix」という名前の画面が立ち上がる。)

3.Remix画面上の左上の「+」(New File)ボタンを押下し、新規ページを開く。

4.新規ページに適当なコントラクトコードを作成。
  (※以下はサンプル例。)

<test.sol>
(説明)
 set_numメソッドで適当な値(int)をブロックチェーン上にセットし、
 get_numメソッドでセットした値を取り出す。

pragma solidity ^0.4.0;
contract test {
    int a;
    function set_num(int num){
        a = num;
    }
    function get_num() returns(int){
        return a;
    }
}

5.gethを以下のオプションを使用して起動させる。
  (注:「rpc~」は、JSON-RPCサーバを起動させ、連携させる為に必要な設定。
     コントラクトのデプロイ時や実行時に、アンロック化が必要なので
     「--unlock」オプションを使用して、任意のアカウントをgeth起動時に解除しておくのが望ましい。)
  (※実際の引数は、各自の環境に合わせて設定してください。)

> geth --networkid 12345 --datadir ABC --nodiscover --rpc --rpccorsdomain "*" --rpcport 8545 --rpcaddr "localhost" --rpcapi "web3,eth,net,personal" --unlock <addr_account> console 2>> ABC/geth.log

6.geth起動後、Remix画面上の右側で「Environment」を選択。
  「Web3 Provider」で「Web3 Provider Endpoint:」にgeth起動時に指定した設定情報を入力。
  (※詳細は、図1参照。)

(例)

http://localhost:8545

・「localhost」:「--rpcaddr "localhost"」で指定した値(localhost)
・「8545」:「--rpcport 8545」で指定した値(8545)

図1

7.gethコンソール上で、マイニング開始。

> miner.start()

8.マイニングによって作成したコントラクトがブロックチェーン上に取り込まれデプロイされる。
  コントラクト固有のアドレスが割り振られ、Remix画面上に表示されるので、コピーしておく。
  (コントラクトアドレスの表示例は、「図2」参照。)

9.gethコンソール上で、マイニング停止。
  (※興味ある方は、コントラクトのデプロイ時に発行されたトランザクションidを基に、
    トランザクション情報を検索しみてください。)

> miner.stop()

<参考:コントラクトのデプロイ時に発行されたトランザクション情報>
※382個目のブロックにコントラクトが取り込まれている。(blockNumber: 382)

> eth.getTranction("0x29816e23fb48ae82be4ebe7f1546961c4f27e02f4ab8f5440e40003960685f79")
{
  blockHash: "0x7a29cfb1d3e37b5b888025678557da08b21424fed68a3b0c6ccacf533389da7f",
  blockNumber: 382,
  from: "0x08ade1b1470fa937908c0ff605d0028a2a25d2e5",
  gas: 2957117972,
  gasPrice: 20000000000,
  hash: "0x29816e23fb48ae82be4ebe7f1546961c4f27e02f4ab8f5440e40003960685f79",
  input: "0x6060604052341561000c57fe5b5b60c68061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633e27a8e8146044578063545a48d0146067575bfe5b3415604b57fe5b60516084565b6040518082815260200191505060405180910390f35b3415606e57fe5b60826004808035906020019091905050608f565b005b600060005490505b90565b806000819055505b505600a165627a7a72305820bd7eca5b9bbc049ce826fbe5aa22e94cbda268abf1675332bbf3849a465848610029",
  nonce: 8,
  to: null,
  transactionIndex: 0,
  value: 0
}

10.デプロイしたコントラクトを実行する際に、ABI情報が必要となるので、
   Remix画面上の「interface」欄よりコピーしておく。(※「図2」参照。)

図2

11.gethコンソール上で、Remix画面上よりコピーしたコントラクトのアドレスと
   ABI情報を使用して、コントラクトを実行する。
   (※Remix画面上で実行しても良いですが、今回の趣旨はデプロイしたコントラクトの
     呼び出し方法について理解する為なので、gethコンソール上で呼び出しします。)

<コントラクトアドレスを変数に代入>
※正しく代入されたか、変数呼び出し(addr_contract)で確認。

> var addr_contract = "0xbae1b49acfb28819a1782c8c286fbb4f2596161a"
undefined
> addr_contract
"0xbae1b49acfb28819a1782c8c286fbb4f2596161a"

<ABI情報を変数に代入>
※正しく代入されたか、変数呼び出し(abi)で確認。

> var abi = [{"constant":false,"inputs":[],"name":"get_num","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"num","type":"int256"}],"name":"set_num","outputs":[],"payable":false,"type":"function"}]
undefined
> abi
[{
    constant: false,
    inputs: [],
    name: "get_num",
    outputs: [{
        name: "",
        type: "int256"
    }],
    payable: false,
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "num",
        type: "int256"
    }],
    name: "set_num",
    outputs: [],
    payable: false,
    type: "function"
}]

<コントラクトオブジェクトを生成し、変数に代入>
※正しく代入されたか、変数呼び出し(test)で確認。
(※コントラクトアドレス(address)も正しくセットされていれば問題ない。)

> var test = eth.contract(abi).at(addr_contract)
undefined 
> test
{
  abi: [{
      constant: false,
      inputs: [],
      name: "get_num",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "set_num",
      outputs: [],
      payable: false,
      type: "function"
  }],
  address: "0xbae1b49acfb28819a1782c8c286fbb4f2596161a",
  transactionHash: null,
  allEvents: function(),
  get_num: function(),
  set_num: function()
}

12.デプロイしたコントラクトを実際に呼び出す。

<試しに、コントラクトで定義したget_numメソッドを呼び出し>
※set_numで値をセットしていないので、「0」が帰ってくる。
 コントラクト呼び出しとしては成功。

> test.get_num.call()
0

<set_numメソッドを利用して、値(int:5)をセット>
※トランザクションidが発行される。
 マイニングを実施していなければ、この段階ではまだブロックチェーンには登録されていない。
 (※「eth.pendingTransaction」の実行結果でトランザクションが登録済みかどうか確認可能。)

> test.set_num.sendTransaction(5,{from:eth.accounts[0]})
"0xfbb0b189f4451b056ae9c42975502992d4c0c27dc070484e274aec6cf615cab5"

※マイニングを実行し、ブロックチェーンに登録された後で、マイニングを停止。

<再度、get_numメソッドを呼び出し、セットした値の確認>
※set_numでセットした値(int:5)が返ってくる。

> test.get_num.call()
5

<トランザクションidを基に内容確認>
※今回コントラクト実行時に発行されたトランザクションidが391個目のブロックに取り込まれている。
(blockNumber:391)
 「input」欄にて、set_numメソッドでセットした値(int:5)が確認できる。

> eth.getTransaction("0xfbb0b189f4451b056ae9c42975502992d4c0c27dc070484e274aec6cf615cab5")
{
  blockHash: "0xe8ece94be57b4464be65421d0fea018a00a9ba276fc1744d188cf08f73211e49",
  blockNumber: 391,
  from: "0x08ade1b1470fa937908c0ff605d0028a2a25d2e5",
  gas: 90000,
  gasPrice: 20000000000,
  hash: "0xfbb0b189f4451b056ae9c42975502992d4c0c27dc070484e274aec6cf615cab5",
  input: "0x545a48d00000000000000000000000000000000000000000000000000000000000000005",
  nonce: 9,
  to: "0xbae1b49acfb28819a1782c8c286fbb4f2596161a",
  transactionIndex: 0,
  value: 0
}

以上。

【後記】

以上の通り、Browser-Solidityを利用したコントラクトの開発・デプロイ・実行までを概観しました。

Browser-Solidityを利用することで、簡単に作成したコントラクトをgeth上でデプロイ・実行することが可能になることが分かったかと思われます。
今回のやり方を覚えておくと、例えば、デプロイ時に発行されたコントラクトアドレスとABI情報を別ファイルに保存しておき、別のスクリプトから呼び出す等のやり方に応用可能です。
(※コントラクトアドレスやABI情報を敢えて変数に代入していたのはそうした応用方法を見据えている為です。)

Browser-Solidityは特別なセッティングが必要では無い為、コントラクトをサクッと作成し、デプロイ・実行するには適しています。

またコントラクトの作成自体は、Remix画面上の「Environment」で「JavaScript VM」を選択することで、JSON-RPCサーバを起動・接続していない状態でも、メモリ上で仮想的にデプロイ・実行が可能です。
「JavaScript VM」で作成したコントラクトのメソッドが想定通りに動作しているか検証したのち、JSON-RPCサーバを起動・接続させて、実際にブロックチェーン上にコントラクトをデプロイ・実行する開発方法を利用するとより開発効率が上がるのではないかと思われます。

【参考】

以前の投稿でgethコンソール上でのコントラクト作成・デプロイの方法をSolidityバージョンv0.4.9ベースで説明しています。
興味ある方は合わせてご参照ください。

・「Ethereum gethでContractを作成する場合のSolidity versionによる注意点」(Qiita投稿)