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