这篇文章主要介绍“以太坊怎么实现中心化投票DApp与智能合约”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“以太坊怎么实现中心化投票DApp与智能合约”文章能帮助大家解决问题。
专注于为中小企业提供成都网站设计、网站建设服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业福贡免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了1000多家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
从本质上讲,利用区块链技术的去中心化应用程序允许你在没有可信赖的第三方的情况下执行与今天相同的操作(如转移资金)。最好的dApp具有特定的真实世界的用例,以便利用区块链的独特特征。
从本质上讲,区块链是一个共享的,可编程的,加密安全的,可信赖的分类账本,没有任何一个用户可以控制,任何人都可以查询。- Klaus Schwab
即使投票应用对大家来说可能不是一个伟大的应用程序,但是我选择使用它作为本指南,这是因为区块链解决的主要问题:透明度,安全性,可访问性,可信任,是困扰当前民主选举的主要问题。
由于区块链是去中心化的交易(投票)的永久记录,因此每次投票都可以无可辩驳地追溯到它发生的时间和地点,而不会泄露选民的身份。此外,过去的投票也不能被改变,而现在也不能被黑客攻击,因为每个交易都是由网络中的每个节点验证的。任何外部或内部攻击者必须控制51%的节点才能改变记录。
即使攻击者能够在伪造用户输入真实身份证投票时也能实现这一点,但端到端投票系统可以让选民验证他们的投票是否在系统中正确输入,这使得系统极其安全。
我希望你读本指南的其余部分前,了解了区块链和以太坊。这里有一个很棒的指南,写了我想让你知道的核心组件的简要概述。
智能合约充当后端逻辑和存储。合约是用Solidity一种智能合约语言编写的,是一个代码和数据的集合,驻留在以太坊区块链的特定地址。它与面向对象编程中的类非常相似,它包含函数和状态变量。智能合约以及区块链是所有权力下放应用程序的基础。像Blockchain一样,它们是不可变的和分布式的,这意味着如果它们已经在以太坊网络上,升级它们将是一种痛苦。幸运的是,这里有一些方法可以做到这一点。
以太坊虚拟机(EVM)处理整个以太坊网络的内部状态和计算。将EVM视为这种大规模去中心化计算机,其中包含能够执行代码,更改数据和相互交互的addresses
。
Web3.js是一个Javascript API,允许你与区块链进行交互,包括进行交易和调用智能合约。此API抽象了与以太坊客户端的通信,允许开发人员专注于他们的应用程序的内容。你必须在浏览器中嵌入一个web3实例才能执行此操作。
Truffle是以太坊的流行测试开发框架。它包括开发区块链,编译和迁移脚本,用于将合约部署到区块链,合约测试等。它使开发更容易!
Truffle Contracts是Web3 Javascript API之上的抽象,允许你轻松连接智能合约并与之互动。
Metamask将以太坊带入你的浏览器。它是一个浏览器扩展,提供链接到你的以太坊地址的安全web3实例,允许你使用去中心化应用程序。我们不会在本教程中使用Metamask,但它是人们在生产中与DApp交互的一种方式。相反,我们将在开发期间注入我们自己的web3实例。有关更多信息,请查看此链接。
为简单起见,我们实际上不会构建我之前描述的完整投票系统。为了便于说明,它只是一个单页应用程序,用户可以输入他们的ID并为候选人投票。还将有一个按钮,计算并显示每个候选人的投票数。
这样,我们将能够专注于在应用程序中创建智能合约并与之交互的过程。整个应用程序的源代码将在此存储库中,你需要安装Node.js和npm。
npm install -g truffle
要使用Truffle命令,必须在现有项目中运行它们。
git clone https://github.com/tko22/truffle-webpack-boilerplate cd truffle-webpack-boilerplate npm install
这个存储库只是一个Truffle Box的框架,它是可以在一个命令中获得的样板或示例应用程序 - truffle unbox [box name]
。但是,带有webpack的Truffle box未使用最新版本进行更新,并包含一个示例应用程序。因此,我创建了这个repo。
你的目录结构应包括以下内容:
contracts/
:包括所有合约的文件夹。不要删除Migrations.sol
。
migrations/
:包含Migration files
的文件夹,可帮助你将智能合约部署到区块链中。
src/
:保存应用程序的HTML/CSS和Javascript文件。
truffle.js
:truffle配置文件。
build/
:在编译合约之前,你不会看到此文件夹。此文件夹包含构建文件,因此不要修改任何这些文件!构建文件描述了合约的功能和体系结构,并提供了有关如何与区块链中的智能合约进行交互的Truffle Contracts和web3信息。
设置和介绍完,让我们开始写代码吧!首先,我们将编写我们的智能合约,这是用Solidity编写的(其他语言不那么受欢迎)。这可能看起来很不爽,但事实并非如此。
对于任何应用程序,你希望智能合约尽可能简单,甚至是非常简单。请记住,你必须为你所做的每笔计算/交易付费,而你的智能合约将永远存在于区块链中。所以,你真的希望它能够完美地运作——也就是说,它越复杂,就越容易犯错误。
我们的合约将包括:
状态变量:包含永久存储在区块链中的值的变量。我们将使用状态变量来保存选民和候选人的名单和数量。
函数:函数是智能合约的可执行文件。它们是我们要求与区块链进行交互的内容,具有不同级别的内部和外部可见性。请记住,无论何时你想要更改变量的值/状态,都必须进行交易——这要耗费以太币。你也可以calls
区块链,这不会花费任何以太,因为你所做的更改将被销毁(当我们实际进行transactions
和call
时,在下面会有更多内容)。
事件:每当调用事件时,传递给事件的值都将记录在交易日志中。这允许Javascript回调函数或已解析的promises查看你想要在交易之后传回的特定值。这是因为每次进行交易时,都会返回交易日志。我们将使用一个事件来记录新创建的候选者的ID,我们将显示该ID。
结构类型 - 这与C结构非常相似。Structs允许你保存多个变量,并且对于具有多个属性的事物非常棒。Candidates
只会有他们的名字和党派,但你绝对可以为他们添加更多属性。
映射 - 将它们视为hash映射或字典,它具有键值对。我们将使用两个映射。
这里没有列出更多类型,但有些类型稍微复杂一些。这五个包含了智能合约通常使用的大部分结构。这里将更深入地解释这些类型。
作为参考,这是智能合约的代码。请注意,此文件应该被称为Voting.sol
但我希望Github gist
具有style
,所以我给它一个.js扩展名。与本指南的其余部分一样,我将在代码中提供注释解释它正在做什么,然后我将在指出某些警告和逻辑的同时解释大体思路。
pragma solidity ^0.4.18; // written for Solidity version 0.4.18 and above that doesnt break functionality contract Voting { // an event that is called whenever a Candidate is added so the frontend could // appropriately display the candidate with the right element id (it is used // to vote for the candidate, since it is one of arguments for the function "vote") event AddedCandidate(uint candidateID); // describes a Voter, which has an id and the ID of the candidate they voted for struct Voter { bytes32 uid; // bytes32 type are basically strings uint candidateIDVote; } // describes a Candidate struct Candidate { bytes32 name; bytes32 party; // "bool doesExist" is to check if this Struct exists // This is so we can keep track of the candidates bool doesExist; } // These state variables are used keep track of the number of Candidates/Voters // and used to as a way to index them uint numCandidates; // declares a state variable - number Of Candidates uint numVoters; // Think of these as a hash table, with the key as a uint and value of // the struct Candidate/Voter. These mappings will be used in the majority // of our transactions/calls // These mappings will hold all the candidates and Voters respectively mapping (uint => Candidate) candidates; mapping (uint => Voter) voters; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * These functions perform transactions, editing the mappings * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ function addCandidate(bytes32 name, bytes32 party) public { // candidateID is the return variable uint candidateID = numCandidates++; // Create new Candidate Struct with name and saves it to storage. candidates[candidateID] = Candidate(name,party,true); AddedCandidate(candidateID); } function vote(bytes32 uid, uint candidateID) public { // checks if the struct exists for that candidate if (candidates[candidateID].doesExist == true) { uint voterID = numVoters++; //voterID is the return variable voters[voterID] = Voter(uid,candidateID); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * Getter Functions, marked by the key word "view" * * * * * * * * * * * * * * * * * * * * * * * * * * */ // finds the total amount of votes for a specific candidate by looping // through voters function totalVotes(uint candidateID) view public returns (uint) { uint numOfVotes = 0; // we will return this for (uint i = 0; i < numVoters; i++) { // if the voter votes for this specific candidate, we increment the number if (voters[i].candidateIDVote == candidateID) { numOfVotes++; } } return numOfVotes; } function getNumOfCandidates() public view returns(uint) { return numCandidates; } function getNumOfVoters() public view returns(uint) { return numVoters; } // returns candidate information, including its ID, name, and party function getCandidate(uint candidateID) public view returns (uint,bytes32, bytes32) { return (candidateID,candidates[candidateID].name,candidates[candidateID].party); } }
基本上,我们有两个Structs(包含多个变量的类型),用于描述选民和候选人。使用Structs,我们可以为它们分配多个属性,例如电子邮件,地址等。
为了跟踪选民和候选人,我们将它们放入单独的映射中,它们是整数索引的。候选人或选民的索引/密钥——让我们称之为ID——是函数访问它们的唯一方式。
我们还会跟踪选民和候选人的数量,这将有助于我们为他们编制索引。此外,不要忘记第8行中的事件,该事件将在添加时记录候选人的ID。我们的界面将使用此事件,因为我们需要跟踪候选人的ID以便为候选人投票。
1.我知道,与我之前所说的关于使合约变得非常简单的说法相反,我认为这个合约与这个应用实际上做的相比有点复杂。但是,我这样做是为了让你们更容易进行编辑并在之后为此应用程序添加功能(最后更多内容)。如果你想制作一个更简单的投票应用程序,智能合约可以在不到15行代码。
2.请注意,状态变量numCandidates
和numVoters
未声明为public。默认情况下,这些变量具有internal
可见性,这意味着它们只能由当前合约或派生合约直接访问(不用担心,我们不会使用它)。
3.我们使用32bytes
用于字符串而不是使用string
类型。我们的EVM具有32字节的字大小,因此它被optimized
以处理32字节的块中的数据。(当数据不是32字节的块时,编译器,例如Solidity,必须做更多的工作并生成更多的字节码,这实际上会导致更高的天然气成本。)
4.当用户投票时,会创建一个新的Voter结构并将其添加到映射中。为了计算某个候选人的投票数,你必须遍历所有选民并计算投票数。候选人的行为相同。因此,这些映射将保留所有候选人和选民的历史。
完成我们的智能合约后,我们现在需要运行我们的测试区块链并将此合约部署到区块链上。我们还需要一种方法来与它交互,这将通过web3.js完成。
在我们开始测试区块链之前,我们必须在/contracts
文件夹中创建一个名为2_deploy_contracts.js
的文件,告诉它在迁移时包含你的投票智能合约。
var Voting = artifacts.require("Voting") module.exports = function(deployer) { deployer.deploy(Voting) }
要开始开发以太坊区块链,请转到命令行并运行:
truffle develop
由于Solidity是一种编译语言,我们必须首先将其编译为字节码,以便EVM执行。
compile
你现在应该在目录中看到一个文件夹build/
。此文件夹包含构建文件,这对Truffle的内部工作至关重要,因此请勿修改它们!
接下来,我们必须迁移合约。migrations是一个truffle脚本,可帮助你在开发时更改应用程序合约的状态。请记住,你的合约已部署到区块链上的某个地址,因此无论何时进行更改,你的合约都将位于不同的地址。 迁移可帮助你执行此操作,还可帮助你移动数据。
migrate
恭喜!你的智能合约现在永远在区块链上。好吧,还不是真的...... 因为truffle develop会在每次停止时刷新。
如果你想拥有一个持久的区块链,可以考虑一下由Truffle开发的Ganache。如果你使用的是Ganache,则无需调用truffle develop。相反,你将运行truffle compile
和truffle migrate
。要了解在没有Truffle的情况下部署合约需要什么,请查看此博客文章。
一旦我们将智能合约部署到区块链,我们将不得不在应用程序启动时在浏览器上使用Javascript设置web3.0实例。因此,下一段代码将放在js/app.js
的底部。请注意,我们使用的是web3.0版本0.20.1。
// When the page loads, we create a web3 instance and set a provider. We then set up the app window.addEventListener("load", function() { // Is there an injected web3 instance? if (typeof web3 !== "undefined") { console.warn("Using web3 detected from external source like Metamask") // If there is a web3 instance(in Mist/Metamask), then we use its provider to create our web3object window.web3 = new Web3(web3.currentProvider) } else { console.warn("No web3 detected. Falling back to http://localhost:9545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask") // fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail) window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:9545")) } // initializing the App window.App.start() })
如果你不理解这段代码,你真的不必太担心。只要知道这将在应用程序启动时运行,并将检查浏览器中是否已存在web3实例(Metamask)。如果没有,我们将创建一个与localhost:9545
交互Truffle开发区块链。
如果你正在使用Ganache,你必须将端口更改为7545
.一旦创建了一个实例,我们将调用start
函数。
我们需要做的最后一件事是为应用程序编写接口。这涉及任何Web应用程序的基本要素——HTML,CSS和Javascript(我们已经编写了一些用于创建web3实例的Javascript)。首先,让我们创建我们的HTML文件。
Ethereum Voting Dapp Ethereum Voting Dapp
Add ID and click candidate to vote