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: build onRequestCreated #18

Merged
merged 14 commits into from
Aug 8, 2024
Merged
5 changes: 3 additions & 2 deletions packages/automated-dispute/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"author": "",
"license": "ISC",
"dependencies": {
"viem": "2.17.11",
"@ebo-agent/blocknumber": "workspace:*"
"@ebo-agent/blocknumber": "workspace:*",
"@ebo-agent/shared": "workspace:*",
"viem": "2.17.11"
}
}
59 changes: 52 additions & 7 deletions packages/automated-dispute/src/eboActor.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,68 @@
import { BlockNumberService } from "@ebo-agent/blocknumber";
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js";
import { ILogger } from "@ebo-agent/shared";

import { RequestMismatch } from "./exceptions/requestMismatch.js";
import { EboRegistry } from "./interfaces/eboRegistry.js";
import { ProtocolProvider } from "./protocolProvider.js";
import { EboEvent } from "./types/events.js";
import { Dispute, Response } from "./types/prophet.js";

export class EboActor {
private requestActivity: unknown[];

constructor(
private readonly protocolProvider: ProtocolProvider,
private readonly blockNumberService: BlockNumberService,
private readonly registry: EboRegistry,
private readonly requestId: string,
) {
this.requestActivity = [];
private readonly logger: ILogger,
) {}

/**
* Handle RequestCreated event.
*
* @param event RequestCreated event
*/
public async onRequestCreated(event: EboEvent<"RequestCreated">): Promise<void> {
if (event.metadata.requestId != this.requestId)
throw new RequestMismatch(this.requestId, event.metadata.requestId);

this.registry.addRequest(event.metadata.requestId, event.metadata.request);

const { chainId } = event.metadata;
const { currentEpoch, currentEpochTimestamp } =
await this.protocolProvider.getCurrentEpoch();

const epochBlockNumber = await this.blockNumberService.getEpochBlockNumber(
currentEpochTimestamp,
chainId,
);

if (this.alreadyProposed(currentEpoch, chainId, epochBlockNumber)) return;

await this.protocolProvider.proposeResponse(
this.requestId,
currentEpoch,
chainId,
epochBlockNumber,
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

we should handle the case in which proposeResponse fails, if it is an rpc failure or if a propose was already posted for the requestId

}

public async onRequestCreated(_event: EboEvent<"RequestCreated">): Promise<void> {
// TODO: implement
return;
private alreadyProposed(epoch: bigint, chainId: Caip2ChainId, blockNumber: bigint) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

would call this activePropose. We can have a propose on the registry that was already discarded

Copy link
Collaborator

Choose a reason for hiding this comment

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

also , we should add an isActive field to response object

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Does it make sense to have a new propose with the same block? I'd think that if there has already been a proposal with the block N, rejected or not, the agent wouldn't want to propose the same block N again. I might be missing something though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Left this as alreadyProposed, this will be the last thing to check on the agent side; it'd save it of trying to propose a response that has already been created. The activePropose check is done at the beginning of this method.

const responses = this.registry.getResponses();

for (const [responseId, response] of responses) {
if (response.response.block != blockNumber) continue;
if (response.response.chainId != chainId) continue;
if (response.response.epoch != epoch) continue;

this.logger.info(
`Block ${blockNumber} for epoch ${epoch} and chain ${chainId} already proposed on response ${responseId}. Skipping...`,
);

return true;
}

return false;
}

public async onResponseProposed(_event: EboEvent<"ResponseDisputed">): Promise<void> {
Expand Down
22 changes: 22 additions & 0 deletions packages/automated-dispute/src/eboMemoryRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { EboRegistry } from "./interfaces/eboRegistry.js";
import { Dispute, Request, Response } from "./types/prophet.js";

export class EboMemoryRegistry implements EboRegistry {
private requests: Map<string, Request>;
private responses: Map<string, Response>;
private dispute: Map<string, Dispute>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
private requests: Map<string, Request>;
private responses: Map<string, Response>;
private dispute: Map<string, Dispute>;
private requests: Map<string, Request>= new Map();
private responses: Map<string, Response>= new Map();
private dispute: Map<string, Dispute>= new Map();


constructor() {
this.requests = new Map();
this.responses = new Map();
this.dispute = new Map();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
this.requests = new Map();
this.responses = new Map();
this.dispute = new Map();

}

public addRequest(requestId: string, request: Request) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
public addRequest(requestId: string, request: Request) {
/** @inheritdoc */
public addRequest(requestId: string, request: Request) {

Copy link
Collaborator

Choose a reason for hiding this comment

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

ooaa nice, didn't know that. however, vscode is so magical that doesn't need this tag xd

this.requests.set(requestId, request);
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
/** @inheritdoc */

public getResponses() {
return this.responses;
}
}
6 changes: 6 additions & 0 deletions packages/automated-dispute/src/exceptions/requestMismatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class RequestMismatch extends Error {
constructor(requestId: string, eventRequestId: string) {
super(`Actor handling request ${requestId} received a request ${eventRequestId} event.`);
this.name = "RequestMismatch";
}
}
18 changes: 18 additions & 0 deletions packages/automated-dispute/src/interfaces/eboRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Request, Response } from "../types/prophet.js";

export interface EboRegistry {
Copy link
Collaborator

Choose a reason for hiding this comment

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

lets add a high level description here

/**
* Add a `Request` by ID.
*
* @param requestId the ID of the `Request`
* @param request the `Request`
*/
addRequest(requestId: string, request: Request): void;

/**
* Return all responses
*
* @returns responses map
*/
getResponses(): Map<string, Response>;
}
38 changes: 31 additions & 7 deletions packages/automated-dispute/src/protocolProvider.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js";
import { Timestamp } from "@ebo-agent/shared";
import {
Address,
createPublicClient,
Expand Down Expand Up @@ -53,17 +55,28 @@ export class ProtocolProvider {
}

/**
* Gets the current epoch and the block number of the current epoch
* @returns The current epoch and the block number of the current epoch
* Gets the current epoch, the block number and its timestamp of the current epoch
*
* @returns The current epoch, its block number and its timestamp
*/
async getCurrentEpoch(): Promise<{ currentEpoch: bigint; currentEpochBlock: bigint }> {
const [currentEpoch, currentEpochBlock] = await Promise.all([
async getCurrentEpoch(): Promise<{
currentEpoch: bigint;
currentEpochBlockNumber: bigint;
currentEpochTimestamp: Timestamp;
}> {
const [currentEpoch, currentEpochBlockNumber] = await Promise.all([
this.epochManagerContract.read.currentEpoch(),
this.epochManagerContract.read.currentEpochBlock(),
]);

const currentEpochBlock = await this.client.getBlock({
blockNumber: currentEpochBlockNumber,
});

return {
currentEpoch,
currentEpochBlock,
currentEpochBlockNumber,
currentEpochTimestamp: currentEpochBlock.timestamp,
};
}

Expand All @@ -79,6 +92,8 @@ export class ProtocolProvider {
logIndex: 1,
metadata: {
requestId: "0x01",
chainId: "eip155:1",
epoch: 1n,
request: {
requester: "0x12345678901234567890123456789012",
requestModule: "0x12345678901234567890123456789012",
Expand All @@ -102,7 +117,11 @@ export class ProtocolProvider {
response: {
proposer: "0x12345678901234567890123456789012",
requestId: "0x01",
response: "0x01234",
response: {
block: 1n,
chainId: "eip155:1",
epoch: 20n,
},
},
},
} as EboEvent<"ResponseProposed">,
Expand Down Expand Up @@ -173,7 +192,12 @@ export class ProtocolProvider {
return;
}

async proposeResponse(_request: Request, _response: Response): Promise<void> {
async proposeResponse(
_requestId: string,
_epoch: bigint,
_chainId: Caip2ChainId,
_blockNumber: bigint,
): Promise<void> {
// TODO: implement actual method
return;
}
Expand Down
16 changes: 10 additions & 6 deletions packages/automated-dispute/src/types/events.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js";
import { Log } from "viem";

import { Dispute, Request } from "./prophet.js";
import { Dispute, Request, Response } from "./prophet.js";

export type EboEventName =
| "NewEpoch"
Expand All @@ -17,14 +18,17 @@ export interface NewEpoch {
epochBlockNumber: bigint;
}

export interface ResponseCreated {
export interface ResponseProposed {
requestId: string;
request: Request;
responseId: string;
response: Response;
}

export interface RequestCreated {
requestId: string;
epoch: bigint;
chainId: Caip2ChainId;
request: Request;
requestId: string;
}

export interface ResponseDisputed {
Expand Down Expand Up @@ -60,8 +64,8 @@ export type EboEventData<E extends EboEventName> = E extends "NewEpoch"
? NewEpoch
: E extends "RequestCreated"
? RequestCreated
: E extends "ResponseCreated"
? ResponseCreated
: E extends "ResponseProposed"
? ResponseProposed
Copy link
Collaborator

Choose a reason for hiding this comment

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

🤣

: E extends "ResponseDisputed"
? ResponseDisputed
: E extends "DisputeStatusChanged"
Expand Down
9 changes: 8 additions & 1 deletion packages/automated-dispute/src/types/prophet.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js";
import { Address } from "viem";

export interface Request {
Expand All @@ -13,7 +14,13 @@ export interface Request {
export interface Response {
proposer: Address;
requestId: string;
response: Uint8Array;

// To be byte-encode when sending it to Prophet
response: {
chainId: Caip2ChainId; // Pending on-chain definition on CAIP-2 usage
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
chainId: Caip2ChainId; // Pending on-chain definition on CAIP-2 usage
chainId: Caip2ChainId; //FIXME/TODO: Pending on-chain definition on CAIP-2 usage

block: bigint;
epoch: bigint;
};
}

export interface Dispute {
Expand Down
Loading