Разработка Lightning-бота для Telegram
Lightning Network — это не просто «быстрый Bitcoin». Это отдельный протокол с собственной моделью состояния, платёжными каналами, routing-алгоритмами и специфическими видами атак. Telegram-бот поверх Lightning — это UI-слой над этой инфраструктурой, и ошибка в интеграции стоит пользователям реальных денег.
Типичный запрос: «сделайте бота, чтобы пользователи могли получать и отправлять сатоши». За этим скрывается выбор архитектуры ноды, модель кастодиального хранения, управление ликвидностью каналов и логика обработки неудачных платежей.
Архитектура: кастодиальная vs non-custodial
Это первый и самый важный выбор, который определяет всё остальное.
Кастодиальная модель
Бот держит единую Lightning-ноду, пользователи — это просто записи в базе данных с балансами. Платежи между пользователями внутри бота — off-chain операции внутри вашей БД, без реальных Lightning-транзакций.
Преимущества: нет проблем с routing, мгновенные внутренние переводы, простая реализация. Недостатки: вы кастодиан, требуется лицензия в большинстве юрисдикций, пользователи вам доверяют — как биржа, только меньше регулирования.
Архитектура:
Telegram Bot → Node.js сервис → PostgreSQL (балансы) → LND/CLN нода (для внешних платежей)
Внутренний перевод — транзакция в PostgreSQL. Внешний вывод — реальный Lightning invoice через ноду. Пополнение — генерация invoice через ноду, мониторинг payment.
Non-custodial через LSP
Lightning Service Provider (LSP) — сервис, который управляет каналами пользователя. Пользователь держит собственные ключи, LSP обеспечивает ликвидность. Протокол LSPS0-LSPS2 стандартизирует это взаимодействие.
Реализация сложнее: нужно интегрироваться с LSP API (Breez SDK, LDK-node), управлять channel open/close, объяснять пользователям концепцию каналов. Для Telegram-бота это обычно избыточно.
Практический выбор для большинства проектов: кастодиальная модель с прозрачной коммуникацией пользователям, что бот кастодиальный.
Lightning-нода: LND vs Core Lightning
LND (Lightning Network Daemon)
Go, разработка Lightning Labs. gRPC API с хорошей документацией. Самый популярный для интеграций — больше SDK и примеров.
import { AuthenticatedLnd } from "lightning";
import { createInvoice, payViaRoutes, getWalletInfo } from "lightning";
// Создание invoice для пополнения
const { request, id } = await createInvoice({
lnd,
tokens: 10000, // сатоши
description: `Deposit for user ${userId}`,
expires_at: new Date(Date.now() + 3600 * 1000).toISOString(),
});
Пакет lightning (npm) — типизированная обёртка над LND gRPC. Значительно удобнее raw gRPC.
Core Lightning (CLN)
C, разработка Blockstream. Более модульная архитектура через plugin-систему. JSON-RPC API. Меньше экосистемы, но производительнее на большом количестве каналов.
Для большинства Telegram-ботов с объёмом до 10k пользователей — разницы нет. Выбирайте по знакомости стека и наличию документации.
Управление ликвидностью каналов
Главная операционная проблема Lightning-бота — ликвидность. У Lightning-канала есть inbound capacity (сколько можно получить) и outbound capacity (сколько можно отправить). После открытия канала вся ликвидность на вашей стороне — вы можете отправлять, но не получать.
Для бота, который принимает пополнения пользователей, нужна inbound ликвидность:
Покупка inbound liquidity — сервисы типа Bitrefill Thor, Lightning Pool (LND), или ручные договорённости с routing-нодами. Вы платите провайдеру, он открывает канал к вам с балансом на его стороне.
Circular rebalancing — если баланс сместился (много outbound операций), можно провести круговую платёжку через сеть: отправить через A → B → C → себе. Платите routing fee, но перебалансируете канал.
// Мониторинг баланса канала
const channels = await getChannels({ lnd });
for (const channel of channels.channels) {
const localRatio = channel.local_balance / channel.capacity;
if (localRatio < 0.2) {
// Мало outbound — нужна rebalance
await alertOps(`Channel ${channel.id}: low outbound liquidity`);
}
if (localRatio > 0.8) {
// Мало inbound — не можем принимать платежи
await alertOps(`Channel ${channel.id}: low inbound liquidity`);
}
}
Для production бота нужен автоматический rebalancing или интеграция с сервисом ликвидности.
Обработка платежей в боте
Webhook vs polling для Telegram
Webhook предпочтительнее: меньше latency, нет limit на частоту запросов. Требует публичный HTTPS endpoint. Для production — обязательно webhook.
import { Telegraf } from "telegraf";
const bot = new Telegraf(BOT_TOKEN);
bot.command("deposit", async (ctx) => {
const userId = ctx.from.id.toString();
const invoice = await createDepositInvoice(userId, 0); // любая сумма
await ctx.reply(
`Ваш Lightning invoice для пополнения:\n\n\`${invoice.request}\`\n\nДействителен 1 час.`,
{ parse_mode: "Markdown" }
);
});
Мониторинг входящих платежей
LND предоставляет subscribeToInvoices — stream, который нотифицирует при каждом settlement:
const sub = subscribeToInvoices({ lnd });
sub.on("invoice_updated", async (invoice) => {
if (!invoice.is_confirmed) return;
const userId = await getUserByInvoiceId(invoice.id);
if (!userId) return;
await db.transaction(async (trx) => {
await trx("users")
.where({ id: userId })
.increment("balance_sats", invoice.received);
await trx("transactions").insert({
user_id: userId,
type: "deposit",
amount_sats: invoice.received,
lightning_id: invoice.id,
confirmed_at: new Date(),
});
});
await bot.telegram.sendMessage(userId,
`Получено ${invoice.received} sat. Баланс обновлён.`
);
});
Важно: операция должна быть идемпотентной — если воркер упал и перезапустился, повторная обработка одного invoice не должна зачислить деньги дважды. lightning_id с UNIQUE constraint — простая защита.
Отправка платежей (вывод)
Пользователь вставляет invoice, бот его оплачивает:
async function processWithdrawal(userId: string, invoiceStr: string) {
const decoded = await decodePaymentRequest({ lnd, request: invoiceStr });
// Проверки
if (decoded.tokens > user.balance_sats) throw new Error("Недостаточно средств");
if (decoded.expires_at < new Date()) throw new Error("Invoice истёк");
// Резервируем баланс ДО отправки
await db("users")
.where({ id: userId })
.decrement("balance_sats", decoded.tokens);
try {
const payment = await pay({ lnd, request: invoiceStr });
// Успешно, записываем транзакцию
await recordWithdrawal(userId, decoded.tokens, payment.id);
} catch (err) {
// Платёж не прошёл — возвращаем баланс
await db("users")
.where({ id: userId })
.increment("balance_sats", decoded.tokens);
throw new Error(`Платёж не прошёл: ${err.message}`);
}
}
Порядок операций критичен: сначала резервируем, потом отправляем. Иначе — double spend при параллельных запросах. Возврат при ошибке — обязательно.
Специфические атаки на Lightning-ботов
Invoice replay: пользователь присылает один и тот же invoice дважды. Защита — хранить все обработанные payment hashes, проверять перед обработкой.
Amount mismatch на депозитах: пользователь создал invoice на 1000 sat, кто-то отправил 999 sat (partial amount). LND по умолчанию принимает любую сумму если invoice без tokens. Всегда создавайте amount-less invoice или явно указывайте amount и проверяйте received.
Timing attack на вывод: параллельные запросы на вывод одновременно читают баланс и оба видят достаточно средств. Защита — оптимистичный лок через UPDATE users SET balance = balance - X WHERE balance >= X AND id = Y, проверять affected rows.
Стек и деплой
- Нода: LND 0.18.x на отдельном сервере/VPS, Bitcoin full node или Neutrino (легкий клиент)
- Backend: Node.js + TypeScript + Fastify
- База данных: PostgreSQL, таблицы: users, invoices, transactions, channels_log
- Мониторинг: Grafana + LND Prometheus exporter, алерты на channel offline, низкую ликвидность
- Резервные копии: SCB (Static Channel Backups) автоматически после каждого изменения канала — это обязательно, без этого при краше ноды потеряете средства пользователей
Для запуска Lightning-ноды нужно заложить на канальный депозит: минимум 0.1 BTC для небольшого бота, 0.5–1 BTC для production с нормальной ликвидностью.
Сроки разработки
MVP с кастодиальной моделью, deposit/withdraw, базовым p2p переводом — 3–4 недели. Production с авто-rebalancing, мониторингом ликвидности, multi-channel управлением, полным аудитом транзакций — 8–12 недель.







