掘金 后端 ( ) • 2024-04-03 10:51

基础知识

成员变量按照他们在合约源码当中出现的顺序,在storage中按照一种规则依次堆放,这使得每一个成员变量具有固定的位置。

以太坊智能合约存储布局,选择以256位为一个插槽(slot),也就是32个字节进行键值映射。成员变量会在这里按照一定的规则进行堆放。在solidity底层是通过变量在存储布局中的位置来寻找变量,而不是通过变量名称来查找使用,所以,在基本代理结构中,通过实现相同的存储布局和利用delegatecall的保持上下文的特性,达到存储与逻辑操作的分离。

理解智能合约为什么选择256位

在以太坊智能合约中选择256位存储槽的原因与以太坊虚拟机(EVM)的架构密切相关。以下是选择256位的主要原因:

  1. 与加密操作的效率: 以太坊大量依赖于加密算法,如SHA-256和ECDSA,这些算法都是针对256位操作进行优化的。将EVM的字大小与这些加密操作对齐,可以有效地处理它们。
  2. 气体成本优化: 以太坊的气体机制衡量操作的计算工作量,围绕256位字大小进行了优化。由于以太坊的大部分加密操作和账户地址基于256位,这种对齐有助于标准化和优化气体成本。
  3. 地址和操作的一致性: 以太坊地址是160位,交易哈希和其他重要元素是256位。在EVM中使用256位字大小简化了这些元素的处理,无需拆分或组合较小字大小来处理地址和哈希。
  4. 历史和实际考虑: 以太坊的设计受到早期区块链和加密技术的影响,其中许多使用256位大小来实现安全性和效率。这种选择反映了在以太坊设计时的加密标准下,操作效率和确保强大安全性之间的平衡。

256与非256

假设以太坊虚拟机(EVM)使用的是不同的字大小,例如128位和256位,我们可以通过比较它们来看看会有什么不同:

256位

  1. 加密算法效率: 适合处理256位哈希函数(如SHA-256)和椭圆曲线数字签名。这提高了执行这些操作的效率和安全性。
  2. 气体成本优化: 操作、存储和计算的气体成本是针对256位操作优化的,使得与区块链交互更加经济高效。
  3. 数据处理: 由于以太坊地址和哈希通常为256位,因此无需进行额外的数据分割或合并,简化了处理流程。

128位

  1. 加密算法效率: 对于128位的哈希或签名算法可能更高效,但对于以太坊常用的256位算法,它可能需要更复杂的处理过程,降低了操作效率。
  2. 气体成本可能变高: 如果气体成本没有针对128位操作进行优化,执行相同的操作可能需要更多的步骤,从而消耗更多气体。
  3. 数据处理更复杂: 处理256位的以太坊地址和哈希值可能需要额外的步骤来适应128位的限制,增加了数据处理的复杂性。

实例举例

假设有一个操作需要处理一个256位的以太坊地址:

  • 在256位系统中,这个地址可以作为单个单位直接存储和处理。
  • 在128位系统中,地址可能需要分割成两个128位的部分进行处理和存储,这增加了处理复杂性,可能还需要额外的步骤来组合这些部分以进行某些操作,如签名验证或哈希计算。

总的来说,256位的选择为以太坊提供了高效处理其核心加密和区块链操作的能力,同时优化了气体成本并简化了数据处理。而128位系统可能会在这些方面面临更多挑战,尤其是在处理与以太坊生态系统紧密相关的256位操作时。

详解storage layout

堆叠规则

值类型堆叠规则(分享slot)

  1. 值类型需要的存储空间就是这个数据类型的字节大小
  2. 值类型的数据会检查当前slot的剩余空间,如果放的下就放,放不下就会新起一个slot
  3. 静态的数组和结构独占slot,并不会参与其他的slot的剩余空间和分享自己的slot剩余空间(这里不是值类型)

动态数组与映射的堆叠规则

  1. 动态数组和mapping参与成员变量的堆叠占有一个32位的slot,但是数据通过哈希运算存在别的slot中,不参与堆叠
  2. 动态数组存放数组大小,mapping空着 动态数组和映射.png 对于动态数组与mapping这种数据大小不固定的数据结构,solidity采用了一种特别的方式,选择将真实的数据存放在一个遥远的存储区域。

下面是关于动态数组和映射存储规则的详解:

  1. 动态数组(Dynamic Arrays)

动态数组可以在运行时改变大小,具体表现为可以增加或移除元素。在Solidity中,动态数组的存储规则如下:

  • 大小存储:动态数组的大小(即数组中元素的数量)存储在一个固定的存储槽中。这个槽位通常是数组变量在合约中声明的位置决定的。
  • 内容存储:动态数组的内容并不直接存储在紧接着大小槽位的后续槽位。相反,内容存储的起始位置是通过对数组变量的存储槽位地址进行哈希运算得到的。每个数组元素占据一个或多个连续的存储槽。
  • 数据访问:访问动态数组中的元素时,Solidity会计算出该元素的确切存储位置,然后从中读取或写入数据。
  1. 映射(Mappings)

映射是键值对的集合,其中键和值可以是任意类型。映射在Solidity中的存储规则与动态数组有显著不同:

  • 键值哈希:映射本身不占据一个连续的存储空间,它的每个元素都被单独存储。元素的存储位置是通过将键值与映射变量的存储槽位地址进行哈希运算得到的。
  • 无顺序存储:因为映射是通过哈希键值来存储的,所以它们不保持任何元素的顺序。这也意味着你不能遍历映射或直接获取映射的大小。
  • 访问复杂度:映射提供了一种高效的方式来根据键值直接访问存储的数据,但不能像数组那样按索引顺序访问。这也是映射不能遍历的原因