-
Notifications
You must be signed in to change notification settings - Fork 371
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
feat!: Implement the version module #1640
Conversation
Codecov Report
@@ Coverage Diff @@
## v0.13.x #1640 +/- ##
==========================================
Coverage ? 49.09%
==========================================
Files ? 84
Lines ? 4532
Branches ? 0
==========================================
Hits ? 2225
Misses ? 2123
Partials ? 184 Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it 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.
this was meant as an exploration/hacky module for the incentivized testnet fork, but if we end up liking the mechanism, we can cherry-pick this to main as well.
// EndBlock wraps the BaseApp's end block method. This is done to modify the app | ||
// version if necessary. | ||
func (app *App) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { | ||
res = app.BaseApp.EndBlock(req) | ||
ctx := app.GetContextForDeliverTx([]byte{1}) | ||
return appversion.EndBlocker(ctx, app.VersionKeeper, res) | ||
} |
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 have to wrap end block because the base app doesn't actually have a way to modify the app version. It instead relies on the app version being set in BaseApp
, and then returned during the Info
call on boot. To avoid that, we modify the consensus param updates and pass them 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.
I still don't quite get this. Can't you modify the app version in ResponseEndBlock
?
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't you modify the app version in ResponseEndBlock?
that's what we're doing during the version.EndBlocker
, but the Baseapp
can't modify the app version during endblock afaik
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.
meaning that if we do this in the app's EndBlocker, the version won't actually change
Lines 568 to 571 in 058442d
// EndBlocker application updates every end block | |
func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { | |
return app.mm.EndBlock(ctx, req) | |
} |
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.
Does it get overwritten somewhere later on?
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.
only the events are collected in EndBlock, and GetConsensusParams never checks for version changes.
changing the app version using BaseApp
is entirely reliant upon setting the value via SetProtocolVersion
, which isn't passed back to tendermint until Info
, despite tendermint having the capability to update the version during EndBlock
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.
so yeah its overwritten. nothing happens because only the result of GetConsensusParams
is respected, which doesn't check for app vesion
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't we pass the SetProtocolVersion
function to the version module and just call it in the EndBlock
:) - Just an idea (It's what the upgrade module does).
BTW, the SDK should really modify the response to hide the fields you can't change to avoid confusion
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.
Ah I think I get what you mean. We have to set the app version in two different areas
newAppVersion := keeper.GetVersion(ctx) | ||
if ctx.BlockHeader().Version.App != newAppVersion { | ||
resp.ConsensusParamUpdates.Version = &coretypes.VersionParams{ | ||
AppVersion: newAppVersion, | ||
} | ||
// set the version in the application to ensure that tendermint is | ||
// passed the correct value upon rebooting | ||
keeper.versionSetter.SetProtocolVersion(newAppVersion) | ||
} |
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 have to set the app version in the application, exactly what the upgrade module does, but we also have to update the consensus params in order to avoid having to reboot.
Cherry-pick the following commits to the v0.12.x release branch: 1. ee84e3d 2. 909a566 Will create v0.12.2 after this merges --------- Co-authored-by: rene <[email protected]>
e155387
to
691cd36
Compare
A point of clarification. Are we expecting to hardcode the upgrade heights in the binary or get the validators to set them? Also a design question: why did you choose to have this separate from the modified |
@cmwaters note that this is targetting the v0.13.x branch, and is only for incentivized testnet atm. While we might want to reuse some of this for main, this PR is meant to explore what we could do and quickly get a rolling hardfork together.
the validators will eventually set them, although its a good point that we don't yet have that functionality
the target branch doesn't have a modified upgrade module, so initially I don't think I wanted to wrap or copy paste fork the current upgrade module. This is a good point and we should just stick it in there, should we go to main. |
If the validators set them in their config then why do we need it to be mapped with the chainID. Can't it just be:
|
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.
[no blocking feedback] I was a bit confused by the version map but the tests helped clarify its intended usage
sorry, by "validator's setting them" I thought you were referring to signaling, not having a local config. This PR, and therefore the BSR hardfork, will work by the validators choosing a binary that has them preconfigured. I went with the preconfigured approach since its still easy for anyone to change should they formally disagree, but it also attempts to avoid some confusion or miscommunication by requiring a different binary. If users are using the same version/commit of the app, then they will also fork at the same heights. as I think you were suggesting, since they're preconfigured if we want to support multiple networks without creating multiple releases, then we do need such a mapping. |
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.
SGTM.
Excited to try this out
// getVersion returns the app version for a given block height. It performs a | ||
// binary search on the ranges slice. | ||
func getVersion(height int64, ranges []HeightRange) uint64 { | ||
// Perform binary search on the ranges slice |
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.
lol, good job but I feel this is probably overkill. We might have 10 - 20 versions in our lifetime of 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.
yeah true good point
return map[string]ChainVersionConfig{ | ||
MochaChainID: version0Only, | ||
ArabicaChainID: version0Only, | ||
BlockspaceraceChainID: version0Only, |
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.
So we should update this when we know the height for the upgrade
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.
yep!
// ChainVersionConfig. Each hardfork should be added to this map. | ||
func StandardChainVersions() map[string]ChainVersionConfig { | ||
version0Only := NewChainVersionConfig(map[uint64]int64{ | ||
0: 0, |
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.
This is 0
because the current network is set to 0
right?
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.
yeah, hopefully we can stay 1:1 with our major version for simplicity
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.
This is pretty cool! Would be curious to get some eyes from the core SDK team on this well.
The state in the version module is abnormal because it can be modified by the | ||
party building the binary without breaking the app hash. This is because the | ||
state is not stored in the iavl tree, but rather as a simple predefined golang | ||
map. It's important to note that even though this state is not in the state |
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 add a sentence where this map is persisted if not in the state? (even if that is explained in more detail below)
merging now to not block, but will have a follow up PR for the extra sentence @liamsi |
) | ||
|
||
// EndBlocker will modify the app version if the current height is equal to | ||
// a predefined height at which the app version should be changed. |
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.
Late comment to this.
Will this still be activated if more than 2/3rds of the validator set didn't swap the binary before the upgrade height?
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.
nodes that did upgrade will, and nodes that didn't won't. So if 2/3s don't, then they can continue producing blocks while the nodes that did upgrade will halt.
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.
Awesome 👍
Overview
This PR adds a very minimal version module, which will update the app version in the header using end block based on a config. The configs are just hardcoded maps atm, but its possible for a user to pass their own via app options. This PR also isn't meant to be a final solution for #1014 or #1568, but more of an exploratory demo of one way they could done.
the majority of #1594
Checklist