snarkvm_ledger_block/
lib.rs

1// Copyright (c) 2019-2025 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    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        // 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 transmission IDs from the subdag correspond to the block.
202                Self::check_subdag_transmissions(
203                    subdag,
204                    &solutions,
205                    &aborted_solution_ids,
206                    &transactions,
207                    &aborted_transaction_ids,
208                )?;
209            }
210        }
211
212        // Ensure that coinbase accumulator matches the solutions.
213        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        // Ensure that the subdag root matches the authority.
218        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        // Return the block.
227        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    /// Initializes a new block from the given block hash, previous block hash, block header,
241    /// authority, ratifications, solutions, transactions, and aborted transaction IDs.
242    /// This is only called by [`Block::from`], which validates the block components.
243    fn from_unchecked(
244        block_hash: N::BlockHash,
245        previous_hash: N::BlockHash,
246        header: Header<N>,
247        authority: Authority<N>,
248        ratifications: Ratifications<N>,
249        solutions: Solutions<N>,
250        aborted_solution_ids: Vec<SolutionID<N>>,
251        transactions: Transactions<N>,
252        aborted_transaction_ids: Vec<N::TransactionID>,
253    ) -> Result<Self> {
254        // Return the block.
255        Ok(Self {
256            block_hash,
257            previous_hash,
258            header,
259            authority,
260            ratifications,
261            solutions,
262            aborted_solution_ids,
263            transactions,
264            aborted_transaction_ids,
265        })
266    }
267}
268
269impl<N: Network> Block<N> {
270    /// Returns the block hash.
271    pub const fn hash(&self) -> N::BlockHash {
272        self.block_hash
273    }
274
275    /// Returns the previous block hash.
276    pub const fn previous_hash(&self) -> N::BlockHash {
277        self.previous_hash
278    }
279
280    /// Returns the authority.
281    pub const fn authority(&self) -> &Authority<N> {
282        &self.authority
283    }
284
285    /// Returns the ratifications in this block.
286    pub const fn ratifications(&self) -> &Ratifications<N> {
287        &self.ratifications
288    }
289
290    /// Returns the solutions in the block.
291    pub const fn solutions(&self) -> &Solutions<N> {
292        &self.solutions
293    }
294
295    /// Returns the aborted solution IDs in this block.
296    pub const fn aborted_solution_ids(&self) -> &Vec<SolutionID<N>> {
297        &self.aborted_solution_ids
298    }
299
300    /// Returns the transactions in this block.
301    pub const fn transactions(&self) -> &Transactions<N> {
302        &self.transactions
303    }
304
305    /// Returns the aborted transaction IDs in this block.
306    pub const fn aborted_transaction_ids(&self) -> &Vec<N::TransactionID> {
307        &self.aborted_transaction_ids
308    }
309}
310
311impl<N: Network> Block<N> {
312    /// Returns the block header.
313    pub const fn header(&self) -> &Header<N> {
314        &self.header
315    }
316
317    /// Returns the previous state root from the block header.
318    pub const fn previous_state_root(&self) -> N::StateRoot {
319        self.header.previous_state_root()
320    }
321
322    /// Returns the transactions root in the block header.
323    pub const fn transactions_root(&self) -> Field<N> {
324        self.header.transactions_root()
325    }
326
327    /// Returns the finalize root in the block header.
328    pub const fn finalize_root(&self) -> Field<N> {
329        self.header.finalize_root()
330    }
331
332    /// Returns the ratifications root in the block header.
333    pub const fn ratifications_root(&self) -> Field<N> {
334        self.header.ratifications_root()
335    }
336
337    /// Returns the solutions root in the block header.
338    pub const fn solutions_root(&self) -> Field<N> {
339        self.header.solutions_root()
340    }
341
342    /// Returns the metadata in the block header.
343    pub const fn metadata(&self) -> &Metadata<N> {
344        self.header.metadata()
345    }
346
347    /// Returns the network ID of this block.
348    pub const fn network(&self) -> u16 {
349        self.header.network()
350    }
351
352    /// Returns the height of this block.
353    pub const fn height(&self) -> u32 {
354        self.header.height()
355    }
356
357    /// Returns the round number of this block.
358    pub const fn round(&self) -> u64 {
359        self.header.round()
360    }
361
362    /// Returns the epoch number of this block.
363    pub const fn epoch_number(&self) -> u32 {
364        self.height() / N::NUM_BLOCKS_PER_EPOCH
365    }
366
367    /// Returns the cumulative weight for this block.
368    pub const fn cumulative_weight(&self) -> u128 {
369        self.header.cumulative_weight()
370    }
371
372    /// Returns the cumulative proof target for this block.
373    pub const fn cumulative_proof_target(&self) -> u128 {
374        self.header.cumulative_proof_target()
375    }
376
377    /// Returns the coinbase target for this block.
378    pub const fn coinbase_target(&self) -> u64 {
379        self.header.coinbase_target()
380    }
381
382    /// Returns the proof target for this block.
383    pub const fn proof_target(&self) -> u64 {
384        self.header.proof_target()
385    }
386
387    /// Returns the coinbase target of the last coinbase.
388    pub const fn last_coinbase_target(&self) -> u64 {
389        self.header.last_coinbase_target()
390    }
391
392    /// Returns the block timestamp of the last coinbase.
393    pub const fn last_coinbase_timestamp(&self) -> i64 {
394        self.header.last_coinbase_timestamp()
395    }
396
397    /// Returns the Unix timestamp (UTC) for this block.
398    pub const fn timestamp(&self) -> i64 {
399        self.header.timestamp()
400    }
401}
402
403impl<N: Network> Block<N> {
404    /// Returns `true` if the block contains the given transition ID.
405    pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
406        self.transactions.contains_transition(transition_id)
407    }
408
409    /// Returns `true` if the block contains the given serial number.
410    pub fn contains_serial_number(&self, serial_number: &Field<N>) -> bool {
411        self.transactions.contains_serial_number(serial_number)
412    }
413
414    /// Returns `true` if the block contains the given commitment.
415    pub fn contains_commitment(&self, commitment: &Field<N>) -> bool {
416        self.transactions.contains_commitment(commitment)
417    }
418}
419
420impl<N: Network> Block<N> {
421    /// Returns the solution with the given solution ID, if it exists.
422    pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Option<&Solution<N>> {
423        self.solutions.as_ref().and_then(|solution| solution.get_solution(solution_id))
424    }
425
426    /// Returns the transaction with the given transaction ID, if it exists.
427    pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Option<&Transaction<N>> {
428        self.transactions.get(transaction_id).map(|t| t.deref())
429    }
430
431    /// Returns the confirmed transaction with the given transaction ID, if it exists.
432    pub fn get_confirmed_transaction(&self, transaction_id: &N::TransactionID) -> Option<&ConfirmedTransaction<N>> {
433        self.transactions.get(transaction_id)
434    }
435}
436
437impl<N: Network> Block<N> {
438    /// Returns the transaction with the given transition ID, if it exists.
439    pub fn find_transaction_for_transition_id(&self, transition_id: &N::TransitionID) -> Option<&Transaction<N>> {
440        self.transactions.find_transaction_for_transition_id(transition_id)
441    }
442
443    /// Returns the transaction with the given serial number, if it exists.
444    pub fn find_transaction_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transaction<N>> {
445        self.transactions.find_transaction_for_serial_number(serial_number)
446    }
447
448    /// Returns the transaction with the given commitment, if it exists.
449    pub fn find_transaction_for_commitment(&self, commitment: &Field<N>) -> Option<&Transaction<N>> {
450        self.transactions.find_transaction_for_commitment(commitment)
451    }
452
453    /// Returns the transition with the corresponding transition ID, if it exists.
454    pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
455        self.transactions.find_transition(transition_id)
456    }
457
458    /// Returns the transition for the given serial number, if it exists.
459    pub fn find_transition_for_serial_number(&self, serial_number: &Field<N>) -> Option<&Transition<N>> {
460        self.transactions.find_transition_for_serial_number(serial_number)
461    }
462
463    /// Returns the transition for the given commitment, if it exists.
464    pub fn find_transition_for_commitment(&self, commitment: &Field<N>) -> Option<&Transition<N>> {
465        self.transactions.find_transition_for_commitment(commitment)
466    }
467
468    /// Returns the record with the corresponding commitment, if it exists.
469    pub fn find_record(&self, commitment: &Field<N>) -> Option<&Record<N, Ciphertext<N>>> {
470        self.transactions.find_record(commitment)
471    }
472}
473
474impl<N: Network> Block<N> {
475    /// Returns an iterator over the solution IDs in this block.
476    pub fn solution_ids(&self) -> Option<impl '_ + Iterator<Item = &SolutionID<N>>> {
477        self.solutions.as_ref().map(|solution| solution.solution_ids())
478    }
479
480    /// Returns an iterator over the transaction IDs, for all transactions in `self`.
481    pub fn transaction_ids(&self) -> impl '_ + Iterator<Item = &N::TransactionID> {
482        self.transactions.transaction_ids()
483    }
484
485    /// Returns an iterator over all transactions in `self` that are accepted deploy transactions.
486    pub fn deployments(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
487        self.transactions.deployments()
488    }
489
490    /// Returns an iterator over all transactions in `self` that are accepted execute transactions.
491    pub fn executions(&self) -> impl '_ + Iterator<Item = &ConfirmedTransaction<N>> {
492        self.transactions.executions()
493    }
494
495    /// Returns an iterator over all transitions.
496    pub fn transitions(&self) -> impl '_ + Iterator<Item = &Transition<N>> {
497        self.transactions.transitions()
498    }
499
500    /// Returns an iterator over the transition IDs, for all transitions.
501    pub fn transition_ids(&self) -> impl '_ + Iterator<Item = &N::TransitionID> {
502        self.transactions.transition_ids()
503    }
504
505    /// Returns an iterator over the transition public keys, for all transactions.
506    pub fn transition_public_keys(&self) -> impl '_ + Iterator<Item = &Group<N>> {
507        self.transactions.transition_public_keys()
508    }
509
510    /// Returns an iterator over the transition commitments, for all transactions.
511    pub fn transition_commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
512        self.transactions.transition_commitments()
513    }
514
515    /// Returns an iterator over the tags, for all transition inputs that are records.
516    pub fn tags(&self) -> impl '_ + Iterator<Item = &Field<N>> {
517        self.transactions.tags()
518    }
519
520    /// Returns an iterator over the input IDs, for all transition inputs that are records.
521    pub fn input_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
522        self.transactions.input_ids()
523    }
524
525    /// Returns an iterator over the serial numbers, for all transition inputs that are records.
526    pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = &Field<N>> {
527        self.transactions.serial_numbers()
528    }
529
530    /// Returns an iterator over the output IDs, for all transition inputs that are records.
531    pub fn output_ids(&self) -> impl '_ + Iterator<Item = &Field<N>> {
532        self.transactions.output_ids()
533    }
534
535    /// Returns an iterator over the commitments, for all transition outputs that are records.
536    pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
537        self.transactions.commitments()
538    }
539
540    /// Returns an iterator over the records, for all transition outputs that are records.
541    pub fn records(&self) -> impl '_ + Iterator<Item = (&Field<N>, &Record<N, Ciphertext<N>>)> {
542        self.transactions.records()
543    }
544
545    /// Returns an iterator over the nonces, for all transition outputs that are records.
546    pub fn nonces(&self) -> impl '_ + Iterator<Item = &Group<N>> {
547        self.transactions.nonces()
548    }
549
550    /// Returns an iterator over the transaction fee amounts, for all transactions.
551    pub fn transaction_fee_amounts(&self) -> impl '_ + Iterator<Item = Result<U64<N>>> {
552        self.transactions.transaction_fee_amounts()
553    }
554}
555
556impl<N: Network> Block<N> {
557    /// Returns a consuming iterator over the transaction IDs, for all transactions in `self`.
558    pub fn into_transaction_ids(self) -> impl Iterator<Item = N::TransactionID> {
559        self.transactions.into_transaction_ids()
560    }
561
562    /// Returns a consuming iterator over all transactions in `self` that are accepted deploy transactions.
563    pub fn into_deployments(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
564        self.transactions.into_deployments()
565    }
566
567    /// Returns a consuming iterator over all transactions in `self` that are accepted execute transactions.
568    pub fn into_executions(self) -> impl Iterator<Item = ConfirmedTransaction<N>> {
569        self.transactions.into_executions()
570    }
571
572    /// Returns a consuming iterator over all transitions.
573    pub fn into_transitions(self) -> impl Iterator<Item = Transition<N>> {
574        self.transactions.into_transitions()
575    }
576
577    /// Returns a consuming iterator over the transition IDs, for all transitions.
578    pub fn into_transition_ids(self) -> impl Iterator<Item = N::TransitionID> {
579        self.transactions.into_transition_ids()
580    }
581
582    /// Returns a consuming iterator over the transition public keys, for all transactions.
583    pub fn into_transition_public_keys(self) -> impl Iterator<Item = Group<N>> {
584        self.transactions.into_transition_public_keys()
585    }
586
587    /// Returns a consuming iterator over the tags, for all transition inputs that are records.
588    pub fn into_tags(self) -> impl Iterator<Item = Field<N>> {
589        self.transactions.into_tags()
590    }
591
592    /// Returns a consuming iterator over the serial numbers, for all transition inputs that are records.
593    pub fn into_serial_numbers(self) -> impl Iterator<Item = Field<N>> {
594        self.transactions.into_serial_numbers()
595    }
596
597    /// Returns a consuming iterator over the commitments, for all transition outputs that are records.
598    pub fn into_commitments(self) -> impl Iterator<Item = Field<N>> {
599        self.transactions.into_commitments()
600    }
601
602    /// Returns a consuming iterator over the records, for all transition outputs that are records.
603    pub fn into_records(self) -> impl Iterator<Item = (Field<N>, Record<N, Ciphertext<N>>)> {
604        self.transactions.into_records()
605    }
606
607    /// Returns a consuming iterator over the nonces, for all transition outputs that are records.
608    pub fn into_nonces(self) -> impl Iterator<Item = Group<N>> {
609        self.transactions.into_nonces()
610    }
611}
612
613#[cfg(test)]
614pub mod test_helpers {
615    use super::*;
616    use console::account::{Address, PrivateKey};
617    use snarkvm_algorithms::snark::varuna::VarunaVersion;
618    use snarkvm_ledger_query::Query;
619    use snarkvm_ledger_store::{BlockStore, helpers::memory::BlockMemory};
620    use snarkvm_synthesizer_process::Process;
621
622    use aleo_std::StorageMode;
623    use std::sync::OnceLock;
624
625    type CurrentNetwork = console::network::MainnetV0;
626    type CurrentAleo = snarkvm_circuit::network::AleoV0;
627
628    /// Samples a random genesis block.
629    pub(crate) fn sample_genesis_block(rng: &mut TestRng) -> Block<CurrentNetwork> {
630        // Sample the genesis block and components.
631        let (block, _, _) = sample_genesis_block_and_components(rng);
632        // Return the block.
633        block
634    }
635
636    /// Samples a random genesis block and the transaction from the genesis block.
637    pub(crate) fn sample_genesis_block_and_transaction(
638        rng: &mut TestRng,
639    ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>) {
640        // Sample the genesis block and components.
641        let (block, transaction, _) = sample_genesis_block_and_components(rng);
642        // Return the block and transaction.
643        (block, transaction)
644    }
645
646    /// Samples a random genesis block, the transaction from the genesis block, and the genesis private key.
647    pub(crate) fn sample_genesis_block_and_components(
648        rng: &mut TestRng,
649    ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
650        static INSTANCE: OnceLock<(Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>)> =
651            OnceLock::new();
652        INSTANCE.get_or_init(|| sample_genesis_block_and_components_raw(rng)).clone()
653    }
654
655    /// Samples a random genesis block, the transaction from the genesis block, and the genesis private key.
656    fn sample_genesis_block_and_components_raw(
657        rng: &mut TestRng,
658    ) -> (Block<CurrentNetwork>, Transaction<CurrentNetwork>, PrivateKey<CurrentNetwork>) {
659        // Sample the genesis private key.
660        let private_key = PrivateKey::new(rng).unwrap();
661        let address = Address::<CurrentNetwork>::try_from(private_key).unwrap();
662
663        // Prepare the locator.
664        let locator = ("credits.aleo", "transfer_public_to_private");
665        // Prepare the amount for each call to the function.
666        let amount = 100_000_000u64;
667        // Prepare the function inputs.
668        let inputs = [address.to_string(), format!("{amount}_u64")];
669
670        // Initialize the process.
671        let process = Process::load().unwrap();
672        // Authorize the function.
673        let authorization =
674            process.authorize::<CurrentAleo, _>(&private_key, locator.0, locator.1, inputs.iter(), rng).unwrap();
675        // Execute the function.
676        let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
677
678        // Initialize a new block store.
679        let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
680
681        // Prepare the assignments.
682        trace.prepare(&Query::from(block_store)).unwrap();
683        // Compute the proof and construct the execution.
684        let execution = trace.prove_execution::<CurrentAleo, _>(locator.0, VarunaVersion::V1, rng).unwrap();
685        // Convert the execution.
686        // Note: This is a testing-only hack to adhere to Rust's dependency cycle rules.
687        let execution = Execution::from_str(&execution.to_string()).unwrap();
688
689        // Construct the transaction.
690        let transaction = Transaction::from_execution(execution, None).unwrap();
691        // Prepare the confirmed transaction.
692        let confirmed = ConfirmedTransaction::accepted_execute(0, transaction.clone(), vec![]).unwrap();
693        // Prepare the transactions.
694        let transactions = Transactions::from_iter([confirmed]);
695
696        // Construct the ratifications.
697        let ratifications = Ratifications::try_from(vec![]).unwrap();
698
699        // Prepare the block header.
700        let header = Header::genesis(&ratifications, &transactions, vec![]).unwrap();
701        // Prepare the previous block hash.
702        let previous_hash = <CurrentNetwork as Network>::BlockHash::default();
703
704        // Construct the block.
705        let block = Block::new_beacon(
706            &private_key,
707            previous_hash,
708            header,
709            ratifications,
710            None.into(),
711            vec![],
712            transactions,
713            vec![],
714            rng,
715        )
716        .unwrap();
717        assert!(block.header().is_genesis(), "Failed to initialize a genesis block");
718        // Return the block, transaction, and private key.
719        (block, transaction, private_key)
720    }
721
722    pub(crate) fn sample_metadata() -> Metadata<CurrentNetwork> {
723        let network = CurrentNetwork::ID;
724        let round = u64::MAX;
725        let height = u32::MAX;
726        let cumulative_weight = u128::MAX - 1;
727        let cumulative_proof_target = u128::MAX - 1;
728        let coinbase_target = u64::MAX;
729        let proof_target = u64::MAX - 1;
730        let last_coinbase_target = u64::MAX;
731        let timestamp = i64::MAX - 1;
732        let last_coinbase_timestamp = timestamp - 1;
733        Metadata::new(
734            network,
735            round,
736            height,
737            cumulative_weight,
738            cumulative_proof_target,
739            coinbase_target,
740            proof_target,
741            last_coinbase_target,
742            last_coinbase_timestamp,
743            timestamp,
744        )
745        .unwrap()
746    }
747}
748
749#[cfg(test)]
750mod tests {
751    use super::*;
752
753    use console::network::MainnetV0;
754    use indexmap::IndexMap;
755    type CurrentNetwork = MainnetV0;
756
757    #[test]
758    fn test_find_transaction_for_transition_id() {
759        let rng = &mut TestRng::default();
760
761        let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
762        let transactions = block.transactions();
763
764        // Retrieve the transitions.
765        let transitions = transaction.transitions().collect::<Vec<_>>();
766        assert!(!transitions.is_empty());
767
768        // Ensure the transaction is found.
769        for transition in transitions {
770            assert_eq!(block.find_transaction_for_transition_id(transition.id()), Some(&transaction));
771            assert_eq!(transactions.find_transaction_for_transition_id(transition.id()), Some(&transaction));
772        }
773
774        // Ensure the transaction is not found.
775        for _ in 0..10 {
776            let transition_id = &rng.r#gen();
777            assert_eq!(block.find_transaction_for_transition_id(transition_id), None);
778            assert_eq!(transactions.find_transaction_for_transition_id(transition_id), None);
779        }
780    }
781
782    #[test]
783    fn test_find_transaction_for_commitment() {
784        let rng = &mut TestRng::default();
785
786        let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
787        let transactions = block.transactions();
788
789        // Retrieve the commitments.
790        let commitments = transaction.commitments().collect::<Vec<_>>();
791        assert!(!commitments.is_empty());
792
793        // Ensure the commitments are found.
794        for commitment in commitments {
795            assert_eq!(block.find_transaction_for_commitment(commitment), Some(&transaction));
796            assert_eq!(transactions.find_transaction_for_commitment(commitment), Some(&transaction));
797        }
798
799        // Ensure the commitments are not found.
800        for _ in 0..10 {
801            let commitment = &rng.r#gen();
802            assert_eq!(block.find_transaction_for_commitment(commitment), None);
803            assert_eq!(transactions.find_transaction_for_commitment(commitment), None);
804        }
805    }
806
807    #[test]
808    fn test_find_transition() {
809        let rng = &mut TestRng::default();
810
811        let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
812        let transactions = block.transactions();
813
814        // Retrieve the transitions.
815        let transitions = transaction.transitions().collect::<Vec<_>>();
816        assert!(!transitions.is_empty());
817
818        // Ensure the transitions are found.
819        for transition in transitions {
820            assert_eq!(block.find_transition(transition.id()), Some(transition));
821            assert_eq!(transactions.find_transition(transition.id()), Some(transition));
822            assert_eq!(transaction.find_transition(transition.id()), Some(transition));
823        }
824
825        // Ensure the transitions are not found.
826        for _ in 0..10 {
827            let transition_id = &rng.r#gen();
828            assert_eq!(block.find_transition(transition_id), None);
829            assert_eq!(transactions.find_transition(transition_id), None);
830            assert_eq!(transaction.find_transition(transition_id), None);
831        }
832    }
833
834    #[test]
835    fn test_find_transition_for_commitment() {
836        let rng = &mut TestRng::default();
837
838        let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
839        let transactions = block.transactions();
840
841        // Retrieve the transitions.
842        let transitions = transaction.transitions().collect::<Vec<_>>();
843        assert!(!transitions.is_empty());
844
845        for transition in transitions {
846            // Retrieve the commitments.
847            let commitments = transition.commitments().collect::<Vec<_>>();
848            assert!(!commitments.is_empty());
849
850            // Ensure the commitments are found.
851            for commitment in commitments {
852                assert_eq!(block.find_transition_for_commitment(commitment), Some(transition));
853                assert_eq!(transactions.find_transition_for_commitment(commitment), Some(transition));
854                assert_eq!(transaction.find_transition_for_commitment(commitment), Some(transition));
855            }
856        }
857
858        // Ensure the commitments are not found.
859        for _ in 0..10 {
860            let commitment = &rng.r#gen();
861            assert_eq!(block.find_transition_for_commitment(commitment), None);
862            assert_eq!(transactions.find_transition_for_commitment(commitment), None);
863            assert_eq!(transaction.find_transition_for_commitment(commitment), None);
864        }
865    }
866
867    #[test]
868    fn test_find_record() {
869        let rng = &mut TestRng::default();
870
871        let (block, transaction) = crate::test_helpers::sample_genesis_block_and_transaction(rng);
872        let transactions = block.transactions();
873
874        // Retrieve the records.
875        let records = transaction.records().collect::<IndexMap<_, _>>();
876        assert!(!records.is_empty());
877
878        // Ensure the records are found.
879        for (commitment, record) in records {
880            assert_eq!(block.find_record(commitment), Some(record));
881            assert_eq!(transactions.find_record(commitment), Some(record));
882            assert_eq!(transaction.find_record(commitment), Some(record));
883        }
884
885        // Ensure the records are not found.
886        for _ in 0..10 {
887            let commitment = &rng.r#gen();
888            assert_eq!(block.find_record(commitment), None);
889            assert_eq!(transactions.find_record(commitment), None);
890            assert_eq!(transaction.find_record(commitment), None);
891        }
892    }
893
894    #[test]
895    fn test_serde_metadata() {
896        let metadata = crate::test_helpers::sample_metadata();
897        let json_metadata = serde_json::to_string(&metadata).unwrap();
898        let deserialized_metadata: Metadata<CurrentNetwork> = serde_json::from_str(&json_metadata).unwrap();
899        assert_eq!(metadata, deserialized_metadata);
900    }
901}