Dapp voting Voting implementation process

Table of contents

 Realize the effect preview

Prerequisite: Install the package

 Directory Structure

Simple voting Dapp design process 

solidity contract

Create Voting.sol contract

Compile the contract

 (1) Import solc and fs

 (2) Read the content of the contract

(3) Compile the contract code

(4) Take abi, byteCode 

deploy contract

(1) Create a Web3 instance

(2) Create a Contract object

(3) Deployment contract 

full code

 Call the contract method

Front page HTML

Front-end JS

Method 1: Obtain the contract address and abi 

 Method 2: Get abi

Write server.js

Execute server.js

(Unpassed version) Simple voting Dapp design process

!!!!!!!!!

I encountered a problem in this part, but I didn’t get it through, so I changed the version and wrote a new one, which is only for reference

solidity contract

 Create Voting.sol contract

 Compile the contract

(1) Import solc and fs

 (2) Read the content of the contract

(3) Compile the contract code

(4) Take abi and byteCode 

deploy contract

(1) Create a Web3 instance

(2) Create a Contract object

(3) Deployment contract 

Call the contract method


 Realize the effect preview

Prerequisite: Install the package

    Bag:

    "ganache-cli": "^6.1.8",
    "solc": "^0.4.25"
// Before using "solc": "^0.7.3", there are many differences between the two compilations, and many problems have been changed , and finally got stuck with the version
    "web3": "^1.7.0"

   system:

    "ubuntu-20.04.4-desktop-amd64"

Below is the Linux based installation guide. Here we are required to pre-install nodejs and npm, and then use npm to install  ganache , solc , web3  , and then we can continue to the next step of the project

Install the following environment in the simple_vote_dapp folder

mkdir ~/桌面/simple_vote_dapp
cd ~/桌面/simple_vote_dapp

npm init
sudo npm install [email protected] [email protected] [email protected]

If the installation is successful, run the following command, you should be able to see the following output

~/桌面/simple_vote_dapp/node_modules/.bin/ganache-cli

 Create a new contract to store the sol contract file

mkdir ~/桌面/simple_vote_dapp/contracts

 Directory Structure

Make sure that ganache is already running in another window at the same time

Start another terminal , and in the simple_vote_dapp directory

Run node to enter the node console, initialize the web3 object, and query the blockchain to get all accounts.

node
var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
var accounts;
web3.eth.getAccounts().then((res)=>{accounts = res;console.log(accounts)});


Simple voting Dapp design process 

solidity contract

We design a contract called Voting, which has the following contents:

  • A constructor to initialize some candidates
  • A method for voting (count of votes + 1)
  • A method that returns the total number of votes a candidate has received

When you deploy the contract to the blockchain, the constructor is called, and only once. Unlike in the web world where every deployment of code will overwrite the old code, the contract deployed on the blockchain is immutable, that is, if you update the contract and deploy it again, the old contract will still exist on the blockchain


Create Voting.sol contract

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.4.25 <0.9.0;

contract Voting{
    bytes32[] public candidateList;
    mapping(bytes32 => uint8) public votesReceived;

    constructor(bytes32[] memory candidateListName){
        candidateList = candidateListName;
    }

    function validateCandidate(bytes32 candidateName) internal view returns(bool){
        for (uint8 i = 0; i < candidateList.length; i++){
            if(candidateName == candidateList[i]){
                return true;
            }
        }
        return false;
    }

    function vote(bytes32 candidateName) public{
        require(validateCandidate(candidateName));
        votesReceived[candidateName] += 1;
    }

    function totalVotesFor(bytes32 candidateName) public view returns(uint8){
        require(validateCandidate(candidateName));
        return votesReceived[candidateName];
    }

}

To compile the contract, first load the code from Voting.sol and bind it to a variable of type string


Compile the contract

Reference link: Web3 Deploy Smart Contract_zhongliwen1981's Column-CSDN Blog_web3 Deploy Smart Contract 1. Introduction to web3 web3 is a node.js library that specifically interacts with Ethereum. Let's review the steps of using remix to deploy a contract: Step 1: Write the contract. Step 2: Compile the contract (we set up automatic compilation before). Step 3: Deploy the contract, and return the contract address after successful deployment. Step 4: Call the contract. The bottom layer of remix uses web3 to realize the functions of compiling, deploying and calling contracts. So how does web3 realize these functions? After reading this article, it will be clear! ! ! Two, web... https://blog.csdn.net/zhongliwen1981/article/details/89926975

