Skip to content

Commit 88d7ecb

Browse files
PatStilesjotabulaciosNicolasRampoldi
authored
feat(verifier): add Halo2 KZG Verifier (#268)
Co-authored-by: jotabulacios <jbulacios@fi.uba.ar> Co-authored-by: NicolasRampoldi <58613770+NicolasRampoldi@users.noreply.github.com>
1 parent 74297f0 commit 88d7ecb

27 files changed

Lines changed: 3294 additions & 9 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
cache: false
2020
- name: Build SP1 bindings
2121
run: make build_sp1_linux
22+
- name: Build Halo2-KZG bindings
23+
run: make build_halo2_kzg_linux
2224
- name: Build Halo2-IPA bindings
2325
run: make build_halo2_ipa_linux
2426
- name: Build operator
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: test-halo2-kzg
2+
3+
on:
4+
merge_group:
5+
push:
6+
branches: [main]
7+
pull_request:
8+
branches: ["*"]
9+
10+
jobs:
11+
test:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Clear device space
15+
run: |
16+
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
17+
sudo rm -rf /usr/local/lib/android
18+
sudo rm -rf /opt/ghc
19+
sudo rm -rf /usr/local/.ghcup
20+
sudo rm -rf /usr/share/dotnet
21+
sudo rm -rf /opt/ghc
22+
sudo rm -rf "/usr/local/share/boost"
23+
- uses: actions/checkout@v4
24+
- uses: actions/setup-go@v5
25+
with:
26+
go-version: '1.22'
27+
cache: false
28+
29+
- name: Test Halo2-KZG go bindings
30+
run: make test_halo2_kzg_go_bindings_linux

Makefile

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,25 @@ batcher_send_halo2_ipa_task_burst_5: batcher/client/target/release/batcher-clien
208208
--vk test_files/halo2_ipa/params.bin \
209209
--repetitions 5
210210

211+
batcher_send_halo2_kzg_task: batcher/client/target/release/batcher-client
212+
@echo "Sending Halo2 KZG 1!=0 task to Batcher..."
213+
@cd batcher/client/ && cargo run --release -- \
214+
--proving_system Halo2KZG \
215+
--proof test_files/halo2_kzg/proof.bin \
216+
--public_input test_files/halo2_kzg/pub_input.bin \
217+
--vk test_files/halo2_kzg/params.bin \
218+
--proof_generator_addr 0x66f9664f97F2b50F62D13eA064982f936dE76657
219+
220+
batcher_send_halo2_kzg_task_burst_5: batcher/client/target/release/batcher-client
221+
@echo "Sending Halo2 KZG 1!=0 task to Batcher..."
222+
@cd batcher/client/ && cargo run --release -- \
223+
--proving_system Halo2KZG \
224+
--proof test_files/halo2_kzg/proof.bin \
225+
--public_input test_files/halo2_kzg/pub_input.bin \
226+
--vk test_files/halo2_kzg/params.bin \
227+
--repetitions 5 \
228+
--proof_generator_addr 0x66f9664f97F2b50F62D13eA064982f936dE76657
229+
211230
__TASK_SENDERS__:
212231
# TODO add a default proving system
213232

@@ -331,6 +350,27 @@ send_halo2_ipa_proof_loop: ## Send a Halo2 IPA proof using the task sender every
331350
--interval 10 \
332351
2>&1 | zap-pretty
333352

353+
send_halo2_kzg_proof: ## Send a Halo2 KZG proof using the task sender
354+
@echo "Sending Halo2 KZG proof..."
355+
@go run task_sender/cmd/main.go send-task \
356+
--proving-system halo2_kzg \
357+
--proof task_sender/test_examples/halo2_kzg/proof.bin \
358+
--public-input task_sender/test_examples/halo2_kzg/pub_input.bin \
359+
--verification-key task_sender/test_examples/halo2_kzg/params.bin \
360+
--config config-files/config.yaml \
361+
2>&1 | zap-pretty
362+
363+
send_halo2_kzg_proof_loop: ## Send a Halo2 KZG proof using the task sender every 10 seconds
364+
@echo "Sending Halo2 KZG proof in a loop every 10 seconds..."
365+
@go run task_sender/cmd/main.go loop-tasks \
366+
--proving-system halo2_kzg \
367+
--proof task_sender/test_examples/halo2_kzg/proof.bin \
368+
--public-input task_sender/test_examples/halo2_kzg/pub_input.bin \
369+
--verification-key task_sender/test_examples/halo2_kzg/params.bin \
370+
--config config-files/config.yaml \
371+
--interval 10 \
372+
2>&1 | zap-pretty
373+
334374
__METRICS__:
335375
run_metrics: ## Run metrics using metrics-docker-compose.yaml
336376
@echo "Running metrics..."
@@ -443,6 +483,37 @@ test_merkle_tree_go_bindings_linux: build_merkle_tree_linux
443483
@echo "Testing Merkle Tree Go bindings..."
444484
go test ./operator/merkle_tree/... -v
445485

486+
__HALO2_KZG_FFI__: ##
487+
build_halo2_kzg_macos:
488+
@cd operator/halo2kzg/lib && cargo build --release
489+
@cp operator/halo2kzg/lib/target/release/libhalo2kzg_verifier_ffi.dylib operator/halo2kzg/lib/libhalo2kzg_verifier.dylib
490+
@cp operator/halo2kzg/lib/target/release/libhalo2kzg_verifier_ffi.a operator/halo2kzg/lib/libhalo2kzg_verifier.a
491+
492+
build_halo2_kzg_linux:
493+
@cd operator/halo2kzg/lib && cargo build --release
494+
@cp operator/halo2kzg/lib/target/release/libhalo2kzg_verifier_ffi.so operator/halo2kzg/lib/libhalo2kzg_verifier.so
495+
@cp operator/halo2kzg/lib/target/release/libhalo2kzg_verifier_ffi.a operator/halo2kzg/lib/libhalo2kzg_verifier.a
496+
497+
test_halo2_kzg_rust_ffi:
498+
@echo "Testing Halo2-KZG Rust FFI source code..."
499+
@cd operator/halo2kzg/lib && cargo t --release
500+
501+
test_halo2_kzg_go_bindings_macos: build_halo2_kzg_macos
502+
@echo "Testing Halo2-KZG Go bindings..."
503+
go test ./operator/halo2kzg/... -v
504+
505+
test_halo2_kzg_go_bindings_linux: build_halo2_kzg_linux
506+
@echo "Testing Halo2-KZG Go bindings..."
507+
go test ./operator/halo2kzg/... -v
508+
509+
generate_halo2_kzg_proof:
510+
@cd task_sender/test_examples/halo2_kzg && \
511+
cargo clean && \
512+
rm params.bin proof.bin pub_input.bin && \
513+
RUST_LOG=info cargo run --release && \
514+
echo "Generating halo2 plonk proof..." && \
515+
echo "Generated halo2 plonk proof!"
516+
446517
__HALO2_IPA_FFI__: ##
447518
build_halo2_ipa_macos:
448519
@cd operator/halo2ipa/lib && cargo build --release
3.54 KB
Binary file not shown.
992 Bytes
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
q�28aR��j�.H���Zx}�~�����

