Skip to content

Conversation

@neekolas
Copy link
Contributor

@neekolas neekolas commented Mar 13, 2025

TL;DR

Added a utility package for starting and managing Anvil Ethereum test nodes in tests.

Issues

#630

What changed?

  • Created a new anvil package under pkg/testutils with functions to:
    • Find free network ports
    • Start Anvil instances with proper cleanup
    • Wait for Anvil to be ready before proceeding with tests
  • Added comprehensive tests that verify:
    • Basic Anvil startup and connection
    • Concurrent operation of multiple Anvil instances

How to test?

Run the tests in the new package:

go test -v ./pkg/testutils/anvil

The tests will verify that Anvil can be started, connected to, and that multiple instances can run concurrently.

Why make this change?

This utility simplifies testing code that interacts with Ethereum by providing a consistent way to spin up local Anvil instances. It handles port allocation, startup synchronization, and proper cleanup, making blockchain-dependent tests more reliable and easier to write.

Summary by CodeRabbit

  • New Features

    • Introduced a testing utility that automates launching local test instances with dynamic port allocation and readiness verification.
    • Added a method to access the RatesManager instance within the RatesAdmin struct.
    • Added new metrics for tracking performance in payer operations, including node publishing duration and current nonce.
    • Introduced functions to retrieve absolute maximum and minimum payload sizes in the GroupMessages and IdentityUpdates contracts.
  • Tests

    • Added comprehensive tests to verify both single and concurrent initialization of test instances, ensuring reliable startup and proper connection setup.
    • Updated tests to utilize a local Ethereum node for improved contract deployment and error handling.
    • Restructured test cases for GroupMessages and IdentityUpdates contracts, enhancing clarity and robustness.
    • Enhanced tests for the RatesManager contract to cover new functionalities and edge cases.
    • Removed deprecated functions and replaced them with updated utility functions for improved port allocation in test cases.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2025

Walkthrough

A new testing utility package named anvil has been introduced. It provides functions to locate an available TCP port, wait for an Anvil instance to be ready, and start the Anvil process with optional logging and cleanup support. Additionally, test cases were added to validate single and concurrent startup scenarios of the Anvil instance by confirming successful chain ID retrieval through a blockchain client.

Changes

File(s) Change Summary
pkg/testutils/anvil/anvil.go Added new package anvil with functions: waitForAnvil to check Anvil readiness, and StartAnvil to initiate the process and return cleanup functionality.
pkg/testutils/anvil/anvil_test.go Introduced tests: TestStartAnvil validates Anvil instance startup, TestCleanup checks cleanup functionality, and TestStartConcurrent verifies concurrent initialization of multiple Anvil instances with proper client connectivity.
pkg/blockchain/blockchainPublisher_test.go Modified buildPublisher to use anvil.StartAnvil for obtaining rpcUrl and updated cleanup handling in tests.
pkg/blockchain/migrator/migrator_test.go Updated setupRegistry to start an Anvil instance using anvil.StartAnvil for local blockchain setup.
pkg/blockchain/ratesAdmin.go Added method Contract to RatesAdmin struct to access the RatesManager instance.
pkg/blockchain/ratesAdmin_test.go Renamed package to blockchain_test, updated buildRatesAdmin to return a pointer to blockchain.RatesAdmin, and modified contract deployment to use anvil.StartAnvil.
pkg/blockchain/registryAdmin_test.go Updated return types in buildRegistry and addRandomNode to use qualified types from blockchain package and modified contract deployment logic.
pkg/blockchain/registryCaller_test.go Changed package name to blockchain_test to indicate it is a test file.
pkg/blockchain/rpcLogStreamer_test.go Added import for anvil and modified test setup to start an Anvil instance for testing.
pkg/indexer/e2e_test.go Updated startIndexing and messagePublisher functions to use new contract options and modified test to reflect these changes.
pkg/indexer/storer/groupMessage_test.go Integrated anvil for starting an Anvil instance in buildGroupMessageStorer, updated contract deployment logic.
pkg/indexer/storer/identityUpdate_test.go Modified buildIdentityUpdateStorer to use anvil for obtaining rpcUrl and updated contract deployment accordingly.
pkg/testutils/config.go Removed rootPath and getContractAddress, replaced GetContractsOptions with NewContractsOptions that takes rpcUrl as a parameter.
pkg/testutils/contracts.go Updated deployContract and related functions to accept rpcUrl as a parameter for enhanced flexibility in contract deployment.
pkg/testutils/contracts_test.go Modified tests to utilize anvil for starting an Anvil instance and updated contract deployment calls to include rpcUrl.

Sequence Diagram(s)

sequenceDiagram
    participant T as Test
    participant S as StartAnvil
    participant F as findFreePort
    participant C as AnvilProcess
    participant W as waitForAnvil
    participant BC as BlockchainClient

    T->>S: Call StartAnvil(showLogs)
    S->>F: findFreePort()
    F-->>S: Return free port
    S->>C: Start Anvil process on free port
    S->>W: waitForAnvil(url)
    W->>BC: Request chain ID
    BC-->>W: Return chain ID or error
    W-->>S: Confirm Anvil ready
    S-->>T: Return URL and cleanup function
Loading
sequenceDiagram
    participant T as Test
    participant CR as ConcurrentRunner
    participant A1 as AnvilInstance1
    participant A2 as AnvilInstance2
    note over CR: ... up to 10 instances
    T->>CR: Start concurrent initialization (goroutines)
    CR->>A1: StartAnvil (instance 1)
    CR->>A2: StartAnvil (instance 2)
    A1-->>CR: Return URL & cleanup function
    A2-->>CR: Return URL & cleanup function
    note over T,CR: Validate each instance via blockchain client queries
Loading

Possibly related PRs

  • solidity: adjust min and max sizes dynamically #451: The changes in the main PR, which introduce new functions for managing an Anvil instance and testing it, are related to the retrieved PR, which modifies the GroupMessages and IdentityUpdates contracts to dynamically adjust minimum and maximum payload sizes.
  • Add payer metrics #622: The changes in the main PR, which introduce new utility functions for managing an Anvil instance in a testing environment, are related to the changes in the retrieved PR, as both involve the integration of metrics tracking and enhancements in the context of testing and performance monitoring.
  • Make RatesManager upgradeable and deployable #559: The changes in the main PR, which introduce new utility functions for managing an Anvil instance, are related to the retrieved PR, as both involve modifications to the RatesManager contract and its deployment process, specifically through the use of the anvil package for testing and deployment purposes.

Suggested reviewers

  • fbac
  • mkysel

Suggested labels

indexer

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 golangci-lint (1.62.2)

Error: can't load config: the Go language version (go1.23) used to build golangci-lint is lower than the targeted Go version (1.24)
Failed executing command with error: can't load config: the Go language version (go1.23) used to build golangci-lint is lower than the targeted Go version (1.24)

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • 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.
  • @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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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 Author

neekolas commented Mar 13, 2025

This stack of pull requests is managed by Graphite. Learn more about stacking.

@neekolas neekolas marked this pull request as ready for review March 13, 2025 16:34
@neekolas neekolas requested a review from a team as a code owner March 13, 2025 16:34
@neekolas neekolas force-pushed the 03-13-test_utility_to_start_an_anvil_instance branch from 8e49750 to 353362d Compare March 13, 2025 16:35
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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 3

🧹 Nitpick comments (5)
pkg/testutils/anvil/anvil_test.go (2)

34-40: Consider adding context with timeout for goroutines

The goroutines launching Anvil instances don't have a timeout mechanism. If an instance fails to start, the test could hang indefinitely.

 	// Launch goroutines to start anvil instances
 	for i := 0; i < numInstances; i++ {
 		go func() {
+			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+			defer cancel()
 			url, cleanup := StartAnvil(t, false)
 			results <- anvilInstance{url: url, cleanup: cleanup}
 		}()
 	}

47-49: Potential nil dereference risk in cleanup function

This check is reasonable, but consider adding a log message if cleanup is nil as it would indicate an unexpected state.

 		if instance.cleanup != nil {
 			defer instance.cleanup()
+		} else {
+			t.Logf("Warning: nil cleanup function for instance %d", i)
 		}
pkg/testutils/anvil/anvil.go (3)

26-54: Remove unreachable code after t.Fatalf

The return statement after t.Fatalf will never be executed since t.Fatalf terminates the test.

 		case <-ctx.Done():
 			t.Fatalf("Timed out waiting for Anvil to start: %v", ctx.Err())
-			return
 		default:

40-52: Consider adding exponential backoff for polling

The current implementation uses a fixed 100ms sleep between retries. An exponential backoff strategy might be more efficient for handling varying startup times.

+			// Calculate backoff time (starts at 100ms, doubles each retry up to 1s)
+			backoff := 100 * time.Millisecond
+			// Use backoff time for sleep, doubling it each retry up to 1 second
+			if backoff < time.Second {
+				backoff *= 2
+			}
-			time.Sleep(100 * time.Millisecond)
+			time.Sleep(backoff)

72-75: Error handling in cleanup function

The cleanup function silently ignores errors. Consider logging errors for better debugging, especially in test environments.

 	return url, func() {
-		_ = cmd.Process.Kill()
-		_ = cmd.Wait()
+		if err := cmd.Process.Kill(); err != nil {
+			fmt.Fprintf(os.Stderr, "Error killing Anvil process: %v\n", err)
+		}
+		if err := cmd.Wait(); err != nil && !isKilledProcessError(err) {
+			fmt.Fprintf(os.Stderr, "Error waiting for Anvil process to exit: %v\n", err)
+		}
 	}

You would also need to add a helper function to check if the error is due to the process being killed:

func isKilledProcessError(err error) bool {
    // Different OS may return different errors when a process is killed
    return strings.Contains(err.Error(), "signal: killed") || 
           strings.Contains(err.Error(), "process already finished")
}
🛑 Comments failed to post (3)
pkg/testutils/anvil/anvil_test.go (1)

1-1: ⚠️ Potential issue

Package name mismatch causing build failure

The package name should be anvil to match the implementation file, not testutils. This mismatch is causing the pipeline failure.

