Skip to content

Commit

Permalink
Update seal_call to __unstable__ version (use-ink#960)
Browse files Browse the repository at this point in the history
* Update `seal_call` to `__unstable__` version

* Add `Debug` derive

* Make `cargo-spellcheck` happy

* Make `into_u32` take `self`

* Update release notes

* Fix crate docs link

* Use type-system encoded version of empty `ExecutionInput`

* Add `const` for getters and setters

* Shorten if-branches logic

* Update release notes

* Remove dead code

* Improve release notes
  • Loading branch information
Michael Müller authored and xgreenx committed Feb 8, 2022
1 parent 846ad52 commit 2dc377e
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 33 deletions.
1 change: 1 addition & 0 deletions .config/cargo_spellcheck.dic
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ multi
postfix
prefilled
recurse
reentrancy
refcount
scalability
scalable
Expand Down
29 changes: 16 additions & 13 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,36 @@
This is the 7th release candidate for ink! 3.0.

## Added

- The ink! codegen now heavily relies on static type information based on traits defined in `ink_lang`.
- Some of those traits and their carried information can be used for static reflection of ink!
smart contracts. Those types and traits reside in the new `ink_lang::reflect` module and is
publicly usable by ink! smart contract authors.

## Changed

- ink! Contract via `#[ink::contract]`:
- ink! smart contracts now always generated two contract types. Given `MyContract`:

- Upgraded to the unstable `seal_call` API ‒ [#960](https://github.com/paritytech/ink/pull/960).
- This API now enables control over the behavior of cross-contract calls, e.g. to forward/clone input,
enable tail calls and control reentrancy.
The crate documentation contains more details on the [`CallFlags`](https://paritytech.github.io/ink/ink_env/struct.CallFlags.html).
- **Note:** The default behavior of cross-contract calls now disallows reentering the calling contract.
- **Note:** In order to support this you currently have to enable the `unstable-interface` of
the `contracts` pallet, [like here](https://github.com/paritytech/substrate-contracts-node/blob/main/runtime/Cargo.toml#L104-L108).
- ink! contract definitions via `#[ink::contract]`:
- ink! smart contracts now generate two contract types. Given `MyContract`:
- `MyContract` will still be the storage struct.
However, it can now additionally be used as static dependency in other smart contracts.
Static dependencies can be envisioned as being directly embedded into a smart contract.
- `MyContractRef` is pretty much the same of what we had gotten with the old `ink-as-dependency`.
It is a typed thin-wrapper around an `AccountId` that is mirroring the ink! smart contract's API
and implemented traits.
- ink! Trait Definitions via `#[ink::trait_definition]`:
- ink! trait definitions via `#[ink::trait_definition]`:
- ink! trait definitions no longer can define trait constructors.
- ink! trait implementations now inherit `selector` and `payable` properties for trait messages.
- Now explicitly setting `selector` or `payable` property for an implemented ink! trait method
will only act as a guard that the set property is in fact the same as defined by the ink!
trait definition.
- Improve quite a few ink! specific compile errors:
- For example when using ink! messages and constructors that have inputs or outputs that cannot
be encoded or decoded using the SCALE codec.
- Improved some ink! specific compile errors:
- For example, when using ink! messages and constructors that have inputs or
outputs that cannot be encoded or decoded using the SCALE codec.
- Simplified selector computation for ink! trait methods.
- Now selectors are encoded as `blake2b({namespace}::{trait_identifier}::{message_identifier})[0..4]`.
If no `namespace` is set for the ink! trait definition then the formula is
Expand All @@ -37,7 +41,6 @@ This is the 7th release candidate for ink! 3.0.
definition and ink! trait message respectively.

## Fixed

- Contracts that are compiled as root (the default) now properly revert the transaction if a message
returned `Result::Err`.
- This does not apply to ink! smart contracts that are used as dependencies. Therefore it is still possible to match against a result return type for a called dependency.
Expand Down Expand Up @@ -72,11 +75,11 @@ scale = { package = "parity-scale-codec", version = "2", default-features = fals

### New metadata format

There are breaking changes to the metadata format in this release.
There are breaking changes to the metadata format in this release.

- Removes top level `metadataVersion` field from the contract metadata (https://github.com/paritytech/cargo-contract/pull/342/files).
- Introduces new top level versioned metadata [enum](https://github.com/paritytech/ink/blob/master/crates/metadata/src/lib.rs#L68).
- Upgrades to `scale-info` version `1.0` (https://github.com/paritytech/ink/pull/845).
- Introduces new top level versioned metadata [enum](https://github.com/paritytech/ink/blob/master/crates/metadata/src/lib.rs#L68).
- Upgrades to `scale-info` version `1.0` (https://github.com/paritytech/ink/pull/845).
- The previous supported version was `0.6`, so check release notes for all changes since then: https://github.com/paritytech/ink/pull/845
- One of the main changes to be aware of is the change to 0 based type lookup ids: https://github.com/paritytech/scale-info/pull/90

Expand Down
106 changes: 106 additions & 0 deletions crates/env/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,112 @@ impl ReturnFlags {
}
}

/// The flags used to change the behavior of a contract call.
#[derive(Copy, Clone, Debug, Default)]
pub struct CallFlags {
forward_input: bool,
clone_input: bool,
tail_call: bool,
allow_reentry: bool,
}

impl CallFlags {
/// Forwards the input for the current function to the callee.
///
/// # Note
///
/// A forwarding call will consume the current contracts input. Any attempt to
/// access the input after this call returns (e.g. by trying another forwarding call)
/// will lead to a contract revert.
/// Consider using [`Self::set_clone_input`] in order to preserve the input.
pub const fn set_forward_input(mut self, forward_input: bool) -> Self {
self.forward_input = forward_input;
self
}

/// Identical to [`Self::set_forward_input`] but without consuming the input.
///
/// This adds some additional weight costs to the call.
///
/// # Note
///
/// This implies [`Self::set_forward_input`] and takes precedence when both are set.
pub const fn set_clone_input(mut self, clone_input: bool) -> Self {
self.clone_input = clone_input;
self
}

/// Do not return from the call but rather return the result of the callee to the
/// callers caller.
///
/// # Note
///
/// This makes the current contract completely transparent to its caller by replacing
/// this contracts potential output with the callee ones. Any code after the contract
/// calls has been invoked can be safely considered unreachable.
pub const fn set_tail_call(mut self, tail_call: bool) -> Self {
self.tail_call = tail_call;
self
}

/// Allow the callee to reenter into the current contract.
///
/// Without this flag any reentrancy into the current contract that originates from
/// the callee (or any of its callees) is denied. This includes the first callee:
/// You cannot call into yourself with this flag set.
pub const fn set_allow_reentry(mut self, allow_reentry: bool) -> Self {
self.allow_reentry = allow_reentry;
self
}

/// Returns the underlying `u32` representation of the call flags.
///
/// This value is used to forward the call flag information to the
/// `contracts` pallet.
pub(crate) const fn into_u32(self) -> u32 {
self.forward_input as u32
| ((self.clone_input as u32) << 1)
| ((self.tail_call as u32) << 2)
| ((self.allow_reentry as u32) << 3)
}

/// Returns `true` if input forwarding is set.
///
/// # Note
///
/// See [`Self::set_forward_input`] for more information.
pub const fn forward_input(&self) -> bool {
self.forward_input
}

/// Returns `true` if input cloning is set.
///
/// # Note
///
/// See [`Self::set_clone_input`] for more information.
pub const fn clone_input(&self) -> bool {
self.clone_input
}

/// Returns `true` if the tail call property is set.
///
/// # Note
///
/// See [`Self::set_tail_call`] for more information.
pub const fn tail_call(&self) -> bool {
self.tail_call
}

/// Returns `true` if call reentry is allowed.
///
/// # Note
///
/// See [`Self::set_allow_reentry`] for more information.
pub const fn allow_reentry(&self) -> bool {
self.allow_reentry
}
}

/// Environmental contract functionality that does not require `Environment`.
pub trait EnvBackend {
/// Writes the value to the contract storage under the given key.
Expand Down
89 changes: 89 additions & 0 deletions crates/env/src/call/call_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

use crate::{
backend::CallFlags,
call::{
utils::{
EmptyArgumentList,
Expand All @@ -36,6 +37,8 @@ where
{
/// The account ID of the to-be-called smart contract.
callee: E::AccountId,
/// The flags used to change the behavior of a contract call.
call_flags: CallFlags,
/// The maximum gas costs allowed for the call.
gas_limit: u64,
/// The transferred value for the call.
Expand All @@ -56,6 +59,12 @@ where
&self.callee
}

/// Returns the call flags.
#[inline]
pub(crate) fn call_flags(&self) -> &CallFlags {
&self.call_flags
}

/// Returns the chosen gas limit for the called contract execution.
#[inline]
pub(crate) fn gas_limit(&self) -> u64 {
Expand Down Expand Up @@ -202,6 +211,7 @@ where
CallBuilder {
env: Default::default(),
callee: Default::default(),
call_flags: Default::default(),
gas_limit: Default::default(),
transferred_value: Default::default(),
exec_input: Default::default(),
Expand All @@ -217,6 +227,7 @@ where
env: PhantomData<fn() -> E>,
/// The current parameters that have been built up so far.
callee: Callee,
call_flags: CallFlags,
gas_limit: GasLimit,
transferred_value: TransferredValue,
exec_input: Args,
Expand All @@ -238,6 +249,30 @@ where
CallBuilder {
env: Default::default(),
callee: Set(callee),
call_flags: self.call_flags,
gas_limit: self.gas_limit,
transferred_value: self.transferred_value,
exec_input: self.exec_input,
return_type: self.return_type,
}
}
}

impl<E, Callee, GasLimit, TransferredValue, Args, RetType>
CallBuilder<E, Callee, GasLimit, TransferredValue, Args, RetType>
where
E: Environment,
{
/// The flags used to change the behavior of the contract call.
#[inline]
pub fn call_flags(
self,
call_flags: CallFlags,
) -> CallBuilder<E, Callee, GasLimit, TransferredValue, Args, RetType> {
CallBuilder {
env: Default::default(),
callee: self.callee,
call_flags,
gas_limit: self.gas_limit,
transferred_value: self.transferred_value,
exec_input: self.exec_input,
Expand All @@ -260,6 +295,7 @@ where
CallBuilder {
env: Default::default(),
callee: self.callee,
call_flags: self.call_flags,
gas_limit: Set(gas_limit),
transferred_value: self.transferred_value,
exec_input: self.exec_input,
Expand All @@ -282,6 +318,7 @@ where
CallBuilder {
env: Default::default(),
callee: self.callee,
call_flags: self.call_flags,
gas_limit: self.gas_limit,
transferred_value: Set(transferred_value),
exec_input: self.exec_input,
Expand Down Expand Up @@ -324,6 +361,7 @@ where
CallBuilder {
env: Default::default(),
callee: self.callee,
call_flags: self.call_flags,
gas_limit: self.gas_limit,
transferred_value: self.transferred_value,
exec_input: self.exec_input,
Expand Down Expand Up @@ -359,6 +397,7 @@ where
CallBuilder {
env: Default::default(),
callee: self.callee,
call_flags: self.call_flags,
gas_limit: self.gas_limit,
transferred_value: self.transferred_value,
exec_input: Set(exec_input),
Expand All @@ -385,6 +424,7 @@ where
pub fn params(self) -> CallParams<E, Args, RetType> {
CallParams {
callee: self.callee.value(),
call_flags: self.call_flags,
gas_limit: self.gas_limit.unwrap_or_else(|| 0),
transferred_value: self
.transferred_value
Expand All @@ -395,6 +435,35 @@ where
}
}

impl<E, GasLimit, TransferredValue, RetType>
CallBuilder<
E,
Set<E::AccountId>,
GasLimit,
TransferredValue,
Unset<ExecutionInput<EmptyArgumentList>>,
Unset<RetType>,
>
where
E: Environment,
GasLimit: Unwrap<Output = u64>,
TransferredValue: Unwrap<Output = E::Balance>,
{
/// Finalizes the call builder to call a function.
pub fn params(self) -> CallParams<E, EmptyArgumentList, ()> {
CallParams {
callee: self.callee.value(),
call_flags: self.call_flags,
gas_limit: self.gas_limit.unwrap_or_else(|| 0),
transferred_value: self
.transferred_value
.unwrap_or_else(|| E::Balance::from(0u32)),
_return_type: Default::default(),
exec_input: Default::default(),
}
}
}

impl<E, GasLimit, TransferredValue, Args>
CallBuilder<
E,
Expand All @@ -416,6 +485,26 @@ where
}
}

impl<E, GasLimit, TransferredValue>
CallBuilder<
E,
Set<E::AccountId>,
GasLimit,
TransferredValue,
Unset<ExecutionInput<EmptyArgumentList>>,
Unset<ReturnType<()>>,
>
where
E: Environment,
GasLimit: Unwrap<Output = u64>,
TransferredValue: Unwrap<Output = E::Balance>,
{
/// Invokes the cross-chain function call.
pub fn fire(self) -> Result<(), Error> {
self.params().invoke()
}
}

impl<E, GasLimit, TransferredValue, Args, R>
CallBuilder<
E,
Expand Down
6 changes: 3 additions & 3 deletions crates/env/src/call/execution_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use crate::call::Selector;

/// The input data for a smart contract execution.
#[derive(Debug)]
#[derive(Default, Debug)]
pub struct ExecutionInput<Args> {
/// The selector for the smart contract execution.
selector: Selector,
Expand Down Expand Up @@ -70,7 +70,7 @@ impl<'a, Head, Rest> ExecutionInput<ArgumentList<Argument<Head>, Rest>> {
/// arguments. The potentially heap allocating encoding is done right at the end
/// where we can leverage the static environmental buffer instead of allocating
/// heap memory.
#[derive(Debug)]
#[derive(Default, Debug)]
pub struct ArgumentList<Head, Rest> {
/// The first argument of the argument list.
head: Head,
Expand Down Expand Up @@ -99,7 +99,7 @@ impl<T> Argument<T> {
}

/// The end of an argument list.
#[derive(Debug)]
#[derive(Default, Debug)]
pub struct ArgumentListEnd;

/// An empty argument list.
Expand Down
Loading

0 comments on commit 2dc377e

Please sign in to comment.