From 5a8727c717b01e428ab7d324630debb612b34600 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Fri, 20 Feb 2026 15:07:12 -0600 Subject: [PATCH 1/4] test(dpp): complete discarded round-trips in error serialization tests (C-08) --- .../basic/unsupported_protocol_version_error.rs | 11 +++++------ .../consensus/basic/unsupported_version_error.rs | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/rs-dpp/src/errors/consensus/basic/unsupported_protocol_version_error.rs b/packages/rs-dpp/src/errors/consensus/basic/unsupported_protocol_version_error.rs index 9021c8df0e3..610cc30c49a 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/unsupported_protocol_version_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/unsupported_protocol_version_error.rs @@ -47,6 +47,7 @@ impl From for ConsensusError { #[cfg(test)] mod tests { use super::*; + use crate::serialization::PlatformDeserializable; use crate::serialization::PlatformSerializableWithPlatformVersion; use platform_version::version::LATEST_PLATFORM_VERSION; @@ -56,15 +57,13 @@ mod tests { let consensus_error: ConsensusError = error.clone().into(); - let _cbor = consensus_error + let serialized_bytes = consensus_error .serialize_to_bytes_with_platform_version(LATEST_PLATFORM_VERSION) .expect("should serialize"); - // let value = Value::try_from(&consensus_error).expect("should convert to value"); + let recovered_consensus_error = + ConsensusError::deserialize_from_bytes(&serialized_bytes).expect("should deserialize"); - // let recovered_error: UnsupportedProtocolVersionError = - // value.try_into().expect("should recover from value"); - // - // assert_eq!(recovered_error, error); + assert_eq!(consensus_error, recovered_consensus_error); } } diff --git a/packages/rs-dpp/src/errors/consensus/basic/unsupported_version_error.rs b/packages/rs-dpp/src/errors/consensus/basic/unsupported_version_error.rs index cba93d0a35c..5ec01d3bdbb 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/unsupported_version_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/unsupported_version_error.rs @@ -53,6 +53,7 @@ impl From for ConsensusError { #[cfg(test)] mod tests { use super::*; + use crate::serialization::PlatformDeserializable; use crate::serialization::PlatformSerializableWithPlatformVersion; use platform_version::version::LATEST_PLATFORM_VERSION; @@ -62,15 +63,13 @@ mod tests { let consensus_error: ConsensusError = error.clone().into(); - let _cbor = consensus_error + let serialized_bytes = consensus_error .serialize_to_bytes_with_platform_version(LATEST_PLATFORM_VERSION) .expect("expected to serialize"); - // let value = Value::try_from(&consensus_error).expect("should convert to value"); + let recovered_consensus_error = + ConsensusError::deserialize_from_bytes(&serialized_bytes).expect("should deserialize"); - // let recovered_error: UnsupportedVersionError = - // value.try_into().expect("should recover from value"); - // - // assert_eq!(recovered_error, error); + assert_eq!(consensus_error, recovered_consensus_error); } } From b2f16cc689571e271f08f3a0407d0a408c1353d2 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Fri, 20 Feb 2026 15:08:46 -0600 Subject: [PATCH 2/4] test(dpp): fix stale IdentityCreditWithdrawalTransition test structs (C-14) --- .../v0/mod.rs | 120 ++++++------------ 1 file changed, 42 insertions(+), 78 deletions(-) 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 2be39083e0e..dab96bc01d8 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 @@ -46,10 +46,9 @@ pub struct IdentityCreditWithdrawalTransitionV0 { mod test { use crate::identity::core_script::CoreScript; use crate::identity::KeyID; - use crate::prelude::Revision; + use crate::prelude::{IdentityNonce, UserFeeIncrease}; use crate::serialization::{PlatformDeserializable, PlatformSerializable}; use crate::state_transition::identity_credit_withdrawal_transition::v0::Pooling; - use crate::state_transition::StateTransitionType; use crate::ProtocolError; use bincode::{Decode, Encode}; use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; @@ -61,113 +60,97 @@ mod test { #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV01 { - pub protocol_version: u32, + pub identity_id: Identifier, } // Structure with 2 properties #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV02 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, + pub identity_id: Identifier, + pub amount: u64, } // Structure with 3 properties #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV03 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, pub identity_id: Identifier, + pub amount: u64, + pub core_fee_per_byte: u32, } // Structure with 4 properties #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV04 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, pub identity_id: Identifier, pub amount: u64, + pub core_fee_per_byte: u32, + pub pooling: Pooling, } // Structure with 5 properties #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV05 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, pub identity_id: Identifier, pub amount: u64, pub core_fee_per_byte: u32, + pub pooling: Pooling, + pub output_script: CoreScript, } // Structure with 6 properties #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV06 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, pub identity_id: Identifier, pub amount: u64, pub core_fee_per_byte: u32, pub pooling: Pooling, + pub output_script: CoreScript, + pub nonce: IdentityNonce, } // Structure with 7 properties #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV07 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, pub identity_id: Identifier, pub amount: u64, pub core_fee_per_byte: u32, pub pooling: Pooling, pub output_script: CoreScript, + pub nonce: IdentityNonce, + pub user_fee_increase: UserFeeIncrease, } // Structure with 8 properties #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV08 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, pub identity_id: Identifier, pub amount: u64, pub core_fee_per_byte: u32, pub pooling: Pooling, pub output_script: CoreScript, - pub revision: Revision, + pub nonce: IdentityNonce, + pub user_fee_increase: UserFeeIncrease, + pub signature_public_key_id: KeyID, } // Structure with 9 properties #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] #[platform_serialize(unversioned)] struct IdentityCreditWithdrawalTransitionV09 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, pub identity_id: Identifier, pub amount: u64, pub core_fee_per_byte: u32, pub pooling: Pooling, pub output_script: CoreScript, - pub revision: Revision, - pub signature_public_key_id: KeyID, - } - - // Structure with 10 properties - #[derive(Debug, Clone, Encode, Decode, PlatformDeserialize, PlatformSerialize, PartialEq)] - #[platform_serialize(unversioned)] - struct IdentityCreditWithdrawalTransitionV010 { - pub protocol_version: u32, - pub transition_type: StateTransitionType, - pub identity_id: Identifier, - pub amount: u64, - pub core_fee_per_byte: u32, - pub pooling: Pooling, - pub output_script: CoreScript, - pub revision: Revision, + pub nonce: IdentityNonce, + pub user_fee_increase: UserFeeIncrease, pub signature_public_key_id: KeyID, pub signature: BinaryData, } @@ -187,9 +170,8 @@ mod test { #[test] fn test_identity_credit_withdrawal_transition_1() { - let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV01 { - protocol_version: rng.gen(), + identity_id: Identifier::random(), }; test_identity_credit_withdrawal_transition(transition); } @@ -198,8 +180,8 @@ mod test { fn test_identity_credit_withdrawal_transition_2() { let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV02 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, // Generate random value or choose from the available types + identity_id: Identifier::random(), + amount: rng.gen(), }; test_identity_credit_withdrawal_transition(transition); } @@ -208,9 +190,9 @@ mod test { fn test_identity_credit_withdrawal_transition_3() { let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV03 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, // Generate random value or choose from the available types - identity_id: Identifier::random(), // Generate a random Identifier + identity_id: Identifier::random(), + amount: rng.gen(), + core_fee_per_byte: rng.gen(), }; test_identity_credit_withdrawal_transition(transition); } @@ -219,10 +201,10 @@ mod test { fn test_identity_credit_withdrawal_transition_4() { let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV04 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, // Generate random value or choose from the available types - identity_id: Identifier::random(), // Generate a random Identifier + identity_id: Identifier::random(), amount: rng.gen(), + core_fee_per_byte: rng.gen(), + pooling: Pooling::Standard, }; test_identity_credit_withdrawal_transition(transition); } @@ -231,11 +213,11 @@ mod test { fn test_identity_credit_withdrawal_transition_5() { let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV05 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, // Generate random value or choose from the available types - identity_id: Identifier::random(), // Generate a random Identifier + identity_id: Identifier::random(), amount: rng.gen(), core_fee_per_byte: rng.gen(), + pooling: Pooling::Standard, + output_script: CoreScript::from_bytes((0..23).collect::>()), }; test_identity_credit_withdrawal_transition(transition); } @@ -244,12 +226,12 @@ mod test { fn test_identity_credit_withdrawal_transition_6() { let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV06 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, // Generate random value or choose from the available types - identity_id: Identifier::random(), // Generate a random Identifier + identity_id: Identifier::random(), amount: rng.gen(), core_fee_per_byte: rng.gen(), - pooling: Pooling::Standard, // Generate random value or choose from the available options + pooling: Pooling::Standard, + output_script: CoreScript::from_bytes((0..23).collect::>()), + nonce: rng.gen(), }; test_identity_credit_withdrawal_transition(transition); } @@ -258,13 +240,13 @@ mod test { fn test_identity_credit_withdrawal_transition_7() { let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV07 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, identity_id: Identifier::random(), amount: rng.gen(), core_fee_per_byte: rng.gen(), pooling: Pooling::Standard, output_script: CoreScript::from_bytes((0..23).collect::>()), + nonce: rng.gen(), + user_fee_increase: rng.gen(), }; test_identity_credit_withdrawal_transition(transition); } @@ -273,14 +255,14 @@ mod test { fn test_identity_credit_withdrawal_transition_8() { let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV08 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, identity_id: Identifier::random(), amount: rng.gen(), core_fee_per_byte: rng.gen(), pooling: Pooling::Standard, output_script: CoreScript::from_bytes((0..23).collect::>()), - revision: rng.gen(), + nonce: rng.gen(), + user_fee_increase: rng.gen(), + signature_public_key_id: rng.gen(), }; test_identity_credit_withdrawal_transition(transition); } @@ -289,31 +271,13 @@ mod test { fn test_identity_credit_withdrawal_transition_9() { let mut rng = rand::thread_rng(); let transition = IdentityCreditWithdrawalTransitionV09 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, - identity_id: Identifier::random(), - amount: rng.gen(), - core_fee_per_byte: rng.gen(), - pooling: Pooling::Standard, - output_script: CoreScript::from_bytes((0..23).collect::>()), - revision: rng.gen(), - signature_public_key_id: rng.gen(), - }; - test_identity_credit_withdrawal_transition(transition); - } - - #[test] - fn test_identity_credit_withdrawal_transition_10() { - let mut rng = rand::thread_rng(); - let transition = IdentityCreditWithdrawalTransitionV010 { - protocol_version: rng.gen(), - transition_type: StateTransitionType::IdentityCreditWithdrawal, identity_id: Identifier::random(), amount: rng.gen(), core_fee_per_byte: rng.gen(), pooling: Pooling::Standard, output_script: CoreScript::from_bytes((0..23).collect::>()), - revision: rng.gen(), + nonce: rng.gen(), + user_fee_increase: rng.gen(), signature_public_key_id: rng.gen(), signature: [0; 65].to_vec().into(), }; From 5df1da8f8f78bb44c591622617d6f288086f9157 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Fri, 20 Feb 2026 15:09:18 -0600 Subject: [PATCH 3/4] test(dpp): add negative deserialization tests for StateTransition (C-10) --- .../src/state_transition/serialization.rs | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/packages/rs-dpp/src/state_transition/serialization.rs b/packages/rs-dpp/src/state_transition/serialization.rs index 4b2c5d7bf55..fb49b41c110 100644 --- a/packages/rs-dpp/src/state_transition/serialization.rs +++ b/packages/rs-dpp/src/state_transition/serialization.rs @@ -411,4 +411,156 @@ mod tests { .expect("expected to deserialize state transition"); assert_eq!(state_transition, recovered_state_transition); } + + #[test] + fn deserialize_empty_bytes_should_fail() { + let result = StateTransition::deserialize_from_bytes(&[]); + assert!( + result.is_err(), + "deserialization of empty bytes should fail" + ); + } + + #[test] + fn deserialize_single_byte_should_fail() { + let result = StateTransition::deserialize_from_bytes(&[0xFF]); + assert!( + result.is_err(), + "deserialization of a single 0xFF byte should fail" + ); + } + + #[test] + #[cfg(feature = "random-identities")] + fn deserialize_truncated_bytes_should_fail() { + let platform_version = PlatformVersion::latest(); + let identity = Identity::random_identity(5, Some(5), platform_version) + .expect("expected a random identity"); + let transition = IdentityCreditWithdrawalTransitionV0 { + identity_id: identity.id(), + amount: 5000000, + core_fee_per_byte: 34, + pooling: Pooling::Standard, + output_script: CoreScript::from_bytes((0..23).collect::>()), + nonce: 1, + user_fee_increase: 0, + signature_public_key_id: 0, + signature: [1u8; 65].to_vec().into(), + }; + let state_transition: StateTransition = transition.into(); + let bytes = state_transition + .serialize_to_bytes() + .expect("expected to serialize"); + + // Truncate to half + let half = &bytes[..bytes.len() / 2]; + assert!( + StateTransition::deserialize_from_bytes(half).is_err(), + "deserialization of truncated-to-half bytes should fail" + ); + + // Truncate by removing last byte + let minus_one = &bytes[..bytes.len() - 1]; + assert!( + StateTransition::deserialize_from_bytes(minus_one).is_err(), + "deserialization of bytes missing last byte should fail" + ); + + // Keep only first byte + let first_only = &bytes[..1]; + assert!( + StateTransition::deserialize_from_bytes(first_only).is_err(), + "deserialization of only the first byte should fail" + ); + } + + #[test] + #[cfg(feature = "random-identities")] + fn deserialize_corrupted_bytes_should_not_panic() { + let platform_version = PlatformVersion::latest(); + let identity = Identity::random_identity(5, Some(5), platform_version) + .expect("expected a random identity"); + let transition = IdentityCreditWithdrawalTransitionV0 { + identity_id: identity.id(), + amount: 5000000, + core_fee_per_byte: 34, + pooling: Pooling::Standard, + output_script: CoreScript::from_bytes((0..23).collect::>()), + nonce: 1, + user_fee_increase: 0, + signature_public_key_id: 0, + signature: [1u8; 65].to_vec().into(), + }; + let state_transition: StateTransition = transition.into(); + let mut bytes = state_transition + .serialize_to_bytes() + .expect("expected to serialize"); + + // Flip bits in the middle of the payload + let mid = bytes.len() / 2; + bytes[mid] ^= 0xFF; + + // Should either fail or return a different value - must not panic + let result = StateTransition::deserialize_from_bytes(&bytes); + if let Ok(recovered) = result { + assert_ne!( + state_transition, recovered, + "corrupted bytes should not deserialize to the original value" + ); + } + } + + #[test] + fn deserialize_many_empty_list() { + let result = StateTransition::deserialize_many(&[]); + assert_eq!(result.unwrap(), vec![]); + } + + #[test] + fn deserialize_many_with_invalid_entry() { + let result = StateTransition::deserialize_many(&[vec![0xFF]]); + assert!( + result.is_err(), + "deserialize_many with invalid entry should fail" + ); + } + + #[test] + #[cfg(feature = "random-identities")] + fn deserialize_many_with_valid_entries() { + let platform_version = PlatformVersion::latest(); + let identity = Identity::random_identity(5, Some(5), platform_version) + .expect("expected a random identity"); + + let make_transition = |amount: u64, nonce: u64| -> StateTransition { + let t = IdentityCreditWithdrawalTransitionV0 { + identity_id: identity.id(), + amount, + core_fee_per_byte: 34, + pooling: Pooling::Standard, + output_script: CoreScript::from_bytes((0..23).collect::>()), + nonce, + user_fee_increase: 0, + signature_public_key_id: 0, + signature: [1u8; 65].to_vec().into(), + }; + t.into() + }; + + let st1 = make_transition(1000000, 1); + let st2 = make_transition(2000000, 2); + let st3 = make_transition(3000000, 3); + + let raw: Vec> = vec![ + st1.serialize_to_bytes().unwrap(), + st2.serialize_to_bytes().unwrap(), + st3.serialize_to_bytes().unwrap(), + ]; + + let recovered = StateTransition::deserialize_many(&raw).expect("should deserialize all"); + assert_eq!(recovered.len(), 3); + assert_eq!(recovered[0], st1); + assert_eq!(recovered[1], st2); + assert_eq!(recovered[2], st3); + } } From 07ae804e4fc491449875690683b15059053c658f Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Fri, 20 Feb 2026 17:52:13 -0600 Subject: [PATCH 4/4] fix(ffi): correct SPV library path for Cargo workspace in build_ios.sh --- packages/rs-sdk-ffi/build_ios.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/rs-sdk-ffi/build_ios.sh b/packages/rs-sdk-ffi/build_ios.sh index 6a01b36ffac..3fa04d54e8c 100755 --- a/packages/rs-sdk-ffi/build_ios.sh +++ b/packages/rs-sdk-ffi/build_ios.sh @@ -377,11 +377,11 @@ if [ "$BUILD_ARCH" != "x86" ]; then mkdir -p "$OUTPUT_DIR/device" cp "$PROJECT_ROOT/target/aarch64-apple-ios/release/librs_sdk_ffi.a" "$OUTPUT_DIR/device/" # Merge with dash-spv-ffi device lib if available - if [ -f "$SPV_CRATE_PATH/target/aarch64-apple-ios/release/libdash_spv_ffi.a" ]; then + if [ -f "$RUST_DASHCORE_PATH/target/aarch64-apple-ios/release/libdash_spv_ffi.a" ]; then echo -e "${GREEN}Merging device libs (rs-sdk-ffi + dash-spv-ffi)${NC}" libtool -static -o "$OUTPUT_DIR/device/libDashSDKFFI_combined.a" \ "$OUTPUT_DIR/device/librs_sdk_ffi.a" \ - "$SPV_CRATE_PATH/target/aarch64-apple-ios/release/libdash_spv_ffi.a" + "$RUST_DASHCORE_PATH/target/aarch64-apple-ios/release/libdash_spv_ffi.a" COMBINED_DEVICE_LIB=1 fi fi @@ -445,10 +445,10 @@ fi if [ -f "$OUTPUT_DIR/simulator/librs_sdk_ffi.a" ]; then # Try to merge with SPV sim lib if it exists SIM_SPV_LIB="" - if [ -f "$SPV_CRATE_PATH/target/aarch64-apple-ios-sim/release/libdash_spv_ffi.a" ]; then - SIM_SPV_LIB="$SPV_CRATE_PATH/target/aarch64-apple-ios-sim/release/libdash_spv_ffi.a" - elif [ -f "$SPV_CRATE_PATH/target/x86_64-apple-ios/release/libdash_spv_ffi.a" ]; then - SIM_SPV_LIB="$SPV_CRATE_PATH/target/x86_64-apple-ios/release/libdash_spv_ffi.a" + if [ -f "$RUST_DASHCORE_PATH/target/aarch64-apple-ios-sim/release/libdash_spv_ffi.a" ]; then + SIM_SPV_LIB="$RUST_DASHCORE_PATH/target/aarch64-apple-ios-sim/release/libdash_spv_ffi.a" + elif [ -f "$RUST_DASHCORE_PATH/target/x86_64-apple-ios/release/libdash_spv_ffi.a" ]; then + SIM_SPV_LIB="$RUST_DASHCORE_PATH/target/x86_64-apple-ios/release/libdash_spv_ffi.a" fi if [ -n "$SIM_SPV_LIB" ]; then echo -e "${GREEN}Merging simulator libs (rs-sdk-ffi + dash-spv-ffi)${NC}"