-
Notifications
You must be signed in to change notification settings - Fork 371
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: Implement the version module (#1640)
## 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 - [x] New and updated code has appropriate documentation - [x] New and updated code has new and/or updated testing - [x] Required CI checks are passing - [x] Visual proof for any user facing features like CLI or documentation updates - [x] Linked issues closed with keywords --------- Co-authored-by: Rootul P <[email protected]> Co-authored-by: rene <[email protected]>
- Loading branch information
1 parent
8439399
commit f2a18f4
Showing
16 changed files
with
543 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package app | ||
|
||
import ( | ||
appversion "github.com/celestiaorg/celestia-app/x/version" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
) | ||
|
||
// 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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# `x/version` | ||
|
||
## Abstract | ||
|
||
The version module will increment the app version in the header per a predefined | ||
mapping of app versions to heights. This allows for application logic to be | ||
routed in the same binary, which enables single binary syncs and upgrades. | ||
|
||
It accomplishes this by wrapping the `BaseApp`'s `EndBlock` method and checking | ||
if the current height is equal to the height at which the app version should be | ||
incremented. If it is, then the app version is incremented and the new version | ||
is set in the header. | ||
|
||
```go | ||
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) | ||
} | ||
|
||
// EndBlocker will modify the app version if the current height is equal to | ||
// a predefined height at which the app version should be changed. | ||
func EndBlocker(ctx sdk.Context, keeper Keeper, resp abci.ResponseEndBlock) abci.ResponseEndBlock { | ||
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) | ||
} | ||
return resp | ||
} | ||
``` | ||
|
||
## State | ||
|
||
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 | ||
store, that it can still cause consensus breaking changes. This is because that | ||
state controls at which height the application will increment the app version. | ||
|
||
```go | ||
type Keeper struct { | ||
chainAppVersions map[string]ChainVersionConfig | ||
} | ||
``` | ||
|
||
## Events | ||
|
||
TODO: Add events | ||
|
||
### Usage | ||
|
||
The standard usage of the version module is to load the app version height | ||
mappings that are embedded into the binary, but it is also possible to load | ||
custom mappings by including a `map[string]ChainVersionConfig` in the | ||
app options using the `version.CustomVersionConfigKey` key. | ||
|
||
Mappings are defined by a `map[uint64]int64` where the key is the app version | ||
and the value is the height at which the app version should be incremented. For | ||
example: | ||
|
||
```go | ||
vm := map[uint64]int64{ | ||
1: 0, | ||
2: 112093, | ||
3: 300442, | ||
4: 420420, | ||
} | ||
``` | ||
|
||
The application will convert this mapping into a ChainVersionConfig: | ||
|
||
```go | ||
// HeightRange is a range of heights that a version is valid for. It is an | ||
// internal struct used to search for the correct version given a height, and | ||
// should only be created using the createRange function. Heights are | ||
// non-overlapping and inclusive, meaning that the version is valid for all | ||
// heights >= Start and <= End. | ||
type HeightRange struct { | ||
Start int64 | ||
End int64 | ||
Version uint64 | ||
} | ||
|
||
// ChainVersionConfig stores a set of version ranges and provides a method to | ||
// retrieve the correct version for a given height. | ||
type ChainVersionConfig struct { | ||
Ranges []HeightRange | ||
} | ||
``` | ||
|
||
which is used to determine the correct version for a given height. The keeper is | ||
initiated with a mapping of these configs depending on the chain-id. This allows | ||
for the chain-id to be changed and for multiple chain's (ie testnets) to be | ||
supported. | ||
|
||
```go | ||
type Keeper struct { | ||
chainAppVersions map[string]ChainVersionConfig | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package version | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
coretypes "github.com/tendermint/tendermint/proto/tendermint/types" | ||
) | ||
|
||
// EndBlocker will modify the app version if the current height is equal to | ||
// a predefined height at which the app version should be changed. | ||
func EndBlocker(ctx sdk.Context, keeper Keeper, resp abci.ResponseEndBlock) abci.ResponseEndBlock { | ||
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) | ||
} | ||
return resp | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package version | ||
|
||
const ( | ||
MochaChainID = "mocha" | ||
ArabicaChainID = "arabica-4" | ||
BlockspaceraceChainID = "blockspacerace-0" | ||
MainnetChainID = "celestia-1" | ||
) | ||
|
||
// StandardChainVersions returns a map of chain IDs to their respective | ||
// ChainVersionConfig. Each hardfork should be added to this map. | ||
func StandardChainVersions() map[string]ChainVersionConfig { | ||
version0Only := NewChainVersionConfig(map[uint64]int64{ | ||
0: 0, | ||
}) | ||
mainnetVersions := NewChainVersionConfig(map[uint64]int64{ | ||
1: 0, | ||
}) | ||
return map[string]ChainVersionConfig{ | ||
MochaChainID: version0Only, | ||
ArabicaChainID: version0Only, | ||
BlockspaceraceChainID: version0Only, | ||
MainnetChainID: mainnetVersions, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package version | ||
|
||
const ( | ||
CustomVersionConfigKey = "custom-version-config" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package version | ||
|
||
import ( | ||
"testing" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/x/upgrade/exported" | ||
"github.com/stretchr/testify/require" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
coretypes "github.com/tendermint/tendermint/proto/tendermint/types" | ||
"github.com/tendermint/tendermint/proto/tendermint/version" | ||
) | ||
|
||
func TestEndBlock(t *testing.T) { | ||
testChainID := "test" | ||
ctx := sdk.Context{}. | ||
WithBlockHeader(coretypes.Header{ | ||
ChainID: testChainID, | ||
Version: version.Consensus{App: 1}, | ||
Height: 1, | ||
}). | ||
WithChainID(testChainID) | ||
|
||
vcfg := NewChainVersionConfig( | ||
map[uint64]int64{ | ||
1: 1, | ||
2: 2, | ||
}, | ||
) | ||
vsetter := &testVersionSetter{} | ||
configs := map[string]ChainVersionConfig{ | ||
testChainID: vcfg, | ||
} | ||
keeper := NewKeeper(vsetter, configs) | ||
|
||
resp := abci.ResponseEndBlock{ConsensusParamUpdates: &abci.ConsensusParams{}} | ||
resp = EndBlocker(ctx, keeper, resp) | ||
require.Nil(t, resp.ConsensusParamUpdates.Version) | ||
|
||
ctx = ctx.WithBlockHeight(2) | ||
resp = abci.ResponseEndBlock{ConsensusParamUpdates: &abci.ConsensusParams{}} | ||
resp = EndBlocker(ctx, keeper, resp) | ||
require.NotNil(t, resp.ConsensusParamUpdates.Version) | ||
require.Equal(t, uint64(2), resp.ConsensusParamUpdates.Version.AppVersion) | ||
require.Equal(t, uint64(2), vsetter.version) | ||
|
||
ctx = ctx.WithBlockHeight(9999) | ||
resp = abci.ResponseEndBlock{ConsensusParamUpdates: &abci.ConsensusParams{}} | ||
resp = EndBlocker(ctx, keeper, resp) | ||
require.NotNil(t, resp.ConsensusParamUpdates.Version) | ||
require.Equal(t, uint64(2), resp.ConsensusParamUpdates.Version.AppVersion) | ||
require.Equal(t, uint64(2), vsetter.version) | ||
} | ||
|
||
var _ exported.ProtocolVersionSetter = &testVersionSetter{} | ||
|
||
type testVersionSetter struct { | ||
version uint64 | ||
} | ||
|
||
func (vs *testVersionSetter) SetProtocolVersion(v uint64) { | ||
vs.version = v | ||
} |
Oops, something went wrong.