mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-21 15:50:07 +01:00
Allow Coin Selection be able to take external inputs
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <consensus/validation.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <policy/policy.h>
|
||||
#include <script/signingprovider.h>
|
||||
#include <util/check.h>
|
||||
#include <util/fees.h>
|
||||
#include <util/moneystr.h>
|
||||
@@ -31,21 +32,27 @@ std::string COutput::ToString() const
|
||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
|
||||
}
|
||||
|
||||
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
|
||||
int CalculateMaximumSignedInputSize(const CTxOut& txout, const SigningProvider* provider, bool use_max_sig)
|
||||
{
|
||||
CMutableTransaction txn;
|
||||
txn.vin.push_back(CTxIn(COutPoint()));
|
||||
if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
|
||||
if (!provider || !DummySignInput(*provider, txn.vin[0], txout, use_max_sig)) {
|
||||
return -1;
|
||||
}
|
||||
return GetVirtualTransactionInputSize(txn.vin[0]);
|
||||
}
|
||||
|
||||
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
|
||||
{
|
||||
const std::unique_ptr<SigningProvider> provider = wallet->GetSolvingProvider(txout.scriptPubKey);
|
||||
return CalculateMaximumSignedInputSize(txout, provider.get(), use_max_sig);
|
||||
}
|
||||
|
||||
// txouts needs to be in the order of tx.vin
|
||||
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
|
||||
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control)
|
||||
{
|
||||
CMutableTransaction txNew(tx);
|
||||
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
|
||||
if (!wallet->DummySignTx(txNew, txouts, coin_control)) {
|
||||
return TxSize{-1, -1};
|
||||
}
|
||||
CTransaction ctx(txNew);
|
||||
@@ -54,19 +61,27 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
|
||||
return TxSize{vsize, weight};
|
||||
}
|
||||
|
||||
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
|
||||
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const CCoinControl* coin_control)
|
||||
{
|
||||
std::vector<CTxOut> txouts;
|
||||
// Look up the inputs. The inputs are either in the wallet, or in coin_control.
|
||||
for (const CTxIn& input : tx.vin) {
|
||||
const auto mi = wallet->mapWallet.find(input.prevout.hash);
|
||||
// Can not estimate size without knowing the input details
|
||||
if (mi == wallet->mapWallet.end()) {
|
||||
if (mi != wallet->mapWallet.end()) {
|
||||
assert(input.prevout.n < mi->second.tx->vout.size());
|
||||
txouts.emplace_back(mi->second.tx->vout.at(input.prevout.n));
|
||||
} else if (coin_control) {
|
||||
CTxOut txout;
|
||||
if (!coin_control->GetExternalOutput(input.prevout, txout)) {
|
||||
return TxSize{-1, -1};
|
||||
}
|
||||
txouts.emplace_back(txout);
|
||||
} else {
|
||||
return TxSize{-1, -1};
|
||||
}
|
||||
assert(input.prevout.n < mi->second.tx->vout.size());
|
||||
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
|
||||
}
|
||||
return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
|
||||
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
|
||||
}
|
||||
|
||||
void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
|
||||
@@ -435,32 +450,40 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
|
||||
|
||||
std::vector<COutPoint> vPresetInputs;
|
||||
coin_control.ListSelected(vPresetInputs);
|
||||
for (const COutPoint& outpoint : vPresetInputs)
|
||||
{
|
||||
for (const COutPoint& outpoint : vPresetInputs) {
|
||||
int input_bytes = -1;
|
||||
CTxOut txout;
|
||||
std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash);
|
||||
if (it != wallet.mapWallet.end())
|
||||
{
|
||||
if (it != wallet.mapWallet.end()) {
|
||||
const CWalletTx& wtx = it->second;
|
||||
// Clearly invalid input, fail
|
||||
if (wtx.tx->vout.size() <= outpoint.n) {
|
||||
return false;
|
||||
}
|
||||
// Just to calculate the marginal byte size
|
||||
CInputCoin coin(wtx.tx, outpoint.n, GetTxSpendSize(wallet, wtx, outpoint.n, false));
|
||||
nValueFromPresetInputs += coin.txout.nValue;
|
||||
if (coin.m_input_bytes <= 0) {
|
||||
return false; // Not solvable, can't estimate size for fee
|
||||
}
|
||||
coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
|
||||
if (coin_selection_params.m_subtract_fee_outputs) {
|
||||
value_to_select -= coin.txout.nValue;
|
||||
} else {
|
||||
value_to_select -= coin.effective_value;
|
||||
}
|
||||
setPresetCoins.insert(coin);
|
||||
} else {
|
||||
return false; // TODO: Allow non-wallet inputs
|
||||
input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false);
|
||||
txout = wtx.tx->vout.at(outpoint.n);
|
||||
}
|
||||
if (input_bytes == -1) {
|
||||
// The input is external. We either did not find the tx in mapWallet, or we did but couldn't compute the input size with wallet data
|
||||
if (!coin_control.GetExternalOutput(outpoint, txout)) {
|
||||
// Not ours, and we don't have solving data.
|
||||
return false;
|
||||
}
|
||||
input_bytes = CalculateMaximumSignedInputSize(txout, &coin_control.m_external_provider, /* use_max_sig */ true);
|
||||
}
|
||||
|
||||
CInputCoin coin(outpoint, txout, input_bytes);
|
||||
nValueFromPresetInputs += coin.txout.nValue;
|
||||
if (coin.m_input_bytes <= 0) {
|
||||
return false; // Not solvable, can't estimate size for fee
|
||||
}
|
||||
coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
|
||||
if (coin_selection_params.m_subtract_fee_outputs) {
|
||||
value_to_select -= coin.txout.nValue;
|
||||
} else {
|
||||
value_to_select -= coin.effective_value;
|
||||
}
|
||||
setPresetCoins.insert(coin);
|
||||
}
|
||||
|
||||
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
|
||||
@@ -788,10 +811,10 @@ static bool CreateTransactionInternal(
|
||||
}
|
||||
|
||||
// Calculate the transaction fee
|
||||
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
|
||||
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
|
||||
int nBytes = tx_sizes.vsize;
|
||||
if (nBytes < 0) {
|
||||
error = _("Signing transaction failed");
|
||||
error = _("Missing solving data for estimating transaction size");
|
||||
return false;
|
||||
}
|
||||
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
||||
@@ -813,7 +836,7 @@ static bool CreateTransactionInternal(
|
||||
txNew.vout.erase(change_position);
|
||||
|
||||
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
|
||||
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
|
||||
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
|
||||
nBytes = tx_sizes.vsize;
|
||||
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user