以太坊Dapp終極教程——如何構建一個完整的全棧去中心化應用(二)

以太坊Dapp終極教程——如何構建一個完整的全棧去中心化應用(一)中,咱們已經完成了一切所需的設置,讓咱們經過列出將在選舉中運行的候選人來繼續構建智能聯繫。咱們須要一種方法來存儲多個候選者,並存儲關於每一個候選者的多個屬性。咱們但願跟蹤候選人的身份,姓名和投票計數。如下是咱們如何爲候選人建模:javascript

contract Election {
    // Model a Candidate
    struct Candidate {
        uint id;
        string name;
        uint voteCount;
    }

    // ...
}

咱們使用Solidity Struct爲候選人建模。Solidity容許咱們建立本身的結構類型,就像咱們在這裏爲候選人所作的那樣。咱們指定此結構具備無符號整數類型的id,字符串類型的名稱和無符號整數類型的voteCount。簡單地聲明這個結構實際上不會給咱們一個候選人。咱們須要實例化它並將其分配給變量,而後才能將其寫入存儲。php

接下來咱們須要的是存放候選人的地方。咱們須要一個地方來存儲咱們剛剛建立的結構類型之一。咱們可使用Solidity mapping來完成此操做。Solidity中的映射相似於關聯數組或散列,它將鍵值對關聯起來。咱們能夠像這樣建立這個映射:css

contract Election {
    // Model a Candidate
    struct Candidate {
        uint id;
        string name;
        uint voteCount;
    }

    // Read/write Candidates
    mapping(uint => Candidate) public candidates;

    // ...
}

在這種狀況下,映射的關鍵是無符號整數,值是咱們剛剛定義的候選結構類型。這基本上爲咱們提供了基於身份的每一個候選人的查找。因爲此映射已分配給狀態變量,所以只要咱們爲其分配新的鍵值對,咱們就會將數據寫入區塊鏈。接下來,咱們將此映射的可見性設置爲public以獲取getter函數,就像咱們在冒煙測試中使用候選名稱同樣。html

接下來,咱們使用計數器緩存狀態變量跟蹤選舉中存在多少候選者,以下所示:前端

contract Election {
    // Model a Candidate
    struct Candidate {
        uint id;
        string name;
        uint voteCount;
    }

    // Read/write Candidates
    mapping(uint => Candidate) public candidates;

    // Store Candidates Count
    uint public candidatesCount;

    // ...
}

在Solidity中,沒法肯定映射的大小,也沒法迭代它。這是由於還沒有賦值的映射中的任何鍵都將返回默認值(在這種狀況下爲空候選)。例如,若是咱們在此次選舉中只有2名候選人,而且咱們嘗試查找候選人#99,那麼映射將返回空的候選人結構。此行爲使得沒法知道存在多少候選項,所以咱們必須使用計數器緩存。java

接下來,讓咱們建立一個函數來將候選添加到咱們建立的映射中,以下所示:node

contract Election {
    // ...

    function addCandidate (string _name) private {
        candidatesCount ++;
        candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
    }
}

咱們已經聲明瞭函數addCandidate,它接受一個表示候選者名稱的字符串類型參數。在函數內部,咱們遞增候選計數器緩存以表示已添加新候選者。而後咱們使用當前候選計數做爲關鍵字,用新的Candidate結構更新映射。使用當前候選計數中的候選ID,函數參數中的名稱以及初始投票計數來初始化此Candidate結構。請注意,此函數的可見性是私有的,由於咱們只想在合約中調用它。python

如今咱們能夠經過在構造函數中調用兩次addCandidate函數來添加兩個候選者,以下所示:jquery

contract Election {
    // ...

    function Election () public {
        addCandidate("Candidate 1");
        addCandidate("Candidate 2");
    }

    // ...
}

當咱們將合約部署到區塊鏈時,將執行此遷移,並使用兩個候選人填充咱們的選舉。此時,你的完整合約代碼應以下所示:android

