1

我正在编写我的第一个 Eth 合约,我遇到了一个问题,该buy方法的 gas 消耗(估计)非常高(在出现“超过最大 gas 限额”错误之前快速 > N 百万 gas。)

快速地说,这个想法如下: - 有一张地图(2D 地图),您可以拥有多个区域(这里称为单位,这就是我维护“unitsToState”明显映射的原因)。- 您可以一次购买多个相邻区域,因此创建了一个“块”。- 所以当你购买一个新的区块时,合约必须检查里面的所有单元是否都是空的( unitsToState[x] == 0 )。购买块时,这些状态设置为 1。

我在这里不解释太多细节,因为我猜问题主要是我的“Solidity”糟糕的算法编程。

这个方法可以用代表一个小区域的 fromX、fromY、toX、toY 的 500k gas 来执行,但是当它们彼此相距很远时,我在 gas 估计过程中得到“超出最大 gas 允许”错误。所以真的估计有问题。。

``` ...

struct Block {
    address owner;
    uint fromX;
    uint fromY;
    uint toX;
    uint toY;
    string imageUrl;
    string redirectUrl;
    string text;
    bool removed;
}   

uint size = 100;
mapping (uint => uint) unitsToState;
Block[] public blocks;
uint public areaPrice;
uint public areaPerUnit;

...


function buy(uint fromX, uint fromY, uint toX, uint toY, string imageUrl, string redirectUrl, string text) payable public {
    require(fromX >= 0);
    require(fromY >= 0);
    require(fromX <= toX);
    require(fromY <= toY);
    require(toX < size);
    require(toY < size);

    // Here do check of collisions.
    for (uint i = fromX; i <= toX; i++) {
        for (uint j = fromY; j <= toY; j++) {
            require(getUnitsToState(i*size*size + j) == 0);
        }    
    }

    uint width = toX - fromX + 1;
    uint height = toY - fromY + 1;
    uint areaCount = width * height * areaPerUnit;
    uint price = areaCount * areaPrice;
    require(msg.value >= price);

    Block memory b = Block(
       msg.sender,
       fromX,
       fromY,
       toX,
       toY,
       imageUrl,
       redirectUrl,
       text,
       false
    );
    blocks.push(b);

    // Registrer units states.
    for (i = fromX; i <= toX; i++) {
        for (j = fromY; j <= toY; j++) {
            unitsToState[i*size*size + j] = 1;
        }    
    }
}

...

```

4

1 回答 1

0

大循环,尤其是嵌套循环,在 Solidity 中是危险的。你必须小心你在每次迭代中执行的逻辑。

在您的情况下,这是您设置unitsToState标志的第二个循环,导致天然气使用量大幅飙升。每次调用存储非零数据的费用为 20,000 gas(零为 5,000)。您在循环的每次迭代中都会产生这种成本。

看起来您拥有在Block结构内进行碰撞检查所需的一切。你能用那个吗?

for (uint8 i = 0; i < blocks.length; i++) {
  Block storage bTest = blocks[i];

  bool xIntersect = int(fromX) - int(bTest.toX) <= 0 && int(toX) - int(bTest.fromX) >= 0;
  bool yIntersect = int(fromY) - int(bTest.toY) <= 0 && int(toY) - int(bTest.fromY) >= 0;

  require (!(xIntersect && yIntersect));
}

有关gas 费用,请参阅黄皮书的附录 G。

于 2017-12-25T23:56:30.840 回答