-package testutils
+package anvil
📝 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.

package anvil
🧰 Tools
🪛 GitHub Actions: Lint

[error] 1-1: found packages anvil (anvil.go) and testutils (anvil_test.go) in pkg/testutils/anvil (typecheck)

pkg/testutils/anvil/anvil.go (2)

60-60: 💡 Verification agent

🧩 Analysis chain

Verify anvil binary availability

The code assumes that the anvil binary is available in the PATH. Consider adding a check at startup or documenting this requirement.


🏁 Script executed:

#!/bin/bash
# Check if anvil is installed and available in PATH
which anvil >/dev/null 2>&1
if [ $? -ne 0 ]; then
  echo "❌ 'anvil' binary not found in PATH. Make sure it's installed."
  exit 1
else
  echo "✅ 'anvil' binary found in PATH: $(which anvil)"
fi

Length of output: 184


Action Required: Implement a Runtime Check or Document the 'anvil' Binary Requirement

The test script confirmed that the anvil binary is missing from the PATH on your system. Since the current code assumes its presence (as seen in the exec.Command("anvil", "--port", …) call), you should either add a runtime check to verify its availability—using something like Go’s exec.LookPath("anvil")—or explicitly document this dependency so that users know to install it beforehand.

  • Suggestion:
    • Runtime Check: Before invoking the command, check with:
      if _, err := exec.LookPath("anvil"); err != nil {
        log.Fatalf("anvil binary not found in PATH; please ensure it is installed")
      }
    • Documentation: If you prefer not to add a runtime check, update your README or startup instructions to state that the anvil binary must be available in the PATH.

1-1: ⚠️ Potential issue

Package name mismatch causing build failure

The package should be named testutils to align with the directory structure and avoid conflicts with the test file. This inconsistency is causing the pipeline failure.

-package anvil
+package testutils

Alternatively, if you prefer to keep this package name as anvil, then you should update the test file to match:

// In anvil_test.go
-package testutils
+package anvil
📝 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.

package testutils
🧰 Tools
🪛 GitHub Check: Lint-Go

[failure] 1-1:
: found packages anvil (anvil.go) and testutils (anvil_test.go) in pkg/testutils/anvil (typecheck)

🪛 GitHub Actions: Lint

[error] 1-1: found packages anvil (anvil.go) and testutils (anvil_test.go) in pkg/testutils/anvil (typecheck)

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: 1

🧹 Nitpick comments (3)
pkg/testutils/anvil/anvil_test.go (1)

47-49: Consider adding error handling for nil cleanup functions

While the current check prevents calling nil cleanup functions, consider logging a warning when this condition occurs as it may indicate an unexpected state.

if instance.cleanup != nil {
    defer instance.cleanup()
+} else {
+    t.Logf("Warning: nil cleanup function for instance with URL %s", instance.url)
}
pkg/testutils/anvil/anvil.go (2)

26-54: Consider implementing exponential backoff for more efficient polling

While the current fixed interval polling works, implementing exponential backoff would be more efficient for handling varying startup times.

- // Wait a bit before trying again
- time.Sleep(100 * time.Millisecond)
+ // Use exponential backoff
+ waitTime := time.Duration(attempt*50) * time.Millisecond
+ if waitTime > 500*time.Millisecond {
+     waitTime = 500 * time.Millisecond
+ }
+ time.Sleep(waitTime)

You would need to add an attempt counter to track the number of retries.


72-75: Improve error handling in cleanup function

The current cleanup function ignores errors from Kill() and Wait(). Consider logging these errors to help troubleshoot issues during test cleanup.

return url, func() {
-   _ = cmd.Process.Kill()
-   _ = cmd.Wait()
+   if err := cmd.Process.Kill(); err != nil {
+       t.Logf("Error killing anvil process: %v", err)
+   }
+   if err := cmd.Wait(); err != nil && !isExpectedKillError(err) {
+       t.Logf("Error waiting for anvil process to exit: %v", err)
+   }
}

// Helper function to determine if an error is expected after killing the process
func isExpectedKillError(err error) bool {
    return err.Error() == "signal: killed" || err.Error() == "process already finished"
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e49750 and 353362d.

📒 Files selected for processing (2)
  • pkg/testutils/anvil/anvil.go (1 hunks)
  • pkg/testutils/anvil/anvil_test.go (1 hunks)
🔇 Additional comments (3)
pkg/testutils/anvil/anvil_test.go (2)

11-21: LGTM: Well-structured test for basic Anvil startup

The test effectively verifies both the startup and connectivity of an Anvil instance by checking for successful connection and chain ID retrieval.


23-65: Effective concurrent testing approach

This test properly validates the concurrent operation of multiple Anvil instances, which is important for ensuring thread safety in test environments. The use of channels, goroutines, and deferred cleanup functions is well structured.

pkg/testutils/anvil/anvil.go (1)

17-24: LGTM: Effective port allocation

The function correctly finds an available TCP port by letting the system assign one, then retrieving that assigned port number.

@neekolas neekolas force-pushed the 03-13-test_utility_to_start_an_anvil_instance branch from 353362d to 18c0a33 Compare March 13, 2025 16:38
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: 0

🧹 Nitpick comments (4)
pkg/testutils/anvil/anvil_test.go (4)

51-57: Consider adding context with timeout for goroutines

While the concurrent startup approach is good, consider adding a context with timeout to prevent potential hanging if an Anvil instance fails to start properly.

+       ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+       defer cancel()
        go func() {
-           url, cleanup := StartAnvil(t, false)
+           url, cleanup := StartAnvil(t, false) // Consider passing ctx if StartAnvil supports it
            results <- anvilInstance{url: url, cleanup: cleanup}
        }()

59-67: Ensure proper cleanup in failure scenarios

The current implementation defers cleanup for each instance, but if a test assertion fails before all instances are collected, some goroutines might be left running. Consider ensuring cleanup happens even if the test fails partway through.

    // Collect all instances
    instances := make([]anvilInstance, 0, numInstances)
+   defer func() {
+       // Clean up any collected instances in case of early test failure
+       for _, instance := range instances {
+           if instance.cleanup != nil {
+               instance.cleanup()
+           }
+       }
+   }()

    for range numInstances {
        instance := <-results
        instances = append(instances, instance)
-       if instance.cleanup != nil {
-           defer instance.cleanup()
-       }
    }

61-63: Add timeout to channel receive operations

To prevent the test from hanging if some goroutines fail to send results, consider adding a timeout to the channel receive operations.

    for range numInstances {
-       instance := <-results
+       select {
+       case instance := <-results:
+           instances = append(instances, instance)
+       case <-time.After(10 * time.Second):
+           t.Fatalf("Timeout waiting for anvil instance to start")
+       }
-       instances = append(instances, instance)
    }

3-9: Consider adding timeout-related imports

If you implement the suggested timeout improvements, you'll need to add the time package to your imports.

import (
    "context"
    "testing"
+   "time"

    "github.com/stretchr/testify/require"
    "github.com/xmtp/xmtpd/pkg/blockchain"
)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 353362d and 18c0a33.

📒 Files selected for processing (2)
  • pkg/testutils/anvil/anvil.go (1 hunks)
  • pkg/testutils/anvil/anvil_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/testutils/anvil/anvil.go
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Push Docker Images to GitHub Packages (xmtpd-cli)
  • GitHub Check: Test (Node)
  • GitHub Check: Push Docker Images to GitHub Packages (xmtpd)
  • GitHub Check: Build pre-baked anvil-xmtpd
🔇 Additional comments (5)
pkg/testutils/anvil/anvil_test.go (5)

11-21: Good test structure for verifying basic Anvil startup

This test correctly validates that the Anvil instance starts and can be connected to via a blockchain client. The use of defer for cleanup is a good practice that ensures resources are released even if the test fails.


23-38: Good validation of cleanup functionality

This test effectively verifies that the cleanup function properly terminates the Anvil instance by checking that subsequent blockchain operations fail with an error. This is a comprehensive approach to testing the cleanup functionality.


44-49: Well-structured type for test coordination

The anvilInstance struct provides a clean way to organize the URL and cleanup function for each test instance.


71-82: Good detailed verification of each instance

The test properly verifies each Anvil instance with detailed error messages that include the instance index. This will make debugging failures much easier.


40-83: Comprehensive test for concurrent Anvil startup

This test effectively validates that multiple Anvil instances can be started concurrently without conflicts. The approach of using goroutines and channels is appropriate for this use case, and the verification steps ensure each instance is fully functional.

deluca-mike and others added 9 commits March 13, 2025 14:59
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit (edited by Mike)

- **New Features**
	- Added new configuration options for fuzz testing and formatting.
- Introduced new functions for setting minimum and maximum payload sizes
in messaging and identity updates.

- **Refactor**
- Streamlined contract structures and deployment paths across messaging,
identity, node, and rates management systems.
	- Enhanced error handling and clarity in contracts and tests.
- Reorganized import statements and inheritance structures for improved
readability and maintainability.
- Refactored for pure unit tests using harnesses for low-level setters
and getters.

- **Tests**
- Expanded test coverage with consistent naming conventions and fuzz
testing for more robust validation.
- Improved testing framework for node management and rates management
functionalities.
- Enhanced error handling in tests to ensure proper validation of access
controls and input conditions.

- **Chore**
- Consolidated internal setup and tooling, improving overall system
clarity.

- **Bug Fixes**
- Corrected issues with visibility and inheritance in several contracts
to ensure proper functionality.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Far from complete, but a step in the right direction.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced enhanced performance tracking for key operations, enabling
more accurate measurement of processing times, retry counts, and
activity volumes.
- Improved observability across publishing workflows and nonce
management for deeper insights into system performance.
  
- **Chores**
- Expanded metrics collection to support proactive diagnostics and
overall system reliability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Closes #486
- Simplify upgrade test logic by using testcontainers.
- Continue on error in upgrade tests, instead make a comment with test
results.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a new GitHub Actions workflow for automated upgrade
testing.
- **Chores**
- Updated multiple dependencies to boost stability, performance, and
security.
- **Tests**
  - Enhanced functionality to pull Docker images directly in tests.
- Streamlined the upgrade test setup, including a dedicated test
validating the latest development version.
  - Improved error handling and context management in test functions.
  - Refined container-based tests for improved reliability and clarity.
- Improved version management in upgrade tests for better structure and
clarity.
  - Reordered import statements in test files for better organization.
- Removed the "Run Upgrade Tests" step from the GitHub Actions workflow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved error handling during event processing to ensure continuity
even if blockchain queries fail, preventing unnecessary interruptions in
operations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### TL;DR

Refactored blockchain tests to use Anvil for isolated test environments

## Issues
#630

### What changed?

- Modified blockchain test files to use isolated Anvil instances for
each test
- Updated contract deployment functions to accept an RPC URL parameter
- Added proper cleanup of Anvil instances after tests complete
- Added a `Contract()` accessor method to `RatesAdmin` to expose the
underlying contract
- Moved blockchain package tests to `blockchain_test` package for better
isolation
- Replaced `defer cleanup()` with `t.Cleanup(cleanup)` in some tests
- Added `NewContractsOptions` function that accepts an RPC URL parameter
- Fixed initialization of deployed contracts in test utilities

### How to test?

Run the blockchain tests:
```
go test ./pkg/blockchain/... -v
```

### Why make this change?

This change improves test reliability by ensuring each test runs in an
isolated blockchain environment. By using separate Anvil instances for
each test, we eliminate potential interference between tests and make
them more deterministic. The code also properly cleans up resources
after tests complete, preventing resource leaks.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a new administrative interface that enables direct access
to rate management functionality.

- **Quality Improvements**
- Upgraded blockchain integration and configuration to support dynamic
contract deployments.
- Streamlined resource cleanup and testing processes to enhance system
stability and reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Refactored blockchain tests to use Anvil for isolated test environments

#630

- Modified blockchain test files to use isolated Anvil instances for
each test
- Updated contract deployment functions to accept an RPC URL parameter
- Added proper cleanup of Anvil instances after tests complete
- Added a `Contract()` accessor method to `RatesAdmin` to expose the
underlying contract
- Moved blockchain package tests to `blockchain_test` package for better
isolation
- Replaced `defer cleanup()` with `t.Cleanup(cleanup)` in some tests
- Added `NewContractsOptions` function that accepts an RPC URL parameter
- Fixed initialization of deployed contracts in test utilities

Run the blockchain tests:
```
go test ./pkg/blockchain/... -v
```

This change improves test reliability by ensuring each test runs in an
isolated blockchain environment. By using separate Anvil instances for
each test, we eliminate potential interference between tests and make
them more deterministic. The code also properly cleans up resources
after tests complete, preventing resource leaks.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

- **New Features**
- Introduced a new administrative interface that enables direct access
to rate management functionality.

- **Quality Improvements**
- Upgraded blockchain integration and configuration to support dynamic
contract deployments.
- Streamlined resource cleanup and testing processes to enhance system
stability and reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…b.com:xmtp/xmtpd into 03-13-test_utility_to_start_an_anvil_instance
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: 0

🔭 Outside diff range comments (1)
pkg/testutils/config.go (1)

10-10: 💡 Verification agent

🧩 Analysis chain

Potentially unused constant.

The constant BLOCKCHAIN_RPC_URL appears to be no longer used after the introduction of the Anvil utility, as RPC URLs are now dynamically generated.


🏁 Script executed:

#!/bin/bash
# Check if BLOCKCHAIN_RPC_URL is used elsewhere in the codebase
echo "Checking for usage of BLOCKCHAIN_RPC_URL..."
rg "BLOCKCHAIN_RPC_URL" --type go

Length of output: 205


Action Required: Remove the Unused Constant

Based on the search results, the constant BLOCKCHAIN_RPC_URL is only defined in pkg/testutils/config.go and isn’t referenced anywhere else. This aligns with the switch to dynamic RPC URL generation using Anvil.

  • Recommendation: Remove the constant if it’s no longer needed. If it should be retained for potential fallback or documentation purposes, please add a clarifying comment to justify its presence.
🧹 Nitpick comments (2)
pkg/testutils/contracts_test.go (1)

12-14: Well-structured Anvil usage with proper cleanup.

Good implementation of starting an Anvil instance with proper cleanup using defer. The false parameter likely controls logging verbosity, though a comment explaining this would be helpful.

Consider adding a brief comment explaining what the false parameter to StartAnvil controls:

-	rpcUrl, cleanup := anvil.StartAnvil(t, false)
+	// false parameter disables verbose logging
+	rpcUrl, cleanup := anvil.StartAnvil(t, false)
pkg/blockchain/blockchainPublisher_test.go (1)

50-54: Resource cleanup could be more explicit.

While defer cleanup() ensures Anvil will be cleaned up, it might be clearer to structure the cleanup sequence explicitly to ensure the Anvil instance is cleaned up before other resources.

Consider restructuring the cleanup function for more explicit ordering:

return publisher, func() {
-		defer cleanup()
		cancel()
		client.Close()
+		cleanup()
	}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18c0a33 and 63dfafe.

📒 Files selected for processing (13)
  • pkg/blockchain/blockchainPublisher_test.go (3 hunks)
  • pkg/blockchain/migrator/migrator_test.go (3 hunks)
  • pkg/blockchain/ratesAdmin.go (1 hunks)
  • pkg/blockchain/ratesAdmin_test.go (2 hunks)
  • pkg/blockchain/registryAdmin_test.go (1 hunks)
  • pkg/blockchain/registryCaller_test.go (1 hunks)
  • pkg/blockchain/rpcLogStreamer_test.go (2 hunks)
  • pkg/indexer/e2e_test.go (3 hunks)
  • pkg/indexer/storer/groupMessage_test.go (2 hunks)
  • pkg/indexer/storer/identityUpdate_test.go (3 hunks)
  • pkg/testutils/config.go (1 hunks)
  • pkg/testutils/contracts.go (4 hunks)
  • pkg/testutils/contracts_test.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • pkg/blockchain/registryCaller_test.go
🔇 Additional comments (42)
pkg/testutils/contracts.go (5)

107-107: Improved method signature with dynamic RPC URL

The updated function signature now accepts an rpcUrl parameter, which makes the utility more flexible by eliminating the dependency on a hardcoded RPC URL.


122-122: Good implementation of dynamic RPC URL

The client initialization now properly uses the provided rpcUrl parameter instead of the hardcoded constant.


145-149: Enhanced error handling for GroupMessages contract deployment

The added error checks using require.NoError improve the robustness of the test utility by preventing silent failures during contract deployment and initialization.


152-156: Enhanced error handling for IdentityUpdates contract deployment

Similar to the GroupMessages contract, the added error checks ensure proper validation of contract deployment and initialization.


179-193: Updated public function signatures to support dynamic RPC URLs

All exported deployment functions now properly accept and pass along the rpcUrl parameter, maintaining a consistent interface across the package.

pkg/blockchain/ratesAdmin.go (1)

86-88: Added useful getter method for contract access

This getter method provides controlled access to the internal contract instance, following good encapsulation practices while enabling test code to interact with the contract directly.

pkg/blockchain/rpcLogStreamer_test.go (3)

9-12: Added import for the new anvil package

The import statement properly includes the new testing utility package needed for starting Anvil instances.


48-49: Improved test setup with Anvil integration

The test now uses the new anvil utility to start a test Ethereum node and correctly registers the cleanup function using t.Cleanup() to ensure resources are properly released after the test completes.


52-52: Properly using dynamic RPC URL

The client initialization now uses the RPC URL provided by the Anvil instance, ensuring consistency in the test environment.

pkg/blockchain/migrator/migrator_test.go (3)

13-13: Added import for the new anvil package

The import statement properly includes the new testing utility package needed for starting Anvil instances.


22-24: Enhanced test setup with Anvil integration

The setupRegistry function now uses the anvil utility to start a test Ethereum node and creates contract options with the dynamic RPC URL. The contract deployment also correctly passes the RPC URL to maintain consistency.


51-51: Proper resource cleanup

The cleanup function correctly defers the anvil cleanup, ensuring that the Anvil instance is properly shut down when tests complete.

pkg/testutils/contracts_test.go (2)

8-8: Good addition of the anvil package import.

The anvil package is properly imported to support the new test utility for starting Ethereum test nodes.


19-21: Consistent implementation across test functions.

Good consistency in how Anvil is started and cleaned up across different test functions.

pkg/indexer/storer/identityUpdate_test.go (3)

18-18: Good addition of the anvil package import.

The anvil package is properly imported to support starting Ethereum test nodes.


29-32: Good refactoring using the new Anvil utility.

The code now properly starts an Anvil instance for testing and uses the new configuration approach with RPC URL. The contract deployment is handled appropriately.


45-45: Appropriate resource cleanup.

Using defer anvilCleanup() ensures the Anvil instance is properly cleaned up after the test completes.

pkg/testutils/config.go (1)

16-23: Good refactoring of contract options.

The new NewContractsOptions function improves flexibility by accepting a dynamic RPC URL parameter rather than using a hardcoded value, which aligns well with the Anvil utility's approach of using dynamically allocated ports.

pkg/blockchain/blockchainPublisher_test.go (3)

9-11: Good organization of imports.

The imports are well-organized with the new anvil package import.


19-27: Good implementation of Anvil for testing.

The code properly starts an Anvil instance and deploys contracts with the right parameters. The comments explaining what's happening are helpful.


59-59: Good use of t.Cleanup instead of defer.

Using t.Cleanup(cleanup) is a better practice for tests than defer cleanup() as it ensures cleanup happens even if the test fails. This change is consistent with Go testing best practices.

Also applies to: 114-114

pkg/indexer/storer/groupMessage_test.go (3)

15-15: Good addition of the new anvil test utility package.

This import enables the use of the new anvil testing utilities, which will help improve test reliability by providing a standardized way to start and manage Anvil blockchain instances.


25-28: Well-implemented Anvil instance initialization.

The code effectively starts an Anvil instance for testing and properly configures the contracts using the RPC URL. This approach provides better isolation for tests compared to potentially shared test environments.


40-40: Proper resource cleanup with anvilCleanup.

Good practice to include the Anvil cleanup function in the test teardown to ensure resources are properly released after test completion.

pkg/blockchain/ratesAdmin_test.go (6)

1-1: Good package naming convention applied.

Changing to blockchain_test follows Go's recommended practice of using a separate package for tests. This avoids circular imports and provides clearer separation between implementation and tests.


11-13: Appropriate imports to support package refactoring.

The addition of the blockchain package import is necessary due to the package name change, and the anvil package import enables the new testing approach.


16-16: Clear return type declaration.

Explicitly specifying the return type as *blockchain.RatesAdmin improves code readability and maintainability.


19-22: Well-structured Anvil setup with proper cleanup.

Starting an Anvil instance and registering cleanup with t.Cleanup() ensures reliable test execution and resource management. The switch to NewContractsOptions with an explicit RPC URL makes the test setup more explicit.


53-54: Simplified error handling.

Removed the require.Eventually wrapper, making the code more straightforward and easier to understand while maintaining the same functionality.


61-61: Direct contract method call improves readability.

The direct call to ratesAdmin.Contract().GetRates without the require.Eventually wrapper simplifies the code and maintains the same functionality.

pkg/blockchain/registryAdmin_test.go (6)

1-1: Good package naming convention applied.

Changing to blockchain_test follows Go's recommended practice for test packages, preventing circular imports and providing better separation.


9-11: Appropriate imports for package refactoring.

Adding the blockchain package import is necessary after the package name change, and the anvil package import enables the new testing approach.


15-16: Improved type clarity in function signature.

Using fully qualified types (blockchain.INodeRegistryAdmin, blockchain.INodeRegistryCaller) improves code readability and maintainability.


19-24: Well-structured Anvil setup for testing.

Starting an Anvil instance and using its RPC URL for contract deployment provides better test isolation and reliability.


41-41: Proper resource cleanup.

Adding defer cleanup() ensures the Anvil instance is properly terminated after the test completes, preventing resource leaks.


48-48: Improved type clarity in parameter.

Using the fully qualified type blockchain.INodeRegistryAdmin improves code readability and maintainability.

pkg/indexer/e2e_test.go (6)

9-19: Good organization of imports.

The addition of the required imports, especially the anvil package, supports the new testing approach.


24-26: Enhanced function signature with ContractsOptions.

Returning config.ContractsOptions from the function allows for better reuse of configuration in other parts of the test.


31-35: Well-implemented Anvil setup.

The code properly initializes an Anvil instance and deploys the necessary contracts, creating a self-contained test environment.


42-46: Comprehensive cleanup function.

The cleanup function properly handles all resources, including the Anvil instance, database, and context cancellation.


49-54: Improved messagePublisher signature.

Adding the contractsCfg parameter allows the function to use the configuration from startIndexing, making the code more maintainable.


79-80: Updated function calls for new signatures.

The code correctly captures and passes the contract configuration between functions.

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: 1

🔭 Outside diff range comments (1)
pkg/upgrade/docker_utils_test.go (1)

144-181: 🛠️ Refactor suggestion

Validate container operation before termination

Currently, the container is immediately terminated once it signals a successful start. This confirms that the container can start, but it doesn’t validate runtime behavior or ensure the container is stable. To ensure meaningful testing, consider running additional checks or allowing the container to operate for a short time before termination.

🧹 Nitpick comments (25)
pkg/indexer/indexer.go (1)

304-338: Well-structured reorg handling logic with clear control flow

The reorg handling logic is now properly guarded by the skipReorgHandling flag, which provides clearer control flow compared to the previous implementation. The reorganization detection and processing remains intact while adding better resilience for blockchain connectivity issues.

While this approach is sound, consider adding metrics or additional logging to track how frequently reorg handling is being skipped, as frequent skipping might indicate underlying issues with blockchain connectivity that should be addressed.

pkg/metrics/payer.go (2)

21-27: Minor naming inconsistency in metric name.

There's a slight inconsistency in the naming pattern compared to other metrics.

-		Name: "xmtp_payer_read_own_commit_in_time_seconds",
+		Name: "xmtp_payer__read_own_commit_in_time_seconds",

This would make it consistent with the double underscore pattern used in the xmtp_payer__node_publish_duration_seconds metric.


34-44: Gauge metric with clear purpose.

The gauge is well-designed and the comment about thread safety is helpful. Consider adding a brief inline comment explaining when this metric is updated in the application flow.

.github/workflows/upgrade-test.yml (1)

1-49: Workflow File is Fully Commented Out

All lines of this workflow are commented out, resulting in an empty workflow. If this is intentional (for example, if it serves as a template or is being reserved for future use), please add an explanatory comment. Otherwise, consider uncommenting the necessary lines so that the workflow is active and can be executed by GitHub Actions.

🧰 Tools
🪛 actionlint (1.7.4)

1-1: workflow is empty

(syntax-check)

go.mod (1)

32-56: Review of the Indirect Dependencies Block

Several indirect dependencies have been added or updated (e.g., dario.cat/mergo, github.com/AdaLogics/go-fuzz-headers, and github.com/cenkalti/backoff/v4 among others). It is recommended to run go mod tidy to ensure consistency and also double-check licensing and potential conflicts.

pkg/upgrade/docker_utils_test.go (1)

122-142: Log pull progress to aid debugging

Discarding the output from dockerClient.ImagePull makes it difficult to diagnose pull failures or track progress. Consider logging or buffering the pull output:

- _, err = io.Copy(io.Discard, reader)
+ buf := new(bytes.Buffer)
+ _, err = io.Copy(buf, reader)
+ if err == nil {
+   fmt.Printf("Pull logs for %s:\n%s\n", imageName, buf.String())
+ }
pkg/upgrade/main_test.go (1)

26-33: Consider pulling images in parallel

Pulling images sequentially can significantly prolong setup time, especially if the images are large or if there are many. You could optimize this by spawning goroutines for each image pull, then using a sync.WaitGroup to coordinate. However, be mindful of Docker rate limits and network capacity.

pkg/upgrade/upgrade_test.go (2)

11-20: Manage version list maintenance

Hardcoding a list of versions is fine for controlled tests, but can get outdated. For a dynamic or regularly updated environment, consider an external registry query or environment variable. This prevents stale versions in longer-lived test suites.


52-63: Strengthen test coverage for the “latest version”

You only verify that the dev container starts successfully. Adding more container validations—like checking logs for expected service readiness or running a smoke test against an endpoint—would provide stronger confidence in the container’s correctness.

contracts/src/Nodes.sol (3)

13-14: Consider removing or addressing the TODOs.
These TODO comments are minimal but call attention to potentially unnecessary duplication of events and incomplete error handling. Clarifying or removing them ensures the code remains tidy and free of unimplemented references.


103-103: Validate overflow boundary for _nodeCounter.
While uint32 provides a large range, it may eventually overflow if many nodes are minted over the contract’s lifetime. Consider either enforcing a cap or documenting the maximum possible number of nodes in order to avert future issues.


328-365: Consolidate API and replication activation logic if possible.
The _activateApiNode and _activateReplicationNode (and their disable counterparts) are nearly symmetrical. A shared internal utility could reduce code duplication, although it’s optional.

contracts/src/IdentityUpdates.sol (2)

22-22: Consider indexing event parameters.
The TODO note mentions indexing inboxId and sequenceId. Doing so can facilitate quicker and more efficient off-chain filtering and lookups of these events.


128-135: Large maximum payload size may lead to high gas usage.
The upper bound of over 4 MB (4,194,304 bytes) could excessively increase gas consumption and possibly lead to out-of-gas errors. Consider whether storing such large payloads on-chain is necessary.

contracts/src/RatesManager.sol (4)

9-10: Consider making PAGE_SIZE configurable or clarifying the approach.
The TODO suggests that PAGE_SIZE should be a default value that can be overridden by the caller, but there's no mechanism for doing that yet. Either implement a function to override it or clarify that it's fixed in this contract if that's deliberate.


18-23: Index both upgrader and newImplementation events.
The comment indicates a wish to index the event parameters. For more efficient filtering and event queries, consider marking one or both fields as indexed.

-event UpgradeAuthorized(address upgrader, address newImplementation);
+event UpgradeAuthorized(address indexed upgrader, address indexed newImplementation);

72-72: Use custom error for clarity and consistency.
Currently, the code calls require(admin != address(0), ZeroAdminAddress());. Custom errors are typically used with revert. Consider:

- require(admin != address(0), ZeroAdminAddress());
+ if (admin == address(0)) {
+     revert ZeroAdminAddress();
+ }

This ensures you get the benefits of custom error encoding.


168-173: Add a code-size or contract-checking mechanism.
_authorizeUpgrade checks for a zero address but does not confirm there is actual code at newImplementation. Consider verifying the code size to mitigate potential malicious or empty upgrades.

contracts/test/IdentityUpdates.t.sol (1)

44-45: Improve revert message for zero admin address.
You’re reverting with IdentityUpdates.ZeroAdminAddress.selector. To make debugging easier, consider including a short revert reason or an event that logs the invalid address.

contracts/test/RatesManager.t.sol (2)

65-76: Check for admin vs. manager roles.
The test includes a TODO comment about verifying when the admin is not the manager. Consider adding additional tests for a scenario in which these roles are separate addresses to confirm the role-based restrictions.


248-258: Ensure new implementation code presence.
Similar to the earlier file, you revert with RatesManager.ZeroImplementationAddress.selector but do not verify that the pointed address is a valid contract. You might want to check extcodesize(newImplementation) to reduce risk of upgrading to an uninitialized or non-contract address.

contracts/src/GroupMessages.sol (4)

9-10: Clarify TODO items.
The TODO comments “IGroupMessages” and “Abstract PayloadBroadcaster” remain unaddressed. Consider removing these if they’re no longer necessary or opening a follow-up task to finalize these abstractions.


16-22: Indexing recommendation for event parameters.
The groupId and sequenceId parameters are likely good candidates to index in this event, as the TODO suggests. Indexing these can improve on-chain filtering capabilities and ease of analytics.

 event MessageSent(
-    bytes32 groupId,
-    bytes message,
-    uint64 sequenceId
+    bytes32 indexed groupId,
+    bytes indexed message,
+    uint64 indexed sequenceId
 );

24-30: Consider indexing fields in upgrade event.
To align with best practices, indexing upgrader and newImplementation might further facilitate event tracking, consistent with the TODO.


189-197: Upgrade authorization logic.

  • Checking newImplementation != address(0) is crucial.
  • Emitting UpgradeAuthorized event for transparency is good.
    Consider finishing the TODO to check if code is present at newImplementation.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63dfafe and b43f9b6.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (36)
  • .github/workflows/test.yml (0 hunks)
  • .github/workflows/upgrade-test.yml (1 hunks)
  • contracts/foundry.toml (1 hunks)
  • contracts/script/DeployGroupMessages.s.sol (1 hunks)
  • contracts/script/DeployIdentityUpdates.s.sol (1 hunks)
  • contracts/script/DeployNodeRegistry.s.sol (1 hunks)
  • contracts/script/DeployRatesManager.s.sol (1 hunks)
  • contracts/script/upgrades/UpgradeGroupMessages.s.sol (1 hunks)
  • contracts/script/upgrades/UpgradeIdentityUpdates.s.sol (1 hunks)
  • contracts/script/utils/Environment.sol (1 hunks)
  • contracts/script/utils/Utils.sol (2 hunks)
  • contracts/src/GroupMessages.sol (1 hunks)
  • contracts/src/IdentityUpdates.sol (1 hunks)
  • contracts/src/Nodes.sol (9 hunks)
  • contracts/src/RatesManager.sol (2 hunks)
  • contracts/src/interfaces/INodes.sol (4 hunks)
  • contracts/src/interfaces/IPayer.sol (0 hunks)
  • contracts/src/interfaces/IPayerReport.sol (5 hunks)
  • contracts/test/GroupMessage.t.sol (3 hunks)
  • contracts/test/IdentityUpdates.t.sol (3 hunks)
  • contracts/test/Nodes.sol (0 hunks)
  • contracts/test/Nodes.t.sol (1 hunks)
  • contracts/test/RatesManager.t.sol (1 hunks)
  • contracts/test/utils/Harnesses.sol (1 hunks)
  • contracts/test/utils/Utils.sol (1 hunks)
  • go.mod (7 hunks)
  • pkg/api/payer/nodeCursorTracker.go (3 hunks)
  • pkg/api/payer/service.go (5 hunks)
  • pkg/blockchain/blockchainPublisher_test.go (3 hunks)
  • pkg/blockchain/nonceManager.go (2 hunks)
  • pkg/indexer/indexer.go (1 hunks)
  • pkg/metrics/metrics.go (1 hunks)
  • pkg/metrics/payer.go (1 hunks)
  • pkg/upgrade/docker_utils_test.go (2 hunks)
  • pkg/upgrade/main_test.go (1 hunks)
  • pkg/upgrade/upgrade_test.go (1 hunks)
💤 Files with no reviewable changes (3)
  • contracts/test/Nodes.sol
  • contracts/src/interfaces/IPayer.sol
  • .github/workflows/test.yml
✅ Files skipped from review due to trivial changes (2)
  • contracts/src/interfaces/IPayerReport.sol
  • contracts/src/interfaces/INodes.sol
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/blockchain/blockchainPublisher_test.go
🧰 Additional context used
🪛 actionlint (1.7.4)
.github/workflows/upgrade-test.yml

1-1: workflow is empty

(syntax-check)

🔇 Additional comments (142)
contracts/script/utils/Utils.sol (3)

4-4: Import statement refactored for better clarity.

The import statement has been updated to use destructured imports, which explicitly states what is being imported from the module. This is a good practice as it makes dependencies clearer and helps prevent naming conflicts.


7-9: Improved numeric literal readability with underscores.

The chain ID constants have been reformatted with underscores for better readability, which is a good practice in Solidity for large numeric values. This change maintains the same values while making the code more maintainable.


45-55: Function refactored to use early returns pattern.

The _resolveChainID function has been refactored to remove nested if-else blocks in favor of early returns. This simplifies the control flow and makes the code more readable without changing its functionality.

The new structure is cleaner and follows the principle of reducing cognitive load by eliminating nested conditionals.

pkg/indexer/indexer.go (1)

289-302: Improved error handling for blockchain query failures

The introduction of the skipReorgHandling flag with a warning instead of completely skipping the event (via continue) is a good improvement. This change allows event processing to continue even when blockchain query operations fail, making the indexer more resilient against temporary blockchain connectivity issues.

contracts/foundry.toml (3)

22-24: New Fuzz Section Configuration Added

The new [fuzz] section is introduced with runs = 10, which appears suitable for basic local fuzz testing. Please verify that this default value meets your intended testing sensitivity and can be adjusted if future requirements dictate more iterations.


25-27: CI Fuzz Profile Configuration

The [profile.ci.fuzz] section sets runs = 10_000, promoting more exhaustive fuzz testing in a CI environment. Ensure that your CI pipeline has sufficient resources and that this run count does not adversely extend test durations.


28-41: Formatting Configuration Added

The addition of the [fmt] section with detailed formatting options (such as preserving single-line statement blocks, specific line lengths, and tab widths) is a good step toward enforcing consistent style. Please confirm that these settings align with your team's style guidelines and are supported by the current version of Foundry.

contracts/test/utils/Utils.sol (4)

5-6: Approve renaming of EIP1967 constant for improved clarity.

The constant has been renamed from EIP1967_IMPL_SLOT to EIP1967_IMPLEMENTATION_SLOT, which enhances readability by using the full term "IMPLEMENTATION" instead of the abbreviated "IMPL". This follows best practices for naming constants in smart contracts.


9-13: Good improvement to function signature and loop initialization.

Two improvements in this segment:

  1. The return variable payload is now declared directly in the function signature, which is a good Solidity practice that improves readability and documentation.
  2. The loop variable i is initialized without an explicit value (which defaults to zero in Solidity), following modern Solidity conventions.

19-22: Approve formatting improvement for better readability.

The return statement has been reformatted across multiple lines, which improves readability without changing the functionality. This makes the complex expression easier to understand and maintain.


24-27: Consistent improvement to function signature and initialization.

Similar to the changes in _generatePayload:

  1. The return variable message is now declared in the function signature.
  2. Loop variable initialization follows the same pattern of implicit zero initialization.

These changes maintain consistency throughout the codebase and follow modern Solidity best practices.

pkg/metrics/metrics.go (1)

75-79: LGTM! New metrics additions for observability.

Five new metrics collectors have been added to track various aspects of the system:

  • nodePublishDuration
  • cursorBlockTime
  • currentNonce
  • payerBanlistRetry
  • messagesOriginated

These additions enhance system observability without changing behavior.

pkg/blockchain/nonceManager.go (2)

7-7: LGTM! New import for metrics package.

Clean import added for metrics collection.


88-88: LGTM! Enhanced observability for nonce tracking.

Good addition that emits metrics for current nonce values, enhancing monitoring capabilities without altering behavior.

pkg/api/payer/nodeCursorTracker.go (3)

6-7: LGTM! Required imports for new metrics tracking.

Added imports for metrics package and time needed for performance measurement.

Also applies to: 11-11


50-51: LGTM! Performance tracking initialization.

Good addition that initializes a timestamp for measuring the duration of cursor tracking.


105-108: LGTM! Enhancing observability with metrics emission.

Valuable addition that records and emits metrics related to cursor tracking duration, providing insights into system performance.

pkg/api/payer/service.go (4)

6-6: LGTM! Added metrics import.

Clean import added for metrics collection.


184-186: LGTM! Conditional metrics emission for retries.

Good addition that only emits metrics when retries actually occur (retries != 0), avoiding unnecessary metric entries.


225-225: LGTM! Performance tracking for node publishing.

Good additions that:

  1. Track the time taken for node publishing operations
  2. Record the number of messages originated

These metrics enhance observability of the service's performance.

Also applies to: 233-234


268-269: LGTM! Performance tracking for blockchain publishing.

Well-implemented metrics tracking for blockchain publishing operations, measuring both duration and message count, similar to the node publishing metrics.

Also applies to: 329-331

pkg/metrics/payer.go (5)

1-6: Clean and appropriate imports.

The imports for Prometheus and string conversion are appropriate for the metrics functionality being implemented.


8-19: Well-structured node publish duration metric.

The histogram is correctly set up with appropriate naming following Prometheus conventions, and the helper function properly handles type conversion for labels.


46-58: Well-structured histogram with custom buckets.

The custom buckets [0-5] are appropriate for counting retry attempts. The metric name is descriptive, though quite long.


60-71: Counter implementation follows best practices.

The counter for tracking originated messages is correctly implemented with proper labeling and a clear helper function.


1-71:

Details

✅ Verification successful

Ensure metrics are registered correctly.

While the metrics are well-defined, ensure they're properly registered with Prometheus in the registerCollectors function (likely in metrics.go).


🏁 Script executed:

#!/bin/bash
# Check if these metrics are registered in the metrics.go file
grep -n "registerCollectors" pkg/metrics/metrics.go && \
  grep -A 15 "registerCollectors" pkg/metrics/metrics.go | grep -E "nodePublishDuration|cursorBlockTime|currentNonce|payerBanlistRetry|messagesOriginated"

Length of output: 389


Prometheus Metrics Registration Verified

The metrics defined in pkg/metrics/payer.go (i.e., nodePublishDuration, cursorBlockTime, currentNonce, payerBanlistRetry, and messagesOriginated) are properly registered in the registerCollectors function within pkg/metrics/metrics.go.

  • Confirmed that the registration code includes all the metrics in question.
  • No additional changes are required.
go.mod (4)

5-21: Updated Dependency Versions in the First require Block

Notice the version updates for dependencies such as:

  • github.com/grpc-ecosystem/grpc-gateway/v2 updated to v2.26.1,
  • google.golang.org/genproto/googleapis/api updated to v0.0.0-20250218202821-56aae31c358a, and
  • google.golang.org/protobuf updated to v1.36.5.

Please ensure that these version bumps are compatible with existing code and that no unexpected breaking changes are introduced.


23-30: Addition of New Dependencies in the Second require Block

New dependencies such as github.com/docker/docker v27.5.0+incompatible and github.com/testcontainers/testcontainers-go v0.35.0 have been added. These appear aligned with the objectives for enhanced testing and container management. Verify that these dependencies meet your requirements and are correctly integrated.


163-167: Updated OpenTelemetry Dependencies

The OpenTelemetry-related dependencies have been updated to v1.35.0:

  • go.opentelemetry.io/otel
  • go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  • go.opentelemetry.io/otel/metric
  • go.opentelemetry.io/otel/sdk
  • go.opentelemetry.io/otel/trace

Please confirm that these updates integrate smoothly with your observability instrumentation and do not introduce breaking changes.


188-189: Replacement Directive for go-ethereum

The replace directive on line 188 substitutes github.com/ethereum/go-ethereum with github.com/OffchainLabs/go-ethereum at a specific commit/version. Ensure that this replacement is fully compatible with the components relying on go-ethereum, and that the new version satisfies both current and projected requirements.

pkg/upgrade/docker_utils_test.go (1)

9-24: Check for unused imports if any

These newly added imports (Docker client, testcontainers, etc.) align with your container management approach. Ensure that each import is truly needed and remove any that remain unused to keep the import list clean.

pkg/upgrade/upgrade_test.go (1)

4-8: New imports are appropriate

Importing context and require for test usage is a standard practice. They look correct here for managing test container startup and asserting conditions, respectively.

contracts/src/Nodes.sol (2)

128-135: Approve removal from API and replication sets on node removal.
The approach of calling _disableApiNode and _disableReplicationNode ensures consistency. This cleanly removes the node from the corresponding sets without duplication in logic.


207-210: Good use of internal checks for node ownership and status.
The _revertIfNodeDoesNotExist, _revertIfNodeIsDisabled, and _revertIfCallerIsNotOwner checks provide well-structured validations. They increase maintainability and readability.

contracts/src/IdentityUpdates.sol (2)

84-98: Initialization pattern looks correct.
Using initializer with require(admin != address(0)) helps prevent misconfiguration. Granting DEFAULT_ADMIN_ROLE to the specified admin is consistent with best practices.


192-196: Sufficient protection during contract upgrade.
Requiring a non-zero newImplementation address and having a dedicated event mitigate accidental zero upgrades and provide clarity on upgrade events.

contracts/script/utils/Environment.sol (1)

4-4:

Details

✅ Verification successful

Ensure removal of Script inheritance does not affect dependents.
Since you’ve removed is Script, verify that no essential functionality (e.g., console logging, script-based tools) is lost. If referencing Script functionalities elsewhere, consider an alternative approach or library.


🏁 Script executed:

#!/bin/bash
# Confirm no references remain to the `Script` contract or related functionality.
rg 'Script' --context 3

Length of output: 2452


Script Inheritance Removal Verified

The removal of is Script from contracts/script/utils/Environment.sol appears intentional and isolated. Our search confirms that:

  • Environment.sol no longer inherits from Script as expected.
  • Other parts of the repository (e.g., in contracts/script/utils/Utils.sol) still reference and inherit from Script, which seems to be intentional for those utilities.

Please verify that any functionality previously provided by Script in the Environment context is either non-essential or has been adequately replaced. Otherwise, the current changes look safe.

contracts/script/DeployNodeRegistry.s.sol (1)

4-9:

Details

❓ Verification inconclusive

Refactored import statements and contract inheritance

The changes remove the dependency on Forge's Script base contract while maintaining functionality through the Utils and Environment imports. This refactoring appears to be part of a broader change across deployment scripts to standardize how they're structured.

Make sure the vm functions used throughout the contract (like vm.envAddress, vm.startBroadcast, etc.) are properly accessible through the remaining imports (Utils or Environment).


🏁 Script executed:

#!/bin/bash
# Check if vm functions are available in Environment.sol or Utils.sol
echo "Checking for vm accessibility in Utils.sol and Environment.sol"
grep -r "vm\." ./contracts/script/utils/

Length of output: 627


Review: Verify Availability of All Required vm Functions

The refactoring to remove the Forge Script base while relying on Utils and Environment appears sound. Our grep check confirms that Utils.sol provides access to several vm functions (e.g., vm.readFile, vm.writeJson, and vm.projectRoot). However, please double-check that all other vm functions referenced in the deployment scripts—specifically functions such as vm.envAddress and vm.startBroadcast—are available through either Utils or Environment. If these functions are inherited from Forge’s cheatcode interface or are wrapped in one of the imported contracts, then no further changes are required.

  • File: contracts/script/DeployNodeRegistry.s.sol (lines 4-9)
  • Note: Confirm that any deployment calls relying on vm.envAddress or vm.startBroadcast function correctly with the new refactored imports.
contracts/script/upgrades/UpgradeGroupMessages.s.sol (1)

4-12:

Details

❓ Verification inconclusive

Refactored imports and contract inheritance structure

The contract's import statements have been reorganized and the inheritance has been simplified by removing Script. The addition of stdJson and explicit import of GroupMessages with proper path makes dependencies clearer.

Ensure that vm functionality (used in multiple places like vm.envUint, vm.startBroadcast, etc.) is still accessible through the remaining imports (Utils or Environment).


🏁 Script executed:

#!/bin/bash
# Verify if the import paths for GroupMessages are correct
cat ./contracts/src/GroupMessages.sol 2>/dev/null || echo "GroupMessages.sol not found at expected path"

# Check if stdJson is properly accessible 
grep -r "stdJson" ./contracts/script/upgrades/UpgradeGroupMessages.s.sol

Length of output: 7818


Reorganized Imports and Inheritance Structure Verified

  • The explicit import of GroupMessages from ../../src/GroupMessages.sol is correct and the file exists as expected.
  • The import and usage of stdJson from "forge-std/src/StdJson.sol" are confirmed.
  • Action Required: Please double-check that vm functionality (e.g., vm.envUint, vm.startBroadcast, etc.) remains accessible through the Utils or Environment contracts after removing the inheritance from Script.
contracts/script/DeployRatesManager.s.sol (1)

4-11: Refactored imports and contract inheritance

The import structure has been reorganized to be more explicit, and the dependency on Forge's Script contract has been removed. The contract now directly inherits only from Utils and Environment.

This change aligns with the pattern seen in other deployment scripts, creating a more consistent approach across the codebase.

contracts/script/DeployGroupMessages.s.sol (1)

4-11: Refactored imports and contract inheritance structure

The contract's import statements have been reorganized and the inheritance has been simplified by removing Script. The explicit import of GroupMessages with proper namespace improves code clarity.

This change is consistent with the refactoring pattern seen in the other deployment scripts, creating a more uniform approach across the codebase.

contracts/script/upgrades/UpgradeIdentityUpdates.s.sol (7)

4-5: Imports for stdJson and ERC1967Proxy are consistent with usage.
These imports appear necessary for JSON parsing and proxy upgrades, and there are no immediate concerns.


6-6: No impact change.
This blank line likely represents a minor formatting or spacing adjustment.


7-7: Confirm correct relative path for IdentityUpdates.
Ensure references to ../../src/IdentityUpdates.sol match the desired file structure.


8-8: No impact change.
This blank line likely represents a minor formatting or spacing adjustment.


9-10: Imports Utils and Environment appear valid.
These utilities likely provide shared functionalities. Confirm that Utils and Environment properly expose the vm context as needed.


11-11: No impact change.
This blank line likely represents a minor formatting or spacing adjustment.


12-12: Verify removal of Script from inheritance.
Confirm that calls to vm.* and other Script functionalities do not break with the removal of Script as a base class.

contracts/script/DeployIdentityUpdates.s.sol (7)

4-4: OpenZeppelin proxy import is appropriate.
This matches the usage for ERC1967Proxy deployment.


5-5: No impact change.
This blank line likely represents a minor formatting or spacing adjustment.


6-6: Check new import path for IdentityUpdates.
Confirm usage across the codebase matches this updated relative path.


7-7: No impact change.
This blank line likely represents a minor formatting or spacing adjustment.


8-9: Reorganized imports for Utils and Environment.
No issues noted. These imports likely provide essential test or environment functionality.


10-10: No impact change.
This blank line likely represents a minor formatting or spacing adjustment.


11-11: Confirm removal of Script from inheritance.
Ensure vm usage and any Script-provided utilities are still accessible through Utils or other inherited contracts.

contracts/test/utils/Harnesses.sol (7)

1-3: File licensing and pragma are fine.
Using SPDX-License-Identifier: MIT and pragma solidity 0.8.28; is standard.


4-5: Importing EnumerableSet from OpenZeppelin.
This is a standard library import, no concerns.


6-10: Base contract imports check.
Imports for GroupMessages, IdentityUpdates, Nodes, and RatesManager are aligned with harness usage.


11-35: GroupMessagesHarness
Exposes internal storage and pause/unpause methods for testing. This is a typical harness pattern. No immediate problems detected.


37-61: IdentityUpdatesHarness
Similar harness approach, exposing internal state for testing pause/unpause, sequence ID, and payload size. Implementation appears consistent.


63-132: NodesHarness
Provides a comprehensive set of external methods to manipulate node state in tests. This is standard for advanced test scenarios. Ensure usage is restricted to testing only.


134-152: RatesManagerHarness
Exposes internal state push & retrieval methods for rates. No immediate correctness issues.

contracts/test/Nodes.t.sol (28)

1-3: File licensing and pragma are fine.
Adheres to MIT license, with pragma solidity 0.8.28;.


4-5: Importing Test from forge-std.
Ensures Foundry’s testing utilities are available. No issues noted.


20-24: Constants and role definitions.
Defines DEFAULT_ADMIN_ROLE, ADMIN_ROLE, and NODE_MANAGER_ROLE for clarity. Looks correct.


38-43: setUp function.
Initializes NodesHarness with an admin and grants NODE_MANAGER_ROLE to manager. Straightforward approach.


53-79: test_addNode_first
Tests initial node addition. The usage of vm.expectEmit and subsequent assertions are thorough.


138-156: test_addNode_notAdmin
Ensures revert behavior for unauthorized calls. Proper usage of role checks and vm.expectRevert.


188-211: test_disableNode
Validates disabling a node, ensuring correct state changes and event emissions. Comprehensive coverage.


293-316: test_transferFrom
Tests the transfer flow, verifying that active node sets are cleared and events are emitted. Good coverage for ownership transfer logic.


406-460: API enable/disable logic.
Confirms correct revert scenarios if the node is disabled or the caller is unauthorized. Thorough checks.


464-516: Replication enable/disable logic testing.
Similar approach to API enabling with robust revert checks. Implementation is consistent.


520-557: test_setMinMonthlyFee
Validates manager-only assignment for monthly fees. Proper usage of vm.expectRevert for unauthorized roles.


560-569: test_setMaxActiveNodes
Checks admin-right flows and coverage for updating the maximum active nodes. Implementation looks correct.


598-608: Node operator commission percentage updates.
Ensures the commission remains within valid bounds (MAX_BPS).


628-636: test_setBaseURI
Covers event emission and internal base URI updates. Straightforward approach.


670-689: test_getAllNodes
Checks node retrieval after multiple additions, verifying correctness of returned arrays.


725-744: test_getActiveApiNodes
Ensures that active node sets are tracked accurately.


748-767: test_getActiveReplicationNodes
Similar test confirming replication nodes are tracked correctly.


771-782: test_getActiveApiNodesIDs
Demonstrates retrieval of IDs. Implementation is consistent.


786-797: test_getActiveReplicationNodesIDs
Mirrors the API node approach for replication nodes.


801-807: test_getActiveApiNodesCount
Validates the count of active API nodes. Straightforward logic.


811-817: test_getActiveReplicationNodesCount
Checks the count for active replication nodes. No concerns.


821-830: test_getApiNodeIsActive
Ensures correct boolean checks for active nodes.


834-843: test_getReplicationNodeIsActive
Analogous test for replication nodes. Implementation is parallel.


847-852: test_supportsInterface
Verifies compliance with ERC721, IERC165, and AccessControl interfaces. Good coverage.


856-859: test_revokeRole_revokeDefaultAdminRole
Properly checks for revert on default admin role revocation. Implementation aligns with contract constraints.


863-869: test_renounceRole_withinDelay
Confirms that role renunciation within an enforced admin delay is disallowed.


873-894: Helper _addNode
Facilitates manual node creation for testing. Exposes harness methods and mints the token.


895-904: Helper _getRandomNode
Generates pseudo-random node data. Standard approach for test variability.

contracts/src/RatesManager.sol (1)

139-139: Check for empty list logic.
This require(fromIndex < $.allRates.length, FromIndexOutOfRange()); is correct for non-empty arrays, but the function’s behavior for an empty array is handled by a separate condition at lines 137–138. Ensure both checks stay consistent and do not introduce edge-case off-by-one issues.

contracts/test/IdentityUpdates.t.sol (4)

17-18: Good use of utility inheritance.
Extending both Test and Utils centralizes repeated functionality and improves maintainability of cross-cutting utilities or test helpers.


125-161: Comprehensive fuzz testing.
Your fuzz testing for addIdentityUpdate covers a broad range of payload sizes and states (paused/unpaused). This provides good real-world coverage and reduces edge-case bugs.


249-252: Prevent re-initialization.
This test correctly checks that initialize reverts if called on a contract that’s already initialized, enforcing the OpenZeppelin initialization pattern. Well done.


311-316: Clear revert condition for unexpected unpause behavior.
test_unpause_whenNotPaused ensures that unpause() reverts if the contract is already unpaused. This is an excellent defensive check.

contracts/test/RatesManager.t.sol (3)

9-10: Imports for upgradeable contracts.
These import statements improve clarity around the contract's upgradeable nature. Make sure you’re following recommended versions to align with upstream security patches for proxies.


17-20: Consistent usage of roles and structured constants.
Defining DEFAULT_ADMIN_ROLE and RATES_MANAGER_ROLE at the top clarifies their usage and promotes consistency across tests.


126-133: Verify the empty getRates call logic.
When the array is empty, calling getRates(0) returns an empty array and false for hasMore. If you expect multiple usage patterns, be sure to confirm that no out-of-bounds condition arises for any future extension.

contracts/src/GroupMessages.sol (14)

4-7: Good use of upgradeable imports.
These imports from @openzeppelin-contracts-upgradeable are appropriate for implementing UUPS capabilities, role-based access control, and pausable functionality.


31-35: Event documentation is clear.
The comment block adds clarity about the minimum payload size update. No issues found.


38-42: Event documentation for max payload size is consistent.
Nicely parallels the min payload size event.


45-51: New custom errors improve clarity.
The introduction of ZeroImplementationAddress() helps differentiate specific upgrade-related errors. This is good for debugging.


53-56: Constants for payload size.
Defining ABSOLUTE_MIN_PAYLOAD_SIZE and ABSOLUTE_MAX_PAYLOAD_SIZE provides clarity and helps prevent accidental misuse. Nicely done.


58-60: UUPS Storage documentation.
The storage comment clarifies persisting data across upgrades. This ensures devs understand the custom storage location.


61-70: Struct-based storage approach.
Using a dedicated struct for the contract’s storage (via _getGroupMessagesStorage) encapsulates the data and simplifies future upgrades. This is a recommended pattern for UUPS.


71-76: Assembly usage in _getGroupMessagesStorage().
The inline assembly to map a constant storage slot is valid. Just ensure that modifications to this slot remain consistent with upgrade-safe patterns.


80-98: Initialization logic.
The require statement to check admin != address(0) and custom error usage are correct. The storage initialization with min/max is consistent with the newly introduced struct. Nicely handled.


102-116: Pause and unpause functions.
Restricting these calls to onlyRole(DEFAULT_ADMIN_ROLE) and integrating the _pause / _unpause pattern from PausableUpgradeable adheres to best practices. The approach looks solid.


120-137: addMessage function.

  • Validates payload size rigorously with InvalidPayloadSize error.
  • Uses unchecked for incrementing sequence ID to save gas, which is acceptable since overflow is unlikely for uint64.
    Overall, a solid and well-structured approach.

141-156: Setter for min payload size.

  • Valid check ensuring minPayloadSizeRequest <= maxPayloadSize and bounding it above ABSOLUTE_MIN_PAYLOAD_SIZE.
  • Emitting MinPayloadSizeUpdated keeps the external usage in sync.
    Implementation is well-formed.

158-173: Setter for max payload size.

  • Properly checks maxPayloadSizeRequest >= minPayloadSize and <= ABSOLUTE_MAX_PAYLOAD_SIZE.
  • Emits MaxPayloadSizeUpdated.
    No issues observed.

177-185: Getter functions usage.
Exposing minPayloadSize() and maxPayloadSize() is straightforward and aligns with the storage struct.

contracts/test/GroupMessage.t.sol (30)

4-5: Consistent usage of forge-std & IAccessControl.
Importing Test for Foundry-based testing and using IAccessControl for role checks is aligned with the rest of the code.


8-11: Proxy and upgradeable dependencies.
These imports from ERC1967 and OpenZeppelin upgradeable contracts are consistent with the usage in the main contract.


12-15: Test harness usage.
By importing GroupMessagesHarness for more direct testing, you can assert internal states. Also, Utils can help unify repeated logic. This approach is robust.


17-22: Defining test contract constants.
Clearly naming ABSOLUTE_MIN_PAYLOAD_SIZE and ABSOLUTE_MAX_PAYLOAD_SIZE in tests ensures alignment with the contract’s definitions.


23-28: Storing references to implementation and harness.
Keeping groupMessagesImplementation and groupMessages in separate variables helps with clarity when upgrading or re-deploying the proxy.


31-40: setUp() function with proxy deployment.
The usage of ERC1967Proxy paired with GroupMessages.initialize.selector is correct. This flow ensures the underlying implementation is properly set with the correct admin.


44-50: Zero admin address revert test.
Ensures that calling initialize with address(0) is disallowed. This test is effectively verifying the custom error in the contract. Great coverage.


54-59: test_initialState() thoroughness.

  • Checking implementation slot after proxy creation
  • Verifying min/max payload size
  • Confirming initial sequenceId is 0

All are fundamental checks ensuring the contract was set up correctly.


63-72: test_addMessage_minPayload()

  • Utilizes _generatePayload and expects MessageSent event.
  • Verifies sequenceId.
    Aligned with the contract logic.

74-83: test_addMessage_maxPayload()
Similarly tests the upper bound, ensuring behavior parallels min payload tests.


85-98: Tests for payload too small.
Confirms contract reverts with InvalidPayloadSize. Important negative test coverage.


100-113: Tests for payload too large.
Again, checks the revert scenario and ensures coverage of the upper bound logic.


115-123: Check revert when paused.
Ensures that calling addMessage triggers PausableUpgradeable revert. This maintains consistency with the main contract’s Pause/Unpause approach.


125-161: testFuzz_addMessage

  • Using Foundry’s fuzzing approach to bound the parameters covers a wide range of input sizes.
  • The logic toggling paused tests both normal and revert scenarios.
  • Checking final sequenceId ensures correctness upon success.

Comprehensive test design.


165-174: test_setMinPayloadSize_notAdmin & invalid requests.
Covers role-based restriction (reverts for unauthorized) and boundary checks. This is thorough.


185-190: Rejected requests below absolute min.
Reusing the InvalidMinPayloadSize error ensures the contract’s constraints are enforced.


192-203: test_setMinPayloadSize()

  • Emits updated event.
  • Asserts new min size via accessor.
    This completes a positive path test.

207-216: test_setMaxPayloadSize_notAdmin & boundary check.
Similar approach ensures that only privileged addresses can make changes and that invalid sizes revert correctly.


218-225: Reject requests below min.
Guards the contract from an invalid range. This aligns with InvalidMaxPayloadSize usage.


227-232: Reject requests above absolute max.
Again, consistent usage of InvalidMaxPayloadSize.


234-245: test_setMaxPayloadSize()

  • Valid scenario test.
  • Ensures the event is emitted, followed by correct assignment.
    No issues found.

249-252: Prevent re-initialization.
Ensures initialize can only be called once, verifying the InvalidInitialization revert. Good protection.


256-264: test_pause()

  • Checks that only admin can pause.
  • Asserts the state of paused().
    Proper usage of PausableUpgradeable.

266-276: test_pause_notAdmin() & test_pause_whenPaused()

  • Confirms revert if unauthorized calls pause.
  • Confirms revert if already paused.
    These negative paths are crucial for robust coverage.

288-298: test_unpause()

  • Pauses first, then unpauses.
  • Verifies the correct event is emitted.
    Clear coverage for toggling the paused state.

300-309: test_unpause_notAdmin() & test_unpause_whenNotPaused()
Verifies revert conditions for unauthorized or redundant unpause calls. Thorough approach.


320-330: test_upgradeToAndCall_notAdmin()
Correctly ensures only the admin can upgrade.


332-337: Zero implementation address revert.
Prevents accidental bricking of the proxy, aligning with the contract’s custom error.


339-357: Successful upgrade preserving state.

  • Verifies old state after upgrade (max/min payload size and sequenceId).
  • Emits UpgradeAuthorized event.
    This final test ensures the upgrade path works as desired.

361-364: Helper function _getImplementationFromSlot().
Directly checking the proxy’s storage is an effective approach to confirm the correct implementation.

Comment on lines +23 to +50
ctx := context.Background()
for _, version := range xmtpdVersions {
image := fmt.Sprintf("%s:%s", ghcrRepository, version)

t.Run(version, func(t *testing.T) {
envVars := constructVariables(t)
t.Logf("Starting old container")
runContainer(t, fmt.Sprintf("xmtpd_test_%s", version), image, envVars)
err := runContainer(
t,
ctx,
image,
fmt.Sprintf("xmtpd_test_%s", version),
envVars,
)
require.NoError(t, err, "Failed to start container version %s", version)

t.Logf("Starting new container")
runContainer(t, "xmtpd_test_dev", "ghcr.io/xmtp/xmtpd:dev", envVars)
err = runContainer(
t,
ctx,
"ghcr.io/xmtp/xmtpd:dev",
"xmtpd_test_dev",
envVars,
)
require.NoError(t, err, "Failed to start dev container")
})
}
}
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

Clarify or refine “upgrade” testing logic

The test starts and immediately terminates old containers before spinning up the new container, so it's more like separate startup checks than a true upgrade path. If a rolling or in-place upgrade is desired, consider adapting the test to preserve old container data or run them concurrently to validate upgrade steps.

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: 0

🧹 Nitpick comments (1)
contracts/pkg/identityupdates/IdentityUpdates.go (1)

205-265: Consider documenting the relationship between absolute and configurable size limits.

While the implementation is correct, it might be helpful to add documentation comments explaining the relationship between the absolute constants (ABSOLUTEMAXPAYLOADSIZE/ABSOLUTEMINPAYLOADSIZE) and the configurable values (MaxPayloadSize/MinPayloadSize). This would clarify that the absolute values represent hard limits that cannot be exceeded, while the configurable values can be changed within those boundaries.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b43f9b6 and 745963f.

📒 Files selected for processing (6)
  • contracts/pkg/groupmessages/GroupMessages.go (5 hunks)
  • contracts/pkg/identityupdates/IdentityUpdates.go (5 hunks)
  • contracts/pkg/ratesmanager/RatesManager.go (4 hunks)
  • pkg/server/server_test.go (3 hunks)
  • pkg/testutils/anvil/anvil.go (1 hunks)
  • pkg/testutils/network/port.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/testutils/anvil/anvil.go
🔇 Additional comments (17)
contracts/pkg/ratesmanager/RatesManager.go (3)

42-43: Updated ABI and Bin to include the new PAGE_SIZE method.

The contract metadata has been updated with a new PAGE_SIZE function and modified return type specifications for getRatesCount, along with the corresponding binary updates. These changes are automatically generated and reflect the updated contract interface.


244-273: Added PAGE_SIZE accessor methods to the contract bindings.

Three new Go methods have been added to access the PAGE_SIZE constant from the smart contract:

  1. For direct calling via RatesManagerCaller
  2. For session-based calling via RatesManagerSession
  3. For caller session-based calling via RatesManagerCallerSession

These follow the standard go-ethereum binding pattern and correctly map to the Solidity function.


384-384: Updated function signature comments for getRatesCount.

The function signature in the comments has been updated to include the named return parameter count for clarity. This matches the modified contract ABI and improves documentation.

Also applies to: 401-401, 408-408

pkg/testutils/network/port.go (1)

10-15: Well-implemented utility function for finding available ports.

The implementation correctly uses the OS port assignment mechanism and properly handles resource cleanup with deferred close. This is a clean, reusable utility that will help avoid port conflicts in tests.

pkg/server/server_test.go (3)

24-24: Good addition of the network test utilities import.

This import properly integrates the new port finding utility.


78-82: Good refactoring to use the shared port utility.

Replacing the local port finding mechanism with the shared utility eliminates code duplication and improves maintainability.


222-223: Consistent application of the refactoring.

The port utility is consistently used across all test cases, ensuring uniform behavior.

contracts/pkg/identityupdates/IdentityUpdates.go (5)

33-35: ABI updated to include absolute payload size constants.

The contract ABI has been updated to include two new view functions: ABSOLUTE_MAX_PAYLOAD_SIZE and ABSOLUTE_MIN_PAYLOAD_SIZE, which provide fixed constants for payload size limits. These complement the existing maxPayloadSize and minPayloadSize functions that return configurable values.


205-220: Properly implemented Go binding for ABSOLUTEMAXPAYLOADSIZE.

The Go binding correctly implements the Solidity ABSOLUTE_MAX_PAYLOAD_SIZE function as ABSOLUTEMAXPAYLOADSIZE following Go naming conventions. The function is properly defined with appropriate error handling and type conversion.


222-234: Session wrapper methods added for consistency.

The Session and CallerSession wrapper methods are correctly implemented, maintaining consistency with the rest of the binding interface. This allows the function to be called from different contexts (direct caller, session with default options, caller session).


236-265: ABSOLUTEMINPAYLOADSIZE implementation follows the same pattern.

