[技术学习]以太坊智能合约的存储是怎么实现的
存储空间
可以看作为一个包含2^256个入口(slot)的array,每个入口是32-byte大小
但是实际存储中,使用的是key-value的形式存储的,这个可以看作为一个稀疏数组:
32-byte keys to 32-byte values
没有数据存储的key,对应的值就是0
因此,当智能合约中,某个key对应的存储从某个值变为0时,就会回收存储空间,这在以太坊智能合约中就对应于一个refund
固定大小数据存储
对于固定数据的存储,就是按照顺序分配slot
contract StorageTest {
uint256 a;
uint256[2] b;
struct Entry {
uint256 id;
uint256 value;
}
Entry c;
}
动态大小数据存储 - 数组/slice
动态大小的数据结构,存储size和数据,其中size按照顺序存储,实际数据则存储在hash(slotNumber)开始的空间
contract StorageTest {
uint256 a; // slot 0
uint256[2] b; // slots 1-2
struct Entry {
uint256 id;
uint256 value;
}
Entry c; // slots 3-4
Entry[] d;
}
接着上一个solidity合约的例子,d 为变长结构,d的数组大小存储在slot 5内,而d的实际内容存储在hash(5)开始的连续空间
计算函数:
function arrLocation(uint256 slot, uint256 index, uint256 elementSize)
public
pure
returns (uint256)
{
return uint256(keccak256(slot)) + (index * elementSize);
}
动态大小数据存储 - map
map需要根据key对数据内容进行快速查找,存储时先对map的size指定一个slot,但是这个slot内并没有实际内容,只是利用slot的编号,去寻址后续map的内容
hash(value,slotnumber)
contract StorageTest {
uint256 a; // slot 0
uint256[2] b; // slots 1-2
struct Entry {
uint256 id;
uint256 value;
}
Entry c; // slots 3-4
Entry[] d; // slot 5 for length, keccak256(5)+ for data
mapping(uint256 => uint256) e;
mapping(uint256 => uint256) f;
}
计算函数:
function mapLocation(uint256 slot, uint256 key) public pure returns (uint256) {
return uint256(keccak256(key, slot));
}
动态大小数据存储 - 复杂结构
对于比较复杂的结构,主要是分析先取map还是array,然后按照上述的两种计算函数组合即可,arrLocation和mapLocation