diff --git a/cosmwasm/README.md b/cosmwasm/README.md index 001425e1e..57783d4cd 100644 --- a/cosmwasm/README.md +++ b/cosmwasm/README.md @@ -205,6 +205,9 @@ extern "C" { /// Executes a query on the chain (import). Not to be confused with the /// query export, which queries the state of the contract. fn query_chain(request: u32) -> u32; + + /// Evaporates a specified amount of gas (1 evaporate = 1 sdk gas) + fn gas_evaporate(evaporate: u32) -> u32; } ``` diff --git a/cosmwasm/enclaves/shared/contract-engine/src/wasm3/mod.rs b/cosmwasm/enclaves/shared/contract-engine/src/wasm3/mod.rs index 5a2d80bb6..521181b09 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/wasm3/mod.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/wasm3/mod.rs @@ -315,6 +315,8 @@ impl Engine { link_fn(instance, "secp256k1_sign", host_secp256k1_sign)?; link_fn(instance, "ed25519_sign", host_ed25519_sign)?; + link_fn(instance, "gas_evaporate", host_gas_evaporate)?; + // DbReadIndex = 0, // DbWriteIndex = 1, // DbRemoveIndex = 2, @@ -1755,3 +1757,21 @@ fn get_encryption_salt(timestamp: u64) -> Vec { encryption_salt } + +fn host_gas_evaporate( + context: &mut Context, + instance: &wasm3::Instance, + evaporate: i32, +) -> WasmEngineResult { + const GAS_MULTIPLIER: u64 = 1000; // (cosmwasm gas : sdk gas) + + let evaporate_cosmwasm = match evaporate { + 0 => 1_u64, + x => (x as u32) as u64 * GAS_MULTIPLIER, + }; + use_gas(instance, evaporate_cosmwasm)?; + + // return 0 == success + // https://github.com/CosmWasm/cosmwasm/blob/v1.0.0-beta5/packages/vm/src/imports.rs#L281 + Ok(0) +} diff --git a/cosmwasm/packages/sgx-vm/src/compatability.rs b/cosmwasm/packages/sgx-vm/src/compatability.rs index 425b8937a..934f2c9e9 100644 --- a/cosmwasm/packages/sgx-vm/src/compatability.rs +++ b/cosmwasm/packages/sgx-vm/src/compatability.rs @@ -27,6 +27,7 @@ const SUPPORTED_IMPORTS_V010: &[&str] = &[ "env.db_next", #[cfg(feature = "debug-print")] "env.debug_print", + "env.gas_evaporate", ]; /// Lists all v1 imports we provide upon instantiating the instance in Instance::from_module() @@ -50,6 +51,7 @@ const SUPPORTED_IMPORTS_V1: &[&str] = &[ "env.db_scan", #[cfg(feature = "iterator")] "env.db_next", + "env.gas_evaporate", ]; /// Lists all entry points we expect to be present when calling a v0.10 contract. diff --git a/x/compute/internal/keeper/secret_contracts_exec_test.go b/x/compute/internal/keeper/secret_contracts_exec_test.go index 06213ea60..af184795a 100644 --- a/x/compute/internal/keeper/secret_contracts_exec_test.go +++ b/x/compute/internal/keeper/secret_contracts_exec_test.go @@ -2129,3 +2129,46 @@ func TestV1ReplyChainWithError(t *testing.T) { require.Equal(t, expectedFlow, string(data)) } + +func TestEvaporateGas(t *testing.T) { + ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, TestContractPaths[evaporateContract], sdk.NewCoins()) + + _, _, contractAddress, _, initErr := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, true, defaultGasForTests) + require.Empty(t, initErr) + + t.Run("evaporate 1", func(t *testing.T) { + _, ctx, _, _, _, err := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"evaporate_test":{"evaporate":1}}`, true, true, defaultGasForTests, 0) + require.Empty(t, err) + // todo check gas consumed + }) + + t.Run("evaporate 100", func(t *testing.T) { + _, ctx, _, _, _, err := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"evaporate_test":{"evaporate":100}}`, true, true, defaultGasForTests, 0) + require.Empty(t, err) + // todo check gas consumed + }) + + t.Run("evaporate 1000", func(t *testing.T) { + _, ctx, _, _, _, err := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"evaporate_test":{"evaporate":1000}}`, true, true, defaultGasForTests, 0) + require.Empty(t, err) + // todo check gas consumed + }) + + t.Run("evaporate 10000", func(t *testing.T) { + _, ctx, _, _, _, err := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"evaporate_test":{"evaporate":10000}}`, true, true, defaultGasForTests, 0) + require.Empty(t, err) + // todo check gas consumed + }) + + t.Run("evaporate 100000", func(t *testing.T) { + _, _, _, _, _, err := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"evaporate_test":{"evaporate":100000}}`, true, true, defaultGasForTests, 0) + require.Empty(t, err) + // todo check gas consumed + }) + + t.Run("evaporate 0", func(t *testing.T) { + _, ctx, _, _, _, err := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"evaporate_test":{"evaporate":0}}`, true, true, defaultGasForTests, 0) + require.Empty(t, err) + // todo check gas consumed + }) +} diff --git a/x/compute/internal/keeper/test_common.go b/x/compute/internal/keeper/test_common.go index f6e566655..a8e0b2f6a 100644 --- a/x/compute/internal/keeper/test_common.go +++ b/x/compute/internal/keeper/test_common.go @@ -107,6 +107,7 @@ const ( v010WithFloats = "contract_with_floats.wasm" tooHighMemoryContract = "too-high-initial-memory.wasm" staticTooHighMemoryContract = "static-too-high-initial-memory.wasm" + evaporateContract = "evaporate.wasm" ) const benchContract = "bench_contract.wasm" @@ -123,6 +124,7 @@ var TestContractPaths = map[string]string{ tooHighMemoryContract: filepath.Join(".", contractPath, tooHighMemoryContract), staticTooHighMemoryContract: filepath.Join(".", contractPath, staticTooHighMemoryContract), benchContract: filepath.Join(".", contractPath, benchContract), + evaporateContract: filepath.Join(".", contractPath, evaporateContract), } var ( diff --git a/x/compute/internal/keeper/testdata/evaporate.wasm b/x/compute/internal/keeper/testdata/evaporate.wasm new file mode 100644 index 000000000..8c1348f73 Binary files /dev/null and b/x/compute/internal/keeper/testdata/evaporate.wasm differ