Tapscript: новые коды операций, снижение лимитов и ковенанты
Liquid Network Blockstream Research

Tapscript: новые коды операций, снижение лимитов и ковенанты

Andrew Poelstra

Введение

Это третий и заключительный пост в нашем цикле публикаций об элементах усовершенствования Liquid в рамках запуска Taproot. В двух предыдущих постах мы рассказали о самом Taproot и частично подписанных транзакциях Elements (Partially Signed Elements Transactions). В этом посте мы расскажем о новых кодах операций, которые мы ввели в Elements Script, о том, для чего их можно использовать, и что мы с ними делаем.

Этот набор опкодов позволяет значимо усовершенствовать Script впервые с момента запуска Elements Alpha в 2015 году. Наша исследовательская группа также занимается инновациями в области прикладной криптографии, связанной с биткоином: мы выполнили большой объем работ над MuSig, блокчейном Elements и сетью Lightning Network, а также продолжили работу над скриптами и языком Simplicity. В частности, мы внесли значительный вклад в Taproot для биткоина, при чем Elements Script унаследовал некоторые из этих изменений, в том числе:

  • устранение фиксированных числовых ограничений на количество опкодов и размер скриптов;
  • замена подписей ECDSA на Schnorr;
  • замена CHECKMULTISIG на CHECKSIGADD, позволяющая выполнять пакетную проверку нескольких подписей;
  • многочисленные опкоды OP_SUCCESS, которые расширяют набор опкодов, позволяющих активировать софтфорк в сети в сравнении со старыми опкодами NOP.

Мы также расскажем о ковенантах, которые дают Script возможность ограничивать потоки платежей для создания «смарт-контрактов», привязывающих условия к монетам или другим активам. Ковенанты предлагаются для биткоина в разных формах уже много лет, но в Elements они существуют с самого начала. В Tapscript мы учли отзывы разработчиков о нашей первоначальной реализации ковенантов и представили новые опкоды, которые сделают ковенанты более выразительными, эффективными и эргономичными.

Наконец, мы вкратце опишем Elements Miniscript, нашем расширении языка Miniscript для биткоина, который использует эти опкоды операций для создания скриптов с поддержкой ковенантов. Этот язык находится в стадии быстрой разработки, и мы рады, что он позволяет запускать новые приложения в Liquid. Мы расскажем об этом подробнее в одном из следующих постов.


Ковенанты и активы

Ковенанты были первоначально предложены для биткоина Грегом Максвеллом в 2013 году, когда он в несколько шутливой форме предложил пользователям придумать для них примеры до смешного бесполезного применения. Хотя над этими плохими идеями весело подумать, они, к сожалению, по сей день затмевают идею ковенантов, несмотря на что, эти плохие идеи не имеют особого практического значения и не ориентированы на ковенанты. Отчасти в связи с этими рассуждениями о рисках, ковенанты остаются предметом жарких споров в биткоин-пространстве.

Самым последним и популярным предложением для ковенантов в биткоине является  OP_CTV, в котором намеренно урезана функциональность, чтобы избежать возможных разногласий.

На наш взгляд, эти опасения в значительной степени преувеличены. Они основаны на идее, что ковенанты могут быть использованы для «порчи» существующих биткоинов, например, требованием подписи третьей стороны для их использования, которое может распространиться на все предложение монет и которое будет невозможно удалить. Но такая схема действительно может быть реализована сегодня с использованием сценариев 2-из-2 CHECKMULTISIG, в которых одна сторона отказывается подписывать транзакции, не сохраняющие ограничение 2-из-2 «ковенанта». Этого никто не делает, и на то есть веские причины: очевидно, что эти монеты не будут приниматься пользователями вместо обычных монет и не могут быть приняты пользователями, имеющими фидуциарные или иные юридические обязательства по хранению обычных монет. Кроме того, полученные в результате монеты потребуют более высоких сетевых комиссий из-за их дополнительного веса и повысят сложность кошельков, в которых они могли бы быть потрачены.

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

