pub mod trace;
use alloc::vec::Vec;
use core::array;
use miden_core::{
Felt,
deferred::Tag,
field::{PrimeCharacteristicRing, QuadFelt},
utils::RowMajorMatrix,
};
use miden_lifted_air::{AirBuilder, BaseAir, LiftedAir, LiftedAirBuilder};
use miden_precompiles::{CurvePrecompile, UintPrecompile};
use crate::{
ec::{
EcPointMsg,
add::EcGroupAddMsg,
msm::{MsmClaimTermMsg, MsmExprMsg},
},
logup::{
CyclicConstraintLookupBuilder, Deg, LookupAir, LookupBatch, LookupBuilder, LookupColumn,
LookupGroup, NUM_RANDOMNESS, NUM_SIGMA_VALUES, frac_col,
},
relations::{MAX_MESSAGE_WIDTH, NUM_BUS_IDS},
transcript::{
binding::{BindingMsg, ValueTag},
nodes::UintOpId,
poseidon2::{Poseidon2InMsg, Poseidon2OutMsg},
},
uint::{UintValMsg, add::UintAddMsg, mul::UintMulMsg},
utils::{current_main, next_main},
};
pub const COL_ACT: usize = 0;
pub const COL_PERM_SEQ_ID: usize = 1;
pub const COL_LHS_BEGIN: usize = 2;
pub const DIGEST_WIDTH: usize = 4;
pub const COL_LHS_END: usize = COL_LHS_BEGIN + DIGEST_WIDTH;
pub const COL_RHS_BEGIN: usize = COL_LHS_END;
pub const COL_RHS_END: usize = COL_RHS_BEGIN + DIGEST_WIDTH;
pub const COL_H_BEGIN: usize = COL_RHS_END;
pub const COL_H_END: usize = COL_H_BEGIN + DIGEST_WIDTH;
pub const COL_IS_ZERO: usize = COL_H_END;
pub const COL_OUT_MULT: usize = COL_IS_ZERO + 1;
pub const COL_IS_AND: usize = COL_OUT_MULT + 1;
pub const COL_IS_UINT_LEAF: usize = COL_IS_AND + 1;
pub const COL_IS_UINT_OP: usize = COL_IS_UINT_LEAF + 1;
pub const COL_IS_EC_CREATE: usize = COL_IS_UINT_OP + 1;
pub const COL_IS_EC_PAI: usize = COL_IS_EC_CREATE + 1;
pub const COL_IS_EC_OP: usize = COL_IS_EC_PAI + 1;
pub const COL_IS_ADD: usize = COL_IS_EC_OP + 1;
pub const COL_IS_SUB: usize = COL_IS_ADD + 1;
pub const COL_IS_MUL: usize = COL_IS_SUB + 1;
pub const COL_IS_IS: usize = COL_IS_MUL + 1;
pub const COL_IS_PINNED: usize = COL_IS_IS + 1;
pub const COL_PTR: usize = COL_IS_PINNED + 1;
pub const COL_BOUND_PTR: usize = COL_PTR + 1;
pub const COL_TAG_ARG1: usize = COL_BOUND_PTR + 1;
pub const COL_A_PTR: usize = COL_TAG_ARG1 + 1;
pub const COL_B_PTR: usize = COL_A_PTR + 1;
pub const COL_TAG_ARG0: usize = COL_B_PTR + 1;
pub const COL_EC_CONTEXT_GROUP_PTR: usize = COL_TAG_ARG0 + 1;
pub const COL_UINT_VALUE_BOUND_PTR: usize = COL_TAG_ARG1;
pub const COL_PIN_CLAIM_BOUND_PTR: usize = COL_TAG_ARG0;
pub const COL_PIN_CLAIM_PIN_PTR: usize = COL_TAG_ARG1;
pub const COL_EC_CREATE_POINT_PTR: usize = COL_PTR;
pub const COL_EC_CREATE_GROUP_PTR: usize = COL_TAG_ARG1;
pub const COL_EC_CREATE_COORD_BOUND_PTR: usize = COL_BOUND_PTR;
pub const COL_EC_CREATE_X_PTR: usize = COL_A_PTR;
pub const COL_EC_CREATE_Y_PTR: usize = COL_B_PTR;
pub const COL_IS_EC_MSM: usize = COL_EC_CONTEXT_GROUP_PTR + 1;
pub const COL_IS_MSM_LAST: usize = COL_IS_EC_MSM + 1;
pub const COL_MSM_IDX: usize = COL_IS_MSM_LAST + 1;
pub const COL_MSM_EXPR: usize = COL_MSM_IDX + 1;
pub const COL_MSM_IS_HEAD: usize = COL_MSM_EXPR + 1;
pub const NUM_MAIN_COLS: usize = COL_MSM_IS_HEAD + 1;
pub const PUBLIC_ROOT_BEGIN: usize = 0;
pub const PUBLIC_ROOT_END: usize = PUBLIC_ROOT_BEGIN + DIGEST_WIDTH;
pub const NUM_PUBLIC_VALUES: usize = PUBLIC_ROOT_END;
pub const NUM_AUX_COLS: usize = 16;
const COLUMN_SHAPE: [usize; NUM_AUX_COLS] = [1, 2, 2, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 2];
#[derive(Debug, Default, Clone, Copy)]
pub struct TranscriptEvalAir;
impl BaseAir<Felt> for TranscriptEvalAir {
fn width(&self) -> usize {
NUM_MAIN_COLS
}
fn num_public_values(&self) -> usize {
NUM_PUBLIC_VALUES
}
}
impl LiftedAir<Felt, QuadFelt> for TranscriptEvalAir {
fn num_randomness(&self) -> usize {
NUM_RANDOMNESS
}
fn aux_width(&self) -> usize {
NUM_AUX_COLS
}
fn num_aux_values(&self) -> usize {
NUM_SIGMA_VALUES
}
fn build_aux_trace(
&self,
main: &RowMajorMatrix<Felt>,
_air_inputs: &[Felt],
_aux_inputs: &[Felt],
challenges: &[QuadFelt],
) -> (RowMajorMatrix<QuadFelt>, Vec<QuadFelt>) {
trace::build_aux(main, challenges)
}
fn eval<AB: LiftedAirBuilder<F = Felt>>(&self, builder: &mut AB) {
let local: [AB::Var; NUM_MAIN_COLS] = current_main(builder.main(), 0);
let next: [AB::Var; NUM_MAIN_COLS] = next_main(builder.main(), 0);
let act: AB::Expr = local[COL_ACT].into();
let act_next: AB::Expr = next[COL_ACT].into();
let is_zero: AB::Expr = local[COL_IS_ZERO].into();
let out_mult: AB::Expr = local[COL_OUT_MULT].into();
let h: [AB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_H_BEGIN + i].into());
let public_root: [AB::Expr; DIGEST_WIDTH] =
array::from_fn(|i| builder.public_values()[PUBLIC_ROOT_BEGIN + i].into());
builder.assert_bool(local[COL_ACT]);
builder.when_transition().assert_zero((AB::Expr::ONE - act.clone()) * act_next);
builder.assert_bool(local[COL_IS_ZERO]);
for h_i in &h {
builder.assert_zero(is_zero.clone() * h_i.clone());
}
for i in 0..DIGEST_WIDTH {
builder.when_first_row().assert_zero(h[i].clone() - public_root[i].clone());
}
builder.assert_zero((AB::Expr::ONE - act.clone()) * out_mult);
let is_and: AB::Expr = local[COL_IS_AND].into();
let is_uint_leaf: AB::Expr = local[COL_IS_UINT_LEAF].into();
let is_uint_op: AB::Expr = local[COL_IS_UINT_OP].into();
let is_ec_create: AB::Expr = local[COL_IS_EC_CREATE].into();
let is_ec_pai: AB::Expr = local[COL_IS_EC_PAI].into();
let is_ec_op: AB::Expr = local[COL_IS_EC_OP].into();
let is_ec_msm: AB::Expr = local[COL_IS_EC_MSM].into();
let is_msm_last: AB::Expr = local[COL_IS_MSM_LAST].into();
let is_pinned: AB::Expr = local[COL_IS_PINNED].into();
for col in [
COL_IS_AND,
COL_IS_UINT_LEAF,
COL_IS_UINT_OP,
COL_IS_EC_CREATE,
COL_IS_EC_PAI,
COL_IS_EC_OP,
COL_IS_EC_MSM,
COL_IS_MSM_LAST,
COL_IS_PINNED,
] {
builder.assert_bool(local[col]);
}
builder.assert_zero(is_msm_last.clone() * (AB::Expr::ONE - is_ec_msm.clone()));
let is_add: AB::Expr = local[COL_IS_ADD].into();
let is_sub: AB::Expr = local[COL_IS_SUB].into();
let is_mul: AB::Expr = local[COL_IS_MUL].into();
let is_is: AB::Expr = local[COL_IS_IS].into();
for col in [COL_IS_ADD, COL_IS_SUB, COL_IS_MUL, COL_IS_IS] {
builder.assert_bool(local[col]);
}
let is_op = is_add.clone() + is_sub.clone() + is_mul.clone() + is_is.clone();
builder.assert_zero(is_op.clone() - is_uint_op.clone() - is_ec_op.clone());
builder.assert_zero(is_ec_op.clone() * is_mul.clone());
let root_truthy = is_zero.clone() + is_and.clone() + is_is.clone() + is_pinned.clone();
builder.when_first_row().assert_zero(root_truthy - AB::Expr::ONE);
let is_create = is_ec_create.clone() + is_ec_pai.clone();
for i in 0..DIGEST_WIDTH {
let lhs_i: AB::Expr = local[COL_LHS_BEGIN + i].into();
let rhs_i: AB::Expr = local[COL_RHS_BEGIN + i].into();
builder.assert_zero(is_ec_pai.clone() * lhs_i);
builder.assert_zero(is_ec_pai.clone() * rhs_i);
}
let group_ptr: AB::Expr = local[COL_EC_CONTEXT_GROUP_PTR].into();
let is_result_op: AB::Expr = is_op.clone() - is_is.clone();
builder.assert_zero(
is_and
+ is_zero
+ is_uint_leaf.clone()
+ is_uint_op.clone()
+ is_ec_create.clone()
+ is_ec_pai.clone()
+ is_ec_op.clone()
+ is_ec_msm.clone()
- act,
);
let not_uint_leaf: AB::Expr = AB::Expr::ONE - is_uint_leaf.clone();
let ptr: AB::Expr = local[COL_PTR].into();
let bound_ptr: AB::Expr = local[COL_BOUND_PTR].into();
builder.assert_zero(not_uint_leaf.clone() * is_pinned.clone());
builder.assert_zero(
(not_uint_leaf.clone()
- is_result_op
- is_ec_create.clone()
- is_ec_pai
- is_msm_last.clone())
* ptr.clone(),
);
builder.assert_zero(
(not_uint_leaf - is_uint_op.clone() - is_ec_create.clone() - is_ec_msm.clone())
* bound_ptr.clone(),
);
let tag_arg1: AB::Expr = local[COL_TAG_ARG1].into();
let expected_tag_arg1 =
is_uint_leaf * bound_ptr.clone() + is_pinned.clone() * (ptr - bound_ptr.clone());
builder.assert_zero((AB::Expr::ONE - is_create) * (tag_arg1 - expected_tag_arg1));
let a_ptr: AB::Expr = local[COL_A_PTR].into();
let b_ptr: AB::Expr = local[COL_B_PTR].into();
builder.assert_zero(
(AB::Expr::ONE - is_op.clone() - is_ec_create.clone() - is_ec_msm.clone())
* a_ptr.clone(),
);
builder.assert_zero(
(AB::Expr::ONE - is_op - is_ec_create - is_ec_msm.clone()) * b_ptr.clone(),
);
builder.assert_zero(is_is.clone() * (b_ptr - a_ptr));
let tag_arg0: AB::Expr = local[COL_TAG_ARG0].into();
let uint_op_id: AB::Expr = is_add.clone()
+ is_sub.clone() * AB::Expr::from(Felt::from(UintOpId::Sub as u8))
+ is_mul * AB::Expr::from(Felt::from(UintOpId::Mul as u8))
+ is_is.clone() * AB::Expr::from(Felt::from(UintOpId::Is as u8));
let ec_op_id: AB::Expr = is_add
* AB::Expr::from(Felt::from_u32(CurvePrecompile::ADD_OP_ID as u32))
+ is_sub * AB::Expr::from(Felt::from_u32(CurvePrecompile::SUB_OP_ID as u32))
+ is_is.clone() * AB::Expr::from(Felt::from_u32(CurvePrecompile::EQ_OP_ID as u32));
let expected_tag_arg0 =
is_pinned * bound_ptr + is_uint_op * uint_op_id + is_ec_op.clone() * ec_op_id;
builder.assert_zero(tag_arg0 - expected_tag_arg0);
builder.assert_zero(
(AB::Expr::ONE - is_ec_op * (AB::Expr::ONE - is_is) - is_ec_msm.clone()) * group_ptr,
);
let is_ec_msm_next: AB::Expr = next[COL_IS_EC_MSM].into();
let is_msm_head: AB::Expr = local[COL_MSM_IS_HEAD].into();
let is_msm_head_next: AB::Expr = next[COL_MSM_IS_HEAD].into();
builder.assert_bool(local[COL_MSM_IS_HEAD]);
builder.assert_zero(is_msm_head.clone() * (AB::Expr::ONE - is_ec_msm.clone()));
let continues = is_ec_msm.clone() * (AB::Expr::ONE - is_msm_last.clone());
let starts = is_ec_msm_next.clone() * (AB::Expr::ONE - is_ec_msm.clone() + is_msm_last);
builder
.when_first_row()
.assert_zero(is_ec_msm.clone() * (is_msm_head - AB::Expr::ONE));
builder
.when_transition()
.assert_zero(continues.clone() * (AB::Expr::ONE - is_ec_msm_next));
builder
.when_transition()
.assert_zero(continues.clone() * is_msm_head_next.clone());
builder
.when_transition()
.assert_zero(starts.clone() * (is_msm_head_next - AB::Expr::ONE));
let perm_seq_id_local_for_msm: AB::Expr = local[COL_PERM_SEQ_ID].into();
let perm_seq_id_next_for_msm: AB::Expr = next[COL_PERM_SEQ_ID].into();
builder.when_transition().assert_zero(
continues.clone()
* (perm_seq_id_next_for_msm - perm_seq_id_local_for_msm - AB::Expr::ONE),
);
let msm_idx: AB::Expr = local[COL_MSM_IDX].into();
let msm_idx_next: AB::Expr = next[COL_MSM_IDX].into();
builder.when_first_row().assert_zero(is_ec_msm * msm_idx.clone());
builder.when_transition().assert_zero(starts * msm_idx_next.clone());
builder
.when_transition()
.assert_zero(continues.clone() * (msm_idx_next - msm_idx - AB::Expr::ONE));
let msm_expr: AB::Expr = local[COL_MSM_EXPR].into();
let msm_expr_next: AB::Expr = next[COL_MSM_EXPR].into();
let group_local: AB::Expr = local[COL_EC_CONTEXT_GROUP_PTR].into();
let group_next_const: AB::Expr = next[COL_EC_CONTEXT_GROUP_PTR].into();
builder
.when_transition()
.assert_zero(continues.clone() * (msm_expr_next - msm_expr));
builder
.when_transition()
.assert_zero(continues * (group_next_const - group_local));
let mut lb =
CyclicConstraintLookupBuilder::new(builder, self, self.preprocessed_width() > 0);
<Self as LookupAir<_>>::eval(self, &mut lb);
}
}
impl<LB> LookupAir<LB> for TranscriptEvalAir
where
LB: LookupBuilder<F = Felt>,
{
fn num_columns(&self) -> usize {
NUM_AUX_COLS
}
fn column_shape(&self) -> &[usize] {
&COLUMN_SHAPE
}
fn max_message_width(&self) -> usize {
MAX_MESSAGE_WIDTH
}
fn num_bus_ids(&self) -> usize {
NUM_BUS_IDS
}
fn eval(&self, builder: &mut LB) {
let local: [LB::Var; NUM_MAIN_COLS] = current_main(builder.main(), 0);
let is_and: LB::Expr = local[COL_IS_AND].into();
let is_zero: LB::Expr = local[COL_IS_ZERO].into();
let is_uint_leaf: LB::Expr = local[COL_IS_UINT_LEAF].into();
let is_pinned: LB::Expr = local[COL_IS_PINNED].into();
let is_add: LB::Expr = local[COL_IS_ADD].into();
let is_sub: LB::Expr = local[COL_IS_SUB].into();
let is_mul: LB::Expr = local[COL_IS_MUL].into();
let is_is: LB::Expr = local[COL_IS_IS].into();
let perm_seq_id: LB::Expr = local[COL_PERM_SEQ_ID].into();
let out_mult: LB::Expr = local[COL_OUT_MULT].into();
let ptr: LB::Expr = local[COL_PTR].into();
let bound_ptr: LB::Expr = local[COL_BOUND_PTR].into();
let tag_arg1: LB::Expr = local[COL_TAG_ARG1].into();
let a_ptr: LB::Expr = local[COL_A_PTR].into();
let b_ptr: LB::Expr = local[COL_B_PTR].into();
let tag_arg0: LB::Expr = local[COL_TAG_ARG0].into();
let is_uint_op: LB::Expr = local[COL_IS_UINT_OP].into();
let is_ec_create: LB::Expr = local[COL_IS_EC_CREATE].into();
let is_ec_pai: LB::Expr = local[COL_IS_EC_PAI].into();
let is_ec_op: LB::Expr = local[COL_IS_EC_OP].into();
let is_create = is_ec_create.clone() + is_ec_pai;
let is_ec_msm: LB::Expr = local[COL_IS_EC_MSM].into();
let lhs: [LB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_LHS_BEGIN + i].into());
let rhs: [LB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_RHS_BEGIN + i].into());
let h: [LB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_H_BEGIN + i].into());
let is_value_op: LB::Expr = is_uint_op.clone() * (LB::Expr::ONE - is_is.clone());
let node: LB::Expr = is_and.clone()
+ is_uint_leaf.clone()
+ is_uint_op.clone()
+ is_create.clone()
+ is_ec_op.clone()
+ is_ec_msm.clone();
let and_gate: LB::Expr = is_and.clone();
let op_lhs_gate: LB::Expr = is_uint_op.clone();
let op_rhs_gate: LB::Expr = is_uint_op.clone();
let neg_out_mult: LB::Expr = LB::Expr::ZERO - out_mult;
let and_provide: LB::Expr =
neg_out_mult.clone() * (is_and.clone() + is_zero + is_is.clone());
let uint_gate: LB::Expr = is_uint_leaf.clone();
let uint_provide: LB::Expr = neg_out_mult * (is_uint_leaf.clone() + is_value_op);
let transient: LB::Expr = LB::Expr::ONE - is_pinned.clone();
let and_cap = Tag::AND.as_word();
let static_node = is_and
+ is_uint_leaf.clone()
+ is_uint_op.clone()
+ is_create.clone()
+ is_ec_op.clone();
let uint_precompile_id = LB::Expr::from(UintPrecompile::id());
let curve_precompile_id = LB::Expr::from(CurvePrecompile::id());
let pin_claim_tag =
LB::Expr::from(Felt::from(crate::transcript::nodes::UINT_PIN_CLAIM_TAG));
let cap = [
and_gate.clone() * LB::Expr::from(and_cap[0])
+ (is_uint_leaf + op_lhs_gate.clone()) * uint_precompile_id.clone()
+ is_pinned * (pin_claim_tag - uint_precompile_id)
+ (is_create.clone() + is_ec_op.clone()) * curve_precompile_id,
and_gate.clone() * LB::Expr::from(and_cap[1]) + tag_arg0,
and_gate.clone() * LB::Expr::from(and_cap[2]) + tag_arg1,
and_gate.clone() * LB::Expr::from(and_cap[3]),
];
let one_deg = Deg { v: 1, u: 1 };
let two_deg = Deg { v: 2, u: 1 };
let mixed_deg = Deg { v: 1, u: 2 };
let single_deg = Deg { v: 1, u: 2 };
let pair_deg = Deg { v: 3, u: 2 };
frac_col!(
builder,
"binding-and",
single_deg,
("consume-lhs", and_gate.clone(), BindingMsg::truth(lhs.clone()), one_deg),
);
frac_col!(
builder,
"binding-and",
pair_deg,
("consume-rhs", and_gate, BindingMsg::truth(rhs.clone()), one_deg),
("provide-h", and_provide, BindingMsg::truth(h.clone()), two_deg),
);
frac_col!(
builder,
"unhash-p2",
pair_deg,
(
"p2in-rate0",
node.clone(),
Poseidon2InMsg::rate0(perm_seq_id.clone(), lhs.clone()),
one_deg
),
(
"p2in-rate1",
node.clone(),
Poseidon2InMsg::rate1(perm_seq_id.clone(), rhs.clone()),
one_deg
),
);
frac_col!(
builder,
"unhash-p2",
pair_deg,
("p2in-cap", static_node, Poseidon2InMsg::cap(perm_seq_id.clone(), cap), one_deg),
(
"p2out",
node.clone() - is_ec_msm.clone() + local[COL_IS_MSM_LAST].into(),
Poseidon2OutMsg { perm_seq_id, digest: h.clone() },
one_deg
),
);
frac_col!(
builder,
"binding-uint",
single_deg,
(
"consume-uint",
uint_gate,
UintValMsg {
ptr: ptr.clone(),
bound_ptr: bound_ptr.clone(),
limbs: array::from_fn(|i| {
if i < 4 { lhs[i].clone() } else { rhs[i - 4].clone() }
}),
},
one_deg
),
);
frac_col!(
builder,
"binding-uint",
single_deg,
(
"provide-binding",
uint_provide,
BindingMsg {
h,
value_tag: transient.clone() * LB::Expr::from(Felt::from(ValueTag::Uint as u8)),
ptr: transient.clone() * ptr.clone(),
bound_ptr: transient * bound_ptr.clone(),
},
two_deg
),
);
frac_col!(
builder,
"binding-op-children",
pair_deg,
(
"consume-lhs-uint",
op_lhs_gate.clone() + is_ec_create.clone(),
BindingMsg {
h: lhs,
value_tag: LB::Expr::from(Felt::from(ValueTag::Uint as u8)),
ptr: a_ptr.clone(),
bound_ptr: bound_ptr.clone(),
},
one_deg
),
(
"consume-rhs-uint",
op_rhs_gate + is_ec_create.clone(),
BindingMsg {
h: rhs,
value_tag: LB::Expr::from(Felt::from(ValueTag::Uint as u8)),
ptr: b_ptr.clone(),
bound_ptr: bound_ptr.clone(),
},
one_deg
),
);
frac_col!(
builder,
"uint-relations",
single_deg,
(
"consume-uintadd",
is_uint_op.clone() * (is_add.clone() + is_sub.clone()),
UintAddMsg {
bound_ptr: bound_ptr.clone(),
a_ptr: is_add.clone() * a_ptr.clone() + is_sub.clone() * b_ptr.clone(),
b_ptr: is_add.clone() * b_ptr.clone() + is_sub.clone() * ptr.clone(),
c_ptr: is_add.clone() * ptr.clone() + is_sub.clone() * a_ptr.clone(),
nz: LB::Expr::ZERO,
},
mixed_deg
),
);
frac_col!(
builder,
"uint-relations",
single_deg,
(
"consume-uintmul",
is_mul,
UintMulMsg {
kappa_a: LB::Expr::ONE,
kappa_c: LB::Expr::ZERO,
a_ptr,
b_ptr,
c_ptr: bound_ptr.clone(),
r_ptr: ptr,
bound_ptr,
is_sub: LB::Expr::ZERO,
},
one_deg
),
);
let g_lhs: [LB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_LHS_BEGIN + i].into());
let g_rhs: [LB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_RHS_BEGIN + i].into());
let g_h: [LB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_H_BEGIN + i].into());
let ec_value_ptr: LB::Expr = local[COL_PTR].into();
let ec_op_lhs_ptr: LB::Expr = local[COL_A_PTR].into();
let ec_op_rhs_ptr: LB::Expr = local[COL_B_PTR].into();
let create_point_ptr: LB::Expr = local[COL_EC_CREATE_POINT_PTR].into();
let create_x_ptr: LB::Expr = local[COL_EC_CREATE_X_PTR].into();
let create_y_ptr: LB::Expr = local[COL_EC_CREATE_Y_PTR].into();
let create_group_ptr: LB::Expr = local[COL_EC_CREATE_GROUP_PTR].into();
let ec_context_group_ptr: LB::Expr = local[COL_EC_CONTEXT_GROUP_PTR].into();
let create_is_pai: LB::Expr = local[COL_IS_EC_PAI].into();
let g_is_msm_last: LB::Expr = local[COL_IS_MSM_LAST].into();
let ec_binary: LB::Expr = is_ec_op.clone();
let ec_result: LB::Expr = is_ec_op.clone() * (LB::Expr::ONE - is_is);
let g_out_mult: LB::Expr = local[COL_OUT_MULT].into();
let g_neg_out_mult: LB::Expr = LB::Expr::ZERO - g_out_mult;
frac_col!(
builder,
"binding-group",
pair_deg,
(
"consume-p",
is_ec_op.clone(),
BindingMsg::group(g_lhs.clone(), ec_op_lhs_ptr.clone()),
one_deg
),
(
"consume-q",
ec_binary.clone(),
BindingMsg::group(g_rhs.clone(), ec_op_rhs_ptr.clone()),
one_deg
),
);
frac_col!(
builder,
"binding-group",
single_deg,
(
"provide-group",
g_neg_out_mult * (is_create.clone() + ec_result.clone() + g_is_msm_last.clone()),
BindingMsg::group(g_h.clone(), ec_value_ptr.clone()),
two_deg
),
);
frac_col!(
builder,
"ec-relations",
single_deg,
(
"consume-ecpoint",
is_create.clone(),
EcPointMsg {
point_ptr: create_point_ptr.clone(),
group_ptr: create_group_ptr.clone(),
x_ptr: create_x_ptr.clone(),
y_ptr: create_y_ptr.clone(),
is_pai: create_is_pai.clone(),
},
one_deg
),
);
frac_col!(
builder,
"ec-relations",
single_deg,
(
"consume-ecgroupadd",
ec_result.clone(),
EcGroupAddMsg {
group_ptr: ec_context_group_ptr.clone(),
p_ptr: is_add.clone() * ec_op_lhs_ptr.clone()
+ is_sub.clone() * ec_value_ptr.clone(),
q_ptr: (is_add.clone() + is_sub.clone()) * ec_op_rhs_ptr.clone(),
r_ptr: is_add.clone() * ec_value_ptr.clone()
+ is_sub.clone() * ec_op_lhs_ptr.clone(),
},
mixed_deg
),
);
let d_perm_seq_id: LB::Expr = local[COL_PERM_SEQ_ID].into();
let d_is_msm_head: LB::Expr = local[COL_MSM_IS_HEAD].into();
let d_msm_iv = [
LB::Expr::from(CurvePrecompile::id()),
LB::Expr::from(Felt::from_u32(CurvePrecompile::MSM_OP_ID as u32)),
LB::Expr::ZERO,
LB::Expr::ZERO,
];
frac_col!(
builder,
"msm-head-cap",
single_deg,
(
"p2in-cap-msm-head",
d_is_msm_head,
Poseidon2InMsg::cap(d_perm_seq_id, d_msm_iv),
one_deg
),
);
let m_msm: LB::Expr = local[COL_IS_EC_MSM].into();
let m_last: LB::Expr = local[COL_IS_MSM_LAST].into();
let m_lhs: [LB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_LHS_BEGIN + i].into());
let m_rhs: [LB::Expr; DIGEST_WIDTH] = array::from_fn(|i| local[COL_RHS_BEGIN + i].into());
let m_a: LB::Expr = local[COL_A_PTR].into();
let m_b: LB::Expr = local[COL_B_PTR].into();
let m_bound: LB::Expr = local[COL_BOUND_PTR].into();
let m_group: LB::Expr = local[COL_EC_CONTEXT_GROUP_PTR].into();
let m_val: LB::Expr = local[COL_PTR].into();
let m_idx: LB::Expr = local[COL_MSM_IDX].into();
let m_expr: LB::Expr = local[COL_MSM_EXPR].into();
frac_col!(
builder,
"ec-msm-absorb",
pair_deg,
(
"consume-base-group",
m_msm.clone(),
BindingMsg::group(m_lhs, m_a.clone()),
one_deg
),
(
"consume-scalar-uint",
m_msm.clone(),
BindingMsg {
h: m_rhs,
value_tag: LB::Expr::from(Felt::from(ValueTag::Uint as u8)),
ptr: m_b.clone(),
bound_ptr: m_bound,
},
one_deg
),
);
frac_col!(
builder,
"ec-msm-absorb",
pair_deg,
(
"consume-msmclaimterm",
m_msm,
MsmClaimTermMsg {
expr_ptr: m_expr.clone(),
base_ptr: m_a,
scalar_ptr: m_b
},
one_deg
),
(
"consume-msmexpr",
m_last,
MsmExprMsg {
expr_ptr: m_expr,
group_ptr: m_group,
val_ptr: m_val,
k: m_idx + LB::Expr::ONE,
},
one_deg
),
);
}
}