Added customizable quorum to FallbackProvider (#4160).

This commit is contained in:
Richard Moore 2023-07-27 19:51:50 -04:00
parent 229145ddf5
commit 8f0a50921a
2 changed files with 44 additions and 9 deletions

View File

@ -11,6 +11,7 @@ import { QuickNodeProvider } from "./provider-quicknode.js";
import { FallbackProvider } from "./provider-fallback.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import { Network } from "./network.js";
import { WebSocketProvider } from "./provider-websocket.js";
import type { AbstractProvider } from "./abstract-provider.js";
@ -22,6 +23,8 @@ function isWebSocketLike(value: any): value is WebSocketLike {
typeof(value.close) === "function");
}
const Testnets = "goerli kovan sepolia classicKotti optimism-goerli arbitrum-goerli matic-mumbai bnbt".split(" ");
export function getDefaultProvider(network: string | Networkish | WebSocketLike, options?: any): AbstractProvider {
if (options == null) { options = { }; }
@ -33,6 +36,13 @@ export function getDefaultProvider(network: string | Networkish | WebSocketLike,
return new WebSocketProvider(network);
}
// Get the network name, if possible
let name: null | string = null;
try {
name = Network.from(network).name;
} catch (error) { }
const providers: Array<AbstractProvider> = [ ];
if (options.alchemy !== "-") {
@ -96,7 +106,19 @@ export function getDefaultProvider(network: string | Networkish | WebSocketLike,
operation: "getDefaultProvider"
});
// No need for a FallbackProvider
if (providers.length === 1) { return providers[0]; }
return new FallbackProvider(providers);
// We use the floor because public third-party providers can be unreliable,
// so a low number of providers with a large quorum will fail too often
let quorum = Math.floor(providers.length / 2);
// Testnets don't need as strong a security gaurantee and speed is
// more useful during testing
if (name && Testnets.indexOf(name) !== -1) { quorum = 1; }
// Provided override qorum takes priority
if (options && options.quorum) { quorum = options.quorum; }
return new FallbackProvider(providers, undefined, { quorum });
}

View File

@ -165,16 +165,20 @@ async function waitForSync(config: Config, blockNumber: number): Promise<void> {
export type FallbackProviderOptions = {
// How many providers must agree on a value before reporting
// back the response
quorum: number;
quorum?: number;
// How many providers must have reported the same event
// for it to be emitted
eventQuorum: number;
// for it to be emitted (currently unimplmented)
eventQuorum?: number;
// How many providers to dispatch each event to simultaneously.
// Set this to 0 to use getLog polling, which implies eventQuorum
// is equal to quorum.
eventWorkers: number;
// is equal to quorum. (currently unimplemented)
eventWorkers?: number;
cacheTimeout?: number;
pollingInterval?: number;
};
type RunnerResult = { result: any } | { error: Error };
@ -380,8 +384,9 @@ export class FallbackProvider extends AbstractProvider {
* If a [[Provider]] is included in %%providers%%, defaults are used
* for the configuration.
*/
constructor(providers: Array<AbstractProvider | FallbackProviderConfig>, network?: Networkish) {
super(network);
constructor(providers: Array<AbstractProvider | FallbackProviderConfig>, network?: Networkish, options?: FallbackProviderOptions) {
super(network, options);
this.#configs = providers.map((p) => {
if (p instanceof AbstractProvider) {
return Object.assign({ provider: p }, defaultConfig, defaultState );
@ -393,7 +398,15 @@ export class FallbackProvider extends AbstractProvider {
this.#height = -2;
this.#initialSyncPromise = null;
this.quorum = 2; //Math.ceil(providers.length / 2);
if (options && options.quorum != null) {
this.quorum = options.quorum;
} else {
this.quorum = Math.ceil(this.#configs.reduce((accum, config) => {
accum += config.weight;
return accum;
}, 0) / 2);
}
this.eventQuorum = 1;
this.eventWorkers = 1;