Commit 64aa2225 authored by Hero Bird's avatar Hero Bird
Browse files

[pdsl_model] Initial commit for the medium-level abstractions

parent ed21f395
......@@ -2,6 +2,7 @@
members = [
"core",
"model",
# "derive",
]
exclude = [
......
[package]
name = "pdsl_model"
version = "0.1.0"
authors = ["Robin Freyler <robin@parity.io>", "Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0"
readme = "README.md"
# repository = "https://github.com/robbepop/substrate-contract"
# homepage = "https://github.com/robbepop/substrate-contract"
# documentation = "https://robbepop.github.io/pwasm-abi/substrate-contract/"
description = "[pDSL: Parity eDSL] Rust based eDSL for writing smart contracts for Substrate"
keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"]
categories = ["no-std", "embedded"]
include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"]
[dependencies]
parity-codec = { version = "2.2", default-features = false, features = ["derive", "full"] }
pdsl_core = { version = "0.1.0", path = "../core" }
either = { version = "1.5.0", default-features = false }
[features]
default = []
test-env = ["std", "pdsl_core/test-env"]
std = ["pdsl_core/std"]
# pDSL Model
Medium-level abstractions to write and represent Wasm smart contracts.
Greatly inspired by https://github.com/paritytech/fleetwood.
This is currently in a very rough research shape.
Do not use it and don't expect anything to work at the moment.
use crate::{
state::{
ContractState,
EmptyContractState,
},
msg::{
Message,
},
msg_handler::{
UnreachableMessageHandler,
MessageHandler,
MessageHandlerMut,
RawMessageHandler,
RawMessageHandlerMut,
},
};
use core::marker::PhantomData;
pub struct ContractDecl<
State,
HandlerChain,
> {
name: &'static str,
state: PhantomData<State>,
handlers: HandlerChain,
}
impl<State, HandlerChain> Clone for ContractDecl<State, HandlerChain>
where
HandlerChain: Clone,
{
fn clone(&self) -> Self {
Self {
name: self.name,
state: self.state,
handlers: self.handlers.clone(),
}
}
}
impl<State, HandlerChain> Copy for ContractDecl<State, HandlerChain>
where
HandlerChain: Copy,
{}
impl ContractDecl<EmptyContractState, UnreachableMessageHandler> {
/// Creates a new contract declaration with the given name.
pub fn new(name: &'static str) -> Self {
Self {
name,
state: PhantomData,
handlers: UnreachableMessageHandler,
}
}
/// Makes the contract declaration use the given state.
pub fn using_state<State>(self) -> ContractDecl<State, UnreachableMessageHandler> {
ContractDecl {
name: self.name,
state: PhantomData,
handlers: UnreachableMessageHandler,
}
}
}
impl<State, HandlerChain> ContractDecl<State, HandlerChain> {
/// Convenience method to append another message handler.
const fn append_msg_handler<MsgHandler>(self, handler: MsgHandler)
-> ContractDecl<State, (MsgHandler, HandlerChain)>
where
Self: Copy,
{
ContractDecl {
name: self.name,
state: PhantomData,
handlers: (handler, self.handlers)
}
}
/// Registers a read-only message handler.
///
/// # Note
///
/// Read-only message handlers do not mutate contract state.
pub const fn on_msg<Msg>(
self,
handler: RawMessageHandler<Msg, State>,
)
-> ContractDecl<State, (MessageHandler<Msg, State>, HandlerChain)>
where
Self: Copy,
Msg: Message,
State: ContractState,
{
self.append_msg_handler(MessageHandler::from_raw(handler))
}
/// Registers a mutable message handler.
///
/// # Note
///
/// Mutable message handlers may mutate contract state.
pub const fn on_msg_mut<Msg>(
self,
handler: RawMessageHandlerMut<Msg, State>,
)
-> ContractDecl<State, (MessageHandlerMut<Msg, State>, HandlerChain)>
where
Self: Copy,
Msg: Message,
State: ContractState,
{
self.append_msg_handler(MessageHandlerMut::from_raw(handler))
}
}
pub struct ExecutionEnv<S>{
pub state: S,
}
#![cfg_attr(not(feature = "std"), no_std)]
#![feature(const_fn)]
#![allow(unused)]
#[macro_use]
mod state;
#[macro_use]
mod msg;
mod contract;
mod exec_env;
mod msg_handler;
mod tests;
/// A message with an expected input type and output (result) type.
pub trait Message {
/// The expected input type, also known as parameter types.
type Input: parity_codec::Decode;
/// The output of the message, also known as return type.
type Output: parity_codec::Encode;
/// The name of the message.
///
/// # Note
///
/// This should be a valid Rust identifier.
const NAME: &'static str;
}
macro_rules! messages {
( $msg_name:ident ( $( $param_ty:ty ),* ) -> $ret_ty:ty; $($rest:tt)* ) => {
#[derive(Copy, Clone)]
struct $msg_name;
impl crate::msg::Message for $msg_name {
type Input = ($($param_ty),*);
type Output = $ret_ty;
const NAME: &'static str = stringify!($msg_name);
}
messages!($($rest)*);
};
( $msg_name:ident ( $( $param_ty:ty ),* ); $($rest:tt)* ) => {
messages!( $msg_name ( $( $param_ty ),* ) -> (); $($rest)* );
};
() => {};
}
use crate::{
msg::{
Message,
},
exec_env::{
ExecutionEnv,
},
state::{
ContractState,
},
};
use core::{
marker::PhantomData,
result::Result as CoreResult,
};
use parity_codec::Decode;
use either::Either;
/// A raw read-only message handler for the given message and state.
///
/// # Note
///
/// - Read-only message handlers cannot mutate contract state.
/// - Requires `Msg` to impl `Message` and `State` to impl `ContractState`.
pub type RawMessageHandler<Msg, State> =
fn(&ExecutionEnv<State>, <Msg as Message>::Input) -> <Msg as Message>::Output;
/// A raw mutable message handler for the given message and state.
///
/// # Note
///
/// - Mutable message handlers may mutate contract state.
/// - Requires `Msg` to impl `Message` and `State` to impl `ContractState`.
pub type RawMessageHandlerMut<Msg, State> =
fn(&mut ExecutionEnv<State>, <Msg as Message>::Input) -> <Msg as Message>::Output;
/// The raw data with which a contract is being called.
pub struct CallData(pub Vec<u8>);
impl CallData {
/// Returns the message handler selector part of this call data.
pub fn selector(&self) -> MessageHandlerSelector {
unimplemented!() // TODO: Specify and implement behaviour.
}
/// Returns the actual call data in binary format.
pub fn params(&self) -> &[u8] {
unimplemented!() // TODO: Specify and implement behaviour.
}
}
/// A hash to identify a called function.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct MessageHandlerSelector(pub u64);
/// A read-only message handler.
///
/// Read-only message handlers cannot mutate contract state.
pub struct MessageHandler<Msg, State>
where
Msg: Message,
State: ContractState,
{
/// Required in order to trick Rust into thinking that it actually owns a message.
///
/// However, in general message types are zero-sized-types (ZST).
msg_marker: PhantomData<Msg>,
/// The actual mutable handler for the message and state.
raw_handler: RawMessageHandler<Msg, State>,
}
impl<Msg, State> MessageHandler<Msg, State>
where
Msg: Message,
State: ContractState,
{
/// Returns the associated handler selector.
pub const fn selector() -> MessageHandlerSelector {
MessageHandlerSelector(0x0) // TODO: Specify and implement behaviour.
}
}
impl<Msg, State> Copy for MessageHandler<Msg, State>
where
Msg: Message,
State: ContractState,
{}
impl<Msg, State> Clone for MessageHandler<Msg, State>
where
Msg: Message,
State: ContractState,
{
fn clone(&self) -> Self {
Self {
msg_marker: self.msg_marker,
raw_handler: self.raw_handler,
}
}
}
impl<Msg, State> MessageHandler<Msg, State>
where
Msg: Message,
State: ContractState,
{
/// Constructs a message handler from its raw counterpart.
pub const fn from_raw(raw_handler: RawMessageHandler<Msg, State>) -> Self {
Self { msg_marker: PhantomData, raw_handler }
}
}
/// A mutable message handler.
///
/// Mutable message handlers may mutate contract state.
///
/// # Note
///
/// This is a thin wrapper around a raw message handler in order
/// to provide more type safety and better interfaces.
pub struct MessageHandlerMut<Msg, State>
where
Msg: Message,
State: ContractState,
{
/// Required in order to trick Rust into thinking that it actually owns a message.
///
/// However, in general message types are zero-sized-types (ZST).
msg_marker: PhantomData<Msg>,
/// The actual read-only handler for the message and state.
raw_handler: RawMessageHandlerMut<Msg, State>
}
impl<Msg, State> Copy for MessageHandlerMut<Msg, State>
where
Msg: Message,
State: ContractState,
{}
impl<Msg, State> Clone for MessageHandlerMut<Msg, State>
where
Msg: Message,
State: ContractState,
{
fn clone(&self) -> Self {
Self {
msg_marker: self.msg_marker,
raw_handler: self.raw_handler,
}
}
}
impl<Msg, State> MessageHandlerMut<Msg, State>
where
Msg: Message,
State: ContractState,
{
/// Constructs a message handler from its raw counterpart.
pub const fn from_raw(raw_handler: RawMessageHandlerMut<Msg, State>) -> Self {
Self { msg_marker: PhantomData, raw_handler }
}
}
impl<Msg, State> MessageHandlerMut<Msg, State>
where
Msg: Message,
State: ContractState,
{
/// Returns the associated handler selector.
pub const fn selector() -> MessageHandlerSelector {
MessageHandlerSelector(0x0) // TODO: Specify and implement behaviour.
}
}
/// Errors the may occure during message handling.
pub enum Error {
/// Encountered when no function selector
/// matched the given input bytes representing
/// the function selector.
InvalidFunctionSelector,
/// Encountered when wrong parameters have
/// been given to a selected function.
InvalidArguments,
}
/// Results of message handling operations.
pub type Result<T> = CoreResult<T, Error>;
/// Types implementing this trait can handle contract calls.
pub trait HandleCall<State> {
/// The return type of the handled message.
type Output: /*Response + */ 'static;
/// Handles the call and returns the result.
fn handle_call(&self, env: &mut ExecutionEnv<State>, data: CallData) -> Result<Self::Output>;
}
/// A message handler that shall never handle a message.
///
/// # Note
///
/// Since this always comes last in a chain of message
/// handlers it can be used to check for incoming unknown
/// message selectors in call datas from the outside.
#[derive(Copy, Clone)]
pub struct UnreachableMessageHandler;
impl<State> HandleCall<State> for UnreachableMessageHandler {
type Output = ();
fn handle_call(&self, _env: &mut ExecutionEnv<State>, data: CallData) -> Result<Self::Output> {
Err(Error::InvalidFunctionSelector)
}
}
macro_rules! impl_handle_call_for_chain {
( $msg_handler_kind:ident ) => {
impl<Msg, State> HandleCall<State> for $msg_handler_kind<Msg, State>
where
Msg: Message,
<Msg as Message>::Output: 'static, // TODO: Could be less restricted.
State: ContractState,
{
type Output = <Msg as Message>::Output;
fn handle_call(&self, env: &mut ExecutionEnv<State>, data: CallData) -> Result<Self::Output> {
let args = <Msg as Message>::Input::decode(&mut &data.params()[..])
.ok_or(Error::InvalidArguments)?;
Ok((self.raw_handler)(env, args))
}
}
impl<Msg, State, Rest> HandleCall<State> for ($msg_handler_kind<Msg, State>, Rest)
where
Msg: Message,
<Msg as Message>::Output: 'static,
State: ContractState,
Rest: HandleCall<State>,
{
type Output =
Either<
<Msg as Message>::Output,
<Rest as HandleCall<State>>::Output
>;
fn handle_call(&self, env: &mut ExecutionEnv<State>, data: CallData) -> Result<Self::Output> {
let (handler, rest) = self;
if $msg_handler_kind::<Msg, State>::selector() == data.selector() {
handler.handle_call(env, data).map(Either::Left)
} else {
rest.handle_call(env, data).map(Either::Right)
}
}
}
}
}
impl_handle_call_for_chain!(MessageHandler);
impl_handle_call_for_chain!(MessageHandlerMut);
use pdsl_core::{
storage::{
Flush,
alloc::{
AllocateUsing,
Initialize,
},
},
};
pub trait ContractState:
AllocateUsing + Initialize + Flush {}
#[derive(Copy, Clone)]
pub struct EmptyContractState;
impl ContractState for EmptyContractState {}
impl AllocateUsing for EmptyContractState {
unsafe fn allocate_using<A>(_alloc: &mut A) -> Self {
EmptyContractState
}
}
impl Initialize for EmptyContractState {
type Args = ();
fn initialize(&mut self, _args: Self::Args) {}
}
impl Flush for EmptyContractState {
fn flush(&mut self) {}
}
macro_rules! state {
(
$state_name:ident {
$(
$field_name:ident : $field_ty:ty
),*
}
) => {
struct $state_name {
$(
$field_name : $field_ty
),*
}
impl pdsl_core::storage::Flush for $state_name {
fn flush(&mut self) {
$(
self.$field_name.flush();
),*
}
}
impl pdsl_core::storage::alloc::AllocateUsing for $state_name {
unsafe fn allocate_using<A>(alloc: &mut A) -> Self
where
A: pdsl_core::storage::Allocator,
{
use pdsl_core::storage::alloc::AllocateUsing;
Self {
$(
$field_name : AllocateUsing::allocate_using(alloc)
),*
}
}
}
impl ContractState for $state_name {}
};
}
use crate::{
contract::ContractDecl,
state::ContractState,
msg::Message,
};
use pdsl_core::{
storage::{
self,
alloc::{
Initialize,
}
},
};
state! {
State {
val: storage::Value<u32>
}
}
impl Initialize for State {
type Args = ();
fn initialize(&mut self, _args: Self::Args) {
self.val.set(0)
}
}
messages! {
Inc(u32);
Get() -> u32;
}
fn instantiate() {
ContractDecl::new("Adder")
.using_state::<State>()
.on_msg_mut::<Inc>(|env, by| {
env.state.val += by
})
.on_msg::<Get>(|env, ()| {
*env.state.val.get()
});
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment