Solidity学习-投票合约示例

news2024/11/17 15:42:03

以下的合约有一些复杂,但展示了很多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.coloradmin.cn/o/1721460.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决安装 WP Super Cache 插件提示 Advanced-Cache.Php 是另一个插件创建的

昨天晚上一个站长求助明月&#xff0c;说是安装 WP Super Cache 插件的时候提示 advanced-cache.php 被占用了&#xff0c;无法完成安装&#xff0c;收到截图看了才明白原来提示的是“advanced-cache.php 文件&#xff0c;由另一个插件或者系统管理员创建的”&#xff0c;如下图…

【AI界的狼人杀】人工智能“反图灵测试”实验

5月28日&#xff0c; AI Power Users、Unity 独立开发者&#xff1a;Tore Knabe 在其社交平台发布了一则名为《Reverse Turing Test Experiment with AIs》的视频&#xff0c;立马引发了热议。 视频中共出现了五位主要角色&#xff0c;“他们”分别是&#xff1a;亚里士多德&am…

Keil 5恢复默认布局,左边状态栏

第一步&#xff0c;点击windows&#xff1a; 第二步&#xff0c;点击reset view to default&#xff1a; 第三步&#xff0c;点击reset即可&#xff1a;

狗狗的最爱?揭秘福派斯鸭肉梨狗粮的魔力!

福派斯鸭肉梨全价狗粮之所以能够在宠物食品市场中脱颖而出&#xff0c;并赢得广大宠物主人的一致好评和青睐&#xff0c;主要归功于其科学配比且富含营养价值的独特配方&#xff0c;尤其是在狗狗去泪痕这块的效果显著。这款狗粮以优质鸭肉为主要成分&#xff0c;鸭肉含有丰富的…

Java内存模型----JMM

Java内存模型 1. 前言2. 主内存与工作内存3. JMM解决什么问题&#xff1f;4. JMM内存交互5. Happens-Before1. 程序的顺序性规则2. volatile 变量规则3. 管程中锁的规则4. 线程启动规则5. 线程join规则6. 其他规则 1. 前言 内存模型这个概念。我们可以理解为&#xff1a; 在特定…

springboot基础及上传组件封装

简介 本文主要以文件上传为demo&#xff0c;介绍了一些 springboot web 开发的入门的技术栈。 对应刚接触 springboot 的可以参考下。 主要包括文件md5比对、生成图片缩略图、数据库迁移、文件记录持久化、请求全局异常处理等功能。 准备工作 在 idea 中创建项目&#xff…

PPP认证两种:PAP和CHAP,两次握手和三次握手

CHAP&#xff08;Challenge-Handshake Authentication Protocol&#xff0c;质询握手认证协议&#xff09;的设计理念是增强网络认证过程的安全性。在CHAP的三次握手过程中&#xff0c;不直接传送用户的明文密码&#xff0c;以此来提高安全性&#xff0c;具体步骤如下&#xff…

社交媒体数据恢复:QQ空间

本教程将指导您如何恢复QQ空间中的说说、日志和照片等内容。请注意&#xff0c;本教程不涉及推荐任何数据恢复软件。 一、恢复QQ空间说说 登录您的QQ账号&#xff0c;并进入QQ空间。点击“日志”选项&#xff0c;进入空间日志页面。在空间日志页面&#xff0c;您会看到一个“…

MyBatis源码分析--02:SqlSession建立过程

我们再来看看MyBatis使用流程&#xff1a; InputStream inputStream Resources.getResourceAsStream("myBatis_config.xml"); SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream); SqlSession session sqlSessionFactory.op…

【Rust日报】关于在其它语言中(特别是新语言中)能否直接调用Rust现有生态的研究...

关于在其它语言中&#xff08;特别是新语言中&#xff09;能否直接调用Rust现有生态的研究 直接调用&#xff0c;也就是不用写FFI绑定库。这篇文章讨论了其中的一些目前的困难&#xff0c;可以操作的一些方法。 本篇是第一篇&#xff1a;https://verdagon.dev/blog/exploring-s…

