Давайте договоримся: я объясню (а кому-то, возможно, освежу в памяти), что собой представляет и как работает Lightning Network, — подробно, но не вдаваясь чрезмерно в технические детали, – но вам понадобится уделить мне где-то с час вашего времени. Это будет долгое и для кого-то, возможно, непростое путешествие, в конце которого, по задумке, вы должны получить внятное представление о Lightning Network и, я надеюсь, некоторое удовольствия от познания.
Навигация:
- Всегда начинаем с основ
- Как работает Lightning Network
- Сердце Lightning
- Безопасность и атаки на LN
По ходу чтения кому-то могут встретиться незнакомые термины, которые я не буду объяснять, — иногда потому, что считаю полагаю знакомство с ними само собой разумеющимся для этого чтения, иногда потому, не могу вдаваться в разъяснение каждого понятия.
При необходимости рекомендую обращаться к Глоссарию БитНовостей либо к другим источникам.
Всегда начинаем с основ
Lightning Network (как и Bitcoin) является trustless системой: она позволяет обмениваться ценностью без необходимости доверять другим участникам этой сети.
Но как выглядит эта система? Где она находится? Как работает?
Представим, что я управляющий пиццерии. Каждый второй день мой друг Борис приходит купить пиццу. Борису нравится платить биткойнами, но использовать основную сеть для мелких быстрых платежей утомительно: она недостаточно масштабируема, поэтому платёж не происходит мгновенно, плюс ещё относительно высокие комиссии.
Мы с Борисом договариваемся и устанавливаем воображаемый канал связи, в котором мы оба помещаем некоторую сумму денег (сатоши) в сейф. В сейфе также находится лист бумаги, на котором точно указано, сколько сатоши внёс я и сколько Борис. Это позволяет Борису заказывать столько пиццы, сколько он пожелает, пока его доля сатоши не будет полностью исчерпана.
Происходящее с таблицей балансов можно описать так: всякий раз, когда Борис покупает пиццу, в неё вносятся изменения — соответствующее количество сатоши списывается со счёта Бориса и зачисляется на мой. Теперь, когда мы с Борисом установили канал связи, любой друг Бориса, имеющий с ним прямой канал, может воспользоваться и нашим новым соединением.
Таким образом, цель Lightning состоит в том, чтобы обеспечить эффективную «маршрутизацию» платежей, используя существующие каналы и находя минимальный путь для их завершения. Маршрутизация — это процесс поиска оптимального пути в какой-либо транспортной системе, будь то физическая или цифровая. Когда вы строите оптимальный маршрут в приложении с картой на своём смартфоне это, понятно, тоже является роутингом.
Пример с пиццей очень прост и ни в коем случае не может претендовать на полное или реалистичное отражение того, как на самом деле работает Lightning, однако это хорошая отправная точка для погружения в тему.
Протокол справедливости
«Протокол справедливости» — это по определению система, в которой обеспечивается равноправие между участниками.
Но как можно претендовать на справедливость, не предполагая доверия в том или ином виде, — без установления, например, правового государства и без доверенных сторон?
Глупый уже на сегодня вопрос. Благодаря использованию теории игр и криптографии!
В криптографических системах мы доверяем протоколу. Протокол — это не что иное, как система с набором правил, и благодаря положительным и отрицательным стимулам в теории игр мы можем претендовать на создание надёжного протокола без необходимости в доверенных посредниках. Справедливость, однако, достигается только в том случае, если правила хорошо прописаны.
Давайте для ясности рассмотрим пример и перенесём это определение в реальный мир. Во время обеда два маленьких брата спорят, потому что не хотят делить друг с другом порцию картошки фри, — классика. Мать, взяв дело в свои руки, авторитарно навязывает свой закон, распределяя порцию между братьями по своему усмотрению. Она может, намеренно или нет, дать больше картошки одному из них. Результат? Система несправедлива; мы имеем форму правового государства, которое принимает решение полностью автономно.
Более разумным подходом может быть установление правил, например: один из двух братьев делит порцию по тарелкам, а другой может первым выбрать тарелку.
Какие последствия возникают из этой новой динамики? Мы ввели положительные и отрицательные стимулы: если ребёнок, ответственный за разделение порций, попытается схитрить, то другой может его наказать, забрав себе бóльшую тарелку.
Примитивы безопасности
Для того чтобы этот пример с детьми работал, он должен опираться на определённые не допускающие исключений базовые принципы, такие как последовательный порядок
действий и намеренная невозможность отказа
от выполнения:
- выбор тарелки не может произойти до распределения порции;
- оба должны обязаться не отказываться от выбора тарелки.
Почему Lightning?
Как упоминалось в примере с пиццерией, Биткойн плохо масштабируется. Но это фича, а не баг. Биткойну не нужно масштабироваться на первом уровне (L1), он и не будет.
Помните, что транзакции записываются в глобальный таймчейн (блокчейн с фиксированными метками времени), и c ростом спроса на транзакции один блок заполняется очень быстро. Остальные транзакции при этом ставятся в очередь. Чтобы обойти эту очередь и сократить ожидание, вы можете заплатить более высокую комиссию, в результате чего комиссии растут для всех без исключения.
Если спрос на транзакции продолжит расти, всё больше и больше транзакций будут откладываться в пул ожидания, делая микротранзакции экономически невыгодными, так как на их выполнение будет требоваться сатоши больше, сама сумма транзакции.
Чтобы решить эту «проблему» — которая на самом деле таковой не является, — один «гений» решил, что лучше всего будет увеличить размер блока, но… однако мы уже писали об этом эпизоде в Истории «войны» за размер блока.
Как мы можем обеспечить масштабируемые офчейн транзакции, не теряя при этом в безопасности первого уровня (основного блокчейна) Биткойна?
В феврале 2015 Joseph Poon и Thaddeus Dryja представили возможное решение этого вопроса, опубликовав — устаревшую на сегодняшний день — уайтпейпер «The Bitcoin Lightning Network: Scalable Off-Chain Instant Payments» (PDF).
Концепция Lightning Network предполагает появление новой сети, технически называемой вторым уровнем (L2), в рамках которой пользователи могут осуществлять платежи в режиме peer-to-peer без необходимости регистрировать транзакции в таймчейне. Единственные моменты, когда используется таймчейн, — это для открытия коммуникационного канала и для расчёта по этому каналу, то есть для соглашения о выводе биткойнов из Lightning и возвращении их на L1.
Помимо очевидного снижения нагрузки на узлы Биткойна, платежи через Lightning обеспечивают очень низкие комиссии за транзакции и повышенную конфиденциальность (впрочем, только отчасти, как мы ещё увидим), поскольку эти платежи, в отличие от стандартных транзакций, не видны всей L1 сети и внешним наблюдателям за нею.
Функции, отмеченные в уайтпейпер
Я ожидаю, что мы рассмотрим каждый из этих пунктов в следующих нескольких главах. Сейчас это может показаться неопределённым списком, но мне нужно сначала представить концепции, а позже уже подробно их описать.
- Маршрутизация платежей с очень низкой стоимостью и в реальном времени.
- Обмен ценностью без ожидания подтверждения канонического блока, как в L1.
- Платежи являются окончательными, необратимыми и могут быть возвращены только получателем.
- Платежи не видны всей сети.
- История транзакций не хранится постоянно, как в L1.
- Lightning использует концепцию луковой маршрутизации (
onion routing
), поэтому промежуточные узлы, участвующие в передаче платежа, знают только предыдущий и следующий узел, не имея информации об отправителе и получателе. - BTC в Lightning являются настоящими биткойнами; это обеспечивает сохранение ценности и полный контроль баланса, как и в случае с биткойнами в основной сети.
Как работает Lightning Network
Всё, игры кончились. Кто устал, лучше слегка разомнитесь и сделайте себе кофе, потому что вы вот-вот откроете для себя чудеса Lightning Network.
Базовые технические понятия
- Цифровые подписи: это метод, предназначенный для подтверждения подлинности цифрового документа.
Объясню на примере: вам нужно написать некое сообщение близкому другу, и вы хотите быть уверенным, что он знает, что именно вы являетесь автором этого письма и что письмо дошло без искажений. У вас есть волшебный ключ, который позволяет вам подписать письмо математическим способом, доказывая, что только вы могли его написать. Используя этот волшебный ключ, вы создаёте цифровую подпись.
У вашего друга есть другой волшебный ключ, производный от вашего, но он может использовать его только для проверки вашей подписи; он не может создавать подпись от вашего имени.
Это и есть цифровая подпись. А «волшебный ключ» здесь — это не что иное, как секретный ключ, который позволяет вам тратить средства, доказывая всей сети ваше право владения ими в данный конкретный момент.
- Цифровой ключ: состоит из ряда символов — цифр и букв, — который можно использовать как для шифрования, так и для расшифровки данных.
Например: у вас есть особый и уникальный ключ, который может открыть только определённый сейф. Этот цифровой ключ очень мощный, потому что он также позволяет вам подписывать документы или получать доступ к защищённым ресурсам. Им должны обладать только вы, и когда вы его используете, для всех остальных это является доказательством, что вы действительно тот, за кого себя выдаёте.
Этот цифровой ключ состоит из двух частей: открытой и закрытой. Открытую часть (открытый ключ) вы можете использовать публично; она подтверждает, что вы — это вы, но ничего не говорит о том, как это использовать, и не работает для открытия сейфа. Закрытая часть (секретный ключ), напротив, является важным секретом, который вы необходимо хранить в тайне.
Подробнее о криптосистемах с открытым ключом
- Хеш: это математическая функция, которая преобразует данные переменного размера в строку фиксированной длины.
Например: представьте, что вам нужно преобразовать кулинарный рецепт с очень точными дозировками в уникальное число. Существует математическая функция, которая преобразует нечто в число, контрольную сумму (fingerprint).
Если изменить дозировку любого из ингредиентов в рецепте хотя бы на один грамм и снова применить хеш-функцию, мы получим уже совершенно другое число. То есть, если кто-то изменит ваш рецепт, такая проверка сразу вам на это безошибочно укажет. Итак, хеширование — это способ обеспечить целостность и безопасность данных в цифровом мире.
Желающие могут легко опробовать это сами в терминале:
# следующий запрос для строки "some random string"
$ echo -n "some random string" | shasum -a 256
# даст такой результат:
4342e08d01f24ac451bc3bbff686408563b5c26d386d0c18879244f5e7974d7d
#теперь можно повторить запрос, произвольным образом изменив строку, и посмотреть, как изменится вывод
- Биткойн-транзакция: это структура данных, которая кодирует передачу ценности между различными участниками сети.
Транзакция расходует входы (inputs) и создаёт выходы (outputs). Входы транзакции представляют собой ссылки на выходы предыдущих транзакций, и каждая новая транзакция, в свою очередь, генерирует новые выходы.

