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