區塊鏈Dapps Coursera(第二週)Truffle 開發

課程連接 https://www.coursera.org/learn/decentralized-apps-on-blockchain/home/week/2
爲本身學習記的筆記,翻譯可能存在問題,望諒解。javascript

Truffle 開發

目錄css

Truffle IDETruffle Development: Truffle IDE (Part1)html

Truffle IDE (Part 2) (Compile Demo)前端

Truffle IDE (Part 3) (Migration Demo)java

測試驅動開發 Test-Driven Developmentlinux

Test-Driven Development (Part 1) (Test Demo)web

Test-Driven Development (Part 2) (Negative Test Demo)npm

Web Interface & TestingWeb Interface & Testing (Part 1) (Front-End Demo)json

Web Interface & Testing (Part 2) (Metamask Demo)後端

Web Interface & Testing (Part 3) (Metamask Demo Con't)


Truffle IDE
Truffle Development: Truffle IDE (Part1)

咱們準備開發端到端的去中心化應用程序。 Truffle是一站式IDE,可提供從初始應用程序模板到本地區塊鏈的全部內容,以測試完整的Dapp。 與使用remix IDE測試智能合約的方法相似,咱們將使用Truffle IDE環境組裝Dapp的各類組件。

學習目標:
1)完成本模塊後,您將可使用Truffle IDE進行Dapp開發,
2)經過建立智能合約和端到端Dapp探索Dapp開發過程。
3)說明測試驅動的開發對智能合約尤爲重要。
4)此外,您將可以經過本地區塊鏈和Injected Web3環境Metamask在區塊鏈服務器的前端進行交互。

學習目標:
1)您將可以提出Dapp問題定義,
2)概述解決問題的方法,
3)使用智能合約設計高級解決方案
4)識別Truffle IDE的組件。

咱們選擇討論了Solidity文檔的智能合約,voting.sol 或 ballot.sol 中指定的問題。 它表明了廣泛的問題,即經過民主投票來選擇獲勝者或領導者。它表明了從產品評論到購買的各類場景,這些評論是基於收到的好評而購買的。 此外,還能夠經過理事機構中有關預算的法律。 讓咱們從回顧選票Dapp開始。

咱們的目標是設計和開發Dapp進行投票,該Dapp能夠在許多項目中進行選擇,例如一組提案或產品。 在咱們的案例中,咱們將選擇四種不一樣水果的受歡迎程度。主席組織了投票。 選民經過其賬戶地址進行標識。 這是要執行的邏輯或規則。 只有主席能夠註冊其餘選民。 一個選民只能註冊一次。 只有註冊的選民才能投票。 選民只能投票一次。 沒有 「write in」 的意思是選民只能對提出的項目投票。 主席的投票權重是普通選民的兩倍。

爲了僅專一於Dapp開發過程,咱們將不處理投票的時間要素或階段。 此時,咱們將討論設計過程的四個步驟。
1)咱們將使用智能合約和實體來表示問題的主要邏輯。 咱們將從數據和操做開始。
2)咱們將添加修飾符和驗證檢查以確保解決了問題定義中指定的規則。
3)咱們將設計一個測試智能合約,以確保知足全部規定的要求。 這就像單元測試。
4)成功完成單元測試後,咱們將添加前端組件並測試完成的Dapp。 這就像集成測試。

將剛纔討論的通常設計過程應用到選票應用程序須要五個步驟。
1)咱們將設計 ballot.sol
2)用一個修飾符 「onlyOwner」 舉例,它指定有資格的人爲主席。回想一下,在智能合約的設計中,您可使用修飾符來表示全局規則。
3)咱們將爲所需的問題說明添加測試人員代碼,而後運行測試以確保它們所有經過。
4)咱們將添加用戶界面組件。
5)咱們將經過與接口交互來測試完整的應用程序。
記住,在編寫代碼以前進行設計

 ballot smart contract

咱們如何解決問題或將解決方案導入Truffle IDE?
Truffle是一個集成的開發環境或IDE,爲Dapp開發提供了一系列功能,
truffle init:包括用於初始化Dapp的模板或基本目錄結構的命令。 
truffle compile 編譯:智能合約編譯。
truffle develop 開發:用於使用控制檯進行測試的本地測試區塊鏈,
truffle migrate 遷移:用於部署智能合約的遷移腳本,
truffle test 測試:一個測試環境,用於測試部署的合同。

接下來,咱們須要安裝幾個軟件包,包括 Node.js,它將做爲 Dapp 前端的 web 服務器。Truffle 4.0.4 和 Metamask 3.14.1 Chrome插件。

你可能須要從Metamask IO中添加Metamask插件到你的Chrome瀏覽器中。它將連接到由truffle建立的區塊鏈來管理賬戶,充當應用程序前端和託管賬戶的區塊鏈節點之間的橋樑。

咱們提供了一個Linux命令表來幫助您完成這個任務。

Truffle IDE (Part 2) (Compile Demo)

接下來,咱們將建立一個Ballot1文件夾,由於咱們將使用多個版本的選票。Truffle提供了一個具備所需結構的文件夾模板目錄。要知道,在發出命令行中的命令以前,必須運行虛擬機並啓動終端。
1)首先,爲您的項目建立一個名爲Ballot1的文件夾。
2)定位到工做文件夾,在這個文件夾中,經過 mkdir Ballot1 建立一個名爲 Ballot1 的目錄或文件夾。而後經過 cd ballot1導航到這個選票目錄。一旦進入,就能夠開始初始化項目了。您可使用 truffle init 命令來實現這一點。
3)一旦執行了 truffle init,您將看到三個文件夾目錄:contracts、migrations、test。contracts 目錄 包含咱們智能合約的可靠性源文件。Migrations 目錄,Truffle使用一個遷移系統 migration system 來處理合約部署。test 目錄 包含咱們智能合約的 JavaScript 和穩定性測試。
這裏有一個重要的智能合約 migration.sol 是爲促進部署 facilitating deployment 的智能合約。
4) truffle.js。truffle 配置文件,它指定了區塊鏈網絡ID、IP和RPC端口。

如何使用 truffle IDE 編譯智能合約

1)添加合約 ballot.sol 到合約目錄;
2)將目錄更改回 bollet 的根目錄,並使用 truffle compile 進行編譯。

****如下內容是視頻截圖,還沒有實際操做,實際操做後補圖。

1)先在命令行輸入 truffle init
2)經過查看文件夾,能夠看到 contracts,migration, test 以及一些 Script 文件。進入該 contract 文件夾,能夠看到一個用 solidity 編寫的智能合約 Migrations.sol ,它有助於咱們編寫的智能合約的遷移。
4)進入 migration 文件夾,能夠看到一個 script 文件 1_initial_migrations.js ,它有助於 migration.sol 的部署。

1_initial_migrations.js

var Migrations = artifacts.require("Migrations");

module.exports = function(deployer) {
  deployer.deploy(Migrations);
};

 migration.sol

pragma solidity ^0.4.17;

contract Migrations {
  address public owner;
  uint public last_completed_migration;

  modifier restricted() {
    if (msg.sender == owner) _;
  }

  function Migrations() public {
    owner = msg.sender;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }

  function upgrade(address new_address) public restricted {
    Migrations upgraded = Migrations(new_address);
    upgraded.setCompleted(last_completed_migration);
  }
}

5)test 文件夾裏目前什麼都沒有,咱們能夠在這裏添加測試腳本。(後面內容有講到)
以上是基本模板。
如何加入 Ballot.sol 呢?(僅進行視頻描述,具體操做因人而異)
1)使用 pwd 命令顯示當前所在工做目錄的全路徑。
2)將 CourseraDocs 中的 Ballot.sol 文件複製到 contracts文件夾中。進入 contracts 文件夾目錄,能夠看到已經複製成功。
3)回到根目錄進行編譯。使用 truffle compine 命令進行編譯。它編譯了兩個 contract 文件夾中的智能合約。若是智能合約存在語法錯誤,會提示出來。它建立了一個新的文件夾,而這正是編譯器生成的構建工件 build artifacts 的位置。

