Skip to main content

snarkvm_ledger_block/
lib.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18// #![warn(clippy::cast_possible_truncation)]
19#![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    /// The hash of this block.
69    block_hash: N::BlockHash,
70    /// The hash of the previous block.
71    previous_hash: N::BlockHash,
72    /// The header of this block.
73    header: Header<N>,
74    /// The authority for this block.
75    authority: Authority<N>,
76    /// The ratifications in this block.
77    ratifications: Ratifications<N>,
78    /// The solutions in the block.
79    solutions: Solutions<N>,
80    /// The aborted solution IDs in this block.
81    aborted_solution_ids: Vec<SolutionID<N>>,
82    /// The transactions in this block.
83    transactions: Transactions<N>,
84    /// The aborted transaction IDs in this block.
85    aborted_transaction_ids: Vec<N::TransactionID>,
86}
87
88impl<N: Network> Block<N> {
89    /// Initializes a new beacon block from the given previous block hash, block header,
90    /// ratifications, solutions, transactions, and aborted transaction IDs.
91    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        // Compute the block hash.
103        let block_hash = N::hash_bhp1024(&to_bits_le![previous_hash, header.to_root()?])?;
104        // Construct the beacon authority.
105        let authority = Authority::new_beacon(private_key, block_hash, rng)?;
106        // Construct the block.
107        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    /// Initializes a new quorum block from the given previous block hash, block header,
120    /// subdag, ratifications, solutions, transactions, and aborted transaction IDs.
121    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        // Construct the beacon authority.
132        let authority = Authority::new_quorum(subdag);
133        // Construct the block.
134        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    /// Initializes a new block from the given previous block hash, block header, authority,
147    /// ratifications, solutions, aborted solution IDs, transactions, and aborted transaction IDs.
148    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        // Ensure the number of aborted solutions IDs is within the allowed range.
159        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        // Ensure the number of transactions is within the allowed range.
168        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        // Here we do not check that the number of solutions is within the allowed range,
177        // because that was already done when constructing [`Solutions`] values,
178        // specifically in [`PuzzleSolutions::new()`].
179
180        // Ensure the number of aborted transaction IDs is within the allowed range.
181        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        // Compute the block hash.
190        let block_hash = N::hash_bhp1024(&to_bits_le![previous_hash, header.to_root()?])?;
191
192        // Verify the authority.
193        match &authority {
194            Authority::Beacon(signature) => {
195                // Derive the signer address.
196                let address = signature.to_address();
197                // Ensure the signature is valid.
198                ensure!(signature.verify(&address, &[block_hash]), "Invalid signature for block {}", header.height());
199            }
200            Authority::Quorum(subdag) => {
201                // Ensure the certificates follow the canonical order for this consensus version.
202                subdag.check_certificate_order(header.height())?;
203                // Ensure the transmission IDs from the subdag correspond to the block.
204                Self::check_subdag_transmissions(
205                    subdag,
206                    &solutions,
207                    &aborted_solution_ids,
208                    &transactions,
209                    &aborted_transaction_ids,
210                )?;
211            }
212        }
213
214        // Ensure that coinbase accumulator matches the solutions.
215        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        // Ensure that the subdag root matches the authority.
220        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        // Return the block.
229        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    /// Initializes a new block from the given block hash, previous block hash, block header,
243    /// authority, ratifications, solutions, transactions, and aborted transaction IDs.
244    ///
245    /// This function does *not* perform any checks on the given data, and should only be called
246    /// if the inputs are trusted.
247    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        // Return the block.
259        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    /// Returns the block hash.
275    pub const fn hash(&self) -> N::BlockHash {
276        self.block_hash
277    }
278
279    /// Returns the previous block hash.
280    pub const fn previous_hash(&self) -> N::BlockHash {
281        self.previous_hash
282    }
283
284    /// Returns the authority.
285    pub const fn authority(&self) -> &Authority<N> {
286        &self.authority
287    }
288
289    /// Returns the ratifications in this block.
290    pub const fn ratifications(&self) -> &Ratifications<N> {
291        &self.ratifications
292    }
293
294    /// Returns the solutions in the block.
295    pub const fn solutions(&self) -> &Solutions<N> {
296        &self.solutions
297    }
298
299    /// Returns the aborted solution IDs in this block.
300    pub const fn aborted_solution_ids(&self) -> &Vec<SolutionID<N>> {
301        &self.aborted_solution_ids
302    }
303
304    /// Returns the transactions in this block.
305    pub const fn transactions(&self) -> &Transactions<N> {
306        &self.transactions
307    }
308
309    /// Returns the aborted transaction IDs in this block.
310    pub const fn aborted_transaction_ids(&self) -> &Vec<N::TransactionID> {
311        &self.aborted_transaction_ids
312    }
313}
314
315impl<N: Network> Block<N> {
316    /// Returns the block header.
317    pub const fn header(&self) -> &Header<N> {
318        &self.header
319    }
320
321    /// Returns the previous state root from the block header.
322    pub const fn previous_state_root(&self) -> N::StateRoot {
323        self.header.previous_state_root()
324    }
325
326    /// Returns the transactions root in the block header.
327    pub const fn transactions_root(&self) -> Field<N> {
328        self.header.transactions_root()
329    }
330
331    /// Returns the finalize root in the block header.
332    pub const fn finalize_root(&self) -> Field<N> {
333        self.header.finalize_root()
334    }
335
336    /// Returns the ratifications root in the block header.
337    pub const fn ratifications_root(&self) -> Field<N> {
338        self.header.ratifications_root()
339    }
340
341    /// Returns the solutions root in the block header.
342    pub const fn solutions_root(&self) -> Field<N> {
343        self.header.solutions_root()
344    }
345
346    /// Returns the metadata in the block header.
347    pub const fn metadata(&self) -> &Metadata<N> {
348        self.header.metadata()
349    }
350
351    /// Returns the network ID of this block.
352    pub const fn network(&self) -> u16 {
353        self.header.network()
354    }
355
356    /// Returns the height of this block.
357    pub const fn height(&self) -> u32 {
358        self.header.height()
359    }
360
361    /// Returns the round number of this block.
362    pub const fn round(&self) -> u64 {
363        self.header.round()
364    }
365
366    /// Returns the epoch number of this block.
367    pub const fn epoch_number(&self) -> u32 {
368        self.height() / N::NUM_BLOCKS_PER_EPOCH
369    }
370
371    /// Returns the cumulative weight for this block.
372    pub const fn cumulative_weight(&self) -> u128 {
373        self.header.cumulative_weight()
374    }
375
376    /// Returns the cumulative proof target for this block.
377    pub const fn cumulative_proof_target(&self) -> u128 {
378        self.header.cumulative_proof_target()
379    }
380
381    /// Returns the coinbase target for this block.
382    pub const fn coinbase_target(&self) -> u64 {
383        self.header.coinbase_target()
384    }
385
386    /// Returns the proof target for this block.
387    pub const fn proof_target(&self) -> u64 {
388        self.header.proof_target()
389    }
390
391    /// Returns the coinbase target of the last coinbase.
392    pub const fn last_coinbase_target(&self) -> u64 {
393        self.header.last_coinbase_target()
394    }
395
396    /// Returns the block timestamp of the last coinbase.
397    pub const fn last_coinbase_timestamp(&self) -> i64 {
398        self.header.last_coinbase_timestamp()
399    }
400
401    /// Returns the Unix timestamp (UTC) for this block.
402    pub const fn timestamp(&self) -> i64 {
403        self.header.timestamp()
404    }
405}
406
407impl<N: Network> Block<N> {
408    /// Returns `true` if the block contains the given transition ID.
409    pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
410        self.transactions.contains_transition(transition_id)
411    }
412
413    /// Returns `true` if the block contains the given serial number.
414    pub fn contains_serial_number(&self, serial_number: &Field<N>) -> bool {
415        self.transactions.contains_serial_number(serial_number)
416    }
417
418    /// Returns `true` if the block contains the given commitment.
419    pub fn contains_commitment(&self, commitment: &Field<N>) -> bool {
420        self.transactions.contains_commitment(commitment)
421    }
422}
423
424impl<N: Network> Block<N> {
425    /// Returns the solution with the given solution ID, if it exists.
426    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    /// Returns the transaction with the given transaction ID, if it exists.
431    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    /// Returns the confirmed transaction with the given transaction ID, if it exists.
436    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    /// Returns the transaction with the given transition ID, if it exists.
443    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    /// Returns the transaction with the given serial number, if it exists.
448    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    /// Returns the transaction with the given commitment, if it exists.
453    pub fn find_transaction_for_commitment(&self, commitment: &Field<N>) -> Option<&Transaction<N>> {
454        self.transactions.find_transaction_for_commitment(commitment)
455    }
456
457    /// Returns the transition with the corresponding transition ID, if it exists.
458    pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
459        self.transactions.find_transition(transition_id)
460    }
461
462    /// Returns the transition for the given serial number, if it exists.
463    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    /// Returns the transition for the given commitment, if it exists.
468    pub fn find_transition_for_commitment(&self, commitment: &Field<N>) -> Option<&Transition<N>> {
469        self.transactions.find_transition_for_commitment(commitment)
470    }
471
472    /// Returns the record with the corresponding commitment, if it exists.
473    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    /// Returns an iterator over the solution IDs in this block.
480    pub fn solution_ids(&self) -> Option<impl '_ + Iterator<Item = &SolutionID<N>>> {
481        self.solutions.as_ref().map(|solution| solution.solution_ids())
482    }
483
484    /// Returns an iterator over the transaction IDs, for all transactions in `self`.
485    pub fn transaction_ids(&self) -> impl '_ + Iterator<Item = &N::TransactionID> {
486        self.transactions.transaction_ids()
487    }
488
489    /// Returns an iterator over all transactions in `self` that are accepted deploy transactions.
490    pub fn deployments(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
491        self.transactions.deployments()
492    }
493
494    /// Returns an iterator over all transactions in `self` that are accepted execute transactions.
495    pub fn executions(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
496        self.transactions.executions()
497    }
498
499    /// Returns an iterator over all transitions.
500    pub fn transitions(&self) -> impl '_ + Iterator<Item = &Transition<N>> {
501        self.transactions.transitions()
502    }
503
504    /// Returns an iterator over the transition IDs, for all transitions.
505    pub fn transition_ids(&self) -> impl '_ + Iterator<Item = &N::TransitionID> {
506        self.transactions.transition_ids()
507    }
508
509    /// Returns an iterator over the transition public keys, for all transactions.
510    pub fn transition_public_keys(&self) -> impl '_ + Iterator<Item = &Group<N>> {
511        self.transactions.transition_public_keys()
512    }
513
514    /// Returns an iterator over the transition commitments, for all transactions.
515    pub fn transition_commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
516        self.transactions.transition_commitments()
517    }
518
519    /// Returns an iterator over the tags, for all transition inputs that are records.
520    pub fn tags(&self) -> impl '_ + Iterator<Item = &Field<N>> {
521        self.transactions.tags()
522    }
523
524    /// Returns an iterator over the input IDs, for all transition inputs that are records.
525    pub fn input_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
526        self.transactions.input_ids()
527    }
528
529    /// Returns an iterator over the serial numbers, for all transition inputs that are records.
530    pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = &Field<N>> {
531        self.transactions.serial_numbers()
532    }
533
534    /// Returns an iterator over the output IDs, for all transition inputs that are records.
535    pub fn output_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
536        self.transactions.output_ids()
537    }
538
539    /// Returns an iterator over the commitments, for all transition outputs that are records.
540    pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
541        self.transactions.commitments()
542    }
543
544    /// Returns an iterator over the records, for all transition outputs that are records.
545    pub fn records(&self) -> impl '_ + Iterator<Item = (&Field<N>, &Record<N, Ciphertext<N>>)> {
546        self.transactions.records()
547    }
548
549    /// Returns an iterator over the nonces, for all transition outputs that are records.
550    pub fn nonces(&self) -> impl '_ + Iterator<Item = &Group<N>> {
551        self.transactions.nonces()
552    }
553
554    /// Returns an iterator over the transaction fee amounts, for all transactions.
555    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    /// Returns a consuming iterator over the transaction IDs, for all transactions in `self`.
562    pub fn into_transaction_ids(self) -> impl Iterator<Item = N::TransactionID> {
563        self.transactions.into_transaction_ids()
564    }
565
566    /// Returns a consuming iterator over all transactions in `self` that are accepted deploy transactions.
567    pub fn into_deployments(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
568        self.transactions.into_deployments()
569    }
570
571    /// Returns a consuming iterator over all transactions in `self` that are accepted execute transactions.
572    pub fn into_executions(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
573        self.transactions.into_executions()
574    }
575
576    /// Returns a consuming iterator over all transitions.
577    pub fn into_transitions(self) -> impl Iterator<Item = Transition<N>> {
578        self.transactions.into_transitions()
579    }
580
581    /// Returns a consuming iterator over the transition IDs, for all transitions.
582    pub fn into_transition_ids(self) -> impl Iterator<Item = N::TransitionID> {
583        self.transactions.into_transition_ids()
584    }
585
586    /// Returns a consuming iterator over the transition public keys, for all transactions.
587    pub fn into_transition_public_keys(self) -> impl Iterator<Item = Group<N>> {
588        self.transactions.into_transition_public_keys()
589    }
590
591    /// Returns a consuming iterator over the tags, for all transition inputs that are records.
592    pub fn into_tags(self) -> impl Iterator<Item = Field<N>> {
593        self.transactions.into_tags()
594    }
595
596    /// Returns a consuming iterator over the serial numbers, for all transition inputs that are records.
597    pub fn into_serial_numbers(self) -> impl Iterator<Item = Field<N>> {
598        self.transactions.into_serial_numbers()
599    }
600
601    /// Returns a consuming iterator over the commitments, for all transition outputs that are records.
602    pub fn into_commitments(self) -> impl Iterator<Item = Field<N>> {
603        self.transactions.into_commitments()
604    }
605
606    /// Returns a consuming iterator over the records, for all transition outputs that are records.
607    pub fn into_records(self) -> impl Iterator<Item = (Field<N>, Record<N, Ciphertext<N>>)> {
608        self.transactions.into_records()
609    }
610
611    /// Returns a consuming iterator over the nonces, for all transition outputs that are records.
612    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    /// Samples a random genesis block.
635    pub(crate) fn sample_genesis_block(rng: &mut TestRng) -> Block<CurrentNetwork> {
636        // Sample the genesis block and components.
637        let (block, _, _) = sample_genesis_block_and_components(rng);
638        // Return the block.
639        block
640    }
641
642    /// Samples a random genesis block and the transaction from the genesis block.
643    pub(crate) fn sample_genesis_block_and_transaction(
644        rng: &mut TestRng,
645    ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>) {
646        // Sample the genesis block and components.
647        let (block, transaction, _) = sample_genesis_block_and_components(rng);
648        // Return the block and transaction.
649        (block, transaction)
650    }
651
652    /// Samples a random genesis block, the transaction from the genesis block, and the genesis private key.
653    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    /// Samples a random genesis block, the transaction from the genesis block, and the genesis private key.
662    fn sample_genesis_block_and_components_raw(
663        rng: &mut TestRng,
664    ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
665        // Sample the genesis private key.
666        let private_key = PrivateKey::new(rng).unwrap();
667        let address = Address::<CurrentNetwork>::try_from(private_key).unwrap();
668
669        // Prepare the locator.
670        let locator = ("credits.aleo", "transfer_public_to_private");
671        // Prepare the amount for each call to the function.
672        let amount = 100_000_000u64;
673        // Prepare the function inputs.
674        let inputs = [address.to_string(), format!("{amount}_u64")];
675
676        // Initialize the process.
677        let process = Process::load().unwrap();
678        // Authorize the function.
679        let authorization =
680            process.authorize::<CurrentAleo, _>(&private_key, locator.0, locator.1, inputs.iter(), rng).unwrap();
681        // Execute the function.
682        let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
683
684        // Initialize a new block store.
685        let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
686
687        // Prepare the assignments.
688        trace.prepare(&Query::from(block_store)).unwrap();
689        // Compute the proof and construct the execution.
690        let execution = trace.prove_execution::<CurrentAleo, _>(locator.0, VarunaVersion::V1, rng).unwrap();
691        // Convert the execution.
692        // Note: This is a testing-only hack to adhere to Rust's dependency cycle rules.
693        let execution = Execution::from_str(&execution.to_string()).unwrap();
694
695        // Construct the transaction.
696        let transaction = Transaction::from_execution(execution, None).unwrap();
697        // Prepare the confirmed transaction.
698        let confirmed = ConfirmedTransaction::accepted_execute(0, transaction.clone(), vec![]).unwrap();
699        // Prepare the transactions.
700        let transactions = Transactions::from_iter([confirmed]);
701
702        // Construct the ratifications.
703        let ratifications = Ratifications::try_from(vec![]).unwrap();
704
705        // Prepare the block header.
706        let header = Header::genesis(&ratifications, &transactions, vec![]).unwrap();
707        // Prepare the previous block hash.
708        let previous_hash = <CurrentNetwork as Network>::BlockHash::default();
709
710        // Construct the block.
711        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        // Return the block, transaction, and private key.
725        (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        // Retrieve the transitions.
772        let transitions = transaction.transitions().collect::<Vec<_>>();
773        assert!(!transitions.is_empty());
774
775        // Ensure the transaction is found.
776        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        // Ensure the transaction is not found.
782        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        // Retrieve the commitments.
797        let commitments = transaction.commitments().collect::<Vec<_>>();
798        assert!(!commitments.is_empty());
799
800        // Ensure the commitments are found.
801        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        // Ensure the commitments are not found.
807        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        // Retrieve the transitions.
822        let transitions = transaction.transitions().collect::<Vec<_>>();
823        assert!(!transitions.is_empty());
824
825        // Ensure the transitions are found.
826        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        // Ensure the transitions are not found.
833        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        // Retrieve the transitions.
849        let transitions = transaction.transitions().collect::<Vec<_>>();
850        assert!(!transitions.is_empty());
851
852        for transition in transitions {
853            // Retrieve the commitments.
854            let commitments = transition.commitments().collect::<Vec<_>>();
855            assert!(!commitments.is_empty());
856
857            // Ensure the commitments are found.
858            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        // Ensure the commitments are not found.
866        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        // Retrieve the records.
882        let records = transaction.records().collect::<IndexMap<_, _>>();
883        assert!(!records.is_empty());
884
885        // Ensure the records are found.
886        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        // Ensure the records are not found.
893        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}