*Initial merge of rpcproxy
This commit is contained in:
parent
660bb85203
commit
8d95dde848
|
@ -11,27 +11,35 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hyperswarm/dht": "^6.0.1",
|
"@hyperswarm/dht": "^6.0.1",
|
||||||
"@hyperswarm/dht-relay": "^0.3.0",
|
"@hyperswarm/dht-relay": "^0.3.0",
|
||||||
|
"@pokt-network/pocket-js": "^0.8.0-rc",
|
||||||
"@root/greenlock": "^4.0.5",
|
"@root/greenlock": "^4.0.5",
|
||||||
|
"@solana/web3.js": "^1.47.3",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"@types/node-cron": "^3.0.2",
|
"@types/node-cron": "^3.0.2",
|
||||||
"@types/ws": "^8.5.3",
|
"@types/ws": "^8.5.3",
|
||||||
"@types/xml2js": "^0.4.11",
|
"@types/xml2js": "^0.4.11",
|
||||||
"async-mutex": "^0.3.2",
|
"async-mutex": "^0.3.2",
|
||||||
|
"dotenv": "^16.0.1",
|
||||||
|
"ethers": "^5.6.9",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"greenlock-express": "^4.0.3",
|
"greenlock-express": "^4.0.3",
|
||||||
"jayson": "^3.6.6",
|
"jayson": "^3.6.6",
|
||||||
"json-stable-stringify": "^1.0.1",
|
"json-stable-stringify": "^1.0.1",
|
||||||
"libskynet": "^0.0.48",
|
"libskynet": "^0.0.48",
|
||||||
"libskynetnode": "^0.1.3",
|
"libskynetnode": "^0.1.3",
|
||||||
|
"minimatch": "^5.1.0",
|
||||||
"msgpackr": "^1.6.1",
|
"msgpackr": "^1.6.1",
|
||||||
"node-cache": "^5.1.2",
|
"node-cache": "^5.1.2",
|
||||||
"node-cron": "^3.0.1",
|
"node-cron": "^3.0.1",
|
||||||
"node-fetch": "^3.2.6",
|
"node-fetch": "^3.2.6",
|
||||||
"random-access-memory": "^4.1.0",
|
"random-access-memory": "^4.1.0",
|
||||||
|
"sprintf-js": "^1.1.2",
|
||||||
"xml2js": "^0.4.23"
|
"xml2js": "^0.4.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/minimatch": "^3.0.5",
|
||||||
|
"@types/sprintf-js": "^1.1.2",
|
||||||
"hyper-typings": "^1.0.0",
|
"hyper-typings": "^1.0.0",
|
||||||
"prettier": "^2.7.1"
|
"prettier": "^2.7.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,33 @@
|
||||||
|
import fs from "fs";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
const envPath = path.resolve(__dirname, "..", "lumerelay.env");
|
||||||
|
|
||||||
|
if (fs.existsSync(envPath)) {
|
||||||
|
dotenv.config({ path: envPath });
|
||||||
|
}
|
||||||
|
|
||||||
export const RELAY_PORT = process.env.RELAY_PORT ?? 8080;
|
export const RELAY_PORT = process.env.RELAY_PORT ?? 8080;
|
||||||
export const RELAY_DOMAIN = process.env.RELAY_DOMAIN;
|
export const RELAY_DOMAIN = process.env.RELAY_DOMAIN;
|
||||||
export const AFRAID_USERNAME = process.env.AFRAID_USERNAME;
|
export const AFRAID_USERNAME = process.env.AFRAID_USERNAME;
|
||||||
export const AFRAID_PASSWORD = process.env.AFRAID_PASSWORD;
|
export const AFRAID_PASSWORD = process.env.AFRAID_PASSWORD;
|
||||||
export const RELAY_SEED = process.env.RELAY_SEED;
|
export const RELAY_SEED = process.env.RELAY_SEED;
|
||||||
|
export const POCKET_APP_ID = process.env.POCKET_APP_ID || false;
|
||||||
|
export const POCKET_APP_KEY = process.env.POCKET_APP_KEY || false;
|
||||||
|
export const POCKET_ACCOUNT_PUBLIC_KEY =
|
||||||
|
process.env.POCKET_ACCOUNT_PUBLIC_KEY || false;
|
||||||
|
export const POCKET_ACCOUNT_PRIVATE_KEY =
|
||||||
|
process.env.POCKET_ACCOUNT_PRIVATE_KEY || false;
|
||||||
|
|
||||||
|
export const HSD_NETWORK_TYPE = process.env.HSD_NETWORK || "main";
|
||||||
|
export const HSD_HOST = process.env.HSD_HOST || "localhost";
|
||||||
|
export const HSD_PORT = Number(process.env.HSD_PORT) || 12037;
|
||||||
|
export const HSD_API_KEY = process.env.HSD_API_KEY || "foo";
|
||||||
|
|
||||||
|
export const POCKET_HOST = process.env.POCKET_HOST || "localhost";
|
||||||
|
export const POCKET_PORT = process.env.POCKET_PORT || 8081;
|
||||||
|
|
|
@ -4,7 +4,7 @@ let error = false;
|
||||||
|
|
||||||
for (const constant in CONFIG) {
|
for (const constant in CONFIG) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (!CONFIG[constant]) {
|
if (CONFIG[constant] === null || CONFIG[constant] === undefined) {
|
||||||
console.error(`Missing constant ${constant}`);
|
console.error(`Missing constant ${constant}`);
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
|
@ -14,4 +14,14 @@ if (error) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let usingPocketGateway = true;
|
||||||
|
|
||||||
|
export function usePocketGateway() {
|
||||||
|
return usingPocketGateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateUsePocketGateway(state: boolean): void {
|
||||||
|
usingPocketGateway = state;
|
||||||
|
}
|
||||||
|
|
||||||
export * from "./constant_vars.js";
|
export * from "./constant_vars.js";
|
||||||
|
|
152
src/rpc.ts
152
src/rpc.ts
|
@ -5,16 +5,41 @@ import { Mutex } from "async-mutex";
|
||||||
import { createRequire } from "module";
|
import { createRequire } from "module";
|
||||||
import NodeCache from "node-cache";
|
import NodeCache from "node-cache";
|
||||||
import { get as getDHT } from "./dht.js";
|
import { get as getDHT } from "./dht.js";
|
||||||
|
import {
|
||||||
|
POCKET_ACCOUNT_PRIVATE_KEY,
|
||||||
|
POCKET_ACCOUNT_PUBLIC_KEY,
|
||||||
|
POCKET_APP_ID,
|
||||||
|
POCKET_APP_KEY,
|
||||||
|
POCKET_HOST,
|
||||||
|
POCKET_PORT,
|
||||||
|
} from "./constant_vars";
|
||||||
|
import { updateUsePocketGateway, usePocketGateway } from "./constants";
|
||||||
|
import { Server as JSONServer } from "jayson/promise/index.js";
|
||||||
|
import { rpcMethods } from "./rpc/index.js";
|
||||||
|
import {
|
||||||
|
Configuration,
|
||||||
|
HttpRpcProvider,
|
||||||
|
Pocket,
|
||||||
|
PocketAAT,
|
||||||
|
} from "@pokt-network/pocket-js";
|
||||||
|
import {
|
||||||
|
JSONRPCRequest,
|
||||||
|
JSONRPCResponseWithError,
|
||||||
|
JSONRPCResponseWithResult,
|
||||||
|
} from "jayson";
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
const stringify = require("json-stable-stringify");
|
const stringify = require("json-stable-stringify");
|
||||||
|
|
||||||
const clients: { [chain: string]: any } = {};
|
|
||||||
const pendingRequests = new NodeCache();
|
const pendingRequests = new NodeCache();
|
||||||
const processedRequests = new NodeCache({
|
const processedRequests = new NodeCache({
|
||||||
stdTTL: 60 * 60 * 12,
|
stdTTL: 60 * 60 * 12,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let pocketServer: Pocket;
|
||||||
|
let _aat: PocketAAT;
|
||||||
|
let jsonServer: jayson.Server;
|
||||||
|
|
||||||
interface RPCRequest {
|
interface RPCRequest {
|
||||||
force: boolean;
|
force: boolean;
|
||||||
chain: string;
|
chain: string;
|
||||||
|
@ -35,23 +60,6 @@ function hash(data: string): string {
|
||||||
return crypto.createHash("sha256").update(data).digest("hex");
|
return crypto.createHash("sha256").update(data).digest("hex");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClient(chain: string): Function {
|
|
||||||
chain = chain.replace(/[^a-z0-9\-]/g, "");
|
|
||||||
|
|
||||||
if (!(chain in clients)) {
|
|
||||||
clients[chain] = jayson.Client.http({
|
|
||||||
host: process.env.RPC_PROXY_HOST,
|
|
||||||
port: parseInt(process.env.RPC_PROXY_PORT as string),
|
|
||||||
path: "/",
|
|
||||||
headers: {
|
|
||||||
"X-Chain": chain,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return clients[chain];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestId(request: RPCRequest) {
|
function getRequestId(request: RPCRequest) {
|
||||||
const clonedRequest = Object.assign({}, request);
|
const clonedRequest = Object.assign({}, request);
|
||||||
|
|
||||||
|
@ -98,10 +106,13 @@ async function processRequest(request: RPCRequest): Promise<RPCResponse> {
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
rpcResp = await processRpcRequest(
|
||||||
rpcResp = await getClient(request.chain).request(
|
{
|
||||||
request.query,
|
method: request.query,
|
||||||
request.data
|
jsonrpc: "2.0",
|
||||||
|
params: request.data,
|
||||||
|
} as unknown as JSONRPCRequest,
|
||||||
|
request.chain
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = (e as Error).message;
|
error = (e as Error).message;
|
||||||
|
@ -113,15 +124,22 @@ async function processRequest(request: RPCRequest): Promise<RPCResponse> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (rpcResp) {
|
if (rpcResp) {
|
||||||
|
rpcResp = rpcResp as JSONRPCResponseWithResult;
|
||||||
if (false === rpcResp.result) {
|
if (false === rpcResp.result) {
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpcResp = rpcResp as unknown as JSONRPCResponseWithError;
|
||||||
|
|
||||||
if (rpcResp.error) {
|
if (rpcResp.error) {
|
||||||
|
// @ts-ignore
|
||||||
error = rpcResp.error.message;
|
error = rpcResp.error.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbData.data = error ? { error } : rpcResp.result;
|
dbData.data = error
|
||||||
|
? { error }
|
||||||
|
: (rpcResp as unknown as JSONRPCResponseWithResult).result;
|
||||||
|
|
||||||
if (!processedRequests.get(reqId) || request.force) {
|
if (!processedRequests.get(reqId) || request.force) {
|
||||||
processedRequests.set(reqId, dbData);
|
processedRequests.set(reqId, dbData);
|
||||||
|
@ -132,7 +150,93 @@ async function processRequest(request: RPCRequest): Promise<RPCResponse> {
|
||||||
return dbData;
|
return dbData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateAat(aat: PocketAAT): void {
|
||||||
|
_aat = aat;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAat(): PocketAAT {
|
||||||
|
return _aat;
|
||||||
|
}
|
||||||
|
export function getPocketServer(): Pocket {
|
||||||
|
return pocketServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unlockAccount(
|
||||||
|
accountPrivateKey: string,
|
||||||
|
accountPublicKey: string,
|
||||||
|
accountPassphrase: string
|
||||||
|
): Promise<PocketAAT> {
|
||||||
|
try {
|
||||||
|
const account = await pocketServer.keybase.importAccount(
|
||||||
|
Buffer.from(accountPrivateKey, "hex"),
|
||||||
|
accountPassphrase
|
||||||
|
);
|
||||||
|
|
||||||
|
if (account instanceof Error) {
|
||||||
|
// noinspection ExceptionCaughtLocallyJS
|
||||||
|
throw account;
|
||||||
|
}
|
||||||
|
|
||||||
|
await pocketServer.keybase.unlockAccount(
|
||||||
|
account.addressHex,
|
||||||
|
accountPassphrase,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
return await PocketAAT.from(
|
||||||
|
"0.0.1",
|
||||||
|
accountPublicKey,
|
||||||
|
accountPublicKey,
|
||||||
|
accountPrivateKey
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function processRpcRequest(
|
||||||
|
request: JSONRPCRequest,
|
||||||
|
chain: string
|
||||||
|
): Promise<JSONRPCResponseWithResult | JSONRPCResponseWithError | undefined> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
jsonServer.call(
|
||||||
|
request,
|
||||||
|
{ chain },
|
||||||
|
(
|
||||||
|
err?: JSONRPCResponseWithError | null,
|
||||||
|
result?: JSONRPCResponseWithResult
|
||||||
|
): void => {
|
||||||
|
if (err) {
|
||||||
|
return resolve(err);
|
||||||
|
}
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function start() {
|
export async function start() {
|
||||||
|
if (!POCKET_APP_ID || !POCKET_APP_KEY) {
|
||||||
|
const dispatchURL = new URL(`http://${POCKET_HOST}:${POCKET_PORT}`);
|
||||||
|
const rpcProvider = new HttpRpcProvider(dispatchURL);
|
||||||
|
const configuration = new Configuration();
|
||||||
|
pocketServer = new Pocket([dispatchURL], rpcProvider, configuration);
|
||||||
|
updateUsePocketGateway(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usePocketGateway()) {
|
||||||
|
updateAat(
|
||||||
|
await unlockAccount(
|
||||||
|
<string>POCKET_ACCOUNT_PRIVATE_KEY,
|
||||||
|
<string>POCKET_ACCOUNT_PUBLIC_KEY,
|
||||||
|
"0"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonServer = new JSONServer(rpcMethods, { useContext: true });
|
||||||
|
|
||||||
(await getDHT()).on("connection", (socket: any) => {
|
(await getDHT()).on("connection", (socket: any) => {
|
||||||
socket.on("data", async (data: any) => {
|
socket.on("data", async (data: any) => {
|
||||||
let request: RPCRequest;
|
let request: RPCRequest;
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
import { maybeMapChainId, reverseMapChainId } from "../util.js";
|
||||||
|
import minimatch from "minimatch";
|
||||||
|
// @ts-ignore
|
||||||
|
import HTTPClient from "algosdk/dist/cjs/src/client/client.js";
|
||||||
|
import { sprintf } from "sprintf-js";
|
||||||
|
import { RpcMethodList } from "./index.js";
|
||||||
|
import { POCKET_APP_ID } from "../constants.js";
|
||||||
|
|
||||||
|
const allowedEndpoints: { [endpoint: string]: ("GET" | "POST")[] } = {
|
||||||
|
"/v2/teal/compile": ["POST"],
|
||||||
|
"/v2/accounts/*": ["GET"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function proxyRestMethod(
|
||||||
|
apiServer: string,
|
||||||
|
matchChainId: string
|
||||||
|
): Function {
|
||||||
|
return async function (args: any, context: object) {
|
||||||
|
// @ts-ignore
|
||||||
|
let chain = context.chain;
|
||||||
|
let chainId = maybeMapChainId(chain);
|
||||||
|
|
||||||
|
if (!chainId) {
|
||||||
|
throw new Error("Invalid Chain");
|
||||||
|
}
|
||||||
|
|
||||||
|
chainId = reverseMapChainId(chainId as string);
|
||||||
|
if (!chainId || chainId !== matchChainId) {
|
||||||
|
throw new Error("Invalid Chain");
|
||||||
|
}
|
||||||
|
|
||||||
|
let method = args.method ?? false;
|
||||||
|
let endpoint = args.endpoint ?? false;
|
||||||
|
let data = args.data ?? false;
|
||||||
|
let query = args.query ?? false;
|
||||||
|
let fullHeaders = args.fullHeaders ?? {};
|
||||||
|
|
||||||
|
fullHeaders = { ...fullHeaders, Referer: "lumeweb_dns_relay" };
|
||||||
|
|
||||||
|
if (method) {
|
||||||
|
method = method.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endpoint) {
|
||||||
|
throw new Error("Endpoint Missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
let found = false;
|
||||||
|
|
||||||
|
for (const theEndpoint in allowedEndpoints) {
|
||||||
|
if (minimatch(endpoint, theEndpoint)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
throw new Error("Endpoint Invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
let apiUrl;
|
||||||
|
try {
|
||||||
|
apiUrl = sprintf(apiServer, chainId, POCKET_APP_ID);
|
||||||
|
} catch (e) {
|
||||||
|
apiUrl = apiServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = new HTTPClient({}, apiUrl);
|
||||||
|
let resp;
|
||||||
|
switch (method) {
|
||||||
|
case "GET":
|
||||||
|
resp = await client.get(endpoint, query, fullHeaders);
|
||||||
|
break;
|
||||||
|
case "POST":
|
||||||
|
if (Array.isArray(data?.data)) {
|
||||||
|
data = new Uint8Array(Buffer.from(data.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = await client.post(endpoint, data, { ...fullHeaders });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Method Invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCircularReplacer = () => {
|
||||||
|
const seen = new WeakSet();
|
||||||
|
return (key: string, value: any): any => {
|
||||||
|
if (typeof value === "object" && value !== null) {
|
||||||
|
if (seen.has(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return JSON.parse(JSON.stringify(resp, getCircularReplacer()));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
algorand_rest_request: proxyRestMethod(
|
||||||
|
"http://mainnet-api.algonode.network",
|
||||||
|
"algorand-mainnet"
|
||||||
|
),
|
||||||
|
//'algorand_rest_request': proxyRestMethod("https://%s.gateway.pokt.network/v1/lb/%s", "algorand-mainnet"),
|
||||||
|
algorand_rest_indexer_request: proxyRestMethod(
|
||||||
|
"http://mainnet-idx.algonode.network",
|
||||||
|
"algorand-mainnet-indexer"
|
||||||
|
),
|
||||||
|
} as RpcMethodList;
|
|
@ -0,0 +1,100 @@
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { PocketAAT } from "@pokt-network/pocket-js";
|
||||||
|
import { maybeMapChainId, reverseMapChainId } from "../util.js";
|
||||||
|
import { Connection } from "@solana/web3.js";
|
||||||
|
import {
|
||||||
|
POCKET_APP_ID,
|
||||||
|
POCKET_APP_KEY,
|
||||||
|
usePocketGateway,
|
||||||
|
} from "../constants.js";
|
||||||
|
import { getAat, getPocketServer } from "../rpc.js";
|
||||||
|
|
||||||
|
export const chainNetworks = require("../../networks.json");
|
||||||
|
|
||||||
|
type RpcProviderMethod = (method: string, params: Array<any>) => Promise<any>;
|
||||||
|
|
||||||
|
const gatewayProviders: { [name: string]: RpcProviderMethod } = {};
|
||||||
|
|
||||||
|
const gatewayMethods: {
|
||||||
|
[name: string]: (chainId: string) => RpcProviderMethod;
|
||||||
|
} = {
|
||||||
|
default: (chainId: string): RpcProviderMethod => {
|
||||||
|
const provider = new ethers.providers.JsonRpcProvider({
|
||||||
|
url: `https://${chainId}.gateway.pokt.network/v1/lb/${POCKET_APP_ID}`,
|
||||||
|
password: <string>POCKET_APP_KEY,
|
||||||
|
});
|
||||||
|
return provider.send.bind(provider);
|
||||||
|
},
|
||||||
|
"sol-mainnet": (chainId: string): RpcProviderMethod => {
|
||||||
|
const provider = new Connection(
|
||||||
|
`https://solana-mainnet.gateway.pokt.network/v1/lb/${POCKET_APP_ID}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return provider._rpcRequest.bind(provider);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function proxyRpcMethod(
|
||||||
|
method: string,
|
||||||
|
chains: string[] = []
|
||||||
|
): Function {
|
||||||
|
return async function (args: any, context: object) {
|
||||||
|
// @ts-ignore
|
||||||
|
let chain = context.chain;
|
||||||
|
let chainId = maybeMapChainId(chain);
|
||||||
|
|
||||||
|
let chainMatch = true;
|
||||||
|
|
||||||
|
if (
|
||||||
|
chains.length > 0 &&
|
||||||
|
!chains.includes(chain) &&
|
||||||
|
!chains.includes(chainId.toString())
|
||||||
|
) {
|
||||||
|
chainMatch = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chainId || !chainMatch) {
|
||||||
|
throw new Error("Invalid Chain");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usePocketGateway()) {
|
||||||
|
chainId = reverseMapChainId(chainId as string);
|
||||||
|
if (!chainId) {
|
||||||
|
throw new Error("Invalid Chain");
|
||||||
|
}
|
||||||
|
|
||||||
|
let provider: RpcProviderMethod | boolean =
|
||||||
|
gatewayProviders[chainId as string] || false;
|
||||||
|
if (!provider) {
|
||||||
|
provider = getRpcProvider(chainId as string);
|
||||||
|
}
|
||||||
|
gatewayProviders[chainId as string] = provider;
|
||||||
|
return await provider(method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await sendRelay(JSON.stringify(args), <string>chainId, getAat());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call this every time you want to fetch RPC data
|
||||||
|
async function sendRelay(
|
||||||
|
rpcQuery: string,
|
||||||
|
blockchain: string,
|
||||||
|
pocketAAT: PocketAAT
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await getPocketServer().sendRelay(rpcQuery, blockchain, pocketAAT);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRpcProvider(chain: string): RpcProviderMethod {
|
||||||
|
if (chain in gatewayMethods) {
|
||||||
|
return gatewayMethods[chain](chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gatewayMethods.default(chain);
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { isIp } from "../util.js";
|
||||||
|
import { RpcMethodList } from "./index.js";
|
||||||
|
|
||||||
|
const bns = require("bns");
|
||||||
|
const { StubResolver, RecursiveResolver } = bns;
|
||||||
|
|
||||||
|
const resolverOpt = {
|
||||||
|
tcp: true,
|
||||||
|
inet6: false,
|
||||||
|
edns: true,
|
||||||
|
dnssec: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const globalResolver = new RecursiveResolver(resolverOpt);
|
||||||
|
globalResolver.hints.setDefault();
|
||||||
|
globalResolver.open();
|
||||||
|
|
||||||
|
async function resolveNameServer(ns: string): Promise<string | boolean> {
|
||||||
|
if (isIp(ns)) {
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
let result = await getDnsRecords(ns, "A");
|
||||||
|
|
||||||
|
if (result.length) {
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDnsRecords(
|
||||||
|
domain: string,
|
||||||
|
type: string,
|
||||||
|
authority: boolean = false,
|
||||||
|
resolver = globalResolver
|
||||||
|
): Promise<string[]> {
|
||||||
|
let result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = await resolver.lookup(domain, type);
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let prop = authority ? "authority" : "answer";
|
||||||
|
|
||||||
|
if (!result || !result[prop].length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result[prop].map(
|
||||||
|
(item: object) =>
|
||||||
|
// @ts-ignore
|
||||||
|
item.data.address ?? item.data.target ?? item.data.ns ?? null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
dnslookup: async function (args: any) {
|
||||||
|
let dnsResults: string[] = [];
|
||||||
|
let domain = args.domain;
|
||||||
|
let ns = args.nameserver;
|
||||||
|
let dnsResolver = ns ? new StubResolver(resolverOpt) : globalResolver;
|
||||||
|
await dnsResolver.open();
|
||||||
|
|
||||||
|
if (ns) {
|
||||||
|
let nextNs = ns;
|
||||||
|
let prevNs = null;
|
||||||
|
|
||||||
|
while (nextNs) {
|
||||||
|
nextNs = await resolveNameServer(nextNs);
|
||||||
|
if (!nextNs) {
|
||||||
|
nextNs = prevNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsResolver.setServers([nextNs]);
|
||||||
|
|
||||||
|
if (nextNs === prevNs) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let result = await getDnsRecords(domain, "NS", true, dnsResolver);
|
||||||
|
prevNs = nextNs;
|
||||||
|
nextNs = result.length ? result[0] : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const queryType of ["CNAME", "A"]) {
|
||||||
|
let result = await getDnsRecords(domain, queryType, false, dnsResolver);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
dnsResults = dnsResults.concat(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await dnsResolver.close();
|
||||||
|
|
||||||
|
dnsResults = dnsResults.filter(Boolean);
|
||||||
|
|
||||||
|
if (dnsResults.length) {
|
||||||
|
return dnsResults[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
} as RpcMethodList;
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { proxyRpcMethod } from "./common.js";
|
||||||
|
import { RpcMethodList } from "./index.js";
|
||||||
|
|
||||||
|
const rpcMethods: RpcMethodList = {};
|
||||||
|
|
||||||
|
function proxyEvmRpcMethod(method: string): Function {
|
||||||
|
return proxyRpcMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
["eth_call", "eth_chainId", "net_version"].forEach((method) => {
|
||||||
|
rpcMethods[method] = proxyEvmRpcMethod(method);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default rpcMethods;
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { RpcMethodList } from "./index.js";
|
||||||
|
import {
|
||||||
|
HSD_API_KEY,
|
||||||
|
HSD_HOST,
|
||||||
|
HSD_NETWORK_TYPE,
|
||||||
|
HSD_PORT,
|
||||||
|
} from "../constant_vars.js";
|
||||||
|
|
||||||
|
const { NodeClient } = require("hs-client");
|
||||||
|
|
||||||
|
const hnsClient = new NodeClient({
|
||||||
|
network: HSD_NETWORK_TYPE,
|
||||||
|
host: HSD_HOST,
|
||||||
|
port: HSD_PORT,
|
||||||
|
apiKey: HSD_API_KEY,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getnameresource: async function (args: any, context: object) {
|
||||||
|
// @ts-ignore
|
||||||
|
if ("hns" !== context.chain) {
|
||||||
|
throw new Error("Invalid Chain");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await hnsClient.execute("getnameresource", args);
|
||||||
|
},
|
||||||
|
} as RpcMethodList;
|
|
@ -0,0 +1,18 @@
|
||||||
|
export type RpcMethodList = { [name: string]: Function };
|
||||||
|
|
||||||
|
export * from "./common.js";
|
||||||
|
|
||||||
|
import { default as DnsMethods } from "./dns.js";
|
||||||
|
import { default as EvmMethods } from "./evm.js";
|
||||||
|
import { default as HnsMethods } from "./handshake.js";
|
||||||
|
import { default as SolMethods } from "./solana.js";
|
||||||
|
import { default as AlgoMethods } from "./algorand.js";
|
||||||
|
|
||||||
|
export const rpcMethods: RpcMethodList = Object.assign(
|
||||||
|
{},
|
||||||
|
DnsMethods,
|
||||||
|
EvmMethods,
|
||||||
|
HnsMethods,
|
||||||
|
SolMethods,
|
||||||
|
AlgoMethods
|
||||||
|
);
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { chainNetworks, proxyRpcMethod } from "./common.js";
|
||||||
|
import { RpcMethodList } from "./index.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAccountInfo: proxyRpcMethod("getAccountInfo", [
|
||||||
|
chainNetworks["sol-mainnet"],
|
||||||
|
]),
|
||||||
|
} as RpcMethodList;
|
33
src/util.ts
33
src/util.ts
|
@ -1,4 +1,37 @@
|
||||||
|
import { chainNetworks } from "./rpc/index.js";
|
||||||
|
|
||||||
export function errorExit(msg: string): void {
|
export function errorExit(msg: string): void {
|
||||||
console.error(msg);
|
console.error(msg);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function maybeMapChainId(chain: string): string | boolean {
|
||||||
|
if (chain in chainNetworks) {
|
||||||
|
return chainNetworks[chain];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[parseInt(chain, 16).toString(), parseInt(chain, 10).toString()].includes(
|
||||||
|
chain.toLowerCase()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reverseMapChainId(chainId: string): string | boolean {
|
||||||
|
let vals = Object.values(chainNetworks);
|
||||||
|
if (!vals.includes(chainId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(chainNetworks)[vals.indexOf(chainId)];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isIp(ip: string) {
|
||||||
|
return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(
|
||||||
|
ip
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue