NodejsのWebアプリケーションにスマートコントラクトを統合します

スマートコントラクトを使用して製品を開発するトピックに興味があるが、そのようなアプリケーションを作成する完全なサイクルを理解したい場合は、このレッスンが特に役立ちます(願っています)。 それから、開発、テスト、ネットワークへのアップロード、Ethereumブロックチェーンのスマートコントラクトをアプリケーションに統合する方法を学びます。


UIアプリケーション


たとえば、私は子供の頃から誰もがよく知っている金融商品である貯金箱を取りました。 スマートコントラクトのフルパワーを実証するために、特定の金額がアカウントに蓄積されるまでお金を引き出すことができない制限を指定する機能を追加しました。 サンプルを実行するためのスクリプトとUIを含むPiggyBankリポジトリーで、レッスンのすべての資料を見つけることができます。


レッスンの目的は、開発サイクル全体を示すことであるため、一部の場所のコードは大幅に簡素化されています。 日々の開発では、 Truffleのようなツールの使用をお勧めします。


注意! 開始するには、 npm installを呼び出して、ethereumjs-testrpc、web3、およびその他のライブラリをインストールnpm install必要があります。

手順


スマートコントラクト自体を記述することに加えて、次の手順を実行する必要があります。


  1. プログラムでアカウントを作成する
  2. 契約をまとめる
  3. ブロックチェーンで契約をブロックする
  4. テストする
  5. コントラクトと対話するアプリケーションを作成します。

アプリケーションを起動するには、テストネットワークを実行する必要があります。 これは、 node bin/testnet.jsしてnode bin/testnet.jsできます。


ヒント :ステップ1および2で取得したすべてのデータが完成したフォームでリポジトリに追加されるため、3番目のステップまでスキップしてアプリケーションを起動できます。

契約


まず、契約書を作成します。 契約操作アルゴリズムは次のとおりです。


  1. ユーザーは契約を作成し、問題の未変更の制限を示します( PiggyBankメソッド)。
  2. ユーザーは、コインを契約ウォレットに送信します( deposit方法)。
  3. ユーザーは、 canWithdrawオフ操作が使用可能かどうかを確認します( canWithdrawメソッド)。
  4. ユーザーは自分のアカウントに資金を引き落とします( withdrawメソッド)。

