mkdir simple_voting_dapp
cd simple_voting_dapp
npm init
npm install ganache-cli web3 solc
node_modules/.bin/ganache-cli
为了便于测试,ganache 默认会创建 10 个账户,每个账户有 100 个以太。你需要用其中一个账户创建交易,发送、接收以太。 当然,你也可以安装 GUI 版本的图形化 ganache 而不是命令行版本,在这里下 载 GUI 版本:http://truffleframework.com/ganache/
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Voting{
bytes32[] public candidateList;
mapping(bytes32 => uint8)public voteReceived;
constructor(bytes32[] memory candidateNames){
candidateList = candidateNames;
}
function totalVotesFor(bytes32 candidateName) public view returns(uint8){
require(validCandidate(candidateName));
return voteReceived[candidateName];
}
function voteForCandidate(bytes32 candidateName) public {
require(validCandidate(candidateName));
voteReceived[candidateName] += 1;
}
function validCandidate(bytes32 candidateName) public view returns(bool){
for(uint8 i = 0; i < candidateList.length; i++){
if(candidateList[i] == candidateName){
return true;
}
}
return false;
}
}
将Voting.sol合约存储到simple_voting_dapp目录下的contract文件夹中
注意:当你把合约部署到区块链的时候,就会调用构造函数,并只调用一次。与 web 世界里每次部署代码都会覆盖旧代码不同,在区块链上部署的合约是不可 改变的,也就是说,如果你更新合约并再次部署,旧的合约仍然会在区块链上存 在,并且数据仍在。新的部署将会创建合约的一个新的实例
重新开启一个bash,并进入到simple_voting_dapp/contract中,输入node,在node控制台中操作
> var Web3 = require('web3');
> var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); //ganache-cli 启动的默认连接是 http://localhost:8545
> web3.eth.net.isListening().then(console.log); //检测连接是否成功
Promise {
,
[Symbol(async_id_symbol)]: 945,
[Symbol(trigger_async_id_symbol)]: 941,
[Symbol(destroyed)]: { destroyed: false }
}
> true
> var sourceCode = fs.readFileSync('Voting.sol').toString()
> var solc = require('solc');
> var input = {
language: 'Solidity',
sources: {
'Voting.sol': {
content: sourceCode
}
},
settings: {
outputSelection: {
'*': {
'*': ['*']
}
}
}
};
> var output = JSON.parse(solc.compile(JSON.stringify(input))); //编译
> var abi = output.contracts['Voting.sol'].Voting['abi'];
> var byteCode = output.contracts['Voting.sol'].Voting['evm']['bytecode'].object;
> var candidatesHex =[web3.utils.toHex('tom'),web3.utils.toHex('jack'),web3.utils.toHex('mary')]; //byte32[] 为Voting.sol构造函数传入参数做准备
> var account;
> web3.eth.getAccounts().then(function(res){account=res[0]});
> var VotingContract = new web3.eth.Contract(abi);
> var contractInstance = VotingContract.deploy({data:byteCode,arguments:[candidatesHex]}).send({from:account,gas: 1500000,gasPrice: '30000000000000'}).then(function(newContractInstance){
console.log(newContractInstance.options.address)
});
> 0xC65cD0Ada5fD85f5D200071C98751B474B017680 //获取新合约实例的合约地址
> VotingContract.options.address = '0xC65cD0Ada5fD85f5D200071C98751B474B017680' ; //设置合约地址 不然无法调用合约里面的方法
> VotingContract.methods.voteForCandidate(web3.utils.toHex('tom')).send({from:account}).then(console.log); //调用Voting.sol中的voteForCandidate方法为tom投票
> VotingContract.methods.totalVotesFor(web3.utils.toHex('tom')).call({from:account}).then(console.log); //查询tom的投票数
Promise {
,
[Symbol(async_id_symbol)]: 6738,
[Symbol(trigger_async_id_symbol)]: 6734,
[Symbol(destroyed)]: { destroyed: false }
}
> 1
Voting DApp
A Simple Voting Application
Candidate
Votes
tom
jack
mary
Vote
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var abi = JSON.parse('[{"inputs":[{"internalType":"bytes32[]","name":"candidateNames","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"candidateList","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"validCandidate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"voteReceived","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"}]');
var contractAddr = '0x1E3B8A53A32c1C17d16b892c155fAB298e57BBE6';
var contractInstance = new web3.eth.Contract(abi,contractAddr);
var candidates = {'tom':'candidate-1','jack':'candidate-2','mary':'candidate-3'};
var account;
web3.eth.getAccounts().then(function(accounts){
account = accounts[0];
});
function voteForCandidate(){
let candidateName = $("#candidate").val();
let candidateNameHex = web3.utils.toHex(candidateName);
contractInstance.methods.voteForCandidate(candidateNameHex).send({from:account}).then(function(receipt){
$("#msg").html("已投给: "+ candidateName + "
交易哈希: " + receipt.transactionHash + "
投票人地址: " + account);
contractInstance.methods.totalVotesFor(candidateNameHex).call(function(err,res){
$('#'+candidates[candidateName]).html(res);
});
});
}
$(document).ready(function(){
var candidateList = Object.keys(candidates);
for (let i = 0; i < candidateList.length; i++){
let name = candidateList[i];
let nameHex = web3.utils.toHex(name);
let count = contractInstance.methods.totalVotesFor(nameHex).call(function(err,res){
$("#"+candidates[name]).html(res);
});
}
})
如果一切顺利的话,你应该能够在文本框中输入候选者姓名,然后投票数应 该加 1 。 注意:由于网络原因,web3.js 可能无法获取,可自行下载到本地导入。 如果你可以看到页面,为候选者投票,然后看到投票数增加,那就已经成功 创建了第一个合约,恭喜!所有投票都会保存到区块链上,并且是不可改变的。 任何人都可以独立验证每个候选者获得了多少投票。当然,我们所有的事情都是在一个模拟的区块链上(ganache)完成。
下面是你到目前为止已经完成的事情:
1. 通过安装 node, npm 和 ganache,你已经配置好了开发环境。
2. 你编码了一个简单的投票合约,编译并部署到区块链上。
3. 你通过 nodejs 控制台与网页与合约进行了交互