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