Skip to main content

drive_abci/query/
mod.rs

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