燃氣消耗過多
我正在編寫我的第一個 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 <= toX; i++) { for (uint j = fromY; j <= toY; j++) { require(getUnitsToState(i*size*size + j) == 0); } }
可以通過設置資料結構來支持單步查找,重構為一步完成(以任何規模)。
// x => y => value mapping(uint => mapping(uint => uint)) public xy;
隨時設置非零值。
這部分可以從設計中設計出來。
// Registrer units states. for (i = fromX; i <= toX; i++) { for (j = fromY; j <= 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; }
希望能幫助到你。