Skip to content
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

Initial not working draft to refactor #124

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

Conversation

raffaele-oplabs
Copy link
Contributor

Description

Tests

Additional context

Metadata

@raffaele-oplabs raffaele-oplabs requested a review from a team as a code owner January 22, 2025 23:32
Copy link
Contributor

coderabbitai bot commented Jan 22, 2025

Warning

Rate limit exceeded

@raffaele-oplabs has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 54 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c7765b7 and abeb0b1.

📒 Files selected for processing (2)
  • op-eventer/monitoring/monitoring.yaml (1 hunks)
  • op-eventer/setup/query_exec.py (1 hunks)

Walkthrough

The pull request introduces several enhancements to the monitoring and alerting system for Ethereum transactions, specifically targeting pause events on the Mainnet and Sepolia networks. Key changes include the addition of the google.cloud.bigquery dependency, a new script for sending metrics, a Python script for executing queries and processing alerts, and a Bash script for setting up the environment and executing the monitoring process. The configuration also includes new alert rules and checks for unauthorized pause events.

Changes

File Change Summary
op-eventer/requirements.txt Added google.cloud.bigquery dependency for BigQuery integration
op-eventer/monitoring/monitoring.yaml Added configurations for Mainnet and Sepolia, including contract addresses, pause events, authorized addresses, and alert queries
op-eventer/setup/consumers/metrics.sh New script for sending metrics to an endpoint with logging and payload construction
op-eventer/setup/query_exec.py New Python script for executing BigQuery queries, processing results, and sending alerts
op-eventer/setup/run.sh New Bash script to set up virtual environment, validate Google Cloud credentials, and execute query processing script
op-eventer/setup/alerts/op-eventer.yaml Added alert group and multiple alert rules for monitoring opEventer_metric with priority levels
op-eventer/setup/alerts/setup_alerts.sh New script for setting up alerts in the op-eventer namespace, utilizing environment variables and synchronizing alert rules

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
op-monitorism/eventer/monitor.go (2)

23-24: Encapsulate the counter variable within the Monitor struct.

Using a global variable for counter can lead to issues in concurrent environments. Consider moving counter into the Monitor struct to manage state more effectively.

Modify the struct and initialization:

 type Monitor struct {
     log log.Logger
     // ... other fields
+    counter int
 }

 // In the constructor:
 return &Monitor{
     log:      log,
     l1Client: l1Client,
     // ... other fields
+    counter:  0,
 }

181-184: Implement retry logic for failed RPC calls.

When RPC calls fail, adding a retry mechanism can improve resilience against transient issues.

Consider using an exponential backoff strategy:

import "github.com/cenkalti/backoff/v4"

// Replace direct calls with retry logic
operation := func() error {
    header, err := m.l1Client.HeaderByNumber(context.Background(), nil)
    if err != nil {
        m.unexpectedRpcErrors.WithLabelValues("L1", "HeaderByNumber").Inc()
        m.log.Warn("Failed to retrieve latest block header", "error", err.Error())
        return err
    }
    // ... rest of the logic
    return nil
}

backoffConfig := backoff.NewExponentialBackOff()
backoffConfig.MaxElapsedTime = 1 * time.Minute
err := backoff.Retry(operation, backoffConfig)
if err != nil {
    m.log.Error("Failed to retrieve block header after retries", "error", err)
    return
}

Also applies to: 196-199

op-monitorism/eventer/cli.go (1)

48-48: Update environment variable names to accurately reflect their purpose.

The comments indicate a need to change NICKNAME and PATH_YAML to more descriptive names like BLOCKCHAIN_NAME. This can improve clarity and maintainability.

Update the environment variables:

     &cli.StringFlag{
         Name:     NicknameFlagName,
         Usage:    "Nickname of chain being monitored",
-        EnvVars:  opservice.PrefixEnvVar(envVar, "NICKNAME"), //need to change the name to BLOCKCHAIN_NAME
+        EnvVars:  opservice.PrefixEnvVar(envVar, "BLOCKCHAIN_NAME"),
         Required: true,
     },
     &cli.StringFlag{
         Name:     PathYamlRulesFlagName,
         Usage:    "Path to the yaml file containing the events to monitor",
-        EnvVars:  opservice.PrefixEnvVar(envVar, "PATH_YAML"), //need to change the name to BLOCKCHAIN_NAME
+        EnvVars:  opservice.PrefixEnvVar(envVar, "YAML_RULES_PATH"),
         Required: true,
     },

Also applies to: 54-54

op-monitorism/eventer/README.md (3)

3-3: Enhance the introduction with specific driver examples.

Consider listing the specific database drivers that are currently supported to help users understand their options immediately.

-op-eventer is a customizable monitoring tool that listens to a set of events configured via a YAML file. It includes a set of listeners that respond to these events and perform specific actions. The events produced are written into a customizable database, which can be a file, a database, Prometheus, etc., depending on the driver used. The final output can also be written based on the driver used.
+op-eventer is a customizable monitoring tool that listens to a set of events configured via a YAML file. It includes a set of listeners that respond to these events and perform specific actions. The events produced are written into a customizable database using supported drivers such as SQLite, PostgreSQL, or Prometheus. The final output format adapts based on the selected driver.

28-40: Add environment variable usage examples.

The CLI options section would benefit from examples showing how to use environment variables, especially for common configurations.

Add this section after the OPTIONS:

Example using environment variables:
```bash
export GLOBAL_EVENT_MON_L1_NODE_URL="https://mainnet.infura.io/v3/YOUR-PROJECT-ID"
export GLOBAL_EVENT_MON_NICKNAME="mainnet"
export MONITORISM_METRICS_ENABLED=true

---

`65-68`: **Enhance execution instructions with prerequisites and validation steps.**

The execution example would benefit from:
1. Prerequisites (required directory structure)
2. Validation steps to verify successful setup
3. Expected output examples

Add these sections before the execution command:
```markdown
### Prerequisites
1. Ensure the rules directory exists: `mkdir -p /tmp/Monitorism/op-monitorism/global_events/rules/rules_mainnet_L1`
2. Copy the template YAML file to the rules directory

### Validation
After starting the monitor, verify:
1. Metrics endpoint is accessible (if enabled): `curl http://localhost:7300/metrics`
2. Events are being logged according to your configuration
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69cecce and ed27ecd.

