【區塊鏈】使用truffle開發一個投票DApp
阿新 • • 發佈:2019-02-11
機器環境
- win10
- nodev8.9.4
初始化專案
npm install -g truffle
npm install -g ganache-cli
truffle unbox webpack
編寫投票的智慧合約–Voting.sol
- Voting.sol
pragma solidity ^0.4.18; //We have to specify what version of compiler this code will use
contract Voting {
/* mapping is equivalent to an associate array or hash
The key of the mapping is candidate name stored as type bytes32 and value is
an unsigned integer which used to store the vote count
*/
mapping (bytes32 => uint8) public votesReceived;
/* Solidity doesn't let you create an array of strings yet. We will use an array of bytes32 instead to store
the list of candidates
*/
bytes32[] public candidateList;
// Initialize all the contestants
function Voting(bytes32[] candidateNames) public {
candidateList = candidateNames;
}
function totalVotesFor(bytes32 candidate) view public returns (uint8) {
require(validCandidate(candidate));
return votesReceived[candidate];
}
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;
}
}
編寫前端–app目錄
- 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 id="address"></div>
<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 id="msg"></div>
</div>
<input type="text" id="candidate" />
<a href="#" onclick="voteForCandidate()" class="btn btn-primary">Vote</a>
</body>
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<script src="app.js"></script>
</html>
- javascript/app.js
// Import the page's CSS. Webpack will know what to do with it.
import "../stylesheets/app.css";
// Import libraries we need.
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract'
/*
* When you compile and deploy your Voting contract,
* truffle stores the abi and deployed address in a json
* file in the build directory. We will use this information
* to setup a Voting abstraction. We will use this abstraction
* later to create an instance of the Voting contract.
* Compare this against the index.js from our previous tutorial to see the difference
* https://gist.github.com/maheshmurthy/f6e96d6b3fff4cd4fa7f892de8a1a1b4#file-index-js
*/
import voting_artifacts from '../../build/contracts/Voting.json'
var Voting = contract(voting_artifacts);
let candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"}
window.voteForCandidate = function(candidate) {
let candidateName = $("#candidate").val();
try {
$("#msg").html("Vote has been submitted. The vote count will increment as soon as the vote is recorded on the blockchain. Please wait.")
$("#candidate").val("");
/* Voting.deployed() returns an instance of the contract. Every call
* in Truffle returns a promise which is why we have used then()
* everywhere we have a transaction call
*/
Voting.deployed().then(function(contractInstance) {
contractInstance.voteForCandidate(candidateName, {gas: 140000, from: web3.eth.accounts[0]}).then(function() {
let div_id = candidates[candidateName];
return contractInstance.totalVotesFor.call(candidateName).then(function(v) {
$("#" + div_id).html(v.toString());
$("#msg").html("");
});
});
});
} catch (err) {
console.log(err);
}
}
$( document ).ready(function() {
if (typeof web3 !== 'undefined') {
console.warn("Using web3 detected from external source like Metamask")
// Use Mist/MetaMask's provider
window.web3 = new Web3(web3.currentProvider);
} else {
console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
Voting.setProvider(web3.currentProvider);
let candidateNames = Object.keys(candidates);
for (var i = 0; i < candidateNames.length; i++) {
let name = candidateNames[i];
Voting.deployed().then(function(contractInstance) {
contractInstance.totalVotesFor.call(name).then(function(v) {
$("#" + candidates[name]).html(v.toString());
});
})
}
});
- stylesheets/app.css
body {
margin-left: 25%;
margin-right: 25%;
margin-top: 10%;
font-family: "Open Sans", sans-serif;
}
label {
display: inline-block;
width: 100px;
}
input {
width: 500px;
padding: 5px;
font-size: 16px;
}
button {
font-size: 16px;
padding: 5px;
}
h1, h2 {
display: inline-block;
vertical-align: middle;
margin-top: 0px;
margin-bottom: 10px;
}
h2 {
color: #AAA;
font-size: 32px;
}
h3 {
font-weight: normal;
color: #AAA;
font-size: 24px;
}
.black {
color: black;
}
#balance {
color: black;
}
.hint {
color: #666;
}
修改migrate檔案
- migrations/2_deploy_contract.js
var Voting = artifacts.require("./Voting.sol");
module.exports = function(deployer) {
deployer.deploy(Voting, ['Rama', 'Nick', 'Jose']);
};
配置truffle.js
// Allows us to use ES6 in our migrations and tests.
require('babel-register')
module.exports = {
networks: {
development: {
host: 'localhost',
port: 8545,
network_id: '*',
gas: 6600000
}
}
}
啟動專案
- 開一個終端執行
ganache-cli
- 開另一個終端執行:
truffle migrate
,npm run dev
- 設定metamask錢包的網路為
localhost:8545
並匯入ganache-cli的第一個private-key進metamask