Дополнительным доказательством того, что ковенанты безобидны является тот факт, что они существуют в сети Liquid Network с момента ее запуска в 2018 году, и не было ни одного вирусного ковенанта, распространившегося бы по всей системе. (Хотя, как мы увидим, существуют технические причины низкого уровня использования ковенантов в Liquid в целом.)


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

В Elements, в котором есть поддержка выпущенных активов, а функциональность, предоставляемая ковенантами, намного выше, мы можем:

  • Создавать бессрочные лимитные заявки или другие алгоритмические сделки.
  • Создавать финансовые деривативы, такие как опционы.
  • Создавать "контрольные токены", которые представляют собой NFT, обеспечивающие функциональность других контрактов.

В сценарии с несколькими активами и богатым набором арифметических опкодов у нас может быть такой же набор функций, как и в Ethereum и у конкурентов и, избегая полноты по Тьюрингу, мы сможем реализовать сильные формы анализа. Как мы сможем убедиться, здесь складывается более сложная ситуация.

Elements и Bitcoin Script

Сеть Elements была создана в 2015 году как форк Биткоина с дополнительными функциями и в том же году была запущена как тестнет под названием Elements Alpha. Эта сеть существовала еще до появления Segwit, не говоря уже о Taproot; она предшествовала выпущенным активам; в ней была двухэтапная схема привязки, которая в итоге оказалась излишне сложной и подверженной багам, и она была переработана перед развертыванием на Liquid. Ее основными функциями стали были функция Confidential Transactions, которая позднее была расширена для поддержки выпущенных активов, зарождающейся схемы сегрегации свидетелей, которая была упрощена и перенесена в биткоин как Segwit, а также набор дополнительных опкодов для включения ковенантов. На этой последней функции мы и сосредоточимся в этом посте.

Когда мы представили оригинальный набор кодов операций для Elements и Liquid, Bitcoin Script работал на основе низкоуровневого программирования, и его было сложно по-настоящему проанализировать. Наши расширения повысили выразительность языка, но не устранили эти основополагающие ограничения. Пользователи смогли разработать несколько продвинутых приложений, таких как Lending on Liquid, но эти решения скорее свидетельствовали о человеческой изобретательности, чем о простоте программирования на нашей платформе для скриптов. Другие приложения, такие как Bitmatrix, можно было развернуть только после обновления системы скриптов, описанного в этом посте. С тех пор, отдельно от Elements, специалисты исследовательского отдела Blockstream разработали Miniscript, новый способ моделирования Bitcoin Script, который позволил решить эти проблемы анализа.


В частности, с помощью Miniscript можно автоматически перечислить все ключи в скрипте и определить, какие наборы необходимы для осуществления транзакции; определить верхнюю границу размера такой транзакции до сбора подписей; ответить на семантические вопросы о том, какие условия должны быть выполнены, чтобы потратить монеты; а также вычислить точные кодировки для скриптов и свидетелей. Miniscript дополняет формат частично подписанных биткоин-транзакций (Partially Signed Bitcoin Transactions), также разработанный Эндрю Чоу из исследовательского отдела Blockstream, совместно с Питером Уилле, который участвовал в разработке первоначальной концепции и решения, предоставляюшего протокол для сбора данных, необходимых для создания транзакции. Это позволяет кошелькам подписывать транзакции, даже если они не понимают конкретный скрипт (или были созданы до его написания); кошелькам просто нужно убедиться, что транзакция имеет смысл, создать подпись ECDSA или Schnorr, а инструменты Miniscript сделают все остальное.

Однако, даже имея за плечами опыт работы над Miniscript, мы не нашли прямого пути к «легким ковенантам» для Elements. Наши схемы ковенантов, созданные в 2016 году, было сложно использовать по общим принципам, а их большой размер на практике не позволяет избежать ограничения в 201 опкод, унаследованного от Биткоина. Таким образом, даже когда Bitcoin Script стал более мощным благодаря Miniscript, PSBT и Tapscript (разработанному для Miniscript), в него не вошли расширения Elements для Script.

