Skip to content

Commit

Permalink
[feature] #3871: Register<AssetDefinition> permissions
Browse files Browse the repository at this point in the history
Signed-off-by: Ilia Churin <[email protected]>
  • Loading branch information
ilchu committed Nov 17, 2023
1 parent 3ebb484 commit e65d631
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 7 deletions.
61 changes: 58 additions & 3 deletions client/tests/integration/domain_owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,60 @@ fn domain_owner_domain_permissions() -> Result<()> {
wait_for_genesis_committed(&[test_client.clone()], 0);

let kingdom_id: DomainId = "kingdom".parse()?;
let bob_id: AccountId = "bob@kingdom".parse()?;
let coin_id: AssetDefinitionId = "coin#kingdom".parse()?;
let coin = AssetDefinition::quantity(coin_id.clone());

// "alice@wonderland" is owner of "kingdom" domain
let kingdom = Domain::new(kingdom_id.clone());
test_client.submit_blocking(RegisterExpr::new(kingdom))?;

let bob_keypair = KeyPair::generate()?;
let bob = Account::new(bob_id.clone(), [bob_keypair.public_key().clone()]);
test_client.submit_blocking(RegisterExpr::new(bob))?;

// Asset definitions can't be registered by "bob@kingdom" by default
let transaction = TransactionBuilder::new(bob_id.clone())
.with_instructions([RegisterExpr::new(coin.clone())])
.sign(bob_keypair.clone())?;
let err = test_client
.submit_transaction_blocking(&transaction)
.expect_err("Tx should fail due to permissions");

let rejection_reason = err
.downcast_ref::<PipelineRejectionReason>()
.unwrap_or_else(|| panic!("Error {err} is not PipelineRejectionReason"));

assert!(matches!(
rejection_reason,
&PipelineRejectionReason::Transaction(TransactionRejectionReason::Validation(
ValidationFail::NotPermitted(_)
))
));

// "alice@wonderland" owns the domain and can register AssetDefinitions by default as domain owner
test_client.submit_blocking(RegisterExpr::new(coin.clone()))?;
test_client.submit_blocking(UnregisterExpr::new(coin_id))?;

// Granting a respective token also allows "bob@kingdom" to do so
let token = PermissionToken::new(
"CanRegisterAssetDefinitionInDomain".parse().unwrap(),
&json!({ "domain_id": kingdom_id }),
);
test_client.submit_blocking(GrantExpr::new(token.clone(), bob_id.clone()))?;
let transaction = TransactionBuilder::new(bob_id.clone())
.with_instructions([RegisterExpr::new(coin)])
.sign(bob_keypair)?;
test_client.submit_transaction_blocking(&transaction)?;
test_client.submit_blocking(RevokeExpr::new(token, bob_id.clone()))?;

// check that "alice@wonderland" as owner of domain can edit metadata in her domain
let key: Name = "key".parse()?;
let value: Name = "value".parse()?;
test_client.submit_blocking(SetKeyValueExpr::new(kingdom_id.clone(), key.clone(), value))?;
test_client.submit_blocking(RemoveKeyValueExpr::new(kingdom_id.clone(), key))?;

// check that "alice@wonderland" as owner of domain can grant and revoke domain related permission tokens
let bob_id: AccountId = "bob@wonderland".parse()?;
let token = PermissionToken::new(
"CanUnregisterDomain".parse().unwrap(),
&json!({ "domain_id": kingdom_id }),
Expand Down Expand Up @@ -108,7 +149,7 @@ fn domain_owner_asset_definition_permissions() -> Result<()> {
let coin_id: AssetDefinitionId = "coin#kingdom".parse()?;

// "alice@wonderland" is owner of "kingdom" domain
let kingdom = Domain::new(kingdom_id);
let kingdom = Domain::new(kingdom_id.clone());
test_client.submit_blocking(RegisterExpr::new(kingdom))?;

let bob_keypair = KeyPair::generate()?;
Expand All @@ -118,6 +159,13 @@ fn domain_owner_asset_definition_permissions() -> Result<()> {
let rabbit = Account::new(rabbit_id.clone(), []);
test_client.submit_blocking(RegisterExpr::new(rabbit))?;

// Grant permission to register asset definitions to "bob@kingdom"
let token = PermissionToken::new(
"CanRegisterAssetDefinitionInDomain".parse().unwrap(),
&json!({ "domain_id": kingdom_id }),
);
test_client.submit_blocking(GrantExpr::new(token, bob_id.clone()))?;

// register asset definitions by "bob@kingdom" so he is owner of it
let coin = AssetDefinition::quantity(coin_id.clone());
let transaction = TransactionBuilder::new(bob_id.clone())
Expand Down Expand Up @@ -161,13 +209,20 @@ fn domain_owner_asset_permissions() -> Result<()> {
let store_id: AssetDefinitionId = "store#kingdom".parse()?;

// "alice@wonderland" is owner of "kingdom" domain
let kingdom = Domain::new(kingdom_id);
let kingdom = Domain::new(kingdom_id.clone());
test_client.submit_blocking(RegisterExpr::new(kingdom))?;

let bob_keypair = KeyPair::generate()?;
let bob = Account::new(bob_id.clone(), [bob_keypair.public_key().clone()]);
test_client.submit_blocking(RegisterExpr::new(bob))?;

// Grant permission to register asset definitions to "bob@kingdom"
let token = PermissionToken::new(
"CanRegisterAssetDefinitionInDomain".parse().unwrap(),
&json!({ "domain_id": kingdom_id }),
);
test_client.submit_blocking(GrantExpr::new(token, bob_id.clone()))?;

// register asset definitions by "bob@kingdom" so he is owner of it
let coin = AssetDefinition::quantity(coin_id.clone());
let store = AssetDefinition::store(store_id.clone());
Expand Down
Binary file modified configs/peer/executor.wasm
Binary file not shown.
6 changes: 6 additions & 0 deletions core/test_network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ impl TestGenesis for GenesisNetwork {
);
let upgrade_executor_permission =
PermissionToken::new("CanUpgradeExecutor".parse().unwrap(), &json!(null));
let register_asset_definitions_permission =
PermissionToken::new(
"CanRegisterAssetDefinitionInDomain".parse().unwrap(),
&json!({ "domain_id": DomainId::from_str("wonderland").unwrap() } ),
);

let first_transaction = genesis
.first_transaction_mut()
Expand All @@ -116,6 +121,7 @@ impl TestGenesis for GenesisNetwork {
unregister_any_role_permission,
unregister_wonderland_domain,
upgrade_executor_permission,
register_asset_definitions_permission
] {
first_transaction
.append_instruction(GrantExpr::new(permission, alice_id.clone()).into());
Expand Down
2 changes: 1 addition & 1 deletion smart_contract/executor/derive/src/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn2::DeriveInput) -> To
"fn visit_transfer_asset(operation: Transfer<Asset, NumericValue, Account>)",
"fn visit_set_asset_key_value(operation: SetKeyValue<Asset>)",
"fn visit_remove_asset_key_value(operation: RemoveKeyValue<Asset>)",
"fn visit_register_asset_definition(operation: Register<AssetDefinition>)",
"fn visit_unregister_asset_definition(operation: Unregister<AssetDefinition>)",
"fn visit_transfer_asset_definition(operation: Transfer<Account, AssetDefinitionId, Account>)",
"fn visit_set_asset_definition_key_value(operation: SetKeyValue<AssetDefinition>)",
Expand Down Expand Up @@ -198,7 +199,6 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn2::DeriveInput) -> To
})
.collect::<Vec<_>>();

println!("{}", quote!(#(#visit_items)*));
quote! {
impl ::iroha_executor::prelude::Visit for #ident {
#(#visit_items)*
Expand Down
1 change: 1 addition & 0 deletions smart_contract/executor/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ pub fn derive_token(input: TokenStream) -> TokenStream {
/// - `asset_definition::Owner` - checks if the authority is the asset definition owner;
/// - `asset::Owner` - checks if the authority is the asset owner;
/// - `account::Owner` - checks if the authority is the account owner.
/// - `domain::Owner` - checks if the authority is the domain owner.
/// - `AlwaysPass` - checks nothing and always passes.
/// - `OnlyGenesis` - checks that block height is 0.
///
Expand Down
39 changes: 36 additions & 3 deletions smart_contract/executor/src/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ pub use asset::{
visit_set_asset_key_value, visit_transfer_asset, visit_unregister_asset,
};
pub use asset_definition::{
visit_remove_asset_definition_key_value, visit_set_asset_definition_key_value,
visit_transfer_asset_definition, visit_unregister_asset_definition,
visit_register_asset_definition, visit_remove_asset_definition_key_value,
visit_set_asset_definition_key_value, visit_transfer_asset_definition,
visit_unregister_asset_definition,
};
pub use domain::{
visit_remove_domain_key_value, visit_set_domain_key_value, visit_transfer_domain,
Expand Down Expand Up @@ -563,10 +564,42 @@ pub mod account {
}

pub mod asset_definition {
use permission::{account::is_account_owner, asset_definition::is_asset_definition_owner};
use permission::{
account::is_account_owner, asset_definition::is_asset_definition_owner,
domain::is_domain_owner,
};

use super::*;

pub fn visit_register_asset_definition<V: Validate + ?Sized>(
executor: &mut V,
authority: &AccountId,
isi: Register<AssetDefinition>,
) {
let domain_id = isi.object.id().domain_id();

if is_genesis(executor) {
pass!(executor);
}
match is_domain_owner(domain_id, authority) {
Err(err) => deny!(executor, err),
Ok(true) => pass!(executor),
Ok(false) => {}
}
let can_register_asset_definition_token =
tokens::domain::CanRegisterAssetDefinitionInDomain {
domain_id: domain_id.clone(),
};
if can_register_asset_definition_token.is_owned_by(authority) {
pass!(executor);
}

deny!(
executor,
"New `AssetDefinition` can only be registered by domain owner or if granted `CanRegisterAssetDefinitionInDomain` token"
);
}

pub fn visit_unregister_asset_definition<V: Validate + ?Sized>(
executor: &mut V,
authority: &AccountId,
Expand Down
9 changes: 9 additions & 0 deletions smart_contract/executor/src/default/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ declare_tokens! {
crate::default::tokens::domain::{CanUnregisterDomain},
crate::default::tokens::domain::{CanSetKeyValueInDomain},
crate::default::tokens::domain::{CanRemoveKeyValueInDomain},
crate::default::tokens::domain::{CanRegisterAssetDefinitionInDomain},

crate::default::tokens::account::{CanUnregisterAccount},
crate::default::tokens::account::{CanMintUserPublicKeys},
Expand Down Expand Up @@ -145,6 +146,14 @@ pub mod domain {
pub domain_id: DomainId,
}
}

token! {
#[derive(ValidateGrantRevoke, permission::derive_conversions::domain::Owner)]
#[validate(permission::domain::Owner)]
pub struct CanRegisterAssetDefinitionInDomain {
pub domain_id: DomainId,
}
}
}

pub mod account {
Expand Down

0 comments on commit e65d631

Please sign in to comment.