November 22, 2021

Etleneum: смарт-контракты на Lightning Network

Опубликовано в блоге Коваленко Антона

Etleneum начинался как пародия на Ethereum. Попробую сделать небольшое введение в тему в нескольких абзацах; те, кто понимает, могут их пропустить, но я скорее был бы рад критике (если она будет убедительна, отредактирую).

В этом и остальных текстах серии для упрощения предполагается, что "ячейка" подписана публичным электронным ключом и открывается соответствующим ему приватным электронным ключом. Это верно почти для всех адресов и транзакций, но это частный случай более сложной конструкции. Для проверки, подходит ли "ключ" к "замку", каждый узел сети выполняет программу на специальном языке (Bitcoin script), результат которой и определяет, можно ли совершить такую трату или нельзя (и если нельзя, узел отвергает транзакцию и содержащий её блок, даже если в блок доказуемо вложена необходимая работа).

Bitcoin script похож на Форт, ещё немного на postscript (типизированностью данных в стеке) и немного на советский калькулятор МК-61. Естественное представление для него -- это набор байтов, обозначающих инструкции; есть способы записывать их человеко-читаемым образом, но эти способы довольно прозрачны (т.е. это такой "ассемблер", а не язык высокого уровня).

Для того чтобы проверить легальность траты какого-либо выхода, bitcoin-узел выполняет "программу-ключ", указанную в расходной транзакции, затем "программу-замок", соответствующую адресу, который сейчас тратится, и если программа-замок скажет, что ячейку можно отпирать, трата считается допустимой. Например, первая в мире не-coinbase транзакция (т.е. не начисление награды за майнинг, а передача от одного человека к другому) имеет такой scriptSig:

OP_PUSHBYTES_71 304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901

и тратит выход, scriptPubKey которого выглядит так:

OP_PUSHBYTES_65 0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3 OP_CHECKSIG 

Здесь происходит следующее: программа-ключ scriptSig кладёт в стек электронную подпись всей транзакции, программа-замок scriptPubKey докладывает поверх неё публичный ключ и запускает проверку OP_CHECKSIG, результат которой зависит от того, соотвествует ли электронная подпись публичному ключу.

Программы могут быть сложнеe. Зачем это бывает нужно? Самое старое и испытанное применение -- это адреса с мультиподписями, которые можно потратить, если трату подписали, скажем, оба совладельца разом, или два совладельца из трех, или (вот тут уже нужна мощь и гибкость) либо оба старших казначея, либо 3 из 5 членов совета директоров. Самое распространённое применение сейчас -- это каналы сети lightning network (каждому каналу соответствует выход транзакции, который может быть согласованно потрачен двумя владельцами, когда им перестал быть нужен канал, и -- вот тут как раз вступает в дело довольно сложная "программа-замок" -- если один из совладельцев исчезнет, другой может "разорвать" канал и забрать свою часть суммы, отправив остальное на адрес исчезнувшего компаньона). Есть и разные исторические курьёзы, например, адреса, которые может потратить любой человек, нашедший "дырку" в хэш-функции SHA1 (т.е. два разных прообраза одного хэша; награду забрали) или в хэш-функции SHA256 (награда дожидается героя).

Насколько сложнее могут быть программы? Разногласия в том числе по этому вопросу и привели к появлению Ethereum (который можно считать интересным экспериментом, можно жульничеством вокруг "театра децентрализации", а можно чем-то промежуточным). Одно из требований к bitcoin -- легкость проверки трат, так чтобы узлы сети никогда не упирались в вычислительную мощность. Да, для майнинга и получения награды вычислительная мощность всё равно нужна; но вот проверку всей "бухгалтерской книги" по мере появления новых блоков должно осиливать очень скромное устройство.

В bitcoin script нет циклов, функций, рекурсии и переходов назад, и не потому, что разработчики не догадались этого сделать; просто именно при таких условиях из небольшого размера программы прямо следует, что она завершится достаточно быстро. Убедившись, что узел сети разумной конфигурации выполняет программу, умещающуюся в блок, за разумное время, мы можем забыть о проблеме ресурсоёмких программ навсегда.

