1use std::convert::TryFrom;
4use std::fmt;
5use std::str::FromStr;
6
7use bitcoin::hashes::{sha256, Hash};
8use elements::address::Payload;
9use elements::confidential::Asset;
10use elements::hex::{FromHex, ToHex};
11use elements::opcodes::all::*;
12use elements::{confidential, encode, script, Address, AddressParams};
13
14use super::index_ops::IdxExpr;
15use super::param::{ExtParamTranslator, TranslateExtParam};
16use super::{ArgFromStr, CovExtArgs, EvalError, ExtParam, FromTokenIterError, ParseableExt, TxEnv};
17use crate::expression::{FromTree, Tree};
18use crate::miniscript::context::ScriptContextError;
19use crate::miniscript::lex::{Token as Tk, TokenIter};
20use crate::miniscript::satisfy::{Satisfaction, Witness};
21use crate::miniscript::types::extra_props::{OpLimits, TimelockInfo};
22use crate::miniscript::types::{Base, Correctness, Dissat, ExtData, Input, Malleability};
23use crate::{
24 expression, interpreter, script_num_size, Error, ExtTranslator, Extension, Satisfier,
25 ToPublicKey, TranslateExt,
26};
27
28#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
34pub enum AssetExpr<T: ExtParam> {
35 Const(T),
39 CurrInputAsset,
42 Input(IdxExpr),
45 Output(IdxExpr),
48}
49
50#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
56pub enum ValueExpr<T: ExtParam> {
57 Const(T),
60 CurrInputValue,
63 Input(IdxExpr),
66 Output(IdxExpr),
69}
70
71#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
78pub enum SpkExpr<T: ExtParam> {
79 Const(T),
84 CurrInputSpk,
87 Input(IdxExpr),
90 Output(IdxExpr),
93}
94
95#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
100pub enum CovOps<T: ExtParam> {
101 IsExpAsset(AssetExpr<T>),
104 IsExpValue(ValueExpr<T>),
109 AssetEq(AssetExpr<T>, AssetExpr<T>),
112 ValueEq(ValueExpr<T>, ValueExpr<T>),
115 SpkEq(SpkExpr<T>, SpkExpr<T>),
119 CurrIndEq(usize),
122 IdxEq(IdxExpr, IdxExpr),
125}
126
127impl<T: ExtParam> AssetExpr<T> {
128 fn script_size(&self) -> usize {
130 match self {
131 AssetExpr::Const(_) => 33 + 1,
132 AssetExpr::CurrInputAsset => 2,
133 AssetExpr::Input(i) => i.script_size() + 1,
134 AssetExpr::Output(i) => i.script_size() + 1,
135 }
136 }
137
138 fn _translate_ext<Q, E, Ext>(&self, t: &mut Ext) -> Result<AssetExpr<Q>, E>
140 where
141 Ext: ExtParamTranslator<T, Q, E>,
142 Q: ExtParam,
143 {
144 let res = match self {
145 AssetExpr::Const(c) => AssetExpr::Const(t.ext(c)?),
146 AssetExpr::CurrInputAsset => AssetExpr::CurrInputAsset,
147 AssetExpr::Input(i) => AssetExpr::Input(i.clone()),
148 AssetExpr::Output(i) => AssetExpr::Output(i.clone()),
149 };
150 Ok(res)
151 }
152}
153
154impl<T: ExtParam> fmt::Display for AssetExpr<T> {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 match self {
157 AssetExpr::Const(asset) => write!(f, "{}", asset),
158 AssetExpr::CurrInputAsset => write!(f, "curr_inp_asset"),
159 AssetExpr::Input(i) => write!(f, "inp_asset({})", i),
160 AssetExpr::Output(i) => write!(f, "out_asset({})", i),
161 }
162 }
163}
164
165impl<T: ExtParam> fmt::Debug for AssetExpr<T> {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 match self {
168 AssetExpr::Const(asset) => write!(f, "{:?}", asset),
169 AssetExpr::CurrInputAsset => write!(f, "curr_inp_asset"),
170 AssetExpr::Input(i) => write!(f, "inp_asset({:?})", i),
171 AssetExpr::Output(i) => write!(f, "out_asset({:?})", i),
172 }
173 }
174}
175
176impl<T: ExtParam> ArgFromStr for AssetExpr<T> {
177 fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
178 let top = expression::Tree::from_str(s)?;
179 Self::from_tree_parent(&top, parent, pos)
180 }
181}
182
183impl<T: ExtParam> AssetExpr<T> {
184 fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
185 match (top.name, top.args.len()) {
186 ("curr_inp_asset", 0) => Ok(AssetExpr::CurrInputAsset),
187 ("inp_asset", 1) => expression::unary(top, AssetExpr::Input),
188 ("out_asset", 1) => expression::unary(top, AssetExpr::Output),
189 (asset, 0) => Ok(AssetExpr::Const(T::arg_from_str(asset, parent, pos)?)),
190 _ => Err(Error::Unexpected(format!(
191 "{}({} args) while parsing Extension",
192 top.name,
193 top.args.len(),
194 ))),
195 }
196 }
197}
198
199impl<T: ExtParam> ValueExpr<T> {
200 fn script_size(&self) -> usize {
202 match self {
203 ValueExpr::Const(_c) => 33 + 1, ValueExpr::CurrInputValue => 2,
205 ValueExpr::Input(i) => i.script_size() + 1,
206 ValueExpr::Output(i) => i.script_size() + 1,
207 }
208 }
209
210 fn _translate_ext<Q, E, Ext>(&self, t: &mut Ext) -> Result<ValueExpr<Q>, E>
212 where
213 Ext: ExtParamTranslator<T, Q, E>,
214 Q: ExtParam,
215 {
216 let res = match self {
217 ValueExpr::Const(c) => ValueExpr::Const(t.ext(c)?),
218 ValueExpr::CurrInputValue => ValueExpr::CurrInputValue,
219 ValueExpr::Input(i) => ValueExpr::Input(i.clone()),
220 ValueExpr::Output(i) => ValueExpr::Output(i.clone()),
221 };
222 Ok(res)
223 }
224}
225
226impl<T: ExtParam> fmt::Display for ValueExpr<T> {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 match self {
229 ValueExpr::Const(asset) => write!(f, "{}", asset),
230 ValueExpr::CurrInputValue => write!(f, "curr_inp_value"),
231 ValueExpr::Input(i) => write!(f, "inp_value({})", i),
232 ValueExpr::Output(i) => write!(f, "out_value({})", i),
233 }
234 }
235}
236
237impl<T: ExtParam> fmt::Debug for ValueExpr<T> {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 match self {
240 ValueExpr::Const(asset) => write!(f, "{:?}", asset),
241 ValueExpr::CurrInputValue => write!(f, "curr_inp_value"),
242 ValueExpr::Input(i) => write!(f, "inp_value({:?})", i),
243 ValueExpr::Output(i) => write!(f, "out_value({:?})", i),
244 }
245 }
246}
247
248impl<T: ExtParam> ArgFromStr for ValueExpr<T> {
249 fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
250 let top = expression::Tree::from_str(s)?;
251 Self::from_tree_parent(&top, parent, pos)
252 }
253}
254
255impl<T: ExtParam> ValueExpr<T> {
256 fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
257 match (top.name, top.args.len()) {
258 ("curr_inp_value", 0) => Ok(ValueExpr::CurrInputValue),
259 ("inp_value", 1) => expression::unary(top, ValueExpr::Input),
260 ("out_value", 1) => expression::unary(top, ValueExpr::Output),
261 (value, 0) => Ok(ValueExpr::Const(T::arg_from_str(value, parent, pos)?)),
262 _ => Err(Error::Unexpected(format!(
263 "{}({} args) while parsing Extension",
264 top.name,
265 top.args.len(),
266 ))),
267 }
268 }
269}
270
271impl<T: ExtParam> SpkExpr<T> {
272 fn script_size(&self) -> usize {
274 match self {
275 SpkExpr::Const(_c) => 32 + 1 + 1,
276 SpkExpr::CurrInputSpk => 2,
277 SpkExpr::Input(i) => i.script_size() + 1,
278 SpkExpr::Output(i) => i.script_size() + 1,
279 }
280 }
281
282 fn _translate_ext<Q, E, Ext>(&self, t: &mut Ext) -> Result<SpkExpr<Q>, E>
284 where
285 Ext: ExtParamTranslator<T, Q, E>,
286 Q: ExtParam,
287 {
288 let res = match self {
289 SpkExpr::Const(c) => SpkExpr::Const(t.ext(c)?),
290 SpkExpr::CurrInputSpk => SpkExpr::CurrInputSpk,
291 SpkExpr::Input(i) => SpkExpr::Input(i.clone()),
292 SpkExpr::Output(i) => SpkExpr::Output(i.clone()),
293 };
294 Ok(res)
295 }
296}
297
298impl<T: ExtParam> fmt::Display for SpkExpr<T> {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 match self {
301 SpkExpr::Const(asset) => write!(f, "{}", asset),
302 SpkExpr::CurrInputSpk => write!(f, "curr_inp_spk"),
303 SpkExpr::Input(i) => write!(f, "inp_spk({})", i),
304 SpkExpr::Output(i) => write!(f, "out_spk({})", i),
305 }
306 }
307}
308
309impl<T: ExtParam> fmt::Debug for SpkExpr<T> {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 match self {
312 SpkExpr::Const(asset) => write!(f, "{:?}", asset),
313 SpkExpr::CurrInputSpk => write!(f, "curr_inp_spk"),
314 SpkExpr::Input(i) => write!(f, "inp_spk({:?})", i),
315 SpkExpr::Output(i) => write!(f, "out_spk({:?})", i),
316 }
317 }
318}
319
320impl<T: ExtParam> ArgFromStr for SpkExpr<T> {
321 fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
322 let top = expression::Tree::from_str(s)?;
323 Self::from_tree_parent(&top, parent, pos)
324 }
325}
326
327impl<T: ExtParam> SpkExpr<T> {
328 fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
329 match (top.name, top.args.len()) {
330 ("curr_inp_spk", 0) => Ok(SpkExpr::CurrInputSpk),
331 ("inp_spk", 1) => expression::unary(top, SpkExpr::Input),
332 ("out_spk", 1) => expression::unary(top, SpkExpr::Output),
333 (asset, 0) => Ok(SpkExpr::Const(T::arg_from_str(asset, parent, pos)?)),
334 _ => Err(Error::Unexpected(format!(
335 "{}({} args) while parsing Extension",
336 top.name,
337 top.args.len(),
338 ))),
339 }
340 }
341}
342
343impl<T: ExtParam> fmt::Display for CovOps<T> {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 match self {
346 CovOps::IsExpAsset(a) => write!(f, "is_exp_asset({})", a),
347 CovOps::IsExpValue(v) => write!(f, "is_exp_value({})", v),
348 CovOps::AssetEq(a, b) => write!(f, "asset_eq({},{})", a, b),
349 CovOps::ValueEq(a, b) => write!(f, "value_eq({},{})", a, b),
350 CovOps::SpkEq(a, b) => write!(f, "spk_eq({},{})", a, b),
351 CovOps::CurrIndEq(i) => write!(f, "curr_idx_eq({})", i),
352 CovOps::IdxEq(a, b) => write!(f, "idx_eq({},{})", a, b),
353 }
354 }
355}
356
357impl<T: ExtParam> fmt::Debug for CovOps<T> {
358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359 match self {
360 CovOps::IsExpAsset(a) => write!(f, "is_exp_asset({:?})", a),
361 CovOps::IsExpValue(v) => write!(f, "is_exp_value({:?})", v),
362 CovOps::AssetEq(a, b) => write!(f, "asset_eq({:?},{:?})", a, b),
363 CovOps::ValueEq(a, b) => write!(f, "value_eq({:?},{:?})", a, b),
364 CovOps::SpkEq(a, b) => write!(f, "spk_eq({:?},{:?})", a, b),
365 CovOps::CurrIndEq(i) => write!(f, "curr_idx_eq({:?})", i),
366 CovOps::IdxEq(a, b) => write!(f, "idx_eq({},{})", a, b),
367 }
368 }
369}
370
371impl<T: ExtParam> FromStr for CovOps<T> {
372 type Err = Error;
373
374 fn from_str(s: &str) -> Result<Self, Self::Err> {
375 let top = expression::Tree::from_str(s)?;
376 Self::from_tree(&top)
377 }
378}
379
380impl<T: ExtParam> FromTree for CovOps<T> {
381 fn from_tree(top: &Tree<'_>) -> Result<Self, Error> {
382 match (top.name, top.args.len()) {
383 ("is_exp_asset", 1) => {
384 AssetExpr::from_tree_parent(&top.args[0], top.name, 0).map(CovOps::IsExpAsset)
385 }
386 ("is_exp_value", 1) => {
387 ValueExpr::from_tree_parent(&top.args[0], top.name, 0).map(CovOps::IsExpValue)
388 }
389 ("asset_eq", 2) => {
390 let l = AssetExpr::from_tree_parent(&top.args[0], top.name, 0)?;
391 let r = AssetExpr::from_tree_parent(&top.args[1], top.name, 1)?;
392 Ok(CovOps::AssetEq(l, r))
393 }
394 ("value_eq", 2) => {
395 let l = ValueExpr::from_tree_parent(&top.args[0], top.name, 0)?;
396 let r = ValueExpr::from_tree_parent(&top.args[1], top.name, 1)?;
397 Ok(CovOps::ValueEq(l, r))
398 }
399 ("spk_eq", 2) => {
400 let l = SpkExpr::from_tree_parent(&top.args[0], top.name, 0)?;
401 let r = SpkExpr::from_tree_parent(&top.args[1], top.name, 1)?;
402 Ok(CovOps::SpkEq(l, r))
403 }
404 ("curr_idx_eq", 1) => {
405 expression::terminal(&top.args[0], expression::parse_num::<usize>)
406 .map(CovOps::CurrIndEq)
407 }
408 ("idx_eq", 2) => {
409 let l = IdxExpr::from_tree(&top.args[0])?;
410 let r = IdxExpr::from_tree(&top.args[1])?;
411 Ok(CovOps::IdxEq(l, r))
412 }
413 _ => Err(Error::Unexpected(format!(
414 "{}({} args) while parsing Extension",
415 top.name,
416 top.args.len(),
417 ))),
418 }
419 }
420}
421
422impl<T: ExtParam> Extension for CovOps<T> {
423 fn corr_prop(&self) -> Correctness {
424 Correctness {
425 base: Base::B,
426 input: Input::Zero, dissatisfiable: false, unit: true,
429 }
430 }
431
432 fn mall_prop(&self) -> Malleability {
433 Malleability {
434 dissat: Dissat::None, safe: false, non_malleable: true, }
438 }
439
440 fn extra_prop(&self) -> ExtData {
441 ExtData {
442 pk_cost: self.script_size(), has_free_verify: matches!(self, CovOps::CurrIndEq(..)),
444 stack_elem_count_sat: Some(0),
445 stack_elem_count_dissat: Some(0),
446 max_sat_size: Some((0, 0)),
447 max_dissat_size: Some((0, 0)),
448 timelock_info: TimelockInfo::default(),
449 exec_stack_elem_count_sat: Some(4), exec_stack_elem_count_dissat: Some(4),
451 ops: OpLimits {
452 count: 0,
455 sat: Some(0),
456 nsat: Some(0),
457 },
458 }
459 }
460
461 fn script_size(&self) -> usize {
462 match self {
463 CovOps::IsExpAsset(a) => a.script_size() + 3,
464 CovOps::IsExpValue(v) => v.script_size() + 3,
465 CovOps::AssetEq(a, b) => a.script_size() + b.script_size() + 7,
466 CovOps::ValueEq(a, b) => a.script_size() + b.script_size() + 7,
467 CovOps::SpkEq(a, b) => a.script_size() + b.script_size() + 7,
468 CovOps::CurrIndEq(i) => script_num_size(*i) + 2,
469 CovOps::IdxEq(a, b) => a.script_size() + b.script_size() + 1,
470 }
471 }
472
473 fn from_name_tree(name: &str, children: &[Tree<'_>]) -> Result<Self, FromTokenIterError> {
474 let tree = Tree {
475 name,
476 args: children.to_vec(), };
479 Self::from_tree(&tree).map_err(|_| FromTokenIterError)
480 }
481
482 fn segwit_ctx_checks(&self) -> Result<(), ScriptContextError> {
483 Err(ScriptContextError::ExtensionError(
485 "Introspection opcodes only available in Taproot".to_string(),
486 ))
487 }
488}
489
490impl<PArg, QArg> TranslateExt<CovOps<PArg>, CovOps<QArg>> for CovOps<PArg>
491where
492 CovOps<PArg>: Extension,
493 CovOps<QArg>: Extension,
494 PArg: ExtParam,
495 QArg: ExtParam,
496{
497 type Output = CovOps<QArg>;
498
499 fn translate_ext<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
500 where
501 T: ExtTranslator<CovOps<PArg>, CovOps<QArg>, E>,
502 {
503 t.ext(self)
504 }
505}
506
507impl<T, PArg, QArg, E> ExtTranslator<CovOps<PArg>, CovOps<QArg>, E> for T
509where
510 T: ExtParamTranslator<PArg, QArg, E>,
511 PArg: ExtParam,
512 QArg: ExtParam,
513{
514 fn ext(&mut self, cov_ops: &CovOps<PArg>) -> Result<CovOps<QArg>, E> {
516 TranslateExtParam::translate_ext(cov_ops, self)
517 }
518}
519
520#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
523pub struct Spk(SpkInner);
524
525impl Spk {
526 pub fn new(s: elements::Script) -> Self {
528 Spk(SpkInner::Script(s))
529 }
530}
531
532#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
534pub enum SpkInner {
535 Script(elements::Script),
537 Hashed(sha256::Hash),
539}
540
541impl fmt::Display for Spk {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 match &self.0 {
544 SpkInner::Script(s) => write!(f, "{}", s.to_hex()),
545 SpkInner::Hashed(_h) => write!(f, "hashed_spk"), }
547 }
548}
549
550impl ArgFromStr for Spk {
551 fn arg_from_str(s: &str, parent: &str, _pos: usize) -> Result<Self, Error> {
552 if parent != "spk_eq" {
553 return Err(Error::Unexpected(
554 "spk expressions can only used in spk_eq".to_string(),
555 ));
556 }
557 let inner = elements::Script::from_hex(s).map_err(|e| Error::Unexpected(e.to_string()))?;
558 Ok(Spk::new(inner))
559 }
560}
561
562impl ArgFromStr for confidential::Asset {
563 fn arg_from_str(s: &str, parent: &str, _pos: usize) -> Result<Self, Error> {
564 if parent != "asset_eq" && parent != "is_exp_asset" {
565 return Err(Error::Unexpected(
566 "asset expressions only allowed inside asset_eq and is_exp_asset".to_string(),
567 ));
568 }
569 let asset_hex = Vec::<u8>::from_hex(s).map_err(|e| Error::Unexpected(e.to_string()))?;
570 elements::encode::deserialize(&asset_hex).map_err(|e| Error::Unexpected(e.to_string()))
571 }
572}
573
574impl ArgFromStr for confidential::Value {
575 fn arg_from_str(s: &str, parent: &str, _pos: usize) -> Result<Self, Error> {
576 if parent != "value_eq" && parent != "is_exp_value" {
577 return Err(Error::Unexpected(
578 "value expressions only allowed inside value_eq and is_exp_value".to_string(),
579 ));
580 }
581 let asset_hex = Vec::<u8>::from_hex(s).map_err(|e| Error::Unexpected(e.to_string()))?;
582 elements::encode::deserialize(&asset_hex).map_err(|e| Error::Unexpected(e.to_string()))
583 }
584}
585
586fn asset(pref: u8, comm: &[u8]) -> Option<confidential::Asset> {
588 let mut bytes = [0u8; 33];
589 bytes[0] = pref;
590 if comm.len() != 32 {
591 return None;
592 }
593 bytes[1..].copy_from_slice(comm);
594 encode::deserialize(&bytes).ok()
595}
596
597fn value(pref: u8, comm: &[u8]) -> Option<confidential::Value> {
599 if comm.len() == 32 {
600 let mut bytes = [0u8; 33];
601 bytes[0] = pref;
602 bytes[1..].copy_from_slice(comm);
603 encode::deserialize(&bytes).ok()
604 } else if comm.len() == 8 {
605 let mut bytes = [0u8; 8];
606 bytes.copy_from_slice(comm);
607 if pref == 1 {
608 Some(confidential::Value::Explicit(u64::from_le_bytes(bytes)))
609 } else {
610 None
611 }
612 } else {
613 None
614 }
615}
616
617fn spk(pref: i8, prog: &[u8]) -> Option<elements::Script> {
619 if pref == -1 {
620 None
623 } else if pref <= 16 && pref >= 0 {
624 Some(
625 script::Builder::new()
626 .push_int(pref as i64)
627 .push_slice(prog)
628 .into_script(),
629 )
630 } else {
631 None
632 }
633}
634
635fn spk_to_components(s: &elements::Script) -> (i8, Vec<u8>) {
638 if !s.is_witness_program() {
639 (
640 -1,
641 sha256::Hash::hash(s.as_bytes()).to_byte_array().to_vec(),
642 )
643 } else {
644 let addr = Address::from_script(s, None, &AddressParams::ELEMENTS).unwrap();
647 if let Payload::WitnessProgram { version, program } = addr.payload {
648 (version.to_u8() as i8, program)
649 } else {
650 unreachable!("All witness programs have well defined payload")
651 }
652 }
653}
654
655impl AssetExpr<CovExtArgs> {
656 pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
660 match self {
661 AssetExpr::Const(CovExtArgs::Asset(a)) => {
662 match a {
663 Asset::Null => unreachable!("Attempt to push Null asset"),
664 Asset::Explicit(a) => builder.push_slice(a.into_inner().as_ref()).push_int(1), Asset::Confidential(c) => {
666 let ser = c.serialize();
667 builder.push_slice(&ser[1..]).push_int(ser[0] as i64)
668 }
669 }
670 }
671 AssetExpr::Const(_) => unreachable!(
672 "Both constructors from_str and from_token_iter
673 check that the correct variant is used in asset"
674 ),
675 AssetExpr::CurrInputAsset => builder
676 .push_opcode(OP_PUSHCURRENTINPUTINDEX)
677 .push_opcode(OP_INSPECTINPUTASSET),
678 AssetExpr::Input(i) => i.push_to_builder(builder).push_opcode(OP_INSPECTINPUTASSET),
679 AssetExpr::Output(i) => i
680 .push_to_builder(builder)
681 .push_opcode(OP_INSPECTOUTPUTASSET),
682 }
683 }
684
685 pub fn eval(&self, env: &TxEnv) -> Result<confidential::Asset, EvalError> {
687 match self {
688 AssetExpr::Const(CovExtArgs::Asset(a)) => Ok(*a),
689 AssetExpr::Const(_) => unreachable!(
690 "Both constructors from_str and from_token_iter
691 check that the correct variant is used in asset"
692 ),
693 AssetExpr::CurrInputAsset => {
694 if env.idx() >= env.spent_utxos().len() {
695 return Err(EvalError::UtxoIndexOutOfBounds(
696 env.idx(),
697 env.spent_utxos().len(),
698 ));
699 }
700 Ok(env.spent_utxos()[env.idx()].asset)
701 }
702 AssetExpr::Input(i) => {
703 let i = i.eval(env)?;
704 if i >= env.spent_utxos().len() {
705 return Err(EvalError::UtxoIndexOutOfBounds(i, env.spent_utxos().len()));
706 }
707 Ok(env.spent_utxos()[i].asset)
708 }
709 AssetExpr::Output(i) => {
710 let i = i.eval(env)?;
711 if i >= env.tx().output.len() {
712 return Err(EvalError::OutputIndexOutOfBounds(i, env.tx().output.len()));
713 }
714 Ok(env.tx().output[i].asset)
715 }
716 }
717 }
718
719 pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
722 let tks = tokens;
723 let e = end_pos; if let Some(&[Tk::Bytes32(asset_comm), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
725 let asset = asset(u8::try_from(i).ok()?, asset_comm)?;
726 Some((AssetExpr::Const(CovExtArgs::Asset(asset)), e - 2))
727 } else if let Some(&[Tk::CurrInp, Tk::InpAsset]) = tks.get(e.checked_sub(2)?..e) {
728 Some((AssetExpr::CurrInputAsset, e - 2))
729 } else if let Some(&[Tk::InpAsset]) = tks.get(e.checked_sub(1)?..e) {
730 let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
731 Some((AssetExpr::Input(idx_expr), e))
732 } else if let Some(&[Tk::OutAsset]) = tks.get(e.checked_sub(1)?..e) {
733 let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
734 Some((AssetExpr::Output(idx_expr), e))
735 } else {
736 None
737 }
738 }
739}
740
741impl ValueExpr<CovExtArgs> {
742 pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
744 match self {
745 ValueExpr::Const(CovExtArgs::Value(a)) => {
746 match a {
747 confidential::Value::Null => {
748 builder.push_slice(&0i64.to_le_bytes()).push_int(1)
749 } confidential::Value::Explicit(a) => {
751 builder.push_slice(&a.to_le_bytes()).push_int(1)
752 } confidential::Value::Confidential(c) => {
754 let ser = c.serialize();
755 builder.push_slice(&ser[1..]).push_int(ser[0] as i64)
756 }
757 }
758 }
759 ValueExpr::Const(_) => unreachable!(
760 "Both constructors from_str and from_token_iter
761 check that the correct variant is used in Value"
762 ),
763 ValueExpr::CurrInputValue => builder
764 .push_opcode(OP_PUSHCURRENTINPUTINDEX)
765 .push_opcode(OP_INSPECTINPUTVALUE),
766 ValueExpr::Input(i) => i.push_to_builder(builder).push_opcode(OP_INSPECTINPUTVALUE),
767 ValueExpr::Output(i) => i
768 .push_to_builder(builder)
769 .push_opcode(OP_INSPECTOUTPUTVALUE),
770 }
771 }
772
773 pub fn eval(&self, env: &TxEnv) -> Result<confidential::Value, EvalError> {
775 match self {
776 ValueExpr::Const(CovExtArgs::Value(a)) => Ok(*a),
777 ValueExpr::Const(_) => unreachable!(
778 "Both constructors from_str and from_token_iter
779 check that the correct variant is used in Value"
780 ),
781 ValueExpr::CurrInputValue => {
782 if env.idx() >= env.spent_utxos().len() {
783 return Err(EvalError::UtxoIndexOutOfBounds(
784 env.idx(),
785 env.spent_utxos().len(),
786 ));
787 }
788 Ok(env.spent_utxos()[env.idx()].value)
789 }
790 ValueExpr::Input(i) => {
791 let i = i.eval(env)?;
792 if i >= env.spent_utxos().len() {
793 return Err(EvalError::UtxoIndexOutOfBounds(i, env.spent_utxos().len()));
794 }
795 Ok(env.spent_utxos()[i].value)
796 }
797 ValueExpr::Output(i) => {
798 let i = i.eval(env)?;
799 if i >= env.tx().output.len() {
800 return Err(EvalError::OutputIndexOutOfBounds(i, env.tx().output.len()));
801 }
802 Ok(env.tx().output[i].value)
803 }
804 }
805 }
806
807 pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
810 let tks = tokens;
811 let e = end_pos; if let Some(&[Tk::Bytes32(value_comm), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
813 let value = value(u8::try_from(i).ok()?, value_comm)?;
814 Some((ValueExpr::Const(CovExtArgs::Value(value)), e - 2))
815 } else if let Some(&[Tk::Bytes8(exp_val), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
816 let value = value(u8::try_from(i).ok()?, exp_val)?;
817 Some((ValueExpr::Const(CovExtArgs::Value(value)), e - 2))
818 } else if let Some(&[Tk::CurrInp, Tk::InpValue]) = tks.get(e.checked_sub(2)?..e) {
819 Some((ValueExpr::CurrInputValue, e - 2))
820 } else if let Some(&[Tk::InpValue]) = tks.get(e.checked_sub(1)?..e) {
821 let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
822 Some((ValueExpr::Input(idx_expr), e))
823 } else if let Some(&[Tk::OutValue]) = tks.get(e.checked_sub(1)?..e) {
824 let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
825 Some((ValueExpr::Output(idx_expr), e))
826 } else {
827 None
828 }
829 }
830}
831
832impl SpkExpr<CovExtArgs> {
833 pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
835 match self {
836 SpkExpr::Const(CovExtArgs::Script(s)) => {
837 let (ver, prog) = match &s.0 {
838 SpkInner::Script(s) => spk_to_components(s),
839 SpkInner::Hashed(h) => (-1, h.to_byte_array().to_vec()),
840 };
841 builder.push_slice(&prog).push_int(ver as i64)
842 }
843 SpkExpr::Const(_) => unreachable!(
844 "Both constructors from_str and from_token_iter
845 check that the correct variant is used in Script"
846 ),
847 SpkExpr::CurrInputSpk => builder
848 .push_opcode(OP_PUSHCURRENTINPUTINDEX)
849 .push_opcode(OP_INSPECTINPUTSCRIPTPUBKEY),
850 SpkExpr::Input(i) => i
851 .push_to_builder(builder)
852 .push_opcode(OP_INSPECTINPUTSCRIPTPUBKEY),
853 SpkExpr::Output(i) => i
854 .push_to_builder(builder)
855 .push_opcode(OP_INSPECTOUTPUTSCRIPTPUBKEY),
856 }
857 }
858
859 pub fn eval(&self, env: &TxEnv) -> Result<(i8, Vec<u8>), EvalError> {
861 let res = match self {
862 SpkExpr::Const(CovExtArgs::Script(s)) => match &s.0 {
863 SpkInner::Script(s) => spk_to_components(s),
864 SpkInner::Hashed(h) => (-1, h.to_byte_array().to_vec()),
865 },
866 SpkExpr::Const(_) => unreachable!(
867 "Both constructors from_str and from_token_iter
868 check that the correct variant is used in Script pubkey"
869 ),
870 SpkExpr::CurrInputSpk => {
871 if env.idx() >= env.spent_utxos().len() {
872 return Err(EvalError::UtxoIndexOutOfBounds(
873 env.idx(),
874 env.spent_utxos().len(),
875 ));
876 }
877 spk_to_components(&env.spent_utxos()[env.idx()].script_pubkey)
878 }
879 SpkExpr::Input(i) => {
880 let i = i.eval(env)?;
881 if i >= env.spent_utxos().len() {
882 return Err(EvalError::UtxoIndexOutOfBounds(i, env.spent_utxos().len()));
883 }
884 spk_to_components(&(env.spent_utxos()[i].script_pubkey))
885 }
886 SpkExpr::Output(i) => {
887 let i = i.eval(env)?;
888 if i >= env.tx().output.len() {
889 return Err(EvalError::OutputIndexOutOfBounds(i, env.tx().output.len()));
890 }
891 spk_to_components(&(env.tx().output[i].script_pubkey))
892 }
893 };
894 Ok(res)
895 }
896
897 pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
900 let tks = tokens;
901 let e = end_pos; if let Some(&[Tk::Bytes32(spk_vec), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
903 let script = spk(i8::try_from(i).ok()?, spk_vec)?;
904 Some((SpkExpr::Const(CovExtArgs::Script(Spk::new(script))), e - 2))
905 } else if let Some(&[Tk::Bytes32(spk_vec), Tk::NumNeg1]) = tks.get(e.checked_sub(2)?..e) {
906 let mut inner = [0u8; 32];
907 inner.copy_from_slice(spk_vec);
908 let hashed_spk = Spk(SpkInner::Hashed(sha256::Hash::from_byte_array(inner)));
909 Some((SpkExpr::Const(CovExtArgs::Script(hashed_spk)), e - 2))
910 } else if let Some(&[Tk::Push(ref spk_vec), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
911 let script = spk(i8::try_from(i).ok()?, spk_vec)?;
912 Some((SpkExpr::Const(CovExtArgs::Script(Spk::new(script))), e - 2))
913 } else if let Some(&[Tk::CurrInp, Tk::InpSpk]) = tks.get(e.checked_sub(2)?..e) {
914 Some((SpkExpr::CurrInputSpk, e - 2))
915 } else if let Some(&[Tk::InpSpk]) = tks.get(e.checked_sub(1)?..e) {
916 let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
917 Some((SpkExpr::Input(idx_expr), e))
918 } else if let Some(&[Tk::OutSpk]) = tks.get(e.checked_sub(1)?..e) {
919 let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
920 Some((SpkExpr::Output(idx_expr), e))
921 } else {
922 None
923 }
924 }
925}
926
927impl CovOps<CovExtArgs> {
928 pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
930 match self {
931 CovOps::IsExpAsset(x) => x
932 .push_to_builder(builder)
933 .push_int(1)
934 .push_opcode(OP_EQUAL)
935 .push_opcode(OP_NIP),
936 CovOps::IsExpValue(x) => x
937 .push_to_builder(builder)
938 .push_int(1)
939 .push_opcode(OP_EQUAL)
940 .push_opcode(OP_NIP),
941 CovOps::AssetEq(x, y) => {
942 let builder = x.push_to_builder(builder).push_opcode(OP_TOALTSTACK);
944 let builder = y
945 .push_to_builder(builder)
946 .push_opcode(OP_FROMALTSTACK)
947 .push_opcode(OP_EQUAL);
948 builder
949 .push_opcode(OP_TOALTSTACK)
950 .push_opcode(OP_EQUAL)
951 .push_opcode(OP_FROMALTSTACK)
952 .push_opcode(OP_BOOLAND)
953 }
954 CovOps::ValueEq(x, y) => {
955 let builder = x.push_to_builder(builder).push_opcode(OP_TOALTSTACK);
957 let builder = y
958 .push_to_builder(builder)
959 .push_opcode(OP_FROMALTSTACK)
960 .push_opcode(OP_EQUAL);
961 builder
962 .push_opcode(OP_TOALTSTACK)
963 .push_opcode(OP_EQUAL)
964 .push_opcode(OP_FROMALTSTACK)
965 .push_opcode(OP_BOOLAND)
966 }
967 CovOps::SpkEq(x, y) => {
968 let builder = x.push_to_builder(builder).push_opcode(OP_TOALTSTACK);
970 let builder = y
971 .push_to_builder(builder)
972 .push_opcode(OP_FROMALTSTACK)
973 .push_opcode(OP_EQUAL);
974 builder
975 .push_opcode(OP_TOALTSTACK)
976 .push_opcode(OP_EQUAL)
977 .push_opcode(OP_FROMALTSTACK)
978 .push_opcode(OP_BOOLAND)
979 }
980 CovOps::CurrIndEq(i) => builder
981 .push_int(*i as i64)
982 .push_opcode(OP_PUSHCURRENTINPUTINDEX)
983 .push_opcode(OP_EQUAL),
984 CovOps::IdxEq(x, y) => {
985 let builder = x.push_to_builder(builder);
987 let builder = y.push_to_builder(builder);
988 builder.push_opcode(OP_EQUAL)
989 }
990 }
991 }
992
993 pub fn eval(&self, env: &TxEnv) -> Result<bool, EvalError> {
995 match self {
996 CovOps::IsExpAsset(x) => x.eval(env).map(|x| x.is_explicit()),
997 CovOps::IsExpValue(y) => y.eval(env).map(|y| y.is_explicit()),
998 CovOps::AssetEq(x, y) => Ok(x.eval(env)? == y.eval(env)?),
999 CovOps::ValueEq(x, y) => Ok(x.eval(env)? == y.eval(env)?),
1000 CovOps::SpkEq(x, y) => Ok(x.eval(env)? == y.eval(env)?),
1001 CovOps::CurrIndEq(i) => Ok(*i == env.idx()),
1002 CovOps::IdxEq(x, y) => Ok(x.eval(env)? == y.eval(env)?),
1003 }
1004 }
1005
1006 pub fn from_tokens(tks: &[Tk]) -> Option<(Self, usize)> {
1009 let e = tks.len();
1010 if let Some(&[Tk::Num(i), Tk::CurrInp, Tk::Equal]) = tks.get(e.checked_sub(3)?..e) {
1011 Some((CovOps::CurrIndEq(i as usize), e - 3))
1012 } else if let Some(&[Tk::Equal]) = tks.get(e.checked_sub(1)?..e) {
1013 let (y, e) = IdxExpr::from_tokens(tks, e - 1)?;
1014 let (x, e) = IdxExpr::from_tokens(tks, e)?;
1015 Some((CovOps::IdxEq(x, y), e))
1016 } else if let Some(&[Tk::Num(1), Tk::Equal, Tk::Nip]) = tks.get(e.checked_sub(3)?..e) {
1017 if let Some((asset, e)) = AssetExpr::from_tokens(tks, e - 3) {
1018 Some((CovOps::IsExpAsset(asset), e))
1019 } else if let Some((value, e)) = ValueExpr::from_tokens(tks, e - 3) {
1020 Some((CovOps::IsExpValue(value), e))
1021 } else {
1022 None
1023 }
1024 } else if let Some(
1025 &[Tk::FromAltStack, Tk::Equal, Tk::ToAltStack, Tk::Equal, Tk::FromAltStack, Tk::BoolAnd],
1026 ) = tks.get(e.checked_sub(6)?..e)
1027 {
1028 let res = if let Some((y, e)) = AssetExpr::from_tokens(tks, e - 6) {
1029 if tks.get(e - 1) != Some(&Tk::ToAltStack) {
1030 return None;
1031 }
1032 if let Some((x, e)) = AssetExpr::from_tokens(tks, e - 1) {
1033 Some((CovOps::AssetEq(x, y), e))
1034 } else {
1035 None
1036 }
1037 } else {
1038 None
1039 };
1040 if res.is_some() {
1041 return res;
1042 }
1043 let res = if let Some((y, e)) = ValueExpr::from_tokens(tks, e - 6) {
1044 if tks.get(e - 1) != Some(&Tk::ToAltStack) {
1045 return None;
1046 }
1047 if let Some((x, e)) = ValueExpr::from_tokens(tks, e - 1) {
1048 Some((CovOps::ValueEq(x, y), e))
1049 } else {
1050 None
1051 }
1052 } else {
1053 None
1054 };
1055 if res.is_some() {
1056 return res;
1057 }
1058 if let Some((y, e)) = SpkExpr::from_tokens(tks, e - 6) {
1059 if tks.get(e - 1) != Some(&Tk::ToAltStack) {
1060 return None;
1061 }
1062 if let Some((x, e)) = SpkExpr::from_tokens(tks, e - 1) {
1063 Some((CovOps::SpkEq(x, y), e))
1064 } else {
1065 None
1066 }
1067 } else {
1068 None
1069 }
1070 } else {
1071 None
1072 }
1073 }
1074}
1075
1076impl ParseableExt for CovOps<CovExtArgs> {
1077 fn satisfy<Pk, S>(&self, sat: &S) -> Satisfaction
1078 where
1079 Pk: ToPublicKey,
1080 S: Satisfier<Pk>,
1081 {
1082 let env = match (
1083 sat.lookup_tx(),
1084 sat.lookup_spent_utxos(),
1085 sat.lookup_curr_inp(),
1086 ) {
1087 (Some(tx), Some(utxos), Some(idx)) => match TxEnv::new(tx, utxos, idx) {
1088 Some(x) => x,
1089 None => {
1090 return Satisfaction {
1091 stack: Witness::Impossible,
1092 has_sig: false,
1093 }
1094 }
1095 },
1096 _ => {
1097 return Satisfaction {
1098 stack: Witness::Impossible,
1099 has_sig: false,
1100 }
1101 }
1102 };
1103 let wit = match self.eval(&env) {
1104 Ok(false) => Witness::Unavailable,
1105 Ok(true) => Witness::empty(),
1106 Err(_e) => Witness::Impossible,
1107 };
1108 Satisfaction {
1109 stack: wit,
1110 has_sig: false,
1111 }
1112 }
1113
1114 fn dissatisfy<Pk, S>(&self, sat: &S) -> Satisfaction
1115 where
1116 Pk: ToPublicKey,
1117 S: Satisfier<Pk>,
1118 {
1119 let env = match (
1120 sat.lookup_tx(),
1121 sat.lookup_spent_utxos(),
1122 sat.lookup_curr_inp(),
1123 ) {
1124 (Some(tx), Some(utxos), Some(idx)) => match TxEnv::new(tx, utxos, idx) {
1125 Some(x) => x,
1126 None => {
1127 return Satisfaction {
1128 stack: Witness::Impossible,
1129 has_sig: false,
1130 }
1131 }
1132 },
1133 _ => {
1134 return Satisfaction {
1135 stack: Witness::Impossible,
1136 has_sig: false,
1137 }
1138 }
1139 };
1140 let wit = match self.eval(&env) {
1141 Ok(false) => Witness::empty(),
1142 Ok(true) => Witness::Unavailable,
1143 Err(_e) => Witness::Impossible,
1144 };
1145 Satisfaction {
1146 stack: wit,
1147 has_sig: false,
1148 }
1149 }
1150
1151 fn push_to_builder(&self, builder: elements::script::Builder) -> elements::script::Builder {
1152 self.push_to_builder(builder)
1153 }
1154
1155 fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result<Self, FromTokenIterError> {
1156 let len = tokens.len();
1157 match Self::from_tokens(tokens.as_inner_mut()) {
1158 Some((res, last_pos)) => {
1159 tokens.advance(len - last_pos).ok_or(FromTokenIterError)?;
1160 Ok(res)
1161 }
1162 None => Err(FromTokenIterError),
1163 }
1164 }
1165
1166 fn evaluate(
1167 &self,
1168 stack: &mut interpreter::Stack,
1169 txenv: Option<&TxEnv>,
1170 ) -> Result<bool, interpreter::Error> {
1171 let txenv = txenv
1172 .as_ref()
1173 .ok_or(interpreter::Error::ArithError(EvalError::TxEnvNotPresent))?;
1174
1175 match self.eval(txenv) {
1176 Ok(true) => {
1177 stack.push(interpreter::Element::Satisfied);
1178 Ok(true)
1179 }
1180 Ok(false) => {
1181 stack.push(interpreter::Element::Dissatisfied);
1182 Ok(false)
1183 }
1184 Err(e) => Err(interpreter::Error::ArithError(e)),
1185 }
1186 }
1187}
1188
1189impl<PArg, QArg> TranslateExtParam<PArg, QArg> for CovOps<PArg>
1190where
1191 PArg: ExtParam,
1192 QArg: ExtParam,
1193{
1194 type Output = CovOps<QArg>;
1195
1196 fn translate_ext<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
1197 where
1198 T: ExtParamTranslator<PArg, QArg, E>,
1199 {
1200 match self {
1201 CovOps::IsExpAsset(a) => Ok(CovOps::IsExpAsset(a._translate_ext(t)?)),
1202 CovOps::IsExpValue(v) => Ok(CovOps::IsExpValue(v._translate_ext(t)?)),
1203 CovOps::AssetEq(x, y) => {
1204 Ok(CovOps::AssetEq(x._translate_ext(t)?, y._translate_ext(t)?))
1205 }
1206 CovOps::ValueEq(x, y) => {
1207 Ok(CovOps::ValueEq(x._translate_ext(t)?, y._translate_ext(t)?))
1208 }
1209 CovOps::SpkEq(x, y) => Ok(CovOps::SpkEq(x._translate_ext(t)?, y._translate_ext(t)?)),
1210 CovOps::CurrIndEq(i) => Ok(CovOps::CurrIndEq(*i)),
1211 CovOps::IdxEq(x, y) => Ok(CovOps::IdxEq(x.clone(), y.clone())),
1212 }
1213 }
1214}
1215
1216#[cfg(test)]
1217mod tests {
1218 use bitcoin::key::XOnlyPublicKey;
1219
1220 use super::*;
1221 use crate::test_utils::{StrExtTranslator, StrXOnlyKeyTranslator};
1222 use crate::{Miniscript, Segwitv0, Tap, TranslatePk};
1223
1224 #[test]
1225 fn test_index_ops() {
1226 _test_parse("is_exp_asset(inp_asset(curr_idx))");
1228 _test_parse("is_exp_asset(inp_asset(idx_add(9,curr_idx)))");
1229 _test_parse("is_exp_asset(inp_asset(idx_sub(9,curr_idx)))");
1230 _test_parse("is_exp_asset(inp_asset(idx_mul(9,curr_idx)))");
1231 _test_parse("is_exp_asset(inp_asset(idx_div(9,curr_idx)))");
1232 _test_parse("is_exp_asset(inp_asset(idx_mul(1,idx_add(9,curr_idx))))");
1233 _test_parse("is_exp_asset(inp_asset(idx_sub(idx_mul(1,idx_add(9,curr_idx)),1)))");
1234
1235 _test_parse("is_exp_asset(out_asset(idx_add(9,curr_idx)))");
1237 _test_parse("is_exp_value(inp_value(idx_add(9,curr_idx)))");
1238 _test_parse("is_exp_value(out_value(idx_add(9,curr_idx)))");
1239 _test_parse("spk_eq(inp_spk(idx_add(9,curr_idx)),out_spk(idx_sub(9,curr_idx)))");
1240
1241 _test_parse("idx_eq(10,idx_add(9,curr_idx))");
1242 }
1243
1244 #[test]
1245 fn cov_parse() {
1246 _test_parse("is_exp_asset(ConfAst)");
1248 _test_parse("is_exp_asset(ExpAst)");
1249 _test_parse("is_exp_asset(curr_inp_asset)");
1250 _test_parse("is_exp_asset(inp_asset(9))");
1251 _test_parse("is_exp_asset(out_asset(9))");
1252 _test_parse("asset_eq(ConfAst,ExpAst)");
1253 _test_parse("asset_eq(curr_inp_asset,out_asset(1))");
1254 _test_parse("asset_eq(inp_asset(3),out_asset(1))");
1255
1256 _test_parse("is_exp_value(ConfVal)");
1258 _test_parse("is_exp_value(ExpVal)");
1259 _test_parse("is_exp_value(curr_inp_value)");
1260 _test_parse("is_exp_value(inp_value(9))");
1261 _test_parse("is_exp_value(out_value(9))");
1262 _test_parse("value_eq(ConfVal,ExpVal)");
1263 _test_parse("value_eq(curr_inp_value,out_value(1))");
1264 _test_parse("value_eq(inp_value(3),out_value(1))");
1265
1266 _test_parse("spk_eq(V0Spk,out_spk(1))");
1268 _test_parse("spk_eq(V1Spk,inp_spk(1))");
1269 _test_parse("spk_eq(curr_inp_spk,out_spk(1))");
1270 _test_parse("spk_eq(inp_spk(3),out_spk(1))");
1271 _test_parse("spk_eq(out_spk(2),V1Spk)");
1272
1273 _test_parse("curr_idx_eq(1)");
1275 _test_parse("curr_idx_eq(0)");
1276
1277 _test_parse(
1279 "and_v(v:pk(K),and_v(v:is_exp_value(out_value(1)),is_exp_asset(out_asset(1))))",
1280 );
1281 _test_parse("and_v(v:pk(K),and_v(v:value_eq(ConfVal,ConfVal),spk_eq(V1Spk,V1Spk)))");
1282 _test_parse("and_v(v:pk(K),and_v(v:value_eq(ConfVal,ConfVal),and_v(v:spk_eq(V1Spk,V1Spk),curr_idx_eq(1))))");
1283 }
1284
1285 #[test]
1286 fn options_fail_test() {
1287 type MsExt = Miniscript<XOnlyPublicKey, Tap, CovOps<CovExtArgs>>;
1288
1289 MsExt::from_str_insane("asset_eq(out_asset(0),0179d51a47e4ac8e32306486dd0926a88678c392f2ed5f213e3ff2ad461c7c25e1)").unwrap();
1291 MsExt::from_str_insane("asset_eq(out_asset(0),79d51a47e4ac8e32306486dd0926a88678c392f2ed5f213e3ff2ad461c7c25e1)").unwrap_err();
1293 }
1294
1295 #[rustfmt::skip]
1296 fn _test_parse(s: &str) {
1297 type MsExtStr = Miniscript<String, Tap, CovOps<String>>;
1298 type MsExt = Miniscript<XOnlyPublicKey, Tap, CovOps<CovExtArgs>>;
1299 type MsExtSegwitv0 = Miniscript<String, Segwitv0, CovOps<CovExtArgs>>;
1300
1301 assert!(MsExtSegwitv0::from_str_insane(s).is_err());
1303
1304 let ms = MsExtStr::from_str_insane(s).unwrap();
1305 assert_eq!(ms.to_string(), s);
1307 let mut t = StrXOnlyKeyTranslator::default();
1308 let mut ext_t = StrExtTranslator::default();
1309 {
1310 ext_t.ext_map.insert("V1Spk".to_string(),CovExtArgs::spk(elements::Script::from_str("5120c73ac1b7a518499b9642aed8cfa15d5401e5bd85ad760b937b69521c297722f0").unwrap()));
1311 ext_t.ext_map.insert("V0Spk".to_string(),CovExtArgs::spk(elements::Script::from_str("0020c73ac1b7a518499b9642aed8cfa15d5401e5bd85ad760b937b69521c297722f0").unwrap()));
1312 ext_t.ext_map.insert("ConfAst".to_string(),CovExtArgs::asset(encode::deserialize(&Vec::<u8>::from_hex("0adef814ab021498562ab4717287305d3f7abb5686832fe6183e1db495abef7cc7").unwrap()).unwrap()));
1313 ext_t.ext_map.insert("ExpAst".to_string(),CovExtArgs::asset(encode::deserialize(&Vec::<u8>::from_hex("01c73ac1b7a518499b9642aed8cfa15d5401e5bd85ad760b937b69521c297722f0").unwrap()).unwrap()));
1314 ext_t.ext_map.insert("ConfVal".to_string(),CovExtArgs::value(encode::deserialize(&Vec::<u8>::from_hex("09def814ab021498562ab4717287305d3f7abb5686832fe6183e1db495abef7cc7").unwrap()).unwrap()));
1315 ext_t.ext_map.insert("ExpVal".to_string(),CovExtArgs::value(encode::deserialize(&Vec::<u8>::from_hex("010000000011110000").unwrap()).unwrap()));
1316 }
1317 let ms: Miniscript<XOnlyPublicKey, Tap, CovOps<String>> = ms.translate_pk(&mut t).unwrap();
1318 let ms: Miniscript<XOnlyPublicKey, Tap, CovOps<CovExtArgs>> = ms.translate_ext(&mut ext_t).unwrap();
1319 assert_eq!(ms.encode(), MsExt::parse_insane(&ms.encode()).unwrap().encode());
1321 assert_eq!(ms, MsExt::from_str_insane(&ms.to_string()).unwrap())
1323 }
1324}