sp1_core_machine/operations/
global_interaction.rs1use super::poseidon2::{
2 air::{eval_external_round, eval_internal_rounds},
3 permutation::Poseidon2Cols,
4 trace::populate_perm_deg3,
5 Poseidon2Operation, NUM_EXTERNAL_ROUNDS,
6};
7use p3_air::AirBuilder;
8use p3_field::{AbstractExtensionField, AbstractField, Field, PrimeField32};
9use sp1_core_executor::ByteOpcode;
10use sp1_derive::AlignedBorrow;
11use sp1_stark::{
12 air::SP1AirBuilder,
13 septic_curve::{SepticCurve, CURVE_WITNESS_DUMMY_POINT_X, CURVE_WITNESS_DUMMY_POINT_Y},
14 septic_extension::{SepticBlock, SepticExtension},
15};
16
17#[derive(AlignedBorrow, Clone, Copy)]
19#[repr(C)]
20pub struct GlobalInteractionOperation<T: Copy> {
21 pub offset_bits: [T; 8],
22 pub x_coordinate: SepticBlock<T>,
23 pub y_coordinate: SepticBlock<T>,
24 pub y6_bit_decomp: [T; 30],
25 pub range_check_witness: T,
26 pub permutation: Poseidon2Operation<T>,
27}
28
29impl<F: PrimeField32> GlobalInteractionOperation<F> {
30 pub fn get_digest(
31 values: SepticBlock<u32>,
32 is_receive: bool,
33 kind: u8,
34 ) -> (SepticCurve<F>, u8, [F; 16], [F; 16]) {
35 let x_start = SepticExtension::<F>::from_base_fn(|i| F::from_canonical_u32(values.0[i])) +
36 SepticExtension::from_base(F::from_canonical_u32((kind as u32) << 16));
37 let (point, offset, m_trial, m_hash) = SepticCurve::<F>::lift_x(x_start);
38 if !is_receive {
39 return (point.neg(), offset, m_trial, m_hash);
40 }
41 (point, offset, m_trial, m_hash)
42 }
43
44 pub fn populate(
45 &mut self,
46 values: SepticBlock<u32>,
47 is_receive: bool,
48 is_real: bool,
49 kind: u8,
50 ) {
51 if is_real {
52 let (point, offset, m_trial, m_hash) = Self::get_digest(values, is_receive, kind);
53 for i in 0..8 {
54 self.offset_bits[i] = F::from_canonical_u8((offset >> i) & 1);
55 }
56 self.x_coordinate = SepticBlock::<F>::from(point.x.0);
57 self.y_coordinate = SepticBlock::<F>::from(point.y.0);
58 let range_check_value = if is_receive {
59 point.y.0[6].as_canonical_u32() - 1
60 } else {
61 point.y.0[6].as_canonical_u32() - F::ORDER_U32.div_ceil(2)
62 };
63 let mut top_4_bits = F::zero();
64 for i in 0..30 {
65 self.y6_bit_decomp[i] = F::from_canonical_u32((range_check_value >> i) & 1);
66 if i >= 26 {
67 top_4_bits += self.y6_bit_decomp[i];
68 }
69 }
70 top_4_bits -= F::from_canonical_u32(4);
71 self.range_check_witness = top_4_bits.inverse();
72 self.permutation = populate_perm_deg3(m_trial, Some(m_hash));
73
74 assert_eq!(self.x_coordinate.0[0], self.permutation.permutation.perm_output()[0]);
75 } else {
76 self.populate_dummy();
77 assert_eq!(self.x_coordinate.0[0], self.permutation.permutation.perm_output()[0]);
78 }
79 }
80
81 pub fn populate_dummy(&mut self) {
82 for i in 0..8 {
83 self.offset_bits[i] = F::zero();
84 }
85 self.x_coordinate = SepticBlock::<F>::from_base_fn(|i| {
86 F::from_canonical_u32(CURVE_WITNESS_DUMMY_POINT_X[i])
87 });
88 self.y_coordinate = SepticBlock::<F>::from_base_fn(|i| {
89 F::from_canonical_u32(CURVE_WITNESS_DUMMY_POINT_Y[i])
90 });
91 for i in 0..30 {
92 self.y6_bit_decomp[i] = F::zero();
93 }
94 self.range_check_witness = F::zero();
95 self.permutation = populate_perm_deg3([F::zero(); 16], None);
96 }
97}
98
99impl<F: Field> GlobalInteractionOperation<F> {
100 pub fn eval_single_digest<AB: SP1AirBuilder + p3_air::PairBuilder>(
102 builder: &mut AB,
103 values: [AB::Expr; 7],
104 cols: GlobalInteractionOperation<AB::Var>,
105 is_receive: AB::Expr,
106 is_send: AB::Expr,
107 is_real: AB::Var,
108 kind: AB::Var,
109 ) {
110 builder.assert_bool(is_real);
112
113 let mut offset = AB::Expr::zero();
115 for i in 0..8 {
116 builder.assert_bool(cols.offset_bits[i]);
117 offset = offset.clone() + cols.offset_bits[i] * AB::F::from_canonical_u32(1 << i);
118 }
119
120 builder.send_byte(
123 AB::Expr::from_canonical_u8(ByteOpcode::U16Range as u8),
124 values[0].clone(),
125 AB::Expr::zero(),
126 AB::Expr::zero(),
127 is_real,
128 );
129
130 let m_trial = [
134 values[0].clone() + AB::Expr::from_canonical_u32(1 << 16) * kind,
135 values[1].clone(),
136 values[2].clone(),
137 values[3].clone(),
138 values[4].clone(),
139 values[5].clone(),
140 values[6].clone(),
141 offset.clone(),
142 AB::Expr::zero(),
143 AB::Expr::zero(),
144 AB::Expr::zero(),
145 AB::Expr::zero(),
146 AB::Expr::zero(),
147 AB::Expr::zero(),
148 AB::Expr::zero(),
149 AB::Expr::zero(),
150 ];
151
152 for i in 0..16 {
154 builder.when(is_real).assert_eq(
155 cols.permutation.permutation.external_rounds_state()[0][i].into(),
156 m_trial[i].clone(),
157 );
158 }
159
160 for r in 0..NUM_EXTERNAL_ROUNDS {
162 eval_external_round(builder, &cols.permutation.permutation, r);
163 }
164 eval_internal_rounds(builder, &cols.permutation.permutation);
165
166 let m_hash = cols.permutation.permutation.perm_output();
168 for i in 0..7 {
169 builder.when(is_real).assert_eq(cols.x_coordinate[i].into(), m_hash[i]);
170 }
171 let x = SepticExtension::<AB::Expr>::from_base_fn(|i| cols.x_coordinate[i].into());
172 let y = SepticExtension::<AB::Expr>::from_base_fn(|i| cols.y_coordinate[i].into());
173
174 let y2 = y.square();
176 let x3_2x_26z5 = SepticCurve::<AB::Expr>::curve_formula(x);
177 builder.assert_septic_ext_eq(y2, x3_2x_26z5);
178
179 let mut y6_value = AB::Expr::zero();
184 let mut top_4_bits = AB::Expr::zero();
185 for i in 0..30 {
186 builder.assert_bool(cols.y6_bit_decomp[i]);
187 y6_value = y6_value.clone() + cols.y6_bit_decomp[i] * AB::F::from_canonical_u32(1 << i);
188 if i >= 26 {
189 top_4_bits = top_4_bits.clone() + cols.y6_bit_decomp[i];
190 }
191 }
192 builder.when(is_real).assert_eq(
195 cols.range_check_witness * (top_4_bits - AB::Expr::from_canonical_u8(4)),
196 AB::Expr::one(),
197 );
198
199 builder.when(is_receive).assert_eq(y.0[6].clone(), AB::Expr::one() + y6_value.clone());
204 builder.when(is_send).assert_eq(
205 y.0[6].clone(),
206 AB::Expr::from_canonical_u32((1 << 30) - (1 << 26) + 1) + y6_value.clone(),
207 );
208 }
209}