映射內的 sstore 結構,內聯彙編
我正在嘗試使用內聯彙編讀取和寫入結構映射。
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) 可能實際上會做一些事情。
我希望這能回答你的問題。