Solidity

映射多鍵操作方法?

  • March 15, 2020

我正在嘗試創建一個映射,其鍵由兩個 bytes32 鍵組成。

我已經嘗試過使用 tuples-keys 和 struct-keys 但兩者都被編譯器拒絕。

我也在考慮“散列”兩個鍵,但不知道該怎麼做。

周圍有任何程式碼參考嗎?

更新

“四處探勘”在我看來,像 bytes32 hashOfMultiKey = keccak256(abi.encodePacked(i1,i2)); 這樣的函式 對於大多數目的來說“足夠”了,但我正在失去有關原始密鑰的資訊。我想知道是否會有一個更簡單的替代方案來保留有關輸入多鍵的所有原始資訊。

有兩種基本方法,我可以談談在某些情況下有用的第三種方法。

雜湊不一定是有損的。

event LogSet(bytes32 keyA, bytes32 keyB, bytes32 value);

function set(bytes32 keyA, bytes32 keyB, bytes32 value) public {
 myMap(multikey(keyA, keyB) = value;
 emit Log(keyA, keyB, value);
}

function multikey(bytes32 keyA, bytes32 keyB) public pure returns(bytes32) {
 return keccak256(abi.encodedPacked(keyA, keyB));
}

事件日誌確保觀察者有辦法發現所有曾經使用過的 A/B 組合,因此在get()函式中請求它們是完全合理的。它不是有損的,但合約本身無法列舉它們。它將依賴外部輸入,然後執行多鍵計算。

映射映射

mapping也許與直覺相反,將 a 儲存在 a中並不會增加儲存成本mapping

// keyA => mapping(keyB => value)
mapping(bytes32 = mapping(bytes32 => bytes32));

合約需要列舉什麼?

客戶通常有各種需求,例如“get all B in A”,按“whatever”排序等。合約對自己內部邏輯的需求通常要少得多。關於這個話題的一些想法:https ://blog.b9lab.com/the-joy-of-minimalism-in-smart-contract-design-2303010c8b09

契約很難檢查 set keyA 中是否存在 keyB 或 set keyB 中是否存在 keyA,這種情況並不少見。您可能能夠擺脫檢查值!= bytes32(0) 或者您可能需要一些更精細的東西。特別注意需要或不需要從集合中移除項目。曾經是會員,永遠是會員?或者,可以想像一個成員離開集合?

查看這個庫,了解一種將所有鍵視為具有刪除功能的集合成員的方法。這將支持“A 在 B 中嗎?” 和“B 在 A 中嗎?” 以及邏輯刪除成員的能力。

https://medium.com/robhitchens/solidity-crud-epilogue-e563e794fde

Github:https ://github.com/rob-Hitchens/UnorderedKeySet

pragma solidity 0.5.1;

import "./HitchensUnorderedKeySet.sol";

contract KeyAKeyB {

   using HitchensUnorderedKeySetLib for HitchensUnorderedKeySetLib.Set;

   // Key B in A
   mapping(bytes32 => HitchensUnorderedKeySetLib.Set) bInA;

   // Key A in B
   mapping(bytes32 => HitchensUnorderedKeySetLib.Set) aInB;

   // multikey => value;
   mapping(bytes32 => bytes32) public values;

   event LogSet(bytes32 keyA, bytes32 keyB, bytes32 value);
   event LogRem(bytes32 keyA, bytes32 keyB);

   function multikey(bytes32 keyA, bytes32 keyB) public pure returns(bytes32) {
       return keccak256(abi.encodePacked(keyA, keyB));
   }

   // When inserting A, B, value, use a compound key to store the value, and maintain lists of B in A and A in B.
   // Below follows an update or overwrite pattern. 

   function set(bytes32 keyA, bytes32 keyB, bytes32 value) public {
       if(!isBInA(keyA, keyB)) bInA[keyA].insert(keyB); 
       if(!isAInB(keyA, keyB)) aInB[keyB].insert(keyB); 
       values[multikey(keyA, keyB)] = value;
       emit LogSet(keyA, keyB, value);
   }

   function get(bytes32 keyA, bytes32 keyB) public view returns(bytes32 value) {
       // optionally, check B exists in A to differentiate exists but 0x0 from not stored.
       // require(bInA[keyA].exists(keyB));
       return values[multikey(keyA, keyB)];
   }

   // This remove function will reject removing a key pair that does not exist

   function remove(bytes32 keyA, bytes32 keyB) public {
       bInA[keyA].remove(keyB);
       aInB[keyB].remove(keyA);
       delete values[multikey(keyA, keyB)];
       emit LogRem(keyA, keyB);
   }

   // Check existance

   function isBInA(bytes32 keyA, bytes32 keyB) public view returns(bool isIndeed) {
       return bInA[keyA].exists(keyB);
   }

   function isAInB(bytes32 keyA, bytes32 keyB) public view returns(bool isIndeed) {
       return aInB[keyB].exists(keyA);
   }

   // So clients can enumerate keys, all B in A, and all A in B

   function countBinA(bytes32 keyA) public view returns(uint) {
       return bInA[keyA].count();
   }

   function countAinB(bytes32 keyB) public view returns(uint) {
       return aInB[keyB].count();
   }

   function aBAtIndex(bytes32 keyA, uint row) public view returns(bytes32 keyB) {
       return bInA[keyA].keyAtIndex(row);
   }

   function bAAtIndex(bytes32 keyB, uint row) public view returns(bytes32 keyA) {
       return aInB[keyB].keyAtIndex(row);
   }
}

這個例子可能比實際需要的更雄心勃勃。希望它展示瞭如何在遵守嚴格執行關係完整性和完全可發現的內部結構的理想的同時解決各種問題。

希望能幫助到你。

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