Подробнее о биткойн-транзакциях
Узлы сети Bitcoin отслеживают все доступные и расходуемые выходы. Именно поэтому они называются «неизрасходованными выходами транзакций» или сокращённо UTXO
(от англ. unspent transaction outputs
). Совокупность всех UTXO
в сети образует UTXO-set
, который сокращается или увеличивается по мере использования старых и создания новых UTXO.
Создаваемые выходы представляют собой дискретные и неделимые единицы ценности. Это означает, что непотраченный выход должен использоваться целиком.
То есть вы хотите сказать, что, если мне нужно заплатить 10 тысяч сатоши, а у меня есть UTXO в 3 BTC, то я должен потратить всю сумму?
– Именно.
Хорошая новость заключается в том, что вы используете UTXO целиком в качестве входа, но получите два выхода:
- один оплачивает 10 тысяч сатоши получателю,
- другой возвращает вам разницу (за вычетом комиссии за транзакцию) в виде сдачи.
Неприятно это признавать, но процесс работает в сущности ровно так же, как при использовании фиатных наличных: если вам нужно заплатить 100 рублей за леденец, а у вас есть только тысячная купюра, то вам придётся потратить её целиком.
Единственной транзакцией, не имеющей входа, является coinbase-транзакция — особая транзакция, используемая майнерами для поддержания всего процесса майнинга.
Подробнее о coinbase-транзакциях
У каждой транзакции есть идентификатор, называемый transaction ID или, коротко, TxID
. TxID
создаётся с помощью хеш-функции на основе данных транзакции. Для обозначения конкретного выхода TxID, чтобы указать, на какой именно вход мы ссылаемся, мы используем outpoints
(выходные точки) — простые числа, помещаемые в конце TxID после двоеточия (:).
И завершает наш обзор основных понятий…
- Bitcoin Script: язык сценариев, используемый в Биткойне для определения условий, при которых средства высвобождаются в транзакции. Другими словами, он устанавливает правила, которые должны быть соблюдены для того, чтобы средства в транзакции могли быть потрачены.
Скрипты Биткойна состоят из двух частей:
- блокирующие: встроены в выходы транзакций и устанавливают условия, необходимые для расходования выхода;
- разблокирующие: встроены во входы и выполняют условия, установленные блокирующим скриптом.
Приведём наглядный пример. Если бы блокирующий скрипт нашего выхода выглядел так:
3 + x = 5
…то, как несложно догадаться, мы могли бы его потратить, используя во входе своей транзакции разблокирующий скрипт 2.
Любой, кто проверяет эту транзакцию, объединит наш разблокирующий скрипт (2) со скриптом блокировки (3 + x = 5), получив утвердительный ответ, позволяющий потратить выход.
Разумеется, в реальных скриптах не используется элементарная арифметика. На практике для расхода выхода требуется продемонстрировать знание определённого секрета, что возвращает нас к концепции цифрового ключа.
Дополнительно о Bitcoin Script
Теперь, когда мы разобрались со скриптами блокировки и разблокировки, давайте вернёмся к нашему примеру с пиццей, на этот раз несколько приблизившись к тому, как это работает на самом деле:
- Борис платит мне 10 тысяч сат. за покупку пиццы.
- По простейшему блокирующему скрипту, для разблокировки этих средств требуется предоставить мою подпись.
- Скрипт выглядит примерно так:
<подпись> <публичный ключ> CHECKSIG
.
CHECKSIG
принимает два элемента: подпись и открытый ключ, и проверяет, что они принадлежат мне. Открытый ключ уже содержится в скрипте блокировки, а недостающим элементом является подпись, соответствующая этому открытому ключу. Секретным ключом владею (или должен владеть) только я, и только я могу сгенерировать действительную подпись, которая позволит мне потратить эти сатоши.
Я должен предоставить скрипт разблокировки, содержащий мою цифровую подпись. Результатом этой операции будет TRUE
!
&<user-n signature> <user-n pubkey> CHECKSIG
Конечно, существуют и другие, гораздо более сложные типы скриптов. Здесь можно взглянуть на несколько примеров.
Что такое платёжный канал
Lightning Network — это peer-to-peer сеть платёжных каналов, реализованных в виде смарт-контрактов (не подумайте чего плохого) на таймчейне Биткойна. Однако такое определение было бы неполным, поскольку это также коммуникационный протокол, определяющий то, как участники этой сети выполняют эти смарт-контракты.
Платёжный канал представляет собой связь между двумя узлами в сети Lightning. Эта связь позволяет установить баланс (в миллисатоши) между этими двумя узлами.
Lightning-узел — это программа, способная взаимодействовать по протоколу LN. Она обладает тремя основными характеристиками:
- Это кошельки, отправляющие и получающие платежи через сеть Lightning.
- Они должны взаимодействовать с другими узлами в рамках однорангового (peer-to-peer) протокола.
- Они должны иметь доступ к таймчейну для защиты средств, используемых для платежей.
Канал связи между узлами защищён криптографическим протоколом, который гарантирует справедливость средствами криптографии, что делает его де-факто системой справедливости. Этот протокол устанавливается, когда оба участника вносят средства в общий фонд на multisig-адрес 2-из-2. Схема многосторонней подписи 2-из-2 подразумевает, что обе стороны должны прийти к согласию.
В multisig-адресе 2-из-2 каждый участник владеет одним закрытым ключом. Это означает, что ни один из участников не может авторизовать транзакцию самостоятельно; для выполнения транзакции необходимо наличие и проверка обеих подписей.
Мы с Борисом можем обмениваться серией приватных транзакций, которые расходуют этот баланс, но не публикуем их в таймчейне. Последняя транзакция между нами представляет текущее состояние канала и определяет распределение этого баланса между мной и Борисом.
Выполнение транзакции в Lightning эквивалентно перемещению части баланса в мою пользу, если платят мне, или в пользу Бориса, если я должен заплатить ему.

Любое перемещение средств в ту или иную сторону обрабатывается смарт-контрактом, настроенным на наказание участника канала, если тот попытается отправить устаревший статус, который принадлежит прошлому и, следовательно, больше не валиден.
Переадресация платежей
Когда между несколькими участниками сети установлено несколько платёжных каналов, платежи могут быть переадресованы (помните о маршрутизации?) из одного канала в другой, фактически определяя путь в пространстве сети, соединяющий несколько каналов.
Мы уже говорили, что каналы создаются с использованием multisig-адресов, но не упоминали о том, что транзакции обновления баланса канала представляют собой предварительно подписанные биткойн-транзакции. Это означает, что всё доверие, необходимое для работы Lightning Network, — это доверие непосредственно к базовой сети: самому Биткойну.
В чём суть?
Lightning — это приложение, работающее «поверх» Биткойна и использующее биткойн-транзакции и его скриптовый язык. Это творческий и умный способ обеспечить неограниченное количество мгновенных платежей с очень низкими комиссиями, не подразумевающий никакого доверия ни к кому, кроме самой сети Биткойна.

Возвращаясь к теме платёжных каналов, давайте обсудим их возможные ограничения:
- Время, необходимое для передачи нескольких сотен байт по интернету (пренебрежимо мало).
- Ёмкость канала, то есть количество биткойнов, зарезервированных при открытии канала.
- Верхний предел размера транзакции в сети Bitcoin: поскольку каждый платёж в Lightning обеспечивается биткойн-транзакцией, ещё не завершённой, размер блока влияет на количество платежей, которые могут быть одновременно активны в одном платёжном канале.
Транзакция финансирования
Основополагающим элементом канала, как мы уже говорили, является адрес с multisig (многосторонней подписью) 2-из-2. Один из участников платёжного канала может его профинансировать, отправив сатоши на этот multisig-адрес. Такая транзакция называется транзакцией финансирования и не отличается в таймчейне от любой другой биткойн-транзакции. Узнать, что это был канал Lightning, можно только в момент выполнения транзакции закрытия канала.
Сумма, внесённая транзакцией финансирования, называется ёмкостью (capacity
) канала и определяет максимальную сумму, которую можно по нему отправить.
Внимание: ёмкость канала не определяет верхний предел объёма транзакций, которые могут пройти через канал, поскольку, напоминаю, средства могут пересылаться в обоих направлениях.
Транзакция возврата
Предположим, я создаю платёжный канал с Борисом. Однако Борис оказывается недобросовестным пиром. Он как-то побудил меня внести средства на multisig-адрес 2-из-2, но теперь отказывается сотрудничать и подписывать свою транзакцию.
Как мне разблокировать внесённые в канал средства?
Чтобы избежать подобных проблем, мне необходимо заранее создать транзакцию возврата, которая тратит средства с multisig-адреса, возвращая мои сатоши. Эту транзакцию возврата нужно подписать до отправки транзакции финансирования на адрес, то есть до фактического открытия канала.
Теперь я защищён!
Транзакция возврата является одной из транзакций, относящихся к классу commitment-транзакций («транзакций-обязательств», фиксирующих состояние).
Commitment-транзакция
Эта транзакция представляет собой соглашение между участниками канала, автоматически выплачивающее каждому из участников его баланс, обеспечивая тем самым отсутствие необходимости доверять друг другу.
Подписывая commitment-транзакцию, вы фиксируете баланс в канале в данный конкретный момент. Если я захочу вернуть свои средства из канала, то могу сделать это в любое время именно благодаря подписанию этого контракта.
Кстати, всякий раз, когда баланс канала ‘изменяется’, создаются новые транзакции-обязательства, обновляющие текущее состояние канала и распределяющие баланс между тем, что причитается мне, и тем, что причитается другому участнику.
Партнёр по каналу исчез и перестал подавать признаки жизни? Нет проблем. Отказывается сотрудничать? Нет проблем. Пытается меня обмануть? Тоже не проблема.
Пока что ответ «нет проблем» может показаться слишком туманным и бессмысленным, но позже мы разберём это подробно.
Можно ли смошенничать?
Вернёмся к моему другу Борису. Я открываю с ним канал, внося депозит в 100 тысяч сатоши на адрес с multisig 2-из-2. Мы обмениваемся подписями, и я передаю транзакцию в таймчейн.
Мы говорили, что «фиксирующие» commitment-транзакции создаются при каждом изменении баланса канала. Так что, если я, предположим, отправляю Борису 30 тыс. сатоши, то новая commitment-транзакция будет предусматривать выплату 70 тыс. сатоши мне и 30 тыс. Борису.
Но теперь у меня есть две commitment-транзакции: первая, определяющая состояние балансов в момент времени t0
, со 100 тысячами сатоши, и вторая: в момент t1
, отражающая текущее состояние балансов, с 70 тысячами.
Здесь у меня возникает нездоровая идея: а что, если я опубликую свою предыдущую commitment-транзакцию на 100 тысяч сатоши? Означает ли это, что контракт выплатит мне 100 тысяч?
В Биткойне на уровне протокола нет цензуры и ничто не мешает мне опубликовать старое состояние балансов, уже не действительное. Ничто, кроме шифрования, конечно.
Чтобы предотвратить такого рода кражу, commitment-транзакции строятся таким образом, что, если я передам в сеть (намеренно или нет) транзакцию с устаревшим состоянием балансов, то могу быть наказан.
Это антистимул в теории игр, как в примере с младшими братьями и тарелкой чипсов. И мой антистимул к обману партнёра по каналу очень высок, потому что в момент, когда я опубликую устаревший статус канала, Борис может эту транзакцию оспорить и через это претендовать на весь депонированный баланс адреса.
Если бы у меня было очень мало сатоши, стимул для обмана был бы больше, ведь я рискую всего несколькими сатоши. Это не было бы болезненно. Чтобы решить эту проблему, Lightning Network требует, чтобы каждый участник имел минимальный баланс в канале — т.н. шкуру в игре (skin in the game).
Но то, как работают штрафы, мы проанализируем позже, потому что мне придётся ввести понятия таймлока и секрета для отзыва (revocation secret).
Объявим канал
Как мне уведомить всю сеть о существовании моего нового платёжного канала? Как сделать его публичным?
Существует группа протоколов под названием gossip (англ. сплетни). В LN такой протокол используется для сообщения другим узлам о существовании, ёмкости и комиссиях моего канала.
Плюс объявления публичного канала в том, что он становится доступным для маршрутизации платежей между другими узлами, что даже может принести вам какие-то комиссии. С другой стороны, необъявленный, непубличный канал обладает определённой приватностью — по крайней мере до тех пор, пока не будет закрыт транзакцией в блокчейне Биткойна.
Кстати о закрытии: когда, по-вашему, стоит закрывать канал в Lightning Network?

