-
Notifications
You must be signed in to change notification settings - Fork 42
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
Refactoring ContractWrapper
proposal
#26
Comments
This is why multi-test is the only non-1.0 package we have in the CosmWasm rust stuff... it may need breaking. I didn't follow all the details, but we can definitely think of a better formulation of this. In the end, all these generic things should be wrapped by
I envision a Also, I took a look at the diff of your refactor branch: main...contract-wrapper-refactor and I don't see the changes. Maybe you can make a draft PR and add some comments to explain how this issue relates to that code. |
Sure, just wanted to point it out.
That won't actually work in the current multitest design because of a single reason - we store contract code as I would even debate on giving all the methods default failing implementations, so one creating their own impl for any reason (I can imagine such a use-case) have an easier time not implementing IBC-related endpoints and/or reply/migrate.
I see the whole |
Makes sense. |
Context:
When I was figuring out what was to be done for IBC, I faced a bit of a wall in the form of a
ContractWrapper
. As this structure works, it is a serious boilerplate that would quickly go out of control. At this moment, there are 13 generics and the same amount of non-trivial trait bounds (by non-trivial, I mean - more than one bound in a single line). For now, we have six supported entry points in MT, and IBC adds another 6, which makes me think the number of those generics with names in the form of letter-number would roughly double (maybe not entirely, as messages for IBC are not custom, but errors would double for sure).Additionally, many types of aliases would also double in the count - https://github.com/CosmWasm/cw-multi-test/blob/main/src/contracts.rs#L45-L55.
Also the approach taken leads to severe limitations - as all the builder functions (
new
orwith_sudo
) are takingfn
types instead of some generics, it is impossible to pass there any callable types not being trivial - obviously, for 99% cases it is ok, as we pass there entry point functions, but I can imagine one could like to use this utility to build ad-hoc fake/mock/stub contract passing there some closures - and it won't work if those closures capture any context.The more severe limitation is related to how
_empty
functions are defined - they take functions which sometimes takeQ
being empty, sometimes
C`, and sometimes both.For example - the
new_with_empty
assumes all passed functions to have bothC
andQ
anEmpty
, whilemigrate
assumesC
being empty, butQ
already being of the expected type. I assume it is an oversight, but it leads to inconsistencies and messy API. We want some consistent API for this, but on the other hand - I would like to leave those functions mostly as they are to minimize API changes (or make them more generic so they still can be called the old way).Proposal
I want to do a couple of things about the
contract.rs
. The first one is obvious - split the file.ContractWrapper
in whatever form deserves its own module, asContract
trait is.Contract
trait by its nature has to contain all the entry points and it is not a thing to be split in any way, but it will grow (now with IBC entry points, no one knows about the future).The other thing is a bit more complex but should lead to way simpler maintenance of a
ContractWrapper
. I started POC of it oncontract_wrapper_refactor
branch. The core idea is to switch fromBox<dyn Fn(...) -> ...>
functions to generics. In the endContractWrapper
would be defined as something like:Then all bounds would be reduced in impl-block as follows:
Because of trait bounds in impl-block it would be impossible to create an instance of an invalid wrapper, and it would look way clearer.
Before defining the function traits, I would like to point out a problem - the change is breaking. If at any point anyone used the
ContractWrapper
spelling out his generics, he will have to get rid of it - however, I doubt anyone did it. In our examples, we always box it and return it asBox<dyn Contract>
, and it is how it should be used - and if one wants to avoid dynamic dispatch for his case (don't see a use case as this is only to be stored in MT which keeps it as trait object, but maybe), then he should use the wrapper asimpl Contract
. Therefore it is reasonable to do this break, and it is best to do it before IBC implementation because it would make it saner to work on doubling the entry points count.Now let me move to the last important part of the refactor - the functions trait. Those fellows are just extension traits for
Fn
, which:The example trait is the
ContractFnT
:This would allow passing any simple function (just like right now), but it still doesn't enable passing function-like objects. There is trickery to allow this also; the problem to overcome is that such an object could have multiple
Fn
implemented, in particular with differentmsg
arguments - a rough idea of how to fix it by allowing passing arbitrary function-like argument by turbo fishing themsg-type if needed can be found here, on more straightforward example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7843098bcff9460cfd56aadf75f30375. With some
GAT`s love this can be done even nicer, but not sure about updating MRCV for this (not sure if the current one is including GATs).Finally
Contract
trait has to be defined onContractWrapper
, which is trivial.The final touch is about commonizing the
(C, Q) -> (Empty, Empty)
generalization. I find thatdecostumize_deps
and so are supposed to be extension traits for their types (Deps
in this case). Reasoning for that is that it would enable trait-binding on the fact that casting from oneDeps
to another is possible. It should then provide a bit nicer APIs, and also allow for more generic transformation (as "I want to test my contract on different chain supporting superset of messages I use"). This would lead to adding functions on our contract function traits, let say:This is to be added later, but it would allow one to convert entry-point functions between compatible chains arbitrarily:
All the
_with_empty
functions would be left (possibly marked as deprecated at some point) to minimize any changes in code using MT right now.The text was updated successfully, but these errors were encountered: