1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the snarkVM library.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod bytes;

use super::*;
use crate::hash_to_polynomial;
use snarkvm_algorithms::fft::Evaluations as EvaluationsOnDomain;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EpochChallenge<N: Network> {
    /// The epoch number.
    epoch_number: u32,
    /// The epoch block hash, defined as the block hash right before the epoch updated.
    epoch_block_hash: N::BlockHash,
    /// The epoch polynomial.
    epoch_polynomial: DensePolynomial<<N::PairingCurve as PairingEngine>::Fr>,
    /// The evaluations of the epoch polynomial over the product domain.
    epoch_polynomial_evaluations: EvaluationsOnDomain<<N::PairingCurve as PairingEngine>::Fr>,
}

impl<N: Network> EpochChallenge<N> {
    /// Initializes a new epoch challenge.
    pub fn new(epoch_number: u32, epoch_block_hash: N::BlockHash, degree: u32) -> Result<Self> {
        // Construct the 'input' as '( epoch_number || epoch_block_hash )'
        let mut input = vec![];
        epoch_number.write_le(&mut input)?;
        epoch_block_hash.write_le(&mut input)?;

        let product_domain = CoinbasePuzzle::<N>::product_domain(degree)?;

        let epoch_polynomial = hash_to_polynomial::<<N::PairingCurve as PairingEngine>::Fr>(&input, degree);
        ensure!(u32::try_from(epoch_polynomial.degree()).is_ok(), "Degree is too large");

        let epoch_polynomial_evaluations = epoch_polynomial.evaluate_over_domain_by_ref(product_domain);
        // Returns the epoch challenge.
        Ok(EpochChallenge { epoch_number, epoch_block_hash, epoch_polynomial, epoch_polynomial_evaluations })
    }

    /// Returns the epoch number for the solution.
    pub const fn epoch_number(&self) -> u32 {
        self.epoch_number
    }

    /// Returns the epoch block hash for the solution.
    pub const fn epoch_block_hash(&self) -> N::BlockHash {
        self.epoch_block_hash
    }

    /// Returns the epoch polynomial for the solution.
    pub const fn epoch_polynomial(&self) -> &DensePolynomial<<N::PairingCurve as PairingEngine>::Fr> {
        &self.epoch_polynomial
    }

    /// Returns the evaluations of the epoch polynomial over the product domain.
    pub const fn epoch_polynomial_evaluations(&self) -> &EvaluationsOnDomain<<N::PairingCurve as PairingEngine>::Fr> {
        &self.epoch_polynomial_evaluations
    }

    /// Returns the number of coefficients of the epoch polynomial.
    pub fn degree(&self) -> u32 {
        // Convert the degree into a u32.
        // The `unwrap` is guaranteed to succeed as we check the degree is less
        // than `u32::MAX` in `new`.
        u32::try_from(self.epoch_polynomial.degree()).unwrap()
    }

    /// Returns the number of coefficients of the epoch polynomial.
    pub fn num_coefficients(&self) -> Result<u32> {
        let degree = self.degree();
        degree.checked_add(1).ok_or_else(|| anyhow!("Epoch polynomial degree ({degree} + 1) overflows"))
    }
}