masp_primitives/transaction/components/transparent/
builder.rs1use std::fmt;
4
5use crate::{
6 asset_type::AssetType,
7 transaction::{
8 TransparentAddress,
9 components::{
10 amount::{I128Sum, MAX_MONEY, ValueSum},
11 transparent::{self, Authorization, Authorized, Bundle, TxIn, TxOut, fees},
12 },
13 sighash::TransparentAuthorizingContext,
14 },
15};
16use borsh::BorshSchema;
17use borsh::{BorshDeserialize, BorshSerialize};
18
19#[derive(Debug, PartialEq, Eq)]
20pub enum Error {
21 InvalidAddress,
22 InvalidAmount,
23 InvalidAsset,
24}
25
26impl fmt::Display for Error {
27 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28 match self {
29 Error::InvalidAddress => write!(f, "Invalid address"),
30 Error::InvalidAmount => write!(f, "Invalid amount"),
31 Error::InvalidAsset => write!(f, "Invalid asset"),
32 }
33 }
34}
35
36#[cfg(not(feature = "transparent-inputs"))]
39enum InvalidTransparentInput {}
40
41#[cfg(not(feature = "transparent-inputs"))]
42impl fees::InputView for InvalidTransparentInput {
43 fn coin(&self) -> &TxOut {
44 panic!("transparent-inputs feature flag is not enabled.");
45 }
46}
47
48#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
49#[cfg(feature = "transparent-inputs")]
50#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
51struct TransparentInputInfo {
52 coin: TxOut,
53}
54
55#[cfg(feature = "transparent-inputs")]
56impl fees::InputView for TransparentInputInfo {
57 fn coin(&self) -> &TxOut {
58 &self.coin
59 }
60}
61
62#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)]
63pub struct TransparentBuilder {
64 #[cfg(feature = "transparent-inputs")]
65 inputs: Vec<TransparentInputInfo>,
66 vout: Vec<TxOut>,
67}
68
69#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
70#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
71pub struct Unauthorized {
72 #[cfg(feature = "transparent-inputs")]
73 inputs: Vec<TransparentInputInfo>,
74}
75
76impl Authorization for Unauthorized {
77 type TransparentSig = ();
78}
79
80impl TransparentBuilder {
81 pub fn empty() -> Self {
83 TransparentBuilder {
84 #[cfg(feature = "transparent-inputs")]
85 inputs: vec![],
86 vout: vec![],
87 }
88 }
89
90 pub fn inputs(&self) -> &[impl fees::InputView] {
93 #[cfg(feature = "transparent-inputs")]
94 return &self.inputs;
95
96 #[cfg(not(feature = "transparent-inputs"))]
97 {
98 let invalid: &[InvalidTransparentInput] = &[];
99 return invalid;
100 }
101 }
102
103 pub fn outputs(&self) -> &[impl fees::OutputView] {
105 &self.vout
106 }
107
108 #[cfg(feature = "transparent-inputs")]
110 pub fn add_input(&mut self, coin: TxOut) -> Result<(), Error> {
111 self.inputs.push(TransparentInputInfo { coin });
112
113 Ok(())
114 }
115
116 pub fn add_output(
117 &mut self,
118 to: &TransparentAddress,
119 asset_type: AssetType,
120 value: u64,
121 ) -> Result<(), Error> {
122 if value > MAX_MONEY {
123 return Err(Error::InvalidAmount);
124 }
125
126 self.vout.push(TxOut {
127 asset_type,
128 value,
129 address: *to,
130 });
131
132 Ok(())
133 }
134
135 pub fn value_balance(&self) -> I128Sum {
136 #[cfg(feature = "transparent-inputs")]
137 let input_sum = self
138 .inputs
139 .iter()
140 .map(|input| ValueSum::from_pair(input.coin.asset_type, input.coin.value as i128))
141 .sum::<I128Sum>();
142
143 #[cfg(not(feature = "transparent-inputs"))]
144 let input_sum = ValueSum::zero();
145
146 let output_sum = self
147 .vout
148 .iter()
149 .map(|vo| ValueSum::from_pair(vo.asset_type, vo.value as i128))
150 .sum::<I128Sum>();
151
152 input_sum - output_sum
154 }
155
156 pub fn build(self) -> Option<transparent::Bundle<Unauthorized>> {
157 #[cfg(feature = "transparent-inputs")]
158 let vin: Vec<TxIn<Unauthorized>> = self
159 .inputs
160 .iter()
161 .map(|i| TxIn::<Unauthorized> {
162 asset_type: i.coin.asset_type,
163 value: i.coin.value,
164 address: i.coin.address,
165 transparent_sig: (),
166 })
167 .collect();
168
169 #[cfg(not(feature = "transparent-inputs"))]
170 let vin: Vec<TxIn> = vec![];
171
172 if vin.is_empty() && self.vout.is_empty() {
173 None
174 } else {
175 Some(transparent::Bundle {
176 vin,
177 vout: self.vout,
178 authorization: Unauthorized {
179 #[cfg(feature = "transparent-inputs")]
180 inputs: self.inputs,
181 },
182 })
183 }
184 }
185}
186
187#[cfg(not(feature = "transparent-inputs"))]
188impl TransparentAuthorizingContext for Unauthorized {
189 fn input_amounts(&self) -> Vec<(AssetType, i64)> {
190 vec![]
191 }
192}
193
194#[cfg(feature = "transparent-inputs")]
195impl TransparentAuthorizingContext for Unauthorized {
196 fn input_amounts(&self) -> Vec<(AssetType, u64)> {
197 self.inputs
198 .iter()
199 .map(|txin| (txin.coin.asset_type, txin.coin.value))
200 .collect()
201 }
202}
203
204impl Bundle<Unauthorized> {
205 pub fn apply_signatures(self) -> Bundle<Authorized> {
206 transparent::Bundle {
207 vin: self
208 .vin
209 .iter()
210 .map(|txin| TxIn {
211 asset_type: txin.asset_type,
212 address: txin.address,
213 value: txin.value,
214 transparent_sig: (),
215 })
216 .collect(),
217 vout: self.vout,
218 authorization: Authorized,
219 }
220 }
221}