From 940cfa98af3cc5adac7cebe2644398f0f2d1206f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 11 Jul 2018 02:36:50 +0200 Subject: [PATCH] Invariants & random Msgs in progress --- types/coin.go | 6 +- x/mock/random_simulate_blocks.go | 26 +++++++ x/stake/simulation_test.go | 123 +++++++++++++++++++++++++++++-- 3 files changed, 148 insertions(+), 7 deletions(-) diff --git a/types/coin.go b/types/coin.go index eba645932..862614ca0 100644 --- a/types/coin.go +++ b/types/coin.go @@ -15,9 +15,13 @@ type Coin struct { } func NewCoin(denom string, amount int64) Coin { + return NewIntCoin(denom, NewInt(amount)) +} + +func NewIntCoin(denom string, amount Int) Coin { return Coin{ Denom: denom, - Amount: NewInt(amount), + Amount: amount, } } diff --git a/x/mock/random_simulate_blocks.go b/x/mock/random_simulate_blocks.go index 4883cb180..452b8f6bf 100644 --- a/x/mock/random_simulate_blocks.go +++ b/x/mock/random_simulate_blocks.go @@ -135,3 +135,29 @@ func RandFromBigInterval(r *rand.Rand, intervals []BigInterval) sdk.Int { return result } + +// shamelessly copied from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang#31832326 + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = r.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + b[i] = letterBytes[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + return string(b) +} diff --git a/x/stake/simulation_test.go b/x/stake/simulation_test.go index 8a59d9aa0..f9aac426c 100644 --- a/x/stake/simulation_test.go +++ b/x/stake/simulation_test.go @@ -20,13 +20,14 @@ import ( // Currently: total supply, positive power func ModuleInvariants(ck bank.Keeper, k Keeper) mock.Invariant { return func(t *testing.T, app *mock.App, log string) { - TotalSupplyInvariant(ck, k)(t, app, log) + SupplyInvariants(ck, k)(t, app, log) PositivePowerInvariant(k)(t, app, log) + ValidatorSetInvariant(k)(t, app, log) } } -// TotalSupplyInvariant checks that the total supply reflects all held loose tokens, bonded tokens, and unbonding delegations -func TotalSupplyInvariant(ck bank.Keeper, k Keeper) mock.Invariant { +// SupplyInvariants checks that the total supply reflects all held loose tokens, bonded tokens, and unbonding delegations +func SupplyInvariants(ck bank.Keeper, k Keeper) mock.Invariant { return func(t *testing.T, app *mock.App, log string) { ctx := app.NewContext(false, abci.Header{}) pool := k.GetPool(ctx) @@ -40,8 +41,26 @@ func TotalSupplyInvariant(ck bank.Keeper, k Keeper) mock.Invariant { require.True(t, sdk.NewInt(pool.LooseTokens).Equal(loose), "expected loose tokens to equal total steak held by accounts") // Bonded tokens should equal sum of tokens with bonded validators - // Unbonded tokens should equal sum of tokens with unbonded validators + bonded := sdk.ZeroRat() + unbonded := sdk.ZeroRat() + k.IterateValidators(ctx, func(_ int64, validator sdk.Validator) bool { + switch validator.GetStatus() { + case sdk.Bonded: + bonded = bonded.Add(validator.GetPower()) + case sdk.Unbonding: + // TODO + case sdk.Unbonded: + unbonded = unbonded.Add(validator.GetPower()) + } + return false + }) + require.True(t, sdk.NewRat(pool.BondedTokens).Equal(bonded), "expected bonded tokens to equal total steak held by bonded validators") + require.True(t, sdk.NewRat(pool.UnbondedTokens).Equal(unbonded), "expected unbonded tokens to equal total steak held by unbonded validators") + + // TODO Unbonding tokens + + // TODO Inflation check on total supply } } @@ -56,6 +75,59 @@ func PositivePowerInvariant(k Keeper) mock.Invariant { } } +// ValidatorSetInvariant checks equivalence of Tendermint validator set and SDK validator set +func ValidatorSetInvariant(k Keeper) mock.Invariant { + return func(t *testing.T, app *mock.App, log string) { + // TODO + } +} + +// SimulateMsgCreateValidator +func SimulateMsgCreateValidator(m auth.AccountMapper, k Keeper) mock.TestAndRunMsg { + return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) { + denom := k.GetParams(ctx).BondDenom + description := Description{ + Moniker: mock.RandStringOfLength(r, 10), + } + key := keys[r.Intn(len(keys))] + pubkey := key.PubKey() + address := sdk.AccAddress(pubkey.Address()) + amount := m.GetAccount(ctx, address).GetCoins().AmountOf(denom) + if amount.GT(sdk.ZeroInt()) { + amount = sdk.NewInt(int64(r.Intn(int(amount.Int64())))) + } + msg := MsgCreateValidator{ + Description: description, + ValidatorAddr: address, + PubKey: pubkey, + SelfDelegation: sdk.NewIntCoin(denom, amount), + } + action = fmt.Sprintf("TestMsgCreateValidator: %s", msg.GetSignBytes()) + return action, nil + } +} + +// SimulateMsgEditValidator +func SimulateMsgEditValidator(k Keeper) mock.TestAndRunMsg { + return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) { + description := Description{ + Moniker: mock.RandStringOfLength(r, 10), + Identity: mock.RandStringOfLength(r, 10), + Website: mock.RandStringOfLength(r, 10), + Details: mock.RandStringOfLength(r, 10), + } + key := keys[r.Intn(len(keys))] + pubkey := key.PubKey() + address := sdk.AccAddress(pubkey.Address()) + msg := MsgEditValidator{ + Description: description, + ValidatorAddr: address, + } + action = fmt.Sprintf("TestMsgEditValidator: %s", msg.GetSignBytes()) + return action, nil + } +} + // SimulateMsgDelegate func SimulateMsgDelegate(k Keeper) mock.TestAndRunMsg { return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) { @@ -64,6 +136,38 @@ func SimulateMsgDelegate(k Keeper) mock.TestAndRunMsg { } } +// SimulateMsgBeginUnbonding +func SimulateMsgBeginUnbonding(k Keeper) mock.TestAndRunMsg { + return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) { + msg := fmt.Sprintf("TestMsgBeginUnbonding with %s", "ok") + return msg, nil + } +} + +// SimulateMsgCompleteUnbonding +func SimulateMsgCompleteUnbonding(k Keeper) mock.TestAndRunMsg { + return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) { + msg := fmt.Sprintf("TestMsgCompleteUnbonding with %s", "ok") + return msg, nil + } +} + +// SimulateMsgBeginRedelegate +func SimulateMsgBeginRedelegate(k Keeper) mock.TestAndRunMsg { + return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) { + msg := fmt.Sprintf("TestMsgBeginRedelegate with %s", "ok") + return msg, nil + } +} + +// SimulateMsgCompleteRedelegate +func SimulateMsgCompleteRedelegate(k Keeper) mock.TestAndRunMsg { + return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) { + msg := fmt.Sprintf("TestMsgCompleteRedelegate with %s", "ok") + return msg, nil + } +} + // SimulationSetup func SimulationSetup(mapp *mock.App, k Keeper) mock.RandSetup { return func(r *rand.Rand, privKeys []crypto.PrivKey) { @@ -72,12 +176,13 @@ func SimulationSetup(mapp *mock.App, k Keeper) mock.RandSetup { } } -// Test random messages +// TestStakeWithRandomMessages func TestStakeWithRandomMessages(t *testing.T) { mapp := mock.NewApp() bank.RegisterWire(mapp.Cdc) - coinKeeper := bank.NewKeeper(mapp.AccountMapper) + mapper := mapp.AccountMapper + coinKeeper := bank.NewKeeper(mapper) stakeKey := sdk.NewKVStoreKey("stake") stakeKeeper := NewKeeper(mapp.Cdc, stakeKey, coinKeeper, DefaultCodespace) mapp.Router().AddRoute("stake", NewHandler(stakeKeeper)) @@ -89,7 +194,13 @@ func TestStakeWithRandomMessages(t *testing.T) { mapp.SimpleRandomizedTestingFromSeed( t, 20, []mock.TestAndRunMsg{ + SimulateMsgCreateValidator(mapper, stakeKeeper), + SimulateMsgEditValidator(stakeKeeper), SimulateMsgDelegate(stakeKeeper), + SimulateMsgBeginUnbonding(stakeKeeper), + SimulateMsgCompleteUnbonding(stakeKeeper), + SimulateMsgBeginRedelegate(stakeKeeper), + SimulateMsgCompleteRedelegate(stakeKeeper), }, []mock.RandSetup{ SimulationSetup(mapp, stakeKeeper), }, []mock.Invariant{