diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 0c3e6d3c30..bfa68bfd7d 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -71,7 +71,14 @@ jobs: - uses: actions/setup-go@v4 with: go-version: 1.21.1 - # Generate the binaries and release + # Import the GPG key from Github secrets to sign the binaries + - name: Import GPG key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@v4 + with: + gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + # Generate the binaries, release, and sign the checksum - uses: goreleaser/goreleaser-action@v5 with: distribution: goreleaser @@ -79,3 +86,4 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..7535495a0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,76 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: ["main", "v[0-9].[0-9].x", "v[0-9].[0-9][0-9].x", "v[0-9].x"] + schedule: + - cron: '24 20 * * 4' + +jobs: + analyze: + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + runs-on: 'ubuntu-latest' + timeout-minutes: 360 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ['go'] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.goreleaser.yaml b/.goreleaser.yaml index d7944730fc..ae0c789a45 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -19,7 +19,9 @@ builds: - SDKPath={{ "github.com/cosmos/cosmos-sdk/version" }} goarch: - amd64 + - arm64 goos: + - darwin - linux tags: - ledger @@ -47,6 +49,18 @@ archives: {{- if .Arm }}v{{ .Arm }}{{ end }} checksum: name_template: "checksums.txt" +signs: + - artifacts: checksum + args: + [ + "--batch", + "-u", + "{{ .Env.GPG_FINGERPRINT }}", + "--output", + "${signature}", + "--detach-sign", + "${artifact}", + ] snapshot: name_template: "{{ incpatch .Version }}-next" changelog: diff --git a/README.md b/README.md index 828021db9c..6aa0ea0f9a 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ node | | | | ## Install +### Source + 1. [Install Go](https://go.dev/doc/install) 1.21.1 1. Clone this repo 1. Install the celestia-app CLI @@ -44,6 +46,57 @@ node | | | | make install ``` +### Pre-built binary + +If you'd rather not install from source, you can download a pre-built binary from the [releases](https://github.com/celestiaorg/celestia-app/releases) page. + +1. Navigate to the latest release on . +1. Download the binary for your platform (e.g. `celestia-app_Linux_x86_64.tar.gz`) from the **Assets** section. Tip: if you're not sure what platform you're on, you can run `uname -a` and look for the operating system (e.g. `Linux`, `Darwin`) and architecture (e.g. `x86_64`, `arm64`). +1. Extract the archive + + ```shell + tar -xvf celestia-app_Linux_x86_64.tar.gz + ``` + +1. Verify the extracted binary works + + ```shell + ./celestia-appd --help + ``` + +#### Optional: Verify the pre-built binary checksums and signatures + +If you use a pre-built binary, you may also want to verify the checksums and signatures. + +1. Navigate to the latest release on . +1. Download `checksums.txt`, `checksums.txt.sig`, and the binary for your platform (e.g. `celestia-app_Linux_x86_64.tar.gz`) from the **Assets** section. Tip: if you're not sure what platform you're on, you can run `uname -a` and look for the operating system (e.g. `Linux`, `Darwin`) and architecture (e.g. `x86_64`, `arm64`). +1. Verify the checksums + + ```shell + sha256sum --ignore-missing --check checksums.txt + ``` + + You should see output like this: + + ```shell + celestia-app_Linux_x86_64.tar.gz: OK + ``` + +1. Download the [verify-signature.sh](./scripts/signing/verify-signature.sh) script. +1. Verify the signature via the [verify-signature.sh](./scripts/signing/verify-signature.sh) script + + ```shell + ./verify-signature.sh checksums.txt.sig checksums.txt + ``` + + You should see output like this: + + ```shell + gpg: Signature made Thu Sep 21 14:39:26 2023 EDT + gpg: using EDDSA key BF02F32CC36864560B90B764D469F859693DC3FA + gpg: Good signature from "celestia-app-maintainers " [ultimate] + ``` + ### Ledger Support Ledger is not supported on Windows and OpenBSD. @@ -115,33 +168,6 @@ make proto-gen make goreleaser-build ``` -### Publishing a Release - -> **NOTE** Due to `goreleaser`'s CGO limitations, cross-compiling the binary does not work. So the binaries must be built on the target platform. This means that the release process must be done on a Linux amd64 machine. - -To generate the binaries for the Github release, you can run the following command: - -```sh -make goreleaser-release -``` - -This will generate the binaries as defined in `.goreleaser.yaml` and put them in `build/goreleaser` like so: - -```sh -build -└── goreleaser - ├── CHANGELOG.md - ├── artifacts.json - ├── celestia-app_Linux_x86_64.tar.gz - ├── celestia-app_linux_amd64_v1 - │ └── celestia-appd - ├── checksums.txt - ├── config.yaml - └── metadata.json -``` - -For the Github release, you just need to upload the `checksums.txt` and `celestia-app_Linux_x86_64.tar.gz` files. - ### Docs Package-specific READMEs aim to explain implementation details for developers that are contributing to these packages. The [specs](https://celestiaorg.github.io/celestia-app/) aim to explain the protocol as a whole for developers building on top of Celestia. @@ -153,7 +179,7 @@ Package-specific READMEs aim to explain implementation details for developers th ## Audits -[Informal Systems](https://informal.systems/) audited celestia-app [v1.0.0-rc6](https://github.com/celestiaorg/celestia-app/releases/tag/v1.0.0-rc6) in Q3 of 2023. See [audit/informal-systems.pdf](audit/informal-systems.pdf) for the full report. +[Informal Systems](https://informal.systems/) audited celestia-app [v1.0.0-rc6](https://github.com/celestiaorg/celestia-app/releases/tag/v1.0.0-rc6) in Q3 of 2023. See [docs/audit/informal-systems.pdf](docs/audit/informal-systems.pdf) for the full report. ## Careers diff --git a/app/app.go b/app/app.go index 128aabc4af..7a6ab7acdb 100644 --- a/app/app.go +++ b/app/app.go @@ -8,6 +8,7 @@ import ( "github.com/celestiaorg/celestia-app/x/mint" mintkeeper "github.com/celestiaorg/celestia-app/x/mint/keeper" minttypes "github.com/celestiaorg/celestia-app/x/mint/types" + "github.com/celestiaorg/celestia-app/x/upgrade" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" @@ -66,8 +67,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - sdkupgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" - sdkupgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/cosmos/ibc-go/v6/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v6/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" @@ -92,7 +91,6 @@ import ( blobmoduletypes "github.com/celestiaorg/celestia-app/x/blob/types" "github.com/celestiaorg/celestia-app/x/paramfilter" "github.com/celestiaorg/celestia-app/x/tokenfilter" - appupgrade "github.com/celestiaorg/celestia-app/x/upgrade" qgbmodule "github.com/celestiaorg/celestia-app/x/qgb" qgbmodulekeeper "github.com/celestiaorg/celestia-app/x/qgb/keeper" @@ -160,7 +158,7 @@ var ( // ModuleEncodingRegisters keeps track of all the module methods needed to // register interfaces and specific type to encoding config - ModuleEncodingRegisters = extractRegisters(ModuleBasics, appupgrade.TypeRegister{}) + ModuleEncodingRegisters = extractRegisters(ModuleBasics, upgrade.TypeRegister{}) // module account permissions maccPerms = map[string][]string{ @@ -219,7 +217,7 @@ type App struct { DistrKeeper distrkeeper.Keeper GovKeeper govkeeper.Keeper CrisisKeeper crisiskeeper.Keeper - UpgradeKeeper sdkupgradekeeper.Keeper + UpgradeKeeper upgrade.Keeper ParamsKeeper paramskeeper.Keeper IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly EvidenceKeeper evidencekeeper.Keeper @@ -235,6 +233,9 @@ type App struct { // the module manager mm *module.Manager + + // module configurator + configurator module.Configurator } // New returns a reference to an initialized celestia app. @@ -243,13 +244,18 @@ func New( db dbm.DB, traceStore io.Writer, loadLatest bool, - skipUpgradeHeights map[int64]bool, - homePath string, invCheckPeriod uint, encodingConfig encoding.Config, + upgradeSchedule map[string]upgrade.Schedule, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), ) *App { + for _, schedule := range upgradeSchedule { + if err := schedule.ValidateVersions(supportedVersions); err != nil { + panic(err) + } + } + appCodec := encodingConfig.Codec cdc := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry @@ -262,7 +268,7 @@ func New( keys := sdk.NewKVStoreKeys( authtypes.StoreKey, authzkeeper.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, sdkupgradetypes.StoreKey, feegrant.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, upgrade.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey, capabilitytypes.StoreKey, blobmoduletypes.StoreKey, qgbmoduletypes.StoreKey, @@ -328,7 +334,7 @@ func New( ) app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper) - app.UpgradeKeeper = sdkupgradekeeper.NewKeeper(skipUpgradeHeights, keys[sdkupgradetypes.StoreKey], appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.UpgradeKeeper = upgrade.NewKeeper(keys[upgrade.StoreKey], upgradeSchedule) app.QgbKeeper = *qgbmodulekeeper.NewKeeper( appCodec, @@ -463,6 +469,7 @@ func New( paramstypes.ModuleName, authz.ModuleName, vestingtypes.ModuleName, + upgrade.ModuleName, ) app.mm.SetOrderEndBlockers( @@ -485,6 +492,7 @@ func New( paramstypes.ModuleName, authz.ModuleName, vestingtypes.ModuleName, + upgrade.ModuleName, ) // NOTE: The genutils module must occur after staking so that pools are @@ -512,7 +520,7 @@ func New( feegrant.ModuleName, paramstypes.ModuleName, authz.ModuleName, - sdkupgradetypes.ModuleName, + upgrade.ModuleName, ) app.QueryRouter().AddRoute(proof.TxInclusionQueryPath, proof.QueryTxInclusionProof) @@ -520,8 +528,8 @@ func New( app.mm.RegisterInvariants(&app.CrisisKeeper) app.mm.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino) - configurator := module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) - app.mm.RegisterServices(configurator) + app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) + app.mm.RegisterServices(app.configurator) // initialize stores app.MountKVStores(keys) @@ -565,7 +573,17 @@ func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.R // EndBlocker application updates every end block func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - return app.mm.EndBlock(ctx, req) + res := app.mm.EndBlock(ctx, req) + if app.UpgradeKeeper.ShouldUpgrade() { + newAppVersion := app.UpgradeKeeper.GetNextAppVersion() + app.SetProtocolVersion(newAppVersion) + _, err := app.mm.RunMigrations(ctx, app.configurator, GetModuleVersion(newAppVersion)) + if err != nil { + panic(err) + } + app.UpgradeKeeper.MarkUpgradeComplete() + } + return res } // InitChainer application update at chain initialization @@ -574,7 +592,9 @@ func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.Res if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { panic(err) } - app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap()) + if req.ConsensusParams != nil && req.ConsensusParams.Version != nil { + app.SetProtocolVersion(req.ConsensusParams.Version.AppVersion) + } return app.mm.InitGenesis(ctx, app.appCodec, genesisState) } diff --git a/app/default_overrides.go b/app/default_overrides.go index dfc065e1c4..bf5343e3c8 100644 --- a/app/default_overrides.go +++ b/app/default_overrides.go @@ -160,11 +160,10 @@ func (govModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { genState := govtypes.DefaultGenesisState() day := time.Duration(time.Hour * 24) oneWeek := day * 7 - twoWeeks := oneWeek * 2 - genState.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(BondDenom, sdk.NewInt(1_000_000_000))) // 1000 TIA + genState.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(BondDenom, sdk.NewInt(10_000_000_000))) // 10,000 TIA genState.DepositParams.MaxDepositPeriod = &oneWeek - genState.VotingParams.VotingPeriod = &twoWeeks + genState.VotingParams.VotingPeriod = &oneWeek return cdc.MustMarshalJSON(genState) } @@ -188,7 +187,7 @@ func DefaultConsensusParams() *tmproto.ConsensusParams { Evidence: DefaultEvidenceParams(), Validator: coretypes.DefaultValidatorParams(), Version: tmproto.VersionParams{ - AppVersion: appconsts.LatestVersion, + AppVersion: DefaultInitialVersion, }, } } diff --git a/app/default_overrides_test.go b/app/default_overrides_test.go index ec9e155a28..f63fa8b655 100644 --- a/app/default_overrides_test.go +++ b/app/default_overrides_test.go @@ -16,7 +16,6 @@ func Test_newGovModule(t *testing.T) { encCfg := encoding.MakeConfig(ModuleEncodingRegisters...) day := time.Duration(time.Hour * 24) oneWeek := day * 7 - twoWeeks := oneWeek * 2 govModule := newGovModule() raw := govModule.DefaultGenesis(encCfg.Codec) @@ -26,10 +25,10 @@ func Test_newGovModule(t *testing.T) { want := []types.Coin{{ Denom: BondDenom, - Amount: types.NewInt(1000000000), + Amount: types.NewInt(10_000_000_000), }} assert.Equal(t, want, govGenesisState.DepositParams.MinDeposit) assert.Equal(t, oneWeek, *govGenesisState.DepositParams.MaxDepositPeriod) - assert.Equal(t, twoWeeks, *govGenesisState.VotingParams.VotingPeriod) + assert.Equal(t, oneWeek, *govGenesisState.VotingParams.VotingPeriod) } diff --git a/app/deliver_tx.go b/app/deliver_tx.go new file mode 100644 index 0000000000..1b3dbc3d6f --- /dev/null +++ b/app/deliver_tx.go @@ -0,0 +1,23 @@ +package app + +import ( + "fmt" + + "github.com/celestiaorg/celestia-app/x/upgrade" + abci "github.com/tendermint/tendermint/abci/types" +) + +func (app *App) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { + sdkTx, err := app.txConfig.TxDecoder()(req.Tx) + if err == nil { + if appVersion, ok := upgrade.IsUpgradeMsg(sdkTx.GetMsgs()); ok { + if !IsSupported(appVersion) { + panic(fmt.Sprintf("network has upgraded to version %d which is not supported by this node. Please upgrade and restart", appVersion)) + } + app.UpgradeKeeper.PrepareUpgradeAtEndBlock(appVersion) + // TODO: we may want to emit an event for this + return abci.ResponseDeliverTx{Code: abci.CodeTypeOK} + } + } + return app.BaseApp.DeliverTx(req) +} diff --git a/app/prepare_proposal.go b/app/prepare_proposal.go index 2240f79371..8929c31cd5 100644 --- a/app/prepare_proposal.go +++ b/app/prepare_proposal.go @@ -7,6 +7,7 @@ import ( "github.com/celestiaorg/celestia-app/pkg/da" "github.com/celestiaorg/celestia-app/pkg/shares" "github.com/celestiaorg/celestia-app/pkg/square" + "github.com/celestiaorg/celestia-app/x/upgrade" "github.com/cosmos/cosmos-sdk/telemetry" abci "github.com/tendermint/tendermint/abci/types" core "github.com/tendermint/tendermint/proto/tendermint/types" @@ -58,6 +59,27 @@ func (app *App) PrepareProposal(req abci.RequestPrepareProposal) abci.ResponsePr txs = make([][]byte, 0) } else { txs = FilterTxs(app.Logger(), sdkCtx, handler, app.txConfig, req.BlockData.Txs) + + // TODO: this would be improved if we only attempted the upgrade in the first round of the + // height to still allow transactions to pass through without being delayed from trying + // to coordinate the upgrade height + if newVersion, ok := app.UpgradeKeeper.ShouldProposeUpgrade(req.ChainId, req.Height); ok && newVersion > app.GetBaseApp().AppVersion() { + upgradeTx, err := upgrade.NewMsgVersionChange(app.txConfig, newVersion) + if err != nil { + panic(err) + } + // the upgrade transaction must be the first transaction in the block + txs = append([][]byte{upgradeTx}, txs...) + + // because we are adding bytes, we need to check that we are not going over the limit + // if we are, we continually prune the last tx (the lowest paying blobTx). + size := sizeOf(txs) + for size > int(req.BlockDataSize) { + lastTx := txs[len(txs)-1] + txs = txs[:len(txs)-1] + size -= len(lastTx) + } + } } // build the square from the set of valid and prioritised transactions. @@ -102,3 +124,11 @@ func (app *App) PrepareProposal(req abci.RequestPrepareProposal) abci.ResponsePr }, } } + +func sizeOf(txs [][]byte) int { + size := 0 + for _, tx := range txs { + size += len(tx) + } + return size +} diff --git a/app/process_proposal.go b/app/process_proposal.go index b2e6a446b9..5089e995bc 100644 --- a/app/process_proposal.go +++ b/app/process_proposal.go @@ -10,6 +10,7 @@ import ( "github.com/celestiaorg/celestia-app/pkg/shares" "github.com/celestiaorg/celestia-app/pkg/square" blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" + "github.com/celestiaorg/celestia-app/x/upgrade" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" @@ -66,19 +67,42 @@ func (app *App) ProcessProposal(req abci.RequestProcessProposal) (resp abci.Resp // handle non-blob transactions first if !isBlobTx { - _, has := hasPFB(sdkTx.GetMsgs()) + msgs := sdkTx.GetMsgs() + + _, has := hasPFB(msgs) if has { // A non blob tx has a PFB, which is invalid logInvalidPropBlock(app.Logger(), req.Header, fmt.Sprintf("tx %d has PFB but is not a blob tx", idx)) return reject() } + if appVersion, ok := upgrade.IsUpgradeMsg(msgs); ok { + if idx != 0 { + logInvalidPropBlock(app.Logger(), req.Header, fmt.Sprintf("upgrade message %d is not the first transaction", idx)) + return reject() + } + + if !IsSupported(appVersion) { + logInvalidPropBlock(app.Logger(), req.Header, fmt.Sprintf("block proposes an unsupported app version %d", appVersion)) + return reject() + } + + // app version must always increase + if appVersion <= app.GetBaseApp().AppVersion() { + logInvalidPropBlock(app.Logger(), req.Header, fmt.Sprintf("block proposes an app version %d that is not greater than the current app version %d", appVersion, app.GetBaseApp().AppVersion())) + return reject() + } + + // we don't need to pass this message through the ante handler + continue + } + // we need to increment the sequence for every transaction so that // the signature check below is accurate. this error only gets hit // if the account in question doens't exist. sdkCtx, err = handler(sdkCtx, sdkTx, false) if err != nil { - logInvalidPropBlockError(app.Logger(), req.Header, "failure to incrememnt sequence", err) + logInvalidPropBlockError(app.Logger(), req.Header, "failure to increment sequence", err) return reject() } diff --git a/app/test/block_production_test.go b/app/test/block_production_test.go index 11674b3e95..f8bfb5435a 100644 --- a/app/test/block_production_test.go +++ b/app/test/block_production_test.go @@ -36,7 +36,7 @@ func (s *BlockProductionTestSuite) SetupSuite() { } cfg := testnode.DefaultConfig(). - WithAccounts(accounts). + WithFundedAccounts(accounts...). WithTimeoutCommit(s.timeoutCommit) cctx, _, _ := testnode.NewNetwork(t, cfg) diff --git a/app/test/integration_test.go b/app/test/integration_test.go index cc78399dda..5c00219038 100644 --- a/app/test/integration_test.go +++ b/app/test/integration_test.go @@ -60,7 +60,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.accounts[i] = tmrand.Str(20) } - cfg := testnode.DefaultConfig().WithAccounts(s.accounts) + cfg := testnode.DefaultConfig().WithFundedAccounts(s.accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) @@ -192,7 +192,9 @@ func (s *IntegrationTestSuite) TestMaxBlockSize() { require.GreaterOrEqual(t, size, uint64(appconsts.MinSquareSize)) // assert that the app version is correctly set - require.Equal(t, appconsts.LatestVersion, blockRes.Block.Header.Version.App) + // FIXME: This should return the latest version but tendermint v0.34.x doesn't copy + // over the version when converting from proto so it disappears + require.EqualValues(t, 0, blockRes.Block.Header.Version.App) sizes = append(sizes, size) ExtendBlobTest(t, blockRes.Block) @@ -249,7 +251,7 @@ func (s *IntegrationTestSuite) TestSubmitPayForBlob() { "medium random with timeout height", mustNewBlob(ns1, tmrand.Bytes(100000), appconsts.ShareVersionZero), []user.TxOption{ - user.SetTimeoutHeight(1000), + user.SetTimeoutHeight(10000), user.SetGasLimit(1_000_000_000), }, }, @@ -329,7 +331,9 @@ func (s *IntegrationTestSuite) TestShareInclusionProof() { blockRes, err := node.Block(context.Background(), &txResp.Height) require.NoError(t, err) - require.Equal(t, appconsts.LatestVersion, blockRes.Block.Header.Version.App) + // FIXME: This should return the latest version but tendermint v0.34.x doesn't copy + // over the version when converting from proto so it disappears + require.EqualValues(t, 0, blockRes.Block.Header.Version.App) _, isBlobTx := coretypes.UnmarshalBlobTx(blockRes.Block.Txs[txResp.Index]) require.True(t, isBlobTx) diff --git a/app/test/max_total_blob_size_test.go b/app/test/max_total_blob_size_test.go index 2bd26eaa6a..87764727f0 100644 --- a/app/test/max_total_blob_size_test.go +++ b/app/test/max_total_blob_size_test.go @@ -52,7 +52,7 @@ func (s *MaxTotalBlobSizeSuite) SetupSuite() { cParams.Block.MaxBytes = 10 * mebibyte cfg := testnode.DefaultConfig(). - WithAccounts(s.accounts). + WithFundedAccounts(s.accounts...). WithTendermintConfig(tmConfig). WithConsensusParams(cParams) diff --git a/app/test/prepare_proposal_context_test.go b/app/test/prepare_proposal_context_test.go index fd6e6e27b8..ff52880fa6 100644 --- a/app/test/prepare_proposal_context_test.go +++ b/app/test/prepare_proposal_context_test.go @@ -33,7 +33,7 @@ func TestTimeInPrepareProposalContext(t *testing.T) { for i := 0; i < len(accounts); i++ { accounts[i] = tmrand.Str(9) } - cfg := testnode.DefaultConfig().WithAccounts(accounts) + cfg := testnode.DefaultConfig().WithFundedAccounts(accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) ecfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) vestAccName := "vesting" diff --git a/app/test/priority_test.go b/app/test/priority_test.go index b83f58e9bc..c7cffd07ef 100644 --- a/app/test/priority_test.go +++ b/app/test/priority_test.go @@ -43,7 +43,7 @@ func (s *PriorityTestSuite) SetupSuite() { t := s.T() cfg := testnode.DefaultConfig(). - WithAccounts(testfactory.GenerateAccounts(10)). + WithFundedAccounts(testfactory.GenerateAccounts(10)...). // use a long block time to guarantee that some transactions are included in the same block WithTimeoutCommit(time.Second) @@ -54,8 +54,8 @@ func (s *PriorityTestSuite) SetupSuite() { require.NoError(t, cctx.WaitForNextBlock()) - for _, acc := range cfg.Accounts { - addr := testfactory.GetAddress(s.cctx.Keyring, acc) + for _, acc := range cfg.Genesis.Accounts() { + addr := testfactory.GetAddress(s.cctx.Keyring, acc.Name) signer, err := user.SetupSigner(s.cctx.GoContext(), s.cctx.Keyring, s.cctx.GRPCClient, addr, s.ecfg) signer.SetPollTime(time.Millisecond * 300) require.NoError(t, err) diff --git a/app/test/qgb_rpc_test.go b/app/test/qgb_rpc_test.go index f385328759..13f1816567 100644 --- a/app/test/qgb_rpc_test.go +++ b/app/test/qgb_rpc_test.go @@ -5,6 +5,9 @@ import ( "testing" "time" + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/celestiaorg/celestia-app/test/util/testnode" "github.com/celestiaorg/celestia-app/x/qgb/types" "github.com/stretchr/testify/assert" @@ -15,7 +18,8 @@ func TestQGBRPCQueries(t *testing.T) { if testing.Short() { t.Skip("skipping QGB integration test in short mode.") } - cfg := testnode.DefaultConfig() + ecfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + cfg := testnode.DefaultConfig().WithModifiers(genesis.SetDataCommitmentWindow(ecfg.Codec, 100)) cctx, _, _ := testnode.NewNetwork(t, cfg) diff --git a/app/test/square_size_test.go b/app/test/square_size_test.go index 0f3e5ae1a8..0577cc635d 100644 --- a/app/test/square_size_test.go +++ b/app/test/square_size_test.go @@ -12,6 +12,7 @@ import ( "github.com/celestiaorg/celestia-app/pkg/user" "github.com/celestiaorg/celestia-app/test/txsim" "github.com/celestiaorg/celestia-app/test/util/blobfactory" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/celestiaorg/celestia-app/test/util/testfactory" "github.com/celestiaorg/celestia-app/test/util/testnode" blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" @@ -46,7 +47,7 @@ func (s *SquareSizeIntegrationTest) SetupSuite() { t.Log("setting up square size integration test") s.ecfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) cfg := testnode.DefaultConfig(). - WithGenesisOptions(testnode.ImmediateProposals(s.ecfg.Codec)) + WithModifiers(genesis.ImmediateProposals(s.ecfg.Codec)) cctx, rpcAddr, grpcAddr := testnode.NewNetwork(t, cfg) diff --git a/app/test/std_sdk_test.go b/app/test/std_sdk_test.go index c64c740642..9add7bbdfc 100644 --- a/app/test/std_sdk_test.go +++ b/app/test/std_sdk_test.go @@ -51,14 +51,14 @@ func (s *StandardSDKIntegrationTestSuite) SetupSuite() { t := s.T() t.Log("setting up integration test suite") - accounts := make([]string, 300) + accounts := make([]string, 35) for i := 0; i < len(accounts); i++ { accounts[i] = tmrand.Str(9) } - cfg := testnode.DefaultConfig().WithAccounts(accounts) + cfg := testnode.DefaultConfig().WithFundedAccounts(accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) - s.accounts = cfg.Accounts + s.accounts = accounts s.ecfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) s.cctx = cctx } @@ -108,7 +108,7 @@ func (s *StandardSDKIntegrationTestSuite) TestStandardSDK() { { name: "delegate 1 TIA", msgFunc: func() (msgs []sdk.Msg, signer string) { - valopAddr := sdk.ValAddress(testfactory.GetAddress(s.cctx.Keyring, "validator")) + valopAddr := sdk.ValAddress(testfactory.GetAddress(s.cctx.Keyring, testnode.DefaultValidatorAccountName)) account1 := s.unusedAccount() account1Addr := testfactory.GetAddress(s.cctx.Keyring, account1) msg := stakingtypes.NewMsgDelegate(account1Addr, valopAddr, sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000))) @@ -119,10 +119,10 @@ func (s *StandardSDKIntegrationTestSuite) TestStandardSDK() { { name: "undelegate 1 TIA", msgFunc: func() (msgs []sdk.Msg, signer string) { - valAccAddr := testfactory.GetAddress(s.cctx.Keyring, "validator") + valAccAddr := testfactory.GetAddress(s.cctx.Keyring, testnode.DefaultValidatorAccountName) valopAddr := sdk.ValAddress(valAccAddr) msg := stakingtypes.NewMsgUndelegate(valAccAddr, valopAddr, sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000))) - return []sdk.Msg{msg}, "validator" + return []sdk.Msg{msg}, testnode.DefaultValidatorAccountName }, expectedCode: abci.CodeTypeOK, }, @@ -136,10 +136,10 @@ func (s *StandardSDKIntegrationTestSuite) TestStandardSDK() { msg, err := stakingtypes.NewMsgCreateValidator( valopAddr, pv.PrivKey.PubKey(), - sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)), + sdk.NewCoin(app.BondDenom, sdk.NewInt(1)), stakingtypes.NewDescription("taco tuesday", "my keybase", "www.celestia.org", "ping @celestiaorg on twitter", "fake validator"), stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(6, 0o2), sdk.NewDecWithPrec(12, 0o2), sdk.NewDecWithPrec(1, 0o2)), - sdk.NewInt(1000000), + sdk.NewInt(1), ) require.NoError(t, err) return []sdk.Msg{msg}, account diff --git a/app/version.go b/app/version.go new file mode 100644 index 0000000000..7b711abdaf --- /dev/null +++ b/app/version.go @@ -0,0 +1,94 @@ +package app + +import ( + "fmt" + + v1 "github.com/celestiaorg/celestia-app/pkg/appconsts/v1" + v2 "github.com/celestiaorg/celestia-app/pkg/appconsts/v2" + "github.com/celestiaorg/celestia-app/x/blob" + "github.com/celestiaorg/celestia-app/x/mint" + "github.com/celestiaorg/celestia-app/x/qgb" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/capability" + "github.com/cosmos/cosmos-sdk/x/crisis" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/evidence" + feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/ibc-go/v6/modules/apps/transfer" + ibc "github.com/cosmos/ibc-go/v6/modules/core" +) + +var ( + // versions that the current state machine supports + supportedVersions = []uint64{v1.Version, v2.Version} + + v1moduleVersionMap = module.VersionMap{ + "bank": bank.AppModule{}.ConsensusVersion(), + "auth": auth.AppModule{}.ConsensusVersion(), + "authz": authzmodule.AppModule{}.ConsensusVersion(), + "staking": staking.AppModule{}.ConsensusVersion(), + "mint": mint.AppModule{}.ConsensusVersion(), + "distribution": distribution.AppModule{}.ConsensusVersion(), + "slashing": slashing.AppModule{}.ConsensusVersion(), + "gov": gov.AppModule{}.ConsensusVersion(), + "params": params.AppModule{}.ConsensusVersion(), + "vesting": vesting.AppModule{}.ConsensusVersion(), + "feegrant": feegrantmodule.AppModule{}.ConsensusVersion(), + "evidence": evidence.AppModule{}.ConsensusVersion(), + "crisis": crisis.AppModule{}.ConsensusVersion(), + "genutil": genutil.AppModule{}.ConsensusVersion(), + "capability": capability.AppModule{}.ConsensusVersion(), + "blob": blob.AppModule{}.ConsensusVersion(), + "qgb": qgb.AppModule{}.ConsensusVersion(), + "ibc": ibc.AppModule{}.ConsensusVersion(), + "transfer": transfer.AppModule{}.ConsensusVersion(), + } + + // There is currently complete parity between v1 and v2 modules, but this + // will likely change + v2moduleVersionMap = v1moduleVersionMap +) + +const DefaultInitialVersion = v1.Version + +// this is used as a compile time consistency check across different module +// based maps +func init() { + for moduleName := range ModuleBasics { + for _, v := range supportedVersions { + versionMap := GetModuleVersion(v) + if _, ok := versionMap[moduleName]; !ok { + panic(fmt.Sprintf("inconsistency: module %s not found in module version map for version %d", moduleName, v)) + } + } + } +} + +func IsSupported(version uint64) bool { + for _, v := range supportedVersions { + if v == version { + return true + } + } + return false +} + +func GetModuleVersion(appVersion uint64) module.VersionMap { + switch appVersion { + case v1.Version: + return v1moduleVersionMap + case v2.Version: + return v2moduleVersionMap + default: + panic(fmt.Sprintf("unsupported app version %d", appVersion)) + } +} diff --git a/cmd/celestia-appd/cmd/root.go b/cmd/celestia-appd/cmd/root.go index 91c86dc787..4e48f24dd4 100644 --- a/cmd/celestia-appd/cmd/root.go +++ b/cmd/celestia-appd/cmd/root.go @@ -5,7 +5,6 @@ import ( "os" "path/filepath" - "github.com/celestiaorg/celestia-app/pkg/appconsts" qgbcmd "github.com/celestiaorg/celestia-app/x/qgb/client" "github.com/celestiaorg/celestia-app/app" @@ -211,11 +210,6 @@ func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts se cache = store.NewCommitKVStoreCacheManager() } - skipUpgradeHeights := make(map[int64]bool) - for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { - skipUpgradeHeights[int64(h)] = true - } - pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) if err != nil { panic(err) @@ -234,10 +228,10 @@ func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts se } return app.New( - logger, db, traceStore, true, skipUpgradeHeights, - cast.ToString(appOpts.Get(flags.FlagHome)), + logger, db, traceStore, true, cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), encoding.MakeConfig(app.ModuleEncodingRegisters...), // Ideally, we would reuse the one created by NewRootCmd. + nil, appOpts, baseapp.SetPruning(pruningOpts), baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), @@ -249,9 +243,6 @@ func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts se baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval)), cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent)))), - func(b *baseapp.BaseApp) { - b.SetProtocolVersion(appconsts.LatestVersion) - }, ) } @@ -263,13 +254,13 @@ func createAppAndExport( encCfg.Codec = codec.NewProtoCodec(encCfg.InterfaceRegistry) var capp *app.App if height != -1 { - capp = app.New(logger, db, traceStore, false, map[int64]bool{}, "", uint(1), encCfg, appOpts) + capp = app.New(logger, db, traceStore, false, uint(1), encCfg, nil, appOpts) if err := capp.LoadHeight(height); err != nil { return servertypes.ExportedApp{}, err } } else { - capp = app.New(logger, db, traceStore, true, map[int64]bool{}, "", uint(1), encCfg, appOpts) + capp = app.New(logger, db, traceStore, true, uint(1), encCfg, nil, appOpts) } return capp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) diff --git a/audit/informal-systems.pdf b/docs/audit/informal-systems.pdf similarity index 100% rename from audit/informal-systems.pdf rename to docs/audit/informal-systems.pdf diff --git a/go.mod b/go.mod index 8cc0a0fbe1..d8f68f3368 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( golang.org/x/sys v0.12.0 // indirect golang.org/x/term v0.11.0 // indirect google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/grpc v1.58.2 + google.golang.org/grpc v1.58.3 ) require ( diff --git a/go.sum b/go.sum index 50f1b03ae6..f06c35b2eb 100644 --- a/go.sum +++ b/go.sum @@ -1972,8 +1972,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= -google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/pkg/user/signer_test.go b/pkg/user/signer_test.go index 99ff7259bd..4b062d690c 100644 --- a/pkg/user/signer_test.go +++ b/pkg/user/signer_test.go @@ -34,7 +34,7 @@ type SignerTestSuite struct { func (s *SignerTestSuite) SetupSuite() { s.encCfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) - s.ctx, _, _ = testnode.NewNetwork(s.T(), testnode.DefaultConfig().WithAccounts([]string{"a"})) + s.ctx, _, _ = testnode.NewNetwork(s.T(), testnode.DefaultConfig().WithFundedAccounts("a")) _, err := s.ctx.WaitForHeight(1) s.Require().NoError(err) rec, err := s.ctx.Keyring.Key("a") diff --git a/pkg/wrapper/README.md b/pkg/wrapper/README.md index 8a147ece42..4e911aa681 100644 --- a/pkg/wrapper/README.md +++ b/pkg/wrapper/README.md @@ -76,9 +76,9 @@ One namespace ID is located in the first `NamespaceIDSize` bytes, while the othe ## References - Namespaced Merkle tree specifications: -- Celestia original data square specification: -- Celestia constants: -- Celestia reserved namespace IDs: +- Celestia original data square specification: +- Celestia constants: +- Celestia reserved namespace IDs: [nmtlink]: https://github.com/celestiaorg/nmt/blob/master/docs/spec/nmt.md [nmtwrapper-link]: https://github.com/celestiaorg/celestia-app/blob/main/pkg/wrapper/nmt_wrapper.go diff --git a/proto/celestia/blob/v1/query.proto b/proto/celestia/blob/v1/query.proto index 8c6c49e9f8..47d484b3c6 100644 --- a/proto/celestia/blob/v1/query.proto +++ b/proto/celestia/blob/v1/query.proto @@ -3,7 +3,6 @@ package celestia.blob.v1; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; -import "cosmos/base/query/v1beta1/pagination.proto"; import "celestia/blob/v1/params.proto"; option go_package = "github.com/celestiaorg/celestia-app/x/blob/types"; diff --git a/proto/celestia/qgb/v1/query.proto b/proto/celestia/qgb/v1/query.proto index f3ac2a4474..d09d5ee80f 100644 --- a/proto/celestia/qgb/v1/query.proto +++ b/proto/celestia/qgb/v1/query.proto @@ -4,7 +4,6 @@ package celestia.qgb.v1; import "celestia/qgb/v1/genesis.proto"; import "celestia/qgb/v1/types.proto"; import "google/api/annotations.proto"; -import "cosmos/base/query/v1beta1/pagination.proto"; import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; import "google/protobuf/any.proto"; diff --git a/proto/celestia/upgrade/v1/types.proto b/proto/celestia/upgrade/v1/types.proto new file mode 100644 index 0000000000..cee2591479 --- /dev/null +++ b/proto/celestia/upgrade/v1/types.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package celestia.upgrade.v1; + +option go_package = "github.com/celestiaorg/celestia-app/x/upgrade"; + +// MsgVersionChange is a message that signals an app version change +message MsgVersionChange { + // the app version this message proposes upgrading to + uint64 version = 1; +} diff --git a/scripts/signing/celestia-app-maintainers.asc b/scripts/signing/celestia-app-maintainers.asc new file mode 100644 index 0000000000..6c7142c38c --- /dev/null +++ b/scripts/signing/celestia-app-maintainers.asc @@ -0,0 +1,14 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZQyVAhYJKwYBBAHaRw8BAQdArnTc9Gu1/koOMkR7/t9HESJN8k1ee0/YBxI/ +9bk3PBW0QGNlbGVzdGlhLWFwcC1tYWludGFpbmVycyA8Y2VsZXN0aWEtYXBwLW1h +aW50YWluZXJzQGNlbGVzdGlhLm9yZz6IkwQTFgoAOxYhBL8C8yzDaGRWC5C3ZNRp ++FlpPcP6BQJlDJUCAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJENRp ++FlpPcP6sZcBAKpPSeEHPlIsKn7lAOlfV0n9kXQYnL3xxdq9/ytFB5dUAP0S//wt +EycGLLn1Wytp06o9tFyRHw+fmQBXaNFPSsc4B7g4BGUMlQISCisGAQQBl1UBBQEB +B0CpJl7Leh7INkGvlq3QclvXRb3TB6P28tDMXk2mPhgYFAMBCAeIeAQYFgoAIBYh +BL8C8yzDaGRWC5C3ZNRp+FlpPcP6BQJlDJUCAhsMAAoJENRp+FlpPcP6HQgBAMC3 +QoXupYfpmiJGGnxlCcK5iyYpZLe8EWpWq39t0vRlAP4hgvO8A4c0TNZaVkvLq62P +eLp2+KNYB2PhA91X8BL8Bg== +=311S +-----END PGP PUBLIC KEY BLOCK----- diff --git a/scripts/signing/verify-signature.sh b/scripts/signing/verify-signature.sh new file mode 100755 index 0000000000..a86c4b6ace --- /dev/null +++ b/scripts/signing/verify-signature.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# This script enables consumers to verify signatures on artifacts. + +# Check if the number of arguments is not 2 +if [[ $# -ne 2 ]]; then + echo "Error: Exactly two arguments are required." + echo "Example usage:" + echo " ./verify-signature.sh " + exit 1 +fi + +# PGP Key +# celestia-app-maintainers +# BF02F32CC36864560B90B764D469F859693DC3FA +echo "Importing the celestia-app-maintainers public key..." +gpg --keyserver keys.openpgp.org --recv-keys BF02F32CC36864560B90B764D469F859693DC3FA + +echo "Verifying the signature of "$1" with "$2"" +gpg --verify $1 $2 diff --git a/specs/src/specs/params.md b/specs/src/specs/params.md index 71fc28d0ea..9e094741ce 100644 --- a/specs/src/specs/params.md +++ b/specs/src/specs/params.md @@ -31,9 +31,9 @@ are blocked by the `x/paramfilter` module. | distribution.WithdrawAddrEnabled | true | Enables delegators to withdraw funds to a different address. | True | | distribution.BaseProposerReward | 0 | Reward in the mint demonination for proposing a block. | True | | distribution.BonusProposerReward | 0 | Extra reward in the mint denomination for proposers based on the voting power included in the commit. | True | -| gov.DepositParams.MinDeposit | 1000000000utia (1000 TIA) | Minimum deposit for a proposal to enter voting period. | True | -| gov.DepositParams.MaxDepositPeriod | 1209600000000000 (1 week) | Maximum period for token holders to deposit on a proposal in nanoseconds. | True | -| gov.VotingParams.VotingPeriod | 604800000000000 (2 weeks) | Duration of the voting period in nanoseconds. | True | +| gov.DepositParams.MinDeposit | 10_000_000_000 utia (10,000 TIA) | Minimum deposit for a proposal to enter voting period. | True | +| gov.DepositParams.MaxDepositPeriod | 604800000000000 (1 week) | Maximum period for token holders to deposit on a proposal in nanoseconds. | True | +| gov.VotingParams.VotingPeriod | 604800000000000 (1 week) | Duration of the voting period in nanoseconds. | True | | gov.TallyParams.Quorum | 0.334 (33.4%) | Minimum percentage of total stake needed to vote for a result to be considered valid. | True | | gov.TallyParams.Threshold | 0.50 (50%) | Minimum proportion of Yes votes for proposal to pass. | True | | gov.TallyParams.VetoThreshold | 0.334 (33.4%) | Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. | True | diff --git a/test/cmd/txsim/cli_test.go b/test/cmd/txsim/cli_test.go index f802c309d6..491b9c850e 100644 --- a/test/cmd/txsim/cli_test.go +++ b/test/cmd/txsim/cli_test.go @@ -9,6 +9,7 @@ import ( "github.com/celestiaorg/celestia-app/app" "github.com/celestiaorg/celestia-app/app/encoding" "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/celestiaorg/celestia-app/test/util/testfactory" "github.com/celestiaorg/celestia-app/test/util/testnode" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -65,8 +66,10 @@ func setup(t testing.TB) (keyring.Keyring, string, string) { cfg := testnode.DefaultConfig(). WithConsensusParams(cparams). - WithAccounts([]string{testfactory.TestAccName}). - WithGenesisOptions(testnode.FundAccounts(cdc, []sdk.AccAddress{testnode.TestAddress()}, sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(1e15)))) + WithFundedAccounts(testfactory.TestAccName). + WithModifiers( + genesis.FundAccounts(cdc, []sdk.AccAddress{testnode.TestAddress()}, sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(1e15))), + ) cctx, rpcAddr, grpcAddr := testnode.NewNetwork(t, cfg) diff --git a/test/e2e/setup.go b/test/e2e/setup.go index e883fc4080..709c410876 100644 --- a/test/e2e/setup.go +++ b/test/e2e/setup.go @@ -23,12 +23,12 @@ import ( "github.com/tendermint/tendermint/types" ) -type GenesisAccount struct { +type Account struct { PubKey cryptotypes.PubKey InitialTokens int64 } -func MakeGenesis(nodes []*Node, accounts []*GenesisAccount) (types.GenesisDoc, error) { +func MakeGenesis(nodes []*Node, accounts []*Account) (types.GenesisDoc, error) { encCdc := encoding.MakeConfig(app.ModuleEncodingRegisters...) appGenState := app.ModuleBasics.DefaultGenesis(encCdc.Codec) bankGenesis := bank.DefaultGenesisState() diff --git a/test/e2e/simple_test.go b/test/e2e/simple_test.go index 92788979e2..71433ad69d 100644 --- a/test/e2e/simple_test.go +++ b/test/e2e/simple_test.go @@ -35,7 +35,7 @@ func TestE2ESimple(t *testing.T) { t.Cleanup(testnet.Cleanup) require.NoError(t, testnet.CreateGenesisNodes(4, latestVersion, 10000000)) - kr, err := testnet.CreateGenesisAccount("alice", 1e12) + kr, err := testnet.CreateAccount("alice", 1e12) require.NoError(t, err) require.NoError(t, testnet.Setup()) diff --git a/test/e2e/testnet.go b/test/e2e/testnet.go index ad3a104662..3bda11279e 100644 --- a/test/e2e/testnet.go +++ b/test/e2e/testnet.go @@ -16,7 +16,7 @@ import ( type Testnet struct { seed int64 nodes []*Node - genesisAccounts []*GenesisAccount + genesisAccounts []*Account keygen *keyGenerator } @@ -29,7 +29,7 @@ func New(name string, seed int64) (*Testnet, error) { return &Testnet{ seed: seed, nodes: make([]*Node, 0), - genesisAccounts: make([]*GenesisAccount, 0), + genesisAccounts: make([]*Account, 0), keygen: newKeyGenerator(seed), }, nil } @@ -67,7 +67,7 @@ func (t *Testnet) CreateNode(version string, startHeight int64) error { return nil } -func (t *Testnet) CreateGenesisAccount(name string, tokens int64) (keyring.Keyring, error) { +func (t *Testnet) CreateAccount(name string, tokens int64) (keyring.Keyring, error) { cdc := encoding.MakeConfig(app.ModuleEncodingRegisters...).Codec kr := keyring.NewInMemory(cdc) key, _, err := kr.NewMnemonic(name, keyring.English, "", "", hd.Secp256k1) @@ -78,7 +78,7 @@ func (t *Testnet) CreateGenesisAccount(name string, tokens int64) (keyring.Keyri if err != nil { return nil, err } - t.genesisAccounts = append(t.genesisAccounts, &GenesisAccount{ + t.genesisAccounts = append(t.genesisAccounts, &Account{ PubKey: pk, InitialTokens: tokens, }) diff --git a/test/tokenfilter/setup.go b/test/tokenfilter/setup.go index c91e70cb16..454ea97e93 100644 --- a/test/tokenfilter/setup.go +++ b/test/tokenfilter/setup.go @@ -140,7 +140,7 @@ func SetupWithGenesisValSet(t testing.TB, valSet *tmtypes.ValidatorSet, genAccs encCdc := encoding.MakeConfig(app.ModuleEncodingRegisters...) genesisState := app.NewDefaultGenesisState(encCdc.Codec) app := app.New( - log.NewNopLogger(), db, nil, true, map[int64]bool{}, app.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}, + log.NewNopLogger(), db, nil, true, 5, encCdc, nil, simapp.EmptyAppOptions{}, ) // set genesis accounts diff --git a/test/txsim/run_test.go b/test/txsim/run_test.go index b4a0b06ad3..1bdec3792b 100644 --- a/test/txsim/run_test.go +++ b/test/txsim/run_test.go @@ -105,7 +105,7 @@ func TestTxSimulator(t *testing.T) { opts := txsim.DefaultOptions(). SuppressLogs(). - WithPollTime(time.Second) + WithPollTime(time.Millisecond * 100) if tc.useFeegrant { opts.UseFeeGrant() } @@ -144,7 +144,7 @@ func TestTxSimulator(t *testing.T) { func Setup(t testing.TB) (keyring.Keyring, string, string) { t.Helper() - cfg := testnode.DefaultConfig() + cfg := testnode.DefaultConfig().WithTimeoutCommit(300 * time.Millisecond).WithFundedAccounts("txsim-master") cctx, rpcAddr, grpcAddr := testnode.NewNetwork(t, cfg) diff --git a/test/util/genesis/accounts.go b/test/util/genesis/accounts.go new file mode 100644 index 0000000000..6605949443 --- /dev/null +++ b/test/util/genesis/accounts.go @@ -0,0 +1,139 @@ +package genesis + +import ( + "fmt" + mrand "math/rand" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/cosmos/cosmos-sdk/client/tx" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/tendermint/tendermint/crypto" +) + +type Account struct { + Name string + InitialTokens int64 +} + +func NewAccounts(initBal int64, names ...string) []Account { + accounts := make([]Account, len(names)) + for i, name := range names { + accounts[i] = Account{ + Name: name, + InitialTokens: initBal, + } + } + return accounts +} + +func (ga *Account) ValidateBasic() error { + if ga.Name == "" { + return fmt.Errorf("name cannot be empty") + } + if ga.InitialTokens <= 0 { + return fmt.Errorf("initial tokens must be positive") + } + return nil +} + +type Validator struct { + Account + Stake int64 + + // ConsensusKey is the key used by the validator to sign votes. + ConsensusKey crypto.PrivKey + NetworkKey crypto.PrivKey +} + +func NewDefaultValidator(name string) Validator { + r := mrand.New(mrand.NewSource(time.Now().UnixNano())) + return Validator{ + Account: Account{ + Name: name, + InitialTokens: 999_999_999_999_999_999, + }, + Stake: 99_999_999_999_999_999, // save some tokens for fees + ConsensusKey: GenerateEd25519(NewSeed(r)), + NetworkKey: GenerateEd25519(NewSeed(r)), + } +} + +// ValidateBasic performs stateless validation on the validitor +func (v *Validator) ValidateBasic() error { + if err := v.Account.ValidateBasic(); err != nil { + return err + } + if v.Stake <= 0 { + return fmt.Errorf("stake must be positive") + } + if v.ConsensusKey == nil { + return fmt.Errorf("consensus key cannot be empty") + } + if v.Stake > v.InitialTokens { + return fmt.Errorf("stake cannot be greater than initial tokens") + } + return nil +} + +// GenTx generates a genesis transaction to create a validator as configured by +// the validator struct. It assumes the validator's genesis account has already +// been added to the keyring and that the sequence for that account is 0. +func (v *Validator) GenTx(ecfg encoding.Config, kr keyring.Keyring, chainID string) (sdk.Tx, error) { + rec, err := kr.Key(v.Name) + if err != nil { + return nil, err + } + addr, err := rec.GetAddress() + if err != nil { + return nil, err + } + + commission, err := sdk.NewDecFromStr("0.5") + if err != nil { + return nil, err + } + + pk, err := cryptocodec.FromTmPubKeyInterface(v.ConsensusKey.PubKey()) + if err != nil { + return nil, fmt.Errorf("converting public key for node %s: %w", v.Name, err) + } + + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + pk, + sdk.NewCoin(app.BondDenom, sdk.NewInt(v.Stake)), + stakingtypes.NewDescription(v.Name, "", "", "", ""), + stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), + sdk.NewInt(v.Stake/2), + ) + if err != nil { + return nil, err + } + + fee := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) + txBuilder := ecfg.TxConfig.NewTxBuilder() + err = txBuilder.SetMsgs(createValMsg) + if err != nil { + return nil, err + } + txBuilder.SetFeeAmount(fee) // Arbitrary fee + txBuilder.SetGasLimit(1000000) // Need at least 100386 + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(chainID). + WithKeybase(kr). + WithTxConfig(ecfg.TxConfig) + + err = tx.Sign(txFactory, v.Name, txBuilder, true) + if err != nil { + return nil, err + } + + return txBuilder.GetTx(), nil +} diff --git a/test/util/genesis/document.go b/test/util/genesis/document.go new file mode 100644 index 0000000000..4c18840dc1 --- /dev/null +++ b/test/util/genesis/document.go @@ -0,0 +1,115 @@ +package genesis + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + coretypes "github.com/tendermint/tendermint/types" +) + +// Document will create a valid genesis doc with funded addresses. +func Document( + ecfg encoding.Config, + params *tmproto.ConsensusParams, + chainID string, + gentxs []json.RawMessage, + addrs []string, + pubkeys []cryptotypes.PubKey, + mods ...Modifier, +) (*coretypes.GenesisDoc, error) { + genutilGenState := genutiltypes.DefaultGenesisState() + genutilGenState.GenTxs = gentxs + + genBals, genAccs, err := accountsToSDKTypes(addrs, pubkeys) + if err != nil { + return nil, err + } + + accounts, err := authtypes.PackAccounts(genAccs) + if err != nil { + return nil, err + } + + authGenState := authtypes.DefaultGenesisState() + bankGenState := banktypes.DefaultGenesisState() + authGenState.Accounts = append(authGenState.Accounts, accounts...) + bankGenState.Balances = append(bankGenState.Balances, genBals...) + bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) + + // perform some basic validation of the genesis state + if err := authtypes.ValidateGenesis(*authGenState); err != nil { + return nil, err + } + if err := bankGenState.Validate(); err != nil { + return nil, err + } + if err := genutiltypes.ValidateGenesis(genutilGenState, ecfg.TxConfig.TxJSONDecoder()); err != nil { + return nil, err + } + + state := app.ModuleBasics.DefaultGenesis(ecfg.Codec) + state[authtypes.ModuleName] = ecfg.Codec.MustMarshalJSON(authGenState) + state[banktypes.ModuleName] = ecfg.Codec.MustMarshalJSON(bankGenState) + state[genutiltypes.ModuleName] = ecfg.Codec.MustMarshalJSON(genutilGenState) + + for _, modifer := range mods { + state = modifer(state) + } + + stateBz, err := json.MarshalIndent(state, "", " ") + if err != nil { + return nil, err + } + + // Create the genesis doc + genesisDoc := &coretypes.GenesisDoc{ + ChainID: chainID, + GenesisTime: time.Now(), + ConsensusParams: params, + AppState: stateBz, + } + + return genesisDoc, nil +} + +// accountsToSDKTypes converts the genesis accounts to native SDK types. +func accountsToSDKTypes(addrs []string, pubkeys []cryptotypes.PubKey) ([]banktypes.Balance, []authtypes.GenesisAccount, error) { + if len(addrs) != len(pubkeys) { + return nil, nil, fmt.Errorf("length of addresses and public keys are not equal") + } + genBals := make([]banktypes.Balance, len(addrs)) + genAccs := make([]authtypes.GenesisAccount, len(addrs)) + hasMap := make(map[string]bool) + for i, addr := range addrs { + if hasMap[addr] { + return nil, nil, fmt.Errorf("duplicate account address %s", addr) + } + hasMap[addr] = true + + pubKey := pubkeys[i] + + balances := sdk.NewCoins( + sdk.NewCoin(appconsts.BondDenom, sdk.NewInt(999_999_999_999_999_999)), + ) + + genBals[i] = banktypes.Balance{Address: addr, Coins: balances.Sort()} + + parsedAddress, err := sdk.AccAddressFromBech32(addr) + if err != nil { + return nil, nil, err + } + + genAccs[i] = authtypes.NewBaseAccount(parsedAddress, pubKey, uint64(i), 0) + } + return genBals, genAccs, nil +} diff --git a/test/util/genesis/files.go b/test/util/genesis/files.go new file mode 100644 index 0000000000..3272b46537 --- /dev/null +++ b/test/util/genesis/files.go @@ -0,0 +1,69 @@ +package genesis + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/tendermint/tendermint/config" + tmos "github.com/tendermint/tendermint/libs/os" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/privval" +) + +// InitFiles initializes the files for a new tendermint node with the provided +// genesis. It will use the validatorIndex to save the validator's consensus +// key. +func InitFiles( + dir string, + tmCfg *config.Config, + g *Genesis, + validatorIndex int, +) (string, error) { + val, has := g.Validator(validatorIndex) + if !has { + return "", fmt.Errorf("validator %d not found", validatorIndex) + } + + basePath := filepath.Join(dir, ".celestia-app") + tmCfg.SetRoot(basePath) + + // save the genesis file + configPath := filepath.Join(basePath, "config") + err := os.MkdirAll(configPath, os.ModePerm) + if err != nil { + return "", err + } + gDoc, err := g.Export() + if err != nil { + return "", err + } + err = gDoc.SaveAs(tmCfg.GenesisFile()) + if err != nil { + return "", err + } + + pvStateFile := tmCfg.PrivValidatorStateFile() + if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { + return "", err + } + pvKeyFile := tmCfg.PrivValidatorKeyFile() + if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0o777); err != nil { + return "", err + } + filePV := privval.NewFilePV(val.ConsensusKey, pvKeyFile, pvStateFile) + filePV.Save() + + nodeKeyFile := tmCfg.NodeKeyFile() + if err := tmos.EnsureDir(filepath.Dir(nodeKeyFile), 0o777); err != nil { + return "", err + } + nodeKey := &p2p.NodeKey{ + PrivKey: val.NetworkKey, + } + if err := nodeKey.SaveAs(nodeKeyFile); err != nil { + return "", err + } + + return basePath, nil +} diff --git a/test/util/genesis/genesis.go b/test/util/genesis/genesis.go new file mode 100644 index 0000000000..560d20291d --- /dev/null +++ b/test/util/genesis/genesis.go @@ -0,0 +1,212 @@ +package genesis + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + tmrand "github.com/tendermint/tendermint/libs/rand" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + coretypes "github.com/tendermint/tendermint/types" +) + +// Genesis manages the creation of the genesis state of a network. It is meant +// to be used as the first step to any test that requires a network. +type Genesis struct { + ecfg encoding.Config + // ConsensusParams are the consensus parameters of the network. + ConsensusParams *tmproto.ConsensusParams + // ChainID is the chain ID of the network. + ChainID string + // GenesisTime is the genesis time of the network. + GenesisTime time.Time + + // kr is the keyring used to generate the genesis accounts and validators. + // Transaction keys for all genesis accounts are stored in this keyring and + // are indexed by account name. Public keys and addresses can be derived + // from those keys using the existing keyring API. + kr keyring.Keyring + + // accounts are the genesis accounts that will be included in the genesis. + accounts []Account + // validators are the validators of the network. Note that each validator + // also has a genesis account. + validators []Validator + // genTxs are the genesis transactions that will be included in the genesis. + // Transactions are generated upon adding a validator to the genesis. + genTxs []sdk.Tx + genOps []Modifier +} + +// NewDefaultGenesis creates a new default genesis with no accounts or validators. +func NewDefaultGenesis() *Genesis { + ecfg := encoding.MakeConfig(app.ModuleBasics) + g := &Genesis{ + ecfg: ecfg, + ConsensusParams: DefaultConsensusParams(), + ChainID: tmrand.Str(6), + GenesisTime: time.Now(), + kr: keyring.NewInMemory(ecfg.Codec), + genOps: []Modifier{}, + } + return g +} + +func (g *Genesis) WithModifiers(ops ...Modifier) *Genesis { + g.genOps = append(g.genOps, ops...) + return g +} + +func (g *Genesis) WithConsensusParams(params *tmproto.ConsensusParams) *Genesis { + g.ConsensusParams = params + return g +} + +func (g *Genesis) WithChainID(chainID string) *Genesis { + g.ChainID = chainID + return g +} + +func (g *Genesis) WithGenesisTime(genesisTime time.Time) *Genesis { + g.GenesisTime = genesisTime + return g +} + +func (g *Genesis) WithValidators(vals ...Validator) *Genesis { + for _, val := range vals { + err := g.AddValidator(val) + if err != nil { + panic(err) + } + } + return g +} + +func (g *Genesis) WithAccounts(accs ...Account) *Genesis { + for _, acc := range accs { + err := g.AddAccount(acc) + if err != nil { + panic(err) + } + } + return g +} + +func (g *Genesis) AddAccount(acc Account) error { + _, err := g.kr.Key(acc.Name) + if err == nil { + return fmt.Errorf("account with name %s already exists", acc.Name) + } + if err := acc.ValidateBasic(); err != nil { + return err + } + _, _, err = g.kr.NewMnemonic(acc.Name, keyring.English, "", "", hd.Secp256k1) + if err != nil { + return err + } + g.accounts = append(g.accounts, acc) + return nil +} + +func (g *Genesis) AddValidator(val Validator) error { + if err := val.ValidateBasic(); err != nil { + return err + } + + // Add the validator's genesis account + if err := g.AddAccount(val.Account); err != nil { + return err + } + + // Add the validator's genesis transaction + gentx, err := val.GenTx(g.ecfg, g.kr, g.ChainID) + if err != nil { + return err + } + + // install the validator + g.genTxs = append(g.genTxs, gentx) + g.validators = append(g.validators, val) + return nil +} + +func (g *Genesis) Accounts() []Account { + return g.accounts +} + +func (g *Genesis) Export() (*coretypes.GenesisDoc, error) { + addrs := make([]string, 0, len(g.accounts)) + pubKeys := make([]cryptotypes.PubKey, 0, len(g.accounts)) + gentxs := make([]json.RawMessage, 0, len(g.genTxs)) + + for _, acc := range g.Accounts() { + rec, err := g.kr.Key(acc.Name) + if err != nil { + return nil, err + } + + addr, err := rec.GetAddress() + if err != nil { + return nil, err + } + + addrs = append(addrs, addr.String()) + + pubK, err := rec.GetPubKey() + if err != nil { + return nil, err + } + + pubKeys = append(pubKeys, pubK) + } + + for _, genTx := range g.genTxs { + bz, err := g.ecfg.TxConfig.TxJSONEncoder()(genTx) + if err != nil { + return nil, err + } + + gentxs = append(gentxs, json.RawMessage(bz)) + } + + return Document( + g.ecfg, + g.ConsensusParams, + g.ChainID, + gentxs, + addrs, + pubKeys, + g.genOps..., + ) +} + +func (g *Genesis) Keyring() keyring.Keyring { + return g.kr +} + +func (g *Genesis) Validators() []Validator { + return g.validators +} + +// Validator returns the validator at the given index. False is returned if the +// index is out of bounds. +func (g *Genesis) Validator(i int) (Validator, bool) { + if i < len(g.validators) { + return g.validators[i], true + } + return Validator{}, false +} + +func DefaultConsensusParams() *tmproto.ConsensusParams { + cparams := coretypes.DefaultConsensusParams() + cparams.Block.TimeIotaMs = 1 + cparams.Block.MaxBytes = appconsts.DefaultMaxBytes + return cparams +} diff --git a/test/util/testnode/genesis_options.go b/test/util/genesis/modifier.go similarity index 75% rename from test/util/testnode/genesis_options.go rename to test/util/genesis/modifier.go index f18894d7fb..9ffe274d11 100644 --- a/test/util/testnode/genesis_options.go +++ b/test/util/genesis/modifier.go @@ -1,4 +1,4 @@ -package testnode +package genesis import ( "encoding/json" @@ -6,6 +6,7 @@ import ( "github.com/celestiaorg/celestia-app/app" blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" + qgbtypes "github.com/celestiaorg/celestia-app/x/qgb/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -14,13 +15,13 @@ import ( v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" ) -// GenesisOption allows for arbitrary changes to be made on the genesis state +// Modifier allows for arbitrary changes to be made on the genesis state // after initial accounts have been added. It accepts the genesis state as input -// and is expected to return the modifed genesis as output. -type GenesisOption func(state map[string]json.RawMessage) map[string]json.RawMessage +// and is expected to return the modified genesis as output. +type Modifier func(state map[string]json.RawMessage) map[string]json.RawMessage // SetBlobParams will set the provided blob params as genesis state. -func SetBlobParams(codec codec.Codec, params blobtypes.Params) GenesisOption { +func SetBlobParams(codec codec.Codec, params blobtypes.Params) Modifier { return func(state map[string]json.RawMessage) map[string]json.RawMessage { blobGenState := blobtypes.DefaultGenesis() blobGenState.Params = params @@ -31,7 +32,7 @@ func SetBlobParams(codec codec.Codec, params blobtypes.Params) GenesisOption { // ImmediateProposals sets the thresholds for getting a gov proposal to very low // levels. -func ImmediateProposals(codec codec.Codec) GenesisOption { +func ImmediateProposals(codec codec.Codec) Modifier { return func(state map[string]json.RawMessage) map[string]json.RawMessage { gs := v1.DefaultGenesisState() gs.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) @@ -44,10 +45,21 @@ func ImmediateProposals(codec codec.Codec) GenesisOption { } } +// SetDataCommitmentWindow will set the provided data commitment window in the +// qgb module's genesis state. +func SetDataCommitmentWindow(codec codec.Codec, window uint64) Modifier { + return func(state map[string]json.RawMessage) map[string]json.RawMessage { + qgbGenState := qgbtypes.DefaultGenesis() + qgbGenState.Params.DataCommitmentWindow = window + state[qgbtypes.ModuleName] = codec.MustMarshalJSON(qgbGenState) + return state + } +} + // FundAccounts adds a set of accounts to the genesis and then sets their balance as provided. // This is good in the case where you have a separate keyring you want to test against and not // use the one generated by the testnet infra. -func FundAccounts(codec codec.Codec, addresses []sdk.AccAddress, balance sdk.Coin) GenesisOption { +func FundAccounts(codec codec.Codec, addresses []sdk.AccAddress, balance sdk.Coin) Modifier { return func(state map[string]json.RawMessage) map[string]json.RawMessage { // set the accounts in the genesis state var authGenState authtypes.GenesisState diff --git a/test/util/genesis/util.go b/test/util/genesis/util.go new file mode 100644 index 0000000000..42d11288d9 --- /dev/null +++ b/test/util/genesis/util.go @@ -0,0 +1,23 @@ +package genesis + +import ( + "io" + mrand "math/rand" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" +) + +func NewSeed(r *mrand.Rand) []byte { + seed := make([]byte, ed25519.SeedSize) + + _, err := io.ReadFull(r, seed) + if err != nil { + panic(err) // this shouldn't happen + } + return seed +} + +func GenerateEd25519(seed []byte) crypto.PrivKey { + return ed25519.GenPrivKeyFromSecret(seed) +} diff --git a/test/util/malicious/app.go b/test/util/malicious/app.go index ac0e510d2e..20e813748c 100644 --- a/test/util/malicious/app.go +++ b/test/util/malicious/app.go @@ -52,14 +52,12 @@ func New( db dbm.DB, traceStore io.Writer, loadLatest bool, - skipUpgradeHeights map[int64]bool, - homePath string, invCheckPeriod uint, encodingConfig encoding.Config, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), ) *App { - goodApp := app.New(logger, db, traceStore, loadLatest, skipUpgradeHeights, homePath, invCheckPeriod, encodingConfig, appOpts, baseAppOptions...) + goodApp := app.New(logger, db, traceStore, loadLatest, invCheckPeriod, encodingConfig, nil, appOpts, baseAppOptions...) badApp := &App{App: goodApp} // set the malicious prepare proposal handler if it is set in the app options diff --git a/test/util/malicious/app_test.go b/test/util/malicious/app_test.go index 40d8cf6fd0..df4bd2bad4 100644 --- a/test/util/malicious/app_test.go +++ b/test/util/malicious/app_test.go @@ -69,7 +69,7 @@ func TestMaliciousTestNode(t *testing.T) { } accounts := testfactory.RandomAccountNames(5) cfg := OutOfOrderNamespaceConfig(5). - WithAccounts(accounts) + WithFundedAccounts(accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) _, err := cctx.WaitForHeight(6) diff --git a/test/util/malicious/test_app.go b/test/util/malicious/test_app.go index c7f0b80329..c5c00ae135 100644 --- a/test/util/malicious/test_app.go +++ b/test/util/malicious/test_app.go @@ -6,7 +6,6 @@ import ( "github.com/celestiaorg/celestia-app/app" "github.com/celestiaorg/celestia-app/app/encoding" - "github.com/celestiaorg/celestia-app/pkg/appconsts" "github.com/celestiaorg/celestia-app/test/util" "github.com/celestiaorg/celestia-app/test/util/testnode" "github.com/cosmos/cosmos-sdk/baseapp" @@ -60,11 +59,6 @@ func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts se cache = store.NewCommitKVStoreCacheManager() } - skipUpgradeHeights := make(map[int64]bool) - for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { - skipUpgradeHeights[int64(h)] = true - } - pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) if err != nil { panic(err) @@ -83,8 +77,7 @@ func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts se } return New( - logger, db, traceStore, true, skipUpgradeHeights, - cast.ToString(appOpts.Get(flags.FlagHome)), + logger, db, traceStore, true, cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), encoding.MakeConfig(app.ModuleEncodingRegisters...), // Ideally, we would reuse the one created by NewRootCmd. appOpts, @@ -98,8 +91,5 @@ func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts se baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval)), cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent)))), - func(b *baseapp.BaseApp) { - b.SetProtocolVersion(appconsts.LatestVersion) - }, ) } diff --git a/test/util/network/network.go b/test/util/network/network.go index b3b0c298d2..fc130e7d23 100644 --- a/test/util/network/network.go +++ b/test/util/network/network.go @@ -8,7 +8,6 @@ import ( "github.com/celestiaorg/celestia-app/app" "github.com/celestiaorg/celestia-app/app/encoding" - "github.com/celestiaorg/celestia-app/pkg/appconsts" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -77,14 +76,11 @@ func DefaultConfig() network.Config { AccountRetriever: authtypes.AccountRetriever{}, AppConstructor: func(val network.Validator) servertypes.Application { return app.New( - val.Ctx.Logger, tmdb.NewMemDB(), nil, true, map[int64]bool{}, val.Ctx.Config.RootDir, 0, - encCfg, + val.Ctx.Logger, tmdb.NewMemDB(), nil, true, 0, + encCfg, nil, simapp.EmptyAppOptions{}, baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices), - func(b *baseapp.BaseApp) { - b.SetProtocolVersion(appconsts.LatestVersion) - }, ) }, GenesisState: app.ModuleBasics.DefaultGenesis(encCfg.Codec), diff --git a/test/util/test_app.go b/test/util/test_app.go index 35efbe9f70..f13ace9243 100644 --- a/test/util/test_app.go +++ b/test/util/test_app.go @@ -10,7 +10,6 @@ import ( "github.com/celestiaorg/celestia-app/pkg/appconsts" "github.com/celestiaorg/celestia-app/test/util/testfactory" "github.com/celestiaorg/celestia-app/test/util/testnode" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -39,10 +38,10 @@ func init() { simapp.GetSimulatorFlags() } -type emptyAppOptions struct{} +type EmptyAppOptions struct{} // Get implements AppOptions -func (ao emptyAppOptions) Get(_ string) interface{} { +func (ao EmptyAppOptions) Get(_ string) interface{} { return nil } @@ -53,21 +52,19 @@ func (ao emptyAppOptions) Get(_ string) interface{} { func SetupTestAppWithGenesisValSet(cparams *tmproto.ConsensusParams, genAccounts ...string) (*app.App, keyring.Keyring) { // var cache sdk.MultiStorePersistentCache // EmptyAppOptions is a stub implementing AppOptions - emptyOpts := emptyAppOptions{} + emptyOpts := EmptyAppOptions{} // var anteOpt = func(bapp *baseapp.BaseApp) { bapp.SetAnteHandler(nil) } db := dbm.NewMemDB() - skipUpgradeHeights := make(map[int64]bool) encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) testApp := app.New( - log.NewNopLogger(), db, nil, true, skipUpgradeHeights, - cast.ToString(emptyOpts.Get(flags.FlagHome)), + log.NewNopLogger(), db, nil, true, cast.ToUint(emptyOpts.Get(server.FlagInvCheckPeriod)), encCfg, + nil, emptyOpts, ) - testApp.GetBaseApp().SetProtocolVersion(appconsts.LatestVersion) genesisState, valSet, kr := GenesisStateWithSingleValidator(testApp, genAccounts...) @@ -85,6 +82,7 @@ func SetupTestAppWithGenesisValSet(cparams *tmproto.ConsensusParams, genAccounts }, Evidence: &cparams.Evidence, Validator: &cparams.Validator, + Version: &cparams.Version, } genesisTime := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC).UTC() @@ -113,10 +111,10 @@ func SetupTestAppWithGenesisValSet(cparams *tmproto.ConsensusParams, genAccounts return testApp, kr } -// AddGenesisAccount mimics the cli addGenesisAccount command, providing an +// AddAccount mimics the cli addAccount command, providing an // account with an allocation of to "token" and "tia" tokens in the genesis // state -func AddGenesisAccount(addr sdk.AccAddress, appState app.GenesisState, cdc codec.Codec) (map[string]json.RawMessage, error) { +func AddAccount(addr sdk.AccAddress, appState app.GenesisState, cdc codec.Codec) (map[string]json.RawMessage, error) { // create concrete account type based on input parameters var genAccount authtypes.GenesisAccount diff --git a/test/util/testnode/config.go b/test/util/testnode/config.go index b6453e804c..bb04f44ee4 100644 --- a/test/util/testnode/config.go +++ b/test/util/testnode/config.go @@ -5,6 +5,7 @@ import ( "github.com/celestiaorg/celestia-app/cmd/celestia-appd/cmd" "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/celestiaorg/celestia-app/test/util/genesis" pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" "github.com/cosmos/cosmos-sdk/server" srvconfig "github.com/cosmos/cosmos-sdk/server/config" @@ -15,33 +16,27 @@ import ( "github.com/tendermint/tendermint/types" ) +const ( + DefaultValidatorAccountName = "validator" +) + // Config is the configuration of a test node. type Config struct { - // ChainID is the chain ID of the network. - ChainID string - // GenesisTime is the genesis block time of the network. - GenesisTime time.Time + Genesis *genesis.Genesis // TmConfig is the Tendermint configuration used for the network. TmConfig *tmconfig.Config // AppConfig is the application configuration of the test node. AppConfig *srvconfig.Config - // ConsensusParams are the consensus parameters of the test node. - ConsensusParams *tmproto.ConsensusParams // AppOptions are the application options of the test node. AppOptions *KVAppOptions - // GenesisOptions are the genesis options of the test node. - GenesisOptions []GenesisOption - // Accounts are the accounts of the test node. - Accounts []string // AppCreator is used to create the application for the testnode. AppCreator srvtypes.AppCreator // SupressLogs SupressLogs bool } -// WithChainID sets the ChainID and returns the Config. -func (c *Config) WithChainID(s string) *Config { - c.ChainID = s +func (c *Config) WithGenesis(g *genesis.Genesis) *Config { + c.Genesis = g return c } @@ -57,30 +52,12 @@ func (c *Config) WithAppConfig(conf *srvconfig.Config) *Config { return c } -// WithConsensusParams sets the ConsensusParams and returns the Config. -func (c *Config) WithConsensusParams(params *tmproto.ConsensusParams) *Config { - c.ConsensusParams = params - return c -} - // WithAppOptions sets the AppOptions and returns the Config. func (c *Config) WithAppOptions(opts *KVAppOptions) *Config { c.AppOptions = opts return c } -// WithGenesisOptions sets the GenesisOptions and returns the Config. -func (c *Config) WithGenesisOptions(opts ...GenesisOption) *Config { - c.GenesisOptions = opts - return c -} - -// WithAccounts sets the Accounts and returns the Config. -func (c *Config) WithAccounts(accs []string) *Config { - c.Accounts = accs - return c -} - // WithAppCreator sets the AppCreator and returns the Config. func (c *Config) WithAppCreator(creator srvtypes.AppCreator) *Config { c.AppCreator = creator @@ -93,31 +70,60 @@ func (c *Config) WithSupressLogs(sl bool) *Config { return c } -// WithGensisTime sets the GenesisTime and returns the Config. -func (c *Config) WithGensisTime(t time.Time) *Config { - c.GenesisTime = t - return c -} - // WithTimeoutCommit sets the CommitTimeout and returns the Config. func (c *Config) WithTimeoutCommit(d time.Duration) *Config { c.TmConfig.Consensus.TimeoutCommit = d return c } +// WithFundedAccounts sets the genesis accounts and returns the Config. +func (c *Config) WithFundedAccounts(accounts ...string) *Config { + c.Genesis = c.Genesis.WithAccounts( + genesis.NewAccounts(999999999999999999, accounts...)..., + ) + return c +} + +// WithModifiers sets the genesis options and returns the Config. +func (c *Config) WithModifiers(ops ...genesis.Modifier) *Config { + c.Genesis = c.Genesis.WithModifiers(ops...) + return c +} + +// WithGenesisTime sets the genesis time and returns the Config. +func (c *Config) WithGenesisTime(t time.Time) *Config { + c.Genesis = c.Genesis.WithGenesisTime(t) + return c +} + +// WithChainID sets the chain ID and returns the Config. +func (c *Config) WithChainID(id string) *Config { + c.Genesis = c.Genesis.WithChainID(id) + return c +} + +// WithConsensusParams sets the consensus params and returns the Config. +func (c *Config) WithConsensusParams(params *tmproto.ConsensusParams) *Config { + c.Genesis = c.Genesis.WithConsensusParams(params) + return c +} + func DefaultConfig() *Config { tmcfg := DefaultTendermintConfig() tmcfg.Consensus.TimeoutCommit = 1 * time.Millisecond cfg := &Config{} return cfg. - WithGensisTime(time.Now()). - WithAccounts([]string{}). - WithChainID(tmrand.Str(6)). + WithGenesis( + genesis.NewDefaultGenesis(). + WithChainID(tmrand.Str(6)). + WithGenesisTime(time.Now()). + WithConsensusParams(DefaultParams()). + WithModifiers(). + WithValidators(genesis.NewDefaultValidator(DefaultValidatorAccountName)), + ). WithTendermintConfig(DefaultTendermintConfig()). WithAppConfig(DefaultAppConfig()). - WithConsensusParams(DefaultParams()). WithAppOptions(DefaultAppOptions()). - WithGenesisOptions(). WithAppCreator(cmd.NewAppServer). WithSupressLogs(true) } @@ -147,6 +153,7 @@ func DefaultParams() *tmproto.ConsensusParams { cparams := types.DefaultConsensusParams() cparams.Block.TimeIotaMs = 1 cparams.Block.MaxBytes = appconsts.DefaultMaxBytes + cparams.Version.AppVersion = appconsts.LatestVersion return cparams } @@ -156,8 +163,7 @@ func DefaultTendermintConfig() *tmconfig.Config { // before starting the next height. This duration influences the time // interval between blocks. A smaller TimeoutCommit value could lead to // less time between blocks (i.e. shorter block intervals). - tmCfg.Consensus.TimeoutCommit = 300 * time.Millisecond - tmCfg.Consensus.TimeoutPropose = 200 * time.Millisecond + tmCfg.Consensus.TimeoutCommit = 1 * time.Millisecond // set the mempool's MaxTxBytes to allow the testnode to accept a // transaction that fills the entire square. Any blob transaction larger diff --git a/test/util/testnode/full_node.go b/test/util/testnode/full_node.go index 18391ca17b..183d7a5f6d 100644 --- a/test/util/testnode/full_node.go +++ b/test/util/testnode/full_node.go @@ -2,33 +2,22 @@ package testnode import ( "context" - "encoding/json" "fmt" "net" "os" "path/filepath" "testing" - "time" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keyring" srvtypes "github.com/cosmos/cosmos-sdk/server/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/proxy" dbm "github.com/tendermint/tm-db" - - "github.com/celestiaorg/celestia-app/app" - "github.com/celestiaorg/celestia-app/app/encoding" - qgbtypes "github.com/celestiaorg/celestia-app/x/qgb/types" ) // NewCometNode creates a ready to use comet node that operates a single @@ -70,82 +59,6 @@ func NewCometNode(t testing.TB, baseDir string, cfg *Config) (*node.Node, srvtyp return tmNode, app, err } -// InitFiles initializes the files for a new tendermint node with the provided -// genesis state and consensus parameters. The provided keyring is used to -// create a validator key and the chainID is used to initialize the genesis -// state. The keyring is returned with the validator account added. -func InitFiles( - t testing.TB, - cparams *tmproto.ConsensusParams, - tmCfg *config.Config, - genState map[string]json.RawMessage, - kr keyring.Keyring, - chainID string, - genesisTime time.Time, -) (string, keyring.Keyring, error) { - baseDir, err := initFileStructure(t, tmCfg) - if err != nil { - return baseDir, kr, err - } - - encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) - - nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg) - if err != nil { - return baseDir, kr, err - } - - err = createValidator(kr, encCfg, pubKey, "validator", nodeID, chainID, baseDir) - if err != nil { - return baseDir, kr, err - } - - err = initGenFiles(cparams, genState, encCfg.Codec, tmCfg.GenesisFile(), chainID, genesisTime) - if err != nil { - return baseDir, kr, err - } - - return baseDir, kr, collectGenFiles(tmCfg, encCfg, pubKey, nodeID, baseDir) -} - -// DefaultGenesisState returns a default genesis state and a keyring with -// accounts that have coins. It adds a default "validator" account that is -// funded and used for the valop address of the single validator. The keyring -// accounts are based on the fundedAccounts parameter. -func DefaultGenesisState(fundedAccounts ...string) (map[string]json.RawMessage, keyring.Keyring, error) { - encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) - state := app.ModuleBasics.DefaultGenesis(encCfg.Codec) - fundedAccounts = append(fundedAccounts, "validator") - kr, bankBals, authAccs := FundKeyringAccounts(fundedAccounts...) - - // set the accounts in the genesis state - var authGenState authtypes.GenesisState - encCfg.Codec.MustUnmarshalJSON(state[authtypes.ModuleName], &authGenState) - - accounts, err := authtypes.PackAccounts(authAccs) - if err != nil { - return nil, nil, err - } - - authGenState.Accounts = append(authGenState.Accounts, accounts...) - state[authtypes.ModuleName] = encCfg.Codec.MustMarshalJSON(&authGenState) - - // set the balances in the genesis state - var bankGenState banktypes.GenesisState - encCfg.Codec.MustUnmarshalJSON(state[banktypes.ModuleName], &bankGenState) - - bankGenState.Balances = append(bankGenState.Balances, bankBals...) - state[banktypes.ModuleName] = encCfg.Codec.MustMarshalJSON(&bankGenState) - - // use the minimum data commitment window (100) - var qgbGenState qgbtypes.GenesisState - encCfg.Codec.MustUnmarshalJSON(state[qgbtypes.ModuleName], &qgbGenState) - qgbGenState.Params.DataCommitmentWindow = qgbtypes.MinimumDataCommitmentWindow - state[qgbtypes.ModuleName] = encCfg.Codec.MustMarshalJSON(&qgbGenState) - - return state, kr, nil -} - // NewNetwork starts a single valiator celestia-app network using the provided // configurations. Configured accounts will be funded and their keys can be // accessed in keyring returned client.Context. All rpc, p2p, and grpc addresses @@ -161,23 +74,14 @@ func NewNetwork(t testing.TB, cfg *Config) (cctx Context, rpcAddr, grpcAddr stri tmCfg.P2P.ListenAddress = fmt.Sprintf("tcp://127.0.0.1:%d", GetFreePort()) tmCfg.RPC.GRPCListenAddress = fmt.Sprintf("tcp://127.0.0.1:%d", GetFreePort()) - genState, kr, err := DefaultGenesisState(cfg.Accounts...) - require.NoError(t, err) - - for _, opt := range cfg.GenesisOptions { - genState = opt(genState) - } - - chainID := cfg.ChainID - genTime := cfg.GenesisTime - - baseDir, kr, err := InitFiles(t, cfg.ConsensusParams, tmCfg, genState, kr, chainID, genTime) + // initialize the genesis file and validator files for the first validator. + baseDir, err := genesis.InitFiles(t.TempDir(), tmCfg, cfg.Genesis, 0) require.NoError(t, err) tmNode, app, err := NewCometNode(t, baseDir, cfg) require.NoError(t, err) - cctx = NewContext(context.TODO(), kr, tmCfg, chainID) + cctx = NewContext(context.TODO(), cfg.Genesis.Keyring(), tmCfg, cfg.Genesis.ChainID) cctx, stopNode, err := StartNode(tmNode, cctx) require.NoError(t, err) diff --git a/test/util/testnode/full_node_test.go b/test/util/testnode/full_node_test.go index 56c3d09f47..a2e9dca5d8 100644 --- a/test/util/testnode/full_node_test.go +++ b/test/util/testnode/full_node_test.go @@ -10,6 +10,7 @@ import ( "github.com/celestiaorg/celestia-app/app/encoding" "github.com/celestiaorg/celestia-app/pkg/appconsts" appns "github.com/celestiaorg/celestia-app/pkg/namespace" + "github.com/celestiaorg/celestia-app/test/util/genesis" blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/stretchr/testify/suite" @@ -35,8 +36,8 @@ func (s *IntegrationTestSuite) SetupSuite() { } t := s.T() - accounts := make([]string, 40) - for i := 0; i < 40; i++ { + accounts := make([]string, 10) + for i := 0; i < 10; i++ { accounts[i] = tmrand.Str(10) } @@ -45,8 +46,8 @@ func (s *IntegrationTestSuite) SetupSuite() { blobGenState.Params.GovMaxSquareSize = uint64(appconsts.DefaultSquareSizeUpperBound) cfg := DefaultConfig(). - WithAccounts(accounts). - WithGenesisOptions(SetBlobParams(ecfg.Codec, blobGenState.Params)) + WithFundedAccounts(accounts...). + WithModifiers(genesis.SetBlobParams(ecfg.Codec, blobGenState.Params)) cctx, _, _ := NewNetwork(t, cfg) s.cctx = cctx diff --git a/test/util/testnode/node_init.go b/test/util/testnode/node_init.go deleted file mode 100644 index 2004a46bcb..0000000000 --- a/test/util/testnode/node_init.go +++ /dev/null @@ -1,193 +0,0 @@ -package testnode - -import ( - "encoding/json" - "fmt" - "net/url" - "os" - "path/filepath" - "testing" - "time" - - "github.com/celestiaorg/celestia-app/app" - "github.com/celestiaorg/celestia-app/app/encoding" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/tendermint/tendermint/config" - tmos "github.com/tendermint/tendermint/libs/os" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/tendermint/tendermint/types" -) - -func collectGenFiles(tmCfg *config.Config, encCfg encoding.Config, pubKey cryptotypes.PubKey, nodeID, rootDir string) error { - gentxsDir := filepath.Join(rootDir, "gentxs") - - genFile := tmCfg.GenesisFile() - genDoc, err := types.GenesisDocFromFile(genFile) - if err != nil { - return err - } - - initCfg := genutiltypes.NewInitConfig(genDoc.ChainID, gentxsDir, nodeID, pubKey) - - appState, err := genutil.GenAppStateFromConfig( - encCfg.Codec, - encCfg.TxConfig, - tmCfg, - initCfg, - *genDoc, - banktypes.GenesisBalancesIterator{}, - ) - if err != nil { - return err - } - - genDoc = &types.GenesisDoc{ - GenesisTime: genDoc.GenesisTime, - ChainID: genDoc.ChainID, - Validators: nil, - AppState: appState, - ConsensusParams: genDoc.ConsensusParams, - } - - if err := genDoc.ValidateAndComplete(); err != nil { - return err - } - - return genDoc.SaveAs(genFile) -} - -func initGenFiles( - cparams *tmproto.ConsensusParams, - state map[string]json.RawMessage, - _ codec.Codec, - file, - chainID string, - genTime time.Time, -) error { - appGenStateJSON, err := json.MarshalIndent(state, "", " ") - if err != nil { - return err - } - - genDoc := types.GenesisDoc{ - GenesisTime: genTime, - ChainID: chainID, - AppState: appGenStateJSON, - ConsensusParams: cparams, - Validators: nil, - } - - return genDoc.SaveAs(file) -} - -// createValidator creates a genesis transaction for adding a validator account. -// The transaction is stored in the `test.json` file under the 'baseDir/gentxs`. -func createValidator( - kr keyring.Keyring, - encCfg encoding.Config, - pubKey cryptotypes.PubKey, - valopAcc, - nodeID, - chainID, - baseDir string, -) error { - rec, err := kr.Key(valopAcc) - if err != nil { - return err - } - addr, err := rec.GetAddress() - if err != nil { - return err - } - p2pAddr, _, err := server.FreeTCPAddr() - if err != nil { - return err - } - p2pURL, err := url.Parse(p2pAddr) - if err != nil { - return err - } - commission, err := sdk.NewDecFromStr("0.5") - if err != nil { - return err - } - - createValMsg, err := stakingtypes.NewMsgCreateValidator( - sdk.ValAddress(addr), - pubKey, - sdk.NewCoin(app.BondDenom, sdk.NewInt(100000000)), - stakingtypes.NewDescription("test", "", "", "", ""), - stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), - sdk.OneInt(), - ) - if err != nil { - return err - } - - memo := fmt.Sprintf("%s@%s:%s", nodeID, p2pURL.Hostname(), p2pURL.Port()) - fee := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) - txBuilder := encCfg.TxConfig.NewTxBuilder() - err = txBuilder.SetMsgs(createValMsg) - if err != nil { - return err - } - txBuilder.SetFeeAmount(fee) // Arbitrary fee - txBuilder.SetGasLimit(1000000) // Need at least 100386 - txBuilder.SetMemo(memo) - - txFactory := tx.Factory{} - txFactory = txFactory. - WithChainID(chainID). - WithMemo(memo). - WithKeybase(kr). - WithTxConfig(encCfg.TxConfig) - - err = tx.Sign(txFactory, valopAcc, txBuilder, true) - if err != nil { - return err - } - - txBz, err := encCfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) - if err != nil { - return err - } - gentxsDir := filepath.Join(baseDir, "gentxs") - return writeFile(fmt.Sprintf("%v.json", "test"), gentxsDir, txBz) -} - -func writeFile(name string, dir string, contents []byte) error { - writePath := filepath.Join(dir) - file := filepath.Join(writePath, name) - - err := tmos.EnsureDir(writePath, 0o755) - if err != nil { - return err - } - - err = os.WriteFile(file, contents, 0o644) // nolint: gosec - if err != nil { - return err - } - - return nil -} - -func initFileStructure(t testing.TB, tmCfg *config.Config) (string, error) { - basePath := filepath.Join(t.TempDir(), ".celestia-app") - tmCfg.SetRoot(basePath) - configPath := filepath.Join(basePath, "config") - err := os.MkdirAll(configPath, os.ModePerm) - if err != nil { - return "", err - } - return basePath, nil -} diff --git a/x/blob/types/query.pb.go b/x/blob/types/query.pb.go index 3e96f05c2e..c28b5b01ed 100644 --- a/x/blob/types/query.pb.go +++ b/x/blob/types/query.pb.go @@ -6,7 +6,6 @@ package types import ( context "context" fmt "fmt" - _ "github.com/cosmos/cosmos-sdk/types/query" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" @@ -120,26 +119,25 @@ func init() { func init() { proto.RegisterFile("celestia/blob/v1/query.proto", fileDescriptor_29ba8a4248383b64) } var fileDescriptor_29ba8a4248383b64 = []byte{ - // 304 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0x41, 0x4b, 0xc3, 0x30, - 0x18, 0x86, 0x5b, 0xd1, 0x1d, 0xe2, 0x41, 0x8d, 0x03, 0xc7, 0x98, 0x51, 0x86, 0x82, 0x08, 0x26, - 0x76, 0x82, 0x3f, 0x60, 0x47, 0x41, 0xd0, 0x1d, 0xbd, 0x25, 0x23, 0xc4, 0xc2, 0x96, 0x2f, 0x6b, - 0xd2, 0xea, 0xae, 0xfe, 0x02, 0xc1, 0x3f, 0xb5, 0xe3, 0xc0, 0x8b, 0x27, 0x91, 0xd6, 0x1f, 0x22, - 0x6d, 0x3a, 0xc1, 0xf5, 0xe0, 0xed, 0xe3, 0x7b, 0x9f, 0xf7, 0xcd, 0xfb, 0x05, 0xf5, 0xc6, 0x72, - 0x22, 0xad, 0x8b, 0x39, 0x13, 0x13, 0x10, 0x2c, 0x8b, 0xd8, 0x2c, 0x95, 0xc9, 0x9c, 0x9a, 0x04, - 0x1c, 0xe0, 0xdd, 0x95, 0x4a, 0x4b, 0x95, 0x66, 0x51, 0xb7, 0xad, 0x40, 0x41, 0x25, 0xb2, 0x72, - 0xf2, 0x5c, 0xb7, 0xa7, 0x00, 0xd4, 0x44, 0x32, 0x6e, 0x62, 0xc6, 0xb5, 0x06, 0xc7, 0x5d, 0x0c, - 0xda, 0xd6, 0xea, 0xf9, 0x18, 0xec, 0x14, 0x2c, 0x13, 0xdc, 0x4a, 0x1f, 0xcf, 0xb2, 0x48, 0x48, - 0xc7, 0x23, 0x66, 0xb8, 0x8a, 0x75, 0x05, 0xd7, 0xec, 0x61, 0xa3, 0x8f, 0xe1, 0x09, 0x9f, 0xd6, - 0x51, 0xfd, 0x36, 0xc2, 0xf7, 0x65, 0xc0, 0x5d, 0xb5, 0x1c, 0xc9, 0x59, 0x2a, 0xad, 0xeb, 0xdf, - 0xa2, 0xfd, 0x3f, 0x5b, 0x6b, 0x40, 0x5b, 0x89, 0xaf, 0x51, 0xcb, 0x9b, 0x3b, 0xe1, 0x71, 0x78, - 0xb6, 0x3d, 0xe8, 0xd0, 0xf5, 0x73, 0xa8, 0x77, 0x0c, 0x37, 0x17, 0x9f, 0x47, 0xc1, 0xa8, 0xa6, - 0x07, 0x4f, 0x68, 0xab, 0x8a, 0xc3, 0x1a, 0xb5, 0x3c, 0x80, 0x4f, 0x9a, 0xd6, 0x66, 0x8f, 0xee, - 0xe9, 0x3f, 0x94, 0xef, 0xd5, 0x3f, 0x78, 0x79, 0xff, 0x7e, 0xdb, 0xd8, 0xc3, 0x3b, 0x6b, 0x37, - 0x0e, 0x6f, 0x16, 0x39, 0x09, 0x97, 0x39, 0x09, 0xbf, 0x72, 0x12, 0xbe, 0x16, 0x24, 0x58, 0x16, - 0x24, 0xf8, 0x28, 0x48, 0xf0, 0x70, 0xa9, 0x62, 0xf7, 0x98, 0x0a, 0x3a, 0x86, 0x29, 0x5b, 0xbd, - 0x01, 0x89, 0xfa, 0x9d, 0x2f, 0xb8, 0x31, 0xec, 0xd9, 0xe7, 0xb9, 0xb9, 0x91, 0x56, 0xb4, 0xaa, - 0x0f, 0xbb, 0xfa, 0x09, 0x00, 0x00, 0xff, 0xff, 0x39, 0x82, 0x2f, 0xc4, 0xe1, 0x01, 0x00, 0x00, + // 275 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x49, 0x4e, 0xcd, 0x49, + 0x2d, 0x2e, 0xc9, 0x4c, 0xd4, 0x4f, 0xca, 0xc9, 0x4f, 0xd2, 0x2f, 0x33, 0xd4, 0x2f, 0x2c, 0x4d, + 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0xc9, 0xea, 0x81, 0x64, 0xf5, + 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x94, + 0x4c, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x7e, 0x62, 0x41, 0xa6, 0x7e, 0x62, 0x5e, 0x5e, 0x7e, + 0x49, 0x62, 0x49, 0x66, 0x7e, 0x5e, 0x31, 0x54, 0x56, 0x16, 0xc3, 0x8e, 0x82, 0xc4, 0xa2, 0xc4, + 0x5c, 0xa8, 0xb4, 0x92, 0x08, 0x97, 0x50, 0x20, 0xc8, 0xce, 0x00, 0xb0, 0x60, 0x50, 0x6a, 0x61, + 0x69, 0x6a, 0x71, 0x89, 0x92, 0x2f, 0x97, 0x30, 0x8a, 0x68, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0xaa, + 0x90, 0x19, 0x17, 0x1b, 0x44, 0xb3, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xb7, 0x91, 0x84, 0x1e, 0xba, + 0x13, 0xf5, 0x20, 0x3a, 0x9c, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0xaa, 0x36, 0x2a, 0xe7, + 0x62, 0x05, 0x1b, 0x27, 0x94, 0xc7, 0xc5, 0x06, 0x51, 0x20, 0xa4, 0x82, 0xa9, 0x15, 0xd3, 0x1d, + 0x52, 0xaa, 0x04, 0x54, 0x41, 0xdc, 0xa5, 0x24, 0xde, 0x74, 0xf9, 0xc9, 0x64, 0x26, 0x41, 0x21, + 0x7e, 0x34, 0x3f, 0x3a, 0x79, 0x9d, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, + 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, + 0x41, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0xcc, 0x8e, 0xfc, 0xa2, + 0x74, 0x38, 0x5b, 0x37, 0xb1, 0xa0, 0x40, 0xbf, 0x02, 0x62, 0x5e, 0x49, 0x65, 0x41, 0x6a, 0x71, + 0x12, 0x1b, 0x38, 0xc0, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb0, 0x7e, 0x20, 0x5f, 0xb5, + 0x01, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/qgb/handler.go b/x/qgb/handler.go deleted file mode 100644 index 3ea398b4fd..0000000000 --- a/x/qgb/handler.go +++ /dev/null @@ -1,24 +0,0 @@ -package qgb - -import ( - "fmt" - - "cosmossdk.io/errors" - "github.com/celestiaorg/celestia-app/x/qgb/keeper" - "github.com/celestiaorg/celestia-app/x/qgb/types" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// Can be deleted after implementing the Orchestrator and Relayer as per QGB -// ADR-005. NewHandler uses the provided qgb keeper to create an sdk.Handler. -func NewHandler(_ keeper.Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - _ = ctx.WithEventManager(sdk.NewEventManager()) - switch msg := msg.(type) { - default: - errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg) - return nil, errors.Wrap(sdkerrors.ErrUnknownRequest, errMsg) - } - } -} diff --git a/x/qgb/integration_test.go b/x/qgb/integration_test.go index 6beb7f1739..834d1cc713 100644 --- a/x/qgb/integration_test.go +++ b/x/qgb/integration_test.go @@ -39,7 +39,7 @@ func (s *QGBIntegrationSuite) SetupSuite() { s.accounts = []string{"jimmy"} - cfg := testnode.DefaultConfig().WithAccounts(s.accounts) + cfg := testnode.DefaultConfig().WithFundedAccounts(s.accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) s.ecfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) s.cctx = cctx diff --git a/x/qgb/keeper/keeper_attestation.go b/x/qgb/keeper/keeper_attestation.go index 51c3f1b21d..f48f1d1470 100644 --- a/x/qgb/keeper/keeper_attestation.go +++ b/x/qgb/keeper/keeper_attestation.go @@ -53,14 +53,14 @@ func (k Keeper) SetLatestAttestationNonce(ctx sdk.Context, nonce uint64) { } store := ctx.KVStore(k.storeKey) - store.Set([]byte(types.LatestAttestationtNonce), types.UInt64Bytes(nonce)) + store.Set([]byte(types.LatestAttestationNonce), types.UInt64Bytes(nonce)) } // CheckLatestAttestationNonce returns true if the latest attestation request // nonce is declared in the store and false if it has not been initialized. func (k Keeper) CheckLatestAttestationNonce(ctx sdk.Context) bool { store := ctx.KVStore(k.storeKey) - has := store.Has([]byte(types.LatestAttestationtNonce)) + has := store.Has([]byte(types.LatestAttestationNonce)) return has } @@ -71,7 +71,7 @@ func (k Keeper) CheckLatestAttestationNonce(ctx sdk.Context) bool { // method. func (k Keeper) GetLatestAttestationNonce(ctx sdk.Context) uint64 { store := ctx.KVStore(k.storeKey) - bytes := store.Get([]byte(types.LatestAttestationtNonce)) + bytes := store.Get([]byte(types.LatestAttestationNonce)) if bytes == nil { panic("nil LatestAttestationNonce") } diff --git a/x/qgb/module.go b/x/qgb/module.go index 877d12af9b..8d2035d13e 100644 --- a/x/qgb/module.go +++ b/x/qgb/module.go @@ -114,7 +114,7 @@ func (am AppModule) Name() string { // Route returns the capability module's message routing key. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) + return sdk.Route{} } // QuerierRoute returns the capability module's query routing key. diff --git a/x/qgb/types/keys.go b/x/qgb/types/keys.go index 5a2c83b828..f82ba1e937 100644 --- a/x/qgb/types/keys.go +++ b/x/qgb/types/keys.go @@ -31,8 +31,8 @@ const ( // height LatestUnBondingBlockHeight = "LatestUnBondingBlockHeight" - // LatestAttestationtNonce indexes the latest attestation request nonce - LatestAttestationtNonce = "LatestAttestationNonce" + // LatestAttestationNonce indexes the latest attestation request nonce + LatestAttestationNonce = "LatestAttestationNonce" // EarliestAvailableAttestationNonce indexes the earliest available // attestation nonce diff --git a/x/qgb/types/query.pb.go b/x/qgb/types/query.pb.go index c5e8f7c4c0..ed7df634a6 100644 --- a/x/qgb/types/query.pb.go +++ b/x/qgb/types/query.pb.go @@ -8,7 +8,6 @@ import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" types "github.com/cosmos/cosmos-sdk/codec/types" - _ "github.com/cosmos/cosmos-sdk/types/query" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" @@ -763,62 +762,60 @@ func init() { func init() { proto.RegisterFile("celestia/qgb/v1/query.proto", fileDescriptor_c8535c57355a2b91) } var fileDescriptor_c8535c57355a2b91 = []byte{ - // 873 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xcd, 0x6f, 0xe3, 0x44, - 0x18, 0xc6, 0x63, 0xb4, 0x1b, 0x89, 0xb7, 0xd2, 0x7e, 0x4c, 0xb2, 0x29, 0xf5, 0x2e, 0x6e, 0xd7, - 0xe9, 0x76, 0xbb, 0xbb, 0xd4, 0xa3, 0xa4, 0x7c, 0x88, 0xdd, 0xe5, 0xd0, 0x40, 0x51, 0x2b, 0x95, - 0xaf, 0x48, 0xf4, 0xc0, 0x81, 0x6a, 0x9c, 0x4c, 0x5d, 0x8b, 0xd8, 0xe3, 0xd8, 0x4e, 0x44, 0x04, - 0x5c, 0xf8, 0x0b, 0x90, 0x38, 0x72, 0xe6, 0xca, 0x89, 0x13, 0x1c, 0xb9, 0x54, 0x3d, 0x55, 0xe2, - 0xc2, 0x09, 0xa1, 0x96, 0x3f, 0x04, 0x65, 0x3e, 0x52, 0x27, 0x76, 0x9c, 0xa4, 0xe2, 0xe6, 0x99, - 0x79, 0xde, 0xe7, 0xfd, 0xbd, 0xd3, 0xfa, 0x89, 0xe1, 0x7e, 0x8b, 0x76, 0x68, 0x14, 0xbb, 0x04, - 0x77, 0x1d, 0x1b, 0xf7, 0x6b, 0xb8, 0xdb, 0xa3, 0xe1, 0xc0, 0x0a, 0x42, 0x16, 0x33, 0x74, 0x5b, - 0x1d, 0x5a, 0x5d, 0xc7, 0xb6, 0xfa, 0x35, 0xfd, 0xf5, 0x49, 0xb5, 0x43, 0x7d, 0x1a, 0xb9, 0x91, - 0xd0, 0xeb, 0x29, 0xb3, 0x78, 0x10, 0x50, 0x75, 0xf8, 0xc0, 0x61, 0xcc, 0xe9, 0x50, 0x4c, 0x02, - 0x17, 0x13, 0xdf, 0x67, 0x31, 0x89, 0x5d, 0xe6, 0xab, 0xd3, 0xa7, 0x2d, 0x16, 0x79, 0x2c, 0xc2, - 0x36, 0x89, 0xa8, 0x60, 0xc0, 0xfd, 0x9a, 0x4d, 0x63, 0x52, 0xc3, 0x01, 0x71, 0x5c, 0x9f, 0x8b, - 0xa5, 0xb6, 0xec, 0x30, 0x87, 0xf1, 0x47, 0x3c, 0x7c, 0x92, 0xbb, 0x2b, 0xc2, 0xe1, 0x48, 0x1c, - 0x88, 0x85, 0x3a, 0x92, 0xad, 0xf9, 0xca, 0xee, 0x1d, 0x63, 0xe2, 0xcb, 0x11, 0xcd, 0x32, 0xa0, - 0xcf, 0x86, 0xdd, 0x3e, 0x25, 0x21, 0xf1, 0xa2, 0x26, 0xed, 0xf6, 0x68, 0x14, 0x9b, 0x07, 0x50, - 0x1a, 0xdb, 0x8d, 0x02, 0xe6, 0x47, 0x14, 0xbd, 0x05, 0xc5, 0x80, 0xef, 0xbc, 0xa6, 0xad, 0x69, - 0x9b, 0x4b, 0xf5, 0x65, 0x6b, 0xe2, 0x82, 0x2c, 0x51, 0xd0, 0xb8, 0x71, 0xfa, 0xf7, 0x6a, 0xa1, - 0x29, 0xc5, 0xe6, 0x7b, 0xf0, 0x88, 0xbb, 0xed, 0xc4, 0x31, 0x8d, 0xc4, 0xd8, 0xb2, 0x51, 0x63, - 0xf0, 0x31, 0xf3, 0x5b, 0x54, 0xae, 0x50, 0x19, 0x6e, 0xfa, 0xc3, 0x35, 0xb7, 0xbf, 0xd1, 0x14, - 0x0b, 0x73, 0x00, 0x1b, 0xb3, 0xca, 0x25, 0xdf, 0x27, 0xb0, 0x44, 0xae, 0x44, 0x12, 0xb2, 0x6c, - 0x89, 0xe9, 0x2d, 0x35, 0xbd, 0xb5, 0xe3, 0x0f, 0x1a, 0xcb, 0x67, 0xbf, 0x6e, 0x95, 0xd2, 0x8e, - 0xfb, 0xcd, 0xa4, 0x83, 0xb9, 0x0e, 0x26, 0x6f, 0x7d, 0x40, 0x86, 0x7b, 0x09, 0x79, 0x12, 0xdb, - 0x7c, 0x01, 0xd5, 0x5c, 0x95, 0xa4, 0xcb, 0x9e, 0xae, 0x01, 0x4f, 0x13, 0xc5, 0x87, 0xa4, 0x13, - 0xd1, 0x58, 0x8d, 0x47, 0x8f, 0x59, 0x48, 0xe7, 0xb8, 0xa1, 0x2f, 0xe1, 0xd9, 0x5c, 0x1e, 0x12, - 0x04, 0x43, 0xb1, 0xcf, 0x35, 0x53, 0xff, 0x8c, 0xd2, 0x42, 0xca, 0xcc, 0x2a, 0x3c, 0x4c, 0xf8, - 0x7f, 0xee, 0xdb, 0xcc, 0x6f, 0xbb, 0xbe, 0xb3, 0x47, 0x5d, 0xe7, 0x44, 0x35, 0x32, 0x5f, 0x8e, - 0xdd, 0x55, 0x4a, 0x24, 0x7b, 0x57, 0xa0, 0x78, 0xc2, 0x77, 0xe4, 0x04, 0x72, 0x65, 0x9a, 0xb0, - 0x96, 0xa8, 0xfe, 0x80, 0xc4, 0xe4, 0x7d, 0xe6, 0x79, 0x6e, 0xec, 0x51, 0x7f, 0xd4, 0xc1, 0x1b, - 0xc3, 0x98, 0xd4, 0xc8, 0x06, 0x7b, 0x70, 0xbb, 0x4d, 0x62, 0x72, 0xd4, 0x1a, 0x1d, 0xc9, 0x29, - 0x57, 0x53, 0x53, 0x4e, 0x38, 0xdc, 0x6a, 0x8f, 0xad, 0xcd, 0x06, 0x6c, 0xf2, 0x76, 0x13, 0x32, - 0xe2, 0x3b, 0xf4, 0x43, 0x16, 0x8e, 0x0d, 0x3f, 0x75, 0xac, 0x1e, 0x3c, 0x99, 0xc3, 0xe3, 0x7f, - 0x47, 0xdf, 0x85, 0x0a, 0x6f, 0xbb, 0x7b, 0xf8, 0xd1, 0x4e, 0xbb, 0x1d, 0xd2, 0x48, 0xbd, 0xd9, - 0xe8, 0x19, 0xdc, 0xed, 0x93, 0x8e, 0xdb, 0x26, 0x31, 0x0b, 0x8f, 0x88, 0x38, 0xe3, 0x5d, 0x5e, - 0x6d, 0xde, 0x19, 0x1d, 0xc8, 0x1a, 0xf3, 0x39, 0x2c, 0xa7, 0x6c, 0x24, 0xeb, 0x2a, 0x2c, 0xd1, - 0xbe, 0x37, 0xe1, 0x00, 0xb4, 0xef, 0x49, 0x61, 0xfd, 0x37, 0x80, 0x9b, 0xbc, 0x18, 0x7d, 0x05, - 0x45, 0x11, 0x0b, 0xa8, 0x9a, 0x9a, 0x23, 0x9d, 0x3d, 0xfa, 0x7a, 0xbe, 0x48, 0xf4, 0x37, 0x2b, - 0xdf, 0xff, 0xf9, 0xef, 0x8f, 0xaf, 0xdc, 0x41, 0xb7, 0x54, 0xd4, 0x8a, 0xac, 0x41, 0xbf, 0x6b, - 0xb0, 0x32, 0x35, 0x28, 0xd0, 0xdb, 0xd9, 0xde, 0xb3, 0x82, 0x49, 0x7f, 0x67, 0xe1, 0x3a, 0x89, - 0xb9, 0xc5, 0x31, 0x1f, 0xa3, 0x47, 0x0a, 0x33, 0x91, 0x2e, 0x11, 0x0e, 0x45, 0x51, 0x84, 0xbf, - 0xe1, 0xef, 0xf1, 0x77, 0xe8, 0x17, 0x0d, 0x2a, 0xd9, 0x29, 0x82, 0xb6, 0xb3, 0x11, 0x72, 0x93, - 0x49, 0x7f, 0x73, 0xb1, 0x22, 0x09, 0xfd, 0x84, 0x43, 0x57, 0xd1, 0xc3, 0x4c, 0x68, 0x8e, 0x8a, - 0x3b, 0xdc, 0x02, 0x9d, 0x69, 0x60, 0xe4, 0xa7, 0x0e, 0x7a, 0x91, 0xc7, 0x30, 0x23, 0xef, 0xf4, - 0x97, 0xd7, 0x2b, 0x9e, 0x76, 0xfb, 0x22, 0xcf, 0xd4, 0xbd, 0x63, 0x9b, 0xd7, 0x8c, 0x6e, 0xff, - 0x27, 0x0d, 0xee, 0x65, 0xa6, 0x17, 0xaa, 0xe7, 0x61, 0x64, 0xe7, 0xa1, 0xbe, 0xbd, 0x50, 0x8d, - 0x24, 0x5e, 0xe1, 0xc4, 0x25, 0x74, 0x57, 0x11, 0xf7, 0x94, 0x10, 0xfd, 0xa1, 0xc1, 0x83, 0xbc, - 0x18, 0x41, 0xef, 0x66, 0x37, 0x9c, 0x23, 0xbe, 0xf4, 0xe7, 0xd7, 0x29, 0x95, 0xc8, 0x6f, 0x70, - 0xe4, 0x0d, 0xb4, 0xae, 0x90, 0x27, 0x32, 0x0c, 0x87, 0xc3, 0x3a, 0x2c, 0x02, 0x11, 0xfd, 0xac, - 0x41, 0x39, 0x2b, 0xbf, 0x51, 0x2d, 0xef, 0xba, 0x32, 0x7f, 0x0f, 0xf4, 0xfa, 0x22, 0x25, 0x92, - 0x76, 0x83, 0xd3, 0xae, 0x21, 0x63, 0x1a, 0xad, 0xfc, 0xc7, 0xfe, 0x16, 0xe0, 0x2a, 0xf5, 0xd0, - 0xe3, 0xec, 0x4e, 0xa9, 0x78, 0xd5, 0x37, 0x67, 0x0b, 0x25, 0xc8, 0x7d, 0x0e, 0x72, 0x0f, 0x95, - 0x14, 0x48, 0x22, 0x4e, 0x1b, 0xfb, 0xa7, 0x17, 0x86, 0x76, 0x7e, 0x61, 0x68, 0xff, 0x5c, 0x18, - 0xda, 0x0f, 0x97, 0x46, 0xe1, 0xfc, 0xd2, 0x28, 0xfc, 0x75, 0x69, 0x14, 0xbe, 0xc0, 0x8e, 0x1b, - 0x9f, 0xf4, 0x6c, 0xab, 0xc5, 0x3c, 0xac, 0x5a, 0xb1, 0xd0, 0x19, 0x3d, 0x6f, 0x91, 0x20, 0xc0, - 0x5f, 0x73, 0x4f, 0xfe, 0xf1, 0x69, 0x17, 0xf9, 0x67, 0xcf, 0xf6, 0x7f, 0x01, 0x00, 0x00, 0xff, - 0xff, 0x05, 0xfd, 0x5a, 0x43, 0xe9, 0x0a, 0x00, 0x00, + // 848 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xcb, 0x4f, 0xeb, 0x46, + 0x14, 0xc6, 0xe3, 0x0a, 0x22, 0xf5, 0x20, 0xf1, 0x98, 0x84, 0x50, 0x0c, 0x35, 0xe0, 0xf0, 0x2c, + 0xc5, 0x23, 0x42, 0x1f, 0x2a, 0xd0, 0x05, 0x69, 0xa9, 0x40, 0xa2, 0xaf, 0x48, 0x65, 0xd1, 0x45, + 0xd1, 0x24, 0x19, 0x8c, 0xd5, 0xd8, 0x93, 0xd8, 0x4e, 0xd4, 0xa8, 0xed, 0xa6, 0x7f, 0x41, 0xa5, + 0x2e, 0xbb, 0xee, 0xb6, 0xab, 0xae, 0xda, 0x65, 0x37, 0x88, 0x15, 0x52, 0x37, 0x5d, 0x55, 0x15, + 0xdc, 0x3f, 0xe4, 0x2a, 0xe3, 0x99, 0xe0, 0xc4, 0x8e, 0x93, 0xa0, 0xbb, 0xf3, 0xcc, 0x9c, 0xef, + 0x3b, 0xbf, 0x33, 0xe0, 0x2f, 0x86, 0xa5, 0x0a, 0xad, 0x51, 0xcf, 0xb7, 0x08, 0x6e, 0x98, 0x65, + 0xdc, 0xda, 0xc7, 0x8d, 0x26, 0x75, 0xdb, 0x46, 0xdd, 0x65, 0x3e, 0x43, 0x33, 0xf2, 0xd0, 0x68, + 0x98, 0x65, 0xa3, 0xb5, 0xaf, 0xbe, 0xd9, 0x5f, 0x6d, 0x52, 0x87, 0x7a, 0x96, 0x17, 0xd4, 0xab, + 0x11, 0x33, 0xbf, 0x5d, 0xa7, 0xf2, 0x70, 0xd9, 0x64, 0xcc, 0xac, 0x51, 0x4c, 0xea, 0x16, 0x26, + 0x8e, 0xc3, 0x7c, 0xe2, 0x5b, 0xcc, 0x91, 0xa7, 0x59, 0x93, 0x99, 0x8c, 0x3f, 0xe2, 0xce, 0x93, + 0xd8, 0x5d, 0xac, 0x30, 0xcf, 0x66, 0xde, 0x55, 0x70, 0x10, 0x2c, 0xe4, 0x91, 0xb0, 0xe3, 0xab, + 0x72, 0xf3, 0x1a, 0x13, 0x47, 0x60, 0xeb, 0x59, 0x40, 0x5f, 0x76, 0xa6, 0xf8, 0x82, 0xb8, 0xc4, + 0xf6, 0x4a, 0xb4, 0xd1, 0xa4, 0x9e, 0xaf, 0x5f, 0x40, 0xa6, 0x67, 0xd7, 0xab, 0x33, 0xc7, 0xa3, + 0xe8, 0x5d, 0x48, 0xd7, 0xf9, 0xce, 0x1b, 0xca, 0xaa, 0xb2, 0x3d, 0x55, 0x58, 0x30, 0xfa, 0x86, + 0x36, 0x02, 0x41, 0x71, 0xe2, 0xf6, 0xbf, 0x95, 0x54, 0x49, 0x14, 0xeb, 0x1f, 0xc2, 0x06, 0x77, + 0x3b, 0xf1, 0x7d, 0xea, 0x05, 0xa3, 0x88, 0x46, 0xc5, 0xf6, 0x67, 0xcc, 0xa9, 0x50, 0xb1, 0x42, + 0x59, 0x98, 0x74, 0x3a, 0x6b, 0x6e, 0x3f, 0x51, 0x0a, 0x16, 0x7a, 0x1b, 0x36, 0x87, 0xc9, 0x05, + 0xdf, 0xe7, 0x30, 0x45, 0x9e, 0x8a, 0x04, 0x64, 0xd6, 0x08, 0xa6, 0x37, 0xe4, 0xf4, 0xc6, 0x89, + 0xd3, 0x2e, 0x2e, 0xdc, 0xfd, 0xb1, 0x97, 0x89, 0x3a, 0x9e, 0x97, 0xc2, 0x0e, 0xfa, 0x3a, 0xe8, + 0xbc, 0xf5, 0x05, 0xe9, 0xec, 0x85, 0xca, 0xc3, 0xd8, 0xfa, 0x11, 0xe4, 0x13, 0xab, 0x04, 0x5d, + 0xfc, 0x74, 0x45, 0x78, 0x2b, 0x24, 0xbe, 0x24, 0x35, 0x8f, 0xfa, 0x72, 0x3c, 0x7a, 0xcd, 0x5c, + 0x3a, 0xc2, 0x0d, 0x7d, 0x03, 0xbb, 0x23, 0x79, 0x08, 0x10, 0x0c, 0xe9, 0x16, 0xaf, 0x19, 0xf8, + 0x67, 0x14, 0x16, 0xa2, 0x4c, 0xcf, 0xc3, 0x5a, 0xc8, 0xff, 0x2b, 0xa7, 0xcc, 0x9c, 0xaa, 0xe5, + 0x98, 0x67, 0xd4, 0x32, 0x6f, 0x64, 0x23, 0xfd, 0xb8, 0xe7, 0xae, 0x22, 0x45, 0xa2, 0x77, 0x0e, + 0xd2, 0x37, 0x7c, 0x47, 0x4c, 0x20, 0x56, 0xba, 0x0e, 0xab, 0x21, 0xf5, 0xc7, 0xc4, 0x27, 0x1f, + 0x31, 0xdb, 0xb6, 0x7c, 0x9b, 0x3a, 0xdd, 0x0e, 0x76, 0x0f, 0x46, 0x7f, 0x8d, 0x68, 0x70, 0x06, + 0x33, 0x55, 0xe2, 0x93, 0xab, 0x4a, 0xf7, 0x48, 0x4c, 0xb9, 0x12, 0x99, 0xb2, 0xcf, 0x61, 0xba, + 0xda, 0xb3, 0xd6, 0x8b, 0xb0, 0xcd, 0xdb, 0xf5, 0x95, 0x11, 0xc7, 0xa4, 0x9f, 0x30, 0xb7, 0x67, + 0xf8, 0x81, 0x63, 0x35, 0x61, 0x67, 0x04, 0x8f, 0x57, 0x8e, 0x7e, 0x0a, 0x39, 0xde, 0xf6, 0xf4, + 0xf2, 0xd3, 0x93, 0x6a, 0xd5, 0xa5, 0x9e, 0x7c, 0xb3, 0xd1, 0x2e, 0xcc, 0xb5, 0x48, 0xcd, 0xaa, + 0x12, 0x9f, 0xb9, 0x57, 0x24, 0x38, 0xe3, 0x5d, 0x5e, 0x2f, 0xcd, 0x76, 0x0f, 0x84, 0x46, 0x3f, + 0x84, 0x85, 0x88, 0x8d, 0x60, 0x5d, 0x81, 0x29, 0xda, 0xb2, 0xfb, 0x1c, 0x80, 0xb6, 0x6c, 0x51, + 0x58, 0xf8, 0x13, 0x60, 0x92, 0x8b, 0xd1, 0xb7, 0x90, 0x0e, 0x62, 0x01, 0xe5, 0x23, 0x73, 0x44, + 0xb3, 0x47, 0x5d, 0x4f, 0x2e, 0x0a, 0xfa, 0xeb, 0xb9, 0x9f, 0xfe, 0x79, 0xf1, 0xcb, 0x6b, 0xb3, + 0x68, 0x5a, 0xc6, 0x67, 0x90, 0x35, 0xe8, 0x2f, 0x05, 0x16, 0x07, 0x06, 0x05, 0x7a, 0x2f, 0xde, + 0x7b, 0x58, 0x30, 0xa9, 0xef, 0x8f, 0xad, 0x13, 0x98, 0x7b, 0x1c, 0x73, 0x0b, 0x6d, 0x48, 0xcc, + 0x50, 0xba, 0x78, 0xd8, 0x0d, 0x44, 0x1e, 0xfe, 0x9e, 0xbf, 0xc7, 0x3f, 0xa2, 0xdf, 0x15, 0xc8, + 0xc5, 0xa7, 0x08, 0x3a, 0x88, 0x47, 0x48, 0x4c, 0x26, 0xf5, 0x9d, 0xf1, 0x44, 0x02, 0x7a, 0x87, + 0x43, 0xe7, 0xd1, 0x5a, 0x2c, 0x34, 0x47, 0xc5, 0x35, 0x6e, 0x81, 0xee, 0x14, 0xd0, 0x92, 0x53, + 0x07, 0x1d, 0x25, 0x31, 0x0c, 0xc9, 0x3b, 0xf5, 0xf8, 0x79, 0xe2, 0x41, 0xb7, 0x1f, 0xe4, 0x99, + 0xbc, 0x77, 0x5c, 0xe6, 0x9a, 0xee, 0xed, 0xff, 0xaa, 0xc0, 0x7c, 0x6c, 0x7a, 0xa1, 0x42, 0x12, + 0x46, 0x7c, 0x1e, 0xaa, 0x07, 0x63, 0x69, 0x04, 0xf1, 0x22, 0x27, 0xce, 0xa0, 0x39, 0x49, 0xdc, + 0x94, 0x85, 0xe8, 0x6f, 0x05, 0x96, 0x93, 0x62, 0x04, 0x7d, 0x10, 0xdf, 0x70, 0x84, 0xf8, 0x52, + 0x0f, 0x9f, 0x23, 0x15, 0xc8, 0x6f, 0x73, 0xe4, 0x4d, 0xb4, 0x2e, 0x91, 0xfb, 0x32, 0x0c, 0xbb, + 0x1d, 0x1d, 0x0e, 0x02, 0x11, 0xfd, 0xa6, 0x40, 0x36, 0x2e, 0xbf, 0xd1, 0x7e, 0xd2, 0x75, 0xc5, + 0xfe, 0x1e, 0xa8, 0x85, 0x71, 0x24, 0x82, 0x76, 0x93, 0xd3, 0xae, 0x22, 0x6d, 0x10, 0xad, 0xf8, + 0xc7, 0xfe, 0x01, 0xe0, 0x29, 0xf5, 0xd0, 0x56, 0x7c, 0xa7, 0x48, 0xbc, 0xaa, 0xdb, 0xc3, 0x0b, + 0x05, 0xc8, 0x12, 0x07, 0x99, 0x47, 0x19, 0x09, 0x12, 0x8a, 0xd3, 0xe2, 0xf9, 0xed, 0x83, 0xa6, + 0xdc, 0x3f, 0x68, 0xca, 0xff, 0x0f, 0x9a, 0xf2, 0xf3, 0xa3, 0x96, 0xba, 0x7f, 0xd4, 0x52, 0xff, + 0x3e, 0x6a, 0xa9, 0xaf, 0xb1, 0x69, 0xf9, 0x37, 0xcd, 0xb2, 0x51, 0x61, 0x36, 0x96, 0xad, 0x98, + 0x6b, 0x76, 0x9f, 0xf7, 0x48, 0xbd, 0x8e, 0xbf, 0xe3, 0x9e, 0xfc, 0x83, 0xb2, 0x9c, 0xe6, 0x9f, + 0x3d, 0x07, 0x2f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x58, 0x42, 0xec, 0x98, 0xbd, 0x0a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/upgrade/keeper.go b/x/upgrade/keeper.go new file mode 100644 index 0000000000..2b8b15c81f --- /dev/null +++ b/x/upgrade/keeper.go @@ -0,0 +1,101 @@ +package upgrade + +import ( + fmt "fmt" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" + ibctypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" +) + +var _ ibctypes.UpgradeKeeper = (*Keeper)(nil) + +type Keeper struct { + // we use the same upgrade store key so existing IBC client state can + // safely be ported over without any migration + storeKey storetypes.StoreKey + + // in memory copy of the upgrade schedule if any. This is local per node + // and configured from the config. + upgradeSchedule map[string]Schedule + + // the app version that should be set in end blocker + pendingAppVersion uint64 +} + +type VersionSetter func(version uint64) + +// NewKeeper constructs an upgrade keeper +func NewKeeper(storeKey storetypes.StoreKey, upgradeSchedule map[string]Schedule) Keeper { + for chainID, schedule := range upgradeSchedule { + if err := schedule.ValidateBasic(); err != nil { + panic(fmt.Sprintf("invalid schedule %s: %v", chainID, err)) + } + } + return Keeper{ + storeKey: storeKey, + upgradeSchedule: upgradeSchedule, + } +} + +// ScheduleUpgrade implements the ibc upgrade keeper interface. This is a noop as +// no other process is allowed to schedule an upgrade but the upgrade keeper itself. +// This is kept around to support the interface. +func (k Keeper) ScheduleUpgrade(_ sdk.Context, _ types.Plan) error { + return nil +} + +// GetUpgradePlan implements the ibc upgrade keeper interface. This is used in BeginBlock +// to know when to write the upgraded consensus state. The IBC module needs to sign over +// the next consensus state to ensure a smooth transition for counterparty chains. This +// is implemented as a noop. Any IBC breaking change would be invoked by this upgrade module +// in end blocker. +func (k Keeper) GetUpgradePlan(_ sdk.Context) (plan types.Plan, havePlan bool) { + return types.Plan{}, false +} + +// SetUpgradedClient sets the expected upgraded client for the next version of this chain at the last height the current chain will commit. +func (k Keeper) SetUpgradedClient(ctx sdk.Context, planHeight int64, bz []byte) error { + store := ctx.KVStore(k.storeKey) + store.Set(types.UpgradedClientKey(planHeight), bz) + return nil +} + +// GetUpgradedClient gets the expected upgraded client for the next version of this chain +func (k Keeper) GetUpgradedClient(ctx sdk.Context, height int64) ([]byte, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.UpgradedClientKey(height)) + if len(bz) == 0 { + return nil, false + } + + return bz, true +} + +// SetUpgradedConsensusState set the expected upgraded consensus state for the next version of this chain +// using the last height committed on this chain. +func (k Keeper) SetUpgradedConsensusState(ctx sdk.Context, planHeight int64, bz []byte) error { + store := ctx.KVStore(k.storeKey) + store.Set(types.UpgradedConsStateKey(planHeight), bz) + return nil +} + +// GetUpgradedConsensusState get the expected upgraded consensus state for the next version of this chain +func (k Keeper) GetUpgradedConsensusState(ctx sdk.Context, lastHeight int64) ([]byte, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.UpgradedConsStateKey(lastHeight)) + if len(bz) == 0 { + return nil, false + } + + return bz, true +} + +// ClearIBCState clears any planned IBC state +func (k Keeper) ClearIBCState(ctx sdk.Context, lastHeight int64) { + // delete IBC client and consensus state from store if this is IBC plan + store := ctx.KVStore(k.storeKey) + store.Delete(types.UpgradedClientKey(lastHeight)) + store.Delete(types.UpgradedConsStateKey(lastHeight)) +} diff --git a/x/upgrade/test/integration_test.go b/x/upgrade/legacy_test.go similarity index 80% rename from x/upgrade/test/integration_test.go rename to x/upgrade/legacy_test.go index b481c9ef3e..dc17b4e890 100644 --- a/x/upgrade/test/integration_test.go +++ b/x/upgrade/legacy_test.go @@ -1,4 +1,4 @@ -package test +package upgrade_test import ( "context" @@ -8,7 +8,9 @@ import ( "github.com/celestiaorg/celestia-app/app" "github.com/celestiaorg/celestia-app/app/encoding" + testutil "github.com/celestiaorg/celestia-app/test/util" "github.com/celestiaorg/celestia-app/test/util/blobfactory" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/celestiaorg/celestia-app/test/util/testnode" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" @@ -24,14 +26,24 @@ import ( tmrand "github.com/tendermint/tendermint/libs/rand" ) -func TestUpgrade(t *testing.T) { +func TestLegacyUpgrade(t *testing.T) { if testing.Short() { t.Skip("skipping x/upgrade SDK integration test in short mode.") } - suite.Run(t, new(UpgradeTestSuite)) + suite.Run(t, new(LegacyUpgradeTestSuite)) } -type UpgradeTestSuite struct { +// TestRemoval verifies that no handler exists for msg-based software upgrade +// proposals. +func TestRemoval(t *testing.T) { + app, _ := testutil.SetupTestAppWithGenesisValSet(app.DefaultConsensusParams()) + sftwrUpgrd := types.MsgSoftwareUpgrade{} + router := app.MsgServiceRouter() + handler := router.Handler(&sftwrUpgrd) + require.Nil(t, handler) +} + +type LegacyUpgradeTestSuite struct { suite.Suite accounts []string @@ -46,7 +58,7 @@ type UpgradeTestSuite struct { // SetupSuite inits a standard chain, with the only exception being a // dramatically lowered quorum and threshold to pass proposals -func (s *UpgradeTestSuite) SetupSuite() { +func (s *LegacyUpgradeTestSuite) SetupSuite() { t := s.T() s.ecfg = encoding.MakeConfig(app.ModuleBasics) @@ -61,9 +73,9 @@ func (s *UpgradeTestSuite) SetupSuite() { tmCfg.Consensus.TimeoutCommit = 3 * time.Second cfg := testnode.DefaultConfig(). - WithAccounts(accounts). + WithFundedAccounts(accounts...). WithTendermintConfig(tmCfg). - WithGenesisOptions(testnode.ImmediateProposals(s.ecfg.Codec)) + WithModifiers(genesis.ImmediateProposals(s.ecfg.Codec)) cctx, _, _ := testnode.NewNetwork(t, cfg) @@ -85,7 +97,7 @@ func (s *UpgradeTestSuite) SetupSuite() { s.govModuleAddress = acc.GetAddress().String() } -func (s *UpgradeTestSuite) unusedAccount() string { +func (s *LegacyUpgradeTestSuite) unusedAccount() string { s.mut.Lock() acc := s.accounts[s.accountCounter] s.accountCounter++ @@ -95,7 +107,7 @@ func (s *UpgradeTestSuite) unusedAccount() string { // TestLegacyGovUpgradeFailure verifies that a transaction with a legacy // software upgrade proposal fails to execute. -func (s *UpgradeTestSuite) TestLegacyGovUpgradeFailure() { +func (s *LegacyUpgradeTestSuite) TestLegacyGovUpgradeFailure() { t := s.T() dep := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000000000))) @@ -118,13 +130,13 @@ func (s *UpgradeTestSuite) TestLegacyGovUpgradeFailure() { defer cancel() res, err := signer.SubmitTx(subCtx, []sdk.Msg{msg}, blobfactory.DefaultTxOpts()...) require.Error(t, err) - require.EqualValues(t, 9, res.Code, res.RawLog) // we're only submitting the tx, so we expect everything to work - assert.Contains(t, res.RawLog, "no handler exists for proposal type") + // As the type is not registered, the message will fail with unable to resolve type URL + require.EqualValues(t, 2, res.Code, res.RawLog) } // TestNewGovUpgradeFailure verifies that a transaction with a // MsgSoftwareUpgrade fails to execute. -func (s *UpgradeTestSuite) TestNewGovUpgradeFailure() { +func (s *LegacyUpgradeTestSuite) TestNewGovUpgradeFailure() { t := s.T() sss := types.MsgSoftwareUpgrade{ Authority: s.govModuleAddress, @@ -147,11 +159,11 @@ func (s *UpgradeTestSuite) TestNewGovUpgradeFailure() { defer cancel() res, err := signer.SubmitTx(subCtx, []sdk.Msg{msg}, blobfactory.DefaultTxOpts()...) require.Error(t, err) - require.EqualValues(t, 10, res.Code, res.RawLog) // we're only submitting the tx, so we expect everything to work - require.Contains(t, res.RawLog, "proposal message not recognized by router") + // As the type is not registered, the message will fail with unable to resolve type URL + require.EqualValues(t, 2, res.Code, res.RawLog) } -func (s *UpgradeTestSuite) TestIBCUpgradeFailure() { +func (s *LegacyUpgradeTestSuite) TestIBCUpgradeFailure() { t := s.T() plan := types.Plan{ Name: "v2", diff --git a/x/upgrade/module.go b/x/upgrade/module.go deleted file mode 100644 index 34eb187b54..0000000000 --- a/x/upgrade/module.go +++ /dev/null @@ -1,21 +0,0 @@ -package upgrade - -import ( - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" -) - -// TypeRegister is used to register the upgrade module's types in the encoding -// config without defining an entire module. -type TypeRegister struct{} - -// RegisterLegacyAminoCodec registers the upgrade types on the LegacyAmino codec. -func (TypeRegister) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { - upgradetypes.RegisterLegacyAminoCodec(cdc) -} - -// RegisterInterfaces registers the upgrade module types. -func (TypeRegister) RegisterInterfaces(registry codectypes.InterfaceRegistry) { - upgradetypes.RegisterInterfaces(registry) -} diff --git a/x/upgrade/test/removal_test.go b/x/upgrade/test/removal_test.go deleted file mode 100644 index e7313277ea..0000000000 --- a/x/upgrade/test/removal_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package test - -import ( - "testing" - - "github.com/celestiaorg/celestia-app/app" - testutil "github.com/celestiaorg/celestia-app/test/util" - sdkupgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/stretchr/testify/require" -) - -// TestRemoval verifies that no handler exists for msg-based software upgrade -// proposals. -func TestRemoval(t *testing.T) { - app, _ := testutil.SetupTestAppWithGenesisValSet(app.DefaultConsensusParams()) - sftwrUpgrd := sdkupgradetypes.MsgSoftwareUpgrade{} - router := app.MsgServiceRouter() - handler := router.Handler(&sftwrUpgrd) - require.Nil(t, handler) -} diff --git a/x/upgrade/types.go b/x/upgrade/types.go new file mode 100644 index 0000000000..79fe78cfdb --- /dev/null +++ b/x/upgrade/types.go @@ -0,0 +1,138 @@ +package upgrade + +import ( + fmt "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +const ( + StoreKey = upgradetypes.StoreKey + ModuleName = upgradetypes.ModuleName +) + +var _ sdk.Msg = &MsgVersionChange{} + +// TypeRegister is used to register the upgrade module's types in the encoding +// config without defining an entire module. +type TypeRegister struct{} + +// RegisterLegacyAminoCodec registers the upgrade types on the LegacyAmino codec. +func (TypeRegister) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(upgradetypes.Plan{}, "cosmos-sdk/Plan", nil) + cdc.RegisterConcrete(MsgVersionChange{}, "celestia/MsgVersionChange", nil) +} + +// RegisterInterfaces registers the upgrade module types. +func (TypeRegister) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgVersionChange{}, + ) +} + +func (msg *MsgVersionChange) GetSigners() []sdk.AccAddress { + return nil +} + +func (msg *MsgVersionChange) ValidateBasic() error { + return nil +} + +// NewMsgVersionChange creates a tx in byte form used to signal to validators +// to change to a new version +func NewMsgVersionChange(txConfig client.TxConfig, version uint64) ([]byte, error) { + builder := txConfig.NewTxBuilder() + msg := &MsgVersionChange{ + Version: version, + } + if err := builder.SetMsgs(msg); err != nil { + return nil, err + } + return txConfig.TxEncoder()(builder.GetTx()) +} + +func IsUpgradeMsg(msg []sdk.Msg) (uint64, bool) { + if len(msg) != 1 { + return 0, false + } + msgVersionChange, ok := msg[0].(*MsgVersionChange) + if !ok { + return 0, false + } + return msgVersionChange.Version, true +} + +func (s Schedule) ValidateBasic() error { + lastHeight := 0 + lastVersion := uint64(0) + for idx, plan := range s { + if err := plan.ValidateBasic(); err != nil { + return fmt.Errorf("plan %d: %w", idx, err) + } + if plan.Start <= int64(lastHeight) { + return fmt.Errorf("plan %d: start height must be greater than %d, got %d", idx, lastHeight, plan.Start) + } + if plan.Version <= lastVersion { + return fmt.Errorf("plan %d: version must be greater than %d, got %d", idx, lastVersion, plan.Version) + } + lastHeight = int(plan.End) + lastVersion = plan.Version + } + return nil +} + +// ValidateVersions checks if all plan versions are covered by all the app versions +// that the state machine supports. +func (s Schedule) ValidateVersions(appVersions []uint64) error { + versionMap := make(map[uint64]struct{}) + for _, version := range appVersions { + versionMap[version] = struct{}{} + } + for _, plan := range s { + if _, ok := versionMap[plan.Version]; !ok { + return fmt.Errorf("plan version %d not found in app versions %v", plan.Version, appVersions) + } + } + return nil +} + +func (s Schedule) ShouldProposeUpgrade(height int64) (uint64, bool) { + for _, plan := range s { + if height >= plan.Start-1 && height < plan.End { + return plan.Version, true + } + } + return 0, false +} + +func (p Plan) ValidateBasic() error { + if p.Start < 1 { + return fmt.Errorf("plan start height cannot be negative or zero: %d", p.Start) + } + if p.End < 1 { + return fmt.Errorf("plan end height cannot be negative or zero: %d", p.End) + } + if p.Start > p.End { + return fmt.Errorf("plan end height must be greater or equal than start height: %d >= %d", p.Start, p.End) + } + if p.Version == 0 { + return fmt.Errorf("plan version cannot be zero") + } + return nil +} + +func NewSchedule(plans ...Plan) Schedule { + return plans +} + +func NewPlan(startHeight, endHeight int64, version uint64) Plan { + return Plan{ + Start: startHeight, + End: endHeight, + Version: version, + } +} diff --git a/x/upgrade/types.pb.go b/x/upgrade/types.pb.go new file mode 100644 index 0000000000..3e5eb9271e --- /dev/null +++ b/x/upgrade/types.pb.go @@ -0,0 +1,301 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: celestia/upgrade/v1/types.proto + +package upgrade + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgVersionChange is a message that signals an app version change +type MsgVersionChange struct { + // the app version this message proposes upgrading to + Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` +} + +func (m *MsgVersionChange) Reset() { *m = MsgVersionChange{} } +func (m *MsgVersionChange) String() string { return proto.CompactTextString(m) } +func (*MsgVersionChange) ProtoMessage() {} +func (*MsgVersionChange) Descriptor() ([]byte, []int) { + return fileDescriptor_0195354c266f07b3, []int{0} +} +func (m *MsgVersionChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVersionChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVersionChange.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVersionChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVersionChange.Merge(m, src) +} +func (m *MsgVersionChange) XXX_Size() int { + return m.Size() +} +func (m *MsgVersionChange) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVersionChange.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVersionChange proto.InternalMessageInfo + +func (m *MsgVersionChange) GetVersion() uint64 { + if m != nil { + return m.Version + } + return 0 +} + +func init() { + proto.RegisterType((*MsgVersionChange)(nil), "celestia.upgrade.v1.MsgVersionChange") +} + +func init() { proto.RegisterFile("celestia/upgrade/v1/types.proto", fileDescriptor_0195354c266f07b3) } + +var fileDescriptor_0195354c266f07b3 = []byte{ + // 164 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0x4e, 0xcd, 0x49, + 0x2d, 0x2e, 0xc9, 0x4c, 0xd4, 0x2f, 0x2d, 0x48, 0x2f, 0x4a, 0x4c, 0x49, 0xd5, 0x2f, 0x33, 0xd4, + 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0x29, 0xd0, + 0x83, 0x2a, 0xd0, 0x2b, 0x33, 0x54, 0xd2, 0xe1, 0x12, 0xf0, 0x2d, 0x4e, 0x0f, 0x4b, 0x2d, 0x2a, + 0xce, 0xcc, 0xcf, 0x73, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0x15, 0x92, 0xe0, 0x62, 0x2f, 0x83, 0x08, + 0x48, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x04, 0xc1, 0xb8, 0x4e, 0xee, 0x27, 0x1e, 0xc9, 0x31, 0x5e, + 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, + 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x9b, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, + 0xab, 0x0f, 0xb3, 0x27, 0xbf, 0x28, 0x1d, 0xce, 0xd6, 0x4d, 0x2c, 0x28, 0xd0, 0xaf, 0x80, 0x39, + 0x2d, 0x89, 0x0d, 0xec, 0x24, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x04, 0x7a, 0xde, 0x74, + 0xb5, 0x00, 0x00, 0x00, +} + +func (m *MsgVersionChange) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVersionChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVersionChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Version != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { + offset -= sovTypes(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgVersionChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Version != 0 { + n += 1 + sovTypes(uint64(m.Version)) + } + return n +} + +func sovTypes(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTypes(x uint64) (n int) { + return sovTypes(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgVersionChange) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVersionChange: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVersionChange: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTypes(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTypes + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTypes + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTypes + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTypes + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTypes + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTypes + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTypes = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTypes = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/upgrade/types_test.go b/x/upgrade/types_test.go new file mode 100644 index 0000000000..aef2e8bb5e --- /dev/null +++ b/x/upgrade/types_test.go @@ -0,0 +1,69 @@ +package upgrade_test + +import ( + fmt "fmt" + "testing" + + "github.com/celestiaorg/celestia-app/x/upgrade" + "github.com/stretchr/testify/require" +) + +func TestScheduleValidity(t *testing.T) { + testCases := []struct { + schedule upgrade.Schedule + valid bool + }{ + // can be empty + {upgrade.Schedule{}, true}, + // plan can not start at height 0 + {upgrade.Schedule{upgrade.Plan{Start: 0, End: 2, Version: 1}}, false}, + // end height can not be 0 + {upgrade.Schedule{upgrade.Plan{Version: 1}}, false}, + {upgrade.Schedule{upgrade.Plan{Start: 1, End: 2, Version: 1}}, true}, + // version can't be 0 + {upgrade.Schedule{upgrade.Plan{Start: 1, End: 2, Version: 0}}, false}, + // start and end height can be the same + {upgrade.Schedule{upgrade.Plan{Start: 2, End: 2, Version: 1}}, true}, + // end height must be greater than start height + {upgrade.Schedule{upgrade.Plan{Start: 2, End: 1, Version: 1}}, false}, + // plans can not overlap + {upgrade.Schedule{upgrade.Plan{Start: 1, End: 2, Version: 1}, upgrade.Plan{Start: 2, End: 3, Version: 2}}, false}, + // plans must be in order. They can skip versions + {upgrade.Schedule{upgrade.Plan{Start: 1, End: 2, Version: 1}, upgrade.Plan{Start: 3, End: 4, Version: 2}, upgrade.Plan{Start: 5, End: 6, Version: 4}}, true}, + {upgrade.Schedule{upgrade.Plan{Start: 1, End: 2, Version: 1}, upgrade.Plan{Start: 3, End: 4, Version: 2}, upgrade.Plan{Start: 5, End: 10, Version: 1}}, false}, + } + + for idx, tc := range testCases { + t.Run(fmt.Sprintf("case%d", idx), func(t *testing.T) { + if tc.valid { + require.NoError(t, tc.schedule.ValidateBasic()) + } else { + require.Error(t, tc.schedule.ValidateBasic()) + } + }) + } +} + +func TestScheduleValidateVersions(t *testing.T) { + testCases := []struct { + schedule upgrade.Schedule + appVersions []uint64 + valid bool + }{ + // can be empty + {upgrade.Schedule{}, []uint64{1, 2, 3}, true}, + {upgrade.Schedule{upgrade.Plan{Version: 3}}, []uint64{1, 2, 3}, true}, + {upgrade.Schedule{upgrade.Plan{Version: 4}}, []uint64{1, 2, 3}, false}, + {upgrade.Schedule{upgrade.Plan{Version: 2}, upgrade.Plan{Version: 5}}, []uint64{1, 2, 3}, false}, + } + + for idx, tc := range testCases { + t.Run(fmt.Sprintf("case%d", idx), func(t *testing.T) { + if tc.valid { + require.NoError(t, tc.schedule.ValidateVersions(tc.appVersions)) + } else { + require.Error(t, tc.schedule.ValidateVersions(tc.appVersions)) + } + }) + } +} diff --git a/x/upgrade/upgrade.go b/x/upgrade/upgrade.go new file mode 100644 index 0000000000..aedf46aa91 --- /dev/null +++ b/x/upgrade/upgrade.go @@ -0,0 +1,40 @@ +package upgrade + +type Schedule []Plan + +type Plan struct { + Start int64 + End int64 + Version uint64 +} + +// ShouldUpgradeNextHeight returns true if the network of the given chainID should +// modify the app version in the following block. This can be used for both upgrading +// and downgrading. This relies on social consensus to work. At least 2/3+ of the +// validators must have the same app version and height in their schedule for the +// upgrade to happen successfully. +func (k Keeper) ShouldProposeUpgrade(chainID string, height int64) (uint64, bool) { + if k.upgradeSchedule == nil { + return 0, false + } + if schedule, ok := k.upgradeSchedule[chainID]; ok { + return schedule.ShouldProposeUpgrade(height) + } + return 0, false +} + +func (k *Keeper) PrepareUpgradeAtEndBlock(version uint64) { + k.pendingAppVersion = version +} + +func (k *Keeper) ShouldUpgrade() bool { + return k.pendingAppVersion != 0 +} + +func (k Keeper) GetNextAppVersion() uint64 { + return k.pendingAppVersion +} + +func (k *Keeper) MarkUpgradeComplete() { + k.pendingAppVersion = 0 +} diff --git a/x/upgrade/upgrade_test.go b/x/upgrade/upgrade_test.go new file mode 100644 index 0000000000..2921d472e5 --- /dev/null +++ b/x/upgrade/upgrade_test.go @@ -0,0 +1,230 @@ +package upgrade_test + +import ( + "encoding/json" + "testing" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/celestiaorg/celestia-app/pkg/da" + "github.com/celestiaorg/celestia-app/pkg/shares" + "github.com/celestiaorg/celestia-app/pkg/square" + "github.com/celestiaorg/celestia-app/pkg/user" + "github.com/celestiaorg/celestia-app/test/util" + "github.com/celestiaorg/celestia-app/test/util/testfactory" + "github.com/celestiaorg/celestia-app/x/upgrade" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" +) + +func TestUpgradeAppVersion(t *testing.T) { + testApp, kr := setupTestApp(t, upgrade.NewSchedule(upgrade.NewPlan(3, 5, 2))) + addr := testfactory.GetAddress(kr, "account") + encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + signer, err := user.NewSigner(kr, nil, addr, encCfg.TxConfig, testApp.GetChainID(), 1, 0) + require.NoError(t, err) + coins := types.NewCoins(types.NewCoin("utia", types.NewInt(10))) + sendMsg := bank.NewMsgSend(addr, addr, coins) + sendTx, err := signer.CreateTx([]types.Msg{sendMsg}, user.SetGasLimitAndFee(1e6, 1)) + require.NoError(t, err) + + upgradeTx, err := upgrade.NewMsgVersionChange(testApp.GetTxConfig(), 3) + require.NoError(t, err) + respCheckTx := testApp.CheckTx(abci.RequestCheckTx{Tx: upgradeTx}) + // we expect that a new msg version change should always be rejected + // by checkTx + require.EqualValues(t, 15, respCheckTx.Code, respCheckTx.Log) + + resp := testApp.PrepareProposal(abci.RequestPrepareProposal{ + Height: 2, + ChainId: testApp.GetChainID(), + BlockData: &tmproto.Data{}, + BlockDataSize: 1e6, + }) + + // At the height before the first height in the upgrade plan, the + // node should prepend a signal upgrade message. + require.Len(t, resp.BlockData.Txs, 1) + tx, err := testApp.GetTxConfig().TxDecoder()(resp.BlockData.Txs[0]) + require.NoError(t, err) + require.Len(t, tx.GetMsgs(), 1) + msg, ok := tx.GetMsgs()[0].(*upgrade.MsgVersionChange) + require.True(t, ok) + require.EqualValues(t, 2, msg.Version) + + { + // the same thing should happen if we run prepare proposal + // at height 4 as it is within the range + resp := testApp.PrepareProposal(abci.RequestPrepareProposal{ + Height: 4, + ChainId: testApp.GetChainID(), + BlockData: &tmproto.Data{}, + BlockDataSize: 1e6, + }) + + require.Len(t, resp.BlockData.Txs, 1) + tx, err := testApp.GetTxConfig().TxDecoder()(resp.BlockData.Txs[0]) + require.NoError(t, err) + require.Len(t, tx.GetMsgs(), 1) + msg, ok := tx.GetMsgs()[0].(*upgrade.MsgVersionChange) + require.True(t, ok) + require.EqualValues(t, 2, msg.Version) + } + + { + // we send the same proposal but now with an existing message and a + // smaller BlockDataSize. It should kick out the tx in place of the + // upgrade tx + resp := testApp.PrepareProposal(abci.RequestPrepareProposal{ + Height: 2, + ChainId: testApp.GetChainID(), + BlockData: &tmproto.Data{Txs: [][]byte{sendTx}}, + BlockDataSize: 1e2, + }) + require.Len(t, resp.BlockData.Txs, 1) + tx, err := testApp.GetTxConfig().TxDecoder()(resp.BlockData.Txs[0]) + require.NoError(t, err) + require.Len(t, tx.GetMsgs(), 1) + msg, ok := tx.GetMsgs()[0].(*upgrade.MsgVersionChange) + require.True(t, ok) + require.EqualValues(t, 2, msg.Version) + } + + { + // Height 5 however is outside the range and thus the upgrade + // message should not be prepended + resp := testApp.PrepareProposal(abci.RequestPrepareProposal{ + Height: 5, + ChainId: testApp.GetChainID(), + BlockData: &tmproto.Data{}, + BlockDataSize: 1e6, + }) + require.Len(t, resp.BlockData.Txs, 0) + } + + // We should accept this proposal as valid + processProposalResp := testApp.ProcessProposal(abci.RequestProcessProposal{ + Header: tmproto.Header{ + Height: 2, + DataHash: resp.BlockData.Hash, + }, + BlockData: resp.BlockData, + }) + require.True(t, processProposalResp.IsOK()) + + { + // to assert that the upgrade tx must be the first tx + // we insert a tx before the upgrade tx. To get the hash + // we need to build the square and data availability header + txs := [][]byte{[]byte("hello world"), upgradeTx} + dataSquare, txs, err := square.Build(txs, appconsts.LatestVersion, appconsts.DefaultGovMaxSquareSize) + require.NoError(t, err) + eds, err := da.ExtendShares(shares.ToBytes(dataSquare)) + require.NoError(t, err) + dah, err := da.NewDataAvailabilityHeader(eds) + require.NoError(t, err) + blockData := &tmproto.Data{ + Txs: txs, + SquareSize: uint64(dataSquare.Size()), + Hash: dah.Hash(), + } + + processProposalResp := testApp.ProcessProposal(abci.RequestProcessProposal{ + Header: tmproto.Header{ + Height: 2, + DataHash: blockData.Hash, + }, + BlockData: blockData, + }) + require.True(t, processProposalResp.IsRejected()) + } + + testApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 2}}) + respDeliverTx := testApp.DeliverTx(abci.RequestDeliverTx{Tx: resp.BlockData.Txs[0]}) + require.EqualValues(t, 0, respDeliverTx.Code, respDeliverTx.Log) + // app version should not have changed yet + require.EqualValues(t, 1, testApp.AppVersion()) + respEndBlock := testApp.EndBlock(abci.RequestEndBlock{Height: 2}) + // now the app version changes + require.EqualValues(t, 2, respEndBlock.ConsensusParamUpdates.Version.AppVersion) + require.EqualValues(t, 2, testApp.AppVersion()) + + _ = testApp.Commit() + + // If another node proposes a block with a version change that is + // not supported by the nodes own state machine then the node + // rejects the proposed block + respProcessProposal := testApp.ProcessProposal(abci.RequestProcessProposal{ + Header: tmproto.Header{ + Height: 3, + }, + BlockData: &tmproto.Data{ + Txs: [][]byte{upgradeTx}, + }, + }) + require.True(t, respProcessProposal.IsRejected()) + + // if we ask the application to prepare another proposal + // it will not add the upgrade signal message even though + // its within the range of the plan because the application + // has already upgraded to that height + respPrepareProposal := testApp.PrepareProposal(abci.RequestPrepareProposal{ + Height: 3, + ChainId: testApp.GetChainID(), + BlockData: &tmproto.Data{}, + BlockDataSize: 1e6, + }) + require.Len(t, respPrepareProposal.BlockData.Txs, 0) +} + +func setupTestApp(t *testing.T, schedule upgrade.Schedule) (*app.App, keyring.Keyring) { + t.Helper() + + db := dbm.NewMemDB() + chainID := "test_chain" + encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + upgradeSchedule := make(map[string]upgrade.Schedule) + upgradeSchedule[chainID] = schedule + testApp := app.New(log.NewNopLogger(), db, nil, true, 0, encCfg, upgradeSchedule, util.EmptyAppOptions{}) + + genesisState, _, kr := util.GenesisStateWithSingleValidator(testApp, "account") + + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + require.EqualValues(t, 0, testApp.GetBaseApp().AppVersion()) + + cp := app.DefaultConsensusParams() + abciParams := &abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxBytes: cp.Block.MaxBytes, + MaxGas: cp.Block.MaxGas, + }, + Evidence: &cp.Evidence, + Validator: &cp.Validator, + Version: &cp.Version, + } + + _ = testApp.InitChain( + abci.RequestInitChain{ + Time: time.Now(), + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: abciParams, + AppStateBytes: stateBytes, + ChainId: chainID, + }, + ) + + // assert that the chain starts with version provided in genesis + require.EqualValues(t, app.DefaultConsensusParams().Version.AppVersion, testApp.GetBaseApp().AppVersion()) + + _ = testApp.Commit() + return testApp, kr +}