October 26, 2022

Введение в Miniscript

Базовый уровень

Лауреат премии Тьюринга Fernando J.Corbato, разработчик первых операционных систем одновременного доступа написал в своей работе "О строительстве систем, которые ждёт сбой":

использование ограниченных языков для дизайна или синтеза — это мощная методология. Не допуская, чтобы программист или архитектор выражали не релевантные идеи, домен возможных ошибок становится куда более ограниченным

Эта идея ведёт прямиком к принципам, лежащим в основе протокола Bitcoin и технологий его масштабирования, например Lightning Network:

  • Внутренний скриптовый язык Bitcoin Script cущественно экономит ресурсы (размер и скорость) и является более безопасным.
  • LN, находясь вне консенсуса может изменять свои принципы работы хоть 10 раз в день, что соответствует модульному принципу проектирования систем и "ремонтопригодности" отдельных компонентов.

Биткоин Скрипт всегда позволял сделать монеты пригодными для расходования с помощью более сложных политик, чем просто один ключ. Хотя Bitcoin Script в основном используется для платежей с одним ключом, он также является основой для различных кошельков мультиподписью и контрактов атомарного свопа и позволяет работать сети Lightning.

Script можно использовать для представления сложных условий, необходимых для выпуска транзакции - например, (два из A, B, C) и (D или (E и F)), где A-F представляют собой уникальные ключи - а также для проверки хэшей, таймлоков и некоторых других экзотических конструкций. Биткоин Скрипт всегда было затруднительно использовать для разработки в той же степени, в которой было бы опасно создавать реальные приложения на нём.

В этой заметке мы уделим внимание расширению Bitcoin Script — Miniscript. Это не единственная технология протокола Биткоин, которая направлена на усиление его возможностей поддержки "смарт-контрактов" в том смысле, в котором это понимается в Ethereum. В декабре 2017 уже предпринимались попытки создавать подобные расширения, вроде Ivy Playground — тренировочной, учебной платформы для компиляции Smart Contract во внутренний Bitcoin Script, который является производным стековым языком программирования, созданным на базе устаревшего языка Forth. Об этой платформе уже довольно давно нет никаких новостей, последние коммиты датируются 2020 годом.

Пример кода смарт-контракта Ivy

Детальный взгляд на Bitcoin Script

Bitcoin Script имеет большое количество возможностей, из которых реально задействованы лишь немногие. Хорошо известны кошельки с мультиподписью, или контракты HTLC (Hash Timelock Contracts), которые являются рабочими для Lightning Network или atomic swap. В этой статье можно прочитать про таймлок-опкоды, которые могут играть роль в продвинутых методах хранения биткоинов, в этой про предложенный новый опкод OP_CHECKTEMPLATEVERIFY, статья заодно иллюстрирует чем отличаются "смарт-контракты" Биткоина от "смарт-контрактов" Ethereum. Описанные контракты составляют лишь малую часть из поддерживаемых Bitcoin Script.

Иллюстрация работы PubKey Script -- скрипта "разблокировки" биткоинов -- с состоянием стека Биткоин Скрипта внизу. Цветные прямоугольники отражают элементы стека. Операции выполняются с первым элементом с конца (так работает любой "стек"). Источник https://medium.com/@blairlmarshall/how-does-a-bitcoin-transaction-actually-work-1c44818c3996

Редко используются OP коды типа 2DUP или SWAP. Джимми Сонг (автор Programming Bitcoin) приводит две причины для этого. Первая, многие ОР коды не очень полезны, что означает, что они допускают конструирование "смарт-контрактов" которые не ценятся рынком. Вторая, даже если ОР код полезен, он может не иметь своего приложения. Мы можем привести также пример из области TBTC -- моста в Ethereum со стороны Биткоина -- в протоколе которого была обнаружена уязвимость, основанная как раз на малоизвестных разработчикам TBTC оп-кодах. При умелом дизайне исходящей транзакции с кошелька-депозита TBTC с использованием OP_VERIF/OP_VERNOTIF, могла бы быть сконструирована такая транзакция, которая бы по факту оставляла Биткоины у подписантов кошелька депозита (т.е. у одного лица), но могла бы разблокировать страховой депозит в Ethereum.

