refactor(x/accounts/defaults/lockup): Add lockup unit tests (#20721)

This commit is contained in:
son trinh 2024-07-09 16:38:40 +07:00 committed by GitHub
parent 25e99c54ba
commit c349885084
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 880 additions and 13 deletions

View File

@ -0,0 +1,235 @@
package lockup
import (
"context"
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/log"
"cosmossdk.io/math"
lockuptypes "cosmossdk.io/x/accounts/defaults/lockup/types"
)
func setupContinousAccount(t *testing.T, ctx context.Context, ss store.KVStoreService) *ContinuousLockingAccount {
t.Helper()
deps := makeMockDependencies(ss)
owner := "owner"
acc, err := NewContinuousLockingAccount(deps)
require.NoError(t, err)
_, err = acc.Init(ctx, &lockuptypes.MsgInitLockupAccount{
Owner: owner,
EndTime: time.Now().Add(time.Minute * 2),
StartTime: time.Now(),
})
require.NoError(t, err)
return acc
}
func TestContinousAccountDelegate(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupContinousAccount(t, sdkCtx, ss)
_, err := acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err := acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked half of the original locking amount
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 1),
})
_, err = acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(5)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(5)))
delFree, err := acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.NewInt(1)))
}
func TestContinousAccountUndelegate(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupContinousAccount(t, sdkCtx, ss)
// Delegate first
_, err := acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err := acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
// Undelegate
_, err = acc.Undelegate(sdkCtx, &lockuptypes.MsgUndelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.ZeroInt()))
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked half of the original locking amount
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 1),
})
_, err = acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(6)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(5)))
delFree, err := acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.NewInt(1)))
// Undelegate
_, err = acc.Undelegate(sdkCtx, &lockuptypes.MsgUndelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(4)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(2)))
delFree, err = acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.ZeroInt()))
}
func TestContinousAccountSendCoins(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupContinousAccount(t, sdkCtx, ss)
_, err := acc.SendCoins(sdkCtx, &lockuptypes.MsgSend{
Sender: "owner",
ToAddress: "receiver",
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(5))),
})
require.Error(t, err)
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked half of the original locking amount
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 1),
})
_, err = acc.SendCoins(sdkCtx, &lockuptypes.MsgSend{
Sender: "owner",
ToAddress: "receiver",
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(5))),
})
require.NoError(t, err)
}
func TestContinousAccountWithdrawUnlockedCoins(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupContinousAccount(t, sdkCtx, ss)
_, err := acc.WithdrawUnlockedCoins(sdkCtx, &lockuptypes.MsgWithdraw{
Withdrawer: "owner",
ToAddress: "receiver",
Denoms: []string{"test"},
})
require.Error(t, err)
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked half of the original locking amount
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 1),
})
_, err = acc.WithdrawUnlockedCoins(sdkCtx, &lockuptypes.MsgWithdraw{
Withdrawer: "owner",
ToAddress: "receiver",
Denoms: []string{"test"},
})
require.NoError(t, err)
}
func TestContinousAccountGetLockCoinInfo(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupContinousAccount(t, sdkCtx, ss)
unlocked, locked, err := acc.GetLockCoinsInfo(sdkCtx, time.Now())
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.ZeroInt()))
require.True(t, locked.AmountOf("test").Equal(math.NewInt(10)))
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// unlocked half locked token
unlocked, locked, err = acc.GetLockCoinsInfo(sdkCtx, startTime.Add(time.Minute*1))
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.NewInt(5)))
require.True(t, locked.AmountOf("test").Equal(math.NewInt(5)))
// unlocked full locked token
unlocked, locked, err = acc.GetLockCoinsInfo(sdkCtx, startTime.Add(time.Minute*2))
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.NewInt(10)))
require.True(t, locked.AmountOf("test").Equal(math.ZeroInt()))
}

View File

@ -0,0 +1,224 @@
package lockup
import (
"context"
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/log"
"cosmossdk.io/math"
lockuptypes "cosmossdk.io/x/accounts/defaults/lockup/types"
)
func setupDelayedAccount(t *testing.T, ctx context.Context, ss store.KVStoreService) *DelayedLockingAccount {
t.Helper()
deps := makeMockDependencies(ss)
owner := "owner"
acc, err := NewDelayedLockingAccount(deps)
require.NoError(t, err)
_, err = acc.Init(ctx, &lockuptypes.MsgInitLockupAccount{
Owner: owner,
EndTime: time.Now().Add(time.Minute * 2),
})
require.NoError(t, err)
return acc
}
func TestDelayedAccountDelegate(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupDelayedAccount(t, sdkCtx, ss)
_, err := acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err := acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
endTime, err := acc.EndTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked all the original locking amount
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: endTime.Add(time.Second),
})
_, err = acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(5)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
delFree, err := acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.NewInt(5)))
}
func TestDelayedAccountUndelegate(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupDelayedAccount(t, sdkCtx, ss)
// Delegate first
_, err := acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err := acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
// Undelegate
_, err = acc.Undelegate(sdkCtx, &lockuptypes.MsgUndelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.ZeroInt()))
endTime, err := acc.EndTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked all the original locking amount
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: endTime.Add(time.Second),
})
_, err = acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(6)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.ZeroInt()))
delFree, err := acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.NewInt(6)))
// Undelegate
_, err = acc.Undelegate(sdkCtx, &lockuptypes.MsgUndelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(4)),
})
require.NoError(t, err)
delFree, err = acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.NewInt(2)))
}
func TestDelayedAccountSendCoins(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupDelayedAccount(t, sdkCtx, ss)
_, err := acc.SendCoins(sdkCtx, &lockuptypes.MsgSend{
Sender: "owner",
ToAddress: "receiver",
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(5))),
})
require.Error(t, err)
endTime, err := acc.EndTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked all the original locking amount
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: endTime.Add(time.Second),
})
_, err = acc.SendCoins(sdkCtx, &lockuptypes.MsgSend{
Sender: "owner",
ToAddress: "receiver",
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(5))),
})
require.NoError(t, err)
}
func TestDelayedAccountWithdrawUnlockedCoins(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupDelayedAccount(t, sdkCtx, ss)
_, err := acc.WithdrawUnlockedCoins(sdkCtx, &lockuptypes.MsgWithdraw{
Withdrawer: "owner",
ToAddress: "receiver",
Denoms: []string{"test"},
})
require.Error(t, err)
endTime, err := acc.EndTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked all the original locking amount
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: endTime.Add(time.Second),
})
_, err = acc.WithdrawUnlockedCoins(sdkCtx, &lockuptypes.MsgWithdraw{
Withdrawer: "owner",
ToAddress: "receiver",
Denoms: []string{"test"},
})
require.NoError(t, err)
}
func TestDelayedAccountGetLockCoinInfo(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupDelayedAccount(t, sdkCtx, ss)
unlocked, locked, err := acc.GetLockCoinsInfo(sdkCtx, time.Now())
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.ZeroInt()))
require.True(t, locked.AmountOf("test").Equal(math.NewInt(10)))
endTime, err := acc.EndTime.Get(sdkCtx)
require.NoError(t, err)
// unlocked full locked token
unlocked, locked, err = acc.GetLockCoinsInfo(sdkCtx, endTime.Add(time.Second*1))
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.NewInt(10)))
require.True(t, locked.AmountOf("test").Equal(math.ZeroInt()))
}

View File

@ -33,7 +33,7 @@ require (
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect
cosmossdk.io/api v0.7.5 // indirect
cosmossdk.io/errors v1.0.1
cosmossdk.io/log v1.3.1 // indirect
cosmossdk.io/log v1.3.1
cosmossdk.io/math v1.3.0
cosmossdk.io/store v1.1.1-0.20240418092142-896cdf1971bc // indirect
cosmossdk.io/x/auth v0.0.0-00010101000000-000000000000 // indirect
@ -81,7 +81,7 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/protobuf v1.5.4
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect

View File

@ -273,6 +273,8 @@ func TestQueryLockupAccountBaseInfo(t *testing.T) {
baseLockup := setup(t, ctx, ss)
_, err := baseLockup.QueryLockupAccountBaseInfo(ctx, &lockuptypes.QueryLockupAccountInfoRequest{})
res, err := baseLockup.QueryLockupAccountBaseInfo(ctx, &lockuptypes.QueryLockupAccountInfoRequest{})
require.Equal(t, res.OriginalLocking.AmountOf("test"), math.NewInt(10))
require.Equal(t, res.Owner, "owner")
require.NoError(t, err)
}

View File

@ -0,0 +1,274 @@
package lockup
import (
"context"
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/log"
"cosmossdk.io/math"
lockuptypes "cosmossdk.io/x/accounts/defaults/lockup/types"
)
func setupPeriodicAccount(t *testing.T, ctx context.Context, ss store.KVStoreService) *PeriodicLockingAccount {
t.Helper()
deps := makeMockDependencies(ss)
owner := "owner"
acc, err := NewPeriodicLockingAccount(deps)
require.NoError(t, err)
_, err = acc.Init(ctx, &lockuptypes.MsgInitPeriodicLockingAccount{
Owner: owner,
StartTime: time.Now(),
LockingPeriods: []lockuptypes.Period{
{
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(5))),
Length: time.Minute,
},
{
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(2))),
Length: time.Minute,
},
{
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(3))),
Length: time.Minute,
},
},
})
require.NoError(t, err)
return acc
}
func TestPeriodicAccountDelegate(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupPeriodicAccount(t, sdkCtx, ss)
_, err := acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err := acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked first period token
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 1),
})
_, err = acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(5)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(5)))
delFree, err := acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.NewInt(1)))
// Update context time to unlocked all token
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 3),
})
_, err = acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(4)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(5)))
delFree, err = acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.NewInt(5)))
}
func TestPeriodicAccountUndelegate(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupPeriodicAccount(t, sdkCtx, ss)
// Delegate first
_, err := acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err := acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
// Undelegate
_, err = acc.Undelegate(sdkCtx, &lockuptypes.MsgUndelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.ZeroInt()))
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked first period token
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 1),
})
_, err = acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(6)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(5)))
delFree, err := acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.NewInt(1)))
// Undelegate
_, err = acc.Undelegate(sdkCtx, &lockuptypes.MsgUndelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(4)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(2)))
delFree, err = acc.DelegatedFree.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delFree.Equal(math.ZeroInt()))
}
func TestPeriodicAccountSendCoins(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupPeriodicAccount(t, sdkCtx, ss)
_, err := acc.SendCoins(sdkCtx, &lockuptypes.MsgSend{
Sender: "owner",
ToAddress: "receiver",
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(5))),
})
require.Error(t, err)
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked first period token
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 1),
})
_, err = acc.SendCoins(sdkCtx, &lockuptypes.MsgSend{
Sender: "owner",
ToAddress: "receiver",
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(5))),
})
require.NoError(t, err)
}
func TestPeriodicAccountWithdrawUnlockedCoins(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupPeriodicAccount(t, sdkCtx, ss)
_, err := acc.WithdrawUnlockedCoins(sdkCtx, &lockuptypes.MsgWithdraw{
Withdrawer: "owner",
ToAddress: "receiver",
Denoms: []string{"test"},
})
require.Error(t, err)
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// Update context time to unlocked first period token
sdkCtx = sdkCtx.WithHeaderInfo(header.Info{
Time: startTime.Add(time.Minute * 1),
})
_, err = acc.WithdrawUnlockedCoins(sdkCtx, &lockuptypes.MsgWithdraw{
Withdrawer: "owner",
ToAddress: "receiver",
Denoms: []string{"test"},
})
require.NoError(t, err)
}
func TestPeriodicAccountGetLockCoinInfo(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupPeriodicAccount(t, sdkCtx, ss)
unlocked, locked, err := acc.GetLockCoinsInfo(sdkCtx, time.Now())
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.ZeroInt()))
require.True(t, locked.AmountOf("test").Equal(math.NewInt(10)))
startTime, err := acc.StartTime.Get(sdkCtx)
require.NoError(t, err)
// unlocked first period locked token
unlocked, locked, err = acc.GetLockCoinsInfo(sdkCtx, startTime.Add(time.Minute*1))
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.NewInt(5)))
require.True(t, locked.AmountOf("test").Equal(math.NewInt(5)))
// unlocked second period locked token
unlocked, locked, err = acc.GetLockCoinsInfo(sdkCtx, startTime.Add(time.Minute*2))
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.NewInt(7)))
require.True(t, locked.AmountOf("test").Equal(math.NewInt(3)))
// unlocked third period locked token
unlocked, locked, err = acc.GetLockCoinsInfo(sdkCtx, startTime.Add(time.Minute*3))
require.NoError(t, err)
require.True(t, unlocked.AmountOf("test").Equal(math.NewInt(10)))
require.True(t, locked.AmountOf("test").Equal(math.ZeroInt()))
}

