Skip to content

Commit 8258aaa

Browse files
committed
Allow Coin Selection be able to take external inputs
1 parent 698340b commit 8258aaa

File tree

4 files changed

+79
-29
lines changed

4 files changed

+79
-29
lines changed

src/wallet/coincontrol.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,7 @@ void CCoinControl::SetNull()
1919
m_confirm_target.reset();
2020
m_signal_bip125_rbf.reset();
2121
m_fee_mode = FeeEstimateMode::UNSET;
22+
m_external_txouts.clear();
23+
m_external_provider = FlatSigningProvider();
2224
}
2325

src/wallet/coincontrol.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class CCoinControl
3737
bool m_avoid_partial_spends;
3838
//! Fee estimation mode to control arguments to estimateSmartFee
3939
FeeEstimateMode m_fee_mode;
40+
//! SigningProvider that has pubkeys and scripts to do spend size estimation for external inputs
41+
FlatSigningProvider m_external_provider;
4042

4143
CCoinControl()
4244
{
@@ -55,11 +57,32 @@ class CCoinControl
5557
return (setSelected.count(output) > 0);
5658
}
5759

60+
bool IsExternalSelected(const COutPoint& output) const
61+
{
62+
return (m_external_txouts.count(output) > 0);
63+
}
64+
65+
bool GetExternalOutput(const COutPoint& outpoint, CTxOut& txout) const
66+
{
67+
const auto ext_it = m_external_txouts.find(outpoint);
68+
if (ext_it == m_external_txouts.end()) {
69+
return false;
70+
}
71+
txout = ext_it->second;
72+
return true;
73+
}
74+
5875
void Select(const COutPoint& output)
5976
{
6077
setSelected.insert(output);
6178
}
6279

80+
void SelectExternal(const COutPoint& outpoint, const CTxOut& txout)
81+
{
82+
setSelected.insert(outpoint);
83+
m_external_txouts.emplace(outpoint, txout);
84+
}
85+
6386
void UnSelect(const COutPoint& output)
6487
{
6588
setSelected.erase(output);
@@ -77,6 +100,7 @@ class CCoinControl
77100

78101
private:
79102
std::set<COutPoint> setSelected;
103+
std::map<COutPoint, CTxOut> m_external_txouts;
80104
};
81105

82106
#endif // BITCOIN_WALLET_COINCONTROL_H

src/wallet/wallet.cpp

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,57 +1626,66 @@ int64_t CWalletTx::GetTxTime() const
16261626

16271627
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
16281628
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true
1629-
bool CWallet::DummySignInput(CMutableTransaction& tx, const size_t nIn, const CTxOut& txout, bool use_max_sig) const
1629+
static bool DummySignInput(const SigningProvider* provider, CMutableTransaction& tx, const size_t nIn, const CTxOut& txout, bool use_max_sig)
16301630
{
16311631
// Fill in dummy signatures for fee calculation.
16321632
const CScript& scriptPubKey = txout.scriptPubKey;
16331633
SignatureData sigdata;
16341634

1635-
if (!ProduceSignature(*this, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
1635+
if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
16361636
return false;
16371637
}
16381638
UpdateTransaction(tx, nIn, sigdata);
16391639
return true;
16401640
}
16411641

16421642
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
1643-
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig) const
1643+
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const
16441644
{
16451645
// Fill in dummy signatures for fee calculation.
16461646
int nIn = 0;
16471647
for (const auto& txout : txouts)
16481648
{
1649-
if (!DummySignInput(txNew, nIn, txout, use_max_sig)) {
1650-
return false;
1649+
// Use max sig if watch only inputs were used or if this particular input is an external input
1650+
bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || (coin_control && coin_control->IsExternalSelected(txNew.vin[nIn].prevout)));
1651+
if (!DummySignInput(this, txNew, nIn, txout, use_max_sig)) {
1652+
if (!coin_control || !DummySignInput(&coin_control->m_external_provider, txNew, nIn, txout, use_max_sig)) {
1653+
return false;
1654+
}
16511655
}
16521656

16531657
nIn++;
16541658
}
16551659
return true;
16561660
}
16571661

1658-
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
1662+
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const CCoinControl* coin_control)
16591663
{
16601664
std::vector<CTxOut> txouts;
1661-
// Look up the inputs. We should have already checked that this transaction
1662-
// IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
1663-
// wallet, with a valid index into the vout array, and the ability to sign.
1665+
// Look up the inputs. The inputs are either in the wallet, or in coin_control.
16641666
for (const CTxIn& input : tx.vin) {
16651667
const auto mi = wallet->mapWallet.find(input.prevout.hash);
1666-
if (mi == wallet->mapWallet.end()) {
1668+
if (mi != wallet->mapWallet.end()) {
1669+
assert(input.prevout.n < mi->second.tx->vout.size());
1670+
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
1671+
} else if (coin_control) {
1672+
CTxOut txout;
1673+
if (!coin_control->GetExternalOutput(input.prevout, txout)) {
1674+
return -1;
1675+
}
1676+
txouts.emplace_back(txout);
1677+
} else {
16671678
return -1;
16681679
}
1669-
assert(input.prevout.n < mi->second.tx->vout.size());
1670-
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
16711680
}
1672-
return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
1681+
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
16731682
}
16741683

16751684
// txouts needs to be in the order of tx.vin
1676-
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
1685+
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control)
16771686
{
16781687
CMutableTransaction txNew(tx);
1679-
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
1688+
if (!wallet->DummySignTx(txNew, txouts, coin_control)) {
16801689
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
16811690
// implies that we can sign for every input.
16821691
return -1;
@@ -1688,7 +1697,7 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet,
16881697
{
16891698
CMutableTransaction txn;
16901699
txn.vin.push_back(CTxIn(COutPoint()));
1691-
if (!wallet->DummySignInput(txn, 0, txout, use_max_sig)) {
1700+
if (!DummySignInput(wallet, txn, 0, txout, use_max_sig)) {
16921701
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
16931702
// implies that we can sign for every input.
16941703
return -1;
@@ -2690,8 +2699,18 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
26902699
}
26912700
mapValueFromPresetInputs[pcoin->GetOutputAsset(outpoint.n)] += amt;
26922701
setPresetCoins.insert(CInputCoin(pcoin, outpoint.n));
2693-
} else
2694-
return false; // TODO: Allow non-wallet inputs
2702+
} else {
2703+
CTxOut txout;
2704+
if (coin_control.GetExternalOutput(outpoint, txout)) {
2705+
if (!txout.nValue.IsExplicit() || !txout.nAsset.IsExplicit()) {
2706+
return false; // We can't get its value, so abort
2707+
}
2708+
mapValueFromPresetInputs[txout.nAsset.GetAsset()] += txout.nValue.GetAmount();
2709+
setPresetCoins.insert(CInputCoin(outpoint, txout));
2710+
} else {
2711+
return false;
2712+
}
2713+
}
26952714
}
26962715

26972716
// remove preset inputs from vCoins
@@ -2824,8 +2843,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
28242843
coinControl.ListSelected(vPresetInputs);
28252844
for (const COutPoint& presetInput : vPresetInputs) {
28262845
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(presetInput.hash);
2846+
CTxOut txout;
28272847
if (it != mapWallet.end()) {
28282848
setAssets.insert(it->second.GetOutputAsset(presetInput.n));
2849+
} else if (coinControl.GetExternalOutput(presetInput, txout)) {
2850+
setAssets.insert(txout.nAsset.GetAsset());
28292851
}
28302852
}
28312853

@@ -3162,13 +3184,18 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
31623184
std::vector<COutPoint> vPresetInputs;
31633185
coin_control.ListSelected(vPresetInputs);
31643186
for (const COutPoint& presetInput : vPresetInputs) {
3187+
CAsset asset;
31653188
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(presetInput.hash);
3166-
if (it == mapWallet.end()) {
3189+
CTxOut txout;
3190+
if (it != mapWallet.end()) {
3191+
asset = it->second.GetOutputAsset(presetInput.n);
3192+
} else if (coin_control.GetExternalOutput(presetInput, txout)) {
3193+
asset = txout.nAsset.GetAsset();
3194+
} else {
31673195
// Ignore this here, will fail more gracefully later.
31683196
continue;
31693197
}
31703198

3171-
CAsset asset = it->second.GetOutputAsset(presetInput.n);
31723199
if (mapScriptChange.find(asset) != mapScriptChange.end()) {
31733200
// This asset already has a change script.
31743201
continue;
@@ -3509,7 +3536,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
35093536
}
35103537
}
35113538

3512-
nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
3539+
nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, &coin_control);
35133540
if (nBytes < 0) {
35143541
strFailReason = _("Signing transaction failed");
35153542
return false;

src/wallet/wallet.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,14 +1084,13 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
10841084
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true, BlindDetails* blind_details = nullptr, const IssuanceDetails* issuance_details = nullptr);
10851085
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::vector<std::unique_ptr<CReserveKey>>& reservekey, CConnman* connman, CValidationState& state, const BlindDetails* blind_details = nullptr);
10861086

1087-
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
1087+
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const
10881088
{
10891089
std::vector<CTxOut> v_txouts(txouts.size());
10901090
std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
1091-
return DummySignTx(txNew, v_txouts, use_max_sig);
1091+
return DummySignTx(txNew, v_txouts, coin_control);
10921092
}
1093-
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
1094-
bool DummySignInput(CMutableTransaction &tx, const size_t nIn, const CTxOut &txout, bool use_max_sig = false) const;
1093+
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const;
10951094

10961095
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
10971096
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
@@ -1445,8 +1444,6 @@ class WalletRescanReserver
14451444

14461445
// Calculate the size of the transaction assuming all signatures are max size
14471446
// Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
1448-
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
1449-
// be IsAllFromMe).
1450-
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
1451-
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
1447+
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
1448+
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr);
14521449
#endif // BITCOIN_WALLET_WALLET_H

0 commit comments

Comments
 (0)