feat!: minimal gRPC endpoint (app) support#10
feat!: minimal gRPC endpoint (app) support#10gbarros wants to merge 5 commits intoserver-refactorfrom
Conversation
There was a problem hiding this comment.
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.
Ferret-san
left a comment
There was a problem hiding this comment.
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
|
I think I will let this PR sit here until we wait for the traits then @Ferret-san |
|
sounds like a plan to me! |
feat: refactor with lumina server traits
…Status; update dependencies
9522d68 to
ec2027f
Compare
There was a problem hiding this comment.
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.
…in transaction status
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
toniclibrary 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:
src/grpc/mod.rs, which wires up five gRPC services: node info, authentication, transaction broadcasting, transaction status, and gas estimation. (src/grpc/mod.rs)GetNodeInforequests for client initialization. (src/grpc/node_info.rs)src/grpc/auth.rs)src/grpc/tx_service.rs)src/grpc/tx_status.rs)src/grpc/gas_estimator.rs)Dependency and configuration updates:
Cargo.tomlfor gRPC support (tonic,prost,http, etc.), and updated proto dependencies to enable gRPC features. (Cargo.toml)src/main.rsto parse a newGRPC_ADDRenvironment 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.