Storage

映射內的 sstore 結構,內聯彙編

  • April 8, 2022

我正在嘗試使用內聯彙編讀取和寫入結構映射。

getValues()是我如何能夠從儲存中讀取某些內容的範例。and()我可以使用和讀取前兩個值shr()。但是我如何讀取結構中的下一個值?

中的程式碼writeTo()能夠寫入mapping(uint => uint). 但我不知道如何寫入mapping(uint => Struct).

應該散列哪些值sstore()以寫入正確的位置?(無論可能是什麼位置,但我假設得到如何使用 (?) 跳轉到結構中的下一個插槽的shr()答案可能會回答這個問題)

// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;


contract Assemble2{
   
   mapping(uint => Info) infos;

   struct Info{
       uint128 level;
       uint128 amount;

       uint128 num;
       uint128 time;

       address sender;
   }


   constructor(){
   
       infos[0] = Info(
           2,3, 4,5, msg.sender
       );
   }

   function getValues(uint _id) public view returns(uint a, uint b, uint c, uint d, address e){
       Info storage info = infos[_id]; 
       assembly {
           let w := sload(info.slot) 
          
           a := and(w, 0xfff) // gets first value in first slot
           b := shr(128, w) // gets second value in first slot

           //c := shr(?, w) // how to jump to next slot and get first value?
           //d := shr(?, w) // how to jump to next slot and get second value?

           //e := shr(?, w) // how to jump to third slot and get first value?
       }
   }


   function writeTo(uint _id) public {
       Info storage info = infos[_id];

       uint newLevel = 10;
       assembly {
           mstore(0, newLevel)
           mstore(32, info.slot)
           // how to include position within struct?

           // do I somehow hash the sload with the pointer to the position?
           // let w := sload(info.slot)
           // b := shr(128, w)

           let hash := keccak256(0,64)

           sstore(hash,2)
       }
   }

}

getValues() 是我如何從儲存中讀取內容的一個範例。我可以使用 and() 和 shr() 讀取前兩個值。但是我如何讀取結構中的下一個值?

結構的儲存佈局非常簡單,如果結構數據不適合一個插槽,那麼它也會簡單地使用下一個。

所以假設結構儲存槽為0,佈局將如下:

  • 插槽 0:$$ amount (0x10) - level (0x0) $$
  • 插槽 1:$$ time (0x10) - num (0x0) $$
  • 插槽 2:$$ sender (0x0) $$

現在請記住,uint128 佔用 16 個字節的記憶體,而 address 佔用 20 個字節。

和 :

a := and(w, 0xfff) // gets first value in first slot

您只獲得level成員的前 12 位(1.5 字節),但level它是一個 128 位(16 字節)的值。

因此,考慮到結構的儲存佈局和您嘗試讀取的成員的實際大小,更好的getValues實現可能是:

function getValues(uint _id) public view returns(uint a, uint b, uint c, uint d, address e){
   Info storage info = infos[_id]; 
   assembly {
       // Load info.slot 
       let w := sload(info.slot) 
      
       // Get the first 16 bytes of sload(info.slot) : level
       a := and(w, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)

       // Shift sload(info.slot) 16 bytes right : amount
       // Values in assembly are unsigned int by default, shifting right pads with 0s
       // So, no need to apply the mask here but you could if you wanted to
       b := shr(128, w)

       // Load info.slot + 1
       w:= sload(add(info.slot, 1))

       // Get the first 16 bytes of sload(info.slot + 1) : num
       c := and(w, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)

       // Shift sload(info.slot + 1) 16 bytes right : time
       d := shr(128, w)

       // Load info.slot + 2 and take the first 20 bytes : sender
       e := and(sload(add(info.slot, 2)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
   }
}

writeTo() 中的程式碼能夠寫入映射(uint => uint)。但我不知道如何在映射(uint => Struct)中寫入結構中的插槽。

我們將只使用相同的想法,注意不要更改level欄位以外的任何其他內容,如下所示:

function writeTo(uint _id) public {
       Info storage info = infos[_id];

       uint newLevel = 10;
       assembly {

           // Load info.slot : [amount, level]
           let w := sload(info.slot)

           // Clear the first 16 bytes (level)
           w := and(w, not(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))

           // Write the first 16 bytes with the first 16 bytes of newLevel
           w := or(w, and(newLevel, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))

           // Update the storage value
           sstore(info.slot, w)
       }
   }

如果某些按位運算有點令人困惑,我總是以這個描述它們的好答案結束。

sstore() 應該對哪些值進行散列以寫入正確的位置?(無論可能是什麼位置,但我假設使用 shr()(?) 獲得如何跳轉到結構中下一個插槽的答案可能會回答這個問題)

由於您已經從info.slot那裡獲得了儲存插槽,因此無需散列任何內容,因此散列操作是計算目標插槽的方法,但在您的情況下,您不需要這樣做,因為它已經可用……

但是這裡描述了映射元素的儲存槽,在你的情況下,這只是keccak256(h(k) . p)映射本身的儲存槽:(資訊不是資訊,我們在這裡定位映射)。h(k)``_id``p``infos.slot

因此,這兩個功能完全等效:

function getStorageSlotAssembly(uint _id) public view returns (bytes32 slot) {
      assembly {
          mstore(0x00, _id)
          mstore(0x20, infos.slot)
          slot := keccak256(0, 0x40)
      }
  }

function getStorageSlotSolidity(uint _id) public view returns (bytes32 slot) {
        Info storage info = infos[_id];

        assembly {
            slot := info.slot
        }
  }

只需確保徹底閱讀文件,因為如果您要使用不同的密鑰類型, h(k) 可能實際上會做一些事情。

我希望這能回答你的問題。

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