— Нет, каналы обычно лучше вообще не закрывать.
Закрытие каналов подразумевает проведение транзакции в основной сети, комиссии, раскрытие факта существования канала, но главное… в этом нет никакого особого смысла, за исключением некоторых вполне конкретных причин.
Сохранять каналы открытыми полезно также потому, что, даже когда исчерпывается возможность отправлять средства, я всё ещё могу получать входящие транзакции благодаря «ребалансировке».
Канал может быть закрыт тремя разными способами:
- Согласованное закрытие, самый правильный способ:
- Моя LN-нода информирует LN-ноду партнёра по каналу о моём намерении закрыть канал.
- Обе ноды работают над закрытием.
- Новые попытки маршрутизации чужих транзакций не принимаются, текущие завершаются.
- Ноды готовятся к транзакции закрытия: последнее состояние кодируется для распределения баланса между двумя участниками.
- Достигается соглашение о разделении комиссий за закрытие в основной сети.
- Каждый участник канала получает свою долю оставшегося баланса.
- Принудительное закрытие, неоптимальный способ:
- Я пытаюсь закрыть канал без согласия другого участника.
- Публикую последнюю «фиксирующую» commitment-транзакцию моего узла.
- После подтверждения появятся два расходуемых выхода: один мой, другой партнёра.
Здесь всё становится заметно сложнее, поскольку, если я принудительно закрыл канал, то мой выход будет иметь блокировку по времени (timelock), и я не смогу потратить эти биткойны до наступления определённого момента в будущем (обычно это около двух недель, измеряемые в высоте блоков в таймчейне).
- С нарушением протокола, очень плохой способ:
Нарушение протокола происходит, когда я пытаюсь обмануть партнёра по каналу, публикуя commitment-транзакцию, представляющую устаревшее состояние канала. Чтобы заметить эту попытку обмана, пир должен быть онлайн и отслеживать новые блоки в таймчейне и транзакции в них.
Хотя я опубликовал выгодное мне устаревшее состояние балансов, таймлок не позволяет мне потратить эти биткойны, так что у партнёра по каналу есть врем предпринять необходимые действия для того, чтобы защитить свои средства и наказать меня. В этом случае пир может вывести всю зарезервированную для канала сумму (понятно, почему попытка обмана невыгодна?), и закрытие происходит очень быстро, потому что нет необходимости в закрывающей сделке.
Чтобы закончить на позитивной ноте, пир заинтересован в том, чтобы наказывающая меня транзакция была принята в блок как можно скорее, поэтому он готов заплатить максимальные комиссии, в том числе из моей части баланса 😁.
Но что, если срок таймлока истёк, а пир не заметил, что я опубликовал некорректное состояние?
К сожалению, он потеряет средства – либо полностью, либо в соответствии с последним статусом обязательства, которое мне удалось опубликовать и завершить, превысив срок таймлока.
Как мне обнаружить нарушение протокола в отношении себя?
С помощью правильно управляемой Lightning-ноды, работающей 24/7, или с помощью личной или сторонней службы watchtower
.
Watchtower (дословно «наблюдательная вышка») — это служба безопасности, которая позволяет отслеживать канал, когда пользователь находится офлайн. Если обнаруживается подозрительная активность, то такой сервис может вмешаться для защиты средств.
Счёт (инвойс)
Большинство платежей в Lightning Network начинаются с выставления счёта получателем платежа. Этот счёт содержит в себе всю необходимую информацию для осуществления платежа:
- хеш платежа;
- получатель;
- фактическая сумма и, возможно, дополнительное описание.
Хеш платежа
создаётся получателем платежа путём выбора — безопасным и непредсказуемым образом — псевдослучайных данных, которые затем подаются в хеш-функцию. Эти случайные данные мы называем прообразом
.
H = SHA-256(Прообраз)
Таким образом, мы получаем хеш платежа. Из свойств хеш-функций мы знаем, что их невозможно обратить или подобрать, поэтому никто не может определить прообраз только по результату хеширования. Прообраз является секретом, и после его раскрытия любой, у кого есть хеш, может проверить, что именно этот прообраз был исходным секретом.

Суть в том, что хеш платежа позволяет платежу атомарно проходить по нескольким каналам: он либо полностью достигает пункта назначения, либо не проходит вовсе. Промежуточных вариантов не бывает.
Счета обычно отправляются вне LN по любому доступному каналу коммуникации. Очень популярны QR-коды благодаря своему удобству и компактности: QR-код содержит всю информацию, о которой говорилось выше.
Ещё немного об инвойсах:
- У них есть срок действия, чтобы получателю не приходилось хранить все прообразы. Когда инвойс оплачен или срок его действия истёк, его можно удалить.
- Они могут содержать «подсказки» для маршрутизации, позволяющие отправителю использовать необъявленные каналы для построения пути к получателю (теневые каналы, мы ещё обсудим их позже).
Поиск пути и маршрутизация
Эти два термина часто путают:
- Поиск пути (pathfinding) заключается в нахождении наилучшего маршрута от источника к месту назначения.
- Использование этого маршрута называется маршрутизацией (routing).
Lightning использует source-based
протокол для поиска пути и onion-routed
протокол для маршрутизации.
Стандартный способ поиска маршрута заключается в итеративном тесте различных путей, пока не будет найден маршрут с достаточной ликвидностью для пересылки платежей. Это может быть не самый оптимальный метод с точки зрения минимизации комиссий за маршрутизацию, но в целом он работает вполне удовлетворительно.
Было бы замечательно (хотя это ещё как сказать), если бы у нас были точные балансы каждого канала, потому что тогда задачу поиска маршрута мог бы решить любой студент университетского курса по исследованию операций. Однако это не так: балансы каналов не известны и не могут быть известны участникам сети.
Обращаясь на мгновение к маршрутизации, Lightning Network во многом вдохновлена знаменитой сетью Tor. Впрочем, протокол не копирует Tor в точности, заимствуя лишь концепцию. В Lightning протокол маршрутизации называется Sphinx и к нему применима та же самая аналогия с луковицей, что и к Tor: отправитель строит всю луковицу от сердцевины до самого внешнего слоя.
Платёжная информация для получателя зашифрована ключом, который может расшифровать только он; эта информация является ядром всей операции маршрутизации. В главе о маршрутизации мы шаг за шагом проанализируем построение луковицы, в том числе с криптографической точки зрения.
Чтобы дать общее представление, платёж строится подобно луковице, начиная с получателя и добавляя слой за слоем, двигаясь в обратном направлении — от получателя к отправителю. Первым слоем луковицы, внешним, будет пир отправителя по LN-каналу, который получит луковичный пакет для дальнейшей передачи по сети.
При обработке этого луковичного платежа каждый узел на пути будет знать только о том узле, от которого он его непосредственно получил, и о том, которому он платёж передаст, не имея представления об отправителе или получателе.
Небольшое уточнение: может создаться представление, что пакет действительно очищается как луковица.
В действительности, когда каждый узел считывает часть, за которую он отвечает, он также добавляет криптографический «наполнитель», чтобы вернуть размер сообщения к исходному размеру, предусмотренному отправителем (1300 байт). Эта небольшая хитрость применяется для защиты конфиденциальности, предотвращая возможность промежуточных узлов делать выводы о длине пути или количестве задействованных узлов, что повышает общую безопасность.
Эти маленькие «луковицы» (маленькие, потому что они помещаются в один TCP/IP-пакет) сконструированы таким образом, чтобы иметь одинаковую длину на протяжении всего маршрута передачи.
Луковичная передача платежа
Мне нужно переслать луковицу. Я наконец нашёл пальпируемый маршрут.
Я пересылаю сообщение своему пиру по каналу, и мы говорим, что каждый узел обрабатывает один слой «луковицы». По сути, каждый узел получает lightning-сообщение под названием update_add_htlc
с хешем платежа и «луковицей». Затем вступает в действие алгоритм пересылки платежа, который выполняет следующие операции:
- Расшифровывает внешний слой и проверяет целостность сообщения.
- Подтверждает, что он может удовлетворить предложения по маршрутизации на основе комиссий и исходящей ёмкости.
- Обновляет статус во входящем канале.
- Добавляет заполняющие данные, чтобы сохранить постоянную длину «луковицы».
- В свою очередь, маршрутизирует «луковицу» по своему исходящему платёжному каналу, отправляя
update_add_htlc
, который включает тот же хеш платежа и «луковицу». - Работает со своим партнёром по каналу для обновления статуса канала.
Но что происходит, если в этом процессе возникает общая ошибка?
В таком случае коммуникация распространяется в обратном направлении к отправителю с сообщением об ошибке update_fail_htlc
. Каждый узел, участвующий в маршрутизации, также видит это сообщение.
Если у вас возникают вопросы вроде: что такое HTLC? Почему каждый узел должен знать о сбое платежа и получать это сообщение об ошибке? Скоро вы получите все ответы, которые ищете.
Бэкап канала
Я думаю, более или менее все имеют какое-то представление о BIP-39 для Биткойна, позволяющем восстанавливать состояние кошелька с помощью мнемонической фразы. Для тех, кто не помнит: предложение BIP-39 позволяет генерировать последовательность английских слов из общедоступного списка, которая служит seed-значением для создания детерминированного кошелька с практически бесконечным списком открытых и секретных ключей.
Но как сделать резервную копию канала в Lightning?
Кошельки для Lightning также используют бэкапы через мнемонические фразы BIP-39, но только для части, связанной со взаимодействием с таймчейном. Это важно понимать.
Для каналов требуется дополнительный уровень резервного копирования. Такой бэкап называется Static Channel Backup
(SCB) и вступает в действие при каждом изменении состояния канала. Тут у вас могут возникнуть резонные вопросы, потому что, если по несчастливой случайности будет восстановлена старая (уже отозванная) commitment-транзакция, то наш пир по каналу может подумать, что мы пытаемся его обмануть, запросить штрафную транзакцию и забрать весь баланс канала.
Ещё один аспект, который следует учитывать: SCB бэкапы должны быть зашифрованы для обеспечения высокого уровня конфиденциальности и безопасности. Если я потеряю незашифрованную резервную копию, любой сможет использовать её не только для просмотра моих платёжных каналов, но и для их закрытия с целью передачи баланса другой стороне канала.
Очистка (свип)
Что делать, если баланс в моём lightning-кошельке становится слишком большим, и я хочу снизить риск?
Я могу выполнить очистку (sweep) различных типов:
on-chain | off-chain | submarine swap |
---|---|---|
Перемещение средств из LN в биткойн-кошелёк путём совместного закрытия канала, как уже рассматривалось выше | Этот режим предполагает запуск второго, необъявленного LN-узла в сети. Я использую это как копилку, регулярно перемещая средства на этот «скрытый» узел (который, не надо забывать, всё равно является горячим кошельком) | Это обмен между биткойнами ончейн и офчейн. Они атомарные, то есть если я инициирую submarine своп, отправив баланс LN-канала, другая сторона в ответ отправит мне биткойны ончейн. |
Подводный своп (submarine swap)
Я думаю, с понятием «подводный своп» (submarine swap) стоит разобраться, так как оно часто вызывает много путаницы.
Предыстория:
- У меня есть биткойны ончейн и я хочу получить средства в LN (офчейн).
- У Бориса есть средства в LN (офчейн), и он хочет получить биткойны ончейн.
- Марк — это некий посредник между мной и Борисом, координирующий обмен.
- Борис генерирует прообраз (секрет) и хеширует его, встраивая этот хеш в счёт (инвойс) для меня.
- Я выполняю ончейн-транзакцию на адрес контракта с условием, что для получения этих биткойнов необходимо предоставить прообраз хеша из инвойса Бориса и действительную подпись от Марка, чтобы гарантировать, что никто, кроме Марка, не сможет их забрать.
- Марк видит этот контракт и понимает, что для получения этих биткойнов он должен заплатить Борису; только тогда Борис раскроет ему прообраз.
- Борис получает оплату от Марка, после чего раскрывает ему прообраз.
- Марк использует этот прообраз и валидную подпись, чтобы перевести биткойны на адрес, который он контролирует.
- Я удовлетворён тем, что Борис получил оплату, потому что Марк смог переместить средства в контракте, значит, он знал прообраз, который ему сообщил Борис, получив оплату.
Если бы Борис не получил оплату от Марка, Марк не смог бы получить биткойны из контракта. Благодаря особому условию в этом контракте (который, кстати, называется Hashed Timelock Contract
, или HTLC, мы к ним ещё вернёмся) по истечении определённого времени я тоже смог бы вернуть из него свои биткойны по истечении срока таймлока.
А вот так всё, что я здесь изложил, описывается средствами Bitcoin Script:
OP_SIZE 32 OP_EQUAL
OP_IF
OP_HASH160 <ripemd160(swapHash)> OP_EQUALVERIFY
<receiverHtlcKey>
OP_ELSE
OP_DROP
<cltv timeout> OP_CHECKLOCKTIMEVERIFY OP_DROP
<senderHtlcKey>
OP_ENDIF
OP_CHECKSIG
Несколько сравнений с Биткойном
Lightning Network построена на основе Биткойна, и это хорошо. Она наследует некоторые его функции и свойства, но есть и важные отличия:
- Адрес и инвойс: биткойн-адрес можно использовать бесконечное количество раз (хотя крайне не рекомендуется с точки зрения приватности). Инвойсы в LN используются однократно, для конкретной суммы (есть и исключение: механизм
keysend
, о нём позже).

- Выбор UTXO и поиск пути: для платежа в Биткойне необходимо потратить по меньшей мере один UTXO, тогда как в Lightning Network платежи не требуют использования выходов, поскольку, как мы видели, представляют собой по сути ребалансировку средств, находящихся в контракте с многосторонней подписью.
- Комиссии за майнинг и маршрутизацию: в Биткойне мы платим комиссии майнерам за включение нашей транзакции в блокчейн; в LN пользователи платят комиссии другим участникам сети за маршрутизацию платежей через каналы. Эта комиссия состоит из фиксированной
base fee
, самостоятельно определяемой каждым каналом, и переменнойfee rate
, пропорциональной сумме платежа (это ещё одно отличие: в таймчейне комиссия не пропорциональна сумме транзакции). - Публичные биткойн-транзакции и приватные платежи в Lightning: важный аспект: таймчейн публичен, lightning-платежи — нет.
- Сатоши и миллисатоши: в таймчейне наименьшей единицей является сатоши; в LN мы оперируем также тысячными долями сатоши. При закрытии lightning-канала миллисатоши округляются до ближайшего целого сатоши.
Сердце Lightning
Lightning представляет собой комплексный набор протоколов, функционирующих в интернете. Их можно классифицировать на пять различных уровней, где каждый последующий уровень использует и абстрагирует нижележащий.
Все разделы этой главы были намеренно упрощены путём удаления математических выкладок и чрезмерно технических деталей.