Прорыв: новые абстракции

В феврале 2021 года специалисты исследовательского отдела Blockstream Эндрю Поелстра и Санкет Канджалкар наконец нашли способ объединить ковенанты Elements с Miniscript. По сути, мы будем создавать скрипты с поддержкой ковенантов внутри "машины" – шаблона Script фиксированного размера, который будет использовать опкод CHECKSIGFROMSTACK (CSFS) для извлечения всех данных из соответствующих транзакций и оставлять их в стеке в фиксированных местах, где у «реального кода» будет к нему эффективный доступ. Это позволит нам амортизировать высокую стоимость ковенантов в стиле CSFS по многим условиям транзакции.

На самом деле мы затем ушли от этого первоначального подхода, но этот шаг дал нам возможность обойти концептуальные ограничения по практическому созданию универсальных ковенантов, и позволил увидеть реальные ограничения Elements Script:

  • 31-битная арифметика с прямым (big-endian) порядком байтов, унаследованная от Биткоина, которую невозможно было использовать с 64-битными суммами с обратным (little-endian) порядком, применяемыми в транзакциях.
  • В дополнение к этому отсутствуют опкоды умножения и деления; в Elements были добавлены битовые сдвиги и другие арифметические операции, но не базовые элементы, необходимые, например, для расчета комиссий.
  • Для вычисления комиссионных, ковенанты в стиле CSFS не имеют доступа к весу транзакции и, следовательно, не способны вычислить ее комиссию.
  • Невозможность хешировать более 520 байтов данных вызвана ограничением размера элемента стека Биткоина в 520 байт в комбинации с его негибким опкодом SHA256. В сочетании с необходимостью CSFS моментально хешировать значительные объемы данных транзакций, это ограничивало общий размер транзакций, значительно усложняя при этом задачу создателям кошельков.
  • Невозможность выполнения скриптов с более чем 201 опкодами, десятки из которых требовались машине CSFS, а еще десятки – для преобразования 64-битных целых чисел в 32-битные и обратно.
  • Невозможность выполнять арифметику эллиптических кривых, за исключением проверки подписи, которую можно заставить делать многое, но не проверять правильность формирования выходных данных Taproot. Если бы мы сохранили это ограничение в Tapscript, оно бы снизило функциональность и увеличило сложность наших скриптов.

Как только мы осознали эти проблемы, их стало легко решать:

  • Мы добавили новые опкоды для работы с 64-битными значениями, включая умножение, а также опкоды для преобразования их в старые 32-битные значения для использования с обычными опкодами Биткоина.
  • Мы добавили опкоды «прямой интроспекции» для доступа к данным транзакций, устранив необходимость в «машине» и уменьшив количество опкодов. Мы также добавили опкод TXWEIGHT для доступа к весу транзакции для расчета комиссии.
  • Мы добавили опкоды «потокового хеширования», чтобы обеспечить подачу данных в хеш-движок без необходимости помещать их в один элемент стека. Это позволяет полностью избежать ограничения на элемент стека в 520 байт, не требуя дополнительных ресурсов от интерпретатора скриптов.
  • Мы добавили опкод ECMULSCALARVERIFY для обработки выходов Taproot, обязательств по оплате по контракту и других криптографических операций на базе эллиптических кривых.
  • Мы последовали примеру Биткоина и устранили ограничение в 201 опкод, хотя благодаря вышеупомянутым усовершенствованиям, возможно, оно бы, возможно, перестало быть проблемой.

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

Полный список новых опкодов приведен здесь.

Преодолев этот барьер, мы теперь готовы заняться более глубокими проблемами ковенантов, присущими не только Elements.

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

