Commit 1228905c authored by taskooh's avatar taskooh Committed by Hero Bird

Implement ext_call in ink! (#133)

* import ext_call to trait and add function to generate input_data of ext_call

remove #[allow(unused)] from ext_call()

implment call_invoke and call_evaluate

fix format

call_invoke and call_evaluate returns Return<_,CallError>

fixed

* fix format and change return type of test_env

* [core] implement CallError as struct and add call_return field to text_env

* fix trivial and remove lang/src/encode_input_data

* [core] use map_err while handling Result<>
parent 55fb1447
......@@ -40,7 +40,10 @@ mod test_env;
pub use api::*;
pub use traits::*;
pub use self::srml::DefaultSrmlTypes;
pub use self::srml::{
CallError,
DefaultSrmlTypes,
};
/// The storage environment implementation that is currently being used.
///
......
......@@ -19,7 +19,10 @@ mod srml_only;
mod types;
pub use self::types::DefaultSrmlTypes;
pub use self::types::{
CallError,
DefaultSrmlTypes,
};
#[cfg(not(feature = "test-env"))]
pub use self::srml_only::{
......
......@@ -17,6 +17,7 @@
use crate::{
env::{
srml::sys,
CallError,
Env,
EnvStorage,
EnvTypes,
......@@ -25,7 +26,10 @@ use crate::{
storage::Key,
};
use core::marker::PhantomData;
use scale::Decode;
use scale::{
Decode,
Encode,
};
/// Load the contents of the scratch buffer
fn read_scratch_buffer() -> Vec<u8> {
......@@ -160,4 +164,56 @@ where
fn dispatch_raw_call(data: &[u8]) {
unsafe { sys::ext_dispatch_call(data.as_ptr() as u32, data.len() as u32) }
}
fn call_invoke(
callee: <Self as EnvTypes>::AccountId,
gas: u64,
value: <Self as EnvTypes>::Balance,
input_data: &[u8],
) -> Result<(), CallError> {
let callee = callee.encode();
let value = value.encode();
unsafe {
let success = sys::ext_call(
callee.as_ptr() as u32,
callee.len() as u32,
gas,
value.as_ptr() as u32,
value.len() as u32,
input_data.as_ptr() as u32,
input_data.len() as u32,
);
if success == 0 {
Ok(())
} else {
Err(CallError)
}
}
}
fn call_evaluate<U: Decode>(
callee: <Self as EnvTypes>::AccountId,
gas: u64,
value: <Self as EnvTypes>::Balance,
input_data: &[u8],
) -> Result<U, CallError> {
let callee = callee.encode();
let value = value.encode();
unsafe {
let success = sys::ext_call(
callee.as_ptr() as u32,
callee.len() as u32,
gas,
value.as_ptr() as u32,
value.len() as u32,
input_data.as_ptr() as u32,
input_data.len() as u32,
);
if success == 0 {
U::decode(&mut &read_scratch_buffer()[..]).map_err(|_| CallError)
} else {
Err(CallError)
}
}
}
}
......@@ -32,7 +32,6 @@ extern "C" {
) -> u32;
/// Calls a remote smart contract.
#[allow(unused)]
pub fn ext_call(
callee_ptr: u32,
callee_len: u32,
......
......@@ -30,6 +30,13 @@ use scale::{
#[cfg(feature = "ink-generate-abi")]
use type_metadata::Metadata;
/// Errors encountered by calling a remote contract.
///
/// # Note
///
/// This is currently just a placeholder for potential future error codes.
pub struct CallError;
/// The SRML fundamental types.
#[allow(unused)]
#[cfg_attr(feature = "test-env", derive(Debug, Clone, PartialEq, Eq))]
......
......@@ -16,7 +16,10 @@
use super::*;
use crate::{
env::EnvTypes,
env::{
CallError,
EnvTypes,
},
memory::collections::hash_map::{
Entry,
HashMap,
......@@ -47,6 +50,25 @@ impl EventData {
}
}
/// Emulates the data given to remote smart contract call instructions.
pub struct RawCallData {
pub callee: Vec<u8>,
pub gas: u64,
pub value: Vec<u8>,
pub input_data: Vec<u8>,
}
/// Decoded call data of recorded external calls.
pub struct CallData<E>
where
E: crate::env::EnvTypes,
{
pub callee: E::AccountId,
pub gas: u64,
pub value: E::Balance,
pub input_data: Vec<u8>,
}
/// An entry in the storage of the test environment.
///
/// # Note
......@@ -173,6 +195,10 @@ pub struct TestEnvData {
gas_left: Vec<u8>,
/// The total transferred value.
value_transferred: Vec<u8>,
/// The recorded external calls.
calls: Vec<CallData>,
/// The expected return data of the next external call.
call_return: Vec<u8>,
/// Returned data.
return_data: Vec<u8>,
}
......@@ -195,6 +221,8 @@ impl Default for TestEnvData {
gas_left: Vec::new(),
value_transferred: Vec::new(),
dispatched_calls: Vec::new(),
calls: Vec::new(),
call_return: Vec::new(),
return_data: Vec::new(),
}
}
......@@ -215,6 +243,8 @@ impl TestEnvData {
self.total_writes = 0;
self.events.clear();
self.dispatched_calls.clear();
self.calls.clear();
self.call_return.clear();
self.return_data.clear();
}
......@@ -309,6 +339,17 @@ impl TestEnvData {
self.dispatched_calls.iter().map(Vec::as_slice)
}
/// Records a new external call.
pub fn add_call(&mut self, callee: &[u8], gas: u64, value: &[u8], input_data: &[u8]) {
let new_call = CallData {
callee: callee.to_vec(),
gas,
value: value.to_vec(),
input_data: input_data.to_vec(),
};
self.calls.push(new_call);
}
/// Returns the latest returned data.
pub fn returned_data(&self) -> &[u8] {
&self.return_data
......@@ -392,6 +433,17 @@ impl TestEnvData {
pub fn dispatch_call(&mut self, call: &[u8]) {
self.add_dispatched_call(call);
}
pub fn call(
&mut self,
callee: &[u8],
gas: u64,
value: &[u8],
input_data: &[u8],
) -> Vec<u8> {
self.add_call(callee, gas, value, input_data);
self.call_return.clone()
}
}
thread_local! {
......@@ -541,6 +593,35 @@ where
fn dispatch_raw_call(data: &[u8]) {
TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().dispatch_call(data))
}
fn call_invoke(
callee: T::AccountId,
gas: u64,
value: T::Balance,
input_data: &[u8],
) -> Result<(), CallError> {
let callee = &(callee.encode())[..];
let value = &(value.encode())[..];
let _return_data = TEST_ENV_DATA
.with(|test_env| test_env.borrow_mut().call(callee, gas, value, input_data));
Ok(())
}
fn call_evaluate<U: Decode>(
callee: T::AccountId,
gas: u64,
value: T::Balance,
input_data: &[u8],
) -> Result<U, CallError> {
let callee = &(callee.encode())[..];
let value = &(value.encode())[..];
TEST_ENV_DATA.with(|test_env| {
U::decode(
&mut &(test_env.borrow_mut().call(callee, gas, value, input_data))[..],
)
.map_err(|_| CallError)
})
}
}
pub enum TestEnvStorage {}
......
......@@ -15,10 +15,14 @@
// along with ink!. If not, see <http://www.gnu.org/licenses/>.
use crate::{
env::CallError,
memory::vec::Vec,
storage::Key,
};
use scale::Codec;
use scale::{
Codec,
Decode,
};
#[cfg(not(feature = "test-env"))]
/// The environmental types usable by contracts defined with ink!.
......@@ -136,4 +140,20 @@ pub trait Env: EnvTypes {
/// Dispatches a call into the Runtime.
fn dispatch_raw_call(data: &[u8]);
/// Calls a remote smart contract without returning data
fn call_invoke(
callee: <Self as EnvTypes>::AccountId,
gas: u64,
value: <Self as EnvTypes>::Balance,
input_data: &[u8],
) -> Result<(), CallError>;
/// Calls a remote smart contract and return encoded data
fn call_evaluate<T: Decode>(
callee: <Self as EnvTypes>::AccountId,
gas: u64,
value: <Self as EnvTypes>::Balance,
input_data: &[u8],
) -> Result<T, CallError>;
}
......@@ -19,6 +19,7 @@ use core::marker::PhantomData;
use ink_core::{
env::{
self,
CallError,
Env,
},
storage::alloc::{
......@@ -28,7 +29,10 @@ use ink_core::{
Initialize,
},
};
use scale::Encode as _;
use scale::{
Decode,
Encode as _,
};
/// Provides a safe interface to an environment given a contract state.
pub struct ExecutionEnv<State, Env> {
......@@ -194,4 +198,26 @@ impl<T: Env> EnvHandler<T> {
{
T::dispatch_raw_call(call.into().encode().as_slice())
}
/// Calls a remote smart contract without returning data.
pub fn call_invoke(
&mut self,
callee: T::AccountId,
gas: u64,
value: T::Balance,
input_data: &[u8],
) -> Result<(), CallError> {
T::call_invoke(callee, gas, value, input_data)
}
/// Calls a remote smart contract with returning encoded data.
pub fn call_evaluate<U: Decode>(
&mut self,
callee: T::AccountId,
gas: u64,
value: T::Balance,
input_data: &[u8],
) -> Result<U, CallError> {
T::call_evaluate(callee, gas, value, input_data)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment