-
Notifications
You must be signed in to change notification settings - Fork 307
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
fix: never read reserved txs from the mempool #1267
Conversation
func (s *store) has(txKey types.TxKey) bool { | ||
s.mtx.RLock() | ||
defer s.mtx.RUnlock() | ||
_, has := s.txs[txKey] | ||
tx, has := s.txs[txKey] | ||
if has && tx.isReserved() { | ||
return false | ||
} | ||
return has | ||
} |
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.
wasn't sure about this one @cmwaters
do we want to return true even if a tx is reserved? does this defeat the purpose of reserving in the first place?
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 this is correct. We don't want to mark the node as having the transaction until we have verified it. We might want to revisit this solution however when we add the tx status endpoint. There could be several states:
- Processing
- Pending
- Proposed
- Committed
- Evicted
- Rejected
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.
@@ -34,3 +34,5 @@ func newWrappedTx(tx types.Tx, key types.TxKey, height, gasWanted, priority int6 | |||
|
|||
// Size reports the size of the raw transaction in bytes. | |||
func (w *wrappedTx) size() int64 { return int64(len(w.tx)) } | |||
|
|||
func (w *wrappedTx) isReserved() bool { return w.height == -1 } |
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.
[question] what is a reserved transaction? How does it relate to height == -1? Any links to docs would be helpful.
Also this function could use a Go doc.
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.
reserved tx is a cat thing, I'm not sure yet if we need it as afaik it's an optimization
celestia-core/mempool/cat/pool.go
Line 299 in 5cfce0b
if !txmp.store.reserve(key) { |
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 basically a way of not processing the same transaction in parallel. Say I receive transaction x and while processing it I receive tx x again I will verify it again because I haven't yet added it to the 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.
Rephrasing that: isReserved is true when a transaction has been received but hasn't been added to the mempool yet because it's still being processed? Does that mean isReserved == isProcessing?
Are these the exhaustive states a transaction could be in: https://github.com/celestiaorg/celestia-core/pull/1267/files#r1526067986
If yes, would it make sense to consolidate on one term either: isProcessing or isReserved?
yeah thanks, I'm waiting on this comment as I'm not sure if this is correct property or not #1267 (comment) and the test is failing because its testing for that property |
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.
Looks good to me
func (s *store) has(txKey types.TxKey) bool { | ||
s.mtx.RLock() | ||
defer s.mtx.RUnlock() | ||
_, has := s.txs[txKey] | ||
tx, has := s.txs[txKey] | ||
if has && tx.isReserved() { | ||
return false | ||
} | ||
return has | ||
} |
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 this is correct. We don't want to mark the node as having the transaction until we have verified it. We might want to revisit this solution however when we add the tx status endpoint. There could be several states:
- Processing
- Pending
- Proposed
- Committed
- Evicted
- Rejected
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.
Looks good to me, though I have a question regarding the reasoning behind storing reserved transactions.
@@ -27,7 +27,7 @@ func (s *store) set(wtx *wrappedTx) bool { | |||
} | |||
s.mtx.Lock() | |||
defer s.mtx.Unlock() | |||
if tx, exists := s.txs[wtx.key]; !exists || tx.height == -1 { | |||
if tx, exists := s.txs[wtx.key]; !exists || tx.isReserved() { |
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.
[Question]Why should we store a reserved transaction if it will not be processed due to being a duplicate?
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.
idk that's a great question cc @cmwaters
this was just adding a method for syntactic sugar, but it does seem like we should remove that check entirely
if tx != nil && tx.isReserved() { | ||
return nil | ||
} |
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.
[Question] In which cases a reserved tx exists, while the original tx does not?
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.
if its currently being processed by the 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.
Does it mean then there are situations in which the original tx is removed from the mempool, but the reserved one is still in the 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.
The original tx and the reserved tx are the same barring the fact that the height we received the transaction has not yet been set.
If a transaction fails to be added it is removed. If it succeeds the height is added and it's no longer considered reserved
while separate from this change, I'm fine with not merging until we resolve #1267 (comment) |
I don't understand this change enough to approve. |
@rootulp Does this sentence in the original issue help, or perhpaps we can sync on it quickly to get to the bottom it?
The fundemental problem is just that we can't pass len 0 txs to the application, as it will panic. Since the mempool processes things async, atm its possible to reap a reserved tx (a tx that is in the process of being added to the mempool, but only has a hash) |
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. Sync'ed with @evan-forbes offline. My rough notes:
isReserved
is unique to the CAT pool. It doesn't exist in other mempool implementations.isReserved
is a mechanism used to label transactions that are in the process of being added to the mempool.- We don't want a block proposer to reap an
isReserved
transaction and include it in a block yet. Therefore, methods likegetAllKeys
andgetAllTxs
were modified in this PR to return all non-reserved transactions. - Evan and I discussed two potential refactors:
- Modify the conditional in
celestia-core/mempool/cat/pool.go
Line 299 in f95a434
if !txmp.store.reserve(key) { - Consider adding a separate map for reserved transactions
This seems plausible as well. We essentially just want to check this map when a new transaction comes in, in case we are already processing it. It could just be a map of the tx hashes |
closing in favor of #1272 |
) This is an alternative to #1267 that solves the current problem with reserving transactions by using a separate lookup map as suggested by @rootulp.<hr>This is an automatic backport of pull request #1272 done by [Mergify](https://mergify.com). Co-authored-by: Callum Waters <[email protected]>
closes #1266
see details there.