Comparing version 1.11.1 to 1.12.1
@@ -17,3 +17,3 @@ import * as PerfData from './perf-data'; | ||
requestsHistory: string[]; | ||
shRelays?: string[]; | ||
relayShortIds?: string[]; | ||
requests: Map<string, PerfData.PerfData>; | ||
@@ -20,0 +20,0 @@ infoFail?: boolean; |
@@ -39,2 +39,3 @@ import * as DPapi from './dp-api'; | ||
* @param measureRPClatency - determine duration of actual RPC request from exit node, populates response stats | ||
* @param headers - provide additional headers used for requests, e.g. authentication headers | ||
*/ | ||
@@ -55,6 +56,8 @@ export type Ops = { | ||
readonly measureRPClatency?: boolean; | ||
readonly headers?: Record<string, string>; | ||
}; | ||
/** | ||
* Overridable parameters per request. | ||
* See **Ops** for details. | ||
* See **Ops** for other params details | ||
* @param headers - will be merged with provided headers during construction | ||
*/ | ||
@@ -65,2 +68,3 @@ export type RequestOps = { | ||
readonly measureRPClatency?: boolean; | ||
readonly headers?: Record<string, string>; | ||
}; | ||
@@ -98,2 +102,3 @@ /** | ||
* See **RequestOps** for overridable options. | ||
* Returns a **Response.SendError** on error. | ||
*/ | ||
@@ -120,2 +125,3 @@ send: (req: JRPC.Request, ops?: RequestOps) => Promise<Response.Response>; | ||
private stats; | ||
private errHeaders; | ||
} |
@@ -112,5 +112,6 @@ "use strict"; | ||
* See **RequestOps** for overridable options. | ||
* Returns a **Response.SendError** on error. | ||
*/ | ||
this.send = async (req, ops) => { | ||
this.populateChainIds(ops?.provider); | ||
this.populateChainIds(ops?.provider, ops?.headers); | ||
return this.doSend(req, ops); | ||
@@ -122,5 +123,7 @@ }; | ||
return new Promise(async (resolve, reject) => { | ||
const provider = this.determineProvider(reqOps, req); | ||
const headers = this.determineHeaders(provider, this.ops.mevKickbackAddress, ops?.headers); | ||
// sanity check provider url | ||
if (!Utils.isValidURL(reqOps.provider)) { | ||
return reject('Cannot parse provider URL'); | ||
return reject(new Response.SendError('Cannot parse provider URL', provider, this.errHeaders(headers))); | ||
} | ||
@@ -130,3 +133,3 @@ // sanity check mev protection provider url, if it is set | ||
if (!Utils.isValidURL(this.ops.mevProtectionProvider)) { | ||
return reject('Cannot parse mevProtectionProvider URL'); | ||
return reject(new Response.SendError('Cannot parse mevProtectionProvider URL', provider, this.errHeaders(headers))); | ||
} | ||
@@ -137,9 +140,7 @@ } | ||
log.error('Error finding node pair', err); | ||
return reject(`Could not find node pair in ${reqOps.timeout} ms`); | ||
return reject(new Response.SendError(`Could not find node pair in ${reqOps.timeout} ms`, provider, this.errHeaders(headers))); | ||
}); | ||
if (!resNodes) { | ||
return reject('Unexpected code flow - should never be here'); | ||
return reject(new Response.SendError('Unexpected code flow - should never be here', provider, this.errHeaders(headers))); | ||
} | ||
const provider = this.determineProvider(reqOps, req); | ||
const headers = this.determineHeaders(provider, this.ops.mevKickbackAddress); | ||
// create request | ||
@@ -168,3 +169,3 @@ const { entryNode, exitNode, counterOffset } = resNodes; | ||
log.error('error creating request', resReq.error); | ||
return reject('Unable to create request object'); | ||
return reject(new Response.SendError('Unable to create request object', provider, this.errHeaders(headers))); | ||
} | ||
@@ -176,3 +177,3 @@ // split request to segments | ||
if (failMsg) { | ||
return reject(failMsg); | ||
return reject(new Response.SendError(failMsg, provider, this.errHeaders(headers))); | ||
} | ||
@@ -183,3 +184,3 @@ // set request expiration timer | ||
this.removeRequest(request); | ||
return reject('Request timed out'); | ||
return reject(new Response.SendError('Request timed out', provider, this.errHeaders(headers))); | ||
}, reqOps.timeout); | ||
@@ -239,3 +240,3 @@ // track request | ||
log.info('no fallback for resending request available'); | ||
return cacheEntry.reject('No fallback node pair to retry sending request'); | ||
return cacheEntry.reject(new Response.SendError('No fallback node pair to retry sending request', origReq.provider, this.errHeaders(origReq.headers))); | ||
} | ||
@@ -269,3 +270,3 @@ this.redoRequests.add(origReq.id); | ||
log.error('error creating fallback request', resReq.error); | ||
return cacheEntry.reject('Unable to create fallback request object'); | ||
return cacheEntry.reject(new Response.SendError('Unable to create fallback request object', origReq.provider, this.errHeaders(origReq.headers))); | ||
} | ||
@@ -278,3 +279,3 @@ // split request to segments | ||
this.removeRequest(request); | ||
return cacheEntry.reject(failMsg); | ||
return cacheEntry.reject(new Response.SendError(failMsg, request.provider, this.errHeaders(request.headers))); | ||
} | ||
@@ -317,3 +318,3 @@ // track request | ||
this.removeRequest(request); | ||
return cacheEntry.reject('Sending message failed'); | ||
return cacheEntry.reject(new Response.SendError('Sending message failed', request.provider, this.errHeaders(request.headers))); | ||
}); | ||
@@ -357,13 +358,9 @@ }; | ||
const firstSeg = entry.segments.get(0); | ||
if (!firstSeg.body.startsWith('0x')) { | ||
log.info('message is not a response', firstSeg.requestId); | ||
return; | ||
} | ||
const reqEntry = this.requestCache.get(firstSeg.requestId); | ||
const { request, session } = reqEntry; | ||
RequestCache.remove(this.requestCache, request.id); | ||
const hexResp = SegmentCache.toMessage(entry); | ||
const respData = ethers_1.utils.arrayify(hexResp); | ||
const msgData = SegmentCache.toMessage(entry); | ||
const msgBytes = Utils.base64ToBytes(msgData); | ||
const resUnbox = Response.messageToResp({ | ||
respData, | ||
respData: msgBytes, | ||
request, | ||
@@ -379,4 +376,5 @@ session, | ||
log.error('error extracting message', error); | ||
this.nodesColl.requestFailed(reqEntry.request); | ||
return reqEntry.reject('Unable to process response'); | ||
const request = reqEntry.request; | ||
this.nodesColl.requestFailed(request); | ||
return reqEntry.reject(new Response.SendError('Unable to process response', request.provider, this.errHeaders(request.headers))); | ||
}; | ||
@@ -392,5 +390,5 @@ this.responseSuccess = ({ resp }, reqEntry) => { | ||
const r = { | ||
status: 200, | ||
text: () => new Promise((r) => r(JSON.stringify(resp.resp))), | ||
json: () => Promise.resolve(resp.resp), | ||
status: resp.status, | ||
text: async () => resp.text ?? '', | ||
json: async () => JSON.parse(resp.text ?? ''), // will fail to parse if no text (as expected) | ||
}; | ||
@@ -404,19 +402,8 @@ if (request.measureRPClatency) { | ||
const counter = reqEntry.session.updatedTS; | ||
return reject(`Message out of counter range. Exit node expected message counter near ${resp.now} - request got ${counter}.`); | ||
return reject(new Response.SendError(`Message out of counter range. Exit node expected message counter near ${resp.counter} - request got ${counter}.`, request.provider, this.errHeaders(request.headers))); | ||
} | ||
case Payload.RespType.DuplicateFail: | ||
return reject('Message duplicate error. Exit node rejected already processed message'); | ||
case Payload.RespType.HttpError: { | ||
const r = { | ||
status: resp.status, | ||
text: () => Promise.resolve(resp.text), | ||
json: () => new Promise((r) => r(JSON.parse(resp.text))), | ||
}; | ||
if (request.measureRPClatency) { | ||
r.stats = stats; | ||
} | ||
return resolve(r); | ||
} | ||
return reject(new Response.SendError('Message duplicate error. Exit node rejected already processed message', request.provider, this.errHeaders(request.headers))); | ||
case Payload.RespType.Error: | ||
return reject(`Error attempting JSON RPC call: ${resp.reason}`); | ||
return reject(new Response.SendError(`Error attempting JSON RPC call: ${resp.reason}`, request.provider, this.errHeaders(request.headers))); | ||
} | ||
@@ -453,2 +440,3 @@ }; | ||
measureRPClatency, | ||
headers: ops.headers, | ||
}; | ||
@@ -466,6 +454,6 @@ }; | ||
}; | ||
this.fetchChainId = async (provider) => { | ||
const req = JRPC.chainId(provider); | ||
this.fetchChainId = async (provider, headers, starknet) => { | ||
const req = JRPC.chainId(provider, starknet); | ||
// fetch request through RPCh | ||
const res = await this.doSend(req, { provider }).catch((err) => log.warn('error fetching chainId for %s: %s[%o]', provider, JSON.stringify(err), err)); | ||
const res = await this.doSend(req, { provider, headers }).catch((err) => log.warn('error fetching chainId for %s: %s[%o]', provider, JSON.stringify(err), err)); | ||
if (!res) { | ||
@@ -488,8 +476,16 @@ return; | ||
if (JRPC.isError(jrpc)) { | ||
log.warn('jrpc error response for chainId request to %s: %s', provider, JSON.stringify(jrpc.error)); | ||
if (jrpc.error.code === -32601 || | ||
jrpc.error.message.toLowerCase().includes('method not found')) { | ||
// try chainId on starknet | ||
if (!starknet) { | ||
this.fetchChainId(provider, headers, true); | ||
} | ||
} | ||
else { | ||
log.warn('jrpc error response for chainId request to %s: %s', provider, JSON.stringify(jrpc.error)); | ||
} | ||
} | ||
else { | ||
const id = parseInt(jrpc.result, 16); | ||
log.info('determined chain id %d for %s', id, provider); | ||
this.chainIds.set(provider, id); | ||
log.info('determined chain id %s for %s', jrpc.result, provider); | ||
this.chainIds.set(provider, jrpc.result); | ||
} | ||
@@ -510,11 +506,25 @@ } | ||
const cId = this.chainIds.get(provider); | ||
if (cId !== 1) { | ||
return provider; | ||
if (cId === '0x1' || (cId && parseInt(cId) === 1)) { | ||
return this.ops.mevProtectionProvider; | ||
} | ||
return this.ops.mevProtectionProvider; | ||
return provider; | ||
}; | ||
this.determineHeaders = (provider, mevKickbackAddress) => { | ||
this.determineHeaders = (provider, mevKickbackAddress, headers) => { | ||
// if we provide headers we need to provide all of them | ||
// pHTTP exit app will only set content type application/json if there are no headers | ||
if (provider === RPC_PROPELLORHEADS && mevKickbackAddress) { | ||
return { 'X-Tx-Origin': mevKickbackAddress }; | ||
return { | ||
'X-Tx-Origin': mevKickbackAddress, | ||
'Content-Type': 'application/json', | ||
...this.ops.headers, | ||
...headers, | ||
}; | ||
} | ||
if (headers || this.ops.headers) { | ||
return { | ||
'Content-Type': 'application/json', | ||
...this.ops.headers, | ||
...headers, | ||
}; | ||
} | ||
}; | ||
@@ -527,3 +537,3 @@ this.determineHops = (forceZeroHop) => { | ||
}; | ||
this.populateChainIds = (provider) => { | ||
this.populateChainIds = (provider, headers) => { | ||
if (!provider) { | ||
@@ -535,3 +545,3 @@ return; | ||
} | ||
this.fetchChainId(provider); | ||
this.fetchChainId(provider, headers); | ||
}; | ||
@@ -576,13 +586,13 @@ this.checkSegmentLimit = (segLength) => { | ||
if (request.measureRPClatency && | ||
'rDur' in resp && | ||
'eDur' in resp && | ||
resp.rDur && | ||
resp.eDur) { | ||
const rpcDur = resp.rDur; | ||
const exitNodeDur = resp.eDur; | ||
const hoprDur = responseTime - rpcDur - exitNodeDur - segDur; | ||
'callDuration' in resp && | ||
'exitAppDuration' in resp && | ||
resp.callDuration && | ||
resp.exitAppDuration) { | ||
const rpcDur = resp.callDuration; | ||
const exitAppDur = resp.exitAppDuration; | ||
const hoprDur = responseTime - rpcDur - exitAppDur - segDur; | ||
return { | ||
segDur, | ||
rpcDur, | ||
exitNodeDur, | ||
exitAppDur, | ||
hoprDur, | ||
@@ -593,2 +603,5 @@ }; | ||
}; | ||
this.errHeaders = (headers) => { | ||
return { ...headers, 'Content-Type': 'application/json' }; | ||
}; | ||
this.ops = this.sdkOps(ops); | ||
@@ -601,3 +614,3 @@ (this.ops.debugScope || this.ops.logLevel) && | ||
this.nodesColl = new nodes_collector_1.default(this.ops.discoveryPlatformEndpoint, this.clientId, ApplicationTag, this.onMessages, this.onVersions, this.hops, this.ops.forceManualRelaying); | ||
this.fetchChainId(this.ops.provider); | ||
this.fetchChainId(this.ops.provider, this.ops.headers); | ||
log.info('RPCh SDK[v%s] started', version_1.default); | ||
@@ -604,0 +617,0 @@ } |
@@ -22,3 +22,3 @@ export type Request = { | ||
}; | ||
export declare function chainId(id: string): Request; | ||
export declare function chainId(id: string, starknet?: boolean): Request; | ||
export declare function isError(r: Response): r is Error; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isError = exports.chainId = void 0; | ||
function chainId(id) { | ||
function chainId(id, starknet) { | ||
return { | ||
jsonrpc: '2.0', | ||
method: 'eth_chainId', | ||
method: starknet ? 'starknet_chainId' : 'eth_chainId', | ||
id, | ||
@@ -9,0 +9,0 @@ params: [], |
@@ -24,2 +24,3 @@ import WebSocket = require('isomorphic-ws'); | ||
lastSeen: number; | ||
lastSeenLatency: number; | ||
quality: number; | ||
@@ -26,0 +27,0 @@ backoff: number; |
@@ -37,3 +37,3 @@ "use strict"; | ||
const body = JSON.stringify(payload); | ||
return fetch(url, { method: 'POST', headers, body }).then((res) => res.json()); | ||
return fetch(url, { method: 'POST', headers, body, signal: AbortSignal.timeout(30000) }).then((res) => res.json()); | ||
} | ||
@@ -48,3 +48,3 @@ exports.sendMessage = sendMessage; | ||
}; | ||
return fetch(url, { headers }).then((res) => res.json()); | ||
return fetch(url, { headers, signal: AbortSignal.timeout(30000) }).then((res) => res.json()); | ||
} | ||
@@ -60,3 +60,3 @@ exports.version = version; | ||
const body = JSON.stringify({ tag }); | ||
return fetch(url, { method: 'POST', headers, body }).then((res) => { | ||
return fetch(url, { method: 'POST', headers, body, signal: AbortSignal.timeout(30000) }).then((res) => { | ||
return res.json(); | ||
@@ -75,3 +75,3 @@ }); | ||
return new Promise((resolve, reject) => { | ||
return fetch(url, { method: 'DELETE', headers }).then((res) => { | ||
return fetch(url, { method: 'DELETE', headers, signal: AbortSignal.timeout(30000) }).then((res) => { | ||
if (res.status === 204) { | ||
@@ -92,3 +92,3 @@ return resolve(); | ||
}; | ||
return fetch(url, { headers }).then((res) => { | ||
return fetch(url, { headers, signal: AbortSignal.timeout(30000) }).then((res) => { | ||
return res.json(); | ||
@@ -106,3 +106,3 @@ }); | ||
}; | ||
return fetch(url, { headers }).then((res) => res.json()); | ||
return fetch(url, { headers, signal: AbortSignal.timeout(30000) }).then((res) => res.json()); | ||
} | ||
@@ -118,3 +118,3 @@ exports.getPeers = getPeers; | ||
}; | ||
return fetch(url, { headers }).then((res) => res.json()); | ||
return fetch(url, { headers, signal: AbortSignal.timeout(30000) }).then((res) => res.json()); | ||
} | ||
@@ -129,3 +129,3 @@ exports.getAllChannels = getAllChannels; | ||
}; | ||
return fetch(url, { headers }).then((res) => res.json()); | ||
return fetch(url, { headers, signal: AbortSignal.timeout(30000) }).then((res) => res.json()); | ||
} | ||
@@ -132,0 +132,0 @@ exports.getNodeChannels = getNodeChannels; |
@@ -39,3 +39,3 @@ "use strict"; | ||
const InfoResponseTimeout = 10e3; // 10s | ||
const RelayNodesCompatVersions = ['2.0.6', '2.0.7']; | ||
const RelayNodesCompatVersions = ['2.0.6', '2.0.7', '2.1']; | ||
function create(entryNode, exitNodes, applicationTag, messageListener, hops, forceManualRelaying) { | ||
@@ -291,4 +291,4 @@ const entryData = EntryData.create(); | ||
} | ||
const { peerId, version, counter, shRelays } = resDec.res; | ||
const nodeLog = ExitNode.prettyPrint(peerId, version, counter, shRelays); | ||
const { peerId, version, counter, relayShortIds } = resDec.res; | ||
const nodeLog = ExitNode.prettyPrint(peerId, version, counter, relayShortIds); | ||
const exitNode = np.exitNodes.get(peerId); | ||
@@ -308,3 +308,3 @@ if (!exitNode) { | ||
exitData.infoFail = false; | ||
exitData.shRelays = shRelays; | ||
exitData.relayShortIds = relayShortIds; | ||
EntryData.removeOngoingInfo(np.entryData); | ||
@@ -311,0 +311,0 @@ const t = np.infoTimeouts.get(peerId); |
@@ -31,3 +31,3 @@ "use strict"; | ||
const utils_1 = require("./utils"); | ||
const ExitNodesCompatVersions = ['1.']; | ||
const ExitNodesCompatVersions = ['2.']; | ||
/** | ||
@@ -167,9 +167,9 @@ * Try to distribute evenly with best route pairs preferred. | ||
} | ||
if (!xd.shRelays) { | ||
if (!xd.relayShortIds) { | ||
return []; | ||
} | ||
const shRelays = xd.shRelays; | ||
const relayShortIds = xd.relayShortIds; | ||
const relays = np.relays.filter((rId) => rId !== xId && rId !== np.entryNode.id); | ||
const reqRelayPeerId = (0, utils_1.randomEl)(relays); | ||
const respRelays = np.peers.filter((pId) => pId !== xId && shRelays.find((shId) => pId.endsWith(shId))); | ||
const respRelays = np.peers.filter((pId) => pId !== xId && relayShortIds.find((shId) => pId.endsWith(shId))); | ||
const respRelayPeerId = (0, utils_1.randomEl)(respRelays); | ||
@@ -176,0 +176,0 @@ return [reqRelayPeerId, respRelayPeerId]; |
@@ -1,11 +0,11 @@ | ||
import * as JRPC from './jrpc'; | ||
import * as Res from './result'; | ||
export type ReqPayload = { | ||
clientId: string; | ||
provider: string; | ||
req: JRPC.Request; | ||
endpoint: string; | ||
body?: string; | ||
headers?: Record<string, string>; | ||
method?: string; | ||
hops?: number; | ||
relayPeerId?: string; | ||
wDur?: boolean; | ||
withDuration?: boolean; | ||
}; | ||
@@ -16,22 +16,16 @@ export declare enum RespType { | ||
DuplicateFail = 2, | ||
HttpError = 3, | ||
Error = 4 | ||
Error = 3 | ||
} | ||
export type RespPayload = { | ||
type: RespType.Resp; | ||
resp: JRPC.Response; | ||
rDur?: number; | ||
eDur?: number; | ||
status: number; | ||
text?: string; | ||
callDuration?: number; | ||
exitAppDuration?: number; | ||
} | { | ||
type: RespType.CounterFail; | ||
now: number; | ||
counter: number; | ||
} | { | ||
type: RespType.DuplicateFail; | ||
} | { | ||
type: RespType.HttpError; | ||
status: number; | ||
text: string; | ||
rDur?: number; | ||
eDur?: number; | ||
} | { | ||
type: RespType.Error; | ||
@@ -44,3 +38,3 @@ reason: string; | ||
counter: number; | ||
shRelays?: string[]; | ||
relayShortIds?: string[]; | ||
}; | ||
@@ -47,0 +41,0 @@ export declare function encodeReq(payload: ReqPayload): Res.Result<string>; |
@@ -37,8 +37,8 @@ "use strict"; | ||
RespType[RespType["DuplicateFail"] = 2] = "DuplicateFail"; | ||
RespType[RespType["HttpError"] = 3] = "HttpError"; | ||
RespType[RespType["Error"] = 4] = "Error"; | ||
RespType[RespType["Error"] = 3] = "Error"; | ||
})(RespType || (exports.RespType = RespType = {})); | ||
function encodeReq(payload) { | ||
const t = reqToTrans(payload); | ||
try { | ||
const res = lz_string_1.default.compressToUTF16(JSON.stringify(payload)); | ||
const res = lz_string_1.default.compressToUTF16(JSON.stringify(t)); | ||
return Res.ok(res); | ||
@@ -54,3 +54,3 @@ } | ||
const res = JSON.parse(lz_string_1.default.decompressFromUTF16(payload)); | ||
return Res.ok(res); | ||
return Res.ok(transToReq(res)); | ||
} | ||
@@ -63,4 +63,5 @@ catch (ex) { | ||
function encodeResp(payload) { | ||
const t = respToTrans(payload); | ||
try { | ||
const res = lz_string_1.default.compressToUTF16(JSON.stringify(payload)); | ||
const res = lz_string_1.default.compressToUTF16(JSON.stringify(t)); | ||
return Res.ok(res); | ||
@@ -76,3 +77,3 @@ } | ||
const res = JSON.parse(lz_string_1.default.decompressFromUTF16(payload)); | ||
return Res.ok(res); | ||
return Res.ok(transToResp(res)); | ||
} | ||
@@ -85,4 +86,5 @@ catch (ex) { | ||
function encodeInfo(payload) { | ||
const t = infoToTrans(payload); | ||
try { | ||
const res = lz_string_1.default.compressToUTF16(JSON.stringify(payload)); | ||
const res = lz_string_1.default.compressToUTF16(JSON.stringify(t)); | ||
return Res.ok(res); | ||
@@ -98,3 +100,3 @@ } | ||
const res = JSON.parse(lz_string_1.default.decompressFromUTF16(payload)); | ||
return Res.ok(res); | ||
return Res.ok(transToInfo(res)); | ||
} | ||
@@ -106,1 +108,119 @@ catch (ex) { | ||
exports.decodeInfo = decodeInfo; | ||
function reqToTrans(r) { | ||
const t = { | ||
c: r.clientId, | ||
e: r.endpoint, | ||
}; | ||
if (r.body) { | ||
t.b = r.body; | ||
} | ||
if (r.headers) { | ||
t.h = r.headers; | ||
} | ||
if (r.method) { | ||
t.m = r.method; | ||
} | ||
if (r.hops) { | ||
t.n = r.hops; | ||
} | ||
if (r.relayPeerId) { | ||
t.r = r.relayPeerId; | ||
} | ||
if (r.withDuration) { | ||
t.w = r.withDuration; | ||
} | ||
return t; | ||
} | ||
function respToTrans(r) { | ||
switch (r.type) { | ||
case RespType.Resp: { | ||
const t = { | ||
t: RespType.Resp, | ||
s: r.status, | ||
}; | ||
if (r.text) { | ||
t.x = r.text; | ||
} | ||
if (r.callDuration) { | ||
t.f = r.callDuration; | ||
} | ||
if (r.exitAppDuration) { | ||
t.e = r.exitAppDuration; | ||
} | ||
return t; | ||
} | ||
case RespType.CounterFail: | ||
return { | ||
t: RespType.CounterFail, | ||
c: r.counter, | ||
}; | ||
case RespType.DuplicateFail: { | ||
return { | ||
t: RespType.DuplicateFail, | ||
}; | ||
} | ||
case RespType.Error: | ||
return { | ||
t: RespType.Error, | ||
r: r.reason, | ||
}; | ||
} | ||
} | ||
function infoToTrans(r) { | ||
const t = { | ||
i: r.peerId, | ||
v: r.version, | ||
c: r.counter, | ||
}; | ||
if (r.relayShortIds) { | ||
t.r = r.relayShortIds; | ||
} | ||
return t; | ||
} | ||
function transToReq(t) { | ||
return { | ||
clientId: t.c, | ||
endpoint: t.e, | ||
body: t.b, | ||
headers: t.h, | ||
method: t.m, | ||
hops: t.n, | ||
relayPeerId: t.r, | ||
withDuration: t.w, | ||
}; | ||
} | ||
function transToResp(t) { | ||
switch (t.t) { | ||
case RespType.Resp: | ||
return { | ||
type: RespType.Resp, | ||
status: t.s, | ||
text: t.x, | ||
callDuration: t.f, | ||
exitAppDuration: t.e, | ||
}; | ||
case RespType.CounterFail: | ||
return { | ||
type: RespType.CounterFail, | ||
counter: t.c, | ||
}; | ||
case RespType.DuplicateFail: { | ||
return { | ||
type: RespType.DuplicateFail, | ||
}; | ||
} | ||
case RespType.Error: | ||
return { | ||
type: RespType.Error, | ||
reason: t.r, | ||
}; | ||
} | ||
} | ||
function transToInfo(t) { | ||
return { | ||
peerId: t.i, | ||
version: t.v, | ||
counter: t.c, | ||
relayShortIds: t.r, | ||
}; | ||
} |
import * as compatCrypto from '@rpch/compat-crypto'; | ||
import type { Request } from './request'; | ||
import type { Response } from './response'; | ||
import * as Request from './request'; | ||
import * as Response from './response'; | ||
export type Cache = Map<string, Entry>; | ||
export type Entry = { | ||
request: Request; | ||
resolve: (res: Response) => void; | ||
reject: (error: string) => void; | ||
request: Request.Request; | ||
resolve: (res: Response.Response) => void; | ||
reject: (error: Response.SendError) => void; | ||
session: compatCrypto.Session; | ||
@@ -17,5 +17,5 @@ timer: ReturnType<typeof setTimeout>; | ||
export declare function add(cache: Cache, { request, resolve, reject, session, timer, }: { | ||
request: Request; | ||
resolve: (res: Response) => void; | ||
reject: (error: string) => void; | ||
request: Request.Request; | ||
resolve: (res: Response.Response) => void; | ||
reject: (error: Response.SendError) => void; | ||
session: compatCrypto.Session; | ||
@@ -22,0 +22,0 @@ timer: ReturnType<typeof setTimeout>; |
@@ -60,46 +60,4 @@ "use strict"; | ||
function generateId(_cache) { | ||
if (crypto && 'randomUUID' in crypto) { | ||
return crypto.randomUUID(); | ||
} | ||
return fallbackUUID(); | ||
return crypto.randomUUID(); | ||
} | ||
exports.generateId = generateId; | ||
/** | ||
* Fast UUID generator, RFC4122 version 4 compliant. | ||
* @author Jeff Ward (jcward.com). | ||
* @license MIT license | ||
* @link http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 | ||
**/ | ||
let lut; | ||
function fallbackUUID() { | ||
if (!lut) { | ||
lut = []; | ||
for (let i = 0; i < 256; i++) { | ||
lut[i] = (i < 16 ? '0' : '') + i.toString(16); | ||
} | ||
} | ||
const d0 = (Math.random() * 0xffffffff) | 0; | ||
const d1 = (Math.random() * 0xffffffff) | 0; | ||
const d2 = (Math.random() * 0xffffffff) | 0; | ||
const d3 = (Math.random() * 0xffffffff) | 0; | ||
return (lut[d0 & 0xff] + | ||
lut[(d0 >> 8) & 0xff] + | ||
lut[(d0 >> 16) & 0xff] + | ||
lut[(d0 >> 24) & 0xff] + | ||
'-' + | ||
lut[d1 & 0xff] + | ||
lut[(d1 >> 8) & 0xff] + | ||
'-' + | ||
lut[((d1 >> 16) & 0x0f) | 0x40] + | ||
lut[(d1 >> 24) & 0xff] + | ||
'-' + | ||
lut[(d2 & 0x3f) | 0x80] + | ||
lut[(d2 >> 8) & 0xff] + | ||
'-' + | ||
lut[(d2 >> 16) & 0xff] + | ||
lut[(d2 >> 24) & 0xff] + | ||
lut[d3 & 0xff] + | ||
lut[(d3 >> 8) & 0xff] + | ||
lut[(d3 >> 16) & 0xff] + | ||
lut[(d3 >> 24) & 0xff]); | ||
} |
@@ -28,7 +28,6 @@ "use strict"; | ||
const compatCrypto = __importStar(require("@rpch/compat-crypto")); | ||
const ethers_1 = require("ethers"); | ||
const Res = __importStar(require("./result")); | ||
const Payload = __importStar(require("./payload")); | ||
const Segment = __importStar(require("./segment")); | ||
const utils_1 = require("./utils"); | ||
const Utils = __importStar(require("./utils")); | ||
/** | ||
@@ -39,12 +38,11 @@ * Creates a request and compresses its payload. | ||
const payload = { | ||
provider, | ||
endpoint: provider, | ||
clientId, | ||
req, | ||
body: JSON.stringify(req), | ||
headers, | ||
method: 'POST', | ||
hops, | ||
relayPeerId: respRelayPeerId, | ||
withDuration: measureRPClatency, | ||
}; | ||
if (measureRPClatency) { | ||
payload.wDur = true; | ||
} | ||
const resEncode = Payload.encodeReq(payload); | ||
@@ -54,5 +52,8 @@ if (Res.isErr(resEncode)) { | ||
} | ||
const data = ethers_1.utils.toUtf8Bytes(resEncode.res); | ||
const dataJSON = JSON.stringify(payload); | ||
const textEnc = new TextEncoder(); | ||
const data8b = textEnc.encode(dataJSON); | ||
const resBox = compatCrypto.boxRequest({ | ||
message: data, | ||
message: data8b, | ||
// message: data, | ||
exitPeerId, | ||
@@ -99,11 +100,13 @@ uuid: id, | ||
} | ||
const msg = ethers_1.utils.toUtf8String(resUnbox.session.request); | ||
const resDecode = Payload.decodeReq(msg); | ||
if (Res.isErr(resDecode)) { | ||
return resDecode; | ||
const msg = Utils.bytesToString(resUnbox.session.request); | ||
try { | ||
const reqPayload = JSON.parse(msg); | ||
return Res.ok({ | ||
reqPayload, | ||
session: resUnbox.session, | ||
}); | ||
} | ||
return Res.ok({ | ||
reqPayload: resDecode.res, | ||
session: resUnbox.session, | ||
}); | ||
catch (ex) { | ||
return Res.err(`Error during JSON parsing: ${ex.toString()}`); | ||
} | ||
} | ||
@@ -116,7 +119,7 @@ exports.messageToReq = messageToReq; | ||
// we need the entry id ouside of of the actual encrypted payload | ||
const entryIdData = ethers_1.utils.toUtf8Bytes(req.entryPeerId); | ||
const reqData = session.request; | ||
const hexEntryId = ethers_1.utils.hexlify(entryIdData); | ||
const hexData = ethers_1.utils.hexlify(reqData); | ||
const body = `${hexEntryId},${hexData}`; | ||
const pIdBytes = Utils.stringToBytes(req.entryPeerId); | ||
const body = new Uint8Array(pIdBytes.length + reqData.length); | ||
body.set(pIdBytes); | ||
body.set(reqData, pIdBytes.length); | ||
return Segment.toSegments(req.id, body); | ||
@@ -129,7 +132,7 @@ } | ||
function prettyPrint(req) { | ||
const eId = (0, utils_1.shortPeerId)(req.entryPeerId); | ||
const xId = (0, utils_1.shortPeerId)(req.exitPeerId); | ||
const eId = Utils.shortPeerId(req.entryPeerId); | ||
const xId = Utils.shortPeerId(req.exitPeerId); | ||
const path = [`e${eId}`]; | ||
if (req.reqRelayPeerId) { | ||
path.push(`r${(0, utils_1.shortPeerId)(req.reqRelayPeerId)}`); | ||
path.push(`r${Utils.shortPeerId(req.reqRelayPeerId)}`); | ||
} | ||
@@ -141,3 +144,3 @@ else if (req.hops !== 0) { | ||
if (req.respRelayPeerId) { | ||
path.push(`r${(0, utils_1.shortPeerId)(req.respRelayPeerId)}`); | ||
path.push(`r${Utils.shortPeerId(req.respRelayPeerId)}`); | ||
} | ||
@@ -144,0 +147,0 @@ const id = req.id; |
@@ -10,3 +10,3 @@ import * as compatCrypto from '@rpch/compat-crypto'; | ||
* rpcDur - duration of RPC request from exit node | ||
* exitNodeDur - approximate execution duration up to encrypting and compressing response itself | ||
* exitAppDur - approximate execution duration up to encrypting and compressing response itself | ||
* hoprDur - estimated duration of segments during request - response cycle inside hopr network | ||
@@ -17,3 +17,3 @@ */ | ||
rpcDur: number; | ||
exitNodeDur: number; | ||
exitAppDur: number; | ||
hoprDur: number; | ||
@@ -29,2 +29,7 @@ } | { | ||
}; | ||
export declare class SendError extends Error { | ||
readonly provider: string; | ||
readonly reqHeaders: Record<string, string>; | ||
constructor(message: string, provider: string, reqHeaders: Record<string, string>); | ||
} | ||
export type UnboxResponse = { | ||
@@ -39,3 +44,3 @@ resp: Payload.RespPayload; | ||
unboxSession: compatCrypto.Session; | ||
}): Res.Result<string>; | ||
}): Res.Result<Uint8Array>; | ||
export declare function messageToResp({ respData, request, session, }: { | ||
@@ -42,0 +47,0 @@ respData: Uint8Array; |
@@ -26,13 +26,22 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.messageToResp = exports.respToMessage = void 0; | ||
exports.messageToResp = exports.respToMessage = exports.SendError = void 0; | ||
const compatCrypto = __importStar(require("@rpch/compat-crypto")); | ||
const ethers_1 = require("ethers"); | ||
const Payload = __importStar(require("./payload")); | ||
const Res = __importStar(require("./result")); | ||
const Utils = __importStar(require("./utils")); | ||
class SendError extends Error { | ||
constructor(message, provider, reqHeaders) { | ||
super(message); | ||
this.provider = provider; | ||
this.reqHeaders = reqHeaders; | ||
this.name = 'SendError'; | ||
} | ||
} | ||
exports.SendError = SendError; | ||
function respToMessage({ requestId, entryPeerId, respPayload, unboxSession, }) { | ||
const resEncode = Payload.encodeResp(respPayload); | ||
if (Res.isErr(resEncode)) { | ||
return resEncode; | ||
} | ||
const data = ethers_1.utils.toUtf8Bytes(resEncode.res); | ||
// const resEncode = Payload.encodeResp(respPayload); | ||
// if (Res.isErr(resEncode)) { | ||
// return resEncode; | ||
// } | ||
const dataJSON = JSON.stringify(respPayload); | ||
const data = Utils.stringToBytes(dataJSON); | ||
const resBox = compatCrypto.boxResponse(unboxSession, { | ||
@@ -49,4 +58,3 @@ uuid: requestId, | ||
} | ||
const hexData = ethers_1.utils.hexlify(resBox.session.response); | ||
return Res.ok(hexData); | ||
return Res.ok(resBox.session.response); | ||
} | ||
@@ -66,12 +74,14 @@ exports.respToMessage = respToMessage; | ||
} | ||
const msg = ethers_1.utils.toUtf8String(resUnbox.session.response); | ||
const resDecode = Payload.decodeResp(msg); | ||
if (Res.isErr(resDecode)) { | ||
return resDecode; | ||
const msg = Utils.bytesToString(resUnbox.session.response); | ||
try { | ||
const resp = JSON.parse(msg); | ||
return Res.ok({ | ||
resp, | ||
session: resUnbox.session, | ||
}); | ||
} | ||
return Res.ok({ | ||
session: resUnbox.session, | ||
resp: resDecode.res, | ||
}); | ||
catch (ex) { | ||
return Res.err(`Error during JSON parsing: ${ex.toString()}`); | ||
} | ||
} | ||
exports.messageToResp = messageToResp; |
@@ -12,3 +12,3 @@ import * as Res from './result'; | ||
*/ | ||
export declare function toSegments(requestId: string, hexData: string): Segment[]; | ||
export declare function toSegments(requestId: string, data: Uint8Array): Segment[]; | ||
/** | ||
@@ -15,0 +15,0 @@ * Create segment from string message. |
@@ -32,16 +32,23 @@ "use strict"; | ||
exports.MaxSegmentBody = MaxBytes - 17; | ||
function bytesToBase64(bytes) { | ||
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join(''); | ||
return btoa(binString); | ||
} | ||
/** | ||
* Slice data into segments. | ||
*/ | ||
function toSegments(requestId, hexData) { | ||
const chunks = []; | ||
for (let i = 0; i < hexData.length; i += exports.MaxSegmentBody) { | ||
chunks.push(hexData.slice(i, i + exports.MaxSegmentBody)); | ||
function toSegments(requestId, data) { | ||
const dataString = bytesToBase64(data); | ||
const totalCount = Math.ceil(dataString.length / MaxBytes); | ||
const segments = []; | ||
for (let i = 0; i < totalCount; i++) { | ||
const body = dataString.slice(i * MaxBytes, (i + 1) * MaxBytes); | ||
segments.push({ | ||
requestId, | ||
nr: i, | ||
totalCount, | ||
body, | ||
}); | ||
} | ||
return chunks.map((c, nr) => ({ | ||
requestId, | ||
nr, | ||
totalCount: chunks.length, | ||
body: c, | ||
})); | ||
return segments; | ||
} | ||
@@ -48,0 +55,0 @@ exports.toSegments = toSegments; |
@@ -14,2 +14,6 @@ import debug from 'debug'; | ||
export declare function isValidURL(url: string): boolean; | ||
export declare function bytesToString(arr: Uint8Array): string; | ||
export declare function stringToBytes(str: string): Uint8Array; | ||
export declare function bytesToBase64(bytes: Uint8Array): string; | ||
export declare function base64ToBytes(base64: string): Uint8Array; | ||
export declare function logger(namespaces: string[]): { | ||
@@ -16,0 +20,0 @@ error: debug.Debugger; |
@@ -29,5 +29,7 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.setDebugScopeLevel = exports.setDebugScope = exports.versionCompare = exports.logger = exports.isValidURL = exports.average = exports.randomIdx = exports.randomEl = exports.shortPeerId = exports.VrsnCmp = void 0; | ||
exports.setDebugScopeLevel = exports.setDebugScope = exports.versionCompare = exports.logger = exports.base64ToBytes = exports.bytesToBase64 = exports.stringToBytes = exports.bytesToString = exports.isValidURL = exports.average = exports.randomIdx = exports.randomEl = exports.shortPeerId = exports.VrsnCmp = void 0; | ||
const debug_1 = __importDefault(require("debug")); | ||
const Res = __importStar(require("./result")); | ||
const textDecoder = new TextDecoder('utf-8'); | ||
const textEncoder = new TextEncoder(); | ||
var VrsnCmp; | ||
@@ -75,2 +77,20 @@ (function (VrsnCmp) { | ||
exports.isValidURL = isValidURL; | ||
function bytesToString(arr) { | ||
return textDecoder.decode(arr); | ||
} | ||
exports.bytesToString = bytesToString; | ||
function stringToBytes(str) { | ||
return textEncoder.encode(str); | ||
} | ||
exports.stringToBytes = stringToBytes; | ||
function bytesToBase64(bytes) { | ||
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join(''); | ||
return btoa(binString); | ||
} | ||
exports.bytesToBase64 = bytesToBase64; | ||
function base64ToBytes(base64) { | ||
const binString = atob(base64); | ||
return Uint8Array.from(binString, (m) => m.codePointAt(0)); | ||
} | ||
exports.base64ToBytes = base64ToBytes; | ||
function logger(namespaces) { | ||
@@ -77,0 +97,0 @@ namespaces.unshift('rpch'); |
@@ -1,2 +0,2 @@ | ||
declare const _default: "1.11.1"; | ||
declare const _default: "1.12.1"; | ||
export default _default; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = '1.11.1'; | ||
exports.default = '1.12.1'; |
# @rpch/sdk | ||
## 1.11.1 | ||
## 1.12.1 | ||
### Patch Changes | ||
- 04cc52e: fallback to non crypto uuid generation | ||
- b924905: Fixed latency stats naming | ||
## 1.12.0 | ||
### Minor Changes | ||
- f085c2c: Update transmission protocol to work with private HTTP | ||
- 907e34b: Allow arbitrary headers on construction and during requests. | ||
Try chainId fetching from starknet if eth fetching fails. | ||
### Patch Changes | ||
- f085c2c: Add sane timeout values to all node related communication | ||
- c507632: Enhance chainId parsing to allow hex and dec numbers | ||
- 3f3d6a9: Fix provider and request header tracking to reflect actual usage | ||
## 1.11.0 | ||
@@ -10,0 +24,0 @@ |
{ | ||
"name": "@rpch/sdk", | ||
"version": "1.11.1", | ||
"version": "1.12.1", | ||
"license": "LGPL-3.0", | ||
@@ -5,0 +5,0 @@ "main": "./build/index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
151069
3685
49