dpp/shielded/builder/
shield_from_asset_lock.rs1use crate::address_funds::OrchardAddress;
2use crate::prelude::AssetLockProof;
3use crate::state_transition::shield_from_asset_lock_transition::methods::ShieldFromAssetLockTransitionMethodsV0;
4use crate::state_transition::shield_from_asset_lock_transition::ShieldFromAssetLockTransition;
5use crate::state_transition::StateTransition;
6use crate::ProtocolError;
7use platform_version::version::PlatformVersion;
8
9use super::{build_output_only_bundle, serialize_authorized_bundle, OrchardProver};
10
11#[allow(clippy::too_many_arguments)]
25pub fn build_shield_from_asset_lock_transition<P: OrchardProver>(
26 recipient: &OrchardAddress,
27 shield_amount: u64,
28 asset_lock_proof: AssetLockProof,
29 asset_lock_private_key: &[u8],
30 prover: &P,
31 memo: [u8; 36],
32 platform_version: &PlatformVersion,
33) -> Result<StateTransition, ProtocolError> {
34 let bundle = build_output_only_bundle(recipient, shield_amount, memo, prover)?;
35 let sb = serialize_authorized_bundle(&bundle);
36
37 let value_balance = sb
40 .value_balance
41 .checked_neg()
42 .and_then(|v| u64::try_from(v).ok())
43 .ok_or_else(|| {
44 ProtocolError::ShieldedBuildError(
45 "shield_from_asset_lock: bundle value_balance is not negative".to_string(),
46 )
47 })?;
48
49 ShieldFromAssetLockTransition::try_from_asset_lock_with_bundle(
50 asset_lock_proof,
51 asset_lock_private_key,
52 sb.actions,
53 value_balance,
54 sb.anchor,
55 sb.proof,
56 sb.binding_signature,
57 platform_version,
58 )
59}
60
61#[cfg(test)]
62mod tests {
63 use super::super::{build_output_only_bundle, serialize_authorized_bundle};
64 use crate::shielded::builder::test_helpers::{test_orchard_address, TestProver};
65
66 #[test]
70 fn test_output_only_bundle_value_balance_is_negative() {
71 let recipient = test_orchard_address();
72 let amount = 50_000u64;
73
74 let bundle = build_output_only_bundle(&recipient, amount, [0u8; 36], &TestProver)
75 .expect("bundle should build successfully");
76 let sb = serialize_authorized_bundle(&bundle);
77
78 assert!(
80 sb.value_balance < 0,
81 "expected negative value_balance, got {}",
82 sb.value_balance
83 );
84
85 let abs_balance = sb
87 .value_balance
88 .checked_neg()
89 .and_then(|v| u64::try_from(v).ok())
90 .expect("value_balance should be safely negatable");
91 assert_eq!(abs_balance, amount);
92 }
93
94 #[test]
100 fn test_value_balance_positive_would_fail_conversion() {
101 let positive: i64 = 123;
108 let converted = positive.checked_neg().and_then(|v| u64::try_from(v).ok());
109 assert!(converted.is_none(), "negative result cannot be u64");
110
111 let zero: i64 = 0;
112 let converted_zero = zero.checked_neg().and_then(|v| u64::try_from(v).ok());
113 assert_eq!(converted_zero, Some(0));
114
115 let negative: i64 = -42;
116 let converted_neg = negative.checked_neg().and_then(|v| u64::try_from(v).ok());
117 assert_eq!(converted_neg, Some(42));
118 }
119
120 #[test]
121 fn test_output_only_various_amounts_negative_balance() {
122 for amount in [1u64, 100, 1_000_000, u32::MAX as u64] {
125 let recipient = test_orchard_address();
126 let bundle = build_output_only_bundle(&recipient, amount, [0u8; 36], &TestProver)
127 .expect("bundle should build");
128 let sb = serialize_authorized_bundle(&bundle);
129 assert_eq!(
130 sb.value_balance,
131 -(amount as i64),
132 "value_balance mismatch for amount {}",
133 amount
134 );
135 }
136 }
137}