-
Notifications
You must be signed in to change notification settings - Fork 446
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: optimize hook module read in warp apply process by using provide… #5541
base: refactor/consolidate-warp-route-updates
Are you sure you want to change the base?
fix: optimize hook module read in warp apply process by using provide… #5541
Conversation
🦋 Changeset detectedLatest commit: 4f09729 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
8100621
to
ad86c09
Compare
78aac8d
to
a777c6f
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.
good progress
@@ -132,7 +129,7 @@ export class EvmHookModule extends HyperlaneModule< | |||
public async read(): Promise<HookConfig> { | |||
return typeof this.args.config === 'string' | |||
? this.args.addresses.deployedHook | |||
: this.reader.deriveHookConfig(this.args.addresses.deployedHook); | |||
: { ...this.args.config }; |
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 generally still want the Modules to derive onchain values. however, i do see your intention.
the challenge here is that we need some way to force update()
to derive, but have another way to detect if this.args.config
is an already derived config.
One way we can do this is by checking if the field address
exists. This should mean that it's a DerivedHookConfig
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, can you take another look and confirm I understood correctly?
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
.changeset/eighty-planets-tan.md
Outdated
'@hyperlane-xyz/sdk': minor | ||
--- | ||
|
||
Optimize HookModule.read() by removing redundant hook config derivation |
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.
needs update?
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.
updated
return typeof this.args.config === 'string' | ||
? this.args.addresses.deployedHook | ||
: this.reader.deriveHookConfig(this.args.addresses.deployedHook); | ||
const { config, addresses } = this.args; |
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 know you didn't create this, but can you add some comments to both the class, and the update and read function so that consumers know how to think about the semantics and API? It's not super obvious to me why you are able to just skip the hook derivation. IIUC, its that under only some circumstances we pass the actual config in, and the module can just assume that as the result of the read? If that is the case, imo the api has to be made pretty clear, when I call .read
, i would expect that the module actually reads. Probably worth adding tests here if they dont exist cc @ltyu
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.
@ltyu can you please first clarify this and then I write the comments?
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 reason read() has this branching logic is because the Module was designed to allow
- initialization through the constructor, which is used when we have an existing deployed Hook config.
- initialization through
create()
, in this case, it is used to create a new Hook.
In both cases, we want to allow the caller to read()
. The reason I suggested using the address
is due to the first point. If a user initializes a config through the constructor, we can assume they have an existing deployed config i.e., a DerivedHookConfig. For the latter point, deployedHook
address will be populated so we will derive from it.
alternatively, we can also populate this.args.config in the create() with a DerivedConfig to simplify read()
I'm not sure what the if (typeof config === 'string')
is used for. perhaps @paulbalaji can clarify
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.
Ok, so just talked IRL with @ltyu and @paulbalaji, here is our current assessment:
There is a few references for config
in AbstractHyperlaneModule and its children (i.e. Core, Hook, ISM, ICA). As far as I can tell:
- It's mainly a convenience compiler enforcement for the
read
andupdate
function usage of the config - In
module.read
, when thethis.args.config
is a string (i.e a desired hook or ISM that is passed as the contract address) we use that as in indication to skip reading. Afaict, we do so because we expect that it's not derivable and the user "intentionally" set it as such (like when they have a bespoke hook/ISM they want to use) - We do generally pass the constructor-level
config
as "the expected config" for the module, however, we theupdate
andcreate
functions already expect the config as an explicit argument. - In all module
update
functions, we always explicitly callread
which tries to derive the config
All these observations lead me to the following conclusions:
- We practically don't actually use the
TConfig
of the storedargs: HyperlaneModuleParams
- We pretty much always read module config even though the caller/constructor of the module already has it (or they don't, but
module.read
wouldn't give that to them either. - We primarily use the fact that both hook and ism config can be specified as the contract address.
What I would like to suggest us to do:
- Confirm the above understanding
- Remove the storing of the config and replace it with its current only intended usage in form of indicating whether or not to attempt reading the config or not
- Change the function signature of
module.update
to allow the caller to pass in the current config as an optional argument (that like warp module has already read for hook/ism). Alternatively, allow consumers to explicitly set the current config in an instance method (vs. implicitly as a generic constructor argument)
More generally:
- Be much more clear about how we use config and avoid
config
by itself (i.e. prefer to usetargetConfig
,actualConfig
,onchainConfig
etc.)
@mshojaei-txfusion discussing with the team right now, so consider this issue/PR blocked until we confirm the recommendation
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.
Filing #5610 as somewhat related
// We need to normalize the current and target configs to compare. | ||
const normalizedCurrentConfig = normalizeConfig(await this.read()); | ||
const normalizedTargetConfig = normalizeConfig(targetConfig); | ||
|
||
// Update the config |
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.
Why does moving this here make any difference? I feel like if we have logic that depends on the module state, then we should make that a lot more obvious
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.
As now we use this.args.config
on read as well, we can't change it before read.
What do you exactly mean by "we should make that a lot more obvious"?
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 should have clear semantics as to when instance variables (like this.args.config
are populated and readable). In this case, it sounds like this.args.config is expected to be not usable until read
happens, and that is not obvious at all
Description
This PR simplifies the hook module read functionality by removing the
EvmHookReader
class and its associated complexity. The changes include:EvmHookReader
import and instance inEvmHookModule
read()
method to directly return the configupdate()
method to ensure proper comparisonThese changes make the code more maintainable and reduce unnecessary complexity while maintaining the same functionality.
Drive-by changes
update()
method to happen after normalization comparisonRelated issues
Backward compatibility
Yes - These changes are backward compatible. The functionality remains the same, we're just simplifying the implementation. No infrastructure implications as this is a refactoring of the internal SDK code.
Testing
Manual - The changes have been tested manually to ensure the hook module read and update functionality works as expected. The changes are minimal and focused on internal implementation details.