Skip to content

Commit d2ccca2

Browse files
committed
Merge bitcoin/bitcoin#26567: Wallet: estimate the size of signed inputs using descriptors
10546a5 wallet: accurately account for the size of the witness stack (Antoine Poinsot) 9b7ec39 wallet: use descriptor satisfaction size to estimate inputs size (Antoine Poinsot) 8d870a9 script/signingprovider: introduce a MultiSigningProvider (Antoine Poinsot) fa7c46b descriptor: introduce a method to get the satisfaction size (Antoine Poinsot) bdba766 miniscript: introduce a helper to get the maximum witness size (Antoine Poinsot) 4ab382c miniscript: make GetStackSize independent of P2WSH context (Antoine Poinsot) Pull request description: The wallet currently estimates the size of a signed input by doing a dry run of the signing logic. This is unnecessary since all outputs we can sign for can be represented by a descriptor, and we can derive the size of a satisfaction ("signature") directly from the descriptor itself. In addition, the current approach does not generalize well: dry runs of the signing logic are only possible for the most basic scripts. See for instance the discussion in #24149 around that. This introduces a method to get the maximum size of a satisfaction from a descriptor, and makes the wallet use that instead of the dry-run. ACKs for top commit: sipa: utACK 10546a5 achow101: re-ACK 10546a5 Tree-SHA512: 43ed1529fbd30af709d903c8c5063235e8c6a03b500bc8f144273d6184e23a53edf0fea9ef898ed57d8a40d73208b5d935cc73b94a24fad3ad3c63b3b2027174
2 parents cf42182 + 10546a5 commit d2ccca2

15 files changed

+507
-206
lines changed

src/consensus/validation.h

+4
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ static inline int64_t GetTransactionInputWeight(const CTxIn& txin)
158158
// scriptWitness size is added here because witnesses and txins are split up in segwit serialization.
159159
return ::GetSerializeSize(txin, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, PROTOCOL_VERSION);
160160
}
161+
static inline int64_t GetTransactionOutputWeight(const CTxOut& txout)
162+
{
163+
return ::GetSerializeSize(txout, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txout, PROTOCOL_VERSION);
164+
}
161165

162166
/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */
163167
inline int GetWitnessCommitmentIndex(const CBlock& block)

src/script/descriptor.cpp

+167-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <util/vector.h>
2323

2424
#include <memory>
25+
#include <numeric>
2526
#include <optional>
2627
#include <string>
2728
#include <vector>
@@ -706,6 +707,19 @@ class DescriptorImpl : public Descriptor
706707
}
707708

708709
std::optional<OutputType> GetOutputType() const override { return std::nullopt; }
710+
711+
std::optional<int64_t> ScriptSize() const override { return {}; }
712+
713+
/** A helper for MaxSatisfactionWeight.
714+
*
715+
* @param use_max_sig Whether to assume ECDSA signatures will have a high-r.
716+
* @return The maximum size of the satisfaction in raw bytes (with no witness meaning).
717+
*/
718+
virtual std::optional<int64_t> MaxSatSize(bool use_max_sig) const { return {}; }
719+
720+
std::optional<int64_t> MaxSatisfactionWeight(bool) const override { return {}; }
721+
722+
std::optional<int64_t> MaxSatisfactionElems() const override { return {}; }
709723
};
710724

711725
/** A parsed addr(A) descriptor. */
@@ -725,6 +739,8 @@ class AddressDescriptor final : public DescriptorImpl
725739
}
726740
bool IsSingleType() const final { return true; }
727741
bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; }
742+
743+
std::optional<int64_t> ScriptSize() const override { return GetScriptForDestination(m_destination).size(); }
728744
};
729745

730746
/** A parsed raw(H) descriptor. */
@@ -746,6 +762,8 @@ class RawDescriptor final : public DescriptorImpl
746762
}
747763
bool IsSingleType() const final { return true; }
748764
bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; }
765+
766+
std::optional<int64_t> ScriptSize() const override { return m_script.size(); }
749767
};
750768

751769
/** A parsed pk(P) descriptor. */
@@ -766,6 +784,21 @@ class PKDescriptor final : public DescriptorImpl
766784
public:
767785
PKDescriptor(std::unique_ptr<PubkeyProvider> prov, bool xonly = false) : DescriptorImpl(Vector(std::move(prov)), "pk"), m_xonly(xonly) {}
768786
bool IsSingleType() const final { return true; }
787+
788+
std::optional<int64_t> ScriptSize() const override {
789+
return 1 + (m_xonly ? 32 : m_pubkey_args[0]->GetSize()) + 1;
790+
}
791+
792+
std::optional<int64_t> MaxSatSize(bool use_max_sig) const override {
793+
const auto ecdsa_sig_size = use_max_sig ? 72 : 71;
794+
return 1 + (m_xonly ? 65 : ecdsa_sig_size);
795+
}
796+
797+
std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override {
798+
return *MaxSatSize(use_max_sig) * WITNESS_SCALE_FACTOR;
799+
}
800+
801+
std::optional<int64_t> MaxSatisfactionElems() const override { return 1; }
769802
};
770803

