50行のコードでの支払いシステムですか?

最近、ブロックチェーン上の技術的ソリューションが私たちの日常生活にますます浸透しています。 このテクノロジーは新しいため、誰もがそれをどのように、どこで適用するかを理解しているわけではありません。 イーサリアムのスマート契約に基づいて支払いシステムを作成しようとしましたが、その結果に驚きました。 本格的な支払いシステムの機能を実行するスマートコントラクトは、わずか50行のコードです。 Catの下でどのように機能するかに興味を持っているすべての人に尋ねます。



画像



Habré( onetwo )にはすでに優れた出版物があり、スマートコントラクトがどのように作成され、ブロックチェーンに注がれるかを詳細に検討したため、すぐにコードに戻ります。



すべてのアクションは、Rinkebyテストネットワークで実行されます。



スマートコントラクト



支払いシステムの中核はスマートな契約です。それから始めます。



契約機能:



  1. ユーザーからの支払いを受け取る
  2. 管理者の撤回
  3. 管理者の払い戻し
  4. ユーザー権限の制御
  5. 管理者の変更
  6. 支払リストの保管
  7. 請求書の返済をブロックする
  8. 払い戻しのブロック
  9. 契約アドレスに自動払い戻し
  10. 支払い通知の作成、返金、管理者の変更


コメント付きのスマート契約コード
pragma solidity ^0.4.18; //version:4 /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ function Ownable() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) onlyOwner public { require(newOwner != address(0)); OwnershipTransferred(owner, newOwner); owner = newOwner; } } contract PaymentSystem is Ownable { struct order { address payer; uint256 value; bool revert; } //  mapping(uint256 => order) public orders; //        function () public payable { revert(); } event PaymentOrder(uint256 indexed id, address payer, uint256 value); //  function paymentOrder(uint256 _id) public payable returns(bool) { require(orders[_id].value==0 && msg.value>0); orders[_id].payer=msg.sender; orders[_id].value=msg.value; orders[_id].revert=false; //  PaymentOrder(_id, msg.sender, msg.value); return true; } event RevertOrder(uint256 indexed id, address payer, uint256 value); //   function revertOrder(uint256 _id) public onlyOwner returns(bool) { require(orders[_id].value>0 && orders[_id].revert==false); orders[_id].revert=true; orders[_id].payer.transfer(orders[_id].value); RevertOrder(_id, orders[_id].payer, orders[_id].value); return true; } //   function outputMoney(address _from, uint256 _value) public onlyOwner returns(bool) { require(this.balance>=_value); _from.transfer(_value); return true; } }
      
      







ソースコードはrinkeby.etherscan.ioで検証され、「Contract Source」タブに表示されるように、わずか50行しかかかりません。



ユーザーインターフェース



もちろん、ユーザーにMyetherwalletまたはMistを介して支払いを行うように依頼することもできますが、これは不便なので、サイトで支払いフォームを作成する方が良いでしょう。 支払いフォームを機能させるには、ユーザーがMetamaskをインストールする必要があります。 メタマスクは、ユーザーをRPCサーバーに自動的に接続します。



