Skip to content
Prev Previous commit
stash code
  • Loading branch information
Ubuntu
Ubuntu committed Oct 5, 2025
commit c6f9e79742dbc6f1e5324c232860e8e72a9fce93
124 changes: 103 additions & 21 deletions bitvm/src/bn254/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,66 @@ impl G1Affine {
element
}

pub fn hinted_check_add_prevent_degenerate(
t: ark_bn254::G1Affine,
q: ark_bn254::G1Affine,
) -> (Script, Vec<Hint>) {
let mut hints = vec![];
assert_ne!(t, q);
assert_ne!(t, -q);

let (alpha, bias) = if !t.is_zero() && !q.is_zero() {
let alpha = (t.y - q.y) / (t.x - q.x);
let bias = t.y - alpha * t.x;
(alpha, bias)
} else {
(ark_bn254::Fq::ZERO, ark_bn254::Fq::ZERO)
};

let (hinted_script1, hint1) = Self::hinted_check_chord_line(t, q, alpha);
let (hinted_script2, hint2) = Self::hinted_add(t.x, q.x, alpha);

let script = script! { // tx ty qx qy
{ G1Affine::is_zero_keep_element() }
OP_IF
{ G1Affine::drop() }
OP_ELSE
{ G1Affine::roll(1) }
{ G1Affine::is_zero_keep_element() }
OP_IF
{ G1Affine::drop() }
OP_ELSE // qx qy tx ty
for _ in 0..Fq::N_LIMBS {
OP_DEPTH OP_1SUB OP_ROLL
}
for _ in 0..Fq::N_LIMBS {
OP_DEPTH OP_1SUB OP_ROLL
} // qx qy tx ty c3 c4
{ Fq::copy(1) }
{ Fq::copy(1) } // qx qy tx ty c3 c4 c3 c4
{ Fq::copy(5) }
{ Fq::roll(5) } // qx qy tx c3 c4 c3 c4 tx ty
{ Fq::copy(8) }
{ Fq::roll(8) } // qx tx c3 c4 c3 c4 tx ty qx qy
{ hinted_script1 } // qx tx c3 c4 0/1
OP_VERIFY
{ Fq::roll(2) }
{ Fq::roll(3) } // c3 c4 tx qx
{ hinted_script2 } // x' y'
OP_ENDIF
OP_ENDIF
};

if !t.is_zero() && !q.is_zero() {
hints.push(Hint::Fq(alpha));
hints.push(Hint::Fq(-bias));
hints.extend(hint1);
hints.extend(hint2);
}

(script, hints)
}

pub fn hinted_check_add(t: ark_bn254::G1Affine, q: ark_bn254::G1Affine) -> (Script, Vec<Hint>) {
let mut hints = vec![];

Expand Down Expand Up @@ -778,50 +838,72 @@ mod test {
}

#[test]
fn test_g1_affine_hinted_check_add() {
fn test_g1_affine_hinted_check_add_prevent_degenerate() {
//println!("G1.hinted_add: {} bytes", G1Affine::check_add().len());
let mut prng = ChaCha20Rng::seed_from_u64(0);
let t = ark_bn254::G1Affine::rand(&mut prng);
let q = ark_bn254::G1Affine::rand(&mut prng);
let alpha = (t.y - q.y) / (t.x - q.x);
// -bias
let bias_minus = alpha * t.x - t.y;
let r = (t + q).into_affine();

let x = alpha.square() - t.x - q.x;
let y = bias_minus - alpha * x;

let (hinted_check_add, hints) = G1Affine::hinted_check_add(t, q);
let (hinted_check_add, hints) = G1Affine::hinted_check_add_prevent_degenerate(t, q);

let script = script! {
for hint in hints {
{ hint.push() }
}
{ Fq::push(t.x) }
{ Fq::push(t.y) }
{ Fq::push(q.x) }
{ Fq::push(q.y) }
{ G1Affine::push(t) }
{ G1Affine::push(q) }
{ hinted_check_add.clone() }
// [x']
{ Fq::push(y) }
// [x', y', y]
{ Fq::equalverify(1,0) }
// [x']
{ Fq::push(x) }
// [x', x]
{ Fq::equalverify(1,0) }

{ G1Affine::push(r) }
{ G1Affine::equalverify() }
// []
OP_TRUE
// [OP_TRUE]
};
let exec_result = execute_script(script);
assert!(exec_result.success);
println!(
"hinted_check_add: {} @ {} stack",
"hinted_check_add_prevent_degenerate: {} @ {} stack",
hinted_check_add.len(),
exec_result.stats.max_nb_stack_items
);
}

#[test]
fn test_g1_affine_hinted_check_add() {
let mut prng = ChaCha20Rng::seed_from_u64(0);
let t = ark_bn254::G1Affine::rand(&mut prng);
let q = ark_bn254::G1Affine::rand(&mut prng);
let r = (t + q).into_affine();
let tt = (t + t).into_affine();
let negt = -t;
let z = ark_bn254::G1Affine::zero();

for (t, q, r) in [(t, q, r), (t, t, tt), (t, negt, z)] {
let (hinted_check_add, hints) = G1Affine::hinted_check_add(t, q);

let script = script! {
for hint in hints {
{ hint.push() }
}
{ G1Affine::push(t) }
{ G1Affine::push(q) }
{ hinted_check_add.clone() }
{ G1Affine::push(r) }
{ G1Affine::equalverify() }
OP_TRUE
};
let exec_result = execute_script(script);
assert!(exec_result.success);
println!(
"hinted_check_add: {} @ {} stack",
hinted_check_add.len(),
exec_result.stats.max_nb_stack_items
);
}
}

#[test]
fn test_g1_affine_hinted_check_double() {
//println!("G1.hinted_add: {} bytes", G1Affine::check_add().len());
Expand Down
16 changes: 13 additions & 3 deletions bitvm/src/chunk/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,13 +753,23 @@ mod test {
let scalar = crate::chunk::api_runtime_utils::test::PUBLIC_INPUT_BYTES.to_vec();
let proof: ark_groth16::Proof<Bn254> =
ark_groth16::Proof::deserialize_uncompressed(&proof_bytes[..]).unwrap();
let mock_vk: ark_groth16::VerifyingKey<Bn254> =
let vk: ark_groth16::VerifyingKey<Bn254> =
ark_groth16::VerifyingKey::deserialize_uncompressed(&vk_bytes[..]).unwrap();
let scalar: ark_bn254::Fr = ark_bn254::Fr::deserialize_uncompressed(&scalar[..]).unwrap();
let public_inputs = [scalar];

assert!(mock_vk.gamma_abc_g1.len() == NUM_PUBS + 1);
let proof_asserts = generate_assertions(proof, public_inputs.to_vec(), &mock_vk).unwrap();
println!("public_inputs {:?}", public_inputs);
println!("proof {:?}", proof);
println!("vk {:?}", vk);

// verify proof
let pvk = ark_groth16::prepare_verifying_key(&vk);
let res = ark_groth16::Groth16::<ark_bn254::Bn254>::verify_proof(&pvk, &proof, &public_inputs);
println!("verify proof: {:?}", res);
assert!(res.is_ok() && res.unwrap());

assert!(vk.gamma_abc_g1.len() == NUM_PUBS + 1);
let proof_asserts = generate_assertions(proof, public_inputs.to_vec(), &vk).unwrap();

std::fs::create_dir_all("bridge_data/chunker_data")
.expect("Failed to create directory structure");
Expand Down
8 changes: 2 additions & 6 deletions bitvm/src/chunk/api_runtime_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,9 @@ fn utils_execute_chunked_g16(
disprove_scripts: &[ScriptBuf; NUM_TAPS],
) -> Option<(usize, Script)> {
let mut tap_script_index = 0;
println!("aux_hints.len() {}, segments.len() {}", aux_hints.len(), segments.len());
for i in 0..aux_hints.len() {
println!("segment {}: {:?}", i, segments[i].scr_type);
if segments[i].scr_type == ScriptType::NonDeterministic {
continue;
}
Expand Down Expand Up @@ -751,12 +753,6 @@ pub mod test {
let scalars = [public_input];
println!("public input: {:?}", public_input);

// verify proof
let pvk = ark_groth16::prepare_verifying_key(&vk);
let res = ark_groth16::Groth16::<ark_bn254::Bn254>::verify_proof(&pvk, &proof, &scalars);
println!("verify proof: {:?}", res);
assert!(res.is_ok() && res.unwrap());

// generate segments
println!("get_segments_from_groth16_proof");
let (success, segments) = get_segments_from_groth16_proof(proof, scalars.to_vec(), &vk);
Expand Down
7 changes: 5 additions & 2 deletions bitvm/src/chunk/g16_runner_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ pub(crate) fn groth16_generate_segments(
unreachable!();
}
} else if $seg.is_valid_input == false {
println!("Input validity check failed at segment id: {:?}", $seg.id);
println!("Input validity check failed at segment id: {:?}, type: {:?}", $seg.id, $seg.scr_type);
return false;
} else {
let matches = compare(&$seg.result.0, claimed_assertions);
if matches.is_some() && matches.unwrap() == false {
println!("Output validity check failed at segment id: {:?}", $seg.id);
println!("Output validity check failed at segment id: {:?}, type: {:?}", $seg.id, $seg.scr_type);
return false;
}
}
Expand All @@ -132,9 +132,11 @@ pub(crate) fn groth16_generate_segments(

let pub_scalars = pub_scalars.to_vec();

// proof.a
let p4 = wrap_hints_precompute_p(skip_evaluation, all_output_hints.len(), &gp4y, &gp4x);
push_compare_or_return!(p4);

// proof.c
let p2 = wrap_hints_precompute_p(skip_evaluation, all_output_hints.len(), &gp2y, &gp2x);
push_compare_or_return!(p2);

Expand Down Expand Up @@ -165,6 +167,7 @@ pub(crate) fn groth16_generate_segments(
let gcinv = wrap_hint_hash_c_inv(skip_evaluation, all_output_hints.len(), gc);
push_compare_or_return!(gcinv);

// proof.b
let mut t4 = wrap_hint_init_t4(
skip_evaluation,
all_output_hints.len(),
Expand Down
42 changes: 18 additions & 24 deletions bitvm/src/chunk/g16_runner_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,20 +304,25 @@ pub(crate) fn wrap_hint_msm(

let mut segments = vec![];
if !skip {
let houts = chunk_msm(hint_scalars, pub_vky.clone());
let houts = chunk_msm(hint_scalars.clone(), pub_vky.clone());
assert_eq!(houts.len(), num_chunks_per_scalar as usize * scalars.len());
for (msm_chunk_index, (hout_msm, is_valid_input, scr, op_hints)) in
houts.into_iter().enumerate()
{
assert!(is_valid_input);

println!("hout_msm: {:?}\n hint_scalars: {:?}", hout_msm, hint_scalars.clone());
let mut input_segment_info: Vec<(SegmentID, ElementType)> = vec![];
if msm_chunk_index > 0 {
let prev_msm_id = (segment_id + msm_chunk_index - 1) as u32;
input_segment_info.push((prev_msm_id, ElementType::G1));
}

let sc = &scalars[msm_chunk_index / num_chunks_per_scalar as usize];
println!("sc id: {}, msm_chunk_index: {}, num_chunks_per_scalar: {}", sc.id, msm_chunk_index, num_chunks_per_scalar);
input_segment_info.push((sc.id, ElementType::ScalarElem));

println!("MSM segment id: {}, msm_chunk_index: {msm_chunk_index}, g1: {:?}, is_valid_input: {is_valid_input}", segment_id + msm_chunk_index, hout_msm);
segments.push(Segment {
id: (segment_id + msm_chunk_index) as u32,
is_valid_input,
Expand All @@ -339,6 +344,7 @@ pub(crate) fn wrap_hint_msm(
let sc = &scalars[(msm_chunk_index / num_chunks_per_scalar) as usize];
input_segment_info.push((sc.id, ElementType::ScalarElem));

println!("push MSM segment id: {} with MSM zero point", segment_id as u32 + msm_chunk_index);
segments.push(Segment {
id: (segment_id as u32 + msm_chunk_index),
is_valid_input: true,
Expand Down Expand Up @@ -369,11 +375,14 @@ pub(crate) fn wrap_hint_hash_p(
(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());
}
println!("segment id: {segment_id}, skip: {skip}, p3: {p3:?}");
Segment {
id: segment_id as u32,
is_valid_input,
parameter_ids: input_segment_info,
result: (DataType::G1Data(p3.into()), ElementType::G1),
result: (DataType::G1Data(
G1AffineIsomorphic::new(p3.x, p3.y)
), ElementType::G1),
hints: op_hints,
scr_type: ScriptType::PreMillerHashP,
scr: scr.compile(),
Expand All @@ -399,21 +408,14 @@ pub(crate) fn wrap_hints_precompute_p(
vec![],
);
// let mut tap_prex = script! {};
println!("segment id: {segment_id}, skip: {skip}");
println!("segment id: {segment_id}, skip: {skip}, in_py.result: {:?}, in_px.result: {:?}", in_py.result, in_px.result);
if !skip {
let in_py = in_py.result.0.try_into().unwrap();
let in_px = in_px.result.0.try_into().unwrap();
println!("in px: {:?}, in py: {:?} ", in_px, in_py);
(p3d, is_valid_input, scr, op_hints) = chunk_precompute_p(in_py, in_px);
}
println!(
"ONE: {:?}, p3d: {:?}",
G1AffineIsomorphic::from(ark_bn254::G1Affine::new_unchecked(
ark_bn254::Fq::ONE,
ark_bn254::Fq::ONE
)),
p3d
);
println!("p3d: {:?}", p3d);

Segment {
id: segment_id as u32,
Expand All @@ -439,22 +441,14 @@ pub(crate) fn wrap_hints_precompute_p_from_hash(
script! {},
vec![],
);
println!("segment id: {segment_id}, skip: {skip}");
println!("segment id: {segment_id}, input id: {:?}, type: G1, skip: {skip}", in_p.id);

if !skip {
let in_p: G1AffineIsomorphic = in_p.result.0.try_into().unwrap();

let ttt: ark_bn254::G1Affine = in_p.into();
println!("in_p: {:?}, ttt: {:?}", in_p, ttt);
(p3d, is_valid_input, scr, op_hints) = chunk_precompute_p_from_hash(in_p.into());
let affine: ark_bn254::G1Affine = in_p.into();
println!("in p: {:?} ", affine);
(p3d, is_valid_input, scr, op_hints) = chunk_precompute_p_from_hash(in_p);
}
println!(
"ONE: {:?}, p3d: {:?}",
G1AffineIsomorphic::from(ark_bn254::G1Affine::new_unchecked(
ark_bn254::Fq::ONE,
ark_bn254::Fq::ONE
)),
p3d
);

Segment {
id: segment_id as u32,
Expand Down
22 changes: 9 additions & 13 deletions bitvm/src/chunk/taps_ext_miller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,18 @@ pub(crate) fn chunk_precompute_p(

// precompute P
pub(crate) fn chunk_precompute_p_from_hash(
hint_in_p: ark_bn254::G1Affine,
hint_in_p: G1AffineIsomorphic,
) -> (G1AffineIsomorphic, bool, Script, Vec<Hint>) {
println!("chunk_precompute_p_from_hash: hint_in_p {:?}", hint_in_p);
let mut hints = vec![];

let mut px: ark_bn254::Fq = ark_bn254::Fq::ONE;
let mut py: ark_bn254::Fq = ark_bn254::Fq::ONE;

let py_is_not_zero = hint_in_p.y != ark_bn254::Fq::ZERO;
let py_is_not_zero = hint_in_p.zero == false;
if py_is_not_zero {
px = hint_in_p.x;
py = hint_in_p.y;
px = hint_in_p.x();
py = hint_in_p.y();
}

let (on_curve_scr, on_curve_hint) = G1Affine::hinted_is_on_curve(px, py);
Expand Down Expand Up @@ -689,17 +690,12 @@ mod test {
let p4 = ark_bn254::G1Affine::new_unchecked(ark_bn254::Fq::ZERO, ark_bn254::Fq::ONE);
let p5 = ark_bn254::G1Affine::new_unchecked(ark_bn254::Fq::ONE, ark_bn254::Fq::ONE);
let p6 = ark_bn254::G1Affine::new_unchecked(p1.x, p1.x);
let dataset = vec![
(p1, false),
(p2, true),
(p3, true),
(p4, true),
(p5, true),
(p6, true),
];
let dataset = vec![(p1, false), (p2, true), (p3, true), (p4, true), (p5, true), (p6, true)];

for (p, disprovable) in dataset {
let (hint_out, input_is_valid, tap_prex, hint_script) = chunk_precompute_p_from_hash(p);
let (hint_out, input_is_valid, tap_prex, hint_script) = chunk_precompute_p_from_hash(
G1AffineIsomorphic::new(p.x, p.y),
);
assert_eq!(input_is_valid, !disprovable);
let hint_out = DataType::G1Data(hint_out);
let p = DataType::G1Data(G1AffineIsomorphic::new(p.x, p.y));
Expand Down
Loading
Loading