Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reworked message dispatch logic. Moved common parts upper. #999

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions crates/env/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,17 @@ where
})
}

/// Returns the buffer back to the caller of the executed contract.
///
/// # Note
///
/// This function stops the execution of the contract immediately.
pub fn return_value_scoped(return_flags: ReturnFlags, return_value: &[u8]) -> ! {
<EnvInstance as OnInstance>::on_instance(|instance| {
EnvBackend::return_value_scoped(instance, return_flags, return_value)
})
}

/// Returns a random hash seed and the block number since which it was determinable
/// by chain observers.
///
Expand Down
11 changes: 11 additions & 0 deletions crates/env/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,17 @@ pub trait EnvBackend {
where
R: scale::Encode;

/// Returns the buffer to the caller of the executed contract.
///
/// # Note
///
/// Calling this method will end contract execution immediately.
/// It will return the given return value back to its caller.
///
/// The `flags` parameter can be used to revert the state changes of the
/// entire execution if necessary.
fn return_value_scoped(&mut self, flags: ReturnFlags, return_value: &[u8]) -> !;

/// Emit a custom debug message.
///
/// The message is appended to the debug buffer which is then supplied to the calling RPC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! Utilities to encode value into buffer

/// A static buffer with 16 kB of capacity.
pub struct StaticBuffer {
/// The static buffer with a total capacity of 16 kB.
Expand Down Expand Up @@ -170,7 +172,7 @@ impl<'a> ScopedBuffer<'a> {
/// Appends the encoding of `value` to the scoped buffer.
///
/// Does not return the buffer immediately so that other values can be appended
/// afterwards. The [`take_appended`] method shall be used to return the buffer
/// afterwards. The `take_appended` method shall be used to return the buffer
/// that includes all appended encodings as a single buffer.
pub fn append_encoded<T>(&mut self, value: &T)
where
Expand All @@ -185,7 +187,7 @@ impl<'a> ScopedBuffer<'a> {
let _ = core::mem::replace(&mut self.buffer, buffer);
}

/// Returns the buffer containing all encodings appended via [`append_encoded`]
/// Returns the buffer containing all encodings appended via `append_encoded`
/// in a single byte buffer.
pub fn take_appended(&mut self) -> &'a mut [u8] {
debug_assert_ne!(self.offset, 0);
Expand Down
6 changes: 6 additions & 0 deletions crates/env/src/engine/experimental_off_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ impl EnvBackend for EnvInstance {
)
}

fn return_value_scoped(&mut self, _flags: ReturnFlags, _return_value: &[u8]) -> ! {
unimplemented!(
"the experimental off chain env does not implement `seal_return_value`"
)
}

fn debug_message(&mut self, message: &str) {
self.engine.debug_message(message)
}
Expand Down
6 changes: 6 additions & 0 deletions crates/env/src/engine/off_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ impl EnvBackend for EnvInstance {
std::process::exit(flags.into_u32() as i32)
}

fn return_value_scoped(&mut self, flags: ReturnFlags, return_value: &[u8]) -> ! {
let ctx = self.exec_context_mut().expect(UNINITIALIZED_EXEC_CONTEXT);
ctx.output = Some(return_value.to_vec());
std::process::exit(flags.into_u32() as i32)
}

fn debug_message(&mut self, message: &str) {
self.debug_buf.debug_message(message)
}
Expand Down
4 changes: 4 additions & 0 deletions crates/env/src/engine/on_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ impl EnvBackend for EnvInstance {
ext::return_value(flags, enc_return_value);
}

fn return_value_scoped(&mut self, flags: ReturnFlags, return_value: &[u8]) -> ! {
ext::return_value(flags, return_value);
}

fn debug_message(&mut self, content: &str) {
ext::debug_message(content)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/env/src/engine/on_chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

mod buffer;
mod ext;
mod impls;

Expand All @@ -24,6 +23,7 @@ use self::{
ext::Error,
};
use super::OnInstance;
use crate::buffer;

/// The on-chain environment.
pub struct EnvInstance {
Expand Down
1 change: 1 addition & 0 deletions crates/env/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ extern crate ink_allocator;
mod api;
mod arithmetic;
mod backend;
pub mod buffer;
pub mod call;
pub mod chain_extension;
mod engine;
Expand Down
68 changes: 45 additions & 23 deletions crates/lang/codegen/src/generator/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,10 +650,9 @@ impl Dispatch<'_> {
}>>::IDS[#index]
}>>::Output
);
let accepts_payment = quote_spanned!(message_span=>
false ||
!#any_message_accept_payment ||
<#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{
let deny_payment = quote_spanned!(message_span=>
#any_message_accept_payment &&
!<#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{
<#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{
<#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES
}>>::IDS[#index]
Expand All @@ -666,34 +665,31 @@ impl Dispatch<'_> {
}>>::IDS[#index]
}>>::MUTATES
);
let is_dynamic_storage_allocation_enabled = self
.contract
.config()
.is_dynamic_storage_allocator_enabled();
quote_spanned!(message_span=>
Self::#message_ident(input) => {
let config = ::ink_lang::codegen::ExecuteMessageConfig {
payable: #accepts_payment,
mutates: #mutates_storage,
dynamic_storage_alloc: #is_dynamic_storage_allocation_enabled,
};
let mut contract: ::core::mem::ManuallyDrop<#storage_ident> =
::core::mem::ManuallyDrop::new(
::ink_lang::codegen::initiate_message::<#storage_ident>(config)?
);
mutates = #mutates_storage;

if #deny_payment {
::ink_lang::codegen::deny_payment::<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env>()?;
}
let result: #message_output = #message_callable(&mut contract, input);
let failure = ::ink_lang::is_result_type!(#message_output)
&& ::ink_lang::is_result_err!(result);
::ink_lang::codegen::finalize_message::<#storage_ident, #message_output>(
!failure,
&contract,
config,
&result,
)?;
enc_return_value = scoped_buffer.take_encoded(&result);

if failure {
// if result is error it stops execution and returns an error to caller
::ink_env::return_value_scoped(::ink_env::ReturnFlags::default().set_reverted(true), enc_return_value);
}

::core::result::Result::Ok(())
}
)
});
let is_dynamic_storage_allocation_enabled = self
.contract
.config()
.is_dynamic_storage_allocator_enabled();

