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