Contract-Development

燃氣消耗過多

  • March 28, 2022

我正在編寫我的第一個 Eth 合約,我遇到了一個問題,該buy方法的 gas 消耗(估計)非常高(在出現“超過最大 gas 限額”錯誤之前快速 > N 百萬 gas。)

為了快速,這個想法如下:

  • 有一張地圖(2D 地圖),您可以擁有多個區域(這裡稱為單位,這就是我維護“unitsToState”明顯映射的原因)。
  • 您可以一次購買多個相鄰區域,因此創建了一個“塊”。
  • 所以當你購買一個新的 Block 時,合約必須檢查里面的所有單元是否都是空的( 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(isizesize + 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[isizesize + j] = 1; }
} }


編輯:問題似乎主要來自“// Registrer units states.”之後的部分。但是......只要我需要這樣做,它並沒有真正幫助我......

我懷疑這只是做你正在做的事情的成本。IIRC,將零值更改為非零值的氣體成本為 20,000。一個 10x10 的塊需要儲存 1010=100 個,而 10020000 是 2,000,000 氣體。

假設這與您所看到的大致一致,這就是儲存這些數據的成本。

您的迭代過程正在增加相對於規模的互動成本。這不行。它太貴了,最終會失敗。更多關於為什麼這是反模式:https ://blog.b9lab.com/getting-loopy-with-solidity-1d51794622ad

這部分

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

可以通過設置資料結構來支持單步查找,重構為一步完成(以任何規模)。

// x =&gt; y =&gt; value
mapping(uint =&gt; mapping(uint =&gt; uint)) public xy;

隨時設置非零值。

這部分可以從設計中設計出來。

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

這是將所有內容設置為預設值1。您會自動免費獲得預設值0,因此使用它並創建自己的 getter 函式來實現偏移量。

function setThing(uint value) public {
 thing = value - 1;
}

function getThing() public returns(uint) {
 return thing + 1;
}

希望能幫助到你。

引用自:https://ethereum.stackexchange.com/questions/34349