Skip to content

Conversation

@randygrok
Copy link
Contributor

Overview

@github-actions
Copy link
Contributor

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedJan 31, 2026, 9:36 AM

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @randygrok, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the gRPC client's interoperability by enabling it to connect to a wider range of gRPC servers. By introducing a custom HTTP/2 client that supports cleartext connections and explicitly configuring the native gRPC protocol, the client can now seamlessly interact with standard gRPC implementations, including those without TLS.

Highlights

  • HTTP/2 Client Integration: A new newHTTP2Client function has been introduced to create a custom http.Client specifically configured for HTTP/2 connections.
  • Cleartext HTTP/2 (h2c) Support: The custom HTTP/2 client is configured to allow cleartext HTTP/2 (h2c) connections, enabling communication with native gRPC servers that do not use TLS.
  • Native gRPC Protocol Enforcement: The NewClient function now explicitly prepends the connect.WithGRPC() option, ensuring the client uses the native gRPC protocol rather than the Connect protocol.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@claude
Copy link
Contributor

claude bot commented Jan 31, 2026

Claude finished @randygrok's task in 2m 41s —— View job


Code Review Complete

I've performed a comprehensive review of the HTTP/2 gRPC client changes. Here are my findings:


Summary

This PR adds HTTP/2 cleartext (h2c) support to the gRPC client, enabling it to connect to native gRPC servers without TLS. The changes are minimal and focused, adding a custom HTTP/2 client and enforcing the native gRPC protocol.

Overall Assessment: The implementation is functionally sound and achieves its stated goal. However, there are several areas that need attention regarding resource management, security configuration, testing, and documentation.


Critical Issues

1. Resource Leak: Missing Timeout Configuration 🔴

File: execution/grpc/client.go:31-41

The newHTTP2Client() function creates an http.Client without configuring timeouts, which can lead to resource leaks and hanging connections.

Issue:

return &http.Client{
    Transport: &http2.Transport{
        AllowHTTP: true,
        DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
            var d net.Dialer
            return d.DialContext(ctx, network, addr)
        },
    },
}

The net.Dialer is created without timeout configuration, and the http.Client lacks Timeout settings.

Impact:

  • Connections may hang indefinitely if the remote server is slow or unresponsive
  • Resource exhaustion in production under adverse network conditions
  • No protection against slow-read attacks

Recommendation:

func newHTTP2Client() *http.Client {
    return &http.Client{
        Timeout: 30 * time.Second, // Overall request timeout
        Transport: &http2.Transport{
            AllowHTTP: true,
            DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
                d := net.Dialer{
                    Timeout:   10 * time.Second, // Connection timeout
                    KeepAlive: 30 * time.Second, // TCP keepalive
                }
                return d.DialContext(ctx, network, addr)
            },
            // Consider adding these for production resilience:
            ReadIdleTimeout:  30 * time.Second,
            PingTimeout:      15 * time.Second,
            MaxReadFrameSize: 1 << 20, // 1MB
        },
    }
}

Fix timeout configuration →


2. HTTP/2 Transport Configuration Mismatch 🟡

File: execution/grpc/client.go:33 vs execution/grpc/handler.go:46-52

The client's HTTP/2 transport configuration is minimal compared to the server's well-configured settings.

Server configuration (handler.go):

return h2c.NewHandler(mux, &http2.Server{
    IdleTimeout:          120 * time.Second,
    MaxReadFrameSize:     1 << 24, // 16MB
    MaxConcurrentStreams: 100,
    ReadIdleTimeout:      30 * time.Second,
    PingTimeout:          15 * time.Second,
})

Client configuration (client.go):

Transport: &http2.Transport{
    AllowHTTP: true,
    DialTLSContext: func(...) {...},
    // Missing: frame size limits, stream limits, idle timeouts, ping config
}

Recommendation: Align client transport settings with server expectations for better reliability and performance. See the timeout fix above.


3. Security: TLS Config Completely Bypassed 🟡

File: execution/grpc/client.go:35-38

The DialTLSContext function ignores the TLS config parameter entirely:

DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
    var d net.Dialer
    return d.DialContext(ctx, network, addr)
},

Issue:

  • The underscore parameter (_ *tls.Config) means TLS is never used, even if configured
  • No path to enable TLS for production deployments
  • Security downgrade with no opt-in for encryption

Impact:

  • All communication is cleartext (no encryption)
  • Credentials, state roots, and transaction data transmitted in the clear
  • MITM attacks possible

