From 1f0e21d409abbbde4425accf9f2f0b1182671083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=AD=90=EF=B8=8FNINIKA=E2=AD=90=EF=B8=8F?= Date: Tue, 26 Mar 2024 14:00:55 +0300 Subject: [PATCH] [feature] #4350: Expose event set bitfields in schema (#4381) * [feature] #4350: Add BitmapMeta variant to schema * [feature] #4350: Expose the EventSet bitfields in schema Signed-off-by: Nikita Strygin --- data_model/derive/src/event_set.rs | 41 ++++- data_model/src/events/data/events.rs | 9 +- data_model/src/events/data/mod.rs | 2 +- docs/source/references/schema.json | 252 ++++++++++++++++++++++++++- schema/src/lib.rs | 21 +++ schema/src/serialize.rs | 22 ++- 6 files changed, 328 insertions(+), 19 deletions(-) diff --git a/data_model/derive/src/event_set.rs b/data_model/derive/src/event_set.rs index e6ed8c573a1..5c728906d31 100644 --- a/data_model/derive/src/event_set.rs +++ b/data_model/derive/src/event_set.rs @@ -131,20 +131,26 @@ impl ToTokens for EventSetEnum { variants, } = self; + let flag_raw_values = variants + .iter() + .zip(0u32..) + .map(|(_, i)| quote!(1 << #i)) + .collect::>(); + // definitions of consts for each event - let flag_defs = variants.iter().zip(0u32..).map( + let flag_defs = variants.iter().zip(flag_raw_values.iter()).map( |( EventSetVariant { flag_ident, event_ident, .. }, - i, + raw_value, )| { let doc = format!(" Matches [`{event_enum_ident}::{event_ident}`]"); quote! { #[doc = #doc] - #vis const #flag_ident: Self = Self(1 << #i); + #vis const #flag_ident: Self = Self(#raw_value); } }, ); @@ -197,8 +203,7 @@ impl ToTokens for EventSetEnum { // 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, + iroha_schema::TypeId, )] #[repr(transparent)] #[doc = #doc] @@ -380,6 +385,32 @@ impl ToTokens for EventSetEnum { deserializer.deserialize_seq(Visitor) } } + + + impl iroha_schema::IntoSchema for #set_ident { + fn type_name() -> iroha_schema::Ident { + ::id() + } + + fn update_schema_map(metamap: &mut iroha_schema::MetaMap) { + if !metamap.contains_key::() { + if !metamap.contains_key::() { + ::update_schema_map(metamap); + } + metamap.insert::(iroha_schema::Metadata::Bitmap(iroha_schema::BitmapMeta { + repr: core::any::TypeId::of::(), + masks: vec![ + #( + iroha_schema::BitmapMask { + name: String::from(#flag_names), + mask: #flag_raw_values, + }, + )* + ], + })); + } + } + } }) } } diff --git a/data_model/src/events/data/events.rs b/data_model/src/events/data/events.rs index 353eac1e28d..156be776be1 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -543,14 +543,19 @@ mod executor { use iroha_data_model_derive::model; pub use self::model::*; + // this is used in no_std + #[allow(unused)] + use super::*; #[model] pub mod model { - #[cfg(not(feature = "std"))] - use alloc::{format, string::String, vec::Vec}; use iroha_data_model_derive::EventSet; + // this is used in no_std + #[allow(unused)] + use super::*; + #[derive( Debug, Copy, diff --git a/data_model/src/events/data/mod.rs b/data_model/src/events/data/mod.rs index 54fe343831d..45ccd1226cd 100644 --- a/data_model/src/events/data/mod.rs +++ b/data_model/src/events/data/mod.rs @@ -1,7 +1,7 @@ //! Data events. #[cfg(not(feature = "std"))] -use alloc::{format, string::String, vec::Vec}; +use alloc::{format, string::String, vec, vec::Vec}; pub use events::DataEvent; pub use filters::DataEventFilter; diff --git a/docs/source/references/schema.json b/docs/source/references/schema.json index b6d93a29fdc..496bb8eec28 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -94,7 +94,57 @@ } ] }, - "AccountEventSet": "u32", + "AccountEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "AnyAsset", + "mask": 1 + }, + { + "name": "Created", + "mask": 2 + }, + { + "name": "Deleted", + "mask": 4 + }, + { + "name": "AuthenticationAdded", + "mask": 8 + }, + { + "name": "AuthenticationRemoved", + "mask": 16 + }, + { + "name": "PermissionAdded", + "mask": 32 + }, + { + "name": "PermissionRemoved", + "mask": 64 + }, + { + "name": "RoleRevoked", + "mask": 128 + }, + { + "name": "RoleGranted", + "mask": 256 + }, + { + "name": "MetadataInserted", + "mask": 512 + }, + { + "name": "MetadataRemoved", + "mask": 1024 + } + ] + } + }, "AccountId": { "Struct": [ { @@ -310,7 +360,41 @@ } ] }, - "AssetDefinitionEventSet": "u32", + "AssetDefinitionEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "Created", + "mask": 1 + }, + { + "name": "MintabilityChanged", + "mask": 2 + }, + { + "name": "OwnerChanged", + "mask": 4 + }, + { + "name": "Deleted", + "mask": 8 + }, + { + "name": "MetadataInserted", + "mask": 16 + }, + { + "name": "MetadataRemoved", + "mask": 32 + }, + { + "name": "TotalQuantityChanged", + "mask": 64 + } + ] + } + }, "AssetDefinitionId": { "Struct": [ { @@ -393,7 +477,37 @@ } ] }, - "AssetEventSet": "u32", + "AssetEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "Created", + "mask": 1 + }, + { + "name": "Deleted", + "mask": 2 + }, + { + "name": "Added", + "mask": 4 + }, + { + "name": "Removed", + "mask": 8 + }, + { + "name": "MetadataInserted", + "mask": 16 + }, + { + "name": "MetadataRemoved", + "mask": 32 + } + ] + } + }, "AssetId": { "Struct": [ { @@ -631,7 +745,25 @@ } ] }, - "ConfigurationEventSet": "u32", + "ConfigurationEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "Changed", + "mask": 1 + }, + { + "name": "Created", + "mask": 2 + }, + { + "name": "Deleted", + "mask": 4 + } + ] + } + }, "Container": { "Enum": [ { @@ -830,7 +962,41 @@ } ] }, - "DomainEventSet": "u32", + "DomainEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "AnyAccount", + "mask": 1 + }, + { + "name": "AnyAssetDefinition", + "mask": 2 + }, + { + "name": "Created", + "mask": 4 + }, + { + "name": "Deleted", + "mask": 8 + }, + { + "name": "MetadataInserted", + "mask": 16 + }, + { + "name": "MetadataRemoved", + "mask": 32 + }, + { + "name": "OwnerChanged", + "mask": 64 + } + ] + } + }, "DomainId": { "Struct": [ { @@ -1000,7 +1166,17 @@ } ] }, - "ExecutorEventSet": "u32", + "ExecutorEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "Upgraded", + "mask": 1 + } + ] + } + }, "Fail": { "Struct": [ { @@ -2364,7 +2540,21 @@ } ] }, - "PeerEventSet": "u32", + "PeerEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "Added", + "mask": 1 + }, + { + "name": "Removed", + "mask": 2 + } + ] + } + }, "PeerId": { "Struct": [ { @@ -3145,7 +3335,29 @@ } ] }, - "RoleEventSet": "u32", + "RoleEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "Created", + "mask": 1 + }, + { + "name": "Deleted", + "mask": 2 + }, + { + "name": "PermissionRemoved", + "mask": 4 + }, + { + "name": "PermissionAdded", + "mask": 8 + } + ] + } + }, "RoleId": { "Struct": [ { @@ -3863,7 +4075,29 @@ } ] }, - "TriggerEventSet": "u32", + "TriggerEventSet": { + "Bitmap": { + "repr": "u32", + "masks": [ + { + "name": "Created", + "mask": 1 + }, + { + "name": "Deleted", + "mask": 2 + }, + { + "name": "Extended", + "mask": 4 + }, + { + "name": "Shortened", + "mask": 8 + } + ] + } + }, "TriggerId": { "Struct": [ { diff --git a/schema/src/lib.rs b/schema/src/lib.rs index b566e1fa83c..5e880108542 100644 --- a/schema/src/lib.rs +++ b/schema/src/lib.rs @@ -145,6 +145,8 @@ pub enum Metadata { Option(core::any::TypeId), /// Result Result(ResultMeta), + /// A bitmap: integer where bits have a specific meaning + Bitmap(BitmapMeta), } /// Array metadata @@ -239,6 +241,25 @@ pub enum IntMode { Compact, } +/// Bitmap metadata +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BitmapMeta { + /// Underlying integer type + pub repr: core::any::TypeId, + /// Masks, specifying the meaning of the bits + pub masks: Vec, +} + +/// Bitmap mask +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] +pub struct BitmapMask { + /// Symbolic name of the mask + pub name: String, + /// Mask value + // while we can technically have masks with multiple bits set or intersecting masks, we currently only emit single-bit disjoint masks + pub mask: u64, +} + /// Compact predicate. Just for documentation purposes #[derive(Debug, Clone, Serialize)] pub struct Compact(T); diff --git a/schema/src/serialize.rs b/schema/src/serialize.rs index adaa44a1626..13f53a0a1ba 100644 --- a/schema/src/serialize.rs +++ b/schema/src/serialize.rs @@ -214,6 +214,24 @@ impl PartialEq for WithContext<'_, '_, FixedMeta> { } } +impl Serialize for WithContext<'_, '_, BitmapMeta> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(2))?; + map.serialize_entry("repr", self.type_name(self.data.repr))?; + map.serialize_entry("masks", &self.data.masks)?; + map.end() + } +} +impl PartialEq for WithContext<'_, '_, BitmapMeta> { + fn eq(&self, other: &Self) -> bool { + self.type_name(self.data.repr) == other.type_name(other.data.repr) + && self.data.masks == other.data.masks + } +} + impl Serialize for WithContext<'_, '_, Metadata> { fn serialize(&self, serializer: S) -> Result { macro_rules! match_variants { @@ -253,7 +271,7 @@ impl Serialize for WithContext<'_, '_, Metadata> { }; } - match_variants!(Struct, Enum, FixedPoint, Array, Vec, Map, Result) + match_variants!(Struct, Enum, FixedPoint, Array, Vec, Map, Result, Bitmap) } } impl PartialEq for WithContext<'_, '_, Metadata> { @@ -288,7 +306,7 @@ impl PartialEq for WithContext<'_, '_, Metadata> { }; } - match_variants!(Tuple, Struct, Enum, FixedPoint, Array, Vec, Map, Result) + match_variants!(Tuple, Struct, Enum, FixedPoint, Array, Vec, Map, Result, Bitmap) } }