打開編譯器:進入 contracts 文件夾,輸入 gedit Ballot.sol & 並把它放在後臺,這樣在編譯器打開時也能夠作其餘事情。
編譯時提示有編譯錯誤時能夠進入編譯器進行修改,可是最好的方式仍是使用 Remix 進行編譯。

Ballot.sol

pragma solidity ^0.4.17;

contract Ballot {

    struct Voter {
        uint weight;
        bool voted;
        uint8 vote;
        // address delegate;
    }

    //modifer
    modifier onlyOwner () {
      require(msg.sender == chairperson);
      _;
    }

    /* struct Proposal {
        uint voteCount; // could add other data about proposal
    } */
    address public chairperson;
    mapping(address => Voter) public voters;
    uint[4] public proposals;

    // Create a new ballot with 4 different proposals.
    function Ballot() public {
        chairperson = msg.sender;
        voters[chairperson].weight = 2;
    }

    /// Give $(toVoter) the right to vote on this ballot.
    /// May only be called by $(chairperson).
    function register(address toVoter) public onlyOwner{
        if(voters[toVoter].weight != 0) revert();
        voters[toVoter].weight = 1;
        voters[toVoter].voted = false;
    }

    /// Give a single vote to proposal $(toProposal).
    function vote(uint8 toProposal) public {
        Voter storage sender = voters[msg.sender];
        if (sender.voted || toProposal >= 4 || sender.weight == 0) revert();
        sender.voted = true;
        sender.vote = toProposal;
        proposals[toProposal] += sender.weight;
    }

    function winningProposal() public constant returns (uint8 _winningProposal) {
        uint256 winningVoteCount = 0;
        for (uint8 prop = 0; prop < 4; prop++)
            if (proposals[prop] > winningVoteCount) {
                winningVoteCount = proposals[prop];
                _winningProposal = prop;
            }
    }
}

配置 truffle 編譯環境
將ballot home中的空 truffle.js 文件直接替換爲這裏顯示的文件。這將設置您接下來將部署的本地區塊鏈的配置。 9545是Truffle提供的測試鏈的RPC端口。

module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
  // to customize your Truffle configuration!
  networks: {
    development: {
      host: "localhost",
      port: 9545,
      network_id: "*" // Match any network id
    }
  }
};

使用Truffle部署本地區塊鏈
1)在一個新終端中,定位到 bollet 的 home 位置並部署 開發區塊鏈 development blockchain。命令是 truffle develop。該命令將部署一個本地測試區塊鏈,其中包含您將看到的10個賬戶地址。
2)在將這些賬戶連接到錢包 或 諸如MetaMask之類的界面(稍後咱們將使用)時,它還將顯示種子詞 seed words。 將單詞保存好,您也能夠將保存的單詞複製到文件中。
3)咱們須要向migrations目錄添加一個文件來部署smart contract。在 migrations 目錄下建立文件  2_deploy_contracts.js ,在文件中添加以下內容:

var Ballot = artifacts.require("Ballot");

module.exports = function(deployer) {
  deployer.deploy(Ballot);
};

Truffle IDE (Part 3) (Migration Demo)

在執行時,您將看到使用有效的合約名稱將成功的 migrations 保存到網絡。您還能夠看到 message Saving 工件,這些是咱們在課程2 中研究的編譯過程生成的工件。
課程2 中已經成功編譯了 ballot ,如今將其部署或遷移到測試鏈中
(必須先部署測試鏈,而後才能在其上部署智能合約。)
****本文中全部圖片爲視頻截圖,還沒有實際操做,實際操做後補圖。
1)查看目錄,有一個 truffle.js 文件,它是咱們測試鏈的配置。能夠看到目前它裏面沒有內容。
2)將Coursera Docs 中的 truffle.js 文件複製過來:
關於主機IP是什麼以及主機將經過哪一個端口號鏈接到Web應用程序以及網絡ID自己,有一些詳細信息。

3)部署這個測試鏈。先進入一個新的終端,進入有 truffle.js 的相同文件夾,輸入命令 truffle develop

該操做爲我部署一個測試鏈,有10個賬戶,地址從賬戶0到賬戶9均可以看到,這些私鑰的私鑰也是256位。地址是160位,還顯示了 seed words ,請保存好這些 seed words ,咱們可能會在連接測試鏈時用到。

4)咱們的鏈已經準備好了,打開以前的終端進行操做:
如今咱們準備將咱們的智能編譯合約遷移或部署到這裏。爲了部署,咱們還須要把這個文件 2_deploy_contracts 複製到 Migrations 文件夾中。

5)這是一個開發階段,因此若是您對以前部署的智能合約版本不滿意,咱們能夠覆蓋以前的智能合約。一旦你部署了智能合約,你就不能改變它。在這裏,我將重置以前可能部署的任何 ballot ,使用命令 truffle --reset,這將部署編譯後的智能契約。

您能夠看到它使用了咱們剛纔建立的網絡開發。 而後,它部署用於基礎遷移的初始遷移,而後部署咱們編譯的 ballot 合約。 而且還保存了稍後將在鏈接到工件時使用的工件。

您能夠用Truffle控制檯 console 作什麼?
控制檯爲測試鏈上建立的賬戶以及咱們前面在模塊1中討論的管理API提供了命令行界面。
下面是一些用於在賬戶之間傳輸數據的命令:

咱們還經過將一個事務從一個賬戶發送到另外一個賬戶並檢查發送者的餘額來測試 truffle 測試鏈。

總結:在這節課中,咱們引入 ballot 問題來講明 Dapp 在 truffle 的開發過程。咱們介紹了 truffle IDE 命令。咱們還討論了在Truffle IDE 上開發的 DApp 的目錄結構。咱們最後決定開發和部署一份智能合同。

閱讀材料
TRUFFLE TUTORIAL INDEX
Linux command sheet

測試題


測試驅動開發 Test-Driven Development

Test-Driven Development (Part 1) (Test Demo)

測試是硬件開發的基本階段。在產品發佈以前測試是硬件設計的關鍵步驟。一般經過每週更新來提供軟件系統上的錯誤修復。那麼咱們的智能合約(Dapps的核心邏輯)如何?

智能合約就像一個硬件芯片。一旦部署,它們將是最終版本,而且沒法進行更新,除非內置了特殊規定或逃生艙口 escape hatches。測試對於智能合同來講是絕對必要的。

完成本課程後,您將可以詳細解釋測試驅動開發和測試腳本的重要性。

能夠經過正面測試 positive tests 來進行 ballot 智能合約的測試,確保對於給定的有效輸入,它能夠按預期執行。
咱們將測試 deploy,register,vote,winning proposal 的完整 ballot 週期。 並進行負面測試 negative test,確保它可以正確處理無效的輸入和狀況。

一般,測試人員用與要測試的主要應用程序相同的語言編寫。您將以智能合約和Solidity語言編寫測試器 tester 自己。Truffle Pet Shop 示例中對此有很好的說明。 可是,ballot 合約反覆使用主席和選民的地址類型數據。當另外一個智能合約用做測試器時,這會引發問題。 所以,咱們將使用 Truffle 支持的替代語言(即JavaScript)編寫測試。

Truffle 容許 JavaScript 和 Solidity 兩種語言,咱們將使用 JavaScript。 看一下 test.js,它有四個正面測試和兩個負面測試。 若是咱們可使用編輯器打開 test.js,則能夠繼續進行。

到目前爲止,咱們已經使用 Truffle IDE 初始化了 Dapp 開發的模板,並將帶有 ballot.sol 智能合約的模板添加到了 contracts 文件夾中。編譯此智能合約並將其部署或遷移到 Truffle IDE 提供的測試鏈中。如今讓咱們測試它。爲此,咱們須要在 test 文件夾中提供一些測試腳本,如您所見,那裏沒有任何內容。咱們將課程資源中的 test 腳本複製到 test 文件夾中。

