1. 程式人生 > >以太坊DAPP開發例項: 全棧投票系統

以太坊DAPP開發例項: 全棧投票系統

[教程最近更新於2018年1月]


在我以前的文章中,我解釋了以太坊平臺和web 應用的差別。作為一個開發者,學習任何新技術的最好方法是潛入和嘗試構建應用。在這篇文章中,讓我們構建一個簡單的"Hello World!" 應用程式, 這是一個投票應用程式。

該應用程式非常簡單,它所做的只是初始化一組候選人,讓任何人投票給候選人,並顯示每個候選人收到的總票數。我們的目標不是簡單地編寫應用程式,而是學習編譯、部署和互動的過程。

我有意避免使用任何DAPP框架構建這個應用程式,因為框架抽象掉很多細節,你不瞭解系統的內部。此外,當您使用框架時,您將對框架為您所做的所有繁重工作有更多的感激!

在許多方面,這篇文章是上一篇文章的續篇。如果你是剛接觸以太坊,那我推薦你先閱讀以下我的上一篇文章。

這個練習的目的是:
        建立開發環境。
        學習編寫智慧合約、編譯和將其部署到開發環境中的過程。
        與通過Nodejs控制檯與blockchain上的智慧合約互動

        通過一個簡單的網頁與智慧合約互動,通過頁面進行投票,顯示候選人的票數

整個應用程式執行在Ubuntu 16.04 xenial。macOS上我也測試過了。

下面的檢視將是我們要構建的應用


1. 設定開發環境
     我們使用一個模擬的記憶體區塊鏈(ganache)代替真實的區塊鏈在進行開發。在本教程的2章,我們將與真實的區塊鏈互動。下面是安裝ganache、web3js的步驟,然後在linux上啟動一個測試鏈。在macOS上安裝過程也是一樣的。對於Windows,你可以參考

注意:本教程目前與web3js 0.20.2版本。web3js 1.0穩定版本如果釋出,我會更新一下教程.


你可以看到ganache-cli自動建立了10個測試賬號,每個賬號預分配了100(虛構的)ethers

2.簡單的投票合約
 我們將使用solidity程式語言來編寫我們的合約。如果您熟悉面向物件程式設計,學習編寫solidity合約應該是輕而易舉的事。我們將編寫一個合約物件,含有一個建構函式初始化候選人陣列。合約物件有2個方法:

1.返回候選人獲得的總票數

2.增加候選人的投票數。

注意:建構函式只被呼叫一次,當您部署合約到區塊鏈。不像在網路世界裡的每一個部署你的程式碼覆蓋舊的程式碼,部署後的程式碼在區塊鏈上是不變的。例如,如果你更新你的合約並且再次部署,舊合約仍然會在區塊鏈上, 它所儲存的資料不受影響,新的部署將建立一個新例項的合約。

下面是投票合約的程式碼:

pragma solidity ^0.4.18;
// We have to specify what version of compiler this code will compile with

contract Voting {
  /* mapping field below is equivalent to an associative array or hash.
  The key of the mapping is candidate name stored as type bytes32 and value is
  an unsigned integer to store the vote count
  */
  
  mapping (bytes32 => uint8) public votesReceived;
  
  /* Solidity doesn't let you pass in an array of strings in the constructor (yet).
  We will use an array of bytes32 instead to store the list of candidates
  */
  
  bytes32[] public candidateList;

  /* This is the constructor which will be called once when you
  deploy the contract to the blockchain. When we deploy the contract,
  we will pass an array of candidates who will be contesting in the election
  */
  function Voting(bytes32[] candidateNames) public {
    candidateList = candidateNames;
  }

  // This function returns the total votes a candidate has received so far
  function totalVotesFor(bytes32 candidate) view public returns (uint8) {
    require(validCandidate(candidate));
    return votesReceived[candidate];
  }

  // This function increments the vote count for the specified candidate. This
  // is equivalent to casting a vote
  function voteForCandidate(bytes32 candidate) public {
    require(validCandidate(candidate));
    votesReceived[candidate] += 1;
  }

  function validCandidate(bytes32 candidate) view public returns (bool) {
    for(uint i = 0; i < candidateList.length; i++) {
      if (candidateList[i] == candidate) {
        return true;
      }
    }
    return false;
  }
}

