1#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![cfg_attr(test, allow(clippy::single_element_loop))]
20
21pub mod header;
22pub use header::*;
23
24mod helpers;
25pub use helpers::*;
26
27pub mod ratifications;
28pub use ratifications::*;
29
30pub mod ratify;
31pub use ratify::*;
32
33pub mod solutions;
34pub use solutions::*;
35
36pub mod transaction;
37pub use transaction::*;
38
39pub mod transactions;
40pub use transactions::*;
41
42pub mod transition;
43pub use transition::*;
44
45mod bytes;
46mod genesis;
47mod serialize;
48mod string;
49mod verify;
50
51use console::{
52 account::PrivateKey,
53 network::prelude::*,
54 program::{Ciphertext, Record},
55 types::{Field, Group, U64},
56};
57use ledger_authority::Authority;
58use ledger_committee::Committee;
59use ledger_narwhal_data::Data;
60use ledger_narwhal_subdag::Subdag;
61use ledger_narwhal_transmission_id::TransmissionID;
62use ledger_puzzle::{PuzzleSolutions, Solution, SolutionID};
63
64#[derive(Clone, PartialEq, Eq)]
65pub struct Block<N: Network> {
66 block_hash: N::BlockHash,
68 previous_hash: N::BlockHash,
70 header: Header<N>,
72 authority: Authority<N>,
74 ratifications: Ratifications<N>,
76 solutions: Solutions<N>,
78 aborted_solution_ids: Vec<SolutionID<N>>,
80 transactions: Transactions<N>,
82 aborted_transaction_ids: Vec<N::TransactionID>,
84}
85
86impl<N: Network> Block<N> {
87 pub fn new_beacon<R: Rng + CryptoRng>(
90 private_key: &PrivateKey<N>,
91 previous_hash: N::BlockHash,
92 header: Header<N>,
93 ratifications: Ratifications<N>,
94 solutions: Solutions<N>,
95 aborted_solution_ids: Vec<SolutionID<N>>,
96 transactions: Transactions<N>,
97 aborted_transaction_ids: Vec<N::TransactionID>,
98 rng: &mut R,
99 ) -> Result<Self> {
100 let block_hash = N::hash_bhp1024(&to_bits_le![previous_hash, header.to_root()?])?;
102 let authority = Authority::new_beacon(private_key, block_hash, rng)?;
104 Self::from(
106 previous_hash,
107 header,
108 authority,
109 ratifications,
110 solutions,
111 aborted_solution_ids,
112 transactions,
113 aborted_transaction_ids,
114 )
115 }
116
117 pub fn new_quorum(
120 previous_hash: N::BlockHash,
121 header: Header<N>,
122 subdag: Subdag<N>,
123 ratifications: Ratifications<N>,
124 solutions: Solutions<N>,
125 aborted_solution_ids: Vec<SolutionID<N>>,
126 transactions: Transactions<N>,
127 aborted_transaction_ids: Vec<N::TransactionID>,
128 ) -> Result<Self> {
129 let authority = Authority::new_quorum(subdag);
131 Self::from(
133 previous_hash,
134 header,
135 authority,
136 ratifications,
137 solutions,
138 aborted_solution_ids,
139 transactions,
140 aborted_transaction_ids,
141 )
142 }
143
144 pub fn from(
147 previous_hash: N::BlockHash,
148 header: Header<N>,
149 authority: Authority<N>,
150 ratifications: Ratifications<N>,
151 solutions: Solutions<N>,
152 aborted_solution_ids: Vec<SolutionID<N>>,
153 transactions: Transactions<N>,
154 aborted_transaction_ids: Vec<N::TransactionID>,
155 ) -> Result<Self> {
156 if aborted_solution_ids.len() > Solutions::<N>::max_aborted_solutions()? {
158 bail!(
159 "Cannot initialize a block with {} aborted solutions IDs which exceed the maximum {}",
160 aborted_solution_ids.len(),
161 Solutions::<N>::max_aborted_solutions()?
162 );
163 }
164
165 if transactions.len() > Transactions::<N>::MAX_TRANSACTIONS {
167 bail!(
168 "Cannot initialize a block with {} confirmed transactions which exceed the maximum {}",
169 transactions.len(),
170 Transactions::<N>::MAX_TRANSACTIONS
171 );
172 }
173
174 if aborted_transaction_ids.len() > Transactions::<N>::max_aborted_transactions()? {
180 bail!(
181 "Cannot initialize a block with {} aborted transaction IDs which exceed the maximum {}",
182 aborted_transaction_ids.len(),
183 Transactions::<N>::max_aborted_transactions()?
184 );
185 }
186
187 let block_hash = N::hash_bhp1024(&to_bits_le![previous_hash, header.to_root()?])?;
189
190 match &authority {
192 Authority::Beacon(signature) => {
193 let address = signature.to_address();
195 ensure!(signature.verify(&address, &[block_hash]), "Invalid signature for block {}", header.height());
197 }
198 Authority::Quorum(subdag) => {
199 Self::check_subdag_transmissions(
201 subdag,
202 &solutions,
203 &aborted_solution_ids,
204 &transactions,
205 &aborted_transaction_ids,
206 )?;
207 }
208 }
209
210 if header.solutions_root() != solutions.to_solutions_root()? {
212 bail!("The solutions root in the block does not correspond to the solutions");
213 }
214
215 let subdag_root = match &authority {
217 Authority::Beacon(_) => Field::<N>::zero(),
218 Authority::Quorum(subdag) => subdag.to_subdag_root()?,
219 };
220 if header.subdag_root() != subdag_root {
221 bail!("The subdag root in the block does not correspond to the authority");
222 }
223
224 Self::from_unchecked(
226 block_hash.into(),
227 previous_hash,
228 header,
229 authority,
230 ratifications,
231 solutions,
232 aborted_solution_ids,
233 transactions,
234 aborted_transaction_ids,
235 )
236 }
237
238 fn from_unchecked(
242 block_hash: N::BlockHash,
243 previous_hash: N::BlockHash,
244 header: Header<N>,
245 authority: Authority<N>,
246 ratifications: Ratifications<N>,
247 solutions: Solutions<N>,
248 aborted_solution_ids: Vec<SolutionID<N>>,
249 transactions: Transactions<N>,
250 aborted_transaction_ids: Vec<N::TransactionID>,
251 ) -> Result<Self> {
252 Ok(Self {
254 block_hash,
255 previous_hash,
256 header,
257 authority,
258 ratifications,
259 solutions,
260 aborted_solution_ids,
261 transactions,
262 aborted_transaction_ids,
263 })
264 }
265}
266
267impl<N: Network> Block<N> {
268 pub const fn hash(&self) -> N::BlockHash {
270 self.block_hash
271 }
272
273 pub const fn previous_hash(&self) -> N::BlockHash {
275 self.previous_hash
276 }
277
278 pub const fn authority(&self) -> &Authority<N> {
280 &self.authority
281 }
282
283 pub const fn ratifications(&self) -> &Ratifications<N> {
285 &self.ratifications
286 }
287
288 pub const fn solutions(&self) -> &Solutions<N> {
290 &self.solutions
291 }
292
293 pub const fn aborted_solution_ids(&self) -> &Vec<SolutionID<N>> {
295 &self.aborted_solution_ids
296 }
297
298 pub const fn transactions(&self) -> &Transactions<N> {
300 &self.transactions
301 }
302
303 pub const fn aborted_transaction_ids(&self) -> &Vec<N::TransactionID> {
305 &self.aborted_transaction_ids
306 }
307}
308
309impl<N: Network> Block<N> {
310 pub const fn header(&self) -> &Header<N> {
312 &self.header
313 }
314
315 pub const fn previous_state_root(&self) -> N::StateRoot {
317 self.header.previous_state_root()
318 }
319
320 pub const fn transactions_root(&self) -> Field<N> {
322 self.header.transactions_root()
323 }
324
325 pub const fn finalize_root(&self) -> Field<N> {
327 self.header.finalize_root()
328 }
329
330 pub const fn ratifications_root(&self) -> Field<N> {
332 self.header.ratifications_root()
333 }
334
335 pub const fn solutions_root(&self) -> Field<N> {
337 self.header.solutions_root()
338 }
339
340 pub const fn metadata(&self) -> &Metadata<N> {
342 self.header.metadata()
343 }
344
345 pub const fn network(&self) -> u16 {
347 self.header.network()
348 }
349
350 pub const fn height(&self) -> u32 {
352 self.header.height()
353 }
354
355 pub const fn round(&self) -> u64 {
357 self.header.round()
358 }
359
360 pub const fn epoch_number(&self) -> u32 {
362 self.height() / N::NUM_BLOCKS_PER_EPOCH
363 }
364
365 pub const fn cumulative_weight(&self) -> u128 {
367 self.header.cumulative_weight()
368 }
369
370 pub const fn cumulative_proof_target(&self) -> u128 {
372 self.header.cumulative_proof_target()
373 }
374
375 pub const fn coinbase_target(&self) -> u64 {
377 self.header.coinbase_target()
378 }
379
380 pub const fn proof_target(&self) -> u64 {
382 self.header.proof_target()
383 }
384
385 pub const fn last_coinbase_target(&self) -> u64 {
387 self.header.last_coinbase_target()
388 }
389
390 pub const fn last_coinbase_timestamp(&self) -> i64 {
392 self.header.last_coinbase_timestamp()
393 }
394
395 pub const fn timestamp(&self) -> i64 {
397 self.header.timestamp()
398 }
399}
400
401impl<N: Network> Block<N> {
402 pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
404 self.transactions.contains_transition(transition_id)
405 }
406
407 pub fn contains_serial_number(&self, serial_number: &Field<N>) -> bool {
409 self.transactions.contains_serial_number(serial_number)
410 }
411
412 pub fn contains_commitment(&self, commitment: &Field<N>) -> bool {
414 self.transactions.contains_commitment(commitment)
415 }
416}
417
418impl<N: Network> Block<N> {
419 pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Option<&Solution<N>> {
421 self.solutions.as_ref().and_then(|solution| solution.get_solution(solution_id))
422 }
423
424 pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Option<&Transaction<N>> {
426 self.transactions.get(transaction_id).map(|t| t.deref())
427 }
428
429 pub fn get_confirmed_transaction(&self, transaction_id: &N::TransactionID) -> Option<&ConfirmedTransaction<N>> {
431 self.transactions.get(transaction_id)
432 }
433}
434
435impl<N: Network> Block<N> {
436 pub fn find_transaction_for_transition_id(&self, transition_id: &N::TransitionID) -> Option<&Transaction<N>> {
438 self.transactions.find_transaction_for_transition_id(transition_id)
439 }
440
441 pub fn find_transaction_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transaction<N>> {
443 self.transactions.find_transaction_for_serial_number(serial_number)
444 }
445
446 pub fn find_transaction_for_commitment(&self, commitment: &Field<N>) -> Option<&Transaction<N>> {
448 self.transactions.find_transaction_for_commitment(commitment)
449 }
450
451 pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
453 self.transactions.find_transition(transition_id)
454 }
455
456 pub fn find_transition_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transition<N>> {
458 self.transactions.find_transition_for_serial_number(serial_number)
459 }
460
461 pub fn find_transition_for_commitment(&self, commitment: &Field<N>) -> Option<&Transition<N>> {
463 self.transactions.find_transition_for_commitment(commitment)
464 }
465
466 pub fn find_record(&self, commitment: &Field<N>) -> Option<&Record<N, Ciphertext<N>>> {
468 self.transactions.find_record(commitment)
469 }
470}
471
472impl<N: Network> Block<N> {
473 pub fn solution_ids(&self) -> Option<impl '_ + Iterator<Item = &SolutionID<N>>> {
475 self.solutions.as_ref().map(|solution| solution.solution_ids())
476 }
477
478 pub fn transaction_ids(&self) -> impl '_ + Iterator<Item = &N::TransactionID> {
480 self.transactions.transaction_ids()
481 }
482
483 pub fn deployments(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
485 self.transactions.deployments()
486 }
487
488 pub fn executions(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
490 self.transactions.executions()
491 }
492
493 pub fn transitions(&self) -> impl '_ + Iterator<Item = &Transition<N>> {
495 self.transactions.transitions()
496 }
497
498 pub fn transition_ids(&self) -> impl '_ + Iterator<Item = &N::TransitionID> {
500 self.transactions.transition_ids()
501 }
502
503 pub fn transition_public_keys(&self) -> impl '_ + Iterator<Item = &Group<N>> {
505 self.transactions.transition_public_keys()
506 }
507
508 pub fn transition_commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
510 self.transactions.transition_commitments()
511 }
512
513 pub fn tags(&self) -> impl '_ + Iterator<Item = &Field<N>> {
515 self.transactions.tags()
516 }
517
518 pub fn input_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
520 self.transactions.input_ids()
521 }
522
523 pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = &Field<N>> {
525 self.transactions.serial_numbers()
526 }
527
528 pub fn output_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
530 self.transactions.output_ids()
531 }
532
533 pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
535 self.transactions.commitments()
536 }
537
538 pub fn records(&self) -> impl '_ + Iterator<Item = (&Field<N>, &Record<N, Ciphertext<N>>)> {
540 self.transactions.records()
541 }
542
543 pub fn nonces(&self) -> impl '_ + Iterator<Item = &Group<N>> {
545 self.transactions.nonces()
546 }
547
548 pub fn transaction_fee_amounts(&self) -> impl '_ + Iterator<Item = Result<U64<N>>> {
550 self.transactions.transaction_fee_amounts()
551 }
552}
553
554impl<N: Network> Block<N> {
555 pub fn into_transaction_ids(self) -> impl Iterator<Item = N::TransactionID> {
557 self.transactions.into_transaction_ids()
558 }
559
560 pub fn into_deployments(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
562 self.transactions.into_deployments()
563 }
564
565 pub fn into_executions(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
567 self.transactions.into_executions()
568 }
569
570 pub fn into_transitions(self) -> impl Iterator<Item = Transition<N>> {
572 self.transactions.into_transitions()
573 }
574
575 pub fn into_transition_ids(self) -> impl Iterator<Item = N::TransitionID> {
577 self.transactions.into_transition_ids()
578 }
579
580 pub fn into_transition_public_keys(self) -> impl Iterator<Item = Group<N>> {
582 self.transactions.into_transition_public_keys()
583 }
584
585 pub fn into_tags(self) -> impl Iterator<Item = Field<N>> {
587 self.transactions.into_tags()
588 }
589
590 pub fn into_serial_numbers(self) -> impl Iterator<Item = Field<N>> {
592 self.transactions.into_serial_numbers()
593 }
594
595 pub fn into_commitments(self) -> impl Iterator<Item = Field<N>> {
597 self.transactions.into_commitments()
598 }
599
600 pub fn into_records(self) -> impl Iterator<Item = (Field<N>, Record<N, Ciphertext<N>>)> {
602 self.transactions.into_records()
603 }
604
605 pub fn into_nonces(self) -> impl Iterator<Item = Group<N>> {
607 self.transactions.into_nonces()
608 }
609}
610
611#[cfg(test)]
612pub mod test_helpers {
613 use super::*;
614 use algorithms::snark::varuna::VarunaVersion;
615 use console::account::{Address, PrivateKey};
616 use ledger_query::Query;
617 use ledger_store::{BlockStore, helpers::memory::BlockMemory};
618 use synthesizer_process::Process;
619
620 use aleo_std::StorageMode;
621 use once_cell::sync::OnceCell;
622
623 type CurrentNetwork = console::network::MainnetV0;
624 type CurrentAleo = circuit::network::AleoV0;
625
626 pub(crate) fn sample_genesis_block(rng: &mut TestRng) -> Block<CurrentNetwork> {
628 let (block, _, _) = sample_genesis_block_and_components(rng);
630 block
632 }
633
634 pub(crate) fn sample_genesis_block_and_transaction(
636 rng: &mut TestRng,
637 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>) {
638 let (block, transaction, _) = sample_genesis_block_and_components(rng);
640 (block, transaction)
642 }
643
644 pub(crate) fn sample_genesis_block_and_components(
646 rng: &mut TestRng,
647 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
648 static INSTANCE: OnceCell<(Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>)> =
649 OnceCell::new();
650 INSTANCE.get_or_init(|| sample_genesis_block_and_components_raw(rng)).clone()
651 }
652
653 fn sample_genesis_block_and_components_raw(
655 rng: &mut TestRng,
656 ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
657 let private_key = PrivateKey::new(rng).unwrap();
659 let address = Address::<CurrentNetwork>::try_from(private_key).unwrap();
660
661 let locator = ("credits.aleo", "transfer_public_to_private");
663 let amount = 100_000_000u64;
665 let inputs = [address.to_string(), format!("{amount}_u64")];
667
668 let process = Process::load().unwrap();
670 let authorization =
672 process.authorize::<CurrentAleo, _>(&private_key, locator.0, locator.1, inputs.iter(), rng).unwrap();
673 let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
675
676 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
678
679 trace.prepare(&Query::from(block_store)).unwrap();
681 let execution = trace.prove_execution::<CurrentAleo, _>(locator.0, VarunaVersion::V1, rng).unwrap();
683 let execution = Execution::from_str(&execution.to_string()).unwrap();
686
687 let transaction = Transaction::from_execution(execution, None).unwrap();
689 let confirmed = ConfirmedTransaction::accepted_execute(0, transaction.clone(), vec![]).unwrap();
691 let transactions = Transactions::from_iter([confirmed]);
693
694 let ratifications = Ratifications::try_from(vec![]).unwrap();
696
697 let header = Header::genesis(&ratifications, &transactions, vec![]).unwrap();
699 let previous_hash = <CurrentNetwork as Network>::BlockHash::default();
701
702 let block = Block::new_beacon(
704 &private_key,
705 previous_hash,
706 header,
707 ratifications,
708 None.into(),
709 vec![],
710 transactions,
711 vec![],
712 rng,
713 )
714 .unwrap();
715 assert!(block.header().is_genesis(), "Failed to initialize a genesis block");
716 (block, transaction, private_key)
718 }
719
720 pub(crate) fn sample_metadata() -> Metadata<CurrentNetwork> {
721 let network = CurrentNetwork::ID;
722 let round = u64::MAX;
723 let height = u32::MAX;
724 let cumulative_weight = u128::MAX - 1;
725 let cumulative_proof_target = u128::MAX - 1;
726 let coinbase_target = u64::MAX;
727 let proof_target = u64::MAX - 1;
728 let last_coinbase_target = u64::MAX;
729 let timestamp = i64::MAX - 1;
730 let last_coinbase_timestamp = timestamp - 1;
731 Metadata::new(
732 network,
733 round,
734 height,
735 cumulative_weight,
736 cumulative_proof_target,
737 coinbase_target,
738 proof_target,
739 last_coinbase_target,
740 last_coinbase_timestamp,
741 timestamp,
742 )
743 .unwrap()
744 }
745}
746
747#[cfg(test)]
748mod tests {
749 use super::*;
750
751 use console::network::MainnetV0;
752 use indexmap::IndexMap;
753 type CurrentNetwork = MainnetV0;
754
755 #[test]
756 fn test_find_transaction_for_transition_id() {
757 let rng = &mut TestRng::default();
758
759 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
760 let transactions = block.transactions();
761
762 let transitions = transaction.transitions().collect::<Vec<_>>();
764 assert!(!transitions.is_empty());
765
766 for transition in transitions {
768 assert_eq!(block.find_transaction_for_transition_id(transition.id()), Some(&transaction));
769 assert_eq!(transactions.find_transaction_for_transition_id(transition.id()), Some(&transaction));
770 }
771
772 for _ in 0..10 {
774 let transition_id = &rng.gen();
775 assert_eq!(block.find_transaction_for_transition_id(transition_id), None);
776 assert_eq!(transactions.find_transaction_for_transition_id(transition_id), None);
777 }
778 }
779
780 #[test]
781 fn test_find_transaction_for_commitment() {
782 let rng = &mut TestRng::default();
783
784 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
785 let transactions = block.transactions();
786
787 let commitments = transaction.commitments().collect::<Vec<_>>();
789 assert!(!commitments.is_empty());
790
791 for commitment in commitments {
793 assert_eq!(block.find_transaction_for_commitment(commitment), Some(&transaction));
794 assert_eq!(transactions.find_transaction_for_commitment(commitment), Some(&transaction));
795 }
796
797 for _ in 0..10 {
799 let commitment = &rng.gen();
800 assert_eq!(block.find_transaction_for_commitment(commitment), None);
801 assert_eq!(transactions.find_transaction_for_commitment(commitment), None);
802 }
803 }
804
805 #[test]
806 fn test_find_transition() {
807 let rng = &mut TestRng::default();
808
809 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
810 let transactions = block.transactions();
811
812 let transitions = transaction.transitions().collect::<Vec<_>>();
814 assert!(!transitions.is_empty());
815
816 for transition in transitions {
818 assert_eq!(block.find_transition(transition.id()), Some(transition));
819 assert_eq!(transactions.find_transition(transition.id()), Some(transition));
820 assert_eq!(transaction.find_transition(transition.id()), Some(transition));
821 }
822
823 for _ in 0..10 {
825 let transition_id = &rng.gen();
826 assert_eq!(block.find_transition(transition_id), None);
827 assert_eq!(transactions.find_transition(transition_id), None);
828 assert_eq!(transaction.find_transition(transition_id), None);
829 }
830 }
831
832 #[test]
833 fn test_find_transition_for_commitment() {
834 let rng = &mut TestRng::default();
835
836 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
837 let transactions = block.transactions();
838
839 let transitions = transaction.transitions().collect::<Vec<_>>();
841 assert!(!transitions.is_empty());
842
843 for transition in transitions {
844 let commitments = transition.commitments().collect::<Vec<_>>();
846 assert!(!commitments.is_empty());
847
848 for commitment in commitments {
850 assert_eq!(block.find_transition_for_commitment(commitment), Some(transition));
851 assert_eq!(transactions.find_transition_for_commitment(commitment), Some(transition));
852 assert_eq!(transaction.find_transition_for_commitment(commitment), Some(transition));
853 }
854 }
855
856 for _ in 0..10 {
858 let commitment = &rng.gen();
859 assert_eq!(block.find_transition_for_commitment(commitment), None);
860 assert_eq!(transactions.find_transition_for_commitment(commitment), None);
861 assert_eq!(transaction.find_transition_for_commitment(commitment), None);
862 }
863 }
864
865 #[test]
866 fn test_find_record() {
867 let rng = &mut TestRng::default();
868
869 let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
870 let transactions = block.transactions();
871
872 let records = transaction.records().collect::<IndexMap<_, _>>();
874 assert!(!records.is_empty());
875
876 for (commitment, record) in records {
878 assert_eq!(block.find_record(commitment), Some(record));
879 assert_eq!(transactions.find_record(commitment), Some(record));
880 assert_eq!(transaction.find_record(commitment), Some(record));
881 }
882
883 for _ in 0..10 {
885 let commitment = &rng.gen();
886 assert_eq!(block.find_record(commitment), None);
887 assert_eq!(transactions.find_record(commitment), None);
888 assert_eq!(transaction.find_record(commitment), None);
889 }
890 }
891
892 #[test]
893 fn test_serde_metadata() {
894 let metadata = crate::test_helpers::sample_metadata();
895 let json_metadata = serde_json::to_string(&metadata).unwrap();
896 let deserialized_metadata: Metadata<CurrentNetwork> = serde_json::from_str(&json_metadata).unwrap();
897 assert_eq!(metadata, deserialized_metadata);
898 }
899}