Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into feat/grt-145-fetch-ple…
Browse files Browse the repository at this point in the history
…dges-before

# Conflicts:
#	packages/automated-dispute/tests/mocks/eboActor.mocks.ts
#	packages/automated-dispute/tests/services/eboActor/onDisputeStatusUpdated.spec.ts
  • Loading branch information
jahabeebs committed Nov 2, 2024
2 parents 7c779a5 + 0e65a1c commit 563dfd0
Show file tree
Hide file tree
Showing 33 changed files with 643 additions and 105 deletions.
32 changes: 32 additions & 0 deletions apps/agent/config.tenderly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
protocolProvider:
rpcsConfig:
l1:
chainId: eip155:11155111
transactionReceiptConfirmations: 1
timeout: 10000
retryInterval: 150
l2:
chainId: eip155:421614
transactionReceiptConfirmations: 1
timeout: 10000
retryInterval: 150
contracts:
oracle: "0x10224eff6B1Caaf5daC49B2e7104b7161484B128"
epochManager: "0x7975475801BEf845f10Ce7784DC69aB1e0344f11"
eboRequestCreator: "0xa13318684281a820304C164427396385C306d870"
bondEscalationModule: "0x52d7728fE87826FfF51b21b303e2FF7cB04F6Aec"
horizonAccountingExtension: "0xbDAB27D1903da4e18B0D1BE873E18924514E52eC"

blockNumberService:
blockmetaConfig:
baseUrl: "localhost:443"
servicePaths:
blockByTime: /sf.blockmeta.v2.BlockByTime
block: /sf.blockmeta.v2.Block
bearerTokenExpirationWindow: 31536000000

processor:
msBetweenChecks: 7500
accountingModules:
responseModule: "0xb97C59331F89a852Ae21aee215Da28820c533649"
escalationModule: "0x52d7728fE87826FfF51b21b303e2FF7cB04F6Aec"
19 changes: 9 additions & 10 deletions packages/automated-dispute/src/exceptions/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
export * from "./blockNumberServiceRequired.exception.js";
export * from "./customContractError.js";
export * from "./decodeLogDataFailure.js";
export * from "./eboActor/index.js";
export * from "./eboProcessor/index.js";
export * from "./eboRegistry/index.js";

export * from "./errorFactory.js";
export * from "./invalidAccountOnClient.exception.js";
export * from "./invalidActorState.exception.js";
export * from "./invalidBlockHash.exception.js";
export * from "./invalidBlockRangeError.exception.js";
export * from "./invalidDisputeStatus.exception.js";
export * from "./prophetDecodingError.exception.js";
export * from "./requestAlreadyHandled.exception.js";
export * from "./requestMismatch.exception.js";
export * from "./responseAlreadyProposed.exception.js";
export * from "./rpcUrlsEmpty.exception.js";
export * from "./transactionExecutionError.exception.js";
export * from "./invalidAccountOnClient.exception.js";
export * from "./unsupportedEvent.exception.js";
export * from "./decodeLogDataFailure.js";
export * from "./invalidBlockRangeError.exception.js";
export * from "./unknownCustomError.exception.js";
export * from "./invalidBlockHash.exception.js";
export * from "./unknownDisputeStatus.exception.js";
export * from "./blockNumberServiceRequired.exception.js";
export * from "./customContractError.js";
export * from "./errorFactory.js";
export * from "./unsupportedEvent.exception.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ByteArray, Hex } from "viem";

export class ProphetDecodingError extends Error {
constructor(
public readonly id: string,
public readonly data: ByteArray | Hex,
public readonly err?: Error,
) {
super(`Failed to decode ${id} with data ${data}.`);

this.name = "ProphetDecodingError";
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export interface EboRegistryCommand {
/**
* Return the command name
*/
name(): string;

/**
* Run a command to update the registry
*/
Expand Down
4 changes: 1 addition & 3 deletions packages/automated-dispute/src/providers/protocolProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import {
RpcUrlsEmpty,
TransactionExecutionError,
} from "../exceptions/index.js";
import { ProphetCodec } from "../external.js";
import {
IProtocolProvider,
IReadProvider,
Expand Down Expand Up @@ -470,7 +469,7 @@ export class ProtocolProvider implements IProtocolProvider {
responseId: HexUtils.normalize(_dispute.responseId) as ResponseId,
requestId: HexUtils.normalize(_dispute.requestId) as RequestId,
},
status: ProphetCodec.decodeDisputeStatus(_status),
status: _status,
blockNumber: event.blockNumber,
},
} as EboEvent<"DisputeStatusUpdated">;
Expand Down Expand Up @@ -528,7 +527,6 @@ export class ProtocolProvider implements IProtocolProvider {
requestId: HexUtils.normalize(_dispute.requestId) as RequestId,
},
caller: _caller as Address,
blockNumber: event.blockNumber,
},
} as EboEvent<"DisputeEscalated">;
}),
Expand Down
92 changes: 74 additions & 18 deletions packages/automated-dispute/src/services/eboActor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
InvalidActorState,
InvalidDisputeStatus,
PastEventEnqueueError,
ProphetDecodingError,
RequestMismatch,
ResponseAlreadyProposed,
ResponseNotFound,
Expand All @@ -35,6 +36,7 @@ import {
AddRequest,
AddResponse,
FinalizeRequest,
NoOp,
ProphetCodec,
UpdateDisputeStatus,
} from "../services/index.js";
Expand Down Expand Up @@ -149,12 +151,38 @@ export class EboActor {
while ((event = this.eventsQueue.pop())) {
this.lastEventProcessed = event;

const updateStateCommand = this.buildUpdateStateCommand(event);
let updateStateCommand: EboRegistryCommand;

updateStateCommand.run();
try {
updateStateCommand = this.buildUpdateStateCommand(event);

this.logger.debug("Running command...");
this.logger.debug(stringify({ command: updateStateCommand.name() }));

updateStateCommand.run();

this.logger.debug("Command run successfully.");
} catch (err) {
if (err instanceof ProphetDecodingError) {
// Skipping malformed entities
this.logger.warn(
stringify({
reason: err.err?.name,
message: err.message,
}),
);

continue;
} else {
throw err;
}
}

try {
if (this.eventsQueue.isEmpty()) {
const wasLastEvent = this.eventsQueue.isEmpty();
const isDisputeEscalatedEvent = event.name === "DisputeEscalated";

if (wasLastEvent || isDisputeEscalatedEvent) {
// `event` is the last and most recent event thus
// it needs to run some RPCs to keep Prophet's flow going on
await this.onLastEvent(event);
Expand Down Expand Up @@ -207,23 +235,44 @@ export class EboActor {
this.registry,
);

case "ResponseDisputed":
return AddDispute.buildFromEvent(
event as EboEvent<"ResponseDisputed">,
this.registry,
);
case "ResponseDisputed": {
const disputeId = (event as EboEvent<"ResponseDisputed">).metadata.disputeId;
const dispute = this.registry.getDispute(disputeId);

// Prophet's might emit the DisputeEscalated event prior the ResponseDisputed
// event (starting from the 2nd dispute within an EBO request due to the
// BondEscalationModule behavior).
//
// This force the agent to add the Dispute when processing the DisputeEscalated
// event, causing this event to be a no-op.
return dispute
? NoOp.build()
: AddDispute.buildFromEvent(
event as EboEvent<"ResponseDisputed">,
this.registry,
);
}

case "DisputeStatusUpdated":
return UpdateDisputeStatus.buildFromEvent(
event as EboEvent<"DisputeStatusUpdated">,
this.registry,
);

case "DisputeEscalated":
return UpdateDisputeStatus.buildFromEvent(
event as EboEvent<"DisputeEscalated">,
this.registry,
);
case "DisputeEscalated": {
const disputeId = (event as EboEvent<"DisputeEscalated">).metadata.disputeId;
const dispute = this.registry.getDispute(disputeId);

return dispute
? UpdateDisputeStatus.buildFromEvent(
event as EboEvent<"DisputeEscalated">,
this.registry,
)
: AddDispute.buildFromEvent(
event as EboEvent<"DisputeEscalated">,
this.registry,
);
}

case "OracleRequestFinalized":
return FinalizeRequest.buildFromEvent(
Expand Down Expand Up @@ -391,12 +440,12 @@ export class EboActor {
private getActiveDisputes(): Dispute[] {
const disputes = this.registry.getDisputes();

return disputes.filter((dispute) => dispute.status === "Active");
return disputes.filter((dispute) => dispute.decodedData.status === "Active");
}

// TODO: extract this into another service
private canBeSettled(request: Request, dispute: Dispute, atTimestamp: UnixTimestamp): boolean {
if (dispute.status !== "Active") return false;
if (dispute.decodedData.status !== "Active") return false;

const { bondEscalationDeadline, tyingBuffer } = request.decodedData.disputeModuleData;
const deadline = (dispute.createdAt.timestamp +
Expand Down Expand Up @@ -494,7 +543,7 @@ export class EboActor {
// Response is still able to be disputed
if (atTimestamp <= disputeWindow) return false;

return dispute ? ["Lost", "None"].includes(dispute.status) : true;
return dispute ? ["Lost", "None"].includes(dispute.decodedData.status) : true;
}

/**
Expand Down Expand Up @@ -543,7 +592,7 @@ export class EboActor {
// the proposal non-active.
const activeStatus: DisputeStatus[] = ["None", "Active"];

return activeStatus.includes(dispute.status);
return activeStatus.includes(dispute.decodedData.status);
});
}

Expand Down Expand Up @@ -804,6 +853,12 @@ export class EboActor {
`Dispute ${event.metadata.disputeId} needs to be added to the internal registry.`,
);

if (dispute.decodedData.status === "Escalated") {
this.logger.warn(`Skipping dispute ${dispute.id} as it's already been escalated`);

return;
}

const request = this.getActorRequest();
const proposedResponse = this.registry.getResponse(event.metadata.responseId);

Expand Down Expand Up @@ -936,7 +991,7 @@ export class EboActor {
private async onDisputeStatusChanged(event: EboEvent<"DisputeStatusUpdated">): Promise<void> {
const request = this.getActorRequest();
const disputeId = event.metadata.disputeId;
const disputeStatus = event.metadata.status;
const disputeStatus = ProphetCodec.decodeDisputeStatus(event.metadata.status);

this.logger.info(`Dispute ${disputeId} status changed to ${disputeStatus}.`);

Expand Down Expand Up @@ -966,6 +1021,7 @@ export class EboActor {

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

this.logger.info(
`Dispute ${event.metadata.disputeId} for request ${request.id} has been escalated.`,
);
Expand Down
1 change: 1 addition & 0 deletions packages/automated-dispute/src/services/eboProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export class EboProcessor {
const currentEpoch = await this.protocolProvider.getCurrentEpoch();

this.logger.info(`Current epoch fetched.`);
this.logger.debug(stringify(currentEpoch));

return currentEpoch;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class AddDispute implements EboRegistryCommand {
) {}

public static buildFromEvent(
event: EboEvent<"ResponseDisputed">,
event: EboEvent<"ResponseDisputed" | "DisputeEscalated">,
registry: EboRegistry,
): AddDispute {
const dispute: Dispute = {
Expand All @@ -21,13 +21,19 @@ export class AddDispute implements EboRegistryCommand {
blockNumber: event.blockNumber,
logIndex: event.logIndex,
},
status: "Active",
decodedData: {
status: event.name === "ResponseDisputed" ? "Active" : "Escalated",
},
prophetData: event.metadata.dispute,
};

return new AddDispute(registry, dispute);
}

name(): string {
return "AddDispute";
}

run(): void {
if (this.wasRun) throw new CommandAlreadyRun(AddDispute.name);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export class AddRequest implements EboRegistryCommand {
return new AddRequest(registry, request);
}

name(): string {
return "AddRequest";
}

run(): void {
if (this.wasRun) throw new CommandAlreadyRun(AddRequest.name);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export class AddResponse implements EboRegistryCommand {
return new AddResponse(registry, response);
}

name(): string {
return "AddResponse";
}

run(): void {
if (this.wasRun) throw new CommandAlreadyRun(AddResponse.name);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export class FinalizeRequest implements EboRegistryCommand {
return new FinalizeRequest(registry, request);
}

name(): string {
return "FinalizeRequest";
}

run(): void {
if (this.wasRun) throw new CommandAlreadyRun(FinalizeRequest.name);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from "./addDispute.js";
export * from "./addRequest.js";
export * from "./addResponse.js";
export * from "./finalizeRequest.js";
export * from "./noOp.js";
export * from "./updateDisputeStatus.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { CommandAlreadyRun, CommandNotRun } from "../../../exceptions/index.js";
import { EboRegistryCommand } from "../../../interfaces/index.js";

export class NoOp implements EboRegistryCommand {
private wasRun: boolean = false;

private constructor() {}

public static build(): NoOp {
return new NoOp();
}

name(): string {
return "NoOp";
}

run(): void {
if (this.wasRun) throw new CommandAlreadyRun(NoOp.name);

this.wasRun = true;
}

undo(): void {
if (!this.wasRun) throw new CommandNotRun(NoOp.name);
}
}
Loading

0 comments on commit 563dfd0

Please sign in to comment.