複製上面的程式碼,在hello_world_voting目錄下建立一個Voting.sol檔案。現在讓我們來編譯程式碼並將其部署到ganache的區塊鏈上.

為了編譯solidity程式碼,我們需要安裝名字為solc的npm模組

[email protected]:~/hello_world_voting$ npm install solc

我們將在node控制檯中使用這個庫來編譯我們的合約。在上一篇文章中我們提到,web3js是一個讓我們可以通過rpc訪問區塊鏈的庫。我們將使用該庫來部署我們的應用程式並與之互動。

首先,在命令列中斷執行`node`命令進入node控制檯,初始化solc和文字物件。下面的所有程式碼片段都需要在mode控制檯中鍵入

[email protected]:~/hello_world_voting$ node
> Web3 = require('web3')
> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

為了確保web3物件已經初始化、區塊鏈能夠訪問,讓我們試一下查詢區塊鏈上的所有賬戶。您應該看到如下的結果:

> web3.eth.accounts
['0x9c02f5c68e02390a3ab81f63341edc1ba5dbb39e',
'0x7d920be073e92a590dc47e4ccea2f28db3f218cc',
'0xf8a9c7c65c4d1c0c21b06c06ee5da80bd8f074a9',
'0x9d8ee8c3d4f8b1e08803da274bdaff80c2204fc6',
'0x26bb5d139aa7bdb1380af0e1e8f98147ef4c406a',
'0x622e557aad13c36459fac83240f25ae91882127c',
'0xbf8b1630d5640e272f33653e83092ce33d302fd2',
'0xe37a3157cb3081ea7a96ba9f9e942c72cf7ad87b',
'0x175dae81345f36775db285d368f0b1d49f61b2f8',
'0xc26bda5f3370bdd46e7c84bdb909aead4d8f35f3']

從voting.sol載入程式碼,儲存在一個字串變數中,然後開始編譯

> code = fs.readFileSync('Voting.sol').toString()
> solc = require('solc')
> compiledCode = solc.compile(code)

 當你的程式碼編譯成功並列印了合約物件的內容(在node控制檯中輸出的內容),有2個欄位很重要,需要理解它們:
compiledCode.contracts[‘:Voting’].bytecode: Voting.sol原始碼編譯後得到的位元組碼。這是將被部署到blockchain的程式碼。compiledCode.contracts[‘:Voting’].interface: 合約介面或模板(稱為ABI)告訴使用者合約含有哪些方法。您需要這些ABI的定義,因為將來你總是需要與合約互動的。更多ABI資訊請參考:這裡

現在我們來部署合約。你先建立一個合約物件(下面程式碼中的 VotingContract),用於在區塊鏈上部署和初始化智慧合約。

> abiDefinition = JSON.parse(compiledCode.contracts[':Voting'].interface)
> VotingContract = web3.eth.contract(abiDefinition)
> byteCode = compiledCode.contracts[':Voting'].bytecode
> deployedContract = VotingContract.new(['Rama','Nick','Jose'],{data: byteCode, from: web3.eth.accounts[0], gas: 4700000})
> deployedContract.address
> contractInstance = VotingContract.at(deployedContract.address)
VotingContract.new將智慧合約部署到區塊鏈。第一個引數是一個候選人陣列。讓我們看看第二個引數中的是什麼:

data:這是我們部署到鏈上的位元組碼。

from:blockchain要追蹤誰部署了合約。目前,我們只是挑選web3.eth.accounts中的第一個帳戶作為合約的擁有者。記住,在我們啟動測試鏈時web3.eth.accounts有10個ganache建立的賬號。在真是的鏈上,你不能使用任何賬號,你必須先獲取並解鎖賬號。建立賬號時會讓你設定密碼,用來保證你的這個帳戶的所有權。Ganache為了方便預設10個賬戶都解鎖了。

