Lightning Network channels

Lightning Network — передача средств через узлы

В прошлый раз я рассказал, что такое канал на простом уровне. Там я сравнивал каналы с двумя кру́жками одинакого размера, трубочке между ними и объёмом воды, равном одной кружке. Рассказал про передачу средств в биткойне (перекачка водички). Но тот пример был ограничен только передачей средств между двумя сторонами — вами и другим узлом. Но на практике, конечно же, это редкий вариант и не часто восстребованный. Сеть молния не стала бы успешной и популярной, если бы всё работало только именно так: ведь вам бы пришлось открывать канал буквально с каждым, кому вы намеревались бы заплатить. Это свело бы на нет преимущества сети Lightning. Более того, я думаю, что само название сети — «молния» — задумывалось создателями как что-то ветвистое, напоминающее разряд молнии (хотя и скорость здесь тоже имеет не последнее значение). И вот, чтобы сеть молния была практичной, удобной, быстрой — был придуман механизм маршрутизации. Ниже я постарался как можно проще описать механизмы передачи средств через узлы, опуская некоторые детали, но оставляя другие как важные. Эта статья получилась немного технической, но если вы хотите представлять, как работает сеть Lightning более менее в деталях — думаю, что этого будет достаточно для старта.

Пример маршрута

Маршрутизация

Чтобы сеть молнии была быстрой и практичной — в ней придумали маршрутизацию (англ. «routing»). Чтобы далее были понятны принципы работы маршрутизации, я нарисовал схемку примера. На ней вы — зелёный кружок и вы отправитель платежа. Скажем, что вы решили передать платёж получателю «C», но прямого канала у вас с ним нет. Всё, что у вас есть — один канал с узлом «A». В вашей «кружке» (этими аналогиями я оперировал в первом моём посту) есть средства и вы можете перелить их только узлу «A». Протокол молния позволяет вам дать указания для узлов «A» и «B» (можно и больше) переслать платёж получателю «C» за небольшое вознаграждение каждому из узлов в цепочке. Причём механизм таков, что он гарантирует сохранность ваших средств по пути передачи. Как именно — я опущу здесь, для простоты изложения. А пока что просто представьте, что у узла «A» есть кружка с вашим каналом, но есть и вторая кружка — для канала «A<—>B». Точно также у узла «B» есть две кружки — с узлом «A» и «C». Информация о том, у кого с кем кружки и какого размера — хранится в общей базе сети молния, которая называется «Граф» (не от слова граф Монте-Кристо, а от математического слова) и эта база ретранлируется между узлами по специальному протоколу, придуманному для Lightning — «Gossip» (в переводе — «сплетни»). По сути да, узлы сплетнячают между собой и переговариваются примерно так: «есть узел такой-то, у него есть канал с этим», а другой узел: «а я ещё знаю, что у него есть канал с таким-то» и так далее. В протоколе есть защиты, чтобы кто-то не насплетничал неверную информацию. Итак, ваш кошелёк тоже слушает сплетни и он формирует большую картину узлов и знает, кто с кем соединён. Благодаря этому, когда вы хотите послать платёж получателю «C», кошелёк найдёт несколько путей, в том путь, как в нашем примере: «Вы» —> «A» —> «B» —> «C».

Допустим, ваш кошелёк определил маршрут до «C» и знает, что нужно отправить платёж через «B» и «C». Что дальше? Сейчас попробую описать схему максимально просто и так, как она на самом деле выполняется, опуская многие технические детали.

Платёж начинается с инвойса

Итак, допустим мы хотим отправить платёж размером 1000 сатоши. Получатель должен создать для нас сначала «инвойс» (англ.: invoice) — специальное цифровое сообщение, где есть такие данные, как: сумма для получения, публичный ключ получателя, описание (за что платёж) и самое главное ещё — «хэш секрета» (секрет называется в молнии preimage, в переводе — прообраз платежа). Иногда я буду называть его preimage, чтобы вы привыкали к правильным названиям ;-) Итак, прообраз — это длинное секретное число, которое нам просто нереально отгадать, оно случайно и придумано получателем платежа именно для данного инвойса. Получатель хранит его в тайне до тех пор, пока кто-то не пришлёт ему по любому из каналов, которые у него есть, платёж нужной суммы.

Деньги в обмен на секрет

После выставления инвойса получатель начинает ждать получения средств от какого либо узла, с которым у него есть каналы. Получение средств в обмен на секрет. Чтобы все узлы, участвующие в передаче, в том числе вы, как плательщик — понимали, какой именно платёж и к какому секрету он относится — для этого и используется хэш секрета. Хеш — такая хитрая математическая штука, которая с одной стороны не раскрывает числа, над которым она была вычислена (секрет), но если у нас есть секрет — мы можем моментально математически проверить — а соответствует ли хэш этому секрету? То есть хэш — штука публичная, и служит она для проверки соответствия ракрытого секрета (прообраза платежа) этому хешу, а значит и инвойсу.

Готовим лук

Для передачи средств между несколькими узлами в протоколе используется так называемый формат пакета луковицы. Он действительно похож на луковицу тем, что имеет внутри себя упакованные данные, как слои лука. Каждый слой закриптован для определённого ключа, кому он адресован. Снять верхний слой и прочитать, что в нём — может только тот, кому адресован этот слой (через криптографию и механизм «открытых ключей»). Затем тот, кто снял верхний слой, выполняет записанные указания и посылает остаток от луковицы дальше. Другой узел опять снимает верхний слой (уже второй) и посылает луковицу дальше и так далее. Заканчивается всё последним глубинным слоем, который читает получатель платежа. В сети Lightning эти луковицы используются везде, даже не только для передачи средств, но и для сообщения об ошибках всем узлам по пути.

Итак, наш кошелёк знает все публичные ключи для узлов «A», «B» и «C». Он начинает формировать луковицу с глубинного слоя и сначала формирует сообщение для узла «C» (в нашем случае — это получатель, но ) примерно такое: «Вот тебе сумма 1000 сатоши для хэша такого-то. Если всё OK — отдай тому, кто передал тебе это сообщение свой секрет — прообраз платежа. Такое сообщение получит узел «C», когда до него докатится лукивица от узла «B». Затем, это сообщение оборачивается во второй слой выше — для узла «B» для ключа того же узла. Там уже сообщение будет примерно такого плана: «Вот тебе сумма 1002 сатоши для хэша такого-то. Возьми себе из них 2 сатоши, а 1000 сатоши передай дальше — узлу «C» (стоит заметить тот факт, что все узлы в цепочке, включая предпоследний, в нашем случае это узел «B» — не знают, кому предназначен финальный пакет и кто является конечным получателем, и даже узел «B» может только предполагать — узел «C» либо дальше пересылает платёж кому то, либо он конечный получатель — это важно для вашей приватности). Затем ваш кошелёк создаёт третий слой для узла «A»: «Вот тебе сумма 1005 сатоши для хэша такого-то. Возьми себе из них 3 сатоши, а 1002 сатоши передай дальше — узлу «B». После пакет передачи готов для отправки.

Катим луковицу к получателю

Чтобы ни у кого не было мотивации обмануть — в протоколе используется многоходовая схема защиты с временными «окнами» блокировок, после истечения которых, если платёж не был передан дальше, он будет возвращён через on-chain сеть биткойна, при этом канал будет закрыт, а средства вернутся через блокчейн биткойна. Для этого там тоже используется тот самый хеш прообраза платежа. Сам факт того, что получатель получил средства, определяется раскрытием его, который по цепочке передаётся от получателя средств к отправителю (опять же через луковицу, но уже сформированную получателем платежа). Этот секрет сразу же становится «достоянием общественности» после получения платежа и используется в каналах как «разменная монета» за переданный платёж в каждом канале. Если кто либо попытается скрыть секрет — в этом никакого смысла не будет, потому что средства за платёж можно вернуть через блокчейн биткойна — везде фигурирует preimage, поэтому как ни старайся присвоить себе незаконно средства — придётся раскрывать preimage миру, а как только ты это сделаешь — у обманутого оппонента припасена разменная монета на основе preimage, чтобы вернуть себе украденные средства. Поэтому, самый правильный и лучший способ для всех участников — передать прообраз от получателя платежа к отправителю как можно скорее, и закрыть сделки для дальнейших платежей. Относительный размер комиссий (сколько % от суммы) за платежи каждая сторона назначает самостоятельно и транслирует «в граф» сети. То есть отправляя платёж, ваш кошелёк заранее знает, кто сколько берёт комиссий, сам вычисляет сумму и вписывает её в луковицу. Если где либо возникает ошибка — например не хватает средств «в кружке» для передачи дальше, либо кто-то стал недоступен в онлайне или изменились комиссии, а мы их не учли — тогда самый дальный узел формирует луковицу в направлении отправителя платежа с кодами ошибок и кидает её обратно. В таком случае все стороны заинтересованы как можно быстрее сообщить об ошибке, переделать смарт-контракты (многоходовые криптографические схемы) каналов для дальнейших платежей.

