use crate::{Opcode, Operand, RegistersCircuit, RegistersTrait, StackTrait};
use console::{
network::prelude::*,
program::{Identifier, Literal, LiteralType, Locator, Plaintext, PlaintextType, Register, RegisterType, Value},
};
use enum_iterator::Sequence;
pub type HashBHP256<N> = HashInstruction<N, { HashVariant::HashBHP256 as u8 }>;
pub type HashBHP512<N> = HashInstruction<N, { HashVariant::HashBHP512 as u8 }>;
pub type HashBHP768<N> = HashInstruction<N, { HashVariant::HashBHP768 as u8 }>;
pub type HashBHP1024<N> = HashInstruction<N, { HashVariant::HashBHP1024 as u8 }>;
pub type HashBHP256Raw<N> = HashInstruction<N, { HashVariant::HashBHP256Raw as u8 }>;
pub type HashBHP512Raw<N> = HashInstruction<N, { HashVariant::HashBHP512Raw as u8 }>;
pub type HashBHP768Raw<N> = HashInstruction<N, { HashVariant::HashBHP768Raw as u8 }>;
pub type HashBHP1024Raw<N> = HashInstruction<N, { HashVariant::HashBHP1024Raw as u8 }>;
pub type HashKeccak256<N> = HashInstruction<N, { HashVariant::HashKeccak256 as u8 }>;
pub type HashKeccak384<N> = HashInstruction<N, { HashVariant::HashKeccak384 as u8 }>;
pub type HashKeccak512<N> = HashInstruction<N, { HashVariant::HashKeccak512 as u8 }>;
pub type HashKeccak256Raw<N> = HashInstruction<N, { HashVariant::HashKeccak256Raw as u8 }>;
pub type HashKeccak384Raw<N> = HashInstruction<N, { HashVariant::HashKeccak384Raw as u8 }>;
pub type HashKeccak512Raw<N> = HashInstruction<N, { HashVariant::HashKeccak512Raw as u8 }>;
pub type HashKeccak256Native<N> = HashInstruction<N, { HashVariant::HashKeccak256Native as u8 }>;
pub type HashKeccak384Native<N> = HashInstruction<N, { HashVariant::HashKeccak384Native as u8 }>;
pub type HashKeccak512Native<N> = HashInstruction<N, { HashVariant::HashKeccak512Native as u8 }>;
pub type HashKeccak256NativeRaw<N> = HashInstruction<N, { HashVariant::HashKeccak256NativeRaw as u8 }>;
pub type HashKeccak384NativeRaw<N> = HashInstruction<N, { HashVariant::HashKeccak384NativeRaw as u8 }>;
pub type HashKeccak512NativeRaw<N> = HashInstruction<N, { HashVariant::HashKeccak512NativeRaw as u8 }>;
pub type HashPED64<N> = HashInstruction<N, { HashVariant::HashPED64 as u8 }>;
pub type HashPED128<N> = HashInstruction<N, { HashVariant::HashPED128 as u8 }>;
pub type HashPED64Raw<N> = HashInstruction<N, { HashVariant::HashPED64Raw as u8 }>;
pub type HashPED128Raw<N> = HashInstruction<N, { HashVariant::HashPED128Raw as u8 }>;
pub type HashPSD2<N> = HashInstruction<N, { HashVariant::HashPSD2 as u8 }>;
pub type HashPSD4<N> = HashInstruction<N, { HashVariant::HashPSD4 as u8 }>;
pub type HashPSD8<N> = HashInstruction<N, { HashVariant::HashPSD8 as u8 }>;
pub type HashPSD2Raw<N> = HashInstruction<N, { HashVariant::HashPSD2Raw as u8 }>;
pub type HashPSD4Raw<N> = HashInstruction<N, { HashVariant::HashPSD4Raw as u8 }>;
pub type HashPSD8Raw<N> = HashInstruction<N, { HashVariant::HashPSD8Raw as u8 }>;
pub type HashSha3_256<N> = HashInstruction<N, { HashVariant::HashSha3_256 as u8 }>;
pub type HashSha3_384<N> = HashInstruction<N, { HashVariant::HashSha3_384 as u8 }>;
pub type HashSha3_512<N> = HashInstruction<N, { HashVariant::HashSha3_512 as u8 }>;
pub type HashSha3_256Raw<N> = HashInstruction<N, { HashVariant::HashSha3_256Raw as u8 }>;
pub type HashSha3_384Raw<N> = HashInstruction<N, { HashVariant::HashSha3_384Raw as u8 }>;
pub type HashSha3_512Raw<N> = HashInstruction<N, { HashVariant::HashSha3_512Raw as u8 }>;
pub type HashSha3_256Native<N> = HashInstruction<N, { HashVariant::HashSha3_256Native as u8 }>;
pub type HashSha3_384Native<N> = HashInstruction<N, { HashVariant::HashSha3_384Native as u8 }>;
pub type HashSha3_512Native<N> = HashInstruction<N, { HashVariant::HashSha3_512Native as u8 }>;
pub type HashSha3_256NativeRaw<N> = HashInstruction<N, { HashVariant::HashSha3_256NativeRaw as u8 }>;
pub type HashSha3_384NativeRaw<N> = HashInstruction<N, { HashVariant::HashSha3_384NativeRaw as u8 }>;
pub type HashSha3_512NativeRaw<N> = HashInstruction<N, { HashVariant::HashSha3_512NativeRaw as u8 }>;
pub type HashManyPSD2<N> = HashInstruction<N, { HashVariant::HashManyPSD2 as u8 }>;
pub type HashManyPSD4<N> = HashInstruction<N, { HashVariant::HashManyPSD4 as u8 }>;
pub type HashManyPSD8<N> = HashInstruction<N, { HashVariant::HashManyPSD8 as u8 }>;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Sequence)]
pub enum HashVariant {
HashBHP256,
HashBHP512,
HashBHP768,
HashBHP1024,
HashKeccak256,
HashKeccak384,
HashKeccak512,
HashPED64,
HashPED128,
HashPSD2,
HashPSD4,
HashPSD8,
HashSha3_256,
HashSha3_384,
HashSha3_512,
HashManyPSD2,
HashManyPSD4,
HashManyPSD8,
HashBHP256Raw,
HashBHP512Raw,
HashBHP768Raw,
HashBHP1024Raw,
HashKeccak256Raw,
HashKeccak384Raw,
HashKeccak512Raw,
HashPED64Raw,
HashPED128Raw,
HashPSD2Raw,
HashPSD4Raw,
HashPSD8Raw,
HashSha3_256Raw,
HashSha3_384Raw,
HashSha3_512Raw,
HashKeccak256Native,
HashKeccak256NativeRaw,
HashKeccak384Native,
HashKeccak384NativeRaw,
HashKeccak512Native,
HashKeccak512NativeRaw,
HashSha3_256Native,
HashSha3_256NativeRaw,
HashSha3_384Native,
HashSha3_384NativeRaw,
HashSha3_512Native,
HashSha3_512NativeRaw,
}
impl HashVariant {
pub const fn new(variant: u8) -> Self {
match variant {
0 => Self::HashBHP256,
1 => Self::HashBHP512,
2 => Self::HashBHP768,
3 => Self::HashBHP1024,
4 => Self::HashKeccak256,
5 => Self::HashKeccak384,
6 => Self::HashKeccak512,
7 => Self::HashPED64,
8 => Self::HashPED128,
9 => Self::HashPSD2,
10 => Self::HashPSD4,
11 => Self::HashPSD8,
12 => Self::HashSha3_256,
13 => Self::HashSha3_384,
14 => Self::HashSha3_512,
15 => Self::HashManyPSD2,
16 => Self::HashManyPSD4,
17 => Self::HashManyPSD8,
18 => Self::HashBHP256Raw,
19 => Self::HashBHP512Raw,
20 => Self::HashBHP768Raw,
21 => Self::HashBHP1024Raw,
22 => Self::HashKeccak256Raw,
23 => Self::HashKeccak384Raw,
24 => Self::HashKeccak512Raw,
25 => Self::HashPED64Raw,
26 => Self::HashPED128Raw,
27 => Self::HashPSD2Raw,
28 => Self::HashPSD4Raw,
29 => Self::HashPSD8Raw,
30 => Self::HashSha3_256Raw,
31 => Self::HashSha3_384Raw,
32 => Self::HashSha3_512Raw,
33 => Self::HashKeccak256Native,
34 => Self::HashKeccak256NativeRaw,
35 => Self::HashKeccak384Native,
36 => Self::HashKeccak384NativeRaw,
37 => Self::HashKeccak512Native,
38 => Self::HashKeccak512NativeRaw,
39 => Self::HashSha3_256Native,
40 => Self::HashSha3_256NativeRaw,
41 => Self::HashSha3_384Native,
42 => Self::HashSha3_384NativeRaw,
43 => Self::HashSha3_512Native,
44 => Self::HashSha3_512NativeRaw,
_ => panic!("Invalid 'hash' instruction opcode"),
}
}
pub const fn opcode(&self) -> &'static str {
match self {
Self::HashBHP256 => "hash.bhp256",
Self::HashBHP512 => "hash.bhp512",
Self::HashBHP768 => "hash.bhp768",
Self::HashBHP1024 => "hash.bhp1024",
Self::HashKeccak256 => "hash.keccak256",
Self::HashKeccak384 => "hash.keccak384",
Self::HashKeccak512 => "hash.keccak512",
Self::HashPED64 => "hash.ped64",
Self::HashPED128 => "hash.ped128",
Self::HashPSD2 => "hash.psd2",
Self::HashPSD4 => "hash.psd4",
Self::HashPSD8 => "hash.psd8",
Self::HashSha3_256 => "hash.sha3_256",
Self::HashSha3_384 => "hash.sha3_384",
Self::HashSha3_512 => "hash.sha3_512",
Self::HashManyPSD2 => "hash_many.psd2",
Self::HashManyPSD4 => "hash_many.psd4",
Self::HashManyPSD8 => "hash_many.psd8",
Self::HashBHP256Raw => "hash.bhp256.raw",
Self::HashBHP512Raw => "hash.bhp512.raw",
Self::HashBHP768Raw => "hash.bhp768.raw",
Self::HashBHP1024Raw => "hash.bhp1024.raw",
Self::HashKeccak256Raw => "hash.keccak256.raw",
Self::HashKeccak384Raw => "hash.keccak384.raw",
Self::HashKeccak512Raw => "hash.keccak512.raw",
Self::HashPED64Raw => "hash.ped64.raw",
Self::HashPED128Raw => "hash.ped128.raw",
Self::HashPSD2Raw => "hash.psd2.raw",
Self::HashPSD4Raw => "hash.psd4.raw",
Self::HashPSD8Raw => "hash.psd8.raw",
Self::HashSha3_256Raw => "hash.sha3_256.raw",
Self::HashSha3_384Raw => "hash.sha3_384.raw",
Self::HashSha3_512Raw => "hash.sha3_512.raw",
Self::HashKeccak256Native => "hash.keccak256.native",
Self::HashKeccak256NativeRaw => "hash.keccak256.native.raw",
Self::HashKeccak384Native => "hash.keccak384.native",
Self::HashKeccak384NativeRaw => "hash.keccak384.native.raw",
Self::HashKeccak512Native => "hash.keccak512.native",
Self::HashKeccak512NativeRaw => "hash.keccak512.native.raw",
Self::HashSha3_256Native => "hash.sha3_256.native",
Self::HashSha3_256NativeRaw => "hash.sha3_256.native.raw",
Self::HashSha3_384Native => "hash.sha3_384.native",
Self::HashSha3_384NativeRaw => "hash.sha3_384.native.raw",
Self::HashSha3_512Native => "hash.sha3_512.native",
Self::HashSha3_512NativeRaw => "hash.sha3_512.native.raw",
}
}
pub const fn requires_byte_alignment(&self) -> bool {
match self {
Self::HashBHP256
| Self::HashBHP512
| Self::HashBHP768
| Self::HashBHP1024
| Self::HashKeccak256
| Self::HashKeccak384
| Self::HashKeccak512
| Self::HashPED64
| Self::HashPED128
| Self::HashPSD2
| Self::HashPSD4
| Self::HashPSD8
| Self::HashSha3_256
| Self::HashSha3_384
| Self::HashSha3_512
| Self::HashManyPSD2
| Self::HashManyPSD4
| Self::HashManyPSD8 => false,
Self::HashBHP256Raw | Self::HashBHP512Raw | Self::HashBHP768Raw | Self::HashBHP1024Raw => false,
Self::HashKeccak256Raw | Self::HashKeccak384Raw | Self::HashKeccak512Raw => true,
Self::HashPED64Raw | Self::HashPED128Raw | Self::HashPSD2Raw | Self::HashPSD4Raw | Self::HashPSD8Raw => {
false
}
Self::HashSha3_256Raw | Self::HashSha3_384Raw | Self::HashSha3_512Raw => true,
Self::HashKeccak256Native
| Self::HashKeccak256NativeRaw
| Self::HashKeccak384Native
| Self::HashKeccak384NativeRaw
| Self::HashKeccak512Native
| Self::HashKeccak512NativeRaw
| Self::HashSha3_256Native
| Self::HashSha3_256NativeRaw
| Self::HashSha3_384Native
| Self::HashSha3_384NativeRaw
| Self::HashSha3_512Native
| Self::HashSha3_512NativeRaw => true,
}
}
pub const fn is_raw(&self) -> bool {
match self {
Self::HashBHP256
| Self::HashBHP512
| Self::HashBHP768
| Self::HashBHP1024
| Self::HashKeccak256
| Self::HashKeccak384
| Self::HashKeccak512
| Self::HashPED64
| Self::HashPED128
| Self::HashPSD2
| Self::HashPSD4
| Self::HashPSD8
| Self::HashSha3_256
| Self::HashSha3_384
| Self::HashSha3_512
| Self::HashManyPSD2
| Self::HashManyPSD4
| Self::HashManyPSD8 => false,
Self::HashBHP256Raw
| Self::HashBHP512Raw
| Self::HashBHP768Raw
| Self::HashBHP1024Raw
| Self::HashKeccak256Raw
| Self::HashKeccak384Raw
| Self::HashKeccak512Raw
| Self::HashPED64Raw
| Self::HashPED128Raw
| Self::HashPSD2Raw
| Self::HashPSD4Raw
| Self::HashPSD8Raw
| Self::HashSha3_256Raw
| Self::HashSha3_384Raw
| Self::HashSha3_512Raw => true,
Self::HashKeccak256Native
| Self::HashKeccak256NativeRaw
| Self::HashKeccak384Native
| Self::HashKeccak384NativeRaw
| Self::HashKeccak512Native
| Self::HashKeccak512NativeRaw
| Self::HashSha3_256Native
| Self::HashSha3_256NativeRaw
| Self::HashSha3_384Native
| Self::HashSha3_384NativeRaw
| Self::HashSha3_512Native
| Self::HashSha3_512NativeRaw => true,
}
}
pub const fn expected_num_operands(&self) -> usize {
match self {
Self::HashManyPSD2 | Self::HashManyPSD4 | Self::HashManyPSD8 => 2,
_ => 1,
}
}
}
fn check_number_of_operands(variant: u8, opcode: Opcode, num_operands: usize) -> Result<()> {
let variant = HashVariant::new(variant);
let expected = variant.expected_num_operands();
if expected != num_operands {
bail!("Instruction '{opcode}' expects {expected} operands, found {num_operands} operands")
}
Ok(())
}
fn is_valid_destination_type<N: Network>(variant: u8, destination_type: &PlaintextType<N>) -> bool {
match variant {
0..=32 => !matches!(
destination_type,
PlaintextType::Literal(LiteralType::Boolean)
| PlaintextType::Literal(LiteralType::String)
| PlaintextType::Literal(LiteralType::Identifier)
| PlaintextType::Struct(..)
| PlaintextType::ExternalStruct(..)
| PlaintextType::Array(..)
),
33..=44 => matches!(destination_type, PlaintextType::Array(array_type) if array_type.is_bit_array()),
_ => panic!("Invalid 'hash' instruction opcode"),
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct HashInstruction<N: Network, const VARIANT: u8> {
operands: Vec<Operand<N>>,
destination: Register<N>,
destination_type: PlaintextType<N>,
}
impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
pub fn new(
operands: Vec<Operand<N>>,
destination: Register<N>,
destination_type: PlaintextType<N>,
) -> Result<Self> {
check_number_of_operands(VARIANT, Self::opcode(), operands.len())?;
if !is_valid_destination_type(VARIANT, &destination_type) {
bail!("Invalid destination type for 'hash' instruction")
}
Ok(Self { operands, destination, destination_type })
}
pub const fn opcode() -> Opcode {
Opcode::Hash(HashVariant::new(VARIANT).opcode())
}
pub fn operands(&self) -> &[Operand<N>] {
debug_assert!(
check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).is_ok(),
"Invalid number of operands for '{}'",
Self::opcode()
);
&self.operands
}
#[inline]
pub fn destinations(&self) -> Vec<Register<N>> {
vec![self.destination.clone()]
}
#[inline]
pub const fn destination_type(&self) -> &PlaintextType<N> {
&self.destination_type
}
#[inline]
pub fn contains_external_struct(&self) -> bool {
self.destination_type.contains_external_struct()
}
}
#[rustfmt::skip]
macro_rules! do_hash {
($N: ident, $variant: expr, $destination_type: expr, $input: expr, $pt: ty, $lt: ty, $q: expr) => {{
let bits = || $input.to_bits_le();
let bits_raw = || $input.to_bits_raw_le();
let fields = || $q($input.to_fields());
let fields_raw = || $q($input.to_fields_raw());
let check_multiple_of_8 = |bits: Vec<_>| -> Result<Vec<_>> {
ensure!(bits.len() % 8 == 0, "The opcode '{}' expects input whose size in bits is a multiple of 8.", $variant.opcode());
Ok(bits)
};
match ($variant, $destination_type) {
(HashVariant::HashBHP256, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&bits()))?).cast_lossy(*literal_type)?),
(HashVariant::HashBHP512, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&bits()))?).cast_lossy(*literal_type)?),
(HashVariant::HashBHP768, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp768(&bits()))?).cast_lossy(*literal_type)?),
(HashVariant::HashBHP1024, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp1024(&bits()))?).cast_lossy(*literal_type)?),
(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)?),
(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)?),
(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)?),
(HashVariant::HashPED64, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped64(&bits()))?).cast_lossy(*literal_type)?),
(HashVariant::HashPED128, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped128(&bits()))?).cast_lossy(*literal_type)?),
(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)?),
(HashVariant::HashPSD2, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd2(&fields()?))?).cast_lossy(*literal_type)?),
(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)?),
(HashVariant::HashPSD4, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd4(&fields()?))?).cast_lossy(*literal_type)?),
(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)?),
(HashVariant::HashPSD8, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd8(&fields()?))?).cast_lossy(*literal_type)?),
(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)?),
(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)?),
(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)?),
(HashVariant::HashManyPSD2, PlaintextType::Literal(_)) => bail!("'hash_many.psd2' is not yet implemented"),
(HashVariant::HashManyPSD4, PlaintextType::Literal(_)) => bail!("'hash_many.psd4' is not yet implemented"),
(HashVariant::HashManyPSD8, PlaintextType::Literal(_)) => bail!("'hash_many.psd8' is not yet implemented"),
(HashVariant::HashBHP256Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp256(&bits_raw()))?).cast_lossy(*literal_type)?),
(HashVariant::HashBHP512Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp512(&bits_raw()))?).cast_lossy(*literal_type)?),
(HashVariant::HashBHP768Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp768(&bits_raw()))?).cast_lossy(*literal_type)?),
(HashVariant::HashBHP1024Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_bhp1024(&bits_raw()))?).cast_lossy(*literal_type)?),
(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)?),
(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)?),
(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)?),
(HashVariant::HashPED64Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped64(&bits_raw()))?).cast_lossy(*literal_type)?),
(HashVariant::HashPED128Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_to_group_ped128(&bits_raw()))?).cast_lossy(*literal_type)?),
(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)?),
(HashVariant::HashPSD2Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd2(&fields_raw()?))?).cast_lossy(*literal_type)?),
(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)?),
(HashVariant::HashPSD4Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd4(&fields_raw()?))?).cast_lossy(*literal_type)?),
(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)?),
(HashVariant::HashPSD8Raw, PlaintextType::Literal(literal_type)) => <$pt>::from(<$lt>::from($q($N::hash_psd8(&fields_raw()?))?).cast_lossy(*literal_type)?),
(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)?),
(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)?),
(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)?),
(HashVariant::HashKeccak256Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak256(&bits()))?, **array_type.length())?,
(HashVariant::HashKeccak256NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak256(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
(HashVariant::HashKeccak384Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak384(&bits()))?, **array_type.length())?,
(HashVariant::HashKeccak384NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak384(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
(HashVariant::HashKeccak512Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak512(&bits()))?, **array_type.length())?,
(HashVariant::HashKeccak512NativeRaw, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_keccak512(&check_multiple_of_8(bits_raw())?))?, **array_type.length())?,
(HashVariant::HashSha3_256Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_256(&bits()))?, **array_type.length())?,
(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())?,
(HashVariant::HashSha3_384Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_384(&bits()))?, **array_type.length())?,
(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())?,
(HashVariant::HashSha3_512Native, PlaintextType::Array(array_type)) => <$pt>::from_bit_array($q($N::hash_sha3_512(&bits()))?, **array_type.length())?,
(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())?,
(_, destination_type) => bail!("Invalid destination type '{destination_type}' for 'hash' variant: {}", $variant.opcode()),
}
}};
}
pub fn evaluate_hash<N: Network>(
variant: HashVariant,
input: &Value<N>,
destination_type: &PlaintextType<N>,
) -> Result<Plaintext<N>> {
evaluate_hash_internal(variant, input, destination_type)
}
fn evaluate_hash_internal<N: Network>(
variant: HashVariant,
input: &Value<N>,
destination_type: &PlaintextType<N>,
) -> Result<Plaintext<N>> {
Ok(do_hash!(N, variant, destination_type, input, Plaintext::<N>, Literal::<N>, |x| x))
}
impl<N: Network, const VARIANT: u8> HashInstruction<N, VARIANT> {
pub fn evaluate(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
ensure!(
is_valid_destination_type(VARIANT, &self.destination_type),
"Invalid destination type in 'hash' instruction"
);
let input = registers.load(stack, &self.operands[0])?;
let output = evaluate_hash_internal(HashVariant::new(VARIANT), &input, &self.destination_type)?;
registers.store(stack, &self.destination, Value::Plaintext(output))
}
pub fn execute<A: circuit::Aleo<Network = N>>(
&self,
stack: &impl StackTrait<N>,
registers: &mut impl RegistersCircuit<N, A>,
) -> Result<()> {
use circuit::traits::{ToBits, ToBitsRaw, ToFields, ToFieldsRaw};
check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
ensure!(
is_valid_destination_type(VARIANT, &self.destination_type),
"Invalid destination type in 'hash' instruction"
);
let input = registers.load_circuit(stack, &self.operands[0])?;
let output = do_hash!(
A,
HashVariant::new(VARIANT),
&self.destination_type,
input,
circuit::Plaintext::<A>,
circuit::Literal::<A>,
Result::<_>::Ok
);
registers.store_circuit(stack, &self.destination, circuit::Value::Plaintext(output))
}
#[inline]
pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
self.evaluate(stack, registers)
}
pub fn output_types(
&self,
stack: &impl StackTrait<N>,
input_types: &[RegisterType<N>],
) -> Result<Vec<RegisterType<N>>> {
check_number_of_operands(VARIANT, Self::opcode(), input_types.len())?;
check_number_of_operands(VARIANT, Self::opcode(), self.operands.len())?;
ensure!(
is_valid_destination_type(VARIANT, &self.destination_type),
"Invalid destination type in 'hash' instruction"
);
let variant = HashVariant::new(VARIANT);
if variant.requires_byte_alignment() {
ensure!(
variant.expected_num_operands() == 1,
"Expected one operand for '{}', found '{}'",
variant.opcode(),
variant.expected_num_operands()
);
let get_struct = |identifier: &Identifier<N>| stack.program().get_struct(identifier).cloned();
let get_external_struct = |locator: &Locator<N>| {
stack.get_external_stack(locator.program_id())?.program().get_struct(locator.resource()).cloned()
};
let get_record = |identifier: &Identifier<N>| stack.program().get_record(identifier).cloned();
let get_external_record = |locator: &Locator<N>| {
stack.get_external_stack(locator.program_id())?.program().get_record(locator.resource()).cloned()
};
let get_future = |locator: &Locator<N>| {
Ok(match stack.program_id() == locator.program_id() {
true => stack
.program()
.get_function_ref(locator.resource())?
.finalize_logic()
.ok_or_else(|| anyhow!("'{locator}' does not have a finalize scope"))?
.input_types(),
false => stack
.get_external_stack(locator.program_id())?
.program()
.get_function_ref(locator.resource())?
.finalize_logic()
.ok_or_else(|| anyhow!("Failed to find function '{locator}'"))?
.input_types(),
})
};
let size_in_bits = match variant.is_raw() {
false => input_types[0].size_in_bits(
&get_struct,
&get_external_struct,
&get_record,
&get_external_record,
&get_future,
)?,
true => input_types[0].size_in_bits_raw(
&get_struct,
&get_external_struct,
&get_record,
&get_external_record,
&get_future,
)?,
};
ensure!(
size_in_bits % 8 == 0,
"Expected a multiple of 8 bits for '{}', found '{size_in_bits}'",
variant.opcode()
);
}
match variant {
HashVariant::HashManyPSD2 | HashVariant::HashManyPSD4 | HashVariant::HashManyPSD8 => {
bail!("'hash_many' is not yet implemented")
}
_ => Ok(vec![RegisterType::Plaintext(self.destination_type.clone())]),
}
}
}
impl<N: Network, const VARIANT: u8> Parser for HashInstruction<N, VARIANT> {
fn parse(string: &str) -> ParserResult<Self> {
fn parse_operands<N: Network>(string: &str, num_operands: usize) -> ParserResult<Vec<Operand<N>>> {
let mut operands = Vec::with_capacity(num_operands);
let mut string = string;
for _ in 0..num_operands {
let (next_string, _) = Sanitizer::parse_whitespaces(string)?;
let (next_string, operand) = Operand::parse(next_string)?;
string = next_string;
operands.push(operand);
}
Ok((string, operands))
}
let (string, _) = tag(*Self::opcode())(string)?;
let (string, operands) = parse_operands(string, HashVariant::new(VARIANT).expected_num_operands())?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag("into")(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, destination) = Register::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag("as")(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, destination_type) = PlaintextType::parse(string)?;
match destination_type {
PlaintextType::Literal(LiteralType::Boolean)
| PlaintextType::Literal(LiteralType::String)
| PlaintextType::Literal(LiteralType::Identifier) => map_res(fail, |_: ParserResult<Self>| {
Err(error(format!("Failed to parse 'hash': '{destination_type}' is invalid")))
})(string),
_ => Ok((string, Self { operands, destination, destination_type })),
}
}
}
impl<N: Network, const VARIANT: u8> FromStr for HashInstruction<N, VARIANT> {
type Err = Error;
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
impl<N: Network, const VARIANT: u8> Debug for HashInstruction<N, VARIANT> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network, const VARIANT: u8> Display for HashInstruction<N, VARIANT> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|_| fmt::Error)?;
write!(f, "{} ", Self::opcode())?;
self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
write!(f, "into {} as {}", self.destination, self.destination_type)
}
}
impl<N: Network, const VARIANT: u8> FromBytes for HashInstruction<N, VARIANT> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let num_operands = HashVariant::new(VARIANT).expected_num_operands();
let operands = (0..num_operands).map(|_| Operand::read_le(&mut reader)).collect::<Result<_, _>>()?;
let destination = Register::read_le(&mut reader)?;
let destination_type = PlaintextType::read_le(&mut reader)?;
Ok(Self { operands, destination, destination_type })
}
}
impl<N: Network, const VARIANT: u8> ToBytes for HashInstruction<N, VARIANT> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
check_number_of_operands(VARIANT, Self::opcode(), self.operands.len()).map_err(|e| error(format!("{e}")))?;
self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
self.destination.write_le(&mut writer)?;
self.destination_type.write_le(&mut writer)
}
}
#[cfg(test)]
mod tests {
use super::*;
use console::{network::MainnetV0, program::ArrayType, types::U32};
type CurrentNetwork = MainnetV0;
fn sample_valid_destination_types<N: Network, R: CryptoRng + Rng>(
variant: u8,
rng: &mut R,
) -> Vec<PlaintextType<N>> {
match variant {
0..=32 => vec![
PlaintextType::Literal(LiteralType::Address),
PlaintextType::Literal(LiteralType::Field),
PlaintextType::Literal(LiteralType::Group),
PlaintextType::Literal(LiteralType::I8),
PlaintextType::Literal(LiteralType::I16),
PlaintextType::Literal(LiteralType::I32),
PlaintextType::Literal(LiteralType::I64),
PlaintextType::Literal(LiteralType::I128),
PlaintextType::Literal(LiteralType::U8),
PlaintextType::Literal(LiteralType::U16),
PlaintextType::Literal(LiteralType::U32),
PlaintextType::Literal(LiteralType::U64),
PlaintextType::Literal(LiteralType::U128),
PlaintextType::Literal(LiteralType::Scalar),
],
33..=44 => (0..10)
.map(|_| {
PlaintextType::Array(
ArrayType::new(PlaintextType::Literal(LiteralType::Boolean), vec![U32::new(
u32::try_from(rng.gen_range(1..=CurrentNetwork::LATEST_MAX_ARRAY_ELEMENTS())).unwrap(),
)])
.unwrap(),
)
})
.collect(),
_ => panic!("Invalid 'hash' instruction opcode"),
}
}
fn run_test<N: Network, const VARIANT: u8>() {
let rng = &mut TestRng::default();
let opcode = HashInstruction::<N, VARIANT>::opcode();
for destination_type in sample_valid_destination_types(VARIANT, rng) {
let instruction = format!("{opcode} r0 into r1 as {destination_type}");
println!("Testing instruction: '{instruction}'");
let (string, hash) = HashInstruction::<CurrentNetwork, VARIANT>::parse(&instruction).unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
assert_eq!(hash.operands.len(), 1, "The number of operands is incorrect");
assert_eq!(hash.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
assert_eq!(hash.destination, Register::Locator(1), "The destination register is incorrect");
assert_eq!(&hash.destination_type, &destination_type, "The destination type is incorrect");
}
}
#[test]
fn test_parse() {
run_test::<CurrentNetwork, { HashVariant::HashBHP256 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashBHP512 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashBHP768 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashBHP1024 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak256 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak384 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak512 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPED64 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPED128 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPSD2 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPSD4 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPSD8 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_256 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_384 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_512 as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashBHP256Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashBHP512Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashBHP768Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashBHP1024Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak256Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak384Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak512Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPED64Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPED128Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPSD2Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPSD4Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashPSD8Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_256Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_384Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_512Raw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak256Native as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak384Native as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak512Native as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_256Native as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_384Native as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_512Native as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak256NativeRaw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak384NativeRaw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashKeccak512NativeRaw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_256NativeRaw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_384NativeRaw as u8 }>();
run_test::<CurrentNetwork, { HashVariant::HashSha3_512NativeRaw as u8 }>();
}
#[test]
fn check_number_of_hash_variants() {
assert_eq!(enum_iterator::cardinality::<HashVariant>(), 45);
}
#[test]
fn check_byte_aligned_variants_all_have_one_opcode() {
for variant in enum_iterator::all::<HashVariant>() {
if variant.requires_byte_alignment() {
assert_eq!(variant.expected_num_operands(), 1)
}
}
}
}