-
Notifications
You must be signed in to change notification settings - Fork 38.6k
mempool / rpc: add getprioritisedtransactions, delete a mapDeltas entry when delta==0 #27501
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. 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. ConflictsNo conflicts as of last run. |
maflcko
left a comment
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. Wanted to add a deletepriority RPC myself, but I guess it is easy to implement a for loop in python utilising the output of getprioritisationmap
2c0eee6 to
d5069a4
Compare
|
Maybe add a short description for what |
maflcko
left a comment
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.
lgtm ACK. Left two nits.
stickies-v
left a comment
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
ishaanam
left a comment
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.
Approach ACK
4f6ea69 to
ab47d8b
Compare
glozow
left a comment
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.
Addressed comments, rebased, added a release note
|
Concept ACK |
theStack
left a comment
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
Was a bit surprised to see that the fee-delta is returned in BTC as unit rather than Satoshis, I assume our RPC guidelines (saying "Always use AmountFromValue and ValueFromAmount when inputting or outputting monetary values") have higher priority over the idea to simply use the same unit used for prioritisetransaction? Probably miners would be thankful to not need multiply the result by 100000000 first in order to delete mapDelta entries (and it would also make the tests simpler 😅), but happy to ACK either variant.
Left a few small nits below.
src/txmempool.cpp
Outdated
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.
Shouldn't these messages be returned as an RPC result?
Shouldn't the message here also show the resulting total delta, and perhaps whether the tx was already in the mempool (nTransactionsUpdated > 0)?
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.
Would it be API-breaky to change the result of prioritisetransaction?
I've edited the logs though, to show whether the tx is in mempool and what the new delta is. Btw I don't think nTransactionsUpdated starts out at 0 when this is called.
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.
Could add a -deprecatedrpc=prioritisetransactionresult flag to allow access to the current format of result.
ab47d8b to
3e5c90e
Compare
glozow
left a comment
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.
Addressed review comments and moved it from rpc/mempool.cpp to rpc/mining.cpp.
Probably miners would be thankful to not need multiply the result by 100000000 first in order to delete mapDelta entries (and it would also make the tests simpler 😅), but happy to ACK either variant.
I agree! I've changed it to satoshis to be the same as prioritisetransaction.
src/txmempool.cpp
Outdated
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.
Would it be API-breaky to change the result of prioritisetransaction?
I've edited the logs though, to show whether the tx is in mempool and what the new delta is. Btw I don't think nTransactionsUpdated starts out at 0 when this is called.
theStack
left a comment
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.
Code looks good to me, also agree with aj that getprioritisedtranactions is a better naming. Note that the first three commits still have the old names in their commit subjects and should be adapted accordingly.
This allows the user to see prioritisation for not-in-mempool transactions.
It's unnecessary to keep the data around, as it doesn't do anything. If prioritisetransaction is called again, we'll make a new entry in mapDeltas. These entries are only deleted when the transaction is mined or conflicted from a block (i.e. not in replacement or eviction), are persisted in mempool.dat, and never expire. If node operators use the RPC to regularly prioritise/de-prioritise transactions, these (meaningless) map entries may hang around forever and take up valuable mempool memory.
3e5c90e to
67b7fec
Compare
| /** The modified fee (base fee + delta) of this entry. Only present if in_mempool=true. */ | ||
| std::optional<CAmount> modified_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 field is set in GetPrioritisedTransaction, but not read anywhere. Was there a plan to also return that in getprioritisedtransactions or is there any other future use-case? If not, I think it can be removed.
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.
Correct, it is not used. I added it to address this comment: #27501 (comment).
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 could add the modified fee to the RPC result? I don't feel strongly, happy to change however people want.
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.
Have no strong opinion on that either, just wanted to make sure that there wasn't some code using modified_fee field around that was maybe forgotten to be pushed or sth alike.
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.
Probably better to either expose it via the rpc, or not have the useless code at all?
|
Concept ACK |
theStack
left a comment
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.
Code-review ACK 67b7fec
instagibbs
left a comment
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.
code review ACK 67b7fec
just test suggestions, everything looks good
| { | ||
| {RPCResult::Type::OBJ, "txid", "", { | ||
| {RPCResult::Type::NUM, "fee_delta", "transaction fee delta in satoshis"}, | ||
| {RPCResult::Type::BOOL, "in_mempool", "whether this transaction is currently in mempool"}, |
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.
is there a particular reason we're exposing in-mempool-ness here? No strong feelings, just curious.
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.
Provides a good excuse to map to an object rather than just a the delta itself, which in turn might make it easier to add other info in future without having a breaking 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.
100% for object for future-proofing, either way
| assert_equal(raw_before[txid_a], raw_after[txid_a]) | ||
| assert_equal(raw_before, raw_after) | ||
| prioritisation_map_in_mempool = self.nodes[0].getprioritisedtransactions() | ||
| assert_equal(prioritisation_map_in_mempool[txid_b], {"fee_delta" : fee_delta_b*COIN, "in_mempool" : True}) |
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.
just directly construct the entire 2-entry map and assert here? means we wouldn't miss extraneous entries in future regression. Could also keep a running "tally" object that's modified along with the test cases
| self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_1 * COIN)) | ||
| self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_2 * COIN)) | ||
| prioritisation_map_not_in_mempool = self.nodes[0].getprioritisedtransactions() | ||
| assert_equal(prioritisation_map_not_in_mempool[txid_b], {"fee_delta" : fee_delta_b*COIN, "in_mempool" : False}) |
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.
same as above?
| assert_equal(raw_before[txid_a], raw_after[txid_a]) | ||
| assert_equal(raw_before, raw_after) | ||
| prioritisation_map_in_mempool = self.nodes[0].getprioritisedtransactions() | ||
| assert_equal(prioritisation_map_in_mempool[txid_b], {"fee_delta" : fee_delta_b*COIN, "in_mempool" : True}) |
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.
same as above?
| } | ||
| ++nTransactionsUpdated; | ||
| } | ||
| if (delta == 0) { |
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.
maybe add a test case that shows prioritising by value of 0 doesn't add an entry, to cover all the bases
ajtowns
left a comment
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 67b7fec code review only, some nits
|
|
||
| struct delta_info { | ||
| /** Whether this transaction is in the mempool. */ | ||
| const bool in_mempool; |
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 strange to make these const. const auto deltas = mempool.GetPrioritisedTransactions() should be enough...
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 think const in this context is mainly used to force the designated initializer to initialize the field, because no constructor exists and otherwise this may end up being uninitialized?
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 like it would be better to explicitly specify defaults (false and 0?) in that case? If you want to delete the default constructor to force aggregate initialization, then delta_info() = delete; seems more direct?
| "Returns a map of all user-created (see prioritisetransaction) fee deltas by txid, and whether the tx is present in mempool.", | ||
| {}, | ||
| RPCResult{ | ||
| RPCResult::Type::OBJ_DYN, "prioritisation-map", "prioritisation keyed by txid", |
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 just be "" rather than "prioritisation-map" ?
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, the string is eaten by the help generator either way
| RPCResult{ | ||
| RPCResult::Type::OBJ_DYN, "prioritisation-map", "prioritisation keyed by txid", | ||
| { | ||
| {RPCResult::Type::OBJ, "txid", "", { |
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 "transactionid" in getrawmempool and getmempoolancestors, probably better to be consistent?
| { | ||
| {RPCResult::Type::OBJ, "txid", "", { | ||
| {RPCResult::Type::NUM, "fee_delta", "transaction fee delta in satoshis"}, | ||
| {RPCResult::Type::BOOL, "in_mempool", "whether this transaction is currently in mempool"}, |
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.
Provides a good excuse to map to an object rather than just a the delta itself, which in turn might make it easier to add other info in future without having a breaking change?
src/txmempool.cpp
Outdated
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.
Could add a -deprecatedrpc=prioritisetransactionresult flag to allow access to the current format of result.
| /** The modified fee (base fee + delta) of this entry. Only present if in_mempool=true. */ | ||
| std::optional<CAmount> modified_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.
Probably better to either expose it via the rpc, or not have the useless code at all?
|
ACK 67b7fec |
Github-Pull: bitcoin#27501 Rebased-From: 9e9ca36
This allows the user to see prioritisation for not-in-mempool transactions. Github-Pull: bitcoin#27501 Rebased-From: 99f8046
Github-Pull: bitcoin#27501 Rebased-From: 0e5874f
…d expiry Github-Pull: bitcoin#27501 Rebased-From: c1061ac
It's unnecessary to keep the data around, as it doesn't do anything. If prioritisetransaction is called again, we'll make a new entry in mapDeltas. These entries are only deleted when the transaction is mined or conflicted from a block (i.e. not in replacement or eviction), are persisted in mempool.dat, and never expire. If node operators use the RPC to regularly prioritise/de-prioritise transactions, these (meaningless) map entries may hang around forever and take up valuable mempool memory. Github-Pull: bitcoin#27501 Rebased-From: 67b7fec
…nd delete a mapDeltas entry when delta==0 0eebd6f test: Assert that a new tx with a delta of 0 is never added (kevkevin) cfdbcd1 rpc: exposing modified_fee in getprioritisedtransactions (kevkevin) 252a867 rpc: renaming txid -> transactionid (kevkevin) 2fca6c2 rpc: changed prioritisation-map -> "" (kevkevin) 3a118e1 test: Directly constructing 2 entry map for getprioritisedtransactions (kevkevin) Pull request description: In this PR I am addressing some comments in #27501 as a followup. - changed `prioritisation-map` in the `RPCResult` to `""` - Directly constructing 2 entry map for getprioritisedtransactions in functional tests - renamed `txid` to `transactionid` in `RPCResult` to be more consistent with naming elsewhere - exposed the `modified_fee` field instead of having it be a useless arg - Created a new test that asserts when `prioritisedtransaction` is called with a fee_delta of 0 it is not added to mempool ACKs for top commit: glozow: reACK 0eebd6f, only change is the doc suggestion Tree-SHA512: e99056e37a8b1cfc511d87c83edba7c928b50d2cd6c2fd7c038976779850677ad37fddeb2b983e8bc007ca8567eb21ebb78d7eae9b773657c2b297299993ec05
Add an RPC to get prioritised transactions (also tells you whether the tx is in mempool or not), helping users clean up
mapDeltasmanually. WhenCTxMemPool::PrioritiseTransactionsets a delta to 0, remove the entry frommapDeltas.Motivation / Background
mapDeltasentries are never removed from mapDeltas except when the tx is mined in a block or conflicted.prioritisetransactionfor a tx that isn't in the mempool {yet, anymore}. A user can may resbumit a tx and it retains its priority, or mark a tx as "definitely accept" before it is seen.mapDeltasis persisted to mempool.dat and loaded on restart. This is also good, otherwise we lose prioritisation on restart.removeForBlockis called, i.e. when the block is received. If you load a mempool.dat containingmapDeltaswith transactions that were mined already (e.g. the file was saved prior to the last few blocks), you don't delete them.mapDeltas. If you add a priority and forget what the value was, the only way to get that information is to inspect mempool.dat.prioritisetransactionwith an inverse value does not remove it frommapDeltas, it just sets the value to 0. It disappears on a restart (LoadMempoolchecks if delta is 0), but that might not happen for a while.Added together, if a user calls
prioritisetransactionvery regularly and not all those transactions get mined/conflicted,mapDeltasmight keep lots of entries of delta=0 around. A user should clean up the not-in-mempool prioritisations, but that's currently difficult without keeping track of what those txids/amounts are.