当前位置: 首页 > news >正文

Solidity学习-投票合约示例

以下的合约有一些复杂,但展示了很多Solidity的语言特性。它实现了一个投票合约。 当然,电子投票的主要问题是如何将投票权分配给正确的人员以及如何防止被操纵。 我们不会在这里解决所有的问题,但至少我们会展示如何进行委托投票,同时,计票又是 自动和完全透明的 。

我们的想法是为每个(投票)表决创建一份合约,为每个选项提供简称。 然后作为合约的创造者——即主席,将给予每个独立的地址以投票权。

地址后面的人可以选择自己投票,或者委托给他们信任的人来投票。

在投票时间结束时,winningProposal() 将返回获得最多投票的提案。

代码如下:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;/// @title 委托投票
contract Ballot {// 定义投票者struct Voter {uint weight; // 计票的权重bool voted;  // 若为真,代表该人已投票address delegate; // 被委托人uint vote;   // 投票提案的索引}// 定义被投票者struct Proposal {bytes32 name;   // 简称(最长32个字节)uint voteCount; // 得票数}address public chairperson;// 这声明了一个状态变量,为每个可能的地址存储一个 `Voter`。mapping(address => Voter) public voters;// 一个 `Proposal` 结构类型的动态数组Proposal[] public proposals;/// 为 `proposalNames` 中的每个提案,创建一个新的(投票)表决constructor(bytes32[] memory proposalNames) {chairperson = msg.sender;voters[chairperson].weight = 1;//对于提供的每个提案名称,//创建一个新的 Proposal 对象并把它添加到数组的末尾。for (uint i = 0; i < proposalNames.length; i++) {// `Proposal({...})` 创建一个临时 Proposal 对象,// `proposals.push(...)` 将其添加到 `proposals` 的末尾proposals.push(Proposal({name: proposalNames[i],voteCount: 0}));}}// 授权 `voter` 对这个(投票)表决进行投票// 只有 `chairperson` 可以调用该函数。function giveRightToVote(address voter) external {// 若 `require` 的第一个参数的计算结果为 `false`,// 则终止执行,撤销所有对状态和以太币余额的改动。// 在旧版的 EVM 中这曾经会消耗所有 gas,但现在不会了。// 使用 require 来检查函数是否被正确地调用,是一个好习惯。// 你也可以在 require 的第二个参数中提供一个对错误情况的解释。require(msg.sender == chairperson,"Only chairperson can give right to vote.");//判断是否已经投过票了require(!voters[voter].voted,"The voter already voted.");require(voters[voter].weight == 0);voters[voter].weight = 1;}/// 把你的投票委托到投票者 `to`。function delegate(address to) external {// 传引用Voter storage sender = voters[msg.sender];require(sender.weight != 0, "You have no right to vote");require(!sender.voted, "You already voted.");require(to != msg.sender, "Self-delegation is disallowed.");// 委托是可以传递的,只要被委托者 `to` 也设置了委托。// 一般来说,这种循环委托是危险的。因为,如果传递的链条太长,// 则可能需消耗的gas要多于区块中剩余的(大于区块设置的gasLimit),// 这种情况下,委托不会被执行。// 而在另一些情况下,如果形成闭环,则会让合约完全卡住。while (voters[to].delegate != address(0)) {to = voters[to].delegate;// 不允许闭环委托require(to != msg.sender, "Found loop in delegation.");}// `sender` 是一个引用, 相当于对 `voters[msg.sender].voted` 进行修改Voter storage delegate_ = voters[to];// Voters cannot delegate to accounts that cannot vote.require(delegate_.weight >= 1);// Since `sender` is a reference, this// modifies `voters[msg.sender]`.sender.voted = true;sender.delegate = to;if (delegate_.voted) {// 若被委托者已经投过票了,直接增加得票数proposals[delegate_.vote].voteCount += sender.weight;} else {// 若被委托者还没投票,增加委托者的权重delegate_.weight += sender.weight;}}/// 把你的票(包括委托给你的票),/// 投给提案 `proposals[proposal].name`.function vote(uint proposal) external {Voter storage sender = voters[msg.sender];require(!sender.voted, "Already voted.");sender.voted = true;sender.vote = proposal;// 如果 `proposal` 超过了数组的范围,则会自动抛出异常,并恢复所有的改动proposals[proposal].voteCount += sender.weight;}/// @dev 结合之前所有的投票,计算出最终胜出的提案function winningProposal() public view returns (uint winningProposal_){uint winningVoteCount = 0;for (uint p = 0; p < proposals.length; p++) {if (proposals[p].voteCount > winningVoteCount) {winningVoteCount = proposals[p].voteCount;winningProposal_ = p;}}}  // 计算获胜的提案// function winningProposal() public view returns (uint winningProposal) {//     uint winningVoteCount = 0;//     for (uint p = 0; p < proposals.length; p++) {//         if (proposals[p].voteCount > winningVoteCount) {//             winningVoteCount = proposals[p].voteCount;//             winningProposal = p;//         }//     }// }// 调用 winningProposal() 函数以获取提案数组中获胜者的索引,并以此返回获胜者的名称//winnerName 函数调用 winningProposal() 函数,并使用其返回值来访问 proposals 数组。function winnerName() public view returns (bytes32 winnerName_){winnerName_ = proposals[winningProposal()].name;}}

测试

在你的合约中,构造函数需要一个参数 proposalNames,它是一个 bytes32 数组。因此,你需要在部署时提供这个参数。

在“Deploy & Run Transactions”面板中,找到“Deploy”按钮下方的输入框并输入提案名称列表。

例如,你可以输入以下内容:

["0x50726f706f73616c310000000000000000000000000000000000000000000000", "0x50726f706f73616c320000000000000000000000000000000000000000000000"]

在这里插入图片描述
部署成功:

在这里插入图片描述
查看生成的账户:
在 Deploy & Run Transactions 面板中,你会看到一个 ACCOUNT 下拉菜单。这个菜单中列出了所有生成的账户地址及其余额。你可以从中选择一个地址进行测试。

选择和复制账户地址:

点击 ACCOUNT 下拉菜单,选择一个账户。
复制选中的账户地址。

在这里插入图片描述

  1. 授权投票权 (giveRightToVote)
    在 giveRightToVote 的输入框中输入授权投票者的地址。

示例:

在这里插入图片描述
2. 委托投票 (delegate)
在 delegate 的输入框中输入被委托人的地址(可以是同一个账户或其他账户)
在这里插入图片描述

这里测试发现输入的都会这个提示,这个待验证处理

  1. 投票 (vote)
    在 vote 的输入框中输入提案索引。

在这里插入图片描述
4. 查看主席 (chairperson)
点击 chairperson 按钮查看合约的主席地址。

在这里插入图片描述
5. 查看提案信息 (proposals)
在 proposals 的输入框中输入提案索引。

在这里插入图片描述
点击 proposals 按钮查看提案的名称和票数。

在这里插入图片描述
在这里插入图片描述
6. 查看投票者信息 (voters)
在 voters 的输入框中输入投票者的地址。

在这里插入图片描述
7. 获胜提案名称 (winnerName)
点击 winnerName 按钮查看当前获胜提案的名称。

在这里插入图片描述
8. 计算获胜提案 (winningProposal)
点击 winningProposal 按钮查看当前获胜提案的索引。

在这里插入图片描述

http://www.lryc.cn/news/359909.html

相关文章:

  • 前端Vue自定义支付密码输入框键盘与设置弹框组件的设计与实现
  • 【QEMU中文文档】1.1 支持的构建平台
  • 摄影后期照片编辑工具:LrC2024 for Mac/win 中文激活版
  • 通关!游戏设计之道Day20
  • 2024年上半年软件设计师试题及答案(回忆版)--选择题
  • 5.28.1 使用卷积神经网络检测乳腺癌
  • 【JavaScript脚本宇宙】JavaScript日期处理神器: 6款顶级库解析
  • C++基础编程100题-002 OpenJudge-1.1-04 输出保留3位小数的浮点数
  • Linux挂载硬盘
  • 用户购物性别模型标签(USG)之决策树模型
  • Mock的用法
  • 内网-win1
  • 中国电子学会(CEIT)2023年09月真题C语言软件编程等级考试三级(含详细解析答案)
  • golang线程池ants-四种使用方法
  • Flutter开发效率提升1000%,Flutter Quick教程之对组件进行拖拽与接收
  • 揭秘小程序商城的团购奇迹:独特模式引领盈利新纪元
  • ssm_mysql_高校自习室预约系统(源码)
  • AI自动化办公:批量将Excel表格英文内容翻译为中文
  • PPT 隐藏开启对象图层
  • PHP火狼大灌篮游戏源码微信+手机wap源码带控制
  • 推荐几首听无数遍也听不腻的好歌(1)
  • 【全开源】Java短剧系统微信小程序+H5+微信公众号+APP 源码
  • 基于Springboot驾校预约平台小程序的设计与实现(源码+数据库+文档)
  • python列表基本运算
  • Pytorch实用教程:pytorch中nn.Linear()用法详解 | 构建多层感知机 | nn.Module的作用 | nn.Sequential的作用
  • 如何利用unicloud阿里云云函数实现文件包括图片或文件上传,unicloud云函数写法一览
  • Django序列化器中is_valid和validate
  • 关于Golang中自定义包的简单使用-Go Mod
  • Dijkstra求最短路篇二(全网最详细讲解两种方法,适合小白)(python,其他语言也适用)
  • Dijkstra求最短路篇一(全网最详细讲解两种方法,适合小白)(python,其他语言也适用)