Часть III · Глава 35 из 43 · Cancun · Deneb

EIP-5656: опкод копирования памяти, которого у EVM никогда не было

Копирование диапазона памяти — одна из самых базовых вещей, которые делает программа, — и всё же годами у EVM не было для этого инструкции. Контракты либо гоняли многословный цикл MLOAD/MSTORE, либо злоупотребляли identity-прекомпайлом как окольным memcpy. EIP-5656 наконец добавляет MCOPY: один опкод, одно копирование.

Обновлено 2 июл. 2026 г. · 6 мин
Предполагается
  • модель памяти EVM
  • прекомпайлы

Cancun / Deneb, март 2024. Вот отрезвляющий пробел. Почти в любой среде программирования копирование блока байт из одного места в другое — единый, дешёвый примитив — memcpy. У EVM есть память — плоский, расширяемый массив байт, — и контракты копируют её диапазоны постоянно: собирая calldata, ABI-кодируя возвращаемые значения, перетасовывая буферы в хеш-функции. И тем не менее за всю свою историю у EVM не было инструкции копирования память-в-память. Разработчики обходили эту дыру двумя неуклюжими способами, оба расточительны. EIP-5656 закрывает её одним новым опкодом. Давайте его выведем.

1 шаг Нет memcpy

Проблема: у EVM были все способы копировать память, кроме хорошего

Чтобы скопировать N байт памяти, у контракта было два варианта, ни один не хорош. Первый — цикл MLOAD/MSTORE (MLOAD/MSTORE loop) Копирование памяти слово за словом: MLOAD читает 32 байта в стек, MSTORE записывает их в место назначения, повторяется для каждого слова. Работает, но это много опкодов на копирование — лишний газ и лишний байткод ради тривиальной операции. : прочитать 32 байта через MLOAD, записать их через MSTORE и повторять, слово за словом. Корректно, но сжигает кучу опкодов и газа и раздувает байткод контракта. Второй — печально известный хак: прекомпайл идентичности (identity precompile) Прекомпайл по адресу 0x04, возвращающий свой вход без изменений. Поскольку вызов к нему копирует вход в выход, контракты злоупотребляли им как memcpy — платя полные накладные расходы вызова за копирование и скрывая намерение. по адресу 0x04 возвращает всё, что вы ему отправите, так что его можно было вызвать и использовать возвращённые данные как копию — платя накладные расходы вызова, чтобы протащить memcpy из прекомпайла, никогда для этого не предназначавшегося. То, что экосистема опиралась на такие трюки, — тревожный знак: не хватало фундаментального примитива.

скопировать диапазон памяти — но опкода копирования нет вариант A: цикл MLOAD → MSTORE MLOAD → MSTORE MLOAD → MSTORE … много опкодов, много газа и байткода вариант B: хак CALL identity (0x04) использовать прекомпайл как memcpy оверхед вызова, непрозрачно, неуклюже у самой базовой операции — копирования памяти — не было примитива
Без опкода копирования контракты либо гоняли цикл MLOAD, затем MSTORE слово за словом (много опкодов, лишний газ и байткод), либо злоупотребляли identity-прекомпайлом по адресу 0x04 как memcpy (накладные расходы вызова, непрозрачно). У самой базовой операции — копирования памяти — не было нативного примитива.

→ Шаг 2: добавляем ровно эту инструкцию.

2 шаг Добавляем MCOPY

EIP-5656: один опкод, одно копирование

Исправление такое же прямое, как и проблема. EIP-5656 добавляет опкод MCOPY Опкод 0x5e. Снимает со стека три элемента — смещение назначения, смещение источника и длину — и копирует столько байт внутри памяти за одну инструкцию, с корректной обработкой перекрывающихся источника и назначения (как memmove). (0x5e). Он снимает со стека три значения — destination, source и length — и копирует столько байт памяти из источника в назначение за одну инструкцию. Он корректно обрабатывает перекрывающиеся регионы (так же, как memmove), и оценивается, как другие операции копирования EVM: небольшая базовая стоимость плюс 3 газа за 32-байтовое слово, плюс любая стоимость расширения памяти. Ни цикла, ни вызова, ни хака — просто примитив по своей честной цене.

MCOPY(dest, src, len): один опкод src dest копирует len байт разом — перекрытие как в memmove оценивается как другие copy-опкоды: 3 газа + 3 за слово примитив, которого не хватало EVM
EIP-5656 добавляет MCOPY (0x5e): снять со стека dest, src, len и скопировать столько байт памяти одним опкодом, с корректной семантикой перекрытия, оценённым как другие операции копирования (3 газа + 3 за слово). Примитив, которого не хватало EVM.

→ Шаг 3: существующие контракты дешевеют, не пошевелив и пальцем.

3 шаг Дешевле бесплатно

Выигрыш: маленький опкод с широким охватом

Поскольку копирование памяти повсюду — ABI-кодирование (ABI encoding) Стандартный способ, которым контракты раскладывают аргументы функций и возвращаемые значения в памяти. Он постоянно вовлекает копирование и перемещение диапазонов байт, так что более дешёвое копирование памяти удешевляет большую долю обычного выполнения контракта. , работа со строками и массивами и особенно криптографический код, перетасовывающий буферы, — более дешёвое копирование незаметно удешевляет и уменьшает большую долю выполнения контракта. И выигрыш почти автоматический: компиляторы (Solidity, Vyper) эмитируют MCOPY там, где раньше эмитировали цикл копирования, так что контракты дешевеют и становятся компактнее просто от перекомпиляции под более новый тулчейн. Хак с identity-прекомпайлом уходит в отставку, намерение становится явным в байткоде, и у EVM наконец есть memcpy, который у неё всегда должен был быть. Это тот же дух, что и у PUSH0: дать машине маленький, очевидный примитив, которого ей не хватало, — и всё, что построено сверху, становится чуть стройнее.

копирование дешевеет — бесплатно цикл / хак MCOPY компиляторы генерируют это сами — контракты дешевеют при рекомпиляции и хак memcpy через identity-прекомпайл списан
MCOPY делает копирования памяти дешевле и компактнее, а компиляторы эмитируют его автоматически — так что контракты дешевеют просто от перекомпиляции. Хак memcpy через identity-прекомпайл уходит в отставку. Крошечный опкод с охватом на всю цепочку, в духе PUSH0.
04 Глубже Куда двигаться дальше