#![allow(unused)]
use zisk_precomp_helpers::DmaInfo;
use ziskos::zisklib::fcall_proxy;
use crate::{
blake2br, operations::*, sha256f, EmulationMode, InstContext, Mem, ZiskOperationType,
ZiskRequiredOperation, ADD256_COST, ADD_U_W_COST, ARITHA32_COST, ARITHAM32_COST,
ARITH_EQ_384_COST, ARITH_EQ_COST, BINARY_ADD_COST, BINARY_COST, BINARY_E_COST, BLAKE2_COST,
DMA_64_ALIGNED_COST, DMA_COST, DMA_INPUTCPY_COST, DMA_MEMCMP_COST, DMA_MEMCPY_COST,
DMA_MEMSET_COST, DMA_PRE_POST_COST, DMA_UNALIGNED_COST, EXTRA_PARAMS_ADDR, FCALL_COST,
INPUT_ADDR, INTERNAL_COST, JUMP_DEST_COST, KECCAK_COST, M64, MAX_INPUT_SIZE, POSEIDON_COST,
REG_A0, SHA256_COST, SH_ADD_COST, SH_ADD_U_W_COST, SLL_U_W_COST, SYS_ADDR,
};
use paste::paste;
use proofman_fields::{
poseidon1_hash, poseidon2_hash, Goldilocks, Poseidon1_16, Poseidon2_16, PrimeField64,
};
use std::{
collections::HashMap,
fmt::{Debug, Display},
num::Wrapping,
str::FromStr,
};
use tiny_keccak::keccakf;
use ziskos::zisklib::{
keccakf_cache::KECCAKF_STATE_WORDS, FCALL_GET_KECCAKF_CACHE_INDEX_ID, FCALL_INPUT_READY_ID,
FCALL_SET_KECCAKF_CACHE_INDEX_ID,
};
use crate::ops_core::*;
use crate::ops_core_context::*;
use crate::{FCALL_PARAMS_MAX_SIZE, FCALL_RESULT_MAX_SIZE};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum OpType {
Internal,
Arith,
ArithA32,
ArithAm32,
Binary,
BinaryE,
Keccak,
Sha256,
Poseidon,
PubOut,
ArithEq,
Fcall,
ArithEq384,
BigInt,
Evm,
Dma,
Blake2,
Profile,
}
impl From<OpType> for ZiskOperationType {
fn from(op_type: OpType) -> Self {
match op_type {
OpType::Internal => ZiskOperationType::Internal,
OpType::Arith | OpType::ArithA32 | OpType::ArithAm32 => ZiskOperationType::Arith,
OpType::Binary => ZiskOperationType::Binary,
OpType::BinaryE => ZiskOperationType::BinaryE,
OpType::Keccak => ZiskOperationType::Keccak,
OpType::Sha256 => ZiskOperationType::Sha256,
OpType::Poseidon => ZiskOperationType::Poseidon,
OpType::PubOut => ZiskOperationType::PubOut,
OpType::ArithEq => ZiskOperationType::ArithEq,
OpType::Fcall => ZiskOperationType::Fcall,
OpType::ArithEq384 => ZiskOperationType::ArithEq384,
OpType::BigInt => ZiskOperationType::BigInt,
OpType::Evm => ZiskOperationType::Evm,
OpType::Dma => ZiskOperationType::Dma,
OpType::Blake2 => ZiskOperationType::Blake2,
OpType::Profile => ZiskOperationType::Profile,
}
}
}
impl Display for OpType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Internal => write!(f, "i"),
Self::Arith => write!(f, "a"),
Self::ArithA32 => write!(f, "a32"),
Self::ArithAm32 => write!(f, "am32"),
Self::Binary => write!(f, "b"),
Self::BinaryE => write!(f, "BinaryE"),
Self::Keccak => write!(f, "Keccak"),
Self::Sha256 => write!(f, "Sha256"),
Self::Poseidon => write!(f, "Poseidon"),
Self::PubOut => write!(f, "PubOut"),
Self::ArithEq => write!(f, "Arith256"),
Self::Fcall => write!(f, "Fcall"),
Self::ArithEq384 => write!(f, "Arith384"),
Self::BigInt => write!(f, "BigInt"),
Self::Evm => write!(f, "Evm"),
Self::Dma => write!(f, "Dma"),
Self::Blake2 => write!(f, "Blake2"),
Self::Profile => write!(f, "Profile"),
}
}
}
impl FromStr for OpType {
type Err = InvalidOpTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"i" => Ok(Self::Internal),
"a" => Ok(Self::Arith),
"a32" => Ok(Self::ArithA32),
"am32" => Ok(Self::ArithAm32),
"b" => Ok(Self::Binary),
"be" => Ok(Self::BinaryE),
"k" => Ok(Self::Keccak),
"s" => Ok(Self::Sha256),
"p" => Ok(Self::Poseidon),
"aeq" => Ok(Self::ArithEq),
"fcall" => Ok(Self::Fcall),
"aeq384" => Ok(Self::ArithEq384),
"bint" => Ok(Self::BigInt),
"evm" => Ok(Self::Evm),
"dma" => Ok(Self::Dma),
"bl" => Ok(Self::Blake2),
"profile" => Ok(Self::Profile),
_ => Err(InvalidOpTypeError),
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct InvalidOpTypeError;
impl Display for InvalidOpTypeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "invalid operation type")
}
}
#[derive(Copy, Clone, Debug)]
pub struct InvalidNameError;
impl Display for InvalidNameError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "invalid op name")
}
}
#[derive(Copy, Clone, Debug)]
pub struct InvalidCodeError;
impl Display for InvalidCodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "invalid op code")
}
}
pub trait OpStats {
fn mem_align_read(&mut self, addr: u64, count: usize);
fn mem_align_write(&mut self, addr: u64, count: usize);
fn set_variable_cost(&mut self, cost: u64);
}
#[inline(always)]
pub fn ops_none(_ctx: &InstContext, _stats: &mut dyn OpStats) {
}
macro_rules! define_ops {
( $( ($name:ident, $str_name:expr, $type:ident, $cost:expr, $code:expr, $input_size:expr, $output_size:expr, $call_fn:ident, $call_ab_fn:ident, $call_stats_fn:ident ) ),* $(,)? ) => {
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
#[repr(u8)]
pub enum ZiskOp {
$(
$name = $code,
)*
}
impl ZiskOp {
$(
paste! {
pub const [<$str_name:upper>]: u8 = $code;
}
)*
pub const MIN_OPCODE: u8 = {
const CODES: &[u8] = &[$($code),*];
let mut min = 2; let mut i = 1;
while i < CODES.len() {
if CODES[i] > 1 && CODES[i] < min {
min = CODES[i];
}
i += 1;
}
min
};
pub const MAX_OPCODE: u8 = {
const CODES: &[u8] = &[$($code),*];
let mut max = CODES[0];
let mut i = 1;
while i < CODES.len() {
if CODES[i] > max {
max = CODES[i];
}
i += 1;
}
max
};
pub const OPCODES_COUNT: usize = {
const CODES: &[u8] = &[$($code),*];
CODES.len()
};
pub const fn name(&self) -> &'static str {
match self {
$(
Self::$name => $str_name,
)*
}
}
pub const fn op_type(&self) -> OpType {
match self {
$(
Self::$name => OpType::$type,
)*
}
}
pub const fn cost(&self) -> u64 {
match self {
$(
Self::$name => $cost,
)*
}
}
pub const fn code(&self) -> u8 {
match self {
$(
Self::$name => $code,
)*
}
}
pub const fn input_size(&self) -> u64 {
match self {
$(
Self::$name => $input_size,
)*
}
}
pub const fn output_size(&self) -> u64 {
match self {
$(
Self::$name => $output_size,
)*
}
}
#[inline(always)]
pub fn call(&self, ctx: &mut InstContext) {
match self {
$(
Self::$name => $call_fn(ctx),
)*
}
}
pub const fn get_call_function(&self) -> fn(&mut InstContext) -> () {
match self {
$(
Self::$name => $call_fn,
)*
}
}
#[inline(always)]
pub fn call_stats(&self, ctx: &InstContext, stats: &mut dyn OpStats) {
match self {
$(
Self::$name => $call_stats_fn(ctx, stats),
)*
}
}
pub const fn get_call_stats_function(&self) -> fn(&InstContext, &mut dyn OpStats) -> () {
match self {
$(
Self::$name => $call_stats_fn,
)*
}
}
#[inline(always)]
pub fn call_ab(&self, a: u64, b: u64) -> (u64, bool) {
match self {
$(
Self::$name => $call_ab_fn(a, b),
)*
}
}
#[inline(always)]
pub fn is_precompiled(&self) -> bool {
match self {
$(
Self::$name => $input_size > 0,
)*
}
}
pub fn try_from_name(st: &str) -> Result<ZiskOp, InvalidNameError> {
match st {
$(
$str_name => Ok(Self::$name),
)*
_ => Err(InvalidNameError)
}
}
pub const fn try_from_code(code: u8) -> Result<ZiskOp, InvalidCodeError> {
match code {
$(
$code => Ok(Self::$name),
)*
_ => Err(InvalidCodeError)
}
}
#[inline(always)]
pub fn execute(code: u8, a: u64, b: u64) -> (u64, bool) {
match code {
$(
$code => Self::$name.call_ab(a, b),
)*
_ => panic!("Invalid opcode: {}", code),
}
}
}
};
}
define_ops! {
(Flag, "flag", Internal, INTERNAL_COST, 0x00, 0, 0, opc_flag, op_flag, ops_none),
(CopyB, "copyb", Internal, INTERNAL_COST, 0x01, 0, 0, opc_copyb, op_copyb, ops_none),
(Minu, "minu", Binary, BINARY_COST, 0x02, 0, 0, opc_minu, op_minu, ops_none),
(Min, "min", Binary, BINARY_COST, 0x03, 0, 0, opc_min, op_min, ops_none),
(Maxu, "maxu", Binary, BINARY_COST, 0x04, 0, 0, opc_maxu, op_maxu, ops_none),
(Max, "max", Binary, BINARY_COST, 0x05, 0, 0, opc_max, op_max, ops_none),
(Ltu, "ltu", Binary, BINARY_COST, 0x06, 0, 0, opc_ltu, op_ltu, ops_none),
(Lt, "lt", Binary, BINARY_COST, 0x07, 0, 0, opc_lt, op_lt, ops_none),
(Eq, "eq", Binary, BINARY_COST, 0x09, 0, 0, opc_eq, op_eq, ops_none),
(Add, "add", Binary, BINARY_ADD_COST, 0x0a, 0, 0, opc_add, op_add, ops_none),
(Sub, "sub", Binary, BINARY_COST, 0x0b, 0, 0, opc_sub, op_sub, ops_none),
(Leu, "leu", Binary, BINARY_COST, 0x0c, 0, 0, opc_leu, op_leu, ops_none),
(Le, "le", Binary, BINARY_COST, 0x0d, 0, 0, opc_le, op_le, ops_none),
(And, "and", Binary, BINARY_COST, 0x0e, 0, 0, opc_and, op_and, ops_none),
(Or, "or", Binary, BINARY_COST, 0x0f, 0, 0, opc_or, op_or, ops_none),
(Xor, "xor", Binary, BINARY_COST, 0x10, 0, 0, opc_xor, op_xor, ops_none),
(MinuW, "minu_w", Binary, BINARY_COST, 0x12, 0, 0, opc_minu_w, op_minu_w, ops_none),
(MinW, "min_w", Binary, BINARY_COST, 0x13, 0, 0, opc_min_w, op_min_w, ops_none),
(MaxuW, "maxu_w", Binary, BINARY_COST, 0x14, 0, 0, opc_maxu_w, op_maxu_w, ops_none),
(MaxW, "max_w", Binary, BINARY_COST, 0x15, 0, 0, opc_max_w, op_max_w, ops_none),
(LtuW, "ltu_w", Binary, BINARY_COST, 0x16, 0, 0, opc_ltu_w, op_ltu_w, ops_none),
(LtW, "lt_w", Binary, BINARY_COST, 0x17, 0, 0, opc_lt_w, op_lt_w, ops_none),
(EqW, "eq_w", Binary, BINARY_COST, 0x19, 0, 0, opc_eq_w, op_eq_w, ops_none),
(AddW, "add_w", Binary, BINARY_COST, 0x1a, 0, 0, opc_add_w, op_add_w, ops_none),
(SubW, "sub_w", Binary, BINARY_COST, 0x1b, 0, 0, opc_sub_w, op_sub_w, ops_none),
(LeuW, "leu_w", Binary, BINARY_COST, 0x1c, 0, 0, opc_leu_w, op_leu_w, ops_none),
(LeW, "le_w", Binary, BINARY_COST, 0x1d, 0, 0, opc_le_w, op_le_w, ops_none),
(Sll, "sll", BinaryE, BINARY_E_COST, 0x21, 0, 0, opc_sll, op_sll, ops_none),
(Srl, "srl", BinaryE, BINARY_E_COST, 0x22, 0, 0, opc_srl, op_srl, ops_none),
(Sra, "sra", BinaryE, BINARY_E_COST, 0x23, 0, 0, opc_sra, op_sra, ops_none),
(SllW, "sll_w", BinaryE, BINARY_E_COST, 0x24, 0, 0, opc_sll_w, op_sll_w, ops_none),
(SrlW, "srl_w", BinaryE, BINARY_E_COST, 0x25, 0, 0, opc_srl_w, op_srl_w, ops_none),
(SraW, "sra_w", BinaryE, BINARY_E_COST, 0x26, 0, 0, opc_sra_w, op_sra_w, ops_none),
(SignExtendB, "signextend_b", BinaryE, BINARY_E_COST, 0x27, 0, 0, opc_signextend_b, op_signextend_b, ops_none),
(SignExtendH, "signextend_h", BinaryE, BINARY_E_COST, 0x28, 0, 0, opc_signextend_h, op_signextend_h, ops_none),
(SignExtendW, "signextend_w", BinaryE, BINARY_E_COST, 0x29, 0, 0, opc_signextend_w, op_signextend_w, ops_none),
(PubOut, "pubout", PubOut, 0, 0x30, 0, 0, opc_pubout, op_pubout, ops_none),
(Rev8, "rev8", BinaryE, BINARY_E_COST, 0x31, 0, 0, opc_rev8, op_rev8, ops_none),
(Brev8, "brev8", Binary, BINARY_COST, 0x32, 0, 0, opc_brev8, op_brev8, ops_none),
(Andn, "andn", Binary, BINARY_COST, 0x33, 0, 0, opc_andn, op_andn, ops_none),
(Orn, "orn", Binary, BINARY_COST, 0x34, 0, 0, opc_orn, op_orn, ops_none),
(Xnor, "xnor", Binary, BINARY_COST, 0x35, 0, 0, opc_xnor, op_xnor, ops_none),
(Pack, "pack", BinaryE, BINARY_E_COST, 0x36, 0, 0, opc_pack, op_pack, ops_none),
(PackH, "pack_h", BinaryE, BINARY_E_COST, 0x37, 0, 0, opc_pack_h, op_pack_h, ops_none),
(PackW, "pack_w", BinaryE, BINARY_E_COST, 0x38, 0, 0, opc_pack_w, op_pack_w, ops_none),
(Rol, "rol", BinaryE, BINARY_E_COST, 0x39, 0, 0, opc_rol, op_rol, ops_none),
(RolW, "rol_w", BinaryE, BINARY_E_COST, 0x3a, 0, 0, opc_rol_w, op_rol_w, ops_none),
(Ror, "ror", BinaryE, BINARY_E_COST, 0x3b, 0, 0, opc_ror, op_ror, ops_none),
(RorW, "ror_w", BinaryE, BINARY_E_COST, 0x3c, 0, 0, opc_ror_w, op_ror_w, ops_none),
(Clz, "clz", BinaryE, BINARY_E_COST, 0x3d, 0, 0, opc_clz, op_clz, ops_none),
(ClzW, "clz_w", BinaryE, BINARY_E_COST, 0x3e, 0, 0, opc_clz_w, op_clz_w, ops_none),
(Ctz, "ctz", BinaryE, BINARY_E_COST, 0x3f, 0, 0, opc_ctz, op_ctz, ops_none),
(CtzW, "ctz_w", BinaryE, BINARY_E_COST, 0x40, 0, 0, opc_ctz_w, op_ctz_w, ops_none),
(Cpop, "cpop", BinaryE, BINARY_E_COST, 0x41, 0, 0, opc_cpop, op_cpop, ops_none),
(CpopW, "cpop_w", BinaryE, BINARY_E_COST, 0x42, 0, 0, opc_cpop_w, op_cpop_w, ops_none),
(OrcB, "orc_b", BinaryE, BINARY_E_COST, 0x43, 0, 0, opc_orc_b, op_orc_b, ops_none),
(Bclr, "bclr", BinaryE, BINARY_E_COST, 0x44, 0, 0, opc_bclr, op_bclr, ops_none),
(Bext, "bext", BinaryE, BINARY_E_COST, 0x45, 0, 0, opc_bext, op_bext, ops_none),
(Binv, "binv", BinaryE, BINARY_E_COST, 0x46, 0, 0, opc_binv, op_binv, ops_none),
(Bset, "bset", BinaryE, BINARY_E_COST, 0x47, 0, 0, opc_bset, op_bset, ops_none),
(AddUW, "add_u_w", BinaryE, ADD_U_W_COST, 0x48, 0, 0, opc_add_u_w, op_add_u_w, ops_none),
(Sh1add, "sh1add", BinaryE, SH_ADD_COST, 0x49, 0, 0, opc_sh1add, op_sh1add, ops_none),
(Sh1addUW, "sh1add_u_w", BinaryE, SH_ADD_U_W_COST, 0x4a, 0, 0, opc_sh1add_u_w, op_sh1add_u_w, ops_none),
(Sh2add, "sh2add", BinaryE, SH_ADD_COST, 0x4b, 0, 0, opc_sh2add, op_sh2add, ops_none),
(Sh2addUW, "sh2add_u_w", BinaryE, SH_ADD_U_W_COST, 0x4c, 0, 0, opc_sh2add_u_w, op_sh2add_u_w, ops_none),
(Sh3add, "sh3add", BinaryE, SH_ADD_U_W_COST, 0x4d, 0, 0, opc_sh3add, op_sh3add, ops_none),
(Sh3addUW, "sh3add_u_w", BinaryE, SH_ADD_U_W_COST, 0x4e, 0, 0, opc_sh3add_u_w, op_sh3add_u_w, ops_none),
(SllUW, "sll_u_w", BinaryE, SLL_U_W_COST, 0x4f, 0, 0, opc_sll_u_w, op_sll_u_w, ops_none),
(Clmul, "clmul", BinaryE, BINARY_E_COST, 0x52, 0, 0, opc_clmul, op_clmul, ops_none),
(ClmulH, "clmul_h", BinaryE, BINARY_E_COST, 0x53, 0, 0, opc_clmul_h, op_clmul_h, ops_none),
(ClmulR, "clmul_r", BinaryE, BINARY_E_COST, 0x54, 0, 0, opc_clmul_r, op_clmul_r, ops_none),
(Xperm4, "xperm4", BinaryE, BINARY_E_COST, 0x55, 0, 0, opc_xperm4, op_xperm4, ops_none),
(Xperm8, "xperm8", BinaryE, BINARY_E_COST, 0x56, 0, 0, opc_xperm8, op_xperm8, ops_none),
(CzeroEqz, "czero_eqz", BinaryE, BINARY_E_COST, 0x57, 0, 0, opc_czero_eqz, op_czero_eqz, ops_none),
(CzeroNez, "czero_nez", BinaryE, BINARY_E_COST, 0x58, 0, 0, opc_czero_nez, op_czero_nez, ops_none),
(Mulu, "mulu", ArithAm32, ARITHAM32_COST, 0xb0, 0, 0, opc_mulu, op_mulu, ops_none),
(Muluh, "muluh", ArithAm32, ARITHAM32_COST, 0xb1, 0, 0, opc_muluh, op_muluh, ops_none),
(Mulsuh, "mulsuh", ArithAm32, ARITHAM32_COST, 0xb3, 0, 0, opc_mulsuh, op_mulsuh, ops_none),
(Mul, "mul", ArithAm32, ARITHAM32_COST, 0xb4, 0, 0, opc_mul, op_mul, ops_none),
(Mulh, "mulh", ArithAm32, ARITHAM32_COST, 0xb5, 0, 0, opc_mulh, op_mulh, ops_none),
(MulW, "mul_w", ArithAm32, ARITHAM32_COST, 0xb6, 0, 0, opc_mul_w, op_mul_w, ops_none),
(Divu, "divu", ArithAm32, ARITHAM32_COST, 0xb8, 0, 0, opc_divu, op_divu, ops_none),
(Remu, "remu", ArithAm32, ARITHAM32_COST, 0xb9, 0, 0, opc_remu, op_remu, ops_none),
(Div, "div", ArithAm32, ARITHAM32_COST, 0xba, 0, 0, opc_div, op_div, ops_none),
(Rem, "rem", ArithAm32, ARITHAM32_COST, 0xbb, 0, 0, opc_rem, op_rem, ops_none),
(DivuW, "divu_w", ArithA32, ARITHA32_COST, 0xbc, 0, 0, opc_divu_w, op_divu_w, ops_none),
(RemuW, "remu_w", ArithA32, ARITHA32_COST, 0xbd, 0, 0, opc_remu_w, op_remu_w, ops_none),
(DivW, "div_w", ArithA32, ARITHA32_COST, 0xbe, 0, 0, opc_div_w, op_div_w, ops_none),
(RemW, "rem_w", ArithA32, ARITHA32_COST, 0xbf, 0, 0, opc_rem_w, op_rem_w, ops_none),
(JumpDest, "jump_dest", Evm, JUMP_DEST_COST, 0xc0, 8, 0, opc_jump_dest, op_jump_dest, ops_jump_dest),
(DmaMemCpy, "dma_memcpy", Dma, DMA_MEMCPY_COST, 0xd0, 8, 0, opc_dma_memcpy, op_dma_memcpy, ops_dma_memcpy),
(DmaMemCmp, "dma_memcmp", Dma, DMA_MEMCMP_COST, 0xd1, 16, 0, opc_dma_memcmp, op_dma_memcmp, ops_dma_memcmp),
(DmaInputCpy, "dma_inputcpy", Dma, DMA_INPUTCPY_COST, 0xd2, 8, 0, opc_dma_inputcpy, op_dma_inputcpy, ops_dma_inputcpy),
(DmaXMemCpy, "dma_xmemcpy", Dma, DMA_MEMCPY_COST, 0xd6, 8, 0, opc_dma_xmemcpy, op_dma_xmemcpy, ops_dma_xmemcpy),
(DmaXMemCmp, "dma_xmemcmp", Dma, DMA_MEMCMP_COST, 0xd7, 16, 0, opc_dma_xmemcmp, op_dma_xmemcmp, ops_dma_xmemcmp),
(DmaXMemSet, "dma_xmemset", Dma, DMA_MEMSET_COST, 0xd9, 8, 0, opc_dma_xmemset, op_dma_xmemset, ops_dma_xmemset),
(Profile, "profile", Profile, 0, 0xe0, 0, 0, opc_profile, op_profile, ops_profile),
(Poseidon2, "poseidon2", Poseidon, POSEIDON_COST, 0xeb, 128, 128, opc_poseidon2, op_poseidon2, ops_poseidon2),
(Poseidon1, "poseidon1", Poseidon, POSEIDON_COST, 0xec, 128, 128, opc_poseidon1, op_poseidon1, ops_poseidon1),
(Arith384Mod, "arith384_mod", ArithEq384, ARITH_EQ_384_COST, 0xe2, 232, 48, opc_arith384_mod, op_arith384_mod, ops_arith384_mod),
(Bls12_381CurveAdd, "bls12_381_curve_add", ArithEq384, ARITH_EQ_384_COST, 0xe3, 208, 96, opc_bls12_381_curve_add, op_bls12_381_curve_add, ops_bls12_381_curve_add),
(Bls12_381CurveDbl, "bls12_381_curve_dbl", ArithEq384, ARITH_EQ_384_COST, 0xe4, 96, 96, opc_bls12_381_curve_dbl, op_bls12_381_curve_dbl, ops_bls12_381_curve_dbl),
(Bls12_381ComplexAdd, "bls12_381_complex_add", ArithEq384, ARITH_EQ_384_COST, 0xe5, 208, 96, opc_bls12_381_complex_add, op_bls12_381_complex_add, ops_bls12_381_complex_add),
(Bls12_381ComplexSub, "bls12_381_complex_sub", ArithEq384, ARITH_EQ_384_COST, 0xe6, 208, 96, opc_bls12_381_complex_sub, op_bls12_381_complex_sub, ops_bls12_381_complex_sub),
(Bls12_381ComplexMul, "bls12_381_complex_mul", ArithEq384, ARITH_EQ_384_COST, 0xe7, 208, 96, opc_bls12_381_complex_mul, op_bls12_381_complex_mul, ops_bls12_381_complex_mul),
(Add256, "add256", BigInt, ADD256_COST, 0xf0, 104, 32, opc_add256, op_add256, ops_add256),
(Keccak, "keccak", Keccak, KECCAK_COST, 0xf1, 200, 200, opc_keccak, op_keccak, ops_none),
(Arith256, "arith256", ArithEq, ARITH_EQ_COST, 0xf2, 136, 64, opc_arith256, op_arith256, ops_arith256),
(Arith256Mod, "arith256_mod", ArithEq, ARITH_EQ_COST, 0xf3, 168, 32, opc_arith256_mod, op_arith256_mod, ops_arith256_mod),
(Secp256k1Add, "secp256k1_add", ArithEq, ARITH_EQ_COST, 0xf4, 144, 64, opc_secp256k1_add, op_secp256k1_add, ops_secp256k1_add),
(Secp256k1Dbl, "secp256k1_dbl", ArithEq, ARITH_EQ_COST, 0xf5, 64, 64, opc_secp256k1_dbl, op_secp256k1_dbl, ops_secp256k1_dbl),
(Secp256r1Add, "secp256r1_add", ArithEq, ARITH_EQ_COST, 0xe8, 144, 64, opc_secp256r1_add, op_secp256r1_add, ops_secp256r1_add),
(Secp256r1Dbl, "secp256r1_dbl", ArithEq, ARITH_EQ_COST, 0xe9, 64, 64, opc_secp256r1_dbl, op_secp256r1_dbl, ops_secp256r1_dbl),
(Blake2, "blake2", Blake2, BLAKE2_COST, 0xea, 280 , 128, opc_blake2, op_blake2, ops_blake2),
(FcallParam, "fcall_param", Fcall, FCALL_COST, 0xf6, 0, 0, opc_fcall_param, op_fcall_param, ops_none),
(Fcall, "fcall", Fcall, FCALL_COST, 0xf7, 0, 0, opc_fcall, op_fcall, ops_none),
(FcallGet, "fcall_get", Fcall, FCALL_COST, 0xf8, 0, 0, opc_fcall_get, op_fcall_get, ops_none),
(Sha256, "sha256", Sha256, SHA256_COST, 0xf9, 112, 112, opc_sha256, op_sha256, ops_sha256),
(Bn254CurveAdd, "bn254_curve_add", ArithEq, ARITH_EQ_COST, 0xfa, 144, 64, opc_bn254_curve_add, op_bn254_curve_add, ops_bn254_curve_add),
(Bn254CurveDbl, "bn254_curve_dbl", ArithEq, ARITH_EQ_COST, 0xfb, 64, 64, opc_bn254_curve_dbl, op_bn254_curve_dbl, ops_bn254_curve_dbl),
(Bn254ComplexAdd, "bn254_complex_add", ArithEq, ARITH_EQ_COST, 0xfc, 144, 64, opc_bn254_complex_add, op_bn254_complex_add, ops_bn254_complex_add),
(Bn254ComplexSub, "bn254_complex_sub", ArithEq, ARITH_EQ_COST, 0xfd, 144, 64, opc_bn254_complex_sub, op_bn254_complex_sub, ops_bn254_complex_sub),
(Bn254ComplexMul, "bn254_complex_mul", ArithEq, ARITH_EQ_COST, 0xfe, 144, 64, opc_bn254_complex_mul, op_bn254_complex_mul, ops_bn254_complex_mul),
(Halt, "halt", Internal, INTERNAL_COST, 0xff, 144, 0, opc_halt, op_halt, ops_none),
}
#[inline(always)]
pub fn opc_keccak(ctx: &mut InstContext) {
let address = ctx.b;
if address & 0x7 != 0 {
panic!("opc_keccak() found address not aligned to 8 bytes");
}
const WORDS: usize = 25;
let mut data = [0u64; WORDS];
let cache_index = ctx.keccakf_cache.take_pending_index();
match ctx.emulation_mode {
EmulationMode::Mem => {
for (i, d) in data.iter_mut().enumerate() {
*d = ctx.mem.read(address + (8 * i as u64), 8);
}
if let Some(index) = cache_index {
ctx.keccakf_cache.store(&data, index);
}
keccakf(&mut data);
for (i, d) in data.iter().enumerate() {
ctx.mem.write(address + (8 * i as u64), *d, 8);
}
}
EmulationMode::GenerateMemReads => {
for (i, d) in data.iter_mut().enumerate() {
*d = ctx.mem.read(address + (8 * i as u64), 8);
}
ctx.precompiled.input_data.clear();
for (i, d) in data.iter_mut().enumerate() {
ctx.precompiled.input_data.push(*d);
}
if let Some(index) = cache_index {
ctx.keccakf_cache.store(&data, index);
}
keccakf(&mut data);
for (i, d) in data.iter().enumerate() {
ctx.mem.write(address + (8 * i as u64), *d, 8);
}
ctx.precompiled.output_data.clear();
for (i, d) in data.iter_mut().enumerate() {
ctx.precompiled.output_data.push(*d);
}
}
EmulationMode::ConsumeMemReads => {
if ctx.precompiled.input_data.len() != WORDS {
panic!(
"opc_keccak() found ctx.precompiled.input_data.len={} != {}",
ctx.precompiled.input_data.len(),
WORDS
);
}
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_keccak(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_keccak() is not implemented");
}
#[inline(always)]
pub fn ops_keccak(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_direct_data(ctx, stats, 25, 25);
}
#[inline(always)]
pub fn opc_sha256(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 4 + 4;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 4, 4, None, &mut data, "sha256");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (ind, rest) = data.split_at_mut(2);
let (state_slice, input_slice) = rest.split_at_mut(4);
let state: &mut [u64; 4] = state_slice.try_into().unwrap();
let input: &[u64; 8] = input_slice[..8].try_into().unwrap();
sha256f(state, input);
for (i, d) in state.iter().enumerate() {
ctx.mem.write(ind[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_sha256(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_sha256() is not implemented");
}
#[inline(always)]
pub fn ops_sha256(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[4, 8], &[], 1);
}
#[inline(always)]
pub fn opc_poseidon2(ctx: &mut InstContext) {
let address = ctx.b;
if address & 0x7 != 0 {
panic!("opc_poseidon2() found address not aligned to 8 bytes");
}
const WORDS: usize = 16;
let mut data = [0u64; WORDS];
match ctx.emulation_mode {
EmulationMode::Mem => {
for (i, d) in data.iter_mut().enumerate() {
*d = ctx.mem.read(address + (8 * i as u64), 8);
}
let data_gl = data.map(Goldilocks::new);
let res_gl = poseidon2_hash::<Goldilocks, Poseidon2_16, 16>(&data_gl);
for (i, d) in data.iter_mut().enumerate() {
*d = res_gl[i].as_canonical_u64();
}
for (i, d) in data.iter().enumerate() {
ctx.mem.write(address + (8 * i as u64), *d, 8);
}
}
EmulationMode::GenerateMemReads => {
for (i, d) in data.iter_mut().enumerate() {
*d = ctx.mem.read(address + (8 * i as u64), 8);
}
ctx.precompiled.input_data.clear();
for (i, d) in data.iter_mut().enumerate() {
ctx.precompiled.input_data.push(*d);
}
let data_gl = data.map(Goldilocks::new);
let res_gl = poseidon2_hash::<Goldilocks, Poseidon2_16, 16>(&data_gl);
for (i, d) in data.iter_mut().enumerate() {
*d = res_gl[i].as_canonical_u64();
}
for (i, d) in data.iter().enumerate() {
ctx.mem.write(address + (8 * i as u64), *d, 8);
}
ctx.precompiled.output_data.clear();
for (i, d) in data.iter_mut().enumerate() {
ctx.precompiled.output_data.push(*d);
}
}
EmulationMode::ConsumeMemReads => {
if ctx.precompiled.input_data.len() != WORDS {
panic!(
"opc_poseidon2() found ctx.precompiled.input_data.len={} != {}",
ctx.precompiled.input_data.len(),
WORDS
);
}
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_poseidon2(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_poseidon2() is not implemented");
}
#[inline(always)]
pub fn ops_poseidon2(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_direct_data(ctx, stats, 16, 16);
}
#[inline(always)]
pub fn opc_poseidon1(ctx: &mut InstContext) {
let address = ctx.b;
if address & 0x7 != 0 {
panic!("opc_poseidon1() found address not aligned to 8 bytes");
}
const WORDS: usize = 16;
let mut data = [0u64; WORDS];
match ctx.emulation_mode {
EmulationMode::Mem => {
for (i, d) in data.iter_mut().enumerate() {
*d = ctx.mem.read(address + (8 * i as u64), 8);
}
let data_gl = data.map(Goldilocks::new);
let res_gl = poseidon1_hash::<Goldilocks, Poseidon1_16, 16>(&data_gl);
for (i, d) in data.iter_mut().enumerate() {
*d = res_gl[i].as_canonical_u64();
}
for (i, d) in data.iter().enumerate() {
ctx.mem.write(address + (8 * i as u64), *d, 8);
}
}
EmulationMode::GenerateMemReads => {
for (i, d) in data.iter_mut().enumerate() {
*d = ctx.mem.read(address + (8 * i as u64), 8);
}
ctx.precompiled.input_data.clear();
for (i, d) in data.iter_mut().enumerate() {
ctx.precompiled.input_data.push(*d);
}
let data_gl = data.map(Goldilocks::new);
let res_gl = poseidon1_hash::<Goldilocks, Poseidon1_16, 16>(&data_gl);
for (i, d) in data.iter_mut().enumerate() {
*d = res_gl[i].as_canonical_u64();
}
for (i, d) in data.iter().enumerate() {
ctx.mem.write(address + (8 * i as u64), *d, 8);
}
ctx.precompiled.output_data.clear();
for (i, d) in data.iter_mut().enumerate() {
ctx.precompiled.output_data.push(*d);
}
}
EmulationMode::ConsumeMemReads => {
if ctx.precompiled.input_data.len() != WORDS {
panic!(
"opc_poseidon1() found ctx.precompiled.input_data.len={} != {}",
ctx.precompiled.input_data.len(),
WORDS
);
}
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_poseidon1(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_poseidon1() is not implemented");
}
#[inline(always)]
pub fn ops_poseidon1(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_direct_data(ctx, stats, 16, 16);
}
#[inline(always)]
pub fn opc_blake2(ctx: &mut InstContext) {
const WORDS: usize = 3 + 2 * 16; let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 3, 2, 16, 0, Some(0), &mut data, "blake2");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let index = data[0];
let (params, rest) = data.split_at_mut(3);
let (state_slice, input_slice) = rest.split_at_mut(16);
let state: &mut [u64; 16] = state_slice.try_into().unwrap();
let input: &[u64; 16] = input_slice[..16].try_into().unwrap();
blake2br(index, state, input);
let state_addr = params[1];
for (i, d) in state.iter().enumerate() {
ctx.mem.write(state_addr + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_blake2(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_blake2() is not implemented");
}
#[inline(always)]
pub fn ops_blake2(ctx: &InstContext, stats: &mut dyn OpStats) {
let param_addr = ctx.b;
stats.mem_align_read(param_addr, 3);
let state_addr = ctx.mem.read(param_addr + 8, 8);
let input_addr = ctx.mem.read(param_addr + 16, 8);
stats.mem_align_read(state_addr, 16);
stats.mem_align_read(input_addr, 16);
stats.mem_align_write(state_addr, 16);
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
pub fn precompiled_load_data(
ctx: &mut InstContext,
params_count: usize,
load_indirections: usize,
load_chunks: usize,
load_rem: usize,
direct_load_param_idx: Option<usize>,
data: &mut [u64],
title: &str,
) {
internal_precompiled_load_data(
ctx,
params_count,
load_indirections,
load_chunks,
load_rem,
0,
direct_load_param_idx,
data,
title,
);
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
pub fn precompiled_load_data_with_result(
ctx: &mut InstContext,
params_count: usize,
load_indirections: usize,
load_chunks: usize,
load_rem: usize,
direct_load_param_idx: Option<usize>,
data: &mut [u64],
title: &str,
) {
internal_precompiled_load_data(
ctx,
params_count,
load_indirections,
load_chunks,
load_rem,
1,
direct_load_param_idx,
data,
title,
);
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
fn internal_precompiled_load_data(
ctx: &mut InstContext,
params_count: usize,
load_indirections: usize,
load_chunks: usize,
load_rem: usize,
result: usize,
direct_load_param_idx: Option<usize>, data: &mut [u64],
title: &str,
) {
let address = ctx.b;
if address & 0x7 != 0 {
panic!(
"[{title}] precompiled_check_address() found address 0x{address:08X} not aligned \
to 8 bytes at PC:0x{:08X} STEP:{}",
ctx.pc, ctx.step
);
}
if let EmulationMode::ConsumeMemReads = ctx.emulation_mode {
let expected_len = params_count + load_indirections * load_chunks + load_rem + result;
if ctx.precompiled.input_data.len() != expected_len {
panic!(
"[{title}] ctx.precompiled.input_data.len={} != {expected_len} \
[{params_count}+{load_indirections}*{load_chunks}+{load_rem}+{result}] at PC:0x{:08X} STEP:{}",
ctx.precompiled.input_data.len(), ctx.pc, ctx.step,
);
}
for (i, d) in data.iter_mut().enumerate() {
*d = ctx.precompiled.input_data[i];
}
return;
}
for (i, data) in data.iter_mut().enumerate().take(params_count) {
let indirection = ctx.mem.read(address + (8 * i as u64), 8);
if indirection & 0x7 != 0 && Some(i) != direct_load_param_idx {
panic!(
"[{title}] precompiled_check_address() found address_{i} [0x{address:08X}]=0x{indirection:08X} \
not aligned to 8 bytes at PC:0x{:08X} STEP:{}",
ctx.pc, ctx.step
);
}
*data = indirection;
}
let mut data_offset = params_count;
for i in 0..load_indirections {
let param_idx = if let Some(direct_idx) = direct_load_param_idx {
if i >= direct_idx {
i + 1
} else {
i
}
} else {
i
};
let data_offset = i * load_chunks + data_offset;
let param_address =
if params_count == 0 { address + data_offset as u64 } else { data[param_idx] };
for j in 0..load_chunks {
let addr = param_address + (8 * j as u64);
data[data_offset + j] = ctx.mem.read(addr, 8);
}
}
if load_rem > 0 {
data_offset += (load_indirections - 1) * load_chunks;
let param_address = if params_count == 0 {
address + data_offset as u64
} else {
data[load_indirections - 1]
};
for j in load_chunks..load_chunks + load_rem {
let addr = param_address + (8 * j as u64);
data[data_offset + j] = ctx.mem.read(addr, 8);
}
}
if let EmulationMode::GenerateMemReads = ctx.emulation_mode {
ctx.precompiled.input_data.clear();
for (i, d) in data.iter_mut().enumerate() {
ctx.precompiled.input_data.push(*d);
}
ctx.precompiled.step = ctx.step;
}
}
#[inline(always)]
pub fn precompiled_stats_data(
ctx: &InstContext,
stats: &mut dyn OpStats,
inputs: &[u32],
outputs: &[u32],
inputs_reduce_count: usize,
) {
let param_addr = ctx.b;
stats.mem_align_read(param_addr, inputs.len() + outputs.len());
for (index, count) in inputs.iter().enumerate() {
if *count == 0 {
continue;
}
let input_addr = ctx.mem.read(param_addr + (8 * index as u64), 8);
stats.mem_align_read(input_addr, *count as usize);
if index < inputs_reduce_count {
stats.mem_align_write(input_addr, *count as usize);
}
}
let index_offset = inputs.len();
for (index, count) in outputs.iter().enumerate() {
if *count == 0 {
continue;
}
let output_addr = ctx.mem.read(param_addr + (8 * (index + index_offset) as u64), 8);
stats.mem_align_write(output_addr, *count as usize);
}
}
#[inline(always)]
pub fn precompiled_stats_direct_data(
ctx: &InstContext,
stats: &mut dyn OpStats,
inputs: usize,
outputs: usize,
) {
let param_addr = ctx.b;
stats.mem_align_read(param_addr, inputs);
stats.mem_align_write(param_addr, outputs);
}
pub fn opc_add256(ctx: &mut InstContext) {
const WORDS: usize = 4 + 1 + 2 * 4;
let mut data = [0u64; WORDS];
precompiled_load_data_with_result(ctx, 4, 2, 4, 0, Some(2), &mut data, "add256");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let cin = data[2];
let (params, rest) = data.split_at(4); let (a, rest) = rest.split_at(4);
let (b, _) = rest.split_at(4);
let a: &[u64; 4] = a.try_into().expect("opc_add256: a.len != 4");
let b: &[u64; 4] = b.try_into().expect("opc_add256: b.len != 4");
let mut c = [0u64; 4];
let cout = zisk_precomp_helpers::add256(a, b, cin, &mut c);
let c_addr = params[3];
for (i, c_item) in c.iter().enumerate() {
ctx.mem.write(c_addr + (8 * i as u64), *c_item, 8);
}
if let EmulationMode::GenerateMemReads = ctx.emulation_mode {
ctx.precompiled.input_data[4 + 2 * 4] = cout;
}
ctx.c = cout;
ctx.flag = false;
} else {
assert!(data[4 + 2 * 4] <= 1, "opc_add256: cout > 1");
ctx.c = data[4 + 2 * 4];
ctx.flag = false;
}
}
#[inline(always)]
pub fn op_add256(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_add256() is not implemented");
}
#[inline(always)]
pub fn ops_add256(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[4, 4, 0], &[4], 0);
}
#[inline(always)]
pub fn opc_arith256(ctx: &mut InstContext) {
const WORDS: usize = 5 + 3 * 4;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 5, 3, 4, 0, None, &mut data, "arith256");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(5);
let (a, rest) = rest.split_at(4);
let (b, c) = rest.split_at(4);
let a: &[u64; 4] = a.try_into().expect("opc_arith256: a.len != 4");
let b: &[u64; 4] = b.try_into().expect("opc_arith256: b.len != 4");
let c: &[u64; 4] = c.try_into().expect("opc_arith256: c.len != 4");
let mut dl = [0u64; 4];
let mut dh = [0u64; 4];
zisk_precomp_helpers::arith256(a, b, c, &mut dl, &mut dh);
for (i, dl_item) in dl.iter().enumerate() {
ctx.mem.write(data[3] + (8 * i as u64), *dl_item, 8);
}
for (i, dh_item) in dh.iter().enumerate() {
ctx.mem.write(data[4] + (8 * i as u64), *dh_item, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_arith256(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_arith256() is not implemented");
}
#[inline(always)]
pub fn ops_arith256(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[4, 4, 4], &[4, 4], 0);
}
#[inline(always)]
pub fn opc_arith256_mod(ctx: &mut InstContext) {
const WORDS: usize = 5 + 4 * 4;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 5, 4, 4, 0, None, &mut data, "arith256_mod");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(5);
let (a, rest) = rest.split_at(4);
let (b, rest) = rest.split_at(4);
let (c, module) = rest.split_at(4);
let mut d = [0u64; 4];
let a: &[u64; 4] = a.try_into().expect("opc_arith256_mod: a.len != 4");
let b: &[u64; 4] = b.try_into().expect("opc_arith256_mod: b.len != 4");
let c: &[u64; 4] = c.try_into().expect("opc_arith256_mod: c.len != 4");
let module: &[u64; 4] = module.try_into().expect("opc_arith256_mod: module.len != 4");
let mut d = [0u64; 4];
zisk_precomp_helpers::arith256_mod(a, b, c, module, &mut d);
for (i, d) in d.iter().enumerate() {
ctx.mem.write(data[4] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_arith256_mod(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_arith256_mod() is not implemented");
}
#[inline(always)]
pub fn ops_arith256_mod(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[4, 4, 4, 4], &[4], 0);
}
#[inline(always)]
pub fn opc_secp256k1_add(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 8;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 8, 0, None, &mut data, "secp256k1_add");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (p1, p2) = rest.split_at(8);
let p1: &[u64; 8] = p1.try_into().expect("opc_secp256k1_add: p1.len != 8");
let p2: &[u64; 8] = p2.try_into().expect("opc_secp256k1_add: p2.len != 8");
let mut p3 = [0u64; 8];
zisk_precomp_helpers::secp256k1_add(p1, p2, &mut p3);
for (i, d) in p3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_secp256k1_add(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_secp256k1_add() is not implemented");
}
#[inline(always)]
pub fn ops_secp256k1_add(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[8, 8], &[], 1);
}
#[inline(always)]
pub fn opc_secp256k1_dbl(ctx: &mut InstContext) {
const WORDS: usize = 8; let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 0, 1, 8, 0, None, &mut data, "secp256k1_dbl");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let p1: &[u64; 8] = &data;
let mut p3 = [0u64; 8];
zisk_precomp_helpers::secp256k1_dbl(p1, &mut p3);
for (i, d) in p3.iter().enumerate() {
ctx.mem.write(ctx.b + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_secp256k1_dbl(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_secp256k1_dbl() is not implemented");
}
#[inline(always)]
pub fn ops_secp256k1_dbl(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_direct_data(ctx, stats, 8, 8);
}
#[inline(always)]
pub fn opc_secp256r1_add(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 8;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 8, 0, None, &mut data, "secp256r1_add");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (p1, p2) = rest.split_at(8);
let p1: &[u64; 8] = p1.try_into().expect("opc_secp256r1_add: p1.len != 8");
let p2: &[u64; 8] = p2.try_into().expect("opc_secp256r1_add: p2.len != 8");
let mut p3 = [0u64; 8];
zisk_precomp_helpers::secp256r1_add(p1, p2, &mut p3);
for (i, d) in p3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_secp256r1_add(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_secp256r1_add() is not implemented");
}
#[inline(always)]
pub fn ops_secp256r1_add(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[8, 8], &[], 1);
}
#[inline(always)]
pub fn opc_secp256r1_dbl(ctx: &mut InstContext) {
const WORDS: usize = 8; let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 0, 1, 8, 0, None, &mut data, "secp256r1_dbl");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let p1: &[u64; 8] = &data;
let mut p3 = [0u64; 8];
zisk_precomp_helpers::secp256r1_dbl(p1, &mut p3);
for (i, d) in p3.iter().enumerate() {
ctx.mem.write(ctx.b + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_secp256r1_dbl(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_secp256r1_dbl() is not implemented");
}
#[inline(always)]
pub fn ops_secp256r1_dbl(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_direct_data(ctx, stats, 8, 8);
}
#[inline(always)]
pub fn opc_bn254_curve_add(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 8;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 8, 0, None, &mut data, "bn254_curve_add");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (p1, p2) = rest.split_at(8);
let p1: &[u64; 8] = p1.try_into().expect("opc_bn254_curve_add: p1.len != 8");
let p2: &[u64; 8] = p2.try_into().expect("opc_bn254_curve_add: p2.len != 8");
let mut p3 = [0u64; 8];
zisk_precomp_helpers::bn254_curve_add(p1, p2, &mut p3);
for (i, d) in p3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bn254_curve_add(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bn254_curve_add() is not implemented");
}
#[inline(always)]
pub fn ops_bn254_curve_add(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[8, 8], &[], 1);
}
#[inline(always)]
pub fn opc_bn254_curve_dbl(ctx: &mut InstContext) {
const WORDS: usize = 8; let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 0, 1, 8, 0, None, &mut data, "bn254_curve_dbl");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let p1: &[u64; 8] = &data;
let mut p3 = [0u64; 8];
zisk_precomp_helpers::bn254_curve_dbl(p1, &mut p3);
for (i, d) in p3.iter().enumerate() {
ctx.mem.write(ctx.b + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bn254_curve_dbl(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bn254_curve_dbl() is not implemented");
}
#[inline(always)]
pub fn ops_bn254_curve_dbl(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_direct_data(ctx, stats, 8, 8);
}
#[inline(always)]
pub fn opc_bn254_complex_add(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 8;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 8, 0, None, &mut data, "bn254_complex_add");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (f1, f2) = rest.split_at(8);
let f1: &[u64; 8] = f1.try_into().expect("opc_bn254_complex_add: f1.len != 8");
let f2: &[u64; 8] = f2.try_into().expect("opc_bn254_complex_add: f2.len != 8");
let mut f3 = [0u64; 8];
zisk_precomp_helpers::bn254_complex_add(f1, f2, &mut f3);
for (i, d) in f3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bn254_complex_add(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bn254_complex_add() is not implemented");
}
#[inline(always)]
pub fn ops_bn254_complex_add(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[8, 8], &[], 1);
}
#[inline(always)]
pub fn opc_bn254_complex_sub(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 8;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 8, 0, None, &mut data, "bn254_complex_sub");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (f1, f2) = rest.split_at(8);
let f1: &[u64; 8] = f1.try_into().expect("opc_bn254_complex_sub: f1.len != 8");
let f2: &[u64; 8] = f2.try_into().expect("opc_bn254_complex_sub: f2.len != 8");
let mut f3 = [0u64; 8];
zisk_precomp_helpers::bn254_complex_sub(f1, f2, &mut f3);
for (i, d) in f3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bn254_complex_sub(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bn254_complex_sub() is not implemented");
}
#[inline(always)]
pub fn ops_bn254_complex_sub(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[8, 8], &[], 1);
}
#[inline(always)]
pub fn opc_bn254_complex_mul(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 8;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 8, 0, None, &mut data, "bn254_complex_mul");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (f1, f2) = rest.split_at(8);
let f1: &[u64; 8] = f1.try_into().expect("opc_bn254_complex_mul: f1.len != 8");
let f2: &[u64; 8] = f2.try_into().expect("opc_bn254_complex_mul: f2.len != 8");
let mut f3 = [0u64; 8];
zisk_precomp_helpers::bn254_complex_mul(f1, f2, &mut f3);
for (i, d) in f3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bn254_complex_mul(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bn254_complex_mul() is not implemented");
}
#[inline(always)]
pub fn ops_bn254_complex_mul(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[8, 8], &[], 1);
}
#[inline(always)]
pub fn opc_arith384_mod(ctx: &mut InstContext) {
const WORDS: usize = 5 + 4 * 6;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 5, 4, 6, 0, None, &mut data, "arith384_mod");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(5);
let (a, rest) = rest.split_at(6);
let (b, rest) = rest.split_at(6);
let (c, module) = rest.split_at(6);
let mut d = [0u64; 6];
let a: &[u64; 6] = a.try_into().expect("opc_arith384_mod: a.len != 6");
let b: &[u64; 6] = b.try_into().expect("opc_arith384_mod: b.len != 6");
let c: &[u64; 6] = c.try_into().expect("opc_arith384_mod: c.len != 6");
let module: &[u64; 6] = module.try_into().expect("opc_arith384_mod: module.len != 6");
let mut d = [0u64; 6];
zisk_precomp_helpers::arith384_mod(a, b, c, module, &mut d);
for (i, d) in d.iter().enumerate() {
ctx.mem.write(data[4] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_arith384_mod(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_arith384_mod() is not implemented");
}
#[inline(always)]
pub fn ops_arith384_mod(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[6, 6, 6, 6], &[6], 0);
}
#[inline(always)]
pub fn opc_bls12_381_curve_add(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 12;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 12, 0, None, &mut data, "bls12_381_curve_add");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (p1, p2) = rest.split_at(12);
let p1: &[u64; 12] = p1.try_into().expect("opc_bls12_381_curve_add: p1.len != 12");
let p2: &[u64; 12] = p2.try_into().expect("opc_bls12_381_curve_add: p2.len != 12");
let mut p3 = [0u64; 12];
zisk_precomp_helpers::bls12_381_curve_add(p1, p2, &mut p3);
for (i, d) in p3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bls12_381_curve_add(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bls12_381_curve_add() is not implemented");
}
#[inline(always)]
pub fn ops_bls12_381_curve_add(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[12, 12], &[], 1);
}
#[inline(always)]
pub fn opc_bls12_381_curve_dbl(ctx: &mut InstContext) {
const WORDS: usize = 12;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 0, 1, 12, 0, None, &mut data, "bls12_381_curve_dbl");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let p1: &[u64; 12] = &data;
let mut p3 = [0u64; 12];
zisk_precomp_helpers::bls12_381_curve_dbl(p1, &mut p3);
for (i, d) in p3.iter().enumerate() {
ctx.mem.write(ctx.b + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bls12_381_curve_dbl(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bls12_381_curve_dbl() is not implemented");
}
#[inline(always)]
pub fn ops_bls12_381_curve_dbl(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_direct_data(ctx, stats, 12, 12);
}
#[inline(always)]
pub fn opc_bls12_381_complex_add(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 12;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 12, 0, None, &mut data, "bls12_381_complex_add");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (f1, f2) = rest.split_at(12);
let f1: &[u64; 12] = f1.try_into().expect("opc_bls12_381_complex_add: f1.len != 12");
let f2: &[u64; 12] = f2.try_into().expect("opc_bls12_381_complex_add: f2.len != 12");
let mut f3 = [0u64; 12];
zisk_precomp_helpers::bls12_381_complex_add(f1, f2, &mut f3);
for (i, d) in f3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bls12_381_complex_add(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bls12_381_complex_add() is not implemented");
}
#[inline(always)]
pub fn ops_bls12_381_complex_add(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[12, 12], &[], 1);
}
#[inline(always)]
pub fn opc_bls12_381_complex_sub(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 12;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 12, 0, None, &mut data, "bls12_381_complex_sub");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (f1, f2) = rest.split_at(12);
let f1: &[u64; 12] = f1.try_into().expect("opc_bls12_381_complex_sub: f1.len != 12");
let f2: &[u64; 12] = f2.try_into().expect("opc_bls12_381_complex_sub: f2.len != 12");
let mut f3 = [0u64; 12];
zisk_precomp_helpers::bls12_381_complex_sub(f1, f2, &mut f3);
for (i, d) in f3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bls12_381_complex_sub(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bls12_381_complex_sub() is not implemented");
}
#[inline(always)]
pub fn ops_bls12_381_complex_sub(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[12, 12], &[], 1);
}
#[inline(always)]
pub fn opc_bls12_381_complex_mul(ctx: &mut InstContext) {
const WORDS: usize = 2 + 2 * 12;
let mut data = [0u64; WORDS];
precompiled_load_data(ctx, 2, 2, 12, 0, None, &mut data, "bls12_381_complex_mul");
if ctx.emulation_mode != EmulationMode::ConsumeMemReads {
let (_, rest) = data.split_at(2);
let (f1, f2) = rest.split_at(12);
let f1: &[u64; 12] = f1.try_into().expect("opc_bls12_381_complex_mul: f1.len != 12");
let f2: &[u64; 12] = f2.try_into().expect("opc_bls12_381_complex_mul: f2.len != 12");
let mut f3 = [0u64; 12];
zisk_precomp_helpers::bls12_381_complex_mul(f1, f2, &mut f3);
for (i, d) in f3.iter().enumerate() {
ctx.mem.write(data[0] + (8 * i as u64), *d, 8);
}
}
ctx.c = 0;
ctx.flag = false;
}
#[inline(always)]
pub fn op_bls12_381_complex_mul(_a: u64, _b: u64) -> (u64, bool) {
unimplemented!("op_bls12_381_complex_mul() is not implemented");
}
pub fn ops_bls12_381_complex_mul(ctx: &InstContext, stats: &mut dyn OpStats) {
precompiled_stats_data(ctx, stats, &[12, 12], &[], 1);
}
impl From<ZiskRequiredOperation> for ZiskOp {
fn from(value: ZiskRequiredOperation) -> Self {
ZiskOp::try_from_code(value.opcode).unwrap()
}
}
#[inline(always)]
pub const fn op_pubout(a: u64, b: u64) -> (u64, bool) {
(b, false)
}
#[inline(always)]
pub fn opc_pubout(ctx: &mut InstContext) {
(ctx.c, ctx.flag) = op_pubout(ctx.a, ctx.b);
}
#[inline(always)]
pub fn op_fcall_param(a: u64, b: u64) -> (u64, bool) {
unimplemented!("op_fcall_param() is not implemented");
}
#[inline(always)]
pub fn opc_fcall_param(ctx: &mut InstContext) {
ctx.c = ctx.b;
ctx.flag = false;
if ctx.emulation_mode == EmulationMode::ConsumeMemReads {
return;
}
let words = ctx.a;
let param = ctx.b;
if (ctx.fcall.parameters_size + words) as usize > FCALL_PARAMS_MAX_SIZE {
panic!(
"opc_fcall_param({0}) called with ctx.fcall.parameters_size({1}) + param({0})>{2}",
words, ctx.fcall.parameters_size, FCALL_PARAMS_MAX_SIZE
);
}
if words == 1 {
ctx.fcall.parameters[ctx.fcall.parameters_size as usize] = param;
ctx.fcall.parameters_size += 1;
} else {
let addr = param;
for i in 0..words {
let value = ctx.mem.read(addr + i * 8, 8);
ctx.fcall.parameters[(ctx.fcall.parameters_size + i) as usize] = value;
}
ctx.fcall.parameters_size += words;
}
}
#[inline(always)]
pub fn op_fcall(a: u64, b: u64) -> (u64, bool) {
unimplemented!("op_fcall() is not implemented");
}
#[inline(always)]
pub fn opc_fcall(ctx: &mut InstContext) {
ctx.c = ctx.b;
ctx.flag = false;
if ctx.emulation_mode == EmulationMode::ConsumeMemReads {
return;
}
let function_id = ctx.a;
let iresult = if function_id == FCALL_INPUT_READY_ID as u64 {
let required_address = ctx.fcall.parameters[0];
if required_address < INPUT_ADDR {
panic!(
"opc_fcall() FCALL_INPUT_READY_ID called with required_address {:#x} < {:#x}",
required_address, INPUT_ADDR
);
}
if required_address >= INPUT_ADDR + MAX_INPUT_SIZE - 1 {
panic!(
"opc_fcall() FCALL_INPUT_READY_ID called with required_address {:#x} > {:#x}",
required_address,
INPUT_ADDR + MAX_INPUT_SIZE - 1
);
}
let required_bytes = required_address - INPUT_ADDR - 8 + 1; if required_bytes > ctx.input_len {
panic!(
"opc_fcall() FCALL_INPUT_READY_ID called with required_address {:#x} requiring {} bytes, but only {} bytes available",
required_address,
required_bytes,
ctx.input_len
);
}
0
} else if function_id == FCALL_SET_KECCAKF_CACHE_INDEX_ID as u64 {
if ctx.fcall.parameters_size != 1 {
panic!(
"opc_fcall() FCALL_SET_KECCAKF_CACHE_INDEX_ID called with parameters_size={} != 1",
ctx.fcall.parameters_size
);
}
ctx.keccakf_cache.set_pending_index(ctx.fcall.parameters[0]);
0
} else if function_id == FCALL_GET_KECCAKF_CACHE_INDEX_ID as u64 {
if ctx.fcall.parameters_size != KECCAKF_STATE_WORDS as u64 {
panic!(
"opc_fcall() FCALL_GET_KECCAKF_CACHE_INDEX_ID called with parameters_size={} != {}",
ctx.fcall.parameters_size, KECCAKF_STATE_WORDS
);
}
let index = ctx.keccakf_cache.get(&ctx.fcall.parameters[..KECCAKF_STATE_WORDS]);
ctx.fcall.result[0] = index;
1
} else {
fcall_proxy(function_id, &ctx.fcall.parameters, &mut ctx.fcall.result)
};
if iresult < 0 {
panic!("opc_fcall() failed calling Fcall() function_id={function_id} iresult={iresult}");
}
if (iresult > 0) {
ctx.mem.free_input = ctx.fcall.result[0];
} else {
ctx.mem.free_input = 0;
}
ctx.fcall.result_got = 1;
ctx.fcall.result_size = iresult as u64;
ctx.fcall.parameters_size = 0;
}
#[inline(always)]
pub fn op_fcall_get(a: u64, b: u64) -> (u64, bool) {
unimplemented!("op_fcall_get() is not implemented");
}
#[inline(always)]
pub fn opc_fcall_get(ctx: &mut InstContext) {
ctx.c = ctx.b;
ctx.flag = false;
if ctx.emulation_mode == EmulationMode::ConsumeMemReads {
return;
}
if ctx.fcall.result_size == 0 {
panic!("opc_fcall_get() called with ctx.fcall.result_size==0");
}
if ctx.fcall.result_size as usize > FCALL_RESULT_MAX_SIZE {
panic!(
"opc_fcall_get() called with ctx.fcall.result_size=={}>{}",
ctx.fcall.result_size, FCALL_RESULT_MAX_SIZE
);
}
if ctx.fcall.result_got > ctx.fcall.result_size {
panic!(
"opc_fcall_get() called with ctx.fcall.result_got({}) >= ctx.fcall.result_size {}",
ctx.fcall.result_got, ctx.fcall.result_size
);
}
if ctx.fcall.result_got >= ctx.fcall.result_size {
ctx.mem.free_input = 0;
} else {
ctx.mem.free_input = ctx.fcall.result[ctx.fcall.result_got as usize];
}
ctx.fcall.result_got += 1;
ctx.flag = false;
}
#[inline(always)]
pub fn op_halt(a: u64, b: u64) -> (u64, bool) {
unimplemented!("op_halt() is not implemented");
}
#[inline(always)]
pub fn opc_halt(ctx: &mut InstContext) {
ctx.error = true;
ctx.c = 0;
ctx.flag = false;
}