
Web3-утопия Гэвина: история эволюции смарт-контрактов и виртуальных машин Polkadot
Автор: Гоуцзы
Polkadot — старый известный проект в области блокчейна. Даже те, кто занимается индустрией Web3 и не пользовался их продуктами, как минимум слышали о нём.

В последнее время новостей о Polkadot выходит немало: новая архитектура JAM привлекает внимание, а демонстрация создателем игры DOOM показывает новое лицо проекта, что вызвало у меня огромный интерес к его изучению. Проект Polkadot довольно масштабный: есть блокчейн Polkadot, фреймворк для разработки Substrate, релейная и параллельные цепи и так далее. Ограниченный объём статьи и внимание, я выбрал именно линию исполнения (виртуальные машины), которая интересует меня больше всего, чтобы рассказать её историю, текущее состояние и сопутствующую информацию.
Предистория
Когда только создавали Ethereum, Gavin Wood, заинтересовавшись проектом, присоединился к разработке. В то время Ethereum был лишь первоначальным фреймворком, и после прихода Gavin техническая реализация стала возможна. Посмотрим на его вклад в Ethereum:
(1) Реализовал PoC-1 (Proof of Concept-1) Ethereum; (2) Почти в одиночку разработал самый ранний клиент Ethereum на C++; (3) Написал техническую спецификацию Ethereum Yellow Book; (4) Изобрел язык программирования высокого уровня Solidity для разработки смарт-контрактов.
Если вас интересует эта история, вы можете прочитать книгу «Точка ко всему: Ethereum и будущее цифровых финансов», а легендарную историю Gavin Wood легко найти в сети, поэтому здесь мы её опустим.
Сосредоточимся на Solidity и EVM. Сначала посмотрим на простой пример счетчика на Solidity:
pragma solidity ^0.8.3; contract Counter { uint public count; function get() public view returns (uint) { return count; } function inc() public { count += 1; } function dec() public { count -= 1; } }
В этом примере объявлена состоятельная переменная count, которую можно рассматривать как одну ячейку в базе данных. Вы можете запрашивать и изменять её через код управления базой данных. В данном примере контракт определяет функции inc, dec и get, которые используются для изменения или получения значения переменной.
После компиляции исходного кода компилятором solidity solc получается байткод (см. ниже). После развертывания узла через JSON-RPC уровень исполнения сначала проходит консенсус, подтверждение, а затем передает выполнение EVM.
6080604052348015600e575f80fd5b506101d98061001c5f395ff3fe608060405234801561000f575f80fd5 b506004361061004a575f3560e01c806306661abd1461004e578063371303c01461006c5780636d4ce63c14 610076578063b3bcfa8214610094575b5f80fd5b61005661009e565b60405161006391906100f7565b60405 180910390f35b6100746100a3565b005b61007e6100bd565b60405161008b91906100f7565b604051809103 90f35b61009c6100c5565b005b5f5481565b60015f808282546100b4919061013d565b92505081905550565 b5f8054905090565b60015f808282546100d69190610170565b92505081905550565b5f819050919050565b 6100f1816100df565b82525050565b5f60208201905061010a5f8301846100e8565b92915050565b7f4e487 b7100000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61 0147826100df565b9150610152836100df565b925082820190508082111561016a57610169610110565b5b9 2915050565b5f61017a826100df565b9150610185836100df565b925082820390508181111561019d576101 9c610110565b5b9291505056fea26469706673582212207b7edaa91dc37b9d0c1ea9627c0d65eb34996a5e3 791fb8c6a42ddf0571ca98164736f6c634300081a0033
Давайте посмотрим, как это выглядит в виде ассемблерных инструкций:
PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D9 DUP1 PUSH2 0x1C PUSH0 CODECOPY PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0xF JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x4A JUMPI PUSH0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x6661ABD EQ PUSH2 0x4E JUMPI DUP1 PUSH4 0x371303C0 EQ PUSH2 0x6C JUMPI DUP1 PUSH4 0x6D4CE63C EQ PUSH2 0x76 JUMPI DUP1 PUSH4 0xB3BCFA82 EQ PUSH2 0x94 JUMPI JUMPDEST PUSH0 DUP1 REVERT JUMPDEST PUSH2 0x56 PUSH2 0x9E JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x63 SWAP2 SWAP1 PUSH2 0xF7 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x74 PUSH2 0xA3 JUMP JUMPDEST STOP JUMPDEST PUSH2 0x7E PUSH2 0xBD JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x8B SWAP2 SWAP1 PUSH2 0xF7 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x9C PUSH2 0xC5 JUMP JUMPDEST STOP JUMPDEST PUSH0 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x1 PUSH0 DUP1 DUP3 DUP3 SLOAD PUSH2 0xB4 SWAP2 SWAP1 PUSH2 0x13D JUMP JUMPDEST SWAP3 POP POP DUP2 SWAP1 SSTORE POP JUMP JUMPDEST PUSH0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0x1 PUSH0 DUP1 DUP3 DUP3 SLOAD PUSH2 0xD6 SWAP2 SWAP1 PUSH2 0x170 JUMP JUMPDEST SWAP3 POP POP DUP2 SWAP1 SSTORE POP JUMP JUMPDEST PUSH0 DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH2 0xF1 DUP2 PUSH2 0xDF JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH0 PUSH1 0x20 DUP3 ADD SWAP1 POP PUSH2 0x10A PUSH0 DUP4 ADD DUP5 PUSH2 0xE8 JUMP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH32 0x4E487B7100000000000000000000000000000000000000000000000000000000 PUSH0 MSTORE PUSH1 0x11 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH0 REVERT JUMPDEST PUSH0 PUSH2 0x147 DUP3 PUSH2 0xDF JUMP JUMPDEST SWAP2 POP PUSH2 0x152 DUP4 PUSH2 0xDF JUMP JUMPDEST SWAP3 POP DUP3 DUP3 ADD SWAP1 POP DUP1 DUP3 GT ISZERO PUSH2 0x16A JUMPI PUSH2 0x169 PUSH2 0x110 JUMP JUMPDEST JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH0 PUSH2 0x17A DUP3 PUSH2 0xDF JUMP JUMPDEST SWAP2 POP PUSH2 0x185 DUP4 PUSH2 0xDF JUMP JUMPDEST SWAP3 POP DUP3 DUP3 SUB SWAP1 POP DUP2 DUP2 GT ISZERO PUSH2 0x19D JUMPI PUSH2 0x19C PUSH2 0x110 JUMP JUMPDEST JUMPDEST SWAP3 SWAP2 POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH28 0x7EDAA91DC37B9D0C1EA9627C0D65EB34996A5E3791FB8C6A42DDF057 SHR 0xA9 DUP2 PUSH5 0x736F6C6343 STOP ADDMOD BYTE STOP CALLER
С точки зрения теории компиляции процесс выполнения выглядит так: Исходный код Solidity -> лексический анализатор -> синтаксический анализатор -> компиляция-> байткод -> виртуальная машина -> интерпретационное выполнение
Да, с точки зрения IT-индустрии это всего лишь новый язык программирования, но тогда это действительно было потрясающе. Спустя два года после присоединения к проекту разработки Ethereum, он успешно запустился. Если Виталик Бутерин создал тело Ethereum, то Gavin дал ему душу. Bitcoin был системой электронных платежей, а Ethereum сделал блокчейн программируемым. Лозунг «Мировой компьютер» изменил всё.
Web 3.0
Пока Gavin занимал пост соучредителя и технического директора Ethereum, 17 апреля 2014 года в блоге «Insights into a Modern World» он опубликовал статью DApps: What Web 3.0 Looks Like[1], в которой подробно объяснил, каким он видит эпоху Web 3.0, и описал четыре компонента, составляющие Web 3.0. Понятие Web 3.0 настолько популярно и влиятельно, что это очевидно всем. Gavin обладал не только отличными техническими навыками, но и дальновидением. О истории и спорах вокруг Web 3.0 можно почитать в статье Wikipedia про Web3[2]. Американский предприниматель и венчурный инвестор Ноа Шпильвак[3] предложил расширить определение Web 3.0, отразив в нем конкретные проявления новых этапов зрелости различных технологических трендов. Вот цитата:
• Всеобщее подключение: распространение и развитие широкополосных сетей, доступ мобильных устройств к интернету. (например, планшеты)
• Облачные вычисления: бизнес-модель «SaaS-сервисы», взаимодополняемость веб-сервисов, распределенные и сетевые вычисления, вычисления на заказ («вычисления в облаке»).
• Открытые технологии: открытые API и протоколы, открытые форматы данных, платформы с открытым исходным кодом и открытые данные (Creative Commons, лицензии на открытые данные).
• Открытая идентичность: OpenID, открытая репутация, междоменная идентификация и профили.
• Интеллектуальные сети: технологии семантической паутины, такие как RDF, OWL, SWRL, платформы приложений SPARQL и декларативное хранение данных.
• Распределенные базы данных: Всемирная База Данных («World Wide Database», реализуемая технологиями семантической паутины).
• Интеллектуальные приложения: обработка естественного языка, машинное обучение, машинное рассуждение, автономные агенты.

