beignet 0.2.0 → 0.4.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 (215) hide show
  1. package/README.md +306 -1
  2. package/dist/cli/auth.js +238 -0
  3. package/dist/cli/auth.js.map +1 -0
  4. package/dist/cli/beignet-node.js +1034 -57
  5. package/dist/cli/beignet-node.js.map +1 -1
  6. package/dist/cli/cli.js +1257 -18
  7. package/dist/cli/cli.js.map +1 -1
  8. package/dist/cli/config.js +45 -1
  9. package/dist/cli/config.js.map +1 -1
  10. package/dist/cli/daemon.js +398 -38
  11. package/dist/cli/daemon.js.map +1 -1
  12. package/dist/cli/errors.js +2 -0
  13. package/dist/cli/errors.js.map +1 -1
  14. package/dist/cli/index.js +6 -1
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/cli/openapi.js +1343 -89
  17. package/dist/cli/openapi.js.map +1 -1
  18. package/dist/cli/restore.js +89 -0
  19. package/dist/cli/restore.js.map +1 -0
  20. package/dist/cli/wallet-storage.js +34 -0
  21. package/dist/cli/wallet-storage.js.map +1 -0
  22. package/dist/electrum/index.js +107 -12
  23. package/dist/electrum/index.js.map +1 -1
  24. package/dist/index.js +1 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/lightning/advisor/fee-tuner.js +42 -0
  27. package/dist/lightning/advisor/fee-tuner.js.map +1 -0
  28. package/dist/lightning/advisor/index.js +9 -1
  29. package/dist/lightning/advisor/index.js.map +1 -1
  30. package/dist/lightning/advisor/rebalance-planner.js +55 -0
  31. package/dist/lightning/advisor/rebalance-planner.js.map +1 -0
  32. package/dist/lightning/backup/index.js +18 -0
  33. package/dist/lightning/backup/index.js.map +1 -0
  34. package/dist/lightning/backup/scb.js +42 -0
  35. package/dist/lightning/backup/scb.js.map +1 -0
  36. package/dist/lightning/chain/chain-monitor.js +27 -0
  37. package/dist/lightning/chain/chain-monitor.js.map +1 -1
  38. package/dist/lightning/chain/output-resolver.js +66 -15
  39. package/dist/lightning/chain/output-resolver.js.map +1 -1
  40. package/dist/lightning/chain/types.js +1 -0
  41. package/dist/lightning/chain/types.js.map +1 -1
  42. package/dist/lightning/channel/channel-manager.js +91 -24
  43. package/dist/lightning/channel/channel-manager.js.map +1 -1
  44. package/dist/lightning/channel/channel-state.js.map +1 -1
  45. package/dist/lightning/channel/channel.js +122 -4
  46. package/dist/lightning/channel/channel.js.map +1 -1
  47. package/dist/lightning/channel/commitment-builder.js +4 -7
  48. package/dist/lightning/channel/commitment-builder.js.map +1 -1
  49. package/dist/lightning/channel/dual-funding.js +6 -5
  50. package/dist/lightning/channel/dual-funding.js.map +1 -1
  51. package/dist/lightning/channel/splice-tx.js.map +1 -1
  52. package/dist/lightning/channel/types.js +3 -1
  53. package/dist/lightning/channel/types.js.map +1 -1
  54. package/dist/lightning/channel/validation.js +3 -3
  55. package/dist/lightning/channel/validation.js.map +1 -1
  56. package/dist/lightning/crypto/index.js +1 -0
  57. package/dist/lightning/crypto/index.js.map +1 -1
  58. package/dist/lightning/crypto/message-signing.js +116 -0
  59. package/dist/lightning/crypto/message-signing.js.map +1 -0
  60. package/dist/lightning/crypto/xchacha20poly1305.js +77 -0
  61. package/dist/lightning/crypto/xchacha20poly1305.js.map +1 -0
  62. package/dist/lightning/features/flags.js +1 -0
  63. package/dist/lightning/features/flags.js.map +1 -1
  64. package/dist/lightning/gossip/messages.js +37 -1
  65. package/dist/lightning/gossip/messages.js.map +1 -1
  66. package/dist/lightning/index.js +2 -1
  67. package/dist/lightning/index.js.map +1 -1
  68. package/dist/lightning/keys/signer.js +24 -3
  69. package/dist/lightning/keys/signer.js.map +1 -1
  70. package/dist/lightning/keys/wallet-keys.js +1 -0
  71. package/dist/lightning/keys/wallet-keys.js.map +1 -1
  72. package/dist/lightning/message/index.js +1 -0
  73. package/dist/lightning/message/index.js.map +1 -1
  74. package/dist/lightning/message/peer-storage.js +43 -0
  75. package/dist/lightning/message/peer-storage.js.map +1 -0
  76. package/dist/lightning/message/types.js +2 -0
  77. package/dist/lightning/message/types.js.map +1 -1
  78. package/dist/lightning/node/lightning-node.js +1593 -236
  79. package/dist/lightning/node/lightning-node.js.map +1 -1
  80. package/dist/lightning/node/types.js +18 -1
  81. package/dist/lightning/node/types.js.map +1 -1
  82. package/dist/lightning/offer/offer-manager.js +2 -5
  83. package/dist/lightning/offer/offer-manager.js.map +1 -1
  84. package/dist/lightning/offer/tlv.js +3 -1
  85. package/dist/lightning/offer/tlv.js.map +1 -1
  86. package/dist/lightning/storage/encryption.js +67 -0
  87. package/dist/lightning/storage/encryption.js.map +1 -0
  88. package/dist/lightning/storage/index.js +1 -0
  89. package/dist/lightning/storage/index.js.map +1 -1
  90. package/dist/lightning/storage/serialization.js +6 -2
  91. package/dist/lightning/storage/serialization.js.map +1 -1
  92. package/dist/lightning/storage/sqlite-storage.js +405 -33
  93. package/dist/lightning/storage/sqlite-storage.js.map +1 -1
  94. package/dist/lightning/watchtower/blob.js +221 -0
  95. package/dist/lightning/watchtower/blob.js.map +1 -0
  96. package/dist/lightning/watchtower/index.js +55 -0
  97. package/dist/lightning/watchtower/index.js.map +1 -0
  98. package/dist/lightning/watchtower/justice.js +272 -0
  99. package/dist/lightning/watchtower/justice.js.map +1 -0
  100. package/dist/lightning/watchtower/tower-connection.js +208 -0
  101. package/dist/lightning/watchtower/tower-connection.js.map +1 -0
  102. package/dist/lightning/watchtower/types.js +3 -0
  103. package/dist/lightning/watchtower/types.js.map +1 -0
  104. package/dist/lightning/watchtower/watchtower-client.js +472 -0
  105. package/dist/lightning/watchtower/watchtower-client.js.map +1 -0
  106. package/dist/lightning/watchtower/wtwire.js +201 -0
  107. package/dist/lightning/watchtower/wtwire.js.map +1 -0
  108. package/dist/logger.js +41 -0
  109. package/dist/logger.js.map +1 -0
  110. package/dist/shapes/electrum.js +32 -1
  111. package/dist/shapes/electrum.js.map +1 -1
  112. package/dist/shapes/wallet.js +11 -1
  113. package/dist/shapes/wallet.js.map +1 -1
  114. package/dist/transaction/index.js +163 -15
  115. package/dist/transaction/index.js.map +1 -1
  116. package/dist/types/cli/auth.d.ts +34 -0
  117. package/dist/types/cli/beignet-node.d.ts +148 -4
  118. package/dist/types/cli/daemon.d.ts +5 -0
  119. package/dist/types/cli/errors.d.ts +2 -0
  120. package/dist/types/cli/index.d.ts +1 -0
  121. package/dist/types/cli/restore.d.ts +10 -0
  122. package/dist/types/cli/types.d.ts +235 -1
  123. package/dist/types/cli/wallet-storage.d.ts +3 -0
  124. package/dist/types/electrum/index.d.ts +12 -0
  125. package/dist/types/electrum.js +1 -0
  126. package/dist/types/electrum.js.map +1 -1
  127. package/dist/types/index.d.ts +1 -0
  128. package/dist/types/lightning/advisor/fee-tuner.d.ts +20 -0
  129. package/dist/types/lightning/advisor/index.d.ts +4 -0
  130. package/dist/types/lightning/advisor/rebalance-planner.d.ts +12 -0
  131. package/dist/types/lightning/backup/index.d.ts +1 -0
  132. package/dist/types/lightning/backup/scb.d.ts +24 -0
  133. package/dist/types/lightning/chain/chain-monitor.d.ts +1 -0
  134. package/dist/types/lightning/chain/types.d.ts +1 -0
  135. package/dist/types/lightning/channel/channel-manager.d.ts +11 -0
  136. package/dist/types/lightning/channel/channel-state.d.ts +2 -0
  137. package/dist/types/lightning/channel/channel.d.ts +11 -5
  138. package/dist/types/lightning/channel/commitment-builder.d.ts +7 -7
  139. package/dist/types/lightning/channel/dual-funding.d.ts +2 -1
  140. package/dist/types/lightning/channel/splice-tx.d.ts +2 -2
  141. package/dist/types/lightning/channel/types.d.ts +2 -0
  142. package/dist/types/lightning/channel/validation.d.ts +1 -1
  143. package/dist/types/lightning/crypto/index.d.ts +1 -0
  144. package/dist/types/lightning/crypto/message-signing.d.ts +9 -0
  145. package/dist/types/lightning/crypto/xchacha20poly1305.d.ts +3 -0
  146. package/dist/types/lightning/features/flags.d.ts +1 -0
  147. package/dist/types/lightning/gossip/messages.d.ts +4 -0
  148. package/dist/types/lightning/index.d.ts +1 -0
  149. package/dist/types/lightning/keys/signer.d.ts +18 -1
  150. package/dist/types/lightning/keys/wallet-keys.d.ts +2 -1
  151. package/dist/types/lightning/message/index.d.ts +1 -0
  152. package/dist/types/lightning/message/peer-storage.d.ts +9 -0
  153. package/dist/types/lightning/message/types.d.ts +2 -0
  154. package/dist/types/lightning/node/lightning-node.d.ts +120 -4
  155. package/dist/types/lightning/node/types.d.ts +66 -1
  156. package/dist/types/lightning/storage/encryption.d.ts +11 -0
  157. package/dist/types/lightning/storage/index.d.ts +1 -0
  158. package/dist/types/lightning/storage/serialization.d.ts +2 -0
  159. package/dist/types/lightning/storage/sqlite-storage.d.ts +43 -3
  160. package/dist/types/lightning/storage/types.d.ts +62 -0
  161. package/dist/types/lightning/watchtower/blob.d.ts +46 -0
  162. package/dist/types/lightning/watchtower/index.d.ts +9 -0
  163. package/dist/types/lightning/watchtower/justice.d.ts +30 -0
  164. package/dist/types/lightning/watchtower/tower-connection.d.ts +34 -0
  165. package/dist/types/lightning/watchtower/types.d.ts +46 -0
  166. package/dist/types/lightning/watchtower/watchtower-client.d.ts +68 -0
  167. package/dist/types/lightning/watchtower/wtwire.d.ts +90 -0
  168. package/dist/types/logger.d.ts +17 -0
  169. package/dist/types/shapes/electrum.d.ts +3 -1
  170. package/dist/types/transaction/index.d.ts +6 -0
  171. package/dist/types/transaction.js.map +1 -1
  172. package/dist/types/types/electrum.d.ts +3 -2
  173. package/dist/types/types/transaction.d.ts +1 -1
  174. package/dist/types/types/wallet.d.ts +80 -4
  175. package/dist/types/utils/descriptors.d.ts +2 -0
  176. package/dist/types/utils/fees.d.ts +3 -0
  177. package/dist/types/utils/helpers.d.ts +3 -1
  178. package/dist/types/utils/index.d.ts +5 -0
  179. package/dist/types/utils/multisig.d.ts +29 -0
  180. package/dist/types/utils/transaction.d.ts +7 -0
  181. package/dist/types/utils/wallet-storage-encryption.d.ts +3 -0
  182. package/dist/types/utils/wallet.d.ts +1 -1
  183. package/dist/types/utils/watch-only.d.ts +12 -0
  184. package/dist/types/wallet/constants.d.ts +1 -0
  185. package/dist/types/wallet/index.d.ts +63 -4
  186. package/dist/types/wallet.js +3 -0
  187. package/dist/types/wallet.js.map +1 -1
  188. package/dist/utils/derivation-path.js +8 -7
  189. package/dist/utils/derivation-path.js.map +1 -1
  190. package/dist/utils/descriptors.js +56 -0
  191. package/dist/utils/descriptors.js.map +1 -0
  192. package/dist/utils/electrum.js +14 -4
  193. package/dist/utils/electrum.js.map +1 -1
  194. package/dist/utils/fees.js +19 -0
  195. package/dist/utils/fees.js.map +1 -0
  196. package/dist/utils/helpers.js +22 -5
  197. package/dist/utils/helpers.js.map +1 -1
  198. package/dist/utils/index.js +5 -0
  199. package/dist/utils/index.js.map +1 -1
  200. package/dist/utils/multisig.js +90 -0
  201. package/dist/utils/multisig.js.map +1 -0
  202. package/dist/utils/transaction.js +30 -3
  203. package/dist/utils/transaction.js.map +1 -1
  204. package/dist/utils/wallet-storage-encryption.js +45 -0
  205. package/dist/utils/wallet-storage-encryption.js.map +1 -0
  206. package/dist/utils/wallet.js +3 -1
  207. package/dist/utils/wallet.js.map +1 -1
  208. package/dist/utils/watch-only.js +102 -0
  209. package/dist/utils/watch-only.js.map +1 -0
  210. package/dist/wallet/constants.js +2 -1
  211. package/dist/wallet/constants.js.map +1 -1
  212. package/dist/wallet/index.js +810 -51
  213. package/dist/wallet/index.js.map +1 -1
  214. package/docs/ROADMAP.md +365 -0
  215. package/package.json +1 -1
