-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Refactor OutputGroup effective value calculations and filtering to occur within the struct #17458
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
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsNo conflicts as of last run. |
625e494
to
693cfa6
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.
Seems right. The remaining other location that sets effective_value
is in SelectCoins
for preset inputs.
I'd prefer that every setting was done under OutputGroup
but that can be done later.
@@ -299,7 +299,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group | |||
void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) { | |||
m_outputs.push_back(output); | |||
m_from_me &= from_me; | |||
m_value += output.effective_value; |
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 only isn't a reachable "bug" because before this PR the values are the same, yes?
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.
hmm actually no, this happens for any OutputGroup
, which includes Bnb?
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 isn't reachable because the values are the same as the outputs' effective values aren't ever actually to their effective values.
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.
ah I see, it's being set externally in SelectCoinsMinConf
. Ok, this set of changes makes it much easier to understand.
src/wallet/coinselection.h
Outdated
@@ -36,6 +36,8 @@ class CInputCoin { | |||
COutPoint outpoint; | |||
CTxOut txout; | |||
CAmount effective_value; | |||
CAmount m_fee; |
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 commit makes effective_value
a bit ambiguous to the reader now that both short and long-range fees are being stored.
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 makes everything easier to precompute and store these since they are used in multiple places.
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.
I understand the motivation, my nitting is about naming since we're expanding the "scope" of what is being cached.
utACK 693cfa6 |
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.
Concept ACK
Good refactoring, code looks good too but please see my comment/question.
@@ -317,6 +318,8 @@ std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output) | |||
if (it == m_outputs.end()) return it; | |||
m_value -= output.txout.nValue; | |||
effective_value -= output.effective_value; | |||
fee -= output.m_fee; | |||
long_term_fee -= output.m_long_term_fee; |
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.
Should the reverse not also be added to the Insert
method above?
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, but no. The OutputGroup
s are created (and Insert
is called) before the fees are set. But Discard
is called after the fees are set. So not incrementing fee
and long_term_fee
in Insert
is safe because they are 0 at that time. I guess it still should for consistency.
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.
Yeah, I think it would be better if it was added. Otherwise, bugs could be introduced if Insert
is used differently in the future.
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.
I've added it to insert.
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.
I had another pass and it looks good to me and works fine. But I would like to see the change in Insert()
and GetPositiveOnlyGroup()
can be simplified significantly, I think.
} | ||
} | ||
|
||
OutputGroup OutputGroup::GetPositiveOnlyGroup() |
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.
To me this method looks overly complicated and doesn't fit so well with the rest of the API. I would suggest simplifying it like this: fjahr@404e7f2
Tests passed for me with this change.
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.
My understanding was that this was not safe and could result in undefined behavior because we are actively iterating through the vector while also modifying at the same time (Discard
erases from the vector). So the safe way to do this is to use iterators.
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.
You are right! I think this is still a bit of an improvement but only if you have to retouch fjahr@a438884
@@ -317,6 +318,8 @@ std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output) | |||
if (it == m_outputs.end()) return it; | |||
m_value -= output.txout.nValue; | |||
effective_value -= output.effective_value; | |||
fee -= output.m_fee; | |||
long_term_fee -= output.m_long_term_fee; |
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.
Yeah, I think it would be better if it was added. Otherwise, bugs could be introduced if Insert
is used differently in the future.
693cfa6
to
6a6d19e
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.
reACK 6a6d19e
There seems to be a random sigabrt in the BnBExhaustion benchmark. |
ACK 6a6d19e Tests run fine for me locally. |
long_term_fee = 0; | ||
effective_value = 0; | ||
for (CInputCoin& coin : m_outputs) { | ||
coin.m_fee = coin.m_input_bytes < 0 ? 0 : effective_feerate.GetFee(coin.m_input_bytes); |
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.
How would a UTXO's input size ever be smaller than 0? Shouldn't that be an exception?
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.
m_input_bytes
default is -1 to indicate it wasn't calculated.
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.
I see thank you.
@@ -93,6 +95,8 @@ struct OutputGroup | |||
void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants); | |||
std::vector<CInputCoin>::iterator Discard(const CInputCoin& output); | |||
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const; | |||
void SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate); | |||
OutputGroup GetPositiveOnlyGroup(); |
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.
My understanding from #17526 was that one instance of OutputGroup
described a single UTXO with its ancestry, which seems consistent with the struct above.
It seems to me though, that the function GetPositiveOnlyGroup()
operates on a set of OutputGroup in the above sense, though. Is that the case?
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.
No, OutputGroup
is a group of UTXOs that we want to consider together. It doesn't include ancestry. IIRC this is used for avoid reuse and for some privacy stuff.
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.
I see, thanks. I wasn't thinking about the bundling of all utxos associated with one address.
re-run ci |
I thought it was a CI problem, but I'm able to reproduce the test failures from the last run.
|
6a6d19e
to
3ed30f4
Compare
I'm unable to reproduce the test failures. After rebasing, I also can't get the random sigabrt I was getting. |
3ed30f4
to
410c8b1
Compare
OutputGroup::m_value is the true value, not the effective value, so use the true values of the outputs, not their effective values.
410c8b1
to
97f6acd
Compare
Instead of having callers set the fees, effective values, and filtering of outputs, do these within OutputGroups themselves as member functions. m_fee and m_long_term_fee is added to OutputGroup to track the fees of the OutputGroup.
97f6acd
to
9adc2f8
Compare
reACK 9adc2f8 |
@xekyo did you want to take another look here? |
re-ACK 9adc2f8 per |
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.
fee += coin.m_fee; | ||
|
||
coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes); | ||
long_term_fee += coin.m_long_term_fee; |
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.
Actually, why is the long_term_fee
tracked additionally to just the fee here? Is that just for calculating the waste metric?
If I understand that right, at this point the output group is already being cast into the context of the current transaction (i.e. the fee is specific to the transaction we are building). Since the waste metric is only dependent on the current fee rate and the long term fee rate (if that's still the same from when we discussed two years ago), perhaps it would be better to calculate the waste here instead of carrying around the long term fee to calculate the waste later. Might be better to do that in separate PR later, though, even if you agree. ;)
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.
Indeed, I think it would be better to track the waste. I will do that in a followup as it is a slightly larger change which touches the actual coin selection algorithms.
…s and filtering to occur within the struct 9adc2f8 Refactor OutputGroups to handle effective values, fees, and filtering (Andrew Chow) 7d07e86 Use real value when calculating OutputGroup value (Andrew Chow) Pull request description: Currently, the effective values and filtering for positive effective values is done outside of the OutputGroup. We should instead have functions in Outputgroup to do this and call those for each OutputGroup. So this PR does that. This makes future changes for effective values in coin selection much easier. ACKs for top commit: instagibbs: reACK 9adc2f8 fjahr: re-ACK 9adc2f8 meshcollider: Light code review ACK 9adc2f8 Tree-SHA512: 7445c94b7295b45bcd83a6f8a5c8f6961a89453fcc856335192d4b4a66aec7724513616b04e5111588ab208c89b311055399d6279cd9c4ce452aefb85f04b64a
…ng eligibility on grouping 5d45976 Rewrite OutputGroups to be clearer and to use scriptPubKeys (Andrew Chow) f6b3052 Explicitly filter out partial groups when we don't want them (Andrew Chow) 416d74f Move OutputGroup positive only filtering into Insert (Andrew Chow) d895e98 Move EligibleForSpending into GroupOutputs (Andrew Chow) 99b399a Move fee setting of OutputGroup to Insert (Andrew Chow) 6148a8a Move GroupOutputs into SelectCoinsMinConf (Andrew Chow) 2acad03 Remove OutputGroup non-default constructors (Andrew Chow) Pull request description: Even after #17458, we still deal with setting fees of an `OutputGroup` and filtering the `OutputGroup` outside of the struct. We currently make all of the `OutputGroup`s in `SelectCoins` and then copy and modify them within each `SelectCoinsMinConf` scenario. This PR changes this to constructing the `OutputGroup`s within the `SelectCoinsMinConf` so that the scenario can be taken into account during the group construction. Furthermore, setting of fees and filtering for effective value is moved into `OutputGroup::Insert` itself so that we don't add undesirable outputs to an `OutputGroup` rather than deleting them afterwards. To facilitate fee calculation and effective value filtering during `OutputGroup::Insert`, `OutputGroup` now takes the feerates in its constructor and computes the fees and effective value for each output during `Insert`. While removing `OutputGroup`s in accordance with the `CoinEligibilityFilter` still requires creating the `OutputGroup`s first, we can do that within the function that makes them - `GroupOutput`s. ACKs for top commit: Xekyo: Code review ACK: 5d45976 fjahr: Code review ACK 5d45976 meshcollider: Light utACK 5d45976 Tree-SHA512: 35965b6d49a87f4ebb366ec4f00aafaaf78e9282481ae2c9682b515a3a9f2cbcd3cd6e202fee29489d48fe7f3a7cede4270796f5e72bbaff76da647138fb3059
Summary: OutputGroup::m_value is the true value, not the effective value, so use the true values of the outputs, not their effective values. This is a backport of [[bitcoin/bitcoin#17458 | core#17458]] [1/2] bitcoin/bitcoin@9adc2f8 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, majcosta Reviewed By: #bitcoin_abc, majcosta Differential Revision: https://reviews.bitcoinabc.org/D10088
Summary: Instead of having callers set the fees, effective values, and filtering of outputs, do these within OutputGroups themselves as member functions. m_fee and m_long_term_fee is added to OutputGroup to track the fees of the OutputGroup. This is a backport of [[bitcoin/bitcoin#17458 | core#17458]] [2/2] bitcoin/bitcoin@9adc2f8 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, majcosta Reviewed By: #bitcoin_abc, majcosta Subscribers: Detetivepro1 Differential Revision: https://reviews.bitcoinabc.org/D10089
Currently, the effective values and filtering for positive effective values is done outside of the OutputGroup. We should instead have functions in Outputgroup to do this and call those for each OutputGroup. So this PR does that.
This makes future changes for effective values in coin selection much easier.