Compare commits

..

15 commits

Author SHA1 Message Date
Patrick Mulligan
0332a5a34f fix(lnd): allow self-payments for LNURL-withdraw
Some checks failed
Docker Compose Actions Workflow / test (push) Has been cancelled
When the user's wallet (e.g. Zeus) is connected to the same LND node
that LP uses, LNURL-withdraw fails because LND rejects the payment
with "no self-payments allowed". This is safe because LP always
decrements the user's balance before paying and refunds on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:12:45 -05:00
Patrick Mulligan
8fb8bb302f feat(extensions): pay from caller's balance via PayAppUserInvoice
When userPubkey is provided, resolve the ApplicationUser and call
applicationManager.PayAppUserInvoice instead of paymentManager.PayInvoice
directly. This ensures notifyAppUserPayment fires, sending
LiveUserOperation events via Nostr for real-time balance updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:12:45 -05:00
Patrick Mulligan
f7c06dec45 feat(withdraw): track creator pubkey on withdraw links
Store the Nostr pubkey of the user who creates a withdraw link so the
LNURL callback debits the correct user's balance instead of the app
owner's. Pass userPubkey through from RPC handler to WithdrawManager.

- Add creator_pubkey column (migration v4)
- Store creatorPubkey on link creation
- Pass creator_pubkey to payInvoice on LNURL callback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:12:45 -05:00
Patrick Mulligan
f1d8eb5383 feat: route Nostr RPC to extension methods
Initialize extension system before nostrMiddleware so registered
RPC methods are available. Extension methods (e.g. withdraw.createLink)
are intercepted and routed to the extension loader before falling
through to the standard nostrTransport.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:12:45 -05:00
Patrick Mulligan
c429b53f39 feat(withdraw): add HTTP API for creating withdraw links
Add POST /api/v1/withdraw/create endpoint to allow external apps (ATM,
web clients) to create LNURL-withdraw links via HTTP instead of RPC.

Changes:
- Add handleCreateWithdrawLink HTTP handler
- Fix route ordering: callback routes before wildcard :unique_hash
- Extract app_id from Authorization header (Bearer app_<id>)
- Use is_unique=false for simple single-use ATM links

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-01 17:12:45 -05:00
Patrick Mulligan
497ec7f375 feat(server): add CORS support for extension HTTP routes
Enable CORS on the extension HTTP server to allow cross-origin requests
from ATM apps and other web-based clients.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-01 17:12:45 -05:00
Patrick Mulligan
7ea16286e0 feat: integrate extension system with withdraw extension support
- Add extension loader initialization to startup
- Create mainHandlerAdapter to bridge mainHandler with extension context
- Mount extension HTTP routes on separate port (main port + 1)
- Configure EXTENSION_SERVICE_URL for LNURL link generation

The withdraw extension provides LUD-03 LNURL-withdraw support for
creating withdraw links that allow users to pull funds.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-01 17:12:45 -05:00
Patrick Mulligan
819a263e98 feat(extensions): add LNURL-withdraw extension
Implements LUD-03 (LNURL-withdraw) for creating withdraw links
that allow anyone to pull funds from a Lightning wallet.

Features:
- Create withdraw links with min/max amounts
- Quick vouchers: batch creation of single-use codes
- Multi-use links with wait time between uses
- Unique QR codes per use (prevents sharing exploits)
- Webhook notifications on successful withdrawals
- Full LNURL protocol compliance for wallet compatibility

Use cases:
- Faucets
- Gift cards / prepaid cards
- Tips / donations
- User onboarding

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-01 17:12:44 -05:00
Patrick Mulligan
74692a2102 fix: use fresh balance in PayAppUserInvoice notification
notifyAppUserPayment was sending the stale cached balance from the
entity loaded before PayInvoice decremented it. Update the entity's
balance_sats from the PayInvoice response so LiveUserOperation events
contain the correct post-payment balance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:12:44 -05:00
Patrick Mulligan
e8a787694e chore: update Docker build and dependencies
- Add .dockerignore for runtime state files (sqlite, logs, secrets)
- Bump Node.js base image from 18 to 20
- Add @types/better-sqlite3 dev dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:12:44 -05:00
Patrick Mulligan
23ce7e2614 fix: correct nip44v1 secp256k1 getSharedSecret argument types
The @noble/curves secp256k1.getSharedSecret expects Uint8Array arguments,
not hex strings. Use hex.decode() to convert the private and public keys.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-01 17:12:44 -05:00
Patrick Mulligan
e3b1f7580e feat(extensions): add getLnurlPayInfo to ExtensionContext
Enables extensions to get LNURL-pay info for users by pubkey,
supporting Lightning Address (LUD-16) and zap (NIP-57) functionality.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-01 17:12:44 -05:00
Patrick Mulligan
92b9b2564f docs(extensions): add comprehensive extension loader documentation
Covers architecture, API reference, lifecycle, database isolation,
RPC methods, HTTP routes, event handling, and complete examples.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-01 17:12:44 -05:00
Patrick Mulligan
c96cf26d0e feat(extensions): add extension loader infrastructure
Adds a modular extension system for Lightning.Pub that allows
third-party functionality to be added without modifying core code.

Features:
- ExtensionLoader: discovers and loads extensions from directory
- ExtensionContext: provides extensions with access to Lightning.Pub APIs
- ExtensionDatabase: isolated SQLite database per extension
- Lifecycle management: initialize, shutdown, health checks
- RPC method registration: extensions can add new RPC methods
- Event dispatching: routes payments and Nostr events to extensions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-01 17:12:44 -05:00
Patrick Mulligan
b1fd18d45c fix(lnd): wait for chain/graph sync before marking LND ready
Some checks failed
Docker Compose Actions Workflow / test (push) Has been cancelled
Warmup() previously only checked that LND responded to GetInfo(), but
did not verify syncedToChain/syncedToGraph. This caused LP to accept
requests while LND was still syncing, leading to "not synced" errors
on every Health() check. Now waits for full sync with a 10min timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:12:20 -05:00

View file

@ -142,15 +142,20 @@ export default class {
return new Promise<void>((res, rej) => { return new Promise<void>((res, rej) => {
const interval = setInterval(async () => { const interval = setInterval(async () => {
try { try {
await this.GetInfo() const info = await this.GetInfo()
if (!info.syncedToChain || !info.syncedToGraph) {
this.log("LND responding but not synced yet, waiting...")
return
}
clearInterval(interval) clearInterval(interval)
this.ready = true this.ready = true
res() res()
} catch (err) { } catch (err) {
this.log("LND is not ready yet, will try again in 1 second") this.log("LND is not ready yet, will try again in 1 second")
if (Date.now() - now > 1000 * 60) { }
rej(new Error("LND not ready after 1 minute")) if (Date.now() - now > 1000 * 60 * 10) {
} clearInterval(interval)
rej(new Error("LND not synced after 10 minutes"))
} }
}, 1000) }, 1000)
}) })