EIP-5656 MCOPY 指令详解:为 EVM 引入高效的内存复制原语
EIP-5656(Memory copying instruction)为以太坊虚拟机(EVM)新增了 MCOPY 指令(操作码 0x5E),用于在内存区域之间高效复制数据。本文以该 EIP 为主体,结合本仓库中的 Dencun 硬分叉元 EIP(EIP-7569)、identity 预编译 EVM 化提案(EIP-7666) 以及 opcode 表源码 等佐证资料,完整讲解其动机、规范、Gas 定价、语义、测试用例与安全考量。读完本文,你将掌握 MCOPY 的栈接口与定价公式、它与 CALLDATACOPY/RETURNDATACOPY 的关系,以及为何它是替代 identity 预编译进行内存复制的更优方案。
概述:一条指令解决内存复制的开销问题
MCOPY 是一条用于复制 EVM 内存区域的指令。在它出现之前,EVM 上实现内存复制只能依赖两种间接手段:调用 identity 预编译(通过 CALL 的输入/输出内存偏移实现复制),或用 MLOAD/MSTORE 逐字搬运。前者伴随高昂的调用开销,后者对非整字复制(partial-word copy)需要加载、掩码、按位或再存储,代价显著。MCOPY 将这些操作封装为单条指令,成本为 Gverylow + Gcopy * ceil(length / 32),一次复制 256 字节仅需 27 gas。
该指令随 Dencun(Cancun)网络升级在主网激活(主网激活时间戳 1710338135),属于执行层(Execution Layer)变更。本仓库中的 EIP-7569 明确将其列为 Dencun 升级包含的 EIP 之一。
背景与动机:EVM 上内存复制的历史困境
identity 预编译方案及其成本变迁
早期开发者通过 identity 预编译实现内存复制:利用 CALL 的输入内存偏移和输出内存偏移完成搬运。其成本为 15 + 3 * (length / 32) gas,外加调用开销。但随着 CALL 的成本上调至 700 gas,identity 预编译方案变得不再经济;后续 EIP-2929 将 CALL 的冷/热账户访问成本调整为 2600/100,使预编译方案略有改善,但仍不足以成为合理选择。
关于 EIP-2929:它在
accessed_addresses集合中记录已访问账户,首次冷访问收取COLD_ACCOUNT_ACCESS_COST(2600),同一交易内再次访问只收WARM_STORAGE_READ_COST(100)。由于预编译地址被预置于访问集合中,identity 预编译的调用成本因此降为 100 + 15 + 3 * (length / 32)。
MLOAD/MSTORE 逐字搬运的局限
复制整字(exact words)可以用 <offset> MLOAD <offset> MSTORE 或 <offset> DUP1 MLOAD DUP2 MSTORE 实现,每个字至少 12 gas。若偏移量在编译期已知且可展开循环(unrolled),这相当高效;但若在运行时以任意起始偏移实现复制,除控制流开销外,还需用 32 ADD 递增偏移,每个字至少再增加 6 gas。
复制非整字(non-exact words)更棘手:最后一个部分字需要同时加载源与目标、做掩码与或运算、再写回。唯一的高效边界情况是最后"部分字"恰好为单字节时,可用 MSTORE8 高效存储。
量化对比:复制 256 字节的 Gas 成本
EIP 给出了复制 256 字节的典型成本对比:
| 方案 | 最低 Gas |
|---|---|
| identity 预编译(EIP-2929 之前) | 至少 757 |
| identity 预编译(EIP-2929 之后) | 至少 157 |
展开的 MLOAD/MSTORE 序列 |
至少 96 |
本 EIP 的 MCOPY |
27 |
实际收益与生态影响
根据对区块 10537502 至 10538702 的分析,约 10.5% 的内存复制操作本可通过 MCOPY 获得性能提升。内存复制是 Solidity、Vyper 等语言的基础操作,EIP 预期该指令能为构建数据结构(包括切片访问与内存对象复制)提供高效手段,同时为未来 CALL 指令的 Gas 再定价提供前瞻性保护。
此外,专用 MCOPY 指令还降低了静态分析与优化的难度:CALL 的副作用必须被整体围栏(fenced)处理,而 MCOPY 已知只影响内存。即使为预编译添加特例,未来硬分叉仍可能改变 CALL 的语义,使基于 identity 预编译的分析仅在特定区块范围内有效。最后,EIP 明确指出内存复制对 EVM384 这类计算密集型操作至关重要,是其中一项显著开销。
规范:操作码、栈接口与语义
操作码与输入栈
MCOPY 指令的操作码为 0x5E,其输入栈布局如下(栈顶为 top - 0):
| 栈位置 | 值 |
|---|---|
| top - 0 | dst |
| top - 1 | src |
| top - 2 | length |
这一顺序与其他复制指令(CALLDATACOPY、RETURNDATACOPY)保持一致。本仓库 assets/eip-8011/compute_mem_ops.md 中的常量定义也印证了这一点:CALLDATACOPY = 0x37、CODECOPY = 0x39、RETURNDATACOPY = 0x3E、MCOPY = 0x5E,它们同属"计算与内存操作"家族。
从 assets/eip-8337/opcodes.py 的 EVM 操作码表可以看出,仓库对 MCOPY 的元数据描述为 (3, 0, False),即弹出 3 个栈项(dst、src、length)、压入 0 个栈项、且不终止基本块——与 CALLDATACOPY、CODECOPY、RETURNDATACOPY 完全一致:
**{op: (3, 0, False) for op in (0x37, 0x39, 0x3E, 0x5E)}, # CALLDATACOPY, CODECOPY, RETURNDATACOPY, MCOPY
Gas 定价
按黄皮书术语,MCOPY 属于 W_copy 操作码组,遵循黄皮书中 W_copy 的 Gas 计算公式。以 EIP 编写时的参考公式为准:
words_copied = (length + 31) // 32
g_verylow = 3
g_copy = 3 * words_copied + memory_expansion_cost
gas_cost = g_verylow + g_copy
即 Gverylow + Gcopy * ceil(length / 32),其中 Gverylow = 3、Gcopy = 3,与黄皮书中 Wcopy 指令族的定价规则一致(黄皮书中的计算为准)。
语义
- 将
length字节从src指向的偏移复制到dst指向的偏移; - 复制过程如同经过一个中间缓冲区(intermediate buffer),因此允许源与目标内存区域重叠;
- 若
length > 0且src + length或dst + length超出当前内存长度,则按相应 Gas 成本扩展内存(memory expansion)。
输出栈
本指令不产生任何栈输出项。
设计理由
EIP 指出,整字与部分字内存复制的生产级实现可参见 Solidity、Vyper 和 Fe 编译器。与 EIP-2929 配合后,虽然 identity 预编译的调用开销从 700 降至 100 gas,但对于将其重新作为合理的复制替代方案而言仍过于昂贵——这正是推动专用指令的根本原因。
向后兼容性
本 EIP 引入了一条此前不存在的新指令。已经部署的合约若使用了该操作码(0x5E),在本 EIP 生效后其行为可能发生变化。由于 0x5E 在 Dencun 之前是无效操作码,普通合约不受影响,只有字节码中恰好包含 0x5E 字节的合约可能面临行为变更(此类合约在 Dencun 前的执行会立即回滚,升级后则正常执行)。
测试用例
EIP 提供了四个基础测试向量,覆盖正常复制、自复制与双向重叠复制场景。所有用例的 gas used 均为 6(3 个 verylow 级操作消耗)。
MCOPY 0 32 32:从偏移 32 复制 32 字节到偏移 0
pre(空格仅为可读性插入):
0000000000000000000000000000000000000000000000000000000000000000 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
post:
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
gas used: 6
MCOPY 0 0 32:从偏移 0 复制 32 字节到偏移 0(自复制)
pre:
0101010101010101010101010101010101010101010101010101010101010101
post:
0101010101010101010101010101010101010101010101010101010101010101
gas used: 6
MCOPY 0 1 8:从偏移 1 复制 8 字节到偏移 0(重叠)
pre(空格在字节 8 处):
0001020304050607 080000000000000000000000000000000000000000000000
post:
0102030405060708 080000000000000000000000000000000000000000000000
gas used: 6
MCOPY 1 0 8:从偏移 0 复制 8 字节到偏移 1(反向重叠)
pre(空格在字节 8 处):
0001020304050607 080000000000000000000000000000000000000000000000
post:
0000010203040506 070000000000000000000000000000000000000000000000
gas used: 6
注意上面两个重叠用例验证了"如同使用中间缓冲区"的语义:无论源在前还是目标在前,复制结果都与先完整读出再写入等价,而不是逐字节原地搬运。
EIP 还指出,完整的测试套件可在执行规范测试(execution spec tests)的 Cancun 目录下 eip5656_mcopy 套件中找到(详见 EIP 正文中的说明)。
在 Dencun 升级中的落地
本仓库中的 EIP-7569(Hardfork Meta - Dencun) 将 EIP-5656 列为 Dencun 网络升级包含的执行层 EIP 之一(与 EIP-1153 瞬态存储、EIP-4844 Blob 交易等并列)。其激活时间如下:
| 网络 | 激活 Epoch | 激活时间戳 |
|---|---|---|
| Goerli | 231680 |
1705473120 |
| Sepolia | 132608 |
1706655072 |
| Holešky | 29696 |
1707305664 |
| Mainnet | 269568 |
1710338135 |
后续演进:identity 预编译的 EVM 化
MCOPY 的引入为生态提供了放弃 identity 预编译的契机。本仓库中的 EIP-7666(EVM-ify the identity precompile) 明确指出:identity 预编译最初就是为了弥补"没有直接复制内存的操作码"而引入的;自 MCOPY 出现后,可以移除 0x04 地址处的 identity 预编译,代之以一段极简 EVM 代码 0x365f5f37365ff3,其对应指令序列为:
CALLDATASIZE PUSH0 PUSH0 CALLDATACOPY CALLDATASIZE PUSH0 RETURN
即把 calldata 复制进内存后原样返回,功能与 identity 预编译等价。这体现了"专用指令取代预编译"的演进路径,也为未来淘汰其他低使用率预编译(如部分哈希函数与 MODEXP)铺平了道路。
安全考量
- 中间缓冲区问题:客户端实现不应使用中间缓冲区来完成复制(正如 C 标准库的
memmove不使用中间缓冲区),否则可能成为拒绝服务(DoS)向量。大多数语言内置的字节搬移函数(如memmove)在性能特性上是正确的,可作为实现参考。 - 内存扩展定价:除上述注意点外,
MCOPY的 DoS 与内存耗尽分析与所有触碰内存的操作码一致,因为其内存扩展遵循相同的定价规则。
总结
MCOPY(0x5E)是 Dencun 升级为 EVM 带来的基础内存复制原语:它以 dst、src、length 三个栈参数完成带重叠保护的内存复制,定价为 Gverylow + Gcopy * ceil(length / 32),复制 256 字节仅需 27 gas。相比 identity 预编译(约 157 gas)与展开的 MLOAD/MSTORE(约 96 gas),其效率优势显著,并为 Solidity/Vyper 编译器、静态分析器与 EVM384 等重计算场景提供了坚实支撑。理解 MCOPY 的语义与定价,是分析 Dencun 后合约 Gas 行为、评估预编译演进路线的基础。
本文版权遵循原文档的声明,相关权利依据 LICENSE.md 中的 CC0 条款放弃。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.24 K638- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python670
SlideSCIPPT插件,支持素材库、AI助手、一键添加图片标题,复制粘贴位置、一键图片对齐、一键插入Markdown(加粗、超链接等行内样式、代码块、LaTeX等块级样式)、便捷导出图片!C#230
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python52874
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go22545
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java36351