请注意,本文编写于 149 天前,最后修改于 149 天前,其中某些信息可能已经过时。
目录
EVM 存储结构
状态变量如何存储
结构体
映射(map)
数组
定长数组
可变长度素组
字节数组和字符串
EVM 存储结构
- non-volatile (不容易丢失的)存储在code和storage 里的数据
- volatile(容易丢失的) 存储在stack, args, memory里的话数据
各个存储位置的含义
- Code: 专门存储只能合约的二进制源码的空间;
- Storage: 合约持久化存储数据的地方;Storage 是一个巨大的map, 一共2256个插槽,每个插槽有32byte;
- Stack 及所说的运行栈,用来保存EVM 的输入输出,可以免费使用,没有gas消耗,用来保存函数的局部变量,数量被限制在16个,stack的最大深度为1024,其中每个单元是32bytes;
- Args 也叫calldata, 是一段只读的可寻址的保存函数调用参数的空间,与栈不同的地方是,如果要使用calldata 里面的数据,必须手动指定偏移量和读取的字节数。
- Memory 一个简单的字节数组,用于在运行期间存储数据,将参数传递给内部函数。基于32byte 进行寻址和扩展。
状态变量如何存储
- 对于大小在32字节以内的变量(常量),以其定义的顺序作为它的索引值存储。
- 对于连续较小的值,可能被优化存储在同一个位置
合约中前四个状态变量都是 uint64 类型的,则四个状态变量的值会被打包成一个 32 字节的值存储在 0 位置。
结构体
对于代下载32字节以内的结构体同样也是顺序存储。
例如结构体变量索引定义在位置 0,结构体内部有两个成员,则这两个成员的依序为 0 和 1
映射(map)
map 存储位置是通过 keccak256 (bytes32(key) + bytes32(position) ) 计算得到的,position 表示 key 对应 storage 类型变量存储的位置。
数组
定长数组
同上,只要在 32 字节以内也是顺序存储,不过在编译时编译器会进行边界检查防止越界。
可变长度素组
由于可变长度数组长度不定,一般在编译可变长度数组时候会提前预留存储空间,所以就会使用状态变量的位置存储数组的长度。
具体的数据地址会通过keccak256(bytes32(position))算出数组首地址,再加数组长度偏移量获得具体的元素。
理论的最大值是2256−1
字节数组和字符串
- 对于定长字节数组则是同定长数组一样;
- 对于可变字节数组和字符串,会在存储值位置补0一直到32字节,并用补0的最后一个字节存储字符串的编码长度。
当节数组和字符串长度大于31字节时
- 变量位置存储编码长度,并且编码长度公式更换为编码长度 = 字符数 * 2 + 1
- 真实存储值第一个位置通过公式 keccak256(bytes32(position)) 获取,剩余值在获取到的位置顺序存储,同样在最后存储位置补0到32字节。
本文作者:Ramondy
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA
许可协议。转载请注明出处!