Skip to content

Commit

Permalink
feat: add set_callee_permission (#278)
Browse files Browse the repository at this point in the history
* feat: add succeed_readonly to simple callee contract

* feat: add set_callee_permission

* feat: add comment

* chore: cargo clippy
  • Loading branch information
loloicci committed Mar 30, 2023
1 parent f4e432a commit 6774545
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 4 deletions.
7 changes: 6 additions & 1 deletion contracts/simple-callee/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cosmwasm_std::{callable_points, entry_point, DepsMut, Env, MessageInfo, Response};
use cosmwasm_std::{callable_points, entry_point, Deps, DepsMut, Env, MessageInfo, Response};

use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg};
Expand Down Expand Up @@ -32,6 +32,11 @@ mod callable_points {
()
}

#[callable_point]
fn succeed_readonly(_deps: Deps, _env: Env) {
()
}

#[callable_point]
fn fail(_deps: DepsMut, _env: Env) {
panic!()
Expand Down
23 changes: 22 additions & 1 deletion contracts/simple-callee/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ static CONTRACT: &[u8] =
fn required_exports() -> Vec<(String, FunctionType)> {
vec![
(String::from("succeed"), ([Type::I32], []).into()),
(String::from("succeed_readonly"), ([Type::I32], []).into()),
(String::from("fail"), ([Type::I32], []).into()),
(
String::from("_get_callable_points_properties"),
Expand Down Expand Up @@ -80,13 +81,33 @@ fn callable_point_succeed_works() {
}

#[test]
fn callable_fail_fails() {
fn callable_point_succeed_readonly_works() {
let instance = make_instance();
let env = to_vec(&mock_env()).unwrap();
let env_region_ptr = write_data_to_mock_env(&instance.env, &env).unwrap();

let required_exports = required_exports();
let export_index = 1;
assert_eq!("succeed_readonly".to_string(), required_exports[export_index].0);

// check succeed_readonly
instance
.call_function_strict(
&required_exports[export_index].1,
"succeed_readonly",
&[env_region_ptr.into()],
)
.unwrap();
}

#[test]
fn callable_fail_fails() {
let instance = make_instance();
let env = to_vec(&mock_env()).unwrap();
let env_region_ptr = write_data_to_mock_env(&instance.env, &env).unwrap();

let required_exports = required_exports();
let export_index = 2;
assert_eq!("fail".to_string(), required_exports[export_index].0);

// check unreachable
Expand Down
81 changes: 81 additions & 0 deletions packages/vm/src/dynamic_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::conversion::ref_to_u32;
use crate::environment::{process_gas_info, Environment};
use crate::errors::{CommunicationError, VmError, VmResult};
use crate::imports::write_to_contract;
use crate::instance::Instance;
use crate::memory::read_region;
use serde::{Deserialize, Serialize};
use wasmer::{
ExportType, Exports, Function, FunctionType, ImportObject, Module, RuntimeError, Val,
};
Expand All @@ -18,6 +20,8 @@ use cosmwasm_std::{from_slice, Addr};
const MAX_ADDRESS_LENGTH: usize = 64 * 2;
// enough big value for copy interface. This is less than crate::calls::read_limits::XXX
const MAX_INTERFACE_REGIONS_LENGTH: usize = 1024 * 1024;
const MAX_PROPERTIES_REGIONS_LENGTH: usize = 1024 * 1024;
const GET_PROPERTY_FUNCTION: &str = "_get_callable_points_properties";

pub type WasmerVal = Val;

Expand Down Expand Up @@ -284,6 +288,62 @@ where
}
}

// CalleeProperty represents property about the function of callee
#[derive(Serialize, Deserialize)]
struct CalleeProperty {
is_read_only: bool,
}

// This sets callee instance read/write permission according to
// GET_PROPERTY_FUNCTION in callee instance.
// This checks callee instance does not take write permission in readonly context.
pub fn set_callee_permission<A, S, Q>(
callee_instance: &mut Instance<A, S, Q>,
callable_point: &str,
is_readonly_context: bool,
) -> VmResult<()>
where
A: BackendApi + 'static,
S: Storage + 'static,
Q: Querier + 'static,
{
callee_instance.set_storage_readonly(true);
let ret = callee_instance.call_function(GET_PROPERTY_FUNCTION, &[])?;

let ret_datas = read_region_vals_from_env(
&callee_instance.env,
&ret,
MAX_PROPERTIES_REGIONS_LENGTH,
true,
)?;
if ret_datas.len() != 1 {
return Err(VmError::dynamic_call_err(format!(
"{} returns no or more than 1 values. It should returns just 1 value.",
GET_PROPERTY_FUNCTION
)));
};

let properties: HashMap<String, CalleeProperty> = serde_json::from_slice(&ret_datas[0])
.map_err(|e| VmError::dynamic_call_err(e.to_string()))?;

let property = properties.get(callable_point).ok_or_else(|| {
VmError::dynamic_call_err(format!(
"callee function properties has not key:{}",
callable_point
))
})?;

if is_readonly_context && !property.is_read_only {
// An error occurs because read-only permission cannot be inherited from read-write permission
return Err(VmError::dynamic_call_err(
"a read-write callable point is called in read-only context.",
));
};

callee_instance.set_storage_readonly(property.is_read_only);
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -542,4 +602,25 @@ mod tests {
assert!(result.unwrap_err().message().starts_with("func_info:{module_name:caller, name:fail, signature:[] -> []}, error:Error in dynamic link: \"Error executing Wasm: Wasmer runtime error: RuntimeError: unreachable"));
});
}

#[test]
fn set_callee_permission_works_readwrite() {
let mut instance = mock_instance(&CONTRACT_CALLEE, &[]);
set_callee_permission(&mut instance, "succeed", false).unwrap();
assert!(!instance.env.is_storage_readonly())
}

#[test]
fn set_callee_permission_works_readonly() {
let mut instance = mock_instance(&CONTRACT_CALLEE, &[]);
set_callee_permission(&mut instance, "succeed_readonly", true).unwrap();
assert!(instance.env.is_storage_readonly())
}

#[test]
#[should_panic(expected = "a read-write callable point is called in read-only context.")]
fn set_callee_permission_fails() {
let mut instance = mock_instance(&CONTRACT_CALLEE, &[]);
set_callee_permission(&mut instance, "succeed", true).unwrap();
}
}
25 changes: 24 additions & 1 deletion packages/vm/src/errors/vm_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ pub enum VmError {
#[cfg(feature = "backtraces")]
backtrace: Backtrace,
},
#[error("Error during calling dynamic linked callable point: {}", msg)]
DynamicCallErr {
msg: String,
#[cfg(feature = "backtraces")]
backtrace: Backtrace,
},
}

impl VmError {
Expand Down Expand Up @@ -338,6 +344,13 @@ impl VmError {
backtrace: Backtrace::capture(),
}
}
pub(crate) fn dynamic_call_err(msg: impl Into<String>) -> Self {
VmError::DynamicCallErr {
msg: msg.into(),
#[cfg(feature = "backtraces")]
backtrace: Backtrace::capture(),
}
}
}

