diff --git a/core/src/smartcontracts/isi/asset.rs b/core/src/smartcontracts/isi/asset.rs index 845370ed0cc..63706cbef52 100644 --- a/core/src/smartcontracts/isi/asset.rs +++ b/core/src/smartcontracts/isi/asset.rs @@ -190,7 +190,7 @@ pub mod isi { Ok(AssetEvent::new( asset_id.clone(), - DataStatus::Updated(Updated::Asset(AssetUpdated::Burned)), + DataStatus::Updated(Updated::Asset(AssetUpdated::Sent)), )) }) } diff --git a/core/src/smartcontracts/isi/triggers.rs b/core/src/smartcontracts/isi/triggers.rs index fcf36dd616d..fdaaed7168a 100644 --- a/core/src/smartcontracts/isi/triggers.rs +++ b/core/src/smartcontracts/isi/triggers.rs @@ -24,10 +24,12 @@ pub mod isi { self, _authority: ::Id, wsv: &WorldStateView, - ) -> Result, Self::Error> { + ) -> Result<(), Self::Error> { let new_trigger = self.object.clone(); - wsv.triggers.add(new_trigger)?; - Ok(vec![DataEvent::new(self.object.id, DataStatus::Created)]) + wsv.modify_triggers(|triggers| { + triggers.add(new_trigger)?; + Ok(TriggerEvent::Created(self.object.id)) + }) } } @@ -39,10 +41,12 @@ pub mod isi { self, _authority: ::Id, wsv: &WorldStateView, - ) -> Result, Self::Error> { + ) -> Result<(), Self::Error> { let trigger = self.object_id.clone(); - wsv.triggers.remove(trigger)?; - Ok(vec![DataEvent::new(self.object_id, DataStatus::Deleted)]) + wsv.modify_triggers(|triggers| { + triggers.remove(trigger)?; + Ok(TriggerEvent::Deleted(self.object_id)) + }) } } @@ -54,15 +58,14 @@ pub mod isi { self, _authority: ::Id, wsv: &WorldStateView, - ) -> Result, Self::Error> { + ) -> Result<(), Self::Error> { let trigger = self.destination_id.clone(); - wsv.triggers.mod_repeats(trigger, |n| { - n.checked_add(self.object).ok_or(MathError::Overflow) - })?; - Ok(vec![DataEvent::new( - self.destination_id, - Updated::Trigger(TriggerUpdated::Extended), - )]) + wsv.modify_triggers(|triggers| { + triggers.mod_repeats(trigger, |n| { + n.checked_add(self.object).ok_or(MathError::Overflow) + })?; + Ok(TriggerEvent::Extended(self.destination_id)) + }) } } @@ -74,15 +77,14 @@ pub mod isi { self, _authority: ::Id, wsv: &WorldStateView, - ) -> Result, Self::Error> { + ) -> Result<(), Self::Error> { let trigger = self.destination_id.clone(); - wsv.triggers.mod_repeats(trigger, |n| { - n.checked_sub(self.object).ok_or(MathError::Overflow) - })?; - Ok(vec![DataEvent::new( - self.destination_id, - Updated::Trigger(TriggerUpdated::Shortened), - )]) + wsv.modify_triggers(|triggers| { + triggers.mod_repeats(trigger, |n| { + n.checked_sub(self.object).ok_or(MathError::Overflow) + })?; + Ok(TriggerEvent::Shortened(self.destination_id)) + }) } } } diff --git a/core/src/wsv.rs b/core/src/wsv.rs index 91efbb9d6a7..df7bf36f809 100644 --- a/core/src/wsv.rs +++ b/core/src/wsv.rs @@ -8,7 +8,10 @@ use std::{ }; use config::Configuration; -use dashmap::{mapref::one::Ref as DashMapRef, DashSet}; +use dashmap::{ + mapref::one::{Ref as DashMapRef, RefMut as DashMapRefMut}, + DashSet, +}; use eyre::Result; use iroha_crypto::HashOf; use iroha_data_model::{prelude::*, trigger::Action}; @@ -22,6 +25,7 @@ use crate::{ event::EventsSender, prelude::*, smartcontracts::{isi::Error, wasm, Execute, FindError}, + triggers::TriggerSet, DomainsMap, PeersIds, }; @@ -153,30 +157,11 @@ impl WorldStateView { tokens } - /// Add new `Asset` entity. - /// - /// # Errors - /// Fails if there is no account for asset - pub fn add_asset(&self, asset: Asset) -> Result<(), Error> { - let id = asset.id.account_id.clone(); - self.modify_account(&id, move |account| { - account.assets.insert(asset.id.clone(), asset); - Ok(()) - }) - } - - /// Add new `Domain` entity. - pub fn add_domain(&mut self, domain: Domain) { - self.world.domains.insert(domain.id.clone(), domain); - } - fn process_executable(&self, executable: &Executable, authority: &AccountId) -> Result<()> { match executable { Executable::Instructions(instructions) => { instructions.iter().cloned().try_for_each(|instruction| { - let events = instruction.execute(authority.clone(), self)?; - - self.produce_events(events); + instruction.execute(authority.clone(), self)?; Ok::<_, eyre::Report>(()) })?; } @@ -214,20 +199,7 @@ impl WorldStateView { // TODO: Should this block panic instead? for tx in &block.as_v1().transactions { - let account_id = &tx.payload().account_id; - - match &tx.as_v1().payload.instructions { - Executable::Instructions(instructions) => { - instructions.iter().cloned().try_for_each(|instruction| { - instruction.execute(account_id.clone(), self) - })?; - } - Executable::Wasm(bytes) => { - let mut wasm_runtime = wasm::Runtime::new()?; - wasm_runtime.execute(self, account_id, bytes)?; - } - } - + self.process_executable(&tx.as_v1().payload.instructions, &tx.payload().account_id)?; self.transactions.insert(tx.hash()); task::yield_now().await; } @@ -286,7 +258,10 @@ impl WorldStateView { .assets .entry(id.clone()) .or_insert_with(|| Asset::new(id.clone(), default_asset_value.into())); - Ok(()) + Ok(AccountEvent::Asset(AssetEvent::new( + id.clone(), + DataStatus::Created, + ))) }) .map_err(|err| { iroha_logger::warn!(?err); @@ -390,16 +365,22 @@ impl WorldStateView { Ok(()) } - /// Returns reference for domains map - pub fn domains(&self) -> &DomainsMap { - &self.world.domains - } - /// Returns reference for trusted peer ids pub fn trusted_peers_ids(&self) -> &PeersIds { &self.world.trusted_peers_ids } + /// Returns iterator over blockchain blocks starting with the block of the given `height` + pub fn blocks_from_height( + &self, + height: usize, + ) -> impl Iterator + '_ { + self.blocks + .iter() + .skip(height.saturating_sub(1)) + .map(|block_entry| block_entry.value().clone()) + } + /// Get `Domain` without an ability to modify it. /// /// # Errors @@ -416,6 +397,27 @@ impl WorldStateView { Ok(domain) } + /// Get `Domain` with an ability to modify it. + /// + /// # Errors + /// Fails if there is no domain + pub fn domain_mut( + &self, + id: &::Id, + ) -> Result, FindError> { + let domain = self + .world + .domains + .get_mut(id) + .ok_or_else(|| FindError::Domain(id.clone()))?; + Ok(domain) + } + + /// Returns reference for domains map + pub fn domains(&self) -> &DomainsMap { + &self.world.domains + } + /// Get `Domain` and pass it to closure. /// /// # Errors @@ -447,6 +449,63 @@ impl WorldStateView { }) } + /// Construct [`WorldStateView`] with specific [`Configuration`]. + pub fn from_configuration(config: Configuration, world: W) -> Self { + let (new_block_notifier, _) = tokio::sync::watch::channel(()); + + Self { + world, + config, + transactions: DashSet::new(), + blocks: Arc::new(Chain::new()), + metrics: Arc::new(Metrics::default()), + new_block_notifier: Arc::new(new_block_notifier), + triggers: Arc::new(TriggerSet::default()), + events_sender: None, + } + } + + /// Returns [`Some`] milliseconds since the genesis block was + /// committed, or [`None`] if it wasn't. + pub fn genesis_timestamp(&self) -> Option { + self.blocks + .iter() + .next() + .map(|val| val.as_v1().header.timestamp) + } + + /// Check if this [`VersionedTransaction`] is already committed or rejected. + pub fn has_transaction(&self, hash: &HashOf) -> bool { + self.transactions.contains(hash) + } + + /// Height of blockchain + #[inline] + pub fn height(&self) -> u64 { + self.metrics.block_height.get() + } + + /// Initializes WSV with the blocks from block storage. + #[iroha_futures::telemetry_future] + pub async fn init(&self, blocks: Vec) { + for block in blocks { + #[allow(clippy::panic)] + if let Err(error) = self.apply(block).await { + error!(%error, "Initialization of WSV failed"); + panic!("WSV initialization failed"); + } + } + } + + /// Hash of latest block + pub fn latest_block_hash(&self) -> HashOf { + self.blocks + .latest_block() + .map_or(HashOf::from_hash(Hash([0_u8; 32])), |block| { + block.value().hash() + }) + } + /// Get `Account` and pass it to closure. /// /// # Errors @@ -464,19 +523,6 @@ impl WorldStateView { Ok(f(account)) } - /// Get `Domain` and pass it to closure. - /// - /// # Errors - /// Fails if there is no domain - pub fn map_domain( - &self, - id: &::Id, - f: impl FnOnce(&Domain) -> T, - ) -> Result { - let domain = self.domain(id)?; - Ok(f(domain.value())) - } - /// Get `Account` and pass it to closure to modify it /// /// # Errors @@ -495,44 +541,10 @@ impl WorldStateView { }) } - /// Get `Account`'s `Asset`s and pass it to closure - /// - /// # Errors - /// Fails if account finding fails - pub fn account_assets(&self, id: &AccountId) -> Result, FindError> { - self.map_account(id, |account| account.assets.values().cloned().collect()) - } - - /// Get all `PeerId`s without an ability to modify them. - pub fn peers(&self) -> Vec { - let mut vec = self - .world - .trusted_peers_ids - .iter() - .map(|peer| Peer::new((&*peer).clone())) - .collect::>(); - vec.sort(); - vec - } - /// Get `Asset` by its id /// /// # Errors /// Fails if there are no such asset or account - pub fn asset(&self, id: &::Id) -> Result { - self.map_account(&id.account_id, |account| -> Result { - account - .assets - .get(id) - .ok_or_else(|| FindError::Asset(id.clone())) - .map(Clone::clone) - })? - } - - /// Get `Asset` by its id and pass it to closure to modify it - /// - /// # Errors - /// Fails if there are no such asset or account pub fn modify_asset( &self, id: &::Id, @@ -551,51 +563,7 @@ impl WorldStateView { }) } - /// Tries to get asset or inserts new with `default_asset_value`. - /// - /// # Errors - /// Fails if there is no account with such name. - pub fn asset_or_insert( - &self, - id: &::Id, - default_asset_value: impl Into, - ) -> Result { - // This function is strictly infallible. - self.asset(id).or_else(|_| { - self.modify_account(&id.account_id, |account| { - account.assets.insert( - id.clone(), - Asset::new(id.clone(), default_asset_value.into()), - ); - Ok(AccountEvent::Asset(AssetEvent::new( - id.clone(), - DataStatus::Created, - ))) - }) - .map_err(|err| { - iroha_logger::warn!(?err); - err - })?; - self.asset(id).map_err(Into::into) - }) - } - - /// Get `AssetDefinitionEntry` without an ability to modify it. - /// - /// # Errors - /// Fails if asset definition entry does not exist - pub fn asset_definition_entry( - &self, - id: &::Id, - ) -> Result { - self.domain(&id.domain_id)? - .asset_definitions - .get(id) - .ok_or_else(|| FindError::AssetDefinition(id.clone())) - .map(Clone::clone) - } - - /// Get `AssetDefinitionEntry` and pass it to closure to modify it + /// Get `AssetDefinitionEntry` with an ability to modify it. /// /// # Errors /// Fails if asset definition entry does not exist @@ -613,19 +581,6 @@ impl WorldStateView { }) } - /// Get `Domain` and pass it to closure to modify it - /// - /// # Errors - /// Fails if there is no domain - pub fn modify_domain( - &self, - id: &::Id, - f: impl FnOnce(&mut Domain) -> Result<(), Error>, - ) -> Result<(), Error> { - let mut domain = self.domain_mut(id)?; - f(domain.value_mut()) - } - /// Construct [`WorldStateView`] with given [`World`]. pub fn new(world: W) -> Self { Self::from_configuration(Configuration::default(), world) @@ -732,6 +687,30 @@ impl WorldStateView { transactions.sort(); transactions } + + /// Add the ability of emitting events to [`WorldStateView`]. + pub fn with_events(mut self, events_sender: EventsSender) -> Self { + self.events_sender = Some(events_sender); + self + } + + /// Get an immutable view of the `World`. + pub fn world(&self) -> &W { + &self.world + } + + /// Get triggers set and modify it with `f` + /// + /// Produces trigger event from `f` + pub fn modify_triggers(&self, f: F) -> Result<(), Error> + where + F: FnOnce(&TriggerSet) -> Result, + { + let event = f(&self.triggers).map(Into::into)?; + let events: SmallVec<[DataEvent; 1]> = SmallVec(smallvec::smallvec![event]); + self.produce_events(events); + Ok(()) + } } /// This module contains all configuration related logic. diff --git a/data_model/src/events/data.rs b/data_model/src/events/data.rs deleted file mode 100644 index b40a87ac309..00000000000 --- a/data_model/src/events/data.rs +++ /dev/null @@ -1,532 +0,0 @@ -//! Data events. - -#[cfg(not(feature = "std"))] -use alloc::{format, string::String, vec, vec::Vec}; - -use iroha_macro::FromVariant; -use iroha_schema::prelude::*; -use parity_scale_codec::{Decode, Encode}; -use serde::{Deserialize, Serialize}; - -use crate::prelude::*; - -pub mod typed { - //! This module contains typed events - - use super::*; - - macro_rules! typed_event_struct_definition { - ($i:ident, id: $id_type:ty) => { - typed_event_struct_definition! { - $i, - $id_type, - concat!(" ", stringify!($i), " event"), - concat!(" Create new ", stringify!($i), " event") - } - }; - ($i:ident, $id_type:ty, $struct_doc:expr, $new_doc:expr) => { - #[doc = $struct_doc] - #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, - )] - pub struct $i { - id: $id_type, - status: Status, - } - - impl $i { - #[doc = $new_doc] - pub fn new(id: $id_type, status: impl Into) -> Self { - $i { - id, - status: status.into(), - } - } - } - }; - } - - typed_event_struct_definition!(Asset, id: AssetId); - typed_event_struct_definition!(AssetDefinition, id: AssetDefinitionId); - typed_event_struct_definition!(Peer, id: PeerId); - typed_event_struct_definition!(OtherAccountChange, id: AccountId); - typed_event_struct_definition!(OtherDomainChange, id: DomainId); - #[cfg(feature = "roles")] - typed_event_struct_definition!(Role, id: RoleId); - - /// Account event - #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, - )] - pub enum Account { - /// Account change without asset changing - OtherAccountChange(OtherAccountChange), - /// Asset change - Asset(Asset), - } - - /// Domain Event - #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, - )] - pub enum Domain { - /// Domain change without account or asset definition change - OtherDomainChange(OtherDomainChange), - /// Account change - Account(Account), - /// Asset definition change - AssetDefinition(AssetDefinition), - } - - /// World event - #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, - )] - pub enum World { - /// Domain change - Domain(Domain), - /// Peer change - Peer(Peer), - /// Role change - #[cfg(feature = "roles")] - Role(Role), - } - - /// New Event - #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, - )] - pub enum Event { - /// World event - World(World), - /// Domain event - Domain(Domain), - /// Peer event - Peer(Peer), - /// Role event - #[cfg(feature = "roles")] - Role(Role), - /// Account event - Account(Account), - /// Asset definition event - AssetDefinition(AssetDefinition), - /// Asset event - Asset(Asset), - } - - pub mod prelude { - //! Exports common structs and enums from this module. - - #[cfg(feature = "roles")] - pub use super::Role as RoleEvent; - pub use super::{ - Account as AccountEvent, Asset as AssetEvent, AssetDefinition as AssetDefinitionEvent, - Domain as DomainEvent, OtherAccountChange as OtherAccountChangeEvent, - OtherDomainChange as OtherDomainChangeEvent, Peer as PeerEvent, World as WorldEvent, - }; - } -} - -/// Event. -#[derive(Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema)] -pub struct Event { - entity: Entity, - status: Status, -} - -/// Enumeration of all possible Iroha data entities. -#[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, -)] -pub enum Entity { - /// [`Account`]. - Account(AccountId), - /// [`AssetDefinition`]. - AssetDefinition(AssetDefinitionId), - /// [`Asset`]. - Asset(AssetId), - /// [`Domain`]. - Domain(DomainId), - /// [`Peer`]. - Peer(PeerId), - #[cfg(feature = "roles")] - /// [`Role`]. - Role(RoleId), - /// [`Trigger`] - Trigger(TriggerId), -} - -/// Entity status. -#[derive( - Copy, - Clone, - PartialEq, - Eq, - Debug, - Decode, - Encode, - Deserialize, - Serialize, - FromVariant, - IntoSchema, -)] -pub enum Status { - /// Entity was added, registered, minted or another action was made to make entity appear on - /// the blockchain for the first time. - Created, - /// Entity's state was changed, any parameter updated it's value. - Updated(Updated), - /// Entity was archived or by any other way was put into state that guarantees absence of - /// [`Updated`](`Status::Updated`) events for this entity. - Deleted, -} - -/// Description for [`Status::Updated`]. -#[derive( - Copy, - Clone, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - Debug, - Decode, - Encode, - Deserialize, - Serialize, - FromVariant, - IntoSchema, -)] -#[allow(missing_docs)] -pub enum Updated { - Metadata(MetadataUpdated), - Authentication, - Permission, - Asset(AssetUpdated), - Trigger(TriggerUpdated), -} - -/// Description for [`Updated::Metadata`]. -#[derive( - Copy, - Clone, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - Debug, - Decode, - Encode, - Deserialize, - Serialize, - FromVariant, - IntoSchema, -)] -#[allow(missing_docs)] -pub enum MetadataUpdated { - Inserted, - Removed, -} - -/// Description for [`Updated::Metadata`]. -#[derive( - Copy, - Clone, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - Debug, - Decode, - Encode, - Deserialize, - Serialize, - FromVariant, - IntoSchema, -)] -#[allow(missing_docs)] -pub enum TriggerUpdated { - Extended, - Shortened, -} - -/// Description for [`Updated::Asset`]. -#[derive( - Copy, - Clone, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - Debug, - Decode, - Encode, - Deserialize, - Serialize, - FromVariant, - IntoSchema, -)] -#[allow(missing_docs)] -pub enum AssetUpdated { - Received, - Sent, -} - -/// Filter to select [`Event`]s which match the `entity` and `status` conditions. -#[derive( - Default, - Debug, - Clone, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, -)] -pub struct EventFilter { - /// Optional filter by [`Entity`]. [`None`] accepts any entities. - entity: Option, - /// Optional filter by [`Status`]. [`None`] accepts any statuses. - status: Option, -} - -/// Filter to select entities under the [`Entity`] of the optional id, -/// or all the entities of the [`Entity`] type. -#[derive( - Debug, - Decode, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - Encode, - Deserialize, - Serialize, - Clone, - IntoSchema, -)] -pub enum EntityFilter { - /// Filter by [`Entity::Account`]. - Account(Option), - /// Filter by [`Entity::AssetDefinition`]. - AssetDefinition(Option), - /// Filter by [`Entity::Asset`]. - Asset(Option), - /// Filter by [`Entity::Domain`]. - Domain(Option), - /// Filter by [`Entity::Peer`]. - Peer(Option), -} - -/// Filter to select a status. -#[derive( - Copy, - Clone, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - Debug, - Decode, - Encode, - Deserialize, - Serialize, - FromVariant, - IntoSchema, -)] -pub enum StatusFilter { - /// Select [`Status::Created`]. - Created, - /// Select [`Status::Updated`] or more detailed status in option. - Updated(Option), - /// Select [`Status::Deleted`]. - Deleted, -} - -impl Event { - /// Construct [`Event`]. - pub fn new(entity: impl Into, status: impl Into) -> Self { - Self { - entity: entity.into(), - status: status.into(), - } - } -} - -impl EventFilter { - /// Construct [`EventFilter`]. - pub const fn new(entity: Option, status: Option) -> Self { - Self { entity, status } - } - - /// Check if `event` is accepted. - pub fn apply(&self, event: &Event) -> bool { - let entity_check = self - .entity - .as_ref() - .map_or(true, |entity| entity.apply(&event.entity)); - let status_check = self - .status - .map_or(true, |status| status.apply(event.status)); - entity_check && status_check - } -} - -impl EntityFilter { - fn apply(&self, entity: &Entity) -> bool { - match self { - Self::Account(opt) => match entity { - Entity::Account(account_id) => opt - .as_ref() - .map_or(true, |filter_id| account_id == filter_id), - Entity::Asset(asset_id) => opt - .as_ref() - .map_or(false, |filter_id| asset_id.account_id == *filter_id), - _ => false, - }, - Self::AssetDefinition(opt) => match entity { - Entity::AssetDefinition(asset_definition_id) => opt - .as_ref() - .map_or(true, |filter_id| asset_definition_id == filter_id), - Entity::Asset(asset_id) => opt - .as_ref() - .map_or(false, |filter_id| asset_id.definition_id == *filter_id), - _ => false, - }, - Self::Asset(opt) => match entity { - Entity::Asset(asset_id) => { - opt.as_ref().map_or(true, |filter_id| asset_id == filter_id) - } - _ => false, - }, - Self::Domain(opt) => match entity { - Entity::Account(account_id) => opt - .as_ref() - .map_or(false, |filter_id| account_id.domain_id == *filter_id), - Entity::AssetDefinition(asset_definition_id) => { - opt.as_ref().map_or(false, |filter_id| { - asset_definition_id.domain_id == *filter_id - }) - } - Entity::Asset(asset_id) => opt.as_ref().map_or(false, |filter_id| { - asset_id.account_id.domain_id == *filter_id - || asset_id.definition_id.domain_id == *filter_id - }), - Entity::Domain(id) => opt.as_ref().map_or(true, |filter_id| id == filter_id), - _ => false, - }, - Self::Peer(opt) => match entity { - Entity::Peer(peer_id) => { - opt.as_ref().map_or(true, |filter_id| peer_id == filter_id) - } - _ => false, - }, - } - } -} - -impl StatusFilter { - fn apply(self, status: Status) -> bool { - match self { - Self::Created => Status::Created == status, - Self::Updated(opt) => match status { - Status::Updated(detail) => { - opt.map_or(true, |filter_detail| detail == filter_detail) - } - _ => false, - }, - Self::Deleted => Status::Deleted == status, - } - } -} - -impl From for Status { - fn from(src: MetadataUpdated) -> Self { - Self::Updated(src.into()) - } -} - -impl From for Status { - fn from(src: AssetUpdated) -> Self { - Self::Updated(src.into()) - } -} - -mod trigger { - use super::TriggerUpdated; - use crate::prelude::*; - - impl From> for DataEvent { - fn from(src: Register) -> Self { - Self::new(src.object.id, DataStatus::Created) - } - } - - impl From> for DataEvent { - fn from(src: Unregister) -> Self { - Self::new(src.object_id, DataStatus::Deleted) - } - } - - impl From> for DataEvent { - fn from(src: Mint) -> Self { - Self::new( - src.destination_id, - DataStatus::Updated(Updated::Trigger(TriggerUpdated::Extended)), - ) - } - } - - impl From> for DataEvent { - fn from(src: Burn) -> Self { - Self::new( - src.destination_id, - DataStatus::Updated(Updated::Trigger(TriggerUpdated::Shortened)), - ) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn entity_scope() { - const DOMAIN: &str = "wonderland"; - const ACCOUNT: &str = "alice"; - const ASSET: &str = "rose"; - let domain = DomainId::test(DOMAIN); - let account = AccountId::test(ACCOUNT, DOMAIN); - let asset = AssetId::test(ASSET, DOMAIN, ACCOUNT, DOMAIN); - - let entity_created = |entity: Entity| Event::new(entity, Status::Created); - let domain_created = entity_created(Entity::Domain(domain)); - let account_created = entity_created(Entity::Account(account.clone())); - let asset_created = entity_created(Entity::Asset(asset)); - - let account_filter = EventFilter::new(Some(EntityFilter::Account(Some(account))), None); - assert!(!account_filter.apply(&domain_created)); - assert!(account_filter.apply(&account_created)); - assert!(account_filter.apply(&asset_created)); - } -} - -/// Exports common structs and enums from this module. -pub mod prelude { - pub use super::{ - typed::prelude::*, AssetUpdated, Entity as DataEntity, Event as DataEvent, - EventFilter as DataEventFilter, MetadataUpdated, Status as DataStatus, Updated, - }; -} diff --git a/data_model/src/events/data/events.rs b/data_model/src/events/data/events.rs index 02ecadc4ab3..daa740d1904 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -114,16 +114,26 @@ impl IdTrait for DomainEvent { } } +/// Trigger Event +#[derive(Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema)] +#[non_exhaustive] +#[allow(missing_docs)] +pub enum TriggerEvent { + Created(TriggerId), + Deleted(TriggerId), + Extended(TriggerId), + Shortened(TriggerId), +} + /// World event #[derive( Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, )] +#[allow(missing_docs)] pub enum WorldEvent { - /// Domain change Domain(DomainEvent), - /// Peer change Peer(PeerEvent), - /// Role change + #[cfg(feature = "roles")] Role(Role), } @@ -146,6 +156,8 @@ pub enum Event { AssetDefinition(AssetDefinitionEvent), /// Asset event Asset(AssetEvent), + /// Trigger event + Trigger(TriggerEvent), } /// Entity status. @@ -177,6 +189,8 @@ pub enum Status { #[derive( Copy, Clone, + PartialOrd, + Ord, PartialEq, Eq, Debug, @@ -186,6 +200,7 @@ pub enum Status { Serialize, FromVariant, IntoSchema, + Hash, )] #[allow(missing_docs)] pub enum Updated { @@ -199,6 +214,8 @@ pub enum Updated { #[derive( Copy, Clone, + PartialOrd, + Ord, PartialEq, Eq, Debug, @@ -208,6 +225,7 @@ pub enum Updated { Serialize, FromVariant, IntoSchema, + Hash, )] #[allow(missing_docs)] pub enum MetadataUpdated { @@ -219,6 +237,8 @@ pub enum MetadataUpdated { #[derive( Copy, Clone, + PartialOrd, + Ord, PartialEq, Eq, Debug, @@ -228,6 +248,7 @@ pub enum MetadataUpdated { Serialize, FromVariant, IntoSchema, + Hash, )] #[allow(missing_docs)] pub enum AssetUpdated { @@ -248,3 +269,31 @@ impl From for Status { Self::Updated(src.into()) } } + +mod trigger { + use crate::prelude::*; + + impl From> for DataEvent { + fn from(src: Register) -> Self { + Self::Trigger(TriggerEvent::Created(src.object.id)) + } + } + + impl From> for DataEvent { + fn from(src: Unregister) -> Self { + Self::Trigger(TriggerEvent::Deleted(src.object_id)) + } + } + + impl From> for DataEvent { + fn from(src: Mint) -> Self { + Self::Trigger(TriggerEvent::Extended(src.destination_id)) + } + } + + impl From> for DataEvent { + fn from(src: Burn) -> Self { + Self::Trigger(TriggerEvent::Shortened(src.destination_id)) + } + } +} diff --git a/data_model/src/events/data/filters.rs b/data_model/src/events/data/filters.rs index 2014abec3ac..d2fa82ff0da 100644 --- a/data_model/src/events/data/filters.rs +++ b/data_model/src/events/data/filters.rs @@ -5,7 +5,18 @@ use super::*; macro_rules! complex_entity_filter { (pub struct $name: ident { event: $entity_type:ty, filter: $event_filter_type:ty, }) => { #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, + Clone, + PartialOrd, + Ord, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Hash, )] pub struct $name { id_filter: FilterOpt::Id>>, @@ -59,7 +70,20 @@ mod detail { use super::*; - #[derive(Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema)] + #[derive( + Clone, + PartialOrd, + Ord, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Hash, + )] pub struct SimpleEntityFilter { id_filter: IdFilter, status_filter: StatusFilter, @@ -89,7 +113,20 @@ pub trait Filter { fn filter(&self, item: &Self::Item) -> bool; } -#[derive(Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema)] +#[derive( + Clone, + PartialEq, + PartialOrd, + Ord, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Hash, +)] pub enum FilterOpt { AcceptAll, BySome(F), @@ -108,7 +145,19 @@ impl Filter for FilterOpt { /// EntityFilter for `EventFilter` #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, + Clone, + PartialEq, + PartialOrd, + Ord, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + FromVariant, + IntoSchema, + Hash, )] pub enum EntityFilter { /// Domain entity. `None` value will accept all `Domain` events @@ -124,6 +173,7 @@ pub enum EntityFilter { ByAssetDefinition(FilterOpt), /// Asset entity. `None` value will accept all `Asset` events ByAsset(FilterOpt), + ByTrigger(FilterOpt), } impl Filter for EntityFilter { @@ -151,7 +201,19 @@ impl Filter for EntityFilter { } #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, + Clone, + PartialEq, + PartialOrd, + Ord, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + FromVariant, + IntoSchema, + Hash, )] pub enum DomainEventFilter { ByAccount(FilterOpt), @@ -185,7 +247,19 @@ impl Filter for DomainEventFilter { /// AccountFilter for `EntityFilter` #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, + Clone, + PartialEq, + PartialOrd, + Ord, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + FromVariant, + IntoSchema, + Hash, )] pub enum AccountEventFilter { ByAsset(FilterOpt), @@ -214,7 +288,56 @@ impl Filter for AccountEventFilter { } } -#[derive(Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema)] +#[derive( + Clone, + PartialOrd, + Ord, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + FromVariant, + IntoSchema, + Hash, +)] +pub enum TriggerFilter { + ByCreated, + ByDeleted, + ByExtended, + ByShortened, +} + +impl Filter for TriggerFilter { + type Item = TriggerEvent; + + fn filter(&self, event: &TriggerEvent) -> bool { + match (self, event) { + (Self::ByCreated, TriggerEvent::Created(_)) => true, + (Self::ByDeleted, TriggerEvent::Deleted(_)) => true, + (Self::ByExtended, TriggerEvent::Extended(_)) => true, + (Self::ByShortened, TriggerEvent::Shortened(_)) => true, + _ => false, + } + } +} + +#[derive( + Clone, + PartialOrd, + Ord, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Hash, +)] pub struct IdFilter(Id); impl Filter for IdFilter { @@ -227,7 +350,19 @@ impl Filter for IdFilter { /// Filter to select a status. #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, FromVariant, IntoSchema, + Clone, + PartialOrd, + Ord, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + FromVariant, + IntoSchema, + Hash, )] pub enum StatusFilter { Created, @@ -247,7 +382,20 @@ impl Filter for StatusFilter { } } -#[derive(Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema)] +#[derive( + Clone, + PartialOrd, + Ord, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Hash, +)] pub struct UpdatedFilter(Updated); impl Filter for UpdatedFilter { diff --git a/data_model/src/events/data/mod.rs b/data_model/src/events/data/mod.rs index 5233143ddaa..160d6c221a3 100644 --- a/data_model/src/events/data/mod.rs +++ b/data_model/src/events/data/mod.rs @@ -24,8 +24,8 @@ pub mod prelude { pub use super::{ events::{ AccountEvent, AssetDefinitionEvent, AssetEvent, AssetUpdated, DomainEvent, - Event as DataEvent, MetadataUpdated, PeerEvent, Status as DataStatus, Updated, - WorldEvent, + Event as DataEvent, MetadataUpdated, PeerEvent, Status as DataStatus, TriggerEvent, + Updated, WorldEvent, }, filters::{EventFilter as DataEventFilter, FilterOpt::*, *}, };