tasm_lib/traits/
procedure.rs1use std::collections::HashMap;
2
3use rand::prelude::*;
4use triton_vm::prelude::*;
5
6use crate::InitVmState;
7use crate::linker::execute_bench;
8use crate::prelude::Tip5;
9use crate::snippet_bencher::BenchmarkCase;
10use crate::snippet_bencher::NamedBenchmarkResult;
11use crate::snippet_bencher::write_benchmarks;
12use crate::test_helpers::test_rust_equivalence_given_complete_state;
13use crate::traits::basic_snippet::BasicSnippet;
14use crate::traits::rust_shadow::RustShadow;
15use crate::traits::rust_shadow::RustShadowError;
16
17pub trait Procedure: BasicSnippet {
35 fn rust_shadow(
37 &self,
38 stack: &mut Vec<BFieldElement>,
39 memory: &mut HashMap<BFieldElement, BFieldElement>,
40 nondeterminism: &NonDeterminism,
41 public_input: &[BFieldElement],
42 sponge: &mut Option<Tip5>,
43 ) -> Result<Vec<BFieldElement>, RustShadowError>;
44
45 fn preprocess<T: BFieldCodec>(_meta_input: T, _nondeterminism: &mut NonDeterminism) {}
46
47 fn pseudorandom_initial_state(
48 &self,
49 seed: [u8; 32],
50 bench_case: Option<BenchmarkCase>,
51 ) -> ProcedureInitialState;
52
53 fn corner_case_initial_states(&self) -> Vec<ProcedureInitialState> {
54 Vec::new()
55 }
56}
57
58#[derive(Debug, Clone, Default)]
59pub struct ProcedureInitialState {
60 pub stack: Vec<BFieldElement>,
61 pub nondeterminism: NonDeterminism,
62 pub public_input: Vec<BFieldElement>,
63 pub sponge: Option<Tip5>,
64}
65
66impl From<ProcedureInitialState> for InitVmState {
67 fn from(value: ProcedureInitialState) -> Self {
68 Self {
69 stack: value.stack,
70 public_input: value.public_input,
71 nondeterminism: value.nondeterminism,
72 sponge: value.sponge,
73 }
74 }
75}
76
77pub struct ShadowedProcedure<P: Procedure> {
78 procedure: P,
79}
80
81impl<P: Procedure> ShadowedProcedure<P> {
82 pub fn new(procedure: P) -> Self {
83 Self { procedure }
84 }
85}
86
87impl<P: Procedure> RustShadow for ShadowedProcedure<P> {
88 fn inner(&self) -> &dyn BasicSnippet {
89 &self.procedure
90 }
91
92 fn rust_shadow_wrapper(
93 &self,
94 stdin: &[BFieldElement],
95 nondeterminism: &NonDeterminism,
96 stack: &mut Vec<BFieldElement>,
97 memory: &mut HashMap<BFieldElement, BFieldElement>,
98 sponge: &mut Option<Tip5>,
99 ) -> Result<Vec<BFieldElement>, RustShadowError> {
100 self.procedure
101 .rust_shadow(stack, memory, nondeterminism, stdin, sponge)
102 }
103
104 fn test(&self) {
105 let num_states = 5;
106 let seed: [u8; 32] = rand::rng().random();
107 let mut rng = StdRng::from_seed(seed);
108 let procedure = &self.procedure;
109
110 for corner_case in procedure.corner_case_initial_states().into_iter() {
111 self.test_initial_state(corner_case);
112 }
113
114 for _ in 0..num_states {
115 let seed: [u8; 32] = rng.random();
116 let state = procedure.pseudorandom_initial_state(seed, None);
117
118 self.test_initial_state(state);
119 }
120 }
121
122 fn bench(&self) {
123 let mut rng = StdRng::from_seed(
124 hex::decode("73a24b6b8b32e4d7d563a4d9a85f476573a24b6b8b32e4d7d563a4d9a85f4765")
125 .unwrap()
126 .try_into()
127 .unwrap(),
128 );
129 let mut benchmarks = Vec::with_capacity(2);
130
131 for bench_case in [BenchmarkCase::CommonCase, BenchmarkCase::WorstCase] {
132 let ProcedureInitialState {
133 stack,
134 nondeterminism,
135 public_input,
136 sponge,
137 } = self
138 .procedure
139 .pseudorandom_initial_state(rng.random(), Some(bench_case));
140 let program = self.procedure.link_for_isolated_run();
141 let benchmark = execute_bench(&program, &stack, public_input, nondeterminism, sponge);
142 let benchmark = NamedBenchmarkResult {
143 name: self.procedure.entrypoint(),
144 benchmark_result: benchmark,
145 case: bench_case,
146 };
147 benchmarks.push(benchmark);
148 }
149
150 write_benchmarks(benchmarks);
151 }
152}
153
154impl<P: Procedure> ShadowedProcedure<P> {
155 fn test_initial_state(&self, state: ProcedureInitialState) {
156 test_rust_equivalence_given_complete_state(
157 self,
158 &state.stack,
159 &state.public_input,
160 &state.nondeterminism,
161 &state.sponge,
162 None,
163 );
164 }
165}