1use alloc::string::String;
13use alloc::vec::Vec;
14use hekate_core::errors;
15use hekate_core::poly::PolyVariant;
16use hekate_core::trace::{ColumnTrace, ColumnType, Trace, TraceCompatibleField};
17use hekate_math::{Flat, HardwareField, PackableField, TowerField};
18
19use crate::constraint::{BoundaryConstraint, BoundaryTarget, ConstraintAst};
20use crate::expander::VirtualExpander;
21use crate::outer::TableStatics;
22use crate::permutation::{
23 PermutationCheckSpec, RankTable, TableHeight, validate_fixed_selectors, validate_ordered_buses,
24};
25use crate::{Air, FixedColumn, InlineKernelHint, validate_fixed_columns};
26
27#[derive(Clone)]
29pub struct ChipletDef<F: TowerField> {
30 name: String,
31 num_columns: usize,
32 constraint_ast: ConstraintAst<F>,
33 column_layout: Vec<ColumnType>,
34 virtual_column_layout: Vec<ColumnType>,
35 boundary_constraints: Vec<BoundaryConstraint<F>>,
36 fixed_columns: Vec<FixedColumn<F>>,
37 expander: Option<VirtualExpander>,
38 inline_chiplets: Vec<ChipletDef<F>>,
39 inline_kernels: Vec<InlineKernelHint>,
40
41 pub permutation_checks: Vec<(String, PermutationCheckSpec)>,
42}
43
44impl<F: TowerField> ChipletDef<F> {
45 pub fn from_air<P: Air<F> + Send + 'static>(p: &P) -> errors::Result<Self>
48 where
49 F: TraceCompatibleField + PackableField + HardwareField + 'static,
50 <F as PackableField>::Packed: Copy + Send + Sync,
51 {
52 let permutation_checks = p.permutation_checks();
53 for (bus_id, spec) in &permutation_checks {
54 spec.validate_clock_stitching(bus_id)?;
55 }
56
57 let constraint_ast = p.constraint_ast();
58 let boundary_constraints = p.boundary_constraints();
59 let fixed_columns = p.fixed_columns();
60 let inline_chiplets = p.inline_chiplets()?;
61 let inline_kernels = p.inline_chiplet_kernels();
62
63 validate_fixed_selectors(&permutation_checks, &fixed_columns)?;
64 validate_chiplet_boundaries(&boundary_constraints, p.num_columns())?;
65 validate_fixed_columns(&fixed_columns, p.virtual_column_layout(), None)?;
66 validate_ordered_buses(&[RankTable {
67 specs: &permutation_checks,
68 fixed: &fixed_columns,
69 height: TableHeight::Chiplet(None),
70 }])?;
71 validate_expander_coverage(p.virtual_expander(), p.column_layout())?;
72 validate_column_count(p.num_columns(), p.virtual_column_layout().len())?;
73 validate_inline_kernels(
74 &inline_kernels,
75 &inline_chiplets,
76 constraint_ast.roots.len(),
77 p.num_columns(),
78 )?;
79
80 Ok(Self {
81 name: p.name(),
82 num_columns: p.num_columns(),
83 constraint_ast,
84 column_layout: p.column_layout().to_vec(),
85 virtual_column_layout: p.virtual_column_layout().to_vec(),
86 boundary_constraints,
87 fixed_columns,
88 expander: p.virtual_expander().cloned(),
89 inline_chiplets,
90 inline_kernels,
91 permutation_checks,
92 })
93 }
94
95 pub fn ast(&self) -> &ConstraintAst<F> {
97 &self.constraint_ast
98 }
99
100 pub fn boundaries(&self) -> &[BoundaryConstraint<F>] {
101 &self.boundary_constraints
102 }
103
104 pub fn pins(&self) -> &[FixedColumn<F>] {
105 &self.fixed_columns
106 }
107
108 pub fn statics(&self) -> TableStatics<'_, F> {
109 TableStatics {
110 ast: &self.constraint_ast,
111 specs: &self.permutation_checks,
112 fixed: &self.fixed_columns,
113 boundary: &self.boundary_constraints,
114 }
115 }
116
117 pub fn inline_defs(&self) -> &[ChipletDef<F>] {
118 &self.inline_chiplets
119 }
120
121 pub fn inline_hints(&self) -> &[InlineKernelHint] {
122 &self.inline_kernels
123 }
124
125 pub fn prefix_bus_ids(&mut self, prefix: &str, exempt: &[String]) {
128 for (bus_id, _) in &mut self.permutation_checks {
129 if !exempt.contains(bus_id) {
130 let mut prefixed = String::from(prefix);
131 prefixed.push_str("::");
132 prefixed.push_str(bus_id);
133
134 *bus_id = prefixed;
135 }
136 }
137 }
138
139 pub fn expand_variants<'a>(
142 &self,
143 trace: &'a ColumnTrace,
144 ) -> errors::Result<Vec<PolyVariant<'a, F>>>
145 where
146 F: TraceCompatibleField + 'static,
147 {
148 match &self.expander {
149 Some(e) => e.expand_variants(trace, 0),
150 None => trace.get_poly_variants::<F>(),
151 }
152 }
153
154 #[allow(clippy::too_many_arguments)]
157 pub fn from_wire(
158 name: String,
159 num_columns: usize,
160 constraint_ast: ConstraintAst<F>,
161 column_layout: Vec<ColumnType>,
162 virtual_column_layout: Vec<ColumnType>,
163 boundary_constraints: Vec<BoundaryConstraint<F>>,
164 fixed_columns: Vec<FixedColumn<F>>,
165 expander: Option<VirtualExpander>,
166 permutation_checks: Vec<(String, PermutationCheckSpec)>,
167 inline_chiplets: Vec<ChipletDef<F>>,
168 inline_kernels: Vec<InlineKernelHint>,
169 ) -> errors::Result<Self> {
170 for (bus_id, spec) in &permutation_checks {
171 spec.validate_clock_stitching(bus_id)?;
172 }
173
174 validate_fixed_selectors(&permutation_checks, &fixed_columns)?;
175 validate_chiplet_boundaries(&boundary_constraints, num_columns)?;
176
177 let virt_layout = match &expander {
178 Some(e) => e.virtual_layout(),
179 None => virtual_column_layout.as_slice(),
180 };
181
182 validate_fixed_columns(&fixed_columns, virt_layout, None)?;
183 validate_ordered_buses(&[RankTable {
184 specs: &permutation_checks,
185 fixed: &fixed_columns,
186 height: TableHeight::Chiplet(None),
187 }])?;
188 validate_column_count(num_columns, virt_layout.len())?;
189 validate_inline_kernels(
190 &inline_kernels,
191 &inline_chiplets,
192 constraint_ast.roots.len(),
193 num_columns,
194 )?;
195
196 Ok(Self {
197 inline_chiplets,
198 inline_kernels,
199 ..Self::from_parts(
200 name,
201 num_columns,
202 constraint_ast,
203 column_layout,
204 virtual_column_layout,
205 boundary_constraints,
206 fixed_columns,
207 expander,
208 permutation_checks,
209 )
210 })
211 }
212
213 #[allow(clippy::too_many_arguments)]
216 pub(crate) fn from_parts(
217 name: String,
218 num_columns: usize,
219 constraint_ast: ConstraintAst<F>,
220 column_layout: Vec<ColumnType>,
221 virtual_column_layout: Vec<ColumnType>,
222 boundary_constraints: Vec<BoundaryConstraint<F>>,
223 fixed_columns: Vec<FixedColumn<F>>,
224 expander: Option<VirtualExpander>,
225 permutation_checks: Vec<(String, PermutationCheckSpec)>,
226 ) -> Self {
227 Self {
228 name,
229 num_columns,
230 constraint_ast,
231 column_layout,
232 virtual_column_layout,
233 boundary_constraints,
234 fixed_columns,
235 expander,
236 inline_chiplets: Vec::new(),
237 inline_kernels: Vec::new(),
238 permutation_checks,
239 }
240 }
241}
242
243impl<F: TowerField> Air<F> for ChipletDef<F> {
244 fn name(&self) -> String {
245 self.name.clone()
246 }
247
248 fn num_columns(&self) -> usize {
249 self.num_columns
250 }
251
252 fn boundary_constraints(&self) -> Vec<BoundaryConstraint<F>> {
253 self.boundary_constraints.clone()
254 }
255
256 fn column_layout(&self) -> &[ColumnType] {
257 &self.column_layout
258 }
259
260 fn virtual_column_layout(&self) -> &[ColumnType] {
261 match &self.expander {
262 Some(e) => e.virtual_layout(),
263 None => &self.virtual_column_layout,
264 }
265 }
266
267 fn permutation_checks(&self) -> Vec<(String, PermutationCheckSpec)> {
268 self.permutation_checks.clone()
269 }
270
271 fn fixed_columns(&self) -> Vec<FixedColumn<F>> {
272 self.fixed_columns.clone()
273 }
274
275 fn virtual_expander(&self) -> Option<&VirtualExpander> {
276 self.expander.as_ref()
277 }
278
279 fn parse_virtual_row(&self, bytes: &[u8], res: &mut Vec<Flat<F>>)
280 where
281 F: TraceCompatibleField,
282 {
283 if let Some(e) = &self.expander {
284 res.clear();
285
286 e.parse_row(bytes, res)
287 .expect("committed row byte length must match physical_row_bytes");
288 return;
289 }
290
291 res.clear();
292
293 let mut offset = 0;
294 for col_type in &self.column_layout {
295 let size = col_type.byte_size();
296 if offset + size <= bytes.len() {
297 res.push(col_type.parse_from_bytes(&bytes[offset..offset + size]));
298 offset += size;
299 }
300 }
301 }
302
303 fn constraint_ast(&self) -> ConstraintAst<F> {
304 self.constraint_ast.clone()
305 }
306
307 fn inline_chiplets(&self) -> errors::Result<Vec<ChipletDef<F>>> {
308 Ok(self.inline_chiplets.clone())
309 }
310
311 fn inline_chiplet_kernels(&self) -> Vec<InlineKernelHint> {
312 self.inline_kernels.clone()
313 }
314}
315
316fn validate_chiplet_boundaries<F>(
320 boundaries: &[BoundaryConstraint<F>],
321 num_columns: usize,
322) -> errors::Result<()> {
323 for bc in boundaries {
324 if bc.col_idx >= num_columns {
325 return Err(errors::Error::Protocol {
326 protocol: "boundary",
327 message: "chiplet boundary col_idx out of range",
328 });
329 }
330
331 if matches!(bc.target, BoundaryTarget::PublicInput(_)) {
332 return Err(errors::Error::Protocol {
333 protocol: "boundary",
334 message: "chiplet boundaries must use BoundaryTarget::Constant",
335 });
336 }
337 }
338
339 Ok(())
340}
341
342fn validate_expander_coverage(
345 expander: Option<&VirtualExpander>,
346 layout: &[ColumnType],
347) -> errors::Result<()> {
348 let covered = match expander {
349 Some(e) => e.num_physical_columns(),
350 None => layout.len(),
351 };
352
353 if covered != layout.len() {
354 return Err(errors::Error::Protocol {
355 protocol: "chiplet",
356 message: "virtual_expander does not tile column_layout",
357 });
358 }
359
360 Ok(())
361}
362
363fn validate_column_count(num_columns: usize, virtual_columns: usize) -> errors::Result<()> {
364 if num_columns != virtual_columns {
365 return Err(errors::Error::Protocol {
366 protocol: "chiplet",
367 message: "num_columns does not match the virtual column layout",
368 });
369 }
370
371 Ok(())
372}
373
374fn validate_inline_kernels<F: TowerField>(
375 hints: &[InlineKernelHint],
376 chiplets: &[ChipletDef<F>],
377 num_roots: usize,
378 num_columns: usize,
379) -> errors::Result<()> {
380 for hint in hints {
381 let inside = chiplets.get(hint.chiplet_idx).is_some_and(|cd| {
382 let roots_end = hint.root_offset.checked_add(cd.constraint_ast.roots.len());
383 let columns_end = hint.column_offset.checked_add(cd.num_columns);
384
385 roots_end.is_some_and(|end| end <= num_roots)
386 && columns_end.is_some_and(|end| end <= num_columns)
387 });
388
389 if !inside {
390 return Err(errors::Error::Protocol {
391 protocol: "chiplet",
392 message: "inline kernel hint outside the snapshot",
393 });
394 }
395 }
396
397 Ok(())
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403 use crate::constraint::builder::ConstraintSystem;
404 use crate::define_columns;
405 use crate::permutation::{
406 BusKind, ChallengeLabel, EMIT_RANK_LABEL, PermutationCheckSpec, Side, Source,
407 };
408 use crate::{ConstraintAst, FixedShape};
409 use alloc::string::String;
410 use alloc::vec;
411 use hekate_core::trace::ColumnType;
412 use hekate_math::Block128;
413
414 type F = Block128;
415
416 define_columns! {
417 PairedAirCols {
418 KEY: B32,
419 S_SEND: Bit,
420 S_RECV: Bit,
421 }
422 }
423
424 #[derive(Clone)]
425 struct ExpanderAir {
426 expander: VirtualExpander,
427 }
428
429 impl Air<F> for ExpanderAir {
430 fn num_columns(&self) -> usize {
431 self.expander.virtual_layout().len()
432 }
433
434 fn column_layout(&self) -> &[ColumnType] {
435 &[ColumnType::B32, ColumnType::Bit]
436 }
437
438 fn virtual_expander(&self) -> Option<&VirtualExpander> {
439 Some(&self.expander)
440 }
441
442 fn constraint_ast(&self) -> ConstraintAst<F> {
443 ConstraintSystem::<F>::new().build()
444 }
445 }
446
447 #[derive(Clone)]
448 struct OneBusAir {
449 spec: PermutationCheckSpec,
450 }
451
452 impl Air<F> for OneBusAir {
453 fn num_columns(&self) -> usize {
454 2
455 }
456
457 fn column_layout(&self) -> &[ColumnType] {
458 &[ColumnType::B32, ColumnType::Bit]
459 }
460
461 fn permutation_checks(&self) -> Vec<(String, PermutationCheckSpec)> {
462 vec![("test_bus".into(), self.spec.clone())]
463 }
464
465 fn fixed_columns(&self) -> Vec<FixedColumn<F>> {
466 liveness_pin(1)
467 }
468
469 fn constraint_ast(&self) -> ConstraintAst<F> {
470 ConstraintSystem::<F>::new().build()
471 }
472 }
473
474 #[derive(Clone)]
475 struct PairedAir {
476 pinned: bool,
477 }
478
479 impl Air<F> for PairedAir {
480 fn num_columns(&self) -> usize {
481 PairedAirCols::NUM_COLUMNS
482 }
483
484 fn column_layout(&self) -> &[ColumnType] {
485 static LAYOUT: std::sync::OnceLock<Vec<ColumnType>> = std::sync::OnceLock::new();
486 LAYOUT.get_or_init(PairedAirCols::build_layout)
487 }
488
489 fn permutation_checks(&self) -> Vec<(String, PermutationCheckSpec)> {
490 let sources = vec![
491 (Source::Column(PairedAirCols::KEY), b"k_a" as ChallengeLabel),
492 (Source::RowIndexLeBytes(4), b"k_clk" as ChallengeLabel),
493 ];
494
495 vec![(
496 "paired_test_bus".into(),
497 PermutationCheckSpec::new_paired(
498 sources,
499 PairedAirCols::S_SEND,
500 PairedAirCols::S_RECV,
501 BusKind::Permutation,
502 ),
503 )]
504 }
505
506 fn fixed_columns(&self) -> Vec<FixedColumn<F>> {
507 match self.pinned {
508 true => vec![
509 FixedColumn::sparse(PairedAirCols::S_SEND, vec![(0, F::ONE)]),
510 FixedColumn::sparse(PairedAirCols::S_RECV, vec![(1, F::ONE)]),
511 ],
512 false => Vec::new(),
513 }
514 }
515
516 fn constraint_ast(&self) -> ConstraintAst<F> {
517 ConstraintSystem::<F>::new().build()
518 }
519 }
520
521 #[derive(Clone)]
522 struct HintedAir {
523 hints: Vec<InlineKernelHint>,
524 }
525
526 impl Air<F> for HintedAir {
527 fn num_columns(&self) -> usize {
528 2
529 }
530
531 fn column_layout(&self) -> &[ColumnType] {
532 &[ColumnType::B32, ColumnType::Bit]
533 }
534
535 fn constraint_ast(&self) -> ConstraintAst<F> {
536 let cs = ConstraintSystem::<F>::new();
537 cs.assert_boolean(cs.col(1));
538
539 cs.build()
540 }
541
542 fn inline_chiplets(&self) -> errors::Result<Vec<ChipletDef<F>>> {
543 match self.hints.is_empty() {
544 true => Ok(Vec::new()),
545 false => Ok(vec![ChipletDef::from_air(&HintedAir {
546 hints: Vec::new(),
547 })?]),
548 }
549 }
550
551 fn inline_chiplet_kernels(&self) -> Vec<InlineKernelHint> {
552 self.hints.clone()
553 }
554 }
555
556 fn key_only() -> Vec<(Source, ChallengeLabel)> {
557 vec![(Source::Column(0), b"k_a")]
558 }
559
560 fn key_with_clock() -> Vec<(Source, ChallengeLabel)> {
561 vec![
562 (Source::Column(0), b"k_a"),
563 (Source::RowIndexLeBytes(4), b"k_clk"),
564 ]
565 }
566
567 fn key_with_rank() -> Vec<(Source, ChallengeLabel)> {
568 vec![
569 (Source::Column(0), b"k_a"),
570 (Source::EmitRank(Side::Response), EMIT_RANK_LABEL),
571 ]
572 }
573
574 fn snapshot(spec: PermutationCheckSpec) -> errors::Result<ChipletDef<F>> {
575 ChipletDef::from_air(&OneBusAir { spec })
576 }
577
578 fn assert_logup_bus_err<T>(res: errors::Result<T>) {
579 match res {
580 Err(errors::Error::Protocol { protocol, .. }) => {
581 assert_eq!(protocol, "logup_bus");
582 }
583 Ok(_) => panic!("expected Err(Protocol {{ protocol: \"logup_bus\", .. }})"),
584 Err(other) => panic!("expected Err(Protocol), got {:?}", other),
585 }
586 }
587
588 fn liveness_pin(col: usize) -> Vec<FixedColumn<F>> {
589 vec![FixedColumn::sparse(col, vec![(0, F::ONE)])]
590 }
591
592 #[test]
593 fn def_rejects_permutation_without_clock() {
594 let spec = PermutationCheckSpec::new(key_only(), Some(1));
595 assert_logup_bus_err(snapshot(spec));
596 }
597
598 #[test]
599 fn def_rejects_permutation_with_empty_waiver() {
600 let spec = PermutationCheckSpec::new(key_only(), Some(1)).with_clock_waiver("");
601 assert_logup_bus_err(snapshot(spec));
602 }
603
604 #[test]
605 fn def_rejects_permutation_with_clock_and_waiver() {
606 let spec =
607 PermutationCheckSpec::new(key_with_clock(), Some(1)).with_clock_waiver("redundant");
608 assert_logup_bus_err(snapshot(spec));
609 }
610
611 #[test]
612 fn def_rejects_lookup_with_waiver() {
613 let spec = PermutationCheckSpec::new_lookup(key_only(), Some(1)).with_clock_waiver("nope");
614 assert_logup_bus_err(snapshot(spec));
615 }
616
617 #[test]
618 fn def_accepts_permutation_with_row_index() {
619 let spec = PermutationCheckSpec::new(key_with_clock(), Some(1));
620 snapshot(spec).expect("permutation bus with row-index source must accept");
621 }
622
623 #[test]
624 fn def_accepts_permutation_with_clock_waiver() {
625 let spec = PermutationCheckSpec::new(key_only(), Some(1))
626 .with_clock_waiver("see foo.rs:42: structurally unique by AIR body");
627 snapshot(spec).expect("permutation bus with non-empty clock_waiver must accept");
628 }
629
630 #[test]
631 fn def_accepts_lookup_without_clock() {
632 let spec = PermutationCheckSpec::new_lookup(key_only(), Some(1));
633 snapshot(spec).expect("lookup bus without clock must accept");
634 }
635
636 #[test]
637 fn def_rejects_permutation_with_too_short_waiver() {
638 let spec = PermutationCheckSpec::new(key_only(), Some(1)).with_clock_waiver("see x.rs");
639 assert_logup_bus_err(snapshot(spec));
640 }
641
642 #[test]
643 fn def_rejects_permutation_with_missing_see_citation() {
644 let spec = PermutationCheckSpec::new(key_only(), Some(1))
645 .with_clock_waiver("structurally unique by AIR body but no file citation prefix here");
646 assert_logup_bus_err(snapshot(spec));
647 }
648
649 #[test]
650 fn chiplet_def_rejects_paired_spec_with_witness_selectors() {
651 assert_logup_bus_err(ChipletDef::<F>::from_air(&PairedAir { pinned: false }));
652 }
653
654 #[test]
655 fn chiplet_def_accepts_paired_spec_with_fixed_selectors() {
656 ChipletDef::<F>::from_air(&PairedAir { pinned: true })
657 .expect("paired AIR with fixed selectors must snapshot");
658 }
659
660 #[test]
661 fn chiplet_def_requires_expander_to_tile_the_layout() {
662 let tiled = ExpanderAir {
663 expander: VirtualExpander::new()
664 .expand_bits(1, ColumnType::B32)
665 .control_bits(1)
666 .build()
667 .unwrap(),
668 };
669
670 ChipletDef::<F>::from_air(&tiled).expect("tiling expander must snapshot");
671
672 let short = ExpanderAir {
673 expander: VirtualExpander::new()
674 .expand_bits(1, ColumnType::B32)
675 .build()
676 .unwrap(),
677 };
678
679 assert!(ChipletDef::<F>::from_air(&short).is_err());
680 }
681
682 #[test]
683 fn chiplet_def_rejects_hint_outside_the_snapshot() {
684 let hint = |chiplet_idx, root_offset, column_offset| HintedAir {
685 hints: vec![InlineKernelHint {
686 chiplet_idx,
687 root_offset,
688 column_offset,
689 }],
690 };
691
692 let def = ChipletDef::<F>::from_air(&hint(0, 0, 0)).unwrap();
693
694 assert_eq!(def.inline_chiplet_kernels(), hint(0, 0, 0).hints);
695
696 for stray in [
697 hint(1, 0, 0),
698 hint(0, 1, 0),
699 hint(0, 0, 1),
700 hint(0, usize::MAX, 0),
701 ] {
702 assert!(ChipletDef::<F>::from_air(&stray).is_err());
703 }
704 }
705
706 #[test]
707 fn validator_rejects_recv_selector_without_send_selector() {
708 let spec = PermutationCheckSpec {
709 sources: vec![
710 (Source::Column(PairedAirCols::KEY), b"k_a" as ChallengeLabel),
711 (Source::RowIndexLeBytes(4), b"k_clk" as ChallengeLabel),
712 ],
713 selector: None,
714 recv_selector: Some(PairedAirCols::S_RECV),
715 kind: BusKind::Permutation,
716 clock_waiver: None,
717 };
718
719 assert_logup_bus_err(validate_fixed_selectors::<F>(
720 &[("asym_bus".into(), spec)],
721 &liveness_pin(PairedAirCols::S_RECV),
722 ));
723 }
724
725 #[test]
726 fn def_rejects_witness_selector() {
727 let spec = PermutationCheckSpec::new(key_with_clock(), Some(0));
728 assert_logup_bus_err(snapshot(spec));
729 }
730
731 #[test]
732 fn def_accepts_absent_selector() {
733 let spec = PermutationCheckSpec::new(key_with_clock(), None);
734 snapshot(spec).expect("selector-free bus must accept");
735 }
736
737 #[test]
738 fn validator_rejects_selector_pinned_to_substituted_shape() {
739 let spec = PermutationCheckSpec::new(key_with_clock(), Some(1));
740
741 for shape in [
742 FixedShape::FirstRow,
743 FixedShape::LastRow,
744 FixedShape::Custom(vec![true, false]),
745 ] {
746 assert_logup_bus_err(validate_fixed_selectors(
747 &[("svc".into(), spec.clone())],
748 &[FixedColumn::<F> { col_idx: 1, shape }],
749 ));
750 }
751 }
752
753 #[test]
754 fn validator_accepts_selector_pinned_to_overlay_shape() {
755 let spec = PermutationCheckSpec::new(key_with_clock(), Some(1));
756
757 validate_fixed_selectors(&[("svc".into(), spec)], &liveness_pin(1))
758 .expect("overlay-pinned selector must accept");
759 }
760
761 #[test]
762 fn from_wire_rejects_hint_outside_snapshot() {
763 let bare = HintedAir { hints: Vec::new() };
764
765 let wire = |chiplet_idx, root_offset| {
766 ChipletDef::<F>::from_wire(
767 String::from("wired"),
768 2,
769 bare.constraint_ast(),
770 bare.column_layout().to_vec(),
771 bare.column_layout().to_vec(),
772 Vec::new(),
773 Vec::new(),
774 None,
775 Vec::new(),
776 vec![ChipletDef::from_air(&bare).unwrap()],
777 vec![InlineKernelHint {
778 chiplet_idx,
779 root_offset,
780 column_offset: 0,
781 }],
782 )
783 };
784
785 assert!(wire(0, 0).is_ok());
786 assert!(wire(1, 0).is_err());
787 assert!(wire(0, 1).is_err());
788 }
789
790 #[test]
791 fn def_runs_ordered_bus_predicate() {
792 snapshot(PermutationCheckSpec::new(key_with_rank(), Some(1))).unwrap();
793
794 let lookup = PermutationCheckSpec::new_lookup(key_with_rank(), Some(1));
795
796 assert_logup_bus_err(snapshot(lookup.clone()));
797
798 let air = OneBusAir { spec: lookup };
799
800 assert_logup_bus_err(ChipletDef::<F>::from_wire(
801 String::from("wired"),
802 2,
803 air.constraint_ast(),
804 air.column_layout().to_vec(),
805 air.column_layout().to_vec(),
806 Vec::new(),
807 air.fixed_columns(),
808 None,
809 Air::<F>::permutation_checks(&air),
810 Vec::new(),
811 Vec::new(),
812 ));
813 }
814}