Merge branch 'develop' into rigel/sim-cleanup

This commit is contained in:
Christopher Goes 2018-11-09 16:16:01 +01:00 committed by GitHub
commit 8a58fdf634
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 667 additions and 155 deletions

View File

@ -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

View File

@ -47,13 +47,14 @@ 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
* [\#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
* [\#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

View File

@ -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

View File

@ -35,7 +35,7 @@ IMPROVEMENTS
* Gaia
* SDK
- [mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization
- [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization
* Tendermint

View File

@ -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))

View File

@ -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)
}

View File

@ -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,
}
}

View File

@ -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) {

View File

@ -113,6 +113,6 @@ func genAppStateFromConfig(
return
}
err = WriteGenesisFile(genFile, initCfg.ChainID, nil, appState)
err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState)
return
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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)
},
}

View File

@ -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 {

View File

@ -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)
},
}

View File

@ -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}
}

View File

@ -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"})

View File

@ -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)

View File

@ -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)})

View File

@ -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

View File

@ -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.

33
x/auth/genesis.go Normal file
View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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) {

View File

@ -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)
}

View File

@ -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,
}
}

View File

@ -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,

View File

@ -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)))

View File

@ -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)

View File

@ -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, &params)
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,
}
}

View File

@ -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) {}

View File

@ -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)

View File

@ -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)

View File

@ -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()...)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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()))

View File

@ -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
}

View File

@ -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()

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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

View File

@ -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(), &timeslice)
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)

View File

@ -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

View File

@ -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 {