You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Solidity中结构体数组for循环失效导致投票函数回滚问题

投票合约问题修复方案

核心问题分析

你的voterRules修饰器存在两处逻辑错误,导致已注册选民调用vote函数时触发回滚:

1. 选民身份验证逻辑错误

原代码遍历voterList时,只要有一个选民地址与调用者不匹配就触发回滚,这意味着只有当列表中所有地址都等于调用者时才通过验证,完全违背了"检查调用者是否在选民列表中"的需求。正确逻辑应该是遍历列表寻找匹配地址,若全程未找到则回滚。

2. 重复投票验证逻辑错误

原代码遍历candidateList检查voterToCandidate[msg.sender]是否等于任何候选人地址,会导致两种错误:

  • 若存在候选人地址为0x0,未投票的调用者(映射默认值为0x0)会被误判为已投票
  • 已投票的调用者会触发所有候选人的检查,只要匹配就回滚,逻辑冗余且错误

修复后的完整合约代码

//SPDX-License-Identifier:UNLICENSED
pragma solidity ^0.8.0;

contract Ballot{
    // VARIABLES
    address private chairperson;

    // MODIFIERS
    modifier isOwner(){
        require(msg.sender == chairperson);
        _;
    }

    modifier isVoter(address voterAddress_){
        require(voterAddress_ == msg.sender,"The voter must register by itself...");
        _;
    }

    modifier voterRules(){
        require(msg.sender != chairperson,"Chairperson cannot vote!!");
        
        // 修复:验证调用者是否在选民列表中
        bool isRegistered = false;
        for(uint m=0;m<voterList.length;m++){
            if(msg.sender == voterList[m]._voterAddress){
                isRegistered = true;
                break;
            }
        }
        require(isRegistered, "You must register yourself first...");

        // 修复:验证是否已投票(使用Voter结构体的_isVoted字段更可靠)
        bool hasVoted = false;
        for(uint n=0;n<voterList.length;n++){
            if(msg.sender == voterList[n]._voterAddress && voterList[n]._isVoted){
                hasVoted = true;
                break;
            }
        }
        require(!hasVoted, "You cannot vote twice...");
        
        _;
    }

    // CONSTRUCTOR
    constructor(){
        chairperson = msg.sender;
    }

    // STRUCTS
    struct Candidate{
        address _address;
        uint256 _noOfVotes;
    }

    struct Voter{
        address _voterAddress;
        bool _isVoted;
    }
    
    // ARRAYS
    Candidate[] public candidateList;
    Voter[] public voterList;
    
    // MAPPINGS
    mapping(address=>address) private voterToCandidate;
    
    // FUNCTIONS
    function addCandidate(address address_) public isOwner{
        for(uint i=0;i<candidateList.length;i++){
            require(address_ != candidateList[i]._address, "Same Candidate cannot be added twice");
        }
        candidateList.push(Candidate(address_,0));
    }

    function registerVoter(address voterAddress_) external isVoter(voterAddress_){
        for(uint j=0;j<voterList.length;j++){
            require(voterAddress_ != voterList[j]._voterAddress,"Voter already registered");
        }
        voterList.push(Voter(voterAddress_,false));
    }

    function vote(uint256 _candId) public voterRules() {
        // 额外添加:验证候选人ID是否合法,防止越界访问
        require(_candId < candidateList.length, "Invalid candidate ID");
        
        voterToCandidate[msg.sender] = candidateList[_candId]._address;
        candidateList[_candId]._noOfVotes++;
        
        // 更新Voter结构体的_isVoted状态
        for(uint p=0;p<voterList.length;p++){
            if(msg.sender == voterList[p]._voterAddress){
                voterList[p]._isVoted = true;
                break;
            }
        }
    }
}

额外优化说明

  1. 为vote函数添加候选人ID合法性检查,避免数组越界访问
  2. 改用Voter结构体中的_isVoted字段验证重复投票,比依赖映射更可靠,规避了候选人地址为0x0的误判风险
  3. 优化注册函数的错误提示信息,表述更清晰明确

内容的提问来源于stack exchange,提问作者VIVEK T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 22:15:36