contract.solファイルから契約コードを提供します。


 pragma solidity ^0.4.0; contract PiggyBank { //    address public owner; //     uint public limit; //    ether  wei uint decimals = (10 ** 18); //  .    //     . modifier isOwner() { require(msg.sender == owner); _; } //      . event Deposit(address indexed from, uint value); // ,          //         ether. function PiggyBank(uint _limit) public { require(_limit > 0); owner = msg.sender; limit = _limit * decimals; } //    .    payable,  //  . function deposit() public payable { Deposit(msg.sender, msg.value); } //           . //       constant function canWithdraw() public constant returns (bool) { return this.balance >= limit; } //     . //      isOwner    //  . function withdraw() public isOwner { require(canWithdraw()); //  owner   msg.sender,       : //     owner. owner.transfer(this.balance); } //   ,     ,   . function kill() public isOwner { require(this.balance == 0); selfdestruct(owner); } } 

オンラインIDEは、契約を迅速に作成するのに役立ちます。

契約が作成されると、インフラストラクチャの問題の解決を開始できます。


アカウント作成


ファイル1-account.js


このスクリプトは、特定の残高を持つテストアカウントを作成します。 コンソールからファイル0-account.jsを呼び出すと、アカウントにパスワードと金額を入力するよう求められます。 正常に実行されると、秘密鍵と金額がaccount.jsonファイルに書き込まれます。


account.jsonファイルはテストネットで使用されます。 したがって、テストネットが実行されている場合( bin/testnet.js )、再起動します。

キーについて詳しく説明します。 アカウントを作成するには、秘密鍵を作成する必要があります。 秘密鍵から、ウォレットアドレスと公開鍵が将来受信されます。 秘密鍵は256ビットの16進数で、 0xプレフィックスを含む64文字の文字列として表されます。


乱数ジェネレーターを使用して同様の値を取得するのが最善です。


 const crypto = require('crypto'); const key = '0x' + crypto.randomBytes(32).toString('hex'); 

ただし、テストの必要に応じて、ユーザーが入力したパスワードからsha3ハッシュを受け取ります。


 const privateKey = Web3.utils.soliditySha3({ type: 'string', value: '******' }); 

出力から得られるもの:


 0xc774c26b6185ccacd0ea11d1e5f03b5bac7d8171911d1861b8b7c1ab123ec94a 

手動で作成されたウォレットを使用するには、web3 APIを介して追加する必要があります。 このレッスンではこれは必要ありませんが、それでもこれがどのように行われるかを示します。


 //      const address = web3.eth.accounts.privateKeyToAccount(privateKey); //   web3.accounts.wallets.add({ privateKey, address, }); 

その後、生成したキーを使用してトランザクションを送信できます。 これは、アプリケーションでアカウントを個別に作成する場合、特にアカウントが多数ある場合に必要になることがあります。 標準のweb3.eth.personal.newAccountメソッドは、 ~/.ethereum/keystoreディスクにキーを書き込みますが、これは何らかの理由で~/.ethereum/keystore場合があります。


注意! 秘密鍵を暗号化された形式で保存することを強くお勧めします。

鍵管理と独自の鍵の使用については、個別に伝えようとします。


契約編集


ファイル2-compile.js


このスクリプトは、 contract.solファイルからソースコードをコンパイルし、結果をcode.json保存します。これは、将来、デプロイメントおよびコントラクトとの対話に使用されます。


Ethereumネットワーク上の連絡先はバイナリ表現で保存されるため、コントラクトを使用する前に、ソースコードをコンパイルする必要があります。 これは、solcツールを使用して行われ、nodejsの場合はsolcパッケージ(これはemscripten solcを使用してコンパイルされます)。


コンパイル後、 bytecodeバイナリコードと、コントラクトインターフェイスの説明を取得します。 インターフェイスでwithdrawメソッドがどのように表示されるかを以下に示します。


 { "interface": [ { "constant": false, "inputs": [], "name": "withdraw", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, ... 

出力では、solcはソースコードで見つかったすべてのコントラクトを返します。 いずれかを選択する必要があります。この場合はPiggyBankです。


 const compiled = result.contracts[':PiggyBank']; 

展開する


3-deploy.js


スクリプトは、 code.jsonからコンパイルされたコードをcode.jsonます。 そして、彼は契約を作成し、ユーザーに代わってテストネットにコードをアップロードします。 結果として生じる契約のアドレスとインターフェースは、 contract.jsonファイルに書き込まれます。


最初に、インターフェイスとデフォルト設定( fromおよびgas )で空のインスタンスが作成されます。


 const PiggyBank = new web3.eth.Contract(code.interface, { from: coinbase, gas: 5000000, //    }); 

差出人-契約のメソッドが呼び出されるアドレス。


ガスまたはガスは、アプリケーションの運用中に無駄になる契約の燃料です。 ネットワークを停止する可能性のある無限のサイクルを回避するために必要です。


そして、すべてを契約に記入する準備ができています。


  const contract = await PiggyBank.deploy({ //   data: code.bytecode, //   arguments: [1], }) .send(); 

コンストラクタはデプロイ時に呼び出されるため、すぐに引数を渡します。 PiggyBankの場合、コンストラクターには1つの引数uint _limitが含まれuint _limit 。 このコードを実行した後、トランザクションとコンストラクターコードの実行に対して別々に請求されました。


すべての準備が整いました。契約の住所を保存するだけです。


 contract.options.address; 

打ち上げ


4-run.js npm startます。


このスクリプトは、コントラクトと対話するためのシンプルなインターフェイスを備えたポート$PORTまたは8080でWebサーバーを実行します。 ブラウザでhttp://localhost:8080を開くと、アカウントにお金を振り込む( deposit )か、所有者のアカウントに振り込む( withdraw )ことができます


もう少し何が起こるか考えてみてください。 まず、以前にデプロイしたインスタンスを参照するコントラクトインスタンスを作成します。


 const piggy = new web3.eth.Contract(contract.interface, contract.address, { from: coinbase, gas: 5000000, }); 

別の引数がコンストラクターcall- addressに追加されました。これは、これが有効なコントラクトであることを示しています。 それで何ができるか見てみましょう。 canWithdraw 、メソッドdepositcanWithdraw 、およびwithdrawます。 アカウントを補充するには、 depositメソッドを呼び出して、貯金箱にコインを送る必要があります。


 piggy.methods.deposit().send({ //  ether  wei value: web3.utils.toWei('1', 'ether'), }); 

イーサリアムは計算で小数点以下18桁を使用し、浮動小数点型をサポートしません。 計算はveiで行われ、その後eserに変換されます。 これを行うには、送信する前に、 web3.utils.toWeiメソッドを使用してetherをweiに変換します。 次に、 BigNumber.jsライブラリを使用します。
タイプNumberの最大許容値を超える値の計算。


canWithdrawメソッドの呼び出しは異なります。このメソッドは変更を行わないため( constant )、 send call代わりにcallが使用さsend call 。 このような操作では、償却や燃費は発生しません。


 piggy.methods.canWithdraw().call(); 

貯金箱にコインを送る方法は次のようになります。


 router.use(async ({res}) => { await piggy.methods.deposit().send({ value: web3.utils.toWei('1', 'ether'), }); res.json(true); }); 

契約破棄


5-destroy.js


スクリプトは契約を破棄し、ブロックから契約データを削除します。 契約にお金を転送することはできますが、他の操作を実行することはできません。


テスト中


ファイルtest/test.spec.js npm test実行していnpm test


テストには、mochaライブラリが使用されます。 テストを開始する前に、事前定義されたデータを使用して分離されたテストネットを実行する必要があります。 これを行うには、以下を行う必要があります。


  1. ユーザーで新しいテストネットワークを作成します。
  2. Zameplitは、テストネットワークで契約を締結します。

新しいネットワークを初期化するように見える方法は次のとおりです。


 const Web3 = require('web3'); const TestRpc = require('ethereumjs-testrpc'); const web3 = new Web3( TestRpc.provider({ accounts: [ { secretKey: Web3.utils.soliditySha3('password1'), balance: Web3.utils.toWei(String(10), 'ether'), }, { secretKey: Web3.utils.soliditySha3('password2'), balance: Web3.utils.toWei(String(10), 'ether'), }, ], }), ); 

2人のユーザーでテストネットを作成し、web3インスタンスを初期化します。 テストネットの準備ができました。 テストを開始できます。 たとえば、コンストラクタをテストします。


 describe('PiggyBank()', function() { it('Should instantiate contract', async function() { await PiggyBank.deploy({ data: code.bytecode, arguments: [2], }) .send(); const limit = await PiggyBank.methods().limit().call(); should(web3.utils.fromWei(limit, 'ether')).be.equal('2'); }); }); 

おわりに


この例では、非常に複雑な動作を持たない非常に単純なアプリケーションを作成しましたが、契約のライフサイクルを明確に示しています。 これが、Ethereumの開発を学び始めたばかりの人々に役立つことを願っています。



Source: https://habr.com/ru/post/J343744/


All Articles