Skip to content

Commit

Permalink
Make gas limit immutable in cosmwasm_vm::instance::Instance
Browse files Browse the repository at this point in the history
  • Loading branch information
webmaster128 committed Jan 28, 2020
1 parent 36bff00 commit ada2132
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 42 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
- Remove export `check_api_compatibility`. The VM will take care of calling it.
- Let `check_api_compatibility` check imports by fully qualified identifier
`<module>.<name>`.
- Make gas limit immutable in `cosmwasm_vm::instance::Instance`. It is passed
once at construction time and cannot publicly be manipulated anymore.

## 0.6

Expand Down
23 changes: 14 additions & 9 deletions lib/vm/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ where
}

/// get instance returns a wasmer Instance tied to a previously saved wasm
pub fn get_instance(&mut self, id: &[u8], deps: Extern<S, A>) -> Result<Instance<S, A>, Error> {
pub fn get_instance(
&mut self,
id: &[u8],
deps: Extern<S, A>,
gas_limit: u64,
) -> Result<Instance<S, A>, Error> {
let hash = WasmHash::generate(&id);

// pop from lru cache if present
Expand All @@ -88,12 +93,12 @@ where
// try from the module cache
let res = self.modules.load_with_backend(hash, backend());
if let Ok(module) = res {
return Instance::from_module(&module, deps);
return Instance::from_module(&module, deps, gas_limit);
}

// fall back to wasm cache (and re-compiling) - this is for backends that don't support serialization
let wasm = self.load_wasm(id)?;
Instance::from_code(&wasm, deps)
Instance::from_code(&wasm, deps, gas_limit)
}

pub fn store_instance(&mut self, id: &[u8], instance: Instance<S, A>) -> Option<Extern<S, A>> {
Expand Down Expand Up @@ -154,7 +159,7 @@ mod test {
let mut cache = unsafe { CosmCache::new(tmp_dir.path(), 10).unwrap() };
let id = cache.save_wasm(CONTRACT_0_7).unwrap();
let deps = dependencies(20);
let mut instance = cache.get_instance(&id, deps).unwrap();
let mut instance = cache.get_instance(&id, deps, 200_000).unwrap();

// run contract
let params = mock_params(&instance.api, "creator", &coin("1000", "earth"), &[]);
Expand All @@ -172,7 +177,7 @@ mod test {
let mut cache = unsafe { CosmCache::new(tmp_dir.path(), 10).unwrap() };
let id = cache.save_wasm(CONTRACT_0_7).unwrap();
let deps = dependencies(20);
let mut instance = cache.get_instance(&id, deps).unwrap();
let mut instance = cache.get_instance(&id, deps, 200_000).unwrap();

// init contract
let params = mock_params(&instance.api, "creator", &coin("1000", "earth"), &[]);
Expand Down Expand Up @@ -205,7 +210,7 @@ mod test {
let deps2 = dependencies(20);

// init instance 1
let mut instance = cache.get_instance(&id, deps1).unwrap();
let mut instance = cache.get_instance(&id, deps1, 400_000).unwrap();
let params = mock_params(&instance.api, "owner1", &coin("1000", "earth"), &[]);
let msg = r#"{"verifier": "sue", "beneficiary": "mary"}"#.as_bytes();
let res = call_init(&mut instance, &params, msg).unwrap();
Expand All @@ -214,7 +219,7 @@ mod test {
let deps1 = cache.store_instance(&id, instance).unwrap();

// init instance 2
let mut instance = cache.get_instance(&id, deps2).unwrap();
let mut instance = cache.get_instance(&id, deps2, 400_000).unwrap();
let params = mock_params(&instance.api, "owner2", &coin("500", "earth"), &[]);
let msg = r#"{"verifier": "bob", "beneficiary": "john"}"#.as_bytes();
let res = call_init(&mut instance, &params, msg).unwrap();
Expand All @@ -223,7 +228,7 @@ mod test {
let deps2 = cache.store_instance(&id, instance).unwrap();

// run contract 2 - just sanity check - results validate in contract unit tests
let mut instance = cache.get_instance(&id, deps2).unwrap();
let mut instance = cache.get_instance(&id, deps2, 400_000).unwrap();
let params = mock_params(
&instance.api,
"bob",
Expand All @@ -237,7 +242,7 @@ mod test {
let _ = cache.store_instance(&id, instance).unwrap();

// run contract 1 - just sanity check - results validate in contract unit tests
let mut instance = cache.get_instance(&id, deps1).unwrap();
let mut instance = cache.get_instance(&id, deps1, 400_000).unwrap();
let params = mock_params(
&instance.api,
"sue",
Expand Down
50 changes: 18 additions & 32 deletions lib/vm/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ where
S: Storage + 'static,
A: Api + 'static,
{
pub fn from_code(code: &[u8], deps: Extern<S, A>) -> Result<Self> {
pub fn from_code(code: &[u8], deps: Extern<S, A>, gas_limit: u64) -> Result<Self> {
let module = compile(code)?;
Instance::from_module(&module, deps)
Instance::from_module(&module, deps, gas_limit)
}

pub fn from_module(module: &Module, deps: Extern<S, A>) -> Result<Self> {
pub fn from_module(module: &Module, deps: Extern<S, A>, gas_limit: u64) -> Result<Self> {
// copy this so it can be moved into the closures, without pulling in deps
let api = deps.api;
let import_obj = imports! {
Expand Down Expand Up @@ -70,7 +70,8 @@ where
}),
},
};
let instance = module.instantiate(&import_obj).context(WasmerErr {})?;
let mut instance = module.instantiate(&import_obj).context(WasmerErr {})?;
set_gas(&mut instance, gas_limit);
let res = Instance {
instance,
api,
Expand All @@ -84,10 +85,6 @@ where
get_gas(&self.instance)
}

pub fn set_gas(&mut self, gas: u64) {
set_gas(&mut self.instance, gas)
}

pub fn with_storage<F: FnMut(&mut S)>(&self, func: F) {
with_storage_from_context(self.instance.context(), func)
}
Expand Down Expand Up @@ -134,32 +131,26 @@ where
#[cfg(test)]
mod test {
use crate::calls::{call_handle, call_init, call_query};
use crate::testing::mock_instance;
use crate::testing::{mock_instance, mock_instance_with_gas_limit};
use cosmwasm::mock::mock_params;
use cosmwasm::types::coin;

static CONTRACT_0_7: &[u8] = include_bytes!("../testdata/contract_0.7.wasm");

#[test]
#[cfg(feature = "default-cranelift")]
fn get_and_set_gas_cranelift_noop() {
let mut instance = mock_instance(&CONTRACT_0_7);
fn set_get_and_gas_cranelift_noop() {
let instance = mock_instance_with_gas_limit(&CONTRACT_0_7, 123321);
let orig_gas = instance.get_gas();
assert!(orig_gas > 1000);
// this is a no-op
instance.set_gas(123456);
assert_eq!(orig_gas, instance.get_gas());
assert_eq!(orig_gas, 1_000_000);
}

#[test]
#[cfg(feature = "default-singlepass")]
fn get_and_set_gas_singlepass_works() {
let mut instance = mock_instance(&CONTRACT_0_7);
fn set_get_and_gas_singlepass_works() {
let instance = mock_instance_with_gas_limit(&CONTRACT_0_7, 123321);
let orig_gas = instance.get_gas();
assert!(orig_gas > 1000000);
// it is updated to whatever we set it with
instance.set_gas(123456);
assert_eq!(123456, instance.get_gas());
assert_eq!(orig_gas, 123321);
}

#[test]
Expand All @@ -174,8 +165,7 @@ mod test {
#[cfg(feature = "default-singlepass")]
fn contract_deducts_gas() {
let mut instance = mock_instance(&CONTRACT_0_7);
let orig_gas = 200_000;
instance.set_gas(orig_gas);
let orig_gas = instance.get_gas();

// init contract
let params = mock_params(&instance.api, "creator", &coin("1000", "earth"), &[]);
Expand All @@ -189,7 +179,7 @@ mod test {
assert_eq!(init_used, 70533);

// run contract - just sanity check - results validate in contract unit tests
instance.set_gas(orig_gas);
let gas_before_handle = instance.get_gas();
let params = mock_params(
&instance.api,
"verifies",
Expand All @@ -201,17 +191,15 @@ mod test {
let msgs = res.unwrap().messages;
assert_eq!(1, msgs.len());

let handle_used = orig_gas - instance.get_gas();
let handle_used = gas_before_handle - instance.get_gas();
println!("handle used: {}", handle_used);
assert_eq!(handle_used, 115423);
}

#[test]
#[cfg(feature = "default-singlepass")]
fn contract_enforces_gas_limit() {
let mut instance = mock_instance(&CONTRACT_0_7);
let orig_gas = 20_000;
instance.set_gas(orig_gas);
let mut instance = mock_instance_with_gas_limit(&CONTRACT_0_7, 20_000);

// init contract
let params = mock_params(&instance.api, "creator", &coin("1000", "earth"), &[]);
Expand All @@ -224,23 +212,21 @@ mod test {
#[cfg(feature = "default-singlepass")]
fn query_works_with_metering() {
let mut instance = mock_instance(&CONTRACT_0_7);
let orig_gas = 200_000;
instance.set_gas(orig_gas);

// init contract
let params = mock_params(&instance.api, "creator", &coin("1000", "earth"), &[]);
let msg = r#"{"verifier": "verifies", "beneficiary": "benefits"}"#.as_bytes();
let _res = call_init(&mut instance, &params, msg).unwrap().unwrap();

// run contract - just sanity check - results validate in contract unit tests
instance.set_gas(orig_gas);
let gas_before_query = instance.get_gas();
// we need to encode the key in base64
let msg = r#"{"verifier":{}}"#.as_bytes();
let res = call_query(&mut instance, msg).unwrap();
let answer = res.unwrap();
assert_eq!(answer, "verifies".as_bytes());

let query_used = orig_gas - instance.get_gas();
let query_used = gas_before_query - instance.get_gas();
println!("query used: {}", query_used);
assert_eq!(query_used, 60315);
}
Expand Down
9 changes: 8 additions & 1 deletion lib/vm/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ use crate::calls::{call_handle, call_init, call_query};
use crate::compatability::check_api_compatibility;
use crate::instance::Instance;

/// Gas limit for testing
static DEFAULT_GAS_LIMIT: u64 = 500_000;

pub fn mock_instance(wasm: &[u8]) -> Instance<MockStorage, MockApi> {
mock_instance_with_gas_limit(wasm, DEFAULT_GAS_LIMIT)
}

pub fn mock_instance_with_gas_limit(wasm: &[u8], gas_limit: u64) -> Instance<MockStorage, MockApi> {
check_api_compatibility(wasm).unwrap();
let deps = dependencies(20);
Instance::from_code(wasm, deps).unwrap()
Instance::from_code(wasm, deps, gas_limit).unwrap()
}

// init mimicks the call signature of the smart contracts.
Expand Down

0 comments on commit ada2132

Please sign in to comment.