В конце 2015 года Gavin покинул Ethereum.
Затем он основал Parity Technologies и разработал клиент Ethereum на языке Rust, который долгое время монополизировал рынок кошельков экосистемы Ethereum. Причина ухода Gavin из Ethereum нам неизвестна, но известно, что его видение заключалось в создании совершенно нового децентрализованного интернета.
«Для меня Ethereum был экспериментом, прототипом продукта для проверки жизнеспособности технологии. Это также была моя школа, из которой я выпустился. Я хочу попробовать сделать еще больше.»
"На самом деле, наибольшее количество знаний я получил из Ethereum не техническое (тогда там была отдельная исследовательская группа, отвечающая за технические детали), а социальный опыт. Одна из таких тем — управление. Я считаю, что в блокчейн-системах крайне важно повышать способность системы за счет управления. Это революционное новое свойство, которого именно не хватало Ethereum."
Во время работы в Ethereum Gavin всегда был практиком, а не дизайнером архитектуры, но он постоянно готовил новые инновации.
Рождение Polkadot
Через год Gavin решил задачу, которая долго волновала его, и в 2016 году опубликовал белую книгу Polkadot. Многие знают, что помимо решения проблемы масштабируемости, Polkadot также стремится обеспечить связь между независимыми блокчейнами, то есть решить проблему кроссчейнинга. Но самое главное, что нужно знать о Polkadot: шардирование. Доведя шардирование до предела, мы получаем Polkadot.
Это лучше всего подтверждают слова самого создателя Polkadot:
"Логика проектирования Polkadot изначально не была направлена напрямую на обеспечение интероперабельности. Мы ждали внедрения шардирования в Ethereum. Но оно так и не появилось. Поэтому я решил создать собственный «Ethereum» с более высокой масштабируемостью. В процессе проектирования я довел концепцию шардирования до крайности и просто отказался от него, начав проектировать отдельные цепи. При таком подходе разные цепи могут передавать друг другу информацию, и конечным результатом стала коммуникация через общий уровень консенсуса."
Как же правильно понимать шардирование? Давайте сначала обсудим дилемму Ethereum. Проблема производительности всегда была слабым местом Ethereum. Уже в 2018 году из-за популярной криптокошки (CryptoKitties) возникла серьезная перегрузка сети, что увеличило время транзакций и держало комиссии на высоком уровне. Это похоже на банк с одним окном: чем больше людей приходит за услугами, тем длиннее очередь.
А если у банка несколько окон, обслуживающих клиентов одновременно, очереди не будет. Это базовая логика шардирования: разделить весь набор узлов сети на различные зоны, называемые шардами, и разделить обработку большого количества транзакций между разными шардами, что значительно повышает эффективность.
В Polkadot каждый шард содержит логику ядра и поддерживает параллельные транзакции и обмен данными, позволяя множеству блокчейнов подключиться к единой сети.
Появление Polkadot — это не просто сравнение или превышение идеи Ethereum 2.0. Более новаторским аспектом является то, что: внутри Polkadot может существовать множество Ethereum. Он перестал быть просто обычным блокчейном; «Polkadot стремится предоставить истинно открытую и свободную платформу для социальных инноваций».
Это идеал Web 3, идеал Polkadot и идеал Gavin.
Polkadot 1.0
Сама релейная цепь Polkadot не поддерживает смарт-контракты напрямую, но подключенные параллельные цепи[4] могут свободно определять правила перехода состояния, обеспечивая функцию смарт-контрактов. Отличие от других экосистем заключается в том, что в контексте Polkadot параллельные цепи и смарт-контракты находятся на разных уровнях стека: смарт-контракты размещаются поверх параллельных цепей. Параллельные цепи обычно описываются как блокчейны первого уровня, но отличаются тем, что им не нужно самостоятельно обеспечивать безопасность, а также возможностью апгрейда и интероперабельности.
Polkadot предоставляет разработчикам гибкость при создании смарт-контрактов, поддерживая как контракты Solidity, выполняемые EVM, так и контракты на основе Wasm с использованием ink!:
Контракты, совместимые с EVM
Экосистема Polkadot поддерживает виртуальную машину Ethereum (EVM) благодаря инструментарию Frontier[5]. Frontier позволяет блокчейнам на базе Substrate нативно выполнять смарт-контракты Ethereum и обеспечивает бесшовное взаимодействие с экосистемой Ethereum через совместимые API/RPC интерфейсы Ethereum. Контракты пишутся на языках Solidity или Vyper. EVM стандартизирована в блокчейне, включая такие параллельные цепи Polkadot, как Astar, Moonbeam и Acala. Эта совместимость позволяет развертывать контракты в нескольких сетях с минимальными изменениями, пользуясь зрелой и широкой экосистемой разработки.
Пример: Astar[6] — ключевая платформа смарт-контрактов на Polkadot. Ее уникальный подход с несколькими машинами виртуальных сред поддерживает одновременно EVM и WebAssembly (Wasm) смарт-контракты. Поддержка двух ВМ позволяет разработчикам выбирать предпочтительную среду программирования, сохраняя полную совместимость с Ethereum. Рантайм платформы[7] построен на Substrate с использованием FRAME, сочетая ключевые компоненты из Polkadot-SDK с кастомными модулями для обработки уникальных функций.

