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 pub fn from_unchecked(
246 block_hash: N::BlockHash,
247 previous_hash: N::BlockHash,
248 header: Header<N>,
249 authority: Authority<N>,
250 ratifications: Ratifications<N>,
251 solutions: Solutions<N>,
252 aborted_solution_ids: Vec<SolutionID<N>>,
253 transactions: Transactions<N>,
254 aborted_transaction_ids: Vec<N::TransactionID>,
255 ) -> Result<Self> {
256 Ok(Self {
258 block_hash,
259 previous_hash,
260 header,
261 authority,
262 ratifications,
263 solutions,
264 aborted_solution_ids,
265 transactions,
266 aborted_transaction_ids,
267 })
268 }
269}
270
271impl<N: Network> Block<N> {
272 pub const fn hash(&self) -> N::BlockHash {
274 self.block_hash
275 }
276
277 pub const fn previous_hash(&self) -> N::BlockHash {
279 self.previous_hash
280 }
281
282 pub const fn authority(&self) -> &Authority<N> {
284 &self.authority
285 }
286
287 pub const fn ratifications(&self) -> &Ratifications<N> {
289 &self.ratifications
290 }
291
292 pub const fn solutions(&self) -> &Solutions<N> {
294 &self.solutions
295 }
296
297 pub const fn aborted_solution_ids(&self) -> &Vec<SolutionID<N>> {
299 &self.aborted_solution_ids
300 }
301
302 pub const fn transactions(&self) -> &Transactions<N> {
304 &self.transactions
305 }
306
307 pub const fn aborted_transaction_ids(&self) -> &Vec<N::TransactionID> {
309 &self.aborted_transaction_ids
310 }
311}
312
313impl<N: Network> Block<N> {
314 pub const fn header(&self) -> &Header<N> {
316 &self.header
317 }
318
319 pub const fn previous_state_root(&self) -> N::StateRoot {
321 self.header.previous_state_root()
322 }
323
324 pub const fn transactions_root(&self) -> Field<N> {
326 self.header.transactions_root()
327 }
328
329 pub const fn finalize_root(&self) -> Field<N> {
331 self.header.finalize_root()
332 }
333
334 pub const fn ratifications_root(&self) -> Field<N> {
336 self.header.ratifications_root()
337 }
338
339 pub const fn solutions_root(&self) -> Field<N> {
341 self.header.solutions_root()
342 }
343
344 pub const fn metadata(&self) -> &Metadata<N> {
346 self.header.metadata()
347 }
348
349 pub const fn network(&self) -> u16 {
351 self.header.network()
352 }
353
354 pub const fn height(&self) -> u32 {
356 self.header.height()
357 }
358
359 pub const fn round(&self) -> u64 {
361 self.header.round()
362 }
363
364 pub const fn epoch_number(&self) -> u32 {
366 self.height() / N::NUM_BLOCKS_PER_EPOCH
367 }
368
369 pub const fn cumulative_weight(&self) -> u128 {
371 self.header.cumulative_weight()
372 }
373
374 pub const fn cumulative_proof_target(&self) -> u128 {
376 self.header.cumulative_proof_target()
377 }
378
379 pub const fn coinbase_target(&self) -> u64 {
381 self.header.coinbase_target()
382 }
383
384 pub const fn proof_target(&self) -> u64 {
386 self.header.proof_target()
387 }
388
389 pub const fn last_coinbase_target(&self) -> u64 {
391 self.header.last_coinbase_target()
392 }
393
394 pub const fn last_coinbase_timestamp(&self) -> i64 {
396 self.header.last_coinbase_timestamp()
397 }
398
399 pub const fn timestamp(&self) -> i64 {
401 self.header.timestamp()
402 }
403}
404
405impl<N: Network> Block<N> {
406 pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
408 self.transactions.contains_transition(transition_id)
409 }
410
411 pub fn contains_serial_number(&self, serial_number: &Field<N>) -> bool {
413 self.transactions.contains_serial_number(serial_number)
414 }
415
416 pub fn contains_commitment(&self, commitment: &Field<N>) -> bool {
418 self.transactions.contains_commitment(commitment)
419 }
420}
421
422impl<N: Network> Block<N> {
423 pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Option<&Solution<N>> {
425 self.solutions.as_ref().and_then(|solution| solution.get_solution(solution_id))
426 }
427
428 pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Option<&Transaction<N>> {
430 self.transactions.get(transaction_id).map(|t| t.deref())
431 }
432
433 pub fn get_confirmed_transaction(&self, transaction_id: &N::TransactionID) -> Option<&ConfirmedTransaction<N>> {
435 self.transactions.get(transaction_id)
436 }
437}
438
439impl<N: Network> Block<N> {
440 pub fn find_transaction_for_transition_id(&self, transition_id: &N::TransitionID) -> Option<&Transaction<N>> {
442 self.transactions.find_transaction_for_transition_id(transition_id)
443 }
444
445 pub fn find_transaction_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transaction<N>> {
447 self.transactions.find_transaction_for_serial_number(serial_number)
448 }
449
450 pub fn find_transaction_for_commitment(&self, commitment: &Field<N>) -> Option<&Transaction<N>> {
452 self.transactions.find_transaction_for_commitment(commitment)
453 }
454
455 pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
457 self.transactions.find_transition(transition_id)
458 }
459
460 pub fn find_transition_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transition<N>> {
462 self.transactions.find_transition_for_serial_number(serial_number)
463 }
464
465 pub fn find_transition_for_commitment(&self, commitment: &Field<N>) -> Option<&Transition<N>> {
467 self.transactions.find_transition_for_commitment(commitment)
468 }
469
470 pub fn find_record(&self, commitment: &Field<N>) -> Option<&Record<N, Ciphertext<N>>> {
472 self.transactions.find_record(commitment)
473 }
474}
475
476impl<N: Network> Block<N> {
477 pub fn solution_ids(&self) -> Option<impl '_ + Iterator<Item = &SolutionID<N>>> {
479 self.solutions.as_ref().map(|solution| solution.solution_ids())
480 }
481
482 pub fn transaction_ids(&self) -> impl '_ + Iterator<Item = &N::TransactionID> {
484 self.transactions.transaction_ids()
485 }
486
487 pub fn deployments(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
489 self.transactions.deployments()
490 }
491
492 pub fn executions(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
494 self.transactions.executions()
495 }
496
497 pub fn transitions(&self) -> impl '_ + Iterator<Item = &Transition<N>> {
499 self.transactions.transitions()
500 }
501
502 pub fn transition_ids(&self) -> impl '_ + Iterator<Item = &N::TransitionID> {
504 self.transactions.transition_ids()
505 }
506
507 pub fn transition_public_keys(&self) -> impl '_ + Iterator<Item = &Group<N>> {
509 self.transactions.transition_public_keys()
510 }
511
512 pub fn transition_commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
514 self.transactions.transition_commitments()
515 }
516
517 pub fn tags(&self) -> impl '_ + Iterator<Item = &Field<N>> {
519 self.transactions.tags()
520 }
521
522 pub fn input_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
524 self.transactions.input_ids()
525 }
526
527 pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = &Field<N>> {
529 self.transactions.serial_numbers()
530 }
531
532 pub fn output_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
534 self.transactions.output_ids()
535 }
536
537 pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
539 self.transactions.commitments()
540 }
541
542 pub fn records(&self) -> impl '_ + Iterator<Item = (&Field<N>, &Record<N, Ciphertext<N>>)> {
544 self.transactions.records()
545 }
546
547 pub fn nonces(&self) -> impl '_ + Iterator<Item = &Group<N>> {
549 self.transactions.nonces()
550 }
551
552 pub fn transaction_fee_amounts(&self) -> impl '_ + Iterator<Item = Result<U64<N>>> {
554 self.transactions.transaction_fee_amounts()
555 }
556}
557
558impl<N: Network> Block<N> {
559 pub fn into_transaction_ids(self) -> impl Iterator<Item = N::TransactionID> {
561 self.transactions.into_transaction_ids()
562 }
563
564 pub fn into_deployments(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
566 self.transactions.into_deployments()
567 }
568
569 pub fn into_executions(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
571 self.transactions.into_executions()
572 }
573
574 pub fn into_transitions(self) -> impl Iterator<Item = Transition<N>> {
576 self.transactions.into_transitions()
577 }
578
579 pub fn into_transition_ids(self) -> impl Iterator<Item = N::TransitionID> {
581 self.transactions.into_transition_ids()
582 }
583
584 pub fn into_transition_public_keys(self) -> impl Iterator<Item = Group<N>> {
586 self.transactions.into_transition_public_keys()
587 }
588
589 pub fn into_tags(self) -> impl Iterator<Item = Field<N>> {
591 self.transactions.into_tags()
592 }
593
594 pub fn into_serial_numbers(self) -> impl Iterator<Item = Field<N>> {
596 self.transactions.into_serial_numbers()
597 }
598
599 pub fn into_commitments(self) -> impl Iterator<Item = Field<N>> {
601 self.transactions.into_commitments()
602 }
603
604 pub fn into_records(self) -> impl Iterator<Item = (Field<N>, Record<N, Ciphertext<N>>)> {
606 self.transactions.into_records()
607 }
608
609 pub fn into_nonces(self) -> impl Iterator<Item = Group<N>> {
611 self.transactions.into_nonces()
612 }
613}
614
615#[cfg(test)]
616pub mod test_helpers {
617 use super::*;
618
619 use snarkvm_algorithms::snark::varuna::VarunaVersion;
620 use snarkvm_console::account::{Address, PrivateKey};
621 use snarkvm_ledger_query::Query;
622 use snarkvm_ledger_store::{BlockStore, helpers::memory::BlockMemory};
623 use snarkvm_synthesizer_process::Process;
624 use snarkvm_utilities::PrettyUnwrap;
625
626 use aleo_std::StorageMode;
627 use std::sync::OnceLock;
628
629 type CurrentNetwork = console::network::MainnetV0;
630 type CurrentAleo = snarkvm_circuit::network::AleoV0;
631
632 pub(crate) fn sample_genesis_block(rng: &mut TestRng) -> Block<CurrentNetwork> {
634 let (block, _, _) = sample_genesis_block_and_components(rng);
636 block
638 }
639
640 pub(crate) fn sample_genesis_block_and_transaction(
642 rng: &mut TestRng,
643 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>) {
644 let (block, transaction, _) = sample_genesis_block_and_components(rng);
646 (block, transaction)
648 }
649
650 pub(crate) fn sample_genesis_block_and_components(
652 rng: &mut TestRng,
653 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
654 static INSTANCE: OnceLock<(Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>)> =
655 OnceLock::new();
656 INSTANCE.get_or_init(|| sample_genesis_block_and_components_raw(rng)).clone()
657 }
658
659 fn sample_genesis_block_and_components_raw(
661 rng: &mut TestRng,
662 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
663 let private_key = PrivateKey::new(rng).unwrap();
665 let address = Address::<CurrentNetwork>::try_from(private_key).unwrap();
666
667 let locator = ("credits.aleo", "transfer_public_to_private");
669 let amount = 100_000_000u64;
671 let inputs = [address.to_string(), format!("{amount}_u64")];
673
674 let process = Process::load().unwrap();
676 let authorization =
678 process.authorize::<CurrentAleo, _>(&private_key, locator.0, locator.1, inputs.iter(), rng).unwrap();
679 let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
681
682 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
684
685 trace.prepare(&Query::from(block_store)).unwrap();
687 let execution = trace.prove_execution::<CurrentAleo, _>(locator.0, VarunaVersion::V1, rng).unwrap();
689 let execution = Execution::from_str(&execution.to_string()).unwrap();
692
693 let transaction = Transaction::from_execution(execution, None).unwrap();
695 let confirmed = ConfirmedTransaction::accepted_execute(0, transaction.clone(), vec![]).unwrap();
697 let transactions = Transactions::from_iter([confirmed]);
699
700 let ratifications = Ratifications::try_from(vec![]).unwrap();
702
703 let header = Header::genesis(&ratifications, &transactions, vec![]).unwrap();
705 let previous_hash = <CurrentNetwork as Network>::BlockHash::default();
707
708 let block = Block::new_beacon(
710 &private_key,
711 previous_hash,
712 header,
713 ratifications,
714 None.into(),
715 vec![],
716 transactions,
717 vec![],
718 rng,
719 )
720 .unwrap();
721 assert!(block.header().is_genesis().pretty_unwrap(), "Failed to initialize a genesis block");
722 (block, transaction, private_key)
724 }
725
726 pub(crate) fn sample_metadata() -> Metadata<CurrentNetwork> {
727 let network = CurrentNetwork::ID;
728 let round = u64::MAX;
729 let height = u32::MAX;
730 let cumulative_weight = u128::MAX - 1;
731 let cumulative_proof_target = u128::MAX - 1;
732 let coinbase_target = u64::MAX;
733 let proof_target = u64::MAX - 1;
734 let last_coinbase_target = u64::MAX;
735 let timestamp = i64::MAX - 1;
736 let last_coinbase_timestamp = timestamp - 1;
737
738 Metadata::new(
739 network,
740 round,
741 height,
742 cumulative_weight,
743 cumulative_proof_target,
744 coinbase_target,
745 proof_target,
746 last_coinbase_target,
747 last_coinbase_timestamp,
748 timestamp,
749 )
750 .pretty_unwrap()
751 }
752}
753
754#[cfg(test)]
755mod tests {
756 use super::*;
757
758 use console::network::MainnetV0;
759 use indexmap::IndexMap;
760 type CurrentNetwork = MainnetV0;
761
762 #[test]
763 fn test_find_transaction_for_transition_id() {
764 let rng = &mut TestRng::default();
765
766 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
767 let transactions = block.transactions();
768
769 let transitions = transaction.transitions().collect::<Vec<_>>();
771 assert!(!transitions.is_empty());
772
773 for transition in transitions {
775 assert_eq!(block.find_transaction_for_transition_id(transition.id()), Some(&transaction));
776 assert_eq!(transactions.find_transaction_for_transition_id(transition.id()), Some(&transaction));
777 }
778
779 for _ in 0..10 {
781 let transition_id = &rng.r#gen();
782 assert_eq!(block.find_transaction_for_transition_id(transition_id), None);
783 assert_eq!(transactions.find_transaction_for_transition_id(transition_id), None);
784 }
785 }
786
787 #[test]
788 fn test_find_transaction_for_commitment() {
789 let rng = &mut TestRng::default();
790
791 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
792 let transactions = block.transactions();
793
794 let commitments = transaction.commitments().collect::<Vec<_>>();
796 assert!(!commitments.is_empty());
797
798 for commitment in commitments {
800 assert_eq!(block.find_transaction_for_commitment(commitment), Some(&transaction));
801 assert_eq!(transactions.find_transaction_for_commitment(commitment), Some(&transaction));
802 }
803
804 for _ in 0..10 {
806 let commitment = &rng.r#gen();
807 assert_eq!(block.find_transaction_for_commitment(commitment), None);
808 assert_eq!(transactions.find_transaction_for_commitment(commitment), None);
809 }
810 }
811
812 #[test]
813 fn test_find_transition() {
814 let rng = &mut TestRng::default();
815
816 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
817 let transactions = block.transactions();
818
819 let transitions = transaction.transitions().collect::<Vec<_>>();
821 assert!(!transitions.is_empty());
822
823 for transition in transitions {
825 assert_eq!(block.find_transition(transition.id()), Some(transition));
826 assert_eq!(transactions.find_transition(transition.id()), Some(transition));
827 assert_eq!(transaction.find_transition(transition.id()), Some(transition));
828 }
829
830 for _ in 0..10 {
832 let transition_id = &rng.r#gen();
833 assert_eq!(block.find_transition(transition_id), None);
834 assert_eq!(transactions.find_transition(transition_id), None);
835 assert_eq!(transaction.find_transition(transition_id), None);
836 }
837 }
838
839 #[test]
840 fn test_find_transition_for_commitment() {
841 let rng = &mut TestRng::default();
842
843 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
844 let transactions = block.transactions();
845
846 let transitions = transaction.transitions().collect::<Vec<_>>();
848 assert!(!transitions.is_empty());
849
850 for transition in transitions {
851 let commitments = transition.commitments().collect::<Vec<_>>();
853 assert!(!commitments.is_empty());
854
855 for commitment in commitments {
857 assert_eq!(block.find_transition_for_commitment(commitment), Some(transition));
858 assert_eq!(transactions.find_transition_for_commitment(commitment), Some(transition));
859 assert_eq!(transaction.find_transition_for_commitment(commitment), Some(transition));
860 }
861 }
862
863 for _ in 0..10 {
865 let commitment = &rng.r#gen();
866 assert_eq!(block.find_transition_for_commitment(commitment), None);
867 assert_eq!(transactions.find_transition_for_commitment(commitment), None);
868 assert_eq!(transaction.find_transition_for_commitment(commitment), None);
869 }
870 }
871
872 #[test]
873 fn test_find_record() {
874 let rng = &mut TestRng::default();
875
876 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
877 let transactions = block.transactions();
878
879 let records = transaction.records().collect::<IndexMap<_, _>>();
881 assert!(!records.is_empty());
882
883 for (commitment, record) in records {
885 assert_eq!(block.find_record(commitment), Some(record));
886 assert_eq!(transactions.find_record(commitment), Some(record));
887 assert_eq!(transaction.find_record(commitment), Some(record));
888 }
889
890 for _ in 0..10 {
892 let commitment = &rng.r#gen();
893 assert_eq!(block.find_record(commitment), None);
894 assert_eq!(transactions.find_record(commitment), None);
895 assert_eq!(transaction.find_record(commitment), None);
896 }
897 }
898
899 #[test]
900 fn test_serde_metadata() {
901 let metadata = crate::test_helpers::sample_metadata();
902 let json_metadata = serde_json::to_string(&metadata).unwrap();
903 let deserialized_metadata: Metadata<CurrentNetwork> = serde_json::from_str(&json_metadata).unwrap();
904 assert_eq!(metadata, deserialized_metadata);
905 }
906}