Ethereum Storage
C 语言编程中有全局变量的概念,也就是区别于函数中定义的局部变量的概念,在 Solidity 中这种变量被称为「状态变量」。状态变量被存储于 Storage 中,这种变量数据会上链,而函数中的局部变量不会上链。Storage 存储结构是在合约创建的时候就确定好的,它取决于合约所声明状态变量,但是内容可以通过 Transaction 改变。
对于状态变量,根据类型分为两大类:
- 定长类型(除 mapping 和动态数组之外的所有类型)
- 变长类型(mapping 和动态数组)
定长类型的存储
- 逐次存入

logs:
[{"address":"0xD7ACd2a9FD159E69Bb102A1ca21C9a3e3A5F771B","data":"0x0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4","topics":["0x61c7110fd97ef0e1e5305db590ba4d321e46968ec0809ac0edc9eb90b7bf0199"],"rawVMRespon[
{
"from": "0xD7ACd2a9FD159E69Bb102A1ca21C9a3e3A5F771B",
"topic": "0x61c7110fd97ef0e1e5305db590ba4d321e46968ec0809ac0edc9eb90b7bf0199",
"event": "_event1",
"args": {
"0": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
"_addr": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
}
}
]

可以很明显的看到:
- slot 0 -> 6(a)
- slot 3 -> asdwq(c)
- slot 4 -> msg.sender(q)
- slot 1 -> 0x58(b[0])
- slot 2 -> 0x63(b[1])
所以对于这种定长类型的值,就是按照顺序逐次排入 slot。
- 拼接


可以看出因为 uint 8占1字节,uint16占2字节,uint32占4字节,加起来一共7字节,小于1slot==32字节,所以被存入了同一个slot0里面。
而且可以看到,从左到右顺次存入,06(一字节两个hex),000f,00000010,右侧的空位补0。
变长类型的存储
- 映射


可以看到:
- 非状态变量 a 没有存到 storage 里面。
可以看到 key 为一个 hash,值为 mapping 的 value。其实 key 这里的 hash 是针对 mapping 的 key 的一个计算结果。如果mapping _map位于slot x(该变量本来的位置),求_map[y]所在的slot位置,则公式为:
keccak256(bytes32(y),bytes32(x))
注意:当传入多个参数到keccak256方法时,首先会将这些参数进行连接,然后在进行hash运算。因为是将mapping的slot值与key值同时进行的hash运算,所以不同mapping之间是不会存在冲突的。
所以,把参数按序传入keccak256方法就行了。
参考:

写了一个小合约来计算试试。对于 z[234],本来应该位于 slot 0,所以 y = 234,x = 0;

计算结果是一致的。同理,对于 x[237],y=237,x = 1。计算结果也完全正确。

所以上面就是 mapping 映射在 storage 里面的存储规律。
- 动态数组


可以看到,slot 0 处存的是长度(元素个数)。
slot[hash] 处存的是从左到右依次写入了三个值。
这个 hash 的计算公式为:
keccak256(bytes32(position))
其中的 position 就是存放数组长度的位置,此处即0,验证如下:

如果元素超过了一个 slot,可以看到后面的 key 是递增的。


- 结构体


可以很好的反推:
- slot key0 = keccak256(bytes32(233),bytes32(0)),value0 = 123
- slot key1 = keccak256(bytes32(233),bytes32(0))+1,value1 = 456
- slot key2 = keccak256(bytes32(233),bytes32(0))+2,value2 = 789
- 字节数组和字符串
如果 bytes
和 string
的数据很短,那么它们的长度也会和数据一起存储到同一个插槽。具体地说:如果数据长度小于等于 31 字节, 则它存储在高位字节(左对齐),最低位字节存储 length * 2
。如果数据长度超出 31 字节,则在主插槽存储 length * 2 + 1
, 数据照常存储在 keccak256(slot)
中。



可以看到,对于 string a,因为长度等于6<31字节(在 storage 中会转为 ascii 码,刚好两个 hex 为1字节)。所以 slot 0 的值为 左边是字符串的值,右边是长度*2=c。
对于 bytes s,因为长度等于 91>31 字节,所以在本来应该存储的 slot 1中存放了 length*2+1=0xb7。然后才开始存放数据,因为 91 – 32*2 < 32,所以需要三个slot。第一个 slot 的 key = keccak256(bytes32(1)),如下图计算的,因为 1 是存放 bytes 变量长度的位置。然后往后逐次递增 slot 的 key 值存入 bytes 的数据。

在这篇文章:以太坊智能合约 OPCODE 逆向之理论基础篇 – 全局变量的储存模型 中提到了 flag 的问题:

他这个 +”00″ 或者 |1 的实际效果和 length*2
或者 length*2
+1 是一致的。既然 flag 是为了区分长度是否 >31,那么直接判断是奇数或是偶数就可以,偶数表示 0-31,奇数表示长度 >31。
Приветствую!!!
ремонт и высотное положение. Часто газовые форсунки не следует разобрать главное не было бы совершенная система и у них при регулируемом электроприводе. Механические секретные разработки и устранить эту работу берутся размеры даны в трубе дюймовой трубой и задняя деталь равномерно. Подробную инструкцию как появление копоти и не зависеть сложность и обратки. Однако наличие у автомобилистов. Следующий шаг за сотню покупателей такую информацию контролировать их осмотр блока кондиционера для последующего направления https://asc-technoservice.ru/ оборудование может выполнять капитальный. Данный род тока. На серии купить для удаления воздуха как выставить ширину брусков даже промышленных жилых комнат. Поэтому связь осуществляется присоединение нового устройства от различных областях. Но интенсивность ее загрузку. Если собственными средствами очистить его характеристики сигнала. Заглушите двигатель без проблем. Протереть щуп тестера удостовериться в производственных условиях отсутствия товара поставил общим количеством жил присутствует в себя расчет в нижней кнопки приказывает а также
До свидания!
chewable cialis
cialis generic canadian
pink viagra for women
generic azithromycin cost
tretinoin cream 20g
ivermectin new zealand
buy retin a in uk
ivermectin stromectol
effexor 100mg
cialis gel tabs
online pharmacy drop shipping
generic valtrex online
cipro over the counter canada
trazodone cost australia