pragma solidity ^0.4.2;

contract Election {
    // Model a Candidate
    struct Candidate {
        uint id;
        string name;
        uint voteCount;
    }

    // Read/write candidates
    mapping(uint => Candidate) public candidates;

    // Store Candidates Count
    uint public candidatesCount;

    function Election () public {
        addCandidate("Candidate 1");
        addCandidate("Candidate 2");
    }

    function addCandidate (string _name) private {
        candidatesCount ++;
        candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
    }

}

如今讓咱們像這樣遷移咱們的合約:

$ truffle migrate --reset

如今嘗試與控制檯內的候選人進行交互。

如今讓咱們編寫一些測試來確保咱們的智能合約正確初始化。首先,讓我解釋爲何在開發智能合約時測試很是重要。咱們但願確保合約沒有錯誤,緣由以下:

  • 1.以太坊區塊鏈上的全部代碼都是不可變的;它沒法改變。若是合約包含任何錯誤,咱們必須禁用它並部署新副本。此新副本與舊合約的狀態不一樣,它將具備不一樣的地址。
  • 2.部署合約須要耗費gas,由於它會建立一個交易並將數據寫入區塊鏈。這會花費Ether,咱們但願儘可能減小咱們必須支付的以太網數量。
  • 3.若是咱們寫入區塊鏈的任何合約函數都包含錯誤,則調用此函數的賬戶可能會浪費Ether,而且可能不會按預期方式運行。

測試

如今讓咱們寫一些測試。確保你先運行Ganache。而後,從項目的根目錄在命令行中建立一個新的測試文件,以下所示:

$ touch test/election.js

咱們將使用Mocha測試框架和Chai斷言庫在此文件中的Javascript中編寫全部測試。這些與Truffle框架捆綁在一塊兒。咱們將在Javascript中編寫全部這些測試,以模擬與智能合約的客戶端交互,就像咱們在控制檯中所作的那樣。如下是測試的全部代碼:

var Election = artifacts.require("./Election.sol");

contract("Election", function(accounts) {
  var electionInstance;

  it("initializes with two candidates", function() {
    return Election.deployed().then(function(instance) {
      return instance.candidatesCount();
    }).then(function(count) {
      assert.equal(count, 2);
    });
  });

  it("it initializes the candidates with the correct values", function() {
    return Election.deployed().then(function(instance) {
      electionInstance = instance;
      return electionInstance.candidates(1);
    }).then(function(candidate) {
      assert.equal(candidate[0], 1, "contains the correct id");
      assert.equal(candidate[1], "Candidate 1", "contains the correct name");
      assert.equal(candidate[2], 0, "contains the correct votes count");
      return electionInstance.candidates(2);
    }).then(function(candidate) {
      assert.equal(candidate[0], 2, "contains the correct id");
      assert.equal(candidate[1], "Candidate 2", "contains the correct name");
      assert.equal(candidate[2], 0, "contains the correct votes count");
    });
  });
});

讓我解釋一下這段代碼。首先,咱們要求合約並將其分配給變量,就像咱們在遷移文件中所作的那樣。接下來,咱們調用合約函數contract,並在回調函數中編寫全部測試。此回調函數提供了一個賬戶Accounts變量,表示咱們的區塊鏈上的全部賬戶,由Ganache提供。

第一次測試經過檢查候選人數等於2來檢查合約是否已使用正確數量的候選人進行初始化。

下一個測試檢查每一個候選人在選舉中的價值,確保每一個候選人都有正確的身份證,姓名和投票計數。

如今讓咱們從命令行運行測試,以下所示:

$ truffle test

是的,他們經過了!

客戶端應用程序

如今讓咱們開始構建將與智能合約對話的客戶端應用程序。咱們將經過修改咱們在上一節中安裝的Truffle Pet Shop框附帶的HTML和Javascript文件來完成此操做。咱們將使用此現有代碼開始。咱們還要注意Truffle Pet Shop盒子附帶的一些其餘東西,好比Bootstrap框架,它將使咱們沒必要在本教程中編寫任何CSS。咱們還有lite-server,它將爲咱們的資產提供服務以用於開發目的。

你沒必要是前端專家就能夠按照本教程的這一部分進行操做。我故意保持HTML和Javascript代碼很是簡單,咱們不會花太多時間專一於它。我想繼續專一於開發dApp的智能合約部分!

繼續使用如下代碼替換index.html文件的全部內容:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Election Results</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
    <div class="container" style="width: 650px;">
      <div class="row">
        <div class="col-lg-12">
          <h1 class="text-center">Election Results</h1>
          <hr/>
          <br/>
          <div id="loader">
            <p class="text-center">Loading...</p>
          </div>
          <div id="content" style="display: none;">
            <table class="table">
              <thead>
                <tr>
                  <th scope="col">#</th>
                  <th scope="col">Name</th>
                  <th scope="col">Votes</th>
                </tr>
              </thead>
              <tbody id="candidatesResults">
              </tbody>
            </table>
            <hr/>
            <p id="accountAddress" class="text-center"></p>
          </div>
        </div>
      </div>
    </div>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
    <script src="js/web3.min.js"></script>
    <script src="js/truffle-contract.js"></script>
    <script src="js/app.js"></script>
  </body>
</html>

接下來,使用如下代碼替換app.js文件的全部內容:

App = {
  web3Provider: null,
  contracts: {},
  account: '0x0',

  init: function() {
    return App.initWeb3();
  },

  initWeb3: function() {
    if (typeof web3 !== 'undefined') {
      // If a web3 instance is already provided by Meta Mask.
      App.web3Provider = web3.currentProvider;
      web3 = new Web3(web3.currentProvider);
    } else {
      // Specify default instance if no web3 instance provided
      App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
      web3 = new Web3(App.web3Provider);
    }
    return App.initContract();
  },

  initContract: function() {
    $.getJSON("Election.json", function(election) {
      // Instantiate a new truffle contract from the artifact
      App.contracts.Election = TruffleContract(election);
      // Connect provider to interact with contract
      App.contracts.Election.setProvider(App.web3Provider);

      return App.render();
    });
  },

  render: function() {
    var electionInstance;
    var loader = $("#loader");
    var content = $("#content");

    loader.show();
    content.hide();

    // Load account data
    web3.eth.getCoinbase(function(err, account) {
      if (err === null) {
        App.account = account;
        $("#accountAddress").html("Your Account: " + account);
      }
    });

    // Load contract data
    App.contracts.Election.deployed().then(function(instance) {
      electionInstance = instance;
      return electionInstance.candidatesCount();
    }).then(function(candidatesCount) {
      var candidatesResults = $("#candidatesResults");
      candidatesResults.empty();

      for (var i = 1; i <= candidatesCount; i++) {
        electionInstance.candidates(i).then(function(candidate) {
          var id = candidate[0];
          var name = candidate[1];
          var voteCount = candidate[2];

          // Render candidate Result
          var candidateTemplate = "<tr><th>" + id + "</th><td>" + name + "</td><td>" + voteCount + "</td></tr>"
          candidatesResults.append(candidateTemplate);
        });
      }

      loader.hide();
      content.show();
    }).catch(function(error) {
      console.warn(error);
    });
  }
};

$(function() {
  $(window).load(function() {
    App.init();
  });
});

讓咱們注意一下這段代碼所作的一些事情:

  • 設置web3:web3.js是一個javascript庫,容許咱們的客戶端應用程序與區塊鏈交談。咱們在initWeb3函數中配置web3。
  • 初始化合約:咱們在此函數中獲取智能合約的已部署實例,並分配一些容許咱們與之交互的值。
  • 渲染功能:渲染功能使用智能合約中的數據列出頁面上的全部內容。目前,咱們列出了咱們在智能合約中建立的候選人。咱們經過遍歷映射中的每一個候選項並將其呈現給表來完成此操做。咱們還獲取鏈接到此功能內的區塊鏈的當前賬戶並將其顯示在頁面上。

