Prefacio
Ayer publiqué un artículo resumido sobre mi estudio sobre blockchain y DAPP. Mi amigo me hizo algunos comentarios: Los contratos no tienen identidad y una persona puede votar muchas veces siempre que esté dispuesta a gastar gasolina. Esta es una laguna jurídica en el robo de votos. .
Pensándolo bien, me siento un poco vago, así que agregaré otro artículo hoy. Solo dos puntos: 1. Agregar autenticación de identidad para evitar votaciones repetidas; 2. Dar una introducción a web3.0 y su implementación y llamada. De hecho, la función es la misma que ayer pero el método será diferente.
Optimización del contrato de Solidity
pragma solidity ^0.4.0;
contract Voting{
//投票列表
bytes32[] public candidateList;
//对应的票数
mapping(bytes32=>uint8) public votesReceived;
//投票人列表
mapping(address=>People) public Peoples;
//投票人信息及投票时间
struct People{
address account;
uint voingtime;
}
//构造投票列表
constructor(bytes32[] memory candidateListName) public{
candidateList = candidateListName;
}
//获取投票人账号
function getAccountAddress() public returns(uint32)
{
People memory people = Peoples[msg.sender];
return uint32(people.account);
}
//设置已投票地址
function setAccountAddress() private
{
People memory people = People(msg.sender,now);
Peoples[msg.sender] = people;
}
//判断投票信息是否在待投票列表
function valiCandidate(bytes32 candidateName) internal view returns(bool)
{
for(uint8 i=0;i<candidateList.length;i++)
{
if (candidateName==candidateList[i])
{
return true;
}
}
return false;
}
//对投票人增加投票
function voteForCandidate(bytes32 candidateName) public{
require(valiCandidate(candidateName));
//判断是否已投票 0 为未投票
require(getAccountAddress()==0);
setAccountAddress();
votesReceived[candidateName] = votesReceived[candidateName] + 1;
}
function totalVotesFor(bytes32 candidateName) view public returns(uint8){
require(valiCandidate(candidateName));
//返回票数
return votesReceived[candidateName];
}
}
El código está adjunto, puede leer los comentarios para obtener más detalles; de hecho, es muy sencillo usar el mapeo para registrar la dirección del votante (registrado con estructura)
A continuación, hablemos sobre cómo inicializar web3.0 simple.
mkdir web3.0
cd web3.0
npm init
Presione Enter hasta el final para inicializar un proyecto de nodo js
npm install web3 -save
Implementar el contrato a través del nodo js web3
Cree un nuevo implementar.js en el directorio web3.0
var Web3 = require('web3');
// 链接本地 ganache 记得启动
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
Abra remix y podremos ver que el archivo de implementación de web3 es tan simple como
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
var candidateListName = ["0x4100000000000000000000000000000000000000000000000000000000000000","0x4200000000000000000000000000000000000000000000000000000000000000"] ;
var votingContract = new web3.eth.Contract([{"constant":false,"inputs":[],"name":"getAccountAddress","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"candidateName","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"Peoples","outputs":[{"name":"account","type":"address"},{"name":"voingtime","type":"uint256"}],"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":"candidateName","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"candidateListName","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
var voting = votingContract.deploy({
data: '0x608060405234801561001057600080fd5b506040516106d93803806106d9833981018060405281019080805182019291905050508060009080519060200190610049929190610050565b50506100c8565b828054828255906000526020600020908101928215610092579160200282015b82811115610091578251829060001916905591602001919060010190610070565b5b50905061009f91906100a3565b5090565b6100c591905b808211156100c15760008160009055506001016100a9565b5090565b90565b610602806100d76000396000f300608060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630e2562d91461007d5780632f265cf7146100b457806355f503d5146100ff5780637021939f14610189578063b13c744b146101d4578063cc9ab2671461021d575b600080fd5b34801561008957600080fd5b5061009261024e565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b3480156100c057600080fd5b506100e3600480360381019080803560001916906020019092919050505061030f565b604051808260ff1660ff16815260200191505060405180910390f35b34801561010b57600080fd5b50610140600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610355565b604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390f35b34801561019557600080fd5b506101b86004803603810190808035600019169060200190929190505050610399565b604051808260ff1660ff16815260200191505060405180910390f35b3480156101e057600080fd5b506101ff600480360381019080803590602001909291905050506103b9565b60405180826000191660001916815260200191505060405180910390f35b34801561022957600080fd5b5061024c60048036038101908080356000191690602001909291905050506103dc565b005b60006102586105a6565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040805190810160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820154815250509050806000015191505090565b600061031a82610475565b151561032557600080fd5b60016000836000191660001916815260200190815260200160002060009054906101000a900460ff169050919050565b60026020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010154905082565b60016020528060005260406000206000915054906101000a900460ff1681565b6000818154811015156103c857fe5b906000526020600020016000915090505481565b6103e581610475565b15156103f057600080fd5b60006103fa61024e565b63ffffffff1614151561040c57600080fd5b6104146104da565b6001806000836000191660001916815260200190815260200160002060009054906101000a900460ff160160016000836000191660001916815260200190815260200160002060006101000a81548160ff021916908360ff16021790555050565b600080600090505b6000805490508160ff1610156104cf5760008160ff1681548110151561049f57fe5b906000526020600020015460001916836000191614156104c257600191506104d4565b808060010191505061047d565b600091505b50919050565b6104e26105a6565b60408051908101604052803373ffffffffffffffffffffffffffffffffffffffff16815260200142815250905080600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015590505050565b6040805190810160405280600073ffffffffffffffffffffffffffffffffffffffff1681526020016000815250905600a165627a7a72305820230b9460ef4d9c98dc2ad603c8d560d384d66df56e19cce527bbdda332014b4e0029',
arguments: [
candidateListName,
]
}).send({
//from: web3.eth.accounts[0],
from: '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0',
gas: '4700000'
}, function (e, contract){
console.log(e, contract);
if (typeof contract.address !== 'undefined') {
console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
}
}).then(function(contract){
console.log('Contract ! address: ', contract.options.address);
});
de: web3.eth.accounts[0], recuerde cambiarlo a su cuenta ganache y ahora implementar el contrato a través del nodo
node deploy.js
En este momento puede ver la dirección del contrato (¡Dirección del contrato!: 0x32Cf1f3a98aeAF57b88b3740875D19912A522c1A)
El contrato está desplegado, ¿cómo llamarlo? Todavía usamos el proyecto de ayer para llamarlo, por supuesto, se puede llamar en una cadena virtual local.
El siguiente es el código central del contrato de convocatoria del proyecto APP de ayer, lo escribí directamente en los comentarios:
//node js 部署 我们没有这个json文件注释掉
// import votingArtifact from "../../build/contracts/Voting.json";
start: async function() {
const { web3 } = this;
try {
// get contract instance
const networkId = await web3.eth.net.getId();
//这里既然前面注释了,这里应该也没用了
const deployedNetwork = votingArtifact.networks[networkId];
//调用合同部分votingArtifact.abi这个哪里找呢?
//deployedNetwork.address 这个不就是合同地址吗
this.voting = new web3.eth.Contract(
votingArtifact.abi,
//deployedNetwork.address,
'0x32Cf1f3a98aeAF57b88b3740875D19912A522c1A',
);
// get accounts
const accounts = await web3.eth.getAccounts();
this.account = accounts[0];
this.ready();
this.refreshBalance();
} catch (error) {
console.error("Could not connect to contract or chain.");
}
},
La pregunta actual es ¿dónde encontrar voteArtifact.abi? Bien, resulta que lo copié del remix y adjunté todo el código.
import Web3 from "web3";
//import votingArtifact from "../../build/contracts/Voting.json";
const aInBytes32 ="0x4100000000000000000000000000000000000000000000000000000000000000";
const bInBytes32 ="0x4200000000000000000000000000000000000000000000000000000000000000";
const App = {
web3: null,
account: null,
meta: null,
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,
// );
this.voting = new web3.eth.Contract(
[
{
"constant": false,
"inputs": [],
"name": "getAccountAddress",
"outputs": [
{
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "candidateName",
"type": "bytes32"
}
],
"name": "totalVotesFor",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "Peoples",
"outputs": [
{
"name": "account",
"type": "address"
},
{
"name": "voingtime",
"type": "uint256"
}
],
"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": "candidateName",
"type": "bytes32"
}
],
"name": "voteForCandidate",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"name": "candidateListName",
"type": "bytes32[]"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
}
],
'0x32Cf1f3a98aeAF57b88b3740875D19912A522c1A',
);
// get accounts
const accounts = await web3.eth.getAccounts();
this.account = accounts[0];
this.ready();
this.refreshBalance();
} catch (error) {
console.error("Could not connect to contract or chain.");
}
},
ready:async function(){
try{
this.refresh("Alice",aInBytes32);
this.refresh("Bob",bInBytes32);
}catch(err){
console.log(err)
}
},
refresh:async function(id,nameBytes32){
const {totalVotesFor} = this.voting.methods;
const tickets = await totalVotesFor(nameBytes32).call();
const el = document.getElementById(id);
el.innerHTML = tickets.toString();
},
voteForCandidate:async function(){
try{
const {voteForCandidate} = this.voting.methods;
const condidateName = document.getElementById("candidate").value;
console.log(condidateName)
if (condidateName == "Alice")
{
await voteForCandidate(aInBytes32).send({from:this.account});
this.refresh("Alice",aInBytes32);
}
else{
await voteForCandidate(bInBytes32).send({from:this.account});
this.refresh("Bob",bInBytes32);
}
}catch(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();
});
Ahora que el programa está bien, ingrese al directorio de aplicaciones del último proyecto (se omiten detalles)
cd app
npm run dev
Empecé a votar y la primera votación fue exitosa. La segunda votación fue buena, pero dejé de votar. Finalmente, no pude pasar mi voto.
Resumir
El andamiaje que utilicé la última vez estaba listo, pero muchos detalles no estaban en su lugar. Ser más detallado hoy también es el final de mi propio aprendizaje.