Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] #2037: Introduce Pre-commit Triggers #2041

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
Copy link
Contributor

@appetrosyan appetrosyan Mar 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an option-like enum. It should use the niche available in the other data type to encode the discriminant, but I would double check.

Otherwise flatten.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. The sizes are identical even in Debug builds.

/// 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