Skip to main content

drive_abci/query/
service.rs

1use crate::error::execution::ExecutionError;
2use crate::error::query::QueryError;
3use crate::error::Error;
4use crate::metrics::{abci_response_code_metric_label, query_duration_metric};
5use crate::platform_types::platform::Platform;
6use crate::platform_types::platform_state::PlatformState;
7use crate::platform_types::platform_state::PlatformStateV0Methods;
8use crate::query::QueryValidationResult;
9use crate::rpc::core::DefaultCoreRPC;
10use crate::utils::spawn_blocking_task_with_name_if_supported;
11use async_trait::async_trait;
12use dapi_grpc::drive::v0::drive_internal_server::DriveInternal;
13use dapi_grpc::drive::v0::{GetProofsRequest, GetProofsResponse};
14use dapi_grpc::platform::v0::get_path_elements_request;
15use dapi_grpc::platform::v0::platform_server::Platform as PlatformService;
16use dapi_grpc::platform::v0::{
17    BroadcastStateTransitionRequest, BroadcastStateTransitionResponse, GetAddressInfoRequest,
18    GetAddressInfoResponse, GetAddressesBranchStateRequest, GetAddressesBranchStateResponse,
19    GetAddressesInfosRequest, GetAddressesInfosResponse, GetAddressesTrunkStateRequest,
20    GetAddressesTrunkStateResponse, GetConsensusParamsRequest, GetConsensusParamsResponse,
21    GetContestedResourceIdentityVotesRequest, GetContestedResourceIdentityVotesResponse,
22    GetContestedResourceVoteStateRequest, GetContestedResourceVoteStateResponse,
23    GetContestedResourceVotersForIdentityRequest, GetContestedResourceVotersForIdentityResponse,
24    GetContestedResourcesRequest, GetContestedResourcesResponse,
25    GetContractDocumentRemovalsRequest, GetContractDocumentRemovalsResponse,
26    GetContractFeePotsRequest, GetContractFeePotsResponse, GetContractGroupInfoRequest,
27    GetContractGroupInfoResponse, GetContractGroupMembersRequest, GetContractGroupMembersResponse,
28    GetContractGroupsForContractRequest, GetContractGroupsForContractResponse,
29    GetContractModerationActionCountsRequest, GetContractModerationActionCountsResponse,
30    GetContractModerationEntriesRequest, GetContractModerationEntriesResponse,
31    GetContractModerationStatusRequest, GetContractModerationStatusResponse,
32    GetContractTeamActionSignersRequest, GetContractTeamActionSignersResponse,
33    GetContractTeamActionsRequest, GetContractTeamActionsResponse, GetCurrentQuorumsInfoRequest,
34    GetCurrentQuorumsInfoResponse, GetDataContractHistoryRequest, GetDataContractHistoryResponse,
35    GetDataContractRequest, GetDataContractResponse, GetDataContractsByRangeRequest,
36    GetDataContractsLatestVersionsRequest, GetDataContractsLatestVersionsResponse,
37    GetDataContractsRequest, GetDataContractsResponse, GetDocumentHistoryRequest,
38    GetDocumentHistoryResponse, GetDocumentsRequest, GetDocumentsResponse, GetEpochsInfoRequest,
39    GetEpochsInfoResponse, GetEvonodesProposedEpochBlocksByIdsRequest,
40    GetEvonodesProposedEpochBlocksByRangeRequest, GetEvonodesProposedEpochBlocksResponse,
41    GetFinalizedEpochInfosRequest, GetFinalizedEpochInfosResponse, GetGroupActionSignersRequest,
42    GetGroupActionSignersResponse, GetGroupActionsRequest, GetGroupActionsResponse,
43    GetGroupInfoRequest, GetGroupInfoResponse, GetGroupInfosRequest, GetGroupInfosResponse,
44    GetIdentitiesBalancesRequest, GetIdentitiesBalancesResponse, GetIdentitiesContractKeysRequest,
45    GetIdentitiesContractKeysResponse, GetIdentitiesTokenBalancesRequest,
46    GetIdentitiesTokenBalancesResponse, GetIdentitiesTokenInfosRequest,
47    GetIdentitiesTokenInfosResponse, GetIdentityBalanceAndRevisionRequest,
48    GetIdentityBalanceAndRevisionResponse, GetIdentityBalanceRequest, GetIdentityBalanceResponse,
49    GetIdentityByNonUniquePublicKeyHashRequest, GetIdentityByNonUniquePublicKeyHashResponse,
50    GetIdentityByPublicKeyHashRequest, GetIdentityByPublicKeyHashResponse,
51    GetIdentityContractNonceRequest, GetIdentityContractNonceResponse,
52    GetIdentityKeysRemainingBudgetsRequest, GetIdentityKeysRemainingBudgetsResponse,
53    GetIdentityKeysRequest, GetIdentityKeysResponse, GetIdentityNonceRequest,
54    GetIdentityNonceResponse, GetIdentityRequest, GetIdentityResponse,
55    GetIdentityTokenBalancesRequest, GetIdentityTokenBalancesResponse,
56    GetIdentityTokenInfosRequest, GetIdentityTokenInfosResponse,
57    GetMostRecentShieldedAnchorRequest, GetMostRecentShieldedAnchorResponse,
58    GetPathElementsRequest, GetPathElementsResponse, GetPrefundedSpecializedBalanceRequest,
59    GetPrefundedSpecializedBalanceResponse, GetProtocolVersionUpgradeStateRequest,
60    GetProtocolVersionUpgradeStateResponse, GetProtocolVersionUpgradeVoteStatusRequest,
61    GetProtocolVersionUpgradeVoteStatusResponse, GetRecentAddressBalanceChangesRequest,
62    GetRecentAddressBalanceChangesResponse, GetRecentCompactedAddressBalanceChangesRequest,
63    GetRecentCompactedAddressBalanceChangesResponse, GetShieldedAnchorsRequest,
64    GetShieldedAnchorsResponse, GetShieldedEncryptedNotesRequest,
65    GetShieldedEncryptedNotesResponse, GetShieldedNotesCountRequest, GetShieldedNotesCountResponse,
66    GetShieldedNullifiersRequest, GetShieldedNullifiersResponse, GetShieldedPoolStateRequest,
67    GetShieldedPoolStateResponse, GetStatusRequest, GetStatusResponse, GetTokenContractInfoRequest,
68    GetTokenContractInfoResponse, GetTokenDirectPurchasePricesRequest,
69    GetTokenDirectPurchasePricesResponse, GetTokenPerpetualDistributionLastClaimRequest,
70    GetTokenPerpetualDistributionLastClaimResponse, GetTokenPreProgrammedDistributionsRequest,
71    GetTokenPreProgrammedDistributionsResponse, GetTokenStatusesRequest, GetTokenStatusesResponse,
72    GetTokenTotalSupplyRequest, GetTokenTotalSupplyResponse, GetTotalCreditsInPlatformRequest,
73    GetTotalCreditsInPlatformResponse, GetVotePollsByEndDateRequest, GetVotePollsByEndDateResponse,
74    WaitForStateTransitionResultRequest, WaitForStateTransitionResultResponse,
75};
76use dapi_grpc::tonic::{Code, Request, Response, Status};
77use dpp::version::PlatformVersion;
78use drive::error::drive::DriveError as DriveInternalError;
79use drive::error::Error as DriveError;
80use std::sync::atomic::Ordering;
81use std::sync::Arc;
82use std::thread::sleep;
83use std::time::Duration;
84use tracing::Instrument;
85
86const MAX_PATH_COMPONENTS: usize = 256;
87const MAX_GROVEDB_KEY_BYTES: usize = 255;
88const MAX_PATH_QUERY_BYTES: usize = 64 * 1024;
89/// Longest part of a status message the service keeps; a longer message is cut there and gets
90/// a short note with the number of bytes dropped. Tonic sends the message in the
91/// `grpc-message` header, and an HTTP/2 peer refuses an oversized header list. The client then
92/// sees a transport error instead of the original status and may retry it on other nodes.
93/// A message can echo a request field of any length, such as a document type name.
94const MAX_STATUS_MESSAGE_BYTES: usize = 1024;
95
96/// Service to handle platform queries
97pub struct QueryService {
98    platform: Arc<Platform<DefaultCoreRPC>>,
99}
100
101type QueryMethod<RQ, RS> = fn(
102    &Platform<DefaultCoreRPC>,
103    RQ,
104    &PlatformState,
105    &PlatformVersion,
106) -> Result<QueryValidationResult<RS>, Error>;
107
108impl QueryService {
109    /// Creates new QueryService
110    pub fn new(platform: Arc<Platform<DefaultCoreRPC>>) -> Self {
111        Self { platform }
112    }
113
114    async fn handle_blocking_query<RQ, RS>(
115        &self,
116        request: Request<RQ>,
117        query_method: QueryMethod<RQ, RS>,
118        endpoint_name: &str,
119    ) -> Result<Response<RS>, Status>
120    where
121        RS: Clone + Send + 'static,
122        RQ: Send + Clone + 'static,
123    {
124        let mut response_duration_metric = query_duration_metric(endpoint_name);
125
126        let platform = Arc::clone(&self.platform);
127
128        let result = spawn_blocking_task_with_name_if_supported("query", move || {
129            let mut result;
130
131            let query_request = request.into_inner();
132
133            let mut query_counter = 0;
134
135            loop {
136                let platform_state = platform.state.load();
137
138                let platform_version = platform_state
139                    .current_platform_version()
140                    .map_err(|_| Status::unavailable("platform is not initialized"))?;
141
142                // Query is using Platform execution state and Drive state to during the execution.
143                // They are updating every block in finalize block ABCI handler.
144                // The problem is that these two operations aren't atomic and some latency between
145                // them could lead to data races. `committed_block_height_guard` counter that represents
146                // the latest the height of latest committed Drive state and logic bellow ensures
147                // that query is executed only after/before both states are updated.
148                let mut needs_restart = false;
149
150                loop {
151                    let committed_block_height_guard = platform
152                        .committed_block_height_guard
153                        .load(Ordering::Relaxed);
154                    let mut counter = 0;
155                    if platform_state.last_committed_block_height() == committed_block_height_guard
156                    {
157                        break;
158                    } else {
159                        counter += 1;
160                        sleep(Duration::from_millis(10))
161                    }
162
163                    // We try for up to 1 second
164                    if counter >= 100 {
165                        query_counter += 1;
166                        needs_restart = true;
167                        break;
168                    }
169                }
170
171                if query_counter > 3 {
172                    return Err(query_error_into_status(QueryError::NotServiceable(
173                        "platform is saturated (did not attempt query)".to_string(),
174                    )));
175                }
176
177                if needs_restart {
178                    continue;
179                }
180
181                result = query_method(
182                    &platform,
183                    query_request.clone(),
184                    &platform_state,
185                    platform_version,
186                );
187
188                let committed_block_height_guard = platform
189                    .committed_block_height_guard
190                    .load(Ordering::Relaxed);
191
192                if platform_state.last_committed_block_height() == committed_block_height_guard {
193                    // in this case the query almost certainly executed correctly
194                    break;
195                } else {
196                    query_counter += 1;
197
198                    if query_counter > 2 {
199                        // This should never be possible
200                        return Err(query_error_into_status(QueryError::NotServiceable(
201                            "platform is saturated".to_string(),
202                        )));
203                    }
204                }
205            }
206
207            query_result_into_response(result).map(Response::new)
208        })?
209        .instrument(tracing::trace_span!("query", endpoint_name))
210        .await
211        .map_err(|error| Status::internal(format!("query thread failed: {}", error)))?;
212
213        // Query logging and metrics
214        let code = match &result {
215            Ok(_) => Code::Ok,
216            Err(status) => status.code(),
217        };
218
219        let code_label = format!("{:?}", code).to_lowercase();
220
221        // Add code to response duration metric
222        let label = abci_response_code_metric_label(code);
223        response_duration_metric.add_label(label);
224
225        match code {
226            // User errors
227            Code::Ok
228            | Code::InvalidArgument
229            | Code::NotFound
230            | Code::AlreadyExists
231            | Code::ResourceExhausted
232            | Code::PermissionDenied
233            | Code::Unavailable
234            | Code::Aborted
235            | Code::FailedPrecondition
236            | Code::OutOfRange
237            | Code::Cancelled
238            | Code::DeadlineExceeded
239            | Code::Unauthenticated => {
240                let elapsed_time = response_duration_metric.elapsed().as_secs_f64();
241
242                tracing::trace!(
243                    elapsed_time,
244                    endpoint_name,
245                    code = code_label,
246                    "query '{}' executed with code {:?} in {} secs",
247                    endpoint_name,
248                    code,
249                    elapsed_time
250                );
251            }
252            // System errors
253            Code::Unknown | Code::Unimplemented | Code::Internal | Code::DataLoss => {
254                tracing::error!(
255                    endpoint_name,
256                    code = code_label,
257                    "query '{}' execution failed with code {:?}",
258                    endpoint_name,
259                    code
260                );
261            }
262        }
263
264        result
265    }
266}
267
268fn respond_with_unimplemented<RS>(name: &str) -> Result<Response<RS>, Status> {
269    tracing::error!("{} endpoint is called but it's not supported", name);
270
271    Err(Status::unimplemented("the endpoint is not supported"))
272}
273
274#[async_trait]
275impl PlatformService for QueryService {
276    async fn broadcast_state_transition(
277        &self,
278        _request: Request<BroadcastStateTransitionRequest>,
279    ) -> Result<Response<BroadcastStateTransitionResponse>, Status> {
280        respond_with_unimplemented("broadcast_state_transition")
281    }
282
283    async fn get_identity(
284        &self,
285        request: Request<GetIdentityRequest>,
286    ) -> Result<Response<GetIdentityResponse>, Status> {
287        self.handle_blocking_query(
288            request,
289            Platform::<DefaultCoreRPC>::query_identity,
290            "get_identity",
291        )
292        .await
293    }
294
295    async fn get_identities_contract_keys(
296        &self,
297        request: Request<GetIdentitiesContractKeysRequest>,
298    ) -> Result<Response<GetIdentitiesContractKeysResponse>, Status> {
299        self.handle_blocking_query(
300            request,
301            Platform::<DefaultCoreRPC>::query_identities_contract_keys,
302            "get_identities_contract_keys",
303        )
304        .await
305    }
306
307    async fn get_identity_keys(
308        &self,
309        request: Request<GetIdentityKeysRequest>,
310    ) -> Result<Response<GetIdentityKeysResponse>, Status> {
311        self.handle_blocking_query(
312            request,
313            Platform::<DefaultCoreRPC>::query_keys,
314            "get_identity_keys",
315        )
316        .await
317    }
318
319    async fn get_identity_nonce(
320        &self,
321        request: Request<GetIdentityNonceRequest>,
322    ) -> Result<Response<GetIdentityNonceResponse>, Status> {
323        self.handle_blocking_query(
324            request,
325            Platform::<DefaultCoreRPC>::query_identity_nonce,
326            "get_identity_nonce",
327        )
328        .await
329    }
330
331    async fn get_identity_contract_nonce(
332        &self,
333        request: Request<GetIdentityContractNonceRequest>,
334    ) -> Result<Response<GetIdentityContractNonceResponse>, Status> {
335        self.handle_blocking_query(
336            request,
337            Platform::<DefaultCoreRPC>::query_identity_contract_nonce,
338            "get_identity_contract_nonce",
339        )
340        .await
341    }
342
343    async fn get_identity_keys_remaining_budgets(
344        &self,
345        request: Request<GetIdentityKeysRemainingBudgetsRequest>,
346    ) -> Result<Response<GetIdentityKeysRemainingBudgetsResponse>, Status> {
347        self.handle_blocking_query(
348            request,
349            Platform::<DefaultCoreRPC>::query_identity_keys_remaining_budgets,
350            "get_identity_keys_remaining_budgets",
351        )
352        .await
353    }
354
355    async fn get_identity_balance(
356        &self,
357        request: Request<GetIdentityBalanceRequest>,
358    ) -> Result<Response<GetIdentityBalanceResponse>, Status> {
359        self.handle_blocking_query(
360            request,
361            Platform::<DefaultCoreRPC>::query_balance,
362            "get_identity_balance",
363        )
364        .await
365    }
366
367    async fn get_identity_balance_and_revision(
368        &self,
369        request: Request<GetIdentityBalanceAndRevisionRequest>,
370    ) -> Result<Response<GetIdentityBalanceAndRevisionResponse>, Status> {
371        self.handle_blocking_query(
372            request,
373            Platform::<DefaultCoreRPC>::query_balance_and_revision,
374            "get_identity_balance_and_revision",
375        )
376        .await
377    }
378
379    async fn get_data_contract(
380        &self,
381        request: Request<GetDataContractRequest>,
382    ) -> Result<Response<GetDataContractResponse>, Status> {
383        self.handle_blocking_query(
384            request,
385            Platform::<DefaultCoreRPC>::query_data_contract,
386            "get_data_contract",
387        )
388        .await
389    }
390
391    async fn get_data_contract_history(
392        &self,
393        request: Request<GetDataContractHistoryRequest>,
394    ) -> Result<Response<GetDataContractHistoryResponse>, Status> {
395        self.handle_blocking_query(
396            request,
397            Platform::<DefaultCoreRPC>::query_data_contract_history,
398            "get_data_contract_history",
399        )
400        .await
401    }
402
403    async fn get_data_contracts(
404        &self,
405        request: Request<GetDataContractsRequest>,
406    ) -> Result<Response<GetDataContractsResponse>, Status> {
407        self.handle_blocking_query(
408            request,
409            Platform::<DefaultCoreRPC>::query_data_contracts,
410            "get_data_contracts",
411        )
412        .await
413    }
414
415    async fn get_data_contracts_by_range(
416        &self,
417        request: Request<GetDataContractsByRangeRequest>,
418    ) -> Result<Response<GetDataContractsResponse>, Status> {
419        self.handle_blocking_query(
420            request,
421            Platform::<DefaultCoreRPC>::query_data_contracts_by_range,
422            "get_data_contracts_by_range",
423        )
424        .await
425    }
426
427    async fn get_data_contracts_latest_versions(
428        &self,
429        request: Request<GetDataContractsLatestVersionsRequest>,
430    ) -> Result<Response<GetDataContractsLatestVersionsResponse>, Status> {
431        self.handle_blocking_query(
432            request,
433            Platform::<DefaultCoreRPC>::query_data_contracts_latest_versions,
434            "get_data_contracts_latest_versions",
435        )
436        .await
437    }
438
439    async fn get_contract_group_info(
440        &self,
441        request: Request<GetContractGroupInfoRequest>,
442    ) -> Result<Response<GetContractGroupInfoResponse>, Status> {
443        self.handle_blocking_query(
444            request,
445            Platform::<DefaultCoreRPC>::query_contract_group_info,
446            "get_contract_group_info",
447        )
448        .await
449    }
450
451    async fn get_contract_moderation_status(
452        &self,
453        request: Request<GetContractModerationStatusRequest>,
454    ) -> Result<Response<GetContractModerationStatusResponse>, Status> {
455        self.handle_blocking_query(
456            request,
457            Platform::<DefaultCoreRPC>::query_contract_moderation_status,
458            "get_contract_moderation_status",
459        )
460        .await
461    }
462
463    async fn get_contract_document_removals(
464        &self,
465        request: Request<GetContractDocumentRemovalsRequest>,
466    ) -> Result<Response<GetContractDocumentRemovalsResponse>, Status> {
467        self.handle_blocking_query(
468            request,
469            Platform::<DefaultCoreRPC>::query_contract_document_removals,
470            "get_contract_document_removals",
471        )
472        .await
473    }
474
475    async fn get_contract_team_actions(
476        &self,
477        request: Request<GetContractTeamActionsRequest>,
478    ) -> Result<Response<GetContractTeamActionsResponse>, Status> {
479        self.handle_blocking_query(
480            request,
481            Platform::<DefaultCoreRPC>::query_contract_team_actions,
482            "get_contract_team_actions",
483        )
484        .await
485    }
486
487    async fn get_contract_team_action_signers(
488        &self,
489        request: Request<GetContractTeamActionSignersRequest>,
490    ) -> Result<Response<GetContractTeamActionSignersResponse>, Status> {
491        self.handle_blocking_query(
492            request,
493            Platform::<DefaultCoreRPC>::query_contract_team_action_signers,
494            "get_contract_team_action_signers",
495        )
496        .await
497    }
498
499    async fn get_contract_moderation_action_counts(
500        &self,
501        request: Request<GetContractModerationActionCountsRequest>,
502    ) -> Result<Response<GetContractModerationActionCountsResponse>, Status> {
503        self.handle_blocking_query(
504            request,
505            Platform::<DefaultCoreRPC>::query_contract_moderation_action_counts,
506            "get_contract_moderation_action_counts",
507        )
508        .await
509    }
510
511    async fn get_contract_moderation_entries(
512        &self,
513        request: Request<GetContractModerationEntriesRequest>,
514    ) -> Result<Response<GetContractModerationEntriesResponse>, Status> {
515        self.handle_blocking_query(
516            request,
517            Platform::<DefaultCoreRPC>::query_contract_moderation_entries,
518            "get_contract_moderation_entries",
519        )
520        .await
521    }
522
523    async fn get_contract_fee_pots(
524        &self,
525        request: Request<GetContractFeePotsRequest>,
526    ) -> Result<Response<GetContractFeePotsResponse>, Status> {
527        self.handle_blocking_query(
528            request,
529            Platform::<DefaultCoreRPC>::query_contract_fee_pots,
530            "get_contract_fee_pots",
531        )
532        .await
533    }
534
535    async fn get_contract_group_members(
536        &self,
537        request: Request<GetContractGroupMembersRequest>,
538    ) -> Result<Response<GetContractGroupMembersResponse>, Status> {
539        self.handle_blocking_query(
540            request,
541            Platform::<DefaultCoreRPC>::query_contract_group_members,
542            "get_contract_group_members",
543        )
544        .await
545    }
546
547    async fn get_contract_groups_for_contract(
548        &self,
549        request: Request<GetContractGroupsForContractRequest>,
550    ) -> Result<Response<GetContractGroupsForContractResponse>, Status> {
551        self.handle_blocking_query(
552            request,
553            Platform::<DefaultCoreRPC>::query_contract_groups_for_contract,
554            "get_contract_groups_for_contract",
555        )
556        .await
557    }
558
559    async fn get_document_history(
560        &self,
561        request: Request<GetDocumentHistoryRequest>,
562    ) -> Result<Response<GetDocumentHistoryResponse>, Status> {
563        self.handle_blocking_query(
564            request,
565            Platform::<DefaultCoreRPC>::query_document_history,
566            "get_document_history",
567        )
568        .await
569    }
570
571    async fn get_documents(
572        &self,
573        request: Request<GetDocumentsRequest>,
574    ) -> Result<Response<GetDocumentsResponse>, Status> {
575        self.handle_blocking_query(
576            request,
577            Platform::<DefaultCoreRPC>::query_documents,
578            "get_documents",
579        )
580        .await
581    }
582
583    async fn get_identity_by_public_key_hash(
584        &self,
585        request: Request<GetIdentityByPublicKeyHashRequest>,
586    ) -> Result<Response<GetIdentityByPublicKeyHashResponse>, Status> {
587        self.handle_blocking_query(
588            request,
589            Platform::<DefaultCoreRPC>::query_identity_by_public_key_hash,
590            "get_identity_by_public_key_hash",
591        )
592        .await
593    }
594
595    async fn get_identity_by_non_unique_public_key_hash(
596        &self,
597        request: Request<GetIdentityByNonUniquePublicKeyHashRequest>,
598    ) -> Result<Response<GetIdentityByNonUniquePublicKeyHashResponse>, Status> {
599        self.handle_blocking_query(
600            request,
601            Platform::<DefaultCoreRPC>::query_identity_by_non_unique_public_key_hash,
602            "get_identity_by_non_unique_public_key_hash",
603        )
604        .await
605    }
606
607    async fn wait_for_state_transition_result(
608        &self,
609        _request: Request<WaitForStateTransitionResultRequest>,
610    ) -> Result<Response<WaitForStateTransitionResultResponse>, Status> {
611        respond_with_unimplemented("wait_for_state_transition_result")
612    }
613
614    async fn get_consensus_params(
615        &self,
616        _request: Request<GetConsensusParamsRequest>,
617    ) -> Result<Response<GetConsensusParamsResponse>, Status> {
618        respond_with_unimplemented("get_consensus_params")
619    }
620
621    async fn get_protocol_version_upgrade_state(
622        &self,
623        request: Request<GetProtocolVersionUpgradeStateRequest>,
624    ) -> Result<Response<GetProtocolVersionUpgradeStateResponse>, Status> {
625        self.handle_blocking_query(
626            request,
627            Platform::<DefaultCoreRPC>::query_version_upgrade_state,
628            "get_protocol_version_upgrade_state",
629        )
630        .await
631    }
632
633    async fn get_protocol_version_upgrade_vote_status(
634        &self,
635        request: Request<GetProtocolVersionUpgradeVoteStatusRequest>,
636    ) -> Result<Response<GetProtocolVersionUpgradeVoteStatusResponse>, Status> {
637        self.handle_blocking_query(
638            request,
639            Platform::<DefaultCoreRPC>::query_version_upgrade_vote_status,
640            "get_protocol_version_upgrade_vote_status",
641        )
642        .await
643    }
644
645    async fn get_epochs_info(
646        &self,
647        request: Request<GetEpochsInfoRequest>,
648    ) -> Result<Response<GetEpochsInfoResponse>, Status> {
649        self.handle_blocking_query(
650            request,
651            Platform::<DefaultCoreRPC>::query_epoch_infos,
652            "get_epochs_info",
653        )
654        .await
655    }
656
657    async fn get_path_elements(
658        &self,
659        request: Request<GetPathElementsRequest>,
660    ) -> Result<Response<GetPathElementsResponse>, Status> {
661        validate_path_elements_request(request.get_ref())?;
662        self.handle_blocking_query(
663            request,
664            Platform::<DefaultCoreRPC>::query_path_elements,
665            "get_path_elements",
666        )
667        .await
668    }
669
670    async fn get_contested_resources(
671        &self,
672        request: Request<GetContestedResourcesRequest>,
673    ) -> Result<Response<GetContestedResourcesResponse>, Status> {
674        self.handle_blocking_query(
675            request,
676            Platform::<DefaultCoreRPC>::query_contested_resources,
677            "get_contested_resources",
678        )
679        .await
680    }
681
682    async fn get_contested_resource_vote_state(
683        &self,
684        request: Request<GetContestedResourceVoteStateRequest>,
685    ) -> Result<Response<GetContestedResourceVoteStateResponse>, Status> {
686        self.handle_blocking_query(
687            request,
688            Platform::<DefaultCoreRPC>::query_contested_resource_vote_state,
689            "get_contested_resource_vote_state",
690        )
691        .await
692    }
693
694    async fn get_contested_resource_voters_for_identity(
695        &self,
696        request: Request<GetContestedResourceVotersForIdentityRequest>,
697    ) -> Result<Response<GetContestedResourceVotersForIdentityResponse>, Status> {
698        self.handle_blocking_query(
699            request,
700            Platform::<DefaultCoreRPC>::query_contested_resource_voters_for_identity,
701            "get_contested_resource_voters_for_identity",
702        )
703        .await
704    }
705
706    async fn get_contested_resource_identity_votes(
707        &self,
708        request: Request<GetContestedResourceIdentityVotesRequest>,
709    ) -> Result<Response<GetContestedResourceIdentityVotesResponse>, Status> {
710        self.handle_blocking_query(
711            request,
712            Platform::<DefaultCoreRPC>::query_contested_resource_identity_votes,
713            "get_contested_resource_identity_votes",
714        )
715        .await
716    }
717
718    async fn get_vote_polls_by_end_date(
719        &self,
720        request: Request<GetVotePollsByEndDateRequest>,
721    ) -> Result<Response<GetVotePollsByEndDateResponse>, Status> {
722        self.handle_blocking_query(
723            request,
724            Platform::<DefaultCoreRPC>::query_vote_polls_by_end_date_query,
725            "get_vote_polls_by_end_date",
726        )
727        .await
728    }
729
730    async fn get_prefunded_specialized_balance(
731        &self,
732        request: Request<GetPrefundedSpecializedBalanceRequest>,
733    ) -> Result<Response<GetPrefundedSpecializedBalanceResponse>, Status> {
734        self.handle_blocking_query(
735            request,
736            Platform::<DefaultCoreRPC>::query_prefunded_specialized_balance,
737            "get_prefunded_specialized_balance",
738        )
739        .await
740    }
741
742    async fn get_total_credits_in_platform(
743        &self,
744        request: Request<GetTotalCreditsInPlatformRequest>,
745    ) -> Result<Response<GetTotalCreditsInPlatformResponse>, Status> {
746        self.handle_blocking_query(
747            request,
748            Platform::<DefaultCoreRPC>::query_total_credits_in_platform,
749            "get_total_credits_in_platform",
750        )
751        .await
752    }
753
754    async fn get_identities_balances(
755        &self,
756        request: Request<GetIdentitiesBalancesRequest>,
757    ) -> Result<Response<GetIdentitiesBalancesResponse>, Status> {
758        self.handle_blocking_query(
759            request,
760            Platform::<DefaultCoreRPC>::query_identities_balances,
761            "get_identities_balances",
762        )
763        .await
764    }
765
766    async fn get_status(
767        &self,
768        request: Request<GetStatusRequest>,
769    ) -> Result<Response<GetStatusResponse>, Status> {
770        self.handle_blocking_query(
771            request,
772            Platform::<DefaultCoreRPC>::query_partial_status,
773            "query_partial_status",
774        )
775        .await
776    }
777
778    async fn get_evonodes_proposed_epoch_blocks_by_ids(
779        &self,
780        request: Request<GetEvonodesProposedEpochBlocksByIdsRequest>,
781    ) -> Result<Response<GetEvonodesProposedEpochBlocksResponse>, Status> {
782        self.handle_blocking_query(
783            request,
784            Platform::<DefaultCoreRPC>::query_proposed_block_counts_by_evonode_ids,
785            "query_proposed_block_counts_by_evonode_ids",
786        )
787        .await
788    }
789
790    async fn get_evonodes_proposed_epoch_blocks_by_range(
791        &self,
792        request: Request<GetEvonodesProposedEpochBlocksByRangeRequest>,
793    ) -> Result<Response<GetEvonodesProposedEpochBlocksResponse>, Status> {
794        self.handle_blocking_query(
795            request,
796            Platform::<DefaultCoreRPC>::query_proposed_block_counts_by_range,
797            "query_proposed_block_counts_by_range",
798        )
799        .await
800    }
801
802    async fn get_current_quorums_info(
803        &self,
804        request: Request<GetCurrentQuorumsInfoRequest>,
805    ) -> Result<Response<GetCurrentQuorumsInfoResponse>, Status> {
806        self.handle_blocking_query(
807            request,
808            Platform::<DefaultCoreRPC>::query_current_quorums_info,
809            "query_current_quorums_info",
810        )
811        .await
812    }
813
814    async fn get_identity_token_balances(
815        &self,
816        request: Request<GetIdentityTokenBalancesRequest>,
817    ) -> Result<Response<GetIdentityTokenBalancesResponse>, Status> {
818        self.handle_blocking_query(
819            request,
820            Platform::<DefaultCoreRPC>::query_identity_token_balances,
821            "query_identity_token_balances",
822        )
823        .await
824    }
825
826    async fn get_identities_token_balances(
827        &self,
828        request: Request<GetIdentitiesTokenBalancesRequest>,
829    ) -> Result<Response<GetIdentitiesTokenBalancesResponse>, Status> {
830        self.handle_blocking_query(
831            request,
832            Platform::<DefaultCoreRPC>::query_identities_token_balances,
833            "query_identities_token_balances",
834        )
835        .await
836    }
837
838    async fn get_identity_token_infos(
839        &self,
840        request: Request<GetIdentityTokenInfosRequest>,
841    ) -> Result<Response<GetIdentityTokenInfosResponse>, Status> {
842        self.handle_blocking_query(
843            request,
844            Platform::<DefaultCoreRPC>::query_identity_token_infos,
845            "query_identity_token_infos",
846        )
847        .await
848    }
849
850    async fn get_identities_token_infos(
851        &self,
852        request: Request<GetIdentitiesTokenInfosRequest>,
853    ) -> Result<Response<GetIdentitiesTokenInfosResponse>, Status> {
854        self.handle_blocking_query(
855            request,
856            Platform::<DefaultCoreRPC>::query_identities_token_infos,
857            "query_identities_token_infos",
858        )
859        .await
860    }
861
862    async fn get_token_statuses(
863        &self,
864        request: Request<GetTokenStatusesRequest>,
865    ) -> Result<Response<GetTokenStatusesResponse>, Status> {
866        self.handle_blocking_query(
867            request,
868            Platform::<DefaultCoreRPC>::query_token_statuses,
869            "get_token_statuses",
870        )
871        .await
872    }
873
874    async fn get_token_pre_programmed_distributions(
875        &self,
876        request: Request<GetTokenPreProgrammedDistributionsRequest>,
877    ) -> Result<Response<GetTokenPreProgrammedDistributionsResponse>, Status> {
878        self.handle_blocking_query(
879            request,
880            Platform::<DefaultCoreRPC>::query_token_pre_programmed_distributions,
881            "get_token_pre_programmed_distributions",
882        )
883        .await
884    }
885
886    async fn get_token_total_supply(
887        &self,
888        request: Request<GetTokenTotalSupplyRequest>,
889    ) -> Result<Response<GetTokenTotalSupplyResponse>, Status> {
890        self.handle_blocking_query(
891            request,
892            Platform::<DefaultCoreRPC>::query_token_total_supply,
893            "get_token_total_supply",
894        )
895        .await
896    }
897
898    async fn get_group_info(
899        &self,
900        request: Request<GetGroupInfoRequest>,
901    ) -> Result<Response<GetGroupInfoResponse>, Status> {
902        self.handle_blocking_query(
903            request,
904            Platform::<DefaultCoreRPC>::query_group_info,
905            "get_group_info",
906        )
907        .await
908    }
909
910    async fn get_group_infos(
911        &self,
912        request: Request<GetGroupInfosRequest>,
913    ) -> Result<Response<GetGroupInfosResponse>, Status> {
914        self.handle_blocking_query(
915            request,
916            Platform::<DefaultCoreRPC>::query_group_infos,
917            "get_group_infos",
918        )
919        .await
920    }
921
922    async fn get_group_actions(
923        &self,
924        request: Request<GetGroupActionsRequest>,
925    ) -> Result<Response<GetGroupActionsResponse>, Status> {
926        self.handle_blocking_query(
927            request,
928            Platform::<DefaultCoreRPC>::query_group_actions,
929            "get_group_actions",
930        )
931        .await
932    }
933
934    async fn get_group_action_signers(
935        &self,
936        request: Request<GetGroupActionSignersRequest>,
937    ) -> Result<Response<GetGroupActionSignersResponse>, Status> {
938        self.handle_blocking_query(
939            request,
940            Platform::<DefaultCoreRPC>::query_group_action_signers,
941            "get_group_action_signers",
942        )
943        .await
944    }
945
946    async fn get_token_direct_purchase_prices(
947        &self,
948        request: Request<GetTokenDirectPurchasePricesRequest>,
949    ) -> Result<Response<GetTokenDirectPurchasePricesResponse>, Status> {
950        self.handle_blocking_query(
951            request,
952            Platform::<DefaultCoreRPC>::query_token_direct_purchase_prices,
953            "get_token_direct_purchase_prices",
954        )
955        .await
956    }
957
958    async fn get_token_contract_info(
959        &self,
960        request: Request<GetTokenContractInfoRequest>,
961    ) -> Result<Response<GetTokenContractInfoResponse>, Status> {
962        self.handle_blocking_query(
963            request,
964            Platform::<DefaultCoreRPC>::query_token_contract_info,
965            "get_token_contract_info",
966        )
967        .await
968    }
969
970    async fn get_token_perpetual_distribution_last_claim(
971        &self,
972        request: Request<GetTokenPerpetualDistributionLastClaimRequest>,
973    ) -> Result<Response<GetTokenPerpetualDistributionLastClaimResponse>, Status> {
974        self.handle_blocking_query(
975            request,
976            Platform::<DefaultCoreRPC>::query_token_perpetual_distribution_last_claim,
977            "get_token_perpetual_distribution_last_claim",
978        )
979        .await
980    }
981
982    async fn get_finalized_epoch_infos(
983        &self,
984        request: Request<GetFinalizedEpochInfosRequest>,
985    ) -> Result<Response<GetFinalizedEpochInfosResponse>, Status> {
986        self.handle_blocking_query(
987            request,
988            Platform::<DefaultCoreRPC>::query_finalized_epoch_infos,
989            "get_finalized_epoch_infos",
990        )
991        .await
992    }
993
994    async fn get_address_info(
995        &self,
996        request: Request<GetAddressInfoRequest>,
997    ) -> Result<Response<GetAddressInfoResponse>, Status> {
998        self.handle_blocking_query(
999            request,
1000            Platform::<DefaultCoreRPC>::query_address_info,
1001            "get_address_info",
1002        )
1003        .await
1004    }
1005
1006    async fn get_addresses_infos(
1007        &self,
1008        request: Request<GetAddressesInfosRequest>,
1009    ) -> Result<Response<GetAddressesInfosResponse>, Status> {
1010        self.handle_blocking_query(
1011            request,
1012            Platform::<DefaultCoreRPC>::query_addresses_infos,
1013            "get_addresses_infos",
1014        )
1015        .await
1016    }
1017
1018    async fn get_addresses_trunk_state(
1019        &self,
1020        request: Request<GetAddressesTrunkStateRequest>,
1021    ) -> Result<Response<GetAddressesTrunkStateResponse>, Status> {
1022        self.handle_blocking_query(
1023            request,
1024            Platform::<DefaultCoreRPC>::query_addresses_trunk_state,
1025            "get_addresses_trunk_state",
1026        )
1027        .await
1028    }
1029
1030    async fn get_addresses_branch_state(
1031        &self,
1032        request: Request<GetAddressesBranchStateRequest>,
1033    ) -> Result<Response<GetAddressesBranchStateResponse>, Status> {
1034        self.handle_blocking_query(
1035            request,
1036            Platform::<DefaultCoreRPC>::query_addresses_branch_state,
1037            "get_addresses_branch_state",
1038        )
1039        .await
1040    }
1041
1042    async fn get_recent_address_balance_changes(
1043        &self,
1044        request: Request<GetRecentAddressBalanceChangesRequest>,
1045    ) -> Result<Response<GetRecentAddressBalanceChangesResponse>, Status> {
1046        self.handle_blocking_query(
1047            request,
1048            Platform::<DefaultCoreRPC>::query_recent_address_balance_changes,
1049            "get_recent_address_balance_changes",
1050        )
1051        .await
1052    }
1053
1054    async fn get_recent_compacted_address_balance_changes(
1055        &self,
1056        request: Request<GetRecentCompactedAddressBalanceChangesRequest>,
1057    ) -> Result<Response<GetRecentCompactedAddressBalanceChangesResponse>, Status> {
1058        self.handle_blocking_query(
1059            request,
1060            Platform::<DefaultCoreRPC>::query_recent_compacted_address_balance_changes,
1061            "get_recent_compacted_address_balance_changes",
1062        )
1063        .await
1064    }
1065
1066    async fn get_shielded_encrypted_notes(
1067        &self,
1068        request: Request<GetShieldedEncryptedNotesRequest>,
1069    ) -> Result<Response<GetShieldedEncryptedNotesResponse>, Status> {
1070        self.handle_blocking_query(
1071            request,
1072            Platform::<DefaultCoreRPC>::query_shielded_encrypted_notes,
1073            "get_shielded_encrypted_notes",
1074        )
1075        .await
1076    }
1077
1078    async fn get_shielded_anchors(
1079        &self,
1080        request: Request<GetShieldedAnchorsRequest>,
1081    ) -> Result<Response<GetShieldedAnchorsResponse>, Status> {
1082        self.handle_blocking_query(
1083            request,
1084            Platform::<DefaultCoreRPC>::query_shielded_anchors,
1085            "get_shielded_anchors",
1086        )
1087        .await
1088    }
1089
1090    async fn get_most_recent_shielded_anchor(
1091        &self,
1092        request: Request<GetMostRecentShieldedAnchorRequest>,
1093    ) -> Result<Response<GetMostRecentShieldedAnchorResponse>, Status> {
1094        self.handle_blocking_query(
1095            request,
1096            Platform::<DefaultCoreRPC>::query_most_recent_shielded_anchor,
1097            "get_most_recent_shielded_anchor",
1098        )
1099        .await
1100    }
1101
1102    async fn get_shielded_pool_state(
1103        &self,
1104        request: Request<GetShieldedPoolStateRequest>,
1105    ) -> Result<Response<GetShieldedPoolStateResponse>, Status> {
1106        self.handle_blocking_query(
1107            request,
1108            Platform::<DefaultCoreRPC>::query_shielded_pool_state,
1109            "get_shielded_pool_state",
1110        )
1111        .await
1112    }
1113
1114    async fn get_shielded_notes_count(
1115        &self,
1116        request: Request<GetShieldedNotesCountRequest>,
1117    ) -> Result<Response<GetShieldedNotesCountResponse>, Status> {
1118        self.handle_blocking_query(
1119            request,
1120            Platform::<DefaultCoreRPC>::query_shielded_notes_count,
1121            "get_shielded_notes_count",
1122        )
1123        .await
1124    }
1125
1126    async fn get_shielded_nullifiers(
1127        &self,
1128        request: Request<GetShieldedNullifiersRequest>,
1129    ) -> Result<Response<GetShieldedNullifiersResponse>, Status> {
1130        self.handle_blocking_query(
1131            request,
1132            Platform::<DefaultCoreRPC>::query_shielded_nullifiers,
1133            "get_shielded_nullifiers",
1134        )
1135        .await
1136    }
1137}
1138
1139#[async_trait]
1140impl DriveInternal for QueryService {
1141    async fn get_proofs(
1142        &self,
1143        request: Request<GetProofsRequest>,
1144    ) -> Result<Response<GetProofsResponse>, Status> {
1145        self.handle_blocking_query(
1146            request,
1147            Platform::<DefaultCoreRPC>::query_proofs,
1148            "get_proofs",
1149        )
1150        .await
1151    }
1152}
1153
1154fn query_error_into_status(error: QueryError) -> Status {
1155    match error {
1156        QueryError::NotFound(message) => Status::not_found(bounded_message(message)),
1157        QueryError::InvalidArgument(message) => Status::invalid_argument(bounded_message(message)),
1158        QueryError::Query(error) | QueryError::Drive(DriveError::Query(error)) => {
1159            Status::invalid_argument(bounded_message(error.to_string()))
1160        }
1161        QueryError::TooManyElements(message) => Status::invalid_argument(bounded_message(message)),
1162        QueryError::ResourceExhausted(message) => {
1163            Status::resource_exhausted(bounded_message(message))
1164        }
1165        // A feature the current protocol version does not have yet. Whether it is active
1166        // depends only on the protocol version, which every node at this height shares, not on
1167        // this node's build, so the refusal is no fault of this node and must not get it banned.
1168        error @ QueryError::Drive(DriveError::Drive(DriveInternalError::VersionNotActive {
1169            ..
1170        })) => Status::invalid_argument(bounded_message(error.to_string())),
1171        // A request this node cannot decode, or a request version it does not serve, may be
1172        // one a newer node serves, so these stay retryable on another node.
1173        _ => {
1174            tracing::error!("unexpected query error: {:?}", error);
1175
1176            Status::unknown(bounded_message(error.to_string()))
1177        }
1178    }
1179}
1180
1181/// The response, or the status, the service answers for a query method's result.
1182pub(super) fn query_result_into_response<T: Clone>(
1183    result: Result<QueryValidationResult<T>, Error>,
1184) -> Result<T, Status> {
1185    let mut query_result = result.map_err(error_into_status)?;
1186
1187    if query_result.is_valid() {
1188        query_result
1189            .into_data()
1190            .map_err(|error| error_into_status(error.into()))
1191    } else {
1192        let error = query_result.errors.swap_remove(0);
1193
1194        Err(query_error_into_status(error))
1195    }
1196}
1197
1198fn error_into_status(error: Error) -> Status {
1199    match error {
1200        // A query syntax error is the request's fault whichever layer finds it. As an internal
1201        // error, clients would retry it on every other node and stop using each one.
1202        Error::Drive(DriveError::Query(error)) => {
1203            Status::invalid_argument(bounded_message(error.to_string()))
1204        }
1205        // A feature the current protocol version does not have yet, refused alike by every node
1206        // at this height whatever its build.
1207        error @ (Error::Drive(DriveError::Drive(DriveInternalError::VersionNotActive {
1208            ..
1209        }))
1210        | Error::Execution(ExecutionError::VersionNotActive { .. })) => {
1211            Status::invalid_argument(bounded_message(format!("query: {}", error)))
1212        }
1213        error => Status::internal(bounded_message(format!("query: {}", error))),
1214    }
1215}
1216
1217fn bounded_message(mut message: String) -> String {
1218    if message.len() <= MAX_STATUS_MESSAGE_BYTES {
1219        return message;
1220    }
1221    let mut end = MAX_STATUS_MESSAGE_BYTES;
1222    while !message.is_char_boundary(end) {
1223        end -= 1;
1224    }
1225    let truncated = message.len() - end;
1226    message.truncate(end);
1227    message.push_str(&format!("... ({} bytes truncated)", truncated));
1228    message
1229}
1230
1231fn validate_path_elements_request(request: &GetPathElementsRequest) -> Result<(), Status> {
1232    let v0 = match request.version.as_ref() {
1233        Some(get_path_elements_request::Version::V0(v0)) => v0,
1234        None => return Err(Status::invalid_argument("missing request version")),
1235    };
1236    let max_keys = PlatformVersion::latest()
1237        .drive_abci
1238        .query
1239        .max_returned_elements as usize;
1240    if v0.path.len() > MAX_PATH_COMPONENTS || v0.keys.len() > max_keys {
1241        return Err(Status::resource_exhausted(
1242            "too many path or key components",
1243        ));
1244    }
1245
1246    let total_bytes = v0
1247        .path
1248        .iter()
1249        .chain(&v0.keys)
1250        .try_fold(0usize, |total, component| {
1251            if component.len() > MAX_GROVEDB_KEY_BYTES {
1252                return Err(Status::resource_exhausted(
1253                    "path or key component is too large",
1254                ));
1255            }
1256            total
1257                .checked_add(component.len())
1258                .ok_or_else(|| Status::resource_exhausted("path query size overflow"))
1259        })?;
1260
1261    if total_bytes > MAX_PATH_QUERY_BYTES {
1262        return Err(Status::resource_exhausted(
1263            "aggregate path query bytes exceed limit",
1264        ));
1265    }
1266
1267    Ok(())
1268}
1269
1270#[cfg(test)]
1271mod tests {
1272    use super::*;
1273    use dapi_grpc::platform::v0::get_path_elements_request::GetPathElementsRequestV0;
1274
1275    #[test]
1276    fn path_elements_request_is_bounded_before_debug_formatting() {
1277        let request = GetPathElementsRequest {
1278            version: Some(get_path_elements_request::Version::V0(
1279                GetPathElementsRequestV0 {
1280                    path: vec![vec![]; MAX_PATH_COMPONENTS + 1],
1281                    keys: vec![],
1282                    prove: false,
1283                },
1284            )),
1285        };
1286
1287        let status = validate_path_elements_request(&request)
1288            .expect_err("expected excessive path depth to be rejected");
1289        assert_eq!(status.code(), Code::ResourceExhausted);
1290    }
1291}
1292
1293#[cfg(test)]
1294mod query_error_status_tests {
1295    use super::*;
1296    use crate::query::tests::{assert_invalid_argument_status, setup_platform};
1297    use dapi_grpc::platform::v0::get_documents_request;
1298    use dapi_grpc::platform::v0::get_documents_request::GetDocumentsRequestV0;
1299    use dpp::dashcore::Network;
1300    use dpp::system_data_contracts::dpns_contract;
1301    use drive::error::query::QuerySyntaxError;
1302    use rs_dapi_client::CanRetry;
1303
1304    #[test]
1305    fn resource_exhausted_query_error_maps_to_retryable_grpc_status() {
1306        let status = query_error_into_status(QueryError::ResourceExhausted(
1307            "server-side retained state is over capacity".to_string(),
1308        ));
1309
1310        assert_eq!(status.code(), Code::ResourceExhausted);
1311    }
1312
1313    /// A query syntax error is the request's fault whether the handler or Drive found it.
1314    #[test]
1315    fn should_answer_a_query_syntax_error_from_drive_with_invalid_argument() {
1316        let syntax_error = || QuerySyntaxError::InvalidLimit("limit 0".to_string());
1317
1318        let from_result = error_into_status(Error::Drive(DriveError::Query(syntax_error())));
1319        assert_eq!(from_result.code(), Code::InvalidArgument);
1320        assert!(from_result.message().contains("limit 0"));
1321
1322        let from_validation =
1323            query_error_into_status(QueryError::Drive(DriveError::Query(syntax_error())));
1324        assert_eq!(from_validation.code(), Code::InvalidArgument);
1325    }
1326
1327    /// What the request did not cause stays retryable on another node: a fault inside the node,
1328    /// or a request this node cannot decode, which a newer node may serve.
1329    #[test]
1330    fn should_keep_node_faults_and_undecodable_requests_retryable() {
1331        let internal = error_into_status(Error::Drive(DriveError::Drive(
1332            DriveInternalError::CorruptedCodeExecution("broken"),
1333        )));
1334        assert_eq!(internal.code(), Code::Internal);
1335
1336        let undecodable = query_error_into_status(QueryError::DecodingError(
1337            "could not decode data contracts query".to_string(),
1338        ));
1339        assert_eq!(undecodable.code(), Code::Unknown);
1340    }
1341
1342    /// Every node at a height runs the same protocol version, so a feature that version does not
1343    /// have yet is refused by all of them alike, whichever route reports it.
1344    #[test]
1345    fn should_answer_a_feature_not_active_yet_with_invalid_argument() {
1346        let not_active = || DriveInternalError::VersionNotActive {
1347            method: "prove_identity_keys_remaining_budgets".to_string(),
1348            known_versions: vec![0],
1349        };
1350
1351        let from_validation =
1352            query_error_into_status(QueryError::Drive(DriveError::Drive(not_active())));
1353        assert_eq!(from_validation.code(), Code::InvalidArgument);
1354        assert!(from_validation
1355            .message()
1356            .contains("prove_identity_keys_remaining_budgets not active"));
1357
1358        let from_drive = error_into_status(Error::Drive(DriveError::Drive(not_active())));
1359        assert_eq!(from_drive.code(), Code::InvalidArgument);
1360
1361        let from_platform = error_into_status(Error::Execution(ExecutionError::VersionNotActive {
1362            method: "validate_identity_public_keys_limits".to_string(),
1363            known_versions: vec![0],
1364        }));
1365        assert_eq!(from_platform.code(), Code::InvalidArgument);
1366
1367        for status in [from_validation, from_drive, from_platform] {
1368            assert!(!status.can_retry(), "{status}");
1369        }
1370    }
1371
1372    /// A version the code does not know is a fault in this build, and a request version this
1373    /// node does not serve may be served by a newer node: both stay retryable on another node.
1374    #[test]
1375    fn should_keep_unknown_versions_and_unsupported_request_versions_retryable() {
1376        let unknown = || DriveInternalError::UnknownVersionMismatch {
1377            method: "prove_identity_keys_remaining_budgets".to_string(),
1378            known_versions: vec![0],
1379            received: 1,
1380        };
1381
1382        let from_validation =
1383            query_error_into_status(QueryError::Drive(DriveError::Drive(unknown())));
1384        assert_eq!(from_validation.code(), Code::Unknown);
1385
1386        let from_drive = error_into_status(Error::Drive(DriveError::Drive(unknown())));
1387        assert_eq!(from_drive.code(), Code::Internal);
1388
1389        let from_platform =
1390            error_into_status(Error::Execution(ExecutionError::UnknownVersionMismatch {
1391                method: "validate_identity_public_keys_limits".to_string(),
1392                known_versions: vec![0],
1393                received: 1,
1394            }));
1395        assert_eq!(from_platform.code(), Code::Internal);
1396
1397        let unsupported = query_error_into_status(QueryError::UnsupportedQueryVersion(
1398            "identity_keys_remaining_budgets".to_string(),
1399            0,
1400            0,
1401            14,
1402            1,
1403        ));
1404        assert_eq!(unsupported.code(), Code::Unknown);
1405
1406        for status in [from_validation, from_drive, from_platform, unsupported] {
1407            assert!(status.can_retry(), "{status}");
1408        }
1409    }
1410
1411    #[test]
1412    fn should_bound_a_long_status_message_at_a_char_boundary() {
1413        // A two-byte character straddles the bound, so the cut falls one byte short of it.
1414        let message = format!(
1415            "{}{}",
1416            "a".repeat(MAX_STATUS_MESSAGE_BYTES - 1),
1417            "é".repeat(100)
1418        );
1419
1420        let status = query_error_into_status(QueryError::InvalidArgument(message.clone()));
1421
1422        assert_eq!(status.code(), Code::InvalidArgument);
1423        let kept = &message[..MAX_STATUS_MESSAGE_BYTES - 1];
1424        assert_eq!(
1425            status.message(),
1426            format!(
1427                "{}... ({} bytes truncated)",
1428                kept,
1429                message.len() - kept.len()
1430            )
1431        );
1432
1433        let short = query_error_into_status(QueryError::NotFound("absent".to_string()));
1434        assert_eq!(short.message(), "absent");
1435    }
1436
1437    /// An error echoing a 100 KB document type name used to go out whole in the
1438    /// `grpc-message` header, past what HTTP/2 peers accept.
1439    #[test]
1440    fn should_bound_an_error_that_echoes_an_oversized_document_type_name() {
1441        let (platform, state, version) = setup_platform(Some((1, 1)), Network::Testnet, None);
1442
1443        let request = GetDocumentsRequest {
1444            version: Some(get_documents_request::Version::V0(GetDocumentsRequestV0 {
1445                data_contract_id: dpns_contract::ID.to_vec(),
1446                document_type: "x".repeat(100_000),
1447                r#where: vec![],
1448                order_by: vec![],
1449                limit: 0,
1450                start: None,
1451                prove: false,
1452            })),
1453        };
1454
1455        let status =
1456            assert_invalid_argument_status(platform.query_documents(request, &state, version));
1457        assert!(
1458            status.message().len() < MAX_STATUS_MESSAGE_BYTES + 64,
1459            "message is {} bytes",
1460            status.message().len()
1461        );
1462    }
1463}