The implementation of ABSOLUTEMINPAYLOADSIZE follows the same pattern as ABSOLUTEMAXPAYLOADSIZE, with proper error handling and type conversion, as well as appropriate session wrappers.


393-393: Updated comments on existing size methods.

The comments for the existing maxPayloadSize and minPayloadSize methods have been updated to indicate they return a uint256 size. This clarifies the distinction between these configurable methods and the new absolute constants.

Also applies to: 410-410, 424-424, 441-441, 448-448

contracts/pkg/groupmessages/GroupMessages.go (5)

34-35: ABI and binary updates correctly reflect the contract changes.

The ABI string now includes the new ABSOLUTE_MAX_PAYLOAD_SIZE and ABSOLUTE_MIN_PAYLOAD_SIZE functions, and the binary has been updated accordingly. These changes are consistent with the addition of the new contract methods.


205-234: Implementation of ABSOLUTEMAXPAYLOADSIZE follows correct binding pattern.

The new function properly implements the binding to the contract method "ABSOLUTE_MAX_PAYLOAD_SIZE" with appropriate implementations for Caller, Session, and CallerSession contexts, following the established pattern in this codebase.


236-265: Implementation of ABSOLUTEMINPAYLOADSIZE follows correct binding pattern.

The new function properly implements the binding to the contract method "ABSOLUTE_MIN_PAYLOAD_SIZE" with appropriate implementations for Caller, Session, and CallerSession contexts, maintaining consistency with other function implementations.


