Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: minimal binary #11

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

feat: minimal binary #11

wants to merge 6 commits into from

Conversation

tzdybal
Copy link
Member

@tzdybal tzdybal commented Nov 18, 2024

Overview

This PR introduces minimal binary that exposes go-execution-evm as gRPC server.

Resolves #12.

Summary by CodeRabbit

  • New Features

    • Introduced a gRPC server for the Ethereum middleware application, enhancing communication capabilities.
    • Added a new target for building the evm-middleware binary in the Makefile.
  • Improvements

    • Streamlined the EngineAPIExecutionClient by removing the proxy client dependency, allowing for more direct interactions with the Ethereum engine API.
    • Enhanced documentation with detailed architecture diagrams and updated development instructions in the README.
  • Dependencies

    • Updated and replaced several dependencies to ensure compatibility with the latest versions.
    • Added new indirect dependencies to support recent features.
  • Chores

    • Updated the .gitignore file to include the build/ directory.

Copy link

coderabbitai bot commented Nov 18, 2024

Walkthrough

The changes introduce a new gRPC server for an Ethereum middleware application in main.go, which handles initialization, error management, and server lifecycle. The execution.go file sees the removal of the proxy client from the EngineAPIExecutionClient, simplifying its structure and methods. Additionally, the go.mod file is updated with new dependencies and a module replacement, ensuring compatibility with the latest versions while removing outdated ones.

Changes

File Change Summary
cmd/evm-middleware/main.go Implemented a gRPC server for Ethereum middleware, including initialization of JWT secret, gRPC proxy configuration, TCP listener setup, and error handling for client creation and listener setup.
execution.go Removed proxy_json_rpc client from EngineAPIExecutionClient, updated method signatures accordingly, and retained existing error handling in transaction-related methods.
execution_test.go Removed proxy_json_rpc import and updated test functions to reflect changes in NewEngineAPIExecutionClient instantiation without the proxy configuration.
integration_test.go Removed proxy_json_rpc references and updated test functions to instantiate NewEngineAPIExecutionClient without the proxy configuration.
go.mod Updated module path, replaced github.com/rollkit/go-execution with github.com/lastdotnet/go-execution, added several indirect dependencies, and removed golang.org/x/tools.
.gitignore Added entry to ignore build/ directory while retaining exception for .gitkeep in docker/jwttoken/.
Makefile Added new target for building evm-middleware binary and updated clean target to remove build directory with a modified echo message.
README.md Enhanced documentation with a mermaid diagram and new instructions for running the RETH application in Docker, along with a command for compiling the evm-middleware binary.

Assessment against linked issues

