snarkvm_ledger/
is_solution_limit_reached.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
16use super::*;
17
18/// The stake required to land one solution per epoch at various points in time for mainnet.
19pub(super) const MAINNET_STAKE_REQUIREMENTS_PER_SOLUTION: [(i64, u64); 9] = [
20    (1754006399i64, 100_000_000_000u64),   /* 2025-07-31 23:59:59 UTC */
21    (1761955199i64, 250_000_000_000u64),   /* 2025-10-31 23:59:59 UTC */
22    (1769903999i64, 500_000_000_000u64),   /* 2026-01-31 23:59:59 UTC */
23    (1777593599i64, 750_000_000_000u64),   /* 2026-04-30 23:59:59 UTC */
24    (1785542399i64, 1_000_000_000_000u64), /* 2026-07-31 23:59:59 UTC */
25    (1793491199i64, 1_250_000_000_000u64), /* 2026-10-31 23:59:59 UTC */
26    (1801439999i64, 1_500_000_000_000u64), /* 2027-01-31 23:59:59 UTC */
27    (1809129599i64, 2_000_000_000_000u64), /* 2027-04-30 23:59:59 UTC */
28    (1817078399i64, 2_500_000_000_000u64), /* 2027-07-31 23:59:59 UTC */
29];
30
31/// The stake required to land one solution per epoch at various points in time for canary and testnet.
32pub(super) const CANARY_AND_TESTNET_STAKE_REQUIREMENTS_PER_SOLUTION: [(i64, u64); 9] = [
33    (1754006399i64, 1_000_000_000u64),  /* 2025-07-31 23:59:59 UTC */
34    (1761955199i64, 2_500_000_000u64),  /* 2025-10-31 23:59:59 UTC */
35    (1769903999i64, 5_000_000_000u64),  /* 2026-01-31 23:59:59 UTC */
36    (1777593599i64, 7_500_000_000u64),  /* 2026-04-30 23:59:59 UTC */
37    (1785542399i64, 10_000_000_000u64), /* 2026-07-31 23:59:59 UTC */
38    (1793491199i64, 12_500_000_000u64), /* 2026-10-31 23:59:59 UTC */
39    (1801439999i64, 15_000_000_000u64), /* 2027-01-31 23:59:59 UTC */
40    (1809129599i64, 20_000_000_000u64), /* 2027-04-30 23:59:59 UTC */
41    (1817078399i64, 25_000_000_000u64), /* 2027-07-31 23:59:59 UTC */
42];
43
44/// The stake required to land one solution per epoch at various points in time.
45///
46/// Each entry represents a threshold where, starting from the given timestamp,
47/// a prover must have at least the specified amount of stake (in microcredits) to land one solution.
48///
49/// A prover with `n * stake` may land up to `n` solutions per epoch.
50///
51/// Format: `(timestamp, stake_required_per_solution)`
52pub fn stake_requirements_per_solution<N: Network>() -> &'static [(i64, u64)] {
53    match N::ID {
54        console::network::MainnetV0::ID => &MAINNET_STAKE_REQUIREMENTS_PER_SOLUTION,
55        console::network::TestnetV0::ID | console::network::CanaryV0::ID => {
56            &CANARY_AND_TESTNET_STAKE_REQUIREMENTS_PER_SOLUTION
57        }
58        _ => &MAINNET_STAKE_REQUIREMENTS_PER_SOLUTION,
59    }
60}
61
62/// Returns the maximum number of allowed solutions per epoch based on the provided stake and timestamp.
63pub fn maximum_allowed_solutions_per_epoch<N: Network>(prover_stake: u64, current_time: i64) -> u64 {
64    let stake_requirements = stake_requirements_per_solution::<N>();
65
66    // If the block height is earlier than the starting enforcement, do not restrict the maximum number of solutions per epoch.
67    if current_time < stake_requirements.first().map(|(t, _)| *t).unwrap_or(i64::MAX) {
68        return u64::MAX;
69    }
70
71    // Find the minimum stake required for one solution per epoch.
72    let minimum_stake_per_solution_per_epoch = match stake_requirements.binary_search_by_key(&current_time, |(t, _)| *t)
73    {
74        // If a stake limit was found at this height, return it.
75        Ok(index) => stake_requirements[index].1,
76        // If the specified height was not found, determine which limit to return.
77        Err(index) => stake_requirements[index.saturating_sub(1)].1,
78    };
79
80    // Return the number of allowed solutions per epoch.
81    prover_stake.saturating_div(minimum_stake_per_solution_per_epoch)
82}
83
84impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
85    /// Returns the number of remaining solutions a prover can submit in the current epoch.
86    pub fn num_remaining_solutions(&self, prover_address: &Address<N>, additional_solutions_in_block: u64) -> u64 {
87        // Fetch the prover's stake.
88        let prover_stake = self.get_bonded_amount(prover_address).unwrap_or(0);
89
90        // Determine the maximum number of solutions allowed based on this prover's stake.
91        let maximum_allowed_solutions = maximum_allowed_solutions_per_epoch::<N>(prover_stake, self.latest_timestamp());
92
93        // Fetch the number of solutions the prover has earned rewards for in the current epoch.
94        let prover_num_solutions_in_epoch = *self.epoch_provers_cache.read().get(prover_address).unwrap_or(&0);
95
96        // Calculate the total number of solutions the prover has submitted in the current epoch including the current block.
97        let num_solutions = (prover_num_solutions_in_epoch as u64).saturating_add(additional_solutions_in_block);
98
99        // Return the number of remaining solutions.
100        maximum_allowed_solutions.saturating_sub(num_solutions)
101    }
102
103    /// Returns `true` if the given prover address has reached their solution limit for the current epoch.
104    pub fn is_solution_limit_reached(&self, prover_address: &Address<N>, additional_solutions_in_block: u64) -> bool {
105        // Calculate the number of remaining solutions for the prover.
106        let num_remaining_solutions = self.num_remaining_solutions(prover_address, additional_solutions_in_block);
107
108        // If the number of remaining solutions is zero, the limit is reached.
109        num_remaining_solutions == 0
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    type CurrentNetwork = console::network::MainnetV0;
118
119    const ITERATIONS: u64 = 100;
120
121    #[test]
122    fn test_solution_limit_per_epoch() {
123        let mut rng = TestRng::default();
124        let stake_requirements = stake_requirements_per_solution::<CurrentNetwork>();
125
126        for _ in 0..ITERATIONS {
127            for window in stake_requirements.windows(2) {
128                let (prev_time, stake_per_solution) = window[0];
129                let (next_time, _) = window[1];
130
131                // Choose a time strictly between the steps.
132                let timestamp = rng.gen_range(prev_time..next_time);
133                // Generate a random prover stake.
134                let prover_stake: u64 = rng.r#gen();
135                let expected_num_solutions = prover_stake / stake_per_solution;
136
137                assert_eq!(
138                    maximum_allowed_solutions_per_epoch::<CurrentNetwork>(prover_stake, timestamp),
139                    expected_num_solutions,
140                );
141            }
142        }
143    }
144
145    #[test]
146    fn test_solution_limit_before_enforcement() {
147        let mut rng = TestRng::default();
148        let stake_requirements = stake_requirements_per_solution::<CurrentNetwork>();
149
150        // Fetch the first timestamp from the table.
151        let first_timestamp = stake_requirements.first().unwrap().0;
152        let time_before_first = first_timestamp - 1;
153
154        // Check that before enforcement, the number of solutions is unrestricted even without prover stake.
155        let prover_stake = 0;
156        assert_eq!(maximum_allowed_solutions_per_epoch::<CurrentNetwork>(prover_stake, time_before_first), u64::MAX);
157
158        // Check that before enforcement, the number of solutions is unrestricted for any prover stake.
159        for _ in 0..ITERATIONS {
160            assert_eq!(
161                maximum_allowed_solutions_per_epoch::<CurrentNetwork>(rng.r#gen(), rng.gen_range(0..time_before_first)),
162                u64::MAX
163            );
164        }
165    }
166
167    #[test]
168    fn test_solution_limit_after_final_timestamp() {
169        let mut rng = TestRng::default();
170        let stake_requirements = stake_requirements_per_solution::<CurrentNetwork>();
171        let (last_timestamp, stake_per_solution) = *stake_requirements.last().unwrap();
172
173        // Check that all timestamps after the last one are treated as the last one.
174        for _ in 0..ITERATIONS {
175            let prover_stake: u64 = rng.r#gen();
176            let time_after_last = rng.gen_range(last_timestamp..i64::MAX);
177            let expected_num_solutions = prover_stake / stake_per_solution;
178
179            assert_eq!(
180                maximum_allowed_solutions_per_epoch::<CurrentNetwork>(prover_stake, time_after_last),
181                expected_num_solutions
182            );
183        }
184    }
185
186    #[test]
187    fn test_solution_limit_exact_timestamps() {
188        let mut rng = TestRng::default();
189        let stake_requirements = stake_requirements_per_solution::<CurrentNetwork>();
190        // Check that the maximum allowed solutions per epoch is correct for each timestamp in the table.
191        for &(timestamp, stake_per_solution) in stake_requirements.iter() {
192            let expected_num_solutions = rng.gen_range(1..=100);
193            let prover_stake = expected_num_solutions * stake_per_solution;
194
195            assert_eq!(
196                maximum_allowed_solutions_per_epoch::<CurrentNetwork>(prover_stake, timestamp),
197                expected_num_solutions,
198            );
199        }
200    }
201}