-
Notifications
You must be signed in to change notification settings - Fork 86
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/gateway message processing #1991
Conversation
@@ -202,9 +202,13 @@ pub mod pallet { | |||
fn service_message_queue(max_weight: Weight) -> Weight { | |||
let mut weight_used = Weight::zero(); | |||
|
|||
let mut processed_entries = Vec::new(); | |||
let mut nonces = MessageQueue::<T>::iter_keys().collect::<Vec<_>>(); | |||
nonces.sort(); |
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.
Since we are collecting the keys here, it also made sense to me to sort them, just in case. Please let me know if there are any objections to this.
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.
Although this new solution is more simple, I think there is a problem here:
MessageQueue::<T>::iter_keys().collect::<Vec<_>>();
It can collect many keys, making the block impossible to build.
I think we need a complex structure here that allow us to store them already organized
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 can collect many keys, making the block impossible to build.
Can you elaborate on this please?
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 about limiting the number of keys that we collect via:
let mut nonces = MessageQueue::<T>::iter_keys().take(MAX_MESSAGES_PER_BLOCK).collect::<Vec<_>>();
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.
Can you elaborate on this please?
When you collect, the iterator will make one read per item, and could be a number of items that overpass the limit for the block weight capacity.
The take(MAX_MESSAGES_PER_BLOCK)
still does not work because could be left a message in the queue that ideally should be processed first.
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.
Not sure if exists some order structure available in substrate for this. If not, we should create some complex/annoying structure to organize the way the messages are stored.
But I'm not able to see a super simple way TBH. We can leave that fix for another PR to unlock this if we see it's not easy
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 there's a simpler solution that involves using the latest message nonce. I'll try something on a different branch.
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.
Something similar to - #1992
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.
Thanks for all the fixed!
BTW, not sure if we should move this to the internal repo, to not forking both main
branches too much.
@@ -202,9 +202,13 @@ pub mod pallet { | |||
fn service_message_queue(max_weight: Weight) -> Weight { | |||
let mut weight_used = Weight::zero(); | |||
|
|||
let mut processed_entries = Vec::new(); | |||
let mut nonces = MessageQueue::<T>::iter_keys().collect::<Vec<_>>(); | |||
nonces.sort(); |
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.
Although this new solution is more simple, I think there is a problem here:
MessageQueue::<T>::iter_keys().collect::<Vec<_>>();
It can collect many keys, making the block impossible to build.
I think we need a complex structure here that allow us to store them already organized
if res.is_ok() { | ||
TransactionOutcome::Commit(Ok::<(DispatchResult, Weight), DispatchError>(( | ||
res, weight, | ||
))) | ||
} else { | ||
TransactionOutcome::Rollback(Ok::<(DispatchResult, Weight), DispatchError>(( | ||
res, weight, | ||
))) | ||
} |
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.
Super NIT. I think adding the returned type in the closure allows to remove the Ok<stuff>
here
with_transaction(|| -> DispatchResult {
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.
That won't work given:
pub fn with_transaction<T, E, F>(f: F) -> Result<T, E>
where
E: From<DispatchError>,
F: FnOnce() -> TransactionOutcome<Result<T, E>>, // this
{
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.
But given that we will always return LP_DEFENSIVE_WEIGHT
for both inbound/outbound, we can change this a bit. I'll ping you when done.
@@ -333,7 +334,11 @@ impl<T: Config> Pallet<T> { | |||
// we can return. | |||
None => return Ok(()), | |||
Some(stored_inbound_entry) => match stored_inbound_entry { | |||
InboundEntry::Message(message_entry) => message = Some(message_entry.message), | |||
InboundEntry::Message(message_entry) | |||
if message_entry.session_id == session_id => |
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: If session_id
is different, should we remove the entry?
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: If
session_id
is different, should we remove the entry?
We should not because then it's impossible to replay the message and funds are stuck on the EVM side. Keeping entries for an old session id can be made unstuck via execute_message_recovery
.
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.
execute_message_recovery
will only increase the proof count for a specific router ID, we will still hit this logic and be unable to execute a message from an older session. Maybe we should extend execute_message_recovery
and either:
- set the session of a message entry to the current one;
- increase the proof count - current behavior;
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.
Bumping this again since I think we might need to add the 1st point that I mentioned above.
cc @hieronx
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1991 +/- ##
==========================================
+ Coverage 48.28% 48.41% +0.13%
==========================================
Files 183 183
Lines 13406 13412 +6
==========================================
+ Hits 6473 6494 +21
+ Misses 6933 6918 -15 ☔ View full report in Codecov by Sentry. |
* lp-gateway-queue: Ensure messages are processed in order * lp-gateway-queue: Ensure processed messages are skipped * integration-tests: Remove receipt check
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, thanks for tackling all critical queue related findings! Non-blocking nit and question.
// The #[transactional] macro only works for functions that return a | ||
// `DispatchResult` therefore, we need to manually add this here. |
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.
Q: Any reason for going with the overhead of with_transaction
and branching over the result instead of just using #[transactional]
and returning Ok(())
?
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.
Not sure what you mean here - do you mean changing the return of process
to DispatchResult
?
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.
We can't use the #[transactional]
macro in this function, that is not supported. We can either change the signature as I suggested in my previous message, or go with the current approach.
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.
Thanks for tackling this!!
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.
Re-approval
Description
Various fixes for LP gateway-related logic.
Changes and Descriptions
LP Gateway
MessageProcessor
implementation transactional to ensure that storage changes are reverted on failure.LP Gateway Queue
MessageQueue
keys, order them, and iterate over them when servicing theMessageQueue
.Checklist: