Skip to content

feat: evm abci handlers #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Apr 16, 2025
Merged

feat: evm abci handlers #6

merged 22 commits into from
Apr 16, 2025

Conversation

NikhilSharmaWe
Copy link
Collaborator

@NikhilSharmaWe NikhilSharmaWe commented Feb 24, 2025

Description

  • Creating scalerize cosmos application
  • Add required options in the CLI for config
  • Create custom ABCI handlers using engine API calls to the Ethereum node.

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive local testnet setup with new command-line tools, Docker Compose configurations, and shell scripts to launch multi-node networks.
    • Added new persistent flags and environment options for enhanced deployment, enabling both scalerized and reth node setups.
    • Upgraded the execution client integration for improved Ethereum network communication and configuration.
    • Added new Dockerfiles for building and deploying the "scalerized" and "reth" applications.
    • Introduced new scripts for starting testnet nodes and managing Ethereum configurations.
    • Added a new JSON file for defining the genesis block configuration for an Ethereum-like blockchain.
    • Introduced a new interface for managing Ethereum execution clients, enhancing modularity and functionality.
    • Added new environment variable definitions and configuration options for Ethereum data and JWT management.
  • Chores

    • Refined build processes with enhanced Makefile targets and updated dependencies for a smoother development experience.

NikhilSharmaWe and others added 20 commits October 4, 2024 12:56
…ze multi-node setup on docker containers

Signed-off-by: Nikhil Sharma <[email protected]>
…anges for multi node setup

Signed-off-by: Nikhil Sharma <[email protected]>
…nges for creating abci handler

Signed-off-by: Nikhil Sharma <[email protected]>
… logic for eth engine api

Signed-off-by: Nikhil Sharma <[email protected]>
…ded PrepareProposal logic

Signed-off-by: Nikhil Sharma <[email protected]>
…al and changes making sure client creation before abci calls

Signed-off-by: Nikhil Sharma <[email protected]>
Copy link

coderabbitai bot commented Feb 24, 2025

Walkthrough

The changes introduce enhancements across the repository. Updates include modifications to the .gitignore, new Dockerfiles and Compose configurations for containerized deployment, and updates to the Makefile and shell scripts for building and running testnet nodes. Application code now integrates an EVM execution client with improved context handling and flag configuration. New CLI commands and testnet utilities allow for local testnet initialization and startup, while comprehensive additions in the execution modules establish interfaces and methods for Ethereum Engine API interactions, JWT handling, and payload processing. Dependency versions in the module file have also been updated.

Changes

