snarkvm_circuit_account/signature/
mod.rs

1// Copyright 2024-2025 Aleo Network Foundation
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
16mod equal;
17mod helpers;
18mod ternary;
19mod verify;
20
21#[cfg(test)]
22use snarkvm_circuit_types::environment::{assert_count, assert_output_mode, assert_scope};
23
24use crate::ComputeKey;
25use snarkvm_circuit_network::Aleo;
26use snarkvm_circuit_types::{Address, Boolean, Field, Scalar, environment::prelude::*};
27
28#[derive(Clone)]
29pub struct Signature<A: Aleo> {
30    /// The verifier challenge to check against.
31    challenge: Scalar<A>,
32    /// The prover response to the challenge.
33    response: Scalar<A>,
34    /// The compute key of the prover.
35    compute_key: ComputeKey<A>,
36}
37
38#[cfg(feature = "console")]
39impl<A: Aleo> Inject for Signature<A> {
40    type Primitive = console::Signature<A::Network>;
41
42    /// Initializes a signature from the given mode and native signature.
43    fn new(mode: Mode, signature: Self::Primitive) -> Signature<A> {
44        Self {
45            challenge: Scalar::new(mode, signature.challenge()),
46            response: Scalar::new(mode, signature.response()),
47            compute_key: ComputeKey::new(mode, signature.compute_key()),
48        }
49    }
50}
51
52impl<A: Aleo> Signature<A> {
53    /// Returns the challenge.
54    pub const fn challenge(&self) -> &Scalar<A> {
55        &self.challenge
56    }
57
58    /// Returns the response.
59    pub const fn response(&self) -> &Scalar<A> {
60        &self.response
61    }
62
63    /// Returns the account compute key.
64    pub const fn compute_key(&self) -> &ComputeKey<A> {
65        &self.compute_key
66    }
67}
68
69#[cfg(feature = "console")]
70impl<A: Aleo> Eject for Signature<A> {
71    type Primitive = console::Signature<A::Network>;
72
73    /// Ejects the mode of the signature.
74    fn eject_mode(&self) -> Mode {
75        (&self.challenge, &self.response, &self.compute_key).eject_mode()
76    }
77
78    /// Ejects the signature.
79    fn eject_value(&self) -> Self::Primitive {
80        Self::Primitive::from((&self.challenge, &self.response, &self.compute_key).eject_value())
81    }
82}
83
84#[cfg(feature = "console")]
85impl<A: Aleo> Parser for Signature<A> {
86    /// Parses a string into a signature circuit.
87    #[inline]
88    fn parse(string: &str) -> ParserResult<Self> {
89        // Parse the signature from the string.
90        let (string, signature) = console::Signature::parse(string)?;
91        // Parse the mode from the string.
92        let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
93
94        match mode {
95            Some((_, mode)) => Ok((string, Signature::new(mode, signature))),
96            None => Ok((string, Signature::new(Mode::Constant, signature))),
97        }
98    }
99}
100
101#[cfg(feature = "console")]
102impl<A: Aleo> FromStr for Signature<A> {
103    type Err = Error;
104
105    /// Parses a string into a signature.
106    #[inline]
107    fn from_str(string: &str) -> Result<Self> {
108        match Self::parse(string) {
109            Ok((remainder, object)) => {
110                // Ensure the remainder is empty.
111                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
112                // Return the object.
113                Ok(object)
114            }
115            Err(error) => bail!("Failed to parse string. {error}"),
116        }
117    }
118}
119
120#[cfg(feature = "console")]
121impl<A: Aleo> TypeName for Signature<A> {
122    /// Returns the type name of the circuit as a string.
123    #[inline]
124    fn type_name() -> &'static str {
125        console::Signature::<A::Network>::type_name()
126    }
127}
128
129#[cfg(feature = "console")]
130impl<A: Aleo> Debug for Signature<A> {
131    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132        Display::fmt(self, f)
133    }
134}
135
136#[cfg(feature = "console")]
137impl<A: Aleo> Display for Signature<A> {
138    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139        write!(f, "{}.{}", self.eject_value(), self.eject_mode())
140    }
141}
142
143#[cfg(all(test, feature = "console"))]
144mod tests {
145    use super::*;
146    use crate::{Circuit, helpers::generate_account};
147    use snarkvm_utilities::{TestRng, Uniform};
148
149    use anyhow::Result;
150
151    const ITERATIONS: u64 = 250;
152
153    fn check_new(
154        mode: Mode,
155        num_constants: u64,
156        num_public: u64,
157        num_private: u64,
158        num_constraints: u64,
159    ) -> Result<()> {
160        let rng = &mut TestRng::default();
161
162        // Generate a private key, compute key, view key, and address.
163        let (private_key, _compute_key, _view_key, _address) = generate_account()?;
164
165        for i in 0..ITERATIONS {
166            // Generate a signature.
167            let message: Vec<_> = (0..i).map(|_| Uniform::rand(rng)).collect();
168            let signature = console::Signature::sign(&private_key, &message, rng)?;
169
170            Circuit::scope(format!("New {mode}"), || {
171                let candidate = Signature::<Circuit>::new(mode, signature);
172                assert_eq!(signature, candidate.eject_value());
173                // TODO (howardwu): Resolve skipping the cost count checks for the burn-in round.
174                if i > 0 {
175                    assert_scope!(num_constants, num_public, num_private, num_constraints);
176                }
177            });
178            Circuit::reset();
179        }
180        Ok(())
181    }
182
183    #[test]
184    fn test_signature_new_constant() -> Result<()> {
185        check_new(Mode::Constant, 276, 0, 0, 0)
186    }
187
188    #[test]
189    fn test_signature_new_public() -> Result<()> {
190        check_new(Mode::Public, 9, 6, 869, 873)
191    }
192
193    #[test]
194    fn test_signature_new_private() -> Result<()> {
195        check_new(Mode::Private, 9, 0, 875, 873)
196    }
197}