Solidity Storage底层管理
Posted mutourend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Solidity Storage底层管理相关的知识,希望对你有一定的参考价值。
1. 引言
Solidity底层通过SLOAD和SSTORE opcode来控制EVM storage。
2. 何为Storage?
Storage为每个合约的持久mapping,具有 2 256 − 1 2^256-1 2256−1个32 byte words。当在合约中设置某状态变量值时,其会存储在指定的slot中,其将持续在EVM中,除非被相同类型的其它值覆盖。
3. 何时用Storage?何时用Memory?
当首次加载某storage slot时,其是cold的,意味着需要2100 gas,后续再调用该slot时,其是warm的,仅需100 gas。而Memory更便宜,其低至3 gas(当有memory expansion时,将更贵点)。
举例如下,未优化合约:
contract C
struct S
uint256 a;
uint256 b;
address c;
S public s;
function foo(uint256 input) external
// `s.b` is loaded from storage once: warming up the storage!
if (input < s.b) revert;
// Second `s.b` SLOAD with warm storage.
if (s.b > 50) revert;
其中s,b
从storage中加载了2次。可优化为:创建内存变量来存储s.b
值,后续使用该内存变量。原因在于MLOAD比SLOAD便宜。即优化为:
function foo(uint256 input) external
// Initial storage load to store in memory.
uint256 b = s.b;
// Using MLOAD in comparison operations!
if (input < b) revert;
if (b > 50) revert;
4. 手工分配Storage
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;
contract C
struct S
uint16 a; // 2 bytes, 2 bytes total
uint24 b; // 3 bytes, 5 bytes total
address c; // 20 bytes, 25 bytes total + end of slot 0x01
address d; // 20 bytes, slot 0x02
// I've noted the storage slots each state is located at.
// A single slot is 32 bytes :)
uint256 boring; // 0x00
S s_struct; // 0x01, 0x02
S[] s_array; // 0x03
mapping(uint256 => S) s_map; // 0x04
constructor()
boring = 0x288;
s_struct = S(
a: 10,
b: 20,
c: 0xdcD49C36E69bF85FA9c5a25dEA9455602C0B289e,
d: 0x4675C7e5BaAFBFFbca748158bEcBA61ef3b0a263
);
function view_boring() external view returns (bytes32)
bytes32 x;
assembly
x := sload(0x00)
return x;
function view_slot(uint256 slot) external view returns(bytes32)
bytes32 x;
assembly
x := sload(slot)
return x;
function view_b() external view returns (uint256)
bytes32 x;
assembly
// before: 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014 000a
// ^
// after: 0000 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014
// ^
let v := shr(0x10, sload(0x01))
// If both characters aren't 0, keep the bit (1). Otherwise, set to 0.
// mask: 0000000000000000000000000000000000000000000000000000000000 FFFFFF
// v: 000000000000000000dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014
// result: 0000000000000000000000000000000000000000000000000000000000 000014
v := and(0xffffff, v)
// Store in memory bc return uses memory.
mstore(0x40, v)
// Return reads left to right.
// Since our value is far right we can just return 32 bytes from the 64th byte in memory.
x := mload(0x40)
return uint256(x);
// unused bytes c b a
// before: 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014 000a
// unused bytes c b a
// after: 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 0001F4 000a
function set_b(uint24 b) external
assembly
// Removing the `uint16` from the right.
// before: 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014 000a
// ^
// after: 0000 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014
// ^
let new_v := shr(0x10, sload(0x01))
// Create our mask.
new_v := and(0xffffff, new_v)
// Input our value into the mask.
new_v := xor(b, new_v)
// Add back the removed `a` value bits.
new_v := shl(0x10, new_v)
// Replace original 32 bytes' `000014` with `0001F4`.
new_v := xor(new_v, sload(0x01))
// Store our new value.
sstore(0x01, new_v)
4.1 基础类型访问
当想要访问uint256 boring
时,访问方式可为:
assembly
let x := sload(0x00)
4.2 访问Bitpacked结构体
所谓bitpacked,是指在单个slot(32 bytes)内存储了多个变量。在本例中,slot 0x01
中pack了共25字节内容:
uint16 a
(2 bytes).uint24 b
(3 bytes).address c
(20 bytes).
对应s_struct
的slots为:
// 0x01 0x00000000000000dcd49c36e69bf85fa9c5a25dea9455602c0b289e000014000a
// 0x02 0x0000000000000000000000004675c7e5baafbffbca748158becba61ef3b0a263
Bitpacked结构体的查询和设置,可参看上面合约中的view_b
和set_b
。
4.3 访问数组结构
4.4 访问mapping结构
4.5 访问String和Bytes结构
参考资料
[1] A Low-Level Guide To Solidity’s Storage Management
以上是关于Solidity Storage底层管理的主要内容,如果未能解决你的问题,请参考以下文章
solidity笔记——存储区域memory storage stack——2021.5.12
区块链 以太坊 Solidity状态变量局部变量与memory storage
以太坊Solidity的Storage/Memory/Calldata layout
智能合约实战 solidity 语法学习 06 [ memory storage ] 附代码
solidity数据类型storage memory calldata modifier前置条件 继承 接口合约 导入库 using...for solc编译