Skip to main content

tasm_lib/traits/
accessor.rs

1use std::collections::HashMap;
2
3use rand::prelude::*;
4use triton_vm::prelude::*;
5
6use super::basic_snippet::BasicSnippet;
7use super::rust_shadow::RustShadow;
8use super::rust_shadow::RustShadowError;
9use crate::InitVmState;
10use crate::linker::execute_bench;
11use crate::prelude::Tip5;
12use crate::snippet_bencher::BenchmarkCase;
13use crate::snippet_bencher::NamedBenchmarkResult;
14use crate::snippet_bencher::write_benchmarks;
15use crate::test_helpers::test_rust_equivalence_given_complete_state;
16
17/// An Accessor can modify that stack but only read from memory
18///
19/// An Accessor is a piece of tasm code that can read from and write to the
20/// stack but can only read from memory. Note that it cannot write to static
21/// memory. It cannot read from any inputs or write to standard out. It cannot
22/// modify the sponge state.
23/// See also: [closure], [function], [procedure], [algorithm],
24///           [read_only_algorithm], [mem_preserver]
25///
26/// [closure]: crate::traits::closure::Closure
27/// [function]: crate::traits::function::Function
28/// [procedure]: crate::traits::procedure::Procedure
29/// [algorithm]: crate::traits::algorithm::Algorithm
30/// [read_only_algorithm]: crate::traits::read_only_algorithm::ReadOnlyAlgorithm
31/// [mem_preserver]: crate::traits::mem_preserver::MemPreserver
32pub trait Accessor: BasicSnippet {
33    fn rust_shadow(
34        &self,
35        stack: &mut Vec<BFieldElement>,
36        memory: &HashMap<BFieldElement, BFieldElement>,
37    ) -> Result<(), RustShadowError>;
38
39    fn pseudorandom_initial_state(
40        &self,
41        seed: [u8; 32],
42        bench_case: Option<BenchmarkCase>,
43    ) -> AccessorInitialState;
44
45    fn corner_case_initial_states(&self) -> Vec<AccessorInitialState> {
46        Vec::new()
47    }
48}
49
50#[derive(Debug, Clone, Default)]
51pub struct AccessorInitialState {
52    pub stack: Vec<BFieldElement>,
53    pub memory: HashMap<BFieldElement, BFieldElement>,
54}
55
56impl From<AccessorInitialState> for InitVmState {
57    fn from(value: AccessorInitialState) -> Self {
58        let nd = NonDeterminism::default().with_ram(value.memory);
59        Self {
60            stack: value.stack,
61            nondeterminism: nd,
62            ..Default::default()
63        }
64    }
65}
66
67pub struct ShadowedAccessor<T: Accessor> {
68    accessor: T,
69}
70
71impl<T: Accessor> ShadowedAccessor<T> {
72    pub fn new(accessor: T) -> Self {
73        Self { accessor }
74    }
75}
76
77impl<T> RustShadow for ShadowedAccessor<T>
78where
79    T: Accessor,
80{
81    fn inner(&self) -> &dyn BasicSnippet {
82        &self.accessor
83    }
84
85    fn rust_shadow_wrapper(
86        &self,
87        _stdin: &[BFieldElement],
88        _nondeterminism: &NonDeterminism,
89        stack: &mut Vec<BFieldElement>,
90        memory: &mut HashMap<BFieldElement, BFieldElement>,
91        _sponge: &mut Option<Tip5>,
92    ) -> Result<Vec<BFieldElement>, RustShadowError> {
93        self.accessor.rust_shadow(stack, memory)?;
94
95        Ok(Vec::new())
96    }
97
98    fn test(&self) {
99        for corner_case in self.accessor.corner_case_initial_states() {
100            let stdin = vec![];
101            let nd = NonDeterminism::default().with_ram(corner_case.memory);
102            test_rust_equivalence_given_complete_state(
103                self,
104                &corner_case.stack,
105                &stdin,
106                &nd,
107                &None,
108                None,
109            );
110        }
111
112        let num_states = 10;
113        let mut rng = StdRng::from_seed(rand::random());
114        for _ in 0..num_states {
115            let AccessorInitialState { stack, memory } =
116                self.accessor.pseudorandom_initial_state(rng.random(), None);
117
118            let stdin = vec![];
119            let nd = NonDeterminism::default().with_ram(memory);
120            test_rust_equivalence_given_complete_state(self, &stack, &stdin, &nd, &None, None);
121        }
122    }
123
124    fn bench(&self) {
125        let mut rng = StdRng::from_seed(
126            hex::decode("73a24b6b8b32e4d7d563a4d9a85f476573a24b6b8b32e4d7d563a4d9a85f4765")
127                .unwrap()
128                .try_into()
129                .unwrap(),
130        );
131        let mut benchmarks = Vec::with_capacity(2);
132
133        for bench_case in [BenchmarkCase::CommonCase, BenchmarkCase::WorstCase] {
134            let AccessorInitialState { stack, memory } = self
135                .accessor
136                .pseudorandom_initial_state(rng.random(), Some(bench_case));
137            let program = self.accessor.link_for_isolated_run();
138            let nd = NonDeterminism::default().with_ram(memory);
139            let benchmark = execute_bench(&program, &stack, vec![], nd, None);
140            let benchmark = NamedBenchmarkResult {
141                name: self.accessor.entrypoint(),
142                benchmark_result: benchmark,
143                case: bench_case,
144            };
145            benchmarks.push(benchmark);
146        }
147
148        write_benchmarks(benchmarks);
149    }
150}