Чтобы создать этот ковенант, нам должны соблюдать следующие условия (описанные в нотации Miniscript, которую мы подробно рассмотрим в одном из следующих постов).

  • Частичный вывод (partial_withdraw): если в ковенанте больше, чем MAX_WITHDRAW сатоши, то в ковенанте должно оставаться как минимум total_value - MAX_WITHDRAW.
  • "num64_gt(curr_inp_v,MAX_WITHDRAW)": общее количество входных сатоши больше, чем MAX_SATS
  • "asset_eq(curr_inp_asset,out_asset(0))": входные активы и выходной актив 0 одинаковы
  • "num64_geq(out_v(0),sub64(curr_inp_v,MAX_WITHDRAW))": значение выхода должно быть не меньше total_value - MAX_WITHDRAW
  • "spk_eq(curr_inp_spk,out_spk(0))": остальные монеты отправляются в  ковенант
  • Полный вывод (full_withdraw): если в ковенанте меньше MAX_WITHDRAW сатоши, то выходы не имеют ограничений
  • num64_leq(curr_inp_v,MAX_WITHDRAW)
  • Контроль ключей (keys): подождите 60 блоков, прежде чем тратить снова, и потребуйте ключ K для возможности осуществления траты.
  • pk(K): требуется подпись от ключа K
  • older(60): время ожидания 60 блоков до следующего вывода
  • curr_idx_eq(0): индекс текущей траты должен быть равен 0. Это позволяет избежать так называемую проблему половинной траты, когда мы можем потратить два ковенантных utxo вместе, и один из них может выйти из ковенанта.
  • Наконец, мы объединяем все вышеперечисленные условия: and(keys,or(full_withdraw,partial_withdraw))

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

Еще не решенные проблемы и дальнейшая работа

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

Например, можно "скомпоновать" два минискрипта, создав узел and_b, двумя дочерними элементами которого являются два исходных скрипта. (С точки зрения Script мы просто объединяем исходные сценарии и в конце добавляем опкод BOOLAND). Если пользователь убежден, что может удовлетворить каждый из исходных скриптов по отдельности, он может быть уверен, что сможет выполнить и объединенный скрипт.

И наоборот, если необходимо определить выполнимость сложного скрипта, пользователь может рекурсивно пройтись по всему скрипту: всякий раз, когда он видит узел and, он должен удовлетворять оба подскрипта, а когда видит узел or – удовлетворять один из них.

В случае с ковенантами такая форма комбинирования скриптов больше не действует. Хотя по-прежнему можно объединить два скрипта при помощи and_b, чтобы получить синтаксически допустимый скрипт, результат может быть неудовлетворительным, даже если оба подскрипта могут быть удовлетворены. Например, представим себе ситуацию объединения скрипта, требующего сожжения на первом выходе транзакции актива А, со скриптом, требующим, чтобы на первом выходе транзакции был сожжен актив В. Очевидно, что эти требования невозможно выполнить одновременно.

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

С ковенантами, изображенными на картинке, модель с точки зрения монотонных функций просто неприменима, и поэтому мы теряем общие и мощные инструменты анализа Miniscript. Вместо этого программы Elements Miniscript, поддерживающие ковенанты, необходимо анализировать ситуативно, задавая и отвечая на конкретные вопросы.

В языке Simplicity, полностью заменяющем Script и над которым мы работаем параллельно, тоже есть это ограничение. Чтобы сделать этот ситуативный анализ приемлемым, интерпретатор Simplicity имеет эталонную реализацию в помощнике по доказательству теорем Coq, что означает, что хотя человеку нужно придумать правильные вопросы, по крайней мере ответы будут надежными и поддающимися машинной проверке – а Elements Miniscript делают программы полностью зависимыми от проверки кода человеком. Мы надеемся, что разработали язык таким образом, чтобы мощные программы могли тем не менее быть простыми и очевидно правильными.

В будущем мы планируем продолжать расширять функциональность Elements Miniscript, писать вспомогательные инструменты и библиотеки кошельков, способные использовать Elements Miniscript для создания ковенантов и взаимодействия с ними, а также продолжать развивать язык Simplicity, который будет еще более выразительным, чем недавно расширенный Elements Script.

Хорошо вам попрограмить!

If you have specific preferences, please, mark the topic(s) you would like to read: