Skip to main content

binius_examples/circuits/
blake3_compress.rs

1// Copyright 2025-2026 The Binius Developers
2
3use anyhow::Result;
4use binius_circuits::blake3::blake3_compress_2x;
5use binius_core::word::Word;
6use binius_frontend::{CircuitBuilder, Wire, WitnessFiller};
7use clap::Args;
8use rand::prelude::*;
9
10use super::utils::DEFAULT_RANDOM_SEED;
11use crate::ExampleCircuit;
12
13/// Default number of logical BLAKE3 compressions if none is specified.
14const DEFAULT_NUM_COMPRESSIONS: usize = 16;
15
16/// Benchmark for `blake3_compress_2x`: runs ceil(num_compressions / 2) parallel 2-lane
17/// compressions chained through their chaining-value output.
18pub struct Blake3CompressExample {
19	initial_cv: [Wire; 8],
20	pairs: Vec<PairInputs>,
21}
22
23struct PairInputs {
24	block: [Wire; 16],
25	counter_lo: Wire,
26	counter_hi: Wire,
27	block_len: Wire,
28	flags: Wire,
29}
30
31#[derive(Debug, Clone, Args)]
32pub struct Params {
33	/// Total number of logical BLAKE3 compressions. The circuit issues
34	/// `ceil(num_compressions / 2)` calls to `blake3_compress_2x`, running two
35	/// compressions per call in the upper/lower 32-bit lanes.
36	#[arg(long)]
37	pub num_compressions: Option<usize>,
38}
39
40#[derive(Debug, Clone, Args)]
41pub struct Instance {
42	/// RNG seed for witness values.
43	#[arg(long)]
44	pub seed: Option<u64>,
45}
46
47impl ExampleCircuit for Blake3CompressExample {
48	type Params = Params;
49	type Instance = Instance;
50
51	fn build(params: Params, builder: &mut CircuitBuilder) -> Result<Self> {
52		let num_compressions = params.num_compressions.unwrap_or(DEFAULT_NUM_COMPRESSIONS);
53		let n_pairs = num_compressions.div_ceil(2);
54
55		let initial_cv: [Wire; 8] = std::array::from_fn(|_| builder.add_witness());
56		let mut cv = initial_cv;
57		let mut pairs = Vec::with_capacity(n_pairs);
58
59		for _ in 0..n_pairs {
60			let block: [Wire; 16] = std::array::from_fn(|_| builder.add_witness());
61			let counter_lo = builder.add_witness();
62			let counter_hi = builder.add_witness();
63			let block_len = builder.add_witness();
64			let flags = builder.add_witness();
65
66			cv = blake3_compress_2x(builder, cv, block, counter_lo, counter_hi, block_len, flags);
67
68			pairs.push(PairInputs {
69				block,
70				counter_lo,
71				counter_hi,
72				block_len,
73				flags,
74			});
75		}
76
77		Ok(Self { initial_cv, pairs })
78	}
79
80	fn populate_witness(&self, instance: Instance, w: &mut WitnessFiller<'_>) -> Result<()> {
81		let mut rng = StdRng::seed_from_u64(instance.seed.unwrap_or(DEFAULT_RANDOM_SEED));
82		let mut next = || Word(rng.next_u64());
83
84		for wire in self.initial_cv {
85			w[wire] = next();
86		}
87		for pair in &self.pairs {
88			for b in pair.block {
89				w[b] = next();
90			}
91			w[pair.counter_lo] = next();
92			w[pair.counter_hi] = next();
93			w[pair.block_len] = next();
94			w[pair.flags] = next();
95		}
96
97		Ok(())
98	}
99
100	fn param_summary(params: &Self::Params) -> Option<String> {
101		Some(format!("{}c", params.num_compressions.unwrap_or(DEFAULT_NUM_COMPRESSIONS)))
102	}
103}