Skip to content

Commit fb82d91

Browse files
committed
Merge bitcoin#24149: Signing support for Miniscript Descriptors
6c7a17a psbt: support externally provided preimages for Miniscript satisfaction (Antoine Poinsot) 840a396 qa: add a "smart" Miniscript fuzz target (Antoine Poinsot) 17e3547 qa: add a fuzz target generating random nodes from a binary encoding (Antoine Poinsot) 611e125 qa: functional test Miniscript signing with key and timelocks (Antoine Poinsot) d57b7f2 refactor: make descriptors in Miniscript functional test more readable (Antoine Poinsot) 0a8fc9e wallet: check solvability using descriptor in AvailableCoins (Antoine Poinsot) 560e62b script/sign: signing support for Miniscripts with hash preimage challenges (Antoine Poinsot) a2f81b6 script/sign: signing support for Miniscript with timelocks (Antoine Poinsot) 61c6d1a script/sign: basic signing support for Miniscript descriptors (Antoine Poinsot) 4242c1c Align 'e' property of or_d and andor with website spec (Pieter Wuille) f5deb41 Various additional explanations of the satisfaction logic from Pieter (Pieter Wuille) 22c5b00 miniscript: satisfaction support (Antoine Poinsot) Pull request description: This makes the Miniscript descriptors solvable. Note this introduces signing support for much more complex scripts than the wallet was previously able to solve, and the whole tooling isn't provided for a complete Miniscript integration in the wallet. Particularly, the PSBT<->Miniscript integration isn't entirely covered in this PR. ACKs for top commit: achow101: ACK 6c7a17a sipa: utACK 6c7a17a (to the extent that it's not my own code). Tree-SHA512: a71ec002aaf66bd429012caa338fc58384067bcd2f453a46e21d381ed1bacc8e57afb9db57c0fb4bf40de43b30808815e9ebc0ae1fbd9e61df0e7b91a17771cc
2 parents cbf511b + 6c7a17a commit fb82d91

17 files changed

+1879
-58
lines changed

src/psbt.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,18 @@ void PSBTInput::FillSignatureData(SignatureData& sigdata) const
132132
for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) {
133133
sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin);
134134
}
135+
for (const auto& [hash, preimage] : ripemd160_preimages) {
136+
sigdata.ripemd160_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
137+
}
138+
for (const auto& [hash, preimage] : sha256_preimages) {
139+
sigdata.sha256_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
140+
}
141+
for (const auto& [hash, preimage] : hash160_preimages) {
142+
sigdata.hash160_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
143+
}
144+
for (const auto& [hash, preimage] : hash256_preimages) {
145+
sigdata.hash256_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
146+
}
135147
}
136148

137149
void PSBTInput::FromSignatureData(const SignatureData& sigdata)

src/script/descriptor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,7 @@ class MiniscriptDescriptor final : public DescriptorImpl
10141014
return false;
10151015
}
10161016

1017-
bool IsSolvable() const override { return false; } // For now, mark these descriptors as non-solvable (as we don't have signing logic for them).
1017+
bool IsSolvable() const override { return true; }
10181018
bool IsSingleType() const final { return true; }
10191019
};
10201020

src/script/miniscript.cpp

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
172172
(y & "B"_mst).If(x << "Bdu"_mst) | // B=B_y*B_x*d_x*u_x
173173
(x & "o"_mst).If(y << "z"_mst) | // o=o_x*z_y
174174
(x & y & "m"_mst).If(x << "e"_mst && (x | y) << "s"_mst) | // m=m_x*m_y*e_x*(s_x+s_y)
175-
(x & y & "zes"_mst) | // z=z_x*z_y, e=e_x*e_y, s=s_x*s_y
176-
(y & "ufd"_mst) | // u=u_y, f=f_y, d=d_y
175+
(x & y & "zs"_mst) | // z=z_x*z_y, s=s_x*s_y
176+
(y & "ufde"_mst) | // u=u_y, f=f_y, d=d_y, e=e_y
177177
"x"_mst | // x
178178
((x | y) & "ghij"_mst) | // g=g_x+g_y, h=h_x+h_y, i=i_x+i_y, j=j_x+j_y
179179
(x & y & "k"_mst); // k=k_x*k_y
@@ -201,7 +201,7 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
201201
(y & z & "u"_mst) | // u=u_y*u_z
202202
(z & "f"_mst).If((x << "s"_mst) || (y << "f"_mst)) | // f=(s_x+f_y)*f_z
203203
(z & "d"_mst) | // d=d_z
204-
(x & z & "e"_mst).If(x << "s"_mst || y << "f"_mst) | // e=e_x*e_z*(s_x+f_y)
204+
(z & "e"_mst).If(x << "s"_mst || y << "f"_mst) | // e=e_z*(s_x+f_y)
205205
(x & y & z & "m"_mst).If(x << "e"_mst && (x | y | z) << "s"_mst) | // m=m_x*m_y*m_z*e_x*(s_x+s_y+s_z)
206206
(z & (x | y) & "s"_mst) | // s=s_z*(s_x+s_y)
207207
"x"_mst | // x
@@ -279,6 +279,76 @@ size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_
279279
assert(false);
280280
}
281281

282+
InputStack& InputStack::SetAvailable(Availability avail) {
283+
available = avail;
284+
if (avail == Availability::NO) {
285+
stack.clear();
286+
size = std::numeric_limits<size_t>::max();
287+
has_sig = false;
288+
malleable = false;
289+
non_canon = false;
290+
}
291+
return *this;
292+
}
293+
294+
InputStack& InputStack::SetWithSig() {
295+
has_sig = true;
296+
return *this;
297+
}
298+
299+
InputStack& InputStack::SetNonCanon() {
300+
non_canon = true;
301+
return *this;
302+
}
303+
304+
InputStack& InputStack::SetMalleable(bool x) {
305+
malleable = x;
306+
return *this;
307+
}
308+
309+
InputStack operator+(InputStack a, InputStack b) {
310+
a.stack = Cat(std::move(a.stack), std::move(b.stack));
311+
if (a.available != Availability::NO && b.available != Availability::NO) a.size += b.size;
312+
a.has_sig |= b.has_sig;
313+
a.malleable |= b.malleable;
314+
a.non_canon |= b.non_canon;
315+
if (a.available == Availability::NO || b.available == Availability::NO) {
316+
a.SetAvailable(Availability::NO);
317+
} else if (a.available == Availability::MAYBE || b.available == Availability::MAYBE) {
318+
a.SetAvailable(Availability::MAYBE);
319+
}
320+
return a;
321+
}
322+
323+
InputStack operator|(InputStack a, InputStack b) {
324+
// If only one is invalid, pick the other one. If both are invalid, pick an arbitrary one.
325+
if (a.available == Availability::NO) return b;
326+
if (b.available == Availability::NO) return a;
327+
// If only one of the solutions has a signature, we must pick the other one.
328+
if (!a.has_sig && b.has_sig) return a;
329+
if (!b.has_sig && a.has_sig) return b;
330+
if (!a.has_sig && !b.has_sig) {
331+
// If neither solution requires a signature, the result is inevitably malleable.
332+
a.malleable = true;
333+
b.malleable = true;
334+
} else {
335+
// If both options require a signature, prefer the non-malleable one.
336+
if (b.malleable && !a.malleable) return a;
337+
if (a.malleable && !b.malleable) return b;
338+
}
339+
// Between two malleable or two non-malleable solutions, pick the smaller one between
340+
// YESes, and the bigger ones between MAYBEs. Prefer YES over MAYBE.
341+
if (a.available == Availability::YES && b.available == Availability::YES) {
342+
return std::move(a.size <= b.size ? a : b);
343+
} else if (a.available == Availability::MAYBE && b.available == Availability::MAYBE) {
344+
return std::move(a.size >= b.size ? a : b);
345+
} else if (a.available == Availability::YES) {
346+
return a;
347+
} else {
348+
return b;
349+
}
350+
}
351+
282352
std::optional<std::vector<Opcode>> DecomposeScript(const CScript& script)
283353
{
284354
std::vector<Opcode> out;

0 commit comments

Comments
 (0)