Skip to content

feat!: minimal gRPC endpoint (app) support#10

Open
gbarros wants to merge 5 commits intoserver-refactorfrom
gb/grpc-txclient
Open

feat!: minimal gRPC endpoint (app) support#10
gbarros wants to merge 5 commits intoserver-refactorfrom
gb/grpc-txclient

Conversation

@gbarros
Copy link
Copy Markdown
Contributor

@gbarros gbarros commented Feb 25, 2026

This pull request introduces a new gRPC server alongside the existing JSON-RPC server, providing Celestia-compatible gRPC endpoints for node info, authentication, transaction broadcasting, gas estimation, and transaction status. The implementation uses the tonic library and adds several new service modules, each handling a specific gRPC API. The main entrypoint (main.rs) is updated to launch both servers and parse their respective listen addresses.

New gRPC server and services:

  • Added a new gRPC server entrypoint in src/grpc/mod.rs, which wires up five gRPC services: node info, authentication, transaction broadcasting, transaction status, and gas estimation. (src/grpc/mod.rs)
  • Implemented gRPC service modules for:
    • Node info: Handles GetNodeInfo requests for client initialization. (src/grpc/node_info.rs)
    • Authentication: Handles account queries for sequence/account_number. (src/grpc/auth.rs)
    • Transaction broadcasting: Decodes and stores blob transactions, returns tx hash and height. (src/grpc/tx_service.rs)
    • Transaction status: Looks up stored height for a tx and returns status. (src/grpc/tx_status.rs)
    • Gas estimation: Returns fixed gas price and usage estimates for blob transactions. (src/grpc/gas_estimator.rs)

Dependency and configuration updates:

  • Added new dependencies in Cargo.toml for gRPC support (tonic, prost, http, etc.), and updated proto dependencies to enable gRPC features. (Cargo.toml)
  • Updated src/main.rs to parse a new GRPC_ADDR environment variable, initialize the gRPC server, and log its address. (src/main.rs) [1] [2]

These changes allow the application to serve Celestia-compatible gRPC endpoints for local testing and integration, supporting both JSON-RPC and gRPC clients.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds minimal gRPC endpoint support to localestia, enabling it to handle Celestia transaction client (txclient) requests. The implementation provides five gRPC services that complement the existing JSON-RPC functionality, allowing clients to submit blob transactions via gRPC, check transaction status, query account information, estimate gas costs, and retrieve node information.

Changes:

  • Added gRPC server running concurrently with JSON-RPC server on configurable address (default: 0.0.0.0:9090)
  • Implemented five gRPC services: TxService (BroadcastTx), TxStatusService (TxStatus), AuthQueryService (Account query), GasEstimatorService (gas estimation), and NodeInfoService (GetNodeInfo)
  • Added Redis storage methods (store_tx_height, get_tx_height) to maintain transaction hash to height mappings

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/main.rs Added gRPC server initialization and concurrent execution with JSON-RPC server; added GRPC_ADDR environment variable
src/storage/redis_storage.rs Added store_tx_height and get_tx_height methods to store/retrieve transaction height mappings in Redis
src/grpc/mod.rs Module entry point that initializes and serves all five gRPC services
src/grpc/tx_service.rs Implements cosmos.tx.v1beta1.Service for BroadcastTx - decodes BlobTx, stores blobs, and returns tx hash
src/grpc/tx_status.rs Implements celestia.core.v1.tx.Tx for TxStatus - looks up transaction height and returns COMMITTED status
src/grpc/node_info.rs Implements cosmos.base.tendermint.v1beta1.Service for GetNodeInfo - returns static node information
src/grpc/auth.rs Implements cosmos.auth.v1beta1.Query for Account queries - returns mock account info for sequence/account_number
src/grpc/gas_estimator.rs Implements celestia.core.v1.gas_estimation.GasEstimator - returns fixed gas price and usage estimates
Cargo.toml Added gRPC dependencies: celestia-proto, tonic, prost, http, bytes, http-body, http-body-util, tendermint-proto
Cargo.lock Dependency resolution updates including tonic 0.13, prost 0.13, and related transitive dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Collaborator

@Ferret-san Ferret-san left a comment

Choose a reason for hiding this comment

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

I think the gRPC services services could implement the upstream traits to better ensure compatibility with the lumina crates, example generated by Claude:

use celestia_proto::cosmos::tx::v1beta1::{
    service_server::Service as TxService,
    SimulateRequest, SimulateResponse,
    GetTxRequest, GetTxResponse,
    BroadcastTxRequest, BroadcastTxResponse,
    GetTxsEventRequest, GetTxsEventResponse,
    GetBlockWithTxsRequest, GetBlockWithTxsResponse,
    TxDecodeRequest, TxDecodeResponse,
    TxEncodeRequest, TxEncodeResponse,
    TxEncodeAminoRequest, TxEncodeAminoResponse,
    TxDecodeAminoRequest, TxDecodeAminoResponse,
};
use celestia_proto::cosmos::base::abci::v1beta1::{TxResponse, GasInfo, Result as AbciResult};
use std::sync::Arc;
use tonic::{Request, Response, Status};

use crate::storage::RedisStorage;

pub struct MockTxService {
    storage: Arc<RedisStorage>,
}

impl MockTxService {
    pub fn new(storage: Arc<RedisStorage>) -> Self {
        Self { storage }
    }
}

#[tonic::async_trait]
impl TxService for MockTxService {
    async fn simulate(
        &self,
        _request: Request<SimulateRequest>,
    ) -> Result<Response<SimulateResponse>, Status> {
        // Return successful simulation with reasonable gas estimate
        Ok(Response::new(SimulateResponse {
            gas_info: Some(GasInfo {
                gas_wanted: 200_000,
                gas_used: 100_000,
            }),
            result: Some(AbciResult {
                data: vec![],
                log: String::new(),
                events: vec![],
                msg_responses: vec![],
            }),
        }))
    }

    async fn get_tx(
        &self,
        request: Request<GetTxRequest>,
    ) -> Result<Response<GetTxResponse>, Status> {
        let _hash = request.into_inner().hash;
        // Look up tx by hash in Redis, or return not found
        Err(Status::not_found("transaction not found"))
    }

    async fn broadcast_tx(
        &self,
        request: Request<BroadcastTxRequest>,
    ) -> Result<Response<BroadcastTxResponse>, Status> {
        let inner = request.into_inner();
        let _tx_bytes = inner.tx_bytes;

        // This is where the magic happens for blob submission:
        // 1. Decode the tx_bytes (it's a cosmos Tx, possibly a BlobTx)
        // 2. Extract MsgPayForBlobs + the blob sidecar
        // 3. Store blobs in Redis via your existing storage
        // 4. Return a TxResponse with a tx hash

        // For a minimal mock, just accept everything:
        let tx_hash = hex::encode(sha2::Sha256::digest(&_tx_bytes));

        Ok(Response::new(BroadcastTxResponse {
            tx_response: Some(TxResponse {
                height: 1,
                txhash: tx_hash,
                codespace: String::new(),
                code: 0, // 0 = success
                data: String::new(),
                raw_log: String::new(),
                logs: vec![],
                info: String::new(),
                gas_wanted: 200_000,
                gas_used: 100_000,
                tx: None,
                timestamp: String::new(),
                events: vec![],
            }),
        }))
    }

    async fn get_txs_event(
        &self,
        _req: Request<GetTxsEventRequest>,
    ) -> Result<Response<GetTxsEventResponse>, Status> {
        Err(Status::unimplemented("not implemented"))
    }

    async fn get_block_with_txs(
        &self,
        _req: Request<GetBlockWithTxsRequest>,
    ) -> Result<Response<GetBlockWithTxsResponse>, Status> {
        Err(Status::unimplemented("not implemented"))
    }

    async fn tx_decode(
        &self,
        _req: Request<TxDecodeRequest>,
    ) -> Result<Response<TxDecodeResponse>, Status> {
        Err(Status::unimplemented("not implemented"))
    }

    async fn tx_encode(
        &self,
        _req: Request<TxEncodeRequest>,
    ) -> Result<Response<TxEncodeResponse>, Status> {
        Err(Status::unimplemented("not implemented"))
    }

    async fn tx_encode_amino(
        &self,
        _req: Request<TxEncodeAminoRequest>,
    ) -> Result<Response<TxEncodeAminoResponse>, Status> {
        Err(Status::unimplemented("not implemented"))
    }

    async fn tx_decode_amino(
        &self,
        _req: Request<TxDecodeAminoRequest>,
    ) -> Result<Response<TxDecodeAminoResponse>, Status> {
        Err(Status::unimplemented("not implemented"))
    }
}

Also addressing the comments from copilot and adding testing for this feature is required

@Ferret-san Ferret-san changed the title Minimal gRPC endpoint (app) support feat!: minimal gRPC endpoint (app) support Feb 25, 2026
@gbarros
Copy link
Copy Markdown
Contributor Author

gbarros commented Feb 26, 2026

I think I will let this PR sit here until we wait for the traits then @Ferret-san
Correcting anything more could be meaningless in that changed context.

@Ferret-san
Copy link
Copy Markdown
Collaborator

sounds like a plan to me!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants