snarkvm_synthesizer_program/logic/instruction/operation/
hash.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
16use crate::{
17    Opcode,
18    Operand,
19    traits::{RegistersLoad, RegistersLoadCircuit, RegistersStore, RegistersStoreCircuit, StackMatches, StackProgram},
20};
21use console::{
22    network::prelude::*,
23    program::{Literal, LiteralType, Plaintext, PlaintextType, Register, RegisterType, Value},
24};
25
26/// BHP256 is a collision-resistant hash function that processes inputs in 256-bit chunks.
27pub type HashBHP256<N> = HashInstruction<N, { Hasher::HashBHP256 as u8 }>;
28/// BHP512 is a collision-resistant hash function that processes inputs in 512-bit chunks.
29pub type HashBHP512<N> = HashInstruction<N, { Hasher::HashBHP512 as u8 }>;
30/// BHP768 is a collision-resistant hash function that processes inputs in 768-bit chunks.
31pub type HashBHP768<N> = HashInstruction<N, { Hasher::HashBHP768 as u8 }>;
32/// BHP1024 is a collision-resistant hash function that processes inputs in 1024-bit chunks.
33pub type HashBHP1024<N> = HashInstruction<N, { Hasher::HashBHP1024 as u8 }>;
34
35/// Keccak256 is a cryptographic hash function that outputs a 256-bit digest.
36pub type HashKeccak256<N> = HashInstruction<N, { Hasher::HashKeccak256 as u8 }>;
37/// Keccak384 is a cryptographic hash function that outputs a 384-bit digest.
38pub type HashKeccak384<N> = HashInstruction<N, { Hasher::HashKeccak384 as u8 }>;
39/// Keccak512 is a cryptographic hash function that outputs a 512-bit digest.
40pub type HashKeccak512<N> = HashInstruction<N, { Hasher::HashKeccak512 as u8 }>;
41
42/// Pedersen64 is a collision-resistant hash function that processes inputs in 64-bit chunks.
43pub type HashPED64<N> = HashInstruction<N, { Hasher::HashPED64 as u8 }>;
44/// Pedersen128 is a collision-resistant hash function that processes inputs in 128-bit chunks.
45pub type HashPED128<N> = HashInstruction<N, { Hasher::HashPED128 as u8 }>;
46
47/// Poseidon2 is a cryptographic hash function that processes inputs in 2-field chunks.
48pub type HashPSD2<N> = HashInstruction<N, { Hasher::HashPSD2 as u8 }>;
49/// Poseidon4 is a cryptographic hash function that processes inputs in 4-field chunks.
50pub type HashPSD4<N> = HashInstruction<N, { Hasher::HashPSD4 as u8 }>;
51/// Poseidon8 is a cryptographic hash function that processes inputs in 8-field chunks.
52pub type HashPSD8<N> = HashInstruction<N, { Hasher::HashPSD8 as u8 }>;
53
54/// SHA3-256 is a cryptographic hash function that outputs a 256-bit digest.
55pub type HashSha3_256<N> = HashInstruction<N, { Hasher::HashSha3_256 as u8 }>;
56/// SHA3-384 is a cryptographic hash function that outputs a 384-bit digest.
57pub type HashSha3_384<N> = HashInstruction<N, { Hasher::HashSha3_384 as u8 }>;
58/// SHA3-512 is a cryptographic hash function that outputs a 512-bit digest.
59pub type HashSha3_512<N> = HashInstruction<N, { Hasher::HashSha3_512 as u8 }>;
60
61/// Poseidon2 is a cryptographic hash function that processes inputs in 2-field chunks.
62pub type HashManyPSD2<N> = HashInstruction<N, { Hasher::HashManyPSD2 as u8 }>;
63/// Poseidon4 is a cryptographic hash function that processes inputs in 4-field chunks.
64pub type HashManyPSD4<N> = HashInstruction<N, { Hasher::HashManyPSD4 as u8 }>;
65/// Poseidon8 is a cryptographic hash function that processes inputs in 8-field chunks.
66pub type HashManyPSD8<N> = HashInstruction<N, { Hasher::HashManyPSD8 as u8 }>;
67
68enum Hasher {
69    HashBHP256,
70    HashBHP512,
71    HashBHP768,
72    HashBHP1024,
73    HashKeccak256,
74    HashKeccak384,
75    HashKeccak512,
76    HashPED64,
77    HashPED128,
78    HashPSD2,
79    HashPSD4,
80    HashPSD8,
81    HashSha3_256,
82    HashSha3_384,
83    HashSha3_512,
84    HashManyPSD2,
85    HashManyPSD4,
86    HashManyPSD8,
87}
88
89/// Returns the expected number of operands given the variant.
90const fn expected_num_operands(variant: u8) -> usize {
91    match variant {
92        15..=17 => 2,
93        _ => 1,
94    }
95}
96
97/// Returns 'Ok(())' if the number of operands is correct.
98/// Otherwise, returns an error.
99fn check_number_of_operands(variant: u8, opcode: Opcode, num_operands: usize) -> Result<()> {
100    let expected = expected_num_operands(variant);
101    if expected != num_operands {
102        bail!("Instruction '{opcode}' expects {expected} operands, found {num_operands} operands")
103    }
104    Ok(())
105}
106
107/// Returns 'true' if the destination type is valid.
108fn is_valid_destination_type<N: Network>(destination_type: &PlaintextType<N>) -> bool {
109    !matches!(
110        destination_type,
111        PlaintextType::Literal(LiteralType::Boolean)
112            | PlaintextType::Literal(LiteralType::String)
113            | PlaintextType::Struct(..)
114            | PlaintextType::Array(..)
115    )
116}
117
118/// Hashes the operand into the declared type.
119#[derive(Clone, PartialEq, Eq, Hash)]
120pub struct HashInstruction<N: Network, const VARIANT: u8> {
121    /// The operand as `input`.
122    operands: Vec<Operand<N>>,
123    /// The destination register.
124    destination: Register<N>,
125    /// The destination register type.
126    destination_type: PlaintextType<N>,
127}
128
129impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
130    /// Initializes a new `hash` instruction.
131    #[inline]
132    pub fn new(
133        operands: Vec<Operand<N>>,
134        destination: Register<N>,
135        destination_type: PlaintextType<N>,
136    ) -> Result<Self> {
137        // Sanity check the number of operands.
138        check_number_of_operands(VARIANT, Self::opcode(), operands.len())?;
139        // Sanity check the destination type.
140        if !is_valid_destination_type(&destination_type) {
141            bail!("Invalid destination type for 'hash' instruction")
142        }
143        // Return the instruction.
144        Ok(Self { operands, destination, destination_type })
145    }
146
147    /// Returns the opcode.
148    #[inline]
149    pub const fn opcode() -> Opcode {
150        match VARIANT {
151            0 => Opcode::Hash("hash.bhp256"),
152            1 => Opcode::Hash("hash.bhp512"),
153            2 => Opcode::Hash("hash.bhp768"),
154            3 => Opcode::Hash("hash.bhp1024"),
155            4 => Opcode::Hash("hash.keccak256"),
156            5 => Opcode::Hash("hash.keccak384"),
157            6 => Opcode::Hash("hash.keccak512"),
158            7 => Opcode::Hash("hash.ped64"),
159            8 => Opcode::Hash("hash.ped128"),
160            9 => Opcode::Hash("hash.psd2"),
161            10 => Opcode::Hash("hash.psd4"),
162            11 => Opcode::Hash("hash.psd8"),
163            12 => Opcode::Hash("hash.sha3_256"),
164            13 => Opcode::Hash("hash.sha3_384"),
165            14 => Opcode::Hash("hash.sha3_512"),
166            15 => Opcode::Hash("hash_many.psd2"),
167            16 => Opcode::Hash("hash_many.psd4"),
168            17 => Opcode::Hash("hash_many.psd8"),
169            18.. => panic!("Invalid 'hash' instruction opcode"),
170        }
171    }
172
173    /// Returns the operands in the operation.
174    #[inline]
175    pub fn operands(&self) -> &[Operand<N>] {
176        // Sanity check that the operands is the correct length.
177        debug_assert!(
178            check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).is_ok(),
179            "Invalid number of operands for '{}'",
180            Self::opcode()
181        );
182        // Return the operand.
183        &self.operands
184    }
185
186    /// Returns the destination register.
187    #[inline]
188    pub fn destinations(&self) -> Vec<Register<N>> {
189        vec![self.destination.clone()]
190    }
191
192    /// Returns the destination register type.
193    #[inline]
194    pub const fn destination_type(&self) -> &PlaintextType<N> {
195        &self.destination_type
196    }
197}
198
199impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
200    /// Evaluates the instruction.
201    #[inline]
202    pub fn evaluate(
203        &self,
204        stack: &(impl StackMatches<N> + StackProgram<N>),
205        registers: &mut (impl RegistersLoad<N> + RegistersStore<N>),
206    ) -> Result<()> {
207        // Ensure the number of operands is correct.
208        check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
209        // Ensure the destination type is valid.
210        ensure!(is_valid_destination_type(&self.destination_type), "Invalid destination type in 'hash' instruction");
211
212        // Load the operand.
213        let input = registers.load(stack, &self.operands[0])?;
214        // Hash the input.
215        let output = match (VARIANT, &self.destination_type) {
216            (0, PlaintextType::Literal(..)) => Literal::Group(N::hash_to_group_bhp256(&input.to_bits_le())?),
217            (1, PlaintextType::Literal(..)) => Literal::Group(N::hash_to_group_bhp512(&input.to_bits_le())?),
218            (2, PlaintextType::Literal(..)) => Literal::Group(N::hash_to_group_bhp768(&input.to_bits_le())?),
219            (3, PlaintextType::Literal(..)) => Literal::Group(N::hash_to_group_bhp1024(&input.to_bits_le())?),
220            (4, PlaintextType::Literal(..)) => {
221                Literal::Group(N::hash_to_group_bhp256(&N::hash_keccak256(&input.to_bits_le())?)?)
222            }
223            (5, PlaintextType::Literal(..)) => {
224                Literal::Group(N::hash_to_group_bhp512(&N::hash_keccak384(&input.to_bits_le())?)?)
225            }
226            (6, PlaintextType::Literal(..)) => {
227                Literal::Group(N::hash_to_group_bhp512(&N::hash_keccak512(&input.to_bits_le())?)?)
228            }
229            (7, PlaintextType::Literal(..)) => Literal::Group(N::hash_to_group_ped64(&input.to_bits_le())?),
230            (8, PlaintextType::Literal(..)) => Literal::Group(N::hash_to_group_ped128(&input.to_bits_le())?),
231            (9, PlaintextType::Literal(LiteralType::Address)) | (9, PlaintextType::Literal(LiteralType::Group)) => {
232                Literal::Group(N::hash_to_group_psd2(&input.to_fields()?)?)
233            }
234            (9, PlaintextType::Literal(..)) => Literal::Field(N::hash_psd2(&input.to_fields()?)?),
235            (10, PlaintextType::Literal(LiteralType::Address)) | (10, PlaintextType::Literal(LiteralType::Group)) => {
236                Literal::Group(N::hash_to_group_psd4(&input.to_fields()?)?)
237            }
238            (10, PlaintextType::Literal(..)) => Literal::Field(N::hash_psd4(&input.to_fields()?)?),
239            (11, PlaintextType::Literal(LiteralType::Address)) | (11, PlaintextType::Literal(LiteralType::Group)) => {
240                Literal::Group(N::hash_to_group_psd8(&input.to_fields()?)?)
241            }
242            (11, PlaintextType::Literal(..)) => Literal::Field(N::hash_psd8(&input.to_fields()?)?),
243            (12, PlaintextType::Literal(..)) => {
244                Literal::Group(N::hash_to_group_bhp256(&N::hash_sha3_256(&input.to_bits_le())?)?)
245            }
246            (13, PlaintextType::Literal(..)) => {
247                Literal::Group(N::hash_to_group_bhp512(&N::hash_sha3_384(&input.to_bits_le())?)?)
248            }
249            (14, PlaintextType::Literal(..)) => {
250                Literal::Group(N::hash_to_group_bhp512(&N::hash_sha3_512(&input.to_bits_le())?)?)
251            }
252            (15, _) => bail!("'hash_many.psd2' is not yet implemented"),
253            (16, _) => bail!("'hash_many.psd4' is not yet implemented"),
254            (17, _) => bail!("'hash_many.psd8' is not yet implemented"),
255            (18.., _) => bail!("Invalid 'hash' variant: {VARIANT}"),
256            (_, PlaintextType::Struct(..)) => bail!("Cannot hash into a struct"),
257            (_, PlaintextType::Array(..)) => bail!("Cannot hash into an array (yet)"),
258        };
259        // Cast the output to the destination type.
260        let output = match self.destination_type {
261            PlaintextType::Literal(literal_type) => output.cast_lossy(literal_type)?,
262            PlaintextType::Struct(..) => bail!("Cannot hash into a struct"),
263            PlaintextType::Array(..) => bail!("Cannot hash into an array (yet)"),
264        };
265        // Store the output.
266        registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(output)))
267    }
268
269    /// Executes the instruction.
270    #[inline]
271    pub fn execute<A: circuit::Aleo<Network = N>>(
272        &self,
273        stack: &(impl StackMatches<N> + StackProgram<N>),
274        registers: &mut (impl RegistersLoadCircuit<N, A> + RegistersStoreCircuit<N, A>),
275    ) -> Result<()> {
276        use circuit::traits::{ToBits, ToFields};
277
278        // Ensure the number of operands is correct.
279        check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
280        // Ensure the destination type is valid.
281        ensure!(is_valid_destination_type(&self.destination_type), "Invalid destination type in 'hash' instruction");
282
283        // Load the operand.
284        let input = registers.load_circuit(stack, &self.operands[0])?;
285        // Hash the input.
286        let output = match (VARIANT, &self.destination_type) {
287            (0, PlaintextType::Literal(..)) => circuit::Literal::Group(A::hash_to_group_bhp256(&input.to_bits_le())),
288            (1, PlaintextType::Literal(..)) => circuit::Literal::Group(A::hash_to_group_bhp512(&input.to_bits_le())),
289            (2, PlaintextType::Literal(..)) => circuit::Literal::Group(A::hash_to_group_bhp768(&input.to_bits_le())),
290            (3, PlaintextType::Literal(..)) => circuit::Literal::Group(A::hash_to_group_bhp1024(&input.to_bits_le())),
291            (4, PlaintextType::Literal(..)) => {
292                circuit::Literal::Group(A::hash_to_group_bhp256(&A::hash_keccak256(&input.to_bits_le())))
293            }
294            (5, PlaintextType::Literal(..)) => {
295                circuit::Literal::Group(A::hash_to_group_bhp512(&A::hash_keccak384(&input.to_bits_le())))
296            }
297            (6, PlaintextType::Literal(..)) => {
298                circuit::Literal::Group(A::hash_to_group_bhp512(&A::hash_keccak512(&input.to_bits_le())))
299            }
300            (7, PlaintextType::Literal(..)) => circuit::Literal::Group(A::hash_to_group_ped64(&input.to_bits_le())),
301            (8, PlaintextType::Literal(..)) => circuit::Literal::Group(A::hash_to_group_ped128(&input.to_bits_le())),
302            (9, PlaintextType::Literal(LiteralType::Address)) | (9, PlaintextType::Literal(LiteralType::Group)) => {
303                circuit::Literal::Group(A::hash_to_group_psd2(&input.to_fields()))
304            }
305            (9, PlaintextType::Literal(..)) => circuit::Literal::Field(A::hash_psd2(&input.to_fields())),
306            (10, PlaintextType::Literal(LiteralType::Address)) | (10, PlaintextType::Literal(LiteralType::Group)) => {
307                circuit::Literal::Group(A::hash_to_group_psd4(&input.to_fields()))
308            }
309            (10, PlaintextType::Literal(..)) => circuit::Literal::Field(A::hash_psd4(&input.to_fields())),
310            (11, PlaintextType::Literal(LiteralType::Address)) | (11, PlaintextType::Literal(LiteralType::Group)) => {
311                circuit::Literal::Group(A::hash_to_group_psd8(&input.to_fields()))
312            }
313            (11, PlaintextType::Literal(..)) => circuit::Literal::Field(A::hash_psd8(&input.to_fields())),
314            (12, PlaintextType::Literal(..)) => {
315                circuit::Literal::Group(A::hash_to_group_bhp256(&A::hash_sha3_256(&input.to_bits_le())))
316            }
317            (13, PlaintextType::Literal(..)) => {
318                circuit::Literal::Group(A::hash_to_group_bhp512(&A::hash_sha3_384(&input.to_bits_le())))
319            }
320            (14, PlaintextType::Literal(..)) => {
321                circuit::Literal::Group(A::hash_to_group_bhp512(&A::hash_sha3_512(&input.to_bits_le())))
322            }
323            (15, _) => bail!("'hash_many.psd2' is not yet implemented"),
324            (16, _) => bail!("'hash_many.psd4' is not yet implemented"),
325            (17, _) => bail!("'hash_many.psd8' is not yet implemented"),
326            (18.., _) => bail!("Invalid 'hash' variant: {VARIANT}"),
327            (_, PlaintextType::Struct(..)) => bail!("Cannot hash into a struct"),
328            (_, PlaintextType::Array(..)) => bail!("Cannot hash into an array (yet)"),
329        };
330        // Cast the output to the destination type.
331        let output = match self.destination_type {
332            PlaintextType::Literal(literal_type) => output.cast_lossy(literal_type)?,
333            PlaintextType::Struct(..) => bail!("Cannot hash into a struct"),
334            PlaintextType::Array(..) => bail!("Cannot hash into an array (yet)"),
335        };
336        // Convert the output to a stack value.
337        let output = circuit::Value::Plaintext(circuit::Plaintext::Literal(output, Default::default()));
338        // Store the output.
339        registers.store_circuit(stack, &self.destination, output)
340    }
341
342    /// Finalizes the instruction.
343    #[inline]
344    pub fn finalize(
345        &self,
346        stack: &(impl StackMatches<N> + StackProgram<N>),
347        registers: &mut (impl RegistersLoad<N> + RegistersStore<N>),
348    ) -> Result<()> {
349        self.evaluate(stack, registers)
350    }
351
352    /// Returns the output type from the given program and input types.
353    #[inline]
354    pub fn output_types(
355        &self,
356        _stack: &impl StackProgram<N>,
357        input_types: &[RegisterType<N>],
358    ) -> Result<Vec<RegisterType<N>>> {
359        // Ensure the number of input types is correct.
360        check_number_of_operands(VARIANT, Self::opcode(), input_types.len())?;
361        // Ensure the number of operands is correct.
362        check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
363        // Ensure the destination type is valid.
364        ensure!(is_valid_destination_type(&self.destination_type), "Invalid destination type in 'hash' instruction");
365
366        // TODO (howardwu): If the operation is Pedersen, check that it is within the number of bits.
367
368        match VARIANT {
369            0..=14 => Ok(vec![RegisterType::Plaintext(self.destination_type.clone())]),
370            15..=17 => bail!("'hash_many' is not yet implemented"),
371            18.. => bail!("Invalid 'hash' variant: {VARIANT}"),
372        }
373    }
374}
375
376impl<N: Network, const VARIANT: u8> Parser for HashInstruction<N, VARIANT> {
377    /// Parses a string into an operation.
378    #[inline]
379    fn parse(string: &str) -> ParserResult<Self> {
380        /// Parse the operands from the string.
381        fn parse_operands<N: Network>(string: &str, num_operands: usize) -> ParserResult<Vec<Operand<N>>> {
382            let mut operands = Vec::with_capacity(num_operands);
383            let mut string = string;
384
385            for _ in 0..num_operands {
386                // Parse the whitespace from the string.
387                let (next_string, _) = Sanitizer::parse_whitespaces(string)?;
388                // Parse the operand from the string.
389                let (next_string, operand) = Operand::parse(next_string)?;
390                // Update the string.
391                string = next_string;
392                // Push the operand.
393                operands.push(operand);
394            }
395
396            Ok((string, operands))
397        }
398
399        // Parse the opcode from the string.
400        let (string, _) = tag(*Self::opcode())(string)?;
401        // Parse the operands from the string.
402        let (string, operands) = parse_operands(string, expected_num_operands(VARIANT))?;
403        // Parse the whitespace from the string.
404        let (string, _) = Sanitizer::parse_whitespaces(string)?;
405        // Parse the "into" from the string.
406        let (string, _) = tag("into")(string)?;
407        // Parse the whitespace from the string.
408        let (string, _) = Sanitizer::parse_whitespaces(string)?;
409        // Parse the destination register from the string.
410        let (string, destination) = Register::parse(string)?;
411        // Parse the whitespace from the string.
412        let (string, _) = Sanitizer::parse_whitespaces(string)?;
413        // Parse the "as" from the string.
414        let (string, _) = tag("as")(string)?;
415        // Parse the whitespace from the string.
416        let (string, _) = Sanitizer::parse_whitespaces(string)?;
417        // Parse the destination register type from the string.
418        let (string, destination_type) = PlaintextType::parse(string)?;
419        // Ensure the destination type is allowed.
420        match destination_type {
421            PlaintextType::Literal(LiteralType::Boolean) | PlaintextType::Literal(LiteralType::String) => {
422                map_res(fail, |_: ParserResult<Self>| {
423                    Err(error(format!("Failed to parse 'hash': '{destination_type}' is invalid")))
424                })(string)
425            }
426            _ => Ok((string, Self { operands, destination, destination_type })),
427        }
428    }
429}
430
431impl<N: Network, const VARIANT: u8> FromStr for HashInstruction<N, VARIANT> {
432    type Err = Error;
433
434    /// Parses a string into an operation.
435    #[inline]
436    fn from_str(string: &str) -> Result<Self> {
437        match Self::parse(string) {
438            Ok((remainder, object)) => {
439                // Ensure the remainder is empty.
440                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
441                // Return the object.
442                Ok(object)
443            }
444            Err(error) => bail!("Failed to parse string. {error}"),
445        }
446    }
447}
448
449impl<N: Network, const VARIANT: u8> Debug for HashInstruction<N, VARIANT> {
450    /// Prints the operation as a string.
451    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
452        Display::fmt(self, f)
453    }
454}
455
456impl<N: Network, const VARIANT: u8> Display for HashInstruction<N, VARIANT> {
457    /// Prints the operation to a string.
458    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
459        // Ensure the number of operands is correct.
460        check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|_| fmt::Error)?;
461        // Print the operation.
462        write!(f, "{} ", Self::opcode())?;
463        self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
464        write!(f, "into {} as {}", self.destination, self.destination_type)
465    }
466}
467
468impl<N: Network, const VARIANT: u8> FromBytes for HashInstruction<N, VARIANT> {
469    /// Reads the operation from a buffer.
470    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
471        // Prepare the number of operands.
472        let num_operands = expected_num_operands(VARIANT);
473        // Read the operands.
474        let operands = (0..num_operands).map(|_| Operand::read_le(&mut reader)).collect::<Result<_, _>>()?;
475        // Read the destination register.
476        let destination = Register::read_le(&mut reader)?;
477        // Read the destination register type.
478        let destination_type = PlaintextType::read_le(&mut reader)?;
479        // Return the operation.
480        Ok(Self { operands, destination, destination_type })
481    }
482}
483
484impl<N: Network, const VARIANT: u8> ToBytes for HashInstruction<N, VARIANT> {
485    /// Writes the operation to a buffer.
486    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
487        // Ensure the number of operands is correct.
488        check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|e| error(format!("{e}")))?;
489        // Write the operands.
490        self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
491        // Write the destination register.
492        self.destination.write_le(&mut writer)?;
493        // Write the destination register type.
494        self.destination_type.write_le(&mut writer)
495    }
496}
497
498#[cfg(test)]
499mod tests {
500    use super::*;
501    use console::network::MainnetV0;
502
503    type CurrentNetwork = MainnetV0;
504
505    /// **Attention**: When changing this, also update in `tests/instruction/hash.rs`.
506    fn valid_destination_types<N: Network>() -> &'static [PlaintextType<N>] {
507        &[
508            PlaintextType::Literal(LiteralType::Address),
509            PlaintextType::Literal(LiteralType::Field),
510            PlaintextType::Literal(LiteralType::Group),
511            PlaintextType::Literal(LiteralType::I8),
512            PlaintextType::Literal(LiteralType::I16),
513            PlaintextType::Literal(LiteralType::I32),
514            PlaintextType::Literal(LiteralType::I64),
515            PlaintextType::Literal(LiteralType::I128),
516            PlaintextType::Literal(LiteralType::U8),
517            PlaintextType::Literal(LiteralType::U16),
518            PlaintextType::Literal(LiteralType::U32),
519            PlaintextType::Literal(LiteralType::U64),
520            PlaintextType::Literal(LiteralType::U128),
521            PlaintextType::Literal(LiteralType::Scalar),
522        ]
523    }
524
525    #[test]
526    fn test_parse() {
527        for destination_type in valid_destination_types() {
528            let instruction = format!("hash.bhp512 r0 into r1 as {destination_type}");
529            let (string, hash) = HashBHP512::<CurrentNetwork>::parse(&instruction).unwrap();
530            assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
531            assert_eq!(hash.operands.len(), 1, "The number of operands is incorrect");
532            assert_eq!(hash.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
533            assert_eq!(hash.destination, Register::Locator(1), "The destination register is incorrect");
534            assert_eq!(&hash.destination_type, destination_type, "The destination type is incorrect");
535        }
536    }
537}