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()); + } +}