dpp/shielded/builder/
shield.rs1use std::collections::BTreeMap;
2
3use crate::address_funds::AddressFundsFeeStrategy;
4use crate::address_funds::{OrchardAddress, PlatformAddress};
5use crate::fee::Credits;
6use crate::identity::signer::Signer;
7use crate::prelude::{AddressNonce, UserFeeIncrease};
8use crate::state_transition::shield_transition::methods::ShieldTransitionMethodsV0;
9use crate::state_transition::shield_transition::ShieldTransition;
10use crate::state_transition::StateTransition;
11use crate::ProtocolError;
12use platform_version::version::PlatformVersion;
13
14use super::{build_output_only_bundle, serialize_authorized_bundle, OrchardProver};
15
16#[allow(clippy::too_many_arguments)]
32pub fn build_shield_transition<S: Signer<PlatformAddress>, P: OrchardProver>(
33 recipient: &OrchardAddress,
34 shield_amount: u64,
35 inputs: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
36 fee_strategy: AddressFundsFeeStrategy,
37 signer: &S,
38 user_fee_increase: UserFeeIncrease,
39 prover: &P,
40 memo: [u8; 36],
41 platform_version: &PlatformVersion,
42) -> Result<StateTransition, ProtocolError> {
43 if fee_strategy.is_empty() {
44 return Err(ProtocolError::ShieldedBuildError(
45 "fee_strategy must have at least one step".to_string(),
46 ));
47 }
48
49 let bundle = build_output_only_bundle(recipient, shield_amount, memo, prover)?;
50 let sb = serialize_authorized_bundle(&bundle);
51
52 ShieldTransition::try_from_bundle_with_signer(
53 inputs,
54 sb.actions,
55 sb.value_balance.unsigned_abs(),
56 sb.anchor,
57 sb.proof,
58 sb.binding_signature,
59 fee_strategy,
60 signer,
61 user_fee_increase,
62 platform_version,
63 )
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::address_funds::AddressFundsFeeStrategyStep;
70 use crate::address_funds::AddressWitness;
71 use crate::shielded::builder::test_helpers::{test_orchard_address, TestProver};
72 use platform_value::BinaryData;
73
74 #[derive(Debug)]
77 struct DummySigner;
78
79 impl Signer<PlatformAddress> for DummySigner {
80 fn sign(&self, _key: &PlatformAddress, _data: &[u8]) -> Result<BinaryData, ProtocolError> {
81 Ok(BinaryData::new(vec![0u8; 65]))
82 }
83
84 fn sign_create_witness(
85 &self,
86 _key: &PlatformAddress,
87 _data: &[u8],
88 ) -> Result<AddressWitness, ProtocolError> {
89 Ok(AddressWitness::P2pkh {
90 signature: BinaryData::new(vec![0u8; 65]),
91 })
92 }
93
94 fn can_sign_with(&self, _key: &PlatformAddress) -> bool {
95 true
96 }
97 }
98
99 #[test]
100 fn test_build_shield_empty_fee_strategy() {
101 let recipient = test_orchard_address();
102 let platform_version = PlatformVersion::latest();
103 let result = build_shield_transition(
104 &recipient,
105 1000,
106 BTreeMap::new(),
107 vec![], &DummySigner,
109 0,
110 &TestProver,
111 [0u8; 36],
112 platform_version,
113 );
114
115 assert!(result.is_err());
116 let err = result.unwrap_err().to_string();
117 assert!(
118 err.contains("fee_strategy must have at least one step"),
119 "unexpected error: {}",
120 err
121 );
122 }
123
124 #[test]
125 fn test_build_shield_transition_valid() {
126 let recipient = test_orchard_address();
127 let platform_version = PlatformVersion::latest();
128 let input_address = PlatformAddress::P2pkh([1u8; 20]);
130 let mut inputs = BTreeMap::new();
131 inputs.insert(input_address, (0u32, 100_000u64));
132
133 let fee_strategy = vec![AddressFundsFeeStrategyStep::DeductFromInput(0)];
134
135 let result = build_shield_transition(
136 &recipient,
137 50_000,
138 inputs,
139 fee_strategy,
140 &DummySigner,
141 0,
142 &TestProver,
143 [0u8; 36],
144 platform_version,
145 );
146
147 assert!(result.is_ok(), "expected Ok, got: {:?}", result.err());
148 match result.unwrap() {
149 StateTransition::Shield(_) => {} other => panic!("expected Shield variant, got {:?}", other),
151 }
152 }
153}