友情提示:本文讲解比较快,若看不懂,可以先看看我的另一篇文章,链接给出 开发一个简单的智能合约 ,之后再来看本篇文章,会完全掌握开发步骤。
一、环境搭建
- 安装nodejs,npm会同时安装,下载地址:Nodejs
- 安装truffle框架,这是一个流行的以太坊开发框架,内置的智能合约编译,连接,部署等功能,安装命令:
npm install -g truffle
- 安装以太坊网络模拟软件Ganachi,安装命令:
npm install -g ganachi-cli
- 安装web3.js,web3.js能够帮助我们与智能合约交互。
npm install web3
二、开发步骤
- 随便在一个目录下创建一个空目录,命名为Voting,在控制台上进入这个文件,之后再输入turffle unbox webpack
- 这样会初始化这个文件
- 编写智能合约代码,智能合约是用Solidity语言编写的,想要更多了解Solidity,可以去阅读我的其它文章,有很完整的Solidity学习的知识点。在这里我们之间给出智能合约的代码:
pragma solidity ^0.4.18; this code will use
contract Voting {
// 储存候选人及其所得票数
mapping (bytes32 => uint8) public votesReceived;
// 储存候选人的名字
bytes32[] public candidateList;
// 构造方法,加入候选人
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;
}
}
- 代码逻辑很清晰,要看懂一个没什么问题。
将智能合约的代码放在contracts目录下,这是储存合约代码的目录,命名为Voting.sol
- app目录下是储存前端代码的地方,在这里给出前端代码,首先是html代码,修改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代码:
// 导入css
import "../stylesheets/app.css";
// 导入需要的包
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract'
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().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");
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;
}
- 修改部署方式,在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 compile编译,truffle migrate部署智能合约,npm run dev运行
-
设置metamask的钱包网络为8545(和Ganache一致):
问题:什么是metamask?
- metamask是一个谷歌浏览器的插件,是轻量级的以太坊钱包,支持正式的以太坊网络,也可以用于测试合约代码,它有以下简单的功能:
- 创建新账户
- 发币和收币
- 购买正式币和测试币(根据不同的网络,获取方式不一致)
- 更改地址所在的网络
- 调用合约功能测试
- 打开index.html,就能直接运行了:
- 这个投票的智能合约的所有成品我会发出来,关注我就能下载了:
下载链接:DApp投票系统