use zkevm_opcode_defs::system_params::STORAGE_AUX_BYTE;
use crate::base_structures::{
log_query::{self, LogQuery},
register::VMRegister,
vm_state::FULL_SPONGE_QUEUE_STATE_WIDTH,
};
use boojum::gadgets::{u160::UInt160, u256::UInt256};
use super::*;
use crate::base_structures::decommit_query::DecommitQuery;
use crate::base_structures::decommit_query::DecommitQueryWitness;
use crate::base_structures::vm_state::saved_context::ExecutionContextRecord;
use crate::base_structures::vm_state::saved_context::ExecutionContextRecordWitness;
use crate::base_structures::vm_state::GlobalContext;
use crate::base_structures::vm_state::QUEUE_STATE_WIDTH;
use crate::main_vm::opcodes::call_ret_impl::far_call::log_query::LogQueryWitness;
use crate::main_vm::state_diffs::MAX_SPONGES_PER_CYCLE;
use crate::main_vm::witness_oracle::SynchronizedWitnessOracle;
use crate::main_vm::witness_oracle::WitnessOracle;
use crate::tables::CallCostsAndStipendsTable;
use arrayvec::ArrayVec;
use boojum::algebraic_props::round_function::AlgebraicRoundFunction;
use boojum::cs::traits::cs::DstBuffer;
use boojum::gadgets::traits::allocatable::CSAllocatable;
use boojum::gadgets::traits::allocatable::CSAllocatableExt;
use boojum::gadgets::traits::round_function::CircuitRoundFunction;
const FORCED_ERGS_FOR_MSG_VALUE_SIMUALTOR: bool = false;
pub(crate) struct FarCallData<F: SmallField> {
pub(crate) apply_far_call: Boolean<F>,
pub(crate) old_context: ExecutionContextRecord<F>,
pub(crate) new_context: ExecutionContextRecord<F>,
pub(crate) new_decommittment_queue_tail: [Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
pub(crate) new_decommittment_queue_len: UInt32<F>,
pub(crate) new_forward_queue_tail: [Num<F>; QUEUE_STATE_WIDTH],
pub(crate) new_forward_queue_len: UInt32<F>,
pub(crate) pending_sponges: ArrayVec<
(
Boolean<F>,
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
),
MAX_SPONGES_PER_CYCLE,
>,
pub(crate) specific_registers_updates: [Option<(Boolean<F>, VMRegister<F>)>; REGISTERS_COUNT],
pub(crate) specific_registers_zeroing: [Option<Boolean<F>>; REGISTERS_COUNT],
pub(crate) remove_ptr_on_specific_registers: [Option<Boolean<F>>; REGISTERS_COUNT],
pub(crate) pending_exception: Boolean<F>,
pub(crate) new_memory_pages_counter: UInt32<F>,
}
#[derive(Derivative, CSAllocatable, WitnessHookable)]
#[derivative(Clone, Copy, Debug)]
pub(crate) struct FarCallPartialABI<F: SmallField> {
pub(crate) ergs_passed: UInt32<F>,
pub(crate) shard_id: UInt8<F>,
pub(crate) constructor_call: Boolean<F>,
pub(crate) system_call: Boolean<F>,
}
use crate::main_vm::register_input_view::RegisterInputView;
impl<F: SmallField> FarCallPartialABI<F> {
pub fn from_register_view<CS: ConstraintSystem<F>>(
cs: &mut CS,
input: &RegisterInputView<F>,
) -> Self {
let ergs_passed = input.u32x8_view[6];
let shard_id = input.u8x32_view
[zkevm_opcode_defs::definitions::abi::far_call::FAR_CALL_SHARD_ID_BYTE_IDX];
let constructor_call = input.u8x32_view
[zkevm_opcode_defs::definitions::abi::far_call::FAR_CALL_CONSTRUCTOR_CALL_BYTE_IDX]
.is_zero(cs)
.negated(cs);
let system_call = input.u8x32_view
[zkevm_opcode_defs::definitions::abi::far_call::FAR_CALL_SYSTEM_CALL_BYTE_IDX]
.is_zero(cs)
.negated(cs);
let new = Self {
ergs_passed,
shard_id,
constructor_call,
system_call,
};
new
}
}
#[derive(Derivative, CSAllocatable, WitnessHookable)]
#[derivative(Clone, Copy, Debug)]
pub(crate) struct CommonCallRetABI<F: SmallField> {
pub(crate) fat_ptr: FatPtrInABI<F>,
pub(crate) upper_bound: UInt32<F>,
pub(crate) ptr_validation_data: PtrValidationData<F>,
}
#[derive(Derivative, CSAllocatable, WitnessHookable)]
#[derivative(Clone, Copy, Debug)]
pub(crate) struct CallRetForwardingMode<F: SmallField> {
pub(crate) use_heap: Boolean<F>,
pub(crate) use_aux_heap: Boolean<F>,
pub(crate) forward_fat_pointer: Boolean<F>,
}
#[derive(Derivative, CSAllocatable, WitnessHookable)]
#[derivative(Clone, Copy, Debug)]
pub(crate) struct FatPtrInABI<F: SmallField> {
pub(crate) offset: UInt32<F>,
pub(crate) page: UInt32<F>,
pub(crate) start: UInt32<F>,
pub(crate) length: UInt32<F>,
}
impl<F: SmallField> Selectable<F> for FatPtrInABI<F> {
fn conditionally_select<CS: ConstraintSystem<F>>(
cs: &mut CS,
flag: Boolean<F>,
a: &Self,
b: &Self,
) -> Self {
let a = [a.offset, a.page, a.start, a.length];
let b = [b.offset, b.page, b.start, b.length];
let result = UInt32::parallel_select(cs, flag, &a, &b);
Self {
offset: result[0],
page: result[1],
start: result[2],
length: result[3],
}
}
}
#[derive(Derivative, CSAllocatable, WitnessHookable)]
#[derivative(Clone, Copy, Debug)]
pub(crate) struct PtrValidationData<F: SmallField> {
pub(crate) generally_invalid: Boolean<F>, pub(crate) is_non_addressable: Boolean<F>,
}
impl<F: SmallField> FatPtrInABI<F> {
pub(crate) fn parse_and_validate<CS: ConstraintSystem<F>>(
cs: &mut CS,
input: &RegisterInputView<F>,
as_fresh: Boolean<F>,
) -> (Self, UInt32<F>, PtrValidationData<F>) {
let offset = input.u32x8_view[0];
let page = input.u32x8_view[1];
let start = input.u32x8_view[2];
let length = input.u32x8_view[3];
let offset_is_zero = offset.is_zero(cs);
let offset_is_non_zero = offset_is_zero.negated(cs);
let non_zero_offset_if_should_be_fresh =
Boolean::multi_and(cs, &[offset_is_non_zero, as_fresh]);
let (end_non_inclusive, slice_u32_range_overflow) = start.overflowing_add(cs, length);
let (_, is_invalid_as_slice) = length.overflowing_sub(cs, offset);
let ptr_is_invalid = Boolean::multi_or(
cs,
&[
non_zero_offset_if_should_be_fresh,
slice_u32_range_overflow,
is_invalid_as_slice,
],
);
let offset = offset.mask_negated(cs, ptr_is_invalid);
let page = page.mask_negated(cs, ptr_is_invalid);
let start = start.mask_negated(cs, ptr_is_invalid);
let length = length.mask_negated(cs, ptr_is_invalid);
let new = Self {
offset,
page,
start,
length,
};
let validation_data = PtrValidationData {
generally_invalid: ptr_is_invalid,
is_non_addressable: slice_u32_range_overflow,
};
(new, end_non_inclusive, validation_data)
}
pub(crate) fn mask_into_empty<CS: ConstraintSystem<F>>(
&self,
cs: &mut CS,
set_empty: Boolean<F>,
) -> Self {
let offset = self.offset.mask_negated(cs, set_empty);
let page = self.page.mask_negated(cs, set_empty);
let start = self.start.mask_negated(cs, set_empty);
let length = self.length.mask_negated(cs, set_empty);
let new = Self {
offset,
page,
start,
length,
};
new
}
pub(crate) fn readjust<CS: ConstraintSystem<F>>(&self, cs: &mut CS) -> Self {
let new_start = self.start.add_no_overflow(cs, self.offset);
let new_length = self.length.sub_no_overflow(cs, self.offset);
let zero_u32 = UInt32::zero(cs);
let new = Self {
offset: zero_u32,
page: self.page,
start: new_start,
length: new_length,
};
new
}
pub(crate) fn into_register<CS: ConstraintSystem<F>>(self, cs: &mut CS) -> VMRegister<F> {
let zero_u32 = UInt32::zero(cs);
let boolean_true = Boolean::allocated_constant(cs, true);
let result = VMRegister {
is_pointer: boolean_true,
value: UInt256 {
inner: [
self.offset,
self.page,
self.start,
self.length,
zero_u32,
zero_u32,
zero_u32,
zero_u32,
],
},
};
result
}
}
pub(crate) fn callstack_candidate_for_far_call<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
W: WitnessOracle<F>,
>(
cs: &mut CS,
draft_vm_state: &VmLocalState<F>,
common_opcode_state: &CommonOpcodeState<F>,
opcode_carry_parts: &AfterDecodingCarryParts<F>,
witness_oracle: &SynchronizedWitnessOracle<F, W>,
global_context: &GlobalContext<F>,
common_abi_parts: &CommonCallRetABI<F>,
far_call_abi: &FarCallPartialABI<F>,
forwarding_data: &CallRetForwardingMode<F>,
round_function: &R,
) -> FarCallData<F>
where
[(); <ExecutionContextRecord<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN]:,
[(); <LogQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN]:,
[(); <DecommitQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN]:,
{
const FAR_CALL_OPCODE: zkevm_opcode_defs::Opcode =
zkevm_opcode_defs::Opcode::FarCall(zkevm_opcode_defs::FarCallOpcode::Normal);
let execute = common_opcode_state
.decoded_opcode
.properties_bits
.boolean_for_opcode(FAR_CALL_OPCODE);
if crate::config::CIRCUIT_VERSOBE {
if (execute.witness_hook(&*cs))().unwrap_or(false) {
println!("Applying FAR CALL");
}
}
let _is_normal_call = common_opcode_state
.decoded_opcode
.properties_bits
.boolean_for_variant(zkevm_opcode_defs::Opcode::FarCall(
zkevm_opcode_defs::FarCallOpcode::Normal,
));
let is_delegated_call = common_opcode_state
.decoded_opcode
.properties_bits
.boolean_for_variant(zkevm_opcode_defs::Opcode::FarCall(
zkevm_opcode_defs::FarCallOpcode::Delegate,
));
let is_mimic_call = common_opcode_state
.decoded_opcode
.properties_bits
.boolean_for_variant(zkevm_opcode_defs::Opcode::FarCall(
zkevm_opcode_defs::FarCallOpcode::Mimic,
));
let is_kernel_mode = draft_vm_state
.callstack
.current_context
.saved_context
.is_kernel_mode;
let mut current_callstack_entry = draft_vm_state.callstack.current_context.saved_context;
current_callstack_entry.pc = opcode_carry_parts.next_pc;
let mut new_callstack_entry = ExecutionContextRecord::uninitialized(cs);
let implicit_mimic_call_reg = draft_vm_state.registers
[zkevm_opcode_defs::definitions::far_call::CALL_IMPLICIT_PARAMETER_REG_IDX as usize];
let caller_address_for_mimic = UInt160 {
inner: [
implicit_mimic_call_reg.value.inner[0],
implicit_mimic_call_reg.value.inner[1],
implicit_mimic_call_reg.value.inner[2],
implicit_mimic_call_reg.value.inner[3],
implicit_mimic_call_reg.value.inner[4],
],
};
let destination_address = UInt160 {
inner: [
common_opcode_state.src1_view.u32x8_view[0],
common_opcode_state.src1_view.u32x8_view[1],
common_opcode_state.src1_view.u32x8_view[2],
common_opcode_state.src1_view.u32x8_view[3],
common_opcode_state.src1_view.u32x8_view[4],
],
};
let is_static_call = common_opcode_state
.decoded_opcode
.properties_bits
.flag_booleans[FAR_CALL_STATIC_FLAG_IDX];
let is_call_shard = common_opcode_state
.decoded_opcode
.properties_bits
.flag_booleans[FAR_CALL_SHARD_FLAG_IDX];
let destination_shard = far_call_abi.shard_id;
let caller_shard_id = current_callstack_entry.this_shard_id;
let destination_shard =
UInt8::conditionally_select(cs, is_call_shard, &destination_shard, &caller_shard_id);
let target_is_zkporter = destination_shard.is_zero(cs).negated(cs);
let target_is_kernel = {
let destination_16_32 = UInt16::from_le_bytes(
cs,
[
common_opcode_state.src1_view.u8x32_view[2],
common_opcode_state.src1_view.u8x32_view[3],
],
);
let destination_16_32_is_zero = destination_16_32.is_zero(cs);
let destination_32_64_is_zero = common_opcode_state.src1_view.u32x8_view[1].is_zero(cs);
let destination_64_96_is_zero = common_opcode_state.src1_view.u32x8_view[2].is_zero(cs);
let destination_96_128_is_zero = common_opcode_state.src1_view.u32x8_view[3].is_zero(cs);
let destination_128_160_is_zero = common_opcode_state.src1_view.u32x8_view[4].is_zero(cs);
let higher_bytes_are_zeroes = Boolean::multi_and(
cs,
&[
destination_16_32_is_zero,
destination_32_64_is_zero,
destination_64_96_is_zero,
destination_96_128_is_zero,
destination_128_160_is_zero,
],
);
higher_bytes_are_zeroes
};
let target_is_userspace = target_is_kernel.negated(cs);
if crate::config::CIRCUIT_VERSOBE {
if (execute.witness_hook(&*cs))().unwrap_or(false) {
dbg!(destination_address.witness_hook(cs)().unwrap());
dbg!(target_is_kernel.witness_hook(cs)().unwrap());
}
}
let mut far_call_abi = *far_call_abi;
let conversion_constant = UInt32::allocated_constant(
cs,
zkevm_opcode_defs::system_params::INTERNAL_ERGS_TO_VISIBLE_ERGS_CONVERSION_CONSTANT,
);
let cap = UInt32::allocated_constant(cs, u32::MAX);
far_call_abi.ergs_passed = if cs.gate_is_allowed::<U8x4FMAGate>() {
let zero_u32 = UInt32::zero(cs);
let [(low, _), (high, _)] = UInt32::fma_with_carry(
cs,
far_call_abi.ergs_passed,
conversion_constant,
zero_u32,
zero_u32,
);
let fits_u32 = high.is_zero(cs);
UInt32::conditionally_select(cs, fits_u32, &low, &cap)
} else {
unimplemented!()
};
far_call_abi.constructor_call =
Boolean::multi_and(cs, &[far_call_abi.constructor_call, is_kernel_mode]);
far_call_abi.system_call =
Boolean::multi_and(cs, &[far_call_abi.system_call, target_is_kernel]);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap_or(false) {
dbg!(opcode_carry_parts.preliminary_ergs_left.witness_hook(&*cs)().unwrap());
dbg!(forwarding_data.witness_hook(&*cs)().unwrap());
dbg!(far_call_abi.witness_hook(&*cs)().unwrap());
}
}
let timestamp_to_use_for_decommittment_request =
common_opcode_state.timestamp_for_first_decommit_or_precompile_read;
let default_target_memory_page = draft_vm_state.memory_page_counter;
let new_base_page = draft_vm_state.memory_page_counter;
let memory_pages_per_far_call = UInt32::allocated_constant(cs, NEW_MEMORY_PAGES_PER_FAR_CALL);
let new_memory_pages_counter = draft_vm_state
.memory_page_counter
.add_no_overflow(cs, memory_pages_per_far_call);
let new_memory_pages_counter = UInt32::conditionally_select(
cs,
execute,
&new_memory_pages_counter,
&draft_vm_state.memory_page_counter,
);
let mut all_pending_sponges = ArrayVec::new();
let (
call_to_unreachable,
bytecode_hash_from_storage,
(new_forward_queue_tail, new_forward_queue_len),
) = may_be_read_code_hash(
cs,
&mut all_pending_sponges,
&destination_address,
&destination_shard,
&target_is_zkporter,
&global_context.zkporter_is_available,
&execute,
draft_vm_state
.callstack
.current_context
.log_queue_forward_tail,
draft_vm_state
.callstack
.current_context
.log_queue_forward_part_length,
timestamp_to_use_for_decommittment_request,
draft_vm_state.tx_number_in_block,
witness_oracle,
round_function,
);
assert_eq!(all_pending_sponges.len(), 3);
let mut exceptions = ArrayVec::<Boolean<F>, 6>::new();
exceptions.push(call_to_unreachable);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(call_to_unreachable.witness_hook(&*cs)().unwrap());
}
}
let zero_u32 = UInt32::zero(cs);
let limbs_are_zero = bytecode_hash_from_storage.inner.map(|el| el.is_zero(cs));
let bytecode_is_empty = Boolean::multi_and(cs, &limbs_are_zero);
let bytecode_hash_from_storage_upper_decomposition =
bytecode_hash_from_storage.inner[7].decompose_into_bytes(cs);
let version_byte = bytecode_hash_from_storage_upper_decomposition[3];
let code_hash_version_byte = UInt8::allocated_constant(
cs,
zkevm_opcode_defs::ContractCodeSha256Format::VERSION_BYTE,
);
let blob_version_byte =
UInt8::allocated_constant(cs, zkevm_opcode_defs::BlobSha256Format::VERSION_BYTE);
assert_eq!(
zkevm_opcode_defs::ContractCodeSha256Format::CODE_AT_REST_MARKER,
zkevm_opcode_defs::BlobSha256Format::CODE_AT_REST_MARKER
);
assert_eq!(
zkevm_opcode_defs::ContractCodeSha256Format::YET_CONSTRUCTED_MARKER,
zkevm_opcode_defs::BlobSha256Format::YET_CONSTRUCTED_MARKER
);
let code_at_rest_version_byte = UInt8::allocated_constant(
cs,
zkevm_opcode_defs::ContractCodeSha256Format::CODE_AT_REST_MARKER,
);
let now_in_construction_marker_byte = UInt8::allocated_constant(
cs,
zkevm_opcode_defs::ContractCodeSha256Format::YET_CONSTRUCTED_MARKER,
);
let versioned_byte_is_native_code = UInt8::equals(cs, &version_byte, &code_hash_version_byte);
let versioned_byte_is_evm_bytecode = UInt8::equals(cs, &version_byte, &blob_version_byte);
let marker_byte = bytecode_hash_from_storage_upper_decomposition[2];
let is_code_at_rest_marker = UInt8::equals(cs, &marker_byte, &code_at_rest_version_byte);
let is_constructor_call_marker =
UInt8::equals(cs, &marker_byte, &now_in_construction_marker_byte);
let abi_is_normal_call = far_call_abi.constructor_call.negated(cs);
let normal_call_markers_match =
Boolean::multi_and(cs, &[abi_is_normal_call, is_code_at_rest_marker]);
let constructor_call_markers_match = Boolean::multi_and(
cs,
&[far_call_abi.constructor_call, is_constructor_call_marker],
);
let markers_match = Boolean::multi_or(
cs,
&[normal_call_markers_match, constructor_call_markers_match],
);
let markers_mismatch = markers_match.negated(cs);
let can_call_native_without_masking =
Boolean::multi_and(cs, &[markers_match, versioned_byte_is_native_code]);
let can_not_call_native_without_masking =
Boolean::multi_and(cs, &[markers_mismatch, versioned_byte_is_native_code]);
let should_mask_native = Boolean::multi_and(
cs,
&[can_not_call_native_without_masking, target_is_userspace],
);
let mask_to_default_aa = should_mask_native;
let exception_over_native_bytecode_format =
Boolean::multi_and(cs, &[can_not_call_native_without_masking, target_is_kernel]);
let can_call_evm_emulator_without_masking =
Boolean::multi_and(cs, &[markers_match, versioned_byte_is_evm_bytecode]);
let can_not_call_evm_emulator_without_masking =
Boolean::multi_and(cs, &[markers_mismatch, versioned_byte_is_evm_bytecode]);
let should_mask_evm_emulator = Boolean::multi_and(
cs,
&[
can_not_call_evm_emulator_without_masking,
target_is_userspace,
],
);
let mask_to_default_aa = Boolean::multi_or(cs, &[mask_to_default_aa, should_mask_evm_emulator]);
let exception_over_evm_emulator_bytecode_format = Boolean::multi_and(
cs,
&[can_not_call_evm_emulator_without_masking, target_is_kernel],
);
let mask_empty_to_default_aa =
Boolean::multi_and(cs, &[bytecode_is_empty, target_is_userspace]);
let mask_to_default_aa = Boolean::multi_or(cs, &[mask_to_default_aa, mask_empty_to_default_aa]);
let exception_over_empty_bytecode =
Boolean::multi_and(cs, &[bytecode_is_empty, target_is_kernel]);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(exception_over_native_bytecode_format.witness_hook(&*cs)().unwrap());
dbg!(exception_over_evm_emulator_bytecode_format.witness_hook(&*cs)().unwrap());
dbg!(exception_over_empty_bytecode.witness_hook(&*cs)().unwrap());
}
}
let code_format_exception = Boolean::multi_or(
cs,
&[
exception_over_native_bytecode_format,
exception_over_evm_emulator_bytecode_format,
exception_over_empty_bytecode,
],
);
let zero_u256 = UInt256::zero(cs);
let masked_bytecode_hash = UInt256::conditionally_select(
cs,
mask_to_default_aa,
&global_context.default_aa_code_hash,
&bytecode_hash_from_storage,
);
let masked_bytecode_hash = UInt256::conditionally_select(
cs,
can_call_evm_emulator_without_masking,
&global_context.evm_emulator_code_hash,
&masked_bytecode_hash,
);
let masked_bytecode_hash =
UInt256::conditionally_select(cs, code_format_exception, &zero_u256, &masked_bytecode_hash);
let code_len_encoding_word = masked_bytecode_hash.inner[7];
let masked_bytecode_hash_upper_decomposition = code_len_encoding_word.to_le_bytes(cs);
let code_hash_length_in_words = UInt16::from_le_bytes(
cs,
[
masked_bytecode_hash_upper_decomposition[0],
masked_bytecode_hash_upper_decomposition[1],
],
);
exceptions.push(code_format_exception);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(code_format_exception.witness_hook(&*cs)().unwrap());
}
}
let mut normalized_preimage = masked_bytecode_hash;
normalize_bytecode_hash_for_decommit(cs, &mut normalized_preimage);
let forward_fat_pointer = forwarding_data.forward_fat_pointer;
let do_not_forward_ptr = forward_fat_pointer.negated(cs);
let src0_is_integer = common_opcode_state.src0_view.is_ptr.negated(cs);
let fat_ptr_expected_exception =
Boolean::multi_and(cs, &[forward_fat_pointer, src0_is_integer]);
exceptions.push(fat_ptr_expected_exception);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(fat_ptr_expected_exception.witness_hook(&*cs)().unwrap());
}
}
let non_pointer_expected_exception = Boolean::multi_and(
cs,
&[do_not_forward_ptr, common_opcode_state.src0_view.is_ptr],
);
exceptions.push(non_pointer_expected_exception);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(non_pointer_expected_exception.witness_hook(&*cs)().unwrap());
}
}
exceptions.push(common_abi_parts.ptr_validation_data.generally_invalid);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(common_abi_parts
.ptr_validation_data
.generally_invalid
.witness_hook(&*cs)()
.unwrap());
}
}
exceptions.push(common_abi_parts.ptr_validation_data.is_non_addressable);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(common_abi_parts
.ptr_validation_data
.is_non_addressable
.witness_hook(&*cs)()
.unwrap());
}
}
let exceptions_collapsed = Boolean::multi_or(cs, &exceptions);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(exceptions_collapsed.witness_hook(&*cs)().unwrap());
dbg!(can_call_native_without_masking.witness_hook(&*cs)().unwrap());
dbg!(can_call_evm_emulator_without_masking.witness_hook(&*cs)().unwrap());
dbg!(fat_ptr_expected_exception.witness_hook(&*cs)().unwrap());
dbg!(bytecode_hash_from_storage.witness_hook(&*cs)().unwrap());
dbg!(mask_to_default_aa.witness_hook(&*cs)().unwrap());
dbg!(code_hash_length_in_words.witness_hook(&*cs)().unwrap());
dbg!(normalized_preimage.witness_hook(&*cs)().unwrap());
}
}
let fat_ptr = common_abi_parts.fat_ptr;
let fat_ptr_adjusted_if_forward = fat_ptr.readjust(cs);
let page = UInt32::conditionally_select(
cs,
forwarding_data.use_heap,
&opcode_carry_parts.heap_page,
&opcode_carry_parts.aux_heap_page,
);
let fat_ptr_for_heaps = FatPtrInABI {
offset: zero_u32,
page,
start: fat_ptr.start,
length: fat_ptr.length,
};
let final_fat_ptr = FatPtrInABI::conditionally_select(
cs,
forwarding_data.forward_fat_pointer,
&fat_ptr_adjusted_if_forward,
&fat_ptr_for_heaps,
);
let final_fat_ptr = final_fat_ptr.mask_into_empty(cs, exceptions_collapsed);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap_or(false) {
dbg!(final_fat_ptr.witness_hook(&*cs)().unwrap());
}
}
let upper_bound = common_abi_parts.upper_bound;
let upper_bound = upper_bound.mask_negated(cs, exceptions_collapsed);
let memory_region_is_not_addressable = common_abi_parts.ptr_validation_data.is_non_addressable;
let penalize_heap_overflow =
Boolean::multi_and(cs, &[memory_region_is_not_addressable, do_not_forward_ptr]);
let u32_max = UInt32::allocated_constant(cs, u32::MAX);
let upper_bound =
UInt32::conditionally_select(cs, penalize_heap_overflow, &u32_max, &upper_bound);
let heap_max_accessed = upper_bound.mask(cs, forwarding_data.use_heap);
let heap_bound = current_callstack_entry.heap_upper_bound;
let (mut heap_growth, uf) = heap_max_accessed.overflowing_sub(cs, heap_bound);
heap_growth = heap_growth.mask_negated(cs, uf); let new_heap_upper_bound =
UInt32::conditionally_select(cs, uf, &heap_bound, &heap_max_accessed);
let aux_heap_max_accessed = upper_bound.mask(cs, forwarding_data.use_aux_heap);
let aux_heap_bound = current_callstack_entry.aux_heap_upper_bound;
let (mut aux_heap_growth, uf) = aux_heap_max_accessed.overflowing_sub(cs, aux_heap_bound);
aux_heap_growth = aux_heap_growth.mask_negated(cs, uf); let new_aux_heap_upper_bound =
UInt32::conditionally_select(cs, uf, &aux_heap_bound, &aux_heap_max_accessed);
let mut growth_cost = heap_growth.mask(cs, forwarding_data.use_heap);
growth_cost = UInt32::conditionally_select(
cs,
forwarding_data.use_aux_heap,
&aux_heap_growth,
&growth_cost,
);
growth_cost = growth_cost.mask(cs, execute);
let (ergs_left_after_growth, uf) = opcode_carry_parts
.preliminary_ergs_left
.overflowing_sub(cs, growth_cost);
let mut exceptions = ArrayVec::<Boolean<F>, 5>::new();
exceptions.push(exceptions_collapsed);
let ergs_left_after_growth = ergs_left_after_growth.mask_negated(cs, uf); exceptions.push(uf);
let panic = Boolean::multi_or(
cs,
&[common_abi_parts.ptr_validation_data.generally_invalid, uf],
);
let no_panic = panic.negated(cs);
let grow_heap = Boolean::multi_and(cs, &[forwarding_data.use_heap, execute, no_panic]);
let grow_aux_heap = Boolean::multi_and(cs, &[forwarding_data.use_aux_heap, execute, no_panic]);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(ergs_left_after_growth.witness_hook(&*cs)().unwrap());
dbg!(opcode_carry_parts.preliminary_ergs_left.witness_hook(&*cs)().unwrap());
dbg!(grow_heap.witness_hook(&*cs)().unwrap());
dbg!(heap_growth.witness_hook(&*cs)().unwrap());
dbg!(grow_aux_heap.witness_hook(&*cs)().unwrap());
dbg!(aux_heap_growth.witness_hook(&*cs)().unwrap());
dbg!(growth_cost.witness_hook(&*cs)().unwrap());
}
}
current_callstack_entry.heap_upper_bound = UInt32::conditionally_select(
cs,
grow_heap,
&new_heap_upper_bound,
¤t_callstack_entry.heap_upper_bound,
);
current_callstack_entry.aux_heap_upper_bound = UInt32::conditionally_select(
cs,
grow_aux_heap,
&new_aux_heap_upper_bound,
¤t_callstack_entry.aux_heap_upper_bound,
);
let address_low = UInt16::from_le_bytes(
cs,
[
common_opcode_state.src1_view.u8x32_view[0],
common_opcode_state.src1_view.u8x32_view[1],
],
);
let address_low_masked = address_low.mask(cs, target_is_kernel);
let address_low_masked = address_low_masked.mask(cs, far_call_abi.system_call);
let table_id = cs
.get_table_id_for_marker::<CallCostsAndStipendsTable>()
.expect("table of costs and stipends must exist");
let (default_stipend, default_extra_cost) =
zkevm_opcode_defs::STIPENDS_AND_EXTRA_COSTS_TABLE[0];
assert_eq!(default_extra_cost, 0);
assert_eq!(default_stipend, 0);
let [callee_stipend, extra_ergs_from_caller_to_callee] =
cs.perform_lookup::<1, 2>(table_id, &[address_low_masked.get_variable()]);
let extra_ergs_from_caller_to_callee =
unsafe { UInt32::from_variable_unchecked(extra_ergs_from_caller_to_callee) };
let callee_stipend = unsafe { UInt32::from_variable_unchecked(callee_stipend) };
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(address_low.witness_hook(&*cs)().unwrap());
dbg!(address_low_masked.witness_hook(&*cs)().unwrap());
dbg!(ergs_left_after_growth.witness_hook(&*cs)().unwrap());
dbg!(extra_ergs_from_caller_to_callee.witness_hook(&*cs)().unwrap());
dbg!(callee_stipend.witness_hook(&*cs)().unwrap());
}
}
let (ergs_left_after_extra_costs, uf) =
ergs_left_after_growth.overflowing_sub(cs, extra_ergs_from_caller_to_callee);
let ergs_left_after_extra_costs = ergs_left_after_extra_costs.mask_negated(cs, uf); let extra_ergs_from_caller_to_callee = extra_ergs_from_caller_to_callee.mask_negated(cs, uf); exceptions.push(uf);
let exception = Boolean::multi_or(cs, &exceptions);
let valid_execution = exception.negated(cs);
let should_decommit = Boolean::multi_and(cs, &[execute, valid_execution]);
let target_code_memory_page = default_target_memory_page;
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap() {
dbg!(extra_ergs_from_caller_to_callee.witness_hook(&*cs)().unwrap());
dbg!(exception.witness_hook(&*cs)().unwrap());
dbg!(ergs_left_after_extra_costs.witness_hook(&*cs)().unwrap());
}
}
let (
not_enough_ergs_to_decommit,
code_memory_page,
(new_decommittment_queue_tail, new_decommittment_queue_len),
ergs_remaining_after_decommit,
) = add_to_decommittment_queue(
cs,
&mut all_pending_sponges,
&should_decommit,
&ergs_left_after_extra_costs,
&normalized_preimage,
&code_hash_length_in_words,
&draft_vm_state.code_decommittment_queue_state,
&draft_vm_state.code_decommittment_queue_length,
×tamp_to_use_for_decommittment_request,
&target_code_memory_page,
witness_oracle,
round_function,
);
assert_eq!(all_pending_sponges.len(), 4);
let exception = Boolean::multi_or(cs, &[exception, not_enough_ergs_to_decommit]);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap_or(false) {
dbg!(ergs_remaining_after_decommit.witness_hook(&*cs)().unwrap());
dbg!(not_enough_ergs_to_decommit.witness_hook(&*cs)().unwrap());
dbg!(exception.witness_hook(&*cs)().unwrap());
}
}
let call_timestamp = draft_vm_state.timestamp;
let oracle = witness_oracle.clone();
let dependencies = [
call_timestamp.get_variable().into(),
execute.get_variable().into(),
];
let potential_rollback_queue_segment_tail =
Num::allocate_multiple_from_closure_and_dependencies(
cs,
move |inputs: &[F]| {
let call_timestamp = <u32 as WitnessCastable<F, F>>::cast_from_source(inputs[0]);
let execute = <bool as WitnessCastable<F, F>>::cast_from_source(inputs[1]);
let mut guard = oracle.inner.write().expect("not poisoned");
let witness =
guard.get_rollback_queue_tail_witness_for_call(call_timestamp, execute);
drop(guard);
witness
},
&dependencies,
);
new_callstack_entry.reverted_queue_tail = potential_rollback_queue_segment_tail;
new_callstack_entry.reverted_queue_head = potential_rollback_queue_segment_tail;
new_callstack_entry.reverted_queue_segment_len = zero_u32;
let dst_pc = UInt16::zero(cs);
let eh_pc = common_opcode_state.decoded_opcode.imm0;
let preliminary_ergs_left = ergs_remaining_after_decommit;
let (ergs_div_by_64, _) = preliminary_ergs_left.div_by_constant(cs, 64);
let constant_63 = UInt32::allocated_constant(cs, 63);
let max_passable = Num::from_variable(ergs_div_by_64.get_variable())
.mul(cs, &Num::from_variable(constant_63.get_variable()));
let max_passable = unsafe { UInt32::from_variable_unchecked(max_passable.get_variable()) };
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap_or(false) {
dbg!(max_passable.witness_hook(&*cs)().unwrap());
}
}
let leftover = Num::from_variable(preliminary_ergs_left.get_variable())
.sub(cs, &Num::from_variable(max_passable.get_variable()));
let leftover = unsafe { UInt32::from_variable_unchecked(leftover.get_variable()) };
let ergs_to_pass = far_call_abi.ergs_passed;
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap_or(false) {
dbg!(leftover.witness_hook(&*cs)().unwrap());
dbg!(ergs_to_pass.witness_hook(&*cs)().unwrap());
}
}
let (remaining_from_max_passable, uf) = max_passable.overflowing_sub(cs, ergs_to_pass);
let (leftover_and_remaining_if_no_uf, _of) =
leftover.overflowing_add(cs, remaining_from_max_passable);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap_or(false) {
dbg!(remaining_from_max_passable.witness_hook(&*cs)().unwrap());
dbg!(leftover_and_remaining_if_no_uf.witness_hook(&*cs)().unwrap());
}
}
let ergs_to_pass = UInt32::conditionally_select(cs, uf, &max_passable, &ergs_to_pass);
let remaining_for_this_context =
UInt32::conditionally_select(cs, uf, &leftover, &leftover_and_remaining_if_no_uf);
if crate::config::CIRCUIT_VERSOBE {
if execute.witness_hook(&*cs)().unwrap_or(false) {
dbg!(ergs_to_pass.witness_hook(&*cs)().unwrap());
dbg!(remaining_for_this_context.witness_hook(&*cs)().unwrap());
dbg!(extra_ergs_from_caller_to_callee.witness_hook(&*cs)().unwrap());
dbg!(callee_stipend.witness_hook(&*cs)().unwrap());
}
}
let remaining_ergs_if_pass = remaining_for_this_context;
let passed_ergs_if_pass = ergs_to_pass;
let passed_ergs_if_pass = unsafe {
UInt32::from_variable_unchecked(
Num::from_variable(passed_ergs_if_pass.get_variable())
.add(
cs,
&Num::from_variable(extra_ergs_from_caller_to_callee.get_variable()),
)
.get_variable(),
)
};
let passed_ergs_if_pass = passed_ergs_if_pass.add_no_overflow(cs, callee_stipend);
current_callstack_entry.ergs_remaining = remaining_ergs_if_pass;
let new_this_shard_id =
UInt8::conditionally_select(cs, is_delegated_call, &caller_shard_id, &destination_shard);
let mut this_for_next = destination_address;
let mut caller_for_next = current_callstack_entry.this;
this_for_next = UInt160::conditionally_select(
cs,
is_delegated_call,
¤t_callstack_entry.this,
&this_for_next,
);
caller_for_next = UInt160::conditionally_select(
cs,
is_delegated_call,
¤t_callstack_entry.caller,
&caller_for_next,
);
caller_for_next = UInt160::conditionally_select(
cs,
is_mimic_call,
&caller_address_for_mimic,
&caller_for_next,
);
let next_is_static = Boolean::multi_or(
cs,
&[is_static_call, current_callstack_entry.is_static_execution],
);
let next_is_static_masked =
next_is_static.mask_negated(cs, can_call_evm_emulator_without_masking);
new_callstack_entry.ergs_remaining = passed_ergs_if_pass;
new_callstack_entry.pc = dst_pc;
new_callstack_entry.exception_handler_loc = eh_pc;
new_callstack_entry.is_static_execution = next_is_static_masked;
let new_frame_is_kernel = Boolean::conditionally_select(
cs,
is_delegated_call,
¤t_callstack_entry.is_kernel_mode,
&target_is_kernel,
);
new_callstack_entry.is_kernel_mode = new_frame_is_kernel;
let memory_size_stipend_for_kernel = UInt32::allocated_constant(
cs,
zkevm_opcode_defs::system_params::NEW_KERNEL_FRAME_MEMORY_STIPEND,
);
let memory_size_stipend_for_userspace = UInt32::allocated_constant(
cs,
zkevm_opcode_defs::system_params::NEW_FRAME_MEMORY_STIPEND,
);
let memory_size_stipend_for_evm = UInt32::allocated_constant(
cs,
zkevm_opcode_defs::system_params::NEW_EVM_FRAME_MEMORY_STIPEND,
);
let memory_size_stipend_for_userspace = UInt32::conditionally_select(
cs,
versioned_byte_is_evm_bytecode,
&memory_size_stipend_for_evm,
&memory_size_stipend_for_userspace,
);
let memory_stipend = UInt32::conditionally_select(
cs,
new_callstack_entry.is_kernel_mode,
&memory_size_stipend_for_kernel,
&memory_size_stipend_for_userspace,
);
new_callstack_entry.heap_upper_bound = memory_stipend;
new_callstack_entry.aux_heap_upper_bound = memory_stipend;
new_callstack_entry.code_shard_id = destination_shard;
new_callstack_entry.code_address = destination_address;
new_callstack_entry.this_shard_id = new_this_shard_id;
new_callstack_entry.this = this_for_next;
new_callstack_entry.caller = caller_for_next;
new_callstack_entry.caller_shard_id = caller_shard_id;
new_callstack_entry.code_page = code_memory_page;
new_callstack_entry.base_page = new_base_page;
new_callstack_entry.context_u128_value_composite = UInt32::parallel_select(
cs,
is_delegated_call,
¤t_callstack_entry.context_u128_value_composite,
&draft_vm_state.context_composite_u128,
);
let boolean_false = Boolean::allocated_constant(cs, false);
new_callstack_entry.is_local_call = boolean_false;
new_callstack_entry.stipend = callee_stipend;
let oracle = witness_oracle.clone();
let mut dependencies = Vec::with_capacity(
<ExecutionContextRecord<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN + 2,
);
dependencies.push(execute.get_variable().into());
dependencies.push(
draft_vm_state
.callstack
.context_stack_depth
.get_variable()
.into(),
);
dependencies.extend(Place::from_variables(
current_callstack_entry.flatten_as_variables(),
));
cs.set_values_with_dependencies_vararg(
&dependencies,
&[],
move |inputs: &[F], _buffer: &mut DstBuffer<'_, '_, F>| {
let execute = <bool as WitnessCastable<F, F>>::cast_from_source(inputs[0]);
let current_depth = <u32 as WitnessCastable<F, F>>::cast_from_source(inputs[1]);
let mut query: [F;
<ExecutionContextRecord<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN] =
[F::ZERO; <ExecutionContextRecord<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN];
query.copy_from_slice(&inputs[2..]);
let query: ExecutionContextRecordWitness<F> =
CSAllocatableExt::witness_from_set_of_values(query);
let mut guard = oracle.inner.write().expect("not poisoned");
guard.push_callstack_witness(&query, current_depth, execute);
drop(guard);
},
);
let new_r1 = final_fat_ptr.into_register(cs);
let original_call_was_static = next_is_static.mask(cs, can_call_evm_emulator_without_masking);
let r2_low = Num::linear_combination(
cs,
&[
(
far_call_abi.constructor_call.get_variable(),
F::from_u64_unchecked(1u64 << 0),
),
(
far_call_abi.system_call.get_variable(),
F::from_u64_unchecked(1u64 << 1),
),
(
original_call_was_static.get_variable(),
F::from_u64_unchecked(1u64 << 2),
),
],
);
let r2_low = unsafe { UInt32::from_variable_unchecked(r2_low.get_variable()) };
let new_r2 = VMRegister {
is_pointer: boolean_false,
value: UInt256 {
inner: [
r2_low, zero_u32, zero_u32, zero_u32, zero_u32, zero_u32, zero_u32, zero_u32,
],
},
};
let mut specific_registers_updates = [None; REGISTERS_COUNT];
specific_registers_updates[0] = Some((execute, new_r1));
specific_registers_updates[1] = Some((execute, new_r2));
let non_system_call = far_call_abi.system_call.negated(cs);
let cleanup_register = Boolean::multi_and(cs, &[execute, non_system_call]);
let mut register_zero_out = [None; REGISTERS_COUNT];
for reg_idx in zkevm_opcode_defs::definitions::far_call::CALL_SYSTEM_ABI_REGISTERS {
register_zero_out[reg_idx as usize] = Some(cleanup_register);
}
for reg_idx in zkevm_opcode_defs::definitions::far_call::CALL_RESERVED_RANGE {
register_zero_out[reg_idx as usize] = Some(execute);
}
register_zero_out
[zkevm_opcode_defs::definitions::far_call::CALL_IMPLICIT_PARAMETER_REG_IDX as usize] =
Some(execute);
let mut erase_ptr_markers = [None; REGISTERS_COUNT];
for reg_idx in zkevm_opcode_defs::definitions::far_call::CALL_SYSTEM_ABI_REGISTERS {
erase_ptr_markers[reg_idx as usize] = Some(execute);
}
for reg_idx in zkevm_opcode_defs::definitions::far_call::CALL_RESERVED_RANGE {
erase_ptr_markers[reg_idx as usize] = Some(execute);
}
erase_ptr_markers
[zkevm_opcode_defs::definitions::far_call::CALL_IMPLICIT_PARAMETER_REG_IDX as usize] =
Some(execute);
let full_data = FarCallData {
apply_far_call: execute,
old_context: current_callstack_entry,
new_context: new_callstack_entry,
new_decommittment_queue_tail,
new_decommittment_queue_len,
new_forward_queue_tail,
new_forward_queue_len,
new_memory_pages_counter,
pending_sponges: all_pending_sponges,
specific_registers_updates,
specific_registers_zeroing: register_zero_out,
remove_ptr_on_specific_registers: erase_ptr_markers,
pending_exception: exception,
};
if crate::config::CIRCUIT_VERSOBE {
if (execute.witness_hook(&*cs))().unwrap_or(false) {
println!(
"New frame as a result of FAR CALL: {:?}",
full_data.new_context.witness_hook(cs)()
);
}
}
full_data
}
pub fn may_be_read_code_hash<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
W: WitnessOracle<F>,
>(
cs: &mut CS,
relations_buffer: &mut ArrayVec<
(
Boolean<F>,
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
),
MAX_SPONGES_PER_CYCLE,
>,
call_target: &UInt160<F>,
shard_id: &UInt8<F>,
target_is_zkporter: &Boolean<F>,
zkporter_is_available: &Boolean<F>,
should_execute: &Boolean<F>,
forward_queue_tail: [Num<F>; QUEUE_STATE_WIDTH],
forward_queue_length: UInt32<F>,
timestamp_to_use_for_read_request: UInt32<F>,
tx_number_in_block: UInt32<F>,
witness_oracle: &SynchronizedWitnessOracle<F, W>,
round_function: &R,
) -> (
Boolean<F>,
UInt256<F>,
([Num<F>; QUEUE_STATE_WIDTH], UInt32<F>),
)
where
[(); <LogQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN]:,
{
let target_is_porter_and_its_available =
Boolean::multi_and(cs, &[*target_is_zkporter, *zkporter_is_available]);
let target_is_rollup = target_is_zkporter.negated(cs);
let can_read = Boolean::multi_or(cs, &[target_is_rollup, target_is_porter_and_its_available]);
let call_to_unreachable = can_read.negated(cs);
let should_read = Boolean::multi_and(cs, &[*should_execute, can_read]);
let zero_u32 = UInt32::zero(cs);
let target_as_u256 = UInt256 {
inner: [
call_target.inner[0],
call_target.inner[1],
call_target.inner[2],
call_target.inner[3],
call_target.inner[4],
zero_u32,
zero_u32,
zero_u32,
],
};
let deployer_contract_address_low = UInt32::allocated_constant(
cs,
zkevm_opcode_defs::system_params::DEPLOYER_SYSTEM_CONTRACT_ADDRESS_LOW as u32,
);
let deployer_contract_address = UInt160 {
inner: [
deployer_contract_address_low,
zero_u32,
zero_u32,
zero_u32,
zero_u32,
],
};
let zero_u256 = UInt256::zero(cs);
let boolean_false = Boolean::allocated_constant(cs, false);
let aux_byte_for_storage = UInt8::allocated_constant(cs, STORAGE_AUX_BYTE);
let mut log = LogQuery {
address: deployer_contract_address,
key: target_as_u256,
read_value: zero_u256,
written_value: zero_u256,
rw_flag: boolean_false,
aux_byte: aux_byte_for_storage,
rollback: boolean_false,
is_service: boolean_false,
shard_id: *shard_id,
tx_number_in_block,
timestamp: timestamp_to_use_for_read_request,
};
let oracle = witness_oracle.clone();
let mut dependencies =
Vec::with_capacity(<LogQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN + 2);
dependencies.push(should_read.get_variable().into());
dependencies.push(should_execute.get_variable().into());
dependencies.extend(Place::from_variables(log.flatten_as_variables()));
let read_value = UInt256::allocate_from_closure_and_dependencies(
cs,
move |inputs: &[F]| {
let is_storage = <bool as WitnessCastable<F, F>>::cast_from_source(inputs[0]);
let execute = <bool as WitnessCastable<F, F>>::cast_from_source(inputs[1]);
let mut log_query =
[F::ZERO; <LogQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN];
log_query.copy_from_slice(&inputs[2..]);
let log_query: LogQueryWitness<F> =
CSAllocatableExt::witness_from_set_of_values(log_query);
let mut guard = oracle.inner.write().expect("not poisoned");
let witness = guard.get_storage_read_witness(&log_query, is_storage, execute);
drop(guard);
witness
},
&dependencies,
);
log.read_value = read_value;
log.written_value = read_value;
{
let oracle = witness_oracle.clone();
let mut dependencies =
Vec::with_capacity(<LogQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN + 1);
dependencies.push(should_read.get_variable().into());
dependencies.extend(Place::from_variables(log.flatten_as_variables()));
let _pubdata_cost = UInt32::allocate_from_closure_and_dependencies(
cs,
move |inputs: &[F]| {
let execute = <bool as WitnessCastable<F, F>>::cast_from_source(inputs[0]);
let mut log_query =
[F::ZERO; <LogQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN];
log_query.copy_from_slice(&inputs[1..]);
let log_query: LogQueryWitness<F> =
CSAllocatableExt::witness_from_set_of_values(log_query);
let mut guard = oracle.inner.write().expect("not poisoned");
let witness = guard.get_pubdata_cost_for_query(&log_query, false, execute);
drop(guard);
witness
},
&dependencies,
);
}
let code_hash_from_storage = read_value;
let bytecode_hash = code_hash_from_storage;
let (new_forward_queue_tail, new_forward_queue_length) =
construct_hash_relations_code_hash_read(
cs,
relations_buffer,
&log,
&forward_queue_tail,
&forward_queue_length,
&should_read,
round_function,
);
let new_forward_queue_tail = Num::parallel_select(
cs,
should_read,
&new_forward_queue_tail,
&forward_queue_tail,
);
(
call_to_unreachable,
bytecode_hash,
(new_forward_queue_tail, new_forward_queue_length),
)
}
fn construct_hash_relations_code_hash_read<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
>(
cs: &mut CS,
relations_buffer: &mut ArrayVec<
(
Boolean<F>,
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
),
MAX_SPONGES_PER_CYCLE,
>,
log: &LogQuery<F>,
forward_queue_tail: &[Num<F>; 4],
forward_queue_length: &UInt32<F>,
should_read: &Boolean<F>,
_round_function: &R,
) -> ([Num<F>; 4], UInt32<F>) {
let mut current_state = R::create_empty_state(cs);
use boojum::gadgets::traits::encodable::CircuitEncodable;
let forward_packed_log = log.encode(cs);
let boolean_true = Boolean::allocated_constant(cs, true);
let round_0_initial = [
forward_packed_log[0],
forward_packed_log[1],
forward_packed_log[2],
forward_packed_log[3],
forward_packed_log[4],
forward_packed_log[5],
forward_packed_log[6],
forward_packed_log[7],
current_state[8],
current_state[9],
current_state[10],
current_state[11],
];
use boojum::gadgets::round_function::simulate_round_function;
let round_0_final =
simulate_round_function::<_, _, 8, 12, 4, R>(cs, round_0_initial, boolean_true);
current_state = round_0_final;
let round_1_initial = [
forward_packed_log[8],
forward_packed_log[9],
forward_packed_log[10],
forward_packed_log[11],
forward_packed_log[12],
forward_packed_log[13],
forward_packed_log[14],
forward_packed_log[15],
current_state[8],
current_state[9],
current_state[10],
current_state[11],
];
let round_1_final =
simulate_round_function::<_, _, 8, 12, 4, R>(cs, round_1_initial, boolean_true);
current_state = round_1_final;
let round_2_initial = [
forward_packed_log[16],
forward_packed_log[17],
forward_packed_log[18],
forward_packed_log[19],
forward_queue_tail[0].get_variable(),
forward_queue_tail[1].get_variable(),
forward_queue_tail[2].get_variable(),
forward_queue_tail[3].get_variable(),
current_state[8],
current_state[9],
current_state[10],
current_state[11],
];
let round_2_final =
simulate_round_function::<_, _, 8, 12, 4, R>(cs, round_2_initial, boolean_true);
let new_forward_queue_tail = [
round_2_final[0],
round_2_final[1],
round_2_final[2],
round_2_final[3],
];
let new_forward_queue_length_candidate =
unsafe { forward_queue_length.increment_unchecked(cs) };
let new_forward_queue_length = UInt32::conditionally_select(
cs,
*should_read,
&new_forward_queue_length_candidate,
&forward_queue_length,
);
relations_buffer.push((
*should_read,
round_0_initial.map(|el| Num::from_variable(el)),
round_0_final.map(|el| Num::from_variable(el)),
));
relations_buffer.push((
*should_read,
round_1_initial.map(|el| Num::from_variable(el)),
round_1_final.map(|el| Num::from_variable(el)),
));
relations_buffer.push((
*should_read,
round_2_initial.map(|el| Num::from_variable(el)),
round_2_final.map(|el| Num::from_variable(el)),
));
(
new_forward_queue_tail.map(|el| Num::from_variable(el)),
new_forward_queue_length,
)
}
pub fn add_to_decommittment_queue<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
W: WitnessOracle<F>,
>(
cs: &mut CS,
relations_buffer: &mut ArrayVec<
(
Boolean<F>,
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
),
MAX_SPONGES_PER_CYCLE,
>,
should_decommit: &Boolean<F>,
ergs_remaining: &UInt32<F>,
bytecode_hash: &UInt256<F>,
num_words_in_bytecode: &UInt16<F>,
current_decommittment_queue_tail: &[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
current_decommittment_queue_len: &UInt32<F>,
timestamp_to_use_for_decommittment_request: &UInt32<F>,
target_memory_page: &UInt32<F>,
witness_oracle: &SynchronizedWitnessOracle<F, W>,
_round_function: &R,
) -> (
Boolean<F>,
UInt32<F>,
([Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH], UInt32<F>),
UInt32<F>,
)
where
[(); <DecommitQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN]:,
{
let cost_of_decommit_per_word =
UInt32::allocated_constant(cs, zkevm_opcode_defs::ERGS_PER_CODE_WORD_DECOMMITTMENT);
let num_words_in_bytecode =
unsafe { UInt32::from_variable_unchecked(num_words_in_bytecode.get_variable()) };
let default_cost_of_decommittment =
cost_of_decommit_per_word.non_widening_mul(cs, &num_words_in_bytecode);
let boolean_false = Boolean::allocated_constant(cs, false);
let mut decommittment_request = DecommitQuery {
code_hash: *bytecode_hash,
page: *target_memory_page,
is_first: boolean_false,
timestamp: *timestamp_to_use_for_decommittment_request,
};
let oracle = witness_oracle.clone();
let mut dependencies =
Vec::with_capacity(<DecommitQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN + 1);
dependencies.push(should_decommit.get_variable().into());
dependencies.extend(Place::from_variables(
decommittment_request.flatten_as_variables(),
));
let suggested_page = UInt32::allocate_from_closure_and_dependencies(
cs,
move |inputs: &[F]| {
let should_decommit = <bool as WitnessCastable<F, F>>::cast_from_source(inputs[0]);
let mut query =
[F::ZERO; <DecommitQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN];
query.copy_from_slice(&inputs[1..]);
let query: DecommitQueryWitness<F> =
CSAllocatableExt::witness_from_set_of_values(query);
let mut guard = oracle.inner.write().expect("not poisoned");
let witness = guard.get_decommittment_request_suggested_page(&query, should_decommit);
drop(guard);
witness
},
&dependencies,
);
let is_first = UInt32::equals(cs, &target_memory_page, &suggested_page);
let should_charge_for_decommit = Boolean::multi_and(cs, &[*should_decommit, is_first]);
decommittment_request.is_first = is_first;
decommittment_request.page = suggested_page;
let cost_of_decommittment = default_cost_of_decommittment.mask(cs, should_charge_for_decommit);
if crate::config::CIRCUIT_VERSOBE {
if should_decommit.witness_hook(&*cs)().unwrap_or(false) {
dbg!(target_memory_page.witness_hook(&*cs)().unwrap());
dbg!(suggested_page.witness_hook(&*cs)().unwrap());
dbg!(default_cost_of_decommittment.witness_hook(&*cs)().unwrap());
dbg!(cost_of_decommittment.witness_hook(&*cs)().unwrap());
}
}
let (ergs_after_decommit_may_be, uf) =
ergs_remaining.overflowing_sub(cs, cost_of_decommittment);
let not_enough_ergs_to_decommit = uf;
let have_enough_ergs_to_decommit = uf.negated(cs);
let should_decommit = Boolean::multi_and(cs, &[*should_decommit, have_enough_ergs_to_decommit]);
let ergs_remaining_after_decommit = UInt32::conditionally_select(
cs,
have_enough_ergs_to_decommit,
&ergs_after_decommit_may_be,
&ergs_remaining,
);
if crate::config::CIRCUIT_VERSOBE {
if should_decommit.witness_hook(&*cs)().unwrap() {
dbg!(ergs_remaining.witness_hook(&*cs)().unwrap());
dbg!(num_words_in_bytecode.witness_hook(&*cs)().unwrap());
dbg!(default_cost_of_decommittment.witness_hook(&*cs)().unwrap());
dbg!(cost_of_decommittment.witness_hook(&*cs)().unwrap());
dbg!(ergs_after_decommit_may_be.witness_hook(&*cs)().unwrap());
dbg!(should_decommit.witness_hook(&*cs)().unwrap());
dbg!(have_enough_ergs_to_decommit.witness_hook(&*cs)().unwrap());
dbg!(ergs_remaining_after_decommit.witness_hook(&*cs)().unwrap());
}
}
let (new_decommittment_queue_tail, new_decommittment_queue_len) =
add_to_decommittment_queue_inner(
cs,
relations_buffer,
&should_decommit,
current_decommittment_queue_tail,
current_decommittment_queue_len,
&decommittment_request,
_round_function,
);
let target_memory_page = decommittment_request.page;
let unmapped_page = UInt32::allocated_constant(cs, UNMAPPED_PAGE);
let target_memory_page =
UInt32::conditionally_select(cs, should_decommit, &target_memory_page, &unmapped_page);
(
not_enough_ergs_to_decommit,
target_memory_page,
(new_decommittment_queue_tail, new_decommittment_queue_len),
ergs_remaining_after_decommit,
)
}
pub(crate) fn add_to_decommittment_queue_inner<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
>(
cs: &mut CS,
relations_buffer: &mut ArrayVec<
(
Boolean<F>,
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
),
MAX_SPONGES_PER_CYCLE,
>,
should_add: &Boolean<F>,
current_decommittment_queue_tail: &[Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH],
current_decommittment_queue_len: &UInt32<F>,
decommittment_request: &DecommitQuery<F>,
_round_function: &R,
) -> ([Num<F>; FULL_SPONGE_QUEUE_STATE_WIDTH], UInt32<F>)
where
[(); <DecommitQuery<F> as CSAllocatableExt<F>>::INTERNAL_STRUCT_LEN]:,
{
use boojum::gadgets::traits::encodable::CircuitEncodable;
let encoded_request = decommittment_request.encode(cs);
let initial_state = [
encoded_request[0],
encoded_request[1],
encoded_request[2],
encoded_request[3],
encoded_request[4],
encoded_request[5],
encoded_request[6],
encoded_request[7],
current_decommittment_queue_tail[8].get_variable(),
current_decommittment_queue_tail[9].get_variable(),
current_decommittment_queue_tail[10].get_variable(),
current_decommittment_queue_tail[11].get_variable(),
];
use boojum::gadgets::round_function::simulate_round_function;
let final_state = simulate_round_function::<_, _, 8, 12, 4, R>(cs, initial_state, *should_add);
relations_buffer.push((
*should_add,
initial_state.map(|el| Num::from_variable(el)),
final_state.map(|el| Num::from_variable(el)),
));
let final_state = final_state.map(|el| Num::from_variable(el));
let new_decommittment_queue_tail = Num::parallel_select(
cs,
*should_add,
&final_state,
¤t_decommittment_queue_tail,
);
let new_decommittment_queue_len_candidate =
unsafe { current_decommittment_queue_len.increment_unchecked(cs) };
let new_decommittment_queue_len = UInt32::conditionally_select(
cs,
*should_add,
&new_decommittment_queue_len_candidate,
¤t_decommittment_queue_len,
);
(new_decommittment_queue_tail, new_decommittment_queue_len)
}