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::Literal(LiteralType::Identifier)
414 | PlaintextType::Struct(..)
415 | PlaintextType::ExternalStruct(..)
416 | PlaintextType::Array(..)
417 ),
418 33..=44 => matches!(destination_type, PlaintextType::Array(array_type) if array_type.is_bit_array()),
419 _ => panic!("Invalid 'hash' instruction opcode"),
420 }
421}
422
423#[derive(Clone, PartialEq, Eq, Hash)]
425pub struct HashInstruction<N: Network, const VARIANT: u8> {
426 operands: Vec<Operand<N>>,
428 destination: Register<N>,
430 destination_type: PlaintextType<N>,
432}
433
434impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
435 pub fn new(
437 operands: Vec<Operand<N>>,
438 destination: Register<N>,
439 destination_type: PlaintextType<N>,
440 ) -> Result<Self> {
441 check_number_of_operands(VARIANT, Self::opcode(), operands.len())?;
443 if !is_valid_destination_type(VARIANT, &destination_type) {
445 bail!("Invalid destination type for 'hash' instruction")
446 }
447 Ok(Self { operands, destination, destination_type })
449 }
450
451 pub const fn opcode() -> Opcode {
453 Opcode::Hash(HashVariant::new(VARIANT).opcode())
454 }
455
456 pub fn operands(&self) -> &[Operand<N>] {
458 debug_assert!(
460 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).is_ok(),
461 "Invalid number of operands for '{}'",
462 Self::opcode()
463 );
464 &self.operands
466 }
467
468 #[inline]
470 pub fn destinations(&self) -> Vec<Register<N>> {
471 vec![self.destination.clone()]
472 }
473
474 #[inline]
476 pub const fn destination_type(&self) -> &PlaintextType<N> {
477 &self.destination_type
478 }
479
480 #[inline]
482 pub fn contains_external_struct(&self) -> bool {
483 self.destination_type.contains_external_struct()
484 }
485}
486
487#[rustfmt::skip]
493macro_rules! do_hash {
494 ($N: ident, $variant: expr, $destination_type: expr, $input: expr, $pt: ty, $lt: ty, $q: expr) => {{
495 let bits = || $input.to_bits_le();
496 let bits_raw = || $input.to_bits_raw_le();
497
498 let fields = || $q($input.to_fields());
499 let fields_raw = || $q($input.to_fields_raw());
500
501 let check_multiple_of_8 = |bits: Vec<_>| -> Result<Vec<_>> {
502 ensure!(bits.len() % 8 == 0, "The opcode '{}' expects input whose size in bits is a multiple of 8.", $variant.opcode());
503 Ok(bits)
504 };
505
506 match ($variant, $destination_type) {
507 (HashVariant::HashBHP256, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&bits()))?).cast_lossy(*literal_type)?),
508 (HashVariant::HashBHP512, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&bits()))?).cast_lossy(*literal_type)?),
509 (HashVariant::HashBHP768, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp768(&bits()))?).cast_lossy(*literal_type)?),
510 (HashVariant::HashBHP1024, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp1024(&bits()))?).cast_lossy(*literal_type)?),
511 (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)?),
512 (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)?),
513 (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)?),
514 (HashVariant::HashPED64, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped64(&bits()))?).cast_lossy(*literal_type)?),
515 (HashVariant::HashPED128, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped128(&bits()))?).cast_lossy(*literal_type)?),
516 (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)?),
517 (HashVariant::HashPSD2, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd2(&fields()?))?).cast_lossy(*literal_type)?),
518 (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)?),
519 (HashVariant::HashPSD4, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd4(&fields()?))?).cast_lossy(*literal_type)?),
520 (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)?),
521 (HashVariant::HashPSD8, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd8(&fields()?))?).cast_lossy(*literal_type)?),
522 (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)?),
523 (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)?),
524 (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)?),
525 (HashVariant::HashManyPSD2, PlaintextType::Literal(_)) => bail!("'hash_many.psd2' is not yet implemented"),
526 (HashVariant::HashManyPSD4, PlaintextType::Literal(_)) => bail!("'hash_many.psd4' is not yet implemented"),
527 (HashVariant::HashManyPSD8, PlaintextType::Literal(_)) => bail!("'hash_many.psd8' is not yet implemented"),
528
529 (HashVariant::HashBHP256Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&bits_raw()))?).cast_lossy(*literal_type)?),
531 (HashVariant::HashBHP512Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&bits_raw()))?).cast_lossy(*literal_type)?),
532 (HashVariant::HashBHP768Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp768(&bits_raw()))?).cast_lossy(*literal_type)?),
533 (HashVariant::HashBHP1024Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp1024(&bits_raw()))?).cast_lossy(*literal_type)?),
534 (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)?),
535 (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)?),
536 (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)?),
537 (HashVariant::HashPED64Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped64(&bits_raw()))?).cast_lossy(*literal_type)?),
538 (HashVariant::HashPED128Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped128(&bits_raw()))?).cast_lossy(*literal_type)?),
539 (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)?),
540 (HashVariant::HashPSD2Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd2(&fields_raw()?))?).cast_lossy(*literal_type)?),
541 (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)?),
542 (HashVariant::HashPSD4Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd4(&fields_raw()?))?).cast_lossy(*literal_type)?),
543 (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)?),
544 (HashVariant::HashPSD8Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd8(&fields_raw()?))?).cast_lossy(*literal_type)?),
545 (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)?),
546 (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)?),
547 (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)?),
548
549 (HashVariant::HashKeccak256Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak256(&bits()))?, **array_type.length())?,
551 (HashVariant::HashKeccak256NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak256(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
552 (HashVariant::HashKeccak384Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak384(&bits()))?, **array_type.length())?,
553 (HashVariant::HashKeccak384NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak384(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
554 (HashVariant::HashKeccak512Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak512(&bits()))?, **array_type.length())?,
555 (HashVariant::HashKeccak512NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak512(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
556 (HashVariant::HashSha3_256Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_256(&bits()))?, **array_type.length())?,
557 (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())?,
558 (HashVariant::HashSha3_384Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_384(&bits()))?, **array_type.length())?,
559 (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())?,
560 (HashVariant::HashSha3_512Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_512(&bits()))?, **array_type.length())?,
561 (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())?,
562 (_, destination_type) => bail!("Invalid destination type '{destination_type}' for 'hash' variant: {}", $variant.opcode()),
563 }
564 }};
565}
566
567pub fn evaluate_hash<N: Network>(
572 variant: HashVariant,
573 input: &Value<N>,
574 destination_type: &PlaintextType<N>,
575) -> Result<Plaintext<N>> {
576 evaluate_hash_internal(variant, input, destination_type)
577}
578
579fn evaluate_hash_internal<N: Network>(
580 variant: HashVariant,
581 input: &Value<N>,
582 destination_type: &PlaintextType<N>,
583) -> Result<Plaintext<N>> {
584 Ok(do_hash!(N, variant, destination_type, input, Plaintext::<N>, Literal::<N>, |x| x))
585}
586
587impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
588 pub fn evaluate(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
590 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
592 ensure!(
594 is_valid_destination_type(VARIANT, &self.destination_type),
595 "Invalid destination type in 'hash' instruction"
596 );
597
598 let input = registers.load(stack, &self.operands[0])?;
600
601 let output = evaluate_hash_internal(HashVariant::new(VARIANT), &input, &self.destination_type)?;
603
604 registers.store(stack, &self.destination, Value::Plaintext(output))
606 }
607
608 pub fn execute<A: circuit::Aleo<Network = N>>(
610 &self,
611 stack: &impl StackTrait<N>,
612 registers: &mut impl RegistersCircuit<N, A>,
613 ) -> Result<()> {
614 use circuit::traits::{ToBits, ToBitsRaw, ToFields, ToFieldsRaw};
615
616 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
618 ensure!(
620 is_valid_destination_type(VARIANT, &self.destination_type),
621 "Invalid destination type in 'hash' instruction"
622 );
623
624 let input = registers.load_circuit(stack, &self.operands[0])?;
626
627 let output = do_hash!(
629 A,
630 HashVariant::new(VARIANT),
631 &self.destination_type,
632 input,
633 circuit::Plaintext::<A>,
634 circuit::Literal::<A>,
635 Result::<_>::Ok
636 );
637
638 registers.store_circuit(stack, &self.destination, circuit::Value::Plaintext(output))
640 }
641
642 #[inline]
644 pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
645 self.evaluate(stack, registers)
646 }
647
648 pub fn output_types(
650 &self,
651 stack: &impl StackTrait<N>,
652 input_types: &[RegisterType<N>],
653 ) -> Result<Vec<RegisterType<N>>> {
654 check_number_of_operands(VARIANT, Self::opcode(), input_types.len())?;
656 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
658 ensure!(
660 is_valid_destination_type(VARIANT, &self.destination_type),
661 "Invalid destination type in 'hash' instruction"
662 );
663
664 let variant = HashVariant::new(VARIANT);
666
667 if variant.requires_byte_alignment() {
669 ensure!(
671 variant.expected_num_operands() == 1,
672 "Expected one operand for '{}', found '{}'",
673 variant.opcode(),
674 variant.expected_num_operands()
675 );
676
677 let get_struct = |identifier: &Identifier<N>| stack.program().get_struct(identifier).cloned();
679
680 let get_external_struct = |locator: &Locator<N>| {
682 stack.get_external_stack(locator.program_id())?.program().get_struct(locator.resource()).cloned()
683 };
684
685 let get_record = |identifier: &Identifier<N>| stack.program().get_record(identifier).cloned();
687
688 let get_external_record = |locator: &Locator<N>| {
690 stack.get_external_stack(locator.program_id())?.program().get_record(locator.resource()).cloned()
691 };
692
693 let get_future = |locator: &Locator<N>| {
695 Ok(match stack.program_id() == locator.program_id() {
696 true => stack
697 .program()
698 .get_function_ref(locator.resource())?
699 .finalize_logic()
700 .ok_or_else(|| anyhow!("'{locator}' does not have a finalize scope"))?
701 .input_types(),
702 false => stack
703 .get_external_stack(locator.program_id())?
704 .program()
705 .get_function_ref(locator.resource())?
706 .finalize_logic()
707 .ok_or_else(|| anyhow!("Failed to find function '{locator}'"))?
708 .input_types(),
709 })
710 };
711
712 let size_in_bits = match variant.is_raw() {
714 false => input_types[0].size_in_bits(
715 &get_struct,
716 &get_external_struct,
717 &get_record,
718 &get_external_record,
719 &get_future,
720 )?,
721 true => input_types[0].size_in_bits_raw(
722 &get_struct,
723 &get_external_struct,
724 &get_record,
725 &get_external_record,
726 &get_future,
727 )?,
728 };
729 ensure!(
731 size_in_bits % 8 == 0,
732 "Expected a multiple of 8 bits for '{}', found '{size_in_bits}'",
733 variant.opcode()
734 );
735 }
736
737 match variant {
740 HashVariant::HashManyPSD2 | HashVariant::HashManyPSD4 | HashVariant::HashManyPSD8 => {
741 bail!("'hash_many' is not yet implemented")
742 }
743 _ => Ok(vec![RegisterType::Plaintext(self.destination_type.clone())]),
744 }
745 }
746}
747
748impl<N: Network, const VARIANT: u8> Parser for HashInstruction<N, VARIANT> {
749 fn parse(string: &str) -> ParserResult<Self> {
751 fn parse_operands<N: Network>(string: &str, num_operands: usize) -> ParserResult<Vec<Operand<N>>> {
753 let mut operands = Vec::with_capacity(num_operands);
754 let mut string = string;
755
756 for _ in 0..num_operands {
757 let (next_string, _) = Sanitizer::parse_whitespaces(string)?;
759 let (next_string, operand) = Operand::parse(next_string)?;
761 string = next_string;
763 operands.push(operand);
765 }
766
767 Ok((string, operands))
768 }
769
770 let (string, _) = tag(*Self::opcode())(string)?;
772 let (string, operands) = parse_operands(string, HashVariant::new(VARIANT).expected_num_operands())?;
774 let (string, _) = Sanitizer::parse_whitespaces(string)?;
776 let (string, _) = tag("into")(string)?;
778 let (string, _) = Sanitizer::parse_whitespaces(string)?;
780 let (string, destination) = Register::parse(string)?;
782 let (string, _) = Sanitizer::parse_whitespaces(string)?;
784 let (string, _) = tag("as")(string)?;
786 let (string, _) = Sanitizer::parse_whitespaces(string)?;
788 let (string, destination_type) = PlaintextType::parse(string)?;
790 match destination_type {
792 PlaintextType::Literal(LiteralType::Boolean)
793 | PlaintextType::Literal(LiteralType::String)
794 | PlaintextType::Literal(LiteralType::Identifier) => map_res(fail, |_: ParserResult<Self>| {
795 Err(error(format!("Failed to parse 'hash': '{destination_type}' is invalid")))
796 })(string),
797 _ => Ok((string, Self { operands, destination, destination_type })),
798 }
799 }
800}
801
802impl<N: Network, const VARIANT: u8> FromStr for HashInstruction<N, VARIANT> {
803 type Err = Error;
804
805 fn from_str(string: &str) -> Result<Self> {
807 match Self::parse(string) {
808 Ok((remainder, object)) => {
809 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
811 Ok(object)
813 }
814 Err(error) => bail!("Failed to parse string. {error}"),
815 }
816 }
817}
818
819impl<N: Network, const VARIANT: u8> Debug for HashInstruction<N, VARIANT> {
820 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
822 Display::fmt(self, f)
823 }
824}
825
826impl<N: Network, const VARIANT: u8> Display for HashInstruction<N, VARIANT> {
827 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
829 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|_| fmt::Error)?;
831 write!(f, "{} ", Self::opcode())?;
833 self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
834 write!(f, "into {} as {}", self.destination, self.destination_type)
835 }
836}
837
838impl<N: Network, const VARIANT: u8> FromBytes for HashInstruction<N, VARIANT> {
839 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
841 let num_operands = HashVariant::new(VARIANT).expected_num_operands();
843 let operands = (0..num_operands).map(|_| Operand::read_le(&mut reader)).collect::<Result<_, _>>()?;
845 let destination = Register::read_le(&mut reader)?;
847 let destination_type = PlaintextType::read_le(&mut reader)?;
849 Ok(Self { operands, destination, destination_type })
851 }
852}
853
854impl<N: Network, const VARIANT: u8> ToBytes for HashInstruction<N, VARIANT> {
855 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
857 check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|e| error(format!("{e}")))?;
859 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
861 self.destination.write_le(&mut writer)?;
863 self.destination_type.write_le(&mut writer)
865 }
866}
867
868#[cfg(test)]
869mod tests {
870 use super::*;
871 use console::{network::MainnetV0, program::ArrayType, types::U32};
872
873 type CurrentNetwork = MainnetV0;
874
875 fn sample_valid_destination_types<N: Network, R: CryptoRng + Rng>(
877 variant: u8,
878 rng: &mut R,
879 ) -> Vec<PlaintextType<N>> {
880 match variant {
881 0..=32 => vec![
882 PlaintextType::Literal(LiteralType::Address),
883 PlaintextType::Literal(LiteralType::Field),
884 PlaintextType::Literal(LiteralType::Group),
885 PlaintextType::Literal(LiteralType::I8),
886 PlaintextType::Literal(LiteralType::I16),
887 PlaintextType::Literal(LiteralType::I32),
888 PlaintextType::Literal(LiteralType::I64),
889 PlaintextType::Literal(LiteralType::I128),
890 PlaintextType::Literal(LiteralType::U8),
891 PlaintextType::Literal(LiteralType::U16),
892 PlaintextType::Literal(LiteralType::U32),
893 PlaintextType::Literal(LiteralType::U64),
894 PlaintextType::Literal(LiteralType::U128),
895 PlaintextType::Literal(LiteralType::Scalar),
896 ],
897 33..=44 => (0..10)
898 .map(|_| {
899 PlaintextType::Array(
900 ArrayType::new(PlaintextType::Literal(LiteralType::Boolean), vec![U32::new(
901 u32::try_from(rng.gen_range(1..=CurrentNetwork::LATEST_MAX_ARRAY_ELEMENTS())).unwrap(),
902 )])
903 .unwrap(),
904 )
905 })
906 .collect(),
907 _ => panic!("Invalid 'hash' instruction opcode"),
908 }
909 }
910
911 fn run_test<N: Network, const VARIANT: u8>() {
913 let rng = &mut TestRng::default();
915
916 let opcode = HashInstruction::<N, VARIANT>::opcode();
918
919 for destination_type in sample_valid_destination_types(VARIANT, rng) {
920 let instruction = format!("{opcode} r0 into r1 as {destination_type}");
921 println!("Testing instruction: '{instruction}'");
922
923 let (string, hash) = HashInstruction::<CurrentNetwork, VARIANT>::parse(&instruction).unwrap();
924 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
925 assert_eq!(hash.operands.len(), 1, "The number of operands is incorrect");
926 assert_eq!(hash.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
927 assert_eq!(hash.destination, Register::Locator(1), "The destination register is incorrect");
928 assert_eq!(&hash.destination_type, &destination_type, "The destination type is incorrect");
929 }
930 }
931
932 #[test]
933 fn test_parse() {
934 run_test::<CurrentNetwork, { HashVariant::HashBHP256 as u8 }>();
935 run_test::<CurrentNetwork, { HashVariant::HashBHP512 as u8 }>();
936 run_test::<CurrentNetwork, { HashVariant::HashBHP768 as u8 }>();
937 run_test::<CurrentNetwork, { HashVariant::HashBHP1024 as u8 }>();
938
939 run_test::<CurrentNetwork, { HashVariant::HashKeccak256 as u8 }>();
940 run_test::<CurrentNetwork, { HashVariant::HashKeccak384 as u8 }>();
941 run_test::<CurrentNetwork, { HashVariant::HashKeccak512 as u8 }>();
942
943 run_test::<CurrentNetwork, { HashVariant::HashPED64 as u8 }>();
944 run_test::<CurrentNetwork, { HashVariant::HashPED128 as u8 }>();
945
946 run_test::<CurrentNetwork, { HashVariant::HashPSD2 as u8 }>();
947 run_test::<CurrentNetwork, { HashVariant::HashPSD4 as u8 }>();
948 run_test::<CurrentNetwork, { HashVariant::HashPSD8 as u8 }>();
949
950 run_test::<CurrentNetwork, { HashVariant::HashSha3_256 as u8 }>();
951 run_test::<CurrentNetwork, { HashVariant::HashSha3_384 as u8 }>();
952 run_test::<CurrentNetwork, { HashVariant::HashSha3_512 as u8 }>();
953
954 run_test::<CurrentNetwork, { HashVariant::HashBHP256Raw as u8 }>();
960 run_test::<CurrentNetwork, { HashVariant::HashBHP512Raw as u8 }>();
961 run_test::<CurrentNetwork, { HashVariant::HashBHP768Raw as u8 }>();
962 run_test::<CurrentNetwork, { HashVariant::HashBHP1024Raw as u8 }>();
963
964 run_test::<CurrentNetwork, { HashVariant::HashKeccak256Raw as u8 }>();
965 run_test::<CurrentNetwork, { HashVariant::HashKeccak384Raw as u8 }>();
966 run_test::<CurrentNetwork, { HashVariant::HashKeccak512Raw as u8 }>();
967
968 run_test::<CurrentNetwork, { HashVariant::HashPED64Raw as u8 }>();
969 run_test::<CurrentNetwork, { HashVariant::HashPED128Raw as u8 }>();
970
971 run_test::<CurrentNetwork, { HashVariant::HashPSD2Raw as u8 }>();
972 run_test::<CurrentNetwork, { HashVariant::HashPSD4Raw as u8 }>();
973 run_test::<CurrentNetwork, { HashVariant::HashPSD8Raw as u8 }>();
974
975 run_test::<CurrentNetwork, { HashVariant::HashSha3_256Raw as u8 }>();
976 run_test::<CurrentNetwork, { HashVariant::HashSha3_384Raw as u8 }>();
977 run_test::<CurrentNetwork, { HashVariant::HashSha3_512Raw as u8 }>();
978
979 run_test::<CurrentNetwork, { HashVariant::HashKeccak256Native as u8 }>();
980 run_test::<CurrentNetwork, { HashVariant::HashKeccak384Native as u8 }>();
981 run_test::<CurrentNetwork, { HashVariant::HashKeccak512Native as u8 }>();
982
983 run_test::<CurrentNetwork, { HashVariant::HashSha3_256Native as u8 }>();
984 run_test::<CurrentNetwork, { HashVariant::HashSha3_384Native as u8 }>();
985 run_test::<CurrentNetwork, { HashVariant::HashSha3_512Native as u8 }>();
986
987 run_test::<CurrentNetwork, { HashVariant::HashKeccak256NativeRaw as u8 }>();
988 run_test::<CurrentNetwork, { HashVariant::HashKeccak384NativeRaw as u8 }>();
989 run_test::<CurrentNetwork, { HashVariant::HashKeccak512NativeRaw as u8 }>();
990
991 run_test::<CurrentNetwork, { HashVariant::HashSha3_256NativeRaw as u8 }>();
992 run_test::<CurrentNetwork, { HashVariant::HashSha3_384NativeRaw as u8 }>();
993 run_test::<CurrentNetwork, { HashVariant::HashSha3_512NativeRaw as u8 }>();
994 }
995
996 #[test]
997 fn check_number_of_hash_variants() {
998 assert_eq!(enum_iterator::cardinality::<HashVariant>(), 45);
999 }
1000
1001 #[test]
1002 fn check_byte_aligned_variants_all_have_one_opcode() {
1003 for variant in enum_iterator::all::<HashVariant>() {
1004 if variant.requires_byte_alignment() {
1005 assert_eq!(variant.expected_num_operands(), 1)
1006 }
1007 }
1008 }
1009}