如何管理Solidity中的大循环?

时间:2019-03-27 17:23:54

标签: ethereum solidity smartcontracts

所以我有这份合同,出资者可以通过getFundsByAddress函数检索特定活动的总金额。问题是,如果一个活动的创建者超过3万,则该合同无法执行代码,因为它需要经过3万次才能找到所有正确的地址

在Rinkeby网络中,它可以达到的最大循环为30k,然后返回0

如何解决此类情况?

contract CrowdFunding {
    struct Funder {
        address addr;
        uint amount;
    }

    struct Campaign {
        address beneficiary;
        uint numFunders;
        uint amount;
        mapping (uint => Funder) funders;
    }

    uint numCampaigns;
    Campaign[] public campaigns;

    function newCampaign() public returns (uint campaignID) {
        campaignID = campaigns.length++;
        Campaign storage c = campaigns[campaignID];
        c.beneficiary = msg.sender;
    }

    function contribute(uint _campaignID, uint _amount) public {
        Campaign storage c = campaigns[_campaignID];
        c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: _amount});
        c.amount += 100;
    }

    // not tested
    function getFundsByAddress() public view returns (uint[] memory) {
        Campaign storage c = campaigns[0];
        uint cont = c.numFunders;

        uint[] memory allAmount = new uint[](TotalAmountOfUser);

        uint counter = 0;

        for (uint i=0; i < cont; i++) {
           if (c.funders[counter].addr == msg.sender) {
               allAmount[amountCont] = c.funders[counter].amount;
           }
           counter++;
        }

        return allAmount;
    }   
}

2 个答案:

答案 0 :(得分:0)

我看不到任何特别的数字30K可以解释这一点。

您的问题可能是交易要么用光了汽油,要么达到了限制汽油限额。如果您必须遍历数组而不能以任何其他方式执行遍历,则应考虑在多个事务中遍历数组(即0-9999,10.000-19.999,...)。

但是,就气体而言,遍历如此众多的条目将是非常昂贵的,而在实际网络上,这是要花钱的。但是,如果不能以其他方式完成此操作,那么上面的内容应该会对您有所帮助。

答案 1 :(得分:0)

很难猜测getFundsByAddress应该做什么,因为代码没有编译并且循环似乎也无能为力。 (从未使用循环变量i。)

但是,如果我不得不猜测的话,它应该返回调用者贡献的总和。如果是这种情况,请在进行捐款时跟踪该总数,并完全避免循环:

mapping(address => uint256) public totalContributions;

function contribute(uint _campaignID, uint _amount) public {
    ...

    // Add this line to keep track of the total.
    totalContributions[msg.sender] += _amount;
}

// No need for getFundsByAddress at all because a call to `totalContributions(address)`
// (the auto-generated getter) does the trick.

// But if you want a function that returns specifically `msg.sender`'s total:
function getMyContributions() external view returns (uint256) {
    return totalContributions[msg.sender];
}