Skip to content

Commit

Permalink
[feature] hyperledger-iroha#3871: Register<AssetDefinition> permiss…
Browse files Browse the repository at this point in the history
…ions (hyperledger-iroha#4049)

[feature] hyperledger-iroha#3871: Add `Register<AssetDefinition>`/`Register<Account>`/`Mint<Asset>` permissions

Signed-off-by: Ilia Churin <[email protected]>
Signed-off-by: Marin Veršić <[email protected]>
  • Loading branch information
ilchu authored Jan 19, 2024
1 parent 565271d commit cb5972f
Show file tree
Hide file tree
Showing 37 changed files with 345 additions and 158 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
8 changes: 4 additions & 4 deletions client/benches/torii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ fn query_requests(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("query-requests");
let domain_id: DomainId = "domain".parse().expect("Valid");
let create_domain = Register::domain(Domain::new(domain_id.clone()));
let account_id = AccountId::new("account".parse().expect("Valid"), domain_id.clone());
let account_id = AccountId::new(domain_id.clone(), "account".parse().expect("Valid"));
let (public_key, _) = KeyPair::generate()
.expect("Failed to generate KeyPair")
.into();
let create_account = Register::account(Account::new(account_id.clone(), [public_key]));
let asset_definition_id = AssetDefinitionId::new("xor".parse().expect("Valid"), domain_id);
let asset_definition_id = AssetDefinitionId::new(domain_id, "xor".parse().expect("Valid"));
let create_asset =
Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone()));
let quantity: u32 = 200;
Expand Down Expand Up @@ -168,12 +168,12 @@ fn instruction_submits(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("instruction-requests");
let domain_id: DomainId = "domain".parse().expect("Valid");
let create_domain: InstructionBox = Register::domain(Domain::new(domain_id.clone())).into();
let account_id = AccountId::new("account".parse().expect("Valid"), domain_id.clone());
let account_id = AccountId::new(domain_id.clone(), "account".parse().expect("Valid"));
let (public_key, _) = KeyPair::generate()
.expect("Failed to generate Key-pair.")
.into();
let create_account = Register::account(Account::new(account_id.clone(), [public_key])).into();
let asset_definition_id = AssetDefinitionId::new("xor".parse().expect("Valid"), domain_id);
let asset_definition_id = AssetDefinitionId::new(domain_id, "xor".parse().expect("Valid"));
let mut client_config =
iroha_client::samples::get_client_config(get_chain_id(), &get_key_pair());
client_config.torii_api_url = format!("http://{}", peer.api_address).parse().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion client/examples/million_accounts_genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ fn create_million_accounts_directly() {
for i in 0_u32..1_000_000_u32 {
let domain_id: DomainId = format!("wonderland-{i}").parse().expect("Valid");
let normal_account_id = AccountId::new(
format!("bob-{i}").parse().expect("Valid"),
domain_id.clone(),
format!("bob-{i}").parse().expect("Valid"),
);
let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into();
let create_account = Register::account(Account::new(normal_account_id.clone(), [])).into();
Expand Down
10 changes: 5 additions & 5 deletions 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 Expand Up @@ -426,16 +426,16 @@ fn transfer_asset_definition() {

fn account_id_new(account_name: &str, account_domain: &str) -> AccountId {
AccountId::new(
account_name.parse().expect("Valid"),
account_domain.parse().expect("Valid"),
account_name.parse().expect("Valid"),
)
}

fn asset_id_new(definition_name: &str, definition_domain: &str, account_id: AccountId) -> AssetId {
AssetId::new(
AssetDefinitionId::new(
definition_name.parse().expect("Valid"),
definition_domain.parse().expect("Valid"),
definition_name.parse().expect("Valid"),
),
account_id,
)
Expand All @@ -451,17 +451,17 @@ mod register {
pub fn account(account_name: &str, domain_name: &str) -> Register<Account> {
Register::account(Account::new(
AccountId::new(
account_name.parse().expect("Valid"),
domain_name.parse().expect("Valid"),
account_name.parse().expect("Valid"),
),
[],
))
}

pub fn asset_definition(asset_name: &str, domain_name: &str) -> Register<AssetDefinition> {
Register::asset_definition(AssetDefinition::quantity(AssetDefinitionId::new(
asset_name.parse().expect("Valid"),
domain_name.parse().expect("Valid"),
asset_name.parse().expect("Valid"),
)))
}
}
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
46 changes: 27 additions & 19 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 @@ -201,30 +201,38 @@ fn permissions_differ_not_only_by_names() {
let (_rt, _not_drop, client) = <PeerBuilder>::new().with_port(10_745).start_with_runtime();

let alice_id: AccountId = "alice@wonderland".parse().expect("Valid");
let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid");
let mouse_id: AccountId = "mouse@outfit".parse().expect("Valid");
let mouse_keypair = KeyPair::generate().expect("Failed to generate KeyPair.");

// Registering `Store` asset definitions
let hat_definition_id: AssetDefinitionId = "hat#wonderland".parse().expect("Valid");
let new_hat_definition = AssetDefinition::store(hat_definition_id.clone());
let shoes_definition_id: AssetDefinitionId = "shoes#wonderland".parse().expect("Valid");
let new_shoes_definition = AssetDefinition::store(shoes_definition_id.clone());
// Registering mouse
let outfit_domain: DomainId = "outfit".parse().unwrap();
let create_outfit_domain = Register::domain(Domain::new(outfit_domain.clone()));
let new_mouse_account = Account::new(mouse_id.clone(), [mouse_keypair.public_key().clone()]);
client
.submit_all_blocking([
Register::asset_definition(new_hat_definition),
Register::asset_definition(new_shoes_definition),
InstructionBox::from(create_outfit_domain),
Register::account(new_mouse_account).into(),
])
.expect("Failed to register new asset definitions");
.expect("Failed to register mouse");

// Registering mouse
let new_mouse_account = Account::new(mouse_id.clone(), [mouse_keypair.public_key().clone()]);
// Registering `Store` asset definitions
let hat_definition_id: AssetDefinitionId = "hat#outfit".parse().expect("Valid");
let new_hat_definition = AssetDefinition::store(hat_definition_id.clone());
let transfer_shoes_domain = Transfer::domain(alice_id.clone(), outfit_domain, mouse_id.clone());
let shoes_definition_id: AssetDefinitionId = "shoes#outfit".parse().expect("Valid");
let new_shoes_definition = AssetDefinition::store(shoes_definition_id.clone());
let instructions: [InstructionBox; 3] = [
Register::asset_definition(new_hat_definition).into(),
Register::asset_definition(new_shoes_definition).into(),
transfer_shoes_domain.into(),
];
client
.submit_blocking(Register::account(new_mouse_account))
.expect("Failed to register mouse");
.submit_all_blocking(instructions)
.expect("Failed to register new asset definitions");

// 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 +269,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 +320,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 +355,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 +364,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
Loading

0 comments on commit cb5972f

Please sign in to comment.