From 16fd3657083004841e560749abc5b0b08d787629 Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Fri, 6 Oct 2023 15:32:10 +0200 Subject: [PATCH] Add TypedContract trait for lower lvl typed interaction --- rust-macros/Cargo.toml | 2 +- rust-macros/src/contract_impl.rs | 313 ++++++++++++++++++++++-------- rust-macros/src/delegate_impl.rs | 6 +- rust-macros/src/lib.rs | 26 +-- rust/Cargo.toml | 4 +- rust/src/composers.rs | 81 +------- rust/src/contract_interface.rs | 208 +++++++++++++++++++- rust/src/lib.rs | 7 +- rust/src/memory.rs | 2 +- rust/tests/composable_contract.rs | 75 +++---- rust/tests/typed_contract.rs | 73 +++++++ 11 files changed, 574 insertions(+), 223 deletions(-) create mode 100644 rust/tests/typed_contract.rs diff --git a/rust-macros/Cargo.toml b/rust-macros/Cargo.toml index 20e4cb4..2baa319 100644 --- a/rust-macros/Cargo.toml +++ b/rust-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "freenet-macros" -version = "0.0.4" +version = "0.0.5" edition = "2021" rust-version = "1.71.1" publish = true diff --git a/rust-macros/src/contract_impl.rs b/rust-macros/src/contract_impl.rs index 68f4bf9..a8a8290 100644 --- a/rust-macros/src/contract_impl.rs +++ b/rust-macros/src/contract_impl.rs @@ -36,6 +36,50 @@ struct AsocTypes { summary: Type, } +impl syn::parse::Parse for AsocTypes { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut params = None; + let mut delta = None; + let mut summary = None; + while !input.is_empty() { + input.parse::()?; + let asoc_type = input.parse::()?; + input.parse::()?; + let type_def = input.parse::()?; + input.parse::()?; + match asoc_type.to_string().as_str() { + "Parameters" => params = Some(type_def), + "Delta" => delta = Some(type_def), + "Summary" => summary = Some(type_def), + _ => return Err(syn::Error::new(asoc_type.span(), "unknown associated type")), + } + } + if params.is_none() { + return Err(syn::Error::new( + input.span(), + "missing Parameters associated type", + )); + } + if delta.is_none() { + return Err(syn::Error::new( + input.span(), + "missing Delta associated type", + )); + } + if summary.is_none() { + return Err(syn::Error::new( + input.span(), + "Summary Parameters associated type", + )); + } + Ok(AsocTypes { + params: params.unwrap(), + delta: delta.unwrap(), + summary: summary.unwrap(), + }) + } +} + pub(crate) fn contract_ffi_impl( input: &ItemImpl, args: &AttributeArgs, @@ -51,78 +95,19 @@ pub(crate) fn contract_ffi_impl( children: vec![], }; - match c_type { - ContractType::Raw => { - let ffi = impl_trait.gen_extern_functions(); - return quote! { - #input - #ffi - } - .into(); - } - ContractType::Composable => {} - } - - #[derive(Default)] - struct AsocTypesParse { - params: Option, - delta: Option, - summary: Option, - } - let mut asoc_types_opt = AsocTypesParse::default(); - for item in &input.items { - if let ImplItem::Type(asoc_type) = item { - match asoc_type.ident.to_string().as_str() { - "Parameters" => { - asoc_types_opt.params = Some(asoc_type.ty.clone()); - } - "Delta" => { - asoc_types_opt.delta = Some(asoc_type.ty.clone()); - } - "Summary" => { - asoc_types_opt.summary = Some(asoc_type.ty.clone()); - } - _ => {} - } + if let ContractType::Raw = c_type { + let ffi = impl_trait.gen_extern_functions(); + return quote! { + #input + #ffi } + .into(); } - let asoc_types = AsocTypes { - params: { - let Some(p) = asoc_types_opt.params else { - return quote_spanned! { - attr_span => - compile_error!("missing Parameters associated type"); - } - .into(); - }; - p - }, - delta: { - let Some(p) = asoc_types_opt.delta else { - return quote_spanned! { - attr_span => - compile_error!("missing Delta associated type"); - } - .into(); - }; - p - }, - summary: { - let Some(p) = asoc_types_opt.summary else { - return quote_spanned! { - attr_span => - compile_error!("missing Summary associated type"); - } - .into(); - }; - p - }, - }; - let mut children: Vec = vec![]; let mut found_children = false; let mut encoder = None; + let mut found_asoc = None; for m in args.args.iter() { match m { @@ -140,6 +125,18 @@ pub(crate) fn contract_ffi_impl( let children_paths = syn::parse_macro_input!(tokens as ChildrenPaths); children.extend(children_paths.paths); } + Some(id) if id == "types" => { + if found_asoc.is_some() { + return quote_spanned! { + list.span() => + compile_error!("only one `types` list allowed"); + } + .into(); + } + let tokens = list.tokens.clone().into(); + let asoc_types = syn::parse_macro_input!(tokens as AsocTypes); + found_asoc = Some(asoc_types); + } Some(other) => { return quote_spanned! { other.span() => @@ -179,34 +176,129 @@ pub(crate) fn contract_ffi_impl( } } - impl_trait.children = children; - - if encoder.is_none() { - return quote_spanned! { - attr_span => - compile_error!("at least one encoder must be specified, possible protocols: BincodeEncoder"); + if let ContractType::Typed = c_type { + let serialization_adapter = if let Some(asoc) = found_asoc { + let Some(encoder) = encoder else { + return quote_spanned! { + attr_span => + compile_error!("at least one Encoder must be specified, possible default protocols: BincodeEncoder, JsonEncoder"); + } + .into(); + }; + impl_trait.gen_serialization_adapter(&asoc, encoder) + } else { + if encoder.is_some() { + return quote_spanned! { + attr_span => + compile_error!("encoder specified but SerializationAdapter is already implemented"); + } + .into(); + } + quote!() + }; + let contract_iface = impl_trait.gen_typed_contract_iface(); + let ffi = impl_trait.gen_extern_functions(); + return quote! { + #input + #serialization_adapter + #contract_iface + #ffi } .into(); } - let encoder = encoder.unwrap(); - if !found_children { - todo!("impl encoder only") - } else if impl_trait.children.is_empty() { + let Some(encoder) = encoder else { return quote_spanned! { attr_span => - compile_error!("at least one ComposableContract child is required"); + compile_error!("at least one Encoder must be specified, possible default protocols: BincodeEncoder, JsonEncoder"); } .into(); - } else { - let contract_iface = impl_trait.gen_contract_iface(encoder); + }; + + let asoc_types = { + #[derive(Default)] + struct AsocTypesParse { + params: Option, + delta: Option, + summary: Option, + } + let mut asoc_types_opt = AsocTypesParse::default(); + for item in &input.items { + if let ImplItem::Type(asoc_type) = item { + match asoc_type.ident.to_string().as_str() { + "Parameters" => { + asoc_types_opt.params = Some(asoc_type.ty.clone()); + } + "Delta" => { + asoc_types_opt.delta = Some(asoc_type.ty.clone()); + } + "Summary" => { + asoc_types_opt.summary = Some(asoc_type.ty.clone()); + } + _ => {} + } + } + } + + AsocTypes { + params: { + let Some(p) = asoc_types_opt.params else { + return quote_spanned! { + attr_span => + compile_error!("missing Parameters associated type"); + } + .into(); + }; + p + }, + delta: { + let Some(p) = asoc_types_opt.delta else { + return quote_spanned! { + attr_span => + compile_error!("missing Delta associated type"); + } + .into(); + }; + p + }, + summary: { + let Some(p) = asoc_types_opt.summary else { + return quote_spanned! { + attr_span => + compile_error!("missing Summary associated type"); + } + .into(); + }; + p + }, + } + }; + + impl_trait.children = children; + + if let ContractType::Composable = c_type { + let contract_iface = impl_trait.gen_composer_contract_iface(encoder); let ffi = impl_trait.gen_extern_functions(); let serialization_adapter = impl_trait.gen_serialization_adapter(&asoc_types, encoder); - quote! { + return quote! { #input #contract_iface - #ffi #serialization_adapter + #ffi + } + .into(); + } + + if impl_trait.children.is_empty() { + quote_spanned! { + attr_span => + compile_error!("at least one ComposableContract child is required"); + } + .into() + } else { + quote_spanned! { + attr_span => + compile_error!("missing required parameters for this con"); } .into() } @@ -245,7 +337,64 @@ impl ImplTrait { } } - fn gen_contract_iface(&self, encoder: &syn::Path) -> TokenStream { + fn gen_typed_contract_iface(&self) -> TokenStream { + let type_name = &self.type_name; + quote! { + impl ::freenet_stdlib::prelude::ContractInterface for #type_name { + fn validate_state( + parameters: ::freenet_stdlib::prelude::Parameters<'static>, + state: ::freenet_stdlib::prelude::State<'static>, + related: ::freenet_stdlib::prelude::RelatedContracts<'static>, + ) -> ::core::result::Result< + ::freenet_stdlib::prelude::ValidateResult, + ::freenet_stdlib::prelude::ContractError, + > { + ::freenet_stdlib::typed_contract::inner_validate_state::<#type_name>(parameters, state, related) + } + + fn validate_delta( + parameters: ::freenet_stdlib::prelude::Parameters<'static>, + delta: ::freenet_stdlib::prelude::StateDelta<'static>, + ) -> ::core::result::Result { + ::freenet_stdlib::typed_contract::inner_validate_delta::<#type_name>(parameters, delta) + } + + fn update_state( + parameters: ::freenet_stdlib::prelude::Parameters<'static>, + state: ::freenet_stdlib::prelude::State<'static>, + data: Vec>, + ) -> ::core::result::Result< + ::freenet_stdlib::prelude::UpdateModification<'static>, + ::freenet_stdlib::prelude::ContractError, + > { + ::freenet_stdlib::typed_contract::inner_update_state::<#type_name>(parameters, state, data) + } + + fn summarize_state( + parameters: ::freenet_stdlib::prelude::Parameters<'static>, + state: ::freenet_stdlib::prelude::State<'static>, + ) -> ::core::result::Result< + ::freenet_stdlib::prelude::StateSummary<'static>, + ::freenet_stdlib::prelude::ContractError, + > { + ::freenet_stdlib::typed_contract::inner_summarize_state::<#type_name>(parameters, state) + } + + fn get_state_delta( + parameters: ::freenet_stdlib::prelude::Parameters<'static>, + state: ::freenet_stdlib::prelude::State<'static>, + summary: ::freenet_stdlib::prelude::StateSummary<'static>, + ) -> ::core::result::Result< + ::freenet_stdlib::prelude::StateDelta<'static>, + ::freenet_stdlib::prelude::ContractError, + > { + ::freenet_stdlib::typed_contract::inner_state_delta::<#type_name>(parameters, state, summary) + } + } + } + } + + fn gen_composer_contract_iface(&self, encoder: &syn::Path) -> TokenStream { let type_name = &self.type_name; let validate_state_impl = self.children.iter().map(|child| { diff --git a/rust-macros/src/delegate_impl.rs b/rust-macros/src/delegate_impl.rs index 93c4709..3249cf2 100644 --- a/rust-macros/src/delegate_impl.rs +++ b/rust-macros/src/delegate_impl.rs @@ -34,7 +34,7 @@ impl ImplStruct { let param_buf = &*(parameters as *const ::freenet_stdlib::memory::buf::BufferBuilder); let bytes = &*std::ptr::slice_from_raw_parts( param_buf.start(), - param_buf.written(None), + param_buf.bytes_written(), ); Parameters::from(bytes) }; @@ -42,7 +42,7 @@ impl ImplStruct { let attested_buf = &*(attested as *const ::freenet_stdlib::memory::buf::BufferBuilder); let bytes = &*std::ptr::slice_from_raw_parts( attested_buf.start(), - attested_buf.written(None), + attested_buf.bytes_written(), ); if bytes.is_empty() { None @@ -53,7 +53,7 @@ impl ImplStruct { let inbound = unsafe { let inbound_buf = &mut *(inbound as *mut ::freenet_stdlib::memory::buf::BufferBuilder); let bytes = - &*std::ptr::slice_from_raw_parts(inbound_buf.start(), inbound_buf.written(None)); + &*std::ptr::slice_from_raw_parts(inbound_buf.start(), inbound_buf.bytes_written()); match ::freenet_stdlib::prelude::bincode::deserialize(bytes) { Ok(v) => v, Err(err) => return ::freenet_stdlib::prelude::DelegateInterfaceResult::from( diff --git a/rust-macros/src/lib.rs b/rust-macros/src/lib.rs index dd73ce1..7a25756 100644 --- a/rust-macros/src/lib.rs +++ b/rust-macros/src/lib.rs @@ -34,6 +34,7 @@ impl syn::parse::Parse for AttributeArgs { enum ContractType { Raw, + Typed, Composable, } @@ -52,34 +53,25 @@ pub fn contract( }); }; match path.segments.last() { - Some(segment) - if segment.ident == "ContractInterface" || segment.ident == "ComposableContract" => - { + Some(segment) => { let c_type = match segment.ident.to_string().as_str() { "ContractInterface" => ContractType::Raw, + "TypedContract" => ContractType::Typed, "ComposableContract" => ContractType::Composable, - _ => unreachable!(), + _ => { + return proc_macro::TokenStream::from(quote_spanned! { + segment.ident.span() => + compile_error!("trait not supported for contract interaction"); + }) + } }; contract_impl::contract_ffi_impl(&input, &args, c_type) } - // Some(segment) if segment.ident == "ComposableContract" => { - // contract_impl::composable_contract_ffi_impl(&input, &args) - // } - Some(segment) => proc_macro::TokenStream::from(quote_spanned! { - segment.ident.span() => - compile_error!("trait not supported for contract interaction"); - }), None => proc_macro::TokenStream::from(quote_spanned! { path.span() => compile_error!("missing trait identifier"); }), } - // println!("{}", quote!(#input)); - // println!("{output}"); - // proc_macro::TokenStream::from(quote! { - // #input - // #output - // }) } /// Generate the necessary code for the WASM runtime to interact with your contract ergonomically and safely. diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c0ff613..a15134b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "freenet-stdlib" -version = "0.0.7" +version = "0.0.8" edition = "2021" rust-version = "1.71.1" publish = true @@ -29,7 +29,7 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["default", "env-filter"] } # internal -freenet-macros = { path = "../rust-macros", version = "0.0.4" } +freenet-macros = { path = "../rust-macros", version = "0.0.5" } [target.'cfg(any(unix, windows))'.dependencies] tokio = { version = "1", optional = true, features = ["macros", "parking_lot", "rt-multi-thread", "sync", "time"] } diff --git a/rust/src/composers.rs b/rust/src/composers.rs index 2f8b866..4c4fb29 100644 --- a/rust/src/composers.rs +++ b/rust/src/composers.rs @@ -44,9 +44,9 @@ pub trait ComposableContract: std::any::Any + Sized { /// Corresponds to ContractInterface `update_state` fn merge( &mut self, - _parameters: &Self::Parameters, - _delta: &TypedUpdateData, - _related: &RelatedContractsContainer, + parameters: &Self::Parameters, + update: &TypedUpdateData, + related: &RelatedContractsContainer, ) -> MergeResult; /// Corresponds to ContractInterface `summarize` @@ -89,80 +89,6 @@ impl From<(Option, Option)> for TypedUpdateD } } -// pub struct NoChild; - -// impl ComposableContract for NoChild { -// type Parameters = NoChild; -// type Context = NoChild; -// type Delta = NoChild; -// type Summary = NoChild; - -// fn verify( -// &self, -// _parameters: &Self::Parameters, -// _context: &Ctx, -// _related: &RelatedContractsContainer, -// ) -> Result -// where -// Children: ComposableContract, -// // ::Parameters: for<'x> From<&'x Self::Parameters>, -// Self::Context: for<'x> From<&'x Ctx>, -// { -// Ok(ValidateResult::Valid) -// } - -// fn verify_delta( -// _parameters: &Self::Parameters, -// _delta: &Self::Delta, -// ) -> Result -// where -// Children: ComposableContract, -// { -// Ok(true) -// } - -// fn merge( -// &mut self, -// _parameters: &Self::Parameters, -// _delta: &TypedUpdateData, -// _related: &RelatedContractsContainer, -// ) -> MergeResult { -// MergeResult::Success -// } - -// fn delta( -// &self, -// _parameters: &Self::Parameters, -// _summary: &Self::Summary, -// ) -> Result { -// Ok(NoChild) -// } - -// fn summarize( -// &self, -// _parameters: &Self::Parameters, -// _summary: &mut ParentSummary, -// ) -> Result<(), ContractError> -// where -// // ::Parameters: for<'x> From<&'x Self::Parameters>, -// ParentSummary: ComposableSummary<::Summary>, -// { -// Ok(()) -// } -// } - -// impl ComposableParameters for NoChild { -// fn contract_id(&self) -> Option { -// None -// } -// } - -// impl<'x, T> From<&'x T> for NoChild { -// fn from(_: &'x T) -> Self { -// NoChild -// } -// } - pub struct NoContext; impl<'x, T> From<&'x T> for NoContext { @@ -237,7 +163,6 @@ pub mod from_bytes { use super::*; - // <::SelfEncoder as Encoder>::Error pub fn inner_validate_state( parameters: Parameters<'static>, state: State<'static>, diff --git a/rust/src/contract_interface.rs b/rust/src/contract_interface.rs index a481fd7..ca49371 100644 --- a/rust/src/contract_interface.rs +++ b/rust/src/contract_interface.rs @@ -1591,12 +1591,14 @@ mod test { } } -pub(crate) mod serialization { - //! Helper types to interaction between wasm and host boundaries. +pub mod serialization { + //! Helper types for interaction between wasm and host boundaries. use std::marker::PhantomData; use serde::de::DeserializeOwned; + use crate::composers::{MergeResult, RelatedContractsContainer}; + use super::*; /// A contract state and it's associated types which can be encoded and decoded @@ -1615,6 +1617,56 @@ pub(crate) mod serialization { type SummaryEncoder: Encoder; } + pub enum TypedUpdateData { + RelatedState { state: T }, + RelatedDelta { delta: T::Delta }, + RelatedStateAndDelta { state: T, delta: T::Delta }, + } + + impl TypedUpdateData { + pub fn from_other(_value: &TypedUpdateData) -> Self + where + Parent: SerializationAdapter, + T: for<'x> From<&'x Parent>, + { + todo!() + } + } + + impl From<(Option, Option)> for TypedUpdateData { + fn from((_state, _delta): (Option, Option)) -> Self { + todo!() + } + } + + pub trait TypedContract: SerializationAdapter { + fn verify( + &self, + parameters: Self::Parameters, + related: RelatedContractsContainer, + ) -> Result; + + fn verify_delta( + parameters: Self::Parameters, + delta: Self::Delta, + ) -> Result; + + fn merge( + &mut self, + parameters: &Self::Parameters, + update: TypedUpdateData, + related: &RelatedContractsContainer, + ) -> MergeResult; + + fn summarize(&self, parameters: Self::Parameters) -> Result; + + fn delta( + &self, + parameters: Self::Parameters, + summary: Self::Summary, + ) -> Result; + } + pub trait Encoder { type Error: Into; fn deserialize(bytes: &[u8]) -> Result; @@ -1666,4 +1718,156 @@ pub(crate) mod serialization { ContractError::Deser(format!("{value}")) } } + + pub fn inner_validate_state( + parameters: Parameters<'static>, + state: State<'static>, + related: RelatedContracts<'static>, + ) -> Result + where + T: SerializationAdapter + TypedContract, + ContractError: From< + <::ParametersEncoder as Encoder< + ::Parameters, + >>::Error, + >, + ContractError: From<<::SelfEncoder as Encoder>::Error>, + { + let typed_params = + <::ParametersEncoder>::deserialize(parameters.as_ref())?; + let typed_state = <::SelfEncoder>::deserialize(state.as_ref())?; + let related_container = RelatedContractsContainer::from(related); + typed_state.verify(typed_params, related_container) + } + + pub fn inner_validate_delta( + parameters: Parameters<'static>, + delta: StateDelta<'static>, + ) -> Result + where + T: SerializationAdapter + TypedContract, + ContractError: From< + <::ParametersEncoder as Encoder< + ::Parameters, + >>::Error, + >, + ContractError: From< + <::DeltaEncoder as Encoder< + ::Delta, + >>::Error, + >, + { + let typed_params = + <::ParametersEncoder>::deserialize(parameters.as_ref())?; + let typed_delta = <::DeltaEncoder>::deserialize(delta.as_ref())?; + ::verify_delta(typed_params, typed_delta) + } + + pub fn inner_update_state( + parameters: Parameters<'static>, + state: State<'static>, + data: Vec>, + ) -> Result, ContractError> + where + T: SerializationAdapter + TypedContract, + ContractError: From<<::SelfEncoder as Encoder>::Error>, + ContractError: From< + <::ParametersEncoder as Encoder< + ::Parameters, + >>::Error, + >, + ContractError: From< + <::DeltaEncoder as Encoder< + ::Delta, + >>::Error, + >, + { + let typed_params = + <::ParametersEncoder>::deserialize(parameters.as_ref())?; + let mut typed_state = + <::SelfEncoder>::deserialize(state.as_ref())?; + let self_updates = UpdateData::get_self_states(&data); + let related_container = RelatedContractsContainer::from(data); + for (state, delta) in self_updates { + let state = state + .map(|s| <::SelfEncoder>::deserialize(s.as_ref())) + .transpose()?; + let delta = delta + .map(|d| { + <::DeltaEncoder>::deserialize(d.as_ref()) + .map(Into::into) + }) + .transpose()?; + let typed_update = TypedUpdateData::from((state, delta)); + match typed_state.merge(&typed_params, typed_update, &related_container) { + MergeResult::Success => {} + MergeResult::RequestRelated(req) => { + return UpdateModification::requires(req.into()); + } + MergeResult::Error(err) => return Err(err), + } + } + let encoded = <::SelfEncoder>::serialize(&typed_state)?; + Ok(UpdateModification::valid(encoded.into())) + } + + pub fn inner_summarize_state( + parameters: Parameters<'static>, + state: State<'static>, + ) -> Result, ContractError> + where + T: SerializationAdapter + TypedContract, + ContractError: From<<::SelfEncoder as Encoder>::Error>, + ContractError: From< + <::ParametersEncoder as Encoder< + ::Parameters, + >>::Error, + >, + ContractError: From< + <::SummaryEncoder as Encoder< + ::Summary, + >>::Error, + >, + { + let typed_params = + <::ParametersEncoder>::deserialize(parameters.as_ref())?; + let typed_state = <::SelfEncoder>::deserialize(state.as_ref())?; + let summary = typed_state.summarize(typed_params)?; + let encoded = <::SummaryEncoder>::serialize(&summary)?; + Ok(encoded.into()) + } + + pub fn inner_state_delta( + parameters: Parameters<'static>, + state: State<'static>, + summary: StateSummary<'static>, + ) -> Result, ContractError> + where + T: SerializationAdapter + TypedContract, + ContractError: From<<::SelfEncoder as Encoder>::Error>, + ContractError: From< + <::ParametersEncoder as Encoder< + ::Parameters, + >>::Error, + >, + ContractError: From< + <::SummaryEncoder as Encoder< + ::Summary, + >>::Error, + >, + ContractError: From< + <::DeltaEncoder as Encoder< + ::Delta, + >>::Error, + >, + { + let typed_params = + <::ParametersEncoder>::deserialize(parameters.as_ref())?; + let typed_state = <::SelfEncoder>::deserialize(state.as_ref())?; + let typed_summary = + <::SummaryEncoder>::deserialize(summary.as_ref())?; + let summary = typed_state.delta(typed_params, typed_summary)?; + let encoded = <::DeltaEncoder>::serialize(&summary)?; + Ok(encoded.into()) + } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 248e805..72a3477 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -8,6 +8,8 @@ pub mod memory; mod parameters; mod versioning; +pub use contract_interface::serialization as typed_contract; + #[allow(dead_code, unused_imports, clippy::all)] pub(crate) mod client_request_generated; #[allow(dead_code, unused_imports, clippy::all)] @@ -25,12 +27,13 @@ pub mod time; /// Locutus stdlib prelude. pub mod prelude { pub use crate::code_hash::*; + pub use crate::composers::RelatedContractsContainer; pub use crate::contract_interface::serialization::{ BincodeEncoder, Encoder, JsonEncoder, SerializationAdapter, }; - pub use crate::contract_interface::wasm_interface::*; + pub use crate::contract_interface::wasm_interface::ContractInterfaceResult; pub use crate::contract_interface::*; - pub use crate::delegate_interface::wasm_interface::*; + pub use crate::delegate_interface::wasm_interface::DelegateInterfaceResult; pub use crate::delegate_interface::*; pub use crate::parameters::*; pub use crate::versioning::*; diff --git a/rust/src/memory.rs b/rust/src/memory.rs index 35e9ca7..49adcf4 100644 --- a/rust/src/memory.rs +++ b/rust/src/memory.rs @@ -26,7 +26,7 @@ impl WasmLinearMem { #[cfg(feature = "contract")] pub mod wasm_interface { - use crate::prelude::*; + use crate::{contract_interface::wasm_interface::ContractInterfaceResult, prelude::*}; fn set_logger() -> Result<(), ContractInterfaceResult> { #[cfg(feature = "trace")] diff --git a/rust/tests/composable_contract.rs b/rust/tests/composable_contract.rs index ba2eb73..5cbe26c 100644 --- a/rust/tests/composable_contract.rs +++ b/rust/tests/composable_contract.rs @@ -1,15 +1,12 @@ -#![allow(dead_code, unused)] -use std::error::Error; - mod parent { - use std::error::Error; + //! This would be the end crate. + // children would be like a different dependency crate which implements composable types use super::children::{self, *}; + use freenet_stdlib::{composers::*, prelude::*}; use serde::{Deserialize, Serialize}; - use freenet_stdlib::memory::wasm_interface::inner_validate_state; - #[derive(Serialize, Deserialize)] pub struct ParentContract { contract_b_0: ChildContract, @@ -30,23 +27,23 @@ mod parent { #[derive(Serialize, Deserialize)] pub struct ParentContractSummary; impl<'a> From<&'a ParentContract> for ParentContractSummary { - fn from(value: &'a ParentContract) -> Self { - todo!() + fn from(_: &'a ParentContract) -> Self { + unimplemented!() } } impl<'a> From<&'a ParentContractSummary> for ChildContractSummary { - fn from(value: &'a ParentContractSummary) -> Self { - todo!() + fn from(_: &'a ParentContractSummary) -> Self { + unimplemented!() } } impl ComposableSummary for ParentContractSummary { - fn merge(&mut self, _value: ChildContractSummary) { - todo!() + fn merge(&mut self, _: ChildContractSummary) { + unimplemented!() } } impl ComposableSummary for ParentContractSummary { - fn merge(&mut self, _value: ParentContractSummary) { - todo!() + fn merge(&mut self, _: ParentContractSummary) { + unimplemented!() } } @@ -56,23 +53,24 @@ mod parent { contract_b_1: ChildContractDelta, } impl<'a> From<&'a ParentContract> for ChildContract { - fn from(value: &'a ParentContract) -> Self { - todo!() + fn from(_: &'a ParentContract) -> Self { + unimplemented!() } } impl<'a> From<&'a ParentContractDelta> for ChildContractDelta { - fn from(value: &'a ParentContractDelta) -> Self { - todo!() + fn from(_: &'a ParentContractDelta) -> Self { + unimplemented!() } } impl<'a> From<&'a ParentContractParams> for ChildContractParams { - fn from(value: &'a ParentContractParams) -> Self { - todo!() + fn from(_: &'a ParentContractParams) -> Self { + unimplemented!() } } #[contract(children(ChildContract, ChildContract), encoder = BincodeEncoder)] - // todo: this would be derived ideally + // todo: this impl block would be derived ideally, we can have a derive macro + // in the struct where the associated types need to be specified impl ComposableContract for ParentContract { type Context = NoContext; type Parameters = ParentContractParams; @@ -82,7 +80,7 @@ mod parent { fn verify( &self, parameters: &Self::Parameters, - context: &Ctx, + _ctx: &Ctx, related: &RelatedContractsContainer, ) -> Result where @@ -157,10 +155,13 @@ mod parent { where ParentSummary: ComposableSummary<::Summary>, { + // todo: probably need ParentSummary to impl From<&Self>? + let mut this_summary = ParentContractSummary; self.contract_b_0 - .summarize(¶meters.into(), &mut ParentContractSummary)?; + .summarize(¶meters.into(), &mut this_summary)?; self.contract_b_1 - .summarize(¶meters.into(), &mut ParentContractSummary)?; + .summarize(¶meters.into(), &mut this_summary)?; + summary.merge(this_summary); Ok(()) } @@ -169,6 +170,8 @@ mod parent { parameters: &Self::Parameters, summary: &Self::Summary, ) -> Result { + // todo: this impl may be probematic to derive, specially getting the return type + // maybe requires adding an other transformation bound let contract_b_0 = self .contract_b_0 .delta(¶meters.into(), &summary.into())?; @@ -183,14 +186,14 @@ mod parent { } impl<'x> From<&'x ParentContract> for children::PubKey { - fn from(value: &'x ParentContract) -> Self { + fn from(_: &'x ParentContract) -> Self { children::PubKey } } } mod children { - use std::error::Error; + //! This would be a depebdency crate. use freenet_stdlib::{composers::*, prelude::*}; use serde::{Deserialize, Serialize}; @@ -215,7 +218,7 @@ mod children { pub struct PubKey; impl From for PubKey { - fn from(value: ChildContractParams) -> Self { + fn from(_: ChildContractParams) -> Self { PubKey } } @@ -228,26 +231,28 @@ mod children { fn verify( &self, - parameters: &Self::Parameters, + _parameters: &Self::Parameters, context: &Ctx, - related: &RelatedContractsContainer, + _related: &RelatedContractsContainer, ) -> Result where Child: ComposableContract, Self::Context: for<'x> From<&'x Ctx>, { - let pub_key = PubKey::from(context); + let _pub_key = PubKey::from(context); + // assert something in self/context is signed with pub key Ok(ValidateResult::Valid) } fn verify_delta( parameters: &Self::Parameters, - delta: &Self::Delta, + _delta: &Self::Delta, ) -> Result where Child: ComposableContract, { - let pub_key = PubKey::from(parameters.clone()); + let _pub_key = PubKey::from(parameters.clone()); + // assert something in Self::Delta is signed with pub key Ok(true) } @@ -259,7 +264,7 @@ mod children { ) -> MergeResult { let contract_id = parameters.contract_id().unwrap(); let Related::Found { - state: mut contract_b, + state: _other_contract, .. } = related.get::(&contract_id) else { @@ -275,7 +280,7 @@ mod children { _parameters: &Self::Parameters, _summary: &Self::Summary, ) -> Result { - todo!() + Ok(ChildContractDelta) } fn summarize( @@ -287,7 +292,7 @@ mod children { ParentSummary: ComposableSummary<::Summary>, { summary.merge(ChildContractSummary); - todo!() + Ok(()) } } } diff --git a/rust/tests/typed_contract.rs b/rust/tests/typed_contract.rs new file mode 100644 index 0000000..fb25e87 --- /dev/null +++ b/rust/tests/typed_contract.rs @@ -0,0 +1,73 @@ +use freenet_macros::contract; +use freenet_stdlib::{composers::MergeResult, prelude::*, typed_contract::TypedContract}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct Contract; +#[derive(Serialize, Deserialize)] +pub struct CParams; +#[derive(Serialize, Deserialize)] +pub struct CDelta; +#[derive(Serialize, Deserialize)] +pub struct CSummary; + +// use freenet_stdlib::prelude::SerializationAdapter; +// impl SerializationAdapter for Contract { +// type Parameters = CParams; +// type Delta = CDelta; +// type Summary = CSummary; +// type SelfEncoder = BincodeEncoder; +// type ParametersEncoder = BincodeEncoder; +// type DeltaEncoder = BincodeEncoder; +// type SummaryEncoder = BincodeEncoder; +// } + +#[contract( + encoder = BincodeEncoder, + types( + type Parameters = CParams; + type Delta = CDelta; + type Summary = CSummary; + ) +)] +impl TypedContract for Contract { + fn verify( + &self, + _: Self::Parameters, + _: freenet_stdlib::composers::RelatedContractsContainer, + ) -> Result + { + unimplemented!() + } + + fn verify_delta( + _: Self::Parameters, + _: Self::Delta, + ) -> Result { + unimplemented!() + } + + fn merge( + &mut self, + _: &Self::Parameters, + _: freenet_stdlib::typed_contract::TypedUpdateData, + _: &RelatedContractsContainer, + ) -> freenet_stdlib::composers::MergeResult { + MergeResult::Success + } + + fn summarize( + &self, + _: Self::Parameters, + ) -> Result { + Ok(CSummary) + } + + fn delta( + &self, + _: Self::Parameters, + _: Self::Summary, + ) -> Result { + unimplemented!() + } +}