use std::iter::once;
use crate::fsm_input_output::circuit_inputs::INPUT_OUTPUT_COMMITMENT_LENGTH;
use crate::fsm_input_output::commit_variable_length_encodable_item;
use crate::base_structures::vm_state::*;
use crate::fsm_input_output::*;
use crate::linear_hasher::input::LinearHasherInputData;
use boojum::gadgets::u32::UInt32;
use boojum::algebraic_props::round_function::AlgebraicRoundFunction;
use boojum::cs::traits::cs::ConstraintSystem;
use boojum::field::SmallField;
use boojum::gadgets::traits::round_function::CircuitRoundFunction;
use boojum::gadgets::{boolean::Boolean, num::Num, queue::*, traits::selectable::Selectable};
use crate::base_structures::precompile_input_outputs::*;
use crate::log_sorter::input::*;
use crate::storage_application::input::*;
use boojum::gadgets::u8::UInt8;
use super::*;
pub const NUM_CIRCUIT_TYPES_TO_SCHEDULE: usize = crate::recursion::NUM_BASE_LAYER_CIRCUITS;
#[derive(Derivative, serde::Serialize, serde::Deserialize)]
#[derivative(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum BaseLayerCircuitType {
None = 0,
VM = 1,
DecommitmentsFilter = 2,
Decommiter = 3,
LogDemultiplexer = 4,
KeccakPrecompile = 5,
Sha256Precompile = 6,
EcrecoverPrecompile = 7,
RamValidation = 8,
StorageFilter = 9,
StorageApplicator = 10,
EventsRevertsFilter = 11,
L1MessagesRevertsFilter = 12,
L1MessagesHasher = 13,
TransientStorageChecker = 14,
Secp256r1Verify = 15,
ModexpPrecompile = 16,
ECAddPrecompile = 17,
ECMulPrecompile = 18,
ECPairingPrecompile = 19,
EIP4844Repack = 255,
}
impl BaseLayerCircuitType {
pub fn from_numeric_value(value: u8) -> Self {
let t: Self = match value {
a if a == Self::VM as u8 => Self::VM,
a if a == Self::DecommitmentsFilter as u8 => Self::DecommitmentsFilter,
a if a == Self::Decommiter as u8 => Self::Decommiter,
a if a == Self::LogDemultiplexer as u8 => Self::LogDemultiplexer,
a if a == Self::KeccakPrecompile as u8 => Self::KeccakPrecompile,
a if a == Self::Sha256Precompile as u8 => Self::Sha256Precompile,
a if a == Self::EcrecoverPrecompile as u8 => Self::EcrecoverPrecompile,
a if a == Self::RamValidation as u8 => Self::RamValidation,
a if a == Self::StorageFilter as u8 => Self::StorageFilter,
a if a == Self::StorageApplicator as u8 => Self::StorageApplicator,
a if a == Self::EventsRevertsFilter as u8 => Self::EventsRevertsFilter,
a if a == Self::L1MessagesRevertsFilter as u8 => Self::L1MessagesRevertsFilter,
a if a == Self::L1MessagesHasher as u8 => Self::L1MessagesHasher,
a if a == Self::TransientStorageChecker as u8 => Self::TransientStorageChecker,
a if a == Self::Secp256r1Verify as u8 => Self::Secp256r1Verify,
a if a == Self::ModexpPrecompile as u8 => Self::ModexpPrecompile,
a if a == Self::ECAddPrecompile as u8 => Self::ECAddPrecompile,
a if a == Self::ECMulPrecompile as u8 => Self::ECMulPrecompile,
a if a == Self::ECPairingPrecompile as u8 => Self::ECPairingPrecompile,
a if a == Self::EIP4844Repack as u8 => Self::EIP4844Repack,
_ => {
panic!("unknown circuit type {}", value);
}
};
t
}
pub fn as_iter_u8() -> impl Iterator<Item = u8> {
(BaseLayerCircuitType::VM as u8..=BaseLayerCircuitType::ECPairingPrecompile as u8)
.chain(once(BaseLayerCircuitType::EIP4844Repack as u8))
}
}
#[track_caller]
pub(crate) fn compute_precompile_commitment<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
>(
cs: &mut CS,
precompile_queue_state: &QueueState<F, QUEUE_STATE_WIDTH>,
mem_queue_state_before: &QueueState<F, FULL_SPONGE_QUEUE_STATE_WIDTH>,
mem_queue_state_after: &QueueState<F, FULL_SPONGE_QUEUE_STATE_WIDTH>,
round_function: &R,
) -> (
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
) {
let input_data = PrecompileFunctionInputData {
initial_log_queue_state: precompile_queue_state.clone(),
initial_memory_queue_state: mem_queue_state_before.clone(),
};
let input_data_commitment =
commit_variable_length_encodable_item(cs, &input_data, round_function);
let output_data = PrecompileFunctionOutputData {
final_memory_state: mem_queue_state_after.clone(),
};
let output_data_commitment =
commit_variable_length_encodable_item(cs, &output_data, round_function);
(input_data_commitment, output_data_commitment)
}
#[track_caller]
pub(crate) fn compute_storage_sorter_circuit_commitment<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
>(
cs: &mut CS,
shard_id: UInt8<F>,
queue_state_before: &QueueState<F, QUEUE_STATE_WIDTH>,
intermediate_queue_state: &QueueTailState<F, QUEUE_STATE_WIDTH>,
queue_state_after: &QueueState<F, QUEUE_STATE_WIDTH>,
round_function: &R,
) -> (
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
) {
let mut full_state = QueueState::empty(cs);
full_state.tail = *intermediate_queue_state;
let input_data = StorageDeduplicatorInputData {
shard_id_to_process: shard_id,
unsorted_log_queue_state: queue_state_before.clone(),
intermediate_sorted_queue_state: full_state,
};
let input_data_commitment =
commit_variable_length_encodable_item(cs, &input_data, round_function);
let output_data = StorageDeduplicatorOutputData {
final_sorted_queue_state: queue_state_after.clone(),
};
let output_data_commitment =
commit_variable_length_encodable_item(cs, &output_data, round_function);
(input_data_commitment, output_data_commitment)
}
#[track_caller]
pub(crate) fn compute_filter_circuit_commitment<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
>(
cs: &mut CS,
queue_state_before: &QueueState<F, QUEUE_STATE_WIDTH>,
intermediate_queue_state: &QueueTailState<F, QUEUE_STATE_WIDTH>,
queue_state_after: &QueueState<F, QUEUE_STATE_WIDTH>,
round_function: &R,
) -> (
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
) {
let mut full_state = QueueState::empty(cs);
full_state.tail = *intermediate_queue_state;
let input_data = EventsDeduplicatorInputData {
initial_log_queue_state: queue_state_before.clone(),
intermediate_sorted_queue_state: full_state,
};
let input_data_commitment =
commit_variable_length_encodable_item(cs, &input_data, round_function);
let output_data = EventsDeduplicatorOutputData {
final_queue_state: queue_state_after.clone(),
};
let output_data_commitment =
commit_variable_length_encodable_item(cs, &output_data, round_function);
(input_data_commitment, output_data_commitment)
}
#[track_caller]
pub(crate) fn compute_transient_storage_checker_circuit_commitment<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
>(
cs: &mut CS,
queue_state_before: &QueueState<F, QUEUE_STATE_WIDTH>,
intermediate_queue_state: &QueueTailState<F, QUEUE_STATE_WIDTH>,
round_function: &R,
) -> (
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
) {
use crate::transient_storage_validity_by_grand_product::input::TransientStorageDeduplicatorInputData;
let mut full_state = QueueState::empty(cs);
full_state.tail = *intermediate_queue_state;
let transient_input_data = TransientStorageDeduplicatorInputData {
unsorted_log_queue_state: queue_state_before.clone(),
intermediate_sorted_queue_state: full_state,
};
let input_data_commitment =
commit_variable_length_encodable_item(cs, &transient_input_data, round_function);
let output_data = ();
let output_data_commitment =
commit_variable_length_encodable_item(cs, &output_data, round_function);
(input_data_commitment, output_data_commitment)
}
#[track_caller]
pub(crate) fn compute_storage_applicator_circuit_commitment<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
>(
cs: &mut CS,
storage_queue_state: &QueueState<F, QUEUE_STATE_WIDTH>,
initial_root: &[UInt8<F>; 32],
initial_enumeration_counter: &[UInt32<F>; 2],
final_root: &[UInt8<F>; 32],
final_enumeration_counter: &[UInt32<F>; 2],
rollup_state_diff_for_compression: &[UInt8<F>; 32],
shard_id: u8,
round_function: &R,
) -> (
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
) {
let shard_id = UInt8::allocated_constant(cs, shard_id);
let input_data = StorageApplicationInputData {
initial_next_enumeration_counter: *initial_enumeration_counter,
shard: shard_id,
initial_root_hash: *initial_root,
storage_application_log_state: storage_queue_state.clone(),
};
let input_data_commitment =
commit_variable_length_encodable_item(cs, &input_data, round_function);
let output_data = StorageApplicationOutputData {
new_root_hash: *final_root,
new_next_enumeration_counter: *final_enumeration_counter,
state_diffs_keccak256_hash: *rollup_state_diff_for_compression,
};
let output_data_commitment =
commit_variable_length_encodable_item(cs, &output_data, round_function);
(input_data_commitment, output_data_commitment)
}
#[track_caller]
pub(crate) fn compute_hasher_circuit_commitment<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4> + AlgebraicRoundFunction<F, 8, 12, 4>,
>(
cs: &mut CS,
input_queue_state: &QueueState<F, QUEUE_STATE_WIDTH>,
pubdata_hash: &[UInt8<F>; 32],
round_function: &R,
) -> (
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
[Num<F>; CLOSED_FORM_COMMITTMENT_LENGTH],
) {
let input_data = LinearHasherInputData {
queue_state: input_queue_state.clone(),
};
let input_data_commitment =
commit_variable_length_encodable_item(cs, &input_data, round_function);
let output_data = LinearHasherOutputData {
keccak256_hash: *pubdata_hash,
};
let output_data_commitment =
commit_variable_length_encodable_item(cs, &output_data, round_function);
(input_data_commitment, output_data_commitment)
}
#[track_caller]
pub(crate) fn conditionally_enforce_circuit_commitment<F: SmallField, CS: ConstraintSystem<F>>(
cs: &mut CS,
should_validate: Boolean<F>,
actual_commitment: &[Num<F>; INPUT_OUTPUT_COMMITMENT_LENGTH],
sample_commitment: &[Num<F>; INPUT_OUTPUT_COMMITMENT_LENGTH],
) {
for (a, b) in actual_commitment.iter().zip(sample_commitment.iter()) {
Num::conditionally_enforce_equal(cs, should_validate, a, b);
}
}
#[track_caller]
pub(crate) fn conditionally_select_queue_tail<
F: SmallField,
CS: ConstraintSystem<F>,
const N: usize,
>(
cs: &mut CS,
flag: Boolean<F>,
a: &QueueTailState<F, N>,
b: &QueueTailState<F, N>,
) -> QueueTailState<F, N> {
let tail = Num::parallel_select(cs, flag, &a.tail, &b.tail);
let length = UInt32::conditionally_select(cs, flag, &a.length, &b.length);
QueueTailState { tail, length }
}
pub(crate) fn finalize_queue_state<
F: SmallField,
CS: ConstraintSystem<F>,
R: CircuitRoundFunction<F, 8, 12, 4>,
const N: usize,
const M: usize,
>(
cs: &mut CS,
state: &QueueTailState<F, N>,
_round_function: &R,
) -> [Num<F>; M] {
let mut to_absorb = vec![];
to_absorb.extend(state.tail);
let one_num = Num::allocated_constant(cs, F::ONE);
let zero_num = Num::zero(cs);
to_absorb.push(one_num);
let mut multiple = to_absorb.len() / 8;
if to_absorb.len() % 8 != 0 {
multiple += 1;
}
to_absorb.resize(multiple * 8, zero_num);
let mut state = [zero_num; 12];
for chunk in to_absorb.array_chunks::<8>() {
let els_to_keep = R::split_capacity_elements(&state.map(|el| el.get_variable()))
.map(|el| Num::from_variable(el));
state = R::absorb_with_replacement_over_nums(cs, *chunk, els_to_keep);
state = R::compute_round_function_over_nums(cs, state);
}
R::state_into_commitment::<M>(&state.map(|el| el.get_variable()))
.map(|el| Num::from_variable(el))
}