1pub mod batch;
3
4pub mod identity_create;
6
7pub mod identity_credit_transfer;
9
10pub mod identity_credit_withdrawal;
12
13pub mod identity_top_up;
15
16pub mod identity_update;
18
19pub mod identity_key_limits_update;
21
22pub mod data_contract_common;
24
25pub mod data_contract_create;
27
28pub mod data_contract_update;
30
31pub mod contract_user_moderation;
33
34pub mod contract_fee_claim;
36
37pub mod masternode_vote;
39
40pub mod identity_create_from_addresses;
42
43pub mod address_funding_from_asset_lock;
45
46pub mod identity_credit_transfer_to_addresses;
48
49pub mod address_credit_withdrawal;
51
52pub mod address_funds_transfer;
54mod identity_top_up_from_addresses;
55
56pub mod identity_create_from_shielded_pool;
58pub mod identity_top_up_from_shielded_pool;
60pub mod shield;
62pub mod shield_from_asset_lock;
64pub mod shield_from_identity;
66pub mod shielded_common;
68pub mod shielded_transfer;
70pub mod shielded_withdrawal;
72pub mod token_pool_paid_common;
74pub mod token_purchase_from_shielded_pool;
76pub mod token_shielded_transfer_with_shielded_fee;
78pub mod token_unshield_with_shielded_fee;
80pub mod unshield;
82
83use dpp::document::Document;
84use dpp::version::PlatformVersion;
85
86pub(crate) fn stamp_withdrawal_document(
87 document: &mut Document,
88 platform_version: &PlatformVersion,
89) {
90 match document {
91 Document::V0(document) => {
92 document.contract_version =
93 Some(platform_version.system_data_contracts.withdrawals as u32);
94 }
95 }
96}
97
98#[derive(Clone, Copy, Debug, Eq, PartialEq)]
100pub enum ValidationMode {
101 CheckTx,
103 RecheckTx,
105 Validator,
107 NoValidation,
109}
110
111impl ValidationMode {
112 pub fn can_alter_cache(&self) -> bool {
114 match self {
115 ValidationMode::CheckTx => false,
116 ValidationMode::RecheckTx => false,
117 ValidationMode::Validator => true,
118 ValidationMode::NoValidation => false,
119 }
120 }
121}
122
123#[cfg(test)]
124pub(crate) mod test_helpers;
125
126#[cfg(test)]
127pub(in crate::execution) mod tests {
128 use crate::rpc::core::MockCoreRPCLike;
129 use crate::test::helpers::setup::{TempPlatform, TestPlatformBuilder};
130 use dpp::block::block_info::BlockInfo;
131 use dpp::data_contracts::SystemDataContract;
132 use dpp::fee::Credits;
133 use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
134 use dpp::identity::{Identity, IdentityPublicKey, IdentityV0, KeyID, KeyType, Purpose, SecurityLevel, TimestampMillis};
135 use dpp::prelude::{BlockHeight, Identifier, IdentityNonce};
136 use dpp::state_transition::data_contract_create_transition::methods::DataContractCreateTransitionMethodsV0;
137 use dpp::state_transition::data_contract_create_transition::DataContractCreateTransition;
138 use dpp::system_data_contracts::load_system_data_contract;
139 use dpp::tests::json_document::json_document_to_contract_with_ids;
140 use drive::drive::document::query::QueryDocumentsOutcomeV0Methods;
141 use drive::query::DriveDocumentQuery;
142 use platform_version::version::PlatformVersion;
143 use rand::prelude::StdRng;
144 use rand::{Rng, SeedableRng};
145 use simple_signer::signer::SimpleSigner;
146 use std::borrow::Cow;
147 use std::collections::BTreeMap;
148 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
149 use std::ops::Deref;
150 use std::sync::Arc;
151 use arc_swap::Guard;
152 use assert_matches::assert_matches;
153 use dpp::dashcore_rpc::dashcore_rpc_json::{DMNState, MasternodeListItem, MasternodeType};
154 use dapi_grpc::platform::v0::{get_contested_resource_vote_state_request, get_contested_resource_vote_state_response, GetContestedResourceVoteStateRequest, GetContestedResourceVoteStateResponse};
155 use dapi_grpc::platform::v0::get_contested_resource_vote_state_request::get_contested_resource_vote_state_request_v0::ResultType;
156 use dapi_grpc::platform::v0::get_contested_resource_vote_state_request::{get_contested_resource_vote_state_request_v0, GetContestedResourceVoteStateRequestV0};
157 use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::{get_contested_resource_vote_state_response_v0, GetContestedResourceVoteStateResponseV0};
158 use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::get_contested_resource_vote_state_response_v0::FinishedVoteInfo;
159 use dpp::balances::credits::TokenAmount;
160 use dpp::dash_to_credits;
161 use dpp::dashcore::{ProTxHash, Txid};
162 use dpp::dashcore::hashes::Hash;
163 use dpp::data_contract::accessors::v0::{DataContractV0Getters, DataContractV0Setters};
164 use dpp::data_contract::accessors::v1::{DataContractV1Getters, DataContractV1Setters};
165 use dpp::data_contract::{DataContract, GroupContractPosition, TokenContractPosition};
166 use dpp::data_contract::document_type::accessors::{DocumentTypeV0Getters, DocumentTypeV1Setters};
167 use dpp::data_contract::document_type::random_document::{CreateRandomDocument, DocumentFieldFillSize, DocumentFieldFillType};
168 use dpp::document::{Document, DocumentV0Getters, DocumentV0Setters};
169 use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
170 use dpp::fee::fee_result::FeeResult;
171 use dpp::identifier::MasternodeIdentifiers;
172 use dpp::identity::accessors::IdentityGettersV0;
173 use dpp::identity::contract_bounds::ContractBounds;
174 use dpp::identity::hash::IdentityPublicKeyHashMethodsV0;
175 use dpp::platform_value::{Bytes32, Value};
176 use dpp::serialization::PlatformSerializable;
177 use dpp::state_transition::batch_transition::BatchTransition;
178 use dpp::state_transition::batch_transition::accessors::DocumentsBatchTransitionAccessorsV0;
179 use dpp::state_transition::batch_transition::batched_transition::BatchedTransitionMutRef;
180 use dpp::state_transition::batch_transition::batched_transition::document_transition::DocumentTransition;
181 use dpp::state_transition::batch_transition::document_create_transition::v0::v0_methods::DocumentCreateTransitionV0Methods;
182 use dpp::state_transition::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0;
183 use dpp::state_transition::batch_transition::methods::StateTransitionCreationOptions;
184 use dpp::state_transition::masternode_vote_transition::MasternodeVoteTransition;
185 use dpp::state_transition::masternode_vote_transition::methods::MasternodeVoteTransitionMethodsV0;
186 use dpp::state_transition::StateTransition;
187 use dpp::block::epoch::EpochIndex;
188 use dpp::tokens::calculate_token_id;
189 use dpp::util::hash::hash_double;
190 use dpp::util::strings::convert_to_homograph_safe_chars;
191 use dpp::voting::contender_structs::{Contender, ContenderV0};
192 use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice;
193 use dpp::voting::vote_info_storage::contested_document_vote_poll_winner_info::ContestedDocumentVotePollWinnerInfo;
194 use dpp::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll;
195 use dpp::voting::vote_polls::VotePoll;
196 use dpp::voting::votes::resource_vote::ResourceVote;
197 use dpp::voting::votes::resource_vote::v0::ResourceVoteV0;
198 use dpp::voting::votes::Vote;
199 use drive::util::object_size_info::DataContractResolvedInfo;
200 use drive::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed;
201 use drive::query::vote_poll_vote_state_query::ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally;
202 use drive::query::vote_poll_vote_state_query::{ContestedDocumentVotePollDriveQueryResultType, ResolvedContestedDocumentVotePollDriveQuery};
203 use drive::util::test_helpers::setup_contract;
204 use drive::drive::votes::paths::VotePollPaths;
205 use drive::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::resolve::ContestedDocumentResourceVotePollResolver;
206 use drive::fees::op::LowLevelDriveOperation;
207 use drive::grovedb::Element;
208 use crate::execution::types::block_execution_context::BlockExecutionContext;
209 use crate::execution::types::block_execution_context::v0::BlockExecutionContextV0;
210 use crate::expect_match;
211 use crate::execution::check_tx::CheckTxLevel;
212 use dpp::consensus::ConsensusError;
213 use crate::platform_types::platform::PlatformRef;
214 use crate::platform_types::platform_state::PlatformState;
215 use crate::platform_types::platform_state::PlatformStateV0Methods;
216 use crate::platform_types::state_transitions_processing_result::{StateTransitionExecutionResult, StateTransitionsProcessingResult};
217 use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult::{SuccessfulExecution, UnpaidConsensusError};
218 use crate::execution::types::block_state_info::BlockStateInfo;
219 use crate::execution::types::block_state_info::v0::BlockStateInfoV0;
220 use crate::platform_types::epoch_info::EpochInfo;
221 use crate::platform_types::epoch_info::v0::EpochInfoV0;
222 use crate::execution::types::block_fees::v0::BlockFeesV0;
223 use crate::execution::types::processed_block_fees_outcome::v0::ProcessedBlockFeesOutcome;
224 use dpp::data_contract::associated_token::token_configuration::TokenConfiguration;
225 use dpp::data_contract::group::Group;
226 use dpp::tokens::gas_fees_paid_by::GasFeesPaidBy;
227 use dpp::tokens::token_amount_on_contract_token::{DocumentActionTokenCost, DocumentActionTokenEffect};
228 use dpp::data_contract::document_type::accessors::DocumentTypeV0MutGetters;
229 use dpp::serialization::PlatformDeserializableWithPotentialValidationFromVersionedStructureUntrusted;
230
231 pub(in crate::execution) fn setup_identity_with_system_credits(
233 platform: &mut TempPlatform<MockCoreRPCLike>,
234 seed: u64,
235 credits: Credits,
236 ) -> (Identity, SimpleSigner, IdentityPublicKey) {
237 let platform_version = PlatformVersion::latest();
238 platform
239 .drive
240 .add_to_system_credits(credits, None, platform_version)
241 .expect("expected to add to system credits");
242 setup_identity(platform, seed, credits)
243 }
244
245 pub(in crate::execution) fn setup_identity(
246 platform: &mut TempPlatform<MockCoreRPCLike>,
247 seed: u64,
248 credits: Credits,
249 ) -> (Identity, SimpleSigner, IdentityPublicKey) {
250 let platform_version = PlatformVersion::latest();
251 let mut signer = SimpleSigner::default();
252
253 let mut rng = StdRng::seed_from_u64(seed);
254
255 let (master_key, master_private_key) =
256 IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng(
257 0,
258 &mut rng,
259 platform_version,
260 )
261 .expect("expected to get key pair");
262
263 signer.add_identity_public_key(master_key.clone(), master_private_key);
264
265 let (critical_public_key, private_key) =
266 IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng(
267 1,
268 &mut rng,
269 platform_version,
270 )
271 .expect("expected to get key pair");
272
273 signer.add_identity_public_key(critical_public_key.clone(), private_key);
274
275 let identity: Identity = IdentityV0 {
276 id: Identifier::random_with_rng(&mut rng),
277 public_keys: BTreeMap::from([
278 (0, master_key.clone()),
279 (1, critical_public_key.clone()),
280 ]),
281 balance: credits,
282 revision: 0,
283 }
284 .into();
285
286 platform
289 .drive
290 .add_new_identity(
291 identity.clone(),
292 false,
293 &BlockInfo::default(),
294 true,
295 None,
296 platform_version,
297 )
298 .expect("expected to add a new identity");
299
300 (identity, signer, critical_public_key)
301 }
302
303 pub(in crate::execution) fn setup_identity_without_adding_it(
304 seed: u64,
305 credits: Credits,
306 ) -> (Identity, SimpleSigner, IdentityPublicKey) {
307 let platform_version = PlatformVersion::latest();
308 let mut signer = SimpleSigner::default();
309
310 let mut rng = StdRng::seed_from_u64(seed);
311
312 let (master_key, master_private_key) =
313 IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng(
314 0,
315 &mut rng,
316 platform_version,
317 )
318 .expect("expected to get key pair");
319
320 signer.add_identity_public_key(master_key.clone(), master_private_key);
321
322 let (critical_public_key, private_key) =
323 IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng(
324 1,
325 &mut rng,
326 platform_version,
327 )
328 .expect("expected to get key pair");
329
330 signer.add_identity_public_key(critical_public_key.clone(), private_key);
331
332 let identity: Identity = IdentityV0 {
333 id: Identifier::random_with_rng(&mut rng),
334 public_keys: BTreeMap::from([
335 (0, master_key.clone()),
336 (1, critical_public_key.clone()),
337 ]),
338 balance: credits,
339 revision: 0,
340 }
341 .into();
342
343 (identity, signer, critical_public_key)
344 }
345
346 pub(in crate::execution) fn setup_identity_return_master_key(
347 platform: &mut TempPlatform<MockCoreRPCLike>,
348 seed: u64,
349 credits: Credits,
350 ) -> (Identity, SimpleSigner, IdentityPublicKey, IdentityPublicKey) {
351 let platform_version = PlatformVersion::latest();
352 let mut signer = SimpleSigner::default();
353
354 let mut rng = StdRng::seed_from_u64(seed);
355
356 let (master_key, master_private_key) =
357 IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng(
358 0,
359 &mut rng,
360 platform_version,
361 )
362 .expect("expected to get key pair");
363
364 signer.add_identity_public_key(master_key.clone(), master_private_key);
365
366 let (critical_public_key, private_key) =
367 IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng(
368 1,
369 &mut rng,
370 platform_version,
371 )
372 .expect("expected to get key pair");
373
374 signer.add_identity_public_key(critical_public_key.clone(), private_key);
375
376 let identity: Identity = IdentityV0 {
377 id: Identifier::random_with_rng(&mut rng),
378 public_keys: BTreeMap::from([
379 (0, master_key.clone()),
380 (1, critical_public_key.clone()),
381 ]),
382 balance: credits,
383 revision: 0,
384 }
385 .into();
386
387 platform
390 .drive
391 .add_new_identity(
392 identity.clone(),
393 false,
394 &BlockInfo::default(),
395 true,
396 None,
397 platform_version,
398 )
399 .expect("expected to add a new identity");
400
401 (identity, signer, critical_public_key, master_key)
402 }
403
404 #[allow(clippy::too_many_arguments)]
405 pub(crate) fn setup_add_key_to_identity(
406 platform: &mut TempPlatform<MockCoreRPCLike>,
407 identity: &mut Identity,
408 signer: &mut SimpleSigner,
409 seed: u64,
410 key_id: KeyID,
411 purpose: Purpose,
412 security_level: SecurityLevel,
413 key_type: KeyType,
414 contract_bounds: Option<ContractBounds>,
415 ) -> IdentityPublicKey {
416 let platform_version = PlatformVersion::latest();
417
418 let mut rng = StdRng::seed_from_u64(seed);
419
420 let (key, private_key) = IdentityPublicKey::random_key_with_known_attributes(
421 key_id,
422 &mut rng,
423 purpose,
424 security_level,
425 key_type,
426 contract_bounds,
427 platform_version,
428 )
429 .expect("expected to get key pair");
430
431 signer.add_identity_public_key(key.clone(), private_key);
432
433 identity.add_public_key(key.clone());
434
435 platform
436 .drive
437 .add_new_unique_keys_to_identity(
438 identity.id().to_buffer(),
439 vec![key.clone()],
440 &BlockInfo::default(),
441 true,
442 None,
443 platform_version,
444 )
445 .expect("expected to add a new key");
446
447 key
448 }
449
450 pub(in crate::execution) fn setup_identity_with_withdrawal_key_and_system_credits(
451 platform: &mut TempPlatform<MockCoreRPCLike>,
452 seed: u64,
453 withdrawal_key_type: KeyType,
454 credits: Credits,
455 ) -> (Identity, SimpleSigner, IdentityPublicKey, IdentityPublicKey) {
456 let platform_version = PlatformVersion::latest();
457 platform
458 .drive
459 .add_to_system_credits(credits, None, platform_version)
460 .expect("expected to add to system credits");
461 let mut signer = SimpleSigner::default();
462
463 let mut rng = StdRng::seed_from_u64(seed);
464
465 let (master_key, master_private_key) =
466 IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng(
467 0,
468 &mut rng,
469 platform_version,
470 )
471 .expect("expected to get key pair");
472
473 signer.add_identity_public_key(master_key.clone(), master_private_key);
474
475 let (critical_public_key, private_key) =
476 IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng(
477 1,
478 &mut rng,
479 platform_version,
480 )
481 .expect("expected to get key pair");
482
483 signer.add_identity_public_key(critical_public_key.clone(), private_key);
484
485 let (withdrawal_public_key, withdrawal_private_key) =
486 IdentityPublicKey::random_key_with_known_attributes(
487 2,
488 &mut rng,
489 Purpose::TRANSFER,
490 SecurityLevel::CRITICAL,
491 withdrawal_key_type,
492 None,
493 platform_version,
494 )
495 .expect("expected to get key pair");
496
497 signer.add_identity_public_key(withdrawal_public_key.clone(), withdrawal_private_key);
498
499 let identity: Identity = IdentityV0 {
500 id: Identifier::random_with_rng(&mut rng),
501 public_keys: BTreeMap::from([
502 (0, master_key.clone()),
503 (1, critical_public_key.clone()),
504 (2, withdrawal_public_key.clone()),
505 ]),
506 balance: credits,
507 revision: 0,
508 }
509 .into();
510
511 platform
514 .drive
515 .add_new_identity(
516 identity.clone(),
517 false,
518 &BlockInfo::default(),
519 true,
520 None,
521 platform_version,
522 )
523 .expect("expected to add a new identity");
524
525 (identity, signer, critical_public_key, withdrawal_public_key)
526 }
527
528 pub(in crate::execution) fn add_tokens_to_identity(
529 platform: &TempPlatform<MockCoreRPCLike>,
530 token_id: Identifier,
531 identity_id: Identifier,
532 balance_to_add: Credits,
533 ) {
534 let platform_version = PlatformVersion::latest();
535 platform
536 .drive
537 .add_to_identity_token_balance(
538 token_id.to_buffer(),
539 identity_id.to_buffer(),
540 balance_to_add,
541 &BlockInfo::default(),
542 true,
543 None,
544 platform_version,
545 None,
546 )
547 .expect("expected to add token balance to identity");
548 platform
549 .drive
550 .add_to_token_total_supply(
551 token_id.to_buffer(),
552 balance_to_add,
553 true,
554 false,
555 true,
556 &BlockInfo::default(),
557 None,
558 platform_version,
559 )
560 .expect("expected to add to total supply");
561 }
562
563 pub(in crate::execution) fn process_state_transitions(
564 platform: &TempPlatform<MockCoreRPCLike>,
565 state_transitions: &[StateTransition],
566 block_info: BlockInfo,
567 platform_state: &PlatformState,
568 ) -> (Vec<FeeResult>, ProcessedBlockFeesOutcome) {
569 let (execution_results, processed_block_fees) = process_state_transitions_with_results(
570 platform,
571 state_transitions,
572 block_info,
573 platform_state,
574 );
575
576 let fee_results = execution_results.iter().map(|result| {
577 let fee_result = expect_match!(result, StateTransitionExecutionResult::SuccessfulExecution{ fee_result, .. } => fee_result);
578 fee_result.clone()
579 }).collect();
580
581 (fee_results, processed_block_fees)
582 }
583
584 pub(in crate::execution) fn process_state_transitions_with_results(
589 platform: &TempPlatform<MockCoreRPCLike>,
590 state_transitions: &[StateTransition],
591 block_info: BlockInfo,
592 platform_state: &PlatformState,
593 ) -> (
594 Vec<StateTransitionExecutionResult>,
595 ProcessedBlockFeesOutcome,
596 ) {
597 let platform_version = platform_state
600 .current_platform_version()
601 .expect("expected the state's platform version");
602 let raw_state_transitions = state_transitions
603 .iter()
604 .map(|a| a.serialize_to_bytes().expect("expected to serialize"))
605 .collect::<Vec<_>>();
606
607 let transaction = platform.drive.grove.start_transaction();
608
609 let processing_result = platform
610 .platform
611 .process_raw_state_transitions(
612 &raw_state_transitions,
613 platform_state,
614 &block_info,
615 &transaction,
616 platform_version,
617 false,
618 None,
619 )
620 .expect("expected to process state transition");
621
622 let block_fees_v0: BlockFeesV0 = processing_result.aggregated_fees().clone().into();
624
625 let block_execution_context = BlockExecutionContext::V0(BlockExecutionContextV0 {
626 block_state_info: BlockStateInfo::V0(BlockStateInfoV0 {
627 height: block_info.height,
628 round: 0,
629 block_time_ms: block_info.time_ms,
630 previous_block_time_ms: platform_state.last_committed_block_time_ms(),
631 proposer_pro_tx_hash: Default::default(),
632 core_chain_locked_height: 0,
633 block_hash: None,
634 app_hash: None,
635 }),
636 epoch_info: EpochInfo::V0(EpochInfoV0::default()),
637 unsigned_withdrawal_transactions: Default::default(),
638 block_address_balance_changes: Default::default(),
639 block_platform_state: platform_state.clone(),
640 proposer_results: None,
641 });
642
643 let processed_block_fees = platform
645 .process_block_fees_and_validate_sum_trees(
646 &block_execution_context,
647 block_fees_v0.into(),
648 &transaction,
649 platform_version,
650 )
651 .expect("expected to process block fees");
652
653 platform
654 .drive
655 .grove
656 .commit_transaction(transaction)
657 .unwrap()
658 .expect("expected to commit");
659
660 (
661 processing_result.into_execution_results(),
662 processed_block_fees,
663 )
664 }
665
666 pub(in crate::execution) fn fetch_expected_identity_balance(
667 platform: &TempPlatform<MockCoreRPCLike>,
668 identity_id: Identifier,
669 platform_version: &PlatformVersion,
670 expected_balance: Credits,
671 ) {
672 assert_eq!(
673 expected_balance,
674 platform
675 .drive
676 .fetch_identity_balance(identity_id.to_buffer(), None, platform_version)
677 .expect("expected to be able to fetch balance")
678 .expect("expected a balance")
679 );
680 }
681
682 pub(in crate::execution) fn setup_masternode_owner_identity(
683 platform: &mut TempPlatform<MockCoreRPCLike>,
684 seed: u64,
685 credits: Credits,
686 platform_version: &PlatformVersion,
687 ) -> (Identity, SimpleSigner, IdentityPublicKey, IdentityPublicKey) {
688 let mut signer = SimpleSigner::default();
689
690 platform
691 .drive
692 .add_to_system_credits(credits, None, platform_version)
693 .expect("expected to add to system credits");
694
695 let mut rng = StdRng::seed_from_u64(seed);
696
697 let (transfer_key, transfer_private_key) =
698 IdentityPublicKey::random_masternode_transfer_key_with_rng(
699 0,
700 &mut rng,
701 platform_version,
702 )
703 .expect("expected to get key pair");
704
705 let (owner_key, owner_private_key) =
706 IdentityPublicKey::random_masternode_owner_key_with_rng(1, &mut rng, platform_version)
707 .expect("expected to get key pair");
708
709 let owner_address = owner_key
710 .public_key_hash()
711 .expect("expected a public key hash");
712
713 let payout_address = transfer_key
714 .public_key_hash()
715 .expect("expected a public key hash");
716
717 signer.add_identity_public_key(transfer_key.clone(), transfer_private_key);
718 signer.add_identity_public_key(owner_key.clone(), owner_private_key);
719
720 let pro_tx_hash_bytes: [u8; 32] = rng.gen();
721
722 let identity: Identity = IdentityV0 {
723 id: pro_tx_hash_bytes.into(),
724 public_keys: BTreeMap::from([(0, transfer_key.clone()), (1, owner_key.clone())]),
725 balance: credits,
726 revision: 0,
727 }
728 .into();
729
730 platform
733 .drive
734 .add_new_identity(
735 identity.clone(),
736 true,
737 &BlockInfo::default(),
738 true,
739 None,
740 platform_version,
741 )
742 .expect("expected to add a new identity");
743
744 let mut platform_state = platform.state.load().clone().deref().clone();
745
746 let pro_tx_hash = ProTxHash::from_byte_array(pro_tx_hash_bytes);
747
748 let random_ip = Ipv4Addr::new(
749 rng.gen_range(0..255),
750 rng.gen_range(0..255),
751 rng.gen_range(0..255),
752 rng.gen_range(0..255),
753 );
754
755 platform_state.full_masternode_list_mut().insert(
756 pro_tx_hash,
757 MasternodeListItem {
758 node_type: MasternodeType::Regular,
759 pro_tx_hash,
760 collateral_hash: Txid::from_byte_array(rng.gen()),
761 collateral_index: 0,
762 collateral_address: Some(rng.gen()),
763 operator_reward: 0.0,
764 state: DMNState {
765 service: SocketAddr::new(IpAddr::V4(random_ip), 19999),
766 registered_height: 0,
767 pose_revived_height: None,
768 pose_ban_height: None,
769 revocation_reason: 0,
770 owner_address: Some(owner_address),
771 voting_address: rng.gen(),
772 payout_address: Some(payout_address),
773 payouts: None,
774 pub_key_operator: vec![],
775 operator_payout_address: None,
776 platform_node_id: None,
777 #[allow(deprecated)]
778 legacy_platform_p2p_port: None,
779 #[allow(deprecated)]
780 legacy_platform_http_port: None,
781 addresses: None,
782 },
783 },
784 );
785
786 platform.state.store(Arc::new(platform_state));
787
788 (identity, signer, owner_key, transfer_key)
789 }
790
791 pub(in crate::execution) fn setup_masternode_voting_identity(
792 platform: &mut TempPlatform<MockCoreRPCLike>,
793 seed: u64,
794 platform_version: &PlatformVersion,
795 ) -> (Identifier, Identity, SimpleSigner, IdentityPublicKey) {
796 let mut signer = SimpleSigner::default();
797
798 let mut rng = StdRng::seed_from_u64(seed);
799
800 let (voting_key, voting_private_key) =
801 IdentityPublicKey::random_voting_key_with_rng(0, &mut rng, platform_version)
802 .expect("expected to get key pair");
803
804 signer.add_identity_public_key(voting_key.clone(), voting_private_key);
805
806 let pro_tx_hash_bytes: [u8; 32] = rng.gen();
807
808 let voting_address = voting_key
809 .public_key_hash()
810 .expect("expected a public key hash");
811
812 let voter_identifier =
813 Identifier::create_voter_identifier(&pro_tx_hash_bytes, &voting_address);
814
815 let identity: Identity = IdentityV0 {
816 id: voter_identifier,
817 public_keys: BTreeMap::from([(0, voting_key.clone())]),
818 balance: 0,
819 revision: 0,
820 }
821 .into();
822
823 platform
826 .drive
827 .add_new_identity(
828 identity.clone(),
829 true,
830 &BlockInfo::default(),
831 true,
832 None,
833 platform_version,
834 )
835 .expect("expected to add a new identity");
836
837 let mut platform_state = platform.state.load().clone().deref().clone();
838
839 let pro_tx_hash = ProTxHash::from_byte_array(pro_tx_hash_bytes);
840
841 let random_ip = Ipv4Addr::new(
842 rng.gen_range(0..255),
843 rng.gen_range(0..255),
844 rng.gen_range(0..255),
845 rng.gen_range(0..255),
846 );
847
848 platform_state.full_masternode_list_mut().insert(
849 pro_tx_hash,
850 MasternodeListItem {
851 node_type: MasternodeType::Regular,
852 pro_tx_hash,
853 collateral_hash: Txid::from_byte_array(rng.gen()),
854 collateral_index: 0,
855 collateral_address: Some(rng.gen()),
856 operator_reward: 0.0,
857 state: DMNState {
858 service: SocketAddr::new(IpAddr::V4(random_ip), 19999),
859 registered_height: 0,
860 pose_revived_height: None,
861 pose_ban_height: None,
862 revocation_reason: 0,
863 owner_address: Some(rng.gen()),
864 voting_address,
865 payout_address: Some(rng.gen()),
866 payouts: None,
867 pub_key_operator: vec![],
868 operator_payout_address: None,
869 platform_node_id: None,
870 #[allow(deprecated)]
871 legacy_platform_p2p_port: None,
872 #[allow(deprecated)]
873 legacy_platform_http_port: None,
874 addresses: None,
875 },
876 },
877 );
878
879 platform.state.store(Arc::new(platform_state));
880
881 (pro_tx_hash_bytes.into(), identity, signer, voting_key)
882 }
883
884 pub(in crate::execution) fn take_down_masternode_identities(
885 platform: &mut TempPlatform<MockCoreRPCLike>,
886 masternode_identities: &Vec<Identifier>,
887 ) {
888 let mut platform_state = platform.state.load().clone().deref().clone();
889
890 let list = platform_state.full_masternode_list_mut();
891
892 for masternode_identifiers in masternode_identities {
893 let pro_tx_hash = ProTxHash::from_byte_array(masternode_identifiers.to_buffer());
894
895 list.remove(&pro_tx_hash);
896 }
897
898 platform.state.store(Arc::new(platform_state));
899 }
900
901 #[allow(dead_code)]
902 pub(in crate::execution) enum IdentityTestInfo<'a> {
903 Given {
904 identity: &'a Identity,
905 signer: &'a SimpleSigner,
906 public_key: &'a IdentityPublicKey,
907 identity_nonce: IdentityNonce,
908 },
909 UseSeed(u64),
910 UseRng(&'a mut StdRng),
911 }
912
913 pub(in crate::execution) async fn register_contract_from_bytes(
914 platform: &mut TempPlatform<MockCoreRPCLike>,
915 platform_state: &PlatformState,
916 contract_bytes: Vec<u8>,
917 identity_info: IdentityTestInfo<'_>,
918 platform_version: &PlatformVersion,
919 ) -> DataContract {
920 let mut data_contract =
922 DataContract::versioned_deserialize_untrusted(&contract_bytes, false, platform_version)
923 .expect("expected to deserialize data contract");
924
925 let (identity_cow, signer_cow, key_cow, nonce) = match identity_info {
927 IdentityTestInfo::Given {
928 identity,
929 signer,
930 public_key,
931 identity_nonce,
932 } => (
933 Cow::Borrowed(identity),
934 Cow::Borrowed(signer),
935 Cow::Borrowed(public_key),
936 identity_nonce,
937 ),
938 IdentityTestInfo::UseSeed(seed) => {
939 let (identity, signer, key) = setup_identity(platform, seed, dash_to_credits!(1));
940 (Cow::Owned(identity), Cow::Owned(signer), Cow::Owned(key), 1)
941 }
942 IdentityTestInfo::UseRng(rng) => {
943 let seed = rng.gen();
944 let (identity, signer, key) = setup_identity(platform, seed, dash_to_credits!(1));
945 (Cow::Owned(identity), Cow::Owned(signer), Cow::Owned(key), 1)
946 }
947 };
948
949 let contract_id = DataContract::generate_data_contract_id_v0(identity_cow.id(), nonce);
950
951 data_contract.set_id(contract_id);
952
953 let state_transition = DataContractCreateTransition::new_from_data_contract(
955 data_contract.clone(),
956 nonce,
957 &identity_cow.as_ref().clone().into_partial_identity_info(),
958 key_cow.id(),
959 signer_cow.as_ref(),
960 platform_version,
961 None,
962 )
963 .await
964 .expect("expected to create and sign data contract create transition");
965
966 let state_transition_bytes = state_transition
968 .serialize_to_bytes()
969 .expect("expected to serialize state transition");
970
971 let transaction = platform.drive.grove.start_transaction();
972
973 let processing_result = platform
974 .platform
975 .process_raw_state_transitions(
976 &[state_transition_bytes],
977 platform_state,
978 &BlockInfo::default(),
979 &transaction,
980 platform_version,
981 false,
982 None,
983 )
984 .expect("expected to process state transition");
985
986 platform
987 .drive
988 .grove
989 .commit_transaction(transaction)
990 .unwrap()
991 .expect("expected to commit transaction");
992
993 let execution_result = processing_result.into_execution_results().remove(0);
994 assert_matches!(execution_result, SuccessfulExecution { .. });
995
996 data_contract
997 }
998
999 pub(in crate::execution) async fn create_dpns_name_contest_give_key_info(
1000 platform: &mut TempPlatform<MockCoreRPCLike>,
1001 platform_state: &PlatformState,
1002 seed: u64,
1003 name: &str,
1004 platform_version: &PlatformVersion,
1005 ) -> (
1006 (
1007 Identity,
1008 SimpleSigner,
1009 IdentityPublicKey,
1010 (Document, Bytes32),
1011 (Document, Bytes32),
1012 ),
1013 (
1014 Identity,
1015 SimpleSigner,
1016 IdentityPublicKey,
1017 (Document, Bytes32),
1018 (Document, Bytes32),
1019 ),
1020 Arc<DataContract>,
1021 ) {
1022 let mut rng = StdRng::seed_from_u64(seed);
1023
1024 let identity_1_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1025
1026 let identity_2_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1027
1028 let (identity_1_info, identity_2_info) = if identity_1_info.0.id() < identity_2_info.0.id()
1030 {
1031 (identity_1_info, identity_2_info)
1032 } else {
1033 (identity_2_info, identity_1_info)
1034 };
1035
1036 let ((preorder_document_1, document_1), (preorder_document_2, document_2), dpns_contract) =
1037 create_dpns_name_contest_on_identities(
1038 platform,
1039 &identity_1_info,
1040 &identity_2_info,
1041 platform_state,
1042 rng,
1043 name,
1044 None,
1045 false,
1046 platform_version,
1047 )
1048 .await;
1049
1050 let (identity_1, signer_1, identity_key_1) = identity_1_info;
1051
1052 let (identity_2, signer_2, identity_key_2) = identity_2_info;
1053
1054 (
1055 (
1056 identity_1,
1057 signer_1,
1058 identity_key_1,
1059 preorder_document_1,
1060 document_1,
1061 ),
1062 (
1063 identity_2,
1064 signer_2,
1065 identity_key_2,
1066 preorder_document_2,
1067 document_2,
1068 ),
1069 dpns_contract,
1070 )
1071 }
1072
1073 pub(in crate::execution) async fn create_dpns_identity_name_contest(
1074 platform: &mut TempPlatform<MockCoreRPCLike>,
1075 platform_state: &PlatformState,
1076 seed: u64,
1077 name: &str,
1078 platform_version: &PlatformVersion,
1079 ) -> (Identity, Identity, Arc<DataContract>) {
1080 let mut rng = StdRng::seed_from_u64(seed);
1081
1082 let identity_1_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1083
1084 let identity_2_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1085
1086 let (identity_1_info, identity_2_info) = if identity_1_info.0.id() < identity_2_info.0.id()
1088 {
1089 (identity_1_info, identity_2_info)
1090 } else {
1091 (identity_2_info, identity_1_info)
1092 };
1093
1094 let (_, _, dpns_contract) = create_dpns_name_contest_on_identities(
1095 platform,
1096 &identity_1_info,
1097 &identity_2_info,
1098 platform_state,
1099 rng,
1100 name,
1101 None,
1102 false,
1103 platform_version,
1104 )
1105 .await;
1106 (identity_1_info.0, identity_2_info.0, dpns_contract)
1107 }
1108
1109 pub(in crate::execution) async fn create_dpns_identity_name_contest_skip_creating_identities(
1111 platform: &mut TempPlatform<MockCoreRPCLike>,
1112 platform_state: &PlatformState,
1113 seed: u64,
1114 name: &str,
1115 nonce_offset: Option<IdentityNonce>,
1116 platform_version: &PlatformVersion,
1117 ) -> (Identity, Identity, Arc<DataContract>) {
1118 let mut rng = StdRng::seed_from_u64(seed);
1119
1120 let identity_1_info = setup_identity_without_adding_it(rng.gen(), dash_to_credits!(0.5));
1121
1122 let identity_2_info = setup_identity_without_adding_it(rng.gen(), dash_to_credits!(0.5));
1123
1124 let (identity_1_info, identity_2_info) = if identity_1_info.0.id() < identity_2_info.0.id()
1126 {
1127 (identity_1_info, identity_2_info)
1128 } else {
1129 (identity_2_info, identity_1_info)
1130 };
1131
1132 let (_, _, dpns_contract) = create_dpns_name_contest_on_identities(
1133 platform,
1134 &identity_1_info,
1135 &identity_2_info,
1136 platform_state,
1137 rng,
1138 name,
1139 nonce_offset,
1140 true, platform_version,
1142 )
1143 .await;
1144 (identity_1_info.0, identity_2_info.0, dpns_contract)
1145 }
1146
1147 pub(in crate::execution) async fn create_dpns_contract_name_contest(
1148 platform: &mut TempPlatform<MockCoreRPCLike>,
1149 platform_state: &PlatformState,
1150 seed: u64,
1151 name: &str,
1152 platform_version: &PlatformVersion,
1153 ) -> (Identity, Identity, DataContract) {
1154 let mut rng = StdRng::seed_from_u64(seed);
1155
1156 let identity_1_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1157
1158 let identity_2_info = setup_identity(platform, rng.gen(), dash_to_credits!(0.5));
1159
1160 let (identity_1_info, identity_2_info) = if identity_1_info.0.id() < identity_2_info.0.id()
1162 {
1163 (identity_1_info, identity_2_info)
1164 } else {
1165 (identity_2_info, identity_1_info)
1166 };
1167
1168 let dashpay_contract = setup_contract(
1169 &platform.drive,
1170 "tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable.json",
1171 None,
1172 None,
1173 None::<fn(&mut DataContract)>,
1174 None,
1175 None,
1176 );
1177
1178 let card_game = setup_contract(
1179 &platform.drive,
1180 "tests/supporting_files/contract/crypto-card-game/crypto-card-game-direct-purchase.json",
1181 None,
1182 None,
1183 None::<fn(&mut DataContract)>,
1184 None,
1185 None,
1186 );
1187
1188 let (_, _, dpns_contract) = create_dpns_name_contest_on_identities_for_contract_records(
1189 platform,
1190 &identity_1_info,
1191 &identity_2_info,
1192 &dashpay_contract,
1193 &card_game,
1194 platform_state,
1195 rng,
1196 name,
1197 platform_version,
1198 )
1199 .await;
1200 (identity_1_info.0, identity_2_info.0, dpns_contract)
1201 }
1202
1203 #[allow(clippy::too_many_arguments)]
1204 async fn create_dpns_name_contest_on_identities(
1205 platform: &mut TempPlatform<MockCoreRPCLike>,
1206 identity_1: &(Identity, SimpleSigner, IdentityPublicKey),
1207 identity_2: &(Identity, SimpleSigner, IdentityPublicKey),
1208 platform_state: &PlatformState,
1209 mut rng: StdRng,
1210 name: &str,
1211 nonce_offset: Option<IdentityNonce>,
1212 skip_preorder: bool,
1213 platform_version: &PlatformVersion,
1214 ) -> (
1215 ((Document, Bytes32), (Document, Bytes32)),
1216 ((Document, Bytes32), (Document, Bytes32)),
1217 Arc<DataContract>,
1218 ) {
1219 let (identity_1, signer_1, key_1) = identity_1;
1220
1221 let (identity_2, signer_2, key_2) = identity_2;
1222
1223 let dpns = platform
1224 .drive
1225 .cache
1226 .system_data_contracts
1227 .load_dpns(platform_version)
1228 .expect("expected the dpns system contract");
1229 let dpns_contract = dpns.clone();
1230
1231 let preorder = dpns_contract
1232 .document_type_for_name("preorder")
1233 .expect("expected a profile document type");
1234
1235 assert!(!preorder.documents_mutable());
1236 assert!(preorder.documents_can_be_deleted());
1237 assert!(!preorder.documents_transferable().is_transferable());
1238
1239 let domain = dpns_contract
1240 .document_type_for_name("domain")
1241 .expect("expected a profile document type");
1242
1243 assert!(!domain.documents_mutable());
1244 assert!(domain.documents_can_be_deleted());
1246 assert!(domain.documents_transferable().is_transferable());
1247
1248 let entropy = Bytes32::random_with_rng(&mut rng);
1249
1250 let mut preorder_document_1 = preorder
1251 .random_document_with_identifier_and_entropy(
1252 &mut rng,
1253 identity_1.id(),
1254 entropy,
1255 DocumentFieldFillType::FillIfNotRequired,
1256 DocumentFieldFillSize::AnyDocumentFillSize,
1257 platform_version,
1258 )
1259 .expect("expected a random document");
1260 preorder_document_1
1261 .set_id_for_creation(
1262 preorder,
1263 &entropy.0,
1264 2 + nonce_offset.unwrap_or_default(),
1265 platform_version,
1266 )
1267 .expect("expected to set the document id");
1268
1269 let mut preorder_document_2 = preorder
1270 .random_document_with_identifier_and_entropy(
1271 &mut rng,
1272 identity_2.id(),
1273 entropy,
1274 DocumentFieldFillType::FillIfNotRequired,
1275 DocumentFieldFillSize::AnyDocumentFillSize,
1276 platform_version,
1277 )
1278 .expect("expected a random document");
1279 preorder_document_2
1280 .set_id_for_creation(
1281 preorder,
1282 &entropy.0,
1283 2 + nonce_offset.unwrap_or_default(),
1284 platform_version,
1285 )
1286 .expect("expected to set the document id");
1287
1288 let mut document_1 = domain
1289 .random_document_with_identifier_and_entropy(
1290 &mut rng,
1291 identity_1.id(),
1292 entropy,
1293 DocumentFieldFillType::FillIfNotRequired,
1294 DocumentFieldFillSize::AnyDocumentFillSize,
1295 platform_version,
1296 )
1297 .expect("expected a random document");
1298 document_1
1299 .set_id_for_creation(
1300 domain,
1301 &entropy.0,
1302 3 + nonce_offset.unwrap_or_default(),
1303 platform_version,
1304 )
1305 .expect("expected to set the document id");
1306
1307 let mut document_2 = domain
1308 .random_document_with_identifier_and_entropy(
1309 &mut rng,
1310 identity_2.id(),
1311 entropy,
1312 DocumentFieldFillType::FillIfNotRequired,
1313 DocumentFieldFillSize::AnyDocumentFillSize,
1314 platform_version,
1315 )
1316 .expect("expected a random document");
1317 document_2
1318 .set_id_for_creation(
1319 domain,
1320 &entropy.0,
1321 3 + nonce_offset.unwrap_or_default(),
1322 platform_version,
1323 )
1324 .expect("expected to set the document id");
1325
1326 document_1.set("parentDomainName", "dash".into());
1327 document_1.set("normalizedParentDomainName", "dash".into());
1328 document_1.set("label", name.into());
1329 document_1.set(
1330 "normalizedLabel",
1331 convert_to_homograph_safe_chars(name).into(),
1332 );
1333 document_1.set("records.identity", document_1.owner_id().into());
1334 document_1.set("subdomainRules.allowSubdomains", false.into());
1335
1336 document_2.set("parentDomainName", "dash".into());
1337 document_2.set("normalizedParentDomainName", "dash".into());
1338 document_2.set("label", name.into());
1339 document_2.set(
1340 "normalizedLabel",
1341 convert_to_homograph_safe_chars(name).into(),
1342 );
1343 document_2.set("records.identity", document_2.owner_id().into());
1344 document_2.set("subdomainRules.allowSubdomains", false.into());
1345
1346 let salt_1: [u8; 32] = rng.gen();
1347 let salt_2: [u8; 32] = rng.gen();
1348
1349 let mut salted_domain_buffer_1: Vec<u8> = vec![];
1350 salted_domain_buffer_1.extend(salt_1);
1351 salted_domain_buffer_1.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1352
1353 let salted_domain_hash_1 = hash_double(salted_domain_buffer_1);
1354
1355 let mut salted_domain_buffer_2: Vec<u8> = vec![];
1356 salted_domain_buffer_2.extend(salt_2);
1357 salted_domain_buffer_2.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1358
1359 let salted_domain_hash_2 = hash_double(salted_domain_buffer_2);
1360
1361 preorder_document_1.set("saltedDomainHash", salted_domain_hash_1.into());
1362 preorder_document_2.set("saltedDomainHash", salted_domain_hash_2.into());
1363
1364 document_1.set("preorderSalt", salt_1.into());
1365 document_2.set("preorderSalt", salt_2.into());
1366
1367 let documents_batch_create_preorder_transition_1 =
1368 BatchTransition::new_document_creation_transition_from_document(
1369 preorder_document_1.clone(),
1370 preorder,
1371 entropy.0,
1372 key_1,
1373 2 + nonce_offset.unwrap_or_default(),
1374 0,
1375 None,
1376 signer_1,
1377 platform_version,
1378 None,
1379 )
1380 .await
1381 .expect("expect to create documents batch transition");
1382
1383 let documents_batch_create_serialized_preorder_transition_1 =
1384 documents_batch_create_preorder_transition_1
1385 .serialize_to_bytes()
1386 .expect("expected documents batch serialized state transition");
1387
1388 let documents_batch_create_preorder_transition_2 =
1389 BatchTransition::new_document_creation_transition_from_document(
1390 preorder_document_2.clone(),
1391 preorder,
1392 entropy.0,
1393 key_2,
1394 2 + nonce_offset.unwrap_or_default(),
1395 0,
1396 None,
1397 signer_2,
1398 platform_version,
1399 None,
1400 )
1401 .await
1402 .expect("expect to create documents batch transition");
1403
1404 let documents_batch_create_serialized_preorder_transition_2 =
1405 documents_batch_create_preorder_transition_2
1406 .serialize_to_bytes()
1407 .expect("expected documents batch serialized state transition");
1408
1409 let documents_batch_create_transition_1 =
1410 BatchTransition::new_document_creation_transition_from_document(
1411 document_1.clone(),
1412 domain,
1413 entropy.0,
1414 key_1,
1415 3 + nonce_offset.unwrap_or_default(),
1416 0,
1417 None,
1418 signer_1,
1419 platform_version,
1420 None,
1421 )
1422 .await
1423 .expect("expect to create documents batch transition");
1424
1425 let documents_batch_create_serialized_transition_1 = documents_batch_create_transition_1
1426 .serialize_to_bytes()
1427 .expect("expected documents batch serialized state transition");
1428
1429 let documents_batch_create_transition_2 =
1430 BatchTransition::new_document_creation_transition_from_document(
1431 document_2.clone(),
1432 domain,
1433 entropy.0,
1434 key_2,
1435 3 + nonce_offset.unwrap_or_default(),
1436 0,
1437 None,
1438 signer_2,
1439 platform_version,
1440 None,
1441 )
1442 .await
1443 .expect("expect to create documents batch transition");
1444
1445 let documents_batch_create_serialized_transition_2 = documents_batch_create_transition_2
1446 .serialize_to_bytes()
1447 .expect("expected documents batch serialized state transition");
1448
1449 if !skip_preorder {
1450 let transaction = platform.drive.grove.start_transaction();
1451
1452 let processing_result = platform
1453 .platform
1454 .process_raw_state_transitions(
1455 &[
1456 documents_batch_create_serialized_preorder_transition_1.clone(),
1457 documents_batch_create_serialized_preorder_transition_2.clone(),
1458 ],
1459 platform_state,
1460 &BlockInfo::default_with_time(
1461 platform_state
1462 .last_committed_block_time_ms()
1463 .unwrap_or_default()
1464 + 3000,
1465 ),
1466 &transaction,
1467 platform_version,
1468 false,
1469 None,
1470 )
1471 .expect("expected to process state transition");
1472
1473 platform
1474 .drive
1475 .grove
1476 .commit_transaction(transaction)
1477 .unwrap()
1478 .expect("expected to commit transaction");
1479
1480 let successful_count = processing_result
1481 .execution_results()
1482 .iter()
1483 .filter(|result| {
1484 assert_matches!(
1485 result,
1486 StateTransitionExecutionResult::SuccessfulExecution { .. }
1487 );
1488 true
1489 })
1490 .count();
1491
1492 assert_eq!(successful_count, 2);
1493 }
1494
1495 let transaction = platform.drive.grove.start_transaction();
1496
1497 let processing_result = platform
1498 .platform
1499 .process_raw_state_transitions(
1500 &[
1501 documents_batch_create_serialized_transition_1.clone(),
1502 documents_batch_create_serialized_transition_2.clone(),
1503 ],
1504 platform_state,
1505 &BlockInfo::default_with_time(
1506 platform_state
1507 .last_committed_block_time_ms()
1508 .unwrap_or_default()
1509 + 3000,
1510 ),
1511 &transaction,
1512 platform_version,
1513 false,
1514 None,
1515 )
1516 .expect("expected to process state transition");
1517
1518 platform
1519 .drive
1520 .grove
1521 .commit_transaction(transaction)
1522 .unwrap()
1523 .expect("expected to commit transaction");
1524
1525 let successful_count = processing_result
1526 .execution_results()
1527 .iter()
1528 .filter(|result| {
1529 assert_matches!(
1530 result,
1531 StateTransitionExecutionResult::SuccessfulExecution { .. }
1532 );
1533 true
1534 })
1535 .count();
1536
1537 assert_eq!(successful_count, 2);
1538 (
1539 ((preorder_document_1, entropy), (document_1, entropy)),
1540 ((preorder_document_2, entropy), (document_2, entropy)),
1541 dpns_contract,
1542 )
1543 }
1544
1545 #[allow(clippy::too_many_arguments)]
1546 async fn create_dpns_name_contest_on_identities_for_contract_records(
1547 platform: &mut TempPlatform<MockCoreRPCLike>,
1548 identity_1: &(Identity, SimpleSigner, IdentityPublicKey),
1549 identity_2: &(Identity, SimpleSigner, IdentityPublicKey),
1550 contract_1: &DataContract,
1551 contract_2: &DataContract,
1552 platform_state: &PlatformState,
1553 mut rng: StdRng,
1554 name: &str,
1555 platform_version: &PlatformVersion,
1556 ) -> (
1557 ((Document, Bytes32), (Document, Bytes32)),
1558 ((Document, Bytes32), (Document, Bytes32)),
1559 DataContract,
1560 ) {
1561 let (identity_1, signer_1, key_1) = identity_1;
1562
1563 let (identity_2, signer_2, key_2) = identity_2;
1564
1565 let dpns_contract = setup_contract(
1566 &platform.drive,
1567 "tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-with-contract-id.json",
1568 None,
1569 None,
1570 None::<fn(&mut DataContract)>,
1571 None,
1572 None,
1573 );
1574
1575 let preorder = dpns_contract
1576 .document_type_for_name("preorder")
1577 .expect("expected a profile document type");
1578
1579 assert!(!preorder.documents_mutable());
1580 assert!(preorder.documents_can_be_deleted());
1581 assert!(!preorder.documents_transferable().is_transferable());
1582
1583 let domain = dpns_contract
1584 .document_type_for_name("domain")
1585 .expect("expected a profile document type");
1586
1587 assert!(!domain.documents_mutable());
1588 assert!(domain.documents_can_be_deleted());
1590 assert!(domain.documents_transferable().is_transferable());
1591
1592 let entropy = Bytes32::random_with_rng(&mut rng);
1593
1594 let mut preorder_document_1 = preorder
1595 .random_document_with_identifier_and_entropy(
1596 &mut rng,
1597 identity_1.id(),
1598 entropy,
1599 DocumentFieldFillType::FillIfNotRequired,
1600 DocumentFieldFillSize::AnyDocumentFillSize,
1601 platform_version,
1602 )
1603 .expect("expected a random document");
1604 preorder_document_1
1605 .set_id_for_creation(preorder, &entropy.0, 2, platform_version)
1606 .expect("expected to set the document id");
1607
1608 let mut preorder_document_2 = preorder
1609 .random_document_with_identifier_and_entropy(
1610 &mut rng,
1611 identity_2.id(),
1612 entropy,
1613 DocumentFieldFillType::FillIfNotRequired,
1614 DocumentFieldFillSize::AnyDocumentFillSize,
1615 platform_version,
1616 )
1617 .expect("expected a random document");
1618 preorder_document_2
1619 .set_id_for_creation(preorder, &entropy.0, 2, platform_version)
1620 .expect("expected to set the document id");
1621
1622 let mut document_1 = domain
1623 .random_document_with_identifier_and_entropy(
1624 &mut rng,
1625 identity_1.id(),
1626 entropy,
1627 DocumentFieldFillType::FillIfNotRequired,
1628 DocumentFieldFillSize::AnyDocumentFillSize,
1629 platform_version,
1630 )
1631 .expect("expected a random document");
1632 document_1
1633 .set_id_for_creation(domain, &entropy.0, 3, platform_version)
1634 .expect("expected to set the document id");
1635
1636 let mut document_2 = domain
1637 .random_document_with_identifier_and_entropy(
1638 &mut rng,
1639 identity_2.id(),
1640 entropy,
1641 DocumentFieldFillType::FillIfNotRequired,
1642 DocumentFieldFillSize::AnyDocumentFillSize,
1643 platform_version,
1644 )
1645 .expect("expected a random document");
1646 document_2
1647 .set_id_for_creation(domain, &entropy.0, 3, platform_version)
1648 .expect("expected to set the document id");
1649
1650 document_1.set("parentDomainName", "dash".into());
1651 document_1.set("normalizedParentDomainName", "dash".into());
1652 document_1.set("label", name.into());
1653 document_1.set(
1654 "normalizedLabel",
1655 convert_to_homograph_safe_chars(name).into(),
1656 );
1657 document_1.remove("records.identity");
1658 document_1.set("records.contract", contract_1.id().into());
1659 document_1.set("subdomainRules.allowSubdomains", false.into());
1660
1661 document_2.set("parentDomainName", "dash".into());
1662 document_2.set("normalizedParentDomainName", "dash".into());
1663 document_2.set("label", name.into());
1664 document_2.set(
1665 "normalizedLabel",
1666 convert_to_homograph_safe_chars(name).into(),
1667 );
1668 document_2.remove("records.identity");
1669 document_2.set("records.contract", contract_2.id().into());
1670 document_2.set("subdomainRules.allowSubdomains", false.into());
1671
1672 let salt_1: [u8; 32] = rng.gen();
1673 let salt_2: [u8; 32] = rng.gen();
1674
1675 let mut salted_domain_buffer_1: Vec<u8> = vec![];
1676 salted_domain_buffer_1.extend(salt_1);
1677 salted_domain_buffer_1.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1678
1679 let salted_domain_hash_1 = hash_double(salted_domain_buffer_1);
1680
1681 let mut salted_domain_buffer_2: Vec<u8> = vec![];
1682 salted_domain_buffer_2.extend(salt_2);
1683 salted_domain_buffer_2.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1684
1685 let salted_domain_hash_2 = hash_double(salted_domain_buffer_2);
1686
1687 preorder_document_1.set("saltedDomainHash", salted_domain_hash_1.into());
1688 preorder_document_2.set("saltedDomainHash", salted_domain_hash_2.into());
1689
1690 document_1.set("preorderSalt", salt_1.into());
1691 document_2.set("preorderSalt", salt_2.into());
1692
1693 let documents_batch_create_preorder_transition_1 =
1694 BatchTransition::new_document_creation_transition_from_document(
1695 preorder_document_1.clone(),
1696 preorder,
1697 entropy.0,
1698 key_1,
1699 2,
1700 0,
1701 None,
1702 signer_1,
1703 platform_version,
1704 None,
1705 )
1706 .await
1707 .expect("expect to create documents batch transition");
1708
1709 let documents_batch_create_serialized_preorder_transition_1 =
1710 documents_batch_create_preorder_transition_1
1711 .serialize_to_bytes()
1712 .expect("expected documents batch serialized state transition");
1713
1714 let documents_batch_create_preorder_transition_2 =
1715 BatchTransition::new_document_creation_transition_from_document(
1716 preorder_document_2.clone(),
1717 preorder,
1718 entropy.0,
1719 key_2,
1720 2,
1721 0,
1722 None,
1723 signer_2,
1724 platform_version,
1725 None,
1726 )
1727 .await
1728 .expect("expect to create documents batch transition");
1729
1730 let documents_batch_create_serialized_preorder_transition_2 =
1731 documents_batch_create_preorder_transition_2
1732 .serialize_to_bytes()
1733 .expect("expected documents batch serialized state transition");
1734
1735 let documents_batch_create_transition_1 =
1736 BatchTransition::new_document_creation_transition_from_document(
1737 document_1.clone(),
1738 domain,
1739 entropy.0,
1740 key_1,
1741 3,
1742 0,
1743 None,
1744 signer_1,
1745 platform_version,
1746 None,
1747 )
1748 .await
1749 .expect("expect to create documents batch transition");
1750
1751 let documents_batch_create_serialized_transition_1 = documents_batch_create_transition_1
1752 .serialize_to_bytes()
1753 .expect("expected documents batch serialized state transition");
1754
1755 let documents_batch_create_transition_2 =
1756 BatchTransition::new_document_creation_transition_from_document(
1757 document_2.clone(),
1758 domain,
1759 entropy.0,
1760 key_2,
1761 3,
1762 0,
1763 None,
1764 signer_2,
1765 platform_version,
1766 None,
1767 )
1768 .await
1769 .expect("expect to create documents batch transition");
1770
1771 let documents_batch_create_serialized_transition_2 = documents_batch_create_transition_2
1772 .serialize_to_bytes()
1773 .expect("expected documents batch serialized state transition");
1774
1775 let transaction = platform.drive.grove.start_transaction();
1776
1777 let processing_result = platform
1778 .platform
1779 .process_raw_state_transitions(
1780 &[
1781 documents_batch_create_serialized_preorder_transition_1.clone(),
1782 documents_batch_create_serialized_preorder_transition_2.clone(),
1783 ],
1784 platform_state,
1785 &BlockInfo::default_with_time(
1786 platform_state
1787 .last_committed_block_time_ms()
1788 .unwrap_or_default()
1789 + 3000,
1790 ),
1791 &transaction,
1792 platform_version,
1793 false,
1794 None,
1795 )
1796 .expect("expected to process state transition");
1797
1798 platform
1799 .drive
1800 .grove
1801 .commit_transaction(transaction)
1802 .unwrap()
1803 .expect("expected to commit transaction");
1804
1805 assert_eq!(processing_result.valid_count(), 2);
1806
1807 let transaction = platform.drive.grove.start_transaction();
1808
1809 let processing_result = platform
1810 .platform
1811 .process_raw_state_transitions(
1812 &[
1813 documents_batch_create_serialized_transition_1.clone(),
1814 documents_batch_create_serialized_transition_2.clone(),
1815 ],
1816 platform_state,
1817 &BlockInfo::default_with_time(
1818 platform_state
1819 .last_committed_block_time_ms()
1820 .unwrap_or_default()
1821 + 3000,
1822 ),
1823 &transaction,
1824 platform_version,
1825 false,
1826 None,
1827 )
1828 .expect("expected to process state transition");
1829
1830 platform
1831 .drive
1832 .grove
1833 .commit_transaction(transaction)
1834 .unwrap()
1835 .expect("expected to commit transaction");
1836
1837 assert_eq!(processing_result.valid_count(), 2);
1838 (
1839 ((preorder_document_1, entropy), (document_1, entropy)),
1840 ((preorder_document_2, entropy), (document_2, entropy)),
1841 dpns_contract,
1842 )
1843 }
1844
1845 pub(in crate::execution) async fn add_contender_to_dpns_name_contest(
1846 platform: &mut TempPlatform<MockCoreRPCLike>,
1847 platform_state: &PlatformState,
1848 seed: u64,
1849 name: &str,
1850 expect_err: Option<&str>,
1851 platform_version: &PlatformVersion,
1852 ) -> Identity {
1853 let DpnsContenderJoin {
1854 contender: identity,
1855 result,
1856 ..
1857 } = add_contender_to_dpns_name_contest_paying(
1858 platform,
1859 platform_state,
1860 seed,
1861 name,
1862 None,
1863 platform_version,
1864 )
1865 .await;
1866
1867 if let Some(expected_err) = expect_err {
1868 let StateTransitionExecutionResult::PaidConsensusError {
1869 error: consensus_error,
1870 ..
1871 } = result
1872 else {
1873 panic!("expected a paid consensus error, got {result:?}");
1874 };
1875 assert_eq!(consensus_error.to_string(), expected_err);
1876 } else {
1877 assert_matches!(result, SuccessfulExecution { .. });
1878 }
1879 identity
1880 }
1881
1882 pub(in crate::execution) struct DpnsContenderJoin {
1884 pub contender: Identity,
1886 pub balance_before_create: Credits,
1888 pub result: StateTransitionExecutionResult,
1890 }
1891
1892 pub(in crate::execution) async fn add_contender_to_dpns_name_contest_paying(
1897 platform: &mut TempPlatform<MockCoreRPCLike>,
1898 platform_state: &PlatformState,
1899 seed: u64,
1900 name: &str,
1901 contest_fund: Option<Credits>,
1902 platform_version: &PlatformVersion,
1903 ) -> DpnsContenderJoin {
1904 let mut rng = StdRng::seed_from_u64(seed);
1905
1906 let (identity_1, signer_1, key_1) = setup_identity(
1907 platform,
1908 rng.gen(),
1909 dash_to_credits!(0.5) + contest_fund.unwrap_or_default(),
1910 );
1911
1912 let dpns = platform
1913 .drive
1914 .cache
1915 .system_data_contracts
1916 .load_dpns(platform_version)
1917 .expect("expected the dpns system contract");
1918 let dpns_contract = dpns.clone();
1919
1920 let preorder = dpns_contract
1921 .document_type_for_name("preorder")
1922 .expect("expected a profile document type");
1923
1924 let domain = dpns_contract
1925 .document_type_for_name("domain")
1926 .expect("expected a profile document type");
1927
1928 let entropy = Bytes32::random_with_rng(&mut rng);
1929
1930 let mut preorder_document_1 = preorder
1931 .random_document_with_identifier_and_entropy(
1932 &mut rng,
1933 identity_1.id(),
1934 entropy,
1935 DocumentFieldFillType::FillIfNotRequired,
1936 DocumentFieldFillSize::AnyDocumentFillSize,
1937 platform_version,
1938 )
1939 .expect("expected a random document");
1940 preorder_document_1
1941 .set_id_for_creation(preorder, &entropy.0, 2, platform_version)
1942 .expect("expected to set the document id");
1943
1944 let mut document_1 = domain
1945 .random_document_with_identifier_and_entropy(
1946 &mut rng,
1947 identity_1.id(),
1948 entropy,
1949 DocumentFieldFillType::FillIfNotRequired,
1950 DocumentFieldFillSize::AnyDocumentFillSize,
1951 platform_version,
1952 )
1953 .expect("expected a random document");
1954 document_1
1955 .set_id_for_creation(domain, &entropy.0, 3, platform_version)
1956 .expect("expected to set the document id");
1957
1958 document_1.set("parentDomainName", "dash".into());
1959 document_1.set("normalizedParentDomainName", "dash".into());
1960 document_1.set("label", name.into());
1961 document_1.set(
1962 "normalizedLabel",
1963 convert_to_homograph_safe_chars(name).into(),
1964 );
1965 document_1.set("records.identity", document_1.owner_id().into());
1966 document_1.set("subdomainRules.allowSubdomains", false.into());
1967
1968 let salt_1: [u8; 32] = rng.gen();
1969
1970 let mut salted_domain_buffer_1: Vec<u8> = vec![];
1971 salted_domain_buffer_1.extend(salt_1);
1972 salted_domain_buffer_1.extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
1973
1974 let salted_domain_hash_1 = hash_double(salted_domain_buffer_1);
1975
1976 preorder_document_1.set("saltedDomainHash", salted_domain_hash_1.into());
1977
1978 document_1.set("preorderSalt", salt_1.into());
1979
1980 let documents_batch_create_preorder_transition_1 =
1981 BatchTransition::new_document_creation_transition_from_document(
1982 preorder_document_1,
1983 preorder,
1984 entropy.0,
1985 &key_1,
1986 2,
1987 0,
1988 None,
1989 &signer_1,
1990 platform_version,
1991 None,
1992 )
1993 .await
1994 .expect("expect to create documents batch transition");
1995
1996 let documents_batch_create_serialized_preorder_transition_1 =
1997 documents_batch_create_preorder_transition_1
1998 .serialize_to_bytes()
1999 .expect("expected documents batch serialized state transition");
2000
2001 let documents_batch_create_transition_1 =
2002 BatchTransition::new_document_creation_transition_from_document(
2003 document_1,
2004 domain,
2005 entropy.0,
2006 &key_1,
2007 3,
2008 0,
2009 None,
2010 &signer_1,
2011 platform_version,
2012 Some(StateTransitionCreationOptions {
2013 contest_fund,
2014 ..Default::default()
2015 }),
2016 )
2017 .await
2018 .expect("expect to create documents batch transition");
2019
2020 let documents_batch_create_serialized_transition_1 = documents_batch_create_transition_1
2021 .serialize_to_bytes()
2022 .expect("expected documents batch serialized state transition");
2023
2024 let transaction = platform.drive.grove.start_transaction();
2025
2026 let processing_result = platform
2027 .platform
2028 .process_raw_state_transitions(
2029 &[documents_batch_create_serialized_preorder_transition_1.clone()],
2030 platform_state,
2031 &BlockInfo::default_with_time(
2032 platform_state
2033 .last_committed_block_time_ms()
2034 .unwrap_or_default()
2035 + 3000,
2036 ),
2037 &transaction,
2038 platform_version,
2039 false,
2040 None,
2041 )
2042 .expect("expected to process state transition");
2043
2044 platform
2045 .drive
2046 .grove
2047 .commit_transaction(transaction)
2048 .unwrap()
2049 .expect("expected to commit transaction");
2050
2051 assert_eq!(
2052 processing_result.valid_count(),
2053 1,
2054 "expected the preorder to pass: {:?}",
2055 processing_result.execution_results()
2056 );
2057
2058 let balance_before_create = platform
2059 .drive
2060 .fetch_identity_balance(identity_1.id().to_buffer(), None, platform_version)
2061 .expect("expected to fetch the contender's balance")
2062 .expect("expected the contender to have a balance");
2063
2064 let transaction = platform.drive.grove.start_transaction();
2065
2066 let processing_result = platform
2067 .platform
2068 .process_raw_state_transitions(
2069 &[documents_batch_create_serialized_transition_1.clone()],
2070 platform_state,
2071 &BlockInfo::default_with_time(
2072 platform_state
2073 .last_committed_block_time_ms()
2074 .unwrap_or_default()
2075 + 3000,
2076 ),
2077 &transaction,
2078 platform_version,
2079 false,
2080 None,
2081 )
2082 .expect("expected to process state transition");
2083
2084 platform
2085 .drive
2086 .grove
2087 .commit_transaction(transaction)
2088 .unwrap()
2089 .expect("expected to commit transaction");
2090
2091 DpnsContenderJoin {
2092 contender: identity_1,
2093 balance_before_create,
2094 result: processing_result.into_execution_results().remove(0),
2095 }
2096 }
2097
2098 pub(in crate::execution) fn verify_dpns_name_contest(
2099 platform: &mut TempPlatform<MockCoreRPCLike>,
2100 platform_state: &Guard<Arc<PlatformState>>,
2101 dpns_contract: &DataContract,
2102 identity_1: &Identity,
2103 identity_2: &Identity,
2104 name: &str,
2105 platform_version: &PlatformVersion,
2106 ) {
2107 let domain = dpns_contract
2110 .document_type_for_name("domain")
2111 .expect("expected a profile document type");
2112
2113 let config = bincode::config::standard()
2114 .with_big_endian()
2115 .with_no_limit();
2116
2117 let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config)
2118 .expect("expected to encode the word dash");
2119
2120 let quantum_encoded =
2121 bincode::encode_to_vec(Value::Text(convert_to_homograph_safe_chars(name)), config)
2122 .expect("expected to encode the word quantum");
2123
2124 let index_name = "parentNameAndLabel".to_string();
2125
2126 let query_validation_result = platform
2127 .query_contested_resource_vote_state(
2128 GetContestedResourceVoteStateRequest {
2129 version: Some(get_contested_resource_vote_state_request::Version::V0(
2130 GetContestedResourceVoteStateRequestV0 {
2131 contract_id: dpns_contract.id().to_vec(),
2132 document_type_name: domain.name().clone(),
2133 index_name: index_name.clone(),
2134 index_values: vec![dash_encoded.clone(), quantum_encoded.clone()],
2135 result_type: ResultType::DocumentsAndVoteTally as i32,
2136 allow_include_locked_and_abstaining_vote_tally: true,
2137 start_at_identifier_info: None,
2138 count: None,
2139 prove: false,
2140 },
2141 )),
2142 },
2143 platform_state,
2144 platform_version,
2145 )
2146 .expect("expected to execute query")
2147 .into_data()
2148 .expect("expected query to be valid");
2149
2150 let get_contested_resource_vote_state_response::Version::V0(
2151 GetContestedResourceVoteStateResponseV0 {
2152 metadata: _,
2153 result,
2154 },
2155 ) = query_validation_result.version.expect("expected a version");
2156
2157 let Some(
2158 get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders(
2159 get_contested_resource_vote_state_response_v0::ContestedResourceContenders {
2160 contenders,
2161 ..
2162 },
2163 ),
2164 ) = result
2165 else {
2166 panic!("expected contenders")
2167 };
2168
2169 assert_eq!(contenders.len(), 2);
2170
2171 let first_contender = contenders.first().unwrap();
2172
2173 let second_contender = contenders.last().unwrap();
2174
2175 let first_contender_document = Document::from_bytes(
2176 first_contender
2177 .document
2178 .as_ref()
2179 .expect("expected a document")
2180 .as_slice(),
2181 domain,
2182 platform_version,
2183 )
2184 .expect("expected to get document");
2185
2186 let second_contender_document = Document::from_bytes(
2187 second_contender
2188 .document
2189 .as_ref()
2190 .expect("expected a document")
2191 .as_slice(),
2192 domain,
2193 platform_version,
2194 )
2195 .expect("expected to get document");
2196
2197 assert_ne!(first_contender_document, second_contender_document);
2198
2199 assert_eq!(first_contender.identifier, identity_1.id().to_vec());
2200
2201 assert_eq!(second_contender.identifier, identity_2.id().to_vec());
2202
2203 assert_eq!(first_contender.vote_count, Some(0));
2204
2205 assert_eq!(second_contender.vote_count, Some(0));
2206
2207 let GetContestedResourceVoteStateResponse { version } = platform
2208 .query_contested_resource_vote_state(
2209 GetContestedResourceVoteStateRequest {
2210 version: Some(get_contested_resource_vote_state_request::Version::V0(
2211 GetContestedResourceVoteStateRequestV0 {
2212 contract_id: dpns_contract.id().to_vec(),
2213 document_type_name: domain.name().clone(),
2214 index_name: "parentNameAndLabel".to_string(),
2215 index_values: vec![dash_encoded, quantum_encoded],
2216 result_type: ResultType::DocumentsAndVoteTally as i32,
2217 allow_include_locked_and_abstaining_vote_tally: true,
2218 start_at_identifier_info: None,
2219 count: None,
2220 prove: true,
2221 },
2222 )),
2223 },
2224 platform_state,
2225 platform_version,
2226 )
2227 .expect("expected to execute query")
2228 .into_data()
2229 .expect("expected query to be valid");
2230
2231 let get_contested_resource_vote_state_response::Version::V0(
2232 GetContestedResourceVoteStateResponseV0 {
2233 metadata: _,
2234 result,
2235 },
2236 ) = version.expect("expected a version");
2237
2238 let Some(get_contested_resource_vote_state_response_v0::Result::Proof(proof)) = result
2239 else {
2240 panic!("expected contenders")
2241 };
2242
2243 let resolved_contested_document_vote_poll_drive_query =
2244 ResolvedContestedDocumentVotePollDriveQuery {
2245 vote_poll: ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed {
2246 contract: DataContractResolvedInfo::BorrowedDataContract(dpns_contract),
2247 document_type_name: domain.name().clone(),
2248 index_name: index_name.clone(),
2249 index_values: vec![
2250 Value::Text("dash".to_string()),
2251 Value::Text(convert_to_homograph_safe_chars(name)),
2252 ],
2253 },
2254 result_type: DocumentsAndVoteTally,
2255 offset: None,
2256 limit: None,
2257 start_at: None,
2258 allow_include_locked_and_abstaining_vote_tally: true,
2259 };
2260
2261 let (_, result) = resolved_contested_document_vote_poll_drive_query
2262 .verify_vote_poll_vote_state_proof(proof.grovedb_proof.as_ref(), platform_version)
2263 .expect("expected to verify proof");
2264
2265 let contenders = result.contenders;
2266
2267 assert_eq!(contenders.len(), 2);
2268
2269 let first_contender = contenders.first().unwrap();
2270
2271 let second_contender = contenders.last().unwrap();
2272
2273 let first_contender_document = Document::from_bytes(
2274 first_contender
2275 .serialized_document()
2276 .as_ref()
2277 .expect("expected a document")
2278 .as_slice(),
2279 domain,
2280 platform_version,
2281 )
2282 .expect("expected to get document");
2283
2284 let second_contender_document = Document::from_bytes(
2285 second_contender
2286 .serialized_document()
2287 .as_ref()
2288 .expect("expected a document")
2289 .as_slice(),
2290 domain,
2291 platform_version,
2292 )
2293 .expect("expected to get document");
2294
2295 assert_ne!(first_contender_document, second_contender_document);
2296
2297 assert_eq!(first_contender.identity_id(), identity_1.id());
2298
2299 assert_eq!(second_contender.identity_id(), identity_2.id());
2300
2301 assert_eq!(first_contender.vote_tally(), Some(0));
2302
2303 assert_eq!(second_contender.vote_tally(), Some(0));
2304 }
2305
2306 pub(in crate::execution) fn dpns_name_vote_poll(
2308 dpns_contract: &DataContract,
2309 name: &str,
2310 ) -> ContestedDocumentResourceVotePoll {
2311 ContestedDocumentResourceVotePoll {
2312 contract_id: dpns_contract.id(),
2313 document_type_name: "domain".to_string(),
2314 index_name: "parentNameAndLabel".to_string(),
2315 index_values: vec![
2316 Value::Text("dash".to_string()),
2317 Value::Text(convert_to_homograph_safe_chars(name)),
2318 ],
2319 }
2320 }
2321
2322 pub(in crate::execution) fn fill_contest_with_bare_contenders(
2326 platform: &TempPlatform<MockCoreRPCLike>,
2327 vote_poll: &ContestedDocumentResourceVotePoll,
2328 contenders: u64,
2329 platform_version: &PlatformVersion,
2330 ) {
2331 let resolved_vote_poll = vote_poll
2332 .resolve(&platform.drive, None, platform_version)
2333 .expect("expected to resolve the vote poll");
2334 let choices_path = resolved_vote_poll
2335 .contenders_path(platform_version)
2336 .expect("expected the choices path");
2337 let (_, held) = platform
2338 .drive
2339 .fetch_contested_document_vote_poll_contender_count(
2340 &resolved_vote_poll,
2341 u16::MAX,
2342 &Default::default(),
2343 None,
2344 PlatformVersion::latest(),
2345 )
2346 .expect("expected the contender count");
2347 let operations = (held as u64..contenders)
2348 .map(|n| {
2349 let mut key = [0xEEu8; 32];
2350 key[24..].copy_from_slice(&n.to_be_bytes());
2351 LowLevelDriveOperation::insert_for_known_path_key_element(
2352 choices_path.clone(),
2353 key.to_vec(),
2354 Element::empty_tree(),
2355 )
2356 })
2357 .collect();
2358 platform
2359 .drive
2360 .apply_batch_low_level_drive_operations(
2361 None,
2362 None,
2363 operations,
2364 &mut vec![],
2365 &platform_version.drive,
2366 )
2367 .expect("expected to write the bare contenders");
2368 }
2369
2370 #[allow(clippy::too_many_arguments)]
2372 pub(in crate::execution) async fn serialized_dpns_name_vote(
2373 dpns_contract: &DataContract,
2374 resource_vote_choice: ResourceVoteChoice,
2375 name: &str,
2376 signer: &SimpleSigner,
2377 pro_tx_hash: Identifier,
2378 voting_key: &IdentityPublicKey,
2379 nonce: IdentityNonce,
2380 platform_version: &PlatformVersion,
2381 ) -> Vec<u8> {
2382 let vote = Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 {
2383 vote_poll: VotePoll::ContestedDocumentResourceVotePoll(dpns_name_vote_poll(
2384 dpns_contract,
2385 name,
2386 )),
2387 resource_vote_choice,
2388 }));
2389
2390 MasternodeVoteTransition::try_from_vote_with_signer(
2391 vote,
2392 signer,
2393 pro_tx_hash,
2394 voting_key,
2395 nonce,
2396 platform_version,
2397 None,
2398 )
2399 .await
2400 .expect("expected to make transition vote")
2401 .serialize_to_bytes()
2402 .expect("expected to serialize the masternode vote")
2403 }
2404
2405 pub(in crate::execution) fn first_time_check_tx_errors(
2407 platform: &TempPlatform<MockCoreRPCLike>,
2408 platform_state: &PlatformState,
2409 serialized_transition: &[u8],
2410 platform_version: &PlatformVersion,
2411 ) -> Vec<ConsensusError> {
2412 platform
2413 .check_tx(
2414 serialized_transition,
2415 CheckTxLevel::FirstTimeCheck,
2416 &PlatformRef {
2417 drive: &platform.drive,
2418 state: platform_state,
2419 config: &platform.config,
2420 core_rpc: &platform.core_rpc,
2421 },
2422 platform_version,
2423 )
2424 .expect("expected check_tx to run")
2425 .errors
2426 }
2427
2428 #[allow(clippy::too_many_arguments)]
2429 pub(in crate::execution) async fn perform_vote(
2430 platform: &mut TempPlatform<MockCoreRPCLike>,
2431 platform_state: &Guard<Arc<PlatformState>>,
2432 dpns_contract: &DataContract,
2433 resource_vote_choice: ResourceVoteChoice,
2434 name: &str,
2435 signer: &SimpleSigner,
2436 pro_tx_hash: Identifier,
2437 voting_key: &IdentityPublicKey,
2438 nonce: IdentityNonce,
2439 expect_error: Option<&str>,
2440 platform_version: &PlatformVersion,
2441 ) {
2442 let masternode_vote_serialized_transition = serialized_dpns_name_vote(
2443 dpns_contract,
2444 resource_vote_choice,
2445 name,
2446 signer,
2447 pro_tx_hash,
2448 voting_key,
2449 nonce,
2450 platform_version,
2451 )
2452 .await;
2453
2454 if expect_error.is_none() {
2458 crate::test::helpers::state_mutation_guard::assert_check_tx_valid_at_all_levels(
2459 platform,
2460 &masternode_vote_serialized_transition,
2461 "masternode vote",
2462 );
2463 } else {
2464 assert!(
2467 !first_time_check_tx_errors(
2468 platform,
2469 platform_state,
2470 &masternode_vote_serialized_transition,
2471 platform_version,
2472 )
2473 .is_empty(),
2474 "check_tx must refuse a vote that a block refuses"
2475 );
2476 }
2477
2478 let transaction = platform.drive.grove.start_transaction();
2479
2480 let processing_result = platform
2481 .platform
2482 .process_raw_state_transitions(
2483 &[masternode_vote_serialized_transition.clone()],
2484 platform_state,
2485 &BlockInfo::default(),
2486 &transaction,
2487 platform_version,
2488 false,
2489 None,
2490 )
2491 .expect("expected to process state transition");
2492
2493 platform
2494 .drive
2495 .grove
2496 .commit_transaction(transaction)
2497 .unwrap()
2498 .expect("expected to commit transaction");
2499
2500 let execution_result = processing_result.into_execution_results().remove(0);
2501 if let Some(error_msg) = expect_error {
2502 assert_matches!(execution_result, UnpaidConsensusError(..));
2503 let UnpaidConsensusError(consensus_error) = execution_result else {
2504 panic!()
2505 };
2506 assert_eq!(consensus_error.to_string(), error_msg)
2507 } else {
2508 assert_matches!(execution_result, SuccessfulExecution { .. });
2509 }
2510 }
2511
2512 #[allow(clippy::too_many_arguments)]
2513 pub(in crate::execution) async fn perform_votes(
2514 platform: &mut TempPlatform<MockCoreRPCLike>,
2515 dpns_contract: &DataContract,
2516 resource_vote_choice: ResourceVoteChoice,
2517 name: &str,
2518 count: u64,
2519 start_seed: u64,
2520 nonce_offset: Option<IdentityNonce>,
2521 platform_version: &PlatformVersion,
2522 ) -> Vec<(Identifier, Identity, SimpleSigner, IdentityPublicKey)> {
2523 let mut masternode_infos = vec![];
2524 for i in 0..count {
2525 let (pro_tx_hash_bytes, voting_identity, signer, voting_key) =
2526 setup_masternode_voting_identity(platform, start_seed + i, platform_version);
2527
2528 let platform_state = platform.state.load();
2529
2530 perform_vote(
2531 platform,
2532 &platform_state,
2533 dpns_contract,
2534 resource_vote_choice,
2535 name,
2536 &signer,
2537 pro_tx_hash_bytes,
2538 &voting_key,
2539 1 + nonce_offset.unwrap_or_default(),
2540 None,
2541 platform_version,
2542 )
2543 .await;
2544
2545 masternode_infos.push((pro_tx_hash_bytes, voting_identity, signer, voting_key));
2546 }
2547 masternode_infos
2548 }
2549
2550 pub(in crate::execution) async fn perform_votes_multi(
2551 platform: &mut TempPlatform<MockCoreRPCLike>,
2552 dpns_contract: &DataContract,
2553 resource_vote_choices: Vec<(ResourceVoteChoice, u64)>,
2554 name: &str,
2555 start_seed: u64,
2556 nonce_offset: Option<IdentityNonce>,
2557 platform_version: &PlatformVersion,
2558 ) -> BTreeMap<ResourceVoteChoice, Vec<(Identifier, Identity, SimpleSigner, IdentityPublicKey)>>
2559 {
2560 let mut count_aggregate = start_seed;
2561 let mut masternodes_by_vote_choice = BTreeMap::new();
2562 for (resource_vote_choice, count) in resource_vote_choices.into_iter() {
2563 let masternode_infos = perform_votes(
2564 platform,
2565 dpns_contract,
2566 resource_vote_choice,
2567 name,
2568 count,
2569 count_aggregate,
2570 nonce_offset,
2571 platform_version,
2572 )
2573 .await;
2574 masternodes_by_vote_choice.insert(resource_vote_choice, masternode_infos);
2575 count_aggregate += count;
2576 }
2577 masternodes_by_vote_choice
2578 }
2579
2580 #[allow(clippy::too_many_arguments)]
2581 pub(in crate::execution) fn get_vote_states(
2582 platform: &TempPlatform<MockCoreRPCLike>,
2583 platform_state: &PlatformState,
2584 dpns_contract: &DataContract,
2585 name: &str,
2586 count: Option<u32>,
2587 allow_include_locked_and_abstaining_vote_tally: bool,
2588 start_at_identifier_info: Option<
2589 get_contested_resource_vote_state_request_v0::StartAtIdentifierInfo,
2590 >,
2591 result_type: ResultType,
2592 platform_version: &PlatformVersion,
2593 ) -> (
2594 Vec<Contender>,
2595 Option<u32>,
2596 Option<u32>,
2597 Option<FinishedVoteInfo>,
2598 ) {
2599 let domain = dpns_contract
2602 .document_type_for_name("domain")
2603 .expect("expected a profile document type");
2604
2605 let config = bincode::config::standard()
2606 .with_big_endian()
2607 .with_no_limit();
2608
2609 let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config)
2610 .expect("expected to encode the word dash");
2611
2612 let name_encoded =
2613 bincode::encode_to_vec(Value::Text(convert_to_homograph_safe_chars(name)), config)
2614 .expect("expected to encode the word quantum");
2615
2616 let index_name = "parentNameAndLabel".to_string();
2617
2618 let query_validation_result = platform
2619 .query_contested_resource_vote_state(
2620 GetContestedResourceVoteStateRequest {
2621 version: Some(get_contested_resource_vote_state_request::Version::V0(
2622 GetContestedResourceVoteStateRequestV0 {
2623 contract_id: dpns_contract.id().to_vec(),
2624 document_type_name: domain.name().clone(),
2625 index_name: index_name.clone(),
2626 index_values: vec![dash_encoded.clone(), name_encoded.clone()],
2627 result_type: result_type as i32,
2628 allow_include_locked_and_abstaining_vote_tally,
2629 start_at_identifier_info,
2630 count,
2631 prove: false,
2632 },
2633 )),
2634 },
2635 platform_state,
2636 platform_version,
2637 )
2638 .expect("expected to execute query")
2639 .into_data()
2640 .expect("expected query to be valid");
2641
2642 let get_contested_resource_vote_state_response::Version::V0(
2643 GetContestedResourceVoteStateResponseV0 {
2644 metadata: _,
2645 result,
2646 },
2647 ) = query_validation_result.version.expect("expected a version");
2648
2649 let Some(
2650 get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders(
2651 get_contested_resource_vote_state_response_v0::ContestedResourceContenders {
2652 contenders,
2653 abstain_vote_tally,
2654 lock_vote_tally,
2655 finished_vote_info,
2656 },
2657 ),
2658 ) = result
2659 else {
2660 panic!("expected contenders")
2661 };
2662 (
2663 contenders
2664 .into_iter()
2665 .map(|contender| {
2666 ContenderV0 {
2667 identity_id: contender.identifier.try_into().expect("expected 32 bytes"),
2668 document: contender.document.map(|document_bytes| {
2669 Document::from_bytes(
2670 document_bytes.as_slice(),
2671 domain,
2672 platform_version,
2673 )
2674 .expect("expected to deserialize document")
2675 }),
2676 vote_tally: contender.vote_count,
2677 }
2678 .into()
2679 })
2680 .collect(),
2681 abstain_vote_tally,
2682 lock_vote_tally,
2683 finished_vote_info,
2684 )
2685 }
2686
2687 #[allow(clippy::too_many_arguments)]
2688 pub(in crate::execution) fn get_proved_vote_states(
2689 platform: &TempPlatform<MockCoreRPCLike>,
2690 platform_state: &PlatformState,
2691 dpns_contract: &DataContract,
2692 name: &str,
2693 count: Option<u32>,
2694 allow_include_locked_and_abstaining_vote_tally: bool,
2695 start_at_identifier_info: Option<
2696 get_contested_resource_vote_state_request_v0::StartAtIdentifierInfo,
2697 >,
2698 result_type: ResultType,
2699 platform_version: &PlatformVersion,
2700 ) -> (
2701 Vec<Contender>,
2702 Option<u32>,
2703 Option<u32>,
2704 Option<(ContestedDocumentVotePollWinnerInfo, BlockInfo)>,
2705 ) {
2706 let domain = dpns_contract
2709 .document_type_for_name("domain")
2710 .expect("expected a profile document type");
2711
2712 let config = bincode::config::standard()
2713 .with_big_endian()
2714 .with_no_limit();
2715
2716 let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config)
2717 .expect("expected to encode the word dash");
2718
2719 let name_encoded =
2720 bincode::encode_to_vec(Value::Text(convert_to_homograph_safe_chars(name)), config)
2721 .expect("expected to encode the word quantum");
2722
2723 let index_name = "parentNameAndLabel".to_string();
2724
2725 let query_validation_result = platform
2726 .query_contested_resource_vote_state(
2727 GetContestedResourceVoteStateRequest {
2728 version: Some(get_contested_resource_vote_state_request::Version::V0(
2729 GetContestedResourceVoteStateRequestV0 {
2730 contract_id: dpns_contract.id().to_vec(),
2731 document_type_name: domain.name().clone(),
2732 index_name: index_name.clone(),
2733 index_values: vec![dash_encoded.clone(), name_encoded.clone()],
2734 result_type: result_type as i32,
2735 allow_include_locked_and_abstaining_vote_tally,
2736 start_at_identifier_info,
2737 count,
2738 prove: true,
2739 },
2740 )),
2741 },
2742 platform_state,
2743 platform_version,
2744 )
2745 .expect("expected to execute query")
2746 .into_data()
2747 .expect("expected query to be valid");
2748
2749 let get_contested_resource_vote_state_response::Version::V0(
2750 GetContestedResourceVoteStateResponseV0 {
2751 metadata: _,
2752 result,
2753 },
2754 ) = query_validation_result.version.expect("expected a version");
2755
2756 let Some(get_contested_resource_vote_state_response_v0::Result::Proof(proof)) = result
2757 else {
2758 panic!("expected contenders")
2759 };
2760
2761 let resolved_contested_document_vote_poll_drive_query =
2762 ResolvedContestedDocumentVotePollDriveQuery {
2763 vote_poll: ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed {
2764 contract: DataContractResolvedInfo::BorrowedDataContract(dpns_contract),
2765 document_type_name: domain.name().clone(),
2766 index_name: index_name.clone(),
2767 index_values: vec![
2768 Value::Text("dash".to_string()),
2769 Value::Text(convert_to_homograph_safe_chars(name)),
2770 ],
2771 },
2772 result_type: ContestedDocumentVotePollDriveQueryResultType::try_from(
2773 result_type as i32,
2774 )
2775 .expect("expected valid result type"),
2776 offset: None,
2777 limit: count.map(|a| a as u16),
2778 start_at: None,
2779 allow_include_locked_and_abstaining_vote_tally,
2780 };
2781
2782 let (_, result) = resolved_contested_document_vote_poll_drive_query
2783 .verify_vote_poll_vote_state_proof(proof.grovedb_proof.as_ref(), platform_version)
2784 .expect("expected to verify proof");
2785
2786 let abstaining_vote_tally = result.abstaining_vote_tally;
2787 let lock_vote_tally = result.locked_vote_tally;
2788
2789 let contenders = result.contenders;
2790 let finished_vote_info = result.winner;
2791 (
2792 contenders
2793 .into_iter()
2794 .map(|contender| {
2795 ContenderV0 {
2796 identity_id: contender.identity_id(),
2797 document: contender
2798 .serialized_document()
2799 .as_ref()
2800 .map(|document_bytes| {
2801 Document::from_bytes(
2802 document_bytes.as_slice(),
2803 domain,
2804 platform_version,
2805 )
2806 .expect("expected to deserialize document")
2807 }),
2808 vote_tally: contender.vote_tally(),
2809 }
2810 .into()
2811 })
2812 .collect(),
2813 abstaining_vote_tally,
2814 lock_vote_tally,
2815 finished_vote_info,
2816 )
2817 }
2818
2819 pub(in crate::execution) fn create_token_contract_with_owner_identity(
2820 platform: &mut TempPlatform<MockCoreRPCLike>,
2821 identity_id: Identifier,
2822 token_configuration_modification: Option<impl FnOnce(&mut TokenConfiguration)>,
2823 contract_start_time: Option<TimestampMillis>,
2824 add_groups: Option<BTreeMap<GroupContractPosition, Group>>,
2825 contract_start_block: Option<BlockHeight>,
2826 platform_version: &PlatformVersion,
2827 ) -> (DataContract, Identifier) {
2828 create_token_contract_with_owner_identity_with_start_epoch(
2829 platform,
2830 identity_id,
2831 token_configuration_modification,
2832 contract_start_time,
2833 add_groups,
2834 contract_start_block,
2835 None,
2836 platform_version,
2837 )
2838 }
2839
2840 pub(in crate::execution) fn create_token_contract_with_owner_identity_with_start_epoch(
2843 platform: &mut TempPlatform<MockCoreRPCLike>,
2844 identity_id: Identifier,
2845 token_configuration_modification: Option<impl FnOnce(&mut TokenConfiguration)>,
2846 contract_start_time: Option<TimestampMillis>,
2847 add_groups: Option<BTreeMap<GroupContractPosition, Group>>,
2848 contract_start_block: Option<BlockHeight>,
2849 contract_start_epoch: Option<EpochIndex>,
2850 platform_version: &PlatformVersion,
2851 ) -> (DataContract, Identifier) {
2852 let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1);
2853
2854 let basic_token_contract = setup_contract(
2855 &platform.drive,
2856 "tests/supporting_files/contract/basic-token/basic-token.json",
2857 Some(data_contract_id.to_buffer()),
2858 Some(identity_id.to_buffer()),
2859 Some(|data_contract: &mut DataContract| {
2860 data_contract.set_created_at_epoch(Some(contract_start_epoch.unwrap_or_default()));
2861 data_contract.set_created_at(Some(contract_start_time.unwrap_or_default()));
2862 data_contract
2863 .set_created_at_block_height(Some(contract_start_block.unwrap_or_default()));
2864 if let Some(token_configuration_modification) = token_configuration_modification {
2865 let token_configuration = data_contract
2866 .token_configuration_mut(0)
2867 .expect("expected token configuration");
2868 token_configuration_modification(token_configuration);
2869 }
2870 if let Some(add_groups) = add_groups {
2871 data_contract.set_groups(add_groups);
2872 }
2873 }),
2874 None,
2875 Some(platform_version),
2876 );
2877
2878 let token_id = calculate_token_id(data_contract_id.as_bytes(), 0);
2879
2880 (basic_token_contract, token_id.into())
2881 }
2882
2883 pub(in crate::execution) fn create_card_game_internal_token_contract_with_owner_identity_burn_tokens(
2884 platform: &mut TempPlatform<MockCoreRPCLike>,
2885 identity_id: Identifier,
2886 platform_version: &PlatformVersion,
2887 ) -> (DataContract, Identifier, Identifier) {
2888 let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1);
2889
2890 let basic_token_contract = setup_contract(
2891 &platform.drive,
2892 "tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency-burn-tokens.json",
2893 Some(data_contract_id.to_buffer()),
2894 Some(identity_id.to_buffer()),
2895 Some(|data_contract: &mut DataContract| {
2896 data_contract.set_created_at_epoch(Some(0));
2897 data_contract.set_created_at(Some(0));
2898 data_contract.set_created_at_block_height(Some(0));
2899 }),
2900 None,
2901 Some(platform_version),
2902 );
2903
2904 let token_id = calculate_token_id(data_contract_id.as_bytes(), 0);
2905 let token_id_2 = calculate_token_id(data_contract_id.as_bytes(), 1);
2906
2907 (basic_token_contract, token_id.into(), token_id_2.into())
2908 }
2909
2910 pub(in crate::execution) fn create_card_game_internal_token_contract_with_owner_identity_transfer_tokens(
2911 platform: &mut TempPlatform<MockCoreRPCLike>,
2912 identity_id: Identifier,
2913 platform_version: &PlatformVersion,
2914 ) -> (DataContract, Identifier, Identifier) {
2915 let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1);
2916
2917 let basic_token_contract = setup_contract(
2918 &platform.drive,
2919 "tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json",
2920 Some(data_contract_id.to_buffer()),
2921 Some(identity_id.to_buffer()),
2922 Some(|data_contract: &mut DataContract| {
2923 data_contract.set_created_at_epoch(Some(0));
2924 data_contract.set_created_at(Some(0));
2925 data_contract.set_created_at_block_height(Some(0));
2926 }),
2927 None,
2928 Some(platform_version),
2929 );
2930
2931 let token_id = calculate_token_id(data_contract_id.as_bytes(), 0);
2932 let token_id_2 = calculate_token_id(data_contract_id.as_bytes(), 1);
2933
2934 (basic_token_contract, token_id.into(), token_id_2.into())
2935 }
2936
2937 pub(in crate::execution) fn create_card_game_external_token_contract_with_owner_identity(
2938 platform: &mut TempPlatform<MockCoreRPCLike>,
2939 token_contract_id: Identifier,
2940 token_contract_position: TokenContractPosition,
2941 token_cost_amount: TokenAmount,
2942 gas_fees_paid_by: GasFeesPaidBy,
2943 identity_id: Identifier,
2944 platform_version: &PlatformVersion,
2945 ) -> DataContract {
2946 let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1);
2947
2948 let basic_token_contract = setup_contract(
2949 &platform.drive,
2950 "tests/supporting_files/contract/crypto-card-game/crypto-card-game-use-external-currency.json",
2951 Some(data_contract_id.to_buffer()),
2952 Some(identity_id.to_buffer()),
2953 Some(|data_contract: &mut DataContract| {
2954 data_contract.set_created_at_epoch(Some(0));
2955 data_contract.set_created_at(Some(0));
2956 data_contract.set_created_at_block_height(Some(0));
2957 let document_type = data_contract.document_types_mut().get_mut("card").expect("expected a document type with name card");
2958 document_type.set_document_creation_token_cost(Some(DocumentActionTokenCost {
2959 contract_id: Some(token_contract_id),
2960 token_contract_position,
2961 token_amount: token_cost_amount,
2962 effect: DocumentActionTokenEffect::TransferTokenToContractOwner,
2963 gas_fees_paid_by,
2964 optional: false,
2965 }));
2966 let gas_fees_paid_by_int: u8 = gas_fees_paid_by.into();
2967 let schema = document_type.schema_mut();
2968 let token_cost = schema.get_mut("tokenCost").expect("expected to get token cost").expect("expected token cost to be set");
2969 let creation_token_cost = token_cost.get_mut("create").expect("expected to get creation token cost").expect("expected creation token cost to be set");
2970 creation_token_cost.set_value("contractId", token_contract_id.into()).expect("expected to set token contract id");
2971 creation_token_cost.set_value("tokenPosition", token_contract_position.into()).expect("expected to set token position");
2972 creation_token_cost.set_value("amount", token_cost_amount.into()).expect("expected to set token amount");
2973 creation_token_cost.set_value("gasFeesPaidBy", gas_fees_paid_by_int.into()).expect("expected to set token amount");
2974 }),
2975 None,
2976 Some(platform_version),
2977 );
2978
2979 basic_token_contract
2980 }
2981
2982 pub(in crate::execution) fn process_test_state_transition<S: PlatformSerializable>(
2983 platform: &mut TempPlatform<MockCoreRPCLike>,
2984 state_transition: S,
2985 platform_state: &PlatformState,
2986 platform_version: &PlatformVersion,
2987 ) -> StateTransitionsProcessingResult {
2988 let Ok(serialized_state_transition) = state_transition.serialize_to_bytes() else {
2989 panic!("expected documents batch serialized state transition")
2990 };
2991
2992 let transaction = platform.drive.grove.start_transaction();
2993
2994 let processing_result = platform
2995 .platform
2996 .process_raw_state_transitions(
2997 &[serialized_state_transition],
2998 platform_state,
2999 &BlockInfo::default(),
3000 &transaction,
3001 platform_version,
3002 false,
3003 None,
3004 )
3005 .expect("expected to process state transition");
3006
3007 platform
3008 .drive
3009 .grove
3010 .commit_transaction(transaction)
3011 .unwrap()
3012 .expect("expected to commit transaction");
3013
3014 processing_result
3015 }
3016
3017 mod keyword_search_contract {
3018 use dpp::consensus::basic::BasicError;
3019 use dpp::consensus::ConsensusError;
3020 use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult::PaidConsensusError;
3021 use super::*;
3022 fn build_random_doc_of_type(
3031 rng: &mut StdRng,
3032 doc_type_name: &str,
3033 identity_id: Identifier,
3034 contract: &DataContract,
3035 platform_version: &PlatformVersion,
3036 ) -> (Document, Bytes32) {
3037 let doc_type = contract
3038 .document_type_for_name(doc_type_name)
3039 .expect("doc type exists");
3040
3041 let entropy = Bytes32::random_with_rng(rng);
3042
3043 let doc = doc_type
3044 .random_document_with_identifier_and_entropy(
3045 rng,
3046 identity_id,
3047 entropy,
3048 DocumentFieldFillType::FillIfNotRequired,
3049 DocumentFieldFillSize::AnyDocumentFillSize,
3050 platform_version,
3051 )
3052 .expect("random doc");
3053
3054 (doc, entropy)
3055 }
3056
3057 #[tokio::test]
3058 async fn should_err_when_creating_contract_keywords_document() {
3059 let platform_version = PlatformVersion::latest();
3060
3061 let mut platform = TestPlatformBuilder::new()
3062 .build_with_mock_rpc()
3063 .set_genesis_state();
3064
3065 let (identity, signer, key) = setup_identity(&mut platform, 42, dash_to_credits!(1.0));
3066
3067 let contract = load_system_data_contract(
3068 SystemDataContract::KeywordSearch,
3069 PlatformVersion::latest(),
3070 )
3071 .expect("expected to load search contract");
3072
3073 let mut rng = StdRng::seed_from_u64(1);
3074 let (doc, entropy) = build_random_doc_of_type(
3075 &mut rng,
3076 "contractKeywords",
3077 identity.id(),
3078 &contract,
3079 platform_version,
3080 );
3081
3082 let transition = BatchTransition::new_document_creation_transition_from_document(
3083 doc,
3084 contract.document_type_for_name("contractKeywords").unwrap(),
3085 entropy.0, &key,
3087 1,
3088 0,
3089 None,
3090 &signer,
3091 platform_version,
3092 None,
3093 )
3094 .await
3095 .expect("batch transition");
3096
3097 let serialized = transition.serialize_to_bytes().unwrap();
3098
3099 let platform_state = platform.state.load();
3100 let processing_result = platform
3101 .platform
3102 .process_raw_state_transitions(
3103 &[serialized],
3104 &platform_state,
3105 &BlockInfo::default(),
3106 &platform.drive.grove.start_transaction(),
3107 platform_version,
3108 false,
3109 None,
3110 )
3111 .expect("processing failed");
3112
3113 let execution_result = processing_result.into_execution_results().remove(0);
3114 assert_matches!(
3115 execution_result,
3116 StateTransitionExecutionResult::PaidConsensusError{ error, .. }
3117 if error.to_string().contains("not allowed because of the document type's creation restriction mode")
3118 );
3119 }
3120
3121 #[tokio::test]
3122 async fn should_err_when_creating_short_description_document() {
3123 let platform_version = PlatformVersion::latest();
3124
3125 let mut platform = TestPlatformBuilder::new()
3126 .build_with_mock_rpc()
3127 .set_genesis_state();
3128
3129 let (identity, signer, key) = setup_identity(&mut platform, 43, dash_to_credits!(1.0));
3130
3131 let contract = load_system_data_contract(
3132 SystemDataContract::KeywordSearch,
3133 PlatformVersion::latest(),
3134 )
3135 .expect("expected to load search contract");
3136
3137 let mut rng = StdRng::seed_from_u64(2);
3138 let (doc, entropy) = build_random_doc_of_type(
3139 &mut rng,
3140 "shortDescription",
3141 identity.id(),
3142 &contract,
3143 platform_version,
3144 );
3145
3146 let transition = BatchTransition::new_document_creation_transition_from_document(
3147 doc,
3148 contract.document_type_for_name("shortDescription").unwrap(),
3149 entropy.0,
3150 &key,
3151 1,
3152 0,
3153 None,
3154 &signer,
3155 platform_version,
3156 None,
3157 )
3158 .await
3159 .expect("batch transition");
3160
3161 let serialized = transition.serialize_to_bytes().unwrap();
3162
3163 let platform_state = platform.state.load();
3164 let processing_result = platform
3165 .platform
3166 .process_raw_state_transitions(
3167 &[serialized],
3168 &platform_state,
3169 &BlockInfo::default(),
3170 &platform.drive.grove.start_transaction(),
3171 platform_version,
3172 false,
3173 None,
3174 )
3175 .expect("processing failed");
3176
3177 let execution_result = processing_result.into_execution_results().remove(0);
3178 assert_matches!(
3179 execution_result,
3180 StateTransitionExecutionResult::PaidConsensusError{ error, .. }
3181 if error.to_string().contains("not allowed because of the document type's creation restriction mode")
3182 );
3183 }
3184
3185 #[tokio::test]
3186 async fn should_err_when_creating_full_description_document() {
3187 let platform_version = PlatformVersion::latest();
3188
3189 let mut platform = TestPlatformBuilder::new()
3190 .build_with_mock_rpc()
3191 .set_genesis_state();
3192
3193 let (identity, signer, key) = setup_identity(&mut platform, 44, dash_to_credits!(1.0));
3194
3195 let contract = load_system_data_contract(
3196 SystemDataContract::KeywordSearch,
3197 PlatformVersion::latest(),
3198 )
3199 .expect("expected to load search contract");
3200
3201 let mut rng = StdRng::seed_from_u64(3);
3202 let (doc, entropy) = build_random_doc_of_type(
3203 &mut rng,
3204 "fullDescription",
3205 identity.id(),
3206 &contract,
3207 platform_version,
3208 );
3209
3210 let transition = BatchTransition::new_document_creation_transition_from_document(
3211 doc,
3212 contract.document_type_for_name("fullDescription").unwrap(),
3213 entropy.0,
3214 &key,
3215 1,
3216 0,
3217 None,
3218 &signer,
3219 platform_version,
3220 None,
3221 )
3222 .await
3223 .expect("batch transition");
3224
3225 let serialized = transition.serialize_to_bytes().unwrap();
3226
3227 let platform_state = platform.state.load();
3228 let processing_result = platform
3229 .platform
3230 .process_raw_state_transitions(
3231 &[serialized],
3232 &platform_state,
3233 &BlockInfo::default(),
3234 &platform.drive.grove.start_transaction(),
3235 platform_version,
3236 false,
3237 None,
3238 )
3239 .expect("processing failed");
3240
3241 let execution_result = processing_result.into_execution_results().remove(0);
3242 assert_matches!(
3243 execution_result,
3244 StateTransitionExecutionResult::PaidConsensusError{ error, .. }
3245 if error.to_string().contains("not allowed because of the document type's creation restriction mode")
3246 );
3247 }
3248
3249 async fn create_contract_with_keywords_and_description(
3256 platform: &mut TempPlatform<MockCoreRPCLike>,
3257 ) -> (Identity, SimpleSigner, IdentityPublicKey) {
3258 let platform_version = PlatformVersion::latest();
3259
3260 let (owner_identity, signer, key) =
3262 setup_identity(platform, 777, dash_to_credits!(1.0));
3263
3264 let mut contract = json_document_to_contract_with_ids(
3266 "tests/supporting_files/contract/keyword_test/keyword_base_contract.json",
3267 Some(owner_identity.id()),
3268 None,
3269 false,
3270 platform_version,
3271 )
3272 .expect("load contract");
3273
3274 contract.set_description(Some("A short description".to_string()));
3276 contract.set_keywords(vec!["graph".into(), "indexing".into()]);
3277
3278 let create_transition = DataContractCreateTransition::new_from_data_contract(
3280 contract,
3281 1,
3282 &owner_identity.clone().into_partial_identity_info(),
3283 key.id(),
3284 &signer,
3285 platform_version,
3286 None,
3287 )
3288 .await
3289 .expect("build transition");
3290
3291 let serialized = create_transition.serialize_to_bytes().unwrap();
3292 let platform_state = platform.state.load();
3293 let tx = platform.drive.grove.start_transaction();
3294
3295 let processing_result = platform
3296 .platform
3297 .process_raw_state_transitions(
3298 &[serialized],
3299 &platform_state,
3300 &BlockInfo::default(),
3301 &tx,
3302 platform_version,
3303 false,
3304 None,
3305 )
3306 .expect("process");
3307
3308 assert_matches!(
3309 processing_result.execution_results().as_slice(),
3310 [StateTransitionExecutionResult::SuccessfulExecution { .. }]
3311 );
3312
3313 platform
3314 .drive
3315 .grove
3316 .commit_transaction(tx)
3317 .unwrap()
3318 .expect("commit");
3319
3320 (owner_identity, signer, key)
3321 }
3322
3323 #[tokio::test]
3324 async fn owner_can_update_short_description_document() {
3325 let platform_version = PlatformVersion::latest();
3326 let mut platform = TestPlatformBuilder::new()
3327 .build_with_mock_rpc()
3328 .set_genesis_state();
3329
3330 let (_owner, signer, key) =
3331 create_contract_with_keywords_and_description(&mut platform).await;
3332
3333 let search_contract =
3335 load_system_data_contract(SystemDataContract::KeywordSearch, platform_version)
3336 .expect("load search contract");
3337
3338 let doc_type = search_contract
3339 .document_type_for_name("shortDescription")
3340 .unwrap();
3341
3342 let query = DriveDocumentQuery::all_items_query(&search_contract, doc_type, None);
3343 let existing_docs = platform
3344 .drive
3345 .query_documents(
3346 query,
3347 None,
3348 false,
3349 None,
3350 Some(platform_version.protocol_version),
3351 )
3352 .expect("query failed");
3353
3354 let mut doc = existing_docs.documents().first().expect("doc").clone();
3355 doc.set_revision(Some(doc.revision().unwrap_or_default() + 1));
3356 doc.set("description", "updated description".into());
3357
3358 let transition = BatchTransition::new_document_replacement_transition_from_document(
3359 doc,
3360 doc_type,
3361 &key,
3362 2,
3363 0,
3364 None,
3365 &signer,
3366 platform_version,
3367 None,
3368 )
3369 .await
3370 .expect("replace");
3371
3372 let serialized = transition.serialize_to_bytes().unwrap();
3373 let platform_state = platform.state.load();
3374
3375 let processing_result = platform
3376 .platform
3377 .process_raw_state_transitions(
3378 &[serialized],
3379 &platform_state,
3380 &BlockInfo::default(),
3381 &platform.drive.grove.start_transaction(),
3382 platform_version,
3383 false,
3384 None,
3385 )
3386 .expect("process");
3387
3388 assert_matches!(
3389 processing_result.into_execution_results().remove(0),
3390 SuccessfulExecution { .. }
3391 );
3392 }
3393
3394 #[tokio::test]
3395 async fn owner_can_not_delete_keyword_document() {
3396 let platform_version = PlatformVersion::latest();
3397 let mut platform = TestPlatformBuilder::new()
3398 .build_with_mock_rpc()
3399 .set_genesis_state();
3400
3401 let (_owner, signer, key) =
3402 create_contract_with_keywords_and_description(&mut platform).await;
3403
3404 let search_contract =
3405 load_system_data_contract(SystemDataContract::KeywordSearch, platform_version)
3406 .expect("load search contract");
3407 let doc_type = search_contract
3408 .document_type_for_name("contractKeywords")
3409 .unwrap();
3410
3411 let query = DriveDocumentQuery::all_items_query(&search_contract, doc_type, None);
3412 let existing_docs = platform
3413 .drive
3414 .query_documents(
3415 query,
3416 None,
3417 false,
3418 None,
3419 Some(platform_version.protocol_version),
3420 )
3421 .expect("query failed");
3422
3423 let doc = existing_docs.documents().first().unwrap().clone();
3424
3425 let transition = BatchTransition::new_document_deletion_transition_from_document(
3426 doc,
3427 doc_type,
3428 &key,
3429 2,
3430 0,
3431 None,
3432 &signer,
3433 platform_version,
3434 None,
3435 )
3436 .await
3437 .expect("delete");
3438
3439 let serialized = transition.serialize_to_bytes().unwrap();
3440 let platform_state = platform.state.load();
3441
3442 let processing_result = platform
3443 .platform
3444 .process_raw_state_transitions(
3445 &[serialized],
3446 &platform_state,
3447 &BlockInfo::default(),
3448 &platform.drive.grove.start_transaction(),
3449 platform_version,
3450 false,
3451 None,
3452 )
3453 .expect("process");
3454 assert_matches!(
3455 processing_result.execution_results().as_slice(),
3456 [PaidConsensusError {
3457 error: ConsensusError::BasicError(
3458 BasicError::InvalidDocumentTransitionActionError { .. }
3459 ),
3460 ..
3461 }]
3462 );
3463 }
3464 }
3465
3466 mod byte_array_encoding_flip_chain_halt {
3476 use super::*;
3477 use crate::test::helpers::fast_forward_to_block::fast_forward_to_block;
3478 use dpp::data_contract::document_type::schema::validate_schema_compatibility;
3479
3480 const CUSTOM_CONTESTED_CONTRACT: &str =
3481 "tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index.json";
3482 const CUSTOM_CONTESTED_CONTRACT_WIDENED: &str =
3485 "tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-byte-array-widened.json";
3486
3487 #[allow(clippy::too_many_arguments)]
3493 async fn build_preorder_and_domain(
3494 contract: &DataContract,
3495 identity: &Identity,
3496 signer: &SimpleSigner,
3497 key: &IdentityPublicKey,
3498 name: &str,
3499 salt_discriminator: u8,
3503 rng: &mut StdRng,
3504 platform_version: &PlatformVersion,
3505 ) -> (Vec<u8>, Vec<u8>) {
3506 let preorder = contract
3507 .document_type_for_name("preorder")
3508 .expect("expected preorder document type");
3509 let domain = contract
3510 .document_type_for_name("domain")
3511 .expect("expected domain document type");
3512
3513 let entropy = Bytes32::random_with_rng(rng);
3514
3515 let mut preorder_document = preorder
3516 .random_document_with_identifier_and_entropy(
3517 rng,
3518 identity.id(),
3519 entropy,
3520 DocumentFieldFillType::FillIfNotRequired,
3521 DocumentFieldFillSize::AnyDocumentFillSize,
3522 platform_version,
3523 )
3524 .expect("expected a random preorder document");
3525 preorder_document
3526 .set_id_for_creation(preorder, &entropy.0, 2, platform_version)
3527 .expect("expected to set the document id");
3528
3529 let mut domain_document = domain
3530 .random_document_with_identifier_and_entropy(
3531 rng,
3532 identity.id(),
3533 entropy,
3534 DocumentFieldFillType::FillIfNotRequired,
3535 DocumentFieldFillSize::AnyDocumentFillSize,
3536 platform_version,
3537 )
3538 .expect("expected a random domain document");
3539 domain_document
3540 .set_id_for_creation(domain, &entropy.0, 3, platform_version)
3541 .expect("expected to set the document id");
3542
3543 domain_document.set("parentDomainName", "dash".into());
3544 domain_document.set("normalizedParentDomainName", "dash".into());
3545 domain_document.set("label", name.into());
3546 domain_document.set(
3547 "normalizedLabel",
3548 convert_to_homograph_safe_chars(name).into(),
3549 );
3550 domain_document.set("records.identity", domain_document.owner_id().into());
3551 domain_document.set("subdomainRules.allowSubdomains", false.into());
3552
3553 let mut salt: [u8; 32] = [0xFF; 32];
3557 salt[31] = salt_discriminator;
3558
3559 let mut salted_domain_buffer: Vec<u8> = vec![];
3560 salted_domain_buffer.extend(salt);
3561 salted_domain_buffer
3562 .extend((convert_to_homograph_safe_chars(name) + ".dash").as_bytes());
3563 let salted_domain_hash = hash_double(salted_domain_buffer);
3564
3565 preorder_document.set("saltedDomainHash", salted_domain_hash.into());
3566 domain_document.set("preorderSalt", salt.into());
3567
3568 let preorder_transition =
3569 BatchTransition::new_document_creation_transition_from_document(
3570 preorder_document,
3571 preorder,
3572 entropy.0,
3573 key,
3574 2,
3575 0,
3576 None,
3577 signer,
3578 platform_version,
3579 None,
3580 )
3581 .await
3582 .expect("expect to create preorder batch transition");
3583
3584 let domain_transition =
3585 BatchTransition::new_document_creation_transition_from_document(
3586 domain_document,
3587 domain,
3588 entropy.0,
3589 key,
3590 3,
3591 0,
3592 None,
3593 signer,
3594 platform_version,
3595 None,
3596 )
3597 .await
3598 .expect("expect to create domain batch transition");
3599
3600 (
3601 preorder_transition
3602 .serialize_to_bytes()
3603 .expect("serialize preorder transition"),
3604 domain_transition
3605 .serialize_to_bytes()
3606 .expect("serialize domain transition"),
3607 )
3608 }
3609
3610 async fn run_contest_then_resolve(widen: bool) -> Result<(), crate::error::Error> {
3615 let mut platform = TestPlatformBuilder::new()
3616 .with_latest_protocol_version()
3617 .build_with_mock_rpc()
3618 .set_initial_state_structure();
3619
3620 let platform_version = PlatformVersion::latest();
3621
3622 let mut rng = StdRng::seed_from_u64(0xB17E_A77A);
3623
3624 let identity_1_info = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5));
3627 let identity_2_info = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5));
3628
3629 let contract_owner = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5));
3630
3631 let contract = setup_contract(
3635 &platform.drive,
3636 CUSTOM_CONTESTED_CONTRACT,
3637 None,
3638 Some(contract_owner.0.id().to_buffer()),
3639 None::<fn(&mut DataContract)>,
3640 None,
3641 Some(platform_version),
3642 );
3643
3644 let name = "quantum";
3645
3646 let platform_state = platform.state.load();
3647
3648 let (preorder_tx_1, domain_tx_1) = build_preorder_and_domain(
3651 &contract,
3652 &identity_1_info.0,
3653 &identity_1_info.1,
3654 &identity_1_info.2,
3655 name,
3656 0x01,
3657 &mut rng,
3658 platform_version,
3659 )
3660 .await;
3661
3662 let (preorder_tx_2, domain_tx_2) = build_preorder_and_domain(
3663 &contract,
3664 &identity_2_info.0,
3665 &identity_2_info.1,
3666 &identity_2_info.2,
3667 name,
3668 0x02,
3669 &mut rng,
3670 platform_version,
3671 )
3672 .await;
3673
3674 let transaction = platform.drive.grove.start_transaction();
3676 let processing_result = platform
3677 .platform
3678 .process_raw_state_transitions(
3679 &[preorder_tx_1, preorder_tx_2],
3680 &platform_state,
3681 &BlockInfo::default_with_time(
3682 platform_state
3683 .last_committed_block_time_ms()
3684 .unwrap_or_default()
3685 + 3000,
3686 ),
3687 &transaction,
3688 platform_version,
3689 false,
3690 None,
3691 )
3692 .expect("expected to process preorder state transitions");
3693 platform
3694 .drive
3695 .grove
3696 .commit_transaction(transaction)
3697 .unwrap()
3698 .expect("expected to commit transaction");
3699 assert_eq!(
3700 processing_result.valid_count(),
3701 2,
3702 "both preorders should be accepted"
3703 );
3704
3705 let transaction = platform.drive.grove.start_transaction();
3707 let processing_result = platform
3708 .platform
3709 .process_raw_state_transitions(
3710 &[domain_tx_1, domain_tx_2],
3711 &platform_state,
3712 &BlockInfo::default_with_time(
3713 platform_state
3714 .last_committed_block_time_ms()
3715 .unwrap_or_default()
3716 + 3000,
3717 ),
3718 &transaction,
3719 platform_version,
3720 false,
3721 None,
3722 )
3723 .expect("expected to process domain state transitions");
3724 platform
3725 .drive
3726 .grove
3727 .commit_transaction(transaction)
3728 .unwrap()
3729 .expect("expected to commit transaction");
3730 assert_eq!(
3731 processing_result.valid_count(),
3732 2,
3733 "both contenders should be accepted, opening the contest"
3734 );
3735
3736 if widen {
3739 let original_domain_schema = serde_json::json!({
3745 "type": "object",
3746 "properties": {
3747 "preorderSalt": {
3748 "type": "array",
3749 "byteArray": true,
3750 "minItems": 32,
3751 "maxItems": 32,
3752 "position": 4
3753 }
3754 },
3755 "additionalProperties": false
3756 });
3757 let widened_domain_schema = serde_json::json!({
3758 "type": "object",
3759 "properties": {
3760 "preorderSalt": {
3761 "type": "array",
3762 "byteArray": true,
3763 "minItems": 32,
3764 "maxItems": 64,
3765 "position": 4
3766 }
3767 },
3768 "additionalProperties": false
3769 });
3770 let compatibility = validate_schema_compatibility(
3771 &original_domain_schema,
3772 &widened_domain_schema,
3773 platform_version,
3774 )
3775 .expect("schema compatibility validation must not error");
3776 assert!(
3777 compatibility.is_valid(),
3778 "the maxItems 32 -> 64 widening must be accepted by update \
3779 validation for this attack to be reachable on chain; \
3780 reported incompatibilities: {:?}",
3781 compatibility.errors
3782 );
3783
3784 let widened_contract = setup_contract(
3793 &platform.drive,
3794 CUSTOM_CONTESTED_CONTRACT_WIDENED,
3795 None,
3796 Some(contract_owner.0.id().to_buffer()),
3797 None::<fn(&mut DataContract)>,
3798 None,
3799 Some(platform_version),
3800 );
3801 assert_eq!(
3802 widened_contract.id(),
3803 contract.id(),
3804 "the widened contract must replace the original at the same id"
3805 );
3806 }
3807
3808 let time_after_distribution_limit = platform_version
3810 .dpp
3811 .voting_versions
3812 .default_vote_poll_time_duration_test_network_ms
3813 + 10_000;
3814
3815 fast_forward_to_block(&platform, time_after_distribution_limit, 900, 42, 0, false);
3816
3817 let platform_state = platform.state.load();
3818 let transaction = platform.drive.grove.start_transaction();
3819
3820 platform.check_for_ended_vote_polls(
3825 &platform_state,
3826 &platform_state,
3827 &BlockInfo {
3828 time_ms: time_after_distribution_limit,
3829 height: 900,
3830 core_height: 42,
3831 epoch: Default::default(),
3832 },
3833 Some(&transaction),
3834 platform_version,
3835 )
3836 }
3837
3838 #[tokio::test]
3849 async fn widening_byte_array_max_items_halts_vote_poll_resolver() {
3850 let result = run_contest_then_resolve(true).await;
3851
3852 assert!(
3853 result.is_err(),
3854 "check_for_ended_vote_polls must return Err when stored contender \
3855 documents are decoded against a widened byteArray encoding -- the \
3856 chain-halt consequence the `validate_update` fix prevents by \
3857 rejecting the update at the source. Got Ok instead."
3858 );
3859
3860 assert_matches!(
3864 result.unwrap_err(),
3865 crate::error::Error::Protocol(dpp::ProtocolError::DataContractError(
3866 dpp::data_contract::errors::DataContractError::CorruptedSerialization(_)
3867 | dpp::data_contract::errors::DataContractError::DecodingContractError(_)
3868 ))
3869 );
3870 }
3871
3872 #[tokio::test]
3877 async fn contest_without_widening_resolves_successfully() {
3878 let result = run_contest_then_resolve(false).await;
3879
3880 assert!(
3881 result.is_ok(),
3882 "control: without the byteArray widening the vote-poll resolver \
3883 must succeed; got {:?}",
3884 result
3885 );
3886 }
3887 }
3888
3889 mod contested_index_mismatch_chain_halt {
3915 use super::*;
3916 use crate::config::PlatformConfig;
3917 use crate::test::helpers::fast_forward_to_block::fast_forward_to_block;
3918 use dpp::consensus::state::state_error::StateError;
3919 use dpp::consensus::ConsensusError;
3920 use dpp::dashcore::Network;
3921 use dpp::state_transition::batch_transition::document_base_transition::DocumentBaseTransition;
3922 use dpp::state_transition::batch_transition::document_create_transition::DocumentCreateTransitionV0;
3923 use dpp::state_transition::batch_transition::{
3924 BatchTransitionV0, DocumentCreateTransition,
3925 };
3926
3927 const CONTRACT_WITH_PREFIX_INDEX: &str =
3932 "tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-and-prefix-index.json";
3933
3934 async fn run_contest_then_resolve(
3941 prefunded_index: &str,
3942 ) -> (
3943 StateTransitionExecutionResult,
3944 Option<Result<(), crate::error::Error>>,
3945 ) {
3946 let platform_config = PlatformConfig {
3949 network: Network::Mainnet,
3950 ..Default::default()
3951 };
3952 let mut platform = TestPlatformBuilder::new()
3953 .with_latest_protocol_version()
3954 .with_config(platform_config)
3955 .build_with_mock_rpc()
3956 .set_initial_state_structure();
3957
3958 let platform_version = PlatformVersion::latest();
3959
3960 let mut rng = StdRng::seed_from_u64(0x0C01_7E57);
3961
3962 let (identity, signer, key) =
3963 setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5));
3964 let contract_owner = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5));
3965
3966 let contract = setup_contract(
3967 &platform.drive,
3968 CONTRACT_WITH_PREFIX_INDEX,
3969 None,
3970 Some(contract_owner.0.id().to_buffer()),
3971 None::<fn(&mut DataContract)>,
3972 None,
3973 Some(platform_version),
3974 );
3975
3976 let domain = contract
3977 .document_type_for_name("domain")
3978 .expect("expected a domain document type");
3979
3980 let entropy = Bytes32::random_with_rng(&mut rng);
3981
3982 let mut document = domain
3983 .random_document_with_identifier_and_entropy(
3984 &mut rng,
3985 identity.id(),
3986 entropy,
3987 DocumentFieldFillType::FillIfNotRequired,
3988 DocumentFieldFillSize::AnyDocumentFillSize,
3989 platform_version,
3990 )
3991 .expect("expected a random domain document");
3992
3993 document.set("parentDomainName", "dash".into());
3994 document.set("normalizedParentDomainName", "dash".into());
3995 document.set("label", "quantum".into());
3996 document.set("normalizedLabel", "quantum".into());
3997 document.set("records.identity", document.owner_id().into());
3998 document.set("subdomainRules.allowSubdomains", false.into());
3999 document.set("preorderSalt", rng.gen::<[u8; 32]>().into());
4000
4001 document
4002 .set_id_for_creation(domain, &entropy.0, 2, platform_version)
4003 .expect("expected to set the document id");
4004 let owner_id = document.owner_id();
4005 let create_transition: DocumentCreateTransition = DocumentCreateTransitionV0 {
4006 base: DocumentBaseTransition::from_document(
4007 &document,
4008 domain,
4009 None,
4010 2,
4011 platform_version,
4012 None,
4013 )
4014 .expect("expected a base transition"),
4015 entropy: entropy.0,
4016 data: document.clone().properties_consumed(),
4017 prefunded_voting_balance: Some((
4018 prefunded_index.to_string(),
4019 platform_version
4020 .fee_version
4021 .vote_resolution_fund_fees
4022 .contested_document_vote_resolution_fund_required_amount,
4023 )),
4024 }
4025 .into();
4026
4027 let inner_transition: BatchTransition = BatchTransitionV0 {
4028 owner_id,
4029 transitions: vec![create_transition.into()],
4030 user_fee_increase: 0,
4031 signature_public_key_id: 0,
4032 signature: Default::default(),
4033 }
4034 .into();
4035 let mut transition: StateTransition = inner_transition.into();
4036 transition
4037 .sign_external(&key, &signer, Some(|_, _| Ok(SecurityLevel::HIGH)))
4038 .await
4039 .expect("expected to sign");
4040
4041 let serialized_transition = transition
4042 .serialize_to_bytes()
4043 .expect("expected to serialize the transition");
4044
4045 let platform_state = platform.state.load();
4046 let transaction = platform.drive.grove.start_transaction();
4047
4048 let processing_result = platform
4049 .platform
4050 .process_raw_state_transitions(
4051 &[serialized_transition],
4052 &platform_state,
4053 &BlockInfo::default_with_time(3000),
4054 &transaction,
4055 platform_version,
4056 false,
4057 None,
4058 )
4059 .expect("expected to process the state transition");
4060
4061 platform
4062 .drive
4063 .grove
4064 .commit_transaction(transaction)
4065 .unwrap()
4066 .expect("expected to commit transaction");
4067
4068 let execution_result = processing_result.into_execution_results().remove(0);
4069
4070 if !matches!(
4071 execution_result,
4072 StateTransitionExecutionResult::SuccessfulExecution { .. }
4073 ) {
4074 return (execution_result, None);
4075 }
4076
4077 let after_vote_poll_end = platform_version
4078 .dpp
4079 .voting_versions
4080 .default_vote_poll_time_duration_mainnet_ms
4081 + 10_000;
4082
4083 fast_forward_to_block(&platform, after_vote_poll_end, 900, 42, 0, false);
4084
4085 let platform_state = platform.state.load();
4086 let transaction = platform.drive.grove.start_transaction();
4087
4088 let resolver_result = platform.check_for_ended_vote_polls(
4089 &platform_state,
4090 &platform_state,
4091 &BlockInfo {
4092 time_ms: after_vote_poll_end,
4093 height: 900,
4094 core_height: 42,
4095 epoch: Default::default(),
4096 },
4097 Some(&transaction),
4098 platform_version,
4099 );
4100
4101 (execution_result, Some(resolver_result))
4102 }
4103
4104 #[tokio::test]
4108 async fn prefunding_a_prefix_index_is_rejected() {
4109 let (execution_result, resolver_result) = run_contest_then_resolve("parentName").await;
4110
4111 assert_matches!(
4112 execution_result,
4113 StateTransitionExecutionResult::PaidConsensusError {
4114 error: ConsensusError::StateError(
4115 StateError::DocumentContestIndexMismatchError(_)
4116 ),
4117 ..
4118 },
4119 "a prefunded voting balance naming an index other than the contested one must be \
4120 rejected before it can register a vote poll"
4121 );
4122
4123 assert!(
4124 resolver_result.is_none(),
4125 "the transition must not have executed"
4126 );
4127 }
4128
4129 #[tokio::test]
4134 async fn prefunding_the_contested_index_resolves_successfully() {
4135 let (execution_result, resolver_result) =
4136 run_contest_then_resolve("parentNameAndLabel").await;
4137
4138 assert_matches!(
4139 execution_result,
4140 StateTransitionExecutionResult::SuccessfulExecution { .. }
4141 );
4142
4143 let resolver_result =
4144 resolver_result.expect("expected the vote poll resolver to have run");
4145
4146 assert!(
4147 resolver_result.is_ok(),
4148 "control: a contest whose vote poll names its own contested index must resolve; \
4149 got {:?}",
4150 resolver_result
4151 );
4152 }
4153 }
4154}