package/README.md CHANGED
@@ -13,6 +13,7 @@ Beignet provides two layers of Bitcoin wallet functionality:
13
13
 
14
14
  1. [Getting Started](#getting-started)
15
15
  2. [On-Chain Wallet](#on-chain-wallet)
16
+ - [Leveled Logging](#leveled-logging)
16
17
  3. [Lightning Network](#lightning-network)
17
18
  - [Lightning Quick Start (BeignetNode)](#lightning-quick-start-beignetnode)
18
19
  - [Decision-Support APIs](#decision-support-apis)
@@ -137,6 +138,231 @@ const utxos = wallet.value.listUtxos();
137
138
  const history = await wallet.value.getAddressHistory('bc1q...');
138
139
  ```
139
140
 
141
+ ### Watch-Only Wallets
142
+
143
+ A watch-only wallet is constructed from an account-level extended public key
144
+ (xpub/ypub/zpub for mainnet, tpub/upub/vpub for testnet/regtest) instead of a
145
+ mnemonic. The key is assumed to sit at the account level
146
+ (m/purpose'/coin'/account', e.g. m/84'/0'/0' for p2wpkh), so receive and
147
+ change addresses derive publicly as xpub/0/i and xpub/1/i. SLIP-132 version
148
+ bytes are normalized automatically: a zpub/vpub implies p2wpkh and a
149
+ ypub/upub implies p2sh-p2wpkh; a plain xpub/tpub uses the `addressType`
150
+ option (default p2wpkh). Because one account xpub yields exactly one address
151
+ type, a watch-only wallet monitors only that type.
152
+
153
+ Everything read-only works: address generation, gap-limit scanning, Electrum
154
+ refresh, balances, transaction history, UTXOs, fee estimates and address
155
+ subscriptions. Anything that requires private keys
156
+ (send/sendMax/sendMany/sweepPrivateKey/getPrivateKey and the internal signing
157
+ paths) fails with the typed `WatchOnlySigningError`
158
+ (`code: 'WATCH_ONLY_CANNOT_SIGN'`, message `watch-only wallet cannot sign`).
159
+
160
+ Watch-only is a library feature only for now: the HTTP daemon always runs
161
+ with a mnemonic.
162
+
163
+ ```typescript
164
+ import { Wallet } from 'beignet';
165
+
166
+ const res = await Wallet.createWatchOnly({
167
+ xpub: 'zpub6r...',
168
+ network: 'bitcoin',
169
+ electrumOptions: { net, tls },
170
+ });
171
+ if (res.isErr()) return;
172
+ const watchOnly = res.value;
173
+
174
+ const address = await watchOnly.getAddress(); // works
175
+ const balance = watchOnly.getBalance(); // works
176
+ const sendRes = await watchOnly.send({ address: 'bc1q...', amount: 1000 });
177
+ // sendRes.isErr() === true; sendRes.error.message === 'watch-only wallet cannot sign'
178
+ ```
179
+
180
+ ### External Signer (Hardware Wallet) PSBT Flow
181
+
182
+ `buildPsbt` runs the normal transaction setup (coin selection, change, fee)
183
+ but stops before signing and returns a base64 PSBT populated with everything
184
+ a hardware signer needs: `witnessUtxo` (or `nonWitnessUtxo` for legacy
185
+ p2pkh), `redeemScript` for p2sh-p2wpkh, `tapInternalKey` plus
186
+ `tapBip32Derivation` for p2tr, and `bip32Derivation` (fingerprint + path +
187
+ pubkey) on every wallet input. It works on both full and watch-only wallets.
188
+ Note for watch-only wallets: the true master fingerprint is unknowable from
189
+ an account xpub, so the xpub's parent fingerprint is used; signers should
190
+ locate keys by derivation path.
191
+
192
+ ```typescript
193
+ // 1. Build (works on a watch-only wallet)
194
+ const build = await wallet.buildPsbt({
195
+ address: 'bc1q...',
196
+ amount: 50000,
197
+ satsPerByte: 4,
198
+ });
199
+ if (build.isErr()) return;
200
+ const { psbtBase64, fee, vsizeEstimate } = build.value;
201
+
202
+ // 2. Sign externally (hardware wallet / HWI / another machine)
203
+ const signedBase64 = await myHardwareWallet.signPsbt(psbtBase64);
204
+
205
+ // 3. Import: validates a signature on EVERY input, finalizes, does NOT broadcast
206
+ const imported = wallet.importSignedPsbt(signedBase64);
207
+ if (imported.isErr()) return; // missing/invalid signatures are rejected loudly
208
+ const { txHex, txid } = imported.value;
209
+
210
+ // 4. Broadcast when ready
211
+ await wallet.broadcastTransaction(txHex);
212
+
213
+ // Multi-party signing: merge partially signed copies of the same PSBT
214
+ const combined = wallet.combinePsbts([copyA, copyB]);
215
+ ```
216
+
217
+ The HTTP daemon exposes the same flow on its (mnemonic-backed) wallet via
218
+ `POST /psbt/build`, `POST /psbt/import-signed` and `POST /psbt/combine`, and
219
+ the CLI via `beignet psbt build|import-signed|combine`.
220
+
221
+ ### Multisig P2WSH Wallets (sortedmulti)
222
+
223
+ `Wallet.createMultisig` creates a descriptor-based sorted-multisig wallet:
224
+ `wsh(sortedmulti(threshold, key1, key2, ...))`, the interoperable standard
225
+ used by Bitcoin Core, Sparrow and Specter. Derivation follows BIP 48 with
226
+ script type 2 (`m/48'/coin'/account'/2'`, receive `/0/*`, change `/1/*`) and
227
+ public keys are ordered per BIP 67 at every index, so any wallet built from
228
+ the same account xpubs produces identical addresses regardless of the order
229
+ the cosigners were listed in.
230
+
231
+ Cosigners are supplied as account-level extended public keys (`xpub`/`tpub`,
232
+ or the SLIP-132 multisig encodings `Zpub`/`Vpub`, normalized automatically).
233
+ When a mnemonic is provided, this wallet IS one of the cosigners: its BIP 48
234
+ account xpub is derived and included automatically (pass `ourXpub` to assert
235
+ it explicitly; a mismatch is rejected). Omit the mnemonic for a watch-only
236
+ multisig coordinator: the full read-only surface (scanning, balances,
237
+ history, subscriptions) works, signing does not.
238
+
239
+ Spending is PSBT-only. Direct spends (`send`/`sendMany`/`sendMax`) fail with
240
+ the typed `MultisigSpendError` (`code: 'MULTISIG_REQUIRES_PSBT'`). `buildPsbt`
241
+ attaches the `witnessScript` and one `bip32Derivation` entry per cosigner to
242
+ every input; `signPsbtWithOurKey` adds this cosigner's partial signature
243
+ without finalizing; `importSignedPsbt` counts the VALID partial signatures on
244
+ each input against the witnessScript threshold and refuses to finalize below
245
+ it (the error names how many signatures it has and needs).
246
+ `exportDescriptors()` emits the checksummed `wsh(sortedmulti(...))` receive
247
+ and change descriptors for import into Bitcoin Core/Sparrow/Specter; our key
248
+ carries its full key origin, cosigners known only as xpubs carry a
249
+ fingerprint-only origin. Multisig is a library-only feature for now: the
250
+ HTTP daemon wallet stays single-sig.
251
+
252
+ Full 2-of-3 walkthrough:
253
+
254
+ ```typescript
255
+ import { Wallet } from 'beignet';
256
+
257
+ // Each cosigner shares their BIP 48 account xpub (m/48'/0'/0'/2' on mainnet).
258
+ // Grab ours from exportDescriptors() or derive it with any BIP 48 tool.
259
+
260
+ // 1. Cosigner A creates their multisig wallet (A holds mnemonicA).
261
+ const walletA = (
262
+ await Wallet.createMultisig({
263
+ threshold: 2,
264
+ mnemonic: mnemonicA, // we are one cosigner; our xpub is added automatically
265
+ cosigners: [xpubB, xpubC],
266
+ network: 'bitcoin',
267
+ electrumOptions: { net, tls },
268
+ })
269
+ ).value;
270
+
271
+ // Cosigner B does the same in their own instance/machine.
272
+ const walletB = (
273
+ await Wallet.createMultisig({
274
+ threshold: 2,
275
+ mnemonic: mnemonicB,
276
+ cosigners: [xpubA, xpubC],
277
+ network: 'bitcoin',
278
+ electrumOptions: { net, tls },
279
+ })
280
+ ).value;
281
+
282
+ // An optional watch-only coordinator holds no keys at all.
283
+ const coordinator = (
284
+ await Wallet.createMultisig({
285
+ threshold: 2,
286
+ cosigners: [xpubA, xpubB, xpubC],
287
+ network: 'bitcoin',
288
+ electrumOptions: { net, tls },
289
+ })
290
+ ).value;
291
+
292
+ // 2. Fund the multisig: every instance derives the same addresses.
293
+ const deposit = await walletA.getAddress(); // == walletB/coordinator address
294
+
295
+ // 3. Build the unsigned PSBT (works on any instance, coordinator included).
296
+ const built = await walletA.buildPsbt({
297
+ address: 'bc1q...',
298
+ amount: 50000,
299
+ satsPerByte: 4,
300
+ });
301
+ const unsigned = built.value.psbtBase64;
302
+
303
+ // 4. Each cosigner signs their own copy (below threshold nothing finalizes).
304
+ const signedA = walletA.signPsbtWithOurKey(unsigned).value;
305
+ const signedB = walletB.signPsbtWithOurKey(unsigned).value;
306
+
307
+ // 5. Combine the partials, finalize at threshold, broadcast.
308
+ const combined = coordinator.combinePsbts([signedA, signedB]).value;
309
+ const finalized = coordinator.importSignedPsbt(combined).value; // 2-of-3 met
310
+ await coordinator.broadcastTransaction(finalized.txHex);
311
+
312
+ // Importing with only one signature fails loudly:
313
+ // 'Input 0 is below the multisig threshold: have 1 signature(s), need 2.'
314
+
315
+ // Interop: import the wallet into Bitcoin Core/Sparrow/Specter.
316
+ const descriptors = walletA.exportDescriptors().value;
317
+ // wsh(sortedmulti(2,[fp/48h/0h/0h/2h]xpub.../0/*,[fp]xpub.../0/*,...))#checksum
318
+ ```
319
+
320
+ ### Networks, Fee Estimates & Electrum Failover
321
+
322
+ - **Networks:** `mainnet`, `testnet`, `regtest`, and `signet` are supported end to end (on-chain wallet, Electrum, CLI/daemon via `--network signet`, and the Lightning node config, which uses the signet chain hash and `tbs` invoice prefix). Signet shares testnet's address formats and derivation paths (coin type 1); only the chain differs.
323
+ - **Fee estimation source:** `Wallet.create({ feeEstimationSource })` accepts `'electrum' | 'http' | 'auto'` (default `'auto'`). `'electrum'` queries only the connected Electrum server via `blockchain.estimatefee`, so fee lookups never leak to mempool.space/blocktank over clearnet; `'auto'` prefers Electrum and falls back to HTTP only when Electrum is unavailable or returns unusable values. All remote-sourced rates are clamped to at most 5000 sat/vB. The daemon exposes the same option as `feeEstimationSource` / `--fee-source` / `BEIGNET_FEE_SOURCE`.
324
+ - **Electrum failover:** when multiple `electrumOptions.servers` are provided, the wallet rotates through them in order on connect/reconnect failure (then through hardcoded fallback peers for the network), with a per-server cooldown so dead servers are not hammered. `wallet.electrum.currentServer` and `wallet.electrum.rotationCount` expose the current server and rotation history.
325
+ - **BIP21:** `encodeBip21({ address, amountSats?, label?, message? })` builds a `bitcoin:` payment URI; the daemon's `POST /address/new` accepts `{ bip21: true, amountSats?, label?, message? }` and the CLI supports `address --bip21 [--amount <sats>] [--label L] [--message M]`.
326
+
327
+ ### Storage & Encryption
328
+
329
+ The wallet persists its state through the host-injected `TStorage` interface (`storage: { getData, setData }` on `Wallet.create`). Values are handed to the host as-is, so by default they are stored in plaintext. The persisted data is addresses, address indexes, UTXOs, transactions, balance and fee estimates. No private keys and no mnemonic are ever written, so exposure is a privacy concern (full wallet history), not fund loss.
330
+
331
+ To encrypt at rest, wrap any `TStorage` with `createEncryptedStorage` before passing it in. Values are encrypted with AES-256-GCM under a key derived from the seed via HKDF, and pre-existing plaintext values are passed through unchanged and migrate lazily as they are rewritten:
332
+
333
+ ```typescript
334
+ import { createEncryptedStorage, Wallet } from 'beignet';
335
+ import * as bip39 from 'bip39';
336
+
337
+ const seed = bip39.mnemonicToSeedSync(mnemonic);
338
+ const wallet = await Wallet.create({
339
+ mnemonic,
340
+ storage: createEncryptedStorage({ getData, setData }, seed),
341
+ // ...
342
+ });
343
+ ```
344
+
345
+ ### Leveled Logging
346
+
347
+ Diagnostic output (debug/info/warn/error) flows through a small injectable logger, kept separate from the Lightning node's persisted structured action log (`getActionLog`). The `ILogger` interface is four methods, `debug`/`info`/`warn`/`error`(`message: string, meta?: unknown`), with level filtering `debug < info < warn < error` plus `'silent'`:
348
+
349
+ ```typescript
350
+ import { Wallet, createConsoleLogger, noopLogger } from 'beignet';
351
+
352
+ const wallet = await Wallet.create({
353
+ mnemonic,
354
+ logger: createConsoleLogger('warn'), // only warn + error reach the console
355
+ // logger: noopLogger, // fully silent
356
+ // logger: myLogger, // any ILogger: route into your own stack
357
+ // ...
358
+ });
359
+ ```
360
+
361
+ - **`Wallet.create({ logger })`** defaults to `createConsoleLogger('info')`, which preserves the wallet's historical console output. `disableMessages` is independent and keeps its existing meaning: it only gates `onMessage` callbacks.
362
+ - **`LightningNode`** accepts `logger` in `INodeConfig` / `fromMnemonic` options and defaults to `noopLogger` (the node prints nothing, as before). Every structured action-log entry is additionally mirrored to `logger.debug('category:action', data)`.
363
+ - **`BeignetNode.create({ logger, logLevel })`**: log entries that pass `logLevel` are forwarded to the injected logger (in addition to the `'log'` event), and the logger is injected into the underlying `Wallet` and `LightningNode`. Without `logger`, behavior is unchanged (events only).
364
+ - **Daemon:** `beignet start --log-level <debug|info|warn|error|silent>` (or `BEIGNET_LOG_LEVEL`, or `logLevel` in `~/.beignet/config.json`) prints leveled diagnostics to stderr. Unset keeps the daemon silent (the default); stdout stays reserved for command output.
365
+
140
366
  ## Lightning Network
141
367
 
142
368
  ### Lightning Quick Start (BeignetNode)
@@ -212,6 +438,85 @@ const readiness = node.getMainnetReadiness();
212
438
  console.log('Score:', readiness.score + '/100', 'Ready:', readiness.ready);
213
439
  ```
214
440
 
441
+ ### Advisor Execution (circular rebalancing + fee auto-tuning)
442
+
443
+ The advisor can also *act*, not just recommend. Both features are **off by
444
+ default** and only run when explicitly enabled in the node options.
445
+
446
+ ```typescript
447
+ // One-shot circular rebalance: self-payment out over fromChannelId and back
448
+ // in over toChannelId. Aborts WITHOUT paying if the route fee > maxFeeSats.
449
+ const result = await node.rebalanceChannel(
450
+ fromChannelId, toChannelId, 50_000, /* maxFeeSats: */ 50
451
+ );
452
+
453
+ // Inspect what the executor would do (read-only)
454
+ const recs = node.getAdvisorRecommendations(); // analyze() + rebalancePlan[]
455
+
456
+ // Run the advisor's rebalance plan under a per-day fee budget
457
+ const summary = await node.executeRebalances(/* budgetSatsPerDay: */ 500);
458
+ ```
459
+
460
+ Automatic modes (opt-in via `BeignetNodeOptions` / `INodeConfig`):
461
+
462
+ ```typescript
463
+ const node = await BeignetNode.create({
464
+ mnemonic,
465
+ // Periodically executes the rebalance plan. Routing fees spent on
466
+ // rebalances are capped at budgetSatsPerDay per UTC day; the running spend
467
+ // is persisted, so restarts never overspend the same day. The budget
468
+ // resets at midnight UTC.
469
+ autoRebalance: { enabled: true, budgetSatsPerDay: 500, minImbalancePct: 20 },
470
+ // Every intervalMs (default 6h) nudges each channel's proportional fee:
471
+ // +25% when outbound is depleted (<20% local) but still forwarding,
472
+ // -25% when the channel saw no forwards in the window, clamped to
473
+ // [floorPpm, ceilPpm]. One adjustment per channel per interval.
474
+ autoTuneFees: { enabled: true, floorPpm: 1, ceilPpm: 5_000 }
475
+ });
476
+ ```
477
+
478
+ Daemon/CLI surfaces: `POST /rebalance`, `GET /advisor/recommendations`,
479
+ `POST /advisor/execute-rebalances`; `beignet rebalance <from> <to> <sats>
480
+ --max-fee <sats>`, `beignet advisor recommendations`, `beignet advisor
481
+ execute-rebalances [--budget <sats>]`.
482
+
483
+ ### Watchtowers
484
+
485
+ Penalty enforcement normally requires this node's own chain monitor to be online:
486
+ if a counterparty broadcasts a revoked commitment while you are offline, nobody
487
+ sweeps the breach. The **watchtower client** closes that gap. At every revocation
488
+ it builds an encrypted *justice kit* (the revoked commitment's breach hint plus a
489
+ pre-signed to_local penalty) and ships it to one or more remote towers over the
490
+ standard BOLT 8 Noise transport. When a tower later sees the breach transaction on
491
+ chain, it decrypts the kit and broadcasts the penalty on your behalf — reclaiming
492
+ the channel even though you never came back online.
493
+
494
+ - **Altruist only.** Sessions use `reward = 0`; towers take no cut. There is no
495
+ server mode (beignet is a tower *client*, not a tower).
496
+ - **LND-tower compatible.** Implements LND's `wtwire` protocol (Init/CreateSession/
497
+ StateUpdate/DeleteSession, message types 600-607) and the version-0 justice blob
498
+ (XChaCha20-Poly1305, breach hint = `SHA256(txid)[:16]`, key = `SHA256(txid‖txid)`),
499
+ so it interoperates with existing public LND altruist towers.
500
+ - **Legacy + anchor channels.** The to_local revocation penalty (the fund-critical
501
+ breach punishment) is packed for both; taproot channels are not yet backed up.
502
+ - **Durable.** Per-tower session state and the un-acked update backlog are persisted
503
+ (encrypted at rest) and drained with exponential backoff on reconnect. An un-acked
504
+ update is never dropped silently.
505
+
506
+ Configure towers as `pubkey@host:port` URIs (off when empty):
507
+
508
+ ```ts
509
+ const node = await BeignetNode.create({
510
+ mnemonic,
511
+ watchtowers: ['03abc...@tower.example.com:9911']
512
+ });
513
+ ```
514
+
515
+ Daemon/CLI surfaces: `GET /watchtowers`, `POST /watchtower/add`,
516
+ `DELETE /watchtower/remove`; `beignet watchtower list`, `beignet watchtower add
517
+ <pubkey@host:port>`, `beignet watchtower remove <uri>`; daemon flag `--watchtower`
518
+ (repeatable) or `BEIGNET_WATCHTOWERS` (comma-separated).
519
+
215
520
  ### HTTP Daemon
216
521
 
217
522
  BeignetNode can also run as an HTTP/SSE daemon for language-agnostic integrations:
@@ -512,7 +817,7 @@ Beignet is under active development. The following features are missing or carry
512
817
 
513
818
  | Feature | Status | Impact |
514
819
  |---------|--------|--------|
515
- | **Watchtowers** | Not implemented | If your node goes offline, a counterparty could theoretically broadcast a revoked state. Mitigate with frequent backups and auto-reconnect. |
820
+ | **Watchtowers** | Client implemented (altruist) | Ships encrypted justice data to remote LND altruist towers at every revocation so a breach is punished while you are offline (see [Watchtowers](#watchtowers)). Legacy + anchor channels only; taproot channels are not yet backed up, and server mode is out of scope. |
516
821
  | **LSP / LSPS protocols** | Not implemented | No automated inbound liquidity acquisition via LSPS0/1/2. Liquidity ads (bLIP-51) are supported for negotiated leases; otherwise open channels manually. |
517
822
  | **Trampoline routing** | Not implemented | All route computation is local. Cannot delegate pathfinding to a trampoline node. |
518
823
  | **BOLT 12 offers** | Newer | Offer creation/decoding, invoice_request/invoice over onion messages, and receive-side settlement are implemented, but the surface is newer and less battle-tested than BOLT 11. Prefer BOLT 11 invoices for production. |
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiKeyAuthenticator = exports.scopesAllowRoute = exports.getRouteScopes = exports.ROUTE_SCOPES = exports.API_SCOPES = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const errors_1 = require("./errors");
6
+ exports.API_SCOPES = new Set([
7
+ 'readonly',
8
+ 'invoice',
9
+ 'admin'
10
+ ]);
11
+ exports.ROUTE_SCOPES = {
12
+ 'GET /info': ['readonly'],
13
+ 'GET /balance': ['readonly'],
14
+ 'GET /peers': ['readonly'],
15
+ 'GET /channels': ['readonly'],
16
+ 'GET /channels/ready': ['readonly'],
17
+ 'GET /payments': ['readonly'],
18
+ 'GET /payment': ['readonly'],
19
+ 'GET /payment/proof': ['readonly'],
20
+ 'GET /payment/verify-proof': ['readonly'],
21
+ 'GET /forwards': ['readonly'],
22
+ 'GET /forwards/summary': ['readonly'],
23
+ 'GET /readiness': ['readonly'],
24
+ 'GET /stats': ['readonly'],
25
+ 'GET /spend-limit': ['readonly'],
26
+ 'GET /liquidity': ['readonly'],
27
+ 'GET /watchtowers': ['readonly'],
28
+ 'GET /advisor/recommendations': ['readonly'],
29
+ 'GET /fees': ['readonly'],
30
+ 'GET /fees/estimates': ['readonly'],
31
+ 'GET /transactions': ['readonly'],
32
+ 'GET /transactions/boostable': ['readonly'],
33
+ 'GET /utxos': ['readonly'],
34
+ 'GET /address/labels': ['readonly'],
35
+ 'GET /wallet/descriptors': ['readonly'],
36
+ 'GET /channel': ['readonly'],
37
+ 'GET /channel/health': ['readonly'],
38
+ 'GET /channel/policy': ['readonly'],
39
+ 'GET /channel/diagnostics': ['readonly'],
40
+ 'GET /channel/suggestions': ['readonly'],
41
+ 'GET /logs': ['readonly'],
42
+ 'GET /node/uri': ['readonly'],
43
+ 'GET /trusted-peers': ['readonly'],
44
+ 'GET /can-send': ['readonly'],
45
+ 'GET /graph/info': ['readonly'],
46
+ 'GET /graph/node': ['readonly'],
47
+ 'GET /graph/channel': ['readonly'],
48
+ 'GET /graph/describe': ['readonly'],
49
+ 'GET /backup/scb': ['readonly'],
50
+ 'GET /backup/peer-retrieved': ['readonly'],
51
+ 'GET /queue': ['readonly'],
52
+ 'POST /route/estimate': ['readonly'],
53
+ 'POST /route/query': ['readonly'],
54
+ 'POST /payment/estimate': ['readonly'],
55
+ 'POST /message/verify': ['readonly'],
56
+ 'POST /address/validate': ['readonly'],
57
+ 'POST /node/wait-ready': ['readonly'],
58
+ 'POST /channel/wait-ready': ['readonly'],
59
+ 'GET /invoices': ['readonly', 'invoice'],
60
+ 'GET /invoice': ['readonly', 'invoice'],
61
+ 'GET /invoices/held': ['readonly', 'invoice'],
62
+ 'GET /offers': ['readonly', 'invoice'],
63
+ 'GET /can-receive': ['readonly', 'invoice'],
64
+ 'GET /events': ['readonly', 'invoice'],
65
+ 'POST /invoice/decode': ['readonly', 'invoice'],
66
+ 'POST /invoice/validate': ['readonly', 'invoice'],
67
+ 'POST /offer/decode': ['readonly', 'invoice'],
68
+ 'POST /payment/wait': ['readonly', 'invoice'],
69
+ 'POST /invoice/create': ['invoice'],
70
+ 'POST /invoice/create-hold': ['invoice'],
71
+ 'POST /invoice/settle-hold': ['invoice'],
72
+ 'POST /invoice/cancel-hold': ['invoice'],
73
+ 'POST /offer/create': ['invoice'],
74
+ 'POST /address/new': ['invoice'],
75
+ 'GET /mnemonic': [],
76
+ 'GET /webhooks': [],
77
+ 'POST /send': [],
78
+ 'POST /send-max': [],
79
+ 'POST /tx/bump-fee': [],
80
+ 'POST /tx/boost': [],
81
+ 'POST /consolidate': [],
82
+ 'POST /invoice/pay': [],
83
+ 'POST /invoice/pay-safe': [],
84
+ 'POST /invoice/pay-async': [],
85
+ 'POST /invoice/pay-retry': [],
86
+ 'POST /keysend': [],
87
+ 'POST /keysend/safe': [],
88
+ 'POST /offer/pay': [],
89
+ 'POST /payment/send-to-route': [],
90
+ 'POST /payment/cancel': [],
91
+ 'POST /queue/add': [],
92
+ 'POST /queue/cancel': [],
93
+ 'POST /rebalance': [],
94
+ 'POST /advisor/execute-rebalances': [],
95
+ 'POST /recover-fallback-funds': [],
96
+ 'POST /utxo/freeze': [],
97
+ 'POST /utxo/unfreeze': [],
98
+ 'POST /address/label': [],
99
+ 'POST /wallet/refresh': [],
100
+ 'POST /psbt/build': [],
101
+ 'POST /psbt/import-signed': [],
102
+ 'POST /psbt/combine': [],
103
+ 'POST /peer/connect': [],
104
+ 'POST /peer/disconnect': [],
105
+ 'POST /peers/bootstrap': [],
106
+ 'POST /peers/connect-seeds': [],
107
+ 'POST /trusted-peer/add': [],
108
+ 'POST /trusted-peer/remove': [],
109
+ 'POST /channel/open': [],
110
+ 'POST /channel/open-zeroconf': [],
111
+ 'POST /channel/open-v2': [],
112
+ 'POST /channel/open-and-wait': [],
113
+ 'POST /channel/connect-and-open': [],
114
+ 'POST /channels/ensure-minimum': [],
115
+ 'POST /channel/close': [],
116
+ 'POST /channel/forceclose': [],
117
+ 'POST /channel/splice-in': [],
118
+ 'POST /channel/splice-out': [],
119
+ 'POST /channel/update-commitment-feerate': [],
120
+ 'POST /channel/update-fee': [],
121
+ 'POST /channel/update-policy': [],
122
+ 'POST /message/sign': [],
123
+ 'POST /gossip/sync': [],
124
+ 'POST /gossip/sync-rapid': [],
125
+ 'POST /route/probe': [],
126
+ 'POST /payment/metadata': [],
127
+ 'POST /backup': [],
128
+ 'POST /backup/trigger': [],
129
+ 'POST /restore/scb': [],
130
+ 'POST /watchtower/add': [],
131
+ 'DELETE /watchtower/remove': [],
132
+ 'POST /webhooks/register': [],
133
+ 'DELETE /webhooks/unregister': [],
134
+ 'POST /stop': [],
135
+ 'GET /auth/keys': [],
136
+ 'POST /auth/keys/revoke': []
137
+ };
138
+ function getRouteScopes(routeKey) {
139
+ return exports.ROUTE_SCOPES[routeKey] ?? [];
140
+ }
141
+ exports.getRouteScopes = getRouteScopes;
142
+ function scopesAllowRoute(scopes, routeKey) {
143
+ if (scopes.has('admin'))
144
+ return true;
145
+ return getRouteScopes(routeKey).some((s) => scopes.has(s));
146
+ }
147
+ exports.scopesAllowRoute = scopesAllowRoute;
148
+ function sha256(input) {
149
+ return (0, crypto_1.createHash)('sha256').update(input, 'utf8').digest();
150
+ }
151
+ class ApiKeyAuthenticator {
152
+ constructor(apiToken, apiKeys) {
153
+ this.candidates = [];
154
+ this.revoked = new Set();
155
+ this.keyNames = [];
156
+ if (apiToken) {
157
+ this.candidates.push({
158
+ name: null,
159
+ digest: sha256(apiToken),
160
+ scopes: new Set(['admin'])
161
+ });
162
+ }
163
+ for (const def of apiKeys ?? []) {
164
+ this.addKeyDefinition(def, apiToken);
165
+ }
166
+ }
167
+ addKeyDefinition(def, apiToken) {
168
+ if (!def || typeof def.name !== 'string' || def.name.trim() === '') {
169
+ throw new errors_1.BeignetError('INVALID_PARAMS', 'apiKeys entries require a non-empty name');
170
+ }
171
+ if (typeof def.key !== 'string' || def.key === '') {
172
+ throw new errors_1.BeignetError('INVALID_PARAMS', `apiKeys entry "${def.name}" requires a non-empty key`);
173
+ }
174
+ if (!Array.isArray(def.scopes) ||
175
+ def.scopes.length === 0 ||
176
+ def.scopes.some((s) => !exports.API_SCOPES.has(s))) {
177
+ throw new errors_1.BeignetError('INVALID_PARAMS', `apiKeys entry "${def.name}" requires scopes from: readonly, invoice, admin`);
178
+ }
179
+ if (this.keyNames.includes(def.name)) {
180
+ throw new errors_1.BeignetError('INVALID_PARAMS', `Duplicate apiKeys name "${def.name}"`);
181
+ }
182
+ if (apiToken !== undefined && def.key === apiToken) {
183
+ throw new errors_1.BeignetError('INVALID_PARAMS', `apiKeys entry "${def.name}" duplicates the legacy apiToken value`);
184
+ }
185
+ const digest = sha256(def.key);
186
+ for (const existing of this.candidates) {
187
+ if (existing.name !== null && (0, crypto_1.timingSafeEqual)(existing.digest, digest)) {
188
+ throw new errors_1.BeignetError('INVALID_PARAMS', `apiKeys entry "${def.name}" duplicates the key of "${existing.name}"`);
189
+ }
190
+ }
191
+ this.keyNames.push(def.name);
192
+ this.candidates.push({
193
+ name: def.name,
194
+ digest,
195
+ scopes: new Set(def.scopes)
196
+ });
197
+ }
198
+ get enabled() {
199
+ return this.candidates.length > 0;
200
+ }
201
+ authenticate(header) {
202
+ if (!header)
203
+ return { ok: false };
204
+ const match = header.match(/^bearer\s+(.+)$/i);
205
+ if (!match)
206
+ return { ok: false };
207
+ const presented = sha256(match[1]);
208
+ let matched = null;
209
+ for (const candidate of this.candidates) {
210
+ const equal = (0, crypto_1.timingSafeEqual)(candidate.digest, presented);
211
+ if (equal &&
212
+ matched === null &&
213
+ (candidate.name === null || !this.revoked.has(candidate.name))) {
214
+ matched = candidate;
215
+ }
216
+ }
217
+ if (!matched)
218
+ return { ok: false };
219
+ return { ok: true, keyName: matched.name, scopes: matched.scopes };
220
+ }
221
+ revoke(name) {
222
+ if (!this.keyNames.includes(name))
223
+ return false;
224
+ this.revoked.add(name);
225
+ return true;
226
+ }
227
+ listKeys() {
228
+ return this.candidates
229
+ .filter((c) => c.name !== null)
230
+ .map((c) => ({
231
+ name: c.name,
232
+ scopes: [...c.scopes],
233
+ revoked: this.revoked.has(c.name)
234
+ }));
235
+ }
236
+ }
237
+ exports.ApiKeyAuthenticator = ApiKeyAuthenticator;
238
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/cli/auth.ts"],"names":[],"mappings":";;;AAwBA,mCAAqD;AACrD,qCAAwC;AAI3B,QAAA,UAAU,GAA0B,IAAI,GAAG,CAAC;IACxD,UAAU;IACV,SAAS;IACT,OAAO;CACP,CAAC,CAAC;AA4BU,QAAA,YAAY,GAA+B;IAEvD,WAAW,EAAE,CAAC,UAAU,CAAC;IACzB,cAAc,EAAE,CAAC,UAAU,CAAC;IAC5B,YAAY,EAAE,CAAC,UAAU,CAAC;IAC1B,eAAe,EAAE,CAAC,UAAU,CAAC;IAC7B,qBAAqB,EAAE,CAAC,UAAU,CAAC;IACnC,eAAe,EAAE,CAAC,UAAU,CAAC;IAC7B,cAAc,EAAE,CAAC,UAAU,CAAC;IAC5B,oBAAoB,EAAE,CAAC,UAAU,CAAC;IAClC,2BAA2B,EAAE,CAAC,UAAU,CAAC;IACzC,eAAe,EAAE,CAAC,UAAU,CAAC;IAC7B,uBAAuB,EAAE,CAAC,UAAU,CAAC;IACrC,gBAAgB,EAAE,CAAC,UAAU,CAAC;IAC9B,YAAY,EAAE,CAAC,UAAU,CAAC;IAC1B,kBAAkB,EAAE,CAAC,UAAU,CAAC;IAChC,gBAAgB,EAAE,CAAC,UAAU,CAAC;IAC9B,kBAAkB,EAAE,CAAC,UAAU,CAAC;IAChC,8BAA8B,EAAE,CAAC,UAAU,CAAC;IAC5C,WAAW,EAAE,CAAC,UAAU,CAAC;IACzB,qBAAqB,EAAE,CAAC,UAAU,CAAC;IACnC,mBAAmB,EAAE,CAAC,UAAU,CAAC;IACjC,6BAA6B,EAAE,CAAC,UAAU,CAAC;IAC3C,YAAY,EAAE,CAAC,UAAU,CAAC;IAC1B,qBAAqB,EAAE,CAAC,UAAU,CAAC;IACnC,yBAAyB,EAAE,CAAC,UAAU,CAAC;IACvC,cAAc,EAAE,CAAC,UAAU,CAAC;IAC5B,qBAAqB,EAAE,CAAC,UAAU,CAAC;IACnC,qBAAqB,EAAE,CAAC,UAAU,CAAC;IACnC,0BAA0B,EAAE,CAAC,UAAU,CAAC;IACxC,0BAA0B,EAAE,CAAC,UAAU,CAAC;IACxC,WAAW,EAAE,CAAC,UAAU,CAAC;IACzB,eAAe,EAAE,CAAC,UAAU,CAAC;IAC7B,oBAAoB,EAAE,CAAC,UAAU,CAAC;IAClC,eAAe,EAAE,CAAC,UAAU,CAAC;IAC7B,iBAAiB,EAAE,CAAC,UAAU,CAAC;IAC/B,iBAAiB,EAAE,CAAC,UAAU,CAAC;IAC/B,oBAAoB,EAAE,CAAC,UAAU,CAAC;IAClC,qBAAqB,EAAE,CAAC,UAAU,CAAC;IACnC,iBAAiB,EAAE,CAAC,UAAU,CAAC;IAC/B,4BAA4B,EAAE,CAAC,UAAU,CAAC;IAC1C,YAAY,EAAE,CAAC,UAAU,CAAC;IAG1B,sBAAsB,EAAE,CAAC,UAAU,CAAC;IACpC,mBAAmB,EAAE,CAAC,UAAU,CAAC;IACjC,wBAAwB,EAAE,CAAC,UAAU,CAAC;IACtC,sBAAsB,EAAE,CAAC,UAAU,CAAC;IACpC,wBAAwB,EAAE,CAAC,UAAU,CAAC;IACtC,uBAAuB,EAAE,CAAC,UAAU,CAAC;IACrC,0BAA0B,EAAE,CAAC,UAAU,CAAC;IAGxC,eAAe,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IACxC,cAAc,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IACvC,oBAAoB,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IAC7C,aAAa,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IACtC,kBAAkB,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IAC3C,aAAa,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IACtC,sBAAsB,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/C,wBAAwB,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IACjD,oBAAoB,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IAC7C,oBAAoB,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;IAG7C,sBAAsB,EAAE,CAAC,SAAS,CAAC;IACnC,2BAA2B,EAAE,CAAC,SAAS,CAAC;IACxC,2BAA2B,EAAE,CAAC,SAAS,CAAC;IACxC,2BAA2B,EAAE,CAAC,SAAS,CAAC;IACxC,oBAAoB,EAAE,CAAC,SAAS,CAAC;IACjC,mBAAmB,EAAE,CAAC,SAAS,CAAC;IAGhC,eAAe,EAAE,EAAE;IAGnB,eAAe,EAAE,EAAE;IAGnB,YAAY,EAAE,EAAE;IAChB,gBAAgB,EAAE,EAAE;IACpB,mBAAmB,EAAE,EAAE;IACvB,gBAAgB,EAAE,EAAE;IACpB,mBAAmB,EAAE,EAAE;IACvB,mBAAmB,EAAE,EAAE;IACvB,wBAAwB,EAAE,EAAE;IAC5B,yBAAyB,EAAE,EAAE;IAC7B,yBAAyB,EAAE,EAAE;IAC7B,eAAe,EAAE,EAAE;IACnB,oBAAoB,EAAE,EAAE;IACxB,iBAAiB,EAAE,EAAE;IACrB,6BAA6B,EAAE,EAAE;IACjC,sBAAsB,EAAE,EAAE;IAC1B,iBAAiB,EAAE,EAAE;IACrB,oBAAoB,EAAE,EAAE;IACxB,iBAAiB,EAAE,EAAE;IACrB,kCAAkC,EAAE,EAAE;IACtC,8BAA8B,EAAE,EAAE;IAGlC,mBAAmB,EAAE,EAAE;IACvB,qBAAqB,EAAE,EAAE;IACzB,qBAAqB,EAAE,EAAE;IACzB,sBAAsB,EAAE,EAAE;IAC1B,kBAAkB,EAAE,EAAE;IACtB,0BAA0B,EAAE,EAAE;IAC9B,oBAAoB,EAAE,EAAE;IAGxB,oBAAoB,EAAE,EAAE;IACxB,uBAAuB,EAAE,EAAE;IAC3B,uBAAuB,EAAE,EAAE;IAC3B,2BAA2B,EAAE,EAAE;IAC/B,wBAAwB,EAAE,EAAE;IAC5B,2BAA2B,EAAE,EAAE;IAC/B,oBAAoB,EAAE,EAAE;IACxB,6BAA6B,EAAE,EAAE;IACjC,uBAAuB,EAAE,EAAE;IAC3B,6BAA6B,EAAE,EAAE;IACjC,gCAAgC,EAAE,EAAE;IACpC,+BAA+B,EAAE,EAAE;IACnC,qBAAqB,EAAE,EAAE;IACzB,0BAA0B,EAAE,EAAE;IAC9B,yBAAyB,EAAE,EAAE;IAC7B,0BAA0B,EAAE,EAAE;IAC9B,yCAAyC,EAAE,EAAE;IAC7C,0BAA0B,EAAE,EAAE;IAC9B,6BAA6B,EAAE,EAAE;IAGjC,oBAAoB,EAAE,EAAE;IACxB,mBAAmB,EAAE,EAAE;IACvB,yBAAyB,EAAE,EAAE;IAC7B,mBAAmB,EAAE,EAAE;IACvB,wBAAwB,EAAE,EAAE;IAG5B,cAAc,EAAE,EAAE;IAClB,sBAAsB,EAAE,EAAE;IAC1B,mBAAmB,EAAE,EAAE;IACvB,sBAAsB,EAAE,EAAE;IAC1B,2BAA2B,EAAE,EAAE;IAC/B,yBAAyB,EAAE,EAAE;IAC7B,6BAA6B,EAAE,EAAE;IACjC,YAAY,EAAE,EAAE;IAGhB,gBAAgB,EAAE,EAAE;IACpB,wBAAwB,EAAE,EAAE;CAC5B,CAAC;AAMF,SAAgB,cAAc,CAAC,QAAgB;IAC9C,OAAO,oBAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;AACrC,CAAC;AAFD,wCAEC;AAGD,SAAgB,gBAAgB,CAC/B,MAA6B,EAC7B,QAAgB;IAEhB,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,cAAc,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAND,4CAMC;AAED,SAAS,MAAM,CAAC,KAAa;IAC5B,OAAO,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;AAC5D,CAAC;AAcD,MAAa,mBAAmB;IAK/B,YAAY,QAAiB,EAAE,OAA4B;QAJ1C,eAAU,GAAmB,EAAE,CAAC;QAChC,YAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAC5B,aAAQ,GAAa,EAAE,CAAC;QAGxC,IAAI,QAAQ,EAAE;YACb,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;gBACpB,IAAI,EAAE,IAAI;gBACV,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC;gBACxB,MAAM,EAAE,IAAI,GAAG,CAAW,CAAC,OAAO,CAAC,CAAC;aACpC,CAAC,CAAC;SACH;QACD,KAAK,MAAM,GAAG,IAAI,OAAO,IAAI,EAAE,EAAE;YAChC,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;SACrC;IACF,CAAC;IAEO,gBAAgB,CAAC,GAAqB,EAAE,QAAiB;QAChE,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACnE,MAAM,IAAI,qBAAY,CACrB,gBAAgB,EAChB,0CAA0C,CAC1C,CAAC;SACF;QACD,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,GAAG,KAAK,EAAE,EAAE;YAClD,MAAM,IAAI,qBAAY,CACrB,gBAAgB,EAChB,kBAAkB,GAAG,CAAC,IAAI,4BAA4B,CACtD,CAAC;SACF;QACD,IACC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAC1B,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YACvB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,kBAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EACzC;YACD,MAAM,IAAI,qBAAY,CACrB,gBAAgB,EAChB,kBAAkB,GAAG,CAAC,IAAI,kDAAkD,CAC5E,CAAC;SACF;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACrC,MAAM,IAAI,qBAAY,CACrB,gBAAgB,EAChB,2BAA2B,GAAG,CAAC,IAAI,GAAG,CACtC,CAAC;SACF;QACD,IAAI,QAAQ,KAAK,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK,QAAQ,EAAE;YACnD,MAAM,IAAI,qBAAY,CACrB,gBAAgB,EAChB,kBAAkB,GAAG,CAAC,IAAI,wCAAwC,CAClE,CAAC;SACF;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,UAAU,EAAE;YACvC,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,IAAI,IAAA,wBAAe,EAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;gBACvE,MAAM,IAAI,qBAAY,CACrB,gBAAgB,EAChB,kBAAkB,GAAG,CAAC,IAAI,4BAA4B,QAAQ,CAAC,IAAI,GAAG,CACtE,CAAC;aACF;SACD;QACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YACpB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,MAAM;YACN,MAAM,EAAE,IAAI,GAAG,CAAW,GAAG,CAAC,MAAM,CAAC;SACrC,CAAC,CAAC;IACJ,CAAC;IAGD,IAAI,OAAO;QACV,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IACnC,CAAC;IAQD,YAAY,CAAC,MAA0B;QACtC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,OAAO,GAAwB,IAAI,CAAC;QACxC,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE;YACxC,MAAM,KAAK,GAAG,IAAA,wBAAe,EAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAC3D,IACC,KAAK;gBACL,OAAO,KAAK,IAAI;gBAChB,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAC7D;gBACD,OAAO,GAAG,SAAS,CAAC;aACpB;SACD;QACD,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QACnC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IACpE,CAAC;IAOD,MAAM,CAAC,IAAY;QAClB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACb,CAAC;IAGD,QAAQ;QACP,OAAO,IAAI,CAAC,UAAU;aACpB,MAAM,CAAC,CAAC,CAAC,EAAwC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;aACpE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;SACjC,CAAC,CAAC,CAAC;IACN,CAAC;CACD;AA1HD,kDA0HC"}