数学建模 —— 插值与拟合(1)

一、matlab画图 1.1 plot&#xff08;二维图形&#xff09; plot(x) —— 缺省自变量绘图格式 plot(x,y) —— 基本格式&#xff0c;以y(x)的函数关系作出直角坐标图&#xff0c;如果y为nm的矩阵&#xff0c;则以x为自变量&#xff0c;作出m条曲线 plot(x1,y1,x2,y2,…,xn,…

Lesson6--排序(初级数据结构完结篇)

【本节目标】 1. 排序的概念及其运用 2. 常见排序算法的实现 3. 排序算法复杂度及稳定性分析 1.排序的概念及其运用 1.1排序的概念 排序 &#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来…

GPT LoRA 大模型微调,生成猫耳娘

往期热门专栏回顾 专栏描述Java项目实战介绍Java组件安装、使用&#xff1b;手写框架等Aws服务器实战Aws Linux服务器上操作nginx、git、JDK、VueJava微服务实战Java 微服务实战&#xff0c;Spring Cloud Netflix套件、Spring Cloud Alibaba套件、Seata、gateway、shadingjdbc…

MACOS安装 vue 抱错解决方法npm ERR! code EACCESnpm ERR! syscall mkdirnpm ERR!

问题 在使用脚手架 vue-cli 创建 vue 工程的时候存在权限不足的情况下&#xff0c;报错&#xff1b; npm error code EACCES npm error syscall open npm error path /Users/ npm ERR! code EACCESnpm ERR! syscall mkdirnpm ERR! 解决方案&#xff1a; sudo npm cache cl…

什么是最好的手机数据恢复软件?6 款手机数据恢复软件 [2024 年更新]

什么是最好的手机数据恢复软件&#xff1f;在这篇文章中&#xff0c;您将了解 6 款最好的免费手机数据恢复软件&#xff0c;并学习如何恢复数据的完整指南。 最好的手机数据恢复软件是什么&#xff1f; 手机数据恢复软件是恢复智能手机中丢失或删除的文件、消息、照片和其他宝…

反射获取成员变量

目录 利用反射获取成员变量 ​编辑 代码实现 获取class对象 获取成员变量 获取单个成员变量 获取成员变量的名字 获取权限修饰符 获取成员变量的数据类型 获取成员变量记录的值 修改对象里面记录的值 利用反射获取成员变量 代码实现 Student类&#xff1a; 获取clas…

【 0 基础 Docker 极速入门】镜像、容器、常用命令总结

Docker Images&#xff08;镜像&#xff09;生命周期 Docker 是一个用于创建、部署和运行应用容器的平台。为了更好地理解 Docker 的生命周期&#xff0c;以下是相关概念的介绍&#xff0c;并说明它们如何相互关联&#xff1a; Docker&#xff1a; Docker 是一个开源平台&#…

生成ssh密钥,使用ssh连接linux系统

这里写目录标题 ssh密钥大概介绍1、密钥在哪里生成&#xff08;客户端/服务器&#xff09;&#xff1f;2、密钥生成是什么样子的&#xff1f; ssh &#xff08;生成密钥、密钥传输、配置连接、连接服务&#xff09;过程1、生成密钥提示一&#xff1a;输入保存密钥的文件&#x…

Nginx实战:nginx支持带下划线的header

nginx对header 的名字字符做了限制&#xff0c;默认 underscores_in_headers 为off&#xff0c;表示如果header name中包含下划线&#xff0c;则忽略掉&#xff0c;后端服务就获取不到该请求头。 为了支持header带下划线的参数&#xff0c;可以在http内或者server内设置如下参数…

ip地址告诉别人安全吗?ip地址告诉别人会有什么风险

IP地址告诉别人安全吗&#xff1f;在数字化时代&#xff0c;IP地址作为网络连接的关键标识符&#xff0c;承载着重要的安全意义。然而&#xff0c;很多人可能并不清楚&#xff0c;轻易地将自己的IP地址告诉他人可能带来一系列安全风险。那么&#xff0c;IP地址告诉别人会有什么…