Skip to content

Commit

Permalink
Merge branch 'release/v1.17.x' into dwedul/bp-wasmvm-1.17
Browse files Browse the repository at this point in the history
  • Loading branch information
iramiller authored Jan 10, 2024
2 parents 8dd51bf + 118a19d commit 17f4ac2
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Improvements

* Add StoreLoader wrapper to check configuration settings [#1792](https://github.com/provenance-io/provenance/pull/1792).
* Create a default market in `make run`, `localnet`, `devnet` and the `provenanced testnet` command [#1757](https://github.com/provenance-io/provenance/issues/1757).
* Updated documentation for each module to work with docusaurus [PR 1763](https://github.com/provenance-io/provenance/pull/1763)

Expand Down
10 changes: 6 additions & 4 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,7 @@ func New(
}

// Currently in an upgrade hold for this block.
var storeLoader baseapp.StoreLoader
if upgradeInfo.Name != "" && upgradeInfo.Height == app.LastBlockHeight()+1 {
if app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) {
app.Logger().Info("Skipping upgrade based on height",
Expand All @@ -1053,14 +1054,15 @@ func New(
"lastHeight", app.LastBlockHeight(),
)
// See if we have a custom store loader to use for upgrades.
storeLoader := GetUpgradeStoreLoader(app, upgradeInfo)
if storeLoader != nil {
app.SetStoreLoader(storeLoader)
}
storeLoader = GetUpgradeStoreLoader(app, upgradeInfo)
}
}
// --

// Verify configuration settings
storeLoader = ValidateWrapper(app.Logger(), appOpts, storeLoader)
app.SetStoreLoader(storeLoader)

if loadLatest {
if err := app.LoadLatestVersion(); err != nil {
tmos.Exit(err.Error())
Expand Down
75 changes: 75 additions & 0 deletions app/store_loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package app

import (
"errors"
"fmt"
"time"

"github.com/spf13/cast"

"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/server"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// StoreLoaderWrapper is a wrapper function that is called before the StoreLoader.
type StoreLoaderWrapper func(sdk.CommitMultiStore, baseapp.StoreLoader) error

// WrapStoreLoader creates a new StoreLoader by wrapping an existing one.
func WrapStoreLoader(wrapper StoreLoaderWrapper, storeLoader baseapp.StoreLoader) baseapp.StoreLoader {
return func(ms sdk.CommitMultiStore) error {
if storeLoader == nil {
storeLoader = baseapp.DefaultStoreLoader
}

if wrapper == nil {
return errors.New("wrapper must not be nil")
}

return wrapper(ms, storeLoader)
}
}

// ValidateWrapper creates a new StoreLoader that first checks the config settings before calling the provided StoreLoader.
func ValidateWrapper(logger log.Logger, appOpts servertypes.AppOptions, storeLoader baseapp.StoreLoader) baseapp.StoreLoader {
return WrapStoreLoader(func(ms sdk.CommitMultiStore, sl baseapp.StoreLoader) error {
const MaxPruningInterval = 999
const SleepSeconds = 30
backend := server.GetAppDBBackend(appOpts)
interval := cast.ToUint64(appOpts.Get("pruning-interval"))
txIndexer := cast.ToStringMap(appOpts.Get("tx_index"))
indexer := cast.ToString(txIndexer["indexer"])
fastNode := cast.ToBool(appOpts.Get("iavl-disable-fastnode"))
var errs []string

if interval > MaxPruningInterval {
errs = append(errs, fmt.Sprintf("pruning-interval %d EXCEEDS %d AND IS NOT RECOMMENDED, AS IT CAN LEAD TO MISSED BLOCKS ON VALIDATORS", interval, MaxPruningInterval))
}

if indexer != "" {
errs = append(errs, fmt.Sprintf("indexer \"%s\" IS NOT RECOMMENDED, AND IT IS RECOMMENDED TO USE \"%s\"", indexer, ""))
}

if fastNode {
errs = append(errs, fmt.Sprintf("iavl-disable-fastnode \"%v\" IS NOT RECOMMENDED, AND IT IS RECOMMENDED TO USE \"%v\"", fastNode, !fastNode))
}

if backend != dbm.GoLevelDBBackend {
errs = append(errs, fmt.Sprintf("%s IS NO LONGER SUPPORTED. MIGRATE TO %s", backend, dbm.GoLevelDBBackend))
}

if len(errs) > 0 {
logger.Error(fmt.Sprintf("NODE WILL CONTINUE AFTER %d SECONDS", SleepSeconds))
for _, err := range errs {
logger.Error(err)
}
time.Sleep(SleepSeconds * time.Second)
}

return sl(ms)
}, storeLoader)
}
196 changes: 196 additions & 0 deletions app/store_loader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package app

import (
"testing"
"time"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/store/rootmulti"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
)

func TestWrapStoreLoader(t *testing.T) {
var flag bool
tests := []struct {
name string
storeLoader baseapp.StoreLoader
wrapper StoreLoaderWrapper
err string
}{
{
name: "nil store loader is set with valid value",
storeLoader: nil,
wrapper: createMockStoreWrapper(&flag),
},
{
name: "nil wrapper is handled",
storeLoader: createMockStoreLoader(),
wrapper: nil,
err: "wrapper must not be nil",
},
{
name: "contents of wrapper are called",
storeLoader: createMockStoreLoader(),
wrapper: createMockFlipWrapper(&flag),
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
storeLoader := WrapStoreLoader(tc.wrapper, tc.storeLoader)
db := dbm.MemDB{}
ms := rootmulti.NewStore(&db, nil)
assert.NotNil(t, ms, "should create a new multistore for testing")
flag = false

err := storeLoader(ms)
if len(tc.err) > 0 {
assert.EqualError(t, err, tc.err, "should have correct error")
assert.False(t, flag, "wrapper should not be executed")
} else {
assert.NoError(t, err, "should not return an error on success")
assert.True(t, flag, "wrapper should execute and have correct logic")
}

})
}
}

func TestValidateWrapper(t *testing.T) {
tests := []struct {
name string
appOpts MockAppOptions
delta uint64
}{
{
name: "recommended pruning, indexer, db, and fastnode should not wait",
appOpts: MockAppOptions{
pruning: "13",
db: "goleveldb",
fastNode: "false",
indexer: "",
},
delta: 0,
},
{
name: "non-recommended pruning should wait",
appOpts: MockAppOptions{
pruning: "1000",
db: "goleveldb",
fastNode: "false",
indexer: "",
},
delta: 30,
},
{
name: "non-recommended indexer should wait",
appOpts: MockAppOptions{
pruning: "13",
db: "goleveldb",
fastNode: "false",
indexer: "kv",
},
delta: 30,
},
{
name: "non-recommended db should wait",
appOpts: MockAppOptions{
pruning: "13",
db: "cleveldb",
fastNode: "false",
indexer: "",
},
delta: 30,
},
{
name: "non-recommended fastnode should wait",
appOpts: MockAppOptions{
pruning: "13",
db: "goleveldb",
fastNode: "true",
indexer: "",
},
delta: 30,
},
{
name: "multiple non-recommended should wait",
appOpts: MockAppOptions{
pruning: "1000",
db: "cleveldb",
fastNode: "true",
indexer: "kv",
},
delta: 30,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
logger := log.NewNopLogger()
storeLoader := ValidateWrapper(logger, tc.appOpts, createMockStoreLoader())
db := dbm.MemDB{}
ms := rootmulti.NewStore(&db, nil)
assert.NotNil(t, ms, "should create a new multistore for testing")

start := time.Now()
err := storeLoader(ms)
delta := uint64(time.Now().Sub(start).Seconds())
assert.NoError(t, err, "should not throw error")
assert.GreaterOrEqual(t, delta, tc.delta, "should wait correct amount of time")
})
}
}

// createMockStoreLoader creates an empty StoreLoader.
func createMockStoreLoader() baseapp.StoreLoader {
return func(ms sdk.CommitMultiStore) error {
return nil
}
}

// createMockFlipWrapper creates a wrapper that has logic to flip a bit.
func createMockFlipWrapper(flag *bool) StoreLoaderWrapper {
return func(cms sdk.CommitMultiStore, sl baseapp.StoreLoader) error {
*flag = !(*flag)
return nil
}
}

// createMockStoreWrapper creates a wrapper that checks if the StoreLoader is nil and sets the flag accordingly.
func createMockStoreWrapper(flag *bool) StoreLoaderWrapper {
return func(cms sdk.CommitMultiStore, sl baseapp.StoreLoader) error {
*flag = sl != nil
return nil
}
}

// MockAppOptions is a mocked version of AppOpts that allows the developer to provide the pruning attribute.
type MockAppOptions struct {
pruning string
indexer string
db string
fastNode string
}

// Get returns the value for the provided option.
func (m MockAppOptions) Get(opt string) interface{} {
switch opt {
case "pruning-interval":
return m.pruning
case "tx_index":
return map[string]interface{}{
"indexer": m.indexer,
}
case "app-db-backend":
return m.db
case "db-backend":
return m.db
case "iavl-disable-fastnode":
return m.fastNode
}

return nil
}

0 comments on commit 17f4ac2

Please sign in to comment.