beignet 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +25 -13
  2. package/dist/cli/beignet-node.js +103 -4
  3. package/dist/cli/beignet-node.js.map +1 -1
  4. package/dist/cli/cli.js +16 -6
  5. package/dist/cli/cli.js.map +1 -1
  6. package/dist/cli/config.js +17 -2
  7. package/dist/cli/config.js.map +1 -1
  8. package/dist/cli/daemon.js +19 -4
  9. package/dist/cli/daemon.js.map +1 -1
  10. package/dist/cli/openapi.js +56 -11
  11. package/dist/cli/openapi.js.map +1 -1
  12. package/dist/cli/webhooks.js +63 -11
  13. package/dist/cli/webhooks.js.map +1 -1
  14. package/dist/lightning/node/lightning-node.js +216 -41
  15. package/dist/lightning/node/lightning-node.js.map +1 -1
  16. package/dist/lightning/node/types.js.map +1 -1
  17. package/dist/lightning/swaps/client.js +129 -1
  18. package/dist/lightning/swaps/client.js.map +1 -1
  19. package/dist/lightning/swaps/engine-common.js +97 -0
  20. package/dist/lightning/swaps/engine-common.js.map +1 -0
  21. package/dist/lightning/swaps/index.js +2 -0
  22. package/dist/lightning/swaps/index.js.map +1 -1
  23. package/dist/lightning/swaps/ledger.js +15 -15
  24. package/dist/lightning/swaps/ledger.js.map +1 -1
  25. package/dist/lightning/swaps/messages.js +148 -3
  26. package/dist/lightning/swaps/messages.js.map +1 -1
  27. package/dist/lightning/swaps/reverse-engine.js +25 -80
  28. package/dist/lightning/swaps/reverse-engine.js.map +1 -1
  29. package/dist/lightning/swaps/submarine-engine.js +1606 -0
  30. package/dist/lightning/swaps/submarine-engine.js.map +1 -0
  31. package/dist/types/cli/beignet-node.d.ts +9 -1
  32. package/dist/types/cli/types.d.ts +49 -1
  33. package/dist/types/cli/webhooks.d.ts +3 -0
  34. package/dist/types/lightning/node/lightning-node.d.ts +11 -2
  35. package/dist/types/lightning/node/types.d.ts +19 -0
  36. package/dist/types/lightning/swaps/client.d.ts +31 -1
  37. package/dist/types/lightning/swaps/engine-common.d.ts +10 -0
  38. package/dist/types/lightning/swaps/index.d.ts +2 -0
  39. package/dist/types/lightning/swaps/ledger.d.ts +14 -1
  40. package/dist/types/lightning/swaps/messages.d.ts +51 -2
  41. package/dist/types/lightning/swaps/reverse-engine.d.ts +1 -0
  42. package/dist/types/lightning/swaps/submarine-engine.d.ts +144 -0
  43. package/docs/AI_AGENT_GUIDE.md +5 -1
  44. package/package.json +2 -2
package/README.md CHANGED
@@ -497,23 +497,35 @@ node can run for others, each an explicit opt-in switched on with an exact
497
497
  The SSE stream carries `ffor:state`, `ffor:settled`, `ffor:delegated-failed`,
498
498
  `ffor:enforce` and the witness and issuer events.
499
499
 
500
- ### Reverse swaps (Lightning to on-chain)
500
+ ### Swaps (Lightning to on-chain, and on-chain to Lightning)
501
501
 
