找回密码
立即注册
搜索
发回帖 发新帖

5192

积分

0

好友

666

主题
发表于 2 小时前 | 查看: 5| 回复: 0

Core Solidity 是 solc 的新前端。它为语言引入了全新的类型系统,涵盖代数数据类型、模式匹配、泛型、trait 与编译期求值,并将原本内建在编译器中的大量能力迁移到一个用该语言自身编写的标准库里。后端维持不变:Core Solidity 编译到 Yul,由此往后,字节码由 solc 已经为 Classic Solidity(也就是今天 solc 所编译的语言)使用的优化器和代码生成器生成。原型编译器已经以这种方式工作,生成 Yul 并交给 solc。

今年我们交谈过的不少人以为 Core Solidity 会替换整个编译器,这里需要澄清:并非如此。

我们在 The Road to Core Solidity 中介绍了 Core Solidity,在 Core Solidity Deep Dive 中描述了它的设计,并在 Pattern Matching in Core Solidity 中详细展开了其中一个特性。自那几篇文章以来,语法已经有了很大变化,如今看起来更像 Classic Solidity。

这篇文章是一份状态更新,涵盖语言当前形态、仍然缺失的内容、社区反馈、我们由此做出的调整与收获,以及接下来的计划。它反映的是 2026 年 10 月初的状态。Core Solidity 仍处于原型阶段,不用于 mainnet 部署,后续细节可能会发生变化。

语言的当前状态

夏天我们对语法做了一次重构,使它更贴近 Classic Solidity。早期文章中的代码示例用的是临时语法,现在已经无法编译,不过它们描述的概念依旧成立。如今一个小型合约长这样:

import * from std;
import * from std.dispatch;
import * from std.Generic;
import * from std.StorageGeneric;

// 一个托管合约,其生命周期是存储在状态变量中的 enum。
enum Phase {
    AwaitingPayment,
    Funded(uint256),
    Released(uint256)
}

contract Escrow {
    phase: Phase;

    constructor() {
        phase = Phase.AwaitingPayment;
    }

    // 只能注资一次;第二次存入会被拒绝。
    function deposit(amount: uint256) public {
        match (phase) {
            case Phase.AwaitingPayment {
                phase = Phase.Funded(amount);
            }
            default {
                revertWithError("already funded");
            }
        }
    }

    // 记录释放,使其无法重复。
    function release() public returns (uint256) {
        match (phase) {
            case Phase.Funded(amount) {
                phase = Phase.Released(amount);
                return amount;
            }
            default {
                revertWithError("nothing to release");
                return 0;
            }
        }
    }

    // 0 = 等待付款,1 = 已注资,2 = 已释放
    function status() public returns (uint256) {
        match (phase) {
            case Phase.AwaitingPayment { return 0; }
            case Phase.Funded(_) { return 1; }
            case Phase.Released(_) { return 2; }
        }
    }
}

合约和函数看起来正如 Solidity 开发者所预期的那样。可见的区别在于:类型跟在名称后面,也就是后缀类型(phase: Phase、amount: uint256);除了一小部分内置类型之外,除非导入(包括标准库),否则没有任何内容在作用域内;以及 enum 变体可以携带数据。像 Phase 这样的 enum 就是我们所说的代数数据类型。std 提供 uint256 和 revertWithError 这类基本类型与函数,std.dispatch 提供 public 函数背后的代码,std.Generic 和 std.StorageGeneric 则让 Phase 这样的代数数据类型能够存在于 storage 中。目前这些必须是通配符导入,因为编译器为 dispatch 和 storage 生成的代码会引用其中的名称。match 会拆解 Phase,而一个遗漏了某个 case 又没有 default 分支的 match 会被编译器直接拒绝。

这个示例也暴露了两个粗糙之处:还没有自定义错误,所以 revert 携带的是普通字符串;另外 release 需要在 revertWithError 之后跟一个 return 才能通过编译,尽管那一行永远不会执行——这是一个未解决问题。出于简洁考虑,示例省略了价值转移和访问控制。你可以在 playground 中打开它、修改它并调用它。

