Added support to detect and stop providers spinning on intitial network detection (#4015).
This commit is contained in:
parent
7dcecfa47d
commit
f37a52da28
@ -421,6 +421,8 @@ export class AbstractProvider implements Provider {
|
|||||||
// null=unpaused, true=paused+dropWhilePaused, false=paused
|
// null=unpaused, true=paused+dropWhilePaused, false=paused
|
||||||
#pausedState: null | boolean;
|
#pausedState: null | boolean;
|
||||||
|
|
||||||
|
#destroyed: boolean;
|
||||||
|
|
||||||
#networkPromise: null | Promise<Network>;
|
#networkPromise: null | Promise<Network>;
|
||||||
readonly #anyNetwork: boolean;
|
readonly #anyNetwork: boolean;
|
||||||
|
|
||||||
@ -462,6 +464,8 @@ export class AbstractProvider implements Provider {
|
|||||||
this.#plugins = new Map();
|
this.#plugins = new Map();
|
||||||
this.#pausedState = null;
|
this.#pausedState = null;
|
||||||
|
|
||||||
|
this.#destroyed = false;
|
||||||
|
|
||||||
this.#nextTimer = 1;
|
this.#nextTimer = 1;
|
||||||
this.#timers = new Map();
|
this.#timers = new Map();
|
||||||
|
|
||||||
@ -1445,9 +1449,20 @@ export class AbstractProvider implements Provider {
|
|||||||
return this.off(event, listener);
|
return this.off(event, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this provider has been destroyed using the [[destroy]] method.
|
||||||
|
*
|
||||||
|
* Once destroyed, all resources are reclaimed, internal event loops
|
||||||
|
* and timers are cleaned up and no further requests may be sent to
|
||||||
|
* the provider.
|
||||||
|
*/
|
||||||
|
get destroyed(): boolean {
|
||||||
|
return this.#destroyed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sub-classes may use this to shutdown any sockets or release their
|
* Sub-classes may use this to shutdown any sockets or release their
|
||||||
* resources.
|
* resources and reject any pending requests.
|
||||||
*
|
*
|
||||||
* Sub-classes **must** call ``super.destroy()``.
|
* Sub-classes **must** call ``super.destroy()``.
|
||||||
*/
|
*/
|
||||||
@ -1459,6 +1474,8 @@ export class AbstractProvider implements Provider {
|
|||||||
for (const timerId of this.#timers.keys()) {
|
for (const timerId of this.#timers.keys()) {
|
||||||
this._clearTimeout(timerId);
|
this._clearTimeout(timerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.#destroyed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -495,6 +495,11 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
|
|||||||
// Process results in batch order
|
// Process results in batch order
|
||||||
for (const { resolve, reject, payload } of batch) {
|
for (const { resolve, reject, payload } of batch) {
|
||||||
|
|
||||||
|
if (this.destroyed) {
|
||||||
|
reject(makeError("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: payload.method }));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Find the matching result
|
// Find the matching result
|
||||||
const resp = result.filter((r) => (r.id === payload.id))[0];
|
const resp = result.filter((r) => (r.id === payload.id))[0];
|
||||||
|
|
||||||
@ -506,7 +511,6 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
|
|||||||
// The response is an error
|
// The response is an error
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
return reject(this.getRpcError(payload, resp));
|
return reject(this.getRpcError(payload, resp));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All good; send the result
|
// All good; send the result
|
||||||
@ -578,13 +582,6 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
|
|||||||
* Sub-classes **MUST** override this.
|
* Sub-classes **MUST** override this.
|
||||||
*/
|
*/
|
||||||
abstract _send(payload: JsonRpcPayload | Array<JsonRpcPayload>): Promise<Array<JsonRpcResult | JsonRpcError>>;
|
abstract _send(payload: JsonRpcPayload | Array<JsonRpcPayload>): Promise<Array<JsonRpcResult | JsonRpcError>>;
|
||||||
/*
|
|
||||||
{
|
|
||||||
assert(false, "sub-classes must override _send", "UNSUPPORTED_OPERATION", {
|
|
||||||
operation: "jsonRpcApiProvider._send"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -678,11 +675,12 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
|
|||||||
(async () => {
|
(async () => {
|
||||||
|
|
||||||
// Bootstrap the network
|
// Bootstrap the network
|
||||||
while (this.#network == null) {
|
while (this.#network == null && !this.destroyed) {
|
||||||
try {
|
try {
|
||||||
this.#network = await this._detectNetwork();
|
this.#network = await this._detectNetwork();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("JsonRpcProvider failed to startup; retry in 1s");
|
console.log("JsonRpcProvider failed to detect network and cannot start up; retry in 1s (perhaps the URL is wrong or the node is not started)");
|
||||||
|
this.emit("error", makeError("failed to bootstrap network detection", "NETWORK_ERROR", { event: "initial-network-discovery", info: { error } }));
|
||||||
await stall(1000);
|
await stall(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -973,6 +971,11 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
|
|||||||
send(method: string, params: Array<any> | Record<string, any>): Promise<any> {
|
send(method: string, params: Array<any> | Record<string, any>): Promise<any> {
|
||||||
// @TODO: cache chainId?? purge on switch_networks
|
// @TODO: cache chainId?? purge on switch_networks
|
||||||
|
|
||||||
|
// We have been destroyed; no operations are supported anymore
|
||||||
|
if (this.destroyed) {
|
||||||
|
return Promise.reject(makeError("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: method }));
|
||||||
|
}
|
||||||
|
|
||||||
const id = this.#nextId++;
|
const id = this.#nextId++;
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
this.#payloads.push({
|
this.#payloads.push({
|
||||||
@ -1031,6 +1034,26 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
|
|||||||
const accounts: Array<string> = await this.send("eth_accounts", [ ]);
|
const accounts: Array<string> = await this.send("eth_accounts", [ ]);
|
||||||
return accounts.map((a) => new JsonRpcSigner(this, a));
|
return accounts.map((a) => new JsonRpcSigner(this, a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
|
||||||
|
// Stop processing requests
|
||||||
|
if (this.#drainTimer) {
|
||||||
|
clearTimeout(this.#drainTimer);
|
||||||
|
this.#drainTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel all pending requests
|
||||||
|
for (const { payload, reject } of this.#payloads) {
|
||||||
|
reject(makeError("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: payload.method }));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#payloads = [ ];
|
||||||
|
|
||||||
|
// Parent clean-up
|
||||||
|
super.destroy();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class JsonRpcApiPollingProvider extends JsonRpcApiProvider {
|
export abstract class JsonRpcApiPollingProvider extends JsonRpcApiProvider {
|
||||||
|
Loading…
Reference in New Issue
Block a user