Unverified Commit 59a7f5ed authored by Hero Bird's avatar Hero Bird Committed by GitHub
Browse files

Implement ink! chain extensions (#592)



* add new tests for #[ink(namespace = ..)] and #[ink(selector = ..)] attrs

* [lang] implement new errors for ink! attributes: namespace, selector

* add #[ink(extension = N: u32)] attribute

* apply rustfmt

* [lang] add initial implementation of chain extension proc. macro

* [lang] rename AttributeArg -> AttributeFrag

* rename AttributeFrag::kind field to arg

* rename AttributeArgKind -> AttributeArg

Also introduce a new AttributeArgKind type and AttributeArg::kind method.

* [lang] apply rustfmt

* add Display impl for AttributeArgKind

* [lang] use AttributeArgKind in sanitize_attributes and ensure_first methods

* [lang/ir] improve error reporting for duplicate ink! attributes

* [lang/ir] refactoring and improvements to chain extension IR impl

* [lang/ir] add tests for IR implementation of chain extensions

* [lang/ir] remove some code dupe

* [lang/ir] add getters to ChainExtension

* [lang/ir] add re-exports for ChainExtension definitions

* [lang/codegen] add initial impl for ChainExtensions

* [env] add ChainExtension assoc type to the Environment trait

* [lang] initial impl to make it possible to use chain extension syntax

* [env] improvements to chain extensions API

Also removes the unstable crate feature guarding the feature.

* [lang/macro] add UI test for chain extension

* [lang/macro] register new UI test

* [lang/codegen] fix some codegen bugs with new chain extension feature

* [lang, env] apply rustfmt

* [lang/ir] apply clippy suggestions and fix outdated error messages

* [lang/ir] fix some tests and incorrect error messages

* [examples] apply clippy suggestion

* add ErrorCode to ink! chain extension trait

* add FromStatusCode trait to ink_lang

* add #[ink(expect_output)] and #[ink(expect_ok)] attributes

Also add their handling for the #[ink::chain_extension] proc. macro.

* add ink_lang::IsResultType trait

* add ChainExtensionMethodInputs iterator

* hide docs for IsResultType trait

* add some docs to chain extension codegen

* implement expect_ok and expect_output where bounds in codegen

* apply code review suggestions by (cmichi)

* add ReturnCode::into_u32 getter

* change return type of ext::call_chain_extension: RetCode -> u32

Chain extensions do not follow the general scheme of error return code because they have their own set of errors that is custom defined by the author of the chain extension.

* [lang] add ChainExtension trait

Not yet sure we need it.

* change EnvBackend::call_chain_extension signature

The new signature is much more low-level and supports all the different ways to call a chain extension method.
This commit already implements the chain extension call on the on-chain environment.
More work needs to be done to properly support this for the off-chain environment.

* remove old high-level API for calling chain extension methods

* [env] add new high-level module to handle chain extension calls

* rename ChainExtensionMethodInstance -> ChainExtensionMethod

* [env] add doc examples to chain extension method API

* apply rustfmt

* [env] clippy: allow complex type in ChainExtensionMethod type state

* silence some warnings

* re-export IsResultType from ink_lang crate

* [lang] remove FromStatusCode trait

This trait already exists in the ink_env crate.

* [lang] update codegen for chain extensions

Not yet done but close.

* [lang] update simple chain extension test

Compiles now again.

* apply rustfmt

* update some license headers

* [env] fix where bound

* extend chain extension test case to cover all cases

* add messages for read_small, access and unlock_access

* fix some bugs with chain extension method signatures in test

* fix some bugs and docs in chain extension test

* fix test

* update quickcheck to 1.0

* [env] implement chain extension calling for off-chain env

The new interface is much more lower-level and it is still impossible to use chain state from within the chain extension call. The latter must be fixed as soon as the off-chain environment receives its rework.

* apply rustfmt

* add docs to #[ink::chain_extension] proc. macro

* rename env_types -> env everywhere

* make doc examples for #[ink::chain_extension] compile

* add yet another technical limitation

* make link to simple.rs test case more stable (point to revision)

* fix docs (code suggestion)
Co-authored-by: Michael Müller's avatarMichael Müller <michi@parity.io>

* fix docs (code suggestion)
Co-authored-by: Michael Müller's avatarMichael Müller <michi@parity.io>

* fix docs (code suggestion)
Co-authored-by: Michael Müller's avatarMichael Müller <michi@parity.io>

* fix docs (code suggestion)
Co-authored-by: Michael Müller's avatarMichael Müller <michi@parity.io>

* add dev. note to Backend::call_chain_extension

* fix docs example (code suggestion)
Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* [env] chain extension: make doc examples/tests compile

* fix some doc comments according to review suggestions

* review suggestion: telling -> meaning

* fix docs (review suggestion)
Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* fix docs (review suggestion)
Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* improve docs (review suggestion)
Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* rename chain extension test file

* analyse_error_code add doc comments for possible errors

* rename expect_ok -> returns_result and expect_output -> handle_status

Some examples and docs might be invalidated or outdated.

* [lang/ir] improve error reporting for ink! namespace attribute with missing parameter

* [lang/*] implement new handle_status and returns_result attributes

* [env] apply rustfmt
Co-authored-by: Michael Müller's avatarMichael Müller <michi@parity.io>
Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>
parent abd5cf14
Pipeline #119992 failed with stages
in 8 minutes and 13 seconds
......@@ -60,4 +60,3 @@ std = [
"sha3",
"blake2",
]
ink-unstable-chain-extensions = []
......@@ -436,27 +436,6 @@ where
})
}
/// Calls the chain extension with the given ID and inputs.
///
/// Returns the given output type.
///
/// # Errors
///
/// - If the given function ID does not exist in the runtime.
/// - If the given inputs cannot be properly decoded by the runtime.
/// - If the given output type cannot be properly decoded by the contract.
/// - If some chain extension specific conditions are not met.
#[cfg(feature = "ink-unstable-chain-extensions")]
pub fn call_chain_extension<I, O>(func_id: u32, input: &I) -> Result<O>
where
I: scale::Codec + 'static,
O: scale::Codec + 'static,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
EnvBackend::call_chain_extension(instance, func_id, input)
})
}
/// Returns the execution input to the executed contract and decodes it as `T`.
///
/// # Note
......
......@@ -128,7 +128,7 @@ pub trait EnvBackend {
H: CryptoHash,
T: scale::Encode;
/// Calls the chain extension with the given ID and inputs.
/// Low-level interface to call a chain extension method.
///
/// Returns the output of the chain extension of the specified type.
///
......@@ -138,11 +138,29 @@ pub trait EnvBackend {
/// - If the inputs had an unexpected encoding.
/// - If the output could not be properly decoded.
/// - If some extension specific condition has not been met.
#[cfg(feature = "ink-unstable-chain-extensions")]
fn call_chain_extension<I, O>(&mut self, func_id: u32, input: &I) -> Result<O>
///
/// # Dev. Note
///
/// A valid implementation applies the `status_to_result` closure on
/// the status code returned by the actual call to the chain extension
/// method.
/// Only if the closure finds that the given status code indicates a
/// successful call to the chain extension method is the resulting
/// output buffer passed to the `decode_to_result` closure, in order to
/// drive the decoding and error management process from the outside.
fn call_chain_extension<I, T, E, ErrorCode, F, D>(
&mut self,
func_id: u32,
input: &I,
status_to_result: F,
decode_to_result: D,
) -> ::core::result::Result<T, E>
where
I: scale::Codec + 'static,
O: scale::Codec + 'static;
I: scale::Encode,
T: scale::Decode,
E: From<ErrorCode>,
F: FnOnce(u32) -> ::core::result::Result<(), ErrorCode>,
D: FnOnce(&[u8]) -> ::core::result::Result<T, E>;
}
/// Environmental contract functionality.
......
......@@ -206,7 +206,7 @@ where
E: Environment,
{
CallBuilder {
env_types: Default::default(),
env: Default::default(),
callee: Default::default(),
gas_limit: Default::default(),
transferred_value: Default::default(),
......@@ -220,7 +220,7 @@ pub struct CallBuilder<E, Callee, GasLimit, TransferredValue, Args, RetType>
where
E: Environment,
{
env_types: PhantomData<fn() -> E>,
env: PhantomData<fn() -> E>,
/// The current parameters that have been built up so far.
callee: Callee,
gas_limit: GasLimit,
......@@ -242,7 +242,7 @@ where
) -> CallBuilder<E, Set<E::AccountId>, GasLimit, TransferredValue, Args, RetType>
{
CallBuilder {
env_types: Default::default(),
env: Default::default(),
callee: Set(callee),
gas_limit: self.gas_limit,
transferred_value: self.transferred_value,
......@@ -264,7 +264,7 @@ where
gas_limit: u64,
) -> CallBuilder<E, Callee, Set<u64>, TransferredValue, Args, RetType> {
CallBuilder {
env_types: Default::default(),
env: Default::default(),
callee: self.callee,
gas_limit: Set(gas_limit),
transferred_value: self.transferred_value,
......@@ -286,7 +286,7 @@ where
transferred_value: E::Balance,
) -> CallBuilder<E, Callee, GasLimit, Set<E::Balance>, Args, RetType> {
CallBuilder {
env_types: Default::default(),
env: Default::default(),
callee: self.callee,
gas_limit: self.gas_limit,
transferred_value: Set(transferred_value),
......@@ -327,7 +327,7 @@ where
R: IndicateReturnType,
{
CallBuilder {
env_types: Default::default(),
env: Default::default(),
callee: self.callee,
gas_limit: self.gas_limit,
transferred_value: self.transferred_value,
......@@ -362,7 +362,7 @@ where
RetType,
> {
CallBuilder {
env_types: Default::default(),
env: Default::default(),
callee: self.callee,
gas_limit: self.gas_limit,
transferred_value: self.transferred_value,
......
......@@ -113,7 +113,7 @@ pub struct CreateBuilder<E, CodeHash, GasLimit, Endowment, Args, R>
where
E: Environment,
{
env_types: PhantomData<fn() -> E>,
env: PhantomData<fn() -> E>,
code_hash: CodeHash,
gas_limit: GasLimit,
endowment: Endowment,
......@@ -181,7 +181,7 @@ where
R: FromAccountId<E>,
{
CreateBuilder {
env_types: Default::default(),
env: Default::default(),
code_hash: Default::default(),
gas_limit: Default::default(),
endowment: Default::default(),
......@@ -202,7 +202,7 @@ where
code_hash: E::Hash,
) -> CreateBuilder<E, Set<E::Hash>, GasLimit, Endowment, Args, R> {
CreateBuilder {
env_types: Default::default(),
env: Default::default(),
code_hash: Set(code_hash),
gas_limit: self.gas_limit,
endowment: self.endowment,
......@@ -224,7 +224,7 @@ where
gas_limit: u64,
) -> CreateBuilder<E, CodeHash, Set<u64>, Endowment, Args, R> {
CreateBuilder {
env_types: Default::default(),
env: Default::default(),
code_hash: self.code_hash,
gas_limit: Set(gas_limit),
endowment: self.endowment,
......@@ -246,7 +246,7 @@ where
endowment: E::Balance,
) -> CreateBuilder<E, CodeHash, GasLimit, Set<E::Balance>, Args, R> {
CreateBuilder {
env_types: Default::default(),
env: Default::default(),
code_hash: self.code_hash,
gas_limit: self.gas_limit,
endowment: Set(endowment),
......@@ -276,7 +276,7 @@ where
) -> CreateBuilder<E, CodeHash, GasLimit, Endowment, Set<ExecutionInput<Args>>, R>
{
CreateBuilder {
env_types: Default::default(),
env: Default::default(),
code_hash: self.code_hash,
gas_limit: self.gas_limit,
endowment: self.endowment,
......
// Copyright 2018-2021 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Definitions and utilities for calling chain extension methods.
//!
//! Users should not use these types and definitions directly but rather use the provided
//! `#[ink::chain_extension]` procedural macro defined in the `ink_lang` crate.
use crate::{
backend::EnvBackend,
engine::{
EnvInstance,
OnInstance,
},
};
use core::marker::PhantomData;
/// Implemented by error codes in order to construct them from status codes.
///
/// A status code is returned by calling an ink! chain extension method.
/// It is the `u32` return value.
///
/// The purpose of an `ErrorCode` type that implements this trait is to provide
/// more context information about the status of an ink! chain extension method call.
pub trait FromStatusCode: Sized {
/// Returns `Ok` if the status code for the called chain extension method is valid.
///
/// Returning `Ok` will query the output buffer of the call if the chain extension
/// method definition has a return value.
///
/// # Note
///
/// The convention is to use `0` as the only `raw` value that yields `Ok` whereas
/// every other value represents one error code. By convention this mapping should
/// never panic and therefore every `raw` value must map to either `Ok` or to a proper
/// `ErrorCode` variant.
fn from_status_code(status_code: u32) -> Result<(), Self>;
}
/// A concrete instance of a chain extension method.
///
/// This is a utility type used to drive the execution of a chain extension method call.
/// It has several different specializations of its `call` method for different ways to
/// manage error handling when calling a predefined chain extension method.
///
/// - `I` represents the input type of the chain extension method.
/// All tuple types that may act as input parameters for the chain extension method are valid.
/// Examples include `()`, `i32`, `(u8, [u8; 5], i32)`, etc.
/// - `O` represents the return (or output) type of the chain extension method.
/// Only `Result<T, E>` or `NoResult<O>` generic types are allowed for `O`.
/// The `Result<T, E>` type says that the chain extension method returns a `Result` type
/// whereas the `NoResult<O>` type says that the chain extension method returns a non-`Result` value
/// of type `O`.
/// - `ErrorCode` represents how the chain extension method handles the chain extension's error code.
/// Only `HandleErrorCode<E>` and `IgnoreErrorCode` types are allowed that each say to either properly
/// handle or ignore the chain extension's error code respectively.
///
/// The type states for type parameter `O` and `ErrorCode` represent 4 different states:
///
/// 1. The chain extension method makes use of the chain extension's error code: `HandleErrorCode(E)`
/// - **A:** The chain extension method returns a `Result<T, E>` type.
/// - **B:** The chain extension method returns a type `T` that is not a `Result` type: `NoResult<T>`
/// 2. The chain extension ignores the chain extension's error code: `IgnoreErrorCode`
/// - **A:** The chain extension method returns a `Result<T, E>` type.
/// - **B:** The chain extension method returns a type `T` that is not a `Result` type: `NoResult<T>`
#[derive(Debug)]
pub struct ChainExtensionMethod<I, O, ErrorCode> {
func_id: u32,
#[allow(clippy::type_complexity)]
state: PhantomData<fn() -> (I, O, ErrorCode)>,
}
impl ChainExtensionMethod<(), (), ()> {
/// Creates a new chain extension method instance.
#[inline(always)]
pub fn build(func_id: u32) -> Self {
Self {
func_id,
state: Default::default(),
}
}
}
impl<O, ErrorCode> ChainExtensionMethod<(), O, ErrorCode> {
/// Sets the input types of the chain extension method call to `I`.
///
/// # Note
///
/// `I` represents the input type of the chain extension method.
/// All tuple types that may act as input parameters for the chain extension method are valid.
/// Examples include `()`, `i32`, `(u8, [u8; 5], i32)`, etc.
#[inline(always)]
pub fn input<I>(self) -> ChainExtensionMethod<I, O, ErrorCode>
where
I: scale::Encode,
{
ChainExtensionMethod {
func_id: self.func_id,
state: Default::default(),
}
}
}
impl<I, ErrorCode> ChainExtensionMethod<I, (), ErrorCode> {
/// Sets the output type of the chain extension method call to `Result<T, E>`.
///
/// # Note
///
/// This indicates that the chain extension method return value might represent a failure.
#[inline(always)]
pub fn output_result<T, E>(self) -> ChainExtensionMethod<I, Result<T, E>, ErrorCode>
where
Result<T, E>: scale::Decode,
E: From<scale::Error>,
{
ChainExtensionMethod {
func_id: self.func_id,
state: Default::default(),
}
}
/// Sets the output type of the chain extension method call to `O`.
///
/// # Note
///
/// The set returned type `O` must not be of type `Result<T, E>`.
/// When using the `#[ink::chain_extension]` procedural macro to define
/// this chain extension method the above constraint is enforced at
/// compile time.
#[inline(always)]
pub fn output<O>(self) -> ChainExtensionMethod<I, state::NoResult<O>, ErrorCode>
where
O: scale::Decode,
{
ChainExtensionMethod {
func_id: self.func_id,
state: Default::default(),
}
}
}
impl<I, O> ChainExtensionMethod<I, O, ()> {
/// Makes the chain extension method call assume that the returned status code is always success.
///
/// # Note
///
/// This will avoid handling of failure status codes returned by the chain extension method call.
/// Use this only if you are sure that the chain extension method call will never return an error
/// code that represents failure.
///
/// The output of the chain extension method call is always decoded and returned in this case.
#[inline(always)]
pub fn ignore_error_code(self) -> ChainExtensionMethod<I, O, state::IgnoreErrorCode> {
ChainExtensionMethod {
func_id: self.func_id,
state: Default::default(),
}
}
/// Makes the chain exntesion method call handle the returned status code.
///
/// # Note
///
/// This will handle the returned status code and only loads and decodes the value
/// returned in the output of the chain extension method call in case of success.
#[inline(always)]
pub fn handle_error_code<ErrorCode>(
self,
) -> ChainExtensionMethod<I, O, state::HandleErrorCode<ErrorCode>>
where
ErrorCode: FromStatusCode,
{
ChainExtensionMethod {
func_id: self.func_id,
state: Default::default(),
}
}
}
/// Type states of the chain extension method instance.
pub mod state {
use core::marker::PhantomData;
/// Type state meaning that the chain extension method ignores the chain extension's error code.
#[derive(Debug)]
pub enum IgnoreErrorCode {}
/// Type state meaning that the chain extension method uses the chain extension's error code.
#[derive(Debug)]
pub struct HandleErrorCode<T> {
error_code: PhantomData<fn() -> T>,
}
/// Type state meaning that the chain extension method deliberately does not return a `Result` type.
///
/// Additionally this is enforced by the `#[ink::chain_extension]` proc. macro when used.
#[derive(Debug)]
pub struct NoResult<T> {
no_result: PhantomData<fn() -> T>,
}
}
impl<I, T, E, ErrorCode>
ChainExtensionMethod<I, Result<T, E>, state::HandleErrorCode<ErrorCode>>
where
I: scale::Encode,
T: scale::Decode,
E: scale::Decode + From<ErrorCode> + From<scale::Error>,
ErrorCode: FromStatusCode,
{
/// Calls the chain extension method for case 1A described [here].
///
/// [here]: [`ChainExtensionMethodInstance`]
///
/// # Errors
///
/// - If the called chain extension method returns a non-successful error code.
/// - If the `Result` return value of the called chain extension represents an error.
/// - If the `Result` return value cannot be SCALE decoded properly.
/// - If custom constraints specified by the called chain extension method are violated.
/// - These constraints are determined and defined by the author of the chain extension method.
///
/// # Example
///
/// Declares a chain extension method with the unique ID of 5 that requires a `bool` and an `i32`
/// as input parameters and returns a `Result<i32, MyError>` upon completion.
/// It will handle the shared error code from the chain extension.
/// The call is finally invoked with arguments `true` and `42` for the `bool` and `i32` input
/// parameter respectively.
///
/// ```should_panic
/// # // Panics because the off-chain environment has not
/// # // registered a chain extension method for the ID.
/// # use ink_env::chain_extension::{ChainExtensionMethod, FromStatusCode};
/// let result = ChainExtensionMethod::build(5)
/// .input::<(bool, i32)>()
/// .output_result::<i32, MyError>()
/// .handle_error_code::<MyErrorCode>()
/// .call(&(true, 42));
/// # #[derive(scale::Encode, scale::Decode)]
/// # pub struct MyError {}
/// # impl From<scale::Error> for MyError {
/// # fn from(_error: scale::Error) -> Self { Self {} }
/// # }
/// # impl From<MyErrorCode> for MyError {
/// # fn from(_error: MyErrorCode) -> Self { Self {} }
/// # }
/// # pub struct MyErrorCode {}
/// # impl FromStatusCode for MyErrorCode {
/// # fn from_status_code(status_code: u32) -> Result<(), Self> { Ok(()) }
/// # }
/// ```
#[inline(always)]
pub fn call(self, input: &I) -> Result<T, E> {
<EnvInstance as OnInstance>::on_instance(|instance| {
EnvBackend::call_chain_extension::<I, T, E, ErrorCode, _, _>(
instance,
self.func_id,
input,
ErrorCode::from_status_code,
|output| scale::Decode::decode(&mut &output[..]).map_err(Into::into),
)
})
}
}
impl<I, T, E> ChainExtensionMethod<I, Result<T, E>, state::IgnoreErrorCode>
where
I: scale::Encode,
T: scale::Decode,
E: scale::Decode + From<scale::Error>,
{
/// Calls the chain extension method for case 2A described [here].
///
/// [here]: [`ChainExtensionMethodInstance`]
///
/// # Errors
///
/// - If the `Result` return value of the called chain extension represents an error.
/// - If the `Result` return value cannot be SCALE decoded properly.
/// - If custom constraints specified by the called chain extension method are violated.
/// - These constraints are determined and defined by the author of the chain extension method.
///
/// # Example
///
/// Declares a chain extension method with the unique ID of 5 that requires a `bool` and an `i32`
/// as input parameters and returns a `Result<i32, MyError>` upon completion.
/// It will ignore the shared error code from the chain extension and assumes that the call succeeds.
/// The call is finally invoked with arguments `true` and `42` for the `bool` and `i32` input
/// parameter respectively.
///
/// ```should_panic
/// # // Panics because the off-chain environment has not
/// # // registered a chain extension method for the ID.
/// # use ink_env::chain_extension::{ChainExtensionMethod};
/// let result = ChainExtensionMethod::build(5)
/// .input::<(bool, i32)>()
/// .output_result::<i32, MyError>()
/// .ignore_error_code()
/// .call(&(true, 42));
/// # #[derive(scale::Encode, scale::Decode)]
/// # pub struct MyError {}
/// # impl From<scale::Error> for MyError {
/// # fn from(_error: scale::Error) -> Self { Self {} }
/// # }
/// ```
#[inline(always)]
pub fn call(self, input: &I) -> Result<T, E> {
<EnvInstance as OnInstance>::on_instance(|instance| {
EnvBackend::call_chain_extension::<I, T, E, E, _, _>(
instance,
self.func_id,
input,
|_status_code| Ok(()),
|output| scale::Decode::decode(&mut &output[..]).map_err(Into::into),
)
})
}
}
impl<I, O, ErrorCode>
ChainExtensionMethod<I, state::NoResult<O>, state::HandleErrorCode<ErrorCode>>
where
I: scale::Encode,
O: scale::Decode,
ErrorCode: FromStatusCode,
{
/// Calls the chain extension method for case 1B described [here].
///
/// [here]: [`ChainExtensionMethodInstance`]
///
/// # Errors
///
/// - If the called chain extension method returns a non-successful error code.
/// - If custom constraints specified by the called chain extension method are violated.
/// - These constraints are determined and defined by the author of the chain extension method.
///
/// # Panics
///
/// - If the return value cannot be SCALE decoded properly.
///
/// # Example
///
/// Declares a chain extension method with the unique ID of 5 that requires a `bool` and an `i32`
/// as input parameters and returns a `Result<i32, MyErrorCode>` upon completion.
/// It will handle the shared error code from the chain extension.
/// The call is finally invoked with arguments `true` and `42` for the `bool` and `i32` input
/// parameter respectively.
///
/// ```should_panic
/// # // Panics because the off-chain environment has not
/// # // registered a chain extension method for the ID.
/// # use ink_env::chain_extension::{ChainExtensionMethod, FromStatusCode};
/// let result = ChainExtensionMethod::build(5)
/// .input::<(bool, i32)>()
/// .output::<i32>()
/// .handle_error_code::<MyErrorCode>()
/// .call(&(true, 42));
/// # pub struct MyErrorCode {}
/// # impl FromStatusCode for MyErrorCode {
/// # fn from_status_code(status_code: u32) -> Result<(), Self> { Ok(()) }
/// # }
/// ```
#[inline(always)]
pub fn call(self, input: &I) -> Result<O, ErrorCode> {
<EnvInstance as OnInstance>::on_instance(|instance| {
EnvBackend::call_chain_extension::<I, O, ErrorCode, ErrorCode, _, _>(
instance,
self.func_id,
input,
ErrorCode::from_status_code,
|output| {
let decoded = <O as scale::Decode>::decode(&mut &output[..])
.expect("encountered error while decoding chain extension method call return value");
Ok(decoded)
},
)
})
}
}
impl<I, O> ChainExtensionMethod<I, state::NoResult<O>, state::IgnoreErrorCode>
where
I: scale::Encode,
O: scale::Decode,
{
/// Calls the chain extension method for case 2B described [here].
///
/// [here]: [`ChainExtensionMethodInstance`]
///
/// # Panics
///
/// - If the return value cannot be SCALE decoded properly.
///
/// # Example
///
/// Declares a chain extension method with the unique ID of 5 that requires a `bool` and an `i32`
/// as input parameters and returns a `Result<i32, MyErrorCode>` upon completion.
/// It will ignore the shared error code from the chain extension and assumes that the call succeeds.
/// The call is finally invoked with arguments `true` and `42` for the `bool` and `i32` input
/// parameter respectively.
///
/// ```should_panic