snarkvm_ledger_debug/
advance.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
17impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
18    /// Returns a candidate for the next block in the ledger, using a committed subdag and its transmissions.
19    pub fn prepare_advance_to_next_quorum_block(
20        &self,
21        subdag: Subdag<N>,
22        transmissions: IndexMap<TransmissionID<N>, Transmission<N>>,
23    ) -> Result<Block<N>> {
24        // Retrieve the latest block as the previous block (for the next block).
25        let previous_block = self.latest_block();
26
27        // Decouple the transmissions into ratifications, solutions, and transactions.
28        let (ratifications, solutions, transactions) = decouple_transmissions(transmissions.into_iter())?;
29        // Currently, we do not support ratifications from the memory pool.
30        ensure!(ratifications.is_empty(), "Ratifications are currently unsupported from the memory pool");
31        // Construct the block template.
32        let (header, ratifications, solutions, transactions, aborted_transaction_ids) =
33            self.construct_block_template(&previous_block, Some(&subdag), ratifications, solutions, transactions)?;
34
35        // Construct the new quorum block.
36        Block::new_quorum(
37            previous_block.hash(),
38            header,
39            subdag,
40            ratifications,
41            solutions,
42            transactions,
43            aborted_transaction_ids,
44        )
45    }
46
47    /// Returns a candidate for the next block in the ledger.
48    pub fn prepare_advance_to_next_beacon_block<R: Rng + CryptoRng>(
49        &self,
50        private_key: &PrivateKey<N>,
51        candidate_ratifications: Vec<Ratify<N>>,
52        candidate_solutions: Vec<ProverSolution<N>>,
53        candidate_transactions: Vec<Transaction<N>>,
54        rng: &mut R,
55    ) -> Result<Block<N>> {
56        // Currently, we do not support ratifications from the memory pool.
57        ensure!(candidate_ratifications.is_empty(), "Ratifications are currently unsupported from the memory pool");
58
59        // Retrieve the latest block as the previous block (for the next block).
60        let previous_block = self.latest_block();
61
62        // Construct the block template.
63        let (header, ratifications, solutions, transactions, aborted_transaction_ids) = self.construct_block_template(
64            &previous_block,
65            None,
66            candidate_ratifications,
67            candidate_solutions,
68            candidate_transactions,
69        )?;
70
71        // Construct the new beacon block.
72        Block::new_beacon(
73            private_key,
74            previous_block.hash(),
75            header,
76            ratifications,
77            solutions,
78            transactions,
79            aborted_transaction_ids,
80            rng,
81        )
82    }
83
84    /// Adds the given block as the next block in the ledger.
85    pub fn advance_to_next_block(&self, block: &Block<N>) -> Result<()> {
86        // Acquire the write lock on the current block.
87        let mut current_block = self.current_block.write();
88        // Update the VM.
89        self.vm.add_next_block(block)?;
90        // Update the current block.
91        *current_block = block.clone();
92        // Drop the write lock on the current block.
93        drop(current_block);
94
95        // Update the cached committee from storage.
96        if let Ok(current_committee) = self.vm.finalize_store().committee_store().current_committee() {
97            *self.current_committee.write() = Some(current_committee);
98        }
99
100        // If the block is the start of a new epoch, or the epoch challenge has not been set, update the current epoch challenge.
101        if block.height() % N::NUM_BLOCKS_PER_EPOCH == 0 || self.current_epoch_challenge.read().is_none() {
102            // Update the current epoch challenge.
103            self.current_epoch_challenge.write().clone_from(&self.get_epoch_challenge(block.height()).ok());
104        }
105
106        Ok(())
107    }
108}
109
110impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
111    /// Constructs a block template for the next block in the ledger.
112    #[allow(clippy::type_complexity)]
113    fn construct_block_template(
114        &self,
115        previous_block: &Block<N>,
116        subdag: Option<&Subdag<N>>,
117        candidate_ratifications: Vec<Ratify<N>>,
118        candidate_solutions: Vec<ProverSolution<N>>,
119        candidate_transactions: Vec<Transaction<N>>,
120    ) -> Result<(Header<N>, Ratifications<N>, Option<CoinbaseSolution<N>>, Transactions<N>, Vec<N::TransactionID>)>
121    {
122        // Construct the solutions.
123        let (solutions, solutions_root, combined_proof_target) = match candidate_solutions.is_empty() {
124            true => (None, Field::<N>::zero(), 0u128),
125            false => {
126                // Retrieve the coinbase verifying key.
127                let coinbase_verifying_key = self.coinbase_puzzle.coinbase_verifying_key();
128                // Retrieve the latest epoch challenge.
129                let latest_epoch_challenge = self.latest_epoch_challenge()?;
130                // TODO: For mainnet - Add `aborted_solution_ids` to the block. And optimize this logic.
131                // Verify the candidate solutions.
132                let verification_results: Vec<_> = cfg_into_iter!(candidate_solutions)
133                    .map(|solution| {
134                        (
135                            solution,
136                            solution
137                                .verify(coinbase_verifying_key, &latest_epoch_challenge, self.latest_proof_target())
138                                .unwrap_or(false),
139                        )
140                    })
141                    .collect();
142                // Separate the candidate solutions into valid and aborted solutions.
143                let mut valid_candidate_solutions = Vec::with_capacity(N::MAX_SOLUTIONS);
144                let mut aborted_candidate_solutions = Vec::new();
145                for (solution, is_valid) in verification_results.into_iter() {
146                    if is_valid && valid_candidate_solutions.len() < N::MAX_SOLUTIONS {
147                        valid_candidate_solutions.push(solution);
148                    } else {
149                        aborted_candidate_solutions.push(solution);
150                    }
151                }
152
153                // Check if there are any valid solutions.
154                match valid_candidate_solutions.is_empty() {
155                    true => (None, Field::<N>::zero(), 0u128),
156                    false => {
157                        // Construct the solutions.
158                        let solutions = CoinbaseSolution::new(valid_candidate_solutions)?;
159                        // Compute the solutions root.
160                        let solutions_root = solutions.to_accumulator_point()?;
161                        // Compute the combined proof target.
162                        let combined_proof_target = solutions.to_combined_proof_target()?;
163                        // Output the solutions, solutions root, and combined proof target.
164                        (Some(solutions), solutions_root, combined_proof_target)
165                    }
166                }
167            }
168        };
169
170        // Retrieve the latest state root.
171        let latest_state_root = self.latest_state_root();
172        // Retrieve the latest cumulative proof target.
173        let latest_cumulative_proof_target = previous_block.cumulative_proof_target();
174        // Retrieve the latest coinbase target.
175        let latest_coinbase_target = previous_block.coinbase_target();
176
177        // Compute the next round number.
178        let next_round = match subdag {
179            Some(subdag) => subdag.anchor_round(),
180            None => previous_block.round().saturating_add(1),
181        };
182        // Compute the next height.
183        let next_height = previous_block.height().saturating_add(1);
184        // Determine the timestamp for the next block.
185        let next_timestamp = match subdag {
186            Some(subdag) => subdag.timestamp(),
187            None => OffsetDateTime::now_utc().unix_timestamp(),
188        };
189        // Compute the next cumulative weight.
190        let next_cumulative_weight = previous_block.cumulative_weight().saturating_add(combined_proof_target);
191        // Compute the next cumulative proof target.
192        let next_cumulative_proof_target = latest_cumulative_proof_target.saturating_add(combined_proof_target);
193        // Determine if the coinbase target is reached.
194        let is_coinbase_target_reached = next_cumulative_proof_target >= latest_coinbase_target as u128;
195        // Update the next cumulative proof target, if necessary.
196        let next_cumulative_proof_target = match is_coinbase_target_reached {
197            true => 0,
198            false => next_cumulative_proof_target,
199        };
200        // Construct the next coinbase target.
201        let next_coinbase_target = coinbase_target(
202            previous_block.last_coinbase_target(),
203            previous_block.last_coinbase_timestamp(),
204            next_timestamp,
205            N::ANCHOR_TIME,
206            N::NUM_BLOCKS_PER_EPOCH,
207            N::GENESIS_COINBASE_TARGET,
208        )?;
209        // Construct the next proof target.
210        let next_proof_target = proof_target(next_coinbase_target, N::GENESIS_PROOF_TARGET);
211
212        // Construct the next last coinbase target and next last coinbase timestamp.
213        let (next_last_coinbase_target, next_last_coinbase_timestamp) = match is_coinbase_target_reached {
214            true => (next_coinbase_target, next_timestamp),
215            false => (previous_block.last_coinbase_target(), previous_block.last_coinbase_timestamp()),
216        };
217
218        // Calculate the coinbase reward.
219        let coinbase_reward = coinbase_reward(
220            next_height,
221            N::STARTING_SUPPLY,
222            N::ANCHOR_HEIGHT,
223            N::BLOCK_TIME,
224            combined_proof_target,
225            u64::try_from(latest_cumulative_proof_target)?,
226            latest_coinbase_target,
227        )?;
228
229        // Construct the finalize state.
230        let state = FinalizeGlobalState::new::<N>(
231            next_round,
232            next_height,
233            next_cumulative_weight,
234            next_cumulative_proof_target,
235            previous_block.hash(),
236        )?;
237        // Speculate over the ratifications, solutions, and transactions.
238        let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) = self.vm.speculate(
239            state,
240            Some(coinbase_reward),
241            candidate_ratifications,
242            solutions.as_ref(),
243            candidate_transactions.iter(),
244        )?;
245
246        // Compute the ratifications root.
247        let ratifications_root = ratifications.to_ratifications_root()?;
248
249        // Construct the subdag root.
250        let subdag_root = match subdag {
251            Some(subdag) => subdag.to_subdag_root()?,
252            None => Field::zero(),
253        };
254
255        // Construct the metadata.
256        let metadata = Metadata::new(
257            N::ID,
258            next_round,
259            next_height,
260            next_cumulative_weight,
261            next_cumulative_proof_target,
262            next_coinbase_target,
263            next_proof_target,
264            next_last_coinbase_target,
265            next_last_coinbase_timestamp,
266            next_timestamp,
267        )?;
268
269        // Construct the header.
270        let header = Header::from(
271            latest_state_root,
272            transactions.to_transactions_root()?,
273            transactions.to_finalize_root(ratified_finalize_operations)?,
274            ratifications_root,
275            solutions_root,
276            subdag_root,
277            metadata,
278        )?;
279
280        // Return the block template.
281        Ok((header, ratifications, solutions, transactions, aborted_transaction_ids))
282    }
283}