1use crate::{Opcode, Operand, RegistersCircuit, RegistersTrait, StackTrait};
17use console::{
18 network::prelude::*,
19 program::{Identifier, Literal, LiteralType, Locator, Plaintext, PlaintextType, Register, RegisterType, Value},
20};
21
22use enum_iterator::Sequence;
23
24pub type HashBHP256<N> = HashInstruction<N, { HashVariant::HashBHP256 as u8 }>;
26pub type HashBHP512<N> = HashInstruction<N, { HashVariant::HashBHP512 as u8 }>;
28pub type HashBHP768<N> = HashInstruction<N, { HashVariant::HashBHP768 as u8 }>;
30pub type HashBHP1024<N> = HashInstruction<N, { HashVariant::HashBHP1024 as u8 }>;
32
33pub type HashBHP256Raw<N> = HashInstruction<N, { HashVariant::HashBHP256Raw as u8 }>;
35pub type HashBHP512Raw<N> = HashInstruction<N, { HashVariant::HashBHP512Raw as u8 }>;
37pub type HashBHP768Raw<N> = HashInstruction<N, { HashVariant::HashBHP768Raw as u8 }>;
39pub type HashBHP1024Raw<N> = HashInstruction<N, { HashVariant::HashBHP1024Raw as u8 }>;
41
42pub type HashKeccak256<N> = HashInstruction<N, { HashVariant::HashKeccak256 as u8 }>;
44pub type HashKeccak384<N> = HashInstruction<N, { HashVariant::HashKeccak384 as u8 }>;
46pub type HashKeccak512<N> = HashInstruction<N, { HashVariant::HashKeccak512 as u8 }>;
48
49pub type HashKeccak256Raw<N> = HashInstruction<N, { HashVariant::HashKeccak256Raw as u8 }>;
51pub type HashKeccak384Raw<N> = HashInstruction<N, { HashVariant::HashKeccak384Raw as u8 }>;
53pub type HashKeccak512Raw<N> = HashInstruction<N, { HashVariant::HashKeccak512Raw as u8 }>;
55
56pub type HashKeccak256Native<N> = HashInstruction<N, { HashVariant::HashKeccak256Native as u8 }>;
58pub type HashKeccak384Native<N> = HashInstruction<N, { HashVariant::HashKeccak384Native as u8 }>;
60pub type HashKeccak512Native<N> = HashInstruction<N, { HashVariant::HashKeccak512Native as u8 }>;
62
63pub type HashKeccak256NativeRaw<N> = HashInstruction<N, { HashVariant::HashKeccak256NativeRaw as u8 }>;
65pub type HashKeccak384NativeRaw<N> = HashInstruction<N, { HashVariant::HashKeccak384NativeRaw as u8 }>;
67pub type HashKeccak512NativeRaw<N> = HashInstruction<N, { HashVariant::HashKeccak512NativeRaw as u8 }>;
69
70pub type HashPED64<N> = HashInstruction<N, { HashVariant::HashPED64 as u8 }>;
72pub type HashPED128<N> = HashInstruction<N, { HashVariant::HashPED128 as u8 }>;
74
75pub type HashPED64Raw<N> = HashInstruction<N, { HashVariant::HashPED64Raw as u8 }>;
77pub type HashPED128Raw<N> = HashInstruction<N, { HashVariant::HashPED128Raw as u8 }>;
79
80pub type HashPSD2<N> = HashInstruction<N, { HashVariant::HashPSD2 as u8 }>;
82pub type HashPSD4<N> = HashInstruction<N, { HashVariant::HashPSD4 as u8 }>;
84pub type HashPSD8<N> = HashInstruction<N, { HashVariant::HashPSD8 as u8 }>;
86
87pub type HashPSD2Raw<N> = HashInstruction<N, { HashVariant::HashPSD2Raw as u8 }>;
89pub type HashPSD4Raw<N> = HashInstruction<N, { HashVariant::HashPSD4Raw as u8 }>;
91pub type HashPSD8Raw<N> = HashInstruction<N, { HashVariant::HashPSD8Raw as u8 }>;
93
94pub type HashSha3_256<N> = HashInstruction<N, { HashVariant::HashSha3_256 as u8 }>;
96pub type HashSha3_384<N> = HashInstruction<N, { HashVariant::HashSha3_384 as u8 }>;
98pub type HashSha3_512<N> = HashInstruction<N, { HashVariant::HashSha3_512 as u8 }>;
100
101pub type HashSha3_256Raw<N> = HashInstruction<N, { HashVariant::HashSha3_256Raw as u8 }>;
103pub type HashSha3_384Raw<N> = HashInstruction<N, { HashVariant::HashSha3_384Raw as u8 }>;
105pub type HashSha3_512Raw<N> = HashInstruction<N, { HashVariant::HashSha3_512Raw as u8 }>;
107
108pub type HashSha3_256Native<N> = HashInstruction<N, { HashVariant::HashSha3_256Native as u8 }>;
110pub type HashSha3_384Native<N> = HashInstruction<N, { HashVariant::HashSha3_384Native as u8 }>;
112pub type HashSha3_512Native<N> = HashInstruction<N, { HashVariant::HashSha3_512Native as u8 }>;
114
115pub type HashSha3_256NativeRaw<N> = HashInstruction<N, { HashVariant::HashSha3_256NativeRaw as u8 }>;
117pub type HashSha3_384NativeRaw<N> = HashInstruction<N, { HashVariant::HashSha3_384NativeRaw as u8 }>;
119pub type HashSha3_512NativeRaw<N> = HashInstruction<N, { HashVariant::HashSha3_512NativeRaw as u8 }>;
121
122pub type HashManyPSD2<N> = HashInstruction<N, { HashVariant::HashManyPSD2 as u8 }>;
124pub type HashManyPSD4<N> = HashInstruction<N, { HashVariant::HashManyPSD4 as u8 }>;
126pub type HashManyPSD8<N> = HashInstruction<N, { HashVariant::HashManyPSD8 as u8 }>;
128
129#[derive(Debug, Copy, Clone, Eq, PartialEq, Sequence)]
131pub enum HashVariant {
132 HashBHP256,
133 HashBHP512,
134 HashBHP768,
135 HashBHP1024,
136 HashKeccak256,
137 HashKeccak384,
138 HashKeccak512,
139 HashPED64,
140 HashPED128,
141 HashPSD2,
142 HashPSD4,
143 HashPSD8,
144 HashSha3_256,
145 HashSha3_384,
146 HashSha3_512,
147 HashManyPSD2,
148 HashManyPSD4,
149 HashManyPSD8,
150 HashBHP256Raw,
152 HashBHP512Raw,
153 HashBHP768Raw,
154 HashBHP1024Raw,
155 HashKeccak256Raw,
156 HashKeccak384Raw,
157 HashKeccak512Raw,
158 HashPED64Raw,
159 HashPED128Raw,
160 HashPSD2Raw,
161 HashPSD4Raw,
162 HashPSD8Raw,
163 HashSha3_256Raw,
164 HashSha3_384Raw,
165 HashSha3_512Raw,
166 HashKeccak256Native,
168 HashKeccak256NativeRaw,
169 HashKeccak384Native,
170 HashKeccak384NativeRaw,
171 HashKeccak512Native,
172 HashKeccak512NativeRaw,
173 HashSha3_256Native,
174 HashSha3_256NativeRaw,
175 HashSha3_384Native,
176 HashSha3_384NativeRaw,
177 HashSha3_512Native,
178 HashSha3_512NativeRaw,
179}
180
181impl HashVariant {
182 pub const fn new(variant: u8) -> Self {
184 match variant {
185 0 => Self::HashBHP256,
186 1 => Self::HashBHP512,
187 2 => Self::HashBHP768,
188 3 => Self::HashBHP1024,
189 4 => Self::HashKeccak256,
190 5 => Self::HashKeccak384,
191 6 => Self::HashKeccak512,
192 7 => Self::HashPED64,
193 8 => Self::HashPED128,
194 9 => Self::HashPSD2,
195 10 => Self::HashPSD4,
196 11 => Self::HashPSD8,
197 12 => Self::HashSha3_256,
198 13 => Self::HashSha3_384,
199 14 => Self::HashSha3_512,
200 15 => Self::HashManyPSD2,
201 16 => Self::HashManyPSD4,
202 17 => Self::HashManyPSD8,
203 18 => Self::HashBHP256Raw,
205 19 => Self::HashBHP512Raw,
206 20 => Self::HashBHP768Raw,
207 21 => Self::HashBHP1024Raw,
208 22 => Self::HashKeccak256Raw,
209 23 => Self::HashKeccak384Raw,
210 24 => Self::HashKeccak512Raw,
211 25 => Self::HashPED64Raw,
212 26 => Self::HashPED128Raw,
213 27 => Self::HashPSD2Raw,
214 28 => Self::HashPSD4Raw,
215 29 => Self::HashPSD8Raw,
216 30 => Self::HashSha3_256Raw,
217 31 => Self::HashSha3_384Raw,
218 32 => Self::HashSha3_512Raw,
219 33 => Self::HashKeccak256Native,
221 34 => Self::HashKeccak256NativeRaw,
222 35 => Self::HashKeccak384Native,
223 36 => Self::HashKeccak384NativeRaw,
224 37 => Self::HashKeccak512Native,
225 38 => Self::HashKeccak512NativeRaw,
226 39 => Self::HashSha3_256Native,
227 40 => Self::HashSha3_256NativeRaw,
228 41 => Self::HashSha3_384Native,
229 42 => Self::HashSha3_384NativeRaw,
230 43 => Self::HashSha3_512Native,
231 44 => Self::HashSha3_512NativeRaw,
232 _ => panic!("Invalid 'hash' instruction opcode"),
233 }
234 }
235
236 pub const fn opcode(&self) -> &'static str {
238 match self {
239 Self::HashBHP256 => "hash.bhp256",
240 Self::HashBHP512 => "hash.bhp512",
241 Self::HashBHP768 => "hash.bhp768",
242 Self::HashBHP1024 => "hash.bhp1024",
243 Self::HashKeccak256 => "hash.keccak256",
244 Self::HashKeccak384 => "hash.keccak384",
245 Self::HashKeccak512 => "hash.keccak512",
246 Self::HashPED64 => "hash.ped64",
247 Self::HashPED128 => "hash.ped128",
248 Self::HashPSD2 => "hash.psd2",
249 Self::HashPSD4 => "hash.psd4",
250 Self::HashPSD8 => "hash.psd8",
251 Self::HashSha3_256 => "hash.sha3_256",
252 Self::HashSha3_384 => "hash.sha3_384",
253 Self::HashSha3_512 => "hash.sha3_512",
254 Self::HashManyPSD2 => "hash_many.psd2",
255 Self::HashManyPSD4 => "hash_many.psd4",
256 Self::HashManyPSD8 => "hash_many.psd8",
257 Self::HashBHP256Raw => "hash.bhp256.raw",
259 Self::HashBHP512Raw => "hash.bhp512.raw",
260 Self::HashBHP768Raw => "hash.bhp768.raw",
261 Self::HashBHP1024Raw => "hash.bhp1024.raw",
262 Self::HashKeccak256Raw => "hash.keccak256.raw",
263 Self::HashKeccak384Raw => "hash.keccak384.raw",
264 Self::HashKeccak512Raw => "hash.keccak512.raw",
265 Self::HashPED64Raw => "hash.ped64.raw",
266 Self::HashPED128Raw => "hash.ped128.raw",
267 Self::HashPSD2Raw => "hash.psd2.raw",
268 Self::HashPSD4Raw => "hash.psd4.raw",
269 Self::HashPSD8Raw => "hash.psd8.raw",
270 Self::HashSha3_256Raw => "hash.sha3_256.raw",
271 Self::HashSha3_384Raw => "hash.sha3_384.raw",
272 Self::HashSha3_512Raw => "hash.sha3_512.raw",
273 Self::HashKeccak256Native => "hash.keccak256.native",
275 Self::HashKeccak256NativeRaw => "hash.keccak256.native.raw",
276 Self::HashKeccak384Native => "hash.keccak384.native",
277 Self::HashKeccak384NativeRaw => "hash.keccak384.native.raw",
278 Self::HashKeccak512Native => "hash.keccak512.native",
279 Self::HashKeccak512NativeRaw => "hash.keccak512.native.raw",
280 Self::HashSha3_256Native => "hash.sha3_256.native",
281 Self::HashSha3_256NativeRaw => "hash.sha3_256.native.raw",
282 Self::HashSha3_384Native => "hash.sha3_384.native",
283 Self::HashSha3_384NativeRaw => "hash.sha3_384.native.raw",
284 Self::HashSha3_512Native => "hash.sha3_512.native",
285 Self::HashSha3_512NativeRaw => "hash.sha3_512.native.raw",
286 }
287 }
288
289 pub const fn requires_byte_alignment(&self) -> bool {
291 match self {
292 Self::HashBHP256
293 | Self::HashBHP512
294 | Self::HashBHP768
295 | Self::HashBHP1024
296 | Self::HashKeccak256
297 | Self::HashKeccak384
298 | Self::HashKeccak512
299 | Self::HashPED64
300 | Self::HashPED128
301 | Self::HashPSD2
302 | Self::HashPSD4
303 | Self::HashPSD8
304 | Self::HashSha3_256
305 | Self::HashSha3_384
306 | Self::HashSha3_512
307 | Self::HashManyPSD2
308 | Self::HashManyPSD4
309 | Self::HashManyPSD8 => false,
310 Self::HashBHP256Raw | Self::HashBHP512Raw | Self::HashBHP768Raw | Self::HashBHP1024Raw => false,
312 Self::HashKeccak256Raw | Self::HashKeccak384Raw | Self::HashKeccak512Raw => true,
313 Self::HashPED64Raw | Self::HashPED128Raw | Self::HashPSD2Raw | Self::HashPSD4Raw | Self::HashPSD8Raw => {
314 false
315 }
316 Self::HashSha3_256Raw | Self::HashSha3_384Raw | Self::HashSha3_512Raw => true,
317 Self::HashKeccak256Native
319 | Self::HashKeccak256NativeRaw
320 | Self::HashKeccak384Native
321 | Self::HashKeccak384NativeRaw
322 | Self::HashKeccak512Native
323 | Self::HashKeccak512NativeRaw
324 | Self::HashSha3_256Native
325 | Self::HashSha3_256NativeRaw
326 | Self::HashSha3_384Native
327 | Self::HashSha3_384NativeRaw
328 | Self::HashSha3_512Native
329 | Self::HashSha3_512NativeRaw => true,
330 }
331 }
332
333 pub const fn is_raw(&self) -> bool {
335 match self {
336 Self::HashBHP256
337 | Self::HashBHP512
338 | Self::HashBHP768
339 | Self::HashBHP1024
340 | Self::HashKeccak256
341 | Self::HashKeccak384
342 | Self::HashKeccak512
343 | Self::HashPED64
344 | Self::HashPED128
345 | Self::HashPSD2
346 | Self::HashPSD4
347 | Self::HashPSD8
348 | Self::HashSha3_256
349 | Self::HashSha3_384
350 | Self::HashSha3_512
351 | Self::HashManyPSD2
352 | Self::HashManyPSD4
353 | Self::HashManyPSD8 => false,
354 Self::HashBHP256Raw
356 | Self::HashBHP512Raw
357 | Self::HashBHP768Raw
358 | Self::HashBHP1024Raw
359 | Self::HashKeccak256Raw
360 | Self::HashKeccak384Raw
361 | Self::HashKeccak512Raw
362 | Self::HashPED64Raw
363 | Self::HashPED128Raw
364 | Self::HashPSD2Raw
365 | Self::HashPSD4Raw
366 | Self::HashPSD8Raw
367 | Self::HashSha3_256Raw
368 | Self::HashSha3_384Raw
369 | Self::HashSha3_512Raw => true,
370 Self::HashKeccak256Native
372 | Self::HashKeccak256NativeRaw
373 | Self::HashKeccak384Native
374 | Self::HashKeccak384NativeRaw
375 | Self::HashKeccak512Native
376 | Self::HashKeccak512NativeRaw
377 | Self::HashSha3_256Native
378 | Self::HashSha3_256NativeRaw
379 | Self::HashSha3_384Native
380 | Self::HashSha3_384NativeRaw
381 | Self::HashSha3_512Native
382 | Self::HashSha3_512NativeRaw => true,
383 }
384 }
385
386 pub const fn expected_num_operands(&self) -> usize {
388 match self {
389 Self::HashManyPSD2 | Self::HashManyPSD4 | Self::HashManyPSD8 => 2,
390 _ => 1,
391 }
392 }
393}
394
395fn check_number_of_operands(variant: u8, opcode: Opcode, num_operands: usize) -> Result<()> {
398 let variant = HashVariant::new(variant);
399 let expected = variant.expected_num_operands();
400 if expected != num_operands {
401 bail!("Instruction '{opcode}' expects {expected} operands, found {num_operands} operands")
402 }
403 Ok(())
404}
405
406fn is_valid_destination_type<N: Network>(variant: u8, destination_type: &PlaintextType<N>) -> bool {
408 match variant {
409 0..=32 => !matches!(
410 destination_type,
411 PlaintextType::Literal(LiteralType::Boolean)
412 | PlaintextType::Literal(LiteralType::String)
413 | PlaintextType::Struct(..)
414 | PlaintextType::ExternalStruct(..)
415 | PlaintextType::Array(..)
416 ),
417 33..=44 => matches!(destination_type, PlaintextType::Array(array_type) if array_type.is_bit_array()),
418 _ => panic!("Invalid 'hash' instruction opcode"),
419 }
420}
421
422#[derive(Clone, PartialEq, Eq, Hash)]
424pub struct HashInstruction<N: Network, const VARIANT: u8> {
425 operands: Vec<Operand<N>>,
427 destination: Register<N>,
429 destination_type: PlaintextType<N>,
431}
432
433impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
434 pub fn new(
436 operands: Vec<Operand<N>>,
437 destination: Register<N>,
438 destination_type: PlaintextType<N>,
439 ) -> Result<Self> {
440 check_number_of_operands(VARIANT, Self::opcode(), operands.len())?;
442 if !is_valid_destination_type(VARIANT, &destination_type) {
444 bail!("Invalid destination type for 'hash' instruction")
445 }
446 Ok(Self { operands, destination, destination_type })
448 }
449
450 pub const fn opcode() -> Opcode {
452 Opcode::Hash(HashVariant::new(VARIANT).opcode())
453 }
454
455 pub fn operands(&self) -> &[Operand<N>] {
457 debug_assert!(
459 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).is_ok(),
460 "Invalid number of operands for '{}'",
461 Self::opcode()
462 );
463 &self.operands
465 }
466
467 #[inline]
469 pub fn destinations(&self) -> Vec<Register<N>> {
470 vec![self.destination.clone()]
471 }
472
473 #[inline]
475 pub const fn destination_type(&self) -> &PlaintextType<N> {
476 &self.destination_type
477 }
478
479 #[inline]
481 pub fn contains_external_struct(&self) -> bool {
482 self.destination_type.contains_external_struct()
483 }
484}
485
486#[rustfmt::skip]
492macro_rules! do_hash {
493 ($N: ident, $variant: expr, $destination_type: expr, $input: expr, $pt: ty, $lt: ty, $q: expr) => {{
494 let bits = || $input.to_bits_le();
495 let bits_raw = || $input.to_bits_raw_le();
496
497 let fields = || $q($input.to_fields());
498 let fields_raw = || $q($input.to_fields_raw());
499
500 let check_multiple_of_8 = |bits: Vec<_>| -> Result<Vec<_>> {
501 ensure!(bits.len() % 8 == 0, "The opcode '{}' expects input whose size in bits is a multiple of 8.", $variant.opcode());
502 Ok(bits)
503 };
504
505 match ($variant, $destination_type) {
506 (HashVariant::HashBHP256, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&bits()))?).cast_lossy(*literal_type)?),
507 (HashVariant::HashBHP512, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&bits()))?).cast_lossy(*literal_type)?),
508 (HashVariant::HashBHP768, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp768(&bits()))?).cast_lossy(*literal_type)?),
509 (HashVariant::HashBHP1024, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp1024(&bits()))?).cast_lossy(*literal_type)?),
510 (HashVariant::HashKeccak256, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&$q($N::hash_keccak256(&bits()))?))?).cast_lossy(*literal_type)?),
511 (HashVariant::HashKeccak384, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&$q($N::hash_keccak384(&bits()))?))?).cast_lossy(*literal_type)?),
512 (HashVariant::HashKeccak512, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&$q($N::hash_keccak512(&bits()))?))?).cast_lossy(*literal_type)?),
513 (HashVariant::HashPED64, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped64(&bits()))?).cast_lossy(*literal_type)?),
514 (HashVariant::HashPED128, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped128(&bits()))?).cast_lossy(*literal_type)?),
515 (HashVariant::HashPSD2, PlaintextType::Literal(literal_type @ LiteralType::Address) | PlaintextType::Literal(literal_type @ LiteralType::Group)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_psd2(&fields()?))?).cast_lossy(*literal_type)?),
516 (HashVariant::HashPSD2, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd2(&fields()?))?).cast_lossy(*literal_type)?),
517 (HashVariant::HashPSD4, PlaintextType::Literal(literal_type @ LiteralType::Address) | PlaintextType::Literal(literal_type @ LiteralType::Group)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_psd4(&fields()?))?).cast_lossy(*literal_type)?),
518 (HashVariant::HashPSD4, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd4(&fields()?))?).cast_lossy(*literal_type)?),
519 (HashVariant::HashPSD8, PlaintextType::Literal(literal_type @ LiteralType::Address) | PlaintextType::Literal(literal_type @ LiteralType::Group)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_psd8(&fields()?))?).cast_lossy(*literal_type)?),
520 (HashVariant::HashPSD8, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd8(&fields()?))?).cast_lossy(*literal_type)?),
521 (HashVariant::HashSha3_256, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&$q($N::hash_sha3_256(&bits()))?))?).cast_lossy(*literal_type)?),
522 (HashVariant::HashSha3_384, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&$q($N::hash_sha3_384(&bits()))?))?).cast_lossy(*literal_type)?),
523 (HashVariant::HashSha3_512, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&$q($N::hash_sha3_512(&bits()))?))?).cast_lossy(*literal_type)?),
524 (HashVariant::HashManyPSD2, PlaintextType::Literal(_)) => bail!("'hash_many.psd2' is not yet implemented"),
525 (HashVariant::HashManyPSD4, PlaintextType::Literal(_)) => bail!("'hash_many.psd4' is not yet implemented"),
526 (HashVariant::HashManyPSD8, PlaintextType::Literal(_)) => bail!("'hash_many.psd8' is not yet implemented"),
527
528 (HashVariant::HashBHP256Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&bits_raw()))?).cast_lossy(*literal_type)?),
530 (HashVariant::HashBHP512Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&bits_raw()))?).cast_lossy(*literal_type)?),
531 (HashVariant::HashBHP768Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp768(&bits_raw()))?).cast_lossy(*literal_type)?),
532 (HashVariant::HashBHP1024Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp1024(&bits_raw()))?).cast_lossy(*literal_type)?),
533 (HashVariant::HashKeccak256Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&$q($N::hash_keccak256(&check_multiple_of_8(bits_raw())?))?))?).cast_lossy(*literal_type)?),
534 (HashVariant::HashKeccak384Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&$q($N::hash_keccak384(&check_multiple_of_8(bits_raw())?))?))?).cast_lossy(*literal_type)?),
535 (HashVariant::HashKeccak512Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&$q($N::hash_keccak512(&check_multiple_of_8(bits_raw())?))?))?).cast_lossy(*literal_type)?),
536 (HashVariant::HashPED64Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped64(&bits_raw()))?).cast_lossy(*literal_type)?),
537 (HashVariant::HashPED128Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped128(&bits_raw()))?).cast_lossy(*literal_type)?),
538 (HashVariant::HashPSD2Raw, PlaintextType::Literal(literal_type @ LiteralType::Address) | PlaintextType::Literal(literal_type @ LiteralType::Group)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_psd2(&fields_raw()?))?).cast_lossy(*literal_type)?),
539 (HashVariant::HashPSD2Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd2(&fields_raw()?))?).cast_lossy(*literal_type)?),
540 (HashVariant::HashPSD4Raw, PlaintextType::Literal(literal_type @ LiteralType::Address) | PlaintextType::Literal(literal_type @ LiteralType::Group)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_psd4(&fields_raw()?))?).cast_lossy(*literal_type)?),
541 (HashVariant::HashPSD4Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd4(&fields_raw()?))?).cast_lossy(*literal_type)?),
542 (HashVariant::HashPSD8Raw, PlaintextType::Literal(literal_type @ LiteralType::Address) | PlaintextType::Literal(literal_type @ LiteralType::Group)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_psd8(&fields_raw()?))?).cast_lossy(*literal_type)?),
543 (HashVariant::HashPSD8Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd8(&fields_raw()?))?).cast_lossy(*literal_type)?),
544 (HashVariant::HashSha3_256Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&$q($N::hash_sha3_256(&check_multiple_of_8(bits_raw())?))?))?).cast_lossy(*literal_type)?),
545 (HashVariant::HashSha3_384Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&$q($N::hash_sha3_384(&check_multiple_of_8(bits_raw())?))?))?).cast_lossy(*literal_type)?),
546 (HashVariant::HashSha3_512Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&$q($N::hash_sha3_512(&check_multiple_of_8(bits_raw())?))?))?).cast_lossy(*literal_type)?),
547
548 (HashVariant::HashKeccak256Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak256(&bits()))?, **array_type.length())?,
550 (HashVariant::HashKeccak256NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak256(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
551 (HashVariant::HashKeccak384Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak384(&bits()))?, **array_type.length())?,
552 (HashVariant::HashKeccak384NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak384(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
553 (HashVariant::HashKeccak512Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak512(&bits()))?, **array_type.length())?,
554 (HashVariant::HashKeccak512NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak512(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
555 (HashVariant::HashSha3_256Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_256(&bits()))?, **array_type.length())?,
556 (HashVariant::HashSha3_256NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_256(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
557 (HashVariant::HashSha3_384Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_384(&bits()))?, **array_type.length())?,
558 (HashVariant::HashSha3_384NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_384(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
559 (HashVariant::HashSha3_512Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_512(&bits()))?, **array_type.length())?,
560 (HashVariant::HashSha3_512NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_512(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
561 (_, destination_type) => bail!("Invalid destination type '{destination_type}' for 'hash' variant: {}", $variant.opcode()),
562 }
563 }};
564}
565
566pub fn evaluate_hash<N: Network>(
571 variant: HashVariant,
572 input: &Value<N>,
573 destination_type: &PlaintextType<N>,
574) -> Result<Plaintext<N>> {
575 evaluate_hash_internal(variant, input, destination_type)
576}
577
578fn evaluate_hash_internal<N: Network>(
579 variant: HashVariant,
580 input: &Value<N>,
581 destination_type: &PlaintextType<N>,
582) -> Result<Plaintext<N>> {
583 Ok(do_hash!(N, variant, destination_type, input, Plaintext::<N>, Literal::<N>, |x| x))
584}
585
586impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
587 pub fn evaluate(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
589 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
591 ensure!(
593 is_valid_destination_type(VARIANT, &self.destination_type),
594 "Invalid destination type in 'hash' instruction"
595 );
596
597 let input = registers.load(stack, &self.operands[0])?;
599
600 let output = evaluate_hash_internal(HashVariant::new(VARIANT), &input, &self.destination_type)?;
602
603 registers.store(stack, &self.destination, Value::Plaintext(output))
605 }
606
607 pub fn execute<A: circuit::Aleo<Network = N>>(
609 &self,
610 stack: &impl StackTrait<N>,
611 registers: &mut impl RegistersCircuit<N, A>,
612 ) -> Result<()> {
613 use circuit::traits::{ToBits, ToBitsRaw, ToFields, ToFieldsRaw};
614
615 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
617 ensure!(
619 is_valid_destination_type(VARIANT, &self.destination_type),
620 "Invalid destination type in 'hash' instruction"
621 );
622
623 let input = registers.load_circuit(stack, &self.operands[0])?;
625
626 let output = do_hash!(
628 A,
629 HashVariant::new(VARIANT),
630 &self.destination_type,
631 input,
632 circuit::Plaintext::<A>,
633 circuit::Literal::<A>,
634 Result::<_>::Ok
635 );
636
637 registers.store_circuit(stack, &self.destination, circuit::Value::Plaintext(output))
639 }
640
641 #[inline]
643 pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
644 self.evaluate(stack, registers)
645 }
646
647 pub fn output_types(
649 &self,
650 stack: &impl StackTrait<N>,
651 input_types: &[RegisterType<N>],
652 ) -> Result<Vec<RegisterType<N>>> {
653 check_number_of_operands(VARIANT, Self::opcode(), input_types.len())?;
655 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
657 ensure!(
659 is_valid_destination_type(VARIANT, &self.destination_type),
660 "Invalid destination type in 'hash' instruction"
661 );
662
663 let variant = HashVariant::new(VARIANT);
665
666 if variant.requires_byte_alignment() {
668 ensure!(
670 variant.expected_num_operands() == 1,
671 "Expected one operand for '{}', found '{}'",
672 variant.opcode(),
673 variant.expected_num_operands()
674 );
675
676 let get_struct = |identifier: &Identifier<N>| stack.program().get_struct(identifier).cloned();
678
679 let get_external_struct = |locator: &Locator<N>| {
681 stack.get_external_stack(locator.program_id())?.program().get_struct(locator.resource()).cloned()
682 };
683
684 let get_record = |identifier: &Identifier<N>| stack.program().get_record(identifier).cloned();
686
687 let get_external_record = |locator: &Locator<N>| {
689 stack.get_external_stack(locator.program_id())?.program().get_record(locator.resource()).cloned()
690 };
691
692 let get_future = |locator: &Locator<N>| {
694 Ok(match stack.program_id() == locator.program_id() {
695 true => stack
696 .program()
697 .get_function_ref(locator.resource())?
698 .finalize_logic()
699 .ok_or_else(|| anyhow!("'{locator}' does not have a finalize scope"))?
700 .input_types(),
701 false => stack
702 .get_external_stack(locator.program_id())?
703 .program()
704 .get_function_ref(locator.resource())?
705 .finalize_logic()
706 .ok_or_else(|| anyhow!("Failed to find function '{locator}'"))?
707 .input_types(),
708 })
709 };
710
711 let size_in_bits = match variant.is_raw() {
713 false => input_types[0].size_in_bits(
714 &get_struct,
715 &get_external_struct,
716 &get_record,
717 &get_external_record,
718 &get_future,
719 )?,
720 true => input_types[0].size_in_bits_raw(
721 &get_struct,
722 &get_external_struct,
723 &get_record,
724 &get_external_record,
725 &get_future,
726 )?,
727 };
728 ensure!(
730 size_in_bits % 8 == 0,
731 "Expected a multiple of 8 bits for '{}', found '{size_in_bits}'",
732 variant.opcode()
733 );
734 }
735
736 match variant {
739 HashVariant::HashManyPSD2 | HashVariant::HashManyPSD4 | HashVariant::HashManyPSD8 => {
740 bail!("'hash_many' is not yet implemented")
741 }
742 _ => Ok(vec![RegisterType::Plaintext(self.destination_type.clone())]),
743 }
744 }
745}
746
747impl<N: Network, const VARIANT: u8> Parser for HashInstruction<N, VARIANT> {
748 fn parse(string: &str) -> ParserResult<Self> {
750 fn parse_operands<N: Network>(string: &str, num_operands: usize) -> ParserResult<Vec<Operand<N>>> {
752 let mut operands = Vec::with_capacity(num_operands);
753 let mut string = string;
754
755 for _ in 0..num_operands {
756 let (next_string, _) = Sanitizer::parse_whitespaces(string)?;
758 let (next_string, operand) = Operand::parse(next_string)?;
760 string = next_string;
762 operands.push(operand);
764 }
765
766 Ok((string, operands))
767 }
768
769 let (string, _) = tag(*Self::opcode())(string)?;
771 let (string, operands) = parse_operands(string, HashVariant::new(VARIANT).expected_num_operands())?;
773 let (string, _) = Sanitizer::parse_whitespaces(string)?;
775 let (string, _) = tag("into")(string)?;
777 let (string, _) = Sanitizer::parse_whitespaces(string)?;
779 let (string, destination) = Register::parse(string)?;
781 let (string, _) = Sanitizer::parse_whitespaces(string)?;
783 let (string, _) = tag("as")(string)?;
785 let (string, _) = Sanitizer::parse_whitespaces(string)?;
787 let (string, destination_type) = PlaintextType::parse(string)?;
789 match destination_type {
791 PlaintextType::Literal(LiteralType::Boolean) | PlaintextType::Literal(LiteralType::String) => {
792 map_res(fail, |_: ParserResult<Self>| {
793 Err(error(format!("Failed to parse 'hash': '{destination_type}' is invalid")))
794 })(string)
795 }
796 _ => Ok((string, Self { operands, destination, destination_type })),
797 }
798 }
799}
800
801impl<N: Network, const VARIANT: u8> FromStr for HashInstruction<N, VARIANT> {
802 type Err = Error;
803
804 fn from_str(string: &str) -> Result<Self> {
806 match Self::parse(string) {
807 Ok((remainder, object)) => {
808 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
810 Ok(object)
812 }
813 Err(error) => bail!("Failed to parse string. {error}"),
814 }
815 }
816}
817
818impl<N: Network, const VARIANT: u8> Debug for HashInstruction<N, VARIANT> {
819 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
821 Display::fmt(self, f)
822 }
823}
824
825impl<N: Network, const VARIANT: u8> Display for HashInstruction<N, VARIANT> {
826 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
828 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|_| fmt::Error)?;
830 write!(f, "{} ", Self::opcode())?;
832 self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
833 write!(f, "into {} as {}", self.destination, self.destination_type)
834 }
835}
836
837impl<N: Network, const VARIANT: u8> FromBytes for HashInstruction<N, VARIANT> {
838 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
840 let num_operands = HashVariant::new(VARIANT).expected_num_operands();
842 let operands = (0..num_operands).map(|_| Operand::read_le(&mut reader)).collect::<Result<_, _>>()?;
844 let destination = Register::read_le(&mut reader)?;
846 let destination_type = PlaintextType::read_le(&mut reader)?;
848 Ok(Self { operands, destination, destination_type })
850 }
851}
852
853impl<N: Network, const VARIANT: u8> ToBytes for HashInstruction<N, VARIANT> {
854 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
856 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|e| error(format!("{e}")))?;
858 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
860 self.destination.write_le(&mut writer)?;
862 self.destination_type.write_le(&mut writer)
864 }
865}
866
867#[cfg(test)]
868mod tests {
869 use super::*;
870 use console::{network::MainnetV0, program::ArrayType, types::U32};
871
872 type CurrentNetwork = MainnetV0;
873
874 fn sample_valid_destination_types<N: Network, R: CryptoRng + Rng>(
876 variant: u8,
877 rng: &mut R,
878 ) -> Vec<PlaintextType<N>> {
879 match variant {
880 0..=32 => vec![
881 PlaintextType::Literal(LiteralType::Address),
882 PlaintextType::Literal(LiteralType::Field),
883 PlaintextType::Literal(LiteralType::Group),
884 PlaintextType::Literal(LiteralType::I8),
885 PlaintextType::Literal(LiteralType::I16),
886 PlaintextType::Literal(LiteralType::I32),
887 PlaintextType::Literal(LiteralType::I64),
888 PlaintextType::Literal(LiteralType::I128),
889 PlaintextType::Literal(LiteralType::U8),
890 PlaintextType::Literal(LiteralType::U16),
891 PlaintextType::Literal(LiteralType::U32),
892 PlaintextType::Literal(LiteralType::U64),
893 PlaintextType::Literal(LiteralType::U128),
894 PlaintextType::Literal(LiteralType::Scalar),
895 ],
896 33..=44 => (0..10)
897 .map(|_| {
898 PlaintextType::Array(
899 ArrayType::new(PlaintextType::Literal(LiteralType::Boolean), vec![U32::new(
900 u32::try_from(rng.gen_range(1..=CurrentNetwork::MAX_ARRAY_ELEMENTS)).unwrap(),
901 )])
902 .unwrap(),
903 )
904 })
905 .collect(),
906 _ => panic!("Invalid 'hash' instruction opcode"),
907 }
908 }
909
910 fn run_test<N: Network, const VARIANT: u8>() {
912 let rng = &mut TestRng::default();
914
915 let opcode = HashInstruction::<N, VARIANT>::opcode();
917
918 for destination_type in sample_valid_destination_types(VARIANT, rng) {
919 let instruction = format!("{opcode} r0 into r1 as {destination_type}");
920 println!("Testing instruction: '{instruction}'");
921
922 let (string, hash) = HashInstruction::<CurrentNetwork, VARIANT>::parse(&instruction).unwrap();
923 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
924 assert_eq!(hash.operands.len(), 1, "The number of operands is incorrect");
925 assert_eq!(hash.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
926 assert_eq!(hash.destination, Register::Locator(1), "The destination register is incorrect");
927 assert_eq!(&hash.destination_type, &destination_type, "The destination type is incorrect");
928 }
929 }
930
931 #[test]
932 fn test_parse() {
933 run_test::<CurrentNetwork, { HashVariant::HashBHP256 as u8 }>();
934 run_test::<CurrentNetwork, { HashVariant::HashBHP512 as u8 }>();
935 run_test::<CurrentNetwork, { HashVariant::HashBHP768 as u8 }>();
936 run_test::<CurrentNetwork, { HashVariant::HashBHP1024 as u8 }>();
937
938 run_test::<CurrentNetwork, { HashVariant::HashKeccak256 as u8 }>();
939 run_test::<CurrentNetwork, { HashVariant::HashKeccak384 as u8 }>();
940 run_test::<CurrentNetwork, { HashVariant::HashKeccak512 as u8 }>();
941
942 run_test::<CurrentNetwork, { HashVariant::HashPED64 as u8 }>();
943 run_test::<CurrentNetwork, { HashVariant::HashPED128 as u8 }>();
944
945 run_test::<CurrentNetwork, { HashVariant::HashPSD2 as u8 }>();
946 run_test::<CurrentNetwork, { HashVariant::HashPSD4 as u8 }>();
947 run_test::<CurrentNetwork, { HashVariant::HashPSD8 as u8 }>();
948
949 run_test::<CurrentNetwork, { HashVariant::HashSha3_256 as u8 }>();
950 run_test::<CurrentNetwork, { HashVariant::HashSha3_384 as u8 }>();
951 run_test::<CurrentNetwork, { HashVariant::HashSha3_512 as u8 }>();
952
953 run_test::<CurrentNetwork, { HashVariant::HashBHP256Raw as u8 }>();
959 run_test::<CurrentNetwork, { HashVariant::HashBHP512Raw as u8 }>();
960 run_test::<CurrentNetwork, { HashVariant::HashBHP768Raw as u8 }>();
961 run_test::<CurrentNetwork, { HashVariant::HashBHP1024Raw as u8 }>();
962
963 run_test::<CurrentNetwork, { HashVariant::HashKeccak256Raw as u8 }>();
964 run_test::<CurrentNetwork, { HashVariant::HashKeccak384Raw as u8 }>();
965 run_test::<CurrentNetwork, { HashVariant::HashKeccak512Raw as u8 }>();
966
967 run_test::<CurrentNetwork, { HashVariant::HashPED64Raw as u8 }>();
968 run_test::<CurrentNetwork, { HashVariant::HashPED128Raw as u8 }>();
969
970 run_test::<CurrentNetwork, { HashVariant::HashPSD2Raw as u8 }>();
971 run_test::<CurrentNetwork, { HashVariant::HashPSD4Raw as u8 }>();
972 run_test::<CurrentNetwork, { HashVariant::HashPSD8Raw as u8 }>();
973
974 run_test::<CurrentNetwork, { HashVariant::HashSha3_256Raw as u8 }>();
975 run_test::<CurrentNetwork, { HashVariant::HashSha3_384Raw as u8 }>();
976 run_test::<CurrentNetwork, { HashVariant::HashSha3_512Raw as u8 }>();
977
978 run_test::<CurrentNetwork, { HashVariant::HashKeccak256Native as u8 }>();
979 run_test::<CurrentNetwork, { HashVariant::HashKeccak384Native as u8 }>();
980 run_test::<CurrentNetwork, { HashVariant::HashKeccak512Native as u8 }>();
981
982 run_test::<CurrentNetwork, { HashVariant::HashSha3_256Native as u8 }>();
983 run_test::<CurrentNetwork, { HashVariant::HashSha3_384Native as u8 }>();
984 run_test::<CurrentNetwork, { HashVariant::HashSha3_512Native as u8 }>();
985
986 run_test::<CurrentNetwork, { HashVariant::HashKeccak256NativeRaw as u8 }>();
987 run_test::<CurrentNetwork, { HashVariant::HashKeccak384NativeRaw as u8 }>();
988 run_test::<CurrentNetwork, { HashVariant::HashKeccak512NativeRaw as u8 }>();
989
990 run_test::<CurrentNetwork, { HashVariant::HashSha3_256NativeRaw as u8 }>();
991 run_test::<CurrentNetwork, { HashVariant::HashSha3_384NativeRaw as u8 }>();
992 run_test::<CurrentNetwork, { HashVariant::HashSha3_512NativeRaw as u8 }>();
993 }
994
995 #[test]
996 fn check_number_of_hash_variants() {
997 assert_eq!(enum_iterator::cardinality::<HashVariant>(), 45);
998 }
999
1000 #[test]
1001 fn check_byte_aligned_variants_all_have_one_opcode() {
1002 for variant in enum_iterator::all::<HashVariant>() {
1003 if variant.requires_byte_alignment() {
1004 assert_eq!(variant.expected_num_operands(), 1)
1005 }
1006 }
1007 }
1008}