Dapp简单的投票小例子

谁都会走 提交于 2020-10-29 17:14:24

准备工作

相关命令

  • mkdir simple_voting_dapp //创建文件夹
  • cd simple_voting_dapp //进入文件夹
  • npm init  //初始化npm包管理文件,输入ls 可以看到创建的package.json文件
  • npm intsall web3@0.20.1 //安装web3
  • npm install solc@0.4.25   //安装solc
  • npm install -g ganache-cli   //安装ganache-cli  参考文档 
  • 输入ganache-cli 启动,出现如下界面,表示启动成功,列出10个账户以及各自的秘钥,每个账户都有100以太,这个类似dev私链的模式

Solidity合约

合约内容

  • 构造函数,用来初始化一些候选者
  • 用来投票的方法(对于票数加 1)
  • 返回候选者获得的总的票数的方法

合约代码

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;
        }
    }
}

合约编译

编译前测试

  • 查看安装的版本 npm list web3;npm list solc
  • node 进入命令行
  • var Web3 =  require('web3')
  • var web3 = new Web3(new Web3.providers.HttpProvider('Http://localhost:8545'))//这个需要和ganache-cli设置的一致,才可以连接上。
  • 使用web3.isConnected()//查看是否连接上,此时需要将ganache-cli启动
  • 使用命令web3.eth.accounts,获取目前的账户,需要注意每次启动ganache-cli都会产生全新的账号和秘钥

正式开始编译

  • var solc = require('solc')
  • 输入solc. 查看可以使用的命令
  • var sourceCode = fs.readFileSync('Voting.sol').toString()  //需要注意,必须给与文件读取权限
  • 输入sourceCode 可以查看编码文件
  • var compiledCode = solc.compile(sourceCode)  //这个就是正式的编译过程
  • 输入compiledCode可以查看字节码文件,编译结果如下图所示

  • 这是一个js对象,最关键的是bytecode(字节码)和interface接口(ABI),这两个文件是部署合约最关键的文件
  • 部署合约 var abi = JSON.parse(compiledCode.contracts[':Voting'].interface);    输入abi进行验证,是否取到正确的值,JSON.parse是为了Json格式化代码
  • var byteCode = compiledCode.contracts[':Voting'].bytecode 取到字节码     其中:Voting是合约名称
  • 创建合约对象 var VotingContract = web3.eth.contract(abi)
  • var deployTxObj = {data:byteCode,from:web3.eth.accounts[0],gas:30000000}  定义一笔交易
  • 定义合约实例 var contractInstance = VotingContract.new(["Alice","Bob","Cary"],deployTxObj);
  • contractInstance = VotingContract.new(["Alice","Bob","Cary"],{data:byteCode,from:web3.eth.accounts[0],gas:5000000});
  • 上面这一条命令是 定义合约实例 和 定义一笔交易的结合体,如果分开,有可能会报错
  • 取到合约实例的地址 contractInstance.address  这就将合约部署在区块链上,这个区块链是本地起的ganache-cli模拟区块链
  • contractInstance.vote("Alice",{from:web3.eth.accounts[0]})  账号0给Alice投出一票
  • contractINstance.totalVotesFor("Alice").toString()  查看Alice的总共的票数
  • contractINstance.totalVotesFor.call("Alice").toString() 查看
  • contractInstance.vote.sendTransaction("Alice",{from:web3.eth.accounts[0]}) 

页面交互

页面 index.html

<!DOCTYPE html>
<html>
	<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>
			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.min.js">
	</script>
	<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js">
	</script>
	<script src="./index.js">
	</script>

</html>

注意

  • 不再需要引入web3对象,因为页面里面就包含了web3对象,https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js,打开这个链接之后,在页面的最下面会显示,

index.js

var web3 = new Web3(new Web3.providers.HttpProvider("Http://localhost:8545"));
var abi = JSON.parse('[{"constant":true,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"validCandidate","outputs":[{"name":"","type":"bool"}],"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":"candidate","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"candidateNames","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]');
// var contractAddr = "0x3577f4d4902f4766753056468bd08ef68df6c623";
var VotingContract = new web3.eth.Contract(abi); 
// var contractInstance =new web3.eth.Contract(abi,contractAddr);
// contractInstance = VotingContract.at(contractAddr);
var contractInstance = VotingContract.at('0x3577f4d4902f4766753056468bd08ef68df6c623');
var candidates = {
    "Alice":"candidate-1",
    "Bob":"candidate-2",
    "Cary":"candidate-3"
};
function voteForCandidate(){
    let candidateName = $("#candidate").val();
    try{
        contractInstance.voteForCandidate(candidateName),{from:web3.eth.accounts[0]},(err,res)=>{
            if(err)
                console.log("Error:",err);
            else{
                let div_id = candidates[candidateName];
                let count = contractInstance.totalVotesFor(candidateName).toString();
                $("#" + id).html(count);
            }

        }
    }catch(err){

    }
};

$(document).ready(function(){
    candidateNames = Object.keys(candidates);
    for(let i=0;i<candidateNames.length;i++){
        let name = candidateNames[i];
        let val = contractInstance.totalVotesFor.call(name).toString();
        $("#" + candidates[name]).html(val);
    }
});

报错

无解

  • at无法使用
  • 如果使用 var contractInstance =new web3.eth.Contract(abi,contractAddr);避免了at,但是无法使用call回调函数,依然报错

部署服务

server.js

  • 异步的方式启动,同步的会报错
var http = require('http');
var fs = require('fs');
var url = require('url');

http.createServer(function(req,res){
    var pathName = url.parse(req.url).pathname;
    console.log("Request for:"+pathName + "received.");

    fs.readFile(pathName.substr(1),function(err,data){
        if(err){
            console.log(err);
            res.writeHead(400,{'Content-Type':"text/html"});
        }else{
            res.writeHead(200,{'Content-Type':"text/html"});
            res.write(data.toString());
        }
        res.end();
    });
}).listen(8888);
  • 使用命令 node server.js启动服务器
  • 浏览器输入 http://127.0.0.1:8888/index.html 查看

  • 如果报错 没有favicon.ico  直接创建一个就好,这个是图标文件

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!