支払フォームコード
  <body> <div class="main_section"> <h3 class="section_title">Pay</h3> <div class="edit"><input type="text" class="myedit" id="edit_id" placeholder="id"></div> <div class="edit"><input type="text" class="myedit" id="edit_value" placeholder="value (ETH)"></div> <div id="button_pay" class="mybutton">pay</div> <div id="message_pay" class="message"></div> <script> // //----------------------------------------------------------------- var button_pay = document.querySelector('#button_pay'); button_pay.addEventListener('click', function() { var pay_id = document.getElementById("edit_id").value; var pay_value = web3.toWei(parseFloat(document.getElementById("edit_value").value), 'ether') var user_adress = web3.eth.accounts[0]; if (!web3.isAddress(user_adress)) { write_wessage("#message_pay", "error: MetaMask not open"); return; } if (pay_id.length==0) { write_wessage("#message_pay", "error: not id"); return; } if (pay_value==0) { write_wessage("#message_pay", "error: volume 0"); return; } contract.paymentOrder( pay_id, {from: user_adress, value: pay_value, gasPrice: 41000000000}, function (err, transaction_hash) { if (err) { write_wessage("#message_pay", "error"); console.log(err); } else { write_wessage("#message_pay", "transaction hash: "+transaction_hash); } }); }); </script> </div> </body> <script> function write_wessage(element, message) { document.querySelector(element).innerHTML = message; } if (typeof web3 === 'undefined') { document.getElementsByTagName("body")[0].innerHTML = 'You need to install MetaMask'; } else { //  var contract_adress='0x3b4a22858093B9942514eE42eD1B4BF177632ba3'; var abi=[ { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "orders", "outputs": [ { "name": "payer", "type": "address" }, { "name": "value", "type": "uint256" }, { "name": "revert", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "previousOwner", "type": "address" }, { "indexed": true, "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "id", "type": "uint256" }, { "indexed": false, "name": "payer", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "PaymentOrder", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "id", "type": "uint256" }, { "indexed": false, "name": "payer", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "RevertOrder", "type": "event" }, { "constant": false, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "outputMoney", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_id", "type": "uint256" } ], "name": "paymentOrder", "outputs": [ { "name": "", "type": "bool" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_id", "type": "uint256" } ], "name": "revertOrder", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "newOwner", "type": "address" } ], "name": "transferOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "payable": true, "stateMutability": "payable", "type": "fallback" } ]; var contract = web3.eth.contract(abi).at(contract_adress); } </script>
      
      







画像

また、管理者がスマートコントラクトと対話するためのページを作成します。



管理ページのコード
  <body> <div class="main_section"> <h3 class="section_title">Info</h3> <div id="message_balance" class="message"></div> <div id="message_owner" class="message"></div> </div> <div class="main_section"> <h3 class="section_title">Output money</h3> <div class="edit"><input type="text" class="myedit" id="edit_adress" placeholder="adress"></div> <div class="edit"><input type="text" class="myedit" id="edit_value" placeholder="value (ETH)"></div> <div id="button_output" class="mybutton">output</div> <div id="message_output" class="message"></div> <script> //  //----------------------------------------------------------------- var button_output = document.querySelector('#button_output'); button_output.addEventListener('click', function() { var pay_value = web3.toWei(parseFloat(document.getElementById("edit_value").value), 'ether') var to_adress = document.getElementById("edit_adress").value; var user_adress = web3.eth.accounts[0]; if (!web3.isAddress(user_adress)) { write_wessage("#message_output", "error: MetaMask not open"); return; } if (!web3.isAddress(to_adress)) { write_wessage("#message_output", "error: adress not valid"); return; } if (pay_value==0) { write_wessage("#message_output", "error: volume 0"); return; } contract.outputMoney( to_adress, pay_value, {from: user_adress, gasPrice: 41000000000}, function (err, transaction_hash) { if (err) { write_wessage("#message_output", "error"); console.log(err); } else { write_wessage("#message_output", "transaction hash: "+transaction_hash); } }); }); </script> </div> <div class="main_section"> <h3 class="section_title">Revert order</h3> <div class="edit"><input type="text" class="myedit" id="edit_id" placeholder="id"></div> <div id="button_revert" class="mybutton">revert</div> <div id="message_revert" class="message"></div> <script> //  //----------------------------------------------------------------- var button_revert = document.querySelector('#button_revert'); button_revert.addEventListener('click', function() { var pay_id = document.getElementById("edit_id").value; var user_adress = web3.eth.accounts[0]; if (!web3.isAddress(user_adress)) { write_wessage("#message_revert", "error: MetaMask not open"); return; } if (pay_id.length==0) { write_wessage("#message_revert", "error: not id"); return; } contract.revertOrder( pay_id, {from: user_adress, gasPrice: 41000000000}, function (err, transaction_hash) { if (err) { write_wessage("#message_revert", "error"); console.log(err); } else { write_wessage("#message_revert", "transaction hash: "+transaction_hash); } }); }); </script> </div> </body> <script> function write_wessage(element, message) { document.querySelector(element).innerHTML = message; } if (typeof web3 === 'undefined') { document.getElementsByTagName("body")[0].innerHTML = 'You need to install MetaMask'; } else { //  var contract_adress='0x3b4a22858093B9942514eE42eD1B4BF177632ba3'; var abi=[ { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "orders", "outputs": [ { "name": "payer", "type": "address" }, { "name": "value", "type": "uint256" }, { "name": "revert", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "previousOwner", "type": "address" }, { "indexed": true, "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "id", "type": "uint256" }, { "indexed": false, "name": "payer", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "PaymentOrder", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "id", "type": "uint256" }, { "indexed": false, "name": "payer", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "RevertOrder", "type": "event" }, { "constant": false, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "outputMoney", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_id", "type": "uint256" } ], "name": "paymentOrder", "outputs": [ { "name": "", "type": "bool" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_id", "type": "uint256" } ], "name": "revertOrder", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "newOwner", "type": "address" } ], "name": "transferOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "payable": true, "stateMutability": "payable", "type": "fallback" } ]; var contract = web3.eth.contract(abi).at(contract_adress); //  web3.eth.getBalance(contract_adress.toString(), function (err, result) { write_wessage("#message_balance", "contract balance: "+web3.fromWei(result, 'ether')+" ETH"); }); // owner contract.owner(function(err, data) { if (err) { write_wessage("#message_owner", "error"); } else { write_wessage("#message_owner", "owner: "+data); } }); } </script>
      
      







画像



通知



自動注文処理の場合、ほとんどの支払いシステムには、支払いのステータスを追跡したり、受け取った支払いを通知するためのAPIが用意されています。 ブロックチェーンは支払いの通知を送信しませんが、ブロックを読み取り、契約によって作成されたイベントのリストを取得できます。



ブロックにアクセスするには、RPC-HTTPサーバーをオンにしてGethを使用します。



 geth --rinkeby --datadir "D:/eth/blockchain_rinkeby" --rpc --rpcaddr "0.0.0.0" --rpcapi "admin,debug,miner,shh,txpool,personal,eth,net,web3" console
      
      





Gethへの接続をphpで実装しましたが、POSTリクエストを実行できるプラットフォームであれば実行できます。



開発をスピードアップするために、 ethereum-phpを使用しました



イベントのリストを取得するコード
 <?php require 'ethereum-php-master/ethereum.php'; $rate=0.000000000000000001; //   $ethereum = new Ethereum('192.168.56.1', 8545); //   $filter = new Ethereum_Filter('0x0', 'latest', '0x3b4a22858093B9942514eE42eD1B4BF177632ba3', []); //    $result_filter=$ethereum->eth_newFilter($filter); //  events $logs=$ethereum->eth_getFilterLogs($result_filter); foreach ($logs as $key => $value) { /*     topics,           : PaymentOrder(uint256,address,uint256)  : Keccak-256 (      )    topics     */ if (strcasecmp($value->{'topics'}[0], "0x"."c84883193d3a69d991d82f61928c06e179b647e413da4c20be80d8c0314c2e1b") == 0) { echo "Payment order id:".hexdec($value->{'topics'}[1]); /*   data       32  */ $data=str_split(substr($value->{'data'}, 2),64); echo " volume:".hexdec($data[1])*$rate." ETH"; echo "<br>"; } } ?>
      
      







画像



私のスクリプトでは、ゼロから最後のブロックまでeth_getFilterLogsメソッドを使用しました。これは当然、最速で最も効果的なオプションではありません。 ブロック数でeth_getFilterLogsを制限するか、新しいブロックからのみイベントを返すeth_getFilterChangesメソッドを使用することをお勧めします。



JSON-RPCメソッドの詳細な説明は、 ドキュメントに記載されています



おわりに



したがって、独立した支払いシステムを取得し、それを変更してサイトに接続するのは非常に簡単です。 この例で、多くの人にとって、ブロックチェーンの使用方法と場所がより明確になると思います。



rinkeby.etherscan.ioでのスマート契約

GitHubリポジトリ



All Articles