讓咱們看看 test.js 包含什麼。 我已經在 Atom 編輯器中將其打開。 咱們設計的方式是先創建正測試用例,而後再創建幾個負測試用例。稍後,咱們能夠添加任意數量的負面測試用例。
第一行寫着咱們須要 Ballot.sol 工件。而後建立此 Ballot 的 test instance,咱們確保它被部署在第一個測試用例中。在第二個測試用例中,咱們要確保良好的 user registration 正在進行中。咱們正在註冊三個帳戶,確保只有全部者能夠註冊。你能夠看到這是有效的註冊。第三個功能是 Valid voting 。第四個功能是 Validate winner。所以,前四個用例將徹底測試管道 pipeline 或函數 deploy,register,vote,winning proposal 的進程。
接下來的兩個測試用例是負測試用例,不是全部者試圖註冊一個賬戶,一名未登記的選民正試圖爲這一進程投票。這裏有兩個測試用例,可是可能有更多。在後面的演示中會看到更多的測試用例。這將是您未來可能進行的應用程序測試的模板。你須要一些基本的JavaScript知識。
test.js

let Ballot = artifacts.require("./Ballot.sol");

let ballotInstance;

contract('Ballot Contract', function (accounts) {
  //accounts[0] is the default account
  //Positive Test 1 
  it("Contract deployment", function() {
    return Ballot.deployed().then(function (instance) {
      ballotInstance = instance;
      assert(ballotInstance !== undefined, 'Ballot contract should be defined');
    });
  });

  //Positive Test 2
  it("Valid user registration", function() {
    return ballotInstance.register(accounts[1], { from: accounts[0]}).then(function (result) {
      assert.equal('0x01', result.receipt.status, 'Registration is valid');
      return ballotInstance.register(accounts[2], { from: accounts[0]});
    }).then(function (result) {
      assert.equal('0x01', result.receipt.status, 'Registration is valid');
      return ballotInstance.register(accounts[3], { from: accounts[0]});
    }).then(function(result) {
      assert.equal('0x01', result.receipt.status, 'Registration is valid');
    });
  });

  //Positive Test 3
  it("Valid voting", function() {
    return ballotInstance.vote(2, {from: accounts[0]}).then(function (result) {
      assert.equal('0x01', result.receipt.status, 'Voting is done');
      return ballotInstance.vote(1, {from: accounts[1]});
    }).then(function (result) {
      assert.equal('0x01', result.receipt.status, 'Voting is done');
      return ballotInstance.vote(1, {from: accounts[2]});
    }).then(function (result) {
      assert.equal('0x01', result.receipt.status, 'Voting is done');
      return ballotInstance.vote(1, {from: accounts[3]});
    }).then(function (result) {
      assert.equal('0x01', result.receipt.status, 'Voting is done');
    });
  });

  //Positive Test 4
  it("Validate winner", function () {
    return ballotInstance.winningProposal.call().then(function (result) {
      assert.equal(1, result.toNumber(), 'Winner is validated with the expected winner');
    });
  });

  //Negative Test 5
  it("Should NOT accept unauthorized registration", function () {
    return ballotInstance.register(accounts[6], { from: accounts[1]})
	.then(function (result) {
		/* 	If the user had handled the above mentioned failure case in solidity,
			then this block will not be executed.
			Truffle would directly throw the revert error which will be catched
		*/
		throw("Condition not implemented in Smart Contract");
    }).catch(function (e) {
		/*	If the error is custom thrown then the condition was not checked and hence fail the test case
			else pass the test case
		*/
		if(e === "Condition not implemented in Smart Contract") {
			assert(false);
		} else {
			assert(true);
		}
	})
  });

  //Negative Test 6
  it("Should NOT accept unregistered user vote", function () {
    return ballotInstance.vote(1, {from: accounts[7]})
		.then(function (result) {
				throw("Condition not implemented in Smart Contract");
    }).catch(function (e) {
		if(e === "Condition not implemented in Smart Contract") {
			assert(false);
		} else {
			assert(true);
		}
	})
  });
});

回到基本目錄,命令是 truffle test 。它編譯了它,因此咱們要對它進行雙重測試。