771804
/** A parsed pkh(P) descriptor. */
@@ -782,6 +815,19 @@ class PKHDescriptor final : public DescriptorImpl
782815
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pkh") {}
783816
std::optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; }
784817
bool IsSingleType() const final { return true; }
818+
819+
std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 1 + 20 + 1 + 1; }
820+
821+
std::optional<int64_t> MaxSatSize(bool use_max_sig) const override {
822+
const auto sig_size = use_max_sig ? 72 : 71;
823+
return 1 + sig_size + 1 + m_pubkey_args[0]->GetSize();
824+
}
825+
826+
std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override {
827+
return *MaxSatSize(use_max_sig) * WITNESS_SCALE_FACTOR;
828+
}
829+
830+
std::optional<int64_t> MaxSatisfactionElems() const override { return 2; }
785831
};
786832

787833
/** A parsed wpkh(P) descriptor. */
@@ -798,6 +844,19 @@ class WPKHDescriptor final : public DescriptorImpl
798844
WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "wpkh") {}
799845
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
800846
bool IsSingleType() const final { return true; }
847+
848+
std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 20; }
849+
850+
std::optional<int64_t> MaxSatSize(bool use_max_sig) const override {
851+
const auto sig_size = use_max_sig ? 72 : 71;
852+
return (1 + sig_size + 1 + 33);
853+
}
854+
855+
std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override {
856+
return MaxSatSize(use_max_sig);
857+
}
858+
859+
std::optional<int64_t> MaxSatisfactionElems() const override { return 2; }
801860
};
802861

803862
/** A parsed combo(P) descriptor. */
@@ -842,6 +901,24 @@ class MultisigDescriptor final : public DescriptorImpl
842901
public:
843902
MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {}
844903
bool IsSingleType() const final { return true; }
904+
905+
std::optional<int64_t> ScriptSize() const override {
906+
const auto n_keys = m_pubkey_args.size();
907+
auto op = [](int64_t acc, const std::unique_ptr<PubkeyProvider>& pk) { return acc + 1 + pk->GetSize();};
908+
const auto pubkeys_size{std::accumulate(m_pubkey_args.begin(), m_pubkey_args.end(), int64_t{0}, op)};
909+
return 1 + BuildScript(n_keys).size() + BuildScript(m_threshold).size() + pubkeys_size;
910+
}
911+
912+
std::optional<int64_t> MaxSatSize(bool use_max_sig) const override {
913+
const auto sig_size = use_max_sig ? 72 : 71;
914+
return (1 + (1 + sig_size) * m_threshold);
915+
}
916+
917+
std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override {
918+
return *MaxSatSize(use_max_sig) * WITNESS_SCALE_FACTOR;
919+
}
920+
921+
std::optional<int64_t> MaxSatisfactionElems() const override { return 1 + m_threshold; }
845922
};
846923

847924
/** A parsed (sorted)multi_a(...) descriptor. Always uses x-only pubkeys. */
@@ -867,6 +944,17 @@ class MultiADescriptor final : public DescriptorImpl
867944
public:
868945
MultiADescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti_a" : "multi_a"), m_threshold(threshold), m_sorted(sorted) {}
869946
bool IsSingleType() const final { return true; }
947+
948+
std::optional<int64_t> ScriptSize() const override {
949+
const auto n_keys = m_pubkey_args.size();
950+
return (1 + 32 + 1) * n_keys + BuildScript(m_threshold).size() + 1;
951+
}
952+
953+
std::optional<int64_t> MaxSatSize(bool use_max_sig) const override {
954+
return (1 + 65) * m_threshold + (m_pubkey_args.size() - m_threshold);
955+
}
956+
957+
std::optional<int64_t> MaxSatisfactionElems() const override { return m_pubkey_args.size(); }
870958
};
871959

872960
/** A parsed sh(...) descriptor. */
@@ -879,16 +967,39 @@ class SHDescriptor final : public DescriptorImpl
879967
if (ret.size()) out.scripts.emplace(CScriptID(scripts[0]), scripts[0]);
880968
return ret;
881969
}
970+
971+
bool IsSegwit() const { return m_subdescriptor_args[0]->GetOutputType() == OutputType::BECH32; }
972+
882973
public:
883974
SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
884975

885976
std::optional<OutputType> GetOutputType() const override
886977
{
887978
assert(m_subdescriptor_args.size() == 1);
888-
if (m_subdescriptor_args[0]->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT;
979+
if (IsSegwit()) return OutputType::P2SH_SEGWIT;
889980
return OutputType::LEGACY;
890981
}
891982
bool IsSingleType() const final { return true; }
983+
984+
std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 20 + 1; }
985+
986+
std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override {
987+
if (const auto sat_size = m_subdescriptor_args[0]->MaxSatSize(use_max_sig)) {
988+
if (const auto subscript_size = m_subdescriptor_args[0]->ScriptSize()) {
989+
// The subscript is never witness data.
990+
const auto subscript_weight = (1 + *subscript_size) * WITNESS_SCALE_FACTOR;
991+
// The weight depends on whether the inner descriptor is satisfied using the witness stack.
992+
if (IsSegwit()) return subscript_weight + *sat_size;
993+
return subscript_weight + *sat_size * WITNESS_SCALE_FACTOR;
994+
}
995+
}
996+
return {};
997+
}
998+
999+
std::optional<int64_t> MaxSatisfactionElems() const override {
1000+
if (const auto sub_elems = m_subdescriptor_args[0]->MaxSatisfactionElems()) return 1 + *sub_elems;
1001+
return {};
1002+
}
8921003
};
8931004

8941005
/** A parsed wsh(...) descriptor. */
@@ -905,6 +1016,26 @@ class WSHDescriptor final : public DescriptorImpl
9051016
WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {}
9061017
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
9071018
bool IsSingleType() const final { return true; }
1019+
1020+
std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 32; }
1021+
1022+
std::optional<int64_t> MaxSatSize(bool use_max_sig) const override {
1023+
if (const auto sat_size = m_subdescriptor_args[0]->MaxSatSize(use_max_sig)) {
1024+
if (const auto subscript_size = m_subdescriptor_args[0]->ScriptSize()) {
1025+
return GetSizeOfCompactSize(*subscript_size) + *subscript_size + *sat_size;
1026+
}
1027+
}
1028+
return {};
1029+
}
1030+
1031+
std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override {
1032+
return MaxSatSize(use_max_sig);
1033+
}
1034+
1035+
std::optional<int64_t> MaxSatisfactionElems() const override {
1036+
if (const auto sub_elems = m_subdescriptor_args[0]->MaxSatisfactionElems()) return 1 + *sub_elems;
1037+
return {};
1038+
}
9081039
};
9091040

9101041
/** A parsed tr(...) descriptor. */
@@ -958,6 +1089,18 @@ class TRDescriptor final : public DescriptorImpl
9581089
}
9591090
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32M; }
9601091
bool IsSingleType() const final { return true; }
1092+
1093+
std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 32; }
1094+
1095+
std::optional<int64_t> MaxSatisfactionWeight(bool) const override {
1096+
// FIXME: We assume keypath spend, which can lead to very large underestimations.
1097+
return 1 + 65;
1098+
}
1099+
1100+
std::optional<int64_t> MaxSatisfactionElems() const override {
1101+
// FIXME: See above, we assume keypath spend.
1102+
return 1;
1103+
}
9611104
};
9621105

9631106
/* We instantiate Miniscript here with a simple integer as key type.
@@ -1041,6 +1184,17 @@ class MiniscriptDescriptor final : public DescriptorImpl
10411184

10421185
bool IsSolvable() const override { return true; }
10431186
bool IsSingleType() const final { return true; }
1187+
1188+
std::optional<int64_t> ScriptSize() const override { return m_node->ScriptSize(); }
1189+
1190+
std::optional<int64_t> MaxSatSize(bool) const override {
1191+
// For Miniscript we always assume high-R ECDSA signatures.
1192+
return m_node->GetWitnessSize();
1193+
}
1194+
1195+
std::optional<int64_t> MaxSatisfactionElems() const override {
1196+
return m_node->GetStackSize();
1197+
}
10441198
};
10451199

10461200
/** A parsed rawtr(...) descriptor. */
@@ -1059,6 +1213,18 @@ class RawTRDescriptor final : public DescriptorImpl
10591213
RawTRDescriptor(std::unique_ptr<PubkeyProvider> output_key) : DescriptorImpl(Vector(std::move(output_key)), "rawtr") {}
10601214
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32M; }
10611215
bool IsSingleType() const final { return true; }
1216+
1217+
std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 32; }
1218+
1219+
std::optional<int64_t> MaxSatisfactionWeight(bool) const override {
1220+
// We can't know whether there is a script path, so assume key path spend.
1221+
return 1 + 65;
1222+
}
1223+
1224+
std::optional<int64_t> MaxSatisfactionElems() const override {
1225+
// See above, we assume keypath spend.
1226+
return 1;
1227+
}
10621228
};
10631229

10641230
////////////////////////////////////////////////////////////////////////////

src/script/descriptor.h

+12
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ struct Descriptor {
146146

147147
/** @return The OutputType of the scriptPubKey(s) produced by this descriptor. Or nullopt if indeterminate (multiple or none) */
148148
virtual std::optional<OutputType> GetOutputType() const = 0;
149+
150+
/** Get the size of the scriptPubKey for this descriptor. */
151+
virtual std::optional<int64_t> ScriptSize() const = 0;
152+
153+
/** Get the maximum size of a satisfaction for this descriptor, in weight units.
154+
*
155+
* @param use_max_sig Whether to assume ECDSA signatures will have a high-r.
156+
*/
157+
virtual std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const = 0;
158+
159+
/** Get the maximum size number of stack elements for satisfying this descriptor. */
160+
virtual std::optional<int64_t> MaxSatisfactionElems() const = 0;
149161
};
150162

151163
/** Parse a `descriptor` string. Included private keys are put in `out`.

0 commit comments

Comments
 (0)