• 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对开发人员要求较高,能准确使用可见性
    移除修改器再密集使用的情况下,可能会对合约大小产生重大影响函数的验证减少,出错率增加
  • 相关阅读:
    智能宠物喂食器方案软硬件设计
    前后端分离Vue+nodejs酒店公寓客房预订管理系统udr7l-java-php-django-springboot
    使用微信小程序编写会议OA项目-首页
    Java List.sort()的使用 和Comparator转换器的实现原理
    论文调研
    基于threejs的商品VR展示平台的设计与实现思路
    commons-io版本变动在windows环境下引发的NTFS ADS separator问题
    七款云上共享文件系统 POSIX 兼容性大比拼
    GIT简明命令
    好用的博客评论系统 Valine 使用及避坑指南
  • 原文地址:https://blog.csdn.net/qq_33842966/article/details/126425874