From 3fdb86fd82be45f43e08a23c15d2ae6cfb8b7ad5 Mon Sep 17 00:00:00 2001 From: Yaco 0x Date: Mon, 16 Sep 2024 08:23:07 -0300 Subject: [PATCH 1/6] refactor: define Epoch type --- .../src/interfaces/protocolProvider.ts | 10 ++------- .../src/providers/protocolProvider.ts | 22 +++++++------------ .../src/services/eboProcessor.ts | 19 ++++++++-------- .../automated-dispute/src/types/epochs.ts | 14 ++++++++++++ packages/automated-dispute/src/types/index.ts | 1 + .../tests/services/eboProcessor.spec.ts | 4 ++++ 6 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 packages/automated-dispute/src/types/epochs.ts diff --git a/packages/automated-dispute/src/interfaces/protocolProvider.ts b/packages/automated-dispute/src/interfaces/protocolProvider.ts index ce236c7..37cb770 100644 --- a/packages/automated-dispute/src/interfaces/protocolProvider.ts +++ b/packages/automated-dispute/src/interfaces/protocolProvider.ts @@ -1,9 +1,7 @@ import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js"; -import { Timestamp } from "@ebo-agent/shared"; import { Address } from "viem"; -import type { EboEvent, EboEventName } from "../types/events.js"; -import type { Dispute, Request, Response } from "../types/prophet.js"; +import type { Dispute, EboEvent, EboEventName, Epoch, Request, Response } from "../types/index.js"; import { ProtocolContractsNames } from "../constants.js"; export type ProtocolContract = (typeof ProtocolContractsNames)[number]; @@ -18,11 +16,7 @@ export interface IReadProvider { * * @returns A promise that resolves with the current epoch, block number, and timestamp. */ - getCurrentEpoch(): Promise<{ - currentEpoch: bigint; - currentEpochBlockNumber: bigint; - currentEpochTimestamp: Timestamp; - }>; + getCurrentEpoch(): Promise; /** * Gets the last finalized block number. diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index b91d5f6..481420c 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -1,5 +1,4 @@ import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js"; -import { Timestamp } from "@ebo-agent/shared"; import { Address, BaseError, @@ -19,8 +18,7 @@ import { import { privateKeyToAccount } from "viem/accounts"; import { arbitrum } from "viem/chains"; -import type { EboEvent, EboEventName } from "../types/events.js"; -import type { Dispute, Request, Response } from "../types/prophet.js"; +import type { Dispute, EboEvent, EboEventName, Epoch, Request, Response } from "../types/index.js"; import { eboRequestCreatorAbi, epochManagerAbi, oracleAbi } from "../abis/index.js"; import { RpcUrlsEmpty } from "../exceptions/rpcUrlsEmpty.exception.js"; import { @@ -138,24 +136,20 @@ export class ProtocolProvider implements IProtocolProvider { * * @returns The current epoch, its block number and its timestamp */ - async getCurrentEpoch(): Promise<{ - currentEpoch: bigint; - currentEpochBlockNumber: bigint; - currentEpochTimestamp: Timestamp; - }> { - const [currentEpoch, currentEpochBlockNumber] = await Promise.all([ + async getCurrentEpoch(): Promise { + const [epoch, epochFirstBlockNumber] = await Promise.all([ this.epochManagerContract.read.currentEpoch(), this.epochManagerContract.read.currentEpochBlock(), ]); - const currentEpochBlock = await this.readClient.getBlock({ - blockNumber: currentEpochBlockNumber, + const epochFirstBlock = await this.readClient.getBlock({ + blockNumber: epochFirstBlockNumber, }); return { - currentEpoch, - currentEpochBlockNumber, - currentEpochTimestamp: currentEpochBlock.timestamp, + epoch, + epochFirstBlockNumber, + epochStartTimestamp: epochFirstBlock.timestamp, }; } diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index ce18690..eeda76d 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -5,8 +5,7 @@ import { Address, ILogger } from "@ebo-agent/shared"; import { ProcessorAlreadyStarted } from "../exceptions/index.js"; import { ProtocolProvider } from "../providers/protocolProvider.js"; import { alreadyDeletedActorWarning, droppingUnhandledEventsWarning } from "../templates/index.js"; -import { EboEvent, EboEventName } from "../types/events.js"; -import { RequestId } from "../types/prophet.js"; +import { EboEvent, EboEventName, Epoch, RequestId } from "../types/index.js"; import { EboActorsManager } from "./eboActorsManager.js"; const DEFAULT_MS_BETWEEN_CHECKS = 10 * 60 * 1000; // 10 minutes @@ -54,8 +53,10 @@ export class EboProcessor { // This process should somehow check if there's already a request created for the epoch // and chain that has no agent assigned and create it if that's the case. try { + const currentEpoch = await this.getCurrentEpoch(); + if (!this.lastCheckedBlock) { - this.lastCheckedBlock = await this.getEpochStartBlock(); + this.lastCheckedBlock = currentEpoch.epochFirstBlockNumber; } const lastBlock = await this.getLastFinalizedBlock(); @@ -97,18 +98,18 @@ export class EboProcessor { } /** - * Fetches the first block of the current epoch. + * Fetches the current epoch for the protocol chain. * - * @returns the first block of the current epoch + * @returns the current epoch properties of the protocol chain. */ - private async getEpochStartBlock(): Promise { + private async getCurrentEpoch(): Promise { this.logger.info("Fetching current epoch start block..."); - const { currentEpochBlockNumber } = await this.protocolProvider.getCurrentEpoch(); + const currentEpoch = await this.protocolProvider.getCurrentEpoch(); - this.logger.info(`Current epoch start block ${currentEpochBlockNumber} fetched.`); + this.logger.info(`Current epoch fetched.`); - return currentEpochBlockNumber; + return currentEpoch; } /** diff --git a/packages/automated-dispute/src/types/epochs.ts b/packages/automated-dispute/src/types/epochs.ts new file mode 100644 index 0000000..265c114 --- /dev/null +++ b/packages/automated-dispute/src/types/epochs.ts @@ -0,0 +1,14 @@ +import { Timestamp } from "@ebo-agent/shared"; + +/** + * Type representing an epoch's data. + * + * @property {bigint} epoch - epoch number + * @property {bigint} epochFirstBlockNumber - number of the first block of the epoch + * @property {Timestamp} epochStartTimestamp - timestamp of the first block of the epoch + */ +export type Epoch = { + epoch: bigint; + epochFirstBlockNumber: bigint; + epochStartTimestamp: Timestamp; +}; diff --git a/packages/automated-dispute/src/types/index.ts b/packages/automated-dispute/src/types/index.ts index 518c0ca..463b5a8 100644 --- a/packages/automated-dispute/src/types/index.ts +++ b/packages/automated-dispute/src/types/index.ts @@ -1,3 +1,4 @@ +export * from "./epochs.js"; export * from "./events.js"; export * from "../interfaces/protocolProvider.js"; export * from "./prophet.js"; diff --git a/packages/automated-dispute/tests/services/eboProcessor.spec.ts b/packages/automated-dispute/tests/services/eboProcessor.spec.ts index 97a9f35..c436901 100644 --- a/packages/automated-dispute/tests/services/eboProcessor.spec.ts +++ b/packages/automated-dispute/tests/services/eboProcessor.spec.ts @@ -379,6 +379,10 @@ describe("EboProcessor", () => { expect(mockActor2Enqueue).toHaveBeenCalledWith(eventStream[1]); }); + it.todo("creates a request when no actor is handling a chain's current epoch"); + + it.todo("does not create a new request if a corresponding actor already exist"); + it.skip("notifies if an actor throws while handling events"); it("removes the actor from registry when terminating", async () => { From fb2e84310374235f2e58646d8a98ea305f228c39 Mon Sep 17 00:00:00 2001 From: Yaco 0x Date: Mon, 16 Sep 2024 10:28:01 -0300 Subject: [PATCH 2/6] feat: spawn new actors during new epochs --- .../src/providers/protocolProvider.ts | 2 +- .../src/services/eboActor.ts | 31 +++-- .../src/services/eboActorsManager.ts | 15 +-- .../src/services/eboProcessor.ts | 71 +++++++++++ .../tests/services/eboProcessor.spec.ts | 112 ++++++++++++++++-- .../tests/services/protocolProvider.spec.ts | 6 +- 6 files changed, 197 insertions(+), 40 deletions(-) diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 481420c..52a18eb 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -236,7 +236,7 @@ export class ProtocolProvider implements IProtocolProvider { } // TODO: use Caip2 Chain ID instead of string in return type - async getAvailableChains(): Promise { + async getAvailableChains(): Promise { // TODO: implement actual method return ["eip155:1", "eip155:42161"]; } diff --git a/packages/automated-dispute/src/services/eboActor.ts b/packages/automated-dispute/src/services/eboActor.ts index 8c3278a..cdd5667 100644 --- a/packages/automated-dispute/src/services/eboActor.ts +++ b/packages/automated-dispute/src/services/eboActor.ts @@ -5,6 +5,16 @@ import { Mutex } from "async-mutex"; import { Heap } from "heap-js"; import { ContractFunctionRevertedError } from "viem"; +import type { + Dispute, + DisputeStatus, + EboEvent, + EboEventName, + Request, + RequestId, + Response, + ResponseBody, +} from "../types/index.js"; import { DisputeWithoutResponse, EBORequestCreator_ChainNotAdded, @@ -27,16 +37,6 @@ import { FinalizeRequest, UpdateDisputeStatus, } from "../services/index.js"; -import { - Dispute, - DisputeStatus, - EboEvent, - EboEventName, - Request, - RequestId, - Response, - ResponseBody, -} from "../types/index.js"; /** * Compare function to sort events chronologically in ascending order by block number @@ -53,6 +53,8 @@ const EBO_EVENT_COMPARATOR = (e1: EboEvent, e2: EboEvent { // FIXME(non-current epochs): adapt this code to fetch timestamps corresponding // to the first block of any epoch, not just the current epoch - const { currentEpochTimestamp } = await this.protocolProvider.getCurrentEpoch(); + const { epochStartTimestamp } = await this.protocolProvider.getCurrentEpoch(); const epochBlockNumber = await this.blockNumberService.getEpochBlockNumber( - currentEpochTimestamp, + epochStartTimestamp, chainId, ); diff --git a/packages/automated-dispute/src/services/eboActorsManager.ts b/packages/automated-dispute/src/services/eboActorsManager.ts index 4643343..d6a5e0c 100644 --- a/packages/automated-dispute/src/services/eboActorsManager.ts +++ b/packages/automated-dispute/src/services/eboActorsManager.ts @@ -4,8 +4,8 @@ import { Mutex } from "async-mutex"; import { RequestAlreadyHandled } from "../exceptions/index.js"; import { ProtocolProvider } from "../providers/protocolProvider.js"; -import { RequestId } from "../types/prophet.js"; -import { EboActor } from "./eboActor.js"; +import { RequestId } from "../types/index.js"; +import { ActorRequest, EboActor } from "./eboActor.js"; import { EboMemoryRegistry } from "./eboRegistry/eboMemoryRegistry.js"; export class EboActorsManager { @@ -21,7 +21,11 @@ export class EboActorsManager { * @returns array of normalized request IDs */ public getRequestIds(): RequestId[] { - return [...this.requestActorMap.entries()].map((entry) => Address.normalize(entry[0])); + return [...this.requestActorMap.keys()].map((requestId) => Address.normalize(requestId)); + } + + public getActorsRequests(): ActorRequest[] { + return [...this.requestActorMap.values()].map((actor) => actor.actorRequest); } /** @@ -30,10 +34,7 @@ export class EboActorsManager { * @param actor an `EboActor` instance that handles a request. */ public createActor( - actorRequest: { - id: RequestId; - epoch: bigint; - }, + actorRequest: ActorRequest, protocolProvider: ProtocolProvider, blockNumberService: BlockNumberService, logger: ILogger, diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index eeda76d..4c7bdc9 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -1,11 +1,13 @@ import { isNativeError } from "util/types"; import { BlockNumberService } from "@ebo-agent/blocknumber"; +import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js"; import { Address, ILogger } from "@ebo-agent/shared"; import { ProcessorAlreadyStarted } from "../exceptions/index.js"; import { ProtocolProvider } from "../providers/protocolProvider.js"; import { alreadyDeletedActorWarning, droppingUnhandledEventsWarning } from "../templates/index.js"; import { EboEvent, EboEventName, Epoch, RequestId } from "../types/index.js"; +import { ActorRequest } from "./eboActor.js"; import { EboActorsManager } from "./eboActorsManager.js"; const DEFAULT_MS_BETWEEN_CHECKS = 10 * 60 * 1000; // 10 minutes @@ -85,6 +87,8 @@ export class EboProcessor { this.logger.info(`Consumed events up to block ${lastBlock}.`); + this.createMissingRequests(currentEpoch.epoch); + this.lastCheckedBlock = lastBlock; } catch (err) { if (isNativeError(err)) { @@ -236,6 +240,7 @@ export class EboProcessor { const actorRequest = { id: Address.normalize(event.requestId), epoch: event.metadata.epoch, + chainId: event.metadata.chainId, }; const actor = this.actorsManager.createActor( @@ -260,6 +265,72 @@ export class EboProcessor { this.terminateActor(requestId); } + /** + * Creates missing requests for the specified epoch, based on the + * available chains and the currently being handled requests. + * + * @param epoch the epoch number + */ + private async createMissingRequests(epoch: Epoch["epoch"]): Promise { + try { + const handledEpochChains = this.actorsManager + .getActorsRequests() + .reduce((actorRequestMap, actorRequest: ActorRequest) => { + const epochRequests = actorRequestMap.get(actorRequest.epoch) ?? new Set(); + + epochRequests.add(actorRequest.chainId); + + return actorRequestMap.set(actorRequest.epoch, epochRequests); + }, new Map>()); + + this.logger.info("Fetching available chains..."); + + const availableChains: Caip2ChainId[] = + await this.protocolProvider.getAvailableChains(); + + this.logger.info("Available chains fetched."); + + const unhandledEpochChain = availableChains.filter((chain) => { + const epochRequests = handledEpochChains.get(epoch); + const isHandled = epochRequests && epochRequests.has(chain); + + return !isHandled; + }); + + this.logger.info("Creating missing requests..."); + + const epochChainRequests = unhandledEpochChain.map(async (chain) => { + try { + this.logger.info(`Creating request for chain ${chain} and epoch ${epoch}`); + + await this.protocolProvider.createRequest(epoch, [chain]); + + this.logger.info(`Request created for chain ${chain} and epoch ${epoch}`); + } catch (err) { + // Request creation must be notified but it's not critical, as it will be + // retried during next sync. + + // TODO: warn when getting a EBORequestCreator_RequestAlreadyCreated + // TODO: notify under any other error + + this.logger.error( + `Could not create a request for epoch ${epoch} and chain ${chain}.`, + ); + } + }); + + await Promise.all(epochChainRequests); + + this.logger.info("Missing requests created."); + } catch (err) { + // TODO: notify + + this.logger.error( + `Requests creation missing: ${isNativeError(err) ? err.message : err}`, + ); + } + } + /** * Removes the actor from tracking the request. * diff --git a/packages/automated-dispute/tests/services/eboProcessor.spec.ts b/packages/automated-dispute/tests/services/eboProcessor.spec.ts index c436901..5795942 100644 --- a/packages/automated-dispute/tests/services/eboProcessor.spec.ts +++ b/packages/automated-dispute/tests/services/eboProcessor.spec.ts @@ -94,12 +94,12 @@ describe("EboProcessor", () => { const { actor } = mocks.buildEboActor(request, logger); const currentEpoch = { - currentEpoch: 1n, - currentEpochBlockNumber: 1n, - currentEpochTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + epoch: 1n, + epochFirstBlockNumber: 1n, + epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; - const currentBlock = currentEpoch.currentEpochBlockNumber + 10n; + const currentBlock = currentEpoch.epochFirstBlockNumber + 10n; const requestCreatedEvent: EboEvent<"RequestCreated"> = { name: "RequestCreated", @@ -127,7 +127,7 @@ describe("EboProcessor", () => { await processor.start(msBetweenChecks); expect(mockGetEvents).toHaveBeenCalledWith( - currentEpoch.currentEpochBlockNumber, + currentEpoch.epochFirstBlockNumber, currentBlock, ); }); @@ -139,9 +139,9 @@ describe("EboProcessor", () => { const { actor } = mocks.buildEboActor(request, logger); const currentEpoch = { - currentEpoch: 1n, - currentEpochBlockNumber: 1n, - currentEpochTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + epoch: 1n, + epochFirstBlockNumber: 1n, + epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; const mockProtocolProviderGetEvents = vi @@ -166,7 +166,7 @@ describe("EboProcessor", () => { expect(mockProtocolProviderGetEvents).toHaveBeenNthCalledWith( 1, - currentEpoch.currentEpochBlockNumber, + currentEpoch.epochFirstBlockNumber, initialCurrentBlock + 10n, ); @@ -177,7 +177,7 @@ describe("EboProcessor", () => { expect(mockProtocolProviderGetEvents).toHaveBeenNthCalledWith( 2, - currentEpoch.currentEpochBlockNumber, + currentEpoch.epochFirstBlockNumber, initialCurrentBlock + 20n, ); }); @@ -379,11 +379,97 @@ describe("EboProcessor", () => { expect(mockActor2Enqueue).toHaveBeenCalledWith(eventStream[1]); }); - it.todo("creates a request when no actor is handling a chain's current epoch"); + it.skip("notifies if an actor throws while handling events"); - it.todo("does not create a new request if a corresponding actor already exist"); + it("creates a request when no actor is handling a chain's current epoch", async () => { + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); - it.skip("notifies if an actor throws while handling events"); + const currentEpoch = { + epoch: 1n, + epochFirstBlockNumber: 1n, + epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + }; + + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(1n); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); + + vi.spyOn(actorsManager, "getActorsRequests").mockReturnValue([ + { id: "0x01", chainId: "eip155:1", epoch: currentEpoch.epoch }, + ]); + + const mockProtocolProviderCreateRequest = vi + .spyOn(protocolProvider, "createRequest") + .mockImplementation(() => Promise.resolve()); + + await processor.start(); + + expect(mockProtocolProviderCreateRequest).toHaveBeenCalledWith(currentEpoch.epoch, [ + "eip155:42161", + ]); + }); + + it("does not create a new request if a corresponding actor already exist", async () => { + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + + const currentEpoch = { + epoch: 1n, + epochFirstBlockNumber: 1n, + epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + }; + + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(1n); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); + + vi.spyOn(actorsManager, "getActorsRequests").mockReturnValue([ + { id: "0x01", chainId: "eip155:1", epoch: currentEpoch.epoch }, + { id: "0x02", chainId: "eip155:42161", epoch: currentEpoch.epoch }, + ]); + + const mockProtocolProviderCreateRequest = vi + .spyOn(protocolProvider, "createRequest") + .mockImplementation(() => Promise.resolve()); + + await processor.start(); + + expect(mockProtocolProviderCreateRequest).not.toHaveBeenCalled(); + }); + + it("handles errors during request creation", async () => { + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + + const currentEpoch = { + epoch: 1n, + epochFirstBlockNumber: 1n, + epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + }; + + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(1n); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); + vi.spyOn(protocolProvider, "createRequest").mockImplementation(() => Promise.reject()); + + vi.spyOn(actorsManager, "getActorsRequests").mockReturnValue([ + { id: "0x01", chainId: "eip155:1", epoch: currentEpoch.epoch }, + ]); + + expect(processor.start()).resolves.not.toThrow(); + }); + + it.skip("notifies if a request failed to be created"); it("removes the actor from registry when terminating", async () => { const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index 9340621..61327b0 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -165,9 +165,9 @@ describe("ProtocolProvider", () => { const result = await protocolProvider.getCurrentEpoch(); - expect(result.currentEpoch).toBe(mockEpoch); - expect(result.currentEpochBlockNumber).toBe(mockEpochBlock); - expect(result.currentEpochTimestamp).toBe(mockEpochTimestamp); + expect(result.epoch).toBe(mockEpoch); + expect(result.epochFirstBlockNumber).toBe(mockEpochBlock); + expect(result.epochStartTimestamp).toBe(mockEpochTimestamp); }); it("throws when current epoch request fails", async () => { const protocolProvider = new ProtocolProvider( From e77f76480acc791787d1d3b7baaf1aae0cc3cc8d Mon Sep 17 00:00:00 2001 From: Yaco 0x Date: Mon, 16 Sep 2024 10:55:26 -0300 Subject: [PATCH 3/6] fix: improve actor termination checks --- .../src/services/eboActor.ts | 9 ++- .../src/services/eboProcessor.ts | 12 ++- .../tests/services/eboActor.spec.ts | 73 ++++++++++++++++++- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/packages/automated-dispute/src/services/eboActor.ts b/packages/automated-dispute/src/services/eboActor.ts index cdd5667..948ae39 100644 --- a/packages/automated-dispute/src/services/eboActor.ts +++ b/packages/automated-dispute/src/services/eboActor.ts @@ -10,6 +10,7 @@ import type { DisputeStatus, EboEvent, EboEventName, + Epoch, Request, RequestId, Response, @@ -431,15 +432,19 @@ export class EboActor { * * Be aware that a request can be finalized but some of its disputes can still be pending resolution. * + * At last, actors must be kept alive until their epoch concludes, to ensure no actor/request duplication. + * + * @param currentEpoch the epoch to check against actor termination * @param blockNumber block number to check entities at * @returns `true` if all entities are settled, otherwise `false` */ - public canBeTerminated(blockNumber: bigint): boolean { + public canBeTerminated(currentEpoch: Epoch["epoch"], blockNumber: bigint): boolean { const request = this.getActorRequest(); + const isPastEpoch = currentEpoch > request.epoch; const isRequestFinalized = request.status === "Finalized"; const nonSettledProposals = this.activeProposals(blockNumber); - return isRequestFinalized && nonSettledProposals.length === 0; + return isPastEpoch && isRequestFinalized && nonSettledProposals.length === 0; } /** diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index 4c7bdc9..0ad22a1 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -77,7 +77,7 @@ export class EboProcessor { try { const events = eventsByRequestId.get(requestId) ?? []; - await this.syncRequest(requestId, events, lastBlock); + await this.syncRequest(requestId, events, currentEpoch.epoch, lastBlock); } catch (err) { this.onActorError(requestId, err as Error); } @@ -187,9 +187,15 @@ export class EboProcessor { * * @param requestId the ID of the `Request` * @param events a stream of consumed events + * @param currentEpoch the current epoch based on the last block * @param lastBlock the last block checked */ - private async syncRequest(requestId: RequestId, events: EboEventStream, lastBlock: bigint) { + private async syncRequest( + requestId: RequestId, + events: EboEventStream, + currentEpoch: Epoch["epoch"], + lastBlock: bigint, + ) { const firstEvent = events[0]; const actor = this.getOrCreateActor(requestId, firstEvent); @@ -204,7 +210,7 @@ export class EboProcessor { await actor.processEvents(); await actor.onLastBlockUpdated(lastBlock); - if (actor.canBeTerminated(lastBlock)) { + if (actor.canBeTerminated(currentEpoch, lastBlock)) { this.terminateActor(requestId); } } diff --git a/packages/automated-dispute/tests/services/eboActor.spec.ts b/packages/automated-dispute/tests/services/eboActor.spec.ts index ec4af4e..6329837 100644 --- a/packages/automated-dispute/tests/services/eboActor.spec.ts +++ b/packages/automated-dispute/tests/services/eboActor.spec.ts @@ -253,7 +253,9 @@ describe("EboActor", () => { vi.spyOn(registry, "getRequest").mockReturnValue(request); vi.spyOn(registry, "getResponses").mockReturnValue([]); - expect(actor.canBeTerminated(currentBlockNumber)).toBe(false); + expect(actor.canBeTerminated(actor.actorRequest.epoch + 1n, currentBlockNumber)).toBe( + false, + ); }); it("returns false if there's one disputable response", () => { @@ -266,7 +268,9 @@ describe("EboActor", () => { vi.spyOn(registry, "getResponses").mockReturnValue([response]); vi.spyOn(registry, "getResponseDispute").mockReturnValue(undefined); - expect(actor.canBeTerminated(currentBlockNumber)).toBe(false); + expect(actor.canBeTerminated(actor.actorRequest.epoch + 1n, currentBlockNumber)).toBe( + false, + ); }); it("returns false if the request is finalized but there's one active dispute", () => { @@ -286,11 +290,69 @@ describe("EboActor", () => { vi.spyOn(registry, "getResponses").mockReturnValue([response]); vi.spyOn(registry, "getResponseDispute").mockReturnValue(dispute); - const canBeTerminated = actor.canBeTerminated(currentBlockNumber); + const canBeTerminated = actor.canBeTerminated( + actor.actorRequest.epoch + 1n, + currentBlockNumber, + ); expect(canBeTerminated).toBe(false); }); + it("returns false if we are still in the same epoch", () => { + const request: Request = { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, + status: "Finalized", + }; + + const disputedResponse = mocks.buildResponse(request, { id: "0x01" }); + const undisputedResponse = mocks.buildResponse(request, { + id: "0x02", + createdAt: request.prophetData.responseModuleData.deadline - 1n, + }); + + const escalatedDispute = mocks.buildDispute(request, disputedResponse, { + status: "Escalated", + }); + + const { actor, registry } = mocks.buildEboActor(request, logger); + const currentBlockNumber = + undisputedResponse.createdAt + + request.prophetData.disputeModuleData.disputeWindow + + 1n; + + vi.spyOn(registry, "getRequest").mockReturnValue(request); + + vi.spyOn(registry, "getResponses").mockReturnValue([ + disputedResponse, + undisputedResponse, + ]); + + vi.spyOn(registry, "getResponseDispute").mockImplementation((response) => { + switch (response.id) { + case disputedResponse.id: + return escalatedDispute; + + case undisputedResponse.id: + return undefined; + } + }); + + const canBeTerminatedDuringCurrentEpoch = actor.canBeTerminated( + actor.actorRequest.epoch, + currentBlockNumber, + ); + + const canBeTerminatedDuringNextEpoch = actor.canBeTerminated( + actor.actorRequest.epoch + 1n, + currentBlockNumber, + ); + + expect(canBeTerminatedDuringCurrentEpoch).toBe(false); + // This is to validate that the change in the current epoch is the one that + // changes the output + expect(canBeTerminatedDuringNextEpoch).toBe(true); + }); + it("returns true once everything is settled", () => { const request: Request = { ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, @@ -330,7 +392,10 @@ describe("EboActor", () => { } }); - const canBeTerminated = actor.canBeTerminated(currentBlockNumber); + const canBeTerminated = actor.canBeTerminated( + actor.actorRequest.epoch + 1n, + currentBlockNumber, + ); expect(canBeTerminated).toBe(true); }); From 52b6a9a03c5bd2e33a1815af03247865365b087e Mon Sep 17 00:00:00 2001 From: Yaco 0x Date: Mon, 16 Sep 2024 11:04:09 -0300 Subject: [PATCH 4/6] fix: logs messages --- packages/automated-dispute/src/services/eboProcessor.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index 0ad22a1..ab7682f 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -107,7 +107,7 @@ export class EboProcessor { * @returns the current epoch properties of the protocol chain. */ private async getCurrentEpoch(): Promise { - this.logger.info("Fetching current epoch start block..."); + this.logger.info("Fetching current epoch..."); const currentEpoch = await this.protocolProvider.getCurrentEpoch(); @@ -307,7 +307,7 @@ export class EboProcessor { const epochChainRequests = unhandledEpochChain.map(async (chain) => { try { - this.logger.info(`Creating request for chain ${chain} and epoch ${epoch}`); + this.logger.info(`Creating request for chain ${chain} and epoch ${epoch}...`); await this.protocolProvider.createRequest(epoch, [chain]); From a3fb435dd4278a31859044d172c6f6814eb47267 Mon Sep 17 00:00:00 2001 From: Yaco 0x Date: Mon, 16 Sep 2024 11:07:11 -0300 Subject: [PATCH 5/6] chore: remove feature's TODO comment --- packages/automated-dispute/src/services/eboProcessor.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index ab7682f..5f75577 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -50,10 +50,6 @@ export class EboProcessor { /** Sync new blocks and their events with their corresponding actors. */ private async sync() { - // TODO: detect new epoch by comparing subgraph's data with EpochManager's current epoch - // and trigger a request creation if there's no actor handling an request. - // This process should somehow check if there's already a request created for the epoch - // and chain that has no agent assigned and create it if that's the case. try { const currentEpoch = await this.getCurrentEpoch(); From 2aade37622dcefcef84030ef45f99043ef607e40 Mon Sep 17 00:00:00 2001 From: Yaco 0x Date: Mon, 16 Sep 2024 13:47:51 -0300 Subject: [PATCH 6/6] refactor: rename epoch properties --- .../src/providers/protocolProvider.ts | 6 +- .../src/services/eboActor.ts | 9 ++- .../src/services/eboActorsManager.ts | 4 +- .../src/services/eboProcessor.ts | 15 ++-- .../src/types/actorRequest.ts | 5 ++ .../automated-dispute/src/types/epochs.ts | 6 +- packages/automated-dispute/src/types/index.ts | 1 + .../tests/services/eboProcessor.spec.ts | 71 +++++++++---------- .../tests/services/protocolProvider.spec.ts | 6 +- 9 files changed, 62 insertions(+), 61 deletions(-) create mode 100644 packages/automated-dispute/src/types/actorRequest.ts diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 52a18eb..24fdf37 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -147,9 +147,9 @@ export class ProtocolProvider implements IProtocolProvider { }); return { - epoch, - epochFirstBlockNumber, - epochStartTimestamp: epochFirstBlock.timestamp, + number: epoch, + firstBlockNumber: epochFirstBlockNumber, + startTimestamp: epochFirstBlock.timestamp, }; } diff --git a/packages/automated-dispute/src/services/eboActor.ts b/packages/automated-dispute/src/services/eboActor.ts index 948ae39..f7da738 100644 --- a/packages/automated-dispute/src/services/eboActor.ts +++ b/packages/automated-dispute/src/services/eboActor.ts @@ -12,7 +12,6 @@ import type { EboEventName, Epoch, Request, - RequestId, Response, ResponseBody, } from "../types/index.js"; @@ -38,6 +37,7 @@ import { FinalizeRequest, UpdateDisputeStatus, } from "../services/index.js"; +import { ActorRequest } from "../types/actorRequest.js"; /** * Compare function to sort events chronologically in ascending order by block number @@ -54,8 +54,6 @@ const EBO_EVENT_COMPARATOR = (e1: EboEvent, e2: EboEvent request.epoch; const isRequestFinalized = request.status === "Finalized"; @@ -560,7 +558,8 @@ export class EboActor { private async buildResponse(chainId: Caip2ChainId): Promise { // FIXME(non-current epochs): adapt this code to fetch timestamps corresponding // to the first block of any epoch, not just the current epoch - const { epochStartTimestamp } = await this.protocolProvider.getCurrentEpoch(); + const { startTimestamp: epochStartTimestamp } = + await this.protocolProvider.getCurrentEpoch(); const epochBlockNumber = await this.blockNumberService.getEpochBlockNumber( epochStartTimestamp, diff --git a/packages/automated-dispute/src/services/eboActorsManager.ts b/packages/automated-dispute/src/services/eboActorsManager.ts index d6a5e0c..4b09f97 100644 --- a/packages/automated-dispute/src/services/eboActorsManager.ts +++ b/packages/automated-dispute/src/services/eboActorsManager.ts @@ -4,8 +4,8 @@ import { Mutex } from "async-mutex"; import { RequestAlreadyHandled } from "../exceptions/index.js"; import { ProtocolProvider } from "../providers/protocolProvider.js"; -import { RequestId } from "../types/index.js"; -import { ActorRequest, EboActor } from "./eboActor.js"; +import { ActorRequest, RequestId } from "../types/index.js"; +import { EboActor } from "./eboActor.js"; import { EboMemoryRegistry } from "./eboRegistry/eboMemoryRegistry.js"; export class EboActorsManager { diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index 5f75577..7950502 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -6,8 +6,7 @@ import { Address, ILogger } from "@ebo-agent/shared"; import { ProcessorAlreadyStarted } from "../exceptions/index.js"; import { ProtocolProvider } from "../providers/protocolProvider.js"; import { alreadyDeletedActorWarning, droppingUnhandledEventsWarning } from "../templates/index.js"; -import { EboEvent, EboEventName, Epoch, RequestId } from "../types/index.js"; -import { ActorRequest } from "./eboActor.js"; +import { ActorRequest, EboEvent, EboEventName, Epoch, RequestId } from "../types/index.js"; import { EboActorsManager } from "./eboActorsManager.js"; const DEFAULT_MS_BETWEEN_CHECKS = 10 * 60 * 1000; // 10 minutes @@ -54,7 +53,7 @@ export class EboProcessor { const currentEpoch = await this.getCurrentEpoch(); if (!this.lastCheckedBlock) { - this.lastCheckedBlock = currentEpoch.epochFirstBlockNumber; + this.lastCheckedBlock = currentEpoch.firstBlockNumber; } const lastBlock = await this.getLastFinalizedBlock(); @@ -73,7 +72,7 @@ export class EboProcessor { try { const events = eventsByRequestId.get(requestId) ?? []; - await this.syncRequest(requestId, events, currentEpoch.epoch, lastBlock); + await this.syncRequest(requestId, events, currentEpoch.number, lastBlock); } catch (err) { this.onActorError(requestId, err as Error); } @@ -83,7 +82,7 @@ export class EboProcessor { this.logger.info(`Consumed events up to block ${lastBlock}.`); - this.createMissingRequests(currentEpoch.epoch); + this.createMissingRequests(currentEpoch.number); this.lastCheckedBlock = lastBlock; } catch (err) { @@ -189,7 +188,7 @@ export class EboProcessor { private async syncRequest( requestId: RequestId, events: EboEventStream, - currentEpoch: Epoch["epoch"], + currentEpoch: Epoch["number"], lastBlock: bigint, ) { const firstEvent = events[0]; @@ -273,7 +272,7 @@ export class EboProcessor { * * @param epoch the epoch number */ - private async createMissingRequests(epoch: Epoch["epoch"]): Promise { + private async createMissingRequests(epoch: Epoch["number"]): Promise { try { const handledEpochChains = this.actorsManager .getActorsRequests() @@ -283,7 +282,7 @@ export class EboProcessor { epochRequests.add(actorRequest.chainId); return actorRequestMap.set(actorRequest.epoch, epochRequests); - }, new Map>()); + }, new Map>()); this.logger.info("Fetching available chains..."); diff --git a/packages/automated-dispute/src/types/actorRequest.ts b/packages/automated-dispute/src/types/actorRequest.ts new file mode 100644 index 0000000..3c83f11 --- /dev/null +++ b/packages/automated-dispute/src/types/actorRequest.ts @@ -0,0 +1,5 @@ +import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js"; + +import { RequestId } from "./prophet.js"; + +export type ActorRequest = { id: RequestId; epoch: bigint; chainId: Caip2ChainId }; diff --git a/packages/automated-dispute/src/types/epochs.ts b/packages/automated-dispute/src/types/epochs.ts index 265c114..e6fc028 100644 --- a/packages/automated-dispute/src/types/epochs.ts +++ b/packages/automated-dispute/src/types/epochs.ts @@ -8,7 +8,7 @@ import { Timestamp } from "@ebo-agent/shared"; * @property {Timestamp} epochStartTimestamp - timestamp of the first block of the epoch */ export type Epoch = { - epoch: bigint; - epochFirstBlockNumber: bigint; - epochStartTimestamp: Timestamp; + number: bigint; + firstBlockNumber: bigint; + startTimestamp: Timestamp; }; diff --git a/packages/automated-dispute/src/types/index.ts b/packages/automated-dispute/src/types/index.ts index 463b5a8..3139df0 100644 --- a/packages/automated-dispute/src/types/index.ts +++ b/packages/automated-dispute/src/types/index.ts @@ -1,3 +1,4 @@ +export * from "./actorRequest.js"; export * from "./epochs.js"; export * from "./events.js"; export * from "../interfaces/protocolProvider.js"; diff --git a/packages/automated-dispute/tests/services/eboProcessor.spec.ts b/packages/automated-dispute/tests/services/eboProcessor.spec.ts index 5795942..26559f8 100644 --- a/packages/automated-dispute/tests/services/eboProcessor.spec.ts +++ b/packages/automated-dispute/tests/services/eboProcessor.spec.ts @@ -94,12 +94,12 @@ describe("EboProcessor", () => { const { actor } = mocks.buildEboActor(request, logger); const currentEpoch = { - epoch: 1n, - epochFirstBlockNumber: 1n, - epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; - const currentBlock = currentEpoch.epochFirstBlockNumber + 10n; + const currentBlock = currentEpoch.firstBlockNumber + 10n; const requestCreatedEvent: EboEvent<"RequestCreated"> = { name: "RequestCreated", @@ -126,10 +126,7 @@ describe("EboProcessor", () => { await processor.start(msBetweenChecks); - expect(mockGetEvents).toHaveBeenCalledWith( - currentEpoch.epochFirstBlockNumber, - currentBlock, - ); + expect(mockGetEvents).toHaveBeenCalledWith(currentEpoch.firstBlockNumber, currentBlock); }); it("keeps the last block checked unaltered when something fails during sync", async () => { @@ -139,9 +136,9 @@ describe("EboProcessor", () => { const { actor } = mocks.buildEboActor(request, logger); const currentEpoch = { - epoch: 1n, - epochFirstBlockNumber: 1n, - epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; const mockProtocolProviderGetEvents = vi @@ -166,7 +163,7 @@ describe("EboProcessor", () => { expect(mockProtocolProviderGetEvents).toHaveBeenNthCalledWith( 1, - currentEpoch.epochFirstBlockNumber, + currentEpoch.firstBlockNumber, initialCurrentBlock + 10n, ); @@ -177,7 +174,7 @@ describe("EboProcessor", () => { expect(mockProtocolProviderGetEvents).toHaveBeenNthCalledWith( 2, - currentEpoch.epochFirstBlockNumber, + currentEpoch.firstBlockNumber, initialCurrentBlock + 20n, ); }); @@ -190,12 +187,12 @@ describe("EboProcessor", () => { processor["lastCheckedBlock"] = mockLastCheckedBlock; const currentEpoch = { - currentEpoch: 1n, - currentEpochBlockNumber: 1n, - currentEpochTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; - const currentBlock = currentEpoch.currentEpochBlockNumber + 10n; + const currentBlock = currentEpoch.firstBlockNumber + 10n; const requestCreatedEvent: EboEvent<"RequestCreated"> = { name: "RequestCreated", @@ -231,12 +228,12 @@ describe("EboProcessor", () => { const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); const currentEpoch = { - currentEpoch: 1n, - currentEpochBlockNumber: 1n, - currentEpochTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; - const currentBlock = currentEpoch.currentEpochBlockNumber + 10n; + const currentBlock = currentEpoch.firstBlockNumber + 10n; vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); @@ -294,12 +291,12 @@ describe("EboProcessor", () => { const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); const currentEpoch = { - currentEpoch: 1n, - currentEpochBlockNumber: 1n, - currentEpochTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; - const currentBlock = currentEpoch.currentEpochBlockNumber + 10n; + const currentBlock = currentEpoch.firstBlockNumber + 10n; vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); @@ -385,9 +382,9 @@ describe("EboProcessor", () => { const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); const currentEpoch = { - epoch: 1n, - epochFirstBlockNumber: 1n, - epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); @@ -399,7 +396,7 @@ describe("EboProcessor", () => { ]); vi.spyOn(actorsManager, "getActorsRequests").mockReturnValue([ - { id: "0x01", chainId: "eip155:1", epoch: currentEpoch.epoch }, + { id: "0x01", chainId: "eip155:1", epoch: currentEpoch.number }, ]); const mockProtocolProviderCreateRequest = vi @@ -408,7 +405,7 @@ describe("EboProcessor", () => { await processor.start(); - expect(mockProtocolProviderCreateRequest).toHaveBeenCalledWith(currentEpoch.epoch, [ + expect(mockProtocolProviderCreateRequest).toHaveBeenCalledWith(currentEpoch.number, [ "eip155:42161", ]); }); @@ -417,9 +414,9 @@ describe("EboProcessor", () => { const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); const currentEpoch = { - epoch: 1n, - epochFirstBlockNumber: 1n, - epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); @@ -431,8 +428,8 @@ describe("EboProcessor", () => { ]); vi.spyOn(actorsManager, "getActorsRequests").mockReturnValue([ - { id: "0x01", chainId: "eip155:1", epoch: currentEpoch.epoch }, - { id: "0x02", chainId: "eip155:42161", epoch: currentEpoch.epoch }, + { id: "0x01", chainId: "eip155:1", epoch: currentEpoch.number }, + { id: "0x02", chainId: "eip155:42161", epoch: currentEpoch.number }, ]); const mockProtocolProviderCreateRequest = vi @@ -449,8 +446,8 @@ describe("EboProcessor", () => { const currentEpoch = { epoch: 1n, - epochFirstBlockNumber: 1n, - epochStartTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index 61327b0..18b8abf 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -165,9 +165,9 @@ describe("ProtocolProvider", () => { const result = await protocolProvider.getCurrentEpoch(); - expect(result.epoch).toBe(mockEpoch); - expect(result.epochFirstBlockNumber).toBe(mockEpochBlock); - expect(result.epochStartTimestamp).toBe(mockEpochTimestamp); + expect(result.number).toBe(mockEpoch); + expect(result.firstBlockNumber).toBe(mockEpochBlock); + expect(result.startTimestamp).toBe(mockEpochTimestamp); }); it("throws when current epoch request fails", async () => { const protocolProvider = new ProtocolProvider(