batcher/src/halo2/kzg/mod.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
use halo2_proofs::{
2+
plonk::{verify_proof, VerifyingKey},
3+
poly::{
4+
commitment::Params,
5+
kzg::{
6+
commitment::KZGCommitmentScheme, multiopen::VerifierSHPLONK, strategy::SingleStrategy,
7+
},
8+
},
9+
transcript::{Blake2bRead, Challenge255, TranscriptReadBuffer},
10+
SerdeFormat,
11+
};
12+
use halo2curves::bn256::{Bn256, Fr, G1Affine};
13+
use std::io::{BufReader, ErrorKind, Read};
14+
15+
//TODO(pat): refactor halo2 verification to a common create to eliminate deduplicated code.
16+
17+
// MaxProofSize 4KB
18+
pub const MAX_PROOF_SIZE: usize = 4 * 1024;
19+
20+
// MaxConstraintSystemSize 2KB
21+
pub const MAX_CONSTRAINT_SYSTEM_SIZE: usize = 2 * 1024;
22+
23+
// MaxVerificationKeySize 1KB
24+
pub const MAX_VERIFIER_KEY_SIZE: usize = 1024;
25+
26+
// MaxKzgParamsSize 4KB
27+
pub const MAX_KZG_PARAMS_SIZE: usize = 4 * 1024;
28+
29+
// MaxPublicInputSize 4KB
30+
pub const MAX_PUBLIC_INPUT_SIZE: usize = 4 * 1024;
31+
32+
pub fn verify_halo2_kzg(proof: &[u8], public_input: &[u8], verification_key: &[u8]) -> bool {
33+
let mut cs_buffer = [0u8; MAX_CONSTRAINT_SYSTEM_SIZE];
34+
let cs_len_buf: [u8; 4] = verification_key[..4]
35+
.try_into()
36+
.map_err(|_| "Failed to convert slice to [u8; 4]")
37+
.unwrap();
38+
let cs_len = u32::from_le_bytes(cs_len_buf) as usize;
39+
let cs_offset = 12;
40+
cs_buffer[..cs_len].clone_from_slice(&verification_key[cs_offset..(cs_offset + cs_len)]);
41+
42+
// Select Verifier Key Bytes
43+
let mut vk_buffer = [0u8; MAX_VERIFIER_KEY_SIZE];
44+
let vk_len_buf: [u8; 4] = verification_key[4..8]
45+
.try_into()
46+
.map_err(|_| "Failed to convert slice to [u8; 4]")
47+
.unwrap();
48+
let vk_len = u32::from_le_bytes(vk_len_buf) as usize;
49+
let vk_offset = cs_offset + cs_len;
50+
vk_buffer[..vk_len].clone_from_slice(&verification_key[vk_offset..(vk_offset + vk_len)]);
51+
52+
// Select KZG Params Bytes
53+
let mut kzg_params_buffer = [0u8; MAX_KZG_PARAMS_SIZE];
54+
let kzg_len_buf: [u8; 4] = verification_key[8..12]
55+
.try_into()
56+
.map_err(|_| "Failed to convert slice to [u8; 4]")
57+
.unwrap();
58+
let kzg_params_len = u32::from_le_bytes(kzg_len_buf) as usize;
59+
let kzg_offset = vk_offset + vk_len;
60+
kzg_params_buffer[..kzg_params_len].clone_from_slice(&verification_key[kzg_offset..]);
61+
62+
if let Ok(cs) = bincode::deserialize(&cs_buffer[..]) {
63+
if let Ok(vk) = VerifyingKey::<G1Affine>::read(
64+
&mut BufReader::new(&vk_buffer[..]),
65+
SerdeFormat::RawBytes,
66+
cs,
67+
) {
68+
if let Ok(params) = Params::read::<_>(&mut BufReader::new(&kzg_params_buffer[..])) {
69+
if let Ok(res) = read_fr(&public_input[..]) {
70+
let strategy = SingleStrategy::new(&params);
71+
let instances = res.as_slice();
72+
let mut transcript =
73+
Blake2bRead::<&[u8], G1Affine, Challenge255<_>>::init(&proof[..]);
74+
return verify_proof::<
75+
KZGCommitmentScheme<Bn256>,
76+
VerifierSHPLONK<'_, Bn256>,
77+
Challenge255<G1Affine>,
78+
Blake2bRead<&[u8], G1Affine, Challenge255<G1Affine>>,
79+
SingleStrategy<'_, Bn256>,
80+
>(
81+
&params, &vk, strategy, &[&[instances]], &mut transcript
82+
)
83+
.is_ok();
84+
}
85+
}
86+
}
87+
}
88+
false
89+
}
90+
91+
fn read_fr(mut buf: &[u8]) -> Result<Vec<Fr>, ErrorKind> {
92+
let mut instances = Vec::with_capacity(buf.len() / 32);
93+
// Buffer to store each 32-byte slice
94+
let mut buffer = [0; 32];
95+
96+
loop {
97+
// Read 32 bytes into the buffer
98+
match buf.read_exact(&mut buffer) {
99+
Ok(_) => {
100+
instances.push(Fr::from_bytes(&buffer).unwrap());
101+
}
102+
Err(ref e) if e.kind() == ErrorKind::UnexpectedEof => {
103+
// If end of file reached, break the loop
104+
break;
105+
}
106+
Err(e) => {
107+
eprintln!("Error Deserializing Public Inputs: {}", e);
108+
return Err(ErrorKind::Other);
109+
}
110+
}
111+
}
112+
113+
Ok(instances)
114+
}

batcher/src/halo2/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod ipa;
2+
pub mod kzg;

batcher/src/lib.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ impl Batcher {
212212
async fn process_batch_and_update_state(&self, block_number: u64) -> (Vec<u8>, [u8; 32]) {
213213
let mut current_batch = self.current_batch.lock().await;
214214

215-
216215
let mut batch_bytes =
217216
serde_json::to_vec(current_batch.as_slice()).expect("Failed to serialize batch");
218217

@@ -231,13 +230,17 @@ impl Batcher {
231230
}
232231
}
233232

234-
debug!("Batch size exceeds max batch size, splitting batch at index: {}", current_batch_end);
235-
batch_to_send = current_batch.drain(..current_batch_end)
236-
.collect::<Vec<_>>();
233+
debug!(
234+
"Batch size exceeds max batch size, splitting batch at index: {}",
235+
current_batch_end
236+
);
237+
batch_to_send = current_batch.drain(..current_batch_end).collect::<Vec<_>>();
237238

238-
info!("# of Elements remaining for next batch: {}", current_batch.len());
239-
batch_bytes = serde_json::to_vec(&batch_to_send)
240-
.expect("Failed to serialize batch");
239+
info!(
240+
"# of Elements remaining for next batch: {}",
241+
current_batch.len()
242+
);
243+
batch_bytes = serde_json::to_vec(&batch_to_send).expect("Failed to serialize batch");
241244
} else {
242245
batch_to_send = current_batch.clone();
243246
current_batch.clear();

batcher/src/types/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use sp1_sdk::ProverClient;
99

1010
use crate::gnark::verify_gnark;
1111
use crate::halo2::ipa::verify_halo2_ipa;
12+
use crate::halo2::kzg::verify_halo2_kzg;
1213

1314
lazy_static! {
1415
static ref SP1_PROVER_CLIENT: ProverClient = ProverClient::new();
@@ -21,6 +22,7 @@ pub enum ProvingSystemId {
2122
Groth16Bn254,
2223
#[default]
2324
SP1,
25+
Halo2KZG,
2426
Halo2IPA,
2527
}
2628

@@ -44,7 +46,17 @@ impl VerificationData {
4446
warn!("Trying to verify SP1 proof but ELF was not provided. Returning false");
4547
false
4648
}
49+
ProvingSystemId::Halo2KZG => {
50+
let vk = &self
51+
.verification_key
52+
.as_ref()
53+
.expect("Verification key is required");
4754

55+
let pub_input = &self.pub_input.as_ref().expect("Public input is required");
56+
let is_valid = verify_halo2_kzg(&self.proof, pub_input, vk);
57+
debug!("Halo2-KZG proof is valid: {}", is_valid);
58+
is_valid
59+
}
4860
ProvingSystemId::Halo2IPA => {
4961
let vk = &self
5062
.verification_key
@@ -179,7 +191,8 @@ pub fn parse_proving_system(proving_system: &str) -> anyhow::Result<ProvingSyste
179191
"Groth16Bn254" => Ok(ProvingSystemId::Groth16Bn254),
180192
"SP1" => Ok(ProvingSystemId::SP1),
181193
"Halo2IPA" => Ok(ProvingSystemId::Halo2IPA),
182-
_ => Err(anyhow!("Invalid proving system: {}, Available proving systems are: [GnarkPlonkBls12_381, GnarkPlonkBn254, Groth16Bn254, SP1, Halo2IPA]", proving_system))
194+
"Halo2KZG" => Ok(ProvingSystemId::Halo2KZG),
195+
_ => Err(anyhow!("Invalid proving system: {}, Available proving systems are: [GnarkPlonkBls12_381, GnarkPlonkBn254, Groth16Bn254, SP1, Halo2KZG, Halo2IPA]", proving_system))
183196
}
184197
}
185198

0 commit comments

Comments
 (0)