drive_abci/query/
mod.rs

1mod address_funds;
2mod data_contract_based_queries;
3mod document_query;
4mod group_queries;
5mod identity_based_queries;
6mod prefunded_specialized_balances;
7mod proofs;
8mod response_metadata;
9mod service;
10mod shielded;
11mod system;
12mod token_queries;
13mod validator_queries;
14mod voting;
15
16use crate::error::query::QueryError;
17
18use dpp::validation::ValidationResult;
19
20pub use service::QueryService;
21
22/// A query validation result
23pub type QueryValidationResult<TData> = ValidationResult<TData, QueryError>;
24
25#[cfg(test)]
26pub(crate) mod tests {
27    use crate::error::query::QueryError;
28    use crate::platform_types::platform::Platform;
29    use crate::platform_types::platform_state::PlatformState;
30    use crate::platform_types::platform_state::PlatformStateV0Methods;
31    use crate::query::QueryValidationResult;
32    use crate::rpc::core::MockCoreRPCLike;
33    use crate::test::helpers::setup::{TempPlatform, TestPlatformBuilder};
34    use dpp::block::block_info::BlockInfo;
35    use dpp::data_contract::DataContract;
36
37    use crate::config::PlatformConfig;
38    use dpp::dashcore::Network;
39    use dpp::data_contract::document_type::DocumentTypeRef;
40    use dpp::document::Document;
41    use dpp::prelude::{CoreBlockHeight, TimestampMillis};
42    use drive::util::batch::DriveOperation::{DataContractOperation, DocumentOperation};
43    use drive::util::batch::{DataContractOperationType, DocumentOperationType};
44    use drive::util::object_size_info::{
45        DataContractInfo, DocumentInfo, DocumentTypeInfo, OwnedDocumentInfo,
46    };
47    use drive::util::storage_flags::StorageFlags;
48    use platform_version::version::{PlatformVersion, ProtocolVersion};
49    use std::borrow::Cow;
50    use std::sync::Arc;
51
52    pub fn setup_platform<'a>(
53        with_genesis_state: Option<(TimestampMillis, CoreBlockHeight)>,
54        network: Network,
55        initial_protocol_version: Option<ProtocolVersion>,
56    ) -> (
57        TempPlatform<MockCoreRPCLike>,
58        Arc<PlatformState>,
59        &'a PlatformVersion,
60    ) {
61        let platform = if let Some((timestamp, activation_core_block_height)) = with_genesis_state {
62            let mut platform_builder = TestPlatformBuilder::new()
63                .with_config(PlatformConfig::default_for_network(network));
64
65            if let Some(initial_protocol_version) = initial_protocol_version {
66                platform_builder =
67                    platform_builder.with_initial_protocol_version(initial_protocol_version);
68            }
69
70            platform_builder
71                .build_with_mock_rpc()
72                .set_genesis_state_with_activation_info(timestamp, activation_core_block_height)
73        } else {
74            let mut platform_builder = TestPlatformBuilder::new()
75                .with_config(PlatformConfig::default_for_network(network));
76
77            if let Some(initial_protocol_version) = initial_protocol_version {
78                platform_builder =
79                    platform_builder.with_initial_protocol_version(initial_protocol_version);
80            }
81
82            platform_builder
83                .build_with_mock_rpc()
84                .set_initial_state_structure()
85        };
86
87        // We can't return a reference to Arc (`load` method) so we clone Arc (`load_full`).
88        // This is a bit slower, but we don't care since we are in test environment
89        let platform_state = platform.platform.state.load_full();
90
91        let platform_version = platform_state.current_platform_version().unwrap();
92
93        (platform, platform_state, platform_version)
94    }
95
96    pub fn store_data_contract(
97        platform: &Platform<MockCoreRPCLike>,
98        data_contract: &DataContract,
99        platform_version: &PlatformVersion,
100    ) {
101        let operation = DataContractOperation(DataContractOperationType::ApplyContract {
102            contract: Cow::Owned(data_contract.to_owned()),
103            storage_flags: None,
104        });
105
106        let block_info = BlockInfo::genesis();
107
108        platform
109            .drive
110            .apply_drive_operations(
111                vec![operation],
112                true,
113                &block_info,
114                None,
115                platform_version,
116                None,
117            )
118            .expect("expected to apply drive operations");
119    }
120
121    pub fn store_document(
122        platform: &Platform<MockCoreRPCLike>,
123        data_contract: &DataContract,
124        document_type: DocumentTypeRef,
125        document: &Document,
126        platform_version: &PlatformVersion,
127    ) {
128        let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
129
130        let operation = DocumentOperation(DocumentOperationType::AddDocument {
131            owned_document_info: OwnedDocumentInfo {
132                document_info: DocumentInfo::DocumentRefInfo((document, storage_flags)),
133                owner_id: None,
134            },
135            contract_info: DataContractInfo::BorrowedDataContract(data_contract),
136            document_type_info: DocumentTypeInfo::DocumentTypeRef(document_type),
137            override_document: false,
138        });
139
140        let block_info = BlockInfo::genesis();
141
142        platform
143            .drive
144            .apply_drive_operations(
145                vec![operation],
146                true,
147                &block_info,
148                None,
149                platform_version,
150                None,
151            )
152            .expect("expected to apply drive operations");
153    }
154
155    pub fn assert_invalid_identifier<TData: Clone>(
156        validation_result: QueryValidationResult<TData>,
157    ) {
158        assert!(matches!(
159            validation_result.errors.as_slice(),
160            [QueryError::InvalidArgument(msg)] if msg.contains("id must be a valid identifier (32 bytes long)")
161        ));
162    }
163
164    /// Set up a platform with token state for testing token queries.
165    ///
166    /// Creates 3 identities and a data contract with 3 tokens.
167    /// Token 0: minted 100 to identity 1, 100 to identity 2. Identity 2 frozen on token 0.
168    /// Token 1: minted 200 to identity 1, 100 to identity 3. Token 1 is paused.
169    /// Token 2: minted 300 to identity 1, 200 to identity 2. Has pre-programmed distributions.
170    ///
171    /// Returns (platform, state, version, contract_id, [token_id_0, token_id_1, token_id_2],
172    ///          [identity_id_1, identity_id_2, identity_id_3])
173    #[allow(clippy::type_complexity)]
174    pub fn setup_platform_with_token_state<'a>() -> (
175        TempPlatform<MockCoreRPCLike>,
176        Arc<PlatformState>,
177        &'a PlatformVersion,
178        [u8; 32],
179        [[u8; 32]; 3],
180        [[u8; 32]; 3],
181    ) {
182        use dpp::data_contract::associated_token::token_configuration::v0::TokenConfigurationV0;
183        use dpp::data_contract::associated_token::token_configuration_convention::v0::TokenConfigurationConventionV0;
184        use dpp::data_contract::associated_token::token_distribution_rules::v0::TokenDistributionRulesV0;
185        use dpp::data_contract::associated_token::token_distribution_rules::TokenDistributionRules;
186        use dpp::data_contract::associated_token::token_keeps_history_rules::v0::TokenKeepsHistoryRulesV0;
187        use dpp::data_contract::associated_token::token_marketplace_rules::v0::TokenMarketplaceRulesV0;
188        use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_function::DistributionFunction;
189        use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_recipient::TokenDistributionRecipient;
190        use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_type::RewardDistributionType;
191        use dpp::data_contract::associated_token::token_perpetual_distribution::v0::TokenPerpetualDistributionV0;
192        use dpp::data_contract::associated_token::token_perpetual_distribution::TokenPerpetualDistribution;
193        use dpp::data_contract::associated_token::token_pre_programmed_distribution::v0::TokenPreProgrammedDistributionV0;
194        use dpp::data_contract::associated_token::token_pre_programmed_distribution::TokenPreProgrammedDistribution;
195        use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers;
196        use dpp::data_contract::change_control_rules::v0::ChangeControlRulesV0;
197        use dpp::data_contract::config::DataContractConfig;
198        use dpp::data_contract::group::v0::GroupV0;
199        use dpp::data_contract::group::Group;
200        use dpp::data_contract::v1::DataContractV1;
201        use dpp::data_contract::{TokenConfiguration, INITIAL_DATA_CONTRACT_VERSION};
202        use dpp::identifier::Identifier;
203        use dpp::identity::accessors::IdentitySettersV0;
204        use dpp::identity::Identity;
205        use dpp::prelude::IdentityPublicKey;
206        use dpp::tokens::calculate_token_id;
207        use dpp::tokens::status::v0::TokenStatusV0;
208        use dpp::tokens::status::TokenStatus;
209        use dpp::tokens::token_pricing_schedule::TokenPricingSchedule;
210        use rand::rngs::StdRng;
211        use rand::SeedableRng;
212        use std::collections::BTreeMap;
213        use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
214
215        let (platform, state, version) = setup_platform(Some((1, 1)), Network::Testnet, None);
216
217        let identity_id_1 = [1u8; 32];
218        let identity_id_2 = [2u8; 32];
219        let identity_id_3 = [3u8; 32];
220        let contract_id = [3u8; 32];
221
222        let token_id_0 = calculate_token_id(&contract_id, 0);
223        let token_id_1 = calculate_token_id(&contract_id, 1);
224        let token_id_2 = calculate_token_id(&contract_id, 2);
225
226        // Register identities
227        let credits_per_identity: u64 = 10_000_000_000_000;
228        let total_credits = credits_per_identity * 3;
229        platform
230            .platform
231            .drive
232            .add_to_system_credits(total_credits, None, version)
233            .expect("expected to add system credits");
234
235        let mut rng = StdRng::seed_from_u64(0u64);
236        let non_unique_key = IdentityPublicKey::random_voting_key_with_rng(11, &mut rng, version)
237            .expect("expected to create voting key");
238
239        for id_bytes in [identity_id_1, identity_id_2, identity_id_3] {
240            let id = Identifier::new(id_bytes);
241            let mut identity =
242                Identity::create_basic_identity(id, version).expect("expected to create identity");
243            identity.set_balance(credits_per_identity);
244
245            let seed = id_bytes[0];
246            let mut rng = StdRng::seed_from_u64(seed as u64);
247            let mut keys =
248                IdentityPublicKey::main_keys_with_random_authentication_keys_with_private_keys_with_rng(
249                    3, &mut rng, version,
250                )
251                .expect("expected to create keys");
252
253            let transfer_key =
254                IdentityPublicKey::random_masternode_transfer_key_with_rng(3, &mut rng, version)
255                    .expect("expected to create transfer key");
256            keys.push(transfer_key);
257            keys.push(non_unique_key.clone());
258
259            identity.set_public_keys(keys.into_iter().map(|(key, _)| (key.id(), key)).collect());
260
261            platform
262                .platform
263                .drive
264                .add_new_identity(identity, false, &BlockInfo::genesis(), true, None, version)
265                .expect("expected to add identity");
266        }
267
268        // Create data contract with tokens
269        let groups = [
270            (
271                0,
272                Group::V0(GroupV0 {
273                    members: [
274                        (Identifier::new(identity_id_1), 1),
275                        (Identifier::new(identity_id_2), 1),
276                    ]
277                    .into(),
278                    required_power: 1,
279                }),
280            ),
281            (
282                1,
283                Group::V0(GroupV0 {
284                    members: [
285                        (Identifier::new(identity_id_1), 1),
286                        (Identifier::new(identity_id_2), 1),
287                        (Identifier::new(identity_id_3), 1),
288                    ]
289                    .into(),
290                    required_power: 3,
291                }),
292            ),
293            (
294                2,
295                Group::V0(GroupV0 {
296                    members: [
297                        (Identifier::new(identity_id_1), 1),
298                        (Identifier::new(identity_id_3), 1),
299                    ]
300                    .into(),
301                    required_power: 1,
302                }),
303            ),
304        ]
305        .into();
306
307        let token_configuration = TokenConfiguration::V0(TokenConfigurationV0 {
308            conventions: TokenConfigurationConventionV0 {
309                localizations: Default::default(),
310                decimals: 8,
311            }
312            .into(),
313            conventions_change_rules: ChangeControlRulesV0::default().into(),
314            base_supply: 100000,
315            max_supply: None,
316            keeps_history: TokenKeepsHistoryRulesV0::default().into(),
317            start_as_paused: false,
318            allow_transfer_to_frozen_balance: true,
319            max_supply_change_rules: ChangeControlRulesV0 {
320                authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
321                admin_action_takers: Default::default(),
322                changing_authorized_action_takers_to_no_one_allowed: false,
323                changing_admin_action_takers_to_no_one_allowed: false,
324                self_changing_admin_action_takers_allowed: false,
325            }
326            .into(),
327            distribution_rules: TokenDistributionRulesV0 {
328                perpetual_distribution: Some(TokenPerpetualDistribution::V0(
329                    TokenPerpetualDistributionV0 {
330                        distribution_type: RewardDistributionType::BlockBasedDistribution {
331                            interval: 10,
332                            function: DistributionFunction::FixedAmount { amount: 100 },
333                        },
334                        distribution_recipient: TokenDistributionRecipient::ContractOwner,
335                    },
336                )),
337                perpetual_distribution_rules: ChangeControlRulesV0::default().into(),
338                pre_programmed_distribution: None,
339                new_tokens_destination_identity: None,
340                new_tokens_destination_identity_rules: ChangeControlRulesV0::default().into(),
341                minting_allow_choosing_destination: true,
342                minting_allow_choosing_destination_rules: ChangeControlRulesV0::default().into(),
343                change_direct_purchase_pricing_rules: ChangeControlRulesV0 {
344                    authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
345                    admin_action_takers: Default::default(),
346                    changing_authorized_action_takers_to_no_one_allowed: false,
347                    changing_admin_action_takers_to_no_one_allowed: false,
348                    self_changing_admin_action_takers_allowed: false,
349                }
350                .into(),
351            }
352            .into(),
353            marketplace_rules: TokenMarketplaceRulesV0 {
354                trade_mode: Default::default(),
355                trade_mode_change_rules: ChangeControlRulesV0::default().into(),
356            }
357            .into(),
358            manual_minting_rules: ChangeControlRulesV0 {
359                authorized_to_make_change: AuthorizedActionTakers::Group(0),
360                admin_action_takers: Default::default(),
361                changing_authorized_action_takers_to_no_one_allowed: false,
362                changing_admin_action_takers_to_no_one_allowed: false,
363                self_changing_admin_action_takers_allowed: false,
364            }
365            .into(),
366            manual_burning_rules: ChangeControlRulesV0 {
367                authorized_to_make_change: AuthorizedActionTakers::Group(2),
368                admin_action_takers: Default::default(),
369                changing_authorized_action_takers_to_no_one_allowed: false,
370                changing_admin_action_takers_to_no_one_allowed: false,
371                self_changing_admin_action_takers_allowed: false,
372            }
373            .into(),
374            freeze_rules: ChangeControlRulesV0 {
375                authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
376                admin_action_takers: Default::default(),
377                changing_authorized_action_takers_to_no_one_allowed: false,
378                changing_admin_action_takers_to_no_one_allowed: false,
379                self_changing_admin_action_takers_allowed: false,
380            }
381            .into(),
382            unfreeze_rules: ChangeControlRulesV0 {
383                authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
384                admin_action_takers: Default::default(),
385                changing_authorized_action_takers_to_no_one_allowed: false,
386                changing_admin_action_takers_to_no_one_allowed: false,
387                self_changing_admin_action_takers_allowed: false,
388            }
389            .into(),
390            destroy_frozen_funds_rules: ChangeControlRulesV0 {
391                authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
392                admin_action_takers: Default::default(),
393                changing_authorized_action_takers_to_no_one_allowed: false,
394                changing_admin_action_takers_to_no_one_allowed: false,
395                self_changing_admin_action_takers_allowed: false,
396            }
397            .into(),
398            emergency_action_rules: ChangeControlRulesV0 {
399                authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
400                admin_action_takers: Default::default(),
401                changing_authorized_action_takers_to_no_one_allowed: false,
402                changing_admin_action_takers_to_no_one_allowed: false,
403                self_changing_admin_action_takers_allowed: false,
404            }
405            .into(),
406            main_control_group: None,
407            main_control_group_can_be_modified: Default::default(),
408            description: Some("Some token description".to_string()),
409        });
410
411        // Token 2 gets pre-programmed distributions
412        let mut token_configuration_2 = token_configuration.clone();
413        if let TokenConfiguration::V0(ref mut cfg) = token_configuration_2 {
414            if let TokenDistributionRules::V0(ref mut rules) = cfg.distribution_rules {
415                rules.pre_programmed_distribution = Some(TokenPreProgrammedDistribution::V0(
416                    TokenPreProgrammedDistributionV0 {
417                        distributions: BTreeMap::from([
418                            (
419                                1000,
420                                BTreeMap::from([
421                                    (Identifier::new(identity_id_1), 500),
422                                    (Identifier::new(identity_id_2), 300),
423                                ]),
424                            ),
425                            (
426                                5000,
427                                BTreeMap::from([(Identifier::new(identity_id_1), 1000)]),
428                            ),
429                            (
430                                10000,
431                                BTreeMap::from([
432                                    (Identifier::new(identity_id_2), 750),
433                                    (Identifier::new(identity_id_3), 250),
434                                ]),
435                            ),
436                        ]),
437                    },
438                ));
439            }
440        }
441
442        let tokens = [
443            (0, token_configuration.clone()),
444            (1, token_configuration),
445            (2, token_configuration_2),
446        ]
447        .into();
448
449        let data_contract = DataContract::V1(DataContractV1 {
450            id: Identifier::new(contract_id),
451            config: DataContractConfig::default_for_version(version)
452                .expect("expected contract config"),
453            version: INITIAL_DATA_CONTRACT_VERSION,
454            owner_id: Identifier::new(identity_id_1),
455            document_types: Default::default(),
456            schema_defs: Default::default(),
457            created_at: None,
458            updated_at: None,
459            created_at_block_height: None,
460            updated_at_block_height: None,
461            created_at_epoch: None,
462            updated_at_epoch: None,
463            groups,
464            tokens,
465            keywords: vec!["cat".into(), "white".into()],
466            description: Some("Some contract description".to_string()),
467        });
468
469        let block_info = BlockInfo::genesis();
470
471        platform
472            .platform
473            .drive
474            .apply_contract(&data_contract, block_info, true, None, None, version)
475            .expect("expected to apply contract");
476
477        // Mint tokens
478        // Token 0: 100 to identity_1, 100 to identity_2
479        // Token 1: 200 to identity_1, 100 to identity_3
480        // Token 2: 300 to identity_1, 200 to identity_2
481        for (token_id, id, amount) in [
482            (token_id_0, identity_id_1, 100u64),
483            (token_id_0, identity_id_2, 100),
484            (token_id_1, identity_id_1, 200),
485            (token_id_1, identity_id_3, 100),
486            (token_id_2, identity_id_1, 300),
487            (token_id_2, identity_id_2, 200),
488        ] {
489            platform
490                .platform
491                .drive
492                .token_mint(
493                    token_id,
494                    id,
495                    amount,
496                    false,
497                    false,
498                    &block_info,
499                    true,
500                    None,
501                    version,
502                )
503                .expect("expected to mint tokens");
504        }
505
506        // Freeze identity 2 on token 0
507        platform
508            .platform
509            .drive
510            .token_freeze(
511                Identifier::new(token_id_0),
512                Identifier::new(identity_id_2),
513                &block_info,
514                true,
515                None,
516                version,
517            )
518            .expect("expected to freeze");
519
520        // Pause token 1
521        let status = TokenStatus::V0(TokenStatusV0 { paused: true });
522        platform
523            .platform
524            .drive
525            .token_apply_status(token_id_1, status, &block_info, true, None, version)
526            .expect("expected to apply status");
527
528        // Set direct purchase prices
529        platform
530            .platform
531            .drive
532            .token_set_direct_purchase_price(
533                token_id_1,
534                Some(TokenPricingSchedule::SinglePrice(25)),
535                &block_info,
536                true,
537                None,
538                version,
539            )
540            .expect("expected to set direct purchase price");
541
542        let pricing = TokenPricingSchedule::SetPrices(
543            (0..=900)
544                .step_by(100)
545                .map(|amount| (amount, 1000 - amount))
546                .collect(),
547        );
548        platform
549            .platform
550            .drive
551            .token_set_direct_purchase_price(
552                token_id_2,
553                Some(pricing),
554                &block_info,
555                true,
556                None,
557                version,
558            )
559            .expect("expected to set direct purchase price");
560
561        (
562            platform,
563            state,
564            version,
565            contract_id,
566            [token_id_0, token_id_1, token_id_2],
567            [identity_id_1, identity_id_2, identity_id_3],
568        )
569    }
570}