quote_spanned!(span=>
const _: () = {
Expand Down Expand Up @@ -733,9 +729,35 @@ impl Dispatch<'_> {
impl ::ink_lang::reflect::ExecuteDispatchable for __ink_MessageDecoder {
#[allow(clippy::nonminimal_bool)]
fn execute_dispatchable(self) -> ::core::result::Result<(), ::ink_lang::reflect::DispatchError> {
use ::core::convert::From;
use ::core::default::Default;

if #is_dynamic_storage_allocation_enabled {
::ink_storage::alloc::initialize(::ink_storage::alloc::ContractPhase::Call);
}

const CAPACITY: usize = 1 << 14;
let mut local_buffer: [u8; CAPACITY] = [0; CAPACITY];
let mut scoped_buffer = ::ink_env::buffer::ScopedBuffer::from(&mut local_buffer[..]);
let enc_return_value;

let root_key = <#storage_ident as ::ink_lang::codegen::ContractRootKey>::ROOT_KEY;
let mut contract: ::core::mem::ManuallyDrop<#storage_ident> =
::core::mem::ManuallyDrop::new(
::ink_storage::traits::pull_spread_root::<#storage_ident>(&root_key)
);
let mutates;
match self {
#( #message_execute ),*
}?;

if mutates {
::ink_storage::traits::push_spread_root::<#storage_ident>(&contract, &root_key);
}
if #is_dynamic_storage_allocation_enabled {
::ink_storage::alloc::finalize();
}
::ink_env::return_value_scoped(::ink_env::ReturnFlags::default(), enc_return_value);
}
}

