mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-26 15:36:19 +01:00
Merge bitcoin/bitcoin#26567: Wallet: estimate the size of signed inputs using descriptors
10546a569cwallet: accurately account for the size of the witness stack (Antoine Poinsot)9b7ec393b8wallet: use descriptor satisfaction size to estimate inputs size (Antoine Poinsot)8d870a9873script/signingprovider: introduce a MultiSigningProvider (Antoine Poinsot)fa7c46b503descriptor: introduce a method to get the satisfaction size (Antoine Poinsot)bdba7667d2miniscript: introduce a helper to get the maximum witness size (Antoine Poinsot)4ab382c2cdminiscript: 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: utACK10546a569cachow101: re-ACK10546a569cTree-SHA512: 43ed1529fbd30af709d903c8c5063235e8c6a03b500bc8f144273d6184e23a53edf0fea9ef898ed57d8a40d73208b5d935cc73b94a24fad3ad3c63b3b2027174
This commit is contained in:
@@ -1703,96 +1703,6 @@ void CWallet::InitWalletFlags(uint64_t flags)
|
||||
if (!LoadWalletFlags(flags)) assert(false);
|
||||
}
|
||||
|
||||
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
|
||||
// or a max-sized low-S signature (e.g. 72 bytes) depending on coin_control
|
||||
bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool can_grind_r, const CCoinControl* coin_control)
|
||||
{
|
||||
// Fill in dummy signatures for fee calculation.
|
||||
const CScript& scriptPubKey = txout.scriptPubKey;
|
||||
SignatureData sigdata;
|
||||
|
||||
// Use max sig if watch only inputs were used, if this particular input is an external input,
|
||||
// or if this wallet uses an external signer, to ensure a sufficient fee is attained for the requested feerate.
|
||||
const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(tx_in.prevout) || !can_grind_r);
|
||||
if (!ProduceSignature(provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
|
||||
return false;
|
||||
}
|
||||
UpdateInput(tx_in, sigdata);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FillInputToWeight(CTxIn& txin, int64_t target_weight)
|
||||
{
|
||||
assert(txin.scriptSig.empty());
|
||||
assert(txin.scriptWitness.IsNull());
|
||||
|
||||
int64_t txin_weight = GetTransactionInputWeight(txin);
|
||||
|
||||
// Do nothing if the weight that should be added is less than the weight that already exists
|
||||
if (target_weight < txin_weight) {
|
||||
return false;
|
||||
}
|
||||
if (target_weight == txin_weight) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Subtract current txin weight, which should include empty witness stack
|
||||
int64_t add_weight = target_weight - txin_weight;
|
||||
assert(add_weight > 0);
|
||||
|
||||
// We will want to subtract the size of the Compact Size UInt that will also be serialized.
|
||||
// However doing so when the size is near a boundary can result in a problem where it is not
|
||||
// possible to have a stack element size and combination to exactly equal a target.
|
||||
// To avoid this possibility, if the weight to add is less than 10 bytes greater than
|
||||
// a boundary, the size will be split so that 2/3rds will be in one stack element, and
|
||||
// the remaining 1/3rd in another. Using 3rds allows us to avoid additional boundaries.
|
||||
// 10 bytes is used because that accounts for the maximum size. This does not need to be super precise.
|
||||
if ((add_weight >= 253 && add_weight < 263)
|
||||
|| (add_weight > std::numeric_limits<uint16_t>::max() && add_weight <= std::numeric_limits<uint16_t>::max() + 10)
|
||||
|| (add_weight > std::numeric_limits<uint32_t>::max() && add_weight <= std::numeric_limits<uint32_t>::max() + 10)) {
|
||||
int64_t first_weight = add_weight / 3;
|
||||
add_weight -= first_weight;
|
||||
|
||||
first_weight -= GetSizeOfCompactSize(first_weight);
|
||||
txin.scriptWitness.stack.emplace(txin.scriptWitness.stack.end(), first_weight, 0);
|
||||
}
|
||||
|
||||
add_weight -= GetSizeOfCompactSize(add_weight);
|
||||
txin.scriptWitness.stack.emplace(txin.scriptWitness.stack.end(), add_weight, 0);
|
||||
assert(GetTransactionInputWeight(txin) == target_weight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
|
||||
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const
|
||||
{
|
||||
// Fill in dummy signatures for fee calculation.
|
||||
int nIn = 0;
|
||||
const bool can_grind_r = CanGrindR();
|
||||
for (const auto& txout : txouts)
|
||||
{
|
||||
CTxIn& txin = txNew.vin[nIn];
|
||||
// If weight was provided, fill the input to that weight
|
||||
if (coin_control && coin_control->HasInputWeight(txin.prevout)) {
|
||||
if (!FillInputToWeight(txin, coin_control->GetInputWeight(txin.prevout))) {
|
||||
return false;
|
||||
}
|
||||
nIn++;
|
||||
continue;
|
||||
}
|
||||
const std::unique_ptr<SigningProvider> provider = GetSolvingProvider(txout.scriptPubKey);
|
||||
if (!provider || !DummySignInput(*provider, txin, txout, can_grind_r, coin_control)) {
|
||||
if (!coin_control || !DummySignInput(coin_control->m_external_provider, txin, txout, can_grind_r, coin_control)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nIn++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp)
|
||||
{
|
||||
auto spk_man = GetLegacyScriptPubKeyMan();
|
||||
|
||||
Reference in New Issue
Block a user