From 47acf03b03602be805c11138f2f2b5676314b078 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 16 Mar 2026 14:24:15 +0700 Subject: [PATCH 01/13] fix(dpp): bind token config update action_id to payload value (v1) The v0 action_id calculation for token config updates used only the u8 enum discriminant (via u8_item_index()), not the actual payload value. This allowed a group member to propose one config value, collect votes, then swap to a different value with the same discriminant during finalization (e.g. MaxSupply(100) vs MaxSupply(999999999999) both hashed identically). Introduce a versioned action_id calculation: - v0 (unchanged): hashes only the u8 discriminant for backward compat - v1 (new): serializes the full TokenConfigurationChangeItem via bincode and includes it in the double-hash, binding the voted-on value The version is controlled by a new platform version field (token_config_update_action_id_version) so v1 activates in a future protocol version without breaking existing production data. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../batched_transition/multi_party_action.rs | 13 ++ .../v0/v0_methods.rs | 37 ++++ .../v0_methods.rs | 206 ++++++++++++++++++ .../batched_transition/token_transition.rs | 32 +++ .../batch_transition/v1/v1_methods.rs | 3 +- .../validate_basic_structure/v0/mod.rs | 2 +- .../dpp_versions/dpp_token_versions/mod.rs | 5 + .../dpp_versions/dpp_token_versions/v1.rs | 1 + .../dpp_versions/dpp_token_versions/v2.rs | 8 + 9 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/v2.rs diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs index 52f7cf4e7f4..273a4b95bcc 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs @@ -1,5 +1,18 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; pub trait AllowedAsMultiPartyAction { fn calculate_action_id(&self, owner_id: Identifier) -> Identifier; + + /// Version-aware action_id calculation. By default, delegates to the + /// non-versioned [`calculate_action_id`](Self::calculate_action_id). + /// Transition types that need version-dependent behaviour (e.g. + /// `TokenConfigUpdateTransition`) override this method. + fn calculate_action_id_versioned( + &self, + owner_id: Identifier, + _platform_version: &PlatformVersion, + ) -> Identifier { + self.calculate_action_id(owner_id) + } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs index a9e24acc377..ae9ead1b500 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs @@ -1,4 +1,5 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -69,6 +70,7 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransitionV0 { } impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { + /// v0 action_id: uses only the u8 discriminant (kept for backward compat). fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenConfigUpdateTransitionV0 { base, @@ -83,4 +85,39 @@ impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { update_token_configuration_item.u8_item_index(), ) } + + /// Version-aware action_id calculation. + /// v0: uses only the u8 discriminant (existing production behavior). + /// v1: includes the full serialized config change item to prevent vote swap. + fn calculate_action_id_versioned( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Identifier { + let TokenConfigUpdateTransitionV0 { + base, + update_token_configuration_item, + .. + } = self; + + match platform_version + .dpp + .token_versions + .token_config_update_action_id_version + { + 0 => TokenConfigUpdateTransition::calculate_action_id_with_fields( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + update_token_configuration_item.u8_item_index(), + ), + 1 => TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + update_token_configuration_item, + ), + version => panic!("unsupported token_config_update_action_id_version {version}"), + } + } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index b16720f136c..942e96b4d91 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -1,4 +1,5 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use crate::prelude::IdentityNonce; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; @@ -71,9 +72,23 @@ impl AllowedAsMultiPartyAction for TokenConfigUpdateTransition { TokenConfigUpdateTransition::V0(v0) => v0.calculate_action_id(owner_id), } } + + fn calculate_action_id_versioned( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Identifier { + match self { + TokenConfigUpdateTransition::V0(v0) => { + v0.calculate_action_id_versioned(owner_id, platform_version) + } + } + } } impl TokenConfigUpdateTransition { + /// v0: action_id uses only the u8 discriminant of the config change item. + /// This is kept for backward compatibility with existing production data. pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], @@ -88,4 +103,195 @@ impl TokenConfigUpdateTransition { hash_double(bytes).into() } + + /// v1: action_id includes the full serialized config change item, binding + /// the voted-on value into the hash and preventing vote-swap attacks. + pub fn calculate_action_id_with_fields_v1( + token_id: &[u8; 32], + owner_id: &[u8; 32], + identity_contract_nonce: IdentityNonce, + update_token_configuration_item: &TokenConfigurationChangeItem, + ) -> Identifier { + let serialized_item = + bincode::encode_to_vec(update_token_configuration_item, bincode::config::standard()) + .expect("expected to encode token configuration change item"); + + let mut bytes = b"action_token_config_update".to_vec(); + bytes.extend_from_slice(token_id); + bytes.extend_from_slice(owner_id); + bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); + bytes.extend_from_slice(&serialized_item); + + hash_double(bytes).into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; + use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0; + use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; + + fn make_transition(item: TokenConfigurationChangeItem) -> TokenConfigUpdateTransition { + TokenConfigUpdateTransition::V0(TokenConfigUpdateTransitionV0 { + base: TokenBaseTransition::V0(TokenBaseTransitionV0 { + identity_contract_nonce: 1, + token_contract_position: 0, + data_contract_id: Identifier::new([1u8; 32]), + token_id: Identifier::new([2u8; 32]), + using_group_info: None, + }), + update_token_configuration_item: item, + public_note: None, + }) + } + + #[test] + fn v0_action_id_same_discriminant_different_values_produces_same_id_vulnerability() { + // This test documents the v0 vulnerability: two different MaxSupply + // values produce the same action_id because only the u8 discriminant + // is hashed, not the actual value. + let owner_id = Identifier::new([3u8; 32]); + + let t_small = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(100))); + let t_large = make_transition(TokenConfigurationChangeItem::MaxSupply(Some( + 999_999_999_999, + ))); + + let id_small = t_small.calculate_action_id(owner_id); + let id_large = t_large.calculate_action_id(owner_id); + + // v0: these are EQUAL -- the vulnerability + assert_eq!( + id_small, id_large, + "v0 should produce the same action_id for different MaxSupply values (vulnerability)" + ); + } + + #[test] + fn v1_action_id_same_discriminant_different_values_produces_different_ids() { + // After the fix, the full serialized config item is included in the + // hash, so different values produce different action_ids. + let token_id = [2u8; 32]; + let owner_id = [3u8; 32]; + let nonce = 1u64; + + let item_small = TokenConfigurationChangeItem::MaxSupply(Some(100)); + let item_large = TokenConfigurationChangeItem::MaxSupply(Some(999_999_999_999)); + + let id_small = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + &token_id, + &owner_id, + nonce, + &item_small, + ); + let id_large = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + &token_id, + &owner_id, + nonce, + &item_large, + ); + + // v1: these must be DIFFERENT -- the fix + assert_ne!( + id_small, id_large, + "v1 should produce different action_ids for different MaxSupply values" + ); + } + + #[test] + fn v1_action_id_different_item_types_produces_different_ids() { + let token_id = [2u8; 32]; + let owner_id = [3u8; 32]; + let nonce = 1u64; + + let item_max_supply = TokenConfigurationChangeItem::MaxSupply(Some(100)); + let item_allow_dest = TokenConfigurationChangeItem::MintingAllowChoosingDestination(true); + + let id_max = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + &token_id, + &owner_id, + nonce, + &item_max_supply, + ); + let id_dest = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + &token_id, + &owner_id, + nonce, + &item_allow_dest, + ); + + assert_ne!( + id_max, id_dest, + "v1 should produce different action_ids for different config item types" + ); + } + + #[test] + fn v0_and_v1_produce_different_ids_for_same_input() { + // Verify v0 and v1 are not accidentally identical (they hash + // different payloads). + let token_id = [2u8; 32]; + let owner_id = [3u8; 32]; + let nonce = 1u64; + let item = TokenConfigurationChangeItem::MaxSupply(Some(100)); + + let id_v0 = TokenConfigUpdateTransition::calculate_action_id_with_fields( + &token_id, + &owner_id, + nonce, + item.u8_item_index(), + ); + let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + &token_id, &owner_id, nonce, &item, + ); + + assert_ne!( + id_v0, id_v1, + "v0 and v1 should produce different action_ids for the same config item" + ); + } + + #[test] + fn versioned_dispatch_uses_v0_on_current_platform_version() { + // On the current platform version (v12), the versioned method should + // produce the same result as the v0 method. + let owner_id = Identifier::new([3u8; 32]); + let t = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(100))); + + let platform_version = PlatformVersion::latest(); + + let id_plain = t.calculate_action_id(owner_id); + let id_versioned = t.calculate_action_id_versioned(owner_id, platform_version); + + assert_eq!( + id_plain, id_versioned, + "on current platform version, versioned and plain should produce the same id" + ); + } + + #[test] + fn v1_action_id_identical_items_produces_same_id() { + // Sanity check: identical config items should produce the same + // action_id under v1. + let token_id = [2u8; 32]; + let owner_id = [3u8; 32]; + let nonce = 1u64; + + let item1 = TokenConfigurationChangeItem::MaxSupply(Some(42)); + let item2 = TokenConfigurationChangeItem::MaxSupply(Some(42)); + + let id1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + &token_id, &owner_id, nonce, &item1, + ); + let id2 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + &token_id, &owner_id, nonce, &item2, + ); + + assert_eq!( + id1, id2, + "v1 should produce the same action_id for identical config items" + ); + } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs index 765574fc6e4..6fc8e032c20 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs @@ -215,6 +215,16 @@ pub trait TokenTransitionV0Methods { fn calculate_action_id(&self, owner_id: Identifier) -> Option; + /// Version-aware action_id calculation. Falls back to the non-versioned + /// method for all transitions except `ConfigUpdate`, which dispatches + /// between v0 (discriminant-only) and v1 (full-payload) based on + /// `platform_version`. + fn calculate_action_id_versioned( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Option; + fn can_calculate_action_id(&self) -> bool; /// Historical document type name for the token history contract fn historical_document_type_name(&self) -> &str; @@ -295,6 +305,28 @@ impl TokenTransitionV0Methods for TokenTransition { } } + fn calculate_action_id_versioned( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Option { + match self { + TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Transfer(_) => None, + TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Claim(_) => None, + TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::ConfigUpdate(t) => { + Some(t.calculate_action_id_versioned(owner_id, platform_version)) + } + TokenTransition::DirectPurchase(_) => None, + TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id)), + } + } + fn can_calculate_action_id(&self) -> bool { match self { TokenTransition::Burn(_) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs index 1458ad8dcc2..408ab890dc0 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs @@ -640,7 +640,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = config_update_transition.calculate_action_id(owner_id); + let action_id = config_update_transition + .calculate_action_id_versioned(owner_id, _platform_version); config_update_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs index 559a26dcd4e..70308e30618 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs @@ -191,7 +191,7 @@ impl BatchTransition { if let Some(group_state_transition_info) = transition.base().using_group_info() { if group_state_transition_info.action_is_proposer { if let Some(calculated_action_id) = - transition.calculate_action_id(self.owner_id()) + transition.calculate_action_id_versioned(self.owner_id(), platform_version) { if group_state_transition_info.action_id != calculated_action_id { result.add_error(BasicError::InvalidActionIdError( diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/mod.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/mod.rs index 6fad1c1578b..ed2283c859a 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/mod.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/mod.rs @@ -1,4 +1,5 @@ pub mod v1; +pub mod v2; use versioned_feature_core::FeatureVersion; @@ -7,4 +8,8 @@ pub struct DPPTokenVersions { pub identity_token_info_default_structure_version: FeatureVersion, pub identity_token_status_default_structure_version: FeatureVersion, pub token_contract_info_default_structure_version: FeatureVersion, + /// Version for the token config update action_id calculation. + /// v0: uses only the u8 discriminant of the config change item (vulnerable to value swap) + /// v1: includes the full serialized config change item in the hash + pub token_config_update_action_id_version: FeatureVersion, } diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/v1.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/v1.rs index f2737f8b09c..4e3e8e3861a 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/v1.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/v1.rs @@ -4,4 +4,5 @@ pub const TOKEN_VERSIONS_V1: DPPTokenVersions = DPPTokenVersions { identity_token_info_default_structure_version: 0, identity_token_status_default_structure_version: 0, token_contract_info_default_structure_version: 0, + token_config_update_action_id_version: 0, }; diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/v2.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/v2.rs new file mode 100644 index 00000000000..32c2c0fdfa1 --- /dev/null +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_token_versions/v2.rs @@ -0,0 +1,8 @@ +use crate::version::dpp_versions::dpp_token_versions::DPPTokenVersions; + +pub const TOKEN_VERSIONS_V2: DPPTokenVersions = DPPTokenVersions { + identity_token_info_default_structure_version: 0, + identity_token_status_default_structure_version: 0, + token_contract_info_default_structure_version: 0, + token_config_update_action_id_version: 1, +}; From 71964ad2249ce3d713512774bdd30275ae2313fc Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 16 Mar 2026 17:07:19 +0700 Subject: [PATCH 02/13] version fix --- packages/rs-platform-version/src/version/v12.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rs-platform-version/src/version/v12.rs b/packages/rs-platform-version/src/version/v12.rs index c14b80facb6..394260d2b49 100644 --- a/packages/rs-platform-version/src/version/v12.rs +++ b/packages/rs-platform-version/src/version/v12.rs @@ -10,7 +10,7 @@ use crate::version::dpp_versions::dpp_state_transition_conversion_versions::v2:: use crate::version::dpp_versions::dpp_state_transition_method_versions::v1::STATE_TRANSITION_METHOD_VERSIONS_V1; use crate::version::dpp_versions::dpp_state_transition_serialization_versions::v2::STATE_TRANSITION_SERIALIZATION_VERSIONS_V2; use crate::version::dpp_versions::dpp_state_transition_versions::v3::STATE_TRANSITION_VERSIONS_V3; -use crate::version::dpp_versions::dpp_token_versions::v1::TOKEN_VERSIONS_V1; +use crate::version::dpp_versions::dpp_token_versions::v2::TOKEN_VERSIONS_V2; use crate::version::dpp_versions::dpp_validation_versions::v3::DPP_VALIDATION_VERSIONS_V3; use crate::version::dpp_versions::dpp_voting_versions::v2::VOTING_VERSION_V2; use crate::version::dpp_versions::DPPVersion; @@ -55,7 +55,7 @@ pub const PLATFORM_V12: PlatformVersion = PlatformVersion { document_versions: DOCUMENT_VERSIONS_V3, identity_versions: IDENTITY_VERSIONS_V1, voting_versions: VOTING_VERSION_V2, - token_versions: TOKEN_VERSIONS_V1, + token_versions: TOKEN_VERSIONS_V2, // fixes issue with token config update action id asset_lock_versions: DPP_ASSET_LOCK_VERSIONS_V1, methods: DPP_METHOD_VERSIONS_V2, factory_versions: DPP_FACTORY_VERSIONS_V1, From ebcb500e542bf8d2690db616f218a5a11c9f71ff Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 16 Mar 2026 17:23:40 +0700 Subject: [PATCH 03/13] fix(test): update versioned dispatch test for v12 using action_id v1 The test assumed calculate_action_id_versioned should match calculate_action_id (v0) on the current platform version. But v12 uses TOKEN_VERSIONS_V2 which sets token_config_update_action_id_version to 1, so the versioned call correctly uses v1 (full payload hash) while the plain call uses v0 (discriminant only). Updated the test to assert they differ and verify the versioned result matches calculate_action_id_with_fields_v1 directly. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../v0_methods.rs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index 942e96b4d91..2cdfd526239 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -130,6 +130,7 @@ impl TokenConfigUpdateTransition { mod tests { use super::*; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; + use crate::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0; use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; @@ -254,20 +255,35 @@ mod tests { } #[test] - fn versioned_dispatch_uses_v0_on_current_platform_version() { - // On the current platform version (v12), the versioned method should - // produce the same result as the v0 method. + fn versioned_dispatch_uses_v1_on_current_platform_version() { + // On the current platform version (v12), token_config_update_action_id_version + // is 1, so the versioned method should produce the v1 result (which includes + // the full config item payload), NOT the v0 result (discriminant only). let owner_id = Identifier::new([3u8; 32]); let t = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(100))); let platform_version = PlatformVersion::latest(); - let id_plain = t.calculate_action_id(owner_id); + let id_plain_v0 = t.calculate_action_id(owner_id); let id_versioned = t.calculate_action_id_versioned(owner_id, platform_version); + // v1 produces a different id from v0 because it hashes the full payload + assert_ne!( + id_plain_v0, id_versioned, + "on current platform version (v1), versioned should differ from plain (v0)" + ); + + // Verify the versioned result matches v1 directly + let base = t.base(); + let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + t.update_token_configuration_item(), + ); assert_eq!( - id_plain, id_versioned, - "on current platform version, versioned and plain should produce the same id" + id_versioned, id_v1, + "versioned dispatch should use v1 on current platform version" ); } From 740f415c778b8bf386856af3225421576c042c18 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 16 Mar 2026 18:14:57 +0700 Subject: [PATCH 04/13] refactor(dpp): unify calculate_action_id with platform_version and Result Merge calculate_action_id and calculate_action_id_versioned into a single calculate_action_id(owner_id, platform_version) -> Result method. This removes the separate versioned dispatch and makes platform_version a required parameter for all action_id calculations. Changes: - AllowedAsMultiPartyAction::calculate_action_id now takes platform_version and returns Result - Remove calculate_action_id_versioned (merged into calculate_action_id) - Rename calculate_action_id_with_fields to calculate_action_id_with_fields_v0 - Replace panic! with ProtocolError::UnknownVersionMismatch - Update all 7 token transition impls and all callers - Update TokenTransitionV0Methods::calculate_action_id signature Co-Authored-By: Claude Opus 4.6 (1M context) --- .../batched_transition/multi_party_action.rs | 15 +----- .../token_burn_transition/v0/v0_methods.rs | 8 +-- .../token_burn_transition/v0_methods.rs | 8 +-- .../v0/v0_methods.rs | 39 ++++---------- .../v0_methods.rs | 30 ++++------- .../v0/v0_methods.rs | 8 +-- .../v0_methods.rs | 8 +-- .../v0/v0_methods.rs | 8 +-- .../v0_methods.rs | 8 +-- .../token_freeze_transition/v0/v0_methods.rs | 8 +-- .../token_freeze_transition/v0_methods.rs | 8 +-- .../token_mint_transition/v0/v0_methods.rs | 7 +-- .../token_mint_transition/v0_methods.rs | 7 +-- .../v0/v0_methods.rs | 8 +-- .../v0_methods.rs | 8 +-- .../batched_transition/token_transition.rs | 52 ++++--------------- .../v0/v0_methods.rs | 8 +-- .../token_unfreeze_transition/v0_methods.rs | 8 +-- .../batch_transition/v1/v1_methods.rs | 16 +++--- .../validate_basic_structure/v0/mod.rs | 2 +- .../src/execution/check_tx/v0/mod.rs | 2 +- .../batch/tests/token/burn/mod.rs | 4 +- .../batch/tests/token/config_update/mod.rs | 16 +++--- .../batch/tests/token/freeze/mod.rs | 6 +-- .../batch/tests/token/mint/mod.rs | 16 +++--- .../batch/tests/token/transfer/mod.rs | 2 +- 26 files changed, 133 insertions(+), 177 deletions(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs index 273a4b95bcc..692f95d83c3 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs @@ -1,18 +1,7 @@ use platform_value::Identifier; use platform_version::version::PlatformVersion; +use crate::ProtocolError; pub trait AllowedAsMultiPartyAction { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier; - - /// Version-aware action_id calculation. By default, delegates to the - /// non-versioned [`calculate_action_id`](Self::calculate_action_id). - /// Transition types that need version-dependent behaviour (e.g. - /// `TokenConfigUpdateTransition`) override this method. - fn calculate_action_id_versioned( - &self, - owner_id: Identifier, - _platform_version: &PlatformVersion, - ) -> Identifier { - self.calculate_action_id(owner_id) - } + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result; } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs index ee24438ecf4..5eec4f3db9d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -60,16 +62,16 @@ impl TokenBurnTransitionV0Methods for TokenBurnTransitionV0 { } impl AllowedAsMultiPartyAction for TokenBurnTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenBurnTransitionV0 { base, burn_amount, .. } = self; - TokenBurnTransition::calculate_action_id_with_fields( + Ok(TokenBurnTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *burn_amount, - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs index d37a08463fe..70920697b4b 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs @@ -1,6 +1,8 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::balances::credits::TokenAmount; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -61,15 +63,15 @@ impl TokenBurnTransitionV0Methods for TokenBurnTransition { } impl AllowedAsMultiPartyAction for TokenBurnTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenBurnTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenBurnTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } impl TokenBurnTransition { - pub fn calculate_action_id_with_fields( + pub fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs index ae9ead1b500..090fbb72f5d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs @@ -1,6 +1,7 @@ use platform_value::Identifier; use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -71,29 +72,7 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransitionV0 { impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { /// v0 action_id: uses only the u8 discriminant (kept for backward compat). - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { - let TokenConfigUpdateTransitionV0 { - base, - update_token_configuration_item, - .. - } = self; - - TokenConfigUpdateTransition::calculate_action_id_with_fields( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - update_token_configuration_item.u8_item_index(), - ) - } - - /// Version-aware action_id calculation. - /// v0: uses only the u8 discriminant (existing production behavior). - /// v1: includes the full serialized config change item to prevent vote swap. - fn calculate_action_id_versioned( - &self, - owner_id: Identifier, - platform_version: &PlatformVersion, - ) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion,) -> Result { let TokenConfigUpdateTransitionV0 { base, update_token_configuration_item, @@ -105,19 +84,23 @@ impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { .token_versions .token_config_update_action_id_version { - 0 => TokenConfigUpdateTransition::calculate_action_id_with_fields( + 0 => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), update_token_configuration_item.u8_item_index(), - ), - 1 => TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + )), + 1 => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), update_token_configuration_item, - ), - version => panic!("unsupported token_config_update_action_id_version {version}"), + )), + version => Err(ProtocolError::UnknownVersionMismatch { + method: "calculate_action_id".to_string(), + known_versions: vec![0, 1], + received: version, + }), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index 2cdfd526239..7dafa3eac64 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -2,6 +2,7 @@ use platform_value::Identifier; use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -67,21 +68,9 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransition { } impl AllowedAsMultiPartyAction for TokenConfigUpdateTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenConfigUpdateTransition::V0(v0) => v0.calculate_action_id(owner_id), - } - } - - fn calculate_action_id_versioned( - &self, - owner_id: Identifier, - platform_version: &PlatformVersion, - ) -> Identifier { - match self { - TokenConfigUpdateTransition::V0(v0) => { - v0.calculate_action_id_versioned(owner_id, platform_version) - } + TokenConfigUpdateTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } @@ -89,7 +78,7 @@ impl AllowedAsMultiPartyAction for TokenConfigUpdateTransition { impl TokenConfigUpdateTransition { /// v0: action_id uses only the u8 discriminant of the config change item. /// This is kept for backward compatibility with existing production data. - pub fn calculate_action_id_with_fields( + pub fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, @@ -160,8 +149,9 @@ mod tests { 999_999_999_999, ))); - let id_small = t_small.calculate_action_id(owner_id); - let id_large = t_large.calculate_action_id(owner_id); + let platform_version = PlatformVersion::first(); + let id_small = t_small.calculate_action_id(owner_id, platform_version).expect("expected action id"); + let id_large = t_large.calculate_action_id(owner_id, platform_version).expect("expected action id"); // v0: these are EQUAL -- the vulnerability assert_eq!( @@ -238,7 +228,7 @@ mod tests { let nonce = 1u64; let item = TokenConfigurationChangeItem::MaxSupply(Some(100)); - let id_v0 = TokenConfigUpdateTransition::calculate_action_id_with_fields( + let id_v0 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( &token_id, &owner_id, nonce, @@ -264,8 +254,8 @@ mod tests { let platform_version = PlatformVersion::latest(); - let id_plain_v0 = t.calculate_action_id(owner_id); - let id_versioned = t.calculate_action_id_versioned(owner_id, platform_version); + let id_plain_v0 = t.calculate_action_id(owner_id, PlatformVersion::first()).expect("expected action id"); + let id_versioned = t.calculate_action_id(owner_id, platform_version).expect("expected action id"); // v1 produces a different id from v0 because it hashes the full payload assert_ne!( diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs index ccd6c0b24ce..211c4e2b1db 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -61,18 +63,18 @@ impl TokenDestroyFrozenFundsTransitionV0Methods for TokenDestroyFrozenFundsTrans } impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenDestroyFrozenFundsTransitionV0 { base, frozen_identity_id, .. } = self; - TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields( + Ok(TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs index e0560b49498..7e4dc821b9d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -62,15 +64,15 @@ impl TokenDestroyFrozenFundsTransitionV0Methods for TokenDestroyFrozenFundsTrans } impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenDestroyFrozenFundsTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenDestroyFrozenFundsTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } impl TokenDestroyFrozenFundsTransition { - pub fn calculate_action_id_with_fields( + pub fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs index 02abb8e415e..783785f7be1 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_emergency_action_transition::TokenEmergencyActionTransitionV0; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -63,18 +65,18 @@ impl TokenEmergencyActionTransitionV0Methods for TokenEmergencyActionTransitionV } impl AllowedAsMultiPartyAction for TokenEmergencyActionTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenEmergencyActionTransitionV0 { base, emergency_action, .. } = self; - TokenEmergencyActionTransition::calculate_action_id_with_fields( + Ok(TokenEmergencyActionTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *emergency_action, - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs index c57cf214ae2..7538dea53ed 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_emergency_action_transition::v0::v0_methods::TokenEmergencyActionTransitionV0Methods; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -61,15 +63,15 @@ impl TokenEmergencyActionTransitionV0Methods for TokenEmergencyActionTransition } impl AllowedAsMultiPartyAction for TokenEmergencyActionTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenEmergencyActionTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenEmergencyActionTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } impl TokenEmergencyActionTransition { - pub fn calculate_action_id_with_fields( + pub fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs index 78a9555038c..959a26a75ba 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -61,18 +63,18 @@ impl TokenFreezeTransitionV0Methods for TokenFreezeTransitionV0 { } impl AllowedAsMultiPartyAction for TokenFreezeTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenFreezeTransitionV0 { base, identity_to_freeze_id: frozen_identity_id, .. } = self; - TokenFreezeTransition::calculate_action_id_with_fields( + Ok(TokenFreezeTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs index fc4642abefb..47fc225afeb 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -60,15 +62,15 @@ impl TokenFreezeTransitionV0Methods for TokenFreezeTransition { } impl AllowedAsMultiPartyAction for TokenFreezeTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenFreezeTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenFreezeTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } impl TokenFreezeTransition { - pub fn calculate_action_id_with_fields( + pub fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs index 3b3694a442d..44cee2cb31e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs @@ -1,4 +1,5 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; use crate::data_contract::associated_token::token_configuration::TokenConfiguration; use crate::data_contract::associated_token::token_distribution_rules::accessors::v0::TokenDistributionRulesV0Getters; @@ -99,14 +100,14 @@ impl TokenMintTransitionV0Methods for TokenMintTransitionV0 { } impl AllowedAsMultiPartyAction for TokenMintTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenMintTransitionV0 { base, amount, .. } = self; - TokenMintTransition::calculate_action_id_with_fields( + Ok(TokenMintTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *amount, - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs index b956f1f71c4..a1010caae10 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs @@ -1,4 +1,5 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::balances::credits::TokenAmount; use crate::data_contract::associated_token::token_configuration::TokenConfiguration; use crate::prelude::IdentityNonce; @@ -84,15 +85,15 @@ impl TokenMintTransitionV0Methods for TokenMintTransition { } impl AllowedAsMultiPartyAction for TokenMintTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenMintTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenMintTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } impl TokenMintTransition { - pub fn calculate_action_id_with_fields( + pub fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs index 8ec1935292b..132a7600753 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -63,14 +65,14 @@ impl TokenSetPriceForDirectPurchaseTransitionV0Methods } impl AllowedAsMultiPartyAction for TokenSetPriceForDirectPurchaseTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenSetPriceForDirectPurchaseTransitionV0 { base, price, .. } = self; - TokenSetPriceForDirectPurchaseTransition::calculate_action_id_with_fields( + Ok(TokenSetPriceForDirectPurchaseTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), price.as_ref(), - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs index 15409f6009f..640a7d151ca 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -63,15 +65,15 @@ impl TokenSetPriceForDirectPurchaseTransitionV0Methods } impl AllowedAsMultiPartyAction for TokenSetPriceForDirectPurchaseTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenSetPriceForDirectPurchaseTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenSetPriceForDirectPurchaseTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } impl TokenSetPriceForDirectPurchaseTransition { - pub fn calculate_action_id_with_fields( + pub fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs index 6fc8e032c20..8415dd88210 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs @@ -213,17 +213,7 @@ pub trait TokenTransitionV0Methods { /// sets identity contract nonce fn set_identity_contract_nonce(&mut self, nonce: IdentityNonce); - fn calculate_action_id(&self, owner_id: Identifier) -> Option; - - /// Version-aware action_id calculation. Falls back to the non-versioned - /// method for all transitions except `ConfigUpdate`, which dispatches - /// between v0 (discriminant-only) and v1 (full-payload) based on - /// `platform_version`. - fn calculate_action_id_versioned( - &self, - owner_id: Identifier, - platform_version: &PlatformVersion, - ) -> Option; + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Option>; fn can_calculate_action_id(&self) -> bool; /// Historical document type name for the token history contract @@ -289,41 +279,19 @@ impl TokenTransitionV0Methods for TokenTransition { self.base().data_contract_id() } - fn calculate_action_id(&self, owner_id: Identifier) -> Option { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Option> { match self { - TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::Transfer(_) => None, - TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::Claim(_) => None, - TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::ConfigUpdate(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::DirectPurchase(_) => None, - TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id)), - } - } - - fn calculate_action_id_versioned( - &self, - owner_id: Identifier, - platform_version: &PlatformVersion, - ) -> Option { - match self { - TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Transfer(_) => None, - TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Claim(_) => None, - TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::ConfigUpdate(t) => { - Some(t.calculate_action_id_versioned(owner_id, platform_version)) - } + TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::ConfigUpdate(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::DirectPurchase(_) => None, - TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id, platform_version)), } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs index 0559542f7a7..f47edb49fdf 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_unfreeze_transition::TokenUnfreezeTransitionV0; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -61,18 +63,18 @@ impl TokenUnfreezeTransitionV0Methods for TokenUnfreezeTransitionV0 { } impl AllowedAsMultiPartyAction for TokenUnfreezeTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenUnfreezeTransitionV0 { base, frozen_identity_id, .. } = self; - TokenUnfreezeTransition::calculate_action_id_with_fields( + Ok(TokenUnfreezeTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs index b90cf25acbd..e7bfda283f2 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -60,15 +62,15 @@ impl TokenUnfreezeTransitionV0Methods for TokenUnfreezeTransition { } impl AllowedAsMultiPartyAction for TokenUnfreezeTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenUnfreezeTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenUnfreezeTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } impl TokenUnfreezeTransition { - pub fn calculate_action_id_with_fields( + pub fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs index 408ab890dc0..f272765624b 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs @@ -113,7 +113,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = mint_transition.calculate_action_id(owner_id); + let action_id = mint_transition.calculate_action_id(owner_id, _platform_version)?; mint_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -188,7 +188,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = burn_transition.calculate_action_id(owner_id); + let action_id = burn_transition.calculate_action_id(owner_id, _platform_version)?; burn_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -329,7 +329,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = freeze_transition.calculate_action_id(owner_id); + let action_id = freeze_transition.calculate_action_id(owner_id, _platform_version)?; freeze_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -406,7 +406,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = unfreeze_transition.calculate_action_id(owner_id); + let action_id = unfreeze_transition.calculate_action_id(owner_id, _platform_version)?; unfreeze_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -484,7 +484,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = destroy_frozen_funds_transition.calculate_action_id(owner_id); + let action_id = destroy_frozen_funds_transition.calculate_action_id(owner_id, _platform_version)?; destroy_frozen_funds_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -563,7 +563,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = emergency_action_transition.calculate_action_id(owner_id); + let action_id = emergency_action_transition.calculate_action_id(owner_id, _platform_version)?; emergency_action_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -641,7 +641,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { group_contract_position, ) => { let action_id = config_update_transition - .calculate_action_id_versioned(owner_id, _platform_version); + .calculate_action_id(owner_id, _platform_version)?; config_update_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -774,7 +774,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { group_contract_position, ) => { let action_id = - change_direct_purchase_price_transition.calculate_action_id(owner_id); + change_direct_purchase_price_transition.calculate_action_id(owner_id, _platform_version)?; change_direct_purchase_price_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs index 70308e30618..e34401a353c 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs @@ -191,7 +191,7 @@ impl BatchTransition { if let Some(group_state_transition_info) = transition.base().using_group_info() { if group_state_transition_info.action_is_proposer { if let Some(calculated_action_id) = - transition.calculate_action_id_versioned(self.owner_id(), platform_version) + transition.calculate_action_id(self.owner_id(), platform_version).transpose()? { if group_state_transition_info.action_id != calculated_action_id { result.add_error(BasicError::InvalidActionIdError( diff --git a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs index acacd8cb07b..32d513d83e2 100644 --- a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs @@ -3712,7 +3712,7 @@ mod tests { .unwrap() .expect("expected to commit transaction"); - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs index b907841dec3..f2fef674d0b 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs @@ -456,7 +456,7 @@ mod token_burn_tests { assert_eq!(token_balance, Some(expected_amount)); // Step 4: Confirm burn by second group member - let action_id = TokenBurnTransition::calculate_action_id_with_fields( + let action_id = TokenBurnTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity1.id().as_bytes(), 2, @@ -715,7 +715,7 @@ mod token_burn_tests { assert_eq!(token_balance, Some(expected_amount)); // Step 3: Confirm burn by second group member - let action_id = TokenBurnTransition::calculate_action_id_with_fields( + let action_id = TokenBurnTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity1.id().as_bytes(), 2, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/config_update/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/config_update/mod.rs index 071d5e6555f..e1f672dd11f 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/config_update/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/config_update/mod.rs @@ -1078,7 +1078,7 @@ mod token_config_update_tests { platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -1293,7 +1293,7 @@ mod token_config_update_tests { platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -1461,7 +1461,7 @@ mod token_config_update_tests { // now that we are group 0 as the group that can make the change, let's make the change to allow minting to choose the destination - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 3, @@ -1684,7 +1684,7 @@ mod token_config_update_tests { platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -1909,7 +1909,7 @@ mod token_config_update_tests { // Let's try doing this action with group 1 - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity_3.id().as_bytes(), 2, @@ -2146,7 +2146,7 @@ mod token_config_update_tests { platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity_3.id().as_bytes(), 2, @@ -2433,7 +2433,7 @@ mod token_config_update_tests { // Now let's have Group 0 change the control of the conventions to identity 2 only let action_id_change_control = - TokenConfigUpdateTransition::calculate_action_id_with_fields( + TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2577,7 +2577,7 @@ mod token_config_update_tests { // Now let's have Group 0 hand it back to Group 1 - let action_id_return = TokenConfigUpdateTransition::calculate_action_id_with_fields( + let action_id_return = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 3, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs index e313b5fcb0d..4c9bd0940f8 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs @@ -1431,7 +1431,7 @@ mod token_freeze_tests { } // second signer - let action_id = TokenFreezeTransition::calculate_action_id_with_fields( + let action_id = TokenFreezeTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), id1.id().as_bytes(), 2, @@ -1775,7 +1775,7 @@ mod token_freeze_tests { .expect("expected to commit transaction"); // second signer - let action_id = TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields( + let action_id = TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), id1.id().as_bytes(), 4, @@ -2223,7 +2223,7 @@ mod token_freeze_tests { .expect("expected to commit transaction"); // second signer - let action_id = TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields( + let action_id = TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), id1.id().as_bytes(), 4, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs index 7c27ceea35c..657b0bec7d5 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs @@ -1602,7 +1602,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -1868,7 +1868,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2191,7 +2191,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2411,7 +2411,7 @@ mod token_mint_tests { // Now we need to get the second identity to also sign it, but we are going to resubmit with first // This will create an error - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2630,7 +2630,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2919,7 +2919,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -3335,7 +3335,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -3472,7 +3472,7 @@ mod token_mint_tests { ); // The second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/transfer/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/transfer/mod.rs index d13f0051742..0ae291586a1 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/transfer/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/transfer/mod.rs @@ -1080,7 +1080,7 @@ mod token_transfer_tests { .unwrap() .expect("expected to commit transaction"); - let action_id = TokenMintTransition::calculate_action_id_with_fields( + let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( token_id.as_bytes(), identity.id().as_bytes(), 2, From fb77572e3b980901d9df7d237567454a846cbb3d Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 16 Mar 2026 18:36:43 +0700 Subject: [PATCH 05/13] refactor(dpp): keep AllowedAsMultiPartyAction trait simple, dispatch at call site MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert AllowedAsMultiPartyAction trait back to the simple signature: fn calculate_action_id(&self, owner_id: Identifier) -> Identifier The v0/v1 version dispatch for TokenConfigUpdateTransition now happens at the call site (v1_methods.rs) rather than inside the trait impl. Key changes: - Trait has no platform_version parameter, no Result return - All 7 non-config-update transitions use simple calculate_action_id - calculate_action_id_with_fields_v1 takes (u8, Option<&[u8]>) instead of &TokenConfigurationChangeItem — callers serialize and pass payload - Added calculate_action_id_with_platform_version on TokenTransition for use in validation where version-aware calculation is needed - Version dispatch in v1_methods.rs ConfigUpdate arm only Co-Authored-By: Claude Opus 4.6 (1M context) --- .../batched_transition/multi_party_action.rs | 4 +- .../token_burn_transition/v0/v0_methods.rs | 8 +- .../token_burn_transition/v0_methods.rs | 6 +- .../v0/v0_methods.rs | 33 ++---- .../v0_methods.rs | 107 +++++++++--------- .../v0/v0_methods.rs | 8 +- .../v0_methods.rs | 6 +- .../v0/v0_methods.rs | 8 +- .../v0_methods.rs | 6 +- .../token_freeze_transition/v0/v0_methods.rs | 8 +- .../token_freeze_transition/v0_methods.rs | 6 +- .../token_mint_transition/v0/v0_methods.rs | 7 +- .../token_mint_transition/v0_methods.rs | 5 +- .../v0/v0_methods.rs | 8 +- .../v0_methods.rs | 6 +- .../batched_transition/token_transition.rs | 64 +++++++++-- .../v0/v0_methods.rs | 8 +- .../token_unfreeze_transition/v0_methods.rs | 6 +- .../batch_transition/v1/v1_methods.rs | 46 ++++++-- .../validate_basic_structure/v0/mod.rs | 2 +- 20 files changed, 186 insertions(+), 166 deletions(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs index 692f95d83c3..52f7cf4e7f4 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs @@ -1,7 +1,5 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; -use crate::ProtocolError; pub trait AllowedAsMultiPartyAction { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result; + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier; } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs index 5eec4f3db9d..95720912e1e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs @@ -1,6 +1,4 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -62,16 +60,16 @@ impl TokenBurnTransitionV0Methods for TokenBurnTransitionV0 { } impl AllowedAsMultiPartyAction for TokenBurnTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenBurnTransitionV0 { base, burn_amount, .. } = self; - Ok(TokenBurnTransition::calculate_action_id_with_fields_v0( + TokenBurnTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *burn_amount, - )) + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs index 70920697b4b..896ec217dd6 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs @@ -1,8 +1,6 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::balances::credits::TokenAmount; use crate::prelude::IdentityNonce; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -63,9 +61,9 @@ impl TokenBurnTransitionV0Methods for TokenBurnTransition { } impl AllowedAsMultiPartyAction for TokenBurnTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { match self { - TokenBurnTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenBurnTransition::V0(v0) => v0.calculate_action_id(owner_id), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs index 090fbb72f5d..8c21fc2db0e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs @@ -1,7 +1,5 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -72,35 +70,18 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransitionV0 { impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { /// v0 action_id: uses only the u8 discriminant (kept for backward compat). - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion,) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenConfigUpdateTransitionV0 { base, update_token_configuration_item, .. } = self; - match platform_version - .dpp - .token_versions - .token_config_update_action_id_version - { - 0 => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - update_token_configuration_item.u8_item_index(), - )), - 1 => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - update_token_configuration_item, - )), - version => Err(ProtocolError::UnknownVersionMismatch { - method: "calculate_action_id".to_string(), - known_versions: vec![0, 1], - received: version, - }), - } + TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + update_token_configuration_item.u8_item_index(), + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index 7dafa3eac64..f6b7467308f 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -1,8 +1,6 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use crate::prelude::IdentityNonce; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -68,9 +66,9 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransition { } impl AllowedAsMultiPartyAction for TokenConfigUpdateTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { match self { - TokenConfigUpdateTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenConfigUpdateTransition::V0(v0) => v0.calculate_action_id(owner_id), } } } @@ -93,23 +91,24 @@ impl TokenConfigUpdateTransition { hash_double(bytes).into() } - /// v1: action_id includes the full serialized config change item, binding - /// the voted-on value into the hash and preventing vote-swap attacks. + /// v1: action_id includes the u8 discriminant and the full serialized + /// config change item payload, binding the voted-on value into the hash + /// and preventing vote-swap attacks. pub fn calculate_action_id_with_fields_v1( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, - update_token_configuration_item: &TokenConfigurationChangeItem, + update_token_config_item: u8, + payload: Option<&[u8]>, ) -> Identifier { - let serialized_item = - bincode::encode_to_vec(update_token_configuration_item, bincode::config::standard()) - .expect("expected to encode token configuration change item"); - let mut bytes = b"action_token_config_update".to_vec(); bytes.extend_from_slice(token_id); bytes.extend_from_slice(owner_id); bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); - bytes.extend_from_slice(&serialized_item); + bytes.push(update_token_config_item); + if let Some(payload) = payload { + bytes.extend_from_slice(payload); + } hash_double(bytes).into() } @@ -119,7 +118,6 @@ impl TokenConfigUpdateTransition { mod tests { use super::*; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; - use crate::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0; use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; @@ -137,6 +135,11 @@ mod tests { }) } + fn serialize_item(item: &TokenConfigurationChangeItem) -> Vec { + bincode::encode_to_vec(item, bincode::config::standard()) + .expect("expected to encode token configuration change item") + } + #[test] fn v0_action_id_same_discriminant_different_values_produces_same_id_vulnerability() { // This test documents the v0 vulnerability: two different MaxSupply @@ -149,9 +152,8 @@ mod tests { 999_999_999_999, ))); - let platform_version = PlatformVersion::first(); - let id_small = t_small.calculate_action_id(owner_id, platform_version).expect("expected action id"); - let id_large = t_large.calculate_action_id(owner_id, platform_version).expect("expected action id"); + let id_small = t_small.calculate_action_id(owner_id); + let id_large = t_large.calculate_action_id(owner_id); // v0: these are EQUAL -- the vulnerability assert_eq!( @@ -171,17 +173,22 @@ mod tests { let item_small = TokenConfigurationChangeItem::MaxSupply(Some(100)); let item_large = TokenConfigurationChangeItem::MaxSupply(Some(999_999_999_999)); + let payload_small = serialize_item(&item_small); + let payload_large = serialize_item(&item_large); + let id_small = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - &item_small, + item_small.u8_item_index(), + Some(&payload_small), ); let id_large = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - &item_large, + item_large.u8_item_index(), + Some(&payload_large), ); // v1: these must be DIFFERENT -- the fix @@ -200,17 +207,22 @@ mod tests { let item_max_supply = TokenConfigurationChangeItem::MaxSupply(Some(100)); let item_allow_dest = TokenConfigurationChangeItem::MintingAllowChoosingDestination(true); + let payload_max = serialize_item(&item_max_supply); + let payload_dest = serialize_item(&item_allow_dest); + let id_max = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - &item_max_supply, + item_max_supply.u8_item_index(), + Some(&payload_max), ); let id_dest = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - &item_allow_dest, + item_allow_dest.u8_item_index(), + Some(&payload_dest), ); assert_ne!( @@ -227,6 +239,7 @@ mod tests { let owner_id = [3u8; 32]; let nonce = 1u64; let item = TokenConfigurationChangeItem::MaxSupply(Some(100)); + let payload = serialize_item(&item); let id_v0 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( &token_id, @@ -235,7 +248,11 @@ mod tests { item.u8_item_index(), ); let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, &item, + &token_id, + &owner_id, + nonce, + item.u8_item_index(), + Some(&payload), ); assert_ne!( @@ -244,39 +261,6 @@ mod tests { ); } - #[test] - fn versioned_dispatch_uses_v1_on_current_platform_version() { - // On the current platform version (v12), token_config_update_action_id_version - // is 1, so the versioned method should produce the v1 result (which includes - // the full config item payload), NOT the v0 result (discriminant only). - let owner_id = Identifier::new([3u8; 32]); - let t = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(100))); - - let platform_version = PlatformVersion::latest(); - - let id_plain_v0 = t.calculate_action_id(owner_id, PlatformVersion::first()).expect("expected action id"); - let id_versioned = t.calculate_action_id(owner_id, platform_version).expect("expected action id"); - - // v1 produces a different id from v0 because it hashes the full payload - assert_ne!( - id_plain_v0, id_versioned, - "on current platform version (v1), versioned should differ from plain (v0)" - ); - - // Verify the versioned result matches v1 directly - let base = t.base(); - let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - t.update_token_configuration_item(), - ); - assert_eq!( - id_versioned, id_v1, - "versioned dispatch should use v1 on current platform version" - ); - } - #[test] fn v1_action_id_identical_items_produces_same_id() { // Sanity check: identical config items should produce the same @@ -288,11 +272,22 @@ mod tests { let item1 = TokenConfigurationChangeItem::MaxSupply(Some(42)); let item2 = TokenConfigurationChangeItem::MaxSupply(Some(42)); + let payload1 = serialize_item(&item1); + let payload2 = serialize_item(&item2); + let id1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, &item1, + &token_id, + &owner_id, + nonce, + item1.u8_item_index(), + Some(&payload1), ); let id2 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, &item2, + &token_id, + &owner_id, + nonce, + item2.u8_item_index(), + Some(&payload2), ); assert_eq!( diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs index 211c4e2b1db..d154024166e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs @@ -1,6 +1,4 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -63,18 +61,18 @@ impl TokenDestroyFrozenFundsTransitionV0Methods for TokenDestroyFrozenFundsTrans } impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenDestroyFrozenFundsTransitionV0 { base, frozen_identity_id, .. } = self; - Ok(TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( + TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - )) + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs index 7e4dc821b9d..877410603bc 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs @@ -1,7 +1,5 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -64,9 +62,9 @@ impl TokenDestroyFrozenFundsTransitionV0Methods for TokenDestroyFrozenFundsTrans } impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { match self { - TokenDestroyFrozenFundsTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenDestroyFrozenFundsTransition::V0(v0) => v0.calculate_action_id(owner_id), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs index 783785f7be1..7cd16ae8d68 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs @@ -1,6 +1,4 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_emergency_action_transition::TokenEmergencyActionTransitionV0; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -65,18 +63,18 @@ impl TokenEmergencyActionTransitionV0Methods for TokenEmergencyActionTransitionV } impl AllowedAsMultiPartyAction for TokenEmergencyActionTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenEmergencyActionTransitionV0 { base, emergency_action, .. } = self; - Ok(TokenEmergencyActionTransition::calculate_action_id_with_fields_v0( + TokenEmergencyActionTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *emergency_action, - )) + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs index 7538dea53ed..2b2ae822736 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs @@ -1,7 +1,5 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_emergency_action_transition::v0::v0_methods::TokenEmergencyActionTransitionV0Methods; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -63,9 +61,9 @@ impl TokenEmergencyActionTransitionV0Methods for TokenEmergencyActionTransition } impl AllowedAsMultiPartyAction for TokenEmergencyActionTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { match self { - TokenEmergencyActionTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenEmergencyActionTransition::V0(v0) => v0.calculate_action_id(owner_id), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs index 959a26a75ba..2974f8d8d53 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs @@ -1,6 +1,4 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -63,18 +61,18 @@ impl TokenFreezeTransitionV0Methods for TokenFreezeTransitionV0 { } impl AllowedAsMultiPartyAction for TokenFreezeTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenFreezeTransitionV0 { base, identity_to_freeze_id: frozen_identity_id, .. } = self; - Ok(TokenFreezeTransition::calculate_action_id_with_fields_v0( + TokenFreezeTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - )) + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs index 47fc225afeb..44fe90808b5 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs @@ -1,7 +1,5 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -62,9 +60,9 @@ impl TokenFreezeTransitionV0Methods for TokenFreezeTransition { } impl AllowedAsMultiPartyAction for TokenFreezeTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { match self { - TokenFreezeTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenFreezeTransition::V0(v0) => v0.calculate_action_id(owner_id), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs index 44cee2cb31e..23baabaec03 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs @@ -1,5 +1,4 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; use crate::data_contract::associated_token::token_configuration::TokenConfiguration; use crate::data_contract::associated_token::token_distribution_rules::accessors::v0::TokenDistributionRulesV0Getters; @@ -100,14 +99,14 @@ impl TokenMintTransitionV0Methods for TokenMintTransitionV0 { } impl AllowedAsMultiPartyAction for TokenMintTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenMintTransitionV0 { base, amount, .. } = self; - Ok(TokenMintTransition::calculate_action_id_with_fields_v0( + TokenMintTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *amount, - )) + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs index a1010caae10..d8c678957a0 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs @@ -1,5 +1,4 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::balances::credits::TokenAmount; use crate::data_contract::associated_token::token_configuration::TokenConfiguration; use crate::prelude::IdentityNonce; @@ -85,9 +84,9 @@ impl TokenMintTransitionV0Methods for TokenMintTransition { } impl AllowedAsMultiPartyAction for TokenMintTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { match self { - TokenMintTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenMintTransition::V0(v0) => v0.calculate_action_id(owner_id), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs index 132a7600753..90aa51019e4 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs @@ -1,6 +1,4 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -65,14 +63,14 @@ impl TokenSetPriceForDirectPurchaseTransitionV0Methods } impl AllowedAsMultiPartyAction for TokenSetPriceForDirectPurchaseTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenSetPriceForDirectPurchaseTransitionV0 { base, price, .. } = self; - Ok(TokenSetPriceForDirectPurchaseTransition::calculate_action_id_with_fields_v0( + TokenSetPriceForDirectPurchaseTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), price.as_ref(), - )) + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs index 640a7d151ca..a04e074deae 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs @@ -1,7 +1,5 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -65,9 +63,9 @@ impl TokenSetPriceForDirectPurchaseTransitionV0Methods } impl AllowedAsMultiPartyAction for TokenSetPriceForDirectPurchaseTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { match self { - TokenSetPriceForDirectPurchaseTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenSetPriceForDirectPurchaseTransition::V0(v0) => v0.calculate_action_id(owner_id), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs index 8415dd88210..a7ca06f9d56 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs @@ -213,7 +213,16 @@ pub trait TokenTransitionV0Methods { /// sets identity contract nonce fn set_identity_contract_nonce(&mut self, nonce: IdentityNonce); - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Option>; + fn calculate_action_id(&self, owner_id: Identifier) -> Option; + + /// Version-aware action_id calculation. For most variants this delegates to + /// `calculate_action_id`. For `ConfigUpdate` it uses `platform_version` to + /// choose between v0 (discriminant only) and v1 (discriminant + payload). + fn calculate_action_id_with_platform_version( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Option>; fn can_calculate_action_id(&self) -> bool; /// Historical document type name for the token history contract @@ -279,19 +288,54 @@ impl TokenTransitionV0Methods for TokenTransition { self.base().data_contract_id() } - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Option> { + fn calculate_action_id(&self, owner_id: Identifier) -> Option { match self { - TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id, platform_version)), - TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id, platform_version)), - TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id, platform_version)), - TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id)), TokenTransition::Transfer(_) => None, - TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id)), TokenTransition::Claim(_) => None, - TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id, platform_version)), - TokenTransition::ConfigUpdate(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::ConfigUpdate(t) => Some(t.calculate_action_id(owner_id)), TokenTransition::DirectPurchase(_) => None, - TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id)), + } + } + + fn calculate_action_id_with_platform_version( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Option> { + match self { + TokenTransition::ConfigUpdate(t) => { + let base = t.base(); + let item = t.update_token_configuration_item(); + Some(match platform_version.dpp.token_versions.token_config_update_action_id_version { + 0 => Ok(t.calculate_action_id(owner_id)), + 1 => { + match bincode::encode_to_vec(item, bincode::config::standard()) { + Ok(serialized) => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + item.u8_item_index(), + Some(&serialized), + )), + Err(e) => Err(ProtocolError::EncodingError(e.to_string())), + } + } + version => Err(ProtocolError::UnknownVersionMismatch { + method: "token_config_update_action_id".to_string(), + known_versions: vec![0, 1], + received: version, + }), + }) + } + // For all other variants, delegate to the simple trait method + other => other.calculate_action_id(owner_id).map(Ok), } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs index f47edb49fdf..9ef9c37b511 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs @@ -1,6 +1,4 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_unfreeze_transition::TokenUnfreezeTransitionV0; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -63,18 +61,18 @@ impl TokenUnfreezeTransitionV0Methods for TokenUnfreezeTransitionV0 { } impl AllowedAsMultiPartyAction for TokenUnfreezeTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { let TokenUnfreezeTransitionV0 { base, frozen_identity_id, .. } = self; - Ok(TokenUnfreezeTransition::calculate_action_id_with_fields_v0( + TokenUnfreezeTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - )) + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs index e7bfda283f2..18acce6f365 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs @@ -1,7 +1,5 @@ use platform_value::Identifier; -use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; -use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -62,9 +60,9 @@ impl TokenUnfreezeTransitionV0Methods for TokenUnfreezeTransition { } impl AllowedAsMultiPartyAction for TokenUnfreezeTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { match self { - TokenUnfreezeTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenUnfreezeTransition::V0(v0) => v0.calculate_action_id(owner_id), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs index f272765624b..e144be78ea3 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs @@ -53,6 +53,8 @@ use crate::state_transition::batch_transition::token_claim_transition::TokenClai #[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; #[cfg(feature = "state-transition-signing")] +use crate::state_transition::batch_transition::token_config_update_transition::v0::v0_methods::TokenConfigUpdateTransitionV0Methods; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_destroy_frozen_funds_transition::TokenDestroyFrozenFundsTransitionV0; #[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_direct_purchase_transition::TokenDirectPurchaseTransitionV0; @@ -113,7 +115,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = mint_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = mint_transition.calculate_action_id(owner_id); mint_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -188,7 +190,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = burn_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = burn_transition.calculate_action_id(owner_id); burn_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -329,7 +331,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = freeze_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = freeze_transition.calculate_action_id(owner_id); freeze_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -406,7 +408,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = unfreeze_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = unfreeze_transition.calculate_action_id(owner_id); unfreeze_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -484,7 +486,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = destroy_frozen_funds_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = destroy_frozen_funds_transition.calculate_action_id(owner_id); destroy_frozen_funds_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -563,7 +565,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = emergency_action_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = emergency_action_transition.calculate_action_id(owner_id); emergency_action_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -640,8 +642,34 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = config_update_transition - .calculate_action_id(owner_id, _platform_version)?; + let action_id = match _platform_version + .dpp + .token_versions + .token_config_update_action_id_version + { + 0 => Ok(config_update_transition.calculate_action_id(owner_id)), + 1 => { + let base = config_update_transition.base(); + let item = config_update_transition.update_token_configuration_item(); + let serialized = + bincode::encode_to_vec(item, bincode::config::standard()) + .map_err(|e| ProtocolError::EncodingError(e.to_string()))?; + Ok( + TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + item.u8_item_index(), + Some(&serialized), + ), + ) + } + version => Err(ProtocolError::UnknownVersionMismatch { + method: "token_config_update_action_id".to_string(), + known_versions: vec![0, 1], + received: version, + }), + }?; config_update_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -774,7 +802,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { group_contract_position, ) => { let action_id = - change_direct_purchase_price_transition.calculate_action_id(owner_id, _platform_version)?; + change_direct_purchase_price_transition.calculate_action_id(owner_id); change_direct_purchase_price_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs index e34401a353c..c3fd413cb86 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs @@ -191,7 +191,7 @@ impl BatchTransition { if let Some(group_state_transition_info) = transition.base().using_group_info() { if group_state_transition_info.action_is_proposer { if let Some(calculated_action_id) = - transition.calculate_action_id(self.owner_id(), platform_version).transpose()? + transition.calculate_action_id_with_platform_version(self.owner_id(), platform_version).transpose()? { if group_state_transition_info.action_id != calculated_action_id { result.add_error(BasicError::InvalidActionIdError( From ba261c22ca2e179479d227b0586696a88faebd2e Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 16 Mar 2026 18:42:12 +0700 Subject: [PATCH 06/13] Revert "refactor(dpp): keep AllowedAsMultiPartyAction trait simple, dispatch at call site" This reverts commit fb77572e3. Restoring the unified calculate_action_id with platform_version and Result return type as designed. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../batched_transition/multi_party_action.rs | 4 +- .../token_burn_transition/v0/v0_methods.rs | 8 +- .../token_burn_transition/v0_methods.rs | 6 +- .../v0/v0_methods.rs | 33 ++++-- .../v0_methods.rs | 107 +++++++++--------- .../v0/v0_methods.rs | 8 +- .../v0_methods.rs | 6 +- .../v0/v0_methods.rs | 8 +- .../v0_methods.rs | 6 +- .../token_freeze_transition/v0/v0_methods.rs | 8 +- .../token_freeze_transition/v0_methods.rs | 6 +- .../token_mint_transition/v0/v0_methods.rs | 7 +- .../token_mint_transition/v0_methods.rs | 5 +- .../v0/v0_methods.rs | 8 +- .../v0_methods.rs | 6 +- .../batched_transition/token_transition.rs | 64 ++--------- .../v0/v0_methods.rs | 8 +- .../token_unfreeze_transition/v0_methods.rs | 6 +- .../batch_transition/v1/v1_methods.rs | 46 ++------ .../validate_basic_structure/v0/mod.rs | 2 +- 20 files changed, 166 insertions(+), 186 deletions(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs index 52f7cf4e7f4..692f95d83c3 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; pub trait AllowedAsMultiPartyAction { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier; + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result; } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs index 95720912e1e..5eec4f3db9d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -60,16 +62,16 @@ impl TokenBurnTransitionV0Methods for TokenBurnTransitionV0 { } impl AllowedAsMultiPartyAction for TokenBurnTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenBurnTransitionV0 { base, burn_amount, .. } = self; - TokenBurnTransition::calculate_action_id_with_fields_v0( + Ok(TokenBurnTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *burn_amount, - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs index 896ec217dd6..70920697b4b 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs @@ -1,6 +1,8 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::balances::credits::TokenAmount; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -61,9 +63,9 @@ impl TokenBurnTransitionV0Methods for TokenBurnTransition { } impl AllowedAsMultiPartyAction for TokenBurnTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenBurnTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenBurnTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs index 8c21fc2db0e..090fbb72f5d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -70,18 +72,35 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransitionV0 { impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { /// v0 action_id: uses only the u8 discriminant (kept for backward compat). - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion,) -> Result { let TokenConfigUpdateTransitionV0 { base, update_token_configuration_item, .. } = self; - TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - update_token_configuration_item.u8_item_index(), - ) + match platform_version + .dpp + .token_versions + .token_config_update_action_id_version + { + 0 => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + update_token_configuration_item.u8_item_index(), + )), + 1 => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + update_token_configuration_item, + )), + version => Err(ProtocolError::UnknownVersionMismatch { + method: "calculate_action_id".to_string(), + known_versions: vec![0, 1], + received: version, + }), + } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index f6b7467308f..7dafa3eac64 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -1,6 +1,8 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -66,9 +68,9 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransition { } impl AllowedAsMultiPartyAction for TokenConfigUpdateTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenConfigUpdateTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenConfigUpdateTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } @@ -91,24 +93,23 @@ impl TokenConfigUpdateTransition { hash_double(bytes).into() } - /// v1: action_id includes the u8 discriminant and the full serialized - /// config change item payload, binding the voted-on value into the hash - /// and preventing vote-swap attacks. + /// v1: action_id includes the full serialized config change item, binding + /// the voted-on value into the hash and preventing vote-swap attacks. pub fn calculate_action_id_with_fields_v1( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, - update_token_config_item: u8, - payload: Option<&[u8]>, + update_token_configuration_item: &TokenConfigurationChangeItem, ) -> Identifier { + let serialized_item = + bincode::encode_to_vec(update_token_configuration_item, bincode::config::standard()) + .expect("expected to encode token configuration change item"); + let mut bytes = b"action_token_config_update".to_vec(); bytes.extend_from_slice(token_id); bytes.extend_from_slice(owner_id); bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); - bytes.push(update_token_config_item); - if let Some(payload) = payload { - bytes.extend_from_slice(payload); - } + bytes.extend_from_slice(&serialized_item); hash_double(bytes).into() } @@ -118,6 +119,7 @@ impl TokenConfigUpdateTransition { mod tests { use super::*; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; + use crate::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0; use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; @@ -135,11 +137,6 @@ mod tests { }) } - fn serialize_item(item: &TokenConfigurationChangeItem) -> Vec { - bincode::encode_to_vec(item, bincode::config::standard()) - .expect("expected to encode token configuration change item") - } - #[test] fn v0_action_id_same_discriminant_different_values_produces_same_id_vulnerability() { // This test documents the v0 vulnerability: two different MaxSupply @@ -152,8 +149,9 @@ mod tests { 999_999_999_999, ))); - let id_small = t_small.calculate_action_id(owner_id); - let id_large = t_large.calculate_action_id(owner_id); + let platform_version = PlatformVersion::first(); + let id_small = t_small.calculate_action_id(owner_id, platform_version).expect("expected action id"); + let id_large = t_large.calculate_action_id(owner_id, platform_version).expect("expected action id"); // v0: these are EQUAL -- the vulnerability assert_eq!( @@ -173,22 +171,17 @@ mod tests { let item_small = TokenConfigurationChangeItem::MaxSupply(Some(100)); let item_large = TokenConfigurationChangeItem::MaxSupply(Some(999_999_999_999)); - let payload_small = serialize_item(&item_small); - let payload_large = serialize_item(&item_large); - let id_small = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - item_small.u8_item_index(), - Some(&payload_small), + &item_small, ); let id_large = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - item_large.u8_item_index(), - Some(&payload_large), + &item_large, ); // v1: these must be DIFFERENT -- the fix @@ -207,22 +200,17 @@ mod tests { let item_max_supply = TokenConfigurationChangeItem::MaxSupply(Some(100)); let item_allow_dest = TokenConfigurationChangeItem::MintingAllowChoosingDestination(true); - let payload_max = serialize_item(&item_max_supply); - let payload_dest = serialize_item(&item_allow_dest); - let id_max = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - item_max_supply.u8_item_index(), - Some(&payload_max), + &item_max_supply, ); let id_dest = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - item_allow_dest.u8_item_index(), - Some(&payload_dest), + &item_allow_dest, ); assert_ne!( @@ -239,7 +227,6 @@ mod tests { let owner_id = [3u8; 32]; let nonce = 1u64; let item = TokenConfigurationChangeItem::MaxSupply(Some(100)); - let payload = serialize_item(&item); let id_v0 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( &token_id, @@ -248,11 +235,7 @@ mod tests { item.u8_item_index(), ); let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item.u8_item_index(), - Some(&payload), + &token_id, &owner_id, nonce, &item, ); assert_ne!( @@ -261,6 +244,39 @@ mod tests { ); } + #[test] + fn versioned_dispatch_uses_v1_on_current_platform_version() { + // On the current platform version (v12), token_config_update_action_id_version + // is 1, so the versioned method should produce the v1 result (which includes + // the full config item payload), NOT the v0 result (discriminant only). + let owner_id = Identifier::new([3u8; 32]); + let t = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(100))); + + let platform_version = PlatformVersion::latest(); + + let id_plain_v0 = t.calculate_action_id(owner_id, PlatformVersion::first()).expect("expected action id"); + let id_versioned = t.calculate_action_id(owner_id, platform_version).expect("expected action id"); + + // v1 produces a different id from v0 because it hashes the full payload + assert_ne!( + id_plain_v0, id_versioned, + "on current platform version (v1), versioned should differ from plain (v0)" + ); + + // Verify the versioned result matches v1 directly + let base = t.base(); + let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + t.update_token_configuration_item(), + ); + assert_eq!( + id_versioned, id_v1, + "versioned dispatch should use v1 on current platform version" + ); + } + #[test] fn v1_action_id_identical_items_produces_same_id() { // Sanity check: identical config items should produce the same @@ -272,22 +288,11 @@ mod tests { let item1 = TokenConfigurationChangeItem::MaxSupply(Some(42)); let item2 = TokenConfigurationChangeItem::MaxSupply(Some(42)); - let payload1 = serialize_item(&item1); - let payload2 = serialize_item(&item2); - let id1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item1.u8_item_index(), - Some(&payload1), + &token_id, &owner_id, nonce, &item1, ); let id2 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item2.u8_item_index(), - Some(&payload2), + &token_id, &owner_id, nonce, &item2, ); assert_eq!( diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs index d154024166e..211c4e2b1db 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -61,18 +63,18 @@ impl TokenDestroyFrozenFundsTransitionV0Methods for TokenDestroyFrozenFundsTrans } impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenDestroyFrozenFundsTransitionV0 { base, frozen_identity_id, .. } = self; - TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( + Ok(TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs index 877410603bc..7e4dc821b9d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -62,9 +64,9 @@ impl TokenDestroyFrozenFundsTransitionV0Methods for TokenDestroyFrozenFundsTrans } impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenDestroyFrozenFundsTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenDestroyFrozenFundsTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs index 7cd16ae8d68..783785f7be1 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_emergency_action_transition::TokenEmergencyActionTransitionV0; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -63,18 +65,18 @@ impl TokenEmergencyActionTransitionV0Methods for TokenEmergencyActionTransitionV } impl AllowedAsMultiPartyAction for TokenEmergencyActionTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenEmergencyActionTransitionV0 { base, emergency_action, .. } = self; - TokenEmergencyActionTransition::calculate_action_id_with_fields_v0( + Ok(TokenEmergencyActionTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *emergency_action, - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs index 2b2ae822736..7538dea53ed 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_emergency_action_transition::v0::v0_methods::TokenEmergencyActionTransitionV0Methods; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -61,9 +63,9 @@ impl TokenEmergencyActionTransitionV0Methods for TokenEmergencyActionTransition } impl AllowedAsMultiPartyAction for TokenEmergencyActionTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenEmergencyActionTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenEmergencyActionTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs index 2974f8d8d53..959a26a75ba 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -61,18 +63,18 @@ impl TokenFreezeTransitionV0Methods for TokenFreezeTransitionV0 { } impl AllowedAsMultiPartyAction for TokenFreezeTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenFreezeTransitionV0 { base, identity_to_freeze_id: frozen_identity_id, .. } = self; - TokenFreezeTransition::calculate_action_id_with_fields_v0( + Ok(TokenFreezeTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs index 44fe90808b5..47fc225afeb 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -60,9 +62,9 @@ impl TokenFreezeTransitionV0Methods for TokenFreezeTransition { } impl AllowedAsMultiPartyAction for TokenFreezeTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenFreezeTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenFreezeTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs index 23baabaec03..44cee2cb31e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs @@ -1,4 +1,5 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; use crate::data_contract::associated_token::token_configuration::TokenConfiguration; use crate::data_contract::associated_token::token_distribution_rules::accessors::v0::TokenDistributionRulesV0Getters; @@ -99,14 +100,14 @@ impl TokenMintTransitionV0Methods for TokenMintTransitionV0 { } impl AllowedAsMultiPartyAction for TokenMintTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenMintTransitionV0 { base, amount, .. } = self; - TokenMintTransition::calculate_action_id_with_fields_v0( + Ok(TokenMintTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), *amount, - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs index d8c678957a0..a1010caae10 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs @@ -1,4 +1,5 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::balances::credits::TokenAmount; use crate::data_contract::associated_token::token_configuration::TokenConfiguration; use crate::prelude::IdentityNonce; @@ -84,9 +85,9 @@ impl TokenMintTransitionV0Methods for TokenMintTransition { } impl AllowedAsMultiPartyAction for TokenMintTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenMintTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenMintTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs index 90aa51019e4..132a7600753 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -63,14 +65,14 @@ impl TokenSetPriceForDirectPurchaseTransitionV0Methods } impl AllowedAsMultiPartyAction for TokenSetPriceForDirectPurchaseTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenSetPriceForDirectPurchaseTransitionV0 { base, price, .. } = self; - TokenSetPriceForDirectPurchaseTransition::calculate_action_id_with_fields_v0( + Ok(TokenSetPriceForDirectPurchaseTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), price.as_ref(), - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs index a04e074deae..640a7d151ca 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -63,9 +65,9 @@ impl TokenSetPriceForDirectPurchaseTransitionV0Methods } impl AllowedAsMultiPartyAction for TokenSetPriceForDirectPurchaseTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenSetPriceForDirectPurchaseTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenSetPriceForDirectPurchaseTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs index a7ca06f9d56..8415dd88210 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs @@ -213,16 +213,7 @@ pub trait TokenTransitionV0Methods { /// sets identity contract nonce fn set_identity_contract_nonce(&mut self, nonce: IdentityNonce); - fn calculate_action_id(&self, owner_id: Identifier) -> Option; - - /// Version-aware action_id calculation. For most variants this delegates to - /// `calculate_action_id`. For `ConfigUpdate` it uses `platform_version` to - /// choose between v0 (discriminant only) and v1 (discriminant + payload). - fn calculate_action_id_with_platform_version( - &self, - owner_id: Identifier, - platform_version: &PlatformVersion, - ) -> Option>; + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Option>; fn can_calculate_action_id(&self) -> bool; /// Historical document type name for the token history contract @@ -288,54 +279,19 @@ impl TokenTransitionV0Methods for TokenTransition { self.base().data_contract_id() } - fn calculate_action_id(&self, owner_id: Identifier) -> Option { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Option> { match self { - TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::Transfer(_) => None, - TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::Claim(_) => None, - TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id)), - TokenTransition::ConfigUpdate(t) => Some(t.calculate_action_id(owner_id)), + TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::ConfigUpdate(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::DirectPurchase(_) => None, - TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id)), - } - } - - fn calculate_action_id_with_platform_version( - &self, - owner_id: Identifier, - platform_version: &PlatformVersion, - ) -> Option> { - match self { - TokenTransition::ConfigUpdate(t) => { - let base = t.base(); - let item = t.update_token_configuration_item(); - Some(match platform_version.dpp.token_versions.token_config_update_action_id_version { - 0 => Ok(t.calculate_action_id(owner_id)), - 1 => { - match bincode::encode_to_vec(item, bincode::config::standard()) { - Ok(serialized) => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - item.u8_item_index(), - Some(&serialized), - )), - Err(e) => Err(ProtocolError::EncodingError(e.to_string())), - } - } - version => Err(ProtocolError::UnknownVersionMismatch { - method: "token_config_update_action_id".to_string(), - known_versions: vec![0, 1], - received: version, - }), - }) - } - // For all other variants, delegate to the simple trait method - other => other.calculate_action_id(owner_id).map(Ok), + TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id, platform_version)), } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs index 9ef9c37b511..f47edb49fdf 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs @@ -1,4 +1,6 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::batched_transition::token_unfreeze_transition::TokenUnfreezeTransitionV0; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; @@ -61,18 +63,18 @@ impl TokenUnfreezeTransitionV0Methods for TokenUnfreezeTransitionV0 { } impl AllowedAsMultiPartyAction for TokenUnfreezeTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { let TokenUnfreezeTransitionV0 { base, frozen_identity_id, .. } = self; - TokenUnfreezeTransition::calculate_action_id_with_fields_v0( + Ok(TokenUnfreezeTransition::calculate_action_id_with_fields_v0( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), frozen_identity_id.as_bytes(), - ) + )) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs index 18acce6f365..e7bfda283f2 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs @@ -1,5 +1,7 @@ use platform_value::Identifier; +use platform_version::version::PlatformVersion; use crate::prelude::IdentityNonce; +use crate::ProtocolError; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; @@ -60,9 +62,9 @@ impl TokenUnfreezeTransitionV0Methods for TokenUnfreezeTransition { } impl AllowedAsMultiPartyAction for TokenUnfreezeTransition { - fn calculate_action_id(&self, owner_id: Identifier) -> Identifier { + fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { match self { - TokenUnfreezeTransition::V0(v0) => v0.calculate_action_id(owner_id), + TokenUnfreezeTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs index e144be78ea3..f272765624b 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs @@ -53,8 +53,6 @@ use crate::state_transition::batch_transition::token_claim_transition::TokenClai #[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; #[cfg(feature = "state-transition-signing")] -use crate::state_transition::batch_transition::token_config_update_transition::v0::v0_methods::TokenConfigUpdateTransitionV0Methods; -#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_destroy_frozen_funds_transition::TokenDestroyFrozenFundsTransitionV0; #[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_direct_purchase_transition::TokenDirectPurchaseTransitionV0; @@ -115,7 +113,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = mint_transition.calculate_action_id(owner_id); + let action_id = mint_transition.calculate_action_id(owner_id, _platform_version)?; mint_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -190,7 +188,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = burn_transition.calculate_action_id(owner_id); + let action_id = burn_transition.calculate_action_id(owner_id, _platform_version)?; burn_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -331,7 +329,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = freeze_transition.calculate_action_id(owner_id); + let action_id = freeze_transition.calculate_action_id(owner_id, _platform_version)?; freeze_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -408,7 +406,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = unfreeze_transition.calculate_action_id(owner_id); + let action_id = unfreeze_transition.calculate_action_id(owner_id, _platform_version)?; unfreeze_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -486,7 +484,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = destroy_frozen_funds_transition.calculate_action_id(owner_id); + let action_id = destroy_frozen_funds_transition.calculate_action_id(owner_id, _platform_version)?; destroy_frozen_funds_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -565,7 +563,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = emergency_action_transition.calculate_action_id(owner_id); + let action_id = emergency_action_transition.calculate_action_id(owner_id, _platform_version)?; emergency_action_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -642,34 +640,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = match _platform_version - .dpp - .token_versions - .token_config_update_action_id_version - { - 0 => Ok(config_update_transition.calculate_action_id(owner_id)), - 1 => { - let base = config_update_transition.base(); - let item = config_update_transition.update_token_configuration_item(); - let serialized = - bincode::encode_to_vec(item, bincode::config::standard()) - .map_err(|e| ProtocolError::EncodingError(e.to_string()))?; - Ok( - TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - item.u8_item_index(), - Some(&serialized), - ), - ) - } - version => Err(ProtocolError::UnknownVersionMismatch { - method: "token_config_update_action_id".to_string(), - known_versions: vec![0, 1], - received: version, - }), - }?; + let action_id = config_update_transition + .calculate_action_id(owner_id, _platform_version)?; config_update_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -802,7 +774,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { group_contract_position, ) => { let action_id = - change_direct_purchase_price_transition.calculate_action_id(owner_id); + change_direct_purchase_price_transition.calculate_action_id(owner_id, _platform_version)?; change_direct_purchase_price_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs index c3fd413cb86..e34401a353c 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs @@ -191,7 +191,7 @@ impl BatchTransition { if let Some(group_state_transition_info) = transition.base().using_group_info() { if group_state_transition_info.action_is_proposer { if let Some(calculated_action_id) = - transition.calculate_action_id_with_platform_version(self.owner_id(), platform_version).transpose()? + transition.calculate_action_id(self.owner_id(), platform_version).transpose()? { if group_state_transition_info.action_id != calculated_action_id { result.add_error(BasicError::InvalidActionIdError( From 2184a81648edef6d112f3f0b26f6ebe8aab4b2f5 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 16 Mar 2026 18:49:14 +0700 Subject: [PATCH 07/13] refactor(dpp): change calculate_action_id_with_fields_v1 to take u8 + Option payload Change v1 signature from &TokenConfigurationChangeItem to (u8 discriminant, Option<&[u8]> payload). Serialization of the config item now happens at the call site, keeping the hash function generic. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../v0/v0_methods.rs | 20 ++++++--- .../v0_methods.rs | 44 ++++++++++++------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs index 090fbb72f5d..4b87f3bbddc 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs @@ -90,12 +90,20 @@ impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { base.identity_contract_nonce(), update_token_configuration_item.u8_item_index(), )), - 1 => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - update_token_configuration_item, - )), + 1 => { + let serialized_item = bincode::encode_to_vec( + update_token_configuration_item, + bincode::config::standard(), + ) + .map_err(|e| ProtocolError::EncodingError(e.to_string()))?; + Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + update_token_configuration_item.u8_item_index(), + Some(&serialized_item), + )) + } version => Err(ProtocolError::UnknownVersionMismatch { method: "calculate_action_id".to_string(), known_versions: vec![0, 1], diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index 7dafa3eac64..d5868b1a30d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -93,23 +93,24 @@ impl TokenConfigUpdateTransition { hash_double(bytes).into() } - /// v1: action_id includes the full serialized config change item, binding - /// the voted-on value into the hash and preventing vote-swap attacks. + /// v1: action_id includes the u8 discriminant plus an optional serialized + /// payload, binding the voted-on value into the hash and preventing + /// vote-swap attacks. pub fn calculate_action_id_with_fields_v1( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, - update_token_configuration_item: &TokenConfigurationChangeItem, + update_token_config_item: u8, + payload: Option<&[u8]>, ) -> Identifier { - let serialized_item = - bincode::encode_to_vec(update_token_configuration_item, bincode::config::standard()) - .expect("expected to encode token configuration change item"); - let mut bytes = b"action_token_config_update".to_vec(); bytes.extend_from_slice(token_id); bytes.extend_from_slice(owner_id); bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); - bytes.extend_from_slice(&serialized_item); + bytes.push(update_token_config_item); + if let Some(payload) = payload { + bytes.extend_from_slice(payload); + } hash_double(bytes).into() } @@ -123,6 +124,11 @@ mod tests { use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0; use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; + fn serialize_item(item: &TokenConfigurationChangeItem) -> Vec { + bincode::encode_to_vec(item, bincode::config::standard()) + .expect("expected to encode item") + } + fn make_transition(item: TokenConfigurationChangeItem) -> TokenConfigUpdateTransition { TokenConfigUpdateTransition::V0(TokenConfigUpdateTransitionV0 { base: TokenBaseTransition::V0(TokenBaseTransitionV0 { @@ -175,13 +181,15 @@ mod tests { &token_id, &owner_id, nonce, - &item_small, + item_small.u8_item_index(), + Some(&serialize_item(&item_small)), ); let id_large = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - &item_large, + item_large.u8_item_index(), + Some(&serialize_item(&item_large)), ); // v1: these must be DIFFERENT -- the fix @@ -204,13 +212,15 @@ mod tests { &token_id, &owner_id, nonce, - &item_max_supply, + item_max_supply.u8_item_index(), + Some(&serialize_item(&item_max_supply)), ); let id_dest = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( &token_id, &owner_id, nonce, - &item_allow_dest, + item_allow_dest.u8_item_index(), + Some(&serialize_item(&item_allow_dest)), ); assert_ne!( @@ -235,7 +245,7 @@ mod tests { item.u8_item_index(), ); let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, &item, + &token_id, &owner_id, nonce, item.u8_item_index(), Some(&serialize_item(&item)), ); assert_ne!( @@ -265,11 +275,13 @@ mod tests { // Verify the versioned result matches v1 directly let base = t.base(); + let item = t.update_token_configuration_item(); let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), - t.update_token_configuration_item(), + item.u8_item_index(), + Some(&serialize_item(item)), ); assert_eq!( id_versioned, id_v1, @@ -289,10 +301,10 @@ mod tests { let item2 = TokenConfigurationChangeItem::MaxSupply(Some(42)); let id1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, &item1, + &token_id, &owner_id, nonce, item1.u8_item_index(), Some(&serialize_item(&item1)), ); let id2 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, &item2, + &token_id, &owner_id, nonce, item2.u8_item_index(), Some(&serialize_item(&item2)), ); assert_eq!( From 555a1f682dfe4c4d20d3ed802adb0e8034f252e5 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 17 Mar 2026 09:21:10 +0700 Subject: [PATCH 08/13] fixes --- .../token_configuration_item.rs | 57 ++++++++++++ .../authorized_action_takers.rs | 56 ++++++++++++ .../batched_transition/multi_party_action.rs | 8 +- .../token_burn_transition/v0/v0_methods.rs | 8 +- .../token_burn_transition/v0_methods.rs | 8 +- .../v0/v0_methods.rs | 44 +++------- .../v0_methods.rs | 86 ++++++++++++++++--- .../v0/v0_methods.rs | 20 +++-- .../v0_methods.rs | 12 ++- .../v0/v0_methods.rs | 20 +++-- .../v0_methods.rs | 12 ++- .../token_freeze_transition/v0/v0_methods.rs | 8 +- .../token_freeze_transition/v0_methods.rs | 8 +- .../token_mint_transition/v0/v0_methods.rs | 8 +- .../token_mint_transition/v0_methods.rs | 8 +- .../v0/v0_methods.rs | 20 +++-- .../v0_methods.rs | 12 ++- .../batched_transition/token_transition.rs | 28 ++++-- .../v0/v0_methods.rs | 8 +- .../token_unfreeze_transition/v0_methods.rs | 8 +- .../batch_transition/v1/v1_methods.rs | 42 +++++---- .../validate_basic_structure/v0/mod.rs | 5 +- .../src/execution/check_tx/v0/mod.rs | 2 +- .../batch/tests/token/burn/mod.rs | 4 +- .../batch/tests/token/config_update/mod.rs | 86 +++++++++++-------- .../batch/tests/token/freeze/mod.rs | 6 +- .../batch/tests/token/mint/mod.rs | 16 ++-- .../batch/tests/token/transfer/mod.rs | 2 +- 28 files changed, 430 insertions(+), 172 deletions(-) diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs b/packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs index dcd92012b02..323e5f5024f 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs @@ -66,6 +66,63 @@ pub enum TokenConfigurationChangeItem { MainControlGroup(Option), } impl TokenConfigurationChangeItem { + pub fn payload_serialization(&self) -> Result>, ProtocolError> { + Ok(match self { + TokenConfigurationChangeItem::TokenConfigurationNoChange => None, + TokenConfigurationChangeItem::Conventions(convention) => Some( + bincode::encode_to_vec(convention, bincode::config::standard()) + .map_err(|e| ProtocolError::EncodingError(e.to_string()))?, + ), + TokenConfigurationChangeItem::ConventionsControlGroup(a) + | TokenConfigurationChangeItem::ConventionsAdminGroup(a) + | TokenConfigurationChangeItem::MaxSupplyControlGroup(a) + | TokenConfigurationChangeItem::MaxSupplyAdminGroup(a) + | TokenConfigurationChangeItem::PerpetualDistributionControlGroup(a) + | TokenConfigurationChangeItem::PerpetualDistributionAdminGroup(a) + | TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup(a) + | TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup(a) + | TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup(a) + | TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup(a) + | TokenConfigurationChangeItem::ManualMinting(a) + | TokenConfigurationChangeItem::ManualMintingAdminGroup(a) + | TokenConfigurationChangeItem::ManualBurning(a) + | TokenConfigurationChangeItem::ManualBurningAdminGroup(a) + | TokenConfigurationChangeItem::Freeze(a) + | TokenConfigurationChangeItem::FreezeAdminGroup(a) + | TokenConfigurationChangeItem::Unfreeze(a) + | TokenConfigurationChangeItem::UnfreezeAdminGroup(a) + | TokenConfigurationChangeItem::DestroyFrozenFunds(a) + | TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup(a) + | TokenConfigurationChangeItem::EmergencyAction(a) + | TokenConfigurationChangeItem::EmergencyActionAdminGroup(a) + | TokenConfigurationChangeItem::MarketplaceTradeModeControlGroup(a) + | TokenConfigurationChangeItem::MarketplaceTradeModeAdminGroup(a) => Some(a.to_bytes()), + TokenConfigurationChangeItem::MaxSupply(max_supply) => { + max_supply.map(|amount| amount.to_be_bytes().to_vec()) + } + TokenConfigurationChangeItem::PerpetualDistribution(distribution) => distribution + .as_ref() + .map(|dist| { + bincode::encode_to_vec(dist, bincode::config::standard()) + .map_err(|e| ProtocolError::EncodingError(e.to_string())) + }) + .transpose()?, + TokenConfigurationChangeItem::NewTokensDestinationIdentity(identity) => { + identity.map(|id| id.to_vec()) + } + TokenConfigurationChangeItem::MintingAllowChoosingDestination(allow) => { + Some(vec![*allow as u8]) + } + TokenConfigurationChangeItem::MarketplaceTradeMode(mode) => Some( + bincode::encode_to_vec(mode, bincode::config::standard()) + .map_err(|e| ProtocolError::EncodingError(e.to_string()))?, + ), + TokenConfigurationChangeItem::MainControlGroup(position) => match position { + Some(pos) => Some(pos.to_be_bytes().to_vec()), + None => Some(vec![]), + }, + }) + } pub fn u8_item_index(&self) -> u8 { match self { TokenConfigurationChangeItem::TokenConfigurationNoChange => 0, diff --git a/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs b/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs index 7912d3b5360..a980d0549c3 100644 --- a/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs +++ b/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs @@ -2,6 +2,7 @@ use crate::data_contract::group::accessors::v0::GroupV0Getters; use crate::data_contract::group::{Group, GroupMemberPower}; use crate::data_contract::GroupContractPosition; use crate::group::action_taker::{ActionGoal, ActionTaker}; +use crate::ProtocolError; use bincode::{Decode, Encode}; use platform_value::Identifier; use serde::{Deserialize, Serialize}; @@ -33,6 +34,61 @@ impl fmt::Display for AuthorizedActionTakers { } impl AuthorizedActionTakers { + pub fn to_bytes(&self) -> Vec { + match self { + AuthorizedActionTakers::NoOne => vec![0], + AuthorizedActionTakers::ContractOwner => vec![1], + AuthorizedActionTakers::Identity(identifier) => { + let mut bytes = vec![2]; + bytes.extend_from_slice(identifier.as_bytes()); + bytes + } + AuthorizedActionTakers::MainGroup => vec![3], + AuthorizedActionTakers::Group(position) => { + let mut bytes = vec![4]; + bytes.extend_from_slice(&position.to_be_bytes()); + bytes + } + } + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let Some(&tag) = bytes.first() else { + return Err(ProtocolError::DecodingError( + "empty bytes for AuthorizedActionTakers".to_string(), + )); + }; + match tag { + 0 => Ok(AuthorizedActionTakers::NoOne), + 1 => Ok(AuthorizedActionTakers::ContractOwner), + 2 => { + if bytes.len() != 33 { + return Err(ProtocolError::DecodingError(format!( + "expected 33 bytes for AuthorizedActionTakers::Identity, got {}", + bytes.len() + ))); + } + let identifier = Identifier::from_bytes(&bytes[1..]) + .map_err(|e| ProtocolError::DecodingError(e.to_string()))?; + Ok(AuthorizedActionTakers::Identity(identifier)) + } + 3 => Ok(AuthorizedActionTakers::MainGroup), + 4 => { + if bytes.len() != 3 { + return Err(ProtocolError::DecodingError(format!( + "expected 3 bytes for AuthorizedActionTakers::Group, got {}", + bytes.len() + ))); + } + let position = u16::from_be_bytes([bytes[1], bytes[2]]); + Ok(AuthorizedActionTakers::Group(position)) + } + other => Err(ProtocolError::DecodingError(format!( + "unknown AuthorizedActionTakers tag: {}", + other + ))), + } + } pub fn allowed_for_action_taker( &self, contract_owner_id: &Identifier, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs index 692f95d83c3..3aedf6d3cbc 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/multi_party_action.rs @@ -1,7 +1,11 @@ +use crate::ProtocolError; use platform_value::Identifier; use platform_version::version::PlatformVersion; -use crate::ProtocolError; pub trait AllowedAsMultiPartyAction { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result; + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result; } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs index 5eec4f3db9d..9925f2cf675 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs @@ -62,12 +62,16 @@ impl TokenBurnTransitionV0Methods for TokenBurnTransitionV0 { } impl AllowedAsMultiPartyAction for TokenBurnTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + _platform_version: &PlatformVersion, + ) -> Result { let TokenBurnTransitionV0 { base, burn_amount, .. } = self; - Ok(TokenBurnTransition::calculate_action_id_with_fields_v0( + Ok(TokenBurnTransition::calculate_action_id_with_fields( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs index 70920697b4b..edd576753aa 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs @@ -63,7 +63,11 @@ impl TokenBurnTransitionV0Methods for TokenBurnTransition { } impl AllowedAsMultiPartyAction for TokenBurnTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { match self { TokenBurnTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } @@ -71,7 +75,7 @@ impl AllowedAsMultiPartyAction for TokenBurnTransition { } impl TokenBurnTransition { - pub fn calculate_action_id_with_fields_v0( + pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs index 4b87f3bbddc..c4fe6cff67b 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs @@ -72,43 +72,23 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransitionV0 { impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { /// v0 action_id: uses only the u8 discriminant (kept for backward compat). - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion,) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { let TokenConfigUpdateTransitionV0 { base, update_token_configuration_item, .. } = self; - match platform_version - .dpp - .token_versions - .token_config_update_action_id_version - { - 0 => Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - update_token_configuration_item.u8_item_index(), - )), - 1 => { - let serialized_item = bincode::encode_to_vec( - update_token_configuration_item, - bincode::config::standard(), - ) - .map_err(|e| ProtocolError::EncodingError(e.to_string()))?; - Ok(TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - update_token_configuration_item.u8_item_index(), - Some(&serialized_item), - )) - } - version => Err(ProtocolError::UnknownVersionMismatch { - method: "calculate_action_id".to_string(), - known_versions: vec![0, 1], - received: version, - }), - } + TokenConfigUpdateTransition::calculate_action_id_with_fields( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + update_token_configuration_item, + platform_version, + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index d5868b1a30d..04d5bce3325 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -68,17 +68,58 @@ impl TokenConfigUpdateTransitionV0Methods for TokenConfigUpdateTransition { } impl AllowedAsMultiPartyAction for TokenConfigUpdateTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { match self { - TokenConfigUpdateTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenConfigUpdateTransition::V0(v0) => { + v0.calculate_action_id(owner_id, platform_version) + } } } } impl TokenConfigUpdateTransition { + pub fn calculate_action_id_with_fields( + token_id: &[u8; 32], + owner_id: &[u8; 32], + identity_contract_nonce: IdentityNonce, + token_configuration_change_item: &TokenConfigurationChangeItem, + platform_version: &PlatformVersion, + ) -> Result { + match platform_version + .dpp + .token_versions + .token_config_update_action_id_version + { + 0 => Ok(Self::calculate_action_id_with_fields_v0( + token_id, + owner_id, + identity_contract_nonce, + token_configuration_change_item.u8_item_index(), + )), + 1 => { + let payload = token_configuration_change_item.payload_serialization()?; + Ok(Self::calculate_action_id_with_fields_v1( + token_id, + owner_id, + identity_contract_nonce, + token_configuration_change_item.u8_item_index(), + payload.as_ref().map(|a| a.as_slice()), + )) + } + version => Err(ProtocolError::UnknownVersionMismatch { + method: "calculate_action_id_with_fields".to_string(), + known_versions: vec![0, 1], + received: version, + }), + } + } /// v0: action_id uses only the u8 discriminant of the config change item. /// This is kept for backward compatibility with existing production data. - pub fn calculate_action_id_with_fields_v0( + fn calculate_action_id_with_fields_v0( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, @@ -96,7 +137,7 @@ impl TokenConfigUpdateTransition { /// v1: action_id includes the u8 discriminant plus an optional serialized /// payload, binding the voted-on value into the hash and preventing /// vote-swap attacks. - pub fn calculate_action_id_with_fields_v1( + fn calculate_action_id_with_fields_v1( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, @@ -125,8 +166,7 @@ mod tests { use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; fn serialize_item(item: &TokenConfigurationChangeItem) -> Vec { - bincode::encode_to_vec(item, bincode::config::standard()) - .expect("expected to encode item") + bincode::encode_to_vec(item, bincode::config::standard()).expect("expected to encode item") } fn make_transition(item: TokenConfigurationChangeItem) -> TokenConfigUpdateTransition { @@ -156,8 +196,12 @@ mod tests { ))); let platform_version = PlatformVersion::first(); - let id_small = t_small.calculate_action_id(owner_id, platform_version).expect("expected action id"); - let id_large = t_large.calculate_action_id(owner_id, platform_version).expect("expected action id"); + let id_small = t_small + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); + let id_large = t_large + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); // v0: these are EQUAL -- the vulnerability assert_eq!( @@ -245,7 +289,11 @@ mod tests { item.u8_item_index(), ); let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, item.u8_item_index(), Some(&serialize_item(&item)), + &token_id, + &owner_id, + nonce, + item.u8_item_index(), + Some(&serialize_item(&item)), ); assert_ne!( @@ -264,8 +312,12 @@ mod tests { let platform_version = PlatformVersion::latest(); - let id_plain_v0 = t.calculate_action_id(owner_id, PlatformVersion::first()).expect("expected action id"); - let id_versioned = t.calculate_action_id(owner_id, platform_version).expect("expected action id"); + let id_plain_v0 = t + .calculate_action_id(owner_id, PlatformVersion::first()) + .expect("expected action id"); + let id_versioned = t + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); // v1 produces a different id from v0 because it hashes the full payload assert_ne!( @@ -301,10 +353,18 @@ mod tests { let item2 = TokenConfigurationChangeItem::MaxSupply(Some(42)); let id1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, item1.u8_item_index(), Some(&serialize_item(&item1)), + &token_id, + &owner_id, + nonce, + item1.u8_item_index(), + Some(&serialize_item(&item1)), ); let id2 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, &owner_id, nonce, item2.u8_item_index(), Some(&serialize_item(&item2)), + &token_id, + &owner_id, + nonce, + item2.u8_item_index(), + Some(&serialize_item(&item2)), ); assert_eq!( diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs index 211c4e2b1db..46d66bf0490 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs @@ -63,18 +63,24 @@ impl TokenDestroyFrozenFundsTransitionV0Methods for TokenDestroyFrozenFundsTrans } impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + _platform_version: &PlatformVersion, + ) -> Result { let TokenDestroyFrozenFundsTransitionV0 { base, frozen_identity_id, .. } = self; - Ok(TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - frozen_identity_id.as_bytes(), - )) + Ok( + TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + frozen_identity_id.as_bytes(), + ), + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs index 7e4dc821b9d..ce0ee2672cb 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs @@ -64,15 +64,21 @@ impl TokenDestroyFrozenFundsTransitionV0Methods for TokenDestroyFrozenFundsTrans } impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { match self { - TokenDestroyFrozenFundsTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenDestroyFrozenFundsTransition::V0(v0) => { + v0.calculate_action_id(owner_id, platform_version) + } } } } impl TokenDestroyFrozenFundsTransition { - pub fn calculate_action_id_with_fields_v0( + pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs index 783785f7be1..4b9c5823e56 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs @@ -65,18 +65,24 @@ impl TokenEmergencyActionTransitionV0Methods for TokenEmergencyActionTransitionV } impl AllowedAsMultiPartyAction for TokenEmergencyActionTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + _platform_version: &PlatformVersion, + ) -> Result { let TokenEmergencyActionTransitionV0 { base, emergency_action, .. } = self; - Ok(TokenEmergencyActionTransition::calculate_action_id_with_fields_v0( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - *emergency_action, - )) + Ok( + TokenEmergencyActionTransition::calculate_action_id_with_fields( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + *emergency_action, + ), + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs index 7538dea53ed..e49eed4a151 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs @@ -63,15 +63,21 @@ impl TokenEmergencyActionTransitionV0Methods for TokenEmergencyActionTransition } impl AllowedAsMultiPartyAction for TokenEmergencyActionTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { match self { - TokenEmergencyActionTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenEmergencyActionTransition::V0(v0) => { + v0.calculate_action_id(owner_id, platform_version) + } } } } impl TokenEmergencyActionTransition { - pub fn calculate_action_id_with_fields_v0( + pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs index 959a26a75ba..428070b4f4b 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs @@ -63,14 +63,18 @@ impl TokenFreezeTransitionV0Methods for TokenFreezeTransitionV0 { } impl AllowedAsMultiPartyAction for TokenFreezeTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + _platform_version: &PlatformVersion, + ) -> Result { let TokenFreezeTransitionV0 { base, identity_to_freeze_id: frozen_identity_id, .. } = self; - Ok(TokenFreezeTransition::calculate_action_id_with_fields_v0( + Ok(TokenFreezeTransition::calculate_action_id_with_fields( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs index 47fc225afeb..c5e79a0659e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs @@ -62,7 +62,11 @@ impl TokenFreezeTransitionV0Methods for TokenFreezeTransition { } impl AllowedAsMultiPartyAction for TokenFreezeTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { match self { TokenFreezeTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } @@ -70,7 +74,7 @@ impl AllowedAsMultiPartyAction for TokenFreezeTransition { } impl TokenFreezeTransition { - pub fn calculate_action_id_with_fields_v0( + pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs index 44cee2cb31e..41528e0022d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0/v0_methods.rs @@ -100,10 +100,14 @@ impl TokenMintTransitionV0Methods for TokenMintTransitionV0 { } impl AllowedAsMultiPartyAction for TokenMintTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + _platform_version: &PlatformVersion, + ) -> Result { let TokenMintTransitionV0 { base, amount, .. } = self; - Ok(TokenMintTransition::calculate_action_id_with_fields_v0( + Ok(TokenMintTransition::calculate_action_id_with_fields( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs index a1010caae10..2fb4193eedf 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_mint_transition/v0_methods.rs @@ -85,7 +85,11 @@ impl TokenMintTransitionV0Methods for TokenMintTransition { } impl AllowedAsMultiPartyAction for TokenMintTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { match self { TokenMintTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } @@ -93,7 +97,7 @@ impl AllowedAsMultiPartyAction for TokenMintTransition { } impl TokenMintTransition { - pub fn calculate_action_id_with_fields_v0( + pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs index 132a7600753..b987d4664e2 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0/v0_methods.rs @@ -65,14 +65,20 @@ impl TokenSetPriceForDirectPurchaseTransitionV0Methods } impl AllowedAsMultiPartyAction for TokenSetPriceForDirectPurchaseTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + _platform_version: &PlatformVersion, + ) -> Result { let TokenSetPriceForDirectPurchaseTransitionV0 { base, price, .. } = self; - Ok(TokenSetPriceForDirectPurchaseTransition::calculate_action_id_with_fields_v0( - base.token_id().as_bytes(), - owner_id.as_bytes(), - base.identity_contract_nonce(), - price.as_ref(), - )) + Ok( + TokenSetPriceForDirectPurchaseTransition::calculate_action_id_with_fields( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + price.as_ref(), + ), + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs index 640a7d151ca..dc838ac1e78 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_set_price_for_direct_purchase_transition/v0_methods.rs @@ -65,15 +65,21 @@ impl TokenSetPriceForDirectPurchaseTransitionV0Methods } impl AllowedAsMultiPartyAction for TokenSetPriceForDirectPurchaseTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { match self { - TokenSetPriceForDirectPurchaseTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), + TokenSetPriceForDirectPurchaseTransition::V0(v0) => { + v0.calculate_action_id(owner_id, platform_version) + } } } } impl TokenSetPriceForDirectPurchaseTransition { - pub fn calculate_action_id_with_fields_v0( + pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs index 8415dd88210..ba3df8c6620 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs @@ -213,7 +213,11 @@ pub trait TokenTransitionV0Methods { /// sets identity contract nonce fn set_identity_contract_nonce(&mut self, nonce: IdentityNonce); - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Option>; + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Option>; fn can_calculate_action_id(&self) -> bool; /// Historical document type name for the token history contract @@ -279,19 +283,31 @@ impl TokenTransitionV0Methods for TokenTransition { self.base().data_contract_id() } - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Option> { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Option> { match self { TokenTransition::Burn(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::Mint(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::Freeze(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::Unfreeze(t) => Some(t.calculate_action_id(owner_id, platform_version)), TokenTransition::Transfer(_) => None, - TokenTransition::DestroyFrozenFunds(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::DestroyFrozenFunds(t) => { + Some(t.calculate_action_id(owner_id, platform_version)) + } TokenTransition::Claim(_) => None, - TokenTransition::EmergencyAction(t) => Some(t.calculate_action_id(owner_id, platform_version)), - TokenTransition::ConfigUpdate(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::EmergencyAction(t) => { + Some(t.calculate_action_id(owner_id, platform_version)) + } + TokenTransition::ConfigUpdate(t) => { + Some(t.calculate_action_id(owner_id, platform_version)) + } TokenTransition::DirectPurchase(_) => None, - TokenTransition::SetPriceForDirectPurchase(t) => Some(t.calculate_action_id(owner_id, platform_version)), + TokenTransition::SetPriceForDirectPurchase(t) => { + Some(t.calculate_action_id(owner_id, platform_version)) + } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs index f47edb49fdf..4a05a1f965a 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs @@ -63,14 +63,18 @@ impl TokenUnfreezeTransitionV0Methods for TokenUnfreezeTransitionV0 { } impl AllowedAsMultiPartyAction for TokenUnfreezeTransitionV0 { - fn calculate_action_id(&self, owner_id: Identifier, _platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + _platform_version: &PlatformVersion, + ) -> Result { let TokenUnfreezeTransitionV0 { base, frozen_identity_id, .. } = self; - Ok(TokenUnfreezeTransition::calculate_action_id_with_fields_v0( + Ok(TokenUnfreezeTransition::calculate_action_id_with_fields( base.token_id().as_bytes(), owner_id.as_bytes(), base.identity_contract_nonce(), diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs index e7bfda283f2..56b7ad6be3f 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs @@ -62,7 +62,11 @@ impl TokenUnfreezeTransitionV0Methods for TokenUnfreezeTransition { } impl AllowedAsMultiPartyAction for TokenUnfreezeTransition { - fn calculate_action_id(&self, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn calculate_action_id( + &self, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { match self { TokenUnfreezeTransition::V0(v0) => v0.calculate_action_id(owner_id, platform_version), } @@ -70,7 +74,7 @@ impl AllowedAsMultiPartyAction for TokenUnfreezeTransition { } impl TokenUnfreezeTransition { - pub fn calculate_action_id_with_fields_v0( + pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], identity_contract_nonce: IdentityNonce, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs index f272765624b..f0dde7145c0 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v1_methods.rs @@ -92,7 +92,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { identity_contract_nonce: IdentityNonce, user_fee_increase: UserFeeIncrease, signer: &S, - _platform_version: &PlatformVersion, + platform_version: &PlatformVersion, options: Option, ) -> Result { let mut mint_transition = TokenMintTransition::V0(TokenMintTransitionV0 { @@ -113,7 +113,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = mint_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = + mint_transition.calculate_action_id(owner_id, platform_version)?; mint_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -168,7 +169,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { identity_contract_nonce: IdentityNonce, user_fee_increase: UserFeeIncrease, signer: &S, - _platform_version: &PlatformVersion, + platform_version: &PlatformVersion, options: Option, ) -> Result { let mut burn_transition = TokenBurnTransition::V0(TokenBurnTransitionV0 { @@ -188,7 +189,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = burn_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = + burn_transition.calculate_action_id(owner_id, platform_version)?; burn_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -309,7 +311,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { identity_contract_nonce: IdentityNonce, user_fee_increase: UserFeeIncrease, signer: &S, - _platform_version: &PlatformVersion, + platform_version: &PlatformVersion, options: Option, ) -> Result { let mut freeze_transition = TokenFreezeTransition::V0(TokenFreezeTransitionV0 { @@ -329,7 +331,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = freeze_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = + freeze_transition.calculate_action_id(owner_id, platform_version)?; freeze_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -386,7 +389,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { identity_contract_nonce: IdentityNonce, user_fee_increase: UserFeeIncrease, signer: &S, - _platform_version: &PlatformVersion, + platform_version: &PlatformVersion, options: Option, ) -> Result { let mut unfreeze_transition = TokenUnfreezeTransition::V0(TokenUnfreezeTransitionV0 { @@ -406,7 +409,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = unfreeze_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = + unfreeze_transition.calculate_action_id(owner_id, platform_version)?; unfreeze_transition.base_mut().set_using_group_info(Some( GroupStateTransitionInfo { group_contract_position, @@ -463,7 +467,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { identity_contract_nonce: IdentityNonce, user_fee_increase: UserFeeIncrease, signer: &S, - _platform_version: &PlatformVersion, + platform_version: &PlatformVersion, options: Option, ) -> Result { let mut destroy_frozen_funds_transition = @@ -484,7 +488,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = destroy_frozen_funds_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = destroy_frozen_funds_transition + .calculate_action_id(owner_id, platform_version)?; destroy_frozen_funds_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -542,7 +547,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { identity_contract_nonce: IdentityNonce, user_fee_increase: UserFeeIncrease, signer: &S, - _platform_version: &PlatformVersion, + platform_version: &PlatformVersion, options: Option, ) -> Result { let mut emergency_action_transition = @@ -563,7 +568,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = emergency_action_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = emergency_action_transition + .calculate_action_id(owner_id, platform_version)?; emergency_action_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -619,7 +625,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { identity_contract_nonce: IdentityNonce, user_fee_increase: UserFeeIncrease, signer: &S, - _platform_version: &PlatformVersion, + platform_version: &PlatformVersion, options: Option, ) -> Result { let mut config_update_transition = @@ -640,8 +646,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = config_update_transition - .calculate_action_id(owner_id, _platform_version)?; + let action_id = + config_update_transition.calculate_action_id(owner_id, platform_version)?; config_update_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { @@ -750,7 +756,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { identity_contract_nonce: IdentityNonce, user_fee_increase: UserFeeIncrease, signer: &S, - _platform_version: &PlatformVersion, + platform_version: &PlatformVersion, options: Option, ) -> Result { let mut change_direct_purchase_price_transition = @@ -773,8 +779,8 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( group_contract_position, ) => { - let action_id = - change_direct_purchase_price_transition.calculate_action_id(owner_id, _platform_version)?; + let action_id = change_direct_purchase_price_transition + .calculate_action_id(owner_id, platform_version)?; change_direct_purchase_price_transition .base_mut() .set_using_group_info(Some(GroupStateTransitionInfo { diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs index e34401a353c..fbf9159b3e8 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/validation/validate_basic_structure/v0/mod.rs @@ -190,8 +190,9 @@ impl BatchTransition { // But only if we are the proposer if let Some(group_state_transition_info) = transition.base().using_group_info() { if group_state_transition_info.action_is_proposer { - if let Some(calculated_action_id) = - transition.calculate_action_id(self.owner_id(), platform_version).transpose()? + if let Some(calculated_action_id) = transition + .calculate_action_id(self.owner_id(), platform_version) + .transpose()? { if group_state_transition_info.action_id != calculated_action_id { result.add_error(BasicError::InvalidActionIdError( diff --git a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs index 32d513d83e2..acacd8cb07b 100644 --- a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs @@ -3712,7 +3712,7 @@ mod tests { .unwrap() .expect("expected to commit transaction"); - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs index f2fef674d0b..b907841dec3 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs @@ -456,7 +456,7 @@ mod token_burn_tests { assert_eq!(token_balance, Some(expected_amount)); // Step 4: Confirm burn by second group member - let action_id = TokenBurnTransition::calculate_action_id_with_fields_v0( + let action_id = TokenBurnTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity1.id().as_bytes(), 2, @@ -715,7 +715,7 @@ mod token_burn_tests { assert_eq!(token_balance, Some(expected_amount)); // Step 3: Confirm burn by second group member - let action_id = TokenBurnTransition::calculate_action_id_with_fields_v0( + let action_id = TokenBurnTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity1.id().as_bytes(), 2, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/config_update/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/config_update/mod.rs index e1f672dd11f..bb57d705ed7 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/config_update/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/config_update/mod.rs @@ -1078,12 +1078,14 @@ mod token_config_update_tests { platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, - TokenConfigurationChangeItem::MaxSupply(Some(1000000)).u8_item_index(), - ); + &TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + platform_version, + ) + .expect("expected to calculate action id"); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, @@ -1293,15 +1295,16 @@ mod token_config_update_tests { platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, - TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup( + &TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup( AuthorizedActionTakers::Group(0), - ) - .u8_item_index(), - ); + ), + platform_version, + ) + .expect("expected to calculate action id"); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, @@ -1461,12 +1464,14 @@ mod token_config_update_tests { // now that we are group 0 as the group that can make the change, let's make the change to allow minting to choose the destination - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 3, - TokenConfigurationChangeItem::MintingAllowChoosingDestination(true).u8_item_index(), - ); + &TokenConfigurationChangeItem::MintingAllowChoosingDestination(true), + platform_version, + ) + .expect("expected to calculate action id"); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, @@ -1684,15 +1689,16 @@ mod token_config_update_tests { platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, - TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup( + &TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup( AuthorizedActionTakers::Group(1), - ) - .u8_item_index(), - ); + ), + platform_version, + ) + .expect("expected to calculate action id"); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, @@ -1909,15 +1915,16 @@ mod token_config_update_tests { // Let's try doing this action with group 1 - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity_3.id().as_bytes(), 2, - TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup( + &TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup( AuthorizedActionTakers::Group(2), - ) - .u8_item_index(), - ); + ), + platform_version, + ) + .expect("expected to calculate action id"); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, @@ -2146,15 +2153,16 @@ mod token_config_update_tests { platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity_3.id().as_bytes(), 2, - TokenConfigurationChangeItem::ConventionsAdminGroup(AuthorizedActionTakers::Group( - 0, - )) - .u8_item_index(), - ); + &TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(0), + ), + platform_version, + ) + .expect("expected to calculate action id"); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, @@ -2433,15 +2441,16 @@ mod token_config_update_tests { // Now let's have Group 0 change the control of the conventions to identity 2 only let action_id_change_control = - TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + TokenConfigUpdateTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, - TokenConfigurationChangeItem::ConventionsControlGroup( + &TokenConfigurationChangeItem::ConventionsControlGroup( AuthorizedActionTakers::Identity(identity_2.id()), - ) - .u8_item_index(), - ); + ), + platform_version, + ) + .expect("expected to calculate action id"); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, @@ -2577,15 +2586,16 @@ mod token_config_update_tests { // Now let's have Group 0 hand it back to Group 1 - let action_id_return = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( + let action_id_return = TokenConfigUpdateTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 3, - TokenConfigurationChangeItem::ConventionsAdminGroup(AuthorizedActionTakers::Group( - 1, - )) - .u8_item_index(), - ); + &TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(1), + ), + platform_version, + ) + .expect("expected to calculate action id"); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs index 4c9bd0940f8..e313b5fcb0d 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs @@ -1431,7 +1431,7 @@ mod token_freeze_tests { } // second signer - let action_id = TokenFreezeTransition::calculate_action_id_with_fields_v0( + let action_id = TokenFreezeTransition::calculate_action_id_with_fields( token_id.as_bytes(), id1.id().as_bytes(), 2, @@ -1775,7 +1775,7 @@ mod token_freeze_tests { .expect("expected to commit transaction"); // second signer - let action_id = TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( + let action_id = TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields( token_id.as_bytes(), id1.id().as_bytes(), 4, @@ -2223,7 +2223,7 @@ mod token_freeze_tests { .expect("expected to commit transaction"); // second signer - let action_id = TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields_v0( + let action_id = TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields( token_id.as_bytes(), id1.id().as_bytes(), 4, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs index 657b0bec7d5..7c27ceea35c 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs @@ -1602,7 +1602,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -1868,7 +1868,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2191,7 +2191,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2411,7 +2411,7 @@ mod token_mint_tests { // Now we need to get the second identity to also sign it, but we are going to resubmit with first // This will create an error - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2630,7 +2630,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -2919,7 +2919,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -3335,7 +3335,7 @@ mod token_mint_tests { assert_eq!(token_balance, Some(100000)); // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, @@ -3472,7 +3472,7 @@ mod token_mint_tests { ); // The second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/transfer/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/transfer/mod.rs index 0ae291586a1..d13f0051742 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/transfer/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/transfer/mod.rs @@ -1080,7 +1080,7 @@ mod token_transfer_tests { .unwrap() .expect("expected to commit transaction"); - let action_id = TokenMintTransition::calculate_action_id_with_fields_v0( + let action_id = TokenMintTransition::calculate_action_id_with_fields( token_id.as_bytes(), identity.id().as_bytes(), 2, From cdec469e96d64a2b5663473e296a5f7b63e15d5a Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 17 Mar 2026 09:25:45 +0700 Subject: [PATCH 09/13] fixes --- .../token_config_update_transition/v0_methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index 04d5bce3325..b573c7e463d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -107,7 +107,7 @@ impl TokenConfigUpdateTransition { owner_id, identity_contract_nonce, token_configuration_change_item.u8_item_index(), - payload.as_ref().map(|a| a.as_slice()), + payload.as_deref(), )) } version => Err(ProtocolError::UnknownVersionMismatch { From 6d290b147fea5276cb53ea07c9adbc7c2037e40b Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 17 Mar 2026 09:36:05 +0700 Subject: [PATCH 10/13] fixes --- .../token_config_update_transition/v0_methods.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index b573c7e463d..07c9d30f8a3 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -333,7 +333,9 @@ mod tests { owner_id.as_bytes(), base.identity_contract_nonce(), item.u8_item_index(), - Some(&serialize_item(item)), + item.payload_serialization() + .expect("expected to serialize payload") + .as_deref(), ); assert_eq!( id_versioned, id_v1, From 767f889d9feab0a1d787b460698cf7ab365f1f8c Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 17 Mar 2026 11:51:49 +0700 Subject: [PATCH 11/13] fix(test): use production path in v1 action_id tests The 3 v1 tests were using a bincode serialize_item() helper that serialized the entire enum (including bincode's variant discriminant), but production uses payload_serialization() which serializes only the inner value. This meant the tests proved hash diffusion (trivially true) rather than that the production serialization differentiates values. All 3 tests now go through the full production calculate_action_id() path with PlatformVersion::latest(), directly proving the vote-swap fix works end-to-end. Removed the serialize_item() helper entirely. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../v0_methods.rs | 152 +++++++----------- 1 file changed, 59 insertions(+), 93 deletions(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index 07c9d30f8a3..4cabd67abeb 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -165,10 +165,6 @@ mod tests { use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0; use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; - fn serialize_item(item: &TokenConfigurationChangeItem) -> Vec { - bincode::encode_to_vec(item, bincode::config::standard()).expect("expected to encode item") - } - fn make_transition(item: TokenConfigurationChangeItem) -> TokenConfigUpdateTransition { TokenConfigUpdateTransition::V0(TokenConfigUpdateTransitionV0 { base: TokenBaseTransition::V0(TokenBaseTransitionV0 { @@ -211,30 +207,25 @@ mod tests { } #[test] - fn v1_action_id_same_discriminant_different_values_produces_different_ids() { - // After the fix, the full serialized config item is included in the - // hash, so different values produce different action_ids. - let token_id = [2u8; 32]; - let owner_id = [3u8; 32]; - let nonce = 1u64; - - let item_small = TokenConfigurationChangeItem::MaxSupply(Some(100)); - let item_large = TokenConfigurationChangeItem::MaxSupply(Some(999_999_999_999)); - - let id_small = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item_small.u8_item_index(), - Some(&serialize_item(&item_small)), - ); - let id_large = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item_large.u8_item_index(), - Some(&serialize_item(&item_large)), - ); + fn v1_same_discriminant_different_values_produces_different_ids_through_production_path() { + // This is the core regression test for the vote-swap fix. + // Uses the full production calculate_action_id path on the latest + // platform version (v1) to prove that different MaxSupply values + // produce different action_ids. + let owner_id = Identifier::new([3u8; 32]); + let platform_version = PlatformVersion::latest(); + + let t_small = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(100))); + let t_large = make_transition(TokenConfigurationChangeItem::MaxSupply(Some( + 999_999_999_999, + ))); + + let id_small = t_small + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); + let id_large = t_large + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); // v1: these must be DIFFERENT -- the fix assert_ne!( @@ -244,28 +235,20 @@ mod tests { } #[test] - fn v1_action_id_different_item_types_produces_different_ids() { - let token_id = [2u8; 32]; - let owner_id = [3u8; 32]; - let nonce = 1u64; - - let item_max_supply = TokenConfigurationChangeItem::MaxSupply(Some(100)); - let item_allow_dest = TokenConfigurationChangeItem::MintingAllowChoosingDestination(true); - - let id_max = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item_max_supply.u8_item_index(), - Some(&serialize_item(&item_max_supply)), - ); - let id_dest = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item_allow_dest.u8_item_index(), - Some(&serialize_item(&item_allow_dest)), - ); + fn v1_different_item_types_produces_different_ids_through_production_path() { + let owner_id = Identifier::new([3u8; 32]); + let platform_version = PlatformVersion::latest(); + + let t_max = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(100))); + let t_dest = + make_transition(TokenConfigurationChangeItem::MintingAllowChoosingDestination(true)); + + let id_max = t_max + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); + let id_dest = t_dest + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); assert_ne!( id_max, id_dest, @@ -275,26 +258,18 @@ mod tests { #[test] fn v0_and_v1_produce_different_ids_for_same_input() { - // Verify v0 and v1 are not accidentally identical (they hash - // different payloads). - let token_id = [2u8; 32]; - let owner_id = [3u8; 32]; - let nonce = 1u64; - let item = TokenConfigurationChangeItem::MaxSupply(Some(100)); - - let id_v0 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v0( - &token_id, - &owner_id, - nonce, - item.u8_item_index(), - ); - let id_v1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item.u8_item_index(), - Some(&serialize_item(&item)), - ); + // Verify v0 (first platform version) and v1 (latest) produce + // different action_ids for the same config item. + let owner_id = Identifier::new([3u8; 32]); + + let t = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(100))); + + let id_v0 = t + .calculate_action_id(owner_id, PlatformVersion::first()) + .expect("expected action id"); + let id_v1 = t + .calculate_action_id(owner_id, PlatformVersion::latest()) + .expect("expected action id"); assert_ne!( id_v0, id_v1, @@ -344,30 +319,21 @@ mod tests { } #[test] - fn v1_action_id_identical_items_produces_same_id() { + fn v1_identical_items_produces_same_id_through_production_path() { // Sanity check: identical config items should produce the same - // action_id under v1. - let token_id = [2u8; 32]; - let owner_id = [3u8; 32]; - let nonce = 1u64; - - let item1 = TokenConfigurationChangeItem::MaxSupply(Some(42)); - let item2 = TokenConfigurationChangeItem::MaxSupply(Some(42)); - - let id1 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item1.u8_item_index(), - Some(&serialize_item(&item1)), - ); - let id2 = TokenConfigUpdateTransition::calculate_action_id_with_fields_v1( - &token_id, - &owner_id, - nonce, - item2.u8_item_index(), - Some(&serialize_item(&item2)), - ); + // action_id under v1, through the production path. + let owner_id = Identifier::new([3u8; 32]); + let platform_version = PlatformVersion::latest(); + + let t1 = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(42))); + let t2 = make_transition(TokenConfigurationChangeItem::MaxSupply(Some(42))); + + let id1 = t1 + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); + let id2 = t2 + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); assert_eq!( id1, id2, From 0c13f4774e0ed0866aa0e5a6d04ef93cc49e3853 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 17 Mar 2026 12:03:34 +0700 Subject: [PATCH 12/13] fix(dpp): add AuthorizedActionTakers tests and fix MainControlGroup consistency - Fix MainControlGroup(None) to return None from payload_serialization() instead of Some(vec![]), matching MaxSupply(None) and other None variants - Add test for AuthorizedActionTakers-based variants (ManualMinting with Group(5) vs Group(9) vs NoOne) through the production path - Add test proving None-payload variants (MaxSupply(None), NewTokensDestinationIdentity(None), PerpetualDistribution(None), MainControlGroup(None)) produce different action_ids via discriminant Co-Authored-By: Claude Opus 4.6 (1M context) --- .../token_configuration_item.rs | 7 +- .../v0_methods.rs | 75 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs b/packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs index 323e5f5024f..def46953e52 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_configuration_item.rs @@ -117,10 +117,9 @@ impl TokenConfigurationChangeItem { bincode::encode_to_vec(mode, bincode::config::standard()) .map_err(|e| ProtocolError::EncodingError(e.to_string()))?, ), - TokenConfigurationChangeItem::MainControlGroup(position) => match position { - Some(pos) => Some(pos.to_be_bytes().to_vec()), - None => Some(vec![]), - }, + TokenConfigurationChangeItem::MainControlGroup(position) => { + position.map(|pos| pos.to_be_bytes().to_vec()) + } }) } pub fn u8_item_index(&self) -> u8 { diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index 4cabd67abeb..1dd585c5cc7 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -340,4 +340,79 @@ mod tests { "v1 should produce the same action_id for identical config items" ); } + + #[test] + fn v1_authorized_action_takers_variant_differentiates_values() { + // Exercises payload_serialization() for AuthorizedActionTakers-based + // variants, which use to_bytes() internally. A regression in + // AuthorizedActionTakers::to_bytes() or payload_serialization() + // would be caught here. + use crate::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers; + + let owner_id = Identifier::new([3u8; 32]); + let platform_version = PlatformVersion::latest(); + + let t_group5 = make_transition(TokenConfigurationChangeItem::ManualMinting( + AuthorizedActionTakers::Group(5), + )); + let t_group9 = make_transition(TokenConfigurationChangeItem::ManualMinting( + AuthorizedActionTakers::Group(9), + )); + let t_no_one = make_transition(TokenConfigurationChangeItem::ManualMinting( + AuthorizedActionTakers::NoOne, + )); + + let id_group5 = t_group5 + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); + let id_group9 = t_group9 + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); + let id_no_one = t_no_one + .calculate_action_id(owner_id, platform_version) + .expect("expected action id"); + + assert_ne!( + id_group5, id_group9, + "ManualMinting(Group(5)) and ManualMinting(Group(9)) must differ" + ); + assert_ne!( + id_group5, id_no_one, + "ManualMinting(Group(5)) and ManualMinting(NoOne) must differ" + ); + } + + #[test] + fn v1_none_payload_variants_still_differentiated_by_discriminant() { + // MaxSupply(None), NewTokensDestinationIdentity(None), + // PerpetualDistribution(None), and MainControlGroup(None) all + // return None from payload_serialization(). They must still produce + // different action_ids because of their different u8 discriminants. + let owner_id = Identifier::new([3u8; 32]); + let platform_version = PlatformVersion::latest(); + + let t_max = make_transition(TokenConfigurationChangeItem::MaxSupply(None)); + let t_dest = + make_transition(TokenConfigurationChangeItem::NewTokensDestinationIdentity(None)); + let t_dist = make_transition(TokenConfigurationChangeItem::PerpetualDistribution(None)); + let t_ctrl = make_transition(TokenConfigurationChangeItem::MainControlGroup(None)); + + let ids: Vec<_> = [t_max, t_dest, t_dist, t_ctrl] + .iter() + .map(|t| { + t.calculate_action_id(owner_id, platform_version) + .expect("expected action id") + }) + .collect(); + + // All 4 must be unique + for i in 0..ids.len() { + for j in (i + 1)..ids.len() { + assert_ne!( + ids[i], ids[j], + "None-payload variants at indices {i} and {j} must have different action_ids" + ); + } + } + } } From fb320117c975a8efa117195acbc4fd0a1ca9952e Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 17 Mar 2026 12:05:56 +0700 Subject: [PATCH 13/13] chore: cargo fmt Co-Authored-By: Claude Opus 4.6 (1M context) --- .../token_config_update_transition/v0_methods.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index 1dd585c5cc7..c2a65faad69 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -392,8 +392,9 @@ mod tests { let platform_version = PlatformVersion::latest(); let t_max = make_transition(TokenConfigurationChangeItem::MaxSupply(None)); - let t_dest = - make_transition(TokenConfigurationChangeItem::NewTokensDestinationIdentity(None)); + let t_dest = make_transition(TokenConfigurationChangeItem::NewTokensDestinationIdentity( + None, + )); let t_dist = make_transition(TokenConfigurationChangeItem::PerpetualDistribution(None)); let t_ctrl = make_transition(TokenConfigurationChangeItem::MainControlGroup(None));