-
Notifications
You must be signed in to change notification settings - Fork 37.5k
wallet: Pass through transaction locktime and preset input sequences and scripts to CreateTransaction #25273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e1abfb5
5321786
596642c
4d335bb
0fefcbb
14e5074
2d39db7
758501b
0295b44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,58 @@ const int DEFAULT_MAX_DEPTH = 9999999; | |
//! Default for -avoidpartialspends | ||
static constexpr bool DEFAULT_AVOIDPARTIALSPENDS = false; | ||
|
||
class PreselectedInput | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in 59a7c74 Naming is hard. We already have struct
It'd be nice to have consistent terms to refer to different data. Perhaps @murchandamus have thought about terminology. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤷 |
||
{ | ||
private: | ||
//! The previous output being spent by this input | ||
std::optional<CTxOut> m_txout; | ||
//! The input weight for spending this input | ||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in 53cb734 There is redundancy between Can we make the interface better? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is actually expected that both can be provided, and not match. We may be funding a transaction with other inputs that already have some signatures (e.g. a multisig where one of the parties is adding another input for fees, and one other party has already signed with sighash_single), so |
||
//! 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: | ||
/** | ||
* Set the previous output for this input. | ||
* Only necessary if the input is expected to be an external input. | ||
*/ | ||
void SetTxOut(const CTxOut& txout); | ||
/** Retrieve the previous output for this input. */ | ||
CTxOut GetTxOut() const; | ||
/** Return whether the previous output is set for this input. */ | ||
bool HasTxOut() const; | ||
|
||
/** Set the weight for this input. */ | ||
void SetInputWeight(int64_t weight); | ||
/** Retrieve the input weight for this input. */ | ||
std::optional<int64_t> GetInputWeight() const; | ||
achow101 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** Set the sequence for this input. */ | ||
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. */ | ||
class CCoinControl | ||
{ | ||
|
@@ -59,6 +111,10 @@ class CCoinControl | |
int m_max_depth = DEFAULT_MAX_DEPTH; | ||
//! SigningProvider that has pubkeys and scripts to do spend size estimation for external inputs | ||
FlatSigningProvider m_external_provider; | ||
//! Locktime | ||
std::optional<uint32_t> m_locktime; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in 0342002
Same is true for some other fields in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With miniscript, it does matter since scripts can contain locktimes. |
||
//! Version | ||
std::optional<uint32_t> m_version; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in c854439 Same comment as for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With miniscript, it does matter, as it controls whether something with |
||
|
||
CCoinControl(); | ||
|
||
|
@@ -69,11 +125,11 @@ class CCoinControl | |
/** | ||
* Returns true if the given output is pre-selected. | ||
*/ | ||
bool IsSelected(const COutPoint& output) const; | ||
bool IsSelected(const COutPoint& outpoint) const; | ||
/** | ||
* Returns true if the given output is selected as an external input. | ||
*/ | ||
bool IsExternalSelected(const COutPoint& output) const; | ||
bool IsExternalSelected(const COutPoint& outpoint) const; | ||
/** | ||
* Returns the external output for the given outpoint if it exists. | ||
*/ | ||
|
@@ -82,16 +138,11 @@ class CCoinControl | |
* Lock-in the given output for spending. | ||
* The output will be included in the transaction even if it's not the most optimal choice. | ||
*/ | ||
void Select(const COutPoint& output); | ||
/** | ||
* Lock-in the given output as an external input for spending because it is not in the wallet. | ||
* The output will be included in the transaction even if it's not the most optimal choice. | ||
*/ | ||
void SelectExternal(const COutPoint& outpoint, const CTxOut& txout); | ||
PreselectedInput& Select(const COutPoint& outpoint); | ||
/** | ||
* Unselects the given output. | ||
*/ | ||
void UnSelect(const COutPoint& output); | ||
void UnSelect(const COutPoint& outpoint); | ||
/** | ||
* Unselects all outputs. | ||
*/ | ||
|
@@ -104,23 +155,33 @@ class CCoinControl | |
* Set an input's weight. | ||
*/ | ||
void SetInputWeight(const COutPoint& outpoint, int64_t weight); | ||
/** | ||
* Returns true if the input weight is set. | ||
*/ | ||
bool HasInputWeight(const COutPoint& outpoint) const; | ||
/** | ||
* Returns the input weight. | ||
*/ | ||
int64_t GetInputWeight(const COutPoint& outpoint) const; | ||
std::optional<int64_t> GetInputWeight(const COutPoint& outpoint) const; | ||
achow101 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/** 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::set<COutPoint> m_selected_inputs; | ||
//! Map of external inputs to include in the transaction | ||
//! These are not in the wallet, so we need to track them separately | ||
std::map<COutPoint, CTxOut> m_external_txouts; | ||
//! Map of COutPoints to the maximum weight for that input | ||
std::map<COutPoint, int64_t> m_input_weights; | ||
std::map<COutPoint, PreselectedInput> m_selected; | ||
unsigned int m_selection_pos{0}; | ||
}; | ||
} // namespace wallet | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
4D1F
|
@@ -281,12 +281,12 @@ class WalletImpl : public Wallet | |
CAmount& fee) override | ||
{ | ||
LOCK(m_wallet->cs_wallet); | ||
auto res = CreateTransaction(*m_wallet, recipients, change_pos, | ||
auto res = CreateTransaction(*m_wallet, recipients, change_pos == -1 ? std::nullopt : std::make_optional(change_pos), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in 3d1a5f2: note for a follow-up: would be nice if we could use this convention throughout the codebase instead of switching the magic number into a std::optional, like we are doing here. |
||
coin_control, sign); | ||
if (!res) return util::Error{util::ErrorString(res)}; | ||
const auto& txr = *res; | ||
fee = txr.fee; | ||
change_pos = txr.change_pos; | ||
change_pos = txr.change_pos ? *txr.change_pos : -1; | ||
|
||
return txr.tx; | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in 7b5d22f:
Is there a situation where an internal input would have a value for
TxOut
, accidentally or purposefully? Only asking because it feels slightly more fragile to rely only on this to determine if something is external or internal vs having two separate vectors, but I also don't have a good sense if what I'm worried about is even possible (internal havingTxOut
set) or if its even that bad if we confuse external and internal inputs 🤷There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The consequence is that size estimation will overestimate that input's size by 1 byte, which is up to 4 weight units of overestimation and therefore fees. However it should not be possible to accidentally set an internal input to be external as all calls to
SelectExternal
are guarded by a!IsMine()
.