Objective Addressed Explanation
Setup connection to EVM and expose go-execution API via gRPC (#12)

Possibly related PRs

  • chore: initial EVM Execution Client Implementation #10: The changes in execution.go regarding the EngineAPIExecutionClient struct and its methods are directly related to the main PR's implementation of the gRPC server for the Ethereum middleware application, as both involve the execution client functionality.
  • test: fix integration & unit tests #14: Modifications in execution.go and execution_test.go streamline the execution client functionality, aligning with the main PR's goal to enhance the Ethereum middleware's execution capabilities.

Suggested reviewers

  • gupadhyaya
  • MSevey

Poem

In the land of code where rabbits play,
A gRPC server hops into the fray.
With dependencies fresh and a client anew,
The Ethereum dreams now come true!
So let’s celebrate this joyful spree,
For the middleware shines, oh so free! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3f8fa79 and 6eea299.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (1)
  • go.mod (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • go.mod

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • 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 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.

@tzdybal tzdybal changed the title initial changes + binary feat: simple binary Nov 20, 2024
@tzdybal tzdybal changed the title feat: simple binary feat: minimal binary Nov 20, 2024
@tzdybal tzdybal marked this pull request as ready for review November 20, 2024 07:59
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

🧹 Outside diff range and nitpick comments (4)
cmd/evm-middleware/main.go (2)

3-19: Consider adding context handling and documenting the buffer size constant

The imports look good, but consider adding context package for proper context handling in gRPC operations. Also, the buffer size constant would benefit from a comment explaining its purpose and why 1MB was chosen.

import (
+       "context"
        "errors"
        "github.com/ethereum/go-ethereum/common"
        "google.golang.org/grpc"
        "log"
        "net"
        "os"
        "sync"
        "time"
        ...
)

+// bufSize defines the maximum request size (1MB) for gRPC messages
const bufSize = 1024 * 1024

Also applies to: 21-21


29-32: Consider making configuration values configurable

The gRPC configuration uses hardcoded values for timeout and request size. These should be configurable to accommodate different deployment scenarios.

Consider using environment variables or configuration file:

+       defaultTimeout := 5 * time.Second
+       if timeoutStr := os.Getenv("GRPC_TIMEOUT"); timeoutStr != "" {
+               if parsed, err := time.ParseDuration(timeoutStr); err == nil {
+                       defaultTimeout = parsed
+               }
+       }
        config := &grpcproxy.Config{
-               DefaultTimeout: 5 * time.Second,
+               DefaultTimeout: defaultTimeout,
                MaxRequestSize: bufSize,
        }
go.mod (1)

Based on the commit history from the fork, I can now generate a final response:

Fork usage appears justified but requires tracking

The fork is 4 commits behind upstream but contains important changes:

  • Added context parameter to all executor methods (#31)
  • Updated dependencies (go-header, grpc, gogoproto)

The main change (adding context parameter) is a significant refactor that aligns with the PR's gRPC server objectives. However:

  • Create an issue to track merging these changes back to upstream
  • Plan to sync with upstream once PR #31 is reviewed and merged
🔗 Analysis chain

Line range hint 4-4: Verify the fork usage and track upstream changes.

Using a forked dependency (github.com/lastdotnet/go-execution) instead of the upstream repository could lead to maintenance challenges. Consider:

  1. Documenting why this fork is necessary
  2. Creating an issue to track the eventual migration back to the upstream repository
  3. Setting up automated notifications for updates in the upstream repository

Let me gather more information about the fork to understand the changes and their necessity.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the fork is ahead/behind the upstream repository
gh repo view github.com/rollkit/go-execution --json defaultBranchRef --jq '.defaultBranchRef.name' | \
xargs -I {} curl -s "https://api.github.com/repos/rollkit/go-execution/compare/{}...lastdotnet:go-execution:{}" | \
jq '.ahead_by, .behind_by'

Length of output: 247


Script:

#!/bin/bash
# Check the commit history and changes in the fork
gh api repos/lastdotnet/go-execution/commits --jq '.[0:4] | .[] | {sha: .sha, message: .commit.message, date: .commit.author.date}'

# Check if there are any open PRs from the fork to upstream
gh api search/issues --raw-field q="repo:rollkit/go-execution is:pr author:lastdotnet" --jq '.items[] | {title: .title, url: .html_url, state: .state}'

Length of output: 3387

execution.go (1)

Line range hint 1-283: Architecture review: Direct Engine API interaction is cleaner

The removal of the proxy client layer simplifies the architecture by allowing direct interaction with the Engine API. This change:

  1. Reduces complexity and potential points of failure
  2. Maintains proper security through JWT authentication
  3. Preserves comprehensive error handling throughout RPC calls

The implementation properly handles:

  • JWT-based authentication for Engine API
  • Context propagation for cancellation
  • Proper cleanup in Stop()
  • Comprehensive error wrapping

Consider documenting the architectural decision to remove the proxy client layer in the project's architecture documentation, explaining the benefits of direct Engine API interaction.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1403e27 and d698728.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (3)
  • cmd/evm-middleware/main.go (1 hunks)
  • execution.go (1 hunks)
  • go.mod (4 hunks)
🔇 Additional comments (4)
go.mod (3)

110-110: LGTM: gRPC dependencies are appropriate.

The addition of gRPC and related dependencies aligns well with the PR objective of implementing a gRPC server. The versions chosen are stable and include necessary supporting packages.

Also applies to: 96-99


15-15: Verify security of new indirect dependencies.

Several new indirect dependencies have been added. Please ensure they don't have known vulnerabilities:

  • github.com/VictoriaMetrics/fastcache v1.12.2
  • github.com/cespare/xxhash/v2 v2.3.0
  • github.com/cosmos/gogoproto v1.7.0
  • github.com/google/go-cmp v0.6.0
  • github.com/holiman/bloomfilter/v2 v2.0.3

Also applies to: 18-18, 22-22, 40-40, 43-43

✅ Verification successful

No known vulnerabilities found in the new indirect dependencies

The security check performed through GitHub's Security Advisory Database shows no known vulnerabilities for any of the new indirect dependencies at their specified versions:

  • github.com/VictoriaMetrics/fastcache v1.12.2
  • github.com/cespare/xxhash/v2 v2.3.0
  • github.com/cosmos/gogoproto v1.7.0
  • github.com/google/go-cmp v0.6.0
  • github.com/holiman/bloomfilter/v2 v2.0.3
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for known vulnerabilities in new dependencies
gh api graphql -f query='
query {
  securityVulnerabilities(first: 5, ecosystem: GO, package: "github.com/VictoriaMetrics/fastcache") {
    nodes {
      advisory {
        summary
        severity
      }
      vulnerableVersionRange
    }
  }
}' 

# Repeat for other new dependencies
for pkg in "github.com/cespare/xxhash" "github.com/cosmos/gogoproto" "github.com/google/go-cmp" "github.com/holiman/bloomfilter"; do
  gh api graphql -f query="
  query {
    securityVulnerabilities(first: 5, ecosystem: GO, package: \"$pkg\") {
      nodes {
        advisory {
          summary
          severity
        }
        vulnerableVersionRange
      }
    }
  }"
done

Length of output: 2069


Line range hint 7-10: Verify the specific go-ethereum version requirement.

Using a specific commit hash (1.14.6-0.20241115130523-7c0ff0568509) of go-ethereum instead of a released version. Please:

  1. Document why this specific version is required
  2. Create an issue to track upgrading to the next stable release
✅ Verification successful

Let me gather more information about the dependency usage.


Let me check the actual usage of go-ethereum in the code to understand if there are any specific version dependencies.


Based on the gathered information, I can now provide a final response about the specific go-ethereum version requirement.

The specific go-ethereum version is required for Cancun upgrade compatibility

The commit 7c0ff0568509 is from November 15, 2024, and contains Go 1.23.3 build updates. The integration tests reveal that this version is specifically needed for Cancun fork compatibility, as evidenced by:

  • The genesis configuration in docker/chain/genesis.json includes "cancunTime": 1710338100
  • Test case TestExecutionClient_InitChain_InvalidPayloadTimestamp explicitly tests Cancun timestamp validation
  • The code uses engine_forkchoiceUpdatedV3 and engine_getPayloadV3 APIs which are part of the Cancun upgrade
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the commit message and associated PR for the specific go-ethereum version
curl -s "https://api.github.com/repos/ethereum/go-ethereum/commits/7c0ff0568509" | \
jq '.commit.message'

Length of output: 228


Script:

#!/bin/bash
# Check if there are any specific imports or usage patterns from go-ethereum
rg --type go "github.com/ethereum/go-ethereum" -l

# Look for any documentation about the version requirement
fd README.md | xargs rg -i "ethereum|geth" -A 2

Length of output: 388


Script:

#!/bin/bash
# Check the actual imports and usage from go-ethereum in the main files
rg --type go "github.com/ethereum/go-ethereum" -B 2 -A 2

# Check if there are any recent changes or commits mentioning the version update
git log -p --grep="ethereum" --grep="geth"

Length of output: 115220

execution.go (1)

93-94: Consider adding basic client readiness checks

While the simplified implementation aligns with removing the proxy client, consider adding basic validation to ensure the RPC clients are properly initialized and ready:

func (c *EngineAPIExecutionClient) Start() error {
+	// Verify engine client is initialized and responsive
+	var version string
+	if err := c.engineClient.CallContext(context.Background(), &version, "web3_clientVersion"); err != nil {
+		return fmt.Errorf("engine client not ready: %w", err)
+	}
+
+	// Verify eth client is initialized and responsive
+	if _, err := c.ethClient.BlockNumber(context.Background()); err != nil {
+		return fmt.Errorf("eth client not ready: %w", err)
+	}
+
	return nil
}

Let's verify the interface requirements:

Comment on lines +24 to +26
jwtSecret := ""
if len(os.Args) == 2 {
jwtSecret = os.Args[1]
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance JWT secret handling security

The current JWT secret handling has several security concerns:

  1. Allows empty JWT secret without warning
  2. No validation of secret strength or format
  3. Accepts secrets directly from command line (visible in process list)

Consider:

  1. Requiring JWT secret through environment variables or file
  2. Implementing minimum security requirements
  3. Adding proper validation
-       jwtSecret := ""
-       if len(os.Args) == 2 {
-               jwtSecret = os.Args[1]
-       }
+       jwtSecret := os.Getenv("JWT_SECRET")
+       if jwtSecret == "" {
+               log.Fatal("JWT_SECRET environment variable is required")
+       }
+       if len(jwtSecret) < 32 {
+               log.Fatal("JWT_SECRET must be at least 32 characters long")
+       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jwtSecret := ""
if len(os.Args) == 2 {
jwtSecret = os.Args[1]
}
jwtSecret := os.Getenv("JWT_SECRET")
if jwtSecret == "" {
log.Fatal("JWT_SECRET environment variable is required")
}
if len(jwtSecret) < 32 {
log.Fatal("JWT_SECRET must be at least 32 characters long")
}

Comment on lines 34 to 39
listener, err := net.Listen("tcp4", "0.0.0.0:40041")
if err != nil {
log.Fatalf("error while creating listener: %v\n", err)
}
defer func() {
_ = listener.Close()
}()
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve network listener configuration and error handling

Several improvements can be made to the listener setup:

  1. Hardcoded port should be configurable
  2. Consider supporting both IPv4 and IPv6
  3. Listener close errors should be logged
+       port := os.Getenv("GRPC_PORT")
+       if port == "" {
+               port = "40041"
+       }
-       listener, err := net.Listen("tcp4", "0.0.0.0:40041")
+       listener, err := net.Listen("tcp", ":"+port)
        if err != nil {
                log.Fatalf("error while creating listener: %v\n", err)
        }
        defer func() {
-               _ = listener.Close()
+               if err := listener.Close(); err != nil {
+                       log.Printf("error closing listener: %v\n", err)
+               }
        }()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
listener, err := net.Listen("tcp4", "0.0.0.0:40041")
if err != nil {
log.Fatalf("error while creating listener: %v\n", err)
}
defer func() {
_ = listener.Close()
}()
port := os.Getenv("GRPC_PORT")
if port == "" {
port = "40041"
}
listener, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatalf("error while creating listener: %v\n", err)
}
defer func() {
if err := listener.Close(); err != nil {
log.Printf("error closing listener: %v\n", err)
}
}()

Comment on lines +56 to +72
log.Println("Starting server...")
server := grpcproxy.NewServer(evmClient, config)
s := grpc.NewServer()
pb.RegisterExecutionServiceServer(s, server)

wg := sync.WaitGroup{}
wg.Add(1)

go func() {
log.Println("Serving...")
if err := s.Serve(listener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
log.Fatalf("Server exited with error: %v\n", err)
}
wg.Done()
}()
defer s.Stop()

wg.Wait()
log.Println("Server stopped")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement proper graceful shutdown handling

The server lifecycle management needs improvement:

  1. Missing signal handling for graceful shutdown
  2. No timeout for graceful shutdown
  3. No context cancellation

Consider implementing proper shutdown handling:

+       ctx, cancel := context.WithCancel(context.Background())
+       defer cancel()
        
        log.Println("Starting server...")
-       server := grpcproxy.NewServer(evmClient, config)
+       server := grpcproxy.NewServer(evmClient, config)
        s := grpc.NewServer()
        pb.RegisterExecutionServiceServer(s, server)

        wg := sync.WaitGroup{}
        wg.Add(1)

+       // Handle shutdown signals
+       sigCh := make(chan os.Signal, 1)
+       signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
+       go func() {
+               <-sigCh
+               log.Println("Received shutdown signal")
+               cancel()
+               s.GracefulStop()
+       }()

        go func() {
                log.Println("Serving...")
                if err := s.Serve(listener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
                        log.Printf("Server exited with error: %v\n", err)
                }
                wg.Done()
        }()
-       defer s.Stop()

-       wg.Wait()
+       // Wait for either context cancellation or server stop
+       select {
+       case <-ctx.Done():
+               log.Println("Context cancelled, initiating shutdown")
+               s.GracefulStop()
+       }
+       
+       // Wait with timeout
+       done := make(chan struct{})
+       go func() {
+               wg.Wait()
+               close(done)
+       }()
+       select {
+       case <-done:
+               log.Println("Server stopped gracefully")
+       case <-time.After(10 * time.Second):
+               log.Println("Shutdown timeout, forcing stop")
+               s.Stop()
+       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log.Println("Starting server...")
server := grpcproxy.NewServer(evmClient, config)
s := grpc.NewServer()
pb.RegisterExecutionServiceServer(s, server)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
log.Println("Serving...")
if err := s.Serve(listener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
log.Fatalf("Server exited with error: %v\n", err)
}
wg.Done()
}()
defer s.Stop()
wg.Wait()
log.Println("Server stopped")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
log.Println("Starting server...")
server := grpcproxy.NewServer(evmClient, config)
s := grpc.NewServer()
pb.RegisterExecutionServiceServer(s, server)
wg := sync.WaitGroup{}
wg.Add(1)
// Handle shutdown signals
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigCh
log.Println("Received shutdown signal")
cancel()
s.GracefulStop()
}()
go func() {
log.Println("Serving...")
if err := s.Serve(listener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
log.Printf("Server exited with error: %v\n", err)
}
wg.Done()
}()
// Wait for either context cancellation or server stop
select {
case <-ctx.Done():
log.Println("Context cancelled, initiating shutdown")
s.GracefulStop()
}
// Wait with timeout
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
log.Println("Server stopped gracefully")
case <-time.After(10 * time.Second):
log.Println("Shutdown timeout, forcing stop")
s.Stop()
}

Comment on lines 42 to 52
// TODO(tzdybal): initialize from genesis file?
var genesisHash common.Hash
var feeRecipient common.Address

genesis := core.DefaultGenesisBlock()
genesisHash = genesis.ToBlock().Hash()

evmClient, err := evm.NewEngineAPIExecutionClient("http://:8545", "http://8551", jwtSecret, genesisHash, feeRecipient)
if err != nil {
log.Fatalf("failed to create Engine API client middleware: %v", err)
}
_ = evmClient.Start()
defer evmClient.Stop()
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

Genesis file exists but core package needs genesis loading implementation

The review suggestions need modification based on the verification results:

  1. A genesis file exists at docker/chain/genesis.json with proper chain configuration, but the core package doesn't appear to have LoadGenesisFile functionality yet.
  2. DefaultGenesisBlock function referenced in the code is not found in the codebase.
  3. No existing environment variable configurations were found.

Consider this revised implementation:

-       // TODO(tzdybal): initialize from genesis file?
+       // Load genesis from file
+       genesisData, err := os.ReadFile("docker/chain/genesis.json")
+       if err != nil {
+               log.Fatalf("failed to read genesis file: %v", err)
+       }
+       genesis := new(core.Genesis)
+       if err := json.Unmarshal(genesisData, genesis); err != nil {
+               log.Fatalf("failed to parse genesis file: %v", err)
+       }
        genesisHash = genesis.ToBlock().Hash()

-       evmClient, err := evm.NewEngineAPIExecutionClient("http://:8545", "http://8551", jwtSecret, genesisHash, feeRecipient)
+       evmClient, err := evm.NewEngineAPIExecutionClient(
+               "http://localhost:8545",  // Default HTTP URL
+               "http://localhost:8551",  // Default Engine URL
+               jwtSecret,
+               genesisHash,
+               feeRecipient,
+       )
        if err != nil {
                log.Fatalf("failed to create Engine API client middleware: %v", err)
        }
-       _ = evmClient.Start()
+       if err := evmClient.Start(); err != nil {
+               log.Fatalf("failed to start EVM client: %v", err)
+       }
🔗 Analysis chain

Address TODO and improve client initialization

Several critical issues need attention:

  1. TODO comment needs to be addressed regarding genesis file initialization
  2. Hardcoded URLs in client initialization
  3. Client Start() error is ignored

Consider this implementation:

-       // TODO(tzdybal): initialize from genesis file?
+       var genesis *core.Genesis
+       genesisPath := os.Getenv("GENESIS_PATH")
+       if genesisPath != "" {
+               // Load genesis from file
+               genesis, err = core.LoadGenesisFile(genesisPath)
+               if err != nil {
+                       log.Fatalf("failed to load genesis file: %v", err)
+               }
+       } else {
+               genesis = core.DefaultGenesisBlock()
+       }
        genesisHash = genesis.ToBlock().Hash()

-       evmClient, err := evm.NewEngineAPIExecutionClient("http://:8545", "http://8551", jwtSecret, genesisHash, feeRecipient)
+       httpURL := os.Getenv("EVM_HTTP_URL")
+       if httpURL == "" {
+               httpURL = "http://localhost:8545"
+       }
+       engineURL := os.Getenv("EVM_ENGINE_URL")
+       if engineURL == "" {
+               engineURL = "http://localhost:8551"
+       }
+       evmClient, err := evm.NewEngineAPIExecutionClient(httpURL, engineURL, jwtSecret, genesisHash, feeRecipient)
        if err != nil {
                log.Fatalf("failed to create Engine API client middleware: %v", err)
        }
-       _ = evmClient.Start()
+       if err := evmClient.Start(); err != nil {
+               log.Fatalf("failed to start EVM client: %v", err)
+       }
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any genesis-related files in the repository
fd -e json -e toml -e yaml genesis

Length of output: 62


Script:

#!/bin/bash
# Check the content of genesis.json to understand its structure
cat docker/chain/genesis.json

# Look for genesis-related code in the codebase
rg -g '!{node_modules,dist,build}/**' -l 'LoadGenesisFile|DefaultGenesisBlock'

# Check if there are any environment variable configurations
rg -g '!{node_modules,dist,build}/**' 'EVM_HTTP_URL|EVM_ENGINE_URL|GENESIS_PATH'

Length of output: 1145


Script:

#!/bin/bash
# Check if core package has genesis loading functionality
ast-grep --pattern 'func LoadGenesisFile'

# Check how DefaultGenesisBlock is implemented
ast-grep --pattern 'func DefaultGenesisBlock'

# Look for any existing environment variable configurations in the codebase
rg -A 5 'os.Getenv'

Length of output: 111

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

🧹 Outside diff range and nitpick comments (4)
cmd/evm-middleware/main.go (3)

3-19: Improve import organization following Go conventions

The imports should be grouped into:

  1. Standard library
  2. External packages
  3. Internal packages
 import (
 	"errors"
-	"github.com/ethereum/go-ethereum/common"
-	"google.golang.org/grpc"
 	"log"
 	"net"
 	"os"
 	"sync"
 	"time"

+	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core"
+	"google.golang.org/grpc"
 
 	grpcproxy "github.com/rollkit/go-execution/proxy/grpc"
 	pb "github.com/rollkit/go-execution/types/pb/execution"
 	evm "github.com/rollkit/go-execution-evm"
 )
🧰 Tools
🪛 GitHub Check: lint / golangci-lint

[failure] 5-5:
File is not goimports-ed with -local github.com/rollkit (goimports)

🪛 golangci-lint

5-5: File is not goimports-ed with -local github.com/rollkit

(goimports)


21-21: Add documentation for the buffer size constant

The bufSize constant should be documented to explain its purpose and why this specific value was chosen.

+// bufSize defines the maximum size in bytes for gRPC message handling
 const bufSize = 1024 * 1024

29-32: Make gRPC configuration values configurable

The timeout and request size values should be configurable through environment variables to support different deployment scenarios.

+       defaultTimeout := os.Getenv("GRPC_DEFAULT_TIMEOUT")
+       timeout := 5 * time.Second
+       if defaultTimeout != "" {
+               if t, err := time.ParseDuration(defaultTimeout); err == nil {
+                       timeout = t
+               }
+       }
+
+       maxSize := bufSize
+       if size := os.Getenv("GRPC_MAX_REQUEST_SIZE"); size != "" {
+               if s, err := strconv.Atoi(size); err == nil {
+                       maxSize = s
+               }
+       }
+
        config := &grpcproxy.Config{
-               DefaultTimeout: 5 * time.Second,
-               MaxRequestSize: bufSize,
+               DefaultTimeout: timeout,
+               MaxRequestSize: maxSize,
        }
execution.go (1)

Line range hint 28-36: Enhance type documentation

Consider adding documentation about the removal of proxy functionality and the architectural implications.

Add a comment block above the type declaration:

+// EngineAPIExecutionClient implements the execution.Executor interface by directly
+// communicating with the Ethereum execution layer through the Engine API. It manages
+// connections to both the engine API and eth client, handling blockchain operations
+// such as transaction execution, chain initialization, and block finalization.
type EngineAPIExecutionClient struct {
	engineClient *rpc.Client // engine api
	ethClient    *ethclient.Client
	genesisHash  common.Hash
	feeRecipient common.Address
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d698728 and 0a2b882.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • cmd/evm-middleware/main.go (1 hunks)
  • execution.go (1 hunks)
  • execution_test.go (0 hunks)
  • go.mod (4 hunks)
  • integration_test.go (0 hunks)
💤 Files with no reviewable changes (2)
  • execution_test.go
  • integration_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • go.mod
🧰 Additional context used
🪛 GitHub Check: lint / golangci-lint
cmd/evm-middleware/main.go

[failure] 5-5:
File is not goimports-ed with -local github.com/rollkit (goimports)

🪛 golangci-lint
cmd/evm-middleware/main.go

5-5: File is not goimports-ed with -local github.com/rollkit

(goimports)


[medium] 34-34: G102: Binds to all network interfaces

(gosec)

🔇 Additional comments (1)
execution.go (1)

Line range hint 1-276: Verify complete removal of proxy-related code

The removal of proxy functionality is a significant architectural change. Let's verify that all proxy-related code has been consistently removed throughout the codebase.

✅ Verification successful

The initial search didn't find any remaining proxy-related code, but let's perform a broader search to be more thorough:


Proxy-related code is properly isolated to gRPC middleware component

The search results show that proxy-related code only exists in cmd/evm-middleware/main.go which is a separate gRPC middleware component. This is the expected location for such functionality, and there are no remnants of proxy code in the main execution client implementation we're reviewing. The architectural changes in execution.go have been done consistently.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any remaining proxy-related code or references

# Search for proxy-related imports
echo "Checking for proxy-related imports..."
rg -l "proxy_json_rpc"

# Search for proxy-related type references
echo "Checking for proxy-related type references..."
rg -l "proxyClient"

# Search for proxy-related configuration
echo "Checking for proxy-related configuration..."
rg -l "ProxyConfig"

# Search for proxy-related method calls
echo "Checking for proxy-related method calls..."
ast-grep --pattern 'proxyClient.$_($_)'

Length of output: 482


Script:

#!/bin/bash
# Broader search for proxy-related terms with context
echo "Checking for any proxy-related terms..."
rg -i "proxy" -A 2 -B 2

# Check for any RPC proxy patterns
echo "Checking for RPC proxy patterns..."
rg -i "rpc.*proxy|proxy.*rpc" -A 2 -B 2

# Look for any proxy in type definitions
ast-grep --pattern 'type $_ struct {
  $$$
  proxy
  $$$
}'

# Check for proxy in interface definitions
ast-grep --pattern 'type $_ interface {
  $$$
  proxy
  $$$
}'

Length of output: 1994


import (
"errors"
"github.com/ethereum/go-ethereum/common"
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there a make fmt option in the Makefile yet? that would be good to format these imports.

MSevey
MSevey previously requested changes Nov 20, 2024
Copy link
Collaborator

@MSevey MSevey left a comment

Choose a reason for hiding this comment

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

Can we add documentation to the README and a Makefile command for building the binary?

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

🧹 Outside diff range and nitpick comments (4)
README.md (3)

66-72: Enhance Docker setup instructions

The Docker setup instructions could be more comprehensive to help users get started. Consider adding:

  • Prerequisites (Docker and Docker Compose installation)
  • Expected output or verification steps
  • Common troubleshooting tips
 Run RETH in docker:
+
+### Prerequisites
+- Docker
+- Docker Compose v2.x
+
+### Steps
 ```bash
-$ cd docker
-$ docker compose up -d
-$ docker compose down
+cd docker
+docker compose up -d
+
+# Verify the containers are running
+docker compose ps
+
+# To stop the containers
+docker compose down

+### Troubleshooting
+If you encounter issues:
+1. Ensure ports 8545 and 8551 are available
+2. Check container logs: docker compose logs


<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Markdownlint (0.35.0)</summary>

68-68: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

69-69: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

70-70: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

67-67: null
Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

</details>

</details>

---

`73-76`: **Expand binary compilation instructions**

The binary compilation instructions should include more details about the build process and requirements.

```diff
 Compile `evm-middleware` binary:
+
+### Prerequisites
+- Go 1.20 or later
+
 ```bash
-make build
+# Build the binary
+make build
+
+# Verify the binary was created
+./bin/evm-middleware --help

+The binary will be created in the bin directory and implements a gRPC server that exposes the go-execution API.


<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Markdownlint (0.35.0)</summary>

74-74: null
Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

---

76-76: null
Files should end with a single newline character

(MD047, single-trailing-newline)

</details>

</details>

---

Line range hint `1-76`: **Fix markdown formatting issues**

There are several markdown formatting issues that should be addressed for better documentation quality.

1. Add blank lines around fenced code blocks
2. Remove `$` signs from command examples unless showing output
3. Ensure file ends with a single newline

Apply these changes throughout the file for consistent formatting.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Markdownlint (0.35.0)</summary>

68-68: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

69-69: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

70-70: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

67-67: null
Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

---

74-74: null
Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

---

76-76: null
Files should end with a single newline character

(MD047, single-trailing-newline)

</details>

</details>

</blockquote></details>
<details>
<summary>Makefile (1)</summary><blockquote>

`20-22`: **Consider enhancing the build recipe for robustness.**

While the current implementation is functional, consider these improvements:
1. Create build directory if it doesn't exist
2. Add build flags for optimization/debugging

Consider this enhanced version:

```diff
 build/evm-middleware: cmd/evm-middleware/main.go execution.go go.mod go.sum
 	@echo "Building build/evm-middleware"
+	@mkdir -p build
-	@go build -o build/evm-middleware ./cmd/evm-middleware
+	@go build -trimpath -ldflags="-s -w" -o build/evm-middleware ./cmd/evm-middleware

The changes:

  • mkdir -p build: Ensures build directory exists
  • -trimpath: Removes file system paths from binary for reproducible builds
  • -ldflags="-s -w": Reduces binary size by removing debug information
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0a2b882 and fd906a0.

📒 Files selected for processing (3)
  • .gitignore (1 hunks)
  • Makefile (1 hunks)
  • README.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🧰 Additional context used
🪛 Markdownlint (0.35.0)
README.md

68-68: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


69-69: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


70-70: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


67-67: null
Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


74-74: null
Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


76-76: null
Files should end with a single newline character

(MD047, single-trailing-newline)

🔇 Additional comments (2)
Makefile (2)

16-18: LGTM! Well-structured build target declaration.

The build target is properly documented and follows Makefile best practices.


26-28: Consider making clean target more robust.

The clean target correctly removes the build directory, but could be more resilient.

Consider this minor enhancement:

 	@echo "--> Clearing testcache & build directory"
 	@go clean --testcache
-	@rm -rf build
+	@rm -rf build/

Also, ensure that the build/ directory is added to .gitignore if not already present.

@tzdybal
Copy link
Member Author

tzdybal commented Nov 25, 2024

Addressed @MSevey's comments. Other errors/issues (linting, etc) will be addressed in next PRs.

@tzdybal tzdybal requested a review from MSevey November 25, 2024 10:14
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

🧹 Outside diff range and nitpick comments (2)
Makefile (2)

16-22: Consider improving build target configuration and robustness

While the build target is functional, consider these improvements for better maintainability:

  1. Make the binary path configurable via a variable
  2. Explicitly create the build directory
+BINARY_NAME := evm-middleware
+BUILD_DIR := build
+
 ## build: build evm-middleware binary
-build: build/evm-middleware
+build: $(BUILD_DIR)/$(BINARY_NAME)
 .PHONY: build
 
-build/evm-middleware: cmd/evm-middleware/main.go execution.go go.mod go.sum
-	@echo "Building build/evm-middleware"
-	@go build -o build/evm-middleware ./cmd/evm-middleware
+$(BUILD_DIR)/$(BINARY_NAME): cmd/evm-middleware/main.go execution.go go.mod go.sum
+	@mkdir -p $(BUILD_DIR)
+	@echo "Building $(BUILD_DIR)/$(BINARY_NAME)"
+	@go build -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/evm-middleware

65-66: Consider adding golangci-lint installation check

While the formatting command is correct, consider adding a check for the required tool to provide better error messages.

+GOLANGCI_LINT := $(shell which golangci-lint)
+
 ## fmt: Run fixes for linters. Currently only markdownlint.
 fmt:
+	@if [ -z "$(GOLANGCI_LINT)" ]; then \
+		echo "golangci-lint is not installed. Please install it first."; \
+		exit 1; \
+	fi
 	@echo "--> Formatting markdownlint"
 	@markdownlint --config .markdownlint.yaml '**/*.md' -f
 	@echo "--> Formatting go"
 	@golangci-lint run --fix
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fd906a0 and bfbb7a4.

📒 Files selected for processing (2)
  • Makefile (2 hunks)
  • README.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md
🔇 Additional comments (1)
Makefile (1)

26-28: LGTM!

The clean target has been properly updated to handle the new build directory.

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

🧹 Outside diff range and nitpick comments (2)
cmd/evm-middleware/main.go (2)

21-21: Document the buffer size constant

The bufSize constant's value (1MB) should be documented with a comment explaining why this specific size was chosen and what it represents in the context of gRPC message handling.

+// bufSize defines the maximum size (1MB) for gRPC message handling
 const bufSize = 1024 * 1024

29-32: Make gRPC configuration values configurable

The timeout and request size values should be configurable via environment variables to support different deployment scenarios.

+       defaultTimeout := 5 * time.Second
+       if timeoutStr := os.Getenv("GRPC_DEFAULT_TIMEOUT"); timeoutStr != "" {
+               if timeout, err := time.ParseDuration(timeoutStr); err == nil {
+                       defaultTimeout = timeout
+               }
+       }
+
+       maxRequestSize := bufSize
+       if sizeStr := os.Getenv("GRPC_MAX_REQUEST_SIZE"); sizeStr != "" {
+               if size, err := strconv.Atoi(sizeStr); err == nil {
+                       maxRequestSize = size
+               }
+       }
+
        config := &grpcproxy.Config{
-               DefaultTimeout: 5 * time.Second,
-               MaxRequestSize: bufSize,
+               DefaultTimeout: defaultTimeout,
+               MaxRequestSize: maxRequestSize,
        }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bfbb7a4 and 78f7afd.

📒 Files selected for processing (1)
  • cmd/evm-middleware/main.go (1 hunks)
🧰 Additional context used
🪛 GitHub Check: lint / golangci-lint
cmd/evm-middleware/main.go

[failure] 5-5:
File is not goimports-ed with -local github.com/rollkit (goimports)

🪛 golangci-lint (1.61.0)
cmd/evm-middleware/main.go

5-5: File is not goimports-ed with -local github.com/rollkit

(goimports)


[medium] 34-34: G102: Binds to all network interfaces

(gosec)

Comment on lines +49 to +51
evmClient, err := evm.NewEngineAPIExecutionClient("http://:8545", "http://:8551", jwtSecret, genesisHash, feeRecipient)
if err != nil {
log.Fatalf("failed to create Engine API client middleware: %v", err)
}
_ = evmClient.Start()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix EVM client configuration and error handling

Several issues need to be addressed:

  1. HTTP and Engine API URLs are hardcoded with empty host
  2. Error from Start() is ignored
+       httpURL := os.Getenv("EVM_HTTP_URL")
+       if httpURL == "" {
+               httpURL = "http://localhost:8545"
+       }
+
+       engineURL := os.Getenv("EVM_ENGINE_URL")
+       if engineURL == "" {
+               engineURL = "http://localhost:8551"
+       }
+
-       evmClient, err := evm.NewEngineAPIExecutionClient("http://:8545", "http://:8551", jwtSecret, genesisHash, feeRecipient)
+       evmClient, err := evm.NewEngineAPIExecutionClient(httpURL, engineURL, jwtSecret, genesisHash, feeRecipient)
        if err != nil {
                log.Fatalf("failed to create Engine API client middleware: %v", err)
        }
-       _ = evmClient.Start()
+       if err := evmClient.Start(); err != nil {
+               log.Fatalf("failed to start EVM client: %v", err)
+       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
evmClient, err := evm.NewEngineAPIExecutionClient("http://:8545", "http://:8551", jwtSecret, genesisHash, feeRecipient)
if err != nil {
log.Fatalf("failed to create Engine API client middleware: %v", err)
}
_ = evmClient.Start()
httpURL := os.Getenv("EVM_HTTP_URL")
if httpURL == "" {
httpURL = "http://localhost:8545"
}
engineURL := os.Getenv("EVM_ENGINE_URL")
if engineURL == "" {
engineURL = "http://localhost:8551"
}
evmClient, err := evm.NewEngineAPIExecutionClient(httpURL, engineURL, jwtSecret, genesisHash, feeRecipient)
if err != nil {
log.Fatalf("failed to create Engine API client middleware: %v", err)
}
if err := evmClient.Start(); err != nil {
log.Fatalf("failed to start EVM client: %v", err)
}


// TODO(tzdybal): initialize from genesis file?
var genesisHash common.Hash
var feeRecipient common.Address
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Configure fee recipient address

The fee recipient address should be configurable via environment variable to support different deployment scenarios.

+       feeRecipientStr := os.Getenv("FEE_RECIPIENT_ADDRESS")
+       if feeRecipientStr != "" {
+               if !common.IsHexAddress(feeRecipientStr) {
+                       log.Fatalf("invalid fee recipient address format: %s", feeRecipientStr)
+               }
+               feeRecipient = common.HexToAddress(feeRecipientStr)
+       } else {
+               log.Println("Warning: No fee recipient address configured")
+       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var feeRecipient common.Address
var feeRecipient common.Address
feeRecipientStr := os.Getenv("FEE_RECIPIENT_ADDRESS")
if feeRecipientStr != "" {
if !common.IsHexAddress(feeRecipientStr) {
log.Fatalf("invalid fee recipient address format: %s", feeRecipientStr)
}
feeRecipient = common.HexToAddress(feeRecipientStr)
} else {
log.Println("Warning: No fee recipient address configured")
}

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

🧹 Outside diff range and nitpick comments (1)
cmd/evm-middleware/main.go (1)

27-30: Make gRPC configuration values configurable

The timeout and buffer size values should be configurable to support different deployment scenarios.

+const (
+    defaultTimeout = 5 * time.Second
+    defaultBufSize = 1024 * 1024
+)
+
 func main() {
+    timeoutStr := os.Getenv("GRPC_TIMEOUT")
+    timeout := defaultTimeout
+    if timeoutStr != "" {
+        parsedTimeout, err := time.ParseDuration(timeoutStr)
+        if err != nil {
+            log.Printf("Invalid GRPC_TIMEOUT value: %v, using default", err)
+        } else {
+            timeout = parsedTimeout
+        }
+    }
+
+    bufSizeStr := os.Getenv("GRPC_MAX_REQUEST_SIZE")
+    bufSize := defaultBufSize
+    if bufSizeStr != "" {
+        parsedSize, err := strconv.Atoi(bufSizeStr)
+        if err != nil {
+            log.Printf("Invalid GRPC_MAX_REQUEST_SIZE value: %v, using default", err)
+        } else {
+            bufSize = parsedSize
+        }
+    }
+
     config := &grpcproxy.Config{
-        DefaultTimeout: 5 * time.Second,
+        DefaultTimeout: timeout,
         MaxRequestSize: bufSize,
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 78f7afd and 12f4ab5.

📒 Files selected for processing (1)
  • cmd/evm-middleware/main.go (1 hunks)
🧰 Additional context used
🪛 golangci-lint (1.62.2)
cmd/evm-middleware/main.go

5-5: File is not goimports-ed with -local github.com/rollkit

(goimports)


[medium] 32-32: G102: Binds to all network interfaces

(gosec)

🪛 GitHub Check: lint / golangci-lint
cmd/evm-middleware/main.go

[failure] 5-5:
File is not goimports-ed with -local github.com/rollkit (goimports)

var genesisHash common.Hash
var feeRecipient common.Address

genesisHash = common.HexToHash("0x8bf225d50da44f60dee1c4ee6f810fe5b44723c76ac765654b6692d50459f216")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove hardcoded genesis hash

The genesis hash should not be hardcoded as it may vary across different networks and environments.

-       genesisHash = common.HexToHash("0x8bf225d50da44f60dee1c4ee6f810fe5b44723c76ac765654b6692d50459f216")
+       genesisHashStr := os.Getenv("GENESIS_HASH")
+       if genesisHashStr == "" {
+               log.Fatal("GENESIS_HASH environment variable is required")
+       }
+       if !common.IsHexAddress(genesisHashStr) {
+               log.Fatal("Invalid genesis hash format")
+       }
+       genesisHash = common.HexToHash(genesisHashStr)

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

MaxRequestSize: bufSize,
}

listener, err := net.Listen("tcp4", "0.0.0.0:40041")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security: Restrict network binding

Binding to all network interfaces (0.0.0.0) in production could expose the service to unauthorized access. Consider restricting the binding address based on the deployment environment.

+       // Default to localhost for security, allow override for production
+       bindAddr := os.Getenv("GRPC_BIND_ADDR")
+       if bindAddr == "" {
+               bindAddr = "127.0.0.1"
+       }
+       bindPort := os.Getenv("GRPC_PORT")
+       if bindPort == "" {
+               bindPort = "40041"
+       }
-       listener, err := net.Listen("tcp4", "0.0.0.0:40041")
+       listener, err := net.Listen("tcp", fmt.Sprintf("%s:%s", bindAddr, bindPort))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
listener, err := net.Listen("tcp4", "0.0.0.0:40041")
// Default to localhost for security, allow override for production
bindAddr := os.Getenv("GRPC_BIND_ADDR")
if bindAddr == "" {
bindAddr = "127.0.0.1"
}
bindPort := os.Getenv("GRPC_PORT")
if bindPort == "" {
bindPort = "40041"
}
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%s", bindAddr, bindPort))
🧰 Tools
🪛 golangci-lint (1.62.2)

[medium] 32-32: G102: Binds to all network interfaces

(gosec)

Comment on lines +63 to +66
if err := s.Serve(listener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
log.Fatalf("Server exited with error: %v\n", err)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve server error handling

Using log.Fatalf on server error prevents proper cleanup through deferred functions. Consider logging the error and triggering a graceful shutdown instead.

-               if err := s.Serve(listener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
-                       log.Fatalf("Server exited with error: %v\n", err)
-               }
+               if err := s.Serve(listener); err != nil {
+                       if !errors.Is(err, grpc.ErrServerStopped) {
+                               log.Printf("Server exited with error: %v\n", err)
+                       }
+                       // Trigger graceful shutdown
+                       s.GracefulStop()
+               }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if err := s.Serve(listener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
log.Fatalf("Server exited with error: %v\n", err)
}
if err := s.Serve(listener); err != nil {
if !errors.Is(err, grpc.ErrServerStopped) {
log.Printf("Server exited with error: %v\n", err)
}
// Trigger graceful shutdown
s.GracefulStop()
}

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

🧹 Outside diff range and nitpick comments (3)
cmd/evm-middleware/main.go (3)

19-19: Add documentation for the buffer size constant

Document the purpose and implications of the 1MB buffer size constant.

-const bufSize = 1024 * 1024
+// bufSize defines the maximum size (1MB) for gRPC message handling
+const bufSize = 1024 * 1024

27-30: Make gRPC configuration values configurable

The timeout and request size should be configurable via environment variables to support different deployment scenarios.

+       timeoutStr := os.Getenv("GRPC_TIMEOUT_SECONDS")
+       timeout := 5 * time.Second
+       if timeoutStr != "" {
+               if t, err := time.ParseDuration(timeoutStr + "s"); err == nil {
+                       timeout = t
+               }
+       }
+
+       maxSizeStr := os.Getenv("GRPC_MAX_REQUEST_SIZE")
+       maxSize := bufSize
+       if maxSizeStr != "" {
+               if s, err := strconv.Atoi(maxSizeStr); err == nil {
+                       maxSize = s
+               }
+       }
+
        config := &grpcproxy.Config{
-               DefaultTimeout: 5 * time.Second,
-               MaxRequestSize: bufSize,
+               DefaultTimeout: timeout,
+               MaxRequestSize: maxSize,
        }

54-56: Add observability features

Consider adding metrics and health checks for better production monitoring:

  1. Prometheus metrics for request counts, latencies, etc.
  2. Health check endpoint for load balancers
  3. Readiness probe for startup coordination

Would you like me to provide an example implementation of these observability features?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 12f4ab5 and 7048fa9.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (8)
  • .gitignore (1 hunks)
  • Makefile (2 hunks)
  • README.md (1 hunks)
  • cmd/evm-middleware/main.go (1 hunks)
  • execution.go (1 hunks)
  • execution_test.go (0 hunks)
  • go.mod (4 hunks)
  • integration_test.go (0 hunks)
💤 Files with no reviewable changes (2)
  • execution_test.go
  • integration_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • .gitignore
  • Makefile
  • README.md
  • go.mod
🧰 Additional context used
🪛 GitHub Check: lint / golangci-lint
cmd/evm-middleware/main.go

[failure] 5-5:
File is not goimports-ed with -local github.com/rollkit (goimports)

🪛 golangci-lint (1.62.2)
cmd/evm-middleware/main.go

5-5: File is not goimports-ed with -local github.com/rollkit

(goimports)


[medium] 32-32: G102: Binds to all network interfaces

(gosec)

🔇 Additional comments (1)
execution.go (1)

Line range hint 1-94: Verify impact of proxy client removal on system architecture

The removal of the proxy client functionality represents a significant architectural change. While this simplifies the codebase, we should ensure that all proxy-related responsibilities have been properly addressed or are no longer needed.

Let's verify there are no remaining proxy dependencies or references:

Comment on lines +50 to +52
_ = evmClient.Start()
defer evmClient.Stop()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential race condition in client lifecycle

There's a potential race condition between client start and stop. The client start error is ignored, and the stop is deferred regardless of start success.

-       _ = evmClient.Start()
-       defer evmClient.Stop()
+       if err := evmClient.Start(); err != nil {
+               log.Fatalf("failed to start EVM client: %v", err)
+       }
+       defer func() {
+               if err := evmClient.Stop(); err != nil {
+                       log.Printf("error stopping EVM client: %v", err)
+               }
+       }()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_ = evmClient.Start()
defer evmClient.Stop()
if err := evmClient.Start(); err != nil {
log.Fatalf("failed to start EVM client: %v", err)
}
defer func() {
if err := evmClient.Stop(); err != nil {
log.Printf("error stopping EVM client: %v", err)
}
}()

execution.go Outdated
Comment on lines 93 to 97
func (c *EngineAPIExecutionClient) Start() error {
return nil
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

Critical: Connection validation is essential for reliable operation

The codebase analysis confirms that both engineClient and ethClient are extensively used throughout the execution client with critical operations including:

  • Engine API calls (forkchoice updates, payload management)
  • Block operations
  • Transaction pool management
  • RLP encoding/decoding
  • Finalization

All these operations assume valid client connections and have proper error handling. However, the Start() method currently doesn't validate these connections, which could lead to delayed failures. The suggested connection validation is crucial for early detection of configuration issues.

  • Add the suggested connection validation to Start() method to ensure both clients are properly initialized before any operations
  • Include logging for successful initialization to aid in operational monitoring
🔗 Analysis chain

Critical: Implement proper client initialization and validation

The current implementation of Start() lacks proper initialization checks and connection validation, which could lead to runtime failures. While the removal of proxy functionality simplifies the code, we still need to ensure system reliability.

  1. Add connection validation as suggested in the previous review:
func (c *EngineAPIExecutionClient) Start() error {
+       ctx := context.Background()
+       
+       // Validate engine client connection
+       var version string
+       if err := c.engineClient.CallContext(ctx, &version, "web3_clientVersion"); err != nil {
+               return fmt.Errorf("failed to validate engine client connection: %w", err)
+       }
+       
+       // Validate eth client connection
+       if _, err := c.ethClient.BlockNumber(ctx); err != nil {
+               return fmt.Errorf("failed to validate eth client connection: %w", err)
+       }
+       
        return nil
}
  1. Consider adding logging for successful initialization:
func (c *EngineAPIExecutionClient) Start() error {
        // ... validation code ...
+       log.Info("Execution client successfully initialized",
+               "engineURL", c.engineClient.URL(),
+               "ethURL", c.ethClient.URL())
        return nil
}

Let's verify the impact of these connections in the codebase:


The output confirms extensive usage of both engineClient and ethClient throughout the codebase, with critical operations including:

  1. Block operations via ethClient.BlockByNumber
  2. Multiple engine API calls via engineClient.CallContext
  3. Transaction pool operations
  4. Forkchoice updates and payload management

Let me verify one more aspect to ensure we have a complete picture:


Let me try a different pattern to check error handling:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential connection usage patterns
echo "Checking connection usage patterns..."
rg -A 3 "engineClient\.CallContext|ethClient\." execution.go

Length of output: 1780


Script:

#!/bin/bash
# Check for error handling patterns around these clients
echo "Checking error handling patterns..."
rg "if err .+ (engineClient|ethClient).+" -B 2 execution.go

Length of output: 142


Script:

#!/bin/bash
# Check for error handling around client operations
echo "Checking error handling in client operations..."
rg "if err .+ nil" -B 2 -A 1 execution.go

Length of output: 2695

Introduce a new build target to compile the evm-middleware binary and update the clean target to remove the build directory. Also, document the build process in the README and update the .gitignore to exclude the build directory.
@tzdybal tzdybal dismissed MSevey’s stale review November 26, 2024 15:25

@MSevey is on vacation, so will not re-review.

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

🧹 Outside diff range and nitpick comments (1)
execution.go (1)

Line range hint 93-95: Critical: Add connection validation in Start() method

The Start() method currently lacks proper connection validation for both engineClient and ethClient. This could lead to delayed failures in critical operations like:

  • Engine API calls (forkchoice updates, payload management)
  • Block operations
  • Transaction pool management

Implement connection validation as follows:

func (c *EngineAPIExecutionClient) Start() error {
+       ctx := context.Background()
+       
+       // Validate engine client connection
+       var version string
+       if err := c.engineClient.CallContext(ctx, &version, "web3_clientVersion"); err != nil {
+               return fmt.Errorf("failed to validate engine client connection: %w", err)
+       }
+       
+       // Validate eth client connection
+       if _, err := c.ethClient.BlockNumber(ctx); err != nil {
+               return fmt.Errorf("failed to validate eth client connection: %w", err)
+       }
+       
+       log.Info("Execution client successfully initialized",
+               "engineURL", c.engineClient.URL(),
+               "ethURL", c.ethClient.URL())
        return nil
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7048fa9 and 3f8fa79.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (8)
  • .gitignore (1 hunks)
  • Makefile (2 hunks)
  • README.md (1 hunks)
  • cmd/evm-middleware/main.go (1 hunks)
  • execution.go (1 hunks)
  • execution_test.go (0 hunks)
  • go.mod (4 hunks)
  • integration_test.go (0 hunks)
💤 Files with no reviewable changes (2)
  • execution_test.go
  • integration_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • .gitignore
  • Makefile
  • README.md
  • cmd/evm-middleware/main.go
  • go.mod
🔇 Additional comments (3)
execution.go (3)

21-21: LGTM: Import changes are appropriate

The addition of the rollkit execution package import is correctly implemented.


Line range hint 142-308: LGTM: Methods implementation is robust and well-structured

The implementation of ExecuteTxs, InitChain, and SetFinal methods demonstrates:

  • Proper error handling
  • Correct usage of engine API V3
  • Appropriate transaction encoding
  • Proper state management

Line range hint 308-311: Verify potential integer overflow in derivePrevRandao

The conversion of block height to int64 could potentially overflow for very large block numbers.

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

Successfully merging this pull request may close these issues.

Create binary
2 participants