From 6799c703af33219213920919e74431598b60119f Mon Sep 17 00:00:00 2001 From: Zhimao Liu Date: Mon, 17 Sep 2018 15:40:25 -0700 Subject: [PATCH] move claimInterest to vote module and add basic stake interest test (#285) * add basic stake interest test * move claimInterest to vote module * finish basic stake interest test * adjust test case --- test/global/stake_interest_test.go | 101 +++++++++++++++++++++++++++++ x/account/handler.go | 10 --- x/account/manager.go | 32 --------- x/account/manager_test.go | 58 ----------------- x/account/model/account.go | 1 - x/account/model/storage_test.go | 1 - x/account/msg.go | 53 --------------- x/account/msg_test.go | 34 ---------- x/account/wire.go | 1 - x/post/event_test.go | 4 -- x/post/handler_test.go | 1 - x/vote/handler.go | 24 ++++++- x/vote/manager.go | 42 ++++++++++++ x/vote/manager_test.go | 42 ++++++++++++ x/vote/model/storage_test.go | 10 +-- x/vote/model/voter.go | 1 + x/vote/msg.go | 53 +++++++++++++++ x/vote/msg_test.go | 34 ++++++++++ x/vote/test_util.go | 3 + x/vote/wire.go | 1 + 20 files changed, 306 insertions(+), 200 deletions(-) create mode 100644 test/global/stake_interest_test.go diff --git a/test/global/stake_interest_test.go b/test/global/stake_interest_test.go new file mode 100644 index 00000000..3d9dc575 --- /dev/null +++ b/test/global/stake_interest_test.go @@ -0,0 +1,101 @@ +package global + +import ( + "testing" + "time" + + "github.com/lino-network/lino/test" + "github.com/lino-network/lino/types" + "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/lino-network/lino/x/post" + vote "github.com/lino-network/lino/x/vote" +) + +func TestStakeInterest(t *testing.T) { + postUserPriv := secp256k1.GenPrivKey() + donatorPriv := secp256k1.GenPrivKey() + u1Priv := secp256k1.GenPrivKey() + u2Priv := secp256k1.GenPrivKey() + + postUserName := "poster" + donatorName := "donator" + u1Name := "user1" + u2Name := "user2" + + postID := "New Post" + + // to recover the coin day + baseTime := time.Now().Unix() + 7200 + lb := test.NewTestLinoBlockchain(t, test.DefaultNumOfVal) + + test.CreateAccount(t, donatorName, lb, 0, + secp256k1.GenPrivKey(), donatorPriv, secp256k1.GenPrivKey(), "100000") + test.CreateAccount(t, u1Name, lb, 1, + secp256k1.GenPrivKey(), u1Priv, secp256k1.GenPrivKey(), "100000") + test.CreateAccount(t, u2Name, lb, 2, + secp256k1.GenPrivKey(), u2Priv, secp256k1.GenPrivKey(), "100000") + test.CreateAccount(t, postUserName, lb, 3, + secp256k1.GenPrivKey(), postUserPriv, secp256k1.GenPrivKey(), "100000") + test.CreateTestPost( + t, lb, postUserName, postID, 0, postUserPriv, "", "", "", "", "0", baseTime) + + donateMsg := post.NewDonateMsg( + donatorName, types.LNO("2000"), postUserName, postID, "", "") + u1StakeInMsg := vote.NewStakeInMsg(u1Name, types.LNO("10000")) + u2StakeInMsg := vote.NewStakeInMsg(u2Name, types.LNO("40000")) + + test.SignCheckDeliver(t, lb, donateMsg, 0, true, donatorPriv, baseTime) + test.SignCheckDeliver(t, lb, u1StakeInMsg, 0, true, u1Priv, baseTime) + test.SignCheckDeliver(t, lb, u2StakeInMsg, 0, true, u2Priv, baseTime) + + test.CheckBalance(t, u1Name, lb, types.NewCoinFromInt64(89999*types.Decimals)) + test.CheckBalance(t, u2Name, lb, types.NewCoinFromInt64(59999*types.Decimals)) + + // 3rd day + baseTime += 3600 * 24 * 3 + test.SimulateOneBlock(lb, baseTime) + + u1ClaimInterestMsg := vote.NewClaimInterestMsg(u1Name) + u2ClaimInterestMsg := vote.NewClaimInterestMsg(u2Name) + + test.SignCheckDeliver(t, lb, u1ClaimInterestMsg, 1, true, u1Priv, baseTime) + test.SignCheckDeliver(t, lb, u2ClaimInterestMsg, 1, true, u2Priv, baseTime) + + test.CheckBalance(t, u1Name, lb, types.NewCoinFromInt64((89999+0.15748)*types.Decimals)) + test.CheckBalance(t, u2Name, lb, types.NewCoinFromInt64((59999+0.62992)*types.Decimals)) + + u1StakeInMsg = vote.NewStakeInMsg(u1Name, types.LNO("20000")) + u2StakeOutMsg := vote.NewStakeOutMsg(u2Name, types.LNO("10000")) + + test.SignCheckDeliver(t, lb, u1StakeInMsg, 2, true, u1Priv, baseTime) + test.SignCheckDeliver(t, lb, u2StakeOutMsg, 2, true, u2Priv, baseTime) + test.SignCheckDeliver(t, lb, donateMsg, 1, true, donatorPriv, baseTime) + + // 4th day + baseTime += 3600 * 24 * 1 + test.SimulateOneBlock(lb, baseTime) + test.SignCheckDeliver(t, lb, donateMsg, 2, true, donatorPriv, baseTime) + + // 5th day + baseTime += 3600 * 24 * 1 + test.SimulateOneBlock(lb, baseTime) + test.SignCheckDeliver(t, lb, donateMsg, 3, true, donatorPriv, baseTime) + + u1StakeOutMsg := vote.NewStakeOutMsg(u1Name, types.LNO("30000")) + u2StakeOutMsg = vote.NewStakeOutMsg(u2Name, types.LNO("30000")) + + test.SignCheckDeliver(t, lb, u1StakeOutMsg, 3, true, u1Priv, baseTime) + test.SignCheckDeliver(t, lb, u2StakeOutMsg, 3, true, u2Priv, baseTime) + + // 6th day + baseTime += 3600 * 24 * 1 + test.SimulateOneBlock(lb, baseTime) + + test.SignCheckDeliver(t, lb, u1ClaimInterestMsg, 4, true, u1Priv, baseTime) + test.SignCheckDeliver(t, lb, u2ClaimInterestMsg, 4, true, u2Priv, baseTime) + + test.CheckBalance(t, u1Name, lb, types.NewCoinFromInt64((69999+1.10088)*types.Decimals)) + test.CheckBalance(t, u2Name, lb, types.NewCoinFromInt64((59999+1.57332)*types.Decimals)) + +} diff --git a/x/account/handler.go b/x/account/handler.go index 4602400e..b11d1c95 100644 --- a/x/account/handler.go +++ b/x/account/handler.go @@ -22,8 +22,6 @@ func NewHandler(am AccountManager, gm global.GlobalManager) sdk.Handler { return handleTransferMsg(ctx, am, msg) case ClaimMsg: return handleClaimMsg(ctx, am, msg) - case ClaimInterestMsg: - return handleClaimInterestMsg(ctx, am, msg) case RecoverMsg: return handleRecoverMsg(ctx, am, msg) case RegisterMsg: @@ -114,14 +112,6 @@ func handleClaimMsg(ctx sdk.Context, am AccountManager, msg ClaimMsg) sdk.Result return sdk.Result{} } -func handleClaimInterestMsg(ctx sdk.Context, am AccountManager, msg ClaimInterestMsg) sdk.Result { - // claim interest - if err := am.ClaimInterest(ctx, msg.Username); err != nil { - return err.Result() - } - return sdk.Result{} -} - func handleRecoverMsg(ctx sdk.Context, am AccountManager, msg RecoverMsg) sdk.Result { // recover if !am.DoesAccountExist(ctx, msg.Username) { diff --git a/x/account/manager.go b/x/account/manager.go index 138fc8d0..d574d46e 100644 --- a/x/account/manager.go +++ b/x/account/manager.go @@ -658,38 +658,6 @@ func (accManager AccountManager) ClaimReward( return nil } -// ClaimInterest - add lino power interst to user balance -func (accManager AccountManager) ClaimInterest( - ctx sdk.Context, username types.AccountKey) sdk.Error { - reward, err := accManager.storage.GetReward(ctx, username) - if err != nil { - return err - } - if err := accManager.AddSavingCoin( - ctx, username, reward.Interest, "", "", types.ClaimInterest); err != nil { - return err - } - reward.Interest = types.NewCoinFromInt64(0) - if err := accManager.storage.SetReward(ctx, username, reward); err != nil { - return err - } - return nil -} - -// AddInterest - add interst -func (accManager AccountManager) AddInterest( - ctx sdk.Context, username types.AccountKey, interest types.Coin) sdk.Error { - reward, err := accManager.storage.GetReward(ctx, username) - if err != nil { - return err - } - reward.Interest = reward.Interest.Plus(interest) - if err := accManager.storage.SetReward(ctx, username, reward); err != nil { - return err - } - return nil -} - // ClearRewardHistory - clear user reward history func (accManager AccountManager) ClearRewardHistory( ctx sdk.Context, username types.AccountKey) sdk.Error { diff --git a/x/account/manager_test.go b/x/account/manager_test.go index 000a546c..2d5323e9 100644 --- a/x/account/manager_test.go +++ b/x/account/manager_test.go @@ -1033,7 +1033,6 @@ func TestCreateAccountNormalCase(t *testing.T) { InflationIncome: types.NewCoinFromInt64(0), FrictionIncome: types.NewCoinFromInt64(0), UnclaimReward: types.NewCoinFromInt64(0), - Interest: types.NewCoinFromInt64(0), } checkAccountReward(t, ctx, tc.testName, tc.username, reward) @@ -1134,7 +1133,6 @@ func TestCreateAccountWithLargeRegisterFee(t *testing.T) { InflationIncome: types.NewCoinFromInt64(0), FrictionIncome: types.NewCoinFromInt64(0), UnclaimReward: types.NewCoinFromInt64(0), - Interest: types.NewCoinFromInt64(0), } checkAccountReward(t, ctx, testName, accKey, reward) @@ -1434,7 +1432,6 @@ func TestAddIncomeAndReward(t *testing.T) { FrictionIncome: c200, InflationIncome: c300, UnclaimReward: c300, - Interest: c0, } checkAccountReward(t, ctx, testName, accKey, reward) checkRewardHistory(t, ctx, testName, accKey, 0, 1) @@ -1450,7 +1447,6 @@ func TestAddIncomeAndReward(t *testing.T) { FrictionIncome: c500, InflationIncome: c500, UnclaimReward: c500, - Interest: c0, } checkAccountReward(t, ctx, testName, accKey, reward) checkRewardHistory(t, ctx, testName, accKey, 0, 2) @@ -1466,7 +1462,6 @@ func TestAddIncomeAndReward(t *testing.T) { FrictionIncome: c500, InflationIncome: c500, UnclaimReward: c500, - Interest: c0, } checkAccountReward(t, ctx, testName, accKey, reward) checkRewardHistory(t, ctx, testName, accKey, 0, 2) @@ -1494,59 +1489,6 @@ func TestAddIncomeAndReward(t *testing.T) { FrictionIncome: c500, InflationIncome: c500, UnclaimReward: c0, - Interest: c0, - } - checkAccountReward(t, ctx, testName, accKey, reward) -} - -func TestAddAndClaimInterest(t *testing.T) { - testName := "TestAddAndClaimInterest" - - ctx, am, _ := setupTest(t, 1) - accParam, _ := am.paramHolder.GetAccountParam(ctx) - accKey := types.AccountKey("accKey") - - createTestAccount(ctx, am, string(accKey)) - - err := am.AddInterest(ctx, accKey, c500) - if err != nil { - t.Errorf("%s: failed to add interest, got err %v", testName, err) - } - - reward := model.Reward{ - TotalIncome: c0, - OriginalIncome: c0, - FrictionIncome: c0, - InflationIncome: c0, - UnclaimReward: c0, - Interest: c500, - } - checkAccountReward(t, ctx, testName, accKey, reward) - - bank := model.AccountBank{ - Saving: accParam.RegisterFee, - NumOfTx: 1, - NumOfReward: 0, - CoinDay: accParam.RegisterFee, - } - checkBankKVByUsername(t, ctx, testName, accKey, bank) - - err = am.ClaimInterest(ctx, accKey) - if err != nil { - t.Errorf("%s: failed to add claim interest, got err %v", testName, err) - } - - bank.Saving = accParam.RegisterFee.Plus(c500) - bank.NumOfTx += 1 - checkBankKVByUsername(t, ctx, testName, accKey, bank) - - reward = model.Reward{ - TotalIncome: c0, - OriginalIncome: c0, - FrictionIncome: c0, - InflationIncome: c0, - UnclaimReward: c0, - Interest: c0, } checkAccountReward(t, ctx, testName, accKey, reward) } diff --git a/x/account/model/account.go b/x/account/model/account.go index 3d24858d..5fd55c61 100644 --- a/x/account/model/account.go +++ b/x/account/model/account.go @@ -88,7 +88,6 @@ type FollowingMeta struct { // Reward - get from the inflation pool type Reward struct { - Interest types.Coin `json:"interest"` TotalIncome types.Coin `json:"total_income"` OriginalIncome types.Coin `json:"original_income"` FrictionIncome types.Coin `json:"friction_income"` diff --git a/x/account/model/storage_test.go b/x/account/model/storage_test.go index 7aead791..1062bf7f 100644 --- a/x/account/model/storage_test.go +++ b/x/account/model/storage_test.go @@ -90,7 +90,6 @@ func TestAccountReward(t *testing.T) { ctx := getContext() reward := Reward{ - Interest: types.NewCoinFromInt64(0), TotalIncome: types.NewCoinFromInt64(0), OriginalIncome: types.NewCoinFromInt64(0), FrictionIncome: types.NewCoinFromInt64(0), diff --git a/x/account/msg.go b/x/account/msg.go index 13b496ef..011c5111 100644 --- a/x/account/msg.go +++ b/x/account/msg.go @@ -14,7 +14,6 @@ import ( var _ types.Msg = FollowMsg{} var _ types.Msg = UnfollowMsg{} var _ types.Msg = ClaimMsg{} -var _ types.Msg = ClaimInterestMsg{} var _ types.Msg = TransferMsg{} var _ types.Msg = RecoverMsg{} var _ types.Msg = RegisterMsg{} @@ -47,11 +46,6 @@ type ClaimMsg struct { Username types.AccountKey `json:"username"` } -// ClaimInterestMsg - claim interest generated from lino power -type ClaimInterestMsg struct { - Username types.AccountKey `json:"username"` -} - // RecoverMsg - replace three public keys type RecoverMsg struct { Username types.AccountKey `json:"username"` @@ -221,53 +215,6 @@ func (msg ClaimMsg) GetConsumeAmount() types.Coin { return types.NewCoinFromInt64(0) } -// NewClaimInterestMsg - return a ClaimInterestMsg -func NewClaimInterestMsg(username string) ClaimInterestMsg { - return ClaimInterestMsg{ - Username: types.AccountKey(username), - } -} - -// Type - implements sdk.Msg -func (msg ClaimInterestMsg) Type() string { return types.AccountRouterName } - -// ValidateBasic - implements sdk.Msg -func (msg ClaimInterestMsg) ValidateBasic() sdk.Error { - if len(msg.Username) < types.MinimumUsernameLength || - len(msg.Username) > types.MaximumUsernameLength { - return ErrInvalidUsername("illegal length") - } - return nil -} - -func (msg ClaimInterestMsg) String() string { - return fmt.Sprintf("ClaimInterestMsg{Username:%v}", msg.Username) -} - -// GetPermission - implements types.Msg -func (msg ClaimInterestMsg) GetPermission() types.Permission { - return types.AppPermission -} - -// GetSignBytes - implements sdk.Msg -func (msg ClaimInterestMsg) GetSignBytes() []byte { - b, err := msgCdc.MarshalJSON(msg) // XXX: ensure some canonical form - if err != nil { - panic(err) - } - return b -} - -// GetSigners - implements sdk.Msg -func (msg ClaimInterestMsg) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{sdk.AccAddress(msg.Username)} -} - -// GetConsumeAmount - implements types.Msg -func (msg ClaimInterestMsg) GetConsumeAmount() types.Coin { - return types.NewCoinFromInt64(0) -} - // NewTransferMsg - return a TransferMsg func NewTransferMsg(sender, receiver string, amount types.LNO, memo string) TransferMsg { return TransferMsg{ diff --git a/x/account/msg_test.go b/x/account/msg_test.go index 19478c80..6d1fcd5e 100644 --- a/x/account/msg_test.go +++ b/x/account/msg_test.go @@ -269,40 +269,6 @@ func TestClaimMsg(t *testing.T) { } } -func TestClaimInterestMsg(t *testing.T) { - testCases := map[string]struct { - msg ClaimInterestMsg - wantCode sdk.CodeType - }{ - "normal case": { - msg: NewClaimInterestMsg("test"), - wantCode: sdk.CodeOK, - }, - "invalid claim interest - Username is too short": { - msg: NewClaimInterestMsg("te"), - wantCode: types.CodeInvalidUsername, - }, - "invalid claim interest - Username is too long": { - msg: NewClaimInterestMsg("testtesttesttesttesttest"), - wantCode: types.CodeInvalidUsername, - }, - } - - for testName, tc := range testCases { - got := tc.msg.ValidateBasic() - - if got == nil { - if tc.wantCode != sdk.CodeOK { - t.Errorf("%s: diff error: got %v, want %v", testName, tc.wantCode, tc.wantCode) - } - continue - } - if got.Code() != tc.wantCode { - t.Errorf("%s: diff error code: got %v, want %v", testName, got.Code(), tc.wantCode) - } - } -} - func TestUpdateAccountMsg(t *testing.T) { testCases := map[string]struct { msg UpdateAccountMsg diff --git a/x/account/wire.go b/x/account/wire.go index 1d1debfe..72ae1b51 100644 --- a/x/account/wire.go +++ b/x/account/wire.go @@ -11,7 +11,6 @@ func RegisterWire(cdc *wire.Codec) { cdc.RegisterConcrete(UnfollowMsg{}, "lino/unfollow", nil) cdc.RegisterConcrete(TransferMsg{}, "lino/transfer", nil) cdc.RegisterConcrete(ClaimMsg{}, "lino/claim", nil) - cdc.RegisterConcrete(ClaimInterestMsg{}, "lino/claimInterest", nil) cdc.RegisterConcrete(RecoverMsg{}, "lino/recover", nil) cdc.RegisterConcrete(UpdateAccountMsg{}, "lino/updateAcc", nil) } diff --git a/x/post/event_test.go b/x/post/event_test.go index c4d86568..3d482c7b 100644 --- a/x/post/event_test.go +++ b/x/post/event_test.go @@ -65,7 +65,6 @@ func TestRewardEvent(t *testing.T) { FrictionIncome: types.NewCoinFromInt64(15), InflationIncome: types.NewCoinFromInt64(50), UnclaimReward: types.NewCoinFromInt64(50), - Interest: types.NewCoinFromInt64(0), }, expectVoterDeposit: types.NewCoinFromInt64(50), }, @@ -97,7 +96,6 @@ func TestRewardEvent(t *testing.T) { FrictionIncome: types.NewCoinFromInt64(15), InflationIncome: types.NewCoinFromInt64(0), UnclaimReward: types.NewCoinFromInt64(0), - Interest: types.NewCoinFromInt64(0), }, expectVoterDeposit: types.NewCoinFromInt64(1), }, @@ -129,7 +127,6 @@ func TestRewardEvent(t *testing.T) { FrictionIncome: types.NewCoinFromInt64(15), InflationIncome: types.NewCoinFromInt64(50), UnclaimReward: types.NewCoinFromInt64(50), - Interest: types.NewCoinFromInt64(0), }, expectVoterDeposit: types.NewCoinFromInt64(50), }, @@ -162,7 +159,6 @@ func TestRewardEvent(t *testing.T) { FrictionIncome: types.NewCoinFromInt64(15), InflationIncome: types.NewCoinFromInt64(0), UnclaimReward: types.NewCoinFromInt64(0), - Interest: types.NewCoinFromInt64(0), }, expectVoterDeposit: types.NewCoinFromInt64(0), }, diff --git a/x/post/handler_test.go b/x/post/handler_test.go index c38da0d7..6431010d 100644 --- a/x/post/handler_test.go +++ b/x/post/handler_test.go @@ -681,7 +681,6 @@ func TestHandlerPostDonate(t *testing.T) { tc.expectAuthorReward.FrictionIncome = types.NewCoinFromInt64(0) tc.expectAuthorReward.InflationIncome = types.NewCoinFromInt64(0) tc.expectAuthorReward.UnclaimReward = types.NewCoinFromInt64(0) - tc.expectAuthorReward.Interest = types.NewCoinFromInt64(0) if !assert.Equal(t, tc.expectAuthorReward, *reward) { t.Errorf("%s: diff reward, got %v, want %v", tc.testName, *reward, tc.expectAuthorReward) } diff --git a/x/vote/handler.go b/x/vote/handler.go index c00880cf..97bf0195 100644 --- a/x/vote/handler.go +++ b/x/vote/handler.go @@ -24,6 +24,8 @@ func NewHandler(vm VoteManager, am acc.AccountManager, gm global.GlobalManager, return handleDelegateMsg(ctx, vm, gm, am, rm, msg) case DelegatorWithdrawMsg: return handleDelegatorWithdrawMsg(ctx, vm, gm, am, rm, msg) + case ClaimInterestMsg: + return handleClaimInterestMsg(ctx, vm, gm, am, msg) default: errMsg := fmt.Sprintf("Unrecognized vote msg type: %v", reflect.TypeOf(msg).Name()) return sdk.ErrUnknownRequest(errMsg).Result() @@ -162,6 +164,22 @@ func handleDelegatorWithdrawMsg( return sdk.Result{} } +func handleClaimInterestMsg(ctx sdk.Context, vm VoteManager, gm global.GlobalManager, am acc.AccountManager, msg ClaimInterestMsg) sdk.Result { + if err := calculateAndAddInterest(ctx, vm, gm, am, msg.Username); err != nil { + return err.Result() + } + // claim interest + interest, err := vm.ClaimInterest(ctx, msg.Username) + if err != nil { + return err.Result() + } + if err := am.AddSavingCoin( + ctx, msg.Username, interest, "", "", types.ClaimInterest); err != nil { + return err.Result() + } + return sdk.Result{} +} + func AddStake( ctx sdk.Context, username types.AccountKey, stake types.Coin, vm VoteManager, gm global.GlobalManager, am acc.AccountManager, rm rep.ReputationManager) sdk.Error { @@ -224,7 +242,11 @@ func calculateAndAddInterest(ctx sdk.Context, vm VoteManager, gm global.GlobalMa return err } - if err := am.AddInterest(ctx, name, interest); err != nil { + if err := vm.AddInterest(ctx, name, interest); err != nil { + return err + } + + if err := vm.SetLinoStakeLastChangedAt(ctx, name, ctx.BlockHeader().Time.Unix()); err != nil { return err } diff --git a/x/vote/manager.go b/x/vote/manager.go index 8868a173..6ce708a0 100644 --- a/x/vote/manager.go +++ b/x/vote/manager.go @@ -262,6 +262,35 @@ func (vm VoteManager) DelegatorWithdraw( return nil } +// ClaimInterest - add lino power interst to user balance +func (vm VoteManager) ClaimInterest( + ctx sdk.Context, username types.AccountKey) (types.Coin, sdk.Error) { + voter, err := vm.storage.GetVoter(ctx, username) + if err != nil { + return types.NewCoinFromInt64(0), err + } + claimedInterest := voter.Interest + voter.Interest = types.NewCoinFromInt64(0) + if err := vm.storage.SetVoter(ctx, username, voter); err != nil { + return types.NewCoinFromInt64(0), err + } + return claimedInterest, nil +} + +// AddInterest - add interst +func (vm VoteManager) AddInterest( + ctx sdk.Context, username types.AccountKey, interest types.Coin) sdk.Error { + voter, err := vm.storage.GetVoter(ctx, username) + if err != nil { + return err + } + voter.Interest = voter.Interest.Plus(interest) + if err := vm.storage.SetVoter(ctx, username, voter); err != nil { + return err + } + return nil +} + // GetVotingPower - get voter voting power func (vm VoteManager) GetVotingPower(ctx sdk.Context, voterName types.AccountKey) (types.Coin, sdk.Error) { voter, err := vm.storage.GetVoter(ctx, voterName) @@ -323,6 +352,19 @@ func (vm VoteManager) GetLinoStakeLastChangedAt(ctx sdk.Context, accKey types.Ac return voter.LastPowerChangeAt, nil } +// SetLinoStakeLastChangedAt - set linoStake last changed time +func (vm VoteManager) SetLinoStakeLastChangedAt(ctx sdk.Context, accKey types.AccountKey, lastChangedAt int64) sdk.Error { + voter, err := vm.storage.GetVoter(ctx, accKey) + if err != nil { + return err + } + voter.LastPowerChangeAt = lastChangedAt + if err := vm.storage.SetVoter(ctx, accKey, voter); err != nil { + return err + } + return nil +} + // GetAllDelegators - get all delegators of a voter func (vm VoteManager) GetAllDelegators(ctx sdk.Context, voterName types.AccountKey) ([]types.AccountKey, sdk.Error) { return vm.storage.GetAllDelegators(ctx, voterName) diff --git a/x/vote/manager_test.go b/x/vote/manager_test.go index 50f56b51..916318ac 100644 --- a/x/vote/manager_test.go +++ b/x/vote/manager_test.go @@ -80,6 +80,48 @@ func TestCanBecomeValidator(t *testing.T) { } } +func TestAddAndClaimInterest(t *testing.T) { + testName := "TestAddAndClaimInterest" + ctx, am, vm, _, _ := setupTest(t, 0) + + accKey := types.AccountKey("accKey") + minBalance := types.NewCoinFromInt64(1000 * types.Decimals) + createTestAccount(ctx, am, "user1", minBalance) + + err := vm.AddVoter(ctx, accKey, c100) + if err != nil { + t.Errorf("%s: failed to add voter, got err %v", testName, err) + } + + err = vm.AddInterest(ctx, accKey, c500) + if err != nil { + t.Errorf("%s: failed to add interest, got err %v", testName, err) + } + + voter, err := vm.storage.GetVoter(ctx, accKey) + if err != nil { + t.Errorf("%s: failed to get voter, got err %v", testName, err) + } + + if !assert.Equal(t, c500, voter.Interest) { + t.Errorf("%s: diff interest", testName) + } + + _, err = vm.ClaimInterest(ctx, accKey) + if err != nil { + t.Errorf("%s: failed to add claim interest, got err %v", testName, err) + } + voter, err = vm.storage.GetVoter(ctx, accKey) + if err != nil { + t.Errorf("%s: failed to get voter, got err %v", testName, err) + } + + if !assert.Equal(t, true, voter.Interest.IsZero()) { + t.Errorf("%s: diff interest", testName) + } + +} + func TestIsInValidatorList(t *testing.T) { ctx, am, vm, _, _ := setupTest(t, 0) minBalance := types.NewCoinFromInt64(1 * types.Decimals) diff --git a/x/vote/model/storage_test.go b/x/vote/model/storage_test.go index f5f7d556..4f7686be 100644 --- a/x/vote/model/storage_test.go +++ b/x/vote/model/storage_test.go @@ -173,10 +173,12 @@ func TestVoter(t *testing.T) { user := types.AccountKey("user") voter := Voter{ - Username: user, - LinoStake: types.NewCoinFromInt64(1000), - DelegatedPower: types.NewCoinFromInt64(10000), - DelegateToOthers: types.NewCoinFromInt64(10000), + Username: user, + LinoStake: types.NewCoinFromInt64(1000), + DelegatedPower: types.NewCoinFromInt64(10000), + DelegateToOthers: types.NewCoinFromInt64(10000), + LastPowerChangeAt: 0, + Interest: types.NewCoinFromInt64(0), } err := vs.SetVoter(ctx, user, &voter) assert.Nil(t, err) diff --git a/x/vote/model/voter.go b/x/vote/model/voter.go index 4ca08f55..5cf27d48 100644 --- a/x/vote/model/voter.go +++ b/x/vote/model/voter.go @@ -11,6 +11,7 @@ type Voter struct { DelegatedPower types.Coin `json:"delegated_power"` DelegateToOthers types.Coin `json:"delegate_to_others"` LastPowerChangeAt int64 `json:"last_power_change_at"` + Interest types.Coin `json:"interest"` } // Vote - a vote is created by a voter to a proposal diff --git a/x/vote/msg.go b/x/vote/msg.go index 89ab9cd8..c289e258 100644 --- a/x/vote/msg.go +++ b/x/vote/msg.go @@ -13,6 +13,7 @@ var _ types.Msg = StakeInMsg{} var _ types.Msg = StakeOutMsg{} var _ types.Msg = DelegateMsg{} var _ types.Msg = DelegatorWithdrawMsg{} +var _ types.Msg = ClaimInterestMsg{} // StakeInMsg - voter deposit type StakeInMsg struct { @@ -40,6 +41,11 @@ type DelegatorWithdrawMsg struct { Amount types.LNO `json:"amount"` } +// ClaimInterestMsg - claim interest generated from lino power +type ClaimInterestMsg struct { + Username types.AccountKey `json:"username"` +} + // NewStakeInMsg - return a StakeInMsg func NewStakeInMsg(username string, deposit types.LNO) StakeInMsg { return StakeInMsg{ @@ -255,3 +261,50 @@ func (msg DelegatorWithdrawMsg) GetSigners() []sdk.AccAddress { func (msg DelegatorWithdrawMsg) GetConsumeAmount() types.Coin { return types.NewCoinFromInt64(0) } + +// NewClaimInterestMsg - return a ClaimInterestMsg +func NewClaimInterestMsg(username string) ClaimInterestMsg { + return ClaimInterestMsg{ + Username: types.AccountKey(username), + } +} + +// Type - implements sdk.Msg +func (msg ClaimInterestMsg) Type() string { return types.VoteRouterName } + +// ValidateBasic - implements sdk.Msg +func (msg ClaimInterestMsg) ValidateBasic() sdk.Error { + if len(msg.Username) < types.MinimumUsernameLength || + len(msg.Username) > types.MaximumUsernameLength { + return ErrInvalidUsername() + } + return nil +} + +func (msg ClaimInterestMsg) String() string { + return fmt.Sprintf("ClaimInterestMsg{Username:%v}", msg.Username) +} + +// GetPermission - implements types.Msg +func (msg ClaimInterestMsg) GetPermission() types.Permission { + return types.AppPermission +} + +// GetSignBytes - implements sdk.Msg +func (msg ClaimInterestMsg) GetSignBytes() []byte { + b, err := msgCdc.MarshalJSON(msg) // XXX: ensure some canonical form + if err != nil { + panic(err) + } + return b +} + +// GetSigners - implements sdk.Msg +func (msg ClaimInterestMsg) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{sdk.AccAddress(msg.Username)} +} + +// GetConsumeAmount - implements types.Msg +func (msg ClaimInterestMsg) GetConsumeAmount() types.Coin { + return types.NewCoinFromInt64(0) +} diff --git a/x/vote/msg_test.go b/x/vote/msg_test.go index 5e087a8e..55337bfb 100644 --- a/x/vote/msg_test.go +++ b/x/vote/msg_test.go @@ -40,6 +40,40 @@ func TestStakeInMsg(t *testing.T) { } } +func TestClaimInterestMsg(t *testing.T) { + testCases := map[string]struct { + msg ClaimInterestMsg + wantCode sdk.CodeType + }{ + "normal case": { + msg: NewClaimInterestMsg("test"), + wantCode: sdk.CodeOK, + }, + "invalid claim interest - Username is too short": { + msg: NewClaimInterestMsg("te"), + wantCode: types.CodeInvalidUsername, + }, + "invalid claim interest - Username is too long": { + msg: NewClaimInterestMsg("testtesttesttesttesttest"), + wantCode: types.CodeInvalidUsername, + }, + } + + for testName, tc := range testCases { + got := tc.msg.ValidateBasic() + + if got == nil { + if tc.wantCode != sdk.CodeOK { + t.Errorf("%s: diff error: got %v, want %v", testName, tc.wantCode, tc.wantCode) + } + continue + } + if got.Code() != tc.wantCode { + t.Errorf("%s: diff error code: got %v, want %v", testName, got.Code(), tc.wantCode) + } + } +} + func TestStakeOutMsg(t *testing.T) { testCases := []struct { testName string diff --git a/x/vote/test_util.go b/x/vote/test_util.go index cd1a6be9..3b6f6eef 100644 --- a/x/vote/test_util.go +++ b/x/vote/test_util.go @@ -27,6 +27,9 @@ var ( testGlobalKVStoreKey = sdk.NewKVStoreKey("global") testParamKVStoreKey = sdk.NewKVStoreKey("param") testRepKVStoreKey = sdk.NewKVStoreKey("reputation") + + c100 = types.NewCoinFromInt64(100 * types.Decimals) + c500 = types.NewCoinFromInt64(500 * types.Decimals) ) func initGlobalManager(ctx sdk.Context, gm global.GlobalManager) error { diff --git a/x/vote/wire.go b/x/vote/wire.go index 5b0f16a3..9905b89c 100644 --- a/x/vote/wire.go +++ b/x/vote/wire.go @@ -10,6 +10,7 @@ func RegisterWire(cdc *wire.Codec) { cdc.RegisterConcrete(StakeOutMsg{}, "lino/stakeOut", nil) cdc.RegisterConcrete(DelegateMsg{}, "lino/delegate", nil) cdc.RegisterConcrete(DelegatorWithdrawMsg{}, "lino/delegateWithdraw", nil) + cdc.RegisterConcrete(ClaimInterestMsg{}, "lino/claimInterest", nil) } var msgCdc = wire.NewCodec()