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