Слой |
---|
Network connection: сетевое соединение; определяет протоколы, обеспечивающие взаимодействие в сети |
Messaging: обмен сообщениями; определяет протоколы, полезные для форматирования или кодирования сообщений |
Peer-to-Peer: определяет протоколы коммуникации между узлами LN |
Routing: маршрутизация; определяет протоколы обнаружения путей и маршрутизации сообщений |
Payment: платежи; определяет интерфейс оплаты счётов (инвойсов) |
Платёжные каналы
Справочная таблица: peer-to-peer уровень, а также автомат открытия, закрытия и состояний канала.
Чтобы понять концепцию работы платёжных каналов, важно задать себе вопрос:
Что значит владеть биткойнами?
Владеть биткойнами значит обладать секретным ключом от биткойн-адреса, имеющего хотя бы один UTXO. Этот ключ позволяет подписывать транзакции и подтверждает факт владения соответствующим балансом, поскольку никто другой не может распоряжаться моим секретным ключом.
Однако владение не всегда сосредоточено в руках одного человека. Биткойн поддерживает также адреса с многосторонней подписью (multisig), где для подписания транзакции требуются несколько секретных ключей. Простая для понимания схема «мультиподписи» — 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 году, стало возможным ссылаться на выходы транзакций, используя хеш транзакции вместо идентификатора выхода. Это позволяет связывать в цепочку транзакции, которые ещё не были переданы в сеть.
Да, звучит как чушь, но это не так. Позвольте мне объяснить.
Что я имею в виду, это то, что транзакция возврата считается действительной при условии её передачи в таймчейн и наличия в её входах подписей моей и Бориса. Даже если мой узел ещё не передал финансирующую транзакцию, я могу заблаговременно создать возвратную транзакцию, вычислив хеш финансирующей транзакции и указав его в качестве входов транзакции возврата. Я заранее знаю, что эта ссылка будет действительной, поскольку уже рассчитал её хеш.
Теперь канал установлен, однако вся ликвидность сосредоточена на моей стороне. Это означает, что я могу отправлять сатоши Борису, но у Бориса нет средств в канале для отправки мне. При отправке части моих сатоши статус канала изменится, и будет создана «фиксирующая» commitment-транзакция. В результате балансы канала обновятся.
История всех commitment-транзакций не должна вводить в заблуждение и наводить на мысль о возможности двойной траты. Только одна из этих транзакций может быть подтверждена в таймчейне. И мы полагаемся на способность Биткойна предотвращать двойное расходование.
Предположим, что мы с Борисом начали обмениваться сатоши, в результате чего было сгенерировано множество commitment-транзакций. В итоге мы пришли к такому балансу в канале:
При закрытии канала посредством передачи и подтверждения актуальной commitment-транзакции, я не смогу использовать свой баланс в течение 400 блоков, в то время как Борис получит возможность распоряжаться своими средствами немедленно. Разумеется, в обратной ситуации будет применяться аналогичное правило.
Для чего нужен этот таймлок, какова цель этой блокировки?
Мы уже упоминали об этом ранее. Эта функция позволяет Борису применить штрафные санкции в случае, если я попытаюсь передать устаревшую commitment-транзакцию с целью присвоения его сатоши. Этот таймлок согласовывается сторонами при создании канала.
Но что произойдёт, если я опубликую устаревшее состояние канала, и как меня могут наказать?
При каждом обновлении состояния канала новым коммитом мой контрагент предоставляет мне криптографический revocation secret
(секрет отзыва), связанный с предыдущим состоянием. В случае моей попытки мошенничества Борис может использовать секрет отзыва из предыдущего состояния для получения криптографического доказательства его отзыва и демонстрации того, что я нарушаю правила, пытаясь закрыть канал с недействительным состоянием.
Возможность для Бориса меня наказать основана на секрете отзыва (revocation secret
), который даёт ему математическое доказательство моего обмана.
Однако я не хочу его обманывать, а вместо этого пытаюсь закрыть канал совместно. Я договариваюсь о финальной commitment-транзакции, называемой shutdown, которая выплачивает каждой стороне её баланс в соответствии с текущим состоянием канала. Указываю биткойн-скрипт, соответствующий адресу закрытия моего кошелька, и прошу Бориса выполнить транзакцию закрытия, которая переведёт мой баланс на этот кошелёк. Борис сделает то же самое и, кроме того, примет запрос о совместном закрытии. Наконец, мы можем произвести расчёт. Я отправляю последнее сообщение, closing_signed, в котором предлагаю комиссию за ончейн-транзакцию закрытия со своей подписью, и если Борис согласен, то ответит той же предложенной комиссией со своей подписью. Если не согласен, то предложит другую комиссию. Этот цикл закрытия может продолжаться до тех пор, пока мы не найдём решение, которое устроит нас обоих.
Маршрутизация каналов
Справочная таблица: уровень маршрутизации, атомарные multihop контракты без потребности в доверии.
Чтобы понять обширный мир маршрутизации, начнём с примера из реальной жизни.
Мне нужно отправить Борису 10 монет из очень редкого материала, но у меня нет с ним прямой связи. Однако мы оба знаем Марка и имеем с ним прямое соединение. Как мне убедиться, что Марк передаст 10 монет Борису без возможности обмануть или просто сбежать с монетами? И, главное, как я узнаю, что монеты действительно были переданы Борису?
Одним из возможных решений могло бы быть пообещать Марку 10 монет, если он сможет доказать мне, что доставил 10 монет Борису.
Но зачем Марку подписывать такой контракт без выгоды для себя?
На самом деле, это не очень удобно, поэтому мы могли бы изменить условия, пообещав Марку 11 монет, если он докажет, что доставил Борису 10. Так он получил бы комиссию в размере одной золотой монеты — неплохо.
Однако остаётся проблема доверия, поэтому мы решаем воспользоваться гарантийным сервисом, называемым escrow
.
Борису нужно получить оплату, поэтому он генерирует секретное значение R
(для простоты предположим, что оно равно строке «hello world!») и хеширует его алгоритмом SHA-256. Хеш значения R мы назовём payment hash
, а секрет, который разблокирует платёж — payment pre-image
.
На этом этапе Борис отправляет мне хеш платежа через Telegram. Я не знаю секрет, но тем временем могу переписать свой контракт с Марком, используя этот хеш платежа и сказав ему:
«Марк, я компенсирую тебе 11 монет при предъявлении действительного сообщения (прообраза), соответствующего данному хешу платежа. Ты можешь получить это сообщение, заключив контракт с Борисом. Чтобы гарантировать тебе выплату, я заблокирую эти монеты в условном депонировании (эскроу) до того момента, как ты заключишь контракт с Борисом».
Данный смарт-контракт обеспечивает сохранность моих монет путем их временной блокировки в эскроу. Ключевой аспект заключается в том, что я произведу оплату Марку только при условии, что тот предоставит мне действительный прообраз для хеша платежа. Прообраз служит подтверждением того, что Марк осуществил выплату Борису.
На данном этапе Марк заключает аналогичный контракт с Борисом, обязуясь выплатить ему 10 монет при предъявлении действительного сообщения, соответствующего хешу платежа. Он также оговаривает условие возмещения после раскрытия секрета, размещая 10 монет в эскроу. (Но помните, что Борис является получателем, и именно он сгенерировал прообраз, поэтому он может предъявить его Марку и фактически получить оплату).
Все стороны имеют контракт. Борис отправляет предварительный образ Марку. Марк проверяет соответствие секрета хешу платежа и, получив подтверждение, дает указание эскроу выплатить 10 монет Борису. Затем Марк предоставляет прообраз мне. Я его проверяю и поручаю эскроу выплатить 11 монет Марку.
На этом все контракты исполнены. Я заплатил в общей сложности 11 монет, из которых 1 получил Марк в качестве комиссии, а остальные 10 достались моему получателю Борису.
Почему это работает?
Это работает потому, что при такой цепочке контрактов Марк не мог избежать блокировки монет в эскроу, и он действительно поместил их в эскроу.
А в чём слабые стороны?
Если бы Борис решил не раскрывать свой прообраз, мои монеты и монеты Марка остались бы заблокированными в эскроу. Однако эта проблема легко решается путём установления дедлайна для исполнения контракта, т.н. таймлока, временной блокировки.
Транзакции в Lightning Network атомарны: они либо проходят, либо отменяются полностью!
Hash Time-Locked Contracts (HTLC)
Маршрутизирующий протокол справедливости, используемый в Lightning Network, называется hash time-locked contract
(HTLC) и представляет собой механизм, описанный в предыдущем примере.
HTLC использует хеш прообраза платежа в качестве секрета для разблокировки платежа. Существует и альтернативный механизм маршрутизации — point time-locked contract
(PTLC), обладающий повышенной эффективностью и конфиденциальностью. Он основывается на новом алгоритме подписей Шнорра, интегрированном в Биткойн в 2021 году.
Рассмотрев пример с HTLC, я хотел бы обратить внимание на небольшую проблему: каждый из этих контрактов может быть разблокирован любым, кто знает прообраз. Возникает вопрос: что произойдёт, если Борис потратит и HTLC Марка, и мой? В таком случае это вовсе не была бы не требующая доверия система.
Скрипт HTLC должен включать дополнительное условие, связывающее каждый HTLC с конкретным получателем. Это достигается путём требования цифровой подписи, соответствующей открытому ключу каждого получателя, что исключает возможность использования данного HTLC кем-либо другим.
И ещё одна небольшая проблема, которую необходимо решить:
Что происходит, если какой-то узел выходит из сети или отказывается сотрудничать? Как мне аккуратно сделать так, чтобы платёж не прошёл?
Есть два основных способа:
- Совместный: HTLC «отматывается» назад путём удаления его из commitment-транзакции без изменения баланса (подробнее об этом ниже).
- Возврат с таймлоком: как уже обсуждалось ранее, каждый HTLC включает в себя пункт о возврате, связанный с блокировкой по времени.
Пересылка платежей
Справочная таблица: peer-to-peer уровень, добавление, расчёт, сбой HTLC.
HTLC кажутся прерогативой multihop («многоскачковых») платежей, но в действительности протокол Lightning использует их и для «локальных» платежей внутри канала. Причина заключается в поддержании согласованности и единого дизайна протокола в каждой точке сети. Для получателя платежа нет разницы между платежом, сделанным его партнёром по каналу, и платежом, переданным его партнёром от имени кого-то другого.
Рассмотрим пример с участием меня (N), Марка и Бориса.
- У нас с Марком есть канал с балансом 70 тыс. сатоши на каждой стороне. Напомню, что commitment-транзакция, которая привела нас к этому состоянию, отложена и может быть отозвана.
- Я хочу, чтобы Марк принял HTLC на 50 тысяч сатоши для пересылки Борису. Но для этого мне нужно отправить детали этого HTLC, такие как сумма платежа и его хеш. Я делаю это с помощью сообщения
update_add_htlc
.
Технически обмен сообщениями происходит следующим образом:
Информации, полученной Марком, достаточно для создания новой фиксирующей commitment-транзакции, которая сохраняет тот же баланс для состояния канала (моего и Марка), и содержит новый выход, представляющий предложенный мной HTLC. Это новая commitment-транзакция будет иметь в HTLC-выходе 50 тыс. сатоши — сумму, которая поступает непосредственно от меня, так что мой новый баланс составит 20 тыс. сатоши.
При изменении состояния канала должно быть создано новое обязательство (commitment), и после того как Марк его создаёт, я подписываю его сообщением commitment_signed
.
Теперь у Марка есть подписанное обязательство, и он должен подтвердить его и отозвать старое. Он делает это с помощью сообщения revoke_and_ack
, что позволяет мне создать ключ отзыва для формирования штрафной транзакции. Суть в том, что Марк больше не может опубликовать своё старое, отозванное только что обязательство, иначе у меня появится всё необходимое, чтобы его наказать (ключ отзыва). Старое обязательство фактически было отменено.
Чего не хватает?
Что ж, я ещё не отозвал своё старое обязательство, и в текущем состоянии всё ещё нет HTLC! Я создаю новое зеркальное обязательство, содержащее HTLC, которое ещё должно быть подписано Марком. Точно так же, как и Марк, я выполняю revoke_and_ack
и подписываю новое обязательство с помощью commitment_signed
. Теперь я также обязуюсь не публиковать старое состояние: я предоставил Марку ключ для отзыва старого обязательства.
В любой момент времени в сети Lightning у каждого отдельного канала могут быть сотни HTLC.
Теперь у нас с Марком есть новая commitment-транзакция по каналу, содержащая также дополнительный HTLC выход, но новый бюджет всё ещё не отражает того факта, что я отправил Марку 50 тысяч. Платёж станет возможным только в обмен на доказательство оплаты Борису!
Предположим теперь, что Марк и Борис делают в точности одно и то же. Борис является получателем и единственным, кто знает прообраз хеша платежа, поэтому может немедленно удовлетворить условия HTLC с Марком.
Фактически, именно это он и делает, отправляя Марку сообщение update_fulfill_htlc
. Получив это сообщение, Марк тут же проверяет, производит ли этот прообраз нужный хеш платежа; если сравнение даёт TRUE, этот секрет может быть использован для погашения HTLC.
Марк также отправляет мне update_fulfill_htlc
, таким образом прообраз распространяется от Бориса ко мне. Я выполню ту же проверку, что и Марк, вычислив хеш прообраза. Это и есть точное доказательство того, что Борис получил оплату!
Марк и я можем удалить HTLC из commitment-транзакций и обновить балансы нашего канала, чтобы отразить новое состояние (очевидно, снова проходя цикл обязательств и отзыва 🙂).
Но что, если произойдёт сбой или истечёт срок?
В этом сценарии процесс будет развиваться аналогичным образом, в отличие от сообщения об ошибке update_fail_htlc
. Мне придётся удалить HTLC и пройти через цикл транзакций обязательств и отзыва, чтобы перевести состояние канала к новому обязательству, со сбросом балансов к состоянию до HTLC.
А как насчёт платежей локально, в пределах одного канала?
Замените в примере Марка на Бориса.
Луковая маршрутизация
Справочная таблица: уровень маршрутизации, луковая маршрутизация от источника (SPHINX).
Первый узел отправителя платежа называется исходным. Последний узел, который является получателем, называется конечным узлом. Каждый промежуточный узел между источником и пунктом назначения называется хопом
, и каждый хоп должен установить исходящий HTLC для следующего хопа. Информация, которую я, узел-отправитель, посылаю, можно назвать «полезной нагрузкой» (пэйлоудом) или «данными» хопа, а всё сообщение целиком — луковицей.
Таким образом, если перефразировать пример из предыдущего параграфа с этой новой терминологией, то можно сказать, что я создал луковицу с пэйлоудом хопа и каждому хопу указал, как построить исходящий HTLC, чтобы платёж дошёл до Бориса.
Есть два возможных формата, которые я могу использовать для передачи информации каждому хопу: устаревший, называемый данными хопа (hop data), и более гибкий, называемый пэйлоудом хопа (hop payload).
С чего мне следует начать составление этого сообщения?
Очевидно, что с пэйлоуда хопа, которая будет доставлена Борису, конечному узлу. Этого достаточно, чтобы сказать, что сообщение, которое получит Борис, будет отличаться из-за наличия поля, все значения которого будут установлены на 0. Это поле называется short_channel_id
и является ссылкой на канал, которую я, как создатель пэйлоуда хопа, использую для обращения к каналам. Борис — мой получатель, поэтому я заполняю это поле нулями, поскольку Борис, являясь получателем пэйлоуда, не будет создавать исходящий HTLC.
Разумеется, никто, кроме Бориса, эту информацию знать не будет, поскольку она зашифрована.
Затем я могу начать сериализировать луковичное сообщение в формате type-length-value, подходящем для пэйлоуда хопов. После этого я также подготовлю пэйлоуд хопа для Марка.
В пэйлоуде хопа есть три поля: short_channel_id
(уже рассмотрели), amt_to_forward
, которое представляет сумму в миллисатоши, и outgoing_cltv_value
, представляющее задержку таймлока HCLT, выраженную в высоте блока в таймчейне.
Если amt_to_forward
составляет 50100 сатоши, Марк ожидает комиссию в 100 сатоши за маршрутизацию платежа Борису. Ожидания относительно таймлока и комиссий за маршрутизацию определяются разницей между двумя HTLC — входящим и исходящим.
Чтобы немного усложнить маршрутизацию, предположим, что между мной и Марком есть ещё один узел.
У меня есть три пэйлоуда: я их построил для Бориса, Марка и Лукаса. Эти три промежуточные пэйлоуда будут обёрнуты в луковичную структуру.
Итак, теперь мне нужно сгенерировать несколько ключей, которые понадобятся для шифрования различных слоёв «луковицы»:
- Я должен убедиться, что только конечный узел сможет прочитать сообщение.
- Каждый промежуточный узел должен знать только предыдущий и следующий узлы.
- Никому не нужно знать общую длину пути.
- Каждый промежуточный узел сможет проверить, что сообщение не было подменено.
Процесс формирования сообщения называется конструированием пакета. В луковой маршрутизации применяется интересный метод: путь делается одинаковой длины для каждого узла, как если бы каждый узел видел луковицу всё ещё в начале её пути с ещё 19 возможными переходами («хопами») впереди (поскольку максимальный путь луковой маршрутизации составляет 20 хопов).
По мере удаления каждого слоя добавляются данные-заполнители, обеспечивающие постоянную длину пэйлоуда.
Всё прекрасно, но как этот «луковый» пакет создаётся на практике?
Во-первых, «луковица» строится, начиная с конечного узла, то есть получателя сообщения, которым является Борис.
Я создаю исходное «пустое» поле объемом 1300 байт, генерируемое псевдослучайным образом на основе заданного ключа.
Вставляю предварительно сконструированный пэйлоуд хопа, добавляя его (в абстрактном виде) слева, что приводит к смещению заполнителя вправо. Эта операция вызывает превышение размера, так как объем данных выходит за пределы 1300 байт, в результате чего некоторая часть заполнителя будет удалена.
На данном этапе информация Бориса содержится в открытом виде в луковичном сообщении, поэтому необходимо замаскировать ее с помощью ключа rho, который также известен Борису (и только ему). Для обфускации генерируется поток псевдослучайных данных из этого ключа. Затем между пэйлоудом хопа и сгенерированным потоком выполняется операция XOR (исключающее «или»).
XOR используется, поскольку при повторном применении операции XOR Борис сможет восстановить исходное содержимое пэйлоуда хопа. Это обусловлено свойствами операции XOR: однократное применение к фрагменту данных изменяет его, но повторное применение восстанавливает исходное сообщение, фактически «отменяя» предыдущее преобразование.
Наконец, для обеспечения аутентичности и целостности части Бориса используется дополнительный ключ, известный как mu. Этот ключ применяется для вычисления HMAC — криптографического хеш-кода, который служит контрольной суммой, основанной на хеше всего пэйлоуда хопа.
Теперь очередь пэйлоуда хопа Марка. Я выполняю те же шаги, с той лишь разницей, что теперь я начинаю не с 1300 байт заполнителя, а с 1300 байт, содержащих пэйлоуд хопа Бориса, соответствующим образом обфусцированную 🙂 .
Вставляя пэйлоуд хопа Марка слева, надо быть очень осторожным, чтобы не перезаписать часть данных Бориса! Здесь я также вычисляю HMAC с помощью ключа mu
и обфусцирую всё с помощью ключа rho
. Только в этот раз ключи mu
и rho
известны только мне и Марку, поскольку этот хоп предназначен только для него.
Два замечания:
- У нас есть два HMAC: только что добавленный внешний от Марка и внутренний от Бориса.
- Когда Марк получит луковичное сообщение, он не сможет определить количество пэйлоудов внутри. Для него это всегда будет выглядеть как первый пэйлоуд из возможных 20 — именно потому, что мы сохраняем фиксированную длину с помощью приёма, который скоро рассмотрим.
Я также выполняю те же шаги для Лукаса. Луковичный пэйлоуд готов, и я могу отправить его Лукасу, моему новому партнеру по каналу. В итоге он будет сформирован следующим образом:
- 1 байт для версии луковицы, обеспечивающий возможность различать будущие обновления формата протокола.
- 33 байта публичного сессионного ключа, который используется каждым узлом для вычисления известных ключей обфускации и контрольной суммы (а также третьего ключа, о котором я ещё не упоминал, называемого pad).
- Наш луковичный пэйлоуд размером 1300 байт.
- 32 байта внешнего HMAC для проверки моим партнёром по каналу, Лукасом.
Я готов отправить сообщение update_add_htlc
Лукасу.
Сначала потребуется обновить состояние канала с помощью commitment-транзакции, но эту часть мы уже подробно рассмотрели, так что её опустим. «Новые» операции, которые Лукасу нужно будет выполнить: взять ключ сессии, сгенерировать ключ mu
и проверить контрольную сумму HMAC
. После подтверждения целостности и подлинности Лукасу необходимо извлечь свою часть пэйлоуда, а затем переслать луковицу следующим узлам.
Проблема: если Лукас уберёт свой пэйлоуд хопа, то мы потеряем фиксированную длину в 1300 байт (что плохо).
Здесь и пригодится трюк, о котором я упоминал: каждый узел должен генерировать заполнитель перед отправкой луковицы следующему узлу.
Лукас применяет следующую процедуру: он дублирует 1300-байтовый пэйлоуд хопа, размещая две копии последовательно. В результате он получает два хопа по 1300 байт каждый. Второй хоп заполняется нулями. Затем он генерирует ключ rho
на основе сессионного ключа и выполняет операцию XOR между 2600-байтовым пэйлоудом хопа (состоящим из одной реальной и одной нулевой части) и 2600-байтовым потоком, сгенерированным с использованием ключа rho
.
Происходит следующее: первые 1300 байт, содержащие истинный пэйлоуд хопа, восстанавливаются в исходном виде, поскольку мы применили операцию XOR повторно. Это позволяет Лукасу прочитать свой пэйлоуд хопа. Одновременно с этим, операция XOR между 1300 байтами, заполненными нулями, и потоком, сгенерированным из ключа rho
, создаёт псевдослучайные данные.
Таким образом, Лукас прочитает предназначенную ему информацию, удалит её из пэйлоуда хопа, после чего оставшиеся байты сдвинутся влево, заполняя освободившееся пространство. Оставшийся заполнитель от операции XOR будет использован для сохранения общей длины в 1300 байт. Однако для отправки луковичного пакета Марку необходимо добавить внешний HMAC, чтобы тот мог проверить целостность и корректность данных.
Пэйлоуд хопа Лукаса содержит всю информацию, необходимую для создания HTLC с Марком!
Марк выполняет точно такие же действия, как и для Лукаса, и в итоге Борис получит конечный пэйлоуд. Когда он получает update_add_htlc
с хешем платежа, он понимает, что это платёж для него, то есть это конечная точка маршрута. Он, в общем, не делает ничего особенного: снимает слой с «луковицы», но ничего не пересылает дальше, поскольку пришло время ответить Марку сообщением update_fullfil_htlc
, чтобы погасить HTLC. Это происходит в обратном направлении: Борис -> Марк -> Лукас -> N), и все HTLC будут погашены, соответственно обновив балансы каналов.

