From 52b249b0cdf6267541d6b7ccd7ab726336f8c363 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 29 Aug 2018 18:44:40 -0400 Subject: [PATCH] complete tests --- x/stake/keeper/delegation.go | 39 +++++- x/stake/keeper/delegation_test.go | 205 +++++++++++++++++++++++++++++- x/stake/keeper/slash.go | 9 +- x/stake/types/validator.go | 13 ++ 4 files changed, 248 insertions(+), 18 deletions(-) diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 88260cb7d5..7f66cd5550 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -2,6 +2,7 @@ package keeper import ( "bytes" + "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -256,44 +257,56 @@ func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt // unbond the the delegation return func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress, shares sdk.Dec) (amount sdk.Dec, err sdk.Error) { + fmt.Println("wackydebugoutput unbond 0") // check if delegation has any shares in it unbond delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr) if !found { + fmt.Println("wackydebugoutput unbond 1") err = types.ErrNoDelegatorForAddress(k.Codespace()) return } + fmt.Println("wackydebugoutput unbond 2") // retrieve the amount to remove if delegation.Shares.LT(shares) { + fmt.Println("wackydebugoutput unbond 3") err = types.ErrNotEnoughDelegationShares(k.Codespace(), delegation.Shares.String()) return } + fmt.Println("wackydebugoutput unbond 4") // get validator validator, found := k.GetValidator(ctx, validatorAddr) if !found { + fmt.Println("wackydebugoutput unbond 5") err = types.ErrNoValidatorFound(k.Codespace()) return } + fmt.Println("wackydebugoutput unbond 6") // subtract shares from delegator delegation.Shares = delegation.Shares.Sub(shares) // remove the delegation if delegation.Shares.IsZero() { + fmt.Println("wackydebugoutput unbond 7") // if the delegation is the operator of the validator then // trigger a jail validator if bytes.Equal(delegation.DelegatorAddr, validator.Operator) && validator.Jailed == false { + fmt.Println("wackydebugoutput unbond 8") validator.Jailed = true } + fmt.Println("wackydebugoutput unbond 9") k.RemoveDelegation(ctx, delegation) } else { + fmt.Println("wackydebugoutput unbond 10") // Update height delegation.Height = ctx.BlockHeight() k.SetDelegation(ctx, delegation) } + fmt.Println("wackydebugoutput unbond 11") // remove the coins from the validator pool := k.GetPool(ctx) @@ -303,9 +316,12 @@ func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddr // update then remove validator if necessary validator = k.UpdateValidator(ctx, validator) + fmt.Printf("debug validator: %v\n", validator) if validator.DelegatorShares.IsZero() { + fmt.Println("wackydebugoutput unbond 12") k.RemoveValidator(ctx, validator.Operator) } + fmt.Println("wackydebugoutput unbond 13") return } @@ -315,24 +331,25 @@ func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddr // get info for begin functions: MinTime and CreationHeight func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, validatorSrcAddr sdk.AccAddress) ( minTime time.Time, height int64, completeNow bool) { + fmt.Println("wackydebugoutput getBeginInfo 0") validator, found := k.GetValidator(ctx, validatorSrcAddr) switch { case !found || validator.Status == sdk.Bonded: - // longest wait - just unbonding period from now + // the longest wait - just unbonding period from now minTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) height = ctx.BlockHeader().Height return minTime, height, false + case validator.IsUnbonded(ctx): + return minTime, height, true + case validator.Status == sdk.Unbonding: minTime = validator.UnbondingMinTime height = validator.UnbondingHeight return minTime, height, false - case validator.Status == sdk.Unbonded: - return minTime, height, true - default: panic("unknown validator status") } @@ -403,27 +420,38 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr // complete unbonding an unbonding record func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.AccAddress, sharesAmount sdk.Dec) sdk.Error { + fmt.Println("wackydebugoutput BeginRedelegation 0") // check if this is a transitive redelegation if k.HasReceivingRedelegation(ctx, delegatorAddr, validatorSrcAddr) { + fmt.Println("wackydebugoutput BeginRedelegation 1") return types.ErrTransitiveRedelegation(k.Codespace()) } + fmt.Println("wackydebugoutput BeginRedelegation 2") returnAmount, err := k.unbond(ctx, delegatorAddr, validatorSrcAddr, sharesAmount) if err != nil { + fmt.Println("wackydebugoutput BeginRedelegation 3") return err } + fmt.Println("wackydebugoutput BeginRedelegation 4") params := k.GetParams(ctx) returnCoin := sdk.Coin{params.BondDenom, returnAmount.RoundInt()} + fmt.Printf("debug returnCoin: %v\n", returnCoin) + fmt.Println("wackydebugoutput BeginRedelegation 5") dstValidator, found := k.GetValidator(ctx, validatorDstAddr) if !found { + fmt.Println("wackydebugoutput BeginRedelegation 6") return types.ErrBadRedelegationDst(k.Codespace()) } + fmt.Println("wackydebugoutput BeginRedelegation 7") sharesCreated, err := k.Delegate(ctx, delegatorAddr, returnCoin, dstValidator, false) if err != nil { + fmt.Println("wackydebugoutput BeginRedelegation 8") return err } + fmt.Println("wackydebugoutput BeginRedelegation 9") // create the unbonding delegation minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime) @@ -432,8 +460,10 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAd minTime, height, completeNow := k.getBeginInfo(ctx, params, validatorSrcAddr) if completeNow { // no need to create the redelegation object + fmt.Println("wackydebugoutput BeginRedelegation 10") return nil } + fmt.Println("wackydebugoutput BeginRedelegation 11") red := types.Redelegation{ DelegatorAddr: delegatorAddr, @@ -446,6 +476,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAd Balance: returnCoin, InitialBalance: returnCoin, } + fmt.Println("wackydebugoutput BeginRedelegation 13") k.SetRedelegation(ctx, red) return nil } diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index add16fb52c..e64bfa38ba 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -219,8 +219,6 @@ func TestUndelegateSelfDelegation(t *testing.T) { validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) - pool = keeper.GetPool(ctx) - require.Equal(t, int64(10), validator.Tokens.RoundInt64()) require.Equal(t, sdk.Unbonding, validator.Status) } @@ -358,8 +356,8 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { require.NoError(t, err) // no ubd should have been found, coins should have been returned direcly to account - _, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) - require.False(t, found) + ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.False(t, found, "%v", ubd) } // Make sure that that the retrieving the delegations doesn't affect the state @@ -442,10 +440,203 @@ func TestRedelegation(t *testing.T) { require.False(t, found) } -func TestRedelegationFromUnbondingValidator(t *testing.T) { - require.Fail(t, "") +func TestRedelegateSelfDelegation(t *testing.T) { + + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + pool.LooseTokens = sdk.NewDec(30) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + pool = keeper.GetPool(ctx) + selfDelegation := types.Delegation{ + DelegatorAddr: addrVals[0], + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, selfDelegation) + + // create a second validator + validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator2 = keeper.UpdateValidator(ctx, validator2) + + // create a second delegation to this validator + validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + pool = keeper.GetPool(ctx) + delegation := types.Delegation{ + DelegatorAddr: addrDels[0], + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, delegation) + + err := keeper.BeginRedelegation(ctx, addrVals[0], addrVals[0], addrVals[1], sdk.NewDec(10)) + require.NoError(t, err) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, int64(10), validator.Tokens.RoundInt64()) + require.Equal(t, sdk.Unbonding, validator.Status) } -func TestRedelegationFromUnbondedValidator(t *testing.T) { +func TestRedelegateFromUnbondingValidator(t *testing.T) { + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + pool.LooseTokens = sdk.NewDec(30) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + pool = keeper.GetPool(ctx) + selfDelegation := types.Delegation{ + DelegatorAddr: addrVals[0], + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, selfDelegation) + + // create a second delegation to this validator + validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + pool = keeper.GetPool(ctx) + delegation := types.Delegation{ + DelegatorAddr: addrDels[0], + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, delegation) + + // create a second validator + validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator2 = keeper.UpdateValidator(ctx, validator2) + + header := ctx.BlockHeader() + blockHeight := int64(10) + header.Height = blockHeight + blockTime := time.Unix(333, 0) + header.Time = blockTime + ctx = ctx.WithBlockHeader(header) + + // unbond the all self-delegation to put validator in unbonding state + err := keeper.BeginUnbonding(ctx, addrVals[0], addrVals[0], sdk.NewDec(10)) + require.NoError(t, err) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, blockHeight, validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime)) + + //change the context + header = ctx.BlockHeader() + blockHeight2 := int64(20) + header.Height = blockHeight2 + blockTime2 := time.Unix(444, 0) + header.Time = blockTime2 + ctx = ctx.WithBlockHeader(header) + + // unbond some of the other delegation's shares + err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6)) + require.NoError(t, err) + + // retrieve the unbonding delegation + ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.True(t, ubd.Balance.IsEqual(sdk.NewInt64Coin(params.BondDenom, 6))) + assert.Equal(t, blockHeight, ubd.CreationHeight) + assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.MinTime)) +} + +func TestRedelegateFromUnbondedValidator(t *testing.T) { + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + pool.LooseTokens = sdk.NewDec(30) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + pool = keeper.GetPool(ctx) + selfDelegation := types.Delegation{ + DelegatorAddr: addrVals[0], + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, selfDelegation) + + // create a second delegation to this validator + validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + pool = keeper.GetPool(ctx) + delegation := types.Delegation{ + DelegatorAddr: addrDels[0], + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, delegation) + + // create a second validator + validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator2 = keeper.UpdateValidator(ctx, validator2) + + header := ctx.BlockHeader() + blockHeight := int64(10) + header.Height = blockHeight + blockTime := time.Unix(333, 0) + header.Time = blockTime + ctx = ctx.WithBlockHeader(header) + + // unbond the all self-delegation to put validator in unbonding state + err := keeper.BeginUnbonding(ctx, addrVals[0], addrVals[0], sdk.NewDec(10)) + require.NoError(t, err) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, blockHeight, validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime)) + + // change the context to one which makes the validator considered unbonded + header = ctx.BlockHeader() + blockHeight2 := int64(20) + header.Height = blockHeight2 + blockTime2 := time.Unix(444, 0).Add(params.UnbondingTime) + header.Time = blockTime2 + ctx = ctx.WithBlockHeader(header) + + // unbond some of the other delegation's shares + err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6)) + require.NoError(t, err) + + // no ubd should have been found, coins should have been returned direcly to account + ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.False(t, found, "%v", ubd) require.Fail(t, "") } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index c3c79714ee..be3572d7c3 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -46,14 +46,9 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in return } - // slashing should not be slashing unbonded - if validator.Status == sdk.Unbonded { + // should not be slashing unbonded + if validator.IsUnbonded(ctx) { panic(fmt.Sprintf("should not be slashing unbonded validator: %v", validator)) - } else if validator.Status == sdk.Unbonding { - ctxTime := ctx.BlockHeader().Time - if ctxTime.After(validator.UnbondingMinTime) { - panic(fmt.Sprintf("should not be slashing unbonded validator: %v", validator)) - } } operatorAddress := validator.GetOperator() diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index a912b26dc7..13b00f9094 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -418,6 +418,19 @@ func (v Validator) BondedTokens() sdk.Dec { return sdk.ZeroDec() } +// Returns if the validator should be considered unbonded +func (v Validator) IsUnbonded(ctx sdk.Context) bool { + if v.Status == sdk.Unbonded { + return true + } else if v.Status == sdk.Unbonding { + ctxTime := ctx.BlockHeader().Time + if ctxTime.After(v.UnbondingMinTime) { + return true + } + } + return false +} + //______________________________________________________________________ // ensure fulfills the sdk validator types