Added better CCIP debug tracing.

This commit is contained in:
Richard Moore 2022-11-09 02:51:07 -05:00
parent c85cc72564
commit 96d1eaabf3
3 changed files with 64 additions and 22 deletions

View File

@ -71,6 +71,33 @@ function getTag(prefix: string, value: any): string {
}); });
} }
export type DebugEventAbstractProvider = {
action: "sendCcipReadFetchRequest",
request: FetchRequest
index: number
urls: Array<string>
} | {
action: "receiveCcipReadFetchResult",
request: FetchRequest,
result: any
} | {
action: "receiveCcipReadFetchError",
request: FetchRequest,
result: any
} | {
action: "sendCcipReadCall",
transaction: { to: string, data: string }
} | {
action: "receiveCcipReadCallResult",
transaction: { to: string, data: string }
result: string
} | {
action: "receiveCcipReadCallError",
transaction: { to: string, data: string }
error: Error
};
// Only sub-classes overriding the _getSubscription method will care about this // Only sub-classes overriding the _getSubscription method will care about this
export type Subscription = { export type Subscription = {
type: "block" | "close" | "debug" | "network" | "pending", type: "block" | "close" | "debug" | "network" | "pending",
@ -203,9 +230,9 @@ async function getSubscription(_event: ProviderEvent, provider: AbstractProvider
function getTime(): number { return (new Date()).getTime(); } function getTime(): number { return (new Date()).getTime(); }
export interface ProviderPlugin { export interface AbstractProviderPlugin {
readonly name: string; readonly name: string;
validate(provider: Provider): ProviderPlugin; connect(provider: AbstractProvider): AbstractProviderPlugin;
} }
export type PerformActionFilter = { export type PerformActionFilter = {
@ -225,6 +252,9 @@ export interface PerformActionTransaction extends PreparedTransactionRequest {
} }
export type PerformActionRequest = { export type PerformActionRequest = {
method: "broadcastTransaction",
signedTransaction: string
} | {
method: "call", method: "call",
transaction: PerformActionTransaction, blockTag: BlockTag transaction: PerformActionTransaction, blockTag: BlockTag
} | { } | {
@ -266,9 +296,6 @@ export type PerformActionRequest = {
} | { } | {
method: "getTransactionResult", method: "getTransactionResult",
hash: string hash: string
} | {
method: "broadcastTransaction", // @TODO: rename to broadcast
signedTransaction: string
}; };
type _PerformAccountRequest = { type _PerformAccountRequest = {
@ -290,7 +317,7 @@ type CcipArgs = {
export class AbstractProvider implements Provider { export class AbstractProvider implements Provider {
#subs: Map<string, Sub>; #subs: Map<string, Sub>;
#plugins: Map<string, ProviderPlugin>; #plugins: Map<string, AbstractProviderPlugin>;
// null=unpaused, true=paused+dropWhilePaused, false=paused // null=unpaused, true=paused+dropWhilePaused, false=paused
#pausedState: null | boolean; #pausedState: null | boolean;
@ -341,19 +368,19 @@ export class AbstractProvider implements Provider {
get provider(): this { return this; } get provider(): this { return this; }
get plugins(): Array<ProviderPlugin> { get plugins(): Array<AbstractProviderPlugin> {
return Array.from(this.#plugins.values()); return Array.from(this.#plugins.values());
} }
attachPlugin(plugin: ProviderPlugin): this { attachPlugin(plugin: AbstractProviderPlugin): this {
if (this.#plugins.get(plugin.name)) { if (this.#plugins.get(plugin.name)) {
throw new Error(`cannot replace existing plugin: ${ plugin.name } `); throw new Error(`cannot replace existing plugin: ${ plugin.name } `);
} }
this.#plugins.set(plugin.name, plugin.validate(this)); this.#plugins.set(plugin.name, plugin.connect(this));
return this; return this;
} }
getPlugin<T extends ProviderPlugin = ProviderPlugin>(name: string): null | T { getPlugin<T extends AbstractProviderPlugin = AbstractProviderPlugin>(name: string): null | T {
return <T>(this.#plugins.get(name)) || null; return <T>(this.#plugins.get(name)) || null;
} }
@ -406,13 +433,19 @@ export class AbstractProvider implements Provider {
request.body = { data, sender }; request.body = { data, sender };
} }
this.emit("debug", { action: "sendCcipReadFetchRequest", request, index: i, urls });
let errorMessage = "unknown error"; let errorMessage = "unknown error";
const resp = await request.send(); const resp = await request.send();
try { try {
const result = resp.bodyJson; const result = resp.bodyJson;
if (result.data) { return result.data; } if (result.data) {
this.emit("debug", { action: "receiveCcipReadFetchResult", request, result });
return result.data;
}
if (result.message) { errorMessage = result.message; } if (result.message) { errorMessage = result.message; }
this.emit("debug", { action: "receiveCcipReadFetchError", request, result });
} catch (error) { } } catch (error) { }
// 4xx indicates the result is not present; stop // 4xx indicates the result is not present; stop
@ -485,8 +518,9 @@ export class AbstractProvider implements Provider {
return blockTag; return blockTag;
} }
if (isHexString(blockTag)) { if (isHexString(blockTag)) {
if (dataLength(blockTag) === 32) { return blockTag; } if (isHexString(blockTag, 32)) { return blockTag; }
return toQuantity(blockTag); return toQuantity(blockTag);
} }
@ -737,12 +771,20 @@ export class AbstractProvider implements Provider {
assert(ccipResult != null, "CCIP Read failed to fetch data", "OFFCHAIN_FAULT", { assert(ccipResult != null, "CCIP Read failed to fetch data", "OFFCHAIN_FAULT", {
reason: "FETCH_FAILED", transaction, info: { data: error.data, errorArgs: ccipArgs.errorArgs } }); reason: "FETCH_FAILED", transaction, info: { data: error.data, errorArgs: ccipArgs.errorArgs } });
return this.#call({ const tx = {
to: txSender, to: txSender,
data: concat([ data: concat([ ccipArgs.selector, encodeBytes([ ccipResult, ccipArgs.extraData ]) ])
ccipArgs.selector, encodeBytes([ ccipResult, ccipArgs.extraData ]) };
]),
}, blockTag, attempt + 1); this.emit("debug", { action: "sendCcipReadCall", transaction: tx });
try {
const result = await this.#call(tx, blockTag, attempt + 1);
this.emit("debug", { action: "receiveCcipReadCallResult", transaction: Object.assign({ }, tx), result });
return result;
} catch (error) {
this.emit("debug", { action: "receiveCcipReadCallError", transaction: Object.assign({ }, tx), error });
throw error;
}
} }
throw error; throw error;

View File

@ -11,7 +11,7 @@ import {
import type { BigNumberish, BytesLike, EthersError } from "../utils/index.js"; import type { BigNumberish, BytesLike, EthersError } from "../utils/index.js";
import type { AbstractProvider, ProviderPlugin } from "./abstract-provider.js"; import type { AbstractProvider, AbstractProviderPlugin } from "./abstract-provider.js";
import type { EnsPlugin } from "./plugins-network.js"; import type { EnsPlugin } from "./plugins-network.js";
import type { TransactionRequest, Provider } from "./provider.js"; import type { TransactionRequest, Provider } from "./provider.js";
@ -117,14 +117,14 @@ export interface AvatarResult {
url: null | string; url: null | string;
}; };
export abstract class MulticoinProviderPlugin implements ProviderPlugin { export abstract class MulticoinProviderPlugin implements AbstractProviderPlugin {
readonly name!: string; readonly name!: string;
constructor(name: string) { constructor(name: string) {
defineProperties<MulticoinProviderPlugin>(this, { name }); defineProperties<MulticoinProviderPlugin>(this, { name });
} }
validate(proivder: Provider): ProviderPlugin { connect(proivder: Provider): MulticoinProviderPlugin {
return this; return this;
} }
@ -141,7 +141,7 @@ export abstract class MulticoinProviderPlugin implements ProviderPlugin {
} }
} }
const BasicMulticoinPluginId = "org.ethers.provider-prugins.basicmulticoin"; const BasicMulticoinPluginId = "org.ethers.plugins.BasicMulticoinProviderPlugin";
export class BasicMulticoinProviderPlugin extends MulticoinProviderPlugin { export class BasicMulticoinProviderPlugin extends MulticoinProviderPlugin {
constructor() { constructor() {

View File

@ -69,7 +69,7 @@ export {
export type { export type {
Subscription, Subscriber, Subscription, Subscriber,
ProviderPlugin, AbstractProviderPlugin,
PerformActionFilter, PerformActionTransaction, PerformActionRequest PerformActionFilter, PerformActionTransaction, PerformActionRequest
} from "./abstract-provider.js" } from "./abstract-provider.js"
export type { ContractRunner } from "./contracts.js"; export type { ContractRunner } from "./contracts.js";