From 8228521a6ee224e00be014a07df4401dc2b1cb5c Mon Sep 17 00:00:00 2001 From: tnnd Date: Fri, 7 Mar 2025 10:03:54 -0500 Subject: [PATCH] miniscript: fixes #29098 by only use first k valid signatures --- src/script/miniscript.h | 52 +++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/script/miniscript.h b/src/script/miniscript.h index 75f978457c5..e92e9a676b7 100644 --- a/src/script/miniscript.h +++ b/src/script/miniscript.h @@ -1183,32 +1183,50 @@ private: return {ZERO + InputStack(key), (InputStack(std::move(sig)).SetWithSig() + InputStack(key)).SetAvailable(avail)}; } case Fragment::MULTI_A: { - // sats[j] represents the best stack containing j valid signatures (out of the first i keys). - // In the loop below, these stacks are built up using a dynamic programming approach. - std::vector sats = Vector(EMPTY); + // take the first k number of valid signatures out of node.keys + // if number of validate signatures is less than node.k, return an empty InputStack with Availability::NO + InputStack sat_return; + unsigned int num_of_good_sigs = 0; for (size_t i = 0; i < node.keys.size(); ++i) { - // Get the signature for the i'th key in reverse order (the signature for the first key needs to - // be at the top of the stack, contrary to CHECKMULTISIG's satisfaction). std::vector sig; Availability avail = ctx.Sign(node.keys[node.keys.size() - 1 - i], sig); // Compute signature stack for just this key. auto sat = InputStack(std::move(sig)).SetWithSig().SetAvailable(avail); - // Compute the next sats vector: next_sats[0] is a copy of sats[0] (no signatures). All further - // next_sats[j] are equal to either the existing sats[j] + ZERO, or sats[j-1] plus a signature - // for the current (i'th) key. The very last element needs all signatures filled. - std::vector next_sats; - next_sats.push_back(sats[0] + ZERO); - for (size_t j = 1; j < sats.size(); ++j) next_sats.push_back((sats[j] + ZERO) | (std::move(sats[j - 1]) + sat)); - next_sats.push_back(std::move(sats[sats.size() - 1]) + std::move(sat)); - // Switch over. - sats = std::move(next_sats); + if (sat.available == Availability::YES && num_of_good_sigs < node.k) { + ++num_of_good_sigs; + if (sat_return.available == Availability::NO) { + // need to prepend i-1 number of ZEROs to fix the boundary condition that when sat_return is Availability::NO, then the operator+ will clear the stacks when add with sat with valid stacks + auto temp_sat = ZERO; + for (size_t k=0; k < i-1; ++k) + temp_sat = temp_sat + ZERO; + sat_return = std::move(temp_sat) + std::move(sat); + } else { + sat_return = std::move(sat_return) + std::move(sat); + } + } else { + if (sat.available == Availability::NO && sat_return.has_sig == false) { + // when sat is Availability::NO, sat_return should be overwritten by sat: + sat_return = std::move(sat); + } else { + sat_return = std::move(sat_return) + ZERO; + } + } + } + // for nsat_return, it expects node.keys.size() number of ZEROs, thus adding this for loop + InputStack nsat_return; + for (size_t i = 0; i < node.keys.size(); ++i) { + nsat_return = std::move(nsat_return) + ZERO; } // The dissatisfaction consists of as many empty vectors as there are keys, which is the same as // satisfying 0 keys. - auto& nsat{sats[0]}; + auto& nsat{nsat_return}; assert(node.k != 0); - assert(node.k <= sats.size()); - return {std::move(nsat), std::move(sats[node.k])}; + if (num_of_good_sigs < node.k) + { + // this is to reset sat_return when there are not enough k numbers of valid signatures + sat_return.SetAvailable(Availability::NO); + } + return {std::move(nsat), std::move(sat_return)}; } case Fragment::MULTI: { // sats[j] represents the best stack containing j valid signatures (out of the first i keys).