diff --git a/chain/app/export.go b/chain/app/export.go index e196d7bdea..a47d493215 100644 --- a/chain/app/export.go +++ b/chain/app/export.go @@ -20,14 +20,18 @@ func (app *BandApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteLis ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) if forZeroHeight { + ctx.Logger().Info("Preparing state for zero-height genesis") app.prepForZeroHeightGenesis(ctx, jailWhiteList) } + ctx.Logger().Info("Begin exporting states from each module") genState := app.mm.ExportGenesis(ctx) + ctx.Logger().Info("Encode exported states to JSON") appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err } + ctx.Logger().Info("Writing validators for the genesis") validators = staking.WriteValidators(ctx, app.StakingKeeper) return appState, validators, nil } @@ -54,11 +58,13 @@ func (app *BandApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st } /* Just to be safe, assert the invariants on current state. */ + ctx.Logger().Info("Asserting invarient") app.CrisisKeeper.AssertInvariants(ctx) /* Handle fee distribution state. */ // withdraw all validator commission + ctx.Logger().Info("Withdraw all validator commission") app.StakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) { accumCommission := app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, val.GetOperator()) if accumCommission.IsZero() { @@ -72,6 +78,7 @@ func (app *BandApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st }) // withdraw all delegator rewards + ctx.Logger().Info("Withdraw all delegator rewards") dels := app.StakingKeeper.GetAllDelegations(ctx) for _, delegation := range dels { _, err := app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) @@ -81,16 +88,20 @@ func (app *BandApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st } // clear validator slash events + ctx.Logger().Info("Clear all validator slash events") app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) // clear validator historical rewards + ctx.Logger().Info("Clear all validator historical rewards") app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) // set context height to zero + ctx.Logger().Info("Set context height to zero") height := ctx.BlockHeight() ctx = ctx.WithBlockHeight(0) // reinitialize all validators + ctx.Logger().Info("Re-Initialize all validators") app.StakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) { // donate any unwithdrawn outstanding reward fraction tokens to the community pool @@ -104,17 +115,20 @@ func (app *BandApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st }) // reinitialize all delegations + ctx.Logger().Info("Re-Initialize all delegations") for _, del := range dels { app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress) app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress) } // reset context height + ctx.Logger().Info("Reset context height") ctx = ctx.WithBlockHeight(height) /* Handle staking state. */ // iterate through redelegations, reset creation height + ctx.Logger().Info("Iterate through redelegations, reset creation height") app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) { for i := range red.Entries { red.Entries[i].CreationHeight = 0 @@ -124,6 +138,7 @@ func (app *BandApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st }) // iterate through unbonding delegations, reset creation height + ctx.Logger().Info("Iterate through unbonding delegations, reset creation height") app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) { for i := range ubd.Entries { ubd.Entries[i].CreationHeight = 0 @@ -134,6 +149,7 @@ func (app *BandApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st // Iterate through validators by power descending, reset bond heights, and // update bond intra-tx counters. + ctx.Logger().Info("Iterate through validators by power descending, reset bond heights, and update bond intra-tx counters.") store := ctx.KVStore(app.keys[staking.StoreKey]) iter := sdk.KVStoreReversePrefixIterator(store, staking.ValidatorsKey) counter := int16(0) @@ -161,6 +177,7 @@ func (app *BandApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st /* Handle slashing state. */ // reset start height on signing infos + ctx.Logger().Info("Reset start height on signing infos") app.SlashingKeeper.IterateValidatorSigningInfos( ctx, func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) { diff --git a/chain/cmd/bandd/main.go b/chain/cmd/bandd/main.go index 91bb71c3dc..9f87117985 100644 --- a/chain/cmd/bandd/main.go +++ b/chain/cmd/bandd/main.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "io" + "os" "path/filepath" "strconv" "strings" @@ -140,9 +141,12 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application func exportAppStateAndTMValidators( logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, ) (json.RawMessage, []tmtypes.GenesisValidator, error) { + newLogger := log.NewTMLogger(log.NewSyncWriter(os.Stderr)) + newLogger.Info("Start exporting genesis file...") if height != -1 { - bandApp := app.NewBandApp(logger, db, traceStore, false, uint(1), map[int64]bool{}, "", viper.GetBool(flagDisableFeelessReports), viper.GetUint32(flagWithOwasmCacheSize)) + bandApp := app.NewBandApp(newLogger, db, traceStore, false, uint(1), map[int64]bool{}, "", viper.GetBool(flagDisableFeelessReports), viper.GetUint32(flagWithOwasmCacheSize)) + newLogger.Info("Setup store at specific height", "height", height) err := bandApp.LoadHeight(height) if err != nil { return nil, nil, err @@ -151,6 +155,6 @@ func exportAppStateAndTMValidators( return bandApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } - bandApp := app.NewBandApp(logger, db, traceStore, true, uint(1), map[int64]bool{}, "", viper.GetBool(flagDisableFeelessReports), viper.GetUint32(flagWithOwasmCacheSize)) + bandApp := app.NewBandApp(newLogger, db, traceStore, true, uint(1), map[int64]bool{}, "", viper.GetBool(flagDisableFeelessReports), viper.GetUint32(flagWithOwasmCacheSize)) return bandApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } diff --git a/chain/x/oracle/genesis.go b/chain/x/oracle/genesis.go index 2c1ecc16ab..ca9e5f8c27 100644 --- a/chain/x/oracle/genesis.go +++ b/chain/x/oracle/genesis.go @@ -12,9 +12,10 @@ import ( // GenesisState is the oracle state that must be provided at genesis. type GenesisState struct { - Params types.Params `json:"params" yaml:"params"` - DataSources []types.DataSource `json:"data_sources" yaml:"data_sources"` - OracleScripts []types.OracleScript `json:"oracle_scripts" yaml:"oracle_scripts"` + Params types.Params `json:"params" yaml:"params"` + DataSources []types.DataSource `json:"data_sources" yaml:"data_sources"` + OracleScripts []types.OracleScript `json:"oracle_scripts" yaml:"oracle_scripts"` + Reporters []types.ReportersPerValidator `json:"reporters" yaml:"reporters"` } // DefaultGenesisState returns the default oracle genesis state. @@ -23,6 +24,7 @@ func DefaultGenesisState() GenesisState { Params: types.DefaultParams(), DataSources: []types.DataSource{}, OracleScripts: []types.OracleScript{}, + Reporters: []types.ReportersPerValidator{}, } } @@ -47,6 +49,12 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) []abci.ValidatorU for _, oracleScript := range data.OracleScripts { _ = k.AddOracleScript(ctx, oracleScript) } + for _, reportersPerValidator := range data.Reporters { + for _, reporter := range reportersPerValidator.Reporters { + k.AddReporter(ctx, reportersPerValidator.Validator, reporter) + } + } + return []abci.ValidatorUpdate{} } @@ -56,6 +64,7 @@ func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { Params: k.GetParams(ctx), DataSources: k.GetAllDataSources(ctx), OracleScripts: k.GetAllOracleScripts(ctx), + Reporters: k.GetAllReporters(ctx), } } diff --git a/chain/x/oracle/keeper/reporter.go b/chain/x/oracle/keeper/reporter.go index f69083b69b..bc8c3c57d8 100644 --- a/chain/x/oracle/keeper/reporter.go +++ b/chain/x/oracle/keeper/reporter.go @@ -3,6 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/staking/exported" "github.com/bandprotocol/bandchain/chain/x/oracle/types" ) @@ -51,3 +52,15 @@ func (k Keeper) GetReporters(ctx sdk.Context, val sdk.ValAddress) (reporters []s } return reporters } + +func (k Keeper) GetAllReporters(ctx sdk.Context) []types.ReportersPerValidator { + var reporterList []types.ReportersPerValidator + k.stakingKeeper.IterateBondedValidatorsByPower(ctx, func(index int64, validator exported.ValidatorI) (stop bool) { + valAddress := validator.GetOperator() + reporters := k.GetReporters(ctx, valAddress) + reporterList = append(reporterList, types.NewReportersPerValidator(valAddress, reporters)) + return false + }) + + return reporterList +} diff --git a/chain/x/oracle/keeper/reporter_test.go b/chain/x/oracle/keeper/reporter_test.go index bfb0485a4f..96fc08471f 100644 --- a/chain/x/oracle/keeper/reporter_test.go +++ b/chain/x/oracle/keeper/reporter_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "testing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" "github.com/bandprotocol/bandchain/chain/x/oracle/testapp" @@ -62,3 +63,46 @@ func TestGetReporters(t *testing.T) { require.Contains(t, reporters, testapp.Bob.Address) require.Contains(t, reporters, testapp.Carol.Address) } + +func TestGetAllReporters(t *testing.T) { + _, ctx, k := testapp.CreateTestInput(true) + // Initially, only validators should be reporters of themselves + reporters := k.GetAllReporters(ctx) + expectedReporterMap := map[string][]sdk.AccAddress{ + sdk.ValAddress(testapp.Validator1.Address).String(): {testapp.Validator1.Address}, + sdk.ValAddress(testapp.Validator2.Address).String(): {testapp.Validator2.Address}, + sdk.ValAddress(testapp.Validator3.Address).String(): {testapp.Validator3.Address}, + } + require.Equal(t, len(expectedReporterMap), len(reporters)) + for _, reportersPerValidator := range reporters { + valAddr := sdk.ValAddress(reportersPerValidator.Validator).String() + require.Contains(t, expectedReporterMap, valAddr) + for _, reporter := range reportersPerValidator.Reporters { + require.Contains(t, expectedReporterMap[valAddr], reporter) + } + } + + // After Alice, Bob, and Carol are added, they should be included in result of GetAllReporters + err := k.AddReporter(ctx, testapp.Validator1.ValAddress, testapp.Alice.Address) + require.NoError(t, err) + err = k.AddReporter(ctx, testapp.Validator1.ValAddress, testapp.Bob.Address) + require.NoError(t, err) + err = k.AddReporter(ctx, testapp.Validator3.ValAddress, testapp.Carol.Address) + require.NoError(t, err) + + reporters = k.GetAllReporters(ctx) + expectedReporterMap = map[string][]sdk.AccAddress{ + sdk.ValAddress(testapp.Validator1.Address).String(): {testapp.Validator1.Address, testapp.Bob.Address, testapp.Alice.Address}, + sdk.ValAddress(testapp.Validator2.Address).String(): {testapp.Validator2.Address}, + sdk.ValAddress(testapp.Validator3.Address).String(): {testapp.Validator3.Address, testapp.Carol.Address}, + } + + require.Equal(t, len(expectedReporterMap), len(reporters)) + for _, reportersPerValidator := range reporters { + valAddr := sdk.ValAddress(reportersPerValidator.Validator).String() + require.Contains(t, expectedReporterMap, valAddr) + for _, reporter := range reportersPerValidator.Reporters { + require.Contains(t, expectedReporterMap[valAddr], reporter) + } + } +} diff --git a/chain/x/oracle/types/reporter.go b/chain/x/oracle/types/reporter.go new file mode 100644 index 0000000000..c0d57f453d --- /dev/null +++ b/chain/x/oracle/types/reporter.go @@ -0,0 +1,17 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// ReportersPerValidator represents list of reporter address and their associated validator +type ReportersPerValidator struct { + Validator sdk.ValAddress `json:"validator" yaml:"validator"` + Reporters []sdk.AccAddress `json:"reporters" yaml:"reporters"` +} + +// NewReportersPerValidator creates new instance of ReportersPerValidator +func NewReportersPerValidator(validator sdk.ValAddress, reporters []sdk.AccAddress) ReportersPerValidator { + return ReportersPerValidator{ + Validator: validator, + Reporters: reporters, + } +}