gas:與區塊鏈互動的手續費。這些錢是給那些把你的合約寫入區塊鏈等所有動作的礦工們的。通過‘gas’欄位,你設定想要付多少錢給礦工。你的賬戶中的ether(ETH)資產將用於購買gas。gas的價格是由網路設定的。

我們現在已經部署了合約並擁有了合約例項(上面的變數:contractinstance),可以用它來跟合約互動。在區塊鏈上的智慧合約數量成千上萬。那麼,怎麼去標識合約呢?答:deployedContract.address。當你想要跟合約互動時,你需要合約的部署地址和前面提到的 ABI定義。

3. 在nodejs控制檯中與智慧合約互動

> contractInstance.totalVotesFor.call('Rama')
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})
'0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53'
> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})
'0x02c054d238038d68b65d55770fabfca592a5cf6590229ab91bbe7cd72da46de9'
> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})
'0x3da069a09577514f2baaa11bc3015a16edf26aad28dffbcd126bde2e71f2b76f'
> contractInstance.totalVotesFor.call('Rama').toLocaleString()
'3'

在nodejs控制檯中嘗試以上命令你將看到選票計數在遞增。每次你投票給一個候選人,你得到一個交易ID:例如:“0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53。這個交易ID是發生交易的證明,在任何時間你都可以通過交易ID溯源。交易是不可篡改的,這也是想以太坊這樣的區塊鏈的一大優勢。在後續教程我們將利用這一特性來構建應用.

4. 連線區塊鏈並且可以投票的網頁

現在大部分的工作已經完成,我們現在要做的就是用HTML和js實現一個簡單頁面來展示候選人和呼叫投票命令。下面你可以找到HTML程式碼和js檔案。把它們都丟在hello_world_voting目錄並在瀏覽器中開啟index.html。

<!DOCTYPE html>
<html>
<head>
  <title>Hello World DApp</title>
  <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
  <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'>
</head>
<body class="container">
  <h1>A Simple Hello World Voting Application</h1>
  <div class="table-responsive">
    <table class="table table-bordered">
      <thead>
        <tr>
          <th>Candidate</th>
          <th>Votes</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Rama</td>
          <td id="candidate-1"></td>
        </tr>
        <tr>
          <td>Nick</td>
          <td id="candidate-2"></td>
        </tr>
        <tr>
          <td>Jose</td>
          <td id="candidate-3"></td>
        </tr>
      </tbody>
    </table>
  </div>
  <input type="text" id="candidate" />
  <a href="#" onclick="voteForCandidate()" class="btn btn-primary">Vote</a>
</body>
<script src="https://cdn.rawgit.com/ethereum/web3.js/develop/dist/web3.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<script src="./index.js"></script>
</html>
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
abi = JSON.parse('[{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"validCandidate","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"x","type":"bytes32"}],"name":"bytes32ToString","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"candidateList","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"contractOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"candidateNames","type":"bytes32[]"}],"payable":false,"type":"constructor"}]')
VotingContract = web3.eth.contract(abi);
// In your nodejs console, execute contractInstance.address to get the address at which the contract is deployed and change the line below to use your deployed address
contractInstance = VotingContract.at('0x2a9c1d265d06d47e8f7b00ffa987c9185aecf672');
candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"}

function voteForCandidate() {
  candidateName = $("#candidate").val();
  contractInstance.voteForCandidate(candidateName, {from: web3.eth.accounts[0]}, function() {
    let div_id = candidates[candidateName];
    $("#" + div_id).html(contractInstance.totalVotesFor.call(candidateName).toString());
  });
}

$(document).ready(function() {
  candidateNames = Object.keys(candidates);
  for (var i = 0; i < candidateNames.length; i++) {
    let name = candidateNames[i];
    let val = contractInstance.totalVotesFor.call(name).toString()
    $("#" + candidates[name]).html(val);
  }
});
如果您還記得,我們前面說過,我們需要ABI和合約地址來與智慧合約進行互動。你可以看到在index.js檔案中是如何使用的。

這是你在瀏覽器開啟index.html將看到的畫面