Recommendation:
Either:

  1. Make h2c vs TLS configurable based on URL scheme or option
  2. Document clearly that this client only supports cleartext connections
  3. Add a secure alternative for production use

Example of scheme-based approach:

func NewClient(url string, opts ...connect.ClientOption) *Client {
    httpClient := newHTTP2Client(url) // Detects http:// vs https://
    opts = append([]connect.ClientOption{connect.WithGRPC()}, opts...)
    return &Client{
        client: v1connect.NewExecutorServiceClient(httpClient, url, opts...),
    }
}

func newHTTP2Client(url string) *http.Client {
    // If https://, use proper TLS transport
    // If http://, use h2c transport
}

Add TLS support →


Architecture & Code Quality

4. Function Signature Breaking Change 🟡

File: execution/grpc/client.go:51-60

The NewClient function now modifies the opts parameter by prepending connect.WithGRPC():

func NewClient(url string, opts ...connect.ClientOption) *Client {
    // Prepend WithGRPC to use the native gRPC protocol (required for tonic/gRPC servers)
    opts = append([]connect.ClientOption{connect.WithGRPC()}, opts...)
    return &Client{
        client: v1connect.NewExecutorServiceClient(
            newHTTP2Client(),
            url,
            opts...,
        ),
    }
}

Issue:

  • The function now forces the gRPC protocol, removing flexibility
  • Existing callers using Connect protocol will break
  • No backward compatibility or opt-out mechanism

