ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Solidity 0.6.0 破坏性变更完全指南:从 virtual/override 到 fallback/receive 与编译器接口升级

Solidity 0.6.0 破坏性变更完全指南:从 virtual/override 到 fallback/receive 与编译器接口升级 Solidity 0.6.0 破坏性变更完全指南从 virtual/override 到 fallback/receive 与编译器接口升级【免费下载链接】soliditySolidity, the Smart Contract Programming Language项目地址: https://gitcode.com/GitHub_Trending/so/solidity本指南以 Solidity 官方文档 docs/060-breaking-changes.rst 为骨架系统梳理 v0.6.0 引入的全部破坏性变更编译器可能不警告的行为变化、强制显式化的语法要求、语义变更、新特性以及编译器接口变化并结合当前仓库的solc命令行实现solc/CommandLineParser.cpp、libsolc/libsolc.cpp给出源码级佐证与可复制的迁移代码。读完本文你将能把 0.5.x 及更早版本的合约平滑迁移到 0.6.0并正确使用--metadata-hash、--no-optimize-yul、--old-reporter等新命令行选项与新 C API。Solidity 0.6.0 是一次规模较大的语言版本升级它收紧了许多此前宽松的写法函数重写必须显式声明、存储数组length变为只读、内联汇编变量名受限、状态变量遮蔽被禁止、fallback 函数一分为二同时在编译器接口层面引入了新的错误报告器、可选的元数据哈希算法与默认开启的 Yul 优化器。这些变更绝大多数无法靠编译器警告覆盖升级后旧代码很可能直接编译失败或行为悄悄改变因此需要逐条对照迁移。本文后续各节严格沿用原文档的章节结构并在每个小节补充对应源码实现与测试证据方便你在迁移时自查。一、编译器可能不会警告的行为变化原文档首先提醒有一类破坏性变更编译器不会给出任何警告旧代码在新版本下会静默改变行为。其中最典型的是幂运算exponentiation的结果类型规则幂运算x ** y的结果类型现在是底数x的类型在 0.6.0 之前结果类型是能同时容纳底数与指数类型的最小类型与对称运算的规则一致。此外0.6.0 起有符号类型允许作为幂运算的底数。这一规则变化意味着例如uint8(2) ** uint256(3)这类表达式的静态类型在升级前后可能不同进而影响赋值、比较与溢出检查的行为。由于编译器不会提示迁移时应对所有幂运算表达式重新审视其类型推断结果必要时显式做类型转换。二、显式化要求Explicitness Requirements本节列出的变更不改变语义但要求代码写得更明确。对大部分情况编译器会给出修改建议。1. 函数重写virtual与override函数只有被标记为virtual、或定义在接口interface中时才能被重写。接口之外没有实现的函数也必须标记为virtual。重写函数或修饰器modifier时必须使用新关键字override。当重写的函数/修饰器定义在多个并行基类中时必须在括号中列出全部基类override(Base1, Base2)。配合迁移一节的要求为每个打算重写的非接口函数加上virtual单继承下为每个重写函数加override多继承下写override(A, B, ..)把定义该函数的所有合约列在括号里。当多个基类定义了同一个函数时继承合约必须重写所有冲突函数。2. 数组length变为只读对数组length的成员访问现在是只读的即使对 storage 数组也一样不再允许通过给length赋值来调整 storage 数组大小。应改用push()、push(value)、pop()或整体赋值一个新数组整体赋值会覆盖原有内容。原文档给出了明确理由防止巨型 storage 数组造成存储碰撞storage collisions。3.abstract关键字新关键字abstract用于把合约标记为抽象合约如果一个合约没有实现其全部函数就必须这样标记。抽象合约不能用new运算符创建编译时也无法为其生成字节码。4. 库必须实现全部函数库library现在必须实现它的所有函数而不再只是内部函数internal ones。5. 内联汇编变量名的限制内联汇编中声明的变量名不能再以_slot或_offset结尾这两个后缀被编译器内部用于引用状态变量的存储位置。内联汇编中的变量声明不得遮蔽shadow汇编块外部的任何声明如果名字包含点号则点号之前的名称前缀也不能与块外声明冲突。6. 无参操作码改为内置函数形式在内联汇编中不接受参数的 opcode 现在表示为内置函数而非独立标识符例如gas要写成gas()。迁移时需为所有不接受参数的操作码加上()如pc→pc()、gas→gas()。7. 状态变量遮蔽被禁止派生合约只有在任何基类中都没有同名可见状态变量时才能声明状态变量x。也就是说跨继承层次的状态变量同名遮蔽shadowing现在是不允许的。三、语义与语法变更Semantic and Syntactic Changes本节变更是改了代码之后行为也会变的类型必须逐条修改。1. 外部函数类型到address的转换被禁止从外部函数类型external function type到address的显式转换不再允许。取而代之外部函数类型现在有一个名为address的成员用法类似于已有的selector成员把address(f)改为f.address。2.push(value)不再返回新长度动态 storage 数组的push(value)不再返回新长度返回空。迁移方式把uint length array.push(value);改成array.push(value);新长度通过array.length访问把array.length改成array.push()以增加长度用pop()减少长度。3. fallback 函数一分为二fallback与receive过去那个未命名函数俗称 fallback 函数被拆成两个receive函数用receive关键字定义。只要调用数据call data为空就调用它无论是否附带 ether且该函数隐式payable。fallback函数用fallback关键字定义。当没有其他函数匹配时被调用如果不存在receive函数则包含空调用数据的调用也归它管。可以把它设为payable或非payable若非payable则不匹配任何函数且附带 value 的交易会回滚。原文档特别指出只有当你采用升级upgrade或代理proxy模式时才需要实现新的 fallback 函数。迁移方式把function () external [payable] { ... }替换为receive() external payable { ... }、fallback() external [payable] { ... }或两者都写只要可能优先只使用receive函数。四、0.6.0 新特性New Featurestry/catch语句允许对外部调用失败做出反应。完整语法与语义见文档 docs/control-structures.rst 中的try-catch章节第 810 行起有完整示例与注意事项。文件级struct与enumstruct和enum类型现在可以在文件级别声明。calldata 数组切片数组切片可用于 calldata 数组例如abi.decode(msg.data[4:], (uint, uint))是解析函数调用载荷function call payload的一种底层方式。NatSpec 多返回值支持开发者文档developer documentation支持多个返回参数并强制与param相同的命名检查。迁移要求对函数dev文档中的每个具名返回参数定义一条以参数名开头的return条目——例如函数function f() public returns (uint value)配dev时应写return value The return value.具名与未具名返回参数的文档可以混用只要说明顺序与元组返回类型中的顺序一致。leave语句Yul 与内联汇编新增leave语句用于立即退出当前函数。payable(x)转换从address到address payable现在可以通过payable(x)完成其中x必须为address类型。五、编译器接口变化Interface Changes本节变更与语言本身无关但影响命令行用法、可编程接口与编译输出分析方式。1. 新的错误报告器引入新的错误报告器目标是让命令行错误消息更易读。它默认启用传入--old-reporter可回退到已弃用的旧错误报告器。2. 元数据哈希选项--metadata-hash默认情况下编译器现在把元数据文件的 IPFS 哈希追加到字节码末尾细节见 docs/metadata.rst。0.6.0 之前默认追加的是 Swarm 哈希。为保持兼容新增命令行选项--metadata-hash可传入ipfs、swarm或nonenone完全移除哈希。该选项在仓库中的完整实现位于 solc/CommandLineParser.cpp它把字符串参数映射到CompilerStack::MetadataHash枚举的三个取值IPFS、Bzzr1、None定义见 libsolidity/interface/CompilerStack.h非法值会抛出CommandLineValidationError。随后在 solc/CommandLineInterface.cpp 中通过m_compiler-setMetadataHash(...)生效。另外--metadata-hash与--no-cbor-metadata互斥同时指定且哈希非none时会报错见 solc/CommandLineParser.cpp。这些变化同样可以通过 Standard JSON 接口使用并影响编译器生成的元数据 JSON。关于如何读取元数据docs/metadata.rst 给出了权威做法读取字节码最后两个字节确定 CBOR 编码长度再对该数据块做正确的 CBOR 解码。注意不要依赖 CBOR 数据以0xa264开头——映射中可能还有其他键务必从字节码尾部按长度解析。3. Yul 优化器默认开启与 legacy 字节码优化器一起Yul 优化器现在在调用编译器并加--optimize时默认开启可用--no-optimize-yul关闭该选项定义于 solc/CommandLineParser.cpp。这一变化主要影响使用 ABI coder v2 的代码。4. C API 变化libsolc使用libsolcC API 的客户端代码现在接管编译器的内存管理改动如下solidity_free被重命名为solidity_reset新增solidity_alloc与solidity_freesolidity_compile现在返回一个字符串调用方必须显式用solidity_free()释放。源码实现见 libsolc/libsolc.cppsolidity_alloc在内部solidityAllocations链表中分配_size字节libsolc/libsolc.cppsolidity_compile将编译结果放入该链表并返回data()指针libsolc/libsolc.cppsolidity_free通过takeOverAllocation校验并释放指定内存libsolc/libsolc.cpp非法指针会触发abort()solidity_reset在每次编译前重置YulStringRepository并清空分配链表libsolc/libsolc.cpp。完整 API 契约定义于头文件 libsolc/libsolc.h回调中需要传给编译器的数据必须用solidity_alloc()分配所有权转移给编译器后由其负责释放solidity_compile返回的指针必须由调用方用solidity_free()或solidity_reset()释放solidity_reset()调用后此前所有指针包括solidity_compile返回值与solidity_alloc分配的内存都会失效。六、如何更新你的代码迁移速查表原文档给出了针对每一项破坏性变更的迁移指令整理如下旧写法新写法address(f)f为外部函数类型f.addressfunction () external [payable] { ... }receive() external payable { ... }和/或fallback() external [payable] { ... }优先只用receiveuint length array.push(value);array.push(value);新长度用array.length访问array.lengtharray.push()增大、pop()减小函数dev文档中具名返回参数无对应说明为每个具名返回参数写return name ...条目顺序与元组返回类型一致内联汇编变量名与块外声明冲突为内联汇编变量声明选择不与块外声明冲突的唯一标识符打算重写的函数未标记非接口函数加virtual无实现函数接口外加virtual单继承重写加override多继承写override(A, B, ..)列出全部基类内联汇编中的无参 opcode 如pc、gas改为pc()、gas()七、总结Solidity 0.6.0 的核心思路是让继承、存储与汇编更安全让编译器接口更可控virtual/override/abstract显式化继承体系length只读与状态变量遮蔽禁令消除存储安全隐患fallback/receive拆分让接收 ether 的语义更精确try/catch让外部调用失败可被优雅处理在工具链层面新的错误报告器、IPFS 元数据哈希选项、默认开启的 Yul 优化器与重构后的 C API 共同改变了编译器的使用方式。迁移时建议优先对照上文迁移速查表逐条修改并用新版本编译器重新编译全部存量合约同时关注未警告类的行为变更尤其是幂运算类型规则。【免费下载链接】soliditySolidity, the Smart Contract Programming Language项目地址: https://gitcode.com/GitHub_Trending/so/solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进