snarkvm_synthesizer_program/logic/instruction/operation/
sign_verify.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 crate::{Opcode, Operand, RegistersCircuit, RegistersTrait, StackTrait};
17use circuit::prelude::ToFields as CircuitToFields;
18use console::{
19    network::prelude::*,
20    program::{
21        Address,
22        Literal,
23        LiteralType,
24        PlaintextType,
25        Register,
26        RegisterType,
27        Signature,
28        ToFields as ConsoleToFields,
29        Value,
30    },
31    types::Boolean,
32};
33
34/// Computes whether `signature` is valid for the given `address` and `message`.
35pub type SignVerify<N> = SignatureVerification<N>;
36
37/// Computes whether `signature` is valid for the given `address` and `message`.
38#[derive(Clone, PartialEq, Eq, Hash)]
39pub struct SignatureVerification<N: Network> {
40    /// The operands.
41    operands: Vec<Operand<N>>,
42    /// The destination register.
43    destination: Register<N>,
44}
45
46impl<N: Network> SignatureVerification<N> {
47    /// Initializes a new `sign.verify` instruction.
48    #[inline]
49    pub fn new(operands: Vec<Operand<N>>, destination: Register<N>) -> Result<Self> {
50        // Sanity check the number of operands.
51        ensure!(operands.len() == 3, "Instruction '{}' must have three operands", Self::opcode());
52        // Return the instruction.
53        Ok(Self { operands, destination })
54    }
55
56    /// Returns the opcode.
57    #[inline]
58    pub const fn opcode() -> Opcode {
59        Opcode::Sign("sign.verify")
60    }
61
62    /// Returns the operands in the operation.
63    #[inline]
64    pub fn operands(&self) -> &[Operand<N>] {
65        // Sanity check that there are exactly three operands.
66        debug_assert!(self.operands.len() == 3, "Instruction '{}' must have three operands", Self::opcode());
67        // Return the operands.
68        &self.operands
69    }
70
71    /// Returns the destination register.
72    #[inline]
73    pub fn destinations(&self) -> Vec<Register<N>> {
74        vec![self.destination.clone()]
75    }
76}
77
78/// Evaluate a Schnorr verification operation.
79///
80/// This allows running the verification without the machinery of stacks and registers.
81/// This is necessary for the Leo interpreter.
82pub fn evaluate_schnorr_verification<N: Network>(
83    signature: &Signature<N>,
84    address: &Address<N>,
85    message: &Value<N>,
86) -> Result<bool> {
87    // Verify the signature.
88    Ok(signature.verify(address, &message.to_fields()?))
89}
90
91impl<N: Network> SignatureVerification<N> {
92    /// Evaluates the instruction.
93    #[inline]
94    pub fn evaluate(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
95        // Ensure the number of operands is correct.
96        if self.operands.len() != 3 {
97            bail!("Instruction '{}' expects 3 operands, found {} operands", Self::opcode(), self.operands.len())
98        }
99
100        // Retrieve the inputs.
101        let signature = match registers.load_literal(stack, &self.operands[0])? {
102            Literal::Signature(signature) => signature,
103            _ => bail!("Expected the first operand to be a signature."),
104        };
105        let address = match registers.load_literal(stack, &self.operands[1])? {
106            Literal::Address(address) => address,
107            _ => bail!("Expected the second operand to be an address."),
108        };
109        let message = registers.load(stack, &self.operands[2])?;
110
111        // Verify the signature.
112        let output = evaluate_schnorr_verification(&signature, &address, &message)?;
113        let output = Literal::Boolean(Boolean::new(output));
114
115        // Store the output.
116        registers.store_literal(stack, &self.destination, output)
117    }
118
119    /// Executes the instruction.
120    #[inline]
121    pub fn execute<A: circuit::Aleo<Network = N>>(
122        &self,
123        stack: &impl StackTrait<N>,
124        registers: &mut impl RegistersCircuit<N, A>,
125    ) -> Result<()> {
126        // Ensure the number of operands is correct.
127        if self.operands.len() != 3 {
128            bail!("Instruction '{}' expects 3 operands, found {} operands", Self::opcode(), self.operands.len())
129        }
130
131        // Retrieve the inputs.
132        let signature = match registers.load_literal_circuit(stack, &self.operands[0])? {
133            circuit::Literal::Signature(signature) => signature,
134            _ => bail!("Expected the first operand to be a signature."),
135        };
136        let address = match registers.load_literal_circuit(stack, &self.operands[1])? {
137            circuit::Literal::Address(address) => address,
138            _ => bail!("Expected the second operand to be an address."),
139        };
140        let message = registers.load_circuit(stack, &self.operands[2])?;
141
142        // Verify the signature.
143        let output = circuit::Literal::Boolean(signature.verify(&address, &message.to_fields()));
144
145        // Store the output.
146        registers.store_literal_circuit(stack, &self.destination, output)
147    }
148
149    /// Finalizes the instruction.
150    #[inline]
151    pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
152        self.evaluate(stack, registers)
153    }
154
155    /// Returns the output type from the given program and input types.
156    #[inline]
157    pub fn output_types(
158        &self,
159        _stack: &impl StackTrait<N>,
160        input_types: &[RegisterType<N>],
161    ) -> Result<Vec<RegisterType<N>>> {
162        // Ensure the number of input types is correct.
163        if input_types.len() != 3 {
164            bail!("Instruction '{}' expects 3 inputs, found {} inputs", Self::opcode(), input_types.len())
165        }
166
167        // Ensure the first operand is a signature.
168        if input_types[0] != RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Signature)) {
169            bail!(
170                "Instruction '{}' expects the first input to be a 'signature'. Found input of type '{}'",
171                Self::opcode(),
172                input_types[0]
173            )
174        }
175
176        // Ensure the second operand is an address.
177        if input_types[1] != RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Address)) {
178            bail!(
179                "Instruction '{}' expects the second input to be an 'address'. Found input of type '{}'",
180                Self::opcode(),
181                input_types[1]
182            )
183        }
184
185        Ok(vec![RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Boolean))])
186    }
187}
188
189impl<N: Network> Parser for SignatureVerification<N> {
190    /// Parses a string into an operation.
191    #[inline]
192    fn parse(string: &str) -> ParserResult<Self> {
193        // Parse the opcode from the string.
194        let (string, _) = tag(*Self::opcode())(string)?;
195        // Parse the whitespace from the string.
196        let (string, _) = Sanitizer::parse_whitespaces(string)?;
197        // Parse the first operand from the string.
198        let (string, first) = Operand::parse(string)?;
199        // Parse the whitespace from the string.
200        let (string, _) = Sanitizer::parse_whitespaces(string)?;
201        // Parse the second operand from the string.
202        let (string, second) = Operand::parse(string)?;
203        // Parse the whitespace from the string.
204        let (string, _) = Sanitizer::parse_whitespaces(string)?;
205        // Parse the third operand from the string.
206        let (string, third) = Operand::parse(string)?;
207        // Parse the whitespace from the string.
208        let (string, _) = Sanitizer::parse_whitespaces(string)?;
209        // Parse the "into" from the string.
210        let (string, _) = tag("into")(string)?;
211        // Parse the whitespace from the string.
212        let (string, _) = Sanitizer::parse_whitespaces(string)?;
213        // Parse the destination register from the string.
214        let (string, destination) = Register::parse(string)?;
215
216        Ok((string, Self { operands: vec![first, second, third], destination }))
217    }
218}
219
220impl<N: Network> FromStr for SignatureVerification<N> {
221    type Err = Error;
222
223    /// Parses a string into an operation.
224    #[inline]
225    fn from_str(string: &str) -> Result<Self> {
226        match Self::parse(string) {
227            Ok((remainder, object)) => {
228                // Ensure the remainder is empty.
229                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
230                // Return the object.
231                Ok(object)
232            }
233            Err(error) => bail!("Failed to parse string. {error}"),
234        }
235    }
236}
237
238impl<N: Network> Debug for SignatureVerification<N> {
239    /// Prints the operation as a string.
240    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
241        Display::fmt(self, f)
242    }
243}
244
245impl<N: Network> Display for SignatureVerification<N> {
246    /// Prints the operation to a string.
247    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
248        // Ensure the number of operands is 3.
249        if self.operands.len() != 3 {
250            return Err(fmt::Error);
251        }
252        // Print the operation.
253        write!(f, "{} ", Self::opcode())?;
254        self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
255        write!(f, "into {}", self.destination)
256    }
257}
258
259impl<N: Network> FromBytes for SignatureVerification<N> {
260    /// Reads the operation from a buffer.
261    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
262        // Initialize the vector for the operands.
263        let mut operands = Vec::with_capacity(3);
264        // Read the operands.
265        for _ in 0..3 {
266            operands.push(Operand::read_le(&mut reader)?);
267        }
268        // Read the destination register.
269        let destination = Register::read_le(&mut reader)?;
270
271        // Return the operation.
272        Ok(Self { operands, destination })
273    }
274}
275
276impl<N: Network> ToBytes for SignatureVerification<N> {
277    /// Writes the operation to a buffer.
278    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
279        // Ensure the number of operands is 3.
280        if self.operands.len() != 3 {
281            return Err(error(format!("The number of operands must be 3, found {}", self.operands.len())));
282        }
283        // Write the operands.
284        self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
285        // Write the destination register.
286        self.destination.write_le(&mut writer)
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293    use console::network::MainnetV0;
294
295    type CurrentNetwork = MainnetV0;
296
297    #[test]
298    fn test_parse() {
299        let (string, is) = SignVerify::<CurrentNetwork>::parse("sign.verify r0 r1 r2 into r3").unwrap();
300        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
301        assert_eq!(is.operands.len(), 3, "The number of operands is incorrect");
302        assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
303        assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
304        assert_eq!(is.operands[2], Operand::Register(Register::Locator(2)), "The third operand is incorrect");
305        assert_eq!(is.destination, Register::Locator(3), "The destination register is incorrect");
306    }
307}