mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-14 03:55:19 +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
275 lines
9.1 KiB
Go
275 lines
9.1 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
sdkmath "cosmossdk.io/math"
|
|
|
|
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/types/module"
|
|
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
|
|
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
|
|
evmutilkeeper "github.com/kava-labs/kava/x/evmutil/keeper"
|
|
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"
|
|
)
|
|
|
|
const (
|
|
UpgradeName_Mainnet = "v0.27.0"
|
|
UpgradeName_Testnet = "v0.27.0-alpha.0"
|
|
)
|
|
|
|
// RegisterUpgradeHandlers registers the upgrade handlers for the app.
|
|
func (app App) RegisterUpgradeHandlers() {
|
|
app.upgradeKeeper.SetUpgradeHandler(
|
|
UpgradeName_Mainnet,
|
|
upgradeHandler(app, UpgradeName_Mainnet),
|
|
)
|
|
app.upgradeKeeper.SetUpgradeHandler(
|
|
UpgradeName_Testnet,
|
|
upgradeHandler(app, UpgradeName_Testnet),
|
|
)
|
|
|
|
upgradeInfo, err := app.upgradeKeeper.ReadUpgradeInfoFromDisk()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
doUpgrade := upgradeInfo.Name == UpgradeName_Mainnet ||
|
|
upgradeInfo.Name == UpgradeName_Testnet
|
|
|
|
if doUpgrade && !app.upgradeKeeper.IsSkipHeight(upgradeInfo.Height) {
|
|
storeUpgrades := storetypes.StoreUpgrades{
|
|
Added: []string{
|
|
precisebanktypes.ModuleName,
|
|
},
|
|
}
|
|
|
|
// configure store loader that checks if version == upgradeHeight and applies store upgrades
|
|
app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades))
|
|
}
|
|
}
|
|
|
|
// upgradeHandler returns an UpgradeHandler for the given upgrade parameters.
|
|
func upgradeHandler(
|
|
app App,
|
|
name string,
|
|
) upgradetypes.UpgradeHandler {
|
|
return func(
|
|
ctx sdk.Context,
|
|
plan upgradetypes.Plan,
|
|
fromVM module.VersionMap,
|
|
) (module.VersionMap, error) {
|
|
logger := app.Logger()
|
|
logger.Info(fmt.Sprintf("running %s upgrade handler", name))
|
|
|
|
// Run migrations for all modules and return new consensus version map.
|
|
versionMap, err := app.mm.RunMigrations(ctx, app.configurator, fromVM)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
logger.Info("completed store migrations")
|
|
|
|
// Migration of fractional balances from x/evmutil to x/precisebank
|
|
if err := MigrateEvmutilToPrecisebank(
|
|
ctx,
|
|
app.accountKeeper,
|
|
app.bankKeeper,
|
|
app.evmutilKeeper,
|
|
app.precisebankKeeper,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
logger.Info("completed x/evmutil to x/precisebank migration")
|
|
|
|
return versionMap, nil
|
|
}
|
|
}
|
|
|
|
// MigrateEvmutilToPrecisebank migrates all required state from x/evmutil to
|
|
// x/precisebank and ensures the resulting state is correct.
|
|
// This migrates the following state:
|
|
// - Fractional balances
|
|
// - Fractional balance reserve
|
|
// Initializes the following state in x/precisebank:
|
|
// - Remainder amount
|
|
func MigrateEvmutilToPrecisebank(
|
|
ctx sdk.Context,
|
|
accountKeeper evmutiltypes.AccountKeeper,
|
|
bankKeeper bankkeeper.Keeper,
|
|
evmutilKeeper evmutilkeeper.Keeper,
|
|
precisebankKeeper precisebankkeeper.Keeper,
|
|
) error {
|
|
logger := ctx.Logger()
|
|
|
|
aggregateSum, err := TransferFractionalBalances(
|
|
ctx,
|
|
evmutilKeeper,
|
|
precisebankKeeper,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("fractional balances transfer: %w", err)
|
|
}
|
|
logger.Info(
|
|
"fractional balances transferred from x/evmutil to x/precisebank",
|
|
"aggregate sum", aggregateSum,
|
|
)
|
|
|
|
remainder := InitializeRemainder(ctx, precisebankKeeper, aggregateSum)
|
|
logger.Info("remainder amount initialized in x/precisebank", "remainder", remainder)
|
|
|
|
// Migrate fractional balances, reserve, and ensure reserve fully backs all
|
|
// fractional balances.
|
|
if err := TransferFractionalBalanceReserve(
|
|
ctx,
|
|
accountKeeper,
|
|
bankKeeper,
|
|
precisebankKeeper,
|
|
); err != nil {
|
|
return fmt.Errorf("reserve transfer: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TransferFractionalBalances migrates fractional balances from x/evmutil to
|
|
// x/precisebank. It sets the fractional balance in x/precisebank and deletes
|
|
// the account from x/evmutil. Returns the aggregate sum of all fractional
|
|
// balances.
|
|
func TransferFractionalBalances(
|
|
ctx sdk.Context,
|
|
evmutilKeeper evmutilkeeper.Keeper,
|
|
precisebankKeeper precisebankkeeper.Keeper,
|
|
) (sdkmath.Int, error) {
|
|
aggregateSum := sdkmath.ZeroInt()
|
|
|
|
var iterErr error
|
|
|
|
evmutilKeeper.IterateAllAccounts(ctx, func(acc evmutiltypes.Account) bool {
|
|
// Set account balance in x/precisebank
|
|
precisebankKeeper.SetFractionalBalance(ctx, acc.Address, acc.Balance)
|
|
|
|
// Delete account from x/evmutil
|
|
iterErr := evmutilKeeper.SetAccount(ctx, evmutiltypes.Account{
|
|
Address: acc.Address,
|
|
// Set balance to 0 to delete it
|
|
Balance: sdkmath.ZeroInt(),
|
|
})
|
|
|
|
// Halt iteration if there was an error
|
|
if iterErr != nil {
|
|
return true
|
|
}
|
|
|
|
// Aggregate sum of all fractional balances
|
|
aggregateSum = aggregateSum.Add(acc.Balance)
|
|
|
|
// Continue iterating
|
|
return false
|
|
})
|
|
|
|
return aggregateSum, iterErr
|
|
}
|
|
|
|
// InitializeRemainder initializes the remainder amount in x/precisebank. It
|
|
// calculates the remainder amount that is needed to ensure that the sum of all
|
|
// fractional balances is a multiple of the conversion factor. The remainder
|
|
// amount is stored in the store and returned.
|
|
func InitializeRemainder(
|
|
ctx sdk.Context,
|
|
precisebankKeeper precisebankkeeper.Keeper,
|
|
aggregateSum sdkmath.Int,
|
|
) sdkmath.Int {
|
|
// Extra fractional coins that exceed the conversion factor.
|
|
// This extra + remainder should equal the conversion factor to ensure
|
|
// (sum(fBalances) + remainder) % conversionFactor = 0
|
|
extraFractionalAmount := aggregateSum.Mod(precisebanktypes.ConversionFactor())
|
|
remainder := precisebanktypes.ConversionFactor().
|
|
Sub(extraFractionalAmount).
|
|
// Mod conversion factor to ensure remainder is valid.
|
|
// If extraFractionalAmount is a multiple of conversion factor, the
|
|
// remainder is 0.
|
|
Mod(precisebanktypes.ConversionFactor())
|
|
|
|
// Panics if the remainder is invalid. In a correct chain state and only
|
|
// mint/burns due to transfers, this would be 0.
|
|
precisebankKeeper.SetRemainderAmount(ctx, remainder)
|
|
|
|
return remainder
|
|
}
|
|
|
|
// TransferFractionalBalanceReserve migrates the fractional balance reserve from
|
|
// x/evmutil to x/precisebank. It transfers the reserve balance from x/evmutil
|
|
// to x/precisebank and ensures that the reserve fully backs all fractional
|
|
// balances. It mints or burns coins to back the fractional balances exactly.
|
|
func TransferFractionalBalanceReserve(
|
|
ctx sdk.Context,
|
|
accountKeeper evmutiltypes.AccountKeeper,
|
|
bankKeeper bankkeeper.Keeper,
|
|
precisebankKeeper precisebankkeeper.Keeper,
|
|
) error {
|
|
logger := ctx.Logger()
|
|
|
|
// Transfer x/evmutil reserve to x/precisebank.
|
|
evmutilAddr := accountKeeper.GetModuleAddress(evmutiltypes.ModuleName)
|
|
reserveBalance := bankKeeper.GetBalance(ctx, evmutilAddr, precisebanktypes.IntegerCoinDenom)
|
|
|
|
if err := bankKeeper.SendCoinsFromModuleToModule(
|
|
ctx,
|
|
evmutiltypes.ModuleName, // from x/evmutil
|
|
precisebanktypes.ModuleName, // to x/precisebank
|
|
sdk.NewCoins(reserveBalance),
|
|
); err != nil {
|
|
return fmt.Errorf("failed to transfer reserve from x/evmutil to x/precisebank: %w", err)
|
|
}
|
|
|
|
logger.Info(fmt.Sprintf("transferred reserve balance: %s", reserveBalance))
|
|
|
|
// Ensure x/precisebank reserve fully backs all fractional balances.
|
|
totalFractionalBalances := precisebankKeeper.GetTotalSumFractionalBalances(ctx)
|
|
|
|
// Does NOT ensure state is correct, total fractional balances should be a
|
|
// multiple of conversion factor but is not guaranteed due to the remainder.
|
|
// Remainder initialization is handled by InitializeRemainder.
|
|
|
|
// Determine how much the reserve is off by, e.g. unbacked amount
|
|
expectedReserveBalance := totalFractionalBalances.Quo(precisebanktypes.ConversionFactor())
|
|
|
|
// If there is a remainder (totalFractionalBalances % conversionFactor != 0),
|
|
// then expectedReserveBalance is rounded up to the nearest integer.
|
|
if totalFractionalBalances.Mod(precisebanktypes.ConversionFactor()).IsPositive() {
|
|
expectedReserveBalance = expectedReserveBalance.Add(sdkmath.OneInt())
|
|
}
|
|
|
|
unbackedAmount := expectedReserveBalance.Sub(reserveBalance.Amount)
|
|
logger.Info(fmt.Sprintf("total account fractional balances: %s", totalFractionalBalances))
|
|
|
|
// Three possible cases:
|
|
// 1. Reserve is not enough, mint coins to back the fractional balances
|
|
// 2. Reserve is too much, burn coins to back the fractional balances exactly
|
|
// 3. Reserve is exactly enough, no action needed
|
|
if unbackedAmount.IsPositive() {
|
|
coins := sdk.NewCoins(sdk.NewCoin(precisebanktypes.IntegerCoinDenom, unbackedAmount))
|
|
if err := bankKeeper.MintCoins(ctx, precisebanktypes.ModuleName, coins); err != nil {
|
|
return fmt.Errorf("failed to mint extra reserve coins: %w", err)
|
|
}
|
|
|
|
logger.Info(fmt.Sprintf("unbacked amount minted to reserve: %s", unbackedAmount))
|
|
} else if unbackedAmount.IsNegative() {
|
|
coins := sdk.NewCoins(sdk.NewCoin(precisebanktypes.IntegerCoinDenom, unbackedAmount.Neg()))
|
|
if err := bankKeeper.BurnCoins(ctx, precisebanktypes.ModuleName, coins); err != nil {
|
|
return fmt.Errorf("failed to burn extra reserve coins: %w", err)
|
|
}
|
|
|
|
logger.Info(fmt.Sprintf("extra reserve amount burned: %s", unbackedAmount.Neg()))
|
|
} else {
|
|
logger.Info("reserve exactly backs fractional balances, no mint/burn needed")
|
|
}
|
|
|
|
return nil
|
|
}
|