mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 10:37:26 +00:00 
			
		
		
		
	Fix reserve minting an extra coin when the recipient module both carries fractional over to integer balance AND remainder is insufficient. Adjusts fractional carry to simply send from reserve, instead of doing an additional mint. Add invariant to ensure reserve matches exactly with fractional balances + remainder, failing on both insufficient and excess funds.
		
			
				
	
	
		
			130 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package keeper_test
 | 
						|
 | 
						|
import (
 | 
						|
	"testing"
 | 
						|
 | 
						|
	sdkmath "cosmossdk.io/math"
 | 
						|
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
						|
	"github.com/kava-labs/kava/x/precisebank/keeper"
 | 
						|
	"github.com/kava-labs/kava/x/precisebank/testutil"
 | 
						|
	"github.com/kava-labs/kava/x/precisebank/types"
 | 
						|
	"github.com/stretchr/testify/suite"
 | 
						|
)
 | 
						|
 | 
						|
type invariantsIntegrationTestSuite struct {
 | 
						|
	testutil.Suite
 | 
						|
}
 | 
						|
 | 
						|
func (suite *invariantsIntegrationTestSuite) SetupTest() {
 | 
						|
	suite.Suite.SetupTest()
 | 
						|
}
 | 
						|
 | 
						|
func TestInvariantsIntegrationTest(t *testing.T) {
 | 
						|
	suite.Run(t, new(invariantsIntegrationTestSuite))
 | 
						|
}
 | 
						|
 | 
						|
func (suite *invariantsIntegrationTestSuite) FundReserve(amt sdkmath.Int) {
 | 
						|
	coins := sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, amt))
 | 
						|
	err := suite.BankKeeper.MintCoins(suite.Ctx, types.ModuleName, coins)
 | 
						|
	suite.Require().NoError(err)
 | 
						|
}
 | 
						|
 | 
						|
func (suite *invariantsIntegrationTestSuite) TestReserveBackingFractionalInvariant() {
 | 
						|
	tests := []struct {
 | 
						|
		name       string
 | 
						|
		setupFn    func(ctx sdk.Context, k keeper.Keeper)
 | 
						|
		wantBroken bool
 | 
						|
		wantMsg    string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			"valid - empty state",
 | 
						|
			func(_ sdk.Context, _ keeper.Keeper) {},
 | 
						|
			false,
 | 
						|
			"",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"valid - fractional balances, no remainder",
 | 
						|
			func(ctx sdk.Context, k keeper.Keeper) {
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{1}, types.ConversionFactor().QuoRaw(2))
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{2}, types.ConversionFactor().QuoRaw(2))
 | 
						|
				// 1 integer backs same amount fractional
 | 
						|
				suite.FundReserve(sdk.NewInt(1))
 | 
						|
			},
 | 
						|
			false,
 | 
						|
			"",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"valid - fractional balances, with remainder",
 | 
						|
			func(ctx sdk.Context, k keeper.Keeper) {
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{1}, types.ConversionFactor().QuoRaw(2))
 | 
						|
				k.SetRemainderAmount(ctx, types.ConversionFactor().QuoRaw(2))
 | 
						|
				// 1 integer backs same amount fractional including remainder
 | 
						|
				suite.FundReserve(sdk.NewInt(1))
 | 
						|
			},
 | 
						|
			false,
 | 
						|
			"",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"invalid - no fractional balances, non-zero remainder",
 | 
						|
			func(ctx sdk.Context, k keeper.Keeper) {
 | 
						|
				k.SetRemainderAmount(ctx, types.ConversionFactor().QuoRaw(2))
 | 
						|
			},
 | 
						|
			true,
 | 
						|
			"precisebank: module reserve backing total fractional balances invariant\nakava reserve balance 0 mismatches 500000000000 (fractional balances 0 + remainder 500000000000)\n\n",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"invalid - insufficient reserve backing",
 | 
						|
			func(ctx sdk.Context, k keeper.Keeper) {
 | 
						|
				amt := types.ConversionFactor().QuoRaw(2)
 | 
						|
 | 
						|
				// 0.5 int coins x 4
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{1}, amt)
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{2}, amt)
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{3}, amt)
 | 
						|
				k.SetRemainderAmount(ctx, amt)
 | 
						|
 | 
						|
				// Needs 2 to back 0.5 x 4
 | 
						|
				suite.FundReserve(sdk.NewInt(1))
 | 
						|
			},
 | 
						|
			true,
 | 
						|
			"precisebank: module reserve backing total fractional balances invariant\nakava reserve balance 1000000000000 mismatches 2000000000000 (fractional balances 1500000000000 + remainder 500000000000)\n\n",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"invalid - excess reserve backing",
 | 
						|
			func(ctx sdk.Context, k keeper.Keeper) {
 | 
						|
				amt := types.ConversionFactor().QuoRaw(2)
 | 
						|
 | 
						|
				// 0.5 int coins x 4
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{1}, amt)
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{2}, amt)
 | 
						|
				k.SetFractionalBalance(ctx, sdk.AccAddress{3}, amt)
 | 
						|
				k.SetRemainderAmount(ctx, amt)
 | 
						|
 | 
						|
				// Needs 2 to back 0.5 x 4
 | 
						|
				suite.FundReserve(sdk.NewInt(3))
 | 
						|
			},
 | 
						|
			true,
 | 
						|
			"precisebank: module reserve backing total fractional balances invariant\nakava reserve balance 3000000000000 mismatches 2000000000000 (fractional balances 1500000000000 + remainder 500000000000)\n\n",
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tt := range tests {
 | 
						|
		suite.Run(tt.name, func() {
 | 
						|
			// Reset each time
 | 
						|
			suite.SetupTest()
 | 
						|
 | 
						|
			tt.setupFn(suite.Ctx, suite.Keeper)
 | 
						|
 | 
						|
			invariantFn := keeper.ReserveBacksFractionsInvariant(suite.Keeper)
 | 
						|
			msg, broken := invariantFn(suite.Ctx)
 | 
						|
 | 
						|
			if tt.wantBroken {
 | 
						|
				suite.Require().True(broken, "invariant should be broken but is not")
 | 
						|
				suite.Require().Equal(tt.wantMsg, msg)
 | 
						|
			} else {
 | 
						|
				suite.Require().Falsef(broken, "invariant should not be broken but is: %s", msg)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |