@ckb-ccc/libp2p 0.0.2 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @ckb-ccc/libp2p
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#532](https://github.com/ckb-devrel/ccc/pull/532) [`47bd5cb`](https://github.com/ckb-devrel/ccc/commit/47bd5cb4fb23949ab01a7300ce1de2dc7ea61e40) Thanks [@Hanssen0](https://github.com/Hanssen0)! - Improve long-lived Khie connections by redialing known addresses for every
8
+ JSON-RPC stream, sharing address updates with Identify Push, and retaining
9
+ learned addresses for the session.
10
+
11
+ - [#532](https://github.com/ckb-devrel/ccc/pull/532) [`3fddda3`](https://github.com/ckb-devrel/ccc/commit/3fddda3d1ec1b6d2c693b77431c04c2d0167b781) Thanks [@Hanssen0](https://github.com/Hanssen0)! - Improve Khie reconnection across disconnects and page suspension, preserving
12
+ active pairings and restoring direct connections when possible.
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [[`182b880`](https://github.com/ckb-devrel/ccc/commit/182b880bd6343b7ecdc1f5732c1f72cd5a22a44d)]:
17
+ - @ckb-ccc/core@1.21.0
18
+
19
+ ## 0.1.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [#525](https://github.com/ckb-devrel/ccc/pull/525) [`f8c79a5`](https://github.com/ckb-devrel/ccc/commit/f8c79a5dc6f60a52b286b5caa744f00ad2d0ffb4) Thanks [@Hanssen0](https://github.com/Hanssen0)! - Improve Khie connection fallback and path selection. Allow configuring the
24
+ default Relay and add the `hide-mark` Connector attribute.
25
+
26
+ - [#523](https://github.com/ckb-devrel/ccc/pull/523) [`58b3b4d`](https://github.com/ckb-devrel/ccc/commit/58b3b4d307f5eb65f5f710127cb3bd5bf3191d22) Thanks [@Hanssen0](https://github.com/Hanssen0)! - Expose the actual stream connection on incoming JSON-RPC requests so consumers
27
+ can report the transport path used by each request.
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [[`58b3b4d`](https://github.com/ckb-devrel/ccc/commit/58b3b4d307f5eb65f5f710127cb3bd5bf3191d22), [`fc937f0`](https://github.com/ckb-devrel/ccc/commit/fc937f08f9e33c852697831dc5b17a9423fd9e3f)]:
32
+ - @ckb-ccc/core@1.20.1
33
+
3
34
  ## 0.0.2
4
35
 
5
36
  ### Patch Changes
package/dist/barrel.d.mts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { ccc } from "@ckb-ccc/core";
2
2
  import { Multiaddr } from "@multiformats/multiaddr";
3
- import { AbortOptions, Libp2p, PeerId } from "@libp2p/interface";
4
- import { ConnectionManager, Registrar } from "@libp2p/interface-internal";
3
+ import { AbortOptions, Connection, Libp2p, PeerId } from "@libp2p/interface";
4
+ import { AddressManager, ConnectionManager, Registrar } from "@libp2p/interface-internal";
5
+ //#region src/dial.d.ts
6
+ declare function dialKnownAddresses(node: Libp2p, peerId: PeerId, signal?: AbortSignal): Promise<import("@libp2p/interface").Connection | undefined>;
7
+ //#endregion
5
8
  //#region src/jsonRpcService.d.ts
6
9
  type JsonRpcServiceComponents = {
7
10
  registrar: Registrar;
@@ -11,6 +14,7 @@ type JsonRpcServiceConfig = {
11
14
  maxMessageLength?: number;
12
15
  };
13
16
  type JsonRpcRequest = {
17
+ connection: Connection;
14
18
  peerId: PeerId;
15
19
  payload: ccc.JsonRpcPayload;
16
20
  };
@@ -46,10 +50,13 @@ declare class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {
46
50
  private readonly timeout;
47
51
  constructor(node: Libp2p, peerId: PeerId, config: JsonRpcTransportLibp2pConfig);
48
52
  request(payload: ccc.JsonRpcPayload): Promise<ccc.JsonRpcResponse>;
53
+ private openStream;
54
+ private connections;
49
55
  }
50
56
  //#endregion
51
57
  //#region src/pairingService.d.ts
52
58
  type PairingServiceComponents = {
59
+ addressManager?: Pick<AddressManager, "getAddresses">;
53
60
  connectionManager: ConnectionManager;
54
61
  registrar: Registrar;
55
62
  };
@@ -90,6 +97,8 @@ declare abstract class PairingService<Components extends PairingServiceComponent
90
97
  private handleProtocol;
91
98
  private addPairedPeer;
92
99
  private notifyPairedPeer;
100
+ private addresses;
101
+ private openFallbackConnection;
93
102
  private rotateSecret;
94
103
  private removePairedPeer;
95
104
  private createPeerTimeout;
@@ -107,8 +116,8 @@ declare class PairingEndpointRoleError extends Error {
107
116
  declare function encodePairingEndpoint(endpointUrl: string, addresses: readonly Multiaddr[], secret: string, role?: string): Promise<string>;
108
117
  declare function decodePairingEndpoint(endpoint: string, expectedRole?: string): Promise<PairingTarget>;
109
118
  declare namespace barrel_d_exports {
110
- export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, encodePairingEndpoint, jsonRpcService, pairingService };
119
+ export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, dialKnownAddresses, encodePairingEndpoint, jsonRpcService, pairingService };
111
120
  }
112
121
  //#endregion
113
- export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, encodePairingEndpoint, jsonRpcService, pairingService, barrel_d_exports as t };
122
+ export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, dialKnownAddresses, encodePairingEndpoint, jsonRpcService, pairingService, barrel_d_exports as t };
114
123
  //# sourceMappingURL=barrel.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"barrel.d.mts","names":[],"sources":["../src/jsonRpcService.ts","../src/jsonRpcTransport.ts","../src/pairingService.ts","../src/pairingEndpoint.ts","../src/barrel.ts"],"mappings":";;;;;KAOY;EACV,WAAW;;KAGD;EACV;EACA;;KAGU;EACV,QAAQ;EACR,SAAS,IAAI;;KAGH,sBACV,mBAAmB,2BAA2B,6BAC3C,MAAM,eAAe,aAAa,SAAS;uBAE1B,eACpB,mBAAmB,2BAA2B;WAMnC,YAAY;WACZ,QAAQ;mBALF;WACR;EAGE,YAAA,YAAY,YACZ,QAAQ;qBAaA,cAAc,SAAS;EAE1C,QAAQ,WAAW,OAAO;EAKpB,SAAK;EAQL,QAAI;UAII;UA6CN;;iBA4CM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,sBAAsB,eAC7B,YAAY,eAAe,eAAe;;;KCxJlC;EACV;EACA;EACA;EACA,SAAS;EACT,cAAc,UAAU,IAAI;;cAGjB,kCAAkC,IAAI;mBAK9B;WACR,QAAQ;mBACA;mBANF;mBACA;EAGE,YAAA,MAAM,QACd,QAAQ,QACA,QAAQ;EAoBrB,QAAQ,SAAS,IAAI,iBAAiB,QAAQ,IAAI;;;;KC5B9C;EACV,mBAAmB;EACnB,WAAW;;KAGD;;EAEV;EACA;EACA;;KAGU;EACV,WAAW;EACX;;KAGU,aACV,mBAAmB,2BAA2B,6BAC3C,MAAM,eAAe,aAAa,QAAQ;uBAQzB,eACpB,mBAAmB,2BAA2B;WAenC,YAAY;WACZ,QAAQ;UAdX;mBACS;mBACA;mBAGA;mBACA;mBACA;EAMN,YAAA,YAAY,YACZ,QAAQ;qBAOA,QAAQ,QAAQ;;MAG/B;EAIJ,QAAQ,WAAW,OAAO;EAK1B,SAAS,WAAW,QAAQ,QAAQ;EAKpC,WAAW,WAAW,QAAQ;EAK9B,gBAAgB,WAAW;EAKrB,SAAK;EAQL,QAAI;EAOV,SAAS,QAAQ;EAIjB,QAAQ,QAAQ;EAWV,KACJ,QAAQ,eACR,UAAS,eACR,QAAQ;EA6CL,OAAO,QAAQ,SAAM;UAyBb;UAkDN;UAWA;UAIA;UAOA;UAgBA;UAMM;UAsBN;;iBASM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,aAAa,eACpB,YAAY,eAAe,eAAe;;;cC5TjC,iCAAiC;WAEjC;WACA;EADA,YAAA,sBACA;;iBASS,sBACpB,qBACA,oBAAoB,aACpB,gBACA,gBAAa;iBAuCO,sBACpB,kBACA,wBACC,QAAQ"}
1
+ {"version":3,"file":"barrel.d.mts","names":[],"sources":["../src/dial.ts","../src/jsonRpcService.ts","../src/jsonRpcTransport.ts","../src/pairingService.ts","../src/pairingEndpoint.ts","../src/barrel.ts"],"mappings":";;;;;iBAGsB,mBACpB,MAAM,QACN,QAAQ,QACR,SAAS,cAAW,oCAAA;;;KCCV;EACV,WAAW;;KAGD;EACV;EACA;;KAGU;EACV,YAAY;EACZ,QAAQ;EACR,SAAS,IAAI;;KAGH,sBACV,mBAAmB,2BAA2B,6BAC3C,MAAM,eAAe,aAAa,SAAS;uBAE1B,eACpB,mBAAmB,2BAA2B;WAMnC,YAAY;WACZ,QAAQ;mBALF;WACR;EAGE,YAAA,YAAY,YACZ,QAAQ;qBAaA,cAAc,SAAS;EAE1C,QAAQ,WAAW,OAAO;EAKpB,SAAK;EAQL,QAAI;UAII;UA8CN;;iBA4CM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,sBAAsB,eAC7B,YAAY,eAAe,eAAe;;;KCzJlC;EACV;EACA;EACA;EACA,SAAS;EACT,cAAc,UAAU,IAAI;;cAGjB,kCAAkC,IAAI;mBAK9B;WACR,QAAQ;mBACA;mBANF;mBACA;EAGE,YAAA,MAAM,QACd,QAAQ,QACA,QAAQ;EAoBrB,QAAQ,SAAS,IAAI,iBAAiB,QAAQ,IAAI;UAmC1C;UAsBN;;;;KClFE;EACV,iBAAiB,KAAK;EACtB,mBAAmB;EACnB,WAAW;;KAGD;;EAEV;EACA;EACA;;KAGU;EACV,WAAW;EACX;;KAGU,aACV,mBAAmB,2BAA2B,6BAC3C,MAAM,eAAe,aAAa,QAAQ;uBASzB,eACpB,mBAAmB,2BAA2B;WAenC,YAAY;WACZ,QAAQ;UAdX;mBACS;mBACA;mBAGA;mBACA;mBACA;EAMN,YAAA,YAAY,YACZ,QAAQ;qBAOA,QAAQ,QAAQ;;MAG/B;EAIJ,QAAQ,WAAW,OAAO;EAK1B,SAAS,WAAW,QAAQ,QAAQ;EAKpC,WAAW,WAAW,QAAQ;EAK9B,gBAAgB,WAAW;EAKrB,SAAK;EAQL,QAAI;EAOV,SAAS,QAAQ;EAIjB,QAAQ,QAAQ;EAWV,KACJ,QAAQ,eACR,UAAS,eACR,QAAQ;EAyCL,OAAO,QAAQ,SAAM;UAyBb;UAwDN;UAWA;UAIA;UAIM;UAYN;UAOA;UAgBA;UAkBM;UAsBN;;iBASM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,aAAa,eACpB,YAAY,eAAe,eAAe;;;cChWjC,iCAAiC;WAEjC;WACA;EADA,YAAA,sBACA;;iBASS,sBACpB,qBACA,oBAAoB,aACpB,gBACA,gBAAa;iBAuCO,sBACpB,kBACA,wBACC,QAAQ"}
package/dist/barrel.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{t as e}from"./rolldown-runtime-DK3Fl9T5.mjs";import{JsonRpcError as t,ccc as n}from"@ckb-ccc/core";import{lpStream as r}from"@libp2p/utils";import{multiaddr as i}from"@multiformats/multiaddr";import*as a from"it-length-prefixed";var o=class{constructor(e,t){this.components=e,this.config=t,this.errorListeners=new Set;let n=t.maxMessageLength??1048576;if(!Number.isSafeInteger(n)||n<=0)throw Error(`Maximum JSON-RPC message length must be a positive integer`);this.maxMessageLength=n}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}async start(){await this.components.registrar.handle(this.config.protocol,this.handleProtocol.bind(this),{runOnLimitedConnection:!0})}async stop(){await this.components.registrar.unhandle(this.config.protocol)}async handleProtocol(e,t){let i=!1;try{let a=r(e,{maxDataLength:this.maxMessageLength}),o=c(n.bytesTo((await a.read()).subarray(),`utf8`)),l;try{l={jsonrpc:`2.0`,id:o.id,result:await this.handleRequest({peerId:t.remotePeer,payload:o})}}catch(e){i=u(e);let t=s(e);l={jsonrpc:`2.0`,id:o.id,error:{code:t.code,message:t.message,data:t.data}}}await a.write(n.bytesFrom(JSON.stringify(l),`utf8`)),await e.close()}catch(t){if(i)return;this.handleError(t,e)}}handleError(e,t){let n=l(e);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function s(e){return e instanceof t?e:new t({code:-32603,message:l(e).message})}function c(e){let t=JSON.parse(e);if(t?.jsonrpc!==`2.0`||typeof t.id!=`number`&&typeof t.id!=`string`||typeof t.method!=`string`)throw Error(`Invalid JSON-RPC request`);return t}function l(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}function u(e){return e instanceof Error&&(e.name===`AbortError`||e.name===`TimeoutError`)}function d(e,t){return n=>new class extends o{handleRequest(e){return t.call(this,e)}}(n,e)}var f=class{constructor(e,t,n){this.node=e,this.peerId=t,this.config=n;let r=n.maxMessageLength??1048576;if(!Number.isSafeInteger(r)||r<=0)throw Error(`Maximum JSON-RPC message length must be a positive integer`);this.maxMessageLength=r;let i=n.timeout??12e4;if(!Number.isSafeInteger(i)||i<=0)throw Error(`JSON-RPC request timeout must be a positive integer`);this.timeout=i}async request(e){let t=AbortSignal.timeout(this.timeout),i=this.config.signal?n.abortSignalAny([this.config.signal,t]):t,a,o;try{a=await this.node.dialProtocol(this.peerId,this.config.protocol,{runOnLimitedConnection:!0,signal:i});let t=r(a,{maxDataLength:this.maxMessageLength});await t.write(n.bytesFrom(JSON.stringify(e),`utf8`),{signal:i}),await a.close({signal:i}),o=JSON.parse(n.bytesTo((await t.read({signal:i})).subarray(),`utf8`)),await a.closeRead({signal:i}),i.throwIfAborted()}catch(e){let t=p(e);throw a?.abort(t),t}return this.config.onResponse?.(o),o}};function p(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}const m=16384,h=[`addresses`,`role`,`secret`];var g=class extends Error{constructor(e,t){super(`Expected a ${e} pairing endpoint, received ${t?`role "${t}"`:`an endpoint without a role`}`),this.expectedRole=e,this.actualRole=t,this.name=`PairingEndpointRoleError`}};async function _(e,t,r,i){if(t.length===0)throw Error(`Pairing endpoint requires at least one address`);if(t.length>16)throw Error(`Pairing endpoint supports at most 16 addresses`);let a=b(t);if(a.byteLength>m)throw Error(`Pairing endpoint address data is too large`);let o=await S(a,new CompressionStream(`deflate`)),s=n.bytesTo(o,`base64url`),c=new URL(e),{params:l,route:u}=y(c);return h.forEach(e=>{l.delete(e)}),i&&l.set(`role`,i),l.set(`addresses`,s),l.set(`secret`,r),c.hash=`${u}?${l.toString()}`,c.toString()}async function v(e,t){let{params:n}=y(new URL(e.trim()));if(t){let e=n.get(`role`)?.trim()||void 0;if(e!==t)throw new g(t,e)}let r=n.get(`addresses`)?.trim(),i=n.get(`secret`)?.trim();if(!r||!i)throw Error(`Pairing endpoint is incomplete`);return{addresses:await x(r),secret:i}}function y(e){let t=e.hash.slice(1),n=t.indexOf(`?`);return n===-1?{params:new URLSearchParams,route:t}:{params:new URLSearchParams(t.slice(n+1)),route:t.slice(0,n)}}function b(e){return n.bytesConcat(...a.encode(e.map(e=>e.bytes)))}async function x(e){try{let t=await S(n.bytesFrom(e,`base64url`),new DecompressionStream(`deflate`),m),r=[];for(let e of a.decode([t],{maxDataLength:m})){if(r.length>=16)throw Error(`Too many addresses`);r.push(i(e.subarray()))}if(r.length===0)throw Error(`Missing addresses`);return r}catch(e){throw Error(`Pairing endpoint contains invalid compressed addresses`,{cause:e})}}async function S(e,t,n){let r=n===void 0?t.readable:t.readable.pipeThrough(C(n)),i=new Response(r).arrayBuffer().then(e=>({buffer:e,ok:!0}),e=>({error:e,ok:!1})),a=t.writable.getWriter();try{await a.write(Uint8Array.from(e)),await a.close()}catch(e){let t=await i;throw t.ok?e:t.error}let o=await i;if(!o.ok)throw o.error;return new Uint8Array(o.buffer)}function C(e){let t=0;return new TransformStream({transform(n,r){if(n.byteLength>e-t)throw Error(`Decompressed address data is too large`);t+=n.byteLength,r.enqueue(n)}})}const w=1024;var T=class{constructor(e,t){if(this.components=e,this.config=t,this.currentSecret=j(),this.errorListeners=new Set,this.pairedListeners=new Set,this.unpairedListeners=new Set,this.secretChangedListeners=new Set,this.pairingTimeouts=new Map,t.pairedPeerTimeoutMs<=0)throw Error(`Paired peer timeout must be greater than zero`)}get secret(){return this.currentSecret}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}onPaired(e){return this.pairedListeners.add(e),()=>this.pairedListeners.delete(e)}onUnpaired(e){return this.unpairedListeners.add(e),()=>this.unpairedListeners.delete(e)}onSecretChanged(e){return this.secretChangedListeners.add(e),()=>this.secretChangedListeners.delete(e)}async start(){await this.components.registrar.handle(this.config.protocol,this.handleProtocol.bind(this),{runOnLimitedConnection:!0})}async stop(){await this.components.registrar.unhandle(this.config.protocol),this.pairingTimeouts.forEach(e=>clearTimeout(e)),this.pairingTimeouts.clear()}isPaired(e){return this.pairingTimeouts.has(e.toString())}refresh(e){let t=e.toString(),n=this.pairingTimeouts.get(t);n!==void 0&&(clearTimeout(n),this.pairingTimeouts.set(t,this.createPeerTimeout(e)))}async pair(e,t={}){let n,r;try{let i=await this.openStream(e.addresses,t);if(n=i.stream,!i.connection)throw Error(`Could not determine remote peer`);this.addPairedPeer(i.connection.remotePeer)&&(r=i.connection.remotePeer);let a=await D(n,{type:`pair`,secret:e.secret,name:this.config.name},t);if(t.signal?.throwIfAborted(),n=void 0,!a.ok)throw Error(a.error);return r&&(this.rotateSecret(),this.notifyPairedPeer(r,a.name)),i.connection.remotePeer}catch(e){throw r&&this.removePairedPeer(r,!1),this.handleError(e,n)}}async unpair(e){if(!this.removePairedPeer(e))return;let t={signal:AbortSignal.timeout(1e4)},n;try{let r=await this.openStream(e,t);n=r.stream;let i=await D(r.stream,{type:`unpair`},t);if(n=void 0,!i.ok)throw Error(i.error)}catch(e){this.handleError(e,n)}}async handleProtocol(e,t){let n;try{let i=r(e,{maxDataLength:w}),a=k((await i.read()).subarray());if(a.type===`unpair`){this.removePairedPeer(t.remotePeer),await O(i,e,{ok:!0});return}if(a.secret!==this.secret||!this.canPair(t.remotePeer)){await O(i,e,{ok:!1,error:`Pairing rejected`});return}this.rotateSecret(),this.addPairedPeer(t.remotePeer)&&(n=t.remotePeer),await O(i,e,{ok:!0,name:this.config.name}),n&&this.notifyPairedPeer(n,a.name)}catch(t){n&&this.removePairedPeer(n,!1),this.handleError(t,e)}}addPairedPeer(e){let t=e.toString(),n=this.pairingTimeouts.get(t);return n!==void 0&&clearTimeout(n),this.pairingTimeouts.set(t,this.createPeerTimeout(e)),n===void 0}notifyPairedPeer(e,t){this.pairedListeners.forEach(n=>n(e,t))}rotateSecret(){this.currentSecret=j(),this.secretChangedListeners.forEach(e=>e(this.currentSecret))}removePairedPeer(e,t=!0){let n=e.toString(),r=this.pairingTimeouts.get(n);return r!==void 0&&(clearTimeout(r),this.pairingTimeouts.delete(n),t&&this.unpairedListeners.forEach(t=>t(e)),!0)}createPeerTimeout(e){return setTimeout(()=>{this.removePairedPeer(e)},this.config.pairedPeerTimeoutMs)}async openStream(e,t){let n;return{stream:await this.components.connectionManager.openStream(e,this.config.protocol,{runOnLimitedConnection:!0,signal:t.signal,onProgress(e){e.type===`connection:opened-stream`&&(n=e.detail.connection)}}),connection:n}}handleError(e,t){let n=M(e,`Pairing operation failed`);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function E(e,t){return n=>new class extends T{canPair(e){return t.call(this,e)}}(n,e)}async function D(e,t,i){let a=r(e,{maxDataLength:w});await a.write(n.bytesFrom(JSON.stringify(t),`utf8`),{signal:i.signal}),await e.close(i);let o=A((await a.read(i)).subarray());return await e.closeRead(i),o}async function O(e,t,r){await e.write(n.bytesFrom(JSON.stringify(r),`utf8`)),await t.close()}function k(e){let t=JSON.parse(n.bytesTo(e,`utf8`));if(t.type===`pair`&&typeof t.secret==`string`&&t.secret)return{type:`pair`,secret:t.secret,name:typeof t.name==`string`?t.name:void 0};if(t.type===`unpair`)return{type:`unpair`};throw Error(`Invalid pairing request`)}function A(e){let t=JSON.parse(n.bytesTo(e,`utf8`));if(t.ok===!0)return{ok:!0,name:typeof t.name==`string`?t.name:void 0};if(t.ok===!1&&typeof t.error==`string`)return{ok:!1,error:t.error};throw Error(`Invalid pairing response`)}function j(){return n.bytesTo(crypto.getRandomValues(new Uint8Array(16)),`base64url`)}function M(e,t){return e instanceof Error?e:Error(t)}var N=e({JsonRpcService:()=>o,JsonRpcTransportLibp2p:()=>f,PairingEndpointRoleError:()=>g,PairingService:()=>T,decodePairingEndpoint:()=>v,encodePairingEndpoint:()=>_,jsonRpcService:()=>d,pairingService:()=>E});export{o as JsonRpcService,f as JsonRpcTransportLibp2p,g as PairingEndpointRoleError,T as PairingService,v as decodePairingEndpoint,_ as encodePairingEndpoint,d as jsonRpcService,E as pairingService,N as t};
1
+ import{t as e}from"./rolldown-runtime-DK3Fl9T5.mjs";import{JsonRpcError as t,ccc as n}from"@ckb-ccc/core";import{lpStream as r}from"@libp2p/utils";import{CODE_P2P as i,multiaddr as a}from"@multiformats/multiaddr";import*as o from"it-length-prefixed";async function s(e,t,n){let r=(await e.peerStore.get(t)).addresses.flatMap(({multiaddr:e})=>{let n=c(e,t);return n?[n]:[]});if(r.length!==0)return n?e.dial(r,{signal:n}):e.dial(r)}function c(e,t){let n=e.getComponents().at(-1);return n?.name===`p2p`?n.value===t.toString()?e:void 0:e.encapsulate(`/p2p/${t.toString()}`)}var l=class{constructor(e,t){this.components=e,this.config=t,this.errorListeners=new Set;let n=t.maxMessageLength??1048576;if(!Number.isSafeInteger(n)||n<=0)throw Error(`Maximum JSON-RPC message length must be a positive integer`);this.maxMessageLength=n}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}async start(){await this.components.registrar.handle(this.config.protocol,this.handleProtocol.bind(this),{runOnLimitedConnection:!0})}async stop(){await this.components.registrar.unhandle(this.config.protocol)}async handleProtocol(e,t){let i=!1;try{let a=r(e,{maxDataLength:this.maxMessageLength}),o=d(n.bytesTo((await a.read()).subarray(),`utf8`)),s;try{s={jsonrpc:`2.0`,id:o.id,result:await this.handleRequest({connection:t,peerId:t.remotePeer,payload:o})}}catch(e){i=p(e);let t=u(e);s={jsonrpc:`2.0`,id:o.id,error:{code:t.code,message:t.message,data:t.data}}}await a.write(n.bytesFrom(JSON.stringify(s),`utf8`)),await e.close()}catch(t){if(i)return;this.handleError(t,e)}}handleError(e,t){let n=f(e);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function u(e){return e instanceof t?e:new t({code:-32603,message:f(e).message})}function d(e){let t=JSON.parse(e);if(t?.jsonrpc!==`2.0`||typeof t.id!=`number`&&typeof t.id!=`string`||typeof t.method!=`string`)throw Error(`Invalid JSON-RPC request`);return t}function f(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}function p(e){return e instanceof Error&&(e.name===`AbortError`||e.name===`TimeoutError`)}function m(e,t){return n=>new class extends l{handleRequest(e){return t.call(this,e)}}(n,e)}var h=class{constructor(e,t,n){this.node=e,this.peerId=t,this.config=n;let r=n.maxMessageLength??1048576;if(!Number.isSafeInteger(r)||r<=0)throw Error(`Maximum JSON-RPC message length must be a positive integer`);this.maxMessageLength=r;let i=n.timeout??12e4;if(!Number.isSafeInteger(i)||i<=0)throw Error(`JSON-RPC request timeout must be a positive integer`);this.timeout=i}async request(e){let t=AbortSignal.timeout(this.timeout),i=this.config.signal?n.abortSignalAny([this.config.signal,t]):t,a,o;try{a=await this.openStream(i);let t=r(a,{maxDataLength:this.maxMessageLength});await t.write(n.bytesFrom(JSON.stringify(e),`utf8`),{signal:i}),await a.close({signal:i}),o=JSON.parse(n.bytesTo((await t.read({signal:i})).subarray(),`utf8`)),await a.closeRead({signal:i}),i.throwIfAborted()}catch(e){let t=_(e);throw a?.abort(t),t}return this.config.onResponse?.(o),o}async openStream(e){s(this.node,this.peerId).catch(()=>{});for(let t of this.connections())try{return await t.newStream(this.config.protocol,{runOnLimitedConnection:!0,signal:e})}catch{e.throwIfAborted()}return this.node.dialProtocol(this.peerId,this.config.protocol,{runOnLimitedConnection:!0,signal:e})}connections(){return this.node.getConnections(this.peerId).filter(({status:e})=>e===`open`).sort(g)}};function g(e,t){return e.direct===t.direct?e.rtt===void 0?t.rtt===void 0?0:1:t.rtt===void 0?-1:e.rtt-t.rtt:e.direct?-1:1}function _(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}const v=16384,y=[`addresses`,`role`,`secret`];var b=class extends Error{constructor(e,t){super(`Expected a ${e} pairing endpoint, received ${t?`role "${t}"`:`an endpoint without a role`}`),this.expectedRole=e,this.actualRole=t,this.name=`PairingEndpointRoleError`}};async function x(e,t,r,i){if(t.length===0)throw Error(`Pairing endpoint requires at least one address`);if(t.length>16)throw Error(`Pairing endpoint supports at most 16 addresses`);let a=w(t);if(a.byteLength>v)throw Error(`Pairing endpoint address data is too large`);let o=await E(a,new CompressionStream(`deflate`)),s=n.bytesTo(o,`base64url`),c=new URL(e),{params:l,route:u}=C(c);return y.forEach(e=>{l.delete(e)}),i&&l.set(`role`,i),l.set(`addresses`,s),l.set(`secret`,r),c.hash=`${u}?${l.toString()}`,c.toString()}async function S(e,t){let{params:n}=C(new URL(e.trim()));if(t){let e=n.get(`role`)?.trim()||void 0;if(e!==t)throw new b(t,e)}let r=n.get(`addresses`)?.trim(),i=n.get(`secret`)?.trim();if(!r||!i)throw Error(`Pairing endpoint is incomplete`);return{addresses:await T(r),secret:i}}function C(e){let t=e.hash.slice(1),n=t.indexOf(`?`);return n===-1?{params:new URLSearchParams,route:t}:{params:new URLSearchParams(t.slice(n+1)),route:t.slice(0,n)}}function w(e){return n.bytesConcat(...o.encode(e.map(e=>e.bytes)))}async function T(e){try{let t=await E(n.bytesFrom(e,`base64url`),new DecompressionStream(`deflate`),v),r=[];for(let e of o.decode([t],{maxDataLength:v})){if(r.length>=16)throw Error(`Too many addresses`);r.push(a(e.subarray()))}if(r.length===0)throw Error(`Missing addresses`);return r}catch(e){throw Error(`Pairing endpoint contains invalid compressed addresses`,{cause:e})}}async function E(e,t,n){let r=n===void 0?t.readable:t.readable.pipeThrough(D(n)),i=new Response(r).arrayBuffer().then(e=>({buffer:e,ok:!0}),e=>({error:e,ok:!1})),a=t.writable.getWriter();try{await a.write(Uint8Array.from(e)),await a.close()}catch(e){let t=await i;throw t.ok?e:t.error}let o=await i;if(!o.ok)throw o.error;return new Uint8Array(o.buffer)}function D(e){let t=0;return new TransformStream({transform(n,r){if(n.byteLength>e-t)throw Error(`Decompressed address data is too large`);t+=n.byteLength,r.enqueue(n)}})}const O=1024;var k=class{constructor(e,t){if(this.components=e,this.config=t,this.currentSecret=z(),this.errorListeners=new Set,this.pairedListeners=new Set,this.unpairedListeners=new Set,this.secretChangedListeners=new Set,this.pairingTimeouts=new Map,t.pairedPeerTimeoutMs<=0)throw Error(`Paired peer timeout must be greater than zero`)}get secret(){return this.currentSecret}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}onPaired(e){return this.pairedListeners.add(e),()=>this.pairedListeners.delete(e)}onUnpaired(e){return this.unpairedListeners.add(e),()=>this.unpairedListeners.delete(e)}onSecretChanged(e){return this.secretChangedListeners.add(e),()=>this.secretChangedListeners.delete(e)}async start(){await this.components.registrar.handle(this.config.protocol,this.handleProtocol.bind(this),{runOnLimitedConnection:!0})}async stop(){await this.components.registrar.unhandle(this.config.protocol),this.pairingTimeouts.forEach(e=>clearTimeout(e)),this.pairingTimeouts.clear()}isPaired(e){return this.pairingTimeouts.has(e.toString())}refresh(e){let t=e.toString(),n=this.pairingTimeouts.get(t);n!==void 0&&(clearTimeout(n),this.pairingTimeouts.set(t,this.createPeerTimeout(e)))}async pair(e,t={}){let n,r;try{let i=await this.openStream(e.addresses,t);if(n=i.stream,!i.connection)throw Error(`Could not determine remote peer`);this.addPairedPeer(i.connection.remotePeer)&&(r=i.connection.remotePeer);let a=await j(n,M(e.secret,this.config.name,this.addresses()),t);if(t.signal?.throwIfAborted(),n=void 0,!a.ok)throw Error(a.error);return r&&(this.rotateSecret(),this.notifyPairedPeer(r,a.name)),i.connection.remotePeer}catch(e){throw r&&this.removePairedPeer(r,!1),this.handleError(e,n)}}async unpair(e){if(!this.removePairedPeer(e))return;let t={signal:AbortSignal.timeout(1e4)},n;try{let r=await this.openStream(e,t);n=r.stream;let i=await j(r.stream,{type:`unpair`},t);if(n=void 0,!i.ok)throw Error(i.error)}catch(e){this.handleError(e,n)}}async handleProtocol(e,t){let n;try{let i=r(e,{maxDataLength:O}),a=F((await i.read()).subarray());if(a.type===`unpair`){this.removePairedPeer(t.remotePeer),await P(i,e,{ok:!0});return}if(a.secret!==this.secret||!this.canPair(t.remotePeer)){await P(i,e,{ok:!1,error:`Pairing rejected`});return}let o=L(a.addresses,t.remotePeer);this.rotateSecret(),this.addPairedPeer(t.remotePeer)&&(n=t.remotePeer),await P(i,e,{ok:!0,name:this.config.name}),n&&(this.notifyPairedPeer(n,a.name),this.openFallbackConnection(o))}catch(t){n&&this.removePairedPeer(n,!1),this.handleError(t,e)}}addPairedPeer(e){let t=e.toString(),n=this.pairingTimeouts.get(t);return n!==void 0&&clearTimeout(n),this.pairingTimeouts.set(t,this.createPeerTimeout(e)),n===void 0}notifyPairedPeer(e,t){this.pairedListeners.forEach(n=>n(e,t))}addresses(){return this.components.addressManager?.getAddresses()??[]}async openFallbackConnection(e){if(e.length!==0)try{await this.components.connectionManager.openConnection(e)}catch{}}rotateSecret(){this.currentSecret=z(),this.secretChangedListeners.forEach(e=>e(this.currentSecret))}removePairedPeer(e,t=!0){let n=e.toString(),r=this.pairingTimeouts.get(n);return r!==void 0&&(clearTimeout(r),this.pairingTimeouts.delete(n),t&&this.unpairedListeners.forEach(t=>t(e)),!0)}createPeerTimeout(e){return setTimeout(()=>{if(this.components.connectionManager.getConnections(e).some(({status:e})=>e===`open`)){this.pairingTimeouts.set(e.toString(),this.createPeerTimeout(e));return}this.removePairedPeer(e)},this.config.pairedPeerTimeoutMs)}async openStream(e,t){let n;return{stream:await this.components.connectionManager.openStream(e,this.config.protocol,{runOnLimitedConnection:!0,signal:t.signal,onProgress(e){e.type===`connection:opened-stream`&&(n=e.detail.connection)}}),connection:n}}handleError(e,t){let n=B(e,`Pairing operation failed`);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function A(e,t){return n=>new class extends k{canPair(e){return t.call(this,e)}}(n,e)}async function j(e,t,i){let a=r(e,{maxDataLength:O});await a.write(n.bytesFrom(JSON.stringify(t),`utf8`),{signal:i.signal}),await e.close(i);let o=R((await a.read(i)).subarray());return await e.closeRead(i),o}function M(e,t,n){let r=n.map(e=>e.toString()),i={type:`pair`,secret:e,name:t,addresses:r};for(;r.length>0&&!N(i);)r.pop();if(N(i)||delete i.addresses,N(i)||delete i.name,!N(i))throw Error(`Pairing request is too large`);return i}function N(e){return n.bytesFrom(JSON.stringify(e),`utf8`).byteLength<=O}async function P(e,t,r){await e.write(n.bytesFrom(JSON.stringify(r),`utf8`)),await t.close()}function F(e){let t=JSON.parse(n.bytesTo(e,`utf8`));if(t.type===`pair`&&typeof t.secret==`string`&&t.secret)return{type:`pair`,secret:t.secret,name:typeof t.name==`string`?t.name:void 0,addresses:I(t.addresses)};if(t.type===`unpair`)return{type:`unpair`};throw Error(`Invalid pairing request`)}function I(e){if(Array.isArray(e))return e.filter(e=>typeof e==`string`&&e.length>0)}function L(e,t){return(e??[]).flatMap(e=>{try{let n=a(e);return n.getComponents().filter(({code:e})=>e===i).at(-1)?.value===t.toString()?[n]:[]}catch{return[]}})}function R(e){let t=JSON.parse(n.bytesTo(e,`utf8`));if(t.ok===!0)return{ok:!0,name:typeof t.name==`string`?t.name:void 0};if(t.ok===!1&&typeof t.error==`string`)return{ok:!1,error:t.error};throw Error(`Invalid pairing response`)}function z(){return n.bytesTo(crypto.getRandomValues(new Uint8Array(16)),`base64url`)}function B(e,t){return e instanceof Error?e:Error(t)}var V=e({JsonRpcService:()=>l,JsonRpcTransportLibp2p:()=>h,PairingEndpointRoleError:()=>b,PairingService:()=>k,decodePairingEndpoint:()=>S,dialKnownAddresses:()=>s,encodePairingEndpoint:()=>x,jsonRpcService:()=>m,pairingService:()=>A});export{l as JsonRpcService,h as JsonRpcTransportLibp2p,b as PairingEndpointRoleError,k as PairingService,S as decodePairingEndpoint,s as dialKnownAddresses,x as encodePairingEndpoint,m as jsonRpcService,A as pairingService,V as t};
2
2
  //# sourceMappingURL=barrel.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"barrel.mjs","names":["DEFAULT_MAX_MESSAGE_LENGTH","asJsonRpcError"],"sources":["../src/jsonRpcService.ts","../src/jsonRpcTransport.ts","../src/pairingEndpoint.ts","../src/pairingService.ts","../src/barrel.ts"],"sourcesContent":["import { ccc, JsonRpcError } from \"@ckb-ccc/core\";\nimport type { Connection, PeerId, Stream } from \"@libp2p/interface\";\nimport type { Registrar } from \"@libp2p/interface-internal\";\nimport { lpStream } from \"@libp2p/utils\";\n\nconst DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;\n\nexport type JsonRpcServiceComponents = {\n registrar: Registrar;\n};\n\nexport type JsonRpcServiceConfig = {\n protocol: string;\n maxMessageLength?: number;\n};\n\nexport type JsonRpcRequest = {\n peerId: PeerId;\n payload: ccc.JsonRpcPayload;\n};\n\nexport type JsonRpcRequestHandler<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n> = (this: JsonRpcService<Components>, request: JsonRpcRequest) => unknown;\n\nexport abstract class JsonRpcService<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n> {\n private readonly errorListeners = new Set<(error: Error) => void>();\n readonly maxMessageLength: number;\n\n constructor(\n readonly components: Components,\n readonly config: JsonRpcServiceConfig,\n ) {\n const maxMessageLength =\n config.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH;\n if (!Number.isSafeInteger(maxMessageLength) || maxMessageLength <= 0) {\n throw new Error(\n \"Maximum JSON-RPC message length must be a positive integer\",\n );\n }\n\n this.maxMessageLength = maxMessageLength;\n }\n\n protected abstract handleRequest(request: JsonRpcRequest): unknown;\n\n onError(listener: (error: Error) => void) {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n async start() {\n await this.components.registrar.handle(\n this.config.protocol,\n this.handleProtocol.bind(this),\n { runOnLimitedConnection: true },\n );\n }\n\n async stop() {\n await this.components.registrar.unhandle(this.config.protocol);\n }\n\n private async handleProtocol(stream: Stream, connection: Connection) {\n let requestAborted = false;\n\n try {\n const rpcStream = lpStream(stream, {\n maxDataLength: this.maxMessageLength,\n });\n const payload = parseJsonRpcRequest(\n ccc.bytesTo((await rpcStream.read()).subarray(), \"utf8\"),\n );\n\n let response: ccc.JsonRpcResponse;\n try {\n response = {\n jsonrpc: \"2.0\",\n id: payload.id,\n result: await this.handleRequest({\n peerId: connection.remotePeer,\n payload,\n }),\n };\n } catch (cause) {\n requestAborted = isAbortError(cause);\n const error = toJsonRpcError(cause);\n response = {\n jsonrpc: \"2.0\",\n id: payload.id,\n error: {\n code: error.code,\n message: error.message,\n data: error.data,\n },\n };\n }\n\n await rpcStream.write(ccc.bytesFrom(JSON.stringify(response), \"utf8\"));\n await stream.close();\n } catch (cause) {\n if (requestAborted) {\n return;\n }\n this.handleError(cause, stream);\n }\n }\n\n private handleError(cause: unknown, stream?: Stream) {\n const error = asJsonRpcError(cause);\n\n stream?.abort(error);\n this.errorListeners.forEach((listener) => listener(error));\n return error;\n }\n}\n\nfunction toJsonRpcError(cause: unknown) {\n if (cause instanceof JsonRpcError) {\n return cause;\n }\n\n return new JsonRpcError({\n code: -32603,\n message: asJsonRpcError(cause).message,\n });\n}\n\nfunction parseJsonRpcRequest(data: string) {\n const payload = JSON.parse(data) as Partial<ccc.JsonRpcPayload>;\n if (\n payload?.jsonrpc !== \"2.0\" ||\n (typeof payload.id !== \"number\" && typeof payload.id !== \"string\") ||\n typeof payload.method !== \"string\"\n ) {\n throw new Error(\"Invalid JSON-RPC request\");\n }\n\n return payload as ccc.JsonRpcPayload;\n}\n\nfunction asJsonRpcError(cause: unknown) {\n return cause instanceof Error ? cause : new Error(\"JSON-RPC request failed\");\n}\n\nfunction isAbortError(cause: unknown) {\n return (\n cause instanceof Error &&\n (cause.name === \"AbortError\" || cause.name === \"TimeoutError\")\n );\n}\n\nexport function jsonRpcService<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n>(\n config: JsonRpcServiceConfig,\n handler: JsonRpcRequestHandler<Components>,\n): (components: Components) => JsonRpcService<Components> {\n return (components: Components) =>\n new (class extends JsonRpcService<Components> {\n protected handleRequest(request: JsonRpcRequest) {\n return handler.call(this, request);\n }\n })(components, config);\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport type { Libp2p, PeerId, Stream } from \"@libp2p/interface\";\nimport { lpStream } from \"@libp2p/utils\";\n\nconst DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;\nconst DEFAULT_TIMEOUT = 120_000;\n\nexport type JsonRpcTransportLibp2pConfig = {\n protocol: string;\n maxMessageLength?: number;\n timeout?: number;\n signal?: AbortSignal;\n onResponse?: (response: ccc.JsonRpcResponse) => void;\n};\n\nexport class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {\n private readonly maxMessageLength: number;\n private readonly timeout: number;\n\n constructor(\n private readonly node: Libp2p,\n readonly peerId: PeerId,\n private readonly config: JsonRpcTransportLibp2pConfig,\n ) {\n const maxMessageLength =\n config.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH;\n if (!Number.isSafeInteger(maxMessageLength) || maxMessageLength <= 0) {\n throw new Error(\n \"Maximum JSON-RPC message length must be a positive integer\",\n );\n }\n\n this.maxMessageLength = maxMessageLength;\n\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n if (!Number.isSafeInteger(timeout) || timeout <= 0) {\n throw new Error(\"JSON-RPC request timeout must be a positive integer\");\n }\n\n this.timeout = timeout;\n }\n\n async request(payload: ccc.JsonRpcPayload): Promise<ccc.JsonRpcResponse> {\n const timeoutSignal = AbortSignal.timeout(this.timeout);\n const signal = this.config.signal\n ? ccc.abortSignalAny([this.config.signal, timeoutSignal])\n : timeoutSignal;\n\n let stream: Stream | undefined;\n let response: ccc.JsonRpcResponse;\n try {\n // dialProtocol handles the signal, so no separate entry check is needed.\n stream = await this.node.dialProtocol(this.peerId, this.config.protocol, {\n runOnLimitedConnection: true,\n signal,\n });\n\n const rpcStream = lpStream(stream, {\n maxDataLength: this.maxMessageLength,\n });\n\n await rpcStream.write(ccc.bytesFrom(JSON.stringify(payload), \"utf8\"), {\n signal,\n });\n await stream.close({ signal });\n\n response = JSON.parse(\n ccc.bytesTo((await rpcStream.read({ signal })).subarray(), \"utf8\"),\n ) as ccc.JsonRpcResponse;\n await stream.closeRead({ signal });\n signal.throwIfAborted();\n } catch (cause) {\n const error = asJsonRpcError(cause);\n stream?.abort(error);\n throw error;\n }\n\n this.config.onResponse?.(response);\n return response;\n }\n}\n\nfunction asJsonRpcError(cause: unknown) {\n return cause instanceof Error ? cause : new Error(\"JSON-RPC request failed\");\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport { multiaddr, type Multiaddr } from \"@multiformats/multiaddr\";\nimport * as lp from \"it-length-prefixed\";\nimport type { PairingTarget } from \"./pairingService.js\";\n\nconst MAX_DECOMPRESSED_ADDRESSES_LENGTH = 16 * 1024;\nconst MAX_ADDRESSES = 16;\nconst PAIRING_PARAMETER_NAMES = [\"addresses\", \"role\", \"secret\"] as const;\n\nexport class PairingEndpointRoleError extends Error {\n constructor(\n readonly expectedRole: string,\n readonly actualRole: string | undefined,\n ) {\n super(\n `Expected a ${expectedRole} pairing endpoint, received ${actualRole ? `role \"${actualRole}\"` : \"an endpoint without a role\"}`,\n );\n this.name = \"PairingEndpointRoleError\";\n }\n}\n\nexport async function encodePairingEndpoint(\n endpointUrl: string,\n addresses: readonly Multiaddr[],\n secret: string,\n role?: string,\n) {\n if (addresses.length === 0) {\n throw new Error(\"Pairing endpoint requires at least one address\");\n }\n if (addresses.length > MAX_ADDRESSES) {\n throw new Error(\n `Pairing endpoint supports at most ${MAX_ADDRESSES} addresses`,\n );\n }\n\n const addressBytes = encodeAddresses(addresses);\n if (addressBytes.byteLength > MAX_DECOMPRESSED_ADDRESSES_LENGTH) {\n throw new Error(\"Pairing endpoint address data is too large\");\n }\n\n const compressedAddressBytes = await transformBytes(\n addressBytes,\n new CompressionStream(\"deflate\"),\n );\n\n const encodedAddresses = ccc.bytesTo(compressedAddressBytes, \"base64url\");\n\n const url = new URL(endpointUrl);\n const { params, route } = fragmentParameters(url);\n\n PAIRING_PARAMETER_NAMES.forEach((name) => {\n params.delete(name);\n });\n if (role) {\n params.set(\"role\", role);\n }\n params.set(\"addresses\", encodedAddresses);\n params.set(\"secret\", secret);\n url.hash = `${route}?${params.toString()}`;\n\n return url.toString();\n}\n\nexport async function decodePairingEndpoint(\n endpoint: string,\n expectedRole?: string,\n): Promise<PairingTarget> {\n const url = new URL(endpoint.trim());\n const { params } = fragmentParameters(url);\n if (expectedRole) {\n const actualRole = params.get(\"role\")?.trim() || undefined;\n if (actualRole !== expectedRole) {\n throw new PairingEndpointRoleError(expectedRole, actualRole);\n }\n }\n const compressedAddresses = params.get(\"addresses\")?.trim();\n const secret = params.get(\"secret\")?.trim();\n\n if (!compressedAddresses || !secret) {\n throw new Error(\"Pairing endpoint is incomplete\");\n }\n\n const addresses = await decodeCompressedAddresses(compressedAddresses);\n\n return { addresses, secret };\n}\n\nfunction fragmentParameters(url: URL) {\n const fragment = url.hash.slice(1);\n const separator = fragment.indexOf(\"?\");\n if (separator === -1) {\n return { params: new URLSearchParams(), route: fragment };\n }\n\n return {\n params: new URLSearchParams(fragment.slice(separator + 1)),\n route: fragment.slice(0, separator),\n };\n}\n\nfunction encodeAddresses(addresses: readonly Multiaddr[]): Uint8Array {\n return ccc.bytesConcat(\n ...lp.encode(addresses.map((address) => address.bytes)),\n );\n}\n\nasync function decodeCompressedAddresses(value: string): Promise<Multiaddr[]> {\n try {\n const compressedBytes = ccc.bytesFrom(value, \"base64url\");\n const bytes = await transformBytes(\n compressedBytes,\n new DecompressionStream(\"deflate\"),\n MAX_DECOMPRESSED_ADDRESSES_LENGTH,\n );\n const addresses: Multiaddr[] = [];\n for (const address of lp.decode([bytes], {\n maxDataLength: MAX_DECOMPRESSED_ADDRESSES_LENGTH,\n })) {\n if (addresses.length >= MAX_ADDRESSES) {\n throw new Error(\"Too many addresses\");\n }\n addresses.push(multiaddr(address.subarray()));\n }\n\n if (addresses.length === 0) {\n throw new Error(\"Missing addresses\");\n }\n return addresses;\n } catch (cause) {\n throw new Error(\"Pairing endpoint contains invalid compressed addresses\", {\n cause,\n });\n }\n}\n\nasync function transformBytes(\n bytes: Uint8Array,\n transform: CompressionStream | DecompressionStream,\n maxOutputLength?: number,\n) {\n const readable =\n maxOutputLength === undefined\n ? transform.readable\n : transform.readable.pipeThrough(limitBytes(maxOutputLength));\n const output = new Response(readable).arrayBuffer().then(\n (buffer) => ({ buffer, ok: true }) as const,\n (error: unknown) => ({ error, ok: false }) as const,\n );\n const writer = transform.writable.getWriter();\n try {\n await writer.write(Uint8Array.from(bytes));\n await writer.close();\n } catch (cause) {\n const result = await output;\n if (!result.ok) {\n throw result.error;\n }\n throw cause;\n }\n\n const result = await output;\n if (!result.ok) {\n throw result.error;\n }\n return new Uint8Array(result.buffer);\n}\n\nfunction limitBytes(maxLength: number) {\n let length = 0;\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n if (chunk.byteLength > maxLength - length) {\n throw new Error(\"Decompressed address data is too large\");\n }\n length += chunk.byteLength;\n controller.enqueue(chunk);\n },\n });\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport type {\n AbortOptions,\n Connection,\n PeerId,\n Stream,\n} from \"@libp2p/interface\";\nimport type { ConnectionManager, Registrar } from \"@libp2p/interface-internal\";\nimport { lpStream } from \"@libp2p/utils\";\nimport type { Multiaddr } from \"@multiformats/multiaddr\";\n\nconst MAX_PAIRING_MESSAGE_LENGTH = 1024;\nconst UNPAIR_TIMEOUT_MS = 10_000;\n\nexport type PairingServiceComponents = {\n connectionManager: ConnectionManager;\n registrar: Registrar;\n};\n\nexport type PairingServiceConfig = {\n /** Optional self-reported label shared with the paired peer. */\n name?: string;\n protocol: string;\n pairedPeerTimeoutMs: number;\n};\n\nexport type PairingTarget = {\n addresses: Multiaddr[];\n secret: string;\n};\n\nexport type PairingGuard<\n Components extends PairingServiceComponents = PairingServiceComponents,\n> = (this: PairingService<Components>, peerId: PeerId) => boolean;\n\ntype PairingRequest =\n { type: \"pair\"; secret: string; name?: string } | { type: \"unpair\" };\n\ntype PairingResponse =\n { ok: true; name?: string } | { ok: false; error: string };\n\nexport abstract class PairingService<\n Components extends PairingServiceComponents = PairingServiceComponents,\n> {\n private currentSecret = createSecret();\n private readonly errorListeners = new Set<(error: Error) => void>();\n private readonly pairedListeners = new Set<\n (peerId: PeerId, name?: string) => void\n >();\n private readonly unpairedListeners = new Set<(peerId: PeerId) => void>();\n private readonly secretChangedListeners = new Set<(secret: string) => void>();\n private readonly pairingTimeouts = new Map<\n string,\n ReturnType<typeof setTimeout>\n >();\n\n constructor(\n readonly components: Components,\n readonly config: PairingServiceConfig,\n ) {\n if (config.pairedPeerTimeoutMs <= 0) {\n throw new Error(\"Paired peer timeout must be greater than zero\");\n }\n }\n\n protected abstract canPair(peerId: PeerId): boolean;\n\n /** The credential to include in the currently issued pairing endpoint. */\n get secret() {\n return this.currentSecret;\n }\n\n onError(listener: (error: Error) => void) {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n onPaired(listener: (peerId: PeerId, name?: string) => void) {\n this.pairedListeners.add(listener);\n return () => this.pairedListeners.delete(listener);\n }\n\n onUnpaired(listener: (peerId: PeerId) => void) {\n this.unpairedListeners.add(listener);\n return () => this.unpairedListeners.delete(listener);\n }\n\n onSecretChanged(listener: (secret: string) => void) {\n this.secretChangedListeners.add(listener);\n return () => this.secretChangedListeners.delete(listener);\n }\n\n async start() {\n await this.components.registrar.handle(\n this.config.protocol,\n this.handleProtocol.bind(this),\n { runOnLimitedConnection: true },\n );\n }\n\n async stop() {\n await this.components.registrar.unhandle(this.config.protocol);\n\n this.pairingTimeouts.forEach((timeout) => clearTimeout(timeout));\n this.pairingTimeouts.clear();\n }\n\n isPaired(peerId: PeerId) {\n return this.pairingTimeouts.has(peerId.toString());\n }\n\n refresh(peerId: PeerId) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout === undefined) {\n return;\n }\n\n clearTimeout(timeout);\n this.pairingTimeouts.set(key, this.createPeerTimeout(peerId));\n }\n\n async pair(\n target: PairingTarget,\n options: AbortOptions = {},\n ): Promise<PeerId> {\n let stream: Stream | undefined;\n let addedPeer: PeerId | undefined;\n\n try {\n const opened = await this.openStream(target.addresses, options);\n stream = opened.stream;\n\n if (!opened.connection) {\n throw new Error(\"Could not determine remote peer\");\n }\n\n if (this.addPairedPeer(opened.connection.remotePeer)) {\n addedPeer = opened.connection.remotePeer;\n }\n\n const response = await exchange(\n stream,\n {\n type: \"pair\",\n secret: target.secret,\n name: this.config.name,\n },\n options,\n );\n options.signal?.throwIfAborted();\n stream = undefined;\n\n if (!response.ok) {\n throw new Error(response.error);\n }\n\n if (addedPeer) {\n this.rotateSecret();\n this.notifyPairedPeer(addedPeer, response.name);\n }\n return opened.connection.remotePeer;\n } catch (cause) {\n if (addedPeer) {\n this.removePairedPeer(addedPeer, false);\n }\n throw this.handleError(cause, stream);\n }\n }\n\n async unpair(peerId: PeerId) {\n if (!this.removePairedPeer(peerId)) {\n return;\n }\n\n const options = { signal: AbortSignal.timeout(UNPAIR_TIMEOUT_MS) };\n\n let stream: Stream | undefined;\n try {\n const opened = await this.openStream(peerId, options);\n stream = opened.stream;\n const response = await exchange(\n opened.stream,\n { type: \"unpair\" },\n options,\n );\n stream = undefined;\n if (!response.ok) {\n throw new Error(response.error);\n }\n } catch (cause) {\n this.handleError(cause, stream);\n }\n }\n\n private async handleProtocol(stream: Stream, connection: Connection) {\n let addedPeer: PeerId | undefined;\n\n try {\n const pairingStream = lpStream(stream, {\n maxDataLength: MAX_PAIRING_MESSAGE_LENGTH,\n });\n const request = parsePairingRequest(\n (await pairingStream.read()).subarray(),\n );\n\n if (request.type === \"unpair\") {\n this.removePairedPeer(connection.remotePeer);\n await writeResponse(pairingStream, stream, { ok: true });\n return;\n }\n\n if (\n request.secret !== this.secret ||\n !this.canPair(connection.remotePeer)\n ) {\n await writeResponse(pairingStream, stream, {\n ok: false,\n error: \"Pairing rejected\",\n });\n return;\n }\n\n // Consume the credential before the next asynchronous boundary so two\n // concurrent requests cannot both pair with the same endpoint.\n this.rotateSecret();\n\n if (this.addPairedPeer(connection.remotePeer)) {\n addedPeer = connection.remotePeer;\n }\n await writeResponse(pairingStream, stream, {\n ok: true,\n name: this.config.name,\n });\n if (addedPeer) {\n this.notifyPairedPeer(addedPeer, request.name);\n }\n } catch (cause) {\n if (addedPeer) {\n this.removePairedPeer(addedPeer, false);\n }\n this.handleError(cause, stream);\n }\n }\n\n private addPairedPeer(peerId: PeerId) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout !== undefined) {\n clearTimeout(timeout);\n }\n\n this.pairingTimeouts.set(key, this.createPeerTimeout(peerId));\n return timeout === undefined;\n }\n\n private notifyPairedPeer(peerId: PeerId, name?: string) {\n this.pairedListeners.forEach((listener) => listener(peerId, name));\n }\n\n private rotateSecret() {\n this.currentSecret = createSecret();\n this.secretChangedListeners.forEach((listener) =>\n listener(this.currentSecret),\n );\n }\n\n private removePairedPeer(peerId: PeerId, notify = true) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout === undefined) {\n return false;\n }\n\n clearTimeout(timeout);\n this.pairingTimeouts.delete(key);\n\n if (notify) {\n this.unpairedListeners.forEach((listener) => listener(peerId));\n }\n return true;\n }\n\n private createPeerTimeout(peerId: PeerId) {\n return setTimeout(() => {\n this.removePairedPeer(peerId);\n }, this.config.pairedPeerTimeoutMs);\n }\n\n private async openStream(\n target: PeerId | Multiaddr | Multiaddr[],\n options: AbortOptions,\n ) {\n let connection: Connection | undefined;\n const stream = await this.components.connectionManager.openStream(\n target,\n this.config.protocol,\n {\n runOnLimitedConnection: true,\n signal: options.signal,\n onProgress(event) {\n if (event.type === \"connection:opened-stream\") {\n connection = event.detail.connection;\n }\n },\n },\n );\n\n return { stream, connection };\n }\n\n private handleError(cause: unknown, stream?: Stream) {\n const error = asError(cause, \"Pairing operation failed\");\n\n stream?.abort(error);\n this.errorListeners.forEach((listener) => listener(error));\n return error;\n }\n}\n\nexport function pairingService<\n Components extends PairingServiceComponents = PairingServiceComponents,\n>(\n config: PairingServiceConfig,\n canPair: PairingGuard<Components>,\n): (components: Components) => PairingService<Components> {\n return (components: Components) =>\n new (class extends PairingService<Components> {\n protected canPair(peerId: PeerId) {\n return canPair.call(this, peerId);\n }\n })(components, config);\n}\n\nasync function exchange(\n stream: Stream,\n request: PairingRequest,\n options: AbortOptions,\n) {\n const pairingStream = lpStream(stream, {\n maxDataLength: MAX_PAIRING_MESSAGE_LENGTH,\n });\n\n await pairingStream.write(ccc.bytesFrom(JSON.stringify(request), \"utf8\"), {\n signal: options.signal,\n });\n await stream.close(options);\n\n const response = parsePairingResponse(\n (await pairingStream.read(options)).subarray(),\n );\n await stream.closeRead(options);\n return response;\n}\n\nasync function writeResponse(\n pairingStream: ReturnType<typeof lpStream>,\n stream: Stream,\n response: PairingResponse,\n) {\n await pairingStream.write(ccc.bytesFrom(JSON.stringify(response), \"utf8\"));\n await stream.close();\n}\n\nfunction parsePairingRequest(data: Uint8Array): PairingRequest {\n const request = JSON.parse(\n ccc.bytesTo(data, \"utf8\"),\n ) as Partial<PairingRequest>;\n if (\n request.type === \"pair\" &&\n typeof request.secret === \"string\" &&\n request.secret\n ) {\n return {\n type: \"pair\",\n secret: request.secret,\n name: typeof request.name === \"string\" ? request.name : undefined,\n };\n }\n\n if (request.type === \"unpair\") {\n return { type: \"unpair\" };\n }\n\n throw new Error(\"Invalid pairing request\");\n}\n\nfunction parsePairingResponse(data: Uint8Array): PairingResponse {\n const response = JSON.parse(\n ccc.bytesTo(data, \"utf8\"),\n ) as Partial<PairingResponse>;\n if (response.ok === true) {\n return {\n ok: true,\n name: typeof response.name === \"string\" ? response.name : undefined,\n };\n }\n if (response.ok === false && typeof response.error === \"string\") {\n return { ok: false, error: response.error };\n }\n\n throw new Error(\"Invalid pairing response\");\n}\n\nfunction createSecret() {\n return ccc.bytesTo(crypto.getRandomValues(new Uint8Array(16)), \"base64url\");\n}\n\nfunction asError(cause: unknown, fallback: string) {\n return cause instanceof Error ? cause : new Error(fallback);\n}\n","export * from \"./jsonRpcService.js\";\nexport * from \"./jsonRpcTransport.js\";\nexport * from \"./pairingEndpoint.js\";\nexport * from \"./pairingService.js\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"barrel.mjs","names":["DEFAULT_MAX_MESSAGE_LENGTH","asJsonRpcError"],"sources":["../src/dial.ts","../src/jsonRpcService.ts","../src/jsonRpcTransport.ts","../src/pairingEndpoint.ts","../src/pairingService.ts","../src/barrel.ts"],"sourcesContent":["import type { Libp2p, PeerId } from \"@libp2p/interface\";\nimport type { Multiaddr } from \"@multiformats/multiaddr\";\n\nexport async function dialKnownAddresses(\n node: Libp2p,\n peerId: PeerId,\n signal?: AbortSignal,\n) {\n const peer = await node.peerStore.get(peerId);\n // PeerStore may omit the target peer id. Restore it so libp2p can\n // attempt a direct upgrade without force-dialing.\n const addresses = peer.addresses.flatMap(({ multiaddr }) => {\n const address = addressForPeer(multiaddr, peerId);\n return address ? [address] : [];\n });\n if (addresses.length === 0) {\n return;\n }\n\n return signal ? node.dial(addresses, { signal }) : node.dial(addresses);\n}\n\nfunction addressForPeer(address: Multiaddr, peerId: PeerId) {\n const last = address.getComponents().at(-1);\n if (last?.name !== \"p2p\") {\n return address.encapsulate(`/p2p/${peerId.toString()}`);\n }\n return last.value === peerId.toString() ? address : undefined;\n}\n","import { ccc, JsonRpcError } from \"@ckb-ccc/core\";\nimport type { Connection, PeerId, Stream } from \"@libp2p/interface\";\nimport type { Registrar } from \"@libp2p/interface-internal\";\nimport { lpStream } from \"@libp2p/utils\";\n\nconst DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;\n\nexport type JsonRpcServiceComponents = {\n registrar: Registrar;\n};\n\nexport type JsonRpcServiceConfig = {\n protocol: string;\n maxMessageLength?: number;\n};\n\nexport type JsonRpcRequest = {\n connection: Connection;\n peerId: PeerId;\n payload: ccc.JsonRpcPayload;\n};\n\nexport type JsonRpcRequestHandler<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n> = (this: JsonRpcService<Components>, request: JsonRpcRequest) => unknown;\n\nexport abstract class JsonRpcService<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n> {\n private readonly errorListeners = new Set<(error: Error) => void>();\n readonly maxMessageLength: number;\n\n constructor(\n readonly components: Components,\n readonly config: JsonRpcServiceConfig,\n ) {\n const maxMessageLength =\n config.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH;\n if (!Number.isSafeInteger(maxMessageLength) || maxMessageLength <= 0) {\n throw new Error(\n \"Maximum JSON-RPC message length must be a positive integer\",\n );\n }\n\n this.maxMessageLength = maxMessageLength;\n }\n\n protected abstract handleRequest(request: JsonRpcRequest): unknown;\n\n onError(listener: (error: Error) => void) {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n async start() {\n await this.components.registrar.handle(\n this.config.protocol,\n this.handleProtocol.bind(this),\n { runOnLimitedConnection: true },\n );\n }\n\n async stop() {\n await this.components.registrar.unhandle(this.config.protocol);\n }\n\n private async handleProtocol(stream: Stream, connection: Connection) {\n let requestAborted = false;\n\n try {\n const rpcStream = lpStream(stream, {\n maxDataLength: this.maxMessageLength,\n });\n const payload = parseJsonRpcRequest(\n ccc.bytesTo((await rpcStream.read()).subarray(), \"utf8\"),\n );\n\n let response: ccc.JsonRpcResponse;\n try {\n response = {\n jsonrpc: \"2.0\",\n id: payload.id,\n result: await this.handleRequest({\n connection,\n peerId: connection.remotePeer,\n payload,\n }),\n };\n } catch (cause) {\n requestAborted = isAbortError(cause);\n const error = toJsonRpcError(cause);\n response = {\n jsonrpc: \"2.0\",\n id: payload.id,\n error: {\n code: error.code,\n message: error.message,\n data: error.data,\n },\n };\n }\n\n await rpcStream.write(ccc.bytesFrom(JSON.stringify(response), \"utf8\"));\n await stream.close();\n } catch (cause) {\n if (requestAborted) {\n return;\n }\n this.handleError(cause, stream);\n }\n }\n\n private handleError(cause: unknown, stream?: Stream) {\n const error = asJsonRpcError(cause);\n\n stream?.abort(error);\n this.errorListeners.forEach((listener) => listener(error));\n return error;\n }\n}\n\nfunction toJsonRpcError(cause: unknown) {\n if (cause instanceof JsonRpcError) {\n return cause;\n }\n\n return new JsonRpcError({\n code: -32603,\n message: asJsonRpcError(cause).message,\n });\n}\n\nfunction parseJsonRpcRequest(data: string) {\n const payload = JSON.parse(data) as Partial<ccc.JsonRpcPayload>;\n if (\n payload?.jsonrpc !== \"2.0\" ||\n (typeof payload.id !== \"number\" && typeof payload.id !== \"string\") ||\n typeof payload.method !== \"string\"\n ) {\n throw new Error(\"Invalid JSON-RPC request\");\n }\n\n return payload as ccc.JsonRpcPayload;\n}\n\nfunction asJsonRpcError(cause: unknown) {\n return cause instanceof Error ? cause : new Error(\"JSON-RPC request failed\");\n}\n\nfunction isAbortError(cause: unknown) {\n return (\n cause instanceof Error &&\n (cause.name === \"AbortError\" || cause.name === \"TimeoutError\")\n );\n}\n\nexport function jsonRpcService<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n>(\n config: JsonRpcServiceConfig,\n handler: JsonRpcRequestHandler<Components>,\n): (components: Components) => JsonRpcService<Components> {\n return (components: Components) =>\n new (class extends JsonRpcService<Components> {\n protected handleRequest(request: JsonRpcRequest) {\n return handler.call(this, request);\n }\n })(components, config);\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport type { Connection, Libp2p, PeerId, Stream } from \"@libp2p/interface\";\nimport { lpStream } from \"@libp2p/utils\";\nimport { dialKnownAddresses } from \"./dial.js\";\n\nconst DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;\nconst DEFAULT_TIMEOUT = 120_000;\n\nexport type JsonRpcTransportLibp2pConfig = {\n protocol: string;\n maxMessageLength?: number;\n timeout?: number;\n signal?: AbortSignal;\n onResponse?: (response: ccc.JsonRpcResponse) => void;\n};\n\nexport class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {\n private readonly maxMessageLength: number;\n private readonly timeout: number;\n\n constructor(\n private readonly node: Libp2p,\n readonly peerId: PeerId,\n private readonly config: JsonRpcTransportLibp2pConfig,\n ) {\n const maxMessageLength =\n config.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH;\n if (!Number.isSafeInteger(maxMessageLength) || maxMessageLength <= 0) {\n throw new Error(\n \"Maximum JSON-RPC message length must be a positive integer\",\n );\n }\n\n this.maxMessageLength = maxMessageLength;\n\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n if (!Number.isSafeInteger(timeout) || timeout <= 0) {\n throw new Error(\"JSON-RPC request timeout must be a positive integer\");\n }\n\n this.timeout = timeout;\n }\n\n async request(payload: ccc.JsonRpcPayload): Promise<ccc.JsonRpcResponse> {\n const timeoutSignal = AbortSignal.timeout(this.timeout);\n const signal = this.config.signal\n ? ccc.abortSignalAny([this.config.signal, timeoutSignal])\n : timeoutSignal;\n\n let stream: Stream | undefined;\n let response: ccc.JsonRpcResponse;\n try {\n stream = await this.openStream(signal);\n\n const rpcStream = lpStream(stream, {\n maxDataLength: this.maxMessageLength,\n });\n\n await rpcStream.write(ccc.bytesFrom(JSON.stringify(payload), \"utf8\"), {\n signal,\n });\n await stream.close({ signal });\n\n response = JSON.parse(\n ccc.bytesTo((await rpcStream.read({ signal })).subarray(), \"utf8\"),\n ) as ccc.JsonRpcResponse;\n await stream.closeRead({ signal });\n signal.throwIfAborted();\n } catch (cause) {\n const error = asJsonRpcError(cause);\n stream?.abort(error);\n throw error;\n }\n\n this.config.onResponse?.(response);\n return response;\n }\n\n private async openStream(signal: AbortSignal) {\n void dialKnownAddresses(this.node, this.peerId).catch(() => {\n // This is opportunistic; the current stream can still use relay/fallbacks.\n });\n\n for (const connection of this.connections()) {\n try {\n return await connection.newStream(this.config.protocol, {\n runOnLimitedConnection: true,\n signal,\n });\n } catch {\n signal.throwIfAborted();\n }\n }\n\n return this.node.dialProtocol(this.peerId, this.config.protocol, {\n runOnLimitedConnection: true,\n signal,\n });\n }\n\n private connections() {\n return this.node\n .getConnections(this.peerId)\n .filter(({ status }) => status === \"open\")\n .sort(compareConnections);\n }\n}\n\nfunction compareConnections(a: Connection, b: Connection) {\n if (a.direct !== b.direct) {\n return a.direct ? -1 : 1;\n }\n if (a.rtt === undefined) {\n return b.rtt === undefined ? 0 : 1;\n }\n return b.rtt === undefined ? -1 : a.rtt - b.rtt;\n}\n\nfunction asJsonRpcError(cause: unknown) {\n return cause instanceof Error ? cause : new Error(\"JSON-RPC request failed\");\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport { multiaddr, type Multiaddr } from \"@multiformats/multiaddr\";\nimport * as lp from \"it-length-prefixed\";\nimport type { PairingTarget } from \"./pairingService.js\";\n\nconst MAX_DECOMPRESSED_ADDRESSES_LENGTH = 16 * 1024;\nconst MAX_ADDRESSES = 16;\nconst PAIRING_PARAMETER_NAMES = [\"addresses\", \"role\", \"secret\"] as const;\n\nexport class PairingEndpointRoleError extends Error {\n constructor(\n readonly expectedRole: string,\n readonly actualRole: string | undefined,\n ) {\n super(\n `Expected a ${expectedRole} pairing endpoint, received ${actualRole ? `role \"${actualRole}\"` : \"an endpoint without a role\"}`,\n );\n this.name = \"PairingEndpointRoleError\";\n }\n}\n\nexport async function encodePairingEndpoint(\n endpointUrl: string,\n addresses: readonly Multiaddr[],\n secret: string,\n role?: string,\n) {\n if (addresses.length === 0) {\n throw new Error(\"Pairing endpoint requires at least one address\");\n }\n if (addresses.length > MAX_ADDRESSES) {\n throw new Error(\n `Pairing endpoint supports at most ${MAX_ADDRESSES} addresses`,\n );\n }\n\n const addressBytes = encodeAddresses(addresses);\n if (addressBytes.byteLength > MAX_DECOMPRESSED_ADDRESSES_LENGTH) {\n throw new Error(\"Pairing endpoint address data is too large\");\n }\n\n const compressedAddressBytes = await transformBytes(\n addressBytes,\n new CompressionStream(\"deflate\"),\n );\n\n const encodedAddresses = ccc.bytesTo(compressedAddressBytes, \"base64url\");\n\n const url = new URL(endpointUrl);\n const { params, route } = fragmentParameters(url);\n\n PAIRING_PARAMETER_NAMES.forEach((name) => {\n params.delete(name);\n });\n if (role) {\n params.set(\"role\", role);\n }\n params.set(\"addresses\", encodedAddresses);\n params.set(\"secret\", secret);\n url.hash = `${route}?${params.toString()}`;\n\n return url.toString();\n}\n\nexport async function decodePairingEndpoint(\n endpoint: string,\n expectedRole?: string,\n): Promise<PairingTarget> {\n const url = new URL(endpoint.trim());\n const { params } = fragmentParameters(url);\n if (expectedRole) {\n const actualRole = params.get(\"role\")?.trim() || undefined;\n if (actualRole !== expectedRole) {\n throw new PairingEndpointRoleError(expectedRole, actualRole);\n }\n }\n const compressedAddresses = params.get(\"addresses\")?.trim();\n const secret = params.get(\"secret\")?.trim();\n\n if (!compressedAddresses || !secret) {\n throw new Error(\"Pairing endpoint is incomplete\");\n }\n\n const addresses = await decodeCompressedAddresses(compressedAddresses);\n\n return { addresses, secret };\n}\n\nfunction fragmentParameters(url: URL) {\n const fragment = url.hash.slice(1);\n const separator = fragment.indexOf(\"?\");\n if (separator === -1) {\n return { params: new URLSearchParams(), route: fragment };\n }\n\n return {\n params: new URLSearchParams(fragment.slice(separator + 1)),\n route: fragment.slice(0, separator),\n };\n}\n\nfunction encodeAddresses(addresses: readonly Multiaddr[]): Uint8Array {\n return ccc.bytesConcat(\n ...lp.encode(addresses.map((address) => address.bytes)),\n );\n}\n\nasync function decodeCompressedAddresses(value: string): Promise<Multiaddr[]> {\n try {\n const compressedBytes = ccc.bytesFrom(value, \"base64url\");\n const bytes = await transformBytes(\n compressedBytes,\n new DecompressionStream(\"deflate\"),\n MAX_DECOMPRESSED_ADDRESSES_LENGTH,\n );\n const addresses: Multiaddr[] = [];\n for (const address of lp.decode([bytes], {\n maxDataLength: MAX_DECOMPRESSED_ADDRESSES_LENGTH,\n })) {\n if (addresses.length >= MAX_ADDRESSES) {\n throw new Error(\"Too many addresses\");\n }\n addresses.push(multiaddr(address.subarray()));\n }\n\n if (addresses.length === 0) {\n throw new Error(\"Missing addresses\");\n }\n return addresses;\n } catch (cause) {\n throw new Error(\"Pairing endpoint contains invalid compressed addresses\", {\n cause,\n });\n }\n}\n\nasync function transformBytes(\n bytes: Uint8Array,\n transform: CompressionStream | DecompressionStream,\n maxOutputLength?: number,\n) {\n const readable =\n maxOutputLength === undefined\n ? transform.readable\n : transform.readable.pipeThrough(limitBytes(maxOutputLength));\n const output = new Response(readable).arrayBuffer().then(\n (buffer) => ({ buffer, ok: true }) as const,\n (error: unknown) => ({ error, ok: false }) as const,\n );\n const writer = transform.writable.getWriter();\n try {\n await writer.write(Uint8Array.from(bytes));\n await writer.close();\n } catch (cause) {\n const result = await output;\n if (!result.ok) {\n throw result.error;\n }\n throw cause;\n }\n\n const result = await output;\n if (!result.ok) {\n throw result.error;\n }\n return new Uint8Array(result.buffer);\n}\n\nfunction limitBytes(maxLength: number) {\n let length = 0;\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n if (chunk.byteLength > maxLength - length) {\n throw new Error(\"Decompressed address data is too large\");\n }\n length += chunk.byteLength;\n controller.enqueue(chunk);\n },\n });\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport type {\n AbortOptions,\n Connection,\n PeerId,\n Stream,\n} from \"@libp2p/interface\";\nimport type {\n AddressManager,\n ConnectionManager,\n Registrar,\n} from \"@libp2p/interface-internal\";\nimport { lpStream } from \"@libp2p/utils\";\nimport { CODE_P2P, multiaddr, type Multiaddr } from \"@multiformats/multiaddr\";\n\nconst MAX_PAIRING_MESSAGE_LENGTH = 1024;\nconst UNPAIR_TIMEOUT_MS = 10_000;\n\nexport type PairingServiceComponents = {\n addressManager?: Pick<AddressManager, \"getAddresses\">;\n connectionManager: ConnectionManager;\n registrar: Registrar;\n};\n\nexport type PairingServiceConfig = {\n /** Optional self-reported label shared with the paired peer. */\n name?: string;\n protocol: string;\n pairedPeerTimeoutMs: number;\n};\n\nexport type PairingTarget = {\n addresses: Multiaddr[];\n secret: string;\n};\n\nexport type PairingGuard<\n Components extends PairingServiceComponents = PairingServiceComponents,\n> = (this: PairingService<Components>, peerId: PeerId) => boolean;\n\ntype PairingRequest =\n | { type: \"pair\"; secret: string; name?: string; addresses?: string[] }\n | { type: \"unpair\" };\n\ntype PairingResponse =\n { ok: true; name?: string } | { ok: false; error: string };\n\nexport abstract class PairingService<\n Components extends PairingServiceComponents = PairingServiceComponents,\n> {\n private currentSecret = createSecret();\n private readonly errorListeners = new Set<(error: Error) => void>();\n private readonly pairedListeners = new Set<\n (peerId: PeerId, name?: string) => void\n >();\n private readonly unpairedListeners = new Set<(peerId: PeerId) => void>();\n private readonly secretChangedListeners = new Set<(secret: string) => void>();\n private readonly pairingTimeouts = new Map<\n string,\n ReturnType<typeof setTimeout>\n >();\n\n constructor(\n readonly components: Components,\n readonly config: PairingServiceConfig,\n ) {\n if (config.pairedPeerTimeoutMs <= 0) {\n throw new Error(\"Paired peer timeout must be greater than zero\");\n }\n }\n\n protected abstract canPair(peerId: PeerId): boolean;\n\n /** The credential to include in the currently issued pairing endpoint. */\n get secret() {\n return this.currentSecret;\n }\n\n onError(listener: (error: Error) => void) {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n onPaired(listener: (peerId: PeerId, name?: string) => void) {\n this.pairedListeners.add(listener);\n return () => this.pairedListeners.delete(listener);\n }\n\n onUnpaired(listener: (peerId: PeerId) => void) {\n this.unpairedListeners.add(listener);\n return () => this.unpairedListeners.delete(listener);\n }\n\n onSecretChanged(listener: (secret: string) => void) {\n this.secretChangedListeners.add(listener);\n return () => this.secretChangedListeners.delete(listener);\n }\n\n async start() {\n await this.components.registrar.handle(\n this.config.protocol,\n this.handleProtocol.bind(this),\n { runOnLimitedConnection: true },\n );\n }\n\n async stop() {\n await this.components.registrar.unhandle(this.config.protocol);\n\n this.pairingTimeouts.forEach((timeout) => clearTimeout(timeout));\n this.pairingTimeouts.clear();\n }\n\n isPaired(peerId: PeerId) {\n return this.pairingTimeouts.has(peerId.toString());\n }\n\n refresh(peerId: PeerId) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout === undefined) {\n return;\n }\n\n clearTimeout(timeout);\n this.pairingTimeouts.set(key, this.createPeerTimeout(peerId));\n }\n\n async pair(\n target: PairingTarget,\n options: AbortOptions = {},\n ): Promise<PeerId> {\n let stream: Stream | undefined;\n let addedPeer: PeerId | undefined;\n\n try {\n const opened = await this.openStream(target.addresses, options);\n stream = opened.stream;\n\n if (!opened.connection) {\n throw new Error(\"Could not determine remote peer\");\n }\n\n if (this.addPairedPeer(opened.connection.remotePeer)) {\n addedPeer = opened.connection.remotePeer;\n }\n\n const response = await exchange(\n stream,\n createPairRequest(target.secret, this.config.name, this.addresses()),\n options,\n );\n options.signal?.throwIfAborted();\n stream = undefined;\n\n if (!response.ok) {\n throw new Error(response.error);\n }\n\n if (addedPeer) {\n this.rotateSecret();\n this.notifyPairedPeer(addedPeer, response.name);\n }\n return opened.connection.remotePeer;\n } catch (cause) {\n if (addedPeer) {\n this.removePairedPeer(addedPeer, false);\n }\n throw this.handleError(cause, stream);\n }\n }\n\n async unpair(peerId: PeerId) {\n if (!this.removePairedPeer(peerId)) {\n return;\n }\n\n const options = { signal: AbortSignal.timeout(UNPAIR_TIMEOUT_MS) };\n\n let stream: Stream | undefined;\n try {\n const opened = await this.openStream(peerId, options);\n stream = opened.stream;\n const response = await exchange(\n opened.stream,\n { type: \"unpair\" },\n options,\n );\n stream = undefined;\n if (!response.ok) {\n throw new Error(response.error);\n }\n } catch (cause) {\n this.handleError(cause, stream);\n }\n }\n\n private async handleProtocol(stream: Stream, connection: Connection) {\n let addedPeer: PeerId | undefined;\n\n try {\n const pairingStream = lpStream(stream, {\n maxDataLength: MAX_PAIRING_MESSAGE_LENGTH,\n });\n const request = parsePairingRequest(\n (await pairingStream.read()).subarray(),\n );\n\n if (request.type === \"unpair\") {\n this.removePairedPeer(connection.remotePeer);\n await writeResponse(pairingStream, stream, { ok: true });\n return;\n }\n\n if (\n request.secret !== this.secret ||\n !this.canPair(connection.remotePeer)\n ) {\n await writeResponse(pairingStream, stream, {\n ok: false,\n error: \"Pairing rejected\",\n });\n return;\n }\n\n const addresses = parseAddresses(\n request.addresses,\n connection.remotePeer,\n );\n\n // Consume the credential before the next asynchronous boundary so two\n // concurrent requests cannot both pair with the same endpoint.\n this.rotateSecret();\n\n if (this.addPairedPeer(connection.remotePeer)) {\n addedPeer = connection.remotePeer;\n }\n await writeResponse(pairingStream, stream, {\n ok: true,\n name: this.config.name,\n });\n if (addedPeer) {\n this.notifyPairedPeer(addedPeer, request.name);\n void this.openFallbackConnection(addresses);\n }\n } catch (cause) {\n if (addedPeer) {\n this.removePairedPeer(addedPeer, false);\n }\n this.handleError(cause, stream);\n }\n }\n\n private addPairedPeer(peerId: PeerId) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout !== undefined) {\n clearTimeout(timeout);\n }\n\n this.pairingTimeouts.set(key, this.createPeerTimeout(peerId));\n return timeout === undefined;\n }\n\n private notifyPairedPeer(peerId: PeerId, name?: string) {\n this.pairedListeners.forEach((listener) => listener(peerId, name));\n }\n\n private addresses() {\n return this.components.addressManager?.getAddresses() ?? [];\n }\n\n private async openFallbackConnection(addresses: Multiaddr[]) {\n if (addresses.length === 0) {\n return;\n }\n\n try {\n await this.components.connectionManager.openConnection(addresses);\n } catch {\n // The original pairing connection remains usable when the fallback fails.\n }\n }\n\n private rotateSecret() {\n this.currentSecret = createSecret();\n this.secretChangedListeners.forEach((listener) =>\n listener(this.currentSecret),\n );\n }\n\n private removePairedPeer(peerId: PeerId, notify = true) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout === undefined) {\n return false;\n }\n\n clearTimeout(timeout);\n this.pairingTimeouts.delete(key);\n\n if (notify) {\n this.unpairedListeners.forEach((listener) => listener(peerId));\n }\n return true;\n }\n\n private createPeerTimeout(peerId: PeerId) {\n return setTimeout(() => {\n if (\n this.components.connectionManager\n .getConnections(peerId)\n .some(({ status }) => status === \"open\")\n ) {\n this.pairingTimeouts.set(\n peerId.toString(),\n this.createPeerTimeout(peerId),\n );\n return;\n }\n\n this.removePairedPeer(peerId);\n }, this.config.pairedPeerTimeoutMs);\n }\n\n private async openStream(\n target: PeerId | Multiaddr | Multiaddr[],\n options: AbortOptions,\n ) {\n let connection: Connection | undefined;\n const stream = await this.components.connectionManager.openStream(\n target,\n this.config.protocol,\n {\n runOnLimitedConnection: true,\n signal: options.signal,\n onProgress(event) {\n if (event.type === \"connection:opened-stream\") {\n connection = event.detail.connection;\n }\n },\n },\n );\n\n return { stream, connection };\n }\n\n private handleError(cause: unknown, stream?: Stream) {\n const error = asError(cause, \"Pairing operation failed\");\n\n stream?.abort(error);\n this.errorListeners.forEach((listener) => listener(error));\n return error;\n }\n}\n\nexport function pairingService<\n Components extends PairingServiceComponents = PairingServiceComponents,\n>(\n config: PairingServiceConfig,\n canPair: PairingGuard<Components>,\n): (components: Components) => PairingService<Components> {\n return (components: Components) =>\n new (class extends PairingService<Components> {\n protected canPair(peerId: PeerId) {\n return canPair.call(this, peerId);\n }\n })(components, config);\n}\n\nasync function exchange(\n stream: Stream,\n request: PairingRequest,\n options: AbortOptions,\n) {\n const pairingStream = lpStream(stream, {\n maxDataLength: MAX_PAIRING_MESSAGE_LENGTH,\n });\n\n await pairingStream.write(ccc.bytesFrom(JSON.stringify(request), \"utf8\"), {\n signal: options.signal,\n });\n await stream.close(options);\n\n const response = parsePairingResponse(\n (await pairingStream.read(options)).subarray(),\n );\n await stream.closeRead(options);\n return response;\n}\n\nfunction createPairRequest(\n secret: string,\n name: string | undefined,\n addresses: Multiaddr[],\n): PairingRequest {\n const addressStrings = addresses.map((address) => address.toString());\n const request: PairingRequest = {\n type: \"pair\",\n secret,\n name,\n addresses: addressStrings,\n };\n while (addressStrings.length > 0 && !pairingRequestFits(request)) {\n addressStrings.pop();\n }\n\n if (!pairingRequestFits(request)) {\n delete request.addresses;\n }\n if (!pairingRequestFits(request)) {\n delete request.name;\n }\n if (!pairingRequestFits(request)) {\n throw new Error(\"Pairing request is too large\");\n }\n return request;\n}\n\nfunction pairingRequestFits(request: PairingRequest) {\n return (\n ccc.bytesFrom(JSON.stringify(request), \"utf8\").byteLength <=\n MAX_PAIRING_MESSAGE_LENGTH\n );\n}\n\nasync function writeResponse(\n pairingStream: ReturnType<typeof lpStream>,\n stream: Stream,\n response: PairingResponse,\n) {\n await pairingStream.write(ccc.bytesFrom(JSON.stringify(response), \"utf8\"));\n await stream.close();\n}\n\nfunction parsePairingRequest(data: Uint8Array): PairingRequest {\n const request = JSON.parse(\n ccc.bytesTo(data, \"utf8\"),\n ) as Partial<PairingRequest>;\n if (\n request.type === \"pair\" &&\n typeof request.secret === \"string\" &&\n request.secret\n ) {\n return {\n type: \"pair\",\n secret: request.secret,\n name: typeof request.name === \"string\" ? request.name : undefined,\n addresses: parseAddressStrings(request.addresses),\n };\n }\n\n if (request.type === \"unpair\") {\n return { type: \"unpair\" };\n }\n\n throw new Error(\"Invalid pairing request\");\n}\n\nfunction parseAddressStrings(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n return value.filter(\n (address): address is string =>\n typeof address === \"string\" && address.length > 0,\n );\n}\n\nfunction parseAddresses(values: string[] | undefined, peerId: PeerId) {\n return (values ?? []).flatMap((value) => {\n try {\n const address = multiaddr(value);\n const peer = address\n .getComponents()\n .filter(({ code }) => code === CODE_P2P)\n .at(-1)?.value;\n return peer === peerId.toString() ? [address] : [];\n } catch {\n return [];\n }\n });\n}\n\nfunction parsePairingResponse(data: Uint8Array): PairingResponse {\n const response = JSON.parse(\n ccc.bytesTo(data, \"utf8\"),\n ) as Partial<PairingResponse>;\n if (response.ok === true) {\n return {\n ok: true,\n name: typeof response.name === \"string\" ? response.name : undefined,\n };\n }\n if (response.ok === false && typeof response.error === \"string\") {\n return { ok: false, error: response.error };\n }\n\n throw new Error(\"Invalid pairing response\");\n}\n\nfunction createSecret() {\n return ccc.bytesTo(crypto.getRandomValues(new Uint8Array(16)), \"base64url\");\n}\n\nfunction asError(cause: unknown, fallback: string) {\n return cause instanceof Error ? cause : new Error(fallback);\n}\n","export * from \"./dial.js\";\nexport * from \"./jsonRpcService.js\";\nexport * from \"./jsonRpcTransport.js\";\nexport * from \"./pairingEndpoint.js\";\nexport * from \"./pairingService.js\";\n"],"mappings":"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"}
@@ -1,8 +1,11 @@
1
+ import { AbortOptions, Connection, Libp2p, PeerId } from "@libp2p/interface";
1
2
  import { ccc } from "@ckb-ccc/core";
2
- import { AbortOptions, Libp2p, PeerId } from "@libp2p/interface";
3
- import { ConnectionManager, Registrar } from "@libp2p/interface-internal";
3
+ import { AddressManager, ConnectionManager, Registrar } from "@libp2p/interface-internal";
4
4
  import { Multiaddr } from "@multiformats/multiaddr";
5
5
  //#endregion
6
+ //#region src/dial.d.ts
7
+ declare function dialKnownAddresses(node: Libp2p, peerId: PeerId, signal?: AbortSignal): Promise<import("@libp2p/interface").Connection | undefined>;
8
+ //#endregion
6
9
  //#region src/jsonRpcService.d.ts
7
10
  type JsonRpcServiceComponents = {
8
11
  registrar: Registrar;
@@ -12,6 +15,7 @@ type JsonRpcServiceConfig = {
12
15
  maxMessageLength?: number;
13
16
  };
14
17
  type JsonRpcRequest = {
18
+ connection: Connection;
15
19
  peerId: PeerId;
16
20
  payload: ccc.JsonRpcPayload;
17
21
  };
@@ -47,10 +51,13 @@ declare class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {
47
51
  private readonly timeout;
48
52
  constructor(node: Libp2p, peerId: PeerId, config: JsonRpcTransportLibp2pConfig);
49
53
  request(payload: ccc.JsonRpcPayload): Promise<ccc.JsonRpcResponse>;
54
+ private openStream;
55
+ private connections;
50
56
  }
51
57
  //#endregion
52
58
  //#region src/pairingService.d.ts
53
59
  type PairingServiceComponents = {
60
+ addressManager?: Pick<AddressManager, "getAddresses">;
54
61
  connectionManager: ConnectionManager;
55
62
  registrar: Registrar;
56
63
  };
@@ -91,6 +98,8 @@ declare abstract class PairingService<Components extends PairingServiceComponent
91
98
  private handleProtocol;
92
99
  private addPairedPeer;
93
100
  private notifyPairedPeer;
101
+ private addresses;
102
+ private openFallbackConnection;
94
103
  private rotateSecret;
95
104
  private removePairedPeer;
96
105
  private createPeerTimeout;
@@ -108,8 +117,8 @@ declare class PairingEndpointRoleError extends Error {
108
117
  declare function encodePairingEndpoint(endpointUrl: string, addresses: readonly Multiaddr[], secret: string, role?: string): Promise<string>;
109
118
  declare function decodePairingEndpoint(endpoint: string, expectedRole?: string): Promise<PairingTarget>;
110
119
  declare namespace barrel_d_exports {
111
- export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, encodePairingEndpoint, jsonRpcService, pairingService };
120
+ export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, dialKnownAddresses, encodePairingEndpoint, jsonRpcService, pairingService };
112
121
  }
113
122
  //#endregion
114
- export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, encodePairingEndpoint, jsonRpcService, pairingService, barrel_d_exports as t };
123
+ export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, dialKnownAddresses, encodePairingEndpoint, jsonRpcService, pairingService, barrel_d_exports as t };
115
124
  //# sourceMappingURL=barrel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"barrel.d.ts","names":[],"sources":["../src/jsonRpcService.ts","../src/jsonRpcTransport.ts","../src/pairingService.ts","../src/pairingEndpoint.ts","../src/barrel.ts"],"mappings":";;;;;;KAOY;EACV,WAAW;;KAGD;EACV;EACA;;KAGU;EACV,QAAQ;EACR,SAAS,IAAI;;KAGH,sBACV,mBAAmB,2BAA2B,6BAC3C,MAAM,eAAe,aAAa,SAAS;uBAE1B,eACpB,mBAAmB,2BAA2B;WAMnC,YAAY;WACZ,QAAQ;mBALF;WACR;EAGE,YAAA,YAAY,YACZ,QAAQ;qBAaA,cAAc,SAAS;EAE1C,QAAQ,WAAW,OAAO;EAKpB,SAAK;EAQL,QAAI;UAII;UA6CN;;iBA4CM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,sBAAsB,eAC7B,YAAY,eAAe,eAAe;;;KCxJlC;EACV;EACA;EACA;EACA,SAAS;EACT,cAAc,UAAU,IAAI;;cAGjB,kCAAkC,IAAI;mBAK9B;WACR,QAAQ;mBACA;mBANF;mBACA;EAGE,YAAA,MAAM,QACd,QAAQ,QACA,QAAQ;EAoBrB,QAAQ,SAAS,IAAI,iBAAiB,QAAQ,IAAI;;;;KC5B9C;EACV,mBAAmB;EACnB,WAAW;;KAGD;;EAEV;EACA;EACA;;KAGU;EACV,WAAW;EACX;;KAGU,aACV,mBAAmB,2BAA2B,6BAC3C,MAAM,eAAe,aAAa,QAAQ;uBAQzB,eACpB,mBAAmB,2BAA2B;WAenC,YAAY;WACZ,QAAQ;UAdX;mBACS;mBACA;mBAGA;mBACA;mBACA;EAMN,YAAA,YAAY,YACZ,QAAQ;qBAOA,QAAQ,QAAQ;;MAG/B;EAIJ,QAAQ,WAAW,OAAO;EAK1B,SAAS,WAAW,QAAQ,QAAQ;EAKpC,WAAW,WAAW,QAAQ;EAK9B,gBAAgB,WAAW;EAKrB,SAAK;EAQL,QAAI;EAOV,SAAS,QAAQ;EAIjB,QAAQ,QAAQ;EAWV,KACJ,QAAQ,eACR,UAAS,eACR,QAAQ;EA6CL,OAAO,QAAQ,SAAM;UAyBb;UAkDN;UAWA;UAIA;UAOA;UAgBA;UAMM;UAsBN;;iBASM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,aAAa,eACpB,YAAY,eAAe,eAAe;;;cC5TjC,iCAAiC;WAEjC;WACA;EADA,YAAA,sBACA;;iBASS,sBACpB,qBACA,oBAAoB,aACpB,gBACA,gBAAa;iBAuCO,sBACpB,kBACA,wBACC,QAAQ"}
1
+ {"version":3,"file":"barrel.d.ts","names":[],"sources":["../src/dial.ts","../src/jsonRpcService.ts","../src/jsonRpcTransport.ts","../src/pairingService.ts","../src/pairingEndpoint.ts","../src/barrel.ts"],"mappings":";;;;;;iBAGsB,mBACpB,MAAM,QACN,QAAQ,QACR,SAAS,cAAW,oCAAA;;;KCCV;EACV,WAAW;;KAGD;EACV;EACA;;KAGU;EACV,YAAY;EACZ,QAAQ;EACR,SAAS,IAAI;;KAGH,sBACV,mBAAmB,2BAA2B,6BAC3C,MAAM,eAAe,aAAa,SAAS;uBAE1B,eACpB,mBAAmB,2BAA2B;WAMnC,YAAY;WACZ,QAAQ;mBALF;WACR;EAGE,YAAA,YAAY,YACZ,QAAQ;qBAaA,cAAc,SAAS;EAE1C,QAAQ,WAAW,OAAO;EAKpB,SAAK;EAQL,QAAI;UAII;UA8CN;;iBA4CM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,sBAAsB,eAC7B,YAAY,eAAe,eAAe;;;KCzJlC;EACV;EACA;EACA;EACA,SAAS;EACT,cAAc,UAAU,IAAI;;cAGjB,kCAAkC,IAAI;mBAK9B;WACR,QAAQ;mBACA;mBANF;mBACA;EAGE,YAAA,MAAM,QACd,QAAQ,QACA,QAAQ;EAoBrB,QAAQ,SAAS,IAAI,iBAAiB,QAAQ,IAAI;UAmC1C;UAsBN;;;;KClFE;EACV,iBAAiB,KAAK;EACtB,mBAAmB;EACnB,WAAW;;KAGD;;EAEV;EACA;EACA;;KAGU;EACV,WAAW;EACX;;KAGU,aACV,mBAAmB,2BAA2B,6BAC3C,MAAM,eAAe,aAAa,QAAQ;uBASzB,eACpB,mBAAmB,2BAA2B;WAenC,YAAY;WACZ,QAAQ;UAdX;mBACS;mBACA;mBAGA;mBACA;mBACA;EAMN,YAAA,YAAY,YACZ,QAAQ;qBAOA,QAAQ,QAAQ;;MAG/B;EAIJ,QAAQ,WAAW,OAAO;EAK1B,SAAS,WAAW,QAAQ,QAAQ;EAKpC,WAAW,WAAW,QAAQ;EAK9B,gBAAgB,WAAW;EAKrB,SAAK;EAQL,QAAI;EAOV,SAAS,QAAQ;EAIjB,QAAQ,QAAQ;EAWV,KACJ,QAAQ,eACR,UAAS,eACR,QAAQ;EAyCL,OAAO,QAAQ,SAAM;UAyBb;UAwDN;UAWA;UAIA;UAIM;UAYN;UAOA;UAgBA;UAkBM;UAsBN;;iBASM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,aAAa,eACpB,YAAY,eAAe,eAAe;;;cChWjC,iCAAiC;WAEjC;WACA;EADA,YAAA,sBACA;;iBASS,sBACpB,qBACA,oBAAoB,aACpB,gBACA,gBAAa;iBAuCO,sBACpB,kBACA,wBACC,QAAQ"}
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,o)=>(o=n==null?{}:e(i(n)),s(r||!n||!n.__esModule||!a.call(n,`default`)?t(o,`default`,{value:n,enumerable:!0}):o,n));let l=require("@ckb-ccc/core"),u=require("@libp2p/utils"),d=require("@multiformats/multiaddr"),f=require("it-length-prefixed");f=c(f);var p=class{constructor(e,t){this.components=e,this.config=t,this.errorListeners=new Set;let n=t.maxMessageLength??1048576;if(!Number.isSafeInteger(n)||n<=0)throw Error(`Maximum JSON-RPC message length must be a positive integer`);this.maxMessageLength=n}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}async start(){await this.components.registrar.handle(this.config.protocol,this.handleProtocol.bind(this),{runOnLimitedConnection:!0})}async stop(){await this.components.registrar.unhandle(this.config.protocol)}async handleProtocol(e,t){let n=!1;try{let r=(0,u.lpStream)(e,{maxDataLength:this.maxMessageLength}),i=h(l.ccc.bytesTo((await r.read()).subarray(),`utf8`)),a;try{a={jsonrpc:`2.0`,id:i.id,result:await this.handleRequest({peerId:t.remotePeer,payload:i})}}catch(e){n=_(e);let t=m(e);a={jsonrpc:`2.0`,id:i.id,error:{code:t.code,message:t.message,data:t.data}}}await r.write(l.ccc.bytesFrom(JSON.stringify(a),`utf8`)),await e.close()}catch(t){if(n)return;this.handleError(t,e)}}handleError(e,t){let n=g(e);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function m(e){return e instanceof l.JsonRpcError?e:new l.JsonRpcError({code:-32603,message:g(e).message})}function h(e){let t=JSON.parse(e);if(t?.jsonrpc!==`2.0`||typeof t.id!=`number`&&typeof t.id!=`string`||typeof t.method!=`string`)throw Error(`Invalid JSON-RPC request`);return t}function g(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}function _(e){return e instanceof Error&&(e.name===`AbortError`||e.name===`TimeoutError`)}function v(e,t){return n=>new class extends p{handleRequest(e){return t.call(this,e)}}(n,e)}var y=class{constructor(e,t,n){this.node=e,this.peerId=t,this.config=n;let r=n.maxMessageLength??1048576;if(!Number.isSafeInteger(r)||r<=0)throw Error(`Maximum JSON-RPC message length must be a positive integer`);this.maxMessageLength=r;let i=n.timeout??12e4;if(!Number.isSafeInteger(i)||i<=0)throw Error(`JSON-RPC request timeout must be a positive integer`);this.timeout=i}async request(e){let t=AbortSignal.timeout(this.timeout),n=this.config.signal?l.ccc.abortSignalAny([this.config.signal,t]):t,r,i;try{r=await this.node.dialProtocol(this.peerId,this.config.protocol,{runOnLimitedConnection:!0,signal:n});let t=(0,u.lpStream)(r,{maxDataLength:this.maxMessageLength});await t.write(l.ccc.bytesFrom(JSON.stringify(e),`utf8`),{signal:n}),await r.close({signal:n}),i=JSON.parse(l.ccc.bytesTo((await t.read({signal:n})).subarray(),`utf8`)),await r.closeRead({signal:n}),n.throwIfAborted()}catch(e){let t=b(e);throw r?.abort(t),t}return this.config.onResponse?.(i),i}};function b(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}const x=16384,S=[`addresses`,`role`,`secret`];var C=class extends Error{constructor(e,t){super(`Expected a ${e} pairing endpoint, received ${t?`role "${t}"`:`an endpoint without a role`}`),this.expectedRole=e,this.actualRole=t,this.name=`PairingEndpointRoleError`}};async function w(e,t,n,r){if(t.length===0)throw Error(`Pairing endpoint requires at least one address`);if(t.length>16)throw Error(`Pairing endpoint supports at most 16 addresses`);let i=D(t);if(i.byteLength>x)throw Error(`Pairing endpoint address data is too large`);let a=await k(i,new CompressionStream(`deflate`)),o=l.ccc.bytesTo(a,`base64url`),s=new URL(e),{params:c,route:u}=E(s);return S.forEach(e=>{c.delete(e)}),r&&c.set(`role`,r),c.set(`addresses`,o),c.set(`secret`,n),s.hash=`${u}?${c.toString()}`,s.toString()}async function T(e,t){let{params:n}=E(new URL(e.trim()));if(t){let e=n.get(`role`)?.trim()||void 0;if(e!==t)throw new C(t,e)}let r=n.get(`addresses`)?.trim(),i=n.get(`secret`)?.trim();if(!r||!i)throw Error(`Pairing endpoint is incomplete`);return{addresses:await O(r),secret:i}}function E(e){let t=e.hash.slice(1),n=t.indexOf(`?`);return n===-1?{params:new URLSearchParams,route:t}:{params:new URLSearchParams(t.slice(n+1)),route:t.slice(0,n)}}function D(e){return l.ccc.bytesConcat(...f.encode(e.map(e=>e.bytes)))}async function O(e){try{let t=await k(l.ccc.bytesFrom(e,`base64url`),new DecompressionStream(`deflate`),x),n=[];for(let e of f.decode([t],{maxDataLength:x})){if(n.length>=16)throw Error(`Too many addresses`);n.push((0,d.multiaddr)(e.subarray()))}if(n.length===0)throw Error(`Missing addresses`);return n}catch(e){throw Error(`Pairing endpoint contains invalid compressed addresses`,{cause:e})}}async function k(e,t,n){let r=n===void 0?t.readable:t.readable.pipeThrough(A(n)),i=new Response(r).arrayBuffer().then(e=>({buffer:e,ok:!0}),e=>({error:e,ok:!1})),a=t.writable.getWriter();try{await a.write(Uint8Array.from(e)),await a.close()}catch(e){let t=await i;throw t.ok?e:t.error}let o=await i;if(!o.ok)throw o.error;return new Uint8Array(o.buffer)}function A(e){let t=0;return new TransformStream({transform(n,r){if(n.byteLength>e-t)throw Error(`Decompressed address data is too large`);t+=n.byteLength,r.enqueue(n)}})}const j=1024;var M=class{constructor(e,t){if(this.components=e,this.config=t,this.currentSecret=R(),this.errorListeners=new Set,this.pairedListeners=new Set,this.unpairedListeners=new Set,this.secretChangedListeners=new Set,this.pairingTimeouts=new Map,t.pairedPeerTimeoutMs<=0)throw Error(`Paired peer timeout must be greater than zero`)}get secret(){return this.currentSecret}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}onPaired(e){return this.pairedListeners.add(e),()=>this.pairedListeners.delete(e)}onUnpaired(e){return this.unpairedListeners.add(e),()=>this.unpairedListeners.delete(e)}onSecretChanged(e){return this.secretChangedListeners.add(e),()=>this.secretChangedListeners.delete(e)}async start(){await this.components.registrar.handle(this.config.protocol,this.handleProtocol.bind(this),{runOnLimitedConnection:!0})}async stop(){await this.components.registrar.unhandle(this.config.protocol),this.pairingTimeouts.forEach(e=>clearTimeout(e)),this.pairingTimeouts.clear()}isPaired(e){return this.pairingTimeouts.has(e.toString())}refresh(e){let t=e.toString(),n=this.pairingTimeouts.get(t);n!==void 0&&(clearTimeout(n),this.pairingTimeouts.set(t,this.createPeerTimeout(e)))}async pair(e,t={}){let n,r;try{let i=await this.openStream(e.addresses,t);if(n=i.stream,!i.connection)throw Error(`Could not determine remote peer`);this.addPairedPeer(i.connection.remotePeer)&&(r=i.connection.remotePeer);let a=await P(n,{type:`pair`,secret:e.secret,name:this.config.name},t);if(t.signal?.throwIfAborted(),n=void 0,!a.ok)throw Error(a.error);return r&&(this.rotateSecret(),this.notifyPairedPeer(r,a.name)),i.connection.remotePeer}catch(e){throw r&&this.removePairedPeer(r,!1),this.handleError(e,n)}}async unpair(e){if(!this.removePairedPeer(e))return;let t={signal:AbortSignal.timeout(1e4)},n;try{let r=await this.openStream(e,t);n=r.stream;let i=await P(r.stream,{type:`unpair`},t);if(n=void 0,!i.ok)throw Error(i.error)}catch(e){this.handleError(e,n)}}async handleProtocol(e,t){let n;try{let r=(0,u.lpStream)(e,{maxDataLength:j}),i=I((await r.read()).subarray());if(i.type===`unpair`){this.removePairedPeer(t.remotePeer),await F(r,e,{ok:!0});return}if(i.secret!==this.secret||!this.canPair(t.remotePeer)){await F(r,e,{ok:!1,error:`Pairing rejected`});return}this.rotateSecret(),this.addPairedPeer(t.remotePeer)&&(n=t.remotePeer),await F(r,e,{ok:!0,name:this.config.name}),n&&this.notifyPairedPeer(n,i.name)}catch(t){n&&this.removePairedPeer(n,!1),this.handleError(t,e)}}addPairedPeer(e){let t=e.toString(),n=this.pairingTimeouts.get(t);return n!==void 0&&clearTimeout(n),this.pairingTimeouts.set(t,this.createPeerTimeout(e)),n===void 0}notifyPairedPeer(e,t){this.pairedListeners.forEach(n=>n(e,t))}rotateSecret(){this.currentSecret=R(),this.secretChangedListeners.forEach(e=>e(this.currentSecret))}removePairedPeer(e,t=!0){let n=e.toString(),r=this.pairingTimeouts.get(n);return r!==void 0&&(clearTimeout(r),this.pairingTimeouts.delete(n),t&&this.unpairedListeners.forEach(t=>t(e)),!0)}createPeerTimeout(e){return setTimeout(()=>{this.removePairedPeer(e)},this.config.pairedPeerTimeoutMs)}async openStream(e,t){let n;return{stream:await this.components.connectionManager.openStream(e,this.config.protocol,{runOnLimitedConnection:!0,signal:t.signal,onProgress(e){e.type===`connection:opened-stream`&&(n=e.detail.connection)}}),connection:n}}handleError(e,t){let n=z(e,`Pairing operation failed`);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function N(e,t){return n=>new class extends M{canPair(e){return t.call(this,e)}}(n,e)}async function P(e,t,n){let r=(0,u.lpStream)(e,{maxDataLength:j});await r.write(l.ccc.bytesFrom(JSON.stringify(t),`utf8`),{signal:n.signal}),await e.close(n);let i=L((await r.read(n)).subarray());return await e.closeRead(n),i}async function F(e,t,n){await e.write(l.ccc.bytesFrom(JSON.stringify(n),`utf8`)),await t.close()}function I(e){let t=JSON.parse(l.ccc.bytesTo(e,`utf8`));if(t.type===`pair`&&typeof t.secret==`string`&&t.secret)return{type:`pair`,secret:t.secret,name:typeof t.name==`string`?t.name:void 0};if(t.type===`unpair`)return{type:`unpair`};throw Error(`Invalid pairing request`)}function L(e){let t=JSON.parse(l.ccc.bytesTo(e,`utf8`));if(t.ok===!0)return{ok:!0,name:typeof t.name==`string`?t.name:void 0};if(t.ok===!1&&typeof t.error==`string`)return{ok:!1,error:t.error};throw Error(`Invalid pairing response`)}function R(){return l.ccc.bytesTo(crypto.getRandomValues(new Uint8Array(16)),`base64url`)}function z(e,t){return e instanceof Error?e:Error(t)}var B=o({JsonRpcService:()=>p,JsonRpcTransportLibp2p:()=>y,PairingEndpointRoleError:()=>C,PairingService:()=>M,decodePairingEndpoint:()=>T,encodePairingEndpoint:()=>w,jsonRpcService:()=>v,pairingService:()=>N});exports.JsonRpcService=p,exports.JsonRpcTransportLibp2p=y,exports.PairingEndpointRoleError=C,exports.PairingService=M,exports.decodePairingEndpoint=T,exports.encodePairingEndpoint=w,exports.jsonRpcService=v,exports.pairingService=N,Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return B}});
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,o)=>(o=n==null?{}:e(i(n)),s(r||!n||!n.__esModule||!a.call(n,`default`)?t(o,`default`,{value:n,enumerable:!0}):o,n));let l=require("@ckb-ccc/core"),u=require("@libp2p/utils"),d=require("@multiformats/multiaddr"),f=require("it-length-prefixed");f=c(f);async function p(e,t,n){let r=(await e.peerStore.get(t)).addresses.flatMap(({multiaddr:e})=>{let n=m(e,t);return n?[n]:[]});if(r.length!==0)return n?e.dial(r,{signal:n}):e.dial(r)}function m(e,t){let n=e.getComponents().at(-1);return n?.name===`p2p`?n.value===t.toString()?e:void 0:e.encapsulate(`/p2p/${t.toString()}`)}var h=class{constructor(e,t){this.components=e,this.config=t,this.errorListeners=new Set;let n=t.maxMessageLength??1048576;if(!Number.isSafeInteger(n)||n<=0)throw Error(`Maximum JSON-RPC message length must be a positive integer`);this.maxMessageLength=n}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}async start(){await this.components.registrar.handle(this.config.protocol,this.handleProtocol.bind(this),{runOnLimitedConnection:!0})}async stop(){await this.components.registrar.unhandle(this.config.protocol)}async handleProtocol(e,t){let n=!1;try{let r=(0,u.lpStream)(e,{maxDataLength:this.maxMessageLength}),i=_(l.ccc.bytesTo((await r.read()).subarray(),`utf8`)),a;try{a={jsonrpc:`2.0`,id:i.id,result:await this.handleRequest({connection:t,peerId:t.remotePeer,payload:i})}}catch(e){n=y(e);let t=g(e);a={jsonrpc:`2.0`,id:i.id,error:{code:t.code,message:t.message,data:t.data}}}await r.write(l.ccc.bytesFrom(JSON.stringify(a),`utf8`)),await e.close()}catch(t){if(n)return;this.handleError(t,e)}}handleError(e,t){let n=v(e);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function g(e){return e instanceof l.JsonRpcError?e:new l.JsonRpcError({code:-32603,message:v(e).message})}function _(e){let t=JSON.parse(e);if(t?.jsonrpc!==`2.0`||typeof t.id!=`number`&&typeof t.id!=`string`||typeof t.method!=`string`)throw Error(`Invalid JSON-RPC request`);return t}function v(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}function y(e){return e instanceof Error&&(e.name===`AbortError`||e.name===`TimeoutError`)}function b(e,t){return n=>new class extends h{handleRequest(e){return t.call(this,e)}}(n,e)}var x=class{constructor(e,t,n){this.node=e,this.peerId=t,this.config=n;let r=n.maxMessageLength??1048576;if(!Number.isSafeInteger(r)||r<=0)throw Error(`Maximum JSON-RPC message length must be a positive integer`);this.maxMessageLength=r;let i=n.timeout??12e4;if(!Number.isSafeInteger(i)||i<=0)throw Error(`JSON-RPC request timeout must be a positive integer`);this.timeout=i}async request(e){let t=AbortSignal.timeout(this.timeout),n=this.config.signal?l.ccc.abortSignalAny([this.config.signal,t]):t,r,i;try{r=await this.openStream(n);let t=(0,u.lpStream)(r,{maxDataLength:this.maxMessageLength});await t.write(l.ccc.bytesFrom(JSON.stringify(e),`utf8`),{signal:n}),await r.close({signal:n}),i=JSON.parse(l.ccc.bytesTo((await t.read({signal:n})).subarray(),`utf8`)),await r.closeRead({signal:n}),n.throwIfAborted()}catch(e){let t=C(e);throw r?.abort(t),t}return this.config.onResponse?.(i),i}async openStream(e){p(this.node,this.peerId).catch(()=>{});for(let t of this.connections())try{return await t.newStream(this.config.protocol,{runOnLimitedConnection:!0,signal:e})}catch{e.throwIfAborted()}return this.node.dialProtocol(this.peerId,this.config.protocol,{runOnLimitedConnection:!0,signal:e})}connections(){return this.node.getConnections(this.peerId).filter(({status:e})=>e===`open`).sort(S)}};function S(e,t){return e.direct===t.direct?e.rtt===void 0?t.rtt===void 0?0:1:t.rtt===void 0?-1:e.rtt-t.rtt:e.direct?-1:1}function C(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}const w=16384,T=[`addresses`,`role`,`secret`];var E=class extends Error{constructor(e,t){super(`Expected a ${e} pairing endpoint, received ${t?`role "${t}"`:`an endpoint without a role`}`),this.expectedRole=e,this.actualRole=t,this.name=`PairingEndpointRoleError`}};async function D(e,t,n,r){if(t.length===0)throw Error(`Pairing endpoint requires at least one address`);if(t.length>16)throw Error(`Pairing endpoint supports at most 16 addresses`);let i=A(t);if(i.byteLength>w)throw Error(`Pairing endpoint address data is too large`);let a=await M(i,new CompressionStream(`deflate`)),o=l.ccc.bytesTo(a,`base64url`),s=new URL(e),{params:c,route:u}=k(s);return T.forEach(e=>{c.delete(e)}),r&&c.set(`role`,r),c.set(`addresses`,o),c.set(`secret`,n),s.hash=`${u}?${c.toString()}`,s.toString()}async function O(e,t){let{params:n}=k(new URL(e.trim()));if(t){let e=n.get(`role`)?.trim()||void 0;if(e!==t)throw new E(t,e)}let r=n.get(`addresses`)?.trim(),i=n.get(`secret`)?.trim();if(!r||!i)throw Error(`Pairing endpoint is incomplete`);return{addresses:await j(r),secret:i}}function k(e){let t=e.hash.slice(1),n=t.indexOf(`?`);return n===-1?{params:new URLSearchParams,route:t}:{params:new URLSearchParams(t.slice(n+1)),route:t.slice(0,n)}}function A(e){return l.ccc.bytesConcat(...f.encode(e.map(e=>e.bytes)))}async function j(e){try{let t=await M(l.ccc.bytesFrom(e,`base64url`),new DecompressionStream(`deflate`),w),n=[];for(let e of f.decode([t],{maxDataLength:w})){if(n.length>=16)throw Error(`Too many addresses`);n.push((0,d.multiaddr)(e.subarray()))}if(n.length===0)throw Error(`Missing addresses`);return n}catch(e){throw Error(`Pairing endpoint contains invalid compressed addresses`,{cause:e})}}async function M(e,t,n){let r=n===void 0?t.readable:t.readable.pipeThrough(N(n)),i=new Response(r).arrayBuffer().then(e=>({buffer:e,ok:!0}),e=>({error:e,ok:!1})),a=t.writable.getWriter();try{await a.write(Uint8Array.from(e)),await a.close()}catch(e){let t=await i;throw t.ok?e:t.error}let o=await i;if(!o.ok)throw o.error;return new Uint8Array(o.buffer)}function N(e){let t=0;return new TransformStream({transform(n,r){if(n.byteLength>e-t)throw Error(`Decompressed address data is too large`);t+=n.byteLength,r.enqueue(n)}})}const P=1024;var F=class{constructor(e,t){if(this.components=e,this.config=t,this.currentSecret=G(),this.errorListeners=new Set,this.pairedListeners=new Set,this.unpairedListeners=new Set,this.secretChangedListeners=new Set,this.pairingTimeouts=new Map,t.pairedPeerTimeoutMs<=0)throw Error(`Paired peer timeout must be greater than zero`)}get secret(){return this.currentSecret}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}onPaired(e){return this.pairedListeners.add(e),()=>this.pairedListeners.delete(e)}onUnpaired(e){return this.unpairedListeners.add(e),()=>this.unpairedListeners.delete(e)}onSecretChanged(e){return this.secretChangedListeners.add(e),()=>this.secretChangedListeners.delete(e)}async start(){await this.components.registrar.handle(this.config.protocol,this.handleProtocol.bind(this),{runOnLimitedConnection:!0})}async stop(){await this.components.registrar.unhandle(this.config.protocol),this.pairingTimeouts.forEach(e=>clearTimeout(e)),this.pairingTimeouts.clear()}isPaired(e){return this.pairingTimeouts.has(e.toString())}refresh(e){let t=e.toString(),n=this.pairingTimeouts.get(t);n!==void 0&&(clearTimeout(n),this.pairingTimeouts.set(t,this.createPeerTimeout(e)))}async pair(e,t={}){let n,r;try{let i=await this.openStream(e.addresses,t);if(n=i.stream,!i.connection)throw Error(`Could not determine remote peer`);this.addPairedPeer(i.connection.remotePeer)&&(r=i.connection.remotePeer);let a=await L(n,R(e.secret,this.config.name,this.addresses()),t);if(t.signal?.throwIfAborted(),n=void 0,!a.ok)throw Error(a.error);return r&&(this.rotateSecret(),this.notifyPairedPeer(r,a.name)),i.connection.remotePeer}catch(e){throw r&&this.removePairedPeer(r,!1),this.handleError(e,n)}}async unpair(e){if(!this.removePairedPeer(e))return;let t={signal:AbortSignal.timeout(1e4)},n;try{let r=await this.openStream(e,t);n=r.stream;let i=await L(r.stream,{type:`unpair`},t);if(n=void 0,!i.ok)throw Error(i.error)}catch(e){this.handleError(e,n)}}async handleProtocol(e,t){let n;try{let r=(0,u.lpStream)(e,{maxDataLength:P}),i=V((await r.read()).subarray());if(i.type===`unpair`){this.removePairedPeer(t.remotePeer),await B(r,e,{ok:!0});return}if(i.secret!==this.secret||!this.canPair(t.remotePeer)){await B(r,e,{ok:!1,error:`Pairing rejected`});return}let a=U(i.addresses,t.remotePeer);this.rotateSecret(),this.addPairedPeer(t.remotePeer)&&(n=t.remotePeer),await B(r,e,{ok:!0,name:this.config.name}),n&&(this.notifyPairedPeer(n,i.name),this.openFallbackConnection(a))}catch(t){n&&this.removePairedPeer(n,!1),this.handleError(t,e)}}addPairedPeer(e){let t=e.toString(),n=this.pairingTimeouts.get(t);return n!==void 0&&clearTimeout(n),this.pairingTimeouts.set(t,this.createPeerTimeout(e)),n===void 0}notifyPairedPeer(e,t){this.pairedListeners.forEach(n=>n(e,t))}addresses(){return this.components.addressManager?.getAddresses()??[]}async openFallbackConnection(e){if(e.length!==0)try{await this.components.connectionManager.openConnection(e)}catch{}}rotateSecret(){this.currentSecret=G(),this.secretChangedListeners.forEach(e=>e(this.currentSecret))}removePairedPeer(e,t=!0){let n=e.toString(),r=this.pairingTimeouts.get(n);return r!==void 0&&(clearTimeout(r),this.pairingTimeouts.delete(n),t&&this.unpairedListeners.forEach(t=>t(e)),!0)}createPeerTimeout(e){return setTimeout(()=>{if(this.components.connectionManager.getConnections(e).some(({status:e})=>e===`open`)){this.pairingTimeouts.set(e.toString(),this.createPeerTimeout(e));return}this.removePairedPeer(e)},this.config.pairedPeerTimeoutMs)}async openStream(e,t){let n;return{stream:await this.components.connectionManager.openStream(e,this.config.protocol,{runOnLimitedConnection:!0,signal:t.signal,onProgress(e){e.type===`connection:opened-stream`&&(n=e.detail.connection)}}),connection:n}}handleError(e,t){let n=K(e,`Pairing operation failed`);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function I(e,t){return n=>new class extends F{canPair(e){return t.call(this,e)}}(n,e)}async function L(e,t,n){let r=(0,u.lpStream)(e,{maxDataLength:P});await r.write(l.ccc.bytesFrom(JSON.stringify(t),`utf8`),{signal:n.signal}),await e.close(n);let i=W((await r.read(n)).subarray());return await e.closeRead(n),i}function R(e,t,n){let r=n.map(e=>e.toString()),i={type:`pair`,secret:e,name:t,addresses:r};for(;r.length>0&&!z(i);)r.pop();if(z(i)||delete i.addresses,z(i)||delete i.name,!z(i))throw Error(`Pairing request is too large`);return i}function z(e){return l.ccc.bytesFrom(JSON.stringify(e),`utf8`).byteLength<=P}async function B(e,t,n){await e.write(l.ccc.bytesFrom(JSON.stringify(n),`utf8`)),await t.close()}function V(e){let t=JSON.parse(l.ccc.bytesTo(e,`utf8`));if(t.type===`pair`&&typeof t.secret==`string`&&t.secret)return{type:`pair`,secret:t.secret,name:typeof t.name==`string`?t.name:void 0,addresses:H(t.addresses)};if(t.type===`unpair`)return{type:`unpair`};throw Error(`Invalid pairing request`)}function H(e){if(Array.isArray(e))return e.filter(e=>typeof e==`string`&&e.length>0)}function U(e,t){return(e??[]).flatMap(e=>{try{let n=(0,d.multiaddr)(e);return n.getComponents().filter(({code:e})=>e===d.CODE_P2P).at(-1)?.value===t.toString()?[n]:[]}catch{return[]}})}function W(e){let t=JSON.parse(l.ccc.bytesTo(e,`utf8`));if(t.ok===!0)return{ok:!0,name:typeof t.name==`string`?t.name:void 0};if(t.ok===!1&&typeof t.error==`string`)return{ok:!1,error:t.error};throw Error(`Invalid pairing response`)}function G(){return l.ccc.bytesTo(crypto.getRandomValues(new Uint8Array(16)),`base64url`)}function K(e,t){return e instanceof Error?e:Error(t)}var q=o({JsonRpcService:()=>h,JsonRpcTransportLibp2p:()=>x,PairingEndpointRoleError:()=>E,PairingService:()=>F,decodePairingEndpoint:()=>O,dialKnownAddresses:()=>p,encodePairingEndpoint:()=>D,jsonRpcService:()=>b,pairingService:()=>I});exports.JsonRpcService=h,exports.JsonRpcTransportLibp2p=x,exports.PairingEndpointRoleError=E,exports.PairingService=F,exports.decodePairingEndpoint=O,exports.dialKnownAddresses=p,exports.encodePairingEndpoint=D,exports.jsonRpcService=b,exports.pairingService=I,Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return q}});
2
2
  //# sourceMappingURL=barrel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"barrel.js","names":["DEFAULT_MAX_MESSAGE_LENGTH","lpStream","ccc","asJsonRpcError","JsonRpcError","ccc","lpStream","ccc","lp","multiaddr","lpStream","ccc"],"sources":["../src/jsonRpcService.ts","../src/jsonRpcTransport.ts","../src/pairingEndpoint.ts","../src/pairingService.ts","../src/barrel.ts"],"sourcesContent":["import { ccc, JsonRpcError } from \"@ckb-ccc/core\";\nimport type { Connection, PeerId, Stream } from \"@libp2p/interface\";\nimport type { Registrar } from \"@libp2p/interface-internal\";\nimport { lpStream } from \"@libp2p/utils\";\n\nconst DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;\n\nexport type JsonRpcServiceComponents = {\n registrar: Registrar;\n};\n\nexport type JsonRpcServiceConfig = {\n protocol: string;\n maxMessageLength?: number;\n};\n\nexport type JsonRpcRequest = {\n peerId: PeerId;\n payload: ccc.JsonRpcPayload;\n};\n\nexport type JsonRpcRequestHandler<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n> = (this: JsonRpcService<Components>, request: JsonRpcRequest) => unknown;\n\nexport abstract class JsonRpcService<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n> {\n private readonly errorListeners = new Set<(error: Error) => void>();\n readonly maxMessageLength: number;\n\n constructor(\n readonly components: Components,\n readonly config: JsonRpcServiceConfig,\n ) {\n const maxMessageLength =\n config.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH;\n if (!Number.isSafeInteger(maxMessageLength) || maxMessageLength <= 0) {\n throw new Error(\n \"Maximum JSON-RPC message length must be a positive integer\",\n );\n }\n\n this.maxMessageLength = maxMessageLength;\n }\n\n protected abstract handleRequest(request: JsonRpcRequest): unknown;\n\n onError(listener: (error: Error) => void) {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n async start() {\n await this.components.registrar.handle(\n this.config.protocol,\n this.handleProtocol.bind(this),\n { runOnLimitedConnection: true },\n );\n }\n\n async stop() {\n await this.components.registrar.unhandle(this.config.protocol);\n }\n\n private async handleProtocol(stream: Stream, connection: Connection) {\n let requestAborted = false;\n\n try {\n const rpcStream = lpStream(stream, {\n maxDataLength: this.maxMessageLength,\n });\n const payload = parseJsonRpcRequest(\n ccc.bytesTo((await rpcStream.read()).subarray(), \"utf8\"),\n );\n\n let response: ccc.JsonRpcResponse;\n try {\n response = {\n jsonrpc: \"2.0\",\n id: payload.id,\n result: await this.handleRequest({\n peerId: connection.remotePeer,\n payload,\n }),\n };\n } catch (cause) {\n requestAborted = isAbortError(cause);\n const error = toJsonRpcError(cause);\n response = {\n jsonrpc: \"2.0\",\n id: payload.id,\n error: {\n code: error.code,\n message: error.message,\n data: error.data,\n },\n };\n }\n\n await rpcStream.write(ccc.bytesFrom(JSON.stringify(response), \"utf8\"));\n await stream.close();\n } catch (cause) {\n if (requestAborted) {\n return;\n }\n this.handleError(cause, stream);\n }\n }\n\n private handleError(cause: unknown, stream?: Stream) {\n const error = asJsonRpcError(cause);\n\n stream?.abort(error);\n this.errorListeners.forEach((listener) => listener(error));\n return error;\n }\n}\n\nfunction toJsonRpcError(cause: unknown) {\n if (cause instanceof JsonRpcError) {\n return cause;\n }\n\n return new JsonRpcError({\n code: -32603,\n message: asJsonRpcError(cause).message,\n });\n}\n\nfunction parseJsonRpcRequest(data: string) {\n const payload = JSON.parse(data) as Partial<ccc.JsonRpcPayload>;\n if (\n payload?.jsonrpc !== \"2.0\" ||\n (typeof payload.id !== \"number\" && typeof payload.id !== \"string\") ||\n typeof payload.method !== \"string\"\n ) {\n throw new Error(\"Invalid JSON-RPC request\");\n }\n\n return payload as ccc.JsonRpcPayload;\n}\n\nfunction asJsonRpcError(cause: unknown) {\n return cause instanceof Error ? cause : new Error(\"JSON-RPC request failed\");\n}\n\nfunction isAbortError(cause: unknown) {\n return (\n cause instanceof Error &&\n (cause.name === \"AbortError\" || cause.name === \"TimeoutError\")\n );\n}\n\nexport function jsonRpcService<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n>(\n config: JsonRpcServiceConfig,\n handler: JsonRpcRequestHandler<Components>,\n): (components: Components) => JsonRpcService<Components> {\n return (components: Components) =>\n new (class extends JsonRpcService<Components> {\n protected handleRequest(request: JsonRpcRequest) {\n return handler.call(this, request);\n }\n })(components, config);\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport type { Libp2p, PeerId, Stream } from \"@libp2p/interface\";\nimport { lpStream } from \"@libp2p/utils\";\n\nconst DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;\nconst DEFAULT_TIMEOUT = 120_000;\n\nexport type JsonRpcTransportLibp2pConfig = {\n protocol: string;\n maxMessageLength?: number;\n timeout?: number;\n signal?: AbortSignal;\n onResponse?: (response: ccc.JsonRpcResponse) => void;\n};\n\nexport class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {\n private readonly maxMessageLength: number;\n private readonly timeout: number;\n\n constructor(\n private readonly node: Libp2p,\n readonly peerId: PeerId,\n private readonly config: JsonRpcTransportLibp2pConfig,\n ) {\n const maxMessageLength =\n config.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH;\n if (!Number.isSafeInteger(maxMessageLength) || maxMessageLength <= 0) {\n throw new Error(\n \"Maximum JSON-RPC message length must be a positive integer\",\n );\n }\n\n this.maxMessageLength = maxMessageLength;\n\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n if (!Number.isSafeInteger(timeout) || timeout <= 0) {\n throw new Error(\"JSON-RPC request timeout must be a positive integer\");\n }\n\n this.timeout = timeout;\n }\n\n async request(payload: ccc.JsonRpcPayload): Promise<ccc.JsonRpcResponse> {\n const timeoutSignal = AbortSignal.timeout(this.timeout);\n const signal = this.config.signal\n ? ccc.abortSignalAny([this.config.signal, timeoutSignal])\n : timeoutSignal;\n\n let stream: Stream | undefined;\n let response: ccc.JsonRpcResponse;\n try {\n // dialProtocol handles the signal, so no separate entry check is needed.\n stream = await this.node.dialProtocol(this.peerId, this.config.protocol, {\n runOnLimitedConnection: true,\n signal,\n });\n\n const rpcStream = lpStream(stream, {\n maxDataLength: this.maxMessageLength,\n });\n\n await rpcStream.write(ccc.bytesFrom(JSON.stringify(payload), \"utf8\"), {\n signal,\n });\n await stream.close({ signal });\n\n response = JSON.parse(\n ccc.bytesTo((await rpcStream.read({ signal })).subarray(), \"utf8\"),\n ) as ccc.JsonRpcResponse;\n await stream.closeRead({ signal });\n signal.throwIfAborted();\n } catch (cause) {\n const error = asJsonRpcError(cause);\n stream?.abort(error);\n throw error;\n }\n\n this.config.onResponse?.(response);\n return response;\n }\n}\n\nfunction asJsonRpcError(cause: unknown) {\n return cause instanceof Error ? cause : new Error(\"JSON-RPC request failed\");\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport { multiaddr, type Multiaddr } from \"@multiformats/multiaddr\";\nimport * as lp from \"it-length-prefixed\";\nimport type { PairingTarget } from \"./pairingService.js\";\n\nconst MAX_DECOMPRESSED_ADDRESSES_LENGTH = 16 * 1024;\nconst MAX_ADDRESSES = 16;\nconst PAIRING_PARAMETER_NAMES = [\"addresses\", \"role\", \"secret\"] as const;\n\nexport class PairingEndpointRoleError extends Error {\n constructor(\n readonly expectedRole: string,\n readonly actualRole: string | undefined,\n ) {\n super(\n `Expected a ${expectedRole} pairing endpoint, received ${actualRole ? `role \"${actualRole}\"` : \"an endpoint without a role\"}`,\n );\n this.name = \"PairingEndpointRoleError\";\n }\n}\n\nexport async function encodePairingEndpoint(\n endpointUrl: string,\n addresses: readonly Multiaddr[],\n secret: string,\n role?: string,\n) {\n if (addresses.length === 0) {\n throw new Error(\"Pairing endpoint requires at least one address\");\n }\n if (addresses.length > MAX_ADDRESSES) {\n throw new Error(\n `Pairing endpoint supports at most ${MAX_ADDRESSES} addresses`,\n );\n }\n\n const addressBytes = encodeAddresses(addresses);\n if (addressBytes.byteLength > MAX_DECOMPRESSED_ADDRESSES_LENGTH) {\n throw new Error(\"Pairing endpoint address data is too large\");\n }\n\n const compressedAddressBytes = await transformBytes(\n addressBytes,\n new CompressionStream(\"deflate\"),\n );\n\n const encodedAddresses = ccc.bytesTo(compressedAddressBytes, \"base64url\");\n\n const url = new URL(endpointUrl);\n const { params, route } = fragmentParameters(url);\n\n PAIRING_PARAMETER_NAMES.forEach((name) => {\n params.delete(name);\n });\n if (role) {\n params.set(\"role\", role);\n }\n params.set(\"addresses\", encodedAddresses);\n params.set(\"secret\", secret);\n url.hash = `${route}?${params.toString()}`;\n\n return url.toString();\n}\n\nexport async function decodePairingEndpoint(\n endpoint: string,\n expectedRole?: string,\n): Promise<PairingTarget> {\n const url = new URL(endpoint.trim());\n const { params } = fragmentParameters(url);\n if (expectedRole) {\n const actualRole = params.get(\"role\")?.trim() || undefined;\n if (actualRole !== expectedRole) {\n throw new PairingEndpointRoleError(expectedRole, actualRole);\n }\n }\n const compressedAddresses = params.get(\"addresses\")?.trim();\n const secret = params.get(\"secret\")?.trim();\n\n if (!compressedAddresses || !secret) {\n throw new Error(\"Pairing endpoint is incomplete\");\n }\n\n const addresses = await decodeCompressedAddresses(compressedAddresses);\n\n return { addresses, secret };\n}\n\nfunction fragmentParameters(url: URL) {\n const fragment = url.hash.slice(1);\n const separator = fragment.indexOf(\"?\");\n if (separator === -1) {\n return { params: new URLSearchParams(), route: fragment };\n }\n\n return {\n params: new URLSearchParams(fragment.slice(separator + 1)),\n route: fragment.slice(0, separator),\n };\n}\n\nfunction encodeAddresses(addresses: readonly Multiaddr[]): Uint8Array {\n return ccc.bytesConcat(\n ...lp.encode(addresses.map((address) => address.bytes)),\n );\n}\n\nasync function decodeCompressedAddresses(value: string): Promise<Multiaddr[]> {\n try {\n const compressedBytes = ccc.bytesFrom(value, \"base64url\");\n const bytes = await transformBytes(\n compressedBytes,\n new DecompressionStream(\"deflate\"),\n MAX_DECOMPRESSED_ADDRESSES_LENGTH,\n );\n const addresses: Multiaddr[] = [];\n for (const address of lp.decode([bytes], {\n maxDataLength: MAX_DECOMPRESSED_ADDRESSES_LENGTH,\n })) {\n if (addresses.length >= MAX_ADDRESSES) {\n throw new Error(\"Too many addresses\");\n }\n addresses.push(multiaddr(address.subarray()));\n }\n\n if (addresses.length === 0) {\n throw new Error(\"Missing addresses\");\n }\n return addresses;\n } catch (cause) {\n throw new Error(\"Pairing endpoint contains invalid compressed addresses\", {\n cause,\n });\n }\n}\n\nasync function transformBytes(\n bytes: Uint8Array,\n transform: CompressionStream | DecompressionStream,\n maxOutputLength?: number,\n) {\n const readable =\n maxOutputLength === undefined\n ? transform.readable\n : transform.readable.pipeThrough(limitBytes(maxOutputLength));\n const output = new Response(readable).arrayBuffer().then(\n (buffer) => ({ buffer, ok: true }) as const,\n (error: unknown) => ({ error, ok: false }) as const,\n );\n const writer = transform.writable.getWriter();\n try {\n await writer.write(Uint8Array.from(bytes));\n await writer.close();\n } catch (cause) {\n const result = await output;\n if (!result.ok) {\n throw result.error;\n }\n throw cause;\n }\n\n const result = await output;\n if (!result.ok) {\n throw result.error;\n }\n return new Uint8Array(result.buffer);\n}\n\nfunction limitBytes(maxLength: number) {\n let length = 0;\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n if (chunk.byteLength > maxLength - length) {\n throw new Error(\"Decompressed address data is too large\");\n }\n length += chunk.byteLength;\n controller.enqueue(chunk);\n },\n });\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport type {\n AbortOptions,\n Connection,\n PeerId,\n Stream,\n} from \"@libp2p/interface\";\nimport type { ConnectionManager, Registrar } from \"@libp2p/interface-internal\";\nimport { lpStream } from \"@libp2p/utils\";\nimport type { Multiaddr } from \"@multiformats/multiaddr\";\n\nconst MAX_PAIRING_MESSAGE_LENGTH = 1024;\nconst UNPAIR_TIMEOUT_MS = 10_000;\n\nexport type PairingServiceComponents = {\n connectionManager: ConnectionManager;\n registrar: Registrar;\n};\n\nexport type PairingServiceConfig = {\n /** Optional self-reported label shared with the paired peer. */\n name?: string;\n protocol: string;\n pairedPeerTimeoutMs: number;\n};\n\nexport type PairingTarget = {\n addresses: Multiaddr[];\n secret: string;\n};\n\nexport type PairingGuard<\n Components extends PairingServiceComponents = PairingServiceComponents,\n> = (this: PairingService<Components>, peerId: PeerId) => boolean;\n\ntype PairingRequest =\n { type: \"pair\"; secret: string; name?: string } | { type: \"unpair\" };\n\ntype PairingResponse =\n { ok: true; name?: string } | { ok: false; error: string };\n\nexport abstract class PairingService<\n Components extends PairingServiceComponents = PairingServiceComponents,\n> {\n private currentSecret = createSecret();\n private readonly errorListeners = new Set<(error: Error) => void>();\n private readonly pairedListeners = new Set<\n (peerId: PeerId, name?: string) => void\n >();\n private readonly unpairedListeners = new Set<(peerId: PeerId) => void>();\n private readonly secretChangedListeners = new Set<(secret: string) => void>();\n private readonly pairingTimeouts = new Map<\n string,\n ReturnType<typeof setTimeout>\n >();\n\n constructor(\n readonly components: Components,\n readonly config: PairingServiceConfig,\n ) {\n if (config.pairedPeerTimeoutMs <= 0) {\n throw new Error(\"Paired peer timeout must be greater than zero\");\n }\n }\n\n protected abstract canPair(peerId: PeerId): boolean;\n\n /** The credential to include in the currently issued pairing endpoint. */\n get secret() {\n return this.currentSecret;\n }\n\n onError(listener: (error: Error) => void) {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n onPaired(listener: (peerId: PeerId, name?: string) => void) {\n this.pairedListeners.add(listener);\n return () => this.pairedListeners.delete(listener);\n }\n\n onUnpaired(listener: (peerId: PeerId) => void) {\n this.unpairedListeners.add(listener);\n return () => this.unpairedListeners.delete(listener);\n }\n\n onSecretChanged(listener: (secret: string) => void) {\n this.secretChangedListeners.add(listener);\n return () => this.secretChangedListeners.delete(listener);\n }\n\n async start() {\n await this.components.registrar.handle(\n this.config.protocol,\n this.handleProtocol.bind(this),\n { runOnLimitedConnection: true },\n );\n }\n\n async stop() {\n await this.components.registrar.unhandle(this.config.protocol);\n\n this.pairingTimeouts.forEach((timeout) => clearTimeout(timeout));\n this.pairingTimeouts.clear();\n }\n\n isPaired(peerId: PeerId) {\n return this.pairingTimeouts.has(peerId.toString());\n }\n\n refresh(peerId: PeerId) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout === undefined) {\n return;\n }\n\n clearTimeout(timeout);\n this.pairingTimeouts.set(key, this.createPeerTimeout(peerId));\n }\n\n async pair(\n target: PairingTarget,\n options: AbortOptions = {},\n ): Promise<PeerId> {\n let stream: Stream | undefined;\n let addedPeer: PeerId | undefined;\n\n try {\n const opened = await this.openStream(target.addresses, options);\n stream = opened.stream;\n\n if (!opened.connection) {\n throw new Error(\"Could not determine remote peer\");\n }\n\n if (this.addPairedPeer(opened.connection.remotePeer)) {\n addedPeer = opened.connection.remotePeer;\n }\n\n const response = await exchange(\n stream,\n {\n type: \"pair\",\n secret: target.secret,\n name: this.config.name,\n },\n options,\n );\n options.signal?.throwIfAborted();\n stream = undefined;\n\n if (!response.ok) {\n throw new Error(response.error);\n }\n\n if (addedPeer) {\n this.rotateSecret();\n this.notifyPairedPeer(addedPeer, response.name);\n }\n return opened.connection.remotePeer;\n } catch (cause) {\n if (addedPeer) {\n this.removePairedPeer(addedPeer, false);\n }\n throw this.handleError(cause, stream);\n }\n }\n\n async unpair(peerId: PeerId) {\n if (!this.removePairedPeer(peerId)) {\n return;\n }\n\n const options = { signal: AbortSignal.timeout(UNPAIR_TIMEOUT_MS) };\n\n let stream: Stream | undefined;\n try {\n const opened = await this.openStream(peerId, options);\n stream = opened.stream;\n const response = await exchange(\n opened.stream,\n { type: \"unpair\" },\n options,\n );\n stream = undefined;\n if (!response.ok) {\n throw new Error(response.error);\n }\n } catch (cause) {\n this.handleError(cause, stream);\n }\n }\n\n private async handleProtocol(stream: Stream, connection: Connection) {\n let addedPeer: PeerId | undefined;\n\n try {\n const pairingStream = lpStream(stream, {\n maxDataLength: MAX_PAIRING_MESSAGE_LENGTH,\n });\n const request = parsePairingRequest(\n (await pairingStream.read()).subarray(),\n );\n\n if (request.type === \"unpair\") {\n this.removePairedPeer(connection.remotePeer);\n await writeResponse(pairingStream, stream, { ok: true });\n return;\n }\n\n if (\n request.secret !== this.secret ||\n !this.canPair(connection.remotePeer)\n ) {\n await writeResponse(pairingStream, stream, {\n ok: false,\n error: \"Pairing rejected\",\n });\n return;\n }\n\n // Consume the credential before the next asynchronous boundary so two\n // concurrent requests cannot both pair with the same endpoint.\n this.rotateSecret();\n\n if (this.addPairedPeer(connection.remotePeer)) {\n addedPeer = connection.remotePeer;\n }\n await writeResponse(pairingStream, stream, {\n ok: true,\n name: this.config.name,\n });\n if (addedPeer) {\n this.notifyPairedPeer(addedPeer, request.name);\n }\n } catch (cause) {\n if (addedPeer) {\n this.removePairedPeer(addedPeer, false);\n }\n this.handleError(cause, stream);\n }\n }\n\n private addPairedPeer(peerId: PeerId) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout !== undefined) {\n clearTimeout(timeout);\n }\n\n this.pairingTimeouts.set(key, this.createPeerTimeout(peerId));\n return timeout === undefined;\n }\n\n private notifyPairedPeer(peerId: PeerId, name?: string) {\n this.pairedListeners.forEach((listener) => listener(peerId, name));\n }\n\n private rotateSecret() {\n this.currentSecret = createSecret();\n this.secretChangedListeners.forEach((listener) =>\n listener(this.currentSecret),\n );\n }\n\n private removePairedPeer(peerId: PeerId, notify = true) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout === undefined) {\n return false;\n }\n\n clearTimeout(timeout);\n this.pairingTimeouts.delete(key);\n\n if (notify) {\n this.unpairedListeners.forEach((listener) => listener(peerId));\n }\n return true;\n }\n\n private createPeerTimeout(peerId: PeerId) {\n return setTimeout(() => {\n this.removePairedPeer(peerId);\n }, this.config.pairedPeerTimeoutMs);\n }\n\n private async openStream(\n target: PeerId | Multiaddr | Multiaddr[],\n options: AbortOptions,\n ) {\n let connection: Connection | undefined;\n const stream = await this.components.connectionManager.openStream(\n target,\n this.config.protocol,\n {\n runOnLimitedConnection: true,\n signal: options.signal,\n onProgress(event) {\n if (event.type === \"connection:opened-stream\") {\n connection = event.detail.connection;\n }\n },\n },\n );\n\n return { stream, connection };\n }\n\n private handleError(cause: unknown, stream?: Stream) {\n const error = asError(cause, \"Pairing operation failed\");\n\n stream?.abort(error);\n this.errorListeners.forEach((listener) => listener(error));\n return error;\n }\n}\n\nexport function pairingService<\n Components extends PairingServiceComponents = PairingServiceComponents,\n>(\n config: PairingServiceConfig,\n canPair: PairingGuard<Components>,\n): (components: Components) => PairingService<Components> {\n return (components: Components) =>\n new (class extends PairingService<Components> {\n protected canPair(peerId: PeerId) {\n return canPair.call(this, peerId);\n }\n })(components, config);\n}\n\nasync function exchange(\n stream: Stream,\n request: PairingRequest,\n options: AbortOptions,\n) {\n const pairingStream = lpStream(stream, {\n maxDataLength: MAX_PAIRING_MESSAGE_LENGTH,\n });\n\n await pairingStream.write(ccc.bytesFrom(JSON.stringify(request), \"utf8\"), {\n signal: options.signal,\n });\n await stream.close(options);\n\n const response = parsePairingResponse(\n (await pairingStream.read(options)).subarray(),\n );\n await stream.closeRead(options);\n return response;\n}\n\nasync function writeResponse(\n pairingStream: ReturnType<typeof lpStream>,\n stream: Stream,\n response: PairingResponse,\n) {\n await pairingStream.write(ccc.bytesFrom(JSON.stringify(response), \"utf8\"));\n await stream.close();\n}\n\nfunction parsePairingRequest(data: Uint8Array): PairingRequest {\n const request = JSON.parse(\n ccc.bytesTo(data, \"utf8\"),\n ) as Partial<PairingRequest>;\n if (\n request.type === \"pair\" &&\n typeof request.secret === \"string\" &&\n request.secret\n ) {\n return {\n type: \"pair\",\n secret: request.secret,\n name: typeof request.name === \"string\" ? request.name : undefined,\n };\n }\n\n if (request.type === \"unpair\") {\n return { type: \"unpair\" };\n }\n\n throw new Error(\"Invalid pairing request\");\n}\n\nfunction parsePairingResponse(data: Uint8Array): PairingResponse {\n const response = JSON.parse(\n ccc.bytesTo(data, \"utf8\"),\n ) as Partial<PairingResponse>;\n if (response.ok === true) {\n return {\n ok: true,\n name: typeof response.name === \"string\" ? response.name : undefined,\n };\n }\n if (response.ok === false && typeof response.error === \"string\") {\n return { ok: false, error: response.error };\n }\n\n throw new Error(\"Invalid pairing response\");\n}\n\nfunction createSecret() {\n return ccc.bytesTo(crypto.getRandomValues(new Uint8Array(16)), \"base64url\");\n}\n\nfunction asError(cause: unknown, fallback: string) {\n return cause instanceof Error ? cause : new Error(fallback);\n}\n","export * from \"./jsonRpcService.js\";\nexport * from \"./jsonRpcTransport.js\";\nexport * from \"./pairingEndpoint.js\";\nexport * from \"./pairingService.js\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"barrel.js","names":["DEFAULT_MAX_MESSAGE_LENGTH","lpStream","ccc","asJsonRpcError","JsonRpcError","ccc","lpStream","ccc","lp","multiaddr","lpStream","ccc","multiaddr","CODE_P2P"],"sources":["../src/dial.ts","../src/jsonRpcService.ts","../src/jsonRpcTransport.ts","../src/pairingEndpoint.ts","../src/pairingService.ts","../src/barrel.ts"],"sourcesContent":["import type { Libp2p, PeerId } from \"@libp2p/interface\";\nimport type { Multiaddr } from \"@multiformats/multiaddr\";\n\nexport async function dialKnownAddresses(\n node: Libp2p,\n peerId: PeerId,\n signal?: AbortSignal,\n) {\n const peer = await node.peerStore.get(peerId);\n // PeerStore may omit the target peer id. Restore it so libp2p can\n // attempt a direct upgrade without force-dialing.\n const addresses = peer.addresses.flatMap(({ multiaddr }) => {\n const address = addressForPeer(multiaddr, peerId);\n return address ? [address] : [];\n });\n if (addresses.length === 0) {\n return;\n }\n\n return signal ? node.dial(addresses, { signal }) : node.dial(addresses);\n}\n\nfunction addressForPeer(address: Multiaddr, peerId: PeerId) {\n const last = address.getComponents().at(-1);\n if (last?.name !== \"p2p\") {\n return address.encapsulate(`/p2p/${peerId.toString()}`);\n }\n return last.value === peerId.toString() ? address : undefined;\n}\n","import { ccc, JsonRpcError } from \"@ckb-ccc/core\";\nimport type { Connection, PeerId, Stream } from \"@libp2p/interface\";\nimport type { Registrar } from \"@libp2p/interface-internal\";\nimport { lpStream } from \"@libp2p/utils\";\n\nconst DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;\n\nexport type JsonRpcServiceComponents = {\n registrar: Registrar;\n};\n\nexport type JsonRpcServiceConfig = {\n protocol: string;\n maxMessageLength?: number;\n};\n\nexport type JsonRpcRequest = {\n connection: Connection;\n peerId: PeerId;\n payload: ccc.JsonRpcPayload;\n};\n\nexport type JsonRpcRequestHandler<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n> = (this: JsonRpcService<Components>, request: JsonRpcRequest) => unknown;\n\nexport abstract class JsonRpcService<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n> {\n private readonly errorListeners = new Set<(error: Error) => void>();\n readonly maxMessageLength: number;\n\n constructor(\n readonly components: Components,\n readonly config: JsonRpcServiceConfig,\n ) {\n const maxMessageLength =\n config.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH;\n if (!Number.isSafeInteger(maxMessageLength) || maxMessageLength <= 0) {\n throw new Error(\n \"Maximum JSON-RPC message length must be a positive integer\",\n );\n }\n\n this.maxMessageLength = maxMessageLength;\n }\n\n protected abstract handleRequest(request: JsonRpcRequest): unknown;\n\n onError(listener: (error: Error) => void) {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n async start() {\n await this.components.registrar.handle(\n this.config.protocol,\n this.handleProtocol.bind(this),\n { runOnLimitedConnection: true },\n );\n }\n\n async stop() {\n await this.components.registrar.unhandle(this.config.protocol);\n }\n\n private async handleProtocol(stream: Stream, connection: Connection) {\n let requestAborted = false;\n\n try {\n const rpcStream = lpStream(stream, {\n maxDataLength: this.maxMessageLength,\n });\n const payload = parseJsonRpcRequest(\n ccc.bytesTo((await rpcStream.read()).subarray(), \"utf8\"),\n );\n\n let response: ccc.JsonRpcResponse;\n try {\n response = {\n jsonrpc: \"2.0\",\n id: payload.id,\n result: await this.handleRequest({\n connection,\n peerId: connection.remotePeer,\n payload,\n }),\n };\n } catch (cause) {\n requestAborted = isAbortError(cause);\n const error = toJsonRpcError(cause);\n response = {\n jsonrpc: \"2.0\",\n id: payload.id,\n error: {\n code: error.code,\n message: error.message,\n data: error.data,\n },\n };\n }\n\n await rpcStream.write(ccc.bytesFrom(JSON.stringify(response), \"utf8\"));\n await stream.close();\n } catch (cause) {\n if (requestAborted) {\n return;\n }\n this.handleError(cause, stream);\n }\n }\n\n private handleError(cause: unknown, stream?: Stream) {\n const error = asJsonRpcError(cause);\n\n stream?.abort(error);\n this.errorListeners.forEach((listener) => listener(error));\n return error;\n }\n}\n\nfunction toJsonRpcError(cause: unknown) {\n if (cause instanceof JsonRpcError) {\n return cause;\n }\n\n return new JsonRpcError({\n code: -32603,\n message: asJsonRpcError(cause).message,\n });\n}\n\nfunction parseJsonRpcRequest(data: string) {\n const payload = JSON.parse(data) as Partial<ccc.JsonRpcPayload>;\n if (\n payload?.jsonrpc !== \"2.0\" ||\n (typeof payload.id !== \"number\" && typeof payload.id !== \"string\") ||\n typeof payload.method !== \"string\"\n ) {\n throw new Error(\"Invalid JSON-RPC request\");\n }\n\n return payload as ccc.JsonRpcPayload;\n}\n\nfunction asJsonRpcError(cause: unknown) {\n return cause instanceof Error ? cause : new Error(\"JSON-RPC request failed\");\n}\n\nfunction isAbortError(cause: unknown) {\n return (\n cause instanceof Error &&\n (cause.name === \"AbortError\" || cause.name === \"TimeoutError\")\n );\n}\n\nexport function jsonRpcService<\n Components extends JsonRpcServiceComponents = JsonRpcServiceComponents,\n>(\n config: JsonRpcServiceConfig,\n handler: JsonRpcRequestHandler<Components>,\n): (components: Components) => JsonRpcService<Components> {\n return (components: Components) =>\n new (class extends JsonRpcService<Components> {\n protected handleRequest(request: JsonRpcRequest) {\n return handler.call(this, request);\n }\n })(components, config);\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport type { Connection, Libp2p, PeerId, Stream } from \"@libp2p/interface\";\nimport { lpStream } from \"@libp2p/utils\";\nimport { dialKnownAddresses } from \"./dial.js\";\n\nconst DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;\nconst DEFAULT_TIMEOUT = 120_000;\n\nexport type JsonRpcTransportLibp2pConfig = {\n protocol: string;\n maxMessageLength?: number;\n timeout?: number;\n signal?: AbortSignal;\n onResponse?: (response: ccc.JsonRpcResponse) => void;\n};\n\nexport class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {\n private readonly maxMessageLength: number;\n private readonly timeout: number;\n\n constructor(\n private readonly node: Libp2p,\n readonly peerId: PeerId,\n private readonly config: JsonRpcTransportLibp2pConfig,\n ) {\n const maxMessageLength =\n config.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH;\n if (!Number.isSafeInteger(maxMessageLength) || maxMessageLength <= 0) {\n throw new Error(\n \"Maximum JSON-RPC message length must be a positive integer\",\n );\n }\n\n this.maxMessageLength = maxMessageLength;\n\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n if (!Number.isSafeInteger(timeout) || timeout <= 0) {\n throw new Error(\"JSON-RPC request timeout must be a positive integer\");\n }\n\n this.timeout = timeout;\n }\n\n async request(payload: ccc.JsonRpcPayload): Promise<ccc.JsonRpcResponse> {\n const timeoutSignal = AbortSignal.timeout(this.timeout);\n const signal = this.config.signal\n ? ccc.abortSignalAny([this.config.signal, timeoutSignal])\n : timeoutSignal;\n\n let stream: Stream | undefined;\n let response: ccc.JsonRpcResponse;\n try {\n stream = await this.openStream(signal);\n\n const rpcStream = lpStream(stream, {\n maxDataLength: this.maxMessageLength,\n });\n\n await rpcStream.write(ccc.bytesFrom(JSON.stringify(payload), \"utf8\"), {\n signal,\n });\n await stream.close({ signal });\n\n response = JSON.parse(\n ccc.bytesTo((await rpcStream.read({ signal })).subarray(), \"utf8\"),\n ) as ccc.JsonRpcResponse;\n await stream.closeRead({ signal });\n signal.throwIfAborted();\n } catch (cause) {\n const error = asJsonRpcError(cause);\n stream?.abort(error);\n throw error;\n }\n\n this.config.onResponse?.(response);\n return response;\n }\n\n private async openStream(signal: AbortSignal) {\n void dialKnownAddresses(this.node, this.peerId).catch(() => {\n // This is opportunistic; the current stream can still use relay/fallbacks.\n });\n\n for (const connection of this.connections()) {\n try {\n return await connection.newStream(this.config.protocol, {\n runOnLimitedConnection: true,\n signal,\n });\n } catch {\n signal.throwIfAborted();\n }\n }\n\n return this.node.dialProtocol(this.peerId, this.config.protocol, {\n runOnLimitedConnection: true,\n signal,\n });\n }\n\n private connections() {\n return this.node\n .getConnections(this.peerId)\n .filter(({ status }) => status === \"open\")\n .sort(compareConnections);\n }\n}\n\nfunction compareConnections(a: Connection, b: Connection) {\n if (a.direct !== b.direct) {\n return a.direct ? -1 : 1;\n }\n if (a.rtt === undefined) {\n return b.rtt === undefined ? 0 : 1;\n }\n return b.rtt === undefined ? -1 : a.rtt - b.rtt;\n}\n\nfunction asJsonRpcError(cause: unknown) {\n return cause instanceof Error ? cause : new Error(\"JSON-RPC request failed\");\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport { multiaddr, type Multiaddr } from \"@multiformats/multiaddr\";\nimport * as lp from \"it-length-prefixed\";\nimport type { PairingTarget } from \"./pairingService.js\";\n\nconst MAX_DECOMPRESSED_ADDRESSES_LENGTH = 16 * 1024;\nconst MAX_ADDRESSES = 16;\nconst PAIRING_PARAMETER_NAMES = [\"addresses\", \"role\", \"secret\"] as const;\n\nexport class PairingEndpointRoleError extends Error {\n constructor(\n readonly expectedRole: string,\n readonly actualRole: string | undefined,\n ) {\n super(\n `Expected a ${expectedRole} pairing endpoint, received ${actualRole ? `role \"${actualRole}\"` : \"an endpoint without a role\"}`,\n );\n this.name = \"PairingEndpointRoleError\";\n }\n}\n\nexport async function encodePairingEndpoint(\n endpointUrl: string,\n addresses: readonly Multiaddr[],\n secret: string,\n role?: string,\n) {\n if (addresses.length === 0) {\n throw new Error(\"Pairing endpoint requires at least one address\");\n }\n if (addresses.length > MAX_ADDRESSES) {\n throw new Error(\n `Pairing endpoint supports at most ${MAX_ADDRESSES} addresses`,\n );\n }\n\n const addressBytes = encodeAddresses(addresses);\n if (addressBytes.byteLength > MAX_DECOMPRESSED_ADDRESSES_LENGTH) {\n throw new Error(\"Pairing endpoint address data is too large\");\n }\n\n const compressedAddressBytes = await transformBytes(\n addressBytes,\n new CompressionStream(\"deflate\"),\n );\n\n const encodedAddresses = ccc.bytesTo(compressedAddressBytes, \"base64url\");\n\n const url = new URL(endpointUrl);\n const { params, route } = fragmentParameters(url);\n\n PAIRING_PARAMETER_NAMES.forEach((name) => {\n params.delete(name);\n });\n if (role) {\n params.set(\"role\", role);\n }\n params.set(\"addresses\", encodedAddresses);\n params.set(\"secret\", secret);\n url.hash = `${route}?${params.toString()}`;\n\n return url.toString();\n}\n\nexport async function decodePairingEndpoint(\n endpoint: string,\n expectedRole?: string,\n): Promise<PairingTarget> {\n const url = new URL(endpoint.trim());\n const { params } = fragmentParameters(url);\n if (expectedRole) {\n const actualRole = params.get(\"role\")?.trim() || undefined;\n if (actualRole !== expectedRole) {\n throw new PairingEndpointRoleError(expectedRole, actualRole);\n }\n }\n const compressedAddresses = params.get(\"addresses\")?.trim();\n const secret = params.get(\"secret\")?.trim();\n\n if (!compressedAddresses || !secret) {\n throw new Error(\"Pairing endpoint is incomplete\");\n }\n\n const addresses = await decodeCompressedAddresses(compressedAddresses);\n\n return { addresses, secret };\n}\n\nfunction fragmentParameters(url: URL) {\n const fragment = url.hash.slice(1);\n const separator = fragment.indexOf(\"?\");\n if (separator === -1) {\n return { params: new URLSearchParams(), route: fragment };\n }\n\n return {\n params: new URLSearchParams(fragment.slice(separator + 1)),\n route: fragment.slice(0, separator),\n };\n}\n\nfunction encodeAddresses(addresses: readonly Multiaddr[]): Uint8Array {\n return ccc.bytesConcat(\n ...lp.encode(addresses.map((address) => address.bytes)),\n );\n}\n\nasync function decodeCompressedAddresses(value: string): Promise<Multiaddr[]> {\n try {\n const compressedBytes = ccc.bytesFrom(value, \"base64url\");\n const bytes = await transformBytes(\n compressedBytes,\n new DecompressionStream(\"deflate\"),\n MAX_DECOMPRESSED_ADDRESSES_LENGTH,\n );\n const addresses: Multiaddr[] = [];\n for (const address of lp.decode([bytes], {\n maxDataLength: MAX_DECOMPRESSED_ADDRESSES_LENGTH,\n })) {\n if (addresses.length >= MAX_ADDRESSES) {\n throw new Error(\"Too many addresses\");\n }\n addresses.push(multiaddr(address.subarray()));\n }\n\n if (addresses.length === 0) {\n throw new Error(\"Missing addresses\");\n }\n return addresses;\n } catch (cause) {\n throw new Error(\"Pairing endpoint contains invalid compressed addresses\", {\n cause,\n });\n }\n}\n\nasync function transformBytes(\n bytes: Uint8Array,\n transform: CompressionStream | DecompressionStream,\n maxOutputLength?: number,\n) {\n const readable =\n maxOutputLength === undefined\n ? transform.readable\n : transform.readable.pipeThrough(limitBytes(maxOutputLength));\n const output = new Response(readable).arrayBuffer().then(\n (buffer) => ({ buffer, ok: true }) as const,\n (error: unknown) => ({ error, ok: false }) as const,\n );\n const writer = transform.writable.getWriter();\n try {\n await writer.write(Uint8Array.from(bytes));\n await writer.close();\n } catch (cause) {\n const result = await output;\n if (!result.ok) {\n throw result.error;\n }\n throw cause;\n }\n\n const result = await output;\n if (!result.ok) {\n throw result.error;\n }\n return new Uint8Array(result.buffer);\n}\n\nfunction limitBytes(maxLength: number) {\n let length = 0;\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n if (chunk.byteLength > maxLength - length) {\n throw new Error(\"Decompressed address data is too large\");\n }\n length += chunk.byteLength;\n controller.enqueue(chunk);\n },\n });\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport type {\n AbortOptions,\n Connection,\n PeerId,\n Stream,\n} from \"@libp2p/interface\";\nimport type {\n AddressManager,\n ConnectionManager,\n Registrar,\n} from \"@libp2p/interface-internal\";\nimport { lpStream } from \"@libp2p/utils\";\nimport { CODE_P2P, multiaddr, type Multiaddr } from \"@multiformats/multiaddr\";\n\nconst MAX_PAIRING_MESSAGE_LENGTH = 1024;\nconst UNPAIR_TIMEOUT_MS = 10_000;\n\nexport type PairingServiceComponents = {\n addressManager?: Pick<AddressManager, \"getAddresses\">;\n connectionManager: ConnectionManager;\n registrar: Registrar;\n};\n\nexport type PairingServiceConfig = {\n /** Optional self-reported label shared with the paired peer. */\n name?: string;\n protocol: string;\n pairedPeerTimeoutMs: number;\n};\n\nexport type PairingTarget = {\n addresses: Multiaddr[];\n secret: string;\n};\n\nexport type PairingGuard<\n Components extends PairingServiceComponents = PairingServiceComponents,\n> = (this: PairingService<Components>, peerId: PeerId) => boolean;\n\ntype PairingRequest =\n | { type: \"pair\"; secret: string; name?: string; addresses?: string[] }\n | { type: \"unpair\" };\n\ntype PairingResponse =\n { ok: true; name?: string } | { ok: false; error: string };\n\nexport abstract class PairingService<\n Components extends PairingServiceComponents = PairingServiceComponents,\n> {\n private currentSecret = createSecret();\n private readonly errorListeners = new Set<(error: Error) => void>();\n private readonly pairedListeners = new Set<\n (peerId: PeerId, name?: string) => void\n >();\n private readonly unpairedListeners = new Set<(peerId: PeerId) => void>();\n private readonly secretChangedListeners = new Set<(secret: string) => void>();\n private readonly pairingTimeouts = new Map<\n string,\n ReturnType<typeof setTimeout>\n >();\n\n constructor(\n readonly components: Components,\n readonly config: PairingServiceConfig,\n ) {\n if (config.pairedPeerTimeoutMs <= 0) {\n throw new Error(\"Paired peer timeout must be greater than zero\");\n }\n }\n\n protected abstract canPair(peerId: PeerId): boolean;\n\n /** The credential to include in the currently issued pairing endpoint. */\n get secret() {\n return this.currentSecret;\n }\n\n onError(listener: (error: Error) => void) {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n onPaired(listener: (peerId: PeerId, name?: string) => void) {\n this.pairedListeners.add(listener);\n return () => this.pairedListeners.delete(listener);\n }\n\n onUnpaired(listener: (peerId: PeerId) => void) {\n this.unpairedListeners.add(listener);\n return () => this.unpairedListeners.delete(listener);\n }\n\n onSecretChanged(listener: (secret: string) => void) {\n this.secretChangedListeners.add(listener);\n return () => this.secretChangedListeners.delete(listener);\n }\n\n async start() {\n await this.components.registrar.handle(\n this.config.protocol,\n this.handleProtocol.bind(this),\n { runOnLimitedConnection: true },\n );\n }\n\n async stop() {\n await this.components.registrar.unhandle(this.config.protocol);\n\n this.pairingTimeouts.forEach((timeout) => clearTimeout(timeout));\n this.pairingTimeouts.clear();\n }\n\n isPaired(peerId: PeerId) {\n return this.pairingTimeouts.has(peerId.toString());\n }\n\n refresh(peerId: PeerId) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout === undefined) {\n return;\n }\n\n clearTimeout(timeout);\n this.pairingTimeouts.set(key, this.createPeerTimeout(peerId));\n }\n\n async pair(\n target: PairingTarget,\n options: AbortOptions = {},\n ): Promise<PeerId> {\n let stream: Stream | undefined;\n let addedPeer: PeerId | undefined;\n\n try {\n const opened = await this.openStream(target.addresses, options);\n stream = opened.stream;\n\n if (!opened.connection) {\n throw new Error(\"Could not determine remote peer\");\n }\n\n if (this.addPairedPeer(opened.connection.remotePeer)) {\n addedPeer = opened.connection.remotePeer;\n }\n\n const response = await exchange(\n stream,\n createPairRequest(target.secret, this.config.name, this.addresses()),\n options,\n );\n options.signal?.throwIfAborted();\n stream = undefined;\n\n if (!response.ok) {\n throw new Error(response.error);\n }\n\n if (addedPeer) {\n this.rotateSecret();\n this.notifyPairedPeer(addedPeer, response.name);\n }\n return opened.connection.remotePeer;\n } catch (cause) {\n if (addedPeer) {\n this.removePairedPeer(addedPeer, false);\n }\n throw this.handleError(cause, stream);\n }\n }\n\n async unpair(peerId: PeerId) {\n if (!this.removePairedPeer(peerId)) {\n return;\n }\n\n const options = { signal: AbortSignal.timeout(UNPAIR_TIMEOUT_MS) };\n\n let stream: Stream | undefined;\n try {\n const opened = await this.openStream(peerId, options);\n stream = opened.stream;\n const response = await exchange(\n opened.stream,\n { type: \"unpair\" },\n options,\n );\n stream = undefined;\n if (!response.ok) {\n throw new Error(response.error);\n }\n } catch (cause) {\n this.handleError(cause, stream);\n }\n }\n\n private async handleProtocol(stream: Stream, connection: Connection) {\n let addedPeer: PeerId | undefined;\n\n try {\n const pairingStream = lpStream(stream, {\n maxDataLength: MAX_PAIRING_MESSAGE_LENGTH,\n });\n const request = parsePairingRequest(\n (await pairingStream.read()).subarray(),\n );\n\n if (request.type === \"unpair\") {\n this.removePairedPeer(connection.remotePeer);\n await writeResponse(pairingStream, stream, { ok: true });\n return;\n }\n\n if (\n request.secret !== this.secret ||\n !this.canPair(connection.remotePeer)\n ) {\n await writeResponse(pairingStream, stream, {\n ok: false,\n error: \"Pairing rejected\",\n });\n return;\n }\n\n const addresses = parseAddresses(\n request.addresses,\n connection.remotePeer,\n );\n\n // Consume the credential before the next asynchronous boundary so two\n // concurrent requests cannot both pair with the same endpoint.\n this.rotateSecret();\n\n if (this.addPairedPeer(connection.remotePeer)) {\n addedPeer = connection.remotePeer;\n }\n await writeResponse(pairingStream, stream, {\n ok: true,\n name: this.config.name,\n });\n if (addedPeer) {\n this.notifyPairedPeer(addedPeer, request.name);\n void this.openFallbackConnection(addresses);\n }\n } catch (cause) {\n if (addedPeer) {\n this.removePairedPeer(addedPeer, false);\n }\n this.handleError(cause, stream);\n }\n }\n\n private addPairedPeer(peerId: PeerId) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout !== undefined) {\n clearTimeout(timeout);\n }\n\n this.pairingTimeouts.set(key, this.createPeerTimeout(peerId));\n return timeout === undefined;\n }\n\n private notifyPairedPeer(peerId: PeerId, name?: string) {\n this.pairedListeners.forEach((listener) => listener(peerId, name));\n }\n\n private addresses() {\n return this.components.addressManager?.getAddresses() ?? [];\n }\n\n private async openFallbackConnection(addresses: Multiaddr[]) {\n if (addresses.length === 0) {\n return;\n }\n\n try {\n await this.components.connectionManager.openConnection(addresses);\n } catch {\n // The original pairing connection remains usable when the fallback fails.\n }\n }\n\n private rotateSecret() {\n this.currentSecret = createSecret();\n this.secretChangedListeners.forEach((listener) =>\n listener(this.currentSecret),\n );\n }\n\n private removePairedPeer(peerId: PeerId, notify = true) {\n const key = peerId.toString();\n const timeout = this.pairingTimeouts.get(key);\n if (timeout === undefined) {\n return false;\n }\n\n clearTimeout(timeout);\n this.pairingTimeouts.delete(key);\n\n if (notify) {\n this.unpairedListeners.forEach((listener) => listener(peerId));\n }\n return true;\n }\n\n private createPeerTimeout(peerId: PeerId) {\n return setTimeout(() => {\n if (\n this.components.connectionManager\n .getConnections(peerId)\n .some(({ status }) => status === \"open\")\n ) {\n this.pairingTimeouts.set(\n peerId.toString(),\n this.createPeerTimeout(peerId),\n );\n return;\n }\n\n this.removePairedPeer(peerId);\n }, this.config.pairedPeerTimeoutMs);\n }\n\n private async openStream(\n target: PeerId | Multiaddr | Multiaddr[],\n options: AbortOptions,\n ) {\n let connection: Connection | undefined;\n const stream = await this.components.connectionManager.openStream(\n target,\n this.config.protocol,\n {\n runOnLimitedConnection: true,\n signal: options.signal,\n onProgress(event) {\n if (event.type === \"connection:opened-stream\") {\n connection = event.detail.connection;\n }\n },\n },\n );\n\n return { stream, connection };\n }\n\n private handleError(cause: unknown, stream?: Stream) {\n const error = asError(cause, \"Pairing operation failed\");\n\n stream?.abort(error);\n this.errorListeners.forEach((listener) => listener(error));\n return error;\n }\n}\n\nexport function pairingService<\n Components extends PairingServiceComponents = PairingServiceComponents,\n>(\n config: PairingServiceConfig,\n canPair: PairingGuard<Components>,\n): (components: Components) => PairingService<Components> {\n return (components: Components) =>\n new (class extends PairingService<Components> {\n protected canPair(peerId: PeerId) {\n return canPair.call(this, peerId);\n }\n })(components, config);\n}\n\nasync function exchange(\n stream: Stream,\n request: PairingRequest,\n options: AbortOptions,\n) {\n const pairingStream = lpStream(stream, {\n maxDataLength: MAX_PAIRING_MESSAGE_LENGTH,\n });\n\n await pairingStream.write(ccc.bytesFrom(JSON.stringify(request), \"utf8\"), {\n signal: options.signal,\n });\n await stream.close(options);\n\n const response = parsePairingResponse(\n (await pairingStream.read(options)).subarray(),\n );\n await stream.closeRead(options);\n return response;\n}\n\nfunction createPairRequest(\n secret: string,\n name: string | undefined,\n addresses: Multiaddr[],\n): PairingRequest {\n const addressStrings = addresses.map((address) => address.toString());\n const request: PairingRequest = {\n type: \"pair\",\n secret,\n name,\n addresses: addressStrings,\n };\n while (addressStrings.length > 0 && !pairingRequestFits(request)) {\n addressStrings.pop();\n }\n\n if (!pairingRequestFits(request)) {\n delete request.addresses;\n }\n if (!pairingRequestFits(request)) {\n delete request.name;\n }\n if (!pairingRequestFits(request)) {\n throw new Error(\"Pairing request is too large\");\n }\n return request;\n}\n\nfunction pairingRequestFits(request: PairingRequest) {\n return (\n ccc.bytesFrom(JSON.stringify(request), \"utf8\").byteLength <=\n MAX_PAIRING_MESSAGE_LENGTH\n );\n}\n\nasync function writeResponse(\n pairingStream: ReturnType<typeof lpStream>,\n stream: Stream,\n response: PairingResponse,\n) {\n await pairingStream.write(ccc.bytesFrom(JSON.stringify(response), \"utf8\"));\n await stream.close();\n}\n\nfunction parsePairingRequest(data: Uint8Array): PairingRequest {\n const request = JSON.parse(\n ccc.bytesTo(data, \"utf8\"),\n ) as Partial<PairingRequest>;\n if (\n request.type === \"pair\" &&\n typeof request.secret === \"string\" &&\n request.secret\n ) {\n return {\n type: \"pair\",\n secret: request.secret,\n name: typeof request.name === \"string\" ? request.name : undefined,\n addresses: parseAddressStrings(request.addresses),\n };\n }\n\n if (request.type === \"unpair\") {\n return { type: \"unpair\" };\n }\n\n throw new Error(\"Invalid pairing request\");\n}\n\nfunction parseAddressStrings(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n return value.filter(\n (address): address is string =>\n typeof address === \"string\" && address.length > 0,\n );\n}\n\nfunction parseAddresses(values: string[] | undefined, peerId: PeerId) {\n return (values ?? []).flatMap((value) => {\n try {\n const address = multiaddr(value);\n const peer = address\n .getComponents()\n .filter(({ code }) => code === CODE_P2P)\n .at(-1)?.value;\n return peer === peerId.toString() ? [address] : [];\n } catch {\n return [];\n }\n });\n}\n\nfunction parsePairingResponse(data: Uint8Array): PairingResponse {\n const response = JSON.parse(\n ccc.bytesTo(data, \"utf8\"),\n ) as Partial<PairingResponse>;\n if (response.ok === true) {\n return {\n ok: true,\n name: typeof response.name === \"string\" ? response.name : undefined,\n };\n }\n if (response.ok === false && typeof response.error === \"string\") {\n return { ok: false, error: response.error };\n }\n\n throw new Error(\"Invalid pairing response\");\n}\n\nfunction createSecret() {\n return ccc.bytesTo(crypto.getRandomValues(new Uint8Array(16)), \"base64url\");\n}\n\nfunction asError(cause: unknown, fallback: string) {\n return cause instanceof Error ? cause : new Error(fallback);\n}\n","export * from \"./dial.js\";\nexport * from \"./jsonRpcService.js\";\nexport * from \"./jsonRpcTransport.js\";\nexport * from \"./pairingEndpoint.js\";\nexport * from \"./pairingService.js\";\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckb-ccc/libp2p",
3
- "version": "0.0.2",
3
+ "version": "0.2.0",
4
4
  "description": "CCC support for libp2p",
5
5
  "author": "Hanssen0 <0@hanssen0.com>",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  "@libp2p/utils": "^7.4.0",
46
46
  "@multiformats/multiaddr": "^13.0.3",
47
47
  "it-length-prefixed": "^11.0.1",
48
- "@ckb-ccc/core": "1.20.0"
48
+ "@ckb-ccc/core": "1.21.0"
49
49
  },
50
50
  "types": "./dist.commonjs/index.d.ts",
51
51
  "scripts": {
package/src/barrel.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./dial.js";
1
2
  export * from "./jsonRpcService.js";
2
3
  export * from "./jsonRpcTransport.js";
3
4
  export * from "./pairingEndpoint.js";
package/src/dial.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { Libp2p, PeerId } from "@libp2p/interface";
2
+ import type { Multiaddr } from "@multiformats/multiaddr";
3
+
4
+ export async function dialKnownAddresses(
5
+ node: Libp2p,
6
+ peerId: PeerId,
7
+ signal?: AbortSignal,
8
+ ) {
9
+ const peer = await node.peerStore.get(peerId);
10
+ // PeerStore may omit the target peer id. Restore it so libp2p can
11
+ // attempt a direct upgrade without force-dialing.
12
+ const addresses = peer.addresses.flatMap(({ multiaddr }) => {
13
+ const address = addressForPeer(multiaddr, peerId);
14
+ return address ? [address] : [];
15
+ });
16
+ if (addresses.length === 0) {
17
+ return;
18
+ }
19
+
20
+ return signal ? node.dial(addresses, { signal }) : node.dial(addresses);
21
+ }
22
+
23
+ function addressForPeer(address: Multiaddr, peerId: PeerId) {
24
+ const last = address.getComponents().at(-1);
25
+ if (last?.name !== "p2p") {
26
+ return address.encapsulate(`/p2p/${peerId.toString()}`);
27
+ }
28
+ return last.value === peerId.toString() ? address : undefined;
29
+ }
@@ -15,6 +15,7 @@ export type JsonRpcServiceConfig = {
15
15
  };
16
16
 
17
17
  export type JsonRpcRequest = {
18
+ connection: Connection;
18
19
  peerId: PeerId;
19
20
  payload: ccc.JsonRpcPayload;
20
21
  };
@@ -80,6 +81,7 @@ export abstract class JsonRpcService<
80
81
  jsonrpc: "2.0",
81
82
  id: payload.id,
82
83
  result: await this.handleRequest({
84
+ connection,
83
85
  peerId: connection.remotePeer,
84
86
  payload,
85
87
  }),
@@ -1,6 +1,7 @@
1
1
  import { ccc } from "@ckb-ccc/core";
2
- import type { Libp2p, PeerId, Stream } from "@libp2p/interface";
2
+ import type { Connection, Libp2p, PeerId, Stream } from "@libp2p/interface";
3
3
  import { lpStream } from "@libp2p/utils";
4
+ import { dialKnownAddresses } from "./dial.js";
4
5
 
5
6
  const DEFAULT_MAX_MESSAGE_LENGTH = 1024 * 1024;
6
7
  const DEFAULT_TIMEOUT = 120_000;
@@ -49,11 +50,7 @@ export class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {
49
50
  let stream: Stream | undefined;
50
51
  let response: ccc.JsonRpcResponse;
51
52
  try {
52
- // dialProtocol handles the signal, so no separate entry check is needed.
53
- stream = await this.node.dialProtocol(this.peerId, this.config.protocol, {
54
- runOnLimitedConnection: true,
55
- signal,
56
- });
53
+ stream = await this.openStream(signal);
57
54
 
58
55
  const rpcStream = lpStream(stream, {
59
56
  maxDataLength: this.maxMessageLength,
@@ -78,6 +75,45 @@ export class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {
78
75
  this.config.onResponse?.(response);
79
76
  return response;
80
77
  }
78
+
79
+ private async openStream(signal: AbortSignal) {
80
+ void dialKnownAddresses(this.node, this.peerId).catch(() => {
81
+ // This is opportunistic; the current stream can still use relay/fallbacks.
82
+ });
83
+
84
+ for (const connection of this.connections()) {
85
+ try {
86
+ return await connection.newStream(this.config.protocol, {
87
+ runOnLimitedConnection: true,
88
+ signal,
89
+ });
90
+ } catch {
91
+ signal.throwIfAborted();
92
+ }
93
+ }
94
+
95
+ return this.node.dialProtocol(this.peerId, this.config.protocol, {
96
+ runOnLimitedConnection: true,
97
+ signal,
98
+ });
99
+ }
100
+
101
+ private connections() {
102
+ return this.node
103
+ .getConnections(this.peerId)
104
+ .filter(({ status }) => status === "open")
105
+ .sort(compareConnections);
106
+ }
107
+ }
108
+
109
+ function compareConnections(a: Connection, b: Connection) {
110
+ if (a.direct !== b.direct) {
111
+ return a.direct ? -1 : 1;
112
+ }
113
+ if (a.rtt === undefined) {
114
+ return b.rtt === undefined ? 0 : 1;
115
+ }
116
+ return b.rtt === undefined ? -1 : a.rtt - b.rtt;
81
117
  }
82
118
 
83
119
  function asJsonRpcError(cause: unknown) {
@@ -5,14 +5,19 @@ import type {
5
5
  PeerId,
6
6
  Stream,
7
7
  } from "@libp2p/interface";
8
- import type { ConnectionManager, Registrar } from "@libp2p/interface-internal";
8
+ import type {
9
+ AddressManager,
10
+ ConnectionManager,
11
+ Registrar,
12
+ } from "@libp2p/interface-internal";
9
13
  import { lpStream } from "@libp2p/utils";
10
- import type { Multiaddr } from "@multiformats/multiaddr";
14
+ import { CODE_P2P, multiaddr, type Multiaddr } from "@multiformats/multiaddr";
11
15
 
12
16
  const MAX_PAIRING_MESSAGE_LENGTH = 1024;
13
17
  const UNPAIR_TIMEOUT_MS = 10_000;
14
18
 
15
19
  export type PairingServiceComponents = {
20
+ addressManager?: Pick<AddressManager, "getAddresses">;
16
21
  connectionManager: ConnectionManager;
17
22
  registrar: Registrar;
18
23
  };
@@ -34,7 +39,8 @@ export type PairingGuard<
34
39
  > = (this: PairingService<Components>, peerId: PeerId) => boolean;
35
40
 
36
41
  type PairingRequest =
37
- { type: "pair"; secret: string; name?: string } | { type: "unpair" };
42
+ | { type: "pair"; secret: string; name?: string; addresses?: string[] }
43
+ | { type: "unpair" };
38
44
 
39
45
  type PairingResponse =
40
46
  { ok: true; name?: string } | { ok: false; error: string };
@@ -141,11 +147,7 @@ export abstract class PairingService<
141
147
 
142
148
  const response = await exchange(
143
149
  stream,
144
- {
145
- type: "pair",
146
- secret: target.secret,
147
- name: this.config.name,
148
- },
150
+ createPairRequest(target.secret, this.config.name, this.addresses()),
149
151
  options,
150
152
  );
151
153
  options.signal?.throwIfAborted();
@@ -221,6 +223,11 @@ export abstract class PairingService<
221
223
  return;
222
224
  }
223
225
 
226
+ const addresses = parseAddresses(
227
+ request.addresses,
228
+ connection.remotePeer,
229
+ );
230
+
224
231
  // Consume the credential before the next asynchronous boundary so two
225
232
  // concurrent requests cannot both pair with the same endpoint.
226
233
  this.rotateSecret();
@@ -234,6 +241,7 @@ export abstract class PairingService<
234
241
  });
235
242
  if (addedPeer) {
236
243
  this.notifyPairedPeer(addedPeer, request.name);
244
+ void this.openFallbackConnection(addresses);
237
245
  }
238
246
  } catch (cause) {
239
247
  if (addedPeer) {
@@ -258,6 +266,22 @@ export abstract class PairingService<
258
266
  this.pairedListeners.forEach((listener) => listener(peerId, name));
259
267
  }
260
268
 
269
+ private addresses() {
270
+ return this.components.addressManager?.getAddresses() ?? [];
271
+ }
272
+
273
+ private async openFallbackConnection(addresses: Multiaddr[]) {
274
+ if (addresses.length === 0) {
275
+ return;
276
+ }
277
+
278
+ try {
279
+ await this.components.connectionManager.openConnection(addresses);
280
+ } catch {
281
+ // The original pairing connection remains usable when the fallback fails.
282
+ }
283
+ }
284
+
261
285
  private rotateSecret() {
262
286
  this.currentSecret = createSecret();
263
287
  this.secretChangedListeners.forEach((listener) =>
@@ -283,6 +307,18 @@ export abstract class PairingService<
283
307
 
284
308
  private createPeerTimeout(peerId: PeerId) {
285
309
  return setTimeout(() => {
310
+ if (
311
+ this.components.connectionManager
312
+ .getConnections(peerId)
313
+ .some(({ status }) => status === "open")
314
+ ) {
315
+ this.pairingTimeouts.set(
316
+ peerId.toString(),
317
+ this.createPeerTimeout(peerId),
318
+ );
319
+ return;
320
+ }
321
+
286
322
  this.removePairedPeer(peerId);
287
323
  }, this.config.pairedPeerTimeoutMs);
288
324
  }
@@ -353,6 +389,41 @@ async function exchange(
353
389
  return response;
354
390
  }
355
391
 
392
+ function createPairRequest(
393
+ secret: string,
394
+ name: string | undefined,
395
+ addresses: Multiaddr[],
396
+ ): PairingRequest {
397
+ const addressStrings = addresses.map((address) => address.toString());
398
+ const request: PairingRequest = {
399
+ type: "pair",
400
+ secret,
401
+ name,
402
+ addresses: addressStrings,
403
+ };
404
+ while (addressStrings.length > 0 && !pairingRequestFits(request)) {
405
+ addressStrings.pop();
406
+ }
407
+
408
+ if (!pairingRequestFits(request)) {
409
+ delete request.addresses;
410
+ }
411
+ if (!pairingRequestFits(request)) {
412
+ delete request.name;
413
+ }
414
+ if (!pairingRequestFits(request)) {
415
+ throw new Error("Pairing request is too large");
416
+ }
417
+ return request;
418
+ }
419
+
420
+ function pairingRequestFits(request: PairingRequest) {
421
+ return (
422
+ ccc.bytesFrom(JSON.stringify(request), "utf8").byteLength <=
423
+ MAX_PAIRING_MESSAGE_LENGTH
424
+ );
425
+ }
426
+
356
427
  async function writeResponse(
357
428
  pairingStream: ReturnType<typeof lpStream>,
358
429
  stream: Stream,
@@ -375,6 +446,7 @@ function parsePairingRequest(data: Uint8Array): PairingRequest {
375
446
  type: "pair",
376
447
  secret: request.secret,
377
448
  name: typeof request.name === "string" ? request.name : undefined,
449
+ addresses: parseAddressStrings(request.addresses),
378
450
  };
379
451
  }
380
452
 
@@ -385,6 +457,31 @@ function parsePairingRequest(data: Uint8Array): PairingRequest {
385
457
  throw new Error("Invalid pairing request");
386
458
  }
387
459
 
460
+ function parseAddressStrings(value: unknown): string[] | undefined {
461
+ if (!Array.isArray(value)) {
462
+ return undefined;
463
+ }
464
+ return value.filter(
465
+ (address): address is string =>
466
+ typeof address === "string" && address.length > 0,
467
+ );
468
+ }
469
+
470
+ function parseAddresses(values: string[] | undefined, peerId: PeerId) {
471
+ return (values ?? []).flatMap((value) => {
472
+ try {
473
+ const address = multiaddr(value);
474
+ const peer = address
475
+ .getComponents()
476
+ .filter(({ code }) => code === CODE_P2P)
477
+ .at(-1)?.value;
478
+ return peer === peerId.toString() ? [address] : [];
479
+ } catch {
480
+ return [];
481
+ }
482
+ });
483
+ }
484
+
388
485
  function parsePairingResponse(data: Uint8Array): PairingResponse {
389
486
  const response = JSON.parse(
390
487
  ccc.bytesTo(data, "utf8"),