Skip to content

Commit 574fe82

Browse files
ashishchandr70Ashish Chandralukitsbrianrkollar
authored
Applying commits from main into release/1.0 (#118)
* Release 1.0: Upgrade Handler and Module Improvements (#82) * Upgrade to 1.0 and fix gosec issues * Added acltypes.StoreKey to registered upgrade handlers * Added more store upgrades * Added remaining custom stores to storeUpgrades * Removing ccvprovider types store key * Changed genesis module init order * use non-consumer modules * WIP for handling the InitGenesis related error * manually initialize provider module (#81) * Renamed upgrade to match go proposal passed in staging * Renamed upgrade back to 1.0 --------- Co-authored-by: Ashish Chandra <ashish@saga.xyz> Co-authored-by: Brian Luk <brian6.dev@gmail.com> Co-authored-by: Brian <45702419+lukitsbrian@users.noreply.github.com> * update params to match spc params (#84) * fix: capture setup fee in escrow deposit calculation (#92) The setup fee in LaunchChainlet() was added to deposit without capturing the returned coin, so the escrow account was funded with only the epoch deposit. This fix assigns the result of deposit.Add(setupfee) back to deposit before calling NewChainletAccount. * Use platform validator set for epoch reward distribution (#93) * use platform validator set for chainlet token distributions * Add fallback to staking validators when no platform validators configured - If PlatformValidators param is empty, fall back to GetValidators() - Add guard against division by zero if no validators exist - Fixes potential panic when PlatformValidators is not set * Fix lint errors in billing module * Fix params test: use nil instead of empty slice for PlatformValidators default * fix(escrow): use QuoTruncate for withdrawal to prevent over-withdrawal (#94) * fix: complete genesis export/import for all modules (#95) * fix: complete genesis export/import for all modules Multiple custom modules only serialized params in ExportGenesis, leaving their keeper KV state out of every exported genesis. This caused data loss during export/import upgrades, state sync bootstraps, or genesis restarts. Changes: - x/chainlet: Export/import chainlets, chainlet stacks, and chainlet count - x/escrow: Export/import chainlet accounts, denomination pools, and funders - x/billing: Export/import billing history and validator payout history - x/peers: Export/import peer data and chain counters For each module: - Extended genesis protobuf to include full KV store contents - Updated ExportGenesis to iterate store prefixes and serialize data - Updated InitGenesis to rehydrate from serialized data - Added validation for duplicate entries - Updated tests to cover new fields and validation * x/peers: input validation * x/chainlet: add IBC tests * fix(escrow): reset shares when pool is fully drained (#113) Clear all funders and reset shares to zero when billing drains a pool's balance to zero. Also add defensive check in deposit to handle invalid pool states. Fixes sherlock-audit/2025-11-saga-ssc-nov-14th#181 * x/chainlet: non-CCV chainlet upgradeable by Saga * x/chainlet: add upgrade restrictions and unit tests * x/chainlet: make upgrade access check more readable * x/chainlet: fix service chainlet not added as a consumer * Missing commits from main. Updated failing mock test * Update saga-sdk (#117) Fixes x/peers access control in the ante handler. --------- Co-authored-by: Ashish Chandra <ashish@saga.xyz> Co-authored-by: Brian Luk <brian6.dev@gmail.com> Co-authored-by: Brian <45702419+lukitsbrian@users.noreply.github.com> Co-authored-by: Roman Kollár <10644651+rkollar@users.noreply.github.com>
1 parent 9297323 commit 574fe82

11 files changed

Lines changed: 419 additions & 102 deletions

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ require (
3535
github.com/grpc-ecosystem/grpc-gateway v1.16.0
3636
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
3737
github.com/ignite/cli v0.27.2
38-
github.com/sagaxyz/saga-sdk v0.11.0
38+
github.com/sagaxyz/saga-sdk v0.11.1
3939
github.com/spf13/cast v1.9.2
4040
github.com/spf13/cobra v1.9.1
4141
github.com/spf13/pflag v1.0.7
@@ -211,14 +211,14 @@ require (
211211
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
212212
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
213213
go.opentelemetry.io/otel/trace v1.37.0 // indirect
214-
go.uber.org/mock v0.5.2 // indirect
214+
go.uber.org/mock v0.6.0 // indirect
215215
go.uber.org/multierr v1.11.0 // indirect
216216
go.yaml.in/yaml/v2 v2.4.2 // indirect
217217
golang.org/x/arch v0.17.0 // indirect
218218
golang.org/x/crypto v0.38.0 // indirect
219219
golang.org/x/net v0.40.0 // indirect
220220
golang.org/x/oauth2 v0.30.0 // indirect
221-
golang.org/x/sync v0.14.0 // indirect
221+
golang.org/x/sync v0.16.0 // indirect
222222
golang.org/x/sys v0.33.0 // indirect
223223
golang.org/x/term v0.32.0 // indirect
224224
golang.org/x/text v0.25.0 // indirect

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,8 +1484,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
14841484
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
14851485
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
14861486
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
1487-
github.com/sagaxyz/saga-sdk v0.11.0 h1:NdKuBLjOqMjhhhVA7xHe3lXREn9h1uMTUmnB2Ou+m9U=
1488-
github.com/sagaxyz/saga-sdk v0.11.0/go.mod h1:jccuO6o/seZpjBQvFHDmMq+tSoa/op/tZsR65+A/7/g=
1487+
github.com/sagaxyz/saga-sdk v0.11.1 h1:NNVAL3QHEaMiEpVIvvCKzbMj9kWE02QmQeU9kPWP1l4=
1488+
github.com/sagaxyz/saga-sdk v0.11.1/go.mod h1:XQEnR9jgvV9fYQeTRg/wPYCGMUyR5z2eKGuCoO+CYqc=
14891489
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
14901490
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
14911491
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
@@ -1627,8 +1627,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
16271627
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
16281628
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
16291629
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
1630-
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
1631-
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
1630+
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
1631+
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
16321632
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
16331633
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
16341634
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
@@ -1853,8 +1853,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
18531853
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
18541854
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
18551855
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
1856-
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
1857-
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
1856+
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
1857+
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
18581858
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
18591859
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
18601860
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

x/chainlet/keeper/ccv_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ func (s *TestSuite) TestConsumerVSC() {
2727
BillAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
2828
Return(nil).
2929
AnyTimes()
30+
s.aclKeeper.EXPECT().
31+
IsAdmin(gomock.Any(), gomock.Any()).
32+
Return(false).
33+
AnyTimes()
3034

3135
// Set up expectations in order (only one round)
3236
gomock.InOrder(

x/chainlet/keeper/ibc_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ func (s *TestSuite) ibcSetup(chainID, consumerID, channelID string) {
2828
BillAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
2929
Return(nil).
3030
AnyTimes()
31+
s.aclKeeper.EXPECT().
32+
IsAdmin(gomock.Any(), gomock.Any()).
33+
Return(false).
34+
AnyTimes()
3135
s.providerKeeper.EXPECT().
3236
GetValidatorSetUpdateId(gomock.Any()).
3337
Return(uint64(1)).

x/chainlet/keeper/keeper_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ var (
3030
addrs = []sdk.AccAddress{
3131
sdk.AccAddress("test1"),
3232
sdk.AccAddress("test2"),
33+
sdk.AccAddress("test3"),
3334
}
34-
creator = addrs[0]
35+
creator = addrs[0]
36+
maintainer = addrs[1]
37+
admin = addrs[2]
3538
)
3639

3740
type TestSuite struct {
@@ -83,11 +86,6 @@ func (s *TestSuite) SetupTest() {
8386
s.escrowKeeper = chainlettestutil.NewMockEscrowKeeper(ctrl)
8487
s.providerMsgServer = chainlettestutil.NewMockProviderMsgServer(ctrl)
8588

86-
s.aclKeeper.EXPECT().
87-
IsAdmin(gomock.Any(), gomock.Any()).
88-
Return(true).
89-
AnyTimes()
90-
9189
// Set up Staking keeper expectations for GetAllValidators since it's used in msg_server_launch_chainlet.go
9290
s.stakingKeeper.EXPECT().
9391
GetAllValidators(gomock.Any()).

x/chainlet/keeper/msg_server_disable_stack_version_test.go

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package keeper_test
33
import (
44
"fmt"
55

6+
ccvprovidertypes "github.com/cosmos/interchain-security/v7/x/ccv/provider/types"
67
"github.com/golang/mock/gomock"
8+
79
"github.com/sagaxyz/ssc/x/chainlet/types"
810
)
911

@@ -18,17 +20,33 @@ func (s *TestSuite) TestDisabledVersionsLaunch() {
1820
BillAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
1921
Return(nil).
2022
AnyTimes()
23+
s.aclKeeper.EXPECT().
24+
IsAdmin(gomock.Any(), gomock.Any()).
25+
Return(false).
26+
AnyTimes()
27+
s.providerMsgServer.EXPECT().
28+
CreateConsumer(gomock.Any(), gomock.Any()).
29+
Return(&ccvprovidertypes.MsgCreateConsumerResponse{
30+
ConsumerId: "0",
31+
}, nil)
32+
s.providerKeeper.EXPECT().
33+
GetValidatorSetUpdateId(gomock.Any()).
34+
Return(uint64(1))
35+
s.providerKeeper.EXPECT().
36+
AppendPendingVSCPackets(gomock.Any(), gomock.Eq("0"), gomock.Any())
37+
s.providerKeeper.EXPECT().
38+
IncrementValidatorSetUpdateId(gomock.Any())
2139

2240
// Create a stack
2341
ver := "1.2.3"
2442
_, err := s.msgServer.CreateChainletStack(s.ctx, types.NewMsgCreateChainletStack(
25-
creator.String(), "test", "test", "test/test:"+ver, ver, "abcd"+ver, fees, false,
43+
creator.String(), "test", "test", "test/test:"+ver, ver, "abcd"+ver, fees, true,
2644
))
2745
s.Require().NoError(err)
2846

2947
// Launch a chainlet
3048
_, err = s.msgServer.LaunchChainlet(s.ctx, types.NewMsgLaunchChainlet(
31-
creator.String(), nil, "test", ver, "test_chainlet", "test_12345-1", "asaga", types.ChainletParams{}, nil, false, "",
49+
creator.String(), []string{creator.String()}, "test", ver, "test_chainlet", "test_12345-1", "asaga", types.ChainletParams{}, nil, false, "",
3250
))
3351
s.Require().NoError(err)
3452

@@ -38,7 +56,7 @@ func (s *TestSuite) TestDisabledVersionsLaunch() {
3856

3957
// Try and fail to launch another chainlet
4058
_, err = s.msgServer.LaunchChainlet(s.ctx, types.NewMsgLaunchChainlet(
41-
creator.String(), nil, "test", ver, "test_chainlet", "test_12346-1", "asaga", types.ChainletParams{}, nil, false, "",
59+
creator.String(), []string{creator.String()}, "test", ver, "test_chainlet", "test_12346-1", "asaga", types.ChainletParams{}, nil, false, "",
4260
))
4361
s.Require().Error(err)
4462
}
@@ -49,7 +67,7 @@ func (s *TestSuite) TestDisabledVersionsUpgrade() {
4967
// Create a stack
5068
ver := "1.2.3"
5169
_, err := s.msgServer.CreateChainletStack(s.ctx, types.NewMsgCreateChainletStack(
52-
creator.String(), "test", "test", "test/test:"+ver, ver, "abcd"+ver, fees, false,
70+
creator.String(), "test", "test", "test/test:"+ver, ver, "abcd"+ver, fees, true,
5371
))
5472
s.Require().NoError(err)
5573

@@ -60,15 +78,32 @@ func (s *TestSuite) TestDisabledVersionsUpgrade() {
6078
s.billingKeeper.EXPECT().
6179
BillAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
6280
Return(nil)
81+
s.aclKeeper.EXPECT().
82+
IsAdmin(gomock.Any(), gomock.Any()).
83+
Return(false).
84+
AnyTimes()
85+
s.providerMsgServer.EXPECT().
86+
CreateConsumer(gomock.Any(), gomock.Any()).
87+
Return(&ccvprovidertypes.MsgCreateConsumerResponse{
88+
ConsumerId: "0",
89+
}, nil)
90+
s.providerKeeper.EXPECT().
91+
GetValidatorSetUpdateId(gomock.Any()).
92+
Return(uint64(1))
93+
s.providerKeeper.EXPECT().
94+
AppendPendingVSCPackets(gomock.Any(), gomock.Eq("0"), gomock.Any())
95+
s.providerKeeper.EXPECT().
96+
IncrementValidatorSetUpdateId(gomock.Any())
97+
6398
_, err = s.msgServer.LaunchChainlet(s.ctx, types.NewMsgLaunchChainlet(
64-
creator.String(), nil, "test", ver, "test_chainlet", "test_12345-1", "asaga", types.ChainletParams{}, nil, false, "",
99+
creator.String(), []string{creator.String()}, "test", ver, "test_chainlet", "test_12345-1", "asaga", types.ChainletParams{}, nil, false, "",
65100
))
66101
s.Require().NoError(err)
67102

68103
// Create a newer but disabled stack version
69104
ver2 := "1.2.4"
70105
_, err = s.msgServer.UpdateChainletStack(s.ctx, types.NewMsgUpdateChainletStack(
71-
creator.String(), "test", "test/test:"+ver2, ver2, "abcd"+ver2, false,
106+
creator.String(), "test", "test/test:"+ver2, ver2, "abcd"+ver2, true,
72107
))
73108
s.Require().NoError(err)
74109
_, err = s.msgServer.DisableChainletStackVersion(s.ctx, types.NewMsgDisableChainletStackVersion(creator.String(), "test", ver2))
@@ -120,12 +155,12 @@ func (s *TestSuite) TestDisabledVersionAutoUpgrade() {
120155
for j, ver := range tt.addedVersions {
121156
if j == 0 {
122157
_, err = s.msgServer.CreateChainletStack(s.ctx, types.NewMsgCreateChainletStack(
123-
creator.String(), "test", "test", "test/test:"+ver, ver, "abcd"+ver, fees, false,
158+
creator.String(), "test", "test", "test/test:"+ver, ver, "abcd"+ver, fees, true,
124159
))
125160
s.Require().NoError(err)
126161
} else {
127162
_, err = s.msgServer.UpdateChainletStack(s.ctx, types.NewMsgUpdateChainletStack(
128-
creator.String(), "test", "test/test:"+ver, ver, "abcd"+ver, false,
163+
creator.String(), "test", "test/test:"+ver, ver, "abcd"+ver, true,
129164
))
130165
s.Require().NoError(err)
131166
}
@@ -137,11 +172,30 @@ func (s *TestSuite) TestDisabledVersionAutoUpgrade() {
137172
s.billingKeeper.EXPECT().
138173
BillAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
139174
Return(nil)
140-
chainId := "test_12345-42"
175+
s.aclKeeper.EXPECT().
176+
IsAdmin(gomock.Any(), gomock.Any()).
177+
Return(false).
178+
AnyTimes()
179+
s.providerMsgServer.EXPECT().
180+
CreateConsumer(gomock.Any(), gomock.Any()).
181+
Return(&ccvprovidertypes.MsgCreateConsumerResponse{
182+
ConsumerId: "0",
183+
}, nil)
184+
s.providerKeeper.EXPECT().
185+
GetValidatorSetUpdateId(gomock.Any()).
186+
Return(uint64(1))
187+
s.providerKeeper.EXPECT().
188+
AppendPendingVSCPackets(gomock.Any(), gomock.Eq("0"), gomock.Any())
189+
s.providerKeeper.EXPECT().
190+
IncrementValidatorSetUpdateId(gomock.Any())
191+
192+
chainId := "test_12345-1"
141193
_, err = s.msgServer.LaunchChainlet(s.ctx, types.NewMsgLaunchChainlet(
142-
creator.String(), nil, "test", tt.current, "test_chainlet", chainId, "asaga", types.ChainletParams{}, nil, false, "",
194+
creator.String(), []string{creator.String()}, "test", tt.current, "test_chainlet", chainId, "asaga", types.ChainletParams{}, nil, false, "",
143195
))
144196
s.Require().NoError(err)
197+
chainlet, err := s.chainletKeeper.Chainlet(s.ctx, chainId)
198+
s.Require().NoError(err)
145199

146200
// Disable specified stack versions
147201
for _, ver := range tt.disabledVersions {
@@ -157,7 +211,7 @@ func (s *TestSuite) TestDisabledVersionAutoUpgrade() {
157211
// Check it with a chainlet auto-upgrade
158212
err = s.chainletKeeper.AutoUpgradeChainlets(s.ctx)
159213
s.Require().NoError(err)
160-
chainlet, err := s.chainletKeeper.Chainlet(s.ctx, chainId)
214+
chainlet, err = s.chainletKeeper.Chainlet(s.ctx, chainId)
161215
s.Require().NoError(err)
162216
s.Require().Equal(tt.expectedLatest, chainlet.ChainletStackVersion)
163217
})

x/chainlet/keeper/msg_server_launch_chainlet.go

Lines changed: 50 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ func (k msgServer) LaunchChainlet(goCtx context.Context, msg *types.MsgLaunchCha
2222
admin := k.aclKeeper.IsAdmin(ctx, msg.GetSigners()[0])
2323
if !admin {
2424
ok, err := types.ValidateNonAdminChainId(msg.ChainId)
25-
if !ok {
25+
if err != nil {
2626
return &types.MsgLaunchChainletResponse{}, err
2727
}
28+
if !ok {
29+
return &types.MsgLaunchChainletResponse{}, types.ErrInvalidChainId
30+
}
2831
}
2932

3033
// get total number of chainlets
@@ -100,68 +103,53 @@ func (k msgServer) LaunchChainlet(goCtx context.Context, msg *types.MsgLaunchCha
100103
}
101104

102105
chainlet.Tags = msg.Tags
103-
104-
err = k.NewChainlet(ctx, chainlet)
105-
if err != nil {
106-
return &types.MsgLaunchChainletResponse{}, err
107-
}
108-
109-
return &types.MsgLaunchChainletResponse{}, ctx.EventManager().EmitTypedEvent(&types.EventLaunchChainlet{
110-
ChainName: chainlet.ChainletName,
111-
Launcher: chainlet.Launcher,
112-
ChainId: chainlet.ChainId,
113-
Stack: chainlet.ChainletStackName,
114-
StackVersion: chainlet.ChainletStackVersion,
115-
})
116-
}
117-
118-
if len(stack.Fees) == 0 {
119-
return &types.MsgLaunchChainletResponse{}, cosmossdkerrors.Wrapf(types.ErrBillingFailure, "chainlet stack '%s' has no fees configured", stack.DisplayName)
120-
}
121-
122-
billed := false
123-
for _, feeOption := range stack.Fees {
124-
125-
// logic to launch non-service chainlets
126-
epochfee, err := sdk.ParseCoinNormalized(feeOption.EpochFee)
127-
if err != nil {
128-
return &types.MsgLaunchChainletResponse{}, types.ErrInvalidCoin
129-
}
130-
setupfee, err := sdk.ParseCoinNormalized(feeOption.SetupFee)
131-
if err != nil {
132-
return &types.MsgLaunchChainletResponse{}, types.ErrInvalidCoin
133-
}
134-
owner, err := sdk.AccAddressFromBech32(msg.Creator)
135-
if err != nil {
136-
return &types.MsgLaunchChainletResponse{}, err
137-
}
138-
139-
multiplier, ok := math.NewIntFromString(k.GetParams(ctx).NEpochDeposit)
140-
if !ok {
141-
return &types.MsgLaunchChainletResponse{}, fmt.Errorf("bad multiplier")
142-
}
143-
144-
deposit := sdk.Coin{
145-
Amount: epochfee.Amount.Mul(multiplier),
146-
Denom: epochfee.Denom,
147-
}
148-
deposit = deposit.Add(setupfee)
149-
err = k.escrowKeeper.NewChainletAccount(ctx, owner, msg.ChainId, deposit)
150-
if err != nil {
151-
return &types.MsgLaunchChainletResponse{}, err
106+
} else {
107+
if len(stack.Fees) == 0 {
108+
return &types.MsgLaunchChainletResponse{}, cosmossdkerrors.Wrapf(types.ErrBillingFailure, "chainlet stack '%s' has no fees configured", stack.DisplayName)
109+
}
110+
111+
billed := false
112+
for _, feeOption := range stack.Fees {
113+
// logic to launch non-service chainlets
114+
epochfee, err := sdk.ParseCoinNormalized(feeOption.EpochFee)
115+
if err != nil {
116+
return &types.MsgLaunchChainletResponse{}, types.ErrInvalidCoin
117+
}
118+
setupfee, err := sdk.ParseCoinNormalized(feeOption.SetupFee)
119+
if err != nil {
120+
return &types.MsgLaunchChainletResponse{}, types.ErrInvalidCoin
121+
}
122+
owner, err := sdk.AccAddressFromBech32(msg.Creator)
123+
if err != nil {
124+
return &types.MsgLaunchChainletResponse{}, err
125+
}
126+
127+
multiplier, ok := math.NewIntFromString(k.GetParams(ctx).NEpochDeposit)
128+
if !ok {
129+
return &types.MsgLaunchChainletResponse{}, fmt.Errorf("bad multiplier")
130+
}
131+
132+
deposit := sdk.Coin{
133+
Amount: epochfee.Amount.Mul(multiplier),
134+
Denom: epochfee.Denom,
135+
}
136+
deposit = deposit.Add(setupfee)
137+
err = k.escrowKeeper.NewChainletAccount(ctx, owner, msg.ChainId, deposit)
138+
if err != nil {
139+
return &types.MsgLaunchChainletResponse{}, err
140+
}
141+
142+
// Bill for the chainlet just after it is launched
143+
totalFee := epochfee.Add(setupfee)
144+
err = k.billingKeeper.BillAccount(ctx, totalFee, chainlet, "launching chainlet")
145+
if err == nil {
146+
billed = true
147+
break
148+
}
149+
}
150+
if !billed {
151+
return &types.MsgLaunchChainletResponse{}, cosmossdkerrors.Wrapf(types.ErrBillingFailure, "failed to bill new account %s", err.Error())
152152
}
153-
154-
// Bill for the chainlet just after it is launched
155-
totalFee := epochfee.Add(setupfee)
156-
err = k.billingKeeper.BillAccount(ctx, totalFee, chainlet, "launching chainlet")
157-
if err == nil {
158-
billed = true
159-
break
160-
}
161-
}
162-
163-
if !billed {
164-
return &types.MsgLaunchChainletResponse{}, cosmossdkerrors.Wrapf(types.ErrBillingFailure, "failed to bill new account %s", err.Error())
165153
}
166154

167155
// Add as a CCV consumer if enabled

0 commit comments

Comments
 (0)