mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-27 00:35:18 +00:00
493ce0516f
Add upgrade handler Migrates from x/evmutil to x/precisebank: - Fractional balances - Reserve funds - Mints or burns coins to ensure fractional balances are fully backed. Initialize remainder if necessary to ensure valid state. E2E test with fixed kvtool
435 lines
13 KiB
Go
435 lines
13 KiB
Go
package app_test
|
|
|
|
import (
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
sdkmath "cosmossdk.io/math"
|
|
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/kava-labs/kava/app"
|
|
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
|
|
precisebankkeeper "github.com/kava-labs/kava/x/precisebank/keeper"
|
|
precisebanktypes "github.com/kava-labs/kava/x/precisebank/types"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMigrateEvmutilToPrecisebank(t *testing.T) {
|
|
// Full test case with all components together
|
|
tests := []struct {
|
|
name string
|
|
initialReserve sdkmath.Int
|
|
fractionalBalances []sdkmath.Int
|
|
}{
|
|
{
|
|
"no fractional balances",
|
|
sdkmath.NewInt(0),
|
|
[]sdkmath.Int{},
|
|
},
|
|
{
|
|
"sufficient reserve, 0 remainder",
|
|
// Accounts adding up to 2 int units, same as reserve
|
|
sdkmath.NewInt(2),
|
|
[]sdkmath.Int{
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"insufficient reserve, 0 remainder",
|
|
// Accounts adding up to 2 int units, but only 1 int unit in reserve
|
|
sdkmath.NewInt(1),
|
|
[]sdkmath.Int{
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"excess reserve, 0 remainder",
|
|
// Accounts adding up to 2 int units, but 3 int unit in reserve
|
|
sdkmath.NewInt(3),
|
|
[]sdkmath.Int{
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"sufficient reserve, non-zero remainder",
|
|
// Accounts adding up to 1.5 int units, same as reserve
|
|
sdkmath.NewInt(2),
|
|
[]sdkmath.Int{
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"insufficient reserve, non-zero remainder",
|
|
// Accounts adding up to 1.5 int units, less than reserve,
|
|
// Reserve should be 2 and remainder 0.5
|
|
sdkmath.NewInt(1),
|
|
[]sdkmath.Int{
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"excess reserve, non-zero remainder",
|
|
// Accounts adding up to 1.5 int units, 3 int units in reserve
|
|
sdkmath.NewInt(3),
|
|
[]sdkmath.Int{
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tApp := app.NewTestApp()
|
|
tApp.InitializeFromGenesisStates()
|
|
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
|
|
|
|
ak := tApp.GetAccountKeeper()
|
|
bk := tApp.GetBankKeeper()
|
|
evmuk := tApp.GetEvmutilKeeper()
|
|
pbk := tApp.GetPrecisebankKeeper()
|
|
|
|
reserveCoin := sdk.NewCoin(precisebanktypes.IntegerCoinDenom, tt.initialReserve)
|
|
err := bk.MintCoins(ctx, evmutiltypes.ModuleName, sdk.NewCoins(reserveCoin))
|
|
require.NoError(t, err)
|
|
|
|
oldReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(evmutiltypes.ModuleName)
|
|
newReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(precisebanktypes.ModuleName)
|
|
|
|
// Double check balances
|
|
oldReserveBalance := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
|
|
newReserveBalance := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
|
|
|
|
require.Equal(t, tt.initialReserve, oldReserveBalance.Amount, "initial x/evmutil reserve balance")
|
|
require.True(t, newReserveBalance.IsZero(), "empty initial new reserve")
|
|
|
|
// Set accounts
|
|
for i, balance := range tt.fractionalBalances {
|
|
addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
|
|
|
|
err := evmuk.SetBalance(ctx, addr, balance)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Run full x/evmutil -> x/precisebank migration
|
|
err = app.MigrateEvmutilToPrecisebank(
|
|
ctx,
|
|
ak,
|
|
bk,
|
|
evmuk,
|
|
pbk,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Check old reserve is empty
|
|
oldReserveBalanceAfter := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
|
|
require.True(t, oldReserveBalanceAfter.IsZero(), "old reserve should be empty")
|
|
|
|
// Check new reserve fully backs fractional balances
|
|
newReserveBalanceAfter := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
|
|
fractionalBalanceTotal := pbk.GetTotalSumFractionalBalances(ctx)
|
|
remainder := pbk.GetRemainderAmount(ctx)
|
|
|
|
expectedReserveBal := fractionalBalanceTotal.Add(remainder)
|
|
require.Equal(
|
|
t,
|
|
expectedReserveBal,
|
|
newReserveBalanceAfter.Amount.Mul(precisebanktypes.ConversionFactor()),
|
|
"new reserve should equal total fractional balances",
|
|
)
|
|
|
|
// Check balances are deleted in evmutil and migrated to precisebank
|
|
for i := range tt.fractionalBalances {
|
|
addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
|
|
acc := evmuk.GetAccount(ctx, addr)
|
|
require.Nil(t, acc, "account should be deleted")
|
|
|
|
balance := pbk.GetFractionalBalance(ctx, addr)
|
|
require.Equal(t, tt.fractionalBalances[i], balance, "balance should be migrated")
|
|
}
|
|
|
|
// Checks balances valid and remainder
|
|
res, stop := precisebankkeeper.AllInvariants(pbk)(ctx)
|
|
require.Falsef(t, stop, "invariants should pass: %s", res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTransferFractionalBalances(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
fractionalBalances []sdkmath.Int
|
|
}{
|
|
{
|
|
"no fractional balances",
|
|
[]sdkmath.Int{},
|
|
},
|
|
{
|
|
"balanced fractional balances",
|
|
[]sdkmath.Int{
|
|
// 4 accounts
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"unbalanced balances",
|
|
[]sdkmath.Int{
|
|
// 3 accounts
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tApp := app.NewTestApp()
|
|
tApp.InitializeFromGenesisStates()
|
|
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
|
|
|
|
evmutilk := tApp.GetEvmutilKeeper()
|
|
pbk := tApp.GetPrecisebankKeeper()
|
|
|
|
for i, balance := range tt.fractionalBalances {
|
|
addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
|
|
|
|
err := evmutilk.SetBalance(ctx, addr, balance)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Run balance transfer
|
|
aggregateSum, err := app.TransferFractionalBalances(
|
|
ctx,
|
|
evmutilk,
|
|
pbk,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Check balances are deleted in evmutil and migrated to precisebank
|
|
sum := sdkmath.ZeroInt()
|
|
for i := range tt.fractionalBalances {
|
|
sum = sum.Add(tt.fractionalBalances[i])
|
|
|
|
addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
|
|
acc := evmutilk.GetAccount(ctx, addr)
|
|
require.Nil(t, acc, "account should be deleted")
|
|
|
|
balance := pbk.GetFractionalBalance(ctx, addr)
|
|
require.Equal(t, tt.fractionalBalances[i], balance, "balance should be migrated")
|
|
}
|
|
|
|
require.Equal(t, sum, aggregateSum, "aggregate sum should be correct")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInitializeRemainder(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
giveAggregateSum sdkmath.Int
|
|
wantRemainder sdkmath.Int
|
|
}{
|
|
{
|
|
"0 remainder, 1ukava",
|
|
precisebanktypes.ConversionFactor(),
|
|
sdkmath.NewInt(0),
|
|
},
|
|
{
|
|
"0 remainder, multiple ukava",
|
|
precisebanktypes.ConversionFactor().MulRaw(5),
|
|
sdkmath.NewInt(0),
|
|
},
|
|
{
|
|
"non-zero remainder, min",
|
|
precisebanktypes.ConversionFactor().SubRaw(1),
|
|
sdkmath.NewInt(1),
|
|
},
|
|
{
|
|
"non-zero remainder, max",
|
|
sdkmath.NewInt(1),
|
|
precisebanktypes.ConversionFactor().SubRaw(1),
|
|
},
|
|
{
|
|
"non-zero remainder, half",
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tApp := app.NewTestApp()
|
|
tApp.InitializeFromGenesisStates()
|
|
|
|
pbk := tApp.GetPrecisebankKeeper()
|
|
|
|
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
|
|
|
|
remainder := app.InitializeRemainder(
|
|
ctx,
|
|
tApp.GetPrecisebankKeeper(),
|
|
tt.giveAggregateSum,
|
|
)
|
|
require.Equal(t, tt.wantRemainder, remainder)
|
|
|
|
// Check actual state
|
|
remainderAfter := pbk.GetRemainderAmount(ctx)
|
|
require.Equal(t, tt.wantRemainder, remainderAfter)
|
|
|
|
// Not checking invariants here since it requires actual balance state
|
|
aggregateSumWithRemainder := tt.giveAggregateSum.Add(remainder)
|
|
require.True(
|
|
t,
|
|
aggregateSumWithRemainder.
|
|
Mod(precisebanktypes.ConversionFactor()).
|
|
IsZero(),
|
|
"remainder + aggregate sum should be a multiple of the conversion factor",
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTransferFractionalBalanceReserve(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
initialReserve sdk.Coin
|
|
fractionalBalances []sdkmath.Int
|
|
}{
|
|
{
|
|
"balanced reserve, no remainder",
|
|
sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
|
|
[]sdkmath.Int{
|
|
// 2 accounts
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"insufficient reserve",
|
|
sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
|
|
[]sdkmath.Int{
|
|
// 4 accounts, total 2 int units
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"extra reserve funds",
|
|
sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(2)),
|
|
[]sdkmath.Int{
|
|
// 2 accounts, total 1 int units
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"insufficient reserve, with remainder",
|
|
sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
|
|
[]sdkmath.Int{
|
|
// 5 accounts, total 2.5 int units
|
|
// Expected 3 int units in reserve, 0.5 remainder
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
{
|
|
"extra reserve funds, with remainder",
|
|
sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(3)),
|
|
[]sdkmath.Int{
|
|
// 3 accounts, total 1.5 int units.
|
|
// Expected 2 int units in reserve, 0.5 remainder
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
precisebanktypes.ConversionFactor().QuoRaw(2),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tApp := app.NewTestApp()
|
|
tApp.InitializeFromGenesisStates()
|
|
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
|
|
|
|
bk := tApp.GetBankKeeper()
|
|
pbk := tApp.GetPrecisebankKeeper()
|
|
err := bk.MintCoins(ctx, evmutiltypes.ModuleName, sdk.NewCoins(tt.initialReserve))
|
|
require.NoError(t, err)
|
|
|
|
oldReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(evmutiltypes.ModuleName)
|
|
newReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(precisebanktypes.ModuleName)
|
|
|
|
// Double check balances
|
|
oldReserveBalance := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
|
|
newReserveBalance := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
|
|
|
|
require.Equal(t, tt.initialReserve, oldReserveBalance)
|
|
require.True(t, newReserveBalance.IsZero(), "empty initial new reserve")
|
|
|
|
for i, balance := range tt.fractionalBalances {
|
|
addr := sdk.AccAddress([]byte{byte(i)})
|
|
|
|
require.NotPanics(t, func() {
|
|
pbk.SetFractionalBalance(ctx, addr, balance)
|
|
}, "given fractional balances should be valid")
|
|
}
|
|
|
|
// Run reserve migration
|
|
err = app.TransferFractionalBalanceReserve(
|
|
ctx,
|
|
tApp.GetAccountKeeper(),
|
|
bk,
|
|
tApp.GetPrecisebankKeeper(),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Check old reserve is empty
|
|
oldReserveBalanceAfter := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
|
|
require.True(t, oldReserveBalanceAfter.IsZero(), "old reserve should be empty")
|
|
|
|
// Check new reserve fully backs fractional balances
|
|
newReserveBalanceAfter := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
|
|
fractionalBalanceTotal := pbk.GetTotalSumFractionalBalances(ctx)
|
|
|
|
expectedReserveBal := fractionalBalanceTotal.
|
|
Quo(precisebanktypes.ConversionFactor())
|
|
|
|
// Check if theres a remainder
|
|
if fractionalBalanceTotal.Mod(precisebanktypes.ConversionFactor()).IsPositive() {
|
|
expectedReserveBal = expectedReserveBal.Add(sdkmath.OneInt())
|
|
}
|
|
|
|
require.Equal(
|
|
t,
|
|
expectedReserveBal,
|
|
newReserveBalanceAfter.Amount,
|
|
"new reserve should equal total fractional balances + remainder",
|
|
)
|
|
})
|
|
}
|
|
}
|