Skip to content

Commit

Permalink
fix(x/accounts/default/lockup): Lockup account track undelegation whe…
Browse files Browse the repository at this point in the history
…n unbonding entry is mature (#22254)

Co-authored-by: Alexander Peters <[email protected]>
  • Loading branch information
sontrinh16 and alpe authored Dec 17, 2024
1 parent 0a89178 commit def23f0
Show file tree
Hide file tree
Showing 26 changed files with 5,374 additions and 2,750 deletions.
1,370 changes: 1,323 additions & 47 deletions api/cosmos/accounts/defaults/lockup/v1/lockup.pulsar.go

Large diffs are not rendered by default.

2,492 changes: 2,238 additions & 254 deletions api/cosmos/accounts/defaults/lockup/v1/query.pulsar.go

Large diffs are not rendered by default.

1,391 changes: 38 additions & 1,353 deletions api/cosmos/accounts/defaults/lockup/v1/tx.pulsar.go

Large diffs are not rendered by default.

50 changes: 16 additions & 34 deletions tests/integration/accounts/lockup/continous_lockup_test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ func (s *IntegrationTestSuite) TestContinuousLockingAccount() {
ctx := sdk.NewContext(app.CommitMultiStore(), false, app.Logger()).WithHeaderInfo(header.Info{
Time: currentTime,
})
s.setupStakingParams(ctx, app)
ownerAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
s.fundAccount(app, ctx, accOwner, sdk.Coins{sdk.NewCoin("stake", math.NewInt(1000000))})
randAcc := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
withdrawAcc := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())

_, accountAddr, err := app.AccountsKeeper.Init(ctx, lockupaccount.CONTINUOUS_LOCKING_ACCOUNT, accOwner, &types.MsgInitLockupAccount{
Owner: ownerAddrStr,
Expand Down Expand Up @@ -62,19 +62,6 @@ func (s *IntegrationTestSuite) TestContinuousLockingAccount() {
err := s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NotNil(t, err)
})
t.Run("error - execute withdraw message, no withdrawable token", func(t *testing.T) {
ownerAddr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
withdrawAddr, err := app.AuthKeeper.AddressCodec().BytesToString(withdrawAcc)
require.NoError(t, err)
msg := &types.MsgWithdraw{
Withdrawer: ownerAddr,
ToAddress: withdrawAddr,
Denoms: []string{"stake"},
}
err = s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NotNil(t, err)
})

// Update context time
// 12 sec = 1/5 of a minute so 200stake should be released
Expand All @@ -95,23 +82,7 @@ func (s *IntegrationTestSuite) TestContinuousLockingAccount() {
balance := app.BankKeeper.GetBalance(ctx, randAcc, "stake")
require.True(t, balance.Amount.Equal(math.NewInt(100)))
})
t.Run("ok - execute withdraw message", func(t *testing.T) {
ownerAddr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
withdrawAddr, err := app.AuthKeeper.AddressCodec().BytesToString(withdrawAcc)
require.NoError(t, err)
msg := &types.MsgWithdraw{
Withdrawer: ownerAddr,
ToAddress: withdrawAddr,
Denoms: []string{"stake"},
}
err = s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NoError(t, err)

// withdrawable amount should be 200 - 100 = 100stake
balance := app.BankKeeper.GetBalance(ctx, withdrawAcc, "stake")
require.True(t, balance.Amount.Equal(math.NewInt(100)))
})
t.Run("ok - execute delegate message", func(t *testing.T) {
msg := &types.MsgDelegate{
Sender: ownerAddrStr,
Expand Down Expand Up @@ -163,17 +134,22 @@ func (s *IntegrationTestSuite) TestContinuousLockingAccount() {
require.NoError(t, err)
require.Equal(t, len(ubd.Entries), 1)

// check if tracking is updated accordingly
lockupAccountInfoResponse := s.queryLockupAccInfo(ctx, app, accountAddr)
delLocking := lockupAccountInfoResponse.DelegatedLocking
require.True(t, delLocking.AmountOf("stake").Equal(math.ZeroInt()))
// check if an entry is added
unbondingEntriesResponse := s.queryUnbondingEntries(ctx, app, accountAddr, val.OperatorAddress)
entries := unbondingEntriesResponse.UnbondingEntries
require.True(t, entries[0].Amount.Amount.Equal(math.NewInt(100)))
require.True(t, entries[0].ValidatorAddress == val.OperatorAddress)
})

// Update context time to end time
ctx = ctx.WithHeaderInfo(header.Info{
Time: currentTime.Add(time.Minute),
})

// trigger endblock for staking to handle matured unbonding delegation
_, err = app.StakingKeeper.EndBlocker(ctx)
require.NoError(t, err)

// test if tracking delegate work perfectly
t.Run("ok - execute delegate message", func(t *testing.T) {
msg := &types.MsgDelegate{
Expand All @@ -196,8 +172,14 @@ func (s *IntegrationTestSuite) TestContinuousLockingAccount() {
// check if tracking is updated accordingly
lockupAccountInfoResponse := s.queryLockupAccInfo(ctx, app, accountAddr)
delLocking := lockupAccountInfoResponse.DelegatedLocking
// should be update as ubd entry is matured
require.True(t, delLocking.AmountOf("stake").Equal(math.ZeroInt()))
delFree := lockupAccountInfoResponse.DelegatedFree
require.True(t, delFree.AmountOf("stake").Equal(math.NewInt(100)))

// check if the entry is removed
unbondingEntriesResponse := s.queryUnbondingEntries(ctx, app, accountAddr, val.OperatorAddress)
entries := unbondingEntriesResponse.UnbondingEntries
require.Len(t, entries, 0)
})
}
56 changes: 20 additions & 36 deletions tests/integration/accounts/lockup/delayed_lockup_test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ func (s *IntegrationTestSuite) TestDelayedLockingAccount() {
ctx := sdk.NewContext(app.CommitMultiStore(), false, app.Logger()).WithHeaderInfo(header.Info{
Time: currentTime,
})
s.setupStakingParams(ctx, app)
ownerAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
s.fundAccount(app, ctx, accOwner, sdk.Coins{sdk.NewCoin("stake", math.NewInt(1000000))})
randAcc := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
withdrawAcc := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())

_, accountAddr, err := app.AccountsKeeper.Init(ctx, lockupaccount.DELAYED_LOCKING_ACCOUNT, accOwner, &types.MsgInitLockupAccount{
Owner: ownerAddrStr,
Expand Down Expand Up @@ -61,19 +61,6 @@ func (s *IntegrationTestSuite) TestDelayedLockingAccount() {
err := s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NotNil(t, err)
})
t.Run("error - execute withdraw message, no withdrawable token", func(t *testing.T) {
ownerAddr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
withdrawAddr, err := app.AuthKeeper.AddressCodec().BytesToString(withdrawAcc)
require.NoError(t, err)
msg := &types.MsgWithdraw{
Withdrawer: ownerAddr,
ToAddress: withdrawAddr,
Denoms: []string{"stake"},
}
err = s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NotNil(t, err)
})
t.Run("ok - execute delegate message", func(t *testing.T) {
msg := &types.MsgDelegate{
Sender: ownerAddrStr,
Expand Down Expand Up @@ -125,18 +112,24 @@ func (s *IntegrationTestSuite) TestDelayedLockingAccount() {
require.NoError(t, err)
require.Equal(t, len(ubd.Entries), 1)

// check if tracking is updated accordingly
lockupAccountInfoResponse := s.queryLockupAccInfo(ctx, app, accountAddr)
delLocking := lockupAccountInfoResponse.DelegatedLocking
require.True(t, delLocking.AmountOf("stake").Equal(math.ZeroInt()))
// check if an entry is added
unbondingEntriesResponse := s.queryUnbondingEntries(ctx, app, accountAddr, val.OperatorAddress)
entries := unbondingEntriesResponse.UnbondingEntries
require.True(t, entries[0].Amount.Amount.Equal(math.NewInt(100)))
require.True(t, entries[0].ValidatorAddress == val.OperatorAddress)
})

// Update context time
// After endtime fund should be unlock
// And unbond time elapsed
ctx = ctx.WithHeaderInfo(header.Info{
Time: currentTime.Add(time.Second * 61),
})

// trigger endblock for staking to handle matured unbonding delegation
_, err = app.StakingKeeper.EndBlocker(ctx)
require.NoError(t, err)

// Check if token is sendable after unlock
t.Run("ok - execute send message", func(t *testing.T) {
msg := &types.MsgSend{
Expand All @@ -149,24 +142,15 @@ func (s *IntegrationTestSuite) TestDelayedLockingAccount() {

balance := app.BankKeeper.GetBalance(ctx, randAcc, "stake")
require.True(t, balance.Amount.Equal(math.NewInt(100)))
})
// Test to withdraw all the remain funds to an account of choice
t.Run("ok - execute withdraw message", func(t *testing.T) {
ownerAddr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
withdrawAddr, err := app.AuthKeeper.AddressCodec().BytesToString(withdrawAcc)
require.NoError(t, err)
msg := &types.MsgWithdraw{
Withdrawer: ownerAddr,
ToAddress: withdrawAddr,
Denoms: []string{"stake"},
}
err = s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NoError(t, err)

// withdrawable amount should be
// 1000stake - 100stake( above sent amt ) - 100stake(above delegate amt) = 800stake
balance := app.BankKeeper.GetBalance(ctx, withdrawAcc, "stake")
require.True(t, balance.Amount.Equal(math.NewInt(800)))
// check if tracking ubd entry is updated accordingly
lockupAccountInfoResponse := s.queryLockupAccInfo(ctx, app, accountAddr)
delLocking := lockupAccountInfoResponse.DelegatedLocking
require.True(t, delLocking.AmountOf("stake").Equal(math.ZeroInt()))

// check if the entry is removed
unbondingEntriesResponse := s.queryUnbondingEntries(ctx, app, accountAddr, val.OperatorAddress)
entries := unbondingEntriesResponse.UnbondingEntries
require.Len(t, entries, 0)
})
}
55 changes: 18 additions & 37 deletions tests/integration/accounts/lockup/periodic_lockup_test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ func (s *IntegrationTestSuite) TestPeriodicLockingAccount() {
ctx := sdk.NewContext(app.CommitMultiStore(), false, app.Logger()).WithHeaderInfo(header.Info{
Time: currentTime,
})
s.setupStakingParams(ctx, app)
ownerAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
s.fundAccount(app, ctx, accOwner, sdk.Coins{sdk.NewCoin("stake", math.NewInt(1000000))})
randAcc := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
withdrawAcc := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())

_, accountAddr, err := app.AccountsKeeper.Init(ctx, lockupaccount.PERIODIC_LOCKING_ACCOUNT, accOwner, &types.MsgInitPeriodicLockingAccount{
Owner: ownerAddrStr,
Expand Down Expand Up @@ -75,19 +75,6 @@ func (s *IntegrationTestSuite) TestPeriodicLockingAccount() {
err := s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NotNil(t, err)
})
t.Run("error - execute withdraw message, no withdrawable token", func(t *testing.T) {
ownerAddr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
withdrawAddr, err := app.AuthKeeper.AddressCodec().BytesToString(withdrawAcc)
require.NoError(t, err)
msg := &types.MsgWithdraw{
Withdrawer: ownerAddr,
ToAddress: withdrawAddr,
Denoms: []string{"stake"},
}
err = s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NotNil(t, err)
})

// Update context time
// After first period 500stake should be unlock
Expand Down Expand Up @@ -115,25 +102,6 @@ func (s *IntegrationTestSuite) TestPeriodicLockingAccount() {
Time: currentTime.Add(time.Minute * 2),
})

t.Run("oke - execute withdraw message", func(t *testing.T) {
ownerAddr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
withdrawAddr, err := app.AuthKeeper.AddressCodec().BytesToString(withdrawAcc)
require.NoError(t, err)
msg := &types.MsgWithdraw{
Withdrawer: ownerAddr,
ToAddress: withdrawAddr,
Denoms: []string{"stake"},
}
err = s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NoError(t, err)

// withdrawable amount should be
// 1000stake - 500stake( above sent amt ) = 500stake
balance := app.BankKeeper.GetBalance(ctx, withdrawAcc, "stake")
require.True(t, balance.Amount.Equal(math.NewInt(500)))
})

t.Run("ok - execute delegate message", func(t *testing.T) {
msg := &types.MsgDelegate{
Sender: ownerAddrStr,
Expand Down Expand Up @@ -185,10 +153,11 @@ func (s *IntegrationTestSuite) TestPeriodicLockingAccount() {
require.NoError(t, err)
require.Equal(t, len(ubd.Entries), 1)

// check if tracking is updated accordingly
lockupAccountInfoResponse := s.queryLockupAccInfo(ctx, app, accountAddr)
delLocking := lockupAccountInfoResponse.DelegatedLocking
require.True(t, delLocking.AmountOf("stake").Equal(math.ZeroInt()))
// check if an entry is added
unbondingEntriesResponse := s.queryUnbondingEntries(ctx, app, accountAddr, val.OperatorAddress)
entries := unbondingEntriesResponse.UnbondingEntries
require.True(t, entries[0].Amount.Amount.Equal(math.NewInt(100)))
require.True(t, entries[0].ValidatorAddress == val.OperatorAddress)
})

// Update context time
Expand All @@ -197,6 +166,10 @@ func (s *IntegrationTestSuite) TestPeriodicLockingAccount() {
Time: currentTime.Add(time.Minute * 3),
})

// trigger endblock for staking to handle matured unbonding delegation
_, err = app.StakingKeeper.EndBlocker(ctx)
require.NoError(t, err)

t.Run("ok - execute delegate message", func(t *testing.T) {
msg := &types.MsgDelegate{
Sender: ownerAddrStr,
Expand All @@ -217,7 +190,15 @@ func (s *IntegrationTestSuite) TestPeriodicLockingAccount() {

// check if tracking is updated accordingly
lockupAccountInfoResponse := s.queryLockupAccInfo(ctx, app, accountAddr)
// check if matured ubd entry cleared
delLocking := lockupAccountInfoResponse.DelegatedLocking
require.True(t, delLocking.AmountOf("stake").Equal(math.ZeroInt()))
delFree := lockupAccountInfoResponse.DelegatedFree
require.True(t, delFree.AmountOf("stake").Equal(math.NewInt(100)))

// check if the entry is removed
unbondingEntriesResponse := s.queryUnbondingEntries(ctx, app, accountAddr, val.OperatorAddress)
entries := unbondingEntriesResponse.UnbondingEntries
require.Len(t, entries, 0)
})
}
49 changes: 45 additions & 4 deletions tests/integration/accounts/lockup/permanent_lockup_test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func (s *IntegrationTestSuite) TestPermanentLockingAccount() {
ctx := sdk.NewContext(app.CommitMultiStore(), false, app.Logger()).WithHeaderInfo(header.Info{
Time: currentTime,
})
s.setupStakingParams(ctx, app)
ownerAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(accOwner)
require.NoError(t, err)
s.fundAccount(app, ctx, accOwner, sdk.Coins{sdk.NewCoin("stake", math.NewInt(1000000))})
Expand Down Expand Up @@ -109,10 +110,11 @@ func (s *IntegrationTestSuite) TestPermanentLockingAccount() {
require.NoError(t, err)
require.Equal(t, len(ubd.Entries), 1)

// check if tracking is updated accordingly
lockupAccountInfoResponse := s.queryLockupAccInfo(ctx, app, accountAddr)
delLocking := lockupAccountInfoResponse.DelegatedLocking
require.True(t, delLocking.AmountOf("stake").Equal(math.ZeroInt()))
// check if an entry is added
unbondingEntriesResponse := s.queryUnbondingEntries(ctx, app, accountAddr, val.OperatorAddress)
entries := unbondingEntriesResponse.UnbondingEntries
require.True(t, entries[0].Amount.Amount.Equal(math.NewInt(100)))
require.True(t, entries[0].ValidatorAddress == val.OperatorAddress)
})

s.fundAccount(app, ctx, accountAddr, sdk.Coins{sdk.NewCoin("stake", math.NewInt(1000))})
Expand All @@ -129,4 +131,43 @@ func (s *IntegrationTestSuite) TestPermanentLockingAccount() {
balance := app.BankKeeper.GetBalance(ctx, randAcc, "stake")
require.True(t, balance.Amount.Equal(math.NewInt(100)))
})

// Update context time
ctx = ctx.WithHeaderInfo(header.Info{
Time: currentTime.Add(time.Second * 11),
})

// trigger endblock for staking to handle matured unbonding delegation
_, err = app.StakingKeeper.EndBlocker(ctx)
require.NoError(t, err)

t.Run("ok - execute delegate message", func(t *testing.T) {
msg := &types.MsgDelegate{
Sender: ownerAddrStr,
ValidatorAddress: val.OperatorAddress,
Amount: sdk.NewCoin("stake", math.NewInt(10)),
}
err = s.executeTx(ctx, msg, app, accountAddr, accOwner)
require.NoError(t, err)

valbz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.OperatorAddress)
require.NoError(t, err)

del, err := app.StakingKeeper.Delegations.Get(
ctx, collections.Join(sdk.AccAddress(accountAddr), sdk.ValAddress(valbz)),
)
require.NoError(t, err)
require.NotNil(t, del)

// check if tracking is updated accordingly
lockupAccountInfoResponse := s.queryLockupAccInfo(ctx, app, accountAddr)
delLocking := lockupAccountInfoResponse.DelegatedLocking
// matured ubd entry should be cleared so del locking should only be 10
require.True(t, delLocking.AmountOf("stake").Equal(math.NewInt(10)))

// check if the entry is removed
unbondingEntriesResponse := s.queryUnbondingEntries(ctx, app, accountAddr, val.OperatorAddress)
entries := unbondingEntriesResponse.UnbondingEntries
require.Len(t, entries, 0)
})
}
Loading

0 comments on commit def23f0

Please sign in to comment.