From 01cfc25c3be14494a3adb9bc81a77ccc6b56c01b Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Thu, 31 Mar 2022 00:42:25 +0300 Subject: [PATCH 1/3] Make as much const fn as possible Signed-off-by: Daniil Polyakov --- data_model/src/events/data/filters.rs | 2 +- data_model/src/events/execute_trigger.rs | 4 ++-- data_model/src/events/time.rs | 8 ++++---- data_model/src/isi.rs | 2 +- data_model/src/merkle.rs | 2 +- tools/kura_inspector/src/print.rs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data_model/src/events/data/filters.rs b/data_model/src/events/data/filters.rs index 89c8ae79e8e..4ac5b5c283e 100644 --- a/data_model/src/events/data/filters.rs +++ b/data_model/src/events/data/filters.rs @@ -29,7 +29,7 @@ macro_rules! entity_filter { impl $name { #[doc = $new_doc] - pub fn new( + pub const fn new( id_filter: FilterOpt::Id>>, event_filter: FilterOpt<$event_filter_type>, ) -> Self { diff --git a/data_model/src/events/execute_trigger.rs b/data_model/src/events/execute_trigger.rs index b96d91bea74..3b562efbb96 100644 --- a/data_model/src/events/execute_trigger.rs +++ b/data_model/src/events/execute_trigger.rs @@ -14,7 +14,7 @@ pub struct Event { impl Event { /// Create new [`Event`] with `trigger_id` and `authority` - pub fn new(trigger_id: TriggerId, authority: AccountId) -> Self { + pub const fn new(trigger_id: TriggerId, authority: AccountId) -> Self { Self { trigger_id, authority, @@ -46,7 +46,7 @@ pub struct EventFilter { impl EventFilter { /// Create new [`EventFilter`] with `trigger_id` and `authority` - pub fn new(trigger_id: TriggerId, authority: AccountId) -> Self { + pub const fn new(trigger_id: TriggerId, authority: AccountId) -> Self { Self { trigger_id, authority, diff --git a/data_model/src/events/time.rs b/data_model/src/events/time.rs index d029f6f16f7..0198f177f42 100644 --- a/data_model/src/events/time.rs +++ b/data_model/src/events/time.rs @@ -18,7 +18,7 @@ pub struct Event { impl Event { /// Construct `Event` with `prev_interval` and `interval` - pub fn new(prev_interval: Option, interval: Interval) -> Self { + pub const fn new(prev_interval: Option, interval: Interval) -> Self { Self { prev_interval, interval, @@ -131,7 +131,7 @@ impl Schedule { /// Create new `Schedule` starting at `start` and without period #[must_use] #[inline] - pub fn starting_at(start: Duration) -> Self { + pub const fn starting_at(start: Duration) -> Self { Self { start, period: None, @@ -141,7 +141,7 @@ impl Schedule { /// Add `period` to `self` #[must_use] #[inline] - pub fn with_period(mut self, period: Duration) -> Self { + pub const fn with_period(mut self, period: Duration) -> Self { self.period = Some(period); self } @@ -173,7 +173,7 @@ pub struct Interval { impl Interval { /// Construct `Interval` with `since` and `step` #[inline] - pub fn new(since: Duration, length: Duration) -> Self { + pub const fn new(since: Duration, length: Duration) -> Self { Self { since, length } } } diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index bc4608c0e3f..0ba0d4a9671 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -322,7 +322,7 @@ pub struct ExecuteTriggerBox { impl ExecuteTriggerBox { /// Construct [`ExecuteTrigger`] #[inline] - pub fn new(trigger_id: TriggerId) -> Self { + pub const fn new(trigger_id: TriggerId) -> Self { Self { trigger_id } } diff --git a/data_model/src/merkle.rs b/data_model/src/merkle.rs index 7cae3a08072..4bb58e75b1f 100644 --- a/data_model/src/merkle.rs +++ b/data_model/src/merkle.rs @@ -93,7 +93,7 @@ impl MerkleTree { } /// Return the `Hash` of the root node. - pub fn root_hash(&self) -> HashOf { + pub const fn root_hash(&self) -> HashOf { self.root_node.hash().transmute() } diff --git a/tools/kura_inspector/src/print.rs b/tools/kura_inspector/src/print.rs index f49c0629bbd..8e0ee8efee9 100644 --- a/tools/kura_inspector/src/print.rs +++ b/tools/kura_inspector/src/print.rs @@ -98,7 +98,7 @@ mod tests { const BLOCKS_PER_FILE: u64 = 3; impl TestOutput { - fn new() -> Self { + const fn new() -> Self { Self { ok: Vec::new(), err: Vec::new(), From 0b6595201c389d3a0ddac65accce5c86a613816d Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Thu, 31 Mar 2022 01:11:45 +0300 Subject: [PATCH 2/3] Add Precommit execution time for filter Signed-off-by: Daniil Polyakov --- .../integration/triggers/time_trigger.rs | 2 +- core/src/smartcontracts/isi/triggers.rs | 5 +- data_model/benches/time_event_filter.rs | 2 +- data_model/src/events/time.rs | 48 +++++++++++++++---- 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/client/tests/integration/triggers/time_trigger.rs b/client/tests/integration/triggers/time_trigger.rs index f91827b7511..15ebfddfb1a 100644 --- a/client/tests/integration/triggers/time_trigger.rs +++ b/client/tests/integration/triggers/time_trigger.rs @@ -76,7 +76,7 @@ fn build_register_trigger_isi(asset_id: AssetId, schedule: TimeSchedule) -> Resu Executable::from(vec![instruction.into()]), Repeats::Indefinitely, asset_id.account_id, - EventFilter::Time(TimeEventFilter(schedule)), + EventFilter::Time(TimeEventFilter(ExecutionTime::Schedule(schedule))), ), )?))) } diff --git a/core/src/smartcontracts/isi/triggers.rs b/core/src/smartcontracts/isi/triggers.rs index 0007cd82c39..5e748cead88 100644 --- a/core/src/smartcontracts/isi/triggers.rs +++ b/core/src/smartcontracts/isi/triggers.rs @@ -16,7 +16,10 @@ impl OccursExactlyAtTime for Action { fn occurs_exactly_at_time(&self) -> bool { matches!( self.filter, - EventFilter::Time(TimeEventFilter(TimeSchedule { period: None, .. })) + EventFilter::Time(TimeEventFilter(ExecutionTime::Schedule(TimeSchedule { + period: None, + .. + }))) ) } } diff --git a/data_model/benches/time_event_filter.rs b/data_model/benches/time_event_filter.rs index e40f2a96bc3..08937c2c0f1 100644 --- a/data_model/benches/time_event_filter.rs +++ b/data_model/benches/time_event_filter.rs @@ -15,7 +15,7 @@ fn schedule_from_zero_with_little_period(criterion: &mut Criterion) { let interval = TimeInterval::new(Duration::from_secs(TIMESTAMP), Duration::from_secs(1)); let event = TimeEvent::new(None, interval); let schedule = TimeSchedule::starting_at(Duration::ZERO).with_period(Duration::from_millis(1)); - let filter = TimeEventFilter(schedule); + let filter = TimeEventFilter(ExecutionTime::Schedule(schedule)); criterion.bench_function("count_matches_from_zero", |b| { b.iter(|| filter.count_matches(&event)); diff --git a/data_model/src/events/time.rs b/data_model/src/events/time.rs index 0198f177f42..d8b7ede9cd0 100644 --- a/data_model/src/events/time.rs +++ b/data_model/src/events/time.rs @@ -42,18 +42,23 @@ impl Event { Serialize, Deserialize, )] -pub struct EventFilter(pub Schedule); +pub struct EventFilter(pub ExecutionTime); impl EventFilter { /// Compute how much times trigger with `self` as filter should be executed on `event` pub fn count_matches(&self, event: &Event) -> u32 { - let current_interval = event.prev_interval.map_or(event.interval, |prev| { - let estimation = event.interval.since + event.interval.length; - let prev_estimation = prev.since + prev.length; - Interval::new(prev_estimation, estimation.saturating_sub(prev_estimation)) - }); - - Self::count_matches_in_interval(&self.0, ¤t_interval) + match &self.0 { + ExecutionTime::PreCommit => 1, + ExecutionTime::Schedule(schedule) => { + let current_interval = event.prev_interval.map_or(event.interval, |prev| { + let estimation = event.interval.since + event.interval.length; + let prev_estimation = prev.since + prev.length; + Interval::new(prev_estimation, estimation.saturating_sub(prev_estimation)) + }); + + Self::count_matches_in_interval(schedule, ¤t_interval) + } + } } /// Count something with the `schedule` within the `interval` @@ -104,6 +109,29 @@ impl EventFilter { } } +/// Trigger execution time +#[derive( + Debug, + Clone, + Copy, + PartialOrd, + Ord, + PartialEq, + Eq, + Encode, + Decode, + Serialize, + Deserialize, + IntoSchema, + Hash, +)] +pub enum ExecutionTime { + /// Execute right before block commit + PreCommit, + /// Execute with some schedule + Schedule(Schedule), +} + /// Schedule of the trigger #[derive( Debug, @@ -188,8 +216,8 @@ impl From for Range { /// Exports common structs and enums from this module. pub mod prelude { pub use super::{ - Event as TimeEvent, EventFilter as TimeEventFilter, Interval as TimeInterval, - Schedule as TimeSchedule, + Event as TimeEvent, EventFilter as TimeEventFilter, ExecutionTime, + Interval as TimeInterval, Schedule as TimeSchedule, }; } From baa6b3bf29996b140f3f7a72086ebe6c9d6e700f Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Thu, 31 Mar 2022 02:05:12 +0300 Subject: [PATCH 3/3] Add integration test for pre-commit trigger Signed-off-by: Daniil Polyakov --- .../integration/triggers/time_trigger.rs | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/client/tests/integration/triggers/time_trigger.rs b/client/tests/integration/triggers/time_trigger.rs index 15ebfddfb1a..2df0eed6b1f 100644 --- a/client/tests/integration/triggers/time_trigger.rs +++ b/client/tests/integration/triggers/time_trigger.rs @@ -42,7 +42,8 @@ fn time_trigger_execution_count_error_should_be_less_than_10_percent() -> Result let schedule = TimeSchedule::starting_at(start_time).with_period(Duration::from_millis(PERIOD_MS)); - let register_trigger = build_register_trigger_isi(asset_id.clone(), schedule)?; + let register_trigger = + build_register_mint_rose_trigger_isi(asset_id.clone(), ExecutionTime::Schedule(schedule))?; test_client.submit(register_trigger)?; submit_sample_isi_on_every_block_commit(&mut test_client, &account_id, 3)?; @@ -67,8 +68,61 @@ fn time_trigger_execution_count_error_should_be_less_than_10_percent() -> Result Ok(()) } +#[test] +fn pre_commit_trigger_should_be_executed() -> Result<()> { + const CHECKS_COUNT: usize = 5; + + let (_rt, _peer, mut test_client) = ::start_test_with_runtime(); + wait_for_genesis_committed(&vec![test_client.clone()], 0); + + let asset_definition_id = AssetDefinitionId::new("rose", "wonderland").expect("Valid"); + let account_id = AccountId::new("alice", "wonderland").expect("Valid"); + let asset_id = AssetId::new(asset_definition_id, account_id.clone()); + + let mut prev_value = get_asset_value(&mut test_client, asset_id.clone())?; + + let register_trigger = + build_register_mint_rose_trigger_isi(asset_id.clone(), ExecutionTime::PreCommit)?; + test_client.submit(register_trigger)?; + + let block_filter = + EventFilter::Pipeline(PipelineEventFilter::by_entity(PipelineEntityType::Block)); + for _ in test_client + .listen_for_events(block_filter)? + .filter(|event| { + if let Ok(Event::Pipeline(event)) = event { + if event.status == PipelineStatus::Committed { + return true; + } + } + false + }) + .take(CHECKS_COUNT) + { + let new_value = get_asset_value(&mut test_client, asset_id.clone())?; + assert_eq!(new_value, prev_value + 1); + prev_value = new_value; + + // ISI just to create a new block + let sample_isi = + SetKeyValueBox::new(account_id.clone(), Name::new("key")?, String::from("value")); + test_client.submit(sample_isi)?; + } + + Ok(()) +} + +/// Get asset numeric value +fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Result { + let asset = client.request(client::asset::by_id(asset_id))?; + Ok(*TryAsRef::::try_as_ref(&asset.value)?) +} + /// Build register ISI for trigger which mints roses -fn build_register_trigger_isi(asset_id: AssetId, schedule: TimeSchedule) -> Result { +fn build_register_mint_rose_trigger_isi( + asset_id: AssetId, + execution_time: ExecutionTime, +) -> Result { let instruction = MintBox::new(1_u32, asset_id.clone()); Ok(RegisterBox::new(IdentifiableBox::from(Trigger::new( "mint_rose", @@ -76,7 +130,7 @@ fn build_register_trigger_isi(asset_id: AssetId, schedule: TimeSchedule) -> Resu Executable::from(vec![instruction.into()]), Repeats::Indefinitely, asset_id.account_id, - EventFilter::Time(TimeEventFilter(ExecutionTime::Schedule(schedule))), + EventFilter::Time(TimeEventFilter(execution_time)), ), )?))) }