From 94f45311a04f2f5e51046d23cf4885c3045262f9 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 9 Nov 2018 01:28:28 +0100 Subject: [PATCH 1/3] Fix state export/import, add to CI (#2690) * Update slashing import/export * More slashing.WriteGenesis * Add test import/export to CI * Store equality comparison. * Fix validator bond intra-tx counter * Set timeslices for unbonding validators * WriteGenesis => ExportGenesis * Delete validators from unbonding queue when re-bonded * Hook for validator deletion, fix staking genesis tests --- .circleci/config.yml | 21 +++++ Makefile | 12 ++- PENDING.md | 5 +- cmd/gaia/app/app.go | 32 +++++--- cmd/gaia/app/genesis.go | 30 ++++--- cmd/gaia/app/sim_test.go | 98 +++++++++++++++++++++++ cmd/gaia/init/collect.go | 2 +- cmd/gaia/init/init.go | 2 +- cmd/gaia/init/testnet.go | 2 +- cmd/gaia/init/utils.go | 8 +- docs/spec/staking/hooks.md | 2 +- examples/basecoin/cmd/basecoind/main.go | 2 +- examples/democoin/app/app.go | 4 +- examples/democoin/cmd/democoind/main.go | 2 +- examples/democoin/x/cool/keeper.go | 4 +- examples/democoin/x/cool/keeper_test.go | 2 +- examples/democoin/x/pow/keeper.go | 4 +- examples/democoin/x/pow/keeper_test.go | 2 +- types/stake.go | 6 +- types/store.go | 38 +++++++++ x/auth/genesis.go | 33 ++++++++ x/auth/{mapper.go => keeper.go} | 0 x/auth/{mapper_test.go => keeper_test.go} | 0 x/distribution/genesis.go | 8 +- x/distribution/keeper/genesis.go | 4 +- x/distribution/keeper/hooks.go | 2 +- x/distribution/keeper/key.go | 9 +++ x/distribution/types/genesis.go | 4 +- x/gov/genesis.go | 59 ++++++++++++-- x/gov/handler.go | 2 +- x/mint/genesis.go | 4 +- x/slashing/genesis.go | 75 ++++++++++++++++- x/slashing/hooks.go | 25 +++++- x/slashing/keeper.go | 16 ---- x/slashing/keeper_test.go | 21 ++--- x/slashing/keys.go | 9 +++ x/slashing/signing_info.go | 33 ++++++++ x/slashing/slashing_period.go | 15 ++++ x/slashing/test_common.go | 2 +- x/slashing/tick_test.go | 3 +- x/stake/genesis.go | 80 +++++++++++++----- x/stake/genesis_test.go | 23 +++--- x/stake/keeper/delegation.go | 15 ++++ x/stake/keeper/hooks.go | 4 +- x/stake/keeper/val_state_change.go | 9 ++- x/stake/keeper/validator.go | 33 +++++++- x/stake/stake.go | 3 + x/stake/types/genesis.go | 16 +++- 48 files changed, 640 insertions(+), 145 deletions(-) create mode 100644 x/auth/genesis.go rename x/auth/{mapper.go => keeper.go} (100%) rename x/auth/{mapper_test.go => keeper_test.go} (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 879df07f0..708b5887d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -137,6 +137,24 @@ jobs: export PATH="$GOBIN:$PATH" make test_sim_gaia_fast + test_sim_gaia_import_export: + <<: *defaults + parallelism: 1 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout + - run: + name: dependencies + command: | + export PATH="$GOBIN:$PATH" + make get_vendor_deps + - run: + name: Test Gaia import/export simulation + command: | + export PATH="$GOBIN:$PATH" + make test_sim_gaia_import_export + test_sim_gaia_multi_seed: <<: *defaults parallelism: 1 @@ -259,6 +277,9 @@ workflows: - test_sim_gaia_fast: requires: - setup_dependencies + - test_sim_gaia_import_export: + requires: + - setup_dependencies - test_sim_gaia_multi_seed: requires: - setup_dependencies diff --git a/Makefile b/Makefile index af4ea71c9..aaa2fbcf3 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,15 @@ test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=500 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=10 -v -timeout 24h +test_sim_gaia_import_export: + @echo "Running Gaia import/export simulation. This may take several minutes..." + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=4 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=11 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=12 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=13 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=414 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=4142 -v -timeout 24h + test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" @bash scripts/multisim.sh 25 @@ -250,4 +259,5 @@ localnet-stop: check_tools check_dev_tools get_tools get_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop \ -format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast test_sim_gaia_multi_seed update_tools update_dev_tools +format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ +test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools diff --git a/PENDING.md b/PENDING.md index 8b2044ea2..1d5752fc2 100644 --- a/PENDING.md +++ b/PENDING.md @@ -28,7 +28,7 @@ FEATURES * Gaia * SDK - * (#1336) Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. + - \#1336 Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. * Tendermint @@ -65,7 +65,8 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia - - \#2670 [x/stake] fixed incorrect `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - \#2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - \#2648 [gaiad] Fix `gaiad export` / `gaiad import` consistency, test in CI - \#2691 Fix local testnet creation by using a single canonical genesis time * SDK diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 1927d235d..918bfc67d 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -210,9 +210,6 @@ func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.R tags := gov.EndBlocker(ctx, app.govKeeper) validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) - // Add these new validators to the addr -> pubkey map. - app.slashingKeeper.AddValidators(ctx, validatorUpdates) - return abci.ResponseEndBlock{ ValidatorUpdates: validatorUpdates, Tags: tags, @@ -231,6 +228,10 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci // return sdk.ErrGenesisParse("").TraceCause(err, "") } + // sort by account number to maintain consistency + sort.Slice(genesisState.Accounts, func(i, j int) bool { + return genesisState.Accounts[i].AccountNumber < genesisState.Accounts[j].AccountNumber + }) // load the accounts for _, gacc := range genesisState.Accounts { acc := gacc.ToAccount() @@ -244,7 +245,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci panic(err) // TODO find a way to do this w/o panics } - // load the address to pubkey map + // initialize module-specific stores + auth.InitGenesis(ctx, app.feeCollectionKeeper, genesisState.AuthData) slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData) gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData) @@ -270,7 +272,6 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx) } - app.slashingKeeper.AddValidators(ctx, validators) // sanity check if len(req.Validators) > 0 { @@ -306,11 +307,12 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val app.accountKeeper.IterateAccounts(ctx, appendAccount) genState := NewGenesisState( accounts, - stake.WriteGenesis(ctx, app.stakeKeeper), - mint.WriteGenesis(ctx, app.mintKeeper), - distr.WriteGenesis(ctx, app.distrKeeper), - gov.WriteGenesis(ctx, app.govKeeper), - slashing.GenesisState{}, // TODO create write methods + auth.ExportGenesis(ctx, app.feeCollectionKeeper), + stake.ExportGenesis(ctx, app.stakeKeeper), + mint.ExportGenesis(ctx, app.mintKeeper), + distr.ExportGenesis(ctx, app.distrKeeper), + gov.ExportGenesis(ctx, app.govKeeper), + slashing.ExportGenesis(ctx, app.slashingKeeper), ) appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { @@ -337,12 +339,15 @@ var _ sdk.StakingHooks = Hooks{} // nolint func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { h.dh.OnValidatorCreated(ctx, valAddr) + h.sh.OnValidatorCreated(ctx, valAddr) } func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { h.dh.OnValidatorModified(ctx, valAddr) + h.sh.OnValidatorModified(ctx, valAddr) } -func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) { - h.dh.OnValidatorRemoved(ctx, valAddr) +func (h Hooks) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + h.dh.OnValidatorRemoved(ctx, consAddr, valAddr) + h.sh.OnValidatorRemoved(ctx, consAddr, valAddr) } func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { h.dh.OnValidatorBonded(ctx, consAddr, valAddr) @@ -358,10 +363,13 @@ func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddre } func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationCreated(ctx, delAddr, valAddr) + h.sh.OnDelegationCreated(ctx, delAddr, valAddr) } func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationSharesModified(ctx, delAddr, valAddr) + h.sh.OnDelegationSharesModified(ctx, delAddr, valAddr) } func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationRemoved(ctx, delAddr, valAddr) + h.sh.OnDelegationRemoved(ctx, delAddr, valAddr) } diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index cfbbe5cc2..38256e415 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -32,6 +32,7 @@ var ( // State to Unmarshal type GenesisState struct { Accounts []GenesisAccount `json:"accounts"` + AuthData auth.GenesisState `json:"auth"` StakeData stake.GenesisState `json:"stake"` MintData mint.GenesisState `json:"mint"` DistrData distr.GenesisState `json:"distr"` @@ -40,11 +41,12 @@ type GenesisState struct { GenTxs []json.RawMessage `json:"gentxs"` } -func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mintData mint.GenesisState, +func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stakeData stake.GenesisState, mintData mint.GenesisState, distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState { return GenesisState{ Accounts: accounts, + AuthData: authData, StakeData: stakeData, MintData: mintData, DistrData: distrData, @@ -53,31 +55,39 @@ func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mi } } -// GenesisAccount doesn't need pubkey or sequence +// nolint type GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - Coins sdk.Coins `json:"coins"` + Address sdk.AccAddress `json:"address"` + Coins sdk.Coins `json:"coins"` + Sequence int64 `json:"sequence_number"` + AccountNumber int64 `json:"account_number"` } func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { return GenesisAccount{ - Address: acc.Address, - Coins: acc.Coins, + Address: acc.Address, + Coins: acc.Coins, + AccountNumber: acc.AccountNumber, + Sequence: acc.Sequence, } } func NewGenesisAccountI(acc auth.Account) GenesisAccount { return GenesisAccount{ - Address: acc.GetAddress(), - Coins: acc.GetCoins(), + Address: acc.GetAddress(), + Coins: acc.GetCoins(), + AccountNumber: acc.GetAccountNumber(), + Sequence: acc.GetSequence(), } } // convert GenesisAccount to auth.BaseAccount func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) { return &auth.BaseAccount{ - Address: ga.Address, - Coins: ga.Coins.Sort(), + Address: ga.Address, + Coins: ga.Coins.Sort(), + AccountNumber: ga.AccountNumber, + Sequence: ga.Sequence, } } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 431c5bc41..60befcc38 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -266,6 +267,103 @@ func TestFullGaiaSimulation(t *testing.T) { require.Nil(t, err) } +func TestGaiaImportExport(t *testing.T) { + if !enabled { + t.Skip("Skipping Gaia import/export simulation") + } + + // Setup Gaia application + var logger log.Logger + if verbose { + logger = log.TestingLogger() + } else { + logger = log.NewNopLogger() + } + var db dbm.DB + dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim") + db, _ = dbm.NewGoLevelDB("Simulation", dir) + defer func() { + db.Close() + os.RemoveAll(dir) + }() + app := NewGaiaApp(logger, db, nil) + require.Equal(t, "GaiaApp", app.Name()) + + // Run randomized simulation + err := simulation.SimulateFromSeed( + t, app.BaseApp, appStateFn, seed, + testAndRunTxs(app), + []simulation.RandSetup{}, + invariants(app), + numBlocks, + blockSize, + commit, + ) + if commit { + // for memdb: + // fmt.Println("Database Size", db.Stats()["database.size"]) + fmt.Println("GoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + } + require.Nil(t, err) + + fmt.Printf("Exporting genesis...\n") + + appState, _, err := app.ExportAppStateAndValidators() + if err != nil { + panic(err) + } + + fmt.Printf("Importing genesis...\n") + + newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2") + newDB, _ := dbm.NewGoLevelDB("Simulation-2", dir) + defer func() { + newDB.Close() + os.RemoveAll(newDir) + }() + newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil) + require.Equal(t, "GaiaApp", newApp.Name()) + request := abci.RequestInitChain{ + AppStateBytes: appState, + } + newApp.InitChain(request) + newApp.Commit() + + fmt.Printf("Comparing stores...\n") + ctxA := app.NewContext(true, abci.Header{}) + ctxB := newApp.NewContext(true, abci.Header{}) + type StoreKeysPrefixes struct { + A sdk.StoreKey + B sdk.StoreKey + Prefixes [][]byte + } + storeKeysPrefixes := []StoreKeysPrefixes{ + {app.keyMain, newApp.keyMain, [][]byte{}}, + {app.keyAccount, newApp.keyAccount, [][]byte{}}, + {app.keyStake, newApp.keyStake, [][]byte{stake.UnbondingQueueKey, stake.RedelegationQueueKey, stake.ValidatorQueueKey}}, // ordering may change but it doesn't matter + {app.keySlashing, newApp.keySlashing, [][]byte{}}, + {app.keyMint, newApp.keyMint, [][]byte{}}, + {app.keyDistr, newApp.keyDistr, [][]byte{}}, + {app.keyFeeCollection, newApp.keyFeeCollection, [][]byte{}}, + {app.keyParams, newApp.keyParams, [][]byte{}}, + {app.keyGov, newApp.keyGov, [][]byte{}}, + } + for _, storeKeysPrefix := range storeKeysPrefixes { + storeKeyA := storeKeysPrefix.A + storeKeyB := storeKeysPrefix.B + prefixes := storeKeysPrefix.Prefixes + storeA := ctxA.KVStore(storeKeyA) + storeB := ctxB.KVStore(storeKeyB) + kvA, kvB, count, equal := sdk.DiffKVStores(storeA, storeB, prefixes) + fmt.Printf("Compared %d key/value pairs between %s and %s\n", count, storeKeyA, storeKeyB) + require.True(t, equal, "unequal stores: %s / %s:\nstore A %s (%X) => %s (%X)\nstore B %s (%X) => %s (%X)", + storeKeyA, storeKeyB, kvA.Key, kvA.Key, kvA.Value, kvA.Value, kvB.Key, kvB.Key, kvB.Value, kvB.Value) + } + +} + // TODO: Make another test for the fuzzer itself, which just has noOp txs // and doesn't depend on gaia func TestAppStateDeterminism(t *testing.T) { diff --git a/cmd/gaia/init/collect.go b/cmd/gaia/init/collect.go index 97d9743a0..cdfc1688c 100644 --- a/cmd/gaia/init/collect.go +++ b/cmd/gaia/init/collect.go @@ -113,6 +113,6 @@ func genAppStateFromConfig( return } - err = WriteGenesisFile(genFile, initCfg.ChainID, nil, appState) + err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState) return } diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index f2a815a5c..a297caee2 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -70,7 +70,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob viper.GetBool(flagOverwrite)); err != nil { return err } - if err = WriteGenesisFile(genFile, chainID, nil, appState); err != nil { + if err = ExportGenesisFile(genFile, chainID, nil, appState); err != nil { return err } diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 42d8332d2..8bada583e 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -298,7 +298,7 @@ func collectGenFiles( genFile := config.GenesisFile() // overwrite each validator's genesis file to have a canonical genesis time - err = WriteGenesisFileWithTime(genFile, chainID, nil, appState, genTime) + err = ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime) if err != nil { return err } diff --git a/cmd/gaia/init/utils.go b/cmd/gaia/init/utils.go index 173843370..58edc9b2a 100644 --- a/cmd/gaia/init/utils.go +++ b/cmd/gaia/init/utils.go @@ -17,9 +17,9 @@ import ( "github.com/tendermint/tendermint/types" ) -// WriteGenesisFile creates and writes the genesis configuration to disk. An +// ExportGenesisFile creates and writes the genesis configuration to disk. An // error is returned if building or writing the configuration to file fails. -func WriteGenesisFile( +func ExportGenesisFile( genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage, ) error { @@ -36,9 +36,9 @@ func WriteGenesisFile( return genDoc.SaveAs(genFile) } -// WriteGenesisFileWithTime creates and writes the genesis configuration to disk. +// ExportGenesisFileWithTime creates and writes the genesis configuration to disk. // An error is returned if building or writing the configuration to file fails. -func WriteGenesisFileWithTime( +func ExportGenesisFileWithTime( genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage, genTime time.Time, ) error { diff --git a/docs/spec/staking/hooks.md b/docs/spec/staking/hooks.md index 7d24e32e6..3644155a6 100644 --- a/docs/spec/staking/hooks.md +++ b/docs/spec/staking/hooks.md @@ -7,7 +7,7 @@ The staking module allow for the following hooks to be registered with staking e type StakingHooks interface { OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes - OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted + OnValidatorRemoved(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator is deleted OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // called when a validator begins unbonding diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index 4f40a1450..f07fbd3ff 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -108,7 +108,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } fmt.Fprintf(os.Stderr, "%s\n", string(out)) - return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, + return gaiaInit.ExportGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) }, } diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index 12f5d8d29..e8ddd066d 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -186,8 +186,8 @@ func (app *DemocoinApp) ExportAppStateAndValidators() (appState json.RawMessage, genState := types.GenesisState{ Accounts: accounts, - POWGenesis: pow.WriteGenesis(ctx, app.powKeeper), - CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper), + POWGenesis: pow.ExportGenesis(ctx, app.powKeeper), + CoolGenesis: cool.ExportGenesis(ctx, app.coolKeeper), } appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index 2f48a64f3..d095b4c79 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -115,7 +115,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } fmt.Fprintf(os.Stderr, "%s\n", string(out)) - return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, + return gaiaInit.ExportGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) }, } diff --git a/examples/democoin/x/cool/keeper.go b/examples/democoin/x/cool/keeper.go index f805ca880..9f46b0209 100644 --- a/examples/democoin/x/cool/keeper.go +++ b/examples/democoin/x/cool/keeper.go @@ -49,8 +49,8 @@ func InitGenesis(ctx sdk.Context, k Keeper, data Genesis) error { return nil } -// WriteGenesis - output the genesis trend -func WriteGenesis(ctx sdk.Context, k Keeper) Genesis { +// ExportGenesis - output the genesis trend +func ExportGenesis(ctx sdk.Context, k Keeper) Genesis { trend := k.GetTrend(ctx) return Genesis{trend} } diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index 1eb40dfb2..904681382 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -37,7 +37,7 @@ func TestCoolKeeper(t *testing.T) { err := InitGenesis(ctx, keeper, Genesis{"icy"}) require.Nil(t, err) - genesis := WriteGenesis(ctx, keeper) + genesis := ExportGenesis(ctx, keeper) require.Nil(t, err) require.Equal(t, genesis, Genesis{"icy"}) diff --git a/examples/democoin/x/pow/keeper.go b/examples/democoin/x/pow/keeper.go index 38a0d93c6..6c3bfc4eb 100644 --- a/examples/democoin/x/pow/keeper.go +++ b/examples/democoin/x/pow/keeper.go @@ -43,8 +43,8 @@ func InitGenesis(ctx sdk.Context, k Keeper, genesis Genesis) error { return nil } -// WriteGenesis for the PoW module -func WriteGenesis(ctx sdk.Context, k Keeper) Genesis { +// ExportGenesis for the PoW module +func ExportGenesis(ctx sdk.Context, k Keeper) Genesis { difficulty, err := k.GetLastDifficulty(ctx) if err != nil { panic(err) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index 86ccbc8c0..c8d5406f9 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -41,7 +41,7 @@ func TestPowKeeperGetSet(t *testing.T) { err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)}) require.Nil(t, err) - genesis := WriteGenesis(ctx, keeper) + genesis := ExportGenesis(ctx, keeper) require.Nil(t, err) require.Equal(t, genesis, Genesis{uint64(1), uint64(0)}) diff --git a/types/stake.go b/types/stake.go index 7b10b17f8..38f1c0c9b 100644 --- a/types/stake.go +++ b/types/stake.go @@ -115,9 +115,9 @@ type DelegationSet interface { // event hooks for staking validator object type StakingHooks interface { - OnValidatorCreated(ctx Context, valAddr ValAddress) // Must be called when a validator is created - OnValidatorModified(ctx Context, valAddr ValAddress) // Must be called when a validator's state changes - OnValidatorRemoved(ctx Context, valAddr ValAddress) // Must be called when a validator is deleted + OnValidatorCreated(ctx Context, valAddr ValAddress) // Must be called when a validator is created + OnValidatorModified(ctx Context, valAddr ValAddress) // Must be called when a validator's state changes + OnValidatorRemoved(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is deleted OnValidatorBonded(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is bonded OnValidatorBeginUnbonding(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator begins unbonding diff --git a/types/store.go b/types/store.go index a90dc223d..4b6e79a76 100644 --- a/types/store.go +++ b/types/store.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "fmt" "io" @@ -176,6 +177,43 @@ func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { return kvs.ReverseIterator(prefix, PrefixEndBytes(prefix)) } +// Compare two KVstores, return either the first key/value pair +// at which they differ and whether or not they are equal, skipping +// value comparison for a set of provided prefixes +func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair, kvB cmn.KVPair, count int64, equal bool) { + iterA := a.Iterator(nil, nil) + iterB := b.Iterator(nil, nil) + count = int64(0) + for { + if !iterA.Valid() && !iterB.Valid() { + break + } + var kvA, kvB cmn.KVPair + if iterA.Valid() { + kvA = cmn.KVPair{Key: iterA.Key(), Value: iterA.Value()} + iterA.Next() + } + if iterB.Valid() { + kvB = cmn.KVPair{Key: iterB.Key(), Value: iterB.Value()} + iterB.Next() + } + compareValue := true + for _, prefix := range prefixesToSkip { + if bytes.Equal(kvA.Key[:len(prefix)], prefix) { + compareValue = false + } + } + if !bytes.Equal(kvA.Key, kvB.Key) { + return kvA, kvB, count, false + } + if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { + return kvA, kvB, count, false + } + count++ + } + return cmn.KVPair{}, cmn.KVPair{}, count, true +} + // CacheKVStore cache-wraps a KVStore. After calling .Write() on // the CacheKVStore, all previously created CacheKVStores on the // object expire. diff --git a/x/auth/genesis.go b/x/auth/genesis.go new file mode 100644 index 000000000..abc4fc3ae --- /dev/null +++ b/x/auth/genesis.go @@ -0,0 +1,33 @@ +package auth + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GenesisState - all auth state that must be provided at genesis +type GenesisState struct { + CollectedFees sdk.Coins `json:"collected_fees"` // collected fees +} + +// Create a new genesis state +func NewGenesisState(collectedFees sdk.Coins) GenesisState { + return GenesisState{ + CollectedFees: collectedFees, + } +} + +// Return a default genesis state +func DefaultGenesisState() GenesisState { + return NewGenesisState(sdk.Coins{}) +} + +// Init store state from genesis data +func InitGenesis(ctx sdk.Context, keeper FeeCollectionKeeper, data GenesisState) { + keeper.setCollectedFees(ctx, data.CollectedFees) +} + +// ExportGenesis returns a GenesisState for a given context and keeper +func ExportGenesis(ctx sdk.Context, keeper FeeCollectionKeeper) GenesisState { + collectedFees := keeper.GetCollectedFees(ctx) + return NewGenesisState(collectedFees) +} diff --git a/x/auth/mapper.go b/x/auth/keeper.go similarity index 100% rename from x/auth/mapper.go rename to x/auth/keeper.go diff --git a/x/auth/mapper_test.go b/x/auth/keeper_test.go similarity index 100% rename from x/auth/mapper_test.go rename to x/auth/keeper_test.go diff --git a/x/distribution/genesis.go b/x/distribution/genesis.go index 4ee5651a6..346808916 100644 --- a/x/distribution/genesis.go +++ b/x/distribution/genesis.go @@ -21,11 +21,12 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) { for _, dw := range data.DelegatorWithdrawInfos { keeper.SetDelegatorWithdrawAddr(ctx, dw.DelegatorAddr, dw.WithdrawAddr) } + keeper.SetPreviousProposerConsAddr(ctx, data.PreviousProposer) } -// WriteGenesis returns a GenesisState for a given context and keeper. The +// ExportGenesis returns a GenesisState for a given context and keeper. The // GenesisState will contain the pool, and validator/delegator distribution info's -func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { +func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { feePool := keeper.GetFeePool(ctx) communityTax := keeper.GetCommunityTax(ctx) baseProposerRewards := keeper.GetBaseProposerReward(ctx) @@ -33,6 +34,7 @@ func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { vdis := keeper.GetAllValidatorDistInfos(ctx) ddis := keeper.GetAllDelegationDistInfos(ctx) dwis := keeper.GetAllDelegatorWithdrawInfos(ctx) + pp := keeper.GetPreviousProposerConsAddr(ctx) return NewGenesisState(feePool, communityTax, baseProposerRewards, - bonusProposerRewards, vdis, ddis, dwis) + bonusProposerRewards, vdis, ddis, dwis, pp) } diff --git a/x/distribution/keeper/genesis.go b/x/distribution/keeper/genesis.go index 804ea5242..8e5a37abe 100644 --- a/x/distribution/keeper/genesis.go +++ b/x/distribution/keeper/genesis.go @@ -36,12 +36,12 @@ func (k Keeper) GetAllDelegationDistInfos(ctx sdk.Context) (ddis []types.Delegat // Get the set of all delegator-withdraw addresses with no limits, used during genesis dump func (k Keeper) GetAllDelegatorWithdrawInfos(ctx sdk.Context) (dwis []types.DelegatorWithdrawInfo) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, DelegationDistInfoKey) + iterator := sdk.KVStorePrefixIterator(store, DelegatorWithdrawInfoKey) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { dw := types.DelegatorWithdrawInfo{ - DelegatorAddr: sdk.AccAddress(iterator.Key()), + DelegatorAddr: GetDelegatorWithdrawInfoAddress(iterator.Key()), WithdrawAddr: sdk.AccAddress(iterator.Value()), } dwis = append(dwis, dw) diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index cdebaf93c..a4f4353fa 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -103,7 +103,7 @@ func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { h.k.onValidatorModified(ctx, valAddr) } -func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) { +func (h Hooks) OnValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) { h.k.onValidatorRemoved(ctx, valAddr) } func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { diff --git a/x/distribution/keeper/key.go b/x/distribution/keeper/key.go index 466d83c24..443d13079 100644 --- a/x/distribution/keeper/key.go +++ b/x/distribution/keeper/key.go @@ -44,3 +44,12 @@ func GetDelegationDistInfosKey(delAddr sdk.AccAddress) []byte { func GetDelegatorWithdrawAddrKey(delAddr sdk.AccAddress) []byte { return append(DelegatorWithdrawInfoKey, delAddr.Bytes()...) } + +// gets an address from a delegator's withdraw info key +func GetDelegatorWithdrawInfoAddress(key []byte) (delAddr sdk.AccAddress) { + addr := key[1:] + if len(addr) != sdk.AddrLen { + panic("unexpected key length") + } + return sdk.AccAddress(addr) +} diff --git a/x/distribution/types/genesis.go b/x/distribution/types/genesis.go index 528295e5c..0577af4d3 100644 --- a/x/distribution/types/genesis.go +++ b/x/distribution/types/genesis.go @@ -18,10 +18,11 @@ type GenesisState struct { ValidatorDistInfos []ValidatorDistInfo `json:"validator_dist_infos"` DelegationDistInfos []DelegationDistInfo `json:"delegator_dist_infos"` DelegatorWithdrawInfos []DelegatorWithdrawInfo `json:"delegator_withdraw_infos"` + PreviousProposer sdk.ConsAddress `json:"previous_proposer"` } func NewGenesisState(feePool FeePool, communityTax, baseProposerReward, bonusProposerReward sdk.Dec, - vdis []ValidatorDistInfo, ddis []DelegationDistInfo, dwis []DelegatorWithdrawInfo) GenesisState { + vdis []ValidatorDistInfo, ddis []DelegationDistInfo, dwis []DelegatorWithdrawInfo, pp sdk.ConsAddress) GenesisState { return GenesisState{ FeePool: feePool, @@ -31,6 +32,7 @@ func NewGenesisState(feePool FeePool, communityTax, baseProposerReward, bonusPro ValidatorDistInfos: vdis, DelegationDistInfos: ddis, DelegatorWithdrawInfos: dwis, + PreviousProposer: pp, } } diff --git a/x/gov/genesis.go b/x/gov/genesis.go index db35f68c8..1243bf559 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -8,10 +8,25 @@ import ( // GenesisState - all staking state that must be provided at genesis type GenesisState struct { - StartingProposalID uint64 `json:"starting_proposalID"` - DepositParams DepositParams `json:"deposit_params"` - VotingParams VotingParams `json:"voting_params"` - TallyParams TallyParams `json:"tally_params"` + StartingProposalID uint64 `json:"starting_proposal_id"` + Deposits []DepositWithMetadata `json:"deposits"` + Votes []VoteWithMetadata `json:"votes"` + Proposals []Proposal `json:"proposals"` + DepositParams DepositParams `json:"deposit_params"` + VotingParams VotingParams `json:"voting_params"` + TallyParams TallyParams `json:"tally_params"` +} + +// DepositWithMetadata (just for genesis) +type DepositWithMetadata struct { + ProposalID uint64 `json:"proposal_id"` + Deposit Deposit `json:"deposit"` +} + +// VoteWithMetadata (just for genesis) +type VoteWithMetadata struct { + ProposalID uint64 `json:"proposal_id"` + Vote Vote `json:"vote"` } func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParams, tp TallyParams) GenesisState { @@ -52,17 +67,47 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { k.setDepositParams(ctx, data.DepositParams) k.setVotingParams(ctx, data.VotingParams) k.setTallyParams(ctx, data.TallyParams) + for _, deposit := range data.Deposits { + k.setDeposit(ctx, deposit.ProposalID, deposit.Deposit.Depositer, deposit.Deposit) + } + for _, vote := range data.Votes { + k.setVote(ctx, vote.ProposalID, vote.Vote.Voter, vote.Vote) + } + for _, proposal := range data.Proposals { + k.SetProposal(ctx, proposal) + } } -// WriteGenesis - output genesis parameters -func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState { - startingProposalID, _ := k.getNewProposalID(ctx) +// ExportGenesis - output genesis parameters +func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { + startingProposalID, _ := k.peekCurrentProposalID(ctx) depositParams := k.GetDepositParams(ctx) votingParams := k.GetVotingParams(ctx) tallyParams := k.GetTallyParams(ctx) + var deposits []DepositWithMetadata + var votes []VoteWithMetadata + proposals := k.GetProposalsFiltered(ctx, nil, nil, StatusNil, 0) + for _, proposal := range proposals { + proposalID := proposal.GetProposalID() + depositsIterator := k.GetDeposits(ctx, proposalID) + for ; depositsIterator.Valid(); depositsIterator.Next() { + var deposit Deposit + k.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) + deposits = append(deposits, DepositWithMetadata{proposalID, deposit}) + } + votesIterator := k.GetVotes(ctx, proposalID) + for ; votesIterator.Valid(); votesIterator.Next() { + var vote Vote + k.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) + votes = append(votes, VoteWithMetadata{proposalID, vote}) + } + } return GenesisState{ StartingProposalID: startingProposalID, + Deposits: deposits, + Votes: votes, + Proposals: proposals, DepositParams: depositParams, VotingParams: votingParams, TallyParams: tallyParams, diff --git a/x/gov/handler.go b/x/gov/handler.go index 279c4cc6e..180f7a21a 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -107,8 +107,8 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { var proposalID uint64 keeper.cdc.MustUnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID) inactiveProposal := keeper.GetProposal(ctx, proposalID) - keeper.RefundDeposits(ctx, proposalID) keeper.DeleteProposal(ctx, proposalID) + keeper.DeleteDeposits(ctx, proposalID) // delete any associated deposits (burned) resTags = resTags.AppendTag(tags.Action, tags.ActionProposalDropped) resTags = resTags.AppendTag(tags.ProposalID, []byte(string(proposalID))) diff --git a/x/mint/genesis.go b/x/mint/genesis.go index 561768573..ce375d71e 100644 --- a/x/mint/genesis.go +++ b/x/mint/genesis.go @@ -31,9 +31,9 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { keeper.SetParams(ctx, data.Params) } -// WriteGenesis returns a GenesisState for a given context and keeper. The +// ExportGenesis returns a GenesisState for a given context and keeper. The // GenesisState will contain the pool, and validator/delegator distribution info's -func WriteGenesis(ctx sdk.Context, keeper Keeper) GenesisState { +func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { minter := keeper.GetMinter(ctx) params := keeper.GetParams(ctx) diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go index 10af155d6..614bf41eb 100644 --- a/x/slashing/genesis.go +++ b/x/slashing/genesis.go @@ -7,13 +7,25 @@ import ( // GenesisState - all slashing state that must be provided at genesis type GenesisState struct { - Params Params + Params Params + SigningInfos map[string]ValidatorSigningInfo + MissedBlocks map[string][]MissedBlock + SlashingPeriods []ValidatorSlashingPeriod +} + +// MissedBlock +type MissedBlock struct { + Index int64 `json:"index"` + Missed bool `json:"missed"` } // HubDefaultGenesisState - default GenesisState used by Cosmos Hub func DefaultGenesisState() GenesisState { return GenesisState{ - Params: DefaultParams(), + Params: DefaultParams(), + SigningInfos: make(map[string]ValidatorSigningInfo), + MissedBlocks: make(map[string][]MissedBlock), + SlashingPeriods: []ValidatorSlashingPeriod{}, } } @@ -24,5 +36,64 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState, sdata types. keeper.addPubkey(ctx, validator.GetConsPubKey()) } + for addr, info := range data.SigningInfos { + address, err := sdk.ConsAddressFromBech32(addr) + if err != nil { + panic(err) + } + keeper.setValidatorSigningInfo(ctx, address, info) + } + + for addr, array := range data.MissedBlocks { + address, err := sdk.ConsAddressFromBech32(addr) + if err != nil { + panic(err) + } + for _, missed := range array { + keeper.setValidatorMissedBlockBitArray(ctx, address, missed.Index, missed.Missed) + } + } + + for _, slashingPeriod := range data.SlashingPeriods { + keeper.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod) + } + keeper.paramspace.SetParamSet(ctx, &data.Params) } + +// ExportGenesis writes the current store values +// to a genesis file, which can be imported again +// with InitGenesis +func ExportGenesis(ctx sdk.Context, keeper Keeper) (data GenesisState) { + var params Params + keeper.paramspace.GetParamSet(ctx, ¶ms) + + signingInfos := make(map[string]ValidatorSigningInfo) + missedBlocks := make(map[string][]MissedBlock) + keeper.iterateValidatorSigningInfos(ctx, func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool) { + bechAddr := address.String() + signingInfos[bechAddr] = info + array := []MissedBlock{} + + keeper.iterateValidatorMissedBlockBitArray(ctx, address, func(index int64, missed bool) (stop bool) { + array = append(array, MissedBlock{index, missed}) + return false + }) + missedBlocks[bechAddr] = array + + return false + }) + + slashingPeriods := []ValidatorSlashingPeriod{} + keeper.iterateValidatorSlashingPeriods(ctx, func(slashingPeriod ValidatorSlashingPeriod) (stop bool) { + slashingPeriods = append(slashingPeriods, slashingPeriod) + return false + }) + + return GenesisState{ + Params: params, + SigningInfos: signingInfos, + MissedBlocks: missedBlocks, + SlashingPeriods: slashingPeriods, + } +} diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index 10c16d199..e09f6c566 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -3,6 +3,8 @@ package slashing import ( "time" + "github.com/tendermint/tendermint/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -36,6 +38,17 @@ func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddre k.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod) } +// When a validator is created, add the address-pubkey relation. +func (k Keeper) onValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { + validator := k.validatorSet.Validator(ctx, valAddr) + k.addPubkey(ctx, validator.GetConsPubKey()) +} + +// When a validator is removed, delete the address-pubkey relation. +func (k Keeper) onValidatorRemoved(ctx sdk.Context, address sdk.ConsAddress) { + k.deleteAddrPubkeyRelation(ctx, crypto.Address(address)) +} + //_________________________________________________________________________________________ // Wrapper struct @@ -60,12 +73,20 @@ func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddre h.k.onValidatorBeginUnbonding(ctx, consAddr, valAddr) } +// Implements sdk.ValidatorHooks +func (h Hooks) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, _ sdk.ValAddress) { + h.k.onValidatorRemoved(ctx, consAddr) +} + +// Implements sdk.ValidatorHooks +func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { + h.k.onValidatorCreated(ctx, valAddr) +} + // nolint - unused hooks func (h Hooks) OnValidatorPowerDidChange(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { } -func (h Hooks) OnValidatorCreated(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) OnValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} -func (h Hooks) OnValidatorRemoved(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) OnDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} func (h Hooks) OnDelegationSharesModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} func (h Hooks) OnDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 4a52ddcb6..fe1531f22 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -4,13 +4,10 @@ import ( "fmt" "time" - tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params" stake "github.com/cosmos/cosmos-sdk/x/stake/types" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" ) @@ -174,19 +171,6 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p k.setValidatorSigningInfo(ctx, consAddr, signInfo) } -// AddValidators adds the validators to the keepers validator addr to pubkey mapping. -func (k Keeper) AddValidators(ctx sdk.Context, vals []abci.ValidatorUpdate) { - for i := 0; i < len(vals); i++ { - val := vals[i] - pubkey, err := tmtypes.PB2TM.PubKey(val.PubKey) - if err != nil { - panic(err) - } - k.addPubkey(ctx, pubkey) - } -} - -// TODO: Make a method to remove the pubkey from the map when a validator is unbonded. func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) { addr := pubkey.Address() k.setAddrPubkeyRelation(ctx, addr, pubkey) diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index caf1cf3da..94251ded3 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -34,8 +34,7 @@ func TestHandleDoubleSign(t *testing.T) { operatorAddr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, operatorAddr).GetPower())) @@ -75,9 +74,8 @@ func TestSlashingPeriodCap(t *testing.T) { valConsPubKey, valConsAddr := pks[0], pks[0].Address() got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, valConsPubKey, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) + stake.EndBlocker(ctx, sk) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, operatorAddr).GetPower())) @@ -141,8 +139,7 @@ func TestHandleAbsentValidator(t *testing.T) { slh := NewHandler(keeper) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) // will exist since the validator has been bonded @@ -298,8 +295,7 @@ func TestHandleNewValidator(t *testing.T) { // Validator created got := sh(ctx, NewTestMsgCreateValidator(addr, val, sdk.NewInt(amt))) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}}) require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, addr).GetPower()) @@ -333,8 +329,7 @@ func TestHandleAlreadyJailed(t *testing.T) { sh := stake.NewHandler(sk) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) // 1000 first blocks OK height := int64(0) @@ -386,8 +381,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { sh := stake.NewHandler(sk) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) // 100 first blocks OK height := int64(0) @@ -400,9 +394,8 @@ func TestValidatorDippingInAndOut(t *testing.T) { newAmt := int64(101) got = sh(ctx, NewTestMsgCreateValidator(addrs[1], pks[1], sdk.NewInt(newAmt))) require.True(t, got.IsOK()) - validatorUpdates = stake.EndBlocker(ctx, sk) + validatorUpdates := stake.EndBlocker(ctx, sk) require.Equal(t, 2, len(validatorUpdates)) - keeper.AddValidators(ctx, validatorUpdates) validator, _ := sk.GetValidator(ctx, addr) require.Equal(t, sdk.Unbonding, validator.Status) diff --git a/x/slashing/keys.go b/x/slashing/keys.go index 8f4fecc6c..750e8825f 100644 --- a/x/slashing/keys.go +++ b/x/slashing/keys.go @@ -20,6 +20,15 @@ func GetValidatorSigningInfoKey(v sdk.ConsAddress) []byte { return append(ValidatorSigningInfoKey, v.Bytes()...) } +// extract the address from a validator signing info key +func GetValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) { + addr := key[1:] + if len(addr) != sdk.AddrLen { + panic("unexpected key length") + } + return sdk.ConsAddress(addr) +} + // stored by *Tendermint* address (not operator address) func GetValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { return append(ValidatorMissedBlockBitArrayKey, v.Bytes()...) diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 6479be928..77c437174 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -20,6 +20,21 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress return } +// Stored by *validator* address (not operator address) +func (k Keeper) iterateValidatorSigningInfos(ctx sdk.Context, handler func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, ValidatorSigningInfoKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + address := GetValidatorSigningInfoAddress(iter.Key()) + var info ValidatorSigningInfo + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &info) + if handler(address, info) { + break + } + } +} + // Stored by *validator* address (not operator address) func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info ValidatorSigningInfo) { store := ctx.KVStore(k.storeKey) @@ -40,6 +55,24 @@ func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.Con return } +// Stored by *validator* address (not operator address) +func (k Keeper) iterateValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, handler func(index int64, missed bool) (stop bool)) { + store := ctx.KVStore(k.storeKey) + index := int64(0) + // Array may be sparse + for ; index < k.SignedBlocksWindow(ctx); index++ { + var missed bool + bz := store.Get(GetValidatorMissedBlockBitArrayKey(address, index)) + if bz == nil { + continue + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &missed) + if handler(index, missed) { + break + } + } +} + // Stored by *validator* address (not operator address) func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) { store := ctx.KVStore(k.storeKey) diff --git a/x/slashing/slashing_period.go b/x/slashing/slashing_period.go index 026d141a2..4caf5d7c9 100644 --- a/x/slashing/slashing_period.go +++ b/x/slashing/slashing_period.go @@ -51,6 +51,21 @@ func (k Keeper) getValidatorSlashingPeriodForHeight(ctx sdk.Context, address sdk return } +// Iterate over all slashing periods in the store, calling on each +// decode slashing period a provided handler function +// Stop if the provided handler function returns true +func (k Keeper) iterateValidatorSlashingPeriods(ctx sdk.Context, handler func(slashingPeriod ValidatorSlashingPeriod) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, ValidatorSlashingPeriodKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + slashingPeriod := k.unmarshalSlashingPeriodKeyValue(iter.Key(), iter.Value()) + if handler(slashingPeriod) { + break + } + } +} + // Stored by validator Tendermint address (not operator address) // This function sets a validator slashing period for a particular validator, // start height, end height, and current slashed-so-far total, or updates diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 2f1113aa1..239ae13d6 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -91,7 +91,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s sk.SetHooks(keeper.Hooks()) require.NotPanics(t, func() { - InitGenesis(ctx, keeper, GenesisState{defaults}, genesis) + InitGenesis(ctx, keeper, GenesisState{defaults, nil, nil, nil}, genesis) }) return ctx, ck, sk, paramstore, keeper diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index a2a2d9f0f..c6590c94e 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -19,8 +19,7 @@ func TestBeginBlocker(t *testing.T) { // bond the validator got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(addr, pk, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 63b038613..d44055ea8 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -2,13 +2,13 @@ package stake import ( "fmt" + "sort" abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/pkg/errors" ) // InitGenesis sets the pool and parameters for the provided keeper and @@ -26,22 +26,33 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ keeper.SetPool(ctx, data.Pool) keeper.SetParams(ctx, data.Params) + keeper.SetIntraTxCounter(ctx, data.IntraTxCounter) + keeper.SetLastTotalPower(ctx, data.LastTotalPower) + + // We only need to set this if we're starting from a list of validators, not a state export + setBondIntraTxCounter := true + for _, validator := range data.Validators { + if validator.BondIntraTxCounter != 0 { + setBondIntraTxCounter = false + } + } for i, validator := range data.Validators { - validator.BondIntraTxCounter = int16(i) // set the intra-tx counter to the order the validators are presented + // set the intra-tx counter to the order the validators are presented, if necessary + if setBondIntraTxCounter { + validator.BondIntraTxCounter = int16(i) + } keeper.SetValidator(ctx, validator) - if validator.Tokens.IsZero() { - return res, errors.Errorf("genesis validator cannot have zero pool shares, validator: %v", validator) - } - if validator.DelegatorShares.IsZero() { - return res, errors.Errorf("genesis validator cannot have zero delegator shares, validator: %v", validator) - } - // Manually set indices for the first time keeper.SetValidatorByConsAddr(ctx, validator) keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool) keeper.OnValidatorCreated(ctx, validator.OperatorAddr) + + // Set timeslice if necessary + if validator.Status == sdk.Unbonding { + keeper.InsertValidatorQueue(ctx, validator) + } } for _, delegation := range data.Bonds { @@ -49,24 +60,56 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ keeper.OnDelegationCreated(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr) } + sort.SliceStable(data.UnbondingDelegations[:], func(i, j int) bool { + return data.UnbondingDelegations[i].CreationHeight < data.UnbondingDelegations[j].CreationHeight + }) + for _, ubd := range data.UnbondingDelegations { + keeper.SetUnbondingDelegation(ctx, ubd) + keeper.InsertUnbondingQueue(ctx, ubd) + } + + sort.SliceStable(data.Redelegations[:], func(i, j int) bool { + return data.Redelegations[i].CreationHeight < data.Redelegations[j].CreationHeight + }) + for _, red := range data.Redelegations { + keeper.SetRedelegation(ctx, red) + keeper.InsertRedelegationQueue(ctx, red) + } + res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) return } -// WriteGenesis returns a GenesisState for a given context and keeper. The +// ExportGenesis returns a GenesisState for a given context and keeper. The // GenesisState will contain the pool, params, validators, and bonds found in // the keeper. -func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { +func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { pool := keeper.GetPool(ctx) params := keeper.GetParams(ctx) + intraTxCounter := keeper.GetIntraTxCounter(ctx) + lastTotalPower := keeper.GetLastTotalPower(ctx) validators := keeper.GetAllValidators(ctx) bonds := keeper.GetAllDelegations(ctx) + var unbondingDelegations []types.UnbondingDelegation + keeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) (stop bool) { + unbondingDelegations = append(unbondingDelegations, ubd) + return false + }) + var redelegations []types.Redelegation + keeper.IterateRedelegations(ctx, func(_ int64, red types.Redelegation) (stop bool) { + redelegations = append(redelegations, red) + return false + }) return types.GenesisState{ - Pool: pool, - Params: params, - Validators: validators, - Bonds: bonds, + Pool: pool, + Params: params, + IntraTxCounter: intraTxCounter, + LastTotalPower: lastTotalPower, + Validators: validators, + Bonds: bonds, + UnbondingDelegations: unbondingDelegations, + Redelegations: redelegations, } } @@ -118,11 +161,8 @@ func validateGenesisStateValidators(validators []types.Validator) (err error) { if val.Jailed && val.Status == sdk.Bonded { return fmt.Errorf("validator is bonded and jailed in genesis state: moniker %v, Address %v", val.Description.Moniker, val.ConsAddress()) } - if val.Tokens.IsZero() { - return fmt.Errorf("genesis validator cannot have zero pool shares, validator: %v", val) - } - if val.DelegatorShares.IsZero() { - return fmt.Errorf("genesis validator cannot have zero delegator shares, validator: %v", val) + if val.DelegatorShares.IsZero() && val.Status != sdk.Unbonding { + return fmt.Errorf("bonded/unbonded genesis validator cannot have zero delegator shares, validator: %v", val) } addrMap[strKey] = true } diff --git a/x/stake/genesis_test.go b/x/stake/genesis_test.go index 7ee16a453..3f7295a7a 100644 --- a/x/stake/genesis_test.go +++ b/x/stake/genesis_test.go @@ -22,29 +22,28 @@ func TestInitGenesis(t *testing.T) { pool.BondedTokens = sdk.NewDec(2) params := keeper.GetParams(ctx) + validators := make([]Validator, 2) var delegations []Delegation - validators := []Validator{ - NewValidator(sdk.ValAddress(keep.Addrs[0]), keep.PKs[0], Description{Moniker: "hoop"}), - NewValidator(sdk.ValAddress(keep.Addrs[1]), keep.PKs[1], Description{Moniker: "bloop"}), - } - genesisState := types.NewGenesisState(pool, params, validators, delegations) - _, err := InitGenesis(ctx, keeper, genesisState) - require.Error(t, err) - // initialize the validators + validators[0].OperatorAddr = sdk.ValAddress(keep.Addrs[0]) + validators[0].ConsPubKey = keep.PKs[0] + validators[0].Description = Description{Moniker: "hoop"} validators[0].Status = sdk.Bonded validators[0].Tokens = sdk.OneDec() validators[0].DelegatorShares = sdk.OneDec() + validators[1].OperatorAddr = sdk.ValAddress(keep.Addrs[1]) + validators[1].ConsPubKey = keep.PKs[1] + validators[1].Description = Description{Moniker: "bloop"} validators[1].Status = sdk.Bonded validators[1].Tokens = sdk.OneDec() validators[1].DelegatorShares = sdk.OneDec() - genesisState = types.NewGenesisState(pool, params, validators, delegations) + genesisState := types.NewGenesisState(pool, params, validators, delegations) vals, err := InitGenesis(ctx, keeper, genesisState) require.NoError(t, err) - actualGenesis := WriteGenesis(ctx, keeper) + actualGenesis := ExportGenesis(ctx, keeper) require.Equal(t, genesisState.Pool, actualGenesis.Pool) require.Equal(t, genesisState.Params, actualGenesis.Params) require.Equal(t, genesisState.Bonds, actualGenesis.Bonds) @@ -126,10 +125,6 @@ func TestValidateGenesis(t *testing.T) { (*data).Validators = genValidators1 (*data).Validators = append((*data).Validators, genValidators1[0]) }, true}, - {"no pool shares", func(data *types.GenesisState) { - (*data).Validators = genValidators1 - (*data).Validators[0].Tokens = sdk.ZeroDec() - }, true}, {"no delegator shares", func(data *types.GenesisState) { (*data).Validators = genValidators1 (*data).Validators[0].DelegatorShares = sdk.ZeroDec() diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index fbb62dcbf..d535419e1 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -283,6 +283,21 @@ func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) { store.Set(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr), []byte{}) } +// iterate through all redelegations +func (k Keeper) IterateRedelegations(ctx sdk.Context, fn func(index int64, red types.Redelegation) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, RedelegationKey) + defer iterator.Close() + + for i := int64(0); iterator.Valid(); iterator.Next() { + red := types.MustUnmarshalRED(k.cdc, iterator.Key(), iterator.Value()) + if stop := fn(i, red); stop { + break + } + i++ + } +} + // remove a redelegation object and associated index func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) { store := ctx.KVStore(k.storeKey) diff --git a/x/stake/keeper/hooks.go b/x/stake/keeper/hooks.go index 4a8496ddd..74e830490 100644 --- a/x/stake/keeper/hooks.go +++ b/x/stake/keeper/hooks.go @@ -17,9 +17,9 @@ func (k Keeper) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { } } -func (k Keeper) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) { +func (k Keeper) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { if k.hooks != nil { - k.hooks.OnValidatorRemoved(ctx, valAddr) + k.hooks.OnValidatorRemoved(ctx, consAddr, valAddr) } } diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index 307270c16..3f98f08c5 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -191,11 +191,13 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. validator, pool = validator.UpdateStatus(pool, sdk.Bonded) k.SetPool(ctx, pool) - // save the now bonded validator record to the three referenced stores + // save the now bonded validator record to the two referenced stores k.SetValidator(ctx, validator) - k.SetValidatorByPowerIndex(ctx, validator, pool) + // delete from queue if present + k.DeleteValidatorQueue(ctx, validator) + // call the bond hook if present if k.hooks != nil { k.hooks.OnValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddr) @@ -224,9 +226,8 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) validator.UnbondingHeight = ctx.BlockHeader().Height - // save the now unbonded validator record + // save the now unbonded validator record and power index k.SetValidator(ctx, validator) - k.SetValidatorByPowerIndex(ctx, validator, pool) // Adds to unbonding validator queue diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 4646480ac..c7919537c 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "container/list" "fmt" "time" @@ -201,6 +202,11 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) store.Delete(GetValidatorsByPowerIndexKey(validator, pool)) + // call hook if present + if k.hooks != nil { + k.hooks.OnValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddr) + } + } //___________________________________________________________________________ @@ -320,6 +326,12 @@ func (k Keeper) SetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time, store.Set(GetValidatorQueueTimeKey(timestamp), bz) } +// Deletes a specific validator queue timeslice. +func (k Keeper) DeleteValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) { + store := ctx.KVStore(k.storeKey) + store.Delete(GetValidatorQueueTimeKey(timestamp)) +} + // Insert an validator address to the appropriate timeslice in the validator queue func (k Keeper) InsertValidatorQueue(ctx sdk.Context, val types.Validator) { timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingMinTime) @@ -331,6 +343,22 @@ func (k Keeper) InsertValidatorQueue(ctx sdk.Context, val types.Validator) { } } +// Delete a validator address from the validator queue +func (k Keeper) DeleteValidatorQueue(ctx sdk.Context, val types.Validator) { + timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingMinTime) + newTimeSlice := []sdk.ValAddress{} + for _, addr := range timeSlice { + if !bytes.Equal(addr, val.OperatorAddr) { + newTimeSlice = append(newTimeSlice, addr) + } + } + if len(newTimeSlice) == 0 { + k.DeleteValidatorQueueTimeSlice(ctx, val.UnbondingMinTime) + } else { + k.SetValidatorQueueTimeSlice(ctx, val.UnbondingMinTime, newTimeSlice) + } +} + // Returns all the validator queue timeslices from time 0 until endTime func (k Keeper) ValidatorQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { store := ctx.KVStore(k.storeKey) @@ -358,9 +386,12 @@ func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) for _, valAddr := range timeslice { val, found := k.GetValidator(ctx, valAddr) - if !found || val.GetStatus() != sdk.Unbonding { + if !found { continue } + if val.GetStatus() != sdk.Unbonding { + panic("unexpected validator in unbonding queue, status was not unbonding") + } k.unbondingToUnbonded(ctx, val) if val.GetDelegatorShares().IsZero() { k.RemoveValidator(ctx, val.OperatorAddr) diff --git a/x/stake/stake.go b/x/stake/stake.go index 9d7617722..87087e59c 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -57,6 +57,9 @@ var ( GetREDsToValDstIndexKey = keeper.GetREDsToValDstIndexKey GetREDsByDelToValDstIndexKey = keeper.GetREDsByDelToValDstIndexKey TestingUpdateValidator = keeper.TestingUpdateValidator + UnbondingQueueKey = keeper.UnbondingQueueKey + RedelegationQueueKey = keeper.RedelegationQueueKey + ValidatorQueueKey = keeper.ValidatorQueueKey DefaultParamspace = keeper.DefaultParamspace KeyUnbondingTime = types.KeyUnbondingTime diff --git a/x/stake/types/genesis.go b/x/stake/types/genesis.go index d08c6b899..f1673a376 100644 --- a/x/stake/types/genesis.go +++ b/x/stake/types/genesis.go @@ -1,11 +1,19 @@ package types +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + // GenesisState - all staking state that must be provided at genesis type GenesisState struct { - Pool Pool `json:"pool"` - Params Params `json:"params"` - Validators []Validator `json:"validators"` - Bonds []Delegation `json:"bonds"` + Pool Pool `json:"pool"` + Params Params `json:"params"` + IntraTxCounter int16 `json:"intra_tx_counter"` + LastTotalPower sdk.Int `json:"last_total_power"` + Validators []Validator `json:"validators"` + Bonds []Delegation `json:"bonds"` + UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []Redelegation `json:"redelegations"` } func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState { From 02da39789923c948a4b91b34dda275d46381cc90 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 9 Nov 2018 01:41:57 +0100 Subject: [PATCH 2/3] Merge 0.26.0 back to develop (#2718) * PENDING => CHANGELOG * Linkify changelog * Merge PR #2716: Temporarily disable gaia lite insecure mode * TODO: need to update CHANGELOG w/ import-export PR #2690 --- CHANGELOG.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++ PENDING.md | 24 +------------------- client/lcd/root.go | 37 ++++++++++++++++++++++--------- 3 files changed, 83 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e01163c..8be464b40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,60 @@ # Changelog +## 0.26.0 + +BREAKING CHANGES + +* Gaia + * [gaiad init] [\#2602](https://github.com/cosmos/cosmos-sdk/issues/2602) New genesis workflow + +* SDK + * [simulation] [\#2665](https://github.com/cosmos/cosmos-sdk/issues/2665) only argument to simulation.Invariant is now app + +* Tendermint + * Upgrade to version 0.26.0 + +FEATURES + +* Gaia CLI (`gaiacli`) + * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations + * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations + * [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on. + * [cli] [\#2558](https://github.com/cosmos/cosmos-sdk/issues/2558) Rename --print-sigs to --validate-signatures. It now performs a complete set of sanity checks and reports to the user. Also added --print-signature-only to print the signature only, not the whole transaction. + +* SDK + * [\#1336](https://github.com/cosmos/cosmos-sdk/issues/1336) Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. + +IMPROVEMENTS + +* Gaia + * [\#2637](https://github.com/cosmos/cosmos-sdk/issues/2637) [x/gov] Switched inactive and active proposal queues to an iterator based queue + +* SDK + * [\#2573](https://github.com/cosmos/cosmos-sdk/issues/2573) [x/distribution] add accum invariance + * [\#2556](https://github.com/cosmos/cosmos-sdk/issues/2556) [x/mock/simulation] Fix debugging output + * [\#2396](https://github.com/cosmos/cosmos-sdk/issues/2396) [x/mock/simulation] Change parameters to get more slashes + * [\#2617](https://github.com/cosmos/cosmos-sdk/issues/2617) [x/mock/simulation] Randomize all genesis parameters + * [\#2669](https://github.com/cosmos/cosmos-sdk/issues/2669) [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. + * [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) [x/mock/simulation] Use a transition matrix for block size + * [\#2660](https://github.com/cosmos/cosmos-sdk/issues/2660) [x/mock/simulation] Staking transactions get tested far more frequently + * [\#2610](https://github.com/cosmos/cosmos-sdk/issues/2610) [x/stake] Block redelegation to and from the same validator + * [\#2652](https://github.com/cosmos/cosmos-sdk/issues/2652) [x/auth] Add benchmark for get and set account + * [\#2685](https://github.com/cosmos/cosmos-sdk/issues/2685) [store] Add general merkle absence proof (also for empty substores) + * [\#2708](https://github.com/cosmos/cosmos-sdk/issues/2708) [store] Disallow setting nil values + +BUG FIXES + +* Gaia + * [\#2670](https://github.com/cosmos/cosmos-sdk/issues/2670) [x/stake] fixed incorrect `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + * [\#2691](https://github.com/cosmos/cosmos-sdk/issues/2691) Fix local testnet creation by using a single canonical genesis time + +* SDK + * [\#2625](https://github.com/cosmos/cosmos-sdk/issues/2625) [x/gov] fix AppendTag function usage error + * [\#2677](https://github.com/cosmos/cosmos-sdk/issues/2677) [x/stake, x/distribution] various staking/distribution fixes as found by the simulator + * [\#2674](https://github.com/cosmos/cosmos-sdk/issues/2674) [types] Fix coin.IsLT() impl, coins.IsLT() impl, and renamed coins.Is\* to coins.IsAll\* (see [\#2686](https://github.com/cosmos/cosmos-sdk/issues/2686)) + * [\#2711](https://github.com/cosmos/cosmos-sdk/issues/2711) [x/stake] Add commission data to `MsgCreateValidator` signature bytes. + * Temporarily disable insecure mode for Gaia Lite + ## 0.25.0 *October 24th, 2018* diff --git a/PENDING.md b/PENDING.md index 1d5752fc2..0b56d5333 100644 --- a/PENDING.md +++ b/PENDING.md @@ -7,23 +7,17 @@ BREAKING CHANGES * Gaia CLI (`gaiacli`) * Gaia - * [gaiad init] \#2602 New genesis workflow * SDK - * [simulation] \#2665 only argument to simulation.Invariant is now app * Tendermint - * Upgrade to version 0.26.0 + FEATURES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) - * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations - * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations - * [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on. - * [cli] [\#2558](https://github.com/cosmos/cosmos-sdk/issues/2558) Rename --print-sigs to --validate-signatures. It now performs a complete set of sanity checks and reports to the user. Also added --print-signature-only to print the signature only, not the whole transaction. * Gaia @@ -40,20 +34,8 @@ IMPROVEMENTS * Gaia CLI (`gaiacli`) * Gaia - - #2637 [x/gov] Switched inactive and active proposal queues to an iterator based queue * SDK - - \#2573 [x/distribution] add accum invariance - - \#2556 [x/mock/simulation] Fix debugging output - - \#2396 [x/mock/simulation] Change parameters to get more slashes - - \#2617 [x/mock/simulation] Randomize all genesis parameters - - \#2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. - - \#1924 [x/mock/simulation] Use a transition matrix for block size - - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - - \#2610 [x/stake] Block redelegation to and from the same validator - - \#2652 [x/auth] Add benchmark for get and set account - - \#2685 [store] Add general merkle absence proof (also for empty substores) - - \#2708 [store] Disallow setting nil values * Tendermint @@ -70,9 +52,5 @@ BUG FIXES - \#2691 Fix local testnet creation by using a single canonical genesis time * SDK - - \#2625 [x/gov] fix AppendTag function usage error - - \#2677 [x/stake, x/distribution] various staking/distribution fixes as found by the simulator - - \#2674 [types] Fix coin.IsLT() impl, coins.IsLT() impl, and renamed coins.Is\* to coins.IsAll\* (see \#2686) - - \#2711 [x/stake] Add commission data to `MsgCreateValidator` signature bytes. * Tendermint diff --git a/client/lcd/root.go b/client/lcd/root.go index c6e7917e7..8366b6114 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -2,6 +2,11 @@ package lcd import ( "errors" + "fmt" + "net" + "net/http" + "os" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" @@ -20,9 +25,6 @@ import ( "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/log" tmserver "github.com/tendermint/tendermint/rpc/lib/server" - "net" - "net/http" - "os" ) const ( @@ -46,7 +48,9 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) (err error) { listenAddr := viper.GetString(flagListenAddr) handler := createHandler(cdc) + registerSwaggerUI(handler) + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") maxOpen := viper.GetInt(flagMaxOpenConnections) sslHosts := viper.GetString(flagSSLHosts) @@ -62,14 +66,20 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { }) var cleanupFunc func() + + // TODO: re-enable insecure mode once #2715 has been addressed if viper.GetBool(flagInsecure) { - listener, err = tmserver.StartHTTPServer( - listenAddr, handler, logger, - tmserver.Config{MaxOpenConnections: maxOpen}, + fmt.Println( + "Insecure mode is temporarily disabled, please locally generate an " + + "SSL certificate to test. Support will be re-enabled soon!", ) - if err != nil { - return - } + // listener, err = tmserver.StartHTTPServer( + // listenAddr, handler, logger, + // tmserver.Config{MaxOpenConnections: maxOpen}, + // ) + // if err != nil { + // return + // } } else { if certFile != "" { // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 @@ -77,6 +87,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { if err != nil { return err } + // cert/key pair is provided, read the fingerprint fingerprint, err = fingerprintFromFile(certFile) if err != nil { @@ -88,10 +99,12 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { if err != nil { return err } + cleanupFunc = func() { os.Remove(certFile) os.Remove(keyFile) } + defer cleanupFunc() } @@ -104,9 +117,12 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { if err != nil { return } + logger.Info(fingerprint) + logger.Info("REST server started") } - logger.Info("REST server started") + + // logger.Info("REST server started") return nil }, @@ -123,6 +139,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") + viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) From d8b6d1d1062877ec2edd2237bf07b360d5cbc9e5 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Thu, 8 Nov 2018 16:45:44 -0800 Subject: [PATCH 3/3] Update CHANGELOG/PENDING for straggling PR #2690 --- CHANGELOG.md | 2 ++ PENDING.md | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8be464b40..f635b7ea6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ BUG FIXES * Gaia * [\#2670](https://github.com/cosmos/cosmos-sdk/issues/2670) [x/stake] fixed incorrect `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` * [\#2691](https://github.com/cosmos/cosmos-sdk/issues/2691) Fix local testnet creation by using a single canonical genesis time + - [\#2670](https://github.com/cosmos/cosmos-sdk/issues/2670) [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - [\#2648](https://github.com/cosmos/cosmos-sdk/issues/2648) [gaiad] Fix `gaiad export` / `gaiad import` consistency, test in CI * SDK * [\#2625](https://github.com/cosmos/cosmos-sdk/issues/2625) [x/gov] fix AppendTag function usage error diff --git a/PENDING.md b/PENDING.md index 0b56d5333..4d87301f0 100644 --- a/PENDING.md +++ b/PENDING.md @@ -22,7 +22,6 @@ FEATURES * Gaia * SDK - - \#1336 Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. * Tendermint @@ -47,9 +46,6 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia - - \#2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` - - \#2648 [gaiad] Fix `gaiad export` / `gaiad import` consistency, test in CI - - \#2691 Fix local testnet creation by using a single canonical genesis time * SDK