-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds validation checks related to the missed blocks (#1792)
* Create store_loader with wrappers, and update main to use them. * Skeleton for tests. * Add tests for wrap store loader. * Clean up text and example code. * Add tests for pruning wrapper. * Add comments. * Update CHANGELOG. * Lint fixes. * Set MaxPruningInterval to number supplied by discord. * Remove accidental pushed files. * Fix casting. * Change goleveldb to memdb in tests to prevent directories from being created. * Lint fix. * Change interval max. * Update message to include wait time. * Separate onto new log line. * Add check for indexer and update tests. * Remove accidental binaries. * Update changelog to reflect not just pruning interval. * Add check for database. * Add check for disable-iavl-fastnode. * Tidy by updating changelog, names, and dependencies.
- Loading branch information
1 parent
91b0813
commit daeb740
Showing
4 changed files
with
280 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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 | ||
} |