Skip to content

Commit ef57031

Browse files
thepastaclawclaude
andcommitted
feat(sdk): add client-side validate_structure() to remaining state transitions
Add client-side structure validation to 6 state transition SDK construction methods, following the pattern established in PR dashpay#3096. This ensures invalid transitions are caught early on the client side before being submitted. State transitions updated: - AddressCreditWithdrawalTransition - AddressFundingFromAssetLockTransition - AddressFundsTransferTransition - IdentityCreateFromAddressesTransition - IdentityCreditTransferToAddressesTransition - IdentityTopUpFromAddressesTransition Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 496fd12 commit ef57031

6 files changed

Lines changed: 65 additions & 7 deletions

File tree

  • packages/rs-dpp/src/state_transition/state_transitions

packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_credit_withdrawal_transition/v0/v0_methods.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use crate::serialization::Signable;
1414
use crate::state_transition::address_credit_withdrawal_transition::methods::AddressCreditWithdrawalTransitionMethodsV0;
1515
use crate::state_transition::address_credit_withdrawal_transition::v0::AddressCreditWithdrawalTransitionV0;
1616
#[cfg(feature = "state-transition-signing")]
17+
use crate::state_transition::StateTransitionStructureValidation;
18+
#[cfg(feature = "state-transition-signing")]
1719
use crate::withdrawal::Pooling;
1820
#[cfg(feature = "state-transition-signing")]
1921
use crate::{
@@ -35,7 +37,7 @@ impl AddressCreditWithdrawalTransitionMethodsV0 for AddressCreditWithdrawalTrans
3537
output_script: CoreScript,
3638
signer: &S,
3739
user_fee_increase: UserFeeIncrease,
38-
_platform_version: &PlatformVersion,
40+
platform_version: &PlatformVersion,
3941
) -> Result<StateTransition, ProtocolError> {
4042
tracing::debug!("try_from_inputs_with_signer: Started");
4143
tracing::debug!(
@@ -66,6 +68,14 @@ impl AddressCreditWithdrawalTransitionMethodsV0 for AddressCreditWithdrawalTrans
6668
.map(|address| signer.sign_create_witness(address, &signable_bytes))
6769
.collect::<Result<Vec<AddressWitness>, ProtocolError>>()?;
6870

71+
// Validate the fully-constructed transition structure
72+
let validation_result =
73+
address_credit_withdrawal_transition.validate_structure(platform_version);
74+
if !validation_result.is_valid() {
75+
let first_error = validation_result.errors.into_iter().next().unwrap();
76+
return Err(ProtocolError::ConsensusError(Box::new(first_error)));
77+
}
78+
6979
tracing::debug!("try_from_inputs_with_signer: Successfully created transition");
7080
Ok(address_credit_withdrawal_transition.into())
7181
}

packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funding_from_asset_lock_transition/v0/v0_methods.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use crate::serialization::Signable;
1414
use crate::state_transition::address_funding_from_asset_lock_transition::methods::AddressFundingFromAssetLockTransitionMethodsV0;
1515
use crate::state_transition::address_funding_from_asset_lock_transition::v0::AddressFundingFromAssetLockTransitionV0;
1616
#[cfg(feature = "state-transition-signing")]
17+
use crate::state_transition::StateTransitionStructureValidation;
18+
#[cfg(feature = "state-transition-signing")]
1719
use crate::{prelude::UserFeeIncrease, state_transition::StateTransition, ProtocolError};
1820
#[cfg(feature = "state-transition-signing")]
1921
use dashcore::signer;
@@ -30,7 +32,7 @@ impl AddressFundingFromAssetLockTransitionMethodsV0 for AddressFundingFromAssetL
3032
fee_strategy: AddressFundsFeeStrategy,
3133
signer: &S,
3234
user_fee_increase: UserFeeIncrease,
33-
_platform_version: &PlatformVersion,
35+
platform_version: &PlatformVersion,
3436
) -> Result<StateTransition, ProtocolError> {
3537
tracing::debug!("try_from_asset_lock_with_signer: Started");
3638
tracing::debug!(
@@ -64,6 +66,13 @@ impl AddressFundingFromAssetLockTransitionMethodsV0 for AddressFundingFromAssetL
6466
.map(|address| signer.sign_create_witness(address, &signable_bytes))
6567
.collect::<Result<Vec<AddressWitness>, ProtocolError>>()?;
6668

69+
// Validate the fully-constructed transition structure
70+
let validation_result = address_funding_transition.validate_structure(platform_version);
71+
if !validation_result.is_valid() {
72+
let first_error = validation_result.errors.into_iter().next().unwrap();
73+
return Err(ProtocolError::ConsensusError(Box::new(first_error)));
74+
}
75+
6776
tracing::debug!("try_from_asset_lock_with_signer: Successfully created transition");
6877
Ok(address_funding_transition.into())
6978
}

packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funds_transfer_transition/v0/v0_methods.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use crate::serialization::Signable;
1212
use crate::state_transition::address_funds_transfer_transition::methods::AddressFundsTransferTransitionMethodsV0;
1313
use crate::state_transition::address_funds_transfer_transition::v0::AddressFundsTransferTransitionV0;
1414
#[cfg(feature = "state-transition-signing")]
15+
use crate::state_transition::StateTransitionStructureValidation;
16+
#[cfg(feature = "state-transition-signing")]
1517
use crate::{
1618
prelude::{AddressNonce, UserFeeIncrease},
1719
state_transition::StateTransition,
@@ -28,7 +30,7 @@ impl AddressFundsTransferTransitionMethodsV0 for AddressFundsTransferTransitionV
2830
fee_strategy: AddressFundsFeeStrategy,
2931
signer: &S,
3032
user_fee_increase: UserFeeIncrease,
31-
_platform_version: &PlatformVersion,
33+
platform_version: &PlatformVersion,
3234
) -> Result<StateTransition, ProtocolError> {
3335
tracing::debug!("try_from_inputs_with_signer: Started");
3436
tracing::debug!(
@@ -55,6 +57,13 @@ impl AddressFundsTransferTransitionMethodsV0 for AddressFundsTransferTransitionV
5557
.map(|address| signer.sign_create_witness(address, &signable_bytes))
5658
.collect::<Result<Vec<AddressWitness>, ProtocolError>>()?;
5759

60+
// Validate the fully-constructed transition structure
61+
let validation_result = address_funds_transition.validate_structure(platform_version);
62+
if !validation_result.is_valid() {
63+
let first_error = validation_result.errors.into_iter().next().unwrap();
64+
return Err(ProtocolError::ConsensusError(Box::new(first_error)));
65+
}
66+
5867
tracing::debug!("try_from_inputs_with_signer: Successfully created transition");
5968
Ok(address_funds_transition.into())
6069
}

packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_from_addresses_transition/v0/v0_methods.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use crate::state_transition::StateTransitionType;
2020
// Crate: Feature-Gated (state-transition-signing)
2121
// ============================
2222
#[cfg(feature = "state-transition-signing")]
23+
use crate::state_transition::StateTransitionStructureValidation;
24+
#[cfg(feature = "state-transition-signing")]
2325
use crate::{
2426
address_funds::AddressFundsFeeStrategy,
2527
identity::{
@@ -105,6 +107,14 @@ impl IdentityCreateFromAddressesTransitionMethodsV0 for IdentityCreateFromAddres
105107
.map(|address| address_signer.sign_create_witness(address, &signable_bytes))
106108
.collect::<Result<Vec<_>, ProtocolError>>()?;
107109

110+
// Validate the fully-constructed transition structure
111+
let validation_result =
112+
identity_create_from_addresses_transition.validate_structure(platform_version);
113+
if !validation_result.is_valid() {
114+
let first_error = validation_result.errors.into_iter().next().unwrap();
115+
return Err(ProtocolError::ConsensusError(Box::new(first_error)));
116+
}
117+
108118
Ok(identity_create_from_addresses_transition.into())
109119
}
110120

packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_to_addresses_transition/v0/v0_methods.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::address_funds::PlatformAddress;
66
#[cfg(feature = "state-transition-signing")]
77
use crate::fee::Credits;
88
#[cfg(feature = "state-transition-signing")]
9+
use crate::state_transition::StateTransitionStructureValidation;
10+
#[cfg(feature = "state-transition-signing")]
911
use crate::{
1012
identity::{
1113
accessors::IdentityGettersV0,
@@ -35,22 +37,31 @@ impl IdentityCreditTransferToAddressesTransitionMethodsV0
3537
signer: &S,
3638
signing_withdrawal_key_to_use: Option<&IdentityPublicKey>,
3739
nonce: IdentityNonce,
38-
_platform_version: &PlatformVersion,
40+
platform_version: &PlatformVersion,
3941
_version: Option<FeatureVersion>,
4042
) -> Result<StateTransition, ProtocolError> {
4143
tracing::debug!("try_from_identity: Started");
4244
tracing::debug!(identity_id = %identity.id(), "try_from_identity");
4345
tracing::debug!(recipient_addresses = ?to_recipient_addresses, has_signing_key = signing_withdrawal_key_to_use.is_some(), "try_from_identity inputs");
4446

45-
let mut transition: StateTransition = IdentityCreditTransferToAddressesTransitionV0 {
47+
let transition_v0 = IdentityCreditTransferToAddressesTransitionV0 {
4648
identity_id: identity.id(),
4749
recipient_addresses: to_recipient_addresses,
4850
nonce,
4951
user_fee_increase,
5052
signature_public_key_id: 0,
5153
signature: Default::default(),
54+
};
55+
56+
// Validate structure before .into() conversion and signing, since this transition
57+
// uses sign_external on the StateTransition rather than setting witnesses on the V0 struct.
58+
let validation_result = transition_v0.validate_structure(platform_version);
59+
if !validation_result.is_valid() {
60+
let first_error = validation_result.errors.into_iter().next().unwrap();
61+
return Err(ProtocolError::ConsensusError(Box::new(first_error)));
5262
}
53-
.into();
63+
64+
let mut transition: StateTransition = transition_v0.into();
5465

5566
let identity_public_key = match signing_withdrawal_key_to_use {
5667
Some(key) => {

packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_from_addresses_transition/v0/v0_methods.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use {
1919
prelude::{AddressNonce, UserFeeIncrease},
2020
serialization::Signable,
2121
state_transition::StateTransition,
22+
state_transition::StateTransitionStructureValidation,
2223
version::FeatureVersion,
2324
ProtocolError,
2425
},
@@ -33,7 +34,7 @@ impl IdentityTopUpFromAddressesTransitionMethodsV0 for IdentityTopUpFromAddresse
3334
inputs: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
3435
signer: &S,
3536
user_fee_increase: UserFeeIncrease,
36-
_platform_version: &PlatformVersion,
37+
platform_version: &PlatformVersion,
3738
_version: Option<FeatureVersion>,
3839
) -> Result<StateTransition, ProtocolError> {
3940
let mut identity_top_up_from_addresses_transition =
@@ -58,6 +59,14 @@ impl IdentityTopUpFromAddressesTransitionMethodsV0 for IdentityTopUpFromAddresse
5859
.map(|address| signer.sign_create_witness(address, &signable_bytes))
5960
.collect::<Result<Vec<AddressWitness>, ProtocolError>>()?;
6061

62+
// Validate the fully-constructed transition structure
63+
let validation_result =
64+
identity_top_up_from_addresses_transition.validate_structure(platform_version);
65+
if !validation_result.is_valid() {
66+
let first_error = validation_result.errors.into_iter().next().unwrap();
67+
return Err(ProtocolError::ConsensusError(Box::new(first_error)));
68+
}
69+
6170
Ok(identity_top_up_from_addresses_transition.into())
6271
}
6372
}

0 commit comments

Comments
 (0)