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).
This commit is contained in:
1 parent
14e50746f6
commit
2d39db7aa1
3 files changed
+107
-2
No files matched your search
@@ -39,7 +39,10 @@ std::optional<CTxOut> CCoinControl::GetExternalOutput(const COutPoint& outpoint)
|
|||||||
|
|
||||||
PreselectedInput& CCoinControl::Select(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)
|
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;
|
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)
|
void PreselectedInput::SetTxOut(const CTxOut& txout)
|
||||||
{
|
{
|
||||||
m_txout = txout;
|
m_txout = txout;
|
||||||
@@ -113,4 +122,34 @@ std::optional<uint32_t> PreselectedInput::GetSequence() const
|
|||||||
{
|
{
|
||||||
return m_sequence;
|
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
|
} // namespace wallet
|
||||||
@@ -33,6 +33,12 @@ private:
|
|||||||
std::optional<int64_t> m_weight;
|
std::optional<int64_t> m_weight;
|
||||||
//! The sequence number for this input
|
//! The sequence number for this input
|
||||||
std::optional<uint32_t> m_sequence;
|
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:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -54,6 +60,20 @@ public:
|
|||||||
void SetSequence(uint32_t sequence);
|
void SetSequence(uint32_t sequence);
|
||||||
/** Retrieve the sequence for this input. */
|
/** Retrieve the sequence for this input. */
|
||||||
std::optional<uint32_t> GetSequence() const;
|
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. */
|
/** Coin Control Features. */
|
||||||
@@ -141,10 +161,27 @@ public:
|
|||||||
std::optional<int64_t> GetInputWeight(const COutPoint& outpoint) const;
|
std::optional<int64_t> GetInputWeight(const COutPoint& outpoint) const;
|
||||||
/** Retrieve the sequence for an input */
|
/** Retrieve the sequence for an input */
|
||||||
std::optional<uint32_t> GetSequence(const COutPoint& outpoint) const;
|
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:
|
private:
|
||||||
//! Selected inputs (inputs that will be used, regardless of whether they're optimal or not)
|
//! Selected inputs (inputs that will be used, regardless of whether they're optimal or not)
|
||||||
std::map<COutPoint, PreselectedInput> m_selected;
|
std::map<COutPoint, PreselectedInput> m_selected;
|
||||||
|
unsigned int m_selection_pos{0};
|
||||||
};
|
};
|
||||||
} // namespace wallet
|
} // namespace wallet
|
||||||
|
|
||||||
|
|||||||
+30
-1
@@ -1146,6 +1146,25 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
|||||||
// Shuffle selected coins and fill in final vin
|
// Shuffle selected coins and fill in final vin
|
||||||
std::vector<std::shared_ptr<COutput>> selected_coins = result.GetShuffledInputVector();
|
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
|
// The sequence number is set to non-maxint so that DiscourageFeeSniping
|
||||||
// works.
|
// works.
|
||||||
//
|
//
|
||||||
@@ -1162,7 +1181,15 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
|||||||
// If an input has a preset sequence, we can't do anti-fee-sniping
|
// If an input has a preset sequence, we can't do anti-fee-sniping
|
||||||
use_anti_fee_sniping = false;
|
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) {
|
if (coin_control.m_locktime) {
|
||||||
txNew.nLockTime = coin_control.m_locktime.value();
|
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.SetTxOut(coins[outPoint].out);
|
||||||
}
|
}
|
||||||
preset_txin.SetSequence(txin.nSequence);
|
preset_txin.SetSequence(txin.nSequence);
|
||||||
|
preset_txin.SetScriptSig(txin.scriptSig);
|
||||||
|
preset_txin.SetScriptWitness(txin.scriptWitness);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = CreateTransaction(wallet, vecSend, nChangePosInOut, coinControl, false);
|
auto res = CreateTransaction(wallet, vecSend, nChangePosInOut, coinControl, false);
|
||||||
|
|||||||
Reference in new issue
Block a user