-
Notifications
You must be signed in to change notification settings - Fork 316
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(pd): 🎬 pull grpc endpoint into
penumbra-app
(#3996)
see #3913, #3973 and #3588. this is a second attempt, following up on #3980. #### 🔭 background NB: the difference between this and #3679 is that the latter (_which ran afoul of a regression_) would have `penumbra-app` create a `Routes`, that we would [add](https://github.com/penumbra-zone/penumbra/pull/3679/files#diff-fbc4204ceb976c8cb30ed06168e2476700bae21bfd803e26281b2d026194d430R204) to the builder (_which stays in `pd`_). here, i'm not trying to make that cut between `Router` and `Routes`, and am attempting to hoist the whole thing out of `pd`, without making changes to how we interact with `tonic`. my aim is for us to be able to move this, without running into that bug (#3697) again. NB: after running into problems in #3980, i found a way to easily reproduce the issue locally. my belief was that something related to our dependencies' cargo features was at play. rather than isolate the issue, it was easier to rewrite this (_it's just code motion, after all_) while running some of the network integration tests in a loop. unlike #3980, this moves the rpc server into `penumbra-app`, per #3980 (comment) #### 👁️ overview we would like to use the rust view server in mock consensus tests. in order to run the `penumbra_view::ViewServer` however, we need to spin up the corresponding grpc endpoint for it to connect to. this branch performs a bit of code motion, moving the `grpc_server` out of `pd` and into `penumbra-app`. there will likely be other functional changes to the code in question before we can use it in those tests, but this PR is interested in moving that code into a place where our tests can rely upon it.
- Loading branch information
Showing
5 changed files
with
187 additions
and
161 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,122 @@ | ||
use cnidarium::Storage; | ||
use penumbra_proto::core::app::v1::{ | ||
query_service_server::QueryService, AppParametersRequest, AppParametersResponse, | ||
TransactionsByHeightRequest, TransactionsByHeightResponse, | ||
}; | ||
use tonic::Status; | ||
use tracing::instrument; | ||
|
||
use crate::app::StateReadExt as _; | ||
|
||
// TODO: Hide this and only expose a Router? | ||
pub struct Server { | ||
storage: Storage, | ||
} | ||
mod query; | ||
|
||
impl Server { | ||
pub fn new(storage: Storage) -> Self { | ||
Self { storage } | ||
} | ||
} | ||
|
||
#[tonic::async_trait] | ||
impl QueryService for Server { | ||
#[instrument(skip(self, request))] | ||
async fn transactions_by_height( | ||
&self, | ||
request: tonic::Request<TransactionsByHeightRequest>, | ||
) -> Result<tonic::Response<TransactionsByHeightResponse>, Status> { | ||
let state = self.storage.latest_snapshot(); | ||
let request_inner = request.into_inner(); | ||
let block_height = request_inner.block_height; | ||
// TODO: Once we migrate to Tonic 0.10.0, we'll be able to use the `Routes` structure to have each | ||
// component define a method that returns a `Routes` with all of its query services bundled inside. | ||
// | ||
// This means we won't have to import all this shit and recite every single service -- we can e.g., | ||
// have the app crate assemble all of its components' query services into a single `Routes` and | ||
// then just add that to the gRPC server. | ||
use { | ||
self::query::AppQueryServer, | ||
crate::PenumbraHost, | ||
anyhow::Context, | ||
cnidarium::rpc::{ | ||
proto::v1::query_service_server::QueryServiceServer as StorageQueryServiceServer, | ||
Server as StorageServer, | ||
}, | ||
ibc_proto::ibc::core::{ | ||
channel::v1::query_server::QueryServer as ChannelQueryServer, | ||
client::v1::query_server::QueryServer as ClientQueryServer, | ||
connection::v1::query_server::QueryServer as ConnectionQueryServer, | ||
}, | ||
penumbra_compact_block::component::rpc::Server as CompactBlockServer, | ||
penumbra_dex::component::rpc::Server as DexServer, | ||
penumbra_fee::component::rpc::Server as FeeServer, | ||
penumbra_governance::component::rpc::Server as GovernanceServer, | ||
penumbra_proto::{ | ||
core::{ | ||
app::v1::query_service_server::QueryServiceServer as AppQueryServiceServer, | ||
component::{ | ||
compact_block::v1::query_service_server::QueryServiceServer as CompactBlockQueryServiceServer, | ||
dex::v1::{ | ||
query_service_server::QueryServiceServer as DexQueryServiceServer, | ||
simulation_service_server::SimulationServiceServer, | ||
}, | ||
fee::v1::query_service_server::QueryServiceServer as FeeQueryServiceServer, | ||
governance::v1::query_service_server::QueryServiceServer as GovernanceQueryServiceServer, | ||
sct::v1::query_service_server::QueryServiceServer as SctQueryServiceServer, | ||
shielded_pool::v1::query_service_server::QueryServiceServer as ShieldedPoolQueryServiceServer, | ||
stake::v1::query_service_server::QueryServiceServer as StakeQueryServiceServer, | ||
}, | ||
}, | ||
util::tendermint_proxy::v1::tendermint_proxy_service_server::TendermintProxyServiceServer, | ||
}, | ||
penumbra_sct::component::rpc::Server as SctServer, | ||
penumbra_shielded_pool::component::rpc::Server as ShieldedPoolServer, | ||
penumbra_stake::component::rpc::Server as StakeServer, | ||
penumbra_tendermint_proxy::TendermintProxy, | ||
penumbra_tower_trace::remote_addr, | ||
tonic_web::enable as we, | ||
}; | ||
|
||
let tx_response = state | ||
.transactions_by_height(block_height) | ||
.await | ||
.map_err(|e| tonic::Status::internal(format!("transaction response bad: {e}")))?; | ||
pub fn router( | ||
storage: &cnidarium::Storage, | ||
cometbft_addr: url::Url, | ||
enable_expensive_rpc: bool, | ||
) -> anyhow::Result<tonic::transport::server::Router> { | ||
let tm_proxy = TendermintProxy::new(cometbft_addr); | ||
let ibc = penumbra_ibc::component::rpc::IbcQuery::<PenumbraHost>::new(storage.clone()); | ||
let mut grpc_server = tonic::transport::server::Server::builder() | ||
.trace_fn(|req| match remote_addr(req) { | ||
Some(remote_addr) => { | ||
tracing::error_span!("grpc", ?remote_addr) | ||
} | ||
None => tracing::error_span!("grpc"), | ||
}) | ||
// Allow HTTP/1, which will be used by grpc-web connections. | ||
// This is particularly important when running locally, as gRPC | ||
// typically uses HTTP/2, which requires HTTPS. Accepting HTTP/2 | ||
// allows local applications such as web browsers to talk to pd. | ||
.accept_http1(true) | ||
// As part of #2932, we are disabling all timeouts until we circle back to our | ||
// performance story. | ||
// Sets a timeout for all gRPC requests, but note that in the case of streaming | ||
// requests, the timeout is only applied to the initial request. This means that | ||
// this does not prevent long lived streams, for example to allow clients to obtain | ||
// new blocks. | ||
// .timeout(std::time::Duration::from_secs(7)) | ||
// Wrap each of the gRPC services in a tonic-web proxy: | ||
.add_service(we(StorageQueryServiceServer::new(StorageServer::new( | ||
storage.clone(), | ||
)))) | ||
.add_service(we(AppQueryServiceServer::new(AppQueryServer::new( | ||
storage.clone(), | ||
)))) | ||
.add_service(we(CompactBlockQueryServiceServer::new( | ||
CompactBlockServer::new(storage.clone()), | ||
))) | ||
.add_service(we(DexQueryServiceServer::new(DexServer::new( | ||
storage.clone(), | ||
)))) | ||
.add_service(we(FeeQueryServiceServer::new(FeeServer::new( | ||
storage.clone(), | ||
)))) | ||
.add_service(we(GovernanceQueryServiceServer::new( | ||
GovernanceServer::new(storage.clone()), | ||
))) | ||
.add_service(we(SctQueryServiceServer::new(SctServer::new( | ||
storage.clone(), | ||
)))) | ||
.add_service(we(ShieldedPoolQueryServiceServer::new( | ||
ShieldedPoolServer::new(storage.clone()), | ||
))) | ||
.add_service(we(StakeQueryServiceServer::new(StakeServer::new( | ||
storage.clone(), | ||
)))) | ||
.add_service(we(ClientQueryServer::new(ibc.clone()))) | ||
.add_service(we(ChannelQueryServer::new(ibc.clone()))) | ||
.add_service(we(ConnectionQueryServer::new(ibc.clone()))) | ||
.add_service(we(TendermintProxyServiceServer::new(tm_proxy.clone()))) | ||
.add_service(we(tonic_reflection::server::Builder::configure() | ||
.register_encoded_file_descriptor_set(penumbra_proto::FILE_DESCRIPTOR_SET) | ||
.build() | ||
.with_context(|| "could not configure grpc reflection service")?)); | ||
|
||
Ok(tonic::Response::new(tx_response)) | ||
if enable_expensive_rpc { | ||
grpc_server = grpc_server.add_service(we(SimulationServiceServer::new(DexServer::new( | ||
storage.clone(), | ||
)))); | ||
} | ||
|
||
#[instrument(skip(self, _request))] | ||
async fn app_parameters( | ||
&self, | ||
_request: tonic::Request<AppParametersRequest>, | ||
) -> Result<tonic::Response<AppParametersResponse>, Status> { | ||
let state = self.storage.latest_snapshot(); | ||
// We map the error here to avoid including `tonic` as a dependency | ||
// in the `chain` crate, to support its compilation to wasm. | ||
|
||
let app_parameters = state.get_app_params().await.map_err(|e| { | ||
tonic::Status::unavailable(format!("error getting app parameters: {e}")) | ||
})?; | ||
|
||
Ok(tonic::Response::new(AppParametersResponse { | ||
app_parameters: Some(app_parameters.into()), | ||
})) | ||
} | ||
Ok(grpc_server) | ||
} |
Oops, something went wrong.