Skip to content

Commit

Permalink
[feature] #2037: Introduce Pre-commit Triggers (#2041)
Browse files Browse the repository at this point in the history
  • Loading branch information
Arjentix authored Mar 31, 2022
1 parent 892c2d2 commit 8d83a3e
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 25 deletions.
60 changes: 57 additions & 3 deletions client/tests/integration/triggers/time_trigger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand All @@ -67,16 +68,69 @@ 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) = <TestPeer>::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<u32> {
let asset = client.request(client::asset::by_id(asset_id))?;
Ok(*TryAsRef::<u32>::try_as_ref(&asset.value)?)
}

/// Build register ISI for trigger which mints roses
fn build_register_trigger_isi(asset_id: AssetId, schedule: TimeSchedule) -> Result<RegisterBox> {
fn build_register_mint_rose_trigger_isi(
asset_id: AssetId,
execution_time: ExecutionTime,
) -> Result<RegisterBox> {
let instruction = MintBox::new(1_u32, asset_id.clone());
Ok(RegisterBox::new(IdentifiableBox::from(Trigger::new(
"mint_rose",
Action::new(
Executable::from(vec![instruction.into()]),
Repeats::Indefinitely,
asset_id.account_id,
EventFilter::Time(TimeEventFilter(schedule)),
EventFilter::Time(TimeEventFilter(execution_time)),
),
)?)))
}
Expand Down
5 changes: 4 additions & 1 deletion core/src/smartcontracts/isi/triggers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
..
})))
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion data_model/benches/time_event_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion data_model/src/events/data/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ macro_rules! entity_filter {

impl $name {
#[doc = $new_doc]
pub fn new(
pub const fn new(
id_filter: FilterOpt<IdFilter<<$entity_type as IdTrait>::Id>>,
event_filter: FilterOpt<$event_filter_type>,
) -> Self {
Expand Down
4 changes: 2 additions & 2 deletions data_model/src/events/execute_trigger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
56 changes: 42 additions & 14 deletions data_model/src/events/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct Event {

impl Event {
/// Construct `Event` with `prev_interval` and `interval`
pub fn new(prev_interval: Option<Interval>, interval: Interval) -> Self {
pub const fn new(prev_interval: Option<Interval>, interval: Interval) -> Self {
Self {
prev_interval,
interval,
Expand All @@ -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, &current_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, &current_interval)
}
}
}

/// Count something with the `schedule` within the `interval`
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -131,7 +159,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,
Expand All @@ -141,7 +169,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
}
Expand Down Expand Up @@ -173,7 +201,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 }
}
}
Expand All @@ -188,8 +216,8 @@ impl From<Interval> for Range<Duration> {
/// 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,
};
}

Expand Down
2 changes: 1 addition & 1 deletion data_model/src/isi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}

Expand Down
2 changes: 1 addition & 1 deletion data_model/src/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl<T> MerkleTree<T> {
}

/// Return the `Hash` of the root node.
pub fn root_hash(&self) -> HashOf<Self> {
pub const fn root_hash(&self) -> HashOf<Self> {
self.root_node.hash().transmute()
}

Expand Down
2 changes: 1 addition & 1 deletion tools/kura_inspector/src/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down

0 comments on commit 8d83a3e

Please sign in to comment.