Skip to content

feat: reduce rpc complexity #69

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 9 commits into from
May 6, 2025
Merged

feat: reduce rpc complexity #69

merged 9 commits into from
May 6, 2025

Conversation

randygrok
Copy link
Contributor

@randygrok randygrok commented May 2, 2025

Overview

Summary by CodeRabbit

  • New Features

    • Added RPC endpoints for ABCI queries, node health, consensus parameters, validator sets, network info, and block search.
    • Introduced event subscription stubs and enhanced transaction broadcasting with improved context handling.
    • Implemented status and mempool query endpoints with pagination support.
  • Refactor

    • Migrated RPC handlers from provider methods to standalone functions using a centralized environment.
    • Consolidated RPC routing into a single map for streamlined endpoint registration.
    • Enhanced error handling, input validation, and consistent context usage across RPC methods.
  • Chores

    • Removed legacy JSON-RPC and WebSocket handler implementations and related types.
    • Simplified server startup by internalizing HTTP handler setup and updating logging integration.
    • Cleaned up dependencies by removing unused modules and imports.

Copy link
Contributor

coderabbitai bot commented May 2, 2025

Warning

Rate limit exceeded

@randygrok has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 34 seconds before requesting another review.

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 882883e and 17ebd15.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • go.mod (1 hunks)
  • pkg/rpc/core/blocks.go (9 hunks)
  • pkg/rpc/core/mempool.go (7 hunks)
  • pkg/rpc/server.go (3 hunks)

Walkthrough

This change removes the previous JSON-RPC provider, handler, and service architecture, replacing it with a modular, function-based RPC core. All RPC endpoints are now implemented as standalone functions in the core package, using a global environment for dependencies. The routing table is centralized, and new files organize functionality by domain (blocks, mempool, consensus, etc.). The old provider struct and JSON handler/service files are deleted.

Changes

Files / Groups Change Summary
pkg/rpc/json/handler.go, pkg/rpc/json/service.go, pkg/rpc/json/types.go, pkg/rpc/json/ws.go Deleted all JSON-RPC handler, service, types, and WebSocket logic; removes dynamic reflection-based dispatch and argument parsing.
pkg/rpc/provider/abci.go, pkg/rpc/provider/info.go, pkg/rpc/provider/provider.go Deleted the entire provider struct, its methods, and all ABCI/info-related RPC implementations.
pkg/rpc/core/abci.go, pkg/rpc/core/blocks.go, pkg/rpc/core/consensus.go, pkg/rpc/core/const.go, pkg/rpc/core/env.go, pkg/rpc/core/errors.go, pkg/rpc/core/events.go, pkg/rpc/core/evidence.go, pkg/rpc/core/health.go, pkg/rpc/core/mempool.go, pkg/rpc/core/net.go, pkg/rpc/core/routes.go, pkg/rpc/core/status.go, pkg/rpc/core/tx.go, pkg/rpc/core/utils.go Added new modular files implementing each RPC endpoint as a standalone function; introduced a global environment struct for dependencies; added routing, error, and helper logic.
pkg/rpc/server.go Refactored server to internally construct the HTTP handler from the new core routes, removing dependency on external handler.
server/start.go Simplified node startup, removed explicit RPC provider and handler setup, now uses global environment; updated function signatures and control flow.
go.mod Removed direct dependency on github.com/gorilla/rpc; made github.com/gorilla/websocket indirect.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant RPCServer
    participant CoreFunc
    participant EnvAdapter

    Client->>RPCServer: HTTP JSON-RPC request (e.g., "block", "tx", "status")
    RPCServer->>CoreFunc: Call corresponding standalone function (e.g., Block, Tx)
    CoreFunc->>EnvAdapter: Use global env for state, indexer, adapter
    EnvAdapter-->>CoreFunc: Return data/result
    CoreFunc-->>RPCServer: Return result or error
    RPCServer-->>Client: JSON-RPC response
Loading

Possibly related PRs

  • feat: clean rpc #62: Removes the old RPCServer struct and its methods, closely related as both PRs replace the monolithic RPC server with a modular, function-based approach.

Suggested labels

released

Suggested reviewers

  • tac0turtle

Poem

A hop and a skip, the code takes new shape,
No more providers or tangled escape!
Each RPC call now stands on its own,
In modular burrows, their logic is grown.
The routes are all mapped, the server’s anew—
A rabbit’s delight: clean, simple, and true!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🔭 Outside diff range comments (3)
server/start.go (3)

119-123: 🛠️ Refactor suggestion

Discarding the returned node.Node prevents graceful shutdown & observability
startNode now returns a node.Node instance as its first result, but it is immediately discarded (_).
Keeping a reference lets the process:

  • expose node-level metrics/API if needed
  • participate in a coordinated shutdown (node.Stop() or similar) rather than relying only on context cancellation.
-		_, cleanupFn, err := startNode(ctx, svrCtx, cmtCfg, app)
+		rkNode, cleanupFn, err := startNode(ctx, svrCtx, cmtCfg, app)
 		if err != nil {
 			return err
 		}
 		defer cleanupFn()
+
+		// ensure the node is stopped when the server terminates
+		defer func() {
+			if rkNode != nil {
+				_ = rkNode.Stop() // TODO: check returned error
+			}
+		}()

354-356: 🛠️ Refactor suggestion

Local variable shadows imported package name mempool
Redeclaring mempool := mempool.NewCListMempool(...) hides the imported package for the rest of the block and can confuse readers or tools.

-	mempool := mempool.NewCListMempool(cfg.Mempool, proxyApp.Mempool(), int64(height))
-	executor.SetMempool(mempool)
+	tmMempool := mempool.NewCListMempool(cfg.Mempool, proxyApp.Mempool(), int64(height))
+	executor.SetMempool(tmMempool)

358-365: ⚠️ Potential issue

cleanupFn does not stop RPC server or Rollkit node
cleanupFn only cancels the context. The RPC server and the running node may continue serving until their internal contexts notice the cancellation, which is not guaranteed to be immediate.
Enhance cleanupFn to call rpcServer.Stop() and rollkitNode.Stop() to avoid port leakage in tests and to shut down quickly.

🧹 Nitpick comments (5)
pkg/rpc/server.go (1)

33-34: Update outdated comment about RPC provider

The comment states that RPCServer "delegates the actual RPC method implementations to an rpcProvider", but this is no longer accurate after removing the legacy JSON-RPC provider. The RPC server now uses the global environment and routes map.

- // RPCServer manages the HTTP server for RPC requests.
- // It delegates the actual RPC method implementations to an rpcProvider.
+ // RPCServer manages the HTTP server for RPC requests.
+ // It serves registered RPC functions from the Routes map using the global environment.
pkg/rpc/core/abci.go (1)

31-40: Remove commented-out code

The commented-out ABCIInfo function adds no value since it's not used. It's better to add it back when needed rather than keeping dead code.

-// ABCIInfo gets some info about the application.
-// More: https://docs.cometbft.com/v0.37/rpc/#/ABCI/abci_info
-// func ABCIInfo(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) {
-// 	resInfo, err := env.ProxyAppQuery.InfoSync(proxy.RequestInfo)
-// 	if err != nil {
-// 		return nil, err
-// 	}
-//
-// 	return &ctypes.ResultABCIInfo{Response: *resInfo}, nil
-// }
pkg/rpc/routes.go (1)

8-48: Clean up commented-out routes

The file contains many commented-out routes which clutter the code. Since the PR goal is to reduce complexity, consider removing them.

Options:

  1. Remove all commented code
  2. Move them to a separate reference file
  3. Add a clear comment at the top explaining why they're commented out

If these routes are intended to be re-added later, consider documenting them in a separate markdown file instead of keeping them as commented code.

server/start.go (2)

131-132: Remove or replace commented-out dead code
The commented statement //clientCtx = clientCtx.WithClient(rpcServer) is a left-over from the previous RPC stack and is now misleading.
Delete it or update the comment to explain the new approach so future readers are not puzzled.


256-261: Typo in named return value reduces readability
rolllkitNode (three “l”s) is easy to mis-read and grepping for it later is error-prone.

