diff --git a/.gitignore b/.gitignore index d32b05a922..2fc6232a04 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,17 @@ infra/ansible/playbooks/files/**.pem examples/l2/crates/l2/db examples/l2/crates/l2/zkvm_programs/sp1/elf/sp1_state_transition_program + +# Circom +*.ptau +challenge_0003 +response_0003 +challenge_phase2_0003 +circuit.r1cs +circuit.r1cs.json +circuit.sym +response_phase2_0003 +witness.wtns +*.zkey +circuit_cpp/ +circuit_js diff --git a/Makefile b/Makefile index fddac4016f..2b56b84197 100644 --- a/Makefile +++ b/Makefile @@ -653,6 +653,29 @@ batcher_send_groth16_bn254_infinite: crates/target/release/aligned ## Send a dif @mkdir -p scripts/test_files/gnark_groth16_bn254_infinite_script/infinite_proofs @./crates/cli/send_burst_tasks.sh $(BURST_SIZE) $(START_COUNTER) +batcher_send_circom_groth16_bn128_task: crates/target/release/aligned ## Send a Circom Groth16 BN128 proof to Batcher. Parameters: RPC_URL, NETWORK + @echo "Sending Circom Groth16 BN128 proof to Batcher..." + @cd crates/cli/ && cargo run --release -- submit \ + --proving_system CircomGroth16Bn128 \ + --proof ../../scripts/test_files/circom_groth16_bn128_script/proof.json \ + --public_input ../../scripts/test_files/circom_groth16_bn128_script/public.json \ + --vk ../../scripts/test_files/circom_groth16_bn128_script/verification_key.json \ + --proof_generator_addr 0x66f9664f97F2b50F62D13eA064982f936dE76657 \ + --rpc_url $(RPC_URL) \ + --network $(NETWORK) + +batcher_send_circom_groth16_bn128_burst: crates/target/release/aligned ## Send a burst of Circom Groth16 BN128 proofs to Batcher. Parameters: RPC_URL, NETWORK, BURST_SIZE + @echo "Sending Circom Groth16 BN128 proof to Batcher..." + @cd crates/cli/ && cargo run --release -- submit \ + --proving_system CircomGroth16Bn128 \ + --proof ../../scripts/test_files/circom_groth16_bn128_script/proof.json \ + --public_input ../../scripts/test_files/circom_groth16_bn128_script/public.json \ + --vk ../../scripts/test_files/circom_groth16_bn128_script/verification_key.json \ + --proof_generator_addr 0x66f9664f97F2b50F62D13eA064982f936dE76657 \ + --repetitions $(BURST_SIZE) \ + --rpc_url $(RPC_URL) \ + --network $(NETWORK) + batcher_send_proof_with_random_address: ## Send a proof with a random address to Batcher. Parameters: RPC_URL, NETWORK, PROOF_TYPE, REPETITIONS @cd crates/cli/ && ./send_proof_with_random_address.sh @@ -762,6 +785,13 @@ generate_gnark_groth16_bn254_ineq_proof: ## Run the gnark_plonk_bn254_script @echo "Running gnark_groth_bn254_ineq script..." @go run scripts/test_files/gnark_groth16_bn254_infinite_script/cmd/main.go 1 +generate_circom_groth16_bn128_proof: ## Run the circom_groth16_bn128_script + @echo "Running circom_groth16_bn128 script..." + @cd scripts/test_files/circom_groth16_bn128_script && ./generate_proof.sh + +generate_circom_groth16_bn128_setup: ## Run the circom_groth16_bn128_script setup + @echo "Running circom_groth16_bn128 script setup..." + @cd scripts/test_files/circom_groth16_bn128_script && ./generate_setup.sh __CONTRACTS_DEPLOYMENT__: ## ____ deploy_aligned_contracts: ## Deploy Aligned Contracts. Parameters: NETWORK= @@ -1097,6 +1127,19 @@ docker_batcher_send_groth16_burst: --rpc_url $(DOCKER_RPC_URL) \ --max_fee 0.1ether +docker_batcher_send_circom_groth16_bn128_burst: + @echo "Sending Circom Groth16 BN128 task to Batcher..." + docker exec $(shell docker ps | grep batcher | awk '{print $$1}') aligned submit \ + --private_key $(DOCKER_PROOFS_PRIVATE_KEY) \ + --proving_system CircomGroth16Bn128 \ + --proof ./scripts/test_files/circom_groth16_bn128_script/proof.json \ + --public_input ./scripts/test_files/circom_groth16_bn128_script/public.json \ + --vk ./scripts/test_files/circom_groth16_bn128_script/verification_key.json \ + --proof_generator_addr $(PROOF_GENERATOR_ADDRESS) \ + --repetitions $(DOCKER_BURST_SIZE) \ + --rpc_url $(DOCKER_RPC_URL) \ + --max_fee 0.1ether + # Update target as new proofs are supported. docker_batcher_send_all_proofs_burst: @$(MAKE) docker_batcher_send_sp1_burst @@ -1104,6 +1147,7 @@ docker_batcher_send_all_proofs_burst: @$(MAKE) docker_batcher_send_plonk_bn254_burst @$(MAKE) docker_batcher_send_plonk_bls12_381_burst @$(MAKE) docker_batcher_send_groth16_burst + @$(MAKE) docker_batcher_send_circom_groth16_bn128_burst docker_batcher_send_infinite_groth16: docker exec $(shell docker ps | grep batcher | awk '{print $$1}') \ @@ -1141,6 +1185,7 @@ docker_verify_proofs_onchain: ' DOCKER_PROOFS_WAIT_TIME=60 +DOCKER_SENT_PROOFS=6 docker_verify_proof_submission_success: @echo "Verifying proofs were successfully submitted..." @@ -1169,7 +1214,7 @@ docker_verify_proof_submission_success: fi; \ echo "---------------------------------------------------------------------------------------------------"; \ done; \ - if [ $$(ls -1 ./aligned_verification_data/*.cbor | wc -l) -ne 5 ]; then \ + if [ $$(ls -1 ./aligned_verification_data/*.cbor | wc -l) -ne $(DOCKER_SENT_PROOFS) ]; then \ echo "ERROR: Some proofs were verified successfully, but some proofs are missing in the aligned_verification_data/ directory"; \ exit 1; \ fi; \ diff --git a/common/proving_systems.go b/common/proving_systems.go index 023aa3eae6..dccc4faf0d 100644 --- a/common/proving_systems.go +++ b/common/proving_systems.go @@ -16,6 +16,7 @@ const ( Groth16Bn254 SP1 Risc0 + CircomGroth16Bn128 ) func (t *ProvingSystemId) String() string { @@ -34,6 +35,8 @@ func ProvingSystemIdFromString(provingSystem string) (ProvingSystemId, error) { return SP1, nil case "Risc0": return Risc0, nil + case "CircomGroth16Bn128": + return CircomGroth16Bn128, nil } return 0, fmt.Errorf("unknown proving system: %s", provingSystem) @@ -51,6 +54,8 @@ func ProvingSystemIdToString(provingSystem ProvingSystemId) (string, error) { return "SP1", nil case Risc0: return "Risc0", nil + case CircomGroth16Bn128: + return "CircomGroth16Bn128", nil } return "", fmt.Errorf("unknown proving system: %d", provingSystem) @@ -105,6 +110,8 @@ func (s *ProvingSystemId) UnmarshalCBOR(data []byte) error { *s = SP1 case "Risc0": *s = Risc0 + case "CircomGroth16Bn128": + *s = CircomGroth16Bn128 } return nil diff --git a/crates/batcher/build.rs b/crates/batcher/build.rs index 2af5b138b9..22a61b2eba 100644 --- a/crates/batcher/build.rs +++ b/crates/batcher/build.rs @@ -1,11 +1,20 @@ use std::{env, path::PathBuf, process::Command}; -const GO_SRC: &str = "./gnark/verifier.go"; +const GO_SRC: &str = "./go_verifiers_lib/verifier.go"; const GO_OUT: &str = "libverifier.a"; const GO_LIB: &str = "verifier"; fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + // Fix the missing dependency issue + let mut get_cmd = Command::new("go"); + get_cmd.arg("get") + .arg("github.com/yetanotherco/go-circom-prover-verifier/parsers@v0.0.0-20250618185957-f01a8a8ec4a6"); + + let _ = get_cmd.output(); // Run but don't fail if it has issues + + // Build library let mut go_build = Command::new("go"); go_build .arg("build") diff --git a/crates/batcher/gnark/go.mod b/crates/batcher/go_verifiers_lib/go.mod similarity index 86% rename from crates/batcher/gnark/go.mod rename to crates/batcher/go_verifiers_lib/go.mod index b8b75a0c9e..01718367bc 100644 --- a/crates/batcher/gnark/go.mod +++ b/crates/batcher/go_verifiers_lib/go.mod @@ -5,6 +5,7 @@ go 1.22.3 require ( github.com/consensys/gnark v0.12.0 github.com/consensys/gnark-crypto v0.17.0 + github.com/yetanotherco/go-circom-prover-verifier v0.0.0-20250618185957-f01a8a8ec4a6 ) require ( @@ -25,3 +26,5 @@ require ( golang.org/x/sys v0.30.0 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) + +require github.com/ethereum/go-ethereum v1.14.0 // indirect diff --git a/crates/batcher/gnark/go.sum b/crates/batcher/go_verifiers_lib/go.sum similarity index 95% rename from crates/batcher/gnark/go.sum rename to crates/batcher/go_verifiers_lib/go.sum index f492360c09..4a6c6811d3 100644 --- a/crates/batcher/gnark/go.sum +++ b/crates/batcher/go_verifiers_lib/go.sum @@ -11,6 +11,7 @@ github.com/consensys/gnark-crypto v0.17.0/go.mod h1:A2URlMHUT81ifJ0UlLzSlm7TmnE3 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ethereum/go-ethereum v1.14.0/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -44,6 +45,7 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yetanotherco/go-circom-prover-verifier v0.0.0-20250618185957-f01a8a8ec4a6/go.mod h1:A6TUcQ/lvmwAA/Ir8kRMIX5NcIglk8iNKeHF8Nj6Hu0= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= diff --git a/crates/batcher/gnark/verifier.go b/crates/batcher/go_verifiers_lib/verifier.go similarity index 80% rename from crates/batcher/gnark/verifier.go rename to crates/batcher/go_verifiers_lib/verifier.go index 7e4fe547ff..a27b11c279 100644 --- a/crates/batcher/gnark/verifier.go +++ b/crates/batcher/go_verifiers_lib/verifier.go @@ -8,13 +8,13 @@ typedef struct ListRef { const uint8_t *ptr; uintptr_t len; } ListRef; - - */ import "C" import ( "bytes" + "github.com/yetanotherco/go-circom-prover-verifier/parsers" + "github.com/yetanotherco/go-circom-prover-verifier/verifier" "log" "unsafe" @@ -119,3 +119,30 @@ func verifyGroth16Proof(proofBytesRef C.ListRef, pubInputBytesRef C.ListRef, ver err = groth16.Verify(proof, verificationKey, pubInput) return err == nil } + +//export VerifyCircomGroth16ProofBN128 +func VerifyCircomGroth16ProofBN128(proofBytesRef C.ListRef, pubInputBytesRef C.ListRef, verificationKeyBytesRef C.ListRef) bool { + proofBytes := listRefToBytes(proofBytesRef) + pubInputBytes := listRefToBytes(pubInputBytesRef) + verificationKeyBytes := listRefToBytes(verificationKeyBytesRef) + + proof, err := parsers.ParseProof(proofBytes) + if err != nil { + log.Printf("Could not parse proof: %v", err) + return false + } + + public, err := parsers.ParsePublicSignals(pubInputBytes) + if err != nil { + log.Printf("Could not parse public signals: %v", err) + return false + } + + vk, err := parsers.ParseVk(verificationKeyBytes) + if err != nil { + log.Printf("Could not parse verification key: %v", err) + return false + } + + return verifier.Verify(vk, proof, public) +} diff --git a/crates/batcher/src/circom/mod.rs b/crates/batcher/src/circom/mod.rs new file mode 100644 index 0000000000..9a0722027d --- /dev/null +++ b/crates/batcher/src/circom/mod.rs @@ -0,0 +1 @@ +pub mod verifier; diff --git a/crates/batcher/src/circom/verifier.rs b/crates/batcher/src/circom/verifier.rs new file mode 100644 index 0000000000..db126a395c --- /dev/null +++ b/crates/batcher/src/circom/verifier.rs @@ -0,0 +1,20 @@ +use crate::ffi::circom_ffi::VerifyCircomGroth16ProofBN128; +use aligned_sdk::common::types::ProvingSystemId; + +pub fn verify_circom( + proving_system: &ProvingSystemId, + proof: &Vec, + public_input: &Vec, + verification_key: &Vec, +) -> bool { + let proof = proof.into(); + let public_input = public_input.into(); + let verification_key = verification_key.into(); + + match proving_system { + ProvingSystemId::CircomGroth16Bn128 => unsafe { + VerifyCircomGroth16ProofBN128(proof, public_input, verification_key) + }, + _ => false, + } +} diff --git a/crates/batcher/src/ffi/circom_ffi.rs b/crates/batcher/src/ffi/circom_ffi.rs new file mode 100644 index 0000000000..4e806147ff --- /dev/null +++ b/crates/batcher/src/ffi/circom_ffi.rs @@ -0,0 +1,9 @@ +use crate::ffi::list_ref::ListRef; + +extern "C" { + pub fn VerifyCircomGroth16ProofBN128( + proof: ListRef, + public_input: ListRef, + verification_key: ListRef, + ) -> bool; +} diff --git a/crates/batcher/src/ffi/gnark_ffi.rs b/crates/batcher/src/ffi/gnark_ffi.rs new file mode 100644 index 0000000000..427891ec5b --- /dev/null +++ b/crates/batcher/src/ffi/gnark_ffi.rs @@ -0,0 +1,19 @@ +use crate::ffi::list_ref::ListRef; + +extern "C" { + pub fn VerifyPlonkProofBLS12_381( + proof: ListRef, + public_input: ListRef, + verification_key: ListRef, + ) -> bool; + pub fn VerifyPlonkProofBN254( + proof: ListRef, + public_input: ListRef, + verification_key: ListRef, + ) -> bool; + pub fn VerifyGroth16ProofBN254( + proof: ListRef, + public_input: ListRef, + verification_key: ListRef, + ) -> bool; +} diff --git a/crates/batcher/src/ffi/list_ref.rs b/crates/batcher/src/ffi/list_ref.rs new file mode 100644 index 0000000000..90de671167 --- /dev/null +++ b/crates/batcher/src/ffi/list_ref.rs @@ -0,0 +1,26 @@ +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct ListRef { + data: *const u8, + len: usize, +} + +impl From> for ListRef { + fn from(v: Vec) -> Self { + Self::from(v.as_slice()) + } +} + +impl From<&Vec> for ListRef { + fn from(v: &Vec) -> Self { + Self::from(v.as_slice()) + } +} + +impl From<&[u8]> for ListRef { + fn from(v: &[u8]) -> Self { + let len = v.len(); + let data = v.as_ptr().cast(); + ListRef { data, len } + } +} diff --git a/crates/batcher/src/ffi/mod.rs b/crates/batcher/src/ffi/mod.rs new file mode 100644 index 0000000000..1b423e92a5 --- /dev/null +++ b/crates/batcher/src/ffi/mod.rs @@ -0,0 +1,3 @@ +pub mod circom_ffi; +pub mod gnark_ffi; +mod list_ref; diff --git a/crates/batcher/src/gnark/mod.rs b/crates/batcher/src/gnark/mod.rs index 94a0995422..d7ec086758 100644 --- a/crates/batcher/src/gnark/mod.rs +++ b/crates/batcher/src/gnark/mod.rs @@ -1,32 +1,8 @@ +use crate::ffi::gnark_ffi::{ + VerifyGroth16ProofBN254, VerifyPlonkProofBLS12_381, VerifyPlonkProofBN254, +}; use aligned_sdk::common::types::ProvingSystemId; -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct ListRef { - data: *const u8, - len: usize, -} - -impl From> for ListRef { - fn from(v: Vec) -> Self { - Self::from(v.as_slice()) - } -} - -impl From<&Vec> for ListRef { - fn from(v: &Vec) -> Self { - Self::from(v.as_slice()) - } -} - -impl From<&[u8]> for ListRef { - fn from(v: &[u8]) -> Self { - let len = v.len(); - let data = v.as_ptr().cast(); - ListRef { data, len } - } -} - pub fn verify_gnark( proving_system: &ProvingSystemId, proof: &Vec, @@ -50,21 +26,3 @@ pub fn verify_gnark( _ => false, } } - -extern "C" { - pub fn VerifyPlonkProofBLS12_381( - proof: ListRef, - public_input: ListRef, - verification_key: ListRef, - ) -> bool; - pub fn VerifyPlonkProofBN254( - proof: ListRef, - public_input: ListRef, - verification_key: ListRef, - ) -> bool; - pub fn VerifyGroth16ProofBN254( - proof: ListRef, - public_input: ListRef, - verification_key: ListRef, - ) -> bool; -} diff --git a/crates/batcher/src/lib.rs b/crates/batcher/src/lib.rs index f30839ad7f..a3d0a670e3 100644 --- a/crates/batcher/src/lib.rs +++ b/crates/batcher/src/lib.rs @@ -54,9 +54,11 @@ use types::errors::{BatcherError, TransactionSendError}; use crate::config::{ConfigFromYaml, ContractDeploymentOutput}; use crate::telemetry::sender::TelemetrySender; +pub mod circom; mod config; mod connection; mod eth; +mod ffi; pub mod gnark; pub mod metrics; pub mod retry; diff --git a/crates/batcher/src/zk_utils/mod.rs b/crates/batcher/src/zk_utils/mod.rs index e330d3166d..9b7ee33328 100644 --- a/crates/batcher/src/zk_utils/mod.rs +++ b/crates/batcher/src/zk_utils/mod.rs @@ -1,3 +1,4 @@ +use crate::circom::verifier::verify_circom; use crate::gnark::verify_gnark; use crate::risc_zero::verify_risc_zero_proof; use crate::sp1::verify_sp1_proof; @@ -59,6 +60,25 @@ fn verify_internal(verification_data: &VerificationData) -> bool { debug!("Gnark proof is valid: {}", is_valid); is_valid } + ProvingSystemId::CircomGroth16Bn128 => { + let Some(pub_input) = verification_data.pub_input.as_ref() else { + warn!("Circom Groth16 public input missing"); + return false; + }; + let Some(vk) = verification_data.verification_key.as_ref() else { + warn!("Circom Groth16 verification key missing"); + return false; + }; + + let is_valid = verify_circom( + &verification_data.proving_system, + &verification_data.proof, + pub_input, + vk, + ); + debug!("Circom Groth16 proof is valid: {}", is_valid); + is_valid + } } } @@ -82,6 +102,7 @@ mod test { ProvingSystemId::Groth16Bn254, ProvingSystemId::SP1, ProvingSystemId::Risc0, + ProvingSystemId::CircomGroth16Bn128, ]; // Just to make sure we are not missing any verifier. The compilation will fail if we do and it forces us to add it to the vec above. for verifier in verifiers.iter() { @@ -91,6 +112,7 @@ mod test { ProvingSystemId::GnarkPlonkBls12_381 => (), ProvingSystemId::GnarkPlonkBn254 => (), ProvingSystemId::Groth16Bn254 => (), + ProvingSystemId::CircomGroth16Bn128 => (), } } verifiers @@ -142,7 +164,7 @@ mod test { fn test_some_verifiers_disabled() { let verifiers = get_all_verifiers(); // Disabling only the first verifier - let disabled_verifiers = ethers::types::U256::from(0b10001); + let disabled_verifiers = ethers::types::U256::from(0b100001); for verifier in get_all_verifiers().iter() { let verification_data = VerificationData { proving_system: *verifier, diff --git a/crates/cli/send_proof_with_random_address.sh b/crates/cli/send_proof_with_random_address.sh index 5733c35d58..fce022e39b 100755 --- a/crates/cli/send_proof_with_random_address.sh +++ b/crates/cli/send_proof_with_random_address.sh @@ -1,7 +1,7 @@ #!/bin/bash # Params: -# PROOF_TYPE = sp1|groth16|plonk|risc0 (default sp1) +# PROOF_TYPE = sp1|groth16|plonk|risc0|circom_groth16 (default sp1) # RPC_URL (default localhost:8545) # NETWORK devnet|holesky-stage|holesky # REPETITIONS (default 1) @@ -18,7 +18,7 @@ fi if [ -z $PROOF_TYPE ]; then echo "Proof type not provided, using SP1 default" - PROOF_TYPE="sp1" #sp1|groth16|plonk|risc0 + PROOF_TYPE="sp1" #sp1|groth16|plonk|risc0|circom fi if [ -z $REPETITIONS ]; then @@ -34,8 +34,8 @@ if [[ $PROOF_TYPE == "sp1" ]]; then --proving_system SP1 \ --proof ../../scripts/test_files/sp1/sp1_fibonacci_5_0_0.proof \ --vm_program ../../scripts/test_files/sp1/sp1_fibonacci_5_0_0.elf \ - --random_address \ - --repetitions $REPETITIONS \ + --random_address \ + --repetitions $REPETITIONS \ --rpc_url $RPC_URL \ --network $NETWORK @@ -45,8 +45,8 @@ elif [[ $PROOF_TYPE == "groth16" ]]; then --proof ../../scripts/test_files/gnark_groth16_bn254_script/groth16_0_12_0.proof \ --public_input ../../scripts/test_files/gnark_groth16_bn254_script/groth16_0_12_0.pub \ --vk ../../scripts/test_files/gnark_groth16_bn254_script/groth16_0_12_0.vk \ - --random_address \ - --repetitions $REPETITIONS \ + --random_address \ + --repetitions $REPETITIONS \ --rpc_url $RPC_URL \ --network $NETWORK @@ -57,7 +57,7 @@ elif [[ $PROOF_TYPE == "plonk" ]]; then --public_input ../../scripts/test_files/gnark_plonk_bn254_script/plonk_pub_input_0_12_0.pub \ --vk ../../scripts/test_files/gnark_plonk_bn254_script/plonk_0_12_0.vk \ --random_address \ - --repetitions $REPETITIONS \ + --repetitions $REPETITIONS \ --rpc_url $RPC_URL \ --network $NETWORK @@ -65,13 +65,24 @@ elif [[ $PROOF_TYPE == "risc0" ]]; then aligned submit \ --proving_system Risc0 \ --proof ../../scripts/test_files/risc_zero/fibonacci_proof_generator/risc_zero_fibonacci_2_1_0.proof \ - --vm_program ../../scripts/test_files/risc_zero/fibonacci_proof_generator/fibonacci_id_2_1_0.bin \ - --public_input ../../scripts/test_files/risc_zero/fibonacci_proof_generator/risc_zero_fibonacci_2_1_0.pub \ + --vm_program ../../scripts/test_files/risc_zero/fibonacci_proof_generator/fibonacci_id_2_1_0.bin \ + --public_input ../../scripts/test_files/risc_zero/fibonacci_proof_generator/risc_zero_fibonacci_2_1_0.pub \ --random_address \ - --repetitions $REPETITIONS \ + --repetitions $REPETITIONS \ --rpc_url $RPC_URL \ --network $NETWORK +elif [[ $PROOF_TYPE == "circom_groth16" ]]; then + aligned submit \ + --proving_system CircomGroth16Bn128 \ + --proof ../../scripts/test_files/circom_groth16_bn128_script/proof.json \ + --public_input ../../scripts/test_files/circom_groth16_bn128_script/public.json \ + --vk ../../scripts/test_files/circom_groth16_bn128_script/verification_key.json \ + --random_address \ + --repetitions $REPETITIONS \ + --rpc_url $RPC_URL \ + --network $NETWORK + else echo "Incorrect proof type provided $1" exit 1 diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index b27a2d9aa8..5c4b7b798c 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -356,6 +356,8 @@ pub enum ProvingSystemArg { SP1, #[clap(name = "Risc0")] Risc0, + #[clap(name = "CircomGroth16Bn128")] + CircomGroth16Bn128, } const ANVIL_PRIVATE_KEY: &str = "2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6"; // Anvil address 9 @@ -368,6 +370,7 @@ impl From for ProvingSystemId { ProvingSystemArg::Groth16Bn254 => ProvingSystemId::Groth16Bn254, ProvingSystemArg::SP1 => ProvingSystemId::SP1, ProvingSystemArg::Risc0 => ProvingSystemId::Risc0, + ProvingSystemArg::CircomGroth16Bn128 => ProvingSystemId::CircomGroth16Bn128, } } } @@ -911,6 +914,16 @@ fn verification_data_from_args(args: &SubmitArgs) -> Result { + verification_key = Some(read_file_option( + "--vk", + args.verification_key_file_name.clone(), + )?); + pub_input = Some(read_file_option( + "--public_input", + args.pub_input_file_name.clone(), + )?); + } } let proof_generator_addr = Address::from_str(&args.proof_generator_addr).map_err(|e| { diff --git a/crates/sdk/src/common/types.rs b/crates/sdk/src/common/types.rs index 23a799ec96..6e80426727 100644 --- a/crates/sdk/src/common/types.rs +++ b/crates/sdk/src/common/types.rs @@ -46,6 +46,7 @@ pub enum ProvingSystemId { #[default] SP1, Risc0, + CircomGroth16Bn128, } impl Display for ProvingSystemId { @@ -56,6 +57,7 @@ impl Display for ProvingSystemId { ProvingSystemId::Groth16Bn254 => write!(f, "Groth16Bn254"), ProvingSystemId::SP1 => write!(f, "SP1"), ProvingSystemId::Risc0 => write!(f, "Risc0"), + ProvingSystemId::CircomGroth16Bn128 => write!(f, "CircomGroth16Bn128"), } } } diff --git a/docker/batcher.Dockerfile b/docker/batcher.Dockerfile index 399b124cb2..7feff7c010 100644 --- a/docker/batcher.Dockerfile +++ b/docker/batcher.Dockerfile @@ -2,10 +2,10 @@ FROM ghcr.io/yetanotherco/aligned_layer/aligned_base:latest AS base COPY go.mod . COPY go.sum . -COPY crates/batcher/gnark/verifier.go /aligned_layer/crates/batcher/gnark/verifier.go +COPY crates/batcher/go_verifiers_lib/verifier.go /aligned_layer/crates/batcher/go_verifiers_lib/verifier.go RUN apt update -y && apt install -y gcc -RUN go build -buildmode=c-archive -o libverifier.a /aligned_layer/crates/batcher/gnark/verifier.go +RUN go build -buildmode=c-archive -o libverifier.a /aligned_layer/crates/batcher/go_verifiers_lib/verifier.go FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef diff --git a/docs/2_architecture/0_supported_verifiers.md b/docs/2_architecture/0_supported_verifiers.md index 9aed4089ea..ece0c250de 100644 --- a/docs/2_architecture/0_supported_verifiers.md +++ b/docs/2_architecture/0_supported_verifiers.md @@ -8,7 +8,7 @@ The following is the list of the verifiers currently supported by Aligned: - :white_check_mark: gnark - Plonk (with BN254 and BLS12-381) [(v0.12.0)](https://github.com/Consensys/gnark/releases/tag/v0.12.0) - :white_check_mark: SP1 [(v5.0.0)](https://github.com/succinctlabs/sp1/releases/tag/v5.0.0) - :white_check_mark: Risc0 [(v2.1.0)](https://github.com/risc0/risc0/releases/tag/v2.1.0) -- 🏗️ Circom +- :white_check_mark: Circom [(v2.2.2)](https://github.com/iden3/circom/releases/tag/v2.2.2) - 🏗️ Lambdaworks - 🏗️ Kimchi diff --git a/docs/3_guides/0_submitting_proofs.md b/docs/3_guides/0_submitting_proofs.md index d2be4d6840..38f5f57a86 100644 --- a/docs/3_guides/0_submitting_proofs.md +++ b/docs/3_guides/0_submitting_proofs.md @@ -14,6 +14,7 @@ The following is the list of the verifiers currently supported by Aligned: - :white_check_mark: gnark - Plonk (with BN254 and BLS12-381) [(v0.12.0)](https://github.com/Consensys/gnark/releases/tag/v0.12.0) - :white_check_mark: SP1 [(v5.0.0)](https://github.com/succinctlabs/sp1/releases/tag/v5.0.0) - :white_check_mark: Risc0 [(v2.1.0)](https://github.com/risc0/risc0/releases/tag/v2.1.0) +- :white_check_mark: Circom [(v2.2.2)](https://github.com/iden3/circom/releases/tag/v2.2.2) Learn more about future verifiers [here](../2_architecture/0_supported_verifiers.md). @@ -254,3 +255,34 @@ aligned submit \ --network holesky \ --rpc_url https://ethereum-holesky-rpc.publicnode.com ``` + +### CircomGroth16Bn128 + +The CircomGroth16Bn128 proof needs the proof file, the public input file and the verification key file. + +```bash +rm -rf ./aligned_verification_data/ && +aligned submit \ +--proving_system CircomGroth16Bn128 \ +--proof \ +--public_input \ +--vk \ +--proof_generator_addr [proof_generator_addr] \ +--batch_inclusion_data_directory_path [batch_inclusion_data_directory_path] \ +--keystore_path \ +--network holesky \ +--rpc_url https://ethereum-holesky-rpc.publicnode.com +``` +**Example** + +```bash +rm -rf ./aligned_verification_data/ && +aligned submit \ +--proving_system CircomGroth16Bn128 \ +--proof ./scripts/test_files/circom_groth16_bn128_script/proof.json \ +--public_input ./scripts/test_files/circom_groth16_bn128_script/public.json \ +--vk ./scripts/test_files/circom_groth16_bn128_script/verification_key.json \ +--keystore_path ~/.aligned_keystore/keystore0 \ +--network holesky \ +--rpc_url https://ethereum-holesky-rpc.publicnode.com +``` diff --git a/docs/3_guides/9_aligned_cli.md b/docs/3_guides/9_aligned_cli.md index 3b0bad216f..2d512f2ad9 100644 --- a/docs/3_guides/9_aligned_cli.md +++ b/docs/3_guides/9_aligned_cli.md @@ -58,7 +58,7 @@ Submit a proof to the Aligned Layer batcher. - Holesky: `https://ethereum-holesky-rpc.publicnode.com` - Also, you can use your own Ethereum RPC providers. - `--proving_system `: Proof system of the submitted proof - - Possible values: `GnarkPlonkBls12_381`, `GnarkPlonkBn254`, `Groth16Bn254`, `SP1`, `Risc0` + - Possible values: `GnarkPlonkBls12_381`, `GnarkPlonkBn254`, `Groth16Bn254`, `SP1`, `Risc0`, `CircomGroth16Bn128` - `--proof `: Path to the proof file. - `--public_input `: Path to the public input file. - `--vk `: Path to the verification key file (required for specific proof systems). diff --git a/go.mod b/go.mod index 5311723c0e..dfcce21aea 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/yetanotherco/aligned_layer -go 1.22.2 +go 1.22.3 + +toolchain go1.23.4 require ( github.com/Layr-Labs/eigensdk-go v0.2.0-beta.1 @@ -16,7 +18,9 @@ require ( github.com/consensys/gnark v0.12.0 github.com/consensys/gnark-crypto v0.17.0 github.com/fxamacker/cbor/v2 v2.7.0 + github.com/rs/zerolog v1.33.0 github.com/ugorji/go/codec v1.2.12 + github.com/yetanotherco/go-circom-prover-verifier v0.0.0-20250618185957-f01a8a8ec4a6 gopkg.in/yaml.v3 v3.0.1 ) @@ -71,7 +75,6 @@ require ( github.com/rivo/uniseg v0.4.4 // indirect github.com/ronanh/intcomp v1.1.0 // indirect github.com/rs/cors v1.8.3 // indirect - github.com/rs/zerolog v1.33.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.6+incompatible // indirect github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect diff --git a/go.sum b/go.sum index 34fef0ebf5..68dca0b570 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Layr-Labs/eigensdk-go v0.1.13 h1:llaDZW52AgrezJUpfqCzzgYuf47DK1HUOQLnI3jcVrA= -github.com/Layr-Labs/eigensdk-go v0.1.13/go.mod h1:aYdNURUhaqeYOS+Cq12TfSdPbjFfiLaHkxPdR4Exq/s= github.com/Layr-Labs/eigensdk-go v0.2.0-beta.1 h1:vW7AKcvt7fGlIeOMl2Ft9Au/ib8Z9ush8fSrpFSVr10= github.com/Layr-Labs/eigensdk-go v0.2.0-beta.1/go.mod h1:G4yqiK+5NfUuEMVGGncOEm7QskuGRPmKA7bKxpPzPT4= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -319,6 +317,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/yetanotherco/go-circom-prover-verifier v0.0.0-20250618185957-f01a8a8ec4a6 h1:Agf6nDeTEJelBU/v7QXB2iHx/+7e1L9Gua/IfVG6Bxg= +github.com/yetanotherco/go-circom-prover-verifier v0.0.0-20250618185957-f01a8a8ec4a6/go.mod h1:A6TUcQ/lvmwAA/Ir8kRMIX5NcIglk8iNKeHF8Nj6Hu0= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= diff --git a/operator/pkg/operator.go b/operator/pkg/operator.go index 7f8fc7ca68..397ade4381 100644 --- a/operator/pkg/operator.go +++ b/operator/pkg/operator.go @@ -6,6 +6,8 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/yetanotherco/go-circom-prover-verifier/parsers" + "github.com/yetanotherco/go-circom-prover-verifier/verifier" "log" "math/big" "net/http" @@ -514,6 +516,13 @@ func (o *Operator) verify(verificationData VerificationData, disabledVerifiersBi verificationData.VmProgramCode, verificationData.PubInput) o.Logger.Infof("Risc0 proof verification result: %t", verificationResult) o.handleVerificationResult(results, verificationResult, err, "Risc0 proof verification") + + case common.CircomGroth16Bn128: + verificationResult := o.verifyCircomGroth16Bn128Proof(verificationData.Proof, + verificationData.PubInput, verificationData.VerificationKey) + o.Logger.Infof("Circom Groth16 BN128 proof verification result: %t", verificationResult) + o.handleVerificationResult(results, verificationResult, nil, "Circom Groth16 BN128 proof verification") + default: o.Logger.Error("Unrecognized proving system ID") results <- false @@ -607,6 +616,27 @@ func (o *Operator) verifyGroth16Proof(proofBytes []byte, pubInputBytes []byte, v return err == nil } +// verifyCircomGroth16Bn128Proof verifies a Circom Groth16 proof using BN128 curve. +func (o *Operator) verifyCircomGroth16Bn128Proof(proofBytes []byte, pubInputBytes []byte, verificationKeyBytes []byte) bool { + proof, err := parsers.ParseProof(proofBytes) + if err != nil { + o.Logger.Infof("Could not parse proof: %v", err) + return false + } + public, err := parsers.ParsePublicSignals(pubInputBytes) + if err != nil { + o.Logger.Infof("Could not parse public signals: %v", err) + return false + } + vk, err := parsers.ParseVk(verificationKeyBytes) + if err != nil { + o.Logger.Infof("Could not parse verification key: %v", err) + return false + } + + return verifier.Verify(vk, proof, public) +} + func (o *Operator) SignTaskResponse(batchIdentifierHash [32]byte) *bls.Signature { responseSignature := *o.Config.BlsConfig.KeyPair.SignMessage(batchIdentifierHash) return &responseSignature diff --git a/scripts/test_files/README.md b/scripts/test_files/README.md index 72ec3ff1ac..4e42ac6290 100644 --- a/scripts/test_files/README.md +++ b/scripts/test_files/README.md @@ -32,7 +32,10 @@ make generate_gnark_plonk_bn254_proof make generate_gnark_plonk_bls12_381_proof ``` +## Generate Circom Groth16 BN128 Proof +```bash +make generate_circom_groth16_bn128_proof +``` - - +You can find more details about Circom in [./circom_groth16_bn128_script/README.md](./circom_groth16_bn128_script/README.md). diff --git a/scripts/test_files/circom_groth16_bn128_script/README.md b/scripts/test_files/circom_groth16_bn128_script/README.md new file mode 100644 index 0000000000..f39102fb2b --- /dev/null +++ b/scripts/test_files/circom_groth16_bn128_script/README.md @@ -0,0 +1,43 @@ +# Circom + +# Circom Groth16 BN128 Script + +The proof contained here is generated using the steps from [snarkjs repository](https://github.com/iden3/snarkjs) guide. + +The example uses the following dependencies versions: + +- Node version `v22.16.0` +- Circom version `2.2.2` +- Snarkjs version `0.7.5` + +You can find how to install all dependencies in the snarkjs repository. + +## Powers Of Tau Setup + +You can run the following command from the repository root to create the setup: + +```bash +make generate_circom_groth16_bn128_setup +``` + +## Generate the Circuit + +You can modify `circuit.circom` and `input.json` files to create your own circuit and input. + +## Generate the Proof + +You can run the following command from the repository root to generate the proof: + +```bash +make generate_circom_groth16_bn128_proof +``` + +This will generate the following files `proof.json`, `public.json`, and `verification_key.json` that can be sent to Aligned. + +## Send the Proof to Aligned + +You can run the following command from the repository root to send the proof to Aligned: + +```bash +make batcher_send_circom_groth16_bn128_task +``` \ No newline at end of file diff --git a/scripts/test_files/circom_groth16_bn128_script/circuit.circom b/scripts/test_files/circom_groth16_bn128_script/circuit.circom new file mode 100644 index 0000000000..c2f09b4e70 --- /dev/null +++ b/scripts/test_files/circom_groth16_bn128_script/circuit.circom @@ -0,0 +1,18 @@ +pragma circom 2.0.0; + +template Multiplier(n) { + signal input a; + signal input b; + signal output c; + + signal int[n]; + + int[0] <== a*a + b; + for (var i=1; i