-
Notifications
You must be signed in to change notification settings - Fork 36.6k
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
wallet: Pass through transaction locktime and preset input sequences and scripts to CreateTransaction #25273
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
996f7f8
to
664f743
Compare
I'm not sure this is an improvement. It just adds complexity, for no benefit in this particular instance? |
src/wallet/spend.cpp
Outdated
uint32_t sequence = default_sequence; | ||
if (coin_control.HasSequence(coin.outpoint)) { | ||
sequence = coin_control.GetSequence(coin.outpoint); | ||
// If an input as a preset sequence, we can't do anti-fee-sniping |
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.
Why not? As long as one input has a non-final sequence, it should still work.
(DiscourageFeeSniping
would need its sanity checks adjusted to tolerate it, though)
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.
This is to retain the current behavior. The next step in a followup is to allow anti-fee-sniping and modify DiscourageFeeSniping
to consider the preset sequences.
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.
Where is this behaviour in the current logic? I thought presently it would do the anti-sniping, and then possibly (at worst) have that partially "broken" by subsequent changes?
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.
It's more of a side effect of FundTransaction
rather than explicit behavior. Currently, having preset sequences inherently means a preset locktime which would disable anti-fee-sniping. This is just due to how FundTransaction
works, and FundTransaction
is the only way that preset sequences can even be provided.
Until we update DiscourageFeeSniping
to handle preset sequences and locktime, I would prefer that we have this check explicitly just to avoid any problems in the future with how and when preset sequences and locktimes can be set.
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.
Making sure i got this one right. use_anti_fee_sniping
may only be false
if this is called from FundTransaction
. And FundTransaction
diregards the value of the nLockTime
that may be set here anyways. (Well not anymore in the following commits, but at this stage in the commit series it does.)
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.
Yes, FundTransaction
currently throws away nLockTime
thereby disabling anti-fee-sniping, and this preserves that behavior.
664f743
to
5e19bce
Compare
IMO it makes it easier to understand.
I think I've fixed this. |
5e19bce
to
37a15c0
Compare
0135553
to
45fb4fb
Compare
45fb4fb
to
ed25666
Compare
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.
ACK 1d1a6ff
Left some nits that can be ignored or addressed in a follow-up, but nothing major. There is a type mismatch with GetInputWeight
returning int64_t but being assigned to int32_t. Would be great to fix that but also not a super big deal since weight will never be greater than int32_t anyway (but should def be fixed in a followup).
const auto ext_it = m_external_txouts.find(outpoint); | ||
if (ext_it == m_external_txouts.end()) { | ||
const auto it = m_selected.find(outpoint); | ||
if (it == m_selected.end() || !it->second.HasTxOut()) { |
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 having TxOut
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()
.
@@ -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 comment
The 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.
Instead of having different maps for selected inputs, external inputs, and input weight in CCoinControl, have a class PreselectedInput which tracks stores that information for each input.
Instead of having a separate CCoinControl::SelectExternal function, we can use the normal CCoinControl::Select function and explicitly use PreselectedInput::SetTxOut in the caller. The semantics of what an external input is remains.
We provide the preset nLockTime to CCoinControl so that CreateTransactionInternal can be aware of it and set it in the produced transaction.
We provide the preset nVersion to CCoinControl so that CreateTransactionInternal can be aware of it and set it in the produced transaction.
1d1a6ff
to
9c7eb4c
Compare
…saction 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).
…saction Instead of making -1 a magic number meaning no change or random change position, use an optional to have that meaning.
Instead of using the output parameters, return CreatedTransactionResult from FundTransaction in the same way that CreateTransaction does. Additionally, instead of modifying the original CMutableTransaction, the result from CreateTransactionInternal is used.
9c7eb4c
to
0295b44
Compare
ACK 0295b44 Thanks for taking the suggestions! Looks like the CI failure is a transient failure, I ran the same test locally and it passed. Probably just needs to be re-run. |
Code review ACK 0295b44 Still not a huge fan of 596642c and I have same concerns as josibake. I prefer explicit defitino of external inputs rather than indirectly through Also it seems two commits were squashed "Explicitly preserve scriptSig and scriptWitness in CreateTransaction" and "Preserve preset input order". Was that intentional? |
My concerns were addressed by #25273 (comment). I think the approach in 596642c is much simpler and as the comment points out, the worst-case scenario if inputs are labeled incorrectly is overestimating the fee by 1 byte. |
For the reviewers of this PR, #28560 is a follow-up that further refactors |
@@ -1349,6 +1353,9 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, | |||
// Set the user desired locktime | |||
coinControl.m_locktime = tx.nLockTime; | |||
|
|||
// Set the user desired version | |||
coinControl.m_version = tx.nVersion; |
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.
it seems that the version field is not sanitized. The user provided tx.nVersion
is a int32_t while coinControl.m_version
is a uint32_t.
Since bitcoin#25273, the "insert change at a random position" behavior is instructed by passing std::nullopt, and not -1 anymore. Also, document what 'std::nullopt' means.
Since bitcoin#25273, the behavior of 'inserting change at a random position' is instructed by passing std::nullopt instead of -1. Additionally, added missing documentation about the meaning of 'change_pos=std::nullopt' inside 'CWallet::CreateTransaction()'
Since bitcoin#25273, the behavior of 'inserting change at a random position' is instructed by passing std::nullopt instead of -1. Also, added missing documentation about the meaning of 'change_pos=std::nullopt' inside 'CWallet::CreateTransaction()'
37c75c5 test: wallet, fix change position out of range error (furszy) Pull request description: Fixes #29061. Only the benchmark is affected. Since #25273, the behavior of 'inserting change at a random position' is instructed by passing ´std::nullopt´ instead of -1. Also, added missing documentation about the meaning of 'change_pos=std::nullopt' inside 'CWallet::CreateTransaction()' ACKs for top commit: achow101: ACK 37c75c5 kevkevinpal: ACK [37c75c5](37c75c5) BrandonOdiwuor: ACK 37c75c5 Tree-SHA512: d9a8d8533540455716a5090fcf407573cad9f0d0018a05f903f89e51620302f9b256318db6f7338b85c047f7fab372d724e916b1721d7ed302dbf3d845b08734
Currently
FundTransaction
handles transaction locktime and preset input data by extracting the selected inputs and change output fromCreateTransaction
's results. This means thatCreateTransaction
is actually unaware of any user desired locktime or sequence numbers. This can have an effect on whether and how anti-fee-sniping works.This PR makes
CreateTransaction
aware of the locktime and preset input data by providing them toCCoinControl
.CreateTransasction
will then set the sequences, scriptSigs, scriptWItnesses, and locktime as appropriate if they are specified. This allowsFundTransaction
to actually useCreateTransaction
's result directly instead of having to extract the parts of it that it wants.Additionally
FundTransaction
will return aCreateTransactionResult
asCreateTransaction
does instead of having several output parameters. Lastly, instead of using-1
as a magic number for the change output position, the change position is changed to be an optional with no value set indicating no desired change output position (when provided as an input parameter) or no change output present (in the result).