binius_examples/circuits/
blake3.rs1use std::array;
3
4use anyhow::{Result, ensure};
5use binius_circuits::blake3::{CHUNK_BYTES, blake3_fixed};
6use binius_core::word::Word;
7use binius_frontend::{CircuitBuilder, Wire, WitnessFiller};
8
9use super::utils::{self, HasherInstance, HasherMode, HasherParams};
10use crate::ExampleCircuit;
11
12pub struct Blake3Example {
14 message: Vec<Wire>,
15 digest: [Wire; 8],
16 mode: HasherMode,
17}
18
19impl ExampleCircuit for Blake3Example {
20 type Params = HasherParams;
21 type Instance = HasherInstance;
22
23 fn build(params: HasherParams, builder: &mut CircuitBuilder) -> Result<Self> {
24 let mode = utils::resolve_hasher_mode(¶ms, "BLAKE3", false)?;
27 let HasherMode::Fixed { len_bytes } = mode else {
28 unreachable!("BLAKE3 only supports the fixed-length gadget")
29 };
30
31 ensure!(
34 len_bytes <= CHUNK_BYTES,
35 "BLAKE3 example is limited to single-chunk messages (<= {CHUNK_BYTES} bytes), got {len_bytes}"
36 );
37
38 let n_words = len_bytes.div_ceil(4);
39 let message: Vec<Wire> = (0..n_words).map(|_| builder.add_inout()).collect();
40 let computed_digest = blake3_fixed(builder, &message, len_bytes);
41 let digest: [Wire; 8] = array::from_fn(|_| builder.add_inout());
42 for i in 0..8 {
43 builder.assert_eq(format!("digest[{i}]"), computed_digest[i], digest[i]);
44 }
45
46 Ok(Self {
47 message,
48 digest,
49 mode,
50 })
51 }
52
53 fn populate_witness(&self, instance: HasherInstance, w: &mut WitnessFiller<'_>) -> Result<()> {
54 let message_bytes = utils::resolve_hasher_message(&self.mode, &instance)?;
55
56 for (wire, word) in self
58 .message
59 .iter()
60 .zip(utils::pack_bytes_u32words(&message_bytes, false))
61 {
62 w[*wire] = word;
63 }
64
65 let expected = blake3::hash(&message_bytes);
67 for (i, chunk) in expected.as_bytes().chunks(4).enumerate() {
68 w[self.digest[i]] = Word(u32::from_le_bytes(chunk.try_into().unwrap()) as u64);
69 }
70
71 Ok(())
72 }
73
74 fn param_summary(params: &Self::Params) -> Option<String> {
75 utils::hasher_param_summary(params)
76 }
77}