feat(lightning): deposits, withdrawals, and solvency behind a node interface
The node is an interface, so the code where money is actually at risk is tested against a fake that can be made to fail, stall, or lie. Plugging in Alby Hub is configuration, not new code. Crediting a deposit is idempotent by payment hash: a node reporting the same settlement twice must not mint money. Withdrawals debit before they pay, because a payment that succeeds while the ledger write fails loses money permanently, whereas the reverse is recoverable. Withdrawals above a threshold wait for a human, which bounds what a stolen session token can remove. 17 tests including concurrent settlement, concurrent double-spend, concurrent processors, failed payment refunds, fee caps, and solvency. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
39
migrations/0004_lightning.sql
Normal file
39
migrations/0004_lightning.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
-- Lightning deposits and withdrawals.
|
||||
--
|
||||
-- Invoices key on payment_hash, which is what makes crediting idempotent: a
|
||||
-- node that reports the same settlement twice cannot produce two credits.
|
||||
|
||||
CREATE TABLE lightning_invoices (
|
||||
payment_hash TEXT PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL REFERENCES accounts(id),
|
||||
amount_msat BIGINT NOT NULL CHECK (amount_msat > 0),
|
||||
bolt11 TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
expires_at TIMESTAMPTZ,
|
||||
-- Set exactly once, when the payment is credited to the ledger.
|
||||
credited_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX lightning_invoices_account_idx ON lightning_invoices (account_id, created_at DESC);
|
||||
CREATE INDEX lightning_invoices_pending_idx ON lightning_invoices (created_at)
|
||||
WHERE credited_at IS NULL;
|
||||
|
||||
CREATE TYPE withdrawal_status AS ENUM
|
||||
('queued', 'needs_approval', 'sending', 'paid', 'failed', 'rejected');
|
||||
|
||||
CREATE TABLE lightning_withdrawals (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL REFERENCES accounts(id),
|
||||
bolt11 TEXT NOT NULL,
|
||||
amount_msat BIGINT NOT NULL CHECK (amount_msat > 0),
|
||||
status withdrawal_status NOT NULL,
|
||||
payment_hash TEXT,
|
||||
fee_msat BIGINT,
|
||||
failure TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
resolved_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX lightning_withdrawals_account_idx ON lightning_withdrawals (account_id, id DESC);
|
||||
CREATE INDEX lightning_withdrawals_pending_idx ON lightning_withdrawals (id)
|
||||
WHERE status IN ('queued', 'needs_approval', 'sending');
|
||||
Reference in New Issue
Block a user