Миру нужна сеть Lightning Network
Содержание
Давайте пообещаем: я постараюсь объяснить, что такое Lightning Network, не вдаваясь в (слишком) большие подробности, но вам придется уделить мне час своего времени. Это будет долгий и сложный путь (но есть и радость).
Если вы хотите поддержать мою работу, вы можете посетить страницу donations. Каждый взнос, будь он большим или маленьким, помогает мне тратить больше времени на написание, пересмотр и обновление этих статей. Спасибо за поддержку!
Приорат благосклонен
Мы всегда начинаем с основ. #
Lightning Network (как и Bitcoin) - это недоверенная система, она позволяет обмениваться ценностями без необходимости доверять другим участникам этой сети.
Но как выглядит эта система? Где она расположена? Как она работает?
Предположим, я - менеджер пиццерии. Каждый день мой друг Крис приходит купить пиццу. Крис любит расплачиваться биткоинами, но использовать таймчейн для быстрых трат утомительно. Он недостаточно масштабируется, поэтому платежи проходят не быстро, и за них приходится платить.
Мы с Крисом договариваемся и создаем воображаемый канал связи, в котором оба кладем немного денег (сатоши) в сейф. В сейфе лежит листок бумаги, на котором написано, сколько именно сатоши мы с Крисом положили на депозит. Это позволяет ему заказывать столько пиццы, сколько он захочет, пока его доля сатоши не будет полностью уничтожена.
Каждый раз, когда Крис покупает пиццу, лист обновляется, отнимая у него некоторое количество сатоши и добавляя их на мое имя. Теперь, когда мы с Крисом установили канал связи, любой друг Криса, у которого есть прямой канал связи с ним, может воспользоваться нашей новой связью.
Таким образом, цель Lightning - обеспечить эффективную “маршрутизацию” платежей, используя существующие каналы и находя минимальный путь для их завершения.
Маршрутизация - это процесс поиска наилучшего маршрута в любой транспортной системе (будь то физическая или цифровая).
Приближенно к реальности: когда вы используете карты на своем смартфоне для поиска оптимального маршрута, вы фактически прокладываете маршрут.
Пример с пиццей очень тривиален и ни в коем случае не является полным или реалистичным в отношении того, как на самом деле работает Lightning, но это хороший начальный пример для того, чтобы вникнуть в тему.
Протокол справедливости #
Протокол справедливости - это, по определению, система, в которой существует справедливость между участниками.
Но как можно стремиться к справедливости, если не требуя доверия, навязывая, например, государство закона или предполагая наличие доверенных сторон?
Глупый вопрос: Я написал об этом целую статью: спасибо теории игр и криптографии!
В криптографических системах мы доверяем протоколу. Протокол - это не что иное, как система с набором правил, и благодаря стимулам или антистимулам в теории игр мы можем стремиться к достоверному протоколу без необходимости в посредниках. Но справедливость достигается только в том случае, если правила хорошо прописаны.
Давайте для наглядности рассмотрим пример и перенесем это определение в реальный мир.
Во время обеда два младших брата ссорятся, потому что не хотят делить тарелку с чипсами. Классика. Мать, взяв дело в свои руки, авторитетно навязывает свой закон, разделяя тарелки обоих младших братьев по своему усмотрению. Она может вольно или невольно отдать одному из них больше картошки. Результат? Система несправедлива, у нас есть форма государства закона, которое принимает решение совершенно автономно.
Более разумным методом может быть установление правил, например: один из двух младших братьев делит тарелки, но другой может выбирать, какую тарелку съесть первой.
К каким последствиям приведет эта новая динамика между ними? Мы ввели стимулы и нестимулы, потому что если младший брат, отвечающий за разделение тарелок, попытается схитрить, его брат может наказать его, выбрав большую тарелку.
Примитивы безопасности #
Чтобы пример с двумя младшими братьями работал, он должен опираться на некоторые необходимые примитивы, такие как последовательное упорядочивание
действий и намеренное неотрицание
.
- Выбор блюда одним из двоих не может происходить до порционирования.
- Оба должны взять на себя обязательство не отрекаться от выбора блюда.
Почему молния? #
Как уже говорилось в примере с пиццерией, биткойн плохо масштабируется. Но это не проблема, а функция. Биткойну на первом уровне не нужно масштабироваться и не придется.
Помните, что транзакции записываются в таймчейн глобально, и по мере роста спроса на транзакции один блок очень быстро насыщается. Тем временем все остальные откладываются в очередь. Чтобы обойти очередь и избавиться от ожидания, можно заплатить более высокую плату, поэтому плата растет для всех без исключения.
Если спрос на транзакции продолжает расти, все больше и больше транзакций откладываются, делая микротранзакции нерентабельными, потому что на сборы будет потрачено больше сатоши, чем реальная сумма транзакции.
Чтобы решить эту “проблему”, которая таковой не является, некий “гений” решил, что лучше всего увеличить размер блока, но… мы уже говорили об этом в Bitcoin 101. Пробегитесь и перечитайте война за размер блока.
Как мы можем обеспечить масштабируемые, внецепочечные транзакции без потери безопасности первого уровня Биткойна?
В феврале 2015 года Joseph Poon и Thaddeus Dryja представили возможное решение этого вопроса, опубликовав уже устаревший документ The Bitcoin Lightning Network: Scalable Off-Chain Instant Payments.
Концепция Lightning Network предусматривает появление новой сети, технически называемой уровнем 2, в рамках которой пользователи смогут осуществлять платежи в режиме peer-to-peer без необходимости регистрировать транзакцию в таймчейне. Таймчейн используется только для открытия канала связи и урегулирования этого канала, то есть соглашения о выводе биткоинов из Lightning и возвращении их на уровень 1.
В дополнение к естественному снижению нагрузки на узлы биткоина, при использовании платежей Lightning комиссии за транзакции будут очень низкими и с большей конфиденциальностью (частично, мы увидим), поскольку эти платежи не видны всему уровню 1, как стандартные транзакции.
Функции, выделенные в whitepaper. #
Я предполагаю, что мы рассмотрим каждый пункт в следующих нескольких главах, а пока это может показаться расплывчатым списком, но мне необходимо представить концепции, а затем описать их.
- Маршрутизация платежей по очень низкой цене и в режиме реального времени.
- Обмен ценностями без ожидания подтверждения канонического блока, как на L1.
- Платежи являются окончательными, необратимыми и подлежат возврату только получателем.
- Платежи не видны всей сети.
- Платежи не хранятся постоянно, как в L1.
- Lightning использует концепцию
онионной маршрутизации
, так что промежуточные узлы, участвующие в транспортировке платежа, знают только предшествующий узел и следующий. Они не знают отправителя и получателя. - Биткоины, используемые в Lightning, являются настоящими биткоинами, эта особенность обеспечивает хранение стоимости и полный контроль баланса, как в биткоинах на цепочке.
Как работает Lightning #
С этого момента мы прекращаем играть: если вы устали, идите сделайте кофе, потому что вам предстоит познакомиться с чудесами сети Lightning.
Основные технические предпосылки #
- Цифровые подписи: это метод, предназначенный для подтверждения подлинности цифрового документа.
Поясним на примере: вам нужно тайно написать письмо своему близкому другу, и вы хотите быть уверены, что он знает, что вы действительно являетесь автором этого письма. У вас есть магический ключ, который позволяет вам подписать письмо математическим способом, доказав, что только вы могли его написать. Используя этот магический ключ, вы создаете цифровую подпись.
У вашего друга есть другой магический ключ, полученный от вашего, но он может использовать его только для проверки вашей подписи. Он не может создать подпись от вашего имени.
Это и есть цифровая подпись.
- Цифровой ключ: состоит из набора цифр, которые могут быть использованы как для шифрования, так и для расшифровки информации.
Поясним на примере: у вас есть специальный уникальный ключ, которым можно открыть только определенный сейф. Этот цифровой ключ очень мощный, потому что с его помощью можно подписывать документы или получать доступ к защищенным ресурсам. Он должен быть только у вас, и, когда вы им пользуетесь, все остальные знают, что вы действительно тот, за кого себя выдаете.
Он состоит из двух частей, публичной и частной. Публичная позволяет подтвердить, что вы - это вы, но в ней ничего не говорится о том, как ее использовать, и она не работает, чтобы открыть сейф. С другой стороны, приватный - это секрет, который вы должны хранить.
- Хэш**: это математическая функция, которая преобразует данные переменного размера в строку фиксированной длины.
Поясним на примере: представьте, что вам нужно преобразовать кулинарный рецепт, содержащий очень точные дозы, в уникальное число. Существует математическая функция, которая превращает *‘что-то’’ в число, присваивая ему отпечаток.
Если изменить даже один грамм ингредиента в рецепте и повторно применить хэш-функцию, конечный результат кардинально изменится - число, которое вы получите, будет совершенно другим. Если бы кто-то изменил ваш рецепт, вы бы сразу поняли, что он уже не тот, что раньше. Подводя итог, можно сказать, что это способ обеспечить целостность и безопасность в цифровом мире.
Для более прагматичных людей хэш можно вычислить из терминала следующим образом
$ echo -n "musclesatz" | shasum -a 256
Это даст результат
fb4584b61ffaee257347cbff270e3c0bc9c504317685b68a72c1a47c400984f3
- Биткойн-транзакция: это структура данных, которая кодирует передачу стоимости между различными участниками сети.
Транзакция, которая расходует вход, создает выход. Входы транзакций - это ссылки на выходы предыдущих транзакций, а каждая транзакция, в свою очередь, генерирует новые выходы.
Биткойн-узлы отслеживают все эти доступные и расходуемые выходы. Именно поэтому они называются неизрасходованными транзакционными выходами
или UTXO
для краткости. Совокупность всех UTXO составляет набор UTXO
, и этот набор растет или уменьшается по мере создания или потребления новых UTXO.
Произведенные выходы являются дискретными и неделимыми единицами стоимости, из чего следует, что неизрасходованный выход должен быть потреблен целиком.
То есть вы хотите сказать, что если я должен заплатить 10 тысяч сатоши, но у меня есть UTXO на 3 биткоина, то я должен потратить всю сумму?
Хорошая новость заключается в том, что вы заплатите весь UTXO в качестве входа, но получите два выхода:
- один, который выплачивает 10k сатоши.
- а другой вернет вам разницу в виде resto.
Неприятно слышать, но это работает точно так же, как если бы вы тратили фиатную валюту. Если вам нужно заплатить 0,10 цента за голеадор, но у вас есть купюра в 10 евро, вы должны потратить ее целиком.
Единственная транзакция, которая не имеет входных данных, - это транзакцияcoinbase, специальная транзакция, используемая майнерами для поддержания всего процесса майнинга.
Каждая транзакция имеет идентификатор, называемый ID транзакции, или более кратко TxID
. Этот идентификатор создается с помощью хэш-функции над данными транзакции. Вместо этого для идентификации конкретного вывода TxID мы используем outpoints
- простые числа, помещаемые в конец TxID и предваряемые двоеточием (:), чтобы зафиксировать, к какому вводу мы обращаемся.
- Сценарий биткоина: завершает эту подборку определений и представляет собой язык сценариев, используемый в биткоине для определения условий, при которых средства высвобождаются в транзакции. Другими словами, он определяет правила, которые должны быть выполнены для того, чтобы средства в транзакции были потрачены.
Биткойн-скрипты состоят из двух частей: - Блокирующие скрипты: они встраиваются в транзакции и задают условия, необходимые для расходования средств. - разблокирующие скрипты: они встраиваются во входы, выполняя условия, заданные блокирующим скриптом.
Для упрощения можно привести пример: если у вас есть заблокированный выход от блокирующего скрипта, который говорит:
3 + x = 5
Легко заметить, что мы можем потратиться на использование сценария разблокировки 2 во входе транзакции.
Тот, кто проверяет эту транзакцию, соединит наш разблокирующий скрипт (2) с блокирующим скриптом (3 + x = 5), получив утвердительный ответ, что позволит потратить вывод. Конечно, базовая арифметика в этих сценариях не рассматривается, на практике она требует демонстрации знания секрета, и здесь мы возвращаемся к концепции цифрового ключа.
Теперь, когда мы проанализировали скрипты блокировки и разблокировки, давайте повторим пример с пиццей, пытаясь приблизиться к тому, как это работает на самом деле:
- Крис платит Мусатцу 10 тысяч биткоинов, чтобы купить пиццу.
- Простейший скрипт блокировки требует подписи Мусатца для разблокировки средств.
- Скрипт выглядит примерно так:
<подпись> <пубкей> CHECKSIG
.
CHECKSIG берет два элемента, подпись и открытый ключ, и проверяет, что они мои. Открытый ключ уже находится в блок-скрипте, не хватает только мускульной подписи, соответствующей этому открытому ключу. Только я обладаю (или должен обладать) закрытым ключом. Только я могу сгенерировать действительную подпись, которая позволит мне потратить эти сатоши.
Я должен предоставить скрипт разблокировки, содержащий мою цифровую подпись. Результатом этой операции будет TRUE!
<musclesatz signature> <musclesatz pubkey> CHECKSIG
Конечно, существуют и другие, гораздо более сложные типы скриптов. Здесь несколько примеров.
Что такое платежный канал? #
Lightning Network - это одноранговая сеть платежных каналов, реализованная в виде умных контрактов (не подумайте ничего плохого) на таймчейне Биткойна. Но это определение было бы упрощенным, потому что это также протокол связи, который определяет, как участники этой сети выполняют эти умные контракты.
Платежный канал - это связь между двумя узлами Lightning. Эти отношения позволяют определить баланс (в миллисатоши) между этими двумя узлами Lightning.
Узел Lightning - это программное обеспечение, способное говорить по протоколу LN. Они обладают тремя основными характеристиками:
- Они являются кошельками, которые отправляют и получают платежи через сеть Lightning.
- Они должны взаимодействовать с другими узлами по принципу “равный-равному”.
- Они должны иметь доступ к таймчейну для защиты средств, используемых для платежей.
Канал связи защищен криптографическим протоколом, который гарантирует справедливость, используя криптографию и делая его де-факто справедливой системой. Этот протокол устанавливается, когда оба участника вносят вклад в общий фонд в мультисигмальном адресе 2-ди-2. Внесение вклада в адрес 2-оф-2 подразумевает, что обе стороны должны согласиться.
Мы с Крисом договариваемся о серии приватных транзакций, которые расходуют этот баланс, но при этом никогда не публикуем их в таймчейне. Последняя транзакция из различных последовательностей в нашем канале представляет собой текущее состояние канала и определяет, как этот баланс делится между мной и Крисом.
Проведение Lightning-транзакции эквивалентно перемещению части баланса ко мне, если мне заплатят, и к Крису, если я должен буду заплатить ему.
Любое перемещение средств в ту или иную сторону обрабатывается смарт-контрактом, настроенным на наказание участника канала, если он пытается отправить старый статус, который относится к прошлому и поэтому больше не действителен.
Пересылка платежей #
Когда у нескольких участников сети есть несколько платежных каналов, они могут быть переадресованы (помните маршрутизацию?) из одного канала в другой, эффективно определяя пространственный путь в сети, соединяющий несколько каналов.
Мы уже говорили, что каналы создаются из мультисигма-адресов; чего мы не говорили, так это того, что транзакции по обновлению баланса канала являются предварительно подписанными транзакциями Bitcoin. Это означает, что доверие, необходимое для работы LN, исходит от доверия децентрализованной сети высшего уровня: уровень 1, Биткойн.
В чем суть?
Lightning - это приложение поверх Bitcoin, использующее транзакции Bitcoin и его скриптовый язык. Это креативный и умный способ обеспечить произвольное количество мгновенных платежей с очень низкой комиссией без необходимости доверять кому-либо, кроме самого Биткойна.
Возвращаясь к теме платежных каналов, давайте поговорим о возможных ограничениях:
- Время, затрачиваемое интернетом на передачу нескольких сотен байт (ничтожно мало).
- Пропускная способность канала, то есть количество биткоинов, принятых при открытии канала.
- Верхний предел размера транзакции биткойна: поскольку каждый платеж Lightning поддерживается транзакцией биткойна, которая может быть еще в процессе, размер блока влияет на количество платежей, которые могут быть активны одновременно на одном платежном канале.
Транзакция финансирования #
Основополагающим элементом канала, как мы уже говорили, является мультисигма 2-оф-2 адрес. Один из пиров, участвующих в открытии платежного канала, может профинансировать его, отправив сатоши на мультисиг-адрес. Эта транзакция называется финансирующей транзакцией и не может быть отличима на таймчейне от любой другой транзакции. О том, что это был канал Lightning, можно узнать только в момент урегулирования транзакции, то есть когда канал будет закрыт.
Сумма, вложенная в транзакцию финансирования, называется емкостью канала
и определяет максимальную сумму, которая может быть отправлена по этому финансируемому каналу.
Транзакция возврата средств #
Предположим, что теперь я установил платежный канал с Крисом. Крис, однако, озорничает. Он заставил меня внести средства на адрес multisig 2of-2, но теперь отказывается сотрудничать со мной и подписывать свою транзакцию.
Как мне разблокировать средства?
Чтобы избежать этих проблем, мне нужно заранее создать транзакцию возврата, которая будет расходоваться с мультисиг-адреса, возвращая мои сатоши. Мне нужно, чтобы эта транзакция возврата была подписана до того, как я передам транзакцию финансирования на этот адрес, т.е. до финансирования канала.
Теперь я защищен!
Транзакция обязательства #
Эта транзакция представляет собой соглашение между пирами в канале, которое выплачивает каждому пиру его баланс, гарантируя, что им не нужно доверять друг другу.
Подписывая транзакцию обязательства, вы берете на себя обязательства по балансу, существующему на канале в этот конкретный момент. Если я захочу забрать свои средства с канала, я смогу сделать это в любой момент, именно благодаря подписанию этого контракта.
Кстати, каждый раз, когда баланс канала “меняется”, создаются эти транзакции обязательств, обновляя новый статус канала и разделяя баланс между тем, что причитается мне, и тем, что причитается моему партнеру.
Если мой партнер по каналу исчезнет? Нет проблем. Если мой партнер по каналу отказывается сотрудничать? Нет проблем. Если мой партнер пытается меня обмануть? Нет проблем.
Пока что ответ “без проблем” может показаться дымчатым и бессмысленным, позже мы подробно разберем то, о чем я говорил.
Можете ли вы обманывать? #
Возвращаясь к моему другу Крису, я открываю с ним канал, внося 100 тысяч сатоши на мультисигмальный адрес 2-оф-2. Мы обмениваемся подписями, и я передаю транзакцию на таймчейн.
Мы говорили, что транзакции-обязательства также создаются каждый раз, когда баланс канала меняется, поэтому если мы предположим, что я отправил 30 тысяч Крису, то в новой транзакции-обязательстве будет сказано, что адрес выплачивает 70 тысяч сатоши мне и 30 тысяч сатоши Крису.
Но теперь у меня есть две транзакции обязательств, первая из которых определяет начальное состояние в момент t0
на 100 тыс. сатов, а вторая в момент t1
- представляющая текущее состояние - на 70 тыс. сатов.
Мне приходит в голову нездоровая мысль: а если я опубликую свою предыдущую транзакцию с обязательствами на 100 тыс. сатов, будет ли это означать, что адрес теперь заплатит мне 100 тыс. сатоши?
Биткойн устойчив к цензуре, и ничто не мешает мне опубликовать старый статус, который больше не действителен. Ничто, кроме шифрования, конечно.
Чтобы предотвратить такое воровство, транзакции с обязательствами делаются так, что если старый статус будет передан, вольно или невольно, я могу быть наказан.
Это нежелание из теории игр, как в примере про младших братьев с тарелкой чипсов.
Мой стимул к обману очень высок, потому что в тот момент, когда я публикую старый статус канала, Крис может забить на меня и имеет возможность потребовать весь пополненный баланс адреса.
Как работает наказание, мы разберем позже, потому что мне придется ввести понятия задержка таймлока и секрет отзыва.
Давайте объявим канал! #
Как мне оповестить всю сеть о наличии моего нового платежного канала? Как сделать его публичным?
Существует протокол, называемый gossip
, для сообщения другим узлам о существовании, емкости и комиссии моего канала.
Объявление канала хорошо тем, что он становится доступным для других узлов для маршрутизации платежей, даже генерируя некоторую комиссию для меня. Однако, с другой стороны, неанонсированный канал обладает определенной степенью конфиденциальности, по крайней мере, до тех пор, пока канал не будет закрыт на timechain.
Говоря о закрытии канала, как вы думаете, когда стоит закрыть канал LN?
Нет, как правило, лучше никогда не закрывать каналы.
Их закрытие влечет за собой транзакцию на цепи, сборы, раскрытие присутствия канала, но самое главное… в этом нет особого смысла, за исключением особых причин.
Держать каналы открытыми полезно еще и потому, что в тот момент, когда у меня закончились возможности для отправки, я все еще могу получать благодаря ребалансировке
.
Канал может быть закрыт тремя различными способами:
- С помощью согласованного закрытия, что является правильным способом:
- Мой узел LN сообщает узлу LN моего коллеги о моем намерении закрыться.
- Оба узла работают над закрытием.
- Новые попытки маршрутизации больше не принимаются, а текущие разрешаются.
- Узлы готовятся к транзакции закрытия: последнее состояние кодируется, чтобы определить, когда баланс должен быть присвоен обоим сверстникам.
- Достигается соглашение о том, как разделить комиссионные за закрытие на таймчейне.
- Каждый канальный партнер получает свою долю оставшегося баланса.
- При принудительном закрытии, что является неправильным способом:
- Я пытаюсь закрыть канал без согласия другого участника.
- Я публикую последнюю транзакцию обязательств моего узла.
- После подтверждения будет два расходуемых выхода: один мой, другой равноправного участника.
Именно здесь проблема начинает становиться более сложной, потому что если я принудительно закрыл канал, то мой выход будет заблокирован time-lock delay
и я не смогу потратить свои биткоины до какого-то будущего времени (обычно это две недели, измеряемые в высоте блока на таймчейне).
- При нарушении протокола - очень и очень плохой способ:
Нарушение протокола происходит, когда я пытаюсь обмануть, публикуя транзакцию с обязательствами, которая представляет собой старое состояние канала. Чтобы мой коллега заметил эту попытку обмана, он должен быть онлайн и следить за новыми блоками на таймчейне и транзакциями на них.
Несмотря на то что я опубликовал старое состояние, у меня есть временная блокировка, не позволяющая мне потратить баланс, поэтому у моего коллеги есть время, чтобы принять меры и наказать меня. Наказав меня, мой коллега сможет снять всю внесенную сумму (видите, как отбивает охоту мошенничать?), а закрытие сделки становится очень быстрым, потому что не происходит закрытия сделки.
Чтобы закончить на высокой ноте, мой сверстник хочет, чтобы транзакция, наказывающая меня, была принята как можно скорее в блокчейне, поэтому он также готов заплатить максимальную комиссию на L1: он может оплатить ее из моей части баланса 😁.
Но что делать, если срок блокировки истек, а он не заметил, что я разместил старый статус?
К сожалению, он потеряет средства, либо полностью, либо в соответствии с последним статусом обязательств, который я успел опубликовать и завершить, превысив временную задержку.
Как мы можем обнаружить нарушение протокола против нас?
С помощью правильно управляемого узла Lightning, работающего 24/7, или с помощью личной или сторонней watchtower
.
Счет-фактура #
Большинство платежей Lightning начинаются с выставления счета, который генерируется лицом, осуществляющим сбор средств, - получателем платежа. В этом счете содержится основная информация для проведения платежа:
- Хэш платежа.
- Получатель.
- Фактическая сумма и, возможно, необязательное описание.
Хэш платежа создается получателем платежа, выбирающим безопасным и не предсказуемым способом псевдослучайное число данных, которое подается на хэш-функцию. Это случайное число мы называем предшественником изображения
.
H = SHA-256(Pre-Image)
У нас есть платежный хэш. Из свойств хэшей мы знаем, что его нельзя инвертировать или принудительно изменить, поэтому никто не сможет узнать, что такое Pre-Image из хэша, исходя из результата хэширования. Предварительное изображение является секретом, и после его раскрытия любой, у кого есть хэш, может проверить, что предварительное изображение действительно было секретом.
Суть в том, что хэш платежа позволяет платежу путешествовать по нескольким каналам атомарно: он либо доходит до места назначения, либо нет. Никакой середины нет.
Счета-фактуры обычно отправляются вне Lightning, используя любой механизм связи. Очень популярным является QR-код из-за его удобства и компактности. QR-код содержит всю информацию, о которой говорилось выше.
Еще немного информации о счетах-фактурах:
- У них есть срок действия, поэтому получателю не нужно хранить все предварительные изображения. Когда счет оплачен или срок его действия истекает, его можно удалить.
- Они могут содержать
маршрутные подсказки
, которые позволяют отправителю использовать необъявленные каналы для построения пути к получателю (теневые каналы, мы обсудим это позже в статье).
Поиск пути, маршрутизация #
Эти два термина часто путают:
- Поиск пути* заключается в нахождении наилучшего пути от источника к месту назначения.
- Использование этого пути называется маршрутизацией.
Lightning использует протокол source-based
для поиска пути и протокол onion-routed
для маршрутизации.
Стандартный способ поиска маршрута - это тестирование различных маршрутов итеративно, пока не будет найден один, обладающий достаточной ликвидностью, чтобы позволить пересылать платежи. Возможно, это не тот метод, который минимизирует плату за маршрутизацию, но в целом он работает достойно.
Было бы здорово (а может, и нет, это зависит от ситуации), если бы у нас были точные балансы каждого канала, потому что тогда поиск путей был бы решаем любым студентом университетского курса по исследованию операций. Это не так, балансы не и не могут быть известны участникам сети.
Переходя на минутку к маршрутизации, отметим, что Lightning была очень сильно вдохновлена знаменитой сетью [Tor] (https://www.torproject.org/). Протокол в ней не совсем похож на Tor, потому что он лишь повторяет концепцию; в Lightning протокол маршрутизации называется Sphinx и работает по аналогии с Tor: отправитель строит весь лук от сердцевины до самого внешнего слоя.
Платежная информация для получателя шифруется ключом, который может расшифровать только получатель; эта информация является ядром всей операции маршрутизации. В главе, посвященной маршрутизации, мы пошагово проанализируем построение лука, в том числе с криптографической точки зрения.
Чтобы дать общее представление, платеж строится в форме лука, начиная с получателя и добавляя новый слой к луку, двигаясь в обратном направлении по найденному пути, таким образом, от получателя к отправителю. Первый слой лука, самый внешний, будет являться каналом отправителя, принимающим пакет лука для передачи в сеть.
Когда луковица будет отправлена, каждый узел будет знать только узел, от которого он получил луковицу, и узел, которому он ее передаст, не имея представления о том, кто является отправителем и получателем.
Небольшое уточнение: вы можете подумать, что пакет на самом деле очищается как лук (а разве лук очищается?).
На самом деле, когда каждый узел читает ту часть, за которую он отвечает, он также добавляет криптографический “наполнитель”, чтобы вернуть размер сообщения к первоначальному размеру, задуманному отправителем (1300 байт). Эта небольшая игра делается для защиты конфиденциальности, не позволяя промежуточным узлам получить информацию о длине пути или количестве задействованных узлов, что повышает общую безопасность.
Эти маленькие луки (маленькие, потому что помещаются в один пакет TCP/IP) построены так, чтобы иметь одинаковую длину на всем пути маршрутизации.
Пересылка лука #
Мне нужно переслать лук. Наконец я нахожу подходящий маршрут.
Я пересылаю сообщение своему сверстнику, и мы говорим, что каждый узел обрабатывает один слой лука. По сути, каждый узел получает сообщение Lightning под названием update_add_htlc
с хэшем платежа и лука. Затем в работу вмешивается алгоритм пересылки платежа и выполняет следующие операции:
- Расшифровывает внешний слой и проверяет целостность сообщения.
- Подтверждает, что может удовлетворить предложения по маршрутизации, основанные на плате и исходящей пропускной способности.
- Обновляет статус на входящем канале.
- Добавляет известные данные наполнителя для поддержания постоянной длины лука.
- Он, в свою очередь, направляет лук на свой исходящий платежный канал, отправляя
update_add_htlc
, который включает тот же платежный хэш и лук. - Он работает со своим каналом-аналогом для обновления статуса канала.
Но что произойдет, если в этом процессе возникнет общая ошибка?
Происходит то, что сообщение распространяется в обратном направлении обратно к отправителю с сообщением об ошибке update_fail_htlc
. Каждый узел, участвующий в маршрутизации, также видит это сообщение.
Если вы задаетесь вопросами типа: что такое HTLC? Почему каждый узел должен знать о сбое и получать это сообщение об ошибке?
Вскоре вы получите ответы на все свои вопросы.
(опционально) если вы хотите узнать больше о шифровании одноранговых соединений в Lightning, я рекомендую посетить сайт Noise Protocol Framework.
Резервное копирование каналов #
Более или менее все знакомы с BIP-39 Биткойна, который позволяет нам получить состояние кошелька с помощью мнемоники. Для тех, кто не помнит, Bitcoin Improvement Proposal 39 позволяет генерировать последовательность англоязычных слов из публичного списка, которая служит “затравкой” для создания детерминированного кошелька с почти бесконечным списком открытых и закрытых ключей.
Но как сделать резервную копию в Lightning?
Кошельки Lightning также используют резервную копию мнемоники BIP-39, но только для части внутрицепочечной коммуникации. Это важно понимать.
Для каналов необходим еще один уровень резервного копирования. Это резервное копирование называется статическим резервированием канала
(SCB) и вступает в игру всякий раз, когда в канале происходит изменение состояния. Это должно заставить нас задаться вопросом, потому что если по несчастью старая (уже отозванная) транзакция обязательства будет восстановлена, наш контрагент канала может подумать, что мы пытаемся его обмануть, наказать нас, запросить штрафную транзакцию и опустошить адрес.
Еще один аспект, который следует учитывать, - это то, что резервные копии SCB должны быть зашифрованы, чтобы поддерживать высокий уровень конфиденциальности и безопасности: если я пропущу незашифрованную резервную копию, любой может использовать ее не только для просмотра моих платежных каналов, но и для их закрытия, чтобы передать баланс моему контрагенту.
Sweep #
Что мне делать, если баланс в моем кошельке Lightning становится слишком большим и я хочу снизить риск?
Я могу провести зачистку, причем разных типов:
| on-chain | off-chain | submarine swap |. | ——— | ——– | —— | | Перевести средства с кошелька LN на кошелек Bitcoin путем совместного закрытия канала, мы это уже видели.| _Этот способ включает в себя запуск второго, необъявленного узла LN в сети. Я использую его как копилку, регулярно переводя средства на этот “скрытый” узел, который, как я помню, в любом случае является горячим кошельком.| | _Это обмен между цепочкой и внецепочечным обменом. Он является атомарным, то есть если я инициирую подводный обмен и отправлю баланс канала LN, другая сторона в ответ отправит мне биткоины на цепочке.| |
Submarine swap #
Думаю, стоит разобраться с термином “подводный своп”, потому что он часто вызывает путаницу:
Предыстория:
- У Мусатца есть биткоины на цепочке и он хочет получить средства на LN (вне цепочки).
- Крис имеет средства на LN (вне сети) и хочет получить биткоины на сети.
- Марк - это кто-то между Мусатцем и Крисом, кто координирует торговлю.
- Крис генерирует предварительное изображение (секретное) и хэширует этот секрет, вставляя его в счет для меня.
- Я выполняю транзакцию на цепи по контракту с пунктом о том, что эти биткойны можно выкупить, предоставив Pre-Image хэша инвойса Криса и сделав действительную подпись Марка, чтобы убедиться, что никто, кроме Марка, не может их взять.
- Марк видит этот контракт и знает, что для выкупа этих биткоинов он должен обязательно заплатить Крису, потому что только тогда Крис откроет ему Предварительное изображение.
- Крис получает оплату от Марка, а затем раскрывает ему Предварительное изображение.
- Марк использует это предварительное изображение и действительную подпись, чтобы перевести биткоины на контролируемый им адрес.
- Я убежден, что Крис был оплачен, потому что Марк мог перемещать средства в рамках контракта, поэтому он знал о предварительном изображении и поэтому оно было сообщено ему Крисом, который должен был получить необходимую оплату.
Если бы Марк не заплатил Крису, Марк не смог бы претендовать на биткоины по контракту, и благодаря определенному пункту в этом контракте (который, кстати, называется Hashed Timelock Contract
- HTLC, мы подробно рассмотрим его позже) через определенный промежуток времени я смог бы забрать свои биткоины по этому контракту, потому что там также был таймлок в мою пользу.
Это краткое изложение всего, что я объяснил с точки зрения Биткойн-крипта.
OP_SIZE 32 OP_EQUAL
OP_IF
OP_HASH160 <ripemd160(swapHash)> OP_EQUALVERIFY
<receiverHtlcKey>
OP_ELSE
OP_DROP
<cltv timeout> OP_CHECKLOCKTIMEVERIFY OP_DROP
<отправительHtlcKey>
OP_ENDIF
OP_CHECKSIG
Несколько сравнений с Биткойном #
Lightning построена на базе Bitcoin, пока все хорошо. Он наследует некоторые функции и свойства, но есть и несколько важных отличий:
- Адрес и счет: адрес Bitcoin можно использовать многократно (не рекомендуется с точки зрения конфиденциальности) бесконечное количество раз. LN-счета используются одноразово, на определенную сумму. (есть исключение - механизм
keysend
, который мы рассмотрим позже)
Выбор UTXO и поиск пути: чтобы совершить платеж в Bitcoin, я должен потратить как минимум один UTXO, в то время как в LN платежи не требуют потребления
вывода
, поскольку, как мы видели, это ребалансировка баланса, присутствующего на мультисиг-адресе.Сборы за майнинг и маршрутизацию: в Bitcoin мы платим майнерам за включение нашей транзакции в блокчейн, а в LN пользователи сети платят другим пользователям сети за маршрутизацию платежей по каналам. Эта плата состоит из
базовой комиссии
, которая является фиксированным компонентом, выплачиваемым за маршрутизацию (у каждого канала она может быть своя), иставки
, которая является переменным компонентом платежа, пропорциональным стоимости платежа (еще одно отличие, на timechain комиссия не пропорциональна стоимости)Публичные транзакции биткоина и приватные платежи Lightning: суть статьи. Timechain является публичной, а платежи Lightning - нет.
Сатоши и миллисатоши: в таймчейне наименьшей единицей является сатоши, в то время как в LN есть и тысячные доли сатоши. При расчете канала Lightning миллисатоши округляются до ближайшего целого сатоши.
Программное обеспечение сети Lightning #
Я начал с того, что решил оставить эту главу пустой, чтобы не утяжелять статью. Если вам интересно узнать подробно и технически грамотно, как настроить среду разработки для работы Lightning, дайте мне знать, вы можете связаться со мной в X/Twitter или в telegram.
Lightning - это не продукт или компания, а набор открытых стандартов, которые определяют общую линию взаимодействия. Не существует эталонной реализации, за которой можно было бы следить, как в случае с Bitcoin Core, но стандарт определяется набором директив под названием Basis of Lightning Technology
(BOLT), которые можно найти на GitHub.
Поскольку консенсуса, как в таймчейне, нет, любой может строить поверх основных директив, и если функции станут успешными, они могут стать частью BOLT.
Сердце Lightning #
Lightning состоит из сложного набора протоколов, работающих в Интернете. Я классифицирую их на 5 различных слоев, где каждый слой использует (и абстрагирует) слой, расположенный ниже него.
Layer |
---|
Сетевое соединение: определяет протоколы, которые позволяют взаимодействовать в сети |
Обмен сообщениями: определяет протоколы, полезные для форматирования или кодирования сообщений |
Peer-to-Peer: определяет протоколы связи между различными узлами LN |
Маршрутизация: определяет протоколы обнаружения пути и маршрутизации сообщений |
Оплата: определяет интерфейс оплаты счетов |
Каналы оплаты #
Чтобы понять концепцию работы платежных каналов, необходимо задать себе вопрос:
Что значит владеть биткойнами?
Владеть биткойнами - значит владеть закрытым ключом биткойн-адреса, на котором есть хотя бы один UTXO. Этот ключ позволяет мне подписать транзакцию и узаконивает тот факт, что я являюсь владельцем этого баланса, поскольку никто другой об этом не знает.
Но право собственности не всегда находится в руках одного человека. Биткойн также позволяет использовать мультисиговые адреса, когда для подписания транзакции требуется множество закрытых ключей. Простой для понимания схемой мультисиг является схема 2of-3: она означает, что для подписания транзакции и расходования средств с этого адреса требуется 2 из 3 человек. Число 2 в этом случае называется кворумом.
Но что, если в схеме “2 из 2” одна из сторон не сотрудничает?
Кворума нет, и средства не могут быть потрачены. Такая схема не будет считаться справедливой, и на самом деле есть возможность предотвратить этот сценарий с помощью транзакции возврата средств - уже проанализированной в начале статьи -. На сделку по возврату средств нужно смотреть как на брачный контракт. Прежде чем финансировать адрес 2 из 2, я убеждаюсь, что у меня есть план выхода из сделки и я чисто отделяю свои средства от средств контрагента.
Давайте на секунду вернемся к построению канала. Чтобы создать канал с Крисом, два наших узла должны установить интернет-соединение, чтобы начать торговлю. Каждый узел идентифицируется открытым ключом в шестнадцатеричном формате, сгенерированным на основе закрытого корневого ключа, хранящегося внутри узла. Но этого недостаточно. Нам также нужен сетевой адрес для связи, и здесь у нас есть два варианта: TCP/IP или Tor.
Затем мы определяем идентификатор узла
, который имеет вид ID@Address:Port
, но его все равно сложно прочитать. Было бы лучше встроить все это в QR-код, не так ли?
Просто отсканируйте его, и все, два узла соединены 😉.
Теперь, когда узлы соединены, мы можем начать думать о создании платежного канала, и это делается путем обмена шестью сообщениями (по три для каждого пира) между нашими соответствующими узлами:
open_channel
/accept_channel
: Я отправляю Крису свои возможности и ожидания сообщением open_channel, если Крис принимает мой запрос, он отвечает сообщением accept_channel.funding_created
/funding_signed
: Я хочу избежать обмана, поэтому я создаю как транзакцию финансирования с помощью funding_created, так и транзакцию возврата средств, чтобы защитить себя от возможного обмана. Если Крис не против, он ответит funding_signed. Теперь я могу спокойно передавать транзакцию финансирования (на цепочке) для создания и привязки платежного канала. Это не мгновенная операция, так как мы работаем на таймчейне, поэтому мы будем ждать подтверждения блокировки.funding_locked
/funding_locked
: как только транзакция получит достаточное количество подтверждений (определено в начальном сообщении accept_channel), мы с Крисом обменяемся сообщением funding_locked, чтобы начать отправку Lightning-транзакций.
Во время сужения канала я сделал кое-что необычное: я создал две конкатенированные транзакции. Как я это сделал, если финансирующая транзакция даже не была передана по таймчейну?
Благодаря функции под названием Segregated Witness
(SegWit), появившейся в Биткойне в 2017 году, я могу ссылаться на выходы транзакций, используя хэш транзакции, а не идентификатор выхода. Это позволяет мне конкатенировать транзакции, которые не передаются.
Да, то, что я написал, звучит как бред, но это не так, позвольте мне объяснить.
Я имею в виду, что транзакция возврата средств действительна, если она передается по таймчейну и если на входе этой транзакции есть моя подпись и подпись Криса. Даже если мой узел еще не передал транзакцию финансирования, я могу тем временем создать транзакцию возврата, вычислив хэш транзакции финансирования и сославшись на него как на вход транзакции возврата. Я заранее знаю, что ссылка будет действительной, потому что я вычислил ее хэш.
Теперь канал установлен, но вся ликвидность на моей стороне. Это значит, что я могу отправить сатоши Крису, но у Криса нет средств, чтобы отправить мне. Если я отправлю несколько своих сатоши, статус канала изменится и появится транзакция обязательства. Баланс канала обновляется.
Предположим, что мы с Крисом начали совершать транзакции в сатоши, а затем сгенерировали множество транзакций с обязательствами. Мы приходим к точке, где у нас есть такой баланс в канале:
Если я захочу закрыть канал, передав и подтвердив имеющуюся у меня транзакцию обязательств, я не смогу потратить баланс в течение 400 блоков, тогда как Крис может сделать это немедленно. Конечно, верно и обратное.
Почему существует эта временная задержка и для чего она нужна?
Мы уже упоминали о ней ранее. Она служит для того, чтобы Крис мог применить штраф, если я передал старую транзакцию с обязательствами, чтобы обмануть его, украв саты. Эта временная задержка оговаривается в сообщениях при построении канала.
Но что произойдет, если я опубликую старое состояние канала и как я могу быть наказан?
Каждый раз, когда состояние канала обновляется новой фиксацией, я получаю от своего контрагента криптографический секрет, называемый revocation secret
, связанный с предыдущим состоянием. Если я попытаюсь обмануть Криса, он использует секрет отзыва предыдущего состояния и получит криптографическое доказательство того, что состояние
было отозвано и я нарушаю правила, поскольку закрываю канал с недействительным состоянием.
То, что дает Крису*** возможность наказать меня, - это секрет отзыва, который дает ему математическое доказательство моего жульничества.
Но я не хочу его обманывать и вместо этого хочу закрыть канал кооперативно. Я договариваюсь о заключительной транзакции с обязательствами под названием shutdown, которая выплачивает каждой стороне ее баланс в соответствии с ее текущим состоянием. Я указываю биткойн-скрипт, соответствующий адресу закрытия моего кошелька, и говорю Крису выполнить транзакцию закрытия, которая выплатит мой баланс на этот кошелек. Крис сделает то же самое и, кроме того, согласится на закрытие кооператива. Наконец-то мы можем договориться. Я отправляю финальное сообщение closing_signed, в котором предлагаю комиссию за транзакцию по закрытию внутрицепочечного обмена с моей подписью, и если Крис согласится, он выплатит мне ту же комиссию, предложенную с его собственной подписью. Если он не согласен, то предложит другую плату. Этот цикл закрытия может продолжаться до тех пор, пока мы не найдем решение, которое устроит нас обоих.
Маршрутизация канала #
Чтобы понять обширный мир маршрутизации, мы можем начать с примера из физической реальности.
Мне нужно отправить 10 монет из очень редкого материала Крису, но у меня нет с ним прямой связи. Однако мы оба знаем Марка и имеем с ним прямую связь. Как мне убедить Марка отдать 10 монет Крису, не обманув его и убедившись, что он не сбежит? И главное, как мне узнать, что монеты переданы Крису?
Одно из возможных решений - пообещать Марку 10 монет, если он докажет мне, что передал Крису 10 монет.
Но зачем Марку подписывать такой контракт без какой-либо реальной выгоды?
На самом деле это не очень удобно, так что я мог бы изменить его, пообещав Марку 11 золотых монет, если он докажет мне, что доставил Крису 10, таким образом, он получил бы комиссионные в размере одной золотой монеты, неплохо.
Однако остается проблема доверия, поэтому мы решаем использовать гарантийный сервис под названием escrow
.
Крису необходимо получить деньги, поэтому он генерирует секретное значение R
(для простоты предположим, что это значение: hello world!), подвергнутое хэш-алгоритму SHA-256. Хеш от R мы назовем payment hash
, а секрет, который разблокирует платеж payment Pre-Image
.
В этот момент Крис отправляет мне хэш платежа по телеграмме. Я не знаю секрета, но тем временем я могу переписать свой контракт с Марком, используя этот хэш платежа и сказав:
Марк, я возмещу тебе 11 монет, если ты покажешь мне действительное сообщение (предварительное изображение), соответствующее этому платежному хэшу. Вы можете получить это сообщение, заключив контракт с Крисом. Чтобы гарантировать, что вы получите компенсацию, я заблокирую эти монеты в условном депонировании до того, как вы заключите контракт с Крисом.
Этот контракт защищает мои монеты, потому что пока я заблокирую их в условном депонировании, но главное, что я заплачу Марку, если он покажет мне действительное предварительное изображение для хэша платежа. Предварительное изображение - это доказательство того, что Крис был оплачен Марком.
В этот момент Марк заключает идентичный контракт с Крисом, говоря, что он возместит ему 10 монет, если Крис сможет показать действительное сообщение, соответствующее хэшу платежа. Он также предупреждает, что получит компенсацию после того, как раскроет секрет, поместив 10 монет в условное депонирование.
(Но помните, что Крис - получатель, и именно он сгенерировал предварительное изображение, поэтому он может показать его Марку и действительно получить деньги).
Все стороны заключили договор.
Крис отправляет предварительное изображение Марку; тот проверяет, совпадает ли секрет с хэшем платежа, и, получив подтверждение, приказывает эскроу выдать Крису 10 монет. Теперь Марк передает предварительное изображение мне, я проверяю его и приказываю службе эскроу выдать 11 монет для Марка.
Вот и все, все контракты решены. Я заплатил в общей сложности 11 монет, 1 из которых получил Марк в качестве вознаграждения, а остальные 10 достались моему получателю Крису.
Почему это работает?
Это работает, потому что при такой цепочке контрактов Марк не мог избежать того, чтобы монеты были заперты в депонировании, он их депонировал.
В чем слабость?
Если бы Крис решил не выпускать свой Pre-Image, у нас с Марком монеты застряли бы в эскроу. Но эта проблема легко решается, если применить к контракту крайний срок, таймлок-задержку.
Транзакции Lightning атомарны, они либо проходят, либо отправляются в нокаут!
Контракты с временной блокировкой хэша (HTLC). #
Протокол справедливости маршрутизации, используемый в Lightning, называется хэш-контрактом с временной блокировкой
(HTLC) и является тем, который мы только что описали в примере.
HTLC используют хэш предварительного образа платежа в качестве секрета, который разблокирует платеж. Существует также другой механизм маршрутизации, называемый точечным контрактом с временной блокировкой
(PTLC), который еще более эффективен и обладает лучшей конфиденциальностью, поскольку напрямую зависит от нового алгоритма, добавленного в 2021 году в Биткойн под названием подписи Шнорра
. Здесь немного больше информации.
Прочитав и поняв пример HTLC, я хотел бы обратить внимание на небольшую проблему: каждый из этих контрактов может быть разблокирован любым, кто знает предварительное изображение. Что произойдет, если Крис потратит и HTLC Марка, и мой? В таком случае это была бы не очень надежная система.
Сценарий HTLC должен иметь дополнительное условие, связывающее каждый HTLC с конкретным получателем: для этого мы требуем цифровую подпись, соответствующую открытому ключу каждого получателя, что не позволит никому другому потратить этот HTLC.
Еще одна небольшая проблема, которую необходимо решить:
Что произойдет, если какой-то узел выйдет из сети или не будет сотрудничать? Как мягко сделать так, чтобы платеж не прошел?
- Кооперативно: HTLC “отматывается” назад, удаляя его из транзакции обязательств без изменения баланса (подробнее об этом позже).
- Погашение с фиксацией времени: мы уже обсуждали это, каждый HTLC включает в себя пункт о погашении, связанный с задержкой по времени.
Пересылка платежей #
HTLC кажутся прерогативой многохоповых платежей, но на самом деле протокол Lightning также использует их для “локальных” платежей внутри канала. Причина этого заключается в поддержании последовательности и одинакового дизайна протокола в каждой точке сети. Для получателя платежа нет разницы между платежом, совершенным его коллегой по каналу, и платежом, направленным его коллегой, но от имени кого-то другого.
Давайте рассмотрим пример меня, Марка и Криса.
- У нас с Марком есть канал с балансом в 70 тыс. сатов на каждой стороне. Напоминаю вам, что транзакция обязательств, которая привела нас к такому результату, отложена и может быть отозвана.
- Я хочу, чтобы Марк принял HTLC на 50 тыс. сатов для передачи Крису. Для этого, однако, мне нужно отправить детали этого HTLC, такие как хэш платежа и сумма этого платежа. Я делаю это с помощью сообщения
update_add_htlc
.
Технически обмен сообщениями происходит следующим образом:
Полученной Марком информации достаточно для создания новой транзакции обязательства, которая имеет одинаковый баланс для состояния канала (моего и Марка) и новый выход, представляющий HTLC, предложенный мной. Это новое обязательство будет иметь 50 тыс. сатов на выходе HTLC, сумма, которая поступает непосредственно от меня, поэтому мой новый баланс будет составлять 20 тыс. сатов.
Если происходит изменение состояния канала, то должно появиться новое обязательство, и после того, как Марк создаст его, я подписываю его сообщением commitment_signed
.
Теперь у Марка есть подписанное обязательство, он должен подтвердить его и отозвать старое обязательство; он делает это с помощью сообщения revoke_and_ack
, и это позволяет мне создать ключ отзыва для создания штрафной транзакции. Происходит то, что Марк больше не сможет опубликовать свое старое, только что отозванное обязательство, иначе у меня будет средство наказать его (ключ отзыва). Старое обязательство, во всех смыслах и целях, было отозвано.
Чего не хватает?
Ну, я еще не отозвал свое старое обязательство, в моем текущем состоянии все еще нет HTLC! Я создаю новое зеркальное обязательство, содержащее HTLC, которое все еще должно быть подписано Марком. Точно так же, как это сделал Марк, я делаю revoke_and_ack
и подписываю новое обязательство с помощью commitment_signed
. Теперь я также обязуюсь больше не публиковать старый статус, я предоставил Марку ключ для отзыва старого обязательства.
Теперь у нас с Марком есть новое обязательство по каналу, которое также содержит дополнительный вывод HTLC, но новый бюджет все еще не отражает тот факт, что я отправил 50k Марку. Оплата будет возможна только в обмен на доказательство оплаты Крису!
На самом деле давайте теперь предположим, что Марк и Крис делают совершенно одинаковые вещи. Крис - получатель, единственный, кто знает предварительное изображение хэша платежа, поэтому Крис может немедленно удовлетворить HTLC с Марком.
Собственно, именно это он и делает, отправляя Марку сообщение update_fulfill_htlc
. Как только Марк получает это сообщение, он сразу же проверяет, создает ли это предварительное изображение хэш платежа; если оно соответствует условию TRUE, этот секрет может быть использован для погашения HTLC.
Марк также посылает update_fulfill_htlc
мне, предварительное изображение передается от Криса ко мне. Я выполняю ту же проверку, что и Марк, оценивая хэш предварительного изображения.
Это как раз и есть доказательство того, что Крису заплатили! .
Мы с Марком можем удалить HTLC из транзакций обязательств и обновить балансы наших каналов, чтобы отразить новую ситуацию с финансированием (очевидно, снова выполнив цикл обязательств и отзыва 🙂 ).
Но что, если произойдет ошибка или истечет срок?
В этом сценарии процесс будет развиваться аналогичным образом, в отличие от сообщения о сбое update_fail_htlc
. Мне пришлось бы удалить HTLC и пройти через цикл транзакций по принятию и отзыву обязательств, чтобы перевести состояние канала в новое обязательство, с балансами, установленными так, как они были до HTLC.
А как насчет платежей локально, на том же канале?
Замените в примере Марка на Криса. Прибыль.
Onion routing #
Первый узел-отправитель платежа называется узлом-источником. Последний узел, который является получателем, конечный узел. Каждый промежуточный узел между источником и получателем называется хопом
, и каждый хоп должен установить исходящий HTLC для следующего хопа. Информация, которую я, узел-отправитель, отправляю, может называться полезной нагрузкой хопа или данными хопа, а все сообщение называется одним.
Если перефразировать пример из предыдущего абзаца, используя эту новую терминологию, можно сказать, что я создал лук с данными полезной нагрузки хопа и указанием каждому хопу, как создать исходящий HTLC для отправки платежа Крису.
Есть два возможных формата, которые я могу использовать для передачи информации каждому хопу: устаревший, называемый hop data, и более гибкий, называемый hop payload.
С чего мне следует начать построение этого сообщения?
Очевидно, с полезной нагрузки хопа, которая будет доставлена Крису, конечному узлу. Этого достаточно, чтобы сказать, что сообщение, которое получит Крис, будет отличаться из-за наличия поля, все значения которого будут установлены в 0. Это поле называется short_channel_id
и является ссылкой на канал, которую я, как создатель полезной нагрузки хопа, использую для ссылки на каналы. Крис - мой получатель, поэтому я заполняю это поле всеми 0, потому что Крис не будет строить исходящий HTLC, так как он является получателем полезной нагрузки.
Конечно, никто, кроме Криса, не узнает эту информацию, потому что она зашифрована.
Затем я могу приступить к сериализации лукового сообщения в формате Type-Length-Value, который подходит для полезной нагрузки хопа. Затем я также подготовлю полезную нагрузку hop для Марка.
short_channel_id
(к нему уже обращались), amt_to_forward
- сумма в миллисатоши, и outgoing_cltv_value
- временная задержка HCLT, выраженная в будущей высоте в таймшине.Если amt_to_forward
составляет 50100 сатоши, Марк ожидает вознаграждение в 100 сатов за маршрутизацию платежа Крису. Ожидания по таймлоку и комиссии за маршрутизацию определяются разницей между двумя HTLC, входящим и исходящим.
Предположим, что между мной и Марком есть еще один узел, чтобы немного усложнить маршрутизацию лукового сообщения.
У меня есть три полезные нагрузки: Я создал их для Крис, для Марка и для Лукаса. Эти три полезные нагрузки будут обернуты в луковую упаковку!
Теперь мне нужно сгенерировать несколько ключей, которые понадобятся для шифрования различных слоев лука:
- Я должен быть уверен, что только конечный узел сможет прочитать его.
- Каждый посредник должен знать только предыдущий и следующий узел.
- Никто не должен знать общую длину пути.
- Каждый посредник сможет убедиться, что сообщение не является взломом.
Процесс построения называется построением пакетов. Интересный трюк, используемый в луковой маршрутизации, заключается в том, чтобы сделать путь одинаковым по длине для каждого узла, как если бы каждый узел видел лук в начале своего пути и еще 19 возможных хопов впереди (поскольку максимальный путь луковой маршрутизации составляет 20 хопов).
По мере “устранения” каждого слоя добавляются нежелательные данные, которые служат заполнителем, чтобы полезная нагрузка всегда была одинаковой длины.
Все это красиво, но как этот лук создается на практике?
Сначала луковица строится, начиная с конечного узла, то есть получателя сообщения, которым является Kris.
Я начинаю с создания “пустого” поля размером 1300 байт, сгенерированного псевдослучайным образом с определенным ключом.
Я начинаю вставлять полезную нагрузку hop, которую я создал ранее, вставляя ее (абстрактно) слева, таким образом, вынуждая заполнитель сдвигаться дальше вправо. Эта операция приводит к превышению размера, так как мы превышаем 1300 байт, поэтому небольшой кусок заполнителя “выпадет” и будет удален.
На данный момент данные Криса находятся в открытом виде в луковом сообщении, поэтому я должен как-то обфусцировать их с помощью ключа rho, который также (и только) знает Крис. Для обфускации на основе этого ключа генерируется поток псевдослучайных данных, и выполняется операция XOR между полезной нагрузкой хопа и этим потоком, сгенерированным rho.
Наконец, чтобы гарантировать подлинность и целостность фрагмента Криса, используется еще один ключ mu для вычисления HMAC
, который представляет собой не что иное, как контрольную сумму, основанную на хэше всей полезной нагрузки хопа.
Теперь настала очередь хоп-полезной нагрузки Марка. Я выполняю те же шаги, с той лишь разницей, что теперь я начинаю не с 1300 байт наполнителя, а с 1300 байт, содержащих полезную нагрузку Криса, соответствующим образом обфусцированную 🙂 .
Вставляя полезную нагрузку Марка слева, я должен быть очень осторожен, чтобы не перезаписать часть данных Криса! Здесь я также вычисляю HMAC с помощью ключа mu
и обфусцирую все ключом rho
. Однако на этот раз ключи mu
и rho
известны только Марку и мне, потому что этот хоп предназначен только для него.
Два замечания:
- У нас есть два HMAC, недавно добавленный внешний от Марка и внутренний от Криса.
- Когда Марк получит луковое сообщение, он не сможет определить количество полезной нагрузки внутри, для него оно всегда будет выглядеть как первая полезная нагрузка из возможных 20, именно потому, что мы сохраняем фиксированную длину с помощью трюка, который мы скоро увидим.
Те же действия я выполняю и для Лукаса. Луковая полезная нагрузка готова, и я могу отправить ее Лукасу, который является моим новым партнером по каналу. В итоге он будет выглядеть следующим образом:
- 1 байт для версии лука, чтобы сделать различие для будущих обновлений формата протокола.
- 33 байта открытого сеансового ключа, который будет использоваться каждым узлом для вычисления известных ключей обфускации и вычисления контрольной суммы (а также третий ключ, о котором я вам еще не говорил, называется
pad
). - Полезная нагрузка нашего лука составляет 1300 байт.
- 32 байта внешнего HMAC, который будет проверен моим партнером по каналу, Лукасом.
Я готов отправить Лукасу сообщение update_add_htlc
.
Сначала ему придется обновить состояние канала с помощью транзакции принятия обязательств, но мы уже подробно рассмотрели эту часть, поэтому я ее пропущу. “Новые” операции, которые должен будет выполнить Лукас, заключаются в получении сеансового ключа, генерации ключа mu
и проверке контрольной суммы HMAC
. После проверки целостности и подлинности Лукасу нужно будет получить полезную нагрузку своего хопа и затем переслать лук следующим хопам.
Проблема: если Лукас удалит свою полезную нагрузку, мы потеряем фиксированную длину 1300 байт (очень плохо).
И тут вступает в действие трюк, о котором я говорил: каждый узел должен генерировать заполнитель перед отправкой лука следующему узлу.
Лукас удваивает 1300-байтовую полезную нагрузку и кладет их рядом. Теперь у него есть два 1300-байтовых хопа. Второй хоп устанавливает все это в 0 (заполнение нулем). Он генерирует ключ rho
из ключа сессии и выполняет XOR между полезной нагрузкой хопа размером 2600 байт (один истинный, другой заполнен нулем) и потоком размером 2600 байт, сгенерированным на основе ключа rho.
Происходит следующее: первые 1300 байт, содержавшие истинную полезную нагрузку hop, смещаются, поскольку мы применили XOR во второй раз, поэтому Lucas может прочитать свою полезную нагрузку hop, вместо этого XOR между 1300 заполненными нулями байтами и потоком, сгенерированным на основе ключа rho, даст псевдослучайные данные.
Таким образом, Лукас прочитает то, что ему причитается, удалит это из полезной нагрузки хопа, все остальные байты сдвинутся влево, чтобы покрыть недостающее место в полезной нагрузке Лукаса, и использует оставшийся заполнитель из операции XOR для поддержания длины 1300 байт. Однако для отправки лукового пакета Марку необходимо добавить внешний HMAC, чтобы Марк мог проверить целостность и корректность.
В полезной нагрузке хопа Лукаса есть вся информация, необходимая для построения HTLC с Марком!
Марк выполняет те же действия, что и Лукас, и, наконец, Крис получает конечную полезную нагрузку. Когда он получает update_add_htlc
из хэша платежа, он знает, что это платеж для него, так что это последний прыжок. Он не делает ничего другого (или почти): он снимает слой с луковицы, но ничего не пересылает, потому что пришло время ответить Марку update_fullfil_htlc
, чтобы выкупить HTLC. Это в обратном направлении (Kris->Mark->Lucas->musclesatz) и все HTLC будут выкуплены, соответственно обновляя балансы каналов.
keysend
уже не получатель раскрывает Pre-Image отправителю, а отправитель встраивает Pre-Image в полезную нагрузку хопа: когда получатель получает лук, он использует этот Pre-Image (который соответствует хэшу платежа HTLC) для расчёта платежа! Таким образом, в этом случае нет необходимости обмениваться счетом или вставлять QR 😉Gossip #
До сих пор мы понимали, как построить луковое сообщение и как доставить его между узлами, но теперь вопрос заключается в следующем:
Как построить правильный маршрут к получателю? Как узнать информацию о его канале?
Мне нужен граф каналов
, который представляет собой не что иное, как (взаимосвязанный) набор публично объявленных каналов и узлов, которые они соединяют.
Используемый протокол называется gossip и позволяет каждому узлу обмениваться информацией с сетью Lightning, а также используется при создании каналов для распространения новостей
, например: Я создал новый канал. Я сообщаю всем моим другим каналам об этом новом соединении. Другим моим коллегам: пожалуйста, распространите это сообщение, пусть оно распространится по всей сети.
Сеть Lightning (почти) эквивалентна графу каналов Lightning.
Мы говорим об одноранговой сети, поэтому очень важно провести начальную фазу загрузки, чтобы узлы могли “видеть” друг друга. Первый шаг - обнаружить хотя бы один одноранговый узел, который является частью этой сети и имеет полный граф канала.
Используя один из бесчисленных загрузочных протоколов, я могу подключиться к пиру, так что теперь мне нужно загрузить и подтвердить граф канала. После этого я могу начать открывать и платежные каналы.
Важно поддерживать граф в актуальном состоянии, открывая и подтверждая новые каналы, устраняя закрытые внутрицепочечные каналы, а также устраняя каналы, с которых не приходит “бутстрап” каждые 2 недели или около того.
Как происходит первоначальный бутстрап?
Простым вариантом, но непрактичным из-за своей хрупкости, было бы использование ряда “жестко закодированных” пиров в программном обеспечении узла Lightning. Этот метод используется в качестве обратного хода на случай, если мы не сможем найти пиров с помощью другого метода, называемого bootstrap DNS
.
Для поиска пиров с помощью DNS, BOLT #10 мы используем три записи:
- SRV-записи для нахождения набора открытых ключей узла.
- A-запись для сопоставления открытого ключа с IPv4-адресом.
- AAA-запись для сопоставления pubkey с IPv6-адресом.
Когда вы набираете “musclesatz.com”, вы должны думать о DNS-записях “A” и “AAA” как о картах, которые помогают вам найти мой сайт в Интернете.
Запись “A” - это как карта для поиска домов в старом районе по названию улицы (123 Main Street). На практике это IPv4-адрес. Запись “AAA” - это карта для поиска домов в новом, самом современном районе, который настолько далеко ушел вперед, что уже не имеет стандартного адреса, как IPv4, а представляет собой серию цифр и букв.
Чтобы подключиться к узлу, нам нужен и открытый ключ, и IP-адрес!
Итак, шаги таковы:
Я определяю DNS-сервер, который поддерживает протокол Bolt #10. Для этого я могу использовать общий начальный сервер, поддерживаемый основными реализациями сети Lightning.
Я отправляю запрос
SRV
, чтобы получить набор пиров, которые я могу использовать для загрузки. Каждый из этих пиров идентифицируется открытым ключом, закодированным в форматеbech32
.Я декодирую открытые ключи, полученные из ответа на мой SRV-запрос, чтобы получить идентификаторы узлов.
Я выполняю DNS-запрос, чтобы получить IP-адрес целевого узла в сочетании с его идентификатором (открытым ключом).
Я подключаюсь к узлу с помощью клиента Lightning.
Я нахожу своего сверстника и устанавливаю первое соединение.
Теперь мне нужно синхронизировать и подтвердить ориентированный граф канала. Он называется ориентированным, потому что стрелки имеют связанное направление, а это значит, что связь от узла 1 к узлу 2 может быть не симметричной, то есть не обязательно подразумевает обратную связь от 2 к 1.
Каналы - это UTXO (мультисигмальные адреса 2 к 2), поэтому мы можем дать новое определение графу каналов: это подмножество UTXO Биткойна. Граф невозможно подделать, как мы увидим.
Информация из графа распространяется в сети с помощью трех сообщений, описанных в Bolt #7:
node_announcement
: осуществляет приветствие узла и содержит такую информацию, как идентификатор, возможности узла в маршрутизации платежей или его политики маршрутизации. Они не записываются в цепочку времени и действительны только при наличии соответствующего, последующего сообщенияchannel_announcement
. Проверить эти сообщения очень просто: если оно уже существовало, то новоеchannel_announcement
будет иметь более длинную временную метку; если объявления не было, то channel_announcement обязательно должно существовать в локальном графе каналов (мы рассмотрим это более подробно позже). Оно также должно иметь:- действительную подпись.
- все включенные адреса, упорядоченные по возрастанию в соответствии с идентификатором адреса.
- байты псевдонима должны иметь правильную кодировку UTF-8.
channel_announcement: теперь мы можем позаботиться о том, чтобы объявить о наличии нового канала. Если я хочу, чтобы мой узел разрешил маршрутизацию других onion-сообщений, я должен объявить об этом публично. Если канал не анонсирован, теоретически он не может получать платежи; на практике он может это делать благодаря подсказкам маршрутизации, которые включают информацию для отправителя, чтобы правильно направить луковое сообщение. Фактический граф каналов имеет неизвестный размер именно из-за наличия неанонсированных каналов!
Одна деталь: чтобы избежать спама и обеспечить надежную аутентификацию, мы используем временную цепочку Биткойна. Чтобы узел принял канальное_объявление
, такое объявление должно подтвердить существование открытия канала на таймчейне.
Как можно указать ссылку на канал в таймчейне? Использовать полный аутпоинт канала было бы глупо, потому что для проверки верификатору пришлось бы иметь полную копию информационного набора UTXO, а обычно именно полные узлы обладают всей этой информацией.
Гораздо лучше использовать короткий идентификатор канала. Для создания этой ссылки мы опираемся на положение этого выхода во временной цепочке. Нам нужно только сослаться на определенный блок, указать на транзакцию в нем и затем на конкретный выход, созданный транзакцией. Это и есть короткий идентификатор канала (или scid
).
- channel_update`: Третье и последнее сообщение в протоколе сплетен. Оно используется для обновления информации о платежном канале в сети Lightning, например, таблицы маршрутизации, или для информирования других узлов об изменениях в платежных каналах. Этот тип сообщений позволяет узлам направлять платежи по наиболее эффективным и актуальным каналам.
Мы поняли, что сплетни - это повторяющаяся деятельность, и когда узел загружается в сеть Lightning, он начинает получать сплетни с сообщениями, которые он только что проанализировал; на основе этой информации он начинает строить свой граф каналов. Чем больше сообщений он получает, тем более точной и эффективной становится его карта в поиске оптимального маршрута для отправки платежей.
Существует и другая информация, которую можно хранить: различные реализации Lightning могут связывать другие метаданные, такие как баллы, оценивающие качество узла как пира для маршрутизации платежей.
Поиск пути #
Что это такое и какую проблему мы должны решить с помощью поиска пути?
Платежи на Lightning в основном зависят от поиска пути, соединяющего отправителя с получателем. Этот процесс называется поиском пути, но… он не является частью стандарта BOLT! Он не является его частью, потому что не требует координации или взаимодействия, если маршрутизация определена в BOLT, то поиск и выбор маршрута остается за отправителем платежа в соответствии с алгоритмом используемой им реализации.
Проблема заключается в том, чтобы найти лучший маршрут между отправителем и получателем, оптимальный с точки зрения успешности доставки, низких комиссионных сборов и коротких временных затрат.
Чтобы еще лучше понять проблему транспортировки сатоши между мной и Крисом, необходимо определить 3 атрибута:
- Capacity: это общее (максимальное) количество сатоши, финансируемых в мультисиг-адресе 2-di-2 .
емкость(моего канала с Крисом) = баланс(Мусатц) + баланс(Крис)
Баланс: это сумма, которой располагает каждый пир в канале.
Ликвидность: это доступный баланс, который может быть отправлен. Ликвидность моего канала равна моему балансу, но вычитается из резерва канала и всех HTLC, которые у меня остались.
capacity(musclesatz) = balance(musclesatz) - channel_reserve(musclesatz) - HTLCs(musclesatz)
Единственная публичная ценность для всей сети - это пропускная способность канала. Балансы не объявляются для поддержания высокой масштабируемости и конфиденциальности. Если бы они были публичными, мы бы решили проблему поиска пути с помощью любого алгоритма поиска пути с минимальными затратами.
Нам придется поумнеть, а пока мы можем сказать, что, по крайней мере, уверены, что ликвидность канала находится в пределах (или равна) канального_резерва
(в минимальном случае это нижняя граница), а пропускная способность канала минус канальный_резерв
(верхняя граница)
резерв <= ликвидность <= (емкость - резерв)
Это наш интервал неопределенности, мы не знаем балансов, но у нас есть приблизительная оценка ликвидности, которая должна быть в пределах интервала. Сеть не знает этого интервала, потому что его можем знать только мы с Крисом, но мы можем использовать неудачные HTLC наших попыток оплаты, чтобы обновить нашу оценку ликвидности и снизить неопределенность.
Если HTLC потерпел неудачу, потому что мы думали, что он может принять N сатоши, а оказалось, что он может доставить только M (при M < N), то мы уже можем изменить верхний конец нашего диапазона на M - 1
.
Упрощая, мы используем алгоритм Дейкстры A* (A-star), в котором у нас есть узлы и мы присваиваем вес соединениям между ними. Этот вес - плата за маршрутизацию платежа.
Однако, поскольку ликвидность канала неизвестна отправителю, а является лишь теоретическим диапазоном, проблема является сложной. Мы можем подозревать, какие соединения имеют достаточную пропускную способность, чтобы позволить маршрутизацию платежа. Я могу попытаться отсортировать эти маршруты по весу и пробовать каждый маршрут по порядку, пока платеж не будет успешным. В случае неудачных платежей я использую ошибку HTLC для обновления графа, исправляя ограничения и уменьшая неопределенность, поскольку я исследовал путь.
Итак, подведем итоги:
- Я строю граф канала.
- Я выполняю поиск пути на основе некоторых эвристик.
- Я выполняю попытку оплаты.
- При неудачных платежах я корректирую свой интервал, чтобы уменьшить неопределенность при следующей подаче.
Парадоксально, но я учусь на ошибках, потому что чем больше у меня неудачных платежей, тем лучше я могу уменьшить неопределенность моего интервала и получить более точную карту возможных путей. Знания, полученные в результате ошибок, будут очень полезны для будущих платежей.
Однако будьте осторожны: это не “вечное” знание, потому что оно устаревает по мере того, как другие узлы отправляют или маршрутизируют платежи. Я никогда не увижу ни одного из этих платежей, если только я не являюсь отправителем.
Луковая маршрутизация, как мы уже видели, позволяет мне видеть только один прыжок. Мы также должны подумать о том, как долго хранить эти знания о неопределенности различных маршрутов, прежде чем станет бесполезно держать их в памяти.
Многочастные платежи - это функция, представленная в 2020 году, которая представляет собой стратегию поиска наилучшего пути, но разбивает платеж на множество мелких частей, которые будут отправлены как HTLC по разным путям, очевидно, сохраняя атомарность платежа. Все они должны дойти до получателя, иначе транзакция будет провалена.
Они являются улучшением по очень простой причине: маленьким платежам не нужно подстраиваться под ликвидность каналов, потому что для “маленького кусочка” обязательно найдется достаточно ликвидности. Это как если бы наш интервал неопределенности был стогом сена, а наш небольшой платеж - иголкой.
Конечно, не существует статистической уверенности в том, что все многочастичные платежи проходят.
Безопасность и атаки на LN #
Является ли Lightning частной?
Это зависит от того, что мы понимаем под приватностью.
Узел имеет видимость своего преемника и предшественника. Но если он высокоразвит и гиперсвязан, он может генерировать эвристики и ассоциации, как в случае с очень крупными кошельками без хранилищ. Узлы этих провайдеров отвечают за маршрутизацию очень большого процента платежей в данный узел и из него.
Что произойдет, если два или более узлов окажутся под контролем злонамеренного противника? Если два сговорившихся узла находятся на одном пути платежа, они сразу поймут, что маршрутизируют HTLC для одного и того же платежа, поскольку хэш предварительного изображения совпадает. Каковы риски?
Информационная безопасность чаще всего сводится к трем аспектам:
- Ответственность: действительно ли секретная информация доходит до адресата?
- Целостность: не была ли информация изменена во время транспортировки?
- Доступность: работает ли система или происходит какая-то атака типа “отказ в обслуживании”?
Реальность не только для Lightning, но и для любой другой системы заключается в том, что нет никакой уверенности в том, что злоумышленник не добьется успеха. Это игра с нулевой суммой. Конечно, вы можете снизить риск, но никогда не сможете свести его к нулю.
Есть ли разница между конфиденциальностью Биткойна и Lightning?
На первый взгляд, Lightning предлагает лучшую конфиденциальность. В транзакциях Bitcoin цифровая личность не привязана к конкретному адресу, но верно и то, что транзакции передаются открытым текстом и анализируются. В последние годы появилось множество компаний, которые зарабатывают этим на жизнь.
В Lightning платежи не передаются по всей сети и, главное, не сохраняются навечно, как в timechain, но есть и другие свойства протокола, которые могут вызвать некоторые боли в животе.
Одно из них? У крупных платежей может быть меньше “вариантов” маршрутизации.
Еще? Узлы Lightning сохраняют постоянную идентификацию, в то время как узлы Bitcoin - нет: Я могу получать и отправлять платежи только с того узла, который я использовал для открытия платежного канала на timechain. Я не могу использовать другие. Я также должен объявлять свой IP-адрес и ID, и это создает постоянную связь между ID узлов и IP-адресами. IP-адреса являются промежуточным звеном в атаке на анонимность и часто связаны с физическим местоположением пользователя в реальном мире.
Наконец, пользователям Lightning можно отказать в обслуживании, заблокировав каналы или исчерпав их. Пересылка платежей требует блокировки баланса (дефицитного ресурса) в HTLC по пути следования. Злоумышленник может запустить очень много платежей, не завершив их, и занять капитал на долгое время (но это также зависит от установленной задержки блокировки).
В общем, можно сказать, что с точки зрения конфиденциальности между Bitcoin и Lightning нет победителя, у каждого есть свои слабые и сильные стороны. Что мы можем сказать, так это то, что многие исследовательские группы в настоящее время работают над улучшением аспектов безопасности и конфиденциальности обеих криптовалют.
В последней части этой статьи я расскажу о возможных атаках, которым может подвергнуться или уже подвергся Lightning. Мы почти подошли к концу.
Связь отправителя и получателя
В Lightning анонимность участников необходима для обеспечения свободы платежей - эта концепция является одним из столпов, ради которых был создан Биткойн. Однако злоумышленник может попытаться нарушить эту конфиденциальность, узнав отправителя и получателя платежа. Причин для этого может быть множество. Будет нарушена не только безопасность платежей, но и цензура по отношению к определенным получателям или отправителям.
Мы можем предположить, что существует два типа противников, которые могут попытаться нарушить анонимность в Lightning: противники “вне пути” и противники “на пути”. Противники “вне пути” пытаются вычислить отправителя и получателя платежа, не участвуя непосредственно в процессе маршрутизации платежа, в то время как другие могут использовать информацию, полученную в процессе оплаты, чтобы узнать отправителя и получателя.
Первые, те, что “вне пути”, используют технику, называемую “зондированием” или “прощупыванием”, чтобы узнать индивидуальные балансы в различных платежных каналах. Получив эту информацию, злоумышленник может сравнить снимки сети в моменты времени t1
и t2
, чтобы определить произошедшие платежи и выяснить отправителя, получателя и сумму платежа. Например, он делает снимок сети в момент времени t1 = 21:00 и делает другой снимок в 21:01. Если был только один платеж, то выяснить, кто участвует в нем и какова его сумма, не составит труда, так как существует изменение статуса канала, который должен обновляться каждый раз, когда меняется баланс! Если транзакций было несколько, все становится сложнее, поэтому компании разработали эвристику для разделения различных платежей.
С другой стороны, те, кто находится на маршруте, активно участвуют в маршрутизации платежей и могут наблюдать за информацией о платежах. Их мастерство заключается в том, чтобы исключить определенные узлы из набора анонимности отправителя или получателя, используя информацию, полученную в процессе платежа. Например, узел-посредник может наблюдать за суммой любого маршрутизируемого платежа и различными дельтами временного замка и исключать из набора анонимности любой узел, который был установлен с меньшей емкостью, чем сумма маршрутизируемого платежа.
Отказ в обслуживании
Раскрытие сетевых ресурсов означает увеличение риска их недоступности из-за возможного отказа в обслуживании. Как злоумышленник может отказать мне в обслуживании? Тривиально. Производится бомбардировка бесполезными запросами, неотличимыми от легитимных. Никаких потерь в плане средств нет, и они служат лишь для того, чтобы отправить целевую жертву DoS в оффлайн. Как и Lightning, эта сеть взимает плату за использование публичных ресурсов, но в данном случае каналы являются публичными, а плата - платой за маршрутизацию.
Когда узел отправляет мне платеж, этот узел использует свои данные и пропускную способность для обновления статуса канала, и, помимо прочего, сумма паркуется (HTLC) до тех пор, пока она не будет погашена или пока не истечет срок ее действия (timelock). Платежи, которые не проходят, не влекут за собой никаких расходов, так что это здорово для нас, бедных законных пользователей сети. Для злоумышленников это не так хорошо, поскольку позволяет им бомбардировать сеть запросами на маршрутизацию без каких-либо затрат.
Концептуальным вариантом DoS является атака на основе топологии. Lightning не является идеально децентрализованной, поэтому, чтобы “убрать” узлы из маршрутизации платежей, злоумышленнику не имеет смысла атаковать узлы на краю графа каналов. Гораздо разумнее атаковать средние/крупные узлы, связанные с большим количеством других узлов.
Нарушение канала
Каждый узел может обрабатывать максимум 483 HTLC. При атаке с нарушением канала злоумышленник направляет через целевой канал ровно 483 платежа. Что может произойти?
Похожая версия касается блокировки ликвидности, при которой злоумышленник использует большие HTLC, потребляя всю доступную полосу пропускания канала жертвы. Это, конечно, гораздо дороже, чем отправка 483 HTLC.
Межслойная деанонимизация
Создание и закрытие каналов Lightning происходит на таймчейне, и это облегчает злоумышленникам деанонимизацию пользователя, используя данные как с таймчейна, так и на таймчейне. Какие цели преследуют эти злоумышленники?
Создать кластер биткойн-адресов, принадлежащих одному и тому же пользователю на уровне 1.
- Меры противодействия 1: не используйте повторно выводы финансирования транзакций для открытия новых каналов, избегайте многих эвристик.
- Контрмера 2: никогда не использовать уникальные внешние источники для финансирования, лучше варьировать.
Создайте кластер из узлов Lightning, принадлежащих одному пользователю, на уровне 2.
- Здесь я хочу задать вам вопрос: использование псевдонимов - это здорово, они упрощают жизнь и улучшают юзабилити. Несомненно. Но если я использую в качестве псевдонима свое собственное доменное имя, как вы думаете, это хорошая или плохая идея? Если вы считаете, что это плохая идея, то какие меры следует предпринять?
Создать уникальную связь между узлами Lightning и адресами в timechain.
- Противоположная двум вышеприведенным контрмерам легко позволяет установить межслойную связь между узлом Lightning и сущностью, управляющей адресами на первом уровне.
Можно ли это защитить?
Это нелегко, но сделать это можно.
- Необъявленные каналы, если вы не хотите делать маршрутизацию и не экспортируете.
- Если вы хотите сделать маршрутизацию:
- Установите минимальный размер HTLC, который вы готовы принять, на более высокое значение, чем значение по умолчанию. Таким образом, каждый слот не может быть занят слишком маленьким платежом (во избежание спама).
- Ограничьте максимальное количество слотов, которые может занять пир.
- Отслеживайте частоту ошибок и, если она превышает определенное значение, ограничьте ее.
- Используйте теневые каналы - это параллельные платежные каналы к существующим. Их можно использовать для маршрутизации, но не анонсировать.
- Постарайтесь найти правильный баланс между принятием и отклонением каналов. Когда пир открывает канал на вашем узле, вы не можете знать, будет ли он использован для атаки на ваш узел или нет.
- Избегайте использования одного и того же псевдонима для вашего узла и вашего домена (это ответ на вопрос выше 😜).
- Используйте Tor.
- Продолжайте читать и информировать себя, не полагайтесь только на эту статью.
Пока! #
Надеюсь, вам понравился этот экскурс. Если вы нашли ошибки, неточности или просто хотите улучшить эту статью, вы можете связаться со мной X/Twitter или в telegram.
Если вы считаете, что узнали что-то новое, пожалуйста, поделитесь этой статьей или сделайте пожертвование. Помимо того, что это вознаградит меня лично, это поможет мне поддерживать этот сайт и даст мне стимул для создания нового контента!