目录

实现效果预览图

前提条件:安装包

目录结构

简单投票 Dapp 设计流程

solidity 合约

创建 Voting.sol 合约

编译合约

(1)导入 solc 和 fs

(2)读取合约内容

(3)编译合约代码

(4)取abi 、byteCode

部署合约

(1)创建Web3实例

(2)创建Contract对象

(3)部署合约

完整代码

调用合约方法

前端页面HTML

前端JS

方法一:获得合约地址和abi

方法二:获得 abi

编写 server.js

执行 server.js

(没走通版本)简单投票 Dapp 设计流程

!!!!!!!!!

这部分我遇到问题,没有走通,换版本重新写了一个,仅有借鉴意义

solidity 合约

创建 Voting.sol 合约

编译合约

(1)导入 solc 和 fs

(2)读取合约内容

(3)编译合约代码

(4)取abi 和 byteCode

部署合约

(1)创建Web3实例

(2)创建Contract对象

(3)部署合约

调用合约方法


实现效果预览图

前提条件:安装包

    包:

    "ganache-cli": "^6.1.8",
    "solc": "^0.4.25"
// 之前用"solc":"^0.7.3",二者编译有好多不同,改了好多个问题,最后卡住没办法版本了
    "web3": "^1.7.0"

   系统:

    "ubuntu-20.04.4-desktop-amd64"

下面是基于 Linux 的安装指南。这里要求我们预先安装 nodejs 和 npm,再用 npm 安装 ganachesolcweb3 ,就可以继续项目的下一步了

simple_vote_dapp 文件夹 中安装以下环境

mkdir ~/桌面/simple_vote_dapp
cd ~/桌面/simple_vote_dappnpm init
sudo npm install ganache-cli@6.1.8 web3@1.7.0 solc@0.4.25

如果安装成功,运行如下命令,应该能够看到如下的输出

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

新建一个contracts用来存放 sol 合约文件

mkdir ~/桌面/simple_vote_dapp/contracts

目录结构

确保此时同时 ganache 已经在另一个窗口中运行

另起一个终端中,并在simple_vote_dapp目录下

运行 node 进入 node 控制台,初始化 web3 对象,并向区块链查询获取所有的账户。

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


简单投票 Dapp 设计流程

solidity 合约

我们设计一个叫做 Voting 的合约,这个合约有以下内容:

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

当你把合约部署到区块链的时候,就会调用构造函数,并只调用一次。与 web 世界里每次部署代码都会覆盖旧代码不同,在区块链上部署的合约是不可改变的,也就是说,如果你更新合约并再次部署,旧的合约仍然会在区块链上存在


创建 Voting.sol 合约

// SPDX-License-Identifier: GPL-3.0pragma 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];}}

为了编译合约,先从 Voting.sol 中加载代码并绑定到一个 string 类型的变量


编译合约

参考链接:Web3部署智能合约_zhongliwen1981的专栏-CSDN博客_web3部署智能合约一、web3介绍web3是一个专门与以太坊交互的node.js库。我们先回顾一下使用remix部署合约的步骤:第一步:编写合约。第二步:编译合约(之前我们设置了自动编译)。第三步:部署合约,部署成功后返回合约地址。第四步:调用合约。remix底层就是使用了web3实现了编译、部署、调用合约的功能。那么web3是如何实现这些功能呢?看完这篇文章就一清二楚了!!!二、web...https://blog.csdn.net/zhongliwen1981/article/details/89926975

在 node 控制台下

(1)导入 solc 和 fs

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

(2)读取合约内容

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

(3)编译合约代码

var contractCompiled = solc.compile(contractContent);

(4)取abi 、byteCode

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

部署合约

(1)创建Web3实例

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

(2)创建Contract对象

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

(3)部署合约

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

记下合约地址 contract address,后面修改 fontend-Voting.js 中的contract address 为你自己的

完整代码

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

调用合约方法

调用合约中的 vote 方法,投票给Alice

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

查看候选人的所得票数

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


前端页面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/web3.js@1.7.0/dist/web3.min.js"></script>
<script src="./fontend-Voting.js"></script></html>

前端JS

方法一:获得合约地址和abi

solcjs --abi --bin Voting.sol

方法二:获得 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();
});

编写 server.js

在 fontend 目录下创建 server.js

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/htmlresponse.writeHead(404, {'Content-Type': 'text/html'});}else{             // HTTP 状态码: 200 : OK// Content Type: text/htmlresponse.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/');
})

执行 server.js

调用 server.js ,前提ganache-cli已经在后台启动,且 fontend-Voting.js 中的合约地址 contract address 是上面操作中新创建的

node server.js

之后就可以在浏览器中打开前端页面,测试使用了

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

(没走通版本)简单投票 Dapp 设计流程

!!!!!!!!!

这部分我遇到问题,没有走通,换版本重新写了一个,仅有借鉴意义

   包:

    "ganache-cli": "^6.1.8",
    "solc": "^0.7.3" // 走不通,换^0.4.25了

    "web3": "^1.7.0"

   系统:

    "ubuntu-20.04.4-desktop-amd64"

solidity 合约

我们设计一个叫做 Voting 的合约,这个合约有以下内容:

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

当你把合约部署到区块链的时候,就会调用构造函数,并只调用一次。与 web 世界里每次部署代码都会覆盖旧代码不同,在区块链上部署的合约是不可改变的,也就是说,如果你更新合约并再次部署,旧的合约仍然会在区块链上存在

创建 Voting.sol 合约

// SPDX-License-Identifier: GPL-3.0pragma 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];}}

为了编译合约,先从 Voting.sol 中加载代码并绑定到一个 string 类型的变量

编译合约

参考链接:Web3部署智能合约_zhongliwen1981的专栏-CSDN博客_web3部署智能合约一、web3介绍web3是一个专门与以太坊交互的node.js库。我们先回顾一下使用remix部署合约的步骤:第一步:编写合约。第二步:编译合约(之前我们设置了自动编译)。第三步:部署合约,部署成功后返回合约地址。第四步:调用合约。remix底层就是使用了web3实现了编译、部署、调用合约的功能。那么web3是如何实现这些功能呢?看完这篇文章就一清二楚了!!!二、web...https://blog.csdn.net/zhongliwen1981/article/details/89926975

在 node 控制台下

(1)导入 solc 和 fs

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

(2)读取合约内容

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

(3)编译合约代码

合约内容转化为 JSON 格式

var input = {language: 'Solidity',sources: {contract: {content: contractContent}},settings: {outputSelection: {'*': {'*': '*'}}}
}var contractCompiled = JSON.parse(solc.compile(JSON.stringify(input)));

(4)取abi 和 byteCode

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

部署合约

(1)创建Web3实例

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

(2)创建Contract对象

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

(3)部署合约

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

调用合约方法

 之后我调用这 vote 合约方法走不通了,尝试了好多方法都不行

一直报错误 VM Exception while processing transaction: invalid opcode,如果有好心人知道,望告知

换成0.4.25版本 编译之后就可以了,猜测是因为上面 input 那步骤,因为换了0.4.25版本之后没有用到 input 步骤

具体原因暂时不知

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


运行外部网络访问

在 fontend文件夹下,创建 server.js 文件

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/htmlresponse.writeHead(404, {'Content-Type': 'text/html'});}else{             // HTTP 状态码: 200 : OK// Content Type: text/htmlresponse.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/');
})

将 server.js 放入云服务器上,执行以下命令

node server.js

