diff --git a/.gitmodules b/.gitmodules index 8c360099e..84b52d108 100644 --- a/.gitmodules +++ b/.gitmodules @@ -58,3 +58,6 @@ [submodule "vendor/snarkjs"] path = vendor/snarkjs url = git@github.com:iden3/snarkjs.git +[submodule "vendor/bls12-381-tests"] + path = vendor/bls12-381-tests + url = https://github.com/ethereum/bls12-381-tests diff --git a/beacon-light-client/plonky2/.gitignore b/beacon-light-client/plonky2/.gitignore index 2ad2bdb42..8641aa27a 100644 --- a/beacon-light-client/plonky2/.gitignore +++ b/beacon-light-client/plonky2/.gitignore @@ -2,4 +2,4 @@ gnark_plonky2_verifier/data gnark_plonky2_verifier/DendrETH gnark_plonky2_verifier/circuit/srs_setup gnark_plonky2_verifier/circuit/solidity_bytes.bin -gnark_plonky2_verifier/public_witness.bin +gnark_plonky2_verifier/public_witness.bin \ No newline at end of file diff --git a/beacon-light-client/plonky2/crates/Cargo.lock b/beacon-light-client/plonky2/crates/Cargo.lock index 91d9a1c68..3a91b8951 100644 --- a/beacon-light-client/plonky2/crates/Cargo.lock +++ b/beacon-light-client/plonky2/crates/Cargo.lock @@ -1210,6 +1210,7 @@ dependencies = [ "redis-work-queue", "serde", "serde_json", + "serde_yaml", "snowbridge-amcl", "starky", "starky_bls12_381", @@ -1222,6 +1223,9 @@ version = "0.1.0" dependencies = [ "ahash 0.8.11", "anyhow", + "ark-bls12-381", + "ark-ec", + "ark-serialize", "circuit", "circuit_derive", "hex", diff --git a/beacon-light-client/plonky2/crates/circuit_executables/Cargo.toml b/beacon-light-client/plonky2/crates/circuit_executables/Cargo.toml index 6a1d6bae0..9f5ce0291 100644 --- a/beacon-light-client/plonky2/crates/circuit_executables/Cargo.toml +++ b/beacon-light-client/plonky2/crates/circuit_executables/Cargo.toml @@ -8,7 +8,12 @@ edition.workspace = true [dependencies] circuit = { path = "../circuit" } circuits = { path = "../circuits" } -redis = { version = "0.23", features = ["aio", "async-std-comp", "connection-manager", "json"] } +redis = { version = "0.23", features = [ + "aio", + "async-std-comp", + "connection-manager", + "json", +] } num = { version = "0.4.0", features = ["serde"] } plonky2 = { git = "https://github.com/metacraft-labs/plonky2" } starky = { git = "https://github.com/metacraft-labs/plonky2" } @@ -16,6 +21,7 @@ plonky2-circuit-serializer = { git = "https://github.com/metacraft-labs/plonky2- starky_bls12_381 = { git = "https://github.com/metacraft-labs/starky_bls12_381" } serde = "1.0.164" serde_json = "1.0.96" +serde_yaml = "0.9.34" anyhow = "1.0.71" hex = "0.4.3" redis-work-queue = "0.1.3" diff --git a/beacon-light-client/plonky2/crates/circuit_executables/bin/bls12_381.rs b/beacon-light-client/plonky2/crates/circuit_executables/bin/bls12_381.rs index ddd7b6520..aa340e1d2 100644 --- a/beacon-light-client/plonky2/crates/circuit_executables/bin/bls12_381.rs +++ b/beacon-light-client/plonky2/crates/circuit_executables/bin/bls12_381.rs @@ -1,6 +1,7 @@ use anyhow::Result; use circuit::SerdeCircuitTarget; use circuit_executables::{ + bls_components::{set_bls_witness, BlsComponents, BlsProofs, Input}, constants::SERIALIZED_CIRCUITS_DIR, crud::{ common::{load_circuit_data_starky, load_common_circuit_data_starky, read_from_file}, @@ -12,8 +13,8 @@ use circuits::bls_verification::bls12_381_circuit::BlsCircuitTargets; use futures_lite::future; use plonky2::{ - field::{goldilocks_field::GoldilocksField, types::Field}, - iop::witness::{PartialWitness, WitnessWrite}, + field::goldilocks_field::GoldilocksField, + iop::witness::PartialWitness, plonk::{config::PoseidonGoldilocksConfig, proof::ProofWithPublicInputs}, util::serialization::Buffer, }; @@ -41,44 +42,33 @@ async fn async_main() -> Result<()> { let mut proof_storage = create_proof_storage(&matches).await; - let mut pw = PartialWitness::::new(); - - pw.set_target_arr( - &targets.pubkey, - &hex::decode(pubkey) - .unwrap() - .iter() - .map(|x| GoldilocksField::from_canonical_usize(*x as usize)) - .collect::>(), - ); - pw.set_target_arr( - &targets.sig, - &hex::decode(signature) - .unwrap() - .iter() - .map(|x| GoldilocksField::from_canonical_usize(*x as usize)) - .collect::>(), - ); - pw.set_target_arr( - &targets.msg, - &hex::decode(msg) - .unwrap() - .iter() - .map(|x| GoldilocksField::from_canonical_usize(*x as usize)) - .collect::>(), - ); - - let (pp1, pp2) = get_pairing_precomp_proofs(&mut proof_storage).await; - let (ml1, ml2) = get_miller_loop_proofs(&mut proof_storage).await; + let (pairing_prec_proof1, pairing_prec_proof2) = + get_pairing_precomp_proofs(&mut proof_storage).await; + let (miller_loop_proof1, miller_loop_proof2) = get_miller_loop_proofs(&mut proof_storage).await; let fp12_mul_proof = get_fp12_mul_proof(&mut proof_storage).await; let final_exp_proof = get_final_exp_proof(&mut proof_storage).await; - pw.set_proof_with_pis_target(&targets.pt_pp1, &pp1); - pw.set_proof_with_pis_target(&targets.pt_pp2, &pp2); - pw.set_proof_with_pis_target(&targets.pt_ml1, &ml1); - pw.set_proof_with_pis_target(&targets.pt_ml2, &ml2); - pw.set_proof_with_pis_target(&targets.pt_fp12m, &fp12_mul_proof); - pw.set_proof_with_pis_target(&targets.pt_fe, &final_exp_proof); + let mut pw = PartialWitness::::new(); + set_bls_witness( + &mut pw, + &targets, + &BlsComponents { + input: Input { + pubkey: pubkey.to_string(), + signature: signature.to_string(), + message: msg.to_string(), + }, + output: true, + }, + &BlsProofs { + pairing_prec_proof1, + pairing_prec_proof2, + miller_loop_proof2, + miller_loop_proof1, + fp12_mul_proof, + final_exp_proof, + }, + ); println!("Starting proof generation"); diff --git a/beacon-light-client/plonky2/crates/circuit_executables/bin/bls12_381_components_proofs.rs b/beacon-light-client/plonky2/crates/circuit_executables/bin/bls12_381_components_proofs.rs index eced9ec0f..e99bf0734 100644 --- a/beacon-light-client/plonky2/crates/circuit_executables/bin/bls12_381_components_proofs.rs +++ b/beacon-light-client/plonky2/crates/circuit_executables/bin/bls12_381_components_proofs.rs @@ -1,33 +1,29 @@ use anyhow::Result; -use ark_bls12_381::{Fq, Fq2, G1Affine, G1Projective, G2Affine}; -use ark_ec::Group; -use ark_ff::PrimeField; +use ark_bls12_381::{G1Affine, G1Projective, G2Affine}; +use ark_ec::{CurveGroup, Group}; use ark_serialize::CanonicalDeserialize; use circuit::SerdeCircuitTarget; use circuit_executables::{ + bls_components::{ + compute_native_miller_loop_from, convert_ecp2_to_g2affine, handle_final_exponentiation, + handle_fp12_mul, handle_miller_loop, handle_pairing_precomp, set_bls_witness, + BlsComponents, BlsProofs, Input, + }, constants::SERIALIZED_CIRCUITS_DIR, crud::{ - common::{get_recursive_stark_targets, load_circuit_data_starky, read_from_file}, + common::{load_circuit_data_starky, read_from_file}, proof_storage::proof_storage::create_proof_storage, }, - provers::{ - generate_final_exponentiate, generate_fp12_mul_proof, generate_miller_loop_proof, - generate_pairing_precomp_proof, - }, utils::CommandLineOptionsBuilder, }; use circuits::bls_verification::bls12_381_circuit::BlsCircuitTargets; use futures_lite::future; -use num_bigint::BigUint; use plonky2::{ - field::{goldilocks_field::GoldilocksField, types::Field}, - iop::witness::{PartialWitness, WitnessWrite}, - plonk::{config::PoseidonGoldilocksConfig, proof::ProofWithPublicInputs}, + field::goldilocks_field::GoldilocksField, iop::witness::PartialWitness, util::serialization::Buffer, }; -use snowbridge_amcl::bls381::{big::Big, bls381::utils::hash_to_curve_g2, ecp2::ECP2}; -use starky_bls12_381::native::{miller_loop, Fp, Fp12, Fp2}; -use std::{ops::Neg, str::FromStr}; +use snowbridge_amcl::bls381::bls381::utils::hash_to_curve_g2; +use std::ops::Neg; async fn async_main() -> Result<()> { let matches = CommandLineOptionsBuilder::new("bls12_381_components_proofs") @@ -38,55 +34,29 @@ async fn async_main() -> Result<()> { let signature = "b735d0d0b03f51fcf3e5bc510b5a2cb266075322f5761a6954778714f5ab8831bc99454380d330f5c19d93436f0c4339041bfeecd2161a122c1ce8428033db8dda142768a48e582f5f9bde7d40768ac5a3b6a80492b73719f1523c5da35de275"; let msg = "5bb03392c9c8a8b92c840338f619bb060b109b254c9ab75d4dddc6d00932bce3"; - let message_g2 = hash_to_curve_g2(&hex::decode(msg).unwrap(), DST.as_bytes()); - let message_g2 = convert_ecp2_to_g2affine(message_g2); - - let pubkey_g1 = G1Affine::deserialize_compressed(&*hex::decode(pubkey).unwrap()).unwrap(); - let signature_g2 = G2Affine::deserialize_compressed(&*hex::decode(signature).unwrap()).unwrap(); + let message_g2 = convert_ecp2_to_g2affine(hash_to_curve_g2( + &hex::decode(&msg).unwrap(), + DST.as_bytes(), + )); + let pubkey_g1 = + G1Affine::deserialize_compressed_unchecked(&*hex::decode(pubkey).unwrap()).unwrap(); + let signature_g2 = + G2Affine::deserialize_compressed_unchecked(&*hex::decode(signature).unwrap()).unwrap(); let g1 = G1Projective::generator(); let neg_g1 = g1.neg(); - let miller_loop1 = miller_loop( - Fp::get_fp_from_biguint(pubkey_g1.x.to_string().parse::().unwrap()), - Fp::get_fp_from_biguint(pubkey_g1.y.to_string().parse::().unwrap()), - Fp2([ - Fp::get_fp_from_biguint(message_g2.x.c0.to_string().parse::().unwrap()), - Fp::get_fp_from_biguint(message_g2.x.c1.to_string().parse::().unwrap()), - ]), - Fp2([ - Fp::get_fp_from_biguint(message_g2.y.c0.to_string().parse::().unwrap()), - Fp::get_fp_from_biguint(message_g2.y.c1.to_string().parse::().unwrap()), - ]), - Fp2([ - Fp::get_fp_from_biguint(BigUint::from_str("1").unwrap()), - Fp::get_fp_from_biguint(BigUint::from_str("0").unwrap()), - ]), - ); + let miller_loop1 = compute_native_miller_loop_from(pubkey_g1, message_g2); - let miller_loop2 = miller_loop( - Fp::get_fp_from_biguint(neg_g1.x.to_string().parse::().unwrap()), - Fp::get_fp_from_biguint(neg_g1.y.to_string().parse::().unwrap()), - Fp2([ - Fp::get_fp_from_biguint(signature_g2.x.c0.to_string().parse::().unwrap()), - Fp::get_fp_from_biguint(signature_g2.x.c1.to_string().parse::().unwrap()), - ]), - Fp2([ - Fp::get_fp_from_biguint(signature_g2.y.c0.to_string().parse::().unwrap()), - Fp::get_fp_from_biguint(signature_g2.y.c1.to_string().parse::().unwrap()), - ]), - Fp2([ - Fp::get_fp_from_biguint(BigUint::from_str("1").unwrap()), - Fp::get_fp_from_biguint(BigUint::from_str("0").unwrap()), - ]), - ); + let miller_loop2 = compute_native_miller_loop_from(neg_g1.into_affine(), signature_g2); let fp12_mull = miller_loop1 * miller_loop2; // PROVING HAPPENS HERE let mut proof_storage = create_proof_storage(&matches).await; - let (pp1, pp2) = handle_pairing_precomp(&message_g2, &signature_g2).await; + let (pairing_prec_proof1, pairing_prec_proof2) = + handle_pairing_precomp(&message_g2, &signature_g2).await; - let (ml1, ml2) = + let (miller_loop_proof1, miller_loop_proof2) = handle_miller_loop(&pubkey_g1, &message_g2, &neg_g1.into(), &signature_g2).await; let fp12_mul_proof = handle_fp12_mul(&miller_loop1, &miller_loop2).await; @@ -103,37 +73,26 @@ async fn async_main() -> Result<()> { let targets = BlsCircuitTargets::deserialize(&mut target_buffer).unwrap(); let mut pw = PartialWitness::::new(); - pw.set_target_arr( - &targets.pubkey, - &hex::decode(pubkey) - .unwrap() - .iter() - .map(|x| GoldilocksField::from_canonical_usize(*x as usize)) - .collect::>(), + set_bls_witness( + &mut pw, + &targets, + &BlsComponents { + input: Input { + pubkey: pubkey.to_string(), + signature: signature.to_string(), + message: msg.to_string(), + }, + output: true, + }, + &BlsProofs { + pairing_prec_proof1, + pairing_prec_proof2, + miller_loop_proof2, + miller_loop_proof1, + fp12_mul_proof, + final_exp_proof, + }, ); - pw.set_target_arr( - &targets.sig, - &hex::decode(signature) - .unwrap() - .iter() - .map(|x| GoldilocksField::from_canonical_usize(*x as usize)) - .collect::>(), - ); - pw.set_target_arr( - &targets.msg, - &hex::decode(msg) - .unwrap() - .iter() - .map(|x| GoldilocksField::from_canonical_usize(*x as usize)) - .collect::>(), - ); - - pw.set_proof_with_pis_target(&targets.pt_pp1, &pp1); - pw.set_proof_with_pis_target(&targets.pt_pp2, &pp2); - pw.set_proof_with_pis_target(&targets.pt_ml1, &ml1); - pw.set_proof_with_pis_target(&targets.pt_ml2, &ml2); - pw.set_proof_with_pis_target(&targets.pt_fp12m, &fp12_mul_proof); - pw.set_proof_with_pis_target(&targets.pt_fe, &final_exp_proof); println!("Starting proof generation"); @@ -153,75 +112,6 @@ async fn async_main() -> Result<()> { Ok(()) } -async fn handle_final_exponentiation( - fp12_mull: &Fp12, -) -> ProofWithPublicInputs { - let final_exp_circuit_data = load_circuit_data_starky(&format!( - "{SERIALIZED_CIRCUITS_DIR}/final_exponentiate_circuit" - )); - - let final_exp_targets = get_recursive_stark_targets(&format!( - "{SERIALIZED_CIRCUITS_DIR}/final_exponentiate_circuit" - )) - .unwrap(); - - let final_exp_proof = - generate_final_exponentiate(&fp12_mull, &final_exp_targets, &final_exp_circuit_data); - - final_exp_proof -} - -async fn handle_fp12_mul( - miller_loop1: &Fp12, - miller_loop2: &Fp12, -) -> ProofWithPublicInputs { - let fp12_mul_circuit_data = - load_circuit_data_starky(&format!("{SERIALIZED_CIRCUITS_DIR}/fp12_mul")); - - let fp12_mul_targets = - get_recursive_stark_targets(&format!("{SERIALIZED_CIRCUITS_DIR}/fp12_mul")).unwrap(); - - let fp12_mul_proof = generate_fp12_mul_proof( - &miller_loop1, - &miller_loop2, - &fp12_mul_targets, - &fp12_mul_circuit_data, - ); - - fp12_mul_proof -} - -async fn handle_miller_loop( - pubkey_g1: &G1Affine, - message_g2: &G2Affine, - neg_g1: &G1Affine, - signature_g2: &G2Affine, -) -> ( - ProofWithPublicInputs, - ProofWithPublicInputs, -) { - let miller_loop_circuit_data = - load_circuit_data_starky(&format!("{SERIALIZED_CIRCUITS_DIR}/miller_loop")); - - let miller_loop_targets = - get_recursive_stark_targets(&format!("{SERIALIZED_CIRCUITS_DIR}/miller_loop")).unwrap(); - - let ml1 = generate_miller_loop_proof( - &pubkey_g1, - &message_g2, - &miller_loop_targets, - &miller_loop_circuit_data, - ); - - let ml2 = generate_miller_loop_proof( - &neg_g1, - &signature_g2, - &miller_loop_targets, - &miller_loop_circuit_data, - ); - - (ml1, ml2) -} fn main() { let _ = std::thread::Builder::new() .spawn(|| future::block_on(async_main())) @@ -229,50 +119,3 @@ fn main() { .join() .unwrap(); } - -async fn handle_pairing_precomp( - message_g2: &G2Affine, - signature_g2: &G2Affine, -) -> ( - ProofWithPublicInputs, - ProofWithPublicInputs, -) { - let pairing_precomp_circuit_data = - load_circuit_data_starky(&format!("{SERIALIZED_CIRCUITS_DIR}/pairing_precomp")); - - let pairing_precomp_targets = - get_recursive_stark_targets(&format!("{SERIALIZED_CIRCUITS_DIR}/pairing_precomp")).unwrap(); - - let pp1 = generate_pairing_precomp_proof( - &message_g2, - &pairing_precomp_targets, - &pairing_precomp_circuit_data, - ); - - let pp2 = generate_pairing_precomp_proof( - &signature_g2, - &pairing_precomp_targets, - &pairing_precomp_circuit_data, - ); - - (pp1, pp2) -} - -fn convert_ecp2_to_g2affine(ecp2_point: ECP2) -> G2Affine { - let x = Fq2::new( - convert_big_to_fq(ecp2_point.getpx().geta()), - convert_big_to_fq(ecp2_point.getpx().getb()), - ); - - let y = Fq2::new( - convert_big_to_fq(ecp2_point.getpy().geta()), - convert_big_to_fq(ecp2_point.getpy().getb()), - ); - - G2Affine::new(x, y) -} - -fn convert_big_to_fq(big: Big) -> Fq { - let bytes = &hex::decode(big.to_string()).unwrap(); - Fq::from_be_bytes_mod_order(bytes) -} diff --git a/beacon-light-client/plonky2/crates/circuit_executables/scripts/run_bls12_381_component_proofs.sh b/beacon-light-client/plonky2/crates/circuit_executables/scripts/run_bls12_381_component_proofs.sh new file mode 100755 index 000000000..95781fa20 --- /dev/null +++ b/beacon-light-client/plonky2/crates/circuit_executables/scripts/run_bls12_381_component_proofs.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# Define variables +REPO_URL="https://github.com/ethereum/bls12-381-tests" +REPO_DIR="bls12-381-tests" +OUTPUT_DIR="eth_tests" +VERIFY_DIR="$OUTPUT_DIR/bls/verify" +FROM_CIRCUIT_EXECUTABLES_TO="../../../.." +VENDOR="vendor" + +command pushd $GIT_ROOT/beacon-light-client/plonky2/crates/circuit_executables "$@" > /dev/null + +# Store the current directory +CIRCUIT_EXECUTABLES_DIR=$(pwd) + +cd "$FROM_CIRCUIT_EXECUTABLES_TO/$VENDOR" + +# Clone the repository +if [ ! -d "$REPO_DIR" ]; then + git clone "$REPO_URL" +else + echo "Repository already cloned." +fi + +# Navigate to the repository directory +cd "$REPO_DIR" || exit + +# Set up a Python virtual environment +if [ ! -d "venv" ]; then + python -m venv venv +else + echo "Virtual environment already set up." +fi + +# Activate the virtual environment +# shellcheck source=/dev/null +source venv/bin/activate + +# Install dependencies +pip install -r requirements.txt + +# Create output directory if it doesn't exist +mkdir -p "$OUTPUT_DIR" + +# Run the test generator +python main.py --output-dir="$OUTPUT_DIR" --encoding=yaml + +# Deactivate the virtual environment +deactivate + +# Navigate to the circuit executables directory +cd "../.." +cd "$CIRCUIT_EXECUTABLES_DIR" + +# Store all files in a variable +mapfile -t all_yaml_files_in_verify < <(ls *) + +# Run the verify tests +run_verify_tests() { + local test_name=$1 + local file_path=$2 + + # Set the FILE_PATH environment variable + export FILE_PATH="$file_path" + + # Run the specified Rust test with the given file path + RUST_MIN_STACK=1116777216 cargo test "$test_name" --release -- --nocapture "$file_path" +} + +# Loop through the extracted files in the 'verify' directory +for yaml_file in "${all_yaml_files_in_verify[@]}"; do + run_verify_tests "test_bls12_381_components_proofs_with_verify_eth_cases" "$yaml_file" +done + +command popd "$@" > /dev/null \ No newline at end of file diff --git a/beacon-light-client/plonky2/crates/circuit_executables/src/bls_components.rs b/beacon-light-client/plonky2/crates/circuit_executables/src/bls_components.rs new file mode 100644 index 000000000..a21302d87 --- /dev/null +++ b/beacon-light-client/plonky2/crates/circuit_executables/src/bls_components.rs @@ -0,0 +1,430 @@ +use anyhow::Result; +use ark_bls12_381::{Fq, Fq2, G1Affine, G1Projective, G2Affine}; +use ark_ec::{CurveGroup, Group}; +use ark_ff::PrimeField; +use ark_serialize::CanonicalDeserialize; +use circuit::SerdeCircuitTarget; +use circuits::bls_verification::bls12_381_circuit::BlsCircuitTargets; +use num::BigUint; +use plonky2::{ + field::{goldilocks_field::GoldilocksField, types::Field}, + iop::witness::{PartialWitness, WitnessWrite}, + plonk::{config::PoseidonGoldilocksConfig, proof::ProofWithPublicInputs}, + util::serialization::Buffer, +}; +use serde::Deserialize; +use snowbridge_amcl::bls381::{big::Big, bls381::utils::hash_to_curve_g2, ecp2::ECP2}; +use starky_bls12_381::native::{miller_loop, Fp, Fp12, Fp2}; +use std::{fs, ops::Neg, path::Path, str::FromStr}; + +use crate::{ + constants::SERIALIZED_CIRCUITS_DIR, + crud::common::{get_recursive_stark_targets, load_circuit_data_starky, read_from_file}, + provers::{ + generate_final_exponentiate, generate_fp12_mul_proof, generate_miller_loop_proof, + generate_pairing_precomp_proof, + }, +}; + +const DST: &str = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + +const D: usize = 2; +type C = PoseidonGoldilocksConfig; +type F = GoldilocksField; + +#[derive(Clone, Debug, Deserialize)] +pub struct Input { + pub pubkey: String, + pub signature: String, + pub message: String, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct BlsComponents { + pub input: Input, + pub output: bool, +} + +pub struct BlsProofs { + pub pairing_prec_proof1: ProofWithPublicInputs, + pub pairing_prec_proof2: ProofWithPublicInputs, + pub miller_loop_proof2: ProofWithPublicInputs, + pub miller_loop_proof1: ProofWithPublicInputs, + pub fp12_mul_proof: ProofWithPublicInputs, + pub final_exp_proof: ProofWithPublicInputs, +} + +impl BlsComponents { + fn remove_first_two_chars(&mut self) { + self.input.pubkey = self.input.pubkey.chars().skip(2).collect(); + self.input.signature = self.input.signature.chars().skip(2).collect(); + self.input.message = self.input.message.chars().skip(2).collect(); + } +} + +pub async fn bls12_381_components_proofs( + components: &BlsComponents, +) -> Result> { + let message_g2 = convert_ecp2_to_g2affine(hash_to_curve_g2( + &hex::decode(&components.input.message).unwrap(), + DST.as_bytes(), + )); + let signature_g2 = G2Affine::deserialize_compressed_unchecked( + &*hex::decode(&components.input.signature).unwrap(), + ) + .unwrap(); + let pubkey_g1 = G1Affine::deserialize_compressed_unchecked( + &*hex::decode(&components.input.pubkey).unwrap(), + ) + .unwrap(); + let neg_g1 = G1Projective::generator().neg(); + + let miller_loop1 = compute_native_miller_loop_from(pubkey_g1, message_g2); + + let miller_loop2 = compute_native_miller_loop_from(neg_g1.into_affine(), signature_g2); + + let fp12_mul = miller_loop1 * miller_loop2; + + // PROVING HAPPENS HERE + let (pairing_prec_proof1, pairing_prec_proof2) = + handle_pairing_precomp(&message_g2, &signature_g2).await; + + let (miller_loop_proof1, miller_loop_proof2) = + handle_miller_loop(&pubkey_g1, &message_g2, &neg_g1.into(), &signature_g2).await; + + let fp12_mul_proof = handle_fp12_mul(&miller_loop1, &miller_loop2).await; + + let final_exp_proof = handle_final_exponentiation(&fp12_mul).await; + + let circuit_data = load_circuit_data_starky(&format!("{SERIALIZED_CIRCUITS_DIR}/bls12_381")); + let target_bytes = read_from_file(&format!( + "{}/{}.plonky2_targets", + SERIALIZED_CIRCUITS_DIR, "bls12_381" + ))?; + let mut target_buffer = Buffer::new(&target_bytes); + + let targets = BlsCircuitTargets::deserialize(&mut target_buffer).unwrap(); + let proofs = BlsProofs { + pairing_prec_proof1, + pairing_prec_proof2, + miller_loop_proof1, + miller_loop_proof2, + fp12_mul_proof, + final_exp_proof, + }; + + let mut pw = PartialWitness::::new(); + set_bls_witness(&mut pw, &targets, &components, &proofs); + + println!("Starting proof generation"); + + let proof = circuit_data.prove(pw).unwrap(); + + Ok(proof) +} + +pub async fn handle_final_exponentiation(fp12_mull: &Fp12) -> ProofWithPublicInputs { + let final_exp_circuit_data = load_circuit_data_starky(&format!( + "{SERIALIZED_CIRCUITS_DIR}/final_exponentiate_circuit" + )); + + let final_exp_targets = get_recursive_stark_targets(&format!( + "{SERIALIZED_CIRCUITS_DIR}/final_exponentiate_circuit" + )) + .unwrap(); + + let final_exp_proof = + generate_final_exponentiate(&fp12_mull, &final_exp_targets, &final_exp_circuit_data); + + final_exp_proof +} + +pub async fn handle_fp12_mul( + miller_loop1: &Fp12, + miller_loop2: &Fp12, +) -> ProofWithPublicInputs { + let fp12_mul_circuit_data = + load_circuit_data_starky(&format!("{SERIALIZED_CIRCUITS_DIR}/fp12_mul")); + + let fp12_mul_targets = + get_recursive_stark_targets(&format!("{SERIALIZED_CIRCUITS_DIR}/fp12_mul")).unwrap(); + + let fp12_mul_proof = generate_fp12_mul_proof( + &miller_loop1, + &miller_loop2, + &fp12_mul_targets, + &fp12_mul_circuit_data, + ); + + fp12_mul_proof +} + +pub async fn handle_miller_loop( + pubkey_g1: &G1Affine, + message_g2: &G2Affine, + neg_g1: &G1Affine, + signature_g2: &G2Affine, +) -> ( + ProofWithPublicInputs, + ProofWithPublicInputs, +) { + let miller_loop_circuit_data = + load_circuit_data_starky(&format!("{SERIALIZED_CIRCUITS_DIR}/miller_loop")); + + let miller_loop_targets = + get_recursive_stark_targets(&format!("{SERIALIZED_CIRCUITS_DIR}/miller_loop")).unwrap(); + + let ml1 = generate_miller_loop_proof( + &pubkey_g1, + &message_g2, + &miller_loop_targets, + &miller_loop_circuit_data, + ); + + let ml2 = generate_miller_loop_proof( + &neg_g1, + &signature_g2, + &miller_loop_targets, + &miller_loop_circuit_data, + ); + + (ml1, ml2) +} + +pub async fn handle_pairing_precomp( + message_g2: &G2Affine, + signature_g2: &G2Affine, +) -> ( + ProofWithPublicInputs, + ProofWithPublicInputs, +) { + let pairing_precomp_circuit_data = + load_circuit_data_starky(&format!("{SERIALIZED_CIRCUITS_DIR}/pairing_precomp")); + + let pairing_precomp_targets = + get_recursive_stark_targets(&format!("{SERIALIZED_CIRCUITS_DIR}/pairing_precomp")).unwrap(); + + let pp1 = generate_pairing_precomp_proof( + &message_g2, + &pairing_precomp_targets, + &pairing_precomp_circuit_data, + ); + + let pp2 = generate_pairing_precomp_proof( + &signature_g2, + &pairing_precomp_targets, + &pairing_precomp_circuit_data, + ); + + (pp1, pp2) +} + +pub fn read_yaml_file>( + path: P, +) -> Result> { + let file_content = fs::read_to_string(path)?; + let mut components: BlsComponents = serde_yaml::from_str(&file_content)?; + components.remove_first_two_chars(); + Ok(components) +} + +pub fn set_bls_witness( + pw: &mut PartialWitness, + targets: &BlsCircuitTargets, + components: &BlsComponents, + proofs: &BlsProofs, +) { + pw.set_target_arr( + &targets.pubkey, + &hex::decode(&components.input.pubkey) + .unwrap() + .iter() + .map(|x| F::from_canonical_usize(*x as usize)) + .collect::>(), + ); + pw.set_target_arr( + &targets.sig, + &hex::decode(&components.input.signature) + .unwrap() + .iter() + .map(|x| F::from_canonical_usize(*x as usize)) + .collect::>(), + ); + pw.set_target_arr( + &targets.msg, + &hex::decode(&components.input.message) + .unwrap() + .iter() + .map(|x| F::from_canonical_usize(*x as usize)) + .collect::>(), + ); + + pw.set_proof_with_pis_target(&targets.pt_pp1, &proofs.pairing_prec_proof1); + pw.set_proof_with_pis_target(&targets.pt_pp2, &proofs.pairing_prec_proof2); + pw.set_proof_with_pis_target(&targets.pt_ml1, &proofs.miller_loop_proof1); + pw.set_proof_with_pis_target(&targets.pt_ml2, &proofs.miller_loop_proof2); + pw.set_proof_with_pis_target(&targets.pt_fp12m, &proofs.fp12_mul_proof); + pw.set_proof_with_pis_target(&targets.pt_fe, &proofs.final_exp_proof); +} + +pub fn compute_native_miller_loop_from( + g1_affine_point: G1Affine, + g2_affine_point: G2Affine, +) -> Fp12 { + miller_loop( + Fp::get_fp_from_biguint(g1_affine_point.x.to_string().parse::().unwrap()), + Fp::get_fp_from_biguint(g1_affine_point.y.to_string().parse::().unwrap()), + Fp2([ + Fp::get_fp_from_biguint(g2_affine_point.x.c0.to_string().parse::().unwrap()), + Fp::get_fp_from_biguint(g2_affine_point.x.c1.to_string().parse::().unwrap()), + ]), + Fp2([ + Fp::get_fp_from_biguint(g2_affine_point.y.c0.to_string().parse::().unwrap()), + Fp::get_fp_from_biguint(g2_affine_point.y.c1.to_string().parse::().unwrap()), + ]), + Fp2([ + Fp::get_fp_from_biguint(BigUint::from_str("1").unwrap()), + Fp::get_fp_from_biguint(BigUint::from_str("0").unwrap()), + ]), + ) +} + +pub fn convert_ecp2_to_g2affine(ecp2_point: ECP2) -> G2Affine { + let x = Fq2::new( + convert_big_to_fq(ecp2_point.getpx().geta()), + convert_big_to_fq(ecp2_point.getpx().getb()), + ); + + let y = Fq2::new( + convert_big_to_fq(ecp2_point.getpy().geta()), + convert_big_to_fq(ecp2_point.getpy().getb()), + ); + + G2Affine::new(x, y) +} + +pub fn convert_big_to_fq(big: Big) -> Fq { + let bytes = &hex::decode(big.to_string()).unwrap(); + Fq::from_be_bytes_mod_order(bytes) +} + +#[cfg(test)] +pub mod tests { + use std::env; + + use plonky2::{ + field::goldilocks_field::GoldilocksField, + plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig}, + }; + + use super::{bls12_381_components_proofs, read_yaml_file}; + + const PATH_TO_VERIFY_ETH_TEST_CASES: &str = "scripts/bls12-381-tests/eth_tests/bls/verify"; + const D: usize = 2; + type F = GoldilocksField; + + #[tokio::test] + async fn test_bls12_381_components_proofs_with_verify_eth_cases() { + let args: Vec = env::args().collect(); + if args.len() < 3 { + panic!("Expected a file path as argument"); + } + + let current_file_path = format!("{}/{}", PATH_TO_VERIFY_ETH_TEST_CASES, &args[3]); + println!("current file path is: {:?}", ¤t_file_path); + let bls_components = + read_yaml_file(format!("{}/{}", PATH_TO_VERIFY_ETH_TEST_CASES, &args[3])).unwrap(); + let standard_recursion_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(standard_recursion_config); + + println!("current pubkey: {:?}", bls_components.input.pubkey); + println!("current signature: {:?}", bls_components.input.signature); + println!("current message: {:?}", bls_components.input.message); + let proof = bls12_381_components_proofs(&bls_components).await.unwrap(); + + println!( + "Is valid signature {}", + proof.public_inputs[proof.public_inputs.len() - 1] + ); + + let proof_t = builder.constant(proof.public_inputs[proof.public_inputs.len() - 1]); + if bls_components.output { + builder.assert_one(proof_t); + } else { + builder.assert_zero(proof_t); + } + + println!( + "test case is VALID for: pubkey: {:?}, signature: {:?} and message: {:?}", + bls_components.input.pubkey, + bls_components.input.signature, + bls_components.input.message, + ); + } + + #[tokio::test] + async fn test_bls12_381_at_infinity_case() { + let file_path = "verify_infinity_pubkey_and_infinity_signature.yaml"; + let bls_components = + read_yaml_file(format!("{}/{}", PATH_TO_VERIFY_ETH_TEST_CASES, file_path)).unwrap(); + let standard_recursion_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(standard_recursion_config); + + println!("current pubkey: {:?}", bls_components.input.pubkey); + println!("current signature: {:?}", bls_components.input.signature); + println!("current message: {:?}", bls_components.input.message); + let proof = bls12_381_components_proofs(&bls_components).await.unwrap(); + + println!( + "Is valid signature {}", + proof.public_inputs[proof.public_inputs.len() - 1] + ); + + let proof_t = builder.constant(proof.public_inputs[proof.public_inputs.len() - 1]); + if bls_components.output { + builder.assert_one(proof_t); + } else { + builder.assert_zero(proof_t); + } + + println!( + "test case is VALID for: pubkey: {:?}, signature: {:?} and message: {:?}", + bls_components.input.pubkey, + bls_components.input.signature, + bls_components.input.message, + ); + } + + #[tokio::test] + async fn test_bls12_381_one_privkey() { + let file_path = "verifycase_one_privkey_47117849458281be.yaml"; + let bls_components = + read_yaml_file(format!("{}/{}", PATH_TO_VERIFY_ETH_TEST_CASES, file_path)).unwrap(); + let standard_recursion_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(standard_recursion_config); + + println!("current pubkey: {:?}", bls_components.input.pubkey); + println!("current signature: {:?}", bls_components.input.signature); + println!("current message: {:?}", bls_components.input.message); + let proof = bls12_381_components_proofs(&bls_components).await.unwrap(); + + println!( + "Is valid signature {}", + proof.public_inputs[proof.public_inputs.len() - 1] + ); + + let proof_t = builder.constant(proof.public_inputs[proof.public_inputs.len() - 1]); + if bls_components.output { + builder.assert_one(proof_t); + } else { + builder.assert_zero(proof_t); + } + + println!( + "test case is VALID for: pubkey: {:?}, signature: {:?} and message: {:?}", + bls_components.input.pubkey, + bls_components.input.signature, + bls_components.input.message, + ); + } +} diff --git a/beacon-light-client/plonky2/crates/circuit_executables/src/lib.rs b/beacon-light-client/plonky2/crates/circuit_executables/src/lib.rs index 7a0240689..4e9091836 100644 --- a/beacon-light-client/plonky2/crates/circuit_executables/src/lib.rs +++ b/beacon-light-client/plonky2/crates/circuit_executables/src/lib.rs @@ -2,6 +2,7 @@ #![feature(async_closure)] #![feature(generic_const_exprs)] +pub mod bls_components; pub mod commitment_mapper_context; pub mod commitment_mapper_task; pub mod constants; diff --git a/beacon-light-client/plonky2/crates/circuits/Cargo.toml b/beacon-light-client/plonky2/crates/circuits/Cargo.toml index 3f238d5cc..086b0376e 100644 --- a/beacon-light-client/plonky2/crates/circuits/Cargo.toml +++ b/beacon-light-client/plonky2/crates/circuits/Cargo.toml @@ -27,6 +27,9 @@ starky_bls12_381 = { git = "https://github.com/metacraft-labs/starky_bls12_381" plonky2_crypto = { git = "https://github.com/metacraft-labs/plonky2-crypto" } plonky2-circuit-serializer = { git = "https://github.com/metacraft-labs/plonky2-circuit-serializer" } num-bigint = "0.4.4" +ark-bls12-381 = "0.4.0" +ark-serialize = "0.4.2" +ark-ec = "0.4.2" [dev-dependencies] serde_yaml = "0.9.34" diff --git a/beacon-light-client/plonky2/crates/circuits/src/bls_verification/bls12_381_circuit.rs b/beacon-light-client/plonky2/crates/circuits/src/bls_verification/bls12_381_circuit.rs index cbb2842ea..28a69ebfb 100644 --- a/beacon-light-client/plonky2/crates/circuits/src/bls_verification/bls12_381_circuit.rs +++ b/beacon-light-client/plonky2/crates/circuits/src/bls_verification/bls12_381_circuit.rs @@ -28,7 +28,7 @@ use starky_bls12_381::{ use crate::utils::circuit::verify_proof; -const N: usize = 12; +pub const N: usize = 12; #[derive(CircuitTarget, SerdeCircuitTarget)] pub struct BlsCircuitTargets { @@ -112,7 +112,7 @@ impl Circuit for BLSVerificationCircuit { connect_miller_loop_with_g1(builder, &neg_generator, &pt_ml2); - connect_miller_loop_with_fp12_mull(builder, &pt_ml1, &pt_ml2, &pt_fp12m); + connect_miller_loop_with_fp12_mul(builder, &pt_ml1, &pt_ml2, &pt_fp12m); connect_fp12_mull_with_final_exponentiation(builder, &pt_fp12m, &pt_fe); @@ -148,20 +148,7 @@ impl Circuit for BLSVerificationCircuit { } } -fn connect_fp12_mull_with_final_exponentiation, const D: usize>( - builder: &mut CircuitBuilder, - pt_fp12m: &ProofWithPublicInputsTarget, - pt_fe: &ProofWithPublicInputsTarget, -) { - for i in 0..24 * 3 * 2 { - builder.connect( - pt_fp12m.public_inputs[fp12_mul::PIS_OUTPUT_OFFSET + i], - pt_fe.public_inputs[final_exponentiate::PIS_INPUT_OFFSET + i], - ); - } -} - -fn get_neg_generator, const D: usize>( +pub fn get_neg_generator, const D: usize>( builder: &mut CircuitBuilder, ) -> PointG1Target { let neg_generator_x = builder.constant_biguint(&BigUint::from_str("3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507").unwrap()); @@ -170,7 +157,7 @@ fn get_neg_generator, const D: usize>( [neg_generator_x, neg_generator_y] } -fn get_g2_point_from_pairing_precomp, const D: usize>( +pub fn get_g2_point_from_pairing_precomp, const D: usize>( builder: &mut CircuitBuilder, pt_pp2: &ProofWithPublicInputsTarget, ) -> PointG2Target { @@ -230,7 +217,42 @@ fn get_g2_point_from_pairing_precomp, const D: usiz [[sig_point_x0, sig_point_x1], [sig_point_y0, sig_point_y1]] } -fn connect_miller_loop_with_fp12_mull, const D: usize>( +pub fn get_g1_from_miller_loop(pt_ml1: &ProofWithPublicInputsTarget) -> PointG1Target { + let g1_x = BigUintTarget { + limbs: (0..N) + .into_iter() + .map(|i| { + U32Target(pt_ml1.public_inputs[calc_pairing_precomp::X0_PUBLIC_INPUTS_OFFSET + i]) + }) + .collect(), + }; + + let g1_y = BigUintTarget { + limbs: (0..N) + .into_iter() + .map(|i| { + U32Target(pt_ml1.public_inputs[calc_pairing_precomp::X1_PUBLIC_INPUTS_OFFSET + i]) + }) + .collect(), + }; + + [g1_x, g1_y] +} + +fn connect_fp12_mull_with_final_exponentiation, const D: usize>( + builder: &mut CircuitBuilder, + pt_fp12m: &ProofWithPublicInputsTarget, + pt_fe: &ProofWithPublicInputsTarget, +) { + for i in 0..24 * 3 * 2 { + builder.connect( + pt_fp12m.public_inputs[fp12_mul::PIS_OUTPUT_OFFSET + i], + pt_fe.public_inputs[final_exponentiate::PIS_INPUT_OFFSET + i], + ); + } +} + +fn connect_miller_loop_with_fp12_mul, const D: usize>( builder: &mut CircuitBuilder, pt_ml1: &ProofWithPublicInputsTarget, pt_ml2: &ProofWithPublicInputsTarget, @@ -269,28 +291,6 @@ fn connect_miller_loop_with_g1, const D: usize>( } } -fn get_g1_from_miller_loop(pt_ml1: &ProofWithPublicInputsTarget) -> PointG1Target { - let g1_x = BigUintTarget { - limbs: (0..N) - .into_iter() - .map(|i| { - U32Target(pt_ml1.public_inputs[calc_pairing_precomp::X0_PUBLIC_INPUTS_OFFSET + i]) - }) - .collect(), - }; - - let g1_y = BigUintTarget { - limbs: (0..N) - .into_iter() - .map(|i| { - U32Target(pt_ml1.public_inputs[calc_pairing_precomp::X1_PUBLIC_INPUTS_OFFSET + i]) - }) - .collect(), - }; - - [g1_x, g1_y] -} - fn connect_pairing_precomp_with_miller_loop_g2, const D: usize>( builder: &mut CircuitBuilder, pt_pp1: &plonky2::plonk::proof::ProofWithPublicInputsTarget, diff --git a/beacon-light-client/plonky2/crates/circuits/src/bls_verification/mod.rs b/beacon-light-client/plonky2/crates/circuits/src/bls_verification/mod.rs index 83f92cfef..05eed362c 100644 --- a/beacon-light-client/plonky2/crates/circuits/src/bls_verification/mod.rs +++ b/beacon-light-client/plonky2/crates/circuits/src/bls_verification/mod.rs @@ -1,2 +1,3 @@ pub mod bls12_381_circuit; pub mod build_stark_proof_verifier; +pub mod verify_is_not_at_infinity_circuit; diff --git a/beacon-light-client/plonky2/crates/circuits/src/bls_verification/verify_is_not_at_infinity_circuit.rs b/beacon-light-client/plonky2/crates/circuits/src/bls_verification/verify_is_not_at_infinity_circuit.rs new file mode 100644 index 000000000..ed28ea8e5 --- /dev/null +++ b/beacon-light-client/plonky2/crates/circuits/src/bls_verification/verify_is_not_at_infinity_circuit.rs @@ -0,0 +1,266 @@ +use ark_bls12_381::g1::{G1_GENERATOR_X, G1_GENERATOR_Y}; +use circuit_derive::{CircuitTarget, SerdeCircuitTarget}; +use num::BigUint; +use plonky2::{ + iop::target::{BoolTarget, Target}, + plonk::{ + circuit_builder::CircuitBuilder, + circuit_data::CircuitConfig, + config::{GenericConfig, PoseidonGoldilocksConfig}, + }, +}; +use plonky2_crypto::biguint::{BigUintTarget, CircuitBuilderBiguint}; +use starky_bls12_381::{ + fp2_plonky2::is_zero, + fp_plonky2::{is_equal, FpTarget}, + g1_plonky2::{pk_point_check, PointG1Target}, + g2_plonky2::{signature_point_check, PointG2Target}, +}; + +use circuit::{circuit_builder_extensions::CircuitBuilderExtensions, Circuit}; + +pub type F = >::F; +pub type C = PoseidonGoldilocksConfig; +pub const D: usize = 2; + +pub struct VerifyIsNotAtInfinityCircuit; + +#[derive(CircuitTarget, SerdeCircuitTarget)] +pub struct VerifyIsNotAtInfinityCircuitTargets { + // Pub inputs + #[target(in, out)] + pub pubkey_bytes: [Target; 48], + + #[target(in, out)] + pub sig_bytes: [Target; 96], + + pub pubkey_g1_x: BigUintTarget, + pub pubkey_g1_y: BigUintTarget, + pub sig_g2_x_c0: BigUintTarget, + pub sig_g2_x_c1: BigUintTarget, + pub sig_g2_y_c0: BigUintTarget, + pub sig_g2_y_c1: BigUintTarget, +} + +impl Circuit for VerifyIsNotAtInfinityCircuit { + type F = F; + type C = C; + const D: usize = D; + + const CIRCUIT_CONFIG: CircuitConfig = CircuitConfig::standard_recursion_config(); + type Target = VerifyIsNotAtInfinityCircuitTargets; + + fn define( + builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + _: &Self::Params, + ) -> Self::Target { + let input = Self::read_circuit_input_target(builder); + + let pubkey_g1_x = builder.add_virtual_biguint_target(12); + let pubkey_g1_y = builder.add_virtual_biguint_target(12); + + let sig_g2_x_c0 = builder.add_virtual_biguint_target(12); + let sig_g2_x_c1 = builder.add_virtual_biguint_target(12); + let sig_g2_y_c0 = builder.add_virtual_biguint_target(12); + let sig_g2_y_c1 = builder.add_virtual_biguint_target(12); + let pubkey_bytes = input.pubkey_bytes; + + let pubkey_g1 = [pubkey_g1_x.to_owned(), pubkey_g1_y.to_owned()]; + pk_point_check(builder, &pubkey_g1, &pubkey_bytes); + assert_pk_ne_g1_generator(builder, &pubkey_g1); + let is_g1_point_is_at_infinity = is_g1_point_is_at_infinity(builder, &pubkey_g1); + + let sig_bytes = input.sig_bytes; + let sig_g2 = [ + [sig_g2_x_c0.to_owned(), sig_g2_x_c1.to_owned()], + [sig_g2_y_c0.to_owned(), sig_g2_y_c1.to_owned()], + ]; + signature_point_check(builder, &sig_g2, &sig_bytes); + let is_g2_point_is_at_infinity = is_g2_point_is_at_infinity(builder, &sig_g2); + + assert_g1_or_g2_point_arent_at_infinity( + builder, + is_g1_point_is_at_infinity, + is_g2_point_is_at_infinity, + ); + + Self::Target { + pubkey_bytes, + sig_bytes, + pubkey_g1_x, + pubkey_g1_y, + sig_g2_x_c0, + sig_g2_x_c1, + sig_g2_y_c0, + sig_g2_y_c1, + } + } +} + +fn assert_pk_ne_g1_generator(builder: &mut CircuitBuilder, public_key_point: &PointG1Target) { + let g1_generator_x = builder.constant_biguint(&BigUint::from(G1_GENERATOR_X)); + let g1_generator_y = builder.constant_biguint(&BigUint::from(G1_GENERATOR_Y)); + + let is_pk_point_x_eq_g1_generator_x = is_equal(builder, &public_key_point[0], &g1_generator_x); + let is_pk_point_y_eq_g1_generator_y = is_equal(builder, &public_key_point[1], &g1_generator_y); + let pk_point_eq_not_generator = builder.and( + is_pk_point_x_eq_g1_generator_x, + is_pk_point_y_eq_g1_generator_y, + ); + builder.assert_false(pk_point_eq_not_generator) +} + +fn assert_g1_or_g2_point_arent_at_infinity( + builder: &mut CircuitBuilder, + is_g1_at_infinity: BoolTarget, + is_g2_at_infinity: BoolTarget, +) { + let g1_or_g2_point_is_at_infinity = builder.or(is_g1_at_infinity, is_g2_at_infinity); + builder.assert_false(g1_or_g2_point_is_at_infinity); +} + +fn is_g1_point_is_at_infinity( + builder: &mut CircuitBuilder, + g1_point: &PointG1Target, +) -> BoolTarget { + let is_g1_x_zero = is_fp_zero(builder, &g1_point[0]); + let is_g1_y_zero = is_fp_zero(builder, &g1_point[1]); + builder.and(is_g1_x_zero, is_g1_y_zero) +} + +fn is_g2_point_is_at_infinity( + builder: &mut CircuitBuilder, + g2_point: &PointG2Target, +) -> BoolTarget { + let is_g2_x_zero = is_zero(builder, &g2_point[0]); + let is_g2_y_zero = is_zero(builder, &g2_point[1]); + builder.and(is_g2_x_zero, is_g2_y_zero) +} + +fn is_fp_zero(builder: &mut CircuitBuilder, input: &FpTarget) -> BoolTarget { + let zero = builder.zero_biguint(); + builder.cmp_biguint(input, &zero) +} + +#[cfg(test)] +pub mod tests { + use ark_bls12_381::{G1Affine, G2Affine}; + use ark_serialize::CanonicalDeserialize; + use circuit::{Circuit, CircuitInput, SetWitness}; + use num::BigUint; + use plonky2::{iop::witness::PartialWitness, plonk::circuit_data::CircuitData}; + use plonky2_crypto::biguint::WitnessBigUint; + + use super::{ + VerifyIsNotAtInfinityCircuit, VerifyIsNotAtInfinityCircuitTargets, + VerifyIsNotAtInfinityCircuitTargetsWitnessInput, C, D, F, + }; + + fn input_init( + pubkey: &str, + signature: &str, + ) -> VerifyIsNotAtInfinityCircuitTargetsWitnessInput { + let pubkey_bytes = hex::decode(pubkey).unwrap(); + let pubkey_bytes: [u8; 48] = pubkey_bytes + .into_iter() + .collect::>() + .try_into() + .unwrap(); + + let sig_bytes = hex::decode(signature).unwrap(); + let sig_bytes: [u8; 96] = sig_bytes + .into_iter() + .collect::>() + .try_into() + .unwrap(); + + let concatenated_str = format!( + r#"{{ + "pubkey_bytes": {:?}, + "sig_bytes": {:?} + }}"#, + pubkey_bytes, sig_bytes + ); + + serde_json::from_str::>(&concatenated_str) + .unwrap() + } + + fn test_helper( + pubkey: &str, + signature: &str, + targets: VerifyIsNotAtInfinityCircuitTargets, + circuit: CircuitData, + input: VerifyIsNotAtInfinityCircuitTargetsWitnessInput, + ) { + let pubkey_g1: G1Affine = + G1Affine::deserialize_compressed_unchecked(&*hex::decode(pubkey).unwrap()).unwrap(); + let signature_g2 = + G2Affine::deserialize_compressed_unchecked(&*hex::decode(signature).unwrap()).unwrap(); + + let mut pw = PartialWitness::new(); + targets.set_witness(&mut pw, &input); + pw.set_biguint_target( + &targets.pubkey_g1_x, + &BigUint::try_from(pubkey_g1.x).unwrap(), + ); + pw.set_biguint_target( + &targets.pubkey_g1_y, + &BigUint::try_from(pubkey_g1.y).unwrap(), + ); + pw.set_biguint_target( + &targets.sig_g2_x_c0, + &BigUint::try_from(signature_g2.x.c0).unwrap(), + ); + pw.set_biguint_target( + &targets.sig_g2_x_c1, + &BigUint::try_from(signature_g2.x.c1).unwrap(), + ); + pw.set_biguint_target( + &targets.sig_g2_y_c0, + &BigUint::try_from(signature_g2.y.c0).unwrap(), + ); + pw.set_biguint_target( + &targets.sig_g2_y_c1, + &BigUint::try_from(signature_g2.y.c1).unwrap(), + ); + + let proof = circuit.prove(pw).unwrap(); + let _ = circuit.verify(proof); + } + + #[test] + fn test_valid_case_for_g1_and_g2_at_infinity() { + let (targets, circuit) = VerifyIsNotAtInfinityCircuit::build(&()); + + let pubkey = "b301803f8b5ac4a1133581fc676dfedc60d891dd5fa99028805e5ea5b08d3491af75d0707adab3b70c6a6a580217bf81"; + let signature = "b23c46be3a001c63ca711f87a005c200cc550b9429d5f4eb38d74322144f1b63926da3388979e5321012fb1a0526bcd100b5ef5fe72628ce4cd5e904aeaa3279527843fae5ca9ca675f4f51ed8f83bbf7155da9ecc9663100a885d5dc6df96d9"; + let input = input_init(pubkey, signature); + + test_helper(pubkey, signature, targets, circuit, input); + } + + #[test] + #[should_panic] + fn test_g1_or_g2_are_at_infinity() { + let (targets, circuit) = VerifyIsNotAtInfinityCircuit::build(&()); + + let pubkey = "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + let signature = "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + let input = input_init(pubkey, signature); + + test_helper(pubkey, signature, targets, circuit, input); + } + + #[test] + #[should_panic] + fn test_g1_is_not_the_g1_generator() { + let (targets, circuit) = VerifyIsNotAtInfinityCircuit::build(&()); + + let pubkey = "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"; + let signature = "a42ae16f1c2a5fa69c04cb5998d2add790764ce8dd45bf25b29b4700829232052b52352dcff1cf255b3a7810ad7269601810f03b2bc8b68cf289cf295b206770605a190b6842583e47c3d1c0f73c54907bfb2a602157d46a4353a20283018763"; + let input = input_init(pubkey, signature); + + test_helper(pubkey, signature, targets, circuit, input); + } +} diff --git a/vendor/bls12-381-tests b/vendor/bls12-381-tests new file mode 160000 index 000000000..006855c56 --- /dev/null +++ b/vendor/bls12-381-tests @@ -0,0 +1 @@ +Subproject commit 006855c56cb6491ee19b4aedfddb806aaeacb1db