1mod address_funds;
2mod contract_group_queries;
3mod contract_moderation_queries;
4mod data_contract_based_queries;
5mod document_history;
6mod document_query;
7mod group_queries;
8mod identity_based_queries;
9mod prefunded_specialized_balances;
10mod proofs;
11mod response_metadata;
12mod service;
13mod shielded;
14mod system;
15mod token_queries;
16mod validator_queries;
17mod voting;
18
19use crate::error::query::QueryError;
20
21use dpp::validation::ValidationResult;
22
23pub use service::QueryService;
24
25pub type QueryValidationResult<TData> = ValidationResult<TData, QueryError>;
27
28#[cfg(test)]
29pub(crate) mod tests {
30 use crate::error::query::QueryError;
31 use crate::error::Error;
32 use crate::platform_types::platform::Platform;
33 use crate::platform_types::platform_state::PlatformState;
34 use crate::platform_types::platform_state::PlatformStateV0Methods;
35 use crate::query::service::query_result_into_response;
36 use crate::query::QueryValidationResult;
37 use crate::rpc::core::MockCoreRPCLike;
38 use crate::test::helpers::setup::{TempPlatform, TestPlatformBuilder};
39 use dapi_grpc::tonic::{Code, Status};
40 use dpp::block::block_info::BlockInfo;
41 use dpp::data_contract::DataContract;
42
43 use crate::config::PlatformConfig;
44 use dpp::dashcore::Network;
45 use dpp::data_contract::accessors::v0::{DataContractV0Getters, DataContractV0Setters};
46 use dpp::data_contract::config::moderation::{
47 ContractDocumentRemoval, ContractModerationConfig, ContractModerationReason,
48 ContractModerators,
49 };
50 use dpp::data_contract::document_type::DocumentTypeRef;
51 use dpp::data_contract::schema::DataContractSchemaMethodsV0;
52 use dpp::document::Document;
53 use dpp::identifier::Identifier;
54 use dpp::platform_value::Value;
55 use dpp::prelude::{CoreBlockHeight, TimestampMillis};
56 use drive::util::batch::DriveOperation::{
57 ContractModerationOperation, DataContractOperation, DocumentOperation,
58 };
59 use drive::util::batch::{
60 ContractModerationOperationType, DataContractOperationType, DocumentOperationType,
61 };
62 use drive::util::object_size_info::{
63 DataContractInfo, DocumentInfo, DocumentTypeInfo, OwnedDocumentInfo,
64 };
65 use drive::util::storage_flags::StorageFlags;
66 use platform_version::version::{PlatformVersion, ProtocolVersion};
67 use std::borrow::Cow;
68 use std::collections::BTreeMap;
69 use std::sync::Arc;
70
71 pub fn setup_platform<'a>(
72 with_genesis_state: Option<(TimestampMillis, CoreBlockHeight)>,
73 network: Network,
74 initial_protocol_version: Option<ProtocolVersion>,
75 ) -> (
76 TempPlatform<MockCoreRPCLike>,
77 Arc<PlatformState>,
78 &'a PlatformVersion,
79 ) {
80 let platform = if let Some((timestamp, activation_core_block_height)) = with_genesis_state {
81 let mut platform_builder = TestPlatformBuilder::new()
82 .with_config(PlatformConfig::default_for_network(network));
83
84 if let Some(initial_protocol_version) = initial_protocol_version {
85 platform_builder =
86 platform_builder.with_initial_protocol_version(initial_protocol_version);
87 }
88
89 platform_builder
90 .build_with_mock_rpc()
91 .set_genesis_state_with_activation_info(timestamp, activation_core_block_height)
92 } else {
93 let mut platform_builder = TestPlatformBuilder::new()
94 .with_config(PlatformConfig::default_for_network(network));
95
96 if let Some(initial_protocol_version) = initial_protocol_version {
97 platform_builder =
98 platform_builder.with_initial_protocol_version(initial_protocol_version);
99 }
100
101 platform_builder
102 .build_with_mock_rpc()
103 .set_initial_state_structure()
104 };
105
106 let platform_state = platform.platform.state.load_full();
109
110 let platform_version = platform_state.current_platform_version().unwrap();
111
112 (platform, platform_state, platform_version)
113 }
114
115 pub fn store_data_contract(
116 platform: &Platform<MockCoreRPCLike>,
117 data_contract: &DataContract,
118 platform_version: &PlatformVersion,
119 ) {
120 let operation = DataContractOperation(DataContractOperationType::ApplyContract {
121 contract: Cow::Owned(data_contract.to_owned()),
122 storage_flags: None,
123 });
124
125 let block_info = BlockInfo::genesis();
126
127 platform
128 .drive
129 .apply_drive_operations(
130 vec![operation],
131 true,
132 &block_info,
133 None,
134 platform_version,
135 None,
136 )
137 .expect("expected to apply drive operations");
138 }
139
140 pub fn with_moderated_posts(
145 mut contract: DataContract,
146 platform_version: &PlatformVersion,
147 ) -> DataContract {
148 contract.set_config(contract.config().clone().with_moderation(Some(
149 ContractModerationConfig {
150 banlist: false,
151 suspensions: false,
152 warnings: false,
153 moderators: ContractModerators::ContractOwner,
154 },
155 )));
156 let mut schemas = BTreeMap::new();
157 for (name, schema) in contract.document_schemas() {
158 let mut json: serde_json::Value = schema.clone().try_into().expect("a JSON schema");
159 if name == "post" {
160 json["canBeDeleted"] = false.into();
161 json["moderatorAbilities"] = serde_json::json!({ "delete": true });
162 }
163 if let Some(properties) = json
164 .get_mut("properties")
165 .and_then(|properties| properties.as_object_mut())
166 {
167 for property in properties.values_mut() {
168 if property["refersTo"]["documentType"] == "post" {
169 property["refersTo"]["type"] = "moderatedDocument".into();
170 }
171 }
172 }
173 if let Some(indices) = json
174 .get_mut("indices")
175 .and_then(|indices| indices.as_array_mut())
176 {
177 for index in indices {
178 if let Some(index) = index.as_object_mut() {
179 index.remove("preallocated");
180 }
181 }
182 }
183 schemas.insert(name, Value::from(json));
184 }
185 let defs = contract.schema_defs().cloned();
186 contract
187 .set_document_schemas(schemas, defs, true, &mut vec![], platform_version)
188 .expect("expected the moderated variant to parse");
189 contract
190 }
191
192 pub fn removal_of(id: [u8; 32], owner: [u8; 32]) -> ContractDocumentRemoval {
194 ContractDocumentRemoval {
195 document_owner_id: Identifier::from(owner),
196 moderator_id: Identifier::from([0x77; 32]),
197 reason: ContractModerationReason::from_text("spam"),
198 removed_at: 1_000 + id[0] as u64,
199 document_hash: id,
200 restoration: None,
201 kept_fields: Default::default(),
202 }
203 }
204
205 pub fn remove_post_by_moderator(
208 platform: &Platform<MockCoreRPCLike>,
209 data_contract: &DataContract,
210 id: [u8; 32],
211 removal: ContractDocumentRemoval,
212 platform_version: &PlatformVersion,
213 ) {
214 let moderator_id = removal.moderator_id;
215 let operations = vec![
216 DocumentOperation(DocumentOperationType::ForceDeleteDocument {
217 document_id: Identifier::from(id),
218 contract_info: DataContractInfo::BorrowedDataContract(data_contract),
219 document_type_info: DocumentTypeInfo::DocumentTypeName("post".to_string()),
220 }),
221 ContractModerationOperation(ContractModerationOperationType::AddDocumentRemoval {
222 contract_id: data_contract.id(),
223 document_type_name: "post".to_string(),
224 document_id: Identifier::from(id),
225 removal: Box::new(removal),
226 replaced_record_size: None,
227 estimated_kept_fields_size: 0,
228 moderator_id,
229 }),
230 ContractModerationOperation(ContractModerationOperationType::ForfeitStorageRefunds),
231 ];
232 platform
233 .drive
234 .apply_drive_operations(
235 operations,
236 true,
237 &BlockInfo::genesis(),
238 None,
239 platform_version,
240 None,
241 )
242 .expect("expected to remove the post");
243 }
244
245 pub fn store_document(
246 platform: &Platform<MockCoreRPCLike>,
247 data_contract: &DataContract,
248 document_type: DocumentTypeRef,
249 document: &Document,
250 platform_version: &PlatformVersion,
251 ) {
252 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
253
254 let operation = DocumentOperation(DocumentOperationType::AddDocument {
255 owned_document_info: OwnedDocumentInfo {
256 document_info: DocumentInfo::DocumentRefInfo((document, storage_flags)),
257 owner_id: None,
258 },
259 contract_info: DataContractInfo::BorrowedDataContract(data_contract),
260 document_type_info: DocumentTypeInfo::DocumentTypeRef(document_type),
261 override_document: false,
262 });
263
264 let block_info = BlockInfo::genesis();
265
266 platform
267 .drive
268 .apply_drive_operations(
269 vec![operation],
270 true,
271 &block_info,
272 None,
273 platform_version,
274 None,
275 )
276 .expect("expected to apply drive operations");
277 }
278
279 pub fn assert_invalid_argument_status<TData: Clone>(
282 result: Result<QueryValidationResult<TData>, Error>,
283 ) -> Status {
284 let status = query_result_into_response(result)
285 .err()
286 .expect("expected the request to be refused");
287 assert_eq!(status.code(), Code::InvalidArgument, "{}", status.message());
288 status
289 }
290
291 pub fn assert_invalid_identifier<TData: Clone>(
292 validation_result: QueryValidationResult<TData>,
293 ) {
294 assert!(matches!(
295 validation_result.errors.as_slice(),
296 [QueryError::InvalidArgument(msg)] if msg.contains("id must be a valid identifier (32 bytes long)")
297 ));
298 }
299
300 #[allow(clippy::type_complexity)]
310 pub fn setup_platform_with_token_state<'a>() -> (
311 TempPlatform<MockCoreRPCLike>,
312 Arc<PlatformState>,
313 &'a PlatformVersion,
314 [u8; 32],
315 [[u8; 32]; 3],
316 [[u8; 32]; 3],
317 ) {
318 use dpp::data_contract::associated_token::token_configuration::v0::TokenConfigurationV0;
319 use dpp::data_contract::associated_token::token_configuration_convention::v0::TokenConfigurationConventionV0;
320 use dpp::data_contract::associated_token::token_distribution_rules::v0::TokenDistributionRulesV0;
321 use dpp::data_contract::associated_token::token_distribution_rules::TokenDistributionRules;
322 use dpp::data_contract::associated_token::token_keeps_history_rules::v0::TokenKeepsHistoryRulesV0;
323 use dpp::data_contract::associated_token::token_marketplace_rules::v0::TokenMarketplaceRulesV0;
324 use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_function::DistributionFunction;
325 use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_recipient::TokenDistributionRecipient;
326 use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_type::RewardDistributionType;
327 use dpp::data_contract::associated_token::token_perpetual_distribution::v0::TokenPerpetualDistributionV0;
328 use dpp::data_contract::associated_token::token_perpetual_distribution::TokenPerpetualDistribution;
329 use dpp::data_contract::associated_token::token_pre_programmed_distribution::v0::TokenPreProgrammedDistributionV0;
330 use dpp::data_contract::associated_token::token_pre_programmed_distribution::TokenPreProgrammedDistribution;
331 use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers;
332 use dpp::data_contract::change_control_rules::v0::ChangeControlRulesV0;
333 use dpp::data_contract::config::DataContractConfig;
334 use dpp::data_contract::group::v0::GroupV0;
335 use dpp::data_contract::group::Group;
336 use dpp::data_contract::v1::DataContractV1;
337 use dpp::data_contract::{TokenConfiguration, INITIAL_DATA_CONTRACT_VERSION};
338 use dpp::identifier::Identifier;
339 use dpp::identity::accessors::IdentitySettersV0;
340 use dpp::identity::Identity;
341 use dpp::prelude::IdentityPublicKey;
342 use dpp::tokens::calculate_token_id;
343 use dpp::tokens::status::v0::TokenStatusV0;
344 use dpp::tokens::status::TokenStatus;
345 use dpp::tokens::token_pricing_schedule::TokenPricingSchedule;
346 use rand::rngs::StdRng;
347 use rand::SeedableRng;
348 use std::collections::BTreeMap;
349 use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
350
351 let (platform, state, version) = setup_platform(Some((1, 1)), Network::Testnet, None);
352
353 let identity_id_1 = [1u8; 32];
354 let identity_id_2 = [2u8; 32];
355 let identity_id_3 = [3u8; 32];
356 let contract_id = [3u8; 32];
357
358 let token_id_0 = calculate_token_id(&contract_id, 0);
359 let token_id_1 = calculate_token_id(&contract_id, 1);
360 let token_id_2 = calculate_token_id(&contract_id, 2);
361
362 let credits_per_identity: u64 = 10_000_000_000_000;
364 let total_credits = credits_per_identity * 3;
365 platform
366 .platform
367 .drive
368 .add_to_system_credits(total_credits, None, version)
369 .expect("expected to add system credits");
370
371 let mut rng = StdRng::seed_from_u64(0u64);
372 let non_unique_key = IdentityPublicKey::random_voting_key_with_rng(11, &mut rng, version)
373 .expect("expected to create voting key");
374
375 for id_bytes in [identity_id_1, identity_id_2, identity_id_3] {
376 let id = Identifier::new(id_bytes);
377 let mut identity =
378 Identity::create_basic_identity(id, version).expect("expected to create identity");
379 identity.set_balance(credits_per_identity);
380
381 let seed = id_bytes[0];
382 let mut rng = StdRng::seed_from_u64(seed as u64);
383 let mut keys =
384 IdentityPublicKey::main_keys_with_random_authentication_keys_with_private_keys_with_rng(
385 3, &mut rng, version,
386 )
387 .expect("expected to create keys");
388
389 let transfer_key =
390 IdentityPublicKey::random_masternode_transfer_key_with_rng(3, &mut rng, version)
391 .expect("expected to create transfer key");
392 keys.push(transfer_key);
393 keys.push(non_unique_key.clone());
394
395 identity.set_public_keys(keys.into_iter().map(|(key, _)| (key.id(), key)).collect());
396
397 platform
398 .platform
399 .drive
400 .add_new_identity(identity, false, &BlockInfo::genesis(), true, None, version)
401 .expect("expected to add identity");
402 }
403
404 let groups = [
406 (
407 0,
408 Group::V0(GroupV0 {
409 members: [
410 (Identifier::new(identity_id_1), 1),
411 (Identifier::new(identity_id_2), 1),
412 ]
413 .into(),
414 required_power: 1,
415 }),
416 ),
417 (
418 1,
419 Group::V0(GroupV0 {
420 members: [
421 (Identifier::new(identity_id_1), 1),
422 (Identifier::new(identity_id_2), 1),
423 (Identifier::new(identity_id_3), 1),
424 ]
425 .into(),
426 required_power: 3,
427 }),
428 ),
429 (
430 2,
431 Group::V0(GroupV0 {
432 members: [
433 (Identifier::new(identity_id_1), 1),
434 (Identifier::new(identity_id_3), 1),
435 ]
436 .into(),
437 required_power: 1,
438 }),
439 ),
440 ]
441 .into();
442
443 let token_configuration = TokenConfiguration::V0(TokenConfigurationV0 {
444 conventions: TokenConfigurationConventionV0 {
445 localizations: Default::default(),
446 decimals: 8,
447 }
448 .into(),
449 conventions_change_rules: ChangeControlRulesV0::default().into(),
450 base_supply: 100000,
451 max_supply: None,
452 keeps_history: TokenKeepsHistoryRulesV0::default().into(),
453 start_as_paused: false,
454 allow_transfer_to_frozen_balance: true,
455 max_supply_change_rules: ChangeControlRulesV0 {
456 authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
457 admin_action_takers: Default::default(),
458 changing_authorized_action_takers_to_no_one_allowed: false,
459 changing_admin_action_takers_to_no_one_allowed: false,
460 self_changing_admin_action_takers_allowed: false,
461 }
462 .into(),
463 distribution_rules: TokenDistributionRulesV0 {
464 perpetual_distribution: Some(TokenPerpetualDistribution::V0(
465 TokenPerpetualDistributionV0 {
466 distribution_type: RewardDistributionType::BlockBasedDistribution {
467 interval: 10,
468 function: DistributionFunction::FixedAmount { amount: 100 },
469 },
470 distribution_recipient: TokenDistributionRecipient::ContractOwner,
471 },
472 )),
473 perpetual_distribution_rules: ChangeControlRulesV0::default().into(),
474 pre_programmed_distribution: None,
475 new_tokens_destination_identity: None,
476 new_tokens_destination_identity_rules: ChangeControlRulesV0::default().into(),
477 minting_allow_choosing_destination: true,
478 minting_allow_choosing_destination_rules: ChangeControlRulesV0::default().into(),
479 change_direct_purchase_pricing_rules: ChangeControlRulesV0 {
480 authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
481 admin_action_takers: Default::default(),
482 changing_authorized_action_takers_to_no_one_allowed: false,
483 changing_admin_action_takers_to_no_one_allowed: false,
484 self_changing_admin_action_takers_allowed: false,
485 }
486 .into(),
487 }
488 .into(),
489 marketplace_rules: TokenMarketplaceRulesV0 {
490 trade_mode: Default::default(),
491 trade_mode_change_rules: ChangeControlRulesV0::default().into(),
492 }
493 .into(),
494 manual_minting_rules: ChangeControlRulesV0 {
495 authorized_to_make_change: AuthorizedActionTakers::Group(0),
496 admin_action_takers: Default::default(),
497 changing_authorized_action_takers_to_no_one_allowed: false,
498 changing_admin_action_takers_to_no_one_allowed: false,
499 self_changing_admin_action_takers_allowed: false,
500 }
501 .into(),
502 manual_burning_rules: ChangeControlRulesV0 {
503 authorized_to_make_change: AuthorizedActionTakers::Group(2),
504 admin_action_takers: Default::default(),
505 changing_authorized_action_takers_to_no_one_allowed: false,
506 changing_admin_action_takers_to_no_one_allowed: false,
507 self_changing_admin_action_takers_allowed: false,
508 }
509 .into(),
510 freeze_rules: ChangeControlRulesV0 {
511 authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
512 admin_action_takers: Default::default(),
513 changing_authorized_action_takers_to_no_one_allowed: false,
514 changing_admin_action_takers_to_no_one_allowed: false,
515 self_changing_admin_action_takers_allowed: false,
516 }
517 .into(),
518 unfreeze_rules: ChangeControlRulesV0 {
519 authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
520 admin_action_takers: Default::default(),
521 changing_authorized_action_takers_to_no_one_allowed: false,
522 changing_admin_action_takers_to_no_one_allowed: false,
523 self_changing_admin_action_takers_allowed: false,
524 }
525 .into(),
526 destroy_frozen_funds_rules: ChangeControlRulesV0 {
527 authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
528 admin_action_takers: Default::default(),
529 changing_authorized_action_takers_to_no_one_allowed: false,
530 changing_admin_action_takers_to_no_one_allowed: false,
531 self_changing_admin_action_takers_allowed: false,
532 }
533 .into(),
534 emergency_action_rules: ChangeControlRulesV0 {
535 authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
536 admin_action_takers: Default::default(),
537 changing_authorized_action_takers_to_no_one_allowed: false,
538 changing_admin_action_takers_to_no_one_allowed: false,
539 self_changing_admin_action_takers_allowed: false,
540 }
541 .into(),
542 main_control_group: None,
543 main_control_group_can_be_modified: Default::default(),
544 description: Some("Some token description".to_string()),
545 });
546
547 let mut token_configuration_2 = token_configuration.clone();
549 if let TokenConfiguration::V0(ref mut cfg) = token_configuration_2 {
550 if let TokenDistributionRules::V0(ref mut rules) = cfg.distribution_rules {
551 rules.pre_programmed_distribution = Some(TokenPreProgrammedDistribution::V0(
552 TokenPreProgrammedDistributionV0 {
553 distributions: BTreeMap::from([
554 (
555 1000,
556 BTreeMap::from([
557 (Identifier::new(identity_id_1), 500),
558 (Identifier::new(identity_id_2), 300),
559 ]),
560 ),
561 (
562 5000,
563 BTreeMap::from([(Identifier::new(identity_id_1), 1000)]),
564 ),
565 (
566 10000,
567 BTreeMap::from([
568 (Identifier::new(identity_id_2), 750),
569 (Identifier::new(identity_id_3), 250),
570 ]),
571 ),
572 ]),
573 },
574 ));
575 }
576 }
577
578 let tokens = [
579 (0, token_configuration.clone()),
580 (1, token_configuration),
581 (2, token_configuration_2),
582 ]
583 .into();
584
585 let data_contract = DataContract::V1(DataContractV1 {
586 id: Identifier::new(contract_id),
587 config: DataContractConfig::default_for_version(version)
588 .expect("expected contract config"),
589 version: INITIAL_DATA_CONTRACT_VERSION,
590 owner_id: Identifier::new(identity_id_1),
591 document_types: Default::default(),
592 schema_defs: Default::default(),
593 created_at: None,
594 updated_at: None,
595 created_at_block_height: None,
596 updated_at_block_height: None,
597 created_at_epoch: None,
598 updated_at_epoch: None,
599 groups,
600 tokens,
601 keywords: vec!["cat".into(), "white".into()],
602 description: Some("Some contract description".to_string()),
603 });
604
605 let block_info = BlockInfo::genesis();
606
607 platform
608 .platform
609 .drive
610 .apply_contract(&data_contract, block_info, true, None, None, version)
611 .expect("expected to apply contract");
612
613 for (token_id, id, amount) in [
618 (token_id_0, identity_id_1, 100u64),
619 (token_id_0, identity_id_2, 100),
620 (token_id_1, identity_id_1, 200),
621 (token_id_1, identity_id_3, 100),
622 (token_id_2, identity_id_1, 300),
623 (token_id_2, identity_id_2, 200),
624 ] {
625 platform
626 .platform
627 .drive
628 .token_mint(
629 token_id,
630 id,
631 amount,
632 false,
633 false,
634 &block_info,
635 true,
636 None,
637 version,
638 )
639 .expect("expected to mint tokens");
640 }
641
642 platform
644 .platform
645 .drive
646 .token_freeze(
647 Identifier::new(token_id_0),
648 Identifier::new(identity_id_2),
649 &block_info,
650 true,
651 None,
652 version,
653 )
654 .expect("expected to freeze");
655
656 let status = TokenStatus::V0(TokenStatusV0 { paused: true });
658 platform
659 .platform
660 .drive
661 .token_apply_status(token_id_1, status, &block_info, true, None, version)
662 .expect("expected to apply status");
663
664 platform
666 .platform
667 .drive
668 .token_set_direct_purchase_price(
669 token_id_1,
670 Some(TokenPricingSchedule::SinglePrice(25)),
671 &block_info,
672 true,
673 None,
674 version,
675 )
676 .expect("expected to set direct purchase price");
677
678 let pricing = TokenPricingSchedule::SetPrices(
679 (0..=900)
680 .step_by(100)
681 .map(|amount| (amount, 1000 - amount))
682 .collect(),
683 );
684 platform
685 .platform
686 .drive
687 .token_set_direct_purchase_price(
688 token_id_2,
689 Some(pricing),
690 &block_info,
691 true,
692 None,
693 version,
694 )
695 .expect("expected to set direct purchase price");
696
697 (
698 platform,
699 state,
700 version,
701 contract_id,
702 [token_id_0, token_id_1, token_id_2],
703 [identity_id_1, identity_id_2, identity_id_3],
704 )
705 }
706}