如果您能夠在文字框中輸入候選名稱並進行投票並看到選票計數在增加,那麼你的第一個應用成功了!祝賀你!
總結: 設定你的開發環境,編寫了一個簡單的合約,編譯並部署合約到blockchain,通過nodejs控制檯與合約互動, 通過網頁與合約互動。如果你還沒有開始的話,現在是你佩服自己的好時機:)

第2章中,我們將把這個合約部署到公共測試網路,以便全世界都能看到它併為候選人投票。我們也會變得越來越複雜和使用truffle開發框架(而不使用Nodejs控制檯管理全過程)。希望這個教程幫助你得到一個實際的想法如何開始在以太坊平臺開發DAPP。

如果你想要一個更具挑戰性的專案,我建立了一個教程:基於以太坊和IPFS建立一個去中心化的的eBay

相關推薦

DAPP開發例項: 投票系統

[教程最近更新於2018年1月]在我以前的文章中,我解釋了以太坊平臺和web 應用的差別。作為一個開發者,學習任何新技術的最好方法是潛入和嘗試構建應用。在這篇文章中,讓我們構建一個簡單的"Hello World!" 應用程式, 這是一個投票應用程式。該應用程式非常簡單,它所做

區塊鏈開發-DAPP開發實戰視訊課程-熊麗兵-專題視訊課程

區塊鏈全棧開發-以太坊DAPP開發實戰視訊課程—84人已學習 課程介紹         本門課從一個去中心化應用的概念講起,讓大家理解去中心化應用與傳統應用的不同,然後用一個個案例帶大家瞭解掌握中心

dApp開發教程(如何一步步構造一個式去中心化應用)(四)實現投票功能

一、更新智慧合約 增加投票功能後的智慧合約如下: pragma solidity ^0.4.2; contract Election { //候選者結構體 struct Candidate { uint id; string name; uint vot

DApp 開發入門實戰! 用Node.js和truffle框架搭建——區塊鏈投票系統

以太坊 區塊鏈 Node.js truffle DApp 第一節 概述 面向初學者,內容涵蓋以太坊開發相關的基本概念,並將手把手地教大家如何構建一個 基於以太坊的完整去中心化應用 —— 區塊鏈投票系統。 通過學習,你將掌握: 以太坊區塊鏈的基本知識 開發和部署以太坊合約所需的軟件

教程:學習Dapp開發

以太坊教程 以太坊 以太坊開發 智能合約 區塊鏈 dapp 一、區塊鏈 1. 分布式去中心化 比特幣設計的初衷就是要避免依賴中心化的機構,沒有發行機構,也不可能操縱發行數量。既然沒有中心化的信用機構,在電子貨幣運行的過程中,也勢必需要一種機制來認可運行在區

DApp開發:web3.js與智慧合約互動

前言 環境準備 ubuntu 14.0.4(16.0.4), 64位 還需要安裝以太坊相關的環境: nodejs truffle solidity testrpc 編寫智慧合約 首先在使用者目錄(home)下新建conference目錄(任意目錄都

DApp開發入門教程——Node.js和truffle框架打造區塊鏈投票系統

第一節 課程概述本課程面向初學者,內容涵蓋以太坊開發相關的基本概念,並將手把手地教大家如何構建一個 基於以太坊的完整去中心化應用 —— 區塊鏈投票系統。通過本課程的學習,你將掌握:以太坊區塊鏈的基本知識開發和部署以太坊合約所需的軟體環境使用高階語言(solidity)編寫以太

區塊鏈學習(3)--Dapp開發

c函數 ali cati 意思 定制 區塊鏈學習 網絡 開發 功能 DApp是Decentralized Application的縮寫,譯為:分散式的應用程序。App我們都知道,我們在智能手機上安裝的應用程序也就是App。而DApp比App多了一個‘D’,‘D’的意思是分散

【區塊鏈】DApp開發

機器環境 win10 nodev8.9.4 npm install -g truffle npm install -g ganache-cli Github地址 效果

Dapp開發全過程(solidity)

繼上篇用php70行程式碼獲取所有以太坊區塊鏈應用程式碼,獲取到以太坊dapp的solidity程式碼,除了用mythril工具掃描出安全問題,還是得深入分析程式碼邏輯。然而solidity語法有些不明白的地方,故藉著loomnetwork的cryptozombies遊戲 學

用Visual Studio和c#開發dapp

分享 chain dap 本地 當前 疑問 運行 course 啟動 最近區塊鏈技術引起了我的註意。我剛開始了解它,就看到了區塊鏈去中心化架構的巨大潛力,並且它能夠簡化各種現有繁瑣的流程,通過各種形式的合約。 作為一名.NET開發人員,我主要使用的是一些提供中心數據源的架構

Dapp專案-網頁錢包開發手冊

以太坊Dapp專案-網頁錢包開發手冊 修訂日期 姓名 郵箱 2018-10-10 brucefeng [email protected] 前言 在之前的一篇文章以太坊智慧合約專案-To

如何使用Meteor開發Dapp

本教程將向你展示如何設定Meteor應用程式以用作Ðapp,並可能回答幾個關於為什麼應該使用Meteor的問題。 常問問題 Meteor不是一個完整的堆疊框架,它是如何適應Ðapp開發的 是的,Meteor是一個完整的堆疊框架,它的主要改進是實時Web應用程式,但Meteor也是第一個框架(我知道),它完全支

DAPP[3]-×××-react特性與×××合約例項

×××例項 在之前的課程中,我們介紹瞭如何編譯與部署合約。現在,我們假設已經把合約部署到了ropsten的網路之上。得到了地址。現在,我們需要通過合約的ABI介面與地址來構建×××合約的例項。 新建檔案:src/lottery.js: 1234567 import w

不同步節點線上使用Remix開發Dapp及solidity學習入門 ( 一 ):智慧合約HelloWorld

有問題可以點選–>加群互相學習 本人本來想自己寫公鏈,結果發現任重道遠; 遂,開始寫Dapp,順便寫的時候搞個教程吧。。。 通過系列教程學習將會: 1.基本使用solidity 語言開發智慧合約 2.知道怎麼發自己的以太坊的token 3.看見前方區

用Solidity語言通過錢包開發hello world示例

以太坊 以太坊錢包開發 ethereum truffle solidity 以太坊入門 區塊鏈入門 區塊鏈 區塊鏈開發 blockchain 使用以太坊錢包開發實現經典的HelloWord智能合約類。本文中,我們將看到如何編寫簡單的合約並將其部署到區塊鏈上

Dapp項目-拍賣網站-智能合約編寫測試

添加 unbox 基於 文檔 找不到 文件的 align 保存 returns 修訂日期 姓名 郵箱 2018-10-18 brucefeng [email protected] 前言 寫這篇文章的初衷其實很簡單,在MyEtherWallet上

2018年8月DApp資料分析報告

近日,鏈塔資料BlockData釋出了《2018年8月以太坊DApp資料分析報告》,報告顯示,以太坊上的DApp數量多達775個,形成了一個較為完善的開發生態圈,累計交易筆數多達3.0036603億,累計交易金額超過59億ETH。 1 以太坊DApp總數達775個 以太坊是

錢包開發系列1 - 建立錢包賬號

以太坊去中心化網頁錢包開發系列,將從零開始開發出一個可以實際使用的錢包,本系列文章是理論與實戰相結合,一共有四篇:建立錢包賬號、賬號Keystore檔案匯入匯出、展示錢包資訊及發起簽名交易、傳送Token(代幣),這是第一篇,主要介紹錢包將實現哪些功能及怎麼建立錢包賬號,本錢包是基於ethers.js 進行開

錢包開發系列2 - 賬號Keystore檔案匯入匯出

以太坊去中心化網頁錢包開發系列,將從零開始開發出一個可以實際使用的錢包,本系列文章是理論與實戰相結合,一共有四篇:建立錢包賬號、賬號Keystore檔案匯入匯出、展示錢包資訊及發起簽名交易、傳送Token(代幣),這是第二篇,主要介紹錢包賬號匯出與匯入,將對Keystore檔案的生成的原理進行介紹。 如何匯