您能夠經過測試用例看到它。 有四個有效的測試用例,咱們提供了很好的輸入,而且確實進行了檢查。 接下來的兩個是錯誤的輸入,您的 Solidity 智能合約確實將這些捕獲爲錯誤的輸入,而且沒有執行該操做。咱們經過了六個測試。咱們將在之後的投票演示中添加更多測試。

Test-Driven Development (Part 2) (Negative Test Demo)

如今在 contracts 文件夾中編輯 Ballot.sol。 從註冊功能中刪除 onlyOwner 。 保存 Ballot.sol。 您可使用 gedit 編輯器或您熟悉的任何其餘編輯器(例如atom)。導航回到基本目錄,而後發出命令 truffle compile, truffle migrate, --reset, truffle test. 在這種狀況下,重置 reset 很是重要,由於咱們將覆蓋智能合約的先前版本,而重置將解決該問題。 而後繼續測試,truffle test。您會注意到其中一個負測試案例失敗了。

第二次負面測試。 編輯 Ballot.sol,voter 函數。 刪除(voter.weight == 0)和它前面的逗號,以便它不會檢查未註冊的選民。 這是一個錯誤。 保存。 瀏覽回到基本目錄並再次重複該過程,進行 truffle compile, truffle migrate, --reset, truffle test ,您將看到另外一個負測試用例失敗。

如今回到Ballot.sol,對其進行編輯,添加已刪除的項目。 在將 onlyOwner 添加到 register 函數的標題行中,並將 weight == 0 添加到 voter 函數以後,將其保存,執行 truffle 步驟以確保一切正常。 導航回基本目錄, truffle compile, truffle migrate, --reset, truffle test 。 您應該看到全部測試都經過了。

如今讓咱們檢查負面測試。 有兩個負面測試。 一,由 owner 註冊。 只有全部者能夠註冊。 第二個是僅註冊賬戶能夠投票的賬戶。 如今,咱們將僅測試其中的一個,當咱們擁有完整的層時,稍後將測試第二個。 對於第一個,我將進入合同文件夾並編輯gedit 個人 Ballot.sol 並轉到 register 函數。 在 register 函數中,這裏指定了一個修飾符 onlyOwner,只有全部者能夠註冊 ,我將刪除它。能夠正確編譯,但實際上是存在這個錯誤。測試時,正面測試經過,由於過程是正確的。可是,當涉及到負面測試時,因爲咱們沒有指定 onlyOwner,而且該條件是由咱們的負測試用例檢查的,它會給咱們一個錯誤。

到目前爲止,您應該很是熟悉開發DApp的步驟。即便您不是 JavaScript 專家,也可使用咱們提供的 test.js 做爲編寫測試的模型。

閱讀材料
Ethereum Pet Shop Dapp
Testing of Smart Contracts in the Blockchain world
Smart Contracts and Truffle 101. Part 4 - Testing Functions and Errors

測試題

Web Interface & Testing
Web Interface & Testing (Part 1) (Front-End Demo)

在本課程中,咱們將向DApp添加Web界面。咱們建立了一個預先填寫的 ballot 應用程序。完成這節課後,你能夠列出 Ballot.sol 的基本代碼爲連接到用戶界面所作的更改,並將用戶界面前端集成到選票智能合約和區塊鏈後端。

解壓 Ballot2.zip 這多是任何DApp項目所需的基本結構。請注意,文件和目錄比之前概述的要多。

咱們知道,contracts 文件夾有智能合約,migration 文件夾內是遷移腳本,test 文件夾內是 test 腳本,build 包含由編譯過程生成的JSON構件,源文件夾包含web資產,如JavaScript、CSS和索引HTML。Node module文件夾中有 Node.js 模塊。JSON文件和JavaScript是配置文件。

如今,導航到提供 web 資產的源目錄。 源 Java 的目錄中有一個名爲 app.js 的文件。 這實例化了Web客戶端與區塊鏈節點進行通訊所需的對象。 咱們須要公開經過應用程序的 web3 提供程序對象建立的區塊鏈賬戶。

簡單地說,web3 init代碼就是這樣作的。它展現瞭如何建立web3對象並將其連接到咱們應用的測試鏈。

其次,咱們須要告訴web3在哪裏能夠找到智能合約工件。 app.js 的 Init Contract 方法能夠作到這一點。 而後,您必須添加方法來處理對智能合約的調用,並將值從智能合約返回到Web界面。

app.js

App = {
  web3Provider: null,
  contracts: {},
  names: new Array(),
  url: 'http://127.0.0.1:9545',
  chairPerson:null,
  currentAccount:null,
  init: function() {
    $.getJSON('../proposals.json', function(data) {
      var proposalsRow = $('#proposalsRow');
      var proposalTemplate = $('#proposalTemplate');

      for (i = 0; i < data.length; i ++) {
        proposalTemplate.find('.panel-title').text(data[i].name);
        proposalTemplate.find('img').attr('src', data[i].picture);
        proposalTemplate.find('.btn-vote').attr('data-id', data[i].id);

        proposalsRow.append(proposalTemplate.html());
        App.names.push(data[i].name);
      }
    });

    return App.initWeb3();
  },

  initWeb3: function() {
        // Is there is an injected web3 instance?
    if (typeof web3 !== 'undefined') {
      App.web3Provider = web3.currentProvider;
    } else {
      // If no injected web3 instance is detected, fallback to the TestRPC
      App.web3Provider = new Web3.providers.HttpProvider(App.url);
    }
    web3 = new Web3(App.web3Provider);

    App.populateAddress();
    return App.initContract();
  },

  initContract: function() {
      $.getJSON('Ballot.json', function(data) {
    // Get the necessary contract artifact file and instantiate it with truffle-contract
    var voteArtifact = data;
    App.contracts.vote = TruffleContract(voteArtifact);

    // Set the provider for our contract
    App.contracts.vote.setProvider(App.web3Provider);
    
    App.getChairperson();
    return App.bindEvents();
  });
  },

  

  bindEvents: function() {
    $(document).on('click', '.btn-vote', App.handleVote);
    $(document).on('click', '#win-count', App.handleWinner);
    $(document).on('click', '#register', function(){ var ad = $('#enter_address').val(); App.handleRegister(ad);   });
  },

  
  populateAddress : function(){
    new Web3(new Web3.providers.HttpProvider(App.url)).eth.getAccounts((err, accounts) => {
      jQuery.each(accounts,function(i){
        if(web3.eth.coinbase != accounts[i]){
          var optionElement = '<option value="'+accounts[i]+'">'+accounts[i]+'</option';
          jQuery('#enter_address').append(optionElement);  
        }
      });
    });
  },

  getChairperson : function(){
    App.contracts.vote.deployed().then(function(instance) {
      return instance.chairperson();
    }).then(function(result) {
      App.chairPerson = result.toString();
      App.currentAccount = web3.eth.coinbase;
      if(App.chairPerson != App.currentAccount){
        jQuery('#address_div').css('display','none');
        jQuery('#register_div').css('display','none');
      }else{
        jQuery('#address_div').css('display','block');
        jQuery('#register_div').css('display','block');
      }
    })
  },

  handleRegister: function(addr){

    var voteInstance;
    App.contracts.vote.deployed().then(function(instance) {
      voteInstance = instance;
      return voteInstance.register(addr);
    }).then( function(result){
      if(result.receipt.status == '0x01')
        alert(addr + " is registered successfully")
      else
        alert(addr + " account registeration failed due to revert")
    }).catch( function(err){
      alert(addr + " account registeration failed")
    })
  },

  handleVote: function(event) {
    event.preventDefault();
    var proposalId = parseInt($(event.target).data('id'));
    var voteInstance;

    web3.eth.getAccounts(function(error, accounts) {
      var account = accounts[0];

      App.contracts.vote.deployed().then(function(instance) {
        voteInstance = instance;

        return voteInstance.vote(proposalId, {from: account});
      }).then(function(result){
            if(result.receipt.status == '0x01')
            alert(account + " voting done successfully")
            else
            alert(account + " voting not done successfully due to revert")
        }).catch(function(err){
          alert(account + " voting failed")
    });
    });
  },


  handleWinner : function() {
    var voteInstance;
    App.contracts.vote.deployed().then(function(instance) {
      voteInstance = instance;
      return voteInstance.winningProposal();
    }).then(function(res){
      alert(App.names[res] + "  is the winner ! :)");
    }).catch(function(err){
      console.log(err.message);
    })
  }
};


