1use crate::{
4 access, alu, asm, crypto,
5 error::{
6 EvalSyncError, EvalSyncResult, ExecSyncError, ExecSyncResult, OpSyncError, OpSyncResult,
7 },
8 pred, repeat, total_control_flow,
9 types::convert::bool_from_word,
10 Access, LazyCache, Memory, OpAccess, OpSync, ProgramControlFlow, Repeat, Stack, Vm,
11};
12
13impl From<asm::Access> for OpSync {
14 fn from(op: asm::Access) -> Self {
15 Self::Access(op)
16 }
17}
18
19impl From<asm::Alu> for OpSync {
20 fn from(op: asm::Alu) -> Self {
21 Self::Alu(op)
22 }
23}
24
25impl From<asm::TotalControlFlow> for OpSync {
26 fn from(op: asm::TotalControlFlow) -> Self {
27 Self::ControlFlow(op)
28 }
29}
30
31impl From<asm::Crypto> for OpSync {
32 fn from(op: asm::Crypto) -> Self {
33 Self::Crypto(op)
34 }
35}
36
37impl From<asm::Memory> for OpSync {
38 fn from(op: asm::Memory) -> Self {
39 Self::Memory(op)
40 }
41}
42
43impl From<asm::Pred> for OpSync {
44 fn from(op: asm::Pred) -> Self {
45 Self::Pred(op)
46 }
47}
48
49impl From<asm::Stack> for OpSync {
50 fn from(op: asm::Stack) -> Self {
51 Self::Stack(op)
52 }
53}
54
55pub fn eval_ops(ops: &[OpSync], access: Access) -> EvalSyncResult<bool> {
59 eval(ops, access)
60}
61
62pub fn eval<OA>(op_access: OA, access: Access) -> EvalSyncResult<bool>
66where
67 OA: OpAccess<Op = OpSync>,
68 OA::Error: Into<OpSyncError>,
69{
70 let stack = exec(op_access, access)?;
71 let word = match stack.last() {
72 Some(&w) => w,
73 None => return Err(EvalSyncError::InvalidEvaluation(stack)),
74 };
75 bool_from_word(word).ok_or_else(|| EvalSyncError::InvalidEvaluation(stack))
76}
77
78pub fn exec_ops(ops: &[OpSync], access: Access) -> ExecSyncResult<Stack> {
80 exec(ops, access)
81}
82
83pub fn exec<OA>(mut op_access: OA, access: Access) -> ExecSyncResult<Stack>
85where
86 OA: OpAccess<Op = OpSync>,
87 OA::Error: Into<OpSyncError>,
88{
89 let mut pc = 0;
90 let mut stack = Stack::default();
91 let mut memory = Memory::new();
92 let mut repeat = Repeat::new();
93 let cache = LazyCache::new();
94 while let Some(res) = op_access.op_access(pc) {
95 let op = res.map_err(|err| ExecSyncError(pc, err.into()))?;
96
97 let res = step_op(access, op, &mut stack, &mut memory, pc, &mut repeat, &cache);
98
99 #[cfg(feature = "tracing")]
100 crate::trace_op_res(&mut op_access, pc, &stack, &memory, res.as_ref());
101
102 let update = match res {
103 Ok(update) => update,
104 Err(err) => return Err(ExecSyncError(pc, err)),
105 };
106
107 match update {
108 Some(ProgramControlFlow::Pc(new_pc)) => pc = new_pc,
109 Some(ProgramControlFlow::Halt) => break,
110 None => pc += 1,
111 }
112 }
113 Ok(stack)
114}
115
116pub fn step_op_sync(op: OpSync, access: Access, vm: &mut Vm) -> OpSyncResult<Option<usize>> {
121 let Vm {
122 stack,
123 repeat,
124 pc,
125 memory,
126 cache,
127 ..
128 } = vm;
129 match step_op(access, op, stack, memory, *pc, repeat, cache)? {
130 Some(ProgramControlFlow::Pc(pc)) => return Ok(Some(pc)),
131 Some(ProgramControlFlow::Halt) => return Ok(None),
132 None => (),
133 }
134 let new_pc = vm.pc.checked_add(1).ok_or(OpSyncError::PcOverflow)?;
136 Ok(Some(new_pc))
137}
138
139pub fn step_op(
141 access: Access,
142 op: OpSync,
143 stack: &mut Stack,
144 memory: &mut Memory,
145 pc: usize,
146 repeat: &mut Repeat,
147 cache: &LazyCache,
148) -> OpSyncResult<Option<ProgramControlFlow>> {
149 match op {
150 OpSync::Access(op) => step_op_access(access, op, stack, repeat, cache).map(|_| None),
151 OpSync::Alu(op) => step_op_alu(op, stack).map(|_| None),
152 OpSync::Crypto(op) => step_op_crypto(op, stack).map(|_| None),
153 OpSync::Pred(op) => step_op_pred(op, stack).map(|_| None),
154 OpSync::Stack(op) => step_op_stack(op, pc, stack, repeat),
155 OpSync::ControlFlow(op) => step_op_total_control_flow(op, stack, pc),
156 OpSync::Memory(op) => step_op_memory(op, stack, memory).map(|_| None),
157 }
158}
159
160pub fn step_op_access(
162 access: Access,
163 op: asm::Access,
164 stack: &mut Stack,
165 repeat: &mut Repeat,
166 cache: &LazyCache,
167) -> OpSyncResult<()> {
168 match op {
169 asm::Access::PredicateData => {
170 access::predicate_data(&access.this_solution().predicate_data, stack)
171 }
172 asm::Access::PredicateDataLen => {
173 access::predicate_data_len(&access.this_solution().predicate_data, stack)
174 .map_err(From::from)
175 }
176 asm::Access::PredicateDataSlots => {
177 access::predicate_data_slots(stack, &access.this_solution().predicate_data)
178 }
179 asm::Access::MutKeys => access::push_mut_keys(access, stack),
180 asm::Access::ThisAddress => access::this_address(access.this_solution(), stack),
181 asm::Access::ThisContractAddress => {
182 access::this_contract_address(access.this_solution(), stack)
183 }
184 asm::Access::RepeatCounter => access::repeat_counter(stack, repeat),
185 asm::Access::PredicateExists => access::predicate_exists(stack, access.solutions, cache),
186 }
187}
188
189pub fn step_op_alu(op: asm::Alu, stack: &mut Stack) -> OpSyncResult<()> {
191 match op {
192 asm::Alu::Add => stack.pop2_push1(alu::add),
193 asm::Alu::Sub => stack.pop2_push1(alu::sub),
194 asm::Alu::Mul => stack.pop2_push1(alu::mul),
195 asm::Alu::Div => stack.pop2_push1(alu::div),
196 asm::Alu::Mod => stack.pop2_push1(alu::mod_),
197 asm::Alu::Shl => stack.pop2_push1(alu::shl),
198 asm::Alu::Shr => stack.pop2_push1(alu::shr),
199 asm::Alu::ShrI => stack.pop2_push1(alu::arithmetic_shr),
200 }
201}
202
203pub fn step_op_crypto(op: asm::Crypto, stack: &mut Stack) -> OpSyncResult<()> {
205 match op {
206 asm::Crypto::Sha256 => crypto::sha256(stack),
207 asm::Crypto::VerifyEd25519 => crypto::verify_ed25519(stack),
208 asm::Crypto::RecoverSecp256k1 => crypto::recover_secp256k1(stack),
209 }
210}
211
212pub fn step_op_pred(op: asm::Pred, stack: &mut Stack) -> OpSyncResult<()> {
214 match op {
215 asm::Pred::Eq => stack.pop2_push1(|a, b| Ok((a == b).into())),
216 asm::Pred::EqRange => pred::eq_range(stack),
217 asm::Pred::Gt => stack.pop2_push1(|a, b| Ok((a > b).into())),
218 asm::Pred::Lt => stack.pop2_push1(|a, b| Ok((a < b).into())),
219 asm::Pred::Gte => stack.pop2_push1(|a, b| Ok((a >= b).into())),
220 asm::Pred::Lte => stack.pop2_push1(|a, b| Ok((a <= b).into())),
221 asm::Pred::And => stack.pop2_push1(|a, b| Ok((a != 0 && b != 0).into())),
222 asm::Pred::Or => stack.pop2_push1(|a, b| Ok((a != 0 || b != 0).into())),
223 asm::Pred::Not => stack.pop1_push1(|a| Ok((a == 0).into())),
224 asm::Pred::EqSet => pred::eq_set(stack),
225 asm::Pred::BitAnd => stack.pop2_push1(|a, b| Ok(a & b)),
226 asm::Pred::BitOr => stack.pop2_push1(|a, b| Ok(a | b)),
227 }
228}
229
230pub fn step_op_stack(
232 op: asm::Stack,
233 pc: usize,
234 stack: &mut Stack,
235 repeat: &mut Repeat,
236) -> OpSyncResult<Option<ProgramControlFlow>> {
237 if let asm::Stack::RepeatEnd = op {
238 return Ok(repeat.repeat()?.map(ProgramControlFlow::Pc));
239 }
240 let r = match op {
241 asm::Stack::Drop => stack.pop_len_words(|_| Ok(())),
242 asm::Stack::Dup => stack.pop1_push2(|w| Ok([w, w])),
243 asm::Stack::DupFrom => stack.dup_from().map_err(From::from),
244 asm::Stack::Push(word) => stack.push(word).map_err(From::from),
245 asm::Stack::Pop => stack.pop().map(|_| ()).map_err(From::from),
246 asm::Stack::Swap => stack.pop2_push2(|a, b| Ok([b, a])),
247 asm::Stack::SwapIndex => stack.swap_index().map_err(From::from),
248 asm::Stack::Select => stack.select().map_err(From::from),
249 asm::Stack::SelectRange => stack.select_range().map_err(From::from),
250 asm::Stack::Repeat => repeat::repeat(pc, stack, repeat),
251 asm::Stack::Reserve => stack.reserve_zeroed().map_err(From::from),
252 asm::Stack::Load => stack.load().map_err(From::from),
253 asm::Stack::Store => stack.store().map_err(From::from),
254 asm::Stack::RepeatEnd => unreachable!(),
255 };
256 r.map(|_| None)
257}
258
259pub fn step_op_total_control_flow(
261 op: asm::TotalControlFlow,
262 stack: &mut Stack,
263 pc: usize,
264) -> OpSyncResult<Option<ProgramControlFlow>> {
265 match op {
266 asm::TotalControlFlow::JumpIf => total_control_flow::jump_if(stack, pc),
267 asm::TotalControlFlow::HaltIf => total_control_flow::halt_if(stack),
268 asm::TotalControlFlow::Halt => Ok(Some(ProgramControlFlow::Halt)),
269 asm::TotalControlFlow::PanicIf => total_control_flow::panic_if(stack).map(|_| None),
270 }
271}
272
273pub fn step_op_memory(op: asm::Memory, stack: &mut Stack, memory: &mut Memory) -> OpSyncResult<()> {
275 match op {
276 asm::Memory::Alloc => {
277 let w = stack.pop()?;
278 let len = memory.len()?;
279 memory.alloc(w)?;
280 Ok(stack.push(len)?)
281 }
282 asm::Memory::Store => {
283 let [w, addr] = stack.pop2()?;
284 memory.store(addr, w)?;
285 Ok(())
286 }
287 asm::Memory::Load => stack.pop1_push1(|addr| {
288 let w = memory.load(addr)?;
289 Ok(w)
290 }),
291 asm::Memory::Free => {
292 let addr = stack.pop()?;
293 memory.free(addr)?;
294 Ok(())
295 }
296 asm::Memory::LoadRange => {
297 let [addr, size] = stack.pop2()?;
298 let words = memory.load_range(addr, size)?;
299 Ok(stack.extend(words)?)
300 }
301 asm::Memory::StoreRange => {
302 let addr = stack.pop()?;
303 stack.pop_len_words(|words| {
304 memory.store_range(addr, words)?;
305 Ok::<_, OpSyncError>(())
306 })?;
307 Ok(())
308 }
309 }
310}
311
312#[cfg(test)]
313pub(crate) mod test_util {
314 use crate::{
315 types::{solution::Solution, ContentAddress, PredicateAddress},
316 *,
317 };
318 use asm::Word;
319 use std::collections::HashSet;
320
321 pub(crate) const TEST_SET_CA: ContentAddress = ContentAddress([0xFF; 32]);
322 pub(crate) const TEST_PREDICATE_CA: ContentAddress = ContentAddress([0xAA; 32]);
323 pub(crate) const TEST_PREDICATE_ADDR: PredicateAddress = PredicateAddress {
324 contract: TEST_SET_CA,
325 predicate: TEST_PREDICATE_CA,
326 };
327 pub(crate) const TEST_SOLUTION: Solution = Solution {
328 predicate_to_solve: TEST_PREDICATE_ADDR,
329 predicate_data: vec![],
330 state_mutations: vec![],
331 };
332
333 pub(crate) fn test_empty_keys() -> &'static HashSet<&'static [Word]> {
334 static INSTANCE: std::sync::LazyLock<HashSet<&[Word]>> =
335 std::sync::LazyLock::new(|| HashSet::with_capacity(0));
336 &INSTANCE
337 }
338
339 pub(crate) fn test_solutions() -> &'static [Solution] {
340 static INSTANCE: std::sync::LazyLock<[Solution; 1]> =
341 std::sync::LazyLock::new(|| [TEST_SOLUTION]);
342 &*INSTANCE
343 }
344
345 pub(crate) fn test_access() -> &'static Access<'static> {
346 static INSTANCE: std::sync::LazyLock<Access> = std::sync::LazyLock::new(|| Access {
347 solutions: test_solutions(),
348 index: 0,
349 mutable_keys: test_empty_keys(),
350 });
351 &INSTANCE
352 }
353}