Skip to main content

drive_abci/query/
mod.rs

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
25/// A query validation result
26pub 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        // We can't return a reference to Arc (`load` method) so we clone Arc (`load_full`).
107        // This is a bit slower, but we don't care since we are in test environment
108        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    /// `contract` with its `post` type left to the contract owner's moderators alone, on the
141    /// record (`canBeDeleted: false` beside `moderatorAbilities.delete`, whose removals keep
142    /// a record by default), and every reference to it a `refersTo: moderatedDocument` one.
143    /// `preallocated` indexes, which need a `permanentDocument` edge, are dropped.
144    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    /// The record a moderator's removal of the post `id`, owned by `owner`, leaves.
193    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    /// A moderator's removal of the post `id`, as the moderation transition writes it: the
206    /// post deleted past `canBeDeleted: false`, and its removal record.
207    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    /// Asserts the service refuses `result` with INVALID_ARGUMENT, the code that tells a client
280    /// the request itself is wrong, so it neither tries another node nor stops using this one.
281    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    /// Set up a platform with token state for testing token queries.
301    ///
302    /// Creates 3 identities and a data contract with 3 tokens.
303    /// Token 0: minted 100 to identity 1, 100 to identity 2. Identity 2 frozen on token 0.
304    /// Token 1: minted 200 to identity 1, 100 to identity 3. Token 1 is paused.
305    /// Token 2: minted 300 to identity 1, 200 to identity 2. Has pre-programmed distributions.
306    ///
307    /// Returns (platform, state, version, contract_id, [token_id_0, token_id_1, token_id_2],
308    ///          [identity_id_1, identity_id_2, identity_id_3])
309    #[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        // Register identities
363        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        // Create data contract with tokens
405        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        // Token 2 gets pre-programmed distributions
548        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        // Mint tokens
614        // Token 0: 100 to identity_1, 100 to identity_2
615        // Token 1: 200 to identity_1, 100 to identity_3
616        // Token 2: 300 to identity_1, 200 to identity_2
617        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        // Freeze identity 2 on token 0
643        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        // Pause token 1
657        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        // Set direct purchase prices
665        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}