@toon-protocol/relay 1.3.3 → 1.3.4

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.
@@ -1246,6 +1246,48 @@ import {
1246
1246
  fromMnemonic,
1247
1247
  fromSecretKey
1248
1248
  } from "@toon-protocol/sdk";
1249
+
1250
+ // src/launcher/handlers/oblivious-write-handler.ts
1251
+ import { verifyEvent as verifyEvent2 } from "nostr-tools/pure";
1252
+ function createObliviousWriteHandler(config) {
1253
+ return {
1254
+ async handleWrite(c) {
1255
+ let body;
1256
+ try {
1257
+ body = await c.req.json();
1258
+ } catch {
1259
+ return c.json({ error: "Invalid request body" }, 400);
1260
+ }
1261
+ if (!body.event) {
1262
+ return c.json({ error: "Missing required field: event" }, 400);
1263
+ }
1264
+ const event = body.event;
1265
+ const payer = c.req.header("X-TOON-Payer");
1266
+ const amount = c.req.header("X-TOON-Amount");
1267
+ const chain = c.req.header("X-TOON-Chain");
1268
+ console.log(
1269
+ `[oblivious-write] event=${event.id} payer=${payer ?? "-"} amount=${amount ?? "-"} chain=${chain ?? "-"}`
1270
+ );
1271
+ if (!config.devMode && !verifyEvent2(event)) {
1272
+ return c.json({ error: "Invalid event signature" }, 422);
1273
+ }
1274
+ config.eventStore.store(event);
1275
+ config.onStored?.(event);
1276
+ return c.json(
1277
+ {
1278
+ eventId: event.id,
1279
+ storedAt: Math.floor(Date.now() / 1e3),
1280
+ payer,
1281
+ amount,
1282
+ chain
1283
+ },
1284
+ 200
1285
+ );
1286
+ }
1287
+ };
1288
+ }
1289
+
1290
+ // src/launcher/town.ts
1249
1291
  import {
1250
1292
  BootstrapService,
1251
1293
  createDiscoveryTracker,
@@ -1325,6 +1367,12 @@ async function startRelay(config) {
1325
1367
  "RelayConfig: provide either connector or connectorUrl, not both"
1326
1368
  );
1327
1369
  }
