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