@toon-protocol/connector 2.1.0 → 2.3.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 +117 -46
- package/dist/btp/btp-client-manager.d.ts +3 -0
- package/dist/btp/btp-client-manager.d.ts.map +1 -1
- package/dist/btp/btp-client-manager.js +19 -4
- package/dist/btp/btp-client-manager.js.map +1 -1
- package/dist/btp/btp-client.d.ts +4 -1
- package/dist/btp/btp-client.d.ts.map +1 -1
- package/dist/btp/btp-client.js +24 -10
- package/dist/btp/btp-client.js.map +1 -1
- package/dist/btp/inbound-claim-validator.d.ts +4 -1
- package/dist/btp/inbound-claim-validator.d.ts.map +1 -1
- package/dist/btp/inbound-claim-validator.js +23 -3
- package/dist/btp/inbound-claim-validator.js.map +1 -1
- package/dist/config/config-loader.d.ts +4 -0
- package/dist/config/config-loader.d.ts.map +1 -1
- package/dist/config/config-loader.js +166 -1
- package/dist/config/config-loader.js.map +1 -1
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/types.d.ts +22 -15
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +2 -5
- package/dist/config/types.js.map +1 -1
- package/dist/core/connector-node.d.ts +12 -1
- package/dist/core/connector-node.d.ts.map +1 -1
- package/dist/core/connector-node.js +259 -25
- package/dist/core/connector-node.js.map +1 -1
- package/dist/core/packet-handler.d.ts +10 -1
- package/dist/core/packet-handler.d.ts.map +1 -1
- package/dist/core/packet-handler.js +67 -2
- package/dist/core/packet-handler.js.map +1 -1
- package/dist/http/types.d.ts +4 -0
- package/dist/http/types.d.ts.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/settlement/claim-receiver.d.ts +5 -1
- package/dist/settlement/claim-receiver.d.ts.map +1 -1
- package/dist/settlement/claim-receiver.js +28 -1
- package/dist/settlement/claim-receiver.js.map +1 -1
- package/dist/settlement/per-packet-claim-service.d.ts +6 -1
- package/dist/settlement/per-packet-claim-service.d.ts.map +1 -1
- package/dist/settlement/per-packet-claim-service.js +38 -8
- package/dist/settlement/per-packet-claim-service.js.map +1 -1
- package/dist/settlement/privacy/nip59-claim-wrapper.d.ts +10 -0
- package/dist/settlement/privacy/nip59-claim-wrapper.d.ts.map +1 -1
- package/dist/settlement/privacy/nip59-claim-wrapper.js +110 -2
- package/dist/settlement/privacy/nip59-claim-wrapper.js.map +1 -1
- package/dist/settlement/provider/payment-channel-provider.d.ts +9 -0
- package/dist/settlement/provider/payment-channel-provider.d.ts.map +1 -1
- package/dist/transport/direct-transport-provider.d.ts +12 -0
- package/dist/transport/direct-transport-provider.d.ts.map +1 -0
- package/dist/transport/direct-transport-provider.js +27 -0
- package/dist/transport/direct-transport-provider.js.map +1 -0
- package/dist/transport/index.d.ts +7 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +16 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/managed-anon-client.d.ts +47 -0
- package/dist/transport/managed-anon-client.d.ts.map +1 -0
- package/dist/transport/managed-anon-client.js +264 -0
- package/dist/transport/managed-anon-client.js.map +1 -0
- package/dist/transport/probe-tcp-port.d.ts +3 -0
- package/dist/transport/probe-tcp-port.d.ts.map +1 -0
- package/dist/transport/probe-tcp-port.js +59 -0
- package/dist/transport/probe-tcp-port.js.map +1 -0
- package/dist/transport/socks-transport-provider.d.ts +29 -0
- package/dist/transport/socks-transport-provider.d.ts.map +1 -0
- package/dist/transport/socks-transport-provider.js +136 -0
- package/dist/transport/socks-transport-provider.js.map +1 -0
- package/dist/transport/socks-url.d.ts +6 -0
- package/dist/transport/socks-url.d.ts.map +1 -0
- package/dist/transport/socks-url.js +29 -0
- package/dist/transport/socks-url.js.map +1 -0
- package/dist/transport/transport-provider.d.ts +9 -0
- package/dist/transport/transport-provider.d.ts.map +1 -0
- package/dist/transport/transport-provider.js +3 -0
- package/dist/transport/transport-provider.js.map +1 -0
- package/dist/utils/redact.d.ts +3 -0
- package/dist/utils/redact.d.ts.map +1 -0
- package/dist/utils/redact.js +21 -0
- package/dist/utils/redact.js.map +1 -0
- package/package.json +4 -2
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ConnectorNode = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const nodePath = tslib_1.__importStar(require("path"));
|
|
5
7
|
const routing_table_1 = require("../routing/routing-table");
|
|
6
8
|
const btp_client_manager_1 = require("../btp/btp-client-manager");
|
|
7
9
|
const btp_server_1 = require("../btp/btp-server");
|
|
8
10
|
const packet_handler_1 = require("./packet-handler");
|
|
9
11
|
const shared_1 = require("@toon-protocol/shared");
|
|
10
12
|
const types_1 = require("../config/types");
|
|
13
|
+
const transport_1 = require("../transport");
|
|
11
14
|
const payment_handler_1 = require("./payment-handler");
|
|
12
15
|
const types_2 = require("../settlement/types");
|
|
13
16
|
const admin_api_1 = require("../http/admin-api");
|
|
@@ -30,6 +33,8 @@ const chain_provider_registry_1 = require("../settlement/provider/chain-provider
|
|
|
30
33
|
const evm_payment_channel_provider_1 = require("../settlement/provider/evm-payment-channel-provider");
|
|
31
34
|
const claim_sender_db_schema_1 = require("../settlement/claim-sender-db-schema");
|
|
32
35
|
const inbound_claim_validator_1 = require("../btp/inbound-claim-validator");
|
|
36
|
+
const nip59_claim_wrapper_1 = require("../settlement/privacy/nip59-claim-wrapper");
|
|
37
|
+
const utils_1 = require("@noble/hashes/utils");
|
|
33
38
|
const dns_1 = require("dns");
|
|
34
39
|
const package_json_1 = tslib_1.__importDefault(require("../../package.json"));
|
|
35
40
|
class ConnectorNode {
|
|
@@ -54,10 +59,17 @@ class ConnectorNode {
|
|
|
54
59
|
_startTime = new Date();
|
|
55
60
|
_btpServerStarted = false;
|
|
56
61
|
_defaultSettlementTokenId = 'M2M';
|
|
62
|
+
_transportProvider = null;
|
|
63
|
+
_transportProviderReady = false;
|
|
64
|
+
_transportType = null;
|
|
65
|
+
_lastTransportHealthy = true;
|
|
66
|
+
_transportHealthInterval = null;
|
|
67
|
+
_transportHealthIntervalMs;
|
|
57
68
|
get defaultSettlementTokenId() {
|
|
58
69
|
return this._defaultSettlementTokenId;
|
|
59
70
|
}
|
|
60
|
-
constructor(config, logger) {
|
|
71
|
+
constructor(config, logger, opts) {
|
|
72
|
+
this._transportHealthIntervalMs = opts?.transportHealthIntervalMs ?? 30000;
|
|
61
73
|
let resolvedConfig;
|
|
62
74
|
try {
|
|
63
75
|
if (typeof config === 'string') {
|
|
@@ -90,6 +102,7 @@ class ConnectorNode {
|
|
|
90
102
|
}));
|
|
91
103
|
this._routingTable = new routing_table_1.RoutingTable(routingTableEntries, logger.child({ component: 'RoutingTable' }));
|
|
92
104
|
this._btpClientManager = new btp_client_manager_1.BTPClientManager(resolvedConfig.nodeId, logger.child({ component: 'BTPClientManager' }));
|
|
105
|
+
this._btpClientManager.setAgentFactory((peerUrl) => this._transportProvider?.createAgent(peerUrl));
|
|
93
106
|
this._packetHandler = new packet_handler_1.PacketHandler(this._routingTable, this._btpClientManager, resolvedConfig.nodeId, logger.child({ component: 'PacketHandler' }));
|
|
94
107
|
this._btpServer = new btp_server_1.BTPServer(logger.child({ component: 'BTPServer' }), this._packetHandler);
|
|
95
108
|
this._packetHandler.setBTPServer(this._btpServer);
|
|
@@ -195,12 +208,56 @@ class ConnectorNode {
|
|
|
195
208
|
}, 'Starting connector node');
|
|
196
209
|
try {
|
|
197
210
|
(0, types_1.validateChainProviders)(this._config, this._logger);
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
const createdProvider = this._createTransportProvider(this._config.transport);
|
|
212
|
+
this._transportProvider = createdProvider;
|
|
213
|
+
this._transportType =
|
|
214
|
+
this._config.transport === undefined ? 'direct' : this._config.transport.type;
|
|
215
|
+
try {
|
|
216
|
+
await createdProvider.start();
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
this._transportProvider = null;
|
|
220
|
+
this._transportType = null;
|
|
221
|
+
this._transportProviderReady = false;
|
|
222
|
+
throw err;
|
|
223
|
+
}
|
|
224
|
+
this._transportProviderReady = true;
|
|
225
|
+
this._lastTransportHealthy = true;
|
|
226
|
+
this._transportHealthInterval = setInterval(() => {
|
|
227
|
+
const provider = this._transportProvider;
|
|
228
|
+
if (!provider || !this._transportProviderReady)
|
|
229
|
+
return;
|
|
230
|
+
provider
|
|
231
|
+
.healthCheck()
|
|
232
|
+
.then((healthy) => {
|
|
233
|
+
if (this._transportProviderReady && this._transportProvider === provider) {
|
|
234
|
+
this._lastTransportHealthy = healthy;
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
.catch(() => {
|
|
238
|
+
if (this._transportProviderReady && this._transportProvider === provider) {
|
|
239
|
+
this._lastTransportHealthy = false;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}, this._transportHealthIntervalMs);
|
|
243
|
+
this._transportHealthInterval.unref?.();
|
|
244
|
+
const evmProviderConfig = this._config.chainProviders?.find((p) => p.chainType === 'evm');
|
|
245
|
+
const legacySettlementVars = [
|
|
246
|
+
'BASE_L2_RPC_URL',
|
|
247
|
+
'SETTLEMENT_ENABLED',
|
|
248
|
+
'TOKEN_NETWORK_REGISTRY',
|
|
249
|
+
'M2M_TOKEN_ADDRESS',
|
|
250
|
+
'TREASURY_EVM_PRIVATE_KEY',
|
|
251
|
+
];
|
|
252
|
+
const detectedLegacyVars = legacySettlementVars.filter((v) => process.env[v]);
|
|
253
|
+
if (detectedLegacyVars.length > 0) {
|
|
254
|
+
this._logger.warn({ event: 'legacy_env_vars_detected', vars: detectedLegacyVars }, 'Detected legacy settlement env vars -- these are no longer used. Configure chainProviders with an EVM entry instead.');
|
|
255
|
+
}
|
|
256
|
+
const baseRpcUrl = evmProviderConfig?.rpcUrl;
|
|
257
|
+
const registryAddress = evmProviderConfig?.registryAddress;
|
|
258
|
+
const m2mTokenAddress = evmProviderConfig?.tokenAddress;
|
|
259
|
+
const treasuryPrivateKey = evmProviderConfig?.keyId;
|
|
260
|
+
if (evmProviderConfig &&
|
|
204
261
|
baseRpcUrl &&
|
|
205
262
|
registryAddress &&
|
|
206
263
|
m2mTokenAddress &&
|
|
@@ -285,9 +342,8 @@ class ConnectorNode {
|
|
|
285
342
|
const tokenAddressMap = new Map();
|
|
286
343
|
tokenAddressMap.set(this._defaultSettlementTokenId, m2mTokenAddress);
|
|
287
344
|
tokenAddressMap.set(m2mTokenAddress, m2mTokenAddress);
|
|
288
|
-
const defaultSettlementTimeout =
|
|
289
|
-
const initialDepositMultiplier =
|
|
290
|
-
parseInt(process.env.INITIAL_DEPOSIT_MULTIPLIER ?? '1', 10);
|
|
345
|
+
const defaultSettlementTimeout = evmProviderConfig.settlementOptions?.settlementTimeoutSecs ?? 86400;
|
|
346
|
+
const initialDepositMultiplier = evmProviderConfig.settlementOptions?.initialDepositMultiplier ?? 1;
|
|
291
347
|
let accountManager;
|
|
292
348
|
const tigerBeetleClusterId = process.env.TIGERBEETLE_CLUSTER_ID;
|
|
293
349
|
const tigerBeetleReplicas = process.env.TIGERBEETLE_REPLICAS;
|
|
@@ -346,7 +402,7 @@ class ConnectorNode {
|
|
|
346
402
|
this._accountManager = accountManager;
|
|
347
403
|
}
|
|
348
404
|
const peerIds = Array.from(peerIdToAddressMap.keys());
|
|
349
|
-
const settlementThreshold = BigInt(
|
|
405
|
+
const settlementThreshold = BigInt(evmProviderConfig.settlementOptions?.threshold ?? '1000000');
|
|
350
406
|
this._logger.info({
|
|
351
407
|
event: 'settlement_monitor_config',
|
|
352
408
|
peerIds,
|
|
@@ -363,7 +419,7 @@ class ConnectorNode {
|
|
|
363
419
|
const chainProviderChainId = this._config.chainProviders?.find((cp) => cp.chainType === 'evm')?.chainId;
|
|
364
420
|
const primaryChainIdStr = primaryChainId
|
|
365
421
|
? `evm:${primaryChainId}`
|
|
366
|
-
: chainProviderChainId ?? 'evm:unknown';
|
|
422
|
+
: (chainProviderChainId ?? 'evm:unknown');
|
|
367
423
|
const chainRegistry = new chain_provider_registry_1.ChainProviderRegistry();
|
|
368
424
|
const evmProvider = new evm_payment_channel_provider_1.EVMPaymentChannelProvider(this._paymentChannelSDK, primaryChainIdStr, m2mTokenAddress, this._logger);
|
|
369
425
|
chainRegistry.register(evmProvider);
|
|
@@ -381,6 +437,23 @@ class ConnectorNode {
|
|
|
381
437
|
peerIdToChainMap.set(peerId, primaryChainIdStr);
|
|
382
438
|
}
|
|
383
439
|
}
|
|
440
|
+
const nip59Enabled = this._config.nip59?.enabled ?? false;
|
|
441
|
+
const nip59Wrapper = new nip59_claim_wrapper_1.NIP59ClaimWrapper({
|
|
442
|
+
nip59Enabled,
|
|
443
|
+
logger: this._logger,
|
|
444
|
+
});
|
|
445
|
+
const nodeSecp256k1PrivKey = treasuryPrivateKey
|
|
446
|
+
? (0, utils_1.hexToBytes)(treasuryPrivateKey.replace(/^0x/, ''))
|
|
447
|
+
: undefined;
|
|
448
|
+
const peerIdToNip59PubKey = new Map();
|
|
449
|
+
for (const peer of this._config.peers) {
|
|
450
|
+
if (peer.nip59PublicKey) {
|
|
451
|
+
peerIdToNip59PubKey.set(peer.id, (0, utils_1.hexToBytes)(peer.nip59PublicKey));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (nip59Enabled) {
|
|
455
|
+
this._logger.info({ event: 'nip59_enabled', peerCount: peerIdToNip59PubKey.size }, 'NIP-59 transport privacy enabled for claim wrapping');
|
|
456
|
+
}
|
|
384
457
|
this._settlementExecutor = new settlement_executor_1.SettlementExecutor({
|
|
385
458
|
nodeId: this._config.nodeId,
|
|
386
459
|
defaultSettlementTimeout,
|
|
@@ -430,7 +503,7 @@ class ConnectorNode {
|
|
|
430
503
|
for (const indexSql of claim_sender_db_schema_1.SENT_CLAIMS_INDEXES) {
|
|
431
504
|
claimDb.exec(indexSql);
|
|
432
505
|
}
|
|
433
|
-
const perPacketClaimService = new per_packet_claim_service_1.PerPacketClaimService(chainRegistry, this._channelManager, claimDb, this._logger, this._config.nodeId, peerIdToChainMap);
|
|
506
|
+
const perPacketClaimService = new per_packet_claim_service_1.PerPacketClaimService(chainRegistry, this._channelManager, claimDb, this._logger, this._config.nodeId, peerIdToChainMap, nip59Wrapper, nodeSecp256k1PrivKey, peerIdToNip59PubKey);
|
|
434
507
|
this._packetHandler.setPerPacketClaimService(perPacketClaimService);
|
|
435
508
|
this._settlementExecutor?.setPerPacketClaimService(perPacketClaimService);
|
|
436
509
|
this._logger.info({ event: 'per_packet_claims_enabled' }, 'Per-packet claim service wired to PacketHandler and SettlementExecutor');
|
|
@@ -441,7 +514,7 @@ class ConnectorNode {
|
|
|
441
514
|
throw error;
|
|
442
515
|
}
|
|
443
516
|
}
|
|
444
|
-
const inboundClaimValidator = new inbound_claim_validator_1.InboundClaimValidator(this._paymentChannelSDK, this._config.nodeId, this._logger, this._channelManager ?? undefined);
|
|
517
|
+
const inboundClaimValidator = new inbound_claim_validator_1.InboundClaimValidator(this._paymentChannelSDK, this._config.nodeId, this._logger, this._channelManager ?? undefined, nip59Wrapper, nodeSecp256k1PrivKey);
|
|
445
518
|
this._btpServer.setInboundClaimValidator((protocolData, ilpPacket, peerId) => inboundClaimValidator.validate(protocolData, ilpPacket, peerId));
|
|
446
519
|
this._logger.info({ event: 'inbound_claim_validator_enabled' }, 'Inbound claim validator wired to BTP server');
|
|
447
520
|
if (this._paymentChannelSDK) {
|
|
@@ -451,7 +524,7 @@ class ConnectorNode {
|
|
|
451
524
|
const receivedClaimDbPath = `./data/received-claims-${this._config.nodeId}.db`;
|
|
452
525
|
const receivedClaimDb = new BetterSqlite3(receivedClaimDbPath);
|
|
453
526
|
(0, claim_receiver_db_schema_1.initializeClaimReceiverSchema)(receivedClaimDb);
|
|
454
|
-
const claimReceiver = new claim_receiver_1.ClaimReceiver(receivedClaimDb, chainRegistry, this._logger, this._channelManager ?? undefined, peerIdToAddressMap);
|
|
527
|
+
const claimReceiver = new claim_receiver_1.ClaimReceiver(receivedClaimDb, chainRegistry, this._logger, this._channelManager ?? undefined, peerIdToAddressMap, nip59Wrapper, nodeSecp256k1PrivKey);
|
|
455
528
|
claimReceiver.registerWithBTPServer(this._btpServer);
|
|
456
529
|
if (this._settlementMonitor) {
|
|
457
530
|
this._settlementMonitor.setClaimReceiver(claimReceiver);
|
|
@@ -468,7 +541,7 @@ class ConnectorNode {
|
|
|
468
541
|
if (accountManager) {
|
|
469
542
|
const settlementConfig = {
|
|
470
543
|
connectorFeePercentage: this._config.settlement?.connectorFeePercentage ?? 0.1,
|
|
471
|
-
enableSettlement:
|
|
544
|
+
enableSettlement: true,
|
|
472
545
|
tigerBeetleClusterId: tigerBeetleClusterId ? parseInt(tigerBeetleClusterId, 10) : 0,
|
|
473
546
|
tigerBeetleReplicas: tigerBeetleReplicas
|
|
474
547
|
? tigerBeetleReplicas.split(',').map((s) => s.trim())
|
|
@@ -594,6 +667,24 @@ class ConnectorNode {
|
|
|
594
667
|
nodeId: this._config.nodeId,
|
|
595
668
|
error: errorMessage,
|
|
596
669
|
}, 'Failed to start connector node');
|
|
670
|
+
if (this._transportHealthInterval) {
|
|
671
|
+
clearInterval(this._transportHealthInterval);
|
|
672
|
+
this._transportHealthInterval = null;
|
|
673
|
+
}
|
|
674
|
+
if (this._transportProvider) {
|
|
675
|
+
this._transportProviderReady = false;
|
|
676
|
+
try {
|
|
677
|
+
await this._transportProvider.stop();
|
|
678
|
+
}
|
|
679
|
+
catch (stopErr) {
|
|
680
|
+
const stopMsg = stopErr instanceof Error ? stopErr.message : String(stopErr);
|
|
681
|
+
this._logger.warn({ event: 'transport_rollback_stop_failed', error: stopMsg }, 'Transport provider stop() failed during start() rollback; continuing');
|
|
682
|
+
}
|
|
683
|
+
finally {
|
|
684
|
+
this._transportProvider = null;
|
|
685
|
+
this._transportType = null;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
597
688
|
this._healthStatus = 'unhealthy';
|
|
598
689
|
throw error;
|
|
599
690
|
}
|
|
@@ -654,6 +745,20 @@ class ConnectorNode {
|
|
|
654
745
|
}
|
|
655
746
|
await this._healthServer.stop();
|
|
656
747
|
await this._btpServer.stop();
|
|
748
|
+
if (this._transportHealthInterval) {
|
|
749
|
+
clearInterval(this._transportHealthInterval);
|
|
750
|
+
this._transportHealthInterval = null;
|
|
751
|
+
}
|
|
752
|
+
if (this._transportProvider) {
|
|
753
|
+
this._transportProviderReady = false;
|
|
754
|
+
try {
|
|
755
|
+
await this._transportProvider.stop();
|
|
756
|
+
}
|
|
757
|
+
finally {
|
|
758
|
+
this._transportProvider = null;
|
|
759
|
+
this._transportType = null;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
657
762
|
this._logger.info({
|
|
658
763
|
event: 'connector_stopped',
|
|
659
764
|
nodeId: this._config.nodeId,
|
|
@@ -685,6 +790,12 @@ class ConnectorNode {
|
|
|
685
790
|
nodeId: this._config.nodeId,
|
|
686
791
|
version: package_json_1.default.version,
|
|
687
792
|
};
|
|
793
|
+
if (this._transportProviderReady && this._transportProvider && this._transportType) {
|
|
794
|
+
healthStatus.transport = {
|
|
795
|
+
type: this._transportType,
|
|
796
|
+
healthy: this._transportType === 'direct' ? true : this._lastTransportHealthy,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
688
799
|
return healthStatus;
|
|
689
800
|
}
|
|
690
801
|
get routingTable() {
|
|
@@ -693,6 +804,129 @@ class ConnectorNode {
|
|
|
693
804
|
get btpClientManager() {
|
|
694
805
|
return this._btpClientManager;
|
|
695
806
|
}
|
|
807
|
+
get transportProvider() {
|
|
808
|
+
return this._transportProviderReady ? this._transportProvider : null;
|
|
809
|
+
}
|
|
810
|
+
_createTransportProvider(cfg) {
|
|
811
|
+
if (cfg === undefined || cfg.type === 'direct') {
|
|
812
|
+
const externalUrl = `ws://localhost:${this._config.btpServerPort}`;
|
|
813
|
+
this._logger.debug({ event: 'direct_transport_external_url_synthesized', externalUrl }, 'DirectTransportProvider externalUrl synthesized from btpServerPort (local placeholder)');
|
|
814
|
+
return new transport_1.DirectTransportProvider(externalUrl);
|
|
815
|
+
}
|
|
816
|
+
if (cfg.type === 'socks5') {
|
|
817
|
+
let managedClient;
|
|
818
|
+
let externalUrl = cfg.externalUrl;
|
|
819
|
+
if (cfg.managed === true) {
|
|
820
|
+
let cachedFactory;
|
|
821
|
+
let prewarmError;
|
|
822
|
+
const prewarmPromise = (0, transport_1.createDefaultAnonFactory)().then((f) => {
|
|
823
|
+
cachedFactory = f;
|
|
824
|
+
}, (err) => {
|
|
825
|
+
prewarmError = err;
|
|
826
|
+
});
|
|
827
|
+
const anonFactory = (opts) => {
|
|
828
|
+
if (cachedFactory) {
|
|
829
|
+
return cachedFactory(opts);
|
|
830
|
+
}
|
|
831
|
+
if (prewarmError) {
|
|
832
|
+
if (prewarmError.code === 'MODULE_NOT_FOUND') {
|
|
833
|
+
throw prewarmError;
|
|
834
|
+
}
|
|
835
|
+
throw new Error(`Failed to load optional dependency "@anyone-protocol/anyone-client": ` +
|
|
836
|
+
`${prewarmError.message ?? String(prewarmError)}`, { cause: prewarmError });
|
|
837
|
+
}
|
|
838
|
+
try {
|
|
839
|
+
const pkg = '@anyone-protocol/anyone-client';
|
|
840
|
+
const mod = require(pkg);
|
|
841
|
+
const AnonCtor = mod?.Anon ?? mod?.default?.Anon ?? mod?.default;
|
|
842
|
+
if (typeof AnonCtor !== 'function') {
|
|
843
|
+
throw new Error('@anyone-protocol/anyone-client did not export an `Anon` constructor');
|
|
844
|
+
}
|
|
845
|
+
cachedFactory = (o) => new AnonCtor(o);
|
|
846
|
+
return cachedFactory(opts);
|
|
847
|
+
}
|
|
848
|
+
catch (err) {
|
|
849
|
+
const cause = err;
|
|
850
|
+
if (cause?.code === 'MODULE_NOT_FOUND') {
|
|
851
|
+
throw cause;
|
|
852
|
+
}
|
|
853
|
+
if (cause?.code === 'ERR_REQUIRE_ESM') {
|
|
854
|
+
throw new Error(`@anyone-protocol/anyone-client is an ESM-only package; ` +
|
|
855
|
+
`the async lazy-import pre-warm had not completed when the factory ` +
|
|
856
|
+
`was invoked. This is a timing bug — please file an issue.`, { cause });
|
|
857
|
+
}
|
|
858
|
+
throw new Error(`Failed to load optional dependency "@anyone-protocol/anyone-client": ` +
|
|
859
|
+
`${cause?.message ?? String(err)}`, { cause });
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
void prewarmPromise;
|
|
863
|
+
managedClient = new transport_1.ManagedAnonClient({
|
|
864
|
+
socksProxy: cfg.socksProxy,
|
|
865
|
+
hiddenServiceDir: cfg.managedOptions?.hiddenServiceDir,
|
|
866
|
+
hiddenServicePort: cfg.managedOptions?.hiddenServicePort,
|
|
867
|
+
binaryPath: cfg.managedOptions?.binaryPath,
|
|
868
|
+
startupTimeoutMs: cfg.managedOptions?.startupTimeoutMs,
|
|
869
|
+
stopTimeoutMs: cfg.managedOptions?.stopTimeoutMs,
|
|
870
|
+
logger: this._logger,
|
|
871
|
+
anonFactory,
|
|
872
|
+
});
|
|
873
|
+
if (externalUrl === 'auto') {
|
|
874
|
+
externalUrl = 'wss://pending.invalid/btp';
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
else if (externalUrl === 'auto') {
|
|
878
|
+
throw new Error('_createTransportProvider: transport.externalUrl "auto" requires managed: true');
|
|
879
|
+
}
|
|
880
|
+
let resolveExternalUrlOnStart;
|
|
881
|
+
if (cfg.externalUrl === 'auto') {
|
|
882
|
+
const hsDir = cfg.managedOptions?.hiddenServiceDir;
|
|
883
|
+
if (!hsDir) {
|
|
884
|
+
throw new Error('_createTransportProvider: transport.externalUrl "auto" requires managedOptions.hiddenServiceDir');
|
|
885
|
+
}
|
|
886
|
+
const hostnameReadDeadlineMs = cfg.managedOptions?.startupTimeoutMs ?? 30_000;
|
|
887
|
+
const HIDDEN_SERVICE_HOSTNAME_RE = /^[a-z2-7]{16}(?:[a-z2-7]{40})?\.(?:anon|onion)$/;
|
|
888
|
+
resolveExternalUrlOnStart = async () => {
|
|
889
|
+
const hostnameFile = nodePath.join(hsDir, 'hostname');
|
|
890
|
+
const deadline = Date.now() + hostnameReadDeadlineMs;
|
|
891
|
+
let lastErr;
|
|
892
|
+
while (Date.now() < deadline) {
|
|
893
|
+
try {
|
|
894
|
+
const raw = await fs_1.promises.readFile(hostnameFile, 'utf8');
|
|
895
|
+
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? '';
|
|
896
|
+
const hostname = firstLine;
|
|
897
|
+
if (!hostname) {
|
|
898
|
+
lastErr = new Error('hostname file is empty');
|
|
899
|
+
}
|
|
900
|
+
else if (!HIDDEN_SERVICE_HOSTNAME_RE.test(hostname)) {
|
|
901
|
+
lastErr = new Error(`hostname file contents did not match the expected hidden service format ` +
|
|
902
|
+
`(length=${hostname.length}); ignoring and retrying`);
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
return `wss://${hostname}/btp`;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
catch (err) {
|
|
909
|
+
lastErr = err;
|
|
910
|
+
}
|
|
911
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
912
|
+
}
|
|
913
|
+
const reason = lastErr instanceof Error ? lastErr.message : String(lastErr ?? 'unknown');
|
|
914
|
+
throw new Error(`hidden service hostname file "${hostnameFile}" did not become readable ` +
|
|
915
|
+
`within ${hostnameReadDeadlineMs}ms (last error: ${reason})`);
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
return new transport_1.SocksTransportProvider({
|
|
919
|
+
socksProxy: cfg.socksProxy,
|
|
920
|
+
externalUrl,
|
|
921
|
+
logger: this._logger,
|
|
922
|
+
managedClient,
|
|
923
|
+
resolveExternalUrlOnStart,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
const _exhaustive = cfg;
|
|
927
|
+
throw new Error(`Unsupported transport type: ${JSON.stringify(_exhaustive)} ` +
|
|
928
|
+
'(Story 35.4 _createTransportProvider exhaustiveness guard)');
|
|
929
|
+
}
|
|
696
930
|
get paymentChannelSDK() {
|
|
697
931
|
return this._paymentChannelSDK;
|
|
698
932
|
}
|
|
@@ -706,11 +940,9 @@ class ConnectorNode {
|
|
|
706
940
|
return this._chainSDKs.get(chainId) ?? null;
|
|
707
941
|
}
|
|
708
942
|
async _createInMemoryAccountManager() {
|
|
709
|
-
const
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const persistIntervalMs = this._config.settlementInfra?.ledgerPersistIntervalMs ??
|
|
713
|
-
parseInt(process.env.LEDGER_PERSIST_INTERVAL_MS || '30000', 10);
|
|
943
|
+
const evmProvider = this._config.chainProviders?.find((p) => p.chainType === 'evm');
|
|
944
|
+
const snapshotPath = evmProvider?.settlementOptions?.ledgerSnapshotPath ?? './data/ledger-snapshot.json';
|
|
945
|
+
const persistIntervalMs = evmProvider?.settlementOptions?.ledgerPersistIntervalMs ?? 30000;
|
|
714
946
|
let inMemoryClient;
|
|
715
947
|
try {
|
|
716
948
|
inMemoryClient = new in_memory_ledger_client_1.InMemoryLedgerClient({
|
|
@@ -814,8 +1046,10 @@ class ConnectorNode {
|
|
|
814
1046
|
typeof config.authToken !== 'string') {
|
|
815
1047
|
throw new Error('authToken must be a string (can be empty for no auth)');
|
|
816
1048
|
}
|
|
817
|
-
|
|
818
|
-
|
|
1049
|
+
const PLAIN_WS_PREFIX = 'ws' + '://';
|
|
1050
|
+
const SECURE_WS_PREFIX = 'wss' + '://';
|
|
1051
|
+
if (!config.url.startsWith(PLAIN_WS_PREFIX) && !config.url.startsWith(SECURE_WS_PREFIX)) {
|
|
1052
|
+
throw new Error(`URL must start with ${PLAIN_WS_PREFIX} or ${SECURE_WS_PREFIX}`);
|
|
819
1053
|
}
|
|
820
1054
|
if (config.routes) {
|
|
821
1055
|
for (const route of config.routes) {
|
|
@@ -981,7 +1215,7 @@ class ConnectorNode {
|
|
|
981
1215
|
throw new config_loader_1.ConnectorNotStartedError('Connector is not started. Call start() before openChannel().');
|
|
982
1216
|
}
|
|
983
1217
|
if (!this._channelManager) {
|
|
984
|
-
throw new Error('
|
|
1218
|
+
throw new Error('No EVM chain provider configured -- openChannel requires a chainProviders entry with chainType: "evm"');
|
|
985
1219
|
}
|
|
986
1220
|
const existingPeers = this._btpClientManager.getPeerIds();
|
|
987
1221
|
if (!existingPeers.includes(params.peerId)) {
|
|
@@ -1012,7 +1246,7 @@ class ConnectorNode {
|
|
|
1012
1246
|
throw new config_loader_1.ConnectorNotStartedError('Connector is not started. Call start() before getChannelState().');
|
|
1013
1247
|
}
|
|
1014
1248
|
if (!this._channelManager) {
|
|
1015
|
-
throw new Error('
|
|
1249
|
+
throw new Error('No EVM chain provider configured -- openChannel requires a chainProviders entry with chainType: "evm"');
|
|
1016
1250
|
}
|
|
1017
1251
|
const metadata = this._channelManager.getChannelById(channelId);
|
|
1018
1252
|
if (!metadata) {
|