use std::cell::RefCell;
use crate::rlc::{circuit::PureRlcConfig, RLC_PHASE};
use getset::CopyGetters;
use halo2_base::{
gates::{
circuit::CircuitBuilderStage,
flex_gate::{
threads::single_phase::{assign_with_constraints, assign_witnesses},
ThreadBreakPoints,
},
},
halo2_proofs::circuit::Region,
utils::ScalarField,
virtual_region::{
copy_constraints::SharedCopyConstraintManager, manager::VirtualRegionManager,
},
Context,
};
#[derive(Clone, Debug, Default, CopyGetters)]
pub struct RlcManager<F: ScalarField> {
pub threads: Vec<Context<F>>,
pub copy_manager: SharedCopyConstraintManager<F>,
#[getset(get_copy = "pub")]
witness_gen_only: bool,
#[getset(get_copy = "pub")]
pub(crate) use_unknown: bool,
pub break_points: RefCell<Option<ThreadBreakPoints>>,
}
impl<F: ScalarField> RlcManager<F> {
pub fn new(witness_gen_only: bool, copy_manager: SharedCopyConstraintManager<F>) -> Self {
Self {
threads: vec![],
witness_gen_only,
use_unknown: false,
copy_manager,
..Default::default()
}
}
pub fn phase(&self) -> usize {
RLC_PHASE
}
pub fn from_stage(
stage: CircuitBuilderStage,
copy_manager: SharedCopyConstraintManager<F>,
) -> Self {
Self::new(stage.witness_gen_only(), copy_manager)
.unknown(stage == CircuitBuilderStage::Keygen)
}
pub fn unknown(self, use_unknown: bool) -> Self {
Self { use_unknown, ..self }
}
pub fn set_copy_manager(&mut self, copy_manager: SharedCopyConstraintManager<F>) {
for ctx in &mut self.threads {
ctx.copy_manager = copy_manager.clone();
}
self.copy_manager = copy_manager;
}
pub fn use_copy_manager(mut self, copy_manager: SharedCopyConstraintManager<F>) -> Self {
self.set_copy_manager(copy_manager);
self
}
pub fn clear(&mut self) {
self.threads.clear();
self.copy_manager.lock().unwrap().clear();
}
pub fn main(&mut self) -> &mut Context<F> {
if self.threads.is_empty() {
self.new_thread()
} else {
self.threads.last_mut().unwrap()
}
}
pub fn thread_count(&self) -> usize {
self.threads.len()
}
pub(crate) fn new_context(&self, context_id: usize) -> Context<F> {
Context::new(
self.witness_gen_only,
RLC_PHASE,
"axiom-eth:RlcManager:SecondPhase",
context_id,
self.copy_manager.clone(),
)
}
pub fn new_thread(&mut self) -> &mut Context<F> {
let context_id = self.thread_count();
self.threads.push(self.new_context(context_id));
self.threads.last_mut().unwrap()
}
pub fn total_advice(&self) -> usize {
self.threads.iter().map(|ctx| ctx.advice.len()).sum::<usize>()
}
}
impl<F: ScalarField> VirtualRegionManager<F> for RlcManager<F> {
type Config = PureRlcConfig<F>;
fn assign_raw(&self, rlc_config: &Self::Config, region: &mut Region<F>) {
if self.witness_gen_only {
let binding = self.break_points.borrow();
let break_points = binding.as_ref().expect("break points not set");
assign_witnesses(&self.threads, &rlc_config.basic_gates, region, break_points);
} else {
let mut copy_manager = self.copy_manager.lock().unwrap();
assert!(
self.threads.iter().all(|ctx| ctx.phase() == RLC_PHASE),
"all threads must be in RLC_PHASE"
);
let break_points = assign_with_constraints::<F, 3>(
&self.threads,
&rlc_config.basic_gates,
region,
&mut copy_manager,
rlc_config.usable_rows,
self.use_unknown,
);
let mut bp = self.break_points.borrow_mut();
if let Some(bp) = bp.as_ref() {
assert_eq!(bp, &break_points, "break points don't match");
} else {
*bp = Some(break_points);
}
}
}
}