From b76911f1c646a5d1d22dab3722600018898b1720 Mon Sep 17 00:00:00 2001 From: Spoorthi Date: Tue, 23 May 2023 13:58:06 +0000 Subject: [PATCH] implementing gas consume on denom creation - https://github.com/osmosis-labs/osmosis/pull/4983 --- docs/proto/proto-docs.md | 5 +- .../tokenfactory/v1beta1/tokenfactory.proto | 13 +++ x/tokenfactory/keeper/create_denom.go | 25 +++-- x/tokenfactory/keeper/create_denom_test.go | 65 +++++++++++++ x/tokenfactory/types/params.go | 21 ++++- x/tokenfactory/types/tokenfactory.pb.go | 92 ++++++++++++++----- 6 files changed, 184 insertions(+), 37 deletions(-) diff --git a/docs/proto/proto-docs.md b/docs/proto/proto-docs.md index a5ba5e3a0..8c97731a1 100644 --- a/docs/proto/proto-docs.md +++ b/docs/proto/proto-docs.md @@ -156,7 +156,10 @@ Params defines the parameters for the tokenfactory module. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `denom_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `denom_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | DenomCreationFee defines the fee to be charged on the creation of a new denom. The fee is drawn from the MsgCreateDenom's sender account, and transferred to the community pool. | +| `denom_creation_gas_consume` | [uint64](#uint64) | | DenomCreationGasConsume defines the gas cost for creating a new denom. This is intended as a spam deterrence mechanism. + +See: https://github.com/CosmWasm/token-factory/issues/11 | diff --git a/proto/osmosis/tokenfactory/v1beta1/tokenfactory.proto b/proto/osmosis/tokenfactory/v1beta1/tokenfactory.proto index ff342bf0d..46f472bc2 100644 --- a/proto/osmosis/tokenfactory/v1beta1/tokenfactory.proto +++ b/proto/osmosis/tokenfactory/v1beta1/tokenfactory.proto @@ -19,9 +19,22 @@ message DenomAuthorityMetadata { // Params defines the parameters for the tokenfactory module. message Params { + // DenomCreationFee defines the fee to be charged on the creation of a new + // denom. The fee is drawn from the MsgCreateDenom's sender account, and + // transferred to the community pool. repeated cosmos.base.v1beta1.Coin denom_creation_fee = 1 [ (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.moretags) = "yaml:\"denom_creation_fee\"", (gogoproto.nullable) = false ]; + + + // DenomCreationGasConsume defines the gas cost for creating a new denom. + // This is intended as a spam deterrence mechanism. + // + // See: https://github.com/CosmWasm/token-factory/issues/11 + uint64 denom_creation_gas_consume = 2 [ + (gogoproto.moretags) = "yaml:\"denom_creation_gas_consume\"", + (gogoproto.nullable) = true + ]; } \ No newline at end of file diff --git a/x/tokenfactory/keeper/create_denom.go b/x/tokenfactory/keeper/create_denom.go index 6b1aaf07b..60ecb8432 100644 --- a/x/tokenfactory/keeper/create_denom.go +++ b/x/tokenfactory/keeper/create_denom.go @@ -70,16 +70,25 @@ func (k Keeper) validateCreateDenom(ctx sdk.Context, creatorAddr string, subdeno } func (k Keeper) chargeForCreateDenom(ctx sdk.Context, creatorAddr string, _ string) (err error) { - // Send creation fee to community pool - creationFee := k.GetParams(ctx).DenomCreationFee - accAddr, err := sdk.AccAddressFromBech32(creatorAddr) - if err != nil { - return err - } - if creationFee != nil { - if err := k.communityPoolKeeper.FundCommunityPool(ctx, creationFee, accAddr); err != nil { + params := k.GetParams(ctx) + + // if DenomCreationFee is non-zero, transfer the tokens from the creator + // account to community pool + if params.DenomCreationFee != nil { + accAddr, err := sdk.AccAddressFromBech32(creatorAddr) + if err != nil { + return err + } + + if err := k.communityPoolKeeper.FundCommunityPool(ctx, params.DenomCreationFee, accAddr); err != nil { return err } } + + // if DenomCreationGasConsume is non-zero, consume the gas + if params.DenomCreationGasConsume != 0 { + ctx.GasMeter().ConsumeGas(params.DenomCreationGasConsume, "consume denom creation gas") + } + return nil } diff --git a/x/tokenfactory/keeper/create_denom_test.go b/x/tokenfactory/keeper/create_denom_test.go index 382ffc7cf..820f2ab3e 100644 --- a/x/tokenfactory/keeper/create_denom_test.go +++ b/x/tokenfactory/keeper/create_denom_test.go @@ -158,3 +158,68 @@ func (suite *KeeperTestSuite) TestCreateDenom() { }) } } + +func (suite *KeeperTestSuite) TestGasConsume() { + // It's hard to estimate exactly how much gas will be consumed when creating a + // denom, because besides consuming the gas specified by the params, the keeper + // also does a bunch of other things that consume gas. + // + // Rather, we test whether the gas consumed is within a range. Specifically, + // the range [gasConsume, gasConsume + offset]. If the actual gas consumption + // falls within the range for all test cases, we consider the test passed. + // + // In experience, the total amount of gas consumed should consume be ~30k more + // than the set amount. + const offset = 50000 + + for _, tc := range []struct { + desc string + gasConsume uint64 + }{ + { + desc: "gas consume zero", + gasConsume: 0, + }, + { + desc: "gas consume 1,000,000", + gasConsume: 1_000_000, + }, + { + desc: "gas consume 10,000,000", + gasConsume: 10_000_000, + }, + { + desc: "gas consume 25,000,000", + gasConsume: 25_000_000, + }, + { + desc: "gas consume 50,000,000", + gasConsume: 50_000_000, + }, + { + desc: "gas consume 200,000,000", + gasConsume: 200_000_000, + }, + } { + suite.SetupTest() + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + // set params with the gas consume amount + suite.App.TokenFactoryKeeper.SetParams(suite.Ctx, types.NewParams(nil, tc.gasConsume)) + + // amount of gas consumed prior to the denom creation + gasConsumedBefore := suite.Ctx.GasMeter().GasConsumed() + + // create a denom + _, err := suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "larry")) + suite.Require().NoError(err) + + // amount of gas consumed after the denom creation + gasConsumedAfter := suite.Ctx.GasMeter().GasConsumed() + + // the amount of gas consumed must be within the range + gasConsumed := gasConsumedAfter - gasConsumedBefore + suite.Require().Greater(gasConsumed, tc.gasConsume) + suite.Require().Less(gasConsumed, tc.gasConsume+offset) + }) + } +} diff --git a/x/tokenfactory/types/params.go b/x/tokenfactory/types/params.go index 5489a11b2..a1fa44ca1 100644 --- a/x/tokenfactory/types/params.go +++ b/x/tokenfactory/types/params.go @@ -9,7 +9,8 @@ import ( // Parameter store keys. var ( - KeyDenomCreationFee = []byte("DenomCreationFee") + KeyDenomCreationFee = []byte("DenomCreationFee") + KeyDenomCreationGasConsume = []byte("DenomCreationGasConsume") ) // ParamTable for gamm module. @@ -17,16 +18,18 @@ func ParamKeyTable() paramtypes.KeyTable { return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) } -func NewParams(denomCreationFee sdk.Coins) Params { +func NewParams(denomCreationFee sdk.Coins, denomCreationGasConsume uint64) Params { return Params{ - DenomCreationFee: denomCreationFee, + DenomCreationFee: denomCreationFee, + DenomCreationGasConsume: denomCreationGasConsume, } } // default gamm module parameters. func DefaultParams() Params { return Params{ - DenomCreationFee: sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10_000_000)), // 10 STAKE + DenomCreationFee: sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10_000_000)), // 10 STAKE + DenomCreationGasConsume: 0, } } @@ -39,6 +42,7 @@ func (p Params) Validate() error { func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ paramtypes.NewParamSetPair(KeyDenomCreationFee, &p.DenomCreationFee, validateDenomCreationFee), + paramtypes.NewParamSetPair(KeyDenomCreationGasConsume, &p.DenomCreationGasConsume, validateDenomCreationGasConsume), } } @@ -54,3 +58,12 @@ func validateDenomCreationFee(i interface{}) error { return nil } + +func validateDenomCreationGasConsume(i interface{}) error { + _, ok := i.(uint64) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return nil +} diff --git a/x/tokenfactory/types/tokenfactory.pb.go b/x/tokenfactory/types/tokenfactory.pb.go index c9b1bcdc0..25629f3df 100644 --- a/x/tokenfactory/types/tokenfactory.pb.go +++ b/x/tokenfactory/types/tokenfactory.pb.go @@ -76,7 +76,15 @@ func (m *DenomAuthorityMetadata) GetAdmin() string { // Params defines the parameters for the tokenfactory module. type Params struct { + // DenomCreationFee defines the fee to be charged on the creation of a new + // denom. The fee is drawn from the MsgCreateDenom's sender account, and + // transferred to the community pool. DenomCreationFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=denom_creation_fee,json=denomCreationFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"denom_creation_fee" yaml:"denom_creation_fee"` + // DenomCreationGasConsume defines the gas cost for creating a new denom. + // This is intended as a spam deterrence mechanism. + // + // See: https://github.com/CosmWasm/token-factory/issues/11 + DenomCreationGasConsume uint64 `protobuf:"varint,2,opt,name=denom_creation_gas_consume,json=denomCreationGasConsume,proto3" json:"denom_creation_gas_consume,omitempty" yaml:"denom_creation_gas_consume"` } func (m *Params) Reset() { *m = Params{} } @@ -119,6 +127,13 @@ func (m *Params) GetDenomCreationFee() github_com_cosmos_cosmos_sdk_types.Coins return nil } +func (m *Params) GetDenomCreationGasConsume() uint64 { + if m != nil { + return m.DenomCreationGasConsume + } + return 0 +} + func init() { proto.RegisterType((*DenomAuthorityMetadata)(nil), "osmosis.tokenfactory.v1beta1.DenomAuthorityMetadata") proto.RegisterType((*Params)(nil), "osmosis.tokenfactory.v1beta1.Params") @@ -129,30 +144,32 @@ func init() { } var fileDescriptor_ce345928cf8ce182 = []byte{ - // 353 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0xbf, 0x4e, 0x2a, 0x41, - 0x14, 0xc6, 0x77, 0x72, 0xef, 0x25, 0xb9, 0x7b, 0x6f, 0x41, 0x36, 0xc6, 0x00, 0x31, 0xbb, 0x64, - 0x0b, 0x43, 0xc3, 0x8e, 0x68, 0x87, 0x95, 0x60, 0xe8, 0x48, 0x0c, 0x89, 0x8d, 0x0d, 0x39, 0xbb, - 0x3b, 0x2c, 0x13, 0xd8, 0x3d, 0x64, 0x67, 0x50, 0xd7, 0xa7, 0xb0, 0xb2, 0xd6, 0xd6, 0x27, 0xa1, - 0xa4, 0xb4, 0x42, 0x03, 0x8d, 0x35, 0x4f, 0x60, 0x76, 0x67, 0x34, 0x12, 0xab, 0x99, 0x73, 0xbe, - 0xf9, 0x7e, 0xe7, 0xcf, 0x98, 0x14, 0x45, 0x8c, 0x82, 0x0b, 0x2a, 0x71, 0xc2, 0x92, 0x11, 0x04, - 0x12, 0xd3, 0x8c, 0x5e, 0xb7, 0x7c, 0x26, 0xa1, 0xb5, 0x93, 0xf4, 0x66, 0x29, 0x4a, 0xb4, 0x0e, - 0xb4, 0xc1, 0xdb, 0xd1, 0xb4, 0xa1, 0xb6, 0x17, 0x61, 0x84, 0xc5, 0x43, 0x9a, 0xdf, 0x94, 0xa7, - 0x56, 0x0d, 0x0a, 0xd3, 0x50, 0x09, 0x2a, 0xd0, 0x92, 0xad, 0x22, 0xea, 0x83, 0x60, 0x5f, 0x65, - 0x03, 0xe4, 0x89, 0xd2, 0xdd, 0x9e, 0xb9, 0x7f, 0xce, 0x12, 0x8c, 0xcf, 0xe6, 0x72, 0x8c, 0x29, - 0x97, 0x59, 0x9f, 0x49, 0x08, 0x41, 0x82, 0x75, 0x68, 0xfe, 0x81, 0x30, 0xe6, 0x49, 0x85, 0xd4, - 0x49, 0xe3, 0x6f, 0xa7, 0xbc, 0x5d, 0x39, 0xff, 0x33, 0x88, 0xa7, 0x6d, 0xb7, 0x48, 0xbb, 0x03, - 0x25, 0xb7, 0x7f, 0xbf, 0x3f, 0x3a, 0xc4, 0x7d, 0x22, 0x66, 0xe9, 0x02, 0x52, 0x88, 0x85, 0xf5, - 0x40, 0x4c, 0x2b, 0xcc, 0x99, 0xc3, 0x20, 0x65, 0x20, 0x39, 0x26, 0xc3, 0x11, 0x63, 0x15, 0x52, - 0xff, 0xd5, 0xf8, 0x77, 0x5c, 0xf5, 0x74, 0x7b, 0x79, 0x43, 0x9f, 0x63, 0x79, 0x5d, 0xe4, 0x49, - 0xa7, 0xbf, 0x58, 0x39, 0xc6, 0x76, 0xe5, 0x54, 0x55, 0x95, 0x9f, 0x08, 0xf7, 0xf9, 0xd5, 0x69, - 0x44, 0x5c, 0x8e, 0xe7, 0xbe, 0x17, 0x60, 0xac, 0x07, 0xd5, 0x47, 0x53, 0x84, 0x13, 0x2a, 0xb3, - 0x19, 0x13, 0x05, 0x4d, 0x0c, 0xca, 0x05, 0xa0, 0xab, 0xfd, 0x3d, 0xc6, 0x3a, 0x97, 0x8b, 0xb5, - 0x4d, 0x96, 0x6b, 0x9b, 0xbc, 0xad, 0x6d, 0x72, 0xbf, 0xb1, 0x8d, 0xe5, 0xc6, 0x36, 0x5e, 0x36, - 0xb6, 0x71, 0x75, 0xfa, 0x8d, 0x3a, 0x9b, 0xfb, 0x53, 0x1e, 0x34, 0xe1, 0x86, 0x09, 0x8c, 0x19, - 0x15, 0x12, 0xd2, 0x08, 0xee, 0xf2, 0xe5, 0x1d, 0xd1, 0xdb, 0xdd, 0x6f, 0x2c, 0xca, 0xf9, 0xa5, - 0x62, 0x93, 0x27, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x95, 0xb7, 0x19, 0xeb, 0x01, 0x00, - 0x00, + // 396 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x52, 0xb1, 0x8e, 0xd3, 0x40, + 0x10, 0xf5, 0x1e, 0xc7, 0x49, 0x18, 0x8a, 0x93, 0x85, 0x20, 0x89, 0x90, 0x1d, 0x5c, 0x20, 0x53, + 0x9c, 0x97, 0x40, 0x17, 0x2a, 0x1c, 0x14, 0xaa, 0x48, 0x28, 0x12, 0x0d, 0x8d, 0x35, 0xb6, 0x37, + 0xce, 0x2a, 0x59, 0x4f, 0xe4, 0x5d, 0x03, 0xe6, 0x0f, 0xe8, 0xa8, 0xa8, 0xa9, 0xf9, 0x92, 0x94, + 0x29, 0xa9, 0x0c, 0x4a, 0x1a, 0xea, 0x7c, 0x01, 0xb2, 0xd7, 0xa0, 0x18, 0xae, 0xb2, 0x67, 0xde, + 0xbc, 0x37, 0xef, 0x69, 0xd6, 0xa4, 0x28, 0x05, 0x4a, 0x2e, 0xa9, 0xc2, 0x15, 0xcb, 0x16, 0x10, + 0x2b, 0xcc, 0x4b, 0xfa, 0x6e, 0x14, 0x31, 0x05, 0xa3, 0x4e, 0xd3, 0xdf, 0xe4, 0xa8, 0xd0, 0x7a, + 0xd0, 0x12, 0xfc, 0x0e, 0xd6, 0x12, 0x06, 0x77, 0x53, 0x4c, 0xb1, 0x19, 0xa4, 0xf5, 0x9f, 0xe6, + 0x0c, 0xfa, 0x71, 0x43, 0x0a, 0x35, 0xa0, 0x8b, 0x16, 0xb2, 0x75, 0x45, 0x23, 0x90, 0xec, 0xef, + 0xda, 0x18, 0x79, 0xa6, 0x71, 0x77, 0x6a, 0xde, 0x7b, 0xc9, 0x32, 0x14, 0x2f, 0x0a, 0xb5, 0xc4, + 0x9c, 0xab, 0x72, 0xc6, 0x14, 0x24, 0xa0, 0xc0, 0x7a, 0x64, 0xde, 0x84, 0x44, 0xf0, 0xac, 0x47, + 0x86, 0xc4, 0xbb, 0x15, 0x5c, 0x1e, 0x2b, 0xe7, 0x4e, 0x09, 0x62, 0x3d, 0x76, 0x9b, 0xb6, 0x3b, + 0xd7, 0xf0, 0xf8, 0xfc, 0xd7, 0x57, 0x87, 0xb8, 0x9f, 0xce, 0xcc, 0x8b, 0xd7, 0x90, 0x83, 0x90, + 0xd6, 0x17, 0x62, 0x5a, 0x49, 0xad, 0x19, 0xc6, 0x39, 0x03, 0xc5, 0x31, 0x0b, 0x17, 0x8c, 0xf5, + 0xc8, 0xf0, 0x86, 0x77, 0xfb, 0x69, 0xdf, 0x6f, 0xed, 0xd5, 0x86, 0xfe, 0xc4, 0xf2, 0x27, 0xc8, + 0xb3, 0x60, 0xb6, 0xad, 0x1c, 0xe3, 0x58, 0x39, 0x7d, 0xbd, 0xe5, 0x7f, 0x09, 0xf7, 0xdb, 0x0f, + 0xc7, 0x4b, 0xb9, 0x5a, 0x16, 0x91, 0x1f, 0xa3, 0x68, 0x83, 0xb6, 0x9f, 0x2b, 0x99, 0xac, 0xa8, + 0x2a, 0x37, 0x4c, 0x36, 0x6a, 0x72, 0x7e, 0xd9, 0x08, 0x4c, 0x5a, 0xfe, 0x94, 0x31, 0x6b, 0x61, + 0x0e, 0xfe, 0x11, 0x4d, 0x41, 0x86, 0x31, 0x66, 0xb2, 0x10, 0xac, 0x77, 0x36, 0x24, 0xde, 0x79, + 0xf0, 0x78, 0x5b, 0x39, 0xe4, 0x58, 0x39, 0x0f, 0xaf, 0x35, 0x71, 0x32, 0xef, 0xce, 0xef, 0x77, + 0x16, 0xbc, 0x02, 0x39, 0xd1, 0x48, 0xf0, 0x66, 0xbb, 0xb7, 0xc9, 0x6e, 0x6f, 0x93, 0x9f, 0x7b, + 0x9b, 0x7c, 0x3e, 0xd8, 0xc6, 0xee, 0x60, 0x1b, 0xdf, 0x0f, 0xb6, 0xf1, 0xf6, 0xf9, 0x89, 0xfb, + 0x4d, 0x11, 0xad, 0x79, 0x7c, 0x05, 0xef, 0x99, 0x44, 0xc1, 0xa8, 0x54, 0x90, 0xa7, 0xf0, 0xb1, + 0x3e, 0xd2, 0x13, 0xfa, 0xa1, 0xfb, 0x5c, 0x9a, 0x58, 0xd1, 0x45, 0x73, 0xb1, 0x67, 0xbf, 0x03, + 0x00, 0x00, 0xff, 0xff, 0x3a, 0xaa, 0x5e, 0xd8, 0x53, 0x02, 0x00, 0x00, } func (this *DenomAuthorityMetadata) Equal(that interface{}) bool { @@ -229,6 +246,11 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.DenomCreationGasConsume != 0 { + i = encodeVarintTokenfactory(dAtA, i, uint64(m.DenomCreationGasConsume)) + i-- + dAtA[i] = 0x10 + } if len(m.DenomCreationFee) > 0 { for iNdEx := len(m.DenomCreationFee) - 1; iNdEx >= 0; iNdEx-- { { @@ -282,6 +304,9 @@ func (m *Params) Size() (n int) { n += 1 + l + sovTokenfactory(uint64(l)) } } + if m.DenomCreationGasConsume != 0 { + n += 1 + sovTokenfactory(uint64(m.DenomCreationGasConsume)) + } return n } @@ -436,6 +461,25 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomCreationGasConsume", wireType) + } + m.DenomCreationGasConsume = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTokenfactory + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DenomCreationGasConsume |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTokenfactory(dAtA[iNdEx:])