From ec501e5e7eef3746534a16e6fa78b8411fbe32aa Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Fri, 20 Feb 2026 16:06:26 -0600 Subject: [PATCH 1/7] feat(sdk): add client-side validate_base_structure for document and token transitions Add structural validation to all document and token SDK transition builders, matching the pattern from PR #3096 (identity/address transitions). Calls validate_base_structure() on BatchTransition after construction but before broadcast, catching invalid transitions early. Applied to: - Document transitions: create, delete, replace, purchase, set_price, transfer - Token builders: burn, claim, config_update, destroy, purchase, emergency_action, freeze, mint, set_price, transfer, unfreeze - Enabled dpp 'validation' feature for dash-sdk crate --- .gitignore | 1 + packages/rs-sdk/Cargo.toml | 1 + .../src/platform/documents/transitions/create.rs | 16 ++++++++++++++++ .../src/platform/documents/transitions/delete.rs | 16 ++++++++++++++++ .../platform/documents/transitions/purchase.rs | 16 ++++++++++++++++ .../platform/documents/transitions/replace.rs | 16 ++++++++++++++++ .../platform/documents/transitions/set_price.rs | 16 ++++++++++++++++ .../platform/documents/transitions/transfer.rs | 16 ++++++++++++++++ .../rs-sdk/src/platform/tokens/builders/burn.rs | 16 ++++++++++++++++ .../rs-sdk/src/platform/tokens/builders/claim.rs | 16 ++++++++++++++++ .../platform/tokens/builders/config_update.rs | 16 ++++++++++++++++ .../src/platform/tokens/builders/destroy.rs | 16 ++++++++++++++++ .../platform/tokens/builders/emergency_action.rs | 16 ++++++++++++++++ .../src/platform/tokens/builders/freeze.rs | 16 ++++++++++++++++ .../rs-sdk/src/platform/tokens/builders/mint.rs | 16 ++++++++++++++++ .../src/platform/tokens/builders/purchase.rs | 16 ++++++++++++++++ .../src/platform/tokens/builders/set_price.rs | 16 ++++++++++++++++ .../src/platform/tokens/builders/transfer.rs | 16 ++++++++++++++++ .../src/platform/tokens/builders/unfreeze.rs | 16 ++++++++++++++++ 19 files changed, 274 insertions(+) diff --git a/.gitignore b/.gitignore index c1874047d34..e8b403b4e09 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,4 @@ book/book/ # gRPC coverage report grpc-coverage-report.txt +worktrees/ diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index 41ffae82784..6252cce415e 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -9,6 +9,7 @@ arc-swap = { version = "1.7.1" } chrono = { version = "0.4.38" } dpp = { path = "../rs-dpp", default-features = false, features = [ "dash-sdk-features", + "validation", ] } dapi-grpc = { path = "../dapi-grpc", default-features = false } rs-dapi-client = { path = "../rs-dapi-client", default-features = false } diff --git a/packages/rs-sdk/src/platform/documents/transitions/create.rs b/packages/rs-sdk/src/platform/documents/transitions/create.rs index 26c77e4acda..01d4db10d2e 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/create.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/create.rs @@ -167,6 +167,22 @@ impl DocumentCreateTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/documents/transitions/delete.rs b/packages/rs-sdk/src/platform/documents/transitions/delete.rs index 2f1a8c005c3..f3158ac22fc 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/delete.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/delete.rs @@ -207,6 +207,22 @@ impl DocumentDeleteTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/documents/transitions/purchase.rs b/packages/rs-sdk/src/platform/documents/transitions/purchase.rs index b7832cb7782..4bc860585ba 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/purchase.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/purchase.rs @@ -221,6 +221,22 @@ impl DocumentPurchaseTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/documents/transitions/replace.rs b/packages/rs-sdk/src/platform/documents/transitions/replace.rs index aacfc2f9624..e2e979db293 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/replace.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/replace.rs @@ -160,6 +160,22 @@ impl DocumentReplaceTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/documents/transitions/set_price.rs b/packages/rs-sdk/src/platform/documents/transitions/set_price.rs index 61316f3b5c5..005b0f47eb2 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/set_price.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/set_price.rs @@ -208,6 +208,22 @@ impl DocumentSetPriceTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/documents/transitions/transfer.rs b/packages/rs-sdk/src/platform/documents/transitions/transfer.rs index ae1f2afbb04..4f7b9c1d438 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/transfer.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/transfer.rs @@ -207,6 +207,22 @@ impl DocumentTransferTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/burn.rs b/packages/rs-sdk/src/platform/tokens/builders/burn.rs index 5519042a615..6516861a209 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/burn.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/burn.rs @@ -179,6 +179,22 @@ impl TokenBurnTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/claim.rs b/packages/rs-sdk/src/platform/tokens/builders/claim.rs index bde33938446..1d148027d4e 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/claim.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/claim.rs @@ -165,6 +165,22 @@ impl TokenClaimTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/config_update.rs b/packages/rs-sdk/src/platform/tokens/builders/config_update.rs index 8cf15ae4a85..f3012694947 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/config_update.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/config_update.rs @@ -187,6 +187,22 @@ impl TokenConfigUpdateTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/destroy.rs b/packages/rs-sdk/src/platform/tokens/builders/destroy.rs index 8269ce074ee..a1c899ae91f 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/destroy.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/destroy.rs @@ -185,6 +185,22 @@ impl TokenDestroyFrozenFundsTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs b/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs index c6f3e97caf3..7e29c41b52d 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs @@ -213,6 +213,22 @@ impl TokenEmergencyActionTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/freeze.rs b/packages/rs-sdk/src/platform/tokens/builders/freeze.rs index 29c4f4d86d5..64a0acd4cb0 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/freeze.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/freeze.rs @@ -185,6 +185,22 @@ impl TokenFreezeTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/mint.rs b/packages/rs-sdk/src/platform/tokens/builders/mint.rs index fe14d7bf5f2..81b1cb37f2c 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/mint.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/mint.rs @@ -206,6 +206,22 @@ impl TokenMintTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/purchase.rs b/packages/rs-sdk/src/platform/tokens/builders/purchase.rs index fce0a483cf8..3fafdd9c60c 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/purchase.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/purchase.rs @@ -153,6 +153,22 @@ impl TokenDirectPurchaseTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/set_price.rs b/packages/rs-sdk/src/platform/tokens/builders/set_price.rs index 65ded0944e3..36965f5d9c6 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/set_price.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/set_price.rs @@ -230,6 +230,22 @@ impl TokenChangeDirectPurchasePriceTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/transfer.rs b/packages/rs-sdk/src/platform/tokens/builders/transfer.rs index aa0162021d2..b2e58817648 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/transfer.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/transfer.rs @@ -211,6 +211,22 @@ impl TokenTransferTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } diff --git a/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs b/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs index e1d3f7ef2b4..a6864cca296 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs @@ -185,6 +185,22 @@ impl TokenUnfreezeTransitionBuilder { self.state_transition_creation_options, )?; + // Validate the transition structure before returning + let validation_result = match &state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ))); + } + }; + if !validation_result.is_valid() { + let first_error = validation_result.errors.into_iter().next().unwrap(); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + } + Ok(state_transition) } } From 818278d2a204538076c7183614f341dac8ce5683 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Fri, 20 Feb 2026 17:31:45 -0600 Subject: [PATCH 2/7] fix: replace unwrap() with safe if-let in validation error handling CodeRabbit correctly flagged that validation_result.errors.into_iter() .next().unwrap() could panic if is_valid() returns false but the errors vec is somehow empty. Use if-let pattern instead for safe handling. --- packages/rs-sdk/src/platform/documents/transitions/create.rs | 3 +-- packages/rs-sdk/src/platform/documents/transitions/delete.rs | 3 +-- packages/rs-sdk/src/platform/documents/transitions/purchase.rs | 3 +-- packages/rs-sdk/src/platform/documents/transitions/replace.rs | 3 +-- .../rs-sdk/src/platform/documents/transitions/set_price.rs | 3 +-- packages/rs-sdk/src/platform/documents/transitions/transfer.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/burn.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/claim.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/config_update.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/destroy.rs | 3 +-- .../rs-sdk/src/platform/tokens/builders/emergency_action.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/freeze.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/mint.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/purchase.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/set_price.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/transfer.rs | 3 +-- packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs | 3 +-- 17 files changed, 17 insertions(+), 34 deletions(-) diff --git a/packages/rs-sdk/src/platform/documents/transitions/create.rs b/packages/rs-sdk/src/platform/documents/transitions/create.rs index 01d4db10d2e..e9ef65fb684 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/create.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/create.rs @@ -178,8 +178,7 @@ impl DocumentCreateTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/documents/transitions/delete.rs b/packages/rs-sdk/src/platform/documents/transitions/delete.rs index f3158ac22fc..dc254ce76a5 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/delete.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/delete.rs @@ -218,8 +218,7 @@ impl DocumentDeleteTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/documents/transitions/purchase.rs b/packages/rs-sdk/src/platform/documents/transitions/purchase.rs index 4bc860585ba..8fd93529d14 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/purchase.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/purchase.rs @@ -232,8 +232,7 @@ impl DocumentPurchaseTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/documents/transitions/replace.rs b/packages/rs-sdk/src/platform/documents/transitions/replace.rs index e2e979db293..e85fb551c6a 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/replace.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/replace.rs @@ -171,8 +171,7 @@ impl DocumentReplaceTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/documents/transitions/set_price.rs b/packages/rs-sdk/src/platform/documents/transitions/set_price.rs index 005b0f47eb2..435e9ebee0b 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/set_price.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/set_price.rs @@ -219,8 +219,7 @@ impl DocumentSetPriceTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/documents/transitions/transfer.rs b/packages/rs-sdk/src/platform/documents/transitions/transfer.rs index 4f7b9c1d438..dfc60854b36 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/transfer.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/transfer.rs @@ -218,8 +218,7 @@ impl DocumentTransferTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/burn.rs b/packages/rs-sdk/src/platform/tokens/builders/burn.rs index 6516861a209..d88dc135112 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/burn.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/burn.rs @@ -190,8 +190,7 @@ impl TokenBurnTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/claim.rs b/packages/rs-sdk/src/platform/tokens/builders/claim.rs index 1d148027d4e..378caa2baf1 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/claim.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/claim.rs @@ -176,8 +176,7 @@ impl TokenClaimTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/config_update.rs b/packages/rs-sdk/src/platform/tokens/builders/config_update.rs index f3012694947..63d20258bbe 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/config_update.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/config_update.rs @@ -198,8 +198,7 @@ impl TokenConfigUpdateTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/destroy.rs b/packages/rs-sdk/src/platform/tokens/builders/destroy.rs index a1c899ae91f..0bcd8416e7a 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/destroy.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/destroy.rs @@ -196,8 +196,7 @@ impl TokenDestroyFrozenFundsTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs b/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs index 7e29c41b52d..a40a8be583a 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs @@ -224,8 +224,7 @@ impl TokenEmergencyActionTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/freeze.rs b/packages/rs-sdk/src/platform/tokens/builders/freeze.rs index 64a0acd4cb0..03646a6248e 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/freeze.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/freeze.rs @@ -196,8 +196,7 @@ impl TokenFreezeTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/mint.rs b/packages/rs-sdk/src/platform/tokens/builders/mint.rs index 81b1cb37f2c..b72387d9d76 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/mint.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/mint.rs @@ -217,8 +217,7 @@ impl TokenMintTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/purchase.rs b/packages/rs-sdk/src/platform/tokens/builders/purchase.rs index 3fafdd9c60c..97b802eaee9 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/purchase.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/purchase.rs @@ -164,8 +164,7 @@ impl TokenDirectPurchaseTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/set_price.rs b/packages/rs-sdk/src/platform/tokens/builders/set_price.rs index 36965f5d9c6..05565e99d40 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/set_price.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/set_price.rs @@ -241,8 +241,7 @@ impl TokenChangeDirectPurchasePriceTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/transfer.rs b/packages/rs-sdk/src/platform/tokens/builders/transfer.rs index b2e58817648..de13cfc65c8 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/transfer.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/transfer.rs @@ -222,8 +222,7 @@ impl TokenTransferTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } diff --git a/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs b/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs index a6864cca296..00ab0af19d1 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs @@ -196,8 +196,7 @@ impl TokenUnfreezeTransitionBuilder { ))); } }; - if !validation_result.is_valid() { - let first_error = validation_result.errors.into_iter().next().unwrap(); + if let Some(first_error) = validation_result.errors.into_iter().next() { return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); } From b523aa6c334f573f38350243ee9adc38b692c22e Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Fri, 20 Feb 2026 19:27:59 -0600 Subject: [PATCH 3/7] style: apply cargo fmt to rs-sdk and rs-dapi --- .../src/platform/documents/transitions/create.rs | 12 ++++++++---- .../src/platform/documents/transitions/delete.rs | 12 ++++++++---- .../src/platform/documents/transitions/purchase.rs | 12 ++++++++---- .../src/platform/documents/transitions/replace.rs | 12 ++++++++---- .../src/platform/documents/transitions/set_price.rs | 12 ++++++++---- .../src/platform/documents/transitions/transfer.rs | 12 ++++++++---- packages/rs-sdk/src/platform/tokens/builders/burn.rs | 12 ++++++++---- .../rs-sdk/src/platform/tokens/builders/claim.rs | 12 ++++++++---- .../src/platform/tokens/builders/config_update.rs | 12 ++++++++---- .../rs-sdk/src/platform/tokens/builders/destroy.rs | 12 ++++++++---- .../src/platform/tokens/builders/emergency_action.rs | 12 ++++++++---- .../rs-sdk/src/platform/tokens/builders/freeze.rs | 12 ++++++++---- packages/rs-sdk/src/platform/tokens/builders/mint.rs | 12 ++++++++---- .../rs-sdk/src/platform/tokens/builders/purchase.rs | 12 ++++++++---- .../rs-sdk/src/platform/tokens/builders/set_price.rs | 12 ++++++++---- .../rs-sdk/src/platform/tokens/builders/transfer.rs | 12 ++++++++---- .../rs-sdk/src/platform/tokens/builders/unfreeze.rs | 12 ++++++++---- 17 files changed, 136 insertions(+), 68 deletions(-) diff --git a/packages/rs-sdk/src/platform/documents/transitions/create.rs b/packages/rs-sdk/src/platform/documents/transitions/create.rs index e9ef65fb684..22123814aa6 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/create.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/create.rs @@ -173,13 +173,17 @@ impl DocumentCreateTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/documents/transitions/delete.rs b/packages/rs-sdk/src/platform/documents/transitions/delete.rs index dc254ce76a5..9a1862b98af 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/delete.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/delete.rs @@ -213,13 +213,17 @@ impl DocumentDeleteTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/documents/transitions/purchase.rs b/packages/rs-sdk/src/platform/documents/transitions/purchase.rs index 8fd93529d14..6b33bc2bf3b 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/purchase.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/purchase.rs @@ -227,13 +227,17 @@ impl DocumentPurchaseTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/documents/transitions/replace.rs b/packages/rs-sdk/src/platform/documents/transitions/replace.rs index e85fb551c6a..27dede0bb3f 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/replace.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/replace.rs @@ -166,13 +166,17 @@ impl DocumentReplaceTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/documents/transitions/set_price.rs b/packages/rs-sdk/src/platform/documents/transitions/set_price.rs index 435e9ebee0b..537c09283db 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/set_price.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/set_price.rs @@ -214,13 +214,17 @@ impl DocumentSetPriceTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/documents/transitions/transfer.rs b/packages/rs-sdk/src/platform/documents/transitions/transfer.rs index dfc60854b36..53eef22c776 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/transfer.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/transfer.rs @@ -213,13 +213,17 @@ impl DocumentTransferTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/burn.rs b/packages/rs-sdk/src/platform/tokens/builders/burn.rs index d88dc135112..2247419ced5 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/burn.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/burn.rs @@ -185,13 +185,17 @@ impl TokenBurnTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/claim.rs b/packages/rs-sdk/src/platform/tokens/builders/claim.rs index 378caa2baf1..d68923841f4 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/claim.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/claim.rs @@ -171,13 +171,17 @@ impl TokenClaimTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/config_update.rs b/packages/rs-sdk/src/platform/tokens/builders/config_update.rs index 63d20258bbe..f705e847292 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/config_update.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/config_update.rs @@ -193,13 +193,17 @@ impl TokenConfigUpdateTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/destroy.rs b/packages/rs-sdk/src/platform/tokens/builders/destroy.rs index 0bcd8416e7a..72d108e6526 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/destroy.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/destroy.rs @@ -191,13 +191,17 @@ impl TokenDestroyFrozenFundsTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs b/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs index a40a8be583a..760bd5644a9 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs @@ -219,13 +219,17 @@ impl TokenEmergencyActionTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/freeze.rs b/packages/rs-sdk/src/platform/tokens/builders/freeze.rs index 03646a6248e..31736d9d7bd 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/freeze.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/freeze.rs @@ -191,13 +191,17 @@ impl TokenFreezeTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/mint.rs b/packages/rs-sdk/src/platform/tokens/builders/mint.rs index b72387d9d76..294136dff12 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/mint.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/mint.rs @@ -212,13 +212,17 @@ impl TokenMintTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/purchase.rs b/packages/rs-sdk/src/platform/tokens/builders/purchase.rs index 97b802eaee9..3634c7ba84c 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/purchase.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/purchase.rs @@ -159,13 +159,17 @@ impl TokenDirectPurchaseTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/set_price.rs b/packages/rs-sdk/src/platform/tokens/builders/set_price.rs index 05565e99d40..ffb4f9b38ff 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/set_price.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/set_price.rs @@ -236,13 +236,17 @@ impl TokenChangeDirectPurchasePriceTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/transfer.rs b/packages/rs-sdk/src/platform/tokens/builders/transfer.rs index de13cfc65c8..dd309f59f30 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/transfer.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/transfer.rs @@ -217,13 +217,17 @@ impl TokenTransferTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) diff --git a/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs b/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs index 00ab0af19d1..ed6d32a2980 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs @@ -191,13 +191,17 @@ impl TokenUnfreezeTransitionBuilder { batch_transition.validate_base_structure(platform_version)? } _ => { - return Err(Error::Protocol(dpp::ProtocolError::InvalidStateTransitionType( - "expected Batch transition".to_string(), - ))); + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )); } }; if let Some(first_error) = validation_result.errors.into_iter().next() { - return Err(Error::Protocol(dpp::ProtocolError::ConsensusError(Box::new(first_error)))); + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); } Ok(state_transition) From 152a8ce513485b7c97f927541ad14bd60bea0ca2 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Fri, 20 Feb 2026 19:28:04 -0600 Subject: [PATCH 4/7] fix(wasm-sdk): add maxLength to indexed rarity field in test fixture The validation feature added to rs-sdk's dpp dependency now enforces that indexed string properties must have maxLength <= 63. The rarity field in the crypto card game test fixture was missing this constraint, causing all DataContract tests to fail. --- .../tests/unit/fixtures/data-contract-v0-crypto-card-game.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wasm-sdk/tests/unit/fixtures/data-contract-v0-crypto-card-game.ts b/packages/wasm-sdk/tests/unit/fixtures/data-contract-v0-crypto-card-game.ts index 9ff248509f8..d24ddb602c1 100644 --- a/packages/wasm-sdk/tests/unit/fixtures/data-contract-v0-crypto-card-game.ts +++ b/packages/wasm-sdk/tests/unit/fixtures/data-contract-v0-crypto-card-game.ts @@ -52,6 +52,7 @@ const contract = { rarity: { type: 'string', description: 'Rarity level of the card', + maxLength: 9, enum: [ 'common', 'uncommon', From 08a521e2b5eefeee120a574663b82a391620104f Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Sat, 21 Feb 2026 13:56:26 -0600 Subject: [PATCH 5/7] test(sdk): add validate_base_structure error path tests for document and token builders Tests cover: - InvalidTokenAmountError via TokenMintTransitionBuilder with amount=0 - InvalidActionIdError via TokenMintTransitionBuilder with mismatched group action ID - Document nonce masking (validates nonce out-of-bounds is unreachable via builder API) Documents that several error paths (empty transitions, max exceeded, duplicate transitions, invalid token ID) are unreachable through the single-transition builder API by design. --- .../src/platform/documents/transitions/mod.rs | 2 + .../platform/documents/transitions/tests.rs | 374 ++++++++++++++++++ .../src/platform/tokens/builders/mod.rs | 2 + .../src/platform/tokens/builders/tests.rs | 190 +++++++++ 4 files changed, 568 insertions(+) create mode 100644 packages/rs-sdk/src/platform/documents/transitions/tests.rs create mode 100644 packages/rs-sdk/src/platform/tokens/builders/tests.rs diff --git a/packages/rs-sdk/src/platform/documents/transitions/mod.rs b/packages/rs-sdk/src/platform/documents/transitions/mod.rs index 200a2164267..a93fda02beb 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/mod.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/mod.rs @@ -3,6 +3,8 @@ pub mod delete; pub mod purchase; pub mod replace; pub mod set_price; +#[cfg(test)] +mod tests; pub mod transfer; pub use create::{DocumentCreateResult, DocumentCreateTransitionBuilder}; diff --git a/packages/rs-sdk/src/platform/documents/transitions/tests.rs b/packages/rs-sdk/src/platform/documents/transitions/tests.rs new file mode 100644 index 00000000000..87057371e21 --- /dev/null +++ b/packages/rs-sdk/src/platform/documents/transitions/tests.rs @@ -0,0 +1,374 @@ +use super::delete::DocumentDeleteTransitionBuilder; +use crate::{Error, Sdk, SdkBuilder}; +use dpp::address_funds::AddressWitness; +use dpp::data_contract::accessors::v0::DataContractV0Getters; +use dpp::data_contract::config::DataContractConfig; +use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; +use dpp::data_contract::document_type::DocumentType; +use dpp::data_contract::DataContractFactory; +use dpp::document::{Document, DocumentV0}; +use dpp::identity::identity_public_key::v0::IdentityPublicKeyV0; +use dpp::identity::signer::Signer; +use dpp::identity::{IdentityPublicKey, KeyType, Purpose, SecurityLevel}; +use dpp::platform_value::{platform_value, BinaryData, Value}; +use dpp::prelude::Identifier; +use dpp::state_transition::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0; +use dpp::state_transition::batch_transition::BatchTransition; +use dpp::state_transition::StateTransition; +use dpp::ProtocolError; +use drive_proof_verifier::types::IdentityContractNonceFetcher; +use std::collections::BTreeMap; +use std::sync::Arc; + +#[derive(Debug)] +struct TestSigner; + +impl Signer for TestSigner { + fn sign(&self, _key: &IdentityPublicKey, _data: &[u8]) -> Result { + Ok(BinaryData::from(vec![1; 65])) + } + + fn sign_create_witness( + &self, + _key: &IdentityPublicKey, + _data: &[u8], + ) -> Result { + Err(ProtocolError::CorruptedCodeExecution( + "sign_create_witness is not used in these tests".to_string(), + )) + } + + fn can_sign_with(&self, _key: &IdentityPublicKey) -> bool { + true + } +} + +fn test_identity_public_key() -> IdentityPublicKey { + IdentityPublicKey::V0(IdentityPublicKeyV0 { + id: 1, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::CRITICAL, + contract_bounds: None, + key_type: KeyType::ECDSA_SECP256K1, + read_only: false, + data: BinaryData::from(vec![2; 33]), + disabled_at: None, + }) +} + +fn test_data_contract(document_type_name: &str) -> Arc { + let platform_version = dpp::version::PlatformVersion::latest(); + let config = + DataContractConfig::default_for_version(platform_version).expect("create contract config"); + + let schema = platform_value!({ + "type": "object", + "properties": { + "a": { + "type": "string", + "maxLength": 10, + "position": 0 + } + }, + "additionalProperties": false, + }); + + let document_type = DocumentType::try_from_schema( + Identifier::random(), + 1, + config.version(), + document_type_name, + schema, + None, + &BTreeMap::new(), + &config, + true, + &mut vec![], + platform_version, + ) + .expect("create test document type"); + + let mut document_types: BTreeMap = BTreeMap::new(); + document_types.insert( + document_type.name().to_string(), + document_type.schema().clone(), + ); + + let contract = DataContractFactory::new(platform_version.protocol_version) + .expect("create data contract factory") + .create( + Identifier::random(), + 0, + platform_value!(document_types), + None, + None, + ) + .expect("create test data contract") + .data_contract_owned(); + + Arc::new(contract) +} + +const TEST_DOCUMENT_TYPE_NAME: &str = "testDoc"; +const INVALID_NONCE: u64 = 1_u64 << 50; + +fn test_document(owner_id: Identifier) -> Document { + Document::V0(DocumentV0 { + id: Identifier::random(), + owner_id, + properties: Default::default(), + revision: Some(1), + created_at: None, + updated_at: None, + transferred_at: None, + created_at_block_height: None, + updated_at_block_height: None, + transferred_at_block_height: None, + created_at_core_block_height: None, + updated_at_core_block_height: None, + transferred_at_core_block_height: None, + creator_id: None, + }) +} + +fn validate_transition_like_builder(state_transition: &StateTransition) -> Result<(), Error> { + let platform_version = dpp::version::PlatformVersion::latest(); + let validation_result = match state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )) + } + }; + if let Some(first_error) = validation_result.errors.into_iter().next() { + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); + } + Ok(()) +} + +pub(super) fn assert_document_create_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); + let owner_id = Identifier::random(); + let document = test_document(owner_id); + let document_type = data_contract + .document_type_for_name(TEST_DOCUMENT_TYPE_NAME) + .expect("expected test document type"); + let transition = BatchTransition::new_document_creation_transition_from_document( + document, + document_type, + [7; 32], + &test_identity_public_key(), + INVALID_NONCE, + 0, + None, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_document_delete_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); + let owner_id = Identifier::random(); + let document = test_document(owner_id); + let document_type = data_contract + .document_type_for_name(TEST_DOCUMENT_TYPE_NAME) + .expect("expected test document type"); + let transition = BatchTransition::new_document_deletion_transition_from_document( + document, + document_type, + &test_identity_public_key(), + INVALID_NONCE, + 0, + None, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_document_purchase_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); + let owner_id = Identifier::random(); + let document = test_document(owner_id); + let purchaser_id = Identifier::random(); + let document_type = data_contract + .document_type_for_name(TEST_DOCUMENT_TYPE_NAME) + .expect("expected test document type"); + let transition = BatchTransition::new_document_purchase_transition_from_document( + document, + document_type, + purchaser_id, + 100, + &test_identity_public_key(), + INVALID_NONCE, + 0, + None, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_document_replace_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); + let owner_id = Identifier::random(); + let document = test_document(owner_id); + let document_type = data_contract + .document_type_for_name(TEST_DOCUMENT_TYPE_NAME) + .expect("expected test document type"); + let transition = BatchTransition::new_document_replacement_transition_from_document( + document, + document_type, + &test_identity_public_key(), + INVALID_NONCE, + 0, + None, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_document_set_price_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); + let owner_id = Identifier::random(); + let document = test_document(owner_id); + let document_type = data_contract + .document_type_for_name(TEST_DOCUMENT_TYPE_NAME) + .expect("expected test document type"); + let transition = BatchTransition::new_document_update_price_transition_from_document( + document, + document_type, + 200, + &test_identity_public_key(), + INVALID_NONCE, + 0, + None, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_document_transfer_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); + let owner_id = Identifier::random(); + let document = test_document(owner_id); + let recipient_id = Identifier::random(); + let document_type = data_contract + .document_type_for_name(TEST_DOCUMENT_TYPE_NAME) + .expect("expected test document type"); + let transition = BatchTransition::new_document_transfer_transition_from_document( + document, + document_type, + recipient_id, + &test_identity_public_key(), + INVALID_NONCE, + 0, + None, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +async fn new_mock_sdk_with_contract_nonce( + identity_id: Identifier, + contract_id: Identifier, + fetched_nonce: u64, +) -> Sdk { + let mut sdk = SdkBuilder::new_mock().build().expect("build mock sdk"); + + sdk.mock() + .expect_fetch::( + (identity_id, contract_id), + Some(IdentityContractNonceFetcher(fetched_nonce)), + ) + .await + .expect("set nonce fetch expectation"); + + sdk +} + +#[tokio::test] +async fn document_delete_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { + // Document builders always create exactly one transition and obtain nonce through + // `Sdk::get_identity_contract_nonce`, which masks out-of-bounds bits. + // This makes `validate_base_structure` nonce-out-of-bounds errors unreachable here. + let document_type_name = "testDoc"; + let data_contract = test_data_contract(document_type_name); + let owner_id = Identifier::random(); + + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; + + let builder = DocumentDeleteTransitionBuilder::new( + Arc::clone(&data_contract), + document_type_name.to_string(), + Identifier::random(), + owner_id, + ); + + let result = builder + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + result.is_ok(), + "unexpected error while signing document delete transition: {:?}", + result.err() + ); + + assert!( + !matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) + if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( + dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) + )) + ), + "nonce out-of-bounds should be unreachable via document builders" + ); +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/mod.rs b/packages/rs-sdk/src/platform/tokens/builders/mod.rs index eec8c140062..bc675dcdfc9 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/mod.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/mod.rs @@ -9,5 +9,7 @@ pub mod freeze; pub mod mint; pub mod purchase; pub mod set_price; +#[cfg(test)] +mod tests; pub mod transfer; pub mod unfreeze; diff --git a/packages/rs-sdk/src/platform/tokens/builders/tests.rs b/packages/rs-sdk/src/platform/tokens/builders/tests.rs new file mode 100644 index 00000000000..4eccf101d8b --- /dev/null +++ b/packages/rs-sdk/src/platform/tokens/builders/tests.rs @@ -0,0 +1,190 @@ +use super::mint::TokenMintTransitionBuilder; +use crate::{Error, Sdk, SdkBuilder}; +use dpp::address_funds::AddressWitness; +use dpp::consensus::basic::BasicError; +use dpp::consensus::ConsensusError; +use dpp::data_contract::accessors::v0::DataContractV0Getters; +use dpp::data_contract::config::DataContractConfig; +use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; +use dpp::data_contract::document_type::DocumentType; +use dpp::data_contract::DataContractFactory; +use dpp::group::{GroupStateTransitionInfo, GroupStateTransitionInfoStatus}; +use dpp::identity::identity_public_key::v0::IdentityPublicKeyV0; +use dpp::identity::signer::Signer; +use dpp::identity::{IdentityPublicKey, KeyType, Purpose, SecurityLevel}; +use dpp::platform_value::{platform_value, BinaryData, Value}; +use dpp::prelude::Identifier; +use dpp::ProtocolError; +use drive_proof_verifier::types::IdentityContractNonceFetcher; +use std::collections::BTreeMap; +use std::sync::Arc; + +#[derive(Debug)] +struct TestSigner; + +impl Signer for TestSigner { + fn sign(&self, _key: &IdentityPublicKey, _data: &[u8]) -> Result { + Ok(BinaryData::from(vec![1; 65])) + } + + fn sign_create_witness( + &self, + _key: &IdentityPublicKey, + _data: &[u8], + ) -> Result { + Err(ProtocolError::CorruptedCodeExecution( + "sign_create_witness is not used in these tests".to_string(), + )) + } + + fn can_sign_with(&self, _key: &IdentityPublicKey) -> bool { + true + } +} + +fn test_identity_public_key() -> IdentityPublicKey { + IdentityPublicKey::V0(IdentityPublicKeyV0 { + id: 1, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::CRITICAL, + contract_bounds: None, + key_type: KeyType::ECDSA_SECP256K1, + read_only: false, + data: BinaryData::from(vec![2; 33]), + disabled_at: None, + }) +} + +fn test_data_contract(document_type_name: &str) -> Arc { + let platform_version = dpp::version::PlatformVersion::latest(); + let config = + DataContractConfig::default_for_version(platform_version).expect("create contract config"); + + let schema = platform_value!({ + "type": "object", + "properties": { + "a": { + "type": "string", + "maxLength": 10, + "position": 0 + } + }, + "additionalProperties": false, + }); + + let document_type = DocumentType::try_from_schema( + Identifier::random(), + 1, + config.version(), + document_type_name, + schema, + None, + &BTreeMap::new(), + &config, + true, + &mut vec![], + platform_version, + ) + .expect("create test document type"); + + let mut document_types: BTreeMap = BTreeMap::new(); + document_types.insert( + document_type.name().to_string(), + document_type.schema().clone(), + ); + + let contract = DataContractFactory::new(platform_version.protocol_version) + .expect("create data contract factory") + .create( + Identifier::random(), + 0, + platform_value!(document_types), + None, + None, + ) + .expect("create test data contract") + .data_contract_owned(); + + Arc::new(contract) +} + +async fn new_mock_sdk_with_contract_nonce( + identity_id: Identifier, + contract_id: Identifier, + fetched_nonce: u64, +) -> Sdk { + let mut sdk = SdkBuilder::new_mock().build().expect("build mock sdk"); + + sdk.mock() + .expect_fetch::( + (identity_id, contract_id), + Some(IdentityContractNonceFetcher(fetched_nonce)), + ) + .await + .expect("set nonce fetch expectation"); + + sdk +} + +#[tokio::test] +async fn token_mint_sign_returns_invalid_token_amount_error_when_amount_is_zero() { + let issuer_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(issuer_id, data_contract.id(), 0).await; + + let result = TokenMintTransitionBuilder::new(Arc::clone(&data_contract), 0, issuer_id, 0) + .issued_to_identity_id(Identifier::random()) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenAmountError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_mint_sign_returns_invalid_action_id_error_for_mismatched_group_action_id() { + let issuer_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(issuer_id, data_contract.id(), 0).await; + + let invalid_group_info = GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id: Identifier::from_bytes(&[0; 32]).expect("create static action id"), + action_is_proposer: true, + }, + ); + + let result = TokenMintTransitionBuilder::new(Arc::clone(&data_contract), 0, issuer_id, 1) + .issued_to_identity_id(Identifier::random()) + .with_using_group_info(invalid_group_info) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidActionIdError(_))) + ), + "unexpected result: {:?}", + result + ); +} From 9bd6d45eb11999c2782ef0525fb86a35a5f6a762 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Sat, 21 Feb 2026 14:01:49 -0600 Subject: [PATCH 6/7] test(sdk): add validate_base_structure error path tests for all builders --- .../platform/documents/transitions/create.rs | 8 + .../platform/documents/transitions/delete.rs | 8 + .../documents/transitions/purchase.rs | 8 + .../platform/documents/transitions/replace.rs | 8 + .../documents/transitions/set_price.rs | 8 + .../platform/documents/transitions/tests.rs | 244 ++++++ .../documents/transitions/transfer.rs | 8 + .../src/platform/tokens/builders/burn.rs | 8 + .../src/platform/tokens/builders/claim.rs | 8 + .../platform/tokens/builders/config_update.rs | 8 + .../src/platform/tokens/builders/destroy.rs | 8 + .../tokens/builders/emergency_action.rs | 8 + .../src/platform/tokens/builders/freeze.rs | 8 + .../src/platform/tokens/builders/mint.rs | 8 + .../src/platform/tokens/builders/purchase.rs | 8 + .../src/platform/tokens/builders/set_price.rs | 8 + .../src/platform/tokens/builders/tests.rs | 754 +++++++++++++++++- .../src/platform/tokens/builders/transfer.rs | 8 + .../src/platform/tokens/builders/unfreeze.rs | 8 + 19 files changed, 1132 insertions(+), 2 deletions(-) diff --git a/packages/rs-sdk/src/platform/documents/transitions/create.rs b/packages/rs-sdk/src/platform/documents/transitions/create.rs index 22123814aa6..ed529bc9854 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/create.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/create.rs @@ -268,3 +268,11 @@ impl Sdk { } } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_document_create_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/documents/transitions/delete.rs b/packages/rs-sdk/src/platform/documents/transitions/delete.rs index 9a1862b98af..50f491bcbd1 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/delete.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/delete.rs @@ -304,3 +304,11 @@ impl Sdk { } } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_document_delete_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/documents/transitions/purchase.rs b/packages/rs-sdk/src/platform/documents/transitions/purchase.rs index 6b33bc2bf3b..d34f70e9975 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/purchase.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/purchase.rs @@ -321,3 +321,11 @@ impl Sdk { } } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_document_purchase_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/documents/transitions/replace.rs b/packages/rs-sdk/src/platform/documents/transitions/replace.rs index 27dede0bb3f..77b510088e1 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/replace.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/replace.rs @@ -267,3 +267,11 @@ impl Sdk { } } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_document_replace_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/documents/transitions/set_price.rs b/packages/rs-sdk/src/platform/documents/transitions/set_price.rs index 537c09283db..cda735f7c5d 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/set_price.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/set_price.rs @@ -305,3 +305,11 @@ impl Sdk { } } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_document_set_price_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/documents/transitions/tests.rs b/packages/rs-sdk/src/platform/documents/transitions/tests.rs index 87057371e21..1f8002498a2 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/tests.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/tests.rs @@ -1,4 +1,9 @@ +use super::create::DocumentCreateTransitionBuilder; use super::delete::DocumentDeleteTransitionBuilder; +use super::purchase::DocumentPurchaseTransitionBuilder; +use super::replace::DocumentReplaceTransitionBuilder; +use super::set_price::DocumentSetPriceTransitionBuilder; +use super::transfer::DocumentTransferTransitionBuilder; use crate::{Error, Sdk, SdkBuilder}; use dpp::address_funds::AddressWitness; use dpp::data_contract::accessors::v0::DataContractV0Getters; @@ -131,6 +136,35 @@ fn test_document(owner_id: Identifier) -> Document { }) } +fn test_document_for_create( + data_contract_id: &Identifier, + document_type_name: &str, + owner_id: Identifier, + entropy: [u8; 32], +) -> Document { + Document::V0(DocumentV0 { + id: Document::generate_document_id_v0( + data_contract_id, + &owner_id, + document_type_name, + &entropy, + ), + owner_id, + properties: Default::default(), + revision: None, + created_at: None, + updated_at: None, + transferred_at: None, + created_at_block_height: None, + updated_at_block_height: None, + transferred_at_block_height: None, + created_at_core_block_height: None, + updated_at_core_block_height: None, + transferred_at_core_block_height: None, + creator_id: None, + }) +} + fn validate_transition_like_builder(state_transition: &StateTransition) -> Result<(), Error> { let platform_version = dpp::version::PlatformVersion::latest(); let validation_result = match state_transition { @@ -372,3 +406,213 @@ async fn document_delete_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_v "nonce out-of-bounds should be unreachable via document builders" ); } + +#[tokio::test] +async fn document_create_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { + let document_type_name = "testDoc"; + let data_contract = test_data_contract(document_type_name); + let owner_id = Identifier::random(); + let entropy = [11; 32]; + let document = + test_document_for_create(&data_contract.id(), document_type_name, owner_id, entropy); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; + + let builder = DocumentCreateTransitionBuilder::new( + Arc::clone(&data_contract), + document_type_name.to_string(), + document, + entropy, + ); + + let result = builder + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + result.is_ok(), + "unexpected error while signing document create transition: {:?}", + result.err() + ); + + assert!( + !matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) + if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( + dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) + )) + ), + "nonce out-of-bounds should be unreachable via document builders" + ); +} + +#[tokio::test] +async fn document_replace_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { + let document_type_name = "testDoc"; + let data_contract = test_data_contract(document_type_name); + let owner_id = Identifier::random(); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; + + let builder = DocumentReplaceTransitionBuilder::new( + Arc::clone(&data_contract), + document_type_name.to_string(), + test_document(owner_id), + ); + + let result = builder + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + result.is_ok(), + "unexpected error while signing document replace transition: {:?}", + result.err() + ); + + assert!( + !matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) + if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( + dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) + )) + ), + "nonce out-of-bounds should be unreachable via document builders" + ); +} + +#[tokio::test] +async fn document_purchase_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { + let document_type_name = "testDoc"; + let data_contract = test_data_contract(document_type_name); + let owner_id = Identifier::random(); + let purchaser_id = Identifier::random(); + let sdk = new_mock_sdk_with_contract_nonce(purchaser_id, data_contract.id(), 1_u64 << 50).await; + + let builder = DocumentPurchaseTransitionBuilder::new( + Arc::clone(&data_contract), + document_type_name.to_string(), + test_document(owner_id), + purchaser_id, + 100, + ); + + let result = builder + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + result.is_ok(), + "unexpected error while signing document purchase transition: {:?}", + result.err() + ); + + assert!( + !matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) + if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( + dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) + )) + ), + "nonce out-of-bounds should be unreachable via document builders" + ); +} + +#[tokio::test] +async fn document_set_price_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { + let document_type_name = "testDoc"; + let data_contract = test_data_contract(document_type_name); + let owner_id = Identifier::random(); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; + + let builder = DocumentSetPriceTransitionBuilder::new( + Arc::clone(&data_contract), + document_type_name.to_string(), + test_document(owner_id), + 123, + ); + + let result = builder + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + result.is_ok(), + "unexpected error while signing document set_price transition: {:?}", + result.err() + ); + + assert!( + !matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) + if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( + dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) + )) + ), + "nonce out-of-bounds should be unreachable via document builders" + ); +} + +#[tokio::test] +async fn document_transfer_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { + let document_type_name = "testDoc"; + let data_contract = test_data_contract(document_type_name); + let owner_id = Identifier::random(); + let recipient_id = Identifier::random(); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; + + let builder = DocumentTransferTransitionBuilder::new( + Arc::clone(&data_contract), + document_type_name.to_string(), + test_document(owner_id), + recipient_id, + ); + + let result = builder + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + result.is_ok(), + "unexpected error while signing document transfer transition: {:?}", + result.err() + ); + + assert!( + !matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) + if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( + dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) + )) + ), + "nonce out-of-bounds should be unreachable via document builders" + ); +} diff --git a/packages/rs-sdk/src/platform/documents/transitions/transfer.rs b/packages/rs-sdk/src/platform/documents/transitions/transfer.rs index 53eef22c776..452d5590265 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/transfer.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/transfer.rs @@ -305,3 +305,11 @@ impl Sdk { } } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_document_transfer_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/burn.rs b/packages/rs-sdk/src/platform/tokens/builders/burn.rs index 2247419ced5..324f53b1844 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/burn.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/burn.rs @@ -201,3 +201,11 @@ impl TokenBurnTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_burn_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/claim.rs b/packages/rs-sdk/src/platform/tokens/builders/claim.rs index d68923841f4..22fc107cdbe 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/claim.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/claim.rs @@ -187,3 +187,11 @@ impl TokenClaimTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_claim_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/config_update.rs b/packages/rs-sdk/src/platform/tokens/builders/config_update.rs index f705e847292..e231858099d 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/config_update.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/config_update.rs @@ -209,3 +209,11 @@ impl TokenConfigUpdateTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_config_update_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/destroy.rs b/packages/rs-sdk/src/platform/tokens/builders/destroy.rs index 72d108e6526..b07baf37190 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/destroy.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/destroy.rs @@ -207,3 +207,11 @@ impl TokenDestroyFrozenFundsTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_destroy_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs b/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs index 760bd5644a9..da44967f312 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/emergency_action.rs @@ -235,3 +235,11 @@ impl TokenEmergencyActionTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_emergency_action_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/freeze.rs b/packages/rs-sdk/src/platform/tokens/builders/freeze.rs index 31736d9d7bd..a260b5f12eb 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/freeze.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/freeze.rs @@ -207,3 +207,11 @@ impl TokenFreezeTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_freeze_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/mint.rs b/packages/rs-sdk/src/platform/tokens/builders/mint.rs index 294136dff12..0f6dfa52635 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/mint.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/mint.rs @@ -228,3 +228,11 @@ impl TokenMintTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_mint_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/purchase.rs b/packages/rs-sdk/src/platform/tokens/builders/purchase.rs index 3634c7ba84c..58be6b3c45d 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/purchase.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/purchase.rs @@ -175,3 +175,11 @@ impl TokenDirectPurchaseTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_purchase_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/set_price.rs b/packages/rs-sdk/src/platform/tokens/builders/set_price.rs index ffb4f9b38ff..05fa7867c8b 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/set_price.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/set_price.rs @@ -252,3 +252,11 @@ impl TokenChangeDirectPurchasePriceTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_set_price_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/tests.rs b/packages/rs-sdk/src/platform/tokens/builders/tests.rs index 4eccf101d8b..51659b0ad14 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/tests.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/tests.rs @@ -1,9 +1,21 @@ +use super::burn::TokenBurnTransitionBuilder; +use super::claim::TokenClaimTransitionBuilder; +use super::config_update::TokenConfigUpdateTransitionBuilder; +use super::destroy::TokenDestroyFrozenFundsTransitionBuilder; +use super::emergency_action::TokenEmergencyActionTransitionBuilder; +use super::freeze::TokenFreezeTransitionBuilder; use super::mint::TokenMintTransitionBuilder; +use super::purchase::TokenDirectPurchaseTransitionBuilder; +use super::set_price::TokenChangeDirectPurchasePriceTransitionBuilder; +use super::transfer::TokenTransferTransitionBuilder; +use super::unfreeze::TokenUnfreezeTransitionBuilder; use crate::{Error, Sdk, SdkBuilder}; use dpp::address_funds::AddressWitness; use dpp::consensus::basic::BasicError; use dpp::consensus::ConsensusError; use dpp::data_contract::accessors::v0::DataContractV0Getters; +use dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +use dpp::data_contract::associated_token::token_distribution_key::TokenDistributionType; use dpp::data_contract::config::DataContractConfig; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::document_type::DocumentType; @@ -14,6 +26,12 @@ use dpp::identity::signer::Signer; use dpp::identity::{IdentityPublicKey, KeyType, Purpose, SecurityLevel}; use dpp::platform_value::{platform_value, BinaryData, Value}; use dpp::prelude::Identifier; +use dpp::state_transition::batch_transition::methods::v1::DocumentsBatchTransitionMethodsV1; +use dpp::state_transition::batch_transition::BatchTransition; +use dpp::state_transition::StateTransition; +use dpp::tokens::calculate_token_id; +use dpp::tokens::emergency_action::TokenEmergencyAction; +use dpp::tokens::token_pricing_schedule::TokenPricingSchedule; use dpp::ProtocolError; use drive_proof_verifier::types::IdentityContractNonceFetcher; use std::collections::BTreeMap; @@ -108,6 +126,311 @@ fn test_data_contract(document_type_name: &str) -> Arc Result<(), Error> { + let platform_version = dpp::version::PlatformVersion::latest(); + let validation_result = match state_transition { + StateTransition::Batch(batch_transition) => { + batch_transition.validate_base_structure(platform_version)? + } + _ => { + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType( + "expected Batch transition".to_string(), + ), + )) + } + }; + if let Some(first_error) = validation_result.errors.into_iter().next() { + return Err(Error::Protocol(dpp::ProtocolError::ConsensusError( + Box::new(first_error), + ))); + } + Ok(()) +} + +fn token_setup() -> ( + Arc, + Identifier, + Identifier, +) { + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); + let owner_id = Identifier::random(); + let token_id = Identifier::from(calculate_token_id( + data_contract.id().as_bytes(), + TEST_TOKEN_POSITION, + )); + (data_contract, owner_id, token_id) +} + +pub(super) fn assert_token_burn_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_burn_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + 1, + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_claim_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_claim_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + TokenDistributionType::PreProgrammed, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_config_update_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_config_update_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + TokenConfigurationChangeItem::TokenConfigurationNoChange, + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_destroy_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_destroy_frozen_funds_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + Identifier::random(), + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_emergency_action_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_emergency_action_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + TokenEmergencyAction::Pause, + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_freeze_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_freeze_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + Identifier::random(), + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_mint_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_mint_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + 1, + Some(Identifier::random()), + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_purchase_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_direct_purchase_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + 1, + 10, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_set_price_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_change_direct_purchase_price_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + Some(TokenPricingSchedule::SinglePrice(5)), + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_transfer_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_transfer_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + 1, + Identifier::random(), + None, + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + +pub(super) fn assert_token_unfreeze_validate_base_structure_error() { + let platform_version = dpp::version::PlatformVersion::latest(); + let (data_contract, owner_id, token_id) = token_setup(); + let transition = BatchTransition::new_token_unfreeze_transition( + token_id, + owner_id, + data_contract.id(), + TEST_TOKEN_POSITION, + Identifier::random(), + None, + None, + &test_identity_public_key(), + INVALID_NONCE, + 0, + &TestSigner, + platform_version, + None, + ) + .expect("transition should build"); + + let result = validate_transition_like_builder(&transition); + assert!(result.is_err(), "expected validation error, got {result:?}"); +} + async fn new_mock_sdk_with_contract_nonce( identity_id: Identifier, contract_id: Identifier, @@ -129,7 +452,7 @@ async fn new_mock_sdk_with_contract_nonce( #[tokio::test] async fn token_mint_sign_returns_invalid_token_amount_error_when_amount_is_zero() { let issuer_id = Identifier::random(); - let data_contract = test_data_contract("testDoc"); + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); let sdk = new_mock_sdk_with_contract_nonce(issuer_id, data_contract.id(), 0).await; let result = TokenMintTransitionBuilder::new(Arc::clone(&data_contract), 0, issuer_id, 0) @@ -156,7 +479,7 @@ async fn token_mint_sign_returns_invalid_token_amount_error_when_amount_is_zero( #[tokio::test] async fn token_mint_sign_returns_invalid_action_id_error_for_mismatched_group_action_id() { let issuer_id = Identifier::random(); - let data_contract = test_data_contract("testDoc"); + let data_contract = test_data_contract(TEST_DOCUMENT_TYPE_NAME); let sdk = new_mock_sdk_with_contract_nonce(issuer_id, data_contract.id(), 0).await; let invalid_group_info = GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( @@ -188,3 +511,430 @@ async fn token_mint_sign_returns_invalid_action_id_error_for_mismatched_group_ac result ); } + +#[tokio::test] +async fn token_burn_sign_returns_invalid_token_amount_error_when_amount_is_zero() { + let owner_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 0).await; + + let result = TokenBurnTransitionBuilder::new(Arc::clone(&data_contract), 0, owner_id, 0) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenAmountError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_burn_sign_returns_note_too_big_error() { + let owner_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 0).await; + + let result = TokenBurnTransitionBuilder::new(Arc::clone(&data_contract), 0, owner_id, 1) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_transfer_sign_returns_invalid_token_amount_error_when_amount_is_zero() { + let sender_id = Identifier::random(); + let recipient_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(sender_id, data_contract.id(), 0).await; + + let result = TokenTransferTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + sender_id, + recipient_id, + 0, + ) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenAmountError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_transfer_sign_returns_transfer_to_ourself_error() { + let sender_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(sender_id, data_contract.id(), 0).await; + + let result = + TokenTransferTransitionBuilder::new(Arc::clone(&data_contract), 0, sender_id, sender_id, 1) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::TokenTransferToOurselfError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_transfer_sign_returns_note_too_big_error_for_public_note() { + let sender_id = Identifier::random(); + let recipient_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(sender_id, data_contract.id(), 0).await; + + let result = TokenTransferTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + sender_id, + recipient_id, + 1, + ) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_freeze_sign_returns_note_too_big_error() { + let actor_id = Identifier::random(); + let freeze_identity_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(actor_id, data_contract.id(), 0).await; + + let result = TokenFreezeTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + actor_id, + freeze_identity_id, + ) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_unfreeze_sign_returns_note_too_big_error() { + let actor_id = Identifier::random(); + let unfreeze_identity_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(actor_id, data_contract.id(), 0).await; + + let result = TokenUnfreezeTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + actor_id, + unfreeze_identity_id, + ) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_destroy_sign_returns_note_too_big_error() { + let actor_id = Identifier::random(); + let frozen_identity_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(actor_id, data_contract.id(), 0).await; + + let result = TokenDestroyFrozenFundsTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + actor_id, + frozen_identity_id, + ) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_emergency_action_sign_returns_note_too_big_error() { + let actor_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(actor_id, data_contract.id(), 0).await; + + let result = + TokenEmergencyActionTransitionBuilder::pause(Arc::clone(&data_contract), 0, actor_id) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_config_update_sign_returns_no_change_error() { + let owner_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 0).await; + + let result = TokenConfigUpdateTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + owner_id, + TokenConfigurationChangeItem::TokenConfigurationNoChange, + ) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenConfigUpdateNoChangeError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_config_update_sign_returns_note_too_big_error() { + let owner_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 0).await; + + let result = TokenConfigUpdateTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + owner_id, + TokenConfigurationChangeItem::MintingAllowChoosingDestination(true), + ) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_claim_sign_returns_note_too_big_error() { + let owner_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 0).await; + + let result = TokenClaimTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + owner_id, + TokenDistributionType::PreProgrammed, + ) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_purchase_sign_returns_invalid_token_amount_error_when_amount_is_zero() { + let actor_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(actor_id, data_contract.id(), 0).await; + + let result = + TokenDirectPurchaseTransitionBuilder::new(Arc::clone(&data_contract), 0, actor_id, 0, 1000) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenAmountError(_))) + ), + "unexpected result: {:?}", + result + ); +} + +#[tokio::test] +async fn token_set_price_sign_returns_note_too_big_error() { + let issuer_id = Identifier::random(); + let data_contract = test_data_contract("testDoc"); + let sdk = new_mock_sdk_with_contract_nonce(issuer_id, data_contract.id(), 0).await; + + let result = TokenChangeDirectPurchasePriceTransitionBuilder::new( + Arc::clone(&data_contract), + 0, + issuer_id, + ) + .with_public_note("x".repeat(2049)) + .sign( + &sdk, + &test_identity_public_key(), + &TestSigner, + dpp::version::PlatformVersion::latest(), + ) + .await; + + assert!( + matches!( + result, + Err(Error::Protocol(ProtocolError::ConsensusError(ref consensus_error))) + if matches!(**consensus_error, ConsensusError::BasicError(BasicError::InvalidTokenNoteTooBigError(_))) + ), + "unexpected result: {:?}", + result + ); +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/transfer.rs b/packages/rs-sdk/src/platform/tokens/builders/transfer.rs index dd309f59f30..572a4de17fb 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/transfer.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/transfer.rs @@ -233,3 +233,11 @@ impl TokenTransferTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_transfer_validate_base_structure_error(); + } +} diff --git a/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs b/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs index ed6d32a2980..c59fac979cb 100644 --- a/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs +++ b/packages/rs-sdk/src/platform/tokens/builders/unfreeze.rs @@ -207,3 +207,11 @@ impl TokenUnfreezeTransitionBuilder { Ok(state_transition) } } + +#[cfg(test)] +mod validation_tests { + #[test] + fn validate_base_structure_error_case() { + super::super::tests::assert_token_unfreeze_validate_base_structure_error(); + } +} From 085905ca6e29af5ba6ec3163bfed394cc130cdcc Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Sat, 21 Feb 2026 14:14:28 -0600 Subject: [PATCH 7/7] refactor(sdk): remove 5 redundant document nonce-masking tests All 6 document_*_sign_masks_nonce tests tested the same underlying behavior (Sdk::get_identity_contract_nonce masks out-of-bounds bits). Keep one representative test, remove the other 5. Also removes dead second assert that was always true (result was Ok) and unused test_document_for_create helper. --- .../platform/documents/transitions/tests.rs | 266 +----------------- 1 file changed, 6 insertions(+), 260 deletions(-) diff --git a/packages/rs-sdk/src/platform/documents/transitions/tests.rs b/packages/rs-sdk/src/platform/documents/transitions/tests.rs index 1f8002498a2..dd88faaa80a 100644 --- a/packages/rs-sdk/src/platform/documents/transitions/tests.rs +++ b/packages/rs-sdk/src/platform/documents/transitions/tests.rs @@ -1,9 +1,4 @@ -use super::create::DocumentCreateTransitionBuilder; use super::delete::DocumentDeleteTransitionBuilder; -use super::purchase::DocumentPurchaseTransitionBuilder; -use super::replace::DocumentReplaceTransitionBuilder; -use super::set_price::DocumentSetPriceTransitionBuilder; -use super::transfer::DocumentTransferTransitionBuilder; use crate::{Error, Sdk, SdkBuilder}; use dpp::address_funds::AddressWitness; use dpp::data_contract::accessors::v0::DataContractV0Getters; @@ -136,35 +131,6 @@ fn test_document(owner_id: Identifier) -> Document { }) } -fn test_document_for_create( - data_contract_id: &Identifier, - document_type_name: &str, - owner_id: Identifier, - entropy: [u8; 32], -) -> Document { - Document::V0(DocumentV0 { - id: Document::generate_document_id_v0( - data_contract_id, - &owner_id, - document_type_name, - &entropy, - ), - owner_id, - properties: Default::default(), - revision: None, - created_at: None, - updated_at: None, - transferred_at: None, - created_at_block_height: None, - updated_at_block_height: None, - transferred_at_block_height: None, - created_at_core_block_height: None, - updated_at_core_block_height: None, - transferred_at_core_block_height: None, - creator_id: None, - }) -} - fn validate_transition_like_builder(state_transition: &StateTransition) -> Result<(), Error> { let platform_version = dpp::version::PlatformVersion::latest(); let validation_result = match state_transition { @@ -363,10 +329,11 @@ async fn new_mock_sdk_with_contract_nonce( } #[tokio::test] -async fn document_delete_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { - // Document builders always create exactly one transition and obtain nonce through - // `Sdk::get_identity_contract_nonce`, which masks out-of-bounds bits. - // This makes `validate_base_structure` nonce-out-of-bounds errors unreachable here. +async fn document_builder_sign_masks_nonce_so_out_of_bounds_is_unreachable() { + // Document builders obtain nonce through `Sdk::get_identity_contract_nonce`, + // which masks out-of-bounds bits. This makes `validate_base_structure` + // nonce-out-of-bounds errors unreachable through the builder API. + // One test suffices since all document builders use the same SDK nonce path. let document_type_name = "testDoc"; let data_contract = test_data_contract(document_type_name); let owner_id = Identifier::random(); @@ -391,228 +358,7 @@ async fn document_delete_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_v assert!( result.is_ok(), - "unexpected error while signing document delete transition: {:?}", - result.err() - ); - - assert!( - !matches!( - result, - Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) - if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( - dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) - )) - ), - "nonce out-of-bounds should be unreachable via document builders" - ); -} - -#[tokio::test] -async fn document_create_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { - let document_type_name = "testDoc"; - let data_contract = test_data_contract(document_type_name); - let owner_id = Identifier::random(); - let entropy = [11; 32]; - let document = - test_document_for_create(&data_contract.id(), document_type_name, owner_id, entropy); - let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; - - let builder = DocumentCreateTransitionBuilder::new( - Arc::clone(&data_contract), - document_type_name.to_string(), - document, - entropy, - ); - - let result = builder - .sign( - &sdk, - &test_identity_public_key(), - &TestSigner, - dpp::version::PlatformVersion::latest(), - ) - .await; - - assert!( - result.is_ok(), - "unexpected error while signing document create transition: {:?}", - result.err() - ); - - assert!( - !matches!( - result, - Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) - if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( - dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) - )) - ), - "nonce out-of-bounds should be unreachable via document builders" - ); -} - -#[tokio::test] -async fn document_replace_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { - let document_type_name = "testDoc"; - let data_contract = test_data_contract(document_type_name); - let owner_id = Identifier::random(); - let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; - - let builder = DocumentReplaceTransitionBuilder::new( - Arc::clone(&data_contract), - document_type_name.to_string(), - test_document(owner_id), - ); - - let result = builder - .sign( - &sdk, - &test_identity_public_key(), - &TestSigner, - dpp::version::PlatformVersion::latest(), - ) - .await; - - assert!( - result.is_ok(), - "unexpected error while signing document replace transition: {:?}", - result.err() - ); - - assert!( - !matches!( - result, - Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) - if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( - dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) - )) - ), - "nonce out-of-bounds should be unreachable via document builders" - ); -} - -#[tokio::test] -async fn document_purchase_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { - let document_type_name = "testDoc"; - let data_contract = test_data_contract(document_type_name); - let owner_id = Identifier::random(); - let purchaser_id = Identifier::random(); - let sdk = new_mock_sdk_with_contract_nonce(purchaser_id, data_contract.id(), 1_u64 << 50).await; - - let builder = DocumentPurchaseTransitionBuilder::new( - Arc::clone(&data_contract), - document_type_name.to_string(), - test_document(owner_id), - purchaser_id, - 100, - ); - - let result = builder - .sign( - &sdk, - &test_identity_public_key(), - &TestSigner, - dpp::version::PlatformVersion::latest(), - ) - .await; - - assert!( - result.is_ok(), - "unexpected error while signing document purchase transition: {:?}", - result.err() - ); - - assert!( - !matches!( - result, - Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) - if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( - dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) - )) - ), - "nonce out-of-bounds should be unreachable via document builders" - ); -} - -#[tokio::test] -async fn document_set_price_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { - let document_type_name = "testDoc"; - let data_contract = test_data_contract(document_type_name); - let owner_id = Identifier::random(); - let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; - - let builder = DocumentSetPriceTransitionBuilder::new( - Arc::clone(&data_contract), - document_type_name.to_string(), - test_document(owner_id), - 123, - ); - - let result = builder - .sign( - &sdk, - &test_identity_public_key(), - &TestSigner, - dpp::version::PlatformVersion::latest(), - ) - .await; - - assert!( - result.is_ok(), - "unexpected error while signing document set_price transition: {:?}", + "SDK should mask nonce internally; got error: {:?}", result.err() ); - - assert!( - !matches!( - result, - Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) - if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( - dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) - )) - ), - "nonce out-of-bounds should be unreachable via document builders" - ); -} - -#[tokio::test] -async fn document_transfer_sign_masks_nonce_and_does_not_hit_nonce_out_of_bounds_validation() { - let document_type_name = "testDoc"; - let data_contract = test_data_contract(document_type_name); - let owner_id = Identifier::random(); - let recipient_id = Identifier::random(); - let sdk = new_mock_sdk_with_contract_nonce(owner_id, data_contract.id(), 1_u64 << 50).await; - - let builder = DocumentTransferTransitionBuilder::new( - Arc::clone(&data_contract), - document_type_name.to_string(), - test_document(owner_id), - recipient_id, - ); - - let result = builder - .sign( - &sdk, - &test_identity_public_key(), - &TestSigner, - dpp::version::PlatformVersion::latest(), - ) - .await; - - assert!( - result.is_ok(), - "unexpected error while signing document transfer transition: {:?}", - result.err() - ); - - assert!( - !matches!( - result, - Err(Error::Protocol(ProtocolError::ConsensusError(consensus_error))) - if matches!(*consensus_error, dpp::consensus::ConsensusError::BasicError( - dpp::consensus::basic::BasicError::NonceOutOfBoundsError(_) - )) - ), - "nonce out-of-bounds should be unreachable via document builders" - ); }