Feature/frame interface
Closes paritytech/polkadot-sdk#215
Idea
The idea is to have a single entry point for interface like calls. This entry point will be the pallet-interface. The pallet itself is just forwarding calls to an associated type Interface that implements trait UnfilteredDispatchable.
In order to make interfaces re-usable across chains:
- all chains MUST locate the
pallet-interfaceat the same index - all chains MUST locate the actual interfaces at the same index in the
enum InterfaceCallthat is aggregating the interfaces- e.g.
Pip20is agreed on to live at index20→pip20::Callshould be located at index20in theenum InterfaceCall
- e.g.
- interfaces MUST use the
struct Select<From, To>- the struct allows to derive a chain native type from a different type. Like
trait Convertis doing for a lot of things in XCM FromMUST be the same type for all chains- the struct decodes to
From(i.e. it is a transparent wrapper aroundFrom)
- the struct allows to derive a chain native type from a different type. Like
→ this allows to
- decode interface calls for all chains in the same manner
- encode interface calls for all chains in the same manner
Open Questions
- Do you people agree with the approach?
- Do you think it is worth adding?
The interface macro and expansions
Definition of an Interface
The following example definition showcases how an interface would be defined.
A detailed one
#[frame_support::interface]
pub mod pip20 {
use frame_support::{
dispatch::DispatchResult,
interface::{CallResult, Select, Selector, SelectorResult, ViewResult},
Parameter,
};
use sp_core::H256;
use sp_runtime::traits::Member;
pub type CurrencySelectable = H256;
pub type AccountIdSelectable = [u8; 32];
pub type BalanceSelectable = u128;
#[interface::definition]
pub trait Pip20: frame_system::Config {
/// A means for converting between from a [u8; 32] to the native chains account id.
type SelectAccount: Selector<
Selectable = AccountIdSelectable,
Selected = Self::AccountId,
>;
/// The chains native currency type.
type Currency: Parameter + Member;
/// A means for converting between from a `H256` to the chains native currency.
type SelectCurrency: Selector<
Selectable = CurrencySelectable,
Selected = Self::Currency,
>;
/// The chains native balance type.
type Balance: Parameter + Member;
/// A means for converting between from a u128 to the chains native balance.
type SelectBalance: Selector<Selectable = BalanceSelectable, Selected = Self::Balance>;
#[interface::view]
#[interface::view_index(0)]
fn free_balance(
currency: Select<Self::SelectCurrency>,
who: Select<Self::SelectAccount>,
) -> ViewResult<BalanceSelectable>;
#[interface::view]
#[interface::view_index(1)]
fn balances(
who: Select<Self::SelectAccount>,
) -> ViewResult<Vec<(CurrencySelectable, BalanceSelectable)>>;
#[interface::call]
#[interface::call_index(0)]
#[interface::weight(0)]
fn transfer(
origin: Self::RuntimeOrigin,
currency: Select<Self::SelectCurrency>,
recv: Select<Self::SelectAccount>,
amount: Select<Self::SelectBalance>,
) -> CallResult;
#[interface::call]
#[interface::call_index(3)]
#[interface::weight(0)]
fn burn(
origin: Self::RuntimeOrigin,
currency: Select<Self::SelectCurrency>,
from: Select<Self::SelectAccount>,
amount: Select<Self::SelectBalance>,
) -> CallResult;
#[interface::call]
#[interface::call_index(1)]
#[interface::weight(0)]
fn approve(
origin: Self::RuntimeOrigin,
currency: Select<Self::SelectCurrency>,
recv: Select<Self::SelectAccount>,
amount: Select<Self::SelectBalance>,
) -> CallResult;
}
}
A smaller one
#[frame_support::interface]
pub mod pip42 {
use frame_support::interface;
use frame_support::interface::CallResult;
use sp_core::Get;
use sp_runtime::BoundedVec;
#[interface::definition]
pub trait Pip42: frame_system::Config {
type MaxRemark: Get<u32>;
#[interface::call]
#[interface::call_index(0)]
#[interface::weight(0)]
fn remark(
origin: Self::RuntimeOrigin,
bytes: BoundedVec<u8, Self::MaxRemark>,
) -> CallResult;
}
}
The macro expands the module into
- the trait itself without altering it
- an
enum Call- similar to theCallof pallets used for dispatching - an
enum View- the entry points for look-ups that can be called via a runtime API
enum Call
Is mostly identical with the enum Call of a normal pallet. With the most notable difference, that methods are based on the trait of the interface and implemented directly by the Runtime.
enum View
Is mostly identical with the enum Call of a normal pallet. As for the enum Call that methods are based on the trait of the interface and implemented directly by the Runtime. The biggest difference is that these methods do not change some state, as they should always be executed outside of block construction without persisting state changes.
Runtime API
The runtime api defined in frame-support::interface.
sp_api::decl_runtime_apis! {
pub trait Interface<View>
where View: sp_api::Encode + frame_support::interface::View
{
fn view(view: View) -> ViewResult<Vec<u8>>;
}
}
Runtime Level
Given the above interface at the runtime level one would define two additional enums that represent the interfaces a runtime exposes. It is of course required for the Runtime to implement the trait Pip20 to make this compile.
enum InterfaceCall
The enum aggregating all call interfaces for a runtime. Passed to the pallet-interface as the associated type Interface.
E.g.
#[frame_support::call_entry(Runtime)]
pub enum InterfaceCall {
#[call_entry::index(20)]
Pip20(pip20::Call<Runtime>),
}
enum InterfaceView
The enum aggregating all view interfaces for a runtime. Will be used in the runtime api.
E.g.
#[frame_support::view_entry]
pub enum InterfaceView {
#[view_entry::index(20)]
Pip20(pip20::View<Runtime>),
}
Runtime API
impl_runtime_apis! {
impl frame_supporti::interface::Interface<InterfaceView> for Runtime {
fn view(view: InterfaceView) -> ViewResult<Vec<u8>> {
view.view()
}
}
}
Tasks
- [x] Parse interface definition - the trait
- [x] Expand interface
enum Call - [x] Expand interface
enum View - [x] Parse and expand
enum CallEntry - [x] Parse and expand
enum ViewEntry - [ ] (Maybe) make runtime-api accept default imlementation
- [ ] (Maybe) define default index for an interface and trigger a compilation warning if it is positioned at a different index in the
enum InterfaceCall - [ ] Clean up
- [ ] Tests
- [ ] Docs
@bkchr @kianenigma Here is the WIP state. Took me some time, but this was my bedtime project and I forgot about it from time to time so sorry for the delay.
I wanted to create this WIP PR already to get feedback on the direction.
The CI pipeline was cancelled due to failure one of the required jobs. Job name: cargo-check-benches Logs: https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/3121406
Ping @bkchr and @kianenigma ^^
Hey, is anyone still working on this? Due to the inactivity this issue has been automatically marked as stale. It will be closed if no further activity occurs. Thank you for your contributions.