snarkvm_ledger_block/transactions/
mod.rs1pub mod confirmed;
17pub use confirmed::*;
18
19pub mod rejected;
20pub use rejected::*;
21
22mod bytes;
23mod merkle;
24mod serialize;
25mod string;
26
27use crate::{Transaction, Transition};
28use console::{
29 network::prelude::*,
30 program::{
31 Ciphertext,
32 FINALIZE_ID_DEPTH,
33 FINALIZE_OPERATIONS_DEPTH,
34 ProgramOwner,
35 Record,
36 TRANSACTIONS_DEPTH,
37 TransactionsPath,
38 TransactionsTree,
39 },
40 types::{Field, Group, U64},
41};
42use snarkvm_ledger_committee::Committee;
43use snarkvm_ledger_narwhal_batch_header::BatchHeader;
44use snarkvm_synthesizer_program::FinalizeOperation;
45
46use indexmap::IndexMap;
47
48#[cfg(not(feature = "serial"))]
49use rayon::prelude::*;
50
51#[derive(Clone, PartialEq, Eq)]
53pub struct Transactions<N: Network> {
54 transactions: IndexMap<N::TransactionID, ConfirmedTransaction<N>>,
56}
57
58impl<N: Network> Transactions<N> {
59 pub fn from(transactions: &[ConfirmedTransaction<N>]) -> Self {
61 Self::from_iter(transactions.iter())
62 }
63}
64
65impl<N: Network> FromIterator<ConfirmedTransaction<N>> for Transactions<N> {
66 fn from_iter<T: IntoIterator<Item = ConfirmedTransaction<N>>>(iter: T) -> Self {
68 Self { transactions: iter.into_iter().map(|transaction| (transaction.id(), transaction)).collect() }
69 }
70}
71
72impl<'a, N: Network> FromIterator<&'a ConfirmedTransaction<N>> for Transactions<N> {
73 fn from_iter<T: IntoIterator<Item = &'a ConfirmedTransaction<N>>>(iter: T) -> Self {
75 Self::from_iter(iter.into_iter().cloned())
76 }
77}
78
79impl<N: Network> Transactions<N> {
80 pub fn get(&self, transaction_id: &N::TransactionID) -> Option<&ConfirmedTransaction<N>> {
82 self.transactions.get(transaction_id)
83 }
84
85 pub fn is_empty(&self) -> bool {
87 self.transactions.is_empty()
88 }
89
90 pub fn len(&self) -> usize {
92 self.transactions.len()
93 }
94
95 pub fn num_accepted(&self) -> usize {
97 cfg_values!(self.transactions).filter(|tx| tx.is_accepted()).count()
98 }
99
100 pub fn num_rejected(&self) -> usize {
102 cfg_values!(self.transactions).filter(|tx| tx.is_rejected()).count()
103 }
104
105 pub fn num_finalize(&self) -> usize {
107 cfg_values!(self.transactions).map(|tx| tx.num_finalize()).sum()
108 }
109
110 pub fn index_of(&self, transaction_id: &N::TransactionID) -> Option<usize> {
112 self.transactions.get_index_of(transaction_id)
113 }
114}
115
116impl<N: Network> Transactions<N> {
117 pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
119 cfg_values!(self.transactions).any(|tx| tx.contains_transition(transition_id))
120 }
121
122 pub fn contains_serial_number(&self, serial_number: &Field<N>) -> bool {
124 cfg_values!(self.transactions).any(|tx| tx.contains_serial_number(serial_number))
125 }
126
127 pub fn contains_commitment(&self, commitment: &Field<N>) -> bool {
129 cfg_values!(self.transactions).any(|tx| tx.contains_commitment(commitment))
130 }
131}
132
133impl<N: Network> Transactions<N> {
134 pub fn find_confirmed_transaction_for_unconfirmed_transaction_id(
136 &self,
137 unconfirmed_transaction_id: &N::TransactionID,
138 ) -> Option<&ConfirmedTransaction<N>> {
139 cfg_find!(self.transactions, |txn| txn.contains_unconfirmed_transaction_id(unconfirmed_transaction_id))
140 }
141
142 pub fn find_transaction_for_transition_id(&self, transition_id: &N::TransitionID) -> Option<&Transaction<N>> {
147 cfg_find!(self.transactions, |txn| txn.contains_transition(transition_id)).map(|tx| tx.transaction())
148 }
149
150 pub fn find_unconfirmed_transaction_for_transition_id(
155 &self,
156 transition_id: &N::TransitionID,
157 ) -> Result<Option<Transaction<N>>> {
158 let result = cfg_find!(self.transactions, |tx| tx.contains_transition(transition_id));
159
160 match result {
161 Some(txn) => Ok(Some(txn.to_unconfirmed_transaction()?)),
162 None => Ok(None),
163 }
164 }
165
166 pub fn find_transaction_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transaction<N>> {
168 cfg_find!(self.transactions, |txn| txn.contains_serial_number(serial_number)).map(|tx| tx.transaction())
169 }
170
171 pub fn find_transaction_for_commitment(&self, commitment: &Field<N>) -> Option<&Transaction<N>> {
173 cfg_find!(self.transactions, |txn| txn.contains_commitment(commitment)).map(|tx| tx.transaction())
174 }
175
176 pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
178 cfg_find_map!(self.transactions, |txn| txn.find_transition(transition_id))
179 }
180
181 pub fn find_transition_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transition<N>> {
183 cfg_find_map!(self.transactions, |txn| txn.find_transition_for_serial_number(serial_number))
184 }
185
186 pub fn find_transition_for_commitment(&self, commitment: &Field<N>) -> Option<&Transition<N>> {
188 cfg_find_map!(self.transactions, |txn| txn.find_transition_for_commitment(commitment))
189 }
190
191 pub fn find_record(&self, commitment: &Field<N>) -> Option<&Record<N, Ciphertext<N>>> {
193 cfg_find_map!(self.transactions, |txn| txn.find_record(commitment))
194 }
195}
196
197impl<N: Network> Transactions<N> {
198 pub const MAX_TRANSACTIONS: usize = usize::pow(2, TRANSACTIONS_DEPTH as u32).saturating_sub(1);
200
201 pub fn max_aborted_transactions() -> Result<usize> {
203 Ok(BatchHeader::<N>::MAX_TRANSMISSIONS_PER_BATCH
204 * BatchHeader::<N>::MAX_GC_ROUNDS
205 * Committee::<N>::max_committee_size()? as usize)
206 }
207
208 pub fn iter(&self) -> impl '_ + ExactSizeIterator<Item = &ConfirmedTransaction<N>> {
210 self.transactions.values()
211 }
212
213 #[cfg(not(feature = "serial"))]
215 pub fn par_iter(&self) -> impl '_ + IndexedParallelIterator<Item = &ConfirmedTransaction<N>> {
216 self.transactions.par_values()
217 }
218
219 pub fn transaction_ids(&self) -> impl '_ + ExactSizeIterator<Item = &N::TransactionID> {
221 self.transactions.keys()
222 }
223
224 pub fn deployments(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
226 self.iter().filter(|tx| tx.is_accepted() && tx.is_deploy())
227 }
228
229 pub fn executions(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
231 self.iter().filter(|tx| tx.is_accepted() && tx.is_execute())
232 }
233
234 pub fn transitions(&self) -> impl '_ + Iterator<Item = &Transition<N>> {
236 self.iter().flat_map(|tx| tx.transitions())
237 }
238
239 pub fn transition_ids(&self) -> impl '_ + Iterator<Item = &N::TransitionID> {
241 self.iter().flat_map(|tx| tx.transition_ids())
242 }
243
244 pub fn transition_public_keys(&self) -> impl '_ + Iterator<Item = &Group<N>> {
246 self.iter().flat_map(|tx| tx.transition_public_keys())
247 }
248
249 pub fn transition_commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
251 self.iter().flat_map(|tx| tx.transition_commitments())
252 }
253
254 pub fn tags(&self) -> impl '_ + Iterator<Item = &Field<N>> {
256 self.iter().flat_map(|tx| tx.tags())
257 }
258
259 pub fn input_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
261 self.iter().flat_map(|tx| tx.input_ids())
262 }
263
264 pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = &Field<N>> {
266 self.iter().flat_map(|tx| tx.serial_numbers())
267 }
268
269 pub fn output_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
271 self.iter().flat_map(|tx| tx.output_ids())
272 }
273
274 pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
276 self.iter().flat_map(|tx| tx.commitments())
277 }
278
279 pub fn records(&self) -> impl '_ + Iterator<Item = (&Field<N>, &Record<N, Ciphertext<N>>)> {
281 self.iter().flat_map(|tx| tx.records())
282 }
283
284 pub fn nonces(&self) -> impl '_ + Iterator<Item = &Group<N>> {
286 self.iter().flat_map(|tx| tx.nonces())
287 }
288
289 pub fn transaction_fee_amounts(&self) -> impl '_ + Iterator<Item = Result<U64<N>>> {
291 self.iter().map(|tx| tx.fee_amount())
292 }
293
294 pub fn finalize_operations(&self) -> impl '_ + Iterator<Item = &FinalizeOperation<N>> {
296 self.iter().flat_map(|tx| tx.finalize_operations())
297 }
298}
299
300impl<N: Network> IntoIterator for Transactions<N> {
301 type IntoIter = indexmap::map::IntoValues<N::TransactionID, Self::Item>;
302 type Item = ConfirmedTransaction<N>;
303
304 fn into_iter(self) -> Self::IntoIter {
306 self.transactions.into_values()
307 }
308}
309
310impl<N: Network> Transactions<N> {
311 pub fn into_transaction_ids(self) -> impl ExactSizeIterator<Item = N::TransactionID> {
313 self.transactions.into_keys()
314 }
315
316 pub fn into_deployments(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
318 self.into_iter().filter(|tx| tx.is_accepted() && tx.is_deploy())
319 }
320
321 pub fn into_executions(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
323 self.into_iter().filter(|tx| tx.is_accepted() && tx.is_execute())
324 }
325
326 pub fn into_transitions(self) -> impl Iterator<Item = Transition<N>> {
328 self.into_iter().flat_map(|tx| tx.into_transaction().into_transitions())
329 }
330
331 pub fn into_transition_ids(self) -> impl Iterator<Item = N::TransitionID> {
333 self.into_iter().flat_map(|tx| tx.into_transaction().into_transition_ids())
334 }
335
336 pub fn into_transition_public_keys(self) -> impl Iterator<Item = Group<N>> {
338 self.into_iter().flat_map(|tx| tx.into_transaction().into_transition_public_keys())
339 }
340
341 pub fn into_tags(self) -> impl Iterator<Item = Field<N>> {
343 self.into_iter().flat_map(|tx| tx.into_transaction().into_tags())
344 }
345
346 pub fn into_serial_numbers(self) -> impl Iterator<Item = Field<N>> {
348 self.into_iter().flat_map(|tx| tx.into_transaction().into_serial_numbers())
349 }
350
351 pub fn into_commitments(self) -> impl Iterator<Item = Field<N>> {
353 self.into_iter().flat_map(|tx| tx.into_transaction().into_commitments())
354 }
355
356 pub fn into_records(self) -> impl Iterator<Item = (Field<N>, Record<N, Ciphertext<N>>)> {
358 self.into_iter().flat_map(|tx| tx.into_transaction().into_records())
359 }
360
361 pub fn into_nonces(self) -> impl Iterator<Item = Group<N>> {
363 self.into_iter().flat_map(|tx| tx.into_transaction().into_nonces())
364 }
365}
366
367#[cfg(test)]
368pub mod test_helpers {
369 use super::*;
370
371 type CurrentNetwork = console::network::MainnetV0;
372
373 pub(crate) fn sample_block_transactions(rng: &mut TestRng) -> Transactions<CurrentNetwork> {
375 crate::test_helpers::sample_genesis_block(rng).transactions().clone()
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use snarkvm_ledger_narwhal_batch_header::BatchHeader;
383
384 type CurrentNetwork = console::network::MainnetV0;
385
386 #[test]
387 fn test_max_transmissions() {
388 let max_transmissions_per_block = BatchHeader::<CurrentNetwork>::MAX_TRANSMISSIONS_PER_BATCH
390 * BatchHeader::<CurrentNetwork>::MAX_GC_ROUNDS
391 * CurrentNetwork::LATEST_MAX_CERTIFICATES().unwrap() as usize;
392
393 assert!(
397 max_transmissions_per_block <= Transactions::<CurrentNetwork>::MAX_TRANSACTIONS,
398 "The maximum number of transmissions in a block is too large"
399 );
400 }
401}