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 into_vec(self) -> Vec<Asset> {
117 self.assets
118 }
119}
120
121impl PartialEq for NoteAssets {
122 fn eq(&self, other: &Self) -> bool {
123 self.assets == other.assets
124 }
125}
126
127impl Eq for NoteAssets {}
128
129impl SequentialCommit for NoteAssets {
130 type Commitment = Word;
131
132 fn to_elements(&self) -> Vec<Felt> {
134 to_elements(&self.assets)
135 }
136
137 fn to_commitment(&self) -> Self::Commitment {
139 to_commitment(&self.assets)
140 }
141}
142
143fn to_elements(assets: &[Asset]) -> Vec<Felt> {
144 let mut elements = Vec::with_capacity(assets.len() * 2 * WORD_SIZE);
145 elements.extend(assets.iter().flat_map(Asset::as_elements));
146 elements
147}
148
149fn to_commitment(assets: &[Asset]) -> Word {
150 Hasher::hash_elements(&to_elements(assets))
151}
152
153impl Serializable for NoteAssets {
157 fn write_into<W: ByteWriter>(&self, target: &mut W) {
158 const _: () = assert!(NoteAssets::MAX_NUM_ASSETS <= u8::MAX as usize);
159 debug_assert!(self.assets.len() <= NoteAssets::MAX_NUM_ASSETS);
160 target.write_u8(self.assets.len().try_into().expect("Asset number must fit into `u8`"));
161 target.write_many(&self.assets);
162 }
163
164 fn get_size_hint(&self) -> usize {
165 let u8_size = 0u8.get_size_hint();
167
168 let assets_size: usize = self.assets.iter().map(|asset| asset.get_size_hint()).sum();
169
170 u8_size + assets_size
171 }
172}
173
174impl Deserializable for NoteAssets {
175 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
176 let count = source.read_u8()?;
177 let assets = source.read_many_iter::<Asset>(count.into())?.collect::<Result<_, _>>()?;
178 Self::new(assets).map_err(|e| DeserializationError::InvalidValue(format!("{e:?}")))
179 }
180}
181
182#[cfg(test)]
186mod tests {
187 use super::NoteAssets;
188 use crate::account::AccountId;
189 use crate::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails};
190 use crate::testing::account_id::{
191 ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET,
192 ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
193 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
194 };
195
196 #[test]
197 fn iter_fungible_asset() {
198 let faucet_id_1 = AccountId::try_from(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET).unwrap();
199 let faucet_id_2 = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap();
200 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET).unwrap();
201 let details = NonFungibleAssetDetails::new(account_id, vec![1, 2, 3]).unwrap();
202
203 let asset1 = Asset::Fungible(FungibleAsset::new(faucet_id_1, 100).unwrap());
204 let asset2 = Asset::Fungible(FungibleAsset::new(faucet_id_2, 50).unwrap());
205 let non_fungible_asset = Asset::NonFungible(NonFungibleAsset::new(&details).unwrap());
206
207 let assets = NoteAssets::new([asset1, asset2, non_fungible_asset].to_vec()).unwrap();
209
210 let mut fungible_assets = assets.iter_fungible();
211 assert_eq!(fungible_assets.next().unwrap(), asset1.unwrap_fungible());
212 assert_eq!(fungible_assets.next().unwrap(), asset2.unwrap_fungible());
213 assert_eq!(fungible_assets.next(), None);
214 }
215}