File(s) Change Summary
.gitignore Re-added debug_container.log and appended new entries (.scalerized, example-testnet, build).
Dockerfile, reth.Dockerfile, docker-testnet-compose.yml, reth.env Added new Dockerfiles and Compose configuration to build the "scalerized" Go application and set up Reth nodes, defining images, environment variables, and network settings.
Makefile, scripts/execution-client.mk, scripts/init.sh Introduced new build targets, variables, and commands to streamline building for Linux, starting ephemeral execution clients (e.g., reth nodes), and configuring the mempool.
abci/proposal.go Added an ABCIHandler interface and ScalerizeABCIHandler struct (with a constructor) for handling proposal lifecycle events.
app/app.go, app/params/flags.go, cmd/scalerized/cmd/commands.go Modified application initialization to support context management and EVM client configuration; introduced new persistent flags and a testnet command for enhanced CLI control.
client/testnet.go Added CLI commands for local testnet initialization and startup, including creating validator directories and genesis files.
execution/* (including files under execution/evm/ and execution/client.go) Introduced comprehensive EVM client functionality: new interfaces, configuration structures, constants, engine methods, JWT management, payload primitives, RPC interactions, and utility functions.
go.mod Upgraded the Go version, toolchain, and various dependency versions; added new dependencies for Ethereum and consensus types.
start-testnet-node.sh, start-testnet-reth-bootnode.sh, start-testnet-reth-miner-node.sh Introduced new shell scripts to initialize and start scalable testnet nodes and Reth boot/miner nodes.
testing/files/eth-genesis.json, testing/files/jwt.hex Added new configuration files for Ethereum genesis block setup and JWT secret data for testing.

Sequence Diagram(s)

sequenceDiagram
    participant App as Scalerized App
    participant EVM as EVMClient
    participant ABCI as EVMABCIHandler
    participant Engine as EngineAPI
    participant RPC as RPCAPI

    App->>EVM: NewEVMClient(ctx, config, logger)
    EVM-->>App: Connection Started (async ready signal)
    App->>ABCI: NewEVMABCIHandler(ctx, EVM)
    ABCI->>EVM: PrepareProposal()
    EVM->>Engine: Connect/Request latest block info
    Engine-->>EVM: Return block header/number
    ABCI->>EVM: ProcessProposal()/NewPayload()
    EVM->>RPC: Dial RPC and process payload request
    RPC-->>EVM: Return payload/execution result
Loading

Poem

I'm a bunny hopping through the code today,
With Docker dreams and testnets lighting the way.
EVM wires and proposals all in neat array,
Building blocks align as our nodes come out to play.
I celebrate these lines with a joyful, quick hooray! 🐇💻
Code blossoms like spring in every byte and display.
Let’s hop to it—onward now, come what may!


🪧 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @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.

@NikhilSharmaWe NikhilSharmaWe changed the title Feat/evm abci handlers feat: evm abci handlers Feb 24, 2025
@rahulghangas
Copy link
Contributor

@coderabbitai review

Copy link

coderabbitai bot commented Mar 10, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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: 13

🧹 Nitpick comments (48)
app/params/flags.go (1)

3-11: Well-structured configuration flags for EVM integration

The defined flags cover the essential configuration parameters needed for connecting to an Ethereum execution client, including connection details, authentication, and chain identification.

Consider adding default values for these parameters directly in this file or in the command implementation to improve developer experience. Also, adding brief comments for each flag would enhance maintainability.

 var (
-	FlagExecutionClientType          = "execution-client-type"
-	FlagExecutionEngineURL           = "engine-api-url"
-	FlagExecutionEngineJWTSecretPath = "engine-api-jwtsecret"
-	FlagRPCURL                       = "eth-rpc-url"
-	FlagRPCJWTRefreshInterval        = "jwt-refresh-interval"
-	FlagRPCCheckInterval             = "eth-rpc-check-interval"
-	FlagEthChainID                   = "eth-chain-id"
+	// FlagExecutionClientType defines the type of execution client to use
+	FlagExecutionClientType          = "execution-client-type"
+	// FlagExecutionEngineURL defines the URL for the execution engine API
+	FlagExecutionEngineURL           = "engine-api-url"
+	// FlagExecutionEngineJWTSecretPath defines the path to the JWT secret for engine API authentication
+	FlagExecutionEngineJWTSecretPath = "engine-api-jwtsecret"
+	// FlagRPCURL defines the URL for the Ethereum RPC endpoint
+	FlagRPCURL                       = "eth-rpc-url"
+	// FlagRPCJWTRefreshInterval defines how often to refresh JWT tokens
+	FlagRPCJWTRefreshInterval        = "jwt-refresh-interval"
+	// FlagRPCCheckInterval defines how often to check the Ethereum RPC connection
+	FlagRPCCheckInterval             = "eth-rpc-check-interval"
+	// FlagEthChainID defines the Ethereum chain ID to connect to
+	FlagEthChainID                   = "eth-chain-id"
 )
execution/evm/utils.go (1)

18-32: Robust hexToAddress implementation with proper error handling

The hexToAddress function is well-implemented with appropriate error handling:

  1. It correctly handles the "0x" prefix in Ethereum addresses
  2. It properly validates the address length (20 bytes)
  3. It uses clear, descriptive error messages

One small improvement would be to validate the hex string format before attempting to decode it.

 func hexToAddress(hexStr string) (common.Address, error) {
 	var addr [20]byte
 
+	// Remove 0x prefix if present
 	decoded, err := hex.DecodeString(strings.TrimPrefix(hexStr, "0x"))
 	if err != nil {
 		return addr, err
 	}
 
 	if len(decoded) != 20 {
 		return addr, fmt.Errorf("invalid length: got %d, want 20", len(decoded))
 	}
 
 	copy(addr[:], decoded)
 	return addr, nil
 }
execution/evm/config.go (2)

9-16: Default configurations look appropriate for development

The default configuration values appear reasonable for a development environment, with appropriate URLs and intervals.

Consider adding a comment to DefaultEthChainID = "80087" explaining what chain this ID corresponds to, as it's not immediately obvious.


43-51: Consider adding input validation

The NewEVMConfig function parses URLs and retrieves the JWT secret, but it doesn't validate other inputs like the durations.

Add validation for time durations to ensure they are positive values:

func NewEVMConfig(ethChainID *big.Int, rpcJWTRefreshInterval, rpcCheckInterval time.Duration, engineAPIURL, rpcURL, jwtSecretPath string) (*EVMConfig, error) {
+	if rpcJWTRefreshInterval <= 0 {
+		return nil, fmt.Errorf("rpcJWTRefreshInterval must be positive")
+	}
+	if rpcCheckInterval <= 0 {
+		return nil, fmt.Errorf("rpcCheckInterval must be positive")
+	}
+	if ethChainID == nil {
+		return nil, fmt.Errorf("ethChainID cannot be nil")
+	}

	eu, err := url.Parse(engineAPIURL)
	if err != nil {
		return nil, err
	}
	// Rest of the function remains the same
execution/evm/constants.go (1)

22-29: Function implementation looks good but lacks documentation

The ScalerizeSupportedCapabilities function correctly returns a subset of the defined constants for capability exchange.

Add a comment documenting the purpose of this function:

+// ScalerizeSupportedCapabilities returns a list of Engine API methods that this
+// implementation supports for capability exchange with execution clients.
func ScalerizeSupportedCapabilities() []string {
	return []string{
		ForkchoiceUpdatedMethodV3,
		GetPayloadMethodV3,
		NewPayloadMethodV3,
		GetClientVersionV1,
	}
}
Makefile (1)

1-4: Consider using underscores in variable names

The variable STARTING-IP-ADDR uses a dash, which is unconventional for Makefile variables and can cause issues in certain contexts.

Use underscores instead of dashes in Makefile variable names:

-STARTING-IP-ADDR := 172.20.0.2
+STARTING_IP_ADDR := 172.20.0.2

And update references to this variable accordingly.

go.mod (1)

47-52: Beacon Kit dependencies need version consistency

Multiple Beacon Kit modules are included with different versions. This could lead to compatibility issues if the modules have interdependencies. Consider aligning all Beacon Kit module versions.

-github.com/berachain/beacon-kit/mod/chain-spec v0.0.0-20240705193247-d464364483df // indirect
-github.com/berachain/beacon-kit/mod/engine-primitives v0.0.0-20240808194557-e72e74f58197 // indirect
-github.com/berachain/beacon-kit/mod/errors v0.0.0-20240705193247-d464364483df // indirect
-github.com/berachain/beacon-kit/mod/geth-primitives v0.0.0-20240806160829-cde2d1347e7e // indirect
-github.com/berachain/beacon-kit/mod/primitives v0.0.0-20240911165923-82f71ec86570 // indirect
+github.com/berachain/beacon-kit/mod/chain-spec v0.0.0-20240911165923-82f71ec86570 // indirect
+github.com/berachain/beacon-kit/mod/engine-primitives v0.0.0-20240911165923-82f71ec86570 // indirect
+github.com/berachain/beacon-kit/mod/errors v0.0.0-20240911165923-82f71ec86570 // indirect
+github.com/berachain/beacon-kit/mod/geth-primitives v0.0.0-20240911165923-82f71ec86570 // indirect
+github.com/berachain/beacon-kit/mod/primitives v0.0.0-20240911165923-82f71ec86570 // indirect
start-testnet-node.sh (1)

3-9: Add environment variable validation

The script uses environment variables ($ID, $ENGINE_API, $RPC_API) without validating if they exist. This could lead to silent failures or unexpected behavior if these variables are not properly set.

 # !/bin/sh

+# Validate required environment variables
+if [ -z "$ID" ]; then
+  echo "ERROR: ID environment variable is not set"
+  exit 1
+fi
+
+if [ -z "$ENGINE_API" ]; then
+  echo "ERROR: ENGINE_API environment variable is not set"
+  exit 1
+fi
+
+if [ -z "$RPC_API" ]; then
+  echo "ERROR: RPC_API environment variable is not set"
+  exit 1
+fi
+
 # Copy the folder based on ID environment variable
 cp -r ./example-testnet/node$ID/scalerized /root/.scalerized

 # Start the scalerized node
 echo "starting scalerize node $ID in background ..."
 scalerized start --execution-client-type=evm --engine-api-url=$ENGINE_API --eth-rpc-url=$RPC_API 
 tail -f /dev/null
reth.Dockerfile (3)

1-1: Specify a fixed version tag instead of 'latest'

Using 'latest' as a tag can lead to unexpected behavior when the upstream image changes. This might cause reproducibility issues with your build process.

-FROM ghcr.io/paradigmxyz/reth:latest
+FROM ghcr.io/paradigmxyz/reth:v0.1.0-alpha.8  # Replace with a specific version

Consider pinning to a specific version for better reproducibility and version control.


3-6: Optimize apt-get installation to reduce image size

The current installation doesn't clean up the apt cache, which increases the image size.

 RUN apt-get update && apt-get install -y \
     curl \
     jq \
-    iputils-ping
+    iputils-ping \
+    && rm -rf /var/lib/apt/lists/*

14-15: Combine chmod commands to reduce layers

Multiple RUN statements create additional layers in the Docker image. Combining related commands can reduce the image size.

-RUN chmod +x ./start-testnet-reth-bootnode.sh
-RUN chmod +x ./start-testnet-reth-miner-node.sh
+RUN chmod +x ./start-testnet-reth-bootnode.sh ./start-testnet-reth-miner-node.sh
Dockerfile (7)

1-1: Specify a fixed version tag for golang:alpine

Using golang:alpine without a specific version can lead to unexpected behavior when the base image changes.

-FROM golang:alpine AS build-env
+FROM golang:1.21-alpine AS build-env  # Use a specific version that matches your Go module requirements

4-4: Consolidate package installation

The environment variable PACKAGES doesn't include linux-headers, which is installed separately. This makes maintenance more difficult.

-ENV PACKAGES git build-base
+ENV PACKAGES git build-base linux-headers

10-11: Consolidate RUN commands to reduce image layers

Multiple RUN statements create additional layers in the Docker image. Combining related commands reduces the image size.

-RUN apk add --update $PACKAGES
-RUN apk add linux-headers
+RUN apk add --update $PACKAGES

Note: This works with the previous suggestion to include linux-headers in the PACKAGES variable.


17-19: Add error checking in the build process

The build script does not verify that each command succeeds before proceeding to the next step.

-RUN make build
-RUN make localtestnet-example-config
-RUN echo "completed testnet config"
+RUN make build && \
+    make localtestnet-example-config && \
+    echo "completed testnet config"

This ensures that if one command fails, the Docker build will fail immediately.


22-22: Consider using a more recent Alpine version

Alpine 3.17.3 is somewhat outdated. A more recent version might include security patches and improvements.

-FROM alpine:3.17.3
+FROM alpine:3.19  # Or the latest version available

25-25: Optimize apk installation to reduce image size

The current installation doesn't clean up the apk cache, which increases the image size.

-RUN apk add --update ca-certificates jq bash curl
+RUN apk add --update ca-certificates jq bash curl && \
+    rm -rf /var/cache/apk/*

30-33: Use COPY with multiple source files to reduce image layers

Multiple COPY statements create additional layers in the Docker image. Combining related commands reduces the image size.

-COPY --from=build-env /go/src/github.com/aerius-labs/scalerize/build/scalerized /usr/bin/scalerized
-COPY --from=build-env /go/src/github.com/aerius-labs/scalerize/example-testnet example-testnet
-COPY --from=build-env /go/src/github.com/aerius-labs/scalerize/start-testnet-node.sh start-testnet-node.sh
-COPY --from=build-env /go/src/github.com/aerius-labs/scalerize/testing/files/jwt.hex jwt.hex
+COPY --from=build-env /go/src/github.com/aerius-labs/scalerize/build/scalerized /usr/bin/scalerized
+COPY --from=build-env /go/src/github.com/aerius-labs/scalerize/example-testnet example-testnet \
+                      /go/src/github.com/aerius-labs/scalerize/start-testnet-node.sh start-testnet-node.sh \
+                      /go/src/github.com/aerius-labs/scalerize/testing/files/jwt.hex jwt.hex

Note: The multi-source COPY format may need adjustment based on Docker version compatibility requirements.

start-testnet-reth-miner-node.sh (2)

39-50: Verify directory existence before other operations.

Line 39 creates the data directory, but there's no error handling if this operation fails. Consider adding error checking for critical operations.

-mkdir -p $ETH_DATA_DIR
+if ! mkdir -p $ETH_DATA_DIR; then
+  echo "Error: Failed to create data directory $ETH_DATA_DIR"
+  exit 1
+fi

1-7: Add environment variable validation.

The script relies on environment variables like BOOTNODE_IP and BOOTNODE_RPC_PORT but doesn't validate their existence before use. Add validation to prevent runtime errors.

 ETH_DATA_DIR=.scalerized/eth
 JWT_PATH=testing/files/jwt.hex
 ETH_GENESIS_PATH=testing/files/eth-genesis.json
 retries=3
 delay=3
 enode_url=""
 
+# Validate required environment variables
+if [ -z "$BOOTNODE_IP" ] || [ -z "$BOOTNODE_RPC_PORT" ]; then
+  echo "Error: BOOTNODE_IP and BOOTNODE_RPC_PORT environment variables must be set"
+  exit 1
+fi
🧰 Tools
🪛 Shellcheck (0.10.0)

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

start-testnet-reth-bootnode.sh (2)

5-16: Consider better error handling for critical operations.

The script uses && for command chaining, which will silently stop execution if any command fails. Consider adding explicit error handling for critical operations.

-mkdir -p $ETH_DATA_DIR
-reth init --datadir $ETH_DATA_DIR --chain $ETH_GENESIS_PATH && \
-reth node \
+if ! mkdir -p $ETH_DATA_DIR; then
+  echo "Error: Failed to create data directory $ETH_DATA_DIR"
+  exit 1
+fi
+
+if ! reth init --datadir $ETH_DATA_DIR --chain $ETH_GENESIS_PATH; then
+  echo "Error: Failed to initialize Reth node"
+  exit 1
+fi
+
+reth node \

1-4: Verify configuration files exist before use.

The script uses JWT_PATH and ETH_GENESIS_PATH without checking if these files exist. Add validation to provide clearer error messages if files are missing.

 ETH_DATA_DIR=.scalerized/eth
 JWT_PATH=testing/files/jwt.hex
 ETH_GENESIS_PATH=testing/files/eth-genesis.json
 
+# Verify critical files exist
+if [ ! -f "$JWT_PATH" ]; then
+  echo "Error: JWT file not found at $JWT_PATH"
+  exit 1
+fi
+
+if [ ! -f "$ETH_GENESIS_PATH" ]; then
+  echo "Error: Genesis file not found at $ETH_GENESIS_PATH"
+  exit 1
+fi
🧰 Tools
🪛 Shellcheck (0.10.0)

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

testing/files/eth-genesis.json (2)

2-24: Consider adding documentation for the chosen network parameters.

The config section defines important parameters like chainId and fork blocks, but lacks documentation explaining why these specific values were chosen. Consider adding comments to explain the reasoning for test network configuration choices.


342-355: Document the purpose of contract deployments.

The file includes three accounts with pre-deployed contract code, but doesn't document their purpose. Consider adding comments explaining the function of each contract to improve maintainability.

execution/evm/rpc.go (2)

24-28: Consider adding error context for failed RPC calls.

While the error handling in GetBlockByNumber is functional, consider wrapping the error with additional context to make debugging easier, especially since this is a critical RPC call.

 func (ec *EVMClient) GetBlockByNumber(num rpc.BlockNumber, withTxs bool) (*coretypes.Header, error) {
 	result := &coretypes.Header{}
 	err := ec.rpcClient.Client().CallContext(ec.ctx, result, BlockByNumberMethod, num, withTxs)
-	return result, err
+	if err != nil {
+		return nil, fmt.Errorf("failed to get block %d from EVM client: %w", num, err)
+	}
+	return result, nil
 }

3-8: Add missing import for the suggested change.

If you implement the suggested error context enhancement, you'll need to add the fmt package to your imports.

 import (
+	"fmt"
 	"strconv"

 	coretypes "github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/rpc"
 )
execution/evm/engine.go (3)

9-18: Validate response and naming clarity
Currently, the method name does not convey what "capabilities" are being exchanged or validated, and the returned slice is not checked for emptiness. Consider adding a brief comment clarifying the nature of these capabilities and confirm that an empty returned slice is acceptable.


20-34: Refine fork choice payload status checks
Relying on (result.PayloadStatus == PayloadStatus{}) could be error-prone if the type’s zero value is valid under certain conditions. Consider verifying specific fields within PayloadStatus, or enhance the error message to clarify why the status is considered invalid.


46-55: Verify returned payload status
While this code is valid, consider adding a sanity check or logging around result to confirm it is not unexpectedly defaulting to an empty struct before returning.

scripts/execution-client.mk (2)

6-22: Consider specifying container name and logs
When running Docker, naming containers (e.g., --name reth_node) and optionally redirecting logs could simplify debugging when issues arise or if multiple similar containers run on the same host.


23-33: Handle local node operation
Running the node directly on the host is fine. You may want to run it in the background (e.g., with systemd or tmux) and manage logs for long-running tests.

app/app.go (3)

117-121: Context usage
Creating a new background context is effective for now, but consider passing a propagated context from upstream (e.g., server-level) to allow more flexible cancellation or timeouts.


144-192: Execution client switch
The switch-based approach for selecting the client type is clean. Consider adding a safer type assertion or fallback if the flag is unset or an unexpected type. Proper logs upon invalid input can improve debugging.


172-192: Avoid panics in goroutine
Inside the goroutine that starts your EVM client, a panic on startup error could abruptly crash the entire application. A more graceful shutdown approach or returning an error from the main flow could be preferable.

Apply this diff to handle the error more gracefully:

 go func() {
     if err := evmClient.Start(ctx, ensureClientCreatedCh); err != nil {
-        panic(err)
+        app.Logger().Error("failed to start EVM client", "error", err)
+        ensureClientCreatedCh <- false
         return
     }
 }()
execution/evm/jwt.go (2)

22-36: Consider handling repeated refresh failures more robustly.

Currently, the refreshJWTForRPCClient method logs an error but continues attempting to refresh indefinitely. If the remote service remains unreachable, the loop will spam logs. You might consider imposing a maximum number of retries or an exponential backoff to handle sustained connectivity failures more gracefully.


74-83: Add token expiration or other claims to enhance security.

Currently, the JWT only includes an "issued-at" (iat) claim. Consider adding an "expiration" (exp) claim to enforce a limited token lifetime and reduce the window of opportunity if tokens are compromised. Additional claims like sub (subject) or aud (audience) could improve clarity and security, depending on API needs.

docker-testnet-compose.yml (3)

1-90: Enhance production-readiness with health checks and auto-restart policies.

Although this Compose file is likely for local testnet development, consider adding Docker health checks and restart policies to automatically restart failed containers. Health checks can help detect containers stuck in an unresponsive state and improve overall reliability.


91-91: Fix trailing spaces in the file.

Line 91 has trailing whitespace. This violates YAML lint rules. Remove the trailing spaces to comply with lint requirements.

-    image: "reth/node"  
+    image: "reth/node"
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 91-91: trailing spaces

(trailing-spaces)


164-164: Add a newline at the end of the file.

Static analysis indicates there is no newline character at the end of the file. Adding a final newline ensures compliance with YAML best practices and lint rules.

-        - subnet: 172.20.0.0/16
\ No newline at end of file
+        - subnet: 172.20.0.0/16
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 164-164: no new line character at the end of file

(new-line-at-end-of-file)

execution/evm/client.go (2)

41-50: Avoid infinite polling in the background without a fallback.

The Start method schedules repeated checks for engine and RPC availability. If the services are down for an extended period, the logs may become noisy. Consider a finite retry mechanism or backoff strategy to reduce noise and handle permanent failures more gracefully.


123-151: Consider unifying dial logic and logging for consistent error handling.

The dialRPCCLient function branches on the client type but duplicates some setup logic. You might factor out common steps (like logging or context handling) into shared utility code. This keeps the function concise and reduces repetitive error handling.

execution/evm/abci_handler.go (2)

30-30: Implement retries for reliability.

A TODO note indicates the need for retry logic on engine API calls. Would you like assistance implementing a simple retry or circuit-breaker approach, or shall I open a follow-up issue tracking this enhancement?


37-46: Use structured logging instead of fmt.Printf.

Relying on fmt.Printf for production logs can make it harder to collect and parse logs. Consider switching to a structured logger (e.g., Cosmos SDK’s logger) to maintain consistency and improve observability.

Also applies to: 76-76, 83-84

execution/evm/primitives.go (1)

60-105: Consider consolidating manual required-field checks with gencodec.

Your manual required-field checks are robust. However, you also use gencodec:"required" in struct tags, which can similarly enforce those constraints. If desired, you could unify or streamline checks by relying on one approach for clarity and maintainability.

Also applies to: 141-186, 188-283

client/testnet.go (4)

127-129: Use consistent binary name in example usage.
This code snippet references "evmosd" from what appears to be a leftover fork. Consider updating the example to the correct binary name (e.g., "scalerized") to avoid confusion.


176-176: Update example command reference.
Likewise, the "evmosd" reference here should be replaced with the correct binary name to maintain consistency.


260-263: Consider safer error handling approach.
Currently, if an error occurs, the entire output directory is removed. This could be destructive if the directory contains other data. Consider a selective cleanup strategy or verifying that the directory is exclusively used for testnet initialization.


430-447: Clean up or uncomment code for better clarity.
Large blocks of commented-out code related to gov, mint, or crisis modules can introduce confusion or code bloat. Consider removing them if not needed or uncommenting them if they are relevant.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fe805f6 and 92e23cb.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (30)
  • .gitignore (1 hunks)
  • Dockerfile (1 hunks)
  • Makefile (2 hunks)
  • abci/proposal.go (1 hunks)
  • app/app.go (5 hunks)
  • app/params/config.go (1 hunks)
  • app/params/flags.go (1 hunks)
  • client/testnet.go (1 hunks)
  • cmd/scalerized/cmd/commands.go (3 hunks)
  • docker-testnet-compose.yml (1 hunks)
  • execution/client.go (1 hunks)
  • execution/evm/abci_handler.go (1 hunks)
  • execution/evm/client.go (1 hunks)
  • execution/evm/config.go (1 hunks)
  • execution/evm/constants.go (1 hunks)
  • execution/evm/engine.go (1 hunks)
  • execution/evm/jwt.go (1 hunks)
  • execution/evm/primitives.go (1 hunks)
  • execution/evm/rpc.go (1 hunks)
  • execution/evm/utils.go (1 hunks)
  • go.mod (3 hunks)
  • reth.Dockerfile (1 hunks)
  • reth.env (1 hunks)
  • scripts/execution-client.mk (1 hunks)
  • scripts/init.sh (1 hunks)
  • start-testnet-node.sh (1 hunks)
  • start-testnet-reth-bootnode.sh (1 hunks)
  • start-testnet-reth-miner-node.sh (1 hunks)
  • testing/files/eth-genesis.json (1 hunks)
  • testing/files/jwt.hex (1 hunks)
🧰 Additional context used
🪛 Shellcheck (0.10.0)
start-testnet-node.sh

[error] 1-1: Remove spaces between # and ! in the shebang.

(SC1115)

start-testnet-reth-bootnode.sh

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

start-testnet-reth-miner-node.sh

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

🪛 YAMLlint (1.35.1)
docker-testnet-compose.yml

[error] 91-91: trailing spaces

(trailing-spaces)


[error] 164-164: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (29)
.gitignore (1)

2-5: Appropriate gitignore entries for the new EVM functionality

The added entries are well-chosen for excluding build artifacts, test networks, and logs that shouldn't be tracked in version control. These additions align properly with the PR objectives of developing a Cosmos application with EVM ABCI handlers.

scripts/init.sh (1)

27-27: Confirm mempool.type "nop" is appropriate for this application

Setting the mempool type to "nop" and skipping validation means the mempool won't process transactions, which could be intentional for an EVM-based application where transaction handling might be delegated to the EVM execution client.

Consider documenting why this setting is necessary, perhaps with a comment explaining that transaction processing is handled by the EVM execution client instead of the standard Cosmos mempool.

execution/evm/utils.go (1)

12-16: Consider making generateRandao an unexported function

The generateRandao function appears to be correctly implemented but may need some refinement:

  1. Since this function uses crypto/rand, it's important for cryptographic operations - consider documenting its purpose
  2. As this function isn't exported (capitalized), verify it's only used internally within the package

The implementation itself is sound - it properly fills a hash with cryptographically secure random data.

abci/proposal.go (1)

7-16: Interface design looks solid, but commented methods need clarification

The ABCIHandler interface appropriately defines the essential handlers for proposal management in ABCI. The commented methods suggest planned future functionality.

Are the commented methods (lines 12-15) planned for future implementation? If so, consider adding a TODO comment explaining when they'll be implemented.

execution/evm/config.go (1)

27-42:

✅ Verification successful

Missing JWTSecret implementation

The NewEVMConfig function calls getJWTFromPath, but this function is not defined in the provided code.

Implement the getJWTFromPath function or ensure it's properly imported from another package. The code won't compile without this function.


🏁 Script executed:

#!/bin/bash
# Check if getJWTFromPath is defined elsewhere in the codebase
rg -t go "func getJWTFromPath" --no-ignore

Length of output: 119


JWTSecret implementation confirmed — No changes needed

The function getJWTFromPath is implemented in execution/evm/jwt.go (see the output from the rg command). There is no missing implementation issue.

execution/evm/constants.go (1)

3-20: Well-documented constants

The constants are well-named and properly documented with comments explaining their purpose. They follow standard Ethereum JSON-RPC and Engine API naming conventions.

Makefile (2)

41-49: Build targets look well-structured

The build targets are well-organized with proper dependencies and output directory configuration.


6-6:

✅ Verification successful

Verify inclusion of external script

The Makefile includes an external script that isn't shown in the provided code.

Ensure that scripts/execution-client.mk exists and is properly set up. The build will fail if this file is missing.


🏁 Script executed:

#!/bin/bash
# Check if the execution-client.mk file exists
if [ -f "scripts/execution-client.mk" ]; then
    echo "File exists"
    head -n 10 scripts/execution-client.mk
else
    echo "File does not exist"
fi

Length of output: 397


External Script Inclusion Verified
The scripts/execution-client.mk file exists in the repository and includes the expected configuration (e.g., directory definitions and target for starting an ephemeral reth node). No modifications are necessary.

go.mod (3)

25-31: New Ethereum dependencies align with project objectives

The addition of these key dependencies aligns with the PR objective of developing custom ABCI handlers to interact with an Ethereum node. The Ethereum packages and Beacon Kit integration will enable the implementation of the EVM execution client.


3-5:

❓ Verification inconclusive

Go version upgrade requires attention

The Go version has been updated from 1.21 to 1.23.0, which represents a significant upgrade. This change should be accompanied by verification that all dependencies are compatible with Go 1.23, especially considering that Go 1.23 was released very recently.


🏁 Script executed:

#!/bin/bash
# Check Go version compatibility for packages
go version
go env GOFLAGS
go mod tidy -v

Length of output: 14959


Verification: Go 1.23 Upgrade Verified Successfully

  • The go.mod file now declares go 1.23.0 with a toolchain of go1.23.2 and the system reports a runtime version of go1.23.3, which is consistent with expected minor patch differences.
  • The execution of go mod tidy -v successfully downloaded all dependencies without errors, indicating that dependency compatibility with Go 1.23 is intact.
  • No additional changes are required; please proceed knowing that the upgraded Go version has been verified across dependencies.

[resolve_review_comment]


31-31:

✅ Verification successful

Verify JWT package version security

The JWT package is being used for authentication between services. Ensure this is the latest secure version.


🌐 Web query:

Is GitHub.com/golang-jwt/jwt/v5 v5.2.1 the latest version? Are there any known security vulnerabilities?

💡 Result:

The latest version of github.com/golang-jwt/jwt/v5 is v5.2.1, as confirmed by multiple sources including its GitHub release history and Ubuntu package updates[1][7][10]. This version includes dependency upgrades and maintenance fixes but no breaking changes[7].

Security Status

There are no known security vulnerabilities specifically reported for v5.2.1. However, earlier versions of the library had critical issues, such as:

  • CVE-2024-5174: Affected v4.x versions (fixed in v4.5.1) due to improper error handling in ParseWithClaims, which could allow invalid tokens to be accepted if errors weren't checked properly[5][6]. This vulnerability did not impact v5.x[5].
  • Earlier vulnerabilities (e.g., CVE-2020-26160 in older forks) were resolved when migrating to the golang-jwt/jwt repository[9][16].

The project actively addresses vulnerabilities, as seen in recent updates to dependencies like golang.org/x/net and golang-jwt/jwt/v4 in related ecosystems[6][9]. The v5 branch benefits from improved validation logic and stricter error handling by design[2][5][13].

For safety, ensure:

  1. You use jwt/v5 imports (github.com/golang-jwt/jwt/v5)[2][13].
  2. Avoid deprecated forks like dgrijalva/jwt-go, which had unpatched vulnerabilities[16].
  3. Follow the library's validation guidelines, especially for token algorithms and error checking[11][14].

Citations:


JWT Package Version Verified – The currently used version (github.com/golang-jwt/jwt/v5 v5.2.1) is confirmed to be the latest release with no known security vulnerabilities. Please continue using this version and ensure you follow the library’s usage guidelines for secure token handling.

execution/client.go (1)

3-5: Well-designed minimal interface

The ExecutionClient interface is well-designed with a single method for now, following the interface segregation principle. This allows for different execution client implementations while maintaining a clear contract.

As the project evolves, consider adding documentation to explain what an ExecutionClient is supposed to do beyond providing a name.

reth.env (2)

1-4: Environment configuration for execution client

The environment configuration correctly defines the necessary paths for the execution client. This aligns with the PR objective of adding configuration options for the Scalerize Cosmos application.

Consider adding a brief comment at the top of the file explaining its purpose for better maintainability.


3-3:

✅ Verification successful

JWT file path consistency

The JWT path references the jwt.hex file reviewed earlier. Ensure this file exists in the specified path during deployment, or provide a mechanism to generate it as needed.


🏁 Script executed:

#!/bin/bash
# Check if JWT file exists and has correct format
JWT_FILE="testing/files/jwt.hex"
if [ -f "$JWT_FILE" ]; then
  echo "JWT file exists"
  # Check if it's a valid hex string of correct length (64 characters for 32 bytes)
  if [[ $(cat "$JWT_FILE" | wc -c) -eq 64 ]] && [[ $(cat "$JWT_FILE" | grep -E "^[0-9a-fA-F]+$") ]]; then
    echo "JWT file contains valid hex string"
  else
    echo "JWT file does not contain a valid 32-byte hex string"
  fi
else
  echo "JWT file not found at $JWT_FILE"
fi

# Check ETH_GENESIS_PATH
if [ -f "testing/files/eth-genesis.json" ]; then
  echo "Genesis file exists"
else
  echo "Genesis file not found"
fi

Length of output: 469


JWT and Genesis Files Verified

The verification confirms that:

  • The JWT file at testing/files/jwt.hex exists and contains a valid 32-byte hex string.
  • The Ethereum genesis file at testing/files/eth-genesis.json is present.

No further action is necessary based on the current deployment setup.

start-testnet-reth-miner-node.sh (2)

32-35: Good error handling implementation.

The script properly checks the return value of the get_enode function and provides a clear error message before exiting when the enode URL cannot be retrieved.


8-30: Well-structured function for retrieving enode information.

The get_enode function includes retry logic and proper error handling, with informative logging about the process. The regex-based IP replacement in line 20 is a good approach to ensure the enode URL has the correct bootnode IP.

testing/files/eth-genesis.json (1)

1-358: Well-structured Ethereum genesis configuration.

The genesis file is properly formatted with appropriate values for an Ethereum testnet. It includes:

  • All hard forks activated at block 0
  • A configured chainId (80087)
  • Pre-funded accounts for testing
  • Special contract deployments for blockchain functionality

This is a good configuration for a testing environment.

execution/evm/rpc.go (2)

10-22: Well-implemented block number retrieval with proper error handling.

The GetLatestBlockNumber method correctly:

  1. Makes the RPC call with proper context
  2. Handles potential errors during the call
  3. Parses the string result to an integer with error checking
  4. Returns the correctly typed block number

1-29:

✅ Verification successful

Request verification of undefined constants.

The code references constants BlockNumberMethod and BlockByNumberMethod which aren't defined in this file. Ensure these are properly defined elsewhere in the package.


🏁 Script executed:

#!/bin/bash
# Verify the constants are defined somewhere in the codebase
echo "Searching for BlockNumberMethod and BlockByNumberMethod definitions..."
# First, find all Go files in the execution/evm directory
files=$(fd --extension go . execution/evm)
# Then grep for the constants in those files
grep -n "BlockNumberMethod\|BlockByNumberMethod" $files || echo "Constants not found in any Go files in execution/evm directory"

Length of output: 1253


CONSTANTS ARE DEFINED CORRECTLY
After verifying the codebase, both BlockNumberMethod and BlockByNumberMethod are defined in execution/evm/constants.go (lines 12–15). The earlier concern regarding undefined constants is resolved.

execution/evm/engine.go (1)

36-44: Looks good
The retrieval of the execution payload envelope and direct return of the RPC error appear correct. No major issues here.

scripts/execution-client.mk (1)

1-5: No major issues
Variable definitions look straightforward and appropriate.

app/app.go (3)

4-31: Imports and mempool configuration
Introducing the no-op mempool is valid for a custom execution flow. Ensure that external transaction handling is not inadvertently disabled if the application eventually needs normal Cosmos transactions.


42-42: Good addition of new params import
This importer is crucial for pulling in the flag definitions. No issues found.


194-205: ABCI handlers assignment
The configuration of proposal and block handlers looks correct. Revisit enabling the commented-out handlers (e.g., BeginBlocker) if they become relevant.

execution/evm/jwt.go (1)

38-60: Clarify behavior for partial or zero-length JWT secrets.

The code allows a secret to be shorter than 32 bytes, silently copying fewer bytes into the JWTSecret struct, which might be intended. However, consider validating the exact length or documenting why partial secrets are acceptable if that’s intended, to avoid confusion or future security issues.

execution/evm/abci_handler.go (1)

71-81: Check for nil PayloadID to avoid panics.

If the engine API could return a ForkchoiceResponse with a nil PayloadID, calling GetPayload(*fcres.PayloadID) at line 78 will panic. Please confirm that the engine always provides a non-nil ID in successful scenarios. Otherwise, a nil check or fallback handling is advisable.

cmd/scalerized/cmd/commands.go (1)

46-60: LGTM for new persistent flags and testnet command.

client/testnet.go (2)

1-39: Implementation looks good.
No major concerns in the initial package definition and import statements.


551-596: No major issues.
The startTestnet function correctly configures and launches the in-process testnet, then waits for user input before cleanup. The approach appears consistent with typical Cosmos SDK patterns.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
execution/evm/jwt.go (1)

18-36: ⚠️ Potential issue

Be mindful of concurrent pointer updates in refreshJWTForRPCClient.

This function periodically calls dialRPCCLient to refresh tokens. Updating shared pointers (e.g., engineClient) from multiple goroutines without synchronization may introduce data races. This issue aligns with a previously flagged concurrency concern. Consider guarding shared pointers with a mutex or ensuring their updates occur only on a single goroutine.

🧹 Nitpick comments (11)
execution/evm/jwt.go (2)

38-60: Minor naming consideration.

Inside getJWTFromPath, the local variable bytes can overshadow the bytes package name if imported. While not critical, consider using a more descriptive name (e.g., decodedBytes) to avoid confusion.


74-83: Consider adding an expiration claim to the JWT.

Currently, the token never expires. Adding an exp claim improves security by limiting token validity.

execution/evm/client.go (2)

47-50: Reconsider starting the refresh loop unconditionally.

The JWT refresh loop is started in a deferred block before confirming successful connections. If the client fails to connect, this goroutine still runs, possibly creating unnecessary retries. Consider starting it only after establishing a valid connection.


55-66: Reduce duplicated logic in the two goroutines.

The retry logic for engine and RPC connections is almost identical, differing only in URLs and log messages. Consider extracting the shared logic into a helper function to adhere to DRY principles.

Also applies to: 69-80

client/testnet.go (5)

127-129: Rename leftover references to "evmosd" for consistency.

The documentation references "evmosd" even though this repository is named "scalerize." Consider updating these references for clarity and consistency:

-	evmosd testnet init-files --v 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2
+	scalerized testnet init-files --v 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2

201-202: Clarify or remove the commented-out JSON-RPC address flag.

Lines 201-202 comment out the JSON-RPC address configuration, which can be confusing. If this flag is no longer needed, consider removing it. Otherwise, enable it with a default value or a comment explaining its purpose.


208-392: Break down the large initTestnetFiles function.

Currently, initTestnetFiles spans many lines and handles multiple responsibilities (e.g., creating node directories, generating keys, collecting genesis files). Consider splitting it into smaller, dedicated helper functions to improve readability, maintainability, and testability.


260-264: Avoid removing the entire output directory on errors.

This block removes the full testnet output directory upon encountering an error, which can complicate debugging or partial reuse. Consider a more granular cleanup strategy or preserve logs and configuration files for diagnostic purposes.


315-318: Mask or clarify plaintext storage of seed words.

Seed words are stored in plaintext JSON, which is acceptable for purely local, ephemeral testnets. However, it may pose a security risk if deployed more widely. Consider emphasizing that this is for development purposes only or adding a configurable encryption mechanism.

start-testnet-reth-miner-node.sh (2)

7-7: Environment Variable Check: Validate BOOTNODE_IP and BOOTNODE_RPC_PORT

The script uses BOOTNODE_IP and BOOTNODE_RPC_PORT without verifying they’re set. For enhanced robustness, consider adding a check at the top of the script to ensure these required environment variables are defined.

+if [ -z "$BOOTNODE_IP" ] || [ -z "$BOOTNODE_RPC_PORT" ]; then
+  echo "Error: BOOTNODE_IP and BOOTNODE_RPC_PORT environment variables must be set."
+  exit 1
+fi

51-52: Trailing Backslash: Remove Unnecessary Line Continuation

The last argument (--trusted-peers $enode_url) is followed by a trailing backslash. This may lead to a syntax error if there is no subsequent argument. Removing the backslash will properly terminate the multi-line command.

-    --trusted-peers $enode_url \
+    --trusted-peers $enode_url
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 92e23cb and 37dc293.

📒 Files selected for processing (7)
  • client/testnet.go (1 hunks)
  • execution/evm/client.go (1 hunks)
  • execution/evm/config.go (1 hunks)
  • execution/evm/jwt.go (1 hunks)
  • start-testnet-node.sh (1 hunks)
  • start-testnet-reth-bootnode.sh (1 hunks)
  • start-testnet-reth-miner-node.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • start-testnet-node.sh
  • start-testnet-reth-bootnode.sh
  • execution/evm/config.go
🧰 Additional context used
🧬 Code Definitions (2)
start-testnet-reth-miner-node.sh (2)
app/params/config.go (1) (1)
  • init (34:37)
app/app.go (1) (1)
  • init (84:91)
execution/evm/client.go (4)
execution/evm/config.go (7) (7)
  • EVMConfig (18:25)
  • c (53:55)
  • c (57:59)
  • c (61:63)
  • c (65:67)
  • c (69:71)
  • c (73:75)
execution/evm/jwt.go (2) (2)
  • c (18:36)
  • c (62:72)
execution/evm/engine.go (4) (4)
  • c (9:18)
  • c (20:34)
  • c (36:44)
  • c (46:55)
execution/evm/constants.go (1) (1)
  • ExchangeCapabilities (17:17)
🔇 Additional comments (2)
execution/evm/jwt.go (1)

62-72: Logic appears consistent for building JWT header.

No issues found with header creation and error handling.

client/testnet.go (1)

531-535: Overflow handling for last octet appears correct.

The code checks for rollover when incrementing the final IP octet and returns an error on overflow. This resolves a known issue about exceeding 255. Great job!

Comment on lines 41 to 86
func (c *EVMClient) Start(ctx context.Context, ensureClientCreatedCh chan bool) error {
var (
wg sync.WaitGroup
ticker = time.NewTicker(c.config.RPCCheckInterval())
)

defer func() {
ticker.Stop()
go c.refreshJWTForRPCClient(ctx, engineClient)
}()

c.logger.Info("Connecting to the execution client")
wg.Add(2)

c.logger.Info("Initializing connection with Ethereum Engine API: " + c.config.EngineAPIURL().String())
go func() {
defer wg.Done()
for range ticker.C {
c.logger.Info("Waiting for ethereum engine api to be available: " + c.config.EngineAPIURL().String())
if err := c.connect(engineClient); err != nil {
c.logger.Error("failed to create connection to ethereum engine api")
continue
}

break
}
}()

c.logger.Info("Initializing connection with Ethereum RPC API: " + c.config.EngineAPIURL().String())
go func() {
defer wg.Done()
for range ticker.C {
c.logger.Info("Waiting for ethereum rpc api to be available: " + c.config.RPCURL().String())
if err := c.connect(rpcClient); err != nil {
c.logger.Error("failed to create connection to ethereum rpc api")
continue
}

break
}
}()

wg.Wait()
ensureClientCreatedCh <- true
return nil
}
Copy link

@coderabbitai coderabbitai bot Mar 18, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Synchronize pointer assignments to prevent data races.

Multiple goroutines in Start (lines 54+69) and refreshJWTForRPCClient (line 49 in the deferred call) may invoke dialRPCCLient concurrently, updating the same pointer fields (engineClient or rpcClient). Ensure concurrency safety (e.g., using a mutex) to prevent possible data races.

Also applies to: 123-151

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No changes are made in engineClient and rpcClient in Start method. And in refreshJWTForRPCClient only engineClient header is changed with updated jwt token.

Copy link

Choose a reason for hiding this comment

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

You are on the CodeRabbit Free Plan. In order to use the Chat feature, please upgrade to CodeRabbit Pro.

Comment on lines +591 to +593
cmd.Println("press the Enter Key to terminate")
_, err = fmt.Scanln() // wait for Enter Key
if err != nil {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Provide a non-blocking alternative in startTestnet.

The fmt.Scanln() blocks until user input is received, which can be problematic in automated scripts or CI pipelines. Consider adding a flag to skip interactive prompts or a timeout-based termination strategy.

@NikhilSharmaWe NikhilSharmaWe force-pushed the feat/evm-abci-handlers branch from 37dc293 to 7189625 Compare March 18, 2025 19:22
Copy link

@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 (7)
start-testnet-node.sh (1)

3-4: Environment Variable Safety for Node ID

The script uses the environment variable $ID in the command:

cp -r ./example-testnet/node$ID/scalerized /root/.scalerized

Ensure that $ID is always set or consider adding a check (and possibly a default value) to prevent unexpected failures if it’s missing.

execution/evm/jwt.go (3)

18-36: Consider adding a retry backoff or bounding attempts in the refresh loop.

Right now, this loop dials the RPC client indefinitely at fixed intervals. In scenarios involving transient failures or unreachable endpoints, logs may be spammed and resources wasted with no backoff or limit. Introducing a backoff strategy or capping the attempts can help stabilize retries.


38-60: Consider validating minimum secret length.

Currently, an empty hex string is treated as a valid secret, which might not provide meaningful authentication for generating JWTs. If a non-zero length secret is expected, consider returning an error if the file is empty or insufficiently long.


74-83: Add an expiration claim to enhance security.

Currently, the JWT includes only an issued-at time and lacks an expiration. Without an exp, the token will remain valid indefinitely if the secret leaks. Consider adding an expiry window (e.g., a few minutes) to mitigate potential token misuse.

client/testnet.go (3)

128-129: Rename references from "evmosd" to "scalerized" (or the correct binary) for consistency.
The usage example references "evmosd," which might be confusing to new users if the actual binary name is scalerized.

-       evmosd testnet init-files --v 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2
+       scalerized testnet init-files --v 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2

242-251: Trim whitespace for IP addresses read from the flag.
If a user separates IP addresses with spaces (e.g., "192.168.0.1, 192.168.0.2"), the extra whitespace could cause parsing errors. Consider trimming each split token.

ipList := strings.Split(args.ipAddresses, ",")
for i := range ipList {
-   ipList[i] = ipList[i]
+   ipList[i] = strings.TrimSpace(ipList[i])
}

315-318: Enhance security of private key seed storage.
Seeds are written in plain JSON with 0o600 permissions. This is acceptable for local development but consider encryption or password protection for stronger security in multi-user environments.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 37dc293 and 7189625.

📒 Files selected for processing (7)
  • client/testnet.go (1 hunks)
  • execution/evm/client.go (1 hunks)
  • execution/evm/config.go (1 hunks)
  • execution/evm/jwt.go (1 hunks)
  • start-testnet-node.sh (1 hunks)
  • start-testnet-reth-bootnode.sh (1 hunks)
  • start-testnet-reth-miner-node.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • start-testnet-reth-bootnode.sh
  • start-testnet-reth-miner-node.sh
  • execution/evm/config.go
🔇 Additional comments (14)
start-testnet-node.sh (3)

1-1: Shebang Line Correctness

The shebang line is correctly formatted as #!/bin/sh and aligns with best practices.


9-9: Container Process Persistence

Using tail -f /dev/null to keep the container running is a standard and acceptable approach. Double-check that this fits with your container management strategy.


6-8:

❓ Verification inconclusive

Clarify Background Process Execution

The echo message states that the scalerize node will start in the background:

echo "starting scalerize node $ID in background ..."

However, the subsequent command:

scalerized start --execution-client-type=evm --engine-api-url=$ENGINE_API --eth-rpc-url=$RPC_API 

does not include an ampersand (&), which typically denotes background execution. Verify whether scalerized start internally daemonizes its process. If it does not, consider appending & to ensure it runs in the background, or update the echo message accordingly.


Below is a shell script to help verify whether the scalerized start command runs in the background (by checking for an explicit ampersand) or if it internally daemonizes itself. You can run this script from the repository root to inspect the lines of interest in the start-testnet-node.sh file:


Action Required: Confirm Background Process Execution

The echo message indicates that the node is being started in the background, yet the command does not use an explicit ampersand (&). Please determine one of the following:

  • If scalerized start internally handles daemonization: update the echo message to remove any misleading “in background” phrasing.
  • If not: modify the command to append an ampersand so that it truly runs in the background.

Review and update the code accordingly.


🏁 Script executed:

#!/bin/bash
file="start-testnet-node.sh"
echo "Evaluating execution mode in $file (lines 6-8):"
sed -n '6,8p' "$file"

echo -e "\nChecking for explicit background operator '&' in the scalerized start command..."
if grep "scalerized start" "$file" | grep -q "&"; then
    echo "Found '&' in the command: the process is explicitly backgrounded."
else
    echo "No '&' found. Please verify if 'scalerized start' internally daemonizes its process."
fi

Length of output: 938


Action Required: Confirm Scaling Process Background Execution

The echo message suggests the scalerized node will run in the background, but the code does not append an ampersand (&). The verification shows no explicit background operator is present. Please confirm one of the following:

  • If scalerized start internally daemonizes: Update the echo message to eliminate the misleading mention of background execution.
  • If it does not daemonize: Append an ampersand (&) to the command so that the process truly runs in the background.

Review and update accordingly.

execution/evm/jwt.go (1)

62-72: Implementation looks good.

Building the "Authorization" header with the signed token is a standard approach, and error handling is straightforward.

execution/evm/client.go (6)

1-20: No issues found in package definitions and import statements.


29-35: No issues found in the constructor.


37-39: Getter method looks fine.


41-86: Concurrent assignments remain unsynchronized.

The Start method spawns goroutines that eventually call dialRPCCLient, updating shared pointers (e.g., c.engineClient) without locks or other synchronization. This can trigger data races, as previously noted.


88-121: Concurrent pointer writes can cause data inconsistencies.

The connect method calls dialRPCCLient, which overwrites c.engineClient or c.rpcClient. When multiple goroutines invoke connect concurrently, it can lead to data races.


123-151: Synchronize client pointer assignments and verify resource cleanup.

  1. Unprotected writes to c.engineClient / c.rpcClient risk data races.
  2. Overwriting these clients might leak resources unless the old client is gracefully closed.
client/testnet.go (4)

201-202: Consider re-adding a JSON-RPC address flag if needed.
The JSON-RPC flag is commented out. If you plan to expose or configure a JSON-RPC server, uncomment and finalize the corresponding logic to ensure consistent behavior with other flags.

Do we have references elsewhere in the code that might rely on this flag?


261-280: Revisit removing the output directory on initialization errors.
Calling os.RemoveAll(args.outputDir) may remove valuable partial data if multiple processes or other tasks share that directory. Consider removing only the new node subdirectory instead of the entire testnet output, or prompt the user before cleanup.


530-535: IP address overflow handling looks good!
Your check correctly fails fast when exceeding the maximum octet value. This ensures robust IP calculations for multi-validator setups.


591-593: Offer a non-blocking alternative to user input.
This code blocks until the user presses Enter, which can cause issues in CI or scripted deployments. Consider adding a flag to skip interactive waiting, or a timeout approach.

Signed-off-by: Nikhil Sharma <[email protected]>
@NikhilSharmaWe NikhilSharmaWe force-pushed the feat/evm-abci-handlers branch from 7189625 to 119b6fc Compare March 18, 2025 19:47
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (12)
execution/evm/client.go (1)

111-111: Minor grammatical improvement in error message.

"chainID do not match" should be "chain IDs do not match" or "chain ID does not match."

Apply this diff:

-c.logger.Error("eth chain ID specified not equal to the actual chain ID")
-return fmt.Errorf("chainID do not match for the eth client with what specified in scalerize config")
+c.logger.Error("eth chain ID specified is not equal to the actual chain ID")
+return fmt.Errorf("chain IDs do not match the ID specified in the Scalerize config")
execution/evm/jwt.go (1)

18-36: Clarify naming & log output for the JWT refresh loop.

  1. The method name refreshJWTForRPCClient is misleading if we are passing "engineClient" as the clientType.
  2. Similarly, messages like "Failed to refresh engine auth token" (line 31) and "Refreshed JWT Token for ethereum engine API" (line 33) do not distinguish between engine vs. RPC usage when clientType is "rpcClient".

Consider a more generic name such as refreshJWTForEVMClient, plus conditional log messaging based on clientType:

-func (c *EVMClient) refreshJWTForRPCClient(ctx context.Context, clientType string) {
-	c.logger.Info("Starting JWT refresh loop 🔄")
+func (c *EVMClient) refreshJWTForEVMClient(ctx context.Context, clientType string) {
+	c.logger.Infof("Starting JWT refresh loop for %s 🔄", clientType)

	...
	switch clientType {
	case engineClient:
	    c.logger.Info("Refreshed JWT Token for ethereum engine API")
	case rpcClient:
	    c.logger.Info("Refreshed JWT Token for ethereum RPC API")
	default:
	    ...
	}
}
Makefile (2)

41-44: Separate build targets for multiple environments.

Providing a build-linux target is a good practice for cross-platform compatibility. Consider supporting additional architectures if needed (e.g., ARM).


48-50: Directory creation for build outputs.

Using mkdir -p is straightforward. Alternatively, you might consider install -d for explicit directory creation semantics, but this is fine.

client/testnet.go (5)

59-70: initArgs struct for testnet initialization.

Storing CLI arguments in a dedicated struct improves clarity. Consider adding doc comments on each field, especially to clarify IP address usage vs. the ipAddresses list.


72-84: startArgs struct for in-process testnet.

Similar to initArgs, this approach is clean. Evaluate whether the two structs can be refactored into a shared type if large portions overlap.


166-204: testnetStartCmd: start multi-validator in-process testnet.

  • Allows immediate local testing without Docker or separate processes. Good for quick iteration.
  • The blocking prompt at the end might hinder CI usage. If you want a non-blocking approach, consider adding a --no-wait or --headless flag to skip the blocking prompt.

Do you want me to propose a change to conditionally skip the blocking prompt for automated workflows?


513-538: getIP and calculateIP IP increment logic.

  1. Properly checks for IPv4 overflow.
  2. The existence of fallback to sdkserver.ExternalIP() is helpful when no IP is provided.

Consider adding error logs whenever the fallback is used, so users are aware.


554-599: startTestnet final function.

  1. Leverages the network.DefaultConfig pattern from Cosmos SDK integration tests.
  2. Blocks on fmt.Scanln() (line 592), which can interfere with automated environments (previously noted).
  3. Cleans up network resources on exit.

Would you like a PR to implement an optional non-blocking exit for CI runs?

execution/evm/config.go (3)

9-16: Consider converting DefaultEthChainID to a constant big.Int for consistency

The DefaultEthChainID is defined as a string "80087", but it's used with a *big.Int type in the EVMConfig struct. Consider defining it as a constant *big.Int or providing a helper function to convert it consistently.

-const (
+var (
    DefaultEngineAPIURL          = "http://localhost:8551"
    DefaultRPCURL                = "http://localhost:8545"
    DefaultRPCJWTRefreshInterval = 20 * time.Second
    DefaultJWTSecretPath         = "./jwt.hex"
    DefaultRPCCheckInterval      = 3 * time.Second
-   DefaultEthChainID            = "80087"
+   // DefaultEthChainID represents the default Ethereum chain ID
+   DefaultEthChainID            = new(big.Int).SetString("80087", 10)
)

18-25: Add documentation for the EVMConfig struct

Add proper Go documentation for the exported EVMConfig type to improve code readability and help other developers understand its purpose and usage.

+// EVMConfig holds configuration parameters for connecting to and interacting with
+// an Ethereum execution client through Engine API and JSON-RPC.
type EVMConfig struct {
    engineAPIURL          *url.URL
    rpcURL                *url.URL
    jwtSecret             *JWTSecret
    rpcJWTRefreshInterval time.Duration
    rpcCheckInterval      time.Duration
    ethChainID            *big.Int
}

27-51: Add documentation for NewEVMConfig and consider wrapping errors with context

The NewEVMConfig function lacks documentation. Also, consider wrapping returned errors with additional context to improve debugging.

+// NewEVMConfig creates a new EVMConfig with the provided parameters.
+// It parses the provided URLs and retrieves the JWT secret from the specified path.
+// Returns an error if URL parsing or JWT retrieval fails.
func NewEVMConfig(ethChainID *big.Int, rpcJWTRefreshInterval, rpcCheckInterval time.Duration, engineAPIURL, rpcURL, jwtSecretPath string) (*EVMConfig, error) {
    eu, err := url.Parse(engineAPIURL)
    if err != nil {
-       return nil, err
+       return nil, fmt.Errorf("failed to parse engine API URL: %w", err)
    }

    ru, err := url.Parse(rpcURL)
    if err != nil {
-       return nil, err
+       return nil, fmt.Errorf("failed to parse RPC URL: %w", err)
    }

    secret, err := getJWTFromPath(jwtSecretPath)
    if err != nil {
-       return nil, err
+       return nil, fmt.Errorf("failed to get JWT from path %s: %w", jwtSecretPath, err)
    }

    return &EVMConfig{
        engineAPIURL:          eu,
        rpcURL:                ru,
        jwtSecret:             &secret,
        rpcJWTRefreshInterval: rpcJWTRefreshInterval,
        rpcCheckInterval:      rpcCheckInterval,
        ethChainID:            ethChainID,
    }, nil
}

Remember to add the fmt import:

import (
    "fmt"
    "math/big"
    "net/url"
    "time"
)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7189625 and 119b6fc.

📒 Files selected for processing (9)
  • Makefile (2 hunks)
  • abci/proposal.go (1 hunks)
  • client/testnet.go (1 hunks)
  • execution/evm/client.go (1 hunks)
  • execution/evm/config.go (1 hunks)
  • execution/evm/jwt.go (1 hunks)
  • start-testnet-node.sh (1 hunks)
  • start-testnet-reth-bootnode.sh (1 hunks)
  • start-testnet-reth-miner-node.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • start-testnet-node.sh
  • start-testnet-reth-bootnode.sh
  • start-testnet-reth-miner-node.sh
  • abci/proposal.go
🔇 Additional comments (18)
execution/evm/client.go (1)

41-86: Revisit concurrency approach to avoid potential data races.

Multiple goroutines in the Start method (lines 56 and 70) and the separate go c.refreshJWTForRPCClient(...) call at line 49 all eventually call dialRPCCLient, which modifies c.engineClient or c.rpcClient. Without synchronization (e.g., via a mutex or channel-based coordination), these pointer assignments can lead to data races.

Makefile (8)

1-4: Define environment variables for testnet configuration.

These variables allow customization of networking (STARTING-IP-ADDR), the number of testnet nodes (NODES), and the binary path (SCALERIZED_BINARY_PATH). They seem appropriate for containerized deployment or local testing workflows.


6-7: Include scripts/execution-client.mk.

This makefile snippet suggests you are modularizing execution client configurations in a separate script. Ensure the file exists and is properly version-controlled.


29-29: Use of .PHONY is correct.

Marking these targets as phony helps ensure they run even if files with the same name exist.


37-40: Comment block for clarity.

The comment block neatly indicates a new section. No concerns here.


45-47: Specified build dependencies.

Requiring go.sum and the $(BUILDDIR)/ directory ensures a clean build environment. Good practice to keep your dependencies locked.


51-54: Comment block for "Misc" section.

Keeping the Makefile organized into sections increases maintainability.


58-59: New target localtestnet-example-config.

This target initializes example testnet files with a straightforward command invocation. If you intend to run it inside a container context, ensure the path and environment are properly set up.


56-56:

✅ Verification successful

Running ./scripts/init.sh.

Ensure that the script file has executable permissions and the environment is prepared for any required dependencies or environment variables.

Please verify the script is executable:


🏁 Script executed:

#!/bin/bash
ls -l ./scripts/init.sh

Length of output: 91


Script Executable Verification Confirmed

The ./scripts/init.sh file has proper executable permissions (-rwxr-xr-x), so no changes are required in this regard. Please ensure that all necessary dependencies and environment variables are correctly set up, as this verification only covers file permissions.

client/testnet.go (7)

1-53: Package and imports overview.

  • The imports cover CometBFT, Cosmos SDK modules, and standard libraries for file, network, and JSON operations. This looks aligned with a typical CLI for a Cosmos-based chain.
  • The introduced flags (for node prefixes, IP addresses, etc.) appear relevant for testnet customization.

86-95: addTestnetFlagsToCmd function.

This function centralizes flag definitions for initialization and startup. The usage of default values is consistent with typical CLI expectations.


97-112: NewTestnetCmd entry point.

Creates a root command with subcommands for starting and initializing. Good separation of concerns.


114-164: testnetInitFilesCmd: multi-validator file initialization.

  • Offers a robust approach for generating validator configurations, keyrings, etc.
  • The usage text is descriptive and helps users understand how to create the testnet config.

394-466: initGenFiles function.

  1. Sets up balance and staking modules for each account, factoring in a base coin unit.
  2. Marshals the final app state into the genesis doc.
  3. Saves each validator’s genesis file with identical chain data for consistency.

No immediate concerns found.


468-511: collectGenFiles function.

  1. Gathers and merges generated transactions (gentxs) into each node’s genesis file.
  2. Uses a canonical timestamp for all validators so they remain in sync.

No major issues.


540-552: writeFile utility.

Streamlined approach to creating and writing to a file. No issues.

execution/evm/config.go (2)

53-75: LGTM! Proper getter methods implemented

The getter methods are well-implemented for all unexported fields, addressing the previous review comment about accessibility.


38-41:

✅ Verification successful

Check for JWTSecret forward declaration

The code calls getJWTFromPath function which returns a JWTSecret type, but neither seems to be defined in this file. Ensure these are properly defined elsewhere in the package.


🏁 Script executed:

#!/bin/bash
# Check for the definition of JWTSecret type and getJWTFromPath function
echo "Searching for JWTSecret type definition..."
rg -A 2 "type JWTSecret" --type go

echo "Searching for getJWTFromPath function definition..."
rg -A 2 "func getJWTFromPath" --type go

Length of output: 558


JWTSecret and getJWTFromPath are defined as expected

After verifying, both the JWTSecret type and the getJWTFromPath function are declared in execution/evm/jwt.go:

  • JWTSecret is defined as type JWTSecret [32]byte
  • getJWTFromPath is implemented to return a JWTSecret along with an error

Since these definitions exist elsewhere in the package, no forward declaration is needed in execution/evm/config.go.

Comment on lines +208 to +392
return err
}

keyringAlgos, _ := kb.SupportedAlgorithms()
algo, err := keyring.NewSigningAlgoFromString(args.algo, keyringAlgos)
if err != nil {
return err
}

addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, "", true, algo)
if err != nil {
_ = os.RemoveAll(args.outputDir)
return err
}

info := map[string]string{"secret": secret}

cliPrint, err := json.Marshal(info)
if err != nil {
return err
}

// save private key seed words
if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), nodeDir, cliPrint); err != nil {
return err
}

accStakingTokens := sdk.TokensFromConsensusPower(5000, PowerReduction)
coins := sdk.Coins{
sdk.NewCoin(params.CoinUnit, accStakingTokens),
}

genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: coins.Sort()})
genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0))

valStr, err := valAddrCodec.BytesToString(sdk.ValAddress(addr))
if err != nil {
return err
}

valTokens := sdk.TokensFromConsensusPower(100, PowerReduction)
createValMsg, err := stakingtypes.NewMsgCreateValidator(
valStr,
valPubKeys[i],
sdk.NewCoin(params.CoinUnit, valTokens),
stakingtypes.NewDescription(nodeDirName, "", "", "", ""),
stakingtypes.NewCommissionRates(math.LegacyOneDec(), math.LegacyOneDec(), math.LegacyOneDec()),
math.OneInt(),
)
if err != nil {
return err
}

txBuilder := clientCtx.TxConfig.NewTxBuilder()
if err := txBuilder.SetMsgs(createValMsg); err != nil {
return err
}

txBuilder.SetMemo(memo)

txFactory := tx.Factory{}
txFactory = txFactory.
WithChainID(args.chainID).
WithMemo(memo).
WithKeybase(kb).
WithTxConfig(clientCtx.TxConfig)

if err := tx.Sign(cmd.Context(), txFactory, nodeDirName, txBuilder, true); err != nil {
return err
}

txBz, err := clientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx())
if err != nil {
return err
}

if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil {
return err
}
// config.DefaultConfig()
// customAppTemplate, customAppConfig := config.DefaultConfig()
srvconfig.SetConfigTemplate(srvconfig.DefaultConfigTemplate)

srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), appConfig)
}

if err := initGenFiles(clientCtx, mbm, args.chainID, params.CoinUnit, genAccounts, genBalances, genFiles, args.numValidators); err != nil {
return err
}

err := collectGenFiles(
clientCtx, nodeConfig, args.chainID, nodeIDs, valPubKeys, args.numValidators,
args.outputDir, args.nodeDirPrefix, args.nodeDaemonHome, genBalIterator, valAddrCodec)
if err != nil {
return err
}

cmd.PrintErrf("Successfully initialized %d node directories\n", args.numValidators)
return nil
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

initTestnetFiles function.

  1. Generates each validator’s private keys, node directories, and genesis transactions.
  2. Cleans up (removing all generated files) if an error occurs, which is convenient but may be surprising in a multi-step pipeline. Ideally, you might isolate partial cleanup or confirm with the user.
  3. Writes mnemonic seeds (line 316) in JSON files. For production usage, strongly consider secure storage or disclaim usage only for test environments.

Copy link

@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

♻️ Duplicate comments (1)
execution/evm/client.go (1)

55-81: Revisit concurrency safety for pointer assignments.

Calls to c.connect(...) in separate goroutines update c.engineClient or c.rpcClient. Additionally, refreshJWTForRPCClient might also manipulate engineClient concurrently. Past reviews flagged potential data races. Even if you’re updating different fields, ensure no reads/writes occur on the same pointer at the same time, or protect assignments with a mutex if needed.

🧹 Nitpick comments (4)
execution/evm/client.go (4)

29-35: Validate the EVMConfig in the constructor.

Currently, NewEVMClient returns an EVMClient even if the provided cfg is nil or improperly configured. Consider validating cfg (e.g., non-nil with valid URLs) to fail fast and avoid downstream errors.


41-49: Provide a way to stop repetitively polling when the node is unavailable.

In Start, the repeated ticker-based retries continue indefinitely if the engine or RPC node never becomes available. Consider introducing a timeout or max attempts to prevent potentially infinite waiting.


83-86: Handle the scenario where connections fail repeatedly.

After waiting for the goroutines to succeed (wg.Wait), if both are unsuccessful indefinitely, ensureClientCreatedCh <- true still executes eventually. Consider adding logic to detect unrecoverable failures and return an error to prevent proceeding with an uninitialized client.


123-151: Add more descriptive error logging in dialRPCCLient.

When dialing fails for the engine or RPC client, you currently return the raw error. For better troubleshooting, prepend a short description (e.g., “Failed dialing engine client: ...”) so logs are more user-friendly, especially if multiple dial operations can fail.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 119b6fc and 0d5d30e.

📒 Files selected for processing (1)
  • execution/evm/client.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
execution/evm/client.go (2)
execution/evm/config.go (1)
  • EVMConfig (18-25)
execution/evm/constants.go (2)
  • ExchangeCapabilities (17-17)
  • ScalerizeSupportedCapabilities (22-29)

@NikhilSharmaWe NikhilSharmaWe force-pushed the feat/evm-abci-handlers branch from 0d5d30e to 9d97c1b Compare April 8, 2025 16:14
…d rename refreshJWTForRPCClient method to refreshJWTForClient

Signed-off-by: Nikhil Sharma <[email protected]>
@NikhilSharmaWe NikhilSharmaWe force-pushed the feat/evm-abci-handlers branch from 9d97c1b to 54d283f Compare April 8, 2025 16:15
Copy link

@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

♻️ Duplicate comments (2)
execution/evm/client.go (2)

123-151: ⚠️ Potential issue

Potential concurrency hazards when assigning c.engineClient and c.rpcClient.

The dialRPCCLient method sets c.engineClient or c.rpcClient, yet the same method may be invoked concurrently from multiple goroutines. Coordinate access to these pointer fields to prevent data races.

For instance, introduce a mutex in the EVMClient struct:

 type EVMClient struct {
 	ctx          context.Context
 	config       *EVMConfig
 	engineClient *ethclient.Client
 	rpcClient    *ethclient.Client
 	logger       log.Logger
+	mu           sync.Mutex
 }

 func (c *EVMClient) dialRPCCLient(clientType string) error {
+	c.mu.Lock()
 	defer c.mu.Unlock()
 	...
 }

41-49: ⚠️ Potential issue

Concurrent pointer updates may cause data races.

Multiple goroutines (the Start method's ticker-based loops and the call to refreshJWTForRPCClient) can simultaneously invoke dialRPCCLient, which replaces c.engineClient or c.rpcClient. Without synchronization (e.g., a mutex), this concurrency can cause data races and undefined behavior.

One approach is to guard the pointer assignments with a mutex or ensure these assignments happen in a single goroutine. For example:

 func (c *EVMClient) Start(ctx context.Context, ensureClientCreatedCh chan bool) error {
   var (
     wg     sync.WaitGroup
     ticker = time.NewTicker(c.config.RPCCheckInterval())
+    mu     sync.Mutex
   )

   defer func() {
     ticker.Stop()
-    go c.refreshJWTForRPCClient(ctx, engineClient)
+    go func() {
+      mu.Lock()
+      defer mu.Unlock()
+      c.refreshJWTForRPCClient(ctx, engineClient)
+    }()
   }()
   ...
 }
🧹 Nitpick comments (2)
execution/evm/client.go (1)

49-49: Mismatch function name: refreshJWTForRPCClient vs. refreshJWTForClient.

The function invoked here is refreshJWTForRPCClient, but the implementation in jwt.go is named refreshJWTForClient. Align these names to improve clarity and avoid confusion.

- go c.refreshJWTForRPCClient(ctx, engineClient)
+ go c.refreshJWTForClient(ctx, engineClient)
execution/evm/jwt.go (1)

30-33: Log message does not reflect the correct client type.

The log statement says "Refreshed JWT Token for ethereum engine API," but this function handles refresh for both "engine-client" and "rpc-client". Use the passed-in clientType in your log message for accuracy.

 c.logger.Info("Starting JWT refresh loop 🔄")
 ...
 c.logger.Info("Refreshed JWT Token for ethereum engine API")
+// Modify to:
+c.logger.Info(fmt.Sprintf("Refreshed JWT Token for %s", clientType))
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0d5d30e and 9d97c1b.

📒 Files selected for processing (2)
  • execution/evm/client.go (1 hunks)
  • execution/evm/jwt.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
execution/evm/jwt.go (1)
execution/evm/client.go (1)
  • EVMClient (21-27)
execution/evm/client.go (2)
execution/evm/config.go (1)
  • EVMConfig (18-25)
execution/evm/constants.go (2)
  • ExchangeCapabilities (17-17)
  • ScalerizeSupportedCapabilities (22-29)

Copy link

@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

♻️ Duplicate comments (3)
execution/evm/client.go (2)

21-27: Synchronize pointer assignments to prevent data races

We've flagged concurrency safety for pointer fields in prior reviews, but those concerns remain valid. If multiple goroutines call dialRPCCLient in parallel (e.g., from Start and the deferred JWT refresh), they may overwrite c.engineClient or c.rpcClient concurrently. Use a mutex or ensure that pointer assignment is done in a single goroutine.


88-121: Concurrent pointer updates in connect may cause data races

When multiple goroutines call connect (e.g., one for engineClient and another for rpcClient), the pointer assignments to c.engineClient or c.rpcClient occur concurrently. This can result in inconsistent states or data races unless synchronization is enforced.

execution/evm/jwt.go (1)

18-36: Avoid potential concurrent pointer assignment in the refresh loop

refreshJWTForClient calls dialRPCCLient, which sets either c.engineClient or c.rpcClient. This may coincide with other goroutines performing the same assignment unless steps are taken to prevent overlap.

🧹 Nitpick comments (1)
execution/evm/client.go (1)

41-86: Consider refactoring the deferred JWT refresh initialization

Currently, the JWT refresh loop starts only after Start returns. If you'd like more granular or immediate control over the token refresh, consider launching the refresh JWT routine once the Engine connection is established, rather than deferring it until the method exits.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9d97c1b and 54d283f.

📒 Files selected for processing (2)
  • execution/evm/client.go (1 hunks)
  • execution/evm/jwt.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
execution/evm/jwt.go (1)
execution/evm/client.go (1)
  • EVMClient (21-27)
execution/evm/client.go (2)
execution/evm/config.go (1)
  • EVMConfig (18-25)
execution/evm/constants.go (2)
  • ExchangeCapabilities (17-17)
  • ScalerizeSupportedCapabilities (22-29)
🔇 Additional comments (1)
execution/evm/client.go (1)

123-151: Enhance concurrency safety and improve naming for dialRPCCLient

  1. The function still updates c.engineClient or c.rpcClient without concurrency safeguards. Please ensure pointer assignments are not updated in parallel by multiple goroutines.
  2. For clarity, consider renaming dialRPCCLientdialRPCClient. This small rename aligns better with existing naming conventions and improves readability.
    [duplicate_comment, suggest_nitpick]

Comment on lines +74 to +83
func buildSignedJWT(s *JWTSecret) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"iat": &jwt.NumericDate{Time: time.Now()},
})
str, err := token.SignedString(s[:])
if err != nil {
return "", errors.Newf("failed to create JWT token: %w", err)
}
return str, nil
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider including an expiration claim in the JWT

Currently, only an "iat" (issued-at) claim is set. Following best practices, adding an "exp" (expiration) claim can help ensure tokens remain short-lived, mitigating risks if a token is compromised.

@rahulghangas rahulghangas merged commit 6adffa9 into main Apr 16, 2025
1 check passed
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.

3 participants