Эти цепи обычно используют готовые модули контрактов и добавляют некоторые инновации. Например: Phala[8]: использует модуль контрактов в среде доверенного исполнения (TEE) для реализации конфиденциальных смарт-контрактов и интероперабельности. Aleph Zero[9]: использует трэй паллет контрактов в среде нулевого разглашения (ZK). t3rn[10]: использует модуль контрактов как строительный блок для многосетевой исполнюемости смарт-контрактов. Подробнее о технических характеристиках совместимых с EVM параллельных цепей, таких как Aster, Moonbeam, Acala и др., смотрите в официальной документации: Параллельные цепи контрактов[11]
Контракты Wasm (ink!):
Polkadot предоставляет модуль Contracts[12] через фреймворк FRAME, который использует WebAssembly в качестве среды выполнения контрактов. Теоретически любой язык, компилируемый в Wasm, может использоваться для разработки смарт-контрактов, но ради оптимизации и удобства Parity специально推出了 ink![13].
Прежде чем обсуждать ink!, необходимо разъяснить, что такое Substrate[14] и его модуль контрактов (pallet-contracts). Substrate — это фреймворк для создания блокчейнов: как независимых, так и подключенных к Kusama[15] или Polkadot[16] блокчейнов, так называемых параллельных цепей.
Substrate содержит множество модулей, которые в терминологии Substrate называются pallets. Substrate поставляется с набором модулей, покрывающих большинство требований современных блокчейнов — стейкинг, фунгибельные токены, невзаимозаменяемые токены (NFT), управление и т.д.
Substrate также поставляется с модулем для смарт-контрактов, называемым Contracts pallet. Если вы разрабатываете параллельную цепь на Substrate, вы можете легко добавить функцию смарт-контрактов, включив этот pallet. Данный модуль является просто средой выполнения, принимающей входные файлы WebAssembly. Смарт-контракты этого модуля должны быть скомпилированы в целевую архитектуру WebAssembly (Wasm).
Как здесь работает ink!? ink! — это язык программирования, а точнее, встроенный предметно-ориентированный язык (eDSL) популярного языка программирования Rust. Это означает, что вы можете использовать весь обычный синтаксис Rust вместе с некоторыми новыми деталями, адаптированными под мир смарт-контрактов. Модуль контрактов принимает эти контракты ink! и выполняет их безопасным образом. Проще говоря:
Используя ink!, вы можете писать смарт-контракты на Rust для блокчейнов, построенных на Substrate и содержащих Contracts pallet.
ink! не создает новый язык; это просто стандартный Rust с четким «форматом контракта» и специальными атрибутами-макро #[ink(…)]. Эти атрибуты сообщают ink!, что представляет собой та или иная часть смарт-контракта на Rust, и в конечном итоге позволяют ink! выполнять всю магию для создания байткода Wasm, совместимого с Polkadot SDK. Поскольку смарт-контракты ink! компилируются в Wasm, они обеспечивают высокую скорость исполнения, независимость от платформы и усиленную безопасность за счет изолированного выполнения.
Посмотрим на простой контракт ink!. Данный контракт хранит в своем хранилище булево значение. После создания контракта он устанавливает значение в true. Контракт открывает две функции: одну для чтения текущего значения булевого (fn get()), другую для переключения значения на противоположное (fn flip()).
#[ink::contract] mod flipper { #[ink(storage)] pub struct Flipper { value: bool, } impl Flipper { #[ink(constructor)] pub fn new(init_value: bool) -> Self { Self { value: init_value } } #[ink(message)] pub fn flip(&mut self) { self.value = !self.value; } #[ink(message)] pub fn get(&self) -> bool { self.value } }}
Функциональность кажется схожей с предлагаемой Solidity: Storage (хранение), Message (сообщения), Errors (ошибки), Event (события) и т.д. Для разработчиков контрактов это означает, что они могут писать смарт-контракты на ink!, но также могут выбрать другие языки: компилятор Solang для Solidity[17] и ask! для AssemblyScript (как показано ниже)
import { env, Pack } from "ask-lang";import { FlipEvent, Flipper } from "./storage";@contractexport class Contract { _data: Pack<Flipper>; constructor() { this._data = instantiate<Pack<Flipper>>(new Flipper(false)); } get data(): Flipper { return this._data.unwrap(); } set data(data: Flipper) { this._data = new Pack(data); } @constructor() default(flag: bool): void { this.data.flag = flag; } @message({ mutates: true }) flip(): void { this.data.flag = !this.data.flag; let event = new FlipEvent(this.data.flag); // @ts-ignore env().emitEvent(event); } @message() get(): bool { return this.data.flag; }}
Добавить новый язык несложно. Достаточно иметь компилятор языка, подходящего для WebAssembly, и реализовать API Contracts pallet.
На данный момент это API включает около 15-20 функций, необходимых для любых возможностей смарт-контрактов: доступ к хранилищу, криптографические функции, информация об окружении (например, номер блока), функции для получения случайных чисел или принудительного завершения контракта и т.д. Не все из них обязательно должны быть реализованы в самом языке. «Hello, World!» для ink! требует всего six API-функций. Следующая диаграмма описывает эту взаимосвязь:

Выбор ink! и Contracts pallet имеет множество преимуществ по сравнению с EVM. Кратко резюмируем некоторые из преимуществ, детально рассмотренных выше:
• ink! — это просто Rust. Вы можете использовать все обычные инструменты Rust: clippy, crates.io[19], IDE и т.д.
• Rust — язык, сочетающий многолетние исследования в области языков программирования. Он безопасен и быстр. Кроме того, из более старых языков смарт-контрактов, таких как Solidity, были извлечены основные уроки и интегрированы в дизайн ink!. Были выбраны более разумные поведения по умолчанию, например, аргументы по умолчанию запрещены или функции сделаны приватными.
• Rust — удивительный язык, который семь лет подряд занимает первое место в списке самых популярных языков программирования на StackOverflow (источник[20]).
• Если вы компания и хотите нанять разработчиков смарт-контрактов, вы можете искать специалистов в экосистеме Rust, которая намного шире нишевого рынка разработчиков Solidity.
• ink! нативен для Substrate, использует похожие примитивы и ту же систему типов.
• Путь миграции от контракта к параллельной цепи очень четкий. Поскольку ink! и Substrate написаны на Rust, разработчики могут использовать большую часть кода, тестов, а также фронтендного и клиентского кода.
• WebAssembly — отраслевой стандарт, который используется не только в мире блокчейна. Его постоянно совершенствуют крупные компании, такие как Google, Apple, Microsoft, Mozilla и Facebook.
• WebAssembly расширяет серию языков, доступных разработчикам смарт-контрактов, включая Rust, C/C++, C#, Typescript, Haxe, Kotlin и другие. Это означает, что вы можете писать смарт-контракты на любом знакомом вам языке. Такой дизайн более перспективен, чем жесткая связка языка и архитектуры исполнения.
Подробнее по данной теме читайте What is Parity's ink!?[21].
Polkadot 2.0 / JAM
28 февраля Гэвин Вуд впервые публично продемонстрировал игру DOOM, запущенную на JAM, во время JAM Tour в Шанхае! Это исторический момент для индустрии блокчейна. JAM превращает блокчейн не просто в сеть, а в мощную платформу суперкомпьютера, способную запускать обычное программное обеспечение, такое как DOOM, одновременно предоставляя высокую вычислительную мощность и низкие задержки.

Оставив в сторону остальное, давайте сосредоточимся исключительно на виртуальных машинах. Основываясь на прочтении исходного кода PolkaVM за последние дни, я могу подтвердить, что это реальность: PolkaVM имеет Host, запустивший VM, предоставила экспортированные интерфейсы вверх, а внутри VM выполняется гостевой RISC-V код Doom.
Исходный код доступен здесь:
https://github.com/paritytech/polkavm/tree/master/examples/doom
Посмотрим на несколько компонентов этого ключевого нового решения для смарт-контрактов:
Revive Pallet
Revive Pallet[22] предоставляет среде выполнения (рантайму) возможность развертывать и исполнять смарт-контракты PolkaVM. Это модифицированный ответвление pallet_contracts. Эти контракты могут быть написаны на любом языке, компилируемом в RISC-V. На данный момент единственными официально поддерживаемыми языками являются Solidity (через revive[23]) и Rust (примеры Rust см. в директории fixtures).
На уровне языков контрактов обеспечивается совместимость с Ethereum: контракты можно писать на Solidity и взаимодействовать с узлом через Ethereum JSON RPC и кошельки вроде MetaMask. На низком уровне контракты перекомпилируются из YUL в RISC-V для запуска на PolkaVM вместо EVM. Для упрощения процесса можно использовать кастомную версию веб-фронтэнда REMIX[24] для компиляции контрактов в RISC-V и их развертывания в параллельной цепи Westend Asset Hub[25].
Revive
Revive[26] — общее название для проекта компилятора «Solidity to PolkaVM», включающего несколько компонентов (например, фронтэнд YUL и сам исполняемый файл resolc). resolc — это название бинарного исполняемого файла фронтэнда с одной точкой входа, который прозрачно использует все компоненты revive для генерации артефактов скомпилированных контрактов. Контракты Solidity компилируются в исполняемый код RISC-V для PolkaVM, что позволяет запускать контракты Solidity в Polkadot.
Для этого нужен компилятор. Принцип его работы заключается в использовании оригинального компилятора solc, после чего промежуточное представление (YUL) перекомпилируется в RISC-V. Фреймворк LLVM[27] используется как бэкенд компилятора, беря на себя основную работу по оптимизации и генерации кода RISC-V. revive отвечает за снижение сгенерированного solc промежуточного представления (IR) YUL до LLVM IR.

Этот подход обеспечивает хороший баланс между высоким уровнем совместимости с Ethereum, производительностью контрактов и разумными инженерными усилиями. По сравнению с реализацией полноценного компилятора Solidity, задача значительно меньше. Выбирая такой подход, мы можем поддерживать Solidity и все её странности и особенности из всех версий.
PolkaVM
PolkaVM[28] — это универсальная пользовательская виртуальная машина на базе RISC-V. Это наиболее очевидное изменение по сравнению с конкурентными технологиями. Вместо EVM для исполнения контрактов используется новая собственная виртуальная машина. В настоящее время в самом рантайме встроен интерпретатор PolkaVM. Последующие обновления обеспечат полноценный JIT PolkaVM, работающий внутри клиента.

• Безопасность по умолчанию и песочница. Код, выполняющийся в виртуальной машине, должен работать в отдельном процессе и не иметь доступа к системе хоста, даже в случае наличия атакующего с полными правами удаленного выполнения кода внутри самой виртуальной машины.
• Высокая скорость исполнения. Производительность времени выполнения кода, исполняемого в VM, должна сопоставляться с современными веб-ассамблейными VM и находиться хотя бы в том же порядке величин.
• Быстрая компиляция, гарантия сложности O(n) для однократной компиляции. Загрузка нового кода в виртуальную машину должна происходить практически мгновенно.
• Низкое потребление памяти. Базовые накладные расходы на память для каждого concurrent-экземпляра VM не должны превышать 128 КБ.
• Маленькие бинарные файлы. Программы, скомпилированные для этой VM, должны занимать максимально мало места.
• Не расходует адресное пространство впустую. VM не должна предварительно выделять гигабайты виртуального адресного пространства для целей песочницы.
• Полная детерминированность. При одинаковых входных данных и коде выполнение всегда должно возвращать абсолютно одинаковый вывод.
• Поддержка высокопроизводительного асинхронного измерения газа. Стоимость газа должна быть низкой, детерминированной и достаточно точной.
• Простота. Один программист может написать интерпретатор, полностью совместимый с данной VM, менее чем за неделю.
• Версионирование операционной семантики. Любые будущие изменения семантики, наблюдаемые клиентскими программами, будут версионироваться и явно приниматься.
• Стандартизация. Должна существовать спецификация, полно описывающая наблюдаемую клиентом операционную семантику этой VM.
• Кроссплатформенность. На неподдерживаемых ОС и платформах VM будет работать в режиме интерпретации.
• Минимум внешних зависимостей. VM должна быть преимущественно независимой, компилироваться быстро и противостоять атакам на цепочку поставок.
• Встроенные инструменты для отладки и анализа производительности.
Два фундаментальных отличия от EVM:
• Машина на регистрах. EVM — стековая машина. Это означает, что параметры функций передаются через бесконечный стек. PolkaVM основана на RISC-V, а значит, является машиной на регистрах. Это означает, что она передает параметры в ограниченном наборе регистров. Главное преимущество такого подхода заключается в том, что переходы на уровень аппаратного обеспечения осуществляются эффективнее. Количество регистров тщательно подобрано так, чтобы быть меньшим, чем у notorious deficient в регистрах набора команд x86-64. Возможность свести NP-hard задачу распределения регистров к простому отображению один-к-одному является секретом быстрого времени компиляции PolkaVM.
• Уменьшенная длина слова (word size). EVM использует 256-битную длину слова. Это означает, что каждая арифметическая операция должна выполняться над этими большими числами. Из-за этого любые осмысленные числовые операции чрезвычайно медленны, так как требуют преобразования во множество нативных инструкций. PolkaVM использует 64-битную длину слова, которая нативно поддерживается нижележащим оборудованием. То есть при трансформации контрактов Solidity через YUL (#Revive) по-прежнему будет использоваться 256-битная арифметика, поскольку YUL слишком низкого уровня для автоматического преобразования типов целых чисел. Однако контракты вполне можно писать на других языках и бесшовно вызывать их из Solidity. Представьте систему, где бизнес-логика написана на Solidity, но низкоуровневая архитектура — на более быстром языке, аналогично тому, как в Python большая часть тяжелой работы выполняется модулями на C.
Заключение
Языки программирования смарт-контрактов
Количество языков смарт-контрактов ежегодно растет. Выбор первого языка может быть сложным, особенно для новичков. Выбор в основном зависит от экосистемы, в которой вы заинтересованы, хотя некоторые языки подходят для不止 одной платформы. У каждого языка есть свои плюсы и минусы, данная статья не ставит целью их детальный разбор.
Возникнет вопрос: почему так много языков контрактов? Я выделил несколько возможных причин: 1) существующие языки не соответствуют уникальным особенностям конкретной цепи; 2) лучшая производительность, безопасность, стоимость; 3) вкусовые предпочтения :))
Я лично не имею предубеждений против языков смарт-контрактов. Ниже приведено несколько цитат из разных источников, которые могут дать различную полезную информацию как с точки зрения использования, так и с точки зрения дизайна языков или VM.
• Почему не использовать Solidity? Solidity — признанный пионер, но она ограничена многими историческими особенностями EVM. В ней отсутствуют распространенные функции, ожидаемые программистами, система типов относительно бедна на выразительность, а экосистема инструментов не унифицирована. В Sway мы даем вам целый набор современных инструментов для разработки смарт-контрактов. Вы получаете полнофункциональный язык с поддержкой дженериков, алгебраических типов данных и полиморфизма на основе черт (traits). Также вы получаете интегрированную, унифицированную и удобную в использовании инструментальную цепочку с LSP-сервером автодополнения кода, форматировщиком, генератором документации и всем необходимым для запуска и развертывания контрактов, чтобы вы могли легко реализовать нужную функциональность. Наша выразительная система типов позволяет выявлять семантические ошибки, мы предлагаем разумные поведения по умолчанию и проводим обширные проверки статического анализа (например, принудительное использование проверок, эффектов и взаимодействия). Вы можете использовать шаблон java.lang.Integer.pattern, чтобы гарантировать безопасный и корректный код еще на этапе компиляции.
via https://docs.fuel.network/docs/sway/
• Почему не использовать Rust? Хотя Rust отличный язык системного программирования (сам Sway написан на Rust), он не совсем подходит для разработки смарт-контрактов. Rust хорош благодаря абстракциям без накладных расходов и сложной модели памяти с проверяемой заимствованиями, что обеспечивает впечатляющую производительность во время выполнения для сложных программ без сборщика мусора. В блокчейне затраты на исполнение и развертывание являются scarce ресурсом. Низкое использование памяти и короткое время выполнения делают сложный менеджмент памяти часто слишком дорогим и нецелесообразным, а проверка заимствований Rust становится обузой без какой-либо пользы. Универсальные языки программирования обычно не подходят для таких сред, так как при их проектировании предполагается выполнение в общих вычислительных средах. Sway пытается предоставить разработчикам смарт-контрактов все остальные преимущества Rust, включая современную систему типов, методы безопасности и разумные поведения по умолчанию, предлагая при этом привычный синтаксис и функции, адаптированные под специфические требования блокчейн-среды.
via https://docs.fuel.network/docs/sway/
• Интерпретация и отправка кода Clarity происходят в точности так, как он написан. Solidity и другие языки компилируются в байткод перед отправкой в цепь. Опасность компиляции языков смарт-контрактов состоит в двух вещах: во-первых, компилятор добавляет слой сложности. Ошибка в компиляторе может привести к расхождению байткода с ожидаемым, что несет риск внедрения уязвимостей. Во-вторых, байткод нечитабелен для человека, что сильно затрудняет проверку того, что на самом деле делает смарт-контракт. Спросите себя: вы подпишете контракт, который не можете прочитать? Если ваш ответ отрицательный, то чем смарт-контракт вообще отличается? С Clarity вы видите то, что получаете.
via https://docs.stacks.co/concepts/clarity\#clarity-is-interpreted-not-compiled
Виртуальные машины и наборы инструкций
От хорошо известного нам JVM до эвристического восхождения EVM в области блокчейна, я знаю десятки виртуальных машин. Разные VM реализуются по-разному, и наиболее базовым способом исполнения обычно является интерпретатор (Interpreter), особенно за пределами Just-In-Time (JIT) или Ahead-Of-Time (AOT) компиляции. Интерпретатор широко используется для исполнения кода, отвечая за парсинг и выполнение байткода.
Пример интерпретатора
Ниже приведен небольшой пример, демонстрирующий базовый интерпретатор без учета механизмов оптимизации.
use std::collections::VecDeque; #[derive(Debug, Clone, Copy)] enum OpCode {Push(i32), Add, Sub, Mul, Div, Print} struct Interpreter { stack: Vec<i32>, } impl Interpreter { fn new() -> Self { Interpreter { stack: Vec::new() } } fn run(&mut self, bytecode: VecDeque<OpCode>) { for op in bytecode { match op { OpCode::Push(value) => self.stack.push(value), OpCode::Add => self.binary_op(|a, b| a + b), OpCode::Sub => self.binary_op(|a, b| a - b), OpCode::Mul => self.binary\_op(|a, b| a * b), OpCode::Div => self.binary\_op(|a, b| a / b), OpCode::Print => { if let Some(value) = self.stack.last() { println!("{}", value); } } } } }fn binary_op(&mut self, op: fn(i32, i32) -> i32) { if self.stack.len() < 2 { panic!("Stack underflow!"); } let b = self.stack.pop().unwrap(); let a = self.stack.pop().unwrap(); self.stack.push(op(a, b)); }} fn main() { let bytecode = VecDeque::from(vec!\[ OpCode::Push(10), OpCode::Push(20), OpCode::Add, OpCode::Push(5), OpCode::Mul, OpCode::Print, ]); let mut interpreter = Interpreter::new(); interpreter.run(bytecode); }
Каждая инструкция состоит из одного opcode (кода операции) и, возможно, параметров. Интерпретатор использует стек (stack) для хранения данных вычислений. Всего 6 инструкций.
Классификация наборов инструкций
Судя по примеру интерпретатора выше, количество инструкций и их масштабируемость определяют общее направление развития виртуальной машины. Мой анализ разделяет виртуальные машины по наборам инструкций примерно на следующие 4 категории:
1. Собственные наборы инструкций, представленные EVM, приводят к необходимости выполнять огромную повторяющуюся работу, такую как компиляторы, дизайн инструкций... До появления EIP-3855[29] уже существовало 141 инструкция, и их количество продолжает расти.
2. WASM, готовое к использованию сразу, поддерживает множество языков, но тяжеловесно. В документации Polkadot также отмечается этот аспект. Мне кажется, преимущество WASM — в нативной поддержке множества языков. Если какая-либо цепь хочет просто выполнить определенную логику и грубым методом записывает состояние в слот хранилища аккаунта, это подойдет. Быстрая разработка, быстрое подключение, но недостаток в том, что инструкции не разработаны специально для блокчейна, поэтому поддерживать специальные возможности или урезать их сложно (более 150+ инструкций[30]).
Здесь также стоит упомянуть SVM от Solana, который выполняет инструкции BPF. Можно динамически линковать библиотеки из любых языков программирования в инструкции BPF, хотя это чуть более нишевое решение по сравнению с WASM.
3. Наборы инструкций на базе RISC-V, такие как PolkaVM и многие современные zkVM: ceno, eigen zkvm, jolt, mozak vm, nexus, o1vm, openvm, powdrVM, risc0, sp1, sphinx и другие. Остальные я пока не успел изучить, но остановлюсь на известных мне PolkaVM и Nexus zkVM. Они реализуют общее объяснение инструкций RISC-V. В настоящее время большинство языков после компиляции через GCC или LLVM могут выводить инструкции RISC-V, что открывает перспективы на уровне языков и закладывает основу для поддержки множества языков в будущем. Базовый набор RV32I состоит из 47 инструкций с опциональными расширениями. По сравнению с x86 (более 3000 инструкций) это действительно минимизировано.
4. Интерпретируемые: например, Clarity (Lisp) от Stacks, Lua от AO, TypeScript от Mina. Итак, подводя итог, если слева у нас EVM, представляющая частные инструкции, а справа открытый WASM, то RISC-V является сбалансированным выбором для вторичной разработки. Во-первых, компиляторы уже поддерживают этот набор инструкций, и реализаций существует немало. Во-вторых, на его базе легко создавать кастомные инструкции.
Дополнительно: Исследование будущего многосетевых смарт-контрактов
В связи с разнообразным развитием экосистем блокчейна все больше проектов выпускают собственные языки смарт-контрактов (Solidity, Move, Cairo, Sway и т.д.) для поддержки уникальных функций. Хотя эта инновация обогащает технологический стек, она создает новые затраты на обучение и проблемы совместимости. Все более остро встает ключевой вопрос: можно ли бесшовно развернуть проверенные контракты Solidity на новых блокчейнах?
Для этого мы объединились с несколькими исследователями, увлеченными технологиями компиляции, и совместно запустили проект компилятора Hummanta. Этот проект направлен на устранение барьеров между цепями, обеспечивая междисциплинарную компиляцию языков смарт-контрактов, поддерживая трансляцию Solidity, Move, Cairo, Sway и других языков в различные среды виртуальных машин, включая EVM и新兴 VM: PolkaVM, SolanaVM, MoveVM, FuelVM и др.

Текущий прогресс проекта
Мы продвигаем эту визуализацию в трех направлениях:
1. Стандартизация синтаксиса и наборов инструкций: систематизация определений синтаксиса различных языков и наборов инструкций VM для создания фундамента междисциплинарной компиляции.
2. Разработка инструментальной цепочки компилятора: создание парсеров на базе LALRPOP[31], генерация кода с использованием Cranelift[32]/LLVM, поддержка преобразования множественных языков в целевой байткод.
3. Единый опыт разработки: выпуск инструмента Hummanta CLI[33] (аналог Cargo), обеспечивающего поддержку полного цикла от компиляции до развертывания.
Присоединяйтесь к официальному сообществу TechFlow
Telegram:https://t.me/TechFlowDaily
X (Twitter):https://x.com/TechFlowPost
X (Twitter) EN:https://x.com/BlockFlow_News













