binius_frontend/artifact/
witness.rs1use std::{
7 fmt,
8 ops::{Index, IndexMut},
9};
10
11use binius_core::{ValueVec, Word};
12use binius_utils::strided_array::StridedArray2DViewMut;
13
14use crate::{Circuit, Wire};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct AssertionFailure {
19 pub path: String,
23 pub detail: String,
28}
29
30impl fmt::Display for AssertionFailure {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 if self.path.is_empty() {
33 f.write_str(&self.detail)
34 } else {
35 write!(f, "{}: {}", self.path, self.detail)
36 }
37 }
38}
39
40#[derive(Debug, thiserror::Error)]
49#[non_exhaustive]
50pub struct PopulateError {
51 pub failures: Vec<AssertionFailure>,
53 pub total: usize,
55}
56
57impl fmt::Display for PopulateError {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "circuit not satisfied: {} assertion(s) failed", self.total)?;
61 for failure in &self.failures {
62 write!(f, "\n {failure}")?;
63 }
64 let omitted = self.total.saturating_sub(self.failures.len());
65 if omitted > 0 {
66 write!(f, "\n ... and {omitted} more, omitted")?;
67 }
68 Ok(())
69 }
70}
71
72pub struct WitnessFiller<'a> {
74 pub(crate) circuit: &'a Circuit,
75 pub(crate) value_vec: ValueVec,
76}
77
78impl WitnessFiller<'_> {
79 pub fn into_value_vec(self) -> ValueVec {
81 self.value_vec
82 }
83
84 pub const fn value_vec(&self) -> &ValueVec {
86 &self.value_vec
87 }
88
89 pub fn pack_bytes_le(&mut self, wires: &[Wire], bytes: &[u8]) {
97 let max_value_size = wires.len() * 8;
98 assert!(
99 bytes.len() <= max_value_size,
100 "bytes length {} exceeds maximum {}",
101 bytes.len(),
102 max_value_size
103 );
104
105 for (&wire, chunk) in std::iter::zip(wires, bytes.chunks(8)) {
107 let mut chunk_arr = [0u8; 8];
108 chunk_arr[..chunk.len()].copy_from_slice(chunk);
109 self[wire] = Word(u64::from_le_bytes(chunk_arr));
110 }
111
112 for &wire in &wires[bytes.len().div_ceil(8)..] {
114 self[wire] = Word::ZERO;
115 }
116 }
117}
118
119impl Index<Wire> for WitnessFiller<'_> {
120 type Output = Word;
121
122 fn index(&self, wire: Wire) -> &Self::Output {
126 let index = self.circuit.witness_index(wire);
127 self.circuit.assert_not_pooled(wire, index);
128 &self.value_vec[index]
129 }
130}
131
132impl IndexMut<Wire> for WitnessFiller<'_> {
133 fn index_mut(&mut self, wire: Wire) -> &mut Self::Output {
137 let index = self.circuit.witness_index(wire);
138 self.circuit.assert_not_pooled(wire, index);
139 &mut self.value_vec[index]
140 }
141}
142
143pub struct BatchWitnessFiller<'a, 'v> {
150 circuit: &'a Circuit,
151 values: &'a mut StridedArray2DViewMut<'v, Word>,
152 instance: usize,
153}
154
155impl<'a, 'v> BatchWitnessFiller<'a, 'v> {
156 pub(crate) const fn new(
158 circuit: &'a Circuit,
159 values: &'a mut StridedArray2DViewMut<'v, Word>,
160 instance: usize,
161 ) -> Self {
162 Self {
163 circuit,
164 values,
165 instance,
166 }
167 }
168}
169
170impl Index<Wire> for BatchWitnessFiller<'_, '_> {
171 type Output = Word;
172
173 fn index(&self, wire: Wire) -> &Self::Output {
174 &self.values[(self.circuit.witness_row(wire), self.instance)]
175 }
176}
177
178impl IndexMut<Wire> for BatchWitnessFiller<'_, '_> {
179 fn index_mut(&mut self, wire: Wire) -> &mut Self::Output {
180 let row = self.circuit.witness_row(wire);
181 &mut self.values[(row, self.instance)]
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 fn failure(path: &str, detail: &str) -> AssertionFailure {
190 AssertionFailure {
191 path: path.to_string(),
192 detail: detail.to_string(),
193 }
194 }
195
196 #[test]
197 fn a_failure_at_the_root_renders_without_a_separator() {
198 assert_eq!(failure("", "Word(0x1) != Word(0x2)").to_string(), "Word(0x1) != Word(0x2)");
200 }
201
202 #[test]
203 fn a_nested_failure_renders_path_then_detail() {
204 assert_eq!(
206 failure(".sha256.round", "Word(0x1) != 0").to_string(),
207 ".sha256.round: Word(0x1) != 0"
208 );
209 }
210
211 #[test]
212 fn the_error_lists_every_retained_failure_and_never_ends_with_a_newline() {
213 let err = PopulateError {
215 failures: vec![failure(".a", "one"), failure(".b", "two")],
216 total: 2,
217 };
218 let rendered = err.to_string();
219 assert_eq!(rendered, "circuit not satisfied: 2 assertion(s) failed\n .a: one\n .b: two");
220 assert!(!rendered.ends_with('\n'));
221 }
222
223 #[test]
224 fn a_capped_error_reports_how_many_it_dropped() {
225 let err = PopulateError {
227 failures: vec![failure(".a", "one")],
228 total: 7,
229 };
230 assert_eq!(
231 err.to_string(),
232 "circuit not satisfied: 7 assertion(s) failed\n .a: one\n ... and 6 more, omitted"
233 );
234 }
235
236 #[test]
237 fn an_uncapped_error_reports_no_omissions() {
238 let err = PopulateError {
240 failures: vec![failure(".a", "one")],
241 total: 1,
242 };
243 assert_eq!(err.to_string(), "circuit not satisfied: 1 assertion(s) failed\n .a: one");
244 }
245
246 #[test]
247 fn the_error_is_a_std_error() {
248 let err = PopulateError {
250 failures: vec![failure(".a", "one")],
251 total: 1,
252 };
253 let boxed: Box<dyn std::error::Error> = Box::new(err);
254 assert!(boxed.to_string().starts_with("circuit not satisfied"));
255 assert!(boxed.source().is_none());
256 }
257}