diff --git a/validator-cli/src/ArbToEth/transactionHandler.test.ts b/validator-cli/src/ArbToEth/transactionHandler.test.ts index 677ed6cf..12b7fb6f 100644 --- a/validator-cli/src/ArbToEth/transactionHandler.test.ts +++ b/validator-cli/src/ArbToEth/transactionHandler.test.ts @@ -288,19 +288,15 @@ describe("ArbToEthTransactionHandler", () => { transactionHandler.transactions.sendSnapshotTxn, mockMessageExecutor ); - expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith( - transactionHandler.transactions.sendSnapshotTxn, - ContractType.OUTBOX - ); expect(transactionHandler.transactions.executeSnapshotTxn).toEqual("0x1234"); }); it("should not resolve challenged claim if txn is pending", async () => { jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(1); - transactionHandler.transactions.sendSnapshotTxn = "0x1234"; + transactionHandler.transactions.executeSnapshotTxn = "0x1234"; await transactionHandler.resolveChallengedClaim(mockMessageExecutor); expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith( - transactionHandler.transactions.sendSnapshotTxn, + transactionHandler.transactions.executeSnapshotTxn, ContractType.OUTBOX ); }); diff --git a/validator-cli/src/ArbToEth/transactionHandler.ts b/validator-cli/src/ArbToEth/transactionHandler.ts index f4fb453a..571b3684 100644 --- a/validator-cli/src/ArbToEth/transactionHandler.ts +++ b/validator-cli/src/ArbToEth/transactionHandler.ts @@ -23,6 +23,13 @@ type Transactions = { executeSnapshotTxn: string | null; }; +enum TransactionStatus { + NOT_MADE = 0, + PENDING = 1, + NOT_FINAL = 2, + FINAL = 3, +} + export enum ContractType { INBOX = "inbox", OUTBOX = "outbox", @@ -71,9 +78,9 @@ export class ArbToEthTransactionHandler { * @param trnxHash Transaction hash to check the status of. * @param contract Contract type to check the transaction status in. * - * @returns False if transaction is pending || not final || not made, else True. + * @returns TransactionStatus. */ - public async checkTransactionStatus(trnxHash: string | null, contract: ContractType): Promise { + public async checkTransactionStatus(trnxHash: string | null, contract: ContractType): Promise { let provider: JsonRpcProvider; if (contract === ContractType.INBOX) { provider = this.veaInboxProvider; @@ -82,15 +89,14 @@ export class ArbToEthTransactionHandler { } if (trnxHash == null) { - return 0; + return TransactionStatus.NOT_MADE; } const receipt = await provider.getTransactionReceipt(trnxHash); if (!receipt) { - // TODO: Add transaction pending timeout- redo transaction. this.emitter.emit(BotEvents.TXN_PENDING, trnxHash); - return 1; + return TransactionStatus.PENDING; } const currentBlock = await provider.getBlock("latest"); @@ -98,11 +104,10 @@ export class ArbToEthTransactionHandler { if (confirmations >= this.requiredConfirmations) { this.emitter.emit(BotEvents.TXN_FINAL, trnxHash, confirmations); - return 3; - } else { - this.emitter.emit(BotEvents.TXN_NOT_FINAL, trnxHash, confirmations); - return 2; + return TransactionStatus.FINAL; } + this.emitter.emit(BotEvents.TXN_NOT_FINAL, trnxHash, confirmations); + return TransactionStatus.NOT_FINAL; } /** @@ -114,7 +119,8 @@ export class ArbToEthTransactionHandler { if (!this.claim) { throw new ClaimNotSetError(); } - if ((await this.checkTransactionStatus(this.transactions.challengeTxn, ContractType.OUTBOX)) > 0) { + const transactionStatus = await this.checkTransactionStatus(this.transactions.challengeTxn, ContractType.OUTBOX); + if (transactionStatus > 0) { return; } const { deposit } = getBridgeConfig(this.chainId); @@ -152,7 +158,11 @@ export class ArbToEthTransactionHandler { if (!this.claim) { throw new ClaimNotSetError(); } - if ((await this.checkTransactionStatus(this.transactions.withdrawChallengeDepositTxn, ContractType.OUTBOX)) > 0) { + const transactionStatus = await this.checkTransactionStatus( + this.transactions.withdrawChallengeDepositTxn, + ContractType.OUTBOX + ); + if (transactionStatus > 0) { return; } const withdrawDepositTxn = await this.veaOutbox.withdrawChallengeDeposit(this.epoch, this.claim); @@ -168,7 +178,8 @@ export class ArbToEthTransactionHandler { if (!this.claim) { throw new ClaimNotSetError(); } - if ((await this.checkTransactionStatus(this.transactions.sendSnapshotTxn, ContractType.INBOX)) > 0) { + const transactionStatus = await this.checkTransactionStatus(this.transactions.sendSnapshotTxn, ContractType.INBOX); + if (transactionStatus > 0) { return; } const sendSnapshotTxn = await this.veaInbox.sendSnapshot(this.epoch, this.claim); @@ -181,7 +192,11 @@ export class ArbToEthTransactionHandler { */ public async resolveChallengedClaim(sendSnapshotTxn: string, executeMsg: typeof messageExecutor = messageExecutor) { this.emitter.emit(BotEvents.EXECUTING_SNAPSHOT, this.epoch); - if ((await this.checkTransactionStatus(this.transactions.sendSnapshotTxn, ContractType.OUTBOX)) > 0) { + const transactionStatus = await this.checkTransactionStatus( + this.transactions.executeSnapshotTxn, + ContractType.OUTBOX + ); + if (transactionStatus > 0) { return; } const msgExecuteTrnx = await executeMsg(sendSnapshotTxn, this.veaInboxProvider, this.veaOutboxProvider); diff --git a/validator-cli/src/ArbToEth/validator.test.ts b/validator-cli/src/ArbToEth/validator.test.ts index 7ff2d1d1..21ccb8ca 100644 --- a/validator-cli/src/ArbToEth/validator.test.ts +++ b/validator-cli/src/ArbToEth/validator.test.ts @@ -12,7 +12,7 @@ describe("validator", () => { let mockClaim: any; let mockGetClaimState: any; let mockGetBlockFinality: any; - + let mockDeps: any; beforeEach(() => { veaInbox = { snapshots: jest.fn(), @@ -41,24 +41,25 @@ describe("validator", () => { }; mockGetClaim = jest.fn(); mockGetBlockFinality = jest.fn().mockResolvedValue([{ number: 0 }, { number: 0, timestamp: 100 }, false]); + mockDeps = { + epoch: 0, + epochPeriod: 10, + veaInbox, + veaInboxProvider, + veaOutboxProvider, + veaOutbox, + transactionHandler: null, + emitter, + fetchClaim: mockGetClaim, + fetchClaimResolveState: mockGetClaimState, + fetchBlocksAndCheckFinality: mockGetBlockFinality, + }; }); describe("challengeAndResolveClaim", () => { it("should return null if no claim is made", async () => { - const transactionHandler = null; mockGetClaim = jest.fn().mockReturnValue(null); - const result = await challengeAndResolveClaim( - 0, - 10, - veaInbox, - veaInboxProvider, - veaOutbox, - veaOutboxProvider, - transactionHandler, - emitter, - mockGetClaim, - mockGetClaimState, - mockGetBlockFinality - ); + mockDeps.fetchClaim = mockGetClaim; + const result = await challengeAndResolveClaim(mockDeps); expect(result).toBeNull(); expect(emitter.emit).toHaveBeenCalledWith(BotEvents.NO_CLAIM, 0); @@ -77,19 +78,10 @@ describe("validator", () => { }; veaInbox.snapshots = jest.fn().mockReturnValue("0x321"); mockGetClaim = jest.fn().mockReturnValue(mockClaim); - const updatedTransactionHandler = await challengeAndResolveClaim( - 0, - 10, - veaInbox, - veaInboxProvider, - veaOutbox, - veaOutboxProvider, - mockTransactionHandler as any, - emitter, - mockGetClaim, - mockGetClaimState, - mockGetBlockFinality - ); + + mockDeps.transactionHandler = mockTransactionHandler; + mockDeps.fetchClaim = mockGetClaim; + const updatedTransactionHandler = await challengeAndResolveClaim(mockDeps); expect(updatedTransactionHandler.transactions.challengeTxn).toBe(challengeTxn); expect(mockTransactionHandler.challengeClaim).toHaveBeenCalled(); }); @@ -98,19 +90,8 @@ describe("validator", () => { mockClaim.challenger = mockClaim.claimer; mockGetClaim = jest.fn().mockReturnValue(mockClaim); veaInbox.snapshots = jest.fn().mockReturnValue(mockClaim.stateRoot); - const updatedTransactionHandler = await challengeAndResolveClaim( - 0, - 10, - veaInbox, - veaInboxProvider, - veaOutbox, - veaOutboxProvider, - null, - emitter, - mockGetClaim, - mockGetClaimState, - mockGetBlockFinality - ); + mockDeps.fetchClaim = mockGetClaim; + const updatedTransactionHandler = await challengeAndResolveClaim(mockDeps); expect(updatedTransactionHandler).toBeNull(); }); @@ -130,19 +111,10 @@ describe("validator", () => { sendSnapshotTxn: "0x0", }, }; - const updatedTransactionHandler = await challengeAndResolveClaim( - 0, - 10, - veaInbox, - veaInboxProvider, - veaOutbox, - veaOutboxProvider, - mockTransactionHandler as any, - emitter, - mockGetClaim, - mockGetClaimState, - mockGetBlockFinality - ); + mockDeps.transactionHandler = mockTransactionHandler; + mockDeps.fetchClaimResolveState = mockGetClaimState; + mockDeps.fetchClaim = mockGetClaim; + const updatedTransactionHandler = await challengeAndResolveClaim(mockDeps); expect(updatedTransactionHandler.transactions.sendSnapshotTxn).toEqual("0x123"); expect(mockTransactionHandler.sendSnapshot).toHaveBeenCalled(); expect(updatedTransactionHandler.claim).toEqual(mockClaim); @@ -164,19 +136,10 @@ describe("validator", () => { executeSnapshotTxn: "0x0", }, }; - const updatedTransactionHandler = await challengeAndResolveClaim( - 0, - 10, - veaInbox, - veaInboxProvider, - veaOutbox, - veaOutboxProvider, - mockTransactionHandler as any, - emitter, - mockGetClaim, - mockGetClaimState, - mockGetBlockFinality - ); + mockDeps.transactionHandler = mockTransactionHandler; + mockDeps.fetchClaimResolveState = mockGetClaimState; + mockDeps.fetchClaim = mockGetClaim; + const updatedTransactionHandler = await challengeAndResolveClaim(mockDeps); expect(updatedTransactionHandler.transactions.executeSnapshotTxn).toEqual("0x123"); expect(mockTransactionHandler.resolveChallengedClaim).toHaveBeenCalled(); expect(updatedTransactionHandler.claim).toEqual(mockClaim); @@ -199,19 +162,10 @@ describe("validator", () => { withdrawChallengeDepositTxn: "0x0", }, }; - const updatedTransactionHandler = await challengeAndResolveClaim( - 0, - 10, - veaInbox, - veaInboxProvider, - veaOutbox, - veaOutboxProvider, - mockTransactionHandler as any, - emitter, - mockGetClaim, - mockGetClaimState, - mockGetBlockFinality - ); + mockDeps.transactionHandler = mockTransactionHandler; + mockDeps.fetchClaimResolveState = mockGetClaimState; + mockDeps.fetchClaim = mockGetClaim; + const updatedTransactionHandler = await challengeAndResolveClaim(mockDeps); expect(updatedTransactionHandler.transactions.withdrawChallengeDepositTxn).toEqual("0x1234"); expect(mockTransactionHandler.withdrawChallengeDeposit).toHaveBeenCalled(); expect(updatedTransactionHandler.claim).toEqual(mockClaim); diff --git a/validator-cli/src/ArbToEth/validator.ts b/validator-cli/src/ArbToEth/validator.ts index 5033c45b..f5fef06b 100644 --- a/validator-cli/src/ArbToEth/validator.ts +++ b/validator-cli/src/ArbToEth/validator.ts @@ -10,19 +10,33 @@ import { getBlocksAndCheckFinality } from "../utils/arbToEthState"; // https://github.com/prysmaticlabs/prysm/blob/493905ee9e33a64293b66823e69704f012b39627/config/params/mainnet_config.go#L103 const secondsPerSlotEth = 12; -export async function challengeAndResolveClaim( - epoch: number, - epochPeriod: number, - veaInbox: VeaInboxArbToEth, - veaInboxProvider: JsonRpcProvider, - veaOutboxProvider: JsonRpcProvider, - veaOutbox: VeaOutboxArbToEth, - transactionHandler: ArbToEthTransactionHandler | null, - emitter: typeof defaultEmitter = defaultEmitter, - fetchClaim: typeof getClaim = getClaim, - fetchClaimResolveState: typeof getClaimResolveState = getClaimResolveState, - fetchBlocksAndCheckFinality: typeof getBlocksAndCheckFinality = getBlocksAndCheckFinality -): Promise { +export interface ChallengeAndResolveClaimParams { + epoch: number; + epochPeriod: number; + veaInbox: any; + veaInboxProvider: JsonRpcProvider; + veaOutboxProvider: JsonRpcProvider; + veaOutbox: any; + transactionHandler: ArbToEthTransactionHandler | null; + emitter?: typeof defaultEmitter; + fetchClaim?: typeof getClaim; + fetchClaimResolveState?: typeof getClaimResolveState; + fetchBlocksAndCheckFinality?: typeof getBlocksAndCheckFinality; +} + +export async function challengeAndResolveClaim({ + epoch, + epochPeriod, + veaInbox, + veaInboxProvider, + veaOutboxProvider, + veaOutbox, + transactionHandler, + emitter = defaultEmitter, + fetchClaim = getClaim, + fetchClaimResolveState = getClaimResolveState, + fetchBlocksAndCheckFinality = getBlocksAndCheckFinality, +}: ChallengeAndResolveClaimParams): Promise { const [arbitrumBlock, ethFinalizedBlock, finalityIssueFlagEth] = await fetchBlocksAndCheckFinality( veaOutboxProvider, veaInboxProvider, diff --git a/validator-cli/src/utils/claim.ts b/validator-cli/src/utils/claim.ts index b24efa04..6b7e2262 100644 --- a/validator-cli/src/utils/claim.ts +++ b/validator-cli/src/utils/claim.ts @@ -10,6 +10,12 @@ import { getMessageStatus } from "./arbMsgExecutor"; * @param epoch epoch number of the claim to be fetched * @returns claim type of ClaimStruct */ + +enum ClaimHonestState { + NONE = 0, + CLAIMER = 1, + CHALLENGER = 2, +} const getClaim = async ( veaOutbox: any, veaOutboxProvider: JsonRpcProvider, @@ -52,11 +58,11 @@ const getClaim = async ( if (hashClaim(claim) == claimHash) { return claim; } - claim.honest = 1; // Assuming claimer is honest + claim.honest = ClaimHonestState.CLAIMER; // Assuming claimer is honest if (hashClaim(claim) == claimHash) { return claim; } - claim.honest = 2; // Assuming challenger is honest + claim.honest = ClaimHonestState.CHALLENGER; // Assuming challenger is honest if (hashClaim(claim) == claimHash) { return claim; } @@ -91,10 +97,9 @@ const getClaimResolveState = async ( }; if (sentSnapshotLogs.length === 0) return claimResolveState; - else { - claimResolveState.sendSnapshot.status = true; - claimResolveState.sendSnapshot.txHash = sentSnapshotLogs[0].transactionHash; - } + + claimResolveState.sendSnapshot.status = true; + claimResolveState.sendSnapshot.txHash = sentSnapshotLogs[0].transactionHash; const status = await fetchMessageStatus(sentSnapshotLogs[0].transactionHash, veaInboxProvider, veaOutboxProvider); claimResolveState.execution.status = status; diff --git a/validator-cli/src/utils/epochHandler.ts b/validator-cli/src/utils/epochHandler.ts index 3cb79b52..0d57128a 100644 --- a/validator-cli/src/utils/epochHandler.ts +++ b/validator-cli/src/utils/epochHandler.ts @@ -57,7 +57,6 @@ const getLatestChallengeableEpoch = ( now: number = Date.now(), fetchBridgeConfig: typeof getBridgeConfig = getBridgeConfig ): number => { - // NOTE: Add logic to check if claim was made here or in main function? const { epochPeriod } = fetchBridgeConfig(chainId); return Math.floor(now / 1000 / epochPeriod) - 2; }; diff --git a/validator-cli/src/utils/errors.ts b/validator-cli/src/utils/errors.ts index c87a65a4..7d4256e5 100644 --- a/validator-cli/src/utils/errors.ts +++ b/validator-cli/src/utils/errors.ts @@ -14,14 +14,6 @@ class ClaimNotSetError extends Error { } } -class ContractNotSupportedError extends Error { - constructor(contract: string) { - super(); - this.name = "ContractNotSupportedError"; - this.message = `Unsupported contract type: ${contract}`; - } -} - class TransactionHandlerNotDefinedError extends Error { constructor() { super(); @@ -30,4 +22,4 @@ class TransactionHandlerNotDefinedError extends Error { } } -export { ClaimNotFoundError, ClaimNotSetError, ContractNotSupportedError, TransactionHandlerNotDefinedError }; +export { ClaimNotFoundError, ClaimNotSetError, TransactionHandlerNotDefinedError }; diff --git a/validator-cli/src/watcher.ts b/validator-cli/src/watcher.ts index 9de4dcb0..0d5795ee 100644 --- a/validator-cli/src/watcher.ts +++ b/validator-cli/src/watcher.ts @@ -41,16 +41,17 @@ export const watch = async ( while (i < epochRange.length) { const epoch = epochRange[i]; emitter.emit(BotEvents.CHECKING, epoch); - const updatedTransactions = await checkAndChallengeResolve( + const checkAndChallengeResolveDeps = { epoch, - veaBridge.epochPeriod, - veaInbox as any, + epochPeriod: veaBridge.epochPeriod, + veaInbox, veaInboxProvider, veaOutboxProvider, - veaOutbox as any, - transactionHandlers[epoch], - emitter - ); + veaOutbox, + transactionHandler: transactionHandlers[epoch], + emitter, + }; + const updatedTransactions = await checkAndChallengeResolve(checkAndChallengeResolveDeps); if (updatedTransactions) { transactionHandlers[epoch] = updatedTransactions; } else {