1use std::{
13 any::{type_name, Any},
14 iter::{self, zip},
15 sync::Arc,
16};
17
18use getset::{CopyGetters, Getters};
19use openvm_circuit_primitives::{
20 var_range::{SharedVariableRangeCheckerChip, VariableRangeCheckerAir},
21 AnyChip, Chip, ColumnsAir,
22};
23use openvm_cpu_backend::CpuBackend;
24use openvm_instructions::{PhantomDiscriminant, VmOpcode};
25use openvm_stark_backend::{
26 interaction::BusIndex,
27 keygen::{types::MultiStarkProvingKey, MultiStarkKeygenBuilder},
28 prover::{AirProvingContext, MatrixDimensions, ProverBackend, ProvingContext},
29 AirRef, AnyAir, StarkEngine, StarkProtocolConfig, Val,
30};
31use rustc_hash::FxHashMap;
32use tracing::info_span;
33
34use super::{GenerationError, PhantomSubExecutor, SystemConfig};
35use crate::{
36 arch::Arena,
37 system::{
38 memory::{BOUNDARY_AIR_OFFSET, MERKLE_AIR_OFFSET},
39 phantom::PhantomExecutor,
40 SystemAirInventory, SystemChipComplex, SystemRecords,
41 },
42};
43
44pub const PROGRAM_AIR_ID: usize = 0;
46pub const PROGRAM_CACHED_TRACE_INDEX: usize = 0;
48pub const CONNECTOR_AIR_ID: usize = 1;
49pub const MEMORY_AIRS_START_IDX: usize = 2;
51pub const BOUNDARY_AIR_ID: usize = MEMORY_AIRS_START_IDX + BOUNDARY_AIR_OFFSET;
53pub const MERKLE_AIR_ID: usize = MEMORY_AIRS_START_IDX + MERKLE_AIR_OFFSET;
56
57pub type ExecutorId = u32;
58
59pub trait AnyAirWithColumns<SC: StarkProtocolConfig>: AnyAir<SC> + ColumnsAir {}
68
69impl<SC, T> AnyAirWithColumns<SC> for T
70where
71 SC: StarkProtocolConfig,
72 T: AnyAir<SC> + ColumnsAir,
73{
74}
75
76pub type AirRefWithColumns<SC> = Arc<dyn AnyAirWithColumns<SC>>;
78
79pub trait VmExecutionExtension<F> {
84 type Executor: AnyEnum;
86
87 fn extend_execution(
88 &self,
89 inventory: &mut ExecutorInventoryBuilder<F, Self::Executor>,
90 ) -> Result<(), ExecutorInventoryError>;
91}
92
93pub trait VmCircuitExtension<SC: StarkProtocolConfig> {
95 fn extend_circuit(&self, inventory: &mut AirInventory<SC>) -> Result<(), AirInventoryError>;
96}
97
98pub trait VmProverExtension<E, RA, EXT>
105where
106 E: StarkEngine,
107 EXT: VmExecutionExtension<Val<E::SC>> + VmCircuitExtension<E::SC>,
108{
109 fn extend_prover(
116 &self,
117 extension: &EXT,
118 inventory: &mut ChipInventory<E::SC, RA, E::PB>,
119 ) -> Result<(), ChipInventoryError>;
120}
121
122pub struct ExecutorInventory<E> {
125 config: SystemConfig,
126 pub instruction_lookup: FxHashMap<VmOpcode, ExecutorId>,
130 pub executors: Vec<E>,
131 ext_start: Vec<usize>,
133}
134
135pub struct ExecutorInventoryBuilder<'a, F, E> {
141 old_executors: Vec<&'a dyn AnyEnum>,
145 new_inventory: ExecutorInventory<E>,
146 phantom_executors: FxHashMap<PhantomDiscriminant, Arc<dyn PhantomSubExecutor<F>>>,
147}
148
149#[derive(Clone, Getters, CopyGetters)]
150pub struct AirInventory<SC: StarkProtocolConfig> {
151 #[get = "pub"]
152 config: SystemConfig,
153 #[get = "pub"]
155 system: SystemAirInventory,
156 #[get = "pub"]
162 ext_airs: Vec<AirRefWithColumns<SC>>,
163 ext_start: Vec<usize>,
165
166 bus_idx_mgr: BusIndexManager,
167}
168
169#[derive(Clone, Copy, Debug, Default)]
170pub struct BusIndexManager {
171 bus_idx_max: BusIndex,
173}
174
175#[derive(Getters)]
178pub struct ChipInventory<SC, RA, PB>
179where
180 SC: StarkProtocolConfig,
181 PB: ProverBackend,
182{
183 #[get = "pub"]
185 airs: AirInventory<SC>,
186 #[get = "pub"]
188 chips: Vec<Box<dyn AnyChip<RA, PB>>>,
189
190 cur_num_exts: usize,
193 pub executor_idx_to_insertion_idx: Vec<usize>,
200}
201
202#[derive(Getters)]
206pub struct VmChipComplex<SC, RA, PB, SCC>
207where
208 SC: StarkProtocolConfig,
209 PB: ProverBackend,
210{
211 pub system: SCC,
213 pub inventory: ChipInventory<SC, RA, PB>,
214}
215
216impl<E> ExecutorInventory<E> {
219 #[allow(clippy::new_without_default)]
221 pub fn new(config: SystemConfig) -> Self {
222 Self {
223 config,
224 instruction_lookup: Default::default(),
225 executors: Default::default(),
226 ext_start: vec![0],
227 }
228 }
229
230 pub fn add_executor(
234 &mut self,
235 executor: impl Into<E>,
236 opcodes: impl IntoIterator<Item = VmOpcode>,
237 ) -> Result<(), ExecutorInventoryError> {
238 let opcodes: Vec<_> = opcodes.into_iter().collect();
239 for opcode in &opcodes {
240 if let Some(id) = self.instruction_lookup.get(opcode) {
241 return Err(ExecutorInventoryError::ExecutorExists {
242 opcode: *opcode,
243 id: *id,
244 });
245 }
246 }
247 let id = self.executors.len();
248 self.executors.push(executor.into());
249 for opcode in opcodes {
250 self.instruction_lookup
251 .insert(opcode, id.try_into().unwrap());
252 }
253 Ok(())
254 }
255
256 pub fn extend<F, E3, EXT>(
259 self,
260 other: &EXT,
261 ) -> Result<ExecutorInventory<E3>, ExecutorInventoryError>
262 where
263 F: 'static,
264 E: Into<E3> + AnyEnum,
265 E3: AnyEnum,
266 EXT: VmExecutionExtension<F>,
267 EXT::Executor: Into<E3>,
268 {
269 let mut builder: ExecutorInventoryBuilder<F, EXT::Executor> = self.builder();
270 other.extend_execution(&mut builder)?;
271 let other_inventory = builder.new_inventory;
272 let other_phantom_executors = builder.phantom_executors;
273 let mut inventory_ext = self.transmute();
274 inventory_ext.append(other_inventory.transmute())?;
275 let phantom_chip: &mut PhantomExecutor<F> = inventory_ext
276 .find_executor_mut()
277 .next()
278 .expect("system always has phantom chip");
279 let phantom_executors = &mut phantom_chip.phantom_executors;
280 for (discriminant, sub_executor) in other_phantom_executors {
281 if phantom_executors
282 .insert(discriminant, sub_executor)
283 .is_some()
284 {
285 return Err(ExecutorInventoryError::PhantomSubExecutorExists { discriminant });
286 }
287 }
288
289 Ok(inventory_ext)
290 }
291
292 pub fn builder<F, E2>(&self) -> ExecutorInventoryBuilder<'_, F, E2>
293 where
294 F: 'static,
295 E: AnyEnum,
296 {
297 let old_executors = self.executors.iter().map(|e| e as &dyn AnyEnum).collect();
298 ExecutorInventoryBuilder {
299 old_executors,
300 new_inventory: ExecutorInventory::new(self.config.clone()),
301 phantom_executors: Default::default(),
302 }
303 }
304
305 pub fn transmute<E2>(self) -> ExecutorInventory<E2>
306 where
307 E: Into<E2>,
308 {
309 ExecutorInventory {
310 config: self.config,
311 instruction_lookup: self.instruction_lookup,
312 executors: self.executors.into_iter().map(|e| e.into()).collect(),
313 ext_start: self.ext_start,
314 }
315 }
316
317 fn append(&mut self, mut other: ExecutorInventory<E>) -> Result<(), ExecutorInventoryError> {
320 let num_executors = self.executors.len();
321 for (opcode, mut id) in other.instruction_lookup.into_iter() {
322 id = id.checked_add(num_executors.try_into().unwrap()).unwrap();
323 if let Some(old_id) = self.instruction_lookup.insert(opcode, id) {
324 return Err(ExecutorInventoryError::ExecutorExists { opcode, id: old_id });
325 }
326 }
327 for id in &mut other.ext_start {
328 *id = id.checked_add(num_executors).unwrap();
329 }
330 self.executors.append(&mut other.executors);
331 self.ext_start.append(&mut other.ext_start);
332 Ok(())
333 }
334
335 pub fn get_executor(&self, opcode: VmOpcode) -> Option<&E> {
336 let id = self.instruction_lookup.get(&opcode)?;
337 self.executors.get(*id as usize)
338 }
339
340 pub fn get_mut_executor(&mut self, opcode: &VmOpcode) -> Option<&mut E> {
341 let id = self.instruction_lookup.get(opcode)?;
342 self.executors.get_mut(*id as usize)
343 }
344
345 pub fn executors(&self) -> &[E] {
346 &self.executors
347 }
348
349 pub fn find_executor<EX: 'static>(&self) -> impl Iterator<Item = &'_ EX>
350 where
351 E: AnyEnum,
352 {
353 self.executors
354 .iter()
355 .filter_map(|e| e.as_any_kind().downcast_ref())
356 }
357
358 pub fn find_executor_mut<EX: 'static>(&mut self) -> impl Iterator<Item = &'_ mut EX>
359 where
360 E: AnyEnum,
361 {
362 self.executors
363 .iter_mut()
364 .filter_map(|e| e.as_any_kind_mut().downcast_mut())
365 }
366
367 pub fn config(&self) -> &SystemConfig {
369 &self.config
370 }
371}
372
373impl<F, E> ExecutorInventoryBuilder<'_, F, E> {
374 pub fn add_executor(
375 &mut self,
376 executor: impl Into<E>,
377 opcodes: impl IntoIterator<Item = VmOpcode>,
378 ) -> Result<(), ExecutorInventoryError> {
379 self.new_inventory.add_executor(executor, opcodes)
380 }
381
382 pub fn add_phantom_sub_executor<PE>(
383 &mut self,
384 phantom_sub: PE,
385 discriminant: PhantomDiscriminant,
386 ) -> Result<(), ExecutorInventoryError>
387 where
388 E: AnyEnum,
389 F: 'static,
390 PE: PhantomSubExecutor<F> + 'static,
391 {
392 let existing = self
393 .phantom_executors
394 .insert(discriminant, Arc::new(phantom_sub));
395 if existing.is_some() {
396 return Err(ExecutorInventoryError::PhantomSubExecutorExists { discriminant });
397 }
398 Ok(())
399 }
400
401 pub fn find_executor<EX: 'static>(&self) -> impl Iterator<Item = &'_ EX>
402 where
403 E: AnyEnum,
404 {
405 self.old_executors
406 .iter()
407 .filter_map(|e| e.as_any_kind().downcast_ref())
408 }
409
410 pub fn pointer_max_bits(&self) -> usize {
412 self.new_inventory.config().memory_config.pointer_max_bits
413 }
414}
415
416impl<SC: StarkProtocolConfig> AirInventory<SC> {
417 pub(crate) fn new(
419 config: SystemConfig,
420 system: SystemAirInventory,
421 bus_idx_mgr: BusIndexManager,
422 ) -> Self {
423 Self {
424 config,
425 system,
426 ext_start: Vec::new(),
427 ext_airs: Vec::new(),
428 bus_idx_mgr,
429 }
430 }
431
432 pub fn start_new_extension(&mut self) {
434 self.ext_start.push(self.ext_airs.len());
435 }
436
437 pub fn new_bus_idx(&mut self) -> BusIndex {
438 self.bus_idx_mgr.new_bus_idx()
439 }
440
441 pub fn find_air<A: 'static>(&self) -> impl Iterator<Item = &'_ A> {
446 self.ext_airs
447 .iter()
448 .filter_map(|air| air.as_any().downcast_ref())
449 }
450
451 pub fn add_air<A: AnyAirWithColumns<SC> + 'static>(&mut self, air: A) {
452 self.add_air_ref(Arc::new(air));
453 }
454
455 pub fn add_air_ref(&mut self, air: AirRefWithColumns<SC>) {
456 self.ext_airs.push(air);
457 }
458
459 pub fn range_checker(&self) -> &VariableRangeCheckerAir {
460 self.find_air()
461 .next()
462 .expect("system always has range checker AIR")
463 }
464
465 pub fn into_airs(self) -> impl Iterator<Item = AirRefWithColumns<SC>> {
470 self.system
471 .into_airs()
472 .into_iter()
473 .chain(self.ext_airs.into_iter().rev())
474 }
475
476 pub fn keygen(self, config: &SC) -> MultiStarkProvingKey<SC> {
479 let system_config = self.config.clone();
480 let mut keygen_builder = MultiStarkKeygenBuilder::new(config.clone());
481 for (air_id, air) in self.into_airs().enumerate() {
482 if system_config.is_required_air_id(air_id) {
483 keygen_builder.add_required_air(air as AirRef<_>);
484 } else {
485 keygen_builder.add_air(air as AirRef<_>);
486 }
487 }
488 keygen_builder.generate_pk().unwrap()
489 }
490
491 pub fn num_airs(&self) -> usize {
493 self.config.num_airs() + self.ext_airs.len()
494 }
495
496 pub fn pointer_max_bits(&self) -> usize {
498 self.config.memory_config.pointer_max_bits
499 }
500}
501
502impl BusIndexManager {
503 pub fn new() -> Self {
504 Self { bus_idx_max: 0 }
505 }
506
507 pub fn new_bus_idx(&mut self) -> BusIndex {
508 let idx = self.bus_idx_max;
509 self.bus_idx_max = self.bus_idx_max.checked_add(1).unwrap();
510 idx
511 }
512}
513
514impl<SC, RA, PB> ChipInventory<SC, RA, PB>
515where
516 SC: StarkProtocolConfig,
517 PB: ProverBackend,
518{
519 pub fn new(airs: AirInventory<SC>) -> Self {
520 Self {
521 airs,
522 chips: Vec::new(),
523 cur_num_exts: 0,
524 executor_idx_to_insertion_idx: Vec::new(),
525 }
526 }
527
528 pub fn config(&self) -> &SystemConfig {
529 &self.airs.config
530 }
531
532 pub fn start_new_extension(&mut self) -> Result<(), ChipInventoryError> {
534 if self.cur_num_exts >= self.airs.ext_start.len() {
535 return Err(ChipInventoryError::MissingCircuitExtension(
536 self.airs.ext_start.len(),
537 ));
538 }
539 if self.chips.len() != self.airs.ext_start[self.cur_num_exts] {
540 return Err(ChipInventoryError::MissingChip {
541 actual: self.chips.len(),
542 expected: self.airs.ext_start[self.cur_num_exts],
543 });
544 }
545
546 self.cur_num_exts += 1;
547 Ok(())
548 }
549
550 pub fn next_air<A: 'static>(&self) -> Result<&A, ChipInventoryError> {
553 let cur_idx = self.chips.len();
554 self.airs
555 .ext_airs
556 .get(cur_idx)
557 .and_then(|air| air.as_any().downcast_ref())
558 .ok_or_else(|| ChipInventoryError::AirNotFound {
559 name: type_name::<A>().to_string(),
560 })
561 }
562
563 pub fn find_chip<C: 'static>(&self) -> impl Iterator<Item = &'_ C> {
568 self.chips.iter().filter_map(|c| c.as_any().downcast_ref())
569 }
570
571 pub fn add_periphery_chip<C: Chip<RA, PB> + 'static>(&mut self, chip: C) {
574 self.chips.push(Box::new(chip));
575 }
576
577 pub fn add_executor_chip<C: Chip<RA, PB> + 'static>(&mut self, chip: C) {
581 tracing::debug!("add_executor_chip: {}", type_name::<C>());
582 self.executor_idx_to_insertion_idx.push(self.chips.len());
583 self.chips.push(Box::new(chip));
584 }
585
586 pub fn executor_idx_to_air_idx(&self) -> Vec<usize> {
591 let num_airs = self.airs.num_airs();
592 assert_eq!(
593 num_airs,
594 self.config().num_airs() + self.chips.len(),
595 "Number of chips does not match number of AIRs"
596 );
597 self.executor_idx_to_insertion_idx
600 .iter()
601 .map(|insertion_idx| {
602 num_airs
603 .checked_sub(insertion_idx.checked_add(1).unwrap())
604 .unwrap_or_else(|| {
605 panic!(
606 "Attempt to subtract num_airs={num_airs} by {}",
607 insertion_idx + 1
608 )
609 })
610 })
611 .collect()
612 }
613
614 pub fn timestamp_max_bits(&self) -> usize {
615 self.airs.config().memory_config.timestamp_max_bits
616 }
617
618 pub fn constant_trace_heights(&self) -> Vec<Option<usize>> {
623 let num_system = self.airs.config().num_airs();
624 let mut heights = vec![None; num_system];
625 heights.extend(
626 self.chips
627 .iter()
628 .rev()
629 .map(|chip| chip.constant_trace_height()),
630 );
631 heights
632 }
633}
634
635impl<SC, RA> ChipInventory<SC, RA, CpuBackend<SC>>
637where
638 SC: StarkProtocolConfig,
639{
640 pub fn range_checker(&self) -> Result<&SharedVariableRangeCheckerChip, ChipInventoryError> {
641 self.find_chip::<SharedVariableRangeCheckerChip>()
642 .next()
643 .ok_or_else(|| ChipInventoryError::ChipNotFound {
644 name: "VariableRangeCheckerChip".to_string(),
645 })
646 }
647}
648
649#[derive(thiserror::Error, Debug)]
652pub enum ExecutorInventoryError {
653 #[error("Opcode {opcode} already owned by executor id {id}")]
654 ExecutorExists { opcode: VmOpcode, id: ExecutorId },
655 #[error("Phantom discriminant {} already has sub-executor", .discriminant.0)]
656 PhantomSubExecutorExists { discriminant: PhantomDiscriminant },
657}
658
659#[derive(thiserror::Error, Debug)]
660pub enum AirInventoryError {
661 #[error("AIR {name} not found")]
662 AirNotFound { name: String },
663}
664
665#[derive(thiserror::Error, Debug)]
666pub enum ChipInventoryError {
667 #[error("Air {name} not found")]
668 AirNotFound { name: String },
669 #[error("Chip {name} not found")]
670 ChipNotFound { name: String },
671 #[error("Adding prover extension without execution extension. Number of execution extensions is {0}")]
672 MissingExecutionExtension(usize),
673 #[error(
674 "Adding prover extension without circuit extension. Number of circuit extensions is {0}"
675 )]
676 MissingCircuitExtension(usize),
677 #[error("Missing chip. Number of chips is {actual}, expected number is {expected}")]
678 MissingChip { actual: usize, expected: usize },
679 #[error("Missing executor chip. Number of executors with associated chips is {actual}, expected number is {expected}")]
680 MissingExecutor { actual: usize, expected: usize },
681}
682
683impl<SC, RA, PB, SCC> VmChipComplex<SC, RA, PB, SCC>
686where
687 SC: StarkProtocolConfig,
688 RA: Arena,
689 PB: ProverBackend,
690 SCC: SystemChipComplex<RA, PB>,
691{
692 pub fn system_config(&self) -> &SystemConfig {
693 self.inventory.config()
694 }
695
696 pub(crate) fn generate_proving_ctx(
700 &mut self,
701 system_records: SystemRecords<PB::Val>,
702 record_arenas: Vec<RA>,
703 ) -> Result<ProvingContext<PB>, GenerationError> {
705 let num_sys_airs = self.system_config().num_airs();
713 let num_airs = num_sys_airs + self.inventory.chips.len();
714 if num_airs != record_arenas.len() {
715 return Err(GenerationError::UnexpectedNumArenas {
716 actual: record_arenas.len(),
717 expected: num_airs,
718 });
719 }
720 let mut _record_arenas = record_arenas;
721 let record_arenas = _record_arenas.split_off(num_sys_airs);
722 let sys_record_arenas = _record_arenas;
723
724 let ctx_without_empties: Vec<(usize, AirProvingContext<_>)> = iter::empty()
733 .chain(info_span!("system_trace_gen").in_scope(|| {
734 self.system
735 .generate_proving_ctx(system_records, sys_record_arenas)
736 }))
737 .chain(
738 zip(self.inventory.chips.iter().enumerate().rev(), record_arenas).map(
739 |((insertion_idx, chip), records)| {
740 let _span = (!records.is_empty()).then(|| {
742 let air_name = self.inventory.airs.ext_airs[insertion_idx].name();
743 info_span!("single_trace_gen", air = air_name).entered()
744 });
745 #[cfg(feature = "metrics")]
746 if let Some(allocated_bytes) = (!records.is_empty())
747 .then(|| records.allocated_bytes())
748 .flatten()
749 {
750 let air_name = self.inventory.airs.ext_airs[insertion_idx].name();
751 let labels = [
752 ("air_name", air_name.to_string()),
753 ("air_id", (num_sys_airs + insertion_idx).to_string()),
754 ];
755 metrics::counter!("trace_gen.record_arena_bytes", &labels)
756 .absolute(allocated_bytes as u64);
757 }
758 chip.generate_proving_ctx(records)
759 },
760 ),
761 )
762 .enumerate()
763 .filter(|(_air_id, ctx)| ctx.common_main.height() > 0)
764 .collect();
765
766 Ok(ProvingContext::new(ctx_without_empties))
767 }
768}
769
770impl<F, EXT: VmExecutionExtension<F>> VmExecutionExtension<F> for Option<EXT> {
773 type Executor = EXT::Executor;
774
775 fn extend_execution(
776 &self,
777 inventory: &mut ExecutorInventoryBuilder<F, Self::Executor>,
778 ) -> Result<(), ExecutorInventoryError> {
779 if let Some(extension) = self {
780 extension.extend_execution(inventory)
781 } else {
782 Ok(())
783 }
784 }
785}
786
787impl<SC: StarkProtocolConfig, EXT: VmCircuitExtension<SC>> VmCircuitExtension<SC> for Option<EXT> {
788 fn extend_circuit(&self, inventory: &mut AirInventory<SC>) -> Result<(), AirInventoryError> {
789 if let Some(extension) = self {
790 extension.extend_circuit(inventory)
791 } else {
792 Ok(())
793 }
794 }
795}
796
797pub trait AnyEnum {
799 fn as_any_kind(&self) -> &dyn Any;
801
802 fn as_any_kind_mut(&mut self) -> &mut dyn Any;
804}
805
806impl AnyEnum for () {
807 fn as_any_kind(&self) -> &dyn Any {
808 self
809 }
810 fn as_any_kind_mut(&mut self) -> &mut dyn Any {
811 self
812 }
813}
814
815#[cfg(test)]
816mod tests {
817 use openvm_circuit_derive::AnyEnum;
818 use openvm_stark_sdk::config::baby_bear_poseidon2::BabyBearPoseidon2Config;
819
820 use super::*;
821 use crate::arch::VmCircuitConfig;
822
823 #[allow(dead_code)]
824 #[derive(Copy, Clone)]
825 enum EnumA {
826 A(u8),
827 B(u32),
828 }
829
830 enum EnumB {
831 C(u64),
832 D(EnumA),
833 }
834
835 #[derive(AnyEnum)]
836 enum EnumC {
837 C(u64),
838 #[any_enum]
839 D(EnumA),
840 }
841
842 impl AnyEnum for EnumA {
843 fn as_any_kind(&self) -> &dyn Any {
844 match self {
845 EnumA::A(a) => a,
846 EnumA::B(b) => b,
847 }
848 }
849
850 fn as_any_kind_mut(&mut self) -> &mut dyn Any {
851 match self {
852 EnumA::A(a) => a,
853 EnumA::B(b) => b,
854 }
855 }
856 }
857
858 impl AnyEnum for EnumB {
859 fn as_any_kind(&self) -> &dyn Any {
860 match self {
861 EnumB::C(c) => c,
862 EnumB::D(d) => d.as_any_kind(),
863 }
864 }
865
866 fn as_any_kind_mut(&mut self) -> &mut dyn Any {
867 match self {
868 EnumB::C(c) => c,
869 EnumB::D(d) => d.as_any_kind_mut(),
870 }
871 }
872 }
873
874 #[test]
875 fn test_any_enum_downcast() {
876 let a = EnumA::A(1);
877 assert_eq!(a.as_any_kind().downcast_ref::<u8>(), Some(&1));
878 let b = EnumB::D(a);
879 assert!(b.as_any_kind().downcast_ref::<u64>().is_none());
880 assert!(b.as_any_kind().downcast_ref::<EnumA>().is_none());
881 assert_eq!(b.as_any_kind().downcast_ref::<u8>(), Some(&1));
882 let c = EnumB::C(3);
883 assert_eq!(c.as_any_kind().downcast_ref::<u64>(), Some(&3));
884 let d = EnumC::D(a);
885 assert!(d.as_any_kind().downcast_ref::<u64>().is_none());
886 assert!(d.as_any_kind().downcast_ref::<EnumA>().is_none());
887 assert_eq!(d.as_any_kind().downcast_ref::<u8>(), Some(&1));
888 let e = EnumC::C(3);
889 assert_eq!(e.as_any_kind().downcast_ref::<u64>(), Some(&3));
890 }
891
892 #[test]
893 fn test_system_bus_indices() {
894 let config = SystemConfig::default();
895 let inventory: AirInventory<BabyBearPoseidon2Config> = config.create_airs().unwrap();
896 let system = inventory.system();
897 let port = system.port();
898 assert_eq!(port.execution_bus.index(), 0);
899 assert_eq!(port.memory_bridge.memory_bus().index(), 1);
900 assert_eq!(port.program_bus.index(), 2);
901 assert_eq!(port.memory_bridge.range_bus().index(), 3);
902 assert_eq!(system.memory.interface.boundary.merkle_bus.index, 4);
903 assert_eq!(system.memory.interface.boundary.compression_bus.index, 5);
904 }
905}