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.
- package/README.md +306 -1
- package/dist/cli/auth.js +238 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/beignet-node.js +1034 -57
- package/dist/cli/beignet-node.js.map +1 -1
- package/dist/cli/cli.js +1257 -18
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/config.js +45 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/daemon.js +398 -38
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/errors.js +2 -0
- package/dist/cli/errors.js.map +1 -1
- package/dist/cli/index.js +6 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/openapi.js +1343 -89
- package/dist/cli/openapi.js.map +1 -1
- package/dist/cli/restore.js +89 -0
- package/dist/cli/restore.js.map +1 -0
- package/dist/cli/wallet-storage.js +34 -0
- package/dist/cli/wallet-storage.js.map +1 -0
- package/dist/electrum/index.js +107 -12
- package/dist/electrum/index.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lightning/advisor/fee-tuner.js +42 -0
- package/dist/lightning/advisor/fee-tuner.js.map +1 -0
- package/dist/lightning/advisor/index.js +9 -1
- package/dist/lightning/advisor/index.js.map +1 -1
- package/dist/lightning/advisor/rebalance-planner.js +55 -0
- package/dist/lightning/advisor/rebalance-planner.js.map +1 -0
- package/dist/lightning/backup/index.js +18 -0
- package/dist/lightning/backup/index.js.map +1 -0
- package/dist/lightning/backup/scb.js +42 -0
- package/dist/lightning/backup/scb.js.map +1 -0
- package/dist/lightning/chain/chain-monitor.js +27 -0
- package/dist/lightning/chain/chain-monitor.js.map +1 -1
- package/dist/lightning/chain/output-resolver.js +66 -15
- package/dist/lightning/chain/output-resolver.js.map +1 -1
- package/dist/lightning/chain/types.js +1 -0
- package/dist/lightning/chain/types.js.map +1 -1
- package/dist/lightning/channel/channel-manager.js +91 -24
- package/dist/lightning/channel/channel-manager.js.map +1 -1
- package/dist/lightning/channel/channel-state.js.map +1 -1
- package/dist/lightning/channel/channel.js +122 -4
- package/dist/lightning/channel/channel.js.map +1 -1
- package/dist/lightning/channel/commitment-builder.js +4 -7
- package/dist/lightning/channel/commitment-builder.js.map +1 -1
- package/dist/lightning/channel/dual-funding.js +6 -5
- package/dist/lightning/channel/dual-funding.js.map +1 -1
- package/dist/lightning/channel/splice-tx.js.map +1 -1
- package/dist/lightning/channel/types.js +3 -1
- package/dist/lightning/channel/types.js.map +1 -1
- package/dist/lightning/channel/validation.js +3 -3
- package/dist/lightning/channel/validation.js.map +1 -1
- package/dist/lightning/crypto/index.js +1 -0
- package/dist/lightning/crypto/index.js.map +1 -1
- package/dist/lightning/crypto/message-signing.js +116 -0
- package/dist/lightning/crypto/message-signing.js.map +1 -0
- package/dist/lightning/crypto/xchacha20poly1305.js +77 -0
- package/dist/lightning/crypto/xchacha20poly1305.js.map +1 -0
- package/dist/lightning/features/flags.js +1 -0
- package/dist/lightning/features/flags.js.map +1 -1
- package/dist/lightning/gossip/messages.js +37 -1
- package/dist/lightning/gossip/messages.js.map +1 -1
- package/dist/lightning/index.js +2 -1
- package/dist/lightning/index.js.map +1 -1
- package/dist/lightning/keys/signer.js +24 -3
- package/dist/lightning/keys/signer.js.map +1 -1
- package/dist/lightning/keys/wallet-keys.js +1 -0
- package/dist/lightning/keys/wallet-keys.js.map +1 -1
- package/dist/lightning/message/index.js +1 -0
- package/dist/lightning/message/index.js.map +1 -1
- package/dist/lightning/message/peer-storage.js +43 -0
- package/dist/lightning/message/peer-storage.js.map +1 -0
- package/dist/lightning/message/types.js +2 -0
- package/dist/lightning/message/types.js.map +1 -1
- package/dist/lightning/node/lightning-node.js +1593 -236
- package/dist/lightning/node/lightning-node.js.map +1 -1
- package/dist/lightning/node/types.js +18 -1
- package/dist/lightning/node/types.js.map +1 -1
- package/dist/lightning/offer/offer-manager.js +2 -5
- package/dist/lightning/offer/offer-manager.js.map +1 -1
- package/dist/lightning/offer/tlv.js +3 -1
- package/dist/lightning/offer/tlv.js.map +1 -1
- package/dist/lightning/storage/encryption.js +67 -0
- package/dist/lightning/storage/encryption.js.map +1 -0
- package/dist/lightning/storage/index.js +1 -0
- package/dist/lightning/storage/index.js.map +1 -1
- package/dist/lightning/storage/serialization.js +6 -2
- package/dist/lightning/storage/serialization.js.map +1 -1
- package/dist/lightning/storage/sqlite-storage.js +405 -33
- package/dist/lightning/storage/sqlite-storage.js.map +1 -1
- package/dist/lightning/watchtower/blob.js +221 -0
- package/dist/lightning/watchtower/blob.js.map +1 -0
- package/dist/lightning/watchtower/index.js +55 -0
- package/dist/lightning/watchtower/index.js.map +1 -0
- package/dist/lightning/watchtower/justice.js +272 -0
- package/dist/lightning/watchtower/justice.js.map +1 -0
- package/dist/lightning/watchtower/tower-connection.js +208 -0
- package/dist/lightning/watchtower/tower-connection.js.map +1 -0
- package/dist/lightning/watchtower/types.js +3 -0
- package/dist/lightning/watchtower/types.js.map +1 -0
- package/dist/lightning/watchtower/watchtower-client.js +472 -0
- package/dist/lightning/watchtower/watchtower-client.js.map +1 -0
- package/dist/lightning/watchtower/wtwire.js +201 -0
- package/dist/lightning/watchtower/wtwire.js.map +1 -0
- package/dist/logger.js +41 -0
- package/dist/logger.js.map +1 -0
- package/dist/shapes/electrum.js +32 -1
- package/dist/shapes/electrum.js.map +1 -1
- package/dist/shapes/wallet.js +11 -1
- package/dist/shapes/wallet.js.map +1 -1
- package/dist/transaction/index.js +163 -15
- package/dist/transaction/index.js.map +1 -1
- package/dist/types/cli/auth.d.ts +34 -0
- package/dist/types/cli/beignet-node.d.ts +148 -4
- package/dist/types/cli/daemon.d.ts +5 -0
- package/dist/types/cli/errors.d.ts +2 -0
- package/dist/types/cli/index.d.ts +1 -0
- package/dist/types/cli/restore.d.ts +10 -0
- package/dist/types/cli/types.d.ts +235 -1
- package/dist/types/cli/wallet-storage.d.ts +3 -0
- package/dist/types/electrum/index.d.ts +12 -0
- package/dist/types/electrum.js +1 -0
- package/dist/types/electrum.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lightning/advisor/fee-tuner.d.ts +20 -0
- package/dist/types/lightning/advisor/index.d.ts +4 -0
- package/dist/types/lightning/advisor/rebalance-planner.d.ts +12 -0
- package/dist/types/lightning/backup/index.d.ts +1 -0
- package/dist/types/lightning/backup/scb.d.ts +24 -0
- package/dist/types/lightning/chain/chain-monitor.d.ts +1 -0
- package/dist/types/lightning/chain/types.d.ts +1 -0
- package/dist/types/lightning/channel/channel-manager.d.ts +11 -0
- package/dist/types/lightning/channel/channel-state.d.ts +2 -0
- package/dist/types/lightning/channel/channel.d.ts +11 -5
- package/dist/types/lightning/channel/commitment-builder.d.ts +7 -7
- package/dist/types/lightning/channel/dual-funding.d.ts +2 -1
- package/dist/types/lightning/channel/splice-tx.d.ts +2 -2
- package/dist/types/lightning/channel/types.d.ts +2 -0
- package/dist/types/lightning/channel/validation.d.ts +1 -1
- package/dist/types/lightning/crypto/index.d.ts +1 -0
- package/dist/types/lightning/crypto/message-signing.d.ts +9 -0
- package/dist/types/lightning/crypto/xchacha20poly1305.d.ts +3 -0
- package/dist/types/lightning/features/flags.d.ts +1 -0
- package/dist/types/lightning/gossip/messages.d.ts +4 -0
- package/dist/types/lightning/index.d.ts +1 -0
- package/dist/types/lightning/keys/signer.d.ts +18 -1
- package/dist/types/lightning/keys/wallet-keys.d.ts +2 -1
- package/dist/types/lightning/message/index.d.ts +1 -0
- package/dist/types/lightning/message/peer-storage.d.ts +9 -0
- package/dist/types/lightning/message/types.d.ts +2 -0
- package/dist/types/lightning/node/lightning-node.d.ts +120 -4
- package/dist/types/lightning/node/types.d.ts +66 -1
- package/dist/types/lightning/storage/encryption.d.ts +11 -0
- package/dist/types/lightning/storage/index.d.ts +1 -0
- package/dist/types/lightning/storage/serialization.d.ts +2 -0
- package/dist/types/lightning/storage/sqlite-storage.d.ts +43 -3
- package/dist/types/lightning/storage/types.d.ts +62 -0
- package/dist/types/lightning/watchtower/blob.d.ts +46 -0
- package/dist/types/lightning/watchtower/index.d.ts +9 -0
- package/dist/types/lightning/watchtower/justice.d.ts +30 -0
- package/dist/types/lightning/watchtower/tower-connection.d.ts +34 -0
- package/dist/types/lightning/watchtower/types.d.ts +46 -0
- package/dist/types/lightning/watchtower/watchtower-client.d.ts +68 -0
- package/dist/types/lightning/watchtower/wtwire.d.ts +90 -0
- package/dist/types/logger.d.ts +17 -0
- package/dist/types/shapes/electrum.d.ts +3 -1
- package/dist/types/transaction/index.d.ts +6 -0
- package/dist/types/transaction.js.map +1 -1
- package/dist/types/types/electrum.d.ts +3 -2
- package/dist/types/types/transaction.d.ts +1 -1
- package/dist/types/types/wallet.d.ts +80 -4
- package/dist/types/utils/descriptors.d.ts +2 -0
- package/dist/types/utils/fees.d.ts +3 -0
- package/dist/types/utils/helpers.d.ts +3 -1
- package/dist/types/utils/index.d.ts +5 -0
- package/dist/types/utils/multisig.d.ts +29 -0
- package/dist/types/utils/transaction.d.ts +7 -0
- package/dist/types/utils/wallet-storage-encryption.d.ts +3 -0
- package/dist/types/utils/wallet.d.ts +1 -1
- package/dist/types/utils/watch-only.d.ts +12 -0
- package/dist/types/wallet/constants.d.ts +1 -0
- package/dist/types/wallet/index.d.ts +63 -4
- package/dist/types/wallet.js +3 -0
- package/dist/types/wallet.js.map +1 -1
- package/dist/utils/derivation-path.js +8 -7
- package/dist/utils/derivation-path.js.map +1 -1
- package/dist/utils/descriptors.js +56 -0
- package/dist/utils/descriptors.js.map +1 -0
- package/dist/utils/electrum.js +14 -4
- package/dist/utils/electrum.js.map +1 -1
- package/dist/utils/fees.js +19 -0
- package/dist/utils/fees.js.map +1 -0
- package/dist/utils/helpers.js +22 -5
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/multisig.js +90 -0
- package/dist/utils/multisig.js.map +1 -0
- package/dist/utils/transaction.js +30 -3
- package/dist/utils/transaction.js.map +1 -1
- package/dist/utils/wallet-storage-encryption.js +45 -0
- package/dist/utils/wallet-storage-encryption.js.map +1 -0
- package/dist/utils/wallet.js +3 -1
- package/dist/utils/wallet.js.map +1 -1
- package/dist/utils/watch-only.js +102 -0
- package/dist/utils/watch-only.js.map +1 -0
- package/dist/wallet/constants.js +2 -1
- package/dist/wallet/constants.js.map +1 -1
- package/dist/wallet/index.js +810 -51
- package/dist/wallet/index.js.map +1 -1
- package/docs/ROADMAP.md +365 -0
- package/package.json +1 -1
package/dist/cli/daemon.js
CHANGED
|
@@ -23,16 +23,20 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.startDaemon = exports.parseBody = void 0;
|
|
26
|
+
exports.startDaemon = exports.getRelayedEvents = exports.AUTH_EXEMPT_ROUTES = exports.parseBody = void 0;
|
|
27
27
|
const http = __importStar(require("http"));
|
|
28
28
|
const https = __importStar(require("https"));
|
|
29
29
|
const fs = __importStar(require("fs"));
|
|
30
|
+
const console_1 = require("console");
|
|
30
31
|
const beignet_node_1 = require("./beignet-node");
|
|
32
|
+
const logger_1 = require("../logger");
|
|
31
33
|
const errors_1 = require("./errors");
|
|
32
34
|
const openapi_1 = require("./openapi");
|
|
33
35
|
const webhooks_1 = require("./webhooks");
|
|
34
36
|
const payment_queue_1 = require("./payment-queue");
|
|
35
37
|
const http_rate_limiter_1 = require("./http-rate-limiter");
|
|
38
|
+
const transaction_1 = require("../utils/transaction");
|
|
39
|
+
const auth_1 = require("./auth");
|
|
36
40
|
const MAX_BODY_BYTES = 1048576;
|
|
37
41
|
const IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1000;
|
|
38
42
|
const IDEMPOTENCY_CLEANUP_INTERVAL_MS = 10 * 60 * 1000;
|
|
@@ -42,7 +46,9 @@ const IDEMPOTENT_ROUTES = new Set([
|
|
|
42
46
|
'POST /invoice/pay-async',
|
|
43
47
|
'POST /invoice/pay-retry',
|
|
44
48
|
'POST /keysend',
|
|
45
|
-
'POST /keysend/safe'
|
|
49
|
+
'POST /keysend/safe',
|
|
50
|
+
'POST /rebalance',
|
|
51
|
+
'POST /advisor/execute-rebalances'
|
|
46
52
|
]);
|
|
47
53
|
function success(result) {
|
|
48
54
|
return { ok: true, result };
|
|
@@ -50,6 +56,11 @@ function success(result) {
|
|
|
50
56
|
function failure(code, message) {
|
|
51
57
|
return { ok: false, error: { code, message } };
|
|
52
58
|
}
|
|
59
|
+
function insufficientScopeMessage(auth, routeKey) {
|
|
60
|
+
const accepted = [...(0, auth_1.getRouteScopes)(routeKey), 'admin'].join(', ');
|
|
61
|
+
const who = auth.keyName === null ? 'API key' : `API key "${auth.keyName}"`;
|
|
62
|
+
return `${who} lacks the required scope (accepted: ${accepted})`;
|
|
63
|
+
}
|
|
53
64
|
async function parseBody(req) {
|
|
54
65
|
return new Promise((resolve, reject) => {
|
|
55
66
|
const chunks = [];
|
|
@@ -81,28 +92,48 @@ async function parseBody(req) {
|
|
|
81
92
|
});
|
|
82
93
|
}
|
|
83
94
|
exports.parseBody = parseBody;
|
|
84
|
-
|
|
85
|
-
const header = req.headers['authorization'];
|
|
86
|
-
if (!header)
|
|
87
|
-
return false;
|
|
88
|
-
const match = header.match(/^bearer\s+(.+)$/i);
|
|
89
|
-
if (!match)
|
|
90
|
-
return false;
|
|
91
|
-
return match[1] === apiToken;
|
|
92
|
-
}
|
|
93
|
-
const AUTH_EXEMPT_ROUTES = new Set([
|
|
95
|
+
exports.AUTH_EXEMPT_ROUTES = new Set([
|
|
94
96
|
'GET /health',
|
|
95
97
|
'GET /ready',
|
|
96
98
|
'GET /openapi.json',
|
|
97
99
|
'GET /metrics'
|
|
98
100
|
]);
|
|
101
|
+
function getRelayedEvents(htlcEvents) {
|
|
102
|
+
const events = [
|
|
103
|
+
'payment:received',
|
|
104
|
+
'payment:sent',
|
|
105
|
+
'payment:failed',
|
|
106
|
+
'invoice:settled',
|
|
107
|
+
'channel:opening',
|
|
108
|
+
'channel:ready',
|
|
109
|
+
'channel:pending-close',
|
|
110
|
+
'channel:force-closing',
|
|
111
|
+
'channel:closed',
|
|
112
|
+
'peer:connect',
|
|
113
|
+
'peer:disconnect',
|
|
114
|
+
'node:ready'
|
|
115
|
+
];
|
|
116
|
+
if (htlcEvents === true) {
|
|
117
|
+
events.push('htlc:forwarded', 'htlc:fulfilled', 'htlc:failed');
|
|
118
|
+
}
|
|
119
|
+
return events;
|
|
120
|
+
}
|
|
121
|
+
exports.getRelayedEvents = getRelayedEvents;
|
|
99
122
|
async function startDaemon(opts) {
|
|
100
123
|
const port = opts.daemonPort !== undefined && opts.daemonPort !== null
|
|
101
124
|
? opts.daemonPort
|
|
102
125
|
: 2112;
|
|
103
126
|
const host = opts.daemonHost || '127.0.0.1';
|
|
104
|
-
const
|
|
105
|
-
|
|
127
|
+
const authenticator = new auth_1.ApiKeyAuthenticator(opts.apiToken, opts.apiKeys);
|
|
128
|
+
let logger = opts.logger;
|
|
129
|
+
if (!logger && opts.logLevel && opts.logLevel !== 'silent') {
|
|
130
|
+
const stderrConsole = new console_1.Console({
|
|
131
|
+
stdout: process.stderr,
|
|
132
|
+
stderr: process.stderr
|
|
133
|
+
});
|
|
134
|
+
logger = (0, logger_1.createConsoleLogger)(opts.logLevel, stderrConsole);
|
|
135
|
+
}
|
|
136
|
+
const node = await beignet_node_1.BeignetNode.create(logger ? { ...opts, logger } : opts);
|
|
106
137
|
const storage = node.getStorage();
|
|
107
138
|
const webhookManager = new webhooks_1.WebhookManager(storage);
|
|
108
139
|
const paymentQueue = new payment_queue_1.PaymentQueue((bolt11, timeout, maxFee, amount, meta) => node.payInvoiceSafe(bolt11, timeout, maxFee, amount, meta), (amount) => node.canSend(amount), undefined, storage);
|
|
@@ -122,8 +153,8 @@ async function startDaemon(opts) {
|
|
|
122
153
|
const routes = {
|
|
123
154
|
'GET /info': () => success(node.getInfo()),
|
|
124
155
|
'GET /mnemonic': () => {
|
|
125
|
-
if (!
|
|
126
|
-
return failure('MNEMONIC_REQUIRES_AUTH', 'Configure apiToken to enable mnemonic access');
|
|
156
|
+
if (!authenticator.enabled) {
|
|
157
|
+
return failure('MNEMONIC_REQUIRES_AUTH', 'Configure apiToken or apiKeys to enable mnemonic access');
|
|
127
158
|
}
|
|
128
159
|
return success({ mnemonic: node.getMnemonic() });
|
|
129
160
|
},
|
|
@@ -148,6 +179,24 @@ async function startDaemon(opts) {
|
|
|
148
179
|
filter.metadataValue = query.get('metadataValue');
|
|
149
180
|
return success(node.listPayments(Object.keys(filter).length > 0 ? filter : undefined));
|
|
150
181
|
},
|
|
182
|
+
'GET /forwards': (_body, query) => {
|
|
183
|
+
const filter = {};
|
|
184
|
+
if (query.get('since'))
|
|
185
|
+
filter.since = Number(query.get('since'));
|
|
186
|
+
if (query.get('until'))
|
|
187
|
+
filter.until = Number(query.get('until'));
|
|
188
|
+
if (query.get('limit'))
|
|
189
|
+
filter.limit = Number(query.get('limit'));
|
|
190
|
+
if (query.get('offset'))
|
|
191
|
+
filter.offset = Number(query.get('offset'));
|
|
192
|
+
if (query.get('channelId'))
|
|
193
|
+
filter.channelId = query.get('channelId');
|
|
194
|
+
return success(node.listForwards(Object.keys(filter).length > 0 ? filter : undefined));
|
|
195
|
+
},
|
|
196
|
+
'GET /forwards/summary': (_body, query) => {
|
|
197
|
+
const since = query.get('since') ? Number(query.get('since')) : undefined;
|
|
198
|
+
return success(node.getForwardingSummary(since));
|
|
199
|
+
},
|
|
151
200
|
'GET /invoices': () => success(node.listInvoices()),
|
|
152
201
|
'GET /invoice': (_body, query) => {
|
|
153
202
|
const paymentHash = query.get('paymentHash');
|
|
@@ -170,6 +219,41 @@ async function startDaemon(opts) {
|
|
|
170
219
|
},
|
|
171
220
|
'GET /spend-limit': () => success(node.getDailySpendInfo()),
|
|
172
221
|
'GET /liquidity': () => success(node.getLiquiditySnapshot()),
|
|
222
|
+
'GET /watchtowers': () => success({ towers: node.listWatchtowers() }),
|
|
223
|
+
'POST /watchtower/add': (body) => {
|
|
224
|
+
const { uri } = body;
|
|
225
|
+
if (!uri)
|
|
226
|
+
return failure('INVALID_PARAMS', 'uri required');
|
|
227
|
+
try {
|
|
228
|
+
node.addWatchtower(uri);
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
return failure('INVALID_PARAMS', err instanceof Error ? err.message : String(err));
|
|
232
|
+
}
|
|
233
|
+
return success({ added: uri });
|
|
234
|
+
},
|
|
235
|
+
'DELETE /watchtower/remove': (body) => {
|
|
236
|
+
const { uri } = body;
|
|
237
|
+
if (!uri)
|
|
238
|
+
return failure('INVALID_PARAMS', 'uri required');
|
|
239
|
+
node.removeWatchtower(uri);
|
|
240
|
+
return success({ removed: uri });
|
|
241
|
+
},
|
|
242
|
+
'GET /advisor/recommendations': () => success(node.getAdvisorRecommendations()),
|
|
243
|
+
'POST /advisor/execute-rebalances': async (body) => {
|
|
244
|
+
const { budgetSatsPerDay } = body;
|
|
245
|
+
return success(await node.executeRebalances(budgetSatsPerDay));
|
|
246
|
+
},
|
|
247
|
+
'POST /rebalance': async (body) => {
|
|
248
|
+
const { fromChannelId, toChannelId, amountSats, maxFeeSats } = body;
|
|
249
|
+
if (!fromChannelId ||
|
|
250
|
+
!toChannelId ||
|
|
251
|
+
amountSats === undefined ||
|
|
252
|
+
maxFeeSats === undefined) {
|
|
253
|
+
return failure('INVALID_PARAMS', 'fromChannelId, toChannelId, amountSats, and maxFeeSats required');
|
|
254
|
+
}
|
|
255
|
+
return success(await node.rebalanceChannel(fromChannelId, toChannelId, amountSats, maxFeeSats));
|
|
256
|
+
},
|
|
173
257
|
'GET /fees': () => {
|
|
174
258
|
const snapshot = node.getFeeSnapshot();
|
|
175
259
|
if (!snapshot)
|
|
@@ -189,6 +273,26 @@ async function startDaemon(opts) {
|
|
|
189
273
|
return success(txs);
|
|
190
274
|
},
|
|
191
275
|
'GET /utxos': () => success(node.listUtxos()),
|
|
276
|
+
'POST /utxo/freeze': async (body) => {
|
|
277
|
+
const { txid, index } = body;
|
|
278
|
+
if (!txid || index === undefined)
|
|
279
|
+
return failure('INVALID_PARAMS', 'txid and index required');
|
|
280
|
+
return success(await node.freezeUtxo(txid, index));
|
|
281
|
+
},
|
|
282
|
+
'POST /utxo/unfreeze': async (body) => {
|
|
283
|
+
const { txid, index } = body;
|
|
284
|
+
if (!txid || index === undefined)
|
|
285
|
+
return failure('INVALID_PARAMS', 'txid and index required');
|
|
286
|
+
return success(await node.unfreezeUtxo(txid, index));
|
|
287
|
+
},
|
|
288
|
+
'POST /address/label': async (body) => {
|
|
289
|
+
const { address, label } = body;
|
|
290
|
+
if (!address || label === undefined)
|
|
291
|
+
return failure('INVALID_PARAMS', 'address and label required (empty label clears)');
|
|
292
|
+
return success(await node.setAddressLabel(address, label));
|
|
293
|
+
},
|
|
294
|
+
'GET /address/labels': () => success(node.listAddressLabels()),
|
|
295
|
+
'GET /wallet/descriptors': () => success(node.exportDescriptors()),
|
|
192
296
|
'GET /channel/suggestions': (_body, query) => {
|
|
193
297
|
const count = query.get('count') ? Number(query.get('count')) : undefined;
|
|
194
298
|
return success(node.getChannelSuggestions(count));
|
|
@@ -203,7 +307,16 @@ async function startDaemon(opts) {
|
|
|
203
307
|
options.limit = Number(query.get('limit'));
|
|
204
308
|
return success(node.getActionLog(Object.keys(options).length > 0 ? options : undefined));
|
|
205
309
|
},
|
|
206
|
-
'POST /address/new': async () =>
|
|
310
|
+
'POST /address/new': async (body) => {
|
|
311
|
+
const { bip21: wantBip21, amountSats, label, message } = (body ?? {});
|
|
312
|
+
const address = await node.getNewAddress();
|
|
313
|
+
if (!wantBip21)
|
|
314
|
+
return success({ address });
|
|
315
|
+
const uri = (0, transaction_1.encodeBip21)({ address, amountSats, label, message });
|
|
316
|
+
if (uri.isErr())
|
|
317
|
+
return failure('INVALID_PARAMS', uri.error.message);
|
|
318
|
+
return success({ address, bip21: uri.value });
|
|
319
|
+
},
|
|
207
320
|
'POST /wallet/refresh': async () => {
|
|
208
321
|
await node.refreshWallet();
|
|
209
322
|
return success({ refreshed: true });
|
|
@@ -214,10 +327,53 @@ async function startDaemon(opts) {
|
|
|
214
327
|
return failure('INVALID_PARAMS', 'address and amountSats required');
|
|
215
328
|
return success(await node.sendOnchain(address, amountSats, satsPerVbyte));
|
|
216
329
|
},
|
|
330
|
+
'POST /send-max': async (body) => {
|
|
331
|
+
const { address, satsPerVbyte } = body;
|
|
332
|
+
if (!address)
|
|
333
|
+
return failure('INVALID_PARAMS', 'address required');
|
|
334
|
+
return success(await node.sendMaxOnchain(address, satsPerVbyte));
|
|
335
|
+
},
|
|
336
|
+
'POST /tx/bump-fee': async (body) => {
|
|
337
|
+
const { txid, satsPerVbyte } = body;
|
|
338
|
+
if (!txid || satsPerVbyte === undefined)
|
|
339
|
+
return failure('INVALID_PARAMS', 'txid and satsPerVbyte required');
|
|
340
|
+
return success(await node.bumpFeeOnchain(txid, satsPerVbyte));
|
|
341
|
+
},
|
|
342
|
+
'POST /tx/boost': async (body) => {
|
|
343
|
+
const { txid, satsPerVbyte } = body;
|
|
344
|
+
if (!txid)
|
|
345
|
+
return failure('INVALID_PARAMS', 'txid required');
|
|
346
|
+
return success(await node.boostOnchain(txid, satsPerVbyte));
|
|
347
|
+
},
|
|
348
|
+
'GET /transactions/boostable': () => success(node.listBoostableTransactions()),
|
|
349
|
+
'POST /consolidate': async (body) => {
|
|
350
|
+
const { satsPerVbyte } = body;
|
|
351
|
+
return success(await node.consolidateUtxos(satsPerVbyte));
|
|
352
|
+
},
|
|
353
|
+
'POST /psbt/build': async (body) => {
|
|
354
|
+
const { outputs, satsPerVbyte } = body;
|
|
355
|
+
if (!outputs || !Array.isArray(outputs) || outputs.length === 0)
|
|
356
|
+
return failure('INVALID_PARAMS', 'outputs array of { address, amountSats } required');
|
|
357
|
+
return success(await node.buildPsbt(outputs, satsPerVbyte));
|
|
358
|
+
},
|
|
359
|
+
'POST /psbt/import-signed': (body) => {
|
|
360
|
+
const { psbtBase64 } = body;
|
|
361
|
+
if (!psbtBase64)
|
|
362
|
+
return failure('INVALID_PARAMS', 'psbtBase64 required');
|
|
363
|
+
return success(node.importSignedPsbt(psbtBase64));
|
|
364
|
+
},
|
|
365
|
+
'POST /psbt/combine': (body) => {
|
|
366
|
+
const { psbts } = body;
|
|
367
|
+
if (!psbts || !Array.isArray(psbts) || psbts.length < 2)
|
|
368
|
+
return failure('INVALID_PARAMS', 'psbts array with at least two base64 PSBTs required');
|
|
369
|
+
return success(node.combinePsbts(psbts));
|
|
370
|
+
},
|
|
217
371
|
'POST /peer/connect': async (body) => {
|
|
218
372
|
const { pubkey, host: peerHost, port: peerPort } = body;
|
|
219
|
-
if (!pubkey
|
|
220
|
-
return failure('INVALID_PARAMS', 'pubkey
|
|
373
|
+
if (!pubkey)
|
|
374
|
+
return failure('INVALID_PARAMS', 'pubkey required');
|
|
375
|
+
if ((peerHost === undefined) !== (peerPort === undefined))
|
|
376
|
+
return failure('INVALID_PARAMS', 'host and port must be provided together (omit both to resolve by node id)');
|
|
221
377
|
return success(await node.connectPeer(pubkey, peerHost, peerPort));
|
|
222
378
|
},
|
|
223
379
|
'POST /peer/disconnect': (body) => {
|
|
@@ -254,7 +410,7 @@ async function startDaemon(opts) {
|
|
|
254
410
|
commitmentTxid: result.commitmentTxid
|
|
255
411
|
});
|
|
256
412
|
},
|
|
257
|
-
'POST /channel/update-
|
|
413
|
+
'POST /channel/update-commitment-feerate': (body) => {
|
|
258
414
|
const { channelId, feeratePerKw } = body;
|
|
259
415
|
if (!channelId)
|
|
260
416
|
return failure('INVALID_PARAMS', 'channelId required');
|
|
@@ -262,6 +418,32 @@ async function startDaemon(opts) {
|
|
|
262
418
|
return failure('INVALID_PARAMS', 'feeratePerKw required');
|
|
263
419
|
return success(node.updateChannelFee(channelId, feeratePerKw));
|
|
264
420
|
},
|
|
421
|
+
'POST /channel/update-policy': (body) => {
|
|
422
|
+
const { channelId, all, feeBaseMsat, feeProportionalMillionths, cltvExpiryDelta, htlcMinimumMsat, htlcMaximumMsat } = body;
|
|
423
|
+
if (!channelId && all !== true)
|
|
424
|
+
return failure('INVALID_PARAMS', 'channelId or all:true required');
|
|
425
|
+
try {
|
|
426
|
+
return success(node.updateChannelPolicy(all === true ? 'all' : channelId, {
|
|
427
|
+
feeBaseMsat,
|
|
428
|
+
feeProportionalMillionths,
|
|
429
|
+
cltvExpiryDelta,
|
|
430
|
+
htlcMinimumMsat,
|
|
431
|
+
htlcMaximumMsat
|
|
432
|
+
}));
|
|
433
|
+
}
|
|
434
|
+
catch (err) {
|
|
435
|
+
return failure('INVALID_PARAMS', err.message);
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
'GET /channel/policy': (body, query) => {
|
|
439
|
+
const channelId = query.get('channelId') || body.channelId;
|
|
440
|
+
if (!channelId)
|
|
441
|
+
return failure('INVALID_PARAMS', 'channelId required');
|
|
442
|
+
const policy = node.getChannelPolicy(channelId);
|
|
443
|
+
if (!policy)
|
|
444
|
+
return failure('NOT_FOUND', 'Channel not found');
|
|
445
|
+
return success(policy);
|
|
446
|
+
},
|
|
265
447
|
'GET /channel': (body, query) => {
|
|
266
448
|
const channelId = query.get('channelId') || body.channelId;
|
|
267
449
|
if (!channelId)
|
|
@@ -306,6 +488,40 @@ async function startDaemon(opts) {
|
|
|
306
488
|
: undefined;
|
|
307
489
|
return success(node.createInvoice(amountSats, description, expirySecs, hashBuf));
|
|
308
490
|
},
|
|
491
|
+
'POST /invoice/create-hold': (body) => {
|
|
492
|
+
const { paymentHash, amountMsat, amountSats, description, expiry } = body;
|
|
493
|
+
if (!paymentHash)
|
|
494
|
+
return failure('INVALID_PARAMS', 'paymentHash required');
|
|
495
|
+
let amountMsatBig;
|
|
496
|
+
if (amountMsat !== undefined) {
|
|
497
|
+
try {
|
|
498
|
+
amountMsatBig = BigInt(amountMsat);
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
return failure('INVALID_PARAMS', 'amountMsat must be an integer');
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return success(node.createHoldInvoice({
|
|
505
|
+
paymentHash,
|
|
506
|
+
amountMsat: amountMsatBig,
|
|
507
|
+
amountSats,
|
|
508
|
+
description,
|
|
509
|
+
expiry
|
|
510
|
+
}));
|
|
511
|
+
},
|
|
512
|
+
'POST /invoice/settle-hold': (body) => {
|
|
513
|
+
const { preimage } = body;
|
|
514
|
+
if (!preimage)
|
|
515
|
+
return failure('INVALID_PARAMS', 'preimage required');
|
|
516
|
+
return success(node.settleHoldInvoice(preimage));
|
|
517
|
+
},
|
|
518
|
+
'POST /invoice/cancel-hold': (body) => {
|
|
519
|
+
const { paymentHash } = body;
|
|
520
|
+
if (!paymentHash)
|
|
521
|
+
return failure('INVALID_PARAMS', 'paymentHash required');
|
|
522
|
+
return success(node.cancelHoldInvoice(paymentHash));
|
|
523
|
+
},
|
|
524
|
+
'GET /invoices/held': () => success(node.listHoldInvoices()),
|
|
309
525
|
'POST /invoice/decode': (body) => {
|
|
310
526
|
const { bolt11 } = body;
|
|
311
527
|
if (!bolt11)
|
|
@@ -525,6 +741,101 @@ async function startDaemon(opts) {
|
|
|
525
741
|
return failure('INVALID_PARAMS', 'destination and amountSats required');
|
|
526
742
|
return success(node.probeRoute(destination, amountSats));
|
|
527
743
|
},
|
|
744
|
+
'GET /graph/info': () => success(node.getGraphInfo()),
|
|
745
|
+
'GET /graph/node': (_body, query) => {
|
|
746
|
+
const pubkey = query.get('pubkey');
|
|
747
|
+
if (!pubkey)
|
|
748
|
+
return failure('INVALID_PARAMS', 'pubkey required');
|
|
749
|
+
const info = node.getGraphNode(pubkey);
|
|
750
|
+
if (!info)
|
|
751
|
+
return failure('NOT_FOUND', 'Node not found in graph');
|
|
752
|
+
return success(info);
|
|
753
|
+
},
|
|
754
|
+
'GET /graph/channel': (_body, query) => {
|
|
755
|
+
const scid = query.get('scid');
|
|
756
|
+
if (!scid)
|
|
757
|
+
return failure('INVALID_PARAMS', 'scid required');
|
|
758
|
+
const info = node.getGraphChannel(scid);
|
|
759
|
+
if (!info)
|
|
760
|
+
return failure('NOT_FOUND', 'Channel not found in graph');
|
|
761
|
+
return success(info);
|
|
762
|
+
},
|
|
763
|
+
'GET /graph/describe': (_body, query) => {
|
|
764
|
+
const limitParam = query.get('limit');
|
|
765
|
+
const offsetParam = query.get('offset');
|
|
766
|
+
let limit;
|
|
767
|
+
let offset;
|
|
768
|
+
if (limitParam !== null) {
|
|
769
|
+
limit = Number(limitParam);
|
|
770
|
+
if (!Number.isInteger(limit) || limit < 1)
|
|
771
|
+
return failure('INVALID_PARAMS', 'limit must be a positive integer');
|
|
772
|
+
}
|
|
773
|
+
if (offsetParam !== null) {
|
|
774
|
+
offset = Number(offsetParam);
|
|
775
|
+
if (!Number.isInteger(offset) || offset < 0)
|
|
776
|
+
return failure('INVALID_PARAMS', 'offset must be a non-negative integer');
|
|
777
|
+
}
|
|
778
|
+
return success(node.describeGraph(limit, offset));
|
|
779
|
+
},
|
|
780
|
+
'POST /route/query': (body) => {
|
|
781
|
+
const { destination, amountSats, maxFeeSats } = body;
|
|
782
|
+
if (!destination || amountSats === undefined)
|
|
783
|
+
return failure('INVALID_PARAMS', 'destination and amountSats required');
|
|
784
|
+
return success(node.queryRoute(destination, amountSats, maxFeeSats));
|
|
785
|
+
},
|
|
786
|
+
'POST /payment/send-to-route': (body) => {
|
|
787
|
+
const { paymentHash, route, paymentSecret } = body;
|
|
788
|
+
if (!paymentHash || !route)
|
|
789
|
+
return failure('INVALID_PARAMS', 'paymentHash and route required');
|
|
790
|
+
return success(node.sendToRoute(paymentHash, route, paymentSecret));
|
|
791
|
+
},
|
|
792
|
+
'POST /message/sign': (body) => {
|
|
793
|
+
const { message } = body;
|
|
794
|
+
if (!message)
|
|
795
|
+
return failure('INVALID_PARAMS', 'message required');
|
|
796
|
+
return success(node.signMessage(message));
|
|
797
|
+
},
|
|
798
|
+
'POST /message/verify': (body) => {
|
|
799
|
+
const { message, signature } = body;
|
|
800
|
+
if (!message || !signature)
|
|
801
|
+
return failure('INVALID_PARAMS', 'message and signature required');
|
|
802
|
+
return success(node.verifyMessage(message, signature));
|
|
803
|
+
},
|
|
804
|
+
'POST /gossip/sync': (body) => {
|
|
805
|
+
const { pubkey } = body;
|
|
806
|
+
return success({ syncedFrom: node.syncGossip(pubkey) });
|
|
807
|
+
},
|
|
808
|
+
'POST /gossip/sync-rapid': async () => {
|
|
809
|
+
const result = await node.syncRapidGossip();
|
|
810
|
+
if (!result) {
|
|
811
|
+
return failure('INVALID_PARAMS', 'Rapid gossip sync is only available on mainnet');
|
|
812
|
+
}
|
|
813
|
+
return success(result);
|
|
814
|
+
},
|
|
815
|
+
'GET /channel/diagnostics': (body, query) => {
|
|
816
|
+
const channelId = query.get('channelId') || body.channelId;
|
|
817
|
+
if (!channelId)
|
|
818
|
+
return failure('INVALID_PARAMS', 'channelId required');
|
|
819
|
+
const diagnostics = node.getChannelDiagnostics(channelId);
|
|
820
|
+
if (!diagnostics)
|
|
821
|
+
return failure('NOT_FOUND', 'Channel not found');
|
|
822
|
+
return success(diagnostics);
|
|
823
|
+
},
|
|
824
|
+
'POST /address/validate': (body) => {
|
|
825
|
+
const { address } = body;
|
|
826
|
+
if (!address)
|
|
827
|
+
return failure('INVALID_PARAMS', 'address required');
|
|
828
|
+
return success({ address, valid: node.validateAddress(address) });
|
|
829
|
+
},
|
|
830
|
+
'POST /recover-fallback-funds': async (body) => {
|
|
831
|
+
const { feeRatePerVbyte } = body;
|
|
832
|
+
const result = await node.recoverFallbackFunds(feeRatePerVbyte !== undefined ? { feeRatePerVbyte } : undefined);
|
|
833
|
+
return success(result ?? { recovered: false });
|
|
834
|
+
},
|
|
835
|
+
'POST /backup/trigger': () => {
|
|
836
|
+
node.triggerBackup();
|
|
837
|
+
return success({ triggered: true });
|
|
838
|
+
},
|
|
528
839
|
'POST /backup': async (body) => {
|
|
529
840
|
const { destPath } = body;
|
|
530
841
|
if (!destPath)
|
|
@@ -537,6 +848,35 @@ async function startDaemon(opts) {
|
|
|
537
848
|
await node.backup(destPath);
|
|
538
849
|
return success({ backed_up: true });
|
|
539
850
|
},
|
|
851
|
+
'GET /backup/scb': () => success(node.exportStaticChannelBackup()),
|
|
852
|
+
'GET /backup/peer-retrieved': () => {
|
|
853
|
+
const retrieved = node.getPeerRetrievedBackup();
|
|
854
|
+
if (!retrieved) {
|
|
855
|
+
return failure('NOT_FOUND', 'No peer-retrieved backup this session (no capable peer has returned our SCB yet)');
|
|
856
|
+
}
|
|
857
|
+
return success(retrieved);
|
|
858
|
+
},
|
|
859
|
+
'POST /restore/scb': async (body) => {
|
|
860
|
+
const { encoded, path: scbPath } = body;
|
|
861
|
+
if ((encoded ? 1 : 0) + (scbPath ? 1 : 0) !== 1) {
|
|
862
|
+
return failure('INVALID_PARAMS', 'Provide exactly one of encoded or path');
|
|
863
|
+
}
|
|
864
|
+
let blob = encoded;
|
|
865
|
+
if (scbPath) {
|
|
866
|
+
if (scbPath.includes('..') ||
|
|
867
|
+
scbPath.includes('%2e%2e') ||
|
|
868
|
+
scbPath.includes('%2E%2E')) {
|
|
869
|
+
return failure('INVALID_PARAMS', 'Path traversal not allowed');
|
|
870
|
+
}
|
|
871
|
+
try {
|
|
872
|
+
blob = fs.readFileSync(scbPath, 'utf8');
|
|
873
|
+
}
|
|
874
|
+
catch (err) {
|
|
875
|
+
return failure('INVALID_PARAMS', `Cannot read SCB file: ${err.message}`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return success(await node.restoreFromScb(blob));
|
|
879
|
+
},
|
|
540
880
|
'POST /offer/create': (body) => {
|
|
541
881
|
const { description, amountSats, issuer } = body;
|
|
542
882
|
if (!description)
|
|
@@ -586,8 +926,20 @@ async function startDaemon(opts) {
|
|
|
586
926
|
if (!cancelled)
|
|
587
927
|
return failure('NOT_FOUND', 'Queued payment not found or already processing');
|
|
588
928
|
return success({ cancelled: true });
|
|
929
|
+
},
|
|
930
|
+
'GET /auth/keys': () => success({ keys: authenticator.listKeys() }),
|
|
931
|
+
'POST /auth/keys/revoke': (body) => {
|
|
932
|
+
const { name } = body;
|
|
933
|
+
if (!name)
|
|
934
|
+
return failure('INVALID_PARAMS', 'name required');
|
|
935
|
+
const revoked = authenticator.revoke(name);
|
|
936
|
+
if (!revoked)
|
|
937
|
+
return failure('NOT_FOUND', `No API key named "${name}" (the legacy apiToken has no name; remove it from the config and restart)`);
|
|
938
|
+
return success({ revoked: name });
|
|
589
939
|
}
|
|
590
940
|
};
|
|
941
|
+
routes['POST /channel/update-fee'] =
|
|
942
|
+
routes['POST /channel/update-commitment-feerate'];
|
|
591
943
|
const sseClients = new Set();
|
|
592
944
|
const corsOrigin = opts.cors === true ? '*' : typeof opts.cors === 'string' ? opts.cors : null;
|
|
593
945
|
if (opts.tlsCert && !opts.tlsKey) {
|
|
@@ -616,11 +968,20 @@ async function startDaemon(opts) {
|
|
|
616
968
|
return;
|
|
617
969
|
}
|
|
618
970
|
if (routeKey === 'GET /events') {
|
|
619
|
-
if (
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
971
|
+
if (authenticator.enabled) {
|
|
972
|
+
const auth = authenticator.authenticate(req.headers['authorization']);
|
|
973
|
+
if (!auth.ok) {
|
|
974
|
+
res.setHeader('Content-Type', 'application/json');
|
|
975
|
+
res.statusCode = 401;
|
|
976
|
+
res.end(JSON.stringify(failure('UNAUTHORIZED', 'Invalid or missing Authorization header')));
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (!(0, auth_1.scopesAllowRoute)(auth.scopes, routeKey)) {
|
|
980
|
+
res.setHeader('Content-Type', 'application/json');
|
|
981
|
+
res.statusCode = 403;
|
|
982
|
+
res.end(JSON.stringify(failure('FORBIDDEN', insufficientScopeMessage(auth, routeKey))));
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
624
985
|
}
|
|
625
986
|
const sseHeaders = {
|
|
626
987
|
'Content-Type': 'text/event-stream',
|
|
@@ -634,6 +995,7 @@ async function startDaemon(opts) {
|
|
|
634
995
|
'Content-Type, Authorization';
|
|
635
996
|
}
|
|
636
997
|
res.writeHead(200, sseHeaders);
|
|
998
|
+
res.write(': connected\n\n');
|
|
637
999
|
sseClients.add(res);
|
|
638
1000
|
const keepalive = setInterval(() => {
|
|
639
1001
|
res.write(': keepalive\n\n');
|
|
@@ -650,14 +1012,20 @@ async function startDaemon(opts) {
|
|
|
650
1012
|
return;
|
|
651
1013
|
}
|
|
652
1014
|
res.setHeader('Content-Type', 'application/json');
|
|
653
|
-
if (
|
|
654
|
-
|
|
1015
|
+
if (authenticator.enabled && !exports.AUTH_EXEMPT_ROUTES.has(routeKey)) {
|
|
1016
|
+
const auth = authenticator.authenticate(req.headers['authorization']);
|
|
1017
|
+
if (!auth.ok) {
|
|
655
1018
|
res.statusCode = 401;
|
|
656
1019
|
res.end(JSON.stringify(failure('UNAUTHORIZED', 'Invalid or missing Authorization header')));
|
|
657
1020
|
return;
|
|
658
1021
|
}
|
|
1022
|
+
if (!(0, auth_1.scopesAllowRoute)(auth.scopes, routeKey)) {
|
|
1023
|
+
res.statusCode = 403;
|
|
1024
|
+
res.end(JSON.stringify(failure('FORBIDDEN', insufficientScopeMessage(auth, routeKey))));
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
659
1027
|
}
|
|
660
|
-
if (rateLimiter && !AUTH_EXEMPT_ROUTES.has(routeKey)) {
|
|
1028
|
+
if (rateLimiter && !exports.AUTH_EXEMPT_ROUTES.has(routeKey)) {
|
|
661
1029
|
const clientKey = req.headers['authorization'] || req.socket.remoteAddress || 'unknown';
|
|
662
1030
|
if (!rateLimiter.isAllowed(clientKey)) {
|
|
663
1031
|
res.statusCode = 429;
|
|
@@ -747,16 +1115,7 @@ async function startDaemon(opts) {
|
|
|
747
1115
|
else {
|
|
748
1116
|
server = http.createServer(requestHandler);
|
|
749
1117
|
}
|
|
750
|
-
const sseEvents =
|
|
751
|
-
'payment:received',
|
|
752
|
-
'payment:sent',
|
|
753
|
-
'payment:failed',
|
|
754
|
-
'channel:ready',
|
|
755
|
-
'channel:closed',
|
|
756
|
-
'peer:connect',
|
|
757
|
-
'peer:disconnect',
|
|
758
|
-
'node:ready'
|
|
759
|
-
];
|
|
1118
|
+
const sseEvents = getRelayedEvents(opts.htlcEvents);
|
|
760
1119
|
for (const eventName of sseEvents) {
|
|
761
1120
|
node.on(eventName, (data) => {
|
|
762
1121
|
if (sseClients.size === 0)
|
|
@@ -775,6 +1134,7 @@ async function startDaemon(opts) {
|
|
|
775
1134
|
return new Promise((resolve, reject) => {
|
|
776
1135
|
server.on('error', reject);
|
|
777
1136
|
server.listen(port, host, () => {
|
|
1137
|
+
logger?.info(`Daemon listening on ${host}:${port}`);
|
|
778
1138
|
resolve({ server, node });
|
|
779
1139
|
});
|
|
780
1140
|
});
|