Skip to main content

rs_dapi_client/transport/
grpc.rs

1//! Listing of gRPC requests used in DAPI.
2
3use std::time::Duration;
4
5use super::create_channel;
6use super::{CanRetry, TransportClient, TransportError, TransportRequest};
7use super::{CoreGrpcClient, PlatformGrpcClient};
8use crate::connection_pool::{ConnectionPool, PoolPrefix};
9use crate::{request_settings::AppliedRequestSettings, RequestSettings, Uri};
10use dapi_grpc::core::v0::{self as core_proto};
11use dapi_grpc::platform::v0::{self as platform_proto};
12use dapi_grpc::tonic::{IntoRequest, Streaming};
13use futures::{future::BoxFuture, FutureExt, TryFutureExt};
14
15impl TransportClient for PlatformGrpcClient {
16    fn with_uri(uri: Uri, pool: &ConnectionPool) -> Result<Self, TransportError> {
17        Ok(pool
18            .get_or_create(PoolPrefix::Platform, &uri, None, || {
19                match create_channel(uri.clone(), None) {
20                    Ok(channel) => Ok(Self::new(channel).into()),
21                    Err(e) => Err(dapi_grpc::tonic::Status::invalid_argument(format!(
22                        "channel creation failed: {}",
23                        e
24                    ))),
25                }
26            })?
27            .into())
28    }
29
30    fn with_uri_and_settings(
31        uri: Uri,
32        settings: &AppliedRequestSettings,
33        pool: &ConnectionPool,
34    ) -> Result<Self, TransportError> {
35        Self::with_uri_and_settings_and_generation(uri, settings, pool).map(|(client, _)| client)
36    }
37
38    fn with_uri_and_settings_and_generation(
39        uri: Uri,
40        settings: &AppliedRequestSettings,
41        pool: &ConnectionPool,
42    ) -> Result<(Self, u64), TransportError> {
43        let (item, generation) =
44            pool.get_or_create_with_generation(PoolPrefix::Platform, &uri, Some(settings), || {
45                match create_channel(uri.clone(), Some(settings)) {
46                    Ok(channel) => {
47                        let mut client = Self::new(channel);
48                        if let Some(max_size) = settings.max_decoding_message_size {
49                            client = client.max_decoding_message_size(max_size);
50                        }
51                        Ok(client.into())
52                    }
53                    Err(e) => Err(dapi_grpc::tonic::Status::invalid_argument(format!(
54                        "Channel creation failed: {}",
55                        e
56                    ))),
57                }
58            })?;
59        Ok((item.into(), generation))
60    }
61}
62
63impl TransportClient for CoreGrpcClient {
64    fn with_uri(uri: Uri, pool: &ConnectionPool) -> Result<Self, TransportError> {
65        Ok(pool
66            .get_or_create(PoolPrefix::Core, &uri, None, || {
67                match create_channel(uri.clone(), None) {
68                    Ok(channel) => Ok(Self::new(channel).into()),
69                    Err(e) => Err(dapi_grpc::tonic::Status::invalid_argument(format!(
70                        "Channel creation failed: {}",
71                        e
72                    ))),
73                }
74            })?
75            .into())
76    }
77
78    fn with_uri_and_settings(
79        uri: Uri,
80        settings: &AppliedRequestSettings,
81        pool: &ConnectionPool,
82    ) -> Result<Self, TransportError> {
83        Self::with_uri_and_settings_and_generation(uri, settings, pool).map(|(client, _)| client)
84    }
85
86    fn with_uri_and_settings_and_generation(
87        uri: Uri,
88        settings: &AppliedRequestSettings,
89        pool: &ConnectionPool,
90    ) -> Result<(Self, u64), TransportError> {
91        let (item, generation) =
92            pool.get_or_create_with_generation(PoolPrefix::Core, &uri, Some(settings), || {
93                match create_channel(uri.clone(), Some(settings)) {
94                    Ok(channel) => {
95                        let mut client = Self::new(channel);
96                        if let Some(max_size) = settings.max_decoding_message_size {
97                            client = client.max_decoding_message_size(max_size);
98                        }
99                        Ok(client.into())
100                    }
101                    Err(e) => Err(dapi_grpc::tonic::Status::invalid_argument(format!(
102                        "Channel creation failed: {}",
103                        e
104                    ))),
105                }
106            })?;
107        Ok((item.into(), generation))
108    }
109}
110
111/// Text a node released before request errors were answered with INVALID_ARGUMENT puts in an
112/// UNKNOWN or INTERNAL status when the request itself is wrong. Every node refuses such a
113/// request the same way, so sending it to the next node only gets that one banned too.
114const OLDER_NODE_REQUEST_REFUSALS: [&str; 3] = [
115    // A Drive query syntax error the handler returned as an error: INTERNAL.
116    "storage: query: ",
117    // A Drive query syntax error the handler reported: UNKNOWN.
118    "drive error: query: ",
119    // GroveDB declining to prove a query that asks for nothing, such as a limit of 0.
120    "proved path queries can not be for limit 0",
121];
122
123impl CanRetry for dapi_grpc::tonic::Status {
124    fn can_retry(&self) -> bool {
125        let code = self.code();
126
127        use dapi_grpc::tonic::Code::*;
128
129        if matches!(code, Unknown | Internal)
130            && OLDER_NODE_REQUEST_REFUSALS
131                .iter()
132                .any(|refusal| self.message().contains(refusal))
133        {
134            return false;
135        }
136
137        matches!(
138            code,
139            Ok | DataLoss
140                | Cancelled
141                | Unknown
142                | DeadlineExceeded
143                | ResourceExhausted
144                | Aborted
145                | Internal
146                | Unavailable
147                // During a mixed-version network rollout, Unimplemented means this
148                // particular node runs an older build that doesn't expose the method
149                // yet; another node may serve it. Marking it retryable makes the
150                // executor ban the node (see `update_address_ban_status`) and retry
151                // elsewhere.
152                //
153                // When NO node implements the method, how the failure surfaces depends
154                // on which of `DapiClient::execute`'s two caps trips first:
155                //   * `live_addresses <= settings.retries` — the address list is
156                //     exhausted before the retry budget, so the error surfaces as the
157                //     non-retryable `NoAvailableAddressesToRetry`.
158                //   * `live_addresses > settings.retries` — the per-call retry budget
159                //     trips first, banning `settings.retries + 1` nodes and surfacing
160                //     the raw (still-retryable) `Unimplemented`. A caller that honors
161                //     `CanRetry` then re-enters and bans more nodes each round,
162                //     converging on the exhausted-addresses case above; rs-sdk also
163                //     bounds this via its own `total_retries` cap (see
164                //     `rs-sdk/src/sync.rs`). Either way the loop is bounded — it never
165                //     retries the same node forever.
166                //
167                // Caveat: bans are address-scoped, not (address, method)-scoped, and
168                // Unimplemented is also returned deliberately for genuinely unsupported
169                // / feature-gated endpoints (e.g. rs-drive-abci's
170                // broadcast_state_transition / wait_for_state_transition_result /
171                // get_consensus_params, and any rs-dapi `MethodNotFound`). Calling such
172                // an endpoint bans an otherwise-healthy node across ALL methods for the
173                // ban window. Acceptable for the targeted rollout case; per-method ban
174                // granularity is left as a follow-up so optional endpoints can't poison
175                // the shared address list. TODO(per-method-ban).
176                | Unimplemented
177        )
178    }
179
180    /// Returns the Envoy-advertised ban duration for a `ResourceExhausted`
181    /// response, or `None` if this is not a rate-limit or carries no usable
182    /// `RateLimit-Reset` header.
183    ///
184    /// Envoy's global rate-limit filter emits `RateLimit-Reset: <seconds>` when
185    /// `LIMIT_RESPONSE_HEADERS_ENABLED=true` is set on the Lyft RLS container
186    /// (see `packages/dashmate/docker-compose.rate_limiter.yml`).  The value is
187    /// the whole-second count until the per-IP window resets.
188    ///
189    /// Parse rules (adversarial-input safe):
190    /// * Non-`ResourceExhausted` code → `None`.
191    /// * Header absent, non-numeric, or `0` → `None` (caller uses normal ban
192    ///   ladder).
193    /// * Valid positive integer → clamped to
194    ///   [`[MIN_RATE_LIMIT_BAN_SECS, MAX_RATE_LIMIT_BAN_SECS]`]
195    ///   (`dapi_client.rs`) and returned as `Some(Duration)`.
196    fn rate_limit_ban_duration(&self) -> Option<std::time::Duration> {
197        use crate::dapi_client::{MAX_RATE_LIMIT_BAN_SECS, MIN_RATE_LIMIT_BAN_SECS};
198        use dapi_grpc::tonic::Code;
199        if self.code() != Code::ResourceExhausted {
200            return None;
201        }
202        let secs = self
203            .metadata()
204            .get("ratelimit-reset")
205            .and_then(|v| v.to_str().ok())
206            .and_then(|s| s.trim().parse::<u64>().ok())
207            .filter(|&s| s > 0)?;
208        Some(std::time::Duration::from_secs(
209            secs.clamp(MIN_RATE_LIMIT_BAN_SECS, MAX_RATE_LIMIT_BAN_SECS),
210        ))
211    }
212}
213
214/// Macro to implement the `TransportRequest` trait for a given request type, response type, client type, and settings.
215///
216/// # Parameters
217///
218/// - `$request:ty`: The request type for which the `TransportRequest` trait will be implemented.
219/// - `$response:ty`: The response type returned by the transport request.
220/// - `$client:ty`: The client type used to execute the transport request (eg. generated by `tonic` crate).
221/// - `$settings:expr`: The settings to be used for the transport request; these settings will override client's
222///   default settings, but can still be overridden by arguments to
223///   the [`DapiRequestExecutor::execute`](crate::DapiRequestExecutor::execute) method.
224/// - `$($method:tt)+`: The method of `$client` to be called to execute the request.
225///
226/// # Example
227///
228/// ```compile_fail
229/// impl_transport_request_grpc!(
230///     MyRequestType,
231///     MyResponseType,
232///     MyClientType,
233///     my_settings,
234///     my_method
235/// );
236/// ```
237///
238/// This will generate an implementation of the `TransportRequest` trait for `MyRequestType`
239/// that uses `MyClientType` to execute the `my_method` method, with the specified `my_settings`.
240///
241/// The generated implementation will:
242/// - Define the associated types `Client` and `Response`.
243/// - Set the `SETTINGS_OVERRIDES` constant to the provided settings.
244/// - Implement the `method_name` function to return the name of the method as a string.
245/// - Implement the `execute_transport` function to execute the transport request using the provided client and settings.
246macro_rules! impl_transport_request_grpc {
247    // `names_nothing: |request| ...;` gives the request's `TransportRequest::names_nothing`.
248    (names_nothing: $names_nothing:expr; $request:ty, $response:ty, $client:ty, $settings:expr, $($method:tt)+) => {
249        impl_transport_request_grpc!(@impl $names_nothing; $request, $response, $client, $settings, $($method)+);
250    };
251    ($request:ty, $response:ty, $client:ty, $settings:expr, $($method:tt)+) => {
252        impl_transport_request_grpc!(@impl |_| None; $request, $response, $client, $settings, $($method)+);
253    };
254    (@impl $names_nothing:expr; $request:ty, $response:ty, $client:ty, $settings:expr, $($method:tt)+) => {
255        impl TransportRequest for $request {
256            type Client = $client;
257
258            type Response = $response;
259
260            const SETTINGS_OVERRIDES: RequestSettings = $settings;
261
262            fn method_name(&self) -> &'static str {
263                stringify!($($method)+)
264            }
265
266            fn names_nothing(&self) -> Option<&'static str> {
267                let names_nothing: fn(&$request) -> Option<&'static str> = $names_nothing;
268                names_nothing(self)
269            }
270
271            fn execute_transport<'c>(
272                self,
273                client: &'c mut Self::Client,
274                settings: &AppliedRequestSettings,
275            ) -> BoxFuture<'c, Result<Self::Response, TransportError>>
276            {
277                let mut grpc_request = self.into_request();
278
279                if !settings.timeout.is_zero() {
280                    grpc_request.set_timeout(settings.timeout);
281                }
282
283                client
284                    .$($method)+(grpc_request)
285                    .map_err(TransportError::Grpc)
286                    .map_ok(|response| response.into_inner())
287                    .boxed()
288            }
289        }
290    };
291}
292
293// Link to each platform gRPC request what client and method to use:
294
295const STREAMING_TIMEOUT: Duration = Duration::from_secs(5 * 60);
296
297/// Attempt timeout for unary requests whose responses run to megabytes.
298///
299/// The timeout bounds the whole attempt including the response body (see
300/// `RequestSettings::timeout`), so the 10 s default would fail these responses
301/// on a slow link every time and ban the node that was sending them. Dead
302/// connections are still caught early by the channel's HTTP/2 keepalive.
303const LARGE_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
304
305impl_transport_request_grpc!(
306    platform_proto::GetIdentityRequest,
307    platform_proto::GetIdentityResponse,
308    PlatformGrpcClient,
309    RequestSettings::default(),
310    get_identity
311);
312
313impl_transport_request_grpc!(
314    platform_proto::GetDocumentsRequest,
315    platform_proto::GetDocumentsResponse,
316    PlatformGrpcClient,
317    RequestSettings::default(),
318    get_documents
319);
320
321impl_transport_request_grpc!(
322    platform_proto::GetDataContractRequest,
323    platform_proto::GetDataContractResponse,
324    PlatformGrpcClient,
325    RequestSettings::default(),
326    get_data_contract
327);
328
329// rpc getDataContractsByRange(GetDataContractsByRangeRequest) returns (GetDataContractsResponse);
330impl_transport_request_grpc!(
331    platform_proto::GetDataContractsByRangeRequest,
332    platform_proto::GetDataContractsResponse,
333    PlatformGrpcClient,
334    RequestSettings::default(),
335    get_data_contracts_by_range
336);
337
338// rpc getDataContractsLatestVersions(GetDataContractsLatestVersionsRequest) returns (GetDataContractsLatestVersionsResponse);
339impl_transport_request_grpc!(
340    platform_proto::GetDataContractsLatestVersionsRequest,
341    platform_proto::GetDataContractsLatestVersionsResponse,
342    PlatformGrpcClient,
343    RequestSettings::default(),
344    get_data_contracts_latest_versions
345);
346
347// rpc getContractGroupInfo(GetContractGroupInfoRequest) returns (GetContractGroupInfoResponse);
348impl_transport_request_grpc!(
349    platform_proto::GetContractGroupInfoRequest,
350    platform_proto::GetContractGroupInfoResponse,
351    PlatformGrpcClient,
352    RequestSettings::default(),
353    get_contract_group_info
354);
355
356// rpc getContractModerationStatus(GetContractModerationStatusRequest) returns (GetContractModerationStatusResponse);
357impl_transport_request_grpc!(
358    platform_proto::GetContractModerationStatusRequest,
359    platform_proto::GetContractModerationStatusResponse,
360    PlatformGrpcClient,
361    RequestSettings::default(),
362    get_contract_moderation_status
363);
364
365// rpc getContractModerationEntries(GetContractModerationEntriesRequest) returns (GetContractModerationEntriesResponse);
366impl_transport_request_grpc!(
367    platform_proto::GetContractModerationEntriesRequest,
368    platform_proto::GetContractModerationEntriesResponse,
369    PlatformGrpcClient,
370    RequestSettings::default(),
371    get_contract_moderation_entries
372);
373
374// rpc getContractDocumentRemovals(GetContractDocumentRemovalsRequest) returns (GetContractDocumentRemovalsResponse);
375impl_transport_request_grpc!(
376    platform_proto::GetContractDocumentRemovalsRequest,
377    platform_proto::GetContractDocumentRemovalsResponse,
378    PlatformGrpcClient,
379    RequestSettings::default(),
380    get_contract_document_removals
381);
382
383// rpc getContractTeamActions(GetContractTeamActionsRequest) returns (GetContractTeamActionsResponse);
384impl_transport_request_grpc!(
385    platform_proto::GetContractTeamActionsRequest,
386    platform_proto::GetContractTeamActionsResponse,
387    PlatformGrpcClient,
388    RequestSettings::default(),
389    get_contract_team_actions
390);
391
392// rpc getContractTeamActionSigners(GetContractTeamActionSignersRequest) returns (GetContractTeamActionSignersResponse);
393impl_transport_request_grpc!(
394    platform_proto::GetContractTeamActionSignersRequest,
395    platform_proto::GetContractTeamActionSignersResponse,
396    PlatformGrpcClient,
397    RequestSettings::default(),
398    get_contract_team_action_signers
399);
400
401// rpc getContractModerationActionCounts(GetContractModerationActionCountsRequest) returns (GetContractModerationActionCountsResponse);
402impl_transport_request_grpc!(
403    platform_proto::GetContractModerationActionCountsRequest,
404    platform_proto::GetContractModerationActionCountsResponse,
405    PlatformGrpcClient,
406    RequestSettings::default(),
407    get_contract_moderation_action_counts
408);
409
410// rpc getContractFeePots(GetContractFeePotsRequest) returns (GetContractFeePotsResponse);
411impl_transport_request_grpc!(
412    platform_proto::GetContractFeePotsRequest,
413    platform_proto::GetContractFeePotsResponse,
414    PlatformGrpcClient,
415    RequestSettings::default(),
416    get_contract_fee_pots
417);
418
419// rpc getContractGroupMembers(GetContractGroupMembersRequest) returns (GetContractGroupMembersResponse);
420impl_transport_request_grpc!(
421    platform_proto::GetContractGroupMembersRequest,
422    platform_proto::GetContractGroupMembersResponse,
423    PlatformGrpcClient,
424    RequestSettings::default(),
425    get_contract_group_members
426);
427
428// rpc getContractGroupsForContract(GetContractGroupsForContractRequest) returns (GetContractGroupsForContractResponse);
429impl_transport_request_grpc!(
430    platform_proto::GetContractGroupsForContractRequest,
431    platform_proto::GetContractGroupsForContractResponse,
432    PlatformGrpcClient,
433    RequestSettings::default(),
434    get_contract_groups_for_contract
435);
436
437impl_transport_request_grpc!(
438    platform_proto::GetConsensusParamsRequest,
439    platform_proto::GetConsensusParamsResponse,
440    PlatformGrpcClient,
441    RequestSettings::default(),
442    get_consensus_params
443);
444
445impl_transport_request_grpc!(
446    platform_proto::GetDataContractHistoryRequest,
447    platform_proto::GetDataContractHistoryResponse,
448    PlatformGrpcClient,
449    RequestSettings::default(),
450    get_data_contract_history
451);
452
453impl_transport_request_grpc!(
454    platform_proto::GetDocumentHistoryRequest,
455    platform_proto::GetDocumentHistoryResponse,
456    PlatformGrpcClient,
457    RequestSettings::default(),
458    get_document_history
459);
460
461impl_transport_request_grpc!(
462    platform_proto::BroadcastStateTransitionRequest,
463    platform_proto::BroadcastStateTransitionResponse,
464    PlatformGrpcClient,
465    RequestSettings::default(),
466    broadcast_state_transition
467);
468
469impl_transport_request_grpc!(
470    platform_proto::WaitForStateTransitionResultRequest,
471    platform_proto::WaitForStateTransitionResultResponse,
472    PlatformGrpcClient,
473    RequestSettings {
474        timeout: Some(Duration::from_secs(30)),
475        retries: Some(3),
476        ban_failed_address: None,
477        connect_timeout: None,
478        max_decoding_message_size: None,
479    },
480    wait_for_state_transition_result
481);
482
483impl_transport_request_grpc!(
484    platform_proto::GetIdentityByPublicKeyHashRequest,
485    platform_proto::GetIdentityByPublicKeyHashResponse,
486    PlatformGrpcClient,
487    RequestSettings::default(),
488    get_identity_by_public_key_hash
489);
490
491impl_transport_request_grpc!(
492    platform_proto::GetIdentityBalanceRequest,
493    platform_proto::GetIdentityBalanceResponse,
494    PlatformGrpcClient,
495    RequestSettings::default(),
496    get_identity_balance
497);
498
499impl_transport_request_grpc!(
500    names_nothing: |request| match &request.version {
501        Some(platform_proto::get_identities_balances_request::Version::V0(v0)) if v0.prove && v0.ids.is_empty() => Some(
502            "ids must contain at least one identifier when requesting a proof",
503        ),
504        _ => None,
505    };
506    platform_proto::GetIdentitiesBalancesRequest,
507    platform_proto::GetIdentitiesBalancesResponse,
508    PlatformGrpcClient,
509    RequestSettings::default(),
510    get_identities_balances
511);
512
513impl_transport_request_grpc!(
514    platform_proto::GetIdentityNonceRequest,
515    platform_proto::GetIdentityNonceResponse,
516    PlatformGrpcClient,
517    RequestSettings::default(),
518    get_identity_nonce
519);
520
521impl_transport_request_grpc!(
522    platform_proto::GetIdentityContractNonceRequest,
523    platform_proto::GetIdentityContractNonceResponse,
524    PlatformGrpcClient,
525    RequestSettings::default(),
526    get_identity_contract_nonce
527);
528
529impl_transport_request_grpc!(
530    platform_proto::GetIdentityKeysRemainingBudgetsRequest,
531    platform_proto::GetIdentityKeysRemainingBudgetsResponse,
532    PlatformGrpcClient,
533    RequestSettings::default(),
534    get_identity_keys_remaining_budgets
535);
536
537impl_transport_request_grpc!(
538    platform_proto::GetIdentityBalanceAndRevisionRequest,
539    platform_proto::GetIdentityBalanceAndRevisionResponse,
540    PlatformGrpcClient,
541    RequestSettings::default(),
542    get_identity_balance_and_revision
543);
544
545impl_transport_request_grpc!(
546    names_nothing: |request| match &request.version {
547        Some(platform_proto::get_identities_contract_keys_request::Version::V0(v0)) if v0.prove => {
548            if v0.identities_ids.is_empty() {
549                Some("identities_ids must contain at least one identifier when requesting a proof")
550            } else if v0.purposes.is_empty() {
551                Some("purposes must contain at least one purpose when requesting a proof")
552            } else {
553                None
554            }
555        }
556        _ => None,
557    };
558    platform_proto::GetIdentitiesContractKeysRequest,
559    platform_proto::GetIdentitiesContractKeysResponse,
560    PlatformGrpcClient,
561    RequestSettings::default(),
562    get_identities_contract_keys
563);
564
565impl_transport_request_grpc!(
566    platform_proto::GetIdentityKeysRequest,
567    platform_proto::GetIdentityKeysResponse,
568    PlatformGrpcClient,
569    RequestSettings::default(),
570    get_identity_keys
571);
572
573impl_transport_request_grpc!(
574    platform_proto::GetEpochsInfoRequest,
575    platform_proto::GetEpochsInfoResponse,
576    PlatformGrpcClient,
577    RequestSettings::default(),
578    get_epochs_info
579);
580
581impl_transport_request_grpc!(
582    platform_proto::GetFinalizedEpochInfosRequest,
583    platform_proto::GetFinalizedEpochInfosResponse,
584    PlatformGrpcClient,
585    RequestSettings::default(),
586    get_finalized_epoch_infos
587);
588
589impl_transport_request_grpc!(
590    platform_proto::GetProtocolVersionUpgradeStateRequest,
591    platform_proto::GetProtocolVersionUpgradeStateResponse,
592    PlatformGrpcClient,
593    RequestSettings::default(),
594    get_protocol_version_upgrade_state
595);
596
597impl_transport_request_grpc!(
598    platform_proto::GetProtocolVersionUpgradeVoteStatusRequest,
599    platform_proto::GetProtocolVersionUpgradeVoteStatusResponse,
600    PlatformGrpcClient,
601    RequestSettings::default(),
602    get_protocol_version_upgrade_vote_status
603);
604
605impl_transport_request_grpc!(
606    names_nothing: |request| match &request.version {
607        Some(platform_proto::get_data_contracts_request::Version::V0(v0)) if v0.prove && v0.ids.is_empty() => Some(
608            "ids must contain at least one identifier when requesting a proof",
609        ),
610        _ => None,
611    };
612    platform_proto::GetDataContractsRequest,
613    platform_proto::GetDataContractsResponse,
614    PlatformGrpcClient,
615    RequestSettings::default(),
616    get_data_contracts
617);
618
619// rpc getContestedResources(GetContestedResourcesRequest) returns (GetContestedResourcesResponse);
620impl_transport_request_grpc!(
621    platform_proto::GetContestedResourcesRequest,
622    platform_proto::GetContestedResourcesResponse,
623    PlatformGrpcClient,
624    RequestSettings::default(),
625    get_contested_resources
626);
627
628//  rpc getContestedResourceVoteState(GetContestedResourceVoteStateRequest) returns (GetContestedResourceVoteStateResponse);
629impl_transport_request_grpc!(
630    platform_proto::GetContestedResourceVoteStateRequest,
631    platform_proto::GetContestedResourceVoteStateResponse,
632    PlatformGrpcClient,
633    RequestSettings::default(),
634    get_contested_resource_vote_state
635);
636
637// rpc getContestedResourceVotersForIdentity(GetContestedResourceVotersForIdentityRequest) returns (GetContestedResourceVotersForIdentityResponse);
638impl_transport_request_grpc!(
639    platform_proto::GetContestedResourceVotersForIdentityRequest,
640    platform_proto::GetContestedResourceVotersForIdentityResponse,
641    PlatformGrpcClient,
642    RequestSettings::default(),
643    get_contested_resource_voters_for_identity
644);
645// rpc getContestedResourceIdentityVoteStatus(GetContestedResourceIdentityVoteStatusRequest) returns (GetContestedResourceIdentityVoteStatusResponse);
646impl_transport_request_grpc!(
647    platform_proto::GetContestedResourceIdentityVotesRequest,
648    platform_proto::GetContestedResourceIdentityVotesResponse,
649    PlatformGrpcClient,
650    RequestSettings::default(),
651    get_contested_resource_identity_votes
652);
653// rpc GetVotePollsByEndDateRequest(GetVotePollsByEndDateRequest) returns (GetVotePollsByEndDateResponse);
654impl_transport_request_grpc!(
655    platform_proto::GetVotePollsByEndDateRequest,
656    platform_proto::GetVotePollsByEndDateResponse,
657    PlatformGrpcClient,
658    RequestSettings::default(),
659    get_vote_polls_by_end_date
660);
661
662// rpc GetEvonodesProposedEpochBlocksByIdsRequest(GetEvonodesProposedEpochBlocksByIdsRequest) returns (GetEvonodesProposedEpochBlocksResponse);
663impl_transport_request_grpc!(
664    names_nothing: |request| match &request.version {
665        Some(platform_proto::get_evonodes_proposed_epoch_blocks_by_ids_request::Version::V0(v0)) if v0.prove && v0.ids.is_empty() => Some(
666            "ids must contain at least one identifier when requesting a proof",
667        ),
668        _ => None,
669    };
670    platform_proto::GetEvonodesProposedEpochBlocksByIdsRequest,
671    platform_proto::GetEvonodesProposedEpochBlocksResponse,
672    PlatformGrpcClient,
673    RequestSettings::default(),
674    get_evonodes_proposed_epoch_blocks_by_ids
675);
676
677// rpc GetEvonodesProposedEpochBlocksByRangeRequest(GetEvonodesProposedEpochBlocksByRangeRequest) returns (GetEvonodesProposedEpochBlocksResponse);
678impl_transport_request_grpc!(
679    platform_proto::GetEvonodesProposedEpochBlocksByRangeRequest,
680    platform_proto::GetEvonodesProposedEpochBlocksResponse,
681    PlatformGrpcClient,
682    RequestSettings::default(),
683    get_evonodes_proposed_epoch_blocks_by_range
684);
685
686// rpc getPrefundedSpecializedBalance(GetPrefundedSpecializedBalanceRequest) returns (GetPrefundedSpecializedBalanceResponse);
687impl_transport_request_grpc!(
688    platform_proto::GetPrefundedSpecializedBalanceRequest,
689    platform_proto::GetPrefundedSpecializedBalanceResponse,
690    PlatformGrpcClient,
691    RequestSettings::default(),
692    get_prefunded_specialized_balance
693);
694
695// rpc getPathElements(GetPathElementsRequest) returns (GetPathElementsResponse);
696impl_transport_request_grpc!(
697    platform_proto::GetPathElementsRequest,
698    platform_proto::GetPathElementsResponse,
699    PlatformGrpcClient,
700    RequestSettings::default(),
701    get_path_elements
702);
703
704// rpc getTotalCreditsInPlatform(GetTotalCreditsInPlatformRequest) returns (GetTotalCreditsInPlatformResponse);
705impl_transport_request_grpc!(
706    platform_proto::GetTotalCreditsInPlatformRequest,
707    platform_proto::GetTotalCreditsInPlatformResponse,
708    PlatformGrpcClient,
709    RequestSettings::default(),
710    get_total_credits_in_platform
711);
712
713// rpc getCurrentQuorumsInfo(GetCurrentQuorumsInfoRequest) returns (GetCurrentQuorumsInfoResponse);
714impl_transport_request_grpc!(
715    platform_proto::GetCurrentQuorumsInfoRequest,
716    platform_proto::GetCurrentQuorumsInfoResponse,
717    PlatformGrpcClient,
718    RequestSettings::default(),
719    get_current_quorums_info
720);
721
722// rpc getShieldedEncryptedNotes(GetShieldedEncryptedNotesRequest) returns (GetShieldedEncryptedNotesResponse);
723impl_transport_request_grpc!(
724    platform_proto::GetShieldedEncryptedNotesRequest,
725    platform_proto::GetShieldedEncryptedNotesResponse,
726    PlatformGrpcClient,
727    RequestSettings {
728        // A full chunk carries thousands of encrypted notes (megabytes), and
729        // the notes sync fetches several chunks in parallel over one link.
730        timeout: Some(LARGE_RESPONSE_TIMEOUT),
731        ..RequestSettings::default()
732    },
733    get_shielded_encrypted_notes
734);
735
736// rpc getShieldedAnchors(GetShieldedAnchorsRequest) returns (GetShieldedAnchorsResponse);
737impl_transport_request_grpc!(
738    platform_proto::GetShieldedAnchorsRequest,
739    platform_proto::GetShieldedAnchorsResponse,
740    PlatformGrpcClient,
741    RequestSettings::default(),
742    get_shielded_anchors
743);
744
745// rpc getMostRecentShieldedAnchor(GetMostRecentShieldedAnchorRequest) returns (GetMostRecentShieldedAnchorResponse);
746impl_transport_request_grpc!(
747    platform_proto::GetMostRecentShieldedAnchorRequest,
748    platform_proto::GetMostRecentShieldedAnchorResponse,
749    PlatformGrpcClient,
750    RequestSettings::default(),
751    get_most_recent_shielded_anchor
752);
753
754// rpc getShieldedPoolState(GetShieldedPoolStateRequest) returns (GetShieldedPoolStateResponse);
755impl_transport_request_grpc!(
756    platform_proto::GetShieldedPoolStateRequest,
757    platform_proto::GetShieldedPoolStateResponse,
758    PlatformGrpcClient,
759    RequestSettings::default(),
760    get_shielded_pool_state
761);
762
763// rpc getShieldedNotesCount(GetShieldedNotesCountRequest) returns (GetShieldedNotesCountResponse);
764impl_transport_request_grpc!(
765    platform_proto::GetShieldedNotesCountRequest,
766    platform_proto::GetShieldedNotesCountResponse,
767    PlatformGrpcClient,
768    RequestSettings::default(),
769    get_shielded_notes_count
770);
771
772// rpc getShieldedNullifiers(GetShieldedNullifiersRequest) returns (GetShieldedNullifiersResponse);
773impl_transport_request_grpc!(
774    platform_proto::GetShieldedNullifiersRequest,
775    platform_proto::GetShieldedNullifiersResponse,
776    PlatformGrpcClient,
777    RequestSettings::default(),
778    get_shielded_nullifiers
779);
780
781// Link to each core gRPC request what client and method to use:
782
783impl_transport_request_grpc!(
784    core_proto::GetTransactionRequest,
785    core_proto::GetTransactionResponse,
786    CoreGrpcClient,
787    RequestSettings::default(),
788    get_transaction
789);
790
791impl_transport_request_grpc!(
792    core_proto::GetBlockRequest,
793    core_proto::GetBlockResponse,
794    CoreGrpcClient,
795    RequestSettings::default(),
796    get_block
797);
798
799impl_transport_request_grpc!(
800    core_proto::GetBlockchainStatusRequest,
801    core_proto::GetBlockchainStatusResponse,
802    CoreGrpcClient,
803    RequestSettings::default(),
804    get_blockchain_status
805);
806
807impl_transport_request_grpc!(
808    core_proto::BroadcastTransactionRequest,
809    core_proto::BroadcastTransactionResponse,
810    CoreGrpcClient,
811    RequestSettings::default(),
812    broadcast_transaction
813);
814
815impl_transport_request_grpc!(
816    core_proto::TransactionsWithProofsRequest,
817    Streaming<core_proto::TransactionsWithProofsResponse>,
818    CoreGrpcClient,
819    RequestSettings {
820        timeout: Some(STREAMING_TIMEOUT),
821        ban_failed_address: None,
822        connect_timeout: None,
823        retries: None,
824        max_decoding_message_size: None,
825    },
826    subscribe_to_transactions_with_proofs
827);
828
829impl_transport_request_grpc!(
830    core_proto::MasternodeListRequest,
831    Streaming<core_proto::MasternodeListResponse>,
832    CoreGrpcClient,
833    RequestSettings {
834        timeout: Some(STREAMING_TIMEOUT),
835        ..RequestSettings::default()
836    },
837    subscribe_to_masternode_list
838);
839
840// rpc getStatus(GetStatusRequest) returns (GetStatusResponse);
841impl_transport_request_grpc!(
842    platform_proto::GetStatusRequest,
843    platform_proto::GetStatusResponse,
844    PlatformGrpcClient,
845    RequestSettings::default(),
846    get_status
847);
848
849//   rpc getIdentityByNonUniquePublicKeyHash(GetIdentityByNonUniquePublicKeyHashRequest) returns (GetIdentityByNonUniquePublicKeyHashResponse);
850impl_transport_request_grpc!(
851    platform_proto::GetIdentityByNonUniquePublicKeyHashRequest,
852    platform_proto::GetIdentityByNonUniquePublicKeyHashResponse,
853    PlatformGrpcClient,
854    RequestSettings::default(),
855    get_identity_by_non_unique_public_key_hash
856);
857
858// rpc getIdentityTokenBalances(GetIdentityTokenBalancesRequest) returns (GetIdentityTokenBalancesResponse);
859impl_transport_request_grpc!(
860    names_nothing: |request| match &request.version {
861        Some(platform_proto::get_identity_token_balances_request::Version::V0(v0)) if v0.prove && v0.token_ids.is_empty() => Some(
862            "token_ids must contain at least one identifier when requesting a proof",
863        ),
864        _ => None,
865    };
866    platform_proto::GetIdentityTokenBalancesRequest,
867    platform_proto::GetIdentityTokenBalancesResponse,
868    PlatformGrpcClient,
869    RequestSettings::default(),
870    get_identity_token_balances
871);
872
873// rpc getIdentitiesTokenBalances(GetIdentitiesTokenBalancesRequest) returns (GetIdentitiesTokenBalancesResponse);
874impl_transport_request_grpc!(
875    names_nothing: |request| match &request.version {
876        Some(platform_proto::get_identities_token_balances_request::Version::V0(v0)) if v0.prove && v0.identity_ids.is_empty() => Some(
877            "identity_ids must contain at least one identifier when requesting a proof",
878        ),
879        _ => None,
880    };
881    platform_proto::GetIdentitiesTokenBalancesRequest,
882    platform_proto::GetIdentitiesTokenBalancesResponse,
883    PlatformGrpcClient,
884    RequestSettings::default(),
885    get_identities_token_balances
886);
887
888// rpc getIdentityTokenInfos(GetIdentityTokenInfosRequest) returns (GetIdentityTokenInfosResponse);
889impl_transport_request_grpc!(
890    names_nothing: |request| match &request.version {
891        Some(platform_proto::get_identity_token_infos_request::Version::V0(v0)) if v0.prove && v0.token_ids.is_empty() => Some(
892            "token_ids must contain at least one identifier when requesting a proof",
893        ),
894        _ => None,
895    };
896    platform_proto::GetIdentityTokenInfosRequest,
897    platform_proto::GetIdentityTokenInfosResponse,
898    PlatformGrpcClient,
899    RequestSettings::default(),
900    get_identity_token_infos
901);
902
903// rpc getIdentitiesTokenInfos(GetIdentitiesTokenInfosRequest) returns (GetIdentitiesTokenInfosResponse);
904impl_transport_request_grpc!(
905    names_nothing: |request| match &request.version {
906        Some(platform_proto::get_identities_token_infos_request::Version::V0(v0)) if v0.prove && v0.identity_ids.is_empty() => Some(
907            "identity_ids must contain at least one identifier when requesting a proof",
908        ),
909        _ => None,
910    };
911    platform_proto::GetIdentitiesTokenInfosRequest,
912    platform_proto::GetIdentitiesTokenInfosResponse,
913    PlatformGrpcClient,
914    RequestSettings::default(),
915    get_identities_token_infos
916);
917
918// rpc getTokenStatuses(GetTokenStatusesRequest) returns (GetTokenStatusesResponse);
919impl_transport_request_grpc!(
920    names_nothing: |request| match &request.version {
921        Some(platform_proto::get_token_statuses_request::Version::V0(v0)) if v0.prove && v0.token_ids.is_empty() => Some(
922            "token_ids must contain at least one identifier when requesting a proof",
923        ),
924        _ => None,
925    };
926    platform_proto::GetTokenStatusesRequest,
927    platform_proto::GetTokenStatusesResponse,
928    PlatformGrpcClient,
929    RequestSettings::default(),
930    get_token_statuses
931);
932
933// rpc getTokenPreProgrammedDistributions(GetTokenPreProgrammedDistributionsRequest) returns (GetTokenPreProgrammedDistributionsResponse);
934impl_transport_request_grpc!(
935    platform_proto::GetTokenPreProgrammedDistributionsRequest,
936    platform_proto::GetTokenPreProgrammedDistributionsResponse,
937    PlatformGrpcClient,
938    RequestSettings::default(),
939    get_token_pre_programmed_distributions
940);
941
942// rpc getTokenTotalSupply(GetTokenTotalSupplyRequest) returns (GetTokenTotalSupplyResponse);
943impl_transport_request_grpc!(
944    platform_proto::GetTokenTotalSupplyRequest,
945    platform_proto::GetTokenTotalSupplyResponse,
946    PlatformGrpcClient,
947    RequestSettings::default(),
948    get_token_total_supply
949);
950
951// rpc getGroupInfo(GetGroupInfoRequest) returns (GetGroupInfoResponse);
952impl_transport_request_grpc!(
953    platform_proto::GetGroupInfoRequest,
954    platform_proto::GetGroupInfoResponse,
955    PlatformGrpcClient,
956    RequestSettings::default(),
957    get_group_info
958);
959
960// rpc getGroupInfos(GetGroupInfosRequest) returns (GetGroupInfosResponse);
961impl_transport_request_grpc!(
962    platform_proto::GetGroupInfosRequest,
963    platform_proto::GetGroupInfosResponse,
964    PlatformGrpcClient,
965    RequestSettings::default(),
966    get_group_infos
967);
968
969// rpc getGroupActions(GetGroupActionsRequest) returns (GetGroupActionsResponse);
970impl_transport_request_grpc!(
971    platform_proto::GetGroupActionsRequest,
972    platform_proto::GetGroupActionsResponse,
973    PlatformGrpcClient,
974    RequestSettings::default(),
975    get_group_actions
976);
977
978// rpc getGroupActionSigners(GetGroupActionSignersRequest) returns (GetGroupActionSignersResponse);
979impl_transport_request_grpc!(
980    platform_proto::GetGroupActionSignersRequest,
981    platform_proto::GetGroupActionSignersResponse,
982    PlatformGrpcClient,
983    RequestSettings::default(),
984    get_group_action_signers
985);
986
987// rpc getTokenDirectPurchasePrices(GetTokenDirectPurchasePricesRequest) returns (GetTokenDirectPurchasePricesResponse);
988impl_transport_request_grpc!(
989    platform_proto::GetTokenDirectPurchasePricesRequest,
990    platform_proto::GetTokenDirectPurchasePricesResponse,
991    PlatformGrpcClient,
992    RequestSettings::default(),
993    get_token_direct_purchase_prices
994);
995
996// rpc getTokenContractInfo(GetTokenContractInfoRequest) returns (GetTokenContractInfoResponse);
997impl_transport_request_grpc!(
998    platform_proto::GetTokenContractInfoRequest,
999    platform_proto::GetTokenContractInfoResponse,
1000    PlatformGrpcClient,
1001    RequestSettings::default(),
1002    get_token_contract_info
1003);
1004
1005// rpc getTokenPerpetualDistributionLastClaimRequest(GetTokenPerpetualDistributionLastClaimRequest) returns (GetTokenPerpetualDistributionLastClaimResponse);
1006impl_transport_request_grpc!(
1007    platform_proto::GetTokenPerpetualDistributionLastClaimRequest,
1008    platform_proto::GetTokenPerpetualDistributionLastClaimResponse,
1009    PlatformGrpcClient,
1010    RequestSettings::default(),
1011    get_token_perpetual_distribution_last_claim
1012);
1013
1014// rpc getAddressInfo(GetAddressInfoRequest) returns (GetAddressInfoResponse);
1015impl_transport_request_grpc!(
1016    platform_proto::GetAddressInfoRequest,
1017    platform_proto::GetAddressInfoResponse,
1018    PlatformGrpcClient,
1019    RequestSettings::default(),
1020    get_address_info
1021);
1022
1023// rpc getAddressesInfos(GetAddressesInfosRequest) returns (GetAddressesInfosResponse);
1024impl_transport_request_grpc!(
1025    names_nothing: |request| match &request.version {
1026        Some(platform_proto::get_addresses_infos_request::Version::V0(v0)) if v0.prove && v0.addresses.is_empty() => Some(
1027            "addresses must contain at least one address when requesting a proof",
1028        ),
1029        _ => None,
1030    };
1031    platform_proto::GetAddressesInfosRequest,
1032    platform_proto::GetAddressesInfosResponse,
1033    PlatformGrpcClient,
1034    RequestSettings::default(),
1035    get_addresses_infos
1036);
1037
1038// rpc getAddressesTrunkState(GetAddressesTrunkStateRequest) returns (GetAddressesTrunkStateResponse);
1039impl_transport_request_grpc!(
1040    platform_proto::GetAddressesTrunkStateRequest,
1041    platform_proto::GetAddressesTrunkStateResponse,
1042    PlatformGrpcClient,
1043    RequestSettings::default(),
1044    get_addresses_trunk_state
1045);
1046
1047// rpc getAddressesBranchState(GetAddressesBranchStateRequest) returns (GetAddressesBranchStateResponse);
1048impl_transport_request_grpc!(
1049    platform_proto::GetAddressesBranchStateRequest,
1050    platform_proto::GetAddressesBranchStateResponse,
1051    PlatformGrpcClient,
1052    RequestSettings::default(),
1053    get_addresses_branch_state
1054);
1055
1056// rpc getRecentAddressBalanceChanges(GetRecentAddressBalanceChangesRequest) returns (GetRecentAddressBalanceChangesResponse);
1057impl_transport_request_grpc!(
1058    platform_proto::GetRecentAddressBalanceChangesRequest,
1059    platform_proto::GetRecentAddressBalanceChangesResponse,
1060    PlatformGrpcClient,
1061    RequestSettings::default(),
1062    get_recent_address_balance_changes
1063);
1064
1065// rpc getRecentCompactedAddressBalanceChanges(GetRecentCompactedAddressBalanceChangesRequest) returns (GetRecentCompactedAddressBalanceChangesResponse);
1066impl_transport_request_grpc!(
1067    platform_proto::GetRecentCompactedAddressBalanceChangesRequest,
1068    platform_proto::GetRecentCompactedAddressBalanceChangesResponse,
1069    PlatformGrpcClient,
1070    RequestSettings {
1071        // GetRecentCompactedAddressBalanceChangesResponse can have 100 values * 2048 addresses * ~44  bytes each = ~9MB
1072        // We set it to 16MB to be safe
1073        max_decoding_message_size: Some(16 * 1024 * 1024),
1074        timeout: Some(LARGE_RESPONSE_TIMEOUT),
1075        ..RequestSettings::default()
1076    },
1077    get_recent_compacted_address_balance_changes
1078);