1use 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
26pub type HashBHP256<N> = HashInstruction<N, { Hasher::HashBHP256 as u8 }>;
28pub type HashBHP512<N> = HashInstruction<N, { Hasher::HashBHP512 as u8 }>;
30pub type HashBHP768<N> = HashInstruction<N, { Hasher::HashBHP768 as u8 }>;
32pub type HashBHP1024<N> = HashInstruction<N, { Hasher::HashBHP1024 as u8 }>;
34
35pub type HashKeccak256<N> = HashInstruction<N, { Hasher::HashKeccak256 as u8 }>;
37pub type HashKeccak384<N> = HashInstruction<N, { Hasher::HashKeccak384 as u8 }>;
39pub type HashKeccak512<N> = HashInstruction<N, { Hasher::HashKeccak512 as u8 }>;
41
42pub type HashPED64<N> = HashInstruction<N, { Hasher::HashPED64 as u8 }>;
44pub type HashPED128<N> = HashInstruction<N, { Hasher::HashPED128 as u8 }>;
46
47pub type HashPSD2<N> = HashInstruction<N, { Hasher::HashPSD2 as u8 }>;
49pub type HashPSD4<N> = HashInstruction<N, { Hasher::HashPSD4 as u8 }>;
51pub type HashPSD8<N> = HashInstruction<N, { Hasher::HashPSD8 as u8 }>;
53
54pub type HashSha3_256<N> = HashInstruction<N, { Hasher::HashSha3_256 as u8 }>;
56pub type HashSha3_384<N> = HashInstruction<N, { Hasher::HashSha3_384 as u8 }>;
58pub type HashSha3_512<N> = HashInstruction<N, { Hasher::HashSha3_512 as u8 }>;
60
61pub type HashManyPSD2<N> = HashInstruction<N, { Hasher::HashManyPSD2 as u8 }>;
63pub type HashManyPSD4<N> = HashInstruction<N, { Hasher::HashManyPSD4 as u8 }>;
65pub 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
89const fn expected_num_operands(variant: u8) -> usize {
91 match variant {
92 15..=17 => 2,
93 _ => 1,
94 }
95}
96
97fn 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
107fn 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#[derive(Clone, PartialEq, Eq, Hash)]
120pub struct HashInstruction<N: Network, const VARIANT: u8> {
121 operands: Vec<Operand<N>>,
123 destination: Register<N>,
125 destination_type: PlaintextType<N>,
127}
128
129impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
130 #[inline]
132 pub fn new(
133 operands: Vec<Operand<N>>,
134 destination: Register<N>,
135 destination_type: PlaintextType<N>,
136 ) -> Result<Self> {
137 check_number_of_operands(VARIANT, Self::opcode(), operands.len())?;
139 if !is_valid_destination_type(&destination_type) {
141 bail!("Invalid destination type for 'hash' instruction")
142 }
143 Ok(Self { operands, destination, destination_type })
145 }
146
147 #[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 #[inline]
175 pub fn operands(&self) -> &[Operand<N>] {
176 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 &self.operands
184 }
185
186 #[inline]
188 pub fn destinations(&self) -> Vec<Register<N>> {
189 vec![self.destination.clone()]
190 }
191
192 #[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 #[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 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
209 ensure!(is_valid_destination_type(&self.destination_type), "Invalid destination type in 'hash' instruction");
211
212 let input = registers.load(stack, &self.operands[0])?;
214 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 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 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(output)))
267 }
268
269 #[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 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
280 ensure!(is_valid_destination_type(&self.destination_type), "Invalid destination type in 'hash' instruction");
282
283 let input = registers.load_circuit(stack, &self.operands[0])?;
285 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 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 let output = circuit::Value::Plaintext(circuit::Plaintext::Literal(output, Default::default()));
338 registers.store_circuit(stack, &self.destination, output)
340 }
341
342 #[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 #[inline]
354 pub fn output_types(
355 &self,
356 _stack: &impl StackProgram<N>,
357 input_types: &[RegisterType<N>],
358 ) -> Result<Vec<RegisterType<N>>> {
359 check_number_of_operands(VARIANT, Self::opcode(), input_types.len())?;
361 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
363 ensure!(is_valid_destination_type(&self.destination_type), "Invalid destination type in 'hash' instruction");
365
366 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 #[inline]
379 fn parse(string: &str) -> ParserResult<Self> {
380 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 let (next_string, _) = Sanitizer::parse_whitespaces(string)?;
388 let (next_string, operand) = Operand::parse(next_string)?;
390 string = next_string;
392 operands.push(operand);
394 }
395
396 Ok((string, operands))
397 }
398
399 let (string, _) = tag(*Self::opcode())(string)?;
401 let (string, operands) = parse_operands(string, expected_num_operands(VARIANT))?;
403 let (string, _) = Sanitizer::parse_whitespaces(string)?;
405 let (string, _) = tag("into")(string)?;
407 let (string, _) = Sanitizer::parse_whitespaces(string)?;
409 let (string, destination) = Register::parse(string)?;
411 let (string, _) = Sanitizer::parse_whitespaces(string)?;
413 let (string, _) = tag("as")(string)?;
415 let (string, _) = Sanitizer::parse_whitespaces(string)?;
417 let (string, destination_type) = PlaintextType::parse(string)?;
419 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 #[inline]
436 fn from_str(string: &str) -> Result<Self> {
437 match Self::parse(string) {
438 Ok((remainder, object)) => {
439 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
441 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 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 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
459 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|_| fmt::Error)?;
461 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 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
471 let num_operands = expected_num_operands(VARIANT);
473 let operands = (0..num_operands).map(|_| Operand::read_le(&mut reader)).collect::<Result<_, _>>()?;
475 let destination = Register::read_le(&mut reader)?;
477 let destination_type = PlaintextType::read_le(&mut reader)?;
479 Ok(Self { operands, destination, destination_type })
481 }
482}
483
484impl<N: Network, const VARIANT: u8> ToBytes for HashInstruction<N, VARIANT> {
485 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
487 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|e| error(format!("{e}")))?;
489 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
491 self.destination.write_le(&mut writer)?;
493 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 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}