Skip to content
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

Allow for non-'static Module #493

Closed
wants to merge 5 commits into from
Closed

Allow for non-'static Module #493

wants to merge 5 commits into from

Conversation

plafer
Copy link
Contributor

@plafer plafer commented Mar 2, 2023

Closes: #490

Description

This PR removes the 'static bound from the Module trait. This has a ripple effect of requiring us to track that lifetime on the Router, ValidationContext, and ExecutionContext.

Associated basecoin-rs PR.


PR author checklist:

  • Added changelog entry, using unclog.
  • Added tests.
  • Linked to GitHub issue.
  • Updated code comments and documentation (e.g., docs/).
  • Tagged one reviewer who will be the one responsible for shepherding this PR.

Reviewer checklist:

  • Reviewed Files changed in the GitHub PR explorer.
  • Manually tested (in case integration/unit/mock tests are absent).

@codecov
Copy link

codecov bot commented Mar 2, 2023

Codecov Report

Patch coverage: 94.92% and project coverage change: +0.07 🎉

Comparison is base (61c33db) 71.52% compared to head (db8e663) 71.59%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #493      +/-   ##
==========================================
+ Coverage   71.52%   71.59%   +0.07%     
==========================================
  Files         125      125              
  Lines       15890    15939      +49     
==========================================
+ Hits        11365    11412      +47     
- Misses       4525     4527       +2     
Impacted Files Coverage Δ
crates/ibc/src/core/context.rs 84.34% <ø> (ø)
crates/ibc/src/core/ics02_client/client_state.rs 75.00% <ø> (ø)
crates/ibc/src/core/ics03_connection/handler.rs 93.33% <ø> (ø)
crates/ibc/src/core/ics04_channel/context.rs 77.77% <ø> (ø)
.../ibc/src/core/ics04_channel/handler/send_packet.rs 81.63% <ø> (ø)
crates/ibc/src/core/ics26_routing/context.rs 81.48% <ø> (-0.67%) ⬇️
crates/ibc/src/core/handler.rs 79.10% <11.11%> (+0.84%) ⬆️
...s/ibc/src/clients/ics07_tendermint/client_state.rs 51.82% <37.50%> (ø)
crates/ibc/src/test_utils.rs 78.89% <98.91%> (-0.78%) ⬇️
crates/ibc/src/applications/transfer/context.rs 51.06% <100.00%> (-0.68%) ⬇️
... and 30 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@plafer plafer requested a review from Farhad-Shabani March 2, 2023 20:31
@plafer plafer marked this pull request as ready for review March 2, 2023 20:31
Copy link
Member

@Farhad-Shabani Farhad-Shabani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can totally see how this change was like a hassle for you :)

I had thought about the changes in this PR and the previous one in #480. It seems like we might be reducing our support for Tendermint chains and putting more responsibility on implementors to handle things on their end (like with the IbcModuleWrapper trait we defined in basecoin-rs). Maybe we should create a new trait called ModuleSafe that is bound by send + sync + 'static. We can then pass it to the Router as an associated type. (ofc as one possible solution, there might be other ways to handle it) WDYT?

Comment on lines +123 to +128
pub trait Router<'m> {
/// Returns a reference to a `Module` registered against the specified `ModuleId`
fn get_route(&self, module_id: &ModuleId) -> Option<&dyn Module>;
fn get_route(&self, module_id: &ModuleId) -> Option<&(dyn Module + 'm)>;

/// Returns a mutable reference to a `Module` registered against the specified `ModuleId`
fn get_route_mut(&mut self, module_id: &ModuleId) -> Option<&mut dyn Module>;
fn get_route_mut(&mut self, module_id: &ModuleId) -> Option<&mut (dyn Module + 'm)>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like Router is making us to use lifetime everywhere. I think Boxing the return types of get_route and get_route_mut to Option<Box<&dyn Module>> and Option<Box<&mut dyn Module>> respectively, alleviates using lifetime annotations all over the repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boxing a reference seems odd; I suspect that this doesn't actually solve the problem if you want to allow to return Modules with a non-'static lifetime from get_route. In other words, it is not sufficient to remove the 'static bound from Module, you also have to let them be return-able from the Router; and I'd be surprised that there'd be a way to do that without tracking the lifetimes?

I'll look for a counter-example to better show what I mean

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the best solution is simpler than my implementation and yours: it's to simply not change anything! Yes, very silly of me... :

pub trait Router {
    fn get_route(&self, module_id: &ModuleId) -> Option<&dyn Module>;
}

which, without any lifetimes elided, is equivalent to:

pub trait Router {
    fn get_route<'s, 'a>(&'s self, module_id: &'a ModuleId) -> Option<&'s (dyn Module + 's)>;
}

The crucial element that makes it work is the + 's in dyn Module + 's; that is, by default, dyn Modules have the proper lifetime bound (especially not 'static)!

Why my solution worked

My solution worked because implicitly, 'm < 's ('s outlives 'm). So implementations that return a &'s (dyn Module + 's) satisfy the requirement (probably all real-world implementations).

But really the 'm is unneeded and can simply be removed.

Why your solution worked

Your solution, without lifetime elision, expands to:

pub trait Router {
    fn get_route<'s, 'a>(&'s self, module_id: &'a ModuleId) -> Option<&'s Box<dyn Module + 's>>;
}

The dyn Modules have a 's lifetime bound, which is what we want; the Box is not necessary and can be removed.

How to fix this

I think the PR should have been as simple as removing the AsAnyMut bound on Module (and fixing the tests), and not touching the Router at all... Yes, very silly. I'll try that on Monday!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into it. Glad we can keep things simple 👌

Comment on lines 82 to 85
#[derive(Debug)]
pub struct DummyTransferModule {
pub struct DummyTransferContext {
ibc_store: Arc<Mutex<MockIbcStore>>,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get where you're coming from with differentiating the Module and Context implementation. But, in reality, both DummyTransferModule and DummyTransferContext are pretty much the same, just with different names. Plus, DummyTransferContext uses the same ibc_store that's already provided by MockContext. It can get confusing trying to figure out the functionality of each when you're looking at both MockContext and DummyTransferContext/DummyTransferModule at the same time.

Why not extend the capabilities of MockContext by applying token transfer contexts on it? was there any reservation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is the messiest part of the solution. The core problem is that the previous way it was implemented downcasted the Module to a DummyTransferModule, and we can on longer do that because downcasting requires core::any::Any, which requires 'static. See #491 for how I think this could be improved.

I ended up going for this because it worked. I think there was an issue when implementing on the MockContext, because we still need the traits for the callback handlers... But then again there might be a way to make it work.

Note that I find that the shared store architecture is confusing because it couples our types in confusing ways; the whole thing is in need of a refactor IMO.

@plafer plafer mentioned this pull request Mar 6, 2023
7 tasks
@plafer
Copy link
Contributor Author

plafer commented Mar 6, 2023

Closing in favor of #499.

@plafer plafer closed this Mar 6, 2023
@plafer plafer deleted the plafer/490-module-static branch March 6, 2023 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove 'static requirement on Module
2 participants