From 0050a479315609187ce277b2c4154f994fe72790 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Wed, 8 Apr 2026 11:14:59 +0300 Subject: [PATCH] test(dpp): improve coverage for identity state transitions (#3451) Add comprehensive unit tests for all seven identity state transition modules that had the lowest coverage. Tests cover construction, accessors, StateTransitionLike/Owned/SingleSigned trait impls, IdentitySigned trait impls, value conversion roundtrips, serialization roundtrips, estimated fee validation, and error paths for unknown versions. Also adds thorough tests for public key structure validation including duplicate key detection, master key requirements, and security level enforcement. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../identity_create_transition/mod.rs | 118 ++++++++ .../identity_create_transition/v0/mod.rs | 119 ++++++++ .../mod.rs | 225 ++++++++++++++ .../v0/mod.rs | 151 ++++++++++ .../mod.rs | 238 +++++++++++++++ .../v0/mod.rs | 135 +++++++++ .../v1/mod.rs | 165 ++++++++++ .../identity/identity_topup_transition/mod.rs | 117 ++++++++ .../identity_topup_transition/v0/mod.rs | 91 ++++++ .../identity_update_transition/mod.rs | 182 +++++++++++ .../identity_update_transition/v0/mod.rs | 168 +++++++++++ .../masternode_vote_transition/mod.rs | 154 ++++++++++ .../masternode_vote_transition/v0/mod.rs | 131 ++++++++ .../identity/public_key_in_creation/mod.rs | 283 ++++++++++++++++++ .../identity/public_key_in_creation/v0/mod.rs | 132 ++++++++ 15 files changed, 2409 insertions(+) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/mod.rs index fe3b43e9849..245c3469c83 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/mod.rs @@ -94,3 +94,121 @@ impl StateTransitionFieldTypes for IdentityCreateTransition { vec![] } } + +#[cfg(test)] +mod test { + use super::*; + use crate::identity::state_transition::asset_lock_proof::AssetLockProof; + use crate::serialization::{PlatformDeserializable, PlatformSerializable}; + use crate::state_transition::identity_create_transition::accessors::IdentityCreateTransitionAccessorsV0; + use crate::state_transition::{ + StateTransitionEstimatedFeeValidation, StateTransitionHasUserFeeIncrease, + StateTransitionLike, StateTransitionOwned, StateTransitionSingleSigned, + StateTransitionType, + }; + use crate::version::LATEST_PLATFORM_VERSION; + use platform_value::{BinaryData, Identifier}; + + fn make_create() -> IdentityCreateTransition { + IdentityCreateTransition::V0(IdentityCreateTransitionV0 { + public_keys: vec![], + asset_lock_proof: AssetLockProof::default(), + user_fee_increase: 0, + signature: [0u8; 65].to_vec().into(), + identity_id: Identifier::random(), + }) + } + + #[test] + fn test_default_versioned() { + let t = IdentityCreateTransition::default_versioned(LATEST_PLATFORM_VERSION) + .expect("should create default"); + match t { + IdentityCreateTransition::V0(_) => {} + } + } + + #[test] + fn test_serialization_roundtrip() { + let t = make_create(); + let bytes = t.serialize_to_bytes().expect("should serialize"); + let restored = + IdentityCreateTransition::deserialize_from_bytes(&bytes).expect("should deserialize"); + assert_eq!(t, restored); + } + + #[test] + fn test_state_transition_like() { + let t = make_create(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityCreate + ); + assert_eq!(t.state_transition_protocol_version(), 0); + let ids = t.modified_data_ids(); + assert_eq!(ids.len(), 1); + } + + #[test] + fn test_owner_id() { + let t = make_create(); + match &t { + IdentityCreateTransition::V0(v0) => { + assert_eq!(t.owner_id(), v0.identity_id); + } + } + } + + #[test] + fn test_user_fee_increase() { + let mut t = make_create(); + assert_eq!(t.user_fee_increase(), 0); + t.set_user_fee_increase(5); + assert_eq!(t.user_fee_increase(), 5); + } + + #[test] + fn test_single_signed() { + let mut t = make_create(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2, 3])); + assert_eq!(t.signature().as_slice(), &[1, 2, 3]); + t.set_signature_bytes(vec![4, 5]); + assert_eq!(t.signature().as_slice(), &[4, 5]); + } + + #[test] + fn test_accessors() { + let t = make_create(); + assert!(t.public_keys().is_empty()); + assert_ne!(t.identity_id(), Identifier::default()); + } + + #[test] + fn test_field_types() { + let sig = IdentityCreateTransition::signature_property_paths(); + assert_eq!(sig.len(), 2); + let ids = IdentityCreateTransition::identifiers_property_paths(); + assert_eq!(ids.len(), 1); + let bin = IdentityCreateTransition::binary_property_paths(); + assert!(bin.is_empty()); + } + + #[test] + fn test_estimated_fee() { + let t = make_create(); + let fee = t + .calculate_min_required_fee(LATEST_PLATFORM_VERSION) + .expect("fee calc should work"); + assert!(fee > 0); + } + + #[test] + fn test_into_from_v0() { + let v0 = IdentityCreateTransitionV0::default(); + let t: IdentityCreateTransition = v0.clone().into(); + match t { + IdentityCreateTransition::V0(inner) => assert_eq!(inner, v0), + } + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/v0/mod.rs index c43ed99b3d1..2d507a332fb 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/v0/mod.rs @@ -130,3 +130,122 @@ impl IdentityCreateTransitionV0 { } } } + +#[cfg(test)] +mod test { + use super::*; + use crate::state_transition::identity_create_transition::accessors::IdentityCreateTransitionAccessorsV0; + use crate::state_transition::{ + StateTransitionHasUserFeeIncrease, StateTransitionLike, StateTransitionOwned, + StateTransitionSingleSigned, StateTransitionType, + }; + use platform_value::BinaryData; + + fn make_create_v0() -> IdentityCreateTransitionV0 { + IdentityCreateTransitionV0 { + public_keys: vec![], + asset_lock_proof: AssetLockProof::default(), + user_fee_increase: 0, + signature: [0u8; 65].to_vec().into(), + identity_id: Identifier::random(), + } + } + + #[test] + fn test_default() { + let t = IdentityCreateTransitionV0::default(); + assert_eq!(t.user_fee_increase, 0); + assert!(t.public_keys.is_empty()); + assert!(t.signature.is_empty()); + } + + #[test] + fn test_state_transition_like() { + let t = make_create_v0(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityCreate + ); + assert_eq!(t.state_transition_protocol_version(), 0); + assert_eq!(t.modified_data_ids(), vec![t.identity_id]); + } + + #[test] + fn test_unique_identifiers() { + let t = make_create_v0(); + let ids = t.unique_identifiers(); + assert_eq!(ids.len(), 1); + assert!(!ids[0].is_empty()); + } + + #[test] + fn test_owner_id() { + let t = make_create_v0(); + assert_eq!(t.owner_id(), t.identity_id); + } + + #[test] + fn test_user_fee_increase() { + let mut t = make_create_v0(); + assert_eq!(t.user_fee_increase(), 0); + t.set_user_fee_increase(7); + assert_eq!(t.user_fee_increase(), 7); + } + + #[test] + fn test_single_signed() { + let mut t = make_create_v0(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2, 3])); + assert_eq!(t.signature().as_slice(), &[1, 2, 3]); + t.set_signature_bytes(vec![4, 5]); + assert_eq!(t.signature().as_slice(), &[4, 5]); + } + + #[test] + fn test_into_state_transition() { + use crate::state_transition::StateTransition; + let t = make_create_v0(); + let st: StateTransition = t.into(); + match st { + StateTransition::IdentityCreate(_) => {} + _ => panic!("expected IdentityCreate"), + } + } + + #[test] + fn test_accessors() { + let mut t = make_create_v0(); + assert!(t.public_keys().is_empty()); + assert_eq!(t.identity_id(), t.identity_id); + + // Test set_public_keys and add_public_keys + t.set_public_keys(vec![]); + assert!(t.public_keys().is_empty()); + } + + #[test] + fn test_to_object_produces_value() { + use crate::state_transition::StateTransitionValueConvert; + let t = make_create_v0(); + let obj = t.to_object(false).expect("to_object should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_value_conversion_skip_signature() { + use crate::state_transition::StateTransitionValueConvert; + let t = make_create_v0(); + let obj = t.to_object(true).expect("to_object should work"); + let map = obj.into_btree_string_map().expect("should be a map"); + assert!(!map.contains_key("signature")); + } + + #[test] + fn test_to_cleaned_object() { + use crate::state_transition::StateTransitionValueConvert; + let t = make_create_v0(); + let obj = t.to_cleaned_object(false).expect("should work"); + assert!(obj.is_map()); + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs index 81357507922..9f15a4999fb 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs @@ -98,3 +98,228 @@ impl StateTransitionFieldTypes for IdentityCreditTransferTransition { vec![] } } + +#[cfg(test)] +mod test { + use super::*; + use crate::serialization::{PlatformDeserializable, PlatformSerializable}; + use crate::state_transition::identity_credit_transfer_transition::accessors::IdentityCreditTransferTransitionAccessorsV0; + use crate::state_transition::{ + StateTransitionEstimatedFeeValidation, StateTransitionHasUserFeeIncrease, + StateTransitionIdentityEstimatedFeeValidation, StateTransitionLike, StateTransitionOwned, + StateTransitionSingleSigned, StateTransitionType, StateTransitionValueConvert, + }; + use crate::version::LATEST_PLATFORM_VERSION; + use platform_value::{BinaryData, Identifier, Value}; + + fn make_transfer() -> IdentityCreditTransferTransition { + IdentityCreditTransferTransition::V0(IdentityCreditTransferTransitionV0 { + identity_id: Identifier::random(), + recipient_id: Identifier::random(), + amount: 500_000, + nonce: 7, + user_fee_increase: 3, + signature_public_key_id: 2, + signature: [0u8; 65].to_vec().into(), + }) + } + + #[test] + fn test_default_versioned() { + let transition = + IdentityCreditTransferTransition::default_versioned(LATEST_PLATFORM_VERSION) + .expect("should create default"); + match transition { + IdentityCreditTransferTransition::V0(_) => {} + } + } + + #[test] + fn test_serialization_roundtrip() { + let transition = make_transfer(); + let bytes = transition.serialize_to_bytes().expect("should serialize"); + let restored = IdentityCreditTransferTransition::deserialize_from_bytes(&bytes) + .expect("should deserialize"); + assert_eq!(transition, restored); + } + + #[test] + fn test_state_transition_like() { + let transition = make_transfer(); + assert_eq!( + transition.state_transition_type(), + StateTransitionType::IdentityCreditTransfer + ); + assert_eq!(transition.state_transition_protocol_version(), 0); + let ids = transition.modified_data_ids(); + assert_eq!(ids.len(), 2); + let unique = transition.unique_identifiers(); + assert_eq!(unique.len(), 1); + assert!(!unique[0].is_empty()); + } + + #[test] + fn test_owner_id() { + let transition = make_transfer(); + match &transition { + IdentityCreditTransferTransition::V0(v0) => { + assert_eq!(transition.owner_id(), v0.identity_id); + } + } + } + + #[test] + fn test_user_fee_increase() { + let mut transition = make_transfer(); + assert_eq!(transition.user_fee_increase(), 3); + transition.set_user_fee_increase(99); + assert_eq!(transition.user_fee_increase(), 99); + } + + #[test] + fn test_single_signed() { + let mut transition = make_transfer(); + assert_eq!(transition.signature().len(), 65); + transition.set_signature(BinaryData::new(vec![1, 2, 3])); + assert_eq!(transition.signature().as_slice(), &[1, 2, 3]); + transition.set_signature_bytes(vec![4, 5]); + assert_eq!(transition.signature().as_slice(), &[4, 5]); + } + + #[test] + fn test_accessors() { + let mut transition = make_transfer(); + assert_eq!(transition.amount(), 500_000); + transition.set_amount(1_000_000); + assert_eq!(transition.amount(), 1_000_000); + assert_eq!(transition.nonce(), 7); + transition.set_nonce(42); + assert_eq!(transition.nonce(), 42); + let old_recipient = transition.recipient_id(); + let new_recipient = Identifier::random(); + transition.set_recipient_id(new_recipient); + assert_eq!(transition.recipient_id(), new_recipient); + assert_ne!(transition.recipient_id(), old_recipient); + } + + #[test] + fn test_field_types() { + let sig_paths = IdentityCreditTransferTransition::signature_property_paths(); + assert_eq!(sig_paths.len(), 1); + let id_paths = IdentityCreditTransferTransition::identifiers_property_paths(); + assert_eq!(id_paths.len(), 2); + let bin_paths = IdentityCreditTransferTransition::binary_property_paths(); + assert!(bin_paths.is_empty()); + } + + #[test] + fn test_value_conversion_roundtrip() { + let transition = make_transfer(); + let obj = StateTransitionValueConvert::to_object(&transition, false) + .expect("to_object should work"); + let restored = + ::from_object( + obj, + LATEST_PLATFORM_VERSION, + ) + .expect("from_object should work"); + assert_eq!(transition, restored); + } + + #[test] + fn test_from_value_map_roundtrip() { + let transition = make_transfer(); + let obj = StateTransitionValueConvert::to_object(&transition, false) + .expect("to_object should work"); + let map = obj.into_btree_string_map().expect("should convert to map"); + let restored = + ::from_value_map( + map, + LATEST_PLATFORM_VERSION, + ) + .expect("from_value_map should work"); + assert_eq!(transition, restored); + } + + #[test] + fn test_to_cleaned_object() { + let transition = make_transfer(); + let obj = StateTransitionValueConvert::to_cleaned_object(&transition, false) + .expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_to_canonical_cleaned_object() { + let transition = make_transfer(); + let obj = StateTransitionValueConvert::to_canonical_cleaned_object(&transition, false) + .expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_to_object_skip_signature() { + let transition = make_transfer(); + let obj = StateTransitionValueConvert::to_object(&transition, true).expect("should work"); + let map = obj.into_btree_string_map().expect("should be a map"); + assert!(!map.contains_key("signature")); + } + + #[test] + fn test_clean_value_unknown_version() { + let mut value = Value::from([("$stateTransitionProtocolVersion", Value::U8(255))]); + let result = ::clean_value( + &mut value, + ); + assert!(result.is_err()); + } + + #[test] + fn test_from_object_unknown_version() { + let value = Value::from([("$stateTransitionProtocolVersion", Value::U16(255))]); + let result = ::from_object( + value, + LATEST_PLATFORM_VERSION, + ); + assert!(result.is_err()); + } + + #[test] + fn test_estimated_fee_validation_sufficient() { + let transition = make_transfer(); + let fee = transition + .calculate_min_required_fee(LATEST_PLATFORM_VERSION) + .expect("fee calculation should work"); + assert!(fee > 0); + let result = transition + .validate_estimated_fee(fee + transition.amount() + 1000, LATEST_PLATFORM_VERSION) + .expect("validation should succeed"); + assert!(result.is_valid()); + } + + #[test] + fn test_estimated_fee_validation_insufficient() { + let transition = make_transfer(); + let result = transition + .validate_estimated_fee(0, LATEST_PLATFORM_VERSION) + .expect("validation should succeed"); + assert!(!result.is_valid()); + } + + #[test] + fn test_into_from_v0() { + let v0 = IdentityCreditTransferTransitionV0 { + identity_id: Identifier::random(), + recipient_id: Identifier::random(), + amount: 42, + nonce: 1, + user_fee_increase: 0, + signature_public_key_id: 0, + signature: vec![].into(), + }; + let transition: IdentityCreditTransferTransition = v0.clone().into(); + match transition { + IdentityCreditTransferTransition::V0(inner) => assert_eq!(inner, v0), + } + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/v0/mod.rs index 8d291207a04..5da1c542f1c 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/v0/mod.rs @@ -90,4 +90,155 @@ mod test { test_identity_credit_transfer_transition(transition); } + + fn make_transfer_v0() -> IdentityCreditTransferTransitionV0 { + IdentityCreditTransferTransitionV0 { + identity_id: Identifier::random(), + recipient_id: Identifier::random(), + amount: 100_000, + nonce: 42, + user_fee_increase: 5, + signature_public_key_id: 1, + signature: [0u8; 65].to_vec().into(), + } + } + + #[test] + fn test_state_transition_like_v0() { + use crate::state_transition::{ + StateTransitionLike, StateTransitionOwned, StateTransitionType, + }; + let transition = make_transfer_v0(); + assert_eq!( + transition.state_transition_type(), + StateTransitionType::IdentityCreditTransfer + ); + assert_eq!(transition.state_transition_protocol_version(), 0); + assert_eq!(transition.owner_id(), transition.identity_id); + let modified = transition.modified_data_ids(); + assert_eq!(modified.len(), 2); + assert_eq!(modified[0], transition.identity_id); + assert_eq!(modified[1], transition.recipient_id); + } + + #[test] + fn test_unique_identifiers_v0() { + use crate::state_transition::StateTransitionLike; + let transition = make_transfer_v0(); + let ids = transition.unique_identifiers(); + assert_eq!(ids.len(), 1); + assert!(!ids[0].is_empty()); + } + + #[test] + fn test_identity_signed_v0() { + use crate::identity::{Purpose, SecurityLevel}; + use crate::state_transition::StateTransitionIdentitySigned; + let mut transition = make_transfer_v0(); + assert_eq!(transition.signature_public_key_id(), 1); + transition.set_signature_public_key_id(99); + assert_eq!(transition.signature_public_key_id(), 99); + let security = transition.security_level_requirement(Purpose::TRANSFER); + assert_eq!(security, vec![SecurityLevel::CRITICAL]); + let purpose = transition.purpose_requirement(); + assert_eq!(purpose, vec![Purpose::TRANSFER]); + } + + #[test] + fn test_user_fee_increase_v0() { + use crate::state_transition::StateTransitionHasUserFeeIncrease; + let mut transition = make_transfer_v0(); + assert_eq!(transition.user_fee_increase(), 5); + transition.set_user_fee_increase(10); + assert_eq!(transition.user_fee_increase(), 10); + } + + #[test] + fn test_single_signed_v0() { + use crate::state_transition::StateTransitionSingleSigned; + use platform_value::BinaryData; + let mut transition = make_transfer_v0(); + assert_eq!(transition.signature().len(), 65); + let new_sig = BinaryData::new(vec![1, 2, 3]); + transition.set_signature(new_sig.clone()); + assert_eq!(transition.signature(), &new_sig); + transition.set_signature_bytes(vec![4, 5, 6]); + assert_eq!(transition.signature().as_slice(), &[4, 5, 6]); + } + + #[test] + fn test_into_state_transition_v0() { + use crate::state_transition::StateTransition; + let transition = make_transfer_v0(); + let st: StateTransition = transition.into(); + match st { + StateTransition::IdentityCreditTransfer(_) => {} + _ => panic!("expected IdentityCreditTransfer"), + } + } + + #[test] + fn test_value_conversion_roundtrip_v0() { + use crate::state_transition::StateTransitionValueConvert; + use crate::version::LATEST_PLATFORM_VERSION; + let transition = make_transfer_v0(); + let obj = transition.to_object(false).expect("to_object should work"); + let restored = + IdentityCreditTransferTransitionV0::from_object(obj, LATEST_PLATFORM_VERSION) + .expect("from_object should work"); + assert_eq!(transition, restored); + } + + #[test] + fn test_value_conversion_skip_signature_v0() { + use crate::state_transition::StateTransitionValueConvert; + let transition = make_transfer_v0(); + let obj = transition.to_object(true).expect("to_object should work"); + // The signature field should have been removed + let map = obj.into_btree_string_map().expect("should be a map"); + assert!(!map.contains_key("signature")); + } + + #[test] + fn test_to_cleaned_object_v0() { + use crate::state_transition::StateTransitionValueConvert; + let transition = make_transfer_v0(); + let obj = transition + .to_cleaned_object(false) + .expect("to_cleaned_object should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_to_canonical_cleaned_object_v0() { + use crate::state_transition::StateTransitionValueConvert; + let transition = make_transfer_v0(); + let obj = transition + .to_canonical_cleaned_object(false) + .expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_from_value_map_v0() { + use crate::state_transition::StateTransitionValueConvert; + use crate::version::LATEST_PLATFORM_VERSION; + let transition = make_transfer_v0(); + let obj = transition.to_object(false).expect("to_object should work"); + let map = obj + .into_btree_string_map() + .expect("should convert to btree map"); + let restored = + IdentityCreditTransferTransitionV0::from_value_map(map, LATEST_PLATFORM_VERSION) + .expect("from_value_map should work"); + assert_eq!(transition, restored); + } + + #[test] + fn test_default_v0() { + let transition = IdentityCreditTransferTransitionV0::default(); + assert_eq!(transition.amount, 0); + assert_eq!(transition.nonce, 0); + assert_eq!(transition.user_fee_increase, 0); + } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs index f54195ebd3f..180058ed4f5 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs @@ -118,3 +118,241 @@ impl StateTransitionFieldTypes for IdentityCreditWithdrawalTransition { } impl OptionallyAssetLockProved for IdentityCreditWithdrawalTransition {} + +#[cfg(test)] +mod test { + use super::*; + use crate::identity::core_script::CoreScript; + use crate::serialization::{PlatformDeserializable, PlatformSerializable}; + use crate::state_transition::identity_credit_withdrawal_transition::accessors::IdentityCreditWithdrawalTransitionAccessorsV0; + use crate::state_transition::{ + StateTransitionEstimatedFeeValidation, StateTransitionHasUserFeeIncrease, + StateTransitionIdentityEstimatedFeeValidation, StateTransitionLike, StateTransitionOwned, + StateTransitionSingleSigned, StateTransitionType, StateTransitionValueConvert, + }; + use crate::version::LATEST_PLATFORM_VERSION; + use crate::withdrawal::Pooling; + use platform_value::{BinaryData, Identifier, Value}; + + fn make_withdrawal_v0() -> IdentityCreditWithdrawalTransition { + IdentityCreditWithdrawalTransition::V0(IdentityCreditWithdrawalTransitionV0 { + identity_id: Identifier::random(), + amount: 300_000, + core_fee_per_byte: 1, + pooling: Pooling::Never, + output_script: CoreScript::from_bytes((0..23).collect::>()), + nonce: 3, + user_fee_increase: 1, + signature_public_key_id: 1, + signature: [0u8; 65].to_vec().into(), + }) + } + + fn make_withdrawal_v1() -> IdentityCreditWithdrawalTransition { + IdentityCreditWithdrawalTransition::V1(IdentityCreditWithdrawalTransitionV1 { + identity_id: Identifier::random(), + amount: 400_000, + core_fee_per_byte: 2, + pooling: Pooling::Standard, + output_script: None, + nonce: 5, + user_fee_increase: 2, + signature_public_key_id: 3, + signature: [0u8; 65].to_vec().into(), + }) + } + + #[test] + fn test_default_versioned() { + let t = IdentityCreditWithdrawalTransition::default_versioned(LATEST_PLATFORM_VERSION) + .expect("should create default"); + match t { + IdentityCreditWithdrawalTransition::V0(_) + | IdentityCreditWithdrawalTransition::V1(_) => {} + } + } + + #[test] + fn test_serialization_roundtrip_v0() { + let t = make_withdrawal_v0(); + let bytes = t.serialize_to_bytes().expect("should serialize"); + let restored = IdentityCreditWithdrawalTransition::deserialize_from_bytes(&bytes) + .expect("should deserialize"); + assert_eq!(t, restored); + } + + #[test] + fn test_serialization_roundtrip_v1() { + let t = make_withdrawal_v1(); + let bytes = t.serialize_to_bytes().expect("should serialize"); + let restored = IdentityCreditWithdrawalTransition::deserialize_from_bytes(&bytes) + .expect("should deserialize"); + assert_eq!(t, restored); + } + + #[test] + fn test_state_transition_like_v0() { + let t = make_withdrawal_v0(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityCreditWithdrawal + ); + assert_eq!(t.state_transition_protocol_version(), 0); + } + + #[test] + fn test_state_transition_like_v1() { + let t = make_withdrawal_v1(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityCreditWithdrawal + ); + assert_eq!(t.state_transition_protocol_version(), 0); + } + + #[test] + fn test_owner_id() { + let t = make_withdrawal_v0(); + assert_eq!(t.owner_id(), t.identity_id()); + } + + #[test] + fn test_user_fee_increase() { + let mut t = make_withdrawal_v0(); + assert_eq!(t.user_fee_increase(), 1); + t.set_user_fee_increase(50); + assert_eq!(t.user_fee_increase(), 50); + } + + #[test] + fn test_single_signed() { + let mut t = make_withdrawal_v0(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2])); + assert_eq!(t.signature().as_slice(), &[1, 2]); + t.set_signature_bytes(vec![3, 4]); + assert_eq!(t.signature().as_slice(), &[3, 4]); + } + + #[test] + fn test_accessors() { + let mut t = make_withdrawal_v0(); + assert_eq!(t.amount(), 300_000); + t.set_amount(500_000); + assert_eq!(t.amount(), 500_000); + assert_eq!(t.nonce(), 3); + t.set_nonce(99); + assert_eq!(t.nonce(), 99); + assert_eq!(t.pooling(), Pooling::Never); + t.set_pooling(Pooling::Standard); + assert_eq!(t.pooling(), Pooling::Standard); + assert_eq!(t.core_fee_per_byte(), 1); + t.set_core_fee_per_byte(5); + assert_eq!(t.core_fee_per_byte(), 5); + } + + #[test] + fn test_accessors_v1_output_script_none() { + let t = make_withdrawal_v1(); + assert!(t.output_script().is_none()); + } + + #[test] + fn test_field_types() { + let sig = IdentityCreditWithdrawalTransition::signature_property_paths(); + assert_eq!(sig.len(), 2); + let ids = IdentityCreditWithdrawalTransition::identifiers_property_paths(); + assert_eq!(ids.len(), 1); + let bin = IdentityCreditWithdrawalTransition::binary_property_paths(); + assert_eq!(bin.len(), 2); + } + + #[test] + fn test_value_conversion_roundtrip_v0() { + let t = make_withdrawal_v0(); + let obj = StateTransitionValueConvert::to_object(&t, false).expect("should work"); + let restored = + ::from_object( + obj, + LATEST_PLATFORM_VERSION, + ) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_value_conversion_roundtrip_v1() { + let t = make_withdrawal_v1(); + let obj = StateTransitionValueConvert::to_object(&t, false).expect("should work"); + let restored = + ::from_object( + obj, + LATEST_PLATFORM_VERSION, + ) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_from_value_map_v0() { + let t = make_withdrawal_v0(); + let obj = StateTransitionValueConvert::to_object(&t, false).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + let restored = + ::from_value_map( + map, + LATEST_PLATFORM_VERSION, + ) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_from_object_unknown_version() { + let value = Value::from([("$stateTransitionProtocolVersion", Value::U16(255))]); + let result = + ::from_object( + value, + LATEST_PLATFORM_VERSION, + ); + assert!(result.is_err()); + } + + #[test] + fn test_clean_value_unknown_version() { + let mut value = Value::from([("$stateTransitionProtocolVersion", Value::U8(255))]); + let result = + ::clean_value( + &mut value, + ); + assert!(result.is_err()); + } + + #[test] + fn test_estimated_fee_sufficient() { + let t = make_withdrawal_v0(); + let fee = t + .calculate_min_required_fee(LATEST_PLATFORM_VERSION) + .expect("fee calc should work"); + assert!(fee > 0); + let result = t + .validate_estimated_fee(fee + t.amount() + 1000, LATEST_PLATFORM_VERSION) + .expect("validation should succeed"); + assert!(result.is_valid()); + } + + #[test] + fn test_estimated_fee_insufficient() { + let t = make_withdrawal_v0(); + let result = t + .validate_estimated_fee(0, LATEST_PLATFORM_VERSION) + .expect("validation should succeed"); + assert!(!result.is_valid()); + } + + #[test] + fn test_min_withdrawal_amount_constant() { + assert!(MIN_WITHDRAWAL_AMOUNT > 0); + assert!(MIN_CORE_FEE_PER_BYTE == 1); + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/mod.rs index f16d0275916..ceff97c9ae2 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/mod.rs @@ -286,4 +286,139 @@ mod test { }; test_identity_credit_withdrawal_transition(transition); } + + fn make_withdrawal_v0() -> super::IdentityCreditWithdrawalTransitionV0 { + super::IdentityCreditWithdrawalTransitionV0 { + identity_id: Identifier::random(), + amount: 100_000, + core_fee_per_byte: 1, + pooling: Pooling::Never, + output_script: CoreScript::from_bytes((0..23).collect::>()), + nonce: 5, + user_fee_increase: 2, + signature_public_key_id: 1, + signature: [0u8; 65].to_vec().into(), + } + } + + #[test] + fn test_default() { + let t = super::IdentityCreditWithdrawalTransitionV0::default(); + assert_eq!(t.amount, 0); + assert_eq!(t.nonce, 0); + assert_eq!(t.core_fee_per_byte, 0); + } + + #[test] + fn test_state_transition_like_v0() { + use crate::state_transition::{ + StateTransitionLike, StateTransitionOwned, StateTransitionType, + }; + let t = make_withdrawal_v0(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityCreditWithdrawal + ); + assert_eq!(t.state_transition_protocol_version(), 0); + assert_eq!(t.modified_data_ids(), vec![t.identity_id]); + assert_eq!(t.owner_id(), t.identity_id); + } + + #[test] + fn test_unique_identifiers_v0() { + use crate::state_transition::StateTransitionLike; + let t = make_withdrawal_v0(); + let ids = t.unique_identifiers(); + assert_eq!(ids.len(), 1); + assert!(!ids[0].is_empty()); + } + + #[test] + fn test_identity_signed_v0() { + use crate::identity::{Purpose, SecurityLevel}; + use crate::state_transition::StateTransitionIdentitySigned; + let mut t = make_withdrawal_v0(); + assert_eq!(t.signature_public_key_id(), 1); + t.set_signature_public_key_id(42); + assert_eq!(t.signature_public_key_id(), 42); + let security = t.security_level_requirement(Purpose::TRANSFER); + assert_eq!(security, vec![SecurityLevel::CRITICAL]); + let purpose = t.purpose_requirement(); + assert_eq!(purpose, vec![Purpose::TRANSFER]); + } + + #[test] + fn test_user_fee_increase_v0() { + use crate::state_transition::StateTransitionHasUserFeeIncrease; + let mut t = make_withdrawal_v0(); + assert_eq!(t.user_fee_increase(), 2); + t.set_user_fee_increase(99); + assert_eq!(t.user_fee_increase(), 99); + } + + #[test] + fn test_single_signed_v0() { + use crate::state_transition::StateTransitionSingleSigned; + use platform_value::BinaryData; + let mut t = make_withdrawal_v0(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2, 3])); + assert_eq!(t.signature().as_slice(), &[1, 2, 3]); + t.set_signature_bytes(vec![4, 5]); + assert_eq!(t.signature().as_slice(), &[4, 5]); + } + + #[test] + fn test_into_state_transition_v0() { + use crate::state_transition::StateTransition; + let t = make_withdrawal_v0(); + let st: StateTransition = t.into(); + match st { + StateTransition::IdentityCreditWithdrawal(_) => {} + _ => panic!("expected IdentityCreditWithdrawal"), + } + } + + #[test] + fn test_value_conversion_roundtrip_v0() { + use crate::state_transition::StateTransitionValueConvert; + use crate::version::LATEST_PLATFORM_VERSION; + let t = make_withdrawal_v0(); + let obj = t.to_object(false).expect("to_object should work"); + let restored = + super::IdentityCreditWithdrawalTransitionV0::from_object(obj, LATEST_PLATFORM_VERSION) + .expect("from_object should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_to_cleaned_object_v0() { + use crate::state_transition::StateTransitionValueConvert; + let t = make_withdrawal_v0(); + let obj = t.to_cleaned_object(false).expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_to_canonical_cleaned_object_v0() { + use crate::state_transition::StateTransitionValueConvert; + let t = make_withdrawal_v0(); + let obj = t.to_canonical_cleaned_object(false).expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_from_value_map_v0() { + use crate::state_transition::StateTransitionValueConvert; + use crate::version::LATEST_PLATFORM_VERSION; + let t = make_withdrawal_v0(); + let obj = t.to_object(false).expect("to_object should work"); + let map = obj.into_btree_string_map().expect("should be a map"); + let restored = super::IdentityCreditWithdrawalTransitionV0::from_value_map( + map, + LATEST_PLATFORM_VERSION, + ) + .expect("should work"); + assert_eq!(t, restored); + } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/mod.rs index 88e67cbb1ef..2bba4191747 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/mod.rs @@ -46,3 +46,168 @@ pub struct IdentityCreditWithdrawalTransitionV1 { #[platform_signable(exclude_from_sig_hash)] pub signature: BinaryData, } + +#[cfg(test)] +mod test { + use super::*; + use crate::state_transition::{ + StateTransitionHasUserFeeIncrease, StateTransitionIdentitySigned, StateTransitionLike, + StateTransitionOwned, StateTransitionSingleSigned, StateTransitionType, + StateTransitionValueConvert, + }; + use platform_value::BinaryData; + + fn make_withdrawal_v1() -> IdentityCreditWithdrawalTransitionV1 { + IdentityCreditWithdrawalTransitionV1 { + identity_id: Identifier::random(), + amount: 200_000, + core_fee_per_byte: 1, + pooling: Pooling::Never, + output_script: Some(CoreScript::from_bytes((0..23).collect::>())), + nonce: 10, + user_fee_increase: 3, + signature_public_key_id: 2, + signature: [0u8; 65].to_vec().into(), + } + } + + fn make_withdrawal_v1_no_script() -> IdentityCreditWithdrawalTransitionV1 { + IdentityCreditWithdrawalTransitionV1 { + identity_id: Identifier::random(), + amount: 200_000, + core_fee_per_byte: 1, + pooling: Pooling::Standard, + output_script: None, + nonce: 10, + user_fee_increase: 0, + signature_public_key_id: 2, + signature: [0u8; 65].to_vec().into(), + } + } + + #[test] + fn test_default() { + let t = IdentityCreditWithdrawalTransitionV1::default(); + assert_eq!(t.amount, 0); + assert!(t.output_script.is_none()); + } + + #[test] + fn test_state_transition_like() { + let t = make_withdrawal_v1(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityCreditWithdrawal + ); + assert_eq!(t.state_transition_protocol_version(), 0); + assert_eq!(t.modified_data_ids(), vec![t.identity_id]); + assert_eq!(t.owner_id(), t.identity_id); + } + + #[test] + fn test_unique_identifiers() { + let t = make_withdrawal_v1(); + let ids = t.unique_identifiers(); + assert_eq!(ids.len(), 1); + assert!(!ids[0].is_empty()); + } + + #[test] + fn test_identity_signed() { + use crate::identity::{Purpose, SecurityLevel}; + let mut t = make_withdrawal_v1(); + assert_eq!(t.signature_public_key_id(), 2); + t.set_signature_public_key_id(55); + assert_eq!(t.signature_public_key_id(), 55); + let security = t.security_level_requirement(Purpose::TRANSFER); + assert_eq!(security, vec![SecurityLevel::CRITICAL]); + let purpose = t.purpose_requirement(); + assert!(purpose.contains(&Purpose::TRANSFER)); + assert!(purpose.contains(&Purpose::OWNER)); + } + + #[test] + fn test_user_fee_increase() { + let mut t = make_withdrawal_v1(); + assert_eq!(t.user_fee_increase(), 3); + t.set_user_fee_increase(100); + assert_eq!(t.user_fee_increase(), 100); + } + + #[test] + fn test_single_signed() { + let mut t = make_withdrawal_v1(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2, 3])); + assert_eq!(t.signature().as_slice(), &[1, 2, 3]); + t.set_signature_bytes(vec![4, 5]); + assert_eq!(t.signature().as_slice(), &[4, 5]); + } + + #[test] + fn test_into_state_transition() { + use crate::state_transition::StateTransition; + let t = make_withdrawal_v1(); + let st: StateTransition = t.into(); + match st { + StateTransition::IdentityCreditWithdrawal(_) => {} + _ => panic!("expected IdentityCreditWithdrawal"), + } + } + + #[test] + fn test_value_conversion_roundtrip_with_script() { + use crate::version::LATEST_PLATFORM_VERSION; + let t = make_withdrawal_v1(); + let obj = t.to_object(false).expect("to_object should work"); + let restored = + IdentityCreditWithdrawalTransitionV1::from_object(obj, LATEST_PLATFORM_VERSION) + .expect("from_object should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_value_conversion_roundtrip_without_script() { + use crate::version::LATEST_PLATFORM_VERSION; + let t = make_withdrawal_v1_no_script(); + let obj = t.to_object(false).expect("to_object should work"); + let restored = + IdentityCreditWithdrawalTransitionV1::from_object(obj, LATEST_PLATFORM_VERSION) + .expect("from_object should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_from_value_map() { + use crate::version::LATEST_PLATFORM_VERSION; + let t = make_withdrawal_v1(); + let obj = t.to_object(false).expect("to_object should work"); + let map = obj.into_btree_string_map().expect("should be map"); + let restored = + IdentityCreditWithdrawalTransitionV1::from_value_map(map, LATEST_PLATFORM_VERSION) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_to_cleaned_object() { + let t = make_withdrawal_v1(); + let obj = t.to_cleaned_object(false).expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_to_canonical_cleaned_object() { + let t = make_withdrawal_v1(); + let obj = t.to_canonical_cleaned_object(false).expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_to_object_skip_signature() { + let t = make_withdrawal_v1(); + let obj = t.to_object(true).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + assert!(!map.contains_key("signature")); + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/mod.rs index ae118ede6a6..f669693080a 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/mod.rs @@ -93,3 +93,120 @@ impl StateTransitionFieldTypes for IdentityTopUpTransition { vec![] } } + +#[cfg(test)] +mod test { + use super::*; + use crate::identity::state_transition::asset_lock_proof::AssetLockProof; + use crate::state_transition::{ + StateTransitionEstimatedFeeValidation, StateTransitionHasUserFeeIncrease, + StateTransitionLike, StateTransitionOwned, StateTransitionSingleSigned, + StateTransitionType, StateTransitionValueConvert, + }; + use crate::version::LATEST_PLATFORM_VERSION; + use platform_value::{BinaryData, Identifier, Value}; + + fn make_topup() -> IdentityTopUpTransition { + IdentityTopUpTransition::V0(IdentityTopUpTransitionV0 { + asset_lock_proof: AssetLockProof::default(), + identity_id: Identifier::random(), + user_fee_increase: 1, + signature: [0u8; 65].to_vec().into(), + }) + } + + #[test] + fn test_default_versioned() { + let t = IdentityTopUpTransition::default_versioned(LATEST_PLATFORM_VERSION) + .expect("should create default"); + match t { + IdentityTopUpTransition::V0(_) => {} + } + } + + #[test] + fn test_state_transition_like() { + let t = make_topup(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityTopUp + ); + assert_eq!(t.state_transition_protocol_version(), 0); + let ids = t.modified_data_ids(); + assert_eq!(ids.len(), 1); + } + + #[test] + fn test_owner_id() { + let t = make_topup(); + match &t { + IdentityTopUpTransition::V0(v0) => { + assert_eq!(t.owner_id(), v0.identity_id); + } + } + } + + #[test] + fn test_user_fee_increase() { + let mut t = make_topup(); + assert_eq!(t.user_fee_increase(), 1); + t.set_user_fee_increase(50); + assert_eq!(t.user_fee_increase(), 50); + } + + #[test] + fn test_single_signed() { + let mut t = make_topup(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![7, 8, 9])); + assert_eq!(t.signature().as_slice(), &[7, 8, 9]); + t.set_signature_bytes(vec![10, 11]); + assert_eq!(t.signature().as_slice(), &[10, 11]); + } + + #[test] + fn test_field_types() { + let sig = IdentityTopUpTransition::signature_property_paths(); + assert_eq!(sig.len(), 1); + let ids = IdentityTopUpTransition::identifiers_property_paths(); + assert_eq!(ids.len(), 1); + let bin = IdentityTopUpTransition::binary_property_paths(); + assert!(bin.is_empty()); + } + + #[test] + fn test_estimated_fee() { + let t = make_topup(); + let fee = t + .calculate_min_required_fee(LATEST_PLATFORM_VERSION) + .expect("fee calc should work"); + assert!(fee > 0); + } + + #[test] + fn test_from_object_unknown_version() { + let value = Value::from([("$stateTransitionProtocolVersion", Value::U16(255))]); + let result = ::from_object( + value, + LATEST_PLATFORM_VERSION, + ); + assert!(result.is_err()); + } + + #[test] + fn test_clean_value_unknown_version() { + let mut value = Value::from([("$stateTransitionProtocolVersion", Value::U8(255))]); + let result = + ::clean_value(&mut value); + assert!(result.is_err()); + } + + #[test] + fn test_into_from_v0() { + let v0 = IdentityTopUpTransitionV0::default(); + let t: IdentityTopUpTransition = v0.clone().into(); + match t { + IdentityTopUpTransition::V0(inner) => assert_eq!(inner, v0), + } + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/v0/mod.rs index a19663a304b..28034c9d9eb 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/v0/mod.rs @@ -48,3 +48,94 @@ pub struct IdentityTopUpTransitionV0 { #[platform_signable(exclude_from_sig_hash)] pub signature: BinaryData, } + +#[cfg(test)] +mod test { + use super::*; + use crate::state_transition::{ + StateTransitionHasUserFeeIncrease, StateTransitionLike, StateTransitionOwned, + StateTransitionSingleSigned, StateTransitionType, + }; + use platform_value::BinaryData; + + fn make_topup_v0() -> IdentityTopUpTransitionV0 { + IdentityTopUpTransitionV0 { + asset_lock_proof: AssetLockProof::default(), + identity_id: Identifier::random(), + user_fee_increase: 2, + signature: [0u8; 65].to_vec().into(), + } + } + + #[test] + fn test_default() { + let t = IdentityTopUpTransitionV0::default(); + assert_eq!(t.user_fee_increase, 0); + assert_eq!(t.identity_id, Identifier::default()); + } + + #[test] + fn test_state_transition_like() { + let t = make_topup_v0(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityTopUp + ); + assert_eq!(t.state_transition_protocol_version(), 0); + assert_eq!(t.modified_data_ids(), vec![t.identity_id]); + } + + #[test] + fn test_unique_identifiers() { + let t = make_topup_v0(); + let ids = t.unique_identifiers(); + assert_eq!(ids.len(), 1); + // With a default AssetLockProof, create_identifier fails, so the + // implementation returns a default empty string as fallback + } + + #[test] + fn test_owner_id() { + let t = make_topup_v0(); + assert_eq!(t.owner_id(), t.identity_id); + } + + #[test] + fn test_user_fee_increase() { + let mut t = make_topup_v0(); + assert_eq!(t.user_fee_increase(), 2); + t.set_user_fee_increase(10); + assert_eq!(t.user_fee_increase(), 10); + } + + #[test] + fn test_single_signed() { + let mut t = make_topup_v0(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2, 3])); + assert_eq!(t.signature().as_slice(), &[1, 2, 3]); + t.set_signature_bytes(vec![4, 5, 6]); + assert_eq!(t.signature().as_slice(), &[4, 5, 6]); + } + + #[test] + fn test_into_state_transition() { + use crate::state_transition::StateTransition; + let t = make_topup_v0(); + let st: StateTransition = t.into(); + match st { + StateTransition::IdentityTopUp(_) => {} + _ => panic!("expected IdentityTopUp"), + } + } + + #[test] + fn test_asset_lock_proved() { + use crate::identity::state_transition::AssetLockProved; + let mut t = make_topup_v0(); + let proof = t.asset_lock_proof().clone(); + assert_eq!(&proof, t.asset_lock_proof()); + t.set_asset_lock_proof(AssetLockProof::default()) + .expect("should set proof"); + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs index 5820b90e8cc..8e8f5e68f83 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs @@ -101,3 +101,185 @@ impl StateTransitionFieldTypes for IdentityUpdateTransition { ] } } + +#[cfg(test)] +mod test { + use super::*; + use crate::serialization::{PlatformDeserializable, PlatformSerializable}; + use crate::state_transition::identity_update_transition::accessors::IdentityUpdateTransitionAccessorsV0; + use crate::state_transition::{ + StateTransitionEstimatedFeeValidation, StateTransitionHasUserFeeIncrease, + StateTransitionIdentityEstimatedFeeValidation, StateTransitionLike, StateTransitionOwned, + StateTransitionSingleSigned, StateTransitionType, StateTransitionValueConvert, + }; + use crate::version::LATEST_PLATFORM_VERSION; + use platform_value::{BinaryData, Identifier, Value}; + + fn make_update() -> IdentityUpdateTransition { + IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 { + identity_id: Identifier::random(), + revision: 3, + nonce: 10, + add_public_keys: vec![], + disable_public_keys: vec![1], + user_fee_increase: 2, + signature_public_key_id: 0, + signature: [0u8; 65].to_vec().into(), + }) + } + + #[test] + fn test_default_versioned() { + let t = IdentityUpdateTransition::default_versioned(LATEST_PLATFORM_VERSION) + .expect("should create default"); + match t { + IdentityUpdateTransition::V0(_) => {} + } + } + + #[test] + fn test_serialization_roundtrip() { + let t = make_update(); + let bytes = t.serialize_to_bytes().expect("should serialize"); + let restored = + IdentityUpdateTransition::deserialize_from_bytes(&bytes).expect("should deserialize"); + assert_eq!(t, restored); + } + + #[test] + fn test_state_transition_like() { + let t = make_update(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityUpdate + ); + assert_eq!(t.state_transition_protocol_version(), 0); + let ids = t.modified_data_ids(); + assert_eq!(ids.len(), 1); + let unique = t.unique_identifiers(); + assert_eq!(unique.len(), 1); + } + + #[test] + fn test_owner_id() { + let t = make_update(); + assert_eq!(t.owner_id(), t.identity_id()); + } + + #[test] + fn test_user_fee_increase() { + let mut t = make_update(); + assert_eq!(t.user_fee_increase(), 2); + t.set_user_fee_increase(50); + assert_eq!(t.user_fee_increase(), 50); + } + + #[test] + fn test_single_signed() { + let mut t = make_update(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2])); + assert_eq!(t.signature().as_slice(), &[1, 2]); + t.set_signature_bytes(vec![3, 4]); + assert_eq!(t.signature().as_slice(), &[3, 4]); + } + + #[test] + fn test_accessors() { + let mut t = make_update(); + assert_eq!(t.revision(), 3); + t.set_revision(5); + assert_eq!(t.revision(), 5); + assert_eq!(t.nonce(), 10); + t.set_nonce(20); + assert_eq!(t.nonce(), 20); + assert!(t.public_keys_to_add().is_empty()); + assert_eq!(t.public_key_ids_to_disable(), &[1]); + t.set_public_key_ids_to_disable(vec![2, 3]); + assert_eq!(t.public_key_ids_to_disable(), &[2, 3]); + } + + #[test] + fn test_field_types() { + let sig = IdentityUpdateTransition::signature_property_paths(); + assert_eq!(sig.len(), 3); + let ids = IdentityUpdateTransition::identifiers_property_paths(); + assert_eq!(ids.len(), 1); + let bin = IdentityUpdateTransition::binary_property_paths(); + assert_eq!(bin.len(), 2); + } + + #[test] + fn test_estimated_fee_sufficient() { + let t = make_update(); + let fee = t + .calculate_min_required_fee(LATEST_PLATFORM_VERSION) + .expect("fee calc should work"); + assert!(fee > 0); + let result = t + .validate_estimated_fee(fee + 1000, LATEST_PLATFORM_VERSION) + .expect("validation should work"); + assert!(result.is_valid()); + } + + #[test] + fn test_estimated_fee_insufficient() { + let t = make_update(); + let result = t + .validate_estimated_fee(0, LATEST_PLATFORM_VERSION) + .expect("validation should work"); + assert!(!result.is_valid()); + } + + #[test] + fn test_value_conversion_roundtrip() { + let t = make_update(); + let obj = StateTransitionValueConvert::to_object(&t, false).expect("should work"); + let restored = ::from_object( + obj, + LATEST_PLATFORM_VERSION, + ) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_from_value_map() { + let t = make_update(); + let obj = StateTransitionValueConvert::to_object(&t, false).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + let restored = ::from_value_map( + map, + LATEST_PLATFORM_VERSION, + ) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_from_object_unknown_version() { + let value = Value::from([("$stateTransitionProtocolVersion", Value::U16(255))]); + let result = ::from_object( + value, + LATEST_PLATFORM_VERSION, + ); + assert!(result.is_err()); + } + + #[test] + fn test_clean_value_unknown_version() { + let mut value = Value::from([("$stateTransitionProtocolVersion", Value::U8(255))]); + let result = + ::clean_value(&mut value); + assert!(result.is_err()); + } + + #[test] + fn test_into_from_v0() { + let v0 = IdentityUpdateTransitionV0::default(); + let t: IdentityUpdateTransition = v0.clone().into(); + match t { + IdentityUpdateTransition::V0(inner) => assert_eq!(inner, v0), + } + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/v0/mod.rs index 227eea1fe59..64644ba2bd9 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/v0/mod.rs @@ -86,6 +86,174 @@ fn get_list>( .collect() } +#[cfg(test)] +mod test { + use super::*; + use crate::state_transition::{ + StateTransitionHasUserFeeIncrease, StateTransitionIdentitySigned, StateTransitionLike, + StateTransitionOwned, StateTransitionSingleSigned, StateTransitionType, + StateTransitionValueConvert, + }; + use platform_value::BinaryData; + + fn make_update_v0() -> IdentityUpdateTransitionV0 { + IdentityUpdateTransitionV0 { + identity_id: Identifier::random(), + revision: 2, + nonce: 5, + add_public_keys: vec![], + disable_public_keys: vec![1, 2], + user_fee_increase: 3, + signature_public_key_id: 0, + signature: [0u8; 65].to_vec().into(), + } + } + + #[test] + fn test_default() { + let t = IdentityUpdateTransitionV0::default(); + assert_eq!(t.revision, 0); + assert_eq!(t.nonce, 0); + assert!(t.add_public_keys.is_empty()); + assert!(t.disable_public_keys.is_empty()); + } + + #[test] + fn test_state_transition_like() { + let t = make_update_v0(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::IdentityUpdate + ); + assert_eq!(t.state_transition_protocol_version(), 0); + assert_eq!(t.modified_data_ids(), vec![t.identity_id]); + assert_eq!(t.owner_id(), t.identity_id); + } + + #[test] + fn test_unique_identifiers() { + let t = make_update_v0(); + let ids = t.unique_identifiers(); + assert_eq!(ids.len(), 1); + assert!(!ids[0].is_empty()); + } + + #[test] + fn test_identity_signed() { + use crate::identity::{Purpose, SecurityLevel}; + let mut t = make_update_v0(); + assert_eq!(t.signature_public_key_id(), 0); + t.set_signature_public_key_id(42); + assert_eq!(t.signature_public_key_id(), 42); + let security = t.security_level_requirement(Purpose::AUTHENTICATION); + assert_eq!(security, vec![SecurityLevel::MASTER]); + } + + #[test] + fn test_user_fee_increase() { + let mut t = make_update_v0(); + assert_eq!(t.user_fee_increase(), 3); + t.set_user_fee_increase(10); + assert_eq!(t.user_fee_increase(), 10); + } + + #[test] + fn test_single_signed() { + let mut t = make_update_v0(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2, 3])); + assert_eq!(t.signature().as_slice(), &[1, 2, 3]); + t.set_signature_bytes(vec![4, 5]); + assert_eq!(t.signature().as_slice(), &[4, 5]); + } + + #[test] + fn test_into_state_transition() { + use crate::state_transition::StateTransition; + let t = make_update_v0(); + let st: StateTransition = t.into(); + match st { + StateTransition::IdentityUpdate(_) => {} + _ => panic!("expected IdentityUpdate"), + } + } + + #[test] + fn test_value_conversion_roundtrip() { + let t = make_update_v0(); + let obj = t.to_object(false).expect("to_object should work"); + let restored = + IdentityUpdateTransitionV0::from_object(obj, crate::version::PlatformVersion::latest()) + .expect("from_object should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_to_object_skip_signature() { + let t = make_update_v0(); + let obj = t.to_object(true).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + assert!(!map.contains_key("signature")); + } + + #[test] + fn test_to_cleaned_object() { + let t = make_update_v0(); + let obj = t.to_cleaned_object(false).expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_to_cleaned_object_removes_empty_arrays() { + let t = IdentityUpdateTransitionV0 { + identity_id: Identifier::random(), + revision: 1, + nonce: 1, + add_public_keys: vec![], + disable_public_keys: vec![], + user_fee_increase: 0, + signature_public_key_id: 0, + signature: vec![].into(), + }; + let obj = t.to_cleaned_object(false).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + // Empty arrays should be removed + assert!(!map.contains_key("addPublicKeys")); + assert!(!map.contains_key("disablePublicKeys")); + } + + #[test] + fn test_from_value_map() { + let t = make_update_v0(); + let obj = t.to_object(false).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + let restored = IdentityUpdateTransitionV0::from_value_map( + map, + crate::version::PlatformVersion::latest(), + ) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_get_list_empty() { + use crate::state_transition::public_key_in_creation::v0::IdentityPublicKeyInCreationV0; + let mut val = Value::Map(vec![]); + let result: Result, _> = + get_list(&mut val, "nonexistent"); + assert!(result.is_ok()); + assert!(result.unwrap().is_empty()); + } + + #[test] + fn test_remove_integer_list_or_default_empty() { + let mut val = Value::Map(vec![]); + let result: Result, _> = remove_integer_list_or_default(&mut val, "nonexistent"); + assert!(result.is_ok()); + assert!(result.unwrap().is_empty()); + } +} + /// if the property isn't present the empty list is returned. If property is defined, the function /// might return some serialization-related errors fn remove_integer_list_or_default( diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/mod.rs index 1c42915a2e6..4af1d81d4ab 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/mod.rs @@ -98,3 +98,157 @@ impl StateTransitionFieldTypes for MasternodeVoteTransition { vec![] } } + +#[cfg(test)] +mod test { + use super::*; + use crate::serialization::{PlatformDeserializable, PlatformSerializable}; + use crate::state_transition::{ + StateTransitionEstimatedFeeValidation, StateTransitionLike, StateTransitionOwned, + StateTransitionSingleSigned, StateTransitionType, StateTransitionValueConvert, + }; + use crate::version::LATEST_PLATFORM_VERSION; + use crate::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; + use crate::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll; + use crate::voting::vote_polls::VotePoll; + use crate::voting::votes::resource_vote::v0::ResourceVoteV0; + use crate::voting::votes::resource_vote::ResourceVote; + use crate::voting::votes::Vote; + use platform_value::{BinaryData, Identifier, Value}; + + fn make_vote() -> MasternodeVoteTransition { + MasternodeVoteTransition::V0(MasternodeVoteTransitionV0 { + pro_tx_hash: Identifier::random(), + voter_identity_id: Identifier::random(), + vote: Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 { + vote_poll: VotePoll::ContestedDocumentResourceVotePoll( + ContestedDocumentResourceVotePoll { + contract_id: Default::default(), + document_type_name: "test".to_string(), + index_name: "idx".to_string(), + index_values: vec![], + }, + ), + resource_vote_choice: ResourceVoteChoice::Abstain, + })), + nonce: 1, + signature_public_key_id: 2, + signature: [0u8; 65].to_vec().into(), + }) + } + + #[test] + fn test_default_versioned() { + let t = MasternodeVoteTransition::default_versioned(LATEST_PLATFORM_VERSION) + .expect("should create default"); + match t { + MasternodeVoteTransition::V0(_) => {} + } + } + + #[test] + fn test_serialization_roundtrip() { + let t = make_vote(); + let bytes = t.serialize_to_bytes().expect("should serialize"); + let restored = + MasternodeVoteTransition::deserialize_from_bytes(&bytes).expect("should deserialize"); + assert_eq!(t, restored); + } + + #[test] + fn test_state_transition_like() { + let t = make_vote(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::MasternodeVote + ); + assert_eq!(t.state_transition_protocol_version(), 0); + let ids = t.modified_data_ids(); + assert_eq!(ids.len(), 1); + let unique = t.unique_identifiers(); + assert_eq!(unique.len(), 1); + } + + #[test] + fn test_owner_id() { + let t = make_vote(); + match &t { + MasternodeVoteTransition::V0(v0) => { + assert_eq!(t.owner_id(), v0.voter_identity_id); + } + } + } + + #[test] + fn test_single_signed() { + let mut t = make_vote(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![1, 2])); + assert_eq!(t.signature().as_slice(), &[1, 2]); + t.set_signature_bytes(vec![3, 4]); + assert_eq!(t.signature().as_slice(), &[3, 4]); + } + + #[test] + fn test_field_types() { + let sig = MasternodeVoteTransition::signature_property_paths(); + assert_eq!(sig.len(), 1); + let ids = MasternodeVoteTransition::identifiers_property_paths(); + assert_eq!(ids.len(), 1); + let bin = MasternodeVoteTransition::binary_property_paths(); + assert!(bin.is_empty()); + } + + #[test] + fn test_estimated_fee() { + let t = make_vote(); + let fee = t + .calculate_min_required_fee(LATEST_PLATFORM_VERSION) + .expect("fee calc should work"); + assert!(fee > 0); + } + + #[test] + fn test_value_conversion_roundtrip() { + let t = make_vote(); + let obj = StateTransitionValueConvert::to_object(&t, false).expect("should work"); + let restored = ::from_object( + obj, + LATEST_PLATFORM_VERSION, + ) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_from_value_map() { + let t = make_vote(); + let obj = StateTransitionValueConvert::to_object(&t, false).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + let restored = ::from_value_map( + map, + LATEST_PLATFORM_VERSION, + ) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_from_object_unknown_version() { + let value = Value::from([("$stateTransitionProtocolVersion", Value::U16(255))]); + let result = ::from_object( + value, + LATEST_PLATFORM_VERSION, + ); + assert!(result.is_err()); + } + + #[test] + fn test_into_from_v0() { + let v0 = MasternodeVoteTransitionV0::default(); + let t: MasternodeVoteTransition = v0.clone().into(); + match t { + MasternodeVoteTransition::V0(inner) => assert_eq!(inner, v0), + } + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/v0/mod.rs index 2b46d03e12a..65c35bf6d08 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/masternode_vote_transition/v0/mod.rs @@ -105,4 +105,135 @@ mod test { test_masternode_vote_transition(transition); } + + fn make_vote_v0() -> MasternodeVoteTransitionV0 { + MasternodeVoteTransitionV0 { + pro_tx_hash: Identifier::random(), + voter_identity_id: Identifier::random(), + vote: Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 { + vote_poll: VotePoll::ContestedDocumentResourceVotePoll( + ContestedDocumentResourceVotePoll { + contract_id: Default::default(), + document_type_name: "test_doc".to_string(), + index_name: "idx".to_string(), + index_values: vec![], + }, + ), + resource_vote_choice: ResourceVoteChoice::Abstain, + })), + nonce: 7, + signature_public_key_id: 3, + signature: [0u8; 65].to_vec().into(), + } + } + + #[test] + fn test_default() { + let t = MasternodeVoteTransitionV0::default(); + assert_eq!(t.nonce, 0); + assert_eq!(t.signature_public_key_id, 0); + } + + #[test] + fn test_state_transition_like_v0() { + use crate::state_transition::{ + StateTransitionLike, StateTransitionOwned, StateTransitionType, + }; + let t = make_vote_v0(); + assert_eq!( + t.state_transition_type(), + StateTransitionType::MasternodeVote + ); + assert_eq!(t.state_transition_protocol_version(), 0); + assert_eq!(t.modified_data_ids(), vec![t.voter_identity_id]); + assert_eq!(t.owner_id(), t.voter_identity_id); + } + + #[test] + fn test_unique_identifiers_v0() { + use crate::state_transition::StateTransitionLike; + let t = make_vote_v0(); + let ids = t.unique_identifiers(); + assert_eq!(ids.len(), 1); + assert!(!ids[0].is_empty()); + } + + #[test] + fn test_identity_signed_v0() { + use crate::identity::{Purpose, SecurityLevel}; + use crate::state_transition::StateTransitionIdentitySigned; + let mut t = make_vote_v0(); + assert_eq!(t.signature_public_key_id(), 3); + t.set_signature_public_key_id(77); + assert_eq!(t.signature_public_key_id(), 77); + let security = t.security_level_requirement(Purpose::VOTING); + assert!(security.contains(&SecurityLevel::CRITICAL)); + assert!(security.contains(&SecurityLevel::HIGH)); + assert!(security.contains(&SecurityLevel::MEDIUM)); + let purpose = t.purpose_requirement(); + assert_eq!(purpose, vec![Purpose::VOTING]); + } + + #[test] + fn test_single_signed_v0() { + use crate::state_transition::StateTransitionSingleSigned; + use platform_value::BinaryData; + let mut t = make_vote_v0(); + assert_eq!(t.signature().len(), 65); + t.set_signature(BinaryData::new(vec![9, 8, 7])); + assert_eq!(t.signature().as_slice(), &[9, 8, 7]); + t.set_signature_bytes(vec![6, 5]); + assert_eq!(t.signature().as_slice(), &[6, 5]); + } + + #[test] + fn test_into_state_transition_v0() { + use crate::state_transition::StateTransition; + let t = make_vote_v0(); + let st: StateTransition = t.into(); + match st { + StateTransition::MasternodeVote(_) => {} + _ => panic!("expected MasternodeVote"), + } + } + + #[test] + fn test_value_conversion_roundtrip_v0() { + use crate::state_transition::StateTransitionValueConvert; + use crate::version::LATEST_PLATFORM_VERSION; + let t = make_vote_v0(); + let obj = t.to_object(false).expect("to_object should work"); + let restored = MasternodeVoteTransitionV0::from_object(obj, LATEST_PLATFORM_VERSION) + .expect("from_object should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_from_value_map_v0() { + use crate::state_transition::StateTransitionValueConvert; + use crate::version::LATEST_PLATFORM_VERSION; + let t = make_vote_v0(); + let obj = t.to_object(false).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + let restored = MasternodeVoteTransitionV0::from_value_map(map, LATEST_PLATFORM_VERSION) + .expect("should work"); + assert_eq!(t, restored); + } + + #[test] + fn test_to_cleaned_object_v0() { + use crate::state_transition::StateTransitionValueConvert; + let t = make_vote_v0(); + let obj = t.to_cleaned_object(false).expect("should work"); + assert!(obj.is_map()); + } + + #[test] + fn test_to_object_skip_signature_v0() { + use crate::state_transition::StateTransitionValueConvert; + let t = make_vote_v0(); + let obj = t.to_object(true).expect("should work"); + let map = obj.into_btree_string_map().expect("should be map"); + assert!(!map.contains_key("signature")); + } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/mod.rs index a3d0db374ef..0d96aa21e4c 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/mod.rs @@ -97,3 +97,286 @@ impl From<&IdentityPublicKey> for IdentityPublicKeyInCreation { } } } + +#[cfg(test)] +mod test { + use super::*; + use crate::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; + use crate::identity::{KeyType, Purpose, SecurityLevel}; + use crate::state_transition::public_key_in_creation::accessors::IdentityPublicKeyInCreationV0Getters; + use crate::state_transition::public_key_in_creation::methods::IdentityPublicKeyInCreationMethodsV0; + use crate::version::LATEST_PLATFORM_VERSION; + use platform_value::BinaryData; + + fn make_master_key(id: u16) -> IdentityPublicKeyInCreation { + IdentityPublicKeyInCreation::V0(IdentityPublicKeyInCreationV0 { + id: id.into(), + key_type: KeyType::ECDSA_SECP256K1, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::MASTER, + contract_bounds: None, + read_only: false, + data: BinaryData::new(vec![0u8; 33]), + signature: BinaryData::new(vec![0u8; 65]), + }) + } + + fn make_high_key(id: u16) -> IdentityPublicKeyInCreation { + IdentityPublicKeyInCreation::V0(IdentityPublicKeyInCreationV0 { + id: id.into(), + key_type: KeyType::ECDSA_SECP256K1, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::HIGH, + contract_bounds: None, + read_only: false, + data: BinaryData::new(vec![id as u8; 33]), + signature: BinaryData::new(vec![]), + }) + } + + fn make_critical_transfer_key(id: u16) -> IdentityPublicKeyInCreation { + IdentityPublicKeyInCreation::V0(IdentityPublicKeyInCreationV0 { + id: id.into(), + key_type: KeyType::ECDSA_SECP256K1, + purpose: Purpose::TRANSFER, + security_level: SecurityLevel::CRITICAL, + contract_bounds: None, + read_only: false, + data: BinaryData::new(vec![id as u8; 33]), + signature: BinaryData::new(vec![]), + }) + } + + #[test] + fn test_default_versioned() { + let key = IdentityPublicKeyInCreation::default_versioned(LATEST_PLATFORM_VERSION) + .expect("should create default"); + match key { + IdentityPublicKeyInCreation::V0(_) => {} + } + } + + #[test] + fn test_from_into_identity_public_key() { + let key = make_master_key(0); + let pk: IdentityPublicKey = key.clone().into(); + let back: IdentityPublicKeyInCreation = pk.into(); + assert_eq!(back.id(), key.id()); + assert_eq!(back.purpose(), key.purpose()); + } + + #[test] + fn test_from_ref_into_identity_public_key() { + let key = make_master_key(0); + let pk: IdentityPublicKey = (&key).into(); + assert_eq!(pk.id(), key.id()); + } + + #[test] + fn test_from_identity_public_key_ref() { + let key = make_master_key(0); + let pk: IdentityPublicKey = key.clone().into(); + let back: IdentityPublicKeyInCreation = (&pk).into(); + assert_eq!(back.id(), key.id()); + } + + #[test] + fn test_into_identity_public_key_method() { + let key = make_master_key(0); + let pk = key.clone().into_identity_public_key(); + assert_eq!(pk.id(), key.id()); + } + + #[test] + fn test_validate_structure_valid_create() { + let keys = vec![make_master_key(0), make_high_key(1)]; + let result = IdentityPublicKeyInCreation::validate_identity_public_keys_structure( + &keys, + true, + LATEST_PLATFORM_VERSION, + ) + .expect("validation should not error"); + assert!( + result.is_valid(), + "valid keys should pass: {:?}", + result.errors + ); + } + + #[test] + fn test_validate_structure_missing_master_in_create() { + let keys = vec![make_high_key(0)]; + let result = IdentityPublicKeyInCreation::validate_identity_public_keys_structure( + &keys, + true, + LATEST_PLATFORM_VERSION, + ) + .expect("validation should not error"); + assert!(!result.is_valid(), "should fail without master key"); + } + + #[test] + fn test_validate_structure_too_many_master_in_create() { + let keys = vec![make_master_key(0), make_master_key(1)]; + let result = IdentityPublicKeyInCreation::validate_identity_public_keys_structure( + &keys, + true, + LATEST_PLATFORM_VERSION, + ) + .expect("validation should not error"); + // Keys have same data, will be caught as duplicate data + assert!( + !result.is_valid(), + "should fail with duplicated master keys" + ); + } + + #[test] + fn test_validate_structure_duplicate_key_ids() { + // Two keys with the same id + let keys = vec![make_master_key(0), make_high_key(0)]; + let result = IdentityPublicKeyInCreation::validate_identity_public_keys_structure( + &keys, + true, + LATEST_PLATFORM_VERSION, + ) + .expect("validation should not error"); + assert!(!result.is_valid(), "should fail with duplicate key ids"); + } + + #[test] + fn test_validate_structure_duplicate_key_data() { + // Two keys with the same data but different ids + let key1 = make_master_key(0); + let key2_inner = IdentityPublicKeyInCreationV0 { + id: 1, + key_type: KeyType::ECDSA_SECP256K1, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::HIGH, + contract_bounds: None, + read_only: false, + data: BinaryData::new(vec![0u8; 33]), // same data as key1 + signature: BinaryData::new(vec![]), + }; + let key2 = IdentityPublicKeyInCreation::V0(key2_inner); + let keys = vec![key1, key2]; + let result = IdentityPublicKeyInCreation::validate_identity_public_keys_structure( + &keys, + true, + LATEST_PLATFORM_VERSION, + ) + .expect("validation should not error"); + assert!(!result.is_valid(), "should fail with duplicate key data"); + } + + #[test] + fn test_validate_structure_not_in_create_no_master_required() { + // When not in create, master key is not required + let keys = vec![make_high_key(0)]; + let result = IdentityPublicKeyInCreation::validate_identity_public_keys_structure( + &keys, + false, + LATEST_PLATFORM_VERSION, + ) + .expect("validation should not error"); + assert!( + result.is_valid(), + "should pass without master key when not in create: {:?}", + result.errors + ); + } + + #[test] + fn test_validate_structure_transfer_key_valid() { + let keys = vec![make_master_key(0), make_critical_transfer_key(1)]; + let result = IdentityPublicKeyInCreation::validate_identity_public_keys_structure( + &keys, + true, + LATEST_PLATFORM_VERSION, + ) + .expect("validation should not error"); + assert!(result.is_valid(), "should pass: {:?}", result.errors); + } + + #[test] + fn test_validate_structure_invalid_security_level_for_purpose() { + // Transfer keys must be CRITICAL security level + let bad_key = IdentityPublicKeyInCreation::V0(IdentityPublicKeyInCreationV0 { + id: 1, + key_type: KeyType::ECDSA_SECP256K1, + purpose: Purpose::TRANSFER, + security_level: SecurityLevel::HIGH, // invalid for TRANSFER + contract_bounds: None, + read_only: false, + data: BinaryData::new(vec![1u8; 33]), + signature: BinaryData::new(vec![]), + }); + let keys = vec![make_master_key(0), bad_key]; + let result = IdentityPublicKeyInCreation::validate_identity_public_keys_structure( + &keys, + true, + LATEST_PLATFORM_VERSION, + ) + .expect("validation should not error"); + assert!( + !result.is_valid(), + "should fail with invalid security level for purpose" + ); + } + + #[test] + fn test_duplicated_key_ids_witness() { + let keys = vec![make_master_key(0), make_high_key(0)]; + let dups = + IdentityPublicKeyInCreation::duplicated_key_ids_witness(&keys, LATEST_PLATFORM_VERSION) + .expect("should work"); + assert_eq!(dups.len(), 1); + } + + #[test] + fn test_duplicated_key_ids_witness_no_dups() { + let keys = vec![make_master_key(0), make_high_key(1)]; + let dups = + IdentityPublicKeyInCreation::duplicated_key_ids_witness(&keys, LATEST_PLATFORM_VERSION) + .expect("should work"); + assert!(dups.is_empty()); + } + + #[test] + fn test_duplicated_keys_witness() { + // Keys with same data + let key1 = make_master_key(0); + let key2 = IdentityPublicKeyInCreation::V0(IdentityPublicKeyInCreationV0 { + id: 1, + key_type: KeyType::ECDSA_SECP256K1, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::HIGH, + contract_bounds: None, + read_only: false, + data: BinaryData::new(vec![0u8; 33]), + signature: BinaryData::new(vec![]), + }); + let keys = vec![key1, key2]; + let dups = + IdentityPublicKeyInCreation::duplicated_keys_witness(&keys, LATEST_PLATFORM_VERSION) + .expect("should work"); + assert_eq!(dups.len(), 1); + } + + #[test] + fn test_hash_with_hash160_key() { + // ECDSA_HASH160 keys don't need valid secp256k1 data for hashing + let key = IdentityPublicKeyInCreation::V0(IdentityPublicKeyInCreationV0 { + id: 0, + key_type: KeyType::ECDSA_HASH160, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::MASTER, + contract_bounds: None, + read_only: false, + data: BinaryData::new(vec![0u8; 20]), + signature: BinaryData::new(vec![]), + }); + let hash = key.hash().expect("should hash"); + assert_eq!(hash.len(), 20); + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/v0/mod.rs index a521370447f..fc4ef336d45 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/v0/mod.rs @@ -350,3 +350,135 @@ impl TryFrom<&IdentityPublicKeyInCreationV0> for Value { platform_value::to_value(value) } } + +#[cfg(test)] +mod test { + use super::*; + use crate::identity::{KeyType, Purpose, SecurityLevel}; + use crate::state_transition::public_key_in_creation::accessors::{ + IdentityPublicKeyInCreationV0Getters, IdentityPublicKeyInCreationV0Setters, + }; + use crate::state_transition::public_key_in_creation::methods::IdentityPublicKeyInCreationMethodsV0; + + fn make_key_v0() -> IdentityPublicKeyInCreationV0 { + IdentityPublicKeyInCreationV0 { + id: 0, + key_type: KeyType::ECDSA_SECP256K1, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::MASTER, + contract_bounds: None, + read_only: false, + data: BinaryData::new(vec![0u8; 33]), + signature: BinaryData::new(vec![0u8; 65]), + } + } + + #[test] + fn test_default() { + let key = IdentityPublicKeyInCreationV0::default(); + assert_eq!(key.id, 0); + assert!(key.data.is_empty()); + assert!(key.signature.is_empty()); + } + + #[test] + fn test_getters() { + let key = make_key_v0(); + assert_eq!(key.id(), 0); + assert_eq!(key.key_type(), KeyType::ECDSA_SECP256K1); + assert_eq!(key.purpose(), Purpose::AUTHENTICATION); + assert_eq!(key.security_level(), SecurityLevel::MASTER); + assert!(!key.read_only()); + assert_eq!(key.data().len(), 33); + assert_eq!(key.signature().len(), 65); + assert!(key.contract_bounds().is_none()); + } + + #[test] + fn test_setters() { + let mut key = make_key_v0(); + key.set_id(5); + assert_eq!(key.id(), 5); + key.set_type(KeyType::BLS12_381); + assert_eq!(key.key_type(), KeyType::BLS12_381); + key.set_purpose(Purpose::TRANSFER); + assert_eq!(key.purpose(), Purpose::TRANSFER); + key.set_security_level(SecurityLevel::CRITICAL); + assert_eq!(key.security_level(), SecurityLevel::CRITICAL); + key.set_read_only(true); + assert!(key.read_only()); + key.set_data(BinaryData::new(vec![1, 2, 3])); + assert_eq!(key.data().as_slice(), &[1, 2, 3]); + key.set_signature(BinaryData::new(vec![4, 5])); + assert_eq!(key.signature().as_slice(), &[4, 5]); + key.set_contract_bounds(None); + assert!(key.contract_bounds().is_none()); + } + + #[test] + fn test_into_identity_public_key() { + let key = make_key_v0(); + let pk = key.clone().into_identity_public_key(); + assert_eq!(pk.id(), key.id); + assert_eq!(pk.purpose(), key.purpose); + assert_eq!(pk.security_level(), key.security_level); + assert_eq!(pk.key_type(), key.key_type); + } + + #[test] + fn test_from_identity_public_key() { + let key = make_key_v0(); + let pk: IdentityPublicKey = key.clone().into(); + let back: IdentityPublicKeyInCreationV0 = pk.into(); + assert_eq!(back.id, key.id); + assert_eq!(back.purpose, key.purpose); + assert_eq!(back.security_level, key.security_level); + assert_eq!(back.key_type, key.key_type); + assert_eq!(back.data, key.data); + assert!(back.signature.is_empty()); // signature is cleared on conversion + } + + #[test] + fn test_from_identity_public_key_ref() { + let key = make_key_v0(); + let pk: IdentityPublicKey = key.clone().into(); + let back: IdentityPublicKeyInCreationV0 = (&pk).into(); + assert_eq!(back.id, key.id); + } + + #[test] + fn test_from_ref_into_identity_public_key() { + let key = make_key_v0(); + let pk: IdentityPublicKey = (&key).into(); + assert_eq!(pk.id(), key.id); + } + + #[test] + fn test_try_from_value_roundtrip() { + let key = make_key_v0(); + let value: Value = (&key).try_into().expect("should convert to value"); + let restored: IdentityPublicKeyInCreationV0 = + value.try_into().expect("should convert from value"); + assert_eq!(key, restored); + } + + #[test] + fn test_try_from_value_owned() { + let key = make_key_v0(); + let value: Value = key.clone().try_into().expect("should convert to value"); + let restored: IdentityPublicKeyInCreationV0 = + value.try_into().expect("should convert from value"); + assert_eq!(key, restored); + } + + #[test] + fn test_is_master() { + let key = make_key_v0(); + assert!(key.is_master()); + let non_master = IdentityPublicKeyInCreationV0 { + security_level: SecurityLevel::HIGH, + ..make_key_v0() + }; + assert!(!non_master.is_master()); + } +}