Skip to content

Commit

Permalink
feat: on last event (#34)
Browse files Browse the repository at this point in the history
# 🤖 Linear

Closes GRT-153

## Description
* Renames `onNewEvent` to `onLastEvent` (seems more accurate)
* Uses the new "last event" handlers during the enqueued events
processing
* Fixes last event handlers tests (most of their cases were skipped
until this was implemented)
* Refactors the `shouldHandleEvent` function to reflect its actual name
lol
  • Loading branch information
0xyaco authored Sep 5, 2024
1 parent 3733332 commit fccd01f
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 317 deletions.
77 changes: 52 additions & 25 deletions packages/automated-dispute/src/eboActor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class EboActor {
* @param event EBO event
*/
public enqueue(event: EboEvent<EboEventName>): void {
if (this.shouldHandleRequest(event.requestId)) {
if (!this.shouldHandleRequest(event.requestId)) {
this.logger.error(`The request ${event.requestId} is not handled by this actor.`);

throw new RequestMismatch(this.actorRequest.id, event.requestId);
Expand Down Expand Up @@ -143,7 +143,7 @@ export class EboActor {
if (this.eventsQueue.isEmpty()) {
// `event` is the last and most recent event thus
// it needs to run some RPCs to keep Prophet's flow going on
await this.onNewEvent(event);
await this.onLastEvent(event);
}
} catch (err) {
this.logger.error(`Error processing event ${event.name}: ${err}`);
Expand Down Expand Up @@ -209,15 +209,47 @@ export class EboActor {
}

/**
* Handle a new event and triggers reactive interactions with smart contracts.
* Handle the last known event and triggers reactive interactions with smart contracts.
*
* A basic example would be reacting to a new request by proposing a response.
*
* @param _event EBO event
* @param event EBO event
*/
private async onNewEvent(_event: EboEvent<EboEventName>) {
// TODO
return;
private async onLastEvent(event: EboEvent<EboEventName>) {
switch (event.name) {
case "RequestCreated":
await this.onRequestCreated(event as EboEvent<"RequestCreated">);

break;

case "ResponseProposed":
await this.onResponseProposed(event as EboEvent<"ResponseProposed">);

break;

case "ResponseDisputed":
await this.onResponseDisputed(event as EboEvent<"ResponseDisputed">);

break;

case "DisputeStatusChanged":
await this.onDisputeStatusChanged(event as EboEvent<"DisputeStatusChanged">);

break;

case "DisputeEscalated":
await this.onDisputeEscalated(event as EboEvent<"DisputeEscalated">);

break;

case "RequestFinalized":
await this.onRequestFinalized(event as EboEvent<"RequestFinalized">);

break;

default:
throw new UnknownEvent(event.name);
}
}

/**
Expand Down Expand Up @@ -404,15 +436,7 @@ export class EboActor {
*
* @param event `RequestCreated` event
*/
public async onRequestCreated(event: EboEvent<"RequestCreated">): Promise<void> {
if (this.registry.getRequest(event.metadata.requestId)) {
this.logger.error(
`The request ${event.metadata.requestId} was already being handled by an actor.`,
);

throw new InvalidActorState();
}

private async onRequestCreated(event: EboEvent<"RequestCreated">): Promise<void> {
if (this.anyActiveProposal()) {
// Skipping new proposal until the actor receives a ResponseDisputed event;
// at that moment, it will be possible to re-propose again.
Expand Down Expand Up @@ -539,7 +563,7 @@ export class EboActor {
* @param event a `ResponseProposed` event
* @returns void
*/
public async onResponseProposed(event: EboEvent<"ResponseProposed">): Promise<void> {
private async onResponseProposed(event: EboEvent<"ResponseProposed">): Promise<void> {
const eventResponse = event.metadata.response;
const actorResponse = await this.buildResponse(eventResponse.response.chainId);

Expand Down Expand Up @@ -578,7 +602,7 @@ export class EboActor {
* @returns `true` if the actor is handling the request, `false` otherwise
*/
private shouldHandleRequest(requestId: string) {
return this.actorRequest.id.toLowerCase() !== requestId.toLowerCase();
return this.actorRequest.id.toLowerCase() === requestId.toLowerCase();
}

/**
Expand All @@ -600,7 +624,7 @@ export class EboActor {
*
* @param event `ResponseDisputed` event.
*/
public async onResponseDisputed(event: EboEvent<"ResponseDisputed">): Promise<void> {
private async onResponseDisputed(event: EboEvent<"ResponseDisputed">): Promise<void> {
const dispute = this.registry.getDispute(event.metadata.disputeId);

if (!dispute)
Expand Down Expand Up @@ -699,7 +723,7 @@ export class EboActor {
*
* @param event `DisputeStatusChanged` event
*/
public async onDisputeStatusChanged(event: EboEvent<"DisputeStatusChanged">): Promise<void> {
private async onDisputeStatusChanged(event: EboEvent<"DisputeStatusChanged">): Promise<void> {
const request = this.getActorRequest();
const disputeId = event.metadata.disputeId;
const disputeStatus = event.metadata.status;
Expand Down Expand Up @@ -730,9 +754,14 @@ export class EboActor {
}
}

private async onDisputeEscalated(disputeId: string, request: Request) {
private async onDisputeEscalated(event: EboEvent<"DisputeEscalated">) {
const request = this.getActorRequest();

// TODO: notify
this.logger.info(`Dispute ${disputeId} for request ${request.id} has been escalated.`);

this.logger.info(
`Dispute ${event.metadata.disputeId} for request ${request.id} has been escalated.`,
);
}

private async onDisputeWithNoResolution(disputeId: string, request: Request) {
Expand Down Expand Up @@ -763,9 +792,7 @@ export class EboActor {
*
* @param event `ResponseFinalized` event
*/
public async onRequestFinalized(event: EboEvent<"RequestFinalized">): Promise<void> {
this.shouldHandleRequest(event.metadata.requestId);

private async onRequestFinalized(_event: EboEvent<"RequestFinalized">): Promise<void> {
const request = this.getActorRequest();

this.logger.info(`Request ${request.id} has been finalized.`);
Expand Down
34 changes: 13 additions & 21 deletions packages/automated-dispute/src/types/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,13 @@ import { Address, Log } from "viem";
import { Dispute, DisputeStatus, Request, RequestId, Response } from "./prophet.js";

export type EboEventName =
| "NewEpoch"
| "RequestCreated"
| "ResponseProposed"
| "ResponseDisputed"
| "DisputeStatusChanged"
| "DisputeEscalated"
| "RequestFinalized";

export interface NewEpoch {
epoch: bigint;
epochBlockNumber: bigint;
}

export interface ResponseProposed {
requestId: string;
responseId: string;
Expand Down Expand Up @@ -56,21 +50,19 @@ export interface RequestFinalized {
blockNumber: bigint;
}

export type EboEventData<E extends EboEventName> = E extends "NewEpoch"
? NewEpoch
: E extends "RequestCreated"
? RequestCreated
: E extends "ResponseProposed"
? ResponseProposed
: E extends "ResponseDisputed"
? ResponseDisputed
: E extends "DisputeStatusChanged"
? DisputeStatusChanged
: E extends "DisputeEscalated"
? DisputeEscalated
: E extends "RequestFinalized"
? RequestFinalized
: never;
export type EboEventData<E extends EboEventName> = E extends "RequestCreated"
? RequestCreated
: E extends "ResponseProposed"
? ResponseProposed
: E extends "ResponseDisputed"
? ResponseDisputed
: E extends "DisputeStatusChanged"
? DisputeStatusChanged
: E extends "DisputeEscalated"
? DisputeEscalated
: E extends "RequestFinalized"
? RequestFinalized
: never;

export type EboEvent<T extends EboEventName> = {
name: T;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ const logger: ILogger = {
debug: vi.fn(),
};

describe.skip("onDisputeStatusChanged", () => {
describe("onDisputeStatusChanged", () => {
const actorRequest = DEFAULT_MOCKED_REQUEST_CREATED_DATA;
const response = mocks.buildResponse(actorRequest);

it("updates the state of the dispute", async () => {
const dispute = mocks.buildDispute(actorRequest, response, { status: "None" });
const event: EboEvent<"DisputeStatusChanged"> = {
name: "DisputeStatusChanged",
requestId: actorRequest.id,
blockNumber: 1n,
logIndex: 1,
metadata: {
Expand All @@ -37,17 +38,18 @@ describe.skip("onDisputeStatusChanged", () => {

const mockUpdateDisputeStatus = vi.spyOn(registry, "updateDisputeStatus");

await actor.onDisputeStatusChanged(event);
actor.enqueue(event);

await actor.processEvents();

expect(mockUpdateDisputeStatus).toHaveBeenCalledWith(dispute.id, "Lost");
});

it.skip("notifies when dispute has been escalated");

it("proposes a new response when dispute status goes into NoResolution", async () => {
const dispute = mocks.buildDispute(actorRequest, response, { status: "Escalated" });
const event: EboEvent<"DisputeStatusChanged"> = {
name: "DisputeStatusChanged",
requestId: actorRequest.id,
blockNumber: 1n,
logIndex: 1,
metadata: {
Expand All @@ -65,13 +67,22 @@ describe.skip("onDisputeStatusChanged", () => {

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

vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue({
currentEpoch: actorRequest.epoch,
currentEpochBlockNumber: actorRequest.createdAt,
currentEpochTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0)),
});

vi.spyOn(blockNumberService, "getEpochBlockNumber").mockResolvedValue(
response.prophetData.response.block + 1n,
);

const mockProposeResponse = vi.spyOn(protocolProvider, "proposeResponse");

await actor.onDisputeStatusChanged(event);
actor.enqueue(event);

await actor.processEvents();

expect(mockProposeResponse).toHaveBeenCalledWith(
actorRequest.id,
Expand All @@ -81,5 +92,6 @@ describe.skip("onDisputeStatusChanged", () => {
);
});

it.skip("notifies when dispute has been escalated");
it.skip("notifies if it will duplicate old proposal when handling NoResolution");
});
Loading

0 comments on commit fccd01f

Please sign in to comment.