Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement EboActor.onRequestFinalized handler #23

Merged
merged 1 commit into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions packages/automated-dispute/src/eboActor.ts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wdyt about having the callback function as parameter of the onRequestFinalized instead of the constructor?
it will make it obvious that the function will be called from there

Copy link
Collaborator Author

@0xyaco 0xyaco Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great proposal, you got me thinking why did I go for this option lol 🤔

=== PR #24 spoiler ahead ===
There are some situations in which that onTerminate callback is being used outside the onRequestFinalized handler (eg the request was "escalated", so the agent stops tracking it as it will be handled by another system outside the EBO agent)

Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,32 @@ import { ProtocolProvider } from "./protocolProvider.js";
import { EboEvent } from "./types/events.js";
import { Dispute, Request, Response, ResponseBody } from "./types/prophet.js";

type OnTerminateActorCallback = (request: Request) => Promise<void>;

/**
* Actor that handles a singular Prophet's request asking for the block number that corresponds
* to an instant on an indexed chain.
*/
export class EboActor {
/**
* Creates an `EboActor` instance.
*
* @param actorRequest.id request ID this actor will handle
* @param actorRequest.epoch requested epoch
* @param actorRequest.epoch requested epoch's timestamp
* @param onTerminate callback to be run when this instance is being terminated
* @param protocolProvider a `ProtocolProvider` instance
* @param blockNumberService a `BlockNumberService` instance
* @param registry an `EboRegistry` instance
* @param logger an `ILogger` instance
*/
constructor(
private readonly actorRequest: {
id: string;
epoch: bigint;
epochTimestamp: bigint;
},
private readonly onTerminate: OnTerminateActorCallback,
private readonly protocolProvider: ProtocolProvider,
private readonly blockNumberService: BlockNumberService,
private readonly registry: EboRegistry,
Expand Down Expand Up @@ -332,9 +347,17 @@ export class EboActor {
}
}

public async onFinalizeRequest(_event: EboEvent<"RequestFinalizable">): Promise<void> {
// TODO: implement
return;
/**
* Handle the `ResponseFinalized` event.
*
* @param event `ResponseFinalized` event
*/
public async onRequestFinalized(event: EboEvent<"RequestFinalized">): Promise<void> {
this.shouldHandleRequest(event.metadata.requestId);

const request = this.getActorRequest();

await this.onTerminate(request);
}

public async onDisputeStatusChanged(_event: EboEvent<"DisputeStatusChanged">): Promise<void> {
Expand Down
13 changes: 3 additions & 10 deletions packages/automated-dispute/src/types/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export type EboEventName =
| "ResponseDisputed"
| "DisputeStatusChanged"
| "DisputeEscalated"
| "RequestFinalizable"
| "RequestFinalized";

export interface NewEpoch {
Expand Down Expand Up @@ -49,10 +48,6 @@ export interface DisputeEscalated {
blockNumber: bigint;
}

export interface RequestFinalizable {
requestId: string;
}

Comment on lines -52 to -55
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting stuff related to RequestFinalizable, as this case will be handled by the agent's periodic checks; not event related.

export interface RequestFinalized {
requestId: string;
responseId: string;
Expand All @@ -72,11 +67,9 @@ export type EboEventData<E extends EboEventName> = E extends "NewEpoch"
? DisputeStatusChanged
: E extends "DisputeEscalated"
? DisputeEscalated
: E extends "RequestFinalizable"
? RequestFinalizable
: E extends "RequestFinalized"
? RequestFinalized
: never;
: E extends "RequestFinalized"
? RequestFinalized
: never;

export type EboEvent<T extends EboEventName> = {
name: T;
Expand Down
5 changes: 5 additions & 0 deletions packages/automated-dispute/tests/eboActor/mocks/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BlockNumberService } from "@ebo-agent/blocknumber";
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types";
import { ILogger } from "@ebo-agent/shared";
import { vi } from "vitest";

import { EboActor } from "../../../src/eboActor";
import { EboMemoryRegistry } from "../../../src/eboMemoryRegistry";
Expand All @@ -18,6 +19,8 @@ import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS } from "../fixtures";
function buildEboActor(request: Request, logger: ILogger) {
const { id, chainId, epoch, epochTimestamp } = request;

const onTerminate = vi.fn();

const protocolProviderRpcUrls = ["http://localhost:8538"];
const protocolProvider = new ProtocolProvider(
protocolProviderRpcUrls,
Expand All @@ -33,6 +36,7 @@ function buildEboActor(request: Request, logger: ILogger) {

const actor = new EboActor(
{ id, epoch, epochTimestamp },
onTerminate,
protocolProvider,
blockNumberService,
registry,
Expand All @@ -41,6 +45,7 @@ function buildEboActor(request: Request, logger: ILogger) {

return {
actor,
onTerminate,
protocolProvider,
blockNumberService,
registry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ describe("EboActor", () => {
},
};

const onTerminate = vi.fn();

let protocolProvider: ProtocolProvider;
let blockNumberService: BlockNumberService;
let registry: EboMemoryRegistry;
Expand Down Expand Up @@ -88,6 +90,7 @@ describe("EboActor", () => {

const actor = new EboActor(
requestConfig,
onTerminate,
protocolProvider,
blockNumberService,
registry,
Expand Down Expand Up @@ -130,6 +133,7 @@ describe("EboActor", () => {

const actor = new EboActor(
requestConfig,
onTerminate,
protocolProvider,
blockNumberService,
registry,
Expand Down Expand Up @@ -179,6 +183,7 @@ describe("EboActor", () => {

const actor = new EboActor(
requestConfig,
onTerminate,
protocolProvider,
blockNumberService,
registry,
Expand All @@ -201,6 +206,7 @@ describe("EboActor", () => {

const actor = new EboActor(
requestConfig,
onTerminate,
protocolProvider,
blockNumberService,
registry,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ILogger } from "@ebo-agent/shared";
import { describe, expect, it, vi } from "vitest";

import { InvalidActorState } from "../../src/exceptions/invalidActorState.exception.js";
import { EboEvent } from "../../src/types/events.js";
import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "./fixtures.js";
import mocks from "./mocks/index.js";

const logger: ILogger = {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
};

describe("EboActor", () => {
describe("onRequestFinalized", () => {
const actorRequest = DEFAULT_MOCKED_REQUEST_CREATED_DATA;

const event: EboEvent<"RequestFinalized"> = {
name: "RequestFinalized",
blockNumber: 1n,
logIndex: 1,
metadata: {
blockNumber: 1n,
caller: "0x01",
requestId: actorRequest.id,
responseId: "0x02",
},
};

it("executes the actor's callback during termination", async () => {
const { actor, onTerminate, registry } = mocks.buildEboActor(actorRequest, logger);

vi.spyOn(registry, "getRequest").mockReturnValue(actorRequest);

onTerminate.mockImplementation(() => Promise.resolve());

await actor.onRequestFinalized(event);

expect(onTerminate).toHaveBeenCalledWith(actorRequest);
});

it("throws if the event's request is not handled by actor", () => {
const { actor } = mocks.buildEboActor(actorRequest, logger);

const otherRequestEvent = {
...event,
metadata: {
...event.metadata,
requestId: actorRequest.id + "123",
},
};

expect(actor.onRequestFinalized(otherRequestEvent)).rejects.toThrow(InvalidActorState);
});

// The one who defines the callback is responsible for handling callback errors
it("throws if the callback throws", () => {
const { actor, onTerminate } = mocks.buildEboActor(actorRequest, logger);

onTerminate.mockImplementation(() => {
throw new Error();
});

expect(actor.onRequestFinalized(event)).rejects.toThrow(InvalidActorState);
});
});
});