From 583467408f6c34e52ab94c1c5b3085cd965d4d1f Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Sun, 12 Jan 2025 18:52:10 +0530 Subject: [PATCH 1/9] feat: proof & relay tests --- relayer-cli/src/testnetRelayer.ts | 11 +- relayer-cli/src/utils/ethers.ts | 2 +- relayer-cli/src/utils/proof.test.ts | 20 +++ relayer-cli/src/utils/proof.ts | 45 ++++-- relayer-cli/src/utils/relay.test.ts | 186 ++++++++++++++++++++++++ relayer-cli/src/utils/relay.ts | 81 +++++++++-- relayer-cli/src/utils/relayerHelpers.ts | 4 +- 7 files changed, 322 insertions(+), 27 deletions(-) create mode 100644 relayer-cli/src/utils/proof.test.ts create mode 100644 relayer-cli/src/utils/relay.test.ts diff --git a/relayer-cli/src/testnetRelayer.ts b/relayer-cli/src/testnetRelayer.ts index 247345f9..5ec104a0 100644 --- a/relayer-cli/src/testnetRelayer.ts +++ b/relayer-cli/src/testnetRelayer.ts @@ -1,5 +1,5 @@ require("dotenv").config(); -import { relayBatch } from "utils/relay"; +import { relayBatch, RelayBatchDeps } from "utils/relay"; import { initialize, updateStateFile, delay, setupExitHandlers, ShutdownManager } from "utils/relayerHelpers"; import { getEpochPeriod } from "consts/bridgeRoutes"; @@ -7,13 +7,18 @@ export async function start(shutdownManager: ShutdownManager = new ShutdownManag const network = "testnet"; const chainId = parseInt(process.env.VEAOUTBOX_CHAIN_ID); const epochPeriod = getEpochPeriod(chainId); - const batchSize = 10; // 10 messages per batch + const maxBatchSize = 10; // 10 messages per batch await setupExitHandlers(chainId, shutdownManager, network); while (!shutdownManager.getIsShuttingDown()) { let nonce = await initialize(chainId, network); - nonce = await relayBatch(chainId, nonce, batchSize); + const relayBatchDeps: RelayBatchDeps = { + chainId, + nonce, + maxBatchSize, + }; + nonce = await relayBatch(relayBatchDeps); if (nonce != null) await updateStateFile(chainId, Math.floor(Date.now() / 1000), nonce, network); const currentTS = Math.floor(Date.now() / 1000); const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000; diff --git a/relayer-cli/src/utils/ethers.ts b/relayer-cli/src/utils/ethers.ts index 3aee5308..e8f44ce2 100644 --- a/relayer-cli/src/utils/ethers.ts +++ b/relayer-cli/src/utils/ethers.ts @@ -6,7 +6,7 @@ import { VeaInboxArbToGnosis__factory, VeaOutboxArbToGnosis__factory, } from "@kleros/vea-contracts/typechain-types"; -import { getBridgeConfig } from "consts/bridgeRoutes"; +import { getBridgeConfig } from "../consts/bridgeRoutes"; function getWallet(privateKey: string, web3ProviderURL: string): Wallet { return new Wallet(privateKey, new JsonRpcProvider(web3ProviderURL)); diff --git a/relayer-cli/src/utils/proof.test.ts b/relayer-cli/src/utils/proof.test.ts new file mode 100644 index 00000000..b6438b2b --- /dev/null +++ b/relayer-cli/src/utils/proof.test.ts @@ -0,0 +1,20 @@ +import { getProofIndices } from "./proof"; + +describe("proof", () => { + describe("getProofIndices", () => { + it("should return an empty array", () => { + const result = getProofIndices(7, 7); + expect(result).toEqual([]); + }); + it("should return the proof indices", () => { + const expectedProofIndices = ["3", "0,1", "4,6"]; + const result = getProofIndices(2, 7); + expect(result).toEqual(expectedProofIndices); + }); + it("should return the proof indices(for a large count", () => { + const expectedProofIndices = ["6", "4,5", "0,3", "8,14"]; + const result = getProofIndices(7, 15); + expect(result).toEqual(expectedProofIndices); + }); + }); +}); diff --git a/relayer-cli/src/utils/proof.ts b/relayer-cli/src/utils/proof.ts index 15d0765e..693e13bf 100644 --- a/relayer-cli/src/utils/proof.ts +++ b/relayer-cli/src/utils/proof.ts @@ -1,11 +1,17 @@ import request from "graphql-request"; import { getInboxSubgraph } from "../consts/bridgeRoutes"; -const getMessageDataToRelay = async (chainid: number, nonce: number) => { +/** + * Get the message data to relay from the subgraph + * @param chainId The chain id of the veaOutbox chain + * @param nonce The nonce of the message + * @returns The message id and data to relay + */ +const getMessageDataToRelay = async (chainId: number, nonce: number, requestGraph: typeof request = request) => { try { - const subgraph = getInboxSubgraph(chainid); + const subgraph = getInboxSubgraph(chainId); - const result = await request( + const result = await requestGraph( `https://api.studio.thegraph.com/query/${subgraph}`, `{ messageSents(first: 5, where: {nonce: ${nonce}}) { @@ -24,9 +30,22 @@ const getMessageDataToRelay = async (chainid: number, nonce: number) => { } }; -const getProofAtCount = async (chainid: number, nonce: number, count: number): Promise => { - const proofIndices = getProofIndices(nonce, count); - +/** + * Get the proof of the message at a given count + * @param chainId The chain id of the veaOutbox chain + * @param nonce The nonce of the message + * @param count The current veaOutbox count + * @returns The proof of the message + */ +const getProofAtCount = async ( + chainId: number, + nonce: number, + count: number, + requestGraph: typeof request = request, + calculateProofIndices: typeof getProofIndices = getProofIndices, + fetchInboxSubgraph: typeof getInboxSubgraph = getInboxSubgraph +): Promise => { + const proofIndices = calculateProofIndices(nonce, count); if (proofIndices.length == 0) return []; let query = "{"; @@ -38,16 +57,14 @@ const getProofAtCount = async (chainid: number, nonce: number, count: number): P query += "}"; try { - const subgraph = getInboxSubgraph(chainid); + const subgraph = fetchInboxSubgraph(chainId); - const result = await request(`https://api.studio.thegraph.com/query/${subgraph}`, query); + const result = await requestGraph(`https://api.studio.thegraph.com/query/${subgraph}`, query); const proof = []; - for (let i = 0; i < proofIndices.length; i++) { proof.push(result[`layer${i}`][0].hash); } - return proof; } catch (e) { console.log(e); @@ -55,6 +72,12 @@ const getProofAtCount = async (chainid: number, nonce: number, count: number): P } }; +/** + * Get the proof indices of the message + * @param nonce The nonce of the message + * @param count The current veaOutbox count + * @returns The proof indices of the message + */ const getProofIndices = (nonce: number, count: number) => { let proof = []; if (nonce >= count) return proof; @@ -76,4 +99,4 @@ const getProofIndices = (nonce: number, count: number) => { return proof; }; -export { getProofAtCount, getMessageDataToRelay }; +export { getProofAtCount, getMessageDataToRelay, getProofIndices }; diff --git a/relayer-cli/src/utils/relay.test.ts b/relayer-cli/src/utils/relay.test.ts new file mode 100644 index 00000000..0ae6d551 --- /dev/null +++ b/relayer-cli/src/utils/relay.test.ts @@ -0,0 +1,186 @@ +import { abi } from "web3/lib/commonjs/eth.exports"; +import { relayBatch } from "./relay"; + +describe("relay", () => { + describe("relayBatch", () => { + const veaOutboxAddress = "0x123"; + const chainId = 1; + const nonce = 0; + const maxBatchSize = 10; + const fetchBridgeConfig = jest.fn(); + const fetchCount = jest.fn(); + const setBatchedSend = jest.fn(); + const fetchVeaOutbox = jest.fn(); + const fetchProofAtCount = jest.fn(); + const fetchMessageDataToRelay = jest.fn(); + const web3 = jest.fn() as any; + const mockBatchedSend = jest.fn(async (txns) => Promise.resolve()); + beforeEach(() => { + fetchBridgeConfig.mockReturnValue({ + veaOutboxContract: { + abi: [], + }, + }); + fetchCount.mockReturnValue(1); + setBatchedSend.mockReturnValue(mockBatchedSend); + fetchVeaOutbox.mockReturnValue({ + isMsgRelayed: jest.fn(), + sendMessage: jest.fn(), + }); + fetchProofAtCount.mockReturnValue([]); + fetchMessageDataToRelay.mockReturnValue(["to", "data"]); + web3.mockReturnValue({ + eth: { + Contract: jest.fn().mockReturnValue({ + methods: { + sendMessage: jest.fn(), + }, + options: { + address: veaOutboxAddress, + }, + }), + }, + }); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + it("should not relay any messages if there are no messages to relay", async () => { + fetchCount.mockReturnValue(0); + const sendBatch = jest.fn(); + setBatchedSend.mockReturnValue({ + sendBatch, + }); + const updatedNonce = await relayBatch({ + chainId, + nonce, + maxBatchSize, + fetchBridgeConfig, + fetchCount, + setBatchedSend, + fetchVeaOutbox, + fetchProofAtCount, + fetchMessageDataToRelay, + web3, + }); + expect(sendBatch).not.toHaveBeenCalled(); + expect(updatedNonce).toBe(0); + }); + + it("should relay a single message", async () => { + fetchCount.mockReturnValue(1); + const updatedNonce = await relayBatch({ + chainId, + nonce, + maxBatchSize, + fetchBridgeConfig, + fetchCount, + setBatchedSend, + fetchVeaOutbox, + fetchProofAtCount, + fetchMessageDataToRelay, + web3, + }); + expect(mockBatchedSend).toHaveBeenCalledTimes(1); + expect(mockBatchedSend).toHaveBeenCalledWith([ + { + args: [[], 0, "to", "data"], + method: expect.any(Function), // sendMessage function + to: veaOutboxAddress, + }, + ]); + expect(updatedNonce).toBe(1); + }); + + it("should relay multiple messages in a single batch", async () => { + fetchCount.mockReturnValue(7); + const updatedNonce = await relayBatch({ + chainId, + nonce, + maxBatchSize, + fetchBridgeConfig, + fetchCount, + setBatchedSend, + fetchVeaOutbox, + fetchProofAtCount, + fetchMessageDataToRelay, + web3, + }); + expect(mockBatchedSend).toHaveBeenCalledTimes(1); + const expectedCalls = Array.from({ length: 7 }, (_, index) => ({ + args: [[], index, "to", "data"], + method: expect.any(Function), + to: veaOutboxAddress, + })); + + expect(mockBatchedSend).toHaveBeenCalledWith(expectedCalls); + + expect(updatedNonce).toBe(7); + }); + it("should relay multiple messages in multiple batches", async () => { + fetchCount.mockReturnValue(15); + const updatedNonce = await relayBatch({ + chainId, + nonce, + maxBatchSize, + fetchBridgeConfig, + fetchCount, + setBatchedSend, + fetchVeaOutbox, + fetchProofAtCount, + fetchMessageDataToRelay, + web3, + }); + expect(mockBatchedSend).toHaveBeenCalledTimes(2); + const firstBatchCalls = Array.from({ length: 10 }, (_, index) => ({ + args: [[], index, "to", "data"], + method: expect.any(Function), + to: veaOutboxAddress, + })); + + const secondBatchCalls = Array.from({ length: 5 }, (_, index) => ({ + args: [[], index + 10, "to", "data"], + method: expect.any(Function), + to: veaOutboxAddress, + })); + + expect(mockBatchedSend).toHaveBeenNthCalledWith(1, firstBatchCalls); + expect(mockBatchedSend).toHaveBeenNthCalledWith(2, secondBatchCalls); + expect(updatedNonce).toBe(15); + }); + it("should not relay messages that have already been relayed", async () => { + fetchCount.mockReturnValue(3); + fetchVeaOutbox.mockReturnValue({ + isMsgRelayed: jest.fn().mockImplementation((nonce) => nonce === 1), + sendMessage: jest.fn(), + }); + const updatedNonce = await relayBatch({ + chainId, + nonce, + maxBatchSize, + fetchBridgeConfig, + fetchCount, + setBatchedSend, + fetchVeaOutbox, + fetchProofAtCount, + fetchMessageDataToRelay, + web3, + }); + expect(mockBatchedSend).toHaveBeenCalledTimes(1); + const batchCall = [ + { + args: [[], 0, "to", "data"], + method: expect.any(Function), + to: veaOutboxAddress, + }, + { + args: [[], 2, "to", "data"], + method: expect.any(Function), + to: veaOutboxAddress, + }, + ]; + expect(mockBatchedSend).toHaveBeenCalledWith(batchCall); + expect(updatedNonce).toBe(3); + }); + }); +}); diff --git a/relayer-cli/src/utils/relay.ts b/relayer-cli/src/utils/relay.ts index 3b3a0f06..0fb96e12 100644 --- a/relayer-cli/src/utils/relay.ts +++ b/relayer-cli/src/utils/relay.ts @@ -7,6 +7,11 @@ import { getProofAtCount, getMessageDataToRelay } from "./proof"; import { getVeaOutbox } from "./ethers"; import { getBridgeConfig, getInboxSubgraph } from "../consts/bridgeRoutes"; +/** + * Get the count of the veaOutbox + * @param veaOutbox The veaOutbox contract instance + * @param chainId The chain id of the veaOutbox chain + */ const getCount = async (veaOutbox: VeaOutboxArbToEth | VeaOutboxArbToGnosis, chainId: number): Promise => { const subgraph = getInboxSubgraph(chainId); const stateRoot = await veaOutbox.stateRoot(); @@ -25,6 +30,11 @@ const getCount = async (veaOutbox: VeaOutboxArbToEth | VeaOutboxArbToGnosis, cha return Number(result["snapshotSaveds"][0].count); }; +/** + * Relay a message from the veaOutbox + * @param chainId The chain id of the veaOutbox chain + * @param nonce The nonce of the message + */ const relay = async (chainId: number, nonce: number) => { const routeParams = getBridgeConfig(chainId); const veaOutbox = getVeaOutbox(routeParams.veaOutboxAddress, process.env.PRIVATE_KEY, routeParams.rpcOutbox, chainId); @@ -40,13 +50,52 @@ const relay = async (chainId: number, nonce: number) => { return receipt; }; -const relayBatch = async (chainId: number, nonce: number, maxBatchSize: number) => { - const routeParams = getBridgeConfig(chainId); - const web3 = new Web3(routeParams.rpcOutbox); - const batchedSend = initializeBatchedSend(web3, routeParams.batcher, process.env.PRIVATE_KEY, 0); - const veaOutboxInstance = new web3.eth.Contract(routeParams.veaOutboxContract.abi, routeParams.veaOutboxAddress); - const veaOutbox = getVeaOutbox(routeParams.veaOutboxAddress, process.env.PRIVATE_KEY, routeParams.rpcOutbox, chainId); - const count = await getCount(veaOutbox, chainId); +interface RelayBatchDeps { + chainId: number; + nonce: number; + maxBatchSize: number; + fetchVeaOutbox?: typeof getVeaOutbox; + fetchCount?: typeof getCount; + setBatchedSend?: typeof initializeBatchedSend; + fetchBridgeConfig?: typeof getBridgeConfig; + fetchProofAtCount?: typeof getProofAtCount; + fetchMessageDataToRelay?: typeof getMessageDataToRelay; + web3?: typeof Web3; +} + +/** + * Relay a batch of messages from the veaOutbox + * @param chainId The chain id of the veaOutbox chain + * @param nonce The nonce of the message + * @param maxBatchSize The maximum number of messages to relay in a single batch + */ +const relayBatch = async ({ + chainId, + nonce, + maxBatchSize, + fetchBridgeConfig = getBridgeConfig, + fetchCount = getCount, + setBatchedSend = initializeBatchedSend, + fetchVeaOutbox = getVeaOutbox, + fetchProofAtCount = getProofAtCount, + fetchMessageDataToRelay = getMessageDataToRelay, + web3 = Web3, +}: RelayBatchDeps) => { + const routeParams = fetchBridgeConfig(chainId); + const web3Instance = new web3(routeParams.rpcOutbox); + + const batchedSend = setBatchedSend(web3Instance, routeParams.batcher, process.env.PRIVATE_KEY, 0); + const veaOutboxInstance = new web3Instance.eth.Contract( + routeParams.veaOutboxContract.abi, + routeParams.veaOutboxAddress + ); + const veaOutbox = fetchVeaOutbox( + routeParams.veaOutboxAddress, + process.env.PRIVATE_KEY, + routeParams.rpcOutbox, + chainId + ); + const count = await fetchCount(veaOutbox, chainId); while (nonce < count) { let batchMessages = 0; @@ -58,8 +107,8 @@ const relayBatch = async (chainId: number, nonce: number, maxBatchSize: number) continue; } const [proof, [to, data]] = await Promise.all([ - getProofAtCount(chainId, nonce, count), - getMessageDataToRelay(chainId, nonce), + fetchProofAtCount(chainId, nonce, count), + fetchMessageDataToRelay(chainId, nonce), ]); txns.push({ args: [proof, nonce, to, data], @@ -76,6 +125,12 @@ const relayBatch = async (chainId: number, nonce: number, maxBatchSize: number) return nonce; }; +/** + * Relay all messages from the veaOutbox for a given sender + * @param chainId The chain id of the veaOutbox chain + * @param nonce The nonce of the first message to relay + * @param msgSender The address of the sender + */ const relayAllFrom = async (chainId: number, nonce: number, msgSender: string): Promise => { const routeParams = getBridgeConfig(chainId); @@ -115,6 +170,12 @@ const relayAllFrom = async (chainId: number, nonce: number, msgSender: string): return nonces[nonces.length - 1]; }; +/** + * Get the nonces of messages sent by a given sender + * @param chainId The chain id of the veaOutbox chain + * @param nonce The nonce of the first message to relay + * @param msgSender The address of the sender + */ const getNonceFrom = async (chainId: number, nonce: number, msgSender: string) => { const subgraph = getInboxSubgraph(chainId); @@ -138,4 +199,4 @@ const getNonceFrom = async (chainId: number, nonce: number, msgSender: string) = return result[`messageSents`].map((a: { nonce: number }) => a.nonce); }; -export { relayAllFrom, relay, relayBatch }; +export { relayAllFrom, relay, relayBatch, RelayBatchDeps }; diff --git a/relayer-cli/src/utils/relayerHelpers.ts b/relayer-cli/src/utils/relayerHelpers.ts index 28e48ebe..f9782672 100644 --- a/relayer-cli/src/utils/relayerHelpers.ts +++ b/relayer-cli/src/utils/relayerHelpers.ts @@ -4,7 +4,7 @@ import ShutdownManager from "./shutdownManager"; async function initialize(chainId: number, network: string): Promise { claimLock(network, chainId); - + console.log("lock claimed"); // STATE_DIR is absolute path of the directory where the state files are stored // STATE_DIR must have trailing slash const state_file = process.env.STATE_DIR + network + "_" + chainId + ".json"; @@ -13,9 +13,9 @@ async function initialize(chainId: number, network: string): Promise { const tsnow = Math.floor(Date.now() / 1000); await updateStateFile(chainId, tsnow, 0, network); } - // print pwd for debugging console.log(process.cwd()); + const chain_state_raw = fs.readFileSync(state_file, { encoding: "utf8" }); const chain_state = JSON.parse(chain_state_raw); let nonce = 0; From 7130a0f73bef2b6cb87eb86f1bc60debb6cd5dcb Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 13 Jan 2025 19:19:00 +0530 Subject: [PATCH 2/9] feat: relayerHelper tests --- relayer-cli/src/utils/relayerHelpers.test.ts | 62 ++++++++++++++++++++ relayer-cli/src/utils/relayerHelpers.ts | 30 +++++++--- 2 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 relayer-cli/src/utils/relayerHelpers.test.ts diff --git a/relayer-cli/src/utils/relayerHelpers.test.ts b/relayer-cli/src/utils/relayerHelpers.test.ts new file mode 100644 index 00000000..9a5671a7 --- /dev/null +++ b/relayer-cli/src/utils/relayerHelpers.test.ts @@ -0,0 +1,62 @@ +import { initialize, updateStateFile } from "./relayerHelpers"; + +describe("relayerHelpers", () => { + const chainId = 1; + const network = "testing"; + const claimLock = jest.fn(); + const mockUpdateStateFile = jest.fn(); + const fileSystem = { + readFileSync: jest.fn(), + existsSync: jest.fn(), + writeFileSync: jest.fn(), + promises: { + unlink: jest.fn(), + }, + }; + const releaseLock = jest.fn(); + afterEach(() => { + jest.clearAllMocks(); + }); + describe("initialize", () => { + it("should claimLock and create a state file if it doesn't exist", async () => { + fileSystem.existsSync.mockReturnValue(false); + fileSystem.readFileSync.mockReturnValue('{"nonce":0}'); + const nonce = await initialize(chainId, network, claimLock, mockUpdateStateFile, fileSystem as any); + expect(claimLock).toHaveBeenCalledWith(network, chainId); + expect(mockUpdateStateFile).toHaveBeenCalledWith(chainId, expect.any(Number), 0, network); + expect(nonce).toBe(0); + }); + it("should claimLock and return nonce from existing state file", async () => { + fileSystem.existsSync.mockReturnValue(true); + fileSystem.readFileSync.mockReturnValue('{"nonce":10}'); + const nonce = await initialize(chainId, network, claimLock, mockUpdateStateFile, fileSystem as any); + expect(claimLock).toHaveBeenCalledWith(network, chainId); + expect(mockUpdateStateFile).not.toHaveBeenCalled(); + expect(nonce).toBe(10); + }); + }); + + describe("updateStateFile", () => { + it("should write a state file with the provided nonce", async () => { + const createdTimestamp = 123456; + const fileDirectory = "./state/testing_1.json"; + await updateStateFile(chainId, createdTimestamp, 10, network, fileSystem as any, releaseLock); + expect(fileSystem.writeFileSync).toHaveBeenCalledWith( + fileDirectory, + JSON.stringify({ ts: createdTimestamp, nonce: 10 }), + { encoding: "utf8" } + ); + expect(releaseLock).toHaveBeenCalledWith(network, chainId); + }); + }); + + describe("setupExitHandlers", () => { + it.todo("should register signal handlers for SIGINT, SIGTERM, and SIGQUIT"); + + it.todo("should trigger shutdown and cleanup on SIGINT signal"); + + it.todo("should trigger shutdown and cleanup on SIGTERM signal"); + + it.todo("should trigger shutdown and cleanup on SIGQUIT signal"); + }); +}); diff --git a/relayer-cli/src/utils/relayerHelpers.ts b/relayer-cli/src/utils/relayerHelpers.ts index f9782672..f0d5f53c 100644 --- a/relayer-cli/src/utils/relayerHelpers.ts +++ b/relayer-cli/src/utils/relayerHelpers.ts @@ -2,21 +2,27 @@ import * as fs from "fs"; import { claimLock, releaseLock } from "./lock"; import ShutdownManager from "./shutdownManager"; -async function initialize(chainId: number, network: string): Promise { - claimLock(network, chainId); +async function initialize( + chainId: number, + network: string, + setLock: typeof claimLock = claimLock, + syncStateFile: typeof updateStateFile = updateStateFile, + fileSystem: typeof fs = fs +): Promise { + setLock(network, chainId); console.log("lock claimed"); // STATE_DIR is absolute path of the directory where the state files are stored // STATE_DIR must have trailing slash const state_file = process.env.STATE_DIR + network + "_" + chainId + ".json"; - if (!fs.existsSync(state_file)) { + if (!fileSystem.existsSync(state_file)) { // No state file so initialize starting now const tsnow = Math.floor(Date.now() / 1000); - await updateStateFile(chainId, tsnow, 0, network); + await syncStateFile(chainId, tsnow, 0, network); } // print pwd for debugging console.log(process.cwd()); - const chain_state_raw = fs.readFileSync(state_file, { encoding: "utf8" }); + const chain_state_raw = fileSystem.readFileSync(state_file, { encoding: "utf8" }); const chain_state = JSON.parse(chain_state_raw); let nonce = 0; if ("nonce" in chain_state) { @@ -26,15 +32,22 @@ async function initialize(chainId: number, network: string): Promise { return nonce; } -async function updateStateFile(chainId: number, createdTimestamp: number, nonceFrom: number, network: string) { +async function updateStateFile( + chainId: number, + createdTimestamp: number, + nonceFrom: number, + network: string, + fileSystem: typeof fs = fs, + removeLock: typeof releaseLock = releaseLock +) { const chain_state_file = "./state/" + network + "_" + chainId + ".json"; const json = { ts: createdTimestamp, nonce: nonceFrom, }; - fs.writeFileSync(chain_state_file, JSON.stringify(json), { encoding: "utf8" }); + fileSystem.writeFileSync(chain_state_file, JSON.stringify(json), { encoding: "utf8" }); - releaseLock(network, chainId); + removeLock(network, chainId); } async function setupExitHandlers(chainId: number, shutdownManager: ShutdownManager, network: string) { @@ -54,7 +67,6 @@ async function setupExitHandlers(chainId: number, shutdownManager: ShutdownManag ["SIGINT", "SIGTERM", "SIGQUIT"].forEach((signal) => process.on(signal, async () => { await handleExit(0); - process.exit(0); }) ); From 3178c6702c6da3201d3dda1cda5bbfbd16fb2527 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Tue, 14 Jan 2025 17:47:40 +0530 Subject: [PATCH 3/9] feat: logger --- relayer-cli/src/testnetRelayer.ts | 49 ++++++++++++++++---- relayer-cli/src/utils/botEvents.ts | 15 ++++++ relayer-cli/src/utils/logger.ts | 49 ++++++++++++++++++++ relayer-cli/src/utils/relay.test.ts | 1 - relayer-cli/src/utils/relay.ts | 6 +++ relayer-cli/src/utils/relayerHelpers.test.ts | 24 ++++++++-- relayer-cli/src/utils/relayerHelpers.ts | 31 ++++++++++--- relayer-cli/state/testnet_11155111.json | 4 ++ 8 files changed, 158 insertions(+), 21 deletions(-) create mode 100644 relayer-cli/src/utils/botEvents.ts create mode 100644 relayer-cli/src/utils/logger.ts create mode 100644 relayer-cli/state/testnet_11155111.json diff --git a/relayer-cli/src/testnetRelayer.ts b/relayer-cli/src/testnetRelayer.ts index 5ec104a0..6708894e 100644 --- a/relayer-cli/src/testnetRelayer.ts +++ b/relayer-cli/src/testnetRelayer.ts @@ -1,33 +1,64 @@ require("dotenv").config(); +import { EventEmitter } from "node:events"; import { relayBatch, RelayBatchDeps } from "utils/relay"; -import { initialize, updateStateFile, delay, setupExitHandlers, ShutdownManager } from "utils/relayerHelpers"; +import { + initialize as initializeNonce, + updateStateFile, + delay, + setupExitHandlers, + ShutdownManager, +} from "utils/relayerHelpers"; import { getEpochPeriod } from "consts/bridgeRoutes"; +import { initialize as initializeEmitter } from "utils/logger"; +import { BotEvents } from "utils/botEvents"; -export async function start(shutdownManager: ShutdownManager = new ShutdownManager()) { - const network = "testnet"; - const chainId = parseInt(process.env.VEAOUTBOX_CHAIN_ID); +interface RelayerConfig { + chainId: number; + network: string; + shutdownManager: ShutdownManager; + emitter: EventEmitter; +} + +/** + * Start the relayer + * @param chainId The chain id of the relayer + * @param network The network of the relayer + * @param shutdownManager The shutdown manager + * @param emitter The event emitter + */ +export async function start({ chainId, network, shutdownManager, emitter }: RelayerConfig) { + initializeEmitter(emitter); + emitter.emit(BotEvents.STARTED, chainId, network); const epochPeriod = getEpochPeriod(chainId); const maxBatchSize = 10; // 10 messages per batch - await setupExitHandlers(chainId, shutdownManager, network); + await setupExitHandlers(chainId, shutdownManager, network, emitter); while (!shutdownManager.getIsShuttingDown()) { - let nonce = await initialize(chainId, network); + let nonce = await initializeNonce(chainId, network, emitter); const relayBatchDeps: RelayBatchDeps = { chainId, nonce, maxBatchSize, }; nonce = await relayBatch(relayBatchDeps); - if (nonce != null) await updateStateFile(chainId, Math.floor(Date.now() / 1000), nonce, network); + if (nonce != null) await updateStateFile(chainId, Math.floor(Date.now() / 1000), nonce, network, emitter); const currentTS = Math.floor(Date.now() / 1000); const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000; - console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds"); + emitter.emit(BotEvents.WAITING, delayAmount); await delay(delayAmount); } } if (require.main === module) { + const emitter = new EventEmitter(); const shutdownManager = new ShutdownManager(false); - start(shutdownManager); + const testnetRelayerConfig: RelayerConfig = { + shutdownManager, + emitter, + chainId: Number(process.env.VEAOUTBOX_CHAIN_ID), + network: "testnet", + }; + + start(testnetRelayerConfig); } diff --git a/relayer-cli/src/utils/botEvents.ts b/relayer-cli/src/utils/botEvents.ts new file mode 100644 index 00000000..566509e7 --- /dev/null +++ b/relayer-cli/src/utils/botEvents.ts @@ -0,0 +1,15 @@ +export enum BotEvents { + // Bridger state + STARTED = "started", + WAITING = "waiting", + EXIT = "exit", + + // Bot health + EXCEPTION = "exception", + PROMISE_REJECTION = "promise_rejection", + + // Lock file + LOCK_CLAIMED = "lock_claimed", + LOCK_DIRECTORY = "lock_directory", + LOCK_RELEASED = "lock_released", +} diff --git a/relayer-cli/src/utils/logger.ts b/relayer-cli/src/utils/logger.ts new file mode 100644 index 00000000..aa860940 --- /dev/null +++ b/relayer-cli/src/utils/logger.ts @@ -0,0 +1,49 @@ +import { EventEmitter } from "node:events"; +import { BotEvents } from "./botEvents"; + +/** + * Listens to relevant events of an EventEmitter instance and issues log lines + * + * @param emitter - The event emitter instance that issues the relevant events + * + * @example + * + * const emitter = new EventEmitter(); + * initialize(emitter); + */ + +export const initialize = (emitter: EventEmitter) => { + return configurableInitialize(emitter); +}; + +export const configurableInitialize = (emitter: EventEmitter) => { + // Relayer state logs + emitter.on(BotEvents.STARTED, (chainId, network) => { + console.log(`Relayer started for ${chainId} on ${network}`); + }); + emitter.on(BotEvents.WAITING, (delayAmount) => { + console.log(`Waiting for next epoch: ${delayAmount} ms`); + }); + emitter.on(BotEvents.EXIT, () => { + console.log("Exiting"); + }); + + // Bot health logs + emitter.on(BotEvents.EXCEPTION, (err) => { + console.error("Uncaught Exception occurred", err); + }); + emitter.on(BotEvents.PROMISE_REJECTION, (reason, promise) => { + console.error("Unhandled promise rejection:", reason, "at", promise); + }); + + // Lock file logs + emitter.on(BotEvents.LOCK_CLAIMED, () => { + console.log("Lock claimed"); + }); + emitter.on(BotEvents.LOCK_DIRECTORY, (pwd) => { + console.log(`Lock file directory: ${pwd}`); + }); + emitter.on(BotEvents.LOCK_RELEASED, () => { + console.log("Lock released"); + }); +}; diff --git a/relayer-cli/src/utils/relay.test.ts b/relayer-cli/src/utils/relay.test.ts index 0ae6d551..51b7b906 100644 --- a/relayer-cli/src/utils/relay.test.ts +++ b/relayer-cli/src/utils/relay.test.ts @@ -1,4 +1,3 @@ -import { abi } from "web3/lib/commonjs/eth.exports"; import { relayBatch } from "./relay"; describe("relay", () => { diff --git a/relayer-cli/src/utils/relay.ts b/relayer-cli/src/utils/relay.ts index 0fb96e12..d4eabd82 100644 --- a/relayer-cli/src/utils/relay.ts +++ b/relayer-cli/src/utils/relay.ts @@ -11,6 +11,7 @@ import { getBridgeConfig, getInboxSubgraph } from "../consts/bridgeRoutes"; * Get the count of the veaOutbox * @param veaOutbox The veaOutbox contract instance * @param chainId The chain id of the veaOutbox chain + * @returns The count of the veaOutbox */ const getCount = async (veaOutbox: VeaOutboxArbToEth | VeaOutboxArbToGnosis, chainId: number): Promise => { const subgraph = getInboxSubgraph(chainId); @@ -34,6 +35,7 @@ const getCount = async (veaOutbox: VeaOutboxArbToEth | VeaOutboxArbToGnosis, cha * Relay a message from the veaOutbox * @param chainId The chain id of the veaOutbox chain * @param nonce The nonce of the message + * @returns The transaction receipt */ const relay = async (chainId: number, nonce: number) => { const routeParams = getBridgeConfig(chainId); @@ -68,6 +70,8 @@ interface RelayBatchDeps { * @param chainId The chain id of the veaOutbox chain * @param nonce The nonce of the message * @param maxBatchSize The maximum number of messages to relay in a single batch + * + * @returns The nonce of the last message relayed */ const relayBatch = async ({ chainId, @@ -130,6 +134,7 @@ const relayBatch = async ({ * @param chainId The chain id of the veaOutbox chain * @param nonce The nonce of the first message to relay * @param msgSender The address of the sender + * @returns The nonce of the last message relayed */ const relayAllFrom = async (chainId: number, nonce: number, msgSender: string): Promise => { const routeParams = getBridgeConfig(chainId); @@ -175,6 +180,7 @@ const relayAllFrom = async (chainId: number, nonce: number, msgSender: string): * @param chainId The chain id of the veaOutbox chain * @param nonce The nonce of the first message to relay * @param msgSender The address of the sender + * @returns The nonces of the messages sent by the sender */ const getNonceFrom = async (chainId: number, nonce: number, msgSender: string) => { const subgraph = getInboxSubgraph(chainId); diff --git a/relayer-cli/src/utils/relayerHelpers.test.ts b/relayer-cli/src/utils/relayerHelpers.test.ts index 9a5671a7..c21c5301 100644 --- a/relayer-cli/src/utils/relayerHelpers.test.ts +++ b/relayer-cli/src/utils/relayerHelpers.test.ts @@ -1,6 +1,8 @@ +import EventEmitter from "events"; import { initialize, updateStateFile } from "./relayerHelpers"; describe("relayerHelpers", () => { + const emitter = new EventEmitter(); const chainId = 1; const network = "testing"; const claimLock = jest.fn(); @@ -21,15 +23,29 @@ describe("relayerHelpers", () => { it("should claimLock and create a state file if it doesn't exist", async () => { fileSystem.existsSync.mockReturnValue(false); fileSystem.readFileSync.mockReturnValue('{"nonce":0}'); - const nonce = await initialize(chainId, network, claimLock, mockUpdateStateFile, fileSystem as any); + const nonce = await initialize( + chainId, + network, + emitter as any, + claimLock, + mockUpdateStateFile, + fileSystem as any + ); expect(claimLock).toHaveBeenCalledWith(network, chainId); - expect(mockUpdateStateFile).toHaveBeenCalledWith(chainId, expect.any(Number), 0, network); + expect(mockUpdateStateFile).toHaveBeenCalledWith(chainId, expect.any(Number), 0, network, emitter); expect(nonce).toBe(0); }); it("should claimLock and return nonce from existing state file", async () => { fileSystem.existsSync.mockReturnValue(true); fileSystem.readFileSync.mockReturnValue('{"nonce":10}'); - const nonce = await initialize(chainId, network, claimLock, mockUpdateStateFile, fileSystem as any); + const nonce = await initialize( + chainId, + network, + emitter as any, + claimLock, + mockUpdateStateFile, + fileSystem as any + ); expect(claimLock).toHaveBeenCalledWith(network, chainId); expect(mockUpdateStateFile).not.toHaveBeenCalled(); expect(nonce).toBe(10); @@ -40,7 +56,7 @@ describe("relayerHelpers", () => { it("should write a state file with the provided nonce", async () => { const createdTimestamp = 123456; const fileDirectory = "./state/testing_1.json"; - await updateStateFile(chainId, createdTimestamp, 10, network, fileSystem as any, releaseLock); + await updateStateFile(chainId, createdTimestamp, 10, network, emitter as any, fileSystem as any, releaseLock); expect(fileSystem.writeFileSync).toHaveBeenCalledWith( fileDirectory, JSON.stringify({ ts: createdTimestamp, nonce: 10 }), diff --git a/relayer-cli/src/utils/relayerHelpers.ts b/relayer-cli/src/utils/relayerHelpers.ts index f0d5f53c..2cfd71e4 100644 --- a/relayer-cli/src/utils/relayerHelpers.ts +++ b/relayer-cli/src/utils/relayerHelpers.ts @@ -1,26 +1,36 @@ import * as fs from "fs"; +import { EventEmitter } from "events"; import { claimLock, releaseLock } from "./lock"; import ShutdownManager from "./shutdownManager"; +import { BotEvents } from "./botEvents"; +/** + * Initialize the relayer by claiming the lock and reading the nonce from the state file. + * If the state file does not exist, it will be created with the current timestamp and nonce 0. + * + * @param chainId Chain ID of the relayer + * @param network Network name of the relayer (e.g. "testnet") + */ async function initialize( chainId: number, network: string, + emitter: EventEmitter, setLock: typeof claimLock = claimLock, syncStateFile: typeof updateStateFile = updateStateFile, fileSystem: typeof fs = fs ): Promise { setLock(network, chainId); - console.log("lock claimed"); + emitter.emit(BotEvents.LOCK_CLAIMED); // STATE_DIR is absolute path of the directory where the state files are stored // STATE_DIR must have trailing slash const state_file = process.env.STATE_DIR + network + "_" + chainId + ".json"; if (!fileSystem.existsSync(state_file)) { // No state file so initialize starting now const tsnow = Math.floor(Date.now() / 1000); - await syncStateFile(chainId, tsnow, 0, network); + await syncStateFile(chainId, tsnow, 0, network, emitter); } // print pwd for debugging - console.log(process.cwd()); + emitter.emit(BotEvents.LOCK_DIRECTORY, process.cwd()); const chain_state_raw = fileSystem.readFileSync(state_file, { encoding: "utf8" }); const chain_state = JSON.parse(chain_state_raw); @@ -37,6 +47,7 @@ async function updateStateFile( createdTimestamp: number, nonceFrom: number, network: string, + emitter: EventEmitter, fileSystem: typeof fs = fs, removeLock: typeof releaseLock = releaseLock ) { @@ -48,11 +59,17 @@ async function updateStateFile( fileSystem.writeFileSync(chain_state_file, JSON.stringify(json), { encoding: "utf8" }); removeLock(network, chainId); + emitter.emit(BotEvents.LOCK_RELEASED); } -async function setupExitHandlers(chainId: number, shutdownManager: ShutdownManager, network: string) { +async function setupExitHandlers( + chainId: number, + shutdownManager: ShutdownManager, + network: string, + emitter: EventEmitter +) { const cleanup = async () => { - console.log("exit"); + emitter.emit(BotEvents.EXIT); const lockFileName = "./state/" + network + "_" + chainId + ".pid"; if (fs.existsSync(lockFileName)) { await fs.promises.unlink(lockFileName); @@ -75,12 +92,12 @@ async function setupExitHandlers(chainId: number, shutdownManager: ShutdownManag }); process.on("uncaughtException", async (err) => { - console.error("Uncaught exception:", err); + emitter.emit(BotEvents.EXCEPTION, err); await handleExit(1); }); process.on("unhandledRejection", async (reason, promise) => { - console.error("Unhandled promise rejection:", reason, "at", promise); + emitter.emit(BotEvents.PROMISE_REJECTION, reason, promise); await handleExit(1); }); } diff --git a/relayer-cli/state/testnet_11155111.json b/relayer-cli/state/testnet_11155111.json new file mode 100644 index 00000000..120b3930 --- /dev/null +++ b/relayer-cli/state/testnet_11155111.json @@ -0,0 +1,4 @@ +{ + "ts": 1736856288, + "nonce": 10 +} From b9c20633f35beb80472b1b4cc47a790b3f3a47c9 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Wed, 15 Jan 2025 12:26:12 +0530 Subject: [PATCH 4/9] feat: static msg execution before batching --- relayer-cli/src/utils/relay.test.ts | 48 ++++++++++++++++++++++++++++- relayer-cli/src/utils/relay.ts | 6 ++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/relayer-cli/src/utils/relay.test.ts b/relayer-cli/src/utils/relay.test.ts index 51b7b906..9b45012f 100644 --- a/relayer-cli/src/utils/relay.test.ts +++ b/relayer-cli/src/utils/relay.test.ts @@ -32,7 +32,9 @@ describe("relay", () => { eth: { Contract: jest.fn().mockReturnValue({ methods: { - sendMessage: jest.fn(), + sendMessage: jest.fn().mockReturnValue({ + call: jest.fn(), + }), }, options: { address: veaOutboxAddress, @@ -181,5 +183,49 @@ describe("relay", () => { expect(mockBatchedSend).toHaveBeenCalledWith(batchCall); expect(updatedNonce).toBe(3); }); + it("should not relay messages that fail to execute", async () => { + fetchCount.mockReturnValue(3); + web3.mockReturnValue({ + eth: { + Contract: jest.fn().mockReturnValue({ + methods: { + sendMessage: jest.fn().mockReturnValue({ + call: jest.fn().mockResolvedValueOnce(Promise.reject("Error")).mockResolvedValueOnce(Promise.resolve()), + }), + }, + options: { + address: veaOutboxAddress, + }, + }), + }, + }); + const updatedNonce = await relayBatch({ + chainId, + nonce, + maxBatchSize, + fetchBridgeConfig, + fetchCount, + setBatchedSend, + fetchVeaOutbox, + fetchProofAtCount, + fetchMessageDataToRelay, + web3, + }); + expect(mockBatchedSend).toHaveBeenCalledTimes(1); + const batchCall = [ + { + args: [[], 1, "to", "data"], + method: expect.any(Function), + to: veaOutboxAddress, + }, + { + args: [[], 2, "to", "data"], + method: expect.any(Function), + to: veaOutboxAddress, + }, + ]; + expect(mockBatchedSend).toHaveBeenCalledWith(batchCall); + expect(updatedNonce).toBe(3); + }); }); }); diff --git a/relayer-cli/src/utils/relay.ts b/relayer-cli/src/utils/relay.ts index d4eabd82..e8a05782 100644 --- a/relayer-cli/src/utils/relay.ts +++ b/relayer-cli/src/utils/relay.ts @@ -114,6 +114,12 @@ const relayBatch = async ({ fetchProofAtCount(chainId, nonce, count), fetchMessageDataToRelay(chainId, nonce), ]); + try { + await veaOutboxInstance.methods.sendMessage(proof, nonce, to, data).call(); + } catch { + nonce++; + continue; + } txns.push({ args: [proof, nonce, to, data], method: veaOutboxInstance.methods.sendMessage, From 9a19159c0d230668799aeadfa507d9532add7ebf Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Wed, 15 Jan 2025 12:27:22 +0530 Subject: [PATCH 5/9] feat(devnet): logger --- relayer-cli/src/devnetRelayExample.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/relayer-cli/src/devnetRelayExample.ts b/relayer-cli/src/devnetRelayExample.ts index 555cc126..2ee043aa 100644 --- a/relayer-cli/src/devnetRelayExample.ts +++ b/relayer-cli/src/devnetRelayExample.ts @@ -1,26 +1,29 @@ +import { EventEmitter } from "events"; import { relayAllFrom } from "./utils/relay"; import { initialize, ShutdownManager, updateStateFile, setupExitHandlers, delay } from "./utils/relayerHelpers"; +import { BotEvents } from "utils/botEvents"; -export async function start(shutdownManager: ShutdownManager = new ShutdownManager()) { +export async function start(shutdownManager: ShutdownManager = new ShutdownManager(), emitter: EventEmitter) { const chainId = parseInt(process.env.VEAOUTBOX_CHAIN_ID); const epochPeriod = 1800; // 30 min const network = "devnet"; - await setupExitHandlers(chainId, shutdownManager, network); + await setupExitHandlers(chainId, shutdownManager, network, emitter); while (!shutdownManager.getIsShuttingDown()) { - let nonce = await initialize(chainId, network); + let nonce = await initialize(chainId, network, emitter); // This is libghtbulb switch address in arbitrum sepolia const sender = process.env.DEVNET_SENDER; nonce = await relayAllFrom(chainId, nonce, sender); - if (nonce != null) await updateStateFile(chainId, Math.floor(Date.now() / 1000), nonce, network); + if (nonce != null) await updateStateFile(chainId, Math.floor(Date.now() / 1000), nonce, network, emitter); const currentTS = Math.floor(Date.now() / 1000); const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000; - console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds"); + emitter.emit(BotEvents.WAITING, delayAmount); await delay(delayAmount); } } if (require.main === module) { + const emitter = new EventEmitter(); const shutdownManager = new ShutdownManager(false); - start(shutdownManager); + start(shutdownManager, emitter); } From b6c735a02a9c23366b591c04705321e772ed6047 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Wed, 15 Jan 2025 12:27:58 +0530 Subject: [PATCH 6/9] fix: web3 dep --- relayer-cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer-cli/package.json b/relayer-cli/package.json index 39f1c2a0..19db6e6a 100644 --- a/relayer-cli/package.json +++ b/relayer-cli/package.json @@ -20,7 +20,7 @@ "dotenv": "^16.4.5", "pm2": "^5.2.2", "typescript": "^4.9.5", - "web3": "^4.16.0", + "web3": "^1.10.4", "web3-batched-send": "^1.0.3" }, "devDependencies": { From 47900e95abbeb28eff4fe4e1dd7215e138dfd279 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Wed, 15 Jan 2025 13:12:10 +0530 Subject: [PATCH 7/9] fix: chain state file dir --- relayer-cli/src/utils/relayerHelpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/relayer-cli/src/utils/relayerHelpers.ts b/relayer-cli/src/utils/relayerHelpers.ts index 2cfd71e4..28a887ef 100644 --- a/relayer-cli/src/utils/relayerHelpers.ts +++ b/relayer-cli/src/utils/relayerHelpers.ts @@ -51,7 +51,7 @@ async function updateStateFile( fileSystem: typeof fs = fs, removeLock: typeof releaseLock = releaseLock ) { - const chain_state_file = "./state/" + network + "_" + chainId + ".json"; + const chain_state_file = process.env.STATE_DIR + network + "_" + chainId + ".json"; const json = { ts: createdTimestamp, nonce: nonceFrom, @@ -70,7 +70,7 @@ async function setupExitHandlers( ) { const cleanup = async () => { emitter.emit(BotEvents.EXIT); - const lockFileName = "./state/" + network + "_" + chainId + ".pid"; + const lockFileName = process.env.STATE_DIR + network + "_" + chainId + ".pid"; if (fs.existsSync(lockFileName)) { await fs.promises.unlink(lockFileName); } From 4da75bff91c174834bc5433e1a851c70e0f00ad7 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 27 Jan 2025 17:50:10 +0530 Subject: [PATCH 8/9] fix: jsdoc comments --- relayer-cli/src/testnetRelayer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/relayer-cli/src/testnetRelayer.ts b/relayer-cli/src/testnetRelayer.ts index 6708894e..81ff016a 100644 --- a/relayer-cli/src/testnetRelayer.ts +++ b/relayer-cli/src/testnetRelayer.ts @@ -21,10 +21,10 @@ interface RelayerConfig { /** * Start the relayer - * @param chainId The chain id of the relayer - * @param network The network of the relayer - * @param shutdownManager The shutdown manager - * @param emitter The event emitter + * @param config.chainId The chain id of the veaOutbox chain + * @param config.network The network of the veaOutbox chain + * @param config.shutdownManager The shutdown manager + * @param config.emitter The event emitter */ export async function start({ chainId, network, shutdownManager, emitter }: RelayerConfig) { initializeEmitter(emitter); From 160930069ef5f812b1d3a5f9327165276d7d4e92 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 27 Jan 2025 17:50:45 +0530 Subject: [PATCH 9/9] fix: state file dir --- relayer-cli/src/utils/relayerHelpers.test.ts | 2 +- relayer-cli/src/utils/relayerHelpers.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/relayer-cli/src/utils/relayerHelpers.test.ts b/relayer-cli/src/utils/relayerHelpers.test.ts index c21c5301..f788077e 100644 --- a/relayer-cli/src/utils/relayerHelpers.test.ts +++ b/relayer-cli/src/utils/relayerHelpers.test.ts @@ -55,7 +55,7 @@ describe("relayerHelpers", () => { describe("updateStateFile", () => { it("should write a state file with the provided nonce", async () => { const createdTimestamp = 123456; - const fileDirectory = "./state/testing_1.json"; + const fileDirectory = process.env.STATE_DIR + network + "_" + chainId + ".json"; await updateStateFile(chainId, createdTimestamp, 10, network, emitter as any, fileSystem as any, releaseLock); expect(fileSystem.writeFileSync).toHaveBeenCalledWith( fileDirectory, diff --git a/relayer-cli/src/utils/relayerHelpers.ts b/relayer-cli/src/utils/relayerHelpers.ts index 28a887ef..9007165b 100644 --- a/relayer-cli/src/utils/relayerHelpers.ts +++ b/relayer-cli/src/utils/relayerHelpers.ts @@ -3,6 +3,7 @@ import { EventEmitter } from "events"; import { claimLock, releaseLock } from "./lock"; import ShutdownManager from "./shutdownManager"; import { BotEvents } from "./botEvents"; +require("dotenv").config(); /** * Initialize the relayer by claiming the lock and reading the nonce from the state file. @@ -51,6 +52,7 @@ async function updateStateFile( fileSystem: typeof fs = fs, removeLock: typeof releaseLock = releaseLock ) { + console.log(process.env.STATE_DIR); const chain_state_file = process.env.STATE_DIR + network + "_" + chainId + ".json"; const json = { ts: createdTimestamp,