В платежах типа keysend
уже не получатель раскрывает прообраз отправителю, а сам отправитель встраивает прообраз в пэйлоуд хопа: когда получатель принимает зашифрованное сообщение (луковицу), он использует этот прообраз, соответствующий хешу HTLC платежа, для завершения транзакции. Таким образом, в данном случае отпадает необходимость в обмене инвойсом или сканировании QR-кода 😉.
Госсип
Справочная таблица: уровень маршрутизации и P2P, комиссии за маршрутизацию, метаданные каналов и повтор госсип.
Итак, мы разобрались, как формировать луковичное сообщение и как передавать его между узлами, но теперь возникает вопрос:
Как построить действительный маршрут до получателя? Откуда мне знать информацию о его канале?
Для этого мне нужен граф каналов
, представляющий собой не что иное, как (взаимосвязанный) набор публично объявленных каналов и узлов, которые эти каналы соединяют.
Используемый протокол называется gossip (дословно «сплетни») и позволяет каждому узлу делиться информацией с сетью Lightning. Он применяется при создании каналов для распространения информации, например: «Я создал новый канал и сообщаю об этом новом соединении всем моим другим каналам. Моим пирам: пожалуйста, распространите это сообщение и пусть оно разойдётся по всей сети».
Сеть Lightning (почти) эквивалентна графу каналов LN.
Поскольку речь идет о P2P-сети, критически важно иметь начальную фазу загрузки, чтобы узлы могли обнаруживать друг друга. Первый шаг заключается в нахождении хотя бы одного пира, который является частью этой сети и обладает полным графом каналов.
Используя один из множества протоколов начальной загрузки я могу установить соединение с узлом, после чего необходимо загрузить и верифицировать граф каналов. После этого уже можно приступать к открытию платёжных каналов.
Крайне важно поддерживать актуальность графа, что включает в себя обнаружение и верификацию новых каналов, удаление закрытых в ончейн-транзакции каналов, а также удаление каналов, от которых не поступает сигнал о синхронизации приблизительно каждые две недели.
Как происходит начальная загрузка?
Простой, но непрактичный ввиду своей ненадёжности вариант заключался бы в использовании жёстко прописанного списка пиров в ПО lightning-узла. Этот метод используется в качестве резервного на случай, если мы не можем найти пиров с помощью основного метода, называемого bootstrap DNS
.
Для поиска пиров с использованием DNS, BOLT #10 мы используем три типа записей:
- SRV-записи для определения набора открытых ключей узла.
- A-запись для сопоставления открытого ключа с IPv4-адресом.
- AAA-запись для сопоставления открытого ключа с IPv6-адресом.
Когда вы вводите в адресной строке браузера «bitnovosti.io», DNS-записи A и AAA можно представить как карты, помогающие найти сайт в интернете.
«A»-запись аналогична карте для поиска домов в старом районе, где указан традиционный адрес (например, ул. Главная, 123). В контексте интернета это соответствует IPv4-адресу. «AAA»-запись можно сравнить с картой для поиска домов в каком-нибудь новом, современном районе, где вместо стандартных адресов, как в IPv4, используется более сложная последовательность цифр и букв, характерная для IPv6.
Для установления подключения с узлом нам нужен как его публичный ключ, так и IP-адрес!
Процесс включает следующие шаги:
- Я определяю DNS-сервер, поддерживающий протокол Bolt #10. Для этого я могу использовать общий сид-сервер, поддерживаемый основными реализациями Lightning Network.
- Отправляю
SRV
-запрос для получения набора пиров, которые можно использовать для начальной синхронизации. Каждый из этих пиров идентифицируется публичным ключом, закодированным в формате bech32. - Я декодирую публичные ключи, полученные в ответ на мой SRV-запрос, чтобы получить идентификаторы узлов.
- Выполняю DNS-запрос для получения IP-адреса целевого узла вместе с его идентификатором (публичным ключом).
- Подключаюсь к узлу, используя для этого lightning-клиент.
Таким образом я нахожу своего пира и устанавливаю первое соединение.
Теперь мне необходимо синхронизировать и проверить ориентированный граф каналов. Он называется ориентированным, поскольку рёбра графа имеют определённое направление. Это означает, что связь от узла 1 к узлу 2 не обязательно симметрична и не подразумевает непременное наличие обратной связи от узла 2 к узлу 1.
Каналы представляют собой UTXO (адреса с multisig 2-из-2), поэтому мы можем дать и другое определение графу каналов: это подмножество UTXO Биткойна. Невозможно подделать граф, как мы ещё увидим далее.
Информация из графа распространяется в сети посредством трёх сообщений, описанных в Bolt #7:
node_announcement
: выполняет функцию «приветствия» узла и содержит такую информацию, как идентификатор, возможности узла в маршрутизации платежей или его политика маршрутизации. Эти сообщения не записываются в таймчейн и действительны только при наличии соответствующегоchannel_announcement
сообщения. Проверка этих сообщений очень простая: если оно уже существовало, то новоеchannel_announcement
будет иметь более позднюю метку времени; если объявления канала нет, тоchannel_announcement
обязательно должно существовать в локальном графе каналов (подробнее об этом позже). Оно также должно иметь:- действительную подпись;
- все включённые адреса, упорядоченные по возрастанию в соответствии с идентификатором адреса;
- байты псевдонима должны иметь корректную UTF-8 кодировку.
channel_announcement
: теперь мы можем объявить о новом канале. Если я хочу, чтобы мой узел позволял пересылать через него другие луковичные сообщения, я должен объявить его публично. Когда канал не объявлен, в теории он вовсе не может получать платежи. На практике это возможно благодаря подсказкам маршрутизации, которые включают информацию, позволяющую отправителю правильно направить луковичное сообщение. Фактический размер графа каналов неизвестен именно из-за наличия необъявленных каналов!
Важный момент: чтобы избежать спама и обеспечить надёжную аутентификацию, мы используем таймчейн Биткойна. Чтобы узел принял channel_announcement
, объявление должно доказывать наличие транзакции открытия канала в таймчейне.
Как можно обозначить ссылку на канал в таймчейне? Использовать для этого полный выход канала было бы неразумно, поскольку для проверки верификатору потребовалась бы полная копия информации об UTXO, обычно доступная только полным нодам.
Гораздо лучше использовать короткий идентификатор канала. Для создания этой ссылки мы опираемся на позицию данного выхода в таймчейне. Нужно лишь указать на определённый блок, затем на транзакцию внутри него и, наконец, на конкретный выход, созданный этой транзакцией. Это и есть короткий идентификатор канала (или scid
).
channel_update
: третье и последнее сообщение в gossip— протоколе, используется для обновления информации о платёжном канале в сети Lightning. Этот тип сообщения позволяет узлам направлять платежи через наиболее эффективные и актуальные каналы.
Итак, мы поняли, что gossip — это повторяющаяся активность, и как только узел синхронизируется с Lightning Network, он начинает получать gossip-сообщения, которые мы только что проанализировали; с помощью этой информации он начнёт строить свой граф каналов. Чем больше сообщений он получает, тем точнее и эффективнее становится его карта для поиска наилучшего маршрута отправки платежей.
Помимо этого, различные реализации Lightning могут хранить дополнительную информацию, включая разного рода метаданные, такие как оценка качества узла как пира для маршрутизации платежей.
Поиск пути
Справочная таблица: уровень платежей, попытки платежей, поиск пути, выбор пути.
Что это и какую проблему нам нужно решить с помощью поиска пути?
Платёж в Lightning Network по сути зависит от нахождения пути, соединяющего отправителя с получателем. Этот процесс называется поиском пути, однако… он не является частью стандарта BOLT! Он не включён в стандарт, потому что не требует координации или совместимости. Если маршрутизация описана в BOLT, то поиск и выбор маршрута оставлены на усмотрение отправителя платежа в соответствии с алгоритмом используемой им реализации Lightning.
Проблема состоит в нахождении наилучшего маршрута между отправителем и получателем. «Наилучший» в данном случае означает успешную доставку, низкие комиссии и минимальные таймлоки.
Для лучшего понимания проблемы транспорта сатоши между мной и Борисом необходимо определить три атрибута:
- Ёмкость: это общая (максимальная) сумма сатоши, размещённая в адресе multisig 2-из-2.
capacity (моего канала с Борисом) = balance(N) + balance(Boris)
- Баланс: это сумма, которую держит в канале каждый из его участников.
- Ликвидность: доступный баланс, который фактически может быть отправлен. Ликвидность моего канала равняется моему балансу минус резерв канала и любые мои неисполненные HTLC.
capacity(N) = balance(N) - channel_reserve(N) - HTLCs(N)
Единственной публичной величиной, доступной всей сети, является емкость канала. Балансы не раскрываются для обеспечения масштабируемости и конфиденциальности. Если бы они были общедоступными, то проблема поиска маршрута могла бы быть решена с помощью любого алгоритма поиска пути с минимальными затратами.
Необходимо проявить изобретательность. На данный момент мы можем с уверенностью утверждать лишь то, что ликвидность канала находится в диапазоне от channel_reserve
(нижняя граница) до ёмкости канала за вычетом channel_reserve
(верхняя граница).
reserve <= liquidity <= (capacity - reserve)
Это наш интервал неопределенности: мы не располагаем точными данными о балансах, но имеем приблизительную оценку ликвидности, которая должна находиться в этом диапазоне. Сеть не знает этого диапазона, поскольку знать его можем только Борис и я, но мы могли бы использовать неудавшиеся HTLC из наших попыток платежей для уточнения нашей оценки ликвидности и сужения интервала неопределённости.
Если HTLC не срабатывает, потому что мы думали, что он может обработать N сатоши, а оказывается, что он может передать только M (где M < N), то мы уже можем изменить верхнюю границу нашего диапазона на M - 1
.
Упрощая, мы применяем алгоритм A*, в котором у нас есть узлы, а соединениям между ними присваиваются веса. Эти веса представляют собой комиссии за маршрутизацию платежа.
Поскольку ликвидность канала неизвестна отправителю и представляет собой лишь теоретический диапазон, проблема усложняется. Мы можем только предполагать, какие соединения обладают достаточной ёмкостью для обеспечения маршрутизации платежа. Возможно отсортировать эти маршруты по весу и последовательно пробовать каждый из них, пока платёж не пройдёт успешно. При каждой неудачной попытке платежа можно использовать ошибку HTLC для обновления графа, уточняя пределы и уменьшая неопределённость, поскольку я исследовал определённый путь.
Подведём итог:
- я строю граф каналов;
- выполняю поиск пути на основе определённых эвристик;
- осуществляю попытку платежа;
- при неудачных платежах я корректирую свой интервал, чтобы уменьшить неопределённость при следующих попытках.
Парадоксальным образом, я учусь на ошибках, так как чем больше у меня неудачных платежей, тем лучше я могу уменьшить неопределённость своего интервала и получить более точную карту возможных путей. Знания, полученные в результате этих ошибок, будут чрезвычайно ценны для будущих платежей.
Однако важно помнить, что это знание не является постоянным, поскольку оно устаревает по мере того, как другие узлы отправляют или маршрутизируют платежи. Я не буду иметь доступа к информации об этих платежах, если только сам не являюсь отправителем.
Луковая маршрутизация, как мы видели, позволяет нам видеть только один переход (хоп) транзакции. Надо также учитывать, как долго хранить эту информацию о неопределённости различных маршрутов, прежде чем её хранение в памяти станет бесполезным.
Многокомпонентные платежи — это функция, представленная в 2020 году, которая основана на стратегии поиска оптимального пути, но с разделением платежа на множество мелких частей, отправляемых в виде HTLC по разным маршрутам, очевидно сохраняя атомарность платежа. Для успешного завершения все части должны достичь получателя; в противном случае платёж считается неудачным.
Они представляют собой улучшение по очень простой причине: небольшие платежи не должны адаптироваться к ликвидности каналов, поскольку она практически наверняка будет достаточной для каждой такой «частицы». Это как если бы наш интервал неопределённости был стогом сена, а микроплатёж — иголкой.
Конечно, с полной статистической достоверностью гарантировать успешное прохождение каждого многокомпонентного платежа нельзя.
Безопасность и атаки на LN
Lightning Network конфиденциальна?
Это зависит от того, что мы подразумеваем под конфиденциальностью.
Узел видит своего преемника и предшественника. При наличии множества каналов и высокой связности он способен генерировать эвристики и ассоциации, как в случае с очень крупными некастодиальными кошельками. Ноды таких провайдеров отвечают за маршрутизацию значительной доли платежей, проходящих через данный узел в обоих направлениях.
Что произойдёт, если два или более узла окажутся под контролем злоумышленника? Если два сговорившихся узла находятся на одном платёжном пути, они сразу понимают, что маршрутизируют HTLC для одного и того же платежа по одинаковому хешу прообраза. Какие риски это влечёт за собой?
Информационная безопасность чаще всего рассматривается в трёх аспектах:
- Ответственность: действительно ли секретная информация достигает предполагаемого получателя?
- Целостность: не была ли информация изменена во время передачи?
- Доступность: работает ли система или подвергается какая-либо атаке типа «отказ в обслуживании»?
Реальность такова, что не только для Lightning, но и для любой системы нет гарантии, что атакующий не добьётся успеха. Это игра с нулевой суммой. Конечно, можно снизить риск, но никогда не свести его к нулю.
Есть ли разница между приватностью Биткойна и Lightning?
На первый взгляд, Lightning обеспечивает лучшую приватность. В Биткойне транзакции не связывают цифровую личность с конкретным адресом, но верно также и то, что транзакции передаются в открытом виде и остаются открытыми для анализа. За последние годы появилось множество компаний, которые занимаются этим профессионально.
В LN платежи не передаются всей сети и, главное, не сохраняются навечно, как в основном блокчейне Биткойна. Однако у протокола есть другие свойства, которые могут создавать определённые сложности.
Одно из них? Крупные платежи могут иметь меньше вариантов маршрутизации.
Что ещё? Узлы Lightning поддерживают постоянную идентификацию, в отличие от узлов Биткойна: я могу получать и отправлять платежи только с того узла, который использовал для открытия платёжного канала в основной цепи, и не могу использовать никакие другие. Кроме того, мне приходится объявлять свой IP-адрес и идентификатор, что создаёт постоянную связь между ID узлов и IP-адресами. IP-адреса являются промежуточным звеном в атаке на анонимность и часто связаны с физическим местоположением пользователя в реальном мире.
Наконец, пользователи Lightning могут столкнуться с отказом в обслуживании путём блокировки каналов или их истощения. Для пересылки платежей требуется, чтобы баланс (ограниченный ресурс) был заблокирован в HTLC на всём пути. Злоумышленник мог бы инициировать множество платежей, не завершая их, надолго заняв требуемый для них капитал (хотя это зависит также от продолжительности таймлока).
В итоге можно заключить, что с точки зрения конфиденциальности явного преимущества ни у Биткойна, ни у Lightning Network, нет — у каждой системы есть свои сильные и слабые стороны. Стоит отметить, что многочисленные исследовательские группы в настоящее время работают над совершенствованием аспектов безопасности и приватности обеих систем.
В заключительной части этой статьи я представлю обзор возможных атак, которым Lightning может подвергнуться или уже подвергалась. Мы почти закончили.
Установление связи между отправителем и получателем
В Lightning анонимность участников имеет ключевое значение для обеспечения свободы платежей, что является одним из основополагающих принципов, ради которых создавался Биткойн. Однако злоумышленник может попытаться нарушить эту приватность, раскрыв отправителя и получателя платежа. Причины могут быть самыми разными. Это не только поставит под угрозу безопасность платежей, но и способствует установлению цензуры в отношении определённых получателей или отправителей.
Можно предположить, что существует два типа наблюдателей, которые могут попытаться нарушить анонимность в LN: «вне маршрута» (off-path) и «на маршруте» (on-path) [платежа]. Наблюдатели «вне маршрута» пытаются определить отправителя и получателя платежа, не участвуя напрямую в процессе маршрутизации. Наблюдатели «на маршруте» могут использовать для этого информацию, полученную в процессе выполнения платежа.
Первые, off-path, используют технику, называемую «зондированием» или «пробированием», чтобы определить индивидуальные балансы в различных платёжных каналах. Получив эту информацию, атакующий может сравнить снимки сети в моменты времени t1
и t2
, чтобы выявить произошедшие платежи и обнаружить отправителя, получателя и сумму платежа. Например, делается снимок сети в момент t1 = 21:00 и ещё один снимок в 21:01. Если был только один платёж, то легко выяснить, кто являются участниками и какова сумма, тем более что статус канала должен обновляться каждый раз при изменении баланса. Если было несколько транзакций, задача усложняется, поэтому компании разработали эвристические методы для разделения различных платежей.
Наблюдатели on-path, напротив, активно участвуют в маршрутизации платежей и могут видеть часть информации о платеже. Их скилл заключается в исключении на основе информации, полученной в процессе платежа, определённых узлов из множества анонимности отправителя или получателя. Например, промежуточный узел может видеть сумму любого маршрутизируемого платежа и различные дельты таймлока, и исключить из множества анонимности любой узел с ёмкостью меньшей, чем сумма маршрутизируемого платежа.
Отказ в обслуживании
Предоставление онлайн-ресурсов повышает риск их недоступности вследствие потенциальных атак типа «отказ в обслуживании» (DoS). Как злоумышленник может лишить меня доступа к сервису? Элементарно. Производится массированная атака бесполезными запросами, неотличимыми от легитимных. Потери средств при этом не происходит, а единственная цель — вывести из строя объект DoS-атаки. В контексте Lightning Network эта сеть взимает комиссию за использование публичных ресурсов, но в данном случае каналы являются публичными, а комиссии представляют собой плату за маршрутизацию.
Когда узел отправляет платёж от моего имени, он использует свои данные и пропускную способность для обновления состояния канала, и, помимо прочего, сумма резервируется (HTLC) до тех пор, пока платёж не будет успешно завершён или не истечёт таймлок. Неудачные платежи не подразумевают уплаты комиссий, что, конечно, прекрасно для нас, добросовестных пользователей сети. Но куда менее приятно, то, что это позволяет злоумышленникам бесплатно бомбардировать сеть запросами на маршрутизацию.
Концептуальной разновидностью DoS-атаки является атака, основанная на топологии сети. Поскольку LN не является полностью децентрализованной системой, для нарушения работы узлов, участвующих в маршрутизации платежей, атакующему нецелесообразно нацеливаться на те, что находятся на периферии графа каналов. Более эффективной стратегией является атака на средние и крупные узлы, имеющие множество связей с другими узлами сети.
Нарушение работы канала
Каждый узел способен обрабатывать не более 483 HTLC. При атаке, направленной на нарушение работы канала, злоумышленник пропускает через целевой канал ровно 483 платежа. Какие могут быть последствия?
Схожий вариант атаки связан с блокировкой ликвидности, когда атакующий использует крупные HTLC, потребляющие всю доступную пропускную способность канала жертвы. Это, безусловно, гораздо дороже, чем просто отправка 483 HTLC.
Межуровневая деанонимизация
Создание и закрытие lightning-каналов происходит в таймчейне, что облегчает для атакующего деанонимизацию пользователя с использованием как офчейн, так и ончейн данных. Каковы цели таких атакующих?
- Создать кластер биткойн-адресов, принадлежащих одному пользователю, на базовом уровне Биткойна.
- Контрмера 1: не использовать повторно выходы финансирующих транзакций для открытия новых каналов; это позволяет противодействовать многим эвристическим методам анализа.
- Контрмера 2: никогда не использовать уникальные внешние источники для финансирования, лучше их разнообразить.
- Создать кластер lightning-узлов, принадлежащих одному пользователю, на втором уровне (L2).
- Здесь я хотел бы задать вам вопрос: использование псевдонимов, несомненно, отлично упрощает жизнь и повышает удобство использования. Однако, если я использую собственное доменное имя в качестве псевдонима, считаете ли вы это хорошей или плохой идеей? Если вы считаете это нежелательным, какие контрмеры вы бы порекомендовали?
- Установить связь между lightning-узлами и адресами в таймчейне.
- Действуя противоположно двум контрмерам выше, вы облегчаете установление межуровневой связи между узлом Lightning Network и субъектом сети первого уровня.
Можно ли от этого защититься?
Непросто, но возможно.
- Используйте необъявленные каналы, если не собираетесь заниматься маршрутизацией.
- Если вы хотите заниматься маршрутизацией:
- Установите минимальный размер HTLC, которые вы готовы принимать, выше значения по умолчанию. Таким образом, каждый слот не может быть занят слишком маленьким платежом (спам-фильтр).
- Ограничьте максимальное количество слотов, которые может использовать один пир.
- Отслеживайте частоту ошибок и, если у какого-то пира она превышает определённый уровень, ограничивайте частоту запросов от него.
- Используйте теневые каналы — платёжные каналы, параллельные существующим. Их можно использовать для маршрутизации, но они не объявляются сети.
- Постарайтесь найти правильный баланс между принятием и отклонением каналов. Когда пир открывает канал на вашем узле, вы не можете знать, будет ли он использован для атаки на ваш узел или нет.
- Избегайте использования одинакового псевдонима для вашего узла и вашего домена (см. выше 😜).
- Используйте Tor.
- Больше читайте, не полагайтесь исключительно на эту статью.
🙌
Подписывайтесь на BitNovosti в Telegram!
Делитесь вашим мнением об этой статье в комментариях ниже.
Источник