From d4d63a156c218392df41701252f9d927736a32f7 Mon Sep 17 00:00:00 2001 From: h4x3rotab Date: Wed, 17 Aug 2022 20:15:35 +0000 Subject: [PATCH 1/4] wip --- crates/env/src/api.rs | 2 +- crates/env/src/backend.rs | 9 +- crates/env/src/engine/off_chain/impls.rs | 93 ++++++++++--- crates/env/src/engine/off_chain/mod.rs | 76 ++++++++++- crates/env/src/engine/off_chain/stack.rs | 129 ++++++++++++++++++ crates/env/src/engine/off_chain/test_api.rs | 15 ++ crates/env/src/engine/on_chain/impls.rs | 2 +- crates/env/src/lib.rs | 11 ++ crates/lang/codegen/src/generator/contract.rs | 7 +- crates/lang/codegen/src/generator/dispatch.rs | 19 ++- crates/lang/src/codegen/dispatch/execution.rs | 3 +- crates/lang/src/reflect/dispatch.rs | 2 + crates/lang/src/reflect/mod.rs | 1 + examples/delegator/adder/lib.rs | 40 ++++++ 14 files changed, 375 insertions(+), 34 deletions(-) create mode 100644 crates/env/src/engine/off_chain/stack.rs diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 581d7dabb4f..de728087265 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -387,7 +387,7 @@ where /// # Note /// /// This function stops the execution of the contract immediately. -pub fn return_value(return_flags: ReturnFlags, return_value: &R) -> ! +pub fn return_value(return_flags: ReturnFlags, return_value: &R) where R: scale::Encode, { diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 43ee3252332..9c69a947f11 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -30,7 +30,7 @@ use crate::{ use ink_primitives::Key; /// The flags to indicate further information about the end of a contract execution. -#[derive(Default)] +#[derive(Default, Debug, Clone)] pub struct ReturnFlags { value: u32, } @@ -46,6 +46,11 @@ impl ReturnFlags { self } + /// Returns if the flag is set to reverted. + pub fn reverted(&self) -> bool { + self.value > 0 + } + /// Returns the underlying `u32` representation. #[cfg(not(feature = "std"))] pub(crate) fn into_u32(self) -> u32 { @@ -219,7 +224,7 @@ pub trait EnvBackend { /// /// The `flags` parameter can be used to revert the state changes of the /// entire execution if necessary. - fn return_value(&mut self, flags: ReturnFlags, return_value: &R) -> ! + fn return_value(&mut self, flags: ReturnFlags, return_value: &R) where R: scale::Encode; diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 26e1e3b4f1a..31a4d17c379 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -218,14 +218,18 @@ impl EnvBackend for EnvInstance { where T: scale::Decode, { - unimplemented!("the off-chain env does not implement `seal_input`") + scale::Decode::decode(&mut &self.stack.peek().input[..]) + .map_err(|err| Error::Decode(err)) } - fn return_value(&mut self, _flags: ReturnFlags, _return_value: &R) -> ! + fn return_value(&mut self, flags: ReturnFlags, return_value: &R) where R: scale::Encode, { - unimplemented!("the off-chain env does not implement `seal_return_value`") + if flags.reverted() { + panic!("reverted."); + } + self.stack.set_return_value(flags, return_value.encode()); } fn debug_message(&mut self, message: &str) { @@ -337,8 +341,12 @@ impl EnvBackend for EnvInstance { Ok(decoded) } - fn set_code_hash(&mut self, _code_hash: &[u8]) -> Result<()> { - unimplemented!("off-chain environment does not support `set_code_hash`") + fn set_code_hash(&mut self, code_hash: &[u8]) -> Result<()> { + let me = self.stack.peek().callee; + let hash: crate::Hash = scale::Decode::decode(&mut &code_hash[..]) + .map_err(|err| Error::Decode(err))?; + self.contracts.update_code(me, hash); + Ok(()) } } @@ -420,11 +428,16 @@ impl TypedEnvBackend for EnvInstance { R: scale::Decode, { let _gas_limit = params.gas_limit(); - let _callee = params.callee(); + let callee = params.callee(); let _call_flags = params.call_flags().into_u32(); - let _transferred_value = params.transferred_value(); - let _input = params.exec_input(); - unimplemented!("off-chain environment does not support contract invocation") + let transferred_value = params.transferred_value(); + if *transferred_value != num_traits::Zero::zero() { + unimplemented!("off-chain environment does not support value trnsfer when calling contracts") + } + let input = params.exec_input(); + let input_data = scale::Encode::encode(&input); + let norm_callee = from_env::account_id::(callee); + self.call(&norm_callee, input_data) } fn invoke_contract_delegate( @@ -451,12 +464,24 @@ impl TypedEnvBackend for EnvInstance { Args: scale::Encode, Salt: AsRef<[u8]>, { - let _code_hash = params.code_hash(); + let code_hash = params.code_hash(); let _gas_limit = params.gas_limit(); - let _endowment = params.endowment(); - let _input = params.exec_input(); - let _salt_bytes = params.salt_bytes(); - unimplemented!("off-chain environment does not support contract instantiation") + let endowment = params.endowment(); // TODO: not considered yet + let input = params.exec_input(); + let _salt_bytes = params.salt_bytes(); // TODO: not considered yet + + if *endowment != num_traits::Zero::zero() { + unimplemented!("off-chain environment does not support value trnsfer when instantiating contracts") + } + + let hash = from_env::hash::(code_hash); + let input_data = scale::Encode::encode(&input); + + // gen address (hash [n]) + let account = self.contracts.next_address_of(&hash); + self.contracts.register_contract(hash, account.clone()); + self.deploy(&account, input_data)?; + Ok(to_env::account_id::(&account)) } fn terminate_contract(&mut self, beneficiary: E::AccountId) -> ! @@ -495,31 +520,59 @@ impl TypedEnvBackend for EnvInstance { scale::Decode::decode(&mut &output[..]).map_err(Into::into) } - fn is_contract(&mut self, _account: &E::AccountId) -> bool + fn is_contract(&mut self, account: &E::AccountId) -> bool where E: Environment, { - unimplemented!("off-chain environment does not support contract instantiation") + self.contracts.is_contract(&from_env::account_id::(account)) } fn caller_is_origin(&mut self) -> bool where E: Environment, { - unimplemented!("off-chain environment does not support cross-contract calls") + EnvInstance::caller_is_origin(self) } - fn code_hash(&mut self, _account: &E::AccountId) -> Result + fn code_hash(&mut self, account: &E::AccountId) -> Result where E: Environment, { - unimplemented!("off-chain environment does not support `code_hash`") + self + .contracts + .code_hash(&from_env::account_id::(account)) + .map(|h| to_env::hash::(&h)) + .ok_or(Error::CodeNotFound) } fn own_code_hash(&mut self) -> Result where E: Environment, { - unimplemented!("off-chain environment does not support `own_code_hash`") + let me = self.stack.peek().callee; + let hash = self.contracts.code_hash(&me) + .expect("contract must has code hash"); + Ok(to_env::hash::(&hash)) + } +} + +mod from_env { + pub fn account_id(account: &E::AccountId) -> crate::AccountId { + crate::AccountId::try_from(account.as_ref()).unwrap() + } + pub fn hash(hash: &E::Hash) -> crate::Hash { + crate::Hash::try_from(hash.as_ref()).unwrap() + } +} + +mod to_env { + use scale::{Encode, Decode}; + pub fn account_id(account: &crate::AccountId) -> E::AccountId { + let raw = account.encode(); + Decode::decode(&mut &raw[..]).unwrap() + } + pub fn hash(hash: &crate::Hash) -> E::Hash { + let raw = hash.encode(); + Decode::decode(&mut &raw[..]).unwrap() } } diff --git a/crates/env/src/engine/off_chain/mod.rs b/crates/env/src/engine/off_chain/mod.rs index 2d2d0be6e62..3c1b95cf95f 100644 --- a/crates/env/src/engine/off_chain/mod.rs +++ b/crates/env/src/engine/off_chain/mod.rs @@ -16,6 +16,7 @@ mod call_data; mod impls; pub mod test_api; mod types; +mod stack; #[cfg(test)] mod tests; @@ -24,6 +25,7 @@ pub use call_data::CallData; use super::OnInstance; use crate::Error; +use stack::{Stack, Frame, ContractStore}; use derive_more::From; use ink_engine::ext::Engine; @@ -31,6 +33,76 @@ use ink_engine::ext::Engine; /// The off-chain environment. pub struct EnvInstance { engine: Engine, + stack: Stack, + pub contracts: ContractStore, +} + +impl EnvInstance { + fn sync_stack(&mut self) { + use scale::Encode; + + let ctx = self.stack.peek(); + if let Some(caller) = ctx.caller { + self.engine.set_caller(caller.encode()); + } + self.engine.set_callee(ctx.callee.encode()); + } + + fn push_frame(&mut self, callee: &crate::AccountId, input: Vec) { + self.stack.push(callee, input); + self.sync_stack(); + } + + fn pop_frame(&mut self) -> Option { + let ctx = self.stack.pop(); + if ctx.is_some() { + self.sync_stack(); + } + ctx + } + + pub fn call(&mut self, callee: &crate::AccountId, input: Vec) -> crate::Result { + self.push_frame(callee, input.clone()); + let (_deploy, call) = self.contracts.entrypoints(callee) + .ok_or(Error::NotCallable)?; + // TODO: snapshot the db + // TODO: unwind panic? + call(); + // Read return value & process revert + let frame = self.pop_frame().expect("frame exists; qed."); + let data = if let Some((flags, data)) = frame.return_value { + if flags.reverted() { + // TODO: revert the db snapshot + return Err(Error::CalleeReverted) + } + data + } else { + Default::default() + }; + scale::Decode::decode(&mut &data[..]) + .map_err(|err| Error::Decode(err)) + } + + pub fn deploy(&mut self, account: &crate::AccountId, input: Vec) -> crate::Result<()> { + self.push_frame(account, input.clone()); + let (deploy, _call) = self.contracts.entrypoints(account) + .ok_or(Error::NotCallable)?; + deploy(); + self.pop_frame(); + // Read OUTPUT + // what if revert? + // scale::Decode::decode(&mut &input[..]) + // .map_err(|err| Error::Decode(err)) + Ok(()) + } + + pub fn caller_is_origin(&self) -> bool { + let origin = self.stack.origin(); + let ctx = self.stack.peek(); + assert!(ctx.level > 0, "should never reach when there's no running contract"); + ctx.caller.expect("contract has caller; qed.") == origin + } + } impl OnInstance for EnvInstance { @@ -42,7 +114,9 @@ impl OnInstance for EnvInstance { thread_local!( static INSTANCE: RefCell = RefCell::new( EnvInstance { - engine: Engine::new() + engine: Engine::new(), + stack: Stack::new(crate::AccountId::from([1u8; 32])), + contracts: Default::default(), } ) ); diff --git a/crates/env/src/engine/off_chain/stack.rs b/crates/env/src/engine/off_chain/stack.rs new file mode 100644 index 00000000000..2331304859e --- /dev/null +++ b/crates/env/src/engine/off_chain/stack.rs @@ -0,0 +1,129 @@ +use std::vec::Vec; +use std::collections::BTreeMap; + +use crate::{AccountId, Hash, account_id, ReturnFlags}; + + +/// A frame in the call stack +#[derive(Clone, Debug)] +pub struct Frame { + pub level: u32, + pub caller: Option, + pub callee: AccountId, + pub input: Vec, + pub return_value: Option<(ReturnFlags, Vec)> +} + +pub struct Stack { + pub stack: Vec, +} + +impl Stack { + /// Crates a call stack with the default `account` + pub fn new(account: AccountId) -> Self { + Self { + stack: vec![Frame { + level: 0, + caller: None, + callee: account, + input: Default::default(), + return_value: None, + }], + } + } + + /// Changes the caller account + /// + /// Only allowed outside any contract call (when the stack is empty). + pub fn switch_account(&mut self, account: AccountId) -> Result<(), ()> { + let stack = &mut self.stack; + if stack.len() != 1 { + return Err(()) + } + let ctx = stack.get_mut(0).ok_or(())?; + ctx.callee = account; + Ok(()) + } + + + /// Pushes a new call frame + pub fn push(&mut self, callee: &AccountId, input: Vec) { + let parent_ctx = self.peek(); + self.stack.push(Frame { + level: parent_ctx.level + 1, + caller: Some(parent_ctx.callee), + callee: callee.clone(), + input, + return_value: None, + }); + self.sync_to_ink(); + } + + /// Pops the call frame and returns the frame + pub fn pop(&mut self) -> Option { + if self.stack.len() > 1 { + let ctx = self.stack.pop(); + self.sync_to_ink(); + ctx + } else { + None + } + } + + /// Peeks the current call frame + pub fn peek(&self) -> Frame { + self.stack.last().cloned().expect("stack is never empty; qed.") + } + + pub fn set_return_value(&mut self, flags: ReturnFlags, value: Vec) { + let cur = self.stack.last_mut().expect("stack is never empty; qed."); + cur.return_value = Some((flags, value)); + } + + pub fn origin(&self) -> AccountId { + self.stack.first().expect("stack is never empty; qed").callee + } + + /// Syncs the top call frame to ink testing environment + pub fn sync_to_ink(&self) {} +} + +#[derive(Default)] +pub struct ContractStore { + code: BTreeMap, + fns: BTreeMap, +} + +impl ContractStore { + pub fn register_contract(&mut self, code: Hash, id: AccountId) { + self.code.insert(id, code); + } + pub fn register_entrypoints(&mut self, code: Hash, deploy: fn(), call: fn()) + { + self.fns.insert(code, (deploy, call)); + } + pub fn entrypoints(&self, account: &AccountId) -> Option<(fn(), fn())> { + let code = self.code.get(account)?; + self.fns.get(code).cloned() + } + pub fn is_contract(&self, account: &AccountId) -> bool { + self.code.contains_key(account) + } + pub fn code_hash(&self, account: &AccountId) -> Option { + self.code.get(account).cloned() + } + pub fn update_code(&mut self, account: AccountId, code: Hash) { + // account can be non-existing? + self.code.insert(account, code); + } + pub fn next_address_of(&self, code: &Hash) -> AccountId { + let count = self.code + .iter() + .filter(|(_k, v)| *v == code) + .count(); + let mut raw = [0u8; 32]; + raw.copy_from_slice(code.as_ref()); + raw[raw.len() - 1] = count as u8; + AccountId::from(raw) + } +} diff --git a/crates/env/src/engine/off_chain/test_api.rs b/crates/env/src/engine/off_chain/test_api.rs index 4d73be03ac3..29d0c31fce0 100644 --- a/crates/env/src/engine/off_chain/test_api.rs +++ b/crates/env/src/engine/off_chain/test_api.rs @@ -355,3 +355,18 @@ pub fn assert_contract_termination( assert_eq!(value_transferred, expected_value_transferred_to_beneficiary); assert_eq!(beneficiary, expected_beneficiary); } + +/// TODO +pub fn register_contract(code_hash: crate::Hash) +where + T: Environment, + C: crate::reflect::ContractEntrypoints + 'static, +{ + ::on_instance(|instance| { + let deploy = ::deploy; + let call = ::call; + instance + .contracts + .register_entrypoints(code_hash, deploy, call); + }) +} diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index e8a98d71c6f..c38fdd30b7c 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -256,7 +256,7 @@ impl EnvBackend for EnvInstance { self.get_property::(ext::input) } - fn return_value(&mut self, flags: ReturnFlags, return_value: &R) -> ! + fn return_value(&mut self, flags: ReturnFlags, return_value: &R) where R: scale::Encode, { diff --git a/crates/env/src/lib.rs b/crates/env/src/lib.rs index 306b6ea774e..674c4f3902a 100644 --- a/crates/env/src/lib.rs +++ b/crates/env/src/lib.rs @@ -110,6 +110,17 @@ pub use self::{ }, }; +/// TODO +pub mod reflect { + /// TODO + pub trait ContractEntrypoints { + /// TODO + fn call(); + /// TODO + fn deploy(); + } +} + cfg_if::cfg_if! { if #[cfg(any(feature = "ink-debug", feature = "std"))] { /// Required by the `debug_print*` macros below, because there is no guarantee that diff --git a/crates/lang/codegen/src/generator/contract.rs b/crates/lang/codegen/src/generator/contract.rs index 1662b826428..f0ea0b678fd 100644 --- a/crates/lang/codegen/src/generator/contract.rs +++ b/crates/lang/codegen/src/generator/contract.rs @@ -50,7 +50,7 @@ impl GenerateCode for Contract<'_> { .items() .iter() .filter_map(ir::Item::map_rust_item); - quote! { + let r = quote! { #( #attrs )* #vis mod #ident { #env @@ -62,6 +62,9 @@ impl GenerateCode for Contract<'_> { #metadata #( #non_ink_items )* } - } + }; + + println!("CONTRACT\n{}\nCONTRACT", r); + r } } diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index 6ea44b7a950..b8e6f16054c 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -79,8 +79,8 @@ impl GenerateCode for Dispatch<'_> { #constructor_decoder_type #message_decoder_type - #[cfg(not(test))] - #[cfg(not(feature = "ink-as-dependency"))] + //#[cfg(not(test))] + //#[cfg(not(feature = "ink-as-dependency"))] const _: () = { #entry_points }; @@ -401,8 +401,7 @@ impl Dispatch<'_> { let any_message_accept_payment = self.any_message_accepts_payment_expr(message_spans); quote_spanned!(span=> - #[cfg(not(test))] - #[no_mangle] + #[cfg_attr(all(not(feature = "ink-as-dependency"), not(test)), no_mangle)] #[allow(clippy::nonminimal_bool)] fn deploy() { if !#any_constructor_accept_payment { @@ -422,8 +421,7 @@ impl Dispatch<'_> { }) } - #[cfg(not(test))] - #[no_mangle] + #[cfg_attr(all(not(feature = "ink-as-dependency"), not(test)), no_mangle)] #[allow(clippy::nonminimal_bool)] fn call() { if !#any_message_accept_payment { @@ -442,6 +440,15 @@ impl Dispatch<'_> { ::core::panic!("dispatching ink! message failed: {}", error) }) } + + impl ::ink_lang::reflect::ContractEntrypoints for #storage_ident { + fn deploy() { + deploy(); + } + fn call() { + call(); + } + } ) } diff --git a/crates/lang/src/codegen/dispatch/execution.rs b/crates/lang/src/codegen/dispatch/execution.rs index 749b1d841dc..db915611c90 100644 --- a/crates/lang/src/codegen/dispatch/execution.rs +++ b/crates/lang/src/codegen/dispatch/execution.rs @@ -97,7 +97,8 @@ where >( ReturnFlags::default().set_reverted(true), result.return_value(), - ) + ); + panic!("execute_constructor reverted"); } } } diff --git a/crates/lang/src/reflect/dispatch.rs b/crates/lang/src/reflect/dispatch.rs index e18e5212214..a350a2d7956 100644 --- a/crates/lang/src/reflect/dispatch.rs +++ b/crates/lang/src/reflect/dispatch.rs @@ -653,3 +653,5 @@ impl From for scale::Error { pub trait DecodeDispatch: scale::Decode { fn decode_dispatch(input: &mut I) -> Result; } + +pub use ink_env::reflect::ContractEntrypoints; diff --git a/crates/lang/src/reflect/mod.rs b/crates/lang/src/reflect/mod.rs index 3f59becff34..8f54e6cef4f 100644 --- a/crates/lang/src/reflect/mod.rs +++ b/crates/lang/src/reflect/mod.rs @@ -45,6 +45,7 @@ pub use self::{ DispatchableConstructorInfo, DispatchableMessageInfo, ExecuteDispatchable, + ContractEntrypoints, }, event::ContractEventBase, trait_def::{ diff --git a/examples/delegator/adder/lib.rs b/examples/delegator/adder/lib.rs index 5996ed1a787..c05b455b9df 100644 --- a/examples/delegator/adder/lib.rs +++ b/examples/delegator/adder/lib.rs @@ -32,3 +32,43 @@ mod adder { } } } + +#[cfg(test)] +mod test { + #[test] + fn it_works() { + use super::*; + use accumulator::{Accumulator, AccumulatorRef}; + + // register Accumulator & Adder + let hash1 = ink_env::Hash::try_from([1u8; 32]).unwrap(); + let hash2 = ink_env::Hash::try_from([2u8; 32]).unwrap(); + ink_env::test::register_contract::( + hash1.clone() + ); + ink_env::test::register_contract::( + hash2.clone() + ); + + let accumualtor = AccumulatorRef::new(0) + .code_hash(hash1.clone()) + .endowment(0) + .salt_bytes([0u8; 0]) + .instantiate() + .expect("failed at instantiating the `AccumulatorRef` contract"); + let adder = AdderRef::new(accumualtor.clone()) + .code_hash(hash1.clone()) + .endowment(0) + .salt_bytes([0u8; 0]) + .instantiate() + .expect("failed at instantiating the `AdderRef` contract"); + + + // instantiate accumulator + // instantiate adder + + // adder.new(accumulator) + // adder.inc(accumulator) + // assert(accumulator.get() == 1) + } +} From 7cdabc7b15b21b2a261f4304f947043f4dca6a98 Mon Sep 17 00:00:00 2001 From: h4x3rotab Date: Wed, 17 Aug 2022 21:05:07 +0000 Subject: [PATCH 2/4] walk around BorrowMutError --- crates/env/src/api.rs | 12 ++++- crates/env/src/backend.rs | 31 +++++++++++-- crates/env/src/engine/off_chain/impls.rs | 58 ++++++++++++++++++++---- crates/env/src/engine/off_chain/mod.rs | 24 +--------- examples/delegator/accumulator/lib.rs | 3 ++ examples/delegator/adder/lib.rs | 27 +++++++---- 6 files changed, 107 insertions(+), 48 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index de728087265..a7ecb52b40e 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -250,8 +250,12 @@ where Args: scale::Encode, R: scale::Decode, { + let call = ::on_instance(|instance| { + TypedEnvBackend::invoke_contract_begin::(instance, params) + })?; + call(); ::on_instance(|instance| { - TypedEnvBackend::invoke_contract::(instance, params) + TypedEnvBackend::invoke_contract_end::(instance) }) } @@ -303,8 +307,12 @@ where Args: scale::Encode, Salt: AsRef<[u8]>, { + let deploy = ::on_instance(|instance| { + TypedEnvBackend::instantiate_contract_begin::(instance, params) + })?; + deploy(); ::on_instance(|instance| { - TypedEnvBackend::instantiate_contract::(instance, params) + TypedEnvBackend::instantiate_contract_end::(instance) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 9c69a947f11..4ce32c1a51f 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -391,15 +391,27 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`invoke_contract`][`crate::invoke_contract`] - fn invoke_contract( + fn invoke_contract_begin( &mut self, call_data: &CallParams, Args, R>, - ) -> Result + ) -> Result where E: Environment, Args: scale::Encode, R: scale::Decode; + /// Invokes a contract message and returns its result. + /// + /// # Note + /// + /// For more details visit: [`invoke_contract`][`crate::invoke_contract`] + fn invoke_contract_end( + &mut self, + ) -> Result + where + E: Environment, + R: scale::Decode; + /// Invokes a contract message via delegate call and returns its result. /// /// # Note @@ -419,15 +431,26 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`instantiate_contract`][`crate::instantiate_contract`] - fn instantiate_contract( + fn instantiate_contract_begin( &mut self, params: &CreateParams, - ) -> Result + ) -> Result where E: Environment, Args: scale::Encode, Salt: AsRef<[u8]>; + /// Instantiates another contract. + /// + /// # Note + /// + /// For more details visit: [`instantiate_contract`][`crate::instantiate_contract`] + fn instantiate_contract_end( + &mut self, + ) -> Result + where + E: Environment; + /// Terminates a smart contract. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 31a4d17c379..cdec8acf295 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -418,10 +418,10 @@ impl TypedEnvBackend for EnvInstance { self.engine.deposit_event(&enc_topics[..], enc_data); } - fn invoke_contract( + fn invoke_contract_begin( &mut self, params: &CallParams, Args, R>, - ) -> Result + ) -> Result where E: Environment, Args: scale::Encode, @@ -435,11 +435,38 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("off-chain environment does not support value trnsfer when calling contracts") } let input = params.exec_input(); - let input_data = scale::Encode::encode(&input); - let norm_callee = from_env::account_id::(callee); - self.call(&norm_callee, input_data) + let input = scale::Encode::encode(&input); + let callee = from_env::account_id::(callee); + + self.push_frame(&callee, input.clone()); + let (_deploy, call) = self.contracts.entrypoints(&callee) + .ok_or(Error::NotCallable)?; + // TODO: snapshot the db + // TODO: unwind panic? + Ok(call) } + fn invoke_contract_end(&mut self) -> Result + where + E: Environment, + R: scale::Decode, + { + // Read return value & process revert + let frame = self.pop_frame().expect("frame exists; qed."); + let data = if let Some((flags, data)) = frame.return_value { + if flags.reverted() { + // TODO: revert the db snapshot + return Err(Error::CalleeReverted) + } + data + } else { + Default::default() + }; + scale::Decode::decode(&mut &data[..]) + .map_err(|err| Error::Decode(err)) + } + + fn invoke_contract_delegate( &mut self, params: &CallParams, Args, R>, @@ -455,10 +482,10 @@ impl TypedEnvBackend for EnvInstance { ) } - fn instantiate_contract( + fn instantiate_contract_begin( &mut self, params: &CreateParams, - ) -> Result + ) -> Result where E: Environment, Args: scale::Encode, @@ -475,13 +502,24 @@ impl TypedEnvBackend for EnvInstance { } let hash = from_env::hash::(code_hash); - let input_data = scale::Encode::encode(&input); + let input = scale::Encode::encode(&input); // gen address (hash [n]) let account = self.contracts.next_address_of(&hash); self.contracts.register_contract(hash, account.clone()); - self.deploy(&account, input_data)?; - Ok(to_env::account_id::(&account)) + + self.push_frame(&account, input.clone()); + let (deploy, _call) = self.contracts.entrypoints(&account) + .ok_or(Error::NotCallable)?; + Ok(deploy) + } + + fn instantiate_contract_end(&mut self) -> Result + where + E: Environment, + { + let frame = self.pop_frame().expect("frame must exist; qed."); + Ok(to_env::account_id::(&frame.callee)) } fn terminate_contract(&mut self, beneficiary: E::AccountId) -> ! diff --git a/crates/env/src/engine/off_chain/mod.rs b/crates/env/src/engine/off_chain/mod.rs index 3c1b95cf95f..43bd4c5b38d 100644 --- a/crates/env/src/engine/off_chain/mod.rs +++ b/crates/env/src/engine/off_chain/mod.rs @@ -49,6 +49,7 @@ impl EnvInstance { } fn push_frame(&mut self, callee: &crate::AccountId, input: Vec) { + println!("PUSH {:?} ({:?})", callee, input); self.stack.push(callee, input); self.sync_stack(); } @@ -56,33 +57,12 @@ impl EnvInstance { fn pop_frame(&mut self) -> Option { let ctx = self.stack.pop(); if ctx.is_some() { + println!("POP {:?}", ctx.as_ref().unwrap().callee); self.sync_stack(); } ctx } - pub fn call(&mut self, callee: &crate::AccountId, input: Vec) -> crate::Result { - self.push_frame(callee, input.clone()); - let (_deploy, call) = self.contracts.entrypoints(callee) - .ok_or(Error::NotCallable)?; - // TODO: snapshot the db - // TODO: unwind panic? - call(); - // Read return value & process revert - let frame = self.pop_frame().expect("frame exists; qed."); - let data = if let Some((flags, data)) = frame.return_value { - if flags.reverted() { - // TODO: revert the db snapshot - return Err(Error::CalleeReverted) - } - data - } else { - Default::default() - }; - scale::Decode::decode(&mut &data[..]) - .map_err(|err| Error::Decode(err)) - } - pub fn deploy(&mut self, account: &crate::AccountId, input: Vec) -> crate::Result<()> { self.push_frame(account, input.clone()); let (deploy, _call) = self.contracts.entrypoints(account) diff --git a/examples/delegator/accumulator/lib.rs b/examples/delegator/accumulator/lib.rs index fae1482a9a6..97e56d57ef4 100644 --- a/examples/delegator/accumulator/lib.rs +++ b/examples/delegator/accumulator/lib.rs @@ -25,7 +25,10 @@ pub mod accumulator { /// Mutates the internal value. #[ink(message)] pub fn inc(&mut self, by: i32) { + println!("acc::inc invoked with {} by {:?} myself {:?}", + by, self.env().caller(), self.env().account_id()); self.value += by; + panic!("stack tract"); } /// Returns the current state. diff --git a/examples/delegator/adder/lib.rs b/examples/delegator/adder/lib.rs index c05b455b9df..fb50507cf68 100644 --- a/examples/delegator/adder/lib.rs +++ b/examples/delegator/adder/lib.rs @@ -28,6 +28,8 @@ mod adder { /// Increases the `accumulator` value by some amount. #[ink(message)] pub fn inc(&mut self, by: i32) { + println!("adder::inc invoked with {} by {:?} myself {:?}", + by, self.env().caller(), self.env().account_id()); self.accumulator.inc(by) } } @@ -41,8 +43,8 @@ mod test { use accumulator::{Accumulator, AccumulatorRef}; // register Accumulator & Adder - let hash1 = ink_env::Hash::try_from([1u8; 32]).unwrap(); - let hash2 = ink_env::Hash::try_from([2u8; 32]).unwrap(); + let hash1 = ink_env::Hash::try_from([10u8; 32]).unwrap(); + let hash2 = ink_env::Hash::try_from([20u8; 32]).unwrap(); ink_env::test::register_contract::( hash1.clone() ); @@ -50,25 +52,30 @@ mod test { hash2.clone() ); - let accumualtor = AccumulatorRef::new(0) + let acc = AccumulatorRef::new(0) .code_hash(hash1.clone()) .endowment(0) .salt_bytes([0u8; 0]) .instantiate() .expect("failed at instantiating the `AccumulatorRef` contract"); - let adder = AdderRef::new(accumualtor.clone()) + let mut adder = AdderRef::new(acc.clone()) .code_hash(hash1.clone()) .endowment(0) .salt_bytes([0u8; 0]) .instantiate() .expect("failed at instantiating the `AdderRef` contract"); + print_env("1"); + assert_eq!(acc.get(), 0); + print_env("2"); + adder.inc(1); + assert_eq!(acc.get(), 1); + } - // instantiate accumulator - // instantiate adder - - // adder.new(accumulator) - // adder.inc(accumulator) - // assert(accumulator.get() == 1) + fn print_env(prefix: &str) { + use ink_lang::codegen::StaticEnv; + use super::*; + println!("[{}] outer env caller = {:?} callee = {:?}", + prefix, Adder::env().caller(), Adder::env().account_id()); } } From ff3edae94eb33d88c0a7e8ea176e0507d7b1810e Mon Sep 17 00:00:00 2001 From: h4x3rotab Date: Wed, 17 Aug 2022 21:10:21 +0000 Subject: [PATCH 3/4] fix delegator example test --- crates/env/src/engine/off_chain/mod.rs | 2 -- crates/lang/codegen/src/generator/contract.rs | 7 ++----- examples/delegator/accumulator/lib.rs | 3 --- examples/delegator/adder/lib.rs | 13 +------------ 4 files changed, 3 insertions(+), 22 deletions(-) diff --git a/crates/env/src/engine/off_chain/mod.rs b/crates/env/src/engine/off_chain/mod.rs index 43bd4c5b38d..9ae3df89aa2 100644 --- a/crates/env/src/engine/off_chain/mod.rs +++ b/crates/env/src/engine/off_chain/mod.rs @@ -49,7 +49,6 @@ impl EnvInstance { } fn push_frame(&mut self, callee: &crate::AccountId, input: Vec) { - println!("PUSH {:?} ({:?})", callee, input); self.stack.push(callee, input); self.sync_stack(); } @@ -57,7 +56,6 @@ impl EnvInstance { fn pop_frame(&mut self) -> Option { let ctx = self.stack.pop(); if ctx.is_some() { - println!("POP {:?}", ctx.as_ref().unwrap().callee); self.sync_stack(); } ctx diff --git a/crates/lang/codegen/src/generator/contract.rs b/crates/lang/codegen/src/generator/contract.rs index f0ea0b678fd..1662b826428 100644 --- a/crates/lang/codegen/src/generator/contract.rs +++ b/crates/lang/codegen/src/generator/contract.rs @@ -50,7 +50,7 @@ impl GenerateCode for Contract<'_> { .items() .iter() .filter_map(ir::Item::map_rust_item); - let r = quote! { + quote! { #( #attrs )* #vis mod #ident { #env @@ -62,9 +62,6 @@ impl GenerateCode for Contract<'_> { #metadata #( #non_ink_items )* } - }; - - println!("CONTRACT\n{}\nCONTRACT", r); - r + } } } diff --git a/examples/delegator/accumulator/lib.rs b/examples/delegator/accumulator/lib.rs index 97e56d57ef4..fae1482a9a6 100644 --- a/examples/delegator/accumulator/lib.rs +++ b/examples/delegator/accumulator/lib.rs @@ -25,10 +25,7 @@ pub mod accumulator { /// Mutates the internal value. #[ink(message)] pub fn inc(&mut self, by: i32) { - println!("acc::inc invoked with {} by {:?} myself {:?}", - by, self.env().caller(), self.env().account_id()); self.value += by; - panic!("stack tract"); } /// Returns the current state. diff --git a/examples/delegator/adder/lib.rs b/examples/delegator/adder/lib.rs index fb50507cf68..e87767f38dd 100644 --- a/examples/delegator/adder/lib.rs +++ b/examples/delegator/adder/lib.rs @@ -28,8 +28,6 @@ mod adder { /// Increases the `accumulator` value by some amount. #[ink(message)] pub fn inc(&mut self, by: i32) { - println!("adder::inc invoked with {} by {:?} myself {:?}", - by, self.env().caller(), self.env().account_id()); self.accumulator.inc(by) } } @@ -59,23 +57,14 @@ mod test { .instantiate() .expect("failed at instantiating the `AccumulatorRef` contract"); let mut adder = AdderRef::new(acc.clone()) - .code_hash(hash1.clone()) + .code_hash(hash2.clone()) .endowment(0) .salt_bytes([0u8; 0]) .instantiate() .expect("failed at instantiating the `AdderRef` contract"); - print_env("1"); assert_eq!(acc.get(), 0); - print_env("2"); adder.inc(1); assert_eq!(acc.get(), 1); } - - fn print_env(prefix: &str) { - use ink_lang::codegen::StaticEnv; - use super::*; - println!("[{}] outer env caller = {:?} callee = {:?}", - prefix, Adder::env().caller(), Adder::env().account_id()); - } } From 26deeed5e7ac6c023c6197b367788961bde51a8f Mon Sep 17 00:00:00 2001 From: h4x3rotab Date: Wed, 17 Aug 2022 21:21:01 +0000 Subject: [PATCH 4/4] clean up --- crates/env/src/engine/off_chain/impls.rs | 5 ++++- crates/env/src/engine/off_chain/mod.rs | 20 -------------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index cdec8acf295..a1f2c96a5a1 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -569,7 +569,10 @@ impl TypedEnvBackend for EnvInstance { where E: Environment, { - EnvInstance::caller_is_origin(self) + let origin = self.stack.origin(); + let ctx = self.stack.peek(); + assert!(ctx.level > 0, "should never reach when there's no running contract"); + ctx.caller.expect("contract has caller; qed.") == origin } fn code_hash(&mut self, account: &E::AccountId) -> Result diff --git a/crates/env/src/engine/off_chain/mod.rs b/crates/env/src/engine/off_chain/mod.rs index 9ae3df89aa2..4df553732b5 100644 --- a/crates/env/src/engine/off_chain/mod.rs +++ b/crates/env/src/engine/off_chain/mod.rs @@ -61,26 +61,6 @@ impl EnvInstance { ctx } - pub fn deploy(&mut self, account: &crate::AccountId, input: Vec) -> crate::Result<()> { - self.push_frame(account, input.clone()); - let (deploy, _call) = self.contracts.entrypoints(account) - .ok_or(Error::NotCallable)?; - deploy(); - self.pop_frame(); - // Read OUTPUT - // what if revert? - // scale::Decode::decode(&mut &input[..]) - // .map_err(|err| Error::Decode(err)) - Ok(()) - } - - pub fn caller_is_origin(&self) -> bool { - let origin = self.stack.origin(); - let ctx = self.stack.peek(); - assert!(ctx.level > 0, "should never reach when there's no running contract"); - ctx.caller.expect("contract has caller; qed.") == origin - } - } impl OnInstance for EnvInstance {