Problem
The EcOps trait (and its supertrait FieldArith) currently acts as a type-level tag rather than a proper interface. Every method takes compiler,
range_checks, and params as explicit arguments:
pub trait EcOps: FieldArith {
fn point_double(
compiler: &mut NoirToR1CSCompiler,
range_checks: &mut BTreeMap<u32, Vec<usize>>,
params: &MultiLimbParams,
p: EcPoint,
) -> EcPoint;
// ...
}
NativeEcOps ignores _range_checks entirely, making the signature dishonest about what each impl actually needs. The same pattern exists in FieldArith
(field_add, field_sub, etc.).
Ref: #310 (comment)
Proposed Solution
Make methods take &mut self so each impl carries its own state:
pub trait EcOps {
fn point_double(&mut self, p: EcPoint) -> EcPoint;
fn point_add(&mut self, p1: EcPoint, p2: EcPoint) -> EcPoint;
fn verify_on_curve(&mut self, p: EcPoint);
}
Each impl struct would hold the state it actually needs:
pub struct NativeEcOps<'a> {
compiler: &'a mut NoirToR1CSCompiler,
params: &'a MultiLimbParams,
// no range_checks — native doesn't need them
}
pub struct NonNativeEcOps<'a> {
compiler: &'a mut NoirToR1CSCompiler,
range_checks: &'a mut BTreeMap<u32, Vec<usize>>,
params: &'a MultiLimbParams,
}
Same refactor applies to the FieldArith supertrait — field_add, field_sub, etc. should also move to &mut self.
Problem
The EcOps trait (and its supertrait FieldArith) currently acts as a type-level tag rather than a proper interface. Every method takes compiler,
range_checks, and params as explicit arguments:
NativeEcOps ignores _range_checks entirely, making the signature dishonest about what each impl actually needs. The same pattern exists in FieldArith
(field_add, field_sub, etc.).
Ref: #310 (comment)
Proposed Solution
Make methods take &mut self so each impl carries its own state:
Each impl struct would hold the state it actually needs:
Same refactor applies to the FieldArith supertrait — field_add, field_sub, etc. should also move to &mut self.