View File

@ -100,8 +100,9 @@ func (plva PermanentLockingAccount) RegisterInitHandler(builder *accountstd.Init
func (plva PermanentLockingAccount) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) {
accountstd.RegisterExecuteHandler(builder, plva.Delegate)
accountstd.RegisterExecuteHandler(builder, plva.Undelegate)
accountstd.RegisterExecuteHandler(builder, plva.SendCoins)
plva.BaseLockup.RegisterExecuteHandlers(builder)
accountstd.RegisterExecuteHandler(builder, plva.WithdrawReward)
}
func (plva PermanentLockingAccount) RegisterQueryHandlers(builder *accountstd.QueryBuilder) {

View File

@ -0,0 +1,97 @@
package lockup
import (
"context"
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/log"
"cosmossdk.io/math"
lockuptypes "cosmossdk.io/x/accounts/defaults/lockup/types"
)
func setupPermanentAccount(t *testing.T, ctx context.Context, ss store.KVStoreService) *PermanentLockingAccount {
t.Helper()
deps := makeMockDependencies(ss)
owner := "owner"
acc, err := NewPermanentLockingAccount(deps)
require.NoError(t, err)
_, err = acc.Init(ctx, &lockuptypes.MsgInitLockupAccount{
Owner: owner,
})
require.NoError(t, err)
return acc
}
func TestPermanentAccountDelegate(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupPermanentAccount(t, sdkCtx, ss)
_, err := acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err := acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
}
func TestPermanentAccountUndelegate(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupPermanentAccount(t, sdkCtx, ss)
// Delegate first
_, err := acc.Delegate(sdkCtx, &lockuptypes.MsgDelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err := acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.NewInt(1)))
// Undelegate
_, err = acc.Undelegate(sdkCtx, &lockuptypes.MsgUndelegate{
Sender: "owner",
ValidatorAddress: "val_address",
Amount: sdk.NewCoin("test", math.NewInt(1)),
})
require.NoError(t, err)
delLocking, err = acc.DelegatedLocking.Get(ctx, "test")
require.NoError(t, err)
require.True(t, delLocking.Equal(math.ZeroInt()))
}
func TestPermanentAccountSendCoins(t *testing.T) {
ctx, ss := newMockContext(t)
sdkCtx := sdk.NewContext(nil, true, log.NewNopLogger()).WithContext(ctx).WithHeaderInfo(header.Info{
Time: time.Now(),
})
acc := setupPermanentAccount(t, sdkCtx, ss)
_, err := acc.SendCoins(sdkCtx, &lockuptypes.MsgSend{
Sender: "owner",
ToAddress: "receiver",
Amount: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(5))),
})
require.Error(t, err)
}

View File

@ -2,19 +2,25 @@ package lockup
import (
"context"
"fmt"
"testing"
gogoproto "github.com/cosmos/gogoproto/proto"
"github.com/golang/protobuf/proto" // nolint: staticcheck // needed because gogoproto.Merge does not work consistently. See NOTE: comments.
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/runtime/protoiface"
"cosmossdk.io/collections"
"cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/math"
"cosmossdk.io/x/accounts/accountstd"
banktypes "cosmossdk.io/x/bank/types"
distrtypes "cosmossdk.io/x/distribution/types"
stakingtypes "cosmossdk.io/x/staking/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -23,7 +29,11 @@ type ProtoMsg = protoiface.MessageV1
var TestFunds = sdk.NewCoins(sdk.NewCoin("test", math.NewInt(10)))
// mock statecodec
type mockStateCodec struct{}
type mockStateCodec struct {
codec.Codec
}
var _ codec.Codec = mockStateCodec{}
func (c mockStateCodec) Marshal(m gogoproto.Message) ([]byte, error) {
// Size() check can catch the typed nil value.
@ -53,13 +63,32 @@ type addressCodec struct{}
func (a addressCodec) StringToBytes(text string) ([]byte, error) { return []byte(text), nil }
func (a addressCodec) BytesToString(bz []byte) (string, error) { return string(bz), nil }
// mock header service
type headerService struct{}
func (h headerService) HeaderInfo(ctx context.Context) header.Info {
return sdk.UnwrapSDKContext(ctx).HeaderInfo()
}
func newMockContext(t *testing.T) (context.Context, store.KVStoreService) {
t.Helper()
return accountstd.NewMockContext(
0, []byte("lockup_account"), []byte("sender"), TestFunds, func(ctx context.Context, sender []byte, msg, msgResp ProtoMsg) error {
return nil
}, func(ctx context.Context, sender []byte, msg ProtoMsg) (ProtoMsg, error) {
return nil, nil
typeUrl := sdk.MsgTypeURL(msg)
switch typeUrl {
case "/cosmos.staking.v1beta1.MsgDelegate":
return &stakingtypes.MsgDelegateResponse{}, nil
case "/cosmos.staking.v1beta1.MsgUndelegate":
return &stakingtypes.MsgUndelegate{}, nil
case "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward":
return &distrtypes.MsgWithdrawDelegatorRewardResponse{}, nil
case "/cosmos.bank.v1beta1.MsgSend":
return &banktypes.MsgSendResponse{}, nil
default:
return nil, fmt.Errorf("unrecognized request type")
}
}, func(ctx context.Context, req, resp ProtoMsg) error {
_, ok := req.(*banktypes.QueryBalanceRequest)
if !ok {
@ -70,14 +99,16 @@ func newMockContext(t *testing.T) (context.Context, store.KVStoreService) {
BondDenom: "test",
},
})
return nil
} else {
// NOTE: using gogoproto.Merge will fail for some reason unknown to me, but
// using proto.Merge with gogo messages seems to work fine.
proto.Merge(resp.(gogoproto.Message), &banktypes.QueryBalanceResponse{
Balance: &(sdk.Coin{
Denom: "test",
Amount: TestFunds.AmountOf("test"),
}),
})
}
gogoproto.Merge(resp.(gogoproto.Message), &banktypes.QueryBalanceResponse{
Balance: &sdk.Coin{
Denom: "test",
Amount: math.NewInt(5),
},
})
return nil
},
@ -91,5 +122,8 @@ func makeMockDependencies(storeservice store.KVStoreService) accountstd.Dependen
SchemaBuilder: sb,
AddressCodec: addressCodec{},
LegacyStateCodec: mockStateCodec{},
Environment: appmodule.Environment{
HeaderService: headerService{},
},
}
}