Skip to content

Commit

Permalink
[feature] #3871: Add Register<AssetDefinition>/Register<Account>/…
Browse files Browse the repository at this point in the history
…`Mint<Asset>` permissions

Signed-off-by: Ilia Churin <[email protected]>
Signed-off-by: Marin Veršić <[email protected]>
  • Loading branch information
ilchu authored and mversic committed Jan 18, 2024
1 parent cd4d6f1 commit 0020dcb
Show file tree
Hide file tree
Showing 23 changed files with 236 additions and 79 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/iroha2-dev-pr-static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ jobs:
run: cargo fmt --all -- --check
- name: Lints without features
if: always()
run: cargo clippy -Zlints --workspace --benches --tests --examples --no-default-features --quiet
run: cargo clippy --workspace --benches --tests --examples --no-default-features --quiet
- name: Lints with all features enabled
if: always()
run: cargo clippy -Zlints --workspace --benches --tests --examples --all-features --quiet
run: cargo clippy --workspace --benches --tests --examples --all-features --quiet
- name: Documentation
if: always()
run: cargo doc --no-deps --quiet
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ categories = ["cryptography::cryptocurrencies"]
iroha = { path = "cli" }
iroha_dsl = { version = "=2.0.0-pre-rc.20", path = "dsl" }
iroha_torii = { version = "=2.0.0-pre-rc.20", path = "torii" }
iroha_torii_macro = { version = "=2.0.0-pre-rc.20", path = "torii/macro" }
iroha_torii_derive = { version = "=2.0.0-pre-rc.20", path = "torii/derive" }
iroha_macro_utils = { version = "=2.0.0-pre-rc.20", path = "macro/utils" }
iroha_telemetry = { version = "=2.0.0-pre-rc.20", path = "telemetry" }
iroha_telemetry_derive = { version = "=2.0.0-pre-rc.20", path = "telemetry/derive" }
Expand Down Expand Up @@ -241,7 +241,7 @@ members = [
"tools/wasm_builder_cli",
"tools/wasm_test_runner",
"torii",
"torii/macro",
"torii/derive",
"version",
"version/derive",
"wasm_codec",
Expand Down
2 changes: 1 addition & 1 deletion client/tests/integration/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ fn find_rate_and_make_exchange_isi_should_succeed() {
};

let grant_alice_asset_transfer_permission = |asset_id: AssetId, owner_keypair: KeyPair| {
let allow_alice_to_transfer_asset = Grant::permission_token(
let allow_alice_to_transfer_asset = Grant::permission(
PermissionToken::new(
"CanTransferUserAsset".parse().unwrap(),
&json!({ "asset_id": asset_id }),
Expand Down
83 changes: 70 additions & 13 deletions client/tests/integration/domain_owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,72 @@ use test_network::*;

#[test]
fn domain_owner_domain_permissions() -> Result<()> {
let chain_id = ChainId::new("0");

let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(11_080).start_with_runtime();
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(Register::domain(kingdom))?;

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

// Asset definitions can't be registered by "bob@kingdom" by default
let transaction = TransactionBuilder::new(chain_id.clone(), bob_id.clone())
.with_instructions([Register::asset_definition(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(Register::asset_definition(coin.clone()))?;
test_client.submit_blocking(Unregister::asset_definition(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(Grant::permission(token.clone(), bob_id.clone()))?;
let transaction = TransactionBuilder::new(chain_id, bob_id.clone())
.with_instructions([Register::asset_definition(coin)])
.sign(bob_keypair)?;
test_client.submit_transaction_blocking(&transaction)?;
test_client.submit_blocking(Revoke::permission(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(SetKeyValue::domain(kingdom_id.clone(), key.clone(), value))?;
test_client.submit_blocking(RemoveKeyValue::domain(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 }),
);
test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission_token(token, bob_id))?;
test_client.submit_blocking(Grant::permission(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission(token, bob_id))?;

// check that "alice@wonderland" as owner of domain can unregister her domain
test_client.submit_blocking(Unregister::domain(kingdom_id))?;
Expand Down Expand Up @@ -90,8 +133,8 @@ fn domain_owner_account_permissions() -> Result<()> {
"CanUnregisterAccount".parse().unwrap(),
&json!({ "account_id": mad_hatter_id }),
);
test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission_token(token, bob_id))?;
test_client.submit_blocking(Grant::permission(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission(token, bob_id))?;

// check that "alice@wonderland" as owner of domain can unregister accounts in her domain
test_client.submit_blocking(Unregister::account(mad_hatter_id))?;
Expand All @@ -111,7 +154,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(Register::domain(kingdom))?;

let bob_keypair = KeyPair::generate()?;
Expand All @@ -121,6 +164,13 @@ fn domain_owner_asset_definition_permissions() -> Result<()> {
let rabbit = Account::new(rabbit_id.clone(), []);
test_client.submit_blocking(Register::account(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(Grant::permission(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(chain_id, bob_id.clone())
Expand Down Expand Up @@ -151,8 +201,8 @@ fn domain_owner_asset_definition_permissions() -> Result<()> {
"CanUnregisterAssetDefinition".parse().unwrap(),
&json!({ "asset_definition_id": coin_id }),
);
test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission_token(token, bob_id))?;
test_client.submit_blocking(Grant::permission(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission(token, bob_id))?;

// check that "alice@wonderland" as owner of domain can unregister asset definitions in her domain
test_client.submit_blocking(Unregister::asset_definition(coin_id))?;
Expand All @@ -174,13 +224,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(Register::domain(kingdom))?;

let bob_keypair = KeyPair::generate()?;
let bob = Account::new(bob_id.clone(), [bob_keypair.public_key().clone()]);
test_client.submit_blocking(Register::account(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(Grant::permission(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 Expand Up @@ -216,8 +273,8 @@ fn domain_owner_asset_permissions() -> Result<()> {
"CanUnregisterUserAsset".parse().unwrap(),
&json!({ "asset_id": bob_store_id }),
);
test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission_token(token, bob_id))?;
test_client.submit_blocking(Grant::permission(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission(token, bob_id))?;

Ok(())
}
Expand Down Expand Up @@ -273,8 +330,8 @@ fn domain_owner_trigger_permissions() -> Result<()> {
"CanUnregisterUserTrigger".parse().unwrap(),
&json!({ "trigger_id": trigger_id }),
);
test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission_token(token, bob_id))?;
test_client.submit_blocking(Grant::permission(token.clone(), bob_id.clone()))?;
test_client.submit_blocking(Revoke::permission(token, bob_id))?;

// check that "alice@wonderland" as owner of domain can unregister triggers in her domain
test_client.submit_blocking(Unregister::trigger(trigger_id))?;
Expand Down
12 changes: 6 additions & 6 deletions client/tests/integration/permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn genesis_transactions_are_validated() {

// Setting up genesis

let genesis = GenesisNetwork::test_with_instructions([Grant::permission_token(
let genesis = GenesisNetwork::test_with_instructions([Grant::permission(
PermissionToken::new("InvalidToken".parse().unwrap(), &json!(null)),
AccountId::from_str("alice@wonderland").unwrap(),
)
Expand Down Expand Up @@ -224,7 +224,7 @@ fn permissions_differ_not_only_by_names() {

// Granting permission to Alice to modify metadata in Mouse's hats
let mouse_hat_id = AssetId::new(hat_definition_id, mouse_id.clone());
let allow_alice_to_set_key_value_in_hats = Grant::permission_token(
let allow_alice_to_set_key_value_in_hats = Grant::permission(
PermissionToken::new(
"CanSetKeyValueInUserAsset".parse().unwrap(),
&json!({ "asset_id": mouse_hat_id }),
Expand Down Expand Up @@ -261,7 +261,7 @@ fn permissions_differ_not_only_by_names() {
.expect_err("Expected Alice to fail to modify Mouse's shoes");

// Granting permission to Alice to modify metadata in Mouse's shoes
let allow_alice_to_set_key_value_in_shoes = Grant::permission_token(
let allow_alice_to_set_key_value_in_shoes = Grant::permission(
PermissionToken::new(
"CanSetKeyValueInUserAsset".parse().unwrap(),
&json!({ "asset_id": mouse_shoes_id }),
Expand Down Expand Up @@ -312,7 +312,7 @@ fn stored_vs_granted_token_payload() -> Result<()> {

// Allow alice to mint mouse asset and mint initial value
let mouse_asset = AssetId::new(asset_definition_id, mouse_id.clone());
let allow_alice_to_set_key_value_in_mouse_asset = Grant::permission_token(
let allow_alice_to_set_key_value_in_mouse_asset = Grant::permission(
PermissionToken::from_str_unchecked(
"CanSetKeyValueInUserAsset".parse().unwrap(),
// NOTE: Introduced additional whitespaces in the serialized form
Expand Down Expand Up @@ -347,7 +347,7 @@ fn permission_tokens_are_unified() {
// Given
let alice_id = AccountId::from_str("alice@wonderland").expect("Valid");

let allow_alice_to_transfer_rose_1 = Grant::permission_token(
let allow_alice_to_transfer_rose_1 = Grant::permission(
PermissionToken::from_str_unchecked(
"CanTransferUserAsset".parse().unwrap(),
// NOTE: Introduced additional whitespaces in the serialized form
Expand All @@ -356,7 +356,7 @@ fn permission_tokens_are_unified() {
alice_id.clone(),
);

let allow_alice_to_transfer_rose_2 = Grant::permission_token(
let allow_alice_to_transfer_rose_2 = Grant::permission(
PermissionToken::from_str_unchecked(
"CanTransferUserAsset".parse().unwrap(),
// NOTE: Introduced additional whitespaces in the serialized form
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,18 @@ fn main(_owner: AccountId, _event: Event) {

let limits = MetadataLimits::new(256, 256);

let bad_domain_ids: [DomainId; 2] = [
"genesis".parse().dbg_unwrap(),
"garden_of_live_flowers".parse().dbg_unwrap(),
];

for account in accounts_cursor {
let account = account.dbg_unwrap();

if bad_domain_ids.contains(account.id().domain_id()) {
continue;
}

let mut metadata = Metadata::new();
let name = format!(
"nft_for_{}_in_{}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ impl Executor {
accounts
.iter()
.try_for_each(|(account, domain_id)| {
Revoke::permission_token(
Revoke::permission(
PermissionToken::new(
can_unregister_domain_definition_id.clone(),
&json!({ "domain_id": domain_id }),
Expand All @@ -137,7 +137,7 @@ impl Executor {
)
})?;

Grant::permission_token(
Grant::permission(
PermissionToken::new(
can_control_domain_lives_definition_id.clone(),
&json!(null),
Expand Down
2 changes: 1 addition & 1 deletion client_cli/pytests/common/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Stderr(Enum):
CANNOT_BE_EMPTY = 'cannot be empty\n\nFor more information, try \'--help\'.\n'
REPETITION = 'Repetition'
TOO_LONG = 'Name length violation'
FAILED_TO_FIND_DOMAIN = 'Entity missing'
FAILED_TO_FIND_DOMAIN = 'Failed to find domain'
INVALID_CHARACTER = 'Invalid character'
INVALID_VALUE_TYPE = 'Matching variant not found'
RESERVED_CHARACTER = 'The `@` character is reserved for `account@domain` constructs,' \
Expand Down
2 changes: 1 addition & 1 deletion client_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ mod account {
permission,
metadata,
} = self;
let grant = iroha_client::data_model::isi::Grant::permission_token(permission.0, id);
let grant = iroha_client::data_model::isi::Grant::permission(permission.0, id);
submit([grant], metadata.load()?, context)
.wrap_err("Failed to grant the permission to the account")
}
Expand Down
Binary file modified configs/peer/executor.wasm
Binary file not shown.
9 changes: 9 additions & 0 deletions configs/peer/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@
}
}
},
{
"Transfer": {
"Domain": {
"source_id": "genesis@genesis",
"object": "wonderland",
"destination_id": "alice@wonderland"
}
}
},
{
"Grant": {
"PermissionToken": {
Expand Down
6 changes: 3 additions & 3 deletions core/benches/blocks/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub fn populate_wsv(
let domain_id = construct_domain_id(i);
let domain = Domain::new(domain_id.clone());
instructions.push(Register::domain(domain).into());
let can_unregister_domain = Grant::permission_token(
let can_unregister_domain = Grant::permission(
PermissionToken::new(
"CanUnregisterDomain".parse().unwrap(),
&json!({ "domain_id": domain_id.clone() }),
Expand All @@ -78,7 +78,7 @@ pub fn populate_wsv(
let account_id = construct_account_id(j, domain_id.clone());
let account = Account::new(account_id.clone(), []);
instructions.push(Register::account(account).into());
let can_unregister_account = Grant::permission_token(
let can_unregister_account = Grant::permission(
PermissionToken::new(
"CanUnregisterAccount".parse().unwrap(),
&json!({ "account_id": account_id.clone() }),
Expand All @@ -94,7 +94,7 @@ pub fn populate_wsv(
iroha_data_model::asset::AssetValueType::Quantity,
);
instructions.push(Register::asset_definition(asset_definition).into());
let can_unregister_asset_definition = Grant::permission_token(
let can_unregister_asset_definition = Grant::permission(
PermissionToken::new(
"CanUnregisterAssetDefinition".parse().unwrap(),
&json!({ "asset_definition_id": asset_definition_id }),
Expand Down
6 changes: 3 additions & 3 deletions core/test_network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ impl TestGenesis for GenesisNetwork {
let alice_id = AccountId::from_str("alice@wonderland").expect("valid names");

let mint_rose_permission = PermissionToken::new(
"CanMintAssetsWithDefinition".parse().unwrap(),
"CanMintAssetWithDefinition".parse().unwrap(),
&json!({ "asset_definition_id": rose_definition_id }),
);
let burn_rose_permission = PermissionToken::new(
"CanBurnAssetsWithDefinition".parse().unwrap(),
"CanBurnAssetWithDefinition".parse().unwrap(),
&json!({ "asset_definition_id": rose_definition_id }),
);
let unregister_any_peer_permission =
Expand All @@ -124,7 +124,7 @@ impl TestGenesis for GenesisNetwork {
upgrade_executor_permission,
] {
first_transaction
.append_instruction(Grant::permission_token(permission, alice_id.clone()).into());
.append_instruction(Grant::permission(permission, alice_id.clone()).into());
}

for isi in extra_isi.into_iter() {
Expand Down
Loading

0 comments on commit 0020dcb

Please sign in to comment.