1use crate::{
4 types::{
5 predicate::Predicate,
6 solution::{Solution, SolutionIndex, SolutionSet},
7 Key, PredicateAddress, Word,
8 },
9 vm::{
10 self,
11 asm::{self, FromBytesError},
12 Access, Gas, GasLimit, Memory, Stack,
13 },
14};
15#[cfg(feature = "tracing")]
16use essential_hash::content_addr;
17use essential_types::{predicate::Program, ContentAddress, Value};
18use essential_vm::{StateRead, StateReads};
19use std::{
20 collections::{BTreeMap, HashMap, HashSet},
21 fmt,
22 sync::Arc,
23};
24use thiserror::Error;
25
26use rayon::prelude::*;
27
28#[cfg(test)]
29mod tests;
30
31#[cfg(test)]
32mod test_graph_ops;
33
34#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
36pub struct CheckPredicateConfig {
37 pub collect_all_failures: bool,
44}
45
46pub trait GetPredicate {
48 fn get_predicate(&self, addr: &PredicateAddress) -> Arc<Predicate>;
55}
56
57pub trait GetProgram {
59 fn get_program(&self, ca: &ContentAddress) -> Arc<Program>;
67}
68
69#[derive(Debug)]
70pub struct Ctx<'a> {
72 pub run_mode: RunMode,
74 pub cache: &'a mut Cache,
76}
77
78pub type Cache = HashMap<u16, Arc<(Stack, Memory)>>;
80
81struct ProgramCtx {
83 parents: Vec<Arc<(Stack, Memory)>>,
85 leaf: bool,
87}
88
89#[derive(Debug, PartialEq)]
91pub struct Outputs {
92 pub gas: Gas,
94 pub data: Vec<DataFromSolution>,
96}
97
98#[derive(Debug, PartialEq)]
100pub struct DataFromSolution {
101 pub solution_index: SolutionIndex,
103 pub data: Vec<DataOutput>,
105}
106
107#[derive(Debug, PartialEq)]
109enum ProgramOutput {
110 Satisfied(bool),
113 DataOutput(DataOutput),
115}
116
117#[derive(Debug, PartialEq)]
119pub enum DataOutput {
120 Memory(Memory),
122}
123
124enum Output {
127 Leaf(ProgramOutput),
129 Parent(Arc<(Stack, Memory)>),
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
135pub enum RunMode {
136 #[default]
138 Outputs,
139 Checks,
141}
142
143#[derive(Debug, Error)]
145pub enum InvalidSolutionSet {
146 #[error("invalid solution: {0}")]
148 Solution(#[from] InvalidSolution),
149 #[error("state mutations validation failed: {0}")]
151 StateMutations(#[from] InvalidSetStateMutations),
152}
153
154#[derive(Debug, Error)]
156pub enum InvalidSolution {
157 #[error("must be at least one solution")]
159 Empty,
160 #[error("the number of solutions ({0}) exceeds the limit ({MAX_SOLUTIONS})")]
162 TooMany(usize),
163 #[error("solution {0}'s predicate data length exceeded {1} (limit: {MAX_PREDICATE_DATA})")]
165 PredicateDataLenExceeded(usize, usize),
166 #[error("Invalid state mutation entry: {0}")]
168 StateMutationEntry(KvError),
169 #[error("Predicate data value len {0} exceeds limit {MAX_VALUE_SIZE}")]
171 PredDataValueTooLarge(usize),
172}
173
174#[derive(Debug, Error)]
176pub enum KvError {
177 #[error("key with length {0} exceeds limit {MAX_KEY_SIZE}")]
179 KeyTooLarge(usize),
180 #[error("value with length {0} exceeds limit {MAX_VALUE_SIZE}")]
182 ValueTooLarge(usize),
183}
184
185#[derive(Debug, Error)]
187pub enum InvalidSetStateMutations {
188 #[error("the number of state mutations ({0}) exceeds the limit ({MAX_STATE_MUTATIONS})")]
190 TooMany(usize),
191 #[error("attempt to apply multiple mutations to the same slot: {0:?} {1:?}")]
193 MultipleMutationsForSlot(PredicateAddress, Key),
194}
195
196#[derive(Debug, Error)]
198pub enum PredicatesError<E> {
199 #[error("{0}")]
201 Failed(#[from] PredicateErrors<E>),
202 #[error("summing solution gas overflowed")]
204 GasOverflowed,
205 #[error("tried to compute mutations on solution set with existing mutations")]
207 ExistingMutations,
208}
209
210#[derive(Debug, Error)]
212pub struct PredicateErrors<E>(pub Vec<(SolutionIndex, PredicateError<E>)>);
213
214#[derive(Debug, Error)]
216pub enum PredicateError<E> {
217 #[error("failed to retrieve edges for node {0} indicating an invalid graph")]
219 InvalidNodeEdges(usize),
220 #[error("one or more program execution errors occurred: {0}")]
222 ProgramErrors(#[from] ProgramErrors<E>),
223 #[error("one or more constraints unsatisfied: {0}")]
225 ConstraintsUnsatisfied(#[from] ConstraintsUnsatisfied),
226 #[error(transparent)]
228 Mutations(#[from] MutationsError),
229}
230
231#[derive(Debug, Error)]
233pub struct ProgramErrors<E>(Vec<(usize, ProgramError<E>)>);
234
235#[derive(Debug, Error)]
237pub enum ProgramError<E> {
238 #[error("failed to parse an op during bytecode mapping: {0}")]
240 OpsFromBytesError(#[from] FromBytesError),
241 #[error("concatenating parent program `Stack`s caused an overflow: {0}")]
243 ParentStackConcatOverflow(#[from] vm::error::StackError),
244 #[error("concatenating parent program `Memory` slices caused an overflow: {0}")]
246 ParentMemoryConcatOverflow(#[from] vm::error::MemoryError),
247 #[error("VM execution error: {0}")]
249 Vm(#[from] vm::error::ExecError<E>),
250}
251
252#[derive(Debug, Error)]
254pub struct ConstraintsUnsatisfied(pub Vec<usize>);
255
256#[derive(Debug, Error)]
258pub enum MutationsError {
259 #[error("duplicate mutations for the same key: {0:?}")]
261 DuplicateMutations(Key),
262 #[error(transparent)]
264 DecodeError(#[from] essential_types::solution::decode::MutationDecodeError),
265}
266
267pub const MAX_PREDICATE_DATA: u32 = 100;
269pub const MAX_SOLUTIONS: usize = 100;
271pub const MAX_STATE_MUTATIONS: usize = 1000;
273pub const MAX_VALUE_SIZE: usize = 10_000;
275pub const MAX_KEY_SIZE: usize = 1000;
277
278impl<E: fmt::Display> fmt::Display for PredicateErrors<E> {
279 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280 f.write_str("predicate checking failed for one or more solutions:\n")?;
281 for (ix, err) in &self.0 {
282 f.write_str(&format!(" {ix}: {err}\n"))?;
283 }
284 Ok(())
285 }
286}
287
288impl<E: fmt::Display> fmt::Display for ProgramErrors<E> {
289 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290 f.write_str("the programs at the following node indices failed: \n")?;
291 for (node_ix, err) in &self.0 {
292 f.write_str(&format!(" {node_ix}: {err}\n"))?;
293 }
294 Ok(())
295 }
296}
297
298impl fmt::Display for ConstraintsUnsatisfied {
299 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
300 f.write_str("the constraints at the following indices returned false: \n")?;
301 for ix in &self.0 {
302 f.write_str(&format!(" {ix}\n"))?;
303 }
304 Ok(())
305 }
306}
307
308impl<F> GetPredicate for F
309where
310 F: Fn(&PredicateAddress) -> Arc<Predicate>,
311{
312 fn get_predicate(&self, addr: &PredicateAddress) -> Arc<Predicate> {
313 (*self)(addr)
314 }
315}
316
317impl<F> GetProgram for F
318where
319 F: Fn(&ContentAddress) -> Arc<Program>,
320{
321 fn get_program(&self, ca: &ContentAddress) -> Arc<Program> {
322 (*self)(ca)
323 }
324}
325
326impl GetPredicate for HashMap<PredicateAddress, Arc<Predicate>> {
327 fn get_predicate(&self, addr: &PredicateAddress) -> Arc<Predicate> {
328 self[addr].clone()
329 }
330}
331
332impl GetProgram for HashMap<ContentAddress, Arc<Program>> {
333 fn get_program(&self, ca: &ContentAddress) -> Arc<Program> {
334 self[ca].clone()
335 }
336}
337
338impl<T: GetPredicate> GetPredicate for Arc<T> {
339 fn get_predicate(&self, addr: &PredicateAddress) -> Arc<Predicate> {
340 (**self).get_predicate(addr)
341 }
342}
343
344impl<T: GetProgram> GetProgram for Arc<T> {
345 fn get_program(&self, ca: &ContentAddress) -> Arc<Program> {
346 (**self).get_program(ca)
347 }
348}
349
350#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, fields(solution = %content_addr(set)), err))]
355pub fn check_set(set: &SolutionSet) -> Result<(), InvalidSolutionSet> {
356 check_solutions(&set.solutions)?;
357 check_set_state_mutations(set)?;
358 Ok(())
359}
360
361fn check_value_size(value: &[Word]) -> Result<(), KvError> {
362 if value.len() > MAX_VALUE_SIZE {
363 Err(KvError::ValueTooLarge(value.len()))
364 } else {
365 Ok(())
366 }
367}
368
369fn check_key_size(value: &[Word]) -> Result<(), KvError> {
370 if value.len() > MAX_KEY_SIZE {
371 Err(KvError::KeyTooLarge(value.len()))
372 } else {
373 Ok(())
374 }
375}
376
377pub fn check_solutions(solutions: &[Solution]) -> Result<(), InvalidSolution> {
379 if solutions.is_empty() {
382 return Err(InvalidSolution::Empty);
383 }
384 if solutions.len() > MAX_SOLUTIONS {
386 return Err(InvalidSolution::TooMany(solutions.len()));
387 }
388
389 for (solution_ix, solution) in solutions.iter().enumerate() {
391 if solution.predicate_data.len() > MAX_PREDICATE_DATA as usize {
393 return Err(InvalidSolution::PredicateDataLenExceeded(
394 solution_ix,
395 solution.predicate_data.len(),
396 ));
397 }
398 for v in &solution.predicate_data {
399 check_value_size(v).map_err(|_| InvalidSolution::PredDataValueTooLarge(v.len()))?;
400 }
401 }
402 Ok(())
403}
404
405pub fn check_set_state_mutations(set: &SolutionSet) -> Result<(), InvalidSolutionSet> {
407 if set.state_mutations_len() > MAX_STATE_MUTATIONS {
410 return Err(InvalidSetStateMutations::TooMany(set.state_mutations_len()).into());
411 }
412
413 for solution in &set.solutions {
415 let mut mut_keys = HashSet::new();
416 for mutation in &solution.state_mutations {
417 if !mut_keys.insert(&mutation.key) {
418 return Err(InvalidSetStateMutations::MultipleMutationsForSlot(
419 solution.predicate_to_solve.clone(),
420 mutation.key.clone(),
421 )
422 .into());
423 }
424 check_key_size(&mutation.key).map_err(InvalidSolution::StateMutationEntry)?;
426 check_value_size(&mutation.value).map_err(InvalidSolution::StateMutationEntry)?;
428 }
429 }
430
431 Ok(())
432}
433
434fn decode_mutations<E>(
435 outputs: Outputs,
436 mut set: SolutionSet,
437) -> Result<SolutionSet, PredicatesError<E>> {
438 for output in outputs.data {
440 let s = &mut set.solutions[output.solution_index as usize];
443
444 let mut mut_set = HashSet::new();
446
447 for data in output.data {
449 match data {
450 DataOutput::Memory(memory) => {
451 for mutation in essential_types::solution::decode::decode_mutations(&memory)
452 .map_err(|e| {
453 PredicatesError::Failed(PredicateErrors(vec![(
454 output.solution_index,
455 PredicateError::Mutations(MutationsError::DecodeError(e)),
456 )]))
457 })?
458 {
459 if !mut_set.insert(mutation.key.clone()) {
461 return Err(PredicatesError::Failed(PredicateErrors(vec![(
462 output.solution_index,
463 PredicateError::Mutations(MutationsError::DuplicateMutations(
464 mutation.key.clone(),
465 )),
466 )])));
467 }
468
469 s.state_mutations.push(mutation);
471 }
472 }
473 }
474 }
475 }
476 Ok(set)
477}
478
479#[derive(Debug, Default)]
481struct PostState {
482 state: HashMap<ContentAddress, HashMap<Key, Value>>,
484}
485
486#[derive(Debug, Default)]
489struct PostStateArc<E>(Arc<PostState>, std::marker::PhantomData<E>);
490
491impl<E> Clone for PostStateArc<E> {
492 fn clone(&self) -> Self {
493 Self(self.0.clone(), Default::default())
494 }
495}
496
497impl<E> StateRead for PostStateArc<E>
498where
499 E: std::fmt::Display + std::fmt::Debug,
500{
501 type Error = E;
502
503 fn key_range(
504 &self,
505 contract_addr: ContentAddress,
506 mut key: Key,
507 num_values: usize,
508 ) -> Result<Vec<Vec<essential_types::Word>>, Self::Error> {
509 let out = self
510 .0
511 .state
512 .get(&contract_addr)
513 .map(|state| {
514 let mut values = Vec::with_capacity(num_values);
515 for _ in 0..num_values {
516 let Some(value) = state.get(&key) else {
517 return values;
518 };
519 values.push(value.clone());
520 let Some(k) = next_key(key) else {
521 return values;
522 };
523 key = k;
524 }
525 values
526 })
527 .unwrap_or_default();
528 Ok(out)
529 }
530}
531
532fn next_key(mut key: Key) -> Option<Key> {
534 for w in key.iter_mut().rev() {
535 match *w {
536 Word::MAX => *w = Word::MIN,
537 _ => {
538 *w += 1;
539 return Some(key);
540 }
541 }
542 }
543 None
544}
545
546#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
553pub fn check_and_compute_solution_set_two_pass<S>(
554 state: &S,
555 solution_set: SolutionSet,
556 get_predicate: impl GetPredicate + Sync + Clone,
557 get_program: impl 'static + Clone + GetProgram + Send + Sync,
558 config: Arc<CheckPredicateConfig>,
559) -> Result<(Gas, SolutionSet), PredicatesError<S::Error>>
560where
561 S: Clone + StateRead + Send + Sync + 'static,
562 S::Error: Send + Sync + 'static,
563{
564 let post_state = PostStateArc::<S::Error>(Arc::new(PostState::default()), Default::default());
566
567 let mut cache = HashMap::new();
569
570 let (mut gas, solution_set) = check_and_compute_solution_set(
572 &(state.clone(), post_state.clone()),
573 solution_set,
574 get_predicate.clone(),
575 get_program.clone(),
576 config.clone(),
577 RunMode::Outputs,
578 &mut cache,
579 )?;
580
581 let mut post_state =
583 Arc::try_unwrap(post_state.0).expect("post state should have one reference");
584
585 for solution in &solution_set.solutions {
587 for mutation in &solution.state_mutations {
588 post_state
589 .state
590 .entry(solution.predicate_to_solve.contract.clone())
591 .or_default()
592 .insert(mutation.key.clone(), mutation.value.clone());
593 }
594 }
595
596 let post_state = PostStateArc(Arc::new(post_state), Default::default());
598
599 let (g, solution_set) = check_and_compute_solution_set(
601 &(state.clone(), post_state.clone()),
602 solution_set,
603 get_predicate,
604 get_program,
605 config,
606 RunMode::Checks,
607 &mut cache,
608 )?;
609
610 gas = gas.saturating_add(g);
612
613 Ok((gas, solution_set))
615}
616
617#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
620pub fn check_and_compute_solution_set<S>(
621 state: &S,
622 solution_set: SolutionSet,
623 get_predicate: impl GetPredicate + Sync,
624 get_program: impl 'static + Clone + GetProgram + Send + Sync,
625 config: Arc<CheckPredicateConfig>,
626 run_mode: RunMode,
627 cache: &mut HashMap<SolutionIndex, Cache>,
628) -> Result<(Gas, SolutionSet), PredicatesError<S::Error>>
629where
630 S: Clone + StateReads + Send + Sync + 'static,
631 S::Error: Send,
632{
633 let set = Arc::new(solution_set);
635 let outputs = check_set_predicates(
636 state,
637 set.clone(),
638 get_predicate,
639 get_program,
640 config,
641 run_mode,
642 cache,
643 )?;
644
645 let set = Arc::try_unwrap(set).expect("set should have one reference");
647
648 let gas = outputs.gas;
650
651 let set = decode_mutations(outputs, set)?;
652
653 Ok((gas, set))
654}
655
656pub fn check_set_predicates<S>(
673 state: &S,
674 solution_set: Arc<SolutionSet>,
675 get_predicate: impl GetPredicate + Sync,
676 get_program: impl 'static + Clone + GetProgram + Send + Sync,
677 config: Arc<CheckPredicateConfig>,
678 run_mode: RunMode,
679 cache: &mut HashMap<SolutionIndex, Cache>,
680) -> Result<Outputs, PredicatesError<S::Error>>
681where
682 S: Clone + StateReads + Send + Sync + 'static,
683 S::Error: Send,
684{
685 #[cfg(feature = "tracing")]
686 tracing::trace!("{}", essential_hash::content_addr(&*solution_set));
687
688 let caches: Vec<_> = (0..solution_set.solutions.len())
689 .map(|i| {
690 let cache = cache.entry(i as u16).or_default();
691 core::mem::take(cache)
692 })
693 .collect();
694 let (ok, failed): (Vec<_>, Vec<_>) = solution_set
696 .solutions
697 .par_iter()
698 .zip(caches)
699 .enumerate()
700 .map(|(solution_index, (solution, mut cache))| {
701 let predicate = get_predicate.get_predicate(&solution.predicate_to_solve);
702 let solution_set = solution_set.clone();
703 let state = state.clone();
704 let config = config.clone();
705 let get_program = get_program.clone();
706
707 let res = check_predicate(
708 &state,
709 solution_set,
710 predicate,
711 get_program,
712 solution_index
713 .try_into()
714 .expect("solution index already validated"),
715 &config,
716 Ctx {
717 run_mode,
718 cache: &mut cache,
719 },
720 );
721
722 match res {
723 Ok(ok) => Ok((solution_index as u16, ok, cache)),
724 Err(e) => Err((solution_index as u16, e)),
725 }
726 })
727 .partition(Result::is_ok);
728
729 if !failed.is_empty() {
731 return Err(PredicateErrors(failed.into_iter().map(Result::unwrap_err).collect()).into());
732 }
733
734 let mut total_gas: u64 = 0;
736 let outputs = ok
737 .into_iter()
738 .map(Result::unwrap)
739 .map(|(solution_index, (gas, data_outputs), c)| {
740 let output = DataFromSolution {
741 solution_index,
742 data: data_outputs,
743 };
744 total_gas = total_gas.saturating_add(gas);
745 *cache.get_mut(&solution_index).expect("cache should exist") = c;
746 output
747 })
748 .collect();
749
750 Ok(Outputs {
751 gas: total_gas,
752 data: outputs,
753 })
754}
755
756pub fn check_predicate<S>(
774 state: &S,
775 solution_set: Arc<SolutionSet>,
776 predicate: Arc<Predicate>,
777 get_program: impl GetProgram + Send + Sync + 'static,
778 solution_index: SolutionIndex,
779 config: &CheckPredicateConfig,
780 ctx: Ctx,
781) -> Result<(Gas, Vec<DataOutput>), PredicateError<S::Error>>
782where
783 S: Clone + StateReads + Send + Sync + 'static,
784 S::Error: Send,
785{
786 let p = predicate.clone();
787
788 let run = |ix: u16, parents: Vec<Arc<(Stack, Memory)>>| {
790 let program = get_program.get_program(&predicate.nodes[ix as usize].program_address);
791 let ctx = ProgramCtx {
792 parents,
793 leaf: predicate
794 .node_edges(ix as usize)
795 .expect("This is already checked")
796 .is_empty(),
797 };
798 let res = run_program(
799 state.clone(),
800 solution_set.clone(),
801 solution_index,
802 program,
803 ctx,
804 );
805 (ix, res)
806 };
807
808 check_predicate_inner(run, p, config, &get_program, ctx)
809}
810
811fn create_parent_map<E>(
813 predicate: &Predicate,
814) -> Result<BTreeMap<u16, Vec<u16>>, PredicateError<E>> {
815 let mut nodes: BTreeMap<u16, Vec<u16>> = BTreeMap::new();
816 for node_ix in 0..predicate.nodes.len() {
818 nodes.entry(node_ix as u16).or_default();
820
821 for edge in predicate
823 .node_edges(node_ix)
824 .ok_or_else(|| PredicateError::InvalidNodeEdges(node_ix))?
825 {
826 nodes.entry(*edge).or_default().push(node_ix as u16);
828 }
829 }
830 Ok(nodes)
831}
832
833fn in_degrees(num_nodes: usize, parent_map: &BTreeMap<u16, Vec<u16>>) -> BTreeMap<u16, usize> {
834 let mut in_degrees = BTreeMap::new();
835 for node in 0..num_nodes {
836 in_degrees.insert(
837 node as u16,
838 parent_map.get(&(node as u16)).map_or(0, |v| v.len()),
839 );
840 }
841
842 in_degrees
843}
844
845fn reduce_in_degrees(in_degrees: &mut BTreeMap<u16, usize>, children: &[u16]) {
846 for child in children {
847 if let Some(in_degree) = in_degrees.get_mut(child) {
848 *in_degree = in_degree.saturating_sub(1);
849 }
850 }
851}
852
853fn find_nodes_with_no_parents(in_degrees: &BTreeMap<u16, usize>) -> Vec<u16> {
854 in_degrees
855 .iter()
856 .filter_map(
857 |(node, in_degree)| {
858 if *in_degree == 0 {
859 Some(*node)
860 } else {
861 None
862 }
863 },
864 )
865 .collect()
866}
867
868fn parallel_topo_sort<E>(
888 predicate: &Predicate,
889 parent_map: &BTreeMap<u16, Vec<u16>>,
890) -> Result<Vec<Vec<u16>>, PredicateError<E>> {
891 let mut in_degrees = in_degrees(predicate.nodes.len(), parent_map);
892
893 let mut out = Vec::new();
894 while !in_degrees.is_empty() {
895 let current_level = find_nodes_with_no_parents(&in_degrees);
896 if current_level.is_empty() {
897 return Err(PredicateError::InvalidNodeEdges(0));
900 }
901
902 out.push(current_level.clone());
903
904 for node in current_level {
905 let children = predicate
906 .node_edges(node as usize)
907 .ok_or_else(|| PredicateError::InvalidNodeEdges(node as usize))?;
908 reduce_in_degrees(&mut in_degrees, children);
909 in_degrees.remove(&node);
910 }
911 }
912
913 Ok(out)
914}
915
916fn find_deferred<F>(predicate: &Predicate, is_deferred: F) -> HashSet<u16>
917where
918 F: Fn(&essential_types::predicate::Node) -> bool,
919{
920 let mut deferred = HashSet::new();
921 for (ix, node) in predicate.nodes.iter().enumerate() {
922 if is_deferred(node) {
923 deferred.insert(ix as u16);
924 }
925 if deferred.contains(&(ix as u16)) {
926 for child in predicate.node_edges(ix).expect("Already checked") {
927 deferred.insert(*child);
928 }
929 }
930 }
931 deferred
932}
933
934fn should_cache(node: u16, predicate: &Predicate, deferred: &HashSet<u16>) -> bool {
935 !deferred.contains(&node)
936 && predicate
937 .node_edges(node as usize)
938 .expect("Already checked")
939 .iter()
940 .any(|child| deferred.contains(child))
941}
942
943fn remove_deferred(nodes: Vec<Vec<u16>>, deferred: &HashSet<u16>) -> Vec<Vec<u16>> {
944 nodes
945 .into_iter()
946 .map(|level| {
947 level
948 .into_iter()
949 .filter(|node| !deferred.contains(node))
950 .collect::<Vec<_>>()
951 })
952 .filter(|level| !level.is_empty())
953 .collect()
954}
955
956fn remove_not_deferred(nodes: Vec<Vec<u16>>, deferred: &HashSet<u16>) -> Vec<Vec<u16>> {
957 nodes
958 .into_iter()
959 .map(|level| {
960 level
961 .into_iter()
962 .filter(|node| deferred.contains(node))
963 .collect::<Vec<_>>()
964 })
965 .filter(|level| !level.is_empty())
966 .collect()
967}
968
969fn check_predicate_inner<F, E>(
975 run: F,
976 predicate: Arc<Predicate>,
977 config: &CheckPredicateConfig,
978 get_program: &(impl GetProgram + Send + Sync + 'static),
979 ctx: Ctx<'_>,
980) -> Result<(Gas, Vec<DataOutput>), PredicateError<E>>
981where
982 F: Fn(u16, Vec<Arc<(Stack, Memory)>>) -> (u16, Result<(Output, u64), ProgramError<E>>)
983 + Send
984 + Sync
985 + Copy,
986 E: Send,
987{
988 let Ctx { run_mode, cache } = ctx;
990
991 let parent_map = create_parent_map(&predicate)?;
993
994 let sorted_nodes = parallel_topo_sort(&predicate, &parent_map)?;
996
997 let deferred_filter = |node: &essential_types::predicate::Node| -> bool {
999 asm::effects::bytes_contains_any(
1000 &get_program.get_program(&node.program_address).0,
1001 asm::effects::Effects::PostKeyRange | asm::effects::Effects::PostKeyRangeExtern,
1002 )
1003 };
1004
1005 let deferred = find_deferred(&predicate, deferred_filter);
1007
1008 let sorted_nodes = match run_mode {
1010 RunMode::Outputs => remove_deferred(sorted_nodes, &deferred),
1011 RunMode::Checks => remove_not_deferred(sorted_nodes, &deferred),
1012 };
1013
1014 let mut local_cache = Cache::new();
1016
1017 let mut failed: Vec<(_, _)> = vec![];
1019 let mut total_gas: Gas = 0;
1020 let mut unsatisfied = Vec::new();
1021 let mut data_outputs = Vec::new();
1022
1023 for parallel_nodes in sorted_nodes {
1025 let outputs: BTreeMap<u16, Result<(Output, Gas), _>> =
1027 if parallel_nodes.len() == 1 || parallel_nodes.is_empty() {
1028 parallel_nodes
1029 .into_iter()
1030 .map(|ix| {
1031 let inputs = parent_map[&ix]
1034 .iter()
1035 .filter_map(|parent_ix| {
1036 cache
1037 .get(parent_ix)
1038 .cloned()
1039 .or_else(|| local_cache.get(parent_ix).cloned())
1040 })
1041 .collect();
1042
1043 run(ix, inputs)
1045 })
1046 .collect()
1047 } else {
1048 parallel_nodes
1049 .into_par_iter()
1050 .map(|ix| {
1051 let inputs = parent_map[&ix]
1054 .iter()
1055 .filter_map(|parent_ix| {
1056 cache
1057 .get(parent_ix)
1058 .cloned()
1059 .or_else(|| local_cache.get(parent_ix).cloned())
1060 })
1061 .collect();
1062
1063 run(ix, inputs)
1065 })
1066 .collect()
1067 };
1068 for (node, res) in outputs {
1069 match res {
1070 Ok((Output::Parent(o), gas)) => {
1071 if should_cache(node, &predicate, &deferred) {
1073 cache.insert(node, o.clone());
1074 } else {
1075 local_cache.insert(node, o.clone());
1076 }
1077
1078 total_gas = total_gas.saturating_add(gas);
1080 }
1081 Ok((Output::Leaf(o), gas)) => {
1082 match o {
1083 ProgramOutput::Satisfied(false) => {
1084 unsatisfied.push(node as usize);
1085 }
1086 ProgramOutput::Satisfied(true) => {
1087 }
1089 ProgramOutput::DataOutput(data_output) => {
1090 data_outputs.push(data_output);
1091 }
1092 }
1093
1094 total_gas = total_gas.saturating_add(gas);
1096 }
1097 Err(e) => {
1098 failed.push((node as usize, e));
1099
1100 if !config.collect_all_failures {
1101 return Err(ProgramErrors(failed).into());
1102 }
1103 }
1104 }
1105 }
1106 }
1107
1108 if !failed.is_empty() {
1110 return Err(ProgramErrors(failed).into());
1111 }
1112
1113 if !unsatisfied.is_empty() {
1115 return Err(ConstraintsUnsatisfied(unsatisfied).into());
1116 }
1117
1118 Ok((total_gas, data_outputs))
1119}
1120
1121fn run_program<S>(
1126 state: S,
1127 solution_set: Arc<SolutionSet>,
1128 solution_index: SolutionIndex,
1129 program: Arc<Program>,
1130 ctx: ProgramCtx,
1131) -> Result<(Output, Gas), ProgramError<S::Error>>
1132where
1133 S: StateReads,
1134{
1135 let ProgramCtx { parents, leaf } = ctx;
1136
1137 let ops = asm::from_bytes(program.0.iter().copied()).collect::<Result<Vec<_>, _>>()?;
1139
1140 let mut vm = vm::Vm::default();
1142
1143 for parent_result in parents {
1145 let (parent_stack, parent_memory) = Arc::unwrap_or_clone(parent_result);
1146 let mut stack: Vec<Word> = std::mem::take(&mut vm.stack).into();
1148 stack.append(&mut parent_stack.into());
1149 vm.stack = stack.try_into()?;
1150
1151 let mut memory: Vec<Word> = std::mem::take(&mut vm.memory).into();
1153 memory.append(&mut parent_memory.into());
1154 vm.memory = memory.try_into()?;
1155 }
1156
1157 let access = Access::new(Arc::new(solution_set.solutions.clone()), solution_index);
1159
1160 let gas_cost = |_: &asm::Op| 1;
1162 let gas_limit = GasLimit::UNLIMITED;
1163
1164 let gas_spent = vm.exec_ops(&ops, access, &state, &gas_cost, gas_limit)?;
1166
1167 let out = if leaf {
1168 match vm.stack[..] {
1169 [2] => Output::Leaf(ProgramOutput::DataOutput(DataOutput::Memory(vm.memory))),
1170 [1] => Output::Leaf(ProgramOutput::Satisfied(true)),
1171 _ => Output::Leaf(ProgramOutput::Satisfied(false)),
1172 }
1173 } else {
1174 let output = Arc::new((vm.stack, vm.memory));
1175 Output::Parent(output)
1176 };
1177
1178 Ok((out, gas_spent))
1179}