-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/relayer tests #395
base: dev
Are you sure you want to change the base?
Feat/relayer tests #395
Conversation
WalkthroughThe pull request introduces significant modifications to the relayer CLI project, focusing on enhancing modularity and adopting an event-driven architecture. Key changes include the introduction of an Changes
Sequence DiagramsequenceDiagram
participant Relayer
participant EventEmitter
participant Logger
participant StateManager
Relayer->>EventEmitter: Initialize
EventEmitter-->>Logger: Register Event Listeners
Relayer->>StateManager: Start Relaying
StateManager-->>EventEmitter: Emit STARTED Event
EventEmitter-->>Logger: Log Start Event
Relayer->>Relayer: Relay Batches
alt Waiting
EventEmitter->>Logger: Emit WAITING Event
end
alt Exit
EventEmitter->>Logger: Emit EXIT Event
end
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
❌ Deploy Preview for veascan failed. Why did it fail? →
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🔭 Outside diff range comments (4)
relayer-cli/src/utils/proof.ts (3)
Line range hint
10-31
: Improve error handling in getMessageDataToRelayThe function has several issues that need attention:
- The error is only logged, not propagated
- No validation for empty results
- Potential undefined return if error occurs
const getMessageDataToRelay = async (chainId: number, nonce: number, requestGraph: typeof request = request) => { try { const subgraph = getInboxSubgraph(chainId); const result = await requestGraph( `https://api.studio.thegraph.com/query/${subgraph}`, `{ messageSents(first: 5, where: {nonce: ${nonce}}) { nonce to { id } data } }` ); + if (!result?.messageSents?.[0]) { + throw new Error(`No message found for nonce ${nonce}`); + } return [result[`messageSents`][0].to.id, result[`messageSents`][0].data]; } catch (e) { - console.log(e); + throw new Error(`Failed to get message data: ${e.message}`); } };
Line range hint
40-71
: Improve error handling and query construction in getProofAtCountSeveral improvements needed:
- Error handling only returns empty array
- Query string construction could be vulnerable to injection
- No validation for subgraph response
const getProofAtCount = async ( chainId: number, nonce: number, count: number, requestGraph: typeof request = request, calculateProofIndices: typeof getProofIndices = getProofIndices, fetchInboxSubgraph: typeof getInboxSubgraph = getInboxSubgraph ): Promise<string[]> => { const proofIndices = calculateProofIndices(nonce, count); if (proofIndices.length == 0) return []; - let query = "{"; - for (let i = 0; i < proofIndices.length; i++) { - query += `layer${i}: nodes(first: 1, where: {id: "${proofIndices[i]}"}) { - hash - }`; - } - query += "}"; + const query = `{ + ${proofIndices.map((index, i) => ` + layer${i}: nodes(first: 1, where: {id: ${JSON.stringify(index)}}) { + hash + } + `).join('')} + }`; try { const subgraph = fetchInboxSubgraph(chainId); const result = await requestGraph(`https://api.studio.thegraph.com/query/${subgraph}`, query); + if (!result) { + throw new Error('No result from subgraph'); + } + const proof = []; for (let i = 0; i < proofIndices.length; i++) { + if (!result[`layer${i}`]?.[0]?.hash) { + throw new Error(`Missing hash for layer ${i}`); + } proof.push(result[`layer${i}`][0].hash); } return proof; } catch (e) { - console.log(e); - return []; + throw new Error(`Failed to get proof: ${e.message}`); } };
Line range hint
81-100
: Add input validation to getProofIndicesThe function needs input validation to handle invalid inputs gracefully.
const getProofIndices = (nonce: number, count: number) => { + if (nonce < 0 || count < 0) { + throw new Error('Nonce and count must be non-negative'); + } + let proof = []; if (nonce >= count) return proof; const treeDepth = Math.ceil(Math.log2(count));relayer-cli/src/utils/relayerHelpers.ts (1)
Line range hint
73-76
: [Critical Issue] Inconsistent use of lock file directory path.In
setupExitHandlers
,lockFileName
is constructed with a hard-coded"./state/"
directory, whereas in other parts of the code,process.env.STATE_DIR
is used. This inconsistency can cause the cleanup process to fail to remove the lock file from the correct directory.Apply this diff to fix the inconsistency:
- const lockFileName = "./state/" + network + "_" + chainId + ".pid"; + const lockFileName = process.env.STATE_DIR + network + "_" + chainId + ".pid";
🧹 Nitpick comments (14)
relayer-cli/src/utils/proof.test.ts (2)
14-14
: Fix typo in test descriptionThere's a missing parenthesis in the test description.
- it("should return the proof indices(for a large count", () => { + it("should return the proof indices (for a large count)", () => {
5-18
: Enhance test coverage with edge casesWhile the current tests cover basic scenarios, consider adding tests for:
- Edge cases: count = 0, nonce = 0
- Invalid inputs: negative numbers
- Boundary conditions: nonce > count
relayer-cli/src/utils/proof.ts (1)
Line range hint
14-31
: Extract GraphQL endpoint to configurationThe GraphQL endpoint URL is hardcoded in multiple places. Consider extracting it to a configuration constant.
+const GRAPH_API_ENDPOINT = 'https://api.studio.thegraph.com/query'; + const getMessageDataToRelay = async (...) => { try { const subgraph = getInboxSubgraph(chainId); - const result = await requestGraph( - `https://api.studio.thegraph.com/query/${subgraph}`, + const result = await requestGraph( + `${GRAPH_API_ENDPOINT}/${subgraph}`,Also applies to: 62-62
relayer-cli/src/testnetRelayer.ts (1)
2-2
: Ensure consistent import ofEventEmitter
In this file,
EventEmitter
is imported from"node:events"
, whereas in other files it's imported from"events"
. Consider using the same import statement across all files for consistency. Using the standard"events"
module is generally preferred unless you have a specific reason to import from"node:events"
.Apply this diff to adjust the import statement:
- import { EventEmitter } from "node:events"; + import { EventEmitter } from "events";relayer-cli/src/utils/botEvents.ts (1)
1-15
: Ensure consistent naming conventions for enum membersThe
BotEvents
enum members use uppercase (e.g.,STARTED
,WAITING
), but the assigned string values are lowercase with underscores (e.g.,"started"
,"promise_rejection"
). For consistency and clarity, consider using a consistent naming convention for enum members and their string values. For example, you could use uppercase with underscores for both enum keys and values, or usecamelCase
, depending on your project's style guide.Apply this diff to adjust the enum values:
export enum BotEvents { // Bridger state - STARTED = "started", - WAITING = "waiting", - EXIT = "exit", + STARTED = "STARTED", + WAITING = "WAITING", + EXIT = "EXIT", // Bot health - EXCEPTION = "exception", - PROMISE_REJECTION = "promise_rejection", + EXCEPTION = "EXCEPTION", + PROMISE_REJECTION = "PROMISE_REJECTION", // Lock file - LOCK_CLAIMED = "lock_claimed", - LOCK_DIRECTORY = "lock_directory", - LOCK_RELEASED = "lock_released", + LOCK_CLAIMED = "LOCK_CLAIMED", + LOCK_DIRECTORY = "LOCK_DIRECTORY", + LOCK_RELEASED = "LOCK_RELEASED", }relayer-cli/src/devnetRelayExample.ts (2)
1-1
: Ensure consistent import ofEventEmitter
In this file,
EventEmitter
is imported from"events"
, whereas in other files it's imported from"node:events"
. Consider using the same import statement across all files for consistency. Using the standard"events"
module is generally preferred unless there's a specific reason to import from"node:events"
.Apply this diff to adjust the import statement:
- import { EventEmitter } from "events"; + import { EventEmitter } from "node:events";
6-6
: Add JSDoc comments to thestart
functionConsider adding JSDoc comments to the
start
function to document its purpose and parameters (shutdownManager
,emitter
). This enhances code readability and helps other developers understand how to use the function.Apply this diff to add JSDoc comments:
+ /** + * Starts the devnet relayer example. + * @param shutdownManager Manages graceful shutdowns. + * @param emitter Event emitter for logging and event handling. + */ export async function start(shutdownManager: ShutdownManager = new ShutdownManager(), emitter: EventEmitter) {relayer-cli/src/utils/logger.ts (2)
15-18
: Consider removing redundant initialize function.The
initialize
function simply forwards toconfigurableInitialize
without adding any value. Consider removing it and usingconfigurableInitialize
directly.-export const initialize = (emitter: EventEmitter) => { - return configurableInitialize(emitter); -};
31-37
: Enhance error logging for uncaught exceptions.The error logging for exceptions could be more detailed to aid in debugging. Consider including stack traces and error codes if available.
- emitter.on(BotEvents.EXCEPTION, (err) => { - console.error("Uncaught Exception occurred", err); - }); + emitter.on(BotEvents.EXCEPTION, (err) => { + console.error("Uncaught Exception occurred:", { + message: err.message, + stack: err.stack, + code: err.code + }); + });relayer-cli/src/utils/relayerHelpers.test.ts (1)
69-77
: Add descriptions for TODO tests.The
setupExitHandlers
test cases are marked as TODO without descriptions. Consider adding detailed descriptions of what each test should verify.- it.todo("should register signal handlers for SIGINT, SIGTERM, and SIGQUIT"); + it.todo("should register signal handlers for SIGINT, SIGTERM, and SIGQUIT and verify they are properly attached to process.on()"); - it.todo("should trigger shutdown and cleanup on SIGINT signal"); + it.todo("should trigger shutdown and cleanup on SIGINT signal and verify all resources are properly released");relayer-cli/src/utils/relay.test.ts (2)
186-229
: Enhance error case testing.The error handling test could be more comprehensive. Consider testing different types of errors and retry scenarios.
Add test cases for:
- Network errors
- Timeout errors
- Invalid message format
- Retry mechanism
96-120
: Add memory usage tests for large batches.Consider adding tests to verify memory usage remains stable with large batch sizes.
Add a test case that:
- Processes a very large number of messages
- Monitors memory usage
- Verifies no memory leaks occur
relayer-cli/src/utils/relayerHelpers.ts (2)
59-59
: Ensure the target directory exists before writing the state file.When writing the state file, if the target directory does not exist,
fileSystem.writeFileSync
will throw an error. To prevent this, ensure that the directory exists before attempting to write the file.Apply this diff to check and create the directory if necessary:
+ const dir = path.dirname(chain_state_file); + if (!fileSystem.existsSync(dir)) { + fileSystem.mkdirSync(dir, { recursive: true }); + } fileSystem.writeFileSync(chain_state_file, JSON.stringify(json), { encoding: "utf8" });Remember to import the
path
module:+ import * as path from "path";
65-70
: InjectfileSystem
intosetupExitHandlers
for consistency and testability.In other functions like
initialize
andupdateStateFile
,fileSystem
is injected as a parameter to allow for easier mocking and testing. For consistency and improved testability, consider injectingfileSystem
intosetupExitHandlers
as well, instead of directly importing and usingfs
within the function.Apply this diff to update the function signature and usage:
async function setupExitHandlers( chainId: number, shutdownManager: ShutdownManager, network: string, emitter: EventEmitter, + fileSystem: typeof fs = fs ) {
Update references to
fs
within the function to usefileSystem
instead.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
relayer-cli/package.json
(1 hunks)relayer-cli/src/devnetRelayExample.ts
(1 hunks)relayer-cli/src/testnetRelayer.ts
(1 hunks)relayer-cli/src/utils/botEvents.ts
(1 hunks)relayer-cli/src/utils/ethers.ts
(1 hunks)relayer-cli/src/utils/logger.ts
(1 hunks)relayer-cli/src/utils/proof.test.ts
(1 hunks)relayer-cli/src/utils/proof.ts
(4 hunks)relayer-cli/src/utils/relay.test.ts
(1 hunks)relayer-cli/src/utils/relay.ts
(7 hunks)relayer-cli/src/utils/relayerHelpers.test.ts
(1 hunks)relayer-cli/src/utils/relayerHelpers.ts
(3 hunks)relayer-cli/state/testnet_11155111.json
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- relayer-cli/state/testnet_11155111.json
- relayer-cli/src/utils/ethers.ts
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: test
- GitHub Check: Scorecard analysis
- GitHub Check: dependency-review
- GitHub Check: Analyze (javascript)
🔇 Additional comments (4)
relayer-cli/src/utils/proof.ts (1)
4-9
: Great job on JSDoc comments!The JSDoc comments are well-written and provide clear documentation for the functions.
Also applies to: 33-39, 75-80
relayer-cli/src/utils/botEvents.ts (1)
1-15
: Good addition ofBotEvents
enumThe introduction of the
BotEvents
enum provides a centralized and type-safe way to manage event strings, improving code maintainability and reducing the risk of typos in event names.relayer-cli/package.json (1)
23-23
:⚠️ Potential issueSignificant downgrade of web3.js requires attention.
The change downgrades web3.js from v4.16.0 to v1.10.4, which is a major version downgrade that:
- Removes access to newer Web3.js features and improvements
- May impact security fixes available in v4.x.x
- Could affect compatibility with Node.js 18+ requirement
Please run this script to check compatibility and security implications:
Could you please:
- Document the reason for this downgrade in the PR description?
- Verify if this is related to compatibility with [email protected]?
- Consider using a more recent version if possible, or create a tracking issue for future upgrade?
relayer-cli/src/utils/relayerHelpers.ts (1)
22-23
: LGTM: Proper use of dependency injection and event emission.The
initialize
function correctly uses dependency injection forsetLock
and emits theLOCK_CLAIMED
event after acquiring the lock.
chainId: Number(process.env.VEAOUTBOX_CHAIN_ID), | ||
network: "testnet", | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verify that VEAOUTBOX_CHAIN_ID
environment variable is defined
You're using Number(process.env.VEAOUTBOX_CHAIN_ID)
to set chainId
. If VEAOUTBOX_CHAIN_ID
is undefined
, Number(undefined)
results in NaN
, which could cause issues downstream. Consider adding a check to ensure that process.env.VEAOUTBOX_CHAIN_ID
is defined and handle the case where it is not.
Apply this diff to add a check for the environment variable:
+ if (!process.env.VEAOUTBOX_CHAIN_ID) {
+ throw new Error('VEAOUTBOX_CHAIN_ID environment variable is not defined');
+ }
const testnetRelayerConfig: RelayerConfig = {
shutdownManager,
emitter,
chainId: Number(process.env.VEAOUTBOX_CHAIN_ID),
network: "testnet",
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
chainId: Number(process.env.VEAOUTBOX_CHAIN_ID), | |
network: "testnet", | |
}; | |
if (!process.env.VEAOUTBOX_CHAIN_ID) { | |
throw new Error('VEAOUTBOX_CHAIN_ID environment variable is not defined'); | |
} | |
const testnetRelayerConfig: RelayerConfig = { | |
shutdownManager, | |
emitter, | |
chainId: Number(process.env.VEAOUTBOX_CHAIN_ID), | |
network: "testnet", | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's already an issue created for env validation and it will be addressed there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar Understood, thanks for letting me know that environment variable validation will be handled separately.
relayer-cli/src/testnetRelayer.ts
Outdated
/** | ||
* 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 | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Update JSDoc comments to reflect the new function signature
The start
function now accepts a single RelayerConfig
object as a parameter, but the JSDoc comments list individual parameters (chainId
, network
, shutdownManager
, emitter
). Please update the JSDoc to accurately describe the RelayerConfig
parameter and its properties.
Apply this diff to update the JSDoc comments:
/**
* 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 The relayer configuration
+ * @param config.chainId The chain id of the relayer
+ * @param config.network The network of the relayer
+ * @param config.shutdownManager The shutdown manager
+ * @param config.emitter The event emitter
*/
export async function start({ chainId, network, shutdownManager, emitter }: RelayerConfig) {
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
/** | |
* 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 | |
*/ | |
/** | |
* Start the relayer | |
* @param config The relayer configuration | |
* @param config.chainId The chain id of the relayer | |
* @param config.network The network of the relayer | |
* @param config.shutdownManager The shutdown manager | |
* @param config.emitter The event emitter | |
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think its needed, the comments and implementation is clear enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar Understood, I'll retract the suggestion. The TypeScript types and current documentation are indeed clear enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar the bot is right, the correct way of documenting object properties is with the dot notation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
const epochPeriod = 1800; // 30 min | ||
const network = "devnet"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verify environment variables are defined before use
You're using process.env.VEAOUTBOX_CHAIN_ID
to set chainId
. If this environment variable is undefined
, parseInt(undefined)
results in NaN
, which could cause issues. Similarly, sender
is set from process.env.DEVNET_SENDER
. Consider adding checks to ensure these environment variables are defined and handle the case where they are not.
Apply this diff to add checks for the environment variables:
+ if (!process.env.VEAOUTBOX_CHAIN_ID) {
+ throw new Error('VEAOUTBOX_CHAIN_ID environment variable is not defined');
+ }
const chainId = parseInt(process.env.VEAOUTBOX_CHAIN_ID);
const epochPeriod = 1800; // 30 min
const network = "devnet";
+ if (!process.env.DEVNET_SENDER) {
+ throw new Error('DEVNET_SENDER environment variable is not defined');
+ }
const sender = process.env.DEVNET_SENDER;
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's already an issue created for env validation and it will be addressed there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar Understood, I'll defer to that issue for environment variable validation.
⊙﹏⊙
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"); | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling to event listeners.
The event listeners should include try-catch blocks to prevent crashes if event handling fails.
Here's an example for one event handler:
- emitter.on(BotEvents.STARTED, (chainId, network) => {
- console.log(`Relayer started for ${chainId} on ${network}`);
- });
+ emitter.on(BotEvents.STARTED, (chainId, network) => {
+ try {
+ console.log(`Relayer started for ${chainId} on ${network}`);
+ } catch (error) {
+ console.error('Failed to handle STARTED event:', error);
+ }
+ });
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
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"); | |
}); | |
}; | |
export const configurableInitialize = (emitter: EventEmitter) => { | |
// Relayer state logs | |
emitter.on(BotEvents.STARTED, (chainId, network) => { | |
try { | |
console.log(`Relayer started for ${chainId} on ${network}`); | |
} catch (error) { | |
console.error('Failed to handle STARTED event:', error); | |
} | |
}); | |
emitter.on(BotEvents.WAITING, (delayAmount) => { | |
try { | |
console.log(`Waiting for next epoch: ${delayAmount} ms`); | |
} catch (error) { | |
console.error('Failed to handle WAITING event:', error); | |
} | |
}); | |
emitter.on(BotEvents.EXIT, () => { | |
try { | |
console.log("Exiting"); | |
} catch (error) { | |
console.error('Failed to handle EXIT event:', error); | |
} | |
}); | |
// Bot health logs | |
emitter.on(BotEvents.EXCEPTION, (err) => { | |
try { | |
console.error("Uncaught Exception occurred", err); | |
} catch (error) { | |
console.error('Failed to handle EXCEPTION event:', error); | |
} | |
}); | |
emitter.on(BotEvents.PROMISE_REJECTION, (reason, promise) => { | |
try { | |
console.error("Unhandled promise rejection:", reason, "at", promise); | |
} catch (error) { | |
console.error('Failed to handle PROMISE_REJECTION event:', error); | |
} | |
}); | |
// Lock file logs | |
emitter.on(BotEvents.LOCK_CLAIMED, () => { | |
try { | |
console.log("Lock claimed"); | |
} catch (error) { | |
console.error('Failed to handle LOCK_CLAIMED event:', error); | |
} | |
}); | |
emitter.on(BotEvents.LOCK_DIRECTORY, (pwd) => { | |
try { | |
console.log(`Lock file directory: ${pwd}`); | |
} catch (error) { | |
console.error('Failed to handle LOCK_DIRECTORY event:', error); | |
} | |
}); | |
emitter.on(BotEvents.LOCK_RELEASED, () => { | |
try { | |
console.log("Lock released"); | |
} catch (error) { | |
console.error('Failed to handle LOCK_RELEASED event:', error); | |
} | |
}); | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont see how this is necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar You're right. The event listeners are just doing simple console logging, which is very unlikely to throw errors. Adding try-catch blocks would be unnecessary complexity. I'll retract this suggestion.
✏️ Learnings added
Learnt from: mani99brar
PR: kleros/vea#395
File: relayer-cli/src/utils/logger.ts:19-49
Timestamp: 2025-01-15T07:41:36.295Z
Learning: For simple logging operations using console.log/error, adding try-catch blocks is unnecessary as these operations are highly reliable and any errors would be caught by process-level error handlers.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
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 | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Secure private key handling required.
The private key is being read directly from environment variables. Consider using a secure key management service or encrypted configuration.
Consider implementing a secure key provider interface:
interface KeyProvider {
getPrivateKey(): Promise<string>;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bot will be deployed in a private environment so I don't think this will be an issue, @fcanela wdyt, is this something to be concerned about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
try { | ||
await veaOutboxInstance.methods.sendMessage(proof, nonce, to, data).call(); | ||
} catch { | ||
nonce++; | ||
continue; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling in batch processing.
The current implementation silently continues on errors. Consider logging the error and implementing a retry mechanism.
try {
await veaOutboxInstance.methods.sendMessage(proof, nonce, to, data).call();
} catch (error) {
+ console.error(`Failed to process message ${nonce}:`, error);
+ if (shouldRetry(error)) {
+ await delay(1000);
+ continue;
+ }
nonce++;
continue;
}
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
retry and restart mechanisms are not implemented across the repo, will consider it to use after monitoring testnet deployments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar Understood, that's a sensible approach to wait for testnet monitoring data before implementing retry mechanisms.
✏️ Learnings added
Learnt from: mani99brar
PR: kleros/vea#395
File: relayer-cli/src/utils/relay.ts:117-122
Timestamp: 2025-01-15T07:41:35.455Z
Learning: Retry and restart mechanisms are not implemented across the repository. The team prefers to monitor testnet deployments first before considering implementing retry logic.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
emitter.emit(BotEvents.EXCEPTION, err); | ||
await handleExit(1); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Critical Issue] exitCode
parameter in handleExit
is unused.
The handleExit
function accepts an exitCode
parameter but does not utilize it when calling process.exit()
. As a result, the application always exits with code 0
, even in error scenarios where an exit code of 1
is intended.
Modify handleExit
to use the exitCode
parameter:
const handleExit = async (exitCode: number = 0) => {
shutdownManager.triggerShutdown();
await cleanup();
- process.exit(0);
+ process.exit(exitCode);
};
Also applies to: 100-102
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will work on shutdownManager in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar Understood! Would you like me to create a GitHub issue to track the shutdown manager improvements for the follow-up PR?
✏️ Learnings added
Learnt from: mani99brar
PR: kleros/vea#395
File: relayer-cli/src/utils/relayerHelpers.ts:95-97
Timestamp: 2025-01-15T07:41:33.898Z
Learning: The shutdown manager improvements in relayer-cli/src/utils/relayerHelpers.ts, including proper exit code handling, will be addressed in a separate PR to keep the current changes focused.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
const chain_state_raw = fs.readFileSync(state_file, { encoding: "utf8" }); | ||
emitter.emit(BotEvents.LOCK_DIRECTORY, process.cwd()); | ||
|
||
const chain_state_raw = fileSystem.readFileSync(state_file, { encoding: "utf8" }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling for file read operation.
Reading the state file using fileSystem.readFileSync
may throw an exception if the file cannot be read due to permissions issues or unexpected errors. It's recommended to wrap the file read operation in a try-catch block to handle any potential errors gracefully.
Apply this diff to include error handling:
- const chain_state_raw = fileSystem.readFileSync(state_file, { encoding: "utf8" });
+ let chain_state_raw;
+ try {
+ chain_state_raw = fileSystem.readFileSync(state_file, { encoding: "utf8" });
+ } catch (err) {
+ emitter.emit(BotEvents.FILE_READ_ERROR, err);
+ throw new Error(`Failed to read state file: ${err.message}`);
+ }
Ensure that BotEvents
includes a FILE_READ_ERROR
event, or handle the error appropriately.
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The shutdownManager will handle any uncaught unexpected errors, so let it fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar Understood. I'll retract the suggestion since the shutdownManager
is designed to handle such uncaught errors.
✏️ Learnings added
Learnt from: mani99brar
PR: kleros/vea#395
File: relayer-cli/src/utils/relayerHelpers.ts:35-35
Timestamp: 2025-01-15T07:41:33.722Z
Learning: In the VEA relayer, uncaught errors are intentionally allowed to propagate up to be handled by the shutdownManager, which has handlers for uncaughtException and unhandledRejection events, rather than adding explicit try-catch blocks.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai in general, try to avoid suggesting adding a try/catch for everything that may fail. We let the errors pop up to be handled by an upper layer or crash the process (that's fine). We are not pokemon trainers: our goal is not catch them all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fcanela Understood, I'll refrain from suggesting try/catch blocks for every potential failure point. Thank you for the clear guidance (and the Pokemon reference 😄).
✏️ Learnings added
Learnt from: fcanela
PR: kleros/vea#395
File: relayer-cli/src/utils/relayerHelpers.ts:35-35
Timestamp: 2025-01-15T12:37:24.802Z
Learning: Error handling philosophy: Avoid adding try/catch blocks for every potential failure. Let errors propagate up to be handled by upper layers or crash the process if necessary. The goal is not to catch every possible error ("we are not pokemon trainers").
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
emitter: EventEmitter, | ||
fileSystem: typeof fs = fs, | ||
removeLock: typeof releaseLock = releaseLock | ||
) { | ||
const chain_state_file = "./state/" + network + "_" + chainId + ".json"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Critical Issue] Inconsistent use of state file directory path.
There is an inconsistency in how the state file path is constructed in updateStateFile
compared to initialize
. In initialize
, state_file
is built using process.env.STATE_DIR
, while in updateStateFile
, chain_state_file
uses a hard-coded "./state/"
path. This could lead to incorrect file paths if STATE_DIR
is set to a different directory.
Apply this diff to fix the inconsistency:
- const chain_state_file = "./state/" + network + "_" + chainId + ".json";
+ const chain_state_file = process.env.STATE_DIR + network + "_" + chainId + ".json";
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar Thanks for agreeing to fix the state file path inconsistency!
(^◕ᴥ◕^)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
relayer-cli/src/utils/relayerHelpers.ts (1)
14-21
: LGTM! Consider enhancing function documentation.The dependency injection pattern and event emission changes look good. Consider updating the JSDoc to document the new parameters and their purposes.
/** * 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") + * @param emitter EventEmitter instance for logging state changes + * @param setLock Optional function for claiming locks (defaults to claimLock) + * @param syncStateFile Optional function for state file updates (defaults to updateStateFile) + * @param fileSystem Optional filesystem interface (defaults to fs) + * @returns Promise<number> The current nonce */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
relayer-cli/src/utils/relayerHelpers.ts
(3 hunks)
🧰 Additional context used
📓 Learnings (1)
relayer-cli/src/utils/relayerHelpers.ts (2)
Learnt from: mani99brar
PR: kleros/vea#395
File: relayer-cli/src/utils/relayerHelpers.ts:95-97
Timestamp: 2025-01-15T07:41:34.012Z
Learning: The shutdown manager improvements in relayer-cli/src/utils/relayerHelpers.ts, including proper exit code handling, will be addressed in a separate PR to keep the current changes focused.
Learnt from: mani99brar
PR: kleros/vea#395
File: relayer-cli/src/utils/relayerHelpers.ts:35-35
Timestamp: 2025-01-15T07:41:33.847Z
Learning: In the VEA relayer, uncaught errors are intentionally allowed to propagate up to be handled by the shutdownManager, which has handlers for uncaughtException and unhandledRejection events, rather than adding explicit try-catch blocks.
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: dependency-review
- GitHub Check: Scorecard analysis
- GitHub Check: test
- GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
relayer-cli/src/utils/relayerHelpers.ts (2)
45-62
: LGTM! Path handling and event emission look good.The function now consistently uses
process.env.STATE_DIR
and properly emits events for lock state changes.
Line range hint
65-102
: Event-driven error handling looks good.The replacement of console.error with event emissions improves observability. Note that the exit code handling improvements are intentionally deferred to a separate PR.
Let's verify that all error scenarios emit the appropriate events:
✅ Verification successful
Error handling and event emissions are properly implemented
The verification confirms that:
- Uncaught exceptions emit
BotEvents.EXCEPTION
- Unhandled rejections emit
BotEvents.PROMISE_REJECTION
- Both events are properly handled by the logger
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that all error handling code paths emit events # Look for error handling code that might be missing event emissions rg -U "catch.*\{(?:(?!emit).)*\}"Length of output: 276
Script:
#!/bin/bash # Search for catch blocks and error handling patterns echo "=== Searching for catch blocks ===" rg "catch.*\{" -A 3 echo -e "\n=== Searching for error-related event emissions ===" rg "emit.*(?:ERROR|EXCEPTION|PROMISE_REJECTION)" --pcre2 echo -e "\n=== Searching for BotEvents error-related constants ===" rg "BotEvents\.[A-Z_]+" -l | xargs rg "ERROR|EXCEPTION|PROMISE_REJECTION"Length of output: 5135
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no full control over the Vea logic, but code-wise it looks great. Good job!
relayer-cli/src/testnetRelayer.ts
Outdated
/** | ||
* 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 | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar the bot is right, the correct way of documenting object properties is with the dot notation
for (let i = 0; i < proofIndices.length; i++) { | ||
proof.push(result[`layer${i}`][0].hash); | ||
} | ||
|
||
return proof; | ||
} catch (e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a change of the PR, so please don't address it within it... but this try/catch is unexpected for me. If it fails getting the hashes... it returns an empty array? not throw the error? the request failing should be unexpected, not a controlled error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you are right. It doesn't make any sense to return an empty array to me either. Creating an issue for it.
const chain_state_raw = fs.readFileSync(state_file, { encoding: "utf8" }); | ||
emitter.emit(BotEvents.LOCK_DIRECTORY, process.cwd()); | ||
|
||
const chain_state_raw = fileSystem.readFileSync(state_file, { encoding: "utf8" }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai in general, try to avoid suggesting adding a try/catch for everything that may fail. We let the errors pop up to be handled by an upper layer or crash the process (that's fine). We are not pokemon trainers: our goal is not catch them all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this file meant to be committed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the state file which the bot starts from or initialises. Pushed it to create the state
directory as I had to create the folder each time
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
relayer-cli/src/testnetRelayer.ts (3)
33-33
: Consider making maxBatchSize configurable.The batch size is hardcoded to 10 messages per batch. Consider making this configurable through the RelayerConfig interface to allow for different batch sizes based on network conditions or requirements.
interface RelayerConfig { chainId: number; network: string; shutdownManager: ShutdownManager; emitter: EventEmitter; + maxBatchSize?: number; } - const maxBatchSize = 10; // 10 messages per batch + const maxBatchSize = config.maxBatchSize ?? 10; // Default to 10 messages per batch
46-49
: Extract delay calculation logic for better readability.The delay calculation logic could be extracted into a separate function to improve code readability and maintainability.
+ function calculateNextEpochDelay(epochPeriod: number): number { + const currentTS = Math.floor(Date.now() / 1000); + return (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000; + } - const currentTS = Math.floor(Date.now() / 1000); - const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000; + const delayAmount = calculateNextEpochDelay(epochPeriod); emitter.emit(BotEvents.WAITING, delayAmount); await delay(delayAmount);
44-45
: Add explicit null check for nonce.The code assumes that a null nonce should skip state file updates. Consider making this more explicit with a clear condition check.
- if (nonce != null) await updateStateFile(chainId, Math.floor(Date.now() / 1000), nonce, network, emitter); + if (nonce === null) { + emitter.emit(BotEvents.NONCE_UPDATE_SKIPPED); + } else { + await updateStateFile(chainId, Math.floor(Date.now() / 1000), nonce, network, emitter); + }relayer-cli/src/utils/relayerHelpers.ts (2)
23-24
: Consider adding debug logging for state file initialization.While the event emission for lock claiming is good, it would be helpful to emit an event when initializing a new state file to aid in debugging.
if (!fileSystem.existsSync(state_file)) { // No state file so initialize starting now const tsnow = Math.floor(Date.now() / 1000); + emitter.emit(BotEvents.STATE_FILE_INIT, { timestamp: tsnow, network, chainId }); await syncStateFile(chainId, tsnow, 0, network, emitter); }
Also applies to: 28-32
46-56
: Replace console.log with event emission.Debug logging should use the event emitter for consistency with the rest of the codebase.
- console.log(process.env.STATE_DIR); + emitter.emit(BotEvents.DEBUG, { type: 'state_dir', value: process.env.STATE_DIR });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
relayer-cli/src/testnetRelayer.ts
(1 hunks)relayer-cli/src/utils/relayerHelpers.test.ts
(1 hunks)relayer-cli/src/utils/relayerHelpers.ts
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- relayer-cli/src/utils/relayerHelpers.test.ts
🧰 Additional context used
📓 Learnings (1)
relayer-cli/src/utils/relayerHelpers.ts (3)
Learnt from: fcanela
PR: kleros/vea#395
File: relayer-cli/src/utils/relayerHelpers.ts:35-35
Timestamp: 2025-01-15T12:37:24.982Z
Learning: Error handling philosophy: Avoid adding try/catch blocks for every potential failure. Let errors propagate up to be handled by upper layers or crash the process if necessary. The goal is not to catch every possible error ("we are not pokemon trainers").
Learnt from: mani99brar
PR: kleros/vea#395
File: relayer-cli/src/utils/relayerHelpers.ts:95-97
Timestamp: 2025-01-15T07:41:34.012Z
Learning: The shutdown manager improvements in relayer-cli/src/utils/relayerHelpers.ts, including proper exit code handling, will be addressed in a separate PR to keep the current changes focused.
Learnt from: mani99brar
PR: kleros/vea#395
File: relayer-cli/src/utils/relayerHelpers.ts:35-35
Timestamp: 2025-01-15T07:41:33.847Z
Learning: In the VEA relayer, uncaught errors are intentionally allowed to propagate up to be handled by the shutdownManager, which has handlers for uncaughtException and unhandledRejection events, rather than adding explicit try-catch blocks.
🔇 Additional comments (5)
relayer-cli/src/testnetRelayer.ts (2)
2-20
: LGTM! Well-structured imports and interface definition.The introduction of EventEmitter and the well-defined RelayerConfig interface enhance the modularity and type safety of the code.
59-59
: Environment variable validation remains to be addressed.The usage of
process.env.VEAOUTBOX_CHAIN_ID
without validation was previously discussed, and it was noted that this will be addressed in a separate issue for environment variable validation.relayer-cli/src/utils/relayerHelpers.ts (3)
2-6
: LGTM! Clean import organization.The new imports for EventEmitter and BotEvents are well-organized and align with the shift towards an event-driven architecture.
15-22
: LGTM! Well-structured dependency injection pattern.The function signature changes improve testability while maintaining backward compatibility through default parameters.
67-72
: LGTM! Improved error handling with event emission.The replacement of console logging with event emission provides better error tracking and aligns with the event-driven architecture. The exit code handling will be addressed in a separate PR focused on shutdown manager improvements.
Also applies to: 74-75, 97-97, 102-102
PR-Codex overview
This PR introduces event-driven logging for the relayer, updates dependencies, and enhances the proof handling logic. It also restructures the relayer's initialization and state management processes, ensuring better event emission and handling.
Detailed summary
BotEvents
enum for event handling.web3
version inpackage.json
.initialize
andupdateStateFile
functions to accept anEventEmitter
.getProofIndices
andrelayBatch
functions.relayBatch
andgetMessageDataToRelay
.Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Chores
Documentation