diff --git a/package-lock.json b/package-lock.json index 8e15d43f..7eacb03a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@kenshi.io/unchained", - "version": "0.8.3", + "version": "0.8.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@kenshi.io/unchained", - "version": "0.8.3", + "version": "0.8.4", "license": "BUSL-1.1", "dependencies": { "@chainsafe/bls": "^7.1.2", diff --git a/package.json b/package.json index e3a76a9c..6e487d27 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "name": "@kenshi.io/unchained", - "version": "0.8.3", + "version": "0.8.4", "description": "", "main": "dist/index.js", "scripts": { "build": "tsc", - "lint": "tslint -p tsconfig.json" + "lint": "tslint -p tsconfig.json", + "prepublishOnly": "npm run build" }, "keywords": [], "bin": { diff --git a/src/index.ts b/src/index.ts index ecdcddd0..1a58a8c0 100755 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,7 @@ program .option("--log ", "log level") .option("--lite", "run in lite mode") .option("--generate", "generate a secret key") + .option("--gossip ", "set gossip bucket size") .action(startAction); program.parse(); diff --git a/src/lib/cli/actions/start.ts b/src/lib/cli/actions/start.ts index 4833294a..f6b9ce9b 100644 --- a/src/lib/cli/actions/start.ts +++ b/src/lib/cli/actions/start.ts @@ -14,6 +14,7 @@ interface StartOptions { log?: string; lite?: boolean; generate?: boolean; + gossip?: string; } export const startAction = async ( @@ -30,6 +31,7 @@ export const startAction = async ( logger.level = options.log || config.log || "info"; config.lite = options.lite || config.lite || false; + config.gossip = parseInt(options.gossip || "0") || config.gossip || 5; if (!config.secretKey && !options.generate) { logger.error("No secret key supplied"); diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 18de2ba6..eebc5764 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -7,7 +7,7 @@ import { StringGossipMethodObject, } from "./types.js"; -export const version = "0.8.3"; +export const version = "0.8.4"; export const protocolVersion = "0.8.0"; export const topic = sha(`Kenshi.Unchained.Testnet.Topic.V${protocolVersion}`); @@ -27,6 +27,7 @@ export const config: Config = { }, secretKey: "", lite: false, + gossip: 5, }; export const rpcMethods: StringAnyObject = {}; diff --git a/src/lib/gossip/index.ts b/src/lib/gossip/index.ts index b080d673..1cc9b944 100644 --- a/src/lib/gossip/index.ts +++ b/src/lib/gossip/index.ts @@ -1,4 +1,4 @@ -import { gossipMethods, errors, keys, sockets } from "../constants.js"; +import { gossipMethods, errors, keys, sockets, config } from "../constants.js"; import { encoder } from "../bls/keys.js"; import { Gossip, GossipRequest, MetaData, NodeSystemError } from "../types.js"; @@ -27,6 +27,15 @@ const gossipTo = async (nodes: MetaData[], data: any): Promise => { await Promise.all(promises).catch(() => null); }; +const randomDistinct = (length: number, count: number): number[] => { + const set = new Set(); + while (set.size < count) { + const random = randomIndex(length); + set.add(random); + } + return [...set]; +}; + export const gossip = async ( request: GossipRequest, seen: string[] @@ -47,11 +56,11 @@ export const gossip = async ( if (!nodes.length) { return; } - if (nodes.length <= 3) { + if (nodes.length <= config.gossip) { await gossipTo(nodes, payload); } else { - const random = new Array(3).fill(null).map(() => randomIndex(nodes.length)); - const chosen = [...new Set(random)].map((index) => nodes[index]); + const indexes = randomDistinct(nodes.length, config.gossip); + const chosen = indexes.map((index) => nodes[index]); await gossipTo(chosen, payload); } }; diff --git a/src/lib/plugins/uniswap/uniswap.ts b/src/lib/plugins/uniswap/uniswap.ts index 65f4b345..34831ba4 100644 --- a/src/lib/plugins/uniswap/uniswap.ts +++ b/src/lib/plugins/uniswap/uniswap.ts @@ -36,9 +36,25 @@ const ws = (endpoint: string): WebSocketLike => retry: { forever: true }, }) as WebSocketLike; -const getProvider = (config: Config) => { - if (!provider) { - const endpoint = config.rpc.ethereum; +let currentProvider: number = 0; + +const getNextConnectionUrl = (config: Config): string => { + if (typeof config.rpc.ethereum === "string") { + return config.rpc.ethereum; + } else { + if (currentProvider > config.rpc.ethereum.length) { + currentProvider = 0; + } + return config.rpc.ethereum[currentProvider++]; + } +}; + +const getProvider = (config: Config, fresh: boolean = false) => { + if (fresh || !provider) { + if (fresh) { + provider?.destroy(); + } + const endpoint = getNextConnectionUrl(config); provider = endpoint.startsWith("wss://") ? new ethers.WebSocketProvider(ws(endpoint)) : new ethers.JsonRpcProvider(endpoint); @@ -264,7 +280,9 @@ export const work = async ( ...signed, }; } catch (error) { - logger.warn("Could not get the Ethereum price. Check your RPC."); + logger.warn("Could not get the Ethereum price."); + logger.warn("Getting a new RPC connection."); + getProvider(config, true); throw error; } }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 69a7cb67..794150ef 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -11,7 +11,7 @@ export interface KeyPair { } interface RPCList { - ethereum: string; + ethereum: string | string[]; } interface DatabaseConfig { @@ -26,6 +26,7 @@ export interface Config { lite: boolean; database?: DatabaseConfig; secretKey: string; + gossip: number; } export interface MetaData {