Skip to content

Commit bbabce4

Browse files
mdulin2Maxwell Dulin
and
Maxwell Dulin
authored
Wormchain token denom bug fix (#4265)
* Wormchain token denom bug fix * Fix conditional block height in test --------- Co-authored-by: Maxwell Dulin <strikeout@maxwells-mbp.lan>
1 parent 6de7cd2 commit bbabce4

File tree

9 files changed

+220
-2
lines changed

9 files changed

+220
-2
lines changed

wormchain/app/apptesting/test_suite.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ var (
5151
// Setup sets up basic environment for suite (App, Ctx, and test accounts)
5252
func (s *KeeperTestHelper) Setup() {
5353
s.App = Setup(s.T(), true, 0)
54-
s.Ctx = s.App.BaseApp.NewContext(false, tmtypes.Header{Height: 1, ChainID: "osmosis-1", Time: time.Now().UTC()})
54+
s.Ctx = s.App.BaseApp.NewContext(false, tmtypes.Header{Height: 1, ChainID: "wormchain", Time: time.Now().UTC()})
5555
s.QueryHelper = &baseapp.QueryServiceTestHelper{
5656
GRPCQueryRouter: s.App.GRPCQueryRouter(),
5757
Ctx: s.Ctx,

wormchain/x/tokenfactory/bindings/helpers_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121

2222
func CreateTestInput(t *testing.T) (*app.App, sdk.Context) {
2323
osmosis := apptesting.Setup(t, true, 0)
24-
ctx := osmosis.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: "osmosis-1", Time: time.Now().UTC()})
24+
ctx := osmosis.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: "wormchain", Time: time.Now().UTC()})
2525
return osmosis, ctx
2626
}
2727

wormchain/x/tokenfactory/keeper/bankactions.go

+22
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,37 @@ import (
66
sdk "github.com/cosmos/cosmos-sdk/types"
77

88
"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
9+
denoms "github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
910
)
1011

12+
var MainnetUseConditionalHeight = int64(12275950)
13+
1114
func (k Keeper) mintTo(ctx sdk.Context, amount sdk.Coin, mintTo string) error {
1215
// verify that denom is an x/tokenfactory denom
1316
_, _, err := types.DeconstructDenom(amount.Denom)
1417
if err != nil {
1518
return err
1619
}
1720

21+
// We enable the new conditional approximately two weeks after block 12,066,314 for mainnet and 13,361,706 on testnet, which is
22+
// calculated by dividing the number of seconds in a week by the average block time (~6s).
23+
// On testnet, the block height is different (and so is the block time) with a block time of ~6s.
24+
// On mainnet, the average block time is 5.77 seconds according to the PR https://github.com/wormhole-foundation/wormhole/pull/3946/files.
25+
// At 5.77 seconds/block, this is ~209,636 blocks for mainnet. On testnet at 6 seconds/block, this is ~201,600 blocks for testnet.
26+
// Therefore, mainnet cutover height is 12,066,314 + 209,636 = 12,275,950 and testnet cutover height is 13,361,706 + 201,600 = 13,563,306.
27+
// The target is about ~7:30pm UTC January 28th, 2025.
28+
isMainnet := ctx.ChainID() == "wormchain"
29+
isTestnet := ctx.ChainID() == "wormchain-testnet-0"
30+
31+
if (isMainnet && ctx.BlockHeight() >= MainnetUseConditionalHeight) || (isTestnet && ctx.BlockHeight() >= 13563306) {
32+
// Cutover is required because the call to GetSupply() will use more gas, which would result in a consensus failure.
33+
totalSupplyCurrent := k.bankKeeper.GetSupply(ctx, amount.Denom)
34+
TotalSupplyAfter := totalSupplyCurrent.Add(amount) // Can't integer overflow because of a ValidateBasic() check on this amount
35+
if TotalSupplyAfter.Amount.GTE(denoms.MintAmountLimit) {
36+
return fmt.Errorf("failed to mint - surpassed maximum mint amount")
37+
}
38+
}
39+
1840
err = k.bankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins(amount))
1941
if err != nil {
2042
return err

wormchain/x/tokenfactory/keeper/msg_server_test.go

+177
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@ package keeper_test
22

33
import (
44
"fmt"
5+
"math/big"
6+
"time"
57

68
"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
79

810
sdk "github.com/cosmos/cosmos-sdk/types"
11+
denoms "github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
12+
913
//banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
14+
tmtypes "github.com/tendermint/tendermint/proto/tendermint/types"
15+
keeper "github.com/wormhole-foundation/wormchain/x/tokenfactory/keeper"
1016
)
1117

1218
// TestMintDenomMsg tests TypeMsgMint message is emitted on a successful mint
@@ -49,6 +55,177 @@ func (suite *KeeperTestSuite) TestMintDenomMsg() {
4955
}
5056
}
5157

58+
func (suite *KeeperTestSuite) TestMintHuge() {
59+
// Create a denom
60+
suite.CreateDefaultDenom()
61+
62+
suite.Ctx = suite.App.BaseApp.NewContext(false, tmtypes.Header{Height: keeper.MainnetUseConditionalHeight, ChainID: "wormchain", Time: time.Now().UTC()})
63+
64+
largeAmount := big.NewInt(0).Sub(big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)), big.NewInt(1)) // (2 ** 256)-1
65+
belowLargeAmount := big.NewInt(0).Exp(big.NewInt(2), big.NewInt(191), big.NewInt(0)) // 2 ** 191
66+
for _, tc := range []struct {
67+
desc string
68+
amount sdk.Int
69+
mintDenom string
70+
admin string
71+
valid bool
72+
expectedMessageEvents int
73+
}{
74+
{
75+
desc: "failure case - too many",
76+
amount: sdk.NewIntFromBigInt(largeAmount),
77+
mintDenom: suite.defaultDenom,
78+
admin: suite.TestAccs[0].String(),
79+
valid: false,
80+
expectedMessageEvents: 0,
81+
},
82+
{
83+
desc: "success case with 191",
84+
amount: sdk.NewIntFromBigInt(belowLargeAmount),
85+
mintDenom: suite.defaultDenom,
86+
admin: suite.TestAccs[0].String(),
87+
valid: true,
88+
expectedMessageEvents: 1,
89+
},
90+
{
91+
desc: "failure case - too many accumulated tokens",
92+
amount: sdk.NewIntFromBigInt(belowLargeAmount),
93+
mintDenom: suite.defaultDenom,
94+
admin: suite.TestAccs[0].String(),
95+
valid: false,
96+
expectedMessageEvents: 0,
97+
},
98+
} {
99+
suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
100+
ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
101+
suite.Require().Equal(0, len(ctx.EventManager().Events()))
102+
// Test mint message
103+
suite.msgServer.Mint(sdk.WrapSDKContext(ctx), types.NewMsgMint(tc.admin, sdk.NewCoin(tc.mintDenom, tc.amount))) //nolint:errcheck
104+
// Ensure current number and type of event is emitted
105+
suite.AssertEventEmitted(ctx, types.TypeMsgMint, tc.expectedMessageEvents)
106+
})
107+
}
108+
}
109+
110+
func (suite *KeeperTestSuite) TestMintOffByOne() {
111+
// Create a denom
112+
suite.CreateDefaultDenom()
113+
suite.Ctx = suite.App.BaseApp.NewContext(false, tmtypes.Header{Height: keeper.MainnetUseConditionalHeight, ChainID: "wormchain", Time: time.Now().UTC()})
114+
115+
for _, tc := range []struct {
116+
desc string
117+
amount sdk.Int
118+
mintDenom string
119+
admin string
120+
valid bool
121+
expectedMessageEvents int
122+
}{
123+
{
124+
desc: "failure case - too many plus 1",
125+
amount: denoms.MintAmountLimit.Add(sdk.NewIntFromUint64(1)), // 2 ** 192 + 1
126+
mintDenom: suite.defaultDenom,
127+
admin: suite.TestAccs[0].String(),
128+
valid: false,
129+
expectedMessageEvents: 0,
130+
},
131+
{
132+
desc: "failure case - too many exactly",
133+
amount: denoms.MintAmountLimit, // 2 ** 192
134+
mintDenom: suite.defaultDenom,
135+
admin: suite.TestAccs[0].String(),
136+
valid: true,
137+
expectedMessageEvents: 0,
138+
},
139+
{
140+
desc: "success case - one less than limit",
141+
amount: denoms.MintAmountLimit.Sub(sdk.NewIntFromUint64(1)), // 2 ** 192 -1
142+
mintDenom: suite.defaultDenom,
143+
admin: suite.TestAccs[0].String(),
144+
valid: true,
145+
expectedMessageEvents: 1,
146+
},
147+
} {
148+
suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
149+
ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
150+
suite.Require().Equal(0, len(ctx.EventManager().Events()))
151+
// Test mint message
152+
suite.msgServer.Mint(sdk.WrapSDKContext(ctx), types.NewMsgMint(tc.admin, sdk.NewCoin(tc.mintDenom, tc.amount))) //nolint:errcheck
153+
// Ensure current number and type of event is emitted
154+
suite.AssertEventEmitted(ctx, types.TypeMsgMint, tc.expectedMessageEvents)
155+
})
156+
}
157+
}
158+
159+
func (suite *KeeperTestSuite) TestMintFixBlockHeightChecks() {
160+
// Create a denom
161+
suite.CreateDefaultDenom()
162+
163+
test_cases := []struct {
164+
desc string
165+
amount sdk.Int
166+
mintDenom string
167+
admin string
168+
valid bool
169+
expectedMessageEvents int
170+
}{
171+
{
172+
desc: "success case - check not implemented before block height",
173+
amount: denoms.MintAmountLimit, // 2 ** 192
174+
mintDenom: suite.defaultDenom,
175+
admin: suite.TestAccs[0].String(),
176+
valid: true,
177+
expectedMessageEvents: 1,
178+
},
179+
{
180+
desc: "failure case - check implemented on specific block height",
181+
amount: denoms.MintAmountLimit, // 2 ** 192
182+
mintDenom: suite.defaultDenom,
183+
admin: suite.TestAccs[0].String(),
184+
valid: false,
185+
expectedMessageEvents: 0,
186+
},
187+
{
188+
desc: "failure case - check implemented after specific block height",
189+
amount: denoms.MintAmountLimit, // 2 ** 192
190+
mintDenom: suite.defaultDenom,
191+
admin: suite.TestAccs[0].String(),
192+
valid: false,
193+
expectedMessageEvents: 0,
194+
},
195+
}
196+
// Before the block has been reached. Should succeed with the call.
197+
suite.Ctx = suite.App.BaseApp.NewContext(false, tmtypes.Header{Height: keeper.MainnetUseConditionalHeight - 1, ChainID: "wormchain", Time: time.Now().UTC()})
198+
199+
ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
200+
suite.Require().Equal(0, len(ctx.EventManager().Events()))
201+
// Test mint message
202+
suite.msgServer.Mint(sdk.WrapSDKContext(ctx), types.NewMsgMint(test_cases[0].admin, sdk.NewCoin(test_cases[0].mintDenom, test_cases[0].amount))) //nolint:errcheck
203+
204+
// Ensure current number and type of event is emitted
205+
suite.AssertEventEmitted(ctx, types.TypeMsgMint, test_cases[0].expectedMessageEvents)
206+
207+
// On the block has been reached
208+
suite.Ctx = suite.App.BaseApp.NewContext(false, tmtypes.Header{Height: keeper.MainnetUseConditionalHeight, ChainID: "wormchain", Time: time.Now().UTC()})
209+
ctx = suite.Ctx.WithEventManager(sdk.NewEventManager())
210+
suite.Require().Equal(0, len(ctx.EventManager().Events()))
211+
// Test mint message
212+
suite.msgServer.Mint(sdk.WrapSDKContext(ctx), types.NewMsgMint(test_cases[1].admin, sdk.NewCoin(test_cases[1].mintDenom, test_cases[1].amount))) //nolint:errcheck
213+
214+
// Ensure current number and type of event is emitted
215+
suite.AssertEventEmitted(ctx, types.TypeMsgMint, test_cases[1].expectedMessageEvents)
216+
217+
// After the block has been reached
218+
suite.Ctx = suite.App.BaseApp.NewContext(false, tmtypes.Header{Height: keeper.MainnetUseConditionalHeight + 1, ChainID: "wormchain", Time: time.Now().UTC()})
219+
ctx = suite.Ctx.WithEventManager(sdk.NewEventManager())
220+
suite.Require().Equal(0, len(ctx.EventManager().Events()))
221+
// Test mint message
222+
suite.msgServer.Mint(sdk.WrapSDKContext(ctx), types.NewMsgMint(test_cases[2].admin, sdk.NewCoin(test_cases[2].mintDenom, test_cases[2].amount))) //nolint:errcheck
223+
224+
// Ensure current number and type of event is emitted
225+
suite.AssertEventEmitted(ctx, types.TypeMsgMint, test_cases[2].expectedMessageEvents)
226+
227+
}
228+
52229
// TestBurnDenomMsg tests TypeMsgBurn message is emitted on a successful burn
53230
func (suite *KeeperTestSuite) TestBurnDenomMsg() {
54231
// Create a denom.

wormchain/x/tokenfactory/types/denoms.go

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package types
22

33
import (
4+
"math/big"
45
"strings"
56

67
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -18,6 +19,9 @@ const (
1819
MaxCreatorLength = 59 + MaxHrpLength
1920
)
2021

22+
// 2 ** 192
23+
var MintAmountLimit = sdk.NewIntFromBigInt(big.NewInt(0).Exp(big.NewInt(2), big.NewInt(192), big.NewInt(0)))
24+
2125
// GetTokenDenom constructs a denom string for tokens created by tokenfactory
2226
// based on an input creator address and a subdenom
2327
// The denom constructed is factory/{creator}/{subdenom}

wormchain/x/tokenfactory/types/errors.go

+1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ var (
2020
ErrCreatorTooLong = sdkerrors.Register(ModuleName, 9, fmt.Sprintf("creator too long, max length is %d bytes", MaxCreatorLength))
2121
ErrDenomDoesNotExist = sdkerrors.Register(ModuleName, 10, "denom does not exist")
2222
ErrCapabilityNotEnabled = sdkerrors.Register(ModuleName, 11, "this capability is not enabled on chain")
23+
ErrMintAmountTooLarge = sdkerrors.Register(ModuleName, 12, "mint amount exceeds capacity")
2324
)

wormchain/x/tokenfactory/types/expected_keepers.go

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type BankKeeper interface {
1212
SetDenomMetaData(ctx sdk.Context, denomMetaData banktypes.Metadata)
1313

1414
HasSupply(ctx sdk.Context, denom string) bool
15+
GetSupply(ctx sdk.Context, denom string) sdk.Coin
1516

1617
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
1718
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error

wormchain/x/tokenfactory/types/msgs.go

+4
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ func (m MsgMint) ValidateBasic() error {
8888
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String())
8989
}
9090

91+
if m.Amount.Amount.GTE(MintAmountLimit) {
92+
return ErrMintAmountTooLarge
93+
}
94+
9195
return nil
9296
}
9397

wormchain/x/tokenfactory/types/msgs_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package types_test
22

33
import (
44
fmt "fmt"
5+
"math/big"
56
"testing"
67

78
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -189,6 +190,14 @@ func TestMsgMint(t *testing.T) {
189190
}),
190191
expectPass: false,
191192
},
193+
{
194+
name: "too large amount",
195+
msg: createMsg(func(msg types.MsgMint) types.MsgMint {
196+
msg.Amount.Amount = sdk.NewIntFromBigInt(big.NewInt(0).Sub(big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)), big.NewInt(1)))
197+
return msg
198+
}),
199+
expectPass: false,
200+
},
192201
}
193202

194203
for _, test := range tests {

0 commit comments

Comments
 (0)