-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Make middlewares less confusing #11955
Comments
I like the proposal, but personally, I would still love to see a sequential approach rather than recursive one. Its so much easier to reason about when you know the ordering and execution stack of the middleware. Does proposal (1) still use a recursive approach? I'll also post this here as it might be useful. Osmosis did something similar with respect to EndBlock and BeginBlock: https://github.com/osmosis-labs/osmosis/blob/main/app/modules.go#L178. They essentially define a global ordering and then define more custom local relational ordering. Maybe we can do something similar? |
Not necessarily. Actual proposal 1 is similar to what you described in Osmosis: you define relative ordering of middlewares (see |
I think having a topological sort of a dependency graph is going to be the more correct approach for begin/end blocker, init chain, and ante/middleware ordering. Otherwise, you need to drop the idea of independent components altogether. I agree we should drop the wrapping behavior and just have things execute one after the other (or even in parallel in the future) based on dependency order. If there is need for before/after behavior that can be dealt with using two handlers where after depends on before. I'm wondering what the best way is to specify order. One thought is to do something similar to what's happening with dependency injection where handlers specify the types of things that must be created/happen using go types. This would be instead of passing func ExtractPubKeys(tx Tx) PubKeyInfo
func VerifySignatures(pubkeys PubKeyInfo) SignatureValidationResult { ... }
func DeductFees(sigs SignatureValidationResult)
func IncrementSeqs(sigs SignatureValidationResult) Then in dependency graph we would know that this order is required based on the type of information needed:
This may be a case where putting too much in I don't think we would actually need to do as much store branching with this approach - deduct fees and increment seqs should commit on their own if signatures were validated. IMHO they don't actually need a transaction wrapping from the outside but instead should be able to initiate a tx and commit to the store based on they already know. I can't think of a scenario where something else happening should prevent them from committing or even if say deduct fees fails but increment seqs doesn't that we should rollback both. I'm actually a bit confused by why in the current ordering we verify sigs after running deduct fees and then roll that back based on sigs instead of waiting to do it until after verifying sigs. |
Great, do you think proposal (1) is inline with that? I'd like to use something clean and simple. |
@aaronc Your We could also give a name ( |
I'm concerned that strings are hard to coordinate around and don't force good specification. Actually passing dependencies is more explicit even if it's "magic" |
Either way I think we should probably postpone a dependency graph solution till after v0.46. In the meantime, should we change to what we're doing in 0.46? Maybe moving to a simple ante and post handler approach where each of those has store branching would be an improvement, even if it's a bit of a step backwards? |
Yes, absolutely. The flow and model is just way too confusing...too much recursion. This is what I recommended to @AmauryM -- release v0.46 with the caveat that middleware is subject to API breakage. So I recommend we refactor it while releasing 0.46, but only a medium term slightly better refactor, while we work on a more permanent solution later. |
That could work. It solves problem 2 in OP, but not 1. We can think of middleware dependencies later. Alternatively, my preferred solution would be to revert all middleware changes, do branching in baseapp like before, and add a new posthandler in baseapp for tips. It's an even bigger step backwards, but at the end of the day it's less API-breaking changes, and probably safer too (less code changes compared to v0.45). |
I'm very in favor of:
|
I wouldn't call it recursion: a middleware doesn't call itself. It wait's for other downstream middleware to complete and continue it's execution. |
Many projects were asking and updating their code for the new middleware design. This will be a pain to do the revert and redo the work. |
It is 100% recursion...you're literally calling |
To be fair, antehandlers in this case would also be recursive by your definition. I think what made middlewares more confusing than antehandlers was that each middleware could be ante-runMsgs and post-runMsgs at the same time, and tracking that in a big pile of 15 middlewares is hard. |
Yes, technically that's true, but AnteHandlers were always ordered and executed sequentially ( |
Exactly, anthehandlers recuse but the good feature is that they user tail recursion, so correct - it's easy to reason. Personally I like the pre / post approach - it makes it powerful. However I agree it can create bugs if people don't think about it. |
I think @aaronc design is the right one, but it will require adding custom types for correctly resolving dependencies and it will be harder for newcomers. |
I think there are some cases where the middleware wrapping makes sense, but when you have to isolate state changes, it breaks down. A directed graph (i.e. A -> B -> C) is much easier to reason about and you can draw boxes around the parts which are in the same transaction. Otherwise with the middleware approach you end up with (pre A -> pre B -> pre C -> post C -> post B -> post A) and if you have transactions which only wrap say part of the post workflow, then you need to do weird things which is what we ended up with. |
Yes, exactly! |
I don't think reverting to anthe handlers is a good solution. Ante handler implementation is more complex and it is "dirty" (involves tangled code which breaks the modularity - see the Moreover, Antehandlers don't solve problem 1:
Antehanders must be correctly ordered as well. I would claim that it doesn't solve the problem 2 either:
Ante handlers use the With Middlewares, at least this is more clean. |
I wrote more analysis here: https://hackmd.io/UY-Xh1gBQTmK95WXjJ_anw with motivation AGAINST Antehandlers. It also provides another way how we can implement Middleware with example implementation |
@robert-zaremba you have dead links in there so I have no idea what As for your alternative, I'm still pretty opposed to it. I really do not like the notion of "pre" and "post" logic being executed in the same context. It's too confusing IMO. While you may argue that it's cleaner and more extensible, I think that's pretty subjective as I can't see how that's the case. |
FWIW, I'm not really a fan of the I feel like theres other ways to handling the gas metering panicing, e.g. making a single "gas handling wrapper" around the rest of the ante handler. Such wrapping activities should be very explicitly specified, rather than via post-handling methods imo. The linearization of events should always remain clear. |
Yes, I totally agree @ValarDragon |
Another aspect to the current decision to revert is that we feel that if we're releasing something new, it should be something we want to stick with for a while. That clearly isn't the current state of things and we don't think we'll have a better API ready in a short period of time. So reverting to the previous thing we had working seems better than releasing an API we can't stand behind. |
@alexanderbez sorry, the links were missing a tag, I've updated them @ValarDragon almost all Antehandlers are written using the decorator style - so with the |
@ValarDragon where the linearization of the handlers/middlewares is not clear? |
I view that v0.46.0 failed to revert fees upon signature failure as linearization of state reversion logic being very unclear Also the next api design feels like a hack to simplify some panic recovery, that makes everything pay in complexity. I can't fearlessly add a new ante handler, it could very well just skip every ante handler after it. It should have a worst case of just rejecting a tx, not causing a tx with invalid sig to be accepted. |
) ## Description We decided to remove middlewares, and revert to antehandlers (exactly like in v045) for this release. A better middleware solution will be implemented after v046. ref: #11955 Because this refactor is big, so I decided to cut it into two. This PR is part 1 of 2: - part 1: Revert baseapp and middlewares to v0.45.4 - part 2: Add posthandler, tips, priority --- Suggestion for reviewers: This PR might still be hard to review though. I think it's easier to actually review the diff between v0.45.4 and this PR: - `git difftool -d v0.45.4..am/revert-045-baseapp baseapp` - most important parts to review: runTx, runMsgs - `git difftool -d v0.45.4..am/revert-045-baseapp x/auth/ante` - only cosmetic changes - `git difftool -d v0.45.4..am/revert-045-baseapp simapp` --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
…11985) ## Description We decided to remove middlewares, and revert to antehandlers (exactly like in v045) for this release. A better middleware solution will be implemented after v046. ref: #11955 This PR is part 2 of 2: - part 1: Revert baseapp and middlewares to v0.45.4 - part 2: Add posthandler, tips, priority Depends on: - [x] #11979 --- Suggestion for reviewers: - Apart from correctness, I would also like someone to review **exhaustiveness**. I.e. all changes we made in v046 into the [middleware folder](https://github.com/cosmos/cosmos-sdk/tree/v0.46.0-beta2/x/auth/middleware) are reflected in this PR, and that I didn't forget anything. I found the following ones: - add a TxFeeChecker in DeductFee - add a ExtensionChecker in ExtCheckerDecorator - add a TipDecorator --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
In v0.46.0-rc1 we reverted to antehandlers. I'll keep this issue open for future work on how to best refactor antehandlers. |
…ion (#11988) ## Description ref: #11955 - [x] Reverts the documentation updates about middlewares (#11918, #11445 and part of #11860). - [x] Adds explanation about post-handlers --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
…ion (#11988) ## Description ref: #11955 - [x] Reverts the documentation updates about middlewares (#11918, #11445 and part of #11860). - [x] Adds explanation about post-handlers --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) (cherry picked from commit f5694bf)
…ion (#11988) (#12065) ## Description ref: #11955 - [x] Reverts the documentation updates about middlewares (#11918, #11445 and part of #11860). - [x] Adds explanation about post-handlers --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) (cherry picked from commit f5694bf) Co-authored-by: Julien Robert <julien@rbrt.fr>
…mos#11979) ## Description We decided to remove middlewares, and revert to antehandlers (exactly like in v045) for this release. A better middleware solution will be implemented after v046. ref: cosmos#11955 Because this refactor is big, so I decided to cut it into two. This PR is part 1 of 2: - part 1: Revert baseapp and middlewares to v0.45.4 - part 2: Add posthandler, tips, priority --- Suggestion for reviewers: This PR might still be hard to review though. I think it's easier to actually review the diff between v0.45.4 and this PR: - `git difftool -d v0.45.4..am/revert-045-baseapp baseapp` - most important parts to review: runTx, runMsgs - `git difftool -d v0.45.4..am/revert-045-baseapp x/auth/ante` - only cosmetic changes - `git difftool -d v0.45.4..am/revert-045-baseapp simapp` --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
closing since we reverted the middleware approach |
Summary
Reconsider the middleware "decorator" approach as described in ADR-045 in favor of a less confusing model.
Problem Definition
Multiple feedback concur that middlewares are hard to reason about (cf Bez, Amaury, Yihuang).
Some problems:
1. Middlewares have dependencies between them, but those dependencies are not clearly defined.
For example,
SigVerificationMiddleware
always needs to be run afterSetPubKeyMiddleware
, but the only place where this dependency is defined is in the godoc comment. OrGasTxMiddleware
: any other middlewares that access the GasMeter needs to run after it. Again, this is only enforced by go comments.If any chain puts the middlewares in wrong order, then there are security risks.
2. It's hard to track pre- and post-runMsg logic for the whole middleware stack.
If
middlewareA
is belowmiddlewareB
in the stack, it's slightly unclear which which one's pre/post-hook runs after the other's pre/post-hook. But this gets impossible to track with 15+ middlewares. For example, store branching and when to write back to store is hard to get right with the current composition design: #11942.In
ConsumeBlockGasMiddleware
, we also use adefer func()
. Good luck tracking that ordering with other post-hook logic.Middleware dependencies
Here's a quick overview of the dependencies between middlewares.
\t⬑
means "depends on" i.e. "needs to run after", and items on the same level of indentation can be run parallely (have no dependencies).Proposals
In any of the 2 proposals below, we should consider removing the possibility of having both pre and post logic for middlewares. A middleware should either be a pre-middleware, or a post-middleware, not both at the same time.
Proposal 1: Build a dependency graph between middlewares
App developers specify relative dependencies of middlewares.
Then, an algorithm will propose a graph that satisfies all the defined dependencies. Finally, the SDK will figure out the sequential ordering of middlewares by traversing the dependency graph, in a way that this sequential order satisfies all the initial dependencies.
Example:
In this case the SDK will know that any of the 2 following orderings are valid:
Done in v0.46
Proposal 2: Fallback to something similar to the old baseappWe could also define 2 middleware stacks:
This would make the code easier to reason about.
Further Notes
If we go with the dependency graph solution, we could use the same graph design for BeginBlockers, EndBlockers, InitChain etc (as proposed initially by @ValarDragon): instead of middleware dependencies, it would be module dependencies.
The text was updated successfully, but these errors were encountered: