Skip to content

Commit

Permalink
fix(x/auth/vesting): panic on overflowing & negative EndTimes for Per…
Browse files Browse the repository at this point in the history
…iodicVestingAccount (backport #16733) (#16735)

Co-authored-by: Emmanuel T Odeke <[email protected]>
Co-authored-by: Julien Robert <[email protected]>
  • Loading branch information
3 people authored Jun 28, 2023
1 parent 84256a0 commit 2377c63
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/auth) [#16554](https://github.com/cosmos/cosmos-sdk/pull/16554) `ModuleAccount.Validate` now reports a nil `.BaseAccount` instead of panicking.
* (baseapp) [#16613](https://github.com/cosmos/cosmos-sdk/pull/16613) Ensure each message in a transaction has a registered handler, otherwise `CheckTx` will fail.
* [#16639](https://github.com/cosmos/cosmos-sdk/pull/16639) Make sure we don't execute blocks beyond the halt height.
* (x/auth/vesting) [#16733](https://github.com/cosmos/cosmos-sdk/pull/16733) Panic on overflowing and negative EndTimes when creating a PeriodicVestingAccount.

### API Breaking Changes

Expand Down
5 changes: 5 additions & 0 deletions x/auth/vesting/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ func (s msgServer) CreatePeriodicVestingAccount(goCtx context.Context, msg *type
baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
vestingAccount := types.NewPeriodicVestingAccount(baseAccount, totalCoins.Sort(), msg.StartTime, msg.VestingPeriods)

// Enforce and sanity check that we don't have any negative endTime.
if bva := vestingAccount.BaseVestingAccount; bva != nil && bva.EndTime < 0 {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "cumulative endtime is negative")
}

s.AccountKeeper.SetAccount(ctx, vestingAccount)

defer func() {
Expand Down
9 changes: 8 additions & 1 deletion x/auth/vesting/types/vesting_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"errors"
"fmt"
"time"

"cosmossdk.io/math"
Expand Down Expand Up @@ -259,9 +260,15 @@ func NewPeriodicVestingAccountRaw(bva *BaseVestingAccount, startTime int64, peri
// NewPeriodicVestingAccount returns a new PeriodicVestingAccount
func NewPeriodicVestingAccount(baseAcc *authtypes.BaseAccount, originalVesting sdk.Coins, startTime int64, periods Periods) *PeriodicVestingAccount {
endTime := startTime
for _, p := range periods {
for i, p := range periods {
if p.Length < 0 {
panic(fmt.Sprintf("period #%d has a negative length: %d", i, p.Length))
}
endTime += p.Length
}
if endTime < 0 || endTime < startTime {
panic("cumulative endTime overflowed, and/or is less than startTime")
}
baseVestingAcc := &BaseVestingAccount{
BaseAccount: baseAcc,
OriginalVesting: originalVesting,
Expand Down
61 changes: 61 additions & 0 deletions x/auth/vesting/types/vesting_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,67 @@ func TestGetVestedCoinsPeriodicVestingAcc(t *testing.T) {
require.Equal(t, origCoins, vestedCoins)
}

func TestOverflowAndNegativeVestedCoinsPeriods(t *testing.T) {
now := tmtime.Now()
tests := []struct {
name string
periods []types.Period
wantPanic string
}{
{
"negative .Length",
types.Periods{
types.Period{Length: -1, Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}},
types.Period{Length: 6 * 60 * 60, Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}},
},
"period #0 has a negative length: -1",
},
{
"overflow after .Length additions",
types.Periods{
types.Period{Length: 9223372036854775108, Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}},
types.Period{Length: 6 * 60 * 60, Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}},
},
"cumulative endTime overflowed, and/or is less than startTime",
},
{
"good periods that are not negative nor overflow",
types.Periods{
types.Period{Length: now.Unix() - 1000, Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}},
types.Period{Length: 60, Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}},
},
"",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bacc, origCoins := initBaseAccount()
defer func() {
r := recover()
if r == nil {
if tt.wantPanic != "" {
t.Fatalf("expected a panic with substring: %q", tt.wantPanic)
}
return
}

// Otherwise ensure we match the panic substring.
require.Contains(t, r, tt.wantPanic)
}()

pva := types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), tt.periods)
if pva == nil {
return
}

if pbva := pva.BaseVestingAccount; pbva.EndTime < 0 {
t.Fatalf("Unfortunately we still have negative .EndTime :-(: %d", pbva.EndTime)
}
})
}
}

func TestGetVestingCoinsPeriodicVestingAcc(t *testing.T) {
now := tmtime.Now()
endTime := now.Add(24 * time.Hour)
Expand Down

0 comments on commit 2377c63

Please sign in to comment.