Solidity

如何從 web3 訪問動態列表?

  • April 3, 2022

在我的應用程序中,玩家可以創建和加入遊戲。我將每個遊戲都儲存在一個映射中(uint => Game),並在創建遊戲時發送一個 GameCreated 事件。我的問題是有效地在客戶端獲取開放(尚未加入)遊戲的列表。

我可以遍歷所有 Game 對象並過濾掉那些已加入的對象,但這是不可擴展的。起初我認為監聽 GameCreated 事件會有所幫助,但我仍然必須使用 GameJoined 事件進行過濾,因為我認為我不能修改已經記錄的日誌。這具有相同的可擴展性問題。我可以通過僅獲取過去一周的日誌或其他方式來緩解它,但我不太喜歡這個選項。

我考慮製作一個動態大小的數組,其中每個項目都是打開遊戲的遊戲 ID,但為了更新這個列表,我需要能夠在加入遊戲時刪除條目,這需要將索引儲存在每個遊戲對象。但是當一個條目被刪除時,所有其他遊戲對像都需要使用新索引進行更新,這只是一團糟。

理想情況下,這個應用程序有一天會獲得大量流量,所以我需要一個有效的解決方案。我在這裡有什麼明顯的遺漏嗎?您遇到過更好的解決方法嗎?我希望我可以僅使用目前打開的遊戲迭代映射,但我認為這是不可能的。

你預計會有多少場比賽?

Solidity 可以一次返回整個數組,因此您可以將每個遊戲保存在一個數組中,一次呼叫將其全部返回,然後使用客戶端過濾。您可以嘗試模擬一個返回 10,000 個遊戲的函式,看看普通的舊 JS .filter() 是否能足夠快地處理它。(雖然我不知道節點能夠以多快的速度返回整個列表。)

但是讓我們說這行不通。我的下一個建議是連結列表。它不是 Solidity 原生的,您可能必須為它編寫一個庫以方便使用,但您可以在恆定時間內添加和刪除。我不確定汽油會貴多少(或更少?),但我懷疑它不會貴多少。

您需要像這樣使用雙向鍊錶

struct GameListItem {
 uint prev;
 uint next;
}

mapping (uint => GameListItem) public openGamesList;
uint public firstOpenGame;

function addOpenGame (uint gameID) internal {
   require (gameID != 0, "Game ID is NULL");
   GameListItem storage item = openGamesList [gameID];
   require (item.next == 0, "Game is already in the list");

   if (firstOpenGame == 0) {
       item.next = gameID;
       item.prev = gameID;
       firstOpenGame = gameID;
   } else {
       GameListItem storage first = openGamesList [firstOpenGame];
       uint lastID = first.prev;
       GameListItem storage last = openGamesList [lastID];

       item.next = firstOpenGame;
       item.prev = lastID;
       first.prev = gameID;
       last.next = gameID;
   }
}

function removeOpenGame (uint gameID) internal {
   require (gameID != 0, "Game ID is NULL");
   GameListItem storage item = openGamesList [gameID];
   uint nextID = item.next;
   require (nextID != 0, "Game is not in the list");

   uint prevID = item.prev;
   if (nextID == gameID)
       firstOpenGame = 0;
   else {
       GameListItem storage next = openGamesList [nextID];
       GameListItem storage prev = openGamesList [prevID];

       next.prev = prevID;
       prev.next = nextID;

       if (firstOpenGame == gameID) firstOpenGame = nextID;
   }

   item.next = 0;
   item.prev = 0;
}

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