diff --git a/client/tests/integration/smartcontracts/executor_with_custom_token/src/lib.rs b/client/tests/integration/smartcontracts/executor_with_custom_token/src/lib.rs index 9cb0166657c..c6fa6d2c624 100644 --- a/client/tests/integration/smartcontracts/executor_with_custom_token/src/lib.rs +++ b/client/tests/integration/smartcontracts/executor_with_custom_token/src/lib.rs @@ -97,7 +97,7 @@ impl Executor { })?; if let Ok(can_unregister_domain_token) = - iroha_executor::default::tokens::domain::CanUnregisterDomain::try_from(token) + iroha_executor::default::tokens::domain::CanUnregisterDomain::try_from(&token) { found_accounts.push((account, can_unregister_domain_token.domain_id)); break; diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm index 135f1021cc0..afde5635a2d 100644 Binary files a/configs/swarm/executor.wasm and b/configs/swarm/executor.wasm differ diff --git a/data_model/derive/src/enum_ref.rs b/data_model/derive/src/enum_ref.rs new file mode 100644 index 00000000000..e75620a94fc --- /dev/null +++ b/data_model/derive/src/enum_ref.rs @@ -0,0 +1,175 @@ +use darling::{FromAttributes, FromDeriveInput, FromMeta, FromVariant}; +use iroha_macro_utils::Emitter; +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; + +pub fn impl_enum_ref(_emitter: &mut Emitter, input: &syn2::DeriveInput) -> TokenStream { + let input = EnumRef::from_derive_input(input).unwrap(); + quote! { #input } +} + +#[derive(Clone, Copy)] +enum Transparent { + Transparent, + NotTransparent, +} + +#[derive(Clone)] +enum EnumRefDeriveAttrs { + Derive(Vec), +} + +#[derive(Clone, FromAttributes)] +#[darling(attributes(enum_ref))] +struct EnumRefAttrs { + derive: EnumRefDeriveAttrs, +} + +#[derive(Clone, Copy, FromAttributes)] +#[darling(attributes(enum_ref))] +struct EnumRefVariantAttrs { + transparent: Transparent, +} + +#[derive(Clone)] +struct EnumRefField { + ty: syn2::Type, +} + +#[derive(Clone)] +struct EnumRefVariant { + ident: syn2::Ident, + field: EnumRefField, +} + +#[derive(Clone)] +struct EnumRef { + attrs: EnumRefAttrs, + ident: syn2::Ident, + generics: syn2::Generics, + data: darling::ast::Data, +} + +impl FromMeta for Transparent { + fn from_none() -> Option { + Some(Self::NotTransparent) + } + + fn from_word() -> darling::Result { + Ok(Self::Transparent) + } +} + +impl FromMeta for EnumRefDeriveAttrs { + fn from_list(items: &[darling::ast::NestedMeta]) -> darling::Result { + Ok(Self::Derive(items.to_vec())) + } +} + +impl FromVariant for EnumRefVariant { + fn from_variant(variant: &syn2::Variant) -> darling::Result { + let transparent = EnumRefVariantAttrs::from_attributes(&variant.attrs)?; + + let mut fields: Vec<_> = variant + .fields + .iter() + .map(|field| { + assert_eq!(field.ident, None); + + EnumRefField { + ty: gen_field_ty(transparent.transparent, &field.ty), + } + }) + .collect(); + + if fields.len() > 1 { + unimplemented!() + } + + Ok(Self { + ident: variant.ident.clone(), + field: fields.swap_remove(0), + }) + } +} + +impl FromDeriveInput for EnumRef { + fn from_derive_input(input: &syn2::DeriveInput) -> darling::Result { + Ok(Self { + attrs: EnumRefAttrs::from_attributes(&input.attrs)?, + ident: gen_enum_ref_ident(&input.ident), + generics: input.generics.clone(), + data: darling::ast::Data::try_from(&input.data)?, + }) + } +} + +impl ToTokens for EnumRefAttrs { + fn to_tokens(&self, tokens: &mut TokenStream) { + let EnumRefDeriveAttrs::Derive(derive) = &self.derive; + quote! { + #[derive(#(#derive),*)] + } + .to_tokens(tokens); + } +} + +impl ToTokens for EnumRefField { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.ty.to_tokens(tokens); + } +} + +impl ToTokens for EnumRefVariant { + fn to_tokens(&self, tokens: &mut TokenStream) { + let EnumRefVariant { ident, field } = self; + + quote! { + #ident(#field) + } + .to_tokens(tokens); + } +} + +impl ToTokens for EnumRef { + fn to_tokens(&self, tokens: &mut TokenStream) { + let EnumRef { + attrs, + ident, + generics, + data, + } = self; + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + let variants = if let darling::ast::Data::Enum(variants) = data { + variants + } else { + unreachable!() + }; + + quote! { + #attrs + pub(super) enum #ident<'a> #impl_generics #where_clause { + #(#variants),* + } + } + .to_tokens(tokens); + } +} + +fn gen_enum_ref_ident(ident: &syn2::Ident) -> syn2::Ident { + syn2::Ident::new(&format!("{ident}Ref"), proc_macro2::Span::call_site()) +} + +fn gen_field_ty(transparent: Transparent, field_ty: &syn2::Type) -> syn2::Type { + if matches!(transparent, Transparent::Transparent) { + if let syn2::Type::Path(ty) = field_ty { + if let Some(ident) = ty.path.get_ident() { + let ident = gen_enum_ref_ident(ident); + return syn2::parse_quote! { #ident<'a> }; + } + } + } + + syn2::parse_quote!(&'a #field_ty) +} diff --git a/data_model/derive/src/lib.rs b/data_model/derive/src/lib.rs index daf1b3a9a4f..49c626efb68 100644 --- a/data_model/derive/src/lib.rs +++ b/data_model/derive/src/lib.rs @@ -1,4 +1,5 @@ //! A crate containing various derive macros for `data_model` +mod enum_ref; mod filter; mod has_origin; mod id; @@ -9,6 +10,55 @@ use iroha_macro_utils::Emitter; use manyhow::{emit, manyhow, Result}; use proc_macro2::TokenStream; +/// Construct a matching enum with references in place of enum variant fields +/// +/// # Example +/// +/// ```rust +/// use iroha_data_model_derive::EnumRef; +/// use parity_scale_codec::Encode; +/// +/// #[derive(EnumRef)] +/// enum InnerEnum { +/// A(u32), +/// B(i32) +/// } +/// +/// #[derive(EnumRef)] +/// #[enum_ref(derive(Encode))] +/// enum OuterEnum { +/// A(String), +/// #[enum_ref(transparent)] +/// B(InnerEnum), +/// } +/// +/// /* will produce: +/// +/// enum InnerEnumRef<'a> { +/// A(&'a u32), +/// B(&'a i32), +/// } +/// +/// enum OuterEnumRef<'a> { +/// A(&'a String), +/// B(InnerEnumRef<'a>), +/// } +/// */ +/// ``` +/// +#[manyhow] +#[proc_macro_derive(EnumRef, attributes(enum_ref))] +pub fn enum_ref(input: TokenStream) -> TokenStream { + let mut emitter = Emitter::new(); + + let Some(input) = emitter.handle(syn2::parse2(input)) else { + return emitter.finish_token_stream(); + }; + + let enum_ref = enum_ref::impl_enum_ref(&mut emitter, &input); + emitter.finish_token_stream_with(enum_ref) +} + /// Macro which controls how to export item's API. The behaviour is controlled with `transparent_api` /// feature flag. If the flag is active, item's public fields will be exposed as public, however, if /// it's not active, item will be exposed as opaque, i.e. no fields will be visible. This enables diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index ff966821b45..5e9388f64cf 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -5,7 +5,7 @@ use alloc::{format, string::String, vec::Vec}; use core::fmt::{Debug, Display}; use derive_more::{Constructor, DebugCustom, Display}; -use iroha_data_model_derive::model; +use iroha_data_model_derive::{model, EnumRef}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -20,10 +20,16 @@ use crate::{seal, Level, Registered}; /// Instructions allows to change the state of `Iroha`. /// All possible instructions are implementors of this trait, excluding /// [`InstructionBox`] which is just a wrapper. -pub trait Instruction: Into + seal::Sealed {} +pub trait Instruction: Into + seal::Sealed { + /// [`Encode`] [`Self`] as [`InstructionBox`]. + /// + /// Used to avoid an unnecessary clone + fn encode_as_instruction_box(&self) -> Vec; +} #[model] pub mod model { + use iroha_macro::FromVariant; pub use transparent::*; use super::*; @@ -41,13 +47,16 @@ pub mod model { Eq, PartialOrd, Ord, + EnumRef, EnumDiscriminants, + FromVariant, Decode, Encode, Deserialize, Serialize, IntoSchema, )] + #[enum_ref(derive(Encode, FromVariant))] #[strum_discriminants( name(InstructionType), derive( @@ -64,29 +73,37 @@ pub mod model { any(feature = "ffi_import", feature = "ffi_export"), derive(iroha_ffi::FfiType) ), - allow(missing_docs), repr(u8) )] #[ffi_type(opaque)] #[allow(missing_docs)] pub enum InstructionBox { #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] Register(RegisterBox), #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] Unregister(UnregisterBox), #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] Mint(MintBox), #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] Burn(BurnBox), #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] Transfer(TransferBox), #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] SetKeyValue(SetKeyValueBox), #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] RemoveKeyValue(RemoveKeyValueBox), #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] Grant(GrantBox), #[debug(fmt = "{_0:?}")] + #[enum_ref(transparent)] Revoke(RevokeBox), #[debug(fmt = "{_0:?}")] ExecuteTrigger(ExecuteTrigger), @@ -102,67 +119,74 @@ pub mod model { #[debug(fmt = "{_0:?}")] Fail(Fail), } +} - impl Instruction for InstructionBox {} - - impl Instruction for SetKeyValue {} - impl Instruction for SetKeyValue {} - impl Instruction for SetKeyValue {} - impl Instruction for SetKeyValue {} - - impl Instruction for RemoveKeyValue {} - impl Instruction for RemoveKeyValue {} - impl Instruction for RemoveKeyValue {} - impl Instruction for RemoveKeyValue {} - - impl Instruction for Register {} - impl Instruction for Register {} - impl Instruction for Register {} - impl Instruction for Register {} - impl Instruction for Register {} - impl Instruction for Register {} - impl Instruction for Register> {} - - impl Instruction for Unregister {} - impl Instruction for Unregister {} - impl Instruction for Unregister {} - impl Instruction for Unregister {} - impl Instruction for Unregister {} - impl Instruction for Unregister {} - impl Instruction for Unregister> {} - - impl Instruction for Mint {} - impl Instruction for Mint {} - impl Instruction for Mint {} - impl Instruction for Mint {} - impl Instruction for Mint {} - impl Instruction for Mint> {} - - impl Instruction for Burn {} - impl Instruction for Burn {} - impl Instruction for Burn {} - impl Instruction for Burn {} - impl Instruction for Burn> {} - - impl Instruction for Transfer {} - impl Instruction for Transfer {} - impl Instruction for Transfer {} - impl Instruction for Transfer {} - impl Instruction for Transfer {} - impl Instruction for Transfer {} - - impl Instruction for Grant {} - impl Instruction for Grant {} - - impl Instruction for Revoke {} - impl Instruction for Revoke {} - - impl Instruction for SetParameter {} - impl Instruction for NewParameter {} - impl Instruction for Upgrade {} - impl Instruction for ExecuteTrigger {} - impl Instruction for Log {} - impl Instruction for Fail {} +macro_rules! impl_instruction { + ($($ty:ty),+ $(,)?) => { $( + impl Instruction for $ty { + fn encode_as_instruction_box(&self) -> Vec { + InstructionBoxRef::from(self).encode() + } + } )+ + } +} + +impl_instruction! { + SetKeyValue, + SetKeyValue, + SetKeyValue, + SetKeyValue, + RemoveKeyValue, + RemoveKeyValue, + RemoveKeyValue, + RemoveKeyValue, + Register, + Register, + Register, + Register, + Register, + Register, + Register>, + Unregister, + Unregister, + Unregister, + Unregister, + Unregister, + Unregister, + Unregister>, + Mint, + Mint, + Mint, + Mint, + Mint, + Mint>, + Burn, + Burn, + Burn, + Burn, + Burn>, + Transfer, + Transfer, + Transfer, + Transfer, + Transfer, + Transfer, + Grant, + Grant, + Revoke, + Revoke, + SetParameter, + NewParameter, + Upgrade, + ExecuteTrigger, + Log, + Fail, +} + +impl Instruction for InstructionBox { + fn encode_as_instruction_box(&self) -> Vec { + self.encode() + } } mod transparent { @@ -207,26 +231,22 @@ mod transparent { } macro_rules! impl_into_box { - ( - $($isi:ident $(< $($generic:ident $(< $nested_generic:ident >)?),+ >)?)|* - ==> $boxed:ident :: $variant:ident + ( $($isi:ty)|* + => $middle:ty => $boxed:ty[$variant:ident], + => $middle_ref:ty => $boxed_ref:ty[$variant_ref:ident] ) => {$( - impl From<$isi $(< $($generic $(< $nested_generic >)?),+ >)? > for $boxed { - fn from(instruction: $isi $(< $($generic $(< $nested_generic >)?),+ >)?) -> Self { - Self::$variant(instruction) + impl From<$isi> for $boxed { + fn from(instruction: $isi) -> Self { + Self::$variant(<$middle>::from(instruction)) } } - )*}; - ( - $($isi:ident $(< $($generic:ident $(< $nested_generic:ident >)?),+ >)?)|* - => $middle:ident ==> $boxed:ident :: $variant:ident - ) => {$( - impl From<$isi $(< $($generic $(< $nested_generic >)?),+ >)? > for $boxed { - fn from(instruction: $isi $(< $($generic $(< $nested_generic >)?),+ >)?) -> Self { - Self::$variant($middle::from(instruction)) + + impl<'a> From<&'a $isi> for $boxed_ref { + fn from(instruction: &'a $isi) -> Self { + Self::$variant(<$middle_ref>::from(instruction)) } - } - )*}; + })* + }; } isi! { @@ -242,8 +262,6 @@ mod transparent { } } - impl_into_box!(SetParameter ==> InstructionBox::SetParameter); - isi! { /// Sized structure for all possible on-chain configuration parameters when they are first created. /// Generic instruction for setting a chain-wide config parameter. @@ -258,8 +276,6 @@ mod transparent { } } - impl_into_box!(NewParameter ==> InstructionBox::NewParameter); - isi! { /// Generic instruction to set key value at the object. #[schema(bounds = "O: Identifiable, O::Id: IntoSchema")] @@ -336,7 +352,9 @@ mod transparent { SetKeyValue | SetKeyValue | SetKeyValue | - SetKeyValue => SetKeyValueBox ==> InstructionBox::SetKeyValue + SetKeyValue + => SetKeyValueBox => InstructionBox[SetKeyValue], + => SetKeyValueBoxRef<'a> => InstructionBoxRef<'a>[SetKeyValue] } isi! { @@ -405,7 +423,9 @@ mod transparent { RemoveKeyValue | RemoveKeyValue | RemoveKeyValue | - RemoveKeyValue => RemoveKeyValueBox ==> InstructionBox::RemoveKeyValue + RemoveKeyValue + => RemoveKeyValueBox => InstructionBox[RemoveKeyValue], + => RemoveKeyValueBoxRef<'a> => InstructionBoxRef<'a>[RemoveKeyValue] } isi! { @@ -490,7 +510,9 @@ mod transparent { Register | Register | Register | - Register > => RegisterBox ==> InstructionBox::Register + Register> + => RegisterBox => InstructionBox[Register], + => RegisterBoxRef<'a> => InstructionBoxRef<'a>[Register] } isi! { @@ -519,7 +541,9 @@ mod transparent { Unregister | Unregister | Unregister | - Unregister > => UnregisterBox ==> InstructionBox::Unregister + Unregister> + => UnregisterBox => InstructionBox[Unregister], + => UnregisterBoxRef<'a> => InstructionBoxRef<'a>[Unregister] } impl Unregister { @@ -669,13 +693,17 @@ mod transparent { impl_into_box! { Mint | - Mint => AccountMintBox ==> MintBox::Account + Mint + => AccountMintBox => MintBox[Account], + => AccountMintBoxRef<'a> => MintBoxRef<'a>[Account] } impl_into_box! { Mint | Mint | - Mint => AssetMintBox ==> MintBox::Asset + Mint + => AssetMintBox => MintBox[Asset], + => AssetMintBoxRef<'a> => MintBoxRef<'a>[Asset] } impl_into_box! { @@ -684,7 +712,9 @@ mod transparent { Mint | Mint | Mint | - Mint > => MintBox ==> InstructionBox::Mint + Mint> + => MintBox => InstructionBox[Mint], + => MintBoxRef<'a> => InstructionBoxRef<'a>[Mint] } isi! { @@ -763,7 +793,9 @@ mod transparent { impl_into_box! { Burn | Burn | - Burn => AssetBurnBox ==> BurnBox::Asset + Burn + => AssetBurnBox => BurnBox[Asset], + => AssetBurnBoxRef<'a> => BurnBoxRef<'a>[Asset] } impl_into_box! { @@ -771,7 +803,9 @@ mod transparent { Burn | Burn | Burn | - Burn > => BurnBox ==> InstructionBox::Burn + Burn> + => BurnBox => InstructionBox[Burn], + => BurnBoxRef<'a> => InstructionBoxRef<'a>[Burn] } isi! { @@ -878,7 +912,9 @@ mod transparent { Transfer | Transfer | Transfer | - Transfer => AssetTransferBox ==> TransferBox::Asset + Transfer + => AssetTransferBox => TransferBox[Asset], + => AssetTransferBoxRef<'a> => TransferBoxRef<'a>[Asset] } impl_into_box! { @@ -887,7 +923,9 @@ mod transparent { Transfer | Transfer | Transfer | - Transfer => TransferBox ==> InstructionBox::Transfer + Transfer + => TransferBox => InstructionBox[Transfer], + => TransferBoxRef<'a> => InstructionBoxRef<'a>[Transfer] } isi! { @@ -902,8 +940,6 @@ mod transparent { } } - impl_into_box!(Fail ==> InstructionBox::Fail); - isi! { /// Generic instruction for granting permission to an entity. pub struct Grant> { @@ -946,7 +982,9 @@ mod transparent { impl_into_box! { Grant | - Grant => GrantBox ==> InstructionBox::Grant + Grant + => GrantBox => InstructionBox[Grant], + => GrantBoxRef<'a> => InstructionBoxRef<'a>[Grant] } isi! { @@ -991,7 +1029,9 @@ mod transparent { impl_into_box! { Revoke | - Revoke => RevokeBox ==> InstructionBox::Revoke + Revoke + => RevokeBox => InstructionBox[Revoke], + => RevokeBoxRef<'a> => InstructionBoxRef<'a>[Revoke] } isi! { @@ -1006,8 +1046,6 @@ mod transparent { } } - impl_into_box!(ExecuteTrigger ==> InstructionBox::ExecuteTrigger); - isi! { /// Generic instruction for upgrading runtime objects. #[derive(Constructor, Display)] @@ -1020,8 +1058,6 @@ mod transparent { } } - impl_into_box!(Upgrade ==> InstructionBox::Upgrade); - isi! { /// Instruction to print logs #[derive(Constructor, Display)] @@ -1035,8 +1071,6 @@ mod transparent { pub msg: String, } } - - impl_into_box!(Log ==> InstructionBox::Log); } macro_rules! isi_box { @@ -1049,6 +1083,8 @@ macro_rules! isi_box { PartialOrd, Ord, Display, + EnumRef, + EnumDiscriminants, parity_scale_codec::Decode, parity_scale_codec::Encode, serde::Deserialize, @@ -1056,12 +1092,18 @@ macro_rules! isi_box { iroha_schema::IntoSchema, derive_more::From, )] + #[enum_ref(derive(Encode, iroha_macro::FromVariant))] $($meta)* $item }; } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(SetKeyValueType), + derive(Encode), + )] /// Enum with all supported [`SetKeyValue`] instructions. pub enum SetKeyValueBox { /// Set key value for [`Domain`]. @@ -1076,6 +1118,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(RemoveKeyValueType), + derive(Encode), + )] /// Enum with all supported [`RemoveKeyValue`] instructions. pub enum RemoveKeyValueBox { /// Remove key value from [`Domain`]. @@ -1090,6 +1137,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(RegisterType), + derive(Encode), + )] /// Enum with all supported [`Register`] instructions. pub enum RegisterBox { /// Register [`Peer`]. @@ -1110,6 +1162,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(UnregisterType), + derive(Encode), + )] /// Enum with all supported [`Unregister`] instructions. pub enum UnregisterBox { /// Unregister [`Peer`]. @@ -1130,11 +1187,18 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(MintType), + derive(Encode), + )] /// Enum with all supported [`Mint`] instructions. pub enum MintBox { /// Mint for [`Account`]. + #[enum_ref(transparent)] Account(AccountMintBox), /// Mint for [`Asset`]. + #[enum_ref(transparent)] Asset(AssetMintBox), /// Mint [`Trigger`] repetitions. TriggerRepetitions(Mint>), @@ -1142,6 +1206,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(AccountMintType), + derive(Encode), + )] /// Enum with all supported [`Mint`] instructions related to [`Account`]. pub enum AccountMintBox { /// Mint [`PublicKey`]. @@ -1152,6 +1221,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(AssetMintType), + derive(Encode), + )] /// Enum with all supported [`Mint`] instructions related to [`Asset`]. pub enum AssetMintBox { /// Mint [`Asset`] of [`Quantity`] type. @@ -1164,11 +1238,17 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(BurnType), + derive(Encode), + )] /// Enum with all supported [`Burn`] instructions. pub enum BurnBox { /// Burn [`PublicKey`] for [`Account`]. AccountPublicKey(Burn), /// Burn [`Asset`]. + #[enum_ref(transparent)] Asset(AssetBurnBox), /// Burn [`Trigger`] repetitions. TriggerRepetitions(Burn>), @@ -1176,6 +1256,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(AssetBurnType), + derive(Encode), + )] /// Enum with all supported [`Burn`] instructions related to [`Asset`]. pub enum AssetBurnBox { /// Burn [`Asset`] of [`Quantity`] type. @@ -1188,6 +1273,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(TransferType), + derive(Encode), + )] /// Enum with all supported [`Transfer`] instructions. pub enum TransferBox { /// Transfer [`Domain`] to another [`Account`]. @@ -1195,11 +1285,17 @@ isi_box! { /// Transfer [`AssetDefinition`] to another [`Account`]. AssetDefinition(Transfer), /// Transfer [`Asset`] to another [`Account`]. + #[enum_ref(transparent)] Asset(AssetTransferBox), } } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(AssetTransferType), + derive(Encode), + )] /// Enum with all supported [`Transfer`] instructions related to [`Asset`]. pub enum AssetTransferBox { /// Transfer [`Asset`] of [`Quantity`] type. @@ -1214,6 +1310,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(GrantType), + derive(Encode), + )] /// Enum with all supported [`Grant`] instructions. pub enum GrantBox { /// Grant [`PermissionToken`] to [`Account`]. @@ -1224,6 +1325,11 @@ isi_box! { } isi_box! { + #[strum_discriminants( + vis(pub(crate)), + name(RevokeType), + derive(Encode), + )] /// Enum with all supported [`Revoke`] instructions. pub enum RevokeBox { /// Revoke [`PermissionToken`] from [`Account`]. diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 561228a73cc..068d069298f 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -31,7 +31,7 @@ use events::TriggeringFilterBox; use getset::Getters; use iroha_crypto::{HashOf, PublicKey}; use iroha_data_model_derive::{ - model, IdEqOrdHash, PartiallyTaggedDeserialize, PartiallyTaggedSerialize, + model, EnumRef, IdEqOrdHash, PartiallyTaggedDeserialize, PartiallyTaggedSerialize, }; use iroha_macro::{error::ErrorTryFromEnum, FromVariant}; use iroha_primitives::{ @@ -610,6 +610,7 @@ pub mod model { Eq, PartialOrd, Ord, + EnumRef, FromVariant, Decode, Encode, @@ -617,6 +618,7 @@ pub mod model { Serialize, IntoSchema, )] + #[enum_ref(derive(Encode, FromVariant))] #[allow(clippy::enum_variant_names)] #[ffi_type(local)] pub enum IdBox { @@ -652,6 +654,7 @@ pub mod model { Eq, PartialOrd, Ord, + EnumRef, FromVariant, Decode, Encode, @@ -659,6 +662,7 @@ pub mod model { Serialize, IntoSchema, )] + #[enum_ref(derive(Encode, FromVariant))] #[ffi_type] pub enum RegistrableBox { /// [`Peer`](`peer::Peer`) variant. @@ -693,6 +697,7 @@ pub mod model { Eq, PartialOrd, Ord, + EnumRef, FromVariant, Decode, Encode, @@ -700,6 +705,7 @@ pub mod model { Serialize, IntoSchema, )] + #[enum_ref(derive(Encode, FromVariant))] #[ffi_type] pub enum IdentifiableBox { /// [`NewDomain`](`domain::NewDomain`) variant. @@ -737,6 +743,7 @@ pub mod model { Eq, PartialOrd, Ord, + EnumRef, FromVariant, Decode, Encode, @@ -744,6 +751,7 @@ pub mod model { Serialize, IntoSchema, )] + #[enum_ref(derive(Encode, FromVariant))] // SAFETY: `UpgradableBox` has no trap representations in `executor::Executor` #[ffi_type(unsafe {robust})] #[serde(untagged)] // Unaffected by #3330, because stores binary data with no `u128` @@ -1022,6 +1030,99 @@ pub mod model { } } +macro_rules! impl_encode_as_id_box { + ($($ty:ty),+ $(,)?) => { $( + impl $ty { + /// [`Encode`] [`Self`] as [`IdBox`]. + /// + /// Used to avoid an unnecessary clone + pub fn encode_as_id_box(&self) -> Vec { + IdBoxRef::from(self).encode() + } + } )+ + } +} + +macro_rules! impl_encode_as_identifiable_box { + ($($ty:ty),+ $(,)?) => { $( + impl $ty { + /// [`Encode`] [`Self`] as [`IdentifiableBox`]. + /// + /// Used to avoid an unnecessary clone + pub fn encode_as_identifiable_box(&self) -> Vec { + IdentifiableBoxRef::from(self).encode() + } + } )+ + } +} + +macro_rules! impl_encode_as_registrable_box { + ($($ty:ty),+ $(,)?) => { $( + impl $ty { + /// [`Encode`] [`Self`] as [`RegistrableBox`]. + /// + /// Used to avoid an unnecessary clone + pub fn encode_as_registrable_box(&self) -> Vec { + RegistrableBoxRef::from(self).encode() + } + } )+ + } +} + +macro_rules! impl_encode_as_upgradable_box { + ($($ty:ty),+ $(,)?) => { $( + impl $ty { + /// [`Encode`] [`Self`] as [`RegistrableBox`]. + /// + /// Used to avoid an unnecessary clone + pub fn encode_as_upgradable_box(&self) -> Vec { + UpgradableBoxRef::from(self).encode() + } + } )+ + } +} + +impl_encode_as_id_box! { + peer::PeerId, + domain::DomainId, + account::AccountId, + asset::AssetDefinitionId, + asset::AssetId, + role::RoleId, + trigger::TriggerId, + permission::PermissionTokenId, + parameter::ParameterId, +} + +impl_encode_as_identifiable_box! { + peer::Peer, + domain::NewDomain, + account::NewAccount, + asset::NewAssetDefinition, + role::NewRole, + domain::Domain, + account::Account, + asset::AssetDefinition, + asset::Asset, + role::Role, + trigger::Trigger, + parameter::Parameter, +} + +impl_encode_as_registrable_box! { + peer::Peer, + domain::NewDomain, + account::NewAccount, + asset::NewAssetDefinition, + asset::Asset, + role::NewRole, + trigger::Trigger, +} + +impl_encode_as_upgradable_box! { + executor::Executor, +} + impl Decode for ChainId { fn decode( input: &mut I, diff --git a/primitives/src/addr.rs b/primitives/src/addr.rs index 0e704f10353..9236f013d58 100644 --- a/primitives/src/addr.rs +++ b/primitives/src/addr.rs @@ -766,10 +766,9 @@ mod test { port: 9019, }); - let kita = &serde_json::to_string(&v6).unwrap(); - println!("{kita}"); - let kara = serde_json::from_str::(kita).unwrap(); - assert_eq!(kara, v6); + let addr_str = &serde_json::to_string(&v6).unwrap(); + let addr = serde_json::from_str::(addr_str).unwrap(); + assert_eq!(addr, v6); let host = SocketAddr::Host(SocketAddrHost { host: "localhost".into(), diff --git a/smart_contract/executor/derive/src/token.rs b/smart_contract/executor/derive/src/token.rs index b7c868e4b0a..a68c22ebcb4 100644 --- a/smart_contract/executor/derive/src/token.rs +++ b/smart_contract/executor/derive/src/token.rs @@ -38,7 +38,7 @@ fn impl_token(ident: &syn2::Ident, generics: &syn2::Generics) -> proc_macro2::To res, "Failed to get permission token from cursor" )) - .filter_map(|token| Self::try_from(token).ok()) + .filter_map(|token| Self::try_from(&token).ok()) .any(|token| self == &token) } } @@ -50,10 +50,10 @@ fn impl_try_from_permission_token(ident: &syn2::Ident, generics: &syn2::Generics let token_id = quote! { <#ident #ty_generics as ::iroha_executor::permission::Token>::name() }; quote! { - impl #impl_generics ::core::convert::TryFrom<::iroha_executor::data_model::permission::PermissionToken> for #ident #ty_generics #where_clause { + impl #impl_generics ::core::convert::TryFrom<&::iroha_executor::data_model::permission::PermissionToken> for #ident #ty_generics #where_clause { type Error = ::iroha_executor::permission::PermissionTokenConversionError; - fn try_from(token: ::iroha_executor::data_model::permission::PermissionToken) -> ::core::result::Result { + fn try_from(token: &::iroha_executor::data_model::permission::PermissionToken) -> ::core::result::Result { if #token_id != *token.definition_id() { return Err(::iroha_executor::permission::PermissionTokenConversionError::Id( ToOwned::to_owned(token.definition_id()) diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index 85639593b4c..6a418b7aea1 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -298,7 +298,7 @@ pub mod domain { #[allow(clippy::too_many_lines)] fn is_token_domain_associated(permission: &PermissionToken, domain_id: &DomainId) -> bool { - let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { + let Ok(permission) = AnyPermissionToken::try_from(permission) else { return false; }; match permission { @@ -611,7 +611,7 @@ pub mod account { } fn is_token_account_associated(permission: &PermissionToken, account_id: &AccountId) -> bool { - let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { + let Ok(permission) = AnyPermissionToken::try_from(permission) else { return false; }; match permission { @@ -844,7 +844,7 @@ pub mod asset_definition { permission: &PermissionToken, asset_definition_id: &AssetDefinitionId, ) -> bool { - let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { + let Ok(permission) = AnyPermissionToken::try_from(permission) else { return false; }; match permission { @@ -995,7 +995,7 @@ pub mod asset { where V: Validate + ?Sized, Q: Into, - Mint: Instruction + Encode + Clone, + Mint: Instruction + Encode, { let asset_id = isi.destination_id(); if is_genesis(executor) { @@ -1053,7 +1053,7 @@ pub mod asset { where V: Validate + ?Sized, Q: Into, - Burn: Instruction + Encode + Clone, + Burn: Instruction + Encode, { let asset_id = isi.destination_id(); if is_genesis(executor) { @@ -1116,7 +1116,7 @@ pub mod asset { ) where V: Validate + ?Sized, Q: Into, - Transfer: Instruction + Encode + Clone, + Transfer: Instruction + Encode, { let asset_id = isi.source_id(); if is_genesis(executor) { @@ -1303,7 +1303,7 @@ pub mod role { let mut unknown_tokens = Vec::new(); if !is_genesis($executor) { for token in role.permissions() { - if let Ok(token) = AnyPermissionToken::try_from(token.clone()) { + if let Ok(token) = AnyPermissionToken::try_from(token) { if let Err(error) = permission::ValidateGrantRevoke::$method( &token, $authority, @@ -1340,7 +1340,7 @@ pub mod role { for token in role.permissions() { iroha_smart_contract::debug!(&format!("Checking `{token:?}`")); - if let Ok(any_token) = AnyPermissionToken::try_from(token.clone()) { + if let Ok(any_token) = AnyPermissionToken::try_from(token) { let token = PermissionToken::from(any_token); new_role = new_role.add_permission(token); continue; @@ -1527,7 +1527,7 @@ pub mod trigger { } fn is_token_trigger_associated(permission: &PermissionToken, trigger_id: &TriggerId) -> bool { - let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { + let Ok(permission) = AnyPermissionToken::try_from(permission) else { return false; }; match permission { @@ -1588,11 +1588,10 @@ pub mod permission_token { macro_rules! impl_validate { ($executor:ident, $authority:ident, $isi:ident, $method:ident, $isi_type:ty) => { - // TODO: https://github.com/hyperledger/iroha/issues/4082 - let token = $isi.object().clone(); let account_id = $isi.destination_id().clone(); + let token = $isi.object(); - if let Ok(any_token) = AnyPermissionToken::try_from(token.clone()) { + if let Ok(any_token) = AnyPermissionToken::try_from(token) { let token = PermissionToken::from(any_token.clone()); let isi = <$isi_type>::permission(token, account_id); if is_genesis($executor) { diff --git a/smart_contract/executor/src/default/tokens.rs b/smart_contract/executor/src/default/tokens.rs index 6e90f0fa6aa..92289ffa755 100644 --- a/smart_contract/executor/src/default/tokens.rs +++ b/smart_contract/executor/src/default/tokens.rs @@ -38,16 +38,14 @@ macro_rules! declare_tokens { /// Enum with every default token #[allow(clippy::enum_variant_names)] #[derive(Clone)] - pub(crate) enum AnyPermissionToken { - $( - $token_ty($($token_path::)+$token_ty), - )* + pub(crate) enum AnyPermissionToken { $( + $token_ty($($token_path::)+$token_ty), )* } - impl TryFrom<$crate::data_model::permission::PermissionToken> for AnyPermissionToken { + impl TryFrom<&$crate::data_model::permission::PermissionToken> for AnyPermissionToken { type Error = $crate::permission::PermissionTokenConversionError; - fn try_from(token: $crate::data_model::permission::PermissionToken) -> Result { + fn try_from(token: &$crate::data_model::permission::PermissionToken) -> Result { match token.definition_id().as_ref() { $( stringify!($token_ty) => { let token = <$($token_path::)+$token_ty>::try_from(token)?; @@ -60,29 +58,22 @@ macro_rules! declare_tokens { impl From for $crate::data_model::permission::PermissionToken { fn from(token: AnyPermissionToken) -> Self { - match token { - $( - AnyPermissionToken::$token_ty(token) => Self::from(token), - )* + match token { $( + AnyPermissionToken::$token_ty(token) => Self::from(token), )* } } } impl $crate::permission::ValidateGrantRevoke for AnyPermissionToken { fn validate_grant(&self, authority: &AccountId, block_height: u64) -> Result { - match self { - $( - AnyPermissionToken::$token_ty(token) => token.validate_grant(authority, block_height), - )* + match self { $( + AnyPermissionToken::$token_ty(token) => token.validate_grant(authority, block_height), )* } - } fn validate_revoke(&self, authority: &AccountId, block_height: u64) -> Result { - match self { - $( - AnyPermissionToken::$token_ty(token) => token.validate_revoke(authority, block_height), - )* + match self { $( + AnyPermissionToken::$token_ty(token) => token.validate_revoke(authority, block_height), )* } } } diff --git a/smart_contract/executor/src/permission.rs b/smart_contract/executor/src/permission.rs index d733d0c57c9..1bcbb66489f 100644 --- a/smart_contract/executor/src/permission.rs +++ b/smart_contract/executor/src/permission.rs @@ -11,12 +11,9 @@ use crate::{data_model::prelude::*, prelude::*}; /// [`Token`] trait is used to check if the token is owned by the account. pub trait Token: - Serialize - + DeserializeOwned - + IntoSchema - + TryFrom - + PartialEq - + ValidateGrantRevoke + Serialize + DeserializeOwned + IntoSchema + PartialEq + ValidateGrantRevoke +where + for<'a> Self: TryFrom<&'a PermissionToken, Error = PermissionTokenConversionError>, { /// Return name of this permission token fn name() -> Name { diff --git a/smart_contract/src/lib.rs b/smart_contract/src/lib.rs index 005b2c69338..1654b9e8bc5 100644 --- a/smart_contract/src/lib.rs +++ b/smart_contract/src/lib.rs @@ -112,21 +112,19 @@ pub trait ExecuteOnHost: Instruction { fn execute(&self) -> Result<(), ValidationFail>; } -// TODO: Remove the Clone bound. It can be done by custom serialization to InstructionExpr -impl ExecuteOnHost for I { +impl ExecuteOnHost for I { fn execute(&self) -> Result<(), ValidationFail> { #[cfg(not(test))] use host::execute_instruction as host_execute_instruction; #[cfg(test)] use tests::_iroha_smart_contract_execute_instruction_mock as host_execute_instruction; - // TODO: Redundant conversion into `InstructionExpr` - let isi_box: InstructionBox = self.clone().into(); + let bytes = self.encode_as_instruction_box(); // Safety: `host_execute_instruction` doesn't take ownership of it's pointer parameter unsafe { - decode_with_length_prefix_from_raw(encode_and_execute( - &isi_box, - host_execute_instruction, + decode_with_length_prefix_from_raw(host_execute_instruction( + bytes.as_ptr(), + bytes.len(), )) } }