Skip to content

Instantly share code, notes, and snippets.

@lotz84
Created June 25, 2017 05:47
Show Gist options
  • Save lotz84/fa26a4e51debd4797d7e0cf6ec0bb6dc to your computer and use it in GitHub Desktop.
Save lotz84/fa26a4e51debd4797d7e0cf6ec0bb6dc to your computer and use it in GitHub Desktop.
Ethereum上でのICOを実装するまでの道筋

環境設定

geth

Ethereum ブロックチェーンに接続するためのミドルウェア

$ # geth のインストール
$ brew install ethereum
$ geth version
Geth
Version: 1.6.6-stable
...

Solidity

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} ...

開発用のPrivate Testnetの作成

参考: Ethereum の Private Testnet をローカルに構築して Ether を送金するところまで

$ # 開発用フォルダの作成
$ mkdir dapp-test
$ cd dapp-test

Genesis Block の作成

$ 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...

Private Testnetでマイニング

$ 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

Consoleの起動

$ 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

Etherを送金する

マイニングしている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

Hello Smart Contract!

$ 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!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment