diff --git a/client/benches/tps/utils.rs b/client/benches/tps/utils.rs index c00aed40439..f9c452980c7 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -172,9 +172,8 @@ impl MeasurerUnit { let listener = self.client.clone(); let (init_sender, init_receiver) = mpsc::channel(); let event_filter = PipelineEventFilter::new() - .entity_kind(PipelineEntityKind::Block) - .status_kind(PipelineStatusKind::Committed) - .into(); + .for_entity(PipelineEntityKind::Block) + .for_status(PipelineStatusKind::Committed); let blocks_expected = self.config.blocks as usize; let name = self.name; let handle = thread::spawn(move || -> Result<()> { diff --git a/client/src/client.rs b/client/src/client.rs index 789f6ef7a76..4ec503bf9c2 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, } @@ -605,9 +605,7 @@ impl Client { let mut event_iterator = { let event_iterator_result = tokio::time::timeout_at( deadline, - self.listen_for_events_async( - PipelineEventFilter::new().hash(hash.into()).into(), - ), + self.listen_for_events_async(PipelineEventFilter::new().for_hash(hash.into())), ) .await .map_err(Into::into) @@ -905,8 +903,9 @@ impl Client { /// - Forwards from [`events_api::EventIterator::new`] pub fn listen_for_events( &self, - event_filter: FilterBox, + event_filter: impl Into, ) -> Result>> { + let event_filter = event_filter.into(); iroha_logger::trace!(?event_filter); events_api::EventIterator::new(self.events_handler(event_filter)?) } @@ -918,8 +917,9 @@ impl Client { /// - Forwards from [`events_api::AsyncEventStream::new`] pub async fn listen_for_events_async( &self, - event_filter: FilterBox, + event_filter: impl Into + Send, ) -> Result { + let event_filter = event_filter.into(); iroha_logger::trace!(?event_filter, "Async listening with"); events_api::AsyncEventStream::new(self.events_handler(event_filter)?).await } @@ -929,9 +929,12 @@ impl Client { /// # Errors /// Fails if handler construction fails #[inline] - pub fn events_handler(&self, event_filter: FilterBox) -> Result { + pub fn events_handler( + &self, + event_filter: impl Into, + ) -> Result { events_api::flow::Init::new( - event_filter, + event_filter.into(), self.headers.clone(), self.torii_url .join(torii_uri::SUBSCRIPTION) @@ -1235,7 +1238,7 @@ pub mod events_api { /// Initialization struct for Events API flow. pub struct Init { /// Event filter - filter: FilterBox, + filter: EventFilterBox, /// HTTP request headers headers: HashMap, /// TORII URL @@ -1249,7 +1252,7 @@ pub mod events_api { /// Fails if [`transform_ws_url`] fails. #[inline] pub(in super::super) fn new( - filter: FilterBox, + filter: EventFilterBox, headers: HashMap, url: Url, ) -> Result { diff --git a/client/tests/integration/domain_owner_permissions.rs b/client/tests/integration/domain_owner_permissions.rs index 06892c6a3b3..b26eb647728 100644 --- a/client/tests/integration/domain_owner_permissions.rs +++ b/client/tests/integration/domain_owner_permissions.rs @@ -309,11 +309,9 @@ fn domain_owner_trigger_permissions() -> Result<()> { trigger_instructions, Repeats::from(2_u32), bob_id, - // FIXME: due to restriction in `ExecuteTriggerEventFilter` it's impossible to execute trigger on behalf of multiple users - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - alice_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new().for_trigger(trigger_id.clone()), + ), ), )); test_client.submit_blocking(register_trigger)?; diff --git a/client/tests/integration/events/data.rs b/client/tests/integration/events/data.rs index 065ac37aa14..4250ff2b682 100644 --- a/client/tests/integration/events/data.rs +++ b/client/tests/integration/events/data.rs @@ -138,7 +138,7 @@ fn transaction_execution_should_produce_events( let listener = client.clone(); let (init_sender, init_receiver) = mpsc::channel(); let (event_sender, event_receiver) = mpsc::channel(); - let event_filter = DataEventFilter::AcceptAll.into(); + let event_filter = DataEventFilter::Any; thread::spawn(move || -> Result<()> { let event_iterator = listener.listen_for_events(event_filter)?; init_sender.send(())?; @@ -182,7 +182,7 @@ fn produce_multiple_events() -> Result<()> { let listener = client.clone(); let (init_sender, init_receiver) = mpsc::channel(); let (event_sender, event_receiver) = mpsc::channel(); - let event_filter = DataEventFilter::AcceptAll.into(); + let event_filter = DataEventFilter::Any; thread::spawn(move || -> Result<()> { let event_iterator = listener.listen_for_events(event_filter)?; init_sender.send(())?; diff --git a/client/tests/integration/events/notification.rs b/client/tests/integration/events/notification.rs index ae57791710a..e7738e04aaa 100644 --- a/client/tests/integration/events/notification.rs +++ b/client/tests/integration/events/notification.rs @@ -21,10 +21,11 @@ fn trigger_completion_success_should_produce_event() -> Result<()> { vec![InstructionBox::from(instruction)], Repeats::Indefinitely, asset_id.account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - asset_id.account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(asset_id.account_id), + ), ), )); test_client.submit_blocking(register_trigger)?; @@ -35,11 +36,9 @@ fn trigger_completion_success_should_produce_event() -> Result<()> { let (sender, receiver) = mpsc::channel(); let _handle = thread::spawn(move || -> Result<()> { let mut event_it = thread_client.listen_for_events( - NotificationEventFilter::TriggerCompleted(TriggerCompletedEventFilter::new( - Some(trigger_id), - Some(TriggerCompletedOutcomeType::Success), - )) - .into(), + TriggerCompletedEventFilter::new() + .for_trigger(trigger_id) + .for_outcome(TriggerCompletedOutcomeType::Success), )?; if event_it.next().is_some() { sender.send(())?; @@ -70,10 +69,11 @@ fn trigger_completion_failure_should_produce_event() -> Result<()> { vec![InstructionBox::from(instruction)], Repeats::Indefinitely, account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), + ), ), )); test_client.submit_blocking(register_trigger)?; @@ -84,11 +84,9 @@ fn trigger_completion_failure_should_produce_event() -> Result<()> { let (sender, receiver) = mpsc::channel(); let _handle = thread::spawn(move || -> Result<()> { let mut event_it = thread_client.listen_for_events( - NotificationEventFilter::TriggerCompleted(TriggerCompletedEventFilter::new( - Some(trigger_id), - Some(TriggerCompletedOutcomeType::Failure), - )) - .into(), + TriggerCompletedEventFilter::new() + .for_trigger(trigger_id) + .for_outcome(TriggerCompletedOutcomeType::Failure), )?; if event_it.next().is_some() { sender.send(())?; diff --git a/client/tests/integration/events/pipeline.rs b/client/tests/integration/events/pipeline.rs index 96f85500fac..30f17528219 100644 --- a/client/tests/integration/events/pipeline.rs +++ b/client/tests/integration/events/pipeline.rs @@ -82,12 +82,12 @@ impl Checker { thread::spawn(move || { let mut event_iterator = self .listener - .listen_for_events(FilterBox::Pipeline( + .listen_for_events( PipelineEventFilter::new() - .entity_kind(PipelineEntityKind::Transaction) - .status_kind(status_kind) - .hash(*self.hash), - )) + .for_entity(PipelineEntityKind::Transaction) + .for_status(status_kind) + .for_hash(*self.hash), + ) .expect("Failed to create event iterator."); let event_result = event_iterator.next().expect("Stream closed"); let _event = event_result.expect("Must be valid"); @@ -101,9 +101,8 @@ fn committed_block_must_be_available_in_kura() { wait_for_genesis_committed(&[client.clone()], 0); let event_filter = PipelineEventFilter::new() - .entity_kind(PipelineEntityKind::Block) - .status_kind(PipelineStatusKind::Committed) - .into(); + .for_entity(PipelineEntityKind::Block) + .for_status(PipelineStatusKind::Committed); let mut event_iter = client .listen_for_events(event_filter) .expect("Failed to subscribe for events"); diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index e00767959a8..6b4473d8f97 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -9,7 +9,7 @@ use iroha_client::{ transaction::Executable, }, }; -use iroha_data_model::{events::TriggeringFilterBox, transaction::WasmSmartContract}; +use iroha_data_model::{events::TriggeringEventFilterBox, transaction::WasmSmartContract}; use iroha_genesis::GenesisNetwork; use iroha_logger::info; use test_network::*; @@ -59,8 +59,11 @@ fn execute_trigger_should_produce_event() -> Result<()> { let thread_client = test_client.clone(); let (sender, receiver) = mpsc::channel(); let _handle = thread::spawn(move || -> Result<()> { - let mut event_it = thread_client - .listen_for_events(ExecuteTriggerEventFilter::new(trigger_id, account_id).into())?; + let mut event_it = thread_client.listen_for_events( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id) + .under_authority(account_id), + )?; if event_it.next().is_some() { sender.send(())?; return Ok(()); @@ -121,10 +124,11 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { bad_trigger_instructions, Repeats::Indefinitely, account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - bad_trigger_id.clone(), - account_id.clone(), - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(bad_trigger_id.clone()) + .under_authority(account_id.clone()), + ), ), )); test_client.submit(register_bad_trigger)?; @@ -139,7 +143,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 - TriggeringFilterBox::Time(TimeEventFilter::new(ExecutionTime::PreCommit)), + TriggeringEventFilterBox::Time(TimeEventFilter::new(ExecutionTime::PreCommit)), ), )); test_client.submit_blocking(register_trigger)?; @@ -176,10 +180,11 @@ fn trigger_should_not_be_executed_with_zero_repeats_count() -> Result<()> { trigger_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), + ), ), )); test_client.submit_blocking(register_trigger)?; @@ -240,10 +245,11 @@ fn trigger_should_be_able_to_modify_its_own_repeats_count() -> Result<()> { trigger_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), + ), ), )); test_client.submit_blocking(register_trigger)?; @@ -283,10 +289,11 @@ fn unregister_trigger() -> Result<()> { Vec::::new(), Repeats::Indefinitely, account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), + ), ), ); let register_trigger = Register::trigger(trigger.clone()); @@ -360,10 +367,11 @@ fn trigger_in_genesis_using_base64() -> Result<()> { .wrap_err("Can't deserialize wasm using base64")?, Repeats::Indefinitely, account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - account_id.clone(), - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id.clone()), + ), ), ); @@ -410,10 +418,11 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { trigger_unregister_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id_unregister.clone(), - account_id.clone(), - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id_unregister.clone()) + .under_authority(account_id.clone()), + ), ), )); test_client.submit_blocking(register_trigger)?; @@ -426,10 +435,11 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { trigger_should_be_unregistered_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id_to_be_unregistered.clone(), - account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id_to_be_unregistered.clone()) + .under_authority(account_id), + ), ), )); test_client.submit_blocking(register_trigger)?; @@ -470,10 +480,11 @@ fn trigger_burn_repetitions() -> Result<()> { trigger_instructions, Repeats::from(1_u32), account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), + ), ), )); test_client.submit_blocking(register_trigger)?; @@ -514,10 +525,11 @@ fn unregistering_one_of_two_triggers_with_identical_wasm_should_not_cause_origin wasm.clone(), Repeats::Indefinitely, account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id, - account_id.clone(), - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id) + .under_authority(account_id.clone()), + ), ), ) }; @@ -555,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( @@ -564,10 +576,11 @@ fn build_register_trigger_isi( trigger_instructions, Repeats::Indefinitely, asset_id.account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id, - asset_id.account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + 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 32f6ae92037..decbb865cd5 100644 --- a/client/tests/integration/triggers/data_trigger.rs +++ b/client/tests/integration/triggers/data_trigger.rs @@ -23,16 +23,9 @@ fn must_execute_both_triggers() -> Result<()> { [instruction.clone()], Repeats::Indefinitely, account_id.clone(), - // FIXME: rewrite the filters using the builder DSL https://github.com/hyperledger/iroha/issues/3068 - TriggeringFilterBox::Data(BySome(DataEntityFilter::ByDomain(BySome( - DomainFilter::new( - AcceptAll, - BySome(DomainEventFilter::ByAccount(BySome(AccountFilter::new( - AcceptAll, - BySome(AccountEventFilter::ByCreated), - )))), - ), - )))), + TriggeringEventFilterBox::Data(DataEventFilter::Account( + AccountEventFilter::new().for_events(AccountEventSet::Created), + )), ), )); test_client.submit_blocking(register_trigger)?; @@ -43,9 +36,9 @@ fn must_execute_both_triggers() -> Result<()> { [instruction], Repeats::Indefinitely, account_id, - TriggeringFilterBox::Data(BySome(DataEntityFilter::ByDomain(BySome( - DomainFilter::new(AcceptAll, BySome(DomainEventFilter::ByCreated)), - )))), + TriggeringEventFilterBox::Data(DataEventFilter::Domain( + DomainEventFilter::new().for_events(DomainEventSet::Created), + )), ), )); test_client.submit_blocking(register_trigger)?; @@ -95,16 +88,9 @@ 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, - // FIXME: rewrite the filters using the builder DSL https://github.com/hyperledger/iroha/issues/3068 - TriggeringFilterBox::Data(BySome(DataEntityFilter::ByDomain(BySome( - DomainFilter::new( - AcceptAll, - BySome(DomainEventFilter::ByAccount(BySome(AccountFilter::new( - AcceptAll, - BySome(AccountEventFilter::ByCreated), - )))), - ), - )))), + TriggeringEventFilterBox::Data(DataEventFilter::Account( + 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 c6250fdfe3e..a153b262464 100644 --- a/client/tests/integration/triggers/event_trigger.rs +++ b/client/tests/integration/triggers/event_trigger.rs @@ -24,18 +24,9 @@ fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { vec![instruction], Repeats::Indefinitely, account_id, - // FIXME: rewrite the filters using the builder DSL https://github.com/hyperledger/iroha/issues/3068 - TriggeringFilterBox::Data(BySome(DataEntityFilter::ByDomain(BySome( - DomainFilter::new( - AcceptAll, - BySome(DomainEventFilter::ByAssetDefinition(BySome( - AssetDefinitionFilter::new( - AcceptAll, - BySome(AssetDefinitionEventFilter::ByCreated), - ), - ))), - ), - )))), + TriggeringEventFilterBox::Data(DataEventFilter::AssetDefinition( + 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 fcb714bb711..06042776200 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(), - TriggeringFilterBox::Time(TimeEventFilter::new(ExecutionTime::Schedule(schedule))), + TriggeringEventFilterBox::Time(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(), - TriggeringFilterBox::Time(TimeEventFilter::new(ExecutionTime::Schedule(schedule))), + TriggeringEventFilterBox::Time(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(), - TriggeringFilterBox::Time(TimeEventFilter::new(ExecutionTime::PreCommit)), + TriggeringEventFilterBox::Time(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(), - TriggeringFilterBox::Time(TimeEventFilter::new(ExecutionTime::Schedule(schedule))), + TriggeringEventFilterBox::Time(TimeEventFilter::new(ExecutionTime::Schedule(schedule))), ), )); test_client.submit(register_trigger)?; @@ -273,11 +273,9 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { fn get_block_committed_event_listener( client: &Client, ) -> Result>> { - let block_filter = FilterBox::Pipeline( - PipelineEventFilter::new() - .entity_kind(PipelineEntityKind::Block) - .status_kind(PipelineStatusKind::Committed), - ); + let block_filter = PipelineEventFilter::new() + .for_entity(PipelineEntityKind::Block) + .for_status(PipelineStatusKind::Committed); client.listen_for_events(block_filter) } diff --git a/client/tests/integration/triggers/trigger_rollback.rs b/client/tests/integration/triggers/trigger_rollback.rs index 82031f7ea42..2bbcd387699 100644 --- a/client/tests/integration/triggers/trigger_rollback.rs +++ b/client/tests/integration/triggers/trigger_rollback.rs @@ -28,10 +28,11 @@ fn failed_trigger_revert() -> Result<()> { instructions, Repeats::Indefinitely, account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - account_id, - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id), + ), ), )); let _ = client.submit_blocking(register_trigger); diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index c38e2e13025..807d504a280 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -258,22 +258,25 @@ mod events { Pipeline, /// Gets data events Data, - /// Get notification events - Notification, + /// Get execute trigger events + ExecuteTrigger, + /// Get trigger completed events + TriggerCompleted, } impl RunArgs for Args { fn run(self, context: &mut dyn RunContext) -> Result<()> { - let filter = match self { - Args::Pipeline => FilterBox::Pipeline(PipelineEventFilter::new()), - Args::Data => FilterBox::Data(DataEventFilter::AcceptAll), - Args::Notification => FilterBox::Notification(NotificationEventFilter::AcceptAll), - }; - listen(filter, context) + match self { + Args::Pipeline => listen(PipelineEventFilter::new(), context), + Args::Data => listen(DataEventFilter::Any, context), + Args::ExecuteTrigger => listen(ExecuteTriggerEventFilter::new(), context), + Args::TriggerCompleted => listen(TriggerCompletedEventFilter::new(), context), + } } } - fn listen(filter: FilterBox, context: &mut dyn RunContext) -> Result<()> { + fn listen(filter: impl Into, context: &mut dyn RunContext) -> Result<()> { + let filter = filter.into(); let iroha_client = context.client_from_config(); eprintln!("Listening to events with filter: {filter:?}"); iroha_client diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm old mode 100644 new mode 100755 index b447246240b..9eabd2f3906 Binary files a/configs/swarm/executor.wasm and b/configs/swarm/executor.wasm differ diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 5f36bb2208c..8218f0bb994 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -378,10 +378,11 @@ mod tests { Vec::::new(), Repeats::Indefinitely, account_id.clone(), - TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( - trigger_id.clone(), - account_id.clone(), - )), + TriggeringEventFilterBox::ExecuteTrigger( + ExecuteTriggerEventFilter::new() + .for_trigger(trigger_id.clone()) + .under_authority(account_id.clone()), + ), ), )); diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index 9560c23c1b5..88db4d6330b 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -50,7 +50,7 @@ impl_lazy! { iroha_data_model::metadata::MetadataValueBox, iroha_data_model::query::TransactionQueryOutput, iroha_data_model::permission::PermissionTokenSchema, - iroha_data_model::trigger::Trigger, + 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 d90b5f65932..4205286f724 100644 --- a/core/src/smartcontracts/isi/triggers/mod.rs +++ b/core/src/smartcontracts/isi/triggers/mod.rs @@ -13,14 +13,14 @@ pub mod set; /// - TODO: authority permissions. pub mod isi { use iroha_data_model::{ - events::Filter, + events::EventFilter, isi::error::{InvalidParameterError, RepetitionError}, trigger::prelude::*, }; 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; @@ -38,25 +38,25 @@ pub mod isi { let triggers = wsv.triggers_mut(); let trigger_id = new_trigger.id().clone(); let success = match &new_trigger.action.filter { - TriggeringFilterBox::Data(_) => triggers.add_data_trigger( + TriggeringEventFilterBox::Data(_) => triggers.add_data_trigger( &engine, new_trigger .try_into() .map_err(|e: &str| Error::Conversion(e.to_owned()))?, ), - TriggeringFilterBox::Pipeline(_) => triggers.add_pipeline_trigger( + TriggeringEventFilterBox::Pipeline(_) => triggers.add_pipeline_trigger( &engine, new_trigger .try_into() .map_err(|e: &str| Error::Conversion(e.to_owned()))?, ), - TriggeringFilterBox::Time(_) => triggers.add_time_trigger( + TriggeringEventFilterBox::Time(_) => triggers.add_time_trigger( &engine, new_trigger .try_into() .map_err(|e: &str| Error::Conversion(e.to_owned()))?, ), - TriggeringFilterBox::ExecuteTrigger(_) => triggers.add_by_call_trigger( + TriggeringEventFilterBox::ExecuteTrigger(_) => triggers.add_by_call_trigger( &engine, new_trigger .try_into() @@ -79,7 +79,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 +98,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 +130,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; @@ -159,7 +159,7 @@ pub mod isi { wsv.triggers() .inspect_by_id(id, |action| -> Result<(), Error> { - let allow_execute = if let TriggeringFilterBox::ExecuteTrigger(filter) = + let allow_execute = if let TriggeringEventFilterBox::ExecuteTrigger(filter) = action.clone_and_box().filter { let event = ExecuteTriggerEvent { @@ -194,7 +194,7 @@ pub mod isi { pub mod query { //! Queries associated to triggers. use iroha_data_model::{ - events::TriggeringFilterBox, + events::TriggeringEventFilterBox, metadata::MetadataValueBox, query::error::QueryExecutionFail as Error, trigger::{Trigger, TriggerId}, @@ -215,7 +215,10 @@ 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, Error> { let id = &self.id; iroha_logger::trace!(%id); // Can't use just `LoadedActionTrait::clone_and_box` cause this will trigger lifetime mismatch @@ -256,7 +259,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; diff --git a/core/src/smartcontracts/isi/triggers/set.rs b/core/src/smartcontracts/isi/triggers/set.rs index b8a41ea0b3e..dc1587848bb 100644 --- a/core/src/smartcontracts/isi/triggers/set.rs +++ b/core/src/smartcontracts/isi/triggers/set.rs @@ -14,7 +14,7 @@ use std::{fmt, num::NonZeroU64}; use indexmap::{map::Entry, IndexMap}; use iroha_crypto::HashOf; use iroha_data_model::{ - events::Filter as EventFilter, + events::EventFilter, isi::error::{InstructionExecutionError, MathError}, prelude::*, query::error::FindError, @@ -88,13 +88,15 @@ pub trait LoadedActionTrait { fn mintable(&self) -> bool; /// Convert action to a boxed representation - fn into_boxed(self) -> LoadedAction; + fn into_boxed(self) -> LoadedAction; /// Same as [`into_boxed()`](LoadedActionTrait::into_boxed) but clones `self` - fn clone_and_box(&self) -> LoadedAction; + fn clone_and_box(&self) -> LoadedAction; } -impl + Clone> LoadedActionTrait for LoadedAction { +impl + Clone> LoadedActionTrait + for LoadedAction +{ fn executable(&self) -> &LoadedExecutable { &self.executable } @@ -119,7 +121,7 @@ impl + Clone> LoadedActionTrait for Loaded self.filter.mintable() } - fn into_boxed(self) -> LoadedAction { + fn into_boxed(self) -> LoadedAction { let Self { executable, repeats, @@ -137,7 +139,7 @@ impl + Clone> LoadedActionTrait for Loaded } } - fn clone_and_box(&self) -> LoadedAction { + fn clone_and_box(&self) -> LoadedAction { self.clone().into_boxed() } } @@ -405,7 +407,7 @@ impl Set { /// # Errors /// /// Return [`Err`] if failed to preload wasm trigger - fn add_to( + fn add_to( &mut self, engine: &wasmtime::Engine, trigger: Trigger, @@ -705,7 +707,7 @@ impl Set { /// Note that this function doesn't remove the trigger from [`Set::ids`]. /// /// Returns `true` if trigger was removed and `false` otherwise. - fn remove_from( + fn remove_from( original_contracts: &mut WasmSmartContractMap, triggers: &mut IndexMap>, trigger_id: &TriggerId, @@ -884,7 +886,7 @@ impl Set { } /// Remove actions with zero execution count from `triggers` - fn remove_zeros( + fn remove_zeros( ids: &mut IndexMap, original_contracts: &mut WasmSmartContractMap, triggers: &mut IndexMap>, diff --git a/core/src/wsv.rs b/core/src/wsv.rs index c736af16b22..fbe6730f77f 100644 --- a/core/src/wsv.rs +++ b/core/src/wsv.rs @@ -12,7 +12,7 @@ use iroha_crypto::HashOf; use iroha_data_model::{ account::AccountId, block::SignedBlock, - events::notification::{TriggerCompletedEvent, TriggerCompletedOutcome}, + events::trigger_completed::{TriggerCompletedEvent, TriggerCompletedOutcome}, isi::error::{InstructionExecutionError as Error, MathError}, parameter::{Parameter, ParameterValueBox}, permission::PermissionTokenSchema, @@ -567,8 +567,7 @@ impl WorldStateView { event } }; - self.events_buffer - .push(NotificationEvent::from(event).into()); + self.events_buffer.push(event.into()); } } diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 1d1de7ac494..0b036eff0fe 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -672,7 +672,7 @@ pub trait TestClient: Sized { fn test_with_account(api_url: &SocketAddr, keys: KeyPair, account_id: &AccountId) -> Self; /// Loop for events with filter and handler function - fn for_each_event(self, event_filter: FilterBox, f: impl Fn(Result)); + fn for_each_event(self, event_filter: impl Into, f: impl Fn(Result)); /// Submit instruction with polling /// @@ -811,7 +811,7 @@ impl TestClient for Client { Client::new(config) } - fn for_each_event(self, event_filter: FilterBox, f: impl Fn(Result)) { + fn for_each_event(self, event_filter: impl Into, f: impl Fn(Result)) { for event_result in self .listen_for_events(event_filter) .expect("Failed to create event iterator.") diff --git a/data_model/derive/src/event_set.rs b/data_model/derive/src/event_set.rs new file mode 100644 index 00000000000..e6ed8c573a1 --- /dev/null +++ b/data_model/derive/src/event_set.rs @@ -0,0 +1,395 @@ +#![allow(unused)] + +use darling::{FromDeriveInput, FromVariant}; +use iroha_macro_utils::Emitter; +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::{DeriveInput, Variant}; + +enum FieldsStyle { + Unit, + Unnamed, + Named, +} + +/// Converts the `FieldStyle` to an ignoring pattern (to be put after the variant name) +impl ToTokens for FieldsStyle { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + FieldsStyle::Unit => {} + FieldsStyle::Unnamed => tokens.extend(quote!((..))), + FieldsStyle::Named => tokens.extend(quote!({ .. })), + } + } +} + +struct EventSetVariant { + event_ident: syn::Ident, + flag_ident: syn::Ident, + fields_style: FieldsStyle, +} + +impl FromVariant for EventSetVariant { + fn from_variant(variant: &Variant) -> darling::Result { + let syn::Variant { + attrs: _, + ident: event_ident, + fields, + discriminant: _, + } = variant; + + // a nested event is an event within an event (like `AccountEvent::Asset`, which bears an `AssetEvent`) + // we detect those by checking whether the payload type (if any) ends with `Event` + let is_nested = match fields { + syn::Fields::Unnamed(fields) => { + fields.unnamed.len() == 1 + && matches!(&fields.unnamed[0].ty, syn::Type::Path(p) if p.path.segments.last().unwrap().ident.to_string().ends_with("Event")) + } + syn::Fields::Unit | + // just a fail-safe, we don't use named fields in events + syn::Fields::Named(_) => false, + }; + + // we have a different naming convention for nested events + // to signify that there are actually multiple types of events inside + let flag_ident = if is_nested { + syn::Ident::new(&format!("Any{event_ident}"), event_ident.span()) + } else { + event_ident.clone() + }; + + let fields_style = match fields { + syn::Fields::Unnamed(_) => FieldsStyle::Unnamed, + syn::Fields::Named(_) => FieldsStyle::Named, + syn::Fields::Unit => FieldsStyle::Unit, + }; + + Ok(Self { + event_ident: event_ident.clone(), + flag_ident, + fields_style, + }) + } +} + +struct EventSetEnum { + vis: syn::Visibility, + event_enum_ident: syn::Ident, + set_ident: syn::Ident, + variants: Vec, +} + +impl FromDeriveInput for EventSetEnum { + fn from_derive_input(input: &DeriveInput) -> darling::Result { + let syn::DeriveInput { + attrs: _, + vis, + ident: event_ident, + generics, + data, + } = &input; + + let mut accumulator = darling::error::Accumulator::default(); + + if !generics.params.is_empty() { + accumulator.push(darling::Error::custom( + "EventSet cannot be derived on generic enums", + )); + } + + let Some(variants) = darling::ast::Data::::try_from(data)?.take_enum() + else { + accumulator.push(darling::Error::custom( + "EventSet can be derived only on enums", + )); + + return Err(accumulator.finish().unwrap_err()); + }; + + if variants.len() > 32 { + accumulator.push(darling::Error::custom( + "EventSet can be derived only on enums with up to 32 variants", + )); + } + + accumulator.finish_with(Self { + vis: vis.clone(), + event_enum_ident: event_ident.clone(), + set_ident: syn::Ident::new(&format!("{event_ident}Set"), event_ident.span()), + variants, + }) + } +} + +impl ToTokens for EventSetEnum { + #[allow(clippy::too_many_lines)] // splitting it is not really feasible, it's all tightly coupled =( + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + vis, + event_enum_ident, + set_ident, + variants, + } = self; + + // definitions of consts for each event + let flag_defs = variants.iter().zip(0u32..).map( + |( + EventSetVariant { + flag_ident, + event_ident, + .. + }, + i, + )| { + let doc = format!(" Matches [`{event_enum_ident}::{event_ident}`]"); + quote! { + #[doc = #doc] + #vis const #flag_ident: Self = Self(1 << #i); + } + }, + ); + // identifiers of all the flag constants to use in impls + let flag_idents = variants + .iter() + .map(|EventSetVariant { flag_ident, .. }| quote!(#set_ident::#flag_ident)) + .collect::>(); + // names of all the flag (as string literals) to use in debug impl + let flag_names = variants + .iter() + .map(|EventSetVariant { flag_ident, .. }| { + let flag_name = flag_ident.to_string(); + quote! { + #flag_name + } + }) + .collect::>(); + // names of all the flags in `quotes` to use in error message + let flag_names_str = variants + .iter() + .map(|EventSetVariant { flag_ident, .. }| format!("`{flag_ident}`")) + .collect::>() + .join(", "); + // patterns for matching events in the `matches` method + let event_patterns = variants.iter().map( + |EventSetVariant { + event_ident, + fields_style, + .. + }| { + quote! { + #event_enum_ident::#event_ident #fields_style + } + }, + ); + + let doc = format!(" An event set for [`{event_enum_ident}`]s\n\nEvent sets of the same type can be combined with a custom `|` operator"); + + tokens.extend(quote! { + #[derive( + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + // this introduces tight coupling with these crates + // but it's the easiest way to make sure those traits are implemented + parity_scale_codec::Decode, + parity_scale_codec::Encode, + // TODO: we probably want to represent the bit values for each variant in the schema + iroha_schema::IntoSchema, + )] + #[repr(transparent)] + #[doc = #doc] + #vis struct #set_ident(u32); + + // we want to imitate an enum here, so not using the SCREAMING_SNAKE_CASE here + #[allow(non_upper_case_globals)] + impl #set_ident { + #( #flag_defs )* + } + + impl #set_ident { + /// Creates an empty event set + pub const fn empty() -> Self { + Self(0) + } + + /// Creates an event set containing all events + pub const fn all() -> Self { + Self( + #( + #flag_idents.0 + )|* + ) + } + + /// Combines two event sets by computing a union of two sets + /// + /// A const method version of the `|` operator + pub const fn or(self, other: Self) -> Self { + Self(self.0 | other.0) + } + + /// Checks whether an event set is a superset of another event set + /// + /// That is, whether `self` will match all events that `other` contains + const fn contains(&self, other: Self) -> bool { + (self.0 & other.0) == other.0 + } + + /// Decomposes an `EventSet` into a vector of basis `EventSet`s, each containing a single event + /// + /// Each of the event set in the vector will be equal to some of the associated constants for the `EventSet` + fn decompose(&self) -> Vec { + let mut result = Vec::new(); + + #(if self.contains(#flag_idents) { + result.push(#flag_idents); + })* + + result + } + + /// Checks whether an event set contains a specific event + pub const fn matches(&self, event: &#event_enum_ident) -> bool { + match event { + #( + #event_patterns => self.contains(#flag_idents), + )* + } + } + } + + impl core::fmt::Debug for #set_ident { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{}[", stringify!(#set_ident))?; + + let mut need_comma = false; + + #(if self.contains(#flag_idents) { + if need_comma { + write!(f, ", ")?; + } else { + need_comma = true; + } + write!(f, "{}", #flag_names)? + })* + + write!(f, "]") + } + } + + impl core::ops::BitOr for #set_ident { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self { + self.or(rhs) + } + } + + impl core::ops::BitOrAssign for #set_ident { + fn bitor_assign(&mut self, rhs: Self) { + *self = self.or(rhs); + } + } + + impl serde::Serialize for #set_ident { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::*; + + let basis = self.decompose(); + let mut seq = serializer.serialize_seq(Some(basis.len()))?; + for event in basis { + let str = match event { + #(#flag_idents => #flag_names,)* + _ => unreachable!(), + }; + + seq.serialize_element(&str)?; + } + seq.end() + } + } + + impl<'de> serde::Deserialize<'de> for #set_ident { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = #set_ident; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a sequence of strings") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut result = #set_ident::empty(); + + struct SingleEvent(#set_ident); + impl<'de> serde::Deserialize<'de> for SingleEvent { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = SingleEvent; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + let event = match v { + #( + #flag_names => #flag_idents, + )* + _ => return Err(serde::de::Error::custom(format!("unknown event variant `{}`, expected one of {}", v, #flag_names_str))), + }; + + Ok(SingleEvent(event)) + } + } + + deserializer.deserialize_str(Visitor) + } + } + while let Some(SingleEvent(event)) = seq.next_element::()? { + result |= event; + } + + Ok(result) + } + } + + deserializer.deserialize_seq(Visitor) + } + } + }) + } +} + +pub fn impl_event_set_derive(emitter: &mut Emitter, input: &syn::DeriveInput) -> TokenStream { + let Some(enum_) = emitter.handle(EventSetEnum::from_derive_input(input)) else { + return quote! {}; + }; + + quote! { + #enum_ + } +} diff --git a/data_model/derive/src/filter.rs b/data_model/derive/src/filter.rs deleted file mode 100644 index a023f4496cf..00000000000 --- a/data_model/derive/src/filter.rs +++ /dev/null @@ -1,265 +0,0 @@ -use darling::{FromDeriveInput, FromVariant}; -use iroha_macro_utils::Emitter; -use manyhow::emit; -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; -use syn::{Generics, Ident, Variant, Visibility}; - -#[derive(FromDeriveInput)] -#[darling(supports(enum_tuple))] -struct EventEnum { - vis: Visibility, - ident: Ident, - generics: Generics, - data: darling::ast::Data, -} - -enum EventVariant { - /// A variant of event that delegates to some other event. Identified by conventional naming of the event types: ending with `Event`. - /// Delegates all the filterting to the corresponding event's filter. - Delegating { - variant_name: Ident, - /// A name of the event this variant delegates to, without the the `Event` suffix - delegated_event_name_base: String, - }, - /// An actual event. Has either an Id or an identifiable object as a payload - /// The presense of the Id field is not required by this macro per se, but will be enfored by `OriginFilter` requiring a `HasOrigin` impl. - Direct(Ident), -} - -impl FromVariant for EventVariant { - fn from_variant(variant: &Variant) -> darling::Result { - let syn::Fields::Unnamed(fields) = &variant.fields else { - return Err( - darling::Error::custom("Expected an enum with unnamed fields") - .with_span(&variant.fields), - ); - }; - // note: actually, we have only one field in the event variants - // this is not enforced by this macro, but by `IntoSchema` - let Some(first_field_ty) = fields.unnamed.first().map(|v| &v.ty) else { - return Err(darling::Error::custom("Expected at least one field").with_span(&fields)); - }; - let syn::Type::Path(path) = first_field_ty else { - return Err( - darling::Error::custom("Only identifiers supported as event types") - .with_span(first_field_ty), - ); - }; - let Some(first_field_ty_name) = path.path.get_ident() else { - return Err( - darling::Error::custom("Only identifiers supported as event types") - .with_span(first_field_ty), - ); - }; - - // What clippy suggests is much less readable in this case - #[allow(clippy::option_if_let_else)] - if let Some(delegated_event_name_base) = - first_field_ty_name.to_string().strip_suffix("Event") - { - Ok(EventVariant::Delegating { - variant_name: variant.ident.clone(), - delegated_event_name_base: delegated_event_name_base.to_string(), - }) - } else { - Ok(EventVariant::Direct(variant.ident.clone())) - } - } -} - -impl EventEnum { - fn variants(&self) -> &[EventVariant] { - match &self.data { - darling::ast::Data::Enum(variants) => variants, - _ => unreachable!("BUG: only enums should be here"), - } - } - - fn filter_map_variants Option>(&self, fun: F) -> Vec { - self.variants().iter().filter_map(fun).collect() - } - - /// Used to produce fields like `ByAccount(crate::prelude::FilterOpt)` in `DomainEventFilter`. - fn generate_filter_variants_for_delegating_events(&self) -> Vec { - self.filter_map_variants(|variant| { - if let EventVariant::Delegating { - variant_name, - delegated_event_name_base, - } = variant - { - // E.g. `Account` field in the event => `ByAccount` in the event filter - let filter_variant_ident = format_ident!("By{}", variant_name); - // E.g. `AccountEvent` inner field from `Account` variant in event => - // `AccountFilter` inside the event filter - let inner_filter_ident = format_ident!("{}Filter", delegated_event_name_base); - let import_path = quote! {crate::prelude}; - Some(quote! { - #filter_variant_ident(#import_path::FilterOpt<#inner_filter_ident>) - }) - } else { - None - } - }) - } - - /// Used to produce fields like `ByCreated` in `DomainEventFilter`. - fn generate_filter_variants_for_direct_events(&self) -> Vec { - self.filter_map_variants(|variant| { - if let EventVariant::Direct(event_variant_ident) = variant { - // Event fields such as `MetadataRemoved` get mapped to `ByMetadataRemoved` - let filter_variant_ident = format_ident!("By{}", event_variant_ident); - Some(filter_variant_ident) - } else { - None - } - }) - } - - /// Match arms for `Filter` impls of event filters of the form - /// `(Self::ByAccount(filter_opt), crate::prelude::DomainEvent::Account(event)) => {filter_opt.matches(event)}`. - fn generate_filter_arms_for_delegating_events(&self) -> Vec { - self.filter_map_variants(|variant| { - if let EventVariant::Delegating { variant_name, .. } = variant { - let event_ident = &self.ident; - let filter_variant_ident = format_ident!("By{}", variant_name); - let import_path = quote! {crate::prelude}; - Some(quote! { - ( - Self::#filter_variant_ident(filter_opt), - #import_path::#event_ident::#variant_name(event) - ) => { - filter_opt.matches(event) - } - }) - } else { - None - } - }) - } - - /// Match arms for `Filter` impls of event filters of the form - /// `(Self::ByCreated, crate::prelude::DomainEvent::Created(_))`. - fn generate_filter_patterns_for_direct_events(&self) -> Vec { - self.filter_map_variants(|variant| { - if let EventVariant::Direct(event_variant_ident) = variant { - let event_ident = &self.ident; - let filter_variant_ident = format_ident!("By{}", event_variant_ident); - let import_path = quote! {crate::prelude}; - Some(quote! { - ( - Self::#filter_variant_ident, - #import_path::#event_ident::#event_variant_ident(_) - ) - }) - } else { - None - } - }) - } -} - -/// Generates the event filter for the event. E.g. for `AccountEvent`, `AccountEventFilter` -/// and its `impl Filter` are generated. -fn impl_event_filter(event: &EventEnum) -> proc_macro2::TokenStream { - let EventEnum { - vis, - ident: event_ident, - generics, - .. - } = event; - - let id_variants = event.generate_filter_variants_for_direct_events(); - let event_variants = event.generate_filter_variants_for_delegating_events(); - - let id_patterns = event.generate_filter_patterns_for_direct_events(); - let event_arms = event.generate_filter_arms_for_delegating_events(); - - let event_filter_ident = format_ident!("{}Filter", event_ident); - let import_path = quote! { crate::prelude }; - let imp_event = quote! { #import_path::#event_ident }; - - let event_filter_doc = format!(" Event filter for {event_ident} entity"); - - quote! { - iroha_data_model_derive::model_single! { - #[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] - #[allow(clippy::enum_variant_names, missing_docs)] - #[doc = #event_filter_doc] - #vis enum #event_filter_ident #generics { - #(#id_variants),*, - #(#event_variants),* - } - } - - #[cfg(feature = "transparent_api")] - impl #import_path::Filter for #event_filter_ident { - type Event = #imp_event; - - fn matches(&self, event: &#imp_event) -> bool { - match (self, event) { - #(#id_patterns)|* => true, - #(#event_arms),* - _ => false, - } - } - } - } -} - -/// Generates the filter for the event. E.g. for `AccountEvent`, `AccountFilter` -/// and its `impl Filter` are generated. -pub fn impl_filter(emitter: &mut Emitter, input: &syn::DeriveInput) -> TokenStream { - let Some(event) = emitter.handle(EventEnum::from_derive_input(input)) else { - return quote!(); - }; - - let EventEnum { - vis, - ident: event_ident, - generics, - .. - } = &event; - - let event_filter_and_impl = impl_event_filter(&event); - - let event_base = event_ident.to_string().strip_suffix("Event").map_or_else( - || { - emit!(emitter, event_ident, "Event name should end with `Event`"); - event_ident.to_string() - }, - ToString::to_string, - ); - - let filter_ident = format_ident!("{}Filter", event_base); - let event_filter_ident = format_ident!("{}Filter", event_ident); - - let import_path = quote! { crate::prelude }; - let fil_opt = quote! { #import_path::FilterOpt }; - let orig_fil = quote! { #import_path::OriginFilter }; - let imp_event = quote! { #import_path::#event_ident }; - - let filter_doc = format!(" Filter for {event_ident} entity"); - - quote! { - iroha_data_model_derive::model_single! { - #[derive(Debug, Clone, PartialEq, Eq, derive_more::Constructor, Decode, Encode, Deserialize, Serialize, IntoSchema)] - #[doc = #filter_doc] - #vis struct #filter_ident #generics { - origin_filter: #fil_opt<#orig_fil<#imp_event>>, - event_filter: #fil_opt<#event_filter_ident> - } - } - - #[cfg(feature = "transparent_api")] - impl #import_path::Filter for #filter_ident { - type Event = #imp_event; - - fn matches(&self, event: &Self::Event) -> bool { - self.origin_filter.matches(event) && self.event_filter.matches(event) - } - } - - #event_filter_and_impl - } -} diff --git a/data_model/derive/src/lib.rs b/data_model/derive/src/lib.rs index 36aba376f4f..384ca813542 100644 --- a/data_model/derive/src/lib.rs +++ b/data_model/derive/src/lib.rs @@ -1,6 +1,6 @@ //! A crate containing various derive macros for `data_model` mod enum_ref; -mod filter; +mod event_set; mod has_origin; mod id; mod model; @@ -285,181 +285,6 @@ pub fn id_eq_ord_hash(input: TokenStream) -> TokenStream { emitter.finish_token_stream_with(result) } -/// [`Filter`] is used for code generation of `...Filter` structs and `...EventFilter` enums, as well as -/// implementing the `Filter` trait for both of them. -/// This macro should only be attributed to `Event` enums. E.g. if the event is called `AccountEvent`, -/// then the macro will produce `AccountEventFilter` and `AccountFilter`. The latter will have `new` and -/// field getters defined, and both will have their respective `Filter` trait impls generated. -/// Due to name scoping, the macro currently properly -/// expands only from within the `iroha_data_model` crate as it relies on a few of `crate::prelude` -/// imports. This macro also depends on the naming conventions adopted so far, such as that -/// `Event` enums always have tuple variants with either some sort of `Id` or another `Event` inside -/// of them, as well as that all `Event` inner fields precede `Id` fields in the enum definition. -/// -/// # Examples -/// -/// ```ignore -/// use iroha_data_model_derive::{Filter, IdEqOrdHash}; -/// use iroha_data_model::prelude::{HasOrigin, Identifiable, IdBox}; -/// use serde::{Deserialize, Serialize}; -/// -/// -/// #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Filter, Deserialize, Serialize)] -/// pub enum LayerEvent { -/// SubLayer(SubLayerEvent), -/// Created(LayerId), -/// } -/// -/// pub enum SubLayerEvent { -/// Created(SubLayerId), -/// } -/// -/// pub struct LayerId { -/// name: u32, -/// } -/// -/// #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -/// pub struct SubLayerId { -/// name: u32, -/// parent_id: LayerId, -/// } -/// -/// #[derive(Debug, Clone, IdEqOrdHash)] -/// pub struct Layer { -/// id: ::Id, -/// } -/// -/// #[derive(Debug, Clone, IdEqOrdHash)] -/// pub struct SubLayer { -/// id: ::Id, -/// } -/// -/// # impl From for IdBox { -/// # fn from(_source: Layer) -> Self { -/// # unimplemented!("Only present to make the example work") -/// # } -/// # } -/// -/// # impl From for IdBox { -/// # fn from(_source: SubLayer) -> Self { -/// # unimplemented!("Only present to make the example work") -/// # } -/// # } -/// -/// impl HasOrigin for LayerEvent { -/// type Origin = Layer; -/// -/// fn origin_id(&self) -> &::Id { -/// match self { -/// Self::SubLayer(sub_layer) => &sub_layer.origin_id().parent_id, -/// Self::Created(id) => id, -/// } -/// } -/// } -/// -/// impl HasOrigin for SubLayerEvent { -/// type Origin = SubLayer; -/// -/// fn origin_id(&self) -> &::Id { -/// match self { -/// Self::Created(id) => id, -/// } -/// } -/// } -/// ``` -/// -/// Deriving [`Filter`] for `LayerEvent` expands into: -/// -/// ``` -/// /* -/// #[doc = " Filter for LayerEvent entity"] -/// #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, derive_more::Constructor, Decode, Encode, Deserialize, Serialize, IntoSchema)] -/// pub struct LayerFilter { -/// origin_filter: -/// crate::prelude::FilterOpt>, -/// event_filter: crate::prelude::FilterOpt, -/// } -/// impl LayerFilter { -/// #[doc = " Construct new LayerFilter"] -/// pub const fn new( -/// origin_filter: crate::prelude::FilterOpt< -/// crate::prelude::OriginFilter, -/// >, -/// event_filter: crate::prelude::FilterOpt, -/// ) -> Self { -/// Self { -/// origin_filter, -/// event_filter, -/// } -/// } -/// #[doc = r" Get `origin_filter`"] -/// #[inline] -/// pub const fn origin_filter( -/// &self, -/// ) -> &crate::prelude::FilterOpt> { -/// &self.origin_filter -/// } -/// #[doc = r" Get `event_filter`"] -/// #[inline] -/// pub const fn event_filter(&self) -> &crate::prelude::FilterOpt { -/// &self.event_filter -/// } -/// } -/// impl crate::prelude::Filter for LayerFilter { -/// type TriggeringEventType = crate::prelude::LayerEvent; -/// fn matches(&self, event: &Self::TriggeringEventType) -> bool { -/// self.origin_filter.matches(event) && self.event_filter.matches(event) -/// } -/// } -/// #[doc = " Event filter for LayerEvent entity"] -/// #[allow(clippy::enum_variant_names, missing_docs)] -/// #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Decode, Encode, Deserialize, Serialize, IntoSchema)] -/// pub enum LayerEventFilter { -/// ByCreated, -/// BySubLayer(crate::prelude::FilterOpt), -/// } -/// impl crate::prelude::Filter for LayerEventFilter { -/// type TriggeringEventType = crate::prelude::LayerEvent; -/// fn matches(&self, event: &crate::prelude::LayerEvent) -> bool { -/// match (self, event) { -/// (Self::ByCreated, crate::prelude::LayerEvent::Created(_)) => true, -/// (Self::BySubLayer(filter_opt), crate::prelude::LayerEvent::SubLayer(event)) => { -/// filter_opt.matches(event) -/// } -/// _ => false, -/// } -/// } -/// } */ -/// ``` -/// -/// ## A note on `#[derive(...)]` limitations -/// -/// This proc-macro crate parses the `#[derive(...)]` attributes. -/// Due to technical limitations of proc macros, it does not have access to the resolved path of the macro, only to what is written in the derive. -/// As such, it cannot support derives that are used through aliases, such as -/// -/// ```ignore -/// use getset::Getters as GettersAlias; -/// #[derive(GettersAlias)] -/// pub struct Hello { -/// // ... -/// } -/// ``` -/// -/// It assumes that the derive is imported and referred to by its original name. -#[manyhow] -#[proc_macro_derive(Filter)] -pub fn filter_derive(input: TokenStream) -> TokenStream { - let mut emitter = Emitter::new(); - - let Some(input) = emitter.handle(syn::parse2(input)) else { - return emitter.finish_token_stream(); - }; - - let result = filter::impl_filter(&mut emitter, &input); - emitter.finish_token_stream_with(result) -} - /// Derive `::serde::Serialize` trait for `enum` with possibility to avoid tags for selected variants /// /// ``` @@ -653,3 +478,83 @@ pub fn has_origin_derive(input: TokenStream) -> TokenStream { emitter.finish_token_stream_with(result) } + +/// Create an event set structure from an event enum. +/// +/// Event set is a set of multiple event types, allowing to efficiently test whether an event is in a set. +/// +/// For this event enum: +/// +/// ```rust +/// # use iroha_data_model_derive::EventSet; +/// #[derive(EventSet)] +/// pub enum TestEvent { +/// Event1, +/// Event2, +/// NestedEvent(AnotherEvent), +/// } +/// pub struct AnotherEvent; +/// ``` +/// +/// The macro will generate a `TestEventSet` struct. +/// +/// It will have associated constants that correspond to a singleton set for each event that can be accessed like `TestEventSet::Event1`. +/// +/// The sets can be combined either with a `|` operator or with the `or` method to match multiple events at once: `TestEventSet::Event1 | TestEventSet::Event2`. +/// +/// Variants that: +/// 1. have a single unnamed field +/// 2. have the type name end with `Event` +/// +/// are treated as nested events and their set constant has `Any` prepended to the name. For example, `TestEventSet::AnyNestedEvent`. +/// +/// The set has the following methods: +/// ```ignore +/// impl TestEventSet { +/// /// Returns a set that doesn't contain any events +/// const fn empty() -> TestEventSet; +/// /// Returns a set that contains all events +/// const fn all() -> TestEventSet; +/// /// Computes union of two sets, const form of the `|` operator +/// const fn or(self, other: TestEventSet) -> TestEventSet; +/// /// Checks if the set contains a specific event +/// const fn matches(&self, event: &TestEvent) -> bool; +/// } +/// ``` +/// +/// Implemented traits: +/// ```ignore +/// #[derive( +/// Copy, +/// Clone, +/// PartialEq, +/// Eq, +/// PartialOrd, +/// Ord, +/// Hash, +/// parity_scale_codec::Decode, +/// parity_scale_codec::Encode, +/// iroha_schema::IntoSchema, +/// )] +/// +/// /// Prints the list of events in the set +/// /// +/// /// For example: `TestEventSet[Event1, AnyNestedEvent]` +/// impl core::fmt::Debug; +/// +/// /// Union of two sets +/// impl core::ops::BitOr; +/// ``` +#[manyhow] +#[proc_macro_derive(EventSet)] +pub fn event_set_derive(input: TokenStream) -> TokenStream { + let mut emitter = Emitter::new(); + + let Some(input) = emitter.handle(syn::parse2(input)) else { + return emitter.finish_token_stream(); + }; + + let result = event_set::impl_event_set_derive(&mut emitter, &input); + + emitter.finish_token_stream_with(result) +} diff --git a/data_model/derive/tests/event_set.rs b/data_model/derive/tests/event_set.rs new file mode 100644 index 00000000000..9af5f782506 --- /dev/null +++ b/data_model/derive/tests/event_set.rs @@ -0,0 +1,166 @@ +mod events { + use iroha_data_model_derive::EventSet; + #[derive(EventSet)] + pub enum TestEvent { + Event1, + Event2, + NestedEvent(AnotherEvent), + } + + pub struct AnotherEvent; +} + +use events::{AnotherEvent, TestEvent, TestEventSet}; +use serde_json::json; + +#[test] +fn serialize() { + assert_eq!( + serde_json::to_value(TestEventSet::Event1).unwrap(), + json!(["Event1"]) + ); + assert_eq!( + serde_json::to_value(TestEventSet::Event1 | TestEventSet::Event2).unwrap(), + json!(["Event1", "Event2"]) + ); + assert_eq!( + serde_json::to_value(TestEventSet::Event1 | TestEventSet::AnyNestedEvent).unwrap(), + json!(["Event1", "AnyNestedEvent"]) + ); + assert_eq!( + serde_json::to_value(TestEventSet::all()).unwrap(), + json!(["Event1", "Event2", "AnyNestedEvent"]) + ); +} + +#[test] +fn deserialize() { + assert_eq!( + serde_json::from_value::(json!([])).unwrap(), + TestEventSet::empty() + ); + assert_eq!( + serde_json::from_value::(json!(["Event1"])).unwrap(), + TestEventSet::Event1 + ); + assert_eq!( + serde_json::from_value::(json!(["Event1", "Event2"])).unwrap(), + TestEventSet::Event1 | TestEventSet::Event2 + ); + assert_eq!( + serde_json::from_value::(json!(["Event1", "AnyNestedEvent"])).unwrap(), + TestEventSet::Event1 | TestEventSet::AnyNestedEvent + ); + assert_eq!( + serde_json::from_value::(json!(["Event1", "Event2", "AnyNestedEvent"])) + .unwrap(), + TestEventSet::all(), + ); + + assert_eq!( + serde_json::from_value::(json!(["Event1", "Event1", "AnyNestedEvent"])) + .unwrap(), + TestEventSet::Event1 | TestEventSet::AnyNestedEvent, + ); +} + +#[test] +fn deserialize_invalid() { + assert_eq!( + serde_json::from_value::(json!(32)) + .unwrap_err() + .to_string(), + "invalid type: integer `32`, expected a sequence of strings" + ); + + assert_eq!( + serde_json::from_value::(json!([32])) + .unwrap_err() + .to_string(), + "invalid type: integer `32`, expected a string" + ); + + assert_eq!( + serde_json::from_value::(json!(["InvalidVariant"])) + .unwrap_err() + .to_string(), + "unknown event variant `InvalidVariant`, expected one of `Event1`, `Event2`, `AnyNestedEvent`" + ); + + assert_eq!( + serde_json::from_value::(json!(["Event1", "Event1", "InvalidVariant"])) + .unwrap_err() + .to_string(), + "unknown event variant `InvalidVariant`, expected one of `Event1`, `Event2`, `AnyNestedEvent`" + ); +} + +#[test] +fn full_set() { + let any_matcher = TestEventSet::all(); + assert_eq!( + any_matcher, + TestEventSet::Event1 | TestEventSet::Event2 | TestEventSet::AnyNestedEvent + ); + + assert_eq!( + format!("{any_matcher:?}"), + "TestEventSet[Event1, Event2, AnyNestedEvent]" + ); + + assert!(any_matcher.matches(&TestEvent::Event1)); + assert!(any_matcher.matches(&TestEvent::Event2)); + assert!(any_matcher.matches(&TestEvent::NestedEvent(AnotherEvent))); +} + +#[test] +fn empty_set() { + let none_matcher = TestEventSet::empty(); + + assert_eq!(format!("{none_matcher:?}"), "TestEventSet[]"); + + assert!(!none_matcher.matches(&TestEvent::Event1)); + assert!(!none_matcher.matches(&TestEvent::Event2)); + assert!(!none_matcher.matches(&TestEvent::NestedEvent(AnotherEvent))); +} + +#[test] +fn event1_set() { + let event1_matcher = TestEventSet::Event1; + + assert_eq!(format!("{event1_matcher:?}"), "TestEventSet[Event1]"); + + assert!(event1_matcher.matches(&TestEvent::Event1)); + assert!(!event1_matcher.matches(&TestEvent::Event2)); + assert!(!event1_matcher.matches(&TestEvent::NestedEvent(AnotherEvent))); +} + +#[test] +fn event1_or_2_set() { + let event1_or_2_matcher = TestEventSet::Event1 | TestEventSet::Event2; + + assert_eq!( + format!("{event1_or_2_matcher:?}"), + "TestEventSet[Event1, Event2]" + ); + + assert!(event1_or_2_matcher.matches(&TestEvent::Event1)); + assert!(event1_or_2_matcher.matches(&TestEvent::Event2)); + assert!(!event1_or_2_matcher.matches(&TestEvent::NestedEvent(AnotherEvent))); +} + +#[test] +fn repeated() { + assert_eq!( + TestEventSet::Event1 | TestEventSet::Event1, + TestEventSet::Event1 + ); + assert_eq!( + TestEventSet::Event1 | TestEventSet::Event2 | TestEventSet::Event1, + TestEventSet::Event1 | TestEventSet::Event2 + ); + assert_eq!( + TestEventSet::all() | TestEventSet::AnyNestedEvent, + TestEventSet::all() + ); +} diff --git a/data_model/derive/tests/ui_pass/filter.rs b/data_model/derive/tests/ui_pass/filter.rs deleted file mode 100644 index 94dccc72e95..00000000000 --- a/data_model/derive/tests/ui_pass/filter.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! A smoke-test for the `derive(Filter)` - -use iroha_data_model::{ - prelude::{HasOrigin, Identifiable}, - IdBox, -}; -use iroha_data_model_derive::{Filter, IdEqOrdHash}; -use iroha_schema::IntoSchema; -use parity_scale_codec::{Decode, Encode}; -use serde::{Deserialize, Serialize}; - -// These are dummy types for the FilterDerive to work -// They would not work with `feature = transparent_api`, but are enough for the smoke test -mod prelude { - use iroha_schema::IntoSchema; - use parity_scale_codec::{Decode, Encode}; - use serde::{Deserialize, Serialize}; - - #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, IntoSchema)] - pub struct FilterOpt(T); - - #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, IntoSchema)] - pub struct OriginFilter(T); - - pub use super::LayerEvent; -} - -#[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - Encode, - Decode, - IntoSchema, -)] -pub struct SubLayerEvent; - -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, IntoSchema)] -pub struct SubLayerFilter; - -#[derive( - Copy, - Clone, - IntoSchema, - Ord, - PartialOrd, - Eq, - PartialEq, - Serialize, - Deserialize, - Decode, - Encode, - Debug, - Hash, -)] -pub struct LayerId { - name: u32, -} - -impl HasOrigin for LayerEvent { - type Origin = Layer; - - fn origin_id(&self) -> &::Id { - todo!() - } -} - -#[derive(Debug, IdEqOrdHash)] -pub struct Layer { - id: LayerId, -} - -impl From for IdBox { - fn from(_: LayerId) -> Self { - unreachable!() - } -} - -/// The tested type -#[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - Encode, - Decode, - IntoSchema, - Filter, -)] -pub enum LayerEvent { - SubLayer(SubLayerEvent), - Created(LayerId), -} - -fn main() {} diff --git a/data_model/src/events/data/events.rs b/data_model/src/events/data/events.rs index 29472ed2c5d..618007e6522 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -2,7 +2,7 @@ #![allow(missing_docs)] use getset::Getters; -use iroha_data_model_derive::{model, Filter, HasOrigin}; +use iroha_data_model_derive::{model, EventSet, HasOrigin}; use iroha_primitives::numeric::Numeric; pub use self::model::*; @@ -18,8 +18,8 @@ macro_rules! data_event { Eq, PartialOrd, Ord, - Filter, HasOrigin, + EventSet, parity_scale_codec::Decode, parity_scale_codec::Encode, serde::Deserialize, @@ -33,6 +33,8 @@ macro_rules! data_event { }; } +// NOTE: if adding/editing events here, make sure to update the corresponding event filter in [`super::filter`] + #[model] pub mod model { use super::*; @@ -485,7 +487,7 @@ mod trigger { use super::*; data_event! { - #[has_origin(origin = Trigger)] + #[has_origin(origin = Trigger)] pub enum TriggerEvent { Created(TriggerId), Deleted(TriggerId), @@ -547,6 +549,8 @@ mod executor { #[cfg(not(feature = "std"))] use alloc::{format, string::String, vec::Vec}; + use iroha_data_model_derive::EventSet; + #[derive( Debug, Copy, @@ -560,6 +564,7 @@ mod executor { serde::Deserialize, serde::Serialize, iroha_schema::IntoSchema, + EventSet, )] #[non_exhaustive] #[ffi_type] @@ -568,39 +573,6 @@ mod executor { pub enum ExecutorEvent { Upgraded, } - - /// Filter for [`ExecutorEvent`]. - #[derive( - Debug, - Copy, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - parity_scale_codec::Decode, - parity_scale_codec::Encode, - serde::Deserialize, - serde::Serialize, - iroha_schema::IntoSchema, - )] - #[non_exhaustive] - #[serde(untagged)] // Unaffected by #3330, as single unit variant - #[repr(transparent)] - pub enum ExecutorFilter { - Upgraded, - } - } - - #[cfg(feature = "transparent_api")] - impl super::Filter for ExecutorFilter { - type Event = ExecutorEvent; - - fn matches(&self, event: &Self::Event) -> bool { - match (self, event) { - (Self::Upgraded, Self::Event::Upgraded) => true, - } - } } } @@ -647,24 +619,19 @@ impl DataEvent { pub mod prelude { pub use super::{ - account::{ - AccountEvent, AccountEventFilter, AccountFilter, AccountPermissionChanged, - AccountRoleChanged, - }, + account::{AccountEvent, AccountEventSet, AccountPermissionChanged, AccountRoleChanged}, asset::{ - AssetChanged, AssetDefinitionEvent, AssetDefinitionEventFilter, AssetDefinitionFilter, + AssetChanged, AssetDefinitionEvent, AssetDefinitionEventSet, AssetDefinitionOwnerChanged, AssetDefinitionTotalQuantityChanged, AssetEvent, - AssetEventFilter, AssetFilter, + AssetEventSet, }, - config::ConfigurationEvent, - domain::{DomainEvent, DomainEventFilter, DomainFilter, DomainOwnerChanged}, - executor::{ExecutorEvent, ExecutorFilter}, - peer::{PeerEvent, PeerEventFilter, PeerFilter}, + config::{ConfigurationEvent, ConfigurationEventSet}, + domain::{DomainEvent, DomainEventSet, DomainOwnerChanged}, + executor::{ExecutorEvent, ExecutorEventSet}, + peer::{PeerEvent, PeerEventSet}, permission::PermissionTokenSchemaUpdateEvent, - role::{RoleEvent, RoleEventFilter, RoleFilter, RolePermissionChanged}, - trigger::{ - TriggerEvent, TriggerEventFilter, TriggerFilter, TriggerNumberOfExecutionsChanged, - }, + role::{RoleEvent, RoleEventSet, RolePermissionChanged}, + trigger::{TriggerEvent, TriggerEventSet, TriggerNumberOfExecutionsChanged}, DataEvent, HasOrigin, MetadataChanged, }; } diff --git a/data_model/src/events/data/filters.rs b/data_model/src/events/data/filters.rs index 2d497cd128c..77aed5d8558 100644 --- a/data_model/src/events/data/filters.rs +++ b/data_model/src/events/data/filters.rs @@ -1,25 +1,21 @@ -//! This module contains `EventFilter` and entities for filter +//! This module contains filters for data events. +//! +//! For each event in [`super::events`], there's a corresponding filter type in this module. It can filter the events by origin id and event type. +//! +//! Event types are filtered with an `EventSet` type, allowing to filter for multiple event types at once. use core::fmt::Debug; -use derive_more::Constructor; +use getset::Getters; use iroha_data_model_derive::model; pub use self::model::*; use super::*; -/// Filter for all events -pub type DataEventFilter = FilterOpt; - #[model] pub mod model { use super::*; - /// Optional filter. May pass all items or may filter them by `F` - /// - /// It's better than `Optional` because `Optional` already has its own `filter` method and it - /// would be ugly to use fully qualified syntax to call `Filter::filter()` method on it. - /// Also `FilterOpt` variant names look better for filter needs #[derive( Debug, Clone, @@ -27,166 +23,720 @@ pub mod model { Eq, PartialOrd, Ord, + FromVariant, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + pub enum DataEventFilter { + /// Matches any data events ([`DataEvent`]) + Any, + /// Matches [`PeerEvent`]s + Peer(PeerEventFilter), + /// Matches [`DomainEvent`]s + Domain(DomainEventFilter), + /// Matches [`AccountEvent`]s + Account(AccountEventFilter), + /// Matches [`AssetEvent`]s + Asset(AssetEventFilter), + /// Matches [`AssetDefinitionEvent`]s + AssetDefinition(AssetDefinitionEventFilter), + /// Matches [`TriggerEvent`]s + Trigger(TriggerEventFilter), + /// Matches [`RoleEvent`]s + Role(RoleEventFilter), + /// Matches [`PermissionTokenSchemaUpdateEvent`]s + // nothing to filter for, really + PermissionTokenSchemaUpdate, + /// Matches [`ConfigurationEvent`]s + Configuration(ConfigurationEventFilter), + /// Matches [`ExecutorEvent`]s + Executor(ExecutorEventFilter), + } + + /// An event filter for [`PeerEvent`]s + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + pub struct PeerEventFilter { + /// If specified matches only events originating from this peer + pub(super) id_matcher: Option, + /// Matches only event from this set + pub(super) event_set: PeerEventSet, + } + + /// An event filter for [`DomainEvent`]s + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + pub struct DomainEventFilter { + /// If specified matches only events originating from this domain + pub(super) id_matcher: Option, + /// Matches only event from this set + pub(super) event_set: DomainEventSet, + } + + /// An event filter for [`AccountEvent`]s + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, )] - #[serde(untagged)] // Unaffected by #3330 - pub enum FilterOpt { - /// Accept all items that will be passed to `filter()` method - #[serde(with = "accept_all_as_string")] - AcceptAll, - /// Use filter `F` to choose acceptable items passed to `filter()` method - BySome(F), + pub struct AccountEventFilter { + /// If specified matches only events originating from this account + pub(super) id_matcher: Option, + /// Matches only event from this set + pub(super) event_set: AccountEventSet, } + /// An event filter for [`AssetEvent`]s #[derive( - Debug, Clone, PartialEq, Eq, FromVariant, Decode, Encode, Deserialize, Serialize, IntoSchema, + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + pub struct AssetEventFilter { + /// If specified matches only events originating from this asset + pub(super) id_matcher: Option, + /// Matches only event from this set + pub(super) event_set: AssetEventSet, + } + + /// An event filter for [`AssetDefinitionEvent`]s + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, )] - #[allow(clippy::enum_variant_names)] - /// Filters event by entity - pub enum DataEntityFilter { - /// Filter by Peer entity. `AcceptAll` value will accept all `Peer` events - ByPeer(FilterOpt), - /// Filter by Domain entity. `AcceptAll` value will accept all `Domain` events - ByDomain(FilterOpt), - /// Filter by Trigger entity. `AcceptAll` value will accept all `Trigger` events - ByTrigger(FilterOpt), - /// Filter by Role entity. `AcceptAll` value will accept all `Role` events - ByRole(FilterOpt), - } - - /// Filter that accepts a data event with the matching origin. + pub struct AssetDefinitionEventFilter { + /// If specified matches only events originating from this asset definition + pub(super) id_matcher: Option, + /// Matches only event from this set + pub(super) event_set: AssetDefinitionEventSet, + } + + /// An event filter for [`TriggerEvent`]s #[derive( + Debug, Clone, + PartialEq, + Eq, PartialOrd, Ord, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + pub struct TriggerEventFilter { + /// If specified matches only events originating from this trigger + pub(super) id_matcher: Option, + /// Matches only event from this set + pub(super) event_set: TriggerEventSet, + } + + /// An event filter for [`RoleEvent`]s + #[derive( + Debug, + Clone, + PartialEq, Eq, + PartialOrd, + Ord, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + pub struct RoleEventFilter { + /// If specified matches only events originating from this role + pub(super) id_matcher: Option, + /// Matches only event from this set + pub(super) event_set: RoleEventSet, + } + + /// An event filter for [`ConfigurationEvent`]s + #[derive( Debug, - Constructor, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, Decode, Encode, + Deserialize, Serialize, + IntoSchema, + )] + pub struct ConfigurationEventFilter { + /// If specified matches only events originating from this configuration + pub(super) id_matcher: Option, + /// Matches only event from this set + pub(super) event_set: ConfigurationEventSet, + } + + /// An event filter for [`ExecutorEvent`]. + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, + Decode, + Encode, Deserialize, + Serialize, IntoSchema, )] - #[serde(bound( - deserialize = "<::Origin as Identifiable>::Id: Deserialize<'de>" - ))] - #[serde(transparent)] - #[repr(transparent)] - pub struct OriginFilter(pub(super) ::Id) - where - ::Id: - Debug + Clone + Eq + Ord + Decode + Encode + Serialize + IntoSchema; + pub struct ExecutorEventFilter { + // executor is a global entity, so no id here + /// Matches only event from this set + pub(super) event_set: ExecutorEventSet, + } } -mod accept_all_as_string { - //! Module to (de-)serialize `FilterOpt::AcceptAll` variant as string +impl PeerEventFilter { + /// Creates a new [`PeerEventFilter`] accepting all [`PeerEvent`]s. + pub const fn new() -> Self { + Self { + id_matcher: None, + event_set: PeerEventSet::all(), + } + } - #[cfg(not(feature = "std"))] - use alloc::format; + /// Modifies a [`PeerEventFilter`] to accept only [`PeerEvent`]s originating from ids matching `id_matcher`. + #[must_use] + pub fn for_peer(mut self, id_matcher: PeerId) -> Self { + self.id_matcher = Some(id_matcher); + self + } - use serde::{Deserializer, Serializer}; + /// Modifies a [`PeerEventFilter`] to accept only [`PeerEvent`]s of types contained in `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: PeerEventSet) -> Self { + self.event_set = event_set; + self + } +} - /// Serialize bytes using `base64` - pub fn serialize(serializer: S) -> Result { - serializer.serialize_str("AcceptAll") +impl Default for PeerEventFilter { + fn default() -> Self { + Self::new() } +} - /// Deserialize bytes using `base64` - pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<(), D::Error> { - struct Vis; +#[cfg(feature = "transparent_api")] +impl EventFilter for PeerEventFilter { + type Event = super::PeerEvent; - impl serde::de::Visitor<'_> for Vis { - type Value = (); + fn matches(&self, event: &Self::Event) -> bool { + if let Some(id_matcher) = &self.id_matcher { + if id_matcher != event.origin_id() { + return false; + } + } + + if !self.event_set.matches(event) { + return false; + } + + true + } +} + +impl DomainEventFilter { + /// Creates a new [`DomainEventFilter`] accepting all [`DomainEvent`]s. + pub const fn new() -> Self { + Self { + id_matcher: None, + event_set: DomainEventSet::all(), + } + } + + /// Modifies a [`DomainEventFilter`] to accept only [`DomainEvent`]s originating from ids matching `id_matcher`. + #[must_use] + pub fn for_domain(mut self, id_matcher: DomainId) -> Self { + self.id_matcher = Some(id_matcher); + self + } - fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { - formatter.write_str("an AcceptAll string") + /// Modifies a [`DomainEventFilter`] to accept only [`DomainEvent`]s of types contained in `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: DomainEventSet) -> Self { + self.event_set = event_set; + self + } +} + +impl Default for DomainEventFilter { + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "transparent_api")] +impl EventFilter for DomainEventFilter { + type Event = super::DomainEvent; + + fn matches(&self, event: &Self::Event) -> bool { + if let Some(id_matcher) = &self.id_matcher { + if id_matcher != event.origin_id() { + return false; } + } - fn visit_str(self, s: &str) -> Result { - if s == "AcceptAll" { - Ok(()) - } else { - Err(E::custom(format!("expected AcceptAll, got {s}"))) - } + if !self.event_set.matches(event) { + return false; + } + + true + } +} + +impl AccountEventFilter { + /// Creates a new [`AccountEventFilter`] accepting all [`AccountEvent`]s. + pub const fn new() -> Self { + Self { + id_matcher: None, + event_set: AccountEventSet::all(), + } + } + + /// Modifies a [`AccountEventFilter`] to accept only [`AccountEvent`]s originating from ids matching `id_matcher`. + #[must_use] + pub fn for_account(mut self, id_matcher: AccountId) -> Self { + self.id_matcher = Some(id_matcher); + self + } + + /// Modifies a [`AccountEventFilter`] to accept only [`AccountEvent`]s of types contained in `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: AccountEventSet) -> Self { + self.event_set = event_set; + self + } +} + +impl Default for AccountEventFilter { + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "transparent_api")] +impl super::EventFilter for AccountEventFilter { + type Event = super::AccountEvent; + + fn matches(&self, event: &Self::Event) -> bool { + if let Some(id_matcher) = &self.id_matcher { + if id_matcher != event.origin_id() { + return false; } } - deserializer.deserialize_str(Vis) + + if !self.event_set.matches(event) { + return false; + } + + true + } +} + +impl AssetEventFilter { + /// Creates a new [`AssetEventFilter`] accepting all [`AssetEvent`]s. + pub const fn new() -> Self { + Self { + id_matcher: None, + event_set: AssetEventSet::all(), + } + } + + /// Modifies a [`AssetEventFilter`] to accept only [`AssetEvent`]s originating from ids matching `id_matcher`. + #[must_use] + pub fn for_asset(mut self, id_matcher: AssetId) -> Self { + self.id_matcher = Some(id_matcher); + self + } + + /// Modifies a [`AssetEventFilter`] to accept only [`AssetEvent`]s of types contained in `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: AssetEventSet) -> Self { + self.event_set = event_set; + self + } +} + +impl Default for AssetEventFilter { + fn default() -> Self { + Self::new() } } #[cfg(feature = "transparent_api")] -impl Filter for FilterOpt { - type Event = F::Event; +impl super::EventFilter for AssetEventFilter { + type Event = super::AssetEvent; + + fn matches(&self, event: &Self::Event) -> bool { + if let Some(id_matcher) = &self.id_matcher { + if id_matcher != event.origin_id() { + return false; + } + } - fn matches(&self, item: &Self::Event) -> bool { - match self { - Self::AcceptAll => true, - Self::BySome(filter) => filter.matches(item), + if !self.event_set.matches(event) { + return false; } + + true + } +} + +impl AssetDefinitionEventFilter { + /// Creates a new [`AssetDefinitionEventFilter`] accepting all [`AssetDefinitionEvent`]s. + pub const fn new() -> Self { + Self { + id_matcher: None, + event_set: AssetDefinitionEventSet::all(), + } + } + + /// Modifies a [`AssetDefinitionEventFilter`] to accept only [`AssetDefinitionEvent`]s originating from ids matching `id_matcher`. + #[must_use] + pub fn for_asset_definition(mut self, id_matcher: AssetDefinitionId) -> Self { + self.id_matcher = Some(id_matcher); + self + } + + /// Modifies a [`AssetDefinitionEventFilter`] to accept only [`AssetDefinitionEvent`]s of types contained in `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: AssetDefinitionEventSet) -> Self { + self.event_set = event_set; + self + } +} + +impl Default for AssetDefinitionEventFilter { + fn default() -> Self { + Self::new() } } #[cfg(feature = "transparent_api")] -impl Filter for DataEntityFilter { - type Event = DataEvent; +impl super::EventFilter for AssetDefinitionEventFilter { + type Event = super::AssetDefinitionEvent; - fn matches(&self, event: &DataEvent) -> bool { - match (self, event) { - (Self::ByPeer(filter_opt), DataEvent::Peer(peer)) => filter_opt.matches(peer), - (Self::ByDomain(filter_opt), DataEvent::Domain(domain)) => filter_opt.matches(domain), - (Self::ByTrigger(filter_opt), DataEvent::Trigger(trigger)) => { - filter_opt.matches(trigger) + fn matches(&self, event: &Self::Event) -> bool { + if let Some(id_matcher) = &self.id_matcher { + if id_matcher != event.origin_id() { + return false; } - (Self::ByRole(filter_opt), DataEvent::Role(role)) => filter_opt.matches(role), - _ => false, } + + if !self.event_set.matches(event) { + return false; + } + + true } } -impl OriginFilter -where - ::Id: - Debug + Clone + Eq + Ord + Decode + Encode + Serialize + IntoSchema, -{ - /// Get the id of the origin of the data event that this filter accepts. - pub fn origin_id(&self) -> &::Id { - &self.0 +impl TriggerEventFilter { + /// Creates a new [`TriggerEventFilter`] accepting all [`TriggerEvent`]s. + pub const fn new() -> Self { + Self { + id_matcher: None, + event_set: TriggerEventSet::all(), + } + } + + /// Modifies a [`TriggerEventFilter`] to accept only [`TriggerEvent`]s originating from ids matching `id_matcher`. + #[must_use] + pub fn for_trigger(mut self, id_matcher: TriggerId) -> Self { + self.id_matcher = Some(id_matcher); + self + } + + /// Modifies a [`TriggerEventFilter`] to accept only [`TriggerEvent`]s of types matching `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: TriggerEventSet) -> Self { + self.event_set = event_set; + self + } +} + +impl Default for TriggerEventFilter { + fn default() -> Self { + Self::new() } } #[cfg(feature = "transparent_api")] -impl Filter for OriginFilter -where - ::Id: - Debug + Clone + Eq + Ord + Decode + Encode + Serialize + IntoSchema, -{ - type Event = T; +impl super::EventFilter for TriggerEventFilter { + type Event = super::TriggerEvent; - fn matches(&self, event: &T) -> bool { - event.origin_id() == self.origin_id() + fn matches(&self, event: &Self::Event) -> bool { + if let Some(id_matcher) = &self.id_matcher { + if id_matcher != event.origin_id() { + return false; + } + } + + if !self.event_set.matches(event) { + return false; + } + + true + } +} + +impl RoleEventFilter { + /// Creates a new [`RoleEventFilter`] accepting all [`RoleEvent`]s. + pub const fn new() -> Self { + Self { + id_matcher: None, + event_set: RoleEventSet::all(), + } + } + + /// Modifies a [`RoleEventFilter`] to accept only [`RoleEvent`]s originating from ids matching `id_matcher`. + #[must_use] + pub fn for_role(mut self, id_matcher: RoleId) -> Self { + self.id_matcher = Some(id_matcher); + self + } + + /// Modifies a [`RoleEventFilter`] to accept only [`RoleEvent`]s of types matching `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: RoleEventSet) -> Self { + self.event_set = event_set; + self } } -impl PartialEq for OriginFilter -where - ::Id: - Debug + Clone + Eq + Ord + Decode + Encode + Serialize + IntoSchema, -{ - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 +impl Default for RoleEventFilter { + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "transparent_api")] +impl super::EventFilter for RoleEventFilter { + type Event = super::RoleEvent; + + fn matches(&self, event: &Self::Event) -> bool { + if let Some(id_matcher) = &self.id_matcher { + if id_matcher != event.origin_id() { + return false; + } + } + + if !self.event_set.matches(event) { + return false; + } + + true + } +} + +impl ConfigurationEventFilter { + /// Creates a new [`ConfigurationEventFilter`] accepting all [`ConfigurationEvent`]s. + pub const fn new() -> Self { + Self { + id_matcher: None, + event_set: ConfigurationEventSet::all(), + } + } + + /// Modifies a [`ConfigurationEventFilter`] to accept only [`ConfigurationEvent`]s originating from ids matching `id_matcher`. + #[must_use] + pub fn for_parameter(mut self, id_matcher: ParameterId) -> Self { + self.id_matcher = Some(id_matcher); + self + } + + /// Modifies a [`ConfigurationEventFilter`] to accept only [`ConfigurationEvent`]s of types matching `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: ConfigurationEventSet) -> Self { + self.event_set = event_set; + self + } +} + +impl Default for ConfigurationEventFilter { + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "transparent_api")] +impl super::EventFilter for ConfigurationEventFilter { + type Event = super::ConfigurationEvent; + + fn matches(&self, event: &Self::Event) -> bool { + if let Some(id_matcher) = &self.id_matcher { + if id_matcher != event.origin_id() { + return false; + } + } + + if !self.event_set.matches(event) { + return false; + } + + true + } +} + +impl ExecutorEventFilter { + /// Creates a new [`ExecutorEventFilter`] accepting all [`ExecutorEvent`]s. + pub const fn new() -> Self { + Self { + event_set: ExecutorEventSet::all(), + } + } + + /// Modifies a [`ExecutorEventFilter`] to accept only [`ExecutorEvent`]s of types matching `event_set`. + #[must_use] + pub const fn for_events(mut self, event_set: ExecutorEventSet) -> Self { + self.event_set = event_set; + self + } +} + +impl Default for ExecutorEventFilter { + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "transparent_api")] +impl super::EventFilter for ExecutorEventFilter { + type Event = super::ExecutorEvent; + + fn matches(&self, event: &Self::Event) -> bool { + if !self.event_set.matches(event) { + return false; + } + + true + } +} + +#[cfg(feature = "transparent_api")] +impl EventFilter for DataEventFilter { + type Event = DataEvent; + + fn matches(&self, event: &DataEvent) -> bool { + use DataEventFilter::*; + + match (event, self) { + ( + DataEvent::Domain(DomainEvent::Account(AccountEvent::Asset(event))), + Asset(filter), + ) => filter.matches(event), + (DataEvent::Domain(DomainEvent::Account(event)), Account(filter)) => { + filter.matches(event) + } + (DataEvent::Domain(DomainEvent::AssetDefinition(event)), AssetDefinition(filter)) => { + filter.matches(event) + } + (DataEvent::Domain(event), Domain(filter)) => filter.matches(event), + + (DataEvent::Peer(event), Peer(filter)) => filter.matches(event), + (DataEvent::Trigger(event), Trigger(filter)) => filter.matches(event), + (DataEvent::Role(event), Role(filter)) => filter.matches(event), + (DataEvent::PermissionToken(_), PermissionTokenSchemaUpdate) => true, + (DataEvent::Configuration(event), Configuration(filter)) => filter.matches(event), + (DataEvent::Executor(event), Executor(filter)) => filter.matches(event), + + ( + DataEvent::Peer(_) + | DataEvent::Domain(_) + | DataEvent::Trigger(_) + | DataEvent::Role(_) + | DataEvent::PermissionToken(_) + | DataEvent::Configuration(_) + | DataEvent::Executor(_), + Any, + ) => true, + ( + DataEvent::Peer(_) + | DataEvent::Domain(_) + | DataEvent::Trigger(_) + | DataEvent::Role(_) + | DataEvent::PermissionToken(_) + | DataEvent::Configuration(_) + | DataEvent::Executor(_), + _, + ) => false, + } } } pub mod prelude { pub use super::{ - DataEntityFilter, DataEventFilter, - FilterOpt::{self, *}, - OriginFilter, + AccountEventFilter, AssetDefinitionEventFilter, AssetEventFilter, ConfigurationEventFilter, + DataEventFilter, DomainEventFilter, ExecutorEventFilter, PeerEventFilter, RoleEventFilter, + TriggerEventFilter, }; } @@ -239,31 +789,10 @@ mod tests { DomainEvent::Account(AccountEvent::Asset(AssetEvent::Created(asset))).into(); // test how the differently nested filters with with the events - // FIXME: rewrite the filters using the builder DSL https://github.com/hyperledger/iroha/issues/3068 - let domain_filter = BySome(DataEntityFilter::ByDomain(BySome(DomainFilter::new( - BySome(OriginFilter(domain_id)), - AcceptAll, - )))); - let account_filter = BySome(DataEntityFilter::ByDomain(BySome(DomainFilter::new( - // kind of unfortunately, we have to specify the domain id filter, - // even though we will filter it with the account id filter (account id contains domain id with and account name) - // FIXME: maybe make this more orthogonal by introducing a partial id (in account event filter only by account name) - AcceptAll, - BySome(DomainEventFilter::ByAccount(BySome(AccountFilter::new( - BySome(OriginFilter(account_id)), - AcceptAll, - )))), - )))); - let asset_filter = BySome(DataEntityFilter::ByDomain(BySome(DomainFilter::new( - AcceptAll, - BySome(DomainEventFilter::ByAccount(BySome(AccountFilter::new( - AcceptAll, - BySome(AccountEventFilter::ByAsset(BySome(AssetFilter::new( - BySome(OriginFilter(asset_id)), - AcceptAll, - )))), - )))), - )))); + let domain_filter = DataEventFilter::Domain(DomainEventFilter::new().for_domain(domain_id)); + let account_filter = + DataEventFilter::Account(AccountEventFilter::new().for_account(account_id)); + let asset_filter = DataEventFilter::Asset(AssetEventFilter::new().for_asset(asset_id)); // domain filter matches all of those, because all of those events happened in the same domain assert!(domain_filter.matches(&domain_created)); diff --git a/data_model/src/events/data/mod.rs b/data_model/src/events/data/mod.rs index 70d0cd0d66e..54fe343831d 100644 --- a/data_model/src/events/data/mod.rs +++ b/data_model/src/events/data/mod.rs @@ -11,7 +11,7 @@ use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; #[cfg(feature = "transparent_api")] -use super::Filter; +use super::EventFilter; use crate::prelude::*; pub use crate::Registered; diff --git a/data_model/src/events/execute_trigger.rs b/data_model/src/events/execute_trigger.rs index 9b05dde3163..eb672703b38 100644 --- a/data_model/src/events/execute_trigger.rs +++ b/data_model/src/events/execute_trigger.rs @@ -1,6 +1,5 @@ //! Trigger execution event and filter -use derive_more::Constructor; use getset::Getters; use iroha_data_model_derive::model; @@ -44,7 +43,8 @@ pub mod model { Ord, PartialEq, Eq, - Constructor, + Default, + Getters, Decode, Encode, Deserialize, @@ -53,21 +53,60 @@ pub mod model { )] pub struct ExecuteTriggerEventFilter { /// Id of trigger catch executions of - pub(super) trigger_id: TriggerId, + pub(super) trigger_id: Option, /// Authority of user who owns trigger - pub(super) authority: AccountId, + pub(super) authority: Option, + } +} + +impl ExecuteTriggerEventFilter { + /// Creates a new [`ExecuteTriggerEventFilter`] accepting all [`ExecuteTriggerEvent`]s + #[must_use] + #[inline] + pub const fn new() -> Self { + Self { + trigger_id: None, + authority: None, + } + } + + /// Modifies a [`ExecuteTriggerEventFilter`] to accept only [`ExecuteTriggerEvent`]s originating from a specific trigger + #[must_use] + #[inline] + pub fn for_trigger(mut self, trigger_id: TriggerId) -> Self { + self.trigger_id = Some(trigger_id); + self + } + + /// Modifies a [`ExecuteTriggerEventFilter`] to accept only [`ExecuteTriggerEvent`]s from triggers executed under specific authority + #[must_use] + #[inline] + pub fn under_authority(mut self, authority: AccountId) -> Self { + self.authority = Some(authority); + self } } #[cfg(feature = "transparent_api")] -impl Filter for ExecuteTriggerEventFilter { +impl EventFilter for ExecuteTriggerEventFilter { type Event = ExecuteTriggerEvent; /// Check if `event` matches filter /// /// Event considered as matched if trigger ids are equal fn matches(&self, event: &ExecuteTriggerEvent) -> bool { - self.trigger_id == event.trigger_id && self.authority == event.authority + if let Some(trigger_id) = &self.trigger_id { + if trigger_id != &event.trigger_id { + return false; + } + } + if let Some(authority) = &self.authority { + if authority != &event.authority { + return false; + } + } + + true } } diff --git a/data_model/src/events/mod.rs b/data_model/src/events/mod.rs index 37382ddcc8a..daa9e544e26 100644 --- a/data_model/src/events/mod.rs +++ b/data_model/src/events/mod.rs @@ -13,9 +13,9 @@ pub use self::model::*; pub mod data; pub mod execute_trigger; -pub mod notification; pub mod pipeline; pub mod time; +pub mod trigger_completed; #[model] pub mod model { @@ -46,8 +46,8 @@ pub mod model { Time(time::TimeEvent), /// Trigger execution event. ExecuteTrigger(execute_trigger::ExecuteTriggerEvent), - /// Notification event. - Notification(notification::NotificationEvent), + /// Trigger completion event. + TriggerCompleted(trigger_completed::TriggerCompletedEvent), } /// Event type which could invoke trigger execution. @@ -68,11 +68,22 @@ pub mod model { /// Event filter. #[allow(variant_size_differences)] #[derive( - Debug, Clone, PartialEq, Eq, FromVariant, Decode, Encode, Deserialize, Serialize, IntoSchema, + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + FromVariant, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, )] // TODO: Temporarily made opaque #[ffi_type(opaque)] - pub enum FilterBox { + pub enum EventFilterBox { /// Listen to pipeline events with filter. Pipeline(pipeline::PipelineEventFilter), /// Listen to data events with filter. @@ -81,18 +92,29 @@ pub mod model { Time(time::TimeEventFilter), /// Listen to trigger execution event with filter. ExecuteTrigger(execute_trigger::ExecuteTriggerEventFilter), - /// Listen to notifications event with filter. - Notification(notification::NotificationEventFilter), + /// Listen to trigger completion event with filter. + TriggerCompleted(trigger_completed::TriggerCompletedEventFilter), } /// Event filter which could be attached to trigger. #[allow(variant_size_differences)] #[derive( - Debug, Clone, PartialEq, Eq, FromVariant, Decode, Encode, Deserialize, Serialize, IntoSchema, + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + FromVariant, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, )] // TODO: Temporarily made opaque #[ffi_type(opaque)] - pub enum TriggeringFilterBox { + pub enum TriggeringEventFilterBox { /// Listen to pipeline events with filter. Pipeline(pipeline::PipelineEventFilter), /// Listen to data events with filter. @@ -106,7 +128,7 @@ pub mod model { /// Trait for filters #[cfg(feature = "transparent_api")] -pub trait Filter { +pub trait EventFilter { /// Type of event that can be filtered type Event; @@ -133,7 +155,7 @@ pub trait Filter { } #[cfg(feature = "transparent_api")] -impl Filter for FilterBox { +impl EventFilter for EventFilterBox { type Event = Event; /// Apply filter to event. @@ -143,26 +165,28 @@ impl Filter for FilterBox { (Event::Data(event), Self::Data(filter)) => filter.matches(event), (Event::Time(event), Self::Time(filter)) => filter.matches(event), (Event::ExecuteTrigger(event), Self::ExecuteTrigger(filter)) => filter.matches(event), - (Event::Notification(event), Self::Notification(filter)) => filter.matches(event), + (Event::TriggerCompleted(event), Self::TriggerCompleted(filter)) => { + filter.matches(event) + } // Fail to compile in case when new variant to event or filter is added ( Event::Pipeline(_) | Event::Data(_) | Event::Time(_) | Event::ExecuteTrigger(_) - | Event::Notification(_), + | Event::TriggerCompleted(_), Self::Pipeline(_) | Self::Data(_) | Self::Time(_) | Self::ExecuteTrigger(_) - | Self::Notification(_), + | Self::TriggerCompleted(_), ) => false, } } } #[cfg(feature = "transparent_api")] -impl Filter for TriggeringFilterBox { +impl EventFilter for TriggeringEventFilterBox { type Event = Event; /// Apply filter to event. @@ -178,7 +202,7 @@ impl Filter for TriggeringFilterBox { | Event::Data(_) | Event::Time(_) | Event::ExecuteTrigger(_) - | Event::Notification(_), + | Event::TriggerCompleted(_), Self::Pipeline(_) | Self::Data(_) | Self::Time(_) | Self::ExecuteTrigger(_), ) => false, } @@ -210,7 +234,7 @@ pub mod stream { /// Request sent by the client to subscribe to events. #[derive(Debug, Clone, Constructor, Decode, Encode, IntoSchema)] #[repr(transparent)] - pub struct EventSubscriptionRequest(pub FilterBox); + pub struct EventSubscriptionRequest(pub EventFilterBox); } impl From for Event { @@ -225,10 +249,10 @@ pub mod prelude { #[cfg(feature = "http")] pub use super::stream::{EventMessage, EventSubscriptionRequest}; #[cfg(feature = "transparent_api")] - pub use super::Filter; + pub use super::EventFilter; pub use super::{ - data::prelude::*, execute_trigger::prelude::*, notification::prelude::*, - pipeline::prelude::*, time::prelude::*, Event, FilterBox, TriggeringEventType, - TriggeringFilterBox, + data::prelude::*, execute_trigger::prelude::*, pipeline::prelude::*, time::prelude::*, + trigger_completed::prelude::*, Event, EventFilterBox, TriggeringEventFilterBox, + TriggeringEventType, }; } diff --git a/data_model/src/events/pipeline.rs b/data_model/src/events/pipeline.rs index 62a208fb0d9..5d3b962144f 100644 --- a/data_model/src/events/pipeline.rs +++ b/data_model/src/events/pipeline.rs @@ -28,6 +28,7 @@ pub mod model { PartialOrd, Ord, Default, + Getters, Decode, Encode, Serialize, @@ -154,33 +155,37 @@ pub mod model { } impl PipelineEventFilter { - /// Construct [`EventFilter`]. + /// Creates a new [`PipelineEventFilter`] accepting all [`PipelineEvent`]s #[must_use] #[inline] - pub fn new() -> Self { - Self::default() + pub const fn new() -> Self { + Self { + status_kind: None, + entity_kind: None, + hash: None, + } } - /// Filter by [`EntityKind`]. + /// Modifies a [`PipelineEventFilter`] to accept only [`PipelineEvent`]s originating from a specific entity kind (block/transaction). #[must_use] #[inline] - pub const fn entity_kind(mut self, entity_kind: PipelineEntityKind) -> Self { + pub const fn for_entity(mut self, entity_kind: PipelineEntityKind) -> Self { self.entity_kind = Some(entity_kind); self } - /// Filter by [`StatusKind`]. + /// Modifies a [`PipelineEventFilter`] to accept only [`PipelineEvent`]s with a specific status. #[must_use] #[inline] - pub const fn status_kind(mut self, status_kind: PipelineStatusKind) -> Self { + pub const fn for_status(mut self, status_kind: PipelineStatusKind) -> Self { self.status_kind = Some(status_kind); self } - /// Filter by [`struct@Hash`]. + /// Modifies a [`PipelineEventFilter`] to accept only [`PipelineEvent`]s originating from an entity with specified hash. #[must_use] #[inline] - pub const fn hash(mut self, hash: Hash) -> Self { + pub const fn for_hash(mut self, hash: Hash) -> Self { self.hash = Some(hash); self } @@ -193,7 +198,7 @@ impl PipelineEventFilter { } #[cfg(feature = "transparent_api")] -impl super::Filter for PipelineEventFilter { +impl super::EventFilter for PipelineEventFilter { type Event = PipelineEvent; /// Check if `self` accepts the `event`. @@ -230,7 +235,7 @@ mod tests { #[cfg(not(feature = "std"))] use alloc::{string::ToString as _, vec, vec::Vec}; - use super::{super::Filter, PipelineRejectionReason::*, *}; + use super::{super::EventFilter, PipelineRejectionReason::*, *}; use crate::{transaction::error::TransactionRejectionReason::*, ValidationFail}; #[test] @@ -277,7 +282,7 @@ mod tests { events .iter() .filter(|&event| PipelineEventFilter::new() - .hash(Hash::prehashed([0_u8; Hash::LENGTH])) + .for_hash(Hash::prehashed([0_u8; Hash::LENGTH])) .matches(event)) .cloned() .collect::>() @@ -291,7 +296,7 @@ mod tests { events .iter() .filter(|&event| PipelineEventFilter::new() - .entity_kind(PipelineEntityKind::Block) + .for_entity(PipelineEntityKind::Block) .matches(event)) .cloned() .collect::>() @@ -305,8 +310,8 @@ mod tests { events .iter() .filter(|&event| PipelineEventFilter::new() - .entity_kind(PipelineEntityKind::Transaction) - .hash(Hash::prehashed([2_u8; Hash::LENGTH])) + .for_entity(PipelineEntityKind::Transaction) + .for_hash(Hash::prehashed([2_u8; Hash::LENGTH])) .matches(event)) .cloned() .collect::>() diff --git a/data_model/src/events/time.rs b/data_model/src/events/time.rs index dc676145f78..65ed91682ad 100644 --- a/data_model/src/events/time.rs +++ b/data_model/src/events/time.rs @@ -132,7 +132,7 @@ pub mod model { } #[cfg(feature = "transparent_api")] -impl Filter for TimeEventFilter { +impl EventFilter for TimeEventFilter { type Event = TimeEvent; /// Isn't useful for time-triggers diff --git a/data_model/src/events/notification.rs b/data_model/src/events/trigger_completed.rs similarity index 72% rename from data_model/src/events/notification.rs rename to data_model/src/events/trigger_completed.rs index 8f98f3f0c73..9d0d43d195c 100644 --- a/data_model/src/events/notification.rs +++ b/data_model/src/events/trigger_completed.rs @@ -19,27 +19,6 @@ use crate::trigger::TriggerId; pub mod model { use super::*; - /// Notification event for events that arise during block application process like trigger execution for example - #[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - FromVariant, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - #[ffi_type] - #[non_exhaustive] - pub enum NotificationEvent { - TriggerCompleted(TriggerCompletedEvent), - } - /// Event that notifies that a trigger was executed #[derive( Debug, @@ -95,28 +74,6 @@ pub mod model { Failure(String), } - /// Filter for [`NotificationEvent`] - #[derive( - Debug, - Clone, - FromVariant, - PartialEq, - Eq, - PartialOrd, - Ord, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - #[ffi_type] - #[non_exhaustive] - pub enum NotificationEventFilter { - AcceptAll, - TriggerCompleted(TriggerCompletedEventFilter), - } - /// Filter [`TriggerCompletedEvent`] by /// 1. if `triger_id` is some filter based on trigger id /// 2. if `outcome_type` is some filter based on execution outcome (success/failure) @@ -128,7 +85,7 @@ pub mod model { Eq, PartialOrd, Ord, - Constructor, + Default, Getters, Decode, Encode, @@ -139,29 +96,41 @@ pub mod model { #[ffi_type] #[getset(get = "pub")] pub struct TriggerCompletedEventFilter { - trigger_id: Option, - outcome_type: Option, + pub(super) trigger_id: Option, + pub(super) outcome_type: Option, } } -#[cfg(feature = "transparent_api")] -impl super::Filter for NotificationEventFilter { - type Event = NotificationEvent; - - /// Check if `self` accepts the `event`. +impl TriggerCompletedEventFilter { + /// Creates a new [`TriggerCompletedEventFilter`] accepting all [`TriggerCompletedEvent`]s + #[must_use] #[inline] - fn matches(&self, event: &Self::Event) -> bool { - match (self, event) { - (Self::AcceptAll, _) => true, - (Self::TriggerCompleted(filter), NotificationEvent::TriggerCompleted(event)) => { - filter.matches(event) - } + pub const fn new() -> Self { + Self { + trigger_id: None, + outcome_type: None, } } + + /// Modifies a [`TriggerCompletedEventFilter`] to accept only [`TriggerCompletedEvent`]s originating from a specific trigger + #[must_use] + #[inline] + pub fn for_trigger(mut self, trigger_id: TriggerId) -> Self { + self.trigger_id = Some(trigger_id); + self + } + + /// Modifies a [`TriggerCompletedEventFilter`] to accept only [`TriggerCompletedEvent`]s with a specific outcome + #[must_use] + #[inline] + pub const fn for_outcome(mut self, outcome_type: TriggerCompletedOutcomeType) -> Self { + self.outcome_type = Some(outcome_type); + self + } } #[cfg(feature = "transparent_api")] -impl super::Filter for TriggerCompletedEventFilter { +impl super::EventFilter for TriggerCompletedEventFilter { type Event = TriggerCompletedEvent; /// Check if `self` accepts the `event`. @@ -191,8 +160,8 @@ impl super::Filter for TriggerCompletedEventFilter { /// Exports common structs and enums from this module. pub mod prelude { pub use super::{ - NotificationEvent, NotificationEventFilter, TriggerCompletedEvent, - TriggerCompletedEventFilter, TriggerCompletedOutcome, TriggerCompletedOutcomeType, + TriggerCompletedEvent, TriggerCompletedEventFilter, TriggerCompletedOutcome, + TriggerCompletedOutcomeType, }; } @@ -200,7 +169,7 @@ pub mod prelude { #[cfg(feature = "transparent_api")] mod tests { use super::*; - use crate::events::Filter; + use crate::events::EventFilter; #[test] fn trigger_completed_events_filter() { @@ -220,69 +189,65 @@ mod tests { let event_2_success = TriggerCompletedEvent::new(trigger_id_2.clone(), TriggerCompletedOutcome::Success); - let filter_accept_all = TriggerCompletedEventFilter::new(None, None); + let filter_accept_all = TriggerCompletedEventFilter::new(); assert!(filter_accept_all.matches(&event_1_failure)); assert!(filter_accept_all.matches(&event_1_success)); assert!(filter_accept_all.matches(&event_2_failure)); assert!(filter_accept_all.matches(&event_2_success)); let filter_accept_success = - TriggerCompletedEventFilter::new(None, Some(TriggerCompletedOutcomeType::Success)); + TriggerCompletedEventFilter::new().for_outcome(TriggerCompletedOutcomeType::Success); assert!(!filter_accept_success.matches(&event_1_failure)); assert!(filter_accept_success.matches(&event_1_success)); assert!(!filter_accept_success.matches(&event_2_failure)); assert!(filter_accept_success.matches(&event_2_success)); let filter_accept_failure = - TriggerCompletedEventFilter::new(None, Some(TriggerCompletedOutcomeType::Failure)); + TriggerCompletedEventFilter::new().for_outcome(TriggerCompletedOutcomeType::Failure); assert!(filter_accept_failure.matches(&event_1_failure)); assert!(!filter_accept_failure.matches(&event_1_success)); assert!(filter_accept_failure.matches(&event_2_failure)); assert!(!filter_accept_failure.matches(&event_2_success)); - let filter_accept_1 = TriggerCompletedEventFilter::new(Some(trigger_id_1.clone()), None); + let filter_accept_1 = TriggerCompletedEventFilter::new().for_trigger(trigger_id_1.clone()); assert!(filter_accept_1.matches(&event_1_failure)); assert!(filter_accept_1.matches(&event_1_success)); assert!(!filter_accept_1.matches(&event_2_failure)); assert!(!filter_accept_1.matches(&event_2_success)); - let filter_accept_1_failure = TriggerCompletedEventFilter::new( - Some(trigger_id_1.clone()), - Some(TriggerCompletedOutcomeType::Failure), - ); + let filter_accept_1_failure = TriggerCompletedEventFilter::new() + .for_trigger(trigger_id_1.clone()) + .for_outcome(TriggerCompletedOutcomeType::Failure); assert!(filter_accept_1_failure.matches(&event_1_failure)); assert!(!filter_accept_1_failure.matches(&event_1_success)); assert!(!filter_accept_1_failure.matches(&event_2_failure)); assert!(!filter_accept_1_failure.matches(&event_2_success)); - let filter_accept_1_success = TriggerCompletedEventFilter::new( - Some(trigger_id_1), - Some(TriggerCompletedOutcomeType::Success), - ); + let filter_accept_1_success = TriggerCompletedEventFilter::new() + .for_trigger(trigger_id_1) + .for_outcome(TriggerCompletedOutcomeType::Success); assert!(!filter_accept_1_success.matches(&event_1_failure)); assert!(filter_accept_1_success.matches(&event_1_success)); assert!(!filter_accept_1_success.matches(&event_2_failure)); assert!(!filter_accept_1_success.matches(&event_2_success)); - let filter_accept_2 = TriggerCompletedEventFilter::new(Some(trigger_id_2.clone()), None); + let filter_accept_2 = TriggerCompletedEventFilter::new().for_trigger(trigger_id_2.clone()); assert!(!filter_accept_2.matches(&event_1_failure)); assert!(!filter_accept_2.matches(&event_1_success)); assert!(filter_accept_2.matches(&event_2_failure)); assert!(filter_accept_2.matches(&event_2_success)); - let filter_accept_2_failure = TriggerCompletedEventFilter::new( - Some(trigger_id_2.clone()), - Some(TriggerCompletedOutcomeType::Failure), - ); + let filter_accept_2_failure = TriggerCompletedEventFilter::new() + .for_trigger(trigger_id_2.clone()) + .for_outcome(TriggerCompletedOutcomeType::Failure); assert!(!filter_accept_2_failure.matches(&event_1_failure)); assert!(!filter_accept_2_failure.matches(&event_1_success)); assert!(filter_accept_2_failure.matches(&event_2_failure)); assert!(!filter_accept_2_failure.matches(&event_2_success)); - let filter_accept_2_success = TriggerCompletedEventFilter::new( - Some(trigger_id_2), - Some(TriggerCompletedOutcomeType::Success), - ); + let filter_accept_2_success = TriggerCompletedEventFilter::new() + .for_trigger(trigger_id_2) + .for_outcome(TriggerCompletedOutcomeType::Success); assert!(!filter_accept_2_success.matches(&event_1_failure)); assert!(!filter_accept_2_success.matches(&event_1_success)); assert!(!filter_accept_2_success.matches(&event_2_failure)); diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index ef63f12a28f..2ac25db4fa6 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 db04257ad1e..06db2566b03 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -18,7 +18,7 @@ use alloc::{ use core::{fmt, fmt::Debug, ops::RangeInclusive, str::FromStr}; use derive_more::{Constructor, Display, From, FromStr}; -use events::TriggeringFilterBox; +use events::TriggeringEventFilterBox; use getset::Getters; use iroha_crypto::PublicKey; use iroha_data_model_derive::{model, EnumRef, IdEqOrdHash}; @@ -86,7 +86,7 @@ mod seal { Register, Register, Register, - Register >, + Register >, Unregister, Unregister, @@ -94,16 +94,16 @@ mod seal { Unregister, Unregister, Unregister, - Unregister >, + Unregister >, Mint, Mint, Mint, - Mint >, + Mint >, Burn, Burn, - Burn >, + Burn >, Transfer, Transfer, @@ -722,7 +722,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 +916,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 9f3e402f8fd..b3d3cc6dded 100644 --- a/data_model/src/query/mod.rs +++ b/data_model/src/query/mod.rs @@ -31,7 +31,7 @@ use self::{ use crate::{ account::{Account, AccountId}, block::{BlockHeader, SignedBlock}, - events::TriggeringFilterBox, + events::TriggeringEventFilterBox, metadata::MetadataValueBox, seal, transaction::{SignedTransaction, TransactionPayload, TransactionValue}, @@ -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), ); @@ -986,6 +986,7 @@ pub mod trigger { use super::{MetadataValueBox, Query, QueryType}; use crate::{ domain::prelude::*, + events::TriggeringEventFilterBox, prelude::InstructionBox, trigger::{Trigger, TriggerId}, Executable, Identifiable, Name, diff --git a/data_model/src/trigger.rs b/data_model/src/trigger.rs index 4270d2d34ac..df53f68707b 100644 --- a/data_model/src/trigger.rs +++ b/data_model/src/trigger.rs @@ -73,30 +73,30 @@ pub mod model { } #[ffi_impl_opaque] -impl Trigger { +impl Trigger { /// [`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> { + impl TryFrom> for Trigger<$filter_type> { type Error = &'static str; - fn try_from(boxed: Trigger) -> Result { - if let TriggeringFilterBox::$variant(concrete_filter) = boxed.action.filter { + 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, @@ -108,7 +108,7 @@ macro_rules! impl_try_from_box { action, }) } else { - Err(concat!("Expected `TriggeringFilterBox::", stringify!($variant),"`, but another variant found")) + Err(concat!("Expected `TriggeringEventFilterBox::", stringify!($variant),"`, but another variant found")) } } } @@ -222,7 +222,7 @@ pub mod action { } #[ffi_impl_opaque] - impl Action { + impl Action { /// The executable linked to this action pub fn executable(&self) -> &Executable { &self.executable @@ -238,7 +238,7 @@ pub mod action { &self.authority } /// Defines events which trigger the `Action` - pub fn filter(&self) -> &TriggeringFilterBox { + pub fn filter(&self) -> &TriggeringEventFilterBox { &self.filter } } @@ -330,20 +330,22 @@ mod tests { #[test] fn trigger_with_filterbox_can_be_unboxed() { - /// Should fail to compile if a new variant will be added to `TriggeringFilterBox` + /// Should fail to compile if a new variant will be added to `TriggeringEventFilterBox` #[allow(dead_code)] - fn compile_time_check(boxed: Trigger) { + fn compile_time_check(boxed: Trigger) { match &boxed.action.filter { - TriggeringFilterBox::Data(_) => Trigger::::try_from(boxed) + TriggeringEventFilterBox::Data(_) => Trigger::::try_from(boxed) .map(|_| ()) .unwrap(), - TriggeringFilterBox::Pipeline(_) => Trigger::::try_from(boxed) - .map(|_| ()) - .unwrap(), - TriggeringFilterBox::Time(_) => Trigger::::try_from(boxed) + TriggeringEventFilterBox::Pipeline(_) => { + Trigger::::try_from(boxed) + .map(|_| ()) + .unwrap() + } + TriggeringEventFilterBox::Time(_) => Trigger::::try_from(boxed) .map(|_| ()) .unwrap(), - TriggeringFilterBox::ExecuteTrigger(_) => { + TriggeringEventFilterBox::ExecuteTrigger(_) => { Trigger::::try_from(boxed) .map(|_| ()) .unwrap() diff --git a/data_model/src/visit.rs b/data_model/src/visit.rs index e45b1939dc4..56abc38f676 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 6e729ccd1d7..04e5ed6c954 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -83,66 +83,18 @@ ] }, "AccountEventFilter": { - "Enum": [ - { - "tag": "ByCreated", - "discriminant": 0 - }, - { - "tag": "ByDeleted", - "discriminant": 1 - }, - { - "tag": "ByAuthenticationAdded", - "discriminant": 2 - }, - { - "tag": "ByAuthenticationRemoved", - "discriminant": 3 - }, - { - "tag": "ByPermissionAdded", - "discriminant": 4 - }, - { - "tag": "ByPermissionRemoved", - "discriminant": 5 - }, - { - "tag": "ByRoleRevoked", - "discriminant": 6 - }, - { - "tag": "ByRoleGranted", - "discriminant": 7 - }, - { - "tag": "ByMetadataInserted", - "discriminant": 8 - }, - { - "tag": "ByMetadataRemoved", - "discriminant": 9 - }, - { - "tag": "ByAsset", - "discriminant": 10, - "type": "FilterOpt" - } - ] - }, - "AccountFilter": { "Struct": [ { - "name": "origin_filter", - "type": "FilterOpt>" + "name": "id_matcher", + "type": "Option" }, { - "name": "event_filter", - "type": "FilterOpt" + "name": "event_set", + "type": "AccountEventSet" } ] }, + "AccountEventSet": "u32", "AccountId": { "Struct": [ { @@ -193,7 +145,7 @@ } ] }, - "Action": { + "Action": { "Struct": [ { "name": "executable", @@ -209,7 +161,7 @@ }, { "name": "filter", - "type": "TriggeringFilterBox" + "type": "TriggeringEventFilterBox" }, { "name": "metadata", @@ -347,49 +299,18 @@ ] }, "AssetDefinitionEventFilter": { - "Enum": [ - { - "tag": "ByCreated", - "discriminant": 0 - }, - { - "tag": "ByMintabilityChanged", - "discriminant": 1 - }, - { - "tag": "ByOwnerChanged", - "discriminant": 2 - }, - { - "tag": "ByDeleted", - "discriminant": 3 - }, - { - "tag": "ByMetadataInserted", - "discriminant": 4 - }, - { - "tag": "ByMetadataRemoved", - "discriminant": 5 - }, - { - "tag": "ByTotalQuantityChanged", - "discriminant": 6 - } - ] - }, - "AssetDefinitionFilter": { "Struct": [ { - "name": "origin_filter", - "type": "FilterOpt>" + "name": "id_matcher", + "type": "Option" }, { - "name": "event_filter", - "type": "FilterOpt" + "name": "event_set", + "type": "AssetDefinitionEventSet" } ] }, + "AssetDefinitionEventSet": "u32", "AssetDefinitionId": { "Struct": [ { @@ -461,45 +382,18 @@ ] }, "AssetEventFilter": { - "Enum": [ - { - "tag": "ByCreated", - "discriminant": 0 - }, - { - "tag": "ByDeleted", - "discriminant": 1 - }, - { - "tag": "ByAdded", - "discriminant": 2 - }, - { - "tag": "ByRemoved", - "discriminant": 3 - }, - { - "tag": "ByMetadataInserted", - "discriminant": 4 - }, - { - "tag": "ByMetadataRemoved", - "discriminant": 5 - } - ] - }, - "AssetFilter": { "Struct": [ { - "name": "origin_filter", - "type": "FilterOpt>" + "name": "id_matcher", + "type": "Option" }, { - "name": "event_filter", - "type": "FilterOpt" + "name": "event_set", + "type": "AssetEventSet" } ] }, + "AssetEventSet": "u32", "AssetId": { "Struct": [ { @@ -668,7 +562,7 @@ } ] }, - "Burn>": { + "Burn>": { "Struct": [ { "name": "object", @@ -695,7 +589,7 @@ { "tag": "TriggerRepetitions", "discriminant": 2, - "type": "Burn>" + "type": "Burn>" } ] }, @@ -725,6 +619,19 @@ } ] }, + "ConfigurationEventFilter": { + "Struct": [ + { + "name": "id_matcher", + "type": "Option" + }, + { + "name": "event_set", + "type": "ConfigurationEventSet" + } + ] + }, + "ConfigurationEventSet": "u32", "Container": { "Enum": [ { @@ -744,30 +651,6 @@ } ] }, - "DataEntityFilter": { - "Enum": [ - { - "tag": "ByPeer", - "discriminant": 0, - "type": "FilterOpt" - }, - { - "tag": "ByDomain", - "discriminant": 1, - "type": "FilterOpt" - }, - { - "tag": "ByTrigger", - "discriminant": 2, - "type": "FilterOpt" - }, - { - "tag": "ByRole", - "discriminant": 3, - "type": "FilterOpt" - } - ] - }, "DataEvent": { "Enum": [ { @@ -807,589 +690,336 @@ } ] }, - "Domain": { - "Struct": [ - { - "name": "id", - "type": "DomainId" - }, - { - "name": "accounts", - "type": "SortedMap" - }, - { - "name": "asset_definitions", - "type": "SortedMap" - }, - { - "name": "asset_total_quantities", - "type": "SortedMap" - }, - { - "name": "logo", - "type": "Option" - }, - { - "name": "metadata", - "type": "Metadata" - }, - { - "name": "owned_by", - "type": "AccountId" - } - ] - }, - "DomainEvent": { + "DataEventFilter": { "Enum": [ { - "tag": "Account", - "discriminant": 0, - "type": "AccountEvent" + "tag": "Any", + "discriminant": 0 }, { - "tag": "AssetDefinition", + "tag": "Peer", "discriminant": 1, - "type": "AssetDefinitionEvent" + "type": "PeerEventFilter" }, { - "tag": "Created", + "tag": "Domain", "discriminant": 2, - "type": "Domain" + "type": "DomainEventFilter" }, { - "tag": "Deleted", + "tag": "Account", "discriminant": 3, - "type": "DomainId" + "type": "AccountEventFilter" }, { - "tag": "MetadataInserted", + "tag": "Asset", "discriminant": 4, - "type": "MetadataChanged" + "type": "AssetEventFilter" }, { - "tag": "MetadataRemoved", + "tag": "AssetDefinition", "discriminant": 5, - "type": "MetadataChanged" + "type": "AssetDefinitionEventFilter" }, { - "tag": "OwnerChanged", + "tag": "Trigger", "discriminant": 6, - "type": "DomainOwnerChanged" - } - ] - }, - "DomainEventFilter": { - "Enum": [ - { - "tag": "ByCreated", - "discriminant": 0 - }, - { - "tag": "ByDeleted", - "discriminant": 1 - }, - { - "tag": "ByMetadataInserted", - "discriminant": 2 - }, - { - "tag": "ByMetadataRemoved", - "discriminant": 3 + "type": "TriggerEventFilter" }, { - "tag": "ByOwnerChanged", - "discriminant": 4 + "tag": "Role", + "discriminant": 7, + "type": "RoleEventFilter" }, { - "tag": "ByAccount", - "discriminant": 5, - "type": "FilterOpt" + "tag": "PermissionTokenSchemaUpdate", + "discriminant": 8 }, { - "tag": "ByAssetDefinition", - "discriminant": 6, - "type": "FilterOpt" - } - ] - }, - "DomainFilter": { - "Struct": [ - { - "name": "origin_filter", - "type": "FilterOpt>" + "tag": "Configuration", + "discriminant": 9, + "type": "ConfigurationEventFilter" }, { - "name": "event_filter", - "type": "FilterOpt" - } - ] - }, - "DomainId": { - "Struct": [ - { - "name": "name", - "type": "Name" + "tag": "Executor", + "discriminant": 10, + "type": "ExecutorEventFilter" } ] }, - "DomainOwnerChanged": { + "Domain": { "Struct": [ { - "name": "domain_id", + "name": "id", "type": "DomainId" }, { - "name": "new_owner", - "type": "AccountId" - } - ] - }, - "Duration": { - "Tuple": [ - "u64", - "u32" - ] - }, - "Event": { - "Enum": [ - { - "tag": "Pipeline", - "discriminant": 0, - "type": "PipelineEvent" - }, - { - "tag": "Data", - "discriminant": 1, - "type": "DataEvent" - }, - { - "tag": "Time", - "discriminant": 2, - "type": "TimeEvent" + "name": "accounts", + "type": "SortedMap" }, { - "tag": "ExecuteTrigger", - "discriminant": 3, - "type": "ExecuteTriggerEvent" + "name": "asset_definitions", + "type": "SortedMap" }, { - "tag": "Notification", - "discriminant": 4, - "type": "NotificationEvent" - } - ] - }, - "EventMessage": "Event", - "EventSubscriptionRequest": "FilterBox", - "Executable": { - "Enum": [ - { - "tag": "Instructions", - "discriminant": 0, - "type": "Vec" + "name": "asset_total_quantities", + "type": "SortedMap" }, { - "tag": "Wasm", - "discriminant": 1, - "type": "WasmSmartContract" - } - ] - }, - "ExecuteTrigger": { - "Struct": [ - { - "name": "trigger_id", - "type": "TriggerId" - } - ] - }, - "ExecuteTriggerEvent": { - "Struct": [ - { - "name": "trigger_id", - "type": "TriggerId" + "name": "logo", + "type": "Option" }, { - "name": "authority", - "type": "AccountId" - } - ] - }, - "ExecuteTriggerEventFilter": { - "Struct": [ - { - "name": "trigger_id", - "type": "TriggerId" + "name": "metadata", + "type": "Metadata" }, { - "name": "authority", + "name": "owned_by", "type": "AccountId" } ] }, - "ExecutionTime": { - "Enum": [ - { - "tag": "PreCommit", - "discriminant": 0 - }, - { - "tag": "Schedule", - "discriminant": 1, - "type": "Schedule" - } - ] - }, - "Executor": { - "Struct": [ - { - "name": "wasm", - "type": "WasmSmartContract" - } - ] - }, - "ExecutorEvent": { - "Enum": [ - { - "tag": "Upgraded", - "discriminant": 0 - } - ] - }, - "ExecutorMode": { - "Enum": [ - { - "tag": "Path", - "discriminant": 0, - "type": "String" - }, - { - "tag": "Inline", - "discriminant": 1, - "type": "Executor" - } - ] - }, - "Fail": { - "Struct": [ - { - "name": "message", - "type": "String" - } - ] - }, - "FilterBox": { + "DomainEvent": { "Enum": [ { - "tag": "Pipeline", + "tag": "Account", "discriminant": 0, - "type": "PipelineEventFilter" + "type": "AccountEvent" }, { - "tag": "Data", + "tag": "AssetDefinition", "discriminant": 1, - "type": "FilterOpt" + "type": "AssetDefinitionEvent" }, { - "tag": "Time", + "tag": "Created", "discriminant": 2, - "type": "TimeEventFilter" + "type": "Domain" }, { - "tag": "ExecuteTrigger", + "tag": "Deleted", "discriminant": 3, - "type": "ExecuteTriggerEventFilter" - }, - { - "tag": "Notification", - "discriminant": 4, - "type": "NotificationEventFilter" - } - ] - }, - "FilterOpt": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 + "type": "DomainId" }, { - "tag": "BySome", - "discriminant": 1, - "type": "AccountEventFilter" - } - ] - }, - "FilterOpt": { - "Enum": [ + "tag": "MetadataInserted", + "discriminant": 4, + "type": "MetadataChanged" + }, { - "tag": "AcceptAll", - "discriminant": 0 + "tag": "MetadataRemoved", + "discriminant": 5, + "type": "MetadataChanged" }, { - "tag": "BySome", - "discriminant": 1, - "type": "AccountFilter" + "tag": "OwnerChanged", + "discriminant": 6, + "type": "DomainOwnerChanged" } ] }, - "FilterOpt": { - "Enum": [ + "DomainEventFilter": { + "Struct": [ { - "tag": "AcceptAll", - "discriminant": 0 + "name": "id_matcher", + "type": "Option" }, { - "tag": "BySome", - "discriminant": 1, - "type": "AssetDefinitionEventFilter" + "name": "event_set", + "type": "DomainEventSet" } ] }, - "FilterOpt": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 - }, + "DomainEventSet": "u32", + "DomainId": { + "Struct": [ { - "tag": "BySome", - "discriminant": 1, - "type": "AssetDefinitionFilter" + "name": "name", + "type": "Name" } ] }, - "FilterOpt": { - "Enum": [ + "DomainOwnerChanged": { + "Struct": [ { - "tag": "AcceptAll", - "discriminant": 0 + "name": "domain_id", + "type": "DomainId" }, { - "tag": "BySome", - "discriminant": 1, - "type": "AssetEventFilter" + "name": "new_owner", + "type": "AccountId" } ] }, - "FilterOpt": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 - }, - { - "tag": "BySome", - "discriminant": 1, - "type": "AssetFilter" - } + "Duration": { + "Tuple": [ + "u64", + "u32" ] }, - "FilterOpt": { + "Event": { "Enum": [ { - "tag": "AcceptAll", - "discriminant": 0 + "tag": "Pipeline", + "discriminant": 0, + "type": "PipelineEvent" }, { - "tag": "BySome", + "tag": "Data", "discriminant": 1, - "type": "DataEntityFilter" - } - ] - }, - "FilterOpt": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 + "type": "DataEvent" }, { - "tag": "BySome", - "discriminant": 1, - "type": "DomainEventFilter" - } - ] - }, - "FilterOpt": { - "Enum": [ + "tag": "Time", + "discriminant": 2, + "type": "TimeEvent" + }, { - "tag": "AcceptAll", - "discriminant": 0 + "tag": "ExecuteTrigger", + "discriminant": 3, + "type": "ExecuteTriggerEvent" }, { - "tag": "BySome", - "discriminant": 1, - "type": "DomainFilter" + "tag": "TriggerCompleted", + "discriminant": 4, + "type": "TriggerCompletedEvent" } ] }, - "FilterOpt>": { + "EventFilterBox": { "Enum": [ { - "tag": "AcceptAll", - "discriminant": 0 + "tag": "Pipeline", + "discriminant": 0, + "type": "PipelineEventFilter" }, { - "tag": "BySome", + "tag": "Data", "discriminant": 1, - "type": "OriginFilter" - } - ] - }, - "FilterOpt>": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 + "type": "DataEventFilter" }, { - "tag": "BySome", - "discriminant": 1, - "type": "OriginFilter" - } - ] - }, - "FilterOpt>": { - "Enum": [ + "tag": "Time", + "discriminant": 2, + "type": "TimeEventFilter" + }, { - "tag": "AcceptAll", - "discriminant": 0 + "tag": "ExecuteTrigger", + "discriminant": 3, + "type": "ExecuteTriggerEventFilter" }, { - "tag": "BySome", - "discriminant": 1, - "type": "OriginFilter" + "tag": "TriggerCompleted", + "discriminant": 4, + "type": "TriggerCompletedEventFilter" } ] }, - "FilterOpt>": { + "EventMessage": "Event", + "EventSubscriptionRequest": "EventFilterBox", + "Executable": { "Enum": [ { - "tag": "AcceptAll", - "discriminant": 0 + "tag": "Instructions", + "discriminant": 0, + "type": "Vec" }, { - "tag": "BySome", + "tag": "Wasm", "discriminant": 1, - "type": "OriginFilter" + "type": "WasmSmartContract" } ] }, - "FilterOpt>": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 - }, + "ExecuteTrigger": { + "Struct": [ { - "tag": "BySome", - "discriminant": 1, - "type": "OriginFilter" + "name": "trigger_id", + "type": "TriggerId" } ] }, - "FilterOpt>": { - "Enum": [ + "ExecuteTriggerEvent": { + "Struct": [ { - "tag": "AcceptAll", - "discriminant": 0 + "name": "trigger_id", + "type": "TriggerId" }, { - "tag": "BySome", - "discriminant": 1, - "type": "OriginFilter" + "name": "authority", + "type": "AccountId" } ] }, - "FilterOpt>": { - "Enum": [ + "ExecuteTriggerEventFilter": { + "Struct": [ { - "tag": "AcceptAll", - "discriminant": 0 + "name": "trigger_id", + "type": "Option" }, { - "tag": "BySome", - "discriminant": 1, - "type": "OriginFilter" + "name": "authority", + "type": "Option" } ] }, - "FilterOpt": { + "ExecutionTime": { "Enum": [ { - "tag": "AcceptAll", + "tag": "PreCommit", "discriminant": 0 }, { - "tag": "BySome", + "tag": "Schedule", "discriminant": 1, - "type": "PeerEventFilter" + "type": "Schedule" } ] }, - "FilterOpt": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 - }, + "Executor": { + "Struct": [ { - "tag": "BySome", - "discriminant": 1, - "type": "PeerFilter" + "name": "wasm", + "type": "WasmSmartContract" } ] }, - "FilterOpt": { + "ExecutorEvent": { "Enum": [ { - "tag": "AcceptAll", + "tag": "Upgraded", "discriminant": 0 - }, - { - "tag": "BySome", - "discriminant": 1, - "type": "RoleEventFilter" } ] }, - "FilterOpt": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 - }, + "ExecutorEventFilter": { + "Struct": [ { - "tag": "BySome", - "discriminant": 1, - "type": "RoleFilter" + "name": "event_set", + "type": "ExecutorEventSet" } ] }, - "FilterOpt": { + "ExecutorEventSet": "u32", + "ExecutorMode": { "Enum": [ { - "tag": "AcceptAll", - "discriminant": 0 + "tag": "Path", + "discriminant": 0, + "type": "String" }, { - "tag": "BySome", + "tag": "Inline", "discriminant": 1, - "type": "TriggerEventFilter" + "type": "Executor" } ] }, - "FilterOpt": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 - }, + "Fail": { + "Struct": [ { - "tag": "BySome", - "discriminant": 1, - "type": "TriggerFilter" + "name": "message", + "type": "String" } ] }, @@ -1909,7 +1539,7 @@ { "tag": "Trigger", "discriminant": 9, - "type": "Trigger" + "type": "Trigger" }, { "tag": "Role", @@ -2437,7 +2067,7 @@ } ] }, - "Mint>": { + "Mint>": { "Struct": [ { "name": "object", @@ -2464,7 +2094,7 @@ { "tag": "TriggerRepetitions", "discriminant": 2, - "type": "Mint>" + "type": "Mint>" } ] }, @@ -2584,28 +2214,6 @@ "NonTrivial>": "Vec>", "NonZero": "u32", "NonZero": "u64", - "NotificationEvent": { - "Enum": [ - { - "tag": "TriggerCompleted", - "discriminant": 0, - "type": "TriggerCompletedEvent" - } - ] - }, - "NotificationEventFilter": { - "Enum": [ - { - "tag": "AcceptAll", - "discriminant": 0 - }, - { - "tag": "TriggerCompleted", - "discriminant": 1, - "type": "TriggerCompletedEventFilter" - } - ] - }, "Numeric": { "Struct": [ { @@ -2626,6 +2234,15 @@ } ] }, + "Option": { + "Option": "AccountId" + }, + "Option": { + "Option": "AssetDefinitionId" + }, + "Option": { + "Option": "AssetId" + }, "Option": { "Option": "DomainId" }, @@ -2650,12 +2267,21 @@ "Option>": { "Option": "NonZero" }, + "Option": { + "Option": "ParameterId" + }, + "Option": { + "Option": "PeerId" + }, "Option": { "Option": "PipelineEntityKind" }, "Option": { "Option": "PipelineStatusKind" }, + "Option": { + "Option": "RoleId" + }, "Option": { "Option": "String" }, @@ -2674,13 +2300,6 @@ "Option": { "Option": "u32" }, - "OriginFilter": "AccountId", - "OriginFilter": "AssetDefinitionId", - "OriginFilter": "AssetId", - "OriginFilter": "DomainId", - "OriginFilter": "PeerId", - "OriginFilter": "RoleId", - "OriginFilter": "TriggerId", "Parameter": { "Struct": [ { @@ -2748,29 +2367,18 @@ ] }, "PeerEventFilter": { - "Enum": [ - { - "tag": "ByAdded", - "discriminant": 0 - }, - { - "tag": "ByRemoved", - "discriminant": 1 - } - ] - }, - "PeerFilter": { "Struct": [ { - "name": "origin_filter", - "type": "FilterOpt>" + "name": "id_matcher", + "type": "Option" }, { - "name": "event_filter", - "type": "FilterOpt" + "name": "event_set", + "type": "PeerEventSet" } ] }, + "PeerEventSet": "u32", "PeerId": { "Struct": [ { @@ -3316,11 +2924,11 @@ } ] }, - "Register>": { + "Register>": { "Struct": [ { "name": "object", - "type": "Trigger" + "type": "Trigger" } ] }, @@ -3359,7 +2967,7 @@ { "tag": "Trigger", "discriminant": 6, - "type": "Register>" + "type": "Register>" } ] }, @@ -3552,37 +3160,18 @@ ] }, "RoleEventFilter": { - "Enum": [ - { - "tag": "ByCreated", - "discriminant": 0 - }, - { - "tag": "ByDeleted", - "discriminant": 1 - }, - { - "tag": "ByPermissionRemoved", - "discriminant": 2 - }, - { - "tag": "ByPermissionAdded", - "discriminant": 3 - } - ] - }, - "RoleFilter": { "Struct": [ { - "name": "origin_filter", - "type": "FilterOpt>" + "name": "id_matcher", + "type": "Option" }, { - "name": "event_filter", - "type": "FilterOpt" + "name": "event_set", + "type": "RoleEventSet" } ] }, + "RoleEventSet": "u32", "RoleId": { "Struct": [ { @@ -4203,7 +3792,7 @@ } ] }, - "Trigger": { + "Trigger": { "Struct": [ { "name": "id", @@ -4211,7 +3800,7 @@ }, { "name": "action", - "type": "Action" + "type": "Action" } ] }, @@ -4289,37 +3878,18 @@ ] }, "TriggerEventFilter": { - "Enum": [ - { - "tag": "ByCreated", - "discriminant": 0 - }, - { - "tag": "ByDeleted", - "discriminant": 1 - }, - { - "tag": "ByExtended", - "discriminant": 2 - }, - { - "tag": "ByShortened", - "discriminant": 3 - } - ] - }, - "TriggerFilter": { "Struct": [ { - "name": "origin_filter", - "type": "FilterOpt>" + "name": "id_matcher", + "type": "Option" }, { - "name": "event_filter", - "type": "FilterOpt" + "name": "event_set", + "type": "TriggerEventSet" } ] }, + "TriggerEventSet": "u32", "TriggerId": { "Struct": [ { @@ -4344,7 +3914,7 @@ } ] }, - "TriggeringFilterBox": { + "TriggeringEventFilterBox": { "Enum": [ { "tag": "Pipeline", @@ -4354,7 +3924,7 @@ { "tag": "Data", "discriminant": 1, - "type": "FilterOpt" + "type": "DataEventFilter" }, { "tag": "Time", @@ -4435,7 +4005,7 @@ } ] }, - "Unregister>": { + "Unregister>": { "Struct": [ { "name": "object_id", @@ -4478,7 +4048,7 @@ { "tag": "Trigger", "discriminant": 6, - "type": "Unregister>" + "type": "Unregister>" } ] }, diff --git a/schema/gen/src/lib.rs b/schema/gen/src/lib.rs index 9107283f683..259120b048d 100644 --- a/schema/gen/src/lib.rs +++ b/schema/gen/src/lib.rs @@ -65,25 +65,25 @@ types!( Account, AccountEvent, AccountEventFilter, - AccountFilter, + AccountEventSet, AccountId, AccountMintBox, AccountPermissionChanged, AccountRoleChanged, - Action, + Action, Algorithm, Asset, AssetChanged, AssetDefinition, AssetDefinitionEvent, AssetDefinitionEventFilter, - AssetDefinitionFilter, + AssetDefinitionEventSet, AssetDefinitionId, AssetDefinitionOwnerChanged, AssetDefinitionTotalQuantityChanged, AssetEvent, AssetEventFilter, - AssetFilter, + AssetEventSet, AssetId, AssetTransferBox, AssetValue, @@ -107,23 +107,24 @@ types!( BlockSubscriptionRequest, Box>, Box, - Burn>, + Burn>, Burn, Burn, BurnBox, ChainId, ConfigurationEvent, + ConfigurationEventFilter, + ConfigurationEventSet, ConstString, ConstVec, ConstVec, Container, - DataEntityFilter, DataEvent, DataEventFilter, Domain, DomainEvent, DomainEventFilter, - DomainFilter, + DomainEventSet, DomainId, DomainOwnerChanged, Duration, @@ -137,29 +138,10 @@ types!( ExecutionTime, Executor, ExecutorEvent, + ExecutorEventFilter, + ExecutorEventSet, Fail, - FilterBox, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt>, - FilterOpt>, - FilterOpt>, - FilterOpt>, - FilterOpt>, - FilterOpt>, - FilterOpt>, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt, - FilterOpt, + EventFilterBox, FindAccountById, FindAccountKeyValueByIdAndKey, FindAccountsByDomainId, @@ -234,7 +216,7 @@ types!( MetadataError, MetadataLimits, MetadataValueBox, - Mint>, + Mint>, Mint, Mint, Mint, @@ -251,11 +233,12 @@ types!( NonTrivial, NonZeroU32, NonZeroU64, - NotificationEvent, - NotificationEventFilter, Numeric, NumericSpec, Option, + Option, + Option, + Option, Option, Option, Option, @@ -264,27 +247,23 @@ types!( Option, Option, Option, + Option, + Option, Option, Option, + Option, Option, Option, Option, Option, Option, - OriginFilter, - OriginFilter, - OriginFilter, - OriginFilter, - OriginFilter, - OriginFilter, - OriginFilter, Parameter, ParameterId, ParameterValueBox, Peer, PeerEvent, PeerEventFilter, - PeerFilter, + PeerEventSet, PeerId, RolePermissionChanged, PermissionToken, @@ -309,7 +288,7 @@ types!( Register, Register, Register, - Register>, + Register>, RegisterBox, RemoveKeyValue, RemoveKeyValue, @@ -325,7 +304,7 @@ types!( Role, RoleEvent, RoleEventFilter, - RoleFilter, + RoleEventSet, RoleId, SemiInterval, SemiInterval, @@ -374,17 +353,17 @@ types!( Transfer, Transfer, TransferBox, - Trigger, + Trigger, TriggerCompletedEvent, TriggerCompletedEventFilter, TriggerCompletedOutcome, TriggerCompletedOutcomeType, TriggerEvent, TriggerEventFilter, - TriggerFilter, + TriggerEventSet, TriggerId, TriggerNumberOfExecutionsChanged, - TriggeringFilterBox, + TriggeringEventFilterBox, TypeError, UniqueVec, Unregister, @@ -393,7 +372,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 4e46084bde5..af341e733d3 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 41a4ab12b46..e7e9a2645e9 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 00391f5e58e..cab5c555b19 100644 --- a/tools/parity_scale_decoder/build.rs +++ b/tools/parity_scale_decoder/build.rs @@ -12,7 +12,7 @@ fn main() { sample_into_binary_file::("domain").expect("Failed to encode into domain.bin."); - sample_into_binary_file::>("trigger") + sample_into_binary_file::>("trigger") .expect("Failed to encode into trigger.bin."); } diff --git a/tools/parity_scale_decoder/samples/trigger.bin b/tools/parity_scale_decoder/samples/trigger.bin index f6c3d19d8ce..bf1bf3f4b9e 100644 Binary files a/tools/parity_scale_decoder/samples/trigger.bin and b/tools/parity_scale_decoder/samples/trigger.bin differ diff --git a/tools/parity_scale_decoder/samples/trigger.json b/tools/parity_scale_decoder/samples/trigger.json index f8a8e9e9c2e..bcc84114c88 100644 --- a/tools/parity_scale_decoder/samples/trigger.json +++ b/tools/parity_scale_decoder/samples/trigger.json @@ -17,11 +17,9 @@ "authority": "alice@wonderland", "filter": { "Data": { - "ByDomain": { - "origin_filter": "AcceptAll", - "event_filter": { - "ByAccount": "AcceptAll" - } + "Domain": { + "id_matcher": null, + "event_set": ["AnyAccount"] } } }, diff --git a/tools/parity_scale_decoder/src/main.rs b/tools/parity_scale_decoder/src/main.rs index b84c373dfd6..86f3139d391 100644 --- a/tools/parity_scale_decoder/src/main.rs +++ b/tools/parity_scale_decoder/src/main.rs @@ -271,20 +271,19 @@ mod tests { ); let rose_id = AssetId::new(rose_definition_id, account_id.clone()); let trigger_id = "mint_rose".parse().expect("Valid"); - let action = Action::::new( + let action = Action::new( vec![Mint::asset_numeric(1u32, rose_id)], Repeats::Indefinitely, account_id, - // FIXME: rewrite the filters using the builder DSL https://github.com/hyperledger/iroha/issues/3068 - FilterBox::Data(BySome(DataEntityFilter::ByDomain(BySome( - DomainFilter::new(AcceptAll, BySome(DomainEventFilter::ByAccount(AcceptAll))), - )))), + EventFilterBox::Data(DataEventFilter::Domain( + DomainEventFilter::new().for_events(DomainEventSet::AnyAccount), + )), ); let trigger = Trigger::new(trigger_id, action); decode_sample( "trigger.bin", - String::from("Trigger"), + String::from("Trigger"), &trigger, ); } diff --git a/torii/src/event.rs b/torii/src/event.rs index 9a9dcc0ff1d..873f81d91ec 100644 --- a/torii/src/event.rs +++ b/torii/src/event.rs @@ -19,7 +19,9 @@ pub enum Error { Stream(Box), /// Error from converting received message to filter #[error("Can't retrieve subscription filter: {0}")] - CantRetrieveSubscriptionFilter(#[from] ErrorTryFromEnum), + CantRetrieveSubscriptionFilter( + #[from] ErrorTryFromEnum, + ), /// Error from provided websocket #[error("WebSocket error: {0}")] WebSocket(#[from] warp::Error), @@ -42,7 +44,7 @@ pub type Result = core::result::Result; #[derive(Debug)] pub struct Consumer { stream: WebSocket, - filter: FilterBox, + filter: EventFilterBox, } impl Consumer {