-) (rolllkitNode node.Node, cleanupFn func(), err error) {
+) (rollkitNode node.Node, cleanupFn func(), err error) {

Remember to update all references inside the function.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d62bf75 and de6bee0.

📒 Files selected for processing (19)
  • pkg/rpc/client.go (0 hunks)
  • pkg/rpc/core/abci.go (1 hunks)
  • pkg/rpc/core/env.go (1 hunks)
  • pkg/rpc/json/handler.go (0 hunks)
  • pkg/rpc/json/service.go (0 hunks)
  • pkg/rpc/json/types.go (0 hunks)
  • pkg/rpc/json/ws.go (0 hunks)
  • pkg/rpc/provider/abci.go (0 hunks)
  • pkg/rpc/provider/blockchain.go (0 hunks)
  • pkg/rpc/provider/events.go (0 hunks)
  • pkg/rpc/provider/evidence.go (0 hunks)
  • pkg/rpc/provider/info.go (0 hunks)
  • pkg/rpc/provider/mempool.go (0 hunks)
  • pkg/rpc/provider/provider.go (0 hunks)
  • pkg/rpc/provider/tx.go (0 hunks)
  • pkg/rpc/provider/utils.go (0 hunks)
  • pkg/rpc/routes.go (1 hunks)
  • pkg/rpc/server.go (3 hunks)
  • server/start.go (9 hunks)
💤 Files with no reviewable changes (14)
  • pkg/rpc/provider/evidence.go
  • pkg/rpc/client.go
  • pkg/rpc/json/handler.go
  • pkg/rpc/provider/events.go
  • pkg/rpc/provider/info.go
  • pkg/rpc/json/ws.go
  • pkg/rpc/provider/abci.go
  • pkg/rpc/provider/tx.go
  • pkg/rpc/json/types.go
  • pkg/rpc/provider/provider.go
  • pkg/rpc/provider/utils.go
  • pkg/rpc/provider/mempool.go
  • pkg/rpc/provider/blockchain.go
  • pkg/rpc/json/service.go
🧰 Additional context used
🧬 Code Graph Analysis (3)
pkg/rpc/core/env.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (39-55)
pkg/rpc/core/abci.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (39-55)
pkg/rpc/routes.go (1)
pkg/rpc/core/abci.go (1)
  • ABCIQuery (12-29)
🔇 Additional comments (2)
pkg/rpc/server.go (1)

63-65: Well-structured RPC handler initialization

The refactoring to create the HTTP handler internally is a good simplification. This approach centralizes HTTP handler management within the RPC server rather than requiring it to be passed in from outside.

pkg/rpc/routes.go (1)

44-44:

❓ Verification inconclusive

Use of server.Cacheable for abci_query

The active abci_query route doesn't use the server.Cacheable() option unlike many of the commented-out routes. Consider whether caching would be beneficial for ABCI queries.

Would ABCI queries benefit from caching similar to other read operations? If so, update the route registration:

- "abci_query": server.NewRPCFunc(core.ABCIQuery, "path,data,height,prove"),
+ "abci_query": server.NewRPCFunc(core.ABCIQuery, "path,data,height,prove", server.Cacheable("height")),

Run this to check if other similar query endpoints use caching:


🏁 Script executed:

#!/bin/bash
# Look for any other similar query endpoints in the codebase 
# to see if they use caching for consistency

# Search for NewRPCFunc usage with query-related functions
rg -A 2 "NewRPCFunc\(.*query.*" --type go

Length of output: 484


Reevaluate caching for abci_query in pkg/rpc/routes.go

Unlike the commented-out validators route (which uses server.Cacheable("height")), abci_query accepts dynamic path and data parameters. Caching by height alone can return incorrect results when different queries share the same height.

• Location: pkg/rpc/routes.go:44
• Current registration:

"abci_query": server.NewRPCFunc(core.ABCIQuery, "path,data,height,prove"),

• If you decide caching is desirable, include all identifiers in the cache key:

- "abci_query": server.NewRPCFunc(core.ABCIQuery, "path,data,height,prove"),
+ "abci_query": server.NewRPCFunc(
+   core.ABCIQuery,
+   "path,data,height,prove",
+   server.Cacheable("path,data,height"),
+ ),

Otherwise, it’s safer to leave caching disabled to guarantee fresh responses per unique query.

Comment on lines +8 to +11
var (
// set by Node
env *Environment
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add synchronization for global environment access

The global env variable could cause race conditions or nil pointer dereferences if accessed before initialization or concurrently modified. While there's a comment warning about races, there are no actual synchronization mechanisms.

Consider using a sync.Once pattern to ensure initialization happens only once:

import (
	cmtlog "github.com/cometbft/cometbft/libs/log"
	"github.com/rollkit/go-execution-abci/pkg/adapter"
+	"sync"
)

var (
	// set by Node
	env *Environment
+	envOnce sync.Once
+	envMutex sync.RWMutex
)

Then update the setter function:

// SetEnvironment sets up the given Environment.
// It will race if multiple Node call SetEnvironment.
func SetEnvironment(e *Environment) {
-	env = e
+	envOnce.Do(func() {
+		envMutex.Lock()
+		defer envMutex.Unlock()
+		env = e
+	})
}

And add a safe getter that returns an error if env is nil:

// GetEnvironment safely retrieves the environment, returning an error if not set
func GetEnvironment() (*Environment, error) {
	envMutex.RLock()
	defer envMutex.RUnlock()
	
	if env == nil {
		return nil, errors.New("environment not initialized")
	}
	return env, nil
}

Comment on lines +13 to +17
// SetEnvironment sets up the given Environment.
// It will race if multiple Node call SetEnvironment.
func SetEnvironment(e *Environment) {
env = e
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add safeguards against multiple environment initializations

The comment warns about races with multiple calls to SetEnvironment, but there's no enforcement mechanism to prevent this. This could lead to unpredictable behavior.

Following the pattern suggested above with sync.Once would help, or alternatively:

// SetEnvironment sets up the given Environment.
// It will race if multiple Node call SetEnvironment.
func SetEnvironment(e *Environment) {
+	if env != nil {
+		panic("Environment already set - cannot be initialized twice")
+	}
	env = e
}
📝 Committable suggestion

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

Suggested change
// SetEnvironment sets up the given Environment.
// It will race if multiple Node call SetEnvironment.
func SetEnvironment(e *Environment) {
env = e
}
// SetEnvironment sets up the given Environment.
// It will race if multiple Node call SetEnvironment.
func SetEnvironment(e *Environment) {
if env != nil {
panic("Environment already set - cannot be initialized twice")
}
env = e
}

Comment on lines +19 to +25
resp, err := env.Adapter.App.Query(ctx.Context(), &abci.RequestQuery{
Data: data,
Path: path,
})
if err != nil {
return nil, err
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add nil check for environment before use

The function directly accesses the global env variable without checking if it's been initialized. This could lead to nil pointer panics if SetEnvironment hasn't been called.

func ABCIQuery(
	ctx *rpctypes.Context,
	path string,
	data bytes.HexBytes,
	height int64,
	prove bool,
) (*ctypes.ResultABCIQuery, error) {
+	if env == nil {
+		return nil, errors.New("rpc environment not initialized")
+	}
+	
	resp, err := env.Adapter.App.Query(ctx.Context(), &abci.RequestQuery{
		Data: data,
		Path: path,
	})
	if err != nil {
		return nil, err
	}
	return &ctypes.ResultABCIQuery{
		Response: *resp,
	}, nil
}
📝 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
resp, err := env.Adapter.App.Query(ctx.Context(), &abci.RequestQuery{
Data: data,
Path: path,
})
if err != nil {
return nil, err
}
func ABCIQuery(
ctx *rpctypes.Context,
path string,
data bytes.HexBytes,
height int64,
prove bool,
) (*ctypes.ResultABCIQuery, error) {
if env == nil {
return nil, errors.New("rpc environment not initialized")
}
resp, err := env.Adapter.App.Query(ctx.Context(), &abci.RequestQuery{
Data: data,
Path: path,
})
if err != nil {
return nil, err
}
return &ctypes.ResultABCIQuery{
Response: *resp,
}, nil
}

Comment on lines +12 to +28
func ABCIQuery(
ctx *rpctypes.Context,
path string,
data bytes.HexBytes,
height int64,
prove bool,
) (*ctypes.ResultABCIQuery, error) {
resp, err := env.Adapter.App.Query(ctx.Context(), &abci.RequestQuery{
Data: data,
Path: path,
})
if err != nil {
return nil, err
}
return &ctypes.ResultABCIQuery{
Response: *resp,
}, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Unused parameters and missing validation in ABCIQuery

The function declares height and prove parameters but doesn't use them in the actual query. There's also no input validation.

Two issues need to be addressed:

  1. Utilize the unused parameters or remove them to avoid confusion
  2. Add basic validation for inputs
func ABCIQuery(
	ctx *rpctypes.Context,
	path string,
	data bytes.HexBytes,
	height int64,
	prove bool,
) (*ctypes.ResultABCIQuery, error) {
+	// Validate inputs
+	if height < 0 {
+		return nil, fmt.Errorf("height cannot be negative: %d", height)
+	}
+
	resp, err := env.Adapter.App.Query(ctx.Context(), &abci.RequestQuery{
		Data: data,
		Path: path,
+		Height: height,
+		Prove: prove,
	})
	if err != nil {
		return nil, err
	}
	return &ctypes.ResultABCIQuery{
		Response: *resp,
	}, nil
}
📝 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
func ABCIQuery(
ctx *rpctypes.Context,
path string,
data bytes.HexBytes,
height int64,
prove bool,
) (*ctypes.ResultABCIQuery, error) {
resp, err := env.Adapter.App.Query(ctx.Context(), &abci.RequestQuery{
Data: data,
Path: path,
})
if err != nil {
return nil, err
}
return &ctypes.ResultABCIQuery{
Response: *resp,
}, nil
func ABCIQuery(
ctx *rpctypes.Context,
path string,
data bytes.HexBytes,
height int64,
prove bool,
) (*ctypes.ResultABCIQuery, error) {
// Validate inputs
if height < 0 {
return nil, fmt.Errorf("height cannot be negative: %d", height)
}
resp, err := env.Adapter.App.Query(ctx.Context(), &abci.RequestQuery{
Data: data,
Path: path,
Height: height,
Prove: prove,
})
if err != nil {
return nil, err
}
return &ctypes.ResultABCIQuery{
Response: *resp,
}, nil
}

server/start.go Outdated
Comment on lines 396 to 405
core.SetEnvironment(&core.Environment{
Logger: servercmtlog.CometLoggerWrapper{Logger: logger},
Adapter: executor,
})

// Pass the created handler to the RPC server constructor
rpcServer := rpc.NewRPCServer(rpcHandler, cfg.RPC, logger)
rpcServer := rpc.NewRPCServer(cfg.RPC, logger)
err = rpcServer.Start()
if err != nil {
return nil, nil, cleanupFn, fmt.Errorf("failed to start rpc server: %w", err)
return nil, cleanupFn, fmt.Errorf("failed to start rpc server: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Potential race: RPC server starts before executor is running
core.SetEnvironment(...) and rpcServer.Start() are invoked before executor.Start(ctx) at line 419.
If an abci_query comes in during that gap, the adapter hasn’t begun processing and may return inconsistent results or panic.

Consider either:

  1. Starting the executor first, or
  2. Having the RPC handler block until executor.Start signals readiness.
-	rpcServer := rpc.NewRPCServer(cfg.RPC, logger)
-	err = rpcServer.Start()
+	executorReady := make(chan struct{})
+	go func() {
+		<-executorReady
+		_ = rpcServer.Start()
+	}()
 ...
-	if err != nil { ... }
 ...
-	if err = executor.Start(ctx); err != nil {
+	close(executorReady)
+	if err = executor.Start(ctx); err != nil {

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

server/start.go Outdated
Comment on lines 402 to 407
rpcServer := rpc.NewRPCServer(cfg.RPC, logger)
err = rpcServer.Start()
if err != nil {
return nil, nil, cleanupFn, fmt.Errorf("failed to start rpc server: %w", err)
return nil, cleanupFn, fmt.Errorf("failed to start rpc server: %w", err)
}

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure RPC server is stopped when startNode returns with error
If rpcServer.Start() succeeds but a later step fails (e.g. node run, executor start), the function returns an error yet the RPC listener keeps the port bound.
Capture rpcServer in the earlier cleanupFn to guarantee it is closed in all exit paths.

 	cleanupFn = func() {
 		cancelFn()
+		if rpcServer != nil {
+			_ = rpcServer.Stop() // ignore secondary error
+		}
 	}

Comment on lines +48 to +52
func Genesis(ctx *rpctypes.Context) (*coretypes.ResultGenesis, error) {
// Returning unimplemented as per the original code.
// Consider implementing or returning a more specific error if needed.
panic("unimplemented")
}
Copy link

Choose a reason for hiding this comment

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

The Genesis function currently uses panic("unimplemented") which would crash the application if called. This violates the principle of graceful error handling. Consider returning a proper error value instead:

func Genesis(ctx *rpctypes.Context) (*coretypes.ResultGenesis, error) {
    return nil, errors.New("Genesis RPC method is not implemented")
}

This approach allows callers to handle the error appropriately without crashing the application.

Suggested change
func Genesis(ctx *rpctypes.Context) (*coretypes.ResultGenesis, error) {
// Returning unimplemented as per the original code.
// Consider implementing or returning a more specific error if needed.
panic("unimplemented")
}
func Genesis(ctx *rpctypes.Context) (*coretypes.ResultGenesis, error) {
return nil, errors.New("Genesis RPC method is not implemented")
}

Spotted by Diamond (based on custom rules)

Is this helpful? React 👍 or 👎 to let us know.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

🔭 Outside diff range comments (4)
pkg/rpc/core/events.go (1)

12-37: 💡 Verification agent

🧩 Analysis chain

Subscription entry‐point is a stub: callers will get a permanent 501-like error

Subscribe immediately returns event subscription functionality is not yet implemented, which means every consumer of the "subscribe" RPC route will always fail.
That is fine while the route is flagged experimental, but once the route is registered in Routes the node will expose an RPC that can never succeed – effectively breaking client libraries that expect CometBFT compatibility.

Recommended minimum guard-rail:

-func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
+// Subscribe is currently not implemented.  We deliberately *do not* register
+// it in Routes until the implementation is ready to avoid breaking clients.
+func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {

and make sure routes.go omits the registration (or feature-gate it behind a build tag / config flag).

If you do intend to ship the route, please implement at least the happy-path (parse query, call EventBus.Subscribe, return a valid ResultSubscribe) before merging.


🏁 Script executed:

#!/bin/bash
set -e
echo "1) Locate all routes.go files in pkg/rpc:"
find pkg/rpc -type f -name "routes.go"
echo
echo "2) Search for 'subscribe' registration in pkg/rpc/core/routes.go:"
rg -n -C2 "subscribe" pkg/rpc/core/routes.go || echo "No 'subscribe' entry found in routes.go"

Length of output: 688


Prevent exposing unimplemented Subscribe RPC route

The Subscribe handler in pkg/rpc/core/events.go always returns an error and is nevertheless registered in pkg/rpc/core/routes.go – any client calling the "subscribe" RPC will permanently fail. You must either gate this route or implement the happy-path before exposing it.

Locations to address:

  • pkg/rpc/core/events.go: stubbed Subscribe returns errors.New("…not yet implemented")
  • pkg/rpc/core/routes.go: line 9 registers the stub via server.NewWSRPCFunc(Subscribe, "query")

Suggested fix (remove registration until implementation is ready):

--- a/pkg/rpc/core/routes.go
@@ -6,7 +6,7 @@ var Routes = map[string]*server.RPCFunc{
-   "subscribe":       server.NewWSRPCFunc(Subscribe, "query"),
+   // "subscribe":       server.NewWSRPCFunc(Subscribe, "query")  // unimplemented
    "unsubscribe":     server.NewWSRPCFunc(Unsubscribe, "query"),
    "unsubscribe_all": server.NewWSRPCFunc(UnsubscribeAll, ""),

Alternatively, feature-gate the registration behind a config flag or build tag, or implement the subscription logic (parse query, call EventBus.Subscribe, stream events).

pkg/rpc/core/tx.go (1)

134-151: 🛠️ Refactor suggestion

API result construction bypasses hash validation

You recompute the hash with types.Tx(result.Tx).Hash() for search results, but in the simple Tx path you trust hash from the caller. An invalid hash that collides with an existing tx length could leak inconsistent data.

Recommendation: in Tx, recompute the hash too and compare to the supplied hash. If they mismatch, return ErrTxHashMismatch.

pkg/rpc/core/mempool.go (1)

27-61: 🛠️ Refactor suggestion

BroadcastTxAsync blocks until CheckTx finishes – not truly async

The CometBFT spec states that broadcast_tx_async should return immediately after the tx is enqueued, only providing the hash. Waiting for the CheckTx callback turns it into “sync” behaviour and adds head-of-line blocking under load.

Minimum change:

-    select {
-    case res = <-responseCh:
-
-    case <-unwrappedCtx.Done():
-
-    }
-
-    return &coretypes.ResultBroadcastTx{ … }, nil
+    return &coretypes.ResultBroadcastTx{
+        Code:      0,     // unknown yet
+        Hash:      tx.Hash(),
+    }, nil

Alternatively keep the callback for logging but do not block the RPC response.

pkg/rpc/core/blocks.go (1)

116-125: 🛠️ Refactor suggestion

Guard against nil header before hashing

header, data, err := env.Adapter.Store.GetBlockData(...) could legally return a nil header (e.g. pruned block).
header.Hash() on a nil pointer causes a panic.

+ if header == nil {
+     return nil, fmt.Errorf("block at height %d not found", heightValue)
+ }

Replicate the same guard in BlockByHash.

♻️ Duplicate comments (4)
pkg/rpc/core/abci.go (2)

12-18: height and prove parameters are silently ignored
The function signature advertises that callers can specify a block height and whether a proof is required, but these values are never forwarded to abci.RequestQuery. This is confusing for callers and may hide bugs.


18-25: Add nil-check for global env before use
Same concern as raised in earlier review: dereferencing env.Adapter without verifying initialisation will panic if SetEnvironment hasn’t been invoked.

pkg/rpc/core/events.go (1)

40-63: 🛠️ Refactor suggestion

Same “TODO” pattern and nil checks apply to Unsubscribe/UnsubscribeAll

Both functions replicate the concerns above: stubbed implementation, missing env nil-guards, and unused ctx parameter. Please address them together with Subscribe to keep API behaviour consistent.

pkg/rpc/core/env.go (1)

12-21: 🛠️ Refactor suggestion

⚠️ Potential issue

Global env is still an unsynchronised singleton – races & nil-dereference remain possible

The past review already pointed out that concurrent reads/writes to the package-level env may cause data races, and that multiple initialisations are not prevented. Nothing has changed in this revision – the variable is still a plain pointer and SetEnvironment merely assigns it.

-var (
-    // set by Node
-    env *Environment
-)
+var (
+    // set by Node — protected by once + RW-lock
+    env     *Environment
+    envOnce sync.Once
+    envMu   sync.RWMutex
+)
+
+// GetEnvironment safely returns the global env or an error if not initialised.
+func GetEnvironment() (*Environment, error) {
+    envMu.RLock()
+    defer envMu.RUnlock()
+    if env == nil {
+        return nil, errors.New("rpc core environment not initialised")
+    }
+    return env, nil
+}

…and in SetEnvironment:

-func SetEnvironment(e *Environment) {
-    env = e
-}
+func SetEnvironment(e *Environment) {
+    envOnce.Do(func() {
+        envMu.Lock()
+        defer envMu.Unlock()
+        env = e
+    })
+}

Besides eliminating the race, callers now get a safer accessor instead of dereferencing a global that might still be nil.

🧹 Nitpick comments (12)
pkg/rpc/core/status.go (1)

25-40: NodeInfo scaffold still contains TODOs – callers receive mostly-empty fields
All critical metadata (protocol versions, node ID, network, moniker, etc.) is commented out. Until these are populated, clients of the status RPC receive an almost-blank node_info, which can break dashboards and light clients. Consider wiring the necessary data in or, at minimum, documenting the limitation in RPC docs.

pkg/rpc/core/tx.go (2)

35-54: prove path always returns an error – consider short-circuiting earlier

Because proofs are not supported yet, the extra comments and half-implemented code inside the if prove { … } block add cognitive load without providing functionality.

Simpler alternative:

- var proof types.TxProof
- if prove {
-     return nil, errors.New("transaction proof generation is not supported")
- }
+ if prove {
+     return nil, errors.New("transaction proof generation is not supported")
+ }
+ var proof types.TxProof // zero value

This removes dead code while keeping the API.


70-123: Large in-memory sort on every TxSearch call can DOS the node

env.TxIndexer.Search may already return results sorted; sorting again (potentially tens of thousands of entries) is O(n log n) and allocates additional memory. On a public endpoint this becomes an attack vector.

Action items:

  1. Confirm TxIndexer.Search ordering contract; if deterministic, drop local sort.
  2. If local sort is required, cap totalCount to a sane upper bound (e.g., 10 000).
  3. Stream results instead of building a full slice when possible.
pkg/rpc/core/consensus.go (3)

33-37: Int vs int64 mismatch risks overflow on large heights

normalizeHeight returns int, later compared to int64:

if heightPtr != nil && int64(height) != s.LastBlockHeight { … }

On 32-bit builds or extremely high chains, the cast may overflow. Prefer using int64 throughout height-handling helpers to avoid truncation.


41-53: Pagination helpers silently mutate slice – copy when trimming

validators = validators[start:end] re-slices the backing array. If the original slice is reused elsewhere this may create subtle bugs. Consider copying:

paged := make([]*cmttypes.Validator, end-start)
copy(paged, validators[start:end])
validators = paged

81-83: ConsensusState/DumpConsensusState just return “not available” – expose in routes?

These stubs satisfy the interface but may surprise clients. If Rollkit will never support these endpoints consider omitting them from Routes to return “method not found” instead of a runtime error.

pkg/rpc/core/mempool.go (4)

70-80: Possible goroutine leak if env.Adapter.Mempool.CheckTx immediately errors

When CheckTx returns an error, resCh is never closed and the anonymous callback might still attempt to send, blocking forever.
Consider:

err := env.Adapter.Mempool.CheckTx(tx, func(res *abci.ResponseCheckTx) {
    select {
    case resCh <- res:
    default: // channel already closed on error path
    }
}, mempool.TxInfo{})

…and close(resCh) right after the error is detected.


90-107: Gossip failure returns error even though CheckTx succeeded – may surprise clients

Current behaviour:

  1. CheckTx ok
  2. Gossip fails ⇒ RPC returns error

Tendermint keeps tx in the mempool and still returns success (code 0) while logging the gossip failure. Consider aligning with that to preserve liveness under temporary P2P issues.


128-143: Hard-coded maxSubs / maxClients placeholders

Leaving TODO values in production code creates hidden limits. Either:

  • Read them from env.Adapter.CometCfg.RPC (there are config fields for both), or
  • Make them constants with explanatory names.

Otherwise clients may unexpectedly hit an arbitrary ceiling.


301-327: CheckTx waits indefinitely – add timeout guard

If the ABCI app never calls back (bug or panic) this RPC will block forever. Enforce an upper-bound using time.After, similar to the commit path.

select {
case res = <-responseCh:
    …
case <-time.After(env.Adapter.CometCfg.RPC.TimeoutBroadcastTxCommit):
    return nil, errors.New("check_tx timed out")
case <-unwrappedCtx.Done():
    …
}
pkg/rpc/core/blocks.go (2)

231-253: Update parameter type after fixing the duplicate import

After removing the un-aliased import, change the function signature to keep working:

-func HeaderByHash(ctx *rpctypes.Context, hash bytes.HexBytes) (*ctypes.ResultHeader, error) {
+func HeaderByHash(ctx *rpctypes.Context, hash cmtbytes.HexBytes) (*ctypes.ResultHeader, error) {

Failing to do so will re-introduce the shadowing problem.


259-299: Large in-memory slice can be capped for safety

blocks := make([]*cmttypes.BlockMeta, 0, maxHeight-minHeight+1) may allocate up to 20 items (due to the limit), which is fine, but if the limit constant is ever changed the allocation could explode.

Consider using min(maxHeight-minHeight+1, limit) in the capacity expression to future-proof memory usage.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between de6bee0 and 7061d21.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (17)
  • go.mod (1 hunks)
  • pkg/rpc/core/abci.go (1 hunks)
  • pkg/rpc/core/blocks.go (9 hunks)
  • pkg/rpc/core/consensus.go (1 hunks)
  • pkg/rpc/core/const.go (1 hunks)
  • pkg/rpc/core/env.go (1 hunks)
  • pkg/rpc/core/errors.go (1 hunks)
  • pkg/rpc/core/events.go (3 hunks)
  • pkg/rpc/core/evidence.go (1 hunks)
  • pkg/rpc/core/health.go (1 hunks)
  • pkg/rpc/core/mempool.go (10 hunks)
  • pkg/rpc/core/net.go (1 hunks)
  • pkg/rpc/core/routes.go (1 hunks)
  • pkg/rpc/core/status.go (1 hunks)
  • pkg/rpc/core/tx.go (5 hunks)
  • pkg/rpc/core/utils.go (2 hunks)
  • pkg/rpc/server.go (3 hunks)
✅ Files skipped from review due to trivial changes (4)
  • pkg/rpc/core/health.go
  • pkg/rpc/core/errors.go
  • pkg/rpc/core/const.go
  • pkg/rpc/core/routes.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/rpc/server.go
🧰 Additional context used
🧬 Code Graph Analysis (6)
pkg/rpc/core/abci.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (39-55)
pkg/rpc/core/utils.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (39-55)
pkg/rpc/core/events.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (39-55)
pkg/rpc/core/net.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (39-55)
pkg/rpc/core/env.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (39-55)
pkg/rpc/core/mempool.go (4)
pkg/rpc/core/tx.go (1)
  • Tx (20-64)
pkg/adapter/adapter.go (1)
  • Adapter (39-55)
pkg/p2p/errors.go (1)
  • ErrNotReady (5-5)
pkg/rpc/core/events.go (2)
  • Subscribe (12-38)
  • Unsubscribe (42-54)
🔇 Additional comments (4)
go.mod (1)

104-104:

✅ Verification successful

Ensure github.com/gorilla/websocket isn’t an orphaned dependency

Our new core RPC implementation uses nhooyr.io/websocket, so github.com/gorilla/websocket may no longer be needed. Please verify there are no remaining imports and clean up your module:

If it’s unused, remove the line from go.mod; otherwise, confirm its necessity.


🏁 Script executed:

#!/bin/bash
# Check for any direct imports of gorilla/websocket
rg -n "github.com/gorilla/websocket" --glob='*.go'

# Tidy up go.mod and go.sum
go mod tidy

Length of output: 18695


🏁 Script executed:

#!/bin/bash
# Search for any direct imports in all .go files
rg -n "github.com/gorilla/websocket"

# Identify which dependencies pull in gorilla/websocket transitively
go mod graph | grep "github.com/gorilla/websocket"

Length of output: 1555


No action needed: retain github.com/gorilla/websocket as an indirect dependency

  • rg confirms there are no direct imports of github.com/gorilla/websocket in any .go file.
  • go mod graph shows it’s pulled in transitively by core dependencies (e.g. cometbft/cometbft, cosmos-sdk, rollkit, go-jsonrpc, ipfs/boxo, libp2p/*, etc.).
  • Removing it manually would simply cause go mod tidy to re-add it for those modules.

Leave the existing line in go.mod; go mod tidy will keep it only as an indirect dependency.

pkg/rpc/core/env.go (1)

62-74: Verify visibility of defaultPerPage and maxPerPage constants

These identifiers are referenced but not declared in this file. Ensure they live in the same package and are exported or imported to avoid a compilation failure.

pkg/rpc/core/blocks.go (2)

31-33: Incorrect type assertion for the null indexer

*blockidxnull.BlockerIndexer does not exist; the null implementation is called Indexer (or Blocker, depending on the CometBFT version).
A wrong assertion will always panic even when indexing is enabled.

Please confirm the concrete type name and adjust:

- if _, ok := env.BlockIndexer.(*blockidxnull.BlockerIndexer); ok {
+ if _, ok := env.BlockIndexer.(*blockidxnull.Indexer); ok { // or the correct concrete type

71-88: 🛠️ Refactor suggestion

Missing nil-checks may lead to panics on corrupted stores

If env.Adapter.Store.GetBlockData returns nil, nil, nil for a height that the indexer knows about, the subsequent call to header.Hash() inside ToABCIBlock will dereference a nil pointer and crash the node.

Consider hard-stopping before conversion:

- if header == nil || data == nil {
-     return nil, fmt.Errorf("nil header or data for height %d from store", height)
- }
+ if header == nil || data == nil {
+     // Decide whether to skip silently or to fail fast.
+     return nil, fmt.Errorf("corrupted store: missing header or data for height %d", height)
+ }

Likely an incorrect or invalid review comment.

Comment on lines +13 to 18
func BroadcastEvidence(ctx *rpctypes.Context, ev cmttypes.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
// Log that evidence broadcasting is not supported or is a no-op?
p.logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")
return &coretypes.ResultBroadcastEvidence{
Hash: evidence.Hash(),
env.Logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")
return &ctypes.ResultBroadcastEvidence{
Hash: ev.Hash(),
}, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Guard against an un-initialised RPC environment
This function dereferences env.Logger without checking that env (and its Logger) has been set via SetEnvironment. A nil global will cause a panic at runtime.

+	if env == nil || env.Logger == nil {
+		return nil, errors.New("rpc environment not initialised")
+	}
+
 	env.Logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")

(Remember to import errors.)

📝 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
func BroadcastEvidence(ctx *rpctypes.Context, ev cmttypes.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
// Log that evidence broadcasting is not supported or is a no-op?
p.logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")
return &coretypes.ResultBroadcastEvidence{
Hash: evidence.Hash(),
env.Logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")
return &ctypes.ResultBroadcastEvidence{
Hash: ev.Hash(),
}, nil
func BroadcastEvidence(ctx *rpctypes.Context, ev cmttypes.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
if env == nil || env.Logger == nil {
return nil, errors.New("rpc environment not initialised")
}
// Log that evidence broadcasting is not supported or is a no-op?
env.Logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")
return &ctypes.ResultBroadcastEvidence{
Hash: ev.Hash(),
}, nil
}

⚠️ Potential issue

Unused ctx parameter will break compilation – either use it or mark it intentionally ignored
ctx is never referenced after being declared, so go vet / the compiler will error with “ctx declared but not used”. Rename the parameter to _ (or actually make use of it) to keep the public signature while satisfying the compiler.

-func BroadcastEvidence(ctx *rpctypes.Context, ev cmttypes.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
+func BroadcastEvidence(_ *rpctypes.Context, ev cmttypes.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
📝 Committable suggestion

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

Suggested change
func BroadcastEvidence(ctx *rpctypes.Context, ev cmttypes.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
// Log that evidence broadcasting is not supported or is a no-op?
p.logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")
return &coretypes.ResultBroadcastEvidence{
Hash: evidence.Hash(),
env.Logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")
return &ctypes.ResultBroadcastEvidence{
Hash: ev.Hash(),
}, nil
func BroadcastEvidence(_ *rpctypes.Context, ev cmttypes.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
// Log that evidence broadcasting is not supported or is a no-op?
env.Logger.Debug("BroadcastEvidence called, but evidence handling is not implemented in Rollkit RPC.")
return &ctypes.ResultBroadcastEvidence{
Hash: ev.Hash(),
}, nil
}

Comment on lines 15 to 23
info, err := env.Adapter.App.Info(&abci.RequestInfo{})
if err != nil {
return nil, err
}

s, err := env.Adapter.LoadState(ctx.Context())
if err != nil {
return nil, err
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing nil-check for global env can panic
env.Adapter.App.Info and env.Adapter.LoadState are both called unconditionally. If env is nil, or Adapter is nil, the node will crash.

+	if env == nil || env.Adapter == nil {
+		return nil, errors.New("rpc environment not initialised")
+	}

(Remember to add errors to the import list.)

Comment on lines +14 to 16
if env.Adapter.EventBus == nil {
return nil, errors.New("event bus is not configured, cannot subscribe to events")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Potential nil-pointer dereference on global env

env.Adapter is accessed without a nil check (env.Adapter.EventBus).
If SetEnvironment is accidentally skipped (unit-tests, CLI tools, etc.) the process will panic on every RPC call.

- if env.Adapter.EventBus == nil {
+ if env == nil || env.Adapter == nil || env.Adapter.EventBus == nil {
     return nil, errors.New("event bus is not configured, cannot subscribe to events")
 }
📝 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 env.Adapter.EventBus == nil {
return nil, errors.New("event bus is not configured, cannot subscribe to events")
}
if env == nil || env.Adapter == nil || env.Adapter.EventBus == nil {
return nil, errors.New("event bus is not configured, cannot subscribe to events")
}

Comment on lines +20 to 33
func Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
// if index is disabled, return error
if _, ok := env.TxIndexer.(*null.TxIndex); ok {
return nil, fmt.Errorf("transaction indexing is disabled")
}

txResult, err := p.txIndexer.Get(hash)
r, err := env.TxIndexer.Get(hash)
if err != nil {
// If the tx is not found, return nil result without error, maintaining behaviour.
// This differs from some Tendermint versions that might return an error.
// TODO: Consider aligning error handling with target Tendermint version if necessary.
// A more robust check might involve type asserting the error if txIndexer returns a specific "not found" error type.
if txResult == nil { // Heuristic check if error implies not found
return nil, nil
}
// Return other errors encountered during Get
return nil, fmt.Errorf("error getting tx from indexer: %w", err)
return nil, err
}

if txResult == nil {
// Tx not found
return nil, nil
if r == nil {
return nil, fmt.Errorf("tx (%X) not found", hash)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing nil checks & ambiguous error when TxIndexer is not wired

You guard the disabled case (null.TxIndex) but not the absence of an indexer:

if env.TxIndexer == nil {
    return nil, errors.New("tx indexer is not configured")
}

Without this, env.TxIndexer.Get will panic.
Also, a zero-length hash slice should be rejected early to avoid a linear scan returning “not found”.

Comment on lines +11 to +45
func NetInfo(ctx *rpctypes.Context) (*coretypes.ResultNetInfo, error) {
res := coretypes.ResultNetInfo{
Listening: true, // Assuming node is always listening if RPC is up
Listeners: []string{}, // TODO: Populate with actual listener addresses if available
}

// Access P2P client from adapter to get peer info
if env.Adapter.P2PClient != nil {
for _, ma := range env.Adapter.P2PClient.Addrs() {
res.Listeners = append(res.Listeners, ma.String())
}
peers := env.Adapter.P2PClient.Peers()
res.NPeers = len(peers)
for _, peer := range peers {
// Convert peer info to coretypes.Peer
// Ensure p2p.DefaultNodeInfo is correctly populated from peer.NodeInfo
res.Peers = append(res.Peers, coretypes.Peer{
NodeInfo: p2p.DefaultNodeInfo{ // Adapt this based on actual available PeerInfo structure
// Access fields via peer.NodeInfo
DefaultNodeID: p2p.ID(peer.NodeInfo.NodeID),
ListenAddr: peer.NodeInfo.ListenAddr,
Network: peer.NodeInfo.Network,
// Other fields like Moniker, Version might be available too
},
IsOutbound: peer.IsOutbound,
RemoteIP: peer.RemoteIP,
})
}
} else {
// Handle case where P2P client is not available or initialized
res.NPeers = 0
res.Peers = []coretypes.Peer{}
}

return &res, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

NetInfo exposes partially-filled peer objects and risks panics

Issues observed:

  1. peer.NodeInfo is dereferenced without checking peer.NodeInfo != nil.
  2. p2p.DefaultNodeInfo is not the concrete type returned by many libp2p peers and may not compile depending on imports.
  3. Listening is hard-coded true; if the node is in light-client mode it will be wrong.

Quick defensive patch:

- res := coretypes.ResultNetInfo{
-     Listening: true,
-     Listeners: []string{},
- }
+ res := coretypes.ResultNetInfo{
+     Listening: env.Adapter != nil && env.Adapter.P2PClient != nil,
+     Listeners: []string{},
+ }

and inside the loop:

if peer.NodeInfo != nil {
    res.Peers = append(res.Peers, coretypes.Peer{ /* … */ })
}

Ideally create a helper to convert your concrete peer type into the CometBFT RPC shape.

Comment on lines +41 to +60
func validatePage(pagePtr *int, perPage, totalCount int) (int, error) {
if perPage < 1 {
panic(fmt.Sprintf("zero or negative perPage: %d", perPage))
}

if pagePtr == nil { // no page parameter
return 1, nil
}

pages := ((totalCount - 1) / perPage) + 1
if pages == 0 {
pages = 1 // one page (even if it's empty)
}
page := *pagePtr
if page <= 0 || page > pages {
return 1, fmt.Errorf("page should be within [1, %d] range, given %d", pages, page)
}

return page, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

validatePage panics on invalid perPage; prefer graceful error

A public RPC helper should not bring the process down with panic. Returning an error provides the caller the chance to surface a proper JSON-RPC error to the user.

-    if perPage < 1 {
-        panic(fmt.Sprintf("zero or negative perPage: %d", perPage))
-    }
+    if perPage < 1 {
+        return 0, fmt.Errorf("per_page must be positive, got %d", perPage)
+    }

Adjust the callers to handle the extra error.

📝 Committable suggestion

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

Suggested change
func validatePage(pagePtr *int, perPage, totalCount int) (int, error) {
if perPage < 1 {
panic(fmt.Sprintf("zero or negative perPage: %d", perPage))
}
if pagePtr == nil { // no page parameter
return 1, nil
}
pages := ((totalCount - 1) / perPage) + 1
if pages == 0 {
pages = 1 // one page (even if it's empty)
}
page := *pagePtr
if page <= 0 || page > pages {
return 1, fmt.Errorf("page should be within [1, %d] range, given %d", pages, page)
}
return page, nil
}
func validatePage(pagePtr *int, perPage, totalCount int) (int, error) {
if perPage < 1 {
- panic(fmt.Sprintf("zero or negative perPage: %d", perPage))
+ return 0, fmt.Errorf("per_page must be positive, got %d", perPage)
}
if pagePtr == nil { // no page parameter
return 1, nil
}
pages := ((totalCount - 1) / perPage) + 1
if pages == 0 {
pages = 1 // one page (even if it's empty)
}
page := *pagePtr
if page <= 0 || page > pages {
return 1, fmt.Errorf("page should be within [1, %d] range, given %d", pages, page)
}
return page, nil
}

Comment on lines 18 to 37
func Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*coretypes.ResultValidators, error) {
// Determine the height to query validators for.
// If height is nil or latest, use the current block height.
// Otherwise, use the specified height (if state for that height is available).
// Note: Loading state for arbitrary past heights might not be supported
// depending on state pruning. The current implementation implicitly loads latest state.
height := normalizeHeight(heightPtr)

s, err := env.Adapter.LoadState(ctx.Context()) // Loads the *latest* state
if err != nil {
return nil, fmt.Errorf("failed to load current state: %w", err)
}

// Check if the requested height matches the loaded state height if a specific height was requested.
// If state history is not kept, this check might be necessary or always fail for past heights.
if heightPtr != nil && int64(height) != s.LastBlockHeight {
// This implies state for the requested height is not available with the current LoadState method.
// Adjust implementation if historical state access is possible and needed.
return nil, fmt.Errorf("validator set for height %d is not available, latest height is %d", height, s.LastBlockHeight)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Validators currently always loads latest state – historical queries will fail

env.Adapter.LoadState fetches only the current state, so any height that differs from s.LastBlockHeight returns an error. This diverges from CometBFT’s RPC semantics where historical validator sets are available as long as they are retained.

Two options:

  1. Document the limitation explicitly in the RPC docs and return a canonical “height not available” error code.
  2. If the store keeps historical states, expose LoadStateAt(height int64) in the adapter and use it here.

Failing silently may break existing clients expecting Tendermint-compat behaviour.

Comment on lines 268 to 289
// UnconfirmedTxs gets unconfirmed transactions (maximum ?limit entries)
// including their number.
// More: https://docs.cometbft.com/v0.37/rpc/#/Info/unconfirmed_txs
func UnconfirmedTxs(ctx *rpctypes.Context, limitPtr *int) (*ctypes.ResultUnconfirmedTxs, error) {
txs := env.Adapter.Mempool.ReapMaxTxs(-1) // Reap all transactions

var res *abci.ResponseCheckTx
select {
case res = <-resCh:
// Got response
case <-ctx.Done():
return nil, fmt.Errorf("context cancelled waiting for CheckTx: %w", ctx.Err())
}

// Gossip the transaction if it passed CheckTx.
if res.Code == abci.CodeTypeOK {
if p.adapter.TxGossiper == nil {
return nil, execp2p.ErrNotReady // Cannot gossip if gossiper is not ready
}

err = p.adapter.TxGossiper.Publish(ctx, tx)
if err != nil {
// If gossiping fails, remove the tx from the mempool to allow resubmission.
// This matches the behaviour described in the original comments.
rmErr := p.adapter.Mempool.RemoveTxByKey(tx.Key())
if rmErr != nil {
// Log if removal also failed, but return the gossip error primarily.
p.logger.Error("Failed to remove tx from mempool after gossip failure", "tx_key", tx.Key(), "removal_error", rmErr)
}
return nil, fmt.Errorf("failed to gossip tx: %w", err)
limit := len(txs)
if limitPtr != nil {
limit = cmtmath.MinInt(*limitPtr, limit)
if limit < 0 {
limit = 0
}
}
paginatedTxs := txs[:limit]

return &coretypes.ResultBroadcastTx{
Code: res.Code,
Data: res.Data,
Log: res.Log,
Codespace: res.Codespace,
Hash: tx.Hash(),
return &coretypes.ResultUnconfirmedTxs{
Count: len(paginatedTxs),
Total: env.Adapter.Mempool.Size(),
TotalBytes: env.Adapter.Mempool.SizeBytes(),
Txs: paginatedTxs,
}, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

UnconfirmedTxs slices without copying – risk of data race with mempool

ReapMaxTxs returns references into the mempool; concurrent mempool updates may mutate the slice while it’s being JSON-encoded. Make an explicit copy:

raw := env.Adapter.Mempool.ReapMaxTxs(-1)
copied := append([]types.Tx(nil), raw[:limit]...)

Comment on lines +35 to +38
// Use the locally defined maxQueryLength from provider_utils.go
if len(query) > maxQueryLength {
return nil, errors.New("maximum query length exceeded")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

maxQueryLength is referenced but never defined

maxQueryLength was previously located in provider_utils.go, which no longer exists.
Dereferencing an undefined identifier breaks compilation.

If you still want the guard, add a constant in a common file (e.g. env.go) and import it here:

// env.go
const MaxQueryLength = 512
- if len(query) > maxQueryLength {
+ if len(query) > MaxQueryLength {

Comment on lines 8 to 13
"github.com/cometbft/cometbft/libs/bytes"
cmtbytes "github.com/cometbft/cometbft/libs/bytes"
cmtmath "github.com/cometbft/cometbft/libs/math"
cmtquery "github.com/cometbft/cometbft/libs/pubsub/query"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Duplicate import of the same package causes a hard‐compile error

"github.com/cometbft/cometbft/libs/bytes" is imported twice – once unnamed (line 8) and once as the alias cmtbytes (line 9).
Go does not allow the same import path to appear twice, so the file will not compile.
In addition, the first import shadows the standard‐library bytes package everywhere in this file, which is highly confusing.

-    "github.com/cometbft/cometbft/libs/bytes"
-    cmtbytes "github.com/cometbft/cometbft/libs/bytes"
+    cmtbytes "github.com/cometbft/cometbft/libs/bytes"

You will also need to:

  • Update the HeaderByHash signature (line 231) from bytes.HexBytes to cmtbytes.HexBytes.
  • Replace the single remaining occurrence of the un­aliased bytes.HexBytes (line 231) accordingly.
📝 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
"github.com/cometbft/cometbft/libs/bytes"
cmtbytes "github.com/cometbft/cometbft/libs/bytes"
cmtmath "github.com/cometbft/cometbft/libs/math"
cmtquery "github.com/cometbft/cometbft/libs/pubsub/query"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
import (
cmtbytes "github.com/cometbft/cometbft/libs/bytes"
cmtmath "github.com/cometbft/cometbft/libs/math"
cmtquery "github.com/cometbft/cometbft/libs/pubsub/query"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
)

@randygrok randygrok enabled auto-merge (squash) May 6, 2025 09:56
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

♻️ Duplicate comments (5)
pkg/rpc/core/utils.go (2)

148-166: 🛠️ Refactor suggestion

getBlockMeta should return errors instead of logging and returning nil

Currently, the function logs errors and returns nil, forcing callers to inspect the return value for nil every time instead of handling specific errors.

-func getBlockMeta(ctx context.Context, n uint64) *cmtypes.BlockMeta {
+func getBlockMeta(ctx context.Context, n uint64) (*cmtypes.BlockMeta, error) {
 	header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx, n)
 	if err != nil {
-		env.Logger.Error("Failed to get block data in getBlockMeta", "height", n, "err", err)
-		return nil
+		return nil, fmt.Errorf("get block %d: %w", n, err)
 	}
 	if header == nil || data == nil {
-		env.Logger.Error("Nil header or data returned from GetBlockData", "height", n)
-		return nil
+		return nil, fmt.Errorf("missing header/data for height %d", n)
 	}
 	// Assuming ToABCIBlockMeta is now in pkg/rpc/provider/provider_utils.go
 	bmeta, err := ToABCIBlockMeta(header, data) // Removed rpc. prefix
 	if err != nil {
-		env.Logger.Error("Failed to convert block to ABCI block meta", "height", n, "err", err)
-		return nil
+		return nil, fmt.Errorf("convert block meta: %w", err)
 	}

-	return bmeta
+	return bmeta, nil
}

The comment on line 158 is also outdated as the function is now in this file, not in provider_utils.go.


120-146: 🛠️ Refactor suggestion

Handle errors more robustly in normalizeHeight function

The current implementation swallows errors and returns 0 when the store lookup fails, which can yield misleading results - callers might think the chain is at genesis height when there's actually an error.

-func normalizeHeight(height *int64) uint64 {
+func normalizeHeight(height *int64) (uint64, error) {
 	var heightValue uint64
 	if height == nil {
 		var err error
 		// TODO: Decide how to handle context here. Using background for now.
 		heightValue, err = env.Adapter.RollkitStore.Height(context.Background())
 		if err != nil {
-			// TODO: Consider logging or returning error
-			env.Logger.Error("Failed to get current height in normalizeHeight", "err", err)
-			return 0
+			return 0, fmt.Errorf("failed to get current height: %w", err)
 		}
 	} else if *height < 0 {
 		// Handle negative heights if they have special meaning (e.g., -1 for latest)
 		// Currently, just treat them as 0 or latest, adjust as needed.
 		// For now, let's assume negative height means latest valid height.
 		var err error
 		heightValue, err = env.Adapter.RollkitStore.Height(context.Background())
 		if err != nil {
-			env.Logger.Error("Failed to get current height for negative height in normalizeHeight", "err", err)
-			return 0
+			return 0, fmt.Errorf("failed to get height for negative input: %w", err)
 		}
 	} else {
 		heightValue = uint64(*height)
 	}

-	return heightValue
+	return heightValue, nil
}

All call sites will need to be updated to handle the error return value, preventing silent failures.

pkg/rpc/core/blocks.go (3)

35-38: ⚠️ Potential issue

maxQueryLength is referenced but not defined

The variable maxQueryLength is referenced in the comment and code but not defined in this file or imported. It was likely defined in the removed provider_utils.go file.

Add a constant definition in a common file (like env.go or const.go):

// In const.go or env.go
const MaxQueryLength = 512 // Adjust value as appropriate

Then update the reference here:

-	// Use the locally defined maxQueryLength from provider_utils.go
-	if len(query) > maxQueryLength {
+	if len(query) > MaxQueryLength {

231-231: ⚠️ Potential issue

Fix the parameter type to use the aliased HexBytes

Since you're removing the unnamed import of github.com/cometbft/cometbft/libs/bytes, update the parameter type to use the aliased cmtbytes.HexBytes.

-func HeaderByHash(ctx *rpctypes.Context, hash bytes.HexBytes) (*ctypes.ResultHeader, error) {
+func HeaderByHash(ctx *rpctypes.Context, hash cmtbytes.HexBytes) (*ctypes.ResultHeader, error) {

8-9: ⚠️ Potential issue

Fix duplicate package import

The package github.com/cometbft/cometbft/libs/bytes is imported twice - once unnamed (which shadows the standard library bytes package) and once as cmtbytes. This causes a compilation error.

-import (
-	"errors"
-	"fmt"
-	"sort"
-
-	"github.com/cometbft/cometbft/libs/bytes"
-	cmtbytes "github.com/cometbft/cometbft/libs/bytes"
+import (
+	"errors"
+	"fmt"
+	"sort"
+
+	cmtbytes "github.com/cometbft/cometbft/libs/bytes"

You'll also need to update line 231 to use the alias:

-func HeaderByHash(ctx *rpctypes.Context, hash bytes.HexBytes) (*ctypes.ResultHeader, error) {
+func HeaderByHash(ctx *rpctypes.Context, hash cmtbytes.HexBytes) (*ctypes.ResultHeader, error) {
🧹 Nitpick comments (3)
pkg/rpc/core/utils.go (1)

158-158: Remove outdated code comment

The comment refers to ToABCIBlockMeta being in pkg/rpc/provider/provider_utils.go, but this function is actually now defined in this file.

-	// Assuming ToABCIBlockMeta is now in pkg/rpc/provider/provider_utils.go
 	bmeta, err := ToABCIBlockMeta(header, data) // Removed rpc. prefix
pkg/rpc/core/consensus.go (1)

95-95: Remove outdated TODO comment

The comment suggests normalizeHeight should be moved to rpcProvider, but this function is already in the core package.

-	// Use normalizeHeight which should be moved to rpcProvider as well
 	normalizedHeight := normalizeHeight(heightPtr) // Changed r.normalizeHeight to p.normalizeHeight
pkg/rpc/core/blocks.go (1)

246-250: Clarify behavior for block not found by hash

There's an ambiguous comment about whether to return an empty result or an error when a block is not found by hash. This should be explicitly decided.

For better API consistency, consider returning an error instead of an empty result:

 	if blockMeta == nil {
-		// Return empty result without error if block not found by hash, consistent with original behaviour?
-		// Or return an error? fmt.Errorf("block with hash %X not found", hash)
-		return &ctypes.ResultHeader{}, nil // Current behaviour matches original code
+		return nil, fmt.Errorf("block with hash %X not found", hash)
 	}

This would align with the behavior of the Header function which returns an error when a block is not found by height.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7061d21 and 45f2e0a.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (6)
  • go.mod (1 hunks)
  • pkg/rpc/core/blocks.go (9 hunks)
  • pkg/rpc/core/consensus.go (1 hunks)
  • pkg/rpc/core/status.go (1 hunks)
  • pkg/rpc/core/utils.go (2 hunks)
  • server/start.go (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • go.mod
  • pkg/rpc/core/status.go
  • server/start.go
🧰 Additional context used
🧬 Code Graph Analysis (2)
pkg/rpc/core/consensus.go (4)
pkg/adapter/adapter.go (1)
  • Adapter (40-57)
pkg/adapter/store.go (1)
  • Store (22-24)
pkg/rpc/core/errors.go (1)
  • ErrConsensusStateNotAvailable (7-7)
pkg/rpc/core/blocks.go (1)
  • Block (104-136)
pkg/rpc/core/utils.go (1)
pkg/adapter/adapter.go (1)
  • Adapter (40-57)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: lint / golangci-lint

Comment on lines +24 to +37
height := normalizeHeight(heightPtr)

s, err := env.Adapter.Store.LoadState(ctx.Context()) // Loads the *latest* state
if err != nil {
return nil, fmt.Errorf("failed to load current state: %w", err)
}

// Check if the requested height matches the loaded state height if a specific height was requested.
// If state history is not kept, this check might be necessary or always fail for past heights.
if heightPtr != nil && int64(height) != s.LastBlockHeight {
// This implies state for the requested height is not available with the current LoadState method.
// Adjust implementation if historical state access is possible and needed.
return nil, fmt.Errorf("validator set for height %d is not available, latest height is %d", height, s.LastBlockHeight)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update function to handle returning error from normalizeHeight

If you implement the suggested change to make normalizeHeight return an error, this function needs to be updated to handle it.

 	// If height is nil or latest, use the current block height.
 	// Otherwise, use the specified height (if state for that height is available).
 	// Note: Loading state for arbitrary past heights might not be supported
 	// depending on state pruning. The current implementation implicitly loads latest state.
-	height := normalizeHeight(heightPtr)
+	height, err := normalizeHeight(heightPtr)
+	if err != nil {
+		return nil, fmt.Errorf("failed to normalize height: %w", err)
+	}

 	s, err := env.Adapter.Store.LoadState(ctx.Context()) // Loads the *latest* state
 	if err != nil {
 		return nil, fmt.Errorf("failed to load current state: %w", 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
height := normalizeHeight(heightPtr)
s, err := env.Adapter.Store.LoadState(ctx.Context()) // Loads the *latest* state
if err != nil {
return nil, fmt.Errorf("failed to load current state: %w", err)
}
// Check if the requested height matches the loaded state height if a specific height was requested.
// If state history is not kept, this check might be necessary or always fail for past heights.
if heightPtr != nil && int64(height) != s.LastBlockHeight {
// This implies state for the requested height is not available with the current LoadState method.
// Adjust implementation if historical state access is possible and needed.
return nil, fmt.Errorf("validator set for height %d is not available, latest height is %d", height, s.LastBlockHeight)
}
// If height is nil or latest, use the current block height.
// Otherwise, use the specified height (if state for that height is available).
// Note: Loading state for arbitrary past heights might not be supported
// depending on state pruning. The current implementation implicitly loads latest state.
- height := normalizeHeight(heightPtr)
+ height, err := normalizeHeight(heightPtr)
+ if err != nil {
+ return nil, fmt.Errorf("failed to normalize height: %w", err)
+ }
s, err := env.Adapter.Store.LoadState(ctx.Context()) // Loads the *latest* state
if err != nil {
return nil, fmt.Errorf("failed to load current state: %w", err)
}
// Check if the requested height matches the loaded state height if a specific height was requested.
// If state history is not kept, this check might be necessary or always fail for past heights.
if heightPtr != nil && int64(height) != s.LastBlockHeight {
// This implies state for the requested height is not available with the current LoadState method.
// Adjust implementation if historical state access is possible and needed.
return nil, fmt.Errorf("validator set for height %d is not available, latest height is %d", height, s.LastBlockHeight)
}

Comment on lines +95 to +98
normalizedHeight := normalizeHeight(heightPtr) // Changed r.normalizeHeight to p.normalizeHeight
return &coretypes.ResultConsensusParams{
BlockHeight: int64(normalizedHeight), //nolint:gosec
ConsensusParams: cmttypes.ConsensusParams{
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update function to handle returning error from normalizeHeight

If you implement the suggested change to make normalizeHeight return an error, this function needs to be updated to handle it.

 	// Use normalizeHeight which should be moved to rpcProvider as well
-	normalizedHeight := normalizeHeight(heightPtr) // Changed r.normalizeHeight to p.normalizeHeight
+	normalizedHeight, err := normalizeHeight(heightPtr)
+	if err != nil {
+		return nil, fmt.Errorf("failed to normalize height: %w", err)
+	}
 	return &coretypes.ResultConsensusParams{
 		BlockHeight: int64(normalizedHeight), //nolint:gosec
📝 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
normalizedHeight := normalizeHeight(heightPtr) // Changed r.normalizeHeight to p.normalizeHeight
return &coretypes.ResultConsensusParams{
BlockHeight: int64(normalizedHeight), //nolint:gosec
ConsensusParams: cmttypes.ConsensusParams{
// Use normalizeHeight which should be moved to rpcProvider as well
normalizedHeight, err := normalizeHeight(heightPtr)
if err != nil {
return nil, fmt.Errorf("failed to normalize height: %w", err)
}
return &coretypes.ResultConsensusParams{
BlockHeight: int64(normalizedHeight), //nolint:gosec
ConsensusParams: cmttypes.ConsensusParams{

Comment on lines +18 to +37
func Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*coretypes.ResultValidators, error) {
// Determine the height to query validators for.
// If height is nil or latest, use the current block height.
// Otherwise, use the specified height (if state for that height is available).
// Note: Loading state for arbitrary past heights might not be supported
// depending on state pruning. The current implementation implicitly loads latest state.
height := normalizeHeight(heightPtr)

s, err := env.Adapter.Store.LoadState(ctx.Context()) // Loads the *latest* state
if err != nil {
return nil, fmt.Errorf("failed to load current state: %w", err)
}

// Check if the requested height matches the loaded state height if a specific height was requested.
// If state history is not kept, this check might be necessary or always fail for past heights.
if heightPtr != nil && int64(height) != s.LastBlockHeight {
// This implies state for the requested height is not available with the current LoadState method.
// Adjust implementation if historical state access is possible and needed.
return nil, fmt.Errorf("validator set for height %d is not available, latest height is %d", height, s.LastBlockHeight)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Validators function only provides latest validator set

The function always loads the latest state, even when a specific historical height is requested. This diverges from CometBFT's RPC semantics where historical validator sets are available (if retained).

Consider one of these approaches:

  1. Document this limitation in the function comment for API clarity
  2. If the store keeps historical states, enhance the adapter with a LoadStateAt(height uint64) method and use it here

🏁 Script executed:

#!/bin/bash
# Check if RollkitStore interface has support for historical state access

echo "Checking RollkitStore interface definition:"
rg -A 15 "type Store interface" "pkg/store/store.go"

echo "Checking for any existing implementations that might access historical states:"
rg -A 3 "LoadState.*height|LoadStateAt" --type go

Length of output: 427


🏁 Script executed:

#!/bin/bash
set -e

echo "Searching for Store interface definitions across the repo:"
rg -R "type Store interface" -A15

echo
echo "Searching for any LoadState implementations or overloads:"
rg -R "func .*LoadState" -A3 --type go

echo
echo "Searching for Adapter definition to locate Store field:"
rg -R "type Adapter" -A10 --type go

Length of output: 576


🏁 Script executed:

#!/bin/bash
set -e

echo "Searching for Store interface definitions across the repo:"
rg "type Store interface" -A15 --type go || echo "No Store interface found"

echo
echo "Searching for any LoadState implementations or overloads:"
rg "func .*LoadState" -A3 --type go || echo "No LoadState implementations found"

echo
echo "Searching for Adapter definition to locate Store field:"
rg "type Adapter" -A10 --type go || echo "No Adapter definition found"

Length of output: 1360


Validators RPC only returns the latest validator set

The Validators handler in pkg/rpc/core/consensus.go always calls

s, err := env.Adapter.Store.LoadState(ctx.Context())

which only loads the latest state—any non-nil heightPtr will trigger an error rather than fetching a historical validator set.

Please address this by one of the following:

  • Document the limitation if your chain does not retain past state (due to pruning). For example, update the function comment:

    // Validators returns the validator set at the specified height.
    // Note: only the latest state is currently supported; requesting a past height
    // will return an error if historical state is unavailable.
    func Validators(…)
  • Implement historical‐state lookup if the underlying store can serve past heights:

    1. Add a method on your adapter, e.g.
      func (s *Store) LoadStateAt(ctx context.Context, height int64) (*cmtstate.State, error) { … }
    2. In Validators, branch on heightPtr != nil to call LoadStateAt when fetching a past height.

Locations needing attention:

  • pkg/rpc/core/consensus.go (lines 18–37)
  • pkg/adapter/store.go – no LoadStateAt method exists today

Comment on lines +190 to +191
_ = normalizeHeight(heightPtr) // Use height to avoid unused variable error, logic depends on future implementation
return &ctypes.ResultBlockResults{}, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update function to handle returning error from normalizeHeight

If you implement the suggested change to make normalizeHeight return an error, this function needs to be updated to handle it.

-	_ = normalizeHeight(heightPtr) // Use height to avoid unused variable error, logic depends on future implementation
+	_, _ = normalizeHeight(heightPtr) // Use height to avoid unused variable error, logic depends on future implementation

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

Comment on lines +166 to +167
heightValue := normalizeHeight(heightPtr)
header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update function to handle returning error from normalizeHeight

If you implement the suggested change to make normalizeHeight return an error, this function needs to be updated to handle it.

-	heightValue := normalizeHeight(heightPtr)
+	heightValue, err := normalizeHeight(heightPtr)
+	if err != nil {
+		return nil, fmt.Errorf("failed to normalize height: %w", err)
+	}
 	header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue)
📝 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
heightValue := normalizeHeight(heightPtr)
header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue)
heightValue, err := normalizeHeight(heightPtr)
if err != nil {
return nil, fmt.Errorf("failed to normalize height: %w", err)
}
header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue)

Comment on lines +221 to +222
height := normalizeHeight(heightPtr)
blockMeta := getBlockMeta(ctx.Context(), height)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update function to handle returning error from normalizeHeight and getBlockMeta

If you implement the suggested changes to make normalizeHeight and getBlockMeta return errors, this function needs to be updated to handle them.

-	height := normalizeHeight(heightPtr)
-	blockMeta := getBlockMeta(ctx.Context(), height)
+	height, err := normalizeHeight(heightPtr)
+	if err != nil {
+		return nil, fmt.Errorf("failed to normalize height: %w", err)
+	}
+	
+	blockMeta, err := getBlockMeta(ctx.Context(), height)
 	if blockMeta == nil {
 		return nil, fmt.Errorf("block at height %d not found", height)
 	}

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

Comment on lines +283 to 284
bMeta := getBlockMeta(ctx.Context(), uint64(h))
if bMeta != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update function to handle returning error from getBlockMeta

If you implement the suggested change to make getBlockMeta return an error, this function needs to be updated to handle it.

-		bMeta := getBlockMeta(ctx.Context(), uint64(h))
+		bMeta, err := getBlockMeta(ctx.Context(), uint64(h))
+		if err != nil {
+			// Log the error but continue - maintain existing behavior of skipping blocks with retrieval errors
+			env.Logger.Debug("Failed to get block meta", "height", h, "err", err)
+		}
 		if bMeta != nil {
 			blocks = append(blocks, bMeta)
 		}
📝 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
bMeta := getBlockMeta(ctx.Context(), uint64(h))
if bMeta != nil {
bMeta, err := getBlockMeta(ctx.Context(), uint64(h))
if err != nil {
// Log the error but continue - maintain existing behavior of skipping blocks with retrieval errors
env.Logger.Debug("Failed to get block meta", "height", h, "err", err)
}
if bMeta != nil {
blocks = append(blocks, bMeta)
}

Comment on lines +104 to 115
func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
var heightValue uint64

switch {
// block tag = included
case height != nil && *height == -1:
case heightPtr != nil && *heightPtr == -1:
// heightValue = p.adapter.store.GetDAIncludedHeight()
// TODO: implement
return nil, errors.New("DA included height not implemented")
default:
heightValue = p.normalizeHeight(height)
heightValue = normalizeHeight(heightPtr)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update function to handle returning error from normalizeHeight

If you implement the suggested change to make normalizeHeight return an error, this function needs to be updated to handle it.

 	default:
-		heightValue = normalizeHeight(heightPtr)
+		var err error
+		heightValue, err = normalizeHeight(heightPtr)
+		if err != nil {
+			return nil, fmt.Errorf("failed to normalize height: %w", err)
+		}
 	}
 	header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue)
📝 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
func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
var heightValue uint64
switch {
// block tag = included
case height != nil && *height == -1:
case heightPtr != nil && *heightPtr == -1:
// heightValue = p.adapter.store.GetDAIncludedHeight()
// TODO: implement
return nil, errors.New("DA included height not implemented")
default:
heightValue = p.normalizeHeight(height)
heightValue = normalizeHeight(heightPtr)
}
func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
var heightValue uint64
switch {
// block tag = included
case heightPtr != nil && *heightPtr == -1:
// heightValue = p.adapter.store.GetDAIncludedHeight()
// TODO: implement
return nil, errors.New("DA included height not implemented")
default:
var err error
heightValue, err = normalizeHeight(heightPtr)
if err != nil {
return nil, fmt.Errorf("failed to normalize height: %w", err)
}
}
header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue)
// ...
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (2)
pkg/rpc/core/mempool.go (1)

52-59: ⚠️ Potential issue

Return value uses undefined package name.

The function attempts to return a coretypes.ResultBroadcastTx but there's no import for coretypes, only for ctypes.

-	return &coretypes.ResultBroadcastTx{
+	return &ctypes.ResultBroadcastTx{
		Code:      res.Code,
		Data:      res.Data,
		Log:       res.Log,
		Codespace: res.Codespace,
		Hash:      tx.Hash(),
	}, nil
🧰 Tools
🪛 GitHub Check: lint / golangci-lint

[failure] 52-52:
undefined: coretypes

pkg/rpc/core/blocks.go (1)

187-215: 🛠️ Refactor suggestion

Consider implementing full BlockResults functionality.

The BlockResults function is currently a stub that returns empty results. Consider implementing the full functionality using the original commented-out logic as a guide, as this would provide valuable block result data to clients.

♻️ Duplicate comments (5)
pkg/rpc/core/mempool.go (1)

269-286: UnconfirmedTxs slices without copying – risk of data race with mempool

ReapMaxTxs returns references into the mempool; concurrent mempool updates may mutate the slice while it's being JSON-encoded. Make an explicit copy.

-	txs := env.Adapter.Mempool.ReapMaxTxs(-1) // Reap all transactions
+	rawTxs := env.Adapter.Mempool.ReapMaxTxs(-1) // Reap all transactions
+	txs := make([]types.Tx, len(rawTxs))
+	copy(txs, rawTxs)

	limit := len(txs)
	if limitPtr != nil {
		limit = cmtmath.MinInt(*limitPtr, limit)
		if limit < 0 {
			limit = 0
		}
	}
	paginatedTxs := txs[:limit]
pkg/rpc/core/blocks.go (4)

165-168: Update function to handle returning error from normalizeHeight.

Previously, it was suggested to make normalizeHeight return an error that needs to be handled here.

-	heightValue := normalizeHeight(heightPtr)
+	heightValue, err := normalizeHeight(heightPtr)
+	if err != nil {
+		return nil, fmt.Errorf("failed to normalize height: %w", err)
+	}
 	header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue)

220-225: Update function to handle returning errors from helper functions.

The functions normalizeHeight and getBlockMeta should return errors that need to be handled here.

-	height := normalizeHeight(heightPtr)
-	blockMeta := getBlockMeta(ctx.Context(), height)
+	height, err := normalizeHeight(heightPtr)
+	if err != nil {
+		return nil, fmt.Errorf("failed to normalize height: %w", err)
+	}
+	
+	blockMeta, err := getBlockMeta(ctx.Context(), height)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get block meta: %w", err)
+	}
	if blockMeta == nil {
		return nil, fmt.Errorf("block at height %d not found", height)
	}

283-286: Update function to handle returning error from getBlockMeta.

The getBlockMeta function should return an error that needs to be handled here.

-		bMeta := getBlockMeta(ctx.Context(), uint64(h))
+		bMeta, err := getBlockMeta(ctx.Context(), uint64(h))
+		if err != nil {
+			// Log the error but continue - maintain existing behavior of skipping blocks with retrieval errors
+			env.Logger.Debug("Failed to get block meta", "height", h, "err", err)
+		}
		if bMeta != nil {
			blocks = append(blocks, bMeta)
		}

231-235: ⚠️ Potential issue

Undefined bytes package - fix import issue.

The HeaderByHash function signature uses bytes.HexBytes but there's no import for the bytes package. You're already importing the correct package as cmtbytes.

- func HeaderByHash(ctx *rpctypes.Context, hash bytes.HexBytes) (*ctypes.ResultHeader, error) {
+ func HeaderByHash(ctx *rpctypes.Context, hash cmtbytes.HexBytes) (*ctypes.ResultHeader, error) {
🧰 Tools
🪛 GitHub Check: lint / golangci-lint

[failure] 231-231:
undefined: bytes

🪛 GitHub Actions: CI and Release

[error] 231-231: undefined: bytes

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 45f2e0a and 882883e.

📒 Files selected for processing (5)
  • pkg/rpc/core/blocks.go (9 hunks)
  • pkg/rpc/core/const.go (1 hunks)
  • pkg/rpc/core/env.go (1 hunks)
  • pkg/rpc/core/mempool.go (9 hunks)
  • pkg/rpc/server.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/rpc/core/env.go
🧰 Additional context used
🧬 Code Graph Analysis (1)
pkg/rpc/server.go (1)
pkg/rpc/core/routes.go (1)
  • Routes (7-48)
🪛 GitHub Check: lint / golangci-lint
pkg/rpc/core/blocks.go

[failure] 231-231:
undefined: bytes

pkg/rpc/core/mempool.go

[failure] 28-28:
undefined: types


[failure] 117-117:
undefined: types


[failure] 106-106:
undefined: coretypes


[failure] 64-64:
undefined: types


[failure] 302-302:
undefined: types

🪛 GitHub Actions: CI and Release
pkg/rpc/core/blocks.go

[error] 231-231: undefined: bytes

🔇 Additional comments (5)
pkg/rpc/core/const.go (1)

1-10: Well-structured constants with clear documentation.

The constants define appropriate limits for query length and pagination, with good descriptive names and helpful comments.

pkg/rpc/server.go (3)

13-19: Clean import organization with appropriate dependencies.

The imports are well-organized with the new core package import properly added.


27-31: Appropriate logger wrapping for CometBFT compatibility.

The server now properly wraps the SDK logger with the CometBFT logger wrapper, improving compatibility with the CometBFT libraries.


65-67: Clean RPC route registration pattern.

The server now creates and manages its own HTTP mux, registering the centralized RPC functions from the core package. This approach is more modular and self-contained than the previous external handler injection.

pkg/rpc/core/blocks.go (1)

34-38: Great use of the newly defined maxQueryLength constant.

The code now correctly uses the maxQueryLength constant defined in pkg/rpc/core/const.go, addressing a previous issue.

Comment on lines +1 to 17
package core

import (
"context"
"errors"
"fmt"
"time"

abci "github.com/cometbft/cometbft/abci/types" // Check if logger is used here
abci "github.com/cometbft/cometbft/abci/types"
cmtmath "github.com/cometbft/cometbft/libs/math"
"github.com/cometbft/cometbft/mempool"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
cmttypes "github.com/cometbft/cometbft/types"

execp2p "github.com/rollkit/go-execution-abci/pkg/p2p"
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix missing type imports.

The code references undefined types that need to be imported.

package core

import (
	"context"
	"errors"
	"fmt"
	"time"

	abci "github.com/cometbft/cometbft/abci/types"
	cmtmath "github.com/cometbft/cometbft/libs/math"
	"github.com/cometbft/cometbft/mempool"
	ctypes "github.com/cometbft/cometbft/rpc/core/types"
	rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
	cmttypes "github.com/cometbft/cometbft/types"

	execp2p "github.com/rollkit/go-execution-abci/pkg/p2p"
+	"github.com/rollkit/rollkit/types"
)
📝 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
package core
import (
"context"
"errors"
"fmt"
"time"
abci "github.com/cometbft/cometbft/abci/types" // Check if logger is used here
abci "github.com/cometbft/cometbft/abci/types"
cmtmath "github.com/cometbft/cometbft/libs/math"
"github.com/cometbft/cometbft/mempool"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
cmttypes "github.com/cometbft/cometbft/types"
execp2p "github.com/rollkit/go-execution-abci/pkg/p2p"
)
package core
import (
"context"
"errors"
"fmt"
"time"
abci "github.com/cometbft/cometbft/abci/types"
cmtmath "github.com/cometbft/cometbft/libs/math"
"github.com/cometbft/cometbft/mempool"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types"
cmttypes "github.com/cometbft/cometbft/types"
execp2p "github.com/rollkit/go-execution-abci/pkg/p2p"
"github.com/rollkit/rollkit/types"
)

Comment on lines 11 to 17
"cosmossdk.io/log"
cmtcfg "github.com/cometbft/cometbft/config"
"github.com/rs/cors"
cmtlog "github.com/cometbft/cometbft/libs/log"
rpcserver "github.com/cometbft/cometbft/rpc/jsonrpc/server"
servercmtlog "github.com/cosmos/cosmos-sdk/server/log"
"golang.org/x/net/netutil"
)
"github.com/rs/cors"
Copy link

Choose a reason for hiding this comment

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

Consider organizing imports into logical groups with blank lines between them:

  1. Standard library imports
  2. External dependencies (with CometBFT packages grouped together)
  3. Internal packages

This makes the import section more readable and follows common Go conventions.

Suggested change
"cosmossdk.io/log"
cmtcfg "github.com/cometbft/cometbft/config"
"github.com/rs/cors"
cmtlog "github.com/cometbft/cometbft/libs/log"
rpcserver "github.com/cometbft/cometbft/rpc/jsonrpc/server"
servercmtlog "github.com/cosmos/cosmos-sdk/server/log"
"golang.org/x/net/netutil"
)
"github.com/rs/cors"
"cosmossdk.io/log"
cmtcfg "github.com/cometbft/cometbft/config"
cmtlog "github.com/cometbft/cometbft/libs/log"
rpcserver "github.com/cometbft/cometbft/rpc/jsonrpc/server"
servercmtlog "github.com/cosmos/cosmos-sdk/server/log"
"github.com/rs/cors"
"golang.org/x/net/netutil"

Spotted by Diamond (based on custom rules)

Is this helpful? React 👍 or 👎 to let us know.

@randygrok randygrok merged commit 7eab829 into main May 6, 2025
10 checks passed
@randygrok randygrok deleted the feat/improve-rpc-server branch May 6, 2025 10:27
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.

2 participants