Skip to content

Commit 95b1196

Browse files
committed
affine versioned pairing supporting for BitVM
1 parent 5a781ae commit 95b1196

File tree

4 files changed

+288
-12
lines changed

4 files changed

+288
-12
lines changed

ec/src/models/bn/g2.rs

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,88 @@ pub struct G2HomProjective<P: BnConfig> {
4040
z: Fp2<P::Fp2Config>,
4141
}
4242

43+
impl<P: BnConfig> G2Prepared<P> {
44+
fn affine_double_in_place(t: &mut G2Affine<P>, three_div_two: &P::Fp) -> EllCoeff<P> {
45+
// for affine coordinates
46+
// slope: alpha = 3 * x^2 / 2 * y
47+
let mut alpha = t.x.square();
48+
alpha /= t.y;
49+
alpha.mul_assign_by_fp(&three_div_two);
50+
let bias = t.y - alpha * t.x;
51+
52+
// update T
53+
// T.x = alpha^2 - 2 * t.x
54+
// T.y = -bias - alpha * T.x
55+
let tx = alpha.square() - t.x.double();
56+
t.y = -bias - alpha * tx;
57+
t.x = tx;
58+
59+
(Fp2::<P::Fp2Config>::ONE, alpha, -bias)
60+
}
61+
62+
fn affine_add_in_place(t: &mut G2Affine<P>, q: &G2Affine<P>) -> EllCoeff<P> {
63+
// alpha = (t.y - q.y) / (t.x - q.x)
64+
// bias = t.y - alpha * t.x
65+
let alpha = (t.y - q.y) / (t.x - q.x);
66+
let bias = t.y - alpha * t.x;
67+
68+
// update T
69+
// T.x = alpha^2 - t.x - q.x
70+
// T.y = -bias - alpha * T.x
71+
let tx = alpha.square() - t.x - q.x;
72+
t.y = -bias - alpha * tx;
73+
t.x = tx;
74+
75+
(Fp2::<P::Fp2Config>::ONE, alpha, -bias)
76+
}
77+
78+
/// !!! this method cannot be used directly for users, so we need reuse the `from` trait already exists
79+
fn from_affine(q: G2Affine<P>) -> Self {
80+
if q.infinity {
81+
G2Prepared {
82+
ell_coeffs: vec![],
83+
infinity: true,
84+
}
85+
} else {
86+
// let two_inv = P::Fp::one().double().inverse().unwrap();
87+
let two_inv = P::Fp::one().double().inverse().unwrap();
88+
let three_div_two = (P::Fp::one().double() + P::Fp::one()) * two_inv;
89+
90+
let mut ell_coeffs = vec![];
91+
let mut r = q.clone();
92+
93+
let neg_q = -q;
94+
95+
for bit in P::ATE_LOOP_COUNT.iter().rev().skip(1) {
96+
ell_coeffs.push(Self::affine_double_in_place(&mut r, &three_div_two));
97+
98+
match bit {
99+
1 => ell_coeffs.push(Self::affine_add_in_place(&mut r, &q)),
100+
-1 => ell_coeffs.push(Self::affine_add_in_place(&mut r, &neg_q)),
101+
_ => continue,
102+
}
103+
}
104+
105+
let q1 = mul_by_char::<P>(q);
106+
let mut q2 = mul_by_char::<P>(q1);
107+
108+
if P::X_IS_NEGATIVE {
109+
r.y = -r.y;
110+
}
111+
112+
q2.y = -q2.y;
113+
114+
ell_coeffs.push(Self::affine_add_in_place(&mut r, &q1));
115+
ell_coeffs.push(Self::affine_add_in_place(&mut r, &q2));
116+
117+
Self {
118+
ell_coeffs,
119+
infinity: false,
120+
}
121+
}
122+
}
123+
}
124+
43125
impl<P: BnConfig> G2HomProjective<P> {
44126
pub fn double_in_place(&mut self, two_inv: &P::Fp) -> EllCoeff<P> {
45127
// Formula for line function when working with
@@ -96,8 +178,24 @@ impl<P: BnConfig> Default for G2Prepared<P> {
96178
}
97179
}
98180

181+
/// !!! affine mode is for the purpose of verifying pairings
99182
impl<P: BnConfig> From<G2Affine<P>> for G2Prepared<P> {
100183
fn from(q: G2Affine<P>) -> Self {
184+
if q.infinity {
185+
G2Prepared {
186+
ell_coeffs: vec![],
187+
infinity: true,
188+
}
189+
} else {
190+
Self::from_affine(q)
191+
}
192+
}
193+
}
194+
195+
/// !!! projective mode is for the purpose of computing pairings
196+
impl<P: BnConfig> From<G2Projective<P>> for G2Prepared<P> {
197+
fn from(q: G2Projective<P>) -> Self {
198+
let q = q.into_affine();
101199
if q.infinity {
102200
G2Prepared {
103201
ell_coeffs: vec![],
@@ -144,12 +242,6 @@ impl<P: BnConfig> From<G2Affine<P>> for G2Prepared<P> {
144242
}
145243
}
146244

147-
impl<P: BnConfig> From<G2Projective<P>> for G2Prepared<P> {
148-
fn from(q: G2Projective<P>) -> Self {
149-
q.into_affine().into()
150-
}
151-
}
152-
153245
impl<'a, P: BnConfig> From<&'a G2Affine<P>> for G2Prepared<P> {
154246
fn from(other: &'a G2Affine<P>) -> Self {
155247
(*other).into()
@@ -158,7 +250,7 @@ impl<'a, P: BnConfig> From<&'a G2Affine<P>> for G2Prepared<P> {
158250

159251
impl<'a, P: BnConfig> From<&'a G2Projective<P>> for G2Prepared<P> {
160252
fn from(q: &'a G2Projective<P>) -> Self {
161-
q.into_affine().into()
253+
(*q).into()
162254
}
163255
}
164256

ec/src/models/bn/mod.rs

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,71 @@ pub trait BnConfig: 'static + Sized {
102102
MillerLoopOutput(f)
103103
}
104104

105+
fn multi_miller_loop_affine(
106+
a: impl IntoIterator<Item = impl Into<G1Prepared<Self>>>,
107+
b: impl IntoIterator<Item = impl Into<G2Prepared<Self>>>,
108+
) -> MillerLoopOutput<Bn<Self>> {
109+
let mut pairs = a
110+
.into_iter()
111+
.zip_eq(b)
112+
.filter_map(|(p, q)| {
113+
// if input q is projective coordinates, then we will enter `into`` computing pairing mode
114+
// otherwise if input q is affine coordinates, then we will enter `into` verifying pairing mode
115+
let (p, q) = (p.into(), q.into());
116+
match !p.is_zero() && !q.is_zero() {
117+
true => Some((
118+
-p.0.x / p.0.y,
119+
p.0.y.inverse().unwrap(),
120+
q.ell_coeffs.into_iter(),
121+
)),
122+
false => None,
123+
}
124+
})
125+
.collect::<Vec<_>>();
126+
127+
let mut f = cfg_chunks_mut!(pairs, 4)
128+
.map(|pairs| {
129+
let mut f = <Bn<Self> as Pairing>::TargetField::one();
130+
for i in (1..Self::ATE_LOOP_COUNT.len()).rev() {
131+
if i != Self::ATE_LOOP_COUNT.len() - 1 {
132+
f.square_in_place();
133+
}
134+
135+
for (coeff_1, coeff_2, coeffs) in pairs.iter_mut() {
136+
Bn::<Self>::ell_affine(&mut f, &coeffs.next().unwrap(), &coeff_1, &coeff_2);
137+
}
138+
139+
let bit = Self::ATE_LOOP_COUNT[i - 1];
140+
if bit == 1 || bit == -1 {
141+
for (coeff_1, coeff_2, coeffs) in pairs.iter_mut() {
142+
Bn::<Self>::ell_affine(
143+
&mut f,
144+
&coeffs.next().unwrap(),
145+
&coeff_1,
146+
&coeff_2,
147+
);
148+
}
149+
}
150+
}
151+
f
152+
})
153+
.product::<<Bn<Self> as Pairing>::TargetField>();
154+
155+
if Self::X_IS_NEGATIVE {
156+
f.cyclotomic_inverse_in_place();
157+
}
158+
159+
for (coeff_1, coeff_2, coeffs) in &mut pairs {
160+
Bn::<Self>::ell_affine(&mut f, &coeffs.next().unwrap(), &coeff_1, &coeff_2);
161+
}
162+
163+
for (coeff_1, coeff_2, coeffs) in &mut pairs {
164+
Bn::<Self>::ell_affine(&mut f, &coeffs.next().unwrap(), &coeff_1, &coeff_2);
165+
}
166+
167+
MillerLoopOutput(f)
168+
}
169+
105170
#[allow(clippy::let_and_return)]
106171
fn final_exponentiation(f: MillerLoopOutput<Bn<Self>>) -> Option<PairingOutput<Bn<Self>>> {
107172
// Easy part: result = elt^((q^6-1)*(q^2+1)).
@@ -180,7 +245,7 @@ pub use self::{
180245
pub struct Bn<P: BnConfig>(PhantomData<fn() -> P>);
181246

182247
impl<P: BnConfig> Bn<P> {
183-
/// Evaluates the line function at point p.
248+
/// Evaluates the line function at point p, where the line function is in projective mode
184249
fn ell(f: &mut Fp12<P::Fp12Config>, coeffs: &g2::EllCoeff<P>, p: &G1Affine<P>) {
185250
let mut c0 = coeffs.0;
186251
let mut c1 = coeffs.1;
@@ -200,6 +265,35 @@ impl<P: BnConfig> Bn<P> {
200265
}
201266
}
202267

268+
/// Evaluates the line function at point p, where the line function is in affine mode
269+
/// input:
270+
/// f, Fq12
271+
/// coeffs, (1, alpha, bias)
272+
/// x' = -p.x / p.y
273+
/// y' = 1 / p.y
274+
/// output:
275+
/// f = f * f_Q(P)', where f_Q(P)' is a vairant of f_Q(P), f_Q(P) = y' * f_Q(P)
276+
fn ell_affine(f: &mut Fp12<P::Fp12Config>, coeffs: &g2::EllCoeff<P>, xx: &P::Fp, yy: &P::Fp) {
277+
// c0 is a trival value 1
278+
let c0 = coeffs.0;
279+
let mut c1 = coeffs.1;
280+
let mut c2 = coeffs.2;
281+
282+
match P::TWIST_TYPE {
283+
TwistType::M => {
284+
c1.mul_assign_by_fp(&xx);
285+
c2.mul_assign_by_fp(&yy);
286+
f.mul_by_014(&c0, &c1, &c2);
287+
},
288+
// line evaluation is y' * f_Q(P), coefficients are (1, x' * lambda, -y' * bias)
289+
TwistType::D => {
290+
c1.mul_assign_by_fp(&xx);
291+
c2.mul_assign_by_fp(&yy);
292+
f.mul_by_034(&c0, &c1, &(c2));
293+
},
294+
}
295+
}
296+
203297
fn exp_by_neg_x(mut f: Fp12<P::Fp12Config>) -> Fp12<P::Fp12Config> {
204298
f = f.cyclotomic_exp(P::X);
205299
if !P::X_IS_NEGATIVE {
@@ -227,6 +321,13 @@ impl<P: BnConfig> Pairing for Bn<P> {
227321
P::multi_miller_loop(a, b)
228322
}
229323

324+
fn multi_miller_loop_affine(
325+
a: impl IntoIterator<Item = impl Into<Self::G1Prepared>>,
326+
b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
327+
) -> MillerLoopOutput<Self> {
328+
P::multi_miller_loop_affine(a, b)
329+
}
330+
230331
fn final_exponentiation(f: MillerLoopOutput<Self>) -> Option<PairingOutput<Self>> {
231332
P::final_exponentiation(f)
232333
}

ec/src/pairing.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ pub trait Pairing: Sized + 'static + Copy + Debug + Sync + Send + Eq {
8888
b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
8989
) -> MillerLoopOutput<Self>;
9090

91+
/// Computes the product of Miller loops for some number of (G1, G2) pairs, where the line functions are in affine mode
92+
fn multi_miller_loop_affine(
93+
a: impl IntoIterator<Item = impl Into<Self::G1Prepared>>,
94+
b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
95+
) -> MillerLoopOutput<Self> {
96+
unimplemented!()
97+
}
98+
9199
/// Computes the Miller loop over `a` and `b`.
92100
fn miller_loop(
93101
a: impl Into<Self::G1Prepared>,
@@ -108,13 +116,29 @@ pub trait Pairing: Sized + 'static + Copy + Debug + Sync + Send + Eq {
108116
Self::final_exponentiation(Self::multi_miller_loop(a, b)).unwrap()
109117
}
110118

119+
/// Computes a "product" of pairings, where the line functions are in affine mode
120+
fn multi_pairing_affine(
121+
a: impl IntoIterator<Item = impl Into<Self::G1Prepared>>,
122+
b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
123+
) -> PairingOutput<Self> {
124+
Self::final_exponentiation(Self::multi_miller_loop_affine(a, b)).unwrap()
125+
}
126+
111127
/// Performs multiple pairing operations
112128
fn pairing(
113129
p: impl Into<Self::G1Prepared>,
114130
q: impl Into<Self::G2Prepared>,
115131
) -> PairingOutput<Self> {
116132
Self::multi_pairing([p], [q])
117133
}
134+
135+
/// Performs multiple pairing operations, where the line functions are in affine mode
136+
fn pairing_affine(
137+
p: impl Into<Self::G1Prepared>,
138+
q: impl Into<Self::G2Prepared>,
139+
) -> PairingOutput<Self> {
140+
Self::multi_pairing_affine([p], [q])
141+
}
118142
}
119143

120144
/// Represents the target group of a pairing. This struct is a

0 commit comments

Comments
 (0)