位于 solcore 仓库 中的原型编译器目前支持以下内容,链接会在 playground 中打开可运行示例:

  • 代数数据类型,支持模式匹配和穷尽性检查(托管合约、带类型化所有权的 NFT)。
  • Trait 和实现(电灯开关),以及用于生成 trait 实现的 derive。
  • 带 where 约束的泛型和类型推断(为每个有序类型提供一个 max)。
  • 具有显式导入和导出的模块,包括在导出类型时不导出其变体,这样其他模块就不能直接构造它们(恒定乘积池)。
  • 编译期求值,这也是常量的编写方式(comptime)。
  • Lambda(重入锁金库)。
  • 带命名字段的 struct。
  • storage 和 mapping,包括 storage 中的代数数据类型。
  • ABI 编码和解码,为用户定义类型派生编码器和解码器。
  • 数组字面量和 storage 数组。

原型也在移植的现有合约上做过验证。该仓库包含 Ethereum 存款合约、WETH9 和一个最小 ERC20 token 的移植版本,其测试套件会在持续集成中通过 solc 编译它们并在 EVM 上执行。这些移植版本暂时省略了事件,因为原型目前还没有事件。

playground 是尝试 Core Solidity 的最快方式。你可以编译合约、调用其函数、查看生成的字节码,并分享指向该示例和你当前打开视图的链接。除了上面链接的示例之外,它还包括组合于一个共享引擎模块之上的三个金库和由功能模块构建的 ERC20 风格 token。新语言特性通常会先进入 solcore 仓库,之后才会抵达 playground,所以 playground 可能会落后于上面的列表。例如,struct 在仓库中已经可用,但在撰写本文时尚未进入 playground。

我们还在 Lean 4 证明助手中形式化 Core Solidity 的语义,工作位于 solcore-lean 仓库。这是进行中的工作,一旦有可报告的结果,我们会分享更多。

仍缺失的内容

生产合约依赖的若干特性仍然缺失。下面的列表描述的是 solcore 仓库 main 分支上的原型编译器。许多缺失特性已计划加入该语言,未出现在原型中并不意味着会被排除在 Core Solidity 之外。每周都有 pull request 被打开和合并,因此这些列表在本文发布后不久就会过时,而且它们也并非详尽无遗。

进行中,已有开放的 pull request

  • 通用 memory 数组和数组切片。目前 memory 数组仅以有限形式存在。
  • 有符号整数以及 256 位以外的整数宽度。今天,除了 word(原始 256 位 EVM word)之外,uint256 是唯一的运行时整数类型。
  • 接口和对其他合约的调用,从基本类型开始。今天只有低级调用可用。
  • && 和 || 的短路求值。今天两个操作数都会被求值。

尚未具备

  • 检查算术。目前整数运算会回绕,将其默认设为检查运算仍有待完成。
  • 事件和 emit。原始 EVM log 可用。
  • 自定义错误声明和带参数的错误。通过 require 或 revertWithError,用字符串或错误选择器进行 revert 是可行的,不过该字符串尚未按 Classic Solidity 编码 Error(string) 的方式编码。
  • view 和 pure。
  • Immutables。
  • 使用 new 从其他合约部署合约。
  • try/catch 的替代方案。
  • 通过 .selector 或类似机制获取函数选择器。
  • 若干运算符:移位、幂运算、自增和自减,以及一元负号。
  • 对 storage 布局的控制,就像 Classic Solidity 中的 layout at。
  • 瞬态状态变量。tload 和 tstore 操作码可以直接使用。

Gas 成本和代码大小并不是原型的重点,我们尚未将其与 Classic Solidity 比较。

刻意不同

Core Solidity 没有继承,因此没有 virtual,也没有抽象合约;同样没有 Classic 风格的库。trait 和模块的组合取代了它们。composition 和 extensions 示例展示了今天的样子。接下来几周内我们会发布一篇关于无继承组合的深度剖析。

文档

进行中的参考文档是为编译器和工具开发者编写的,记录了语法、类型系统和模块系统。面向合约开发者的大部分章节尚未编写。

社区告诉了我们什么

自 2026 年 3 月以来,我们完成了 18 次关于 Core Solidity 的定性访谈,大多数是一对一,也有几次有多位嘉宾参与。我们与协议开发者、安全研究人员和审计员、教育工作者、库和开发者工具维护者、一个钱包团队,以及研究编程语言设计的人进行了交流。在大多数通话中,他们第一次在 playground 中阅读 Core Solidity 代码,并向我们讲述他们看到的内容。最早的访谈发生在 playground 上线之前,因此下面关于代码的反应来自较晚的访谈。如果你也想分享反馈,文末说明了联系方式。

第一反应

代数数据类型和模式匹配几乎受到所有看到它们的人的好评。几位嘉宾特别询问或指出了编译器会拒绝遗漏 case 这一行为。

大多数没有提前准备就开始阅读 Core Solidity 合约的嘉宾都能看懂,很多人说它看起来像 Solidity。

继承和组合

当一个 Core Solidity 合约由模块组合而成时,来自模块的函数只有在作者显式将其包装在 public 函数中之后,才能从外部调用。这是该概念的最小示例,节选自 extensions 示例:

// ownable.sol
// 这里省略了 imports 以及 ownerSlot() 和 owner() 辅助函数。
export {owner, initOwner, requireOwner, transferOwnership};

function initOwner(who: address) {
    require(owner() == address(0), "already initialized");
    sstore(ownerSlot(), Typedef.rep(who));
}

function requireOwner() {
    require(sender() == owner(), "not the owner");
}

function transferOwnership(to: address) {
    requireOwner();
    sstore(ownerSlot(), Typedef.rep(to));
}
// Token.sol
// 这里省略了其他导入。
import {owner as storedOwner, initOwner, requireOwner, transferOwnership as setOwner} from ownable;

contract Token {
    constructor() {
        initOwner(sender());
    }

    function mint(to: address, amount: uint256) public {
        requireOwner();
        // 这里省略了余额更新。
    }

    function owner() public returns (address) {
        return storedOwner();
    }

    function transferOwnership(to: address) public {
        setOwner(to);
    }
}

该模块拥有自己的 storage,并导出普通函数。Token 导入了其中四个函数,而在这四个函数中,只有 owner 和 transferOwnership 成为其接口的一部分,因为它们是 Token 用 public 函数包装的两个函数。initOwner 在构造函数中运行,requireOwner 保护 mint(其余额更新在上文中省略),两者都不能从外部调用。看到这一点的审计员和安全研究人员很喜欢它,因为找出一份合约的暴露面正是审查工作的第一步。

大多数表明立场的嘉宾对移除继承感到满意,其中几位审计合约的人表示继承让审查变得更困难。少数团队今天依赖特定的继承模式,他们希望在形成看法之前看到这些模式用组合表达出来。我们听到的问题是:组合能否经得起大型真实世界合约的考验?可复用模块如何在不直接处理原始 storage slot 的情况下拥有自己的状态?playground 中的几个示例直接来自访谈中的反馈和请求,我们正在构建更多、更大的组合示例。

extensions 和 reentrancy 示例中的模块今天直接读写 ERC-7201 命名空间 slot。这种方法的一个已知缺点是,除了计算 slot 的标准库函数之外,语言尚不支持这种模式。编译器不知道这些 slot,因此它无法报告它们,也无法阻止两个模块使用同一个 slot——而 solc 在 Classic Solidity 中布局状态变量时可以做到这一点。

工具和采用

维护分析和开发者工具的嘉宾预计,支持 Core Solidity 只需要适度的工作量。对于读取语法树的工具,他们预计主要需要一个新解析器,而处理字节码的工具则不受影响。对类型进行推理的静态分析器需要的不仅仅是一个解析器,具体需要多少取决于最终的类型系统。有一位要求语法树保持接近今天的样子。

关于采用,我们听说保守、成熟的项目只有在新的编译器版本有经过验证的记录之后,并且审计员和工具准备就绪之后,才会采用它们,他们预计也会以同样的方式对待 Core Solidity。

开放问题

我们还询问了两个仍然开放的话题。

第一个是两种语言之间的 ABI 兼容性。Core Solidity 被设计为使用与 Classic Solidity 相同的 ABI,因此 Core Solidity 合约将能够从外部调用所有 Classic Solidity 合约。在原型中,类型化外部调用仍在进行中,并且 ABI 编码与 Classic Solidity 存在已知差异。另一个方向才是开放的部分。外部接口中的代数数据类型需要当前 ABI 规范未定义的编码,因此 Classic Solidity 中的合约,或任何其他实现当前 ABI 的语言中的合约,若不进一步工作,就无法调用每一个 Core Solidity 接口。大多数表明立场的嘉宾都乐于将其视为较低优先级。有一位要求我们尽可能支持两个方向,以便两种语言彼此保持可组合性。

