Quick learning-simple voting DApp

Simple voting DApp

Next we have to start to actually make a DApp, although it is a very simple voting application, but it will contain the complete workflow and interactive page. The main steps to build this application are as follows:

  1. We first install a simulated blockchain called ganache, which enables our program to run in the development environment.
  2. Write a contract and deploy to ganache.
  3. Then we will interact with ganache through the command line and web page.

The way we communicate with the blockchain is through RPC (Remote Procedure Call). web3js is a JavaScript library that abstracts all RPC calls so that you can interact with the blockchain through JavaScript. Another benefit is that web3js allows you to build great web applications using your favorite JavaScript framework.

Development Preparation-Linux

The following is a Linux-based installation guide. This requires us to install nodejs and npm in advance, and then install ganache-cli, web3, and solc with npm to continue to the next step of the project.

mkdir simple_voting_dapp
cd simple_voting_dapp
npm init
npm install ganache-cli web3@0.20.1 solc
node_modules/.bin/ganache-cli

If the installation is successful, run the command node_modules / .bin / ganache-cli, you should be able to see the output shown in the figure below.
Insert picture description here
In order to facilitate testing, ganache will create 10 accounts by default, each account has 100 ether. . You need to use one of the accounts to create a transaction, send and receive ether.

Of course, you can also install the GUI version of ganache instead of the command line version, download the GUI version here: http://truffleframework.com/ganache/

Solidity contract

We will write a contract called Voting, this contract has the following content:

  • A constructor used to initialize some candidates.
  • A method for voting (add 1 to the number of votes)
  • A method to return the total number of votes obtained by the candidate

When you deploy a contract to the blockchain, the constructor is called, and only once. Unlike every code deployed in the web world that will overwrite the old code, the contract deployed on the blockchain is immutable, that is, if you update the contract and deploy again, the old contract will still exist on the blockchain , And the data is still there. The new deployment will create a new instance of the contract.

Code and explanation

pragma solidity ^ 0.4 .22;
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;
	}
}

Line 1. We must specify which version of the compiler the code will compile

Line 3. mapping is equivalent to an associative array or dictionary, which is a key-value pair. The key of mapping votesReceived is the name of the candidate, and the type is bytes32. The value of mapping is an unassigned integer, which stores the number of votes.

Line 4. In many programming languages ​​(such as the dictionary <HashTable inherited from the dictionary> in java and python), all candidate names can be obtained only through votesReceived.keys. However, there is no such method in solidity, so we must separately manage a candidate array candidateList.

Line 14. Note that votesReceived [key] has a default value of 0, so you don't need to initialize it to 0, just add 1 directly.

You will also notice that each function has a visibility specifier (such as public in this example). This means that functions can be called from outside the contract. If you don't want anyone else to call this function, you can make it a private function. If you do not specify visibility, the compiler will throw a warning. Recently, the solidity compiler has made some improvements. If the user forgets to mark the private function and the private function can be called externally, the compiler will catch this problem.

You will also see a modifier view on some functions. It is usually used to tell the compiler that the function is read-only (that is, the state of the blockchain is not updated when the function is called).

Next, we will use the solc library installed in the previous section to compile the code. If you remember, we mentioned before that web3js is a library that allows you to interact with the blockchain via RPC. We will use this library to deploy contracts in the node console and interact with the blockchain.

Compile the contract

In the node console> Web3 = require('web3')
> web3 = new Web3(new
Web3.providers.HttpProvider("http://localhost:8545"));
> web3.eth.accounts
['0x5c252a0c0475f9711b56ab160a1999729eccce97'
'0x353d310bed379b2d1df3b727645e200997016ba3']
> code = fs.readFileSync('Voting.sol').toString()
> solc = require('solc')
> compiledCode = solc.compile(code)

First, run node in the terminal to enter the node console, initialize the web3 object, and query the blockchain to obtain all accounts.

Make sure ganache is already running in another window

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

When you successfully compile the contract, print the compiledCode object (you can see the content directly by entering the compiledCode in the node console), you will notice that there are two important fields, they are very important, you must understand:

  1. compiledCode.contracts[':Voting'].bytecode:This is the byte code after Voting.sol is compiled. It is also the code to be deployed on the blockchain.
  2. compiledCode.contracts[':Voting'].interface: This is a contract interface or template (called abi definition), which tells the user what methods are in this contract. Whenever you want to interact with any contract in the future, you will need this abi definition. You can read more about ABI here. In future projects, we will use the truffle framework to manage compilation and interaction with the blockchain.

However, before using any framework, it is helpful to have a deep understanding of how it works, because the framework will abstract away these contents.

Deployment contract

Let's continue the course and now deploy the contract on the blockchain. To do this, you must first create a contract object VotingContract by passing in the abi definition. Then use this object to deploy and initialize the contract on the chain.

Execute this in your node console:
> abiDefinition = 
JSON.parse(compiledCode.contracts[':Voting'].interface)
> VotingContract = web3.eth.contract(abiDefinition)
> byteCode = compiledCode.contracts[':Voting'].bytecode
> deployedContract = 
VotingContract.new(['Alice','Bob','Cary'],{data: byteCode, from: 
web3.eth.accounts[0], gas: 4700000})
> deployedContract.address
'0x0396d2b97871144f75ba9a9c8ae12bf6c019f610'
// Your address will be different
> contractInstance = VotingContract.at(deployedContract.address)

VotingContract.new deploys the contract to the blockchain.
The first parameter is an array of candidates. Candidates will compete for elections, which is easy to understand. Let's see what is in the second parameter:

  1. data: This is the bytecode we deployed to the blockchain after compilation.
  2. from: The blockchain must track who deployed this contract. In this case, we are just the first account returned from calling web3.eth.accounts as the account that deployed this contract. Remember, web3.eth.accounts returns an array of 10 test accounts created by ganache. Before trading, you must have this account and unlock it. When creating an account, you will be asked to enter a password, which is what you use to prove your ownership of the account. In the next section, we will introduce it in detail. For convenience, ganache will unlock 10 accounts by default.
  3. gas: It costs money to interact with the blockchain. This money is used to pay miners, because they help you include the code in the blockchain. You must specify how much you are willing to spend to include your code in the blockchain, which is to set the value of "gas". The ETH balance in your "from" account will be used to purchase gas. The price of gas is set by the network. We have deployed the contract and have a contract instance (variable contractInstance) that we can use to interact with the contract.

There are thousands of contracts on the blockchain. So, how to recognize that your contract is on the chain? The answer is to find the address of the deployed contract deployedContract.address. When you need to interact with the contract, you need this deployment address and the abi definition we talked about earlier.

Console interaction

In your node console: > contractInstance.totalVotesFor.call('Rama') { [String: '0'] s: 1, e: 0, c: [ 0 ] } > contractInstance.voteForCandidate('Rama', {from:
web3.eth.accounts[0]})
'0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc78
6bf53'
> contractInstance.voteForCandidate('Rama', {from:
web3.eth.accounts[0]})
'0x02c054d238038d68b65d55770fabfca592a5cf6590229ab91bbe7cd72da
46de9'
> contractInstance.voteForCandidate('Rama', {from:
web3.eth.accounts[0]})
'0x3da069a09577514f2baaa11bc3015a16edf26aad28dffbcd126bde2e71f
2b76f'
>
contractInstance.totalVotesFor.call('Rama').toLocaleString()'3
'

{[String: '0'] s: 1, e: 0, c: [0]} is the scientific notation of the number 0. The value returned here is a bigNumber object, and its .toNumber () method can be used To display the numbers:

contractInstance.totalVotesFor.call('Alice').toNumber()
	web3.fromWei(web3.eth.getBalance(web3.eth.accounts[1]).toNumber(),'ether'
)

