snarkvm_synthesizer_program/logic/instruction/operation/
ecdsa_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 console::{
18    algorithms::{ECDSASignature, Keccak256, Keccak384, Keccak512, Sha3_256, Sha3_384, Sha3_512},
19    network::prelude::*,
20    program::{Boolean, Identifier, Literal, LiteralType, Locator, PlaintextType, Register, RegisterType, Value},
21};
22use snarkvm_utilities::bytes_from_bits_le;
23
24/// The ECDSA signature verification instruction using a precomputed digest.
25pub type ECDSAVerifyDigest<N> = ECDSAVerify<N, { ECDSAVerifyVariant::Digest as u8 }>;
26/// The ECDSA signature verification instruction using a precomputed digest and an Ethereum address.
27pub type ECDSAVerifyDigestEth<N> = ECDSAVerify<N, { ECDSAVerifyVariant::DigestEth as u8 }>;
28
29/// The ECDSA signature verification instruction using Keccak256.
30pub type ECDSAVerifyKeccak256<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak256 as u8 }>;
31/// The ECDSA signature verification instruction using Keccak256 with raw inputs.
32pub type ECDSAVerifyKeccak256Raw<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak256Raw as u8 }>;
33/// The ECDSA signature verification instruction using Keccak256 and an Ethereum address.
34pub type ECDSAVerifyKeccak256Eth<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak256Eth as u8 }>;
35/// The ECDSA signature verification instruction using Keccak384.
36pub type ECDSAVerifyKeccak384<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak384 as u8 }>;
37/// The ECDSA signature verification instruction using Keccak384 with raw inputs.
38pub type ECDSAVerifyKeccak384Raw<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak384Raw as u8 }>;
39/// The ECDSA signature verification instruction using Keccak384 and an Ethereum address.
40pub type ECDSAVerifyKeccak384Eth<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak384Eth as u8 }>;
41/// The ECDSA signature verification instruction using Keccak512.
42pub type ECDSAVerifyKeccak512<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak512 as u8 }>;
43/// The ECDSA signature verification instruction using Keccak512 with raw inputs.
44pub type ECDSAVerifyKeccak512Raw<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak512Raw as u8 }>;
45/// The ECDSA signature verification instruction using Keccak512 and an Ethereum address.
46pub type ECDSAVerifyKeccak512Eth<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashKeccak512Eth as u8 }>;
47
48/// The ECDSA signature verification instruction using SHA3-256.
49pub type ECDSAVerifySha3_256<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_256 as u8 }>;
50/// The ECDSA signature verification instruction using SHA3-256 with raw inputs.
51pub type ECDSAVerifySha3_256Raw<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_256Raw as u8 }>;
52/// The ECDSA signature verification instruction using SHA3-256 and an Ethereum address.
53pub type ECDSAVerifySha3_256Eth<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_256Eth as u8 }>;
54/// The ECDSA signature verification instruction using SHA3-384.
55pub type ECDSAVerifySha3_384<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_384 as u8 }>;
56/// The ECDSA signature verification instruction using SHA3-384 with raw inputs.
57pub type ECDSAVerifySha3_384Raw<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_384Raw as u8 }>;
58/// The ECDSA signature verification instruction using SHA3-384 and an Ethereum address.
59pub type ECDSAVerifySha3_384Eth<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_384Eth as u8 }>;
60/// The ECDSA signature verification instruction using SHA3-512.
61pub type ECDSAVerifySha3_512<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_512 as u8 }>;
62/// The ECDSA signature verification instruction using SHA3-512 with raw inputs.
63pub type ECDSAVerifySha3_512Raw<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_512Raw as u8 }>;
64/// The ECDSA signature verification instruction using SHA3-512 and an Ethereum address.
65pub type ECDSAVerifySha3_512Eth<N> = ECDSAVerify<N, { ECDSAVerifyVariant::HashSha3_512Eth as u8 }>;
66
67/// Which hash function to use.
68#[derive(Debug, Clone, Eq, PartialEq)]
69pub enum ECDSAVerifyVariant {
70    Digest,
71    DigestEth,
72    HashKeccak256,
73    HashKeccak256Raw,
74    HashKeccak256Eth,
75    HashKeccak384,
76    HashKeccak384Raw,
77    HashKeccak384Eth,
78    HashKeccak512,
79    HashKeccak512Raw,
80    HashKeccak512Eth,
81    HashSha3_256,
82    HashSha3_256Raw,
83    HashSha3_256Eth,
84    HashSha3_384,
85    HashSha3_384Raw,
86    HashSha3_384Eth,
87    HashSha3_512,
88    HashSha3_512Raw,
89    HashSha3_512Eth,
90}
91
92impl ECDSAVerifyVariant {
93    // Initializes a new `ECDSAVerifyVariant`.
94    pub const fn new(variant: u8) -> Self {
95        match variant {
96            0 => Self::Digest,
97            1 => Self::DigestEth,
98            2 => Self::HashKeccak256,
99            3 => Self::HashKeccak256Raw,
100            4 => Self::HashKeccak256Eth,
101            5 => Self::HashKeccak384,
102            6 => Self::HashKeccak384Raw,
103            7 => Self::HashKeccak384Eth,
104            8 => Self::HashKeccak512,
105            9 => Self::HashKeccak512Raw,
106            10 => Self::HashKeccak512Eth,
107            11 => Self::HashSha3_256,
108            12 => Self::HashSha3_256Raw,
109            13 => Self::HashSha3_256Eth,
110            14 => Self::HashSha3_384,
111            15 => Self::HashSha3_384Raw,
112            16 => Self::HashSha3_384Eth,
113            17 => Self::HashSha3_512,
114            18 => Self::HashSha3_512Raw,
115            19 => Self::HashSha3_512Eth,
116            _ => panic!("Invalid 'ecdsa.verify' instruction opcode"),
117        }
118    }
119
120    // Returns the opcode associated with the variant.
121    pub const fn opcode(&self) -> &'static str {
122        match self {
123            Self::Digest => "ecdsa.verify.digest",
124            Self::DigestEth => "ecdsa.verify.digest.eth",
125            Self::HashKeccak256 => "ecdsa.verify.keccak256",
126            Self::HashKeccak256Raw => "ecdsa.verify.keccak256.raw",
127            Self::HashKeccak256Eth => "ecdsa.verify.keccak256.eth",
128            Self::HashKeccak384 => "ecdsa.verify.keccak384",
129            Self::HashKeccak384Raw => "ecdsa.verify.keccak384.raw",
130            Self::HashKeccak384Eth => "ecdsa.verify.keccak384.eth",
131            Self::HashKeccak512 => "ecdsa.verify.keccak512",
132            Self::HashKeccak512Raw => "ecdsa.verify.keccak512.raw",
133            Self::HashKeccak512Eth => "ecdsa.verify.keccak512.eth",
134            Self::HashSha3_256 => "ecdsa.verify.sha3_256",
135            Self::HashSha3_256Raw => "ecdsa.verify.sha3_256.raw",
136            Self::HashSha3_256Eth => "ecdsa.verify.sha3_256.eth",
137            Self::HashSha3_384 => "ecdsa.verify.sha3_384",
138            Self::HashSha3_384Raw => "ecdsa.verify.sha3_384.raw",
139            Self::HashSha3_384Eth => "ecdsa.verify.sha3_384.eth",
140            Self::HashSha3_512 => "ecdsa.verify.sha3_512",
141            Self::HashSha3_512Raw => "ecdsa.verify.sha3_512.raw",
142            Self::HashSha3_512Eth => "ecdsa.verify.sha3_512.eth",
143        }
144    }
145
146    // Returns true if the variant requires byte alignment.
147    pub const fn requires_byte_alignment(&self) -> bool {
148        match self {
149            Self::Digest => true,
150            Self::DigestEth => true,
151            Self::HashKeccak256 => false,
152            Self::HashKeccak256Raw => true,
153            Self::HashKeccak256Eth => true,
154            Self::HashKeccak384 => false,
155            Self::HashKeccak384Raw => true,
156            Self::HashKeccak384Eth => true,
157            Self::HashKeccak512 => false,
158            Self::HashKeccak512Raw => true,
159            Self::HashKeccak512Eth => true,
160            Self::HashSha3_256 => false,
161            Self::HashSha3_256Raw => true,
162            Self::HashSha3_256Eth => true,
163            Self::HashSha3_384 => false,
164            Self::HashSha3_384Raw => true,
165            Self::HashSha3_384Eth => true,
166            Self::HashSha3_512 => false,
167            Self::HashSha3_512Raw => true,
168            Self::HashSha3_512Eth => true,
169        }
170    }
171
172    // Returns `true` if the variant uses raw bits.
173    pub const fn is_raw(&self) -> bool {
174        match self {
175            Self::Digest => true,
176            Self::DigestEth => true,
177            Self::HashKeccak256 => false,
178            Self::HashKeccak256Raw => true,
179            Self::HashKeccak256Eth => true,
180            Self::HashKeccak384 => false,
181            Self::HashKeccak384Raw => true,
182            Self::HashKeccak384Eth => true,
183            Self::HashKeccak512 => false,
184            Self::HashKeccak512Raw => true,
185            Self::HashKeccak512Eth => true,
186            Self::HashSha3_256 => false,
187            Self::HashSha3_256Raw => true,
188            Self::HashSha3_256Eth => true,
189            Self::HashSha3_384 => false,
190            Self::HashSha3_384Raw => true,
191            Self::HashSha3_384Eth => true,
192            Self::HashSha3_512 => false,
193            Self::HashSha3_512Raw => true,
194            Self::HashSha3_512Eth => true,
195        }
196    }
197}
198
199/// Computes whether `signature` is valid for the given `address` and `message`.
200#[derive(Clone, PartialEq, Eq, Hash)]
201pub struct ECDSAVerify<N: Network, const VARIANT: u8> {
202    /// The operands.
203    operands: Vec<Operand<N>>,
204    /// The destination register.
205    destination: Register<N>,
206}
207
208impl<N: Network, const VARIANT: u8> ECDSAVerify<N, VARIANT> {
209    /// Initializes a new `ecdsa.verify` instruction.
210    #[inline]
211    pub fn new(operands: Vec<Operand<N>>, destination: Register<N>) -> Result<Self> {
212        // Sanity check the number of operands.
213        ensure!(operands.len() == 3, "Instruction '{}' must have three operands", Self::opcode());
214        // Return the instruction.
215        Ok(Self { operands, destination })
216    }
217
218    /// Returns the opcode.
219    #[inline]
220    pub const fn opcode() -> Opcode {
221        Opcode::ECDSA(ECDSAVerifyVariant::new(VARIANT).opcode())
222    }
223
224    /// Returns the operands in the operation.
225    #[inline]
226    pub fn operands(&self) -> &[Operand<N>] {
227        // Sanity check that there are exactly three operands.
228        debug_assert!(self.operands.len() == 3, "Instruction '{}' must have three operands", Self::opcode());
229        // Return the operands.
230        &self.operands
231    }
232
233    /// Returns the destination register.
234    #[inline]
235    pub fn destinations(&self) -> Vec<Register<N>> {
236        vec![self.destination.clone()]
237    }
238}
239
240// Perform the ECDSA verification based on the variant.
241#[rustfmt::skip]
242macro_rules! do_ecdsa_verification {
243    ($variant: expr, $signature: expr, $pub_key: expr, $message: expr) => {{
244        let bits = || $message.to_bits_le();
245        let bits_raw = || $message.to_bits_raw_le();
246
247        let pub_key = || ECDSASignature::verifying_key_from_bytes(&bytes_from_bits_le(&$pub_key.to_bits_raw_le()));
248        let ethereum_address = || {
249            bytes_from_bits_le(&$pub_key.to_bits_raw_le())
250                .try_into()
251                .map_err(|_| anyhow!("Failed to parse Ethereum address"))
252        };
253
254        let signature_bytes = bytes_from_bits_le(&$signature.to_bits_raw_le());
255        let ecdsa_signature = ECDSASignature::from_bytes_le(&signature_bytes)?;
256
257        let output = match $variant {
258            ECDSAVerifyVariant::Digest           => ecdsa_signature.verify_with_digest(&pub_key()?, &bits_raw()),
259            ECDSAVerifyVariant::DigestEth        => ecdsa_signature.verify_ethereum_with_digest(&ethereum_address()?, &bits_raw()),
260            ECDSAVerifyVariant::HashKeccak256    => ecdsa_signature.verify(&pub_key()?, &Keccak256::default(), &bits()),
261            ECDSAVerifyVariant::HashKeccak256Raw => ecdsa_signature.verify(&pub_key()?, &Keccak256::default(), &bits_raw()),
262            ECDSAVerifyVariant::HashKeccak256Eth => ecdsa_signature.verify_ethereum(&ethereum_address()?, &Keccak256::default(), &bits_raw()),
263            ECDSAVerifyVariant::HashKeccak384    => ecdsa_signature.verify(&pub_key()?, &Keccak384::default(), &bits()),
264            ECDSAVerifyVariant::HashKeccak384Raw => ecdsa_signature.verify(&pub_key()?, &Keccak384::default(), &bits_raw()),
265            ECDSAVerifyVariant::HashKeccak384Eth => ecdsa_signature.verify_ethereum(&ethereum_address()?, &Keccak384::default(), &bits_raw()),
266            ECDSAVerifyVariant::HashKeccak512    => ecdsa_signature.verify(&pub_key()?, &Keccak512::default(), &bits()),
267            ECDSAVerifyVariant::HashKeccak512Raw => ecdsa_signature.verify(&pub_key()?, &Keccak512::default(), &bits_raw()),
268            ECDSAVerifyVariant::HashKeccak512Eth => ecdsa_signature.verify_ethereum(&ethereum_address()?, &Keccak512::default(), &bits_raw()),
269            ECDSAVerifyVariant::HashSha3_256     => ecdsa_signature.verify(&pub_key()?, &Sha3_256::default(), &bits()),
270            ECDSAVerifyVariant::HashSha3_256Raw  => ecdsa_signature.verify(&pub_key()?, &Sha3_256::default(), &bits_raw()),
271            ECDSAVerifyVariant::HashSha3_256Eth  => ecdsa_signature.verify_ethereum(&ethereum_address()?, &Sha3_256::default(), &bits_raw()),
272            ECDSAVerifyVariant::HashSha3_384     => ecdsa_signature.verify(&pub_key()?, &Sha3_384::default(), &bits()),
273            ECDSAVerifyVariant::HashSha3_384Raw  => ecdsa_signature.verify(&pub_key()?, &Sha3_384::default(), &bits_raw()),
274            ECDSAVerifyVariant::HashSha3_384Eth  => ecdsa_signature.verify_ethereum(&ethereum_address()?, &Sha3_384::default(), &bits_raw()),
275            ECDSAVerifyVariant::HashSha3_512     => ecdsa_signature.verify(&pub_key()?, &Sha3_512::default(), &bits()),
276            ECDSAVerifyVariant::HashSha3_512Raw  => ecdsa_signature.verify(&pub_key()?, &Sha3_512::default(), &bits_raw()),
277            ECDSAVerifyVariant::HashSha3_512Eth  => ecdsa_signature.verify_ethereum(&ethereum_address()?, &Sha3_512::default(), &bits_raw()),
278        };
279
280        output.is_ok()
281    }};
282}
283
284/// Evaluate an ECDSA verification operation.
285///
286/// This allows running the verification without the machinery of stacks and registers.
287/// This is necessary for the Leo interpreter.
288pub fn evaluate_ecdsa_verification<N: Network>(
289    variant: ECDSAVerifyVariant,
290    signature: &Value<N>,
291    public_key: &Value<N>,
292    message: &Value<N>,
293) -> Result<bool> {
294    evaluate_ecdsa_verification_internal(variant, signature, public_key, message)
295}
296
297fn evaluate_ecdsa_verification_internal<N: Network>(
298    variant: ECDSAVerifyVariant,
299    signature: &Value<N>,
300    public_key: &Value<N>,
301    message: &Value<N>,
302) -> Result<bool> {
303    Ok(do_ecdsa_verification!(variant, signature, public_key, message))
304}
305
306impl<N: Network, const VARIANT: u8> ECDSAVerify<N, VARIANT> {
307    /// Evaluates the instruction.
308    #[inline]
309    pub fn evaluate(&self, _stack: &impl StackTrait<N>, _registers: &mut impl RegistersTrait<N>) -> Result<()> {
310        bail!("Instruction '{}' is currently only supported in finalize", Self::opcode());
311    }
312
313    /// Executes the instruction.
314    #[inline]
315    pub fn execute<A: circuit::Aleo<Network = N>>(
316        &self,
317        _stack: &impl StackTrait<N>,
318        _registers: &mut impl RegistersCircuit<N, A>,
319    ) -> Result<()> {
320        bail!("Instruction '{}' is currently only supported in finalize", Self::opcode());
321    }
322
323    /// Finalizes the instruction.
324    #[inline]
325    pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
326        // Ensure the number of operands is correct.
327        if self.operands.len() != 3 {
328            bail!("Instruction '{}' expects 3 operands, found {} operands", Self::opcode(), self.operands.len())
329        }
330
331        // Retrieve the inputs.
332        // Note: There is no need to check the types here, as this is done in `output_types`.
333        let signature = registers.load(stack, &self.operands[0])?;
334        let public_key = registers.load(stack, &self.operands[1])?;
335        let message = registers.load(stack, &self.operands[2])?;
336
337        // Perform the verification.
338        let output =
339            evaluate_ecdsa_verification_internal(ECDSAVerifyVariant::new(VARIANT), &signature, &public_key, &message)?;
340        let output = Literal::Boolean(Boolean::new(output));
341
342        // Store the output.
343        registers.store_literal(stack, &self.destination, output)
344    }
345
346    /// Returns the output type from the given program and input types.
347    #[inline]
348    pub fn output_types(
349        &self,
350        stack: &impl StackTrait<N>,
351        input_types: &[RegisterType<N>],
352    ) -> Result<Vec<RegisterType<N>>> {
353        // Ensure the number of input types is correct.
354        if input_types.len() != 3 {
355            bail!("Instruction '{}' expects 3 inputs, found {} inputs", Self::opcode(), input_types.len())
356        }
357
358        // Enforce that the signature is an array of 65 bytes.
359        match &input_types[0] {
360            RegisterType::Plaintext(PlaintextType::Array(array_type))
361                if array_type.base_element_type() == &PlaintextType::Literal(LiteralType::U8)
362                    && **array_type.length() as usize == ECDSASignature::SIGNATURE_SIZE_IN_BYTES =>
363            {
364                // valid signature array
365            }
366            _ => bail!(
367                "Instruction '{}' expects the first input to be a {}-byte array. Found input of type '{}'",
368                Self::opcode(),
369                ECDSASignature::SIGNATURE_SIZE_IN_BYTES,
370                input_types[0]
371            ),
372        }
373
374        // Get the variant.
375        let variant = ECDSAVerifyVariant::new(VARIANT);
376
377        // Expected byte length for the public key input depending on the variant.
378        let expected_length = match variant {
379            // Non-Ethereum address variant expects a compressed verifying key.
380            ECDSAVerifyVariant::Digest
381            | ECDSAVerifyVariant::HashKeccak256
382            | ECDSAVerifyVariant::HashKeccak256Raw
383            | ECDSAVerifyVariant::HashKeccak384
384            | ECDSAVerifyVariant::HashKeccak384Raw
385            | ECDSAVerifyVariant::HashKeccak512
386            | ECDSAVerifyVariant::HashKeccak512Raw
387            | ECDSAVerifyVariant::HashSha3_256
388            | ECDSAVerifyVariant::HashSha3_256Raw
389            | ECDSAVerifyVariant::HashSha3_384
390            | ECDSAVerifyVariant::HashSha3_384Raw
391            | ECDSAVerifyVariant::HashSha3_512
392            | ECDSAVerifyVariant::HashSha3_512Raw => ECDSASignature::VERIFYING_KEY_SIZE_IN_BYTES,
393            // Ethereum address variant expects a 20-byte array.
394            ECDSAVerifyVariant::DigestEth
395            | ECDSAVerifyVariant::HashKeccak256Eth
396            | ECDSAVerifyVariant::HashKeccak384Eth
397            | ECDSAVerifyVariant::HashKeccak512Eth
398            | ECDSAVerifyVariant::HashSha3_256Eth
399            | ECDSAVerifyVariant::HashSha3_384Eth
400            | ECDSAVerifyVariant::HashSha3_512Eth => ECDSASignature::ETHEREUM_ADDRESS_SIZE_IN_BYTES,
401        };
402
403        // Validate that the public key input type is correct.
404        match &input_types[1] {
405            RegisterType::Plaintext(PlaintextType::Array(array_type))
406                if array_type.base_element_type() == &PlaintextType::Literal(LiteralType::U8)
407                    && expected_length == **array_type.length() as usize => {}
408
409            invalid_input_type => bail!(
410                "Instruction '{}' expects the second input to be a {}-byte array. Found '{}'",
411                Self::opcode(),
412                expected_length,
413                invalid_input_type
414            ),
415        }
416
417        // If the variant uses a precomputed digest, ensure the message is a 32-byte array.
418        if matches!(variant, ECDSAVerifyVariant::Digest | ECDSAVerifyVariant::DigestEth) {
419            // Expected byte length for the digest input.
420            let expected_message_length = ECDSASignature::PREHASH_SIZE_IN_BYTES;
421
422            match &input_types[2] {
423                RegisterType::Plaintext(PlaintextType::Array(array_type))
424                    if array_type.base_element_type() == &PlaintextType::Literal(LiteralType::U8)
425                        && expected_message_length == **array_type.length() as usize => {}
426
427                invalid_input_type => bail!(
428                    "Instruction '{}' expects the third input to be a {}-byte array. Found '{}'",
429                    Self::opcode(),
430                    expected_message_length,
431                    invalid_input_type
432                ),
433            }
434        }
435        // Otherwise if the variant needs to be byte aligned, check that its size in bits is a multiple of 8.
436        else if variant.requires_byte_alignment() {
437            // A helper to get a struct declaration.
438            let get_struct = |identifier: &Identifier<N>| stack.program().get_struct(identifier).cloned();
439
440            // A helper to get a record declaration.
441            let get_record = |identifier: &Identifier<N>| stack.program().get_record(identifier).cloned();
442
443            // A helper to get an external record declaration.
444            let get_external_record = |locator: &Locator<N>| {
445                stack.get_external_stack(locator.program_id())?.program().get_record(locator.resource()).cloned()
446            };
447
448            // A helper to get the argument types of a future.
449            let get_future = |locator: &Locator<N>| {
450                Ok(match stack.program_id() == locator.program_id() {
451                    true => stack
452                        .program()
453                        .get_function_ref(locator.resource())?
454                        .finalize_logic()
455                        .ok_or_else(|| anyhow!("'{locator}' does not have a finalize scope"))?
456                        .input_types(),
457                    false => stack
458                        .get_external_stack(locator.program_id())?
459                        .program()
460                        .get_function_ref(locator.resource())?
461                        .finalize_logic()
462                        .ok_or_else(|| anyhow!("Failed to find function '{locator}'"))?
463                        .input_types(),
464                })
465            };
466
467            // Get the size in bits of the message.
468            let size_in_bits = match variant.is_raw() {
469                false => input_types[2].size_in_bits(&get_struct, &get_record, &get_external_record, &get_future)?,
470                true => input_types[2].size_in_bits_raw(&get_struct, &get_record, &get_external_record, &get_future)?,
471            };
472            // Check the number of bits.
473            ensure!(
474                size_in_bits % 8 == 0,
475                "Expected a multiple of 8 bits for '{}', found '{size_in_bits}'",
476                variant.opcode()
477            );
478        }
479
480        Ok(vec![RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Boolean))])
481    }
482}
483
484impl<N: Network, const VARIANT: u8> Parser for ECDSAVerify<N, VARIANT> {
485    /// Parses a string into an operation.
486    #[inline]
487    fn parse(string: &str) -> ParserResult<Self> {
488        // Parse the opcode from the string.
489        let (string, _) = tag(*Self::opcode())(string)?;
490        // Parse the whitespace from the string.
491        let (string, _) = Sanitizer::parse_whitespaces(string)?;
492        // Parse the first operand from the string.
493        let (string, first) = Operand::parse(string)?;
494        // Parse the whitespace from the string.
495        let (string, _) = Sanitizer::parse_whitespaces(string)?;
496        // Parse the second operand from the string.
497        let (string, second) = Operand::parse(string)?;
498        // Parse the whitespace from the string.
499        let (string, _) = Sanitizer::parse_whitespaces(string)?;
500        // Parse the third operand from the string.
501        let (string, third) = Operand::parse(string)?;
502        // Parse the whitespace from the string.
503        let (string, _) = Sanitizer::parse_whitespaces(string)?;
504        // Parse the "into" from the string.
505        let (string, _) = tag("into")(string)?;
506        // Parse the whitespace from the string.
507        let (string, _) = Sanitizer::parse_whitespaces(string)?;
508        // Parse the destination register from the string.
509        let (string, destination) = Register::parse(string)?;
510
511        Ok((string, Self { operands: vec![first, second, third], destination }))
512    }
513}
514
515impl<N: Network, const VARIANT: u8> FromStr for ECDSAVerify<N, VARIANT> {
516    type Err = Error;
517
518    /// Parses a string into an operation.
519    #[inline]
520    fn from_str(string: &str) -> Result<Self> {
521        match Self::parse(string) {
522            Ok((remainder, object)) => {
523                // Ensure the remainder is empty.
524                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
525                // Return the object.
526                Ok(object)
527            }
528            Err(error) => bail!("Failed to parse string. {error}"),
529        }
530    }
531}
532
533impl<N: Network, const VARIANT: u8> Debug for ECDSAVerify<N, VARIANT> {
534    /// Prints the operation as a string.
535    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
536        Display::fmt(self, f)
537    }
538}
539
540impl<N: Network, const VARIANT: u8> Display for ECDSAVerify<N, VARIANT> {
541    /// Prints the operation to a string.
542    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
543        // Ensure the number of operands is 3.
544        if self.operands.len() != 3 {
545            return Err(fmt::Error);
546        }
547        // Print the operation.
548        write!(f, "{} ", Self::opcode())?;
549        self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
550        write!(f, "into {}", self.destination)
551    }
552}
553
554impl<N: Network, const VARIANT: u8> FromBytes for ECDSAVerify<N, VARIANT> {
555    /// Reads the operation from a buffer.
556    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
557        // Initialize the vector for the operands.
558        let mut operands = Vec::with_capacity(3);
559        // Read the operands.
560        for _ in 0..3 {
561            operands.push(Operand::read_le(&mut reader)?);
562        }
563        // Read the destination register.
564        let destination = Register::read_le(&mut reader)?;
565
566        // Return the operation.
567        Ok(Self { operands, destination })
568    }
569}
570
571impl<N: Network, const VARIANT: u8> ToBytes for ECDSAVerify<N, VARIANT> {
572    /// Writes the operation to a buffer.
573    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
574        // Ensure the number of operands is 3.
575        if self.operands.len() != 3 {
576            return Err(error(format!("The number of operands must be 3, found {}", self.operands.len())));
577        }
578        // Write the operands.
579        self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
580        // Write the destination register.
581        self.destination.write_le(&mut writer)
582    }
583}
584
585#[cfg(test)]
586mod tests {
587    use super::*;
588    use console::network::MainnetV0;
589
590    type CurrentNetwork = MainnetV0;
591
592    #[test]
593    fn test_parse() {
594        let (string, is) = ECDSAVerifyDigest::<CurrentNetwork>::parse("ecdsa.verify.digest r0 r1 r2 into r3").unwrap();
595        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
596        assert_eq!(is.operands.len(), 3, "The number of operands is incorrect");
597        assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
598        assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
599        assert_eq!(is.operands[2], Operand::Register(Register::Locator(2)), "The third operand is incorrect");
600        assert_eq!(is.destination, Register::Locator(3), "The destination register is incorrect");
601
602        let (string, is) =
603            ECDSAVerifyDigestEth::<CurrentNetwork>::parse("ecdsa.verify.digest.eth r0 r1 r2 into r3").unwrap();
604        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
605        assert_eq!(is.operands.len(), 3, "The number of operands is incorrect");
606        assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
607        assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
608        assert_eq!(is.operands[2], Operand::Register(Register::Locator(2)), "The third operand is incorrect");
609        assert_eq!(is.destination, Register::Locator(3), "The destination register is incorrect");
610
611        let (string, is) =
612            ECDSAVerifyKeccak256::<CurrentNetwork>::parse("ecdsa.verify.keccak256 r0 r1 r2 into r3").unwrap();
613        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
614        assert_eq!(is.operands.len(), 3, "The number of operands is incorrect");
615        assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
616        assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
617        assert_eq!(is.operands[2], Operand::Register(Register::Locator(2)), "The third operand is incorrect");
618        assert_eq!(is.destination, Register::Locator(3), "The destination register is incorrect");
619
620        let (string, is) =
621            ECDSAVerifyKeccak256Raw::<CurrentNetwork>::parse("ecdsa.verify.keccak256.raw r0 r1 r2 into r3").unwrap();
622        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
623        assert_eq!(is.operands.len(), 3, "The number of operands is incorrect");
624        assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
625        assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
626        assert_eq!(is.operands[2], Operand::Register(Register::Locator(2)), "The third operand is incorrect");
627        assert_eq!(is.destination, Register::Locator(3), "The destination register is incorrect");
628
629        let (string, is) =
630            ECDSAVerifyKeccak256Eth::<CurrentNetwork>::parse("ecdsa.verify.keccak256.eth r0 r1 r2 into r3").unwrap();
631        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
632        assert_eq!(is.operands.len(), 3, "The number of operands is incorrect");
633        assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
634        assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
635        assert_eq!(is.operands[2], Operand::Register(Register::Locator(2)), "The third operand is incorrect");
636        assert_eq!(is.destination, Register::Locator(3), "The destination register is incorrect");
637    }
638}