Подведём итоги

  1. Фактом оплаты в Lightning (кстати не только в маршрутизации, но и в самом простом примере с одним каналом) является раскрытие секрета (прообраза, или preimage) получателем платежа (опять же через обратную луковицу). Этот секрет, после получения средств получателем, двигается в обратном направлении от узла к узлу, начиная от получателя к отправителю средств. Каждый узел, передавший платёж, узнаёт прообраз и обновляет данные канала или каналов с теми сторонами, с которыми он исполнял платёж (корректирует балансы с учётом отправленных сумм и причитающейся ему комиссии, если он маршрутизировал платёж). Так постепенно луковица ответа докатывается до отправителя платежа (в данном примере — до вас), который также узнаёт для себя прообраз и обновляёт баланс как урегулированный с ближайшим узлом (в данном примере — с узлом «A»). Как я писал ранее про хеш прообраза — благодаря тому, что перед оплатой известен хеш секрета, мы сразу можем легко проверить — соответствует ли данный нам preimage ранее известному нам хешу для него или нет (а то мало ли — вдруг нам «туфту» всякую подкинули, а не секретное число). Этот факт и проверяет каждый узел, когда получает и передаёт дальше эту информацию.
  2. Опираясь на факт, описанный пунктом ранее — можно легко доказать миру факт оплаты. Для этого миру надо предъявить инвойс и раскрытый секрет — прообраз платежа. На практике — это две строки, не очень длинные, и из них можно легко сделать вывод, что оплата была или нет: в инвойсе есть информация о получателе (публичный ключ узла, например это может быть узел магазина), также там есть сумма и хеш прообраза платежа, а представленный прообраз платежа должен «обращаться» в тот самый хеш, указанный в инвойсе. Поскольку прообразы всегда уникальны для каждого инвойса, и они также являются секретом, который раскрывается получателем только в случае полной оплаты счёта — то факт того, что вы предоставили кому либо этот секрет говорит о том, что средства были получены получателем в полном объёме.
  3. Каждый узел в передаче знает только такие данные — сколько он получает средств из входящего канала, и сколько он должен отправить средств дальше (в какой именно канал и какому узлу). Разница в средствах — это его комиссия за пересылку платежа. Если его устраивает комиссии и он может отправить средства дальше — всё отлично. Если нет, то он формирует пакет-ошибку и кидает его в обратном направлении. Это очень важная часть протокола — она обеспечивает гладкий процесс передачи средств и прячет чувствительные данные от каждого узла (узлы, участвующие в пересылке — не знают, кто кому платит).
  4. Важно понимать, за что и какой стороной берутся комиссии. Понять это просто, если представить путь передачи в виде стрелок от вас к получателю через узлы. Представили? Тогда комиссии применяются только теми узлами, в которых есть две стрелки — одна входящая и одна исходящая, и комиссия берётся только за исходящую стрелку. В нашем примере таких узла только два — «A» и «B». Именно у них в каждом две стрелки — в узел «A» входит одна стрелка от нас и выходит стрелка на узел «B» — значит узел «A» возьмёт себе комиссию с платежа, который уходит из него (исходящая стрелка). Точно также с узлом «B» — у него две стрелки — входящая в него от узла «A» и исходящая на узел «C» — в таком случае узел «B» берёт себе комиссию с «исходящей стрелки» — то есть с суммы, которую он передаёт на узел «C». Из этого правила следуют два следствия: если путь простой от плательщика до получателя — в нём не может быть комиссиий, так как нет узла, в котором бы были две стрелки (входящая и исходящая), и это логично: платёж идёт напрямую и брать с самого себя не логично. Также получатель не берёт и не может контролировать комиссию за входящий платёж просто даже потому, что протоколом это не предусмотрено (и слава богу!) — к тому же, смысла в этом нет никакого, так как если получатель хочет взять какую либо комиссию — он просто заложит её сумму в той самой сумме, которая прописывается в инвойсе. Остаётся единственный путь, когда берётся комиссия — когда между платёльщиком и получателем есть один или более пересылающих узлов — в таком случае каждый узел может взять только один раз комиссию в пересылке и только с «исходящего от него потока», так сказать. Причём он не может взять сколько хочет — сумма комиссии для каждого узла в пути прописывается отправителем, когда формируется луковица, и формируется она на основе данных из графа сети (куда, в свою очередь, каждый узел сети транслирует свои процентные ставки за передачу средств). Если, например, узел изменил свои комиссии, а отправитель этого не увидел — отправитель посчитает комиссии по старым ставкам, и уже дело промежуточного узла решить — согласится с такой отправкой и взять назначенные комиссии или нет (сплошная демократия, так сказать ;-)

Понравилась статья? Поделиться с друзьями:

Добавить комментарий

%d такие блоггеры, как: