我正在编写我的第一份Eth合同,我遇到了一个问题,buy
方法的耗气量(估算值)真的很高(在达到“最大值”之前快速> N百万气体燃气余量超过“错误。”
要快点,想法如下: - 有一个地图(2D地图),你可以拥有多个区域(在这里称为单位,这就是为什么我保持“unitsToState”明显的映射)。 - 您可以一次购买多个相邻区域,因此会创建“块”。 - 因此,当您购买新的块时,合约必须检查内部的所有单位是否为空(unitsToState [x] == 0)。购买该块时,这些状态将设置为1.
我在这里不解释太多细节,因为我猜这个问题主要是“Solidity”糟糕的算法编程。
这种方法可以用500k左右的气体进行,从X,从Y,到X,到Y代表一个小区域,但是当它们相距很远时,我在气体估算期间得到了“超出最大气体容量”误差。真的,我觉得有问题..
``` ...
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;
}
}
}
...
```
答案 0 :(得分:0)
大循环,特别是嵌套循环,在Solidity中是危险的。你必须要小心你在每次迭代时执行的逻辑。
在您的情况下,它是您设置unitsToState
标志的第二个循环,导致燃气使用量大幅增加。存储非零数据的每次调用都要花费20,000气体(零为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));
}
请参阅Appendix G of the yellow paper了解天然气费用。