Solidity

根據循環優化刪除數組的索引

  • June 17, 2018

我有一個 dapp,管理員可以在其中上傳許多蛋糕(20 到 30 個)。人們可以進入網站並決定是否要購買管理員上傳的蛋糕。

所以對於智能合約,我做了一個數組uint來儲存管理員上傳的每id一個。Cake一旦買家購買了其中一個蛋糕,它將刪除數組中的那個idCake我決定刪除它以盡可能避免循環,因為在我迭代數組以將剩餘的蛋糕列表返回給買家時,它會導致氣體耗盡。在優化方面聽起來如何?

下面是我的實現。有沒有更好的解決方案,或者這足以繼續嗎?

struct Cake {
   uint id;
   address buyer;
}

mapping (uint => Cake) public cakes;
uint[] public cakeIds;

function addNewCake(uint _id) public {
   cakes[_id] = Cake(_id, 0x0);

   cakeIds.push(_id);
}

function buyCake(uint _id) public {      
   Cake storage cake = cakes[_id];       
   cake.buyer = msg.sender;

   removeCakeInArray(_id);
}

function removeCakeInArray(uint _id) private {
   for (uint i = 0; i <= getNumOfCakes(); i++) {            
       if (cakeIds[i] == _id) {
           remove(i);                 
       }    
   }
}

function remove(uint index) private {
   if (index >= getNumOfCakes()) return;

   for (uint i = index; i < getNumOfCakes() - 1; i++){
       cakeIds[i] = cakeIds[i+1];
   }
   cakeIds.length--;
}

function getAllUnsoldCakes() public view returns (uint[]) {
   uint length = getNumOfCakes();  
   uint[] memory ids = new uint[](length);

   for (uint i = 0; i < length; i++) {       
       uint cakeId = cakeIds[i];
       Cake memory cake = cakes[cakeId];

       if (cake.buyer == 0x0) {
           ids[i] = cake.id;             
       }
   }

   return ids;
}

function getNumOfCakes() public view returns (uint) {
   return cakeIds.length;
}

這些循環,特別是在 中remove(),是一個危險信號:你的數組可以增長到無限長度,包括如果有人惡意增加它,你的合約將達到 gas 限制並變得不可用。僅當您可以明確限制列表的長度時,這才可行 - 例如,如果有人嘗試添加第 11 個未售出的蛋糕,revert即交易,因為蛋糕店的貨架已滿。

如果您確實需要能夠使用solidity 函式呼叫檢索未售出的蛋糕列表,則更安全的方法是使用鍊錶,因為無論列表的長度如何,都可以以相同的成本進行更新。這仍然會讓您在讀取列表時擔心 gas 成本,但如果它僅用於從節點讀取數據,而不是作為事務的一部分被呼叫,那可能沒問題。

但是,您真的需要能夠通過合約函式呼叫獲得未售出的蛋糕列表嗎?將這項工作轉移到其他地方可能會更好 - 例如,當您添加或出售蛋糕時記錄事件,然後從您的 dapp javascript 中讀取這些事件並使用它來建構您向使用者顯示的列表。

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