binius_examples/circuits/
keccak.rs1use std::array;
4
5use anyhow::Result;
6use binius_circuits::{
7 fixed_byte_vec::ByteVec,
8 keccak::{N_WORDS_PER_DIGEST, fixed_length::keccak256, keccak256_varlen},
9};
10use binius_core::word::Word;
11use binius_frontend::{CircuitBuilder, Wire, WitnessFiller};
12use sha3::Digest;
13
14use super::utils::{self, HasherInstance, HasherMode, HasherParams};
15use crate::ExampleCircuit;
16
17pub struct KeccakExample {
19 circuit: KeccakCircuit,
20 mode: HasherMode,
21}
22
23enum KeccakCircuit {
25 Fixed {
27 message: Vec<Wire>,
28 digest: [Wire; N_WORDS_PER_DIGEST],
29 },
30 Variable {
32 message: ByteVec,
33 digest: [Wire; N_WORDS_PER_DIGEST],
34 },
35}
36
37impl ExampleCircuit for KeccakExample {
38 type Params = HasherParams;
39 type Instance = HasherInstance;
40
41 fn build(params: HasherParams, builder: &mut CircuitBuilder) -> Result<Self> {
42 let mode = utils::resolve_hasher_mode(¶ms, "Keccak-256", true)?;
43
44 let circuit = match mode {
45 HasherMode::Fixed { len_bytes } => {
47 let n_words = len_bytes.div_ceil(8);
48 let message: Vec<Wire> = (0..n_words).map(|_| builder.add_inout()).collect();
49 let computed_digest = keccak256(builder, &message, len_bytes);
50 let digest: [Wire; N_WORDS_PER_DIGEST] = array::from_fn(|_| builder.add_inout());
51 for i in 0..N_WORDS_PER_DIGEST {
52 builder.assert_eq(format!("digest[{i}]"), computed_digest[i], digest[i]);
53 }
54 KeccakCircuit::Fixed { message, digest }
55 }
56 HasherMode::Variable { max_len_bytes } => {
58 let n_words = max_len_bytes.div_ceil(8);
59 let len_bytes = builder.add_witness();
60 let data = (0..n_words).map(|_| builder.add_inout()).collect();
61 let message = ByteVec::new(data, len_bytes);
62 let digest: [Wire; N_WORDS_PER_DIGEST] = array::from_fn(|_| builder.add_inout());
63 let computed_digest = keccak256_varlen(builder, &message);
64 for i in 0..N_WORDS_PER_DIGEST {
65 builder.assert_eq(format!("digest[{i}]"), computed_digest[i], digest[i]);
66 }
67 KeccakCircuit::Variable { message, digest }
68 }
69 };
70
71 Ok(Self { circuit, mode })
72 }
73
74 fn populate_witness(&self, instance: HasherInstance, w: &mut WitnessFiller<'_>) -> Result<()> {
75 let message = utils::resolve_hasher_message(&self.mode, &instance)?;
76 let mut hasher = sha3::Keccak256::new();
77 hasher.update(&message);
78 let digest: [u8; 32] = hasher.finalize().into();
79
80 match &self.circuit {
81 KeccakCircuit::Fixed {
82 message: message_wires,
83 digest: digest_wires,
84 } => {
85 for (wire, word) in message_wires
87 .iter()
88 .zip(utils::pack_bytes_u64words(&message, false))
89 {
90 w[*wire] = word;
91 }
92 for (i, chunk) in digest.chunks(8).enumerate() {
94 w[digest_wires[i]] = Word(u64::from_le_bytes(chunk.try_into().unwrap()));
95 }
96 }
97 KeccakCircuit::Variable {
98 message: byte_vec,
99 digest: digest_wires,
100 } => {
101 byte_vec.populate_data(w, &message);
102 byte_vec.populate_len_bytes(w, message.len());
103 for (i, chunk) in digest.chunks(8).enumerate() {
105 w[digest_wires[i]] = Word(u64::from_le_bytes(chunk.try_into().unwrap()));
106 }
107 }
108 }
109
110 Ok(())
111 }
112
113 fn param_summary(params: &Self::Params) -> Option<String> {
114 utils::hasher_param_summary(params)
115 }
116}