Центральное предложение проекта ethereum -- сделать язык для "замков и ключей" "полноценным", чтобы там было всё, что есть в других языках общего назначения. А как устроить, чтобы вычислительные ресурсы узлов сети не сожрали маленькие, но долгие программы? Очень просто: за сам процесс выполнения программ надо брать плату в пользу майнеров, добывающих блоки.

Тут можно заметить одно из важных культурных различий между биткойнерами и эфирщиками: для первых "полный" узел, который проверяет транзакции, но при этом не занимается майнингом, очень важный член сети; для вторых он настолько неважен, что можно проигнорировать маленькую проблемку: доплата за выполнение сложных программ достаётся только майнерам, а выполнять их и проверять результат должны все узлы.

Полноценные программы на блокчейне -- это очень соблазнительная идея, привлекающая developers, developers, developers (c), а также investors, investors, investors. "Тьюринг-полный язык" для обычного девелопера фактически означает "ты можешь сделать что угодно, на что хватит фантазии". И пока биткойнеры пилили lightning network на "неполноценном" bitcoin script (замысел около 2015, запуск 2018), эфирщики запускали токены на блокчейне, биржи на блокчейне, доменные имена на блокчейне, игрушечных котят на блокчейне!.. всё это с использованием "дружественного" языка программирования solidity, с циклами, функциями и прочим блэкджеком. Скрытая сложность системы, внешняя дружественность языка, инвестиционный бум и некоторые другие факторы в конце концов привели к событию, известному как "DAO hack": непредсказуемый способ вызова смарт-контракта (так принято называть нетривиальные программы на блокчейнах) позволил неизвестному хакеру увести несколько миллионов чужих долларов на свой адрес.

Вокруг bitcoin с самого начала идут споры о том, как именно обеспечена неизменность правил, которых придерживаются узлы сети. Что толку закладывать в протокол ограничение количества монет, если его можно пересмотреть и заложить новое? А если многие влиятельные люди согласны, что полученные тобой монеты достались тебе неправедно, не смогут ли они повлиять на разработчиков bitcoin, чтобы именно эту транзакцию как-то откатили обратно или хотя бы запретили тратить? Нет же никакой технической проблемы выпустить клиент bitcoin-0.23, в котором адрес вымогателя colonial pipeline принудительно считается пустым, а субсидия майнерам никогда не уменьшается? (Дело не в том, что мы очень любим вымогателей, или похитителей людей, или взломщиков бирж -- просто если можно откатить транзакцию в пользу вымогателя, вообще не стоило весь этот electronic cash затевать, потому что по той же процедуре можно откатить транзакцию в пользу гомофоба, еретика, торговца оружием, автора вредной книжки и т.д.)

Простого ответа на эти сомнения не существует: на каждую запятую можно написать пару страниц аргументов, почему всё хорошо, а к ним четыре страницы примечаний, почему всё не так просто. Всё это было бы не очень убедительно, если бы не подкреплялось историческими примерами, когда очень влиятельные люди очень хотели изменить какое-то правило, но не смогли этого сделать (влиятельные не только по мировым меркам, но и внутри сообщества; одно дело какой-нибудь жалкий Байден, другое дело заслуженные разработчики bitcoin плюс владельцы крупнейших бирж плюс известные "bitcion-евангелисты" плюс большинство майнеров...). Одному из этих примеров посвящена недавно переведённая книжка The Blocksize War. В том числе и оттуда можно понять, насколько для биткойнеров фундаментально право отказаться от апгрейда: если мой вчерашний софт не работает в одной сети с вашим сегодняшним, то "настоящий bitcoin" это именно тот, который обрабатывается вчерашним. Как это сочетается с апгрейдами протокола -- отдельная интересная история.

Реакция Ethereum Foundation (организация, отвечающая за разработку ethereum; её аналогом в биткойне будет... а, тьфу, нет аналога) на "the DAO hack" была следующей: решено было срочно выпустить реализацию полного узла ethereum, который будет считать транзакцию хакера неслучившейся, а средства на "дырявом" смарт-контракте переданными на "недырявый" более тупой смарт-контракт, который умеет только возвращать деньги инвесторам (идея не в откате цепочки старому блоку, а в нестандартном переписывании текущих балансов: как проводки "сторно" в настоящей бухгалтерской книге). По самой природе этого изменения право не апрейдится с ним несовместимо (это было не первое изменение, жестко требующее апгрейда от всех участников, но первое, связанное с откатом конкретной транзакции). При этом в EF полагали, что апгрейд будет принят всеми пользователями и майнерами единогласно (оказалось не так, и появилась монета Ethereum Classic), а среди наблюдателей-биткойнеров было популярно мнение, что в конце концов по стоимости, популярности и прочим метрикам победит неотредактированная цепочка (оказалось опять же не так).

Итак, если у нас есть платформа для смарт-контрактов, в которой "код -- это закон", но последнее слово всё равно остаётся за Виталиком, почему бы не сделать платформу, в которой последнее слово остаётся за @fiatjaf'ом? Но при этом забить на всю эту мороку с блокчейном, валидацией, майнерами, полными узлами, и не изображать, что "аварийного выключателя" не существует?

Нам нужен ввод, вывод и обработка балансов? У нас есть lightning и его родная денежная единица (сатоши, одна стомиллионная биткойна). Нужно исполнять пользовательский код? Прикрутим интерпретатор lua, позволим пользователям платно регистрировать lua-скрипты и их выполнять. Для каждого контракта будет храниться состояние, которое функции контракта могут обновлять и использовать, у каждого контракта будет баланс средств, находящихся в его распоряжении.

Etleneum, начинаясь как чистая пародия, заодно служил полигоном для семейства протоколов LNURL. Пользовательские логины? Добро пожаловать через lnurl-auth. Заплатить за вызов смарт-контракта с заданными параметрами? lnurl-pay. Вывести деньги на свой кошелек? lnurl-withdraw.

Со временем нарастали и контракты: тестовые, дурацкие, интересные, полезные. Последний пример представляет целое семейство "платных фич-реквестов" (то же самое для кошелька blixt); на этой основе было организовано финансирование перевода вышеупомянутой книжки The Blocksize War (замысел скромный, но многоэтапный и многолюдный, организовать все эти расчёты ручной пересылкой лайтнинга было бы намного труднее). Как мы видим, к некоторым контрактам есть удобные веб-морды на поддоменах etleneum.com; это не так чтобы большая привилегия, поскольку вызовы в любом случае доступны через api, и к своему контракту можно приделать веб-морду на своём домене (впрочем, можно и предложить её на официальный поддомен на https://github.com/fiatjaf/etleneum). Хотя etleneum не очень дружествен к новичкам, недавно новый заинтересовавшийся человек довольно быстро сделал на нём страницу для аукционов, через которую уже продали, в частности, несколько газет из Эль Сальвадора (тех самых, которые bitcoin day), и гиперинфляционную немецкую банкноту.

ToS проекта etleneum освобождает владельца от всякой ответственности (по нынешним временам мы знаем, что это признак серьёзного общественно-важного проекта, а не какой-нибудь любительской хрени). Круглосуточную работу сервера (и особенно стоящего за ним лайтнинг-узла) никто не гарантирует, и иногда внезапно не гарантирует.

Когда я слышу слова "платформа для смарт-контрактов", представляется молодой амбициозный стартап, который придумал свой блокчейн и (к гадалке не ходи) свою криптомонету, у которого два пути: в небытие или найти инвесторов, ещё найти инвесторов, реклама, ICO, листинг на биржах, запретить доступ американцам, рассчитаться с SEC, девелоперс, девелоперс, кризис, юзеры разбегаются, небытие.

Тем интереснее было наблюдать, как всё делается наоборот. Блокчейн не нужен. Своя монета не нужна. Инвесторы не нужны. Ничего в общем-то не нужно, только иногда хочется запилить что-то интересное.