393-393: Updated comment clarifies return value for maxPayloadSize.

The comment has been improved to specify that the function returns a size value, enhancing code documentation.

Also applies to: 410-410, 417-417


424-424: Updated comment clarifies return value for minPayloadSize.

The comment has been improved to specify that the function returns a size value, enhancing code documentation.

Also applies to: 441-441, 448-448

…b.com:xmtp/xmtpd into 03-13-test_utility_to_start_an_anvil_instance
### TL;DR

Refactored blockchain tests to use Anvil for isolated test environments

## Issues
#630

### What changed?

- Modified blockchain test files to use isolated Anvil instances for
each test
- Updated contract deployment functions to accept an RPC URL parameter
- Added proper cleanup of Anvil instances after tests complete
- Added a `Contract()` accessor method to `RatesAdmin` to expose the
underlying contract
- Moved blockchain package tests to `blockchain_test` package for better
isolation
- Replaced `defer cleanup()` with `t.Cleanup(cleanup)` in some tests
- Added `NewContractsOptions` function that accepts an RPC URL parameter
- Fixed initialization of deployed contracts in test utilities

### How to test?

Run the blockchain tests:
```
go test ./pkg/blockchain/... -v
```

### Why make this change?

This change improves test reliability by ensuring each test runs in an
isolated blockchain environment. By using separate Anvil instances for
each test, we eliminate potential interference between tests and make
them more deterministic. The code also properly cleans up resources
after tests complete, preventing resource leaks.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a new administrative interface that enables direct access
to rate management functionality.

- **Quality Improvements**
- Upgraded blockchain integration and configuration to support dynamic
contract deployments.
- Streamlined resource cleanup and testing processes to enhance system
stability and reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@neekolas neekolas merged commit 4411fb4 into main Mar 14, 2025
13 checks passed
@neekolas neekolas deleted the 03-13-test_utility_to_start_an_anvil_instance branch March 14, 2025 19:51
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.

4 participants