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;
89const MAX_STATUS_MESSAGE_BYTES: usize = 1024;
95
96pub 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 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 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 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 break;
195 } else {
196 query_counter += 1;
197
198 if query_counter > 2 {
199 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 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 let label = abci_response_code_metric_label(code);
223 response_duration_metric.add_label(label);
224
225 match code {
226 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 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 error @ QueryError::Drive(DriveError::Drive(DriveInternalError::VersionNotActive {
1169 ..
1170 })) => Status::invalid_argument(bounded_message(error.to_string())),
1171 _ => {
1174 tracing::error!("unexpected query error: {:?}", error);
1175
1176 Status::unknown(bounded_message(error.to_string()))
1177 }
1178 }
1179}
1180
1181pub(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 Error::Drive(DriveError::Query(error)) => {
1203 Status::invalid_argument(bounded_message(error.to_string()))
1204 }
1205 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 #[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 #[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 #[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 #[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 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 #[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}