Under the node console

 (1) Import solc and fs

var solc = require('solc')
var fs = require('fs')

 (2) Read the content of the contract

var contractContent = fs.readFileSync('./contracts/Voting.sol', 'utf-8').toString()

(3) Compile the contract code

var contractCompiled = solc.compile(contractContent);

(4) Take abi, byteCode 

var abi = contractCompiled['contracts'][':Voting']['interface']
var byteCode = contractCompiled['contracts'][':Voting']['bytecode']


deploy contract

(1) Create a Web3 instance

var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

(2) Create a Contract object

var contract = new web3.eth.Contract(JSON.parse(abi));

(3) Deployment contract 

var accounts;
web3.eth.getAccounts().then((res)=>{accounts = res;console.log(accounts)});

// 合约拥有者账户
var account = accounts[0];
var gasLimit = 3000000;

// 参数需要两个数组是因为 deploy 参数 arguments 本身接收的就是一个数组,用以接收多个数据,所以最外层是一个数组,里面的数组是我们 Voting合约构造函数定义的参数为 bytes32[]
// 因为构造函数参数定义为 bytes32[],所以我们需要传入 bytes32,而不能直接传入字符串
var argument = [[web3.utils.fromAscii('Alice'),web3.utils.fromAscii('Bob'),web3.utils.fromAscii('Cary')]]
// 或
// var argument = [["0x416c696365000000000000000000000000000000000000000000000000000000","0x426f620000000000000000000000000000000000000000000000000000000000","0x4361727900000000000000000000000000000000000000000000000000000000"]]

contract.deploy({
    data:byteCode,
	arguments:argument
}).send({
    from:account,
    gas:gasLimit,
}).then(instance => {
	contractInstance = instance;
    console.log("contract address:", instance.options.address)
})

Write down the contract address contract address, and later modify the contract address in fontend-Voting.js to your own

full code

var solc = require('solc')
var fs = require('fs')
var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

var contractContent = fs.readFileSync('./contracts/Voting.sol', 'utf-8').toString()
var contractCompiled = solc.compile(contractContent);

var abi = contractCompiled['contracts'][':Voting']['interface']
var byteCode = contractCompiled['contracts'][':Voting']['bytecode']
var contract = new web3.eth.Contract(JSON.parse(abi));

var accounts;
web3.eth.getAccounts().then((res)=>{accounts = res;console.log(accounts)});

var account = accounts[0];
var gasLimit = 3000000;


var argument = [[web3.utils.fromAscii('Alice'),web3.utils.fromAscii('Bob'),web3.utils.fromAscii('Cary')]]
// 或
// var argument = [["0x416c696365000000000000000000000000000000000000000000000000000000","0x426f620000000000000000000000000000000000000000000000000000000000","0x4361727900000000000000000000000000000000000000000000000000000000"]]

contract.deploy({
    data:byteCode,
	arguments:argument
}).send({
    from:account,
    gas:gasLimit,
}).then(instance => {
	contractInstance = instance;
    console.log("contract address:", instance.options.address)
})

 Call the contract method

Call the vote method in the contract to vote for Alice

var voteTo = web3.utils.fromAscii('Alice')
contractInstance.methods.vote(voteTo).send({from:accounts[0]}).then(console.log)

 See how many votes a candidate has received

var aVoter = web3.utils.fromAscii('Alice')
contractInstance.methods.totalVotesFor(aVoter).call({from:accounts[0]}).then(res=>console.log(res))


Front page HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Voting DApp</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" type="text/css">
</head>

<body class="container">
    <h1>Simple Voting DApp</h1>
    <div class="table-responsive">
        <table class="table table-bordered">
            <thead>
                <th>Candidate</th>
                <th>Vote Count</th>
            </thead>
            <tbody>
                <tr>
                    <td>Alice</td>
                    <td id="candidate-1"></td>
                </tr>
                <tr>
                    <td>Bob</td>
                    <td id="candidate-2"></td>
                </tr>
                <tr>
                    <td>Cary</td>
                    <td id="candidate-3"></td>
                </tr>
            </tbody>
        </table>
        <input type="text" id="candidate" />
        <a href="#" onclick="" class="btn btn-primary">Vote</a>
    </div>
</body>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/ethereum/[email protected]/dist/web3.min.js"></script>
<script src="./fontend-Voting.js"></script>

</html>


Front-end JS

Method 1: Obtain the contract address and abi 

solcjs --abi --bin Voting.sol

 

 Method 2: Get abi

solc --abi --bin Voting.sol

let voteForCandidate;
let initial = async() => {
    // 不需要先 var Web3 = require('web3'), 因为已经网络引入了
    var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

    var abi = JSON.parse('[{"constant":true,"inputs":[{"name":"candidateName","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"getLengthList","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tlength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"candidateName","type":"bytes32"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"candidateList","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"candidateListName","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]')
    var contractAddress = '使用你自己的,上面步骤中有输出';
    var contractInstance = await new web3.eth.Contract(abi, contractAddress);

    var accounts = await web3.eth.getAccounts();

    console.log(accounts)

    // 对应关系
    var candidates = { "Alice": "candidate-1", "Bob": "candidate-2", "Cary": "candidate-3" };

    (async() => {
        var candidateList = Object.keys(candidates); // 拿出 candidates 中的 key 值
        for (let i = 0; i < candidateList.length; i++) {
            let name = candidateList[i];
            let aVoter = await web3.utils.fromAscii(name)
            let count = await contractInstance.methods.totalVotesFor(aVoter).call({ from: accounts[0] });
            console.log('count=', count)
            $("#" + candidates[name]).html(count)
        }
    })();


    voteForCandidate = async() => {
        var candidateName = $("#candidate").val()
        try {
            var voteTo = await web3.utils.fromAscii(candidateName)
            contractInstance.methods.vote(voteTo).send({ from: accounts[0] }, async(err, res) => {
                if (err) {
                    console.log("Error: ", err);
                } else {
                    let id = candidates[candidateName];
                    var count = await contractInstance.methods.totalVotesFor(voteTo).call({ from: accounts[0] });
                    console.log(count)
                    $("#" + id).html(count);
                }
            })
        } catch (err) {
            console.log(err)
        }
    }
}
$(document).ready(function() {
    initial();
});


Write server.js

Create server.js in the fontend directory

var http = require('http');
var fs = require('fs');
var url = require('url');
 
 
// 创建服务器
let server = 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/html
         response.writeHead(404, {'Content-Type': 'text/html'});
      }else{             
         // HTTP 状态码: 200 : OK
         // Content Type: text/html
         response.writeHead(200, {'Content-Type': 'text/html'});    
         
         // 响应文件内容
         response.write(data.toString());        
      }
      //  发送响应数据
      response.end();
   });   
})
 
server.listen(8888, '0.0.0.0', () => {
	console.log('Server running at http://0.0.0.0:8888/');
})

Execute server.js

Call server.js, provided that ganache-cli has been started in the background, and the contract address contract address in fontend-Voting.js is newly created in the above operation

node server.js

After that, you can open the front-end page in the browser and test it using

http://127.0.0.1:8888/fontend-Voting.html


(Unpassed version) Simple voting Dapp design process

!!!!!!!!!

I encountered a problem in this part, but I didn’t get it through, so I changed the version and wrote a new one, which is only for reference

   Bag:

    "ganache-cli": "^6.1.8",
    "solc": "^0.7.3" // Failed, changed to ^0.4.25

    "web3": "^1.7.0"

   system:

    "ubuntu-20.04.4-desktop-amd64"

solidity contract

We design a contract called Voting, which has the following contents:

  • A constructor to initialize some candidates
  • A method for voting (count of votes + 1)
  • A method that returns the total number of votes a candidate has received

When you deploy the contract to the blockchain, the constructor is called, and only once. Unlike in the web world where every deployment of code will overwrite the old code, the contract deployed on the blockchain is immutable, that is, if you update the contract and deploy it again, the old contract will still exist on the blockchain

 Create Voting.sol contract

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.4.25 <0.9.0;

contract Voting{
    bytes32[] public candidateList;
    mapping(bytes32 => uint8) public votesReceived;

    constructor(bytes32[] memory candidateListName){
        candidateList = candidateListName;
    }

    function validateCandidate(bytes32 candidateName) internal view returns(bool){
        for (uint8 i = 0; i < candidateList.length; i++){
            if(candidateName == candidateList[i]){
                return true;
            }
        }
        return false;
    }

    function vote(bytes32 candidateName) public{
        require(validateCandidate(candidateName));
        votesReceived[candidateName] += 1;
    }

    function totalVotesFor(bytes32 candidateName) public view returns(uint8){
        require(validateCandidate(candidateName));
        return votesReceived[candidateName];
    }

}

