1#![deny(missing_docs, unsafe_code)]
35
36pub use access::{
37 mut_keys, mut_keys_set, mut_keys_slices, Access, SolutionAccess, StateSlotSlice, StateSlots,
38};
39#[doc(inline)]
40pub use bytecode::{BytecodeMapped, BytecodeMappedLazy, BytecodeMappedSlice};
41pub use cached::LazyCache;
42#[doc(inline)]
43pub use error::{CheckResult, ConstraintResult, OpResult, StackResult};
44use error::{ConstraintError, ConstraintErrors, ConstraintsUnsatisfied};
45#[doc(inline)]
46pub use essential_constraint_asm as asm;
47use essential_constraint_asm::Op;
48pub use essential_types as types;
49use essential_types::{convert::bool_from_word, ConstraintBytecode};
50#[doc(inline)]
51pub use memory::Memory;
52#[doc(inline)]
53pub use op_access::OpAccess;
54#[doc(inline)]
55pub use repeat::Repeat;
56#[doc(inline)]
57pub use stack::Stack;
58#[doc(inline)]
59pub use total_control_flow::ProgramControlFlow;
60
61mod access;
62mod alu;
63mod bytecode;
64mod cached;
65mod crypto;
66pub mod error;
67mod memory;
68mod op_access;
69mod pred;
70mod repeat;
71mod sets;
72mod stack;
73mod total_control_flow;
74
75pub fn check_predicate(predicate: &[ConstraintBytecode], access: Access) -> CheckResult<()> {
85 use rayon::{iter::Either, prelude::*};
86 let (failed, unsatisfied): (Vec<_>, Vec<_>) = predicate
87 .par_iter()
88 .map(|bytecode| eval_bytecode_iter(bytecode.iter().copied(), access))
89 .enumerate()
90 .filter_map(|(i, constraint_res)| match constraint_res {
91 Err(err) => Some(Either::Left((i, err))),
92 Ok(b) if !b => Some(Either::Right(i)),
93 _ => None,
94 })
95 .partition_map(|either| either);
96 if !failed.is_empty() {
97 return Err(ConstraintErrors(failed).into());
98 }
99 if !unsatisfied.is_empty() {
100 return Err(ConstraintsUnsatisfied(unsatisfied).into());
101 }
102 Ok(())
103}
104
105pub fn eval_bytecode(bytes: &BytecodeMapped<Op>, access: Access) -> ConstraintResult<bool> {
109 eval(bytes, access)
110}
111
112pub fn eval_bytecode_iter<I>(bytes: I, access: Access) -> ConstraintResult<bool>
117where
118 I: IntoIterator<Item = u8>,
119{
120 eval(BytecodeMappedLazy::new(bytes), access)
121}
122
123pub fn eval_ops(ops: &[Op], access: Access) -> ConstraintResult<bool> {
127 eval(ops, access)
128}
129
130pub fn eval<OA>(op_access: OA, access: Access) -> ConstraintResult<bool>
134where
135 OA: OpAccess<Op = Op>,
136 OA::Error: Into<error::OpError>,
137{
138 let stack = exec(op_access, access)?;
139 let word = match stack.last() {
140 Some(&w) => w,
141 None => return Err(ConstraintError::InvalidEvaluation(stack)),
142 };
143 bool_from_word(word).ok_or_else(|| ConstraintError::InvalidEvaluation(stack))
144}
145
146pub fn exec_bytecode(bytes: &BytecodeMapped<Op>, access: Access) -> ConstraintResult<Stack> {
148 exec(bytes, access)
149}
150
151pub fn exec_bytecode_iter<I>(bytes: I, access: Access) -> ConstraintResult<Stack>
156where
157 I: IntoIterator<Item = u8>,
158{
159 exec(BytecodeMappedLazy::new(bytes), access)
160}
161
162pub fn exec_ops(ops: &[Op], access: Access) -> ConstraintResult<Stack> {
164 exec(ops, access)
165}
166
167pub fn exec<OA>(mut op_access: OA, access: Access) -> ConstraintResult<Stack>
169where
170 OA: OpAccess<Op = Op>,
171 OA::Error: Into<error::OpError>,
172{
173 let mut pc = 0;
174 let mut stack = Stack::default();
175 let mut memory = Memory::new();
176 let mut repeat = Repeat::new();
177 let cache = LazyCache::new();
178 while let Some(res) = op_access.op_access(pc) {
179 let op = res.map_err(|err| ConstraintError::Op(pc, err.into()))?;
180
181 let res = step_op(access, op, &mut stack, &mut memory, pc, &mut repeat, &cache);
182
183 #[cfg(feature = "tracing")]
184 trace_op_res(pc, &op, &stack, &memory, res.as_ref());
185
186 let update = match res {
187 Ok(update) => update,
188 Err(err) => return Err(ConstraintError::Op(pc, err)),
189 };
190
191 match update {
192 Some(ProgramControlFlow::Pc(new_pc)) => pc = new_pc,
193 Some(ProgramControlFlow::Halt) => break,
194 None => pc += 1,
195 }
196 }
197 Ok(stack)
198}
199
200#[cfg(feature = "tracing")]
206fn trace_op_res<T, E>(pc: usize, op: &Op, stack: &Stack, memory: &Memory, op_res: Result<T, E>)
207where
208 E: core::fmt::Display,
209{
210 let pc_op = format!("0x{pc:02X}: {op:?}");
211 match op_res {
212 Ok(_) => {
213 tracing::trace!("{pc_op}\n ├── {:?}\n └── {:?}", &stack, &memory)
214 }
215 Err(ref err) => {
216 tracing::trace!("{pc_op}");
217 tracing::debug!("{err}");
218 }
219 }
220}
221
222pub fn step_op(
224 access: Access,
225 op: Op,
226 stack: &mut Stack,
227 memory: &mut Memory,
228 pc: usize,
229 repeat: &mut Repeat,
230 cache: &LazyCache,
231) -> OpResult<Option<ProgramControlFlow>> {
232 match op {
233 Op::Access(op) => step_op_access(access, op, stack, repeat, cache).map(|_| None),
234 Op::Alu(op) => step_op_alu(op, stack).map(|_| None),
235 Op::Crypto(op) => step_op_crypto(op, stack).map(|_| None),
236 Op::Pred(op) => step_op_pred(op, stack).map(|_| None),
237 Op::Stack(op) => step_op_stack(op, pc, stack, repeat),
238 Op::TotalControlFlow(op) => step_on_total_control_flow(op, stack, pc),
239 Op::Temporary(op) => step_on_temporary(op, stack, memory).map(|_| None),
240 }
241}
242
243pub fn step_op_access(
245 access: Access,
246 op: asm::Access,
247 stack: &mut Stack,
248 repeat: &mut Repeat,
249 cache: &LazyCache,
250) -> OpResult<()> {
251 match op {
252 asm::Access::DecisionVar => {
253 access::decision_var(&access.solution.this_data().decision_variables, stack)
254 }
255 asm::Access::DecisionVarLen => {
256 access::decision_var_len(&access.solution.this_data().decision_variables, stack)
257 }
258 asm::Access::MutKeys => access::push_mut_keys(access.solution, stack),
259 asm::Access::State => access::state(access.state_slots, stack),
260 asm::Access::StateLen => access::state_len(access.state_slots, stack),
261 asm::Access::ThisAddress => access::this_address(access.solution.this_data(), stack),
262 asm::Access::ThisContractAddress => {
263 access::this_contract_address(access.solution.this_data(), stack)
264 }
265 asm::Access::RepeatCounter => access::repeat_counter(stack, repeat),
266 asm::Access::NumSlots => access::num_slots(
267 stack,
268 &access.state_slots,
269 &access.solution.this_data().decision_variables,
270 ),
271 asm::Access::PredicateExists => {
272 access::predicate_exists(stack, access.solution.data, cache)
273 }
274 }
275}
276
277pub fn step_op_alu(op: asm::Alu, stack: &mut Stack) -> OpResult<()> {
279 match op {
280 asm::Alu::Add => stack.pop2_push1(alu::add),
281 asm::Alu::Sub => stack.pop2_push1(alu::sub),
282 asm::Alu::Mul => stack.pop2_push1(alu::mul),
283 asm::Alu::Div => stack.pop2_push1(alu::div),
284 asm::Alu::Mod => stack.pop2_push1(alu::mod_),
285 asm::Alu::Shl => stack.pop2_push1(alu::shl),
286 asm::Alu::Shr => stack.pop2_push1(alu::shr),
287 asm::Alu::ShrI => stack.pop2_push1(alu::arithmetic_shr),
288 }
289}
290
291pub fn step_op_crypto(op: asm::Crypto, stack: &mut Stack) -> OpResult<()> {
293 match op {
294 asm::Crypto::Sha256 => crypto::sha256(stack),
295 asm::Crypto::VerifyEd25519 => crypto::verify_ed25519(stack),
296 asm::Crypto::RecoverSecp256k1 => crypto::recover_secp256k1(stack),
297 }
298}
299
300pub fn step_op_pred(op: asm::Pred, stack: &mut Stack) -> OpResult<()> {
302 match op {
303 asm::Pred::Eq => stack.pop2_push1(|a, b| Ok((a == b).into())),
304 asm::Pred::EqRange => pred::eq_range(stack),
305 asm::Pred::Gt => stack.pop2_push1(|a, b| Ok((a > b).into())),
306 asm::Pred::Lt => stack.pop2_push1(|a, b| Ok((a < b).into())),
307 asm::Pred::Gte => stack.pop2_push1(|a, b| Ok((a >= b).into())),
308 asm::Pred::Lte => stack.pop2_push1(|a, b| Ok((a <= b).into())),
309 asm::Pred::And => stack.pop2_push1(|a, b| Ok((a != 0 && b != 0).into())),
310 asm::Pred::Or => stack.pop2_push1(|a, b| Ok((a != 0 || b != 0).into())),
311 asm::Pred::Not => stack.pop1_push1(|a| Ok((a == 0).into())),
312 asm::Pred::EqSet => pred::eq_set(stack),
313 asm::Pred::BitAnd => stack.pop2_push1(|a, b| Ok(a & b)),
314 asm::Pred::BitOr => stack.pop2_push1(|a, b| Ok(a | b)),
315 }
316}
317
318pub fn step_op_stack(
320 op: asm::Stack,
321 pc: usize,
322 stack: &mut Stack,
323 repeat: &mut Repeat,
324) -> OpResult<Option<ProgramControlFlow>> {
325 if let asm::Stack::RepeatEnd = op {
326 return Ok(repeat.repeat()?.map(ProgramControlFlow::Pc));
327 }
328 let r = match op {
329 asm::Stack::Dup => stack.pop1_push2(|w| Ok([w, w])),
330 asm::Stack::DupFrom => stack.dup_from().map_err(From::from),
331 asm::Stack::Push(word) => stack.push(word).map_err(From::from),
332 asm::Stack::Pop => stack.pop().map(|_| ()).map_err(From::from),
333 asm::Stack::Swap => stack.pop2_push2(|a, b| Ok([b, a])),
334 asm::Stack::SwapIndex => stack.swap_index().map_err(From::from),
335 asm::Stack::Select => stack.select().map_err(From::from),
336 asm::Stack::SelectRange => stack.select_range().map_err(From::from),
337 asm::Stack::Repeat => repeat::repeat(pc, stack, repeat),
338 asm::Stack::Reserve => stack.reserve_zeroed().map_err(From::from),
339 asm::Stack::Load => stack.load().map_err(From::from),
340 asm::Stack::Store => stack.store().map_err(From::from),
341 asm::Stack::RepeatEnd => unreachable!(),
342 };
343 r.map(|_| None)
344}
345
346pub fn step_on_total_control_flow(
348 op: asm::TotalControlFlow,
349 stack: &mut Stack,
350 pc: usize,
351) -> OpResult<Option<ProgramControlFlow>> {
352 match op {
353 asm::TotalControlFlow::JumpForwardIf => total_control_flow::jump_forward_if(stack, pc),
354 asm::TotalControlFlow::HaltIf => total_control_flow::halt_if(stack),
355 asm::TotalControlFlow::Halt => Ok(Some(ProgramControlFlow::Halt)),
356 asm::TotalControlFlow::PanicIf => total_control_flow::panic_if(stack).map(|_| None),
357 }
358}
359
360pub fn step_on_temporary(
362 op: asm::Temporary,
363 stack: &mut Stack,
364 memory: &mut Memory,
365) -> OpResult<()> {
366 match op {
367 asm::Temporary::Alloc => {
368 let w = stack.pop()?;
369 let len = memory.len()?;
370 memory.alloc(w)?;
371 Ok(stack.push(len)?)
372 }
373 asm::Temporary::Store => {
374 let [addr, w] = stack.pop2()?;
375 memory.store(addr, w)
376 }
377 asm::Temporary::Load => stack.pop1_push1(|addr| memory.load(addr)),
378 asm::Temporary::Free => {
379 let addr = stack.pop()?;
380 memory.free(addr)
381 }
382 asm::Temporary::LoadRange => {
383 let [addr, size] = stack.pop2()?;
384 let words = memory.load_range(addr, size)?;
385 Ok(stack.extend(words)?)
386 }
387 asm::Temporary::StoreRange => {
388 let addr = stack.pop()?;
389 stack.pop_len_words(|words| memory.store_range(addr, words))?;
390 Ok(())
391 }
392 }
393}
394
395#[cfg(test)]
396pub(crate) mod test_util {
397 use std::collections::HashSet;
398
399 use asm::Word;
400
401 use crate::{
402 types::{solution::SolutionData, ContentAddress, PredicateAddress},
403 *,
404 };
405
406 pub(crate) const TEST_SET_CA: ContentAddress = ContentAddress([0xFF; 32]);
407 pub(crate) const TEST_PREDICATE_CA: ContentAddress = ContentAddress([0xAA; 32]);
408 pub(crate) const TEST_PREDICATE_ADDR: PredicateAddress = PredicateAddress {
409 contract: TEST_SET_CA,
410 predicate: TEST_PREDICATE_CA,
411 };
412 pub(crate) const TEST_SOLUTION_DATA: SolutionData = SolutionData {
413 predicate_to_solve: TEST_PREDICATE_ADDR,
414 decision_variables: vec![],
415 state_mutations: vec![],
416 };
417
418 pub(crate) fn test_empty_keys() -> &'static HashSet<&'static [Word]> {
419 static INSTANCE: std::sync::LazyLock<HashSet<&[Word]>> =
420 std::sync::LazyLock::new(|| HashSet::with_capacity(0));
421 &INSTANCE
422 }
423
424 pub(crate) fn test_solution_data_arr() -> &'static [SolutionData] {
425 static INSTANCE: std::sync::LazyLock<[SolutionData; 1]> =
426 std::sync::LazyLock::new(|| [TEST_SOLUTION_DATA]);
427 &*INSTANCE
428 }
429
430 pub(crate) fn test_solution_access() -> &'static SolutionAccess<'static> {
431 static INSTANCE: std::sync::LazyLock<SolutionAccess> =
432 std::sync::LazyLock::new(|| SolutionAccess {
433 data: test_solution_data_arr(),
434 index: 0,
435 mutable_keys: test_empty_keys(),
436 });
437 &INSTANCE
438 }
439
440 pub(crate) fn test_access() -> &'static Access<'static> {
441 static INSTANCE: std::sync::LazyLock<Access> = std::sync::LazyLock::new(|| Access {
442 solution: *test_solution_access(),
443 state_slots: StateSlots::EMPTY,
444 });
445 &INSTANCE
446 }
447}
448
449#[cfg(test)]
450mod pred_tests {
451 use crate::{
452 asm::{Pred, Stack},
453 test_util::*,
454 *,
455 };
456
457 #[test]
458 fn pred_eq_false() {
459 let ops = &[
460 Stack::Push(6).into(),
461 Stack::Push(7).into(),
462 Pred::Eq.into(),
463 ];
464 assert!(!eval_ops(ops, *test_access()).unwrap());
465 }
466
467 #[test]
468 fn pred_eq_true() {
469 let ops = &[
470 Stack::Push(42).into(),
471 Stack::Push(42).into(),
472 Pred::Eq.into(),
473 ];
474 assert!(eval_ops(ops, *test_access()).unwrap());
475 }
476
477 #[test]
478 fn pred_gt_false() {
479 let ops = &[
480 Stack::Push(7).into(),
481 Stack::Push(7).into(),
482 Pred::Gt.into(),
483 ];
484 assert!(!eval_ops(ops, *test_access()).unwrap());
485 }
486
487 #[test]
488 fn pred_gt_true() {
489 let ops = &[
490 Stack::Push(7).into(),
491 Stack::Push(6).into(),
492 Pred::Gt.into(),
493 ];
494 assert!(eval_ops(ops, *test_access()).unwrap());
495 }
496
497 #[test]
498 fn pred_lt_false() {
499 let ops = &[
500 Stack::Push(7).into(),
501 Stack::Push(7).into(),
502 Pred::Lt.into(),
503 ];
504 assert!(!eval_ops(ops, *test_access()).unwrap());
505 }
506
507 #[test]
508 fn pred_lt_true() {
509 let ops = &[
510 Stack::Push(6).into(),
511 Stack::Push(7).into(),
512 Pred::Lt.into(),
513 ];
514 assert!(eval_ops(ops, *test_access()).unwrap());
515 }
516
517 #[test]
518 fn pred_gte_false() {
519 let ops = &[
520 Stack::Push(6).into(),
521 Stack::Push(7).into(),
522 Pred::Gte.into(),
523 ];
524 assert!(!eval_ops(ops, *test_access()).unwrap());
525 }
526
527 #[test]
528 fn pred_gte_true() {
529 let ops = &[
530 Stack::Push(7).into(),
531 Stack::Push(7).into(),
532 Pred::Gte.into(),
533 ];
534 assert!(eval_ops(ops, *test_access()).unwrap());
535 let ops = &[
536 Stack::Push(8).into(),
537 Stack::Push(7).into(),
538 Pred::Gte.into(),
539 ];
540 assert!(eval_ops(ops, *test_access()).unwrap());
541 }
542
543 #[test]
544 fn pred_lte_false() {
545 let ops = &[
546 Stack::Push(7).into(),
547 Stack::Push(6).into(),
548 Pred::Lte.into(),
549 ];
550 assert!(!eval_ops(ops, *test_access()).unwrap());
551 }
552
553 #[test]
554 fn pred_lte_true() {
555 let ops = &[
556 Stack::Push(7).into(),
557 Stack::Push(7).into(),
558 Pred::Lte.into(),
559 ];
560 assert!(eval_ops(ops, *test_access()).unwrap());
561 let ops = &[
562 Stack::Push(7).into(),
563 Stack::Push(8).into(),
564 Pred::Lte.into(),
565 ];
566 assert!(eval_ops(ops, *test_access()).unwrap());
567 }
568
569 #[test]
570 fn pred_and_true() {
571 let ops = &[
572 Stack::Push(42).into(),
573 Stack::Push(42).into(),
574 Pred::And.into(),
575 ];
576 assert!(eval_ops(ops, *test_access()).unwrap());
577 }
578
579 #[test]
580 fn pred_and_false() {
581 let ops = &[
582 Stack::Push(42).into(),
583 Stack::Push(0).into(),
584 Pred::And.into(),
585 ];
586 assert!(!eval_ops(ops, *test_access()).unwrap());
587 let ops = &[
588 Stack::Push(0).into(),
589 Stack::Push(0).into(),
590 Pred::And.into(),
591 ];
592 assert!(!eval_ops(ops, *test_access()).unwrap());
593 }
594
595 #[test]
596 fn pred_or_true() {
597 let ops = &[
598 Stack::Push(42).into(),
599 Stack::Push(42).into(),
600 Pred::Or.into(),
601 ];
602 assert!(eval_ops(ops, *test_access()).unwrap());
603 let ops = &[
604 Stack::Push(0).into(),
605 Stack::Push(42).into(),
606 Pred::Or.into(),
607 ];
608 assert!(eval_ops(ops, *test_access()).unwrap());
609 let ops = &[
610 Stack::Push(42).into(),
611 Stack::Push(0).into(),
612 Pred::Or.into(),
613 ];
614 assert!(eval_ops(ops, *test_access()).unwrap());
615 }
616
617 #[test]
618 fn pred_or_false() {
619 let ops = &[
620 Stack::Push(0).into(),
621 Stack::Push(0).into(),
622 Pred::Or.into(),
623 ];
624 assert!(!eval_ops(ops, *test_access()).unwrap());
625 }
626
627 #[test]
628 fn pred_not_true() {
629 let ops = &[Stack::Push(0).into(), Pred::Not.into()];
630 assert!(eval_ops(ops, *test_access()).unwrap());
631 }
632
633 #[test]
634 fn pred_not_false() {
635 let ops = &[Stack::Push(42).into(), Pred::Not.into()];
636 assert!(!eval_ops(ops, *test_access()).unwrap());
637 }
638}