Skip to content

Commit d5cfb86

Browse files
committed
Allow Coin Selection be able to take external inputs
1 parent a00eb38 commit d5cfb86

File tree

5 files changed

+101
-46
lines changed

5 files changed

+101
-46
lines changed

src/wallet/coincontrol.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
#include <policy/feerate.h>
1010
#include <policy/fees.h>
1111
#include <primitives/transaction.h>
12+
#include <script/keyorigin.h>
13+
#include <script/signingprovider.h>
1214
#include <script/standard.h>
1315

1416
#include <optional>
17+
#include <algorithm>
18+
#include <map>
19+
#include <set>
1520

1621
const int DEFAULT_MIN_DEPTH = 0;
1722
const int DEFAULT_MAX_DEPTH = 9999999;
@@ -53,6 +58,8 @@ class CCoinControl
5358
int m_min_depth = DEFAULT_MIN_DEPTH;
5459
//! Maximum chain depth value for coin availability
5560
int m_max_depth = DEFAULT_MAX_DEPTH;
61+
//! SigningProvider that has pubkeys and scripts to do spend size estimation for external inputs
62+
FlatSigningProvider m_external_provider;
5663

5764
CCoinControl();
5865

@@ -66,11 +73,32 @@ class CCoinControl
6673
return (setSelected.count(output) > 0);
6774
}
6875

76+
bool IsExternalSelected(const COutPoint& output) const
77+
{
78+
return (m_external_txouts.count(output) > 0);
79+
}
80+
81+
bool GetExternalOutput(const COutPoint& outpoint, CTxOut& txout) const
82+
{
83+
const auto ext_it = m_external_txouts.find(outpoint);
84+
if (ext_it == m_external_txouts.end()) {
85+
return false;
86+
}
87+
txout = ext_it->second;
88+
return true;
89+
}
90+
6991
void Select(const COutPoint& output)
7092
{
7193
setSelected.insert(output);
7294
}
7395

96+
void Select(const COutPoint& outpoint, const CTxOut& txout)
97+
{
98+
setSelected.insert(outpoint);
99+
m_external_txouts.emplace(outpoint, txout);
100+
}
101+
74102
void UnSelect(const COutPoint& output)
75103
{
76104
setSelected.erase(output);
@@ -88,6 +116,7 @@ class CCoinControl
88116

89117
private:
90118
std::set<COutPoint> setSelected;
119+
std::map<COutPoint, CTxOut> m_external_txouts;
91120
};
92121

93122
#endif // BITCOIN_WALLET_COINCONTROL_H

src/wallet/spend.cpp

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <consensus/validation.h>
66
#include <interfaces/chain.h>
77
#include <policy/policy.h>
8+
#include <script/signingprovider.h>
89
#include <util/check.h>
910
#include <util/fees.h>
1011
#include <util/moneystr.h>
@@ -31,21 +32,27 @@ std::string COutput::ToString() const
3132
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
3233
}
3334

34-
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
35+
int CalculateMaximumSignedInputSize(const CTxOut& txout, const SigningProvider* provider, bool use_max_sig)
3536
{
3637
CMutableTransaction txn;
3738
txn.vin.push_back(CTxIn(COutPoint()));
38-
if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
39+
if (!provider || !DummySignInput(*provider, txn.vin[0], txout, use_max_sig)) {
3940
return -1;
4041
}
4142
return GetVirtualTransactionInputSize(txn.vin[0]);
4243
}
4344

45+
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
46+
{
47+
const std::unique_ptr<SigningProvider> provider = wallet->GetSolvingProvider(txout.scriptPubKey);
48+
return CalculateMaximumSignedInputSize(txout, provider.get(), use_max_sig);
49+
}
50+
4451
// txouts needs to be in the order of tx.vin
45-
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
52+
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control)
4653
{
4754
CMutableTransaction txNew(tx);
48-
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
55+
if (!wallet->DummySignTx(txNew, txouts, coin_control)) {
4956
return TxSize{-1, -1};
5057
}
5158
CTransaction ctx(txNew);
@@ -54,19 +61,27 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
5461
return TxSize{vsize, weight};
5562
}
5663

57-
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
64+
TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const CCoinControl* coin_control)
5865
{
5966
std::vector<CTxOut> txouts;
67+
// Look up the inputs. The inputs are either in the wallet, or in coin_control.
6068
for (const CTxIn& input : tx.vin) {
6169
const auto mi = wallet->mapWallet.find(input.prevout.hash);
6270
// Can not estimate size without knowing the input details
63-
if (mi == wallet->mapWallet.end()) {
71+
if (mi != wallet->mapWallet.end()) {
72+
assert(input.prevout.n < mi->second.tx->vout.size());
73+
txouts.emplace_back(mi->second.tx->vout.at(input.prevout.n));
74+
} else if (coin_control) {
75+
CTxOut txout;
76+
if (!coin_control->GetExternalOutput(input.prevout, txout)) {
77+
return TxSize{-1, -1};
78+
}
79+
txouts.emplace_back(txout);
80+
} else {
6481
return TxSize{-1, -1};
6582
}
66-
assert(input.prevout.n < mi->second.tx->vout.size());
67-
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
6883
}
69-
return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
84+
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
7085
}
7186

7287
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
435450

436451
std::vector<COutPoint> vPresetInputs;
437452
coin_control.ListSelected(vPresetInputs);
438-
for (const COutPoint& outpoint : vPresetInputs)
439-
{
453+
for (const COutPoint& outpoint : vPresetInputs) {
454+
int input_bytes = -1;
455+
CTxOut txout;
440456
std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash);
441-
if (it != wallet.mapWallet.end())
442-
{
457+
if (it != wallet.mapWallet.end()) {
443458
const CWalletTx& wtx = it->second;
444459
// Clearly invalid input, fail
445460
if (wtx.tx->vout.size() <= outpoint.n) {
446461
return false;
447462
}
448-
// Just to calculate the marginal byte size
449-
CInputCoin coin(wtx.tx, outpoint.n, GetTxSpendSize(wallet, wtx, outpoint.n, false));
450-
nValueFromPresetInputs += coin.txout.nValue;
451-
if (coin.m_input_bytes <= 0) {
452-
return false; // Not solvable, can't estimate size for fee
453-
}
454-
coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
455-
if (coin_selection_params.m_subtract_fee_outputs) {
456-
value_to_select -= coin.txout.nValue;
457-
} else {
458-
value_to_select -= coin.effective_value;
463+
input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false);
464+
txout = wtx.tx->vout.at(outpoint.n);
465+
}
466+
if (input_bytes == -1) {
467+
// 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
468+
if (!coin_control.GetExternalOutput(outpoint, txout)) {
469+
// Not ours, and we don't have solving data.
470+
return false;
459471
}
460-
setPresetCoins.insert(coin);
472+
input_bytes = CalculateMaximumSignedInputSize(txout, &coin_control.m_external_provider, /* use_max_sig */ true);
473+
}
474+
475+
CInputCoin coin(outpoint, txout, input_bytes);
476+
nValueFromPresetInputs += coin.txout.nValue;
477+
if (coin.m_input_bytes <= 0) {
478+
return false; // Not solvable, can't estimate size for fee
479+
}
480+
coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
481+
if (coin_selection_params.m_subtract_fee_outputs) {
482+
value_to_select -= coin.txout.nValue;
461483
} else {
462-
return false; // TODO: Allow non-wallet inputs
484+
value_to_select -= coin.effective_value;
463485
}
486+
setPresetCoins.insert(coin);
464487
}
465488

466489
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
@@ -788,10 +811,10 @@ static bool CreateTransactionInternal(
788811
}
789812

790813
// Calculate the transaction fee
791-
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
814+
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
792815
int nBytes = tx_sizes.vsize;
793816
if (nBytes < 0) {
794-
error = _("Signing transaction failed");
817+
error = _("Missing solving data for estimating transaction size");
795818
return false;
796819
}
797820
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
@@ -813,7 +836,7 @@ static bool CreateTransactionInternal(
813836
txNew.vout.erase(change_position);
814837

815838
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
816-
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
839+
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
817840
nBytes = tx_sizes.vsize;
818841
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
819842
}

src/wallet/spend.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class COutput
6666

6767
//Get the marginal bytes of spending the specified output
6868
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
69+
int CalculateMaximumSignedInputSize(const CTxOut& txout, const SigningProvider* pwallet, bool use_max_sig = false);
6970

7071
struct TxSize {
7172
int64_t vsize{-1};
@@ -76,8 +77,8 @@ struct TxSize {
7677
* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
7778
* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
7879
* be AllInputsMine). */
79-
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
80-
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
80+
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr);
81+
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
8182

8283
/**
8384
* populate vCoins with vector of available COutputs.

src/wallet/wallet.cpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,34 +1448,35 @@ bool CWallet::AddWalletFlags(uint64_t flags)
14481448

14491449
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
14501450
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true
1451-
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) const
1451+
bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig)
14521452
{
14531453
// Fill in dummy signatures for fee calculation.
14541454
const CScript& scriptPubKey = txout.scriptPubKey;
14551455
SignatureData sigdata;
14561456

1457-
std::unique_ptr<SigningProvider> provider = GetSolvingProvider(scriptPubKey);
1458-
if (!provider) {
1459-
// We don't know about this scriptpbuKey;
1460-
return false;
1461-
}
1462-
1463-
if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
1457+
if (!ProduceSignature(provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
14641458
return false;
14651459
}
14661460
UpdateInput(tx_in, sigdata);
14671461
return true;
14681462
}
14691463

14701464
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
1471-
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig) const
1465+
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const
14721466
{
14731467
// Fill in dummy signatures for fee calculation.
14741468
int nIn = 0;
14751469
for (const auto& txout : txouts)
14761470
{
1477-
if (!DummySignInput(txNew.vin[nIn], txout, use_max_sig)) {
1478-
return false;
1471+
CTxIn& txin = txNew.vin[nIn];
1472+
// Use max sig if watch only inputs were used or if this particular input is an external input
1473+
// to ensure a sufficient fee is attained for the requested feerate.
1474+
const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(txin.prevout));
1475+
const std::unique_ptr<SigningProvider> provider = GetSolvingProvider(txout.scriptPubKey);
1476+
if (!provider || !DummySignInput(*provider, txin, txout, use_max_sig)) {
1477+
if (!coin_control || !DummySignInput(coin_control->m_external_provider, txin, txout, use_max_sig)) {
1478+
return false;
1479+
}
14791480
}
14801481

14811482
nIn++;

src/wallet/wallet.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -576,14 +576,13 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
576576
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
577577
bool SubmitTxMemoryPoolAndRelay(const CWalletTx& wtx, std::string& err_string, bool relay) const;
578578

579-
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
579+
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const
580580
{
581581
std::vector<CTxOut> v_txouts(txouts.size());
582582
std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
583-
return DummySignTx(txNew, v_txouts, use_max_sig);
583+
return DummySignTx(txNew, v_txouts, coin_control);
584584
}
585-
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
586-
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const;
585+
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const;
587586

588587
bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
589588
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -928,4 +927,6 @@ bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
928927
//! Remove wallet name from persistent configuration so it will not be loaded on startup.
929928
bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
930929

930+
bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig);
931+
931932
#endif // BITCOIN_WALLET_WALLET_H

0 commit comments

Comments
 (0)