📒 Files selected for processing (6)
  • op-monitorism/eventer/README.md (1 hunks)
  • op-monitorism/eventer/cli.go (1 hunks)
  • op-monitorism/eventer/monitor.go (1 hunks)
  • op-monitorism/eventer/rules/rules_mainnet_L1/rules_TEMPLATE_COPY_PASTE.yaml (1 hunks)
  • op-monitorism/eventer/rules/rules_sepolia_L1/rules_TEMPLATE_COPY_PASTE.yaml (1 hunks)
  • op-monitorism/eventer/types.go (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • op-monitorism/eventer/rules/rules_sepolia_L1/rules_TEMPLATE_COPY_PASTE.yaml
  • op-monitorism/eventer/rules/rules_mainnet_L1/rules_TEMPLATE_COPY_PASTE.yaml
🔇 Additional comments (1)
op-monitorism/eventer/README.md (1)

52-52: Document priority levels.

The priority field (P5) is mentioned without explaining the available priority levels and their significance.

Could you add documentation explaining:

  • Available priority levels (P1-P5?)
  • Impact of priority on monitoring behavior
  • Use cases for different priority levels

Comment on lines 81 to 84
if err != nil {
fmt.Println("Error reading YAML file:", err)
panic("Error reading YAML")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid using panic in library functions; return errors instead.

Using panic for error handling in ReadYamlFile is not recommended. It's better to return the error and let the caller handle it appropriately.

You can modify the function to return an error:

-func ReadYamlFile(filename string) Configuration {
+func ReadYamlFile(filename string) (Configuration, error) {
    var config Configuration
    data, err := os.ReadFile(filename)
    if err != nil {
-       fmt.Println("Error reading YAML file:", err)
-       panic("Error reading YAML")
+       return Configuration{}, fmt.Errorf("error reading YAML file: %w", err)
    }
    err = yaml.Unmarshal(data, &config)
    if err != nil {
-       fmt.Println("Error reading YAML file:", err)
-       panic("Error reading YAML")
+       return Configuration{}, fmt.Errorf("error unmarshalling YAML file: %w", err)
    }
    return config, nil
}

Also applies to: 86-89

Comment on lines 126 to 129
fmt.Println("Error reading directory:", err)
panic("Error reading directory")
}
var yamlFiles []os.DirEntry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace panic with proper error handling in ReadAllYamlRules.

In ReadAllYamlRules, using panic can cause unexpected crashes. Instead, return errors to the caller to handle them gracefully.

Modify the error handling as follows:

-func ReadAllYamlRules(PathYamlRules string, log log.Logger) (GlobalConfiguration, error) {
+func ReadAllYamlRules(PathYamlRules string, log log.Logger) (GlobalConfiguration, error) {
    var GlobalConfig GlobalConfiguration

    entries, err := os.ReadDir(PathYamlRules)
    if err != nil {
-       fmt.Println("Error reading directory:", err)
-       panic("Error reading directory")
+       return GlobalConfiguration{}, fmt.Errorf("error reading directory: %w", err)
    }
    // ...

    if err != nil {
-       log.Warn("Fail to marshal GlobalConfig to yaml", "ERROR", err)
-       panic("Fail to marshal GlobalConfig to yaml")
+       return GlobalConfiguration{}, fmt.Errorf("failed to marshal GlobalConfig to YAML: %w", err)
    }
    err = os.WriteFile("/tmp/globalconfig.yaml", yaml_marshalled, 0644)
    if err != nil {
-       log.Warn("Error writing the globalconfig YAML file on the disk:", "ERROR", err)
-       panic("Error writing the globalconfig YAML file on the disk")
+       return GlobalConfiguration{}, fmt.Errorf("error writing globalconfig YAML file: %w", err)
    }
    return GlobalConfig, nil
}

Also applies to: 156-158, 160-163

Comment on lines 140 to 142
if formattedSignature == "" {
panic("Invalid signature")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle invalid signatures without panicking.

In FormatAndHash, panicking on an invalid signature can cause the entire application to crash. It's better to return an error to handle this scenario gracefully.

Modify the function to return an error:

-func FormatAndHash(signature string) common.Hash {
+func FormatAndHash(signature string) (common.Hash, error) {
    formattedSignature := formatSignature(signature)
    if formattedSignature == "" {
-       panic("Invalid signature")
+       return common.Hash{}, fmt.Errorf("invalid signature: %s", signature)
    }
    hash := crypto.Keccak256([]byte(formattedSignature))
-   return common.BytesToHash(hash)
+   return common.BytesToHash(hash), nil
}

Committable suggestion skipped: line range outside the PR's diff.


globalConfig.DisplayMonitorAddresses(log) //Display all the addresses that are monitored.
log.Info("--------------------------------------- End of Infos -----------------------------\n")
time.Sleep(10 * time.Second) // sleep for 10 seconds useful to read the information before the prod.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid using time.Sleep in production code.

Pausing execution with time.Sleep(10 * time.Second) can hinder performance. If the delay is necessary for startup initialization, consider implementing a more robust synchronization mechanism or using a configuration setting.

If this is for debugging, you might want to conditionally execute it:

-    time.Sleep(10 * time.Second) // sleep for 10 seconds useful to read the information before the prod.
+    if cfg.Debug {
+        time.Sleep(10 * time.Second)
+    }

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
op-eventer/parse_yaml.py (1)

4-4: Remove unused import.

The pprint module is imported but never used in the code.

-from pprint import pprint
op-eventer/checker/query.yaml (2)

13-19: Add comments explaining the CTE logic and consider performance optimization.

The CTE could benefit from:

  1. Documentation explaining the purpose of filtering by ContractAddress and PauseEvent
  2. Performance optimization by adding a date range filter
   WITH log_transactions AS (
     SELECT DISTINCT transaction_hash
     FROM `bigquery-public-data.crypto_ethereum.logs`
     WHERE
     LOWER(address) = LOWER('$ContractAddress')
     AND topics[OFFSET(0)] = "$PauseEvent"
+    -- Add date range to improve query performance
+    AND block_timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
     )

29-36: Consider using YAML anchors to reduce duplication.

The check descriptions and structure are duplicated for both environments.

 Checks:
   - Path: consumers/metrics.py
     Queries:
+      # Define the base check using YAML anchor
+      &base_check
+        Description: &desc "Alert when a transaction is paused by an unauthorized address"
+
       Mainnet-TransactionPausedByUnauthorizedAddress:
-        Description: "Alert when a transaction is paused by an unauthorized address"
+        <<: *base_check
         Parameters: Mainnet
         Query: TransactionPausedByUnauthorizedAddress
       Sepolia-TransactionPausedByUnauthorizedAddress:
-        Description: "Alert when a transaction is paused by an unauthorized address"
+        <<: *base_check
         Parameters: Sepolia
         Query: TransactionPausedByUnauthorizedAddress
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed27ecd and 0e3b6d4.

📒 Files selected for processing (2)
  • op-eventer/checker/query.yaml (1 hunks)
  • op-eventer/parse_yaml.py (1 hunks)

Comment on lines 36 to 39
if __name__ == "__main__":
for name, query in process_queries(sys.argv[1]):
print(f"\n=== {name} ===")
print(query)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add command line argument validation.

The script should validate that a file path is provided as an argument.

 if __name__ == "__main__":
+    if len(sys.argv) != 2:
+        print("Usage: python parse_yaml.py <config_file>")
+        sys.exit(1)
+
     for name, query in process_queries(sys.argv[1]):
         print(f"\n=== {name} ===")
         print(query)
📝 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.

Suggested change
if __name__ == "__main__":
for name, query in process_queries(sys.argv[1]):
print(f"\n=== {name} ===")
print(query)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python parse_yaml.py <config_file>")
sys.exit(1)
for name, query in process_queries(sys.argv[1]):
print(f"\n=== {name} ===")
print(query)

Comment on lines 28 to 31
query_parameters = Parameters[query_parameters_name]
query_template = Template(QueriesTemplate[query_template_name])
parsed_query = query_template.safe_substitute(query_parameters)
processed_queries.append((query_name, parsed_query))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate query parameters against template placeholders.

The code should verify that all required template placeholders have corresponding parameters before substitution to prevent silent failures.

             query_parameters = Parameters[query_parameters_name]
             query_template = Template(QueriesTemplate[query_template_name])
+            # Extract required placeholders from template
+            required_params = [p[1] for p in Template.pattern.findall(QueriesTemplate[query_template_name])]
+            missing_params = [p for p in required_params if p not in query_parameters]
+            if missing_params:
+                raise ValueError(f"Missing parameters for {query_name}: {', '.join(missing_params)}")
+
             parsed_query = query_template.safe_substitute(query_parameters)
📝 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.

Suggested change
query_parameters = Parameters[query_parameters_name]
query_template = Template(QueriesTemplate[query_template_name])
parsed_query = query_template.safe_substitute(query_parameters)
processed_queries.append((query_name, parsed_query))
query_parameters = Parameters[query_parameters_name]
query_template = Template(QueriesTemplate[query_template_name])
# Extract required placeholders from template
required_params = [p[1] for p in Template.pattern.findall(QueriesTemplate[query_template_name])]
missing_params = [p for p in required_params if p not in query_parameters]
if missing_params:
raise ValueError(f"Missing parameters for {query_name}: {', '.join(missing_params)}")
parsed_query = query_template.safe_substitute(query_parameters)
processed_queries.append((query_name, parsed_query))

Comment on lines 8 to 16
with open(file_path, 'r') as file:
config = yaml.safe_load(file)

# Get all Parameters
Parameters = config['Parameters']
# Get all QueryTemplate
QueriesTemplate = config['QueriesTemplate']
# Process Checks
Checks = config['Checks']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for file operations and YAML parsing.

The current implementation could raise uncaught exceptions for:

  • FileNotFoundError
  • PermissionError
  • yaml.YAMLError
  • KeyError when accessing config sections
 def process_queries(file_path):
+    try:
         with open(file_path, 'r') as file:
             config = yaml.safe_load(file)
 
+        # Validate required sections
+        required_sections = ['Parameters', 'QueriesTemplate', 'Checks']
+        missing_sections = [section for section in required_sections if section not in config]
+        if missing_sections:
+            raise ValueError(f"Missing required sections: {', '.join(missing_sections)}")
+
         # Get all Parameters
         Parameters = config['Parameters']
         # Get all QueryTemplate
         QueriesTemplate = config['QueriesTemplate']
         # Process Checks
         Checks = config['Checks']
+    except FileNotFoundError:
+        raise FileNotFoundError(f"Configuration file not found: {file_path}")
+    except yaml.YAMLError as e:
+        raise ValueError(f"Invalid YAML format: {str(e)}")
📝 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.

Suggested change
with open(file_path, 'r') as file:
config = yaml.safe_load(file)
# Get all Parameters
Parameters = config['Parameters']
# Get all QueryTemplate
QueriesTemplate = config['QueriesTemplate']
# Process Checks
Checks = config['Checks']
try:
with open(file_path, 'r') as file:
config = yaml.safe_load(file)
# Validate required sections
required_sections = ['Parameters', 'QueriesTemplate', 'Checks']
missing_sections = [section for section in required_sections if section not in config]
if missing_sections:
raise ValueError(f"Missing required sections: {', '.join(missing_sections)}")
# Get all Parameters
Parameters = config['Parameters']
# Get all QueryTemplate
QueriesTemplate = config['QueriesTemplate']
# Process Checks
Checks = config['Checks']
except FileNotFoundError:
raise FileNotFoundError(f"Configuration file not found: {file_path}")
except yaml.YAMLError as e:
raise ValueError(f"Invalid YAML format: {str(e)}")

WHERE LOWER(t.to_address) NOT IN (SELECT LOWER(addr) FROM UNNEST($AuthorizedPauseAddresses) AS addr)

Checks:
- Path: consumers/metrics.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Missing consumer file: consumers/metrics.py does not exist in the repository

The configuration references a consumer file that doesn't exist in the codebase. This will likely cause runtime failures when the system attempts to load the consumer module.

🔗 Analysis chain

Verify the existence of the consumer file.

The check references consumers/metrics.py which isn't provided in the PR.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the consumer file exists
fd -t f "metrics.py" consumers/

Length of output: 129


Script:

#!/bin/bash
# Search for metrics.py anywhere in the repository
fd -t f "metrics.py"

# Search for references to metrics.py in YAML files
rg -t yaml "metrics\.py"

Length of output: 108

Comment on lines 3 to 9
ContractAddress: "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
PauseEvent: "0x62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258"
AuthorizedPauseAddresses: ["0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a"]
Sepolia:
ContractAddress: "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
PauseEvent: "0x62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258"
AuthorizedPauseAddresses: ["0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Incorrect configuration: Mainnet addresses used in Sepolia environment

The contract address and authorized pause address are specifically designated for mainnet use:

  • 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed is the Optimism Portal contract for mainnet
  • 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A is a mainnet-specific safe address

Sepolia should use its own network-specific addresses, as evidenced by different safe addresses in the codebase (e.g., Sepolia safe: 0x837DE453AD5F21E89771e3c06239d8236c0EFd5E).

🔗 Analysis chain

Verify contract and authorized addresses for each environment.

The same contract address and authorized pause addresses are used for both Mainnet and Sepolia. This might not be intentional and could pose a security risk.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify contract addresses on both networks
echo "Checking contract addresses..."
curl -s -X POST https://api.etherscan.io/api \
  -d "module=contract&action=getcontractcreation&contractaddresses=0xbEb5Fc579115071764c7423A4f12eDde41f106Ed&apikey=YourApiKeyToken"

echo "Checking Sepolia contract..."
curl -s -X POST https://api-sepolia.etherscan.io/api \
  -d "module=contract&action=getcontractcreation&contractaddresses=0xbEb5Fc579115071764c7423A4f12eDde41f106Ed&apikey=YourApiKeyToken"

Length of output: 638


Script:

#!/bin/bash
# Search for addresses in the codebase
echo "Searching for contract address..."
rg -i "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed" -A 2 -B 2

echo -e "\nSearching for authorized pause address..."
rg -i "0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a" -A 2 -B 2

echo -e "\nLooking for deployment configs and docs..."
fd -e md -e txt -e json -e yaml -e toml | xargs grep -l -i "deploy\|config\|network"

Length of output: 7195

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (7)
op-eventer/checker.py (2)

27-37: Use snake_case for variable names to follow Python conventions.

Variables like Parameters, QueriesTemplate, and Checks should be renamed to use snake_case for consistency and readability.

Apply this diff to rename the variables:

-    Parameters = config['Parameters']
+    parameters = config['Parameters']
-    QueriesTemplate = config['QueriesTemplate']
+    queries_template = config['QueriesTemplate']
-    Checks = config['Checks']
+    checks = config['Checks']

Remember to update all references to these variables throughout the code.


5-5: Remove unused imports for cleaner code.

The imports pprint and json are not used in the script and can be safely removed.

Apply this diff to remove the unused imports:

-from pprint import pprint
...
-import json

Also applies to: 8-8

op-eventer/requirements.txt (1)

1-1: Specify the dependency version for google.cloud.bigquery.

Defining a specific version helps ensure consistent environments and can prevent future compatibility issues.

For example, specify the version like:

google-cloud-bigquery==3.10.0

Make sure to choose a version compatible with your project requirements.

op-eventer/consumers/metrics.sh (2)

4-4: Make the environment file path configurable for flexibility.

Hardcoding the environment file path may lead to issues across different environments. Consider allowing the path to be set via an environment variable or script argument.

Apply this diff to make the path configurable:

-source ~/op-eventer.env
+ENV_FILE="${ENV_FILE_PATH:-~/op-eventer.env}"
+source "$ENV_FILE"

16-16: Use the log function for consistent logging.

By using the log function, you maintain consistent formatting for all log messages.

Apply this diff to update the logging:

- echo "Sending metric: $payload"
+ log "Sending metric: $payload"
op-eventer/checkers/query.yaml (2)

1-9: Add parameter documentation and use checksummed addresses.

Consider the following improvements:

  1. Add documentation for each parameter explaining its purpose and format
  2. Use checksummed Ethereum addresses for better safety
 Parameters:
   Mainnet:
-    ContractAddress: "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
+    # The contract being monitored for pause events
+    ContractAddress: "0xbEB5Fc579115071764c7423A4f12eDde41f106Ed"
+    # Keccak256 hash of Paused(address) event signature
     PauseEvent: "0x62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258"
-    AuthorizedPauseAddresses: ["0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3b"]
+    # List of addresses authorized to pause the contract
+    AuthorizedPauseAddresses: ["0x9BA6e03D8B90DE867373DB8cf1A58d2F7F006B3B"]

26-36: Enhance alert configuration with severity and frequency controls.

Consider adding:

  1. Alert severity levels
  2. Alert frequency/cooldown periods
  3. Alert grouping strategy
  4. Metrics format specification
 Checks:
   - Path: consumers/metrics.sh
     Queries:
       Mainnet-TransactionPausedByUnauthorizedAddress:
         Description: "Alert when a transaction is paused by an unauthorized address"
+        Severity: "CRITICAL"
+        Frequency: "5m"
+        GroupBy: "to_address"
+        MetricsFormat: "prometheus"
         Parameters: Mainnet
         Query: TransactionPausedByUnauthorizedAddress
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e3b6d4 and 41c0114.

📒 Files selected for processing (4)
  • op-eventer/checker.py (1 hunks)
  • op-eventer/checkers/query.yaml (1 hunks)
  • op-eventer/consumers/metrics.sh (1 hunks)
  • op-eventer/requirements.txt (1 hunks)
🔇 Additional comments (1)
op-eventer/checkers/query.yaml (1)

3-3: Verify contract addresses across networks.

The same contract address is used for both Mainnet and Sepolia networks. This is unusual as contracts typically have different addresses on different networks.

Also applies to: 7-7

✅ Verification successful

Contract address verification successful

The contract exists on both Mainnet and Sepolia networks. Using the same address across networks is valid and likely intentional, possibly deployed using deterministic deployment methods like Create2.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify contract existence and code on both networks
# Note: Using etherscan API as it's more reliable than direct RPC calls

CONTRACT="0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
ETHERSCAN_ENDPOINTS=("https://api.etherscan.io" "https://api-sepolia.etherscan.io")
NETWORKS=("Mainnet" "Sepolia")

for i in {0..1}; do
  echo "Checking ${NETWORKS[$i]}..."
  curl -s "${ETHERSCAN_ENDPOINTS[$i]}/api?module=contract&action=getabi&address=$CONTRACT" | jq .
done

Length of output: 734


Script:

#!/bin/bash
# Description: Verify contract existence using contract source code endpoint
# Note: This endpoint often works without API key

CONTRACT="0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
ETHERSCAN_ENDPOINTS=("https://api.etherscan.io" "https://api-sepolia.etherscan.io")
NETWORKS=("Mainnet" "Sepolia")

for i in {0..1}; do
  echo "Checking ${NETWORKS[$i]}..."
  curl -s "${ETHERSCAN_ENDPOINTS[$i]}/api?module=contract&action=getsourcecode&address=$CONTRACT" | jq .
done

Length of output: 748


Script:

#!/bin/bash
# Description: Check contract existence using blockchain explorer web URLs
# Note: Using -I to get only headers, -s for silent mode

CONTRACT="0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
EXPLORER_URLS=("https://etherscan.io/address" "https://sepolia.etherscan.io/address")
NETWORKS=("Mainnet" "Sepolia")

for i in {0..1}; do
  echo "Checking ${NETWORKS[$i]}..."
  STATUS=$(curl -Is "${EXPLORER_URLS[$i]}/$CONTRACT" | head -n 1)
  echo "Response: $STATUS"
  echo "URL: ${EXPLORER_URLS[$i]}/$CONTRACT"
  echo "---"
done

Length of output: 1040

Comment on lines 59 to 60
subprocess.run(f"bash {script_path} '{
field_string}'", shell=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid using shell=True with untrusted input to prevent security vulnerabilities.

Passing untrusted input to subprocess.run with shell=True can lead to shell injection attacks. It's safer to pass an array of arguments and avoid using shell=True.

Apply this diff to modify the subprocess.run call:

-                subprocess.run(f"bash {script_path} '{field_string}'", shell=True)
+                subprocess.run(["bash", script_path, field_string])
📝 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.

Suggested change
subprocess.run(f"bash {script_path} '{
field_string}'", shell=True)
subprocess.run(["bash", script_path, field_string])

Comment on lines 11 to 12
credentials_path = f"{
home}/.config/gcloud/application_default_credentials.json"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix the syntax error in the credentials_path assignment.

The f-string is incorrectly formatted and will cause a syntax error. You should correct the f-string to properly include the home variable.

Apply this diff to fix the syntax error:

-credentials_path = f"{
-    home}/.config/gcloud/application_default_credentials.json"
+credentials_path = f"{home}/.config/gcloud/application_default_credentials.json"
📝 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.

Suggested change
credentials_path = f"{
home}/.config/gcloud/application_default_credentials.json"
credentials_path = f"{home}/.config/gcloud/application_default_credentials.json"

Comment on lines 66 to 68
for name, query in process_queries(sys.argv[1]):
print(f"\n=== {name} ===")
print(query)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add argument validation to handle missing command-line arguments.

If no file path is provided, the script will raise an IndexError. Adding a check will improve user experience and prevent unexpected crashes.

Apply this diff to add argument validation:

 if __name__ == "__main__":
+    if len(sys.argv) < 2:
+        print("Usage: python checker.py <path_to_yaml_file>")
+        sys.exit(1)
     for name, query in process_queries(sys.argv[1]):
         print(f"\n=== {name} ===")
         print(query)

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 25 to 26
value=$1
metric "raffaele_metrics" "source=test" "${value} metric=1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add input validation to handle missing arguments.

If no value is provided, the script may fail or behave unexpectedly. Adding a check ensures the script is used correctly.

Apply this diff to add argument validation:

 value=$1
+if [ -z "$value" ]; then
+  echo "Usage: $0 <value>"
+  exit 1
+fi
 metric "raffaele_metrics" "source=test" "${value} metric=1"
📝 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.

Suggested change
value=$1
metric "raffaele_metrics" "source=test" "${value} metric=1"
value=$1
if [ -z "$value" ]; then
echo "Usage: $0 <value>"
exit 1
fi
metric "raffaele_metrics" "source=test" "${value} metric=1"

Comment on lines 20 to 23
-u $METRICS_USER \
-H "Content-Type: text/plain" \
"$METRICS_URL" \
-d "$payload"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Check if METRICS_USER and METRICS_URL are set before making the request.

If these environment variables are unset, the curl command will fail. Adding a check can prevent unexpected errors.

Apply this diff to ensure variables are set:

+  if [ -z "$METRICS_USER" ] || [ -z "$METRICS_URL" ]; then
+    log "Error: METRICS_USER and METRICS_URL must be set in the environment."
+    exit 1
+  fi
   curl \

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 13 to 24
WITH log_transactions AS (
SELECT DISTINCT transaction_hash
FROM `bigquery-public-data.crypto_ethereum.logs`
WHERE
LOWER(address) = LOWER('$ContractAddress')
AND topics[OFFSET(0)] = "$PauseEvent"
)
SELECT t.hash as TX
FROM `bigquery-public-data.crypto_ethereum.transactions` t
INNER JOIN log_transactions l
ON t.hash = l.transaction_hash
WHERE LOWER(t.to_address) NOT IN (SELECT LOWER(addr) FROM UNNEST($AuthorizedPauseAddresses) AS addr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize query performance and add error handling.

The query could benefit from the following improvements:

  1. Add time-based filtering to limit the data scanned
  2. Add error handling for malformed event data
  3. Use appropriate index hints for better performance
   TransactionPausedByUnauthorizedAddress: |
     WITH log_transactions AS (
      SELECT DISTINCT transaction_hash
      FROM `bigquery-public-data.crypto_ethereum.logs`
      WHERE
      LOWER(address) = LOWER('$ContractAddress')
      AND topics[OFFSET(0)] = "$PauseEvent"
+     -- Add time-based filtering
+     AND block_timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
+     -- Add error handling for malformed data
+     AND array_length(topics) >= 1
      )
      SELECT t.hash as TX
      FROM `bigquery-public-data.crypto_ethereum.transactions` t
      INNER JOIN log_transactions l
      ON t.hash = l.transaction_hash
      WHERE LOWER(t.to_address) NOT IN (SELECT LOWER(addr) FROM UNNEST($AuthorizedPauseAddresses) AS addr)
+     -- Add ordering and limit for consistent pagination
+     ORDER BY t.block_timestamp DESC
+     LIMIT 1000
📝 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.

Suggested change
WITH log_transactions AS (
SELECT DISTINCT transaction_hash
FROM `bigquery-public-data.crypto_ethereum.logs`
WHERE
LOWER(address) = LOWER('$ContractAddress')
AND topics[OFFSET(0)] = "$PauseEvent"
)
SELECT t.hash as TX
FROM `bigquery-public-data.crypto_ethereum.transactions` t
INNER JOIN log_transactions l
ON t.hash = l.transaction_hash
WHERE LOWER(t.to_address) NOT IN (SELECT LOWER(addr) FROM UNNEST($AuthorizedPauseAddresses) AS addr)
WITH log_transactions AS (
SELECT DISTINCT transaction_hash
FROM `bigquery-public-data.crypto_ethereum.logs`
WHERE
LOWER(address) = LOWER('$ContractAddress')
AND topics[OFFSET(0)] = "$PauseEvent"
-- Add time-based filtering
AND block_timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
-- Add error handling for malformed data
AND array_length(topics) >= 1
)
SELECT t.hash as TX
FROM `bigquery-public-data.crypto_ethereum.transactions` t
INNER JOIN log_transactions l
ON t.hash = l.transaction_hash
WHERE LOWER(t.to_address) NOT IN (SELECT LOWER(addr) FROM UNNEST($AuthorizedPauseAddresses) AS addr)
-- Add ordering and limit for consistent pagination
ORDER BY t.block_timestamp DESC
LIMIT 1000

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
op-eventer/setup/query_exec.py (3)

25-31: Handle potential query failures gracefully.
The current approach doesn't capture exceptions from the BigQuery client. If a query or job fails, the script will terminate abruptly. Consider wrapping this logic in a try-except block and logging any failures for more robust error handling.


67-73: Consider batching or conditional alerting for large result sets.
Sending an alert per row could lead to high alert volumes. If the data set is large, it may overwhelm the metrics system. Consider adding batching logic or thresholds for triggering alerts.


82-90: Infinite loop may benefit from scheduling.
An infinite while True loop with a fixed sleep might be better replaced or supplemented by cron-like scheduling, or at least a break condition, to prevent indefinite running in certain environments.

op-eventer/setup/consumers/metrics.sh (2)

6-9: Quoting arguments in log function.
When using $*, unusual spacing or special characters could break log messages. Consider quoting arguments to preserve formatting.

-function log {
-  echo $(date "+%Y-%m-%d %H:%M:%S") $*
+function log {
+  echo "$(date "+%Y-%m-%d %H:%M:%S")" "$@"
 }

25-30: Validate expected script arguments.
The script assumes three arguments are always passed in. If arguments are missing, the script silently fails. Add a quick usage check to improve reliability.

op-eventer/setup/run.sh (1)

25-25: Validate monitoring configuration existence.
Before invoking monitoring.yaml, consider verifying that this file actually exists and is valid. If the file is missing or malformed, the script will exit without a clear error message.

op-eventer/monitoring/monitoring.yaml (1)

13-24: Optimize BigQuery performance and add time-based filtering.

The query could benefit from several optimizations:

  1. Add block timestamp/number filtering to limit the data scanned
  2. Handle potential null values in to_address
  3. Consider using a subquery for authorized addresses to improve readability
  WITH log_transactions AS (
   SELECT DISTINCT transaction_hash
   FROM `bigquery-public-data.crypto_ethereum.logs`
   WHERE
   LOWER(address) = LOWER('$ContractAddress')
   AND topics[OFFSET(0)] = "$PauseEvent"
+  AND block_timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
   )
   SELECT t.hash as TX
   FROM `bigquery-public-data.crypto_ethereum.transactions` t
   INNER JOIN log_transactions l
   ON t.hash = l.transaction_hash
-  WHERE LOWER(t.to_address) NOT IN (SELECT LOWER(addr) FROM UNNEST($AuthorizedPauseAddresses) AS addr)
+  WHERE t.to_address IS NOT NULL
+  AND LOWER(t.to_address) NOT IN (
+    SELECT LOWER(addr) 
+    FROM UNNEST($AuthorizedPauseAddresses) AS addr
+  )
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41c0114 and de56024.

📒 Files selected for processing (4)
  • op-eventer/monitoring/monitoring.yaml (1 hunks)
  • op-eventer/setup/consumers/metrics.sh (1 hunks)
  • op-eventer/setup/query_exec.py (1 hunks)
  • op-eventer/setup/run.sh (1 hunks)
🔇 Additional comments (3)
op-eventer/setup/consumers/metrics.sh (1)

4-4: Validate environment variables.
Sourcing ~/op-eventer.env is convenient, but ensure it includes all required variables (e.g., METRICS_URL, METRICS_USER). Otherwise, the script may fail without a clear error.

op-eventer/setup/run.sh (1)

12-23: Check for environment readiness.
Excellent job verifying credential presence and validity. For further resilience, you might also verify that gcloud is installed and initialized (e.g., user is logged in to the correct project), ensuring the subsequent BigQuery operations succeed.

op-eventer/monitoring/monitoring.yaml (1)

3-9: Verify contract addresses and authorized pause addresses across networks.

A few concerns about the configuration:

  1. The contract address is identical for both networks (0xbEb5Fc579115071764c7423A4f12eDde41f106Ed). This is unusual as contracts typically have different addresses on different networks.
  2. The authorized addresses are very similar (...b3b vs ...b3a), which could indicate a potential typo.
✅ Verification successful

Configuration is correct and follows expected patterns

The identical contract address (0xbEb5Fc579115071764c7423A4f12eDde41f106Ed) across networks is correct as it's the Optimism Portal contract. The different authorized pause addresses for Mainnet (...b3b) and Sepolia (...b3a) follow security best practices by using network-specific addresses.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify contract addresses and their deployment status

# Check contract existence and verify if they are different contracts
for NETWORK in "mainnet" "sepolia"; do
  echo "Checking $NETWORK contract at 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
  curl -s "https://api.etherscan.io/api?module=contract&action=getcontractcreation&contractaddresses=0xbEb5Fc579115071764c7423A4f12eDde41f106Ed&apikey=YourApiKeyToken" | jq .
done

# Check if the authorized addresses are valid
for ADDR in "0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3b" "0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a"; do
  echo "Checking address $ADDR"
  curl -s "https://api.etherscan.io/api?module=account&action=txlist&address=$ADDR&startblock=0&endblock=99999999&page=1&offset=1&sort=asc&apikey=YourApiKeyToken" | jq .
done

Length of output: 1933


Script:

#!/bin/bash
# Search for contract and authorized addresses in the codebase
echo "=== Searching for contract address ==="
rg -i "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed" -A 2 -B 2

echo -e "\n=== Searching for authorized addresses ==="
rg -i "0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3[ab]" -A 2 -B 2

echo -e "\n=== Looking for deployment configs ==="
fd -e json -e yaml -e toml "deploy|network|config" --exec head -n 20 {}

echo -e "\n=== Checking README for network info ==="
fd "README" --exec cat {}

Length of output: 49208

Comment on lines 34 to 37
def send_alert(script_path, source, metric_name, priority, fields):
subprocess.run(f"bash {script_path} '{metric_name}' '{source}' 'Priority={
priority},{fields} metric=1'", shell=True)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid potential command injection.
Using shell=True in subprocess.run could be risky if untrusted parameters are passed in. Consider passing arguments in a list form (with shell=False) or sanitizing them before constructing the command. For instance:

-def send_alert(script_path, source, metric_name, priority, fields):
-    subprocess.run(f"bash {script_path} '{metric_name}' '{source}' 'Priority={priority},{fields} metric=1'", shell=True)
+def send_alert(script_path, source, metric_name, priority, fields):
+    subprocess.run(
+        [
+            "bash",
+            script_path,
+            metric_name,
+            source,
+            f"Priority={priority},{fields} metric=1"
+        ],
+        shell=False
+    )
📝 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.

Suggested change
def send_alert(script_path, source, metric_name, priority, fields):
subprocess.run(f"bash {script_path} '{metric_name}' '{source}' 'Priority={
priority},{fields} metric=1'", shell=True)
def send_alert(script_path, source, metric_name, priority, fields):
subprocess.run(
[
"bash",
script_path,
metric_name,
source,
f"Priority={priority},{fields} metric=1"
],
shell=False
)

Comment on lines 26 to 40
Checks:
- Path: consumers/metrics.sh
Queries:
Mainnet-TransactionPausedByUnauthorizedAddress:
Description: "Alert when a transaction is paused by an unauthorized address"
Parameters: Mainnet
Query: TransactionPausedByUnauthorizedAddress
Priority: P1
Source: "test"
Sepolia-TransactionPausedByUnauthorizedAddress:
Description: "Alert when a transaction is paused by an unauthorized address"
Parameters: Sepolia
Query: TransactionPausedByUnauthorizedAddress
Priority: P2
Source: "test"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Address production readiness concerns in alert configuration.

A few suggestions to improve the alert configuration:

  1. Change Source: "test" to an appropriate production value
  2. Consider using absolute paths or environment variables for the consumer path
  3. Add alert grouping and cooldown periods to prevent alert storms
  - Path: consumers/metrics.sh
    Queries:
      Mainnet-TransactionPausedByUnauthorizedAddress:
        Description: "Alert when a transaction is paused by an unauthorized address"
        Parameters: Mainnet
        Query: TransactionPausedByUnauthorizedAddress
        Priority: P1
-       Source: "test"
+       Source: "production"
+       GroupBy: ["to_address"]
+       Cooldown: "15m"
      Sepolia-TransactionPausedByUnauthorizedAddress:
        Description: "Alert when a transaction is paused by an unauthorized address"
        Parameters: Sepolia
        Query: TransactionPausedByUnauthorizedAddress
        Priority: P2
-       Source: "test"
+       Source: "production"
+       GroupBy: ["to_address"]
+       Cooldown: "15m"
📝 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.

Suggested change
Checks:
- Path: consumers/metrics.sh
Queries:
Mainnet-TransactionPausedByUnauthorizedAddress:
Description: "Alert when a transaction is paused by an unauthorized address"
Parameters: Mainnet
Query: TransactionPausedByUnauthorizedAddress
Priority: P1
Source: "test"
Sepolia-TransactionPausedByUnauthorizedAddress:
Description: "Alert when a transaction is paused by an unauthorized address"
Parameters: Sepolia
Query: TransactionPausedByUnauthorizedAddress
Priority: P2
Source: "test"
Checks:
- Path: consumers/metrics.sh
Queries:
Mainnet-TransactionPausedByUnauthorizedAddress:
Description: "Alert when a transaction is paused by an unauthorized address"
Parameters: Mainnet
Query: TransactionPausedByUnauthorizedAddress
Priority: P1
Source: "production"
GroupBy: ["to_address"]
Cooldown: "15m"
Sepolia-TransactionPausedByUnauthorizedAddress:
Description: "Alert when a transaction is paused by an unauthorized address"
Parameters: Sepolia
Query: TransactionPausedByUnauthorizedAddress
Priority: P2
Source: "production"
GroupBy: ["to_address"]
Cooldown: "15m"

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
op-eventer/setup/alerts/op-eventer.yaml (1)

8-8: Fix extra quote in summary text.

Remove the extra quote character from the summary text in all alerts.

-          summary: OpEventer Emitted Alert"
+          summary: "OpEventer Emitted Alert"

Also applies to: 16-16, 24-24, 32-32, 40-40

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de56024 and d0e6bd8.

📒 Files selected for processing (6)
  • op-eventer/monitoring/monitoring.yaml (1 hunks)
  • op-eventer/setup/alerts/op-eventer.yaml (1 hunks)
  • op-eventer/setup/alerts/setup_alerts.sh (1 hunks)
  • op-eventer/setup/consumers/metrics.sh (1 hunks)
  • op-eventer/setup/query_exec.py (1 hunks)
  • op-eventer/setup/run.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • op-eventer/setup/consumers/metrics.sh
🔇 Additional comments (3)
op-eventer/monitoring/monitoring.yaml (2)

34-34: Address production readiness concerns in alert configuration.

Also applies to: 40-40


3-3: Verify contract addresses for different networks.

The same contract address is used for both Mainnet and Sepolia networks. Please verify if this is intentional.

Also applies to: 7-7

✅ Verification successful

Using the same contract address across networks is intentional

The contract is part of a monitoring system that tracks pause events, with properly configured network-specific settings and different priority levels (P1 for Mainnet, P2 for Sepolia).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the contract exists on both networks
# Note: Using etherscan API to verify contract existence

for NETWORK in "api" "api-sepolia"; do
  echo "Checking contract on ${NETWORK}..."
  curl -s "https://${NETWORK}.etherscan.io/api?module=contract&action=getabi&address=0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"
done

Length of output: 606


Script:

#!/bin/bash
# Search for files containing the contract address and network configurations
echo "Files containing the contract address:"
rg -l "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"

echo -e "\nFiles containing network configurations:"
rg -l -i "mainnet|sepolia" --type yaml --type json

echo -e "\nContent of relevant configuration files:"
fd -e yaml -e json -e js -e ts | xargs rg -l "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed" | xargs cat

Length of output: 4300

op-eventer/setup/query_exec.py (1)

34-37: Avoid potential command injection.

@@ -0,0 +1,5 @@
set -o pipefail -o errexit -o nounset

source ~/alerts_cred.env
Copy link
Contributor

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 missing credentials file.

The script should validate that the credentials file exists before attempting to source it.

-source ~/alerts_cred.env
+if [ ! -f ~/alerts_cred.env ]; then
+    echo "Error: ~/alerts_cred.env not found"
+    exit 1
+fi
+source ~/alerts_cred.env
📝 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.

Suggested change
source ~/alerts_cred.env
if [ ! -f ~/alerts_cred.env ]; then
echo "Error: ~/alerts_cred.env not found"
exit 1
fi
source ~/alerts_cred.env

Comment on lines +4 to +5
mimirtool rules diff --namespaces=op-eventer ./op-eventer.yaml
mimirtool rules sync --namespaces=op-eventer ./op-eventer.yaml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for op-eventer.yaml file.

The script should check if the YAML file exists before attempting to use it with mimirtool.

+if [ ! -f ./op-eventer.yaml ]; then
+    echo "Error: ./op-eventer.yaml not found"
+    exit 1
+fi
 mimirtool rules diff --namespaces=op-eventer ./op-eventer.yaml
 mimirtool rules sync --namespaces=op-eventer ./op-eventer.yaml
📝 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.

Suggested change
mimirtool rules diff --namespaces=op-eventer ./op-eventer.yaml
mimirtool rules sync --namespaces=op-eventer ./op-eventer.yaml
if [ ! -f ./op-eventer.yaml ]; then
echo "Error: ./op-eventer.yaml not found"
exit 1
fi
mimirtool rules diff --namespaces=op-eventer ./op-eventer.yaml
mimirtool rules sync --namespaces=op-eventer ./op-eventer.yaml

fi
fi

python $query_exec $MONITORING_DIR/monitoring.yaml
Copy link
Contributor

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 Python script execution.

The script should handle potential failures when executing the Python script.

-python $query_exec $MONITORING_DIR/monitoring.yaml 
+if [ ! -f "$MONITORING_DIR/monitoring.yaml" ]; then
+    echo "Error: $MONITORING_DIR/monitoring.yaml not found"
+    exit 1
+fi
+
+if ! python "$query_exec" "$MONITORING_DIR/monitoring.yaml"; then
+    echo "Error: Failed to execute $query_exec"
+    exit 1
+fi
📝 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.

Suggested change
python $query_exec $MONITORING_DIR/monitoring.yaml
if [ ! -f "$MONITORING_DIR/monitoring.yaml" ]; then
echo "Error: $MONITORING_DIR/monitoring.yaml not found"
exit 1
fi
if ! python "$query_exec" "$MONITORING_DIR/monitoring.yaml"; then
echo "Error: Failed to execute $query_exec"
exit 1
fi

Comment on lines +29 to +30
expr: increase(opEventer_metric{ priority="P3" }[230m]) > 0
for: 1s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect priority labels in P4 and P5 alerts.

The expressions for P4 and P5 alerts are incorrectly using priority="P3" instead of their respective priorities.

-        expr: increase(opEventer_metric{ priority="P3" }[230m]) > 0
+        expr: increase(opEventer_metric{ priority="P4" }[230m]) > 0
-        expr: increase(opEventer_metric{ priority="P3" }[230m]) > 0
+        expr: increase(opEventer_metric{ priority="P5" }[230m]) > 0

Also applies to: 37-38

Comment on lines +25 to +31
def run_query(query):
client = bigquery.Client()

query_job = client.query(query)
results = query_job.result()

return results
Copy link
Contributor

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 BigQuery client initialization and query execution.

The function should handle potential errors when initializing the client and executing queries.

 def run_query(query):
-    client = bigquery.Client()
+    try:
+        client = bigquery.Client()
+    except Exception as e:
+        print(f"Failed to initialize BigQuery client: {e}")
+        raise
 
-    query_job = client.query(query)
-    results = query_job.result()
+    try:
+        query_job = client.query(query)
+        results = query_job.result()
+    except Exception as e:
+        print(f"Failed to execute query: {e}")
+        raise
 
     return results
📝 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.

Suggested change
def run_query(query):
client = bigquery.Client()
query_job = client.query(query)
results = query_job.result()
return results
def run_query(query):
try:
client = bigquery.Client()
except Exception as e:
print(f"Failed to initialize BigQuery client: {e}")
raise
try:
query_job = client.query(query)
results = query_job.result()
except Exception as e:
print(f"Failed to execute query: {e}")
raise
return results

Comment on lines 82 to 90
while True:
with open(file_path, 'r') as file:
config = yaml.safe_load(file)

for name, query in process_queries(config):
print(f"\nProcessing {name} ")
# print(query)

time.sleep(10)
Copy link
Contributor

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 the main loop.

The infinite loop should handle exceptions and implement proper backoff strategy.

+    retry_count = 0
+    max_retries = 3
+    backoff_time = 10
+
     while True:
-        with open(file_path, 'r') as file:
-            config = yaml.safe_load(file)
+        try:
+            with open(file_path, 'r') as file:
+                config = yaml.safe_load(file)
 
-        for name, query in process_queries(config):
-            print(f"\nProcessing {name} ")
-            # print(query)
+            for name, query in process_queries(config):
+                print(f"\nProcessing {name} ")
 
-        time.sleep(10)
+            retry_count = 0
+            time.sleep(backoff_time)
+        except Exception as e:
+            print(f"Error in main loop: {e}")
+            retry_count += 1
+            if retry_count >= max_retries:
+                print("Max retries reached, exiting...")
+                sys.exit(1)
+            time.sleep(backoff_time * retry_count)

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
op-eventer/setup/alerts/setup_alerts.sh (1)

4-5: Add error handling between diff and sync operations.

While it's good practice to run diff before sync, we should ensure the diff operation succeeds before proceeding with sync.

-mimirtool rules diff --namespaces=op-eventer ./op-eventer.yaml
-mimirtool rules sync --namespaces=op-eventer ./op-eventer.yaml
+if ! mimirtool rules diff --namespaces=op-eventer ./op-eventer.yaml; then
+    echo "Error: Rules diff failed. Please review changes before sync." >&2
+    exit 1
+fi
+
+if ! mimirtool rules sync --namespaces=op-eventer ./op-eventer.yaml; then
+    echo "Error: Rules sync failed." >&2
+    exit 1
+fi
op-eventer/setup/run.sh (1)

4-7: Consider using more robust path handling.

While the current path construction works, it could be made more robust:

  1. Quote the paths to handle spaces
  2. Use readlink -f for true absolute paths
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+SCRIPT_DIR="$( cd "$( dirname "$(readlink -f "${BASH_SOURCE[0]}")" )" && pwd )"
-VENV_DIR="$SCRIPT_DIR/../../.venv"
+VENV_DIR="${SCRIPT_DIR}/../../.venv"
-MONITORING_DIR="$SCRIPT_DIR/../monitoring"
+MONITORING_DIR="${SCRIPT_DIR}/../monitoring"
op-eventer/setup/alerts/op-eventer.yaml (1)

6-9: Enhance alert annotations with detailed descriptions.

The current annotations are generic. Consider adding:

  • Specific impact description
  • Recommended actions
  • Link to runbook

Also applies to: 15-17, 23-25, 31-33, 39-41

op-eventer/monitoring/monitoring.yaml (1)

13-24: Optimize BigQuery query performance.

Consider these optimizations:

  1. Add date range filter to reduce scanned data
  2. Use partitioned tables if available
  3. Consider materializing frequently accessed data
     SELECT DISTINCT transaction_hash
     FROM `bigquery-public-data.crypto_ethereum.logs`
     WHERE
+    block_timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
+    AND
     LOWER(address) = LOWER('$ContractAddress')
     AND topics[OFFSET(0)] = "$PauseEvent"
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de56024 and c7765b7.

📒 Files selected for processing (6)
  • op-eventer/monitoring/monitoring.yaml (1 hunks)
  • op-eventer/setup/alerts/op-eventer.yaml (1 hunks)
  • op-eventer/setup/alerts/setup_alerts.sh (1 hunks)
  • op-eventer/setup/consumers/metrics.sh (1 hunks)
  • op-eventer/setup/query_exec.py (1 hunks)
  • op-eventer/setup/run.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • op-eventer/setup/consumers/metrics.sh
🔇 Additional comments (4)
op-eventer/setup/alerts/setup_alerts.sh (1)

1-1: Great use of shell safety options!

Setting pipefail, errexit, and nounset is excellent practice for robust shell scripting.

op-eventer/setup/run.sh (1)

12-24: LGTM! Secure credential handling.

Good practices observed:

  • Proper credential validation
  • Re-authentication on invalid credentials
  • Using application default credentials
op-eventer/monitoring/monitoring.yaml (1)

34-34: Address production readiness concerns.

The Source is still set to "test" which was previously flagged.

Also applies to: 40-40

op-eventer/setup/query_exec.py (1)

34-37: Fix command injection vulnerability.

Using shell=True with unvalidated input is unsafe.

@@ -0,0 +1,5 @@
set -o pipefail -o errexit -o nounset

source ~/alerts_cred.env
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider making the credentials path configurable and validate its existence.

The hardcoded path ~/alerts_cred.env could cause issues in different environments.

-source ~/alerts_cred.env
+ALERTS_CRED_PATH=${ALERTS_CRED_PATH:-"$HOME/alerts_cred.env"}
+if [[ ! -f "$ALERTS_CRED_PATH" ]]; then
+    echo "Error: Credentials file not found at $ALERTS_CRED_PATH" >&2
+    exit 1
+fi
+source "$ALERTS_CRED_PATH"
📝 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.

Suggested change
source ~/alerts_cred.env
ALERTS_CRED_PATH=${ALERTS_CRED_PATH:-"$HOME/alerts_cred.env"}
if [[ ! -f "$ALERTS_CRED_PATH" ]]; then
echo "Error: Credentials file not found at $ALERTS_CRED_PATH" >&2
exit 1
fi
source "$ALERTS_CRED_PATH"

Comment on lines +29 to +30
expr: increase(opEventer_metric{ priority="P3" }[230m]) > 0
for: 1s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix priority mismatch in P4 and P5 alert expressions.

The expressions for P4 and P5 alerts incorrectly check for P3 priority metrics.

-        expr: increase(opEventer_metric{ priority="P3" }[230m]) > 0
+        expr: increase(opEventer_metric{ priority="P4" }[230m]) > 0
-        expr: increase(opEventer_metric{ priority="P3" }[230m]) > 0
+        expr: increase(opEventer_metric{ priority="P5" }[230m]) > 0

Also applies to: 37-38

Comment on lines +25 to +31
def run_query(query):
client = bigquery.Client()

query_job = client.query(query)
results = query_job.result()

return results
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for BigQuery operations.

The query execution lacks proper error handling.

 def run_query(query):
     client = bigquery.Client()
-
-    query_job = client.query(query)
-    results = query_job.result()
-
-    return results
+    try:
+        query_job = client.query(query)
+        return query_job.result()
+    except Exception as e:
+        print(f"Error executing query: {e}")
+        return []
📝 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.

Suggested change
def run_query(query):
client = bigquery.Client()
query_job = client.query(query)
results = query_job.result()
return results
def run_query(query):
client = bigquery.Client()
try:
query_job = client.query(query)
return query_job.result()
except Exception as e:
print(f"Error executing query: {e}")
return []

Comment on lines 82 to 90
while True:
with open(file_path, 'r') as file:
config = yaml.safe_load(file)

for name, query in process_queries(config):
print(f"\nProcessing {name} ")
# print(query)

time.sleep(10)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve main loop robustness.

The infinite loop needs better error handling and graceful shutdown.

     while True:
-        with open(file_path, 'r') as file:
-            config = yaml.safe_load(file)
-
-        for name, query in process_queries(config):
-            print(f"\nProcessing {name} ")
-            # print(query)
-
-        time.sleep(10)
+        try:
+            with open(file_path, 'r') as file:
+                config = yaml.safe_load(file)
+
+            for name, query in process_queries(config):
+                print(f"\nProcessing {name} ")
+
+            time.sleep(10)
+        except Exception as e:
+            print(f"Error in main loop: {e}")
+            time.sleep(30)  # Back off on 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.

Suggested change
while True:
with open(file_path, 'r') as file:
config = yaml.safe_load(file)
for name, query in process_queries(config):
print(f"\nProcessing {name} ")
# print(query)
time.sleep(10)
while True:
try:
with open(file_path, 'r') as file:
config = yaml.safe_load(file)
for name, query in process_queries(config):
print(f"\nProcessing {name} ")
time.sleep(10)
except Exception as e:
print(f"Error in main loop: {e}")
time.sleep(30) # Back off on error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant