Skip to main content

drive_abci/execution/validation/state_transition/state_transitions/
mod.rs

1/// Module containing functionality related to batch processing of documents.
2pub mod batch;
3
4/// Module for creating an identity entity.
5pub mod identity_create;
6
7/// Module for managing transfers of credit between identity entities.
8pub mod identity_credit_transfer;
9
10/// Module for managing withdrawals of credit from an identity entity.
11pub mod identity_credit_withdrawal;
12
13/// Module for topping up credit in an identity entity.
14pub mod identity_top_up;
15
16/// Module for updating an existing identity entity.
17pub mod identity_update;
18
19/// Module for creating a data contract entity.
20pub mod data_contract_create;
21
22/// Module for updating an existing data contract entity.
23pub mod data_contract_update;
24
25/// Module for voting from a masternode.
26pub mod masternode_vote;
27
28/// Identity create from addresses
29pub mod identity_create_from_addresses;
30
31/// Module for validation of address funding from asset lock transitions
32pub mod address_funding_from_asset_lock;
33
34/// Module for validation of credit transfer from an identity to addresses
35pub mod identity_credit_transfer_to_addresses;
36
37/// Module for validation of address credit withdrawal transitions
38pub mod address_credit_withdrawal;
39
40/// Module for validation of address funds transfer transitions
41pub mod address_funds_transfer;
42mod identity_top_up_from_addresses;
43
44/// Module for shield transition validation
45pub mod shield;
46/// Module for shield from asset lock transition validation
47pub mod shield_from_asset_lock;
48/// Common validation logic shared by shielded transitions (proof verification)
49pub mod shielded_common;
50/// Module for shielded transfer transition validation
51pub mod shielded_transfer;
52/// Module for shielded withdrawal transition validation
53pub mod shielded_withdrawal;
54/// Module for unshield transition validation
55pub mod unshield;
56
57/// The validation mode we are using
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
59pub enum ValidationMode {
60    /// The basic checktx before the state transition is put into mempool
61    CheckTx,
62    /// Rechecking a state transition every block
63    RecheckTx,
64    /// The validation during block execution by a proposer or validator
65    Validator,
66    /// A validation mode used to get the action with no validation
67    NoValidation,
68}
69
70impl ValidationMode {
71    /// Can this validation mode alter cache on drive?
72    pub fn can_alter_cache(&self) -> bool {
73        match self {
74            ValidationMode::CheckTx => false,
75            ValidationMode::RecheckTx => false,
76            ValidationMode::Validator => true,
77            ValidationMode::NoValidation => false,
78        }
79    }
80}
81
82#[cfg(test)]
83pub(in crate::execution) mod test_helpers;
84
85#[cfg(test)]
86pub(in crate::execution) mod tests {
87    use crate::rpc::core::MockCoreRPCLike;
88    use crate::test::helpers::setup::{TempPlatform, TestPlatformBuilder};
89    use dpp::block::block_info::BlockInfo;
90    use dpp::data_contracts::SystemDataContract;
91    use dpp::fee::Credits;
92    use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
93    use dpp::identity::{Identity, IdentityPublicKey, IdentityV0, KeyID, KeyType, Purpose, SecurityLevel, TimestampMillis};
94    use dpp::prelude::{BlockHeight, Identifier, IdentityNonce};
95    use dpp::state_transition::data_contract_create_transition::methods::DataContractCreateTransitionMethodsV0;
96    use dpp::state_transition::data_contract_create_transition::DataContractCreateTransition;
97    use dpp::system_data_contracts::load_system_data_contract;
98    use dpp::tests::json_document::json_document_to_contract_with_ids;
99    use drive::drive::document::query::QueryDocumentsOutcomeV0Methods;
100    use drive::query::DriveDocumentQuery;
101    use platform_version::version::PlatformVersion;
102    use rand::prelude::StdRng;
103    use rand::{Rng, SeedableRng};
104    use simple_signer::signer::SimpleSigner;
105    use std::borrow::Cow;
106    use std::collections::BTreeMap;
107    use std::net::{IpAddr, Ipv4Addr, SocketAddr};
108    use std::ops::Deref;
109    use std::sync::Arc;
110    use arc_swap::Guard;
111    use assert_matches::assert_matches;
112    use dpp::dashcore_rpc::dashcore_rpc_json::{DMNState, MasternodeListItem, MasternodeType};
113    use dapi_grpc::platform::v0::{get_contested_resource_vote_state_request, get_contested_resource_vote_state_response, GetContestedResourceVoteStateRequest, GetContestedResourceVoteStateResponse};
114    use dapi_grpc::platform::v0::get_contested_resource_vote_state_request::get_contested_resource_vote_state_request_v0::ResultType;
115    use dapi_grpc::platform::v0::get_contested_resource_vote_state_request::{get_contested_resource_vote_state_request_v0, GetContestedResourceVoteStateRequestV0};
116    use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::{get_contested_resource_vote_state_response_v0, GetContestedResourceVoteStateResponseV0};
117    use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::get_contested_resource_vote_state_response_v0::FinishedVoteInfo;
118    use dpp::balances::credits::TokenAmount;
119    use dpp::dash_to_credits;
120    use dpp::dashcore::{ProTxHash, Txid};
121    use dpp::dashcore::hashes::Hash;
122    use dpp::data_contract::accessors::v0::{DataContractV0Getters, DataContractV0Setters};
123    use dpp::data_contract::accessors::v1::{DataContractV1Getters, DataContractV1Setters};
124    use dpp::data_contract::{DataContract, GroupContractPosition, TokenContractPosition};
125    use dpp::data_contract::document_type::accessors::{DocumentTypeV0Getters, DocumentTypeV1Setters};
126    use dpp::data_contract::document_type::random_document::{CreateRandomDocument, DocumentFieldFillSize, DocumentFieldFillType};
127    use dpp::document::{Document, DocumentV0Getters, DocumentV0Setters};
128    use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
129    use dpp::fee::fee_result::FeeResult;
130    use dpp::identifier::MasternodeIdentifiers;
131    use dpp::identity::accessors::IdentityGettersV0;
132    use dpp::identity::contract_bounds::ContractBounds;
133    use dpp::identity::hash::IdentityPublicKeyHashMethodsV0;
134    use dpp::platform_value::{Bytes32, Value};
135    use dpp::serialization::PlatformSerializable;
136    use dpp::state_transition::batch_transition::BatchTransition;
137    use dpp::state_transition::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0;
138    use dpp::state_transition::masternode_vote_transition::MasternodeVoteTransition;
139    use dpp::state_transition::masternode_vote_transition::methods::MasternodeVoteTransitionMethodsV0;
140    use dpp::state_transition::StateTransition;
141    use dpp::tokens::calculate_token_id;
142    use dpp::util::hash::hash_double;
143    use dpp::util::strings::convert_to_homograph_safe_chars;
144    use dpp::voting::contender_structs::{Contender, ContenderV0};
145    use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice;
146    use dpp::voting::vote_info_storage::contested_document_vote_poll_winner_info::ContestedDocumentVotePollWinnerInfo;
147    use dpp::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll;
148    use dpp::voting::vote_polls::VotePoll;
149    use dpp::voting::votes::resource_vote::ResourceVote;
150    use dpp::voting::votes::resource_vote::v0::ResourceVoteV0;
151    use dpp::voting::votes::Vote;
152    use drive::util::object_size_info::DataContractResolvedInfo;
153    use drive::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed;
154    use drive::query::vote_poll_vote_state_query::ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally;
155    use drive::query::vote_poll_vote_state_query::{ContestedDocumentVotePollDriveQueryResultType, ResolvedContestedDocumentVotePollDriveQuery};
156    use drive::util::test_helpers::setup_contract;
157    use crate::execution::types::block_execution_context::BlockExecutionContext;
158    use crate::execution::types::block_execution_context::v0::BlockExecutionContextV0;
159    use crate::expect_match;
160    use crate::platform_types::platform_state::PlatformState;
161    use crate::platform_types::platform_state::PlatformStateV0Methods;
162    use crate::platform_types::state_transitions_processing_result::{StateTransitionExecutionResult, StateTransitionsProcessingResult};
163    use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult::{SuccessfulExecution, UnpaidConsensusError};
164    use crate::execution::types::block_state_info::BlockStateInfo;
165    use crate::execution::types::block_state_info::v0::BlockStateInfoV0;
166    use crate::platform_types::epoch_info::EpochInfo;
167    use crate::platform_types::epoch_info::v0::EpochInfoV0;
168    use crate::execution::types::block_fees::v0::BlockFeesV0;
169    use crate::execution::types::processed_block_fees_outcome::v0::ProcessedBlockFeesOutcome;
170    use dpp::data_contract::associated_token::token_configuration::TokenConfiguration;
171    use dpp::data_contract::group::Group;
172    use dpp::tokens::gas_fees_paid_by::GasFeesPaidBy;
173    use dpp::tokens::token_amount_on_contract_token::{DocumentActionTokenCost, DocumentActionTokenEffect};
174    use dpp::data_contract::document_type::accessors::DocumentTypeV0MutGetters;
175    use dpp::serialization::PlatformDeserializableWithPotentialValidationFromVersionedStructure;
176
177    /// We add an identity, but we also add the same amount to system credits
178    pub(in crate::execution) fn setup_identity_with_system_credits(
179        platform: &mut TempPlatform<MockCoreRPCLike>,
180        seed: u64,
181        credits: Credits,
182    ) -> (Identity, SimpleSigner, IdentityPublicKey) {
183        let platform_version = PlatformVersion::latest();
184        platform
185            .drive
186            .add_to_system_credits(credits, None, platform_version)
187            .expect("expected to add to system credits");
188        setup_identity(platform, seed, credits)
189    }
190
191    pub(in crate::execution) fn setup_identity(
192        platform: &mut TempPlatform<MockCoreRPCLike>,
193        seed: u64,
194        credits: Credits,
195    ) -> (Identity, SimpleSigner, IdentityPublicKey) {
196        let platform_version = PlatformVersion::latest();
197        let mut signer = SimpleSigner::default();
198
199        let mut rng = StdRng::seed_from_u64(seed);
200
201        let (master_key, master_private_key) =
202            IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng(
203                0,
204                &mut rng,
205                platform_version,
206            )
207            .expect("expected to get key pair");
208
209        signer.add_identity_public_key(master_key.clone(), master_private_key);
210
211        let (critical_public_key, private_key) =
212            IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng(
213                1,
214                &mut rng,
215                platform_version,
216            )
217            .expect("expected to get key pair");
218
219        signer.add_identity_public_key(critical_public_key.clone(), private_key);
220
221        let identity: Identity = IdentityV0 {
222            id: Identifier::random_with_rng(&mut rng),
223            public_keys: BTreeMap::from([
224                (0, master_key.clone()),
225                (1, critical_public_key.clone()),
226            ]),
227            balance: credits,
228            revision: 0,
229        }
230        .into();
231
232        // We just add this identity to the system first
233
234        platform
235            .drive
236            .add_new_identity(
237                identity.clone(),
238                false,
239                &BlockInfo::default(),
240                true,
241                None,
242                platform_version,
243            )
244            .expect("expected to add a new identity");
245
246        (identity, signer, critical_public_key)
247    }
248
249    pub(in crate::execution) fn setup_identity_without_adding_it(
250        seed: u64,
251        credits: Credits,
252    ) -> (Identity, SimpleSigner, IdentityPublicKey) {
253        let platform_version = PlatformVersion::latest();
254        let mut signer = SimpleSigner::default();
255
256        let mut rng = StdRng::seed_from_u64(seed);
257
258        let (master_key, master_private_key) =
259            IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng(
260                0,
261                &mut rng,
262                platform_version,
263            )
264            .expect("expected to get key pair");
265
266        signer.add_identity_public_key(master_key.clone(), master_private_key);
267
268        let (critical_public_key, private_key) =
269            IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng(
270                1,
271                &mut rng,
272                platform_version,
273            )
274            .expect("expected to get key pair");
275
276        signer.add_identity_public_key(critical_public_key.clone(), private_key);
277
278        let identity: Identity = IdentityV0 {
279            id: Identifier::random_with_rng(&mut rng),
280            public_keys: BTreeMap::from([
281                (0, master_key.clone()),
282                (1, critical_public_key.clone()),
283            ]),
284            balance: credits,
285            revision: 0,
286        }
287        .into();
288
289        (identity, signer, critical_public_key)
290    }
291
292    pub(in crate::execution) fn setup_identity_return_master_key(
293        platform: &mut TempPlatform<MockCoreRPCLike>,
294        seed: u64,
295        credits: Credits,
296    ) -> (Identity, SimpleSigner, IdentityPublicKey, IdentityPublicKey) {
297        let platform_version = PlatformVersion::latest();
298        let mut signer = SimpleSigner::default();
299
300        let mut rng = StdRng::seed_from_u64(seed);
301
302        let (master_key, master_private_key) =
303            IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng(
304                0,
305                &mut rng,
306                platform_version,
307            )
308            .expect("expected to get key pair");
309
310        signer.add_identity_public_key(master_key.clone(), master_private_key);
311
312        let (critical_public_key, private_key) =
313            IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng(
314                1,
315                &mut rng,
316                platform_version,
317            )
318            .expect("expected to get key pair");
319
320        signer.add_identity_public_key(critical_public_key.clone(), private_key);
321
322        let identity: Identity = IdentityV0 {
323            id: Identifier::random_with_rng(&mut rng),
324            public_keys: BTreeMap::from([
325                (0, master_key.clone()),
326                (1, critical_public_key.clone()),
327            ]),
328            balance: credits,
329            revision: 0,
330        }
331        .into();
332
333        // We just add this identity to the system first
334
335        platform
336            .drive
337            .add_new_identity(
338                identity.clone(),
339                false,
340                &BlockInfo::default(),
341                true,
342                None,
343                platform_version,
344            )
345            .expect("expected to add a new identity");
346
347        (identity, signer, critical_public_key, master_key)
348    }
349
350    #[allow(clippy::too_many_arguments)]
351    pub(crate) fn setup_add_key_to_identity(
352        platform: &mut TempPlatform<MockCoreRPCLike>,
353        identity: &mut Identity,
354        signer: &mut SimpleSigner,
355        seed: u64,
356        key_id: KeyID,
357        purpose: Purpose,
358        security_level: SecurityLevel,
359        key_type: KeyType,
360        contract_bounds: Option<ContractBounds>,
361    ) -> IdentityPublicKey {
362        let platform_version = PlatformVersion::latest();
363
364        let mut rng = StdRng::seed_from_u64(seed);
365
366        let (key, private_key) = IdentityPublicKey::random_key_with_known_attributes(
367            key_id,
368            &mut rng,
369            purpose,
370            security_level,
371            key_type,
372            contract_bounds,
373            platform_version,
374        )
375        .expect("expected to get key pair");
376
377        signer.add_identity_public_key(key.clone(), private_key);
378
379        identity.add_public_key(key.clone());
380
381        platform
382            .drive
383            .add_new_unique_keys_to_identity(
384                identity.id().to_buffer(),
385                vec![key.clone()],
386                &BlockInfo::default(),
387                true,
388                None,
389                platform_version,
390            )
391            .expect("expected to add a new key");
392
393        key
394    }
395
396    pub(in crate::execution) fn setup_identity_with_withdrawal_key_and_system_credits(
397        platform: &mut TempPlatform<MockCoreRPCLike>,
398        seed: u64,
399        withdrawal_key_type: KeyType,
400        credits: Credits,
401    ) -> (Identity, SimpleSigner, IdentityPublicKey, IdentityPublicKey) {
402        let platform_version = PlatformVersion::latest();
403        platform
404            .drive
405            .add_to_system_credits(credits, None, platform_version)
406            .expect("expected to add to system credits");
407        let mut signer = SimpleSigner::default();
408
409        let mut rng = StdRng::seed_from_u64(seed);
410
411        let (master_key, master_private_key) =
412            IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng(
413                0,
414                &mut rng,
415                platform_version,
416            )
417            .expect("expected to get key pair");
418
419        signer.add_identity_public_key(master_key.clone(), master_private_key);
420
421        let (critical_public_key, private_key) =
422            IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng(
423                1,
424                &mut rng,
425                platform_version,
426            )
427            .expect("expected to get key pair");
428
429        signer.add_identity_public_key(critical_public_key.clone(), private_key);
430
431        let (withdrawal_public_key, withdrawal_private_key) =
432            IdentityPublicKey::random_key_with_known_attributes(
433                2,
434                &mut rng,
435                Purpose::TRANSFER,
436                SecurityLevel::CRITICAL,
437                withdrawal_key_type,
438                None,
439                platform_version,
440            )
441            .expect("expected to get key pair");
442
443        signer.add_identity_public_key(withdrawal_public_key.clone(), withdrawal_private_key);
444
445        let identity: Identity = IdentityV0 {
446            id: Identifier::random_with_rng(&mut rng),
447            public_keys: BTreeMap::from([
448                (0, master_key.clone()),
449                (1, critical_public_key.clone()),
450                (2, withdrawal_public_key.clone()),
451            ]),
452            balance: credits,
453            revision: 0,
454        }
455        .into();
456
457        // We just add this identity to the system first
458
459        platform
460            .drive
461            .add_new_identity(
462                identity.clone(),
463                false,
464                &BlockInfo::default(),
465                true,
466                None,
467                platform_version,
468            )
469            .expect("expected to add a new identity");
470
471        (identity, signer, critical_public_key, withdrawal_public_key)
472    }
473
474    pub(in crate::execution) fn add_tokens_to_identity(
475        platform: &TempPlatform<MockCoreRPCLike>,
476        token_id: Identifier,
477        identity_id: Identifier,
478        balance_to_add: Credits,
479    ) {
480        let platform_version = PlatformVersion::latest();
481        platform
482            .drive
483            .add_to_identity_token_balance(
484                token_id.to_buffer(),
485                identity_id.to_buffer(),
486                balance_to_add,
487                &BlockInfo::default(),
488                true,
489                None,
490                platform_version,
491                None,
492            )
493            .expect("expected to add token balance to identity");
494        platform
495            .drive
496            .add_to_token_total_supply(
497                token_id.to_buffer(),
498                balance_to_add,
499                true,
500                false,
501                true,
502                &BlockInfo::default(),
503                None,
504                platform_version,
505            )
506            .expect("expected to add to total supply");
507    }
508
509    pub(in crate::execution) fn process_state_transitions(
510        platform: &TempPlatform<MockCoreRPCLike>,
511        state_transitions: &[StateTransition],
512        block_info: BlockInfo,
513        platform_state: &PlatformState,
514    ) -> (Vec<FeeResult>, ProcessedBlockFeesOutcome) {
515        let platform_version = PlatformVersion::latest();
516
517        let raw_state_transitions = state_transitions
518            .iter()
519            .map(|a| a.serialize_to_bytes().expect("expected to serialize"))
520            .collect::<Vec<_>>();
521
522        let transaction = platform.drive.grove.start_transaction();
523
524        let processing_result = platform
525            .platform
526            .process_raw_state_transitions(
527                &raw_state_transitions,
528                platform_state,
529                &block_info,
530                &transaction,
531                platform_version,
532                false,
533                None,
534            )
535            .expect("expected to process state transition");
536
537        let fee_results = processing_result.execution_results().iter().map(|result| {
538            let fee_result = expect_match!(result, StateTransitionExecutionResult::SuccessfulExecution{ fee_result, .. } => fee_result);
539            fee_result.clone()
540        }).collect();
541
542        // while we have the state transitions executed, we now need to process the block fees
543        let block_fees_v0: BlockFeesV0 = processing_result.aggregated_fees().clone().into();
544
545        let block_execution_context = BlockExecutionContext::V0(BlockExecutionContextV0 {
546            block_state_info: BlockStateInfo::V0(BlockStateInfoV0 {
547                height: block_info.height,
548                round: 0,
549                block_time_ms: block_info.time_ms,
550                previous_block_time_ms: platform_state.last_committed_block_time_ms(),
551                proposer_pro_tx_hash: Default::default(),
552                core_chain_locked_height: 0,
553                block_hash: None,
554                app_hash: None,
555            }),
556            epoch_info: EpochInfo::V0(EpochInfoV0::default()),
557            unsigned_withdrawal_transactions: Default::default(),
558            block_address_balance_changes: Default::default(),
559            block_platform_state: platform_state.clone(),
560            proposer_results: None,
561        });
562
563        // Process fees
564        let processed_block_fees = platform
565            .process_block_fees_and_validate_sum_trees(
566                &block_execution_context,
567                block_fees_v0.into(),
568                &transaction,
569                platform_version,
570            )
571            .expect("expected to process block fees");
572
573        platform
574            .drive
575            .grove
576            .commit_transaction(transaction)
577            .unwrap()
578            .expect("expected to commit");
579
580        (fee_results, processed_block_fees)
581    }
582
583    pub(in crate::execution) fn fetch_expected_identity_balance(
584        platform: &TempPlatform<MockCoreRPCLike>,
585        identity_id: Identifier,
586        platform_version: &PlatformVersion,
587        expected_balance: Credits,
588    ) {
589        assert_eq!(
590            expected_balance,
591            platform
592                .drive
593                .fetch_identity_balance(identity_id.to_buffer(), None, platform_version)
594                .expect("expected to be able to fetch balance")
595                .expect("expected a balance")
596        );
597    }
598
599    pub(in crate::execution) fn setup_masternode_owner_identity(
600        platform: &mut TempPlatform<MockCoreRPCLike>,
601        seed: u64,
602        credits: Credits,
603        platform_version: &PlatformVersion,
604    ) -> (Identity, SimpleSigner, IdentityPublicKey, IdentityPublicKey) {
605        let mut signer = SimpleSigner::default();
606
607        platform
608            .drive
609            .add_to_system_credits(credits, None, platform_version)
610            .expect("expected to add to system credits");
611
612        let mut rng = StdRng::seed_from_u64(seed);
613
614        let (transfer_key, transfer_private_key) =
615            IdentityPublicKey::random_masternode_transfer_key_with_rng(
616                0,
617                &mut rng,
618                platform_version,
619            )
620            .expect("expected to get key pair");
621
622        let (owner_key, owner_private_key) =
623            IdentityPublicKey::random_masternode_owner_key_with_rng(1, &mut rng, platform_version)
624                .expect("expected to get key pair");
625
626        let owner_address = owner_key
627            .public_key_hash()
628            .expect("expected a public key hash");
629
630        let payout_address = transfer_key
631            .public_key_hash()
632            .expect("expected a public key hash");
633
634        signer.add_identity_public_key(transfer_key.clone(), transfer_private_key);
635        signer.add_identity_public_key(owner_key.clone(), owner_private_key);
636
637        let pro_tx_hash_bytes: [u8; 32] = rng.gen();
638
639        let identity: Identity = IdentityV0 {
640            id: pro_tx_hash_bytes.into(),
641            public_keys: BTreeMap::from([(0, transfer_key.clone()), (1, owner_key.clone())]),
642            balance: credits,
643            revision: 0,
644        }
645        .into();
646
647        // We just add this identity to the system first
648
649        platform
650            .drive
651            .add_new_identity(
652                identity.clone(),
653                true,
654                &BlockInfo::default(),
655                true,
656                None,
657                platform_version,
658            )
659            .expect("expected to add a new identity");
660
661        let mut platform_state = platform.state.load().clone().deref().clone();
662
663        let pro_tx_hash = ProTxHash::from_byte_array(pro_tx_hash_bytes);
664
665        let random_ip = Ipv4Addr::new(
666            rng.gen_range(0..255),
667            rng.gen_range(0..255),
668            rng.gen_range(0..255),
669            rng.gen_range(0..255),
670        );
671
672        platform_state.full_masternode_list_mut().insert(
673            pro_tx_hash,
674            MasternodeListItem {
675                node_type: MasternodeType::Regular,
676                pro_tx_hash,
677                collateral_hash: Txid::from_byte_array(rng.gen()),
678                collateral_index: 0,
679                collateral_address: rng.gen(),
680                operator_reward: 0.0,
681                state: DMNState {
682                    service: SocketAddr::new(IpAddr::V4(random_ip), 19999),
683                    registered_height: 0,
684                    pose_revived_height: None,
685                    pose_ban_height: None,
686                    revocation_reason: 0,
687                    owner_address,
688                    voting_address: rng.gen(),
689                    payout_address,
690                    pub_key_operator: vec![],
691                    operator_payout_address: None,
692                    platform_node_id: None,
693                    platform_p2p_port: None,
694                    platform_http_port: None,
695                },
696            },
697        );
698
699        platform.state.store(Arc::new(platform_state));
700
701        (identity, signer, owner_key, transfer_key)
702    }
703
704    pub(in crate::execution) fn setup_masternode_voting_identity(
705        platform: &mut TempPlatform<MockCoreRPCLike>,
706        seed: u64,
707        platform_version: &PlatformVersion,
708    ) -> (Identifier, Identity, SimpleSigner, IdentityPublicKey) {
709        let mut signer = SimpleSigner::default();
710
711        let mut rng = StdRng::seed_from_u64(seed);
712
713        let (voting_key, voting_private_key) =
714            IdentityPublicKey::random_voting_key_with_rng(0, &mut rng, platform_version)
715                .expect("expected to get key pair");
716
717        signer.add_identity_public_key(voting_key.clone(), voting_private_key);
718
719        let pro_tx_hash_bytes: [u8; 32] = rng.gen();
720
721        let voting_address = voting_key
722            .public_key_hash()
723            .expect("expected a public key hash");
724
725        let voter_identifier =
726            Identifier::create_voter_identifier(&pro_tx_hash_bytes, &voting_address);
727
728        let identity: Identity = IdentityV0 {
729            id: voter_identifier,
730            public_keys: BTreeMap::from([(0, voting_key.clone())]),
731            balance: 0,
732            revision: 0,
733        }
734        .into();
735
736        // We just add this identity to the system first
737
738        platform
739            .drive
740            .add_new_identity(
741                identity.clone(),
742                true,
743                &BlockInfo::default(),
744                true,
745                None,
746                platform_version,
747            )
748            .expect("expected to add a new identity");
749
750        let mut platform_state = platform.state.load().clone().deref().clone();
751
752        let pro_tx_hash = ProTxHash::from_byte_array(pro_tx_hash_bytes);
753
754        let random_ip = Ipv4Addr::new(
755            rng.gen_range(0..255),
756            rng.gen_range(0..255),
757            rng.gen_range(0..255),
758            rng.gen_range(0..255),
759        );
760
761        platform_state.full_masternode_list_mut().insert(
762            pro_tx_hash,
763            MasternodeListItem {
764                node_type: MasternodeType::Regular,
765                pro_tx_hash,
766                collateral_hash: Txid::from_byte_array(rng.gen()),
767                collateral_index: 0,
768                collateral_address: rng.gen(),
769                operator_reward: 0.0,
770                state: DMNState {
771                    service: SocketAddr::new(IpAddr::V4(random_ip), 19999),
772                    registered_height: 0,
773                    pose_revived_height: None,
774                    pose_ban_height: None,
775                    revocation_reason: 0,
776                    owner_address: rng.gen(),
777                    voting_address,
778                    payout_address: rng.gen(),
779                    pub_key_operator: vec![],
780                    operator_payout_address: None,
781                    platform_node_id: None,
782                    platform_p2p_port: None,
783                    platform_http_port: None,
784                },
785            },
786        );
787
788        platform.state.store(Arc::new(platform_state));
789
790        (pro_tx_hash_bytes.into(), identity, signer, voting_key)
791    }
792
793    pub(in crate::execution) fn take_down_masternode_identities(
794        platform: &mut TempPlatform<MockCoreRPCLike>,
795        masternode_identities: &Vec<Identifier>,
796    ) {
797        let mut platform_state = platform.state.load().clone().deref().clone();
798
799        let list = platform_state.full_masternode_list_mut();
800
801        for masternode_identifiers in masternode_identities {
802            let pro_tx_hash = ProTxHash::from_byte_array(masternode_identifiers.to_buffer());
803
804            list.remove(&pro_tx_hash);
805        }
806
807        platform.state.store(Arc::new(platform_state));
808    }
809
810    #[allow(dead_code)]
811    pub(in crate::execution) enum IdentityTestInfo<'a> {
812        Given {
813            identity: &'a Identity,
814            signer: &'a SimpleSigner,
815            public_key: &'a IdentityPublicKey,
816            identity_nonce: IdentityNonce,
817        },
818        UseSeed(u64),
819        UseRng(&'a mut StdRng),
820    }
821
822    pub(in crate::execution) async fn register_contract_from_bytes(
823        platform: &mut TempPlatform<MockCoreRPCLike>,
824        platform_state: &PlatformState,
825        contract_bytes: Vec<u8>,
826        identity_info: IdentityTestInfo<'_>,
827        platform_version: &PlatformVersion,
828    ) -> DataContract {
829        // Deserialize the data contract from bytes
830        let mut data_contract =
831            DataContract::versioned_deserialize(&contract_bytes, false, platform_version)
832                .expect("expected to deserialize data contract");
833
834        // Get identity info based on the enum variant
835        let (identity_cow, signer_cow, key_cow, nonce) = match identity_info {
836            IdentityTestInfo::Given {
837                identity,
838                signer,
839                public_key,
840                identity_nonce,
841            } => (
842                Cow::Borrowed(identity),
843                Cow::Borrowed(signer),
844                Cow::Borrowed(public_key),
845                identity_nonce,
846            ),
847            IdentityTestInfo::UseSeed(seed) => {
848                let (identity, signer, key) = setup_identity(platform, seed, dash_to_credits!(1));
849                (Cow::Owned(identity), Cow::Owned(signer), Cow::Owned(key), 1)
850            }
851            IdentityTestInfo::UseRng(rng) => {
852                let seed = rng.gen();
853                let (identity, signer, key) = setup_identity(platform, seed, dash_to_credits!(1));
854                (Cow::Owned(identity), Cow::Owned(signer), Cow::Owned(key), 1)
855            }
856        };
857
858        let contract_id = DataContract::generate_data_contract_id_v0(identity_cow.id(), nonce);
859
860        data_contract.set_id(contract_id);
861
862        // Create and sign the data contract create transition
863        let state_transition = DataContractCreateTransition::new_from_data_contract(
864            data_contract.clone(),
865            nonce,
866            &identity_cow.as_ref().clone().into_partial_identity_info(),
867            key_cow.id(),
868            signer_cow.as_ref(),
869            platform_version,
870            None,
871        )
872        .await
873        .expect("expected to create and sign data contract create transition");
874
875        // Serialize the state transition
876        let state_transition_bytes = state_transition
877            .serialize_to_bytes()
878            .expect("expected to serialize state transition");
879
880        let transaction = platform.drive.grove.start_transaction();
881
882        let processing_result = platform
883            .platform
884            .process_raw_state_transitions(
885                &[state_transition_bytes],
886                platform_state,
887                &BlockInfo::default(),
888                &transaction,
889                platform_version,
890                false,
891                None,
892            )
893            .expect("expected to process state transition");
894
895        platform
896            .drive
897            .grove
898            .commit_transaction(transaction)
899            .unwrap()
900            .expect("expected to commit transaction");
901
902        let execution_result = processing_result.into_execution_results().remove(0);
903        assert_matches!(execution_result, SuccessfulExecution { .. });
904
905        data_contract
906    }
907
908    pub(in crate::execution) async fn create_dpns_name_contest_give_key_info(
909        platform: &mut TempPlatform<MockCoreRPCLike>,
910        platform_state: &PlatformState,
911        seed: u64,
912        name: &str,
913        platform_version: &PlatformVersion,
914    ) -> (
915        (
916            Identity,
917            SimpleSigner,
918            IdentityPublicKey,
919            (Document, Bytes32),
920            (Document, Bytes32),
921        ),
922        (
923            Identity,
924            SimpleSigner,
925            IdentityPublicKey,
926            (Document, Bytes32),
927            (Document, Bytes32),
928        ),
929        Arc<DataContract>,
930    ) {
931        let mut rng = StdRng::seed_from_u64(seed);
932
933        let identity_1_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
934
935        let identity_2_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
936
937        // Flip them if needed so identity 1 id is always smaller than identity 2 id
938        let (identity_1_info, identity_2_info) = if identity_1_info.0.id() < identity_2_info.0.id()
939        {
940            (identity_1_info, identity_2_info)
941        } else {
942            (identity_2_info, identity_1_info)
943        };
944
945        let ((preorder_document_1, document_1), (preorder_document_2, document_2), dpns_contract) =
946            create_dpns_name_contest_on_identities(
947                platform,
948                &identity_1_info,
949                &identity_2_info,
950                platform_state,
951                rng,
952                name,
953                None,
954                false,
955                platform_version,
956            )
957            .await;
958
959        let (identity_1, signer_1, identity_key_1) = identity_1_info;
960
961        let (identity_2, signer_2, identity_key_2) = identity_2_info;
962
963        (
964            (
965                identity_1,
966                signer_1,
967                identity_key_1,
968                preorder_document_1,
969                document_1,
970            ),
971            (
972                identity_2,
973                signer_2,
974                identity_key_2,
975                preorder_document_2,
976                document_2,
977            ),
978            dpns_contract,
979        )
980    }
981
982    pub(in crate::execution) async fn create_dpns_identity_name_contest(
983        platform: &mut TempPlatform<MockCoreRPCLike>,
984        platform_state: &PlatformState,
985        seed: u64,
986        name: &str,
987        platform_version: &PlatformVersion,
988    ) -> (Identity, Identity, Arc<DataContract>) {
989        let mut rng = StdRng::seed_from_u64(seed);
990
991        let identity_1_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
992
993        let identity_2_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
994
995        // Flip them if needed so identity 1 id is always smaller than identity 2 id
996        let (identity_1_info, identity_2_info) = if identity_1_info.0.id() < identity_2_info.0.id()
997        {
998            (identity_1_info, identity_2_info)
999        } else {
1000            (identity_2_info, identity_1_info)
1001        };
1002
1003        let (_, _, dpns_contract) = create_dpns_name_contest_on_identities(
1004            platform,
1005            &identity_1_info,
1006            &identity_2_info,
1007            platform_state,
1008            rng,
1009            name,
1010            None,
1011            false,
1012            platform_version,
1013        )
1014        .await;
1015        (identity_1_info.0, identity_2_info.0, dpns_contract)
1016    }
1017
1018    /// This can be useful if we already created the identities and we reuse the seed
1019    pub(in crate::execution) async fn create_dpns_identity_name_contest_skip_creating_identities(
1020        platform: &mut TempPlatform<MockCoreRPCLike>,
1021        platform_state: &PlatformState,
1022        seed: u64,
1023        name: &str,
1024        nonce_offset: Option<IdentityNonce>,
1025        platform_version: &PlatformVersion,
1026    ) -> (Identity, Identity, Arc<DataContract>) {
1027        let mut rng = StdRng::seed_from_u64(seed);
1028
1029        let identity_1_info = setup_identity_without_adding_it(rng.gen(), dash_to_credits!(0.5));
1030
1031        let identity_2_info = setup_identity_without_adding_it(rng.gen(), dash_to_credits!(0.5));
1032
1033        // Flip them if needed so identity 1 id is always smaller than identity 2 id
1034        let (identity_1_info, identity_2_info) = if identity_1_info.0.id() < identity_2_info.0.id()
1035        {
1036            (identity_1_info, identity_2_info)
1037        } else {
1038            (identity_2_info, identity_1_info)
1039        };
1040
1041        let (_, _, dpns_contract) = create_dpns_name_contest_on_identities(
1042            platform,
1043            &identity_1_info,
1044            &identity_2_info,
1045            platform_state,
1046            rng,
1047            name,
1048            nonce_offset,
1049            true, //we should also skip preorder
1050            platform_version,
1051        )
1052        .await;
1053        (identity_1_info.0, identity_2_info.0, dpns_contract)
1054    }
1055
1056    pub(in crate::execution) async fn create_dpns_contract_name_contest(
1057        platform: &mut TempPlatform<MockCoreRPCLike>,
1058        platform_state: &PlatformState,
1059        seed: u64,
1060        name: &str,
1061        platform_version: &PlatformVersion,
1062    ) -> (Identity, Identity, DataContract) {
1063        let mut rng = StdRng::seed_from_u64(seed);
1064
1065        let identity_1_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1066
1067        let identity_2_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1068
1069        // Flip them if needed so identity 1 id is always smaller than identity 2 id
1070        let (identity_1_info, identity_2_info) = if identity_1_info.0.id() < identity_2_info.0.id()
1071        {
1072            (identity_1_info, identity_2_info)
1073        } else {
1074            (identity_2_info, identity_1_info)
1075        };
1076
1077        let dashpay_contract = setup_contract(
1078            &platform.drive,
1079            "tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable.json",
1080            None,
1081            None,
1082            None::<fn(&mut DataContract)>,
1083            None,
1084            None,
1085        );
1086
1087        let card_game = setup_contract(
1088            &platform.drive,
1089            "tests/supporting_files/contract/crypto-card-game/crypto-card-game-direct-purchase.json",
1090            None,
1091            None,
1092            None::<fn(&mut DataContract)>,
1093            None,
1094            None,
1095        );
1096
1097        let (_, _, dpns_contract) = create_dpns_name_contest_on_identities_for_contract_records(
1098            platform,
1099            &identity_1_info,
1100            &identity_2_info,
1101            &dashpay_contract,
1102            &card_game,
1103            platform_state,
1104            rng,
1105            name,
1106            platform_version,
1107        )
1108        .await;
1109        (identity_1_info.0, identity_2_info.0, dpns_contract)
1110    }
1111
1112    #[allow(clippy::too_many_arguments)]
1113    async fn create_dpns_name_contest_on_identities(
1114        platform: &mut TempPlatform<MockCoreRPCLike>,
1115        identity_1: &(Identity, SimpleSigner, IdentityPublicKey),
1116        identity_2: &(Identity, SimpleSigner, IdentityPublicKey),
1117        platform_state: &PlatformState,
1118        mut rng: StdRng,
1119        name: &str,
1120        nonce_offset: Option<IdentityNonce>,
1121        skip_preorder: bool,
1122        platform_version: &PlatformVersion,
1123    ) -> (
1124        ((Document, Bytes32), (Document, Bytes32)),
1125        ((Document, Bytes32), (Document, Bytes32)),
1126        Arc<DataContract>,
1127    ) {
1128        let (identity_1, signer_1, key_1) = identity_1;
1129
1130        let (identity_2, signer_2, key_2) = identity_2;
1131
1132        let dpns = platform.drive.cache.system_data_contracts.load_dpns();
1133        let dpns_contract = dpns.clone();
1134
1135        let preorder = dpns_contract
1136            .document_type_for_name("preorder")
1137            .expect("expected a profile document type");
1138
1139        assert!(!preorder.documents_mutable());
1140        assert!(preorder.documents_can_be_deleted());
1141        assert!(!preorder.documents_transferable().is_transferable());
1142
1143        let domain = dpns_contract
1144            .document_type_for_name("domain")
1145            .expect("expected a profile document type");
1146
1147        assert!(!domain.documents_mutable());
1148        // Deletion is disabled with data trigger
1149        assert!(domain.documents_can_be_deleted());
1150        assert!(domain.documents_transferable().is_transferable());
1151
1152        let entropy = Bytes32::random_with_rng(&mut rng);
1153
1154        let mut preorder_document_1 = preorder
1155            .random_document_with_identifier_and_entropy(
1156                &mut rng,
1157                identity_1.id(),
1158                entropy,
1159                DocumentFieldFillType::FillIfNotRequired,
1160                DocumentFieldFillSize::AnyDocumentFillSize,
1161                platform_version,
1162            )
1163            .expect("expected a random document");
1164
1165        let mut preorder_document_2 = preorder
1166            .random_document_with_identifier_and_entropy(
1167                &mut rng,
1168                identity_2.id(),
1169                entropy,
1170                DocumentFieldFillType::FillIfNotRequired,
1171                DocumentFieldFillSize::AnyDocumentFillSize,
1172                platform_version,
1173            )
1174            .expect("expected a random document");
1175
1176        let mut document_1 = domain
1177            .random_document_with_identifier_and_entropy(
1178                &mut rng,
1179                identity_1.id(),
1180                entropy,
1181                DocumentFieldFillType::FillIfNotRequired,
1182                DocumentFieldFillSize::AnyDocumentFillSize,
1183                platform_version,
1184            )
1185            .expect("expected a random document");
1186
1187        let mut document_2 = domain
1188            .random_document_with_identifier_and_entropy(
1189                &mut rng,
1190                identity_2.id(),
1191                entropy,
1192                DocumentFieldFillType::FillIfNotRequired,
1193                DocumentFieldFillSize::AnyDocumentFillSize,
1194                platform_version,
1195            )
1196            .expect("expected a random document");
1197
1198        document_1.set("parentDomainName", "dash".into());
1199        document_1.set("normalizedParentDomainName", "dash".into());
1200        document_1.set("label", name.into());
1201        document_1.set(
1202            "normalizedLabel",
1203            convert_to_homograph_safe_chars(name).into(),
1204        );
1205        document_1.set("records.identity", document_1.owner_id().into());
1206        document_1.set("subdomainRules.allowSubdomains", false.into());
1207
1208        document_2.set("parentDomainName", "dash".into());
1209        document_2.set("normalizedParentDomainName", "dash".into());
1210        document_2.set("label", name.into());
1211        document_2.set(
1212            "normalizedLabel",
1213            convert_to_homograph_safe_chars(name).into(),
1214        );
1215        document_2.set("records.identity", document_2.owner_id().into());
1216        document_2.set("subdomainRules.allowSubdomains", false.into());
1217
1218        let salt_1: [u8; 32] = rng.gen();
1219        let salt_2: [u8; 32] = rng.gen();
1220
1221        let mut salted_domain_buffer_1: Vec<u8> = vec![];
1222        salted_domain_buffer_1.extend(salt_1);
1223        salted_domain_buffer_1.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1224
1225        let salted_domain_hash_1 = hash_double(salted_domain_buffer_1);
1226
1227        let mut salted_domain_buffer_2: Vec<u8> = vec![];
1228        salted_domain_buffer_2.extend(salt_2);
1229        salted_domain_buffer_2.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1230
1231        let salted_domain_hash_2 = hash_double(salted_domain_buffer_2);
1232
1233        preorder_document_1.set("saltedDomainHash", salted_domain_hash_1.into());
1234        preorder_document_2.set("saltedDomainHash", salted_domain_hash_2.into());
1235
1236        document_1.set("preorderSalt", salt_1.into());
1237        document_2.set("preorderSalt", salt_2.into());
1238
1239        let documents_batch_create_preorder_transition_1 =
1240            BatchTransition::new_document_creation_transition_from_document(
1241                preorder_document_1.clone(),
1242                preorder,
1243                entropy.0,
1244                key_1,
1245                2 + nonce_offset.unwrap_or_default(),
1246                0,
1247                None,
1248                signer_1,
1249                platform_version,
1250                None,
1251            )
1252            .await
1253            .expect("expect to create documents batch transition");
1254
1255        let documents_batch_create_serialized_preorder_transition_1 =
1256            documents_batch_create_preorder_transition_1
1257                .serialize_to_bytes()
1258                .expect("expected documents batch serialized state transition");
1259
1260        let documents_batch_create_preorder_transition_2 =
1261            BatchTransition::new_document_creation_transition_from_document(
1262                preorder_document_2.clone(),
1263                preorder,
1264                entropy.0,
1265                key_2,
1266                2 + nonce_offset.unwrap_or_default(),
1267                0,
1268                None,
1269                signer_2,
1270                platform_version,
1271                None,
1272            )
1273            .await
1274            .expect("expect to create documents batch transition");
1275
1276        let documents_batch_create_serialized_preorder_transition_2 =
1277            documents_batch_create_preorder_transition_2
1278                .serialize_to_bytes()
1279                .expect("expected documents batch serialized state transition");
1280
1281        let documents_batch_create_transition_1 =
1282            BatchTransition::new_document_creation_transition_from_document(
1283                document_1.clone(),
1284                domain,
1285                entropy.0,
1286                key_1,
1287                3 + nonce_offset.unwrap_or_default(),
1288                0,
1289                None,
1290                signer_1,
1291                platform_version,
1292                None,
1293            )
1294            .await
1295            .expect("expect to create documents batch transition");
1296
1297        let documents_batch_create_serialized_transition_1 = documents_batch_create_transition_1
1298            .serialize_to_bytes()
1299            .expect("expected documents batch serialized state transition");
1300
1301        let documents_batch_create_transition_2 =
1302            BatchTransition::new_document_creation_transition_from_document(
1303                document_2.clone(),
1304                domain,
1305                entropy.0,
1306                key_2,
1307                3 + nonce_offset.unwrap_or_default(),
1308                0,
1309                None,
1310                signer_2,
1311                platform_version,
1312                None,
1313            )
1314            .await
1315            .expect("expect to create documents batch transition");
1316
1317        let documents_batch_create_serialized_transition_2 = documents_batch_create_transition_2
1318            .serialize_to_bytes()
1319            .expect("expected documents batch serialized state transition");
1320
1321        if !skip_preorder {
1322            let transaction = platform.drive.grove.start_transaction();
1323
1324            let processing_result = platform
1325                .platform
1326                .process_raw_state_transitions(
1327                    &[
1328                        documents_batch_create_serialized_preorder_transition_1.clone(),
1329                        documents_batch_create_serialized_preorder_transition_2.clone(),
1330                    ],
1331                    platform_state,
1332                    &BlockInfo::default_with_time(
1333                        platform_state
1334                            .last_committed_block_time_ms()
1335                            .unwrap_or_default()
1336                            + 3000,
1337                    ),
1338                    &transaction,
1339                    platform_version,
1340                    false,
1341                    None,
1342                )
1343                .expect("expected to process state transition");
1344
1345            platform
1346                .drive
1347                .grove
1348                .commit_transaction(transaction)
1349                .unwrap()
1350                .expect("expected to commit transaction");
1351
1352            let successful_count = processing_result
1353                .execution_results()
1354                .iter()
1355                .filter(|result| {
1356                    assert_matches!(
1357                        result,
1358                        StateTransitionExecutionResult::SuccessfulExecution { .. }
1359                    );
1360                    true
1361                })
1362                .count();
1363
1364            assert_eq!(successful_count, 2);
1365        }
1366
1367        let transaction = platform.drive.grove.start_transaction();
1368
1369        let processing_result = platform
1370            .platform
1371            .process_raw_state_transitions(
1372                &[
1373                    documents_batch_create_serialized_transition_1.clone(),
1374                    documents_batch_create_serialized_transition_2.clone(),
1375                ],
1376                platform_state,
1377                &BlockInfo::default_with_time(
1378                    platform_state
1379                        .last_committed_block_time_ms()
1380                        .unwrap_or_default()
1381                        + 3000,
1382                ),
1383                &transaction,
1384                platform_version,
1385                false,
1386                None,
1387            )
1388            .expect("expected to process state transition");
1389
1390        platform
1391            .drive
1392            .grove
1393            .commit_transaction(transaction)
1394            .unwrap()
1395            .expect("expected to commit transaction");
1396
1397        let successful_count = processing_result
1398            .execution_results()
1399            .iter()
1400            .filter(|result| {
1401                assert_matches!(
1402                    result,
1403                    StateTransitionExecutionResult::SuccessfulExecution { .. }
1404                );
1405                true
1406            })
1407            .count();
1408
1409        assert_eq!(successful_count, 2);
1410        (
1411            ((preorder_document_1, entropy), (document_1, entropy)),
1412            ((preorder_document_2, entropy), (document_2, entropy)),
1413            dpns_contract,
1414        )
1415    }
1416
1417    #[allow(clippy::too_many_arguments)]
1418    async fn create_dpns_name_contest_on_identities_for_contract_records(
1419        platform: &mut TempPlatform<MockCoreRPCLike>,
1420        identity_1: &(Identity, SimpleSigner, IdentityPublicKey),
1421        identity_2: &(Identity, SimpleSigner, IdentityPublicKey),
1422        contract_1: &DataContract,
1423        contract_2: &DataContract,
1424        platform_state: &PlatformState,
1425        mut rng: StdRng,
1426        name: &str,
1427        platform_version: &PlatformVersion,
1428    ) -> (
1429        ((Document, Bytes32), (Document, Bytes32)),
1430        ((Document, Bytes32), (Document, Bytes32)),
1431        DataContract,
1432    ) {
1433        let (identity_1, signer_1, key_1) = identity_1;
1434
1435        let (identity_2, signer_2, key_2) = identity_2;
1436
1437        let dpns_contract = setup_contract(
1438            &platform.drive,
1439            "tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-with-contract-id.json",
1440            None,
1441            None,
1442            None::<fn(&mut DataContract)>,
1443            None,
1444            None,
1445        );
1446
1447        let preorder = dpns_contract
1448            .document_type_for_name("preorder")
1449            .expect("expected a profile document type");
1450
1451        assert!(!preorder.documents_mutable());
1452        assert!(preorder.documents_can_be_deleted());
1453        assert!(!preorder.documents_transferable().is_transferable());
1454
1455        let domain = dpns_contract
1456            .document_type_for_name("domain")
1457            .expect("expected a profile document type");
1458
1459        assert!(!domain.documents_mutable());
1460        // Deletion is disabled with data trigger
1461        assert!(domain.documents_can_be_deleted());
1462        assert!(domain.documents_transferable().is_transferable());
1463
1464        let entropy = Bytes32::random_with_rng(&mut rng);
1465
1466        let mut preorder_document_1 = preorder
1467            .random_document_with_identifier_and_entropy(
1468                &mut rng,
1469                identity_1.id(),
1470                entropy,
1471                DocumentFieldFillType::FillIfNotRequired,
1472                DocumentFieldFillSize::AnyDocumentFillSize,
1473                platform_version,
1474            )
1475            .expect("expected a random document");
1476
1477        let mut preorder_document_2 = preorder
1478            .random_document_with_identifier_and_entropy(
1479                &mut rng,
1480                identity_2.id(),
1481                entropy,
1482                DocumentFieldFillType::FillIfNotRequired,
1483                DocumentFieldFillSize::AnyDocumentFillSize,
1484                platform_version,
1485            )
1486            .expect("expected a random document");
1487
1488        let mut document_1 = domain
1489            .random_document_with_identifier_and_entropy(
1490                &mut rng,
1491                identity_1.id(),
1492                entropy,
1493                DocumentFieldFillType::FillIfNotRequired,
1494                DocumentFieldFillSize::AnyDocumentFillSize,
1495                platform_version,
1496            )
1497            .expect("expected a random document");
1498
1499        let mut document_2 = domain
1500            .random_document_with_identifier_and_entropy(
1501                &mut rng,
1502                identity_2.id(),
1503                entropy,
1504                DocumentFieldFillType::FillIfNotRequired,
1505                DocumentFieldFillSize::AnyDocumentFillSize,
1506                platform_version,
1507            )
1508            .expect("expected a random document");
1509
1510        document_1.set("parentDomainName", "dash".into());
1511        document_1.set("normalizedParentDomainName", "dash".into());
1512        document_1.set("label", name.into());
1513        document_1.set(
1514            "normalizedLabel",
1515            convert_to_homograph_safe_chars(name).into(),
1516        );
1517        document_1.remove("records.identity");
1518        document_1.set("records.contract", contract_1.id().into());
1519        document_1.set("subdomainRules.allowSubdomains", false.into());
1520
1521        document_2.set("parentDomainName", "dash".into());
1522        document_2.set("normalizedParentDomainName", "dash".into());
1523        document_2.set("label", name.into());
1524        document_2.set(
1525            "normalizedLabel",
1526            convert_to_homograph_safe_chars(name).into(),
1527        );
1528        document_2.remove("records.identity");
1529        document_2.set("records.contract", contract_2.id().into());
1530        document_2.set("subdomainRules.allowSubdomains", false.into());
1531
1532        let salt_1: [u8; 32] = rng.gen();
1533        let salt_2: [u8; 32] = rng.gen();
1534
1535        let mut salted_domain_buffer_1: Vec<u8> = vec![];
1536        salted_domain_buffer_1.extend(salt_1);
1537        salted_domain_buffer_1.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1538
1539        let salted_domain_hash_1 = hash_double(salted_domain_buffer_1);
1540
1541        let mut salted_domain_buffer_2: Vec<u8> = vec![];
1542        salted_domain_buffer_2.extend(salt_2);
1543        salted_domain_buffer_2.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1544
1545        let salted_domain_hash_2 = hash_double(salted_domain_buffer_2);
1546
1547        preorder_document_1.set("saltedDomainHash", salted_domain_hash_1.into());
1548        preorder_document_2.set("saltedDomainHash", salted_domain_hash_2.into());
1549
1550        document_1.set("preorderSalt", salt_1.into());
1551        document_2.set("preorderSalt", salt_2.into());
1552
1553        let documents_batch_create_preorder_transition_1 =
1554            BatchTransition::new_document_creation_transition_from_document(
1555                preorder_document_1.clone(),
1556                preorder,
1557                entropy.0,
1558                key_1,
1559                2,
1560                0,
1561                None,
1562                signer_1,
1563                platform_version,
1564                None,
1565            )
1566            .await
1567            .expect("expect to create documents batch transition");
1568
1569        let documents_batch_create_serialized_preorder_transition_1 =
1570            documents_batch_create_preorder_transition_1
1571                .serialize_to_bytes()
1572                .expect("expected documents batch serialized state transition");
1573
1574        let documents_batch_create_preorder_transition_2 =
1575            BatchTransition::new_document_creation_transition_from_document(
1576                preorder_document_2.clone(),
1577                preorder,
1578                entropy.0,
1579                key_2,
1580                2,
1581                0,
1582                None,
1583                signer_2,
1584                platform_version,
1585                None,
1586            )
1587            .await
1588            .expect("expect to create documents batch transition");
1589
1590        let documents_batch_create_serialized_preorder_transition_2 =
1591            documents_batch_create_preorder_transition_2
1592                .serialize_to_bytes()
1593                .expect("expected documents batch serialized state transition");
1594
1595        let documents_batch_create_transition_1 =
1596            BatchTransition::new_document_creation_transition_from_document(
1597                document_1.clone(),
1598                domain,
1599                entropy.0,
1600                key_1,
1601                3,
1602                0,
1603                None,
1604                signer_1,
1605                platform_version,
1606                None,
1607            )
1608            .await
1609            .expect("expect to create documents batch transition");
1610
1611        let documents_batch_create_serialized_transition_1 = documents_batch_create_transition_1
1612            .serialize_to_bytes()
1613            .expect("expected documents batch serialized state transition");
1614
1615        let documents_batch_create_transition_2 =
1616            BatchTransition::new_document_creation_transition_from_document(
1617                document_2.clone(),
1618                domain,
1619                entropy.0,
1620                key_2,
1621                3,
1622                0,
1623                None,
1624                signer_2,
1625                platform_version,
1626                None,
1627            )
1628            .await
1629            .expect("expect to create documents batch transition");
1630
1631        let documents_batch_create_serialized_transition_2 = documents_batch_create_transition_2
1632            .serialize_to_bytes()
1633            .expect("expected documents batch serialized state transition");
1634
1635        let transaction = platform.drive.grove.start_transaction();
1636
1637        let processing_result = platform
1638            .platform
1639            .process_raw_state_transitions(
1640                &[
1641                    documents_batch_create_serialized_preorder_transition_1.clone(),
1642                    documents_batch_create_serialized_preorder_transition_2.clone(),
1643                ],
1644                platform_state,
1645                &BlockInfo::default_with_time(
1646                    platform_state
1647                        .last_committed_block_time_ms()
1648                        .unwrap_or_default()
1649                        + 3000,
1650                ),
1651                &transaction,
1652                platform_version,
1653                false,
1654                None,
1655            )
1656            .expect("expected to process state transition");
1657
1658        platform
1659            .drive
1660            .grove
1661            .commit_transaction(transaction)
1662            .unwrap()
1663            .expect("expected to commit transaction");
1664
1665        assert_eq!(processing_result.valid_count(), 2);
1666
1667        let transaction = platform.drive.grove.start_transaction();
1668
1669        let processing_result = platform
1670            .platform
1671            .process_raw_state_transitions(
1672                &[
1673                    documents_batch_create_serialized_transition_1.clone(),
1674                    documents_batch_create_serialized_transition_2.clone(),
1675                ],
1676                platform_state,
1677                &BlockInfo::default_with_time(
1678                    platform_state
1679                        .last_committed_block_time_ms()
1680                        .unwrap_or_default()
1681                        + 3000,
1682                ),
1683                &transaction,
1684                platform_version,
1685                false,
1686                None,
1687            )
1688            .expect("expected to process state transition");
1689
1690        platform
1691            .drive
1692            .grove
1693            .commit_transaction(transaction)
1694            .unwrap()
1695            .expect("expected to commit transaction");
1696
1697        assert_eq!(processing_result.valid_count(), 2);
1698        (
1699            ((preorder_document_1, entropy), (document_1, entropy)),
1700            ((preorder_document_2, entropy), (document_2, entropy)),
1701            dpns_contract,
1702        )
1703    }
1704
1705    pub(in crate::execution) async fn add_contender_to_dpns_name_contest(
1706        platform: &mut TempPlatform<MockCoreRPCLike>,
1707        platform_state: &PlatformState,
1708        seed: u64,
1709        name: &str,
1710        expect_err: Option<&str>,
1711        platform_version: &PlatformVersion,
1712    ) -> Identity {
1713        let mut rng = StdRng::seed_from_u64(seed);
1714
1715        let (identity_1, signer_1, key_1) =
1716            setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1717
1718        let dpns = platform.drive.cache.system_data_contracts.load_dpns();
1719        let dpns_contract = dpns.clone();
1720
1721        let preorder = dpns_contract
1722            .document_type_for_name("preorder")
1723            .expect("expected a profile document type");
1724
1725        let domain = dpns_contract
1726            .document_type_for_name("domain")
1727            .expect("expected a profile document type");
1728
1729        let entropy = Bytes32::random_with_rng(&mut rng);
1730
1731        let mut preorder_document_1 = preorder
1732            .random_document_with_identifier_and_entropy(
1733                &mut rng,
1734                identity_1.id(),
1735                entropy,
1736                DocumentFieldFillType::FillIfNotRequired,
1737                DocumentFieldFillSize::AnyDocumentFillSize,
1738                platform_version,
1739            )
1740            .expect("expected a random document");
1741
1742        let mut document_1 = domain
1743            .random_document_with_identifier_and_entropy(
1744                &mut rng,
1745                identity_1.id(),
1746                entropy,
1747                DocumentFieldFillType::FillIfNotRequired,
1748                DocumentFieldFillSize::AnyDocumentFillSize,
1749                platform_version,
1750            )
1751            .expect("expected a random document");
1752
1753        document_1.set("parentDomainName", "dash".into());
1754        document_1.set("normalizedParentDomainName", "dash".into());
1755        document_1.set("label", name.into());
1756        document_1.set(
1757            "normalizedLabel",
1758            convert_to_homograph_safe_chars(name).into(),
1759        );
1760        document_1.set("records.identity", document_1.owner_id().into());
1761        document_1.set("subdomainRules.allowSubdomains", false.into());
1762
1763        let salt_1: [u8; 32] = rng.gen();
1764
1765        let mut salted_domain_buffer_1: Vec<u8> = vec![];
1766        salted_domain_buffer_1.extend(salt_1);
1767        salted_domain_buffer_1.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1768
1769        let salted_domain_hash_1 = hash_double(salted_domain_buffer_1);
1770
1771        preorder_document_1.set("saltedDomainHash", salted_domain_hash_1.into());
1772
1773        document_1.set("preorderSalt", salt_1.into());
1774
1775        let documents_batch_create_preorder_transition_1 =
1776            BatchTransition::new_document_creation_transition_from_document(
1777                preorder_document_1,
1778                preorder,
1779                entropy.0,
1780                &key_1,
1781                2,
1782                0,
1783                None,
1784                &signer_1,
1785                platform_version,
1786                None,
1787            )
1788            .await
1789            .expect("expect to create documents batch transition");
1790
1791        let documents_batch_create_serialized_preorder_transition_1 =
1792            documents_batch_create_preorder_transition_1
1793                .serialize_to_bytes()
1794                .expect("expected documents batch serialized state transition");
1795
1796        let documents_batch_create_transition_1 =
1797            BatchTransition::new_document_creation_transition_from_document(
1798                document_1,
1799                domain,
1800                entropy.0,
1801                &key_1,
1802                3,
1803                0,
1804                None,
1805                &signer_1,
1806                platform_version,
1807                None,
1808            )
1809            .await
1810            .expect("expect to create documents batch transition");
1811
1812        let documents_batch_create_serialized_transition_1 = documents_batch_create_transition_1
1813            .serialize_to_bytes()
1814            .expect("expected documents batch serialized state transition");
1815
1816        let transaction = platform.drive.grove.start_transaction();
1817
1818        let processing_result = platform
1819            .platform
1820            .process_raw_state_transitions(
1821                &[documents_batch_create_serialized_preorder_transition_1.clone()],
1822                platform_state,
1823                &BlockInfo::default_with_time(
1824                    platform_state
1825                        .last_committed_block_time_ms()
1826                        .unwrap_or_default()
1827                        + 3000,
1828                ),
1829                &transaction,
1830                platform_version,
1831                false,
1832                None,
1833            )
1834            .expect("expected to process state transition");
1835
1836        platform
1837            .drive
1838            .grove
1839            .commit_transaction(transaction)
1840            .unwrap()
1841            .expect("expected to commit transaction");
1842
1843        assert_eq!(processing_result.valid_count(), 1);
1844
1845        let transaction = platform.drive.grove.start_transaction();
1846
1847        let processing_result = platform
1848            .platform
1849            .process_raw_state_transitions(
1850                &[documents_batch_create_serialized_transition_1.clone()],
1851                platform_state,
1852                &BlockInfo::default_with_time(
1853                    platform_state
1854                        .last_committed_block_time_ms()
1855                        .unwrap_or_default()
1856                        + 3000,
1857                ),
1858                &transaction,
1859                platform_version,
1860                false,
1861                None,
1862            )
1863            .expect("expected to process state transition");
1864
1865        platform
1866            .drive
1867            .grove
1868            .commit_transaction(transaction)
1869            .unwrap()
1870            .expect("expected to commit transaction");
1871
1872        if let Some(expected_err) = expect_err {
1873            let result = processing_result.into_execution_results().remove(0);
1874
1875            let StateTransitionExecutionResult::PaidConsensusError {
1876                error: consensus_error,
1877                ..
1878            } = result
1879            else {
1880                panic!("expected a paid consensus error");
1881            };
1882            assert_eq!(consensus_error.to_string(), expected_err);
1883        } else {
1884            assert_eq!(processing_result.valid_count(), 1);
1885        }
1886        identity_1
1887    }
1888
1889    pub(in crate::execution) fn verify_dpns_name_contest(
1890        platform: &mut TempPlatform<MockCoreRPCLike>,
1891        platform_state: &Guard<Arc<PlatformState>>,
1892        dpns_contract: &DataContract,
1893        identity_1: &Identity,
1894        identity_2: &Identity,
1895        name: &str,
1896        platform_version: &PlatformVersion,
1897    ) {
1898        // Now let's run a query for the vote totals
1899
1900        let domain = dpns_contract
1901            .document_type_for_name("domain")
1902            .expect("expected a profile document type");
1903
1904        let config = bincode::config::standard()
1905            .with_big_endian()
1906            .with_no_limit();
1907
1908        let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config)
1909            .expect("expected to encode the word dash");
1910
1911        let quantum_encoded =
1912            bincode::encode_to_vec(Value::Text(convert_to_homograph_safe_chars(name)), config)
1913                .expect("expected to encode the word quantum");
1914
1915        let index_name = "parentNameAndLabel".to_string();
1916
1917        let query_validation_result = platform
1918            .query_contested_resource_vote_state(
1919                GetContestedResourceVoteStateRequest {
1920                    version: Some(get_contested_resource_vote_state_request::Version::V0(
1921                        GetContestedResourceVoteStateRequestV0 {
1922                            contract_id: dpns_contract.id().to_vec(),
1923                            document_type_name: domain.name().clone(),
1924                            index_name: index_name.clone(),
1925                            index_values: vec![dash_encoded.clone(), quantum_encoded.clone()],
1926                            result_type: ResultType::DocumentsAndVoteTally as i32,
1927                            allow_include_locked_and_abstaining_vote_tally: true,
1928                            start_at_identifier_info: None,
1929                            count: None,
1930                            prove: false,
1931                        },
1932                    )),
1933                },
1934                platform_state,
1935                platform_version,
1936            )
1937            .expect("expected to execute query")
1938            .into_data()
1939            .expect("expected query to be valid");
1940
1941        let get_contested_resource_vote_state_response::Version::V0(
1942            GetContestedResourceVoteStateResponseV0 {
1943                metadata: _,
1944                result,
1945            },
1946        ) = query_validation_result.version.expect("expected a version");
1947
1948        let Some(
1949            get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders(
1950                get_contested_resource_vote_state_response_v0::ContestedResourceContenders {
1951                    contenders,
1952                    ..
1953                },
1954            ),
1955        ) = result
1956        else {
1957            panic!("expected contenders")
1958        };
1959
1960        assert_eq!(contenders.len(), 2);
1961
1962        let first_contender = contenders.first().unwrap();
1963
1964        let second_contender = contenders.last().unwrap();
1965
1966        let first_contender_document = Document::from_bytes(
1967            first_contender
1968                .document
1969                .as_ref()
1970                .expect("expected a document")
1971                .as_slice(),
1972            domain,
1973            platform_version,
1974        )
1975        .expect("expected to get document");
1976
1977        let second_contender_document = Document::from_bytes(
1978            second_contender
1979                .document
1980                .as_ref()
1981                .expect("expected a document")
1982                .as_slice(),
1983            domain,
1984            platform_version,
1985        )
1986        .expect("expected to get document");
1987
1988        assert_ne!(first_contender_document, second_contender_document);
1989
1990        assert_eq!(first_contender.identifier, identity_1.id().to_vec());
1991
1992        assert_eq!(second_contender.identifier, identity_2.id().to_vec());
1993
1994        assert_eq!(first_contender.vote_count, Some(0));
1995
1996        assert_eq!(second_contender.vote_count, Some(0));
1997
1998        let GetContestedResourceVoteStateResponse { version } = platform
1999            .query_contested_resource_vote_state(
2000                GetContestedResourceVoteStateRequest {
2001                    version: Some(get_contested_resource_vote_state_request::Version::V0(
2002                        GetContestedResourceVoteStateRequestV0 {
2003                            contract_id: dpns_contract.id().to_vec(),
2004                            document_type_name: domain.name().clone(),
2005                            index_name: "parentNameAndLabel".to_string(),
2006                            index_values: vec![dash_encoded, quantum_encoded],
2007                            result_type: ResultType::DocumentsAndVoteTally as i32,
2008                            allow_include_locked_and_abstaining_vote_tally: true,
2009                            start_at_identifier_info: None,
2010                            count: None,
2011                            prove: true,
2012                        },
2013                    )),
2014                },
2015                platform_state,
2016                platform_version,
2017            )
2018            .expect("expected to execute query")
2019            .into_data()
2020            .expect("expected query to be valid");
2021
2022        let get_contested_resource_vote_state_response::Version::V0(
2023            GetContestedResourceVoteStateResponseV0 {
2024                metadata: _,
2025                result,
2026            },
2027        ) = version.expect("expected a version");
2028
2029        let Some(get_contested_resource_vote_state_response_v0::Result::Proof(proof)) = result
2030        else {
2031            panic!("expected contenders")
2032        };
2033
2034        let resolved_contested_document_vote_poll_drive_query =
2035            ResolvedContestedDocumentVotePollDriveQuery {
2036                vote_poll: ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed {
2037                    contract: DataContractResolvedInfo::BorrowedDataContract(dpns_contract),
2038                    document_type_name: domain.name().clone(),
2039                    index_name: index_name.clone(),
2040                    index_values: vec![
2041                        Value::Text("dash".to_string()),
2042                        Value::Text(convert_to_homograph_safe_chars(name)),
2043                    ],
2044                },
2045                result_type: DocumentsAndVoteTally,
2046                offset: None,
2047                limit: None,
2048                start_at: None,
2049                allow_include_locked_and_abstaining_vote_tally: true,
2050            };
2051
2052        let (_, result) = resolved_contested_document_vote_poll_drive_query
2053            .verify_vote_poll_vote_state_proof(proof.grovedb_proof.as_ref(), platform_version)
2054            .expect("expected to verify proof");
2055
2056        let contenders = result.contenders;
2057
2058        assert_eq!(contenders.len(), 2);
2059
2060        let first_contender = contenders.first().unwrap();
2061
2062        let second_contender = contenders.last().unwrap();
2063
2064        let first_contender_document = Document::from_bytes(
2065            first_contender
2066                .serialized_document()
2067                .as_ref()
2068                .expect("expected a document")
2069                .as_slice(),
2070            domain,
2071            platform_version,
2072        )
2073        .expect("expected to get document");
2074
2075        let second_contender_document = Document::from_bytes(
2076            second_contender
2077                .serialized_document()
2078                .as_ref()
2079                .expect("expected a document")
2080                .as_slice(),
2081            domain,
2082            platform_version,
2083        )
2084        .expect("expected to get document");
2085
2086        assert_ne!(first_contender_document, second_contender_document);
2087
2088        assert_eq!(first_contender.identity_id(), identity_1.id());
2089
2090        assert_eq!(second_contender.identity_id(), identity_2.id());
2091
2092        assert_eq!(first_contender.vote_tally(), Some(0));
2093
2094        assert_eq!(second_contender.vote_tally(), Some(0));
2095    }
2096
2097    #[allow(clippy::too_many_arguments)]
2098    pub(in crate::execution) async fn perform_vote(
2099        platform: &mut TempPlatform<MockCoreRPCLike>,
2100        platform_state: &Guard<Arc<PlatformState>>,
2101        dpns_contract: &DataContract,
2102        resource_vote_choice: ResourceVoteChoice,
2103        name: &str,
2104        signer: &SimpleSigner,
2105        pro_tx_hash: Identifier,
2106        voting_key: &IdentityPublicKey,
2107        nonce: IdentityNonce,
2108        expect_error: Option<&str>,
2109        platform_version: &PlatformVersion,
2110    ) {
2111        // Let's vote for contender 1
2112
2113        let vote = Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 {
2114            vote_poll: VotePoll::ContestedDocumentResourceVotePoll(
2115                ContestedDocumentResourceVotePoll {
2116                    contract_id: dpns_contract.id(),
2117                    document_type_name: "domain".to_string(),
2118                    index_name: "parentNameAndLabel".to_string(),
2119                    index_values: vec![
2120                        Value::Text("dash".to_string()),
2121                        Value::Text(convert_to_homograph_safe_chars(name)),
2122                    ],
2123                },
2124            ),
2125            resource_vote_choice,
2126        }));
2127
2128        let masternode_vote_transition = MasternodeVoteTransition::try_from_vote_with_signer(
2129            vote,
2130            signer,
2131            pro_tx_hash,
2132            voting_key,
2133            nonce,
2134            platform_version,
2135            None,
2136        )
2137        .await
2138        .expect("expected to make transition vote");
2139
2140        let masternode_vote_serialized_transition = masternode_vote_transition
2141            .serialize_to_bytes()
2142            .expect("expected documents batch serialized state transition");
2143
2144        let transaction = platform.drive.grove.start_transaction();
2145
2146        let processing_result = platform
2147            .platform
2148            .process_raw_state_transitions(
2149                &[masternode_vote_serialized_transition.clone()],
2150                platform_state,
2151                &BlockInfo::default(),
2152                &transaction,
2153                platform_version,
2154                false,
2155                None,
2156            )
2157            .expect("expected to process state transition");
2158
2159        platform
2160            .drive
2161            .grove
2162            .commit_transaction(transaction)
2163            .unwrap()
2164            .expect("expected to commit transaction");
2165
2166        let execution_result = processing_result.into_execution_results().remove(0);
2167        if let Some(error_msg) = expect_error {
2168            assert_matches!(execution_result, UnpaidConsensusError(..));
2169            let UnpaidConsensusError(consensus_error) = execution_result else {
2170                panic!()
2171            };
2172            assert_eq!(consensus_error.to_string(), error_msg)
2173        } else {
2174            assert_matches!(execution_result, SuccessfulExecution { .. });
2175        }
2176    }
2177
2178    #[allow(clippy::too_many_arguments)]
2179    pub(in crate::execution) async fn perform_votes(
2180        platform: &mut TempPlatform<MockCoreRPCLike>,
2181        dpns_contract: &DataContract,
2182        resource_vote_choice: ResourceVoteChoice,
2183        name: &str,
2184        count: u64,
2185        start_seed: u64,
2186        nonce_offset: Option<IdentityNonce>,
2187        platform_version: &PlatformVersion,
2188    ) -> Vec<(Identifier, Identity, SimpleSigner, IdentityPublicKey)> {
2189        let mut masternode_infos = vec![];
2190        for i in 0..count {
2191            let (pro_tx_hash_bytes, voting_identity, signer, voting_key) =
2192                setup_masternode_voting_identity(platform, start_seed + i, platform_version);
2193
2194            let platform_state = platform.state.load();
2195
2196            perform_vote(
2197                platform,
2198                &platform_state,
2199                dpns_contract,
2200                resource_vote_choice,
2201                name,
2202                &signer,
2203                pro_tx_hash_bytes,
2204                &voting_key,
2205                1 + nonce_offset.unwrap_or_default(),
2206                None,
2207                platform_version,
2208            )
2209            .await;
2210
2211            masternode_infos.push((pro_tx_hash_bytes, voting_identity, signer, voting_key));
2212        }
2213        masternode_infos
2214    }
2215
2216    pub(in crate::execution) async fn perform_votes_multi(
2217        platform: &mut TempPlatform<MockCoreRPCLike>,
2218        dpns_contract: &DataContract,
2219        resource_vote_choices: Vec<(ResourceVoteChoice, u64)>,
2220        name: &str,
2221        start_seed: u64,
2222        nonce_offset: Option<IdentityNonce>,
2223        platform_version: &PlatformVersion,
2224    ) -> BTreeMap<ResourceVoteChoice, Vec<(Identifier, Identity, SimpleSigner, IdentityPublicKey)>>
2225    {
2226        let mut count_aggregate = start_seed;
2227        let mut masternodes_by_vote_choice = BTreeMap::new();
2228        for (resource_vote_choice, count) in resource_vote_choices.into_iter() {
2229            let masternode_infos = perform_votes(
2230                platform,
2231                dpns_contract,
2232                resource_vote_choice,
2233                name,
2234                count,
2235                count_aggregate,
2236                nonce_offset,
2237                platform_version,
2238            )
2239            .await;
2240            masternodes_by_vote_choice.insert(resource_vote_choice, masternode_infos);
2241            count_aggregate += count;
2242        }
2243        masternodes_by_vote_choice
2244    }
2245
2246    #[allow(clippy::too_many_arguments)]
2247    pub(in crate::execution) fn get_vote_states(
2248        platform: &TempPlatform<MockCoreRPCLike>,
2249        platform_state: &PlatformState,
2250        dpns_contract: &DataContract,
2251        name: &str,
2252        count: Option<u32>,
2253        allow_include_locked_and_abstaining_vote_tally: bool,
2254        start_at_identifier_info: Option<
2255            get_contested_resource_vote_state_request_v0::StartAtIdentifierInfo,
2256        >,
2257        result_type: ResultType,
2258        platform_version: &PlatformVersion,
2259    ) -> (
2260        Vec<Contender>,
2261        Option<u32>,
2262        Option<u32>,
2263        Option<FinishedVoteInfo>,
2264    ) {
2265        // Now let's run a query for the vote totals
2266
2267        let domain = dpns_contract
2268            .document_type_for_name("domain")
2269            .expect("expected a profile document type");
2270
2271        let config = bincode::config::standard()
2272            .with_big_endian()
2273            .with_no_limit();
2274
2275        let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config)
2276            .expect("expected to encode the word dash");
2277
2278        let name_encoded =
2279            bincode::encode_to_vec(Value::Text(convert_to_homograph_safe_chars(name)), config)
2280                .expect("expected to encode the word quantum");
2281
2282        let index_name = "parentNameAndLabel".to_string();
2283
2284        let query_validation_result = platform
2285            .query_contested_resource_vote_state(
2286                GetContestedResourceVoteStateRequest {
2287                    version: Some(get_contested_resource_vote_state_request::Version::V0(
2288                        GetContestedResourceVoteStateRequestV0 {
2289                            contract_id: dpns_contract.id().to_vec(),
2290                            document_type_name: domain.name().clone(),
2291                            index_name: index_name.clone(),
2292                            index_values: vec![dash_encoded.clone(), name_encoded.clone()],
2293                            result_type: result_type as i32,
2294                            allow_include_locked_and_abstaining_vote_tally,
2295                            start_at_identifier_info,
2296                            count,
2297                            prove: false,
2298                        },
2299                    )),
2300                },
2301                platform_state,
2302                platform_version,
2303            )
2304            .expect("expected to execute query")
2305            .into_data()
2306            .expect("expected query to be valid");
2307
2308        let get_contested_resource_vote_state_response::Version::V0(
2309            GetContestedResourceVoteStateResponseV0 {
2310                metadata: _,
2311                result,
2312            },
2313        ) = query_validation_result.version.expect("expected a version");
2314
2315        let Some(
2316            get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders(
2317                get_contested_resource_vote_state_response_v0::ContestedResourceContenders {
2318                    contenders,
2319                    abstain_vote_tally,
2320                    lock_vote_tally,
2321                    finished_vote_info,
2322                },
2323            ),
2324        ) = result
2325        else {
2326            panic!("expected contenders")
2327        };
2328        (
2329            contenders
2330                .into_iter()
2331                .map(|contender| {
2332                    ContenderV0 {
2333                        identity_id: contender.identifier.try_into().expect("expected 32 bytes"),
2334                        document: contender.document.map(|document_bytes| {
2335                            Document::from_bytes(
2336                                document_bytes.as_slice(),
2337                                domain,
2338                                platform_version,
2339                            )
2340                            .expect("expected to deserialize document")
2341                        }),
2342                        vote_tally: contender.vote_count,
2343                    }
2344                    .into()
2345                })
2346                .collect(),
2347            abstain_vote_tally,
2348            lock_vote_tally,
2349            finished_vote_info,
2350        )
2351    }
2352
2353    #[allow(clippy::too_many_arguments)]
2354    pub(in crate::execution) fn get_proved_vote_states(
2355        platform: &TempPlatform<MockCoreRPCLike>,
2356        platform_state: &PlatformState,
2357        dpns_contract: &DataContract,
2358        name: &str,
2359        count: Option<u32>,
2360        allow_include_locked_and_abstaining_vote_tally: bool,
2361        start_at_identifier_info: Option<
2362            get_contested_resource_vote_state_request_v0::StartAtIdentifierInfo,
2363        >,
2364        result_type: ResultType,
2365        platform_version: &PlatformVersion,
2366    ) -> (
2367        Vec<Contender>,
2368        Option<u32>,
2369        Option<u32>,
2370        Option<(ContestedDocumentVotePollWinnerInfo, BlockInfo)>,
2371    ) {
2372        // Now let's run a query for the vote totals
2373
2374        let domain = dpns_contract
2375            .document_type_for_name("domain")
2376            .expect("expected a profile document type");
2377
2378        let config = bincode::config::standard()
2379            .with_big_endian()
2380            .with_no_limit();
2381
2382        let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config)
2383            .expect("expected to encode the word dash");
2384
2385        let name_encoded =
2386            bincode::encode_to_vec(Value::Text(convert_to_homograph_safe_chars(name)), config)
2387                .expect("expected to encode the word quantum");
2388
2389        let index_name = "parentNameAndLabel".to_string();
2390
2391        let query_validation_result = platform
2392            .query_contested_resource_vote_state(
2393                GetContestedResourceVoteStateRequest {
2394                    version: Some(get_contested_resource_vote_state_request::Version::V0(
2395                        GetContestedResourceVoteStateRequestV0 {
2396                            contract_id: dpns_contract.id().to_vec(),
2397                            document_type_name: domain.name().clone(),
2398                            index_name: index_name.clone(),
2399                            index_values: vec![dash_encoded.clone(), name_encoded.clone()],
2400                            result_type: result_type as i32,
2401                            allow_include_locked_and_abstaining_vote_tally,
2402                            start_at_identifier_info,
2403                            count,
2404                            prove: true,
2405                        },
2406                    )),
2407                },
2408                platform_state,
2409                platform_version,
2410            )
2411            .expect("expected to execute query")
2412            .into_data()
2413            .expect("expected query to be valid");
2414
2415        let get_contested_resource_vote_state_response::Version::V0(
2416            GetContestedResourceVoteStateResponseV0 {
2417                metadata: _,
2418                result,
2419            },
2420        ) = query_validation_result.version.expect("expected a version");
2421
2422        let Some(get_contested_resource_vote_state_response_v0::Result::Proof(proof)) = result
2423        else {
2424            panic!("expected contenders")
2425        };
2426
2427        let resolved_contested_document_vote_poll_drive_query =
2428            ResolvedContestedDocumentVotePollDriveQuery {
2429                vote_poll: ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed {
2430                    contract: DataContractResolvedInfo::BorrowedDataContract(dpns_contract),
2431                    document_type_name: domain.name().clone(),
2432                    index_name: index_name.clone(),
2433                    index_values: vec![
2434                        Value::Text("dash".to_string()),
2435                        Value::Text(convert_to_homograph_safe_chars(name)),
2436                    ],
2437                },
2438                result_type: ContestedDocumentVotePollDriveQueryResultType::try_from(
2439                    result_type as i32,
2440                )
2441                .expect("expected valid result type"),
2442                offset: None,
2443                limit: count.map(|a| a as u16),
2444                start_at: None,
2445                allow_include_locked_and_abstaining_vote_tally,
2446            };
2447
2448        let (_, result) = resolved_contested_document_vote_poll_drive_query
2449            .verify_vote_poll_vote_state_proof(proof.grovedb_proof.as_ref(), platform_version)
2450            .expect("expected to verify proof");
2451
2452        let abstaining_vote_tally = result.abstaining_vote_tally;
2453        let lock_vote_tally = result.locked_vote_tally;
2454
2455        let contenders = result.contenders;
2456        let finished_vote_info = result.winner;
2457        (
2458            contenders
2459                .into_iter()
2460                .map(|contender| {
2461                    ContenderV0 {
2462                        identity_id: contender.identity_id(),
2463                        document: contender
2464                            .serialized_document()
2465                            .as_ref()
2466                            .map(|document_bytes| {
2467                                Document::from_bytes(
2468                                    document_bytes.as_slice(),
2469                                    domain,
2470                                    platform_version,
2471                                )
2472                                .expect("expected to deserialize document")
2473                            }),
2474                        vote_tally: contender.vote_tally(),
2475                    }
2476                    .into()
2477                })
2478                .collect(),
2479            abstaining_vote_tally,
2480            lock_vote_tally,
2481            finished_vote_info,
2482        )
2483    }
2484
2485    pub(in crate::execution) fn create_token_contract_with_owner_identity(
2486        platform: &mut TempPlatform<MockCoreRPCLike>,
2487        identity_id: Identifier,
2488        token_configuration_modification: Option<impl FnOnce(&mut TokenConfiguration)>,
2489        contract_start_time: Option<TimestampMillis>,
2490        add_groups: Option<BTreeMap<GroupContractPosition, Group>>,
2491        contract_start_block: Option<BlockHeight>,
2492        platform_version: &PlatformVersion,
2493    ) -> (DataContract, Identifier) {
2494        let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1);
2495
2496        let basic_token_contract = setup_contract(
2497            &platform.drive,
2498            "tests/supporting_files/contract/basic-token/basic-token.json",
2499            Some(data_contract_id.to_buffer()),
2500            Some(identity_id.to_buffer()),
2501            Some(|data_contract: &mut DataContract| {
2502                data_contract.set_created_at_epoch(Some(0));
2503                data_contract.set_created_at(Some(contract_start_time.unwrap_or_default()));
2504                data_contract
2505                    .set_created_at_block_height(Some(contract_start_block.unwrap_or_default()));
2506                if let Some(token_configuration_modification) = token_configuration_modification {
2507                    let token_configuration = data_contract
2508                        .token_configuration_mut(0)
2509                        .expect("expected token configuration");
2510                    token_configuration_modification(token_configuration);
2511                }
2512                if let Some(add_groups) = add_groups {
2513                    data_contract.set_groups(add_groups);
2514                }
2515            }),
2516            None,
2517            Some(platform_version),
2518        );
2519
2520        let token_id = calculate_token_id(data_contract_id.as_bytes(), 0);
2521
2522        (basic_token_contract, token_id.into())
2523    }
2524
2525    pub(in crate::execution) fn create_card_game_internal_token_contract_with_owner_identity_burn_tokens(
2526        platform: &mut TempPlatform<MockCoreRPCLike>,
2527        identity_id: Identifier,
2528        platform_version: &PlatformVersion,
2529    ) -> (DataContract, Identifier, Identifier) {
2530        let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1);
2531
2532        let basic_token_contract = setup_contract(
2533            &platform.drive,
2534            "tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency-burn-tokens.json",
2535            Some(data_contract_id.to_buffer()),
2536            Some(identity_id.to_buffer()),
2537            Some(|data_contract: &mut DataContract| {
2538                data_contract.set_created_at_epoch(Some(0));
2539                data_contract.set_created_at(Some(0));
2540                data_contract.set_created_at_block_height(Some(0));
2541            }),
2542            None,
2543            Some(platform_version),
2544        );
2545
2546        let token_id = calculate_token_id(data_contract_id.as_bytes(), 0);
2547        let token_id_2 = calculate_token_id(data_contract_id.as_bytes(), 1);
2548
2549        (basic_token_contract, token_id.into(), token_id_2.into())
2550    }
2551
2552    pub(in crate::execution) fn create_card_game_internal_token_contract_with_owner_identity_transfer_tokens(
2553        platform: &mut TempPlatform<MockCoreRPCLike>,
2554        identity_id: Identifier,
2555        platform_version: &PlatformVersion,
2556    ) -> (DataContract, Identifier, Identifier) {
2557        let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1);
2558
2559        let basic_token_contract = setup_contract(
2560            &platform.drive,
2561            "tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json",
2562            Some(data_contract_id.to_buffer()),
2563            Some(identity_id.to_buffer()),
2564            Some(|data_contract: &mut DataContract| {
2565                data_contract.set_created_at_epoch(Some(0));
2566                data_contract.set_created_at(Some(0));
2567                data_contract.set_created_at_block_height(Some(0));
2568            }),
2569            None,
2570            Some(platform_version),
2571        );
2572
2573        let token_id = calculate_token_id(data_contract_id.as_bytes(), 0);
2574        let token_id_2 = calculate_token_id(data_contract_id.as_bytes(), 1);
2575
2576        (basic_token_contract, token_id.into(), token_id_2.into())
2577    }
2578
2579    pub(in crate::execution) fn create_card_game_external_token_contract_with_owner_identity(
2580        platform: &mut TempPlatform<MockCoreRPCLike>,
2581        token_contract_id: Identifier,
2582        token_contract_position: TokenContractPosition,
2583        token_cost_amount: TokenAmount,
2584        gas_fees_paid_by: GasFeesPaidBy,
2585        identity_id: Identifier,
2586        platform_version: &PlatformVersion,
2587    ) -> DataContract {
2588        let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1);
2589
2590        let basic_token_contract = setup_contract(
2591            &platform.drive,
2592            "tests/supporting_files/contract/crypto-card-game/crypto-card-game-use-external-currency.json",
2593            Some(data_contract_id.to_buffer()),
2594            Some(identity_id.to_buffer()),
2595            Some(|data_contract: &mut DataContract| {
2596                data_contract.set_created_at_epoch(Some(0));
2597                data_contract.set_created_at(Some(0));
2598                data_contract.set_created_at_block_height(Some(0));
2599                let document_type = data_contract.document_types_mut().get_mut("card").expect("expected a document type with name card");
2600                document_type.set_document_creation_token_cost(Some(DocumentActionTokenCost {
2601                    contract_id: Some(token_contract_id),
2602                    token_contract_position,
2603                    token_amount: token_cost_amount,
2604                    effect: DocumentActionTokenEffect::TransferTokenToContractOwner,
2605                    gas_fees_paid_by,
2606                }));
2607                let gas_fees_paid_by_int: u8 = gas_fees_paid_by.into();
2608                let schema = document_type.schema_mut();
2609                let token_cost = schema.get_mut("tokenCost").expect("expected to get token cost").expect("expected token cost to be set");
2610                let creation_token_cost = token_cost.get_mut("create").expect("expected to get creation token cost").expect("expected creation token cost to be set");
2611                creation_token_cost.set_value("contractId", token_contract_id.into()).expect("expected to set token contract id");
2612                creation_token_cost.set_value("tokenPosition", token_contract_position.into()).expect("expected to set token position");
2613                creation_token_cost.set_value("amount", token_cost_amount.into()).expect("expected to set token amount");
2614                creation_token_cost.set_value("gasFeesPaidBy", gas_fees_paid_by_int.into()).expect("expected to set token amount");
2615            }),
2616            None,
2617            Some(platform_version),
2618        );
2619
2620        basic_token_contract
2621    }
2622
2623    pub(in crate::execution) fn process_test_state_transition<S: PlatformSerializable>(
2624        platform: &mut TempPlatform<MockCoreRPCLike>,
2625        state_transition: S,
2626        platform_state: &PlatformState,
2627        platform_version: &PlatformVersion,
2628    ) -> StateTransitionsProcessingResult {
2629        let Ok(serialized_state_transition) = state_transition.serialize_to_bytes() else {
2630            panic!("expected documents batch serialized state transition")
2631        };
2632
2633        let transaction = platform.drive.grove.start_transaction();
2634
2635        let processing_result = platform
2636            .platform
2637            .process_raw_state_transitions(
2638                &[serialized_state_transition],
2639                platform_state,
2640                &BlockInfo::default(),
2641                &transaction,
2642                platform_version,
2643                false,
2644                None,
2645            )
2646            .expect("expected to process state transition");
2647
2648        platform
2649            .drive
2650            .grove
2651            .commit_transaction(transaction)
2652            .unwrap()
2653            .expect("expected to commit transaction");
2654
2655        processing_result
2656    }
2657
2658    mod keyword_search_contract {
2659        use dpp::consensus::basic::BasicError;
2660        use dpp::consensus::ConsensusError;
2661        use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult::PaidConsensusError;
2662        use super::*;
2663        //
2664        // ──────────────────────────────────────────────────────────────────────────
2665        //  Keyword‑Search contract – creation is forbidden
2666        // ──────────────────────────────────────────────────────────────────────────
2667        //
2668
2669        /// Return `(document, entropy)` so we can reuse the exact entropy when
2670        /// we build the transition.  **No rng.clone()** (that caused the ID mismatch).
2671        fn build_random_doc_of_type(
2672            rng: &mut StdRng,
2673            doc_type_name: &str,
2674            identity_id: Identifier,
2675            contract: &DataContract,
2676            platform_version: &PlatformVersion,
2677        ) -> (Document, Bytes32) {
2678            let doc_type = contract
2679                .document_type_for_name(doc_type_name)
2680                .expect("doc type exists");
2681
2682            let entropy = Bytes32::random_with_rng(rng);
2683
2684            let doc = doc_type
2685                .random_document_with_identifier_and_entropy(
2686                    rng,
2687                    identity_id,
2688                    entropy,
2689                    DocumentFieldFillType::FillIfNotRequired,
2690                    DocumentFieldFillSize::AnyDocumentFillSize,
2691                    platform_version,
2692                )
2693                .expect("random doc");
2694
2695            (doc, entropy)
2696        }
2697
2698        #[tokio::test]
2699        async fn should_err_when_creating_contract_keywords_document() {
2700            let platform_version = PlatformVersion::latest();
2701
2702            let mut platform = TestPlatformBuilder::new()
2703                .build_with_mock_rpc()
2704                .set_genesis_state();
2705
2706            let (identity, signer, key) = setup_identity(&mut platform, 42, dash_to_credits!(1.0));
2707
2708            let contract = load_system_data_contract(
2709                SystemDataContract::KeywordSearch,
2710                PlatformVersion::latest(),
2711            )
2712            .expect("expected to load search contract");
2713
2714            let mut rng = StdRng::seed_from_u64(1);
2715            let (doc, entropy) = build_random_doc_of_type(
2716                &mut rng,
2717                "contractKeywords",
2718                identity.id(),
2719                &contract,
2720                platform_version,
2721            );
2722
2723            let transition = BatchTransition::new_document_creation_transition_from_document(
2724                doc,
2725                contract.document_type_for_name("contractKeywords").unwrap(),
2726                entropy.0, // same entropy → no ID mismatch
2727                &key,
2728                1,
2729                0,
2730                None,
2731                &signer,
2732                platform_version,
2733                None,
2734            )
2735            .await
2736            .expect("batch transition");
2737
2738            let serialized = transition.serialize_to_bytes().unwrap();
2739
2740            let platform_state = platform.state.load();
2741            let processing_result = platform
2742                .platform
2743                .process_raw_state_transitions(
2744                    &[serialized],
2745                    &platform_state,
2746                    &BlockInfo::default(),
2747                    &platform.drive.grove.start_transaction(),
2748                    platform_version,
2749                    false,
2750                    None,
2751                )
2752                .expect("processing failed");
2753
2754            let execution_result = processing_result.into_execution_results().remove(0);
2755            assert_matches!(
2756                execution_result,
2757                StateTransitionExecutionResult::PaidConsensusError{ error, .. }
2758                    if error.to_string().contains("not allowed because of the document type's creation restriction mode")
2759            );
2760        }
2761
2762        #[tokio::test]
2763        async fn should_err_when_creating_short_description_document() {
2764            let platform_version = PlatformVersion::latest();
2765
2766            let mut platform = TestPlatformBuilder::new()
2767                .build_with_mock_rpc()
2768                .set_genesis_state();
2769
2770            let (identity, signer, key) = setup_identity(&mut platform, 43, dash_to_credits!(1.0));
2771
2772            let contract = load_system_data_contract(
2773                SystemDataContract::KeywordSearch,
2774                PlatformVersion::latest(),
2775            )
2776            .expect("expected to load search contract");
2777
2778            let mut rng = StdRng::seed_from_u64(2);
2779            let (doc, entropy) = build_random_doc_of_type(
2780                &mut rng,
2781                "shortDescription",
2782                identity.id(),
2783                &contract,
2784                platform_version,
2785            );
2786
2787            let transition = BatchTransition::new_document_creation_transition_from_document(
2788                doc,
2789                contract.document_type_for_name("shortDescription").unwrap(),
2790                entropy.0,
2791                &key,
2792                1,
2793                0,
2794                None,
2795                &signer,
2796                platform_version,
2797                None,
2798            )
2799            .await
2800            .expect("batch transition");
2801
2802            let serialized = transition.serialize_to_bytes().unwrap();
2803
2804            let platform_state = platform.state.load();
2805            let processing_result = platform
2806                .platform
2807                .process_raw_state_transitions(
2808                    &[serialized],
2809                    &platform_state,
2810                    &BlockInfo::default(),
2811                    &platform.drive.grove.start_transaction(),
2812                    platform_version,
2813                    false,
2814                    None,
2815                )
2816                .expect("processing failed");
2817
2818            let execution_result = processing_result.into_execution_results().remove(0);
2819            assert_matches!(
2820                execution_result,
2821                StateTransitionExecutionResult::PaidConsensusError{ error, .. }
2822                    if error.to_string().contains("not allowed because of the document type's creation restriction mode")
2823            );
2824        }
2825
2826        #[tokio::test]
2827        async fn should_err_when_creating_full_description_document() {
2828            let platform_version = PlatformVersion::latest();
2829
2830            let mut platform = TestPlatformBuilder::new()
2831                .build_with_mock_rpc()
2832                .set_genesis_state();
2833
2834            let (identity, signer, key) = setup_identity(&mut platform, 44, dash_to_credits!(1.0));
2835
2836            let contract = load_system_data_contract(
2837                SystemDataContract::KeywordSearch,
2838                PlatformVersion::latest(),
2839            )
2840            .expect("expected to load search contract");
2841
2842            let mut rng = StdRng::seed_from_u64(3);
2843            let (doc, entropy) = build_random_doc_of_type(
2844                &mut rng,
2845                "fullDescription",
2846                identity.id(),
2847                &contract,
2848                platform_version,
2849            );
2850
2851            let transition = BatchTransition::new_document_creation_transition_from_document(
2852                doc,
2853                contract.document_type_for_name("fullDescription").unwrap(),
2854                entropy.0,
2855                &key,
2856                1,
2857                0,
2858                None,
2859                &signer,
2860                platform_version,
2861                None,
2862            )
2863            .await
2864            .expect("batch transition");
2865
2866            let serialized = transition.serialize_to_bytes().unwrap();
2867
2868            let platform_state = platform.state.load();
2869            let processing_result = platform
2870                .platform
2871                .process_raw_state_transitions(
2872                    &[serialized],
2873                    &platform_state,
2874                    &BlockInfo::default(),
2875                    &platform.drive.grove.start_transaction(),
2876                    platform_version,
2877                    false,
2878                    None,
2879                )
2880                .expect("processing failed");
2881
2882            let execution_result = processing_result.into_execution_results().remove(0);
2883            assert_matches!(
2884                execution_result,
2885                StateTransitionExecutionResult::PaidConsensusError{ error, .. }
2886                    if error.to_string().contains("not allowed because of the document type's creation restriction mode")
2887            );
2888        }
2889
2890        //
2891        // ──────────────────────────────────────────────────────────────────────────
2892        //  Keyword‑Search contract – owner can update / delete
2893        // ──────────────────────────────────────────────────────────────────────────
2894        //
2895
2896        async fn create_contract_with_keywords_and_description(
2897            platform: &mut TempPlatform<MockCoreRPCLike>,
2898        ) -> (Identity, SimpleSigner, IdentityPublicKey) {
2899            let platform_version = PlatformVersion::latest();
2900
2901            // Owner identity
2902            let (owner_identity, signer, key) =
2903                setup_identity(platform, 777, dash_to_credits!(1.0));
2904
2905            // Load the keyword‑test fixture
2906            let mut contract = json_document_to_contract_with_ids(
2907                "tests/supporting_files/contract/keyword_test/keyword_base_contract.json",
2908                Some(owner_identity.id()),
2909                None,
2910                false,
2911                platform_version,
2912            )
2913            .expect("load contract");
2914
2915            // Inject description + keywords
2916            contract.set_description(Some("A short description".to_string()));
2917            contract.set_keywords(vec!["graph".into(), "indexing".into()]);
2918
2919            // Create transition inside GroveDB tx
2920            let create_transition = DataContractCreateTransition::new_from_data_contract(
2921                contract,
2922                1,
2923                &owner_identity.clone().into_partial_identity_info(),
2924                key.id(),
2925                &signer,
2926                platform_version,
2927                None,
2928            )
2929            .await
2930            .expect("build transition");
2931
2932            let serialized = create_transition.serialize_to_bytes().unwrap();
2933            let platform_state = platform.state.load();
2934            let tx = platform.drive.grove.start_transaction();
2935
2936            let processing_result = platform
2937                .platform
2938                .process_raw_state_transitions(
2939                    &[serialized],
2940                    &platform_state,
2941                    &BlockInfo::default(),
2942                    &tx,
2943                    platform_version,
2944                    false,
2945                    None,
2946                )
2947                .expect("process");
2948
2949            assert_matches!(
2950                processing_result.execution_results().as_slice(),
2951                [StateTransitionExecutionResult::SuccessfulExecution { .. }]
2952            );
2953
2954            platform
2955                .drive
2956                .grove
2957                .commit_transaction(tx)
2958                .unwrap()
2959                .expect("commit");
2960
2961            (owner_identity, signer, key)
2962        }
2963
2964        #[tokio::test]
2965        async fn owner_can_update_short_description_document() {
2966            let platform_version = PlatformVersion::latest();
2967            let mut platform = TestPlatformBuilder::new()
2968                .build_with_mock_rpc()
2969                .set_genesis_state();
2970
2971            let (_owner, signer, key) =
2972                create_contract_with_keywords_and_description(&mut platform).await;
2973
2974            // 🔎 fetch shortDescription doc through query
2975            let search_contract =
2976                load_system_data_contract(SystemDataContract::KeywordSearch, platform_version)
2977                    .expect("load search contract");
2978
2979            let doc_type = search_contract
2980                .document_type_for_name("shortDescription")
2981                .unwrap();
2982
2983            let query = DriveDocumentQuery::all_items_query(&search_contract, doc_type, None);
2984            let existing_docs = platform
2985                .drive
2986                .query_documents(
2987                    query,
2988                    None,
2989                    false,
2990                    None,
2991                    Some(platform_version.protocol_version),
2992                )
2993                .expect("query failed");
2994
2995            let mut doc = existing_docs.documents().first().expect("doc").clone();
2996            doc.set_revision(Some(doc.revision().unwrap_or_default() + 1));
2997            doc.set("description", "updated description".into());
2998
2999            let transition = BatchTransition::new_document_replacement_transition_from_document(
3000                doc,
3001                doc_type,
3002                &key,
3003                2,
3004                0,
3005                None,
3006                &signer,
3007                platform_version,
3008                None,
3009            )
3010            .await
3011            .expect("replace");
3012
3013            let serialized = transition.serialize_to_bytes().unwrap();
3014            let platform_state = platform.state.load();
3015
3016            let processing_result = platform
3017                .platform
3018                .process_raw_state_transitions(
3019                    &[serialized],
3020                    &platform_state,
3021                    &BlockInfo::default(),
3022                    &platform.drive.grove.start_transaction(),
3023                    platform_version,
3024                    false,
3025                    None,
3026                )
3027                .expect("process");
3028
3029            assert_matches!(
3030                processing_result.into_execution_results().remove(0),
3031                SuccessfulExecution { .. }
3032            );
3033        }
3034
3035        #[tokio::test]
3036        async fn owner_can_not_delete_keyword_document() {
3037            let platform_version = PlatformVersion::latest();
3038            let mut platform = TestPlatformBuilder::new()
3039                .build_with_mock_rpc()
3040                .set_genesis_state();
3041
3042            let (_owner, signer, key) =
3043                create_contract_with_keywords_and_description(&mut platform).await;
3044
3045            let search_contract =
3046                load_system_data_contract(SystemDataContract::KeywordSearch, platform_version)
3047                    .expect("load search contract");
3048            let doc_type = search_contract
3049                .document_type_for_name("contractKeywords")
3050                .unwrap();
3051
3052            let query = DriveDocumentQuery::all_items_query(&search_contract, doc_type, None);
3053            let existing_docs = platform
3054                .drive
3055                .query_documents(
3056                    query,
3057                    None,
3058                    false,
3059                    None,
3060                    Some(platform_version.protocol_version),
3061                )
3062                .expect("query failed");
3063
3064            let doc = existing_docs.documents().first().unwrap().clone();
3065
3066            let transition = BatchTransition::new_document_deletion_transition_from_document(
3067                doc,
3068                doc_type,
3069                &key,
3070                2,
3071                0,
3072                None,
3073                &signer,
3074                platform_version,
3075                None,
3076            )
3077            .await
3078            .expect("delete");
3079
3080            let serialized = transition.serialize_to_bytes().unwrap();
3081            let platform_state = platform.state.load();
3082
3083            let processing_result = platform
3084                .platform
3085                .process_raw_state_transitions(
3086                    &[serialized],
3087                    &platform_state,
3088                    &BlockInfo::default(),
3089                    &platform.drive.grove.start_transaction(),
3090                    platform_version,
3091                    false,
3092                    None,
3093                )
3094                .expect("process");
3095            assert_matches!(
3096                processing_result.execution_results().as_slice(),
3097                [PaidConsensusError {
3098                    error: ConsensusError::BasicError(
3099                        BasicError::InvalidDocumentTransitionActionError { .. }
3100                    ),
3101                    ..
3102                }]
3103            );
3104        }
3105    }
3106}