diff --git a/client/tests/integration/events/data.rs b/client/tests/integration/events/data.rs index 9a6d6986cc2..d77e303c9c9 100644 --- a/client/tests/integration/events/data.rs +++ b/client/tests/integration/events/data.rs @@ -199,11 +199,11 @@ fn produce_multiple_events() -> Result<()> { let alice_id = AccountId::from_str("alice@wonderland")?; let role_id = RoleId::from_str("TEST_ROLE")?; let token_1 = PermissionToken::new( - "CanRemoveKeyValueInUserAccount".parse()?, + "CanRemoveKeyValueInAccount".parse()?, &json!({ "account_id": alice_id }), ); let token_2 = PermissionToken::new( - "CanSetKeyValueInUserAccount".parse()?, + "CanSetKeyValueInAccount".parse()?, &json!({ "account_id": alice_id }), ); let role = iroha_client::data_model::role::Role::new(role_id.clone()) diff --git a/client/tests/integration/queries/role.rs b/client/tests/integration/queries/role.rs index 9d18b523910..d437b6f6926 100644 --- a/client/tests/integration/queries/role.rs +++ b/client/tests/integration/queries/role.rs @@ -131,7 +131,7 @@ fn find_roles_by_account_id() -> Result<()> { .cloned() .map(|role_id| { Register::role(Role::new(role_id).add_permission(PermissionToken::new( - "CanSetKeyValueInUserAccount".parse().unwrap(), + "CanSetKeyValueInAccount".parse().unwrap(), &json!({ "account_id": alice_id }), ))) }) diff --git a/client/tests/integration/roles.rs b/client/tests/integration/roles.rs index 6f260e3709f..3a3bcd7aff6 100644 --- a/client/tests/integration/roles.rs +++ b/client/tests/integration/roles.rs @@ -69,11 +69,11 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { let role_id = RoleId::from_str("ACCESS_TO_MOUSE_METADATA")?; let role = Role::new(role_id.clone()) .add_permission(PermissionToken::new( - "CanSetKeyValueInUserAccount".parse()?, + "CanSetKeyValueInAccount".parse()?, &json!({ "account_id": mouse_id }), )) .add_permission(PermissionToken::new( - "CanRemoveKeyValueInUserAccount".parse()?, + "CanRemoveKeyValueInAccount".parse()?, &json!({ "account_id": mouse_id }), )); let register_role = Register::role(role); @@ -119,7 +119,7 @@ fn unregistered_role_removed_from_account() -> Result<()> { // Register root role let register_role = Register::role(Role::new(role_id.clone()).add_permission( PermissionToken::new( - "CanSetKeyValueInUserAccount".parse()?, + "CanSetKeyValueInAccount".parse()?, &json!({ "account_id": alice_id }), ), )); @@ -156,7 +156,7 @@ fn role_with_invalid_permissions_is_not_accepted() -> Result<()> { let role_id = RoleId::from_str("ACCESS_TO_ACCOUNT_METADATA")?; let rose_asset_id = AssetId::from_str("rose##alice@wonderland")?; let role = Role::new(role_id).add_permission(PermissionToken::new( - "CanSetKeyValueInUserAccount".parse()?, + "CanSetKeyValueInAccount".parse()?, &json!({ "account_id": rose_asset_id }), )); @@ -257,7 +257,7 @@ fn grant_revoke_role_permissions() -> Result<()> { "value".to_owned(), ); let permission = PermissionToken::new( - "CanSetKeyValueInUserAccount".parse()?, + "CanSetKeyValueInAccount".parse()?, &json!({ "account_id": mouse_id }), ); let grant_role_permission = Grant::role_permission(permission.clone(), role_id.clone()); diff --git a/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs b/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs index 8eff37089b2..1f62efe9689 100644 --- a/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs +++ b/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs @@ -16,7 +16,7 @@ static ALLOC: LockedAllocator = LockedAllocator::new(FreeList getrandom::register_custom_getrandom!(iroha_trigger::stub_getrandom); #[iroha_trigger::main] -fn main(_owner: AccountId, _event: EventBox) { +fn main(_id: TriggerId, _owner: AccountId, _event: EventBox) { iroha_trigger::log::info!("Executing trigger"); let accounts_cursor = FindAllAccounts.execute().dbg_unwrap(); diff --git a/client/tests/integration/smartcontracts/mint_rose_trigger/src/lib.rs b/client/tests/integration/smartcontracts/mint_rose_trigger/src/lib.rs index 701956c8ad8..1420b7fd394 100644 --- a/client/tests/integration/smartcontracts/mint_rose_trigger/src/lib.rs +++ b/client/tests/integration/smartcontracts/mint_rose_trigger/src/lib.rs @@ -17,12 +17,19 @@ getrandom::register_custom_getrandom!(iroha_trigger::stub_getrandom); /// Mint 1 rose for owner #[iroha_trigger::main] -fn main(owner: AccountId, _event: EventBox) { +fn main(id: TriggerId, owner: AccountId, _event: EventBox) { let rose_definition_id = AssetDefinitionId::from_str("rose#wonderland") .dbg_expect("Failed to parse `rose#wonderland` asset definition id"); let rose_id = AssetId::new(rose_definition_id, owner); - Mint::asset_numeric(1u32, rose_id) + let val: u32 = FindTriggerKeyValueByIdAndKey::new(id, "VAL".parse().unwrap()) + .execute() + .dbg_unwrap() + .into_inner() + .try_into() + .dbg_unwrap(); + + Mint::asset_numeric(val, rose_id) .execute() .dbg_expect("Failed to mint rose"); } diff --git a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/Cargo.toml b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/Cargo.toml index 313e8e91319..9b4ccbcb1f0 100644 --- a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/Cargo.toml +++ b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/Cargo.toml @@ -16,4 +16,5 @@ iroha_smart_contract.workspace = true panic-halt.workspace = true lol_alloc.workspace = true getrandom.workspace = true +parity-scale-codec.workspace = true serde_json = { version = "1.0.108", default-features = false } diff --git a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs index feadea44447..ba048b48815 100644 --- a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs +++ b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs @@ -10,29 +10,43 @@ extern crate alloc; use alloc::string::ToString as _; use core::num::NonZeroU32; -use iroha_smart_contract::{data_model::metadata::MetadataValueBox, parse, prelude::*}; +use iroha_smart_contract::{ + data_model::{metadata::MetadataValueBox, query::cursor::ForwardCursor}, + parse, + prelude::*, +}; use lol_alloc::{FreeListAllocator, LockedAllocator}; +use parity_scale_codec::{Decode, DecodeAll, Encode}; #[global_allocator] static ALLOC: LockedAllocator = LockedAllocator::new(FreeListAllocator::new()); getrandom::register_custom_getrandom!(iroha_smart_contract::stub_getrandom); +#[derive(Debug, Decode)] +struct QueryOutputCursor { + _batch: alloc::vec::Vec, + cursor: ForwardCursor, +} + /// Execute [`FindAllAssets`] and save cursor to the owner's metadata. #[iroha_smart_contract::main] fn main(owner: AccountId) { - let asset_cursor = FindAllAssets - .fetch_size(FetchSize::new(Some(NonZeroU32::try_from(1).dbg_unwrap()))) - .execute() - .dbg_unwrap(); - - let (_batch, cursor) = asset_cursor.into_parts(); + // NOTE: Fields of QueryOutputCursor are private so we guess the layout by encoding/decoding + let asset_cursor = QueryOutputCursor::decode_all( + &mut &FindAllAssets + .fetch_size(FetchSize::new(Some(NonZeroU32::try_from(1).dbg_unwrap()))) + .execute() + .dbg_unwrap() + .encode()[..], + ) + .dbg_unwrap(); SetKeyValue::account( owner, parse!("cursor" as Name), MetadataValueBox::String( - serde_json::to_value(cursor) + serde_json::to_value(&asset_cursor.cursor) .dbg_expect("Failed to convert cursor to JSON") .to_string(), ), diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index ff76caf34f2..37ccf66d12e 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -376,6 +376,13 @@ fn trigger_in_genesis_using_base64() -> Result<()> { let prev_value = get_asset_value(&mut test_client, asset_id.clone()); // Executing trigger + test_client + .submit_blocking(SetKeyValue::trigger( + trigger_id.clone(), + "VAL".parse()?, + numeric!(1), + )) + .unwrap(); let call_trigger = ExecuteTrigger::new(trigger_id); test_client.submit_blocking(call_trigger)?; diff --git a/config/src/parameters/actual.rs b/config/src/parameters/actual.rs index df23be982f7..6b10b3d870b 100644 --- a/config/src/parameters/actual.rs +++ b/config/src/parameters/actual.rs @@ -177,10 +177,11 @@ pub struct ChainWide { pub block_time: Duration, pub commit_time: Duration, pub transaction_limits: TransactionLimits, - pub asset_metadata_limits: MetadataLimits, + pub domain_metadata_limits: MetadataLimits, pub asset_definition_metadata_limits: MetadataLimits, pub account_metadata_limits: MetadataLimits, - pub domain_metadata_limits: MetadataLimits, + pub asset_metadata_limits: MetadataLimits, + pub trigger_metadata_limits: MetadataLimits, pub ident_length_limits: LengthLimits, pub executor_runtime: WasmRuntime, pub wasm_runtime: WasmRuntime, @@ -204,6 +205,7 @@ impl Default for ChainWide { account_metadata_limits: defaults::chain_wide::DEFAULT_METADATA_LIMITS, asset_definition_metadata_limits: defaults::chain_wide::DEFAULT_METADATA_LIMITS, asset_metadata_limits: defaults::chain_wide::DEFAULT_METADATA_LIMITS, + trigger_metadata_limits: defaults::chain_wide::DEFAULT_METADATA_LIMITS, ident_length_limits: defaults::chain_wide::DEFAULT_IDENT_LENGTH_LIMITS, executor_runtime: WasmRuntime::default(), wasm_runtime: WasmRuntime::default(), diff --git a/config/src/parameters/user.rs b/config/src/parameters/user.rs index 1a0d7a5ced1..5afae534472 100644 --- a/config/src/parameters/user.rs +++ b/config/src/parameters/user.rs @@ -566,10 +566,11 @@ pub struct ChainWide { pub block_time: Duration, pub commit_time: Duration, pub transaction_limits: TransactionLimits, - pub asset_metadata_limits: MetadataLimits, + pub domain_metadata_limits: MetadataLimits, pub asset_definition_metadata_limits: MetadataLimits, pub account_metadata_limits: MetadataLimits, - pub domain_metadata_limits: MetadataLimits, + pub asset_metadata_limits: MetadataLimits, + pub trigger_metadata_limits: MetadataLimits, pub ident_length_limits: LengthLimits, pub executor_fuel_limit: u64, pub executor_max_memory: HumanBytes, @@ -585,6 +586,7 @@ impl ChainWide { commit_time, transaction_limits, asset_metadata_limits, + trigger_metadata_limits, asset_definition_metadata_limits, account_metadata_limits, domain_metadata_limits, @@ -601,6 +603,7 @@ impl ChainWide { commit_time, transaction_limits, asset_metadata_limits, + trigger_metadata_limits, asset_definition_metadata_limits, account_metadata_limits, domain_metadata_limits, diff --git a/config/src/parameters/user/boilerplate.rs b/config/src/parameters/user/boilerplate.rs index 2e9b8695474..b2863772d71 100644 --- a/config/src/parameters/user/boilerplate.rs +++ b/config/src/parameters/user/boilerplate.rs @@ -656,10 +656,11 @@ pub struct ChainWidePartial { pub block_time: UserField, pub commit_time: UserField, pub transaction_limits: UserField, - pub asset_metadata_limits: UserField, + pub domain_metadata_limits: UserField, pub asset_definition_metadata_limits: UserField, pub account_metadata_limits: UserField, - pub domain_metadata_limits: UserField, + pub asset_metadata_limits: UserField, + pub trigger_metadata_limits: UserField, pub ident_length_limits: UserField, pub executor_fuel_limit: UserField, pub executor_max_memory: UserField>, @@ -682,8 +683,8 @@ impl UnwrapPartial for ChainWidePartial { transaction_limits: self .transaction_limits .unwrap_or(DEFAULT_TRANSACTION_LIMITS), - asset_metadata_limits: self - .asset_metadata_limits + domain_metadata_limits: self + .domain_metadata_limits .unwrap_or(DEFAULT_METADATA_LIMITS), asset_definition_metadata_limits: self .asset_definition_metadata_limits @@ -691,8 +692,11 @@ impl UnwrapPartial for ChainWidePartial { account_metadata_limits: self .account_metadata_limits .unwrap_or(DEFAULT_METADATA_LIMITS), - domain_metadata_limits: self - .domain_metadata_limits + asset_metadata_limits: self + .asset_metadata_limits + .unwrap_or(DEFAULT_METADATA_LIMITS), + trigger_metadata_limits: self + .trigger_metadata_limits .unwrap_or(DEFAULT_METADATA_LIMITS), ident_length_limits: self .ident_length_limits diff --git a/config/tests/fixtures.rs b/config/tests/fixtures.rs index 0c0c5c586ca..1f95e1650aa 100644 --- a/config/tests/fixtures.rs +++ b/config/tests/fixtures.rs @@ -138,7 +138,7 @@ fn minimal_config_snapshot() -> Result<()> { max_instruction_number: 4096, max_wasm_size_bytes: 4194304, }, - asset_metadata_limits: Limits { + domain_metadata_limits: Limits { capacity: 1048576, max_entry_len: 4096, }, @@ -150,7 +150,11 @@ fn minimal_config_snapshot() -> Result<()> { capacity: 1048576, max_entry_len: 4096, }, - domain_metadata_limits: Limits { + asset_metadata_limits: Limits { + capacity: 1048576, + max_entry_len: 4096, + }, + trigger_metadata_limits: Limits { capacity: 1048576, max_entry_len: 4096, }, @@ -410,10 +414,11 @@ fn full_envs_set_is_consumed() -> Result<()> { block_time: None, commit_time: None, transaction_limits: None, - asset_metadata_limits: None, + domain_metadata_limits: None, asset_definition_metadata_limits: None, account_metadata_limits: None, - domain_metadata_limits: None, + asset_metadata_limits: None, + trigger_metadata_limits: None, ident_length_limits: None, executor_fuel_limit: None, executor_max_memory: None, diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm index c6700b3d1e4..b0ed7abcbc3 100644 Binary files a/configs/swarm/executor.wasm and b/configs/swarm/executor.wasm differ diff --git a/configs/swarm/genesis.json b/configs/swarm/genesis.json index 6c53b83ec1b..e7126967f9e 100644 --- a/configs/swarm/genesis.json +++ b/configs/swarm/genesis.json @@ -135,7 +135,7 @@ "NewParameter": "?TransactionLimits=4096,4194304_TL" }, { - "NewParameter": "?WSVAssetMetadataLimits=1048576,4096_ML" + "NewParameter": "?WSVDomainMetadataLimits=1048576,4096_ML" }, { "NewParameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML" @@ -144,7 +144,10 @@ "NewParameter": "?WSVAccountMetadataLimits=1048576,4096_ML" }, { - "NewParameter": "?WSVDomainMetadataLimits=1048576,4096_ML" + "NewParameter": "?WSVAssetMetadataLimits=1048576,4096_ML" + }, + { + "NewParameter": "?WSVTriggerMetadataLimits=1048576,4096_ML" }, { "NewParameter": "?WSVIdentLengthLimits=1,128_LL" @@ -167,13 +170,13 @@ "id": "ALICE_METADATA_ACCESS", "permissions": [ { - "definition_id": "CanRemoveKeyValueInUserAccount", + "definition_id": "CanRemoveKeyValueInAccount", "payload": { "account_id": "alice@wonderland" } }, { - "definition_id": "CanSetKeyValueInUserAccount", + "definition_id": "CanSetKeyValueInAccount", "payload": { "account_id": "alice@wonderland" } diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index aa814e37f7a..fd238417169 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -285,8 +285,8 @@ pub mod isi { state_transaction .world .emit_events(Some(AccountEvent::MetadataInserted(MetadataChanged { - target_id: account_id.clone(), - key: self.key.clone(), + target_id: account_id, + key: self.key, value: self.value, }))); @@ -316,7 +316,7 @@ pub mod isi { state_transaction .world .emit_events(Some(AccountEvent::MetadataRemoved(MetadataChanged { - target_id: account_id.clone(), + target_id: account_id, key: self.key, value, }))); diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 7d04c07c6ba..2c8dde57a15 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -181,6 +181,7 @@ impl Execute for SetKeyValueBox { Self::Account(isi) => isi.execute(authority, state_transaction), Self::AssetDefinition(isi) => isi.execute(authority, state_transaction), Self::Asset(isi) => isi.execute(authority, state_transaction), + Self::Trigger(isi) => isi.execute(authority, state_transaction), } } } @@ -196,6 +197,7 @@ impl Execute for RemoveKeyValueBox { Self::Account(isi) => isi.execute(authority, state_transaction), Self::AssetDefinition(isi) => isi.execute(authority, state_transaction), Self::Asset(isi) => isi.execute(authority, state_transaction), + Self::Trigger(isi) => isi.execute(authority, state_transaction), } } } diff --git a/core/src/smartcontracts/isi/triggers/mod.rs b/core/src/smartcontracts/isi/triggers/mod.rs index 611f87e01db..00ea4338215 100644 --- a/core/src/smartcontracts/isi/triggers/mod.rs +++ b/core/src/smartcontracts/isi/triggers/mod.rs @@ -177,6 +177,72 @@ pub mod isi { } } + impl Execute for SetKeyValue { + #[metrics(+"set_trigger_key_value")] + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { + let trigger_id = self.object_id; + + let trigger_metadata_limits = state_transaction.config.account_metadata_limits; + state_transaction + .world + .triggers + .inspect_by_id_mut(&trigger_id, |action| { + action.metadata_mut().insert_with_limits( + self.key.clone(), + self.value.clone(), + trigger_metadata_limits, + ) + }) + .ok_or(FindError::Trigger(trigger_id.clone()))??; + + state_transaction + .world + .emit_events(Some(TriggerEvent::MetadataInserted(MetadataChanged { + target_id: trigger_id, + key: self.key, + value: self.value, + }))); + + Ok(()) + } + } + + impl Execute for RemoveKeyValue { + #[metrics(+"remove_trigger_key_value")] + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { + let trigger_id = self.object_id; + + let value = state_transaction + .world + .triggers + .inspect_by_id_mut(&trigger_id, |action| { + action + .metadata_mut() + .remove(&self.key) + .ok_or_else(|| FindError::MetadataKey(self.key.clone())) + }) + .ok_or(FindError::Trigger(trigger_id.clone()))??; + + state_transaction + .world + .emit_events(Some(TriggerEvent::MetadataRemoved(MetadataChanged { + target_id: trigger_id, + key: self.key, + value, + }))); + + Ok(()) + } + } + impl Execute for ExecuteTrigger { #[metrics(+"execute_trigger")] fn execute( diff --git a/core/src/smartcontracts/isi/triggers/specialized.rs b/core/src/smartcontracts/isi/triggers/specialized.rs index 24aa7b34500..ef087275f7f 100644 --- a/core/src/smartcontracts/isi/triggers/specialized.rs +++ b/core/src/smartcontracts/isi/triggers/specialized.rs @@ -152,6 +152,9 @@ pub trait LoadedActionTrait { /// Get action metadata fn metadata(&self) -> &Metadata; + /// Get action metadata + fn metadata_mut(&mut self) -> &mut Metadata; + /// Check if action is mintable. fn mintable(&self) -> bool; @@ -181,10 +184,15 @@ impl + Clone> LoadedActionTrait &self.authority } + /// Get action metadata fn metadata(&self) -> &Metadata { &self.metadata } + fn metadata_mut(&mut self) -> &mut Metadata { + &mut self.metadata + } + fn mintable(&self) -> bool { self.filter.mintable() } diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index 25f27e25675..bb836227aee 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -464,6 +464,8 @@ pub mod state { /// Trigger execution state #[derive(Constructor)] pub struct Trigger { + pub(in super::super) id: TriggerId, + /// Event which activated this trigger pub(in super::super) triggering_event: EventBox, } @@ -985,7 +987,7 @@ impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime Runtime payloads::Trigger { payloads::Trigger { + id: state.specific_state.id.clone(), owner: state.authority.clone(), event: state.specific_state.triggering_event.clone(), } diff --git a/core/src/state.rs b/core/src/state.rs index 20255cdfe51..81ec0c79dec 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -1356,10 +1356,11 @@ impl<'state> StateBlock<'state> { } update_params! { - WSV_ASSET_METADATA_LIMITS => self.config.asset_metadata_limits, + WSV_DOMAIN_METADATA_LIMITS => self.config.domain_metadata_limits, WSV_ASSET_DEFINITION_METADATA_LIMITS => self.config.asset_definition_metadata_limits, WSV_ACCOUNT_METADATA_LIMITS => self.config.account_metadata_limits, - WSV_DOMAIN_METADATA_LIMITS => self.config.domain_metadata_limits, + WSV_ASSET_METADATA_LIMITS => self.config.asset_metadata_limits, + WSV_TRIGGER_METADATA_LIMITS => self.config.trigger_metadata_limits, WSV_IDENT_LENGTH_LIMITS => self.config.ident_length_limits, EXECUTOR_FUEL_LIMIT => self.config.executor_runtime.fuel_limit, EXECUTOR_MAX_MEMORY => self.config.executor_runtime.max_memory_bytes, diff --git a/data_model/src/events/data/events.rs b/data_model/src/events/data/events.rs index 6ee1e5b8dde..10f266f9ef3 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -486,6 +486,9 @@ mod trigger { pub use self::model::*; use super::*; + // type alias required by `Filter` macro + type TriggerMetadataChanged = MetadataChanged; + data_event! { #[has_origin(origin = Trigger)] pub enum TriggerEvent { @@ -495,6 +498,10 @@ mod trigger { Extended(TriggerNumberOfExecutionsChanged), #[has_origin(number_of_executions_changed => &number_of_executions_changed.trigger_id)] Shortened(TriggerNumberOfExecutionsChanged), + #[has_origin(metadata_changed => &metadata_changed.target_id)] + MetadataInserted(TriggerMetadataChanged), + #[has_origin(metadata_changed => &metadata_changed.target_id)] + MetadataRemoved(TriggerMetadataChanged), } } diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index 66a2b706e38..2e9a73504e3 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -134,13 +134,15 @@ macro_rules! impl_instruction { impl_instruction! { SetKeyValue, - SetKeyValue, SetKeyValue, + SetKeyValue, SetKeyValue, + SetKeyValue, RemoveKeyValue, - RemoveKeyValue, RemoveKeyValue, + RemoveKeyValue, RemoveKeyValue, + RemoveKeyValue, Register, Register, Register, @@ -339,6 +341,21 @@ mod transparent { } } + impl SetKeyValue { + /// Constructs a new [`SetKeyValue`] for a [`Trigger`] with the given `key` and `value`. + pub fn trigger( + trigger_id: TriggerId, + key: Name, + value: impl Into, + ) -> Self { + Self { + object_id: trigger_id, + key, + value: value.into(), + } + } + } + impl_display! { SetKeyValue where @@ -353,7 +370,8 @@ mod transparent { SetKeyValue | SetKeyValue | SetKeyValue | - SetKeyValue + SetKeyValue | + SetKeyValue => SetKeyValueBox => InstructionBox[SetKeyValue], => SetKeyValueBoxRef<'a> => InstructionBoxRef<'a>[SetKeyValue] } @@ -410,6 +428,16 @@ mod transparent { } } + impl RemoveKeyValue { + /// Constructs a new [`RemoveKeyValue`] for an [`Asset`] with the given `key`. + pub fn trigger(trigger_id: TriggerId, key: Name) -> Self { + Self { + object_id: trigger_id, + key, + } + } + } + impl_display! { RemoveKeyValue where @@ -424,7 +452,8 @@ mod transparent { RemoveKeyValue | RemoveKeyValue | RemoveKeyValue | - RemoveKeyValue + RemoveKeyValue | + RemoveKeyValue => RemoveKeyValueBox => InstructionBox[RemoveKeyValue], => RemoveKeyValueBoxRef<'a> => InstructionBoxRef<'a>[RemoveKeyValue] } @@ -1059,6 +1088,8 @@ isi_box! { AssetDefinition(SetKeyValue), /// Set key value for [`Asset`]. Asset(SetKeyValue), + /// Set key value for [`Trigger`]. + Trigger(SetKeyValue), } } @@ -1078,6 +1109,8 @@ isi_box! { AssetDefinition(RemoveKeyValue), /// Remove key value from [`Asset`]. Asset(RemoveKeyValue), + /// Remove key value for [`Trigger`]. + Trigger(RemoveKeyValue), } } diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 7112685653b..bd6e9b2b13d 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -70,14 +70,16 @@ mod seal { InstructionBox, SetKeyValue, - SetKeyValue, SetKeyValue, + SetKeyValue, SetKeyValue, + SetKeyValue, RemoveKeyValue, - RemoveKeyValue, RemoveKeyValue, + RemoveKeyValue, RemoveKeyValue, + RemoveKeyValue, Register, Register, @@ -231,10 +233,11 @@ pub mod parameter { pub const BLOCK_TIME: &str = "BlockTime"; pub const COMMIT_TIME_LIMIT: &str = "CommitTimeLimit"; pub const TRANSACTION_LIMITS: &str = "TransactionLimits"; - pub const WSV_ASSET_METADATA_LIMITS: &str = "WSVAssetMetadataLimits"; + pub const WSV_DOMAIN_METADATA_LIMITS: &str = "WSVDomainMetadataLimits"; pub const WSV_ASSET_DEFINITION_METADATA_LIMITS: &str = "WSVAssetDefinitionMetadataLimits"; pub const WSV_ACCOUNT_METADATA_LIMITS: &str = "WSVAccountMetadataLimits"; - pub const WSV_DOMAIN_METADATA_LIMITS: &str = "WSVDomainMetadataLimits"; + pub const WSV_ASSET_METADATA_LIMITS: &str = "WSVAssetMetadataLimits"; + pub const WSV_TRIGGER_METADATA_LIMITS: &str = "WSVTriggerMetadataLimits"; pub const WSV_IDENT_LENGTH_LIMITS: &str = "WSVIdentLengthLimits"; pub const EXECUTOR_FUEL_LIMIT: &str = "ExecutorFuelLimit"; pub const EXECUTOR_MAX_MEMORY: &str = "ExecutorMaxMemory"; diff --git a/data_model/src/smart_contract.rs b/data_model/src/smart_contract.rs index 5d51c2f89d3..d7957b8ad4c 100644 --- a/data_model/src/smart_contract.rs +++ b/data_model/src/smart_contract.rs @@ -17,6 +17,8 @@ pub mod payloads { /// Payload for trigger entrypoint #[derive(Debug, Clone, Encode, Decode)] pub struct Trigger { + /// Id of this trigger + pub id: TriggerId, /// Trigger owner who registered the trigger pub owner: AccountId, /// Event which triggered the execution diff --git a/data_model/src/visit.rs b/data_model/src/visit.rs index b626e8bcde7..f25fc3dc9e0 100644 --- a/data_model/src/visit.rs +++ b/data_model/src/visit.rs @@ -128,12 +128,14 @@ pub trait Visit { visit_set_account_key_value(&SetKeyValue), visit_set_asset_definition_key_value(&SetKeyValue), visit_set_asset_key_value(&SetKeyValue), + visit_set_trigger_key_value(&SetKeyValue), // Visit RemoveKeyValueBox visit_remove_domain_key_value(&RemoveKeyValue), visit_remove_account_key_value(&RemoveKeyValue), visit_remove_asset_definition_key_value(&RemoveKeyValue), visit_remove_asset_key_value(&RemoveKeyValue), + visit_remove_trigger_key_value(&RemoveKeyValue), // Visit GrantBox visit_grant_account_permission(&Grant), @@ -350,6 +352,7 @@ pub fn visit_set_key_value( visitor.visit_set_asset_definition_key_value(authority, obj) } SetKeyValueBox::Asset(obj) => visitor.visit_set_asset_key_value(authority, obj), + SetKeyValueBox::Trigger(obj) => visitor.visit_set_trigger_key_value(authority, obj), } } @@ -365,6 +368,7 @@ pub fn visit_remove_key_value( visitor.visit_remove_asset_definition_key_value(authority, obj) } RemoveKeyValueBox::Asset(obj) => visitor.visit_remove_asset_key_value(authority, obj), + RemoveKeyValueBox::Trigger(obj) => visitor.visit_remove_trigger_key_value(authority, obj), } } @@ -409,6 +413,8 @@ leaf_visitors! { visit_transfer_asset_store(&Transfer), visit_set_asset_key_value(&SetKeyValue), visit_remove_asset_key_value(&RemoveKeyValue), + visit_set_trigger_key_value(&SetKeyValue), + visit_remove_trigger_key_value(&RemoveKeyValue), visit_register_asset_definition(&Register), visit_unregister_asset_definition(&Unregister), visit_transfer_asset_definition(&Transfer), diff --git a/docs/source/references/schema.json b/docs/source/references/schema.json index 5e5c102ebce..e9196b814df 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -2175,6 +2175,22 @@ } ] }, + "MetadataChanged": { + "Struct": [ + { + "name": "target_id", + "type": "TriggerId" + }, + { + "name": "key", + "type": "Name" + }, + { + "name": "value", + "type": "MetadataValueBox" + } + ] + }, "MetadataError": { "Enum": [ { @@ -3175,6 +3191,18 @@ } ] }, + "RemoveKeyValue": { + "Struct": [ + { + "name": "object_id", + "type": "TriggerId" + }, + { + "name": "key", + "type": "Name" + } + ] + }, "RemoveKeyValueBox": { "Enum": [ { @@ -3196,6 +3224,11 @@ "tag": "Asset", "discriminant": 3, "type": "RemoveKeyValue" + }, + { + "tag": "Trigger", + "discriminant": 4, + "type": "RemoveKeyValue" } ] }, @@ -3479,6 +3512,22 @@ } ] }, + "SetKeyValue": { + "Struct": [ + { + "name": "object_id", + "type": "TriggerId" + }, + { + "name": "key", + "type": "Name" + }, + { + "name": "value", + "type": "MetadataValueBox" + } + ] + }, "SetKeyValueBox": { "Enum": [ { @@ -3500,6 +3549,11 @@ "tag": "Asset", "discriminant": 3, "type": "SetKeyValue" + }, + { + "tag": "Trigger", + "discriminant": 4, + "type": "SetKeyValue" } ] }, @@ -4101,6 +4155,16 @@ "tag": "Shortened", "discriminant": 3, "type": "TriggerNumberOfExecutionsChanged" + }, + { + "tag": "MetadataInserted", + "discriminant": 4, + "type": "MetadataChanged" + }, + { + "tag": "MetadataRemoved", + "discriminant": 5, + "type": "MetadataChanged" } ] }, @@ -4135,6 +4199,14 @@ { "name": "Shortened", "mask": 8 + }, + { + "name": "MetadataInserted", + "mask": 16 + }, + { + "name": "MetadataRemoved", + "mask": 32 } ] } diff --git a/schema/gen/src/lib.rs b/schema/gen/src/lib.rs index 874dc171663..e3c56afd90c 100644 --- a/schema/gen/src/lib.rs +++ b/schema/gen/src/lib.rs @@ -213,6 +213,7 @@ types!( MetadataChanged, MetadataChanged, MetadataChanged, + MetadataChanged, MetadataError, MetadataLimits, MetadataValueBox, @@ -292,6 +293,7 @@ types!( RemoveKeyValue, RemoveKeyValue, RemoveKeyValue, + RemoveKeyValue, RemoveKeyValueBox, Repeats, RepetitionError, @@ -311,6 +313,7 @@ types!( SetKeyValue, SetKeyValue, SetKeyValue, + SetKeyValue, SetKeyValueBox, SetParameter, Signature, diff --git a/smart_contract/executor/derive/src/default.rs b/smart_contract/executor/derive/src/default.rs index 441bc8d7c87..cb2778b85e5 100644 --- a/smart_contract/executor/derive/src/default.rs +++ b/smart_contract/executor/derive/src/default.rs @@ -138,6 +138,8 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn::DeriveInput) -> Tok "fn visit_transfer_asset_store(operation: &Transfer)", "fn visit_set_asset_key_value(operation: &SetKeyValue)", "fn visit_remove_asset_key_value(operation: &RemoveKeyValue)", + "fn visit_set_trigger_key_value(operation: &SetKeyValue)", + "fn visit_remove_trigger_key_value(operation: &RemoveKeyValue)", "fn visit_register_asset_definition(operation: &Register)", "fn visit_unregister_asset_definition(operation: &Unregister)", "fn visit_transfer_asset_definition(operation: &Transfer)", diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index 50b951d85a8..f791727318f 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -37,7 +37,8 @@ pub use role::{ }; pub use trigger::{ visit_burn_trigger_repetitions, visit_execute_trigger, visit_mint_trigger_repetitions, - visit_register_trigger, visit_unregister_trigger, + visit_register_trigger, visit_remove_trigger_key_value, visit_set_trigger_key_value, + visit_unregister_trigger, }; use crate::{permission, permission::Token as _, prelude::*}; @@ -376,10 +377,10 @@ pub mod domain { AnyPermissionToken::CanMintUserSignatureCheckConditions(permission) => { permission.account_id.domain_id() == domain_id } - AnyPermissionToken::CanSetKeyValueInUserAccount(permission) => { + AnyPermissionToken::CanSetKeyValueInAccount(permission) => { permission.account_id.domain_id() == domain_id } - AnyPermissionToken::CanRemoveKeyValueInUserAccount(permission) => { + AnyPermissionToken::CanRemoveKeyValueInAccount(permission) => { permission.account_id.domain_id() == domain_id } AnyPermissionToken::CanUnregisterUserTrigger(permission) => { @@ -566,10 +567,9 @@ pub mod account { Ok(true) => execute!(executor, isi), Ok(false) => {} } - let can_set_key_value_in_user_account_token = - tokens::account::CanSetKeyValueInUserAccount { - account_id: account_id.clone(), - }; + let can_set_key_value_in_user_account_token = tokens::account::CanSetKeyValueInAccount { + account_id: account_id.clone(), + }; if can_set_key_value_in_user_account_token.is_owned_by(authority) { execute!(executor, isi); } @@ -596,7 +596,7 @@ pub mod account { Ok(false) => {} } let can_remove_key_value_in_user_account_token = - tokens::account::CanRemoveKeyValueInUserAccount { + tokens::account::CanRemoveKeyValueInAccount { account_id: account_id.clone(), }; if can_remove_key_value_in_user_account_token.is_owned_by(authority) { @@ -626,10 +626,10 @@ pub mod account { AnyPermissionToken::CanMintUserSignatureCheckConditions(permission) => { &permission.account_id == account_id } - AnyPermissionToken::CanSetKeyValueInUserAccount(permission) => { + AnyPermissionToken::CanSetKeyValueInAccount(permission) => { &permission.account_id == account_id } - AnyPermissionToken::CanRemoveKeyValueInUserAccount(permission) => { + AnyPermissionToken::CanRemoveKeyValueInAccount(permission) => { &permission.account_id == account_id } AnyPermissionToken::CanBurnUserAsset(permission) => { @@ -893,8 +893,8 @@ pub mod asset_definition { | AnyPermissionToken::CanMintUserPublicKeys(_) | AnyPermissionToken::CanBurnUserPublicKeys(_) | AnyPermissionToken::CanMintUserSignatureCheckConditions(_) - | AnyPermissionToken::CanSetKeyValueInUserAccount(_) - | AnyPermissionToken::CanRemoveKeyValueInUserAccount(_) + | AnyPermissionToken::CanSetKeyValueInAccount(_) + | AnyPermissionToken::CanRemoveKeyValueInAccount(_) | AnyPermissionToken::CanUnregisterUserTrigger(_) | AnyPermissionToken::CanExecuteUserTrigger(_) | AnyPermissionToken::CanBurnUserTrigger(_) @@ -1246,7 +1246,7 @@ pub mod role { let role_id = $isi.object(); let find_role_query_res = match FindRoleByRoleId::new(role_id.clone()).execute() { - Ok(res) => res.into_parts().0, + Ok(res) => res.into_inner(), Err(error) => { deny!($executor, error); } @@ -1394,7 +1394,7 @@ pub mod role { } pub mod trigger { - use iroha_smart_contract::data_model::{permission::PermissionToken, trigger::TriggerId}; + use iroha_smart_contract::data_model::{permission::PermissionToken, trigger::Trigger}; use permission::{accounts_permission_tokens, trigger::is_trigger_owner}; use tokens::AnyPermissionToken; @@ -1524,6 +1524,62 @@ pub mod trigger { deny!(executor, "Can't execute trigger owned by another account"); } + pub fn visit_set_trigger_key_value( + executor: &mut V, + authority: &AccountId, + isi: &SetKeyValue, + ) { + let trigger_id = isi.object_id(); + + if is_genesis(executor) { + execute!(executor, isi); + } + match is_trigger_owner(trigger_id, authority) { + Err(err) => deny!(executor, err), + Ok(true) => execute!(executor, isi), + Ok(false) => {} + } + let can_set_key_value_in_user_trigger_token = tokens::trigger::CanSetKeyValueInTrigger { + trigger_id: trigger_id.clone(), + }; + if can_set_key_value_in_user_trigger_token.is_owned_by(authority) { + execute!(executor, isi); + } + + deny!( + executor, + "Can't set value to the metadata of another trigger" + ); + } + + pub fn visit_remove_trigger_key_value( + executor: &mut V, + authority: &AccountId, + isi: &RemoveKeyValue, + ) { + let trigger_id = isi.object_id(); + + if is_genesis(executor) { + execute!(executor, isi); + } + match is_trigger_owner(trigger_id, authority) { + Err(err) => deny!(executor, err), + Ok(true) => execute!(executor, isi), + Ok(false) => {} + } + let can_remove_key_value_in_trigger_token = tokens::trigger::CanRemoveKeyValueInTrigger { + trigger_id: trigger_id.clone(), + }; + if can_remove_key_value_in_trigger_token.is_owned_by(authority) { + execute!(executor, isi); + } + + deny!( + executor, + "Can't remove value from the metadata of another trigger" + ); + } + fn is_token_trigger_associated(permission: &PermissionToken, trigger_id: &TriggerId) -> bool { let Ok(permission) = AnyPermissionToken::try_from(permission) else { return false; @@ -1551,8 +1607,8 @@ pub mod trigger { | AnyPermissionToken::CanMintUserPublicKeys(_) | AnyPermissionToken::CanBurnUserPublicKeys(_) | AnyPermissionToken::CanMintUserSignatureCheckConditions(_) - | AnyPermissionToken::CanSetKeyValueInUserAccount(_) - | AnyPermissionToken::CanRemoveKeyValueInUserAccount(_) + | AnyPermissionToken::CanSetKeyValueInAccount(_) + | AnyPermissionToken::CanRemoveKeyValueInAccount(_) | AnyPermissionToken::CanUnregisterAssetDefinition(_) | AnyPermissionToken::CanSetKeyValueInAssetDefinition(_) | AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(_) diff --git a/smart_contract/executor/src/default/tokens.rs b/smart_contract/executor/src/default/tokens.rs index 92289ffa755..3aaa5baf65f 100644 --- a/smart_contract/executor/src/default/tokens.rs +++ b/smart_contract/executor/src/default/tokens.rs @@ -105,8 +105,8 @@ declare_tokens! { crate::default::tokens::account::{CanMintUserPublicKeys}, crate::default::tokens::account::{CanBurnUserPublicKeys}, crate::default::tokens::account::{CanMintUserSignatureCheckConditions}, - crate::default::tokens::account::{CanSetKeyValueInUserAccount}, - crate::default::tokens::account::{CanRemoveKeyValueInUserAccount}, + crate::default::tokens::account::{CanSetKeyValueInAccount}, + crate::default::tokens::account::{CanRemoveKeyValueInAccount}, crate::default::tokens::asset_definition::{CanUnregisterAssetDefinition}, crate::default::tokens::asset_definition::{CanSetKeyValueInAssetDefinition}, @@ -229,14 +229,14 @@ pub mod account { token! { #[derive(ValidateGrantRevoke, permission::derive_conversions::account::Owner)] #[validate(permission::account::Owner)] - pub struct CanSetKeyValueInUserAccount { + pub struct CanSetKeyValueInAccount { pub account_id: AccountId, } } token! { #[derive(ValidateGrantRevoke, permission::derive_conversions::account::Owner)] #[validate(permission::account::Owner)] - pub struct CanRemoveKeyValueInUserAccount { + pub struct CanRemoveKeyValueInAccount { pub account_id: AccountId, } } @@ -507,11 +507,29 @@ pub mod trigger { } } + token! { + #[derive(ValidateGrantRevoke)] + #[validate(permission::trigger::Owner)] + pub struct CanSetKeyValueInTrigger { + pub trigger_id: TriggerId, + } + } + + token! { + #[derive(ValidateGrantRevoke)] + #[validate(permission::trigger::Owner)] + pub struct CanRemoveKeyValueInTrigger { + pub trigger_id: TriggerId, + } + } + impl_froms!( - CanExecuteUserTrigger, CanUnregisterUserTrigger, CanMintUserTrigger, CanBurnUserTrigger, + CanExecuteUserTrigger, + CanSetKeyValueInTrigger, + CanRemoveKeyValueInTrigger, ); } diff --git a/smart_contract/executor/src/permission.rs b/smart_contract/executor/src/permission.rs index 270048f2140..90c9b01cd43 100644 --- a/smart_contract/executor/src/permission.rs +++ b/smart_contract/executor/src/permission.rs @@ -139,8 +139,7 @@ pub mod asset_definition { ) -> Result { let asset_definition = FindAssetDefinitionById::new(asset_definition_id.clone()) .execute() - .map(QueryOutputCursor::into_parts) - .map(|(batch, _cursor)| batch)?; + .map(QueryOutputCursor::into_inner)?; if asset_definition.owned_by() == authority { Ok(true) } else { @@ -226,8 +225,7 @@ pub mod trigger { pub fn is_trigger_owner(trigger_id: &TriggerId, authority: &AccountId) -> Result { let trigger = FindTriggerById::new(trigger_id.clone()) .execute() - .map(QueryOutputCursor::into_parts) - .map(|(batch, _cursor)| batch)?; + .map(QueryOutputCursor::into_inner)?; if trigger.action().authority() == authority { Ok(true) } else { @@ -271,8 +269,7 @@ pub mod domain { pub fn is_domain_owner(domain_id: &DomainId, authority: &AccountId) -> Result { FindDomainById::new(domain_id.clone()) .execute() - .map(QueryOutputCursor::into_parts) - .map(|(batch, _cursor)| batch) + .map(QueryOutputCursor::into_inner) .map(|domain| domain.owned_by() == authority) } diff --git a/smart_contract/src/lib.rs b/smart_contract/src/lib.rs index 576f1915ccb..64563291dd7 100644 --- a/smart_contract/src/lib.rs +++ b/smart_contract/src/lib.rs @@ -290,9 +290,9 @@ pub struct QueryOutputCursor { } impl QueryOutputCursor { - /// Get inner values of batch and cursor, consuming [`Self`]. - pub fn into_parts(self) -> (T, ForwardCursor) { - (self.batch, self.cursor) + /// Returns the query result + pub fn into_inner(self) -> T { + self.batch } } @@ -524,7 +524,7 @@ mod tests { let response: Result, ValidationFail> = Ok(BatchedResponseV1::new( - QUERY_RESULT.unwrap().into_parts().0, + QUERY_RESULT.unwrap().collect().unwrap(), ForwardCursor::new(None, None), ) .into()); diff --git a/smart_contract/trigger/derive/src/entrypoint.rs b/smart_contract/trigger/derive/src/entrypoint.rs index a054363cdfe..29723cd3511 100644 --- a/smart_contract/trigger/derive/src/entrypoint.rs +++ b/smart_contract/trigger/derive/src/entrypoint.rs @@ -47,7 +47,7 @@ pub fn impl_entrypoint(emitter: &mut Emitter, item: syn::ItemFn) -> TokenStream #[doc(hidden)] unsafe extern "C" fn #main_fn_name() { let payload = ::iroha_trigger::get_trigger_payload(); - #fn_name(payload.owner, payload.event) + #fn_name(payload.id, payload.owner, payload.event) } // NOTE: Host objects are always passed by value to wasm diff --git a/tools/kagami/src/genesis.rs b/tools/kagami/src/genesis.rs index 94c851f0e91..1a7292df8b8 100644 --- a/tools/kagami/src/genesis.rs +++ b/tools/kagami/src/genesis.rs @@ -122,11 +122,11 @@ pub fn generate_default( let register_user_metadata_access = Register::role( Role::new("ALICE_METADATA_ACCESS".parse()?) .add_permission(PermissionToken::new( - "CanSetKeyValueInUserAccount".parse()?, + "CanSetKeyValueInAccount".parse()?, &json!({ "account_id": alice_id }), )) .add_permission(PermissionToken::new( - "CanRemoveKeyValueInUserAccount".parse()?, + "CanRemoveKeyValueInAccount".parse()?, &json!({ "account_id": alice_id }), )), ) @@ -143,13 +143,14 @@ pub fn generate_default( Numeric::new(DEFAULT_COMMIT_TIME.as_millis(), 0), )? .add_parameter(TRANSACTION_LIMITS, DEFAULT_TRANSACTION_LIMITS)? - .add_parameter(WSV_ASSET_METADATA_LIMITS, DEFAULT_METADATA_LIMITS)? + .add_parameter(WSV_DOMAIN_METADATA_LIMITS, DEFAULT_METADATA_LIMITS)? .add_parameter( WSV_ASSET_DEFINITION_METADATA_LIMITS, DEFAULT_METADATA_LIMITS, )? .add_parameter(WSV_ACCOUNT_METADATA_LIMITS, DEFAULT_METADATA_LIMITS)? - .add_parameter(WSV_DOMAIN_METADATA_LIMITS, DEFAULT_METADATA_LIMITS)? + .add_parameter(WSV_ASSET_METADATA_LIMITS, DEFAULT_METADATA_LIMITS)? + .add_parameter(WSV_TRIGGER_METADATA_LIMITS, DEFAULT_METADATA_LIMITS)? .add_parameter(WSV_IDENT_LENGTH_LIMITS, DEFAULT_IDENT_LENGTH_LIMITS)? .add_parameter( EXECUTOR_FUEL_LIMIT,