$(function() {
  $(window).load(function() {
    App.init();
    console.log('starting app.js');
  });
});

這裏指出幾個函數,我正在建立一個應用程序對象,該對象將初始化web3對象,該對象將成爲您前端應用程序中全部區塊鏈報告的接收者。 而後它將傳遞給在區塊鏈中打開的基礎RPC端點。
init contract ,即一個 web3 對象。 該 init contract 將指出智能合約所需的全部工件,在本例中爲 Ballot.json。 
handleRegister,處理智能合約的註冊函數。
handleVote,這是你的智能合約的投票功能的粘合劑。
 handleWinner,這是一個處理您的智能合約中 winningProposals 的功能。

因此,你能夠用這個做爲模板來開發你的前端。這是一個重要的文件,它是你的智能合約和前端之間的管道。

回到基礎目錄並進行編譯。

測試,truffle test,能夠看到全部的十個測試用例都經過了,您能夠查看 test.js 以瞭解更多關於測試用例的信息。

我將部署一個更新的版本。

最後,運行 lite server。這是用於咱們將要 npm run 的網頁,運行開發服務器,run dev,這將打開一個Web端點,該端點將顯示咱們的全部Web資產。

導航回到有效的基本目錄。 爲了建立Web界面,最少須要一個index.html文件。 咱們還添加了其餘網絡資產,例如咱們要投票的項目的圖像,資金和CSS樣式文件。 目前,您至少有兩個選擇。 若是您具備前端開發技能,則能夠建立本身的前端,也能夠只使用源目錄中提供的Web資產。

檢查全部文件。

Web Interface & Testing (Part 2) (Metamask Demo)

從新打開測試鏈 test chain,咱們已經完成了如下內容。如今啓動Chrome瀏覽器。去 metamask.io 獲取 Chrome 的 metamask 擴展。

MetaMask是咱們的區塊鏈服務器和網絡之間的橋樑。 咱們已將其添加爲Chrome擴展程序。 這是咱們探索的首選方法。 MetaMask就像一個數字錢包,提供鏈接到基礎區塊鏈節點,管理帳戶,猜想交易要點和餘額的功能。 MetaMask還提供了一個簡單的界面來對交易進行簽名並傳輸執行交易所需的猜想點 guess points。

MetaMask安裝迅速。 它很容易使用。 若是它與您的區塊鏈服務器不一樣步,請當即刪除並再次添加它。 如今,讓咱們看看如何將MetaMask連接到區塊鏈服務器。 在truffle IDE中,由 truffle develop 命令部署的測試鏈的默認端口爲9545。賬戶地址經過此端口公開。 如今,讓咱們連接MetaMask來爲咱們的DApp管理它。
**接下來的內容關於MetaMask的設置操做並不複雜,能夠參考其餘博文。

Web Interface & Testing (Part 3) (Metamask Demo Con't)

我已經準備好了前端節點,而且MetaMask擁有了全部賬戶,而且我在這裏建立了五個賬戶,我將經過註冊一些賬戶開始。進行註冊,註冊成功。這些是您在app.js中造成的支持功能,這些功能將這些功能帶到了網絡上。用一樣的方式註冊其餘帳戶。從賬戶1開始投票。賬戶1的投票權重是2,咱們將它投票給香蕉。除了全部者帳戶1之外,其餘四個帳戶都投票給蘋果。您會看到猜想點和其餘內容確實獲得了反映,並負責MetaMask管理。嘗試用帳戶2註冊新帳戶,失敗。嘗試用還沒有註冊的帳戶進行投票,也是失敗的。最終的獲勝者是蘋果 。這是一個完整的 Dapp 演示。

閱讀材料
Smart Contracts and Truffle 101. Part 3 - Simple functions and tests
Ethereum dApp tutorial - Front end. Part 1

測試題