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 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 subdag.check_certificate_order(header.height())?;
203 Self::check_subdag_transmissions(
205 subdag,
206 &solutions,
207 &aborted_solution_ids,
208 &transactions,
209 &aborted_transaction_ids,
210 )?;
211 }
212 }
213
214 if header.solutions_root() != solutions.to_solutions_root()? {
216 bail!("The solutions root in the block does not correspond to the solutions");
217 }
218
219 let subdag_root = match &authority {
221 Authority::Beacon(_) => Field::<N>::zero(),
222 Authority::Quorum(subdag) => subdag.to_subdag_root()?,
223 };
224 if header.subdag_root() != subdag_root {
225 bail!("The subdag root in the block does not correspond to the authority");
226 }
227
228 Self::from_unchecked(
230 block_hash.into(),
231 previous_hash,
232 header,
233 authority,
234 ratifications,
235 solutions,
236 aborted_solution_ids,
237 transactions,
238 aborted_transaction_ids,
239 )
240 }
241
242 pub fn from_unchecked(
248 block_hash: N::BlockHash,
249 previous_hash: N::BlockHash,
250 header: Header<N>,
251 authority: Authority<N>,
252 ratifications: Ratifications<N>,
253 solutions: Solutions<N>,
254 aborted_solution_ids: Vec<SolutionID<N>>,
255 transactions: Transactions<N>,
256 aborted_transaction_ids: Vec<N::TransactionID>,
257 ) -> Result<Self> {
258 Ok(Self {
260 block_hash,
261 previous_hash,
262 header,
263 authority,
264 ratifications,
265 solutions,
266 aborted_solution_ids,
267 transactions,
268 aborted_transaction_ids,
269 })
270 }
271}
272
273impl<N: Network> Block<N> {
274 pub const fn hash(&self) -> N::BlockHash {
276 self.block_hash
277 }
278
279 pub const fn previous_hash(&self) -> N::BlockHash {
281 self.previous_hash
282 }
283
284 pub const fn authority(&self) -> &Authority<N> {
286 &self.authority
287 }
288
289 pub const fn ratifications(&self) -> &Ratifications<N> {
291 &self.ratifications
292 }
293
294 pub const fn solutions(&self) -> &Solutions<N> {
296 &self.solutions
297 }
298
299 pub const fn aborted_solution_ids(&self) -> &Vec<SolutionID<N>> {
301 &self.aborted_solution_ids
302 }
303
304 pub const fn transactions(&self) -> &Transactions<N> {
306 &self.transactions
307 }
308
309 pub const fn aborted_transaction_ids(&self) -> &Vec<N::TransactionID> {
311 &self.aborted_transaction_ids
312 }
313}
314
315impl<N: Network> Block<N> {
316 pub const fn header(&self) -> &Header<N> {
318 &self.header
319 }
320
321 pub const fn previous_state_root(&self) -> N::StateRoot {
323 self.header.previous_state_root()
324 }
325
326 pub const fn transactions_root(&self) -> Field<N> {
328 self.header.transactions_root()
329 }
330
331 pub const fn finalize_root(&self) -> Field<N> {
333 self.header.finalize_root()
334 }
335
336 pub const fn ratifications_root(&self) -> Field<N> {
338 self.header.ratifications_root()
339 }
340
341 pub const fn solutions_root(&self) -> Field<N> {
343 self.header.solutions_root()
344 }
345
346 pub const fn metadata(&self) -> &Metadata<N> {
348 self.header.metadata()
349 }
350
351 pub const fn network(&self) -> u16 {
353 self.header.network()
354 }
355
356 pub const fn height(&self) -> u32 {
358 self.header.height()
359 }
360
361 pub const fn round(&self) -> u64 {
363 self.header.round()
364 }
365
366 pub const fn epoch_number(&self) -> u32 {
368 self.height() / N::NUM_BLOCKS_PER_EPOCH
369 }
370
371 pub const fn cumulative_weight(&self) -> u128 {
373 self.header.cumulative_weight()
374 }
375
376 pub const fn cumulative_proof_target(&self) -> u128 {
378 self.header.cumulative_proof_target()
379 }
380
381 pub const fn coinbase_target(&self) -> u64 {
383 self.header.coinbase_target()
384 }
385
386 pub const fn proof_target(&self) -> u64 {
388 self.header.proof_target()
389 }
390
391 pub const fn last_coinbase_target(&self) -> u64 {
393 self.header.last_coinbase_target()
394 }
395
396 pub const fn last_coinbase_timestamp(&self) -> i64 {
398 self.header.last_coinbase_timestamp()
399 }
400
401 pub const fn timestamp(&self) -> i64 {
403 self.header.timestamp()
404 }
405}
406
407impl<N: Network> Block<N> {
408 pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
410 self.transactions.contains_transition(transition_id)
411 }
412
413 pub fn contains_serial_number(&self, serial_number: &Field<N>) -> bool {
415 self.transactions.contains_serial_number(serial_number)
416 }
417
418 pub fn contains_commitment(&self, commitment: &Field<N>) -> bool {
420 self.transactions.contains_commitment(commitment)
421 }
422}
423
424impl<N: Network> Block<N> {
425 pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Option<&Solution<N>> {
427 self.solutions.as_ref().and_then(|solution| solution.get_solution(solution_id))
428 }
429
430 pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Option<&Transaction<N>> {
432 self.transactions.get(transaction_id).map(|t| t.deref())
433 }
434
435 pub fn get_confirmed_transaction(&self, transaction_id: &N::TransactionID) -> Option<&ConfirmedTransaction<N>> {
437 self.transactions.get(transaction_id)
438 }
439}
440
441impl<N: Network> Block<N> {
442 pub fn find_transaction_for_transition_id(&self, transition_id: &N::TransitionID) -> Option<&Transaction<N>> {
444 self.transactions.find_transaction_for_transition_id(transition_id)
445 }
446
447 pub fn find_transaction_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transaction<N>> {
449 self.transactions.find_transaction_for_serial_number(serial_number)
450 }
451
452 pub fn find_transaction_for_commitment(&self, commitment: &Field<N>) -> Option<&Transaction<N>> {
454 self.transactions.find_transaction_for_commitment(commitment)
455 }
456
457 pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
459 self.transactions.find_transition(transition_id)
460 }
461
462 pub fn find_transition_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transition<N>> {
464 self.transactions.find_transition_for_serial_number(serial_number)
465 }
466
467 pub fn find_transition_for_commitment(&self, commitment: &Field<N>) -> Option<&Transition<N>> {
469 self.transactions.find_transition_for_commitment(commitment)
470 }
471
472 pub fn find_record(&self, commitment: &Field<N>) -> Option<&Record<N, Ciphertext<N>>> {
474 self.transactions.find_record(commitment)
475 }
476}
477
478impl<N: Network> Block<N> {
479 pub fn solution_ids(&self) -> Option<impl '_ + Iterator<Item = &SolutionID<N>>> {
481 self.solutions.as_ref().map(|solution| solution.solution_ids())
482 }
483
484 pub fn transaction_ids(&self) -> impl '_ + Iterator<Item = &N::TransactionID> {
486 self.transactions.transaction_ids()
487 }
488
489 pub fn deployments(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
491 self.transactions.deployments()
492 }
493
494 pub fn executions(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
496 self.transactions.executions()
497 }
498
499 pub fn transitions(&self) -> impl '_ + Iterator<Item = &Transition<N>> {
501 self.transactions.transitions()
502 }
503
504 pub fn transition_ids(&self) -> impl '_ + Iterator<Item = &N::TransitionID> {
506 self.transactions.transition_ids()
507 }
508
509 pub fn transition_public_keys(&self) -> impl '_ + Iterator<Item = &Group<N>> {
511 self.transactions.transition_public_keys()
512 }
513
514 pub fn transition_commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
516 self.transactions.transition_commitments()
517 }
518
519 pub fn tags(&self) -> impl '_ + Iterator<Item = &Field<N>> {
521 self.transactions.tags()
522 }
523
524 pub fn input_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
526 self.transactions.input_ids()
527 }
528
529 pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = &Field<N>> {
531 self.transactions.serial_numbers()
532 }
533
534 pub fn output_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
536 self.transactions.output_ids()
537 }
538
539 pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
541 self.transactions.commitments()
542 }
543
544 pub fn records(&self) -> impl '_ + Iterator<Item = (&Field<N>, &Record<N, Ciphertext<N>>)> {
546 self.transactions.records()
547 }
548
549 pub fn nonces(&self) -> impl '_ + Iterator<Item = &Group<N>> {
551 self.transactions.nonces()
552 }
553
554 pub fn transaction_fee_amounts(&self) -> impl '_ + Iterator<Item = Result<U64<N>>> {
556 self.transactions.transaction_fee_amounts()
557 }
558}
559
560impl<N: Network> Block<N> {
561 pub fn into_transaction_ids(self) -> impl Iterator<Item = N::TransactionID> {
563 self.transactions.into_transaction_ids()
564 }
565
566 pub fn into_deployments(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
568 self.transactions.into_deployments()
569 }
570
571 pub fn into_executions(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
573 self.transactions.into_executions()
574 }
575
576 pub fn into_transitions(self) -> impl Iterator<Item = Transition<N>> {
578 self.transactions.into_transitions()
579 }
580
581 pub fn into_transition_ids(self) -> impl Iterator<Item = N::TransitionID> {
583 self.transactions.into_transition_ids()
584 }
585
586 pub fn into_transition_public_keys(self) -> impl Iterator<Item = Group<N>> {
588 self.transactions.into_transition_public_keys()
589 }
590
591 pub fn into_tags(self) -> impl Iterator<Item = Field<N>> {
593 self.transactions.into_tags()
594 }
595
596 pub fn into_serial_numbers(self) -> impl Iterator<Item = Field<N>> {
598 self.transactions.into_serial_numbers()
599 }
600
601 pub fn into_commitments(self) -> impl Iterator<Item = Field<N>> {
603 self.transactions.into_commitments()
604 }
605
606 pub fn into_records(self) -> impl Iterator<Item = (Field<N>, Record<N, Ciphertext<N>>)> {
608 self.transactions.into_records()
609 }
610
611 pub fn into_nonces(self) -> impl Iterator<Item = Group<N>> {
613 self.transactions.into_nonces()
614 }
615}
616
617#[cfg(test)]
618pub mod test_helpers {
619 use super::*;
620
621 use snarkvm_algorithms::snark::varuna::VarunaVersion;
622 use snarkvm_console::account::{Address, PrivateKey};
623 use snarkvm_ledger_query::Query;
624 use snarkvm_ledger_store::{BlockStore, helpers::memory::BlockMemory};
625 use snarkvm_synthesizer_process::Process;
626 use snarkvm_utilities::PrettyUnwrap;
627
628 use aleo_std::StorageMode;
629 use std::sync::OnceLock;
630
631 type CurrentNetwork = console::network::MainnetV0;
632 type CurrentAleo = snarkvm_circuit::network::AleoV0;
633
634 pub(crate) fn sample_genesis_block(rng: &mut TestRng) -> Block<CurrentNetwork> {
636 let (block, _, _) = sample_genesis_block_and_components(rng);
638 block
640 }
641
642 pub(crate) fn sample_genesis_block_and_transaction(
644 rng: &mut TestRng,
645 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>) {
646 let (block, transaction, _) = sample_genesis_block_and_components(rng);
648 (block, transaction)
650 }
651
652 pub(crate) fn sample_genesis_block_and_components(
654 rng: &mut TestRng,
655 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
656 static INSTANCE: OnceLock<(Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>)> =
657 OnceLock::new();
658 INSTANCE.get_or_init(|| sample_genesis_block_and_components_raw(rng)).clone()
659 }
660
661 fn sample_genesis_block_and_components_raw(
663 rng: &mut TestRng,
664 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
665 let private_key = PrivateKey::new(rng).unwrap();
667 let address = Address::<CurrentNetwork>::try_from(private_key).unwrap();
668
669 let locator = ("credits.aleo", "transfer_public_to_private");
671 let amount = 100_000_000u64;
673 let inputs = [address.to_string(), format!("{amount}_u64")];
675
676 let process = Process::load().unwrap();
678 let authorization =
680 process.authorize::<CurrentAleo, _>(&private_key, locator.0, locator.1, inputs.iter(), rng).unwrap();
681 let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
683
684 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
686
687 trace.prepare(&Query::from(block_store)).unwrap();
689 let execution = trace.prove_execution::<CurrentAleo, _>(locator.0, VarunaVersion::V1, rng).unwrap();
691 let execution = Execution::from_str(&execution.to_string()).unwrap();
694
695 let transaction = Transaction::from_execution(execution, None).unwrap();
697 let confirmed = ConfirmedTransaction::accepted_execute(0, transaction.clone(), vec![]).unwrap();
699 let transactions = Transactions::from_iter([confirmed]);
701
702 let ratifications = Ratifications::try_from(vec![]).unwrap();
704
705 let header = Header::genesis(&ratifications, &transactions, vec![]).unwrap();
707 let previous_hash = <CurrentNetwork as Network>::BlockHash::default();
709
710 let block = Block::new_beacon(
712 &private_key,
713 previous_hash,
714 header,
715 ratifications,
716 None.into(),
717 vec![],
718 transactions,
719 vec![],
720 rng,
721 )
722 .unwrap();
723 assert!(block.header().is_genesis().pretty_unwrap(), "Failed to initialize a genesis block");
724 (block, transaction, private_key)
726 }
727
728 pub(crate) fn sample_metadata() -> Metadata<CurrentNetwork> {
729 let network = CurrentNetwork::ID;
730 let round = u64::MAX;
731 let height = u32::MAX;
732 let cumulative_weight = u128::MAX - 1;
733 let cumulative_proof_target = u128::MAX - 1;
734 let coinbase_target = u64::MAX;
735 let proof_target = u64::MAX - 1;
736 let last_coinbase_target = u64::MAX;
737 let timestamp = i64::MAX - 1;
738 let last_coinbase_timestamp = timestamp - 1;
739
740 Metadata::new(
741 network,
742 round,
743 height,
744 cumulative_weight,
745 cumulative_proof_target,
746 coinbase_target,
747 proof_target,
748 last_coinbase_target,
749 last_coinbase_timestamp,
750 timestamp,
751 )
752 .pretty_unwrap()
753 }
754}
755
756#[cfg(test)]
757mod tests {
758 use super::*;
759
760 use console::network::MainnetV0;
761 use indexmap::IndexMap;
762 type CurrentNetwork = MainnetV0;
763
764 #[test]
765 fn test_find_transaction_for_transition_id() {
766 let rng = &mut TestRng::default();
767
768 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
769 let transactions = block.transactions();
770
771 let transitions = transaction.transitions().collect::<Vec<_>>();
773 assert!(!transitions.is_empty());
774
775 for transition in transitions {
777 assert_eq!(block.find_transaction_for_transition_id(transition.id()), Some(&transaction));
778 assert_eq!(transactions.find_transaction_for_transition_id(transition.id()), Some(&transaction));
779 }
780
781 for _ in 0..10 {
783 let transition_id = &rng.random();
784 assert_eq!(block.find_transaction_for_transition_id(transition_id), None);
785 assert_eq!(transactions.find_transaction_for_transition_id(transition_id), None);
786 }
787 }
788
789 #[test]
790 fn test_find_transaction_for_commitment() {
791 let rng = &mut TestRng::default();
792
793 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
794 let transactions = block.transactions();
795
796 let commitments = transaction.commitments().collect::<Vec<_>>();
798 assert!(!commitments.is_empty());
799
800 for commitment in commitments {
802 assert_eq!(block.find_transaction_for_commitment(commitment), Some(&transaction));
803 assert_eq!(transactions.find_transaction_for_commitment(commitment), Some(&transaction));
804 }
805
806 for _ in 0..10 {
808 let commitment = &rng.random();
809 assert_eq!(block.find_transaction_for_commitment(commitment), None);
810 assert_eq!(transactions.find_transaction_for_commitment(commitment), None);
811 }
812 }
813
814 #[test]
815 fn test_find_transition() {
816 let rng = &mut TestRng::default();
817
818 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
819 let transactions = block.transactions();
820
821 let transitions = transaction.transitions().collect::<Vec<_>>();
823 assert!(!transitions.is_empty());
824
825 for transition in transitions {
827 assert_eq!(block.find_transition(transition.id()), Some(transition));
828 assert_eq!(transactions.find_transition(transition.id()), Some(transition));
829 assert_eq!(transaction.find_transition(transition.id()), Some(transition));
830 }
831
832 for _ in 0..10 {
834 let transition_id = &rng.random();
835 assert_eq!(block.find_transition(transition_id), None);
836 assert_eq!(transactions.find_transition(transition_id), None);
837 assert_eq!(transaction.find_transition(transition_id), None);
838 }
839 }
840
841 #[test]
842 fn test_find_transition_for_commitment() {
843 let rng = &mut TestRng::default();
844
845 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
846 let transactions = block.transactions();
847
848 let transitions = transaction.transitions().collect::<Vec<_>>();
850 assert!(!transitions.is_empty());
851
852 for transition in transitions {
853 let commitments = transition.commitments().collect::<Vec<_>>();
855 assert!(!commitments.is_empty());
856
857 for commitment in commitments {
859 assert_eq!(block.find_transition_for_commitment(commitment), Some(transition));
860 assert_eq!(transactions.find_transition_for_commitment(commitment), Some(transition));
861 assert_eq!(transaction.find_transition_for_commitment(commitment), Some(transition));
862 }
863 }
864
865 for _ in 0..10 {
867 let commitment = &rng.random();
868 assert_eq!(block.find_transition_for_commitment(commitment), None);
869 assert_eq!(transactions.find_transition_for_commitment(commitment), None);
870 assert_eq!(transaction.find_transition_for_commitment(commitment), None);
871 }
872 }
873
874 #[test]
875 fn test_find_record() {
876 let rng = &mut TestRng::default();
877
878 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
879 let transactions = block.transactions();
880
881 let records = transaction.records().collect::<IndexMap<_, _>>();
883 assert!(!records.is_empty());
884
885 for (commitment, record) in records {
887 assert_eq!(block.find_record(commitment), Some(record));
888 assert_eq!(transactions.find_record(commitment), Some(record));
889 assert_eq!(transaction.find_record(commitment), Some(record));
890 }
891
892 for _ in 0..10 {
894 let commitment = &rng.random();
895 assert_eq!(block.find_record(commitment), None);
896 assert_eq!(transactions.find_record(commitment), None);
897 assert_eq!(transaction.find_record(commitment), None);
898 }
899 }
900
901 #[test]
902 fn test_serde_metadata() {
903 let metadata = crate::test_helpers::sample_metadata();
904 let json_metadata = serde_json::to_string(&metadata).unwrap();
905 let deserialized_metadata: Metadata<CurrentNetwork> = serde_json::from_str(&json_metadata).unwrap();
906 assert_eq!(metadata, deserialized_metadata);
907 }
908}