From 7031bc8e61e448900007670740c508c6df1f1b86 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 9 Nov 2018 16:17:03 +0100 Subject: [PATCH 1/9] Update PENDING.md --- PENDING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PENDING.md b/PENDING.md index 4d87301f0d5b..8da93841b17d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -48,5 +48,7 @@ BUG FIXES * Gaia * SDK + + - \#2733 [x/gov, x/mock/simulation] Fix governance simulation, update x/gov import/export * Tendermint From d761eb7282629c512012819c6f5eebff9b4729e5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 9 Nov 2018 16:26:08 +0100 Subject: [PATCH 2/9] Address remaining comments from #2690 --- Makefile | 7 +---- scripts/import-export-sim.sh | 54 ++++++++++++++++++++++++++++++++++++ types/store.go | 13 +++++---- x/slashing/genesis.go | 6 ++-- x/slashing/signing_info.go | 2 +- 5 files changed, 66 insertions(+), 16 deletions(-) create mode 100755 scripts/import-export-sim.sh diff --git a/Makefile b/Makefile index aaa2fbcf3e48..1c42e1d4bd56 100644 --- a/Makefile +++ b/Makefile @@ -173,12 +173,7 @@ test_sim_gaia_fast: 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 + @bash scripts/import-export-sim.sh 50 test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" diff --git a/scripts/import-export-sim.sh b/scripts/import-export-sim.sh new file mode 100755 index 000000000000..8ace8e0d56c8 --- /dev/null +++ b/scripts/import-export-sim.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \ +11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827) +blocks=$1 + +echo "Running multi-seed import-export simulation with seeds ${seeds[@]}" +echo "Running $blocks blocks per seed" +echo "Edit scripts/import-export-sim.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce." +echo "This script will kill all sub-simulations on SIGINT/SIGTERM (i.e. Ctrl-C)." + +trap 'kill $(jobs -pr)' SIGINT SIGTERM + +tmpdir=$(mktemp -d) +echo "Using temporary log directory: $tmpdir" + +sim() { + seed=$1 + echo "Running import/export Gaia simulation with seed $seed. This may take awhile!" + file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -Iseconds -u).stdout" + echo "Writing stdout to $file..." + go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=$blocks \ + -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file +} + +i=0 +pids=() +for seed in ${seeds[@]}; do + sim $seed & + pids[${i}]=$! + i=$(($i+1)) + sleep 10 # start in order, nicer logs +done + +echo "Simulation processes spawned, waiting for completion..." + +code=0 + +i=0 +for pid in ${pids[*]}; do + wait $pid + last=$? + seed=${seeds[${i}]} + if [ $last -ne 0 ] + then + echo "Import/export simulation with seed $seed failed!" + code=1 + else + echo "Import/export simulation with seed $seed OK" + fi + i=$(($i+1)) +done + +exit $code diff --git a/types/store.go b/types/store.go index 4b6e79a7670f..422ddfbfd401 100644 --- a/types/store.go +++ b/types/store.go @@ -197,16 +197,17 @@ func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair kvB = cmn.KVPair{Key: iterB.Key(), Value: iterB.Value()} iterB.Next() } - compareValue := true + if !bytes.Equal(kvA.Key, kvB.Key) { + return kvA, kvB, count, false + } for _, prefix := range prefixesToSkip { + // Skip value comparision if we matched a prefix if bytes.Equal(kvA.Key[:len(prefix)], prefix) { - compareValue = false + count++ + continue } } - if !bytes.Equal(kvA.Key, kvB.Key) { - return kvA, kvB, count, false - } - if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { + if !bytes.Equal(kvA.Value, kvB.Value) { return kvA, kvB, count, false } count++ diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go index 614bf41eb371..1d4a44369151 100644 --- a/x/slashing/genesis.go +++ b/x/slashing/genesis.go @@ -73,13 +73,13 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) (data GenesisState) { keeper.iterateValidatorSigningInfos(ctx, func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool) { bechAddr := address.String() signingInfos[bechAddr] = info - array := []MissedBlock{} + localMissedBlocks := []MissedBlock{} keeper.iterateValidatorMissedBlockBitArray(ctx, address, func(index int64, missed bool) (stop bool) { - array = append(array, MissedBlock{index, missed}) + localMissedBlocks = append(localMissedBlocks, MissedBlock{index, missed}) return false }) - missedBlocks[bechAddr] = array + missedBlocks[bechAddr] = localMissedBlocks return false }) diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 77c437174b9c..7283c6ca01bf 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -24,7 +24,6 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress 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 @@ -33,6 +32,7 @@ func (k Keeper) iterateValidatorSigningInfos(ctx sdk.Context, handler func(addre break } } + iter.Close() } // Stored by *validator* address (not operator address) From f9c7281124c1b5a407a2e3dd109fb018fc2a1774 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 9 Nov 2018 16:43:46 +0100 Subject: [PATCH 3/9] Linter fix --- types/store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/store.go b/types/store.go index 422ddfbfd401..0099ec516f7f 100644 --- a/types/store.go +++ b/types/store.go @@ -201,7 +201,7 @@ func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair return kvA, kvB, count, false } for _, prefix := range prefixesToSkip { - // Skip value comparision if we matched a prefix + // Skip value comparison if we matched a prefix if bytes.Equal(kvA.Key[:len(prefix)], prefix) { count++ continue From 183c7b21b22e59c5a261898efbcb43520c9b74ad Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 11 Nov 2018 19:09:12 -0800 Subject: [PATCH 4/9] use defer --- x/slashing/signing_info.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 7283c6ca01bf..291351742f39 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -24,6 +24,7 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress 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 @@ -32,7 +33,6 @@ func (k Keeper) iterateValidatorSigningInfos(ctx sdk.Context, handler func(addre break } } - iter.Close() } // Stored by *validator* address (not operator address) @@ -84,10 +84,10 @@ func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.Con func (k Keeper) clearValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress) { store := ctx.KVStore(k.storeKey) iter := sdk.KVStorePrefixIterator(store, GetValidatorMissedBlockBitArrayPrefixKey(address)) + defer iter.Close() for ; iter.Valid(); iter.Next() { store.Delete(iter.Key()) } - iter.Close() } // Construct a new `ValidatorSigningInfo` struct From 2b3226854c2fa3963540d98376e8ed9ac3754899 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 12 Nov 2018 17:25:13 +0100 Subject: [PATCH 5/9] Correctly set return code --- x/gov/simulation/msgs.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/msgs.go index 0eadc7febb6b..84700d891bd3 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/msgs.go @@ -95,7 +95,8 @@ func SimulateMsgSubmitProposal(k gov.Keeper) simulation.Operation { func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, handler sdk.Handler, ctx sdk.Context, event func(string)) (action string, ok bool) { ctx, _ = ctx.CacheContext() - handler(ctx, msg) + result := handler(ctx, msg) + ok = result.IsOK() event(fmt.Sprintf("gov/MsgSubmitProposal/%v", ok)) action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", ok, msg.GetSignBytes()) return From 0e56ed9e4e789a83fc511193b916612485d72570 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 13 Nov 2018 14:36:09 +0100 Subject: [PATCH 6/9] Fix DiffKVStore --- types/store.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/store.go b/types/store.go index 0099ec516f7f..8fe0321f5c7c 100644 --- a/types/store.go +++ b/types/store.go @@ -200,14 +200,14 @@ func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair if !bytes.Equal(kvA.Key, kvB.Key) { return kvA, kvB, count, false } + compareValue := true for _, prefix := range prefixesToSkip { // Skip value comparison if we matched a prefix if bytes.Equal(kvA.Key[:len(prefix)], prefix) { - count++ - continue + compareValue = false } } - if !bytes.Equal(kvA.Value, kvB.Value) { + if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { return kvA, kvB, count, false } count++ From 10713e3c8adb2afbc938f5d891be9ddd25963a9c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 13 Nov 2018 15:08:14 +0100 Subject: [PATCH 7/9] Working on stake import/export --- cmd/gaia/app/app.go | 2 ++ x/stake/genesis.go | 27 ++++++++++++++++++--------- x/stake/keeper/keeper.go | 25 +++++++++++++++++++++++++ x/stake/types/genesis.go | 8 ++++++++ 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 918bfc67d114..765d624e9dc5 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -251,6 +251,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData) distr.InitGenesis(ctx, app.distrKeeper, genesisState.DistrData) + + // validate genesis state err = GaiaValidateGenesisState(genesisState) if err != nil { panic(err) // TODO find a way to do this w/o panics diff --git a/x/stake/genesis.go b/x/stake/genesis.go index d44055ea849b..42a18f9045bc 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -29,17 +29,9 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ 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 { // set the intra-tx counter to the order the validators are presented, if necessary - if setBondIntraTxCounter { + if !data.Exported { validator.BondIntraTxCounter = int16(i) } keeper.SetValidator(ctx, validator) @@ -77,6 +69,16 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ } res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + // overwrite the pool since we exported + if data.Exported { + keeper.SetPool(ctx, data.Pool) + keeper.ClearLastValidatorPowers(ctx) + for _, lv := range data.LastValidatorPowers { + keeper.SetLastValidatorPower(ctx, lv.Address, lv.Power) + } + } + return } @@ -100,16 +102,23 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { redelegations = append(redelegations, red) return false }) + var lastValidatorPowers []types.LastValidatorPower + keeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power sdk.Int) (stop bool) { + lastValidatorPowers = append(lastValidatorPowers, types.LastValidatorPower{addr, power}) + return false + }) return types.GenesisState{ Pool: pool, Params: params, IntraTxCounter: intraTxCounter, LastTotalPower: lastTotalPower, + LastValidatorPowers: lastValidatorPowers, Validators: validators, Bonds: bonds, UnbondingDelegations: unbondingDelegations, Redelegations: redelegations, + Exported: true, } } diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index a74f86084f82..70506f6aba01 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -112,6 +112,31 @@ func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, store.Set(GetLastValidatorPowerKey(operator), bz) } +// Iterate over last validator powers. +func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power sdk.Int) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(iter.Key()[len(LastValidatorPowerKey):]) + var power sdk.Int + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power) + if handler(addr, power) { + break + } + } +} + +// Clear last validator powers. +func (k Keeper) ClearLastValidatorPowers(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + store.Delete(iter.Key()) + } +} + // Delete the last validator power. func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) { store := ctx.KVStore(k.storeKey) diff --git a/x/stake/types/genesis.go b/x/stake/types/genesis.go index f1673a376a4a..1085e8f97087 100644 --- a/x/stake/types/genesis.go +++ b/x/stake/types/genesis.go @@ -10,10 +10,18 @@ type GenesisState struct { Params Params `json:"params"` IntraTxCounter int16 `json:"intra_tx_counter"` LastTotalPower sdk.Int `json:"last_total_power"` + LastValidatorPowers []LastValidatorPower `json:"last_validator_powers"` Validators []Validator `json:"validators"` Bonds []Delegation `json:"bonds"` UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations"` Redelegations []Redelegation `json:"redelegations"` + Exported bool `json:"exported"` +} + +// Last validator power, needed for validator set update logic +type LastValidatorPower struct { + Address sdk.ValAddress + Power sdk.Int } func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState { From 3686a3f47dc22418608b895a71e79ec612359f1b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 13 Nov 2018 15:38:29 +0100 Subject: [PATCH 8/9] Only apply validator set updates on initial genesis --- x/stake/genesis.go | 6 ++---- x/stake/keeper/keeper.go | 10 ---------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 42a18f9045bc..9b5c3c6af11a 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -68,15 +68,13 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ keeper.InsertRedelegationQueue(ctx, red) } - res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) - // overwrite the pool since we exported if data.Exported { - keeper.SetPool(ctx, data.Pool) - keeper.ClearLastValidatorPowers(ctx) for _, lv := range data.LastValidatorPowers { keeper.SetLastValidatorPower(ctx, lv.Address, lv.Power) } + } else { + res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) } return diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index 70506f6aba01..b6c973f98e05 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -127,16 +127,6 @@ func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operato } } -// Clear last validator powers. -func (k Keeper) ClearLastValidatorPowers(ctx sdk.Context) { - store := ctx.KVStore(k.storeKey) - iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - store.Delete(iter.Key()) - } -} - // Delete the last validator power. func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) { store := ctx.KVStore(k.storeKey) From 267728f0256c62bdce6cfa1fdd8870179df74a30 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 13 Nov 2018 15:46:14 +0100 Subject: [PATCH 9/9] Clarify comment --- x/stake/genesis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 9b5c3c6af11a..7be8eff3a4fc 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -68,7 +68,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ keeper.InsertRedelegationQueue(ctx, red) } - // overwrite the pool since we exported + // don't need to run Tendermint updates if we exported if data.Exported { for _, lv := range data.LastValidatorPowers { keeper.SetLastValidatorPower(ctx, lv.Address, lv.Power)