502
- A beignet node can serve reverse swaps to any Lightning peer (issue #737): the
503
- peer pays a hold invoice, this node funds a P2WSH contract the peer claims on
504
- chain with its preimage, and the claim settles the hold. The role is an
505
- explicit opt-in switched on with an exact `true`, because it locks this node's
506
- own coins in contracts for peers:
502
+ A beignet node can serve swaps to any Lightning peer in both directions.
503
+ Reverse (issue #737): the peer pays a hold invoice, this node funds a P2WSH
504
+ contract the peer claims on chain with its preimage, and the claim settles
505
+ the hold. Submarine (issue #743): the peer locks coins in a P2WSH contract,
506
+ this node pays the peer's own invoice under an absolute HTLC expiry ceiling,
507
+ and the preimage that payment reveals claims the coins. Each direction is an
508
+ explicit opt-in switched on with an exact `true`, because it puts this node's
509
+ own funds at risk for peers:
507
510
 
508
511
  | Env | Role |
509
512
  |---|---|
510
513
  | `BEIGNET_SWAPS` | Serve reverse swaps. `BEIGNET_SWAP_FLAT_FEE_SAT` and `BEIGNET_SWAP_FEE_PPM` price them; `BEIGNET_SWAP_MIN_SAT`, `BEIGNET_SWAP_MAX_SAT`, `BEIGNET_SWAP_MAX_EXPOSURE_SAT` and `BEIGNET_SWAP_MAX_CONCURRENT` cap what is at risk; `BEIGNET_SWAP_REFUND_DELTA_BLOCKS`, `BEIGNET_SWAP_FUNDING_CONFS` and `BEIGNET_SWAP_RESOLUTION_CONFS` set the timing. `GET /swaps/status`, `GET /swaps`, `POST /swaps/cancel`. |
511
-
512
- The provider funds only against the complete committed MPP set of the hold
513
- invoice, settles the hold the moment a claim reveals the preimage (mempool
514
- included), and cancels the hold only after its own refund has confirmed to
515
- policy depth; never because the refund height passed. The SSE stream carries
516
- `swap:created` through `swap:settled`, `swap:refunded` and `swap:exposed`.
514
+ | `BEIGNET_SWAP_SUBMARINE` | With `BEIGNET_SWAPS`, also serve submarine swaps (on-chain to Lightning): a peer locks coins in a contract, this node pays the peer's invoice under an absolute HTLC expiry ceiling and claims the coins with the preimage. `BEIGNET_SWAP_CLAIM_SAFETY_BLOCKS`, `BEIGNET_SWAP_PAYMENT_MAX_FEE_PPM`, `BEIGNET_SWAP_CLAIM_BUMP_INTERVAL_BLOCKS` and `BEIGNET_SWAP_SUBMARINE_REFUND_DELTA_BLOCKS` set the direction's margins; the fee and exposure caps above apply to both. |
515
+
516
+ The reverse provider funds only against the complete committed MPP set of
517
+ the hold invoice, settles the hold the moment a claim reveals the preimage
518
+ (mempool included), and cancels the hold only after its own refund has
519
+ confirmed to policy depth; never because the refund height passed. The
520
+ submarine provider pays only once the peer's funding has confirmed to policy
521
+ depth and been re-verified unspent immediately before the dispatch, binds
522
+ every HTLC of the payment to `refundHeight` minus its claim margins, judges
523
+ the payment by the node's own HTLC view (never by a wall clock or a failed
524
+ record while an HTLC is out), and persists its claim before broadcasting it.
525
+ The SSE stream carries `swap:created` through `swap:settled`, `swap:refunded`
526
+ and `swap:exposed` for the reverse direction and `swap:funding-seen`,
527
+ `swap:paying`, `swap:preimage`, `swap:claim-broadcast`,
528
+ `swap:claim-confirmed` and `swap:payment-failed` for the submarine one.
517
529
 
518
530
  ## Protocol layer (advanced)
519
531
 
@@ -616,7 +628,7 @@ LightningNode High-level API (EventEmitter)
616
628
  | `recovery/` | Safety transition layer: atomic persistence, the durable outbound-message outbox, the opt-in hash-chained recovery journal, and the peer_storage Recovery Capsule |
617
629
  | `liquidity/` | JIT channel receive (LSP role): intercept SCIDs, held HTLCs, zero-conf open or splice, then forward; the opening fee is skimmed off the delivery for wallets that accept it, or charged to the sender through the invoice hint (hop mode) for wallets that cannot settle a short HTLC |
618
630
  | `direct-funding/` | Third-party direct funding: the signed payment request envelope, sealed frames, protocol messages, outstanding-request store, the transport registry with its direct-peer, onion and blind-relay lanes, the receiver engine that turns a payer's offered UTXO into channel funding, and the payer engine that verifies and signs it |
619
- | `swaps/` | Swaps: the P2WSH HTLC contract, claim/refund transactions, preimage extraction, admission policies, the durable swap ledger, the chain resolver, the wire protocol, and the reverse swap provider engine (Lightning to on-chain) |
631
+ | `swaps/` | Swaps: the P2WSH HTLC contract, claim/refund transactions, preimage extraction, admission policies, the durable swap ledger, the chain resolver, the wire protocol, and the swap provider engines (reverse: Lightning to on-chain; submarine: on-chain to Lightning) |
620
632
  | `l402/` | L402 (Lightning HTTP 402) client: challenge parsing, macaroon reading, paid credentials |
621
633
  | `node/` | LightningNode orchestrator, the main protocol-layer entry point |
622
634
  | `wallet/` | WalletFundingProvider, adapts the on-chain Wallet for auto-funded opens |
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.BeignetNode = exports.jitInvoiceError = exports.jsonSafeEvent = exports.jitReceiveRefusal = exports.leaseRatesRefusal = exports.clampDirectFundingMinimum = exports.MAX_ASYNC_SPEND_CLAIMS = exports.ASYNC_SPEND_CLAIM_TTL_MS = exports.jsonToRouteHops = exports.routeHopsToJson = exports.spliceRefusalError = exports.decodeOfferInput = exports.paymentSpendSats = exports.spendLimitSats = exports.decodeInvoiceInput = exports.parseScid = exports.formatScid = exports.gossipPrimeLatch = exports.defaultDataDirForMnemonic = exports.parseRecoveryMode = void 0;
26
+ exports.BeignetNode = exports.jitInvoiceError = exports.holdInvoiceEvent = exports.jsonSafeEvent = exports.jitReceiveRefusal = exports.leaseRatesRefusal = exports.clampDirectFundingMinimum = exports.MAX_ASYNC_SPEND_CLAIMS = exports.ASYNC_SPEND_CLAIM_TTL_MS = exports.jsonToRouteHops = exports.routeHopsToJson = exports.spliceRefusalError = exports.decodeOfferInput = exports.paymentSpendSats = exports.spendLimitSats = exports.decodeInvoiceInput = exports.parseScid = exports.formatScid = exports.gossipPrimeLatch = exports.defaultDataDirForMnemonic = exports.parseRecoveryMode = void 0;
27
27
  const path = __importStar(require("path"));
28
28
  const fs = __importStar(require("fs"));
29
29
  const net = __importStar(require("net"));
@@ -430,6 +430,15 @@ function jsonSafeEvent(value) {
430
430
  return value;
431
431
  }
432
432
  exports.jsonSafeEvent = jsonSafeEvent;
433
+ function holdInvoiceEvent(e) {
434
+ return {
435
+ paymentHash: e.paymentHash.toString('hex'),
436
+ state: e.state,
437
+ heldAmountMsat: e.heldAmountMsat.toString(),
438
+ htlcCount: e.htlcCount
439
+ };
440
+ }
441
+ exports.holdInvoiceEvent = holdInvoiceEvent;
433
442
  function jitInvoiceError(err) {
434
443
  if (err instanceof errors_1.BeignetError)
435
444
  return err;
@@ -968,6 +977,29 @@ class BeignetNode extends events_1.EventEmitter {
968
977
  refundDeltaBlocks: opts.swaps.refundDeltaBlocks
969
978
  }
970
979
  }
980
+ : {}),
981
+ ...(opts.swaps.submarine === true
982
+ ? {
983
+ submarine: {
984
+ enabled: true,
985
+ ...(opts.swaps.claimSafetyBlocks !== undefined
986
+ ? { claimSafetyBlocks: opts.swaps.claimSafetyBlocks }
987
+ : {}),
988
+ ...(opts.swaps.paymentMaxFeePpm !== undefined
989
+ ? { paymentMaxFeePpm: opts.swaps.paymentMaxFeePpm }
990
+ : {}),
991
+ ...(opts.swaps.claimBumpIntervalBlocks !== undefined
992
+ ? {
993
+ claimBumpIntervalBlocks: opts.swaps.claimBumpIntervalBlocks
994
+ }
995
+ : {}),
996
+ ...(opts.swaps.submarineRefundDeltaBlocks !== undefined
997
+ ? {
998
+ refundDeltaBlocks: opts.swaps.submarineRefundDeltaBlocks
999
+ }
1000
+ : {})
1001
+ }
1002
+ }
971
1003
  : {})
972
1004
  }
973
1005
  : undefined,
@@ -1151,6 +1183,29 @@ class BeignetNode extends events_1.EventEmitter {
1151
1183
  });
1152
1184
  this.emit('invoice:settled', info);
1153
1185
  });
1186
+ this.node.on('hold:accepted', (e) => {
1187
+ const info = holdInvoiceEvent(e);
1188
+ this.emit('hold:accepted', info);
1189
+ this.log('info', 'Hold invoice accepted', {
1190
+ paymentHash: info.paymentHash,
1191
+ heldAmountMsat: info.heldAmountMsat,
1192
+ htlcCount: info.htlcCount
1193
+ });
1194
+ });
1195
+ this.node.on('hold:settled', (e) => {
1196
+ this.emit('hold:settled', holdInvoiceEvent(e));
1197
+ });
1198
+ this.node.on('hold:cancelled', (e) => {
1199
+ this.emit('hold:cancelled', {
1200
+ ...holdInvoiceEvent({
1201
+ paymentHash: e.paymentHash,
1202
+ state: 'CANCELLED',
1203
+ heldAmountMsat: e.heldAmountMsat,
1204
+ htlcCount: e.htlcsFailed
1205
+ }),
1206
+ reason: e.reason
1207
+ });
1208
+ });
1154
1209
  this.node.on('htlc:forward', (inChannelId, outChannelId, amountMsat, paymentHash) => {
1155
1210
  this.log('info', 'HTLC forward', {
1156
1211
  paymentHash: paymentHash.toString('hex'),
@@ -1248,7 +1303,27 @@ class BeignetNode extends events_1.EventEmitter {
1248
1303
  'ffor:witness-recorded',
1249
1304
  'ffor:witness-released',
1250
1305
  'ffor:issuer-provisioned',
1251
- 'ffor:issuer-issued'
1306
+ 'ffor:issuer-issued',
1307
+ 'swap:created',
1308
+ 'swap:held',
1309
+ 'swap:funding',
1310
+ 'swap:funded',
1311
+ 'swap:claimed',
1312
+ 'swap:settled',
1313
+ 'swap:refund-broadcast',
1314
+ 'swap:refunded',
1315
+ 'swap:hold-cancelled',
1316
+ 'swap:exposed',
1317
+ 'swap:failed',
1318
+ 'swap:funding-seen',
1319
+ 'swap:funding-lost',
1320
+ 'swap:paying',
1321
+ 'swap:payment-unresolved',
1322
+ 'swap:preimage',
1323
+ 'swap:claim-broadcast',
1324
+ 'swap:claim-confirmed',
1325
+ 'swap:payment-failed',
1326
+ 'swap:cancelled'
1252
1327
  ]) {
1253
1328
  this.node.on(evt, (data) => {
1254
1329
  this.emit(evt, jsonSafeEvent(data));
@@ -4215,7 +4290,26 @@ class BeignetNode extends events_1.EventEmitter {
4215
4290
  timeouts: status.timeouts,
4216
4291
  counts: status.counts,
4217
4292
  exposedSat: Number(status.exposedSat),
4218
- exposedCount: status.exposedCount
4293
+ exposedCount: status.exposedCount,
4294
+ submarine: status.submarine.enabled
4295
+ ? {
4296
+ enabled: true,
4297
+ fee: {
4298
+ flatFeeSat: Number(status.submarine.fee.flatFeeSat),
4299
+ feePpm: status.submarine.fee.feePpm
4300
+ },
4301
+ limits: {
4302
+ minSwapSat: Number(status.submarine.limits.minSwapSat),
4303
+ maxSwapSat: Number(status.submarine.limits.maxSwapSat),
4304
+ maxTotalExposureSat: Number(status.submarine.limits.maxTotalExposureSat),
4305
+ maxConcurrentSwaps: status.submarine.limits.maxConcurrentSwaps
4306
+ },
4307
+ timeouts: status.submarine.timeouts,
4308
+ counts: status.submarine.counts,
4309
+ exposedSat: Number(status.submarine.exposedSat),
4310
+ exposedCount: status.submarine.exposedCount
4311
+ }
4312
+ : { enabled: false }
4219
4313
  };
4220
4314
  }
4221
4315
  listSwaps(id) {
@@ -4422,7 +4516,12 @@ class BeignetNode extends events_1.EventEmitter {
4422
4516
  description: opts.description || '',
4423
4517
  expiry: opts.expiry,
4424
4518
  hold: true,
4425
- paymentHash: Buffer.from(opts.paymentHash, 'hex')
4519
+ paymentHash: Buffer.from(opts.paymentHash, 'hex'),
4520
+ ...(opts.minFinalCltvExpiry !== undefined
4521
+ ? {
4522
+ minFinalCltvExpiry: requireFinalCltvExpiry(opts.minFinalCltvExpiry)
4523
+ }
4524
+ : {})
4426
4525
  });
4427
4526
  const info = {
4428
4527
  bolt11: result.bolt11,