@ckb-ccc/libp2p 0.1.0 → 0.2.1

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,28 @@
1
1
  # @ckb-ccc/libp2p
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#538](https://github.com/ckb-devrel/ccc/pull/538) [`272dd4e`](https://github.com/ckb-devrel/ccc/commit/272dd4e1c4cbd4e2027d78379b92cb25df9f111d) Thanks [@Hanssen0](https://github.com/Hanssen0)! - Show the Khie pairing guidance for every invalid endpoint input instead of
8
+ exposing low-level parsing errors.
9
+
10
+ ## 0.2.0
11
+
12
+ ### Minor Changes
13
+
14
+ - [#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
15
+ JSON-RPC stream, sharing address updates with Identify Push, and retaining
16
+ learned addresses for the session.
17
+
18
+ - [#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
19
+ active pairings and restoring direct connections when possible.
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [[`182b880`](https://github.com/ckb-devrel/ccc/commit/182b880bd6343b7ecdc1f5732c1f72cd5a22a44d)]:
24
+ - @ckb-ccc/core@1.21.0
25
+
3
26
  ## 0.1.0
4
27
 
5
28
  ### Minor Changes
package/dist/barrel.d.mts CHANGED
@@ -2,6 +2,9 @@ import { ccc } from "@ckb-ccc/core";
2
2
  import { Multiaddr } from "@multiformats/multiaddr";
3
3
  import { AbortOptions, Connection, Libp2p, PeerId } from "@libp2p/interface";
4
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;
@@ -105,7 +108,10 @@ declare abstract class PairingService<Components extends PairingServiceComponent
105
108
  declare function pairingService<Components extends PairingServiceComponents = PairingServiceComponents>(config: PairingServiceConfig, canPair: PairingGuard<Components>): (components: Components) => PairingService<Components>;
106
109
  //#endregion
107
110
  //#region src/pairingEndpoint.d.ts
108
- declare class PairingEndpointRoleError extends Error {
111
+ declare class PairingEndpointError extends Error {
112
+ constructor(message: string, options?: ErrorOptions);
113
+ }
114
+ declare class PairingEndpointRoleError extends PairingEndpointError {
109
115
  readonly expectedRole: string;
110
116
  readonly actualRole: string | undefined;
111
117
  constructor(expectedRole: string, actualRole: string | undefined);
@@ -113,8 +119,8 @@ declare class PairingEndpointRoleError extends Error {
113
119
  declare function encodePairingEndpoint(endpointUrl: string, addresses: readonly Multiaddr[], secret: string, role?: string): Promise<string>;
114
120
  declare function decodePairingEndpoint(endpoint: string, expectedRole?: string): Promise<PairingTarget>;
115
121
  declare namespace barrel_d_exports {
116
- export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, encodePairingEndpoint, jsonRpcService, pairingService };
122
+ export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointError, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, dialKnownAddresses, encodePairingEndpoint, jsonRpcService, pairingService };
117
123
  }
118
124
  //#endregion
119
- export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, encodePairingEndpoint, jsonRpcService, pairingService, barrel_d_exports as t };
125
+ export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointError, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, dialKnownAddresses, encodePairingEndpoint, jsonRpcService, pairingService, barrel_d_exports as t };
120
126
  //# 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,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;;;KC1JlC;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;UAkBN;;;;KC7EE;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;UAMM;UAsBN;;iBASM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,aAAa,eACpB,YAAY,eAAe,eAAe;;;cCpVjC,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,6BAA6B;EAC5B,YAAA,iBAAiB,UAAU;;cAM5B,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{CODE_P2P as i,multiaddr as a}from"@multiformats/multiaddr";import*as o from"it-length-prefixed";var s=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=l(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=d(e);let t=c(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=u(e);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function c(e){return e instanceof t?e:new t({code:-32603,message:u(e).message})}function l(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 u(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}function d(e){return e instanceof Error&&(e.name===`AbortError`||e.name===`TimeoutError`)}function f(e,t){return n=>new class extends s{handleRequest(e){return t.call(this,e)}}(n,e)}var p=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=h(e);throw a?.abort(t),t}return this.config.onResponse?.(o),o}async openStream(e){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(m)}};function m(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 h(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}const g=16384,_=[`addresses`,`role`,`secret`];var v=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 y(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=S(t);if(a.byteLength>g)throw Error(`Pairing endpoint address data is too large`);let o=await w(a,new CompressionStream(`deflate`)),s=n.bytesTo(o,`base64url`),c=new URL(e),{params:l,route:u}=x(c);return _.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 b(e,t){let{params:n}=x(new URL(e.trim()));if(t){let e=n.get(`role`)?.trim()||void 0;if(e!==t)throw new v(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 C(r),secret:i}}function x(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 S(e){return n.bytesConcat(...o.encode(e.map(e=>e.bytes)))}async function C(e){try{let t=await w(n.bytesFrom(e,`base64url`),new DecompressionStream(`deflate`),g),r=[];for(let e of o.decode([t],{maxDataLength:g})){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 w(e,t,n){let r=n===void 0?t.readable:t.readable.pipeThrough(T(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 T(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 E=1024;var D=class{constructor(e,t){if(this.components=e,this.config=t,this.currentSecret=L(),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 k(n,A(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 k(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:E}),a=N((await i.read()).subarray());if(a.type===`unpair`){this.removePairedPeer(t.remotePeer),await M(i,e,{ok:!0});return}if(a.secret!==this.secret||!this.canPair(t.remotePeer)){await M(i,e,{ok:!1,error:`Pairing rejected`});return}let o=F(a.addresses,t.remotePeer);this.rotateSecret(),this.addPairedPeer(t.remotePeer)&&(n=t.remotePeer),await M(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=L(),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=R(e,`Pairing operation failed`);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function O(e,t){return n=>new class extends D{canPair(e){return t.call(this,e)}}(n,e)}async function k(e,t,i){let a=r(e,{maxDataLength:E});await a.write(n.bytesFrom(JSON.stringify(t),`utf8`),{signal:i.signal}),await e.close(i);let o=I((await a.read(i)).subarray());return await e.closeRead(i),o}function A(e,t,n){let r=n.map(e=>e.toString()),i={type:`pair`,secret:e,name:t,addresses:r};for(;r.length>0&&!j(i);)r.pop();if(j(i)||delete i.addresses,j(i)||delete i.name,!j(i))throw Error(`Pairing request is too large`);return i}function j(e){return n.bytesFrom(JSON.stringify(e),`utf8`).byteLength<=E}async function M(e,t,r){await e.write(n.bytesFrom(JSON.stringify(r),`utf8`)),await t.close()}function N(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:P(t.addresses)};if(t.type===`unpair`)return{type:`unpair`};throw Error(`Invalid pairing request`)}function P(e){if(Array.isArray(e))return e.filter(e=>typeof e==`string`&&e.length>0)}function F(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 I(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 L(){return n.bytesTo(crypto.getRandomValues(new Uint8Array(16)),`base64url`)}function R(e,t){return e instanceof Error?e:Error(t)}var z=e({JsonRpcService:()=>s,JsonRpcTransportLibp2p:()=>p,PairingEndpointRoleError:()=>v,PairingService:()=>D,decodePairingEndpoint:()=>b,encodePairingEndpoint:()=>y,jsonRpcService:()=>f,pairingService:()=>O});export{s as JsonRpcService,p as JsonRpcTransportLibp2p,v as PairingEndpointRoleError,D as PairingService,b as decodePairingEndpoint,y as encodePairingEndpoint,f as jsonRpcService,O as pairingService,z 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(e,t),this.name=`PairingEndpointError`}},x=class extends b{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 S(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=T(t);if(a.byteLength>v)throw Error(`Pairing endpoint address data is too large`);let o=await D(a,new CompressionStream(`deflate`)),s=n.bytesTo(o,`base64url`),c=new URL(e),{params:l,route:u}=w(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 C(e,t){let n;try{n=new URL(e.trim())}catch(e){throw new b(`Pairing endpoint is not a valid URL`,{cause:e})}let{params:r}=w(n);if(t){let e=r.get(`role`)?.trim()||void 0;if(e!==t)throw new x(t,e)}let i=r.get(`addresses`)?.trim(),a=r.get(`secret`)?.trim();if(!i||!a)throw new b(`Pairing endpoint is incomplete`);return{addresses:await E(i),secret:a}}function w(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 T(e){return n.bytesConcat(...o.encode(e.map(e=>e.bytes)))}async function E(e){try{let t=await D(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 new b(`Pairing endpoint contains invalid compressed addresses`,{cause:e})}}async function D(e,t,n){let r=n===void 0?t.readable:t.readable.pipeThrough(O(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 O(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 k=1024;var A=class{constructor(e,t){if(this.components=e,this.config=t,this.currentSecret=B(),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 M(n,N(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 M(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:k}),a=I((await i.read()).subarray());if(a.type===`unpair`){this.removePairedPeer(t.remotePeer),await F(i,e,{ok:!0});return}if(a.secret!==this.secret||!this.canPair(t.remotePeer)){await F(i,e,{ok:!1,error:`Pairing rejected`});return}let o=R(a.addresses,t.remotePeer);this.rotateSecret(),this.addPairedPeer(t.remotePeer)&&(n=t.remotePeer),await F(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=B(),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=V(e,`Pairing operation failed`);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function j(e,t){return n=>new class extends A{canPair(e){return t.call(this,e)}}(n,e)}async function M(e,t,i){let a=r(e,{maxDataLength:k});await a.write(n.bytesFrom(JSON.stringify(t),`utf8`),{signal:i.signal}),await e.close(i);let o=z((await a.read(i)).subarray());return await e.closeRead(i),o}function N(e,t,n){let r=n.map(e=>e.toString()),i={type:`pair`,secret:e,name:t,addresses:r};for(;r.length>0&&!P(i);)r.pop();if(P(i)||delete i.addresses,P(i)||delete i.name,!P(i))throw Error(`Pairing request is too large`);return i}function P(e){return n.bytesFrom(JSON.stringify(e),`utf8`).byteLength<=k}async function F(e,t,r){await e.write(n.bytesFrom(JSON.stringify(r),`utf8`)),await t.close()}function I(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:L(t.addresses)};if(t.type===`unpair`)return{type:`unpair`};throw Error(`Invalid pairing request`)}function L(e){if(Array.isArray(e))return e.filter(e=>typeof e==`string`&&e.length>0)}function R(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 z(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 B(){return n.bytesTo(crypto.getRandomValues(new Uint8Array(16)),`base64url`)}function V(e,t){return e instanceof Error?e:Error(t)}var H=e({JsonRpcService:()=>l,JsonRpcTransportLibp2p:()=>h,PairingEndpointError:()=>b,PairingEndpointRoleError:()=>x,PairingService:()=>A,decodePairingEndpoint:()=>C,dialKnownAddresses:()=>s,encodePairingEndpoint:()=>S,jsonRpcService:()=>m,pairingService:()=>j});export{l as JsonRpcService,h as JsonRpcTransportLibp2p,b as PairingEndpointError,x as PairingEndpointRoleError,A as PairingService,C as decodePairingEndpoint,s as dialKnownAddresses,S as encodePairingEndpoint,m as jsonRpcService,j as pairingService,H 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 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\";\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 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 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 \"./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 PairingEndpointError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"PairingEndpointError\";\n }\n}\n\nexport class PairingEndpointRoleError extends PairingEndpointError {\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 let url: URL;\n try {\n url = new URL(endpoint.trim());\n } catch (cause) {\n throw new PairingEndpointError(\"Pairing endpoint is not a valid URL\", {\n cause,\n });\n }\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 PairingEndpointError(\"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 PairingEndpointError(\n \"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 { ccc } from "@ckb-ccc/core";
2
1
  import { AbortOptions, Connection, Libp2p, PeerId } from "@libp2p/interface";
2
+ import { ccc } from "@ckb-ccc/core";
3
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;
@@ -106,7 +109,10 @@ declare abstract class PairingService<Components extends PairingServiceComponent
106
109
  declare function pairingService<Components extends PairingServiceComponents = PairingServiceComponents>(config: PairingServiceConfig, canPair: PairingGuard<Components>): (components: Components) => PairingService<Components>;
107
110
  //#endregion
108
111
  //#region src/pairingEndpoint.d.ts
109
- declare class PairingEndpointRoleError extends Error {
112
+ declare class PairingEndpointError extends Error {
113
+ constructor(message: string, options?: ErrorOptions);
114
+ }
115
+ declare class PairingEndpointRoleError extends PairingEndpointError {
110
116
  readonly expectedRole: string;
111
117
  readonly actualRole: string | undefined;
112
118
  constructor(expectedRole: string, actualRole: string | undefined);
@@ -114,8 +120,8 @@ declare class PairingEndpointRoleError extends Error {
114
120
  declare function encodePairingEndpoint(endpointUrl: string, addresses: readonly Multiaddr[], secret: string, role?: string): Promise<string>;
115
121
  declare function decodePairingEndpoint(endpoint: string, expectedRole?: string): Promise<PairingTarget>;
116
122
  declare namespace barrel_d_exports {
117
- export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, encodePairingEndpoint, jsonRpcService, pairingService };
123
+ export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointError, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, dialKnownAddresses, encodePairingEndpoint, jsonRpcService, pairingService };
118
124
  }
119
125
  //#endregion
120
- export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, encodePairingEndpoint, jsonRpcService, pairingService, barrel_d_exports as t };
126
+ export { JsonRpcRequest, JsonRpcRequestHandler, JsonRpcService, JsonRpcServiceComponents, JsonRpcServiceConfig, JsonRpcTransportLibp2p, JsonRpcTransportLibp2pConfig, PairingEndpointError, PairingEndpointRoleError, PairingGuard, PairingService, PairingServiceComponents, PairingServiceConfig, PairingTarget, decodePairingEndpoint, dialKnownAddresses, encodePairingEndpoint, jsonRpcService, pairingService, barrel_d_exports as t };
121
127
  //# 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,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;;;KC1JlC;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;UAkBN;;;;KC7EE;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;UAMM;UAsBN;;iBASM,eACd,mBAAmB,2BAA2B,0BAE9C,QAAQ,sBACR,SAAS,aAAa,eACpB,YAAY,eAAe,eAAe;;;cCpVjC,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,6BAA6B;EAC5B,YAAA,iBAAiB,UAAU;;cAM5B,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({connection:t,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.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=x(e);throw r?.abort(t),t}return this.config.onResponse?.(i),i}async openStream(e){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(b)}};function b(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 x(e){return e instanceof Error?e:Error(`JSON-RPC request failed`)}const S=16384,C=[`addresses`,`role`,`secret`];var w=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 T(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=O(t);if(i.byteLength>S)throw Error(`Pairing endpoint address data is too large`);let a=await A(i,new CompressionStream(`deflate`)),o=l.ccc.bytesTo(a,`base64url`),s=new URL(e),{params:c,route:u}=D(s);return C.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 E(e,t){let{params:n}=D(new URL(e.trim()));if(t){let e=n.get(`role`)?.trim()||void 0;if(e!==t)throw new w(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 k(r),secret:i}}function D(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 O(e){return l.ccc.bytesConcat(...f.encode(e.map(e=>e.bytes)))}async function k(e){try{let t=await A(l.ccc.bytesFrom(e,`base64url`),new DecompressionStream(`deflate`),S),n=[];for(let e of f.decode([t],{maxDataLength:S})){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 A(e,t,n){let r=n===void 0?t.readable:t.readable.pipeThrough(j(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 j(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 M=1024;var N=class{constructor(e,t){if(this.components=e,this.config=t,this.currentSecret=U(),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 F(n,I(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 F(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:M}),i=z((await r.read()).subarray());if(i.type===`unpair`){this.removePairedPeer(t.remotePeer),await R(r,e,{ok:!0});return}if(i.secret!==this.secret||!this.canPair(t.remotePeer)){await R(r,e,{ok:!1,error:`Pairing rejected`});return}let a=V(i.addresses,t.remotePeer);this.rotateSecret(),this.addPairedPeer(t.remotePeer)&&(n=t.remotePeer),await R(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=U(),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=W(e,`Pairing operation failed`);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function P(e,t){return n=>new class extends N{canPair(e){return t.call(this,e)}}(n,e)}async function F(e,t,n){let r=(0,u.lpStream)(e,{maxDataLength:M});await r.write(l.ccc.bytesFrom(JSON.stringify(t),`utf8`),{signal:n.signal}),await e.close(n);let i=H((await r.read(n)).subarray());return await e.closeRead(n),i}function I(e,t,n){let r=n.map(e=>e.toString()),i={type:`pair`,secret:e,name:t,addresses:r};for(;r.length>0&&!L(i);)r.pop();if(L(i)||delete i.addresses,L(i)||delete i.name,!L(i))throw Error(`Pairing request is too large`);return i}function L(e){return l.ccc.bytesFrom(JSON.stringify(e),`utf8`).byteLength<=M}async function R(e,t,n){await e.write(l.ccc.bytesFrom(JSON.stringify(n),`utf8`)),await t.close()}function z(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:B(t.addresses)};if(t.type===`unpair`)return{type:`unpair`};throw Error(`Invalid pairing request`)}function B(e){if(Array.isArray(e))return e.filter(e=>typeof e==`string`&&e.length>0)}function V(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 H(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 U(){return l.ccc.bytesTo(crypto.getRandomValues(new Uint8Array(16)),`base64url`)}function W(e,t){return e instanceof Error?e:Error(t)}var G=o({JsonRpcService:()=>p,JsonRpcTransportLibp2p:()=>y,PairingEndpointRoleError:()=>w,PairingService:()=>N,decodePairingEndpoint:()=>E,encodePairingEndpoint:()=>T,jsonRpcService:()=>v,pairingService:()=>P});exports.JsonRpcService=p,exports.JsonRpcTransportLibp2p=y,exports.PairingEndpointRoleError=w,exports.PairingService=N,exports.decodePairingEndpoint=E,exports.encodePairingEndpoint=T,exports.jsonRpcService=v,exports.pairingService=P,Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return G}});
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(e,t),this.name=`PairingEndpointError`}},D=class extends E{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 O(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=j(t);if(i.byteLength>w)throw Error(`Pairing endpoint address data is too large`);let a=await N(i,new CompressionStream(`deflate`)),o=l.ccc.bytesTo(a,`base64url`),s=new URL(e),{params:c,route:u}=A(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 k(e,t){let n;try{n=new URL(e.trim())}catch(e){throw new E(`Pairing endpoint is not a valid URL`,{cause:e})}let{params:r}=A(n);if(t){let e=r.get(`role`)?.trim()||void 0;if(e!==t)throw new D(t,e)}let i=r.get(`addresses`)?.trim(),a=r.get(`secret`)?.trim();if(!i||!a)throw new E(`Pairing endpoint is incomplete`);return{addresses:await M(i),secret:a}}function A(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 j(e){return l.ccc.bytesConcat(...f.encode(e.map(e=>e.bytes)))}async function M(e){try{let t=await N(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 new E(`Pairing endpoint contains invalid compressed addresses`,{cause:e})}}async function N(e,t,n){let r=n===void 0?t.readable:t.readable.pipeThrough(P(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 P(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 F=1024;var I=class{constructor(e,t){if(this.components=e,this.config=t,this.currentSecret=K(),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 R(n,z(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 R(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:F}),i=H((await r.read()).subarray());if(i.type===`unpair`){this.removePairedPeer(t.remotePeer),await V(r,e,{ok:!0});return}if(i.secret!==this.secret||!this.canPair(t.remotePeer)){await V(r,e,{ok:!1,error:`Pairing rejected`});return}let a=W(i.addresses,t.remotePeer);this.rotateSecret(),this.addPairedPeer(t.remotePeer)&&(n=t.remotePeer),await V(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=K(),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=q(e,`Pairing operation failed`);return t?.abort(n),this.errorListeners.forEach(e=>e(n)),n}};function L(e,t){return n=>new class extends I{canPair(e){return t.call(this,e)}}(n,e)}async function R(e,t,n){let r=(0,u.lpStream)(e,{maxDataLength:F});await r.write(l.ccc.bytesFrom(JSON.stringify(t),`utf8`),{signal:n.signal}),await e.close(n);let i=G((await r.read(n)).subarray());return await e.closeRead(n),i}function z(e,t,n){let r=n.map(e=>e.toString()),i={type:`pair`,secret:e,name:t,addresses:r};for(;r.length>0&&!B(i);)r.pop();if(B(i)||delete i.addresses,B(i)||delete i.name,!B(i))throw Error(`Pairing request is too large`);return i}function B(e){return l.ccc.bytesFrom(JSON.stringify(e),`utf8`).byteLength<=F}async function V(e,t,n){await e.write(l.ccc.bytesFrom(JSON.stringify(n),`utf8`)),await t.close()}function H(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:U(t.addresses)};if(t.type===`unpair`)return{type:`unpair`};throw Error(`Invalid pairing request`)}function U(e){if(Array.isArray(e))return e.filter(e=>typeof e==`string`&&e.length>0)}function W(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 G(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 K(){return l.ccc.bytesTo(crypto.getRandomValues(new Uint8Array(16)),`base64url`)}function q(e,t){return e instanceof Error?e:Error(t)}var J=o({JsonRpcService:()=>h,JsonRpcTransportLibp2p:()=>x,PairingEndpointError:()=>E,PairingEndpointRoleError:()=>D,PairingService:()=>I,decodePairingEndpoint:()=>k,dialKnownAddresses:()=>p,encodePairingEndpoint:()=>O,jsonRpcService:()=>b,pairingService:()=>L});exports.JsonRpcService=h,exports.JsonRpcTransportLibp2p=x,exports.PairingEndpointError=E,exports.PairingEndpointRoleError=D,exports.PairingService=I,exports.decodePairingEndpoint=k,exports.dialKnownAddresses=p,exports.encodePairingEndpoint=O,exports.jsonRpcService=b,exports.pairingService=L,Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return J}});
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","multiaddr","CODE_P2P"],"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 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\";\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 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 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 \"./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 PairingEndpointError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"PairingEndpointError\";\n }\n}\n\nexport class PairingEndpointRoleError extends PairingEndpointError {\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 let url: URL;\n try {\n url = new URL(endpoint.trim());\n } catch (cause) {\n throw new PairingEndpointError(\"Pairing endpoint is not a valid URL\", {\n cause,\n });\n }\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 PairingEndpointError(\"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 PairingEndpointError(\n \"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.1.0",
3
+ "version": "0.2.1",
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.1"
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
+ }
@@ -1,6 +1,7 @@
1
1
  import { ccc } from "@ckb-ccc/core";
2
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;
@@ -76,6 +77,10 @@ export class JsonRpcTransportLibp2p implements ccc.JsonRpcTransport {
76
77
  }
77
78
 
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
+
79
84
  for (const connection of this.connections()) {
80
85
  try {
81
86
  return await connection.newStream(this.config.protocol, {
@@ -7,7 +7,14 @@ const MAX_DECOMPRESSED_ADDRESSES_LENGTH = 16 * 1024;
7
7
  const MAX_ADDRESSES = 16;
8
8
  const PAIRING_PARAMETER_NAMES = ["addresses", "role", "secret"] as const;
9
9
 
10
- export class PairingEndpointRoleError extends Error {
10
+ export class PairingEndpointError extends Error {
11
+ constructor(message: string, options?: ErrorOptions) {
12
+ super(message, options);
13
+ this.name = "PairingEndpointError";
14
+ }
15
+ }
16
+
17
+ export class PairingEndpointRoleError extends PairingEndpointError {
11
18
  constructor(
12
19
  readonly expectedRole: string,
13
20
  readonly actualRole: string | undefined,
@@ -66,7 +73,14 @@ export async function decodePairingEndpoint(
66
73
  endpoint: string,
67
74
  expectedRole?: string,
68
75
  ): Promise<PairingTarget> {
69
- const url = new URL(endpoint.trim());
76
+ let url: URL;
77
+ try {
78
+ url = new URL(endpoint.trim());
79
+ } catch (cause) {
80
+ throw new PairingEndpointError("Pairing endpoint is not a valid URL", {
81
+ cause,
82
+ });
83
+ }
70
84
  const { params } = fragmentParameters(url);
71
85
  if (expectedRole) {
72
86
  const actualRole = params.get("role")?.trim() || undefined;
@@ -78,7 +92,7 @@ export async function decodePairingEndpoint(
78
92
  const secret = params.get("secret")?.trim();
79
93
 
80
94
  if (!compressedAddresses || !secret) {
81
- throw new Error("Pairing endpoint is incomplete");
95
+ throw new PairingEndpointError("Pairing endpoint is incomplete");
82
96
  }
83
97
 
84
98
  const addresses = await decodeCompressedAddresses(compressedAddresses);
@@ -128,9 +142,10 @@ async function decodeCompressedAddresses(value: string): Promise<Multiaddr[]> {
128
142
  }
129
143
  return addresses;
130
144
  } catch (cause) {
131
- throw new Error("Pairing endpoint contains invalid compressed addresses", {
132
- cause,
133
- });
145
+ throw new PairingEndpointError(
146
+ "Pairing endpoint contains invalid compressed addresses",
147
+ { cause },
148
+ );
134
149
  }
135
150
  }
136
151
 
@@ -307,6 +307,18 @@ export abstract class PairingService<
307
307
 
308
308
  private createPeerTimeout(peerId: PeerId) {
309
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
+
310
322
  this.removePairedPeer(peerId);
311
323
  }, this.config.pairedPeerTimeoutMs);
312
324
  }