Skip to content

Commit

Permalink
Added support to detect and stop providers spinning on intitial netwo…
Browse files Browse the repository at this point in the history
…rk detection (#4015).
  • Loading branch information
ricmoo committed Jun 6, 2023
1 parent 7dcecfa commit f37a52d
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 11 deletions.
19 changes: 18 additions & 1 deletion src.ts/providers/abstract-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ export class AbstractProvider implements Provider {
// null=unpaused, true=paused+dropWhilePaused, false=paused
#pausedState: null | boolean;

#destroyed: boolean;

#networkPromise: null | Promise<Network>;
readonly #anyNetwork: boolean;

Expand Down Expand Up @@ -462,6 +464,8 @@ export class AbstractProvider implements Provider {
this.#plugins = new Map();
this.#pausedState = null;

this.#destroyed = false;

this.#nextTimer = 1;
this.#timers = new Map();

Expand Down Expand Up @@ -1445,9 +1449,20 @@ export class AbstractProvider implements Provider {
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
* resources.
* resources and reject any pending requests.
*
* Sub-classes **must** call ``super.destroy()``.
*/
Expand All @@ -1459,6 +1474,8 @@ export class AbstractProvider implements Provider {
for (const timerId of this.#timers.keys()) {
this._clearTimeout(timerId);
}

this.#destroyed = true;
}

/**
Expand Down
43 changes: 33 additions & 10 deletions src.ts/providers/provider-jsonrpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,11 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
// Process results in batch order
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
const resp = result.filter((r) => (r.id === payload.id))[0];

Expand All @@ -506,7 +511,6 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
// The response is an error
if ("error" in resp) {
return reject(this.getRpcError(payload, resp));

}

// All good; send the result
Expand Down Expand Up @@ -578,13 +582,6 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
* Sub-classes **MUST** override this.
*/
abstract _send(payload: JsonRpcPayload | Array<JsonRpcPayload>): Promise<Array<JsonRpcResult | JsonRpcError>>;
/*
{
assert(false, "sub-classes must override _send", "UNSUPPORTED_OPERATION", {
operation: "jsonRpcApiProvider._send"
});
}
*/


/**
Expand Down Expand Up @@ -678,11 +675,12 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
(async () => {

// Bootstrap the network
while (this.#network == null) {
while (this.#network == null && !this.destroyed) {
try {
this.#network = await this._detectNetwork();
} 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);
}
}
Expand Down Expand Up @@ -973,6 +971,11 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
send(method: string, params: Array<any> | Record<string, any>): Promise<any> {
// @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 promise = new Promise((resolve, reject) => {
this.#payloads.push({
Expand Down Expand Up @@ -1031,6 +1034,26 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
const accounts: Array<string> = await this.send("eth_accounts", [ ]);
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 {
Expand Down

0 comments on commit f37a52d

Please # to comment.