diff --git a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs index d36250ea3e..27a10ad10c 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs @@ -1,3 +1,4 @@ +use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::platform::Platform; use crate::platform_types::platform_state::v0::PlatformStateV0Methods; @@ -5,9 +6,13 @@ use crate::platform_types::platform_state::PlatformState; use dpp::block::block_info::BlockInfo; use dpp::dashcore::hashes::Hash; use dpp::data_contracts::SystemDataContract; +use dpp::fee::Credits; +use dpp::platform_value::Identifier; +use dpp::serialization::PlatformDeserializable; use dpp::system_data_contracts::load_system_data_contract; use dpp::version::PlatformVersion; use dpp::version::ProtocolVersion; +use dpp::voting::vote_polls::VotePoll; use drive::drive::identity::key::fetch::{ IdentityKeysRequest, KeyIDIdentityPublicKeyPairBTreeMap, KeyRequestType, }; @@ -15,7 +20,15 @@ use drive::drive::identity::withdrawals::paths::{ get_withdrawal_root_path, WITHDRAWAL_TRANSACTIONS_BROADCASTED_KEY, WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY, }; -use drive::grovedb::{Element, Transaction}; +use drive::drive::prefunded_specialized_balances::{ + prefunded_specialized_balances_for_voting_path, + prefunded_specialized_balances_for_voting_path_vec, +}; +use drive::drive::votes::paths::vote_end_date_queries_tree_path_vec; +use drive::grovedb::{Element, PathQuery, Query, QueryItem, SizedQuery, Transaction}; +use drive::query::QueryResultType; +use std::collections::HashSet; +use std::ops::RangeFull; impl Platform { /// Executes protocol-specific events on the first block after a protocol version change. @@ -58,6 +71,20 @@ impl Platform { self.transition_to_version_6(block_info, transaction, platform_version)?; } + if previous_protocol_version < 8 && platform_version.protocol_version >= 8 { + self.transition_to_version_8(block_info, transaction, platform_version) + .or_else(|e| { + tracing::error!( + error = ?e, + "Error while transitioning to version 8: {e}" + ); + + // We ignore this transition errors because it's not changing the state stucture + // and not critical for the system + Ok::<(), Error>(()) + })?; + } + Ok(()) } @@ -86,6 +113,131 @@ impl Platform { Ok(()) } + /// When transitioning to version 8 we need to empty some specialized balances + fn transition_to_version_8( + &self, + block_info: &BlockInfo, + transaction: &Transaction, + platform_version: &PlatformVersion, + ) -> Result<(), Error> { + // Let's start by getting all the specialized balances that exist + let path_holding_specialized_balances = + prefunded_specialized_balances_for_voting_path_vec(); + let path_query = PathQuery::new_single_query_item( + path_holding_specialized_balances, + QueryItem::RangeFull(RangeFull), + ); + let all_specialized_balances_still_around: HashSet<_> = self + .drive + .grove_get_path_query( + &path_query, + Some(transaction), + QueryResultType::QueryKeyElementPairResultType, + &mut vec![], + &platform_version.drive, + )? + .0 + .to_keys() + .into_iter() + .map(Identifier::try_from) + .collect::, dpp::platform_value::Error>>()?; + + let path = vote_end_date_queries_tree_path_vec(); + + let mut query = Query::new_with_direction(true); + + query.insert_all(); + + let mut sub_query = Query::new(); + + sub_query.insert_all(); + + query.default_subquery_branch.subquery = Some(sub_query.into()); + + let current_votes_path_query = PathQuery { + path, + query: SizedQuery { + query, + limit: Some(30000), //Just a high number that shouldn't break the system + offset: None, + }, + }; + + let (query_result_elements, _) = self.drive.grove_get_path_query( + ¤t_votes_path_query, + Some(transaction), + QueryResultType::QueryElementResultType, + &mut vec![], + &platform_version.drive, + )?; + + let active_specialized_balances = query_result_elements + .to_elements() + .into_iter() + .map(|element| { + let contested_document_resource_vote_poll_bytes = element + .into_item_bytes() + .map_err(drive::error::Error::GroveDB)?; + let vote_poll = + VotePoll::deserialize_from_bytes(&contested_document_resource_vote_poll_bytes)?; + match vote_poll { + VotePoll::ContestedDocumentResourceVotePoll(contested) => { + contested.specialized_balance_id().map_err(Error::Protocol) + } + } + }) + .collect::, Error>>()?; + + // let's get the non-active ones + let non_active_specialized_balances = + all_specialized_balances_still_around.difference(&active_specialized_balances); + + let mut total_credits_to_add_to_processing: Credits = 0; + + let mut operations = vec![]; + + for specialized_balance_id in non_active_specialized_balances { + let (credits, mut empty_specialized_balance_operation) = + self.drive.empty_prefunded_specialized_balance_operations( + *specialized_balance_id, + false, + &mut None, + Some(transaction), + platform_version, + )?; + operations.append(&mut empty_specialized_balance_operation); + total_credits_to_add_to_processing = total_credits_to_add_to_processing + .checked_add(credits) + .ok_or(Error::Execution(ExecutionError::Overflow( + "Credits from specialized balances are overflowing", + )))?; + } + + if total_credits_to_add_to_processing > 0 { + operations.push( + self.drive + .add_epoch_processing_credits_for_distribution_operation( + &block_info.epoch, + total_credits_to_add_to_processing, + Some(transaction), + platform_version, + )?, + ); + } + + if !operations.is_empty() { + self.drive.apply_batch_low_level_drive_operations( + None, + Some(transaction), + operations, + &mut vec![], + &platform_version.drive, + )?; + } + + Ok(()) + } + /// Initializes an empty sum tree for withdrawal transactions required for protocol version 4. /// /// This function is called during the transition to protocol version 4 to set up diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/v0/mod.rs index 426888de5d..a7175c4c7d 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/v0/mod.rs @@ -310,6 +310,7 @@ where // This means removing it and also removing all current votes if !vote_polls_with_info.is_empty() { self.clean_up_after_vote_polls_end( + block_info, &vote_polls_with_info, clean_up_testnet_corrupted_reference_issue, transaction, diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/mod.rs index 1c13190a5a..d3cdd55fa2 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/mod.rs @@ -2,6 +2,7 @@ use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::platform::Platform; use crate::rpc::core::CoreRPCLike; +use dpp::block::block_info::BlockInfo; use dpp::identifier::Identifier; use dpp::prelude::TimestampMillis; use dpp::version::PlatformVersion; @@ -11,6 +12,7 @@ use drive::grovedb::TransactionArg; use std::collections::BTreeMap; mod v0; +mod v1; impl Platform where @@ -19,6 +21,7 @@ where /// Checks for ended vote polls pub(in crate::execution) fn clean_up_after_contested_resources_vote_polls_end( &self, + block_info: &BlockInfo, vote_polls: Vec<( &ContestedDocumentResourceVotePollWithContractInfo, &TimestampMillis, @@ -40,9 +43,16 @@ where transaction, platform_version, ), + 1 => self.clean_up_after_contested_resources_vote_polls_end_v1( + block_info, + vote_polls, + clean_up_testnet_corrupted_reference_issue, + transaction, + platform_version, + ), version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "clean_up_after_contested_resources_vote_polls_end".to_string(), - known_versions: vec![0], + known_versions: vec![0, 1], received: version, })), } diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/v0/mod.rs index f444ed1e61..3dc1777acf 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/v0/mod.rs @@ -8,6 +8,7 @@ use dpp::version::PlatformVersion; use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; use dpp::ProtocolError; use drive::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfo; +use drive::fees::op::LowLevelDriveOperation; use drive::grovedb::TransactionArg; use std::collections::BTreeMap; @@ -28,12 +29,43 @@ where transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result<(), Error> { + let operations = self.clean_up_after_contested_resources_vote_polls_end_operations_v0( + vote_polls.as_slice(), + clean_up_testnet_corrupted_reference_issue, + transaction, + platform_version, + )?; + if !operations.is_empty() { + self.drive.apply_batch_low_level_drive_operations( + None, + transaction, + operations, + &mut vec![], + &platform_version.drive, + )?; + } + + Ok(()) + } + /// Checks for ended vote polls + #[inline(always)] + pub(super) fn clean_up_after_contested_resources_vote_polls_end_operations_v0( + &self, + vote_polls: &[( + &ContestedDocumentResourceVotePollWithContractInfo, + &TimestampMillis, + &BTreeMap>, + )], + clean_up_testnet_corrupted_reference_issue: bool, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { let mut operations = vec![]; // We remove the end date query self.drive .remove_contested_resource_vote_poll_end_date_query_operations( - vote_polls.as_slice(), + vote_polls, &mut operations, transaction, platform_version, @@ -42,7 +74,7 @@ where // We remove the votes from under the contenders votes received self.drive .remove_contested_resource_vote_poll_votes_operations( - vote_polls.as_slice(), + vote_polls, true, &mut operations, transaction, @@ -52,7 +84,7 @@ where // We remove the documents that contenders have self.drive .remove_contested_resource_vote_poll_documents_operations( - vote_polls.as_slice(), + vote_polls, clean_up_testnet_corrupted_reference_issue, &mut operations, transaction, @@ -62,7 +94,7 @@ where // We remove the contenders self.drive .remove_contested_resource_vote_poll_contenders_operations( - vote_polls.as_slice(), + vote_polls, &mut operations, transaction, platform_version, @@ -81,7 +113,7 @@ where let mut identity_to_vote_ids_map: BTreeMap<&Identifier, Vec<&Identifier>> = BTreeMap::new(); - for (vote_poll, _, voters_for_contender) in &vote_polls { + for (vote_poll, _, voters_for_contender) in vote_polls { let vote_id = vote_poll_ids .iter() .find_map(|(vp, vid)| if vp == vote_poll { Some(vid) } else { None }) @@ -113,7 +145,7 @@ where if clean_up_testnet_corrupted_reference_issue { self.drive.remove_contested_resource_info_operations( - vote_polls.as_slice(), + vote_polls, &mut operations, transaction, platform_version, @@ -121,23 +153,13 @@ where // We remove the last index self.drive .remove_contested_resource_top_level_index_operations( - vote_polls.as_slice(), + vote_polls, &mut operations, transaction, platform_version, )?; } - if !operations.is_empty() { - self.drive.apply_batch_low_level_drive_operations( - None, - transaction, - operations, - &mut vec![], - &platform_version.drive, - )?; - } - - Ok(()) + Ok(operations) } } diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/v1/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/v1/mod.rs new file mode 100644 index 0000000000..9fd22acf08 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_contested_resources_vote_polls_end/v1/mod.rs @@ -0,0 +1,88 @@ +use crate::error::execution::ExecutionError; +use crate::error::Error; +use crate::platform_types::platform::Platform; +use crate::rpc::core::CoreRPCLike; +use dpp::block::block_info::BlockInfo; +use dpp::fee::Credits; +use dpp::identifier::Identifier; +use dpp::prelude::TimestampMillis; +use dpp::version::PlatformVersion; +use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; +use drive::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfo; +use drive::grovedb::TransactionArg; +use std::collections::BTreeMap; + +impl Platform +where + C: CoreRPCLike, +{ + /// Checks for ended vote polls + #[inline(always)] + pub(super) fn clean_up_after_contested_resources_vote_polls_end_v1( + &self, + block_info: &BlockInfo, + vote_polls: Vec<( + &ContestedDocumentResourceVotePollWithContractInfo, + &TimestampMillis, + &BTreeMap>, + )>, + clean_up_testnet_corrupted_reference_issue: bool, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result<(), Error> { + let mut operations = self.clean_up_after_contested_resources_vote_polls_end_operations_v0( + vote_polls.as_slice(), + clean_up_testnet_corrupted_reference_issue, + transaction, + platform_version, + )?; + + // We also need to clean out the specialized balances + + let mut total_credits_to_add_to_processing: Credits = 0; + + for specialized_balance_id in vote_polls + .iter() + .map(|(vote_poll, _, _)| vote_poll.specialized_balance_id()) + { + let (credits, mut empty_specialized_balance_operation) = + self.drive.empty_prefunded_specialized_balance_operations( + specialized_balance_id?, + false, + &mut None, + transaction, + platform_version, + )?; + operations.append(&mut empty_specialized_balance_operation); + total_credits_to_add_to_processing = total_credits_to_add_to_processing + .checked_add(credits) + .ok_or(Error::Execution(ExecutionError::Overflow( + "Credits from specialized balances are overflowing", + )))?; + } + + if total_credits_to_add_to_processing > 0 { + operations.push( + self.drive + .add_epoch_processing_credits_for_distribution_operation( + &block_info.epoch, + total_credits_to_add_to_processing, + transaction, + platform_version, + )?, + ); + } + + if !operations.is_empty() { + self.drive.apply_batch_low_level_drive_operations( + None, + transaction, + operations, + &mut vec![], + &platform_version.drive, + )?; + } + + Ok(()) + } +} diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_vote_polls_end/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_vote_polls_end/mod.rs index 2852effd32..6580b03099 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_vote_polls_end/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_vote_polls_end/mod.rs @@ -2,6 +2,7 @@ use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::platform::Platform; use crate::rpc::core::CoreRPCLike; +use dpp::block::block_info::BlockInfo; use dpp::prelude::TimestampMillis; use dpp::version::PlatformVersion; use drive::drive::votes::resolved::vote_polls::ResolvedVotePollWithVotes; @@ -17,6 +18,7 @@ where /// Checks for ended vote polls pub(in crate::execution) fn clean_up_after_vote_polls_end( &self, + block_info: &BlockInfo, vote_polls: &BTreeMap>, clean_up_testnet_corrupted_reference_issue: bool, transaction: TransactionArg, @@ -29,6 +31,7 @@ where .clean_up_after_vote_poll_end { 0 => self.clean_up_after_vote_polls_end_v0( + block_info, vote_polls, clean_up_testnet_corrupted_reference_issue, transaction, diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_vote_polls_end/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_vote_polls_end/v0/mod.rs index a5b0d75a3b..81d8b84157 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_vote_polls_end/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/clean_up_after_vote_polls_end/v0/mod.rs @@ -1,6 +1,7 @@ use crate::error::Error; use crate::platform_types::platform::Platform; use crate::rpc::core::CoreRPCLike; +use dpp::block::block_info::BlockInfo; use dpp::identifier::Identifier; use dpp::prelude::TimestampMillis; use dpp::version::PlatformVersion; @@ -18,6 +19,7 @@ where #[inline(always)] pub(super) fn clean_up_after_vote_polls_end_v0( &self, + block_info: &BlockInfo, vote_polls: &BTreeMap>, clean_up_testnet_corrupted_reference_issue: bool, transaction: TransactionArg, @@ -44,6 +46,7 @@ where if !contested_polls.is_empty() { // Call the function to clean up contested document resource vote polls self.clean_up_after_contested_resources_vote_polls_end( + block_info, contested_polls, clean_up_testnet_corrupted_reference_issue, transaction, diff --git a/packages/rs-drive-abci/src/query/prefunded_specialized_balances/balance/v0/mod.rs b/packages/rs-drive-abci/src/query/prefunded_specialized_balances/balance/v0/mod.rs index fed648c116..410a902195 100644 --- a/packages/rs-drive-abci/src/query/prefunded_specialized_balances/balance/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/prefunded_specialized_balances/balance/v0/mod.rs @@ -50,7 +50,7 @@ impl Platform { let Some(balance) = maybe_balance else { return Ok(ValidationResult::new_with_error(QueryError::NotFound( - "No Identity found".to_string(), + "No Specialized balance found".to_string(), ))); }; diff --git a/packages/rs-drive-abci/tests/strategy_tests/upgrade_fork_tests.rs b/packages/rs-drive-abci/tests/strategy_tests/upgrade_fork_tests.rs index 554394956b..6cc875c18f 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/upgrade_fork_tests.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/upgrade_fork_tests.rs @@ -783,7 +783,7 @@ mod tests { chain_lock: ChainLockConfig::default_100_67(), instant_lock: InstantLockConfig::default_100_67(), execution: ExecutionConfig { - verify_sum_trees: true, + verify_sum_trees: false, //faster without this epoch_time_length_s: 1576800, ..Default::default() }, @@ -1369,7 +1369,7 @@ mod tests { chain_lock: ChainLockConfig::default_100_67(), instant_lock: InstantLockConfig::default_100_67(), execution: ExecutionConfig { - verify_sum_trees: true, + verify_sum_trees: false, epoch_time_length_s: 1576800, ..Default::default() }, diff --git a/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs b/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs index 83834520c0..3547f94c80 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use crate::execution::{continue_chain_for_strategy, run_chain_for_strategy}; - use crate::strategy::{ChainExecutionOutcome, ChainExecutionParameters, NetworkStrategy, StrategyRandomness}; + use crate::strategy::{ChainExecutionOutcome, ChainExecutionParameters, NetworkStrategy, StrategyRandomness, UpgradingInfo}; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::random_document::{ DocumentFieldFillSize, DocumentFieldFillType, @@ -9,7 +9,7 @@ mod tests { use dpp::identity::accessors::IdentityGettersV0; use dpp::identity::Identity; use dpp::platform_value::Value; - use drive_abci::config::{ChainLockConfig, ExecutionConfig, InstantLockConfig, PlatformConfig, PlatformTestConfig}; + use drive_abci::config::{ChainLockConfig, ExecutionConfig, InstantLockConfig, PlatformConfig, PlatformTestConfig, ValidatorSetConfig}; use drive_abci::test::helpers::setup::TestPlatformBuilder; use platform_version::version::PlatformVersion; use rand::prelude::StdRng; @@ -23,6 +23,7 @@ mod tests { use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::{get_contested_resource_vote_state_response_v0, GetContestedResourceVoteStateResponseV0}; use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::get_contested_resource_vote_state_response_v0::FinishedVoteInfo; use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::get_contested_resource_vote_state_response_v0::finished_vote_info::FinishedVoteOutcome; + use dpp::block::epoch::Epoch; use dpp::block::extended_block_info::v0::ExtendedBlockInfoV0Getters; use dpp::dash_to_duffs; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; @@ -1322,7 +1323,7 @@ mod tests { } #[test] - fn run_chain_with_voting_on_conflicting_index_distribution_after_won_by_identity() { + fn run_chain_with_voting_after_won_by_identity_no_specialized_funds_distribution() { // In this test we try to insert two state transitions with the same unique index // We use the DPNS contract, and we insert two documents both with the same "name" // This is a common scenario we should see quite often @@ -1341,9 +1342,10 @@ mod tests { }; let mut platform = TestPlatformBuilder::new() .with_config(config.clone()) + .with_initial_protocol_version(7) .build_with_mock_rpc(); - let platform_version = PlatformVersion::latest(); + let platform_version = PlatformVersion::get(7).unwrap(); let mut rng = StdRng::seed_from_u64(567); @@ -1708,5 +1710,1161 @@ mod tests { finished_at_epoch: 1 }) ); + + // not let's see how much is in processing pools + + let processing_fees = platform + .drive + .get_epoch_processing_credits_for_distribution( + &Epoch::new(1).unwrap(), + None, + platform_version, + ) + .expect("expected to get processing fees made in epoch"); + + // A vote costs 10_000_000 + // Hence we did 5 votes in this epoch + assert_eq!(processing_fees, 50_000_000); + } + + #[test] + fn run_chain_with_voting_after_won_by_identity_with_specialized_funds_distribution() { + // In this test we try to insert two state transitions with the same unique index + // We use the DPNS contract, and we insert two documents both with the same "name" + // This is a common scenario we should see quite often + let config = PlatformConfig { + testing_configs: PlatformTestConfig::default_minimal_verifications(), + chain_lock: ChainLockConfig::default_100_67(), + instant_lock: InstantLockConfig::default_100_67(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + + ..Default::default() + }, + block_spacing_ms: 3000, + ..Default::default() + }; + let mut platform = TestPlatformBuilder::new() + .with_config(config.clone()) + .build_with_mock_rpc(); + + let platform_version = PlatformVersion::latest(); + + let mut rng = StdRng::seed_from_u64(567); + + let mut simple_signer = SimpleSigner::default(); + + let (identity1, keys1) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys1); + + let (identity2, keys2) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys2); + + let start_identities: Vec<(Identity, Option)> = + create_state_transitions_for_identities( + vec![identity1, identity2], + &(dash_to_duffs!(1)..=dash_to_duffs!(1)), + &simple_signer, + &mut rng, + platform_version, + ) + .into_iter() + .map(|(identity, transition)| (identity, Some(transition))) + .collect(); + + let dpns_contract = platform + .drive + .cache + .system_data_contracts + .load_dpns() + .as_ref() + .clone(); + + let document_type = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type") + .to_owned_document_type(); + + let identity1_id = start_identities.first().unwrap().0.id(); + let identity2_id = start_identities.last().unwrap().0.id(); + let document_op_1 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([("identity", Value::from(identity1_id))]).into(), + ), + ]), + Some(start_identities.first().unwrap().0.id()), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let document_op_2 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([( + "identity", + Value::from(start_identities.last().unwrap().0.id()), + )]) + .into(), + ), + ]), + Some(start_identities.last().unwrap().0.id()), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let strategy = NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![ + Operation { + op_type: OperationType::Document(document_op_1), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + Operation { + op_type: OperationType::Document(document_op_2), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + ], + start_identities: StartIdentities { + hard_coded: start_identities, + ..Default::default() + }, + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: Some(simple_signer), + }, + total_hpmns: 100, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: None, + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }; + + let mut voting_signer = Some(SimpleSigner::default()); + + // On the first block we only have identities and contracts + let ChainExecutionOutcome { + abci_app, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + current_proposer_versions, + end_time_ms, + identity_nonce_counter, + identity_contract_nonce_counter, + state_transition_results_per_block, + .. + } = run_chain_for_strategy( + &mut platform, + 2, + strategy.clone(), + config.clone(), + 15, + &mut voting_signer, + ); + + let platform = abci_app.platform; + + let platform_state = platform.state.load(); + + let state_transitions_block_2 = state_transition_results_per_block + .get(&2) + .expect("expected to get block 2"); + + let first_document_insert_result = &state_transitions_block_2 + .first() + .as_ref() + .expect("expected a document insert") + .1; + assert_eq!(first_document_insert_result.code, 0); + + let second_document_insert_result = &state_transitions_block_2 + .get(1) + .as_ref() + .expect("expected a document insert") + .1; + + assert_eq!(second_document_insert_result.code, 0); // we expect the second to also be insertable as they are both contested + + let block_start = platform_state + .last_committed_block_info() + .as_ref() + .unwrap() + .basic_info() + .height + + 1; + let day_in_ms = 1000 * 60 * 60 * 24; + let config = PlatformConfig { + chain_lock: ChainLockConfig::default_100_67(), + instant_lock: InstantLockConfig::default_100_67(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + + ..Default::default() + }, + block_spacing_ms: day_in_ms, + ..Default::default() + }; + + let outcome = continue_chain_for_strategy( + abci_app, + ChainExecutionParameters { + block_start, + core_height_start: 1, + block_count: 16, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + current_proposer_versions: Some(current_proposer_versions.clone()), + current_identity_nonce_counter: identity_nonce_counter, + current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), + start_time_ms: 1681094380000, + current_time_ms: end_time_ms, + current_identities: Vec::new(), + }, + NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![Operation { + op_type: OperationType::ResourceVote(ResourceVoteOp { + resolved_vote_poll: ContestedDocumentResourceVotePollWithContractInfo { + contract: DataContractOwnedResolvedInfo::OwnedDataContract( + dpns_contract.clone(), + ), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: vec!["dash".into(), "quantum".into()], + }, + action: VoteAction { + vote_choices_with_weights: vec![ + (ResourceVoteChoice::Abstain, 1), + (ResourceVoteChoice::Lock, 1), + (ResourceVoteChoice::TowardsIdentity(identity1_id), 2), + (ResourceVoteChoice::TowardsIdentity(identity2_id), 10), + ], + }, + }), + frequency: Frequency { + times_per_block_range: 1..3, + chance_per_block: None, + }, + }], + start_identities: StartIdentities::default(), + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: voting_signer, + }, + total_hpmns: 100, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: None, + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }, + config.clone(), + StrategyRandomness::SeedEntropy(9), + ); + + let platform = outcome.abci_app.platform; + + // Now let's run a query for the vote totals + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config) + .expect("expected to encode the word dash"); + + let quantum_encoded = bincode::encode_to_vec(Value::Text("quantum".to_string()), config) + .expect("expected to encode the word quantum"); + + let index_name = "parentNameAndLabel".to_string(); + + let query_validation_result = platform + .query_contested_resource_vote_state( + GetContestedResourceVoteStateRequest { + version: Some(get_contested_resource_vote_state_request::Version::V0( + GetContestedResourceVoteStateRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: document_type.name().clone(), + index_name: index_name.clone(), + index_values: vec![dash_encoded.clone(), quantum_encoded.clone()], + result_type: ResultType::DocumentsAndVoteTally as i32, + allow_include_locked_and_abstaining_vote_tally: true, + start_at_identifier_info: None, + count: None, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resource_vote_state_response::Version::V0( + GetContestedResourceVoteStateResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some( + get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders( + get_contested_resource_vote_state_response_v0::ContestedResourceContenders { + contenders, + abstain_vote_tally, + lock_vote_tally, + finished_vote_info, + }, + ), + ) = result + else { + panic!("expected contenders") + }; + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert_eq!(first_contender.identifier, identity2_id.to_vec()); + + assert_eq!(second_contender.identifier, identity1_id.to_vec()); + + // All vote counts are weighted, so for evonodes, these are in multiples of 4 + + // 19 votes were cast + + assert_eq!(first_contender.vote_count, Some(60)); + + assert_eq!(second_contender.vote_count, Some(4)); + + assert_eq!(lock_vote_tally, Some(4)); + + assert_eq!(abstain_vote_tally, Some(8)); + + assert_eq!( + finished_vote_info, + Some(FinishedVoteInfo { + finished_vote_outcome: FinishedVoteOutcome::TowardsIdentity.into(), + won_by_identity_id: Some(identity2_id.to_vec()), + finished_at_block_height: 17, + finished_at_core_block_height: 1, + finished_at_block_time_ms: 1682303986000, + finished_at_epoch: 1 + }) + ); + + // not let's see how much is in processing pools + + let processing_fees = platform + .drive + .get_epoch_processing_credits_for_distribution( + &Epoch::new(1).unwrap(), + None, + platform_version, + ) + .expect("expected to get processing fees made in epoch"); + + // A vote costs 10_000_000 + // We did 5 votes in this epoch, + // We had 39_810_000_000 left over, which is only the cost of 19 votes + // So we basically have 39_810_000_000 + 50_000_000 + assert_eq!(processing_fees, 39_860_000_000); + } + + #[test] + fn run_chain_with_voting_after_won_by_identity_no_specialized_funds_distribution_until_version_8( + ) { + // In this test the goal is to verify that when we hit version 8 that the specialized balances + // that hadn't been properly distributed are distributed. + let config = PlatformConfig { + validator_set: ValidatorSetConfig { + quorum_size: 10, + ..Default::default() + }, + testing_configs: PlatformTestConfig::default_minimal_verifications(), + chain_lock: ChainLockConfig::default_100_67(), + instant_lock: InstantLockConfig::default_100_67(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + + ..Default::default() + }, + block_spacing_ms: 3000, + ..Default::default() + }; + let mut platform = TestPlatformBuilder::new() + .with_config(config.clone()) + .with_initial_protocol_version(7) + .build_with_mock_rpc(); + + let platform_version = PlatformVersion::get(7).unwrap(); + + let mut rng = StdRng::seed_from_u64(567); + + let mut simple_signer = SimpleSigner::default(); + + let (identity1, keys1) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys1); + + let (identity2, keys2) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys2); + + let start_identities: Vec<(Identity, Option)> = + create_state_transitions_for_identities( + vec![identity1, identity2], + &(dash_to_duffs!(1)..=dash_to_duffs!(1)), + &simple_signer, + &mut rng, + platform_version, + ) + .into_iter() + .map(|(identity, transition)| (identity, Some(transition))) + .collect(); + + let dpns_contract = platform + .drive + .cache + .system_data_contracts + .load_dpns() + .as_ref() + .clone(); + + let document_type = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type") + .to_owned_document_type(); + + let identity1_id = start_identities.first().unwrap().0.id(); + let identity2_id = start_identities.last().unwrap().0.id(); + let document_op_1 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([("identity", Value::from(identity1_id))]).into(), + ), + ]), + Some(identity1_id), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let document_op_2 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([( + "identity", + Value::from(start_identities.last().unwrap().0.id()), + )]) + .into(), + ), + ]), + Some(identity2_id), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let strategy = NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![ + Operation { + op_type: OperationType::Document(document_op_1), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + Operation { + op_type: OperationType::Document(document_op_2), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + ], + start_identities: StartIdentities { + hard_coded: start_identities, + ..Default::default() + }, + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: Some(simple_signer.clone()), + }, + total_hpmns: 20, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: Some(UpgradingInfo { + current_protocol_version: 7, + proposed_protocol_versions_with_weight: vec![(7, 1)], + upgrade_three_quarters_life: 0.2, + }), + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }; + + let mut voting_signer = Some(SimpleSigner::default()); + + // On the first block we only have identities and contracts + let ChainExecutionOutcome { + abci_app, + identities, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + current_proposer_versions, + end_time_ms, + identity_nonce_counter, + identity_contract_nonce_counter, + state_transition_results_per_block, + .. + } = run_chain_for_strategy( + &mut platform, + 2, + strategy.clone(), + config.clone(), + 15, + &mut voting_signer, + ); + + let platform = abci_app.platform; + + let platform_state = platform.state.load(); + + let state_transitions_block_2 = state_transition_results_per_block + .get(&2) + .expect("expected to get block 2"); + + let first_document_insert_result = &state_transitions_block_2 + .first() + .as_ref() + .expect("expected a document insert") + .1; + assert_eq!(first_document_insert_result.code, 0); + + let second_document_insert_result = &state_transitions_block_2 + .get(1) + .as_ref() + .expect("expected a document insert") + .1; + + assert_eq!(second_document_insert_result.code, 0); // we expect the second to also be insertable as they are both contested + + let block_start = platform_state + .last_committed_block_info() + .as_ref() + .unwrap() + .basic_info() + .height + + 1; + let day_in_ms = 1000 * 60 * 60 * 24; + let config = PlatformConfig { + chain_lock: ChainLockConfig::default_100_67(), + instant_lock: InstantLockConfig::default_100_67(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + + ..Default::default() + }, + block_spacing_ms: day_in_ms, + ..Default::default() + }; + + // On the first block we only have identities and contracts + let ChainExecutionOutcome { + abci_app, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + end_time_ms, + identity_nonce_counter, + identity_contract_nonce_counter, + .. + } = continue_chain_for_strategy( + abci_app, + ChainExecutionParameters { + block_start, + core_height_start: 1, + block_count: 16, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + current_proposer_versions: Some(current_proposer_versions.clone()), + current_identity_nonce_counter: identity_nonce_counter, + current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), + start_time_ms: 1681094380000, + current_time_ms: end_time_ms, + current_identities: Vec::new(), + }, + NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![Operation { + op_type: OperationType::ResourceVote(ResourceVoteOp { + resolved_vote_poll: ContestedDocumentResourceVotePollWithContractInfo { + contract: DataContractOwnedResolvedInfo::OwnedDataContract( + dpns_contract.clone(), + ), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: vec!["dash".into(), "quantum".into()], + }, + action: VoteAction { + vote_choices_with_weights: vec![ + (ResourceVoteChoice::Abstain, 1), + (ResourceVoteChoice::Lock, 1), + (ResourceVoteChoice::TowardsIdentity(identity1_id), 2), + (ResourceVoteChoice::TowardsIdentity(identity2_id), 10), + ], + }, + }), + frequency: Frequency { + times_per_block_range: 1..3, + chance_per_block: None, + }, + }], + start_identities: StartIdentities::default(), + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: voting_signer, + }, + total_hpmns: 20, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: Some(UpgradingInfo { + current_protocol_version: 7, + proposed_protocol_versions_with_weight: vec![(7, 1)], + upgrade_three_quarters_life: 0.2, + }), + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }, + config.clone(), + StrategyRandomness::SeedEntropy(9), + ); + + let platform = abci_app.platform; + + // Now let's run a query for the vote totals + + let bincode_config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), bincode_config) + .expect("expected to encode the word dash"); + + let quantum_encoded = + bincode::encode_to_vec(Value::Text("quantum".to_string()), bincode_config) + .expect("expected to encode the word quantum"); + + let index_name = "parentNameAndLabel".to_string(); + + let query_validation_result = platform + .query_contested_resource_vote_state( + GetContestedResourceVoteStateRequest { + version: Some(get_contested_resource_vote_state_request::Version::V0( + GetContestedResourceVoteStateRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: document_type.name().clone(), + index_name: index_name.clone(), + index_values: vec![dash_encoded.clone(), quantum_encoded.clone()], + result_type: ResultType::DocumentsAndVoteTally as i32, + allow_include_locked_and_abstaining_vote_tally: true, + start_at_identifier_info: None, + count: None, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resource_vote_state_response::Version::V0( + GetContestedResourceVoteStateResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some( + get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders( + get_contested_resource_vote_state_response_v0::ContestedResourceContenders { + contenders, + abstain_vote_tally, + lock_vote_tally, + finished_vote_info, + }, + ), + ) = result + else { + panic!("expected contenders") + }; + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert_eq!(first_contender.identifier, identity2_id.to_vec()); + + assert_eq!(second_contender.identifier, identity1_id.to_vec()); + + // All vote counts are weighted, so for evonodes, these are in multiples of 4 + + assert_eq!( + ( + first_contender.vote_count, + second_contender.vote_count, + lock_vote_tally, + abstain_vote_tally + ), + (Some(64), Some(8), Some(0), Some(0)) + ); + + assert_eq!( + finished_vote_info, + Some(FinishedVoteInfo { + finished_vote_outcome: FinishedVoteOutcome::TowardsIdentity.into(), + won_by_identity_id: Some(identity2_id.to_vec()), + finished_at_block_height: 17, + finished_at_core_block_height: 1, + finished_at_block_time_ms: 1682303986000, + finished_at_epoch: 1 + }) + ); + + // not let's see how much is in processing pools + + let processing_fees = platform + .drive + .get_epoch_processing_credits_for_distribution( + &Epoch::new(1).unwrap(), + None, + platform_version, + ) + .expect("expected to get processing fees made in epoch"); + + // A vote costs 10_000_000 + // Hence we did 4 votes in this epoch + assert_eq!(processing_fees, 40_000_000); + + // Now let's upgrade to version 8 + + let platform = abci_app.platform; + + let platform_state = platform.state.load(); + + let block_start = platform_state + .last_committed_block_info() + .as_ref() + .unwrap() + .basic_info() + .height + + 1; + + let ten_hours_in_ms = 1000 * 60 * 60 * 10; + let config = PlatformConfig { + chain_lock: ChainLockConfig::default_100_67(), + instant_lock: InstantLockConfig::default_100_67(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + + ..Default::default() + }, + block_spacing_ms: ten_hours_in_ms, + ..Default::default() + }; + + // We go 45 blocks later + let ChainExecutionOutcome { + abci_app, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + end_time_ms, + identity_nonce_counter, + identity_contract_nonce_counter, + .. + } = continue_chain_for_strategy( + abci_app, + ChainExecutionParameters { + block_start, + core_height_start: 1, + block_count: 45, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + current_proposer_versions: None, + current_identity_nonce_counter: identity_nonce_counter, + current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), + start_time_ms: 1681094380000, + current_time_ms: end_time_ms, + current_identities: Vec::new(), + }, + NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![], + start_identities: StartIdentities::default(), + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: None, + }, + total_hpmns: 20, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: Some(UpgradingInfo { + current_protocol_version: 8, + proposed_protocol_versions_with_weight: vec![(8, 1)], + upgrade_three_quarters_life: 0.1, + }), + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: false, + ..Default::default() + }, + config.clone(), + StrategyRandomness::SeedEntropy(9203), + ); + + let platform = abci_app.platform; + + let platform_state = platform.state.load(); + + let mut block_start = platform_state + .last_committed_block_info() + .as_ref() + .unwrap() + .basic_info() + .height + + 1; + + // We need to create a few more contests + + let document_op_1 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "sam".into()), + ("normalizedLabel".into(), "sam".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ("parentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([("identity", Value::from(identity1_id))]).into(), + ), + ]), + Some(identity1_id), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let document_op_2 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "sam".into()), + ("normalizedLabel".into(), "sam".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ("parentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([("identity", Value::from(identity2_id))]).into(), + ), + ]), + Some(identity2_id), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let ChainExecutionOutcome { + abci_app, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + end_time_ms, + identity_nonce_counter, + identity_contract_nonce_counter, + .. + } = continue_chain_for_strategy( + abci_app, + ChainExecutionParameters { + block_start, + core_height_start: 1, + block_count: 1, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + current_proposer_versions: None, + current_identity_nonce_counter: identity_nonce_counter, + current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), + start_time_ms: 1681094380000, + current_time_ms: end_time_ms, + current_identities: identities, + }, + NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![ + Operation { + op_type: OperationType::Document(document_op_1), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + Operation { + op_type: OperationType::Document(document_op_2), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + ], + start_identities: StartIdentities::default(), + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: Some(simple_signer), + }, + total_hpmns: 20, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: Some(UpgradingInfo { + current_protocol_version: 8, + proposed_protocol_versions_with_weight: vec![(8, 1)], + upgrade_three_quarters_life: 0.1, + }), + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: false, + ..Default::default() + }, + config.clone(), + StrategyRandomness::SeedEntropy(9203), + ); + + block_start += 1; + + // We go 14 blocks later till version 8 is active + let outcome = continue_chain_for_strategy( + abci_app, + ChainExecutionParameters { + block_start, + core_height_start: 1, + block_count: 14, + proposers, + validator_quorums, + current_validator_quorum_hash, + instant_lock_quorums, + current_proposer_versions: None, + current_identity_nonce_counter: identity_nonce_counter, + current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), + start_time_ms: 1681094380000, + current_time_ms: end_time_ms, + current_identities: Vec::new(), + }, + NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![], + start_identities: StartIdentities::default(), + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: None, + }, + total_hpmns: 20, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: Some(UpgradingInfo { + current_protocol_version: 8, + proposed_protocol_versions_with_weight: vec![(8, 1)], + upgrade_three_quarters_life: 0.1, + }), + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: false, + ..Default::default() + }, + config.clone(), + StrategyRandomness::SeedEntropy(9203), + ); + + let platform = outcome.abci_app.platform; + platform + .drive + .fetch_versions_with_counter(None, &platform_version.drive) + .expect("expected to get versions"); + + let state = platform.state.load(); + assert_eq!( + state + .last_committed_block_info() + .as_ref() + .unwrap() + .basic_info() + .epoch + .index, + 4 + ); + assert_eq!(state.current_protocol_version_in_consensus(), 8); + + let processing_fees = platform + .drive + .get_epoch_processing_credits_for_distribution( + &Epoch::new(4).unwrap(), + None, + platform_version, + ) + .expect("expected to get processing fees made in epoch"); + + // A vote costs 10_000_000 + // There were 23 votes total so that means that there would have been 39_780_000_000 left over + // We see that there is 39_780_000_000 to distribute + assert_eq!(processing_fees, 39_780_000_000); } } diff --git a/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/add_epoch_processing_credits_for_distribution_operation/mod.rs b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/add_epoch_processing_credits_for_distribution_operation/mod.rs new file mode 100644 index 0000000000..51d7d32587 --- /dev/null +++ b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/add_epoch_processing_credits_for_distribution_operation/mod.rs @@ -0,0 +1,56 @@ +mod v0; + +use grovedb::TransactionArg; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; + +use dpp::block::epoch::Epoch; +use dpp::fee::Credits; + +use crate::fees::op::LowLevelDriveOperation; +use dpp::version::PlatformVersion; + +impl Drive { + /// Adds to the amount of processing fees to be distributed for the Epoch. + /// + /// # Arguments + /// + /// * `epoch_tree` - A reference to the Epoch. + /// * `amount` - The amount to add. + /// * `transaction` - A TransactionArg instance. + /// * `platform_version` - A PlatformVersion instance representing the version of the drive. + /// + /// # Returns + /// + /// A Result containing either the processing fee for the epoch, if found, + /// or an Error if something goes wrong. + pub fn add_epoch_processing_credits_for_distribution_operation( + &self, + epoch: &Epoch, + amount: Credits, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + match platform_version + .drive + .methods + .credit_pools + .epochs + .add_epoch_processing_credits_for_distribution_operation + { + 0 => self.add_epoch_processing_credits_for_distribution_operation_v0( + epoch, + amount, + transaction, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "add_epoch_processing_credits_for_distribution_operation".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/add_epoch_processing_credits_for_distribution_operation/v0/mod.rs b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/add_epoch_processing_credits_for_distribution_operation/v0/mod.rs new file mode 100644 index 0000000000..f163236b47 --- /dev/null +++ b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/add_epoch_processing_credits_for_distribution_operation/v0/mod.rs @@ -0,0 +1,60 @@ +use grovedb::{Element, TransactionArg}; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; + +use crate::drive::credit_pools::epochs::epoch_key_constants; +use crate::drive::credit_pools::epochs::paths::EpochProposers; +use crate::fees::op::LowLevelDriveOperation; +use crate::util::grove_operations::DirectQueryType; +use dpp::block::epoch::Epoch; +use dpp::fee::Credits; +use dpp::ProtocolError; +use platform_version::version::PlatformVersion; + +impl Drive { + /// Gets the amount of processing fees to be distributed for the Epoch and adds to it. + pub(super) fn add_epoch_processing_credits_for_distribution_operation_v0( + &self, + epoch: &Epoch, + amount: Credits, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + let epoch_tree_path = epoch.get_path(); + let element = self.grove_get_raw_optional( + (&epoch_tree_path).into(), + epoch_key_constants::KEY_POOL_PROCESSING_FEES.as_slice(), + DirectQueryType::StatefulDirectQuery, + transaction, + &mut vec![], + &platform_version.drive, + )?; + + let existing_value = match element { + None => 0, + Some(Element::SumItem(existing_value, _)) => existing_value, + _ => { + return Err(Error::Drive(DriveError::UnexpectedElementType( + "epochs processing fee must be an item", + ))) + } + }; + + if amount > i64::MAX as u64 { + return Err(Error::Protocol(ProtocolError::Overflow( + "adding over i64::Max to processing fee pool", + ))); + } + + let updated_value = existing_value + .checked_add(amount as i64) + .ok_or(ProtocolError::Overflow("overflow when adding to sum item"))?; + Ok(LowLevelDriveOperation::insert_for_known_path_key_element( + epoch_tree_path.iter().map(|a| a.to_vec()).collect(), + epoch_key_constants::KEY_POOL_PROCESSING_FEES.to_vec(), + Element::new_sum_item(updated_value), + )) + } +} diff --git a/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/get_epoch_processing_credits_for_distribution/mod.rs b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/get_epoch_processing_credits_for_distribution/mod.rs index 8e790b846b..db40e29f00 100644 --- a/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/get_epoch_processing_credits_for_distribution/mod.rs +++ b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/get_epoch_processing_credits_for_distribution/mod.rs @@ -26,7 +26,7 @@ impl Drive { /// or an Error if something goes wrong. pub fn get_epoch_processing_credits_for_distribution( &self, - epoch_tree: &Epoch, + epoch: &Epoch, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result { @@ -38,7 +38,7 @@ impl Drive { .get_epoch_processing_credits_for_distribution { 0 => self.get_epoch_processing_credits_for_distribution_v0( - epoch_tree, + epoch, transaction, platform_version, ), diff --git a/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/get_epoch_processing_credits_for_distribution/v0/mod.rs b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/get_epoch_processing_credits_for_distribution/v0/mod.rs index 05782b23b2..ae0265d393 100644 --- a/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/get_epoch_processing_credits_for_distribution/v0/mod.rs +++ b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/get_epoch_processing_credits_for_distribution/v0/mod.rs @@ -15,14 +15,14 @@ impl Drive { /// Gets the amount of processing fees to be distributed for the Epoch. pub(super) fn get_epoch_processing_credits_for_distribution_v0( &self, - epoch_tree: &Epoch, + epoch: &Epoch, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result { let element = self .grove .get( - &epoch_tree.get_path(), + &epoch.get_path(), epoch_key_constants::KEY_POOL_PROCESSING_FEES.as_slice(), transaction, &platform_version.drive.grove_version, diff --git a/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/mod.rs b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/mod.rs index f0a061ce02..2096f12906 100644 --- a/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/mod.rs +++ b/packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/mod.rs @@ -3,6 +3,7 @@ //! This module implements functions in Drive to distribute fees for a given Epoch. //! +mod add_epoch_processing_credits_for_distribution_operation; mod get_epoch_fee_multiplier; mod get_epoch_processing_credits_for_distribution; mod get_epoch_storage_credits_for_distribution; diff --git a/packages/rs-drive/src/drive/mod.rs b/packages/rs-drive/src/drive/mod.rs index fc28c49647..e54d5445a2 100644 --- a/packages/rs-drive/src/drive/mod.rs +++ b/packages/rs-drive/src/drive/mod.rs @@ -42,7 +42,10 @@ pub mod system; mod asset_lock; #[cfg(feature = "server")] mod platform_state; -pub(crate) mod prefunded_specialized_balances; + +/// Prefunded specialized balances module +#[cfg(any(feature = "server", feature = "verify"))] +pub mod prefunded_specialized_balances; /// Vote module #[cfg(any(feature = "server", feature = "verify"))] diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/add_prefunded_specialized_balance_operations/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/add_prefunded_specialized_balance_operations/mod.rs index 18fd27d103..4a02d9e9c6 100644 --- a/packages/rs-drive/src/drive/prefunded_specialized_balances/add_prefunded_specialized_balance_operations/mod.rs +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/add_prefunded_specialized_balance_operations/mod.rs @@ -1,4 +1,5 @@ mod v0; +mod v1; use crate::drive::Drive; use crate::error::drive::DriveError; @@ -50,9 +51,16 @@ impl Drive { transaction, platform_version, ), + 1 => self.add_prefunded_specialized_balance_operations_v1( + specialized_balance_id, + amount, + estimated_costs_only_with_layer_info, + transaction, + platform_version, + ), version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: "add_prefunded_specialized_balance_operations".to_string(), - known_versions: vec![0], + known_versions: vec![0, 1], received: version, })), } diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/add_prefunded_specialized_balance_operations/v1/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/add_prefunded_specialized_balance_operations/v1/mod.rs new file mode 100644 index 0000000000..6a46264949 --- /dev/null +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/add_prefunded_specialized_balance_operations/v1/mod.rs @@ -0,0 +1,91 @@ +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use crate::fees::op::LowLevelDriveOperation::GroveOperation; +use crate::util::grove_operations::DirectQueryType; + +use crate::drive::prefunded_specialized_balances::{ + prefunded_specialized_balances_for_voting_path, + prefunded_specialized_balances_for_voting_path_vec, +}; +use crate::error::identity::IdentityError; +use crate::util::grove_operations::QueryTarget::QueryTargetValue; +use dpp::balances::credits::MAX_CREDITS; +use dpp::identifier::Identifier; +use dpp::version::PlatformVersion; +use grovedb::batch::{KeyInfoPath, QualifiedGroveDbOp}; +use grovedb::{Element, EstimatedLayerInformation, TransactionArg}; +use std::collections::HashMap; + +impl Drive { + /// The operations to add to the specialized balance + #[inline(always)] + pub(super) fn add_prefunded_specialized_balance_operations_v1( + &self, + specialized_balance_id: Identifier, + amount: u64, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let mut drive_operations = vec![]; + if let Some(estimated_costs_only_with_layer_info) = estimated_costs_only_with_layer_info { + Self::add_estimation_costs_for_prefunded_specialized_balance_update( + estimated_costs_only_with_layer_info, + &platform_version.drive, + )?; + } + + let direct_query_type = if estimated_costs_only_with_layer_info.is_none() { + DirectQueryType::StatefulDirectQuery + } else { + DirectQueryType::StatelessDirectQuery { + in_tree_using_sums: true, + query_target: QueryTargetValue(8), + } + }; + + let path_holding_specialized_balances = prefunded_specialized_balances_for_voting_path(); + let previous_credits_in_specialized_balance = self + .grove_get_raw_value_u64_from_encoded_var_vec( + (&path_holding_specialized_balances).into(), + specialized_balance_id.as_slice(), + direct_query_type, + transaction, + &mut drive_operations, + &platform_version.drive, + )?; + let had_previous_balance = previous_credits_in_specialized_balance.is_some(); + let new_total = previous_credits_in_specialized_balance + .unwrap_or_default() + .checked_add(amount) + .ok_or(Error::Drive(DriveError::CriticalCorruptedState( + "trying to add an amount that would overflow credits", + )))?; + // while i64::MAX could potentially work, best to avoid it. + if new_total >= MAX_CREDITS { + return Err(Error::Identity(IdentityError::CriticalBalanceOverflow( + "trying to set prefunded specialized balance to over max credits amount (i64::MAX)", + ))); + }; + let path_holding_total_credits_vec = prefunded_specialized_balances_for_voting_path_vec(); + let op = if had_previous_balance { + QualifiedGroveDbOp::replace_op( + path_holding_total_credits_vec, + specialized_balance_id.to_vec(), + Element::new_sum_item(new_total as i64), + ) + } else { + QualifiedGroveDbOp::insert_or_replace_op( + path_holding_total_credits_vec, + specialized_balance_id.to_vec(), + Element::new_sum_item(new_total as i64), + ) + }; + drive_operations.push(GroveOperation(op)); + Ok(drive_operations) + } +} diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/deduct_from_prefunded_specialized_balance_operations/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/deduct_from_prefunded_specialized_balance_operations/mod.rs index d294ec9087..aef16a0cb5 100644 --- a/packages/rs-drive/src/drive/prefunded_specialized_balances/deduct_from_prefunded_specialized_balance_operations/mod.rs +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/deduct_from_prefunded_specialized_balance_operations/mod.rs @@ -1,4 +1,5 @@ mod v0; +mod v1; use crate::drive::Drive; use crate::error::drive::DriveError; @@ -50,6 +51,13 @@ impl Drive { transaction, platform_version, ), + 1 => self.deduct_from_prefunded_specialized_balance_operations_v1( + specialized_balance_id, + amount, + estimated_costs_only_with_layer_info, + transaction, + platform_version, + ), version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: "deduct_from_prefunded_specialized_balance_operations".to_string(), known_versions: vec![0], diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/deduct_from_prefunded_specialized_balance_operations/v1/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/deduct_from_prefunded_specialized_balance_operations/v1/mod.rs new file mode 100644 index 0000000000..4bf03c848f --- /dev/null +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/deduct_from_prefunded_specialized_balance_operations/v1/mod.rs @@ -0,0 +1,91 @@ +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use crate::fees::op::LowLevelDriveOperation::GroveOperation; +use crate::util::grove_operations::DirectQueryType; + +use crate::drive::prefunded_specialized_balances::{ + prefunded_specialized_balances_for_voting_path, + prefunded_specialized_balances_for_voting_path_vec, +}; +use crate::util::grove_operations::QueryTarget::QueryTargetValue; +use dpp::identifier::Identifier; +use dpp::version::PlatformVersion; +use grovedb::batch::{KeyInfoPath, QualifiedGroveDbOp}; +use grovedb::{Element, EstimatedLayerInformation, TransactionArg}; +use std::collections::HashMap; + +impl Drive { + /// The operations to add to the specialized balance + #[inline(always)] + pub(super) fn deduct_from_prefunded_specialized_balance_operations_v1( + &self, + specialized_balance_id: Identifier, + amount: u64, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let mut drive_operations = vec![]; + if let Some(estimated_costs_only_with_layer_info) = estimated_costs_only_with_layer_info { + Self::add_estimation_costs_for_prefunded_specialized_balance_update( + estimated_costs_only_with_layer_info, + &platform_version.drive, + )?; + } + + let direct_query_type = if estimated_costs_only_with_layer_info.is_none() { + DirectQueryType::StatefulDirectQuery + } else { + DirectQueryType::StatelessDirectQuery { + in_tree_using_sums: true, + query_target: QueryTargetValue(8), + } + }; + + let path_holding_specialized_balances = prefunded_specialized_balances_for_voting_path(); + let previous_credits_in_specialized_balance = match self + .grove_get_raw_value_u64_from_encoded_var_vec( + (&path_holding_specialized_balances).into(), + specialized_balance_id.as_slice(), + direct_query_type, + transaction, + &mut drive_operations, + &platform_version.drive, + )? { + None => { + if estimated_costs_only_with_layer_info.is_none() { + return + Err(Error::Drive( + DriveError::PrefundedSpecializedBalanceDoesNotExist(format!( + "trying to deduct from a prefunded specialized balance {} that does not exist", + specialized_balance_id + )), + )); + } else { + i64::MAX as u64 + } + } + Some(value) => value, + }; + let new_total = previous_credits_in_specialized_balance + .checked_sub(amount) + .ok_or(Error::Drive( + DriveError::PrefundedSpecializedBalanceNotEnough( + previous_credits_in_specialized_balance, + amount, + ), + ))?; + let path_holding_total_credits_vec = prefunded_specialized_balances_for_voting_path_vec(); + let replace_op = QualifiedGroveDbOp::replace_op( + path_holding_total_credits_vec, + specialized_balance_id.to_vec(), + Element::new_sum_item(new_total as i64), + ); + drive_operations.push(GroveOperation(replace_op)); + Ok(drive_operations) + } +} diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance/mod.rs new file mode 100644 index 0000000000..acf4384166 --- /dev/null +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance/mod.rs @@ -0,0 +1,53 @@ +mod v0; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; + +use dpp::fee::Credits; +use dpp::identifier::Identifier; +use dpp::version::PlatformVersion; +use grovedb::TransactionArg; + +impl Drive { + /// Empties from a prefunded specialized balance the entire left over balance + /// + /// # Arguments + /// + /// * `transaction` - A `TransactionArg` object representing the transaction to be used for adding to the system credits. + /// * `platform_version` - A `PlatformVersion` object specifying the version of Platform. + /// + /// # Returns + /// + /// * `Result<(), Error>` - If successful, returns `Ok(())`. If an error occurs during the operation, returns an `Error`. + /// + /// # Errors + /// + /// This function will return an error if the version of Platform is unknown. + pub fn empty_prefunded_specialized_balance( + &self, + specialized_balance_id: Identifier, + error_if_does_not_exist: bool, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + match platform_version + .drive + .methods + .prefunded_specialized_balances + .empty_prefunded_specialized_balance + { + 0 => self.empty_prefunded_specialized_balance_v0( + specialized_balance_id, + error_if_does_not_exist, + transaction, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "empty_prefunded_specialized_balance".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance/v0/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance/v0/mod.rs new file mode 100644 index 0000000000..4e46151f5f --- /dev/null +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance/v0/mod.rs @@ -0,0 +1,52 @@ +use crate::drive::Drive; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; + +use dpp::fee::Credits; +use dpp::identifier::Identifier; +use dpp::version::PlatformVersion; +use grovedb::TransactionArg; + +impl Drive { + /// Empties the prefunded specialized balance + /// + /// # Arguments + /// + /// * `transaction` - A `TransactionArg` object representing the transaction to be used for adding to the system credits. + /// * `platform_version` - A `PlatformVersion` object specifying the version of Platform. + /// + /// # Returns + /// + /// * `Result<(), Error>` - If successful, returns `Ok(())`. If an error occurs during the operation, returns an `Error`. + /// + /// # Errors + /// + /// This function will return an error if the version of Platform is unknown. + #[inline(always)] + pub(super) fn empty_prefunded_specialized_balance_v0( + &self, + specialized_balance_id: Identifier, + error_if_does_not_exist: bool, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + let mut drive_operations = vec![]; + let (credits, batch_operations) = self.empty_prefunded_specialized_balance_operations( + specialized_balance_id, + error_if_does_not_exist, + &mut None, + transaction, + platform_version, + )?; + let grove_db_operations = + LowLevelDriveOperation::grovedb_operations_batch(&batch_operations); + self.grove_apply_batch_with_add_costs( + grove_db_operations, + false, + transaction, + &mut drive_operations, + &platform_version.drive, + )?; + Ok(credits) + } +} diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance_operations/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance_operations/mod.rs new file mode 100644 index 0000000000..d7562fac85 --- /dev/null +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance_operations/mod.rs @@ -0,0 +1,60 @@ +mod v0; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use grovedb::batch::KeyInfoPath; +use std::collections::HashMap; + +use crate::fees::op::LowLevelDriveOperation; +use dpp::fee::Credits; +use dpp::identifier::Identifier; +use dpp::version::PlatformVersion; +use grovedb::{EstimatedLayerInformation, TransactionArg}; + +impl Drive { + /// The operation Deducts from a prefunded specialized balance it's entire amount + /// + /// # Arguments + /// + /// * `transaction` - A `TransactionArg` object representing the transaction to be used for adding to the system credits. + /// * `platform_version` - A `PlatformVersion` object specifying the version of Platform. + /// + /// # Returns + /// + /// * `Result<(), Error>` - If successful, returns `Ok(())`. If an error occurs during the operation, returns an `Error`. + /// + /// # Errors + /// + /// This function will return an error if the version of Platform is unknown. + pub fn empty_prefunded_specialized_balance_operations( + &self, + specialized_balance_id: Identifier, + error_if_does_not_exist: bool, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result<(Credits, Vec), Error> { + match platform_version + .drive + .methods + .prefunded_specialized_balances + .empty_prefunded_specialized_balance + { + 0 => self.empty_prefunded_specialized_balance_operations_v0( + specialized_balance_id, + error_if_does_not_exist, + estimated_costs_only_with_layer_info, + transaction, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "empty_prefunded_specialized_balance_operations".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance_operations/v0/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance_operations/v0/mod.rs new file mode 100644 index 0000000000..c7f6b2049d --- /dev/null +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/empty_prefunded_specialized_balance_operations/v0/mod.rs @@ -0,0 +1,85 @@ +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use crate::fees::op::LowLevelDriveOperation::GroveOperation; +use crate::util::grove_operations::DirectQueryType; + +use crate::drive::prefunded_specialized_balances::{ + prefunded_specialized_balances_for_voting_path, + prefunded_specialized_balances_for_voting_path_vec, +}; +use crate::util::grove_operations::QueryTarget::QueryTargetValue; +use dpp::fee::Credits; +use dpp::identifier::Identifier; +use dpp::version::PlatformVersion; +use grovedb::batch::{KeyInfoPath, QualifiedGroveDbOp}; +use grovedb::{EstimatedLayerInformation, TransactionArg}; +use std::collections::HashMap; + +impl Drive { + /// The operations to add to the specialized balance + #[inline(always)] + pub(super) fn empty_prefunded_specialized_balance_operations_v0( + &self, + specialized_balance_id: Identifier, + error_if_does_not_exist: bool, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result<(Credits, Vec), Error> { + let mut drive_operations = vec![]; + if let Some(estimated_costs_only_with_layer_info) = estimated_costs_only_with_layer_info { + Self::add_estimation_costs_for_prefunded_specialized_balance_update( + estimated_costs_only_with_layer_info, + &platform_version.drive, + )?; + } + let direct_query_type = if estimated_costs_only_with_layer_info.is_none() { + DirectQueryType::StatefulDirectQuery + } else { + DirectQueryType::StatelessDirectQuery { + in_tree_using_sums: true, + query_target: QueryTargetValue(8), + } + }; + + let path_holding_specialized_balances = prefunded_specialized_balances_for_voting_path(); + let previous_credits_in_specialized_balance = match self + .grove_get_raw_value_u64_from_encoded_var_vec( + (&path_holding_specialized_balances).into(), + specialized_balance_id.as_slice(), + direct_query_type, + transaction, + &mut drive_operations, + &platform_version.drive, + )? { + None => { + if estimated_costs_only_with_layer_info.is_none() { + return if error_if_does_not_exist { + Err(Error::Drive( + DriveError::PrefundedSpecializedBalanceDoesNotExist(format!( + "trying to deduct from a prefunded specialized balance {} that does not exist", + specialized_balance_id + )), + )) + } else { + Ok((0, drive_operations)) + }; + } else { + 0 + } + } + Some(value) => value, + }; + let path_holding_total_credits_vec = prefunded_specialized_balances_for_voting_path_vec(); + let delete_op = QualifiedGroveDbOp::delete_op( + path_holding_total_credits_vec, + specialized_balance_id.to_vec(), + ); + drive_operations.push(GroveOperation(delete_op)); + Ok((previous_credits_in_specialized_balance, drive_operations)) + } +} diff --git a/packages/rs-drive/src/drive/prefunded_specialized_balances/mod.rs b/packages/rs-drive/src/drive/prefunded_specialized_balances/mod.rs index 215ac24934..ea5656188c 100644 --- a/packages/rs-drive/src/drive/prefunded_specialized_balances/mod.rs +++ b/packages/rs-drive/src/drive/prefunded_specialized_balances/mod.rs @@ -7,6 +7,10 @@ mod deduct_from_prefunded_specialized_balance; #[cfg(feature = "server")] mod deduct_from_prefunded_specialized_balance_operations; #[cfg(feature = "server")] +mod empty_prefunded_specialized_balance; +#[cfg(feature = "server")] +mod empty_prefunded_specialized_balance_operations; +#[cfg(feature = "server")] mod estimation_costs; #[cfg(feature = "server")] mod fetch; @@ -20,17 +24,18 @@ use crate::util::batch::grovedb_op_batch::GroveDbOpBatchV0Methods; #[cfg(feature = "server")] use crate::util::batch::GroveDbOpBatch; +/// The key for prefunded balances for voting pub const PREFUNDED_BALANCES_FOR_VOTING: u8 = 128; /// prefunded specialized balances for voting -pub(crate) fn prefunded_specialized_balances_path() -> [&'static [u8]; 1] { +pub fn prefunded_specialized_balances_path() -> [&'static [u8]; 1] { [Into::<&[u8; 1]>::into( RootTree::PreFundedSpecializedBalances, )] } /// prefunded specialized balances for voting -pub(crate) fn prefunded_specialized_balances_for_voting_path() -> [&'static [u8]; 2] { +pub fn prefunded_specialized_balances_for_voting_path() -> [&'static [u8]; 2] { [ Into::<&[u8; 1]>::into(RootTree::PreFundedSpecializedBalances), &[PREFUNDED_BALANCES_FOR_VOTING], @@ -38,7 +43,7 @@ pub(crate) fn prefunded_specialized_balances_for_voting_path() -> [&'static [u8] } /// prefunded specialized balances for voting vector -pub(crate) fn prefunded_specialized_balances_for_voting_path_vec() -> Vec> { +pub fn prefunded_specialized_balances_for_voting_path_vec() -> Vec> { vec![ Into::<&[u8; 1]>::into(RootTree::PreFundedSpecializedBalances).to_vec(), vec![PREFUNDED_BALANCES_FOR_VOTING], diff --git a/packages/rs-drive/src/fees/mod.rs b/packages/rs-drive/src/fees/mod.rs index 96e7c996ac..c240c54f0c 100644 --- a/packages/rs-drive/src/fees/mod.rs +++ b/packages/rs-drive/src/fees/mod.rs @@ -2,6 +2,7 @@ use crate::error::fee::FeeError; use crate::error::Error; mod calculate_fee; +/// Operations pub mod op; /// Get overflow error diff --git a/packages/rs-drive/src/lib.rs b/packages/rs-drive/src/lib.rs index a11e8e74bd..a13845e0fc 100644 --- a/packages/rs-drive/src/lib.rs +++ b/packages/rs-drive/src/lib.rs @@ -41,8 +41,9 @@ pub use grovedb_storage; pub mod cache; #[cfg(any(feature = "server", feature = "verify"))] pub mod config; +/// Fees module #[cfg(feature = "server")] -mod fees; +pub mod fees; #[cfg(feature = "server")] mod open; #[cfg(feature = "server")] diff --git a/packages/rs-drive/src/util/grove_operations/grove_get_raw_optional/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/grove_get_raw_optional/v0/mod.rs index 9b9a5be3bb..9c8f567fa9 100644 --- a/packages/rs-drive/src/util/grove_operations/grove_get_raw_optional/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/grove_get_raw_optional/v0/mod.rs @@ -13,7 +13,7 @@ use platform_version::version::drive_versions::DriveVersion; impl Drive { /// grove_get_raw basically means that there are no reference hops, this only matters /// when calculating worst case costs - pub(crate) fn grove_get_raw_optional_v0>( + pub(super) fn grove_get_raw_optional_v0>( &self, path: SubtreePath<'_, B>, key: &[u8], diff --git a/packages/rs-platform-value/src/inner_value.rs b/packages/rs-platform-value/src/inner_value.rs index c92828e91c..d8606bd478 100644 --- a/packages/rs-platform-value/src/inner_value.rs +++ b/packages/rs-platform-value/src/inner_value.rs @@ -361,7 +361,7 @@ impl Value { Self::inner_array_mut_ref(map, key) } - pub fn get_array_slice<'a>(&'a self, key: &'a str) -> Result<&[Value], Error> { + pub fn get_array_slice<'a>(&'a self, key: &'a str) -> Result<&'a [Value], Error> { let map = self.to_map()?; Self::inner_array_slice(map, key) } diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v5.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v5.rs index 900c0c153b..19303e4347 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v5.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v5.rs @@ -91,7 +91,7 @@ pub const DRIVE_ABCI_METHOD_VERSIONS_V5: DriveAbciMethodVersions = DriveAbciMeth voting: DriveAbciVotingMethodVersions { keep_record_of_finished_contested_resource_vote_poll: 0, clean_up_after_vote_poll_end: 0, - clean_up_after_contested_resources_vote_poll_end: 0, + clean_up_after_contested_resources_vote_poll_end: 1, check_for_ended_vote_polls: 0, tally_votes_for_contested_document_resource_vote_poll: 0, award_document_to_winner: 0, diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_credit_pool_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_versions/drive_credit_pool_method_versions/mod.rs index 0d116b4104..4e60e2c417 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_credit_pool_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_credit_pool_method_versions/mod.rs @@ -28,6 +28,7 @@ pub struct DriveCreditPoolEpochsMethodVersions { pub get_epochs_proposer_block_count: FeatureVersion, pub add_update_pending_epoch_refunds_operations: FeatureVersion, pub is_epochs_proposers_tree_empty: FeatureVersion, + pub add_epoch_processing_credits_for_distribution_operation: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_credit_pool_method_versions/v1.rs b/packages/rs-platform-version/src/version/drive_versions/drive_credit_pool_method_versions/v1.rs index 4311729886..7c2252edfb 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_credit_pool_method_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_credit_pool_method_versions/v1.rs @@ -25,6 +25,7 @@ pub const CREDIT_POOL_METHOD_VERSIONS_V1: DriveCreditPoolMethodVersions = get_epochs_proposer_block_count: 0, add_update_pending_epoch_refunds_operations: 0, is_epochs_proposers_tree_empty: 0, + add_epoch_processing_credits_for_distribution_operation: 0, }, pending_epoch_refunds: DriveCreditPoolPendingEpochRefundsMethodVersions { add_delete_pending_epoch_refunds_except_specified: 0, diff --git a/packages/rs-platform-version/src/version/drive_versions/mod.rs b/packages/rs-platform-version/src/version/drive_versions/mod.rs index b8c3fae487..262ac9407a 100644 --- a/packages/rs-platform-version/src/version/drive_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_versions/mod.rs @@ -21,6 +21,7 @@ pub mod drive_verify_method_versions; pub mod drive_vote_method_versions; pub mod v1; pub mod v2; +pub mod v3; #[derive(Clone, Debug, Default)] pub struct DriveVersion { @@ -85,6 +86,7 @@ pub struct DrivePrefundedSpecializedMethodVersions { pub deduct_from_prefunded_specialized_balance: FeatureVersion, pub deduct_from_prefunded_specialized_balance_operations: FeatureVersion, pub estimated_cost_for_prefunded_specialized_balance_update: FeatureVersion, + pub empty_prefunded_specialized_balance: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_versions/v1.rs b/packages/rs-platform-version/src/version/drive_versions/v1.rs index bbe1a12746..3ba9dc974f 100644 --- a/packages/rs-platform-version/src/version/drive_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_versions/v1.rs @@ -93,6 +93,7 @@ pub const DRIVE_VERSION_V1: DriveVersion = DriveVersion { deduct_from_prefunded_specialized_balance: 0, deduct_from_prefunded_specialized_balance_operations: 0, estimated_cost_for_prefunded_specialized_balance_update: 0, + empty_prefunded_specialized_balance: 0, }, }, grove_methods: DRIVE_GROVE_METHOD_VERSIONS_V1, diff --git a/packages/rs-platform-version/src/version/drive_versions/v2.rs b/packages/rs-platform-version/src/version/drive_versions/v2.rs index 5747bc732b..0881f6ca55 100644 --- a/packages/rs-platform-version/src/version/drive_versions/v2.rs +++ b/packages/rs-platform-version/src/version/drive_versions/v2.rs @@ -93,6 +93,7 @@ pub const DRIVE_VERSION_V2: DriveVersion = DriveVersion { deduct_from_prefunded_specialized_balance: 0, deduct_from_prefunded_specialized_balance_operations: 0, estimated_cost_for_prefunded_specialized_balance_update: 0, + empty_prefunded_specialized_balance: 0, }, }, grove_methods: DRIVE_GROVE_METHOD_VERSIONS_V1, diff --git a/packages/rs-platform-version/src/version/drive_versions/v3.rs b/packages/rs-platform-version/src/version/drive_versions/v3.rs new file mode 100644 index 0000000000..2f3efb3729 --- /dev/null +++ b/packages/rs-platform-version/src/version/drive_versions/v3.rs @@ -0,0 +1,101 @@ +use crate::version::drive_versions::drive_contract_method_versions::v1::DRIVE_CONTRACT_METHOD_VERSIONS_V1; +use crate::version::drive_versions::drive_credit_pool_method_versions::v1::CREDIT_POOL_METHOD_VERSIONS_V1; +use crate::version::drive_versions::drive_document_method_versions::v1::DRIVE_DOCUMENT_METHOD_VERSIONS_V1; +use crate::version::drive_versions::drive_grove_method_versions::v1::DRIVE_GROVE_METHOD_VERSIONS_V1; +use crate::version::drive_versions::drive_identity_method_versions::v1::DRIVE_IDENTITY_METHOD_VERSIONS_V1; +use crate::version::drive_versions::drive_state_transition_method_versions::v1::DRIVE_STATE_TRANSITION_METHOD_VERSIONS_V1; +use crate::version::drive_versions::drive_structure_version::v1::DRIVE_STRUCTURE_V1; +use crate::version::drive_versions::drive_verify_method_versions::v1::DRIVE_VERIFY_METHOD_VERSIONS_V1; +use crate::version::drive_versions::drive_vote_method_versions::v2::DRIVE_VOTE_METHOD_VERSIONS_V2; +use crate::version::drive_versions::{ + DriveAssetLockMethodVersions, DriveBalancesMethodVersions, DriveBatchOperationsMethodVersion, + DriveEstimatedCostsMethodVersions, DriveFeesMethodVersions, DriveFetchMethodVersions, + DriveInitializationMethodVersions, DriveMethodVersions, DriveOperationsMethodVersion, + DrivePlatformStateMethodVersions, DrivePlatformSystemMethodVersions, + DrivePrefundedSpecializedMethodVersions, DriveProtocolUpgradeVersions, + DriveProveMethodVersions, DriveSystemEstimationCostsMethodVersions, DriveVersion, +}; +use grovedb_version::version::v1::GROVE_V1; + +pub const DRIVE_VERSION_V3: DriveVersion = DriveVersion { + structure: DRIVE_STRUCTURE_V1, + methods: DriveMethodVersions { + initialization: DriveInitializationMethodVersions { + create_initial_state_structure: 0, + }, + credit_pools: CREDIT_POOL_METHOD_VERSIONS_V1, + protocol_upgrade: DriveProtocolUpgradeVersions { + clear_version_information: 0, + fetch_versions_with_counter: 0, + fetch_proved_versions_with_counter: 0, + fetch_validator_version_votes: 0, + fetch_proved_validator_version_votes: 0, + remove_validators_proposed_app_versions: 0, + update_validator_proposed_app_version: 0, + }, + prove: DriveProveMethodVersions { + prove_elements: 0, + prove_multiple_state_transition_results: 0, + }, + balances: DriveBalancesMethodVersions { + add_to_system_credits: 0, + add_to_system_credits_operations: 0, + remove_from_system_credits: 0, + remove_from_system_credits_operations: 0, + calculate_total_credits_balance: 0, + }, + document: DRIVE_DOCUMENT_METHOD_VERSIONS_V1, + vote: DRIVE_VOTE_METHOD_VERSIONS_V2, + contract: DRIVE_CONTRACT_METHOD_VERSIONS_V1, + fees: DriveFeesMethodVersions { calculate_fee: 0 }, + estimated_costs: DriveEstimatedCostsMethodVersions { + add_estimation_costs_for_levels_up_to_contract: 0, + add_estimation_costs_for_levels_up_to_contract_document_type_excluded: 0, + add_estimation_costs_for_contested_document_tree_levels_up_to_contract: 0, + add_estimation_costs_for_contested_document_tree_levels_up_to_contract_document_type_excluded: 0, + }, + asset_lock: DriveAssetLockMethodVersions { + add_asset_lock_outpoint: 0, + add_estimation_costs_for_adding_asset_lock: 0, + fetch_asset_lock_outpoint_info: 0, + }, + verify: DRIVE_VERIFY_METHOD_VERSIONS_V1, + identity: DRIVE_IDENTITY_METHOD_VERSIONS_V1, + platform_system: DrivePlatformSystemMethodVersions { + estimation_costs: DriveSystemEstimationCostsMethodVersions { + for_total_system_credits_update: 0, + }, + }, + operations: DriveOperationsMethodVersion { + rollback_transaction: 0, + drop_cache: 0, + commit_transaction: 0, + apply_partial_batch_low_level_drive_operations: 0, + apply_partial_batch_grovedb_operations: 0, + apply_batch_low_level_drive_operations: 0, + apply_batch_grovedb_operations: 0, + }, + state_transitions: DRIVE_STATE_TRANSITION_METHOD_VERSIONS_V1, + batch_operations: DriveBatchOperationsMethodVersion { + convert_drive_operations_to_grove_operations: 0, + apply_drive_operations: 0, + }, + platform_state: DrivePlatformStateMethodVersions { + fetch_platform_state_bytes: 0, + store_platform_state_bytes: 0, + }, + fetch: DriveFetchMethodVersions { fetch_elements: 0 }, + prefunded_specialized_balances: DrivePrefundedSpecializedMethodVersions { + fetch_single: 0, + prove_single: 0, + add_prefunded_specialized_balance: 0, + add_prefunded_specialized_balance_operations: 1, //changed in v3 + deduct_from_prefunded_specialized_balance: 1, //changed in v3 + deduct_from_prefunded_specialized_balance_operations: 0, + estimated_cost_for_prefunded_specialized_balance_update: 0, + empty_prefunded_specialized_balance: 0, + }, + }, + grove_methods: DRIVE_GROVE_METHOD_VERSIONS_V1, + grove_version: GROVE_V1, +}; diff --git a/packages/rs-platform-version/src/version/mocks/v2_test.rs b/packages/rs-platform-version/src/version/mocks/v2_test.rs index 931ef19b97..e1371648ff 100644 --- a/packages/rs-platform-version/src/version/mocks/v2_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v2_test.rs @@ -127,6 +127,7 @@ pub const TEST_PLATFORM_V2: PlatformVersion = PlatformVersion { deduct_from_prefunded_specialized_balance: 0, deduct_from_prefunded_specialized_balance_operations: 0, estimated_cost_for_prefunded_specialized_balance_update: 0, + empty_prefunded_specialized_balance: 0, }, }, grove_methods: DRIVE_GROVE_METHOD_VERSIONS_V1, diff --git a/packages/rs-platform-version/src/version/v8.rs b/packages/rs-platform-version/src/version/v8.rs index 437b2f3ecb..7e86606215 100644 --- a/packages/rs-platform-version/src/version/v8.rs +++ b/packages/rs-platform-version/src/version/v8.rs @@ -19,7 +19,7 @@ use crate::version::drive_abci_versions::drive_abci_structure_versions::v1::DRIV use crate::version::drive_abci_versions::drive_abci_validation_versions::v5::DRIVE_ABCI_VALIDATION_VERSIONS_V5; use crate::version::drive_abci_versions::drive_abci_withdrawal_constants::v2::DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V2; use crate::version::drive_abci_versions::DriveAbciVersion; -use crate::version::drive_versions::v2::DRIVE_VERSION_V2; +use crate::version::drive_versions::v3::DRIVE_VERSION_V3; use crate::version::fee::v1::FEE_VERSION1; use crate::version::protocol_version::PlatformVersion; use crate::version::system_data_contract_versions::v1::SYSTEM_DATA_CONTRACT_VERSIONS_V1; @@ -31,7 +31,8 @@ pub const PROTOCOL_VERSION_8: ProtocolVersion = 8; /// This version contains some fixes for withdrawals and nfts. pub const PLATFORM_V8: PlatformVersion = PlatformVersion { protocol_version: PROTOCOL_VERSION_8, - drive: DRIVE_VERSION_V2, + //changed to V3 because of an error in add_prefunded_specialized_balance_operations + drive: DRIVE_VERSION_V3, drive_abci: DriveAbciVersion { structs: DRIVE_ABCI_STRUCTURE_VERSIONS_V1, // We changed `pool_withdrawals_into_transactions_queue` to v1 in order to add pool