Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
remove ambiguity
  • Loading branch information
Ubuntu
Ubuntu committed Oct 22, 2025
commit 4047d9fd63ee114a0087bae49f71503407f4b55d
3 changes: 3 additions & 0 deletions bitvm/src/bn254/ell_coeffs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl G2Prepared {
t: &mut ark_bn254::G2Affine,
three_div_two: &ark_bn254::Fq,
) -> EllCoeff {
assert_ne!(*t, ark_bn254::G2Affine::zero());
// for affine coordinates
// slope: alpha = 3 * x^2 / 2 * y
// intercept: bias = y - alpha * x
Expand All @@ -55,6 +56,8 @@ impl G2Prepared {
}

fn affine_add_in_place(t: &mut ark_bn254::G2Affine, q: &ark_bn254::G2Affine) -> EllCoeff {
assert_ne!(*t, ark_bn254::G2Affine::zero());
assert_ne!(*q, ark_bn254::G2Affine::zero());
// alpha = (t.y - q.y) / (t.x - q.x)
// bias = t.y - alpha * t.x
let alpha = (t.y - q.y) / (t.x - q.x);
Expand Down
8 changes: 6 additions & 2 deletions bitvm/src/bn254/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ impl G1Affine {
q: ark_bn254::G1Affine,
c3: ark_bn254::Fq,
) -> (Script, Vec<Hint>) {
// TODO: uncommnent change the hinted_check_add to hinted_check_add_prevent_degenerate https://github.com/BitVM/BitVM/pull/379
// assert_ne!(t, ark_bn254::G1Affine::zero());
// assert_ne!(q, ark_bn254::G1Affine::zero());
let mut hints = Vec::new();

let (hinted_script1, hint1) = Self::hinted_check_line_through_point(q.x, c3);
Expand Down Expand Up @@ -103,6 +106,7 @@ impl G1Affine {
t: ark_bn254::G1Affine,
c3: ark_bn254::Fq,
) -> (Script, Vec<Hint>) {
assert_ne!(t, ark_bn254::G1Affine::zero());
let mut hints = vec![];

let (hinted_script1, hint1) = Fq::hinted_mul(1, t.y + t.y, 0, c3);
Expand Down Expand Up @@ -496,10 +500,10 @@ pub fn hinted_from_eval_point(px: ark_bn254::Fq, py: ark_bn254::Fq) -> (Script,
pub fn hinted_from_eval_points(px: ark_bn254::Fq, py: ark_bn254::Fq) -> (Script, Vec<Hint>, bool) {
let mut hints = Vec::new();

let p = ark_bn254::G1Affine::new_unchecked(px, py);
if p.is_zero() {
if px == ark_bn254::Fq::ZERO && py == ark_bn254::Fq::ZERO {
return (G1Affine::push_zero(), hints, true);
}
let p = ark_bn254::G1Affine::new_unchecked(px, py);
let valid_point = p.is_on_curve();

let py_inv = py.inverse().unwrap();
Expand Down
3 changes: 3 additions & 0 deletions bitvm/src/bn254/g2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ pub fn hinted_check_tangent_line(
c3: ark_bn254::Fq2,
c4: ark_bn254::Fq2,
) -> (Script, Vec<Hint>) {
assert_ne!(t, ark_bn254::G2Affine::zero());
let mut hints = Vec::new();

// let (hinted_script1, hint1) = Fq2::hinted_mul_by_constant(t.y.double(), &c3);
Expand Down Expand Up @@ -757,6 +758,8 @@ pub fn hinted_check_chord_line(
c3: ark_bn254::Fq2,
c4: ark_bn254::Fq2,
) -> (Script, Vec<Hint>) {
assert_ne!(t, ark_bn254::G2Affine::zero());
assert_ne!(q, ark_bn254::G2Affine::zero());
let mut hints = Vec::new();

let (script1, hint1) = hinted_check_line_through_point(q.x, c3, c4);
Expand Down
2 changes: 2 additions & 0 deletions bitvm/src/bn254/pairing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::bn254::fq2::Fq2;
use crate::bn254::g2::*;
use crate::treepp::*;
use ark_ec::bn::BnConfig;
use ark_ec::AffineRepr;
use ark_ff::{AdditiveGroup, Field};
use num_bigint::BigUint;
use num_traits::One;
Expand Down Expand Up @@ -36,6 +37,7 @@ impl Pairing {
q4: ark_bn254::G2Affine,
) -> (Script, Vec<Hint>) {
assert_eq!(constants.len(), 4);
assert_ne!(q4, ark_bn254::G2Affine::zero());
let num_line_groups = constants.len();
let num_constant = 3;

Expand Down
145 changes: 62 additions & 83 deletions bitvm/src/chunk/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,26 @@ pub enum DataType {
U256Data(ark_ff::BigInt<4>),
}

/// Represent coordinates by evaluation form (-x/y, 1/y), see more: https://github.com/BitVM/BitVM/issues/213
#[derive(Debug, Clone, Copy)]
/// Represent evaluation form (-x/y, 1/y) for G1Affine points, see more: https://github.com/BitVM/BitVM/issues/213
/// Specially, for `chunk_precompute_p` and `chunk_precompute_p_from_hash`, the G1Data stores the values' original form.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FqPair {
x: ark_bn254::Fq, // -x/y
y: ark_bn254::Fq, // 1/y
pub zero: bool, // true if y is zero
x: ark_bn254::Fq,
y: ark_bn254::Fq,
}

impl FqPair {
pub const ZERO: Self = Self {
x: ark_bn254::Fq::ZERO,
y: ark_bn254::Fq::ZERO,
};

pub fn new(x: ark_bn254::Fq, y: ark_bn254::Fq) -> Self {
Self {
x,
y,
zero: y == ark_bn254::Fq::ZERO,
if x == ark_bn254::Fq::ZERO || y == ark_bn254::Fq::ZERO {
assert_eq!(x, ark_bn254::Fq::ZERO);
assert_eq!(y, ark_bn254::Fq::ZERO);
}
Self { x, y }
}

pub fn x(&self) -> ark_bn254::Fq {
Expand All @@ -61,81 +66,55 @@ impl FqPair {
}

/// Recover the evaluation form to original form.
pub fn recover(&self) -> Self {
if self.zero {
if self.x == ark_bn254::Fq::ZERO {
return Self::new(ark_bn254::Fq::ZERO, ark_bn254::Fq::ZERO);
}
return Self::new(self.x, self.y);
pub fn recover(&self) -> ark_bn254::G1Affine {
if self.y == ark_bn254::Fq::ZERO {
return ark_bn254::G1Affine::zero();
}

let (nx, ny) = (self.x, self.y);
let y = ny.inverse().expect("ny should be non-zero to recover");
let y = ny
.inverse()
.expect("ny must be nonzero for evaluation form");
let x = -nx * y; // equivalent to -nx / ny⁻¹

Self::new(x, y)
// Use new_unchecked to get compitiable with generate_segments_using_mock_vk_and_mock_proof.
ark_bn254::G1Affine::new_unchecked(x, y)
}

pub fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self {
let p = ark_bn254::G1Affine::rand(rng);
Self::new(p.x, p.y)
}

pub fn is_zero(&self) -> bool {
*self == Self::ZERO
}
}

impl From<ark_bn254::G1Affine> for FqPair {
fn from(p: ark_bn254::G1Affine) -> Self {
if p.y == ark_bn254::Fq::ZERO {
return Self {
x: p.x,
y: p.y,
zero: true,
};
return Self { x: p.x, y: p.y };
}
let (x, y) = (p.x, p.y);
let ny = y.inverse().expect("y must be nonzero for evaluation form");
let nx = -(x * ny);
Self {
x: nx,
y: ny,
zero: false,
}
Self { x: nx, y: ny }
}
}

//impl From<FqPair> for ark_bn254::G1Affine {
// fn from(pi: FqPair) -> Self {
// if pi.zero {
// if pi.x == ark_bn254::Fq::ZERO {
// return ark_bn254::G1Affine::zero();
// }
// return ark_bn254::G1Affine::new_unchecked(pi.x, pi.y);
// }
//
// let (nx, ny) = (pi.x, pi.y);
// let y = ny
// .inverse()
// .expect("ny must be nonzero for evaluation form");
// let x = -nx * y; // equivalent to -nx / ny⁻¹
//
// ark_bn254::G1Affine::new_unchecked(x, y)
// }
//}

/// Non-G2 Group on E'
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
pub struct TwistPoint {
x: ark_bn254::Fq2,
y: ark_bn254::Fq2,
pub zero: bool,
}

impl TwistPoint {
pub const ZERO: Self = Self {
x: ark_bn254::Fq2::ZERO,
y: ark_bn254::Fq2::ZERO,
};
pub fn new(x: ark_bn254::Fq2, y: ark_bn254::Fq2) -> Self {
Self {
x,
y,
zero: x == ark_bn254::Fq2::ZERO && y == ark_bn254::Fq2::ZERO,
}
Self { x, y }
}

pub fn x(&self) -> ark_bn254::Fq2 {
Expand All @@ -146,25 +125,36 @@ impl TwistPoint {
self.y
}

pub fn is_zero(&self) -> bool {
self.zero
}

pub fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self {
let p = ark_bn254::G2Affine::rand(rng);
Self::new(p.x, p.y)
loop {
let x = ark_bn254::Fq2::rand(rng);
let rhs = x * x * x + G2Config::COEFF_B;
if let Some(y) = rhs.sqrt() {
// This (x, y) satisfies the twist curve equation but is *not* subgroup-checked.
let p = ark_bn254::G2Affine::new_unchecked(x, y);
assert!(p.is_on_curve(), "curve equation must hold");
return TwistPoint::new(x, y);
}
}
}

/// y^2 = x^3 + b' (b' from E')
pub fn is_on_curve(&self) -> bool {
// We don't consider the 0 point is on curve to align with `hinted_is_on_curve`
let x2 = self.x.square();
let y2 = self.y.square();
let x3b = self.x.square() * self.x + <G2Config as SWCurveConfig>::COEFF_B;
let x3b = x2 * self.x + <G2Config as SWCurveConfig>::COEFF_B;
y2 == x3b
}

// check if a zero point
pub fn is_zero(&self) -> bool {
*self == Self::ZERO
}

/// Negation
pub fn neg(&self) -> Self {
if self.zero {
if self.is_zero() {
*self
} else {
Self::new(self.x, -self.y)
Expand All @@ -173,7 +163,7 @@ impl TwistPoint {

/// Point doubling: (x3, y3) = 2 * (x1, y1)
pub fn double(&self) -> Self {
if self.zero {
if self.is_zero() {
return *self;
}

Expand All @@ -187,7 +177,6 @@ impl TwistPoint {
return Self {
x: ark_bn254::Fq2::zero(),
y: ark_bn254::Fq2::zero(),
zero: true,
};
}

Expand All @@ -200,10 +189,10 @@ impl TwistPoint {

/// Point addition on E': P3 = P1 + P2
pub fn add(&self, other: &Self) -> Self {
if self.zero {
if self.is_zero() {
return *other;
}
if other.zero {
if other.is_zero() {
return *self;
}

Expand All @@ -217,7 +206,6 @@ impl TwistPoint {
return Self {
x: ark_bn254::Fq2::zero(),
y: ark_bn254::Fq2::zero(),
zero: true,
};
} else {
return self.double();
Expand All @@ -234,11 +222,7 @@ impl TwistPoint {

impl From<ark_bn254::G2Affine> for TwistPoint {
fn from(p: ark_bn254::G2Affine) -> Self {
Self {
x: p.x,
y: p.y,
zero: p.is_zero(),
}
Self { x: p.x, y: p.y }
}
}

Expand Down Expand Up @@ -419,7 +403,7 @@ impl DataType {
DataType::U256Data(f) => CompressedStateObject::U256(f),
DataType::G1Data(r) => {
let r = r.recover();
let hash = extern_hash_fps(vec![r.x(), r.y()]);
let hash = extern_hash_fps(vec![r.x, r.y]);
CompressedStateObject::Hash(hash)
}
}
Expand Down Expand Up @@ -502,7 +486,7 @@ fn as_hints_scalarelemtype_u256data(elem: ark_ff::BigInt<4>) -> Vec<Hint> {

fn as_hints_g1type_g1data(r: &FqPair) -> Vec<Hint> {
let r = r.recover();
let hints = vec![Hint::Fq(r.x()), Hint::Fq(r.y())];
let hints = vec![Hint::Fq(r.x), Hint::Fq(r.y)];
hints
}

Expand Down Expand Up @@ -636,21 +620,16 @@ mod test {

#[test]
fn test_fq_pair() {
let zero = ark_bn254::G1Affine::new_unchecked(ark_bn254::Fq::ZERO, ark_bn254::Fq::ZERO);
let zero = ark_bn254::G1Affine::zero();
let fp_zero = FqPair::from(zero);
assert!(fp_zero.zero);
assert_eq!(
zero,
ark_bn254::G1Affine::new_unchecked(fp_zero.x(), fp_zero.y())
);
assert_eq!(zero, fp_zero.recover());

let mut rng = thread_rng();
(0..100).into_iter().for_each(|_| {
let random = ark_bn254::G1Affine::rand(&mut rng);
let fp_rand = FqPair::from(random);
assert!(!fp_rand.zero);
let lifted = fp_rand.recover();
assert_eq!(random, ark_bn254::G1Affine::new(lifted.x(), lifted.y()));
assert_eq!(random, lifted);
});
}

Expand Down Expand Up @@ -691,7 +670,7 @@ mod test {
// P + (-P) = 0
let neg = p.neg();
let s3 = p.add(&neg);
assert!(s3.zero, "P + (-P) should be zero point");
assert!(s3.is_zero(), "P + (-P) should be zero point");
}

#[test]
Expand Down
17 changes: 10 additions & 7 deletions bitvm/src/chunk/g16_runner_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,9 @@ pub(crate) fn wrap_chunk_point_ops_and_multiply_line_evals_step_1(
let p3: FqPair = in_p3.result.0.try_into().unwrap();
let p2: FqPair = in_p2.result.0.try_into().unwrap();

let p4 = p4.recover();
let p3 = p3.recover();
let p2 = p2.recover();
let p4 = ark_bn254::G1Affine::new_unchecked(p4.x(), p4.y()).into();
let p3 = ark_bn254::G1Affine::new_unchecked(p3.x(), p3.y()).into();
let p2 = ark_bn254::G1Affine::new_unchecked(p2.x(), p2.y()).into();

let mut q4: Option<TwistPoint> = None;

Expand Down Expand Up @@ -368,13 +368,16 @@ pub(crate) fn wrap_hint_hash_p(
let input_segment_info = vec![(in_t.id, ElementType::G1)];

let t: FqPair = in_t.result.0.try_into().unwrap();
let (mut p3, mut is_valid_input, mut scr, mut op_hints) =
(ark_bn254::G1Affine::identity(), true, script! {}, vec![]);
let (mut p3, mut is_valid_input, mut scr, mut op_hints) = (
ark_bn254::G1Affine::identity().into(),
true,
script! {},
vec![],
);
if !skip {
// the value has been converted to FqPair in chunk_msm, we recover it to G1 coordinate.
let t = t.recover();
let tq = ark_bn254::G1Affine::new_unchecked(t.x(), t.y());
(p3, is_valid_input, scr, op_hints) = chunk_hash_p(tq, pub_vky0);
(p3, is_valid_input, scr, op_hints) = chunk_hash_p(t, pub_vky0);
// op_hints.extend_from_slice(&DataType::G1Data(t).get_hash_preimage_as_hints());
}
Segment {
Expand Down
Loading