Skip to content

feat(lightning): fund-safety hardening and protocol completion#92

Closed
coreyphillips wants to merge 6 commits into
synonymdev:masterfrom
coreyphillips:feat/lightning
Closed

feat(lightning): fund-safety hardening and protocol completion#92
coreyphillips wants to merge 6 commits into
synonymdev:masterfrom
coreyphillips:feat/lightning

Conversation

@coreyphillips

Copy link
Copy Markdown
Collaborator

Summary

This branch adds a from-scratch, BOLT-compliant Lightning Network implementation under src/lightning/, and this update lands a round of adversarial fund-safety hardening on top of the accumulated feature work.

Fund-safety fixes

Each is a loss-of-funds hardening against an untrusted peer and an on-chain adversary, and ships with a regression test:

  • Revoked-breach classification. A revoked commitment that shares our current local commitment index (reachable mid-round when we are the round initiator) was classified as our own commitment and never penalized. Ownership is now decided by matching the actual to_local scripts, so the breach is routed to the penalty path.
  • On-chain preimage claim after a peer force-close. A preimage learned after the peer broadcasts their current commitment is now converted into an on-chain received-HTLC claim, instead of being cached and lost to the peer's timeout path (forwarded-HTLC loss).
  • Lessor lease persistence. isLessor/leaseExpiry are now serialized, so a lessor's lease-locked commitment rebuilds byte-identically after a restart and its cached remote signature stays valid. Covered by a serialize/restore/rebuild byte-parity invariant test.
  • Live force-close feerate. All force-close entry points now resolve a live, urgency-bumped feerate from the fee estimator instead of a hardcoded value that a routine fee spike would strand.
  • Anchor HTLC re-fee-bump. Stuck anchor second-level HTLC transactions are re-fee-attached, so a post-broadcast fee spike cannot forfeit the HTLC race.
  • Liquidity-ads lease check. A lease is rejected unless the seller funds at least the requested amount, so we never pay a lease fee for liquidity that is never delivered.
  • Reorg recovery. Output watches are retained after a spend, and our penalty or HTLC-success is re-broadcast if a reorg evicts it.
  • Low-S enforcement. Remote commitment and HTLC signatures we place into transactions we broadcast are now rejected if non-canonical (high-S), which would otherwise make those transactions non-standard.
  • Leased-taproot rejection. Script-enforced-lease and simple-taproot are mutually exclusive commitment types, so the combination is now rejected at negotiation.

Two additional audited surfaces (cooperative-close output validation and inbound update_fee bounds) were reviewed and found already safe; no changes were required there.

Feature scope on this branch

Simple taproot channels (BOLT 3/5 scripts plus MuSig2 co-signing), hold invoices and async payments, dual funding with liquidity ads (bLIP-0051), BOLT 12 offers, and route blinding.

Testing

  • Full non-interop lightning suite: 2916 passing.
  • BOLT conformance vectors: 42 passing.
  • tsc --noEmit: clean.

coreyphillips and others added 6 commits June 23, 2026 15:19
Implement a full Lightning node on top of beignet's on-chain wallet:
Noise transport, channel state machine, on-chain monitoring, gossip and
routing, invoices, and onion payments, plus a CLI/HTTP daemon and
AI-agent tooling.

Protocol coverage:
- BOLT 1/8: Noise transport, init, peer management
- BOLT 2: channel open/close, HTLC lifecycle, channel reestablish
- BOLT 3: key derivation, commitment & HTLC txs, anchor outputs
- BOLT 4: Sphinx onion, payment forwarding, failure handling
- BOLT 5: force-close, output resolution, sweeps, penalty txs
- BOLT 7: gossip sync, network graph, pathfinding, mission control
- BOLT 11: invoice encode/decode/sign
- Extensions: dual funding (v2), splicing, zero-conf, keysend,
  quiescence, SCID aliases, rapid gossip sync

Anchor channels are the default channel type, made force-close-safe by
wallet-funded fee bumping: zero-fee second-level HTLC txs get a wallet
fee input attached, and commitments are CPFP-bumped via the local anchor
output. Both paths validated end-to-end against LND on regtest.

Adds a CLI/HTTP daemon (OpenAPI spec, webhooks, payment queue, rate
limiting) and AI-agent ergonomics (liquidity/fee advisors, structured
logs, mainnet-readiness checks).