BigNumber values ​​are stored in decimal floating point format in the form of symbols, exponents, and coefficients. s is the sign symbol, that is, positive and negative; e is the exponent exponent, indicating that there are several zeros after the highest bit; c is the coefficient coefficient, that is, the actual significant number; the number of input parameters of the bignumber constructor is limited to 14, so The coefficient representation is an array that is intercepted from back to front, with 14 bits intercepted once. Vote for the candidate and view the number of votes to continue the course, call the voteForCandidate and totalVotesFor methods in your node console and view the results.

Each time you vote for a candidate, you will get a transaction id: for example: ' 0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53'. This transaction id is the credential of the transaction, and you can refer to this transaction at any time in the future. This transaction is immutable. For a blockchain like Ethereum, immutability is one of its main characteristics. In the next chapter, we will use this feature to build applications.

Web page interaction

At this point, most of the work has been completed. What we still need to do is create a simple html with the candidate name and call the voting command (we have tried it in the nodejs console). You can find html code and js code on the right. Place them in the chapter1 directory and open index.html in a browser.

index.html

<!DOCTYPE html>
<html>
	<head>
		<title>
			Voting DApp
		</title>
		<link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/boot
		strap.min.css' rel='stylesheet' type='text/css'>
	</head>
	<body class="container">
		<h1>
			A Simple Voting Application
		</h1>
		<div class="table-responsive">
			<table class="table table-bordered">
				<thead>
					<tr>
						<th>
							Candidate
						</th>
						<th>
							Votes
						</th>
					</tr>
				</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>
		</div>
		<input type="text" id="candidate" />
		<a href="#" onclick="voteForCandidate()" class="btn 
		btn-primary">
			Vote
		</a>
	</body>
	<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.mi
	n.js">
	</script>
	<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js">
	</script>
	<script src="./index.js">
	</script>

</html>

Tips:

  1. <head>Introduce the css type library of bootstrap in the form of link. The following classes such as container, table-responsive, etc. are all from bootstrap
  2. <th>Table header cell, table cell, the cell after the candidate name is the number of votes, distinguished by id to facilitate writing, then the corresponding relationship is written in js
  3. <input>An input box, define the id to facilitate the value in js
  4. <a>The button btn in the form of a hyperlink, href = “#” is a jump to this page, that is, no jump; onclick points to the method in js. To simplify the project, we have hard-coded the candidate name. If you like, you can adjust the code to dynamically select candidates.

index.js

web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
abi = JSON.parse('[{"constant":false,…}]') VotingContract = web3.eth.contract(abi);
contractInstance = VotingContract.at('0x329f5c190380ebcf640a90d06eb1db2d68503a53');
candidates = {
	"Alice": "candidate-1",
	"Bob": "candidate-2",
	"Cary": "candidate-3"
};
function voteForCandidate(candidate) {
	candidateName = $("#candidate").val();
	try {
		contractInstance.voteForCandidate(candidateName, {
			from: web3.eth.accounts[0]
		},
		function() {
			let div_id = candidates[candidateName];
			$("#" + div_id).html(contractInstance.totalVotesFor.call(candidateName).toString());
		});
	} catch(err) {}
}
$(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);
	}
});

On line 4, replace the contract address in the code with your own contract address. The contract address is the previously deployedContract.address

If all goes well, you should be able to enter the name of the candidate in the text box, and then the number of votes should be increased by 1.
Note: due to network reasons, web3.js may not be available, you can download it to the local import.

If you can see the page, vote for the candidate, and then see the increase in the number of votes, then the first contract has been successfully created, congratulations! All votes are saved on the blockchain and are immutable. Anyone can independently verify how many votes each candidate received. Of course, all our things are done on a simulated blockchain (ganache). In the following lessons, we will deploy this contract to the real public chain. In Part 2, we will deploy the contract to a public chain called Ropsten testnet, and also learn how to use the truffle framework to build contracts and manage dapps.

To summarize, here is what you have done so far:

  1. By installing node, npm and ganache, you have configured the development environment.
  2. You code a simple voting contract, compile and deploy it on the blockchain.
  3. You interacted with the webpage and the contract through the nodejs console.
Published 2146 original articles · Like 2405 · Visit 240,000+

Guess you like

Origin blog.csdn.net/weixin_42528266/article/details/105528554