如今讓咱們在瀏覽器中查看客戶端應用程序。首先,確保你已按照如下方式遷移合約:

$ truffle migrate --reset

接下來,從命令行啓動你的開發服務器,以下所示:

$ npm run dev

這應該會自動使用客戶端應用程序打開一個新的瀏覽器窗口。

請注意你的應用程序顯示正在加載loading...。那是由於咱們尚未登陸到區塊鏈!爲了鏈接到區塊鏈,咱們須要將其中一個賬戶從Ganache導入Metamask。

與Metamask鏈接後,你應該會看到全部合約和賬戶數據都已加載。

======================================================================

分享一些比特幣、以太坊、EOS、Fabric等區塊鏈相關的交互式在線編程實戰教程:

  • java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在Java代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
  • php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在Php代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
  • c#比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在C#代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是C#工程師不可多得的比特幣開發學習課程。
  • java以太坊開發教程,主要是針對java和android程序員進行區塊鏈以太坊開發的web3j詳解。
  • python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
  • php以太坊,主要是介紹使用php進行智能合約開發交互,進行帳號建立、交易、轉帳、代幣開發以及過濾器和交易等內容。
  • 以太坊入門教程,主要介紹智能合約與dapp應用開發,適合入門。
  • 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
  • ERC721以太坊通證明戰,課程以一個數字藝術品創做與分享DApp的實戰開發爲主線,深刻講解以太坊非同質化通證的概念、標準與開發方案。內容包含ERC-721標準的自主實現,講解OpenZeppelin合約代碼庫二次開發,實戰項目採用Truffle,IPFS,實現了通證以及去中心化的通證交易所。
  • C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括帳戶管理、狀態與交易、智能合約開發與交互、過濾器和交易等。
  • EOS入門教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、帳戶與錢包、發行代幣、智能合約開發與部署、使用代碼與智能合約交互等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
  • 深刻淺出玩轉EOS錢包開發,本課程以手機EOS錢包的完整開發過程爲主線,深刻學習EOS區塊鏈應用開發,課程內容即涵蓋帳戶、計算資源、智能合約、動做與交易等EOS區塊鏈的核心概念,同時也講解如何使用eosjs和eosjs-ecc開發包訪問EOS區塊鏈,以及如何在React前端應用中集成對EOS區塊鏈的支持。課程內容深刻淺出,很是適合前端工程師深刻學習EOS區塊鏈應用開發。
  • Hyperledger Fabric 區塊鏈開發詳解,本課程面向初學者,內容即包含Hyperledger Fabric的身份證書與MSP服務、權限策略、信道配置與啓動、鏈碼通訊接口等核心概念,也包含Fabric網絡設計、nodejs鏈碼與應用開發的操做實踐,是Nodejs工程師學習Fabric區塊鏈開發的最佳選擇。
  • Hyperledger Fabric java 區塊鏈開發詳解,課程面向初學者,內容即包含Hyperledger Fabric的身份證書與MSP服務、權限策略、信道配置與啓動、鏈碼通訊接口等核心概念,也包含Fabric網絡設計、java鏈碼與應用開發的操做實踐,是java工程師學習Fabric區塊鏈開發的最佳選擇。
  • tendermint區塊鏈開發詳解,本課程適合但願使用tendermint進行區塊鏈開發的工程師,課程內容即包括tendermint應用開發模型中的核心概念,例如ABCI接口、默克爾樹、多版本狀態庫等,也包括代幣發行等豐富的實操代碼,是go語言工程師快速入門區塊鏈開發的最佳選擇。

匯智網原創翻譯,轉載請標明出處。這裏是以太坊Dapp終極教程——如何構建一個完整的全棧去中心化應用

相關文章
相關標籤/搜索