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>;
123type GeneratedNtxBuilderClient = generated::ntx_builder::api_client::ApiClient<InterceptedChannel>;
124
125#[derive(Debug, Clone)]
129pub struct RpcClient(GeneratedRpcClient);
130#[derive(Debug, Clone)]
131pub struct BlockProducerClient(GeneratedBlockProducerClient);
132#[derive(Debug, Clone)]
133pub struct StoreNtxBuilderClient(GeneratedStoreClientForNtxBuilder);
134#[derive(Debug, Clone)]
135pub struct StoreBlockProducerClient(GeneratedStoreClientForBlockProducer);
136#[derive(Debug, Clone)]
137pub struct StoreRpcClient(GeneratedStoreClientForRpc);
138#[derive(Debug, Clone)]
139pub struct RemoteProverProxyStatusClient(GeneratedProxyStatusClient);
140#[derive(Debug, Clone)]
141pub struct RemoteProverClient(GeneratedProverClient);
142#[derive(Debug, Clone)]
143pub struct ValidatorClient(GeneratedValidatorClient);
144#[derive(Debug, Clone)]
145pub struct NtxBuilderClient(GeneratedNtxBuilderClient);
146
147impl DerefMut for RpcClient {
148 fn deref_mut(&mut self) -> &mut Self::Target {
149 &mut self.0
150 }
151}
152
153impl Deref for RpcClient {
154 type Target = GeneratedRpcClient;
155
156 fn deref(&self) -> &Self::Target {
157 &self.0
158 }
159}
160
161impl DerefMut for BlockProducerClient {
162 fn deref_mut(&mut self) -> &mut Self::Target {
163 &mut self.0
164 }
165}
166
167impl Deref for BlockProducerClient {
168 type Target = GeneratedBlockProducerClient;
169
170 fn deref(&self) -> &Self::Target {
171 &self.0
172 }
173}
174
175impl DerefMut for StoreNtxBuilderClient {
176 fn deref_mut(&mut self) -> &mut Self::Target {
177 &mut self.0
178 }
179}
180
181impl Deref for StoreNtxBuilderClient {
182 type Target = GeneratedStoreClientForNtxBuilder;
183
184 fn deref(&self) -> &Self::Target {
185 &self.0
186 }
187}
188
189impl DerefMut for StoreBlockProducerClient {
190 fn deref_mut(&mut self) -> &mut Self::Target {
191 &mut self.0
192 }
193}
194
195impl Deref for StoreBlockProducerClient {
196 type Target = GeneratedStoreClientForBlockProducer;
197
198 fn deref(&self) -> &Self::Target {
199 &self.0
200 }
201}
202
203impl DerefMut for StoreRpcClient {
204 fn deref_mut(&mut self) -> &mut Self::Target {
205 &mut self.0
206 }
207}
208
209impl Deref for StoreRpcClient {
210 type Target = GeneratedStoreClientForRpc;
211
212 fn deref(&self) -> &Self::Target {
213 &self.0
214 }
215}
216
217impl DerefMut for RemoteProverProxyStatusClient {
218 fn deref_mut(&mut self) -> &mut Self::Target {
219 &mut self.0
220 }
221}
222
223impl Deref for RemoteProverProxyStatusClient {
224 type Target = GeneratedProxyStatusClient;
225
226 fn deref(&self) -> &Self::Target {
227 &self.0
228 }
229}
230
231impl DerefMut for RemoteProverClient {
232 fn deref_mut(&mut self) -> &mut Self::Target {
233 &mut self.0
234 }
235}
236
237impl Deref for RemoteProverClient {
238 type Target = GeneratedProverClient;
239
240 fn deref(&self) -> &Self::Target {
241 &self.0
242 }
243}
244
245impl DerefMut for ValidatorClient {
246 fn deref_mut(&mut self) -> &mut Self::Target {
247 &mut self.0
248 }
249}
250
251impl Deref for ValidatorClient {
252 type Target = GeneratedValidatorClient;
253
254 fn deref(&self) -> &Self::Target {
255 &self.0
256 }
257}
258
259impl DerefMut for NtxBuilderClient {
260 fn deref_mut(&mut self) -> &mut Self::Target {
261 &mut self.0
262 }
263}
264
265impl Deref for NtxBuilderClient {
266 type Target = GeneratedNtxBuilderClient;
267
268 fn deref(&self) -> &Self::Target {
269 &self.0
270 }
271}
272
273pub trait GrpcClient {
278 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self;
279}
280
281impl GrpcClient for RpcClient {
282 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
283 Self(GeneratedRpcClient::new(InterceptedService::new(channel, interceptor)))
284 }
285}
286
287impl GrpcClient for BlockProducerClient {
288 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
289 Self(GeneratedBlockProducerClient::new(InterceptedService::new(channel, interceptor)))
290 }
291}
292
293impl GrpcClient for StoreNtxBuilderClient {
294 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
295 Self(GeneratedStoreClientForNtxBuilder::new(InterceptedService::new(
296 channel,
297 interceptor,
298 )))
299 }
300}
301
302impl GrpcClient for StoreBlockProducerClient {
303 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
304 Self(GeneratedStoreClientForBlockProducer::new(InterceptedService::new(
305 channel,
306 interceptor,
307 )))
308 }
309}
310
311impl GrpcClient for StoreRpcClient {
312 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
313 Self(GeneratedStoreClientForRpc::new(InterceptedService::new(channel, interceptor)))
314 }
315}
316
317impl GrpcClient for RemoteProverProxyStatusClient {
318 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
319 Self(GeneratedProxyStatusClient::new(InterceptedService::new(channel, interceptor)))
320 }
321}
322
323impl GrpcClient for RemoteProverClient {
324 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
325 Self(GeneratedProverClient::new(InterceptedService::new(channel, interceptor)))
326 }
327}
328
329impl GrpcClient for ValidatorClient {
330 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
331 Self(GeneratedValidatorClient::new(InterceptedService::new(channel, interceptor)))
332 }
333}
334
335impl GrpcClient for NtxBuilderClient {
336 fn with_interceptor(channel: Channel, interceptor: Interceptor) -> Self {
337 Self(GeneratedNtxBuilderClient::new(InterceptedService::new(channel, interceptor)))
338 }
339}
340
341#[derive(Clone, Debug)]
371pub struct Builder<State> {
372 endpoint: Endpoint,
373 metadata_version: Option<String>,
374 metadata_genesis: Option<String>,
375 enable_otel: bool,
376 _state: PhantomData<State>,
377}
378
379#[derive(Copy, Clone, Debug)]
380pub struct WantsTls;
381#[derive(Copy, Clone, Debug)]
382pub struct WantsTimeout;
383#[derive(Copy, Clone, Debug)]
384pub struct WantsVersion;
385#[derive(Copy, Clone, Debug)]
386pub struct WantsGenesis;
387#[derive(Copy, Clone, Debug)]
388pub struct WantsOTel;
389#[derive(Copy, Clone, Debug)]
390pub struct WantsConnection;
391
392impl<State> Builder<State> {
393 fn next_state<Next>(self) -> Builder<Next> {
395 Builder {
396 endpoint: self.endpoint,
397 metadata_version: self.metadata_version,
398 metadata_genesis: self.metadata_genesis,
399 enable_otel: self.enable_otel,
400 _state: PhantomData::<Next>,
401 }
402 }
403}
404
405impl Builder<WantsTls> {
406 pub fn new(url: Url) -> Builder<WantsTls> {
409 let endpoint = Endpoint::from_shared(String::from(url))
410 .expect("Url type always results in valid endpoint");
411
412 Builder {
413 endpoint,
414 metadata_version: None,
415 metadata_genesis: None,
416 enable_otel: false,
417 _state: PhantomData,
418 }
419 }
420
421 pub fn without_tls(self) -> Builder<WantsTimeout> {
423 self.next_state()
424 }
425
426 pub fn with_tls(mut self) -> Result<Builder<WantsTimeout>> {
428 self.endpoint = self
429 .endpoint
430 .tls_config(ClientTlsConfig::new().with_native_roots())
431 .context("Failed to configure TLS")?;
432
433 Ok(self.next_state())
434 }
435}
436
437impl Builder<WantsTimeout> {
438 pub fn without_timeout(self) -> Builder<WantsVersion> {
440 self.next_state()
441 }
442
443 pub fn with_timeout(mut self, duration: Duration) -> Builder<WantsVersion> {
445 self.endpoint = self.endpoint.timeout(duration);
446 self.next_state()
447 }
448}
449
450impl Builder<WantsVersion> {
451 pub fn without_metadata_version(mut self) -> Builder<WantsGenesis> {
453 self.metadata_version = None;
454 self.next_state()
455 }
456
457 pub fn with_metadata_version(mut self, version: String) -> Builder<WantsGenesis> {
459 self.metadata_version = Some(version);
460 self.next_state()
461 }
462}
463
464impl Builder<WantsGenesis> {
465 pub fn without_metadata_genesis(mut self) -> Builder<WantsOTel> {
467 self.metadata_genesis = None;
468 self.next_state()
469 }
470
471 pub fn with_metadata_genesis(mut self, genesis: String) -> Builder<WantsOTel> {
473 self.metadata_genesis = Some(genesis);
474 self.next_state()
475 }
476}
477
478impl Builder<WantsOTel> {
479 pub fn with_otel_context_injection(mut self) -> Builder<WantsConnection> {
484 self.enable_otel = true;
485 self.next_state()
486 }
487
488 pub fn without_otel_context_injection(mut self) -> Builder<WantsConnection> {
491 self.enable_otel = false;
492 self.next_state()
493 }
494}
495
496impl Builder<WantsConnection> {
497 pub async fn connect<T>(self) -> Result<T>
499 where
500 T: GrpcClient,
501 {
502 let channel = self.endpoint.connect().await?;
503 Ok(self.connect_with_channel::<T>(channel))
504 }
505
506 pub fn connect_lazy<T>(self) -> T
508 where
509 T: GrpcClient,
510 {
511 let channel = self.endpoint.connect_lazy();
512 self.connect_with_channel::<T>(channel)
513 }
514
515 fn connect_with_channel<T>(self, channel: Channel) -> T
516 where
517 T: GrpcClient,
518 {
519 let interceptor = Interceptor::new(
520 self.enable_otel,
521 self.metadata_version.as_deref(),
522 self.metadata_genesis.as_deref(),
523 );
524 T::with_interceptor(channel, interceptor)
525 }
526}