simplicity/policy/
satisfy.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use crate::analysis::Cost;
4use crate::jet::Elements;
5use crate::node::{ConstructNode, Hiding, RedeemNode};
6use crate::policy::ToXOnlyPubkey;
7use crate::types;
8use crate::{Cmr, Policy, Value};
9
10use elements::bitcoin;
11use elements::locktime::Height;
12use elements::taproot::TapLeafHash;
13use hashes::Hash;
14
15use crate::jet::elements::ElementsEnv;
16use std::convert::TryFrom;
17use std::sync::Arc;
18
19/// Type alias for 32-byte preimage.
20pub type Preimage32 = [u8; 32];
21
22/// Lookup table for signatures, hash preimages, etc.
23///
24/// Every method has a default implementation that simply returns `None`
25/// on every query. Users are expected to override the methods that they
26/// have data for.
27pub trait Satisfier<Pk: ToXOnlyPubkey> {
28    /// Given a public key, look up a Schnorr signature with that key.
29    fn lookup_tap_leaf_script_sig(&self, _: &Pk, _: &TapLeafHash) -> Option<elements::SchnorrSig> {
30        None
31    }
32
33    /// Given a SHA256 hash, look up its preimage.
34    fn lookup_sha256(&self, _: &Pk::Sha256) -> Option<Preimage32> {
35        None
36    }
37
38    /// Assert that a relative lock time is satisfied.
39    fn check_older(&self, _: elements::Sequence) -> bool {
40        false
41    }
42
43    /// Assert that an absolute lock time is satisfied.
44    fn check_after(&self, _: elements::LockTime) -> bool {
45        false
46    }
47
48    /// Given a CMR, look up a matching assembly program.
49    ///
50    /// ## Successful execution
51    ///
52    /// It is the **responsibility of the implementor** to ensure that the returned assembly
53    /// program has sufficient witness data to successfully run on the Bit Machine.
54    ///
55    /// The execution of a program depends on the transaction environment,
56    /// so implementations should compute witness data based on that.
57    ///
58    /// If the assembly program fails to run for the current transaction environment,
59    /// then implementations should return `None`.
60    fn lookup_asm_program(&self, _: Cmr) -> Option<Arc<ConstructNode<Elements>>> {
61        None
62    }
63}
64
65impl<Pk: ToXOnlyPubkey> Satisfier<Pk> for elements::Sequence {
66    fn check_older(&self, n: elements::Sequence) -> bool {
67        use elements::bitcoin::locktime::relative::LockTime::*;
68
69        let this = match bitcoin::Sequence(self.0).to_relative_lock_time() {
70            Some(x) => x,
71            None => return false,
72        };
73        let n = match bitcoin::Sequence(n.0).to_relative_lock_time() {
74            Some(x) => x,
75            None => return false,
76        };
77
78        match (n, this) {
79            (Blocks(n), Blocks(lock_time)) => n <= lock_time,
80            (Time(n), Time(lock_time)) => n <= lock_time,
81            _ => false, // Not the same units
82        }
83    }
84}
85
86impl<Pk: ToXOnlyPubkey> Satisfier<Pk> for elements::LockTime {
87    fn check_after(&self, n: elements::LockTime) -> bool {
88        use elements::LockTime::*;
89
90        match (n, *self) {
91            (Blocks(n), Blocks(lock_time)) => n <= lock_time,
92            (Seconds(n), Seconds(lock_time)) => n <= lock_time,
93            _ => false, // Not the same units.
94        }
95    }
96}
97
98#[derive(Debug)]
99pub enum SatisfierError {
100    /// A satisfying witness for the policy could not be produced.
101    Unsatisfiable,
102    /// An assembly program failed to run on the Bit Machine.
103    ///
104    /// This can happen when the [`Satisfier::lookup_asm_program`] method is incorrectly implemented.
105    AssemblyFailed(crate::bit_machine::ExecutionError),
106}
107
108type SatResult = Hiding<Arc<ConstructNode<Elements>>>;
109
110fn ok_if(condition: bool, expr: SatResult) -> SatResult {
111    match condition {
112        true => expr,
113        false => expr.hide(),
114    }
115}
116
117impl<Pk: ToXOnlyPubkey> Policy<Pk> {
118    fn satisfy_internal<S: Satisfier<Pk>>(
119        &self,
120        inference_context: &types::Context,
121        satisfier: &S,
122    ) -> Result<SatResult, SatisfierError> {
123        let node: SatResult = match *self {
124            Policy::Unsatisfiable(entropy) => {
125                super::serialize::unsatisfiable::<SatResult>(inference_context, entropy).hide()
126            }
127            Policy::Trivial => super::serialize::trivial(inference_context),
128            Policy::Key(ref key) => {
129                let signature = satisfier
130                    .lookup_tap_leaf_script_sig(key, &TapLeafHash::all_zeros())
131                    .map(|sig| sig.sig.serialize())
132                    .map(Value::u512);
133                ok_if(
134                    signature.is_some(),
135                    super::serialize::key(inference_context, key, signature),
136                )
137            }
138            Policy::After(n) => {
139                let height = Height::from_consensus(n).expect("timelock is valid");
140                ok_if(
141                    satisfier.check_after(elements::LockTime::Blocks(height)),
142                    super::serialize::after(inference_context, n),
143                )
144            }
145            Policy::Older(n) => ok_if(
146                satisfier.check_older(elements::Sequence(n.into())),
147                super::serialize::older(inference_context, n),
148            ),
149            Policy::Sha256(ref hash) => {
150                let preimage = satisfier.lookup_sha256(hash).map(Value::u256);
151                ok_if(
152                    preimage.is_some(),
153                    super::serialize::sha256::<Pk, _, _>(inference_context, hash, preimage),
154                )
155            }
156            Policy::And {
157                ref left,
158                ref right,
159            } => {
160                let left_res = left.satisfy_internal(inference_context, satisfier)?;
161                let right_res = right.satisfy_internal(inference_context, satisfier)?;
162                super::serialize::and(&left_res, &right_res)
163            }
164            Policy::Or {
165                ref left,
166                ref right,
167            } => {
168                let left_res = left.satisfy_internal(inference_context, satisfier)?;
169                let right_res = right.satisfy_internal(inference_context, satisfier)?;
170                let take_right = match (left_res.as_node(), right_res.as_node()) {
171                    (Some(left), Some(right)) => {
172                        let left_cost = left
173                            .finalize_unpruned()
174                            .expect("serialization should be sound")
175                            .bounds()
176                            .cost;
177                        let right_cost = right
178                            .finalize_unpruned()
179                            .expect("serialization should be sound")
180                            .bounds()
181                            .cost;
182                        right_cost < left_cost
183                    }
184                    (None, Some(..)) => true,
185                    (Some(..), None) => false,
186                    // If both children are unsatisfiable, then the choice doesn't matter.
187                    // The entire expression will be pruned out.
188                    (None, None) => false,
189                };
190
191                let ret = if take_right {
192                    super::serialize::or(&left_res, &right_res, Some(Value::u1(1)))
193                } else {
194                    super::serialize::or(&left_res, &right_res, Some(Value::u1(0)))
195                };
196                ok_if(
197                    left_res.as_node().is_some() || right_res.as_node().is_some(),
198                    ret,
199                )
200            }
201            Policy::Threshold(k, ref subs) => {
202                let subs_res: Vec<SatResult> = subs
203                    .iter()
204                    .map(|sub| sub.satisfy_internal(inference_context, satisfier))
205                    .collect::<Result<_, SatisfierError>>()?;
206                let costs: Vec<Cost> = subs_res
207                    .iter()
208                    .map(|result| match result.as_node() {
209                        Some(node) => node
210                            .finalize_unpruned()
211                            .map(|redeem| redeem.bounds().cost)
212                            .unwrap_or(Cost::CONSENSUS_MAX),
213                        None => Cost::CONSENSUS_MAX,
214                    })
215                    .collect();
216                let selected_node_indices = {
217                    let mut indices: Vec<usize> = (0..costs.len()).collect();
218                    indices.sort_by_key(|&i| costs[i]);
219                    indices.truncate(k);
220                    indices
221                };
222                let all_selected_ok = selected_node_indices
223                    .iter()
224                    .all(|&i| subs_res[i].as_node().is_some());
225                let witness_bits: Vec<Option<Value>> = (0..subs_res.len())
226                    .map(|i| Some(Value::u1(u8::from(selected_node_indices.contains(&i)))))
227                    .collect();
228                let k = u32::try_from(k).expect("k should be less than 2^32");
229                ok_if(
230                    all_selected_ok,
231                    super::serialize::threshold(k, &subs_res, &witness_bits),
232                )
233            }
234            Policy::Assembly(cmr) => match satisfier.lookup_asm_program(cmr) {
235                Some(program) => Hiding::from(program),
236                None => Hiding::hidden(cmr, inference_context.shallow_clone()),
237            },
238        };
239        Ok(node)
240    }
241
242    /// Return the policy program with satisfying witness data.
243    ///
244    /// The program is run on the Bit Machine for pruning,
245    /// so the transaction environment needs to be provided.
246    pub fn satisfy<S: Satisfier<Pk>>(
247        &self,
248        satisfier: &S,
249        env: &ElementsEnv<Arc<elements::Transaction>>,
250    ) -> Result<Arc<RedeemNode<Elements>>, SatisfierError> {
251        let result = self.satisfy_internal(&types::Context::new(), satisfier)?;
252        match result.get_node() {
253            Some(program) => program
254                .finalize_unpruned()
255                .expect("serialization should be sound")
256                .prune(env)
257                .map_err(SatisfierError::AssemblyFailed), // execution fails iff assembly fragment fails
258            None => Err(SatisfierError::Unsatisfiable),
259        }
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266    use crate::bit_encoding::BitCollector;
267    use crate::dag::{DagLike, NoSharing};
268    use crate::jet::elements::ElementsEnv;
269    use crate::node::{CoreConstructible, JetConstructible, SimpleFinalizer, WitnessConstructible};
270    use crate::policy::serialize;
271    use crate::{BitMachine, FailEntropy, SimplicityKey};
272    use elements::bitcoin::key::{Keypair, XOnlyPublicKey};
273    use elements::secp256k1_zkp;
274    use hashes::{sha256, Hash};
275    use std::collections::HashMap;
276    use std::sync::Arc;
277
278    pub struct PolicySatisfier<'a, Pk: SimplicityKey> {
279        pub preimages: HashMap<Pk::Sha256, Preimage32>,
280        pub signatures: HashMap<Pk, elements::SchnorrSig>,
281        pub assembly: HashMap<Cmr, Arc<ConstructNode<Elements>>>,
282        pub tx: &'a elements::Transaction,
283        pub index: usize,
284    }
285
286    impl<Pk: ToXOnlyPubkey> Satisfier<Pk> for PolicySatisfier<'_, Pk> {
287        fn lookup_tap_leaf_script_sig(
288            &self,
289            pk: &Pk,
290            _: &TapLeafHash,
291        ) -> Option<elements::SchnorrSig> {
292            self.signatures.get(pk).copied()
293        }
294
295        fn lookup_sha256(&self, hash: &Pk::Sha256) -> Option<Preimage32> {
296            self.preimages.get(hash).copied()
297        }
298
299        fn check_older(&self, sequence: elements::Sequence) -> bool {
300            let self_sequence = self.tx.input[self.index].sequence;
301            <elements::Sequence as Satisfier<Pk>>::check_older(&self_sequence, sequence)
302        }
303
304        fn check_after(&self, locktime: elements::LockTime) -> bool {
305            <elements::LockTime as Satisfier<Pk>>::check_after(&self.tx.lock_time, locktime)
306        }
307
308        fn lookup_asm_program(&self, cmr: Cmr) -> Option<Arc<ConstructNode<Elements>>> {
309            self.assembly.get(&cmr).cloned()
310        }
311    }
312
313    fn get_satisfier(
314        env: &ElementsEnv<Arc<elements::Transaction>>,
315    ) -> PolicySatisfier<XOnlyPublicKey> {
316        let mut preimages = HashMap::new();
317
318        for i in 0..3 {
319            let preimage = [i; 32];
320            preimages.insert(sha256::Hash::hash(&preimage), preimage);
321        }
322
323        let secp = secp256k1_zkp::Secp256k1::new();
324        let mut rng = secp256k1_zkp::rand::rngs::ThreadRng::default();
325        let mut signatures = HashMap::new();
326
327        for _ in 0..3 {
328            let keypair = Keypair::new(&secp, &mut rng);
329            let xonly = keypair.x_only_public_key().0;
330
331            let sighash = env.c_tx_env().sighash_all();
332            let msg = secp256k1_zkp::Message::from_digest(sighash.to_byte_array());
333            let sig = elements::SchnorrSig {
334                sig: keypair.sign_schnorr(msg),
335                hash_ty: elements::SchnorrSighashType::All,
336            };
337
338            signatures.insert(xonly, sig);
339        }
340
341        PolicySatisfier {
342            preimages,
343            signatures,
344            assembly: HashMap::new(),
345            tx: env.tx(),
346            index: env.ix() as usize,
347        }
348    }
349
350    fn execute_successful(
351        program: Arc<RedeemNode<Elements>>,
352        env: &ElementsEnv<Arc<elements::Transaction>>,
353    ) {
354        let mut mac = BitMachine::for_program(&program).unwrap();
355        assert!(mac.exec(&program, env).is_ok());
356    }
357
358    fn execute_unsuccessful(
359        program: Arc<RedeemNode<Elements>>,
360        env: &ElementsEnv<Arc<elements::Transaction>>,
361    ) {
362        let mut mac = BitMachine::for_program(&program).unwrap();
363        assert!(mac.exec(&program, env).is_err());
364    }
365
366    fn to_witness(program: &RedeemNode<Elements>) -> Vec<&Value> {
367        program
368            .post_order_iter::<NoSharing>()
369            .into_witnesses()
370            .collect()
371    }
372
373    #[test]
374    fn satisfy_unsatisfiable() {
375        let env = ElementsEnv::dummy();
376        let satisfier = get_satisfier(&env);
377        let policy = Policy::Unsatisfiable(FailEntropy::ZERO);
378
379        assert!(policy.satisfy(&satisfier, &env).is_err());
380
381        let commit = policy.commit().expect("no asm");
382        let program = commit
383            .finalize(&mut SimpleFinalizer::new(std::iter::empty()))
384            .expect("finalize");
385
386        execute_unsuccessful(program, &env);
387    }
388
389    #[test]
390    fn satisfy_trivial() {
391        let env = ElementsEnv::dummy();
392        let satisfier = get_satisfier(&env);
393        let policy = Policy::Trivial;
394
395        let program = policy.satisfy(&satisfier, &env).expect("satisfiable");
396        let witness = to_witness(&program);
397        assert!(witness.is_empty());
398
399        execute_successful(program, &env);
400    }
401
402    #[test]
403    fn satisfy_pk() {
404        let env = ElementsEnv::dummy();
405        let satisfier = get_satisfier(&env);
406        let mut it = satisfier.signatures.keys();
407        let xonly = it.next().unwrap();
408        let policy = Policy::Key(*xonly);
409
410        let program = policy.satisfy(&satisfier, &env).expect("satisfiable");
411        let witness = to_witness(&program);
412        assert_eq!(1, witness.len());
413
414        let sighash = env.c_tx_env().sighash_all();
415        let message = secp256k1_zkp::Message::from_digest(sighash.to_byte_array());
416        let signature_bytes = witness[0]
417            .iter_padded()
418            .try_collect_bytes()
419            .expect("to bytes");
420        let signature =
421            secp256k1_zkp::schnorr::Signature::from_slice(&signature_bytes).expect("to signature");
422        assert!(signature.verify(&message, xonly).is_ok());
423
424        execute_successful(program, &env);
425    }
426
427    #[test]
428    fn satisfy_sha256() {
429        let env = ElementsEnv::dummy();
430        let satisfier = get_satisfier(&env);
431        let mut it = satisfier.preimages.keys();
432        let image = *it.next().unwrap();
433        let policy = Policy::Sha256(image);
434
435        let program = policy.satisfy(&satisfier, &env).expect("satisfiable");
436        let witness = to_witness(&program);
437        assert_eq!(1, witness.len());
438
439        let witness_bytes = witness[0]
440            .iter_padded()
441            .try_collect_bytes()
442            .expect("to bytes");
443        let witness_preimage = Preimage32::try_from(witness_bytes.as_slice()).expect("to array");
444        let preimage = *satisfier.preimages.get(&image).unwrap();
445        assert_eq!(preimage, witness_preimage);
446
447        execute_successful(program, &env);
448    }
449
450    #[test]
451    fn satisfy_after() {
452        let height = Height::from_consensus(42).unwrap();
453        let env =
454            ElementsEnv::dummy_with(elements::LockTime::Blocks(height), elements::Sequence::ZERO);
455        let satisfier = get_satisfier(&env);
456
457        let policy0 = Policy::After(41);
458        let program = policy0.satisfy(&satisfier, &env).expect("satisfiable");
459        let witness = to_witness(&program);
460        assert!(witness.is_empty());
461        execute_successful(program, &env);
462
463        let policy1 = Policy::After(42);
464        let program = policy1.satisfy(&satisfier, &env).expect("satisfiable");
465        let witness = to_witness(&program);
466        assert!(witness.is_empty());
467        execute_successful(program, &env);
468
469        let policy2 = Policy::After(43);
470        assert!(policy2.satisfy(&satisfier, &env).is_err(), "unsatisfiable");
471    }
472
473    #[test]
474    fn satisfy_older() {
475        let env = ElementsEnv::dummy_with(
476            elements::LockTime::ZERO,
477            elements::Sequence::from_consensus(42),
478        );
479        let satisfier = get_satisfier(&env);
480
481        let policy0 = Policy::Older(41);
482        let program = policy0.satisfy(&satisfier, &env).expect("satisfiable");
483        let witness = to_witness(&program);
484        assert!(witness.is_empty());
485        execute_successful(program, &env);
486
487        let policy1 = Policy::Older(42);
488        let program = policy1.satisfy(&satisfier, &env).expect("satisfiable");
489        let witness = to_witness(&program);
490        assert!(witness.is_empty());
491        execute_successful(program, &env);
492
493        let policy2 = Policy::Older(43);
494        assert!(policy2.satisfy(&satisfier, &env).is_err(), "unsatisfiable");
495    }
496
497    #[test]
498    fn satisfy_and() {
499        let env = ElementsEnv::dummy();
500        let satisfier = get_satisfier(&env);
501        let images: Vec<_> = satisfier.preimages.keys().copied().collect();
502        let preimages: Vec<_> = images
503            .iter()
504            .map(|x| satisfier.preimages.get(x).unwrap())
505            .collect();
506
507        // Policy 0
508
509        let policy0 = Policy::And {
510            left: Arc::new(Policy::Sha256(images[0])),
511            right: Arc::new(Policy::Sha256(images[1])),
512        };
513        let program = policy0.satisfy(&satisfier, &env).expect("satisfiable");
514        let witness = to_witness(&program);
515        assert_eq!(2, witness.len());
516
517        for i in 0..2 {
518            let preimage_bytes = witness[i]
519                .iter_padded()
520                .try_collect_bytes()
521                .expect("to bytes");
522            let witness_preimage =
523                Preimage32::try_from(preimage_bytes.as_slice()).expect("to array");
524            assert_eq!(preimages[i], &witness_preimage);
525        }
526
527        execute_successful(program, &env);
528
529        // Policy 1
530
531        let policy1 = Policy::And {
532            left: Arc::new(Policy::Sha256(sha256::Hash::from_byte_array([0; 32]))),
533            right: Arc::new(Policy::Sha256(images[1])),
534        };
535        assert!(policy1.satisfy(&satisfier, &env).is_err());
536
537        // Policy 2
538
539        let policy2 = Policy::And {
540            left: Arc::new(Policy::Sha256(images[0])),
541            right: Arc::new(Policy::Sha256(sha256::Hash::from_byte_array([0; 32]))),
542        };
543        assert!(policy2.satisfy(&satisfier, &env).is_err());
544    }
545
546    #[test]
547    fn satisfy_or() {
548        let env = ElementsEnv::dummy();
549        let satisfier = get_satisfier(&env);
550        let images: Vec<_> = satisfier.preimages.keys().copied().collect();
551        let preimages: Vec<_> = images.iter().map(|x| satisfier.preimages[x]).collect();
552
553        let assert_branch = |policy: &Policy<XOnlyPublicKey>, bit: bool| {
554            let program = policy.satisfy(&satisfier, &env).expect("satisfiable");
555            let witness = to_witness(&program);
556            assert_eq!(2, witness.len());
557
558            assert_eq!(Value::u1(bit as u8), *witness[0]);
559            let preimage_bytes = witness[1]
560                .iter_padded()
561                .try_collect_bytes()
562                .expect("to bytes");
563            let witness_preimage =
564                Preimage32::try_from(preimage_bytes.as_slice()).expect("to array");
565            assert_eq!(preimages[bit as usize], witness_preimage);
566
567            execute_successful(program, &env);
568        };
569
570        // Policy 0
571
572        let policy0 = Policy::Or {
573            left: Arc::new(Policy::Sha256(images[0])),
574            right: Arc::new(Policy::Sha256(images[1])),
575        };
576        assert_branch(&policy0, false);
577
578        // Policy 1
579
580        let policy1 = Policy::Or {
581            left: Arc::new(Policy::Sha256(images[0])),
582            right: Arc::new(Policy::Sha256(sha256::Hash::from_byte_array([1; 32]))),
583        };
584        assert_branch(&policy1, false);
585
586        // Policy 2
587
588        let policy2 = Policy::Or {
589            left: Arc::new(Policy::Sha256(sha256::Hash::from_byte_array([0; 32]))),
590            right: Arc::new(Policy::Sha256(images[1])),
591        };
592        assert_branch(&policy2, true);
593
594        // Policy 3
595
596        let policy3 = Policy::Or {
597            left: Arc::new(Policy::Sha256(sha256::Hash::from_byte_array([0; 32]))),
598            right: Arc::new(Policy::Sha256(sha256::Hash::from_byte_array([1; 32]))),
599        };
600        assert!(policy3.satisfy(&satisfier, &env).is_err());
601    }
602
603    #[test]
604    fn satisfy_thresh() {
605        let env = ElementsEnv::dummy();
606        let satisfier = get_satisfier(&env);
607        let images: Vec<_> = satisfier.preimages.keys().copied().collect();
608        let preimages: Vec<_> = images
609            .iter()
610            .map(|x| satisfier.preimages.get(x).unwrap())
611            .collect();
612
613        let assert_branches = |policy: &Policy<XOnlyPublicKey>, bits: &[bool]| {
614            let program = policy.satisfy(&satisfier, &env).expect("satisfiable");
615            let witness = to_witness(&program);
616            assert_eq!(
617                witness.len(),
618                bits.len() + bits.iter().filter(|b| **b).count(),
619            );
620
621            let mut witidx = 0;
622            for (bit_n, bit) in bits.iter().copied().enumerate() {
623                assert_eq!(*witness[witidx], Value::u1(bit.into()));
624                witidx += 1;
625                if bit {
626                    let preimage_bytes = witness[witidx]
627                        .iter_padded()
628                        .try_collect_bytes()
629                        .expect("to bytes");
630                    let witness_preimage =
631                        Preimage32::try_from(preimage_bytes.as_slice()).expect("to array");
632                    assert_eq!(preimages[bit_n], &witness_preimage);
633                    witidx += 1;
634                }
635            }
636
637            execute_successful(program, &env);
638        };
639
640        let image_from_bit = |bit: bool, j: u8| {
641            if bit {
642                images[j as usize]
643            } else {
644                sha256::Hash::from_byte_array([j; 32])
645            }
646        };
647
648        for &bit0 in &[true, false] {
649            let image0 = image_from_bit(bit0, 0);
650
651            for &bit1 in &[true, false] {
652                let image1 = image_from_bit(bit1, 1);
653
654                for &bit2 in &[true, false] {
655                    let image2 = image_from_bit(bit2, 2);
656
657                    let policy = Policy::Threshold(
658                        2,
659                        vec![
660                            Policy::Sha256(image0),
661                            Policy::Sha256(image1),
662                            Policy::Sha256(image2),
663                        ],
664                    );
665
666                    match bit0 as u8 + bit1 as u8 + bit2 as u8 {
667                        3 => assert_branches(&policy, &[bit0, bit1, false]),
668                        2 => assert_branches(&policy, &[bit0, bit1, bit2]),
669                        _ => assert!(policy.satisfy(&satisfier, &env).is_err()),
670                    }
671                }
672            }
673        }
674    }
675
676    #[test]
677    fn satisfy_asm() {
678        let ctx = types::Context::new();
679        let env = ElementsEnv::dummy();
680        let mut satisfier = get_satisfier(&env);
681
682        let mut assert_branch = |witness0: Value, witness1: Value| {
683            let asm_program = serialize::verify_bexp(
684                &Arc::<ConstructNode<Elements>>::pair(
685                    &Arc::<ConstructNode<Elements>>::witness(&ctx, Some(witness0.clone())),
686                    &Arc::<ConstructNode<Elements>>::witness(&ctx, Some(witness1.clone())),
687                )
688                .expect("sound types"),
689                &Arc::<ConstructNode<Elements>>::jet(&ctx, Elements::Eq8),
690            );
691            let cmr = asm_program.cmr();
692            satisfier.assembly.insert(cmr, asm_program);
693
694            let policy = Policy::Assembly(cmr);
695            let result = policy.satisfy(&satisfier, &env);
696
697            if witness0 == witness1 {
698                let program = result.expect("policy should be satisfiable");
699                let witness = to_witness(&program);
700
701                assert_eq!(2, witness.len());
702                assert_eq!(&witness0, witness[0]);
703                assert_eq!(&witness1, witness[1]);
704
705                execute_successful(program, &env);
706            } else {
707                assert!(matches!(result, Err(SatisfierError::AssemblyFailed(..))));
708            }
709        };
710
711        for a in 0..2 {
712            for b in 0..2 {
713                assert_branch(Value::u8(a), Value::u8(b))
714            }
715        }
716    }
717}