Expand Down
101 changes: 1 addition & 100 deletions crates/lang/src/codegen/dispatch/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::reflect::{
ContractEnv,
DispatchError,
};
use crate::reflect::DispatchError;
use core::{
any::TypeId,
convert::Infallible,
mem::ManuallyDrop,
};
Expand All @@ -33,7 +29,6 @@ use ink_storage::{
alloc,
alloc::ContractPhase,
traits::{
pull_spread_root,
push_spread_root,
SpreadAllocate,
SpreadLayout,
Expand Down Expand Up @@ -300,97 +295,3 @@ pub struct ExecuteMessageConfig {
/// Authors can enable it via `#[ink::contract(dynamic_storage_allocator = true)]`.
pub dynamic_storage_alloc: bool,
}

/// Initiates an ink! message call with the given configuration.
///
/// Returns the contract state pulled from the root storage region upon success.
///
/// # Note
///
/// This work around that splits executing an ink! message into initiate
/// and finalize phases was needed due to the fact that `is_result_type`
/// and `is_result_err` macros do not work in generic contexts.
#[inline]
pub fn initiate_message<Contract>(
config: ExecuteMessageConfig,
) -> Result<Contract, DispatchError>
where
Contract: SpreadLayout + ContractEnv,
{
if !config.payable {
deny_payment::<<Contract as ContractEnv>::Env>()?;
}
if config.dynamic_storage_alloc {
alloc::initialize(ContractPhase::Call);
}
let root_key = Key::from([0x00; 32]);
let contract = pull_spread_root::<Contract>(&root_key);
Ok(contract)
}

/// Finalizes an ink! message call with the given configuration.
///
/// This dispatches into fallible and infallible message finalization
/// depending on the given `success` state.
///
/// - If the message call was successful the return value is simply returned
/// and cached storage is pushed back to the contract storage.
/// - If the message call failed the return value result is returned instead
/// and the transaction is signalled to be reverted.
///
/// # Note
///
/// This work around that splits executing an ink! message into initiate
/// and finalize phases was needed due to the fact that `is_result_type`
/// and `is_result_err` macros do not work in generic contexts.
#[inline]
pub fn finalize_message<Contract, R>(
success: bool,
contract: &Contract,
config: ExecuteMessageConfig,
result: &R,
) -> Result<(), DispatchError>
where
Contract: SpreadLayout,
R: scale::Encode + 'static,
{
if success {
finalize_infallible_message(contract, config, result)
} else {
finalize_fallible_message(result)
}
}

#[inline]
fn finalize_infallible_message<Contract, R>(
contract: &Contract,
config: ExecuteMessageConfig,
result: &R,
) -> Result<(), DispatchError>
where
Contract: SpreadLayout,
R: scale::Encode + 'static,
{
if config.mutates {
let root_key = Key::from([0x00; 32]);
push_spread_root::<Contract>(contract, &root_key);
}
if config.dynamic_storage_alloc {
alloc::finalize();
}
if TypeId::of::<R>() != TypeId::of::<()>() {
// In case the return type is `()` we do not return a value.
ink_env::return_value::<R>(ReturnFlags::default(), result)
}
Ok(())
}

#[inline]
fn finalize_fallible_message<R>(result: &R) -> !
where
R: scale::Encode + 'static,
{
// There is no need to push back the intermediate results of the
// contract since the transaction is going to be reverted.
ink_env::return_value::<R>(ReturnFlags::default().set_reverted(true), result)
}
2 changes: 0 additions & 2 deletions crates/lang/src/codegen/dispatch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ pub use self::{
execution::{
deny_payment,
execute_constructor,
finalize_message,
initialize_contract,
initiate_message,
ContractRootKey,
ExecuteConstructorConfig,
ExecuteMessageConfig,
Expand Down
2 changes: 0 additions & 2 deletions crates/lang/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ pub use self::{
dispatch::{
deny_payment,
execute_constructor,
finalize_message,
initialize_contract,
initiate_message,
ContractCallBuilder,
ContractRootKey,
DispatchInput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ error[E0277]: the trait bound `NonCodecType: WrapperTypeDecode` is not satisfied
|
= note: required because of the requirements on the impl of `parity_scale_codec::Decode` for `NonCodecType`
note: required by a bound in `DispatchInput`
--> src/codegen/dispatch/type_check.rs:41:8
--> src/codegen/dispatch/type_check.rs
|
41 | T: scale::Decode + 'static;
| T: scale::Decode + 'static;
| ^^^^^^^^^^^^^ required by this bound in `DispatchInput`

error[E0277]: the trait bound `NonCodecType: WrapperTypeDecode` is not satisfied
Expand Down
Loading