Непопулярность программирования на Bitcoin Script во многом происходит из-за того, что это крайне неудобный для разработки язык. Очень трудно комбинировать и проверять смарт-контракт, собранный из отдельных оп-кодов, поскольку сам по себе скрипт будет связан с состоянием стэка памяти, в котором он выполняется. Полезность оп-кодов во многом ограничена возможностями разработки безопасного и проверяемого кода. Именно поэтому был предложен и разработан Miniscript. Miniscript — это подмножество Script, которое делает лёгким разработку и проверку логики смарт-контракта. Как только кошельки смогут поддерживать Miniscript, вместо Script, проблема разработки нестандартных смарт-контрактов перестанет существовать.

Продвинутый Минискрипт

Самый значительный факт о Минискрипте состоит в том, что он является упрощённым интерфейсом к Биткоин Скрипту в случае Segregated Witness транзакций, и не поддерживает какие-либо операции с Legacy скриптами или не может имплементировать такие скрипты. Это не язык программирования, построенный поверх "ассемблера" блокчейна Биткоин, как Simplicity, а скорее "обёртка" подобная библиотекам Python, упрощающим доступ к основному программному обеспечению, написанному на С++ или Fortran.

Поелстра заметил в IRC:

"если у вас есть высокоструктурированные шаблоны скриптов, то можно обойтись и без понимания скриптов".

Эта идея стала основным мотивом разработки Miniscript и его приложений. Однако сам по себе язык политик трат восходит к дескрипторам кошельков и формату частично подписанных транзакций PSBT — понадобился общий способ создания шаблонов транзакций, который был бы достаточно общим, чтобы избежать частных реализаций в PSBT.

"Что если мы расширим дескрипторы, добавив в них MULTISIG, AND и OR, таймлоки и хэш-секреты?" (Вьюлле)

Эта работа еще не закончена. Сейчас есть две функциональные реализации Miniscript (C++ и Rust) и компилятора политик, но чтобы сделать эту технологию доступной, потребуется интеграция в широко используемые части программного обеспечения.

Самое интересное, что Питер знает C++, но не Rust, а Санкет знает Rust, но не C++, поэтому они фактически не могут скопировать код друг у друга. Это два совершенно независимых компилятора, которые, тем не менее, совпадают на выходе (Поэльстра в Miniscript Workshop).

С совместимой с Miniscript реализацией PSBT (апдейтер и финализатор) многие подписывающие устройства PSBT (включая аппаратные кошельки) могли бы стать пригодными для сложных сценариев, даже без явной поддержки. Компиляторы также могут быть улучшены, поскольку существует множество оптимизаций в преобразованиях политики в скрипты, которые еще не учтены. Недостком внедрения является существенный пробел в раскрытии подробностей устройства Miniscript. Существует некоторое количество статей которые рассказывают о новом языке политик на высоком уровне, есть сайт Питера Вьюлле, в котором знания хорошо структурированы, но кажутся мало пригодными для постепенного погружения.

Также общая презентация Эндрю о минискрипте, текст есть тут https://www.bitcoinhalving.com/talks/explaining-miniscripts-with-andrew-poelstra

Поэтому мы постараемся заполнить промежуток между разработанным исходным кодом примеров и крайне высокоуровневыми статьями, которые почти не имеют общих мест со строчками примеров.

Оригинальный Биткоин Скрипт выглядит так:

<key_user> OP_CHECKSIGVERIFY <key_service> OP_CHECKSIG OP_IFDUP OP_NOTIF
  <a032> OP_CHECKSEQUENCEVERIFY
OP_END

Эквивалент Минискрипта:

and_v(v:pk(A),or_d(pk(B),older(12960)))