第二个是标准库的范围。意见从最小库到综合库不等,中间立场是一个小型基础,由成熟库在其上构建。我们听到的一个支持保持小型化的论点是,其中的每个 bug 都会影响到每一个使用它的人。我们正在研究社区将如何参与塑造该库。

这两个问题我们都尚未决定。如果你对其中任何一个有看法,我们很想听听。

今天的 Solidity 和用 AI 编写的代码

我们还询问了 Classic Solidity 中最令人痛苦的是什么。最常听到的答案是合约大小限制、stack-too-deep 错误和编译时间。合约大小限制由 EVM 本身设定。stack-too-deep 错误和编译时间正是实验性 SSA CFG 代码生成器要解决的问题,而这项工作位于 Core Solidity 共享的后端中。

大多数嘉宾预计合约将越来越多地由 AI 编写、由人类审查。有几位据此得出结论:这意味着可读、显式代码的价值会进一步提升。还有一人补充说,当作者是 agent 时,精确的编译器错误更加重要。

我们改变了什么,学到了什么

访谈除了给我们反馈之外,还让与我们交谈的人看到我们正在做什么以及语言将走向何方。几位嘉宾推荐了我们接下来应该与谁交谈,因此这些对话也带来了新的联系人。在我们这边,访谈已经带来了这些变化:

  • 我们添加了人们要求的 playground 功能,包括字节码输出和在 playground 中调用已部署合约的函数。
  • 在外部读者发现示例合约中的问题后,我们修正或扩展了这些示例合约。
  • 我们为来自公开团队电话会议和访谈的反馈创建了 issue。访谈中提出的一个点是,持有代数数据类型的 state variable 应当必须显式初始化。今天它会以第一个变体开始,就像 Classic Solidity 中未初始化的 enum 以第一个成员开始一样。因此,要求显式值相比 Classic Solidity 将是一个破坏性变更。在撰写本文时,这一问题跟踪于一个开放 issue。
  • 我们根据嘉宾告诉我们的内容修订了访谈问题。

除了个别请求之外,有三件事很突出:

新的前端,相同的后端: 与我们交谈的许多人不知道 Core Solidity 是现有后端上的新前端。今后我们会更清楚地传达这一点。

文档: 这是人们看到真实代码后最先要求的东西之一。playground 示例对第一次阅读来说足够了,之后人们想要一些可以照着工作的内容。我们将在接下来的开发周期中优先更新文档。

编译时间: 编译缓慢是 Classic Solidity 用户一再提出的痛点,也是访谈中最常被提到的问题之一。在 Classic Solidity 中,大部分时间花在 Core Solidity 共享的后端中,因此那里的改进会同时惠及两种语言。原型自己的前端尚未针对速度进行优化。

接下来会发生什么

我们正在确定原型首个版本的发布范围,让人们可以安装并尝试。其目的是尽早获得更多反馈。这是我们的优先事项之一,我们还没有确定日期。

接下来是一篇关于无继承组合的深度剖析,我们也在制定标准库的社区流程。

与此同时,你可以尝试 playground,在 solcore 仓库中关注这项工作,并加入我们的每周团队电话会议,这些会议对所有人开放。如果你想比 playground 走得更远,仓库中有原型编译器的构建说明。

11 月我们将在孟买。在 DeFi Security Summit(11 月 1 日和 2 日),我们将进行演讲 "What you need to know about Core Solidity"。在 Devcon,我们将在 11 月 5 日 12:00 至 12:50 在 Workshop 1 房间举办工作坊 "Your first Core Solidity smart contract"。我们很乐意与那里任何想要演示、或有关于 Solidity 的反馈或看法的人交谈。

访谈仍在继续,还有更多已经排上日程。如果你为 Solidity 编写、审计、教学或构建工具,并希望接受访谈、分享反馈或观看 Core Solidity 的演示,请写信至 coresolidity at argot dot org。

原文链接:soliditylang.org/blog/20...




上一篇:杂谈|新加坡 Claude 活跃度超预期 5.81 倍,藏在 IP 背后的生产力信号
下一篇:DDR等长做了,上电训练仍失败?先查这5处Layout
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-18 05:25 , Processed in 0.067041 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表