Solidity--合约最大栈深度与解决方案

Solidity合约最大栈深度与解决方案

以太坊中的数据存储

以太坊和比特币最大的区别就是,以太坊拥有智能合约可以写入代码,代码会被放在一个地址中永久保存且不能修改。而编写智能合约的solidity语言作为高级语言,不能直接执行,需要解释器来解释。因此soldity编写的合约在编译后会产生字节流(bytecode),产生的字节流会在以太坊中基于栈的虚拟机EVM来进行解释。EVM的主要功能有:

  • 将源码编译成EVM指定的字节码
  • 包含指令和操作数的数据结构(指令用语处理操作数作何种运算)
  • 一个为所有函数操作的调用栈
  • 一个指令指针(Instruction Point- -IP):用于指向下一条将要执行的指令
  • 一个虚拟“CPU”:
    • 取值:获取下一条指令(通过IP获取)

    • 译码:对指令进行翻译,将要做何种操作

    • 执行:执行命令

上图为EVM的架构,在EVM虚拟机中存在易失和非易失两个存储区域:

易失区域

堆栈(stack):解释字节码时使用,每个堆栈顶的大小为256比特,堆栈的最大的大小为1024字。其中保存函数的局部变量数量限制在16个,当合约中声明的局部变量超过16个时,编译合约会报错

内存(memory):易失性的可以读写修改的空间,主要是在运行期间存储数据,江参数传递给内部函数。内存可以在字节级别寻址,一次可以读取32字节。

当合约大于这个深度的时候就无法发布

非易失区域

代码(code):写代码的地方(字节码)。该存储区域跟memory不一样,不能直接被EVM执行

存储(storge):非易失性的可以读写修改存储的空间,也是每个合约持久化存储数据的地方,一共有2^256个插槽,一个插槽有32byte

为什么会超过合约最大的栈深度是1024个字

EVM作为一个堆栈虚拟机运行,为了方便进行密码学计算(如 Keccak-256 哈希或 secp256k1 签名)。EVM栈以字为单位进行操作,EVM采用32字节(256比特)的字长,最多可容纳1024个字。如果超过1024的上限,外部函数调用会失败,这种情况下,Solidity会抛出异常。

  • EVM指令集

EVM和JVM一样,也是执行字节码。由于操作码被限制在一个字节以内,所以EVM指令集最多只能容纳256条指令。目前EVM已经定义了约142条指令,还有100多条指令可供以后扩展。这142条指令包括算数运算指令,比较操作指令,按位运算指令,密码学计算指令,栈、memory、storage操作指令,跳转指令,区块、智能合约相关指令等。

  • EVM栈操作指令

主要有POP、PUSHx、DUPx和SWAPx(其中x为指令后跟随的元素)四种指令对栈进行简单操作:

1、POP指令

POP指令(操作码0x50)从栈顶弹出一个元素。

2、PUSHx指令

PUSH系列指令把紧跟在指令后面的x(1~32)字节元素推入栈定。PUSH系列指令一共有32条,PUSH1(操作码0x60)~PUSH32(操作码0x7A)。

3、DUPx指令

DUP系列指令复制从栈顶开始数的第x(1~26)个元素,并把复制后的元素推入栈顶。DUP系列之灵一共有16条,DUP1(操作码0x80)~DUP16(操作吗0x8A)。

4、SWAPx指令

SWAP系列指令把栈顶元素和从栈顶开始数的第x(1~16)+1个元素进行交换。SWAP系列之灵一共有16条,从SWAP1(操作码0x90)~SWAP16(操作码0x9A)。

栈深度的机制发明是为了保护什么

  • 以太坊运行性能

stack是临时存储,当智能合约运行时有效,当运行结束后回收。如果stack容量过大且未及时收回存储空间会影响整个以太坊的运行,又因为以太坊是图灵完备的,允许循环操作,该机制能预防死循环导致计算和存储资源以及gas的浪费。

  • 安全考虑(防止被攻击)

防止利用固定成本发起DOS(拒绝服务)攻击,任何对合约的调用从gas费上来说都是相对便宜的,但是根据被调用合约代码的大小(从磁盘读取代码、预处理代码、将数据添加到Merkle证明),合约调用对以太坊节点的影响会不成比例地增加。攻击者就会通过很少的资源给别人造成大量的工作,普通用户就可能会遭受到DOS攻击。

解决办法

  • 对合约影响较大
方法 优点 缺点
通过拆分合约 可无限拆分扩容 当跨合约调用之后,gas费用会增大
使用库 库文件不需要声明为内部函数,会在编译过程中直接被添加到合约 会在后台使用DELEGATECALL,如果使用公共函数,这些函数事实上将在一个单独的库合约中
使用代理 只是用调用合约的状态执行另一个合约的函数 增加了很多复杂性
  • 对合约影响中等
方法 优点 缺点
通过压缩减少代码量 节省gas 实现难度较大,优化的空间取决于技术本身对存储空间的理解深度
缩短错误信息 简化合约,节省gas 报错信息不完整,解决问题时可能找不到问题所在
在优化器中考虑一个低运行值 针对每个函数只运行一次的情况进行优化 增加运行函数的gas成本
  • 对合约影响较小

方法 优点 缺点
避免将结构体传递给函数 简化合约大小,节省gas 结构体不清晰,容易混淆
声明函数和变量的正确可见性 节省gas 对开发人员要求较高,能准确使用可见性
移除修改器 再密集使用的情况下,可能会对合约大小产生重大影响 函数的验证减少,出错率增加

猜你喜欢

转载自blog.csdn.net/qq_33842966/article/details/126425874
今日推荐