Dapp 投票 Voting 实现流程相关推荐

  1. DApp投票合约简单开发步骤(完整)

    友情提示:本文讲解比较快,若看不懂,可以先看看我的另一篇文章,链接给出 开发一个简单的智能合约 ,之后再来看本篇文章,会完全掌握开发步骤. 一.环境搭建 安装nodejs,npm会同时安装,下载地址: ...

  2. 以太坊开发入门,如何搭建一个区块链DApp投票系统

    点击关注异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 第一节 概述 对于初学者,需要了解以太坊开发相关的基本概念,另外就是如何构建一个基于以太坊的完整去中心化应用例如一个区块链投票 ...

  3. DAPP基本的开发流程

    一.梳理一下架构关系 理清ganache-cli(原来叫testrpc).web3.以太坊节点.metamask之间的架构关系. ganache-cli:模拟了一个以太坊的测试节点并提供对外的rpc访 ...

  4. 关于集成学习的总结(一) 投票法

    最近在写那个完整的机器学习项目博客时候,我本来打算用一篇博客来写的.结果发现要写的越来越多.而且最关键的是,以前以为有些地方理解了,其实并没有理解.大概这就是写博客记笔记的好处吧...可惜我上高中初中 ...

  5. (附源码)springboot在线投票系统 毕业设计 141307

    基于springboot在线投票系统 摘 要 随着全球Internet的迅猛发展和计算机应用的普及,特别是近几年无线网络的广阔覆盖以及无线终端设备的爆炸式增长,使得人们能够随时随地的访问网络,以获取最 ...

  6. spring boot在线投票系统 毕业设计源码141307

    基于springboot在线投票系统 摘 要 随着全球Internet的迅猛发展和计算机应用的普及,特别是近几年无线网络的广阔覆盖以及无线终端设备的爆炸式增长,使得人们能够随时随地的访问网络,以获取最 ...

  7. (附源码)springboot网上投票系统 毕业设计 282018

    基于springboot网上投票系统 摘 要 随着全球Internet的迅猛发展和计算机应用的普及,特别是近几年无线网络的广阔覆盖以及无线终端设备的爆炸式增长,使得人们能够随时随地的访问网络,以获取最 ...

  8. (附源码)spring boot网上投票系统 毕业设计 282018

    基于springboot网上投票系统 摘 要 随着全球Internet的迅猛发展和计算机应用的普及,特别是近几年无线网络的广阔覆盖以及无线终端设备的爆炸式增长,使得人们能够随时随地的访问网络,以获取最 ...

  9. spring boot网上投票系统毕业设计源码282018

    基于springboot网上投票系统 摘 要 随着全球Internet的迅猛发展和计算机应用的普及,特别是近几年无线网络的广阔覆盖以及无线终端设备的爆炸式增长,使得人们能够随时随地的访问网络,以获取最 ...

最新文章

  1. jmail反馈是否发送成功_如何在钉钉上自动发送定制消息或通知给同事?(10行代码搞定)...
  2. Android Fragment 你应该知道的一切
  3. RTP:实时应用程序传输协议
  4. 在 Linux 下确认 NTP 是否同步的方法
  5. 抽屉效果的实现(DrawerLayout和SlidingMenu的对比)
  6. 机器学习:随机森林算法及其实现
  7. 2020-11-04关于出现tomcat启动失败的一种原因
  8. 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式
  9. 魔兽世界联盟物价稳定的服务器,一个残酷的真相!在《魔兽世界》怀旧服,女生一般都爱选联盟...
  10. python封装exe后其机器能用么_python打包成exe格式后,在部分机子上没法运行
  11. spring.xml从外部文件引入数据库配置信息
  12. Atitit 资源类型的分类法规范MIME类型类型 目录 1.1. 一个MIME类型至少包括两个部分:一个类型(type)和一个子类型(subtype)。 1 1.2. 命名格式MIME类型包括一个
  13. unity序列帧优化—Addressables
  14. 淘客APP有哪些功能?
  15. wps怎么把ppt里的字体一起保存_WPS怎样将PPT中的文字导出为Word文档?
  16. 原生安卓X86 TV安装配置
  17. java自动装配_Spring中自动装配的4种方式
  18. Python爬取链家网上的房源信息
  19. C语言中getch()、getche()和getchar()
  20. DVWA [CSRF] 跨站请求伪造

热门文章

  1. 三维空间中绘制点、线、面、UV贴图,万能的BufferGeometry(three.js实战4)
  2. Visual Studio滚动条设置
  3. 乐MAX 乐视X900_官方线刷包_救砖包_解账户锁
  4. 【USACO-Chapter1-1.3】【贪心】Barn Repair
  5. 读NeurIPS 2018 accepted paper list的十点感想
  6. URP——着色器和材质——烘焙光照 Baked Lit
  7. java网关限流_基于网关GateWay实现限流-令牌桶 及原理解析
  8. 物联网通信原理第4章 中远距离无线通信技术
  9. linux没办法识别u盘,linux系统下不能识别U盘
  10. 2011.5.17 西二在线见面会总结