diff --git a/client/tests/integration/triggers/mod.rs b/client/tests/integration/triggers/mod.rs index 989d34818b0..854da8753d3 100644 --- a/client/tests/integration/triggers/mod.rs +++ b/client/tests/integration/triggers/mod.rs @@ -2,3 +2,4 @@ mod by_call_trigger; mod data_trigger; mod event_trigger; mod time_trigger; +mod trigger_rollback; diff --git a/client/tests/integration/triggers/trigger_rollback.rs b/client/tests/integration/triggers/trigger_rollback.rs new file mode 100644 index 00000000000..67861a9f7b2 --- /dev/null +++ b/client/tests/integration/triggers/trigger_rollback.rs @@ -0,0 +1,43 @@ +use std::str::FromStr as _; + +use eyre::Result; +use iroha_client::client::QueryResult; +use iroha_data_model::{prelude::*, query::asset::FindAllAssetsDefinitions, trigger::TriggerId}; +use test_network::*; + +#[test] +fn failed_trigger_revert() -> Result<()> { + let (_rt, _peer, client) = ::new().with_port(11_110).start_with_runtime(); + wait_for_genesis_committed(&[client.clone()], 0); + + //When + let trigger_id = TriggerId::from_str("trigger")?; + let account_id = AccountId::from_str("alice@wonderland")?; + let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; + let create_asset = RegisterExpr::new(AssetDefinition::quantity(asset_definition_id.clone())); + let instructions: [InstructionExpr; 2] = [create_asset.into(), Fail::new("Always fail").into()]; + let register_trigger = RegisterExpr::new(Trigger::new( + trigger_id.clone(), + Action::new( + instructions, + Repeats::Indefinitely, + account_id.clone(), + TriggeringFilterBox::ExecuteTrigger(ExecuteTriggerEventFilter::new( + trigger_id.clone(), + account_id, + )), + ), + )); + let _ = client.submit_blocking(register_trigger); + + let call_trigger = ExecuteTriggerExpr::new(trigger_id); + client.submit_blocking(call_trigger)?; + + //Then + let request = FindAllAssetsDefinitions; + let query_result = client.request(request)?.collect::>>()?; + assert!(query_result + .iter() + .all(|asset_definition| asset_definition.id() != &asset_definition_id)); + Ok(()) +} diff --git a/configs/peer/executor.wasm b/configs/peer/executor.wasm index b74e020ea15..544c9e29dfa 100644 Binary files a/configs/peer/executor.wasm and b/configs/peer/executor.wasm differ diff --git a/core/src/wsv.rs b/core/src/wsv.rs index 34955f7ecbd..d38c32774ea 100644 --- a/core/src/wsv.rs +++ b/core/src/wsv.rs @@ -551,12 +551,15 @@ impl WorldStateView { continue; } } + let wsv = self.clone(); let event = match self.process_trigger(&id, &action, event) { Ok(_) => { succeed.push(id.clone()); TriggerCompletedEvent::new(id, TriggerCompletedOutcome::Success) } Err(error) => { + // Revert to previous state on failure inside trigger + *self = wsv; let event = TriggerCompletedEvent::new( id, TriggerCompletedOutcome::Failure(error.to_string()),