Skip to main content

drive_abci/query/
mod.rs

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