1#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![cfg_attr(test, allow(clippy::single_element_loop))]
20
21extern crate snarkvm_console as console;
22
23pub mod header;
24pub use header::*;
25
26mod helpers;
27pub use helpers::*;
28
29pub mod ratifications;
30pub use ratifications::*;
31
32pub mod ratify;
33pub use ratify::*;
34
35pub mod solutions;
36pub use solutions::*;
37
38pub mod transaction;
39pub use transaction::*;
40
41pub mod transactions;
42pub use transactions::*;
43
44pub mod transition;
45pub use transition::*;
46
47mod bytes;
48mod genesis;
49mod serialize;
50mod string;
51mod verify;
52
53use console::{
54 account::PrivateKey,
55 network::prelude::*,
56 program::{Ciphertext, Record},
57 types::{Field, Group, U64},
58};
59use snarkvm_ledger_authority::Authority;
60use snarkvm_ledger_committee::Committee;
61use snarkvm_ledger_narwhal_data::Data;
62use snarkvm_ledger_narwhal_subdag::Subdag;
63use snarkvm_ledger_narwhal_transmission_id::TransmissionID;
64use snarkvm_ledger_puzzle::{PuzzleSolutions, Solution, SolutionID};
65
66#[derive(Clone, PartialEq, Eq)]
67pub struct Block<N: Network> {
68 block_hash: N::BlockHash,
70 previous_hash: N::BlockHash,
72 header: Header<N>,
74 authority: Authority<N>,
76 ratifications: Ratifications<N>,
78 solutions: Solutions<N>,
80 aborted_solution_ids: Vec<SolutionID<N>>,
82 transactions: Transactions<N>,
84 aborted_transaction_ids: Vec<N::TransactionID>,
86}
87
88impl<N: Network> Block<N> {
89 pub fn new_beacon<R: Rng + CryptoRng>(
92 private_key: &PrivateKey<N>,
93 previous_hash: N::BlockHash,
94 header: Header<N>,
95 ratifications: Ratifications<N>,
96 solutions: Solutions<N>,
97 aborted_solution_ids: Vec<SolutionID<N>>,
98 transactions: Transactions<N>,
99 aborted_transaction_ids: Vec<N::TransactionID>,
100 rng: &mut R,
101 ) -> Result<Self> {
102 let block_hash = N::hash_bhp1024(&to_bits_le![previous_hash, header.to_root()?])?;
104 let authority = Authority::new_beacon(private_key, block_hash, rng)?;
106 Self::from(
108 previous_hash,
109 header,
110 authority,
111 ratifications,
112 solutions,
113 aborted_solution_ids,
114 transactions,
115 aborted_transaction_ids,
116 )
117 }
118
119 pub fn new_quorum(
122 previous_hash: N::BlockHash,
123 header: Header<N>,
124 subdag: Subdag<N>,
125 ratifications: Ratifications<N>,
126 solutions: Solutions<N>,
127 aborted_solution_ids: Vec<SolutionID<N>>,
128 transactions: Transactions<N>,
129 aborted_transaction_ids: Vec<N::TransactionID>,
130 ) -> Result<Self> {
131 let authority = Authority::new_quorum(subdag);
133 Self::from(
135 previous_hash,
136 header,
137 authority,
138 ratifications,
139 solutions,
140 aborted_solution_ids,
141 transactions,
142 aborted_transaction_ids,
143 )
144 }
145
146 pub fn from(
149 previous_hash: N::BlockHash,
150 header: Header<N>,
151 authority: Authority<N>,
152 ratifications: Ratifications<N>,
153 solutions: Solutions<N>,
154 aborted_solution_ids: Vec<SolutionID<N>>,
155 transactions: Transactions<N>,
156 aborted_transaction_ids: Vec<N::TransactionID>,
157 ) -> Result<Self> {
158 if aborted_solution_ids.len() > Solutions::<N>::max_aborted_solutions()? {
160 bail!(
161 "Cannot initialize a block with {} aborted solutions IDs which exceed the maximum {}",
162 aborted_solution_ids.len(),
163 Solutions::<N>::max_aborted_solutions()?
164 );
165 }
166
167 if transactions.len() > Transactions::<N>::MAX_TRANSACTIONS {
169 bail!(
170 "Cannot initialize a block with {} confirmed transactions which exceed the maximum {}",
171 transactions.len(),
172 Transactions::<N>::MAX_TRANSACTIONS
173 );
174 }
175
176 if aborted_transaction_ids.len() > Transactions::<N>::max_aborted_transactions()? {
182 bail!(
183 "Cannot initialize a block with {} aborted transaction IDs which exceed the maximum {}",
184 aborted_transaction_ids.len(),
185 Transactions::<N>::max_aborted_transactions()?
186 );
187 }
188
189 let block_hash = N::hash_bhp1024(&to_bits_le![previous_hash, header.to_root()?])?;
191
192 match &authority {
194 Authority::Beacon(signature) => {
195 let address = signature.to_address();
197 ensure!(signature.verify(&address, &[block_hash]), "Invalid signature for block {}", header.height());
199 }
200 Authority::Quorum(subdag) => {
201 Self::check_subdag_transmissions(
203 subdag,
204 &solutions,
205 &aborted_solution_ids,
206 &transactions,
207 &aborted_transaction_ids,
208 )?;
209 }
210 }
211
212 if header.solutions_root() != solutions.to_solutions_root()? {
214 bail!("The solutions root in the block does not correspond to the solutions");
215 }
216
217 let subdag_root = match &authority {
219 Authority::Beacon(_) => Field::<N>::zero(),
220 Authority::Quorum(subdag) => subdag.to_subdag_root()?,
221 };
222 if header.subdag_root() != subdag_root {
223 bail!("The subdag root in the block does not correspond to the authority");
224 }
225
226 Self::from_unchecked(
228 block_hash.into(),
229 previous_hash,
230 header,
231 authority,
232 ratifications,
233 solutions,
234 aborted_solution_ids,
235 transactions,
236 aborted_transaction_ids,
237 )
238 }
239
240 fn from_unchecked(
244 block_hash: N::BlockHash,
245 previous_hash: N::BlockHash,
246 header: Header<N>,
247 authority: Authority<N>,
248 ratifications: Ratifications<N>,
249 solutions: Solutions<N>,
250 aborted_solution_ids: Vec<SolutionID<N>>,
251 transactions: Transactions<N>,
252 aborted_transaction_ids: Vec<N::TransactionID>,
253 ) -> Result<Self> {
254 Ok(Self {
256 block_hash,
257 previous_hash,
258 header,
259 authority,
260 ratifications,
261 solutions,
262 aborted_solution_ids,
263 transactions,
264 aborted_transaction_ids,
265 })
266 }
267}
268
269impl<N: Network> Block<N> {
270 pub const fn hash(&self) -> N::BlockHash {
272 self.block_hash
273 }
274
275 pub const fn previous_hash(&self) -> N::BlockHash {
277 self.previous_hash
278 }
279
280 pub const fn authority(&self) -> &Authority<N> {
282 &self.authority
283 }
284
285 pub const fn ratifications(&self) -> &Ratifications<N> {
287 &self.ratifications
288 }
289
290 pub const fn solutions(&self) -> &Solutions<N> {
292 &self.solutions
293 }
294
295 pub const fn aborted_solution_ids(&self) -> &Vec<SolutionID<N>> {
297 &self.aborted_solution_ids
298 }
299
300 pub const fn transactions(&self) -> &Transactions<N> {
302 &self.transactions
303 }
304
305 pub const fn aborted_transaction_ids(&self) -> &Vec<N::TransactionID> {
307 &self.aborted_transaction_ids
308 }
309}
310
311impl<N: Network> Block<N> {
312 pub const fn header(&self) -> &Header<N> {
314 &self.header
315 }
316
317 pub const fn previous_state_root(&self) -> N::StateRoot {
319 self.header.previous_state_root()
320 }
321
322 pub const fn transactions_root(&self) -> Field<N> {
324 self.header.transactions_root()
325 }
326
327 pub const fn finalize_root(&self) -> Field<N> {
329 self.header.finalize_root()
330 }
331
332 pub const fn ratifications_root(&self) -> Field<N> {
334 self.header.ratifications_root()
335 }
336
337 pub const fn solutions_root(&self) -> Field<N> {
339 self.header.solutions_root()
340 }
341
342 pub const fn metadata(&self) -> &Metadata<N> {
344 self.header.metadata()
345 }
346
347 pub const fn network(&self) -> u16 {
349 self.header.network()
350 }
351
352 pub const fn height(&self) -> u32 {
354 self.header.height()
355 }
356
357 pub const fn round(&self) -> u64 {
359 self.header.round()
360 }
361
362 pub const fn epoch_number(&self) -> u32 {
364 self.height() / N::NUM_BLOCKS_PER_EPOCH
365 }
366
367 pub const fn cumulative_weight(&self) -> u128 {
369 self.header.cumulative_weight()
370 }
371
372 pub const fn cumulative_proof_target(&self) -> u128 {
374 self.header.cumulative_proof_target()
375 }
376
377 pub const fn coinbase_target(&self) -> u64 {
379 self.header.coinbase_target()
380 }
381
382 pub const fn proof_target(&self) -> u64 {
384 self.header.proof_target()
385 }
386
387 pub const fn last_coinbase_target(&self) -> u64 {
389 self.header.last_coinbase_target()
390 }
391
392 pub const fn last_coinbase_timestamp(&self) -> i64 {
394 self.header.last_coinbase_timestamp()
395 }
396
397 pub const fn timestamp(&self) -> i64 {
399 self.header.timestamp()
400 }
401}
402
403impl<N: Network> Block<N> {
404 pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
406 self.transactions.contains_transition(transition_id)
407 }
408
409 pub fn contains_serial_number(&self, serial_number: &Field<N>) -> bool {
411 self.transactions.contains_serial_number(serial_number)
412 }
413
414 pub fn contains_commitment(&self, commitment: &Field<N>) -> bool {
416 self.transactions.contains_commitment(commitment)
417 }
418}
419
420impl<N: Network> Block<N> {
421 pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Option<&Solution<N>> {
423 self.solutions.as_ref().and_then(|solution| solution.get_solution(solution_id))
424 }
425
426 pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Option<&Transaction<N>> {
428 self.transactions.get(transaction_id).map(|t| t.deref())
429 }
430
431 pub fn get_confirmed_transaction(&self, transaction_id: &N::TransactionID) -> Option<&ConfirmedTransaction<N>> {
433 self.transactions.get(transaction_id)
434 }
435}
436
437impl<N: Network> Block<N> {
438 pub fn find_transaction_for_transition_id(&self, transition_id: &N::TransitionID) -> Option<&Transaction<N>> {
440 self.transactions.find_transaction_for_transition_id(transition_id)
441 }
442
443 pub fn find_transaction_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transaction<N>> {
445 self.transactions.find_transaction_for_serial_number(serial_number)
446 }
447
448 pub fn find_transaction_for_commitment(&self, commitment: &Field<N>) -> Option<&Transaction<N>> {
450 self.transactions.find_transaction_for_commitment(commitment)
451 }
452
453 pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
455 self.transactions.find_transition(transition_id)
456 }
457
458 pub fn find_transition_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transition<N>> {
460 self.transactions.find_transition_for_serial_number(serial_number)
461 }
462
463 pub fn find_transition_for_commitment(&self, commitment: &Field<N>) -> Option<&Transition<N>> {
465 self.transactions.find_transition_for_commitment(commitment)
466 }
467
468 pub fn find_record(&self, commitment: &Field<N>) -> Option<&Record<N, Ciphertext<N>>> {
470 self.transactions.find_record(commitment)
471 }
472}
473
474impl<N: Network> Block<N> {
475 pub fn solution_ids(&self) -> Option<impl '_ + Iterator<Item = &SolutionID<N>>> {
477 self.solutions.as_ref().map(|solution| solution.solution_ids())
478 }
479
480 pub fn transaction_ids(&self) -> impl '_ + Iterator<Item = &N::TransactionID> {
482 self.transactions.transaction_ids()
483 }
484
485 pub fn deployments(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
487 self.transactions.deployments()
488 }
489
490 pub fn executions(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
492 self.transactions.executions()
493 }
494
495 pub fn transitions(&self) -> impl '_ + Iterator<Item = &Transition<N>> {
497 self.transactions.transitions()
498 }
499
500 pub fn transition_ids(&self) -> impl '_ + Iterator<Item = &N::TransitionID> {
502 self.transactions.transition_ids()
503 }
504
505 pub fn transition_public_keys(&self) -> impl '_ + Iterator<Item = &Group<N>> {
507 self.transactions.transition_public_keys()
508 }
509
510 pub fn transition_commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
512 self.transactions.transition_commitments()
513 }
514
515 pub fn tags(&self) -> impl '_ + Iterator<Item = &Field<N>> {
517 self.transactions.tags()
518 }
519
520 pub fn input_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
522 self.transactions.input_ids()
523 }
524
525 pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = &Field<N>> {
527 self.transactions.serial_numbers()
528 }
529
530 pub fn output_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
532 self.transactions.output_ids()
533 }
534
535 pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
537 self.transactions.commitments()
538 }
539
540 pub fn records(&self) -> impl '_ + Iterator<Item = (&Field<N>, &Record<N, Ciphertext<N>>)> {
542 self.transactions.records()
543 }
544
545 pub fn nonces(&self) -> impl '_ + Iterator<Item = &Group<N>> {
547 self.transactions.nonces()
548 }
549
550 pub fn transaction_fee_amounts(&self) -> impl '_ + Iterator<Item = Result<U64<N>>> {
552 self.transactions.transaction_fee_amounts()
553 }
554}
555
556impl<N: Network> Block<N> {
557 pub fn into_transaction_ids(self) -> impl Iterator<Item = N::TransactionID> {
559 self.transactions.into_transaction_ids()
560 }
561
562 pub fn into_deployments(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
564 self.transactions.into_deployments()
565 }
566
567 pub fn into_executions(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
569 self.transactions.into_executions()
570 }
571
572 pub fn into_transitions(self) -> impl Iterator<Item = Transition<N>> {
574 self.transactions.into_transitions()
575 }
576
577 pub fn into_transition_ids(self) -> impl Iterator<Item = N::TransitionID> {
579 self.transactions.into_transition_ids()
580 }
581
582 pub fn into_transition_public_keys(self) -> impl Iterator<Item = Group<N>> {
584 self.transactions.into_transition_public_keys()
585 }
586
587 pub fn into_tags(self) -> impl Iterator<Item = Field<N>> {
589 self.transactions.into_tags()
590 }
591
592 pub fn into_serial_numbers(self) -> impl Iterator<Item = Field<N>> {
594 self.transactions.into_serial_numbers()
595 }
596
597 pub fn into_commitments(self) -> impl Iterator<Item = Field<N>> {
599 self.transactions.into_commitments()
600 }
601
602 pub fn into_records(self) -> impl Iterator<Item = (Field<N>, Record<N, Ciphertext<N>>)> {
604 self.transactions.into_records()
605 }
606
607 pub fn into_nonces(self) -> impl Iterator<Item = Group<N>> {
609 self.transactions.into_nonces()
610 }
611}
612
613#[cfg(test)]
614pub mod test_helpers {
615 use super::*;
616 use console::account::{Address, PrivateKey};
617 use snarkvm_algorithms::snark::varuna::VarunaVersion;
618 use snarkvm_ledger_query::Query;
619 use snarkvm_ledger_store::{BlockStore, helpers::memory::BlockMemory};
620 use snarkvm_synthesizer_process::Process;
621
622 use aleo_std::StorageMode;
623 use std::sync::OnceLock;
624
625 type CurrentNetwork = console::network::MainnetV0;
626 type CurrentAleo = snarkvm_circuit::network::AleoV0;
627
628 pub(crate) fn sample_genesis_block(rng: &mut TestRng) -> Block<CurrentNetwork> {
630 let (block, _, _) = sample_genesis_block_and_components(rng);
632 block
634 }
635
636 pub(crate) fn sample_genesis_block_and_transaction(
638 rng: &mut TestRng,
639 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>) {
640 let (block, transaction, _) = sample_genesis_block_and_components(rng);
642 (block, transaction)
644 }
645
646 pub(crate) fn sample_genesis_block_and_components(
648 rng: &mut TestRng,
649 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
650 static INSTANCE: OnceLock<(Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>)> =
651 OnceLock::new();
652 INSTANCE.get_or_init(|| sample_genesis_block_and_components_raw(rng)).clone()
653 }
654
655 fn sample_genesis_block_and_components_raw(
657 rng: &mut TestRng,
658 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
659 let private_key = PrivateKey::new(rng).unwrap();
661 let address = Address::<CurrentNetwork>::try_from(private_key).unwrap();
662
663 let locator = ("credits.aleo", "transfer_public_to_private");
665 let amount = 100_000_000u64;
667 let inputs = [address.to_string(), format!("{amount}_u64")];
669
670 let process = Process::load().unwrap();
672 let authorization =
674 process.authorize::<CurrentAleo, _>(&private_key, locator.0, locator.1, inputs.iter(), rng).unwrap();
675 let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
677
678 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
680
681 trace.prepare(&Query::from(block_store)).unwrap();
683 let execution = trace.prove_execution::<CurrentAleo, _>(locator.0, VarunaVersion::V1, rng).unwrap();
685 let execution = Execution::from_str(&execution.to_string()).unwrap();
688
689 let transaction = Transaction::from_execution(execution, None).unwrap();
691 let confirmed = ConfirmedTransaction::accepted_execute(0, transaction.clone(), vec![]).unwrap();
693 let transactions = Transactions::from_iter([confirmed]);
695
696 let ratifications = Ratifications::try_from(vec![]).unwrap();
698
699 let header = Header::genesis(&ratifications, &transactions, vec![]).unwrap();
701 let previous_hash = <CurrentNetwork as Network>::BlockHash::default();
703
704 let block = Block::new_beacon(
706 &private_key,
707 previous_hash,
708 header,
709 ratifications,
710 None.into(),
711 vec![],
712 transactions,
713 vec![],
714 rng,
715 )
716 .unwrap();
717 assert!(block.header().is_genesis(), "Failed to initialize a genesis block");
718 (block, transaction, private_key)
720 }
721
722 pub(crate) fn sample_metadata() -> Metadata<CurrentNetwork> {
723 let network = CurrentNetwork::ID;
724 let round = u64::MAX;
725 let height = u32::MAX;
726 let cumulative_weight = u128::MAX - 1;
727 let cumulative_proof_target = u128::MAX - 1;
728 let coinbase_target = u64::MAX;
729 let proof_target = u64::MAX - 1;
730 let last_coinbase_target = u64::MAX;
731 let timestamp = i64::MAX - 1;
732 let last_coinbase_timestamp = timestamp - 1;
733 Metadata::new(
734 network,
735 round,
736 height,
737 cumulative_weight,
738 cumulative_proof_target,
739 coinbase_target,
740 proof_target,
741 last_coinbase_target,
742 last_coinbase_timestamp,
743 timestamp,
744 )
745 .unwrap()
746 }
747}
748
749#[cfg(test)]
750mod tests {
751 use super::*;
752
753 use console::network::MainnetV0;
754 use indexmap::IndexMap;
755 type CurrentNetwork = MainnetV0;
756
757 #[test]
758 fn test_find_transaction_for_transition_id() {
759 let rng = &mut TestRng::default();
760
761 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
762 let transactions = block.transactions();
763
764 let transitions = transaction.transitions().collect::<Vec<_>>();
766 assert!(!transitions.is_empty());
767
768 for transition in transitions {
770 assert_eq!(block.find_transaction_for_transition_id(transition.id()), Some(&transaction));
771 assert_eq!(transactions.find_transaction_for_transition_id(transition.id()), Some(&transaction));
772 }
773
774 for _ in 0..10 {
776 let transition_id = &rng.r#gen();
777 assert_eq!(block.find_transaction_for_transition_id(transition_id), None);
778 assert_eq!(transactions.find_transaction_for_transition_id(transition_id), None);
779 }
780 }
781
782 #[test]
783 fn test_find_transaction_for_commitment() {
784 let rng = &mut TestRng::default();
785
786 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
787 let transactions = block.transactions();
788
789 let commitments = transaction.commitments().collect::<Vec<_>>();
791 assert!(!commitments.is_empty());
792
793 for commitment in commitments {
795 assert_eq!(block.find_transaction_for_commitment(commitment), Some(&transaction));
796 assert_eq!(transactions.find_transaction_for_commitment(commitment), Some(&transaction));
797 }
798
799 for _ in 0..10 {
801 let commitment = &rng.r#gen();
802 assert_eq!(block.find_transaction_for_commitment(commitment), None);
803 assert_eq!(transactions.find_transaction_for_commitment(commitment), None);
804 }
805 }
806
807 #[test]
808 fn test_find_transition() {
809 let rng = &mut TestRng::default();
810
811 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
812 let transactions = block.transactions();
813
814 let transitions = transaction.transitions().collect::<Vec<_>>();
816 assert!(!transitions.is_empty());
817
818 for transition in transitions {
820 assert_eq!(block.find_transition(transition.id()), Some(transition));
821 assert_eq!(transactions.find_transition(transition.id()), Some(transition));
822 assert_eq!(transaction.find_transition(transition.id()), Some(transition));
823 }
824
825 for _ in 0..10 {
827 let transition_id = &rng.r#gen();
828 assert_eq!(block.find_transition(transition_id), None);
829 assert_eq!(transactions.find_transition(transition_id), None);
830 assert_eq!(transaction.find_transition(transition_id), None);
831 }
832 }
833
834 #[test]
835 fn test_find_transition_for_commitment() {
836 let rng = &mut TestRng::default();
837
838 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
839 let transactions = block.transactions();
840
841 let transitions = transaction.transitions().collect::<Vec<_>>();
843 assert!(!transitions.is_empty());
844
845 for transition in transitions {
846 let commitments = transition.commitments().collect::<Vec<_>>();
848 assert!(!commitments.is_empty());
849
850 for commitment in commitments {
852 assert_eq!(block.find_transition_for_commitment(commitment), Some(transition));
853 assert_eq!(transactions.find_transition_for_commitment(commitment), Some(transition));
854 assert_eq!(transaction.find_transition_for_commitment(commitment), Some(transition));
855 }
856 }
857
858 for _ in 0..10 {
860 let commitment = &rng.r#gen();
861 assert_eq!(block.find_transition_for_commitment(commitment), None);
862 assert_eq!(transactions.find_transition_for_commitment(commitment), None);
863 assert_eq!(transaction.find_transition_for_commitment(commitment), None);
864 }
865 }
866
867 #[test]
868 fn test_find_record() {
869 let rng = &mut TestRng::default();
870
871 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
872 let transactions = block.transactions();
873
874 let records = transaction.records().collect::<IndexMap<_, _>>();
876 assert!(!records.is_empty());
877
878 for (commitment, record) in records {
880 assert_eq!(block.find_record(commitment), Some(record));
881 assert_eq!(transactions.find_record(commitment), Some(record));
882 assert_eq!(transaction.find_record(commitment), Some(record));
883 }
884
885 for _ in 0..10 {
887 let commitment = &rng.r#gen();
888 assert_eq!(block.find_record(commitment), None);
889 assert_eq!(transactions.find_record(commitment), None);
890 assert_eq!(transaction.find_record(commitment), None);
891 }
892 }
893
894 #[test]
895 fn test_serde_metadata() {
896 let metadata = crate::test_helpers::sample_metadata();
897 let json_metadata = serde_json::to_string(&metadata).unwrap();
898 let deserialized_metadata: Metadata<CurrentNetwork> = serde_json::from_str(&json_metadata).unwrap();
899 assert_eq!(metadata, deserialized_metadata);
900 }
901}