snarkvm_ledger_coinbase/helpers/coinbase_solution/
mod.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
15mod bytes;
16mod serialize;
17mod string;
18
19use super::*;
20
21use indexmap::IndexMap;
22
23/// The coinbase puzzle solution is composed of individual prover solutions.
24#[derive(Clone, Eq, PartialEq)]
25pub struct CoinbaseSolution<N: Network> {
26    /// The prover solutions for the coinbase puzzle.
27    solutions: IndexMap<PuzzleCommitment<N>, ProverSolution<N>>,
28}
29
30impl<N: Network> CoinbaseSolution<N> {
31    /// Initializes a new instance of the solutions.
32    pub fn new(solutions: Vec<ProverSolution<N>>) -> Result<Self> {
33        // Ensure the solutions are not empty.
34        ensure!(!solutions.is_empty(), "There are no solutions to verify for the coinbase puzzle");
35        // Ensure the number of partial solutions does not exceed `MAX_PROVER_SOLUTIONS`.
36        if solutions.len() > N::MAX_SOLUTIONS {
37            bail!(
38                "The solutions exceed the allowed number of partial solutions. ({} > {})",
39                solutions.len(),
40                N::MAX_SOLUTIONS
41            );
42        }
43        // Ensure the puzzle commitments are unique.
44        if has_duplicates(solutions.iter().map(|s| s.commitment())) {
45            bail!("The solutions contain duplicate puzzle commitments");
46        }
47        // Return the solutions.
48        Ok(Self { solutions: solutions.into_iter().map(|solution| (solution.commitment(), solution)).collect() })
49    }
50
51    /// Returns the puzzle commitments.
52    pub fn puzzle_commitments(&self) -> impl '_ + Iterator<Item = &PuzzleCommitment<N>> {
53        self.solutions.keys()
54    }
55
56    /// Returns the number of solutions.
57    pub fn len(&self) -> usize {
58        self.solutions.len()
59    }
60
61    /// Returns `true` if there are no solutions.
62    pub fn is_empty(&self) -> bool {
63        self.solutions.is_empty()
64    }
65
66    /// Returns the prover solution for the puzzle commitment.
67    pub fn get_solution(&self, puzzle_commitment: &PuzzleCommitment<N>) -> Option<&ProverSolution<N>> {
68        self.solutions.get(puzzle_commitment)
69    }
70
71    /// Returns the combined sum of the prover solutions.
72    pub fn to_combined_proof_target(&self) -> Result<u128> {
73        // Compute the combined proof target as a u128.
74        self.solutions.values().try_fold(0u128, |combined, solution| {
75            combined.checked_add(solution.to_target()? as u128).ok_or_else(|| anyhow!("Combined target overflowed"))
76        })
77    }
78
79    /// Returns the accumulator challenge point.
80    pub fn to_accumulator_point(&self) -> Result<Field<N>> {
81        let mut challenge_points = hash_commitments(self.solutions.keys().map(|pcm| **pcm))?;
82        ensure!(challenge_points.len() == self.solutions.len() + 1, "Invalid number of challenge points");
83
84        // Pop the last challenge point as the accumulator challenge point.
85        match challenge_points.pop() {
86            Some(point) => Ok(Field::new(point)),
87            None => bail!("Missing the accumulator challenge point"),
88        }
89    }
90}
91
92impl<N: Network> Deref for CoinbaseSolution<N> {
93    type Target = IndexMap<PuzzleCommitment<N>, ProverSolution<N>>;
94
95    fn deref(&self) -> &Self::Target {
96        &self.solutions
97    }
98}