To compile the contract, first load the code from Voting.sol and bind it to a variable of type string

 Compile the contract

Reference link: Web3 Deploy Smart Contract_zhongliwen1981's Column-CSDN Blog_web3 Deploy Smart Contract 1. Introduction to web3 web3 is a node.js library that specifically interacts with Ethereum. Let's review the steps of using remix to deploy a contract: Step 1: Write the contract. Step 2: Compile the contract (we set up automatic compilation before). Step 3: Deploy the contract, and return the contract address after successful deployment. Step 4: Call the contract. The bottom layer of remix uses web3 to realize the functions of compiling, deploying and calling contracts. So how does web3 realize these functions? After reading this article, it will be clear! ! ! Two, web... https://blog.csdn.net/zhongliwen1981/article/details/89926975

Under the node console

(1) Import solc and fs

var solc = require('solc')
var fs = require('fs')

 (2) Read the content of the contract

var contractContent = fs.readFileSync('./contracts/Voting.sol', 'utf-8').toString()

(3) Compile the contract code

The contract content is converted into JSON format

var input = {
  language: 'Solidity',
  sources: {
    contract: {
      content: contractContent
    }
  },
  settings: {
    outputSelection: {
      '*': {
        '*': '*'
      }
    }
  }
}

var contractCompiled = JSON.parse(solc.compile(JSON.stringify(input)));

(4) Take abi and byteCode 

var contractCompliedContent = contractCompiled.contracts.contract.Voting;
var abi = contractCompliedContent.abi;
var byteCode = contractCompliedContent.evm.bytecode.object;


deploy contract

(1) Create a Web3 instance

var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

(2) Create a Contract object

var contract = new web3.eth.Contract(abi);

(3) Deployment contract 

var accounts;
web3.eth.getAccounts().then((res)=>{accounts = res;console.log(accounts)});

// 合约拥有者的帐号
var account = accounts[0];
var gasLimit = 3000000;

// 参数需要两个数组是因为 deploy 参数 arguments 本身接收的就是一个数组,用以接收多个数据,所以最外层是一个数组,里面的数组是我们 Voting合约构造函数定义的参数为 bytes32[]
// 因为构造函数参数定义为 bytes32[],所以我们需要传入 bytes32,而不能直接传入字符串
var argument = [[web3.utils.fromAscii('Alice'),web3.utils.fromAscii('Bob'),web3.utils.fromAscii('Cary')]]
// 或
// var argument = [["0x7465737400000000000000000000000000000000000000000000000000000000","0x7465737400000000000000000000000000000000000000000000000000000000"]]

var contractInstance;
contract.deploy({
    data:byteCode,
	arguments:argument
}).send({
    from:account,
    gas:gasLimit,
}).then(instance => {
    contractInstance = instance;
    console.log("contract address:", instance.options.address)
})


Call the contract method

 After that, I couldn’t call the vote contract method. I tried a lot of methods and it didn’t work.

The error VM Exception while processing transaction: invalid opcode has been reported. If anyone knows, please let me know.

Change to the 0.4.25 version and compile it. The guess is because of the input step above, because the input step is not used after changing the 0.4.25 version.

The specific reason is unknown

contractInstance.methods.vote("0x416c6963650000000000000000000000").send({from:accounts[0]}).then(console.log)


Run external network access

Under the fontend folder, create a server.js file

var http = require('http');
var fs = require('fs');
var url = require('url');
 
 
// 创建服务器
let server = 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/html
         response.writeHead(404, {'Content-Type': 'text/html'});
      }else{             
         // HTTP 状态码: 200 : OK
         // Content Type: text/html
         response.writeHead(200, {'Content-Type': 'text/html'});    
         
         // 响应文件内容
         response.write(data.toString());        
      }
      //  发送响应数据
      response.end();
   });   
})
 
server.listen(8890, '0.0.0.0', () => {
	console.log('Server running at http://0.0.0.0:8890/');
})

 Put server.js on the cloud server and execute the following command

node server.js

Guess you like

Origin blog.csdn.net/m0_46262108/article/details/123493425