Skip to content

Commit 240b0c5

Browse files
committed
Merge ElementsProject#28: Add num64_ prefix to comparison ops
85ed5f3 Fix decoding bug while implementing CurrInputValue (sanket1729) c6dfc12 This fix has a bunch of bug fixes. (sanket1729) 2a5455a Mark has_free_verify more CurrIndEq (sanket1729) 6029aba Add num64_ prefix to comparison ops (sanket1729) Pull request description: Followup to ElementsProject#27 ACKs for top commit: apoelstra: ACK 85ed5f3 Tree-SHA512: db31d2a9f818be80b4cc3cc1744d2f3b587ed2f14e7b63812515ec5d22300dd86664318198231bfb701dbb4df0bfeba9a1c2c0677ad82f9e8e2a006364d10806
2 parents 231d9b6 + 85ed5f3 commit 240b0c5

File tree

5 files changed

+157
-112
lines changed

5 files changed

+157
-112
lines changed

doc/extension_spec.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ To use these with miniscript fragments, we can use them inside comparison extens
4444

4545
Name | Script
4646
--- | ---
47-
num_eq(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] EQUAL64`
48-
le(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] LESSTHAN64`
49-
ge(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] GREATERTHAN64`
50-
leq(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] LESSTHANOREQUAL64`
51-
geq(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] GREATERTHANOREQUAL64`
47+
num64_eq(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] EQUAL`
48+
num64_le(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] LESSTHAN64`
49+
num64_ge(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] GREATERTHAN64`
50+
num64_leq(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] LESSTHANOREQUAL64`
51+
num64_geq(NumExpr_X,NumExpr_Y) | `[NumExpr_X] [NumExpr_Y] GREATERTHANOREQUAL64`
5252

53-
- For example, `num_eq(inp_v(1),mul(curr_inp_v,20))` represents second input value is the multiplication of
53+
- For example, `num64_eq(inp_v(1),mul(curr_inp_v,20))` represents second input value is the multiplication of
5454
current input value and fourth output value. This would abort if any of the values are confidential.
5555

5656
### Tx Value introspection

src/extensions/arith.rs

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,7 @@ impl Expr {
476476
} else if let Some(&[Tk::CurrInp, Tk::InpValue, Tk::Num(1), Tk::Equal, Tk::Verify]) =
477477
tks.get(e.checked_sub(5)?..e)
478478
{
479-
let (x, end_pos) = Self::from_tokens(tokens, e - 5)?;
480-
let expr = Expr::from_inner(ExprInner::Negate(Box::new(x)));
481-
Some((expr, end_pos))
479+
Some((Expr::from_inner(ExprInner::CurrInputIdx), e - 5))
482480
} else if let Some(&[Tk::Div64, Tk::Num(1), Tk::Equal, Tk::Verify, Tk::Nip]) =
483481
tks.get(e.checked_sub(5)?..e)
484482
{
@@ -526,15 +524,15 @@ pub enum Arith {
526524
/// Eq
527525
/// [X] [Y] EQUAL
528526
Eq(Expr, Expr),
529-
/// Le
527+
/// Lt
530528
/// [X] [Y] LESSTHAN
531-
Le(Expr, Expr),
529+
Lt(Expr, Expr),
532530
/// Leq
533531
/// [X] [Y] LESSTHANOREQUAL
534532
Leq(Expr, Expr),
535-
/// Ge
533+
/// Gt
536534
/// [X] [Y] GREATERTHAN
537-
Ge(Expr, Expr),
535+
Gt(Expr, Expr),
538536
/// Geq
539537
/// [X] [Y] GREATERTHANOREQUAL
540538
Geq(Expr, Expr),
@@ -545,9 +543,9 @@ impl Arith {
545543
pub fn depth(&self) -> usize {
546544
match self {
547545
Arith::Eq(x, y)
548-
| Arith::Le(x, y)
546+
| Arith::Lt(x, y)
549547
| Arith::Leq(x, y)
550-
| Arith::Ge(x, y)
548+
| Arith::Gt(x, y)
551549
| Arith::Geq(x, y) => cmp::max(x.depth, y.depth),
552550
}
553551
}
@@ -556,9 +554,9 @@ impl Arith {
556554
pub fn script_size(&self) -> usize {
557555
match self {
558556
Arith::Eq(x, y)
559-
| Arith::Le(x, y)
557+
| Arith::Lt(x, y)
560558
| Arith::Leq(x, y)
561-
| Arith::Ge(x, y)
559+
| Arith::Gt(x, y)
562560
| Arith::Geq(x, y) => x.script_size + y.script_size + 1,
563561
}
564562
}
@@ -572,9 +570,9 @@ impl Arith {
572570
) -> Result<bool, EvalError> {
573571
let res = match self {
574572
Arith::Eq(x, y) => x.eval(curr_ind, tx, utxos)? == y.eval(curr_ind, tx, utxos)?,
575-
Arith::Le(x, y) => x.eval(curr_ind, tx, utxos)? < y.eval(curr_ind, tx, utxos)?,
573+
Arith::Lt(x, y) => x.eval(curr_ind, tx, utxos)? < y.eval(curr_ind, tx, utxos)?,
576574
Arith::Leq(x, y) => x.eval(curr_ind, tx, utxos)? <= y.eval(curr_ind, tx, utxos)?,
577-
Arith::Ge(x, y) => x.eval(curr_ind, tx, utxos)? > y.eval(curr_ind, tx, utxos)?,
575+
Arith::Gt(x, y) => x.eval(curr_ind, tx, utxos)? > y.eval(curr_ind, tx, utxos)?,
578576
Arith::Geq(x, y) => x.eval(curr_ind, tx, utxos)? >= y.eval(curr_ind, tx, utxos)?,
579577
};
580578
Ok(res)
@@ -588,7 +586,7 @@ impl Arith {
588586
let builder = y.push_to_builder(builder);
589587
builder.push_opcode(OP_EQUAL)
590588
}
591-
Arith::Le(x, y) => {
589+
Arith::Lt(x, y) => {
592590
let builder = x.push_to_builder(builder);
593591
let builder = y.push_to_builder(builder);
594592
builder.push_opcode(OP_LESSTHAN64)
@@ -598,7 +596,7 @@ impl Arith {
598596
let builder = y.push_to_builder(builder);
599597
builder.push_opcode(OP_LESSTHANOREQUAL64)
600598
}
601-
Arith::Ge(x, y) => {
599+
Arith::Gt(x, y) => {
602600
let builder = x.push_to_builder(builder);
603601
let builder = y.push_to_builder(builder);
604602
builder.push_opcode(OP_GREATERTHAN64)
@@ -623,9 +621,9 @@ impl Arith {
623621
let (x, pos) = Expr::from_tokens(tokens, pos)?;
624622
match last_opcode {
625623
Tk::Equal => Some((Self::Eq(x, y), pos)),
626-
Tk::Le64 => Some((Self::Le(x, y), pos)),
624+
Tk::Le64 => Some((Self::Lt(x, y), pos)),
627625
Tk::Leq64 => Some((Self::Leq(x, y), pos)),
628-
Tk::Ge64 => Some((Self::Ge(x, y), pos)),
626+
Tk::Ge64 => Some((Self::Gt(x, y), pos)),
629627
Tk::Geq64 => Some((Self::Geq(x, y), pos)),
630628
_ => None,
631629
}
@@ -770,12 +768,12 @@ impl FromTree for Box<Arith> {
770768
impl FromTree for Arith {
771769
fn from_tree(top: &expression::Tree<'_>) -> Result<Self, Error> {
772770
match (top.name, top.args.len()) {
773-
// Disambiguiate with num_eq to avoid confusion with asset_eq
774-
("num_eq", 2) => expression::binary(top, Arith::Eq),
775-
("geq", 2) => expression::binary(top, Arith::Geq),
776-
("ge", 2) => expression::binary(top, Arith::Ge),
777-
("le", 2) => expression::binary(top, Arith::Le),
778-
("leq", 2) => expression::binary(top, Arith::Leq),
771+
// Disambiguiate with num64_eq to avoid confusion with asset_eq
772+
("num64_eq", 2) => expression::binary(top, Arith::Eq),
773+
("num64_geq", 2) => expression::binary(top, Arith::Geq),
774+
("num64_gt", 2) => expression::binary(top, Arith::Gt),
775+
("num64_lt", 2) => expression::binary(top, Arith::Lt),
776+
("num64_leq", 2) => expression::binary(top, Arith::Leq),
779777
_ => Err(Error::Unexpected(format!(
780778
"{}({} args) while parsing Extension",
781779
top.name,
@@ -788,23 +786,23 @@ impl FromTree for Arith {
788786
impl fmt::Display for Arith {
789787
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
790788
match &self {
791-
Arith::Eq(x, y) => write!(f, "num_eq({},{})", x, y),
792-
Arith::Leq(x, y) => write!(f, "leq({},{})", x, y),
793-
Arith::Le(x, y) => write!(f, "le({},{})", x, y),
794-
Arith::Geq(x, y) => write!(f, "geq({},{})", x, y),
795-
Arith::Ge(x, y) => write!(f, "ge({},{})", x, y),
789+
Arith::Eq(x, y) => write!(f, "num64_eq({},{})", x, y),
790+
Arith::Leq(x, y) => write!(f, "num64_leq({},{})", x, y),
791+
Arith::Lt(x, y) => write!(f, "num64_lt({},{})", x, y),
792+
Arith::Geq(x, y) => write!(f, "num64_geq({},{})", x, y),
793+
Arith::Gt(x, y) => write!(f, "num64_gt({},{})", x, y),
796794
}
797795
}
798796
}
799797

800798
impl fmt::Debug for Arith {
801799
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
802800
match &self {
803-
Arith::Eq(x, y) => write!(f, "num_eq({:?},{:?})", x, y),
804-
Arith::Leq(x, y) => write!(f, "leq({:?},{:?})", x, y),
805-
Arith::Le(x, y) => write!(f, "le({:?},{:?})", x, y),
806-
Arith::Geq(x, y) => write!(f, "geq({:?},{:?})", x, y),
807-
Arith::Ge(x, y) => write!(f, "ge({:?},{:?})", x, y),
801+
Arith::Eq(x, y) => write!(f, "num64_eq({:?},{:?})", x, y),
802+
Arith::Leq(x, y) => write!(f, "num64_leq({:?},{:?})", x, y),
803+
Arith::Lt(x, y) => write!(f, "num64_lt({:?},{:?})", x, y),
804+
Arith::Geq(x, y) => write!(f, "num64_geq({:?},{:?})", x, y),
805+
Arith::Gt(x, y) => write!(f, "num64_gt({:?},{:?})", x, y),
808806
}
809807
}
810808
}
@@ -1064,38 +1062,41 @@ mod tests {
10641062

10651063
#[test]
10661064
fn arith_parse() {
1065+
_arith_parse("num64_gt(curr_inp_v,mul(1,out_v(0)))");
10671066
// This does not test the evaluation
1068-
_arith_parse("num_eq(8,8)");
1069-
_arith_parse("ge(9223372036854775807,9223372036854775806)"); // 2**63-1
1067+
_arith_parse("num64_eq(8,8)");
1068+
_arith_parse("num64_gt(9223372036854775807,9223372036854775806)"); // 2**63-1
10701069

10711070
// negatives and comparisons
1072-
_arith_parse("num_eq(-8,-8)"); // negative nums
1073-
_arith_parse("ge(-8,-9)");
1074-
_arith_parse("geq(-8,-8)");
1075-
_arith_parse("leq(-8,-7)");
1076-
_arith_parse("le(-8,-7)");
1071+
_arith_parse("num64_eq(-8,-8)"); // negative nums
1072+
_arith_parse("num64_gt(-8,-9)");
1073+
_arith_parse("num64_geq(-8,-8)");
1074+
_arith_parse("num64_leq(-8,-7)");
1075+
_arith_parse("num64_lt(-8,-7)");
10771076

10781077
// test terminals parsing
1079-
_arith_parse("num_eq(inp_v(0),100)");
1080-
_arith_parse("num_eq(out_v(0),100)");
1081-
_arith_parse("num_eq(inp_issue_v(0),100)");
1082-
_arith_parse("num_eq(inp_reissue_v(0),100)");
1083-
_arith_parse("num_eq(inp_v(0),out_v(0))");
1084-
_arith_parse("num_eq(inp_issue_v(1),inp_reissue_v(1))");
1078+
_arith_parse("num64_eq(inp_v(0),100)");
1079+
_arith_parse("num64_eq(out_v(0),100)");
1080+
_arith_parse("num64_eq(inp_issue_v(0),100)");
1081+
_arith_parse("num64_eq(inp_reissue_v(0),100)");
1082+
_arith_parse("num64_eq(inp_v(0),out_v(0))");
1083+
_arith_parse("num64_eq(inp_issue_v(1),inp_reissue_v(1))");
10851084

10861085
// test combinator
1087-
_arith_parse("num_eq(add(4,3),mul(1,7))");
1088-
_arith_parse("num_eq(sub(3,3),div(0,9))");
1089-
_arith_parse("num_eq(mod(9,3),0)");
1090-
_arith_parse("num_eq(bitand(0,134),0)");
1091-
_arith_parse("num_eq(bitor(1,3),3)");
1092-
_arith_parse("num_eq(bitxor(1,3),2)");
1093-
_arith_parse("num_eq(bitinv(0),-9223372036854775808)");
1094-
_arith_parse("num_eq(neg(1),-1)");
1086+
_arith_parse("num64_eq(add(4,3),mul(1,7))");
1087+
_arith_parse("num64_eq(sub(3,3),div(0,9))");
1088+
_arith_parse("num64_eq(mod(9,3),0)");
1089+
_arith_parse("num64_eq(bitand(0,134),0)");
1090+
_arith_parse("num64_eq(bitor(1,3),3)");
1091+
_arith_parse("num64_eq(bitxor(1,3),2)");
1092+
_arith_parse("num64_eq(bitinv(0),-9223372036854775808)");
1093+
_arith_parse("num64_eq(neg(1),-1)");
10951094

10961095
// test some misc combinations with other miniscript fragments
1097-
_arith_parse("and_v(v:pk(K),ge(8,7))");
1098-
_arith_parse("and_v(v:pk(K),num_eq(mul(inp_v(0),out_v(1)),sub(add(3,inp_issue_v(1)),-9)))");
1096+
_arith_parse("and_v(v:pk(K),num64_gt(8,7))");
1097+
_arith_parse(
1098+
"and_v(v:pk(K),num64_eq(mul(inp_v(0),out_v(1)),sub(add(3,inp_issue_v(1)),-9)))",
1099+
);
10991100
}
11001101

11011102
fn _arith_parse(s: &str) {

src/extensions/introspect_ops.rs

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -168,24 +168,22 @@ impl<T: ExtParam> fmt::Debug for AssetExpr<T> {
168168
}
169169
}
170170

171-
impl<T: ExtParam> FromStr for AssetExpr<T> {
172-
type Err = Error;
173-
174-
fn from_str(s: &str) -> Result<Self, Self::Err> {
171+
impl<T: ExtParam> ArgFromStr for AssetExpr<T> {
172+
fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
175173
let top = expression::Tree::from_str(s)?;
176-
Self::from_tree(&top)
174+
Self::from_tree_parent(&top, parent, pos)
177175
}
178176
}
179177

180-
impl<T: ExtParam> FromTree for AssetExpr<T> {
181-
fn from_tree(top: &Tree<'_>) -> Result<Self, Error> {
178+
impl<T: ExtParam> AssetExpr<T> {
179+
fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
182180
match (top.name, top.args.len()) {
183181
("curr_inp_asset", 0) => Ok(AssetExpr::CurrInputAsset),
184182
("inp_asset", 1) => expression::terminal(&top.args[0], expression::parse_num::<usize>)
185183
.map(AssetExpr::Input),
186184
("out_asset", 1) => expression::terminal(&top.args[0], expression::parse_num::<usize>)
187185
.map(AssetExpr::Output),
188-
(asset, 0) => Ok(AssetExpr::Const(T::arg_from_str(asset, "", 0)?)),
186+
(asset, 0) => Ok(AssetExpr::Const(T::arg_from_str(asset, parent, pos)?)),
189187
_ => Err(Error::Unexpected(format!(
190188
"{}({} args) while parsing Extension",
191189
top.name,
@@ -244,24 +242,22 @@ impl<T: ExtParam> fmt::Debug for ValueExpr<T> {
244242
}
245243
}
246244

247-
impl<T: ExtParam> FromStr for ValueExpr<T> {
248-
type Err = Error;
249-
250-
fn from_str(s: &str) -> Result<Self, Self::Err> {
245+
impl<T: ExtParam> ArgFromStr for ValueExpr<T> {
246+
fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
251247
let top = expression::Tree::from_str(s)?;
252-
Self::from_tree(&top)
248+
Self::from_tree_parent(&top, parent, pos)
253249
}
254250
}
255251

256-
impl<T: ExtParam> FromTree for ValueExpr<T> {
257-
fn from_tree(top: &Tree<'_>) -> Result<Self, Error> {
252+
impl<T: ExtParam> ValueExpr<T> {
253+
fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
258254
match (top.name, top.args.len()) {
259255
("curr_inp_value", 0) => Ok(ValueExpr::CurrInputValue),
260256
("inp_value", 1) => expression::terminal(&top.args[0], expression::parse_num::<usize>)
261257
.map(ValueExpr::Input),
262258
("out_value", 1) => expression::terminal(&top.args[0], expression::parse_num::<usize>)
263259
.map(ValueExpr::Output),
264-
(value, 0) => Ok(ValueExpr::Const(T::arg_from_str(value, "", 0)?)),
260+
(value, 0) => Ok(ValueExpr::Const(T::arg_from_str(value, parent, pos)?)),
265261
_ => Err(Error::Unexpected(format!(
266262
"{}({} args) while parsing Extension",
267263
top.name,
@@ -320,24 +316,22 @@ impl<T: ExtParam> fmt::Debug for SpkExpr<T> {
320316
}
321317
}
322318

323-
impl<T: ExtParam> FromStr for SpkExpr<T> {
324-
type Err = Error;
325-
326-
fn from_str(s: &str) -> Result<Self, Self::Err> {
319+
impl<T: ExtParam> ArgFromStr for SpkExpr<T> {
320+
fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
327321
let top = expression::Tree::from_str(s)?;
328-
Self::from_tree(&top)
322+
Self::from_tree_parent(&top, parent, pos)
329323
}
330324
}
331325

332-
impl<T: ExtParam> FromTree for SpkExpr<T> {
333-
fn from_tree(top: &Tree<'_>) -> Result<Self, Error> {
326+
impl<T: ExtParam> SpkExpr<T> {
327+
fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
334328
match (top.name, top.args.len()) {
335329
("curr_inp_spk", 0) => Ok(SpkExpr::CurrInputSpk),
336330
("inp_spk", 1) => expression::terminal(&top.args[0], expression::parse_num::<usize>)
337331
.map(SpkExpr::Input),
338332
("out_spk", 1) => expression::terminal(&top.args[0], expression::parse_num::<usize>)
339333
.map(SpkExpr::Output),
340-
(asset, 0) => Ok(SpkExpr::Const(T::arg_from_str(asset, "", 0)?)),
334+
(asset, 0) => Ok(SpkExpr::Const(T::arg_from_str(asset, parent, pos)?)),
341335
_ => Err(Error::Unexpected(format!(
342336
"{}({} args) while parsing Extension",
343337
top.name,
@@ -385,11 +379,27 @@ impl<T: ExtParam> FromStr for CovOps<T> {
385379
impl<T: ExtParam> FromTree for CovOps<T> {
386380
fn from_tree(top: &Tree<'_>) -> Result<Self, Error> {
387381
match (top.name, top.args.len()) {
388-
("is_exp_asset", 1) => expression::unary(top, CovOps::IsExpAsset),
389-
("is_exp_value", 1) => expression::unary(top, CovOps::IsExpValue),
390-
("asset_eq", 2) => expression::binary(top, CovOps::AssetEq),
391-
("value_eq", 2) => expression::binary(top, CovOps::ValueEq),
392-
("spk_eq", 2) => expression::binary(top, CovOps::SpkEq),
382+
("is_exp_asset", 1) => {
383+
AssetExpr::from_tree_parent(&top.args[0], &top.name, 0).map(CovOps::IsExpAsset)
384+
}
385+
("is_exp_value", 1) => {
386+
ValueExpr::from_tree_parent(&top.args[0], &top.name, 0).map(CovOps::IsExpValue)
387+
}
388+
("asset_eq", 2) => {
389+
let l = AssetExpr::from_tree_parent(&top.args[0], &top.name, 0)?;
390+
let r = AssetExpr::from_tree_parent(&top.args[1], &top.name, 1)?;
391+
Ok(CovOps::AssetEq(l, r))
392+
}
393+
("value_eq", 2) => {
394+
let l = ValueExpr::from_tree_parent(&top.args[0], &top.name, 0)?;
395+
let r = ValueExpr::from_tree_parent(&top.args[1], &top.name, 1)?;
396+
Ok(CovOps::ValueEq(l, r))
397+
}
398+
("spk_eq", 2) => {
399+
let l = SpkExpr::from_tree_parent(&top.args[0], &top.name, 0)?;
400+
let r = SpkExpr::from_tree_parent(&top.args[1], &top.name, 1)?;
401+
Ok(CovOps::SpkEq(l, r))
402+
}
393403
("curr_idx_eq", 1) => {
394404
expression::terminal(&top.args[0], expression::parse_num::<usize>)
395405
.map(CovOps::CurrIndEq)
@@ -424,7 +434,11 @@ impl<T: ExtParam> Extension for CovOps<T> {
424434
fn extra_prop(&self) -> ExtData {
425435
ExtData {
426436
pk_cost: self.script_size(), // 1 opcodes, 1 key push, msg, 1 msg push
427-
has_free_verify: false,
437+
has_free_verify: if let CovOps::CurrIndEq(..) = self {
438+
true
439+
} else {
440+
false
441+
},
428442
stack_elem_count_sat: Some(0),
429443
stack_elem_count_dissat: Some(0),
430444
max_sat_size: Some((0, 0)),
@@ -1144,6 +1158,16 @@ mod tests {
11441158
_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))))");
11451159
}
11461160

1161+
#[test]
1162+
fn options_fail_test() {
1163+
type MsExt = Miniscript<XOnlyPublicKey, Tap, CovOps<CovExtArgs>>;
1164+
1165+
// 33 bytes explicit asset succeeds
1166+
MsExt::from_str_insane("asset_eq(out_asset(0),0179d51a47e4ac8e32306486dd0926a88678c392f2ed5f213e3ff2ad461c7c25e1)").unwrap();
1167+
// 32 bytes explicit asset without prefix fails
1168+
MsExt::from_str_insane("asset_eq(out_asset(0),79d51a47e4ac8e32306486dd0926a88678c392f2ed5f213e3ff2ad461c7c25e1)").unwrap_err();
1169+
}
1170+
11471171
#[rustfmt::skip]
11481172
fn _test_parse(s: &str) {
11491173
type MsExtStr = Miniscript<String, Tap, CovOps<String>>;
@@ -1170,5 +1194,7 @@ mod tests {
11701194
let ms = ms.translate_ext(&mut ext_t).unwrap();
11711195
// script rtt
11721196
assert_eq!(ms.encode(), MsExt::parse_insane(&ms.encode()).unwrap().encode());
1197+
// String rtt of the translated script
1198+
assert_eq!(ms, MsExt::from_str_insane(&ms.to_string()).unwrap())
11731199
}
11741200
}

0 commit comments

Comments
 (0)