Skip to content

Commit 70f34e3

Browse files
authored
Merge pull request #227 from CosmWasm/charge-deserialization-gas
Point to places where JSON deserialisation gas should be charged
2 parents 0dfdeaf + 8232a41 commit 70f34e3

File tree

4 files changed

+134
-17
lines changed

4 files changed

+134
-17
lines changed

ibc_test.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func TestIBCHandshake(t *testing.T) {
8888
vm := withVM(t)
8989
checksum := createTestContract(t, vm, IBC_TEST_CONTRACT)
9090
gasMeter1 := api.NewMockGasMeter(TESTING_GAS_LIMIT)
91+
deserCost := types.UFraction{1, 1}
9192
// instantiate it with this store
9293
store := api.NewLookup(gasMeter1)
9394
goapi := api.NewMockAPI()
@@ -100,7 +101,7 @@ func TestIBCHandshake(t *testing.T) {
100101
msg := IBCInstantiateMsg{
101102
ReflectCodeID: REFLECT_ID,
102103
}
103-
ires, _, err := vm.Instantiate(checksum, env, info, toBytes(t, msg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT)
104+
ires, _, err := vm.Instantiate(checksum, env, info, toBytes(t, msg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT, deserCost)
104105
require.NoError(t, err)
105106
require.Equal(t, 0, len(ires.Messages))
106107

@@ -110,12 +111,12 @@ func TestIBCHandshake(t *testing.T) {
110111
env = api.MockEnv()
111112
// fails on bad version
112113
channel := api.MockIBCChannel(CHANNEL_ID, types.Ordered, "random-garbage")
113-
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT)
114+
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT, deserCost)
114115
require.Error(t, err)
115116
// passes on good version
116117
channel = api.MockIBCChannel(CHANNEL_ID, types.Ordered, IBC_VERSION)
117118
channel.CounterpartyVersion = ""
118-
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT)
119+
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT, deserCost)
119120
require.NoError(t, err)
120121

121122
// channel connect
@@ -124,7 +125,7 @@ func TestIBCHandshake(t *testing.T) {
124125
env = api.MockEnv()
125126
// completes and dispatches message to create reflect contract
126127
channel = api.MockIBCChannel(CHANNEL_ID, types.Ordered, IBC_VERSION)
127-
res, _, err := vm.IBCChannelConnect(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT)
128+
res, _, err := vm.IBCChannelConnect(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT, deserCost)
128129
require.NoError(t, err)
129130
require.Equal(t, 1, len(res.Messages))
130131

@@ -149,6 +150,7 @@ func TestIBCPacketDispatch(t *testing.T) {
149150
vm := withVM(t)
150151
checksum := createTestContract(t, vm, IBC_TEST_CONTRACT)
151152
gasMeter1 := api.NewMockGasMeter(TESTING_GAS_LIMIT)
153+
deserCost := types.UFraction{1, 1}
152154
// instantiate it with this store
153155
store := api.NewLookup(gasMeter1)
154156
goapi := api.NewMockAPI()
@@ -161,23 +163,23 @@ func TestIBCPacketDispatch(t *testing.T) {
161163
initMsg := IBCInstantiateMsg{
162164
ReflectCodeID: REFLECT_ID,
163165
}
164-
_, _, err := vm.Instantiate(checksum, env, info, toBytes(t, initMsg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT)
166+
_, _, err := vm.Instantiate(checksum, env, info, toBytes(t, initMsg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT, deserCost)
165167
require.NoError(t, err)
166168

167169
// channel open
168170
gasMeter2 := api.NewMockGasMeter(TESTING_GAS_LIMIT)
169171
store.SetGasMeter(gasMeter2)
170172
channel := api.MockIBCChannel(CHANNEL_ID, types.Ordered, IBC_VERSION)
171173
channel.CounterpartyVersion = ""
172-
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT)
174+
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT, deserCost)
173175
require.NoError(t, err)
174176

175177
// channel connect
176178
gasMeter3 := api.NewMockGasMeter(TESTING_GAS_LIMIT)
177179
store.SetGasMeter(gasMeter3)
178180
// completes and dispatches message to create reflect contract
179181
channel = api.MockIBCChannel(CHANNEL_ID, types.Ordered, IBC_VERSION)
180-
res, _, err := vm.IBCChannelConnect(checksum, env, channel, store, *goapi, querier, gasMeter3, TESTING_GAS_LIMIT)
182+
res, _, err := vm.IBCChannelConnect(checksum, env, channel, store, *goapi, querier, gasMeter3, TESTING_GAS_LIMIT, deserCost)
181183
require.NoError(t, err)
182184
require.Equal(t, 1, len(res.Messages))
183185
id := res.Messages[0].ID
@@ -206,14 +208,14 @@ func TestIBCPacketDispatch(t *testing.T) {
206208
},
207209
},
208210
}
209-
_, _, err = vm.Reply(checksum, env, reply, store, *goapi, querier, gasMeter4, TESTING_GAS_LIMIT)
211+
_, _, err = vm.Reply(checksum, env, reply, store, *goapi, querier, gasMeter4, TESTING_GAS_LIMIT, deserCost)
210212
require.NoError(t, err)
211213

212214
// ensure the channel is registered
213215
queryMsg := IBCQueryMsg{
214216
ListAccounts: &struct{}{},
215217
}
216-
qres, _, err := vm.Query(checksum, env, toBytes(t, queryMsg), store, *goapi, querier, gasMeter4, TESTING_GAS_LIMIT)
218+
qres, _, err := vm.Query(checksum, env, toBytes(t, queryMsg), store, *goapi, querier, gasMeter4, TESTING_GAS_LIMIT, deserCost)
217219
require.NoError(t, err)
218220
var accounts ListAccountsResponse
219221
err = json.Unmarshal(qres, &accounts)
@@ -235,7 +237,7 @@ func TestIBCPacketDispatch(t *testing.T) {
235237
},
236238
}
237239
packet := api.MockIBCPacket(CHANNEL_ID, toBytes(t, ibcMsg))
238-
pres, _, err := vm.IBCPacketReceive(checksum, env, packet, store, *goapi, querier, gasMeter5, TESTING_GAS_LIMIT)
240+
pres, _, err := vm.IBCPacketReceive(checksum, env, packet, store, *goapi, querier, gasMeter5, TESTING_GAS_LIMIT, deserCost)
239241
require.NoError(t, err)
240242

241243
// assert app-level success
@@ -245,7 +247,7 @@ func TestIBCPacketDispatch(t *testing.T) {
245247

246248
// error on message from another channel
247249
packet2 := api.MockIBCPacket("no-such-channel", toBytes(t, ibcMsg))
248-
pres2, _, err := vm.IBCPacketReceive(checksum, env, packet2, store, *goapi, querier, gasMeter5, TESTING_GAS_LIMIT)
250+
pres2, _, err := vm.IBCPacketReceive(checksum, env, packet2, store, *goapi, querier, gasMeter5, TESTING_GAS_LIMIT, deserCost)
249251
require.NoError(t, err)
250252
// assert app-level failure
251253
var ack2 AcknowledgeDispatch

lib.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type VM struct {
4141
// `memoryLimit` is the memory limit of each contract execution (in MiB)
4242
// `printDebug` is a flag to enable/disable printing debug logs from the contract to STDOUT. This should be false in production environments.
4343
// `cacheSize` sets the size in MiB of an in-memory cache for e.g. module caching. Set to 0 to disable.
44+
// `deserCost` sets the gas cost of deserializing one byte of data.
4445
func NewVM(dataDir string, supportedFeatures string, memoryLimit uint32, printDebug bool, cacheSize uint32) (*VM, error) {
4546
cache, err := api.InitCache(dataDir, supportedFeatures, cacheSize, memoryLimit)
4647
if err != nil {
@@ -124,6 +125,7 @@ func (vm *VM) Instantiate(
124125
querier Querier,
125126
gasMeter GasMeter,
126127
gasLimit uint64,
128+
deserCost types.UFraction,
127129
) (*types.Response, uint64, error) {
128130
envBin, err := json.Marshal(env)
129131
if err != nil {
@@ -138,6 +140,12 @@ func (vm *VM) Instantiate(
138140
return nil, gasUsed, err
139141
}
140142

143+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
144+
if gasLimit < gasForDeserialization+gasUsed {
145+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
146+
}
147+
gasUsed += gasForDeserialization
148+
141149
var result types.ContractResult
142150
err = json.Unmarshal(data, &result)
143151
if err != nil {
@@ -165,6 +173,7 @@ func (vm *VM) Execute(
165173
querier Querier,
166174
gasMeter GasMeter,
167175
gasLimit uint64,
176+
deserCost types.UFraction,
168177
) (*types.Response, uint64, error) {
169178
envBin, err := json.Marshal(env)
170179
if err != nil {
@@ -179,6 +188,12 @@ func (vm *VM) Execute(
179188
return nil, gasUsed, err
180189
}
181190

191+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
192+
if gasLimit < gasForDeserialization+gasUsed {
193+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
194+
}
195+
196+
gasUsed += gasForDeserialization
182197
var result types.ContractResult
183198
err = json.Unmarshal(data, &result)
184199
if err != nil {
@@ -202,6 +217,7 @@ func (vm *VM) Query(
202217
querier Querier,
203218
gasMeter GasMeter,
204219
gasLimit uint64,
220+
deserCost types.UFraction,
205221
) ([]byte, uint64, error) {
206222
envBin, err := json.Marshal(env)
207223
if err != nil {
@@ -212,6 +228,12 @@ func (vm *VM) Query(
212228
return nil, gasUsed, err
213229
}
214230

231+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
232+
if gasLimit < gasForDeserialization+gasUsed {
233+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
234+
}
235+
gasUsed += gasForDeserialization
236+
215237
var resp types.QueryResponse
216238
err = json.Unmarshal(data, &resp)
217239
if err != nil {
@@ -238,6 +260,7 @@ func (vm *VM) Migrate(
238260
querier Querier,
239261
gasMeter GasMeter,
240262
gasLimit uint64,
263+
deserCost types.UFraction,
241264
) (*types.Response, uint64, error) {
242265
envBin, err := json.Marshal(env)
243266
if err != nil {
@@ -248,6 +271,12 @@ func (vm *VM) Migrate(
248271
return nil, gasUsed, err
249272
}
250273

274+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
275+
if gasLimit < gasForDeserialization+gasUsed {
276+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
277+
}
278+
gasUsed += gasForDeserialization
279+
251280
var resp types.ContractResult
252281
err = json.Unmarshal(data, &resp)
253282
if err != nil {
@@ -274,6 +303,7 @@ func (vm *VM) Sudo(
274303
querier Querier,
275304
gasMeter GasMeter,
276305
gasLimit uint64,
306+
deserCost types.UFraction,
277307
) (*types.Response, uint64, error) {
278308
envBin, err := json.Marshal(env)
279309
if err != nil {
@@ -284,6 +314,12 @@ func (vm *VM) Sudo(
284314
return nil, gasUsed, err
285315
}
286316

317+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
318+
if gasLimit < gasForDeserialization+gasUsed {
319+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
320+
}
321+
gasUsed += gasForDeserialization
322+
287323
var resp types.ContractResult
288324
err = json.Unmarshal(data, &resp)
289325
if err != nil {
@@ -308,6 +344,7 @@ func (vm *VM) Reply(
308344
querier Querier,
309345
gasMeter GasMeter,
310346
gasLimit uint64,
347+
deserCost types.UFraction,
311348
) (*types.Response, uint64, error) {
312349
envBin, err := json.Marshal(env)
313350
if err != nil {
@@ -322,6 +359,12 @@ func (vm *VM) Reply(
322359
return nil, gasUsed, err
323360
}
324361

362+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
363+
if gasLimit < gasForDeserialization+gasUsed {
364+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
365+
}
366+
gasUsed += gasForDeserialization
367+
325368
var resp types.ContractResult
326369
err = json.Unmarshal(data, &resp)
327370
if err != nil {
@@ -344,6 +387,7 @@ func (vm *VM) IBCChannelOpen(
344387
querier Querier,
345388
gasMeter GasMeter,
346389
gasLimit uint64,
390+
deserCost types.UFraction,
347391
) (uint64, error) {
348392
envBin, err := json.Marshal(env)
349393
if err != nil {
@@ -358,6 +402,12 @@ func (vm *VM) IBCChannelOpen(
358402
return gasUsed, err
359403
}
360404

405+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
406+
if gasLimit < gasForDeserialization+gasUsed {
407+
return gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
408+
}
409+
gasUsed += gasForDeserialization
410+
361411
var resp types.IBCChannelOpenResult
362412
err = json.Unmarshal(data, &resp)
363413
if err != nil {
@@ -380,6 +430,7 @@ func (vm *VM) IBCChannelConnect(
380430
querier Querier,
381431
gasMeter GasMeter,
382432
gasLimit uint64,
433+
deserCost types.UFraction,
383434
) (*types.IBCBasicResponse, uint64, error) {
384435
envBin, err := json.Marshal(env)
385436
if err != nil {
@@ -394,6 +445,12 @@ func (vm *VM) IBCChannelConnect(
394445
return nil, gasUsed, err
395446
}
396447

448+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
449+
if gasLimit < gasForDeserialization+gasUsed {
450+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
451+
}
452+
gasUsed += gasForDeserialization
453+
397454
var resp types.IBCBasicResult
398455
err = json.Unmarshal(data, &resp)
399456
if err != nil {
@@ -416,6 +473,7 @@ func (vm *VM) IBCChannelClose(
416473
querier Querier,
417474
gasMeter GasMeter,
418475
gasLimit uint64,
476+
deserCost types.UFraction,
419477
) (*types.IBCBasicResponse, uint64, error) {
420478
envBin, err := json.Marshal(env)
421479
if err != nil {
@@ -430,6 +488,12 @@ func (vm *VM) IBCChannelClose(
430488
return nil, gasUsed, err
431489
}
432490

491+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
492+
if gasLimit < gasForDeserialization+gasUsed {
493+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
494+
}
495+
gasUsed += gasForDeserialization
496+
433497
var resp types.IBCBasicResult
434498
err = json.Unmarshal(data, &resp)
435499
if err != nil {
@@ -452,6 +516,7 @@ func (vm *VM) IBCPacketReceive(
452516
querier Querier,
453517
gasMeter GasMeter,
454518
gasLimit uint64,
519+
deserCost types.UFraction,
455520
) (*types.IBCReceiveResponse, uint64, error) {
456521
envBin, err := json.Marshal(env)
457522
if err != nil {
@@ -466,6 +531,12 @@ func (vm *VM) IBCPacketReceive(
466531
return nil, gasUsed, err
467532
}
468533

534+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
535+
if gasLimit < gasForDeserialization+gasUsed {
536+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
537+
}
538+
gasUsed += gasForDeserialization
539+
469540
var resp types.IBCReceiveResult
470541
err = json.Unmarshal(data, &resp)
471542
if err != nil {
@@ -489,6 +560,7 @@ func (vm *VM) IBCPacketAck(
489560
querier Querier,
490561
gasMeter GasMeter,
491562
gasLimit uint64,
563+
deserCost types.UFraction,
492564
) (*types.IBCBasicResponse, uint64, error) {
493565
envBin, err := json.Marshal(env)
494566
if err != nil {
@@ -503,6 +575,12 @@ func (vm *VM) IBCPacketAck(
503575
return nil, gasUsed, err
504576
}
505577

578+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
579+
if gasLimit < gasForDeserialization+gasUsed {
580+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
581+
}
582+
gasUsed += gasForDeserialization
583+
506584
var resp types.IBCBasicResult
507585
err = json.Unmarshal(data, &resp)
508586
if err != nil {
@@ -526,6 +604,7 @@ func (vm *VM) IBCPacketTimeout(
526604
querier Querier,
527605
gasMeter GasMeter,
528606
gasLimit uint64,
607+
deserCost types.UFraction,
529608
) (*types.IBCBasicResponse, uint64, error) {
530609
envBin, err := json.Marshal(env)
531610
if err != nil {
@@ -540,6 +619,12 @@ func (vm *VM) IBCPacketTimeout(
540619
return nil, gasUsed, err
541620
}
542621

622+
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
623+
if gasLimit < gasForDeserialization+gasUsed {
624+
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
625+
}
626+
gasUsed += gasForDeserialization
627+
543628
var resp types.IBCBasicResult
544629
err = json.Unmarshal(data, &resp)
545630
if err != nil {

0 commit comments

Comments
 (0)