diff options
author | Andrew Chow <achow101-github@achow101.com> | 2021-10-05 15:54:11 -0400 |
---|---|---|
committer | Andrew Chow <achow101-github@achow101.com> | 2022-01-24 11:23:31 -0500 |
commit | 808068e90e758b9c74878a5235b2c59731fec3e5 (patch) | |
tree | f8b619fd07422b3adb82da4421aa74067bbc778d /src/wallet/wallet.cpp | |
parent | 4060c50d7ee31dc8a39229e3553d3d92f8f3516d (diff) |
wallet: Allow user specified input size to override
If the user specifies an input size, allow it to override any input size
calculations during coin selection.
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r-- | src/wallet/wallet.cpp | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3fcb086d2d..337a5139e1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1507,6 +1507,49 @@ bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut 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 { @@ -1515,6 +1558,14 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> 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; + } // Use max sig if watch only inputs were used or if this particular input is an external input // to ensure a sufficient fee is attained for the requested feerate. const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(txin.prevout)); |