miden_node_proto/clients/
mod.rs1use std::marker::PhantomData;
28use std::ops::{Deref, DerefMut};
29use std::str::FromStr;
30use std::time::Duration;
31
32use anyhow::{Context, Result};
33use http::header::ACCEPT;
34use miden_node_utils::tracing::grpc::OtelInterceptor;
35use tonic::metadata::AsciiMetadataValue;
36use tonic::service::interceptor::InterceptedService;
37use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
38use tonic::{Request, Status};
39use url::Url;
40
41use crate::generated;
42
43#[derive(Clone)]
44pub struct Interceptor {
45 otel: Option<OtelInterceptor>,
46 accept: AsciiMetadataValue,
47}
48
49impl Default for Interceptor {
50 fn default() -> Self {
51 Self {
52 otel: None,
53 accept: AsciiMetadataValue::from_static(Self::MEDIA_TYPE),
54 }
55 }
56}
57
58impl Interceptor {
59 const MEDIA_TYPE: &str = "application/vnd.miden";
60 const VERSION: &str = "version";
61 const GENESIS: &str = "genesis";
62
63 fn new(enable_otel: bool, version: Option<&str>, genesis: Option<&str>) -> Self {
64 if let Some(version) = version
65 && !version.is_ascii()
66 {
67 panic!("version contains non-ascii values: {version}");
68 }
69
70 if let Some(genesis) = genesis
71 && !genesis.is_ascii()
72 {
73 panic!("genesis contains non-ascii values: {genesis}");
74 }
75
76 let accept = match (version, genesis) {
77 (None, None) => Self::MEDIA_TYPE.to_string(),
78 (None, Some(genesis)) => format!("{}; {}={genesis}", Self::MEDIA_TYPE, Self::GENESIS),
79 (Some(version), None) => format!("{}; {}={version}", Self::MEDIA_TYPE, Self::VERSION),
80 (Some(version), Some(genesis)) => format!(
81 "{}; {}={version}, {}={genesis}",
82 Self::MEDIA_TYPE,
83 Self::VERSION,
84 Self::GENESIS
85 ),
86 };
87 Self {
88 otel: enable_otel.then_some(OtelInterceptor),
89 accept: AsciiMetadataValue::from_str(&accept).unwrap(),
91 }
92 }
93}
94
95impl tonic::service::Interceptor for Interceptor {
96 fn call(&mut self, mut request: tonic::Request<()>) -> Result<Request<()>, Status> {
97 if let Some(mut otel) = self.otel {
98 request = otel.call(request)?;
99 }
100
101 request.metadata_mut().insert(ACCEPT.as_str(), self.accept.clone());
102
103 Ok(request)
104 }
105}
106
107type InterceptedChannel = InterceptedService<Channel, Interceptor>;
111type GeneratedRpcClient = generated::rpc::api_client::ApiClient<InterceptedChannel>;
112type GeneratedBlockProducerClient =
113 generated::block_producer::api_client::ApiClient<InterceptedChannel>;
114type GeneratedStoreClientForNtxBuilder =
115 generated::store::ntx_builder_client::NtxBuilderClient<InterceptedChannel>;
116type GeneratedStoreClientForBlockProducer =
117 generated::store::block_producer_client::BlockProducerClient<InterceptedChannel>;
118type GeneratedStoreClientForRpc = generated::store::rpc_client::RpcClient<InterceptedChannel>;
119type GeneratedProxyStatusClient =
120 generated::remote_prover::proxy_status_api_client::ProxyStatusApiClient<InterceptedChannel>;
121type GeneratedProverClient = generated::remote_prover::api_client::ApiClient<InterceptedChannel>;
122type GeneratedValidatorClient = generated::validator::api_client::ApiClient<InterceptedChannel>;
123
124#[derive(Debug, Clone)]
128pub struct RpcClient(GeneratedRpcClient);
129#[derive(Debug, Clone)]
130pub struct BlockProducerClient(GeneratedBlockProducerClient);
131#[derive(Debug, Clone)]
132pub struct StoreNtxBuilderClient(GeneratedStoreClientForNtxBuilder);
133#[derive(Debug, Clone)]
134pub struct StoreBlockProducerClient(GeneratedStoreClientForBlockProducer);
135#[derive(Debug, Clone)]
136pub struct StoreRpcClient(GeneratedStoreClientForRpc);
137#[derive(Debug, Clone)]
138pub struct RemoteProverProxyStatusClient(GeneratedProxyStatusClient);
139#[derive(Debug, Clone)]
140pub struct RemoteProverClient(GeneratedProverClient);
141#[derive(Debug, Clone)]
142pub struct ValidatorClient(GeneratedValidatorClient);
143
144impl DerefMut for RpcClient {
145 fn deref_mut(&mut self) -> &mut Self::Target {
146 &mut self.0
147 }
148}
149
150impl Deref for RpcClient {
151 type Target = GeneratedRpcClient;
152
153 fn deref(&self) -> &Self::Target {
154 &self.0
155 }
156}
157
158impl DerefMut for BlockProducerClient {
159 fn deref_mut(&mut self) -> &mut Self::Target {
160 &mut self.0
161 }
162}
163
164impl Deref for BlockProducerClient {
165 type Target = GeneratedBlockProducerClient;
166
167 fn deref(&self) -> &Self::Target {
168 &self.0
169 }
170}
171
172impl DerefMut for StoreNtxBuilderClient {
173 fn deref_mut(&mut self) -> &mut Self::Target {
174 &mut self.0
175 }
176}
177
178impl Deref for StoreNtxBuilderClient {
179 type Target = GeneratedStoreClientForNtxBuilder;
180
181 fn deref(&self) -> &Self::Target {
182 &self.0
183 }
184}
185
186impl DerefMut for StoreBlockProducerClient {
187 fn deref_mut(&mut self) -> &mut Self::Target {
188 &mut self.0
189 }
190}
191
192impl Deref for StoreBlockProducerClient {
193 type Target = GeneratedStoreClientForBlockProducer;
194
195 fn deref(&self) -> &Self::Target {
196 &self.0
197 }
198}
199
200impl DerefMut for StoreRpcClient {
201 fn deref_mut(&mut self) -> &mut Self::Target {
202 &mut self.0
203 }
204}
205
206impl Deref for StoreRpcClient {
207 type Target = GeneratedStoreClientForRpc;
208
209 fn deref(&self) -> &Self::Target {
210 &self.0
211 }
212}
213
214impl DerefMut for RemoteProverProxyStatusClient {
215 fn deref_mut(&mut self) -> &mut Self::Target {
216 &mut self.0
217 }
218}
219
220impl Deref for RemoteProverProxyStatusClient {
221 type Target = GeneratedProxyStatusClient;
222
223 fn deref(&self) -> &Self::Target {
224 &self.0
225 }
226}
227
228impl DerefMut for RemoteProverClient {
229 fn deref_mut(&mut self) -> &mut Self::Target {
230 &mut self.0
231 }
232}
233
234impl Deref for RemoteProverClient {
235 type Target = GeneratedProverClient;
236
237 fn deref(&self) -> &Self::Target {
238 &self.0
239 }
240}
241
242impl DerefMut for ValidatorClient {
243 fn deref_mut(&mut self) -> &mut Self::Target {
244 &mut self.0
245 }
246}
247
248impl Deref for ValidatorClient {
249 type Target = GeneratedValidatorClient;
250
251 fn deref(&self) -> &Self::Target {
252 &self.0
253 }
254}
255
256pub trait GrpcClient {
261 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self;
262}
263
264impl GrpcClient for RpcClient {
265 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
266 Self(GeneratedRpcClient::new(InterceptedService::new(channel, interceptor)))
267 }
268}
269
270impl GrpcClient for BlockProducerClient {
271 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
272 Self(GeneratedBlockProducerClient::new(InterceptedService::new(channel, interceptor)))
273 }
274}
275
276impl GrpcClient for StoreNtxBuilderClient {
277 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
278 Self(GeneratedStoreClientForNtxBuilder::new(InterceptedService::new(
279 channel,
280 interceptor,
281 )))
282 }
283}
284
285impl GrpcClient for StoreBlockProducerClient {
286 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
287 Self(GeneratedStoreClientForBlockProducer::new(InterceptedService::new(
288 channel,
289 interceptor,
290 )))
291 }
292}
293
294impl GrpcClient for StoreRpcClient {
295 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
296 Self(GeneratedStoreClientForRpc::new(InterceptedService::new(channel, interceptor)))
297 }
298}
299
300impl GrpcClient for RemoteProverProxyStatusClient {
301 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
302 Self(GeneratedProxyStatusClient::new(InterceptedService::new(channel, interceptor)))
303 }
304}
305
306impl GrpcClient for RemoteProverClient {
307 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
308 Self(GeneratedProverClient::new(InterceptedService::new(channel, interceptor)))
309 }
310}
311
312impl GrpcClient for ValidatorClient {
313 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
314 Self(GeneratedValidatorClient::new(InterceptedService::new(channel, interceptor)))
315 }
316}
317
318#[derive(Clone, Debug)]
348pub struct Builder<State> {
349 endpoint: Endpoint,
350 metadata_version: Option<String>,
351 metadata_genesis: Option<String>,
352 enable_otel: bool,
353 _state: PhantomData<State>,
354}
355
356#[derive(Copy, Clone, Debug)]
357pub struct WantsTls;
358#[derive(Copy, Clone, Debug)]
359pub struct WantsTimeout;
360#[derive(Copy, Clone, Debug)]
361pub struct WantsVersion;
362#[derive(Copy, Clone, Debug)]
363pub struct WantsGenesis;
364#[derive(Copy, Clone, Debug)]
365pub struct WantsOTel;
366#[derive(Copy, Clone, Debug)]
367pub struct WantsConnection;
368
369impl<State> Builder<State> {
370 fn next_state<Next>(self) -> Builder<Next> {
372 Builder {
373 endpoint: self.endpoint,
374 metadata_version: self.metadata_version,
375 metadata_genesis: self.metadata_genesis,
376 enable_otel: self.enable_otel,
377 _state: PhantomData::<Next>,
378 }
379 }
380}
381
382impl Builder<WantsTls> {
383 pub fn new(url: Url) -> Builder<WantsTls> {
386 let endpoint = Endpoint::from_shared(String::from(url))
387 .expect("Url type always results in valid endpoint");
388
389 Builder {
390 endpoint,
391 metadata_version: None,
392 metadata_genesis: None,
393 enable_otel: false,
394 _state: PhantomData,
395 }
396 }
397
398 pub fn without_tls(self) -> Builder<WantsTimeout> {
400 self.next_state()
401 }
402
403 pub fn with_tls(mut self) -> Result<Builder<WantsTimeout>> {
405 self.endpoint = self
406 .endpoint
407 .tls_config(ClientTlsConfig::new().with_native_roots())
408 .context("Failed to configure TLS")?;
409
410 Ok(self.next_state())
411 }
412}
413
414impl Builder<WantsTimeout> {
415 pub fn without_timeout(self) -> Builder<WantsVersion> {
417 self.next_state()
418 }
419
420 pub fn with_timeout(mut self, duration: Duration) -> Builder<WantsVersion> {
422 self.endpoint = self.endpoint.timeout(duration);
423 self.next_state()
424 }
425}
426
427impl Builder<WantsVersion> {
428 pub fn without_metadata_version(mut self) -> Builder<WantsGenesis> {
430 self.metadata_version = None;
431 self.next_state()
432 }
433
434 pub fn with_metadata_version(mut self, version: String) -> Builder<WantsGenesis> {
436 self.metadata_version = Some(version);
437 self.next_state()
438 }
439}
440
441impl Builder<WantsGenesis> {
442 pub fn without_metadata_genesis(mut self) -> Builder<WantsOTel> {
444 self.metadata_genesis = None;
445 self.next_state()
446 }
447
448 pub fn with_metadata_genesis(mut self, genesis: String) -> Builder<WantsOTel> {
450 self.metadata_genesis = Some(genesis);
451 self.next_state()
452 }
453}
454
455impl Builder<WantsOTel> {
456 pub fn with_otel_context_injection(mut self) -> Builder<WantsConnection> {
461 self.enable_otel = true;
462 self.next_state()
463 }
464
465 pub fn without_otel_context_injection(mut self) -> Builder<WantsConnection> {
468 self.enable_otel = false;
469 self.next_state()
470 }
471}
472
473impl Builder<WantsConnection> {
474 pub async fn connect<T>(self) -> Result<T>
476 where
477 T: GrpcClient,
478 {
479 let channel = self.endpoint.connect().await?;
480 Ok(self.connect_with_channel::<T>(channel))
481 }
482
483 pub fn connect_lazy<T>(self) -> T
485 where
486 T: GrpcClient,
487 {
488 let channel = self.endpoint.connect_lazy();
489 self.connect_with_channel::<T>(channel)
490 }
491
492 fn connect_with_channel<T>(self, channel: Channel) -> T
493 where
494 T: GrpcClient,
495 {
496 let interceptor = Interceptor::new(
497 self.enable_otel,
498 self.metadata_version.as_deref(),
499 self.metadata_genesis.as_deref(),
500 );
501 T::with_interceptor(channel, interceptor)
502 }
503}