参考自(3条消息) 区块链投票应用:使用solidity+truffle+metamsk开发Dapp应用_一袋芋头的博客-CSDN博客下载了项目示例webpack之后
我们需要将里面的其他合约都删除,也可以直接删除这两个文件夹里的内容
然后就可以开始正片了(当然,你得先前就安装好环境)
开启ganache私链,
为了后续实验方便,这里我们通过指定数据存放目录来确保账号等数据保持不变,采用如下命令来启动ganache-cli私链:
#ganache-cli -db /root/MyGanacheData
然后我们需要去创建合约,编译部署至该私链上
首先在项目的合约目录,也就是/contract目录下创建sol文件
$ touch Voting.sol
$ gedit Voting.sol
// SPDX-License-Identifier: SimPL-2.0
pragma solidity >=0.4.18 <0.8.0;
contract Voting{
bytes32[] public candodateList;
mapping(bytes32 => uint8) public votesReceived;
constructor(bytes32[] memory _candidateListName) public {
candodateList = _candidateListName;
}
function validateCan(bytes32 _candidate) internal view returns(bool) {
for(uint8 i = 0; i< candodateList.length; i++) {
if(candodateList[i] == _candidate)
return true;
}
return false;
}
function voteForCan(bytes32 _candidate) public {
votesReceived[_candidate] += 1;
}
function totalVotes(bytes32 _candidate)public view returns(uint8) {
return votesReceived[_candidate];
}
}
然后我们进行编译,truffle compile,编译成功说明我们的.sol合约文件没有啥语法错误,并无卵用
然后基于这个sol文件来生成迁移文件 truffle create migration deploy_Voting,命令执行后会在migrations下生成迁移文件
前面一串数字是truffle部署时所用的ID,这里没有用,不用管,继续,我们需要更改这个js,需要传入参数
// const ConvertLib = artifacts.require("ConvertLib");
// const MetaCoin = artifacts.require("MetaCoin");
const Voting = artifacts.require("Voting");
module.exports = function(deployer) {
// deployer.deploy(ConvertLib);
// deployer.link(ConvertLib, MetaCoin);
// deployer.deploy(MetaCoin);
deployer.deploy(Voting, [
"0x4100000000000000000000000000000000000000000000000000000000000000",
"0x4200000000000000000000000000000000000000000000000000000000000000",
"0x4300000000000000000000000000000000000000000000000000000000000000"
]);
//注意Voting部署时有参数
};
修改网络方式,修改truffle-conflig.js
module.exports = {
networks: {
development: {
host: 'localhost',
port: 8545,
network_id: '*' // Match any network id
}
}
}
下面我们就可以部署合约了,truffle migrate
这是合约部署 的详细情况
这是ganache私链的部署情况,因为一次部署就相当于是一次交易,所以会生成区块,会有交易哈希值产生,到此合约已经部署完毕,但是我们不好使用这个合约,所以我们需要写前端来方便我们使用它
在app/src/目录下写index.html和index.js文件
index.html
<!DOCTYPE html>
<html>
<head>
<title>Voting</title>
</head>
<style>
input {
display: block;
margin-bottom: 12px;
}
</style>
<body>
<h1>Voting App</h1>
<p>Alice : <strong id="alice">loading...</strong> tickets</p>
<p>Bob : <strong id="bob">loading...</strong> tickets</p>
<label>VotoFor :</label>
<input type="text" id="candidate" />
<button onclick="App.voteForCan()">vote</button>
<script src="index.js"></script>
</body>
</html>
index.js
import Web3 from "web3";
//导入Voting.json
import votingArtifact from "../../build/contracts/Voting.json";
//定义两个值,方便传参
const aInBytes32 = "0x4100000000000000000000000000000000000000000000000000000000000000";
const bInBytes32 = "0x4200000000000000000000000000000000000000000000000000000000000000";
const cInBytes32 = "0x4300000000000000000000000000000000000000000000000000000000000000";
//定义了app
const App = {
web3: null,
account: null,
voting: null, //定义一个voting的实例
//启动时需要的
start: async function() {
const { web3 } = this;
try {
// get contract instance
const networkId = await web3.eth.net.getId();
//要改成自己的网络
const deployedNetwork = votingArtifact.networks[networkId];
this.voting = new web3.eth.Contract(
votingArtifact.abi,
deployedNetwork.address,
);
// get accounts
const accounts = await web3.eth.getAccounts();
this.account = accounts[0];
//先刷新一下界面
this.ready();
} catch (error) {
console.error("Could not connect to contract or chain.");
}
},
//start函数后还应该有哪些函数呢
//1.refresh展示最新票数
refresh: async function(id, nameInBytes32) {
const { totalVotes } = this.voting.methods;
//从区块链拿到票数
const tickets = await totalVotes(nameInBytes32).call();
//写到页面上去,并更新最新票数
const element = document.getElementById(id);
element.innerHTML = tickets.toString();
},
//2.ready:当网页好的时候要把票数读出来
ready: async function() {
try {
this.refresh("alice", aInBytes32);
this.refresh("bob", bInBytes32);
this.refresh("clilly", cInBytes32);
} catch (err) {
console.log(err);
}
},
//3.votingfor函数(中间需要展示票数的函数)
voteForCan: async function() {
try {
//先获取到合约的方法
const { voteForCan } = this.voting.methods;
//再获取候选者名字:来自输入框
const candidateName = document.getElementById("candidate").value;
//拿到票数后对应加一
if (candidateName == "Alice") {
await voteForCan(aInBytes32).send({ from: this.account });
this.refresh("alice", aInBytes32);
} else {
if (candidateName == "Bob") {
await voteForCan(bInBytes32).send({ from: this.account });
this.refresh("bob", bInBytes32);
}
else{
if (candidateName == "Cilly") {
await voteForCan(cInBytes32).send({ from: this.account });
this.refresh("cilly", cInBytes32);
}
}
}
} catch (err) {
//如果有err就打印一下
console.log(err);
}
}
};
window.App = App;
window.addEventListener("load", function() {
if (window.ethereum) {
// use MetaMask's provider
App.web3 = new Web3(window.ethereum);
window.ethereum.enable(); // get permission to access accounts
} else {
console.warn(
"No web3 detected. Falling back to http://127.0.0.1:8545. You should remove this fallback when you deploy live",
);
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
App.web3 = new Web3(
new Web3.providers.HttpProvider("http://127.0.0.1:8545"),
);
}
App.start();
});
然后运行
必须在App目录运行
npm run dev
额,报错了error:0308010C:digital envelope routines::unsupported,什么依托答辩,没办法,只好上网搜解答
export NODE_OPTIONS=--openssl-legacy-provider
临时解决问题,编译成功了,很牛逼,但是不知道为什么,用了再说
然后去自己浏览器打开http://localhost:8080/
可以开始使用,每次投票都会生成一个区块,保证了数据的不容篡改