1370
+ const obliviousMode = config.obliviousMode ?? process.env["TOON_OBLIVIOUS_MODE"] === "true";
1371
+ if (obliviousMode && (hasConnector || hasConnectorUrl)) {
1372
+ throw new Error(
1373
+ "RelayConfig: obliviousMode is mutually exclusive with connector/connectorUrl (an oblivious relay runs no embedded connector)"
1374
+ );
1375
+ }
1328
1376
  if (hasConnectorUrl && config.ilpAddress === void 0) {
1329
1377
  throw new Error(
1330
1378
  "RelayConfig: ilpAddress is required when connectorUrl is set (must fall under the parent connector prefix, e.g. g.townhouse.<self>)"
@@ -1342,7 +1390,7 @@ async function startRelay(config) {
1342
1390
  const connectorUrl = config.connectorUrl;
1343
1391
  const basePricePerByte = config.feePerEvent !== void 0 ? BigInt(config.feePerEvent) : config.basePricePerByte ?? 10n;
1344
1392
  const routingBufferPercent = config.routingBufferPercent ?? 10;
1345
- const x402Enabled = config.x402Enabled ?? false;
1393
+ const x402Enabled = obliviousMode ? false : config.x402Enabled ?? false;
1346
1394
  const knownPeers = [...config.knownPeers ?? []];
1347
1395
  const dataDir = config.dataDir ?? "./data";
1348
1396
  const devMode = config.devMode ?? false;
@@ -1362,7 +1410,7 @@ async function startRelay(config) {
1362
1410
  const publishSeedEntryFlag = config.publishSeedEntry ?? false;
1363
1411
  const externalRelayUrl = config.externalRelayUrl ?? (config.ator?.enabled && config.ator.anonAddress ? config.ator.anonAddress : void 0);
1364
1412
  const requestedChain = process.env["TOON_CHAIN"] || config.chain;
1365
- const relayOnly = requestedChain === "none";
1413
+ const relayOnly = requestedChain === "none" || obliviousMode;
1366
1414
  if (relayOnly) {
1367
1415
  console.log("[Town] connector.relay_only", {
1368
1416
  reason: "no settlement chain configured (chain=none)"
@@ -1398,10 +1446,11 @@ async function startRelay(config) {
1398
1446
  seedRelays,
1399
1447
  publishSeedEntry: publishSeedEntryFlag,
1400
1448
  ...externalRelayUrl && { externalRelayUrl },
1401
- chain: chainConfig.name
1449
+ chain: chainConfig.name,
1450
+ obliviousMode
1402
1451
  };
1403
1452
  let autoCreatedConnector = null;
1404
- if (!hasConnector) {
1453
+ if (!hasConnector && !obliviousMode) {
1405
1454
  const btpServerPort = config.btpServerPort ?? 3e3;
1406
1455
  const connectorLogger = createConnectorLogger(
1407
1456
  nodeId,
@@ -1488,7 +1537,7 @@ async function startRelay(config) {
1488
1537
  const effectiveConnector = config.connector ?? autoCreatedConnector;
1489
1538
  mkdirSync(dataDir, { recursive: true });
1490
1539
  const dbPath = join(dataDir, "events.db");
1491
- const eventStore = new SqliteEventStore(dbPath);
1540
+ const eventStore = config.eventStore ?? new SqliteEventStore(dbPath);
1492
1541
  const effectiveChainRpcUrls = config.chainRpcUrls ?? (relayOnly ? void 0 : { [chainKey]: chainConfig.rpcUrl });
1493
1542
  const effectivePreferredTokens = config.preferredTokens ?? (relayOnly ? void 0 : { [chainKey]: chainConfig.usdcAddress });
1494
1543
  const effectiveTokenNetworks = config.tokenNetworks ?? (chainConfig.tokenNetworkAddress ? { [chainKey]: chainConfig.tokenNetworkAddress } : void 0);
@@ -1514,13 +1563,13 @@ async function startRelay(config) {
1514
1563
  preferredTokens: effectivePreferredTokens,
1515
1564
  tokenNetworks: effectiveTokenNetworks
1516
1565
  };
1517
- if (effectiveConnector.openChannel && effectiveConnector.getChannelState) {
1566
+ if (effectiveConnector?.openChannel && effectiveConnector.getChannelState) {
1518
1567
  channelClient = createDirectChannelClient(
1519
1568
  effectiveConnector
1520
1569
  );
1521
1570
  }
1522
1571
  }
1523
- const adminClient = createDirectConnectorAdmin(effectiveConnector);
1572
+ const adminClient = effectiveConnector ? createDirectConnectorAdmin(effectiveConnector) : void 0;
1524
1573
  const verifier = createVerificationPipeline({ devMode });
1525
1574
  const pricer = createPricingValidator({
1526
1575
  basePricePerByte,
@@ -1635,38 +1684,40 @@ async function startRelay(config) {
1635
1684
  })
1636
1685
  );
1637
1686
  });
1638
- app.post("/handle-packet", async (c) => {
1639
- try {
1640
- const body = await c.req.json();
1641
- if (body.amount === void 0 || body.amount === null || body.destination === void 0 || body.destination === null || body.data === void 0 || body.data === null) {
1642
- return c.json(
1643
- { accept: false, code: "F00", message: "Missing required fields" },
1644
- 400
1645
- );
1646
- }
1647
- const result = await handlePacket(body);
1648
- if (result.accept) {
1649
- try {
1650
- const toonBytes = Buffer.from(body.data, "base64");
1651
- const decoded = decodeEventFromToon2(toonBytes);
1652
- if (decoded && decoded.kind === ILP_PEER_INFO_KIND) {
1653
- discoveryTrackerRef.current?.processEvent(decoded);
1687
+ if (!obliviousMode) {
1688
+ app.post("/handle-packet", async (c) => {
1689
+ try {
1690
+ const body = await c.req.json();
1691
+ if (body.amount === void 0 || body.amount === null || body.destination === void 0 || body.destination === null || body.data === void 0 || body.data === null) {
1692
+ return c.json(
1693
+ { accept: false, code: "F00", message: "Missing required fields" },
1694
+ 400
1695
+ );
1696
+ }
1697
+ const result = await handlePacket(body);
1698
+ if (result.accept) {
1699
+ try {
1700
+ const toonBytes = Buffer.from(body.data, "base64");
1701
+ const decoded = decodeEventFromToon2(toonBytes);
1702
+ if (decoded && decoded.kind === ILP_PEER_INFO_KIND) {
1703
+ discoveryTrackerRef.current?.processEvent(decoded);
1704
+ }
1705
+ } catch {
1654
1706
  }
1655
- } catch {
1656
1707
  }
1708
+ return c.json(result, result.accept ? 200 : 400);
1709
+ } catch (error) {
1710
+ console.error("[Town] handle-packet error:", error);
1711
+ return c.json(
1712
+ { accept: false, code: "T00", message: "Internal server error" },
1713
+ 500
1714
+ );
1657
1715
  }
1658
- return c.json(result, result.accept ? 200 : 400);
1659
- } catch (error) {
1660
- console.error("[Town] handle-packet error:", error);
1661
- return c.json(
1662
- { accept: false, code: "T00", message: "Internal server error" },
1663
- 500
1664
- );
1665
- }
1666
- });
1667
- const ilpClient = createDirectIlpClient(effectiveConnector, {
1716
+ });
1717
+ }
1718
+ const ilpClient = effectiveConnector ? createDirectIlpClient(effectiveConnector, {
1668
1719
  toonDecoder: (bytes) => decodeEventFromToon2(bytes)
1669
- });
1720
+ }) : void 0;
1670
1721
  let x402WalletClient;
1671
1722
  let x402PublicClient;
1672
1723
  if (x402Enabled) {
@@ -1700,21 +1751,38 @@ async function startRelay(config) {
1700
1751
  }
1701
1752
  }
1702
1753
  }
1703
- const x402Handler = createX402Handler({
1704
- x402Enabled,
1705
- chainConfig,
1706
- basePricePerByte,
1707
- routingBufferPercent,
1708
- facilitatorAddress: config.facilitatorAddress ?? identity.evmAddress,
1709
- ownPubkey: identity.pubkey,
1710
- devMode,
1711
- eventStore,
1712
- ilpClient,
1713
- walletClient: x402WalletClient,
1714
- publicClient: x402PublicClient
1715
- });
1716
- app.get("/publish", (c) => x402Handler.handlePublish(c));
1717
- app.post("/publish", (c) => x402Handler.handlePublish(c));
1754
+ if (obliviousMode) {
1755
+ const obliviousHandler = createObliviousWriteHandler({
1756
+ eventStore,
1757
+ devMode,
1758
+ onStored: (event) => {
1759
+ try {
1760
+ wsRelayRef.current?.broadcastEvent(event);
1761
+ } catch {
1762
+ }
1763
+ if (event.kind === ILP_PEER_INFO_KIND) {
1764
+ discoveryTrackerRef.current?.processEvent(event);
1765
+ }
1766
+ }
1767
+ });
1768
+ app.post("/write", (c) => obliviousHandler.handleWrite(c));
1769
+ } else {
1770
+ const x402Handler = createX402Handler({
1771
+ x402Enabled,
1772
+ chainConfig,
1773
+ basePricePerByte,
1774
+ routingBufferPercent,
1775
+ facilitatorAddress: config.facilitatorAddress ?? identity.evmAddress,
1776
+ ownPubkey: identity.pubkey,
1777
+ devMode,
1778
+ eventStore,
1779
+ ilpClient,
1780
+ walletClient: x402WalletClient,
1781
+ publicClient: x402PublicClient
1782
+ });
1783
+ app.get("/publish", (c) => x402Handler.handlePublish(c));
1784
+ app.post("/publish", (c) => x402Handler.handlePublish(c));
1785
+ }
1718
1786
  const blsServer = serve({
1719
1787
  fetch: app.fetch,
1720
1788
  port: blsPort
@@ -1728,11 +1796,15 @@ async function startRelay(config) {
1728
1796
  await wsRelay.start();
1729
1797
  await new Promise((resolve) => setTimeout(resolve, 500));
1730
1798
  let running = true;
1731
- bootstrapService.setConnectorAdmin(adminClient);
1799
+ if (adminClient) {
1800
+ bootstrapService.setConnectorAdmin(adminClient);
1801
+ }
1732
1802
  if (channelClient) {
1733
1803
  bootstrapService.setChannelClient(channelClient);
1734
1804
  }
1735
- bootstrapService.setIlpClient(ilpClient);
1805
+ if (ilpClient) {
1806
+ bootstrapService.setIlpClient(ilpClient);
1807
+ }
1736
1808
  bootstrapService.on((event) => {
1737
1809
  switch (event.type) {
1738
1810
  case "bootstrap:peer-registered":
@@ -1745,7 +1817,7 @@ async function startRelay(config) {
1745
1817
  break;
1746
1818
  }
1747
1819
  });
1748
- if (effectiveConnector.setPacketHandler) {
1820
+ if (effectiveConnector?.setPacketHandler) {
1749
1821
  effectiveConnector.setPacketHandler(async (request) => {
1750
1822
  const result = await handlePacket(request);
1751
1823
  if (result.accept && discoveryTrackerRef.current) {
@@ -1771,7 +1843,9 @@ async function startRelay(config) {
1771
1843
  secretKey: identity.secretKey,
1772
1844
  settlementInfo
1773
1845
  });
1774
- discoveryTracker.setConnectorAdmin(adminClient);
1846
+ if (adminClient) {
1847
+ discoveryTracker.setConnectorAdmin(adminClient);
1848
+ }
1775
1849
  if (channelClient) {
1776
1850
  discoveryTracker.setChannelClient(channelClient);
1777
1851
  }
@@ -1841,7 +1915,7 @@ async function startRelay(config) {
1841
1915
  eventStore.store(ilpInfoEvent);
1842
1916
  const firstPeer = knownPeers[0];
1843
1917
  const genesisResult = results[0];
1844
- if (firstPeer && genesisResult) {
1918
+ if (ilpClient && firstPeer && genesisResult) {
1845
1919
  const genesisIlpAddress = genesisResult.peerInfo.ilpAddress;
1846
1920
  const toonBytes = encodeEventToToon3(ilpInfoEvent);
1847
1921
  const base64Toon = Buffer.from(toonBytes).toString("base64");
@@ -1897,7 +1971,7 @@ async function startRelay(config) {
1897
1971
  eventStore.store(serviceDiscoveryEvent);
1898
1972
  const firstPeer = knownPeers[0];
1899
1973
  const genesisResult = results[0];
1900
- if (firstPeer && genesisResult) {
1974
+ if (ilpClient && firstPeer && genesisResult) {
1901
1975
  const genesisIlpAddress = genesisResult.peerInfo.ilpAddress;
1902
1976
  const sdToonBytes = encodeEventToToon3(serviceDiscoveryEvent);
1903
1977
  const sdBase64Toon = Buffer.from(sdToonBytes).toString("base64");
@@ -2026,4 +2100,4 @@ export {
2026
2100
  startRelay,
2027
2101
  startTown
2028
2102
  };
2029
- //# sourceMappingURL=chunk-ZKWFGHZ7.js.map
2103
+ //# sourceMappingURL=chunk-NYKVCNJL.js.map