Compare commits
2 Commits
62a1f9c05f
...
0216b0d7ba
Author | SHA1 | Date |
---|---|---|
Derrick Hammer | 0216b0d7ba | |
Derrick Hammer | 50f21f0434 |
11
README.md
11
README.md
|
@ -1,10 +1 @@
|
||||||
# dht-rpc-client
|
# rpc-client
|
||||||
A client library that uses hypercore and the https://github.com/LumeWeb/relay server along with Skynet for web, to perform `Wisdom of the crowd` RPC requests.
|
|
||||||
|
|
||||||
This enables access to blockchain RPC without running a node, and socializes the cost of access to RPC from use of services such as https://pokt.network
|
|
||||||
|
|
||||||
As demand grows for users, so should the community. It is expected that both businesses operating on web3 and community members donating/supporting in altruism will ensure the upkeep of this dht.
|
|
||||||
|
|
||||||
It is the projects hope that blockchains will evolve in the future such that much of this infrastructure becomes unneeded and RPC can be done directly with light clients. This would also need to support over Websockets like how Webtorrent works.
|
|
||||||
|
|
||||||
As very few blockchains actually support this and for use with decentralized nodes, this type of dht/technology is required for mainstream adoption.
|
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
export declare const ERR_MAX_TRIES_HIT = "ERR_MAX_TRIES_HIT";
|
|
||||||
export declare const ERR_INVALID_SIGNATURE = "INVALID_SIGNATURE";
|
export declare const ERR_INVALID_SIGNATURE = "INVALID_SIGNATURE";
|
||||||
export declare const ERR_NO_RELAYS = "NO_RELAYS";
|
|
||||||
//# sourceMappingURL=error.d.ts.map
|
//# sourceMappingURL=error.d.ts.map
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AACrD,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AACzD,eAAO,MAAM,aAAa,cAAc,CAAC"}
|
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,sBAAsB,CAAC"}
|
|
@ -1,3 +1 @@
|
||||||
export const ERR_MAX_TRIES_HIT = "ERR_MAX_TRIES_HIT";
|
|
||||||
export const ERR_INVALID_SIGNATURE = "INVALID_SIGNATURE";
|
export const ERR_INVALID_SIGNATURE = "INVALID_SIGNATURE";
|
||||||
export const ERR_NO_RELAYS = "NO_RELAYS";
|
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import RpcNetworkQueryFactory from "./query/index.js";
|
import RpcNetworkQueryFactory from "./query/index.js";
|
||||||
export default class RpcNetwork {
|
export default class RpcNetwork {
|
||||||
constructor(dht?: any);
|
private _relaysAvailablePromise?;
|
||||||
|
private _relaysAvailableResolve?;
|
||||||
|
constructor(swarm?: any);
|
||||||
|
private _methods;
|
||||||
|
get methods(): Map<string, Set<string>>;
|
||||||
private _factory;
|
private _factory;
|
||||||
get factory(): RpcNetworkQueryFactory;
|
get factory(): RpcNetworkQueryFactory;
|
||||||
private _dht;
|
private _swarm;
|
||||||
get dht(): any;
|
get swarm(): any;
|
||||||
private _majorityThreshold;
|
private _majorityThreshold;
|
||||||
get majorityThreshold(): number;
|
get majorityThreshold(): number;
|
||||||
set majorityThreshold(value: number);
|
set majorityThreshold(value: number);
|
||||||
|
@ -15,17 +19,16 @@ export default class RpcNetwork {
|
||||||
get relayTimeout(): number;
|
get relayTimeout(): number;
|
||||||
set relayTimeout(value: number);
|
set relayTimeout(value: number);
|
||||||
private _relays;
|
private _relays;
|
||||||
get relays(): string[];
|
get relays(): Map<string, string[]>;
|
||||||
private _ready?;
|
private _ready?;
|
||||||
get ready(): Promise<void>;
|
get ready(): Promise<void>;
|
||||||
|
get readyWithRelays(): Promise<void>;
|
||||||
private _bypassCache;
|
private _bypassCache;
|
||||||
get bypassCache(): boolean;
|
get bypassCache(): boolean;
|
||||||
set bypassCache(value: boolean);
|
set bypassCache(value: boolean);
|
||||||
private _maxRelays;
|
getAvailableRelay(module: string, method: string): string;
|
||||||
get maxRelays(): number;
|
getRelay(pubkey: string): any;
|
||||||
set maxRelays(value: number);
|
private init;
|
||||||
addRelay(pubkey: string): void;
|
private setupRelayPromise;
|
||||||
removeRelay(pubkey: string): boolean;
|
|
||||||
clearRelays(): void;
|
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=network.d.ts.map
|
//# sourceMappingURL=network.d.ts.map
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AAUA,OAAO,sBAAsB,MAAM,kBAAkB,CAAC;AAEtD,MAAM,CAAC,OAAO,OAAO,UAAU;gBACjB,GAAG,MAAY;IAI3B,OAAO,CAAC,QAAQ,CAAoC;IAEpD,IAAI,OAAO,IAAI,sBAAsB,CAEpC;IAED,OAAO,CAAC,IAAI,CAAa;IAEzB,IAAI,GAAG,QAEN;IAED,OAAO,CAAC,kBAAkB,CAAQ;IAElC,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED,IAAI,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAElC;IAED,OAAO,CAAC,aAAa,CAAM;IAE3B,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,YAAY,CAAC,KAAK,EAAE,MAAM,EAE7B;IAED,OAAO,CAAC,aAAa,CAAK;IAE1B,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,YAAY,CAAC,KAAK,EAAE,MAAM,EAE7B;IAED,OAAO,CAAC,OAAO,CAAgB;IAE/B,IAAI,MAAM,IAAI,MAAM,EAAE,CAErB;IAED,OAAO,CAAC,MAAM,CAAC,CAAgB;IAE/B,IAAI,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAMzB;IAED,OAAO,CAAC,YAAY,CAAkB;IAEtC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,OAAO,EAE7B;IAED,OAAO,CAAC,UAAU,CAAa;IAE/B,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAE1B;IAEM,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK9B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAWpC,WAAW,IAAI,IAAI;CAG3B"}
|
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AAEA,OAAO,sBAAsB,MAAM,kBAAkB,CAAC;AAItD,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B,OAAO,CAAC,uBAAuB,CAAC,CAAgB;IAChD,OAAO,CAAC,uBAAuB,CAAC,CAAW;gBAC/B,KAAK,MAAmB;IAKpC,OAAO,CAAC,QAAQ,CAA4D;IAE5E,IAAI,OAAO,IAAI,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAEtC;IAED,OAAO,CAAC,QAAQ,CAAoC;IAEpD,IAAI,OAAO,IAAI,sBAAsB,CAEpC;IAED,OAAO,CAAC,MAAM,CAAoB;IAElC,IAAI,KAAK,QAER;IAED,OAAO,CAAC,kBAAkB,CAAQ;IAElC,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED,IAAI,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAElC;IAED,OAAO,CAAC,aAAa,CAAM;IAE3B,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,YAAY,CAAC,KAAK,EAAE,MAAM,EAE7B;IAED,OAAO,CAAC,aAAa,CAAK;IAE1B,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,YAAY,CAAC,KAAK,EAAE,MAAM,EAE7B;IAED,OAAO,CAAC,OAAO,CAAiD;IAEhE,IAAI,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAElC;IAED,OAAO,CAAC,MAAM,CAAC,CAAgB;IAE/B,IAAI,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAMzB;IAED,IAAI,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnC;IAED,OAAO,CAAC,YAAY,CAAkB;IAEtC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,OAAO,EAE7B;IAEM,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAYhD,QAAQ,CAAC,MAAM,EAAE,MAAM;IAQ9B,OAAO,CAAC,IAAI;IAyCZ,OAAO,CAAC,iBAAiB;CAK1B"}
|
|
@ -1,17 +1,26 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import DHT from "@hyperswarm/dht";
|
import Hyperswarm from "hyperswarm";
|
||||||
import RpcNetworkQueryFactory from "./query/index.js";
|
import RpcNetworkQueryFactory from "./query/index.js";
|
||||||
|
import b4a from "b4a";
|
||||||
|
import { createHash } from "./util.js";
|
||||||
export default class RpcNetwork {
|
export default class RpcNetwork {
|
||||||
constructor(dht = new DHT()) {
|
_relaysAvailablePromise;
|
||||||
this._dht = dht;
|
_relaysAvailableResolve;
|
||||||
|
constructor(swarm = new Hyperswarm()) {
|
||||||
|
this._swarm = swarm;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
_methods = new Map();
|
||||||
|
get methods() {
|
||||||
|
return this._methods;
|
||||||
}
|
}
|
||||||
_factory = new RpcNetworkQueryFactory(this);
|
_factory = new RpcNetworkQueryFactory(this);
|
||||||
get factory() {
|
get factory() {
|
||||||
return this._factory;
|
return this._factory;
|
||||||
}
|
}
|
||||||
_dht;
|
_swarm;
|
||||||
get dht() {
|
get swarm() {
|
||||||
return this._dht;
|
return this._swarm;
|
||||||
}
|
}
|
||||||
_majorityThreshold = 0.75;
|
_majorityThreshold = 0.75;
|
||||||
get majorityThreshold() {
|
get majorityThreshold() {
|
||||||
|
@ -34,17 +43,20 @@ export default class RpcNetwork {
|
||||||
set relayTimeout(value) {
|
set relayTimeout(value) {
|
||||||
this._relayTimeout = value;
|
this._relayTimeout = value;
|
||||||
}
|
}
|
||||||
_relays = [];
|
_relays = new Map();
|
||||||
get relays() {
|
get relays() {
|
||||||
return this._relays;
|
return this._relays;
|
||||||
}
|
}
|
||||||
_ready;
|
_ready;
|
||||||
get ready() {
|
get ready() {
|
||||||
if (!this._ready) {
|
if (!this._ready) {
|
||||||
this._ready = this._dht.ready();
|
this._ready = this._swarm.dht.ready();
|
||||||
}
|
}
|
||||||
return this._ready;
|
return this._ready;
|
||||||
}
|
}
|
||||||
|
get readyWithRelays() {
|
||||||
|
return this.ready.then(() => this._relaysAvailablePromise);
|
||||||
|
}
|
||||||
_bypassCache = false;
|
_bypassCache = false;
|
||||||
get bypassCache() {
|
get bypassCache() {
|
||||||
return this._bypassCache;
|
return this._bypassCache;
|
||||||
|
@ -52,26 +64,55 @@ export default class RpcNetwork {
|
||||||
set bypassCache(value) {
|
set bypassCache(value) {
|
||||||
this._bypassCache = value;
|
this._bypassCache = value;
|
||||||
}
|
}
|
||||||
_maxRelays = 0;
|
getAvailableRelay(module, method) {
|
||||||
get maxRelays() {
|
method = `${module}.${method}`;
|
||||||
return this._maxRelays;
|
let relays = this._methods.get(method) ?? new Set();
|
||||||
}
|
if (!relays.size) {
|
||||||
set maxRelays(value) {
|
throw Error("no available relay");
|
||||||
this._maxRelays = value;
|
|
||||||
}
|
|
||||||
addRelay(pubkey) {
|
|
||||||
this._relays.push(pubkey);
|
|
||||||
this._relays = [...new Set(this._relays)];
|
|
||||||
}
|
|
||||||
removeRelay(pubkey) {
|
|
||||||
if (!this._relays.includes(pubkey)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
delete this._relays[this._relays.indexOf(pubkey)];
|
return Array.from(relays)[Math.floor(Math.random() * relays.size)];
|
||||||
this._relays = Object.values(this._relays);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
clearRelays() {
|
getRelay(pubkey) {
|
||||||
this._relays = [];
|
if (this._relays.has(pubkey)) {
|
||||||
|
return this._relays.get(pubkey);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
init() {
|
||||||
|
this._swarm.join(createHash("lumeweb"));
|
||||||
|
this.setupRelayPromise();
|
||||||
|
this._swarm.on("connection", async (relay) => {
|
||||||
|
const query = this._factory.simple({
|
||||||
|
relay,
|
||||||
|
query: { module: "core", method: "get_methods", data: null },
|
||||||
|
});
|
||||||
|
const resp = await query.result;
|
||||||
|
const pubkey = b4a.from(relay.remotePublicKey).toString("hex");
|
||||||
|
if (resp.data) {
|
||||||
|
this._relays.set(pubkey, relay);
|
||||||
|
resp.data.forEach((item) => {
|
||||||
|
const methods = this._methods.get(item) ?? new Set();
|
||||||
|
methods.add(pubkey);
|
||||||
|
this._methods.set(item, methods);
|
||||||
|
});
|
||||||
|
this._relaysAvailableResolve?.();
|
||||||
|
}
|
||||||
|
relay.on("close", () => {
|
||||||
|
this._methods.forEach((item) => {
|
||||||
|
if (item.has(pubkey)) {
|
||||||
|
item.delete(pubkey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.relays.delete(pubkey);
|
||||||
|
if (!this._relays.size) {
|
||||||
|
this.setupRelayPromise();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setupRelayPromise() {
|
||||||
|
this._relaysAvailablePromise = new Promise((resolve) => {
|
||||||
|
this._relaysAvailableResolve = resolve;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,15 @@ export default abstract class RpcQueryBase {
|
||||||
protected _response?: RPCResponse;
|
protected _response?: RPCResponse;
|
||||||
protected _error?: string;
|
protected _error?: string;
|
||||||
protected _promiseResolve?: (data: any) => void;
|
protected _promiseResolve?: (data: any) => void;
|
||||||
constructor(
|
constructor({
|
||||||
network: RpcNetwork,
|
network,
|
||||||
query: ClientRPCRequest | RPCRequest,
|
query,
|
||||||
options?: RpcQueryOptions
|
options,
|
||||||
);
|
}: {
|
||||||
|
network: RpcNetwork;
|
||||||
|
query: ClientRPCRequest | RPCRequest;
|
||||||
|
options: RpcQueryOptions;
|
||||||
|
});
|
||||||
get result(): Promise<RPCResponse>;
|
get result(): Promise<RPCResponse>;
|
||||||
protected handeTimeout(): void;
|
protected handeTimeout(): void;
|
||||||
protected resolve(data?: RPCResponse, timeout?: boolean): void;
|
protected resolve(data?: RPCResponse, timeout?: boolean): void;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/query/base.ts"],"names":[],"mappings":";AAIA,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,WAAW,EACZ,MAAM,sBAAsB,CAAC;AAI9B,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,YAAY;IACxC,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC;IAC7B,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC;IAEpC,SAAS,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,SAAS,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC;IAC9B,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAS;IACpC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAS;IACtC,SAAS,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC;IAClC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;gBAG9C,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,gBAAgB,GAAG,UAAU,EACpC,OAAO,GAAE,eAAoB;IAO/B,IAAI,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,CAEjC;IAED,SAAS,CAAC,YAAY;IAItB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,OAAO,GAAE,OAAe,GAAG,IAAI;IAc9D,GAAG,IAAI,IAAI;YAmBJ,MAAM;IASpB,SAAS,CAAC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC,OAAO;IAO7D,SAAS,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI;cAEf,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,UAAU;CAyBvD"}
|
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/query/base.ts"],"names":[],"mappings":";AACA,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,WAAW,EACZ,MAAM,sBAAsB,CAAC;AAE9B,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,YAAY;IACxC,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC;IAC7B,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC;IAEpC,SAAS,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,SAAS,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC;IAC9B,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAS;IACpC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAS;IACtC,SAAS,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC;IAClC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;gBAEpC,EACV,OAAO,EACP,KAAK,EACL,OAAY,GACb,EAAE;QACD,OAAO,EAAE,UAAU,CAAC;QACpB,KAAK,EAAE,gBAAgB,GAAG,UAAU,CAAC;QACrC,OAAO,EAAE,eAAe,CAAC;KAC1B;IAMD,IAAI,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,CAEjC;IAED,SAAS,CAAC,YAAY;IAItB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,OAAO,GAAE,OAAe,GAAG,IAAI;IAc9D,GAAG,IAAI,IAAI;YAmBJ,MAAM;IASpB,SAAS,CAAC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC,OAAO;IAO7D,SAAS,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI;cAEf,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,UAAU;CAyBvD"}
|
|
@ -10,7 +10,7 @@ export default class RpcQueryBase {
|
||||||
_response;
|
_response;
|
||||||
_error;
|
_error;
|
||||||
_promiseResolve;
|
_promiseResolve;
|
||||||
constructor(network, query, options = {}) {
|
constructor({ network, query, options = {}, }) {
|
||||||
this._network = network;
|
this._network = network;
|
||||||
this._query = query;
|
this._query = query;
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
|
|
@ -4,12 +4,17 @@ import { RpcQueryOptions } from "../types.js";
|
||||||
import SimpleRpcQuery from "./simple.js";
|
import SimpleRpcQuery from "./simple.js";
|
||||||
export default class ClearCacheRpcQuery extends SimpleRpcQuery {
|
export default class ClearCacheRpcQuery extends SimpleRpcQuery {
|
||||||
protected _relays: string[];
|
protected _relays: string[];
|
||||||
constructor(
|
constructor({
|
||||||
network: RpcNetwork,
|
network,
|
||||||
relays: string[],
|
relays,
|
||||||
query: RPCRequest,
|
query,
|
||||||
options: RpcQueryOptions
|
options,
|
||||||
);
|
}: {
|
||||||
|
network: RpcNetwork;
|
||||||
|
relays: string[];
|
||||||
|
query: RPCRequest;
|
||||||
|
options: RpcQueryOptions;
|
||||||
|
});
|
||||||
protected _run(): Promise<void>;
|
protected _run(): Promise<void>;
|
||||||
protected queryRelay(): Promise<any>;
|
protected queryRelay(): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"clearCache.d.ts","sourceRoot":"","sources":["../../src/query/clearCache.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,EAAuB,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,cAAc,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,cAAc;IAC5D,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBAG1B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,eAAe;cAMV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;cAQrB,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC;CAc3C"}
|
{"version":3,"file":"clearCache.d.ts","sourceRoot":"","sources":["../../src/query/clearCache.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,EAAuB,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,cAAc,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,cAAc;IAC5D,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBAEhB,EACV,OAAO,EACP,MAAM,EACN,KAAK,EACL,OAAO,GACR,EAAE;QACD,OAAO,EAAE,UAAU,CAAC;QACpB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,KAAK,EAAE,UAAU,CAAC;QAClB,OAAO,EAAE,eAAe,CAAC;KAC1B;cAKe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;cAOrB,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC;CAiB3C"}
|
|
@ -1,21 +1,19 @@
|
||||||
import { hashQuery } from "../util.js";
|
import { hashQuery } from "../util.js";
|
||||||
import { getActiveRelay, setupRelay } from "../sharedRelay.js";
|
|
||||||
import SimpleRpcQuery from "./simple.js";
|
import SimpleRpcQuery from "./simple.js";
|
||||||
export default class ClearCacheRpcQuery extends SimpleRpcQuery {
|
export default class ClearCacheRpcQuery extends SimpleRpcQuery {
|
||||||
_relays;
|
_relays;
|
||||||
constructor(network, relays, query, options) {
|
constructor({ network, relays, query, options, }) {
|
||||||
super(network, "", query, options);
|
super({ network, relay: "", query, options });
|
||||||
this._relays = relays;
|
this._relays = relays;
|
||||||
}
|
}
|
||||||
async _run() {
|
async _run() {
|
||||||
await setupRelay(this._network);
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this._relay = getActiveRelay().stream.remotePublicKey;
|
this._relay = getActiveRelay().stream.remotePublicKey;
|
||||||
await this.queryRelay();
|
await this.queryRelay();
|
||||||
await this.checkResponses();
|
await this.checkResponses();
|
||||||
}
|
}
|
||||||
async queryRelay() {
|
async queryRelay() {
|
||||||
return this.queryRpc(getActiveRelay(), {
|
return this.queryRpc(this._network.getAvailableRelay("rpc", "broadcast_request"), {
|
||||||
module: "rpc",
|
module: "rpc",
|
||||||
method: "broadcast_request",
|
method: "broadcast_request",
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { ClientRPCRequest, RPCRequest } from "@lumeweb/relay-types";
|
import { ClientRPCRequest, RPCRequest } from "@lumeweb/relay-types";
|
||||||
import { RpcQueryOptions } from "../types.js";
|
import { RpcQueryOptions } from "../types.js";
|
||||||
import WisdomRpcQuery from "./wisdom.js";
|
|
||||||
import SimpleRpcQuery from "./simple.js";
|
import SimpleRpcQuery from "./simple.js";
|
||||||
import ClearCacheRpcQuery from "./clearCache.js";
|
import ClearCacheRpcQuery from "./clearCache.js";
|
||||||
import RpcNetwork from "../network.js";
|
import RpcNetwork from "../network.js";
|
||||||
|
@ -8,13 +7,6 @@ import RpcQueryBase from "./base.js";
|
||||||
export default class RpcNetworkQueryFactory {
|
export default class RpcNetworkQueryFactory {
|
||||||
private _network;
|
private _network;
|
||||||
constructor(network: RpcNetwork);
|
constructor(network: RpcNetwork);
|
||||||
wisdom({
|
|
||||||
query,
|
|
||||||
options,
|
|
||||||
}: {
|
|
||||||
query: ClientRPCRequest;
|
|
||||||
options?: RpcQueryOptions;
|
|
||||||
}): WisdomRpcQuery;
|
|
||||||
simple({
|
simple({
|
||||||
relay,
|
relay,
|
||||||
query,
|
query,
|
||||||
|
@ -34,5 +26,5 @@ export default class RpcNetworkQueryFactory {
|
||||||
options?: RpcQueryOptions;
|
options?: RpcQueryOptions;
|
||||||
}): ClearCacheRpcQuery;
|
}): ClearCacheRpcQuery;
|
||||||
}
|
}
|
||||||
export { RpcNetwork, RpcQueryBase, SimpleRpcQuery, WisdomRpcQuery };
|
export { RpcNetwork, RpcQueryBase, SimpleRpcQuery };
|
||||||
//# sourceMappingURL=index.d.ts.map
|
//# sourceMappingURL=index.d.ts.map
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,cAAc,MAAM,aAAa,CAAC;AACzC,OAAO,cAAc,MAAM,aAAa,CAAC;AACzC,OAAO,kBAAkB,MAAM,iBAAiB,CAAC;AACjD,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,YAAY,MAAM,WAAW,CAAC;AAErC,MAAM,CAAC,OAAO,OAAO,sBAAsB;IACzC,OAAO,CAAC,QAAQ,CAAa;gBAEjB,OAAO,EAAE,UAAU;IAI/B,MAAM,CAAC,EACL,KAAK,EACL,OAAY,GACb,EAAE;QACD,KAAK,EAAE,gBAAgB,CAAC;QACxB,OAAO,CAAC,EAAE,eAAe,CAAC;KAC3B,GAAG,cAAc;IAWlB,MAAM,CAAC,EACL,KAAK,EACL,KAAK,EACL,OAAY,GACb,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,gBAAgB,CAAC;QACxB,OAAO,CAAC,EAAE,eAAe,CAAC;KAC3B,GAAG,cAAc;IAYlB,UAAU,CAAC,EACT,MAAM,EACN,KAAK,EACL,OAAY,GACb,EAAE;QACD,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,KAAK,EAAE,UAAU,CAAC;QAClB,OAAO,CAAC,EAAE,eAAe,CAAC;KAC3B,GAAG,kBAAkB;CAGvB;AAED,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC"}
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,cAAc,MAAM,aAAa,CAAC;AACzC,OAAO,kBAAkB,MAAM,iBAAiB,CAAC;AACjD,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,YAAY,MAAM,WAAW,CAAC;AAErC,MAAM,CAAC,OAAO,OAAO,sBAAsB;IACzC,OAAO,CAAC,QAAQ,CAAa;gBAEjB,OAAO,EAAE,UAAU;IAI/B,MAAM,CAAC,EACL,KAAK,EACL,KAAK,EACL,OAAY,GACb,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,gBAAgB,CAAC;QACxB,OAAO,CAAC,EAAE,eAAe,CAAC;KAC3B,GAAG,cAAc;IAYlB,UAAU,CAAC,EACT,MAAM,EACN,KAAK,EACL,OAAY,GACb,EAAE;QACD,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,KAAK,EAAE,UAAU,CAAC;QAClB,OAAO,CAAC,EAAE,eAAe,CAAC;KAC3B,GAAG,kBAAkB;CAQvB;AAED,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC"}
|
|
@ -1,4 +1,3 @@
|
||||||
import WisdomRpcQuery from "./wisdom.js";
|
|
||||||
import SimpleRpcQuery from "./simple.js";
|
import SimpleRpcQuery from "./simple.js";
|
||||||
import ClearCacheRpcQuery from "./clearCache.js";
|
import ClearCacheRpcQuery from "./clearCache.js";
|
||||||
import RpcNetwork from "../network.js";
|
import RpcNetwork from "../network.js";
|
||||||
|
@ -8,20 +7,24 @@ export default class RpcNetworkQueryFactory {
|
||||||
constructor(network) {
|
constructor(network) {
|
||||||
this._network = network;
|
this._network = network;
|
||||||
}
|
}
|
||||||
wisdom({ query, options = {}, }) {
|
|
||||||
return new WisdomRpcQuery(this._network, {
|
|
||||||
...query,
|
|
||||||
bypassCache: query.bypassCache || this._network.bypassCache,
|
|
||||||
}, options).run();
|
|
||||||
}
|
|
||||||
simple({ relay, query, options = {}, }) {
|
simple({ relay, query, options = {}, }) {
|
||||||
return new SimpleRpcQuery(this._network, relay, {
|
return new SimpleRpcQuery({
|
||||||
...query,
|
network: this._network,
|
||||||
bypassCache: query.bypassCache || this._network.bypassCache,
|
relay,
|
||||||
}, options).run();
|
query: {
|
||||||
|
...query,
|
||||||
|
bypassCache: query?.bypassCache || this._network.bypassCache,
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
}).run();
|
||||||
}
|
}
|
||||||
clearCache({ relays, query, options = {}, }) {
|
clearCache({ relays, query, options = {}, }) {
|
||||||
return new ClearCacheRpcQuery(this._network, relays, query, options).run();
|
return new ClearCacheRpcQuery({
|
||||||
|
network: this._network,
|
||||||
|
query,
|
||||||
|
relays,
|
||||||
|
options,
|
||||||
|
}).run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export { RpcNetwork, RpcQueryBase, SimpleRpcQuery, WisdomRpcQuery };
|
export { RpcNetwork, RpcQueryBase, SimpleRpcQuery };
|
||||||
|
|
|
@ -3,14 +3,19 @@ import { ClientRPCRequest } from "@lumeweb/relay-types";
|
||||||
import { RpcQueryOptions } from "../types.js";
|
import { RpcQueryOptions } from "../types.js";
|
||||||
import RpcQueryBase from "./base.js";
|
import RpcQueryBase from "./base.js";
|
||||||
export default class SimpleRpcQuery extends RpcQueryBase {
|
export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
protected _relay: string;
|
protected _relay?: string | any;
|
||||||
protected _query: ClientRPCRequest;
|
protected _query: ClientRPCRequest;
|
||||||
constructor(
|
constructor({
|
||||||
network: RpcNetwork,
|
network,
|
||||||
relay: string,
|
relay,
|
||||||
query: ClientRPCRequest,
|
query,
|
||||||
options: RpcQueryOptions
|
options,
|
||||||
);
|
}: {
|
||||||
|
network: RpcNetwork;
|
||||||
|
relay?: string | any;
|
||||||
|
query: ClientRPCRequest;
|
||||||
|
options: RpcQueryOptions;
|
||||||
|
});
|
||||||
protected _run(): Promise<void>;
|
protected _run(): Promise<void>;
|
||||||
protected queryRelay(): Promise<any>;
|
protected queryRelay(): Promise<any>;
|
||||||
protected checkResponses(): Promise<void>;
|
protected checkResponses(): Promise<void>;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"simple.d.ts","sourceRoot":"","sources":["../../src/query/simple.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,EACL,gBAAgB,EAIjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAW9C,OAAO,YAAY,MAAM,WAAW,CAAC;AAErC,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,YAAY;IACtD,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,UAAkB,MAAM,EAAE,gBAAgB,CAAC;gBAGzC,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,eAAe;cAMV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;cAKrB,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC;cAwC1B,cAAc;CAmB/B"}
|
{"version":3,"file":"simple.d.ts","sourceRoot":"","sources":["../../src/query/simple.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAe,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAK9C,OAAO,YAAY,MAAM,WAAW,CAAC;AAGrC,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,YAAY;IACtD,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;IAChC,UAAkB,MAAM,EAAE,gBAAgB,CAAC;gBAE/B,EACV,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,GACR,EAAE;QACD,OAAO,EAAE,UAAU,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;QACrB,KAAK,EAAE,gBAAgB,CAAC;QACxB,OAAO,EAAE,eAAe,CAAC;KAC1B;cAKe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;cAKrB,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC;cAwD1B,cAAc;CAmB/B"}
|
|
@ -1,12 +1,12 @@
|
||||||
import b4a from "b4a";
|
import b4a from "b4a";
|
||||||
import { hashQuery, isPromise, validateTimestampedResponse, } from "../util.js";
|
import { hashQuery, isPromise, validateTimestampedResponse } from "../util.js";
|
||||||
import RPC from "@lumeweb/rpc";
|
import RPC from "@lumeweb/rpc";
|
||||||
import { ERR_INVALID_SIGNATURE } from "../error.js";
|
import { ERR_INVALID_SIGNATURE } from "../error.js";
|
||||||
import RpcQueryBase from "./base.js";
|
import RpcQueryBase from "./base.js";
|
||||||
export default class SimpleRpcQuery extends RpcQueryBase {
|
export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
_relay;
|
_relay;
|
||||||
constructor(network, relay, query, options) {
|
constructor({ network, relay, query, options, }) {
|
||||||
super(network, query, options);
|
super({ network, query, options });
|
||||||
this._relay = relay;
|
this._relay = relay;
|
||||||
}
|
}
|
||||||
async _run() {
|
async _run() {
|
||||||
|
@ -14,16 +14,31 @@ export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
await this.checkResponses();
|
await this.checkResponses();
|
||||||
}
|
}
|
||||||
async queryRelay() {
|
async queryRelay() {
|
||||||
let socket;
|
let socket = this._relay;
|
||||||
try {
|
if (socket) {
|
||||||
socket = this._network.dht.connect(b4a.from(this._relay, "hex"));
|
if (socket === "string") {
|
||||||
if (isPromise(socket)) {
|
try {
|
||||||
socket = await socket;
|
const relay = this._network.getRelay(socket);
|
||||||
|
if (this._network.getRelay(socket)) {
|
||||||
|
socket = relay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
if (socket === "string") {
|
||||||
|
try {
|
||||||
|
socket = this._network.swarm.connect(b4a.from(this._relay, "hex"));
|
||||||
|
if (isPromise(socket)) {
|
||||||
|
socket = await socket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
if (!socket) {
|
||||||
return;
|
socket = this._network.getAvailableRelay(this._query.module, this._query.method);
|
||||||
}
|
}
|
||||||
|
this._relay = socket;
|
||||||
await socket.opened;
|
await socket.opened;
|
||||||
const rpc = new RPC(socket);
|
const rpc = new RPC(socket);
|
||||||
if (this._query.bypassCache) {
|
if (this._query.bypassCache) {
|
||||||
|
@ -41,12 +56,8 @@ export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
await this.queryRpc(rpc, this._query);
|
await this.queryRpc(rpc, this._query);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
// @ts-ignore
|
|
||||||
rpc.end();
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
|
||||||
rpc.end();
|
|
||||||
}
|
}
|
||||||
async checkResponses() {
|
async checkResponses() {
|
||||||
let response = this._response;
|
let response = this._response;
|
||||||
|
@ -54,7 +65,7 @@ export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
response = { error: this._error };
|
response = { error: this._error };
|
||||||
}
|
}
|
||||||
if (!response.error &&
|
if (!response.error &&
|
||||||
!validateTimestampedResponse(b4a.from(this._relay, "hex"), response)) {
|
!validateTimestampedResponse(b4a.from(this._relay.remotePublicKey, "hex"), response)) {
|
||||||
response = { error: ERR_INVALID_SIGNATURE };
|
response = { error: ERR_INVALID_SIGNATURE };
|
||||||
}
|
}
|
||||||
this.resolve(response);
|
this.resolve(response);
|
||||||
|
|
|
@ -12,4 +12,5 @@ export declare function validateTimestampedResponse(
|
||||||
response: RPCResponse
|
response: RPCResponse
|
||||||
): boolean;
|
): boolean;
|
||||||
export declare function hashQuery(query: RPCRequest): string;
|
export declare function hashQuery(query: RPCRequest): string;
|
||||||
|
export declare function createHash(data: string): Buffer;
|
||||||
//# sourceMappingURL=util.d.ts.map
|
//# sourceMappingURL=util.d.ts.map
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAOpE,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,WAM1C;AAKD,wBAAgB,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,GAAE,GAAQ,GAAG,GAAG,EAAE,CA0C1D;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,WAAW,EACrB,WAAW,UAAQ,GAClB,OAAO,CAmBT;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,WAAW,GACpB,OAAO,CAET;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAUnD"}
|
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAOpE,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,WAM1C;AAKD,wBAAgB,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,GAAE,GAAQ,GAAG,GAAG,EAAE,CA0C1D;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,WAAW,EACrB,WAAW,UAAQ,GAClB,OAAO,CAkBT;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,WAAW,GACpB,OAAO,CAET;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAUnD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM/C"}
|
|
@ -49,8 +49,7 @@ export function flatten(target, opts = {}) {
|
||||||
export function validateResponse(relay, response, timestamped = false) {
|
export function validateResponse(relay, response, timestamped = false) {
|
||||||
const field = response.signedField || "data";
|
const field = response.signedField || "data";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const data = response[field];
|
let json = response[field];
|
||||||
let json = data;
|
|
||||||
if (typeof json !== "string") {
|
if (typeof json !== "string") {
|
||||||
json = stringify(json);
|
json = stringify(json);
|
||||||
}
|
}
|
||||||
|
@ -73,3 +72,9 @@ export function hashQuery(query) {
|
||||||
sodium.crypto_generichash(queryHash, Buffer.from(stringify(clonedQuery)));
|
sodium.crypto_generichash(queryHash, Buffer.from(stringify(clonedQuery)));
|
||||||
return queryHash.toString("hex");
|
return queryHash.toString("hex");
|
||||||
}
|
}
|
||||||
|
export function createHash(data) {
|
||||||
|
const buffer = b4a.from(data);
|
||||||
|
let hash = b4a.allocUnsafe(32);
|
||||||
|
sodium.crypto_generichash(hash, buffer);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
|
@ -20,8 +20,11 @@
|
||||||
"@hyperswarm/dht": "^6.0.1",
|
"@hyperswarm/dht": "^6.0.1",
|
||||||
"@lumeweb/rpc": "git+https://git.lumeweb.com/LumeWeb/rpc.git",
|
"@lumeweb/rpc": "git+https://git.lumeweb.com/LumeWeb/rpc.git",
|
||||||
"b4a": "^1.6.1",
|
"b4a": "^1.6.1",
|
||||||
|
"hypercore-crypto": "^3.3.1",
|
||||||
|
"hyperswarm": "^4.3.7",
|
||||||
"json-stringify-deterministic": "^1.0.7",
|
"json-stringify-deterministic": "^1.0.7",
|
||||||
"libskynet": "^0.0.61",
|
"libskynet": "^0.0.61",
|
||||||
"msgpackr": "^1.6.1"
|
"msgpackr": "^1.6.1",
|
||||||
|
"sodium-universal": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
export const ERR_MAX_TRIES_HIT = "ERR_MAX_TRIES_HIT";
|
|
||||||
export const ERR_INVALID_SIGNATURE = "INVALID_SIGNATURE";
|
export const ERR_INVALID_SIGNATURE = "INVALID_SIGNATURE";
|
||||||
export const ERR_NO_RELAYS = "NO_RELAYS";
|
|
||||||
|
|
114
src/network.ts
114
src/network.ts
|
@ -1,10 +1,21 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import DHT from "@hyperswarm/dht";
|
import Hyperswarm from "hyperswarm";
|
||||||
import RpcNetworkQueryFactory from "./query/index.js";
|
import RpcNetworkQueryFactory from "./query/index.js";
|
||||||
|
import b4a from "b4a";
|
||||||
|
import { createHash } from "./util.js";
|
||||||
|
|
||||||
export default class RpcNetwork {
|
export default class RpcNetwork {
|
||||||
constructor(dht = new DHT()) {
|
private _relaysAvailablePromise?: Promise<void>;
|
||||||
this._dht = dht;
|
private _relaysAvailableResolve?: Function;
|
||||||
|
constructor(swarm = new Hyperswarm()) {
|
||||||
|
this._swarm = swarm;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _methods: Map<string, Set<string>> = new Map<string, Set<string>>();
|
||||||
|
|
||||||
|
get methods(): Map<string, Set<string>> {
|
||||||
|
return this._methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _factory = new RpcNetworkQueryFactory(this);
|
private _factory = new RpcNetworkQueryFactory(this);
|
||||||
|
@ -13,10 +24,10 @@ export default class RpcNetwork {
|
||||||
return this._factory;
|
return this._factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dht: typeof DHT;
|
private _swarm: typeof Hyperswarm;
|
||||||
|
|
||||||
get dht() {
|
get swarm() {
|
||||||
return this._dht;
|
return this._swarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _majorityThreshold = 0.75;
|
private _majorityThreshold = 0.75;
|
||||||
|
@ -49,9 +60,9 @@ export default class RpcNetwork {
|
||||||
this._relayTimeout = value;
|
this._relayTimeout = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _relays: string[] = [];
|
private _relays: Map<string, any> = new Map<string, string[]>();
|
||||||
|
|
||||||
get relays(): string[] {
|
get relays(): Map<string, string[]> {
|
||||||
return this._relays;
|
return this._relays;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,12 +70,16 @@ export default class RpcNetwork {
|
||||||
|
|
||||||
get ready(): Promise<void> {
|
get ready(): Promise<void> {
|
||||||
if (!this._ready) {
|
if (!this._ready) {
|
||||||
this._ready = this._dht.ready() as Promise<void>;
|
this._ready = this._swarm.dht.ready() as Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._ready;
|
return this._ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get readyWithRelays(): Promise<void> {
|
||||||
|
return this.ready.then(() => this._relaysAvailablePromise);
|
||||||
|
}
|
||||||
|
|
||||||
private _bypassCache: boolean = false;
|
private _bypassCache: boolean = false;
|
||||||
|
|
||||||
get bypassCache(): boolean {
|
get bypassCache(): boolean {
|
||||||
|
@ -75,33 +90,70 @@ export default class RpcNetwork {
|
||||||
this._bypassCache = value;
|
this._bypassCache = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _maxRelays: number = 0;
|
public getAvailableRelay(module: string, method: string) {
|
||||||
|
method = `${module}.${method}`;
|
||||||
|
|
||||||
get maxRelays(): number {
|
let relays = this._methods.get(method) ?? new Set();
|
||||||
return this._maxRelays;
|
|
||||||
}
|
|
||||||
|
|
||||||
set maxRelays(value: number) {
|
if (!relays.size) {
|
||||||
this._maxRelays = value;
|
throw Error("no available relay");
|
||||||
}
|
|
||||||
|
|
||||||
public addRelay(pubkey: string): void {
|
|
||||||
this._relays.push(pubkey);
|
|
||||||
this._relays = [...new Set(this._relays)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeRelay(pubkey: string): boolean {
|
|
||||||
if (!this._relays.includes(pubkey)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this._relays[this._relays.indexOf(pubkey)];
|
return Array.from(relays)[Math.floor(Math.random() * relays.size)];
|
||||||
this._relays = Object.values(this._relays);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearRelays(): void {
|
public getRelay(pubkey: string) {
|
||||||
this._relays = [];
|
if (this._relays.has(pubkey)) {
|
||||||
|
return this._relays.get(pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
this._swarm.join(createHash("lumeweb"));
|
||||||
|
this.setupRelayPromise();
|
||||||
|
|
||||||
|
this._swarm.on("connection", async (relay: any) => {
|
||||||
|
const query = this._factory.simple({
|
||||||
|
relay,
|
||||||
|
query: { module: "core", method: "get_methods", data: null },
|
||||||
|
});
|
||||||
|
const resp = await query.result;
|
||||||
|
|
||||||
|
const pubkey = b4a.from(relay.remotePublicKey).toString("hex");
|
||||||
|
|
||||||
|
if (resp.data) {
|
||||||
|
this._relays.set(pubkey, relay);
|
||||||
|
|
||||||
|
(resp.data as string[]).forEach((item) => {
|
||||||
|
const methods: Set<string> =
|
||||||
|
this._methods.get(item) ?? new Set<string>();
|
||||||
|
|
||||||
|
methods.add(pubkey);
|
||||||
|
this._methods.set(item, methods);
|
||||||
|
});
|
||||||
|
this._relaysAvailableResolve?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
relay.on("close", () => {
|
||||||
|
this._methods.forEach((item) => {
|
||||||
|
if (item.has(pubkey)) {
|
||||||
|
item.delete(pubkey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.relays.delete(pubkey);
|
||||||
|
|
||||||
|
if (!this._relays.size) {
|
||||||
|
this.setupRelayPromise();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupRelayPromise() {
|
||||||
|
this._relaysAvailablePromise = new Promise<void>((resolve) => {
|
||||||
|
this._relaysAvailableResolve = resolve;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,15 @@ export default abstract class RpcQueryBase {
|
||||||
protected _error?: string;
|
protected _error?: string;
|
||||||
protected _promiseResolve?: (data: any) => void;
|
protected _promiseResolve?: (data: any) => void;
|
||||||
|
|
||||||
constructor(
|
constructor({
|
||||||
network: RpcNetwork,
|
network,
|
||||||
query: ClientRPCRequest | RPCRequest,
|
query,
|
||||||
options: RpcQueryOptions = {}
|
options = {},
|
||||||
) {
|
}: {
|
||||||
|
network: RpcNetwork;
|
||||||
|
query: ClientRPCRequest | RPCRequest;
|
||||||
|
options: RpcQueryOptions;
|
||||||
|
}) {
|
||||||
this._network = network;
|
this._network = network;
|
||||||
this._query = query;
|
this._query = query;
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
|
|
@ -2,24 +2,27 @@ import RpcNetwork from "../network.js";
|
||||||
import { RPCBroadcastRequest, RPCRequest } from "@lumeweb/relay-types";
|
import { RPCBroadcastRequest, RPCRequest } from "@lumeweb/relay-types";
|
||||||
import { RpcQueryOptions } from "../types.js";
|
import { RpcQueryOptions } from "../types.js";
|
||||||
import { hashQuery } from "../util.js";
|
import { hashQuery } from "../util.js";
|
||||||
import { getActiveRelay, setupRelay } from "../sharedRelay.js";
|
|
||||||
import SimpleRpcQuery from "./simple.js";
|
import SimpleRpcQuery from "./simple.js";
|
||||||
|
|
||||||
export default class ClearCacheRpcQuery extends SimpleRpcQuery {
|
export default class ClearCacheRpcQuery extends SimpleRpcQuery {
|
||||||
protected _relays: string[];
|
protected _relays: string[];
|
||||||
|
|
||||||
constructor(
|
constructor({
|
||||||
network: RpcNetwork,
|
network,
|
||||||
relays: string[],
|
relays,
|
||||||
query: RPCRequest,
|
query,
|
||||||
options: RpcQueryOptions
|
options,
|
||||||
) {
|
}: {
|
||||||
super(network, "", query, options);
|
network: RpcNetwork;
|
||||||
|
relays: string[];
|
||||||
|
query: RPCRequest;
|
||||||
|
options: RpcQueryOptions;
|
||||||
|
}) {
|
||||||
|
super({ network, relay: "", query, options });
|
||||||
this._relays = relays;
|
this._relays = relays;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _run(): Promise<void> {
|
protected async _run(): Promise<void> {
|
||||||
await setupRelay(this._network);
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this._relay = getActiveRelay().stream.remotePublicKey;
|
this._relay = getActiveRelay().stream.remotePublicKey;
|
||||||
await this.queryRelay();
|
await this.queryRelay();
|
||||||
|
@ -27,17 +30,20 @@ export default class ClearCacheRpcQuery extends SimpleRpcQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async queryRelay(): Promise<any> {
|
protected async queryRelay(): Promise<any> {
|
||||||
return this.queryRpc(getActiveRelay(), {
|
return this.queryRpc(
|
||||||
module: "rpc",
|
this._network.getAvailableRelay("rpc", "broadcast_request"),
|
||||||
method: "broadcast_request",
|
{
|
||||||
data: {
|
module: "rpc",
|
||||||
request: {
|
method: "broadcast_request",
|
||||||
module: "rpc",
|
data: {
|
||||||
method: "clear_cached_item",
|
request: {
|
||||||
data: hashQuery(this._query),
|
module: "rpc",
|
||||||
},
|
method: "clear_cached_item",
|
||||||
relays: this._relays,
|
data: hashQuery(this._query),
|
||||||
} as RPCBroadcastRequest,
|
},
|
||||||
});
|
relays: this._relays,
|
||||||
|
} as RPCBroadcastRequest,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { ClientRPCRequest, RPCRequest } from "@lumeweb/relay-types";
|
import { ClientRPCRequest, RPCRequest } from "@lumeweb/relay-types";
|
||||||
import { RpcQueryOptions } from "../types.js";
|
import { RpcQueryOptions } from "../types.js";
|
||||||
import WisdomRpcQuery from "./wisdom.js";
|
|
||||||
import SimpleRpcQuery from "./simple.js";
|
import SimpleRpcQuery from "./simple.js";
|
||||||
import ClearCacheRpcQuery from "./clearCache.js";
|
import ClearCacheRpcQuery from "./clearCache.js";
|
||||||
import RpcNetwork from "../network.js";
|
import RpcNetwork from "../network.js";
|
||||||
|
@ -13,23 +12,6 @@ export default class RpcNetworkQueryFactory {
|
||||||
this._network = network;
|
this._network = network;
|
||||||
}
|
}
|
||||||
|
|
||||||
wisdom({
|
|
||||||
query,
|
|
||||||
options = {},
|
|
||||||
}: {
|
|
||||||
query: ClientRPCRequest;
|
|
||||||
options?: RpcQueryOptions;
|
|
||||||
}): WisdomRpcQuery {
|
|
||||||
return new WisdomRpcQuery(
|
|
||||||
this._network,
|
|
||||||
{
|
|
||||||
...query,
|
|
||||||
bypassCache: query.bypassCache || this._network.bypassCache,
|
|
||||||
},
|
|
||||||
options
|
|
||||||
).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
simple({
|
simple({
|
||||||
relay,
|
relay,
|
||||||
query,
|
query,
|
||||||
|
@ -39,15 +21,15 @@ export default class RpcNetworkQueryFactory {
|
||||||
query: ClientRPCRequest;
|
query: ClientRPCRequest;
|
||||||
options?: RpcQueryOptions;
|
options?: RpcQueryOptions;
|
||||||
}): SimpleRpcQuery {
|
}): SimpleRpcQuery {
|
||||||
return new SimpleRpcQuery(
|
return new SimpleRpcQuery({
|
||||||
this._network,
|
network: this._network,
|
||||||
relay,
|
relay,
|
||||||
{
|
query: {
|
||||||
...query,
|
...query,
|
||||||
bypassCache: query.bypassCache || this._network.bypassCache,
|
bypassCache: query?.bypassCache || this._network.bypassCache,
|
||||||
},
|
},
|
||||||
options
|
options,
|
||||||
).run();
|
}).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache({
|
clearCache({
|
||||||
|
@ -59,8 +41,13 @@ export default class RpcNetworkQueryFactory {
|
||||||
query: RPCRequest;
|
query: RPCRequest;
|
||||||
options?: RpcQueryOptions;
|
options?: RpcQueryOptions;
|
||||||
}): ClearCacheRpcQuery {
|
}): ClearCacheRpcQuery {
|
||||||
return new ClearCacheRpcQuery(this._network, relays, query, options).run();
|
return new ClearCacheRpcQuery({
|
||||||
|
network: this._network,
|
||||||
|
query,
|
||||||
|
relays,
|
||||||
|
options,
|
||||||
|
}).run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { RpcNetwork, RpcQueryBase, SimpleRpcQuery, WisdomRpcQuery };
|
export { RpcNetwork, RpcQueryBase, SimpleRpcQuery };
|
||||||
|
|
|
@ -6,18 +6,24 @@ import { hashQuery, isPromise, validateTimestampedResponse } from "../util.js";
|
||||||
import RPC from "@lumeweb/rpc";
|
import RPC from "@lumeweb/rpc";
|
||||||
import { ERR_INVALID_SIGNATURE } from "../error.js";
|
import { ERR_INVALID_SIGNATURE } from "../error.js";
|
||||||
import RpcQueryBase from "./base.js";
|
import RpcQueryBase from "./base.js";
|
||||||
|
import { query } from "express";
|
||||||
|
|
||||||
export default class SimpleRpcQuery extends RpcQueryBase {
|
export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
protected _relay: string;
|
protected _relay?: string | any;
|
||||||
protected declare _query: ClientRPCRequest;
|
protected declare _query: ClientRPCRequest;
|
||||||
|
|
||||||
constructor(
|
constructor({
|
||||||
network: RpcNetwork,
|
network,
|
||||||
relay: string,
|
relay,
|
||||||
query: ClientRPCRequest,
|
query,
|
||||||
options: RpcQueryOptions
|
options,
|
||||||
) {
|
}: {
|
||||||
super(network, query, options);
|
network: RpcNetwork;
|
||||||
|
relay?: string | any;
|
||||||
|
query: ClientRPCRequest;
|
||||||
|
options: RpcQueryOptions;
|
||||||
|
}) {
|
||||||
|
super({ network, query, options });
|
||||||
this._relay = relay;
|
this._relay = relay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,16 +33,37 @@ export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async queryRelay(): Promise<any> {
|
protected async queryRelay(): Promise<any> {
|
||||||
let socket: any;
|
let socket = this._relay;
|
||||||
|
|
||||||
try {
|
if (socket) {
|
||||||
socket = this._network.dht.connect(b4a.from(this._relay, "hex"));
|
if (socket === "string") {
|
||||||
if (isPromise(socket)) {
|
try {
|
||||||
socket = await socket;
|
const relay = this._network.getRelay(socket);
|
||||||
|
if (this._network.getRelay(socket)) {
|
||||||
|
socket = relay;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket === "string") {
|
||||||
|
try {
|
||||||
|
socket = this._network.swarm.connect(b4a.from(this._relay, "hex"));
|
||||||
|
if (isPromise(socket)) {
|
||||||
|
socket = await socket;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!socket) {
|
||||||
|
socket = this._network.getAvailableRelay(
|
||||||
|
this._query.module,
|
||||||
|
this._query.method
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._relay = socket;
|
||||||
|
|
||||||
await socket.opened;
|
await socket.opened;
|
||||||
|
|
||||||
const rpc = new RPC(socket);
|
const rpc = new RPC(socket);
|
||||||
|
@ -57,13 +84,8 @@ export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
try {
|
try {
|
||||||
await this.queryRpc(rpc, this._query);
|
await this.queryRpc(rpc, this._query);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// @ts-ignore
|
|
||||||
rpc.end();
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
rpc.end();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async checkResponses() {
|
protected async checkResponses() {
|
||||||
|
@ -76,7 +98,7 @@ export default class SimpleRpcQuery extends RpcQueryBase {
|
||||||
if (
|
if (
|
||||||
!response.error &&
|
!response.error &&
|
||||||
!validateTimestampedResponse(
|
!validateTimestampedResponse(
|
||||||
b4a.from(this._relay, "hex") as Buffer,
|
b4a.from(this._relay.remotePublicKey, "hex") as Buffer,
|
||||||
response
|
response
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,178 +0,0 @@
|
||||||
import {
|
|
||||||
ClientRPCRequest,
|
|
||||||
RPCBroadcastResponse,
|
|
||||||
RPCRequest,
|
|
||||||
RPCResponse,
|
|
||||||
} from "@lumeweb/relay-types";
|
|
||||||
import { clearTimeout } from "timers";
|
|
||||||
import b4a from "b4a";
|
|
||||||
import {
|
|
||||||
flatten,
|
|
||||||
validateResponse,
|
|
||||||
validateTimestampedResponse,
|
|
||||||
} from "../util.js";
|
|
||||||
import { blake2b } from "libskynet";
|
|
||||||
import { ERR_INVALID_SIGNATURE, ERR_NO_RELAYS } from "../error.js";
|
|
||||||
import RpcQueryBase from "./base.js";
|
|
||||||
import { getActiveRelay, setupRelay } from "../sharedRelay.js";
|
|
||||||
|
|
||||||
function flatHash(data: any) {
|
|
||||||
const flattenedData = flatten(data).sort();
|
|
||||||
return Buffer.from(
|
|
||||||
blake2b(Buffer.from(JSON.stringify(flattenedData)))
|
|
||||||
).toString("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class WisdomRpcQuery extends RpcQueryBase {
|
|
||||||
protected declare _response?: RPCBroadcastResponse;
|
|
||||||
protected declare _query: ClientRPCRequest;
|
|
||||||
get result(): Promise<RPCResponse> {
|
|
||||||
return this._promise as Promise<RPCResponse>;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async _run(): Promise<void> {
|
|
||||||
await setupRelay(this._network);
|
|
||||||
await this.queryRelay();
|
|
||||||
await this.checkResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected resolve(data?: RPCResponse, timeout: boolean = false): void {
|
|
||||||
clearTimeout(this._timeoutTimer);
|
|
||||||
this._timeout = timeout;
|
|
||||||
this._completed = true;
|
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
data = {
|
|
||||||
error: "timeout",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this._promiseResolve?.(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async queryRelay(): Promise<any> {
|
|
||||||
let activeRelay = getActiveRelay();
|
|
||||||
let relays = this.getRelays();
|
|
||||||
|
|
||||||
if (!relays.length) {
|
|
||||||
throw new Error(ERR_NO_RELAYS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._query.bypassCache) {
|
|
||||||
delete this._query.bypassCache;
|
|
||||||
const clearCacheQuery = this._network.factory.clearCache({
|
|
||||||
relays,
|
|
||||||
query: this._query,
|
|
||||||
});
|
|
||||||
await clearCacheQuery.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("bypassCache" in this._query) {
|
|
||||||
delete this._query.bypassCache;
|
|
||||||
}
|
|
||||||
return this.queryRpc(activeRelay, {
|
|
||||||
module: "rpc",
|
|
||||||
method: "broadcast_request",
|
|
||||||
data: {
|
|
||||||
request: this._query,
|
|
||||||
relays,
|
|
||||||
},
|
|
||||||
} as RPCRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async checkResponse() {
|
|
||||||
if (this._error) {
|
|
||||||
this.resolve({ error: this._error });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!validateResponse(
|
|
||||||
// @ts-ignore
|
|
||||||
getActiveRelay().stream.remotePublicKey,
|
|
||||||
this._response as RPCResponse
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.resolve({ error: ERR_INVALID_SIGNATURE });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let relays: RPCResponse[] = [];
|
|
||||||
|
|
||||||
for (const relay in this._response?.relays) {
|
|
||||||
const resp = this._response?.relays[relay];
|
|
||||||
if (resp?.error) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
validateTimestampedResponse(
|
|
||||||
b4a.from(relay, "hex") as Buffer,
|
|
||||||
resp as RPCResponse
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
relays.push(resp as RPCResponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!relays.length) {
|
|
||||||
this.resolve({ error: ERR_NO_RELAYS });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResponseGroup = { [response: string]: number };
|
|
||||||
|
|
||||||
const responseObjects = relays.reduce((output: any, item: RPCResponse) => {
|
|
||||||
const field = item.signedField || "data";
|
|
||||||
// @ts-ignore
|
|
||||||
const hash = flatHash(item[field]);
|
|
||||||
output[hash] = item?.data;
|
|
||||||
return output;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const responses: ResponseGroup = relays.reduce(
|
|
||||||
(output: ResponseGroup, item: RPCResponse) => {
|
|
||||||
const field = item.signedField || "data";
|
|
||||||
// @ts-ignore
|
|
||||||
const hash = flatHash(item[field]);
|
|
||||||
output[hash] = output[hash] ?? 0;
|
|
||||||
output[hash]++;
|
|
||||||
return output;
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const responseHash in responses) {
|
|
||||||
if (
|
|
||||||
responses[responseHash] / relays.length >=
|
|
||||||
this._network.majorityThreshold
|
|
||||||
) {
|
|
||||||
let response: RPCResponse = responseObjects[responseHash];
|
|
||||||
|
|
||||||
response = { data: response };
|
|
||||||
|
|
||||||
this.resolve(response);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getRelays(): string[] {
|
|
||||||
if (
|
|
||||||
this._network.maxRelays === 0 ||
|
|
||||||
this._network.relays.length <= this._network.maxRelays
|
|
||||||
) {
|
|
||||||
return this._network.relays;
|
|
||||||
}
|
|
||||||
|
|
||||||
const list: string[] = [];
|
|
||||||
let available = this._network.relays;
|
|
||||||
|
|
||||||
while (list.length <= this._network.maxRelays) {
|
|
||||||
const item = Math.floor(Math.random() * available.length);
|
|
||||||
list.push(available[item]);
|
|
||||||
available.splice(item, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
import { ERR_NO_RELAYS } from "./error.js";
|
|
||||||
import b4a from "b4a";
|
|
||||||
import { isPromise } from "./util.js";
|
|
||||||
import RPC from "@lumeweb/rpc";
|
|
||||||
import RpcNetwork from "./network.js";
|
|
||||||
|
|
||||||
let activeRelay: RPC | undefined;
|
|
||||||
|
|
||||||
export async function setupRelay(network: RpcNetwork) {
|
|
||||||
const relays = network.relays;
|
|
||||||
|
|
||||||
if (!activeRelay) {
|
|
||||||
if (!relays.length) {
|
|
||||||
throw new Error(ERR_NO_RELAYS);
|
|
||||||
}
|
|
||||||
|
|
||||||
let relay = relays[Math.floor(Math.random() * relays.length)];
|
|
||||||
let socket = network.dht.connect(b4a.from(relay, "hex"));
|
|
||||||
if (isPromise(socket)) {
|
|
||||||
socket = await socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
await socket.opened;
|
|
||||||
|
|
||||||
activeRelay = new RPC(socket);
|
|
||||||
socket.once("close", () => {
|
|
||||||
activeRelay = undefined;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getActiveRelay(): RPC {
|
|
||||||
return activeRelay as RPC;
|
|
||||||
}
|
|
11
src/util.ts
11
src/util.ts
|
@ -69,8 +69,7 @@ export function validateResponse(
|
||||||
): boolean {
|
): boolean {
|
||||||
const field = response.signedField || "data";
|
const field = response.signedField || "data";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const data = response[field];
|
let json = response[field];
|
||||||
let json = data;
|
|
||||||
if (typeof json !== "string") {
|
if (typeof json !== "string") {
|
||||||
json = stringify(json);
|
json = stringify(json);
|
||||||
}
|
}
|
||||||
|
@ -105,3 +104,11 @@ export function hashQuery(query: RPCRequest): string {
|
||||||
|
|
||||||
return queryHash.toString("hex");
|
return queryHash.toString("hex");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createHash(data: string): Buffer {
|
||||||
|
const buffer = b4a.from(data);
|
||||||
|
let hash = b4a.allocUnsafe(32) as Buffer;
|
||||||
|
sodium.crypto_generichash(hash, buffer);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue