diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/advanced_structure_v1/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/advanced_structure_v1/mod.rs index cb4a3593681..504a1135d89 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/advanced_structure_v1/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/advanced_structure_v1/mod.rs @@ -290,6 +290,89 @@ mod tests { .collect() } + #[test] + fn should_require_the_exact_contested_dpns_fee_for_each_protocol_version() { + for (protocol_version, expected_amount) in [(13, 20_000_000_000), (14, 10_000_000_000)] { + let platform_version = PlatformVersion::get(protocol_version).expect("known version"); + for paid_amount in [ + 9_999_999_999, + 10_000_000_000, + 10_000_000_001, + 20_000_000_000, + 20_000_000_001, + ] { + let mut action = create_action( + CONTESTED_LABEL, + Some(CONTESTED_INDEX_NAME), + platform_version, + ); + let DocumentCreateTransitionAction::V0(action_data) = &mut action; + action_data + .prefunded_voting_balance + .as_mut() + .expect("prefunded contest") + .1 = paid_amount; + + let errors = validate(&action, platform_version); + let contest_errors = contest_errors(&errors); + if paid_amount == expected_amount { + assert!( + contest_errors.is_empty(), + "protocol {protocol_version}: {errors:?}" + ); + } else { + let [StateError::DocumentContestNotPaidForError(error)] = + contest_errors.as_slice() + else { + panic!("protocol {protocol_version}: expected a fee error, got {errors:?}"); + }; + assert_eq!(error.expected_amount(), expected_amount); + assert_eq!(error.paid_amount(), paid_amount); + } + } + } + } + + #[test] + fn should_construct_dpns_transitions_with_the_protocol_contested_fee() { + use dpp::document::{Document, DocumentV0}; + use dpp::state_transition::batch_transition::document_create_transition::DocumentCreateTransition; + + for (protocol_version, expected_amount) in [(13, 20_000_000_000), (14, 10_000_000_000)] { + let platform_version = PlatformVersion::get(protocol_version).expect("known version"); + let contract = DataContractFetchInfo::dpns_contract_fixture(protocol_version); + let document_type = contract + .contract + .document_type_for_name("domain") + .expect("domain type"); + + for label in [CONTESTED_LABEL, NON_CONTESTED_LABEL] { + let document = Document::V0(DocumentV0 { + id: Identifier::from([0xAA; 32]), + owner_id: Identifier::from([0xBB; 32]), + properties: domain_properties(label), + ..Default::default() + }); + let DocumentCreateTransition::V0(transition) = + DocumentCreateTransition::from_document( + document, + document_type, + [0xCC; 32], + None, + 1, + platform_version, + None, + None, + ) + .expect("create transition"); + + let expected = (label == CONTESTED_LABEL) + .then(|| (CONTESTED_INDEX_NAME.to_string(), expected_amount)); + assert_eq!(transition.prefunded_voting_balance, expected); + } + } + } + #[test] fn should_reject_a_prefunded_voting_balance_naming_another_index() { let platform_version = PlatformVersion::latest(); diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs index 3707d37c101..8f1eadf030b 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs @@ -5664,6 +5664,10 @@ mod tests { #[tokio::test] async fn test_non_proved_prefunded_specialized_balance_request_after_many_votes() { let platform_version = PlatformVersion::latest(); + let vote_fees = &platform_version.fee_version.vote_resolution_fund_fees; + let contribution = + vote_fees.contested_document_vote_resolution_fund_required_amount; + let vote_cost = vote_fees.contested_document_single_vote_cost; let mut platform = TestPlatformBuilder::new() .with_latest_protocol_version() .build_with_mock_rpc() @@ -5688,7 +5692,7 @@ mod tests { platform_version, ); - assert_eq!(start_balance, dash_to_credits!(0.4)); + assert_eq!(start_balance, 2 * contribution); let (_contender_3, _contender_4, _) = create_dpns_identity_name_contest( &mut platform, @@ -5707,7 +5711,7 @@ mod tests { platform_version, ); - assert_eq!(start_balance_after_more_contenders, dash_to_credits!(0.8)); + assert_eq!(start_balance_after_more_contenders, 4 * contribution); for i in 0..50 { let (pro_tx_hash, _masternode, signer, voting_key) = @@ -5739,7 +5743,7 @@ mod tests { platform_version, ); - assert_eq!(balance_after_50_votes, dash_to_credits!(0.795)); + assert_eq!(balance_after_50_votes, 4 * contribution - 50 * vote_cost); for i in 0..5 { let (pro_tx_hash, _masternode, signer, voting_key) = @@ -5771,12 +5775,16 @@ mod tests { platform_version, ); - assert_eq!(balance_after_55_votes, dash_to_credits!(0.7945)); + assert_eq!(balance_after_55_votes, 4 * contribution - 55 * vote_cost); } #[tokio::test] async fn test_proved_prefunded_specialized_balance_request_after_many_votes() { let platform_version = PlatformVersion::latest(); + let vote_fees = &platform_version.fee_version.vote_resolution_fund_fees; + let contribution = + vote_fees.contested_document_vote_resolution_fund_required_amount; + let vote_cost = vote_fees.contested_document_single_vote_cost; let mut platform = TestPlatformBuilder::new() .with_latest_protocol_version() .build_with_mock_rpc() @@ -5801,7 +5809,7 @@ mod tests { platform_version, ); - assert_eq!(start_balance, dash_to_credits!(0.4)); + assert_eq!(start_balance, 2 * contribution); let (_contender_3, _contender_4, _) = create_dpns_identity_name_contest( &mut platform, @@ -5820,7 +5828,7 @@ mod tests { platform_version, ); - assert_eq!(start_balance_after_more_contenders, dash_to_credits!(0.8)); + assert_eq!(start_balance_after_more_contenders, 4 * contribution); for i in 0..50 { let (pro_tx_hash, _masternode, signer, voting_key) = @@ -5852,7 +5860,7 @@ mod tests { platform_version, ); - assert_eq!(balance_after_50_votes, dash_to_credits!(0.795)); + assert_eq!(balance_after_50_votes, 4 * contribution - 50 * vote_cost); for i in 0..5 { let (pro_tx_hash, _masternode, signer, voting_key) = @@ -5884,7 +5892,7 @@ mod tests { platform_version, ); - assert_eq!(balance_after_55_votes, dash_to_credits!(0.7945)); + assert_eq!(balance_after_55_votes, 4 * contribution - 55 * vote_cost); } } diff --git a/packages/rs-platform-version/src/version/fee/mod.rs b/packages/rs-platform-version/src/version/fee/mod.rs index 8b603e2ddba..c9d57857ba4 100644 --- a/packages/rs-platform-version/src/version/fee/mod.rs +++ b/packages/rs-platform-version/src/version/fee/mod.rs @@ -25,6 +25,7 @@ pub mod state_transition_min_fees; pub mod storage; pub mod v1; pub mod v2; +pub mod v3; pub mod vote_resolution_fund_fees; pub type FeeVersionNumber = u32; diff --git a/packages/rs-platform-version/src/version/fee/v3.rs b/packages/rs-platform-version/src/version/fee/v3.rs new file mode 100644 index 00000000000..70f59df2f2a --- /dev/null +++ b/packages/rs-platform-version/src/version/fee/v3.rs @@ -0,0 +1,11 @@ +use crate::version::fee::v2::FEE_VERSION2; +use crate::version::fee::vote_resolution_fund_fees::v2::VOTE_RESOLUTION_FUND_FEES_VERSION2; +use crate::version::fee::FeeVersion; + +/// Introduced in protocol version 14 (4.2). +pub const FEE_VERSION3: FeeVersion = FeeVersion { + // Contested contributions use the active protocol, so unchanged storage + // rates retain the historical fee_version_number inherited from FEE_VERSION2. + vote_resolution_fund_fees: VOTE_RESOLUTION_FUND_FEES_VERSION2, + ..FEE_VERSION2 +}; diff --git a/packages/rs-platform-version/src/version/fee/vote_resolution_fund_fees/mod.rs b/packages/rs-platform-version/src/version/fee/vote_resolution_fund_fees/mod.rs index ba594961d40..fefd39924b2 100644 --- a/packages/rs-platform-version/src/version/fee/vote_resolution_fund_fees/mod.rs +++ b/packages/rs-platform-version/src/version/fee/vote_resolution_fund_fees/mod.rs @@ -1,6 +1,7 @@ use bincode::{Decode, Encode}; pub mod v1; +pub mod v2; #[derive(Clone, Debug, Encode, Decode, Default, PartialEq, Eq)] pub struct VoteResolutionFundFees { /// This is the amount that will be deducted from an identity and used to pay for voting diff --git a/packages/rs-platform-version/src/version/fee/vote_resolution_fund_fees/v2.rs b/packages/rs-platform-version/src/version/fee/vote_resolution_fund_fees/v2.rs new file mode 100644 index 00000000000..c13e7f8c94e --- /dev/null +++ b/packages/rs-platform-version/src/version/fee/vote_resolution_fund_fees/v2.rs @@ -0,0 +1,8 @@ +use crate::version::fee::vote_resolution_fund_fees::v1::VOTE_RESOLUTION_FUND_FEES_VERSION1; +use crate::version::fee::vote_resolution_fund_fees::VoteResolutionFundFees; + +/// Introduced in protocol version 14 (4.2). +pub const VOTE_RESOLUTION_FUND_FEES_VERSION2: VoteResolutionFundFees = VoteResolutionFundFees { + contested_document_vote_resolution_fund_required_amount: 10_000_000_000, // 0.1 DASH + ..VOTE_RESOLUTION_FUND_FEES_VERSION1 +}; diff --git a/packages/rs-platform-version/src/version/v14.rs b/packages/rs-platform-version/src/version/v14.rs index 7589c485738..8d5f18abb1e 100644 --- a/packages/rs-platform-version/src/version/v14.rs +++ b/packages/rs-platform-version/src/version/v14.rs @@ -22,7 +22,7 @@ use crate::version::drive_abci_versions::drive_abci_validation_versions::v10::DR use crate::version::drive_abci_versions::drive_abci_withdrawal_constants::v3::DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V3; use crate::version::drive_abci_versions::DriveAbciVersion; use crate::version::drive_versions::v9::DRIVE_VERSION_V9; -use crate::version::fee::v2::FEE_VERSION2; +use crate::version::fee::v3::FEE_VERSION3; use crate::version::protocol_version::PlatformVersion; use crate::version::system_data_contract_versions::v3::SYSTEM_DATA_CONTRACT_VERSIONS_V3; use crate::version::system_limits::v4::SYSTEM_LIMITS_V4; @@ -222,7 +222,7 @@ pub const PLATFORM_V14: PlatformVersion = PlatformVersion { factory_versions: DPP_FACTORY_VERSIONS_V1, }, system_data_contracts: SYSTEM_DATA_CONTRACT_VERSIONS_V3, // changed: DashPay v2 adds profile payment address fields (DIP-33) - fee_version: FEE_VERSION2, + fee_version: FEE_VERSION3, // changed: contested document contribution reduced to 0.1 DASH system_limits: SYSTEM_LIMITS_V4, // changed: daily withdrawal limit becomes 15% of the total credits a day ago + time-range overlap-factor cap (24) consensus: ConsensusVersions { tenderdash_consensus_version: 1, @@ -234,6 +234,27 @@ mod tests { use super::*; use crate::version::v13::PLATFORM_V13; + #[test] + fn should_halve_only_the_contested_document_fee_at_protocol_14() { + for protocol_version in 1..14 { + let version = PlatformVersion::get(protocol_version).expect("known protocol version"); + assert_eq!( + version + .fee_version + .vote_resolution_fund_fees + .contested_document_vote_resolution_fund_required_amount, + 20_000_000_000, + "protocol {protocol_version} must preserve the 0.2 DASH contribution" + ); + } + + let mut expected_fees = PLATFORM_V13.fee_version.clone(); + expected_fees + .vote_resolution_fund_fees + .contested_document_vote_resolution_fund_required_amount = 10_000_000_000; + assert_eq!(PLATFORM_V14.fee_version, expected_fees); + } + /// The ranked / boolean-HAVING routing gate lives in v14's own query /// table, so flipping it touches only v14: a v13 node keeps running /// the v0 helper, which rejects every non-empty HAVING, so a