use std::fmt::Display;
use crate::error::{CommonError, Result};
pub use zisk_definitions::hints::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum CtrlHint {
Start = CTRL_START,
End = CTRL_END,
Cancel = CTRL_CANCEL,
Error = CTRL_ERROR,
}
impl Display for CtrlHint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
CtrlHint::Start => "CTRL_START",
CtrlHint::End => "CTRL_END",
CtrlHint::Cancel => "CTRL_CANCEL",
CtrlHint::Error => "CTRL_ERROR",
};
write!(f, "{} ({:#x})", name, *self as u32)
}
}
impl TryFrom<u32> for CtrlHint {
type Error = CommonError;
fn try_from(value: u32) -> Result<Self> {
match value {
CTRL_START => Ok(Self::Start),
CTRL_END => Ok(Self::End),
CTRL_CANCEL => Ok(Self::Cancel),
CTRL_ERROR => Ok(Self::Error),
_ => Err(CommonError::InvalidHint(format!("Invalid control code: {:#x}", value))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum BuiltInHint {
Input = HINT_INPUT,
Sha256 = HINT_SHA256,
Bn254G1Add = HINT_BN254_G1_ADD,
Bn254G1Mul = HINT_BN254_G1_MUL,
Bn254PairingCheck = HINT_BN254_PAIRING_CHECK,
Secp256k1Ecrecover = HINT_SECP256K1_ECRECOVER,
Secp256k1EcdsaVerify = HINT_SECP256K1_ECDSA_VERIFY,
Secp256r1EcdsaVerify = HINT_SECP256R1_ECDSA_VERIFY,
Bls12_381G1Add = HINT_BLS12_381_G1_ADD,
Bls12_381G1Msm = HINT_BLS12_381_G1_MSM,
Bls12_381G2Add = HINT_BLS12_381_G2_ADD,
Bls12_381G2Msm = HINT_BLS12_381_G2_MSM,
Bls12_381PairingCheck = HINT_BLS12_381_PAIRING_CHECK,
Bls12_381FpToG1 = HINT_BLS12_381_FP_TO_G1,
Bls12_381Fp2ToG2 = HINT_BLS12_381_FP2_TO_G2,
ModExp = HINT_MODEXP,
MulMod256 = HINT_MULMOD256,
ReduceMod256 = HINT_REDUCE_MOD256,
AddMod256 = HINT_ADD_MOD256,
SquareMod256 = HINT_SQUARE_MOD256,
PowMod256 = HINT_POW_MOD256,
InvMod256 = HINT_INV_MOD256,
VerifyKzgProof = HINT_VERIFY_KZG_PROOF,
Keccak256 = HINT_KECCAK256,
Blake2bCompress = HINT_BLAKE2B_COMPRESS,
Ripemd160 = HINT_RIPEMD160,
}
impl Display for BuiltInHint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
BuiltInHint::Input => "INPUT",
BuiltInHint::Sha256 => "SHA256",
BuiltInHint::Bn254G1Add => "BN254_G1_ADD",
BuiltInHint::Bn254G1Mul => "BN254_G1_MUL",
BuiltInHint::Bn254PairingCheck => "BN254_PAIRING_CHECK",
BuiltInHint::Secp256k1Ecrecover => "SECP256K1_ECRECOVER",
BuiltInHint::Secp256k1EcdsaVerify => "SECP256K1_ECDSA_VERIFY",
BuiltInHint::Secp256r1EcdsaVerify => "SECP256R1_ECDSA_VERIFY",
BuiltInHint::Bls12_381G1Add => "BLS12_381_G1_ADD",
BuiltInHint::Bls12_381G1Msm => "BLS12_381_G1_MSM",
BuiltInHint::Bls12_381G2Add => "BLS12_381_G2_ADD",
BuiltInHint::Bls12_381G2Msm => "BLS12_381_G2_MSM",
BuiltInHint::Bls12_381PairingCheck => "BLS12_381_PAIRING_CHECK",
BuiltInHint::Bls12_381FpToG1 => "BLS12_381_FP_TO_G1",
BuiltInHint::Bls12_381Fp2ToG2 => "BLS12_381_FP2_TO_G2",
BuiltInHint::ModExp => "MODEXP",
BuiltInHint::MulMod256 => "MULMOD256",
BuiltInHint::ReduceMod256 => "REDUCE_MOD256",
BuiltInHint::AddMod256 => "ADD_MOD256",
BuiltInHint::SquareMod256 => "SQUARE_MOD256",
BuiltInHint::PowMod256 => "POW_MOD256",
BuiltInHint::InvMod256 => "INV_MOD256",
BuiltInHint::VerifyKzgProof => "VERIFY_KZG_PROOF",
BuiltInHint::Keccak256 => "KECCAK256",
BuiltInHint::Blake2bCompress => "BLAKE2B_COMPRESS",
BuiltInHint::Ripemd160 => "RIPEMD160",
};
write!(f, "{} ({:#x})", name, *self as u32)
}
}
impl TryFrom<u32> for BuiltInHint {
type Error = CommonError;
fn try_from(value: u32) -> Result<Self> {
match value {
HINT_INPUT => Ok(Self::Input),
HINT_SHA256 => Ok(Self::Sha256),
HINT_BN254_G1_ADD => Ok(Self::Bn254G1Add),
HINT_BN254_G1_MUL => Ok(Self::Bn254G1Mul),
HINT_BN254_PAIRING_CHECK => Ok(Self::Bn254PairingCheck),
HINT_SECP256K1_ECRECOVER => Ok(Self::Secp256k1Ecrecover),
HINT_SECP256K1_ECDSA_VERIFY => Ok(Self::Secp256k1EcdsaVerify),
HINT_SECP256R1_ECDSA_VERIFY => Ok(Self::Secp256r1EcdsaVerify),
HINT_BLS12_381_G1_ADD => Ok(Self::Bls12_381G1Add),
HINT_BLS12_381_G1_MSM => Ok(Self::Bls12_381G1Msm),
HINT_BLS12_381_G2_ADD => Ok(Self::Bls12_381G2Add),
HINT_BLS12_381_G2_MSM => Ok(Self::Bls12_381G2Msm),
HINT_BLS12_381_PAIRING_CHECK => Ok(Self::Bls12_381PairingCheck),
HINT_BLS12_381_FP_TO_G1 => Ok(Self::Bls12_381FpToG1),
HINT_BLS12_381_FP2_TO_G2 => Ok(Self::Bls12_381Fp2ToG2),
HINT_MODEXP => Ok(Self::ModExp),
HINT_MULMOD256 => Ok(Self::MulMod256),
HINT_REDUCE_MOD256 => Ok(Self::ReduceMod256),
HINT_ADD_MOD256 => Ok(Self::AddMod256),
HINT_SQUARE_MOD256 => Ok(Self::SquareMod256),
HINT_POW_MOD256 => Ok(Self::PowMod256),
HINT_INV_MOD256 => Ok(Self::InvMod256),
HINT_VERIFY_KZG_PROOF => Ok(Self::VerifyKzgProof),
HINT_KECCAK256 => Ok(Self::Keccak256),
HINT_BLAKE2B_COMPRESS => Ok(Self::Blake2bCompress),
HINT_RIPEMD160 => Ok(Self::Ripemd160),
_ => Err(CommonError::InvalidHint(format!("Invalid built-in hint code: {:#x}", value))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum HintCode {
Ctrl(CtrlHint),
BuiltIn(BuiltInHint),
Custom(u32),
}
impl Display for HintCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HintCode::Ctrl(ctrl) => write!(f, "{}", ctrl),
HintCode::BuiltIn(builtin) => write!(f, "{}", builtin),
HintCode::Custom(code) => write!(f, "CUSTOM_HINT_{:#x}", code),
}
}
}
impl TryFrom<u32> for HintCode {
type Error = CommonError;
fn try_from(value: u32) -> Result<Self> {
if let Ok(ctrl) = CtrlHint::try_from(value) {
return Ok(HintCode::Ctrl(ctrl));
}
if let Ok(builtin) = BuiltInHint::try_from(value) {
return Ok(HintCode::BuiltIn(builtin));
}
Err(CommonError::InvalidHint(format!("Unknown hint code: {:#x}", value)))
}
}
impl HintCode {
#[inline]
pub const fn to_u32(self) -> u32 {
match self {
HintCode::Ctrl(CtrlHint::Start) => CTRL_START,
HintCode::Ctrl(CtrlHint::End) => CTRL_END,
HintCode::Ctrl(CtrlHint::Cancel) => CTRL_CANCEL,
HintCode::Ctrl(CtrlHint::Error) => CTRL_ERROR,
HintCode::BuiltIn(BuiltInHint::Input) => HINT_INPUT,
HintCode::BuiltIn(BuiltInHint::Sha256) => HINT_SHA256,
HintCode::BuiltIn(BuiltInHint::Bn254G1Add) => HINT_BN254_G1_ADD,
HintCode::BuiltIn(BuiltInHint::Bn254G1Mul) => HINT_BN254_G1_MUL,
HintCode::BuiltIn(BuiltInHint::Bn254PairingCheck) => HINT_BN254_PAIRING_CHECK,
HintCode::BuiltIn(BuiltInHint::Secp256k1Ecrecover) => HINT_SECP256K1_ECRECOVER,
HintCode::BuiltIn(BuiltInHint::Secp256k1EcdsaVerify) => HINT_SECP256K1_ECDSA_VERIFY,
HintCode::BuiltIn(BuiltInHint::Secp256r1EcdsaVerify) => HINT_SECP256R1_ECDSA_VERIFY,
HintCode::BuiltIn(BuiltInHint::Bls12_381G1Add) => HINT_BLS12_381_G1_ADD,
HintCode::BuiltIn(BuiltInHint::Bls12_381G1Msm) => HINT_BLS12_381_G1_MSM,
HintCode::BuiltIn(BuiltInHint::Bls12_381G2Add) => HINT_BLS12_381_G2_ADD,
HintCode::BuiltIn(BuiltInHint::Bls12_381G2Msm) => HINT_BLS12_381_G2_MSM,
HintCode::BuiltIn(BuiltInHint::Bls12_381PairingCheck) => HINT_BLS12_381_PAIRING_CHECK,
HintCode::BuiltIn(BuiltInHint::Bls12_381FpToG1) => HINT_BLS12_381_FP_TO_G1,
HintCode::BuiltIn(BuiltInHint::Bls12_381Fp2ToG2) => HINT_BLS12_381_FP2_TO_G2,
HintCode::BuiltIn(BuiltInHint::ModExp) => HINT_MODEXP,
HintCode::BuiltIn(BuiltInHint::MulMod256) => HINT_MULMOD256,
HintCode::BuiltIn(BuiltInHint::ReduceMod256) => HINT_REDUCE_MOD256,
HintCode::BuiltIn(BuiltInHint::AddMod256) => HINT_ADD_MOD256,
HintCode::BuiltIn(BuiltInHint::SquareMod256) => HINT_SQUARE_MOD256,
HintCode::BuiltIn(BuiltInHint::PowMod256) => HINT_POW_MOD256,
HintCode::BuiltIn(BuiltInHint::InvMod256) => HINT_INV_MOD256,
HintCode::BuiltIn(BuiltInHint::VerifyKzgProof) => HINT_VERIFY_KZG_PROOF,
HintCode::BuiltIn(BuiltInHint::Keccak256) => HINT_KECCAK256,
HintCode::BuiltIn(BuiltInHint::Blake2bCompress) => HINT_BLAKE2B_COMPRESS,
HintCode::BuiltIn(BuiltInHint::Ripemd160) => HINT_RIPEMD160,
HintCode::Custom(code) => code,
}
}
}
#[derive(Debug, Clone)]
pub struct PartialPrecompileHint {
pub hint_code: HintCode,
pub is_passthrough: bool,
pub data: Vec<u64>,
pub expected_len_bytes: usize,
pub remaining_u64s: usize,
}
#[derive(Debug)]
pub enum PrecompileHintParseResult {
Complete(PrecompileHint),
Partial(PartialPrecompileHint),
}
pub struct PrecompileHint {
pub hint_code: HintCode,
pub is_passthrough: bool,
pub data: Vec<u64>,
pub data_len_bytes: usize,
}
impl std::fmt::Debug for PrecompileHint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let data_display = if self.data.len() <= 10 {
format!("{:x?}", self.data)
} else {
format!("{:x?}... ({} more)", &self.data[..10], self.data.len() - 10)
};
f.debug_struct("PrecompileHint")
.field("hint_type", &self.hint_code)
.field("is_passthrough", &self.is_passthrough)
.field("data_len_bytes", &self.data_len_bytes)
.field("data", &data_display)
.finish()
}
}
impl PrecompileHint {
#[inline(always)]
pub fn from_u64_slice(
slice: &[u64],
idx: usize,
allow_custom: bool,
partial: Option<PartialPrecompileHint>,
) -> Result<(PrecompileHintParseResult, usize)> {
if let Some(partial_hint) = partial {
let available = slice.len().checked_sub(idx).ok_or(CommonError::OutOfBounds)?;
if available >= partial_hint.remaining_u64s {
let consumed = partial_hint.remaining_u64s;
let mut data = partial_hint.data;
data.extend_from_slice(&slice[idx..idx + consumed]);
return Ok((
PrecompileHintParseResult::Complete(PrecompileHint {
hint_code: partial_hint.hint_code,
is_passthrough: partial_hint.is_passthrough,
data,
data_len_bytes: partial_hint.expected_len_bytes,
}),
consumed,
));
} else {
let mut data = partial_hint.data;
data.extend_from_slice(&slice[idx..]);
let remaining_u64s = partial_hint.remaining_u64s - available;
return Ok((
PrecompileHintParseResult::Partial(PartialPrecompileHint {
hint_code: partial_hint.hint_code,
is_passthrough: partial_hint.is_passthrough,
data,
expected_len_bytes: partial_hint.expected_len_bytes,
remaining_u64s,
}),
available,
));
}
}
if slice.len() <= idx {
return Err(CommonError::OutOfBounds);
}
let header = slice[idx];
let length = header & 0xFFFFFFFF;
let length_bytes = length as usize;
let num_u64s = length.div_ceil(8) as usize;
let hint_code_32 = (header >> 32) as u32;
let is_passthrough = hint_code_32 >> 31 != 0;
let hint_code_value = hint_code_32 & 0x7FFFFFFF;
let hint_code = if allow_custom {
HintCode::try_from(hint_code_value).unwrap_or(HintCode::Custom(hint_code_value))
} else {
HintCode::try_from(hint_code_value)?
};
let available_u64s = slice.len() - idx - 1;
if available_u64s < num_u64s {
let data = slice[idx + 1..].to_vec();
let remaining_u64s = num_u64s - available_u64s;
let consumed = 1 + available_u64s;
return Ok((
PrecompileHintParseResult::Partial(PartialPrecompileHint {
hint_code,
is_passthrough,
data,
expected_len_bytes: length_bytes,
remaining_u64s,
}),
consumed,
));
}
let data = slice[idx + 1..idx + 1 + num_u64s].to_vec();
let consumed = 1 + num_u64s;
Ok((
PrecompileHintParseResult::Complete(PrecompileHint {
hint_code,
is_passthrough,
data,
data_len_bytes: length_bytes,
}),
consumed,
))
}
}