From 45ab900bfd8e74ef3e18716882e21bcfaa7baa1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marin=20Ver=C5=A1i=C4=87?= Date: Thu, 22 Jun 2023 15:08:09 +0200 Subject: [PATCH] [feature] #3624: introduce general purpose permission tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marin Veršić --- Cargo.lock | 2 + client/benches/tps/utils.rs | 7 +- client/tests/integration/asset.rs | 3 +- client/tests/integration/events/data.rs | 23 +- client/tests/integration/permissions.rs | 59 +- client/tests/integration/queries/role.rs | 15 +- client/tests/integration/roles.rs | 27 +- .../integration/smartcontracts/Cargo.toml | 3 + .../smartcontracts/mint_rose/src/lib.rs | 6 +- .../validator_with_admin/Cargo.toml | 2 + .../validator_with_admin/src/lib.rs | 10 +- .../validator_with_custom_token/Cargo.toml | 3 + .../validator_with_custom_token/src/lib.rs | 28 +- .../integration/triggers/by_call_trigger.rs | 27 +- client/tests/integration/upgrade.rs | 14 +- configs/peer/genesis.json | 56 +- core/src/lib.rs | 4 - core/src/smartcontracts/isi/account.rs | 25 +- core/src/smartcontracts/isi/mod.rs | 8 +- core/src/smartcontracts/isi/permissions.rs | 69 --- core/src/smartcontracts/isi/world.rs | 148 +++-- core/src/smartcontracts/wasm.rs | 13 +- core/src/validator.rs | 8 +- core/src/wsv.rs | 7 +- core/test_network/Cargo.toml | 1 + core/test_network/src/lib.rs | 28 +- data_model/derive/src/lib.rs | 42 -- data_model/derive/src/variant_discriminant.rs | 107 ---- data_model/src/events/data/events.rs | 59 +- data_model/src/lib.rs | 193 +++---- data_model/src/permission.rs | 197 ++----- data_model/src/predicate.rs | 2 +- data_model/src/query.rs | 52 +- data_model/src/role.rs | 2 +- data_model/src/visit.rs | 2 +- default_validator/src/lib.rs | 6 +- docs/source/references/schema.json | 206 ++----- schema/gen/src/lib.rs | 9 +- schema/src/lib.rs | 14 +- tools/kagami/Cargo.toml | 1 + tools/kagami/src/genesis.rs | 27 +- .../parity_scale_decoder/samples/trigger.bin | Bin 80 -> 80 bytes tools/parity_scale_decoder/src/main.rs | 2 +- wasm/derive/src/lib.rs | 4 +- wasm/src/lib.rs | 11 +- wasm/validator/Cargo.toml | 4 + wasm/validator/derive/src/entrypoint.rs | 41 +- wasm/validator/derive/src/lib.rs | 6 +- wasm/validator/derive/src/token.rs | 181 +----- wasm/validator/derive/src/validate.rs | 5 +- wasm/validator/src/default.rs | 513 ++++++++++-------- wasm/validator/src/lib.rs | 41 +- wasm/validator/src/permission.rs | 49 +- 53 files changed, 904 insertions(+), 1468 deletions(-) delete mode 100644 core/src/smartcontracts/isi/permissions.rs delete mode 100644 data_model/derive/src/variant_discriminant.rs diff --git a/Cargo.lock b/Cargo.lock index 4bdc31f56c0..d7e06c1707a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3484,6 +3484,7 @@ dependencies = [ "iroha_schema_gen", "iroha_wasm_builder", "owo-colors", + "parity-scale-codec", "path-absolutize", "pathdiff", "serde", @@ -5335,6 +5336,7 @@ dependencies = [ "iroha_genesis", "iroha_logger", "iroha_primitives", + "parity-scale-codec", "rand 0.8.5", "tempfile", "tokio", diff --git a/client/benches/tps/utils.rs b/client/benches/tps/utils.rs index bf7fb9a2515..926beed715c 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -181,11 +181,10 @@ impl MeasurerUnit { )); self.client.submit_blocking(register_me)?; - let can_burn_my_asset = PermissionToken::new("can_burn_user_asset".parse()?) - .with_params([("asset_id".parse()?, asset_id.clone().into())]); + let can_burn_my_asset = PermissionToken::new("CanBurnUserAsset".to_owned(), &asset_id); let allow_alice_to_burn_my_asset = GrantBox::new(can_burn_my_asset, alice_id.clone()); - let can_transfer_my_asset = PermissionToken::new("can_transfer_user_asset".parse()?) - .with_params([("asset_id".parse()?, asset_id.clone().into())]); + let can_transfer_my_asset = + PermissionToken::new("CanTransferUserAsset".to_owned(), &asset_id); let allow_alice_to_transfer_my_asset = GrantBox::new(can_transfer_my_asset, alice_id); let grant_tx = TransactionBuilder::new(account_id) .with_instructions([ diff --git a/client/tests/integration/asset.rs b/client/tests/integration/asset.rs index 0e4f179eb49..770c5cef299 100644 --- a/client/tests/integration/asset.rs +++ b/client/tests/integration/asset.rs @@ -259,8 +259,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 = GrantBox::new( - PermissionToken::new("can_transfer_user_asset".parse().expect("Valid")) - .with_params([("asset_id".parse().expect("Valid"), asset_id.clone().into())]), + PermissionToken::new("CanTransferUserAsset".to_owned(), &asset_id), alice_id.clone(), ); diff --git a/client/tests/integration/events/data.rs b/client/tests/integration/events/data.rs index 4796c614660..3512a27175b 100644 --- a/client/tests/integration/events/data.rs +++ b/client/tests/integration/events/data.rs @@ -3,7 +3,7 @@ use std::{fmt::Write as _, str::FromStr, sync::mpsc, thread}; use eyre::Result; use iroha_data_model::{prelude::*, transaction::WasmSmartContract}; -use parity_scale_codec::Encode; +use parity_scale_codec::Encode as _; use test_network::*; use crate::wasm::utils::wasm_template; @@ -163,17 +163,8 @@ fn produce_multiple_events() -> Result<()> { // Registering role let alice_id = ::Id::from_str("alice@wonderland")?; let role_id = ::Id::from_str("TEST_ROLE")?; - let token_1 = PermissionToken::new( - "can_remove_key_value_in_user_account" - .parse() - .expect("valid"), - ) - .with_params([( - "account_id".parse().expect("valid"), - alice_id.clone().into(), - )]); - let token_2 = PermissionToken::new("can_set_key_value_in_user_account".parse().expect("valid")) - .with_params([("account_id".parse().expect("valid"), alice_id.into())]); + let token_1 = PermissionToken::new("CanRemoveKeyValueInUserAccount".to_owned(), &alice_id); + let token_2 = PermissionToken::new("CanSetKeyValueInUserAccount".to_owned(), &alice_id); let role = iroha_data_model::role::Role::new(role_id.clone()) .add_permission(token_1.clone()) .add_permission(token_2.clone()); @@ -207,13 +198,13 @@ fn produce_multiple_events() -> Result<()> { WorldEvent::Domain(DomainEvent::Account(AccountEvent::PermissionAdded( AccountPermissionChanged { account_id: bob_id.clone(), - permission_id: token_1.definition_id().clone(), + permission_id: token_1.definition_id.clone(), }, ))), WorldEvent::Domain(DomainEvent::Account(AccountEvent::PermissionAdded( AccountPermissionChanged { account_id: bob_id.clone(), - permission_id: token_2.definition_id().clone(), + permission_id: token_2.definition_id.clone(), }, ))), WorldEvent::Domain(DomainEvent::Account(AccountEvent::RoleGranted( @@ -225,13 +216,13 @@ fn produce_multiple_events() -> Result<()> { WorldEvent::Domain(DomainEvent::Account(AccountEvent::PermissionRemoved( AccountPermissionChanged { account_id: bob_id.clone(), - permission_id: token_1.definition_id().clone(), + permission_id: token_1.definition_id, }, ))), WorldEvent::Domain(DomainEvent::Account(AccountEvent::PermissionRemoved( AccountPermissionChanged { account_id: bob_id.clone(), - permission_id: token_2.definition_id().clone(), + permission_id: token_2.definition_id, }, ))), WorldEvent::Domain(DomainEvent::Account(AccountEvent::RoleRevoked( diff --git a/client/tests/integration/permissions.rs b/client/tests/integration/permissions.rs index f38d880b9c8..de498f01ead 100644 --- a/client/tests/integration/permissions.rs +++ b/client/tests/integration/permissions.rs @@ -181,11 +181,7 @@ fn permissions_differ_not_only_by_names() { // Granting permission to Alice to modify metadata in Mouse's hats let mouse_hat_id = ::Id::new(hat_definition_id, mouse_id.clone()); let allow_alice_to_set_key_value_in_hats = GrantBox::new( - PermissionToken::new("can_set_key_value_in_user_asset".parse().expect("Valid")) - .with_params([( - "asset_id".parse().expect("Valid"), - mouse_hat_id.clone().into(), - )]), + PermissionToken::new("CanSetKeyValueInUserAsset".to_owned(), &mouse_hat_id), alice_id.clone(), ); @@ -219,8 +215,7 @@ fn permissions_differ_not_only_by_names() { // Granting permission to Alice to modify metadata in Mouse's shoes let allow_alice_to_set_key_value_in_shoes = GrantBox::new( - PermissionToken::new("can_set_key_value_in_user_asset".parse().expect("Valid")) - .with_params([("asset_id".parse().expect("Valid"), mouse_shoes_id.into())]), + PermissionToken::new("CanSetKeyValueInUserAsset".to_owned(), &mouse_shoes_id), alice_id, ); @@ -242,23 +237,13 @@ fn permissions_differ_not_only_by_names() { mod token_parameters { use super::*; - static TEST_TOKEN_DEFINITION_ID: once_cell::sync::Lazy< - ::Id, - > = once_cell::sync::Lazy::new(|| { - ::Id::new( - "test_permission_token_definition".parse().expect("Valid"), - ) - }); - - static NUMBER_PARAMETER_NAME: once_cell::sync::Lazy = - once_cell::sync::Lazy::new(|| "number".parse().expect("Valid")); - static STRING_PARAMETER_NAME: once_cell::sync::Lazy = - once_cell::sync::Lazy::new(|| "string".parse().expect("Valid")); + static TEST_TOKEN_DEFINITION_ID: once_cell::sync::Lazy = + once_cell::sync::Lazy::new(|| "TestPermissionTokenDefinition".parse().expect("Valid")); #[ignore = "ignore, more in #2851"] #[test] fn token_with_missing_parameters_is_not_accepted() { - let token = PermissionToken::new(TEST_TOKEN_DEFINITION_ID.clone()); + let token = PermissionToken::new(TEST_TOKEN_DEFINITION_ID.clone(), &()); let expect = "Expected to fail to grant permission token without parameters"; run_grant_token_error_test(token.clone(), expect); @@ -268,8 +253,7 @@ mod token_parameters { #[ignore = "ignore, more in #2851"] #[test] fn token_with_one_missing_parameter_is_not_accepted() { - let token = PermissionToken::new(TEST_TOKEN_DEFINITION_ID.clone()) - .with_params([(NUMBER_PARAMETER_NAME.clone(), 1_u32.into())]); + let token = PermissionToken::new(TEST_TOKEN_DEFINITION_ID.clone(), &1_u32); let expect = "Expected to fail to grant permission token with one missing parameter"; run_grant_token_error_test(token.clone(), expect); @@ -279,13 +263,7 @@ mod token_parameters { #[ignore = "ignore, more in #2851"] #[test] fn token_with_changed_parameter_name_is_not_accepted() { - let token = PermissionToken::new(TEST_TOKEN_DEFINITION_ID.clone()).with_params([ - (NUMBER_PARAMETER_NAME.clone(), 1_u32.into()), - ( - "it's_a_trap".parse().expect("Valid"), - "test".to_owned().into(), - ), - ]); + let token = PermissionToken::new(TEST_TOKEN_DEFINITION_ID.clone(), &(1_u32, "test")); let expect = "Expected to fail to grant permission token with one changed parameter"; run_grant_token_error_test(token.clone(), expect); @@ -295,14 +273,10 @@ mod token_parameters { #[ignore = "ignore, more in #2851"] #[test] fn token_with_extra_parameter_is_not_accepted() { - let token = PermissionToken::new(TEST_TOKEN_DEFINITION_ID.clone()).with_params([ - (NUMBER_PARAMETER_NAME.clone(), 1_u32.into()), - (STRING_PARAMETER_NAME.clone(), "test".to_owned().into()), - ( - "extra_param".parse().expect("Valid"), - "extra_test".to_owned().into(), - ), - ]); + let token = PermissionToken::new( + TEST_TOKEN_DEFINITION_ID.clone(), + &(1_u32, "test", "extra_test"), + ); let expect = "Expected to fail to grant permission token with extra parameter"; run_grant_token_error_test(token.clone(), expect); @@ -312,13 +286,10 @@ mod token_parameters { #[ignore = "ignore, more in #2851"] #[test] fn token_with_wrong_parameter_type_is_not_accepted() { - let token = PermissionToken::new(TEST_TOKEN_DEFINITION_ID.clone()).with_params([ - (NUMBER_PARAMETER_NAME.clone(), 1_u32.into()), - ( - STRING_PARAMETER_NAME.clone(), - Value::Name("test".parse().expect("Valid")), - ), - ]); + let token = PermissionToken::new( + TEST_TOKEN_DEFINITION_ID.clone(), + &(91_u32, Value::Name("test".parse().expect("Valid"))), + ); let expect = "Expected to fail to grant permission token with wrong parameter type"; run_grant_token_error_test(token.clone(), expect); diff --git a/client/tests/integration/queries/role.rs b/client/tests/integration/queries/role.rs index d3d83bb663e..d848cf03336 100644 --- a/client/tests/integration/queries/role.rs +++ b/client/tests/integration/queries/role.rs @@ -124,17 +124,10 @@ fn find_roles_by_account_id() -> Result<()> { .iter() .cloned() .map(|role_id| { - RegisterBox::new( - Role::new(role_id).add_permission( - PermissionToken::new( - "can_set_key_value_in_user_account".parse().expect("Valid"), - ) - .with_params([( - "account_id".parse().expect("Valid"), - alice_id.clone().into(), - )]), - ), - ) + RegisterBox::new(Role::new(role_id).add_permission(PermissionToken::new( + "CanSetKeyValueInUserAccount".to_owned(), + &alice_id, + ))) }) .collect::>(); test_client.submit_all_blocking(register_roles)?; diff --git a/client/tests/integration/roles.rs b/client/tests/integration/roles.rs index b6be99ddb07..1d603ac12c6 100644 --- a/client/tests/integration/roles.rs +++ b/client/tests/integration/roles.rs @@ -25,7 +25,7 @@ fn register_role_with_empty_token_params() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let role_id = "root".parse().expect("Valid"); - let token = PermissionToken::new("token".parse().expect("Valid")); + let token = PermissionToken::new("token".to_owned(), &()); let role = Role::new(role_id).add_permission(token); test_client.submit(RegisterBox::new(role))?; @@ -61,14 +61,14 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { // Registering role let role_id = ::Id::from_str("ACCESS_TO_MOUSE_METADATA")?; let role = Role::new(role_id.clone()) - .add_permission( - PermissionToken::new("can_set_key_value_in_user_account".parse()?) - .with_params([("account_id".parse()?, mouse_id.clone().into())]), - ) - .add_permission( - PermissionToken::new("can_remove_key_value_in_user_account".parse()?) - .with_params([("account_id".parse()?, mouse_id.clone().into())]), - ); + .add_permission(PermissionToken::new( + "CanSetKeyValueInUserAccount".to_owned(), + &mouse_id, + )) + .add_permission(PermissionToken::new( + "CanRemoveKeyValueInUserAccount".to_owned(), + &mouse_id, + )); let register_role = RegisterBox::new(role); test_client.submit_blocking(register_role)?; @@ -108,12 +108,9 @@ fn unregistered_role_removed_from_account() -> Result<()> { test_client.submit_blocking(register_mouse)?; // Register root role - let register_role = RegisterBox::new( - Role::new(role_id.clone()).add_permission( - PermissionToken::new("can_set_key_value_in_user_account".parse()?) - .with_params([("account_id".parse()?, alice_id.into())]), - ), - ); + let register_role = RegisterBox::new(Role::new(role_id.clone()).add_permission( + PermissionToken::new("CanSetKeyValueInUserAccount".to_owned(), &alice_id), + )); test_client.submit_blocking(register_role)?; // Grant root role to Mouse diff --git a/client/tests/integration/smartcontracts/Cargo.toml b/client/tests/integration/smartcontracts/Cargo.toml index 2caf550b8e5..1e1426115c2 100644 --- a/client/tests/integration/smartcontracts/Cargo.toml +++ b/client/tests/integration/smartcontracts/Cargo.toml @@ -28,4 +28,7 @@ codegen-units = 1 # Further reduces binary size but increases compilation time [workspace.dependencies] iroha_validator = { version = "=2.0.0-pre-rc.16", path = "../../../../wasm/validator", features = ["default-validator"] } iroha_wasm = { version = "=2.0.0-pre-rc.16", path = "../../../../wasm", features = ["debug"]} +iroha_schema = { version = "=2.0.0-pre-rc.16", path = "../../../../schema" } + +parity-scale-codec = { version = "3.2.1", default-features = false } panic-halt = "0.2.0" diff --git a/client/tests/integration/smartcontracts/mint_rose/src/lib.rs b/client/tests/integration/smartcontracts/mint_rose/src/lib.rs index 990a448dcce..57de67375ab 100644 --- a/client/tests/integration/smartcontracts/mint_rose/src/lib.rs +++ b/client/tests/integration/smartcontracts/mint_rose/src/lib.rs @@ -13,10 +13,10 @@ use iroha_wasm::data_model::prelude::*; /// Mint 1 rose for authority #[iroha_wasm::main(params = "[authority]")] -fn main(authority: ::Id) { - let rose_definition_id = ::Id::from_str("rose#wonderland") +fn main(authority: AccountId) { + let rose_definition_id = AssetDefinitionId::from_str("rose#wonderland") .dbg_expect("Failed to parse `rose#wonderland` asset definition id"); - let rose_id = ::Id::new(rose_definition_id, authority); + let rose_id = AssetId::new(rose_definition_id, authority); MintBox::new(1_u32, rose_id) .execute() diff --git a/client/tests/integration/smartcontracts/validator_with_admin/Cargo.toml b/client/tests/integration/smartcontracts/validator_with_admin/Cargo.toml index 7f843fa2d94..1b465081974 100644 --- a/client/tests/integration/smartcontracts/validator_with_admin/Cargo.toml +++ b/client/tests/integration/smartcontracts/validator_with_admin/Cargo.toml @@ -12,4 +12,6 @@ crate-type = ['cdylib'] [dependencies] iroha_validator.workspace = true +iroha_schema.workspace = true + panic-halt.workspace = true diff --git a/client/tests/integration/smartcontracts/validator_with_admin/src/lib.rs b/client/tests/integration/smartcontracts/validator_with_admin/src/lib.rs index 38a350adea1..8662eb2434a 100644 --- a/client/tests/integration/smartcontracts/validator_with_admin/src/lib.rs +++ b/client/tests/integration/smartcontracts/validator_with_admin/src/lib.rs @@ -2,8 +2,6 @@ //! If authority is not `admin@admin` then [`DefaultValidator`] is used as a backup. #![no_std] -extern crate alloc; - use iroha_validator::{ data_model::evaluate::{EvaluationError, ExpressionEvaluator}, parse, @@ -92,8 +90,8 @@ impl Visit for CustomValidator { } impl Validate for CustomValidator { - fn permission_tokens() -> Vec { - DefaultValidator::permission_tokens() + fn permission_token_schema() -> PermissionTokenSchema { + DefaultValidator::permission_token_schema() } fn verdict(&self) -> &Result { @@ -116,8 +114,8 @@ impl ExpressionEvaluator for CustomValidator { /// Entrypoint to return permission token definitions defined in this validator. #[entrypoint] -pub fn permission_tokens() -> Vec { - CustomValidator::permission_tokens() +pub fn permission_token_schema() -> PermissionTokenSchema { + CustomValidator::permission_token_schema() } /// Allow operation if authority is `admin@admin` and if not, diff --git a/client/tests/integration/smartcontracts/validator_with_custom_token/Cargo.toml b/client/tests/integration/smartcontracts/validator_with_custom_token/Cargo.toml index b6a91ff9a35..b7a8a9e4129 100644 --- a/client/tests/integration/smartcontracts/validator_with_custom_token/Cargo.toml +++ b/client/tests/integration/smartcontracts/validator_with_custom_token/Cargo.toml @@ -12,4 +12,7 @@ crate-type = ['cdylib'] [dependencies] iroha_validator.workspace = true +iroha_schema.workspace = true + +parity-scale-codec.workspace = true panic-halt.workspace = true diff --git a/client/tests/integration/smartcontracts/validator_with_custom_token/src/lib.rs b/client/tests/integration/smartcontracts/validator_with_custom_token/src/lib.rs index 1d22d367b81..ff6fe830bae 100644 --- a/client/tests/integration/smartcontracts/validator_with_custom_token/src/lib.rs +++ b/client/tests/integration/smartcontracts/validator_with_custom_token/src/lib.rs @@ -7,11 +7,16 @@ extern crate alloc; +use alloc::{format, string::String}; + +use iroha_schema::IntoSchema; use iroha_validator::{ data_model::evaluate::{EvaluationError, ExpressionEvaluator}, + default::domain::tokens::CanUnregisterDomain, permission::Token as _, prelude::*, }; +use parity_scale_codec::{Decode, Encode}; #[cfg(not(test))] extern crate panic_halt; @@ -22,7 +27,7 @@ mod token { use super::*; /// Token to identify if user can (un-)register domains. - #[derive(Token, ValidateGrantRevoke)] + #[derive(Token, ValidateGrantRevoke, Decode, Encode, IntoSchema)] #[validate(iroha_validator::permission::OnlyGenesis)] pub struct CanControlDomainLives; } @@ -125,19 +130,12 @@ impl Visit for CustomValidator { } impl Validate for CustomValidator { - fn permission_tokens() -> Vec { - let mut tokens = DefaultValidator::permission_tokens(); - - // TODO: Not very convenient usage. - // We need to come up with a better way. - if let Some(pos) = tokens.iter().position(|definition| { - definition - == &iroha_validator::default::domain::tokens::CanUnregisterDomain::definition() - }) { - tokens.remove(pos); - } + fn permission_token_schema() -> PermissionTokenSchema { + let mut tokens = DefaultValidator::permission_token_schema(); + + tokens.remove::(); + tokens.insert::(); - tokens.push(token::CanControlDomainLives::definition()); tokens } @@ -161,8 +159,8 @@ impl ExpressionEvaluator for CustomValidator { /// Entrypoint to return permission token definitions defined in this validator. #[entrypoint] -pub fn permission_tokens() -> Vec { - CustomValidator::permission_tokens() +pub fn permission_token_schema() -> PermissionTokenSchema { + CustomValidator::permission_token_schema() } /// Validate operation diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index 5e725d3ae95..06995b32542 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -107,8 +107,7 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { let asset_id = AssetId::new(asset_definition_id, account_id.clone()); // Registering trigger that should fail on execution - let bad_trigger_id = - as Identifiable>::Id::from_str("bad_trigger")?; + let bad_trigger_id = TriggerId::from_str("bad_trigger")?; // Invalid instruction let bad_trigger_instructions = vec![MintBox::new(1_u32, account_id.clone())]; let register_bad_trigger = RegisterBox::new(Trigger::new( @@ -126,7 +125,7 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { test_client.submit(register_bad_trigger)?; // Registering normal trigger - let trigger_id = as Identifiable>::Id::from_str(TRIGGER_NAME)?; + let trigger_id = TriggerId::from_str(TRIGGER_NAME)?; let trigger_instructions = vec![MintBox::new(1_u32, asset_id.clone())]; let register_trigger = RegisterBox::new(Trigger::new( trigger_id, @@ -160,8 +159,7 @@ fn trigger_should_not_be_executed_with_zero_repeats_count() -> Result<()> { let asset_definition_id = "rose#wonderland".parse()?; let account_id = AccountId::from_str("alice@wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let trigger_id = - as Identifiable>::Id::from_str("self_modifying_trigger")?; + let trigger_id = TriggerId::from_str("self_modifying_trigger")?; let trigger_instructions = vec![MintBox::new(1_u32, asset_id.clone())]; let register_trigger = RegisterBox::new(Trigger::new( @@ -220,8 +218,7 @@ fn trigger_should_be_able_to_modify_its_own_repeats_count() -> Result<()> { let asset_definition_id = "rose#wonderland".parse()?; let account_id = AccountId::from_str("alice@wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let trigger_id = - as Identifiable>::Id::from_str("self_modifying_trigger")?; + let trigger_id = TriggerId::from_str("self_modifying_trigger")?; let trigger_instructions = vec![ MintBox::new(1_u32, trigger_id.clone()), @@ -266,8 +263,7 @@ fn unregister_trigger() -> Result<()> { let account_id = AccountId::from_str("alice@wonderland")?; // Registering trigger - let trigger_id = - as Identifiable>::Id::from_str("empty_trigger")?; + let trigger_id = TriggerId::from_str("empty_trigger")?; let trigger = Trigger::new( trigger_id.clone(), Action::new( @@ -338,9 +334,8 @@ fn trigger_in_genesis_using_base64() -> Result<()> { info!("WASM size is {} bytes", wasm.len()); let wasm_base64 = serde_json::json!(base64::encode(&wasm)).to_string(); - let account_id = ::Id::from_str("alice@wonderland")?; - let trigger_id = - as Identifiable>::Id::from_str("genesis_trigger")?; + let account_id = AccountId::from_str("alice@wonderland")?; + let trigger_id = TriggerId::from_str("genesis_trigger")?; let trigger = Trigger::new( trigger_id.clone(), @@ -395,12 +390,8 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { let asset_definition_id = "rose#wonderland".parse()?; let account_id = AccountId::from_str("alice@wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let trigger_id_unregister = - as Identifiable>::Id::from_str("unregister_other_trigger")?; - let trigger_id_should_be_unregistered = - as Identifiable>::Id::from_str( - "should_be_unregistered_trigger", - )?; + let trigger_id_unregister = TriggerId::from_str("unregister_other_trigger")?; + let trigger_id_should_be_unregistered = TriggerId::from_str("should_be_unregistered_trigger")?; let trigger_unregister_instructions = vec![UnregisterBox::new( trigger_id_should_be_unregistered.clone(), diff --git a/client/tests/integration/upgrade.rs b/client/tests/integration/upgrade.rs index 278b6ca2c65..6d0aa0897ce 100644 --- a/client/tests/integration/upgrade.rs +++ b/client/tests/integration/upgrade.rs @@ -59,11 +59,11 @@ fn validator_upgrade_should_update_tokens() -> Result<()> { wait_for_genesis_committed(&vec![client.clone()], 0); // Check that `can_unregister_domain` exists - let can_unregister_domain_token_id: PermissionTokenId = "can_unregister_domain".parse()?; let definitions = client.request(FindAllPermissionTokenDefinitions)?; assert!(definitions - .into_iter() - .any(|definition| definition.id() == &can_unregister_domain_token_id)); + .token_ids() + .iter() + .any(|id| id == "CanUnregisterDomain")); upgrade_validator( &client, @@ -73,14 +73,14 @@ fn validator_upgrade_should_update_tokens() -> Result<()> { // Check that `can_unregister_domain` doesn't exist let definitions = client.request(FindAllPermissionTokenDefinitions)?; assert!(!definitions + .token_ids() .iter() - .any(|definition| definition.id() == &can_unregister_domain_token_id)); + .any(|id| id == "CanUnregisterDomain")); - // Check that `can_control_domain_lives` exists - let can_control_domain_lives: PermissionTokenId = "can_control_domain_lives".parse()?; assert!(definitions + .token_ids() .iter() - .any(|definition| definition.id() == &can_control_domain_lives)); + .any(|id| id == "CanControlDomainLives")); Ok(()) } diff --git a/configs/peer/genesis.json b/configs/peer/genesis.json index 71017467861..2dbdd3848b1 100644 --- a/configs/peer/genesis.json +++ b/configs/peer/genesis.json @@ -105,8 +105,8 @@ { "Grant": { "PermissionToken": { - "definition_id": "can_set_parameters", - "params": {} + "definition_id": "CanSetParameters", + "payload": [] }, "destination_id": { "AccountId": "alice@wonderland" @@ -178,20 +178,48 @@ "id": "ALICE_METADATA_ACCESS", "permissions": [ { - "definition_id": "can_remove_key_value_in_user_account", - "params": { - "account_id": { - "AccountId": "alice@wonderland" - } - } + "definition_id": "CanRemoveKeyValueInUserAccount", + "payload": [ + 20, + 97, + 108, + 105, + 99, + 101, + 40, + 119, + 111, + 110, + 100, + 101, + 114, + 108, + 97, + 110, + 100 + ] }, { - "definition_id": "can_set_key_value_in_user_account", - "params": { - "account_id": { - "AccountId": "alice@wonderland" - } - } + "definition_id": "CanSetKeyValueInUserAccount", + "payload": [ + 20, + 97, + 108, + 105, + 99, + 101, + 40, + 119, + 111, + 110, + 100, + 101, + 114, + 108, + 97, + 110, + 100 + ] } ] } diff --git a/core/src/lib.rs b/core/src/lib.rs index b647f344ae5..a6a5157d191 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -46,10 +46,6 @@ pub type RolesMap = HashMap<::Id, Role>; /// API to work with a collections of [`AccountId`] [`Permissions`] mappings. pub type PermissionTokensMap = HashMap<::Id, Permissions>; -/// API to work with a collections of [`PermissionTokenDefinitionId`] : [`PermissionTokenDefinition`] mappings. -pub type PermissionTokenDefinitionsMap = - HashMap<::Id, PermissionTokenDefinition>; - /// Type of `Sender` which should be used for channels of `Event` messages. pub type EventsSender = broadcast::Sender; diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index 0f5033eb5b3..71c01b0f9a1 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -36,7 +36,7 @@ pub mod isi { error::{MintabilityError, RepetitionError}, InstructionType, }, - query::error::{PermissionTokenFindError, QueryExecutionFail}, + query::error::QueryExecutionFail, }; use super::*; @@ -261,12 +261,13 @@ pub mod isi { // Check if account exists wsv.account_mut(&account_id)?; - let definition = wsv + if !wsv .permission_token_definitions() - .get(&permission_id) - .ok_or_else(|| FindError::PermissionTokenDefinition(permission_id.clone()))?; - - permissions::check_permission_token_parameters(&permission, definition)?; + .token_ids + .contains(&permission_id) + { + return Err(FindError::PermissionToken(permission_id).into()); + } if wsv.account_contains_inherent_permission(&account_id, &permission) { return Err(RepetitionError { @@ -298,18 +299,8 @@ pub mod isi { // Check if account exists wsv.account_mut(&account_id)?; - if !wsv - .permission_token_definitions() - .contains_key(&permission.definition_id) - { - return Err(FindError::PermissionTokenDefinition(permission.definition_id).into()); - } if !wsv.remove_account_permission(&account_id, &permission) { - return Err(FindError::PermissionToken(PermissionTokenFindError { - account_id: account_id.clone(), - permission_token_id: permission.definition_id, - }) - .into()); + return Err(FindError::PermissionToken(permission.definition_id).into()); } wsv.emit_events(Some(AccountEvent::PermissionRemoved( diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 13da4987b1b..131d4a92d01 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -10,7 +10,6 @@ pub mod account; pub mod asset; pub mod block; pub mod domain; -pub mod permissions; pub mod query; pub mod triggers; pub mod tx; @@ -19,10 +18,7 @@ pub mod world; use eyre::Result; use iroha_data_model::{ evaluate::ExpressionEvaluator, - isi::{ - error::{InstructionEvaluationError, InstructionExecutionError as Error}, - *, - }, + isi::{error::InstructionExecutionError as Error, *}, prelude::*, }; use iroha_logger::prelude::{Span, *}; @@ -119,7 +115,7 @@ impl Execute for UnregisterBox { IdBox::TriggerId(object_id) => { Unregister::> { object_id }.execute(authority, wsv) } - IdBox::PermissionTokenDefinitionId(_) | IdBox::ParameterId(_) => { + IdBox::PermissionTokenId(_) | IdBox::ParameterId(_) => { Err(Error::Evaluate(InstructionType::Unregister.into())) } } diff --git a/core/src/smartcontracts/isi/permissions.rs b/core/src/smartcontracts/isi/permissions.rs deleted file mode 100644 index f8790b54e0e..00000000000 --- a/core/src/smartcontracts/isi/permissions.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! Contains functions to check permission -#![allow( - clippy::arithmetic_side_effects, - clippy::std_instead_of_core, - clippy::std_instead_of_alloc -)] - -use super::*; - -/// Check if a permission `token` has the parameters from its `definition`. -/// -/// Takes `O(max(N, M))` time, where *N* is the number of parameters in `token` -/// and *M* is the number of parameters in `definition`. -/// -/// # Errors -/// Fails if there is a mismatch between a permissions `token` and its `definition`: -/// - If a `token` doesn't have all parameters from its `definition` -/// - If a `token` has parameters that are not in its `definition` -/// - If a `token` has a parameter of a different type than in its `definition` -pub fn check_permission_token_parameters( - token: &PermissionToken, - definition: &PermissionTokenDefinition, -) -> std::result::Result<(), InstructionEvaluationError> { - use iroha_data_model::ValueKind; - use itertools::{ - EitherOrBoth::{Both, Left, Right}, - Itertools, - }; - - for either_or_both in token - .params - .iter() - .map(|(key, value)| (key, ValueKind::from(value))) - .zip_longest(&definition.params) - { - match either_or_both { - Both((key, kind), (expected_key, expected_kind)) => { - // As keys are guaranteed to be in alphabetical order, that's an error if they are mismatched - if key != expected_key { - return Err(missing_parameter(expected_key)); - } - if kind != *expected_kind { - return Err(InstructionEvaluationError::PermissionParameter(format!( - "Permission token parameter `{key}` type mismatch: \ - expected `{expected_kind}`, got `{kind}`" - ))); - } - } - // No more parameters in the definition - Left((key, _)) => { - return Err(InstructionEvaluationError::PermissionParameter(format!( - "Undefined permission token parameter: `{key}`" - ))); - } - // No more parameters in the permission token - Right((expected_key, _)) => { - return Err(missing_parameter(expected_key)); - } - } - } - - Ok(()) -} - -fn missing_parameter(key: &Name) -> InstructionEvaluationError { - InstructionEvaluationError::PermissionParameter(format!( - "Permission parameter `{key}` is missing" - )) -} diff --git a/core/src/smartcontracts/isi/world.rs b/core/src/smartcontracts/isi/world.rs index a4909bd0e48..42ec2c5253e 100644 --- a/core/src/smartcontracts/isi/world.rs +++ b/core/src/smartcontracts/isi/world.rs @@ -112,17 +112,6 @@ pub mod isi { fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { let role = self.object.build(authority); - for permission in &role.permissions { - let definition = wsv - .permission_token_definitions() - .get(&permission.definition_id) - .ok_or_else(|| { - FindError::PermissionTokenDefinition(permission.definition_id.clone()) - })?; - - permissions::check_permission_token_parameters(permission, definition)?; - } - if wsv.roles().contains_key(role.id()) { return Err(RepetitionError { instruction_type: InstructionType::Register, @@ -131,6 +120,16 @@ pub mod isi { .into()); } + for permission in &role.permissions { + if !wsv + .permission_token_definitions() + .token_ids + .contains(&permission.definition_id) + { + return Err(FindError::PermissionToken(permission.definition_id.clone()).into()); + } + } + let world = wsv.world_mut(); let role_id = role.id().clone(); world.roles.insert(role_id, role.clone()); @@ -178,55 +177,34 @@ pub mod isi { } } - fn register_permission_token_definition( - definition: PermissionTokenDefinition, - wsv: &mut WorldStateView, - ) -> Result<(), Error> { - let definition_id = definition.id().clone(); + fn register_permission_token_definition(token_id: PermissionTokenId, wsv: &mut WorldStateView) { + let permission_token_ids = &mut wsv.world_mut().permission_token_definitions.token_ids; - let world = wsv.world_mut(); - if world - .permission_token_definitions - .contains_key(&definition_id) - { - return Err(RepetitionError { - instruction_type: InstructionType::Register, - id: IdBox::PermissionTokenDefinitionId(definition_id), - } - .into()); + // Keep permission tokens sorted + if let Err(pos) = permission_token_ids.binary_search(&token_id) { + permission_token_ids.insert(pos, token_id); } - - world - .permission_token_definitions - .insert(definition_id, definition.clone()); - - wsv.emit_events(Some(PermissionTokenEvent::DefinitionCreated(definition))); - - Ok(()) } fn unregister_permission_token_definition( - definition_id: PermissionTokenId, + token_id: &PermissionTokenId, wsv: &mut WorldStateView, ) -> Result<(), Error> { - remove_token_from_roles(wsv, &definition_id)?; - remove_token_from_accounts(wsv, &definition_id)?; + remove_token_from_roles(token_id, wsv)?; + remove_token_from_accounts(token_id, wsv)?; - let world = wsv.world_mut(); - let definition = world + wsv.world_mut() .permission_token_definitions - .remove(&definition_id) - .ok_or_else(|| FindError::PermissionTokenDefinition(definition_id))?; - - wsv.emit_events(Some(PermissionTokenEvent::DefinitionDeleted(definition))); + .token_ids + .retain(|id| id != token_id); Ok(()) } /// Remove all tokens with specified definition id from all registered roles fn remove_token_from_roles( + token_id: &PermissionTokenId, wsv: &mut WorldStateView, - target_definition_id: &::Id, ) -> Result<(), Error> { let mut roles_containing_token = Vec::new(); @@ -234,7 +212,7 @@ pub mod isi { if role .permissions .iter() - .any(|token| token.definition_id == *target_definition_id) + .any(|token| token.definition_id == *token_id) { roles_containing_token.push(role_id.clone()) } @@ -245,10 +223,10 @@ pub mod isi { for role_id in roles_containing_token { if let Some(role) = world.roles.get_mut(&role_id) { role.permissions - .retain(|token| token.definition_id != *target_definition_id); + .retain(|token| token.definition_id != *token_id); events.push(RoleEvent::PermissionRemoved(PermissionRemoved { role_id: role_id.clone(), - permission_definition_id: target_definition_id.clone(), + permission_token_id: token_id.clone(), })); } else { error!(%role_id, "role not found. This is a bug"); @@ -263,8 +241,8 @@ pub mod isi { /// Remove all tokens with specified definition id from all accounts in all domains fn remove_token_from_accounts( + token_id: &PermissionTokenId, wsv: &mut WorldStateView, - target_definition_id: &::Id, ) -> Result<(), Error> { let mut accounts_with_token = std::collections::HashMap::new(); @@ -273,7 +251,7 @@ pub mod isi { ( account.id().clone(), wsv.account_inherent_permission_tokens(account) - .filter(|token| token.definition_id == *target_definition_id) + .filter(|token| token.definition_id == *token_id) .collect::>(), ) }); @@ -286,9 +264,7 @@ pub mod isi { for token in tokens { if !wsv.remove_account_permission(&account_id, &token) { error!(%token, "token not found. This is a bug"); - return Err( - FindError::PermissionTokenDefinition(token.definition_id.clone()).into(), - ); + return Err(FindError::PermissionToken(token.definition_id.clone()).into()); } events.push(AccountEvent::PermissionRemoved(AccountPermissionChanged { account_id: account_id.clone(), @@ -352,13 +328,12 @@ pub mod isi { let raw_validator = self.object; let engine = wsv.engine.clone(); // Cloning engine is cheap - let (new_validator, new_permission_token_definitions) = + let (new_validator, new_token_schema) = || -> Result<_, crate::smartcontracts::wasm::error::Error> { { let new_validator = Validator::new(raw_validator, &engine)?; - let new_permission_token_definitions = - new_validator.permission_tokens(wsv)?; - Ok((new_validator, new_permission_token_definitions)) + let new_token_schema = new_validator.permission_tokens(wsv)?; + Ok((new_validator, new_token_schema)) } }() .map_err(|error| { @@ -368,36 +343,40 @@ pub mod isi { let world = wsv.world_mut(); let _ = world.upgraded_validator.insert(new_validator); - let old_permission_token_definitions = wsv - .permission_token_definitions() - .values() - .cloned() - .collect::>(); - let new_permission_token_definitions = { + { let mut tokens = HashSet::new(); - for token in new_permission_token_definitions { - let token_id = token.id().clone(); - let newly_inserted = tokens.insert(token); - if !newly_inserted { + + for token_id in &new_token_schema.token_ids { + if !tokens.insert(token_id.clone()) { return Err(InvalidParameterError::Wasm(format!( "Retrieved permission tokens definitions contain duplicate: `{token_id}`", )) .into()); } } - tokens - }; + } + + let old_token_schema = wsv.permission_token_definitions().clone(); + for token_id in &old_token_schema.token_ids { + if !new_token_schema.token_ids.contains(token_id) { + unregister_permission_token_definition(token_id, wsv)?; + } - old_permission_token_definitions - .difference(&new_permission_token_definitions) - .try_for_each(|definition| { - unregister_permission_token_definition(definition.id().clone(), wsv) - })?; + wsv.world_mut().permission_token_definitions.schema = + new_token_schema.schema.clone(); + } + for token_id in &new_token_schema.token_ids { + wsv.world_mut().permission_token_definitions.schema = + new_token_schema.schema.clone(); - new_permission_token_definitions - .difference(&old_permission_token_definitions) - .cloned() - .try_for_each(|definition| register_permission_token_definition(definition, wsv))?; + if !old_token_schema.token_ids.contains(token_id) { + register_permission_token_definition(token_id.clone(), wsv); + } + } + wsv.emit_events(Some(PermissionTokenSchemaUpdateEvent { + old_schema: old_token_schema, + new_schema: new_token_schema, + })); wsv.emit_events(Some(ValidatorEvent::Upgraded)); @@ -462,13 +441,9 @@ pub mod query { } impl ValidQuery for FindAllPermissionTokenDefinitions { - #[metrics("find_all_token_ids")] + #[metrics("find_all_permission_token_ids")] fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv - .permission_token_definitions() - .values() - .cloned() - .collect()) + Ok(wsv.permission_token_definitions().clone()) } } @@ -480,15 +455,18 @@ pub mod query { } impl ValidQuery for DoesAccountHavePermissionToken { - #[metrics("does_account_have_permission")] + #[metrics("does_account_have_permission_token")] fn execute(&self, wsv: &WorldStateView) -> Result { let authority = wsv .evaluate(&self.account_id) .map_err(|e| Error::Evaluate(e.to_string()))?; + let permission_token = wsv + .evaluate(&self.permission_token) + .map_err(|e| Error::Evaluate(e.to_string()))?; wsv.map_account(&authority, |account| { wsv.account_permission_tokens(account) - .contains(&self.permission_token) + .contains(&permission_token) }) } } diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index f65644f6e1a..0a4f0265899 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -11,7 +11,7 @@ use iroha_config::{ }; use iroha_data_model::{ account::AccountId, - permission::PermissionTokenDefinition, + permission::PermissionTokenSchema, prelude::*, validator::{self, NeedsValidationBox}, ValidationFail, @@ -43,8 +43,9 @@ pub mod export { pub const WASM_MAIN_FN_NAME: &str = "_iroha_wasm_main"; /// Name of the exported entry for validator to validate operation pub const VALIDATOR_VALIDATE_FN_NAME: &str = "_iroha_validator_validate"; - /// Name of the exported entry for validator to retrieve [`PermissionTokenDefinition`]s - pub const VALIDATOR_PERMISSION_TOKENS_FN_NAME: &str = "_iroha_validator_permission_tokens"; + /// Name of the exported entry for validator to retrieve [`PermissionTokenSchema`]s + pub const VALIDATOR_PERMISSION_TOKEN_SCHEMA_FN_NAME: &str = + "_iroha_validator_permission_token_schema"; } pub mod import { @@ -900,11 +901,11 @@ impl Runtime { /// - if failed to instantiate provided `module` /// - if failed to get export function for `permission_tokens()` /// - if failed to call export function - /// - if failed to decode `Vec` + /// - if failed to decode `Vec` pub fn execute_validator_permission_tokens( &self, module: &wasmtime::Module, - ) -> Result> { + ) -> Result { let log_span = wasm_log_span!("Retrieving permission tokens"); let state = state::ValidatorPermissionTokens { log_span, @@ -917,7 +918,7 @@ impl Runtime { let permission_tokens_fn = Self::get_typed_func( &instance, &mut store, - export::VALIDATOR_PERMISSION_TOKENS_FN_NAME, + export::VALIDATOR_PERMISSION_TOKEN_SCHEMA_FN_NAME, )?; let offset = permission_tokens_fn diff --git a/core/src/validator.rs b/core/src/validator.rs index dcd6d718799..81711ab9e96 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -2,7 +2,7 @@ use derive_more::DebugCustom; use iroha_data_model::{ - account::AccountId, permission::PermissionTokenDefinition, validator as data_model_validator, + account::AccountId, permission::PermissionTokenSchema, validator as data_model_validator, ValidationFail, }; #[cfg(test)] @@ -100,7 +100,7 @@ impl Validator { pub fn permission_tokens( &self, wsv: &WorldStateView, - ) -> Result, wasm::error::Error> { + ) -> Result { let runtime = wasm::RuntimeBuilder::::new() .with_engine(wsv.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs .with_configuration(wsv.config.wasm_runtime_config) @@ -183,8 +183,8 @@ impl MockValidator { pub fn permission_tokens( &self, _wsv: &WorldStateView, - ) -> Result, wasm::error::Error> { - Ok(Vec::default()) + ) -> Result { + Ok(PermissionTokenSchema::default()) } fn execute_instruction( diff --git a/core/src/wsv.rs b/core/src/wsv.rs index bb7b9e23ff6..c8b7149aa14 100644 --- a/core/src/wsv.rs +++ b/core/src/wsv.rs @@ -26,6 +26,7 @@ use iroha_data_model::{ block::{CommittedBlock, VersionedCommittedBlock}, isi::error::{InstructionExecutionError as Error, MathError}, parameter::Parameter, + permission::PermissionTokenSchema, prelude::*, query::error::{FindError, QueryExecutionFail}, trigger::action::ActionTrait, @@ -65,7 +66,7 @@ pub struct World { /// Permission tokens of an account. pub(crate) account_permission_tokens: crate::PermissionTokensMap, /// Registered permission token ids. - pub(crate) permission_token_definitions: crate::PermissionTokenDefinitionsMap, + pub(crate) permission_token_definitions: PermissionTokenSchema, /// Triggers pub(crate) triggers: TriggerSet, /// Runtime Validator @@ -619,9 +620,9 @@ impl WorldStateView { &self.world.roles } - /// Get all permission token ids + /// Get all permission token definitions #[inline] - pub fn permission_token_definitions(&self) -> &crate::PermissionTokenDefinitionsMap { + pub fn permission_token_definitions(&self) -> &crate::PermissionTokenSchema { &self.world.permission_token_definitions } diff --git a/core/test_network/Cargo.toml b/core/test_network/Cargo.toml index a2ebe97397c..8c61ff9956f 100644 --- a/core/test_network/Cargo.toml +++ b/core/test_network/Cargo.toml @@ -24,3 +24,4 @@ rand = "0.8.5" tempfile = "3.3.0" tokio = { version = "1.23.0", features = ["rt", "rt-multi-thread", "macros"] } unique_port = "0.2.1" +parity-scale-codec = { version = "3.1.5", default-features = false } diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 00c61b24fdb..4df207f4c15 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -85,29 +85,19 @@ impl TestGenesis for GenesisNetwork { ::Id::from_str("alice@wonderland").expect("valid names"); let mint_rose_permission = PermissionToken::new( - "can_mint_assets_with_definition" - .parse() - .expect("valid names"), - ) - .with_params([( - "asset_definition_id".parse().expect("valid names"), - IdBox::from(rose_definition_id.clone()).into(), - )]); + "CanMintAssetsWithDefinition".to_owned(), + &rose_definition_id, + ); let burn_rose_permission = PermissionToken::new( - "can_burn_assets_with_definition" - .parse() - .expect("valid names"), - ) - .with_params([( - "asset_definition_id".parse().expect("valid names"), - IdBox::from(rose_definition_id).into(), - )]); + "CanBurnAssetsWithDefinition".to_owned(), + &rose_definition_id, + ); let unregister_any_peer_permission = - PermissionToken::new("can_unregister_any_peer".parse().expect("valid names")); + PermissionToken::new("CanUnregisterAnyPeer".to_owned(), &()); let unregister_any_role_permission = - PermissionToken::new("can_unregister_any_role".parse().expect("valid names")); + PermissionToken::new("CanUnregisterAnyRole".to_owned(), &()); let upgrade_validator_permission = - PermissionToken::new("can_upgrade_validator".parse().expect("valid names")); + PermissionToken::new("CanUpgradeValidator".to_owned(), &()); for permission in [ mint_rose_permission, diff --git a/data_model/derive/src/lib.rs b/data_model/derive/src/lib.rs index 1ebb20e5ccd..fc73e897dd0 100644 --- a/data_model/derive/src/lib.rs +++ b/data_model/derive/src/lib.rs @@ -6,7 +6,6 @@ mod has_origin; mod id; mod model; mod partially_tagged; -mod variant_discriminant; use proc_macro::TokenStream; use syn::parse_macro_input; @@ -485,44 +484,3 @@ pub fn partially_tagged_deserialize_derive(input: TokenStream) -> TokenStream { pub fn has_origin_derive(input: TokenStream) -> TokenStream { has_origin::impl_has_origin(&parse_macro_input!(input)) } -/// Derive macro implementing the [`AssociatedConstant`](iroha_data_model::AssociatedConstant) trait. -/// -/// # Notes -/// -/// You should import `AssociatedConstant`. -/// -/// # Attributes -/// -/// `#[variant_discriminant(name(DiscriminantType))]` attribute where `DiscriminantType` is the name of the `enum` that should serve as the discriminant enumeration. -/// -/// # Examples -/// -/// ``` -/// use iroha_data_model_derive::VariantDiscriminant; -/// use iroha_data_model::AssociatedConstant; -/// -/// #[derive(VariantDiscriminant)] -/// #[variant_discriminant(name(MyEnumKind))] -/// enum MyEnum { -/// Unsigned(u32), -/// String(String), -/// Boolean(bool), -/// } -/// -/// #[derive(Debug, PartialEq, Eq)] -/// enum MyEnumKind { -/// Unsigned, -/// String, -/// Boolean, -/// } -/// -/// assert_eq!(>::VALUE, MyEnumKind::Unsigned); -/// assert_eq!(>::VALUE, MyEnumKind::String); -/// assert_eq!(>::VALUE, MyEnumKind::Boolean); -/// ``` -#[proc_macro_error::proc_macro_error] -#[proc_macro_derive(VariantDiscriminant, attributes(variant_discriminant))] -pub fn variant_discriminant_derive(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).expect("Failed to parse input Token Stream."); - variant_discriminant::impl_variant_discriminant(&ast) -} diff --git a/data_model/derive/src/variant_discriminant.rs b/data_model/derive/src/variant_discriminant.rs deleted file mode 100644 index d662b9f51f4..00000000000 --- a/data_model/derive/src/variant_discriminant.rs +++ /dev/null @@ -1,107 +0,0 @@ -use iroha_macro_utils::AttrParser; -use proc_macro::TokenStream; -use proc_macro_error::{abort, OptionExt as _}; -use quote::quote; -use syn::{ - parse::{Parse, ParseStream}, - punctuated::Punctuated, - Attribute, Generics, Ident, Token, Type, Visibility, -}; - -mod kw { - syn::custom_keyword!(name); -} - -pub struct VariantDiscriminantEnum { - variants: Punctuated, - discriminant_type: Type, -} - -impl Parse for VariantDiscriminantEnum { - fn parse(input: ParseStream) -> syn::Result { - let attrs = input.call(Attribute::parse_outer)?; - let _vis = input.parse::()?; - let _enum_token = input.parse::()?; - let _ident = input.parse::()?; - let generics = input.parse::()?; - if !generics.params.is_empty() { - abort!(generics, "Generics are not supported"); - } - let content; - let _brace_token = syn::braced!(content in input); - let variants = content.parse_terminated(Variant::parse)?; - let discriminant_type = attrs - .iter() - .find_map(|attr| VariantDiscriminantAttr::::parse(attr).ok()) - .map(|name_attr| name_attr.ty) - .expect_or_abort("Attribute `#[strum_discriminants(name(...))]` is required"); - Ok(Self { - variants, - discriminant_type, - }) - } -} - -pub struct Variant { - ty: Type, - discriminant_name: Ident, -} - -impl Parse for Variant { - fn parse(input: ParseStream) -> syn::Result { - let syn::Variant { ident, fields, .. } = input.parse::()?; - let unnamed = match fields { - syn::Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => unnamed, - fields => abort!(fields, "Only supports tuple variants with single field"), - }; - - let ty = unnamed.unnamed.first().expect("Checked above").ty.clone(); - Ok(Self { - ty, - discriminant_name: ident, - }) - } -} - -pub struct Name { - _kw: kw::name, - _paren: syn::token::Paren, - ty: Type, -} - -impl Parse for Name { - fn parse(input: ParseStream) -> syn::Result { - let kw = input.parse::()?; - let content; - let paren = syn::parenthesized!(content in input); - let ty = content.parse::()?; - Ok(Self { - _kw: kw, - _paren: paren, - ty, - }) - } -} - -struct VariantDiscriminantAttr(core::marker::PhantomData); - -impl AttrParser for VariantDiscriminantAttr { - const IDENT: &'static str = "variant_discriminant"; -} - -pub fn impl_variant_discriminant(enum_: &VariantDiscriminantEnum) -> TokenStream { - let discriminant_type = &enum_.discriminant_type; - let impls = enum_.variants.iter().map(|variant| { - let Variant { - ty, - discriminant_name, - } = variant; - // In order to make doc-tests work, we need to not to use full path to `AssociatedConstant` - quote! { - impl AssociatedConstant<#discriminant_type> for #ty { - const VALUE: #discriminant_type = #discriminant_type::#discriminant_name; - } - } - }); - quote! {#(#impls)*}.into() -} diff --git a/data_model/src/events/data/events.rs b/data_model/src/events/data/events.rs index 86bf013c1e8..4d788272ddc 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -58,7 +58,7 @@ pub mod model { Domain(domain::DomainEvent), Role(role::RoleEvent), Trigger(trigger::TriggerEvent), - PermissionToken(permission::PermissionTokenEvent), + PermissionToken(permission::PermissionTokenSchemaUpdateEvent), Configuration(config::ConfigurationEvent), Validator(validator::ValidatorEvent), } @@ -84,7 +84,7 @@ pub mod model { /// Role event Role(role::RoleEvent), /// Permission token event - PermissionToken(permission::PermissionTokenEvent), + PermissionToken(permission::PermissionTokenSchemaUpdateEvent), /// Configuration event Configuration(config::ConfigurationEvent), /// Validator event @@ -239,24 +239,48 @@ mod role { pub struct PermissionRemoved { /// Role id pub role_id: RoleId, - /// [`PermissionTokenDefinition`] id. All [`PermissionToken`]s with this definition id were removed. - pub permission_definition_id: PermissionTokenId, + // TODO: Skipped temporarily because of FFI + #[getset(skip)] + /// All [`PermissionToken`]s with this id were removed. + pub permission_token_id: PermissionTokenId, } } } mod permission { - //! This module contains [`PermissionTokenEvent`] + //! This module contains [`PermissionTokenSchemaUpdateEvent`] + pub use self::model::*; use super::*; + use crate::permission::PermissionTokenSchema; - data_event! { - #[has_origin(origin = PermissionTokenDefinition)] - pub enum PermissionTokenEvent { - #[has_origin(permission_token_definition => permission_token_definition.id())] - DefinitionCreated(PermissionTokenDefinition), - #[has_origin(permission_token_definition => permission_token_definition.id())] - DefinitionDeleted(PermissionTokenDefinition), + #[model] + pub mod model { + use super::*; + + /// Information about permission tokens update. + /// Only happens when registering new validator + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + #[getset(get = "pub")] + #[ffi_type] + pub struct PermissionTokenSchemaUpdateEvent { + /// Previous set of permission tokens + pub old_schema: PermissionTokenSchema, + /// New set of permission tokens + pub new_schema: PermissionTokenSchema, } } } @@ -320,6 +344,8 @@ mod account { #[ffi_type] pub struct AccountPermissionChanged { pub account_id: AccountId, + // TODO: Skipped temporarily because of FFI + #[getset(skip)] pub permission_id: PermissionTokenId, } @@ -345,6 +371,13 @@ mod account { pub role_id: RoleId, } } + + impl AccountPermissionChanged { + /// Get permission id + pub fn permission_id(&self) -> &str { + &self.permission_id + } + } } mod domain { @@ -585,7 +618,7 @@ pub mod prelude { config::ConfigurationEvent, domain::{DomainEvent, DomainEventFilter, DomainFilter}, peer::{PeerEvent, PeerEventFilter, PeerFilter}, - permission::PermissionTokenEvent, + permission::PermissionTokenSchemaUpdateEvent, role::{PermissionRemoved, RoleEvent, RoleEventFilter, RoleFilter}, trigger::{ TriggerEvent, TriggerEventFilter, TriggerFilter, TriggerNumberOfExecutionsChanged, diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 4c0d4ed7b25..0a192e3ca72 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -40,7 +40,7 @@ use getset::Getters; use iroha_crypto::{HashOf, PublicKey}; pub use iroha_crypto::{SignatureOf, SignaturesOf}; use iroha_data_model_derive::{ - model, IdEqOrdHash, PartiallyTaggedDeserialize, PartiallyTaggedSerialize, VariantDiscriminant, + model, IdEqOrdHash, PartiallyTaggedDeserialize, PartiallyTaggedSerialize, }; use iroha_macro::{error::ErrorTryFromEnum, FromVariant}; use iroha_primitives::{ @@ -50,11 +50,9 @@ use iroha_primitives::{ use iroha_schema::IntoSchema; pub use numeric::model::NumericValue; use parity_scale_codec::{Decode, Encode}; -use prelude::{Executable, TransactionQueryResult}; +use prelude::{Executable, TransactionQueryResult, VersionedSignedTransaction}; use serde::{Deserialize, Serialize}; use serde_with::{DeserializeFromStr, SerializeDisplay}; -use strum::EnumDiscriminants; -use transaction::VersionedSignedTransaction; pub use self::model::*; use crate::{account::SignatureCheckCondition, name::Name}; @@ -86,34 +84,6 @@ pub mod trigger; pub mod validator; pub mod visit; -mod utils { - use core::fmt::*; - - /// Format `input` separating items with a comma, - /// wrapping the whole output into provided characters. - /// - /// # Errors - /// - /// - if cannot write to the `f` - pub fn format_comma_separated( - mut input: impl Iterator, - (open, close): (char, char), - f: &mut Formatter<'_>, - ) -> Result { - f.write_char(open)?; - - if let Some(item) = input.next() { - f.write_fmt(format_args!("{item}"))?; - } - - for item in input { - f.write_fmt(format_args!(", {item}"))?; - } - - f.write_char(close) - } -} - mod seal { use crate::{isi::prelude::*, query::prelude::*}; @@ -260,12 +230,6 @@ impl EnumTryAsError { #[cfg(feature = "std")] impl std::error::Error for EnumTryAsError {} -/// Trait to define associated constant of type `T` -pub trait AssociatedConstant { - /// Associated constant value - const VALUE: T; -} - pub mod parameter { //! Structures, traits and impls related to `Paramater`s. @@ -616,29 +580,29 @@ pub mod model { IntoSchema, )] #[allow(clippy::enum_variant_names)] - #[ffi_type] + #[ffi_type(local)] pub enum IdBox { /// [`DomainId`](`domain::DomainId`) variant. - DomainId(::Id), + DomainId(domain::DomainId), /// [`AccountId`](`account::AccountId`) variant. #[display(fmt = "{_0}")] - AccountId(::Id), + AccountId(account::AccountId), /// [`AssetDefinitionId`](`asset::AssetDefinitionId`) variant. #[display(fmt = "{_0}")] - AssetDefinitionId(::Id), + AssetDefinitionId(asset::AssetDefinitionId), /// [`AssetId`](`asset::AssetId`) variant. #[display(fmt = "{_0}")] - AssetId(::Id), + AssetId(asset::AssetId), /// [`PeerId`](`peer::PeerId`) variant. - PeerId(::Id), + PeerId(peer::PeerId), /// [`TriggerId`](trigger::TriggerId) variant. - TriggerId( as Identifiable>::Id), + TriggerId(trigger::TriggerId), /// [`RoleId`](`role::RoleId`) variant. - RoleId(::Id), - /// [`PermissionTokenId`](`permission::PermissionTokenId`) variant. - PermissionTokenDefinitionId(::Id), + RoleId(role::RoleId), + /// [`PermissionToken`](`permission::PermissionToken`) variant. + PermissionTokenId(permission::PermissionTokenId), /// [`ParameterId`](`parameter::ParameterId`) variant. - ParameterId(::Id), + ParameterId(parameter::ParameterId), } /// Sized container for constructors of all [`Identifiable`]s that can be registered via transaction @@ -722,8 +686,6 @@ pub mod model { Trigger(TriggerBox), /// [`Role`](`role::Role`) variant. Role(role::Role), - /// [`PermissionTokenDefinition`](`permission::PermissionTokenDefinition`) variant. - PermissionTokenDefinition(permission::PermissionTokenDefinition), /// [`Parameter`](`parameter::Parameter`) variant. Parameter(parameter::Parameter), } @@ -789,25 +751,12 @@ pub mod model { PartialOrd, Ord, FromVariant, - EnumDiscriminants, - VariantDiscriminant, Decode, Encode, PartiallyTaggedDeserialize, PartiallyTaggedSerialize, IntoSchema, )] - #[strum_discriminants( - name(ValueKind), - derive(Display, Decode, Encode, Deserialize, Serialize, IntoSchema), - cfg_attr( - any(feature = "ffi_import", feature = "ffi_export"), - derive(iroha_ffi::FfiType) - ), - allow(missing_docs), - repr(u8) - )] - #[variant_discriminant(name(ValueKind))] #[allow(clippy::enum_variant_names, missing_docs)] #[ffi_type(opaque)] pub enum Value { @@ -831,6 +780,7 @@ pub mod model { SignatureCheckCondition(SignatureCheckCondition), TransactionQueryResult(TransactionQueryResult), PermissionToken(permission::PermissionToken), + PermissionTokenSchema(permission::PermissionTokenSchema), Hash(HashValue), Block(VersionedCommittedBlockWrapper), BlockHeader(block::BlockHeader), @@ -1056,7 +1006,6 @@ impl IdentifiableBox { IdentifiableBox::Asset(a) => a.id().clone().into(), IdentifiableBox::Trigger(a) => a.id().clone().into(), IdentifiableBox::Role(a) => a.id().clone().into(), - IdentifiableBox::PermissionTokenDefinition(a) => a.id().clone().into(), IdentifiableBox::Parameter(a) => a.id().clone().into(), } } @@ -1120,6 +1069,7 @@ impl fmt::Display for Value { Value::SignatureCheckCondition(v) => fmt::Display::fmt(&v, f), Value::TransactionQueryResult(_) => write!(f, "TransactionQueryResult"), Value::PermissionToken(v) => fmt::Display::fmt(&v, f), + Value::PermissionTokenSchema(v) => fmt::Display::fmt(&v, f), Value::Hash(v) => fmt::Display::fmt(&v, f), Value::Block(v) => fmt::Display::fmt(&**v, f), Value::BlockHeader(v) => fmt::Display::fmt(&v, f), @@ -1149,6 +1099,7 @@ impl Value { | Name(_) | TransactionQueryResult(_) | PermissionToken(_) + | PermissionTokenSchema(_) | Hash(_) | Block(_) | Ipv4Addr(_) @@ -1187,30 +1138,24 @@ where // TODO: The following macros looks very similar. Try to generalize them under one macro macro_rules! from_and_try_from_value_idbox { - ( $($variant:ident( $ty:ty ),)+ $(,)? ) => { - $( - impl TryFrom for $ty { - type Error = ErrorTryFromEnum; - - fn try_from(value: Value) -> Result { - if let Value::Id(IdBox::$variant(id)) = value { - Ok(id) - } else { - Err(Self::Error::default()) - } - } - } + ( $($variant:ident( $ty:ty ),)+ $(,)? ) => { $( + impl TryFrom for $ty { + type Error = ErrorTryFromEnum; - impl From<$ty> for Value { - fn from(id: $ty) -> Self { - Value::Id(IdBox::$variant(id)) + fn try_from(value: Value) -> Result { + if let Value::Id(IdBox::$variant(id)) = value { + Ok(id) + } else { + Err(Self::Error::default()) } } + } - impl AssociatedConstant for $ty { - const VALUE: ValueKind = ValueKind::Id; + impl From<$ty> for Value { + fn from(id: $ty) -> Self { + Value::Id(IdBox::$variant(id)) } - )+ + })+ }; } @@ -1232,12 +1177,8 @@ macro_rules! from_and_try_from_value_identifiable { fn from(id: $ty) -> Self { Value::Identifiable(IdentifiableBox::$variant(id)) } - } - - impl AssociatedConstant for $ty { - const VALUE: ValueKind = ValueKind::Identifiable; - } - )+ }; + } )+ + }; } macro_rules! from_and_try_from_and_try_as_value_hash { @@ -1291,54 +1232,52 @@ macro_rules! from_and_try_from_and_try_as_value_hash { } macro_rules! from_and_try_from_and_try_as_value_numeric { - ( $( $variant:ident($ty:ty),)+ $(,)? ) => { - $( - impl TryFrom for $ty { - type Error = ErrorTryFromEnum; - - #[inline] - fn try_from(value: Value) -> Result { - if let Value::Numeric(NumericValue::$variant(value)) = value { - Ok(value) - } else { - Err(Self::Error::default()) - } + ( $( $variant:ident($ty:ty),)+ $(,)? ) => { $( + impl TryFrom for $ty { + type Error = ErrorTryFromEnum; + + #[inline] + fn try_from(value: Value) -> Result { + if let Value::Numeric(NumericValue::$variant(value)) = value { + Ok(value) + } else { + Err(Self::Error::default()) } } + } - impl From<$ty> for Value { - #[inline] - fn from(value: $ty) -> Self { - Value::Numeric(NumericValue::$variant(value)) - } + impl From<$ty> for Value { + #[inline] + fn from(value: $ty) -> Self { + Value::Numeric(NumericValue::$variant(value)) } + } - impl TryAsMut<$ty> for NumericValue { - type Error = crate::EnumTryAsError<$ty, NumericValue>; + impl TryAsMut<$ty> for NumericValue { + type Error = crate::EnumTryAsError<$ty, NumericValue>; - #[inline] - fn try_as_mut(&mut self) -> Result<&mut $ty, Self::Error> { - if let NumericValue:: $variant (value) = self { - Ok(value) - } else { - Err(crate::EnumTryAsError::got(*self)) - } + #[inline] + fn try_as_mut(&mut self) -> Result<&mut $ty, Self::Error> { + if let NumericValue:: $variant (value) = self { + Ok(value) + } else { + Err(crate::EnumTryAsError::got(*self)) } } + } - impl TryAsRef<$ty> for NumericValue { - type Error = crate::EnumTryAsError<$ty, NumericValue>; + impl TryAsRef<$ty> for NumericValue { + type Error = crate::EnumTryAsError<$ty, NumericValue>; - #[inline] - fn try_as_ref(&self) -> Result<& $ty, Self::Error> { - if let NumericValue:: $variant (value) = self { - Ok(value) - } else { - Err(crate::EnumTryAsError::got(*self)) - } + #[inline] + fn try_as_ref(&self) -> Result<& $ty, Self::Error> { + if let NumericValue:: $variant (value) = self { + Ok(value) + } else { + Err(crate::EnumTryAsError::got(*self)) } } - )+ + })+ }; } @@ -1367,7 +1306,6 @@ from_and_try_from_value_identifiable!( Asset(asset::Asset), Trigger(TriggerBox), Role(role::Role), - PermissionTokenDefinition(permission::PermissionTokenDefinition), Parameter(parameter::Parameter), ); @@ -1421,7 +1359,6 @@ impl TryFrom for RegistrableBox { Asset(asset) => Ok(RegistrableBox::Asset(asset)), Trigger(TriggerBox::Raw(trigger)) => Ok(RegistrableBox::Trigger(trigger)), Domain(_) - | PermissionTokenDefinition(_) | Account(_) | AssetDefinition(_) | Role(_) diff --git a/data_model/src/permission.rs b/data_model/src/permission.rs index db8c62c2a2d..9ad4bb94d6c 100644 --- a/data_model/src/permission.rs +++ b/data_model/src/permission.rs @@ -1,220 +1,117 @@ //! Permission Token and related impls #[cfg(not(feature = "std"))] -use alloc::{ - collections::{BTreeMap, BTreeSet}, - format, - string::String, - vec::Vec, -}; +use alloc::{collections::BTreeSet, format, string::String, vec::Vec}; #[cfg(feature = "std")] -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeSet; -use derive_more::{Constructor, Display, FromStr}; -use getset::Getters; -use iroha_data_model_derive::{model, IdEqOrdHash}; +use iroha_data_model_derive::model; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; -use serde_with::{DeserializeFromStr, SerializeDisplay}; pub use self::model::*; -use crate::{IdBox, Identifiable, Name, Value, ValueKind}; /// Collection of [`Token`]s pub type Permissions = BTreeSet; -/// Trait to identify [`ValueKind`] of a type which can be used as a [`Token`] parameter. -/// -/// On a higher level, all permission token parameters have [`Value`] type, but for now we allow -/// to define builtin permission tokens with stronger types. -/// This trait is used to retrieve the [`kind`](`ValueKind`) of a [`Value`] which can be constructed -/// from given parameter. -/// -/// Will be removed as well as builtin permission tokens and validators -/// when *runtime validators* and *runtime permissions* will be properly implemented. -pub trait ValueTrait: Into { - /// The kind of the [`Value`] which the implementing type can be converted to. - const TYPE: ValueKind; -} +use super::*; + +/// Unique id of [`PermissionToken`] +pub type PermissionTokenId = String; #[model] pub mod model { use super::*; - /// Unique id of [`PermissionTokenDefinition`] + /// Stored proof of the account having a permission for a certain action. #[derive( - derive_more::DebugCustom, - Display, + Debug, Clone, PartialEq, Eq, PartialOrd, Ord, - Hash, - Constructor, - FromStr, - Getters, Decode, Encode, - DeserializeFromStr, - SerializeDisplay, + Deserialize, + Serialize, IntoSchema, )] - #[repr(transparent)] - #[ffi_type(opaque)] - #[debug(fmt = "PermissionTokenId: {name}")] - pub struct PermissionTokenId { - /// [`PermissionToken`] name - #[getset(get = "pub")] - pub name: Name, - } - - /// Defines a type of [`PermissionToken`] with given id - #[derive(Display, Clone, IdEqOrdHash, Decode, Encode, Deserialize, Serialize, IntoSchema)] - #[display(fmt = "{id}")] #[ffi_type] - pub struct PermissionTokenDefinition { - /// Definition Id - pub id: PermissionTokenId, - /// Parameters and their types that every [`Token`] with this definition should have - pub params: BTreeMap, + pub struct PermissionToken { + /// Token identifier + pub definition_id: PermissionTokenId, + /// SCALE encoded token payload + pub payload: Vec, } - /// Stored proof of the account having a permission for a certain action. + /// Description of tokens defined in the validator #[derive( Debug, + Display, Clone, PartialEq, Eq, PartialOrd, Ord, - Getters, + Default, Decode, Encode, Deserialize, Serialize, IntoSchema, )] + #[display(fmt = "{token_ids:#?}")] #[ffi_type] - pub struct PermissionToken { - /// Name of the permission rule given to account. - #[getset(get = "pub")] - pub definition_id: PermissionTokenId, - /// Params identifying how this rule applies. - pub params: BTreeMap, + pub struct PermissionTokenSchema { + /// Ids of all permission tokens + pub token_ids: Vec, + /// Type schema of permission tokens + /// + /// At the time of writing this doc a complete schema is returned but it's + /// possible that in the future this field will contain references to types + /// defined in the Iroha schema without defining them itself + pub schema: String, } } -impl core::fmt::Debug for PermissionTokenDefinition { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut borrow_checker_happifier = f.debug_struct("PermissionTokenDefinition"); - let intermediate = borrow_checker_happifier.field("id", &self.id.name); - if self.params.is_empty() { - intermediate.finish() - } else { - intermediate.field("params", &self.params).finish() - } - } -} - -impl PermissionTokenDefinition { - /// Construct new [`PermissionTokenDefinition`] - #[inline] - pub const fn new(id: PermissionTokenId) -> Self { - Self { - id, - params: BTreeMap::new(), - } - } - - /// Add parameters to the [`PermissionTokenDefinition`] replacing any parameters previously defined - #[inline] - #[must_use] - pub fn with_params(mut self, params: impl IntoIterator) -> Self { - self.params = params.into_iter().collect(); - self - } - - /// Iterate over parameters of the [`PermissionTokenDefinition`] - /// - /// Values returned from the iterator are guaranteed to be in the alphabetical order. - #[inline] - pub fn params(&self) -> impl ExactSizeIterator { - self.params.iter() +// TODO: Use getset to derive this +impl PermissionTokenSchema { + /// Return id of this token + pub fn token_ids(&self) -> &[PermissionTokenId] { + &self.token_ids } } impl PermissionToken { - /// Construct a permission token. - #[inline] - pub fn new(definition_id: PermissionTokenId) -> Self { + /// Construct [`Self`] + pub fn new(definition_id: PermissionTokenId, payload: &T) -> Self { Self { definition_id, - params: BTreeMap::default(), + payload: payload.encode(), } } - /// Add parameters to the [`Token`] replacing any previously defined - #[inline] - #[must_use] - pub fn with_params(mut self, params: impl IntoIterator) -> Self { - self.params = params.into_iter().collect(); - self - } - - /// Return a reference to the parameter corresponding to the given name - #[inline] - pub fn param(&self, name: &Name) -> Option<&Value> { - self.params.get(name) + /// Return id of this token + // TODO: Use getset to derive this after fixes in FFI + pub fn definition_id(&self) -> &str { + &self.definition_id } - /// Get an iterator over [`Token`] parameters. - /// - /// Values returned from the iterator are guaranteed to be in the alphabetical order. - #[inline] - pub fn params(&self) -> impl ExactSizeIterator { - self.params.iter() + /// Payload of this token + // TODO: Use getset to derive this after fixes in FFI + pub fn payload(&self) -> &[u8] { + &self.payload } } impl core::fmt::Display for PermissionToken { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.definition_id)?; - - if !self.params.is_empty() { - write!(f, ": ")?; - - crate::utils::format_comma_separated( - self.params - .iter() - .map(|(name, value)| format!("`{name}`: `{value}`")), - ('[', ']'), - f, - )?; - } - - Ok(()) + write!(f, "{}", self.definition_id) } } -impl + Into> ValueTrait for I { - const TYPE: ValueKind = ValueKind::Id; -} - -macro_rules! impl_value_trait { - ( $($ty:ident: $kind:expr),+ $(,)? ) => {$( - impl ValueTrait for $ty { - const TYPE: ValueKind = $kind; - } - )+} -} - -impl_value_trait! { - u32: ValueKind::Numeric, - u128: ValueKind::Numeric -} - pub mod prelude { //! The prelude re-exports most commonly used traits, structs and macros from this crate. - pub use super::{PermissionToken, PermissionTokenDefinition, PermissionTokenId}; + pub use super::{PermissionToken, PermissionTokenId, PermissionTokenSchema}; } diff --git a/data_model/src/predicate.rs b/data_model/src/predicate.rs index 4c905f9e9dc..2a5e3efd370 100644 --- a/data_model/src/predicate.rs +++ b/data_model/src/predicate.rs @@ -368,7 +368,7 @@ pub mod string { IdBox::PeerId(id) => self.applies(&id.to_string()), IdBox::TriggerId(id) => self.applies(&id.to_string()), IdBox::RoleId(id) => self.applies(&id.to_string()), - IdBox::PermissionTokenDefinitionId(id) => self.applies(&id.to_string()), + IdBox::PermissionTokenId(id) => self.applies(&id.to_string()), IdBox::ParameterId(id) => self.applies(&id.to_string()), } } diff --git a/data_model/src/query.rs b/data_model/src/query.rs index 080fd529879..a41a2020041 100644 --- a/data_model/src/query.rs +++ b/data_model/src/query.rs @@ -58,6 +58,7 @@ pub mod model { use iroha_crypto::HashOf; use super::*; + use crate::permission::PermissionTokenId; /// Sized container for all possible Queries. #[allow(clippy::enum_variant_names)] @@ -300,11 +301,13 @@ pub mod permission { use derive_more::Display; - use crate::{permission, prelude::*}; + use crate::{ + permission::{self, PermissionTokenSchema}, + prelude::*, + }; queries! { - /// [`FindAllPermissionTokenDefinitions`] Iroha Query finds all registered - /// [`PermissionTokenDefinition`][crate::permission::PermissionTokenDefinition]s + /// Finds all registered permission tokens #[derive(Copy, Display)] #[ffi_type] pub struct FindAllPermissionTokenDefinitions; @@ -329,12 +332,12 @@ pub mod permission { /// `Id` of an account to check. pub account_id: EvaluatesTo, /// `PermissionToken` to check for. - pub permission_token: permission::PermissionToken, + pub permission_token: EvaluatesTo, } } impl Query for FindAllPermissionTokenDefinitions { - type Output = Vec; + type Output = PermissionTokenSchema; } impl Query for FindPermissionTokensByAccountId { @@ -349,11 +352,11 @@ pub mod permission { /// Construct [`DoesAccountHavePermissionToken`]. pub fn new( account_id: impl Into>, - permission_token: permission::PermissionToken, + permission_token: impl Into>, ) -> Self { Self { account_id: account_id.into(), - permission_token, + permission_token: permission_token.into(), } } } @@ -1476,31 +1479,6 @@ pub mod error { Unauthorized, } - /// Type assertion error - #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Deserialize, - Serialize, - Decode, - Encode, - IntoSchema, - )] - #[ffi_type] - #[display( - fmt = "Failed to find permission token `{permission_token_id}` for account `{account_id}`" - )] - #[cfg_attr(feature = "std", derive(thiserror::Error))] - pub struct PermissionTokenFindError { - pub account_id: AccountId, - pub permission_token_id: PermissionTokenId, - } - /// Type assertion error #[derive( Debug, @@ -1550,13 +1528,9 @@ pub mod error { /// Failed to find Role by id. #[display(fmt = "Failed to find role by id: `{_0}`")] Role(RoleId), - /// Failed to find [`PermissionTokenDefinition`] by id. - #[display(fmt = "Failed to find permission token definition by id: `{_0}`")] - PermissionTokenDefinition(PermissionTokenId), - /// Failed to find [`PermissionToken`] for [`AccountId`]. - #[cfg_attr(not(feature = "std"), display(fmt = "Failed to find permission token"))] - #[cfg_attr(feature = "std", error(transparent))] - PermissionToken(PermissionTokenFindError), + /// Failed to find [`PermissionToken`] by id. + #[display(fmt = "Failed to find permission token by id: `{_0}`")] + PermissionToken(PermissionTokenId), /// Failed to find specified [`Parameter`] variant. #[display(fmt = "Failed to find specified parameter variant: `{_0}`")] Parameter(ParameterId), diff --git a/data_model/src/role.rs b/data_model/src/role.rs index 22b8d364156..99185df6d4d 100644 --- a/data_model/src/role.rs +++ b/data_model/src/role.rs @@ -72,7 +72,7 @@ pub mod model { )] #[serde(transparent)] #[repr(transparent)] - #[ffi_type(opaque)] + #[ffi_type(unsafe {robust})] pub struct NewRole { #[allow(missing_docs)] #[id(transparent)] diff --git a/data_model/src/visit.rs b/data_model/src/visit.rs index 650379526c7..161573745ef 100644 --- a/data_model/src/visit.rs +++ b/data_model/src/visit.rs @@ -300,7 +300,7 @@ pub fn visit_instruction( pub fn visit_expression( visitor: &mut V, - authority: &::Id, + authority: &AccountId, expression: &EvaluatesTo, ) { macro_rules! visit_binary_math_expression { diff --git a/default_validator/src/lib.rs b/default_validator/src/lib.rs index f0c3516d04e..0d932260e23 100644 --- a/default_validator/src/lib.rs +++ b/default_validator/src/lib.rs @@ -1,8 +1,6 @@ //! Iroha default validator. #![no_std] -extern crate alloc; - #[cfg(not(test))] extern crate panic_halt; @@ -10,8 +8,8 @@ use iroha_validator::prelude::*; /// Entrypoint to return permission token definitions defined in this validator. #[entrypoint] -pub fn permission_tokens() -> Vec { - DefaultValidator::permission_tokens() +pub fn permission_token_schema() -> PermissionTokenSchema { + DefaultValidator::permission_token_schema() } /// Validation entrypoint diff --git a/docs/source/references/schema.json b/docs/source/references/schema.json index b35b4421063..3a9fe06d2f1 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -167,7 +167,7 @@ }, { "name": "permission_id", - "type": "PermissionTokenId" + "type": "String" } ] }, @@ -898,7 +898,7 @@ { "tag": "PermissionToken", "discriminant": 7, - "type": "PermissionTokenEvent" + "type": "PermissionTokenSchemaUpdateEvent" }, { "tag": "Configuration", @@ -932,7 +932,7 @@ }, { "name": "permission_token", - "type": "PermissionToken" + "type": "EvaluatesTo" } ] }, @@ -1146,6 +1146,14 @@ } ] }, + "EvaluatesTo": { + "Struct": [ + { + "name": "expression", + "type": "Expression" + } + ] + }, "EvaluatesTo": { "Struct": [ { @@ -1962,24 +1970,19 @@ "discriminant": 9, "type": "RoleId" }, - { - "tag": "PermissionTokenDefinition", - "discriminant": 10, - "type": "PermissionTokenId" - }, { "tag": "PermissionToken", - "discriminant": 11, - "type": "PermissionTokenFindError" + "discriminant": 10, + "type": "String" }, { "tag": "Parameter", - "discriminant": 12, + "discriminant": 11, "type": "ParameterId" }, { "tag": "PublicKey", - "discriminant": 13, + "discriminant": 12, "type": "PublicKey" } ] @@ -2173,9 +2176,9 @@ "type": "RoleId" }, { - "tag": "PermissionTokenDefinitionId", + "tag": "PermissionTokenId", "discriminant": 7, - "type": "PermissionTokenId" + "type": "String" }, { "tag": "ParameterId", @@ -2241,14 +2244,9 @@ "discriminant": 10, "type": "Role" }, - { - "tag": "PermissionTokenDefinition", - "discriminant": 11, - "type": "PermissionTokenDefinition" - }, { "tag": "Parameter", - "discriminant": 12, + "discriminant": 11, "type": "Parameter" } ] @@ -3175,8 +3173,8 @@ "type": "RoleId" }, { - "name": "permission_definition_id", - "type": "PermissionTokenId" + "name": "permission_token_id", + "type": "String" } ] }, @@ -3184,57 +3182,35 @@ "Struct": [ { "name": "definition_id", - "type": "PermissionTokenId" + "type": "String" }, { - "name": "params", - "type": "SortedMap" + "name": "payload", + "type": "Vec" } ] }, - "PermissionTokenDefinition": { + "PermissionTokenSchema": { "Struct": [ { - "name": "id", - "type": "PermissionTokenId" - }, - { - "name": "params", - "type": "SortedMap" - } - ] - }, - "PermissionTokenEvent": { - "Enum": [ - { - "tag": "DefinitionCreated", - "discriminant": 0, - "type": "PermissionTokenDefinition" + "name": "token_ids", + "type": "Vec" }, { - "tag": "DefinitionDeleted", - "discriminant": 1, - "type": "PermissionTokenDefinition" + "name": "schema", + "type": "String" } ] }, - "PermissionTokenFindError": { + "PermissionTokenSchemaUpdateEvent": { "Struct": [ { - "name": "account_id", - "type": "AccountId" + "name": "old_schema", + "type": "PermissionTokenSchema" }, { - "name": "permission_token_id", - "type": "PermissionTokenId" - } - ] - }, - "PermissionTokenId": { - "Struct": [ - { - "name": "name", - "type": "Name" + "name": "new_schema", + "type": "PermissionTokenSchema" } ] }, @@ -4049,12 +4025,6 @@ "value": "Value" } }, - "SortedMap": { - "Map": { - "key": "Name", - "value": "ValueKind" - } - }, "SortedVec": { "Vec": "PermissionToken" }, @@ -4557,130 +4527,47 @@ "type": "PermissionToken" }, { - "tag": "Hash", + "tag": "PermissionTokenSchema", "discriminant": 14, + "type": "PermissionTokenSchema" + }, + { + "tag": "Hash", + "discriminant": 15, "type": "HashValue" }, { "tag": "Block", - "discriminant": 15, + "discriminant": 16, "type": "VersionedCommittedBlock" }, { "tag": "BlockHeader", - "discriminant": 16, + "discriminant": 17, "type": "BlockHeader" }, { "tag": "Ipv4Addr", - "discriminant": 17, + "discriminant": 18, "type": "Ipv4Addr" }, { "tag": "Ipv6Addr", - "discriminant": 18, + "discriminant": 19, "type": "Ipv6Addr" }, { "tag": "Numeric", - "discriminant": 19, + "discriminant": 20, "type": "NumericValue" }, { "tag": "Validator", - "discriminant": 20, + "discriminant": 21, "type": "Validator" } ] }, - "ValueKind": { - "Enum": [ - { - "tag": "Bool", - "discriminant": 0 - }, - { - "tag": "String", - "discriminant": 1 - }, - { - "tag": "Name", - "discriminant": 2 - }, - { - "tag": "Vec", - "discriminant": 3 - }, - { - "tag": "LimitedMetadata", - "discriminant": 4 - }, - { - "tag": "MetadataLimits", - "discriminant": 5 - }, - { - "tag": "TransactionLimits", - "discriminant": 6 - }, - { - "tag": "LengthLimits", - "discriminant": 7 - }, - { - "tag": "Id", - "discriminant": 8 - }, - { - "tag": "Identifiable", - "discriminant": 9 - }, - { - "tag": "PublicKey", - "discriminant": 10 - }, - { - "tag": "SignatureCheckCondition", - "discriminant": 11 - }, - { - "tag": "TransactionQueryResult", - "discriminant": 12 - }, - { - "tag": "PermissionToken", - "discriminant": 13 - }, - { - "tag": "Hash", - "discriminant": 14 - }, - { - "tag": "Block", - "discriminant": 15 - }, - { - "tag": "BlockHeader", - "discriminant": 16 - }, - { - "tag": "Ipv4Addr", - "discriminant": 17 - }, - { - "tag": "Ipv6Addr", - "discriminant": 18 - }, - { - "tag": "Numeric", - "discriminant": 19 - }, - { - "tag": "Validator", - "discriminant": 20 - } - ] - }, "ValueOfKey": { "Struct": [ { @@ -4748,6 +4635,9 @@ "Vec": { "Vec": "PeerId" }, + "Vec": { + "Vec": "String" + }, "Vec": { "Vec": "TransactionValue" }, diff --git a/schema/gen/src/lib.rs b/schema/gen/src/lib.rs index 906bb6f60fd..c9cd6e05d05 100644 --- a/schema/gen/src/lib.rs +++ b/schema/gen/src/lib.rs @@ -94,7 +94,6 @@ types!( BTreeMap, BTreeMap>, BTreeMap, - BTreeMap, BTreeSet, BTreeSet, BTreeSet, @@ -294,9 +293,8 @@ types!( PeerId, PermissionRemoved, PermissionToken, - PermissionTokenDefinition, - PermissionTokenEvent, - PermissionTokenId, + PermissionTokenSchema, + PermissionTokenSchemaUpdateEvent, PipelineEntityKind, PipelineEvent, PipelineEventFilter, @@ -364,7 +362,6 @@ types!( Validator, ValidatorEvent, Value, - ValueKind, ValueOfKey, ValuePredicate, Vec, @@ -433,7 +430,7 @@ mod tests { query::error::{FindError, QueryExecutionFail}, transaction::{error::TransactionLimitError, SignedTransaction, TransactionLimits}, validator::Validator, - ValueKind, VersionedCommittedBlockWrapper, + VersionedCommittedBlockWrapper, }; use iroha_genesis::RawGenesisBlock; use iroha_primitives::{ diff --git a/schema/src/lib.rs b/schema/src/lib.rs index 081c131f2a2..9355bd3a366 100644 --- a/schema/src/lib.rs +++ b/schema/src/lib.rs @@ -25,7 +25,7 @@ pub use iroha_schema_derive::*; use serde::Serialize; /// Helper struct for building a full schema -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct MetaMap(pub(crate) btree_map::BTreeMap); impl PartialEq> for MetaMap { @@ -47,6 +47,10 @@ impl MetaMap { pub fn contains_key(&self) -> bool { self.0.contains_key(&Self::key::()) } + /// Remove a key-value pair from the map. + pub fn remove(&mut self) -> bool { + self.0.remove(&Self::key::()).is_some() + } /// Insert a key-value pair into the map. pub fn insert(&mut self, v: Metadata) -> bool { self.0 @@ -87,6 +91,14 @@ pub trait IntoSchema: TypeId { /// Insert descriptions of types referenced by [`Self`] fn update_schema_map(metamap: &mut MetaMap); + /// Remove description of types referenced by [`Self`] + fn remove_from_schema(metamap: &mut MetaMap) -> bool + where + Self: Sized, + { + metamap.remove::() + } + /// Return schema map of types referenced by [`Self`] fn schema() -> MetaMap { let mut map = MetaMap::new(); diff --git a/tools/kagami/Cargo.toml b/tools/kagami/Cargo.toml index aa574ce3382..b1a6e6d5cc9 100644 --- a/tools/kagami/Cargo.toml +++ b/tools/kagami/Cargo.toml @@ -33,6 +33,7 @@ supports-color = "2.0.0" inquire = "0.6.2" duct = "0.13.6" tempfile = "3.6.0" +parity-scale-codec = { version = "3.2.1", default-features = false } [build-dependencies] eyre = "0.6.8" diff --git a/tools/kagami/src/genesis.rs b/tools/kagami/src/genesis.rs index 82a2cd31ece..b957ec2aad8 100644 --- a/tools/kagami/src/genesis.rs +++ b/tools/kagami/src/genesis.rs @@ -130,25 +130,22 @@ pub fn generate_default(validator_path: Option) -> color_eyre::Result::Id::from_str("alice@wonderland")?; let grant_permission_to_set_parameters = GrantBox::new( - PermissionToken::new("can_set_parameters".parse()?), + PermissionToken { + definition_id: "CanSetParameters".to_owned(), + payload: Vec::new(), + }, alice_id, ); let register_user_metadata_access = RegisterBox::new( Role::new("ALICE_METADATA_ACCESS".parse()?) - .add_permission( - PermissionToken::new("can_set_key_value_in_user_account".parse()?).with_params([( - "account_id".parse()?, - IdBox::AccountId("alice@wonderland".parse()?).into(), - )]), - ) - .add_permission( - PermissionToken::new("can_remove_key_value_in_user_account".parse()?).with_params( - [( - "account_id".parse()?, - IdBox::AccountId("alice@wonderland".parse()?).into(), - )], - ), - ), + .add_permission(PermissionToken::new( + "CanSetKeyValueInUserAccount".to_owned(), + &"alice@wonderland".parse::()?, + )) + .add_permission(PermissionToken::new( + "CanRemoveKeyValueInUserAccount".to_owned(), + &"alice@wonderland".parse::()?, + )), ) .into(); diff --git a/tools/parity_scale_decoder/samples/trigger.bin b/tools/parity_scale_decoder/samples/trigger.bin index 895dafcfa03bd85ab086a742cf1eb9cba0af57de..99b44b464041e5d82e98cce35e3d10a5bc67e3b0 100644 GIT binary patch delta 22 dcmWFt5Kzg@%qxj6$}di3U|?b56`3gD002p#1+4%8 delta 22 dcmWFt5Kzg@%qxj6$}di3U|?b56`m;I002px1*`x7 diff --git a/tools/parity_scale_decoder/src/main.rs b/tools/parity_scale_decoder/src/main.rs index b80d6578fe4..cf9f57679ae 100644 --- a/tools/parity_scale_decoder/src/main.rs +++ b/tools/parity_scale_decoder/src/main.rs @@ -43,7 +43,7 @@ use iroha_data_model::{ query::error::{FindError, QueryExecutionFail}, transaction::{error::TransactionLimitError, SignedTransaction, TransactionLimits}, validator::Validator, - ValueKind, VersionedCommittedBlockWrapper, + VersionedCommittedBlockWrapper, }; use iroha_primitives::{ addr::{Ipv4Addr, Ipv6Addr}, diff --git a/wasm/derive/src/lib.rs b/wasm/derive/src/lib.rs index dc871b2144f..1578cba49d5 100644 --- a/wasm/derive/src/lib.rs +++ b/wasm/derive/src/lib.rs @@ -56,7 +56,7 @@ mod entrypoint; /// use iroha_wasm::{data_model::prelude::*, dbg}; /// /// #[iroha_wasm::main(params = "[authority]")] -/// fn main(authority: ::Id) { +/// fn main(authority: AccountId) { /// dbg(&format!("Trigger authority: {authority}")); /// } /// ``` @@ -66,7 +66,7 @@ mod entrypoint; /// use iroha_wasm::{data_model::prelude::*, dbg}; /// /// #[iroha_wasm::main(params = "[authority, triggering_event]")] -/// fn main(authority: ::Id, event: DataEvent) { +/// fn main(authority: AccountId, event: DataEvent) { /// dbg(&format!( /// "Trigger authority: {authority};\n\ /// Triggering event: {event:?}" diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 8a3c75f594b..f5b42f44d40 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -154,7 +154,7 @@ impl iroha_data_model::evaluate::Context for Context { } /// Query the authority of the smart contract -pub fn query_authority() -> ::Id { +pub fn query_authority() -> AccountId { #[cfg(not(test))] use host::query_authority as host_query_authority; #[cfg(test)] @@ -377,7 +377,7 @@ mod tests { let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); FindAccountById::new(account_id).into() } - fn get_test_authority() -> ::Id { + fn get_test_authority() -> AccountId { "alice@wonderland".parse().expect("Valid") } fn get_test_expression() -> EvaluatesTo { @@ -390,10 +390,9 @@ mod tests { .into() } fn get_test_operation() -> NeedsValidationBox { - let alice_id: ::Id = "alice@wonderland".parse().expect("Valid"); - let rose_definition_id: ::Id = - "rose#wonderland".parse().expect("Valid"); - let alice_rose_id = ::Id::new(rose_definition_id, alice_id); + let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let rose_definition_id: AssetDefinitionId = "rose#wonderland".parse().expect("Valid"); + let alice_rose_id = AssetId::new(rose_definition_id, alice_id); NeedsValidationBox::Instruction(MintBox::new(1u32, alice_rose_id).into()) } diff --git a/wasm/validator/Cargo.toml b/wasm/validator/Cargo.toml index ab327c1a09b..bf408ccb9b0 100644 --- a/wasm/validator/Cargo.toml +++ b/wasm/validator/Cargo.toml @@ -15,7 +15,11 @@ default-validator = [] [dependencies] iroha_wasm = { version = "2.0.0-pre-rc.16", path = ".." } +iroha_schema = { path = "../../schema", version = "=2.0.0-pre-rc.16" } iroha_validator_derive = { version = "2.0.0-pre-rc.16", path = "derive" } +parity-scale-codec = { version = "3.1.5", default-features = false } +serde_json = { version = "1.0.91", default-features = false } + [dev-dependencies] webassembly-test.workspace = true diff --git a/wasm/validator/derive/src/entrypoint.rs b/wasm/validator/derive/src/entrypoint.rs index c35f6eb6525..32bcf5b4f61 100644 --- a/wasm/validator/derive/src/entrypoint.rs +++ b/wasm/validator/derive/src/entrypoint.rs @@ -66,10 +66,12 @@ pub fn impl_entrypoint(attr: TokenStream, item: TokenStream) -> TokenStream { match &fn_item.sig.ident { fn_name if fn_name == "validate" => impl_validate_entrypoint(attr, fn_item), - fn_name if fn_name == "permission_tokens" => { - impl_permission_tokens_entrypoint(&attr, fn_item) + fn_name if fn_name == "permission_token_schema" => { + impl_permission_token_schema_entrypoint(&attr, fn_item) } - _ => panic!("Validator entrypoint name should be either `validate` or `permission_tokens`"), + _ => panic!( + "Validator entrypoint name should be either `validate` or `permission_token_schema`" + ), } } @@ -117,10 +119,9 @@ fn impl_validate_entrypoint(attr: TokenStream, fn_item: syn::ItemFn) -> TokenStr #[no_mangle] #[doc(hidden)] unsafe extern "C" fn _iroha_validator_validate() -> *const u8 { - let verdict: ::iroha_validator::iroha_wasm::data_model::validator::Result = #fn_name(#args); - let bytes_box = ::core::mem::ManuallyDrop::new(::iroha_validator::iroha_wasm::encode_with_length_prefix(&verdict)); - - bytes_box.as_ptr() + let verdict: ::iroha_validator::data_model::validator::Result = #fn_name(#args); + let bytes = ::iroha_validator::iroha_wasm::encode_with_length_prefix(&verdict); + ::core::mem::ManuallyDrop::new(bytes).as_ptr() } // NOTE: Host objects are always passed by value to wasm @@ -132,7 +133,10 @@ fn impl_validate_entrypoint(attr: TokenStream, fn_item: syn::ItemFn) -> TokenStr .into() } -fn impl_permission_tokens_entrypoint(attr: &TokenStream, fn_item: syn::ItemFn) -> TokenStream { +fn impl_permission_token_schema_entrypoint( + attr: &TokenStream, + fn_item: syn::ItemFn, +) -> TokenStream { let syn::ItemFn { attrs, vis, @@ -143,28 +147,28 @@ fn impl_permission_tokens_entrypoint(attr: &TokenStream, fn_item: syn::ItemFn) - assert!( matches!(sig.output, syn::ReturnType::Type(_, _)), - "Validator `permission_tokens()` entrypoint must have `Vec` return type" + "Validator `permission_token_schema()` entrypoint must have `PermissionTokenSchema>` return type" ); assert!( attr.is_empty(), - "`#[entrypoint]` macro for Validator `permission_tokens` entrypoint accepts no attributes" + "`#[entrypoint]` macro for Validator `permission_token_schema` entrypoint accepts no attributes" ); quote! { - /// Validator `permission_tokens` entrypoint + /// Validator `permission_token_schema` entrypoint /// /// # Memory safety /// /// This function transfers the ownership of allocated [`Vec`](alloc::vec::Vec). #[no_mangle] #[doc(hidden)] - unsafe extern "C" fn _iroha_validator_permission_tokens() -> *const u8 { - let v: ::alloc::vec::Vec< - ::iroha_validator::data_model::permission::PermissionTokenDefinition - > = #fn_name(); - let bytes_box = ::core::mem::ManuallyDrop::new(::iroha_validator::iroha_wasm::encode_with_length_prefix(&v)); + unsafe extern "C" fn _iroha_validator_permission_token_schema() -> *const u8 { + let tokens: ::iroha_validator::PermissionTokenSchema = #fn_name(); + let bytes = ::iroha_validator::iroha_wasm::encode_with_length_prefix( + &tokens.serialize() + ); - bytes_box.as_ptr() + ::core::mem::ManuallyDrop::new(bytes).as_ptr() } // NOTE: Host objects are always passed by value to wasm @@ -172,5 +176,6 @@ fn impl_permission_tokens_entrypoint(attr: &TokenStream, fn_item: syn::ItemFn) - #(#attrs)* #vis #sig #block - }.into() + } + .into() } diff --git a/wasm/validator/derive/src/lib.rs b/wasm/validator/derive/src/lib.rs index 8e3baaf03a9..75f9c6f5a8d 100644 --- a/wasm/validator/derive/src/lib.rs +++ b/wasm/validator/derive/src/lib.rs @@ -100,11 +100,11 @@ pub fn entrypoint(attr: TokenStream, item: TokenStream) -> TokenStream { /// #[validate(permission::asset::Owner)] /// struct CanDoSomethingWithAsset { /// some_data: String, -/// asset_id: ::Id, +/// asset_id: AssetId, /// } /// /// #[entrypoint(params = "[authority, operation]")] -/// fn validate(authority: ::Id, operation: NeedsValidationBox) -> Result { +/// fn validate(authority: AccountId, operation: NeedsValidationBox) -> Result { /// let NeedsValidationBox::Instruction(instruction) = operation else { /// pass!(); /// }; @@ -113,7 +113,7 @@ pub fn entrypoint(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// CanDoSomethingWithAsset { /// some_data: "some data".to_owned(), -/// asset_id: parse!("rose#wonderland" as ::Id), +/// asset_id: parse!("rose#wonderland" as AssetId), /// }.is_owned_by(&authority) /// } /// ``` diff --git a/wasm/validator/derive/src/token.rs b/wasm/validator/derive/src/token.rs index 9d2449f44d7..569303227dd 100644 --- a/wasm/validator/derive/src/token.rs +++ b/wasm/validator/derive/src/token.rs @@ -7,33 +7,11 @@ use super::*; /// [`derive_token`](crate::derive_token()) macro implementation pub fn impl_derive_token(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let ident = input.ident; + let generics = &input.generics; + let ident = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - - let syn::Data::Struct(syn::DataStruct { fields, .. }) = input.data else { - panic!("`Token` can be derived only for structs"); - }; - let extracted_fields = match fields { - syn::Fields::Named(syn::FieldsNamed { named, .. }) => named, - syn::Fields::Unit => syn::punctuated::Punctuated::default(), - _ => panic!("`Token` can be derived only for structs with named fields or unit structs"), - }; - - let impl_token = impl_token( - &impl_generics, - &ty_generics, - where_clause, - &ident, - &extracted_fields, - ); - let impl_try_from_permission_token = impl_try_from_permission_token( - &impl_generics, - &ty_generics, - where_clause, - &ident, - &extracted_fields, - ); + let impl_token = impl_token(ident, generics); + let impl_try_from_permission_token = impl_try_from_permission_token(ident, generics); quote! { #impl_token @@ -42,39 +20,25 @@ pub fn impl_derive_token(input: TokenStream) -> TokenStream { .into() } -fn impl_token( - impl_generics: &syn::ImplGenerics<'_>, - ty_generics: &syn::TypeGenerics<'_>, - where_clause: Option<&syn::WhereClause>, - ident: &syn::Ident, - fields: &syn::punctuated::Punctuated, -) -> proc_macro2::TokenStream { - let definition = gen_definition(ident, fields); - let permission_token_conversion_code = permission_token_conversion(fields); +fn gen_token_definition_id() -> proc_macro2::TokenStream { + quote! { ::type_name() } +} - quote! { - impl #impl_generics ::iroha_validator::permission::Token for #ident #ty_generics - #where_clause - { - fn definition() -> ::iroha_validator::data_model::permission::PermissionTokenDefinition { - #definition - } +fn impl_token(ident: &syn::Ident, generics: &syn::Generics) -> proc_macro2::TokenStream { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let token_definition_id = gen_token_definition_id(); - fn is_owned_by( - &self, - account_id: &< - ::iroha_validator::data_model::prelude::Account - as - ::iroha_validator::data_model::prelude::Identifiable - >::Id - ) -> bool { - let permission_token = #permission_token_conversion_code; + quote! { + impl #impl_generics ::iroha_validator::permission::Token for #ident #ty_generics #where_clause { + fn is_owned_by(&self, account_id: &::iroha_validator::data_model::prelude::AccountId) -> bool { + let permission_token = ::iroha_validator::data_model::permission::PermissionToken::new( + #token_definition_id, self + ); let value = ::iroha_validator::iroha_wasm::debug::DebugExpectExt::dbg_expect( ::iroha_validator::iroha_wasm::QueryHost::execute( &::iroha_validator::iroha_wasm::data_model::prelude::DoesAccountHavePermissionToken::new( - account_id.clone(), - permission_token, + account_id.clone(), permission_token, ) ), "Failed to execute `DoesAccountHavePermissionToken` query" @@ -88,121 +52,26 @@ fn impl_token( } } -fn gen_definition( - ident: &syn::Ident, - fields: &syn::punctuated::Punctuated, -) -> proc_macro2::TokenStream { - use heck::ToSnakeCase as _; - - let definition_id = proc_macro2::Literal::string(&ident.to_string().to_snake_case()); - - let params = fields.iter().map(|field| { - let ident = field.ident.as_ref().expect("Field must have an identifier"); - let name = proc_macro2::Literal::string(&ident.to_string().to_snake_case()); - let ty = &field.ty; - - quote! { - ( - ::iroha_validator::parse!(#name as ::iroha_validator::data_model::prelude::Name), - <#ty as ::iroha_validator::data_model::AssociatedConstant< - ::iroha_validator::data_model::ValueKind - >>::VALUE - ) - } - }); - - quote! { - ::iroha_validator::data_model::permission::PermissionTokenDefinition::new( - ::iroha_validator::parse!( - #definition_id as < - ::iroha_validator::data_model::permission::PermissionTokenDefinition - as - ::iroha_validator::data_model::prelude::Identifiable - >::Id - ) - ) - .with_params([#(#params),*]) - } -} - fn impl_try_from_permission_token( - impl_generics: &syn::ImplGenerics<'_>, - ty_generics: &syn::TypeGenerics<'_>, - where_clause: Option<&syn::WhereClause>, ident: &syn::Ident, - fields: &syn::punctuated::Punctuated, + generics: &syn::Generics, ) -> proc_macro2::TokenStream { - let field_initializers = fields.iter().map(|field| { - let field_ident = field.ident.as_ref().expect("Field must have an identifier"); - let field_literal = proc_macro2::Literal::string(&field_ident.to_string()); - let field_type = &field.ty; - - let code = quote! { - #field_ident: < - #field_type - as - ::core::convert::TryFrom<::iroha_validator::data_model::prelude::Value> - >::try_from(token - .param(&::iroha_validator::parse!(#field_literal as ::iroha_validator::data_model::prelude::Name)) - .ok_or( - ::iroha_validator::permission::PermissionTokenConversionError::Param(#field_literal) - )? - .clone() - ) - .map_err(|err| { - ::iroha_validator::permission::PermissionTokenConversionError::Value( - ::alloc::string::ToString::to_string(&err) - ) - })? - }; - code - }); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let token_definition_id = gen_token_definition_id(); quote! { - impl #impl_generics ::core::convert::TryFrom<::iroha_validator::data_model::permission::PermissionToken> for #ident #ty_generics - #where_clause - { + impl #impl_generics ::core::convert::TryFrom<::iroha_validator::data_model::permission::PermissionToken> for #ident #ty_generics #where_clause { type Error = ::iroha_validator::permission::PermissionTokenConversionError; - #[allow(unused)] // `params` can be unused if token has none - fn try_from( - token: ::iroha_validator::data_model::permission::PermissionToken - ) -> ::core::result::Result { - if token.definition_id() != - ::definition().id() - { + fn try_from(token: ::iroha_validator::data_model::permission::PermissionToken) -> ::core::result::Result { + if #token_definition_id != *token.definition_id() { return Err(::iroha_validator::permission::PermissionTokenConversionError::Id( - token.definition_id().clone() + ::alloc::borrow::ToOwned::to_owned(token.definition_id()) )); } - Ok(Self { - #(#field_initializers),* - }) + Ok(::decode_all(&mut token.payload()).unwrap()) } } } } - -#[allow(clippy::arithmetic_side_effects)] // Triggers on quote! side -fn permission_token_conversion( - fields: &syn::punctuated::Punctuated, -) -> proc_macro2::TokenStream { - let params = fields.iter().cloned().map(|field| { - let field_ident = field.ident.as_ref().expect("Field must have an identifier"); - let field_literal = proc_macro2::Literal::string(&field_ident.to_string()); - quote! {( - ::iroha_validator::parse!(#field_literal as ::iroha_validator::data_model::prelude::Name), - self.#field_ident.clone().into(), - )} - }); - - quote! { - ::iroha_validator::data_model::permission::PermissionToken::new( - ::definition().id().clone() - ) - .with_params([ - #(#params),* - ]) - } -} diff --git a/wasm/validator/derive/src/validate.rs b/wasm/validator/derive/src/validate.rs index ae8f9ee74a2..72e37caa27a 100644 --- a/wasm/validator/derive/src/validate.rs +++ b/wasm/validator/derive/src/validate.rs @@ -194,10 +194,7 @@ fn gen_validate_impl(isi_name: IsiName, pass_condition: &Type) -> proc_macro2::T #[doc = #pass_condition_str] #[doc = "`]"] #[inline] - fn #fn_name( - &self, - authority: &<::iroha_validator::data_model::account::Account as ::iroha_validator::data_model::prelude::Identifiable>::Id - ) -> ::iroha_validator::data_model::validator::Result { + fn #fn_name(&self, authority: &::iroha_validator::data_model::account::AccountId) -> ::iroha_validator::data_model::validator::Result { let condition = <#pass_condition as ::core::convert::From<&Self>>::from(&self); < #pass_condition diff --git a/wasm/validator/src/default.rs b/wasm/validator/src/default.rs index 87e83bf4223..8978f1021b9 100644 --- a/wasm/validator/src/default.rs +++ b/wasm/validator/src/default.rs @@ -1,7 +1,7 @@ //! Definition of Iroha default validator and accompanying validation functions #![allow(missing_docs, clippy::missing_errors_doc)] -use alloc::{borrow::ToOwned as _, vec::Vec}; +use alloc::{borrow::ToOwned as _, format, string::String, vec::Vec}; use account::{ visit_burn_account_public_key, visit_mint_account_public_key, @@ -71,59 +71,31 @@ macro_rules! map_all_crate_tokens { }; } -macro_rules! tokens { - ( - pattern = { - $(#[$meta:meta])* - $vis:vis struct _ { - $( - $(#[$field_meta:meta])* - $field_vis:vis $field:ident: $field_type:ty - ),* $(,)? - } - }, - $module:ident :: tokens: [$($name:ident),+ $(,)?] - ) => { - declare_tokens!($( - crate::default::$module::tokens::$name - ),+); - - pub mod tokens { - use super::*; - - macro_rules! single_token { - ($name_internal:ident) => { - $(#[$meta])* - $vis struct $name_internal { - $( - $(#[$field_meta])* - $field_vis $field: $field_type - ),* - } - }; - } - - $(single_token!($name);)+ - } +macro_rules! token { + ($($meta:meta)* $item:item) => { + #[derive(parity_scale_codec::Decode, parity_scale_codec::Encode)] + #[derive(iroha_schema::IntoSchema)] + #[derive(Clone, Token)] + $($meta)* + $item }; } pub(crate) use map_all_crate_tokens; -pub(crate) use tokens; impl Validate for DefaultValidator { - fn permission_tokens() -> Vec { - let mut v = Vec::new(); + fn permission_token_schema() -> PermissionTokenSchema { + let mut schema = PermissionTokenSchema::default(); - macro_rules! add_to_vec { + macro_rules! add_to_schema { ($token_ty:ty) => { - v.push(<$token_ty as ::iroha_validator::permission::Token>::definition()); + schema.insert::<$token_ty>(); }; } - map_all_crate_tokens!(add_to_vec); + map_all_crate_tokens!(add_to_schema); - v + schema } fn verdict(&self) -> &Result { @@ -353,7 +325,7 @@ fn visit_unsupported( pub fn visit_expression( validator: &mut V, - authority: &::Id, + authority: &AccountId, expression: &EvaluatesTo, ) { macro_rules! visit_binary_expression { @@ -451,17 +423,19 @@ pub fn visit_sequence( pub mod peer { use super::*; - tokens!( - pattern = { - #[derive(Token, ValidateGrantRevoke)] + declare_tokens! { + crate::default::peer::tokens::CanUnregisterAnyPeer, + } + + pub mod tokens { + use super::*; + + token! { + #[derive(Copy, ValidateGrantRevoke)] #[validate(permission::OnlyGenesis)] - #[derive(Clone, Copy)] - pub struct _ {} - }, - peer::tokens: [ - CanUnregisterAnyPeer, - ] - ); + pub struct CanUnregisterAnyPeer; + } + } #[allow(clippy::needless_pass_by_value)] pub fn visit_unregister_peer( @@ -470,7 +444,7 @@ pub mod peer { _isi: Unregister, ) { const CAN_UNREGISTER_PEER_TOKEN: tokens::CanUnregisterAnyPeer = - tokens::CanUnregisterAnyPeer {}; + tokens::CanUnregisterAnyPeer; if CAN_UNREGISTER_PEER_TOKEN.is_owned_by(authority) { pass!(validator); @@ -483,21 +457,40 @@ pub mod peer { pub mod domain { use super::*; - // TODO: We probably need a better way to allow accounts to modify domains. - tokens!( - pattern = { - #[derive(Token, ValidateGrantRevoke)] + declare_tokens! { + crate::default::domain::tokens::CanUnregisterDomain, + crate::default::domain::tokens::CanSetKeyValueInDomain, + crate::default::domain::tokens::CanRemoveKeyValueInDomain, + } + + pub mod tokens { + // TODO: We probably need a better way to allow accounts to modify domains. + use super::*; + + token! { + #[derive(ValidateGrantRevoke)] #[validate(permission::OnlyGenesis)] - pub struct _ { - pub domain_id: ::Id, + pub struct CanUnregisterDomain { + pub domain_id: DomainId, } - }, - domain::tokens: [ - CanUnregisterDomain, - CanSetKeyValueInDomain, - CanRemoveKeyValueInDomain, - ] - ); + } + + token! { + #[derive(ValidateGrantRevoke)] + #[validate(permission::OnlyGenesis)] + pub struct CanSetKeyValueInDomain { + pub domain_id: DomainId, + } + } + + token! { + #[derive(ValidateGrantRevoke)] + #[validate(permission::OnlyGenesis)] + pub struct CanRemoveKeyValueInDomain { + pub domain_id: DomainId, + } + } + } pub fn visit_unregister_domain( validator: &mut V, @@ -548,23 +541,61 @@ pub mod domain { pub mod account { use super::*; - tokens!( - pattern = { - #[derive(Token, ValidateGrantRevoke, permission::derive_conversions::account::Owner)] + declare_tokens! { + crate::default::account::tokens::CanUnregisterAccount, + crate::default::account::tokens::CanMintUserPublicKeys, + crate::default::account::tokens::CanBurnUserPublicKeys, + crate::default::account::tokens::CanMintUserSignatureCheckConditions, + crate::default::account::tokens::CanSetKeyValueInUserAccount, + crate::default::account::tokens::CanRemoveKeyValueInUserAccount, + } + + pub mod tokens { + use super::*; + + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::account::Owner)] #[validate(permission::account::Owner)] - pub struct _ { + pub struct CanUnregisterAccount { pub account_id: AccountId, } - }, - account::tokens: [ - CanUnregisterAccount, - CanMintUserPublicKeys, - CanBurnUserPublicKeys, - CanMintUserSignatureCheckConditions, - CanSetKeyValueInUserAccount, - CanRemoveKeyValueInUserAccount, - ] - ); + } + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::account::Owner)] + #[validate(permission::account::Owner)] + pub struct CanMintUserPublicKeys { + pub account_id: AccountId, + } + } + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::account::Owner)] + #[validate(permission::account::Owner)] + pub struct CanBurnUserPublicKeys { + pub account_id: AccountId, + } + } + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::account::Owner)] + #[validate(permission::account::Owner)] + pub struct CanMintUserSignatureCheckConditions { + pub account_id: AccountId, + } + } + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::account::Owner)] + #[validate(permission::account::Owner)] + pub struct CanSetKeyValueInUserAccount { + pub account_id: AccountId, + } + } + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::account::Owner)] + #[validate(permission::account::Owner)] + pub struct CanRemoveKeyValueInUserAccount { + pub account_id: AccountId, + } + } + } pub fn visit_unregister_account( validator: &mut V, @@ -690,24 +721,43 @@ pub mod account { pub mod asset_definition { use super::*; - tokens!( - pattern = { - #[derive(Token, ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] + declare_tokens! { + crate::default::asset_definition::tokens::CanUnregisterAssetDefinition, + crate::default::asset_definition::tokens::CanSetKeyValueInAssetDefinition, + crate::default::asset_definition::tokens::CanRemoveKeyValueInAssetDefinition, + } + + pub mod tokens { + use super::*; + + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] #[validate(permission::asset_definition::Owner)] - pub struct _ { - pub asset_definition_id: ::Id, + pub struct CanUnregisterAssetDefinition { + pub asset_definition_id: AssetDefinitionId, } - }, - asset_definition::tokens: [ - CanUnregisterAssetDefinition, - CanSetKeyValueInAssetDefinition, - CanRemoveKeyValueInAssetDefinition, - ] - ); + } + + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] + #[validate(permission::asset_definition::Owner)] + pub struct CanSetKeyValueInAssetDefinition { + pub asset_definition_id: AssetDefinitionId, + } + } + + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] + #[validate(permission::asset_definition::Owner)] + pub struct CanRemoveKeyValueInAssetDefinition { + pub asset_definition_id: AssetDefinitionId, + } + } + } pub(super) fn is_asset_definition_owner( - asset_definition_id: &::Id, - authority: &::Id, + asset_definition_id: &AssetDefinitionId, + authority: &AccountId, ) -> Result { IsAssetDefinitionOwner::new(asset_definition_id.clone(), authority.clone()).execute() } @@ -815,7 +865,7 @@ pub mod asset_definition { pub mod asset { use super::*; - declare_tokens!( + declare_tokens! { crate::default::asset::tokens::CanRegisterAssetsWithDefinition, crate::default::asset::tokens::CanUnregisterAssetsWithDefinition, crate::default::asset::tokens::CanUnregisterUserAsset, @@ -826,89 +876,89 @@ pub mod asset { crate::default::asset::tokens::CanTransferUserAsset, crate::default::asset::tokens::CanSetKeyValueInUserAsset, crate::default::asset::tokens::CanRemoveKeyValueInUserAsset, - ); + } pub mod tokens { use super::*; - /// Strongly-typed representation of `can_register_assets_with_definition` permission token. - #[derive( - Token, ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner, - )] - #[validate(permission::asset_definition::Owner)] - pub struct CanRegisterAssetsWithDefinition { - pub asset_definition_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] + #[validate(permission::asset_definition::Owner)] + pub struct CanRegisterAssetsWithDefinition { + pub asset_definition_id: AssetDefinitionId, + } } - /// Strongly-typed representation of `can_unregister_assets_with_definition` permission token. - #[derive( - Token, ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner, - )] - #[validate(permission::asset_definition::Owner)] - pub struct CanUnregisterAssetsWithDefinition { - pub asset_definition_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] + #[validate(permission::asset_definition::Owner)] + pub struct CanUnregisterAssetsWithDefinition { + pub asset_definition_id: AssetDefinitionId, + } } - /// Strongly-typed representation of `can_unregister_user_asset` permission token. - #[derive(Token, ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] - #[validate(permission::asset::Owner)] - pub struct CanUnregisterUserAsset { - pub asset_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] + #[validate(permission::asset::Owner)] + pub struct CanUnregisterUserAsset { + pub asset_id: AssetId, + } } - /// Strongly-typed representation of `can_burn_assets_with_definition` permission token. - #[derive( - Token, ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner, - )] - #[validate(permission::asset_definition::Owner)] - pub struct CanBurnAssetsWithDefinition { - pub asset_definition_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] + #[validate(permission::asset_definition::Owner)] + pub struct CanBurnAssetsWithDefinition { + pub asset_definition_id: AssetDefinitionId, + } } - /// Strong-typed representation of `can_burn_user_asset` permission token. - #[derive(Token, ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] - #[validate(permission::asset::Owner)] - pub struct CanBurnUserAsset { - pub asset_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] + #[validate(permission::asset::Owner)] + pub struct CanBurnUserAsset { + pub asset_id: AssetId, + } } - /// Strongly-typed representation of `can_mint_assets_with_definition` permission token. - #[derive( - Token, ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner, - )] - #[validate(permission::asset_definition::Owner)] - pub struct CanMintAssetsWithDefinition { - pub asset_definition_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] + #[validate(permission::asset_definition::Owner)] + pub struct CanMintAssetsWithDefinition { + pub asset_definition_id: AssetDefinitionId, + } } - /// Strongly-typed representation of `can_transfer_assets_with_definition` permission token. - #[derive( - Token, ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner, - )] - #[validate(permission::asset_definition::Owner)] - pub struct CanTransferAssetsWithDefinition { - pub asset_definition_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset_definition::Owner)] + #[validate(permission::asset_definition::Owner)] + pub struct CanTransferAssetsWithDefinition { + pub asset_definition_id: AssetDefinitionId, + } } - /// Strongly-typed representation of `can_transfer_user_asset` permission token. - #[derive(Token, ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] - #[validate(permission::asset::Owner)] - pub struct CanTransferUserAsset { - pub asset_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] + #[validate(permission::asset::Owner)] + pub struct CanTransferUserAsset { + pub asset_id: AssetId, + } } - /// Strongly-typed representation of `can_set_key_value_in_user_asset` permission token. - #[derive(Token, ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] - #[validate(permission::asset::Owner)] - pub struct CanSetKeyValueInUserAsset { - pub asset_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] + #[validate(permission::asset::Owner)] + pub struct CanSetKeyValueInUserAsset { + pub asset_id: AssetId, + } } - /// Strongly-typed representation of `can_remove_key_value_in_user_asset` permission token. - #[derive(Token, ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] - #[validate(permission::asset::Owner)] - pub struct CanRemoveKeyValueInUserAsset { - pub asset_id: ::Id, + token! { + #[derive(ValidateGrantRevoke, permission::derive_conversions::asset::Owner)] + #[validate(permission::asset::Owner)] + pub struct CanRemoveKeyValueInUserAsset { + pub asset_id: AssetId, + } } } @@ -1115,22 +1165,25 @@ pub mod parameter { pub mod tokens { use super::*; - /// Strongly-typed representation of `can_grant_permission_to_create_parameters` permission token. - #[derive(Token, ValidateGrantRevoke, Clone, Copy)] - #[validate(permission::OnlyGenesis)] - pub struct CanGrantPermissionToCreateParameters; + token! { + #[derive(Copy, ValidateGrantRevoke)] + #[validate(permission::OnlyGenesis)] + pub struct CanGrantPermissionToCreateParameters; + } - /// Strongly-typed representation of `can_revoke_permission_to_create_parameters` permission token. - #[derive(Token, ValidateGrantRevoke, Clone, Copy)] - #[validate(permission::OnlyGenesis)] - pub struct CanRevokePermissionToCreateParameters; + token! { + #[derive(Copy, ValidateGrantRevoke)] + #[validate(permission::OnlyGenesis)] + pub struct CanRevokePermissionToCreateParameters; + } - /// Strongly-typed representation of `can_create_parameters` permission token. - #[derive(Token, Clone, Copy)] - pub struct CanCreateParameters; + token! { + #[derive(Copy)] + pub struct CanCreateParameters; + } impl ValidateGrantRevoke for CanCreateParameters { - fn validate_grant(&self, authority: &::Id) -> Result { + fn validate_grant(&self, authority: &AccountId) -> Result { if !CanGrantPermissionToCreateParameters.is_owned_by(authority) { return Err(ValidationFail::NotPermitted( "Can't grant permission to create new configuration parameters without permission from genesis" @@ -1141,7 +1194,7 @@ pub mod parameter { Ok(()) } - fn validate_revoke(&self, authority: &::Id) -> Result { + fn validate_revoke(&self, authority: &AccountId) -> Result { if !CanRevokePermissionToCreateParameters.is_owned_by(authority) { return Err(ValidationFail::NotPermitted( "Can't revoke permission to create new configuration parameters without permission from genesis" @@ -1153,22 +1206,25 @@ pub mod parameter { } } - /// Strongly-typed representation of `can_grant_permission_to_set_parameters` permission token. - #[derive(Token, ValidateGrantRevoke, Clone, Copy)] - #[validate(permission::OnlyGenesis)] - pub struct CanGrantPermissionToSetParameters; + token! { + #[derive(Copy, ValidateGrantRevoke)] + #[validate(permission::OnlyGenesis)] + pub struct CanGrantPermissionToSetParameters; + } - /// Strongly-typed representation of `can_revoke_permission_to_set_parameters` permission token. - #[derive(Token, ValidateGrantRevoke, Clone, Copy)] - #[validate(permission::OnlyGenesis)] - pub struct CanRevokePermissionToSetParameters; + token! { + #[derive(Copy, ValidateGrantRevoke)] + #[validate(permission::OnlyGenesis)] + pub struct CanRevokePermissionToSetParameters; + } - /// Strongly-typed representation of `can_set_parameters` permission token. - #[derive(Token, Clone, Copy)] - pub struct CanSetParameters; + token! { + #[derive(Copy)] + pub struct CanSetParameters; + } impl ValidateGrantRevoke for CanSetParameters { - fn validate_grant(&self, authority: &::Id) -> Result { + fn validate_grant(&self, authority: &AccountId) -> Result { if !CanGrantPermissionToSetParameters.is_owned_by(authority) { return Err(ValidationFail::NotPermitted( "Can't grant permission to set configuration parameters without permission from genesis" @@ -1179,7 +1235,7 @@ pub mod parameter { Ok(()) } - fn validate_revoke(&self, authority: &::Id) -> Result { + fn validate_revoke(&self, authority: &AccountId) -> Result { if !CanRevokePermissionToSetParameters.is_owned_by(authority) { return Err(ValidationFail::NotPermitted( "Can't revoke permission to set configuration parameters without permission from genesis" @@ -1228,17 +1284,19 @@ pub mod parameter { pub mod role { use super::*; - tokens!( - pattern = { - #[derive(Token, ValidateGrantRevoke)] + declare_tokens! { + crate::default::role::tokens::CanUnregisterAnyRole, + } + + pub mod tokens { + use super::*; + + token! { + #[derive(Copy, ValidateGrantRevoke)] #[validate(permission::OnlyGenesis)] - #[derive(Clone, Copy)] - pub struct _ {} - }, - role::tokens: [ - CanUnregisterAnyRole, - ] - ); + pub struct CanUnregisterAnyRole; + } + } macro_rules! impl_validate { ($validator:ident, $self:ident, $authority:ident, $method:ident) => { @@ -1292,7 +1350,7 @@ pub mod role { _isi: Unregister, ) { const CAN_UNREGISTER_ROLE_TOKEN: tokens::CanUnregisterAnyRole = - tokens::CanUnregisterAnyRole {}; + tokens::CanUnregisterAnyRole; if CAN_UNREGISTER_ROLE_TOKEN.is_owned_by(authority) { pass!(validator); @@ -1335,20 +1393,39 @@ pub mod trigger { )+}; } - tokens!( - pattern = { - #[derive(Token, Clone, ValidateGrantRevoke)] + declare_tokens! { + crate::default::trigger::tokens::CanExecuteUserTrigger, + crate::default::trigger::tokens::CanUnregisterUserTrigger, + crate::default::trigger::tokens::CanMintUserTrigger, + } + + pub mod tokens { + use super::*; + + token! { + #[derive(ValidateGrantRevoke)] #[validate(permission::trigger::Owner)] - pub struct _ { - pub trigger_id: as Identifiable>::Id, + pub struct CanExecuteUserTrigger { + pub trigger_id: TriggerId, } - }, - trigger::tokens: [ - CanExecuteUserTrigger, - CanUnregisterUserTrigger, - CanMintUserTrigger, - ] - ); + } + + token! { + #[derive(ValidateGrantRevoke)] + #[validate(permission::trigger::Owner)] + pub struct CanUnregisterUserTrigger { + pub trigger_id: TriggerId, + } + } + + token! { + #[derive(ValidateGrantRevoke)] + #[validate(permission::trigger::Owner)] + pub struct CanMintUserTrigger { + pub trigger_id: TriggerId, + } + } + } impl_froms!( tokens::CanExecuteUserTrigger, @@ -1472,17 +1549,19 @@ pub mod permission_token { pub mod validator { use super::*; - tokens!( - pattern = { - #[derive(Token, ValidateGrantRevoke)] + declare_tokens! { + crate::default::validator::tokens::CanUpgradeValidator, + } + + pub mod tokens { + use super::*; + + token! { + #[derive(Copy, ValidateGrantRevoke)] #[validate(permission::OnlyGenesis)] - #[derive(Clone, Copy)] - pub struct _ {} - }, - validator::tokens: [ - CanUpgradeValidator, - ] - ); + pub struct CanUpgradeValidator; + } + } #[allow(clippy::needless_pass_by_value)] pub fn visit_upgrade_validator( @@ -1491,7 +1570,7 @@ pub mod validator { _isi: Upgrade, ) { const CAN_UPGRADE_VALIDATOR_TOKEN: tokens::CanUpgradeValidator = - tokens::CanUpgradeValidator {}; + tokens::CanUpgradeValidator; if CAN_UPGRADE_VALIDATOR_TOKEN.is_owned_by(authority) { pass!(validator); } diff --git a/wasm/validator/src/lib.rs b/wasm/validator/src/lib.rs index 77ccf24cb8d..320c46f58ad 100644 --- a/wasm/validator/src/lib.rs +++ b/wasm/validator/src/lib.rs @@ -9,8 +9,9 @@ extern crate self as iroha_validator; #[cfg(feature = "default-validator")] pub use default::DefaultValidator; +pub use iroha_schema::MetaMap; use iroha_wasm::data_model::{ - permission::PermissionTokenDefinition, validator::Result, visit::Visit, ValidationFail, + permission::PermissionTokenId, validator::Result, visit::Visit, ValidationFail, }; pub use iroha_wasm::{self, data_model}; @@ -110,10 +111,44 @@ macro_rules! declare_tokens { } } +/// Collection of all permission tokens defined by the validator +#[derive(Debug, Clone, Default)] +pub struct PermissionTokenSchema(Vec, MetaMap); + +impl PermissionTokenSchema { + /// Remove permission token from this collection + pub fn remove(&mut self) { + if let Some(pos) = self + .0 + .iter() + .position(|token_id| *token_id == ::type_name()) + { + self.0.remove(pos); + ::remove_from_schema(&mut self.1); + } + } + + /// Insert new permission token into this collection + pub fn insert(&mut self) { + ::update_schema_map(&mut self.1); + self.0.push(::type_name()); + } + + /// Serializes schema into a JSON string representation + pub fn serialize(mut self) -> (Vec, alloc::string::String) { + self.0.sort(); + + ( + self.0, + serde_json::to_string(&self.1).expect("schema serialization must not fail"), + ) + } +} + /// Validator of Iroha operations pub trait Validate: Visit { /// Get all [`PermissionTokenDefinition`]'s defined by validator. - fn permission_tokens() -> Vec; + fn permission_token_schema() -> PermissionTokenSchema; /// Validator verdict. fn verdict(&self) -> &Result; @@ -136,5 +171,5 @@ pub mod prelude { #[cfg(feature = "default-validator")] pub use super::DefaultValidator; - pub use super::{declare_tokens, deny, pass, Validate}; + pub use super::{declare_tokens, deny, pass, PermissionTokenSchema, Validate}; } diff --git a/wasm/validator/src/permission.rs b/wasm/validator/src/permission.rs index c2f2e6fb955..5d932665fae 100644 --- a/wasm/validator/src/permission.rs +++ b/wasm/validator/src/permission.rs @@ -2,19 +2,23 @@ use alloc::borrow::ToOwned as _; +use iroha_schema::IntoSchema; +use parity_scale_codec::{Decode, Encode}; + use crate::{data_model::prelude::*, prelude::*}; /// [`Token`] trait is used to check if the token is owned by the account. pub trait Token: - TryFrom + ValidateGrantRevoke + Decode + + Encode + + IntoSchema + + TryFrom + + ValidateGrantRevoke { - /// Get definition of this token - fn definition() -> PermissionTokenDefinition; - /// Check if token is owned by the account using evaluation on host. /// /// Basically it's a wrapper around [`DoesAccountHavePermissionToken`] query. - fn is_owned_by(&self, account_id: &::Id) -> bool; + fn is_owned_by(&self, account_id: &AccountId) -> bool; } /// Trait that should be implemented for all permission tokens. @@ -22,16 +26,16 @@ pub trait Token: /// instructions containing implementing token. pub trait ValidateGrantRevoke { #[allow(missing_docs, clippy::missing_errors_doc)] - fn validate_grant(&self, authority: &::Id) -> Result; + fn validate_grant(&self, authority: &AccountId) -> Result; #[allow(missing_docs, clippy::missing_errors_doc)] - fn validate_revoke(&self, authority: &::Id) -> Result; + fn validate_revoke(&self, authority: &AccountId) -> Result; } /// Predicate-like trait used for pass conditions to identify if [`Grant`] or [`Revoke`] should be allowed. pub trait PassCondition { #[allow(missing_docs, clippy::missing_errors_doc)] - fn validate(&self, authority: &::Id) -> Result; + fn validate(&self, authority: &AccountId) -> Result; } /// Error type for `TryFrom` implementations. @@ -77,11 +81,11 @@ pub mod asset { /// Pass condition that checks if `authority` is the owner of `asset_id`. #[derive(Debug, Clone)] pub struct Owner<'asset> { - pub asset_id: &'asset ::Id, + pub asset_id: &'asset AssetId, } impl PassCondition for Owner<'_> { - fn validate(&self, authority: &::Id) -> Result { + fn validate(&self, authority: &AccountId) -> Result { if self.asset_id.account_id() != authority { return Err(ValidationFail::NotPermitted( "Can't access asset owned by another account".to_owned(), @@ -99,8 +103,8 @@ pub mod asset_definition { use super::*; fn is_asset_definition_owner( - asset_definition_id: &::Id, - authority: &::Id, + asset_definition_id: &AssetDefinitionId, + authority: &AccountId, ) -> Result { IsAssetDefinitionOwner::new(asset_definition_id.clone(), authority.clone()).execute() } @@ -108,11 +112,11 @@ pub mod asset_definition { /// Pass condition that checks if `authority` is the owner of `asset_definition_id`. #[derive(Debug, Clone)] pub struct Owner<'asset_definition> { - pub asset_definition_id: &'asset_definition ::Id, + pub asset_definition_id: &'asset_definition AssetDefinitionId, } impl PassCondition for Owner<'_> { - fn validate(&self, authority: &::Id) -> Result { + fn validate(&self, authority: &AccountId) -> Result { if !is_asset_definition_owner(self.asset_definition_id, authority)? { return Err(ValidationFail::NotPermitted( "Can't access asset definition owned by another account".to_owned(), @@ -132,11 +136,11 @@ pub mod account { /// Pass condition that checks if `authority` is the owner of `account_id`. #[derive(Debug, Clone)] pub struct Owner<'asset> { - pub account_id: &'asset ::Id, + pub account_id: &'asset AccountId, } impl PassCondition for Owner<'_> { - fn validate(&self, authority: &::Id) -> Result { + fn validate(&self, authority: &AccountId) -> Result { if self.account_id != authority { return Err(ValidationFail::NotPermitted( "Can't access another account".to_owned(), @@ -159,10 +163,7 @@ pub mod trigger { /// # Errors /// /// Fails if query fails - pub fn is_trigger_owner( - trigger_id: as Identifiable>::Id, - authority: &::Id, - ) -> Result { + pub fn is_trigger_owner(trigger_id: TriggerId, authority: &AccountId) -> Result { FindTriggerById::new(trigger_id) .execute() .map(|trigger| trigger.action().authority() == authority) @@ -171,11 +172,11 @@ pub mod trigger { /// Pass condition that checks if `authority` is the owner of `trigger_id`. #[derive(Debug, Clone)] pub struct Owner<'trigger> { - pub trigger_id: &'trigger as Identifiable>::Id, + pub trigger_id: &'trigger TriggerId, } impl PassCondition for Owner<'_> { - fn validate(&self, authority: &::Id) -> Result { + fn validate(&self, authority: &AccountId) -> Result { if !is_trigger_owner(self.trigger_id.clone(), authority)? { return Err(ValidationFail::NotPermitted( "Can't give permission to access trigger owned by another account".to_owned(), @@ -192,7 +193,7 @@ pub mod trigger { pub struct AlwaysPass; impl PassCondition for AlwaysPass { - fn validate(&self, _: &::Id) -> Result { + fn validate(&self, _: &AccountId) -> Result { Ok(()) } } @@ -211,7 +212,7 @@ impl From<&T> for AlwaysPass { pub struct OnlyGenesis; impl PassCondition for OnlyGenesis { - fn validate(&self, _: &::Id) -> Result { + fn validate(&self, _: &AccountId) -> Result { Err(ValidationFail::NotPermitted( "This operation is always denied and only allowed inside the genesis block".to_owned(), ))