snarkvm_ledger_debug/check_next_block.rs
1// Copyright (C) 2019-2023 Aleo Systems 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// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::*;
16
17use rand::{rngs::StdRng, SeedableRng};
18
19impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
20 /// Checks the given block is valid next block.
21 pub fn check_next_block<R: CryptoRng + Rng>(&self, block: &Block<N>, rng: &mut R) -> Result<()> {
22 let height = block.height();
23
24 // Ensure the block hash does not already exist.
25 if self.contains_block_hash(&block.hash())? {
26 bail!("Block hash '{}' already exists in the ledger", block.hash())
27 }
28
29 // Ensure the block height does not already exist.
30 if self.contains_block_height(block.height())? {
31 bail!("Block height '{height}' already exists in the ledger")
32 }
33
34 // Ensure the solutions do not already exist.
35 if let Some(solutions) = block.solutions() {
36 for puzzle_commitment in solutions.puzzle_commitments() {
37 if self.contains_puzzle_commitment(puzzle_commitment)? {
38 bail!("Puzzle commitment {puzzle_commitment} already exists in the ledger");
39 }
40 }
41 }
42
43 // Ensure each transaction is well-formed and unique.
44 // TODO: this intermediate allocation shouldn't be necessary; this is most likely https://github.com/rust-lang/rust/issues/89418.
45 let transactions = block.transactions().iter().collect::<Vec<_>>();
46 let rngs = (0..transactions.len()).map(|_| StdRng::from_seed(rng.gen())).collect::<Vec<_>>();
47 cfg_iter!(transactions).zip(rngs).try_for_each(|(transaction, mut rng)| {
48 self.check_transaction_basic(*transaction, transaction.to_rejected_id()?, &mut rng)
49 .map_err(|e| anyhow!("Invalid transaction found in the transactions list: {e}"))
50 })?;
51
52 // TODO (howardwu): Remove this after moving the total supply into credits.aleo.
53 {
54 // // Retrieve the latest total supply.
55 // let latest_total_supply = self.latest_total_supply_in_microcredits();
56 // // Retrieve the block reward from the first block ratification.
57 // let block_reward = match block.ratifications()[0] {
58 // Ratify::BlockReward(block_reward) => block_reward,
59 // _ => bail!("Block {height} is invalid - the first ratification must be a block reward"),
60 // };
61 // // Retrieve the puzzle reward from the second block ratification.
62 // let puzzle_reward = match block.ratifications()[1] {
63 // Ratify::PuzzleReward(puzzle_reward) => puzzle_reward,
64 // _ => bail!("Block {height} is invalid - the second ratification must be a puzzle reward"),
65 // };
66 // // Compute the next total supply in microcredits.
67 // let next_total_supply_in_microcredits =
68 // update_total_supply(latest_total_supply, block_reward, puzzle_reward, block.transactions())?;
69 // // Ensure the total supply in microcredits is correct.
70 // if next_total_supply_in_microcredits != block.total_supply_in_microcredits() {
71 // bail!("Invalid total supply in microcredits")
72 // }
73 }
74
75 // Construct the finalize state.
76 let state = FinalizeGlobalState::new::<N>(
77 block.round(),
78 block.height(),
79 block.cumulative_weight(),
80 block.cumulative_proof_target(),
81 block.previous_hash(),
82 )?;
83
84 // Ensure speculation over the unconfirmed transactions is correct.
85 let ratified_finalize_operations =
86 self.vm.check_speculate(state, block.ratifications(), block.solutions(), block.transactions())?;
87
88 // Ensure the block is correct.
89 block.verify(
90 &self.latest_block(),
91 self.latest_state_root(),
92 &self.latest_committee()?,
93 self.coinbase_puzzle(),
94 &self.latest_epoch_challenge()?,
95 OffsetDateTime::now_utc().unix_timestamp(),
96 ratified_finalize_operations,
97 )?;
98
99 Ok(())
100 }
101}