aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
authorAndrew Chow <achow101-github@achow101.com>2022-06-01 17:39:58 -0400
committerAndrew Chow <github@achow101.com>2023-12-08 17:12:19 -0500
commit2d39db7aa128a948b6ad11242591ef26a342f5b1 (patch)
treea235dad9cfb243d3a1dffec56971f491e5ad275d /src/wallet
parent14e50746f683361f4d511d384d6f1dc44ed2bf10 (diff)
downloadbitcoin-2d39db7aa128a948b6ad11242591ef26a342f5b1.tar.xz
wallet: Explicitly preserve scriptSig and scriptWitness in CreateTransaction
When creating a transaction with preset inputs, also preserve the scriptSig and scriptWitness for those preset inputs if they are provided (e.g. in fundrawtransaction).
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/coincontrol.cpp41
-rw-r--r--src/wallet/coincontrol.h37
-rw-r--r--src/wallet/spend.cpp31
3 files changed, 107 insertions, 2 deletions
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index f13b7073be..873c5ab383 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -39,7 +39,10 @@ std::optional<CTxOut> CCoinControl::GetExternalOutput(const COutPoint& outpoint)
PreselectedInput& CCoinControl::Select(const COutPoint& outpoint)
{
- return m_selected[outpoint];
+ auto& input = m_selected[outpoint];
+ input.SetPosition(m_selection_pos);
+ ++m_selection_pos;
+ return input;
}
void CCoinControl::UnSelect(const COutPoint& outpoint)
{
@@ -78,6 +81,12 @@ std::optional<uint32_t> CCoinControl::GetSequence(const COutPoint& outpoint) con
return it != m_selected.end() ? it->second.GetSequence() : std::nullopt;
}
+std::pair<std::optional<CScript>, std::optional<CScriptWitness>> CCoinControl::GetScripts(const COutPoint& outpoint) const
+{
+ const auto it = m_selected.find(outpoint);
+ return it != m_selected.end() ? m_selected.at(outpoint).GetScripts() : std::make_pair(std::nullopt, std::nullopt);
+}
+
void PreselectedInput::SetTxOut(const CTxOut& txout)
{
m_txout = txout;
@@ -113,4 +122,34 @@ std::optional<uint32_t> PreselectedInput::GetSequence() const
{
return m_sequence;
}
+
+void PreselectedInput::SetScriptSig(const CScript& script)
+{
+ m_script_sig = script;
+}
+
+void PreselectedInput::SetScriptWitness(const CScriptWitness& script_wit)
+{
+ m_script_witness = script_wit;
+}
+
+bool PreselectedInput::HasScripts() const
+{
+ return m_script_sig.has_value() || m_script_witness.has_value();
+}
+
+std::pair<std::optional<CScript>, std::optional<CScriptWitness>> PreselectedInput::GetScripts() const
+{
+ return {m_script_sig, m_script_witness};
+}
+
+void PreselectedInput::SetPosition(unsigned int pos)
+{
+ m_pos = pos;
+}
+
+std::optional<unsigned int> PreselectedInput::GetPosition() const
+{
+ return m_pos;
+}
} // namespace wallet
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 66f9dcb53c..b2f813383d 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -33,6 +33,12 @@ private:
std::optional<int64_t> m_weight;
//! The sequence number for this input
std::optional<uint32_t> m_sequence;
+ //! The scriptSig for this input
+ std::optional<CScript> m_script_sig;
+ //! The scriptWitness for this input
+ std::optional<CScriptWitness> m_script_witness;
+ //! The position in the inputs vector for this input
+ std::optional<unsigned int> m_pos;
public:
/**
@@ -54,6 +60,20 @@ public:
void SetSequence(uint32_t sequence);
/** Retrieve the sequence for this input. */
std::optional<uint32_t> GetSequence() const;
+
+ /** Set the scriptSig for this input. */
+ void SetScriptSig(const CScript& script);
+ /** Set the scriptWitness for this input. */
+ void SetScriptWitness(const CScriptWitness& script_wit);
+ /** Return whether either the scriptSig or scriptWitness are set for this input. */
+ bool HasScripts() const;
+ /** Retrieve both the scriptSig and the scriptWitness. */
+ std::pair<std::optional<CScript>, std::optional<CScriptWitness>> GetScripts() const;
+
+ /** Store the position of this input. */
+ void SetPosition(unsigned int pos);
+ /** Retrieve the position of this input. */
+ std::optional<unsigned int> GetPosition() const;
};
/** Coin Control Features. */
@@ -141,10 +161,27 @@ public:
std::optional<int64_t> GetInputWeight(const COutPoint& outpoint) const;
/** Retrieve the sequence for an input */
std::optional<uint32_t> GetSequence(const COutPoint& outpoint) const;
+ /** Retrieves the scriptSig and scriptWitness for an input. */
+ std::pair<std::optional<CScript>, std::optional<CScriptWitness>> GetScripts(const COutPoint& outpoint) const;
+
+ bool HasSelectedOrder() const
+ {
+ return m_selection_pos > 0;
+ }
+
+ std::optional<unsigned int> GetSelectionPos(const COutPoint& outpoint) const
+ {
+ const auto it = m_selected.find(outpoint);
+ if (it == m_selected.end()) {
+ return std::nullopt;
+ }
+ return it->second.GetPosition();
+ }
private:
//! Selected inputs (inputs that will be used, regardless of whether they're optimal or not)
std::map<COutPoint, PreselectedInput> m_selected;
+ unsigned int m_selection_pos{0};
};
} // namespace wallet
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index c560c71137..72ec719f22 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -1146,6 +1146,25 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
// Shuffle selected coins and fill in final vin
std::vector<std::shared_ptr<COutput>> selected_coins = result.GetShuffledInputVector();
+ if (coin_control.HasSelected() && coin_control.HasSelectedOrder()) {
+ // When there are preselected inputs, we need to move them to be the first UTXOs
+ // and have them be in the order selected. We can use stable_sort for this, where we
+ // compare with the positions stored in coin_control. The COutputs that have positions
+ // will be placed before those that don't, and those positions will be in order.
+ std::stable_sort(selected_coins.begin(), selected_coins.end(),
+ [&coin_control](const std::shared_ptr<COutput>& a, const std::shared_ptr<COutput>& b) {
+ auto a_pos = coin_control.GetSelectionPos(a->outpoint);
+ auto b_pos = coin_control.GetSelectionPos(b->outpoint);
+ if (a_pos.has_value() && b_pos.has_value()) {
+ return a_pos.value() < b_pos.value();
+ } else if (a_pos.has_value() && !b_pos.has_value()) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+ }
+
// The sequence number is set to non-maxint so that DiscourageFeeSniping
// works.
//
@@ -1162,7 +1181,15 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
// If an input has a preset sequence, we can't do anti-fee-sniping
use_anti_fee_sniping = false;
}
- txNew.vin.emplace_back(coin->outpoint, CScript(), sequence.value_or(default_sequence));
+ txNew.vin.emplace_back(coin->outpoint, CScript{}, sequence.value_or(default_sequence));
+
+ auto scripts = coin_control.GetScripts(coin->outpoint);
+ if (scripts.first) {
+ txNew.vin.back().scriptSig = *scripts.first;
+ }
+ if (scripts.second) {
+ txNew.vin.back().scriptWitness = *scripts.second;
+ }
}
if (coin_control.m_locktime) {
txNew.nLockTime = coin_control.m_locktime.value();
@@ -1381,6 +1408,8 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
preset_txin.SetTxOut(coins[outPoint].out);
}
preset_txin.SetSequence(txin.nSequence);
+ preset_txin.SetScriptSig(txin.scriptSig);
+ preset_txin.SetScriptWitness(txin.scriptWitness);
}
auto res = CreateTransaction(wallet, vecSend, nChangePosInOut, coinControl, false);