1use acir::{
2 AcirField,
3 circuit::opcodes::FunctionInput,
4 native_types::{Witness, WitnessMap},
5};
6use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError, sha256_compression};
7use itertools::Itertools;
8
9use crate::OpcodeResolutionError;
10use crate::pwg::{input_to_value, insert_value};
11
12pub(super) fn solve_generic_256_hash_opcode<F: AcirField>(
15 initial_witness: &mut WitnessMap<F>,
16 inputs: &[FunctionInput<F>],
17 var_message_size: Option<&FunctionInput<F>>,
18 outputs: &[Witness; 32],
19 hash_function: fn(data: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError>,
20) -> Result<(), OpcodeResolutionError<F>> {
21 let message_input = get_hash_input(initial_witness, inputs, var_message_size, 8)?;
22 let digest: [u8; 32] = hash_function(&message_input)?;
23
24 write_digest_to_outputs(initial_witness, outputs, digest)
25}
26
27pub(crate) fn get_hash_input<F: AcirField>(
29 initial_witness: &WitnessMap<F>,
30 inputs: &[FunctionInput<F>],
31 message_size: Option<&FunctionInput<F>>,
32 num_bits: usize,
33) -> Result<Vec<u8>, OpcodeResolutionError<F>> {
34 let mut message_input = Vec::new();
36 for input in inputs {
37 let witness_assignment = input_to_value(initial_witness, *input)?;
38 let bytes = witness_assignment.fetch_nearest_bytes(num_bits);
39 message_input.extend(bytes);
40 }
41
42 match message_size {
44 Some(input) => {
45 let num_bytes_to_take = input_to_value(initial_witness, *input)?
46 .try_into_u128()
47 .map(|num_bytes_to_take| num_bytes_to_take as usize)
48 .expect("expected a 'num_bytes_to_take' that fit into a u128");
49
50 if num_bytes_to_take > message_input.len() {
53 return Err(OpcodeResolutionError::BlackBoxFunctionFailed(
54 acir::BlackBoxFunc::Blake2s,
55 format!(
56 "the number of bytes to take from the message is more than the number of bytes in the message. {} > {}",
57 num_bytes_to_take,
58 message_input.len()
59 ),
60 ));
61 }
62 let truncated_message = message_input[0..num_bytes_to_take].to_vec();
63 Ok(truncated_message)
64 }
65 None => Ok(message_input),
66 }
67}
68
69fn write_digest_to_outputs<F: AcirField>(
71 initial_witness: &mut WitnessMap<F>,
72 outputs: &[Witness; 32],
73 digest: [u8; 32],
74) -> Result<(), OpcodeResolutionError<F>> {
75 for (output_witness, value) in outputs.iter().zip_eq(digest) {
76 insert_value(output_witness, F::from_be_bytes_reduce(&[value]), initial_witness)?;
77 }
78
79 Ok(())
80}
81
82fn to_u32_array<const N: usize, F: AcirField>(
83 initial_witness: &WitnessMap<F>,
84 inputs: &[FunctionInput<F>; N],
85) -> Result<[u32; N], OpcodeResolutionError<F>> {
86 let mut result = [0; N];
87 for (it, input) in result.iter_mut().zip_eq(inputs) {
88 let witness_value = input_to_value(initial_witness, *input)?;
89 *it = witness_value
90 .try_into_u128()
91 .expect("expected the 'witness_value' to fit into a u128")
92 .try_into()
93 .expect("expected the 'witness_value' to fit into a u32");
94 }
95 Ok(result)
96}
97
98pub(crate) fn solve_sha_256_permutation_opcode<F: AcirField>(
99 initial_witness: &mut WitnessMap<F>,
100 inputs: &[FunctionInput<F>; 16],
101 hash_values: &[FunctionInput<F>; 8],
102 outputs: &[Witness; 8],
103) -> Result<(), OpcodeResolutionError<F>> {
104 let state = execute_sha_256_permutation_opcode(initial_witness, inputs, hash_values)?;
105
106 for (output_witness, value) in outputs.iter().zip_eq(state) {
107 insert_value(output_witness, F::from(u128::from(value)), initial_witness)?;
108 }
109
110 Ok(())
111}
112
113pub(crate) fn execute_sha_256_permutation_opcode<F: AcirField>(
114 initial_witness: &WitnessMap<F>,
115 inputs: &[FunctionInput<F>; 16],
116 hash_values: &[FunctionInput<F>; 8],
117) -> Result<[u32; 8], OpcodeResolutionError<F>> {
118 let message = to_u32_array(initial_witness, inputs)?;
119 let mut state = to_u32_array(initial_witness, hash_values)?;
120
121 sha256_compression(&mut state, &message);
122
123 Ok(state)
124}
125
126pub(crate) fn solve_poseidon2_permutation_opcode<F: AcirField>(
127 backend: &impl BlackBoxFunctionSolver<F>,
128 initial_witness: &mut WitnessMap<F>,
129 inputs: &[FunctionInput<F>],
130 outputs: &[Witness],
131) -> Result<(), OpcodeResolutionError<F>> {
132 if inputs.len() != outputs.len() {
133 return Err(OpcodeResolutionError::BlackBoxFunctionFailed(
134 acir::BlackBoxFunc::Poseidon2Permutation,
135 format!(
136 "the input and output sizes are not consistent. {} != {}",
137 inputs.len(),
138 outputs.len()
139 ),
140 ));
141 }
142
143 let state = execute_poseidon2_permutation_opcode(backend, initial_witness, inputs)?;
144
145 for (output_witness, value) in outputs.iter().zip_eq(state) {
147 insert_value(output_witness, value, initial_witness)?;
148 }
149 Ok(())
150}
151
152pub(crate) fn execute_poseidon2_permutation_opcode<F: AcirField>(
153 backend: &impl BlackBoxFunctionSolver<F>,
154 initial_witness: &WitnessMap<F>,
155 inputs: &[FunctionInput<F>],
156) -> Result<Vec<F>, OpcodeResolutionError<F>> {
157 let state: Vec<F> = inputs
159 .iter()
160 .map(|input| input_to_value(initial_witness, *input))
161 .collect::<Result<_, _>>()?;
162
163 let state = backend.poseidon2_permutation(&state)?;
164 Ok(state)
165}
166
167#[cfg(test)]
168mod tests {
169 use crate::pwg::blackbox::solve_generic_256_hash_opcode;
170 use acir::{
171 FieldElement,
172 circuit::opcodes::FunctionInput,
173 native_types::{Witness, WitnessMap},
174 };
175 use acvm_blackbox_solver::{blake2s, blake3};
176 use std::collections::BTreeMap;
177
178 #[test]
179 fn test_blake2s() {
180 let mut inputs = Vec::new();
182 for i in 0..3 {
183 inputs.push(FunctionInput::Witness(Witness(1 + i)));
184 }
185 let mut outputs = [Witness(0); 32];
186 #[allow(clippy::needless_range_loop)]
187 for i in 0..32 {
188 outputs[i] = Witness(4 + i as u32);
189 }
190
191 let mut initial_witness = WitnessMap::from(BTreeMap::from_iter([
192 (Witness(1), FieldElement::from('a' as u128)),
193 (Witness(2), FieldElement::from('b' as u128)),
194 (Witness(3), FieldElement::from('c' as u128)),
195 ]));
196
197 solve_generic_256_hash_opcode(&mut initial_witness, &inputs, None, &outputs, blake2s)
198 .unwrap();
199
200 let expected_output: [u128; 32] = [
201 0x50, 0x8C, 0x5E, 0x8C, 0x32, 0x7C, 0x14, 0xE2, 0xE1, 0xA7, 0x2B, 0xA3, 0x4E, 0xEB,
202 0x45, 0x2F, 0x37, 0x45, 0x8B, 0x20, 0x9E, 0xD6, 0x3A, 0x29, 0x4D, 0x99, 0x9B, 0x4C,
203 0x86, 0x67, 0x59, 0x82,
204 ];
205 let expected_output = expected_output.map(FieldElement::from);
206 let expected_output: Vec<&FieldElement> = expected_output.iter().collect();
207 for i in 0..32 {
208 assert_eq!(initial_witness[&Witness(4 + i as u32)], *expected_output[i]);
209 }
210 }
211
212 #[test]
213 fn test_blake3s() {
214 let mut inputs = Vec::new();
216 for i in 0..3 {
217 inputs.push(FunctionInput::Witness(Witness(1 + i)));
218 }
219 let mut outputs = [Witness(0); 32];
220 #[allow(clippy::needless_range_loop)]
221 for i in 0..32 {
222 outputs[i] = Witness(4 + i as u32);
223 }
224
225 let mut initial_witness = WitnessMap::from(BTreeMap::from_iter([
226 (Witness(1), FieldElement::from('a' as u128)),
227 (Witness(2), FieldElement::from('b' as u128)),
228 (Witness(3), FieldElement::from('c' as u128)),
229 ]));
230
231 solve_generic_256_hash_opcode(&mut initial_witness, &inputs, None, &outputs, blake3)
232 .unwrap();
233
234 let expected_output: [u128; 32] = [
235 0x64, 0x37, 0xB3, 0xAC, 0x38, 0x46, 0x51, 0x33, 0xFF, 0xB6, 0x3B, 0x75, 0x27, 0x3A,
236 0x8D, 0xB5, 0x48, 0xC5, 0x58, 0x46, 0x5D, 0x79, 0xDB, 0x03, 0xFD, 0x35, 0x9C, 0x6C,
237 0xD5, 0xBD, 0x9D, 0x85,
238 ];
239 let expected_output = expected_output.map(FieldElement::from);
240 let expected_output: Vec<&FieldElement> = expected_output.iter().collect();
241 for i in 0..32 {
242 assert_eq!(initial_witness[&Witness(4 + i as u32)], *expected_output[i]);
243 }
244 }
245}