Интуитивно понятными операторами в этом примере являются or, pk или older, которые обозначают "или", "публичный ключ" или условие таймлока nSequence в блоках, которое обозначает разницу в блоках у нового выхода траты по сравнению с входом, в котором присутствует nSequence. В данном примере or_d является выражением Минискрипта типа "B", "базовым" и по правилам языка шаблонов любая политика должна начинаться с такого выражения. Минискрипт B - это сценарий, который, если удовлетворяется, принимает входные данные, выгружает их из стека и оставляет в стеке 1. Если он не удовлетворяется, скрипт оставит в стеке 0 или, возможно, прервет выполнение сценария, но не сделает ничего другого. Он не позволит сценарию продолжить выполнение, не поместив ничего в стек. Для or определены выражения других типов, которые могут занимать другие места в политике траты. Три остальных типа это "V" — выражения верификации, "K" — выражения ключей, "W" — выражения-обёртки.

Поэльстра объяснил наличие типа "K" необходимостью того, что например ключи требуются во многих местах Минискрипта и с ними выполняются всяческие манипуляции, например при проверке подписи CHECKSIG или при определении ключей мультиподписи MULTISIG, а также в переключателе or или условии if. Поэльстра приводит пример, условный оператор выбора из двух ключей может быть реализован как:

IF PUBKEY CHECKSIG ELSE OTHER_PUBKEY CHECKSIG ENDIF

Это громоздкая конструкция, которая расходует дополнительный байт, поэтому разумнее иметь вместо неё альтернативный сценарий:

IF PUBKEY ELSE OTHER_PUBKEY ENDIF

И CHECKSIG для проверки подписи снаружи выражения. Оператор выбота в данном примере становится or_i (появляется индекс i, для различения) и он оборачивает политику типа IF X ELSE Z которая по внутренним правилам сравнивает выражения типа K — ключи — и её результатом также является выражение типа K, которое попадёт на вход проверки подписи CHECKSIG.

Следующая не интуитивная деталь в политике Минискрипта это обёртки, типа c:pk(A). Такие обёртки призваны гарантировать, что конкретная политика имеет единственную интерпретацию, т.е. является "non-malleable". Они являются важными с точки зрения конструкции финального Bitcoin Script и его верификации, которая обеспечивается на высоком уровне Miniscript'ом. Поэльстра привёл любопытный факт, что Питер Вьюлле сгенерировал 100 миллиардов скриптов для сверки двух независимых имплементаций Miniscript.

Наконец, рассмотрим вывод одного из примеров, предоставленных Поэльстрой:

Descriptor: wsh(c:pk())
Miniscript: c:pk()
Policy: pkh()

Он построчно иллюстрирует работу Lifting — особой функции библиотеки, которая "поднимает" Минискрипт строку до уровня политики траты. Верхняя строчка это дескриптор кошелька. Дескрипторы это особый класс объектов, который позволяет дистичь универсальности при формировании транзакций в Биткоине: например если она "приготавливается" несколькими кошельками мультисига, они получают строку дескриптора и "знают" по каким правилам необходимо сформировать подпись. Дескриптор прямо преобразуется в Bitcoin Script. На уровень выше находится Минискрипт, и наконец, совсем высокоуровневое описание — это политика траты.

Материалы по Минискрипт обнаруживают связь с целым рядом других понятий и объектов, которые могут быть не очень хорошо знакомы широкой публике: дескрипторы кошельков, формат частично подписанных транзакций (PSBT) и Taproot. В одной статье невозможно уместить всё сразу, поэтому эти темы возможно будут присутствовать в других статьях. Пока одним из наиболее перспективных видов приложения Минискрипта является Taproot, Tapscript и Schnorr, поскольку они сильно увеличивают возможности конструирования компактных вариантов трат в одной транзакции Биткоина.

Ссылки

Воркшопы:

Прочее:

Поддержите канал!

LNURL1DP68GURN8GHJ7MRWW3UXYMM59E3K7MF0D3H82UNV9ACXZ7FLW4EK2UNWV9KK20TWDA6XWETVVSMHV8MV

Или с помощью Lightning Address [email protected]