Ethereum ブロックチェーンに接続するためのミドルウェア
$ # geth のインストール
$ brew install ethereum
$ geth version
Geth
Version: 1.6.6-stable
...
EVM(Ethereum Virtual Machine)上で動作するプログラムを書くプログラミング言語。コントラクト指向型言語
$ # solidity のインストール
$ brew install solidity
$ solc --version
solc, the solidity compiler commandline interface
Version: 0.4.11+commit.68ef5810.Darwin.appleclang
後のテストのため二つ作成しておく
$ geth --keystore=./keystore/ account new
WARN [06-25|13:00:53] No etherbase set and no accounts found as default
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {1efa37b519fef989c2f22594e39c29a327528a27}
$ geth --keystore=./keystore/ account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {10468037b22793bd5eeb98bf9a07f9d2a18788e7}
作成したアカウントは以下のコマンドで確認可能
$ geth --keystore=./keystore/ account list
Account #0: {1efa37b519fef989c2f22594e39c29a327528a27} ...
Account #1: {10468037b22793bd5eeb98bf9a07f9d2a18788e7} ...
参考: Ethereum の Private Testnet をローカルに構築して Ether を送金するところまで
$ # 開発用フォルダの作成
$ mkdir dapp-test
$ cd dapp-test
$ vim genesis.json
{
"nonce" : "0x0000000000000000",
"alloc" : {},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x20000",
"extraData" : "",
"gasLimit" : "0x2fefd8",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00",
"config": {
"chainId": 0,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
}
}
注意: nonce
の値は他のネットワークからの誤接続を防ぐためランダムな値に変更しておくこと
参考: Operating a private network
$ geth --datadir=./ init ./genesis.json
WARN [06-25|12:56:34] No etherbase set and no accounts found as default
INFO [06-25|12:56:34] Allocated cache and file handles...
INFO [06-25|12:56:34] Writing custom genesis block
INFO [06-25|12:56:34] Successfully wrote genesis state...
INFO [06-25|12:56:34] Allocated cache and file handles...
INFO [06-25|12:56:34] Writing custom genesis block
INFO [06-25|12:56:34] Successfully wrote genesis state...
$ geth --datadir=./ --dev --mine --minerthreads=1 --nodiscover\
--etherbase=0x1efa37b519fef989c2f22594e39c29a327528a27
ここで
Fatal: Error starting protocol stack: database already contains an incompatible genesis block (have 33bbd028dcb05e17, new e5be92145a301820)
のようなエラーが出る場合はgeth
配下のchaindata
を削除する
$ rm geth/chaindata
$ geth attach geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/v1.6.6-stable-10a45cb5/darwin-amd64/go1.8.3
coinbase: 0x1efa37b519fef989c2f22594e39c29a327528a27
at block: 32 (Sun, 25 Jun 2017 13:11:01 JST)
datadir: /<path>/dapp-test2
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
このコンソールではJavaScriptが使用できる。
// coinbaseアドレスの取得
> eth.coinbase
"0x1efa37b519fef989c2f22594e39c29a327528a27"
// coinbaseアドレスの残高の取得
> eth.getBalance(eth.coinbase)
805000000000000000000
マイニングしているcoinbaseアドレスのアカウントから何もしていないアカウントにEtherを送金してみる
// アカウント一覧の表示
> eth.accounts
["0x1efa37b519fef989c2f22594e39c29a327528a27", "0x10468037b22793bd5eeb98bf9a07f9d2a18788e7"]
// マイニングしているcoinbaseアドレス
> eth.coinbase
"0x1efa37b519fef989c2f22594e39c29a327528a27"
// 何もしていないアカウント
> eth.accounts[1]
"0x10468037b22793bd5eeb98bf9a07f9d2a18788e7"
// 何もしていないアカウントの残高
> eth.getBalance(eth.accounts[1])
0
// 送金する前に送金元アドレスをアンロック
> personal.unlockAccount(eth.coinbase)
Unlock account 0x1efa37b519fef989c2f22594e39c29a327528a27
Passphrase:
true
// 送金!
// トランザクションのIDが出力される
> eth.sendTransaction({from: eth.coinbase, to: eth.accounts[1], value: web3.toWei(1, "ether")})
"0x170d6a30e202ba55099e3f47cb4b68ef1b26c550b1fe81abd57db4365489963d"
// トランザクションの確認
> eth.getTransaction("0x170d6a30e202ba55099e3f47cb4b68ef1b26c550b1fe81abd57db4365489963d")
{
blockHash: "0xe174ea2d94a5237e54edcae587b0321357e07665f5f8a42b1141e825d5ce82e0",
blockNumber: 443,
from: "0x1efa37b519fef989c2f22594e39c29a327528a27",
gas: 90000,
gasPrice: 0,
hash: "0x170d6a30e202ba55099e3f47cb4b68ef1b26c550b1fe81abd57db4365489963d",
input: "0x",
nonce: 0,
r: "0xdc7ffe96cd1cf8cbfad9f2f02f6baeeb6a2f2bf11d9240dd7137dd00aae0e02d",
s: "0x39b516e6789fb4f70296cbf6cfb901063d679c8af3055cd703966f1e8105d82f",
to: "0x10468037b22793bd5eeb98bf9a07f9d2a18788e7",
transactionIndex: 0,
v: "0xa96",
value: 1000000000000000000
}
// 何もしていないアカウントの残高が増えていることを確認
> eth.getBalance(eth.accounts[1])
1000000000000000000
$ vim greeter.sol
pragma solidity ^0.4.11;
contract mortal {
/* Define variable owner of the type address*/
address owner;
/* this function is executed at initialization and sets the owner of the contract */
function mortal() { owner = msg.sender; }
/* Function to recover the funds on the contract */
function kill() { if (msg.sender == owner) suicide(owner); }
}
contract greeter is mortal {
/* define variable greeting of the type string */
string greeting;
/* this runs when the contract is executed */
function greeter(string _greeting) public {
greeting = _greeting;
}
/* main function */
function greet() constant returns (string) {
return greeting;
}
}
loadScript
を利用してgethに読み込めるようにコンパイルした後JavaScriptとして出力する
$ echo "var greeterCompiled=`solc --optimize --combined-json abi,bin,interface greeter.sol`" > greeter.js
参考: How to compile Solidity contracts with Geth v1.6?
// コンパイルしたコードの読み込み
> loadScript("greeter.js")
// ABIはJSONの中に埋め込まれている
> greeterCompiled.contracts["greeter.sol:greeter"].abi
"[{\"constant\":false,\"inputs\":[],\"name\":\"kill\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"greet\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_greeting\",\"type\":\"string\"}],\"payable\":false,\"type\":\"constructor\"}]"
// 文字列だと使えないのでパースする
> var abiDefinitions = JSON.parse(greeterCompiled.contracts["greeter.sol:greeter"]["abi"])
// 後で使うコンパイル済みのコードは先頭に0xをつける必要がある
> var greeterCompiledCode = "0x" + greeterCompiled.contracts["greeter.sol:greeter"].bin
// コントラクトを操作するオブジェクトの作成
> var greeterContract = web3.eth.contract(abiDefinitions)
// コントラクトを送信するアカウントのアンロック
> personal.unlockAccount(eth.coinbase)
Unlock account 0x1efa37b519fef989c2f22594e39c29a327528a27
Passphrase:
true
// コントラクトを初期化する変数の設定
> var _greeting = "Hello Smart Contract!"
// コントラクトを送信!
> var greeter = greeterContract.new(_greeting,{from:eth.coinbase, data: greeterCompiledCode, gas: 1000000}, function(e, contract){
if(!e) {
if(!contract.address) {
console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
} else {
console.log("Contract mined! Address: " + contract.address);
}
} else {
console.error(e);
}
})
Contract transaction send: TransactionHash: 0x2172b8be888220a41654a08c4e3b5d8e4876df6b73682ddad8d965e99b038384 waiting to be mined...
undefined
Contract mined! Address: 0xa55452aa78dc9134a30b905c5cca30ff8215236b
// コントラクトのアドレスの確認
> greeter.address
"0xa55452aa78dc9134a30b905c5cca30ff8215236b"
// コントラクトの実行
> greeter.greet()
"Hello Smart Contract!"
コントラクトを他の人に使ってもらうにはABIとアドレスの二つを共有する必要があります。
// ABIとアドレスから別のクライアントを作成
> var anotherClient = eth.contract(abiDefinitions).at(greeter.address)
> anotherClient.greet()
"Hello Smart Contract!"