From 74175941870347458ba8a0074f88b22cb94d0235 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Fri, 15 Apr 2022 14:03:37 +0200 Subject: [PATCH] miniscript: the 'd:' wrapper must not be 'u' The value it leaves on the stack depends on the last element on the stack. However, we can't make sure this element is OP_1 (which would give us the 'u' property) without the MINIMALIF rule. MINIMALIF is only policy for P2WSH, therefore giving 'd:' the 'u' property breaks consensus soundness: it makes it possible (by consensus but not policy) for instance to satisfy a thresh() without satisfying at least k of its subs. This bug was found and reported by Andrew Poelstra. --- src/script/miniscript.cpp | 3 ++- src/test/miniscript_tests.cpp | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/script/miniscript.cpp b/src/script/miniscript.cpp index d0bb937885..019f02f159 100644 --- a/src/script/miniscript.cpp +++ b/src/script/miniscript.cpp @@ -116,7 +116,8 @@ Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vectorGetOps(), 4); // 3 pubkeys + CMS BOOST_CHECK_EQUAL(ms_multi->GetStackSize(), 3); // 1 sig + dummy elem + script push + // The 'd:' wrapper leaves on the stack what was DUP'ed at the beginning of its execution. + // Since it contains an OP_IF just after on the same element, we can make sure that the element + // in question must be OP_1 if OP_IF enforces that its argument must only be OP_1 or the empty + // vector (since otherwise the execution would immediately fail). This is the MINIMALIF rule. + // Unfortunately, this rule is consensus for Taproot but only policy for P2WSH. Therefore we can't + // (for now) have 'd:' be 'u'. This tests we can't use a 'd:' wrapper for a thresh, which requires + // its subs to all be 'u' (taken from https://github.com/rust-bitcoin/rust-miniscript/discussions/341). + const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", CONVERTER); + BOOST_CHECK(!ms_minimalif); // Timelock tests Test("after(100)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock