Skip to content

Commit

Permalink
Merge pull request #227 from CosmWasm/charge-deserialization-gas
Browse files Browse the repository at this point in the history
Point to places where JSON deserialisation gas should be charged
  • Loading branch information
webmaster128 authored Jun 24, 2021
2 parents 0dfdeaf + 8232a41 commit 70f34e3
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 17 deletions.
24 changes: 13 additions & 11 deletions ibc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func TestIBCHandshake(t *testing.T) {
vm := withVM(t)
checksum := createTestContract(t, vm, IBC_TEST_CONTRACT)
gasMeter1 := api.NewMockGasMeter(TESTING_GAS_LIMIT)
deserCost := types.UFraction{1, 1}
// instantiate it with this store
store := api.NewLookup(gasMeter1)
goapi := api.NewMockAPI()
Expand All @@ -100,7 +101,7 @@ func TestIBCHandshake(t *testing.T) {
msg := IBCInstantiateMsg{
ReflectCodeID: REFLECT_ID,
}
ires, _, err := vm.Instantiate(checksum, env, info, toBytes(t, msg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT)
ires, _, err := vm.Instantiate(checksum, env, info, toBytes(t, msg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)
require.Equal(t, 0, len(ires.Messages))

Expand All @@ -110,12 +111,12 @@ func TestIBCHandshake(t *testing.T) {
env = api.MockEnv()
// fails on bad version
channel := api.MockIBCChannel(CHANNEL_ID, types.Ordered, "random-garbage")
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT)
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT, deserCost)
require.Error(t, err)
// passes on good version
channel = api.MockIBCChannel(CHANNEL_ID, types.Ordered, IBC_VERSION)
channel.CounterpartyVersion = ""
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT)
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)

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

Expand All @@ -149,6 +150,7 @@ func TestIBCPacketDispatch(t *testing.T) {
vm := withVM(t)
checksum := createTestContract(t, vm, IBC_TEST_CONTRACT)
gasMeter1 := api.NewMockGasMeter(TESTING_GAS_LIMIT)
deserCost := types.UFraction{1, 1}
// instantiate it with this store
store := api.NewLookup(gasMeter1)
goapi := api.NewMockAPI()
Expand All @@ -161,23 +163,23 @@ func TestIBCPacketDispatch(t *testing.T) {
initMsg := IBCInstantiateMsg{
ReflectCodeID: REFLECT_ID,
}
_, _, err := vm.Instantiate(checksum, env, info, toBytes(t, initMsg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT)
_, _, err := vm.Instantiate(checksum, env, info, toBytes(t, initMsg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)

// channel open
gasMeter2 := api.NewMockGasMeter(TESTING_GAS_LIMIT)
store.SetGasMeter(gasMeter2)
channel := api.MockIBCChannel(CHANNEL_ID, types.Ordered, IBC_VERSION)
channel.CounterpartyVersion = ""
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT)
_, err = vm.IBCChannelOpen(checksum, env, channel, store, *goapi, querier, gasMeter2, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)

// channel connect
gasMeter3 := api.NewMockGasMeter(TESTING_GAS_LIMIT)
store.SetGasMeter(gasMeter3)
// completes and dispatches message to create reflect contract
channel = api.MockIBCChannel(CHANNEL_ID, types.Ordered, IBC_VERSION)
res, _, err := vm.IBCChannelConnect(checksum, env, channel, store, *goapi, querier, gasMeter3, TESTING_GAS_LIMIT)
res, _, err := vm.IBCChannelConnect(checksum, env, channel, store, *goapi, querier, gasMeter3, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)
require.Equal(t, 1, len(res.Messages))
id := res.Messages[0].ID
Expand Down Expand Up @@ -206,14 +208,14 @@ func TestIBCPacketDispatch(t *testing.T) {
},
},
}
_, _, err = vm.Reply(checksum, env, reply, store, *goapi, querier, gasMeter4, TESTING_GAS_LIMIT)
_, _, err = vm.Reply(checksum, env, reply, store, *goapi, querier, gasMeter4, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)

// ensure the channel is registered
queryMsg := IBCQueryMsg{
ListAccounts: &struct{}{},
}
qres, _, err := vm.Query(checksum, env, toBytes(t, queryMsg), store, *goapi, querier, gasMeter4, TESTING_GAS_LIMIT)
qres, _, err := vm.Query(checksum, env, toBytes(t, queryMsg), store, *goapi, querier, gasMeter4, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)
var accounts ListAccountsResponse
err = json.Unmarshal(qres, &accounts)
Expand All @@ -235,7 +237,7 @@ func TestIBCPacketDispatch(t *testing.T) {
},
}
packet := api.MockIBCPacket(CHANNEL_ID, toBytes(t, ibcMsg))
pres, _, err := vm.IBCPacketReceive(checksum, env, packet, store, *goapi, querier, gasMeter5, TESTING_GAS_LIMIT)
pres, _, err := vm.IBCPacketReceive(checksum, env, packet, store, *goapi, querier, gasMeter5, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)

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

// error on message from another channel
packet2 := api.MockIBCPacket("no-such-channel", toBytes(t, ibcMsg))
pres2, _, err := vm.IBCPacketReceive(checksum, env, packet2, store, *goapi, querier, gasMeter5, TESTING_GAS_LIMIT)
pres2, _, err := vm.IBCPacketReceive(checksum, env, packet2, store, *goapi, querier, gasMeter5, TESTING_GAS_LIMIT, deserCost)
require.NoError(t, err)
// assert app-level failure
var ack2 AcknowledgeDispatch
Expand Down
85 changes: 85 additions & 0 deletions lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type VM struct {
// `memoryLimit` is the memory limit of each contract execution (in MiB)
// `printDebug` is a flag to enable/disable printing debug logs from the contract to STDOUT. This should be false in production environments.
// `cacheSize` sets the size in MiB of an in-memory cache for e.g. module caching. Set to 0 to disable.
// `deserCost` sets the gas cost of deserializing one byte of data.
func NewVM(dataDir string, supportedFeatures string, memoryLimit uint32, printDebug bool, cacheSize uint32) (*VM, error) {
cache, err := api.InitCache(dataDir, supportedFeatures, cacheSize, memoryLimit)
if err != nil {
Expand Down Expand Up @@ -124,6 +125,7 @@ func (vm *VM) Instantiate(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.Response, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -138,6 +140,12 @@ func (vm *VM) Instantiate(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var result types.ContractResult
err = json.Unmarshal(data, &result)
if err != nil {
Expand Down Expand Up @@ -165,6 +173,7 @@ func (vm *VM) Execute(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.Response, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -179,6 +188,12 @@ func (vm *VM) Execute(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}

gasUsed += gasForDeserialization
var result types.ContractResult
err = json.Unmarshal(data, &result)
if err != nil {
Expand All @@ -202,6 +217,7 @@ func (vm *VM) Query(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) ([]byte, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -212,6 +228,12 @@ func (vm *VM) Query(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.QueryResponse
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -238,6 +260,7 @@ func (vm *VM) Migrate(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.Response, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -248,6 +271,12 @@ func (vm *VM) Migrate(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.ContractResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -274,6 +303,7 @@ func (vm *VM) Sudo(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.Response, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -284,6 +314,12 @@ func (vm *VM) Sudo(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.ContractResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -308,6 +344,7 @@ func (vm *VM) Reply(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.Response, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -322,6 +359,12 @@ func (vm *VM) Reply(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.ContractResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -344,6 +387,7 @@ func (vm *VM) IBCChannelOpen(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -358,6 +402,12 @@ func (vm *VM) IBCChannelOpen(
return gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.IBCChannelOpenResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -380,6 +430,7 @@ func (vm *VM) IBCChannelConnect(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.IBCBasicResponse, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -394,6 +445,12 @@ func (vm *VM) IBCChannelConnect(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.IBCBasicResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -416,6 +473,7 @@ func (vm *VM) IBCChannelClose(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.IBCBasicResponse, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -430,6 +488,12 @@ func (vm *VM) IBCChannelClose(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.IBCBasicResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -452,6 +516,7 @@ func (vm *VM) IBCPacketReceive(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.IBCReceiveResponse, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -466,6 +531,12 @@ func (vm *VM) IBCPacketReceive(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.IBCReceiveResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -489,6 +560,7 @@ func (vm *VM) IBCPacketAck(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.IBCBasicResponse, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -503,6 +575,12 @@ func (vm *VM) IBCPacketAck(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.IBCBasicResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand All @@ -526,6 +604,7 @@ func (vm *VM) IBCPacketTimeout(
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.IBCBasicResponse, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
Expand All @@ -540,6 +619,12 @@ func (vm *VM) IBCPacketTimeout(
return nil, gasUsed, err
}

gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization

var resp types.IBCBasicResult
err = json.Unmarshal(data, &resp)
if err != nil {
Expand Down
Loading

0 comments on commit 70f34e3

Please sign in to comment.