impl From<BackendError> for VmError {
Expand Down Expand Up @@ -385,7 +398,7 @@ impl From<wasmer::RuntimeError> for VmError {
original.to_string().starts_with(&message),
"The error message we created is not a prefix of the error message from Wasmer. Our message: '{}'. Wasmer messsage: '{}'",
&message,
original.to_string()
original
);
VmError::runtime_err(format!("Wasmer runtime error: {}", &message))
}
Expand Down Expand Up @@ -605,4 +618,14 @@ mod tests {
e => panic!("Unexpected error: {:?}", e),
}
}

#[test]
fn dynamic_call_err() {
let message = "foobar";
let error = VmError::dynamic_call_err(message);
match error {
VmError::DynamicCallErr { msg, .. } => assert_eq!(msg, message),
e => panic!("Unexpected error: {:?}", e),
}
}
}
3 changes: 2 additions & 1 deletion packages/vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ pub use crate::conversion::{ref_to_u32, to_u32};
#[cfg(feature = "bench")]
pub use crate::dynamic_link::native_dynamic_link_trampoline_for_bench;
pub use crate::dynamic_link::{
dynamic_link, read_region_vals_from_env, write_value_to_env, FunctionMetadata, WasmerVal,
dynamic_link, read_region_vals_from_env, set_callee_permission, write_value_to_env,
FunctionMetadata, WasmerVal,
};
pub use crate::environment::Environment;
pub use crate::errors::{
Expand Down
Binary file modified packages/vm/testdata/simple_callee_1.0.0.wasm
Binary file not shown.

0 comments on commit 6774545

Please sign in to comment.