try catch and ...release
ページ
ホーム
Chromeアプリ開発Tips
公開アプリ
Ubuntu
Linuxコマンド
#!/bin/bash
ブロックチェーンと暗号通貨
2016年12月17日土曜日
Ethereumで独自コインを発行し、送金用Dappも作ってみる
[前回は簡単なコントラクトを作ってDappから使ってみました](https://trycatchand.blogspot.jp/2016/12/ethereum-dapp-helloworld.html)が、今回はもう少しだけ面白みのあるコントラクトを作ってみたいと思います。 今回作ってみるコントラクトは、「コイン」コントラクトです。Ethereumを使うことで、誰もが独自コイン(トークン)を簡単に発行できます。シンプルな「コイン」コントラクトを作ってそれを実証してみましょう。
## 目次 (index) + [コイン・コントラクトのコード](#coin-contract) + [コイン・コントラクトのコンパイルとデプロイ](#compile-deploy-contract) + [コインの送信を監視](#watch-contract) + [コイン・コントラクトを使った送金Dapp](#coin-contract-dapp) + [次回](#next) + [リンク](#links) [⤒](#index) ## コイン・コントラクトのコード (coin-contract) 以下はEthereum上で「コイン」(トークン)を発行するためのコントラクトです。 token.sol: ``` contract token { mapping (address => uint) public coinBalanceOf; event CoinTransfer(address sender, address receiver, uint amount); /* Initializes contract with initial supply tokens to the creator of the contract */ function token(uint supply) { coinBalanceOf[msg.sender] = supply; } /* Very simple trade function */ function sendCoin(address receiver, uint amount) returns(bool sufficient) { if (coinBalanceOf[msg.sender] < amount) return false; coinBalanceOf[msg.sender] -= amount; coinBalanceOf[receiver] += amount; CoinTransfer(msg.sender, receiver, amount); return true; } } ``` 上記のコントラクトを簡単に説明すると、 - `mapping (address => uint) public coinBalanceOf;`: このコントラクトは全保有者の残高を保持しています。adressをキーに残高を取得することができます。 - `token(uint supply){...}`: コントラクトの作者は、最初にこのコンストラクタを呼び出し、初期数量(コインの総量)を設定します。最初は作者自身のアドレス(`msg.sender`)が全ての残高を保有することになります。 - `event CoinTransfer(address sender, address receiver, uint amount);`: コインの送信を通知するためのイベントを定義しています。このイベントが発火された時には、引数に「送金者」、「受取人」、「数量」が入ってきます。 - `function sendCoin(address receiver, uint amount){...`: コインを送金する関数です。第一引数に「受取人」、第二引数に「数量」を指定します。 - `CoinTransfer(msg.sender, receiver, amount);`: コイン送金関数の中でコイン送信イベントを発火しています。これにより、送金が行われたことを全体に通知しています。 [⤒](#index) ## コイン・コントラクトのコンパイルとデプロイ (compile-deploy-contract) #### まずは Geth を起動して: (run-geth) ``` $ cd ~/.ethereum_private_testnet $ geth --rpc --rpcapi "eth,net,web3,personal" --datadir=./ --dev --mine --minerthreads=1 --nodiscover --etherbase=0x4a1c11eaec40197beb6e8607ee61953e7d6d8731 ``` #### 次に別ターミナルから Geth Console を起動し、 (run-geth-console) ``` $ cd ~/.ethereum_private_testnet/ $ geth attach ./geth.ipc ``` #### コントラクトをコンパイル: ``` # コントラクトのソースの改行を取り除く: > var tokenSource = ' contract token { mapping (address => uint) public coinBalanceOf; event CoinTransfer(address sender, address receiver, uint amount); /* Initializes contract with initial supply tokens to the creator of the contract */ function token(uint supply) { coinBalanceOf[msg.sender] = supply; } /* Very simple trade function */ function sendCoin(address receiver, uint amount) returns(bool sufficient) { if (coinBalanceOf[msg.sender] < amount) return false; coinBalanceOf[msg.sender] -= amount; coinBalanceOf[receiver] += amount; CoinTransfer(msg.sender, receiver, amount); return true; } }' # コンパイル: > var tokenCompiled = eth.compile.solidity(tokenSource) ``` #### コントラクトをデプロイ: (deploy-contract) ``` # コインの初期残高を10000に設定: > var supply = 10000; # コイン・コントラクトの型定義を作成: > var tokenContract = web3.eth.contract(tokenCompiled.token.info.abiDefinition); # コイン・コントラクトの作成者(account[0])のアドレスをアンロック: > web3.personal.unlockAccount(eth.accounts[0], "<パスフレーズ>", 600); # コイン・コントラクトのインスタンスを生成&デプロイ(作成アカウントは account[0] を使用): > var token = tokenContract.new( supply, { from:web3.eth.accounts[0], data:tokenCompiled.token.code, 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); console.log(contract); } } }) Contract transaction send: TransactionHash: 0xb477861930f1d96ca5434d2023c9de89b4b6168552522185690a1d5edf47d0be waiting to be mined... ``` だいたい30秒以内に採掘が完了し、コントラクトがブロックチェーンにデプロイされます。以下のようなメッセージが出力されたらデプロイ完了です: ``` > Contract mined! Address: 0x8d32bb5685556744a3d0df4536a422bcc73dc6b6 ``` デプロイが完了すると、以下のコマンドで初期保有者(コントラクトの作成者)の初期残高を確認できます: ``` > token.coinBalanceOf(eth.accounts[0]) 10000 ``` [⤒](#index) ## コインの送信を監視 (watch-contract) コントラクト内で定義された`CoinTransfer(...)`イベントを実装することで、誰かによるコインの送信を監視することができるようになります: ``` > var event = token.CoinTransfer({}, '', function(error, result){ if (!error) console.log("Coin transfer: " + result.args.amount + " tokens were sent. Balances now are as following: \n Sender:\t" + result.args.sender + " \t" + token.coinBalanceOf.call(result.args.sender) + " tokens \n Receiver:\t" + result.args.receiver + " \t" + token.coinBalanceOf.call(result.args.receiver) + " tokens") }); ``` 試しに、コントラクト作成者(*account[0]*)から他のアドレス(*account[1]*)にコインを送金してみましょう: ``` # 送金者(account[0])のアドレスをアンロック: > web3.personal.unlockAccount(eth.accounts[0], "<パスフレーズ>", 600); # コイン送金: > token.sendCoin.sendTransaction(eth.accounts[1], 1000, {from: eth.accounts[0]}) ``` 送金イベントが発火し、以下のように通知されます: ``` "0x5dd2dc64a18048016d850555f371b5fd9f83f7c5cde373b0a0efe90d86a81834" Coin transfer: 1000 tokens were sent. Balances now are as following: Sender: 0x4a1c11eaec40197beb6e8607ee61953e7d6d8731 9000 tokens Receiver: 0x5855a19b64e1068a1edc39bb817647e16a1c57e7 1000 tokens ``` 上記の通り、発行者(*accounts[0]*)と受取人(*accounts[1]*)のコイン残高は以下のようにして確認できます: ``` > token.coinBalanceOf.call(eth.accounts[0]) 9000 > token.coinBalanceOf.call(eth.accounts[1]) 1000 ``` ちゃんと発行者から受取人に1000単位移転されていますね。 [⤒](#index) ## コイン・コントラクトを使った送金Dapp (coin-contract-dapp) 先ほど作ったコイン・コントラクトをDapp化してみましょう。Dapp化といっても、コイン・コントラクトをアプリのバックエンドに使うだけのことです。 以下のコードは先ほどのコイン・コントラクトを利用したコイン送金Dappです。コインの送金元とそのパスフレーズ、送金先、そして数量を指定してSend Coinボタンを押すと、Ethereumネットワークを通じて送金されます。Webサーバを立ち上げる必要はなく、ブラウザだけで動きます: coin.html: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Coin Dapp</title> <!-- jQuery --> <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha256-/SIrNqv8h6QGKDuNoLGA4iret+kyesCkHGzVUUV0shc=" crossorigin="anonymous"></script> <!-- Latest compiled and minified CSS --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <!-- Latest compiled and minified JavaScript --> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> <!-- Web3.js --> <script type="text/javascript" src="web3.min.js"></script> </head> <body class="container"> <div> <h2>Send Your Coin</h2> </div> <div class="form-group"> Test Accounts: <br> "0x4a1c11eaec40197beb6e8607ee61953e7d6d8731", // address[0] (Coin Issuer) <br> "0x5855a19b64e1068a1edc39bb817647e16a1c57e7", // address[1] <br> "0x585542d64e07e856c3233042bc2d20a6711bbd75" // address[2] <br> </div> <div class="form-group"> <label for="ipt-from">Send From (Your Address):</label> <input type="text" name="ipt-from" id="ipt-from" class="form-control" placeholder="0x............." value="0x4a1c11eaec40197beb6e8607ee61953e7d6d8731"> <label for="ipt-pass">Passphrase of Your Address:</label> <input type="password" name="ipt-pass" id="ipt-pass" class="form-control" placeholder="Input your secret"> <label for="ipt-to">Send To (Recipient Address):</label> <input type="text" name="ipt-to" id="ipt-to" class="form-control" placeholder="0x............." value="0x5855a19b64e1068a1edc39bb817647e16a1c57e7"> <label for="ipt-amt">Amount:</label> <input type="number" name="ipt-amt" id="ipt-amt" class="form-control" placeholder="100" value="100"> </div> <div class="form-group"> <button id="btn-send" class="btn btn-primary form-control">Send Coin</button> </div> <div class="form-group panel"> <label for="pane-logs">Logs:</label> <pre id="pane-logs"></pre> </div> <script type="text/javascript"> // コイン・コントラクトのアドレス: var contractAddress = "0x8d32bb5685556744a3d0df4536a422bcc73dc6b6"; // ABI: // Geth Console上で `> greeterCompiled.greeter.info.abiDefinition` で得られます var abi = [{ constant: false, inputs: [{ name: "receiver", type: "address" }, { name: "amount", type: "uint256" }], name: "sendCoin", outputs: [{ name: "sufficient", type: "bool" }], payable: false, type: "function" }, { constant: true, inputs: [{ name: "", type: "address" }], name: "coinBalanceOf", outputs: [{ name: "", type: "uint256" }], payable: false, type: "function" }, { inputs: [{ name: "supply", type: "uint256" }], payable: false, type: "constructor" }, { anonymous: false, inputs: [{ indexed: false, name: "sender", type: "address" }, { indexed: false, name: "receiver", type: "address" }, { indexed: false, name: "amount", type: "uint256" }], name: "CoinTransfer", type: "event" }]; var paneLogs = document.querySelector("#pane-logs"); var btnSend = document.querySelector("#btn-send"); // Web3インスタンスの生成とセットアップ: var web3 = new Web3(); web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545')); // コントラクトのインスタンスを生成: let contract = web3.eth.contract(abi).at(contractAddress); // コントラクトのコイン送信イベントを監視 var event = contract.CoinTransfer({}, '', function(error, result) { console.log(`CoinTransfer Event detected. error:${error} result:${result}`); let message; if (!error) { message = `Coin transfer: ${result.args.amount} coins were sent. Coin balances now are as following: Sender: ${result.args.sender} / ${contract.coinBalanceOf.call(result.args.sender)} Receiver: ${result.args.receiver} / ${contract.coinBalanceOf.call(result.args.receiver)} `; } else { message = "Error: " + error.toString(); } // 結果ペインに表示 paneLogs.innerHTML = message; }); // "Send Coin" button click btnSend.onclick = function(e) { let from = document.querySelector("#ipt-from").value; let pass = document.querySelector("#ipt-pass").value; let to = document.querySelector("#ipt-to").value; let amt = document.querySelector("#ipt-amt").value; // Unlock address: paneLogs.innerHTML += `Unlocking sender's address "${from}" ...<br>`; web3.personal.unlockAccount(from, pass, 600); // Send transaction: paneLogs.innerHTML += `Sending ${amt} coins to "${to}" ...<br>`; contract.sendCoin.sendTransaction(to, amt, { from: from }); paneLogs.innerHTML += `Please wait for within 30 seconds for this transaction mining finished...<br>`; } </script> </body> </html> ``` **Note:** HTML内で読み込んでいる`web3.min.js`については[Dappを作成](https://trycatchand.blogspot.jp/2016/12/ethereum-dapp-helloworld.html#create-dapp)を参考に。 **Note:** 一度ブロックチェーンに登録されたコントラクトは、ABIとコントラクトのアドレスさえあれば、`let contract = web3.eth.contract(abi).at(contractAddress);`のようにして何度も呼び出すことができます。 **Note:** 送金トランザクションが採掘されてブロックチェーンへ登録されるまでに約30秒ほど掛かります。送金が完了すると、CoinTransferイベントが通知され、送金元と送金先、そして両者のコイン残高がログに表示されます。 **Dappの実行** 上記のDappを実行する前に、Web3をブラウザから使えるようにするオプション `--rpc` `--rpccorsdomain "*"` を付けて Geth を起動します: ``` $ cd ~/.ethereum_private_testnet $ geth --rpc --rpcapi "eth,net,web3,personal" --rpccorsdomain "*" --datadir=./ --dev --mine --minerthreads=1 --nodiscover --etherbase=0x4a1c11eaec40197beb6e8607ee61953e7d6d8731 ``` Gethが起動したらブラウザで上記の coin.html を開いてみましょう。このDappでは、送金元アドレスとパスフレーズ、送金先アドレス、数量を指定してコインを送金することができます。  どうしても動かない場合は、Gethが正常に起動しているか(特に`HTTP-RPC`が有効になっているか)や、web3インスタンスが正しく生成されているか、**コントラクトのアドレス**や**ABI**が正しいか、パスフレーズやアドレス情報などが正しいか、などを確認してみましょう。 [⤒](#index) ## 次回 (next) 次回はコントラクトをさらに発展させて、[クラウドセール・コントラクト](https://trycatchand.blogspot.jp/2016/12/crowdsale-contract-ethereum.html)を作ってみたいと思います。 [⤒](#index) ## リンク (links) - [The Coin](https://github.com/ethereum/go-ethereum/wiki/Contract-Tutorial#the-coin) [⤒](#index)
0 件のコメント:
コメントを投稿
次の投稿
前の投稿
ホーム
登録:
コメントの投稿 (Atom)
0 件のコメント:
コメントを投稿