Tested: 2,740 lightning + 720 CLI unit tests, plus interop suites
against LND, CLN, and Eclair.
… test coverage

Startup reliability (mainnet):
- electrum: de-dupe concurrent connectToElectrum() calls via a shared
  in-flight promise, so racing startup connects no longer clobber
  rn-electrum-client's shared global socket and emit a spurious
  "Unable to connect to Electrum server."
- electrum-backend: listUnspent() fails fast when disconnected instead of
  probing a not-yet-open socket (which logged a raw "Connection to server
  lost" stack trace from the client library).
- beignet-node: defer fallback-fund recovery until Electrum is actually
  connected; clean the wait timer up on shutdown.
- lightning-node: emit node:ready as soon as a channel reaches NORMAL, so
  waitForReady() no longer spuriously times out when a single stored peer
  is offline/slow to reconnect.

Test coverage:
- BOLT 3/4/8/11 conformance vectors and tests under tests/lightning/conformance.
- HTLC-claim crypto tests and a mempool interop test for on-chain claims.
- chain-resolver, htlc-safety and socks5 test additions.

Accompanying channel/commitment/onion/serialization hardening across the
chain, channel and transport modules.
BOLT 7 requires channel_update and node_announcement timestamps to be
greater than zero but does not specify handling for violations. Reject
zero-timestamp messages in decodeChannelUpdateMessage and
decodeNodeAnnouncementMessage so they never reach the network graph,
matching the parse-time fix LND shipped in v0.20.1-beta for the
zero-timestamp gossip DoS class.

Also wrap the channel_announcement/node_announcement/channel_update
gossip handlers in try/catch so a malformed or rejected message is
dropped silently instead of propagating up through the peer message
dispatcher (which is not itself guarded).

Adds regression tests for both decoders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apply eslint --fix to resolve the 45 pre-existing prettier/prettier
errors flagged by the PR lint check. Formatting-only changes; no logic
affected. `lint:check` now exits clean (0 errors, warnings only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adversarial fund-safety fixes (loss-of-funds hardening against an untrusted
peer and an on-chain adversary), each with regression tests:

- Classify a revoked commitment that shares our local commitment index as a
  breach via script-based ownership disambiguation, so it is penalized
  instead of being mistaken for our own commitment and left unpunished.
- Claim a received HTLC on-chain when the preimage is learned after the peer
  force-closes with their current commitment, preventing loss of a forwarded
  HTLC to the peer's timeout path.
- Persist isLessor/leaseExpiry so a lessor's lease-locked commitment rebuilds
  byte-identically after a restart and stays broadcastable (with a
  serialize, restore, rebuild byte-parity invariant test).
- Resolve a live, urgency-bumped force-close feerate from the fee estimator
  at every force-close entry point instead of a hardcoded rate.
- Re-fee-bump stuck anchor second-level HTLC transactions so a post-broadcast
  fee spike cannot strand the HTLC race.
- Reject a liquidity-ads lease unless the seller funds at least the requested
  amount.
- Recover from reorgs: retain output watches after a spend and re-broadcast
  our penalty or HTLC-success if a reorg evicts it.
- Enforce low-S (BIP146) on the remote commitment and HTLC signatures we
  place in transactions we broadcast.
- Reject the invalid script-enforced-lease plus simple-taproot channel-type
  combination at negotiation (they are mutually exclusive commitment types).

Also lands the accumulated Lightning feature work on this branch: simple
taproot channels (BOLT 3/5 scripts and MuSig2 co-signing), hold invoices and
async payments, dual funding with liquidity ads (bLIP-0051), BOLT 12 offers,
and route blinding, with substantially expanded test coverage.

Verification: full non-interop lightning suite 2916 passing, BOLT conformance
42 passing, tsc clean.
The initial anchor commitment CPFP was one-shot, so a fee spike after the
force-close broadcast could pin the commitment and block every second-level
HTLC claim (each spends a commitment output). ChannelManager now retains each
anchor force-close CPFP and the node re-issues it at the current live feerate
every few blocks, while the commitment is still unconfirmed, until it confirms.

Full non-interop lightning suite 2919 passing, BOLT conformance 42 passing,
tsc clean.
@coreyphillips

Copy link
Copy Markdown
Collaborator Author

Closing: this should target the coreyphillips/beignet fork, not the upstream. Re-opening there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant