转载自DAPP开发
实验内容
1.工具安装,在虚拟机中安装Nodejs和npm
首先安装curl
:
sudo apt install curl
安装nodejs
:
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
执行安装指令:
sudo apt-get install -y nodejs
这里我没有报错,如果有的报了这种无法获得锁
的错
E: 无法获得锁 /var/lib/dpkg/lock。锁正由进程 69782(unattended-upgr)持有
N: 请注意,直接移除锁文件不一定是合适的解决方案,且可能损坏您的系统。
E: 无法锁定管理目录(/var/lib/dpkg/),是否有其他进程正占用它?
只需要执行指令删除报错的文件夹名即可
sudo rm /var/lib/dpkg/lock
然后就可以重新执行安装指令(其他报同样错误的地方也可以使用这个方法)
sudo apt-get install -y nodejs
然后输入node -v
查看安装完成的node的版本
2.模拟环境ganache-安装
由于访问npm网站会比较慢,所以先切换源:
npm config set registry http://registry.npm.taobao.org
在根目录下建一个项目文件夹mkdir simple_vote
进入文件夹cd simple_vote
输入npm init
一直按回车键
再输入ls
就可以看到在项目文件夹下出现了一个package.json
文件
安装web3(必须是0.20.7版本的,不然后面无法运行.isConnected()
)
npm install web3@0.20.7
这里安装web3的时候可能会报错,我是装了一个git
之后解决的
可以参考一下这个Ubuntu 18.04上安装使用Git
装完Git之后,再重新运行web3安装指令,就不会报错了
然后安装ganache-cli
npm install ganache-cli
安装solc(必须安装0.4.24版本,不然后面的compiledCode
会报错)
npm install solc@0.4.24
然后进入.bin
文件夹
cd node_modules/.bin
输入ganache-cli
,然后就会输出一大堆可用的账户和私钥。如果报错端口占用,就改一下端口ganache-cli -p 7878
,端口改过的,在后面的操作中注意灵活改一下
再打开一个终端进入simple_vote:cd simple_vote
然后创建Voting.sol文件
vim Voting.sol
然后编写Voting.sol的内容
pragma solidity >0.4.22;
contract Voting{
bytes32[] public candidateList;
mapping (bytes32 => uint8) public votesReceived;
constructor(bytes32[] memory candidateListName) public {
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];
}
}
然后按Esc
,输入:wq
保存并退出
这里我在constructor
函数的参数中加了个memory
,不然我后面编译的时候会报错
输入node
进入nodejs环境
在nodejs环境里编写代码的时候,因为是写一行运行一行,所以要每一行代码运行之前要仔细检查,看是否游标变量大小写写错,稍微写错一点,它就报错退出nodejs环境(好娇气的说ヽ(ー_ー)ノ),退出去重新进nodejs之后,前面的要重新输入,我就输错了好几遍(ಥ_ಥ)
在node环境内输入
var Web3=require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
前面改过ganache-cli端口的,把这里的8545改掉
然后输入
web3.isConnected()
结果是true
如果不是的话,看一下前一个终端窗口ganache-cli
是不是关掉了(就是下面这个窗口)
然后输入
web3.eth.accounts
输出的结果应该和ganache-cli中的结果一样
然后输入
var solc = require('solc')
输入solc
查看变量
再打开一个终端,进入simple_vote
:cd simple_vote
输入
sudo chmod 666 Voting.sol
然后回到刚才那个node环境的窗口
执行命令
var sourceCode = fs.readFileSync('Voting.sol').toString()
再输入sourceCode
查看变量
继续输入
var compiledCode = solc.compile(sourceCode)
compiledCode
查看变量,然后会显示巨长一串(里面包括了bytecode和interface参数,后面会用到这两个参数)
然后再输入
var abi = JSON.parse(compiledCode.contracts[':Voting'].interface);
abi
查看变量
输入
var byteCode = compiledCode.contracts[':Voting'].bytecode
byteCode
查看变量
byteCode和abi的内容对应的是compiledCode里的参数bytecode
和interface
输入
var VotingContract = web3.eth.contract(abi)
var deployTxObj = {data:byteCode,from:web3.eth.accounts[0],gas:3000000}
var contractInstance = VotingContract.new(['Riane','Michael','Depp'],deployTxObj);//随便命名三个候选人的姓名
contractInstance.address
3.控制台投票交互
继续输入
contractInstance.vote("Riane",{ from:web3.eth.accounts[0]}) //投给Alice
contractInstance.totalVotesFor("Riane")
contractInstance.totalVotesFor("Riane").toString()//查看Riane的票数:1
contractInstance.totalVotesFor("Michael").toString()//查看Michael的票数:0
contractInstance.vote.sendTransaction("Depp",{from:web3.eth.accounts[0]})
contractInstance.totalVotesFor("Depp").toString()//查看Depp的票数:1
4.编辑网页,和以太坊交互
在之前的ganache-cli窗口中选取一个Available Accounts
作为账户地址
然后连按两次ctrl c
离开nodejs环境(不用再怕出问题要重新写了!(。-`ω´-))
然后进行编译(这里Voting.sol是加过memory,见上面,不加的话可能会报错,编译不出来)
solc --bin Voting.sol
solc --abi Voting.sol
以上两个编译结果都要复制记录下来
然后再进入nodejs
环境,(没错,又是娇气的它≡┏|*´・Д・|┛)
输入
var Web3=require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))//这里改过端口的记得把8545改掉
abi = [{"inputs":[{"internalType":"bytes32[]","name":"candidateListName","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"candidateList","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}]
//=后面的一长串就是刚刚复制记录的solc --abi Voting.sol结果
然后输入
bytecode = '608060405234801561001057600080fd5b5060405161041a38038061041a8339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b8382019150602082018581111561006957600080fd5b825186602082028301116401000000008211171561008657600080fd5b8083526020830192505050908051906020019060200280838360005b838110156100bd5780820151818401526020810190506100a2565b5050505090500160405250505080600090805190602001906100e09291906100e7565b5050610159565b828054828255906000526020600020908101928215610123579160200282015b82811115610122578251825591602001919060010190610107565b5b5090506101309190610134565b5090565b61015691905b8082111561015257600081600090555060010161013a565b5090565b90565b6102b2806101686000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80632f265cf7146100515780637021939f14610099578063a69beaba146100e1578063b13c744b1461010f575b600080fd5b61007d6004803603602081101561006757600080fd5b8101908080359060200190929190505050610151565b604051808260ff1660ff16815260200191505060405180910390f35b6100c5600480360360208110156100af57600080fd5b810190808035906020019092919050505061018d565b604051808260ff1660ff16815260200191505060405180910390f35b61010d600480360360208110156100f757600080fd5b81019080803590602001909291905050506101ad565b005b61013b6004803603602081101561012557600080fd5b8101908080359060200190929190505050610200565b6040518082815260200191505060405180910390f35b600061015c82610221565b61016557600080fd5b6001600083815260200190815260200160002060009054906101000a900460ff169050919050565b60016020528060005260406000206000915054906101000a900460ff1681565b6101b681610221565b6101bf57600080fd5b600180600083815260200190815260200160002060008282829054906101000a900460ff160192506101000a81548160ff021916908360ff16021790555050565b6000818154811061020d57fe5b906000526020600020016000915090505481565b600080600090505b6000805490508160ff1610156102725760008160ff168154811061024957fe5b9060005260206000200154831415610265576001915050610278565b8080600101915050610229565b50600090505b91905056fea265627a7a72315820a16746a0a9692cdbbef21916fe8f74d19b6db7e5aeced6c2300ae9a5a55eea3564736f6c63430005100032'
//引号里的结果就是之前复制记录的solc --bin Voting.sol结果
继续输入
VotingContract = web3.eth.contract(abi)
contractInst = VotingContract.new(["Riane","Michael","Depp"],{data:bytecode,from:web3.eth.accounts[0],gas:5000000})
contractInst.address
记录下这个账户地址
然后两次ctrl c
再离开nodejs环境(ε=ε=ε=┌(;´゚ェ゚)┘快跑)
在项目文件夹simple_vote
下,不在就cd进去,编写html和js文件
vim index.html
然后编写index.html内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voting DApp</title>
<link href ="https://cdnjs.cloudflare.com/ajax/libs/twitter-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>
<tr>
<th>Candidate</th>
<th>Vote Count</th>
</tr>
</thead>
<tbody>
<tr>
<td>Riane</td>
<td id="candidate-1"></td>
</tr>
<tr>
<td>Michael</td>
<td id="candidate-2"></td>
</tr>
<tr>
<td>Depp</td>
<td id="candidate-3"></td>
</tr>
</tbody>
</table>
<input type="text" id="candidate"/>
<a href="#" onclick="voteForCandidate()" class="btn btn-primary">Vote</a>
</div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/web3@0.20.6/dist/web3.min.js"></script>
<script src="./index.js"></script>
</html>
然后按Esc
,输入:wq
保存退出
输入
vim index.js
编写index.js内容
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));//端口修改过的把8545换掉
//这里引号里粘贴solc --abi Voting.sol的结果
var abi = JSON.parse('[{"inputs":[{"internalType":"bytes32[]","name":"candidateListName","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"candidateList","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}]');
var contractAddr = "0xb7e3043136553fa6eed53e4d4e0893384991ea1e";//这里换成刚刚复制的账户地址
var VotingContract = web3.eth.contract(abi);
var contractInstance = VotingContract.at(contractAddr);
var candidates = {"Riane":"candidate-1","Michael":"candidate-2","Depp":"candidate-3"};
function voteForCandidate(){
let candidateName = $("#candidate").val();
try{
contractInstance.vote(candidateName,{from:web3.eth.accounts[0]},(err,res)=>{
if(err)
console.log("Error: ",err);
else{
let id = candidates[candidateName];
let count = contractInstance.totalVotesFor(candidateName).toString();
$("#" + id).html(count);
}
})
}
catch (err){console.log("error");}
}
$(document).ready(function(){
var candidateList = Object.keys(candidates);
for(let i =0; i< candidateList.length; i++){
let name = candidateList[i];
let count = contractInstance.totalVotesFor(name).toString();
$("#" + candidates[name]).html(count);
}
});
然后保存退出之后,用firefox打开index.html文件
输入候选人的名字即可投票
最后部署node服务器
在项目文件夹simple_vote下新建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(404,{"Content-Type": "text/html"});
}
else {
res.writeHead(200,{"Content-Type": "text/html"});
res.write(data.toString());
}
res.end();
});
}).listen(8888);
保存退出
输入
node server.js
然后打开火狐,输入http://localhost:8888/index.html
出现投票界面,部署成功
转载自DAPP开发
基本是跟着的博客走的,其中一些地方在我运行的时候报错了,所以对其中做了一些细微的修改,然后成功部署。学长nb !(`・ω・´)ゞ敬礼っ
Tips:因为网址http://localhost:8888/index.html是在node服务器部署下运行的,所以关掉运行node server.js的终端窗口后,网址无法再打开运行。而且关掉ganache-cli窗口后,index.html用火狐打开后也无法运行了(  ̄┏_┓ ̄)。
就等于关掉虚拟机重启之后,要重新部署一遍,才能重新运行成功⊙︿⊙。不知道是不是只有我存在这个问题(˘•ω•˘)