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