Skip to content

Commit 1311c97

Browse files
committed
Merge #269: Fix decoding bug
87c65b6 Add breaking test (sanket1729) e55ef58 Fix decoding up; cleanup code (sanket1729) Pull request description: Caught while reviewing c++ implementation(PR from meshcollider). ACKs for top commit: apoelstra: ACK 87c65b6 Tree-SHA512: f4a32de573d0996818f3653f9913508bfd74921a8fb92b00d2f65097d08ea804323c69304d02330507655671273fea2967e97d953c6f372b1a7e44adba0fd989
2 parents 5048df6 + 87c65b6 commit 1311c97

File tree

2 files changed

+49
-26
lines changed

2 files changed

+49
-26
lines changed

src/miniscript/decode.rs

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ mod private {
9898
#[derive(Copy, Clone, Debug)]
9999
enum NonTerm {
100100
Expression,
101-
MaybeSwap,
101+
WExpression,
102+
Swap,
102103
MaybeAndV,
103104
Alt,
104105
Check,
@@ -208,6 +209,7 @@ macro_rules! match_token {
208209
}
209210

210211
///Vec representing terminals stack while decoding.
212+
#[derive(Debug)]
211213
struct TerminalStack<Pk: MiniscriptKey, Ctx: ScriptContext>(Vec<Miniscript<Pk, Ctx>>);
212214

213215
impl<Pk: MiniscriptKey, Ctx: ScriptContext> TerminalStack<Pk, Ctx> {
@@ -284,8 +286,8 @@ pub fn parse<Ctx: ScriptContext>(
284286
let mut non_term = Vec::with_capacity(tokens.len());
285287
let mut term = TerminalStack(Vec::with_capacity(tokens.len()));
286288

289+
// top level cannot be swap, must be B
287290
non_term.push(NonTerm::MaybeAndV);
288-
non_term.push(NonTerm::MaybeSwap);
289291
non_term.push(NonTerm::Expression);
290292
loop {
291293
match non_term.pop() {
@@ -439,34 +441,24 @@ pub fn parse<Ctx: ScriptContext>(
439441
// `OP_ADD` or not and do the right thing
440442
},
441443
),
442-
// fromaltstack
443-
Tk::FromAltStack => {
444-
non_term.push(NonTerm::Alt);
445-
non_term.push(NonTerm::MaybeAndV);
446-
non_term.push(NonTerm::MaybeSwap);
447-
non_term.push(NonTerm::Expression);
448-
},
449444
// most other fragments
450445
Tk::Num(0) => term.reduce0(Terminal::False)?,
451446
Tk::Num(1) => term.reduce0(Terminal::True)?,
452447
Tk::EndIf => {
453448
non_term.push(NonTerm::EndIf);
454449
non_term.push(NonTerm::MaybeAndV);
455-
non_term.push(NonTerm::MaybeSwap);
456450
non_term.push(NonTerm::Expression);
457451
},
458452
// boolean conjunctions and disjunctions
459453
Tk::BoolAnd => {
460454
non_term.push(NonTerm::AndB);
461455
non_term.push(NonTerm::Expression);
462-
non_term.push(NonTerm::MaybeSwap);
463-
non_term.push(NonTerm::Expression);
456+
non_term.push(NonTerm::WExpression);
464457
},
465458
Tk::BoolOr => {
466459
non_term.push(NonTerm::OrB);
467460
non_term.push(NonTerm::Expression);
468-
non_term.push(NonTerm::MaybeSwap);
469-
non_term.push(NonTerm::Expression);
461+
non_term.push(NonTerm::WExpression);
470462
},
471463
// CHECKMULTISIG based multisig
472464
Tk::CheckMultiSig, Tk::Num(n) => {
@@ -522,15 +514,14 @@ pub fn parse<Ctx: ScriptContext>(
522514
non_term.push(NonTerm::Expression);
523515
}
524516
}
525-
Some(NonTerm::MaybeSwap) => {
517+
Some(NonTerm::Swap) => {
526518
// Handle `SWAP` prefixing
527-
if let Some(&Tk::Swap) = tokens.peek() {
528-
tokens.next();
529-
// let top = term.pop().unwrap();
530-
term.reduce1(Terminal::Swap)?;
531-
// term.push(Terminal::Swap(Arc::new(top)));
532-
non_term.push(NonTerm::MaybeSwap);
533-
}
519+
match_token!(
520+
tokens,
521+
Tk::Swap => {},
522+
);
523+
term.reduce1(Terminal::Swap)?;
524+
// Swap must be always be terminating a NonTerm as it cannot be in and_v
534525
}
535526
Some(NonTerm::Alt) => {
536527
match_token!(
@@ -577,14 +568,14 @@ pub fn parse<Ctx: ScriptContext>(
577568
tokens,
578569
Tk::Add => {
579570
non_term.push(NonTerm::ThreshW { n: n + 1, k });
571+
non_term.push(NonTerm::WExpression);
580572
},
581573
x => {
582574
tokens.un_next(x);
583575
non_term.push(NonTerm::ThreshE { n: n + 1, k });
576+
non_term.push(NonTerm::Expression);
584577
},
585578
);
586-
non_term.push(NonTerm::MaybeSwap);
587-
non_term.push(NonTerm::Expression);
588579
}
589580
Some(NonTerm::ThreshE { n, k }) => {
590581
let mut subs = Vec::with_capacity(n);
@@ -599,7 +590,6 @@ pub fn parse<Ctx: ScriptContext>(
599590
Tk::Else => {
600591
non_term.push(NonTerm::EndIfElse);
601592
non_term.push(NonTerm::MaybeAndV);
602-
non_term.push(NonTerm::MaybeSwap);
603593
non_term.push(NonTerm::Expression);
604594
},
605595
Tk::If => match_token!(
@@ -636,6 +626,14 @@ pub fn parse<Ctx: ScriptContext>(
636626
},
637627
);
638628
}
629+
Some(NonTerm::WExpression) => {
630+
// W expression must be either from swap or Fromaltstack
631+
match_token!(tokens,
632+
Tk::FromAltStack => { non_term.push(NonTerm::Alt);},
633+
tok => { tokens.un_next(tok); non_term.push(NonTerm::Swap);},);
634+
non_term.push(NonTerm::MaybeAndV);
635+
non_term.push(NonTerm::Expression);
636+
}
639637
None => {
640638
// Done :)
641639
break;
@@ -650,7 +648,12 @@ pub fn parse<Ctx: ScriptContext>(
650648

651649
fn is_and_v(tokens: &mut TokenIter) -> bool {
652650
match tokens.peek() {
653-
None | Some(&Tk::If) | Some(&Tk::NotIf) | Some(&Tk::Else) | Some(&Tk::ToAltStack) => false,
651+
None
652+
| Some(&Tk::If)
653+
| Some(&Tk::NotIf)
654+
| Some(&Tk::Else)
655+
| Some(&Tk::ToAltStack)
656+
| Some(&Tk::Swap) => false,
654657
_ => true,
655658
}
656659
}

src/miniscript/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,4 +1072,24 @@ mod tests {
10721072
let wit = tap_ms.satisfy(s).unwrap();
10731073
assert_eq!(wit, vec![schnorr_sig.as_ref().to_vec(), vec![], vec![]]);
10741074
}
1075+
1076+
#[test]
1077+
fn decode_bug_cpp_review() {
1078+
let ms = Miniscript::<String, Segwitv0>::from_str_insane(
1079+
"and_b(1,s:and_v(v:older(9),c:pk_k(A)))",
1080+
)
1081+
.unwrap();
1082+
let ms_trans = ms.translate_pk_infallible(
1083+
|_x| {
1084+
bitcoin::PublicKey::from_str(
1085+
"02fbcf092916824cc56c4591abeedd54586f5ffc73c6ba88118162e3500ad695ea",
1086+
)
1087+
.unwrap()
1088+
},
1089+
|_x| unreachable!(),
1090+
);
1091+
let enc = ms_trans.encode();
1092+
let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse_insane(&enc).unwrap();
1093+
assert_eq!(ms_trans.encode(), ms.encode());
1094+
}
10751095
}

0 commit comments

Comments
 (0)