snarkvm_ledger_block/transactions/
mod.rs1pub mod confirmed;
17pub use confirmed::*;
18
19pub mod rejected;
20pub use rejected::*;
21
22pub mod rejected_reason;
23pub use rejected_reason::*;
24
25mod bytes;
26mod merkle;
27mod serialize;
28mod string;
29
30use crate::{Transaction, Transition};
31use console::{
32 network::prelude::*,
33 program::{
34 Ciphertext,
35 FINALIZE_ID_DEPTH,
36 FINALIZE_OPERATIONS_DEPTH,
37 Identifier,
38 ProgramID,
39 ProgramOwner,
40 Record,
41 TRANSACTIONS_DEPTH,
42 TransactionsPath,
43 TransactionsTree,
44 },
45 types::{Field, Group, U64},
46};
47use snarkvm_ledger_committee::Committee;
48use snarkvm_ledger_narwhal_batch_header::BatchHeader;
49use snarkvm_synthesizer_error::IndexedFinalizeError;
50use snarkvm_synthesizer_program::{Command, FinalizeOperation};
51
52use indexmap::IndexMap;
53
54#[cfg(not(feature = "serial"))]
55use rayon::prelude::*;
56
57#[derive(Clone, PartialEq, Eq)]
59pub struct Transactions<N: Network> {
60 transactions: IndexMap<N::TransactionID, ConfirmedTransaction<N>>,
62}
63
64impl<N: Network> Transactions<N> {
65 pub fn from(transactions: &[ConfirmedTransaction<N>]) -> Self {
67 Self::from_iter(transactions.iter())
68 }
69}
70
71impl<N: Network> FromIterator<ConfirmedTransaction<N>> for Transactions<N> {
72 fn from_iter<T: IntoIterator<Item = ConfirmedTransaction<N>>>(iter: T) -> Self {
74 Self { transactions: iter.into_iter().map(|transaction| (transaction.id(), transaction)).collect() }
75 }
76}
77
78impl<'a, N: Network> FromIterator<&'a ConfirmedTransaction<N>> for Transactions<N> {
79 fn from_iter<T: IntoIterator<Item = &'a ConfirmedTransaction<N>>>(iter: T) -> Self {
81 Self::from_iter(iter.into_iter().cloned())
82 }
83}
84
85impl<N: Network> Transactions<N> {
86 pub fn get(&self, transaction_id: &N::TransactionID) -> Option<&ConfirmedTransaction<N>> {
88 self.transactions.get(transaction_id)
89 }
90
91 pub fn is_empty(&self) -> bool {
93 self.transactions.is_empty()
94 }
95
96 pub fn len(&self) -> usize {
98 self.transactions.len()
99 }
100
101 pub fn num_accepted(&self) -> usize {
103 cfg_values!(self.transactions).filter(|tx| tx.is_accepted()).count()
104 }
105
106 pub fn num_rejected(&self) -> usize {
108 cfg_values!(self.transactions).filter(|tx| tx.is_rejected()).count()
109 }
110
111 pub fn num_finalize(&self) -> usize {
113 cfg_values!(self.transactions).map(|tx| tx.num_finalize()).sum()
114 }
115
116 pub fn index_of(&self, transaction_id: &N::TransactionID) -> Option<usize> {
118 self.transactions.get_index_of(transaction_id)
119 }
120}
121
122impl<N: Network> Transactions<N> {
123 pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
125 cfg_values!(self.transactions).any(|tx| tx.contains_transition(transition_id))
126 }
127
128 pub fn contains_serial_number(&self, serial_number: &Field<N>) -> bool {
130 cfg_values!(self.transactions).any(|tx| tx.contains_serial_number(serial_number))
131 }
132
133 pub fn contains_commitment(&self, commitment: &Field<N>) -> bool {
135 cfg_values!(self.transactions).any(|tx| tx.contains_commitment(commitment))
136 }
137}
138
139impl<N: Network> Transactions<N> {
140 pub fn find_confirmed_transaction_for_unconfirmed_transaction_id(
142 &self,
143 unconfirmed_transaction_id: &N::TransactionID,
144 ) -> Option<&ConfirmedTransaction<N>> {
145 cfg_find!(self.transactions, |txn| txn.contains_unconfirmed_transaction_id(unconfirmed_transaction_id))
146 }
147
148 pub fn find_transaction_for_transition_id(&self, transition_id: &N::TransitionID) -> Option<&Transaction<N>> {
153 cfg_find!(self.transactions, |txn| txn.contains_transition(transition_id)).map(|tx| tx.transaction())
154 }
155
156 pub fn find_unconfirmed_transaction_for_transition_id(
161 &self,
162 transition_id: &N::TransitionID,
163 ) -> Result<Option<Transaction<N>>> {
164 let result = cfg_find!(self.transactions, |tx| tx.contains_transition(transition_id));
165
166 match result {
167 Some(txn) => Ok(Some(txn.to_unconfirmed_transaction()?)),
168 None => Ok(None),
169 }
170 }
171
172 pub fn find_transaction_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transaction<N>> {
174 cfg_find!(self.transactions, |txn| txn.contains_serial_number(serial_number)).map(|tx| tx.transaction())
175 }
176
177 pub fn find_transaction_for_commitment(&self, commitment: &Field<N>) -> Option<&Transaction<N>> {
179 cfg_find!(self.transactions, |txn| txn.contains_commitment(commitment)).map(|tx| tx.transaction())
180 }
181
182 pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
184 cfg_find_map!(self.transactions, |txn| txn.find_transition(transition_id))
185 }
186
187 pub fn find_transition_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transition<N>> {
189 cfg_find_map!(self.transactions, |txn| txn.find_transition_for_serial_number(serial_number))
190 }
191
192 pub fn find_transition_for_commitment(&self, commitment: &Field<N>) -> Option<&Transition<N>> {
194 cfg_find_map!(self.transactions, |txn| txn.find_transition_for_commitment(commitment))
195 }
196
197 pub fn find_record(&self, commitment: &Field<N>) -> Option<&Record<N, Ciphertext<N>>> {
199 cfg_find_map!(self.transactions, |txn| txn.find_record(commitment))
200 }
201}
202
203impl<N: Network> Transactions<N> {
204 pub const MAX_TRANSACTIONS: usize = usize::pow(2, TRANSACTIONS_DEPTH as u32).saturating_sub(1);
206
207 pub fn max_aborted_transactions() -> usize {
209 BatchHeader::<N>::MAX_TRANSMISSIONS_PER_BATCH
210 * BatchHeader::<N>::MAX_GC_ROUNDS
211 * Committee::<N>::max_committee_size() as usize
212 }
213
214 pub fn iter(&self) -> impl '_ + ExactSizeIterator<Item = &ConfirmedTransaction<N>> {
216 self.transactions.values()
217 }
218
219 #[cfg(not(feature = "serial"))]
221 pub fn par_iter(&self) -> impl '_ + IndexedParallelIterator<Item = &ConfirmedTransaction<N>> {
222 self.transactions.par_values()
223 }
224
225 pub fn transaction_ids(&self) -> impl '_ + ExactSizeIterator<Item = &N::TransactionID> {
227 self.transactions.keys()
228 }
229
230 pub fn deployments(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
232 self.iter().filter(|tx| tx.is_accepted() && tx.is_deploy())
233 }
234
235 pub fn executions(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
237 self.iter().filter(|tx| tx.is_accepted() && tx.is_execute())
238 }
239
240 pub fn transitions(&self) -> impl '_ + Iterator<Item = &Transition<N>> {
242 self.iter().flat_map(|tx| tx.transitions())
243 }
244
245 pub fn transition_ids(&self) -> impl '_ + Iterator<Item = &N::TransitionID> {
247 self.iter().flat_map(|tx| tx.transition_ids())
248 }
249
250 pub fn transition_public_keys(&self) -> impl '_ + Iterator<Item = &Group<N>> {
252 self.iter().flat_map(|tx| tx.transition_public_keys())
253 }
254
255 pub fn transition_commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
257 self.iter().flat_map(|tx| tx.transition_commitments())
258 }
259
260 pub fn tags(&self) -> impl '_ + Iterator<Item = &Field<N>> {
262 self.iter().flat_map(|tx| tx.tags())
263 }
264
265 pub fn input_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
267 self.iter().flat_map(|tx| tx.input_ids())
268 }
269
270 pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = &Field<N>> {
272 self.iter().flat_map(|tx| tx.serial_numbers())
273 }
274
275 pub fn output_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
277 self.iter().flat_map(|tx| tx.output_ids())
278 }
279
280 pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
282 self.iter().flat_map(|tx| tx.commitments())
283 }
284
285 pub fn records(&self) -> impl '_ + Iterator<Item = (&Field<N>, &Record<N, Ciphertext<N>>)> {
287 self.iter().flat_map(|tx| tx.records())
288 }
289
290 pub fn nonces(&self) -> impl '_ + Iterator<Item = &Group<N>> {
292 self.iter().flat_map(|tx| tx.nonces())
293 }
294
295 pub fn transaction_fee_amounts(&self) -> impl '_ + Iterator<Item = Result<U64<N>>> {
297 self.iter().map(|tx| tx.fee_amount())
298 }
299
300 pub fn finalize_operations(&self) -> impl '_ + Iterator<Item = &FinalizeOperation<N>> {
302 self.iter().flat_map(|tx| tx.finalize_operations())
303 }
304}
305
306impl<N: Network> IntoIterator for Transactions<N> {
307 type IntoIter = indexmap::map::IntoValues<N::TransactionID, Self::Item>;
308 type Item = ConfirmedTransaction<N>;
309
310 fn into_iter(self) -> Self::IntoIter {
312 self.transactions.into_values()
313 }
314}
315
316impl<N: Network> Transactions<N> {
317 pub fn into_transaction_ids(self) -> impl ExactSizeIterator<Item = N::TransactionID> {
319 self.transactions.into_keys()
320 }
321
322 pub fn into_deployments(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
324 self.into_iter().filter(|tx| tx.is_accepted() && tx.is_deploy())
325 }
326
327 pub fn into_executions(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
329 self.into_iter().filter(|tx| tx.is_accepted() && tx.is_execute())
330 }
331
332 pub fn into_transitions(self) -> impl Iterator<Item = Transition<N>> {
334 self.into_iter().flat_map(|tx| tx.into_transaction().into_transitions())
335 }
336
337 pub fn into_transition_ids(self) -> impl Iterator<Item = N::TransitionID> {
339 self.into_iter().flat_map(|tx| tx.into_transaction().into_transition_ids())
340 }
341
342 pub fn into_transition_public_keys(self) -> impl Iterator<Item = Group<N>> {
344 self.into_iter().flat_map(|tx| tx.into_transaction().into_transition_public_keys())
345 }
346
347 pub fn into_tags(self) -> impl Iterator<Item = Field<N>> {
349 self.into_iter().flat_map(|tx| tx.into_transaction().into_tags())
350 }
351
352 pub fn into_serial_numbers(self) -> impl Iterator<Item = Field<N>> {
354 self.into_iter().flat_map(|tx| tx.into_transaction().into_serial_numbers())
355 }
356
357 pub fn into_commitments(self) -> impl Iterator<Item = Field<N>> {
359 self.into_iter().flat_map(|tx| tx.into_transaction().into_commitments())
360 }
361
362 pub fn into_records(self) -> impl Iterator<Item = (Field<N>, Record<N, Ciphertext<N>>)> {
364 self.into_iter().flat_map(|tx| tx.into_transaction().into_records())
365 }
366
367 pub fn into_nonces(self) -> impl Iterator<Item = Group<N>> {
369 self.into_iter().flat_map(|tx| tx.into_transaction().into_nonces())
370 }
371}
372
373#[cfg(test)]
374pub mod test_helpers {
375 use super::*;
376
377 type CurrentNetwork = console::network::MainnetV0;
378
379 pub(crate) fn sample_block_transactions(rng: &mut TestRng) -> Transactions<CurrentNetwork> {
381 crate::test_helpers::sample_genesis_block(rng).transactions().clone()
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use snarkvm_ledger_narwhal_batch_header::BatchHeader;
389
390 type CurrentNetwork = console::network::MainnetV0;
391
392 #[test]
393 fn test_max_transmissions() {
394 let max_transmissions_per_block = BatchHeader::<CurrentNetwork>::MAX_TRANSMISSIONS_PER_BATCH
396 * BatchHeader::<CurrentNetwork>::MAX_GC_ROUNDS
397 * CurrentNetwork::LATEST_MAX_CERTIFICATES() as usize;
398
399 assert!(
403 max_transmissions_per_block <= Transactions::<CurrentNetwork>::MAX_TRANSACTIONS,
404 "The maximum number of transmissions in a block is too large"
405 );
406 }
407}