2022-11-26 07:59:07 +00:00
|
|
|
import { getRpcServer } from "../rpc/server";
|
|
|
|
import {
|
|
|
|
Plugin,
|
|
|
|
PluginAPI,
|
|
|
|
RPCBroadcastRequest,
|
|
|
|
RPCBroadcastResponse,
|
2022-12-04 03:55:39 +00:00
|
|
|
RPCCacheItem,
|
2022-11-26 07:59:07 +00:00
|
|
|
RPCRequest,
|
|
|
|
RPCResponse,
|
|
|
|
} from "@lumeweb/relay-types";
|
|
|
|
import { getRpcByPeer } from "../rpc";
|
2022-12-04 17:04:18 +00:00
|
|
|
import { get as getSwarm, LUMEWEB_TOPIC_HASH } from "../swarm";
|
2022-11-28 06:37:54 +00:00
|
|
|
import b4a from "b4a";
|
2022-12-15 11:23:28 +00:00
|
|
|
import pTimeout, { ClearablePromise } from "p-timeout";
|
2022-11-26 07:59:07 +00:00
|
|
|
|
|
|
|
async function broadcastRequest(
|
|
|
|
request: RPCRequest,
|
2022-12-15 09:58:54 +00:00
|
|
|
relays: string[],
|
|
|
|
timeout = 5000
|
2022-11-26 07:59:07 +00:00
|
|
|
): Promise<Map<string, Promise<any>>> {
|
|
|
|
const makeRequest = async (relay: string) => {
|
|
|
|
const rpc = await getRpcByPeer(relay);
|
|
|
|
return rpc.request(`${request.module}.${request.method}`, request.data);
|
|
|
|
};
|
|
|
|
|
2022-12-15 09:58:54 +00:00
|
|
|
let relayMap = new Map<string, ClearablePromise<any>>();
|
2022-11-26 07:59:07 +00:00
|
|
|
|
|
|
|
for (const relay of relays) {
|
2022-11-28 06:37:54 +00:00
|
|
|
let req;
|
|
|
|
if (b4a.equals(b4a.from(relay, "hex"), getSwarm().keyPair.publicKey)) {
|
|
|
|
req = getRpcServer().handleRequest(request);
|
|
|
|
} else {
|
|
|
|
req = makeRequest(relay);
|
|
|
|
}
|
|
|
|
|
2022-12-15 09:58:54 +00:00
|
|
|
let timeoutPromise = pTimeout(req, {
|
|
|
|
milliseconds: timeout,
|
|
|
|
message: `relay timed out after ${timeout} milliseconds`,
|
|
|
|
});
|
|
|
|
|
|
|
|
relayMap.set(relay, timeoutPromise);
|
2022-11-26 07:59:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.allSettled([...relays.values()]);
|
|
|
|
return relayMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
const plugin: Plugin = {
|
|
|
|
name: "rpc",
|
|
|
|
async plugin(api: PluginAPI): Promise<void> {
|
|
|
|
api.registerMethod("get_cached_item", {
|
|
|
|
cacheable: false,
|
|
|
|
async handler(req: string): Promise<RPCResponse> {
|
|
|
|
if (typeof req !== "string") {
|
|
|
|
throw new Error("item must be a string");
|
|
|
|
}
|
|
|
|
|
|
|
|
const cache = getRpcServer().cache.data;
|
|
|
|
|
|
|
|
if (!Object.keys(cache).includes(req)) {
|
|
|
|
throw new Error("item does not exist");
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
data: true,
|
2022-12-04 03:55:39 +00:00
|
|
|
...cache.get<RPCCacheItem>(req)?.value,
|
|
|
|
signature: cache.get<RPCCacheItem>(req)?.signature,
|
2022-11-26 07:59:07 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
});
|
|
|
|
api.registerMethod("clear_cached_item", {
|
|
|
|
cacheable: false,
|
2022-11-28 05:24:36 +00:00
|
|
|
async handler(req: string): Promise<void> {
|
2022-11-27 23:10:21 +00:00
|
|
|
if (typeof req !== "string") {
|
|
|
|
throw new Error("item must be a string");
|
2022-11-26 07:59:07 +00:00
|
|
|
}
|
|
|
|
try {
|
2022-11-27 23:10:21 +00:00
|
|
|
api.getRpcServer().cache.deleteItem(req);
|
2022-11-26 07:59:07 +00:00
|
|
|
} catch (e: any) {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
api.registerMethod("broadcast_request", {
|
|
|
|
cacheable: false,
|
|
|
|
async handler(req: RPCBroadcastRequest): Promise<RPCBroadcastResponse> {
|
|
|
|
if (!req?.request) {
|
|
|
|
throw new Error("request required");
|
|
|
|
}
|
2022-11-28 06:37:54 +00:00
|
|
|
if (!req?.request?.module) {
|
|
|
|
throw new Error("request.module required");
|
|
|
|
}
|
|
|
|
if (!req?.request?.method) {
|
|
|
|
throw new Error("request.method required");
|
|
|
|
}
|
2022-11-26 07:59:07 +00:00
|
|
|
if (!req?.relays?.length) {
|
|
|
|
throw new Error("relays required");
|
|
|
|
}
|
|
|
|
|
2022-12-04 06:01:13 +00:00
|
|
|
if (
|
|
|
|
req?.request?.module === "rpc" &&
|
|
|
|
req?.request?.method === "broadcast_request"
|
|
|
|
) {
|
|
|
|
throw new Error("recursive broadcast_request calls are not allowed");
|
|
|
|
}
|
|
|
|
|
2022-12-15 09:58:54 +00:00
|
|
|
let resp = await broadcastRequest(req.request, req.relays, req.timeout);
|
2022-11-26 07:59:07 +00:00
|
|
|
|
|
|
|
const result: RPCBroadcastResponse = {
|
|
|
|
relays: {},
|
|
|
|
data: true,
|
|
|
|
signedField: "relays",
|
|
|
|
};
|
2022-11-28 06:37:54 +00:00
|
|
|
for (const relay of resp.keys()) {
|
|
|
|
let ret: RPCResponse | Error;
|
2022-11-26 07:59:07 +00:00
|
|
|
try {
|
|
|
|
ret = await resp.get(relay);
|
2022-11-28 06:37:54 +00:00
|
|
|
if (ret instanceof Error) {
|
|
|
|
result.relays[relay] = { error: ret.message };
|
|
|
|
} else {
|
|
|
|
result.relays[relay] = ret as RPCResponse;
|
|
|
|
}
|
2022-11-26 07:59:07 +00:00
|
|
|
} catch (e: any) {
|
|
|
|
result.relays[relay] = { error: e.message };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
});
|
2022-12-04 17:04:18 +00:00
|
|
|
api.registerMethod("get_peers", {
|
|
|
|
cacheable: false,
|
|
|
|
async handler(): Promise<string[]> {
|
2022-12-05 20:38:58 +00:00
|
|
|
const pubkey = b4a
|
|
|
|
.from(getRpcServer().cache.swarm.keyPair.publicKey)
|
|
|
|
.toString("hex");
|
|
|
|
|
|
|
|
const online = getRpcServer().cache.dhtCache.online;
|
|
|
|
if (online.has(pubkey)) {
|
|
|
|
online.delete(pubkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
return [...online];
|
2022-12-04 17:04:18 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
api.registerMethod("get_direct_peers", {
|
|
|
|
cacheable: false,
|
|
|
|
async handler(): Promise<string[]> {
|
2022-12-05 20:22:18 +00:00
|
|
|
const online = getRpcServer().cache.dhtCache.online;
|
|
|
|
const pubkey = b4a
|
|
|
|
.from(getRpcServer().cache.swarm.keyPair.publicKey)
|
|
|
|
.toString("hex");
|
|
|
|
|
|
|
|
if (online.has(pubkey)) {
|
|
|
|
online.delete(pubkey);
|
|
|
|
}
|
|
|
|
|
2022-12-04 17:04:18 +00:00
|
|
|
const topic = LUMEWEB_TOPIC_HASH.toString("hex");
|
|
|
|
return [...getRpcServer().cache.swarm.peers.values()]
|
|
|
|
.filter((item: any) => [...item._seenTopics.keys()].includes(topic))
|
2022-12-05 20:22:18 +00:00
|
|
|
.map((item: any) => item.publicKey.toString("hex"))
|
|
|
|
.filter((item: any) => online.has(item));
|
2022-12-04 17:04:18 +00:00
|
|
|
},
|
|
|
|
});
|
2022-11-26 07:59:07 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export default plugin;
|