diff --git a/.github/workflows/buildtest.yaml b/.github/workflows/buildtest.yaml index 038a29361..99b58e9b7 100644 --- a/.github/workflows/buildtest.yaml +++ b/.github/workflows/buildtest.yaml @@ -29,7 +29,7 @@ jobs: name: build quicksilver steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: "1.20.11" env: @@ -63,7 +63,7 @@ jobs: name: test quicksilver steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: "1.20.11" env: @@ -123,7 +123,7 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: "1.20.5" - name: Run simulation tests diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d0ddd0eb1..d6f84d438 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: 1.19.5 # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 3392e0f3a..1c3b001c1 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -22,7 +22,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: "1.20.11" diff --git a/.github/workflows/golangci.yml b/.github/workflows/golangci.yml index 2d747039b..ad7468499 100644 --- a/.github/workflows/golangci.yml +++ b/.github/workflows/golangci.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: '1.21' cache: false diff --git a/.github/workflows/interchaintest.yaml b/.github/workflows/interchaintest.yaml index fa3b5d6f6..f2d615d58 100644 --- a/.github/workflows/interchaintest.yaml +++ b/.github/workflows/interchaintest.yaml @@ -36,7 +36,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: "1.20.5" @@ -57,7 +57,7 @@ jobs: needs: build-and-push-image steps: - name: Set up Go 1.20 - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: "1.20.5" id: go @@ -74,7 +74,7 @@ jobs: needs: build-and-push-image steps: - name: Set up Go 1.20 - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: "1.20.5" id: go @@ -91,7 +91,7 @@ jobs: needs: build-and-push-image steps: - name: Set up Go 1.20 - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: "1.20.5" id: go diff --git a/.github/workflows/simulation.yml b/.github/workflows/simulation.yml index 3e3980f0d..c411accbf 100644 --- a/.github/workflows/simulation.yml +++ b/.github/workflows/simulation.yml @@ -17,7 +17,7 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: "1.20.5" diff --git a/x/interchainstaking/keeper/keeper.go b/x/interchainstaking/keeper/keeper.go index 10040a5cc..9781964f3 100644 --- a/x/interchainstaking/keeper/keeper.go +++ b/x/interchainstaking/keeper/keeper.go @@ -416,29 +416,6 @@ func (k *Keeper) SetValidatorForZone(ctx sdk.Context, zone *types.Zone, data []b return nil } -func (k *Keeper) UpdateWithdrawalRecordsForSlash(ctx sdk.Context, zone *types.Zone, valoper string, delta sdk.Dec) error { - var err error - k.IterateZoneStatusWithdrawalRecords(ctx, zone.ChainId, types.WithdrawStatusUnbond, func(_ int64, record types.WithdrawalRecord) bool { - recordSubAmount := sdkmath.ZeroInt() - distr := record.Distribution - for _, d := range distr { - if d.Valoper != valoper { - continue - } - newAmount := sdk.NewDec(int64(d.Amount)).Quo(delta).TruncateInt() - thisSubAmount := sdkmath.NewInt(int64(d.Amount)).Sub(newAmount) - recordSubAmount = recordSubAmount.Add(thisSubAmount) - d.Amount = newAmount.Uint64() - k.Logger(ctx).Info("Updated withdrawal record due to slashing", "valoper", valoper, "old_amount", d.Amount, "new_amount", newAmount.Int64(), "sub_amount", thisSubAmount.Int64()) - } - record.Distribution = distr - record.Amount = record.Amount.Sub(sdk.NewCoin(zone.BaseDenom, recordSubAmount)) - k.SetWithdrawalRecord(ctx, record) - return false - }) - return err -} - func (k *Keeper) depositInterval(ctx sdk.Context) zoneItrFn { return func(index int64, zone *types.Zone) (stop bool) { if zone.DepositAddress != nil { diff --git a/x/interchainstaking/keeper/withdrawal_record.go b/x/interchainstaking/keeper/withdrawal_record.go index 56e4b1378..bdae2c165 100644 --- a/x/interchainstaking/keeper/withdrawal_record.go +++ b/x/interchainstaking/keeper/withdrawal_record.go @@ -3,8 +3,11 @@ package keeper import ( "encoding/binary" "encoding/hex" + "fmt" "time" + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" @@ -222,3 +225,37 @@ func (k *Keeper) AllZoneUnbondingRecords(ctx sdk.Context, chainID string) []type }) return records } + +func (k *Keeper) UpdateWithdrawalRecordsForSlash(ctx sdk.Context, zone *types.Zone, valoper string, delta sdk.Dec) error { + var err error + k.IterateZoneStatusWithdrawalRecords(ctx, zone.ChainId, types.WithdrawStatusUnbond, func(_ int64, record types.WithdrawalRecord) bool { + recordSubAmount := sdkmath.ZeroInt() + distr := record.Distribution + for _, d := range distr { + if d.Valoper != valoper { + continue + } + distAmount := sdk.NewDec(int64(d.Amount)) + if distAmount.IsNegative() { + err = fmt.Errorf("distAmount cannot be negative; suspected overflow") + return true + } + + newAmount := distAmount.Quo(delta).TruncateInt() + thisSubAmount := distAmount.TruncateInt().Sub(newAmount) + recordSubAmount = recordSubAmount.Add(thisSubAmount) + d.Amount = newAmount.Uint64() + k.Logger(ctx).Info("Updated withdrawal record due to slashing", "valoper", valoper, "old_amount", d.Amount, "new_amount", newAmount.Int64(), "sub_amount", thisSubAmount.Int64()) + } + record.Distribution = distr + subAmount := sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, recordSubAmount)) + if !record.Amount.IsAllGT(subAmount) { + err = fmt.Errorf("deductedTotal cannot contain negative coins; suspected overflow") + return true + } + record.Amount = record.Amount.Sub(subAmount...) + k.SetWithdrawalRecord(ctx, record) + return false + }) + return err +} diff --git a/x/interchainstaking/keeper/withdrawal_record_test.go b/x/interchainstaking/keeper/withdrawal_record_test.go new file mode 100644 index 000000000..9101d7266 --- /dev/null +++ b/x/interchainstaking/keeper/withdrawal_record_test.go @@ -0,0 +1,263 @@ +package keeper_test + +import ( + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + + icskeeper "github.com/quicksilver-zone/quicksilver/x/interchainstaking/keeper" + "github.com/quicksilver-zone/quicksilver/x/interchainstaking/types" +) + +func (suite *KeeperTestSuite) TestUpdateWithdrawalRecordsForSlash() { + tcs := []struct { + Name string + InitialRecords func(ctx sdk.Context, keeper *icskeeper.Keeper) + ExpectedRecords func(ctx sdk.Context, keeper *icskeeper.Keeper) (out []types.WithdrawalRecord) + Validator func(validators []string) string + Delta sdk.Dec + ExpectError bool + }{ + { + Name: "single 5% slashing", + InitialRecords: func(ctx sdk.Context, keeper *icskeeper.Keeper) { + zone, _ := keeper.GetZone(ctx, suite.chainB.ChainID) + validators := keeper.GetValidatorAddresses(ctx, zone.ChainId) + withdrawalRecord := types.WithdrawalRecord{ + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1v4gek4mld0k5yhpe0fsln4takg558cdpyexv2rxr3dh45f2fqrgsw52m97", + Distribution: []*types.Distribution{ + {Amount: 10000, Valoper: validators[0]}, + {Amount: 10000, Valoper: validators[1]}, + {Amount: 10000, Valoper: validators[2]}, + {Amount: 10000, Valoper: validators[3]}, + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(40000))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(32356)), + Txhash: "3BE21C1057ABBFBC44BE8993D2A4701C751507FF9901AA110B5993BA070C176B", + Status: types.WithdrawStatusUnbond, + } + keeper.SetWithdrawalRecord(ctx, withdrawalRecord) + }, + ExpectedRecords: func(ctx sdk.Context, keeper *icskeeper.Keeper) (out []types.WithdrawalRecord) { + zone, _ := keeper.GetZone(ctx, suite.chainB.ChainID) + validators := keeper.GetValidatorAddresses(ctx, zone.ChainId) + out = []types.WithdrawalRecord{ + { + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1v4gek4mld0k5yhpe0fsln4takg558cdpyexv2rxr3dh45f2fqrgsw52m97", + Distribution: []*types.Distribution{ + {Amount: 10000, Valoper: validators[0]}, + {Amount: 9500, Valoper: validators[1]}, + {Amount: 10000, Valoper: validators[2]}, + {Amount: 10000, Valoper: validators[3]}, + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(39500))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(32356)), + Txhash: "3BE21C1057ABBFBC44BE8993D2A4701C751507FF9901AA110B5993BA070C176B", + Status: types.WithdrawStatusUnbond, + }, + } + return out + }, + Delta: sdk.NewDecWithPrec(100, 2).Quo(sdk.NewDecWithPrec(95, 2)), + Validator: func(validators []string) string { return validators[1] }, + + ExpectError: false, + }, + { + Name: "multi record 5% slashing", + InitialRecords: func(ctx sdk.Context, keeper *icskeeper.Keeper) { + zone, _ := keeper.GetZone(ctx, suite.chainB.ChainID) + validators := keeper.GetValidatorAddresses(ctx, zone.ChainId) + withdrawalRecord := types.WithdrawalRecord{ + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1v4gek4mld0k5yhpe0fsln4takg558cdpyexv2rxr3dh45f2fqrgsw52m97", + Distribution: []*types.Distribution{ + {Amount: 10000, Valoper: validators[0]}, + {Amount: 10000, Valoper: validators[1]}, + {Amount: 10000, Valoper: validators[2]}, + {Amount: 10000, Valoper: validators[3]}, + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(40000))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(32356)), + Txhash: "3BE21C1057ABBFBC44BE8993D2A4701C751507FF9901AA110B5993BA070C176B", + Status: types.WithdrawStatusUnbond, + } + keeper.SetWithdrawalRecord(ctx, withdrawalRecord) + withdrawalRecord = types.WithdrawalRecord{ + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1nvkpj5n5mhy2ntvgn2cklntwx9ujvfvcacz5et", + Distribution: []*types.Distribution{ + {Amount: 13000, Valoper: validators[0]}, + {Amount: 14000, Valoper: validators[1]}, + {Amount: 10000, Valoper: validators[2]}, + {Amount: 11000, Valoper: validators[3]}, + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(48000))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(36503)), + Txhash: "FB087A50A4836CBDFACA70D393AF110C28935276267B7BA2838BE3CEEA08F762", + Status: types.WithdrawStatusUnbond, + } + keeper.SetWithdrawalRecord(ctx, withdrawalRecord) + }, + ExpectedRecords: func(ctx sdk.Context, keeper *icskeeper.Keeper) (out []types.WithdrawalRecord) { + zone, _ := keeper.GetZone(ctx, suite.chainB.ChainID) + validators := keeper.GetValidatorAddresses(ctx, zone.ChainId) + out = []types.WithdrawalRecord{ + { + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1v4gek4mld0k5yhpe0fsln4takg558cdpyexv2rxr3dh45f2fqrgsw52m97", + Distribution: []*types.Distribution{ + {Amount: 10000, Valoper: validators[0]}, + {Amount: 9500, Valoper: validators[1]}, + {Amount: 10000, Valoper: validators[2]}, + {Amount: 10000, Valoper: validators[3]}, + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(39500))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(32356)), + Txhash: "3BE21C1057ABBFBC44BE8993D2A4701C751507FF9901AA110B5993BA070C176B", + Status: types.WithdrawStatusUnbond, + }, + { + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1nvkpj5n5mhy2ntvgn2cklntwx9ujvfvcacz5et", + Distribution: []*types.Distribution{ + {Amount: 13000, Valoper: validators[0]}, + {Amount: 13300, Valoper: validators[1]}, + {Amount: 10000, Valoper: validators[2]}, + {Amount: 11000, Valoper: validators[3]}, + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(47300))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(36503)), + Txhash: "FB087A50A4836CBDFACA70D393AF110C28935276267B7BA2838BE3CEEA08F762", + Status: types.WithdrawStatusUnbond, + }, + } + return out + }, + Delta: sdk.NewDecWithPrec(100, 2).Quo(sdk.NewDecWithPrec(95, 2)), + Validator: func(validators []string) string { return validators[1] }, + ExpectError: false, + }, + { + Name: "overflow test", + InitialRecords: func(ctx sdk.Context, keeper *icskeeper.Keeper) { + zone, _ := keeper.GetZone(ctx, suite.chainB.ChainID) + validators := keeper.GetValidatorAddresses(ctx, zone.ChainId) + withdrawalRecord := types.WithdrawalRecord{ + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1v4gek4mld0k5yhpe0fsln4takg558cdpyexv2rxr3dh45f2fqrgsw52m97", + Distribution: []*types.Distribution{ + {Amount: 9223372036854775807 + 1, Valoper: validators[1]}, // max int64 +1 - check for overflow + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(40000))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(32356)), + Txhash: "3BE21C1057ABBFBC44BE8993D2A4701C751507FF9901AA110B5993BA070C176B", + Status: types.WithdrawStatusUnbond, + } + keeper.SetWithdrawalRecord(ctx, withdrawalRecord) + }, + ExpectedRecords: func(ctx sdk.Context, keeper *icskeeper.Keeper) (out []types.WithdrawalRecord) { + zone, _ := keeper.GetZone(ctx, suite.chainB.ChainID) + validators := keeper.GetValidatorAddresses(ctx, zone.ChainId) + out = []types.WithdrawalRecord{ + { + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1v4gek4mld0k5yhpe0fsln4takg558cdpyexv2rxr3dh45f2fqrgsw52m97", + Distribution: []*types.Distribution{ + {Amount: 9223372036854775807 + 1, Valoper: validators[1]}, + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(40000))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(32356)), + Txhash: "3BE21C1057ABBFBC44BE8993D2A4701C751507FF9901AA110B5993BA070C176B", + Status: types.WithdrawStatusUnbond, + }, + } + return out + }, + Delta: sdk.NewDecWithPrec(100, 2).Quo(sdk.NewDecWithPrec(95, 2)), + Validator: func(validators []string) string { return validators[1] }, + ExpectError: true, + }, + + { + Name: "mismatch test", + InitialRecords: func(ctx sdk.Context, keeper *icskeeper.Keeper) { + zone, _ := keeper.GetZone(ctx, suite.chainB.ChainID) + validators := keeper.GetValidatorAddresses(ctx, zone.ChainId) + withdrawalRecord := types.WithdrawalRecord{ + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1nna7k5lywn99cd63elcfqm6p8c5c4qcug4aef5", + Distribution: []*types.Distribution{ + {Amount: 100000000, Valoper: validators[1]}, // slashed amount exceeds total amount + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(10))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(10)), + Txhash: "8A698A142447087E5DC01F7BC3886EC1A6606D377D1FAC766FB279AD09F1407C", + Status: types.WithdrawStatusUnbond, + } + keeper.SetWithdrawalRecord(ctx, withdrawalRecord) + }, + ExpectedRecords: func(ctx sdk.Context, keeper *icskeeper.Keeper) (out []types.WithdrawalRecord) { + zone, _ := keeper.GetZone(ctx, suite.chainB.ChainID) + validators := keeper.GetValidatorAddresses(ctx, zone.ChainId) + out = []types.WithdrawalRecord{ + { + ChainId: zone.ChainId, + Delegator: zone.DelegationAddress.Address, + Recipient: "cosmos1nna7k5lywn99cd63elcfqm6p8c5c4qcug4aef5", + Distribution: []*types.Distribution{ + {Amount: 100000000, Valoper: validators[1]}, // slashed amount exceeds total amount + }, + Amount: sdk.NewCoins(sdk.NewCoin(zone.BaseDenom, math.NewInt(10))), + BurnAmount: sdk.NewCoin(zone.LocalDenom, math.NewInt(10)), + Txhash: "8A698A142447087E5DC01F7BC3886EC1A6606D377D1FAC766FB279AD09F1407C", + Status: types.WithdrawStatusUnbond, + }, + } + return out + }, + Delta: sdk.NewDecWithPrec(100, 2).Quo(sdk.NewDecWithPrec(95, 2)), + Validator: func(validators []string) string { return validators[1] }, + ExpectError: true, + }, + } + + for _, tc := range tcs { + suite.Run(tc.Name, func() { + suite.SetupTest() + suite.setupTestZones() + + app := suite.GetQuicksilverApp(suite.chainA) + ctx := suite.chainA.GetContext() + + zone, found := app.InterchainstakingKeeper.GetZone(ctx, suite.chainB.ChainID) + suite.True(found) + + tc.InitialRecords(ctx, app.InterchainstakingKeeper) + + err := app.InterchainstakingKeeper.UpdateWithdrawalRecordsForSlash(ctx, &zone, tc.Validator(app.InterchainstakingKeeper.GetValidatorAddresses(ctx, zone.ChainId)), tc.Delta) + if tc.ExpectError { + suite.Error(err) + } else { + suite.NoError(err) + } + ctx = suite.chainA.GetContext() + for _, expected := range tc.ExpectedRecords(ctx, app.InterchainstakingKeeper) { + wrd, found := app.InterchainstakingKeeper.GetWithdrawalRecord(ctx, zone.ChainId, expected.Txhash, types.WithdrawStatusUnbond) + suite.True(found) + suite.EqualValues(expected, wrd) + } + }) + } +} diff --git a/x/interchainstaking/types/delegation.go b/x/interchainstaking/types/delegation.go index 00f98a19f..04dd71f07 100644 --- a/x/interchainstaking/types/delegation.go +++ b/x/interchainstaking/types/delegation.go @@ -86,16 +86,22 @@ func (vi ValidatorIntents) GetForValoper(valoper string) (*ValidatorIntent, bool } func (vi ValidatorIntents) SetForValoper(valoper string, intent *ValidatorIntent) ValidatorIntents { - for idx, i := range vi { - if i.ValoperAddress == valoper { - vi[idx] = vi[len(vi)-1] - vi = vi[:len(vi)-1] + idx := -1 // the index of the valoper if found + for i, v := range vi { + // Search for the valoper. + if v.ValoperAddress == valoper { + idx = i break } } - vi = append(vi, intent) - return vi.Sort() + if idx >= 0 { // We found the valoper so just replace it + vi[idx] = intent + } else { + vi = append(vi, intent) + return vi.Sort() + } + return vi } func (vi ValidatorIntents) MustGetForValoper(valoper string) *ValidatorIntent { diff --git a/x/participationrewards/types/allocations_test.go b/x/participationrewards/types/allocations_test.go index 089e05c0b..525a11be0 100644 --- a/x/participationrewards/types/allocations_test.go +++ b/x/participationrewards/types/allocations_test.go @@ -21,13 +21,13 @@ func TestGetRewardsAllocations(t *testing.T) { name string args args want *types.RewardsAllocation - wantErr bool + wantErr string }{ { "empty_params", args{}, nil, - true, + "balance is zero, nothing to allocate", }, { "invalid_no_balance", @@ -40,7 +40,7 @@ func TestGetRewardsAllocations(t *testing.T) { }, }, nil, - true, + "balance is zero, nothing to allocate", }, { "invalid_proportions_gt", @@ -53,7 +53,7 @@ func TestGetRewardsAllocations(t *testing.T) { }, }, nil, - true, + "total distribution proportions must be 1.0: got 1.50", }, { "invalid_proportions_lt", @@ -66,7 +66,7 @@ func TestGetRewardsAllocations(t *testing.T) { }, }, nil, - true, + "total distribution proportions must be 1.0: got 0.90", }, { "valid", @@ -83,7 +83,7 @@ func TestGetRewardsAllocations(t *testing.T) { Holdings: sdk.NewInt(330000000), Lockup: sdk.NewInt(330000000), }, - false, + "", }, { "valid", @@ -100,7 +100,7 @@ func TestGetRewardsAllocations(t *testing.T) { Holdings: sdk.NewInt(250000000), Lockup: sdk.NewInt(250000000), }, - false, + "", }, { "valid", @@ -117,7 +117,7 @@ func TestGetRewardsAllocations(t *testing.T) { Holdings: sdk.NewInt(400000000), Lockup: sdk.NewInt(0), }, - false, + "", }, { "valid", @@ -134,7 +134,7 @@ func TestGetRewardsAllocations(t *testing.T) { Holdings: sdk.NewInt(54164045698), Lockup: sdk.NewInt(54164045698), }, - false, + "", }, { "valid", @@ -151,7 +151,7 @@ func TestGetRewardsAllocations(t *testing.T) { Holdings: sdk.NewInt(41033367953), Lockup: sdk.NewInt(41033367953), }, - false, + "", }, { "valid", @@ -168,15 +168,16 @@ func TestGetRewardsAllocations(t *testing.T) { Holdings: sdk.NewInt(65653388725), Lockup: sdk.NewInt(0), }, - false, + "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := types.GetRewardsAllocations(tt.args.moduleBalance, tt.args.proportions) - if tt.wantErr { + if tt.wantErr != "" { require.Error(t, err) require.Nil(t, got) + require.Contains(t, err.Error(), tt.wantErr) t.Logf("Error: %v", err) return }