From 8467ae5b84d4e3a6a83b275b9b14d2499886d208 Mon Sep 17 00:00:00 2001 From: Nikita Strygin Date: Tue, 19 Mar 2024 12:20:57 +0300 Subject: [PATCH 1/2] [refactor] #4259: Make Action and Filter non-generic types They were previously generic over the event filter type, but this was used only in iroha_core. Instead generic copies of these types are put into core, ultimetely simplifying the public API. Signed-off-by: Nikita Strygin --- client/src/client.rs | 2 +- .../integration/triggers/by_call_trigger.rs | 2 +- configs/swarm/executor.wasm | Bin 799930 -> 799833 bytes core/src/smartcontracts/isi/query.rs | 2 +- core/src/smartcontracts/isi/triggers/mod.rs | 22 +- core/src/smartcontracts/isi/triggers/set.rs | 186 ++++--------- .../isi/triggers/specialized.rs | 248 ++++++++++++++++++ core/src/wsv.rs | 3 +- data_model/src/events/data/events.rs | 2 +- data_model/src/isi.rs | 34 +-- data_model/src/lib.rs | 13 +- data_model/src/query/mod.rs | 6 +- data_model/src/trigger.rs | 111 ++------ data_model/src/visit.rs | 16 +- docs/source/references/schema.json | 26 +- schema/gen/src/lib.rs | 12 +- smart_contract/executor/derive/src/default.rs | 8 +- smart_contract/executor/src/default.rs | 8 +- tools/parity_scale_decoder/build.rs | 3 +- tools/parity_scale_decoder/src/main.rs | 8 +- 20 files changed, 401 insertions(+), 311 deletions(-) create mode 100644 core/src/smartcontracts/isi/triggers/specialized.rs diff --git a/client/src/client.rs b/client/src/client.rs index 4ec503bf9c2..ce942c1752c 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -323,7 +323,7 @@ impl_query_output! { crate::data_model::metadata::MetadataValueBox, crate::data_model::query::TransactionQueryOutput, crate::data_model::permission::PermissionTokenSchema, - crate::data_model::trigger::Trigger, + crate::data_model::trigger::Trigger, crate::data_model::prelude::Numeric, } diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index 6b4473d8f97..b5ae1402fe1 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -567,7 +567,7 @@ fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Numeric { fn build_register_trigger_isi( asset_id: AssetId, trigger_instructions: Vec, -) -> Register> { +) -> Register { let trigger_id: TriggerId = TRIGGER_NAME.parse().expect("Valid"); Register::trigger(Trigger::new( diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm index 9eabd2f39068c887b6ee4dcf2cd248213c6b655e..64f2555e48f689751eb26b750d3200fdb1586155 100755 GIT binary patch delta 73244 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 KqqW}#*8c+!vp@;} delta 72603 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, + iroha_data_model::trigger::Trigger, } /// Query Request statefully validated on the Iroha node side. diff --git a/core/src/smartcontracts/isi/triggers/mod.rs b/core/src/smartcontracts/isi/triggers/mod.rs index 4205286f724..8631e61b9ea 100644 --- a/core/src/smartcontracts/isi/triggers/mod.rs +++ b/core/src/smartcontracts/isi/triggers/mod.rs @@ -5,6 +5,7 @@ use iroha_data_model::{isi::error::MathError, prelude::*, query::error::FindErro use iroha_telemetry::metrics; pub mod set; +pub mod specialized; /// All instructions related to triggers. /// - registering a trigger @@ -20,7 +21,7 @@ pub mod isi { use super::{super::prelude::*, *}; - impl Execute for Register> { + impl Execute for Register { #[metrics(+"register_trigger")] fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { let new_trigger = self.object; @@ -79,7 +80,7 @@ pub mod isi { } } - impl Execute for Unregister> { + impl Execute for Unregister { #[metrics(+"unregister_trigger")] fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { let trigger_id = self.object_id.clone(); @@ -98,7 +99,7 @@ pub mod isi { } } - impl Execute for Mint> { + impl Execute for Mint { #[metrics(+"mint_trigger_repetitions")] fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { let id = self.destination_id; @@ -130,7 +131,7 @@ pub mod isi { } } - impl Execute for Burn> { + impl Execute for Burn { #[metrics(+"burn_trigger_repetitions")] fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { let trigger = self.destination_id; @@ -194,7 +195,6 @@ pub mod isi { pub mod query { //! Queries associated to triggers. use iroha_data_model::{ - events::TriggeringEventFilterBox, metadata::MetadataValueBox, query::error::QueryExecutionFail as Error, trigger::{Trigger, TriggerId}, @@ -215,10 +215,7 @@ pub mod query { impl ValidQuery for FindTriggerById { #[metrics(+"find_trigger_by_id")] - fn execute( - &self, - wsv: &WorldStateView, - ) -> Result, Error> { + fn execute(&self, wsv: &WorldStateView) -> Result { let id = &self.id; iroha_logger::trace!(%id); // Can't use just `LoadedActionTrait::clone_and_box` cause this will trigger lifetime mismatch @@ -228,7 +225,7 @@ pub mod query { .inspect_by_id(id, |action| action.clone_and_box()) .ok_or_else(|| Error::Find(FindError::Trigger(id.clone())))?; - let action = wsv.triggers().get_original_action(loaded_action); + let action = wsv.triggers().get_original_action(loaded_action).into(); // TODO: Should we redact the metadata if the account is not the authority/owner? Ok(Trigger::new(id.clone(), action)) @@ -259,8 +256,7 @@ pub mod query { fn execute<'wsv>( &self, wsv: &'wsv WorldStateView, - ) -> eyre::Result> + 'wsv>, Error> - { + ) -> eyre::Result + 'wsv>, Error> { let domain_id = &self.domain_id; Ok(Box::new( @@ -269,7 +265,7 @@ pub mod query { (trigger_id.clone(), action.clone_and_box()) }) .map(|(trigger_id, action)| { - let action = wsv.triggers().get_original_action(action); + let action = wsv.triggers().get_original_action(action).into(); Trigger::new(trigger_id, action) }), )) diff --git a/core/src/smartcontracts/isi/triggers/set.rs b/core/src/smartcontracts/isi/triggers/set.rs index dc1587848bb..f0e4028e9d4 100644 --- a/core/src/smartcontracts/isi/triggers/set.rs +++ b/core/src/smartcontracts/isi/triggers/set.rs @@ -19,7 +19,6 @@ use iroha_data_model::{ prelude::*, query::error::FindError, transaction::WasmSmartContract, - trigger::Trigger, }; use serde::{ de::{DeserializeSeed, MapAccess, Visitor}, @@ -28,7 +27,15 @@ use serde::{ }; use thiserror::Error; -use crate::{smartcontracts::wasm, wsv::WasmSeed}; +use crate::{ + smartcontracts::{ + isi::triggers::specialized::{ + LoadedAction, LoadedActionTrait, SpecializedAction, SpecializedTrigger, + }, + wasm, + }, + wsv::WasmSeed, +}; /// Error type for [`Set`] operations. #[derive(Debug, Error, displaydoc::Display)] @@ -40,110 +47,6 @@ pub enum Error { /// Result type for [`Set`] operations. pub type Result = core::result::Result; -/// Same as [`Action`](`iroha_data_model::trigger::Action`) but with -/// executable in pre-loaded form -#[derive(Clone, Debug)] -pub struct LoadedAction { - /// The executable linked to this action in loaded form - executable: LoadedExecutable, - /// The repeating scheme of the action. It's kept as part of the - /// action and not inside the [`Trigger`] type, so that further - /// sanity checking can be done. - pub repeats: Repeats, - /// Account executing this action - pub authority: AccountId, - /// Defines events which trigger the `Action` - pub filter: F, - /// Metadata used as persistent storage for trigger data. - pub metadata: Metadata, -} - -impl LoadedAction { - fn extract_blob_hash(&self) -> Option> { - match self.executable { - LoadedExecutable::Wasm(LoadedWasm { blob_hash, .. }) => Some(blob_hash), - LoadedExecutable::Instructions(_) => None, - } - } -} - -/// Trait common for all `LoadedAction`s -pub trait LoadedActionTrait { - /// Get action executable - fn executable(&self) -> &LoadedExecutable; - - /// Get action repeats enum - fn repeats(&self) -> &Repeats; - - /// Set action repeats - fn set_repeats(&mut self, repeats: Repeats); - - /// Get action technical account - fn authority(&self) -> &AccountId; - - /// Get action metadata - fn metadata(&self) -> &Metadata; - - /// Check if action is mintable. - fn mintable(&self) -> bool; - - /// Convert action to a boxed representation - fn into_boxed(self) -> LoadedAction; - - /// Same as [`into_boxed()`](LoadedActionTrait::into_boxed) but clones `self` - fn clone_and_box(&self) -> LoadedAction; -} - -impl + Clone> LoadedActionTrait - for LoadedAction -{ - fn executable(&self) -> &LoadedExecutable { - &self.executable - } - - fn repeats(&self) -> &iroha_data_model::trigger::action::Repeats { - &self.repeats - } - - fn set_repeats(&mut self, repeats: iroha_data_model::trigger::action::Repeats) { - self.repeats = repeats; - } - - fn authority(&self) -> &AccountId { - &self.authority - } - - fn metadata(&self) -> &Metadata { - &self.metadata - } - - fn mintable(&self) -> bool { - self.filter.mintable() - } - - fn into_boxed(self) -> LoadedAction { - let Self { - executable, - repeats, - authority, - filter, - metadata, - } = self; - - LoadedAction { - executable, - repeats, - authority, - filter: filter.into(), - metadata, - } - } - - fn clone_and_box(&self) -> LoadedAction { - self.clone().into_boxed() - } -} - /// [`WasmSmartContract`]s by [`TriggerId`]. /// Stored together with number to count triggers with identical [`WasmSmartContract`]. type WasmSmartContractMap = IndexMap, (WasmSmartContract, NonZeroU64)>; @@ -261,39 +164,49 @@ impl<'de> DeserializeSeed<'de> for WasmSeed<'_, Set> { while let Some(key) = map.next_key::()? { match key.as_str() { "data_triggers" => { - let triggers: IndexMap> = + let triggers: IndexMap> = map.next_value()?; for (id, action) in triggers { - set.add_data_trigger(self.loader.engine, Trigger::new(id, action)) - .unwrap(); + set.add_data_trigger( + self.loader.engine, + SpecializedTrigger::new(id, action), + ) + .unwrap(); } } "pipeline_triggers" => { - let triggers: IndexMap> = - map.next_value()?; + let triggers: IndexMap< + TriggerId, + SpecializedAction, + > = map.next_value()?; for (id, action) in triggers { set.add_pipeline_trigger( self.loader.engine, - Trigger::new(id, action), + SpecializedTrigger::new(id, action), ) .unwrap(); } } "time_triggers" => { - let triggers: IndexMap> = + let triggers: IndexMap> = map.next_value()?; for (id, action) in triggers { - set.add_time_trigger(self.loader.engine, Trigger::new(id, action)) - .unwrap(); + set.add_time_trigger( + self.loader.engine, + SpecializedTrigger::new(id, action), + ) + .unwrap(); } } "by_call_triggers" => { - let triggers: IndexMap> = - map.next_value()?; + let triggers: IndexMap< + TriggerId, + SpecializedAction, + > = map.next_value()?; for (id, action) in triggers { set.add_by_call_trigger( self.loader.engine, - Trigger::new(id, action), + SpecializedTrigger::new(id, action), ) .unwrap(); } @@ -339,7 +252,7 @@ impl Set { pub fn add_data_trigger( &mut self, engine: &wasmtime::Engine, - trigger: Trigger, + trigger: SpecializedTrigger, ) -> Result { self.add_to(engine, trigger, TriggeringEventType::Data, |me| { &mut me.data_triggers @@ -357,7 +270,7 @@ impl Set { pub fn add_pipeline_trigger( &mut self, engine: &wasmtime::Engine, - trigger: Trigger, + trigger: SpecializedTrigger, ) -> Result { self.add_to(engine, trigger, TriggeringEventType::Pipeline, |me| { &mut me.pipeline_triggers @@ -375,7 +288,7 @@ impl Set { pub fn add_time_trigger( &mut self, engine: &wasmtime::Engine, - trigger: Trigger, + trigger: SpecializedTrigger, ) -> Result { self.add_to(engine, trigger, TriggeringEventType::Time, |me| { &mut me.time_triggers @@ -393,7 +306,7 @@ impl Set { pub fn add_by_call_trigger( &mut self, engine: &wasmtime::Engine, - trigger: Trigger, + trigger: SpecializedTrigger, ) -> Result { self.add_to(engine, trigger, TriggeringEventType::ExecuteTrigger, |me| { &mut me.by_call_triggers @@ -410,23 +323,26 @@ impl Set { fn add_to( &mut self, engine: &wasmtime::Engine, - trigger: Trigger, + trigger: SpecializedTrigger, event_type: TriggeringEventType, map: impl FnOnce(&mut Self) -> &mut IndexMap>, ) -> Result { - if self.contains(trigger.id()) { + let SpecializedTrigger { + id: trigger_id, + action: + SpecializedAction { + executable, + repeats, + authority, + filter, + metadata, + }, + } = trigger; + + if self.contains(&trigger_id) { return Ok(false); } - let trigger_id = trigger.id; - let Action { - executable, - repeats, - authority, - filter, - metadata, - } = trigger.action; - let loaded_executable = match executable { Executable::Wasm(bytes) => { let hash = HashOf::new(&bytes); @@ -478,7 +394,7 @@ impl Set { /// Convert [`LoadedAction`] to original [`Action`] by retrieving original /// [`WasmSmartContract`] if applicable - pub fn get_original_action(&self, action: LoadedAction) -> Action { + pub fn get_original_action(&self, action: LoadedAction) -> SpecializedAction { let LoadedAction { executable, repeats, @@ -498,7 +414,7 @@ impl Set { LoadedExecutable::Instructions(isi) => Executable::Instructions(isi), }; - Action { + SpecializedAction { executable: original_executable, repeats, authority, diff --git a/core/src/smartcontracts/isi/triggers/specialized.rs b/core/src/smartcontracts/isi/triggers/specialized.rs new file mode 100644 index 00000000000..09e898b126d --- /dev/null +++ b/core/src/smartcontracts/isi/triggers/specialized.rs @@ -0,0 +1,248 @@ +//! Contains trigger-related types that are specialized for core-specific needs. + +use derive_more::Constructor; +use iroha_crypto::HashOf; +use iroha_data_model::{ + account::AccountId, + events::{EventFilter, TriggeringEventFilterBox}, + metadata::Metadata, + prelude::*, +}; +use serde::{Deserialize, Serialize}; + +use crate::smartcontracts::triggers::set::{LoadedExecutable, LoadedWasm}; + +/// Same as [`iroha_data_model::trigger::action::Action`] but generic over the filter type +/// +/// This is used to split different action types to different collections +#[derive(Serialize, Deserialize)] +pub struct SpecializedAction { + /// The executable linked to this action + pub executable: Executable, + /// The repeating scheme of the action. It's kept as part of the + /// action and not inside the [`iroha_data_model::trigger::Trigger`] type, so that further + /// sanity checking can be done. + pub repeats: Repeats, + /// Account executing this action + pub authority: AccountId, + /// Defines events which trigger the `Action` + pub filter: F, + /// Metadata used as persistent storage for trigger data. + pub metadata: Metadata, +} + +impl SpecializedAction { + /// Construct a specialized action given `executable`, `repeats`, `authority` and `filter`. + pub fn new( + executable: impl Into, + repeats: impl Into, + authority: AccountId, + filter: F, + ) -> Self { + Self { + executable: executable.into(), + repeats: repeats.into(), + // TODO: At this point the authority is meaningless. + authority, + filter, + metadata: Metadata::new(), + } + } +} + +impl From> for Action +where + F: Into, +{ + fn from(value: SpecializedAction) -> Self { + Action { + executable: value.executable, + repeats: value.repeats, + authority: value.authority, + filter: value.filter.into(), + metadata: value.metadata, + } + } +} + +/// Same as [`iroha_data_model::trigger::Trigger`] but generic over the filter type +#[derive(Constructor)] +pub struct SpecializedTrigger { + /// [`Id`] of the [`Trigger`]. + pub id: TriggerId, + /// Action to be performed when the trigger matches. + pub action: SpecializedAction, +} + +macro_rules! impl_try_from_box { + ($($variant:ident => $filter_type:ty),+ $(,)?) => { + $( + impl TryFrom for SpecializedTrigger<$filter_type> { + type Error = &'static str; + + fn try_from(boxed: Trigger) -> Result { + if let TriggeringEventFilterBox::$variant(concrete_filter) = boxed.action.filter { + let action = SpecializedAction::new( + boxed.action.executable, + boxed.action.repeats, + boxed.action.authority, + concrete_filter, + ); + Ok(Self { + id: boxed.id, + action, + }) + } else { + Err(concat!("Expected `TriggeringEventFilterBox::", stringify!($variant),"`, but another variant found")) + } + } + } + )+ + }; +} + +impl_try_from_box! { + Data => DataEventFilter, + Pipeline => PipelineEventFilter, + Time => TimeEventFilter, + ExecuteTrigger => ExecuteTriggerEventFilter, +} + +/// Same as [`iroha_data_model::trigger::action::Action`] but with +/// executable in pre-loaded form +#[derive(Clone, Debug)] +pub struct LoadedAction { + /// The executable linked to this action in loaded form + pub(super) executable: LoadedExecutable, + /// The repeating scheme of the action. It's kept as part of the + /// action and not inside the [`Trigger`] type, so that further + /// sanity checking can be done. + pub repeats: Repeats, + /// Account executing this action + pub authority: AccountId, + /// Defines events which trigger the `Action` + pub filter: F, + /// Metadata used as persistent storage for trigger data. + pub metadata: Metadata, +} + +impl LoadedAction { + pub(super) fn extract_blob_hash(&self) -> Option> { + match self.executable { + LoadedExecutable::Wasm(LoadedWasm { blob_hash, .. }) => Some(blob_hash), + LoadedExecutable::Instructions(_) => None, + } + } +} + +/// Trait common for all `LoadedAction`s +pub trait LoadedActionTrait { + /// Get action executable + fn executable(&self) -> &LoadedExecutable; + + /// Get action repeats enum + fn repeats(&self) -> &Repeats; + + /// Set action repeats + fn set_repeats(&mut self, repeats: Repeats); + + /// Get action technical account + fn authority(&self) -> &AccountId; + + /// Get action metadata + fn metadata(&self) -> &Metadata; + + /// Check if action is mintable. + fn mintable(&self) -> bool; + + /// Convert action to a boxed representation + fn into_boxed(self) -> LoadedAction; + + /// Same as [`into_boxed()`](LoadedActionTrait::into_boxed) but clones `self` + fn clone_and_box(&self) -> LoadedAction; +} + +impl + Clone> LoadedActionTrait + for LoadedAction +{ + fn executable(&self) -> &LoadedExecutable { + &self.executable + } + + fn repeats(&self) -> &iroha_data_model::trigger::action::Repeats { + &self.repeats + } + + fn set_repeats(&mut self, repeats: iroha_data_model::trigger::action::Repeats) { + self.repeats = repeats; + } + + fn authority(&self) -> &AccountId { + &self.authority + } + + fn metadata(&self) -> &Metadata { + &self.metadata + } + + fn mintable(&self) -> bool { + self.filter.mintable() + } + + fn into_boxed(self) -> LoadedAction { + let Self { + executable, + repeats, + authority, + filter, + metadata, + } = self; + + LoadedAction { + executable, + repeats, + authority, + filter: filter.into(), + metadata, + } + } + + fn clone_and_box(&self) -> LoadedAction { + self.clone().into_boxed() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn trigger_with_filterbox_can_be_unboxed() { + /// Should fail to compile if a new variant will be added to `TriggeringEventFilterBox` + #[allow(dead_code)] + fn compile_time_check(boxed: Trigger) { + match &boxed.action.filter { + TriggeringEventFilterBox::Data(_) => { + SpecializedTrigger::::try_from(boxed) + .map(|_| ()) + .unwrap() + } + TriggeringEventFilterBox::Pipeline(_) => { + SpecializedTrigger::::try_from(boxed) + .map(|_| ()) + .unwrap() + } + TriggeringEventFilterBox::Time(_) => { + SpecializedTrigger::::try_from(boxed) + .map(|_| ()) + .unwrap() + } + TriggeringEventFilterBox::ExecuteTrigger(_) => { + SpecializedTrigger::::try_from(boxed) + .map(|_| ()) + .unwrap() + } + } + } + } +} diff --git a/core/src/wsv.rs b/core/src/wsv.rs index fbe6730f77f..ec6f61cfed9 100644 --- a/core/src/wsv.rs +++ b/core/src/wsv.rs @@ -36,7 +36,8 @@ use crate::{ smartcontracts::{ triggers::{ self, - set::{LoadedActionTrait, LoadedWasm, Set as TriggerSet}, + set::{LoadedWasm, Set as TriggerSet}, + specialized::LoadedActionTrait, }, wasm, Execute, }, diff --git a/data_model/src/events/data/events.rs b/data_model/src/events/data/events.rs index 618007e6522..353eac1e28d 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -487,7 +487,7 @@ mod trigger { use super::*; data_event! { - #[has_origin(origin = Trigger)] + #[has_origin(origin = Trigger)] pub enum TriggerEvent { Created(TriggerId), Deleted(TriggerId), diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index 2ac25db4fa6..1ecd901aba7 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -147,21 +147,21 @@ impl_instruction! { Register, Register, Register, - Register>, + Register, Unregister, Unregister, Unregister, Unregister, Unregister, Unregister, - Unregister>, + Unregister, Mint, Mint, Mint, - Mint>, + Mint, Burn, Burn, - Burn>, + Burn, Transfer, Transfer, Transfer, @@ -485,9 +485,9 @@ mod transparent { } } - impl Register> { + impl Register { /// Constructs a new [`Register`] for a [`Trigger`]. - pub fn trigger(new_trigger: Trigger) -> Self { + pub fn trigger(new_trigger: Trigger) -> Self { Self { object: new_trigger, } @@ -511,7 +511,7 @@ mod transparent { Register | Register | Register | - Register> + Register => RegisterBox => InstructionBox[Register], => RegisterBoxRef<'a> => InstructionBoxRef<'a>[Register] } @@ -542,7 +542,7 @@ mod transparent { Unregister | Unregister | Unregister | - Unregister> + Unregister => UnregisterBox => InstructionBox[Unregister], => UnregisterBoxRef<'a> => InstructionBoxRef<'a>[Unregister] } @@ -597,7 +597,7 @@ mod transparent { } } - impl Unregister> { + impl Unregister { /// Constructs a new [`Unregister`] for a [`Trigger`]. pub fn trigger(trigger_id: TriggerId) -> Self { Self { @@ -650,7 +650,7 @@ mod transparent { } } - impl Mint> { + impl Mint { /// Constructs a new [`Mint`] for repetition count of [`Trigger`]. pub fn trigger_repetitions(repetitions: u32, trigger_id: TriggerId) -> Self { Self { @@ -683,7 +683,7 @@ mod transparent { Mint | Mint | Mint | - Mint> + Mint => MintBox => InstructionBox[Mint], => MintBoxRef<'a> => InstructionBoxRef<'a>[Mint] } @@ -719,7 +719,7 @@ mod transparent { } } - impl Burn> { + impl Burn { /// Constructs a new [`Burn`] for repetition count of [`Trigger`]. pub fn trigger_repetitions(repetitions: u32, trigger_id: TriggerId) -> Self { Self { @@ -744,7 +744,7 @@ mod transparent { impl_into_box! { Burn | Burn | - Burn> + Burn => BurnBox => InstructionBox[Burn], => BurnBoxRef<'a> => InstructionBoxRef<'a>[Burn] } @@ -1102,7 +1102,7 @@ isi_box! { /// Register [`Role`]. Role(Register), /// Register [`Trigger`]. - Trigger(Register>) + Trigger(Register) } } @@ -1127,7 +1127,7 @@ isi_box! { /// Unregister [`Role`]. Role(Unregister), /// Unregister [`Trigger`]. - Trigger(Unregister>) + Trigger(Unregister) } } @@ -1145,7 +1145,7 @@ isi_box! { /// Mint for [`Asset`]. Asset(Mint), /// Mint [`Trigger`] repetitions. - TriggerRepetitions(Mint>), + TriggerRepetitions(Mint), } } @@ -1177,7 +1177,7 @@ isi_box! { /// Burn [`Asset`]. Asset(Burn), /// Burn [`Trigger`] repetitions. - TriggerRepetitions(Burn>), + TriggerRepetitions(Burn), } } diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 06db2566b03..87352bead15 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -18,7 +18,6 @@ use alloc::{ use core::{fmt, fmt::Debug, ops::RangeInclusive, str::FromStr}; use derive_more::{Constructor, Display, From, FromStr}; -use events::TriggeringEventFilterBox; use getset::Getters; use iroha_crypto::PublicKey; use iroha_data_model_derive::{model, EnumRef, IdEqOrdHash}; @@ -86,7 +85,7 @@ mod seal { Register, Register, Register, - Register >, + Register, Unregister, Unregister, @@ -94,16 +93,16 @@ mod seal { Unregister, Unregister, Unregister, - Unregister >, + Unregister, Mint, Mint, Mint, - Mint >, + Mint, Burn, Burn, - Burn >, + Burn, Transfer, Transfer, @@ -722,7 +721,7 @@ pub mod model { /// [`Asset`](`asset::Asset`) variant. Asset(asset::Asset), /// [`Trigger`](`trigger::Trigger`) variant. - Trigger(trigger::Trigger), + Trigger(trigger::Trigger), /// [`Role`](`role::Role`) variant. Role(role::Role), /// [`Parameter`](`parameter::Parameter`) variant. @@ -916,7 +915,7 @@ impl_encode_as_identifiable_box! { account::Account, asset::AssetDefinition, asset::Asset, - trigger::Trigger, + trigger::Trigger, role::Role, parameter::Parameter, } diff --git a/data_model/src/query/mod.rs b/data_model/src/query/mod.rs index b3d3cc6dded..7fdb0b9a149 100644 --- a/data_model/src/query/mod.rs +++ b/data_model/src/query/mod.rs @@ -364,9 +364,9 @@ impl_query! { FindAllPeers => Vec, FindAllParameters => Vec, FindAllActiveTriggerIds => Vec, - FindTriggerById => crate::trigger::Trigger, + FindTriggerById => crate::trigger::Trigger, FindTriggerKeyValueByIdAndKey => MetadataValueBox, - FindTriggersByDomainId => Vec>, + FindTriggersByDomainId => Vec, FindAllTransactions => Vec, FindTransactionsByAccountId => Vec, FindTransactionByHash => TransactionQueryOutput, @@ -477,7 +477,7 @@ from_and_try_from_value_identifiable!( Account(crate::account::Account), AssetDefinition(crate::asset::AssetDefinition), Asset(crate::asset::Asset), - Trigger(crate::trigger::Trigger), + Trigger(crate::trigger::Trigger), Role(crate::role::Role), Parameter(crate::parameter::Parameter), ); diff --git a/data_model/src/trigger.rs b/data_model/src/trigger.rs index df53f68707b..fb0ed684981 100644 --- a/data_model/src/trigger.rs +++ b/data_model/src/trigger.rs @@ -1,5 +1,8 @@ //! Structures traits and impls related to `Trigger`s. +// If editing this file, consider updating `core/src/smartcontracts/isi/triggers/specialized.rs` +// It mirrors structures from this file. + #[cfg(not(feature = "std"))] use alloc::{format, string::String, vec::Vec}; use core::{cmp, str::FromStr}; @@ -51,78 +54,41 @@ pub mod model { /// Type which is used for registering a `Trigger`. #[derive( - Debug, - Display, - Clone, - IdEqOrdHash, - Constructor, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, + Debug, Display, Clone, IdEqOrdHash, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[display(fmt = "@@{id}")] #[ffi_type] - pub struct Trigger { + pub struct Trigger { /// [`Id`] of the [`Trigger`]. pub id: TriggerId, /// Action to be performed when the trigger matches. - pub action: action::Action, + pub action: action::Action, } } #[ffi_impl_opaque] -impl Trigger { +impl Trigger { + // we can derive this with `derive_more::Constructor`, but RustRover freaks out and thinks it's signature is (TriggerId, TriggerId, Action, Action), giving bogus errors + /// Construct a trigger given `id` and `action`. + pub fn new(id: TriggerId, action: action::Action) -> Trigger { + Trigger { id, action } + } + /// [`Id`] of the [`Trigger`]. pub fn id(&self) -> &TriggerId { &self.id } /// Action to be performed when the trigger matches. - pub fn action(&self) -> &action::Action { + pub fn action(&self) -> &action::Action { &self.action } } -impl Registered for Trigger { +impl Registered for Trigger { type With = Self; } -macro_rules! impl_try_from_box { - ($($variant:ident => $filter_type:ty),+ $(,)?) => { - $( - impl TryFrom> for Trigger<$filter_type> { - type Error = &'static str; - - fn try_from(boxed: Trigger) -> Result { - if let TriggeringEventFilterBox::$variant(concrete_filter) = boxed.action.filter { - let action = action::Action::new( - boxed.action.executable, - boxed.action.repeats, - boxed.action.authority, - concrete_filter, - ); - Ok(Self { - id: boxed.id, - action, - }) - } else { - Err(concat!("Expected `TriggeringEventFilterBox::", stringify!($variant),"`, but another variant found")) - } - } - } - )+ - }; -} - -impl_try_from_box! { - Data => DataEventFilter, - Pipeline => PipelineEventFilter, - Time => TimeEventFilter, - ExecuteTrigger => ExecuteTriggerEventFilter, -} - impl core::fmt::Display for TriggerId { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { if let Some(ref domain_id) = self.domain_id { @@ -186,7 +152,7 @@ pub mod action { Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[ffi_type] - pub struct Action { + pub struct Action { /// The executable linked to this action pub executable: Executable, /// The repeating scheme of the action. It's kept as part of the @@ -196,7 +162,7 @@ pub mod action { /// Account executing this action pub authority: AccountId, /// Defines events which trigger the `Action` - pub filter: F, + pub filter: TriggeringEventFilterBox, /// Metadata used as persistent storage for trigger data. pub metadata: Metadata, } @@ -215,14 +181,14 @@ pub mod action { } #[cfg(feature = "transparent_api")] - impl crate::HasMetadata for Action { + impl crate::HasMetadata for Action { fn metadata(&self) -> &crate::metadata::Metadata { &self.metadata } } #[ffi_impl_opaque] - impl Action { + impl Action { /// The executable linked to this action pub fn executable(&self) -> &Executable { &self.executable @@ -243,13 +209,13 @@ pub mod action { } } - impl Action { + impl Action { /// Construct an action given `executable`, `repeats`, `authority` and `filter`. pub fn new( executable: impl Into, repeats: impl Into, authority: AccountId, - filter: F, + filter: TriggeringEventFilterBox, ) -> Self { Self { executable: executable.into(), @@ -269,7 +235,7 @@ pub mod action { } } - impl PartialOrd for Action { + impl PartialOrd for Action { fn partial_cmp(&self, other: &Self) -> Option { // Exclude the executable. When debugging and replacing // the trigger, its position in Hash and Tree maps should @@ -282,7 +248,7 @@ pub mod action { } } - impl Ord for Action { + impl Ord for Action { fn cmp(&self, other: &Self) -> cmp::Ordering { self.partial_cmp(other) .expect("`PartialCmp::partial_cmp()` for `Action` should never return `None`") @@ -323,34 +289,3 @@ pub mod prelude { pub use super::{action::prelude::*, Trigger, TriggerId}; } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn trigger_with_filterbox_can_be_unboxed() { - /// Should fail to compile if a new variant will be added to `TriggeringEventFilterBox` - #[allow(dead_code)] - fn compile_time_check(boxed: Trigger) { - match &boxed.action.filter { - TriggeringEventFilterBox::Data(_) => Trigger::::try_from(boxed) - .map(|_| ()) - .unwrap(), - TriggeringEventFilterBox::Pipeline(_) => { - Trigger::::try_from(boxed) - .map(|_| ()) - .unwrap() - } - TriggeringEventFilterBox::Time(_) => Trigger::::try_from(boxed) - .map(|_| ()) - .unwrap(), - TriggeringEventFilterBox::ExecuteTrigger(_) => { - Trigger::::try_from(boxed) - .map(|_| ()) - .unwrap() - } - } - } - } -} diff --git a/data_model/src/visit.rs b/data_model/src/visit.rs index 56abc38f676..b626e8bcde7 100644 --- a/data_model/src/visit.rs +++ b/data_model/src/visit.rs @@ -94,7 +94,7 @@ pub trait Visit { visit_register_asset_definition(&Register), visit_register_asset(&Register), visit_register_role(&Register), - visit_register_trigger(&Register>), + visit_register_trigger(&Register), // Visit UnregisterBox visit_unregister_peer(&Unregister), @@ -104,18 +104,18 @@ pub trait Visit { visit_unregister_asset(&Unregister), // TODO: Need to allow role creator to unregister it somehow visit_unregister_role(&Unregister), - visit_unregister_trigger(&Unregister>), + visit_unregister_trigger(&Unregister), // Visit MintBox visit_mint_asset_numeric(&Mint), visit_mint_account_public_key(&Mint), visit_mint_account_signature_check_condition(&Mint), - visit_mint_trigger_repetitions(&Mint>), + visit_mint_trigger_repetitions(&Mint), // Visit BurnBox visit_burn_account_public_key(&Burn), visit_burn_asset_numeric(&Burn), - visit_burn_trigger_repetitions(&Burn>), + visit_burn_trigger_repetitions(&Burn), // Visit TransferBox visit_transfer_asset_definition(&Transfer), @@ -429,10 +429,10 @@ leaf_visitors! { visit_revoke_account_role(&Revoke), visit_grant_role_permission(&Grant), visit_revoke_role_permission(&Revoke), - visit_register_trigger(&Register>), - visit_unregister_trigger(&Unregister>), - visit_mint_trigger_repetitions(&Mint>), - visit_burn_trigger_repetitions(&Burn>), + visit_register_trigger(&Register), + visit_unregister_trigger(&Unregister), + visit_mint_trigger_repetitions(&Mint), + visit_burn_trigger_repetitions(&Burn), visit_upgrade(&Upgrade), visit_new_parameter(&NewParameter), visit_set_parameter(&SetParameter), diff --git a/docs/source/references/schema.json b/docs/source/references/schema.json index d5afc569ba2..b6d93a29fdc 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -145,7 +145,7 @@ } ] }, - "Action": { + "Action": { "Struct": [ { "name": "executable", @@ -562,7 +562,7 @@ } ] }, - "Burn>": { + "Burn": { "Struct": [ { "name": "object", @@ -589,7 +589,7 @@ { "tag": "TriggerRepetitions", "discriminant": 2, - "type": "Burn>" + "type": "Burn" } ] }, @@ -1525,7 +1525,7 @@ { "tag": "Trigger", "discriminant": 9, - "type": "Trigger" + "type": "Trigger" }, { "tag": "Role", @@ -2053,7 +2053,7 @@ } ] }, - "Mint>": { + "Mint": { "Struct": [ { "name": "object", @@ -2080,7 +2080,7 @@ { "tag": "TriggerRepetitions", "discriminant": 2, - "type": "Mint>" + "type": "Mint" } ] }, @@ -2898,11 +2898,11 @@ } ] }, - "Register>": { + "Register": { "Struct": [ { "name": "object", - "type": "Trigger" + "type": "Trigger" } ] }, @@ -2941,7 +2941,7 @@ { "tag": "Trigger", "discriminant": 6, - "type": "Register>" + "type": "Register" } ] }, @@ -3766,7 +3766,7 @@ } ] }, - "Trigger": { + "Trigger": { "Struct": [ { "name": "id", @@ -3774,7 +3774,7 @@ }, { "name": "action", - "type": "Action" + "type": "Action" } ] }, @@ -3979,7 +3979,7 @@ } ] }, - "Unregister>": { + "Unregister": { "Struct": [ { "name": "object_id", @@ -4022,7 +4022,7 @@ { "tag": "Trigger", "discriminant": 6, - "type": "Unregister>" + "type": "Unregister" } ] }, diff --git a/schema/gen/src/lib.rs b/schema/gen/src/lib.rs index 3fca6d4c61c..ebfb956f436 100644 --- a/schema/gen/src/lib.rs +++ b/schema/gen/src/lib.rs @@ -66,7 +66,7 @@ types!( AccountMintBox, AccountPermissionChanged, AccountRoleChanged, - Action, + Action, Algorithm, Asset, AssetChanged, @@ -103,7 +103,7 @@ types!( BlockSubscriptionRequest, Box>, Box, - Burn>, + Burn, Burn, Burn, BurnBox, @@ -212,7 +212,7 @@ types!( MetadataError, MetadataLimits, MetadataValueBox, - Mint>, + Mint, Mint, Mint, Mint, @@ -284,7 +284,7 @@ types!( Register, Register, Register, - Register>, + Register, RegisterBox, RemoveKeyValue, RemoveKeyValue, @@ -349,7 +349,7 @@ types!( Transfer, Transfer, TransferBox, - Trigger, + Trigger, TriggerCompletedEvent, TriggerCompletedEventFilter, TriggerCompletedOutcome, @@ -368,7 +368,7 @@ types!( Unregister, Unregister, Unregister, - Unregister>, + Unregister, UnregisterBox, Upgrade, ValidationFail, diff --git a/smart_contract/executor/derive/src/default.rs b/smart_contract/executor/derive/src/default.rs index af341e733d3..441bc8d7c87 100644 --- a/smart_contract/executor/derive/src/default.rs +++ b/smart_contract/executor/derive/src/default.rs @@ -151,10 +151,10 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn::DeriveInput) -> Tok "fn visit_revoke_account_role(operation: &Revoke)", "fn visit_grant_role_permission(operation: &Grant)", "fn visit_revoke_role_permission(operation: &Revoke)", - "fn visit_register_trigger(operation: &Register>)", - "fn visit_unregister_trigger(operation: &Unregister>)", - "fn visit_mint_trigger_repetitions(operation: &Mint>)", - "fn visit_burn_trigger_repetitions(operation: &Burn>)", + "fn visit_register_trigger(operation: &Register)", + "fn visit_unregister_trigger(operation: &Unregister)", + "fn visit_mint_trigger_repetitions(operation: &Mint)", + "fn visit_burn_trigger_repetitions(operation: &Burn)", "fn visit_execute_trigger(operation: &ExecuteTrigger)", "fn visit_set_parameter(operation: &SetParameter)", "fn visit_new_parameter(operation: &NewParameter)", diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index e7e9a2645e9..c409a082878 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -1403,7 +1403,7 @@ pub mod trigger { pub fn visit_register_trigger( executor: &mut V, _authority: &AccountId, - isi: &Register>, + isi: &Register, ) { execute!(executor, isi) } @@ -1411,7 +1411,7 @@ pub mod trigger { pub fn visit_unregister_trigger( executor: &mut V, authority: &AccountId, - isi: &Unregister>, + isi: &Unregister, ) { let trigger_id = isi.object_id(); @@ -1446,7 +1446,7 @@ pub mod trigger { pub fn visit_mint_trigger_repetitions( executor: &mut V, authority: &AccountId, - isi: &Mint>, + isi: &Mint, ) { let trigger_id = isi.destination_id(); @@ -1474,7 +1474,7 @@ pub mod trigger { pub fn visit_burn_trigger_repetitions( executor: &mut V, authority: &AccountId, - isi: &Burn>, + isi: &Burn, ) { let trigger_id = isi.destination_id(); diff --git a/tools/parity_scale_decoder/build.rs b/tools/parity_scale_decoder/build.rs index cab5c555b19..5ad93926c7d 100644 --- a/tools/parity_scale_decoder/build.rs +++ b/tools/parity_scale_decoder/build.rs @@ -12,8 +12,7 @@ fn main() { sample_into_binary_file::("domain").expect("Failed to encode into domain.bin."); - sample_into_binary_file::>("trigger") - .expect("Failed to encode into trigger.bin."); + sample_into_binary_file::("trigger").expect("Failed to encode into trigger.bin."); } fn sample_into_binary_file(filename: &str) -> Result<()> diff --git a/tools/parity_scale_decoder/src/main.rs b/tools/parity_scale_decoder/src/main.rs index 86f3139d391..511bdc37cf5 100644 --- a/tools/parity_scale_decoder/src/main.rs +++ b/tools/parity_scale_decoder/src/main.rs @@ -275,17 +275,13 @@ mod tests { vec![Mint::asset_numeric(1u32, rose_id)], Repeats::Indefinitely, account_id, - EventFilterBox::Data(DataEventFilter::Domain( + TriggeringEventFilterBox::Data(DataEventFilter::Domain( DomainEventFilter::new().for_events(DomainEventSet::AnyAccount), )), ); let trigger = Trigger::new(trigger_id, action); - decode_sample( - "trigger.bin", - String::from("Trigger"), - &trigger, - ); + decode_sample("trigger.bin", String::from("Trigger"), &trigger); } fn decode_sample(sample_path: &str, type_id: String, expected: &T) { From efde3762dd5abd04e7418e9d64ad9c0df918c722 Mon Sep 17 00:00:00 2001 From: Nikita Strygin Date: Tue, 19 Mar 2024 12:57:18 +0300 Subject: [PATCH 2/2] [refactor] #4259: Use conversions when constructing `Action`s Make Action's constructor accept `impl Into`. Add shorthand conversions from concrete data event filter types into the top-level boxes Signed-off-by: Nikita Strygin --- .../integration/domain_owner_permissions.rs | 4 +- .../tests/integration/events/notification.rs | 16 ++-- .../integration/triggers/by_call_trigger.rs | 85 +++++++------------ .../integration/triggers/data_trigger.rs | 12 +-- .../integration/triggers/event_trigger.rs | 4 +- .../integration/triggers/time_trigger.rs | 8 +- .../integration/triggers/trigger_rollback.rs | 8 +- core/src/smartcontracts/isi/mod.rs | 8 +- data_model/src/events/mod.rs | 51 +++++++++++ data_model/src/trigger.rs | 4 +- tools/parity_scale_decoder/src/main.rs | 4 +- 11 files changed, 107 insertions(+), 97 deletions(-) diff --git a/client/tests/integration/domain_owner_permissions.rs b/client/tests/integration/domain_owner_permissions.rs index b26eb647728..e0945b85f70 100644 --- a/client/tests/integration/domain_owner_permissions.rs +++ b/client/tests/integration/domain_owner_permissions.rs @@ -309,9 +309,7 @@ fn domain_owner_trigger_permissions() -> Result<()> { trigger_instructions, Repeats::from(2_u32), bob_id, - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new().for_trigger(trigger_id.clone()), - ), + ExecuteTriggerEventFilter::new().for_trigger(trigger_id.clone()), ), )); test_client.submit_blocking(register_trigger)?; diff --git a/client/tests/integration/events/notification.rs b/client/tests/integration/events/notification.rs index e7738e04aaa..bf26feb351b 100644 --- a/client/tests/integration/events/notification.rs +++ b/client/tests/integration/events/notification.rs @@ -21,11 +21,9 @@ fn trigger_completion_success_should_produce_event() -> Result<()> { vec![InstructionBox::from(instruction)], Repeats::Indefinitely, asset_id.account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(asset_id.account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(asset_id.account_id), ), )); test_client.submit_blocking(register_trigger)?; @@ -69,11 +67,9 @@ fn trigger_completion_failure_should_produce_event() -> Result<()> { vec![InstructionBox::from(instruction)], Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), ), )); test_client.submit_blocking(register_trigger)?; diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index b5ae1402fe1..a2c2ac2b41d 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -6,10 +6,9 @@ use iroha_client::{ data_model::{ prelude::*, query::error::{FindError, QueryExecutionFail}, - transaction::Executable, + transaction::{Executable, WasmSmartContract}, }, }; -use iroha_data_model::{events::TriggeringEventFilterBox, transaction::WasmSmartContract}; use iroha_genesis::GenesisNetwork; use iroha_logger::info; use test_network::*; @@ -124,11 +123,9 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { bad_trigger_instructions, Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(bad_trigger_id.clone()) - .under_authority(account_id.clone()), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(bad_trigger_id.clone()) + .under_authority(account_id.clone()), ), )); test_client.submit(register_bad_trigger)?; @@ -143,7 +140,7 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { Repeats::Indefinitely, account_id, // Time-triggers (which are Pre-commit triggers) will be executed last - TriggeringEventFilterBox::Time(TimeEventFilter::new(ExecutionTime::PreCommit)), + TimeEventFilter::new(ExecutionTime::PreCommit), ), )); test_client.submit_blocking(register_trigger)?; @@ -180,11 +177,9 @@ fn trigger_should_not_be_executed_with_zero_repeats_count() -> Result<()> { trigger_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), ), )); test_client.submit_blocking(register_trigger)?; @@ -245,11 +240,9 @@ fn trigger_should_be_able_to_modify_its_own_repeats_count() -> Result<()> { trigger_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), ), )); test_client.submit_blocking(register_trigger)?; @@ -289,11 +282,9 @@ fn unregister_trigger() -> Result<()> { Vec::::new(), Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), ), ); let register_trigger = Register::trigger(trigger.clone()); @@ -367,11 +358,9 @@ fn trigger_in_genesis_using_base64() -> Result<()> { .wrap_err("Can't deserialize wasm using base64")?, Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(account_id.clone()), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id.clone()), ), ); @@ -418,11 +407,9 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { trigger_unregister_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id_unregister.clone()) - .under_authority(account_id.clone()), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id_unregister.clone()) + .under_authority(account_id.clone()), ), )); test_client.submit_blocking(register_trigger)?; @@ -435,11 +422,9 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { trigger_should_be_unregistered_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id_to_be_unregistered.clone()) - .under_authority(account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id_to_be_unregistered.clone()) + .under_authority(account_id), ), )); test_client.submit_blocking(register_trigger)?; @@ -480,11 +465,9 @@ fn trigger_burn_repetitions() -> Result<()> { trigger_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), ), )); test_client.submit_blocking(register_trigger)?; @@ -525,11 +508,9 @@ fn unregistering_one_of_two_triggers_with_identical_wasm_should_not_cause_origin wasm.clone(), Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id) - .under_authority(account_id.clone()), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id) + .under_authority(account_id.clone()), ), ) }; @@ -576,11 +557,9 @@ fn build_register_trigger_isi( trigger_instructions, Repeats::Indefinitely, asset_id.account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id) - .under_authority(asset_id.account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id) + .under_authority(asset_id.account_id), ), )) } diff --git a/client/tests/integration/triggers/data_trigger.rs b/client/tests/integration/triggers/data_trigger.rs index decbb865cd5..46f505a9f9f 100644 --- a/client/tests/integration/triggers/data_trigger.rs +++ b/client/tests/integration/triggers/data_trigger.rs @@ -23,9 +23,7 @@ fn must_execute_both_triggers() -> Result<()> { [instruction.clone()], Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::Data(DataEventFilter::Account( - AccountEventFilter::new().for_events(AccountEventSet::Created), - )), + AccountEventFilter::new().for_events(AccountEventSet::Created), ), )); test_client.submit_blocking(register_trigger)?; @@ -36,9 +34,7 @@ fn must_execute_both_triggers() -> Result<()> { [instruction], Repeats::Indefinitely, account_id, - TriggeringEventFilterBox::Data(DataEventFilter::Domain( - DomainEventFilter::new().for_events(DomainEventSet::Created), - )), + DomainEventFilter::new().for_events(DomainEventSet::Created), ), )); test_client.submit_blocking(register_trigger)?; @@ -88,9 +84,7 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu [Mint::asset_numeric(1u32, asset_id.clone())], Repeats::Indefinitely, account_id, - TriggeringEventFilterBox::Data(DataEventFilter::Account( - AccountEventFilter::new().for_events(AccountEventSet::Created), - )), + AccountEventFilter::new().for_events(AccountEventSet::Created), ), )); test_client.submit_blocking(register_trigger)?; diff --git a/client/tests/integration/triggers/event_trigger.rs b/client/tests/integration/triggers/event_trigger.rs index a153b262464..12a5dca633c 100644 --- a/client/tests/integration/triggers/event_trigger.rs +++ b/client/tests/integration/triggers/event_trigger.rs @@ -24,9 +24,7 @@ fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { vec![instruction], Repeats::Indefinitely, account_id, - TriggeringEventFilterBox::Data(DataEventFilter::AssetDefinition( - AssetDefinitionEventFilter::new().for_events(AssetDefinitionEventSet::Created), - )), + AssetDefinitionEventFilter::new().for_events(AssetDefinitionEventSet::Created), ), )); test_client.submit(register_trigger)?; diff --git a/client/tests/integration/triggers/time_trigger.rs b/client/tests/integration/triggers/time_trigger.rs index 06042776200..1f29a0d8ba9 100644 --- a/client/tests/integration/triggers/time_trigger.rs +++ b/client/tests/integration/triggers/time_trigger.rs @@ -52,7 +52,7 @@ fn time_trigger_execution_count_error_should_be_less_than_15_percent() -> Result vec![instruction], Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::Time(TimeEventFilter::new(ExecutionTime::Schedule(schedule))), + TimeEventFilter::new(ExecutionTime::Schedule(schedule)), ), )); test_client.submit(register_trigger)?; @@ -110,7 +110,7 @@ fn change_asset_metadata_after_1_sec() -> Result<()> { vec![instruction], Repeats::from(1_u32), account_id.clone(), - TriggeringEventFilterBox::Time(TimeEventFilter::new(ExecutionTime::Schedule(schedule))), + TimeEventFilter::new(ExecutionTime::Schedule(schedule)), ), )); test_client.submit(register_trigger)?; @@ -154,7 +154,7 @@ fn pre_commit_trigger_should_be_executed() -> Result<()> { vec![instruction], Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::Time(TimeEventFilter::new(ExecutionTime::PreCommit)), + TimeEventFilter::new(ExecutionTime::PreCommit), ), )); test_client.submit(register_trigger)?; @@ -229,7 +229,7 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { WasmSmartContract::from_compiled(wasm), Repeats::Indefinitely, alice_id.clone(), - TriggeringEventFilterBox::Time(TimeEventFilter::new(ExecutionTime::Schedule(schedule))), + TimeEventFilter::new(ExecutionTime::Schedule(schedule)), ), )); test_client.submit(register_trigger)?; diff --git a/client/tests/integration/triggers/trigger_rollback.rs b/client/tests/integration/triggers/trigger_rollback.rs index 2bbcd387699..6f61cae9835 100644 --- a/client/tests/integration/triggers/trigger_rollback.rs +++ b/client/tests/integration/triggers/trigger_rollback.rs @@ -28,11 +28,9 @@ fn failed_trigger_revert() -> Result<()> { instructions, Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(account_id), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), ), )); let _ = client.submit_blocking(register_trigger); diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 8218f0bb994..16dd365a157 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -378,11 +378,9 @@ mod tests { Vec::::new(), Repeats::Indefinitely, account_id.clone(), - TriggeringEventFilterBox::ExecuteTrigger( - ExecuteTriggerEventFilter::new() - .for_trigger(trigger_id.clone()) - .under_authority(account_id.clone()), - ), + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id.clone()), ), )); diff --git a/data_model/src/events/mod.rs b/data_model/src/events/mod.rs index daa9e544e26..94c003526bc 100644 --- a/data_model/src/events/mod.rs +++ b/data_model/src/events/mod.rs @@ -209,6 +209,57 @@ impl EventFilter for TriggeringEventFilterBox { } } +mod conversions { + use super::prelude::*; + + macro_rules! last_tt { + ($last:tt) => { + $last + }; + ($head:tt $($tail:tt)+) => { + last_tt!($($tail)*) + }; + } + + // chain multiple conversions into one + macro_rules! impl_from_via_path { + ($($initial:ty $(=> $intermediate:ty)*),+ $(,)?) => { + $( + impl From<$initial> for last_tt!($($intermediate)*) { + fn from(filter: $initial) -> Self { + $( + let filter: $intermediate = filter.into(); + )* + filter + } + } + )+ + }; + } + + impl_from_via_path! { + PeerEventFilter => DataEventFilter => EventFilterBox, + DomainEventFilter => DataEventFilter => EventFilterBox, + AccountEventFilter => DataEventFilter => EventFilterBox, + AssetEventFilter => DataEventFilter => EventFilterBox, + AssetDefinitionEventFilter => DataEventFilter => EventFilterBox, + TriggerEventFilter => DataEventFilter => EventFilterBox, + RoleEventFilter => DataEventFilter => EventFilterBox, + ConfigurationEventFilter => DataEventFilter => EventFilterBox, + ExecutorEventFilter => DataEventFilter => EventFilterBox, + + PeerEventFilter => DataEventFilter => TriggeringEventFilterBox, + DomainEventFilter => DataEventFilter => TriggeringEventFilterBox, + AccountEventFilter => DataEventFilter => TriggeringEventFilterBox, + AssetEventFilter => DataEventFilter => TriggeringEventFilterBox, + AssetDefinitionEventFilter => DataEventFilter => TriggeringEventFilterBox, + TriggerEventFilter => DataEventFilter => TriggeringEventFilterBox, + RoleEventFilter => DataEventFilter => TriggeringEventFilterBox, + ConfigurationEventFilter => DataEventFilter => TriggeringEventFilterBox, + ExecutorEventFilter => DataEventFilter => TriggeringEventFilterBox, + } +} + #[cfg(feature = "http")] pub mod stream { //! Structures related to event streaming over HTTP diff --git a/data_model/src/trigger.rs b/data_model/src/trigger.rs index fb0ed684981..e65cf5f4a96 100644 --- a/data_model/src/trigger.rs +++ b/data_model/src/trigger.rs @@ -215,14 +215,14 @@ pub mod action { executable: impl Into, repeats: impl Into, authority: AccountId, - filter: TriggeringEventFilterBox, + filter: impl Into, ) -> Self { Self { executable: executable.into(), repeats: repeats.into(), // TODO: At this point the authority is meaningless. authority, - filter, + filter: filter.into(), metadata: Metadata::new(), } } diff --git a/tools/parity_scale_decoder/src/main.rs b/tools/parity_scale_decoder/src/main.rs index 511bdc37cf5..9bf3a824693 100644 --- a/tools/parity_scale_decoder/src/main.rs +++ b/tools/parity_scale_decoder/src/main.rs @@ -275,9 +275,7 @@ mod tests { vec![Mint::asset_numeric(1u32, rose_id)], Repeats::Indefinitely, account_id, - TriggeringEventFilterBox::Data(DataEventFilter::Domain( - DomainEventFilter::new().for_events(DomainEventSet::AnyAccount), - )), + DomainEventFilter::new().for_events(DomainEventSet::AnyAccount), ); let trigger = Trigger::new(trigger_id, action);