miden_protocol/note/
assets.rs1use alloc::vec::Vec;
2
3use miden_crypto::SequentialCommit;
4
5use crate::asset::{Asset, FungibleAsset, NonFungibleAsset};
6use crate::errors::NoteError;
7use crate::utils::serde::{
8 ByteReader,
9 ByteWriter,
10 Deserializable,
11 DeserializationError,
12 Serializable,
13};
14use crate::{Felt, Hasher, MAX_ASSETS_PER_NOTE, WORD_SIZE, Word};
15
16#[derive(Debug, Default, Clone)]
28pub struct NoteAssets {
29 assets: Vec<Asset>,
30 commitment: Word,
31}
32
33impl NoteAssets {
34 pub const MAX_NUM_ASSETS: usize = MAX_ASSETS_PER_NOTE;
39
40 pub fn new(assets: Vec<Asset>) -> Result<Self, NoteError> {
50 if assets.len() > Self::MAX_NUM_ASSETS {
51 return Err(NoteError::TooManyAssets(assets.len()));
52 }
53
54 for (i, asset) in assets.iter().enumerate().skip(1) {
56 if assets[..i].iter().any(|a| a.is_same(asset)) {
59 return Err(match asset {
60 Asset::Fungible(asset) => NoteError::DuplicateFungibleAsset(asset.faucet_id()),
61 Asset::NonFungible(asset) => NoteError::DuplicateNonFungibleAsset(*asset),
62 });
63 }
64 }
65
66 let commitment = to_commitment(&assets);
67
68 Ok(Self { assets, commitment })
69 }
70
71 pub fn commitment(&self) -> Word {
76 self.commitment
77 }
78
79 pub fn num_assets(&self) -> usize {
81 self.assets.len()
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.assets.is_empty()
87 }
88
89 pub fn iter(&self) -> core::slice::Iter<'_, Asset> {
91 self.assets.iter()
92 }
93
94 pub fn to_elements(&self) -> Vec<Felt> {
96 <Self as SequentialCommit>::to_elements(self)
97 }
98
99 pub fn iter_fungible(&self) -> impl Iterator<Item = FungibleAsset> {
101 self.assets.iter().filter_map(|asset| match asset {
102 Asset::Fungible(fungible_asset) => Some(*fungible_asset),
103 Asset::NonFungible(_) => None,
104 })
105 }
106
107 pub fn iter_non_fungible(&self) -> impl Iterator<Item = NonFungibleAsset> {
109 self.assets.iter().filter_map(|asset| match asset {
110 Asset::Fungible(_) => None,
111 Asset::NonFungible(non_fungible_asset) => Some(*non_fungible_asset),
112 })
113 }
114
115 pub fn add_asset(&mut self, asset: Asset) -> Result<(), NoteError> {
127 if let Some(own_asset) = self.assets.iter_mut().find(|a| a.is_same(&asset)) {
130 match own_asset {
131 Asset::Fungible(f_own_asset) => {
132 let new_asset = f_own_asset
135 .add(asset.unwrap_fungible())
136 .map_err(NoteError::AddFungibleAssetBalanceError)?;
137 *own_asset = Asset::Fungible(new_asset);
138 },
139 Asset::NonFungible(nf_asset) => {
140 return Err(NoteError::DuplicateNonFungibleAsset(*nf_asset));
141 },
142 }
143 } else {
144 self.assets.push(asset);
146 if self.assets.len() > Self::MAX_NUM_ASSETS {
147 return Err(NoteError::TooManyAssets(self.assets.len()));
148 }
149 }
150
151 self.commitment = self.to_commitment();
153
154 Ok(())
155 }
156}
157
158impl PartialEq for NoteAssets {
159 fn eq(&self, other: &Self) -> bool {
160 self.assets == other.assets
161 }
162}
163
164impl Eq for NoteAssets {}
165
166impl SequentialCommit for NoteAssets {
167 type Commitment = Word;
168
169 fn to_elements(&self) -> Vec<Felt> {
171 to_elements(&self.assets)
172 }
173
174 fn to_commitment(&self) -> Self::Commitment {
176 to_commitment(&self.assets)
177 }
178}
179
180fn to_elements(assets: &[Asset]) -> Vec<Felt> {
181 let mut elements = Vec::with_capacity(assets.len() * 2 * WORD_SIZE);
182 elements.extend(assets.iter().flat_map(Asset::as_elements));
183 elements
184}
185
186fn to_commitment(assets: &[Asset]) -> Word {
187 Hasher::hash_elements(&to_elements(assets))
188}
189
190impl Serializable for NoteAssets {
194 fn write_into<W: ByteWriter>(&self, target: &mut W) {
195 const _: () = assert!(NoteAssets::MAX_NUM_ASSETS <= u8::MAX as usize);
196 debug_assert!(self.assets.len() <= NoteAssets::MAX_NUM_ASSETS);
197 target.write_u8(self.assets.len().try_into().expect("Asset number must fit into `u8`"));
198 target.write_many(&self.assets);
199 }
200
201 fn get_size_hint(&self) -> usize {
202 let u8_size = 0u8.get_size_hint();
204
205 let assets_size: usize = self.assets.iter().map(|asset| asset.get_size_hint()).sum();
206
207 u8_size + assets_size
208 }
209}
210
211impl Deserializable for NoteAssets {
212 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
213 let count = source.read_u8()?;
214 let assets = source.read_many_iter::<Asset>(count.into())?.collect::<Result<_, _>>()?;
215 Self::new(assets).map_err(|e| DeserializationError::InvalidValue(format!("{e:?}")))
216 }
217}
218
219#[cfg(test)]
223mod tests {
224 use super::NoteAssets;
225 use crate::Word;
226 use crate::account::AccountId;
227 use crate::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails};
228 use crate::testing::account_id::{
229 ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET,
230 ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
231 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
232 };
233
234 #[test]
235 fn add_asset() {
236 let faucet_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET).unwrap();
237
238 let asset1 = Asset::Fungible(FungibleAsset::new(faucet_id, 100).unwrap());
239 let asset2 = Asset::Fungible(FungibleAsset::new(faucet_id, 50).unwrap());
240
241 let mut assets = NoteAssets::default();
243
244 assert_eq!(assets.commitment, Word::empty());
245
246 assert!(assets.add_asset(asset1).is_ok());
248 assert_eq!(assets.assets, vec![asset1]);
249 assert!(!assets.commitment.is_empty());
250
251 assert!(assets.add_asset(asset2).is_ok());
253 let expected_asset = Asset::Fungible(FungibleAsset::new(faucet_id, 150).unwrap());
254 assert_eq!(assets.assets, vec![expected_asset]);
255 assert!(!assets.commitment.is_empty());
256 }
257 #[test]
258 fn iter_fungible_asset() {
259 let faucet_id_1 = AccountId::try_from(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET).unwrap();
260 let faucet_id_2 = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap();
261 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET).unwrap();
262 let details = NonFungibleAssetDetails::new(account_id, vec![1, 2, 3]).unwrap();
263
264 let asset1 = Asset::Fungible(FungibleAsset::new(faucet_id_1, 100).unwrap());
265 let asset2 = Asset::Fungible(FungibleAsset::new(faucet_id_2, 50).unwrap());
266 let non_fungible_asset = Asset::NonFungible(NonFungibleAsset::new(&details).unwrap());
267
268 let assets = NoteAssets::new([asset1, asset2, non_fungible_asset].to_vec()).unwrap();
270
271 let mut fungible_assets = assets.iter_fungible();
272 assert_eq!(fungible_assets.next().unwrap(), asset1.unwrap_fungible());
273 assert_eq!(fungible_assets.next().unwrap(), asset2.unwrap_fungible());
274 assert_eq!(fungible_assets.next(), None);
275 }
276}