diff --git a/configs/peer/executor.wasm b/configs/peer/executor.wasm index 194481b0e0d..27d59840121 100644 Binary files a/configs/peer/executor.wasm and b/configs/peer/executor.wasm differ diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index 0c7eafa30f2..b4448c6e0c5 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -168,7 +168,9 @@ pub mod peer { } pub mod domain { - use permission::domain::is_domain_owner; + use iroha_smart_contract::data_model::{domain::DomainId, permission::PermissionToken}; + use permission::{accounts_permission_tokens, domain::is_domain_owner}; + use tokens::AnyPermissionToken; use super::*; @@ -187,21 +189,28 @@ pub mod domain { ) { let domain_id = isi.object_id(); - if is_genesis(executor) { - execute!(executor, isi); - } - match is_domain_owner(domain_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_unregister_domain_token = tokens::domain::CanUnregisterDomain { - domain_id: domain_id.clone(), - }; - if can_unregister_domain_token.is_owned_by(authority) { + if is_genesis(executor) + || match is_domain_owner(domain_id, authority) { + Err(err) => deny!(executor, err), + Ok(is_domain_owner) => is_domain_owner, + } + || { + let can_unregister_domain_token = tokens::domain::CanUnregisterDomain { + domain_id: domain_id.clone(), + }; + can_unregister_domain_token.is_owned_by(authority) + } + { + for (owner_id, permission) in accounts_permission_tokens() { + if is_token_domain_associated(&permission, domain_id) { + let isi = Revoke::permission_token(permission, owner_id.clone()); + if let Err(_err) = isi.execute() { + deny!(executor, "Can't revoke associated permission token"); + } + } + } execute!(executor, isi); } - deny!(executor, "Can't unregister domain"); } @@ -273,10 +282,110 @@ pub mod domain { deny!(executor, "Can't remove key value in domain metadata"); } + + fn is_token_domain_associated(permission: &PermissionToken, domain_id: &DomainId) -> bool { + let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { return false; }; + match permission { + AnyPermissionToken::CanUnregisterDomain(permission) => { + &permission.domain_id == domain_id + } + AnyPermissionToken::CanSetKeyValueInDomain(permission) => { + &permission.domain_id == domain_id + } + AnyPermissionToken::CanRemoveKeyValueInDomain(permission) => { + &permission.domain_id == domain_id + } + AnyPermissionToken::CanUnregisterAssetDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanSetKeyValueInAssetDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanRegisterAssetsWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanUnregisterAssetsWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanBurnAssetsWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanMintAssetsWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanTransferAssetsWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanBurnUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanTransferUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanUnregisterUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanSetKeyValueInUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanUnregisterAccount(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanMintUserPublicKeys(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanBurnUserPublicKeys(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanMintUserSignatureCheckConditions(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanSetKeyValueInUserAccount(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAccount(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanUnregisterUserTrigger(permission) => { + permission.trigger_id.domain_id().as_ref() == Some(domain_id) + } + AnyPermissionToken::CanExecuteUserTrigger(permission) => { + permission.trigger_id.domain_id().as_ref() == Some(domain_id) + } + AnyPermissionToken::CanBurnUserTrigger(permission) => { + permission.trigger_id.domain_id().as_ref() == Some(domain_id) + } + AnyPermissionToken::CanMintUserTrigger(permission) => { + permission.trigger_id.domain_id().as_ref() == Some(domain_id) + } + AnyPermissionToken::CanUnregisterAnyPeer(_) + | AnyPermissionToken::CanGrantPermissionToCreateParameters(_) + | AnyPermissionToken::CanRevokePermissionToCreateParameters(_) + | AnyPermissionToken::CanCreateParameters(_) + | AnyPermissionToken::CanGrantPermissionToSetParameters(_) + | AnyPermissionToken::CanRevokePermissionToSetParameters(_) + | AnyPermissionToken::CanSetParameters(_) + | AnyPermissionToken::CanUnregisterAnyRole(_) + | AnyPermissionToken::CanUpgradeExecutor(_) => false, + } + } } pub mod account { - use permission::account::is_account_owner; + use iroha_smart_contract::data_model::permission::PermissionToken; + use permission::{account::is_account_owner, accounts_permission_tokens}; + use tokens::AnyPermissionToken; use super::*; @@ -295,21 +404,28 @@ pub mod account { ) { let account_id = isi.object_id(); - if is_genesis(executor) { - execute!(executor, isi); - } - match is_account_owner(account_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_unregister_user_account = tokens::account::CanUnregisterAccount { - account_id: account_id.clone(), - }; - if can_unregister_user_account.is_owned_by(authority) { + if is_genesis(executor) + || match is_account_owner(account_id, authority) { + Err(err) => deny!(executor, err), + Ok(is_account_owner) => is_account_owner, + } + || { + let can_unregister_user_account = tokens::account::CanUnregisterAccount { + account_id: account_id.clone(), + }; + can_unregister_user_account.is_owned_by(authority) + } + { + for (owner_id, permission) in accounts_permission_tokens() { + if is_token_account_associated(&permission, account_id) { + let isi = Revoke::permission_token(permission, owner_id.clone()); + if let Err(_err) = isi.execute() { + deny!(executor, "Can't revoke associated permission token"); + } + } + } execute!(executor, isi); } - deny!(executor, "Can't unregister another account"); } @@ -449,10 +565,78 @@ pub mod account { "Can't remove value from the metadata of another account" ); } + + fn is_token_account_associated(permission: &PermissionToken, account_id: &AccountId) -> bool { + let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { return false; }; + match permission { + AnyPermissionToken::CanUnregisterAccount(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanMintUserPublicKeys(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanBurnUserPublicKeys(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanMintUserSignatureCheckConditions(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanSetKeyValueInUserAccount(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAccount(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanBurnUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanTransferUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanUnregisterUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanSetKeyValueInUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanUnregisterUserTrigger(_) + | AnyPermissionToken::CanExecuteUserTrigger(_) + | AnyPermissionToken::CanBurnUserTrigger(_) + | AnyPermissionToken::CanMintUserTrigger(_) + | AnyPermissionToken::CanUnregisterAnyPeer(_) + | AnyPermissionToken::CanUnregisterDomain(_) + | AnyPermissionToken::CanSetKeyValueInDomain(_) + | AnyPermissionToken::CanRemoveKeyValueInDomain(_) + | AnyPermissionToken::CanUnregisterAssetDefinition(_) + | AnyPermissionToken::CanSetKeyValueInAssetDefinition(_) + | AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(_) + | AnyPermissionToken::CanRegisterAssetsWithDefinition(_) + | AnyPermissionToken::CanUnregisterAssetsWithDefinition(_) + | AnyPermissionToken::CanBurnAssetsWithDefinition(_) + | AnyPermissionToken::CanMintAssetsWithDefinition(_) + | AnyPermissionToken::CanTransferAssetsWithDefinition(_) + | AnyPermissionToken::CanGrantPermissionToCreateParameters(_) + | AnyPermissionToken::CanRevokePermissionToCreateParameters(_) + | AnyPermissionToken::CanCreateParameters(_) + | AnyPermissionToken::CanGrantPermissionToSetParameters(_) + | AnyPermissionToken::CanRevokePermissionToSetParameters(_) + | AnyPermissionToken::CanSetParameters(_) + | AnyPermissionToken::CanUnregisterAnyRole(_) + | AnyPermissionToken::CanUpgradeExecutor(_) => false, + } + } } pub mod asset_definition { - use permission::{account::is_account_owner, asset_definition::is_asset_definition_owner}; + use iroha_smart_contract::data_model::{asset::AssetDefinitionId, permission::PermissionToken}; + use permission::{ + account::is_account_owner, accounts_permission_tokens, + asset_definition::is_asset_definition_owner, + }; + use tokens::AnyPermissionToken; use super::*; @@ -471,22 +655,29 @@ pub mod asset_definition { ) { let asset_definition_id = isi.object_id(); - if is_genesis(executor) { - execute!(executor, isi); - } - match is_asset_definition_owner(asset_definition_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_unregister_asset_definition_token = - tokens::asset_definition::CanUnregisterAssetDefinition { - asset_definition_id: asset_definition_id.clone(), - }; - if can_unregister_asset_definition_token.is_owned_by(authority) { + if is_genesis(executor) + || match is_asset_definition_owner(asset_definition_id, authority) { + Err(err) => deny!(executor, err), + Ok(is_asset_definition_owner) => is_asset_definition_owner, + } + || { + let can_unregister_asset_definition_token = + tokens::asset_definition::CanUnregisterAssetDefinition { + asset_definition_id: asset_definition_id.clone(), + }; + can_unregister_asset_definition_token.is_owned_by(authority) + } + { + for (owner_id, permission) in accounts_permission_tokens() { + if is_token_asset_definition_associated(&permission, asset_definition_id) { + let isi = Revoke::permission_token(permission, owner_id.clone()); + if let Err(_err) = isi.execute() { + deny!(executor, "Can't revoke associated permission token"); + } + } + } execute!(executor, isi); } - deny!( executor, "Can't unregister assets registered by other accounts" @@ -578,6 +769,76 @@ pub mod asset_definition { "Can't remove value from the asset definition metadata created by another account" ); } + + fn is_token_asset_definition_associated( + permission: &PermissionToken, + asset_definition_id: &AssetDefinitionId, + ) -> bool { + let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { return false; }; + match permission { + AnyPermissionToken::CanUnregisterAssetDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanSetKeyValueInAssetDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanRegisterAssetsWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanUnregisterAssetsWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanBurnAssetsWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanMintAssetsWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanTransferAssetsWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanBurnUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanTransferUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanUnregisterUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanSetKeyValueInUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanUnregisterAccount(_) + | AnyPermissionToken::CanMintUserPublicKeys(_) + | AnyPermissionToken::CanBurnUserPublicKeys(_) + | AnyPermissionToken::CanMintUserSignatureCheckConditions(_) + | AnyPermissionToken::CanSetKeyValueInUserAccount(_) + | AnyPermissionToken::CanRemoveKeyValueInUserAccount(_) + | AnyPermissionToken::CanUnregisterUserTrigger(_) + | AnyPermissionToken::CanExecuteUserTrigger(_) + | AnyPermissionToken::CanBurnUserTrigger(_) + | AnyPermissionToken::CanMintUserTrigger(_) + | AnyPermissionToken::CanUnregisterAnyPeer(_) + | AnyPermissionToken::CanUnregisterDomain(_) + | AnyPermissionToken::CanSetKeyValueInDomain(_) + | AnyPermissionToken::CanRemoveKeyValueInDomain(_) + | AnyPermissionToken::CanGrantPermissionToCreateParameters(_) + | AnyPermissionToken::CanRevokePermissionToCreateParameters(_) + | AnyPermissionToken::CanCreateParameters(_) + | AnyPermissionToken::CanGrantPermissionToSetParameters(_) + | AnyPermissionToken::CanRevokePermissionToSetParameters(_) + | AnyPermissionToken::CanSetParameters(_) + | AnyPermissionToken::CanUnregisterAnyRole(_) + | AnyPermissionToken::CanUpgradeExecutor(_) => false, + } + } } pub mod asset { @@ -1048,7 +1309,9 @@ pub mod role { } pub mod trigger { - use permission::trigger::is_trigger_owner; + use iroha_smart_contract::data_model::{permission::PermissionToken, trigger::TriggerId}; + use permission::{accounts_permission_tokens, trigger::is_trigger_owner}; + use tokens::AnyPermissionToken; use super::*; @@ -1067,21 +1330,28 @@ pub mod trigger { ) { 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_unregister_user_trigger_token = tokens::trigger::CanUnregisterUserTrigger { - trigger_id: trigger_id.clone(), - }; - if can_unregister_user_trigger_token.is_owned_by(authority) { + if is_genesis(executor) + || match is_trigger_owner(trigger_id, authority) { + Err(err) => deny!(executor, err), + Ok(is_trigger_owner) => is_trigger_owner, + } + || { + let can_unregister_user_trigger_token = tokens::trigger::CanUnregisterUserTrigger { + trigger_id: trigger_id.clone(), + }; + can_unregister_user_trigger_token.is_owned_by(authority) + } + { + for (owner_id, permission) in accounts_permission_tokens() { + if is_token_trigger_associated(&permission, trigger_id) { + let isi = Revoke::permission_token(permission, owner_id.clone()); + if let Err(_err) = isi.execute() { + deny!(executor, "Can't revoke associated permission token"); + } + } + } execute!(executor, isi); } - deny!( executor, "Can't unregister trigger owned by another account" @@ -1168,6 +1438,55 @@ pub mod trigger { deny!(executor, "Can't execute trigger owned by another account"); } + + fn is_token_trigger_associated(permission: &PermissionToken, trigger_id: &TriggerId) -> bool { + let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { return false; }; + match permission { + AnyPermissionToken::CanUnregisterUserTrigger(permission) => { + &permission.trigger_id == trigger_id + } + AnyPermissionToken::CanExecuteUserTrigger(permission) => { + &permission.trigger_id == trigger_id + } + AnyPermissionToken::CanBurnUserTrigger(permission) => { + &permission.trigger_id == trigger_id + } + AnyPermissionToken::CanMintUserTrigger(permission) => { + &permission.trigger_id == trigger_id + } + AnyPermissionToken::CanUnregisterAnyPeer(_) + | AnyPermissionToken::CanUnregisterDomain(_) + | AnyPermissionToken::CanSetKeyValueInDomain(_) + | AnyPermissionToken::CanRemoveKeyValueInDomain(_) + | AnyPermissionToken::CanUnregisterAccount(_) + | AnyPermissionToken::CanMintUserPublicKeys(_) + | AnyPermissionToken::CanBurnUserPublicKeys(_) + | AnyPermissionToken::CanMintUserSignatureCheckConditions(_) + | AnyPermissionToken::CanSetKeyValueInUserAccount(_) + | AnyPermissionToken::CanRemoveKeyValueInUserAccount(_) + | AnyPermissionToken::CanUnregisterAssetDefinition(_) + | AnyPermissionToken::CanSetKeyValueInAssetDefinition(_) + | AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(_) + | AnyPermissionToken::CanRegisterAssetsWithDefinition(_) + | AnyPermissionToken::CanUnregisterAssetsWithDefinition(_) + | AnyPermissionToken::CanUnregisterUserAsset(_) + | AnyPermissionToken::CanBurnAssetsWithDefinition(_) + | AnyPermissionToken::CanBurnUserAsset(_) + | AnyPermissionToken::CanMintAssetsWithDefinition(_) + | AnyPermissionToken::CanTransferAssetsWithDefinition(_) + | AnyPermissionToken::CanTransferUserAsset(_) + | AnyPermissionToken::CanSetKeyValueInUserAsset(_) + | AnyPermissionToken::CanRemoveKeyValueInUserAsset(_) + | AnyPermissionToken::CanGrantPermissionToCreateParameters(_) + | AnyPermissionToken::CanRevokePermissionToCreateParameters(_) + | AnyPermissionToken::CanCreateParameters(_) + | AnyPermissionToken::CanGrantPermissionToSetParameters(_) + | AnyPermissionToken::CanRevokePermissionToSetParameters(_) + | AnyPermissionToken::CanSetParameters(_) + | AnyPermissionToken::CanUnregisterAnyRole(_) + | AnyPermissionToken::CanUpgradeExecutor(_) => false, + } + } } pub mod permission_token { diff --git a/smart_contract/executor/src/default/tokens.rs b/smart_contract/executor/src/default/tokens.rs index 97382cc7806..cc60b8c8ed3 100644 --- a/smart_contract/executor/src/default/tokens.rs +++ b/smart_contract/executor/src/default/tokens.rs @@ -49,6 +49,28 @@ macro_rules! declare_tokens { }; } + /// Enum with every default token + #[allow(clippy::enum_variant_names)] + pub(crate) enum AnyPermissionToken { + $( + $token_ty($($token_path::)+$token_ty), + )* + } + + impl TryFrom<::iroha_smart_contract::data_model::permission::PermissionToken> for AnyPermissionToken { + type Error = $crate::permission::PermissionTokenConversionError; + + fn try_from(token: ::iroha_smart_contract::data_model::permission::PermissionToken) -> Result { + match token.definition_id().as_ref() { $( + stringify!($token_ty) => { + let token = <$($token_path::)+$token_ty>::try_from(token)?; + Ok(Self::$token_ty(token)) + } )+ + _ => Err(Self::Error::Id(token.definition_id().clone())) + } + } + } + pub(crate) use map_token; pub(crate) use map_token_type; }; diff --git a/smart_contract/executor/src/permission.rs b/smart_contract/executor/src/permission.rs index e08040fe76a..d733d0c57c9 100644 --- a/smart_contract/executor/src/permission.rs +++ b/smart_contract/executor/src/permission.rs @@ -3,7 +3,7 @@ use alloc::borrow::ToOwned as _; use iroha_schema::IntoSchema; -use iroha_smart_contract::QueryOutputCursor; +use iroha_smart_contract::{data_model::permission::PermissionToken, QueryOutputCursor}; use iroha_smart_contract_utils::debug::DebugExpectExt as _; use serde::{de::DeserializeOwned, Serialize}; @@ -338,3 +338,20 @@ impl From<&T> for OnlyGenesis { Self } } + +/// Iterator over all accounts and theirs permission tokens +pub(crate) fn accounts_permission_tokens() -> impl Iterator { + FindAllAccounts + .execute() + .dbg_expect("failed to query all accounts") + .into_iter() + .map(|account| account.dbg_expect("failed to retrieve account")) + .flat_map(|account| { + FindPermissionTokensByAccountId::new(account.id().clone()) + .execute() + .dbg_expect("failed to query permssion token for account") + .into_iter() + .map(|token| token.dbg_expect("failed to retrieve permission token")) + .map(move |token| (account.id().clone(), token)) + }) +}