Impact:

  • Breaking change for any existing code using Connect protocol
  • Reduces modularity (one of ev-node's core design principles per CLAUDE.md)

Recommendation:

  1. Add a build tag or configuration to choose protocol
  2. Create a separate constructor (NewGRPCClient vs NewConnectClient)
  3. Document the breaking change in commit message and PR description

5. Missing Documentation 📄

File: execution/grpc/client.go:29-41

The newHTTP2Client function lacks comprehensive documentation about:

  • Why cleartext h2c is required
  • Security implications of disabling TLS
  • When this should vs. shouldn't be used
  • Compatibility with different gRPC implementations

Current documentation:

// newHTTP2Client creates an HTTP/2 client that supports cleartext (h2c) connections.
// This is required to connect to native gRPC servers without TLS.

Recommended documentation:

// newHTTP2Client creates an HTTP/2 client that supports cleartext (h2c) connections.
// This is required to connect to native gRPC servers (e.g., tonic, grpc-go) without TLS.
//
// Security Warning: This client sends all data in cleartext without encryption.
// Only use this for:
//   - Local development and testing
//   - Internal networks with network-level security
//   - Communication within a trusted environment
//
// For production deployments over untrusted networks, use TLS-enabled connections.
//
// The client uses HTTP/2 protocol negotiation via h2c (HTTP/2 Cleartext) as defined
// in RFC 7540. This allows direct HTTP/2 communication without the TLS ALPN handshake.

Testing

6. No Test Coverage for New Code 🔴

Files: execution/grpc/client_test.go

The existing tests use httptest.NewServer() which already supports HTTP/2, so they don't exercise the new newHTTP2Client() code path.

Missing test coverage:

  • ✗ Connection to actual h2c server (not httptest)
  • ✗ Timeout behavior validation
  • ✗ Connection failure handling
  • ✗ Compatibility with different gRPC implementations
  • ✗ Resource cleanup on connection errors

Recommendation:
Add integration tests that:

  1. Start a real h2c gRPC server (using the existing handler)
  2. Verify the client can connect and communicate
  3. Test error scenarios (connection refused, timeout, etc.)
  4. Verify proper resource cleanup

Example test structure:

func TestClient_HTTP2ClearText(t *testing.T) {
    // Start real h2c server (not httptest)
    handler := NewExecutorServiceHandler(&mockExecutor{})
    listener, err := net.Listen("tcp", "127.0.0.1:0")
    require.NoError(t, err)
    defer listener.Close()
    
    server := &http.Server{Handler: handler}
    go server.Serve(listener)
    defer server.Close()
    
    // Test client connection
    client := NewClient("http://" + listener.Addr().String())
    // ... test operations
}

7. Behavioral Change Not Reflected in Tests 🟡

The change from http.DefaultClient to newHTTP2Client() is a significant behavioral change, but existing tests pass without modification because they use httptest which abstracts transport details.

Recommendation: Add tests that verify:

  • The client uses HTTP/2 protocol (not HTTP/1.1)
  • The client supports h2c
  • The native gRPC protocol is used (not Connect)

Performance & Resource Efficiency

8. New Client Created Per Instance 🟡

File: execution/grpc/client.go:56

Each call to NewClient() creates a new http.Client instance:

client: v1connect.NewExecutorServiceClient(
    newHTTP2Client(),  // New client every time
    url,
    opts...,
),

Issue:

  • Each http.Client maintains its own connection pool
  • Multiple Client instances to the same server won't share connections
  • Increased memory usage and connection overhead

Current behavior: Each executor client has isolated connections.

Consideration: This may be intentional for isolation, but consider:

  1. Documenting the connection pool behavior
  2. Providing an option to share an http.Client for connection pooling
  3. Adding a Close() method for graceful cleanup (if not using context cancellation)

Additional Observations

9. Positive: Minimal and Focused Change

The PR makes minimal changes to achieve its goal:

  • 20 additions, 1 deletion
  • Single file modified
  • Clear purpose (h2c support)
  • Follows existing code patterns (e.g., handler.go already uses h2c)

10. Positive: Good Code Comments

The inline comment explaining why WithGRPC() is prepended is helpful:

// Prepend WithGRPC to use the native gRPC protocol (required for tonic/gRPC servers)

11. Empty PR Description 📄

The PR description only contains "## Overview" with no content. Consider adding:

  • Motivation: Why h2c support is needed
  • Testing: How this was tested
  • Breaking changes: Protocol enforcement
  • Security: Cleartext implications

Recommendations Summary

Must Fix (Critical)

  1. Add timeout configuration to prevent resource leaks (Issue ABCI interface #1)
  2. Add test coverage for the new h2c client code (Issue Fraud proof generation #6)

Should Fix (Important)

  1. Make TLS configurable or clearly document cleartext-only limitation (Issue Transaction gossiping #3)
  2. Align client/server HTTP/2 settings for reliability (Issue Peer discovery #2)
  3. Document breaking changes and security implications (Issues Fraud proofs gossiping #4, Mempool #5)

Consider

  1. Review connection pooling strategy (Issue Data Availability layer API #8)
  2. Add integration tests with real gRPC servers (Issue Fraud proof generation #6)
  3. Fill in PR description with context

Conclusion

The implementation achieves its goal of h2c support but needs refinement before production use. The most critical issues are missing timeouts (resource leak risk) and lack of test coverage for the new code path. The security implications of cleartext-only communication should be clearly documented or addressed with TLS support.

After addressing the critical issues, this will be a solid addition to ev-node's modular architecture.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for connecting to gRPC servers over HTTP/2 cleartext (h2c). This is achieved by creating a custom http.Client with a specialized transport. The changes are functionally correct, but I've identified a potential issue with the lack of timeouts in the new HTTP client, which could lead to hanging connections. My review includes a suggestion to add appropriate timeouts to improve the client's robustness.

Comment on lines +32 to +40
return &http.Client{
Transport: &http2.Transport{
AllowHTTP: true,
DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, network, addr)
},
},
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The custom http.Client is created without any timeouts. This can lead to the client hanging indefinitely if the server is unresponsive, potentially causing resource exhaustion.

It's a good practice to configure timeouts for production-ready clients. Since all the RPCs in this service are unary, you can safely set a timeout on the http.Client for the entire request-response cycle. Additionally, setting a timeout on the net.Dialer is recommended to avoid long waits during connection establishment.

return &http.Client{
		Timeout: 60 * time.Second,
		Transport: &http2.Transport{
			AllowHTTP: true,
			DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
				d := net.Dialer{Timeout: 15 * time.Second}
				return d.DialContext(ctx, network, addr)
			},
		},
	}

@tac0turtle tac0turtle merged commit 5506eb0 into main Jan 31, 2026
26 of 27 checks passed
@tac0turtle tac0turtle deleted the randygrok/add-http2-grpc-client branch January 31, 2026 09:38
@codecov
Copy link

codecov bot commented Jan 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 55.37%. Comparing base (455b6c1) to head (43c1fd7).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3039      +/-   ##
==========================================
+ Coverage   55.33%   55.37%   +0.03%     
==========================================
  Files         117      117              
  Lines       11685    11694       +9     
==========================================
+ Hits         6466     6475       +9     
  Misses       4494     4494              
  Partials      725      725              
Flag Coverage Δ
combined 55.37% <100.00%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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