主页 > imtoken钱包苹果版价值 > 以太坊区块链使用NodeJs和Web3开发投票DApp
以太坊区块链使用NodeJs和Web3开发投票DApp
为什么80%的码农不能成为架构师? >>>
1、开发完成后,页面预览如下
用户可以查看当前参与投票的候选人名单和各自当前的得票数,选择其中一位进行投票,被投票者的票数将相应增加。
2. 开发准备 3. 编写智能合约
创建一个工作目录Voting-Node,在该目录下新建一个Voting.sol智能合约文件,并在Remix中编辑该文件。 编辑后的文件内容如下:
pragma solidity ^0.4.18;
contract Voting {
mapping (bytes32 => uint8) public votesReceived;
bytes32[] public candidateList;
constructor (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;
}
}
4. 编译、部署、测试智能合约
在Voting-Node目录下打开命令行,输入node命令进入REPL环境,一步步执行编译部署命令。 但是这种方法比较麻烦以太坊区块浏览器api,不方便维护和修改。 直接写在Voting-Node目录下。 一个编译、部署、测试的depoy.js文件,可以直接在Nodejs中运行查看结果。 内容如下:
//引入web3模块
let Web3 = require('web3');
//初始化 web3
let web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:7545"));
//输出初始化结果
console.log('Initialization web3 complete,the first account is '+ web3.eth.accounts[0]);
let fs = require('fs');
let code = fs.readFileSync('Voting.sol').toString();
let solc = require('solc');
//编译合约为ABI文件
let compiledCode = solc.compile(code);
console.log('Compile Voting.sol complete');
//部署合约至区块链节点
let abiDefinition = JSON.parse(compiledCode.contracts[':Voting'].interface);
//写入ABI文件至本地文件目录
fs.writeFile('Voting.json',JSON.stringify(abiDefinition), {}, function(err) {
console.log('write ABI file [Voting.json] complete . ');
});
let VotingContract = web3.eth.contract(abiDefinition);
let byteCode = compiledCode.contracts[':Voting'].bytecode;
//调用VotingContract对象的new()方法来将投票合约部署到区块链。new()方法参数列表应当与合约的 构造函数要求相一致。对于投票合约而言,new()方法的第一个参数是候选人名单。
let deployedContract = VotingContract.new(['Rama','Nick','Jose'],{data: byteCode, from: web3.eth.accounts[0], gas: 4700000});
//输出合约 地址,如果此处没有返回地址,可以在Ganache日志中查看到
console.log('deploy complete,deploy address is '+ deployedContract.address);
//let contractInstance = VotingContract.at(deployedContract.address);
let contractInstance = VotingContract.at('0x99bdfb1f4c5d0c227d6cd98cf7a254bfc27c35cc');
//测试合约调用
contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]});
contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]});
contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]});
contractInstance.voteForCandidate('Nick', {from: web3.eth.accounts[0]});
contractInstance.voteForCandidate('Jose', {from: web3.eth.accounts[0]});
contractInstance.voteForCandidate('Jose', {from: web3.eth.accounts[0]});
console.log("--------------finish----------------");
let RamaVote=contractInstance.totalVotesFor.call('Rama');
let NickVote=contractInstance.totalVotesFor.call('Nick');
let JoseVote=contractInstance.totalVotesFor.call('Jose');
console.log("Rama's vote is "+RamaVote);
console.log("Nick's vote is "+NickVote);
console.log("Jose's vote is "+JoseVote);
执行结果如下:
PS C:\Workspace\Ruoli-Code\Eth-Node> node .\deploy.js
Initialization web3 complete,the first account is 0xa0b4360847bfe6c45f78103e97678dfa79aa9975
Compile Voting.sol complete
deploy complete,deploy address is undefined
--------------finish----------------
Rama's vote is 3
Nick's vote is 1
Jose's vote is 2
write ABI file complete .
最终结果如预期,说明测试调用成功。
如果部署出现问题:TypeError: web3.eth.contract is not a function
需要安装web3 0.19版本,命令如下:
npm install web3@^0.19.0 --保存
安装完成后,问题得到解决。
5.网页交互
完成以上编写、编译、部署、测试等步骤后,添加网页交互就非常简单了。
在Voting-Node目录下打开命令行,执行初始化命令,如下:
npm init -y
该目录下会生成一个package.json文件,修改该文件内容如下:
{
"name": "Voting-Node",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"solc": "^0.4.23",
"web3": "^0.19.0"
},
"devDependencies": {},
"scripts": {
"dev": "node index.js"
},
"keywords": [],
"author": "Ruoli",
"license": "ISC"
}
在该目录下新建index.js、index.html、app.js文件以太坊区块浏览器api,如下图。
index.js的内容如下:
var http = require('http');
var fs = require('fs');
var url = require('url');
// 创建服务器
http.createServer( function (request, response) {
// 解析请求,包括文件名
var pathname = url.parse(request.url).pathname;
// 输出请求的文件名
console.log("Request for " + pathname + " received.");
// 从文件系统中读取请求的文件内容
fs.readFile(pathname.substr(1), function (err, data) {
if (err) {
console.log(err);
// HTTP 状态码: 404 : NOT FOUND
// Content Type: text/plain
response.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 状态码: 200 : OK
// Content Type: text/plain
response.writeHead(200, {'Content-Type': 'text/html'});
// 响应文件内容
response.write(data.toString());
}
// 发送响应数据
response.end();
});
}).listen(3000);
// 控制台会输出以下信息
console.log('Server running at http://127.0.0.1:3000/index.html');
index.html的内容如下:
Voting DApp
简易投票 DApp
app.js的内容如下:
注意:这里需要导入之前保存的ABI文件的内容。
let web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545"));
//导入合约的ABI文件
let abi = JSON.parse('[{"constant":true,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"validCandidate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"candidateList","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"candidateNames","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]')
let VotingContract = web3.eth.contract(abi);
let contractInstance = VotingContract.at('0x99bdfb1f4c5d0c227d6cd98cf7a254bfc27c35cc');
let candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"}
$(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);
}
//初始化事件
$(".vote-btn").click(function(){
//获取投票人名称
let voteName=$(this).prev().prev().text();
contractInstance.voteForCandidate(voteName, {from: web3.eth.accounts[0]}, function() {
let div_id = candidates[voteName];
let vote_num=contractInstance.totalVotesFor.call(voteName).toString();
$("#"+div_id).fadeOut(400);
$("#"+div_id).html(vote_num).fadeIn(800);
});
});
});
至此所有的开发都已经完成。
6.网页访问及测试
执行以下命令启动服务:
PS C:\Workspace\Ruoli-Code\Voting-Node> npm run dev
> Voting-Node@1.0.0 dev C:\Workspace\Ruoli-Code\Voting-Node
> node index.js
Server running at http://127.0.0.1:3000/index.html
执行完成后,在浏览器中访问::3000/index.html
可以看到初始界面,至此所有开发完成。