use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use std::time::Duration;
use anyhow::{Context, Result};
use http::header::ACCEPT;
use miden_node_utils::tracing::grpc::OtelInterceptor;
use tonic::metadata::AsciiMetadataValue;
use tonic::service::interceptor::InterceptedService;
use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
use tonic::{Request, Status};
use url::Url;
use crate::generated;
#[derive(Clone)]
pub struct Interceptor {
otel: Option<OtelInterceptor>,
accept: AsciiMetadataValue,
}
impl Default for Interceptor {
fn default() -> Self {
Self {
otel: None,
accept: AsciiMetadataValue::from_static(Self::MEDIA_TYPE),
}
}
}
impl Interceptor {
const MEDIA_TYPE: &str = "application/vnd.miden";
const VERSION: &str = "version";
const GENESIS: &str = "genesis";
fn new(enable_otel: bool, version: Option<&str>, genesis: Option<&str>) -> Self {
if let Some(version) = version
&& !version.is_ascii()
{
panic!("version contains non-ascii values: {version}");
}
if let Some(genesis) = genesis
&& !genesis.is_ascii()
{
panic!("genesis contains non-ascii values: {genesis}");
}
let accept = match (version, genesis) {
(None, None) => Self::MEDIA_TYPE.to_string(),
(None, Some(genesis)) => format!("{}; {}={genesis}", Self::MEDIA_TYPE, Self::GENESIS),
(Some(version), None) => format!("{}; {}={version}", Self::MEDIA_TYPE, Self::VERSION),
(Some(version), Some(genesis)) => format!(
"{}; {}={version}, {}={genesis}",
Self::MEDIA_TYPE,
Self::VERSION,
Self::GENESIS
),
};
Self {
otel: enable_otel.then_some(OtelInterceptor),
accept: AsciiMetadataValue::from_str(&accept).unwrap(),
}
}
}
impl tonic::service::Interceptor for Interceptor {
fn call(&mut self, mut request: tonic::Request<()>) -> Result<Request<()>, Status> {
if let Some(mut otel) = self.otel {
request = otel.call(request)?;
}
request.metadata_mut().insert(ACCEPT.as_str(), self.accept.clone());
Ok(request)
}
}
type InterceptedChannel = InterceptedService<Channel, Interceptor>;
type GeneratedRpcClient = generated::rpc::api_client::ApiClient<InterceptedChannel>;
type GeneratedBlockProducerClient =
generated::block_producer::api_client::ApiClient<InterceptedChannel>;
type GeneratedStoreClientForNtxBuilder =
generated::store::ntx_builder_client::NtxBuilderClient<InterceptedChannel>;
type GeneratedStoreClientForBlockProducer =
generated::store::block_producer_client::BlockProducerClient<InterceptedChannel>;
type GeneratedStoreClientForRpc = generated::store::rpc_client::RpcClient<InterceptedChannel>;
type GeneratedProxyStatusClient =
generated::remote_prover::proxy_status_api_client::ProxyStatusApiClient<InterceptedChannel>;
type GeneratedProverClient = generated::remote_prover::api_client::ApiClient<InterceptedChannel>;
type GeneratedValidatorClient = generated::validator::api_client::ApiClient<InterceptedChannel>;
type GeneratedNtxBuilderClient = generated::ntx_builder::api_client::ApiClient<InterceptedChannel>;
#[derive(Debug, Clone)]
pub struct RpcClient(GeneratedRpcClient);
#[derive(Debug, Clone)]
pub struct BlockProducerClient(GeneratedBlockProducerClient);
#[derive(Debug, Clone)]
pub struct StoreNtxBuilderClient(GeneratedStoreClientForNtxBuilder);
#[derive(Debug, Clone)]
pub struct StoreBlockProducerClient(GeneratedStoreClientForBlockProducer);
#[derive(Debug, Clone)]
pub struct StoreRpcClient(GeneratedStoreClientForRpc);
#[derive(Debug, Clone)]
pub struct RemoteProverProxyStatusClient(GeneratedProxyStatusClient);
#[derive(Debug, Clone)]
pub struct RemoteProverClient(GeneratedProverClient);
#[derive(Debug, Clone)]
pub struct ValidatorClient(GeneratedValidatorClient);
#[derive(Debug, Clone)]
pub struct NtxBuilderClient(GeneratedNtxBuilderClient);
impl DerefMut for RpcClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for RpcClient {
type Target = GeneratedRpcClient;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for BlockProducerClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for BlockProducerClient {
type Target = GeneratedBlockProducerClient;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for StoreNtxBuilderClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for StoreNtxBuilderClient {
type Target = GeneratedStoreClientForNtxBuilder;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for StoreBlockProducerClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for StoreBlockProducerClient {
type Target = GeneratedStoreClientForBlockProducer;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for StoreRpcClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for StoreRpcClient {
type Target = GeneratedStoreClientForRpc;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for RemoteProverProxyStatusClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for RemoteProverProxyStatusClient {
type Target = GeneratedProxyStatusClient;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for RemoteProverClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for RemoteProverClient {
type Target = GeneratedProverClient;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ValidatorClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for ValidatorClient {
type Target = GeneratedValidatorClient;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for NtxBuilderClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Deref for NtxBuilderClient {
type Target = GeneratedNtxBuilderClient;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait GrpcClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self;
}
impl GrpcClient for RpcClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedRpcClient::new(InterceptedService::new(channel, interceptor)))
}
}
impl GrpcClient for BlockProducerClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedBlockProducerClient::new(InterceptedService::new(channel, interceptor)))
}
}
impl GrpcClient for StoreNtxBuilderClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedStoreClientForNtxBuilder::new(InterceptedService::new(
channel,
interceptor,
)))
}
}
impl GrpcClient for StoreBlockProducerClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedStoreClientForBlockProducer::new(InterceptedService::new(
channel,
interceptor,
)))
}
}
impl GrpcClient for StoreRpcClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedStoreClientForRpc::new(InterceptedService::new(channel, interceptor)))
}
}
impl GrpcClient for RemoteProverProxyStatusClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedProxyStatusClient::new(InterceptedService::new(channel, interceptor)))
}
}
impl GrpcClient for RemoteProverClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedProverClient::new(InterceptedService::new(channel, interceptor)))
}
}
impl GrpcClient for ValidatorClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedValidatorClient::new(InterceptedService::new(channel, interceptor)))
}
}
impl GrpcClient for NtxBuilderClient {
fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
Self(GeneratedNtxBuilderClient::new(InterceptedService::new(channel, interceptor)))
}
}
#[derive(Clone, Debug)]
pub struct Builder<State> {
endpoint: Endpoint,
metadata_version: Option<String>,
metadata_genesis: Option<String>,
enable_otel: bool,
_state: PhantomData<State>,
}
#[derive(Copy, Clone, Debug)]
pub struct WantsTls;
#[derive(Copy, Clone, Debug)]
pub struct WantsTimeout;
#[derive(Copy, Clone, Debug)]
pub struct WantsVersion;
#[derive(Copy, Clone, Debug)]
pub struct WantsGenesis;
#[derive(Copy, Clone, Debug)]
pub struct WantsOTel;
#[derive(Copy, Clone, Debug)]
pub struct WantsConnection;
impl<State> Builder<State> {
fn next_state<Next>(self) -> Builder<Next> {
Builder {
endpoint: self.endpoint,
metadata_version: self.metadata_version,
metadata_genesis: self.metadata_genesis,
enable_otel: self.enable_otel,
_state: PhantomData::<Next>,
}
}
}
impl Builder<WantsTls> {
pub fn new(url: Url) -> Builder<WantsTls> {
let endpoint = Endpoint::from_shared(String::from(url))
.expect("Url type always results in valid endpoint");
Builder {
endpoint,
metadata_version: None,
metadata_genesis: None,
enable_otel: false,
_state: PhantomData,
}
}
pub fn without_tls(self) -> Builder<WantsTimeout> {
self.next_state()
}
pub fn with_tls(mut self) -> Result<Builder<WantsTimeout>> {
self.endpoint = self
.endpoint
.tls_config(ClientTlsConfig::new().with_native_roots())
.context("Failed to configure TLS")?;
Ok(self.next_state())
}
}
impl Builder<WantsTimeout> {
pub fn without_timeout(self) -> Builder<WantsVersion> {
self.next_state()
}
pub fn with_timeout(mut self, duration: Duration) -> Builder<WantsVersion> {
self.endpoint = self.endpoint.timeout(duration);
self.next_state()
}
}
impl Builder<WantsVersion> {
pub fn without_metadata_version(mut self) -> Builder<WantsGenesis> {
self.metadata_version = None;
self.next_state()
}
pub fn with_metadata_version(mut self, version: String) -> Builder<WantsGenesis> {
self.metadata_version = Some(version);
self.next_state()
}
}
impl Builder<WantsGenesis> {
pub fn without_metadata_genesis(mut self) -> Builder<WantsOTel> {
self.metadata_genesis = None;
self.next_state()
}
pub fn with_metadata_genesis(mut self, genesis: String) -> Builder<WantsOTel> {
self.metadata_genesis = Some(genesis);
self.next_state()
}
}
impl Builder<WantsOTel> {
pub fn with_otel_context_injection(mut self) -> Builder<WantsConnection> {
self.enable_otel = true;
self.next_state()
}
pub fn without_otel_context_injection(mut self) -> Builder<WantsConnection> {
self.enable_otel = false;
self.next_state()
}
}
impl Builder<WantsConnection> {
pub async fn connect<T>(self) -> Result<T>
where
T: GrpcClient,
{
let channel = self.endpoint.connect().await?;
Ok(self.connect_with_channel::<T>(channel))
}
pub fn connect_lazy<T>(self) -> T
where
T: GrpcClient,
{
let channel = self.endpoint.connect_lazy();
self.connect_with_channel::<T>(channel)
}
fn connect_with_channel<T>(self, channel: Channel) -> T
where
T: GrpcClient,
{
let interceptor = Interceptor::new(
self.enable_otel,
self.metadata_version.as_deref(),
self.metadata_genesis.as_deref(),
);
T::with_interceptor(channel, interceptor)
}
}