From e1de6091356912511935f09f8f5adea680d112de Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Mon, 11 Dec 2023 01:47:59 +0300 Subject: [PATCH] Disallow the use of boxed instruction types Signed-off-by: Daniil Polyakov --- client/benches/torii.rs | 15 +- client/benches/tps/utils.rs | 12 +- client/examples/million_accounts_genesis.rs | 5 +- client/examples/tutorial.rs | 26 +- client/tests/integration/add_account.rs | 4 +- client/tests/integration/add_domain.rs | 4 +- client/tests/integration/asset.rs | 67 +- client/tests/integration/asset_propagation.rs | 10 +- client/tests/integration/burn_public_keys.rs | 6 +- client/tests/integration/connected_peers.rs | 4 +- client/tests/integration/domain_owner.rs | 110 +-- client/tests/integration/events/data.rs | 8 +- .../tests/integration/events/notification.rs | 6 +- .../integration/multiple_blocks_created.rs | 9 +- .../integration/multisignature_account.rs | 7 +- .../integration/multisignature_transaction.rs | 6 +- client/tests/integration/non_mintable.rs | 21 +- client/tests/integration/pagination.rs | 2 +- client/tests/integration/permissions.rs | 39 +- client/tests/integration/queries/account.rs | 6 +- client/tests/integration/queries/asset.rs | 47 +- client/tests/integration/queries/role.rs | 10 +- client/tests/integration/restart_peer.rs | 4 +- client/tests/integration/roles.rs | 22 +- client/tests/integration/set_parameter.rs | 2 +- .../src/lib.rs | 4 +- .../executor_with_admin/src/lib.rs | 2 +- .../executor_with_custom_token/src/lib.rs | 16 +- .../executor_with_migration_fail/src/lib.rs | 2 +- .../mint_rose_trigger/src/lib.rs | 2 +- .../query_assets_and_save_cursor/src/lib.rs | 2 +- client/tests/integration/sorting.rs | 21 +- client/tests/integration/transfer_asset.rs | 30 +- .../integration/triggers/by_call_trigger.rs | 50 +- .../integration/triggers/data_trigger.rs | 27 +- .../integration/triggers/event_trigger.rs | 6 +- .../integration/triggers/time_trigger.rs | 25 +- .../integration/triggers/trigger_rollback.rs | 4 +- client/tests/integration/tx_history.rs | 6 +- client/tests/integration/tx_rollback.rs | 5 +- client/tests/integration/unregister_peer.rs | 10 +- client/tests/integration/unstable_network.rs | 4 +- client/tests/integration/upgrade.rs | 6 +- client_cli/src/main.rs | 38 +- configs/peer/executor.wasm | Bin 373426 -> 389245 bytes core/benches/blocks/common.rs | 24 +- core/benches/kura.rs | 2 +- core/benches/validation.rs | 11 +- core/src/block.rs | 12 +- core/src/smartcontracts/isi/mod.rs | 18 +- core/src/smartcontracts/isi/query.rs | 2 +- core/src/smartcontracts/wasm.rs | 6 +- core/src/sumeragi/main_loop.rs | 4 +- core/test_network/src/lib.rs | 7 +- data_model/src/isi.rs | 892 ++++++++++++------ data_model/src/lib.rs | 68 +- data_model/src/visit.rs | 6 +- data_model/tests/data_model.rs | 2 +- genesis/src/lib.rs | 24 +- smart_contract/executor/derive/src/default.rs | 5 + smart_contract/executor/src/default.rs | 410 ++++---- smart_contract/executor/src/lib.rs | 19 +- smart_contract/src/lib.rs | 2 +- smart_contract/utils/src/lib.rs | 2 +- tools/kagami/src/genesis.rs | 11 +- tools/parity_scale_decoder/src/main.rs | 2 +- 66 files changed, 1352 insertions(+), 889 deletions(-) diff --git a/client/benches/torii.rs b/client/benches/torii.rs index 043d7fa4c8f..95b6be46304 100644 --- a/client/benches/torii.rs +++ b/client/benches/torii.rs @@ -9,6 +9,7 @@ use iroha_client::{ data_model::prelude::*, }; use iroha_crypto::KeyPair; +use iroha_data_model::isi::InstructionBox; use iroha_genesis::{GenesisNetwork, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; use iroha_version::Encode; @@ -51,17 +52,17 @@ fn query_requests(criterion: &mut Criterion) { }); let mut group = criterion.benchmark_group("query-requests"); let domain_id: DomainId = "domain".parse().expect("Valid"); - let create_domain = RegisterBox::domain(Domain::new(domain_id.clone())); + let create_domain = Register::domain(Domain::new(domain_id.clone())); let account_id = AccountId::new("account".parse().expect("Valid"), domain_id.clone()); let (public_key, _) = KeyPair::generate() .expect("Failed to generate KeyPair") .into(); - let create_account = RegisterBox::account(Account::new(account_id.clone(), [public_key])); + let create_account = Register::account(Account::new(account_id.clone(), [public_key])); let asset_definition_id = AssetDefinitionId::new("xor".parse().expect("Valid"), domain_id); let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); let quantity: u32 = 200; - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id, account_id.clone()), ); @@ -141,12 +142,12 @@ fn instruction_submits(criterion: &mut Criterion) { rt.block_on(builder.start_with_peer(&mut peer)); let mut group = criterion.benchmark_group("instruction-requests"); let domain_id: DomainId = "domain".parse().expect("Valid"); - let create_domain = RegisterBox::domain(Domain::new(domain_id.clone())); + let create_domain: InstructionBox = Register::domain(Domain::new(domain_id.clone())).into(); let account_id = AccountId::new("account".parse().expect("Valid"), domain_id.clone()); let (public_key, _) = KeyPair::generate() .expect("Failed to generate Key-pair.") .into(); - let create_account = RegisterBox::account(Account::new(account_id.clone(), [public_key])); + let create_account = Register::account(Account::new(account_id.clone(), [public_key])).into(); let asset_definition_id = AssetDefinitionId::new("xor".parse().expect("Valid"), domain_id); let mut client_config = iroha_client::samples::get_client_config(&get_key_pair()); client_config.torii_api_url = format!("http://{}", peer.api_address).parse().unwrap(); @@ -161,7 +162,7 @@ fn instruction_submits(criterion: &mut Criterion) { let _dropable = group.bench_function("instructions", |b| { b.iter(|| { let quantity: u32 = 200; - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); diff --git a/client/benches/tps/utils.rs b/client/benches/tps/utils.rs index 1687801c981..7c4d0fc7e3c 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -172,7 +172,7 @@ impl MeasurerUnit { let alice_id = AccountId::from_str("alice@wonderland")?; let asset_id = asset_id(self.name); - let register_me = RegisterBox::account(Account::new( + let register_me = Register::account(Account::new( account_id.clone(), [keypair.public_key().clone()], )); @@ -183,13 +183,13 @@ impl MeasurerUnit { &json!({ "asset_id": asset_id }), ); let allow_alice_to_burn_my_asset = - GrantBox::permission_token(can_burn_my_asset, alice_id.clone()); + Grant::permission_token(can_burn_my_asset, alice_id.clone()); let can_transfer_my_asset = PermissionToken::new( "CanTransferUserAsset".parse().unwrap(), &json!({ "asset_id": asset_id }), ); let allow_alice_to_transfer_my_asset = - GrantBox::permission_token(can_transfer_my_asset, alice_id); + Grant::permission_token(can_transfer_my_asset, alice_id); let grant_tx = TransactionBuilder::new(account_id) .with_instructions([ allow_alice_to_burn_my_asset, @@ -198,7 +198,7 @@ impl MeasurerUnit { .sign(keypair)?; self.client.submit_transaction_blocking(&grant_tx)?; - let mint_a_rose = MintBox::asset_quantity(1_u32, asset_id); + let mint_a_rose = Mint::asset_quantity(1_u32, asset_id); self.client.submit_blocking(mint_a_rose)?; Ok(self) @@ -277,11 +277,11 @@ impl MeasurerUnit { } fn mint(&self) -> InstructionBox { - MintBox::asset_quantity(1_u32, asset_id(self.name)).into() + Mint::asset_quantity(1_u32, asset_id(self.name)).into() } fn transfer(&self) -> InstructionBox { - TransferBox::asset_quantity(asset_id(self.name), 1_u32, account_id(self.next_name)).into() + Transfer::asset_quantity(asset_id(self.name), 1_u32, account_id(self.next_name)).into() } } diff --git a/client/examples/million_accounts_genesis.rs b/client/examples/million_accounts_genesis.rs index cb4dbeb9996..57993c1a972 100644 --- a/client/examples/million_accounts_genesis.rs +++ b/client/examples/million_accounts_genesis.rs @@ -3,6 +3,7 @@ use std::{thread, time::Duration}; use iroha::samples::{construct_executor, get_config}; use iroha_client::data_model::prelude::*; +use iroha_data_model::isi::InstructionBox; use iroha_genesis::{GenesisNetwork, RawGenesisBlock, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; use test_network::{ @@ -64,8 +65,8 @@ fn create_million_accounts_directly() { format!("bob-{i}").parse().expect("Valid"), domain_id.clone(), ); - let create_domain = RegisterBox::domain(Domain::new(domain_id)); - let create_account = RegisterBox::account(Account::new(normal_account_id.clone(), [])); + let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); + let create_account = Register::account(Account::new(normal_account_id.clone(), [])).into(); if test_client .submit_all([create_domain, create_account]) .is_err() diff --git a/client/examples/tutorial.rs b/client/examples/tutorial.rs index 4eed1f06ca2..5cc86cd8495 100644 --- a/client/examples/tutorial.rs +++ b/client/examples/tutorial.rs @@ -50,7 +50,7 @@ fn domain_registration_test(config: &Configuration) -> Result<(), Error> { client::Client, data_model::{ metadata::UnlimitedMetadata, - prelude::{Domain, DomainId, InstructionBox, RegisterBox}, + prelude::{Domain, DomainId, InstructionBox, Register}, }, }; // #endregion domain_register_example_crates @@ -62,7 +62,7 @@ fn domain_registration_test(config: &Configuration) -> Result<(), Error> { // #region domain_register_example_create_isi // Create an ISI - let create_looking_glass = RegisterBox::domain(Domain::new(looking_glass)); + let create_looking_glass = Register::domain(Domain::new(looking_glass)); // #endregion domain_register_example_create_isi // #region rust_client_create @@ -116,7 +116,7 @@ fn account_registration_test(config: &Configuration) -> Result<(), Error> { client::Client, data_model::{ metadata::UnlimitedMetadata, - prelude::{Account, AccountId, InstructionBox, RegisterBox}, + prelude::{Account, AccountId, InstructionBox, Register}, }, }; use iroha_crypto::KeyPair; @@ -140,7 +140,7 @@ fn account_registration_test(config: &Configuration) -> Result<(), Error> { // #region register_account_generate // Generate a new account - let create_account = RegisterBox::account(Account::new(account_id, [public_key])); + let create_account = Register::account(Account::new(account_id, [public_key])); // #endregion register_account_generate // #region register_account_prepare_tx @@ -167,7 +167,7 @@ fn asset_registration_test(config: &Configuration) -> Result<(), Error> { use iroha_client::{ client::Client, data_model::prelude::{ - AccountId, AssetDefinition, AssetDefinitionId, AssetId, MintBox, RegisterBox, + AccountId, AssetDefinition, AssetDefinitionId, AssetId, Mint, Register, }, }; // #endregion register_asset_crates @@ -184,7 +184,7 @@ fn asset_registration_test(config: &Configuration) -> Result<(), Error> { // #region register_asset_init_submit // Initialise the registration time let register_time = - RegisterBox::asset_definition(AssetDefinition::fixed(asset_def_id.clone()).mintable_once()); + Register::asset_definition(AssetDefinition::fixed(asset_def_id.clone()).mintable_once()); // Submit a registration time iroha_client.submit(register_time)?; @@ -197,7 +197,7 @@ fn asset_registration_test(config: &Configuration) -> Result<(), Error> { // #region register_asset_mint_submit // Create a MintBox using a previous asset and account - let mint = MintBox::asset_fixed( + let mint = Mint::asset_fixed( 12.34_f64.try_into()?, AssetId::new(asset_def_id, account_id), ); @@ -216,7 +216,7 @@ fn asset_minting_test(config: &Configuration) -> Result<(), Error> { use iroha_client::{ client::Client, - data_model::prelude::{AccountId, AssetDefinitionId, AssetId, MintBox}, + data_model::prelude::{AccountId, AssetDefinitionId, AssetId, Mint}, }; // #endregion mint_asset_crates @@ -233,7 +233,7 @@ fn asset_minting_test(config: &Configuration) -> Result<(), Error> { // Mint the Asset instance // #region mint_asset_mint - let mint_roses = MintBox::asset_quantity(42_u32, AssetId::new(roses, alice)); + let mint_roses = Mint::asset_quantity(42_u32, AssetId::new(roses, alice)); // #endregion mint_asset_mint // #region mint_asset_submit_tx @@ -248,7 +248,7 @@ fn asset_minting_test(config: &Configuration) -> Result<(), Error> { // or `roses.to_string() + "#" + alice.to_string()`. // The `##` is a short-hand for the rose `which belongs to the same domain as the account // to which it belongs to. - let mint_roses_alt = MintBox::asset_quantity(10_u32, "rose##alice@wonderland".parse()?); + let mint_roses_alt = Mint::asset_quantity(10_u32, "rose##alice@wonderland".parse()?); // #endregion mint_asset_mint_alt // #region mint_asset_submit_tx_alt @@ -267,7 +267,7 @@ fn asset_burning_test(config: &Configuration) -> Result<(), Error> { use iroha_client::{ client::Client, - data_model::prelude::{AccountId, AssetDefinitionId, AssetId, BurnBox}, + data_model::prelude::{AccountId, AssetDefinitionId, AssetId, Burn}, }; // #endregion burn_asset_crates @@ -284,7 +284,7 @@ fn asset_burning_test(config: &Configuration) -> Result<(), Error> { // #region burn_asset_burn // Burn the Asset instance - let burn_roses = BurnBox::asset_quantity(10_u32, AssetId::new(roses, alice)); + let burn_roses = Burn::asset_quantity(10_u32, AssetId::new(roses, alice)); // #endregion burn_asset_burn // #region burn_asset_submit_tx @@ -299,7 +299,7 @@ fn asset_burning_test(config: &Configuration) -> Result<(), Error> { // or `roses.to_string() + "#" + alice.to_string()`. // The `##` is a short-hand for the rose `which belongs to the same domain as the account // to which it belongs to. - let burn_roses_alt = BurnBox::asset_quantity(10_u32, "rose##alice@wonderland".parse()?); + let burn_roses_alt = Burn::asset_quantity(10_u32, "rose##alice@wonderland".parse()?); // #endregion burn_asset_burn_alt // #region burn_asset_submit_tx_alt diff --git a/client/tests/integration/add_account.rs b/client/tests/integration/add_account.rs index 5c2f12d527d..bdec9441783 100644 --- a/client/tests/integration/add_account.rs +++ b/client/tests/integration/add_account.rs @@ -13,14 +13,14 @@ fn client_add_account_with_name_length_more_than_limit_should_not_commit_transac let pipeline_time = super::Configuration::pipeline_time(); let normal_account_id: AccountId = "bob@wonderland".parse().expect("Valid"); - let create_account = RegisterBox::account(Account::new(normal_account_id.clone(), [])); + let create_account = Register::account(Account::new(normal_account_id.clone(), [])); test_client.submit(create_account)?; let too_long_account_name = "0".repeat(2_usize.pow(14)); let incorrect_account_id: AccountId = (too_long_account_name + "@wonderland") .parse() .expect("Valid"); - let create_account = RegisterBox::account(Account::new(incorrect_account_id.clone(), [])); + let create_account = Register::account(Account::new(incorrect_account_id.clone(), [])); test_client.submit(create_account)?; thread::sleep(pipeline_time * 2); diff --git a/client/tests/integration/add_domain.rs b/client/tests/integration/add_domain.rs index 325906cfdd2..09bf95bb90d 100644 --- a/client/tests/integration/add_domain.rs +++ b/client/tests/integration/add_domain.rs @@ -16,11 +16,11 @@ fn client_add_domain_with_name_length_more_than_limit_should_not_commit_transact // Given let normal_domain_id: DomainId = "sora".parse()?; - let create_domain = RegisterBox::domain(Domain::new(normal_domain_id.clone())); + let create_domain = Register::domain(Domain::new(normal_domain_id.clone())); test_client.submit(create_domain)?; let too_long_domain_name: DomainId = "0".repeat(2_usize.pow(14)).parse()?; - let create_domain = RegisterBox::domain(Domain::new(too_long_domain_name.clone())); + let create_domain = Register::domain(Domain::new(too_long_domain_name.clone())); test_client.submit(create_domain)?; thread::sleep(pipeline_time * 2); diff --git a/client/tests/integration/asset.rs b/client/tests/integration/asset.rs index 7c2b163c00b..2cd3cab768f 100644 --- a/client/tests/integration/asset.rs +++ b/client/tests/integration/asset.rs @@ -6,6 +6,7 @@ use iroha_client::{ data_model::prelude::*, }; use iroha_crypto::{KeyPair, PublicKey}; +use iroha_data_model::isi::InstructionBox; use iroha_primitives::fixed::Fixed; use serde_json::json; use test_network::*; @@ -21,12 +22,13 @@ fn client_register_asset_should_add_asset_once_but_not_twice() -> Result<()> { let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let asset_definition_id = AssetDefinitionId::from_str("test_asset#wonderland").expect("Valid"); - let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); - let register_asset = RegisterBox::asset(Asset::new( + let create_asset: InstructionBox = + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())).into(); + let register_asset: InstructionBox = Register::asset(Asset::new( AssetId::new(asset_definition_id.clone(), account_id.clone()), AssetValue::Quantity(0), - )); + )) + .into(); test_client.submit_all([create_asset, register_asset.clone()])?; @@ -57,10 +59,11 @@ fn unregister_asset_should_remove_asset_from_account() -> Result<()> { let asset_definition_id = AssetDefinitionId::from_str("test_asset#wonderland").expect("Valid"); let asset_id = AssetId::new(asset_definition_id.clone(), account_id.clone()); - let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); - let register_asset = RegisterBox::asset(Asset::new(asset_id.clone(), AssetValue::Quantity(0))); - let unregister_asset = UnregisterBox::asset(asset_id); + let create_asset: InstructionBox = + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())).into(); + let register_asset = + Register::asset(Asset::new(asset_id.clone(), AssetValue::Quantity(0))).into(); + let unregister_asset = Unregister::asset(asset_id); test_client.submit_all([create_asset, register_asset])?; @@ -96,11 +99,11 @@ fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount() -> let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); let metadata = iroha_client::data_model::metadata::UnlimitedMetadata::default(); //When let quantity: u32 = 200; - let mint = MintBox::asset_quantity( + let mint = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); @@ -127,11 +130,11 @@ fn client_add_big_asset_quantity_to_existing_asset_should_increase_asset_amount( let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = - RegisterBox::asset_definition(AssetDefinition::big_quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::big_quantity(asset_definition_id.clone())); let metadata = iroha_client::data_model::metadata::UnlimitedMetadata::default(); //When let quantity: u128 = 2_u128.pow(65); - let mint = MintBox::asset_big_quantity( + let mint = Mint::asset_big_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); @@ -158,12 +161,12 @@ fn client_add_asset_with_decimal_should_increase_asset_amount() -> Result<()> { let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let asset_definition = AssetDefinition::fixed(asset_definition_id.clone()); - let create_asset = RegisterBox::asset_definition(asset_definition); + let create_asset = Register::asset_definition(asset_definition); let metadata = iroha_client::data_model::metadata::UnlimitedMetadata::default(); //When let quantity: Fixed = Fixed::try_from(123.456_f64).unwrap(); - let mint = MintBox::asset_fixed( + let mint = Mint::asset_fixed( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); @@ -181,7 +184,7 @@ fn client_add_asset_with_decimal_should_increase_asset_amount() -> Result<()> { // Add some fractional part let quantity2: Fixed = Fixed::try_from(0.55_f64).unwrap(); - let mint = MintBox::asset_fixed( + let mint = Mint::asset_fixed( quantity2, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); @@ -208,7 +211,7 @@ fn client_add_asset_with_name_length_more_than_limit_should_not_commit_transacti // Given let normal_asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); - let create_asset = RegisterBox::asset_definition(AssetDefinition::quantity( + let create_asset = Register::asset_definition(AssetDefinition::quantity( normal_asset_definition_id.clone(), )); test_client.submit(create_asset)?; @@ -217,7 +220,7 @@ fn client_add_asset_with_name_length_more_than_limit_should_not_commit_transacti let too_long_asset_name = "0".repeat(2_usize.pow(14)); let incorrect_asset_definition_id = AssetDefinitionId::from_str(&(too_long_asset_name + "#wonderland")).expect("Valid"); - let create_asset = RegisterBox::asset_definition(AssetDefinition::quantity( + let create_asset = Register::asset_definition(AssetDefinition::quantity( incorrect_asset_definition_id.clone(), )); @@ -264,11 +267,11 @@ fn find_rate_and_make_exchange_isi_should_succeed() { let buyer_keypair = KeyPair::generate().expect("Failed to generate seller KeyPair."); let register_account = |account_id: AccountId, signature: PublicKey| { - RegisterBox::account(Account::new(account_id, [signature])) + Register::account(Account::new(account_id, [signature])) }; let grant_alice_asset_transfer_permission = |asset_id: AssetId, owner_keypair: KeyPair| { - let allow_alice_to_transfer_asset = GrantBox::permission_token( + let allow_alice_to_transfer_asset = Grant::permission_token( PermissionToken::new( "CanTransferUserAsset".parse().unwrap(), &json!({ "asset_id": asset_id }), @@ -305,17 +308,17 @@ fn find_rate_and_make_exchange_isi_should_succeed() { register::asset_definition("btc", "crypto").into(), register::asset_definition("eth", "crypto").into(), register::asset_definition("btc2eth_rate", "exchange").into(), - MintBox::asset_quantity( + Mint::asset_quantity( 200_u32, asset_id_new("eth", "crypto", buyer_account_id.clone()), ) .into(), - MintBox::asset_quantity( + Mint::asset_quantity( 20_u32, asset_id_new("btc", "crypto", seller_account_id.clone()), ) .into(), - MintBox::asset_quantity(20_u32, asset_id.clone()).into(), + Mint::asset_quantity(20_u32, asset_id.clone()).into(), ]; test_client .submit_all_blocking(instructions) @@ -333,12 +336,12 @@ fn find_rate_and_make_exchange_isi_should_succeed() { }; test_client .submit_all_blocking([ - TransferBox::asset_quantity( + Transfer::asset_quantity( asset_id_new("btc", "crypto", seller_account_id.clone()), to_transfer, buyer_account_id.clone(), ), - TransferBox::asset_quantity( + Transfer::asset_quantity( asset_id_new("eth", "crypto", buyer_account_id), to_transfer, seller_account_id, @@ -397,7 +400,7 @@ fn transfer_asset_definition() { let asset_definition_id: AssetDefinitionId = "asset#wonderland".parse().expect("Valid"); test_client - .submit_blocking(RegisterBox::asset_definition(AssetDefinition::quantity( + .submit_blocking(Register::asset_definition(AssetDefinition::quantity( asset_definition_id.clone(), ))) .expect("Failed to submit transaction"); @@ -408,7 +411,7 @@ fn transfer_asset_definition() { assert_eq!(asset_definition.owned_by(), &alice_id); test_client - .submit_blocking(TransferBox::asset_definition( + .submit_blocking(Transfer::asset_definition( alice_id, asset_definition_id.clone(), bob_id.clone(), @@ -441,12 +444,12 @@ fn asset_id_new(definition_name: &str, definition_domain: &str, account_id: Acco mod register { use super::*; - pub fn domain(name: &str) -> RegisterBox { - RegisterBox::domain(Domain::new(DomainId::from_str(name).expect("Valid"))) + pub fn domain(name: &str) -> Register { + Register::domain(Domain::new(DomainId::from_str(name).expect("Valid"))) } - pub fn account(account_name: &str, domain_name: &str) -> RegisterBox { - RegisterBox::account(Account::new( + pub fn account(account_name: &str, domain_name: &str) -> Register { + Register::account(Account::new( AccountId::new( account_name.parse().expect("Valid"), domain_name.parse().expect("Valid"), @@ -455,8 +458,8 @@ mod register { )) } - pub fn asset_definition(asset_name: &str, domain_name: &str) -> RegisterBox { - RegisterBox::asset_definition(AssetDefinition::quantity(AssetDefinitionId::new( + pub fn asset_definition(asset_name: &str, domain_name: &str) -> Register { + Register::asset_definition(AssetDefinition::quantity(AssetDefinitionId::new( asset_name.parse().expect("Valid"), domain_name.parse().expect("Valid"), ))) diff --git a/client/tests/integration/asset_propagation.rs b/client/tests/integration/asset_propagation.rs index 5366958c0f2..de7a5238418 100644 --- a/client/tests/integration/asset_propagation.rs +++ b/client/tests/integration/asset_propagation.rs @@ -9,6 +9,7 @@ use iroha_client::{ }, }; use iroha_crypto::KeyPair; +use iroha_data_model::isi::InstructionBox; use test_network::*; use super::Configuration; @@ -27,18 +28,19 @@ fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount_on_a .into_set_parameters(), )?; - let create_domain = RegisterBox::domain(Domain::new(DomainId::from_str("domain")?)); + let create_domain: InstructionBox = + Register::domain(Domain::new(DomainId::from_str("domain")?)).into(); let account_id = AccountId::from_str("account@domain")?; let (public_key, _) = KeyPair::generate()?.into(); - let create_account = RegisterBox::account(Account::new(account_id.clone(), [public_key])); + let create_account = Register::account(Account::new(account_id.clone(), [public_key])).into(); let asset_definition_id = AssetDefinitionId::from_str("xor#domain")?; let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())).into(); client.submit_all([create_domain, create_account, create_asset])?; thread::sleep(pipeline_time * 3); //When let quantity: u32 = 200; - client.submit(MintBox::asset_quantity( + client.submit(Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ))?; diff --git a/client/tests/integration/burn_public_keys.rs b/client/tests/integration/burn_public_keys.rs index e4edae6d5bd..fb4c54b2251 100644 --- a/client/tests/integration/burn_public_keys.rs +++ b/client/tests/integration/burn_public_keys.rs @@ -51,7 +51,7 @@ fn public_keys_cannot_be_burned_to_nothing() { wait_for_genesis_committed(&vec![client.clone()], 0); let charlie_initial_keypair = KeyPair::generate().unwrap(); - let register_charlie = RegisterBox::account(Account::new( + let register_charlie = Register::account(Account::new( charlie_id.clone(), [charlie_initial_keypair.public_key().clone()], )); @@ -64,7 +64,7 @@ fn public_keys_cannot_be_burned_to_nothing() { let mint_keys = (0..KEYS_COUNT - 1).map(|_| { let (public_key, _) = KeyPair::generate().unwrap().into(); - MintBox::account_public_key(public_key, charlie_id.clone()) + Mint::account_public_key(public_key, charlie_id.clone()) }); let (tx_hash, res) = submit( @@ -80,7 +80,7 @@ fn public_keys_cannot_be_burned_to_nothing() { let charlie = client.request(account::by_id(charlie_id.clone())).unwrap(); let mut keys = charlie.signatories(); let burn = - |key: PublicKey| InstructionBox::from(BurnBox::account_public_key(key, charlie_id.clone())); + |key: PublicKey| InstructionBox::from(Burn::account_public_key(key, charlie_id.clone())); let burn_keys_leaving_one = keys .by_ref() .filter(|pub_key| pub_key != &charlie_initial_keypair.public_key()) diff --git a/client/tests/integration/connected_peers.rs b/client/tests/integration/connected_peers.rs index 40e10bc6fdd..91745b15ff8 100644 --- a/client/tests/integration/connected_peers.rs +++ b/client/tests/integration/connected_peers.rs @@ -52,7 +52,7 @@ fn connected_peers_with_f(faults: u64, start_port: Option) -> Result<()> { // then `status.peers` decrements let peer = network.peers.values().last().unwrap(); let peer_client = Client::test(&peer.api_address); - let unregister_peer = UnregisterBox::peer(peer.id.clone()); + let unregister_peer = Unregister::peer(peer.id.clone()); client.submit_blocking(unregister_peer)?; thread::sleep(pipeline_time * 2); // Wait for some time to allow peers to connect status = client.get_status()?; @@ -63,7 +63,7 @@ fn connected_peers_with_f(faults: u64, start_port: Option) -> Result<()> { // Re-register the peer: committed with f = `faults` - 1 then // `status.peers` increments - let register_peer = RegisterBox::peer(DataModelPeer::new(peer.id.clone())); + let register_peer = Register::peer(DataModelPeer::new(peer.id.clone())); client.submit_blocking(register_peer)?; thread::sleep(pipeline_time * 4); // Wait for some time to allow peers to connect status = client.get_status()?; diff --git a/client/tests/integration/domain_owner.rs b/client/tests/integration/domain_owner.rs index 1d15c58cdf1..b420d5e81c2 100644 --- a/client/tests/integration/domain_owner.rs +++ b/client/tests/integration/domain_owner.rs @@ -13,17 +13,13 @@ fn domain_owner_domain_permissions() -> Result<()> { // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id.clone()); - test_client.submit_blocking(RegisterBox::domain(kingdom))?; + test_client.submit_blocking(Register::domain(kingdom))?; // check that "alice@wonderland" as owner of domain can edit metadata in her domain let key: Name = "key".parse()?; let value: Name = "value".parse()?; - test_client.submit_blocking(SetKeyValueBox::domain( - kingdom_id.clone(), - key.clone(), - value, - ))?; - test_client.submit_blocking(RemoveKeyValueBox::domain(kingdom_id.clone(), key))?; + test_client.submit_blocking(SetKeyValue::domain(kingdom_id.clone(), key.clone(), value))?; + test_client.submit_blocking(RemoveKeyValue::domain(kingdom_id.clone(), key))?; // check that "alice@wonderland" as owner of domain can grant and revoke domain related permission tokens let bob_id: AccountId = "bob@wonderland".parse()?; @@ -31,11 +27,11 @@ fn domain_owner_domain_permissions() -> Result<()> { "CanUnregisterDomain".parse().unwrap(), &json!({ "domain_id": kingdom_id }), ); - test_client.submit_blocking(GrantBox::permission_token(token.clone(), bob_id.clone()))?; - test_client.submit_blocking(RevokeBox::permission_token(token, bob_id))?; + test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?; + test_client.submit_blocking(Revoke::permission_token(token, bob_id))?; // check that "alice@wonderland" as owner of domain can unregister her domain - test_client.submit_blocking(UnregisterBox::domain(kingdom_id))?; + test_client.submit_blocking(Unregister::domain(kingdom_id))?; Ok(()) } @@ -50,28 +46,28 @@ fn domain_owner_account_permissions() -> Result<()> { // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id); - test_client.submit_blocking(RegisterBox::domain(kingdom))?; + test_client.submit_blocking(Register::domain(kingdom))?; let mad_hatter_keypair = KeyPair::generate()?; let mad_hatter = Account::new( mad_hatter_id.clone(), [mad_hatter_keypair.public_key().clone()], ); - test_client.submit_blocking(RegisterBox::account(mad_hatter))?; + test_client.submit_blocking(Register::account(mad_hatter))?; // check that "alice@wonderland" as owner of domain can burn and mint public keys for accounts in her domain let mad_hatter_new_keypair = KeyPair::generate()?; - test_client.submit_blocking(MintBox::account_public_key( + test_client.submit_blocking(Mint::account_public_key( mad_hatter_new_keypair.public_key().clone(), mad_hatter_id.clone(), ))?; - test_client.submit_blocking(BurnBox::account_public_key( + test_client.submit_blocking(Burn::account_public_key( mad_hatter_new_keypair.public_key().clone(), mad_hatter_id.clone(), ))?; // check that "alice@wonderland" as owner of domain can change signature check condition for accounts in her domain - test_client.submit_blocking(MintBox::account_signature_check_condition( + test_client.submit_blocking(Mint::account_signature_check_condition( SignatureCheckCondition::AnyAccountSignatureOr(Vec::new().into()), mad_hatter_id.clone(), ))?; @@ -79,12 +75,12 @@ fn domain_owner_account_permissions() -> Result<()> { // check that "alice@wonderland" as owner of domain can edit metadata of account in her domain let key: Name = "key".parse()?; let value: Name = "value".parse()?; - test_client.submit_blocking(SetKeyValueBox::account( + test_client.submit_blocking(SetKeyValue::account( mad_hatter_id.clone(), key.clone(), value, ))?; - test_client.submit_blocking(RemoveKeyValueBox::account(mad_hatter_id.clone(), key))?; + test_client.submit_blocking(RemoveKeyValue::account(mad_hatter_id.clone(), key))?; // check that "alice@wonderland" as owner of domain can grant and revoke account related permission tokens in her domain let bob_id: AccountId = "bob@wonderland".parse()?; @@ -92,11 +88,11 @@ fn domain_owner_account_permissions() -> Result<()> { "CanUnregisterAccount".parse().unwrap(), &json!({ "account_id": mad_hatter_id }), ); - test_client.submit_blocking(GrantBox::permission_token(token.clone(), bob_id.clone()))?; - test_client.submit_blocking(RevokeBox::permission_token(token, bob_id))?; + test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?; + test_client.submit_blocking(Revoke::permission_token(token, bob_id))?; // check that "alice@wonderland" as owner of domain can unregister accounts in her domain - test_client.submit_blocking(UnregisterBox::account(mad_hatter_id))?; + test_client.submit_blocking(Unregister::account(mad_hatter_id))?; Ok(()) } @@ -113,24 +109,24 @@ fn domain_owner_asset_definition_permissions() -> Result<()> { // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id); - test_client.submit_blocking(RegisterBox::domain(kingdom))?; + test_client.submit_blocking(Register::domain(kingdom))?; let bob_keypair = KeyPair::generate()?; let bob = Account::new(bob_id.clone(), [bob_keypair.public_key().clone()]); - test_client.submit_blocking(RegisterBox::account(bob))?; + test_client.submit_blocking(Register::account(bob))?; let rabbit = Account::new(rabbit_id.clone(), []); - test_client.submit_blocking(RegisterBox::account(rabbit))?; + test_client.submit_blocking(Register::account(rabbit))?; // register asset definitions by "bob@kingdom" so he is owner of it let coin = AssetDefinition::quantity(coin_id.clone()); let transaction = TransactionBuilder::new(bob_id.clone()) - .with_instructions([RegisterBox::asset_definition(coin)]) + .with_instructions([Register::asset_definition(coin)]) .sign(bob_keypair)?; test_client.submit_transaction_blocking(&transaction)?; // check that "alice@wonderland" as owner of domain can transfer asset definitions in her domain - test_client.submit_blocking(TransferBox::asset_definition( + test_client.submit_blocking(Transfer::asset_definition( bob_id, coin_id.clone(), rabbit_id, @@ -139,12 +135,12 @@ fn domain_owner_asset_definition_permissions() -> Result<()> { // check that "alice@wonderland" as owner of domain can edit metadata of asset definition in her domain let key: Name = "key".parse()?; let value: Name = "value".parse()?; - test_client.submit_blocking(SetKeyValueBox::asset_definition( + test_client.submit_blocking(SetKeyValue::asset_definition( coin_id.clone(), key.clone(), value, ))?; - test_client.submit_blocking(RemoveKeyValueBox::asset_definition(coin_id.clone(), key))?; + test_client.submit_blocking(RemoveKeyValue::asset_definition(coin_id.clone(), key))?; // check that "alice@wonderland" as owner of domain can grant and revoke asset definition related permission tokens in her domain let bob_id: AccountId = "bob@wonderland".parse()?; @@ -152,11 +148,11 @@ fn domain_owner_asset_definition_permissions() -> Result<()> { "CanUnregisterAssetDefinition".parse().unwrap(), &json!({ "asset_definition_id": coin_id }), ); - test_client.submit_blocking(GrantBox::permission_token(token.clone(), bob_id.clone()))?; - test_client.submit_blocking(RevokeBox::permission_token(token, bob_id))?; + test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?; + test_client.submit_blocking(Revoke::permission_token(token, bob_id))?; // check that "alice@wonderland" as owner of domain can unregister asset definitions in her domain - test_client.submit_blocking(UnregisterBox::asset_definition(coin_id))?; + test_client.submit_blocking(Unregister::asset_definition(coin_id))?; Ok(()) } @@ -174,19 +170,19 @@ fn domain_owner_asset_permissions() -> Result<()> { // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id); - test_client.submit_blocking(RegisterBox::domain(kingdom))?; + test_client.submit_blocking(Register::domain(kingdom))?; let bob_keypair = KeyPair::generate()?; let bob = Account::new(bob_id.clone(), [bob_keypair.public_key().clone()]); - test_client.submit_blocking(RegisterBox::account(bob))?; + test_client.submit_blocking(Register::account(bob))?; // register asset definitions by "bob@kingdom" so he is owner of it let coin = AssetDefinition::quantity(coin_id.clone()); let store = AssetDefinition::store(store_id.clone()); let transaction = TransactionBuilder::new(bob_id.clone()) .with_instructions([ - RegisterBox::asset_definition(coin), - RegisterBox::asset_definition(store), + Register::asset_definition(coin), + Register::asset_definition(store), ]) .sign(bob_keypair)?; test_client.submit_transaction_blocking(&transaction)?; @@ -194,24 +190,20 @@ fn domain_owner_asset_permissions() -> Result<()> { // check that "alice@wonderland" as owner of domain can register and unregister assets in her domain let bob_coin_id = AssetId::new(coin_id, bob_id.clone()); let bob_coin = Asset::new(bob_coin_id.clone(), 30u32); - test_client.submit_blocking(RegisterBox::asset(bob_coin))?; - test_client.submit_blocking(UnregisterBox::asset(bob_coin_id.clone()))?; + test_client.submit_blocking(Register::asset(bob_coin))?; + test_client.submit_blocking(Unregister::asset(bob_coin_id.clone()))?; // check that "alice@wonderland" as owner of domain can burn, mint and transfer assets in her domain - test_client.submit_blocking(MintBox::asset_quantity(10u32, bob_coin_id.clone()))?; - test_client.submit_blocking(BurnBox::asset_quantity(5u32, bob_coin_id.clone()))?; - test_client.submit_blocking(TransferBox::asset_quantity(bob_coin_id, 5u32, alice_id))?; + test_client.submit_blocking(Mint::asset_quantity(10u32, bob_coin_id.clone()))?; + test_client.submit_blocking(Burn::asset_quantity(5u32, bob_coin_id.clone()))?; + test_client.submit_blocking(Transfer::asset_quantity(bob_coin_id, 5u32, alice_id))?; // check that "alice@wonderland" as owner of domain can edit metadata of store asset in her domain let key: Name = "key".parse()?; let value: Name = "value".parse()?; let bob_store_id = AssetId::new(store_id, bob_id); - test_client.submit_blocking(SetKeyValueBox::asset( - bob_store_id.clone(), - key.clone(), - value, - ))?; - test_client.submit_blocking(RemoveKeyValueBox::asset(bob_store_id.clone(), key))?; + test_client.submit_blocking(SetKeyValue::asset(bob_store_id.clone(), key.clone(), value))?; + test_client.submit_blocking(RemoveKeyValue::asset(bob_store_id.clone(), key))?; // check that "alice@wonderland" as owner of domain can grant and revoke asset related permission tokens in her domain let bob_id: AccountId = "bob@wonderland".parse()?; @@ -219,8 +211,8 @@ fn domain_owner_asset_permissions() -> Result<()> { "CanUnregisterUserAsset".parse().unwrap(), &json!({ "asset_id": bob_store_id }), ); - test_client.submit_blocking(GrantBox::permission_token(token.clone(), bob_id.clone()))?; - test_client.submit_blocking(RevokeBox::permission_token(token, bob_id))?; + test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?; + test_client.submit_blocking(Revoke::permission_token(token, bob_id))?; Ok(()) } @@ -236,18 +228,18 @@ fn domain_owner_trigger_permissions() -> Result<()> { // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id); - test_client.submit_blocking(RegisterBox::domain(kingdom))?; + test_client.submit_blocking(Register::domain(kingdom))?; let bob_keypair = KeyPair::generate()?; let bob = Account::new(bob_id.clone(), [bob_keypair.public_key().clone()]); - test_client.submit_blocking(RegisterBox::account(bob))?; + test_client.submit_blocking(Register::account(bob))?; let asset_definition_id = "rose#wonderland".parse()?; let asset_id = AssetId::new(asset_definition_id, alice_id.clone()); let trigger_id: TriggerId = "trigger$kingdom".parse()?; - let trigger_instructions = vec![MintBox::asset_quantity(1_u32, asset_id)]; - let register_trigger = RegisterBox::trigger(Trigger::new( + let trigger_instructions = vec![Mint::asset_quantity(1_u32, asset_id)]; + let register_trigger = Register::trigger(Trigger::new( trigger_id.clone(), Action::new( trigger_instructions, @@ -263,8 +255,8 @@ fn domain_owner_trigger_permissions() -> Result<()> { test_client.submit_blocking(register_trigger)?; // check that "alice@wonderland" as owner of domain can edit repetitions of triggers in her domain - test_client.submit_blocking(MintBox::trigger_repetitions(1_u32, trigger_id.clone()))?; - test_client.submit_blocking(BurnBox::trigger_repetitions(1_u32, trigger_id.clone()))?; + test_client.submit_blocking(Mint::trigger_repetitions(1_u32, trigger_id.clone()))?; + test_client.submit_blocking(Burn::trigger_repetitions(1_u32, trigger_id.clone()))?; // check that "alice@wonderland" as owner of domain can call triggers in her domain let execute_trigger = ExecuteTrigger::new(trigger_id.clone()); @@ -276,11 +268,11 @@ fn domain_owner_trigger_permissions() -> Result<()> { "CanUnregisterUserTrigger".parse().unwrap(), &json!({ "trigger_id": trigger_id }), ); - test_client.submit_blocking(GrantBox::permission_token(token.clone(), bob_id.clone()))?; - test_client.submit_blocking(RevokeBox::permission_token(token, bob_id))?; + test_client.submit_blocking(Grant::permission_token(token.clone(), bob_id.clone()))?; + test_client.submit_blocking(Revoke::permission_token(token, bob_id))?; // check that "alice@wonderland" as owner of domain can unregister triggers in her domain - test_client.submit_blocking(UnregisterBox::trigger(trigger_id))?; + test_client.submit_blocking(Unregister::trigger(trigger_id))?; Ok(()) } @@ -297,17 +289,17 @@ fn domain_owner_transfer() -> Result<()> { // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id.clone()); - test_client.submit_blocking(RegisterBox::domain(kingdom))?; + test_client.submit_blocking(Register::domain(kingdom))?; let bob_keypair = KeyPair::generate()?; let bob = Account::new(bob_id.clone(), [bob_keypair.public_key().clone()]); - test_client.submit_blocking(RegisterBox::account(bob))?; + test_client.submit_blocking(Register::account(bob))?; let domain = test_client.request(FindDomainById::new(kingdom_id.clone()))?; assert_eq!(domain.owned_by(), &alice_id); test_client - .submit_blocking(TransferBox::domain( + .submit_blocking(Transfer::domain( alice_id, kingdom_id.clone(), bob_id.clone(), diff --git a/client/tests/integration/events/data.rs b/client/tests/integration/events/data.rs index 88f6ab67980..075ba2a8591 100644 --- a/client/tests/integration/events/data.rs +++ b/client/tests/integration/events/data.rs @@ -14,7 +14,7 @@ fn produce_instructions() -> Vec { domains .into_iter() - .map(RegisterBox::domain) + .map(Register::domain) .map(InstructionBox::from) .collect::>() } @@ -155,16 +155,16 @@ fn produce_multiple_events() -> Result<()> { let role = iroha_client::data_model::role::Role::new(role_id.clone()) .add_permission(token_1.clone()) .add_permission(token_2.clone()); - let instructions = [RegisterBox::role(role.clone())]; + let instructions = [Register::role(role.clone())]; client.submit_all_blocking(instructions)?; // Grants role to Bob let bob_id = AccountId::from_str("bob@wonderland")?; - let grant_role = GrantBox::role(role_id.clone(), bob_id.clone()); + let grant_role = Grant::role(role_id.clone(), bob_id.clone()); client.submit_blocking(grant_role)?; // Unregister role - let unregister_role = UnregisterBox::role(role_id.clone()); + let unregister_role = Unregister::role(role_id.clone()); client.submit_blocking(unregister_role)?; // Inspect produced events diff --git a/client/tests/integration/events/notification.rs b/client/tests/integration/events/notification.rs index 8e15dc34e6c..2cd033e2b7c 100644 --- a/client/tests/integration/events/notification.rs +++ b/client/tests/integration/events/notification.rs @@ -14,8 +14,8 @@ fn trigger_completion_success_should_produce_event() -> Result<()> { let asset_id = AssetId::new(asset_definition_id, account_id); let trigger_id = TriggerId::from_str("mint_rose")?; - let instruction = MintBox::asset_quantity(1_u32, asset_id.clone()); - let register_trigger = RegisterBox::trigger(Trigger::new( + let instruction = Mint::asset_quantity(1_u32, asset_id.clone()); + let register_trigger = Register::trigger(Trigger::new( trigger_id.clone(), Action::new( vec![InstructionBox::from(instruction)], @@ -64,7 +64,7 @@ fn trigger_completion_failure_should_produce_event() -> Result<()> { let trigger_id = TriggerId::from_str("fail_box")?; let instruction = Fail::new("Fail box".to_owned()); - let register_trigger = RegisterBox::trigger(Trigger::new( + let register_trigger = Register::trigger(Trigger::new( trigger_id.clone(), Action::new( vec![InstructionBox::from(instruction)], diff --git a/client/tests/integration/multiple_blocks_created.rs b/client/tests/integration/multiple_blocks_created.rs index 071993f5c77..aa3f3a551ba 100644 --- a/client/tests/integration/multiple_blocks_created.rs +++ b/client/tests/integration/multiple_blocks_created.rs @@ -9,6 +9,7 @@ use iroha_client::{ }, }; use iroha_crypto::KeyPair; +use iroha_data_model::isi::InstructionBox; use test_network::*; use super::Configuration; @@ -29,13 +30,13 @@ fn long_multiple_blocks_created() -> Result<()> { .into_set_parameters(), )?; - let create_domain = RegisterBox::domain(Domain::new("domain".parse()?)); + let create_domain: InstructionBox = Register::domain(Domain::new("domain".parse()?)).into(); let account_id: AccountId = "account@domain".parse()?; let (public_key, _) = KeyPair::generate()?.into(); - let create_account = RegisterBox::account(Account::new(account_id.clone(), [public_key])); + let create_account = Register::account(Account::new(account_id.clone(), [public_key])).into(); let asset_definition_id: AssetDefinitionId = "xor#domain".parse()?; let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())).into(); client.submit_all([create_domain, create_account, create_asset])?; @@ -45,7 +46,7 @@ fn long_multiple_blocks_created() -> Result<()> { //When for _ in 0..N_BLOCKS { let quantity: u32 = 1; - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); diff --git a/client/tests/integration/multisignature_account.rs b/client/tests/integration/multisignature_account.rs index be1296d74b7..9f35fb9bbfa 100644 --- a/client/tests/integration/multisignature_account.rs +++ b/client/tests/integration/multisignature_account.rs @@ -20,17 +20,16 @@ fn transaction_signed_by_new_signatory_of_account_should_pass() -> Result<()> { let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); let key_pair = KeyPair::generate()?; - let add_signatory = - MintBox::account_public_key(key_pair.public_key().clone(), account_id.clone()); + let add_signatory = Mint::account_public_key(key_pair.public_key().clone(), account_id.clone()); let instructions: [InstructionBox; 2] = [create_asset.into(), add_signatory.into()]; client.submit_all(instructions)?; thread::sleep(pipeline_time * 2); //When let quantity: u32 = 200; - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); diff --git a/client/tests/integration/multisignature_transaction.rs b/client/tests/integration/multisignature_transaction.rs index 3fd8c80b8f8..280a07751f3 100644 --- a/client/tests/integration/multisignature_transaction.rs +++ b/client/tests/integration/multisignature_transaction.rs @@ -32,8 +32,8 @@ fn multisignature_transactions_should_wait_for_all_signatures() -> Result<()> { let key_pair_2 = KeyPair::generate()?; let asset_definition_id = AssetDefinitionId::from_str("camomile#wonderland")?; let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); - let set_signature_condition = MintBox::account_signature_check_condition( + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + let set_signature_condition = Mint::account_signature_check_condition( SignatureCheckCondition::AllAccountSignaturesAnd( vec![key_pair_2.public_key().clone()].into(), ), @@ -48,7 +48,7 @@ fn multisignature_transactions_should_wait_for_all_signatures() -> Result<()> { //When let quantity: u32 = 200; let asset_id = AssetId::new(asset_definition_id, alice_id.clone()); - let mint_asset = MintBox::asset_quantity(quantity, asset_id.clone()); + let mint_asset = Mint::asset_quantity(quantity, asset_id.clone()); let (public_key1, private_key1) = alice_key_pair.into(); client_configuration.account_id = alice_id.clone(); diff --git a/client/tests/integration/non_mintable.rs b/client/tests/integration/non_mintable.rs index d2bde541848..c80be2ca4d9 100644 --- a/client/tests/integration/non_mintable.rs +++ b/client/tests/integration/non_mintable.rs @@ -5,6 +5,7 @@ use iroha_client::{ client::{self, QueryResult}, data_model::{metadata::UnlimitedMetadata, prelude::*}, }; +use iroha_data_model::isi::InstructionBox; use test_network::*; #[test] @@ -15,13 +16,13 @@ fn non_mintable_asset_can_be_minted_once_but_not_twice() -> Result<()> { // Given let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); - let create_asset = RegisterBox::asset_definition( + let create_asset = Register::asset_definition( AssetDefinition::quantity(asset_definition_id.clone()).mintable_once(), ); let metadata = UnlimitedMetadata::default(); - let mint = MintBox::asset_quantity( + let mint = Mint::asset_quantity( 200_u32, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); @@ -63,12 +64,14 @@ fn non_mintable_asset_cannot_be_minted_if_registered_with_non_zero_value() -> Re // Given let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); - let create_asset = RegisterBox::asset_definition( + let create_asset: InstructionBox = Register::asset_definition( AssetDefinition::quantity(asset_definition_id.clone()).mintable_once(), - ); + ) + .into(); let asset_id = AssetId::new(asset_definition_id.clone(), account_id.clone()); - let register_asset = RegisterBox::asset(Asset::new(asset_id.clone(), 1_u32)); + let register_asset: InstructionBox = + Register::asset(Asset::new(asset_id.clone(), 1_u32)).into(); // We can register the non-mintable token test_client.submit_all([create_asset, register_asset.clone()])?; @@ -84,7 +87,7 @@ fn non_mintable_asset_cannot_be_minted_if_registered_with_non_zero_value() -> Re assert!(test_client.submit_blocking(register_asset).is_err()); // And can't be minted - let mint = MintBox::asset_quantity(1_u32, asset_id); + let mint = Mint::asset_quantity(1_u32, asset_id); assert!(test_client.submit_blocking(mint).is_err()); Ok(()) @@ -98,13 +101,13 @@ fn non_mintable_asset_can_be_minted_if_registered_with_zero_value() -> Result<() // Given let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); - let create_asset = RegisterBox::asset_definition( + let create_asset = Register::asset_definition( AssetDefinition::quantity(asset_definition_id.clone()).mintable_once(), ); let asset_id = AssetId::new(asset_definition_id.clone(), account_id.clone()); - let register_asset = RegisterBox::asset(Asset::new(asset_id.clone(), 0_u32)); - let mint = MintBox::asset_quantity(1_u32, asset_id); + let register_asset = Register::asset(Asset::new(asset_id.clone(), 0_u32)); + let mint = Mint::asset_quantity(1_u32, asset_id); // We can register the non-mintable token wih zero value and then mint it let instructions: [InstructionBox; 3] = diff --git a/client/tests/integration/pagination.rs b/client/tests/integration/pagination.rs index 30221d101c7..6e0ed462ec2 100644 --- a/client/tests/integration/pagination.rs +++ b/client/tests/integration/pagination.rs @@ -50,7 +50,7 @@ fn register_assets(client: &Client) -> Result<()> { .map(|c| c.to_string()) .map(|name| (name + "#wonderland").parse().expect("Valid")) .map(|asset_definition_id| { - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id)).into() + Register::asset_definition(AssetDefinition::quantity(asset_definition_id)).into() }) .collect(); let _ = client.submit_all_blocking(register)?; diff --git a/client/tests/integration/permissions.rs b/client/tests/integration/permissions.rs index dea4bce29a6..fb829ed2ce3 100644 --- a/client/tests/integration/permissions.rs +++ b/client/tests/integration/permissions.rs @@ -18,7 +18,7 @@ fn genesis_transactions_are_validated() { let mut genesis = GenesisNetwork::test(true).expect("Expected genesis"); - let grant_invalid_token = GrantBox::permission_token( + let grant_invalid_token = Grant::permission_token( PermissionToken::new("InvalidToken".parse().unwrap(), &json!(null)), AccountId::from_str("alice@wonderland").unwrap(), ); @@ -79,7 +79,7 @@ fn permissions_disallow_asset_transfer() { let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); let mouse_keypair = iroha_crypto::KeyPair::generate().expect("Failed to generate KeyPair."); let alice_start_assets = get_assets(&iroha_client, &alice_id); @@ -88,7 +88,7 @@ fn permissions_disallow_asset_transfer() { .expect("Failed to prepare state."); let quantity: u32 = 200; - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), bob_id.clone()), ); @@ -97,7 +97,7 @@ fn permissions_disallow_asset_transfer() { .expect("Failed to create asset."); //When - let transfer_asset = TransferBox::asset_quantity( + let transfer_asset = Transfer::asset_quantity( AssetId::new(asset_definition_id, bob_id), quantity, alice_id.clone(), @@ -133,7 +133,7 @@ fn permissions_disallow_asset_burn() { let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); let mouse_keypair = iroha_crypto::KeyPair::generate().expect("Failed to generate KeyPair."); let alice_start_assets = get_assets(&iroha_client, &alice_id); @@ -144,11 +144,11 @@ fn permissions_disallow_asset_burn() { let quantity: u32 = 200; let mint_asset = - MintBox::asset_quantity(quantity, AssetId::new(asset_definition_id.clone(), bob_id)); + Mint::asset_quantity(quantity, AssetId::new(asset_definition_id.clone(), bob_id)); iroha_client .submit_blocking(mint_asset) .expect("Failed to create asset."); - let burn_asset = BurnBox::asset_quantity( + let burn_asset = Burn::asset_quantity( quantity, AssetId::new(asset_definition_id, mouse_id.clone()), ); @@ -184,7 +184,7 @@ fn account_can_query_only_its_own_domain() -> Result<()> { // Given let domain_id: DomainId = "wonderland".parse()?; let new_domain_id: DomainId = "wonderland2".parse()?; - let register_domain = RegisterBox::domain(Domain::new(new_domain_id.clone())); + let register_domain = Register::domain(Domain::new(new_domain_id.clone())); client.submit_blocking(register_domain)?; @@ -213,20 +213,20 @@ fn permissions_differ_not_only_by_names() { let new_shoes_definition = AssetDefinition::store(shoes_definition_id.clone()); client .submit_all_blocking([ - RegisterBox::asset_definition(new_hat_definition), - RegisterBox::asset_definition(new_shoes_definition), + Register::asset_definition(new_hat_definition), + Register::asset_definition(new_shoes_definition), ]) .expect("Failed to register new asset definitions"); // Registering mouse let new_mouse_account = Account::new(mouse_id.clone(), [mouse_keypair.public_key().clone()]); client - .submit_blocking(RegisterBox::account(new_mouse_account)) + .submit_blocking(Register::account(new_mouse_account)) .expect("Failed to register mouse"); // Granting permission to Alice to modify metadata in Mouse's hats let mouse_hat_id = AssetId::new(hat_definition_id, mouse_id.clone()); - let allow_alice_to_set_key_value_in_hats = GrantBox::permission_token( + let allow_alice_to_set_key_value_in_hats = Grant::permission_token( PermissionToken::new( "CanSetKeyValueInUserAsset".parse().unwrap(), &json!({ "asset_id": mouse_hat_id }), @@ -244,7 +244,7 @@ fn permissions_differ_not_only_by_names() { // Checking that Alice can modify Mouse's hats ... client - .submit_blocking(SetKeyValueBox::asset( + .submit_blocking(SetKeyValue::asset( mouse_hat_id, Name::from_str("color").expect("Valid"), "red".to_owned(), @@ -253,7 +253,7 @@ fn permissions_differ_not_only_by_names() { // ... but not shoes let mouse_shoes_id = AssetId::new(shoes_definition_id, mouse_id.clone()); - let set_shoes_color = SetKeyValueBox::asset( + let set_shoes_color = SetKeyValue::asset( mouse_shoes_id.clone(), Name::from_str("color").expect("Valid"), "yellow".to_owned(), @@ -263,7 +263,7 @@ fn permissions_differ_not_only_by_names() { .expect_err("Expected Alice to fail to modify Mouse's shoes"); // Granting permission to Alice to modify metadata in Mouse's shoes - let allow_alice_to_set_key_value_in_shoes = GrantBox::permission_token( + let allow_alice_to_set_key_value_in_shoes = Grant::permission_token( PermissionToken::new( "CanSetKeyValueInUserAsset".parse().unwrap(), &json!({ "asset_id": mouse_shoes_id }), @@ -298,12 +298,12 @@ fn stored_vs_granted_token_payload() -> Result<()> { // Registering mouse and asset definition let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); let create_asset = - RegisterBox::asset_definition(AssetDefinition::store(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::store(asset_definition_id.clone())); let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); let mouse_keypair = iroha_crypto::KeyPair::generate().expect("Failed to generate KeyPair."); let new_mouse_account = Account::new(mouse_id.clone(), [mouse_keypair.public_key().clone()]); let instructions: [InstructionBox; 2] = [ - RegisterBox::account(new_mouse_account).into(), + Register::account(new_mouse_account).into(), create_asset.into(), ]; iroha_client @@ -312,7 +312,7 @@ fn stored_vs_granted_token_payload() -> Result<()> { // Allow alice to mint mouse asset and mint initial value let mouse_asset = AssetId::new(asset_definition_id, mouse_id.clone()); - let allow_alice_to_set_key_value_in_mouse_asset = GrantBox::permission_token( + let allow_alice_to_set_key_value_in_mouse_asset = Grant::permission_token( PermissionToken::from_str_unchecked( "CanSetKeyValueInUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form @@ -330,8 +330,7 @@ fn stored_vs_granted_token_payload() -> Result<()> { .expect("Failed to grant permission to alice."); // Check that alice can indeed mint mouse asset - let set_key_value = - SetKeyValueBox::asset(mouse_asset, Name::from_str("color")?, "red".to_owned()); + let set_key_value = SetKeyValue::asset(mouse_asset, Name::from_str("color")?, "red".to_owned()); iroha_client .submit_blocking(set_key_value) .expect("Failed to mint asset for mouse."); diff --git a/client/tests/integration/queries/account.rs b/client/tests/integration/queries/account.rs index c21243efc6c..69d28c66e6f 100644 --- a/client/tests/integration/queries/account.rs +++ b/client/tests/integration/queries/account.rs @@ -15,7 +15,7 @@ fn find_accounts_with_asset() -> Result<()> { // Registering new asset definition let definition_id = AssetDefinitionId::from_str("test_coin#wonderland").expect("Valid"); let asset_definition = AssetDefinition::quantity(definition_id.clone()); - test_client.submit_blocking(RegisterBox::asset_definition(asset_definition.clone()))?; + test_client.submit_blocking(Register::asset_definition(asset_definition.clone()))?; // Checking results before all let received_asset_definition = @@ -40,7 +40,7 @@ fn find_accounts_with_asset() -> Result<()> { .iter() .skip(1) // Alice has already been registered in genesis .cloned() - .map(|account_id| RegisterBox::account(Account::new(account_id, []))) + .map(|account_id| Register::account(Account::new(account_id, []))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; @@ -48,7 +48,7 @@ fn find_accounts_with_asset() -> Result<()> { .iter() .cloned() .map(|account_id| AssetId::new(definition_id.clone(), account_id)) - .map(|asset_id| MintBox::asset_quantity(1_u32, asset_id)) + .map(|asset_id| Mint::asset_quantity(1_u32, asset_id)) .collect::>(); test_client.submit_all_blocking(mint_asset)?; diff --git a/client/tests/integration/queries/asset.rs b/client/tests/integration/queries/asset.rs index 2f6a4996b96..bb34d302158 100644 --- a/client/tests/integration/queries/asset.rs +++ b/client/tests/integration/queries/asset.rs @@ -8,6 +8,7 @@ use iroha_client::{ }, }; use iroha_crypto::KeyPair; +use iroha_data_model::isi::Instruction; use iroha_primitives::fixed::Fixed; use test_network::*; @@ -20,7 +21,7 @@ fn find_asset_total_quantity() -> Result<()> { // Register new domain let domain_id: DomainId = "looking_glass".parse()?; let domain = Domain::new(domain_id); - test_client.submit_blocking(RegisterBox::domain(domain))?; + test_client.submit_blocking(Register::domain(domain))?; let accounts: [AccountId; 5] = [ "alice@wonderland".parse()?, @@ -41,7 +42,7 @@ fn find_asset_total_quantity() -> Result<()> { .skip(1) // Alice has already been registered in genesis .cloned() .zip(keys.iter().map(KeyPair::public_key).cloned()) - .map(|(account_id, public_key)| RegisterBox::account(Account::new(account_id, [public_key]))) + .map(|(account_id, public_key)| Register::account(Account::new(account_id, [public_key]))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; @@ -55,8 +56,8 @@ fn find_asset_total_quantity() -> Result<()> { 10_u32, 5_u32, NumericValue::U32(30_u32), - MintBox::asset_quantity, - BurnBox::asset_quantity, + Mint::asset_quantity, + Burn::asset_quantity, )?; test_total_quantity( &test_client, @@ -67,8 +68,8 @@ fn find_asset_total_quantity() -> Result<()> { 10_u128, 5_u128, NumericValue::U128(30_u128), - MintBox::asset_big_quantity, - BurnBox::asset_big_quantity, + Mint::asset_big_quantity, + Burn::asset_big_quantity, )?; test_total_quantity( &test_client, @@ -79,14 +80,14 @@ fn find_asset_total_quantity() -> Result<()> { Fixed::try_from(10.0)?, Fixed::try_from(5.0)?, NumericValue::Fixed(Fixed::try_from(30.0)?), - MintBox::asset_fixed, - BurnBox::asset_fixed, + Mint::asset_fixed, + Burn::asset_fixed, )?; // Test for `Store` asset value type let definition_id: AssetDefinitionId = "store#wonderland".parse().expect("Valid"); let asset_definition = AssetDefinition::store(definition_id.clone()); - test_client.submit_blocking(RegisterBox::asset_definition(asset_definition))?; + test_client.submit_blocking(Register::asset_definition(asset_definition))?; let asset_ids = accounts .iter() @@ -104,7 +105,7 @@ fn find_asset_total_quantity() -> Result<()> { .iter() .cloned() .map(|asset_id| Asset::new(asset_id, Metadata::default())) - .map(RegisterBox::asset) + .map(Register::asset) .collect::>(); test_client.submit_all_blocking(register_assets)?; @@ -117,7 +118,7 @@ fn find_asset_total_quantity() -> Result<()> { let unregister_assets = asset_ids .iter() .cloned() - .map(UnregisterBox::asset) + .map(Unregister::asset) .collect::>(); test_client.submit_all_blocking(unregister_assets)?; @@ -128,7 +129,7 @@ fn find_asset_total_quantity() -> Result<()> { assert!(total_asset_quantity.is_zero_value()); // Unregister asset definition - test_client.submit_blocking(UnregisterBox::asset_definition(definition_id.clone()))?; + test_client.submit_blocking(Unregister::asset_definition(definition_id.clone()))?; // Assert that total asset quantity cleared with unregistering of asset definition let result = test_client.request(FindTotalAssetQuantityByAssetDefinitionId::new( @@ -145,7 +146,7 @@ fn find_asset_total_quantity() -> Result<()> { } #[allow(clippy::too_many_arguments)] -fn test_total_quantity>( +fn test_total_quantity( test_client: &Client, accounts: &[AccountId; 5], definition: &str, @@ -154,14 +155,20 @@ fn test_total_quantity>( to_mint: T, to_burn: T, expected_total_asset_quantity: NumericValue, - mint_ctr: impl Fn(T, AssetId) -> MintBox, - burn_ctr: impl Fn(T, AssetId) -> BurnBox, -) -> Result<()> { + mint_ctr: impl Fn(T, AssetId) -> Mint, + burn_ctr: impl Fn(T, AssetId) -> Burn, +) -> Result<()> +where + T: Copy + Into, + Value: From, + Mint: Instruction, + Burn: Instruction, +{ // Registering new asset definition let definition_id: AssetDefinitionId = definition.parse().expect("Failed to parse `definition_id`"); let asset_definition = AssetDefinition::new(definition_id.clone(), asset_value_type); - test_client.submit_blocking(RegisterBox::asset_definition(asset_definition))?; + test_client.submit_blocking(Register::asset_definition(asset_definition))?; let asset_ids = accounts .iter() @@ -179,7 +186,7 @@ fn test_total_quantity>( .iter() .cloned() .map(|asset_id| Asset::new(asset_id, initial_value)) - .map(RegisterBox::asset) + .map(Register::asset) .collect::>(); test_client.submit_all_blocking(register_assets)?; @@ -205,7 +212,7 @@ fn test_total_quantity>( let unregister_assets = asset_ids .iter() .cloned() - .map(UnregisterBox::asset) + .map(Unregister::asset) .collect::>(); test_client.submit_all_blocking(unregister_assets)?; @@ -216,7 +223,7 @@ fn test_total_quantity>( assert!(total_asset_quantity.is_zero_value()); // Unregister asset definition - test_client.submit_blocking(UnregisterBox::asset_definition(definition_id.clone()))?; + test_client.submit_blocking(Unregister::asset_definition(definition_id.clone()))?; // Assert that total asset quantity cleared with unregistering of asset definition let result = test_client.request(FindTotalAssetQuantityByAssetDefinitionId::new( diff --git a/client/tests/integration/queries/role.rs b/client/tests/integration/queries/role.rs index 996ea88cfc6..9d18b523910 100644 --- a/client/tests/integration/queries/role.rs +++ b/client/tests/integration/queries/role.rs @@ -29,7 +29,7 @@ fn find_roles() -> Result<()> { let register_roles = role_ids .iter() .cloned() - .map(|role_id| RegisterBox::role(Role::new(role_id))) + .map(|role_id| Register::role(Role::new(role_id))) .collect::>(); test_client.submit_all_blocking(register_roles)?; @@ -61,7 +61,7 @@ fn find_role_ids() -> Result<()> { let register_roles = role_ids .iter() .cloned() - .map(|role_id| RegisterBox::role(Role::new(role_id))) + .map(|role_id| Register::role(Role::new(role_id))) .collect::>(); test_client.submit_all_blocking(register_roles)?; @@ -87,7 +87,7 @@ fn find_role_by_id() -> Result<()> { let new_role = Role::new(role_id.clone()); // Registering role - let register_role = RegisterBox::role(new_role.clone()); + let register_role = Register::role(new_role.clone()); test_client.submit_blocking(register_role)?; let found_role = test_client.request(client::role::by_id(role_id))?; @@ -130,7 +130,7 @@ fn find_roles_by_account_id() -> Result<()> { .iter() .cloned() .map(|role_id| { - RegisterBox::role(Role::new(role_id).add_permission(PermissionToken::new( + Register::role(Role::new(role_id).add_permission(PermissionToken::new( "CanSetKeyValueInUserAccount".parse().unwrap(), &json!({ "account_id": alice_id }), ))) @@ -142,7 +142,7 @@ fn find_roles_by_account_id() -> Result<()> { let grant_roles = role_ids .iter() .cloned() - .map(|role_id| GrantBox::role(role_id, alice_id.clone())) + .map(|role_id| Grant::role(role_id, alice_id.clone())) .collect::>(); test_client.submit_all_blocking(grant_roles)?; diff --git a/client/tests/integration/restart_peer.rs b/client/tests/integration/restart_peer.rs index 24bec3fd62c..1699ae9763e 100644 --- a/client/tests/integration/restart_peer.rs +++ b/client/tests/integration/restart_peer.rs @@ -23,7 +23,7 @@ fn restarted_peer_should_have_the_same_asset_amount() -> Result<()> { let account_id = AccountId::from_str("alice@wonderland").unwrap(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").unwrap(); let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); let quantity: u32 = 200; let iroha_client = client::Client::test(&peer.api_address); @@ -39,7 +39,7 @@ fn restarted_peer_should_have_the_same_asset_amount() -> Result<()> { wait_for_genesis_committed(&vec![iroha_client.clone()], 0); iroha_client.submit_blocking(create_asset)?; - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); diff --git a/client/tests/integration/roles.rs b/client/tests/integration/roles.rs index 2bf0632e016..8523cfb39b6 100644 --- a/client/tests/integration/roles.rs +++ b/client/tests/integration/roles.rs @@ -14,7 +14,7 @@ fn register_empty_role() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let role_id = "root".parse().expect("Valid"); - let register_role = RegisterBox::role(Role::new(role_id)); + let register_role = Register::role(Role::new(role_id)); test_client.submit(register_role)?; Ok(()) @@ -29,7 +29,7 @@ fn register_role_with_empty_token_params() -> Result<()> { let token = PermissionToken::new("token".parse()?, &json!(null)); let role = Role::new(role_id).add_permission(token); - test_client.submit(RegisterBox::role(role))?; + test_client.submit(Register::role(role))?; Ok(()) } @@ -53,7 +53,7 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { // Registering Mouse let mouse_key_pair = iroha_crypto::KeyPair::generate()?; - let register_mouse = RegisterBox::account(Account::new( + let register_mouse = Register::account(Account::new( mouse_id.clone(), [mouse_key_pair.public_key().clone()], )); @@ -70,18 +70,18 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { "CanRemoveKeyValueInUserAccount".parse()?, &json!({ "account_id": mouse_id }), )); - let register_role = RegisterBox::role(role); + let register_role = Register::role(role); test_client.submit_blocking(register_role)?; // Mouse grants role to Alice - let grant_role = GrantBox::role(role_id.clone(), alice_id.clone()); + let grant_role = Grant::role(role_id.clone(), alice_id.clone()); let grant_role_tx = TransactionBuilder::new(mouse_id.clone()) .with_instructions([grant_role]) .sign(mouse_key_pair)?; test_client.submit_transaction_blocking(&grant_role_tx)?; // Alice modifies Mouse's metadata - let set_key_value = SetKeyValueBox::account( + let set_key_value = SetKeyValue::account( mouse_id, Name::from_str("key").expect("Valid"), Value::String("value".to_owned()), @@ -107,11 +107,11 @@ fn unregistered_role_removed_from_account() -> Result<()> { let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); // Registering Mouse - let register_mouse = RegisterBox::account(Account::new(mouse_id.clone(), [])); + let register_mouse = Register::account(Account::new(mouse_id.clone(), [])); test_client.submit_blocking(register_mouse)?; // Register root role - let register_role = RegisterBox::role(Role::new(role_id.clone()).add_permission( + let register_role = Register::role(Role::new(role_id.clone()).add_permission( PermissionToken::new( "CanSetKeyValueInUserAccount".parse()?, &json!({ "account_id": alice_id }), @@ -120,7 +120,7 @@ fn unregistered_role_removed_from_account() -> Result<()> { test_client.submit_blocking(register_role)?; // Grant root role to Mouse - let grant_role = GrantBox::role(role_id.clone(), mouse_id.clone()); + let grant_role = Grant::role(role_id.clone(), mouse_id.clone()); test_client.submit_blocking(grant_role)?; // Check that Mouse has root role @@ -130,7 +130,7 @@ fn unregistered_role_removed_from_account() -> Result<()> { assert!(found_mouse_roles.contains(&role_id)); // Unregister root role - let unregister_role = UnregisterBox::role(role_id.clone()); + let unregister_role = Unregister::role(role_id.clone()); test_client.submit_blocking(unregister_role)?; // Check that Mouse doesn't have the root role @@ -155,7 +155,7 @@ fn role_with_invalid_permissions_is_not_accepted() -> Result<()> { )); let err = test_client - .submit_blocking(RegisterBox::role(role)) + .submit_blocking(Register::role(role)) .expect_err("Submitting role with invalid permission token should fail"); let rejection_reason = err diff --git a/client/tests/integration/set_parameter.rs b/client/tests/integration/set_parameter.rs index 6ff409710b6..08012429e01 100644 --- a/client/tests/integration/set_parameter.rs +++ b/client/tests/integration/set_parameter.rs @@ -46,7 +46,7 @@ fn parameter_propagated() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let too_long_domain_name: DomainId = "0".repeat(2_usize.pow(8)).parse()?; - let create_domain = RegisterBox::domain(Domain::new(too_long_domain_name)); + let create_domain = Register::domain(Domain::new(too_long_domain_name)); let _ = test_client .submit_blocking(create_domain.clone()) .expect_err("Should fail before ident length limits update"); diff --git a/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs b/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs index 3cc5a5df9e2..bce2802adcb 100644 --- a/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs +++ b/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs @@ -43,10 +43,10 @@ fn main(_owner: AccountId, _event: Event) { let account_nft_id = AssetId::new(nft_id, account.id().clone()); let account_nft = Asset::new(account_nft_id, Metadata::new()); - RegisterBox::asset_definition(nft_definition) + Register::asset_definition(nft_definition) .execute() .dbg_unwrap(); - RegisterBox::asset(account_nft).execute().dbg_unwrap(); + Register::asset(account_nft).execute().dbg_unwrap(); } iroha_trigger::log::info!("Smart contract executed successfully"); diff --git a/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs b/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs index 92cb6b1c9e8..8a950ee38cd 100644 --- a/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs +++ b/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs @@ -21,7 +21,7 @@ struct Executor { fn visit_instruction(executor: &mut Executor, authority: &AccountId, isi: &InstructionBox) { if parse!("admin@admin" as AccountId) == *authority { - pass!(executor); + execute!(executor, isi); } iroha_executor::default::visit_instruction(executor, authority, isi); diff --git a/client/tests/integration/smartcontracts/executor_with_custom_token/src/lib.rs b/client/tests/integration/smartcontracts/executor_with_custom_token/src/lib.rs index 43ab94b72f4..bd9322a7f87 100644 --- a/client/tests/integration/smartcontracts/executor_with_custom_token/src/lib.rs +++ b/client/tests/integration/smartcontracts/executor_with_custom_token/src/lib.rs @@ -118,7 +118,7 @@ impl Executor { accounts .iter() .try_for_each(|(account, domain_id)| { - RevokeBox::permission_token( + Revoke::permission_token( PermissionToken::new( can_unregister_domain_definition_id.clone(), &json!({ "domain_id": domain_id }), @@ -137,7 +137,7 @@ impl Executor { ) })?; - GrantBox::permission_token( + Grant::permission_token( PermissionToken::new( can_control_domain_lives_definition_id.clone(), &json!(null), @@ -169,12 +169,12 @@ impl Executor { } } -fn visit_register_domain(executor: &mut Executor, authority: &AccountId, _isi: &Register) { +fn visit_register_domain(executor: &mut Executor, authority: &AccountId, isi: &Register) { if executor.block_height() == 0 { - pass!(executor) + execute!(executor, isi); } if token::CanControlDomainLives.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -186,13 +186,13 @@ fn visit_register_domain(executor: &mut Executor, authority: &AccountId, _isi: & fn visit_unregister_domain( executor: &mut Executor, authority: &AccountId, - _isi: &Unregister, + isi: &Unregister, ) { if executor.block_height() == 0 { - pass!(executor); + execute!(executor, isi); } if token::CanControlDomainLives.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "You don't have permission to unregister domain"); diff --git a/client/tests/integration/smartcontracts/executor_with_migration_fail/src/lib.rs b/client/tests/integration/smartcontracts/executor_with_migration_fail/src/lib.rs index 69c6dad9871..e603758dd1d 100644 --- a/client/tests/integration/smartcontracts/executor_with_migration_fail/src/lib.rs +++ b/client/tests/integration/smartcontracts/executor_with_migration_fail/src/lib.rs @@ -27,7 +27,7 @@ pub fn migrate(_block_height: u64) -> MigrationResult { // Registering a new domain (using ISI) let domain_id = parse!("failed_migration_test_domain" as DomainId); - RegisterBox::domain(Domain::new(domain_id)) + Register::domain(Domain::new(domain_id)) .execute() .map_err(|error| { format!( diff --git a/client/tests/integration/smartcontracts/mint_rose_trigger/src/lib.rs b/client/tests/integration/smartcontracts/mint_rose_trigger/src/lib.rs index cac419a3c37..f794772bebd 100644 --- a/client/tests/integration/smartcontracts/mint_rose_trigger/src/lib.rs +++ b/client/tests/integration/smartcontracts/mint_rose_trigger/src/lib.rs @@ -20,7 +20,7 @@ fn main(owner: AccountId, _event: Event) { .dbg_expect("Failed to parse `rose#wonderland` asset definition id"); let rose_id = AssetId::new(rose_definition_id, owner); - MintBox::asset_quantity(1_u32, rose_id) + Mint::asset_quantity(1_u32, rose_id) .execute() .dbg_expect("Failed to mint rose"); } diff --git a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs index 66b078b2acf..87137474596 100644 --- a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs +++ b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs @@ -26,7 +26,7 @@ fn main(owner: AccountId) { let (_batch, cursor) = asset_cursor.into_raw_parts(); - SetKeyValueBox::account( + SetKeyValue::account( owner, parse!("cursor" as Name), Value::String( diff --git a/client/tests/integration/sorting.rs b/client/tests/integration/sorting.rs index f12f80bc36a..19f69f3b86e 100644 --- a/client/tests/integration/sorting.rs +++ b/client/tests/integration/sorting.rs @@ -14,6 +14,7 @@ use iroha_client::{ query::{Pagination, Sorting}, }, }; +use iroha_data_model::isi::InstructionBox; use test_network::*; #[test] @@ -46,8 +47,9 @@ fn correct_pagination_assets_after_creating_new_one() { assets.push(asset.clone()); - let create_asset_definition = RegisterBox::asset_definition(asset_definition); - let create_asset = RegisterBox::asset(asset); + let create_asset_definition: InstructionBox = + Register::asset_definition(asset_definition).into(); + let create_asset = Register::asset(asset).into(); instructions.push(create_asset_definition); instructions.push(create_asset); @@ -94,8 +96,9 @@ fn correct_pagination_assets_after_creating_new_one() { AssetValue::Store(new_asset_metadata), ); - let create_asset_definition = RegisterBox::asset_definition(new_asset_definition); - let create_asset = RegisterBox::asset(new_asset.clone()); + let create_asset_definition: InstructionBox = + Register::asset_definition(new_asset_definition).into(); + let create_asset = Register::asset(new_asset.clone()).into(); test_client .submit_all_blocking([create_asset_definition, create_asset]) @@ -153,7 +156,7 @@ fn correct_sorting_of_entities() { metadata_of_assets.push(asset_metadata); asset_definitions.push(asset_definition_id); - let create_asset_definition = RegisterBox::asset_definition(asset_definition); + let create_asset_definition = Register::asset_definition(asset_definition); instructions.push(create_asset_definition); } @@ -203,7 +206,7 @@ fn correct_sorting_of_entities() { accounts.push(account_id); metadata_of_accounts.push(account_metadata); - let create_account = RegisterBox::account(account); + let create_account = Register::account(account); instructions.push(create_account); } @@ -249,7 +252,7 @@ fn correct_sorting_of_entities() { domains.push(domain_id); metadata_of_domains.push(domain_metadata); - let create_account = RegisterBox::domain(domain); + let create_account = Register::domain(domain); instructions.push(create_account); } @@ -294,7 +297,7 @@ fn correct_sorting_of_entities() { domains.push(domain_id); metadata_of_domains.push(domain_metadata); - let create_account = RegisterBox::domain(domain); + let create_account = Register::domain(domain); instructions.push(create_account); } test_client @@ -356,7 +359,7 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { account }; - let create_account = RegisterBox::account(account); + let create_account = Register::account(account); instructions.push(create_account); } diff --git a/client/tests/integration/transfer_asset.rs b/client/tests/integration/transfer_asset.rs index da844b08f3d..cbc23b190fa 100644 --- a/client/tests/integration/transfer_asset.rs +++ b/client/tests/integration/transfer_asset.rs @@ -3,6 +3,7 @@ use iroha_client::{ data_model::{prelude::*, Registered}, }; use iroha_crypto::KeyPair; +use iroha_data_model::isi::Instruction; use iroha_primitives::fixed::Fixed; use test_network::*; @@ -12,8 +13,8 @@ fn simulate_transfer_quantity() { 200_u32, &20_u32, AssetDefinition::quantity, - MintBox::asset_quantity, - TransferBox::asset_quantity, + Mint::asset_quantity, + Transfer::asset_quantity, 10_710, ) } @@ -24,8 +25,8 @@ fn simulate_transfer_big_quantity() { 200_u128, &20_u128, AssetDefinition::big_quantity, - MintBox::asset_big_quantity, - TransferBox::asset_big_quantity, + Mint::asset_big_quantity, + Transfer::asset_big_quantity, 10_785, ) } @@ -36,8 +37,8 @@ fn simulate_transfer_fixed() { Fixed::try_from(200_f64).expect("Valid"), &Fixed::try_from(20_f64).expect("Valid"), AssetDefinition::fixed, - MintBox::asset_fixed, - TransferBox::asset_fixed, + Mint::asset_fixed, + Transfer::asset_fixed, 10_790, ) } @@ -50,21 +51,24 @@ fn simulate_insufficient_funds() { Fixed::try_from(20_f64).expect("Valid"), &Fixed::try_from(200_f64).expect("Valid"), AssetDefinition::fixed, - MintBox::asset_fixed, - TransferBox::asset_fixed, + Mint::asset_fixed, + Transfer::asset_fixed, 10_800, ) } -fn simulate_transfer>( +fn simulate_transfer( starting_amount: T, amount_to_transfer: &T, asset_definition_ctr: impl FnOnce(AssetDefinitionId) -> ::With, - mint_ctr: impl FnOnce(T, AssetId) -> MintBox, - transfer_ctr: impl FnOnce(AssetId, T, AccountId) -> TransferBox, + mint_ctr: impl FnOnce(T, AssetId) -> Mint, + transfer_ctr: impl FnOnce(AssetId, T, AccountId) -> Transfer, port_number: u16, ) where + T: std::fmt::Debug + Clone + Into, Value: From, + Mint: Instruction, + Transfer: Instruction, { let (_rt, _peer, iroha_client) = ::new() .with_port(port_number) @@ -76,10 +80,10 @@ fn simulate_transfer>( let (bob_public_key, _) = KeyPair::generate() .expect("Failed to generate KeyPair") .into(); - let create_mouse = RegisterBox::account(Account::new(mouse_id.clone(), [bob_public_key])); + let create_mouse = Register::account(Account::new(mouse_id.clone(), [bob_public_key])); let asset_definition_id: AssetDefinitionId = "camomile#wonderland".parse().expect("Valid"); let create_asset = - RegisterBox::asset_definition(asset_definition_ctr(asset_definition_id.clone())); + Register::asset_definition(asset_definition_ctr(asset_definition_id.clone())); let mint_asset = mint_ctr( starting_amount, AssetId::new(asset_definition_id.clone(), alice_id.clone()), diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index 31862f45478..41644169a73 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -9,6 +9,7 @@ use iroha_client::{ transaction::Executable, }, }; +use iroha_data_model::events::TriggeringFilterBox; use iroha_genesis::GenesisNetwork; use iroha_logger::info; use test_network::*; @@ -25,7 +26,7 @@ fn call_execute_trigger() -> Result<()> { let asset_id = AssetId::new(asset_definition_id, account_id); let prev_value = get_asset_value(&mut test_client, asset_id.clone())?; - let instruction = MintBox::asset_quantity(1_u32, asset_id.clone()); + let instruction = Mint::asset_quantity(1_u32, asset_id.clone()); let register_trigger = build_register_trigger_isi(asset_id.clone(), vec![instruction.into()]); test_client.submit_blocking(register_trigger)?; @@ -48,7 +49,7 @@ fn execute_trigger_should_produce_event() -> Result<()> { let account_id: AccountId = "alice@wonderland".parse()?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let instruction = MintBox::asset_quantity(1_u32, asset_id.clone()); + let instruction = Mint::asset_quantity(1_u32, asset_id.clone()); let register_trigger = build_register_trigger_isi(asset_id, vec![instruction.into()]); test_client.submit_blocking(register_trigger)?; @@ -87,7 +88,7 @@ fn infinite_recursion_should_produce_one_call_per_block() -> Result<()> { let prev_value = get_asset_value(&mut test_client, asset_id.clone())?; let instructions = vec![ - MintBox::asset_quantity(1_u32, asset_id.clone()).into(), + Mint::asset_quantity(1_u32, asset_id.clone()).into(), call_trigger.clone().into(), ]; let register_trigger = build_register_trigger_isi(asset_id.clone(), instructions); @@ -114,7 +115,7 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { let bad_trigger_id = TriggerId::from_str("bad_trigger")?; // Invalid instruction let bad_trigger_instructions = vec![Fail::new("Bad trigger".to_owned())]; - let register_bad_trigger = RegisterBox::trigger(Trigger::new( + let register_bad_trigger = Register::trigger(Trigger::new( bad_trigger_id.clone(), Action::new( bad_trigger_instructions, @@ -130,8 +131,8 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { // Registering normal trigger let trigger_id = TriggerId::from_str(TRIGGER_NAME)?; - let trigger_instructions = vec![MintBox::asset_quantity(1_u32, asset_id.clone())]; - let register_trigger = RegisterBox::trigger(Trigger::new( + let trigger_instructions = vec![Mint::asset_quantity(1_u32, asset_id.clone())]; + let register_trigger = Register::trigger(Trigger::new( trigger_id, Action::new( trigger_instructions, @@ -165,8 +166,8 @@ fn trigger_should_not_be_executed_with_zero_repeats_count() -> Result<()> { let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id = TriggerId::from_str("self_modifying_trigger")?; - let trigger_instructions = vec![MintBox::asset_quantity(1_u32, asset_id.clone())]; - let register_trigger = RegisterBox::trigger(Trigger::new( + let trigger_instructions = vec![Mint::asset_quantity(1_u32, asset_id.clone())]; + let register_trigger = Register::trigger(Trigger::new( trigger_id.clone(), Action::new( trigger_instructions, @@ -224,10 +225,10 @@ fn trigger_should_be_able_to_modify_its_own_repeats_count() -> Result<()> { let trigger_id = TriggerId::from_str("self_modifying_trigger")?; let trigger_instructions = vec![ - MintBox::trigger_repetitions(1_u32, trigger_id.clone()), - MintBox::asset_quantity(1_u32, asset_id.clone()), + InstructionBox::from(Mint::trigger_repetitions(1_u32, trigger_id.clone())), + InstructionBox::from(Mint::asset_quantity(1_u32, asset_id.clone())), ]; - let register_trigger = RegisterBox::trigger(Trigger::new( + let register_trigger = Register::trigger(Trigger::new( trigger_id.clone(), Action::new( trigger_instructions, @@ -279,7 +280,7 @@ fn unregister_trigger() -> Result<()> { )), ), ); - let register_trigger = RegisterBox::trigger(trigger.clone()); + let register_trigger = Register::trigger(trigger.clone()); test_client.submit_blocking(register_trigger)?; // Finding trigger @@ -303,7 +304,7 @@ fn unregister_trigger() -> Result<()> { assert_eq!(found_trigger, trigger); // Unregistering trigger - let unregister_trigger = UnregisterBox::trigger(trigger_id); + let unregister_trigger = Unregister::trigger(trigger_id); test_client.submit_blocking(unregister_trigger)?; // Checking result @@ -362,7 +363,7 @@ fn trigger_in_genesis_using_base64() -> Result<()> { let tx_ref = &mut genesis.transactions[0].0; match &mut tx_ref.payload_mut().instructions { Executable::Instructions(instructions) => { - instructions.push(RegisterBox::trigger(trigger).into()); + instructions.push(Register::trigger(trigger).into()); } Executable::Wasm(_) => panic!("Expected instructions"), } @@ -399,10 +400,9 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { let trigger_id_unregister = TriggerId::from_str("unregister_other_trigger")?; let trigger_id_to_be_unregistered = TriggerId::from_str("should_be_unregistered_trigger")?; - let trigger_unregister_instructions = vec![UnregisterBox::trigger( - trigger_id_to_be_unregistered.clone(), - )]; - let register_trigger = RegisterBox::trigger(Trigger::new( + let trigger_unregister_instructions = + vec![Unregister::trigger(trigger_id_to_be_unregistered.clone())]; + let register_trigger = Register::trigger(Trigger::new( trigger_id_unregister.clone(), Action::new( trigger_unregister_instructions, @@ -417,8 +417,8 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { test_client.submit_blocking(register_trigger)?; let trigger_should_be_unregistered_instructions = - vec![MintBox::asset_quantity(1_u32, asset_id.clone())]; - let register_trigger = RegisterBox::trigger(Trigger::new( + vec![Mint::asset_quantity(1_u32, asset_id.clone())]; + let register_trigger = Register::trigger(Trigger::new( trigger_id_to_be_unregistered.clone(), Action::new( trigger_should_be_unregistered_instructions, @@ -461,8 +461,8 @@ fn trigger_burn_repetitions() -> Result<()> { let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id = TriggerId::from_str("trigger")?; - let trigger_instructions = vec![MintBox::asset_quantity(1_u32, asset_id)]; - let register_trigger = RegisterBox::trigger(Trigger::new( + let trigger_instructions = vec![Mint::asset_quantity(1_u32, asset_id)]; + let register_trigger = Register::trigger(Trigger::new( trigger_id.clone(), Action::new( trigger_instructions, @@ -476,7 +476,7 @@ fn trigger_burn_repetitions() -> Result<()> { )); test_client.submit_blocking(register_trigger)?; - test_client.submit_blocking(BurnBox::trigger_repetitions(1_u32, trigger_id.clone()))?; + test_client.submit_blocking(Burn::trigger_repetitions(1_u32, trigger_id.clone()))?; // Executing trigger let execute_trigger = ExecuteTrigger::new(trigger_id); @@ -495,10 +495,10 @@ fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Result { fn build_register_trigger_isi( asset_id: AssetId, trigger_instructions: Vec, -) -> RegisterBox { +) -> Register> { let trigger_id: TriggerId = TRIGGER_NAME.parse().expect("Valid"); - RegisterBox::trigger(Trigger::new( + Register::trigger(Trigger::new( trigger_id.clone(), Action::new( trigger_instructions, diff --git a/client/tests/integration/triggers/data_trigger.rs b/client/tests/integration/triggers/data_trigger.rs index 7e09d794341..e7096f7b024 100644 --- a/client/tests/integration/triggers/data_trigger.rs +++ b/client/tests/integration/triggers/data_trigger.rs @@ -13,8 +13,8 @@ fn must_execute_both_triggers() -> Result<()> { let prev_value = get_asset_value(&test_client, asset_id.clone())?; - let instruction = MintBox::asset_quantity(1_u32, asset_id.clone()); - let register_trigger = RegisterBox::trigger(Trigger::new( + let instruction = Mint::asset_quantity(1_u32, asset_id.clone()); + let register_trigger = Register::trigger(Trigger::new( "mint_rose_1".parse()?, Action::new( [instruction.clone()], @@ -27,7 +27,7 @@ fn must_execute_both_triggers() -> Result<()> { )); test_client.submit_blocking(register_trigger)?; - let register_trigger = RegisterBox::trigger(Trigger::new( + let register_trigger = Register::trigger(Trigger::new( "mint_rose_2".parse()?, Action::new( [instruction], @@ -40,11 +40,11 @@ fn must_execute_both_triggers() -> Result<()> { )); test_client.submit_blocking(register_trigger)?; - test_client.submit_blocking(RegisterBox::account(Account::new( + test_client.submit_blocking(Register::account(Account::new( "bunny@wonderland".parse()?, [], )))?; - test_client.submit_blocking(RegisterBox::domain(Domain::new("neverland".parse()?)))?; + test_client.submit_blocking(Register::domain(Domain::new("neverland".parse()?)))?; let new_value = get_asset_value(&test_client, asset_id)?; assert_eq!(new_value, prev_value + 2); @@ -57,18 +57,19 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu let (_rt, _peer, test_client) = ::new().with_port(10_655).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let create_neverland_domain = RegisterBox::domain(Domain::new("neverland".parse()?)); + let create_neverland_domain: InstructionBox = + Register::domain(Domain::new("neverland".parse()?)).into(); let account_id: AccountId = "sapporo@neverland".parse()?; - let create_sapporo_account = RegisterBox::account(Account::new(account_id.clone(), [])); + let create_sapporo_account = Register::account(Account::new(account_id.clone(), [])).into(); let asset_definition_id: AssetDefinitionId = "sakura#neverland".parse()?; let create_sakura_asset_definition = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())).into(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let create_sakura_asset = - RegisterBox::asset(Asset::new(asset_id.clone(), AssetValue::Quantity(0))); + Register::asset(Asset::new(asset_id.clone(), AssetValue::Quantity(0))).into(); test_client.submit_all_blocking([ create_neverland_domain, @@ -79,10 +80,10 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu let prev_value = get_asset_value(&test_client, asset_id.clone())?; - let register_trigger = RegisterBox::trigger(Trigger::new( + let register_trigger = Register::trigger(Trigger::new( "mint_sakura$neverland".parse()?, Action::new( - [MintBox::asset_quantity(1_u32, asset_id.clone())], + [Mint::asset_quantity(1_u32, asset_id.clone())], Repeats::Indefinitely, account_id, TriggeringFilterBox::Data(BySome(DataEntityFilter::ByAccount(BySome( @@ -92,12 +93,12 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu )); test_client.submit_blocking(register_trigger)?; - test_client.submit_blocking(RegisterBox::account(Account::new( + test_client.submit_blocking(Register::account(Account::new( "asahi@wonderland".parse()?, [], )))?; - test_client.submit_blocking(RegisterBox::account(Account::new( + test_client.submit_blocking(Register::account(Account::new( "asahi@neverland".parse()?, [], )))?; diff --git a/client/tests/integration/triggers/event_trigger.rs b/client/tests/integration/triggers/event_trigger.rs index 4f39c18690a..8269a244ad4 100644 --- a/client/tests/integration/triggers/event_trigger.rs +++ b/client/tests/integration/triggers/event_trigger.rs @@ -17,8 +17,8 @@ fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let prev_value = get_asset_value(&mut test_client, asset_id.clone())?; - let instruction = MintBox::asset_quantity(1_u32, asset_id.clone()); - let register_trigger = RegisterBox::trigger(Trigger::new( + let instruction = Mint::asset_quantity(1_u32, asset_id.clone()); + let register_trigger = Register::trigger(Trigger::new( "mint_rose".parse()?, Action::new( vec![instruction], @@ -36,7 +36,7 @@ fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { let tea_definition_id = "tea#wonderland".parse()?; let register_tea_definition = - RegisterBox::asset_definition(AssetDefinition::quantity(tea_definition_id)); + Register::asset_definition(AssetDefinition::quantity(tea_definition_id)); test_client.submit_blocking(register_tea_definition)?; let new_value = get_asset_value(&mut test_client, asset_id)?; diff --git a/client/tests/integration/triggers/time_trigger.rs b/client/tests/integration/triggers/time_trigger.rs index 8797ec8ca9c..9b9c76d3fe6 100644 --- a/client/tests/integration/triggers/time_trigger.rs +++ b/client/tests/integration/triggers/time_trigger.rs @@ -44,8 +44,8 @@ fn time_trigger_execution_count_error_should_be_less_than_15_percent() -> Result let schedule = TimeSchedule::starting_at(start_time).with_period(Duration::from_millis(PERIOD_MS)); - let instruction = MintBox::asset_quantity(1_u32, asset_id.clone()); - let register_trigger = RegisterBox::trigger(Trigger::new( + let instruction = Mint::asset_quantity(1_u32, asset_id.clone()); + let register_trigger = Register::trigger(Trigger::new( "mint_rose".parse()?, Action::new( vec![instruction], @@ -97,12 +97,9 @@ fn change_asset_metadata_after_1_sec() -> Result<()> { let key = Name::from_str("petal")?; let schedule = TimeSchedule::starting_at(start_time + Duration::from_millis(PERIOD_MS)); - let instruction = SetKeyValueBox::asset_definition( - asset_definition_id.clone(), - key.clone(), - 3_u32.to_value(), - ); - let register_trigger = RegisterBox::trigger(Trigger::new( + let instruction = + SetKeyValue::asset_definition(asset_definition_id.clone(), key.clone(), 3_u32.to_value()); + let register_trigger = Register::trigger(Trigger::new( "change_rose_metadata".parse().expect("Valid"), Action::new( vec![instruction], @@ -147,8 +144,8 @@ fn pre_commit_trigger_should_be_executed() -> Result<()> { // Start listening BEFORE submitting any transaction not to miss any block committed event let event_listener = get_block_committed_event_listener(&test_client)?; - let instruction = MintBox::asset_quantity(1_u32, asset_id.clone()); - let register_trigger = RegisterBox::trigger(Trigger::new( + let instruction = Mint::asset_quantity(1_u32, asset_id.clone()); + let register_trigger = Register::trigger(Trigger::new( "mint_rose".parse()?, Action::new( vec![instruction], @@ -165,7 +162,7 @@ fn pre_commit_trigger_should_be_executed() -> Result<()> { prev_value = new_value; // ISI just to create a new block - let sample_isi = SetKeyValueBox::account( + let sample_isi = SetKeyValue::account( account_id.clone(), "key".parse::()?, String::from("value"), @@ -199,7 +196,7 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { .iter() .skip(1) // Alice has already been registered in genesis .cloned() - .map(|account_id| RegisterBox::account(Account::new(account_id, []))) + .map(|account_id| Register::account(Account::new(account_id, []))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; @@ -223,7 +220,7 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { let start_time = current_time(); let schedule = TimeSchedule::starting_at(start_time).with_period(Duration::from_millis(TRIGGER_PERIOD_MS)); - let register_trigger = RegisterBox::trigger(Trigger::new( + let register_trigger = Register::trigger(Trigger::new( "mint_nft_for_all".parse()?, Action::new( WasmSmartContract::from_compiled(wasm), @@ -298,7 +295,7 @@ fn submit_sample_isi_on_every_block_commit( for _ in block_committed_event_listener.take(times) { std::thread::sleep(timeout); // ISI just to create a new block - let sample_isi = SetKeyValueBox::account( + let sample_isi = SetKeyValue::account( account_id.clone(), "key".parse::()?, String::from("value"), diff --git a/client/tests/integration/triggers/trigger_rollback.rs b/client/tests/integration/triggers/trigger_rollback.rs index 701b2801f92..182045a2c7e 100644 --- a/client/tests/integration/triggers/trigger_rollback.rs +++ b/client/tests/integration/triggers/trigger_rollback.rs @@ -15,12 +15,12 @@ fn failed_trigger_revert() -> Result<()> { let account_id = AccountId::from_str("alice@wonderland")?; let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); let instructions: [InstructionBox; 2] = [ create_asset.into(), Fail::new("Always fail".to_owned()).into(), ]; - let register_trigger = RegisterBox::trigger(Trigger::new( + let register_trigger = Register::trigger(Trigger::new( trigger_id.clone(), Action::new( instructions, diff --git a/client/tests/integration/tx_history.rs b/client/tests/integration/tx_history.rs index f24c869d5da..8cbf9a3f5cc 100644 --- a/client/tests/integration/tx_history.rs +++ b/client/tests/integration/tx_history.rs @@ -25,14 +25,14 @@ fn client_has_rejected_and_acepted_txs_should_return_tx_history() -> Result<()> let account_id = AccountId::from_str("alice@wonderland")?; let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); client.submit_blocking(create_asset)?; //When let quantity: u32 = 200; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let mint_existed_asset = MintBox::asset_quantity(quantity, asset_id); - let mint_not_existed_asset = MintBox::asset_quantity( + let mint_existed_asset = Mint::asset_quantity(quantity, asset_id); + let mint_not_existed_asset = Mint::asset_quantity( quantity, AssetId::new( AssetDefinitionId::from_str("foo#wonderland")?, diff --git a/client/tests/integration/tx_rollback.rs b/client/tests/integration/tx_rollback.rs index 8f5186aa66b..0c04bbec3a8 100644 --- a/client/tests/integration/tx_rollback.rs +++ b/client/tests/integration/tx_rollback.rs @@ -16,10 +16,9 @@ fn client_sends_transaction_with_invalid_instruction_should_not_see_any_changes( let account_id = AccountId::from_str("alice@wonderland")?; let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let wrong_asset_definition_id = AssetDefinitionId::from_str("ksor#wonderland")?; - let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id)); + let create_asset = Register::asset_definition(AssetDefinition::quantity(asset_definition_id)); let quantity: u32 = 200; - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(wrong_asset_definition_id.clone(), account_id.clone()), ); diff --git a/client/tests/integration/unregister_peer.rs b/client/tests/integration/unregister_peer.rs index 6f3a6ea6887..84a2e4fa5b3 100644 --- a/client/tests/integration/unregister_peer.rs +++ b/client/tests/integration/unregister_peer.rs @@ -34,7 +34,7 @@ fn unstable_network_stable_after_add_and_after_remove_peer() -> Result<()> { // Then the new peer should already have the mint result. check_assets(&peer_client, &account_id, &asset_definition_id, 100); // Also, when a peer is unregistered - let remove_peer = UnregisterBox::peer(peer.id.clone()); + let remove_peer = Unregister::peer(peer.id.clone()); genesis_client.submit(remove_peer)?; thread::sleep(pipeline_time * 2); // We can mint without error. @@ -82,7 +82,7 @@ fn mint( pipeline_time: std::time::Duration, quantity: u32, ) -> Result { - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); @@ -106,13 +106,13 @@ fn init() -> Result<( let parameters = ParametersBuilder::new() .add_parameter(MAX_TRANSACTIONS_IN_BLOCK, 1u32)? .into_set_parameters(); - let create_domain = RegisterBox::domain(Domain::new("domain".parse()?)); + let create_domain = Register::domain(Domain::new("domain".parse()?)); let account_id: AccountId = "account@domain".parse()?; let (public_key, _) = KeyPair::generate()?.into(); - let create_account = RegisterBox::account(Account::new(account_id.clone(), [public_key])); + let create_account = Register::account(Account::new(account_id.clone(), [public_key])); let asset_definition_id: AssetDefinitionId = "xor#domain".parse()?; let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); let instructions = parameters.into_iter().chain( [ create_domain.into(), diff --git a/client/tests/integration/unstable_network.rs b/client/tests/integration/unstable_network.rs index 55aeeaba9a7..d0cd9ce186b 100644 --- a/client/tests/integration/unstable_network.rs +++ b/client/tests/integration/unstable_network.rs @@ -78,7 +78,7 @@ fn unstable_network( let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); let asset_definition_id: AssetDefinitionId = "camomile#wonderland".parse().expect("Valid"); let register_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); iroha_client .submit_blocking(register_asset) .expect("Failed to register asset"); @@ -100,7 +100,7 @@ fn unstable_network( } let quantity = 1; - let mint_asset = MintBox::asset_quantity( + let mint_asset = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id.clone(), account_id.clone()), ); diff --git a/client/tests/integration/upgrade.rs b/client/tests/integration/upgrade.rs index 19f2f3b564a..cfa822046c2 100644 --- a/client/tests/integration/upgrade.rs +++ b/client/tests/integration/upgrade.rs @@ -17,19 +17,19 @@ fn executor_upgrade_should_work() -> Result<()> { // Register `admin` domain and account let admin_domain = Domain::new("admin".parse()?); - let register_admin_domain = RegisterBox::domain(admin_domain); + let register_admin_domain = Register::domain(admin_domain); client.submit_blocking(register_admin_domain)?; let admin_id: AccountId = "admin@admin".parse()?; let admin_keypair = KeyPair::generate()?; let admin_account = Account::new(admin_id.clone(), [admin_keypair.public_key().clone()]); - let register_admin_account = RegisterBox::account(admin_account); + let register_admin_account = Register::account(admin_account); client.submit_blocking(register_admin_account)?; // Check that admin isn't allowed to transfer alice's rose by default let alice_rose: AssetId = "rose##alice@wonderland".parse()?; let admin_rose: AccountId = "admin@admin".parse()?; - let transfer_alice_rose = TransferBox::asset_quantity(alice_rose, 1_u32, admin_rose); + let transfer_alice_rose = Transfer::asset_quantity(alice_rose, 1_u32, admin_rose); let transfer_rose_tx = TransactionBuilder::new(admin_id.clone()) .with_instructions([transfer_alice_rose.clone()]) .sign(admin_keypair.clone())?; diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index 0aaacb4f5b9..8aca7cef98c 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -391,7 +391,7 @@ mod domain { id, metadata: Metadata(metadata), } = self; - let create_domain = RegisterBox::domain(Domain::new(id)); + let create_domain = iroha_client::data_model::isi::Register::domain(Domain::new(id)); submit([create_domain], metadata, context).wrap_err("Failed to create domain") } } @@ -449,7 +449,7 @@ mod domain { to, metadata: Metadata(metadata), } = self; - let transfer_domain = TransferBox::domain(from, id, to); + let transfer_domain = iroha_client::data_model::isi::Transfer::domain(from, id, to); submit([transfer_domain], metadata, context).wrap_err("Failed to transfer domain") } } @@ -512,7 +512,8 @@ mod account { key, metadata: Metadata(metadata), } = self; - let create_account = RegisterBox::account(Account::new(id, [key])); + let create_account = + iroha_client::data_model::isi::Register::account(Account::new(id, [key])); submit([create_account], metadata, context).wrap_err("Failed to register account") } } @@ -563,7 +564,7 @@ mod account { condition: Signature(condition), metadata: Metadata(metadata), } = self; - let mint_box = MintBox::account_signature_check_condition(condition, account_id); + let mint_box = Mint::account_signature_check_condition(condition, account_id); submit([mint_box], metadata, context).wrap_err("Failed to set signature condition") } } @@ -634,7 +635,7 @@ mod account { permission, metadata: Metadata(metadata), } = self; - let grant = GrantBox::permission_token(permission.0, id); + let grant = iroha_client::data_model::isi::Grant::permission_token(permission.0, id); submit([grant], metadata, context) .wrap_err("Failed to grant the permission to the account") } @@ -727,7 +728,8 @@ mod asset { if unmintable { asset_definition = asset_definition.mintable_once(); } - let create_asset_definition = RegisterBox::asset_definition(asset_definition); + let create_asset_definition = + iroha_client::data_model::isi::Register::asset_definition(asset_definition); submit([create_asset_definition], metadata, context) .wrap_err("Failed to register asset") } @@ -758,7 +760,10 @@ mod asset { quantity, metadata: Metadata(metadata), } = self; - let mint_asset = MintBox::asset_quantity(quantity, AssetId::new(asset, account)); + let mint_asset = iroha_client::data_model::isi::Mint::asset_quantity( + quantity, + AssetId::new(asset, account), + ); submit([mint_asset], metadata, context) .wrap_err("Failed to mint asset of type `NumericValue::U32`") } @@ -789,7 +794,10 @@ mod asset { quantity, metadata: Metadata(metadata), } = self; - let burn_asset = BurnBox::asset_quantity(quantity, AssetId::new(asset, account)); + let burn_asset = iroha_client::data_model::isi::Burn::asset_quantity( + quantity, + AssetId::new(asset, account), + ); submit([burn_asset], metadata, context) .wrap_err("Failed to burn asset of type `NumericValue::U32`") } @@ -824,8 +832,11 @@ mod asset { quantity, metadata: Metadata(metadata), } = self; - let transfer_asset = - TransferBox::asset_quantity(AssetId::new(asset_id, from), quantity, to); + let transfer_asset = iroha_client::data_model::isi::Transfer::asset_quantity( + AssetId::new(asset_id, from), + quantity, + to, + ); submit([transfer_asset], metadata, context).wrap_err("Failed to transfer asset") } } @@ -925,7 +936,9 @@ mod peer { key, metadata: Metadata(metadata), } = self; - let register_peer = RegisterBox::peer(Peer::new(PeerId::new(&address, &key))); + let register_peer = iroha_client::data_model::isi::Register::peer(Peer::new( + PeerId::new(&address, &key), + )); submit([register_peer], metadata, context).wrap_err("Failed to register peer") } } @@ -951,7 +964,8 @@ mod peer { key, metadata: Metadata(metadata), } = self; - let unregister_peer = UnregisterBox::peer(PeerId::new(&address, &key)); + let unregister_peer = + iroha_client::data_model::isi::Unregister::peer(PeerId::new(&address, &key)); submit([unregister_peer], metadata, context).wrap_err("Failed to unregister peer") } } diff --git a/configs/peer/executor.wasm b/configs/peer/executor.wasm index c6af9008e112a65ea317eff5a28790841f787c27..48c0f25b41cc2e2c2b2ef6a26a9d75b19fd5ad4d 100644 GIT binary patch literal 389245 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

wma#Zkdo&zd)&zZ0a-aA&o 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

4J9bVM8&+eTF#)55 zE%rjUS4u+*b?A(r>@CM|Je3cfuMd4zZ>o~FFfMK}{BRE^k(s`EL!5Aj=B zc6(F|2p4k z#ma=kPe#9getieDXS>a|nE8Nh`yU)A5hPf-2qvl{H@4nFd<=CJT z_)iah&e&!vBcnJKH`pIMg=TXeuecD8Qpgk6b~R7(s%*N`)4xnAl9US}YG&u=amOb_ zp(2a&zY}^S^iL%B>3Gyh3UgmukKHXZQFSYamy4GGP1Mz$O^8tPin;r z2;EOKnZoy2JNH-KubLVC%4TD1N4AL&^+bH88&&Z4G!cI3wsr>B@!&l^vqq24cOIWP zU*gE-tp3z+dqgd)9|{AZ%iitQU(@n9nGIlLuUtQ%b};LZDmtEM+0`X$hbP9JbjRIE zb-d}$oq>f(eECa1N;Cf1;pjgDZ`|w_WRp6t0j4##IWprA$7ZR`9qQU%(=C631M|>A zAvDLd0QbX&;o@U$ZgJo?b#`-)6gUzgf4%6kdeOeuGJLNnfQ|1JeS(07fqYxoW*orGRiZvwNLv!p)b;u2=!xL_ZZoYL zLP^mlkjPd#u!m)pMP{d>=Fp1O{)Zc9vX-e>`AN7WKgvSZo5;k1jY68SmM{56^W_^& zmv1y%z7fy$ji$&qni$_Cx1%}T-N6$q?fxrBEYUwp4JY1iyg;#SbrY?>8}Ytu=R|?B z#J4wMV_7W7D~r*yuKNz83IcZzb>m*!wC?9Jd)IEJ=`j0$HYC*`$gst0nAQ6i^F@UZ zgi%%FukIm{C{4+bE9Rm*R75OO@ek~B>Z7dsVV`}nOPEHk1~pPBKz2M9u93AwF{22> z*ozY%tK|9QnwTgQE3b)i2T4;jdtQHBfmZY(Cs-~DDBeBix8mGclJ^2Hl~BRK@=LLMton4W z9_&P8+8D0a-cmUc+8SDYi=kDcAB=H;vH~GV5Ur^!oi|zAF zbf;=PWRWW~+YGyt#J336+JY^6O!|E!bKg@nQpu!=_=_%n=PHUoV%=QW~@-kY< zJeaP(ga;E$i!X)1+M9)4v-MYBP840QU5}3KqNEXR`YNW#UYo^UZx_7DqU$Y9##xjY zyvHs$zL}<|JApg5d%>W@C(BL_a0xgESlATHy}KDOc^>Y|EYDC&wKu^bgAzaJLor)E ze#9imJhhW>jR_!uMms4Ld1j-M+KnlopD`071-37PIOEYZ^qJj9>t+KHiX=~6O&nej zwgM^v&+^4^pLLw##+j0H&gDvoA9*()?L8Gy1bTypt?xTn(P|U)HEYL2(rM(u#Q49 z>`Z`-!3^VA3m*BfGq{iO7=mM)xu`1kf6vzET~!n3c1!&_;&N}gOdHp7#s`V9X+>f2 zB6kAlnUOdl*Pc8v`O!}ok(#0M=!Na&YQx*qDe#R>wL8BGw^~j`wQKqHY*C)M5VW#kitt7ghJ<7YQxIgP4$n_Vv0{zBQNy@;iQrWDsNxJAdAKH(JClt)~1 z1-lekTODDtXu|zg=ySnvx-mw4C)Vu?y)Z$z-@|Jr$(Ql3RzJc=4|VzJ_0SY z(rR=NwLMk`)hz{wF&OYQwmzye`jA)i(eVXhw?K1iQfF${bHJgHe5@{umW%qFGxyc? zx;H;cZ`8#&6h`TH$SMR_m>jBc_=&-!z85@)hr!{!hebeGQ2E|XA&D0YE}#b&Za-(dXF~93Ixm{8)SK= zjj&uQ`EX-yehZU7Z zNCsve>EvuGuD!5)e4RM`-r{=2$p|p><5?J!xIaLp*Zq>9uFL9VjS|IlR=Z|hhhY0? z*lInV>opz>ffC*Z^LXJPfe$Eb!X`8aPpmft%CyyfY$#n%Y=Lu;u${J4{(?Y|3j!G+ zKXalD-)N^zS-o58iFP-xn_D%4(VLj_uRxAage8d=o#J^vsx@1XoZ6zH(NFz@nSwe> zZAPqT$rSokm?>!O^sJbIq4u7&oC99~r%?`U8^>sZ*dI?L_|U% z=rh68d7(BTj(dYR5`36}!RT=%?{w?9{lSsC$)#(lA}nnZl!pE88AJZ`1v8ad#Pt6 zX6jmvL}*On0Lt^0B!I$R94T5!4XRUS{_L})GA9#IVe_*nX`GW#QE9hdzX?xJeb=wU z*3CL^h%gDhe;M^huyx1UZG%%vLs+xBcw^$@w+V;#7O$W!=_Vu%Boocv^0tvANQhkg zZ9)>Z(|{s+xjNA?kU@tj?;i*=!^yc{Z`L*Q^~OBRT5+PN0_`NzK_U{3-miW?_?zmt zu#y%p20!cP^Ys4T(9@Qn`xs0z(hvA82}PBYkGS=gXHTIc!SOKbcP2KR0`#nXKQrBI znlBZr!iRm?Gt$laRs8d0!#{pTHq5jZ7~q2qgAPo%qIBEe&zemi*h1d^_(qnZQ)qiz z(1fwt>)x;d>n@P(7EsXwC2$}c&qY9Z*|DO{w|G#dP)WUz5ZuO=!67{ntTCcJ;(F5MJ zk!XLS2p?j@or7xo>#$gJM1~5lKb7fGtEEl_hzu3YUSiY?FY#ObYoSvBR*u$p#!~Dl z^e-`wY5glLOTA7Lx3Er-mM^=K{>AEyCFTbGD=n*~|49FWXY4I5=;ssLs7jgdy?)&a z!oZQz)>#%>=vY(Il3#gF#J?KLLSOWBiwJF_-_o*hrYfF>^g>Z=EDKd=?Wn6v3UWc6 zNtT5j(w9(HPVT1I6>-YhSU zbMJD&Yf+;m6|4G(I=P50q=V^fb{RW4g0<3dd3ExZC@JAPZh!F-`W6gC4j_yI$7n{2 z*RIX>0PYlRG=m9iLWYZ1M}~_D_Iluf{z+ppkdY7Z>ZPJs#U+WMi7FieEdyXgme5d2 zhbRoBz>*5gkX8(PIIzZpz%s*S)H^TZ+&kJ=?=(k@T$o+`x;L=_IK9NC}ip8?*K#FRO6K3B3bke~8?9?GQu^STm^ z`ir`ve*UtqD5}4rD_tAbkNnj;?b`;zr&aBJ@oySMpd;V64t z85;HcEcJ{CG2oYDtO$A2>=1J>IwVsArdhkcu;bOa%z+s4vidE*lTgHl2W)PB?C<{f z86R?4|5{t*?wpDM25wunyVr|y+p1co-RYaFmj=Yfx!gvDxD-m9cumfoBOI%rCrt`J zY=^5($#0#?v=4tF=wSVazu5oqmxw(+pmuj2UCuz-Tr47`w^NUZ{xGhqDfW!Yq%?Kt z`rsCw!08$;cWu{Z>Y%y!vK-S)9FV zku2Z!vK&iK@1H;o2_M^bj$;S5H>Z}J?QQHk*c6_^8<+sx75Yt<$?Aa_ey~@vdC4Yw zlC<-Ty^`tK#GBFir5*Wxw|!L4QAB=ZYO8)lbu3M}xTpJE&uj9LX!rsc;O2Y3)mrtd zGx8w&_AN7t>hu;BzYR73|uoKt$iA@c8OCXQt~XL%;C5r~xr80L%?t zU>Q&U86cKS4det>@N=ABxm!lZG(N85^m;jOM(HtziG_1c)Bt-5HZ)+eZ{!V+7UaYP=)&g#B?CO{ z!!$NEu+aO!MXt?UO_UC80@oZ5z__wuhXzUYHfFkgRg+0UQK>7}?(RRu3$Kq3^dC~c z=M?oyP~%v;H0!A{oAKrM(?AjJ>KS3d^kqK|Tcmm3r_@?oD2Qs+Wvi&xOb1I;zax@2 z%&`&)KxQgt5u<&^k{I(2P*DeMj6^$eZX$BKH@%XFckL1cJ zZQNAv!1m6s(=k{`OY@C-Akg~F7vl~21U9^iYytILe3S;%5_Vvqse4143G zxCp>zq_@)6cR9ka5dFmk}Jx$yN{O!aVn;YeVGyRm@_TXcX;q83ZEK30sr zrB|C~-=`rn1Y@s}Xfx&+``Nat^S@7ZCd3Tb*oEMV*RjYm_5IB4JQs~aZ3qbP+_X2G zo+IL}+&%hl)Q#DO-n$LDXbYqIY$Jre4$w|Q+{Mb&Qz*vn?OMQYt!z1(_(0G>syE}E)J@=av3RG_{}oO;fpnhfLWL|Q*`WnoHC z)JkGUu&Or2s+U8h9RY>t#_x#%i#+k|R1flFnv*9)jDC1of5FlC#x&0lePb3!hpOM# z560AVSG{zX>Ov#es;p#QH;mldsn{fkl$>f2PdNbhU>TRjt~GX+#B+>2kSKxBd!#g> ztqS9=$DrIdzCL8(xAyu^KBC2Ey{$gq!>5I`RZ7Cj)vEvWmriAAr;?wbTdj!NfVS7D=$6_tFOwAp3g^~1|ot|2LU+q&twIQ zmY^e?wOJdC{tbM#0X}HrmEcmhIceB!PEv{?2TAqy1py*7Izu6pCutIC__yJgfDTb7 zf(wa^rf6vWREbb*d7;=&c;*tz|GHQX2B$=~g2qnKIMOe0KJO`e`dn>~zD?kx;TYS~ zXtYnj1fvs*4#m)zR#0uN-+1O)kw;O~G?+O19wN^-sY?42+PHDFK_7g@$eIIvh-{UQ zoF)Gvmq)R?XrXUcCN5Yae8T9#)yjq5z&sUXsJlobU4S>$o1k=u6^{Ne&xbv~lO|#r z+*r<-l~f%Ne~@UJn%yK(@5HAZ40e>+l8f!jE~p9TlVzv=k)Na_dt~&I;6IipGu1;n zp;Ss&S-(!N2@}+7=4i}`L^yqw4kV*TPK2kfZnp&kzfJx48%}0apu+VJ`Qz%%rSwW- z`*@X_T80nEYAoZURR@Nl`6ccRJJefB@1XJhaf@p>86~QpjUQS=RT!PD@|^qwYF()Q z4>X7|)vII7H{Fca_r0IjXK(p>?JxUZ_l(!+>-X?_e#@^qt#;(%!RwgUaU6cH$$@ zx@6K~`F%?IO74?GLAEcrbIUf%z)~xkl5|Cu>oq`M_QO^~sTU|#zkn5=; z07M`Bm>>lq*(tDI5ee17;(bMctN-x=r9dV=Hq`pB#0elDULlPPgtkeJWm23duMD$( zlSIBuHwi^ee|#Iiq0Up{=91U()`z0<0OxZ?;>HFFb#5d9(~>TyG;#o=$fXr&DnTsi zAQlGV6gq?n+*HU1_hT=;bJRqU<(G1Sf+)Xe6va&Ae;l-cvCuF-h$2eII}&$Ds9qt} z$8jChtG?J6YLK2PNS_*xa*u)P{zx@HqU=G^qEtB_xrOFKH2{b0RlB|&bup@TfS&zOqG$Ay2R16UAt=>BYB7yS!o@#$TMu>@2__Mi;~qm)nppRTF(CG%cK7(P z?twxLPm7s)Y@h1Az=QNY0Yz>cXvD0Utu6u~FxSy$x>MLpj}+DBlXM`Rp$YU|IY`Ax z87552_okNh-)g$L4_1Qio(<4yj_#!^Hr>U*Px^uFRu&^cM}XBNRa|tie83Bdgl6Um zrUf7=EZ00G0TeS7ksnx2g+DmkT$R>%9+&Vyoe8|Lt|6J&;=zNP zDhte7ykOd%t7%(or>$76W%)iYva{+oNyQSq%L?m52J3MwN}4HJ%BAd7f1C5GnGRHk z_ad%P4Md-N%ZCbcX38NAOYF@Wq+UE;DoDOQPqoC;`aF2MwE|bIb_A|y6}GZPL$m-v zaok!oDASe|uBUEqSICky6tySh`KN2|Ike~L4R!NA8Wbyt24Ry^12h=-WfFJ^jsOFA z<}uonc?|ai`yTcDf!yE(a!BKw2~rJ_1=kFwZ=u8N$BS`BfPZ;1=Q@8dJE7~$!HnYs zamydfj_W#iFuPAqpoXeN(`;FisBaO%^Tdn`9fHNc-GmOBes>FH4);>?mZA$Xd-Q%U z%G}ZWEr3A2rl*wz5g67WNgeZ~$3q*~TRvK7&|*^2+6M|7Po%ohS=6?sV$31W~}^1G7MNk3&Txv`2=be@u=JGZG_vPYVSiyQz z{65yl^`2zTk-s`58q@l@X=o0?k>Rm3ZU>KF}vr8>eoN1|Oz z1Zgcz_3O)il^bgNf(C)M@(7lbSjr{5Nr7u&c67i9m3IxUsKIWD1Qs?Vw~iP_Ag$Mm zLbP#=5e(|Kc_l0Z`BFG}AZtQHE8?O4M?lu;QgOQfqYZQ-9@$r_`G!FhtdbZn#@dcW zzXO}MkIwdG0=lG z*k)2{NV1~EnVtFY16r=k6B$P?Cr7o=bu|>w_MDWx^0@ zcILrf@d>Q5j_++~j*6+&D@x^y-{8a0F$YEc=2Y!uh!=x!u7TVOC9h_ba8C?N4G*>a z-k-1KO{hN4UxQwqr?nG)cb@!6@5vwd3Eo_l$$=O+mcNpdJPt`4L!Al3N_IIt#4Drx z1HuQ4R2B@@F#KKpzxV+yh~uiJ`VD?U3vJzlOY%E&w^1HCn15fc5y-hx7(8)Y9_HH) zisF-d%Qa+Ios-oOXaY)UZ#Y_pLYkQXDmr*KN4mo%1Y~ri@JtKREbKsX4a-zxOAH=j zWffqGWM46H1R&2A+os`Ubm3vx8;~tK>Uk}XtdO~@GP<;i)I%OMTZoz`T1DNhQM0*1 zqt|f&{H0v7^w?_DHo!{#Y`QiG6G;E@4!UZBXc{%5RcFy{ski<$AvuDeBBX$@uoTDc zPAFk5DG?gqpt?;1K)WpJyP5p7xCYpCk;$hndxY@+B`_#HPbaDJ4sH_v0s!*%lxm!6{9H9*UR|?rA5sH8}dT5ZX&K|QfRn6?v{bzfxkYK~XGds>Yd*?al+Fiy5=ZuE6RhdPg@NRi4y1gb=r+GVn$@j^bo*B0xJsEjy@*a2l0ClFuK92#5lSID(Mos;VOh zg}sYc`RQ!gMgW)JyD_|UX65^=X$=_5B9s+y-@9y2%8p>+%F326-=`4B7_@9Y z_r2UN4kKX88$)uyMncvw1M@Dp3PbsMfPwRVd49!gAeMe09P9p2r-fBKjTw_#V59gx zUe*FQR*lzSY#8fimiazFvOVHx8UJ4FvtcYlz66@jQ3NqvyebuE>{8W6rJ#?K=)-r_ zg!;6*Dn^O00n?R=tje+QLfkQr8M<_r7TSK?1&glap&1wpEUi@7{f(YOU(yb;-i8jZ zQ2>}&D1;(63US-)N);l)RHTIDq6ig?T=uGp?>eOoVJ4X_XS@RcHAZz|1cTvl7H>Zz z(8B38tK2LZf#oOzLlN|aIpH9Gf!um*>S}-ZXYr<8dgp{wah%`a5hmXEcI3Ctkr4gwFwy3W9t}| z3gN|5l!fFkxyCExuVLl{O(yQvY9D}BL5r8;RXgSCeO@;xI6$VJ2cBEkO(Xc|@UIn%#yC1kt z`x1QHMp`9?BjO85K`Hbc@R?FT4!%WNBHJ6CSA!RNqDpft8-mGr0=G85#~Lb5{DRJ& z0#t!pNi#9*7)&FDR1TB7*jCPdaHc)Q)}jE^SeLaDT<;T?S%*1D{^>I3p(!Ts?CynGYH8>(s9f4nfj8J+5<`#@oS1X|u;g&hK z+*H!;1Y&e{yu+d?d&-Cp_(@~&Zd^7+BX}>JSVR78UEzQN4syn1y^SnG_~z6OVTU&+ zZ+0SX$0dNoZMfMF7$!gd{T8UhFawB~JUlE>A-uID3Kj*rPKT5Jt`DtA=DM==9|cHkO`!bw#*XcptV<+jsvXdu+V5xi$f37 zR??b);#R=(h<%|jw=yJYU5br9xDr>#)slE24ow3USs^5(gYcN4Xblfyi=NTB{R%aQ zAxIZL2Xz5*)oOOZjaz`J6IfP5A=d5MeSUzTWm^6bAiP%x6vO(*fkxpjC1n(&4vngG zL@-EF9;Vm?Qy&oq0)G`sN%8Q&=W~lMAK`84XA5Zfv>;J_IrC3T%7Eo(U4I46BeTMW z1zfEhbkXjiASCFy2tcx6P!E)P3-Kn1nQ#Q(iV;7la{Yuiz6ytNH(G~RICsVRw5YW-L!d%P=7P~^sQd)>? zMM{O`*rU(|wT{$F_!F@}SJ$WN3^pmOtS2*GFu&ePY6(}w0goh(@y3`8fST4|(*Uwj zsxxdIq}8%^ZPXMXgaeCE>^@Xh?KZNZ5Np8XK~y8935-aD7XC%(UNoH>!3ctfpmSj{ zVZi9;S%y^W#-J*3ETxI3n3zqe5t<#D(P9{W2z@6hRtB%YL=z71VPElWdNdi&1dNDp z^I2mcttdC(r{O>rAy{J|1Hc4q2jMleZkS(!xQ#3##AFqiba^7sy2W5OKc)jhs9lUC z)q=s#R(!G};XM=uBSFi8 z0Ol|jDhE7jIHUuiZep~E^Qy!IX=a6>sxm9V&y3}p_(gq8Eet>~sRlGK6#vXah!q#M zFO!C_Ey`d`Fo`gF1z|l9V`C=!R)*qij<(<@%`AqMJwVu_8L$#ZX&o^(51KO>mgNC!JiguD z2NisB5GwvjUS64nzx9O_qjD@!kmfa&`TeM|=gcqd-)HR`zTTpu`fsRqVbb4=$%vFF z^W@>cu5w3N#)vr$2&$;%04@4x#EZ3{*?1`4Qg91R**vruWSTP~#qty*l2$R~QNV+! zj?qCHBd4*7f*xWdz#OJJNgs(n$OsnLduouPal{(Z1mhS%N1PQvevMxf6Z#D;U3i^Y zC<$i^>(eZ0Oa}>xGoO`-eCZgz+7)v6cYlLY6@I42OgUm9Yhe&l{!{fjp%X-e4`j zS8qa}qcsFIONTw9pievEbE;{DVvHrI!L*1_c!eR3CN{Q%f-{cE^dZOX(7qEZB|6mr zmLt;o1(f2bt{r|WB{WSdC8t;^h4}Trw@R{77g#w&_&2QIM(uhOof*k%BRiI4_`z$V zZI&q9n&yfM#mWe^5)QEB(RP9cVp#-N9l>Lzo&Xm(LV@0sXMm(xz;ehK_av@@)+;ph zX}!Wnw=FYPkfqhEAor5xf>)3y1YQ8H?AeE_)p!M|5ID4gyy%A)T;EpR3Uar-Vg+eI z+=!BHTh*)}{|H<`5|%_Yw1m9m!K$e++|+$`1 z=`bDHa~@Yi(s09lk2Ktfv=(g@vnO>hQeZJuV9^n1v-!hjj5tCPrYsuwD7~NRfiiEH zY(#6(rpX3tZXuSMf@8J2tylq6m~05$Tnml22S~FA!u(7&DRi&A4Fm(MRg5jEiSdTk0>+zP2Qc2OMi(&LY}5%c+*bNQfp&>{fa3Dz8Zq4di6Fat1UczPki7;$ zHZW%!1bOPp2=cxkK`e;%V^rESsFMB!5v^52kcTfrkf;4n0S+cqbS6sB>q?)E9XOd8U!NP~bv%!V8MN{kp{LgkwKO4lY-_%CPdw1|GoOx`2W22_mg0%|CRRF*pE{jnyCE$9_4Sj23><>$|YYL6?d8`F8Rm={PZ zkW&$SPyk&YXD#817!R~`493&Q;KJZ@+dXnI@7S07(@7ne$MM7jTMYff<|!>L-6r+M zNg=eeRVRHInma{{fC-U@!bhO#0vLpjfbZ}XzYsk^D-)by4!NeCgGPc3R21#ihf+dM zOiDQ3o0J!a*wHdGq5>e?5VHMlemL zemH;c8?PKbvv5BP0W#@fW|vTg*24vf60Tb#$7L$7*kxMzcP3G^g?5@E_zAxeAwWU< zWY$Fdj*X9)nun$lQPD!GL4G2cV`$iEE}UDaCs`_$p_o-k1l^@QiJhR@T72i8keI_! z0e-+%bVE=KI7)s0z~N^eIKObdoVxepR}YkzKJx7hd=z`ju$s4NYk0L63e*834$$|+ zscllUBvF4I;0n)=NVk)uP7E_ly8uz9)&C516i6<{1H>x;Wn@o{DW2=tV^5u^qG|2~ z@B+_YFVKQdC_}TrS+vsGcRZP59#1AyxRdBCmLS5Fj|0j`#e~Sg2b@qwT9}!y;{7@{ z5SZ`5g1+)Nr_8TB4k}~bM_mx6gxDz5R~_O`+1ySD3YeE|LPf9of1Cw}SPp2XIB^Kd zIVP`g7i>vk#x7DYE6-3)MBk8l#@2SX6})Wqz$eh8FXBF|I7tc!l6vuP3>clp3wG(A zwz+Y!3>Fof_l9XiHxD!$QrLK1ZJ||Z_pne*I2N5wD*!C$7K@oLo9|BSuqUy=q3$g z0de235PpvpK6*#Lec}L~Kri~`QxeDvYG?>F22^+f zK;>j<1qOzJOGp-=7DXWu4T~8>Vh$QsuzD4@&LXxSVK`zzg*Xiu0#v+llGlRISR+W{ z!H;4s1k}^Y2j)0;7<1q95r{h)!FJDF5{^+;B?TGmG_ny6OM=%*JVFBtHi{4k1dJ(K3BvM$ zl_2bnV0b!aluF`Y2h7fk^bizrgH0DLOqB-Ft0B;`L;1fO8}O!k5u1yNJj%hsozC5r zY4phGgvF!aNUt&*4NjaJ$-~Fts)yde>vVqETM~w1edLq1TCi8T_=rqo1;~;%*yZ|D z^*QUY+%RHCi83&!Q>Zi9EU0D`zgRAlicPW&a~&Q1jRA~7&gZ^if`_@64n2U~m;zK` z+oco~2;{Znb0nT-JPn-B3s5*(>BV!ewh)>QyUn!E`I;!iz>pPz1`tZ&I5#>zf>kYE zY>N0aJ8;ML*3^X%npjsi6)o#10u86aWrUw#NHWV(39vc4#h|jqAO--q6cHWA5&kdOm$(zmFK(3~BmI9>MZYrFe+BdLf>5gZ# zcQ{6xPP%|3X9%IRz*#~zH*${<#7Ec;(}i7IG{dLRIa@u6Az)P1%0M$>Fo`44`f;Qz zvjE-*H~7;q;gM4Tf5ZN_H*?HUStn z$v0M^X}+-vNC~J2gNwE$6KD!R6IWAJ0~+#H0*=cE0PKest`--P(Xs-ke)Hms&g@4k zywV8llc*{fA5q|H77cXD@^2I&Bt|NTE(^6^!a;Q$?^r!uJSG4#UH=z^@L*KFq=3v7 zDxU{j1Ev5wA=-w zsN+0kY${4?7t9YHTW=iGOxiF-e5<`-$E5+F@rVF$;*e)rCZaNz5m1iaBNq=BEx(GP zlkfW0%Hu8&9sVibfmM8huG8scB{{_948?C}nBhOJ+z{XpgB9ymS=$WE`saCF99)b{ zTYSH$tNtzf|MoaVzbnqlWsTiD_Qjxqc2OLC479u{s>QFZzF*sXzt;GEZSwuvt1^NR zGD_4#@anrHbmFFHJLrjucG44jwGD{z=3`L=1TPoWAmHYb4+NtTv2DVn!UmBM9pA~n z1j$#?IvXsO23~Ck^`oNe9-8QI_$YBEoqIzE*Wej$74bF1dvx;XdE<;op^o4sCMe)r z%ja=VDab?;M~F4o(^NhzfobslAlx45nFG`^d*UXT`UV=^NHm z`1XnK8+1adWuJ*)(Fue1am{i?t8o~Zsp8Qv3Mfyo1IZN~2LNW( zuEnxcL9mKn+z4MjjxmFC-keT4m@v5N3WkU`YS)r0{pjbzy}_^GpIK%ubC_xUT;qZO zVfh#C5W!eBs}a9ZMFYOJFJFIs5+^jWg<$mwH896clD+r}vS_n_tPhArnA){ufe}JF zMV>;y3So?$mEkH1F-{OTjzXlfB=bB$XF&$_1r^91Z24p54P||2H__061qk#CSMA2x zjXGcy5g<4p5#B3kk2t^Jk~+GB;Uw-Jg_LsU;6UZw-Tt_22p^^6%A zN0+&7_XW}iM8;AH9MeG$b1j5mIjFvvy-0m#n@L?-B`KK4F^1?1Tb1}!7vAn6UI4vN z47Xvk9O-4M_6v8PgKlIL-dka@K&l)AT39-uuSYA};TOdv1Y~rhhVT{t`waiAFGF+Q z#^@wHoDd6`Y4w7Taugv|S92ruew(Z#4uWioFr0|3H;Cb(P0p--a%fd5b1ynfc*Hf? zIp|WJh=Bm)iZH0i+Q$?i0W?4tX2&rL3CM8mb-4o7$ARi5L8vzAu-LS|N0?(tPXwK=)}54)l~c1hxk|>v>T4#V}oi+ zKK402(&EW~(3t>jIB}V3A3cnZ)gs{}K30eLeeYkU+84jX$GX)3bnF{^jH%}1E4%Qq zIt*>ykB`+s`~GfxtPVrZet?gp*;j?3jeGF1I_^7q03TaaQ}S`*zP8nTy!Z(|Hmm02 zW8dOqo1l*xgea*Av?kTo*oD!;v+fOKrQ2b!YYV4HW2-XjxUiePWPX9b!B!<*MP!h+ zmI8_Bf)G?jE8lR4BhN-Nrz@b1wP@oxSzKpqT*@SQ-ln{v+Il;Sf8Gg>qaQIl(TQ|` z&aHaBk#MDp2gVLMPE;n-EXhF?&K|yca0NaI*j8+F%5yl;3K0rCdUk6qK4_6ssOpL8-&b!Fv7vh9wnhY z927AY_X3DkF<~L2N;X7*g^I{X2kk6MCdjB`Y=;3fVCfvUgqww^Vp3tqsV8awL3AHy zqZK`VIgph-dcwp9@KF(El`Cb1ZB7`D$T0|uW*_i1@hnxxSNXM*(*c?Ss|n5Fw6_D; zv;xTGbUp`=f!kO8rcZHxIhVUjrgI! zfDue1AQ@*UVY>+t2|$2e0YrjVhKXbsM8fw#B7w#z&qOBI0g%a5!q5XCh=kE)AEjW) zeMQ!-L@IXa%L+gu06{TU7l8_**Cx_CfM&*=Dw2dY=)?xSh-fs5M7S8OUJ1!o>L)4J zNZ^x#yL$v-(JgI*L0E8xL0AYYVr_+qW)c<^__E@DsQ}``PRNAqTbZyMNGeuA*%P?x z2hyN=%DN!|5bLSEVt11jjMa0g6X8h`=10j@L-N^^VZV{ITCP$VqeS?;5#LHtYp=kP zs;guej)Zau7OF)lvZE0IFopx2Y`-PDuC|Kh6veObk5Cv$fp z0fY+wF51Ua;U|Vf4pHwfI_%YaX$%V+hNxDF}Ma2zXvgu7+mtT^ZwFfu^YI~SQ_32Fl0LY#>K$UuN+_(?aS>DT~n!ehQ?tL~txN+W_1wZN&2 zr&&5QSM}!{!`-MC)rN4bV^1&; zCAyEjEZK<(i0q(4HB@K99g%`Xiyz@W!gfp%@r%fOD@hU>2stziUzN!0&oKkCxQe5_v@A(VQPI0e&TvA zNE#X?j6#O*j%voNz-~3)1%Z^2eSH*-Mr&G@D`JZ-XO&z%prQ(8hISe#R5KsTjumed z(!|{~cQ+weh|BSx1ze7tB-+_Vc1iBx>gN;jt8C`vy&9e1HiKsGy*9W3FDK(al~Egj zS}sDJz=SY4!oDLRq7*3AGJszE^e3r3@ihB^u*e0sI<)yf9+16@%=i?Wwv_NK+69q?uqK z6v)aVM+O?4hzIPNjMoM|>1qfeGKA`iiWRNg&ade=>Oh!^IjOlyY0JHQi`e?BE2%f! z=C&=;ZFAg8>hj&vC`$_X1M>x>ut&+kcf6ec490RL<(&kyB?laIOv(#X>mgL}g@}}T z>8%6phKX8AqeyE2w>*QYzmfXT)PRhzu&WTSRo9QIaf%U0f^)hC1QiHaW3Yxy)q%8J zUCb0-L%_$D~NLP}Da`f5QZXLM;8c-MyD@cbrhW;m#(FjT$ChpCz@WJBGBi zZz0X86e(q7McQJ2hZr%-fjf`k}d~3pTFARDwlC@0p1B`zMXo-hy9BV zN!%fN!5v~D4M7QHF*u|sgusoDhm-ULT;<7AGldqD-T<*)p)Z&(4J0@iH3-(jUnleh zQn!V^@FXgi!E}(K75V~7bFY9+?v8PZLQpbP0Nz)g<}p67=3UL&$X^0nYmX^h8AQJM=__+v!Q?y=N6`;6oD?LB+0cl)Wb$HOPGBjxut;y-&c2mV}c5)o+oN6ry7F z{xg~gF5^GjWo%x(jQ?yOS@EkQp84KG;|SMOn9Uj2R8+pB0)435R>eW+qCO9aK$;UF zvLuqsR4qJ!6}}J5Lhs4qgfd(fNL~%T(v*I`Y208+zqcAUm(uTTawu~Uraw?aTvtl( z*YItnDP~XT=>yWQK?X=`CfcF-5pD9ZXY{+DxFD5&CW}_YPrehCe#X<8S)z)5zB8sh zL{WT2C*dA4yev3m+BlO!!bQMS&?1*4{gjDKFHwM($>cgArG*`a_@QlJKZ7h>ws0s$ z!LA?!B%Eq(NCLQh6q%GIc2Wud3~D45hTpgVs(Hxq6~f0Sc?s_&>M)#|SThRvlc~C^ zXgtK18}_`)=)u|Pq630nf!)#KQ35)Qa7GdK=*4+-k6ltkd3phz#n*9mdLswR2y{Xq zf{G?1CtjEtTtK6UiJ+D}v{o>V2AA=ZsHP&mf)RTfu0U=_5!I4J`PdFTpvJTKM&by^ z4)T+j1K$uwD!x&xivz!Lr~#F$;+w;VEq|`{LBgy@HJd1)7t}mZAy7ass9w0GMc4ga zxWL!E6CNK8=EK-^lb(!OSJ4Jj3EAw1$!qLvy{Zxv+`-=bW`b~NMEk^WBO1Tik62l7 zFxOUkP1XJX%E@2JJq>pqv?Uqt>6b9t{geP2(jedeC80qEm^11!0#Jm*XJQn z&ai?r4r=g2u>Uj?_}iy#)qtEZ19-%B+_ZM3&v5x-ISMztd1DwXZ4L2a!!O&Qep1wC zI9c+Hb4TzLA>lMTYFJX}sD)OzWacAZGwunaDGF5^hb2@|^8%afvja2y_Z5UnhJP5EU6<)8s}sWyR(HL^n9VxR`)yFoJL zDofOG1ag{+7XpI5wNx+Y@%eMRt1fEQzJobI%ml*Cdcxvo;#;hkCcfor)h51j+}r}Q zQuS_}vk^)yVM#p=OAz@{Q!4&a|KOb>J5oFxPLVJ=SUpQ|%mZwh?c{q@w*$Tc>;alf zu2p<=lCn?xUqA)yK6FHd@+L%VY#?(G3^YzpXbxiAU0BRUk(G)xG%^p7bu$FVc06FA zHK4hD0Uu1~GO2fwX;gh=syrtul59t0LP76nVPR~OCYz`@84a}xSgaKHMfJmv!+u}* zf(bkuZB{`&Frbn+!f|pWsjz(#(-#2a?jh0`4O&PCb%+(52fgdv!`yJ2__-O+uG zJCxeT){ST-E6SyPX@`ACAO{B^zy&kK9nlG>LUyuQY65|66lweDO*zZUsB%BtXp-13oX++IYi)h52qet{a5|LdJ zy6qs6!S-Zl#}2aQawr%bQ(>o8P&$%cfj(&00Co-PsikY^8^h)~f^=>D8e=nr#q%A~ zb@UoONN~@~j6}6eMmO*?BT+@PDK^I?D3d)BC~omcdgL?iy1J`Df|7+V)E52Dz$YmE zdN`igB&TX$5!KlcKq34V+H!SeR2o4;{gEV;haq00HaQV7MGx~eh`5Oi?0T!) zGYlsy@OTKJ;6B>dcM!pFs=+J=<*1s(9!F$s2E+4^3_dw*cZV830x z5wD0xoCc3z_px5n0CXGbffd(4UoAh+yXIk_cybp`~5}L2!Fl^^C zhbC%HsbI!v;zhd6UB#9xvNE$|MGOj?$SqM;VhFSg+bxL-aw{<^xO*hM>^g9V3CIoi zEhZX1Cj}XWOt5$`7R;7eENS=#d1E3(urmY&+53s`kOqu=T;I``5-gzI@XC>-qEP_R zNH6J78`ZM{@`FdBL28w`g|9)NOeCo=$xs+5m(EC($g|+A7&yEs%M=#m2QY~Wsf+za zbN&&i9eQgEUVu66hy&-oc&696@`zmje%Mo);pOcfnIeA*#WZ)K3HFB-xSn zi-$Eg38NjVPEN%RG}?kjd#yZlCjpEiS2!cVQYDW#{F4a+uch_jQ{POJ@ejNw_;n%rGy&}I3xCo1b+jgKvtu288Qhptnlh!hUbyN&UetG zau~??s+=z*uq7}HJx%8erg)zZhxip^)#Mf8tO^bhd{x1@u9*^Pgft)#3r|0Ur&mQJ zBSS0VtAMO2ttH8m(u;1*won8TCPI{psG;q2YZwZka>h+P?2<`zJS4VMQLnZ^Lf-|?Gb3IH0e1= zJ;ak$R;ZcA&OIJs+DWk=fWC*|>Jl0bG&}(^unHl}?op0=r_~0>6?G&!KvTz-JbGAq zpn7E1&T3);x=d9Mj`%$bJ|&pnIEN~AbO77i8JZ( z1ELNA1jvF38S;rNoC@HD0tx4IF?RL~|0qc)+8Ii5P!!ta3JO|a08AX$LJ z1R@<41VRn`O!E(Y0C~9I+(!*_Sz%-I6fPiHcX<0ZM zOq?|bpplE6J*FZXuQ{3)D1=Nn1&uA*sJ`+O^ z_8+uqW}yHRTx-ck*#x|ETksaGTT*XWi~_#V=paC9!K!>#_B+C}iq)+HiXnK&Lf(1i zB`XgFtEVlkKCGU1r5^bIxYVN%J6%#SHPen+PzC4dV|?= zr;>j>Fyk*~M!9lE)s>19NK(N)bKLe+X|N?Fm5#W8=ukJm!P)|845f1y@oD~~r$H|y z@=~-C9<-~5=64zG?7z8#5Pno)hPz;{B2{@y-ST0ll`5+IglA@;gm;q;iKvK9v zP=W(rApuBAxX6|qN-H#X7zMF|)qpA7&_isGAJ}Idg}(7&zJQ6w6YcY|0>Q|N=x1Ji zdsvOCCOcN_A}b69dLdz-W(%qv$i{JYzY3R&FZ~PiOqj>oS65RXz-cD*uil_Sq*j@! zVbh3Dp?V=0LkKU3HmKvNA-CwE>r*eu3uYVfA4b!RE)bYRF)20nz70qT2a%&30!I+G zc+IO+12<0KhS#o6n)fga3ERX9tnxdn>r6oBe!qM0tE5oSF~$Z_i93&@2E2WdSMEhLa7t$sfm<*RdtvNKeTo{0 zm4g*ZPe2ZQ?8VuN8n}q5SVLjVax|*dLyRQ#)SVuC-$GRN0VVq|BcY?YI2%aVorIXT zVwBM?1Qi3=(IAZ&fah<~Ma~F6#4G9|j8A@DG&^WWSV=N}l){b&Hv6R&u#euOYTTG7LM9jH_y~kPy5CTCk$>1tj8ZGt>}V1=)ge0Kl0QQkbbN zK$F{1q$5Vs;n|1{nW)o*LdwJf$^y(0;GPXI1_Jlp%F0hJ`fbv6YP@9DYYeQlh+G=3V<*j~{kyV)h4{ZZ8VpotCcBrzH^ z3YcWcTFO%r1}>P@+ye|#>d$u9g-Asc*3Kgol#kwveTy<3H36mz_=`Odoc<@L8IEOX zBbD=p8T@!p{Gbt(23qj3BXsrzI*hOH%Wl=S@yhHagYw1;iVIPd(q_NxJLL zc+#YD22To9J%=ZzUsJSz{pkQ}EFt}?hwN&I2BMW+*h3+s-{u{6Bz_wZyAU%%Xt3z_ zoLKx8_6KSEt!^CMVGqiV*!7_7C;hX-HXcxWy-L}CrtCN`m20mVlsz&iJ0l2si=-WZ z)`+WqQq@6VE#vqV*yns}Tl$FeSMUjjLbAbYKiTQhKoBun8NnKkSVq@H6P{XAM3i?x zSB%E+8BtB2v6h;sN^VctE<7 zg9}G`NAmTAlzoUFEV%U$5VVmZp;x&n7buCjvwK*0Ad(e=7N$#ujs;xIe1i#!D2;u! zWWx-_PhbOvXYK@>HF4GyCUlyQ)D(qqwu#h2@G`O5CYb_q+eoh&3<(|3pJNaRq@WNL zYa?wYwrtH?fN_Ed@t*@@9%t|bmAMAP8nw#)++^U?Xf-ViCh9iQjopb$+em|G*Aw@c z>Sem(WRd_l5D#TmaRw?m#fYgMTaPeu4gU?pTR0{y%w}X=Qs`7b*tq?SD&e$aPM!fu zgfXL(0c<8E&3OhMoU{S|rs~Me81)vfpKS>Wo#i9*&gwX&Fl%rQ<{AQakX{AJB zxb;E!WQ=lj2Cy-6EX9N>y0jRivI}(@vj9*kCJ|`vP!LFgfD%>g#i?QvAo-p=7K{E|`5J`k;>@lK3xcC+R z0RuZ23yeiHVPcV1fzaP8kY!Gv%f%0AaU{5IG7$c&2EFpe7TARgz5v8;s}l6Dp5knLtTE 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 z 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 diff --git a/core/benches/blocks/common.rs b/core/benches/blocks/common.rs index 6cfa0356bb6..7aef12edd2d 100644 --- a/core/benches/blocks/common.rs +++ b/core/benches/blocks/common.rs @@ -62,8 +62,8 @@ pub fn populate_wsv( for i in 0..domains { let domain_id = construct_domain_id(i); let domain = Domain::new(domain_id.clone()); - instructions.push(RegisterBox::domain(domain).into()); - let can_unregister_domain = GrantBox::permission_token( + instructions.push(Register::domain(domain).into()); + let can_unregister_domain = Grant::permission_token( PermissionToken::new( "CanUnregisterDomain".parse().unwrap(), &json!({ "domain_id": domain_id.clone() }), @@ -74,8 +74,8 @@ pub fn populate_wsv( for j in 0..accounts_per_domain { let account_id = construct_account_id(j, domain_id.clone()); let account = Account::new(account_id.clone(), []); - instructions.push(RegisterBox::account(account).into()); - let can_unregister_account = GrantBox::permission_token( + instructions.push(Register::account(account).into()); + let can_unregister_account = Grant::permission_token( PermissionToken::new( "CanUnregisterAccount".parse().unwrap(), &json!({ "account_id": account_id.clone() }), @@ -90,8 +90,8 @@ pub fn populate_wsv( asset_definition_id.clone(), iroha_data_model::asset::AssetValueType::Quantity, ); - instructions.push(RegisterBox::asset_definition(asset_definition).into()); - let can_unregister_asset_definition = GrantBox::permission_token( + instructions.push(Register::asset_definition(asset_definition).into()); + let can_unregister_asset_definition = Grant::permission_token( PermissionToken::new( "CanUnregisterAssetDefinition".parse().unwrap(), &json!({ "asset_definition_id": asset_definition_id }), @@ -114,18 +114,18 @@ pub fn delete_every_nth( for i in 0..domains { let domain_id = construct_domain_id(i); if i % nth == 0 { - instructions.push(UnregisterBox::domain(domain_id.clone()).into()); + instructions.push(Unregister::domain(domain_id.clone()).into()); } else { for j in 0..accounts_per_domain { if j % nth == 0 { let account_id = construct_account_id(j, domain_id.clone()); - instructions.push(UnregisterBox::account(account_id.clone()).into()); + instructions.push(Unregister::account(account_id.clone()).into()); } } for k in 0..assets_per_domain { if k % nth == 0 { let asset_definition_id = construct_asset_definition_id(k, domain_id.clone()); - instructions.push(UnregisterBox::asset_definition(asset_definition_id).into()); + instructions.push(Unregister::asset_definition(asset_definition_id).into()); } } } @@ -144,13 +144,13 @@ pub fn restore_every_nth( let domain_id = construct_domain_id(i); if i % nth == 0 { let domain = Domain::new(domain_id.clone()); - instructions.push(RegisterBox::domain(domain).into()); + instructions.push(Register::domain(domain).into()); } for j in 0..accounts_per_domain { if j % nth == 0 || i % nth == 0 { let account_id = construct_account_id(j, domain_id.clone()); let account = Account::new(account_id.clone(), []); - instructions.push(RegisterBox::account(account).into()); + instructions.push(Register::account(account).into()); } } for k in 0..assets_per_domain { @@ -160,7 +160,7 @@ pub fn restore_every_nth( asset_definition_id, iroha_data_model::asset::AssetValueType::Quantity, ); - instructions.push(RegisterBox::asset_definition(asset_definition).into()); + instructions.push(Register::asset_definition(asset_definition).into()); } } } diff --git a/core/benches/kura.rs b/core/benches/kura.rs index efe6b59298e..a47f731e31d 100644 --- a/core/benches/kura.rs +++ b/core/benches/kura.rs @@ -23,7 +23,7 @@ async fn measure_block_size_for_n_executors(n_executors: u32) { let bob_id = AccountId::from_str("bob@test").expect("tested"); let xor_id = AssetDefinitionId::from_str("xor#test").expect("tested"); let alice_xor_id = AssetId::new(xor_id, alice_id); - let transfer = TransferBox::asset_quantity(alice_xor_id, 10_u32, bob_id); + let transfer = Transfer::asset_quantity(alice_xor_id, 10_u32, bob_id); let keypair = KeyPair::generate().expect("Failed to generate KeyPair."); let tx = TransactionBuilder::new(AccountId::from_str("alice@wonderland").expect("checked")) .with_instructions([transfer]) diff --git a/core/benches/validation.rs b/core/benches/validation.rs index fce6f491c22..3a5bcaefe23 100644 --- a/core/benches/validation.rs +++ b/core/benches/validation.rs @@ -12,7 +12,7 @@ use iroha_core::{ tx::TransactionExecutor, wsv::World, }; -use iroha_data_model::{prelude::*, transaction::TransactionLimits}; +use iroha_data_model::{isi::InstructionBox, prelude::*, transaction::TransactionLimits}; use iroha_primitives::unique_vec::UniqueVec; const START_DOMAIN: &str = "start"; @@ -26,24 +26,25 @@ const TRANSACTION_LIMITS: TransactionLimits = TransactionLimits { fn build_test_transaction(keys: KeyPair) -> SignedTransaction { let domain_name = "domain"; let domain_id = DomainId::from_str(domain_name).expect("does not panic"); - let create_domain = RegisterBox::domain(Domain::new(domain_id)); + let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); let account_name = "account"; let (public_key, _) = KeyPair::generate() .expect("Failed to generate KeyPair.") .into(); - let create_account = RegisterBox::account(Account::new( + let create_account = Register::account(Account::new( AccountId::new( account_name.parse().expect("Valid"), domain_name.parse().expect("Valid"), ), [public_key], - )); + )) + .into(); let asset_definition_id = AssetDefinitionId::new( "xor".parse().expect("Valid"), domain_name.parse().expect("Valid"), ); let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id)); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id)).into(); let instructions = [create_domain, create_account, create_asset]; TransactionBuilder::new(AccountId::new( diff --git a/core/src/block.rs b/core/src/block.rs index 7686c92b297..21a7405ef42 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -735,7 +735,7 @@ mod tests { // Creating an instruction let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset_definition = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id)); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id)); // Making two transactions that have the same instruction let transaction_limits = &wsv.transaction_executor().transaction_limits; @@ -778,7 +778,7 @@ mod tests { // Creating an instruction let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset_definition = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone())); // Making two transactions that have the same instruction let transaction_limits = &wsv.transaction_executor().transaction_limits; @@ -791,12 +791,12 @@ mod tests { let quantity: u32 = 200; let fail_quantity: u32 = 20; - let fail_mint = MintBox::asset_quantity( + let fail_mint = Mint::asset_quantity( fail_quantity, AssetId::new(asset_definition_id.clone(), alice_id.clone()), ); - let succeed_mint = MintBox::asset_quantity( + let succeed_mint = Mint::asset_quantity( quantity, AssetId::new(asset_definition_id, alice_id.clone()), ); @@ -848,10 +848,10 @@ mod tests { let transaction_limits = &wsv.transaction_executor().transaction_limits; let domain_id = DomainId::from_str("domain").expect("Valid"); - let create_domain = RegisterBox::domain(Domain::new(domain_id)); + let create_domain = Register::domain(Domain::new(domain_id)); let asset_definition_id = AssetDefinitionId::from_str("coin#domain").expect("Valid"); let create_asset = - RegisterBox::asset_definition(AssetDefinition::quantity(asset_definition_id)); + Register::asset_definition(AssetDefinition::quantity(asset_definition_id)); let instructions_fail: [InstructionBox; 2] = [ create_domain.clone().into(), Fail::new("Always fail".to_owned()).into(), diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 4497e77a4a9..a0bf424f1ef 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -245,11 +245,11 @@ mod tests { let account_id = AccountId::from_str("alice@wonderland")?; let (public_key, _) = KeyPair::generate()?.into(); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; - RegisterBox::domain(Domain::new(DomainId::from_str("wonderland")?)) + Register::domain(Domain::new(DomainId::from_str("wonderland")?)) .execute(&genesis_account_id, &mut wsv)?; - RegisterBox::account(Account::new(account_id, [public_key])) + Register::account(Account::new(account_id, [public_key])) .execute(&genesis_account_id, &mut wsv)?; - RegisterBox::asset_definition(AssetDefinition::store(asset_definition_id)) + Register::asset_definition(AssetDefinition::store(asset_definition_id)) .execute(&genesis_account_id, &mut wsv)?; Ok(wsv) } @@ -261,7 +261,7 @@ mod tests { let account_id = AccountId::from_str("alice@wonderland")?; let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - SetKeyValueBox::asset( + SetKeyValue::asset( asset_id.clone(), Name::from_str("Bytes")?, vec![1_u32, 2_u32, 3_u32], @@ -288,7 +288,7 @@ mod tests { let kura = Kura::blank_kura_for_testing(); let mut wsv = wsv_with_test_domains(&kura)?; let account_id = AccountId::from_str("alice@wonderland")?; - SetKeyValueBox::account( + SetKeyValue::account( account_id.clone(), Name::from_str("Bytes")?, vec![1_u32, 2_u32, 3_u32], @@ -317,7 +317,7 @@ mod tests { let mut wsv = wsv_with_test_domains(&kura)?; let definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let account_id = AccountId::from_str("alice@wonderland")?; - SetKeyValueBox::asset_definition( + SetKeyValue::asset_definition( definition_id.clone(), Name::from_str("Bytes")?, vec![1_u32, 2_u32, 3_u32], @@ -345,7 +345,7 @@ mod tests { let mut wsv = wsv_with_test_domains(&kura)?; let domain_id = DomainId::from_str("wonderland")?; let account_id = AccountId::from_str("alice@wonderland")?; - SetKeyValueBox::domain( + SetKeyValue::domain( domain_id.clone(), Name::from_str("Bytes")?, vec![1_u32, 2_u32, 3_u32], @@ -397,11 +397,11 @@ mod tests { .expect("Failed to generate KeyPair") .into(); let register_account = - RegisterBox::account(Account::new(fake_account_id.clone(), [public_key])); + Register::account(Account::new(fake_account_id.clone(), [public_key])); register_account.execute(&account_id, &mut wsv)?; // register the trigger - let register_trigger = RegisterBox::trigger(Trigger::new( + let register_trigger = Register::trigger(Trigger::new( trigger_id.clone(), Action::new( Vec::::new(), diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index 84381a80755..d14ed740d0b 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -432,7 +432,7 @@ mod tests { kura.store_block(vcb); let unapplied_tx = TransactionBuilder::new(ALICE_ID.clone()) - .with_instructions([UnregisterBox::account("account@domain".parse().unwrap())]) + .with_instructions([Unregister::account("account@domain".parse().unwrap())]) .sign(ALICE_KEYS.clone())?; let wrong_hash = unapplied_tx.hash(); let not_found = FindTransactionByHash::new(wrong_hash).execute(&wsv); diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index 251be5be71d..2dc0cbbb3b9 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -1716,7 +1716,7 @@ mod tests { let isi_hex = { let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = RegisterBox::account(Account::new(new_authority, [])); + let register_isi = Register::account(Account::new(new_authority, [])); encode_hex(InstructionBox::from(register_isi)) }; @@ -1802,7 +1802,7 @@ mod tests { let isi_hex = { let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = RegisterBox::account(Account::new(new_authority, [])); + let register_isi = Register::account(Account::new(new_authority, [])); encode_hex(InstructionBox::from(register_isi)) }; @@ -1851,7 +1851,7 @@ mod tests { let isi_hex = { let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = RegisterBox::account(Account::new(new_authority, [])); + let register_isi = Register::account(Account::new(new_authority, [])); encode_hex(InstructionBox::from(register_isi)) }; diff --git a/core/src/sumeragi/main_loop.rs b/core/src/sumeragi/main_loop.rs index 65dc838c123..b24b263188a 100644 --- a/core/src/sumeragi/main_loop.rs +++ b/core/src/sumeragi/main_loop.rs @@ -1231,10 +1231,10 @@ mod tests { kura.store_block(genesis); // Making two transactions that have the same instruction - let create_asset_definition1 = RegisterBox::asset_definition(AssetDefinition::quantity( + let create_asset_definition1 = Register::asset_definition(AssetDefinition::quantity( "xor1#wonderland".parse().expect("Valid"), )); - let create_asset_definition2 = RegisterBox::asset_definition(AssetDefinition::quantity( + let create_asset_definition2 = Register::asset_definition(AssetDefinition::quantity( "xor2#wonderland".parse().expect("Valid"), )); diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 796d88a4e31..b51e1726e47 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -117,9 +117,8 @@ impl TestGenesis for GenesisNetwork { unregister_wonderland_domain, upgrade_executor_permission, ] { - first_transaction.append_instruction( - GrantBox::permission_token(permission, alice_id.clone()).into(), - ); + first_transaction + .append_instruction(Grant::permission_token(permission, alice_id.clone()).into()); } if submit_genesis { @@ -212,7 +211,7 @@ impl Network { time::sleep(Configuration::pipeline_time() + Configuration::block_sync_gossip_time()).await; - let add_peer = RegisterBox::peer(DataModelPeer::new(peer.id.clone())); + let add_peer = Register::peer(DataModelPeer::new(peer.id.clone())); genesis_client .submit(add_peer) .expect("Failed to add new peer."); diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index 27323e65f51..0dd3b178d9e 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -6,9 +6,8 @@ use alloc::{boxed::Box, format, string::String, vec::Vec}; use core::fmt::{Debug, Display}; -use derive_more::{DebugCustom, Display}; +use derive_more::{Constructor, DebugCustom, Display}; use iroha_data_model_derive::model; -use iroha_macro::FromVariant; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -18,7 +17,11 @@ pub use self::{model::*, transparent::*}; use super::{prelude::*, Value}; use crate::{seal, Level, Registered}; -/// Marker trait designating instruction +/// Marker trait designating instruction. +/// +/// Instructions allows to change the state of `Iroha`. +/// All possible instructions are implementors of this trait, excluding +/// [`InstructionBox`] which is just a wrapper. pub trait Instruction: Into + seal::Sealed {} #[model] @@ -28,6 +31,10 @@ pub mod model { use super::*; /// Sized structure for all possible Instructions. + /// + /// Note that [`InstructionBox`] is not a self-sufficient instruction, + /// but just a wrapper to pass instructions back and forth. + /// If you are a client SDK user then you likely don't need to use this type directly. #[derive( DebugCustom, Display, @@ -36,7 +43,6 @@ pub mod model { Eq, PartialOrd, Ord, - FromVariant, EnumDiscriminants, Decode, Encode, @@ -101,15 +107,57 @@ pub mod model { impl Instruction for InstructionBox {} - impl Instruction for SetKeyValueBox {} - impl Instruction for RemoveKeyValueBox {} - impl Instruction for RegisterBox {} - impl Instruction for UnregisterBox {} - impl Instruction for MintBox {} - impl Instruction for BurnBox {} - impl Instruction for TransferBox {} - impl Instruction for GrantBox {} - impl Instruction for RevokeBox {} + impl Instruction for SetKeyValue {} + impl Instruction for SetKeyValue {} + impl Instruction for SetKeyValue {} + impl Instruction for SetKeyValue {} + + impl Instruction for RemoveKeyValue {} + impl Instruction for RemoveKeyValue {} + impl Instruction for RemoveKeyValue {} + impl Instruction for RemoveKeyValue {} + + impl Instruction for Register {} + impl Instruction for Register {} + impl Instruction for Register {} + impl Instruction for Register {} + impl Instruction for Register {} + impl Instruction for Register {} + impl Instruction for Register> {} + + impl Instruction for Unregister {} + impl Instruction for Unregister {} + impl Instruction for Unregister {} + impl Instruction for Unregister {} + impl Instruction for Unregister {} + impl Instruction for Unregister {} + impl Instruction for Unregister> {} + + impl Instruction for Mint {} + impl Instruction for Mint {} + impl Instruction for Mint {} + impl Instruction for Mint {} + impl Instruction for Mint {} + impl Instruction for Mint> {} + + impl Instruction for Burn {} + impl Instruction for Burn {} + impl Instruction for Burn {} + impl Instruction for Burn {} + impl Instruction for Burn> {} + + impl Instruction for Transfer {} + impl Instruction for Transfer {} + impl Instruction for Transfer {} + impl Instruction for Transfer {} + impl Instruction for Transfer {} + + impl Instruction for Grant {} + impl Instruction for Grant {} + + impl Instruction for Revoke {} + impl Instruction for Revoke {} + impl Instruction for SetParameter {} impl Instruction for NewParameter {} impl Instruction for Upgrade {} @@ -120,12 +168,13 @@ pub mod model { mod transparent { use super::*; + use crate::{account::NewAccount, domain::NewDomain}; macro_rules! isi { ($($meta:meta)* $item:item) => { iroha_data_model_derive::model_single! { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] - #[derive(derive_more::Constructor, getset::Getters)] + #[derive(getset::Getters)] #[derive(parity_scale_codec::Decode, parity_scale_codec::Encode)] #[derive(serde::Deserialize, serde::Serialize)] #[derive(iroha_schema::IntoSchema)] @@ -158,9 +207,32 @@ mod transparent { } } + macro_rules! impl_into_box { + ( + $($isi:ident $(< $($generic:ident $(< $nested_generic:ident >)?),+ >)?)|* + ==> $boxed:ident :: $variant:ident + ) => {$( + impl From<$isi $(< $($generic $(< $nested_generic >)?),+ >)? > for $boxed { + fn from(instruction: $isi $(< $($generic $(< $nested_generic >)?),+ >)?) -> Self { + Self::$variant(instruction) + } + } + )*}; + ( + $($isi:ident $(< $($generic:ident $(< $nested_generic:ident >)?),+ >)?)|* + => $middle:ident ==> $boxed:ident :: $variant:ident + ) => {$( + impl From<$isi $(< $($generic $(< $nested_generic >)?),+ >)? > for $boxed { + fn from(instruction: $isi $(< $($generic $(< $nested_generic >)?),+ >)?) -> Self { + Self::$variant($middle::from(instruction)) + } + } + )*}; + } + isi! { /// Generic instruction for setting a chain-wide config parameter. - #[derive(Display)] + #[derive(Constructor, Display)] #[display(fmt = "SET `{parameter}`")] #[serde(transparent)] #[repr(transparent)] @@ -171,10 +243,12 @@ mod transparent { } } + impl_into_box!(SetParameter ==> InstructionBox::SetParameter); + isi! { /// Sized structure for all possible on-chain configuration parameters when they are first created. /// Generic instruction for setting a chain-wide config parameter. - #[derive(Display)] + #[derive(Constructor, Display)] #[display(fmt = "SET `{parameter}`")] #[serde(transparent)] #[repr(transparent)] @@ -185,6 +259,8 @@ mod transparent { } } + impl_into_box!(NewParameter ==> InstructionBox::NewParameter); + isi! { /// Generic instruction to set key value at the object. #[schema(bounds = "O: Identifiable, O::Id: IntoSchema")] @@ -199,6 +275,54 @@ mod transparent { } } + impl SetKeyValue { + /// Constructs a new [`SetKeyValue`] for a [`Domain`] with the given `key` and `value`. + pub fn domain(domain_id: DomainId, key: Name, value: impl Into) -> Self { + Self { + object_id: domain_id, + key, + value: value.into(), + } + } + } + + impl SetKeyValue { + /// Constructs a new [`SetKeyValue`] for an [`Account`] with the given `key` and `value`. + pub fn account(account_id: AccountId, key: Name, value: impl Into) -> Self { + Self { + object_id: account_id, + key, + value: value.into(), + } + } + } + + impl SetKeyValue { + /// Constructs a new [`SetKeyValue`] for an [`AssetDefinition`] with the given `key` and `value`. + pub fn asset_definition( + asset_definition_id: AssetDefinitionId, + key: Name, + value: impl Into, + ) -> Self { + Self { + object_id: asset_definition_id, + key, + value: value.into(), + } + } + } + + impl SetKeyValue { + /// Constructs a new [`SetKeyValue`] for an [`Asset`] with the given `key` and `value`. + pub fn asset(asset_id: AssetId, key: Name, value: impl Into) -> Self { + Self { + object_id: asset_id, + key, + value: value.into(), + } + } + } + impl_display! { SetKeyValue where @@ -209,6 +333,13 @@ mod transparent { key, value, object_id, } + impl_into_box! { + SetKeyValue | + SetKeyValue | + SetKeyValue | + SetKeyValue => SetKeyValueBox ==> InstructionBox::SetKeyValue + } + isi! { /// Generic instruction to remove key value at the object. #[schema(bounds = "O: Identifiable, O::Id: IntoSchema")] @@ -221,6 +352,46 @@ mod transparent { } } + impl RemoveKeyValue { + /// Constructs a new [`RemoveKeyValue`] for a [`Domain`] with the given `key`. + pub fn domain(domain_id: DomainId, key: Name) -> Self { + Self { + object_id: domain_id, + key, + } + } + } + + impl RemoveKeyValue { + /// Constructs a new [`RemoveKeyValue`] for an [`Account`] with the given `key`. + pub fn account(account_id: AccountId, key: Name) -> Self { + Self { + object_id: account_id, + key, + } + } + } + + impl RemoveKeyValue { + /// Constructs a new [`RemoveKeyValue`] for an [`AssetDefinition`] with the given `key`. + pub fn asset_definition(asset_definition_id: AssetDefinitionId, key: Name) -> Self { + Self { + object_id: asset_definition_id, + key, + } + } + } + + impl RemoveKeyValue { + /// Constructs a new [`RemoveKeyValue`] for an [`Asset`] with the given `key`. + pub fn asset(asset_id: AssetId, key: Name) -> Self { + Self { + object_id: asset_id, + key, + } + } + } + impl_display! { RemoveKeyValue where @@ -231,6 +402,13 @@ mod transparent { key, object_id, } + impl_into_box! { + RemoveKeyValue | + RemoveKeyValue | + RemoveKeyValue | + RemoveKeyValue => RemoveKeyValueBox ==> InstructionBox::RemoveKeyValue + } + isi! { /// Generic instruction for a registration of an object to the identifiable destination. #[schema(bounds = "O: Registered, O::With: IntoSchema")] @@ -241,6 +419,61 @@ mod transparent { } } + impl Register { + /// Constructs a new [`Register`] for a [`Peer`]. + pub fn peer(new_peer: Peer) -> Self { + Self { object: new_peer } + } + } + + impl Register { + /// Constructs a new [`Register`] for a [`Domain`]. + pub fn domain(new_domain: NewDomain) -> Self { + Self { object: new_domain } + } + } + + impl Register { + /// Constructs a new [`Register`] for an [`Account`]. + pub fn account(new_account: NewAccount) -> Self { + Self { + object: new_account, + } + } + } + + impl Register { + /// Constructs a new [`Register`] for an [`AssetDefinition`]. + pub fn asset_definition(new_asset_definition: NewAssetDefinition) -> Self { + Self { + object: new_asset_definition, + } + } + } + + impl Register { + /// Constructs a new [`Register`] for an [`Asset`]. + pub fn asset(new_asset: Asset) -> Self { + Self { object: new_asset } + } + } + + impl Register { + /// Constructs a new [`Register`] for a [`Role`]. + pub fn role(new_role: NewRole) -> Self { + Self { object: new_role } + } + } + + impl Register> { + /// Constructs a new [`Register`] for a [`Trigger`]. + pub fn trigger(new_trigger: Trigger) -> Self { + Self { + object: new_trigger, + } + } + } + impl_display! { Register where @@ -251,6 +484,16 @@ mod transparent { object, } + impl_into_box! { + Register | + Register | + Register | + Register | + Register | + Register | + Register > => RegisterBox ==> InstructionBox::Register + } + isi! { /// Generic instruction for an unregistration of an object from the identifiable destination. #[schema(bounds = "O: Identifiable, O::Id: IntoSchema")] @@ -270,6 +513,75 @@ mod transparent { object_id, } + impl_into_box! { + Unregister | + Unregister | + Unregister | + Unregister | + Unregister | + Unregister | + Unregister > => UnregisterBox ==> InstructionBox::Unregister + } + + impl Unregister { + /// Constructs a new [`Unregister`] for a [`Peer`]. + pub fn peer(peer_id: PeerId) -> Self { + Self { object_id: peer_id } + } + } + + impl Unregister { + /// Constructs a new [`Unregister`] for a [`Domain`]. + pub fn domain(domain_id: DomainId) -> Self { + Self { + object_id: domain_id, + } + } + } + + impl Unregister { + /// Constructs a new [`Unregister`] for an [`Account`]. + pub fn account(account_id: AccountId) -> Self { + Self { + object_id: account_id, + } + } + } + + impl Unregister { + /// Constructs a new [`Unregister`] for an [`AssetDefinition`]. + pub fn asset_definition(asset_definition_id: AssetDefinitionId) -> Self { + Self { + object_id: asset_definition_id, + } + } + } + + impl Unregister { + /// Constructs a new [`Unregister`] for an [`Asset`]. + pub fn asset(asset_id: AssetId) -> Self { + Self { + object_id: asset_id, + } + } + } + + impl Unregister { + /// Constructs a new [`Unregister`] for a [`Role`]. + pub fn role(role_id: RoleId) -> Self { + Self { object_id: role_id } + } + } + + impl Unregister> { + /// Constructs a new [`Unregister`] for a [`Trigger`]. + pub fn trigger(trigger_id: TriggerId) -> Self { + Self { + object_id: trigger_id, + } + } + } + isi! { /// Generic instruction for a mint of an object to the identifiable destination. #[schema(bounds = "O: Into + IntoSchema, D: Identifiable, D::Id: IntoSchema")] @@ -281,6 +593,69 @@ mod transparent { } } + impl Mint { + /// Constructs a new [`Mint`] for a [`PublicKey`] for [`Account`]. + pub fn account_public_key(public_key: PublicKey, account_id: AccountId) -> Self { + Self { + object: public_key, + destination_id: account_id, + } + } + } + + impl Mint { + /// Constructs a new [`Mint`] for a [`SignatureCheckCondition`] for [`Account`]. + pub fn account_signature_check_condition( + signature_check_condition: SignatureCheckCondition, + account_id: AccountId, + ) -> Self { + Self { + object: signature_check_condition, + destination_id: account_id, + } + } + } + + impl Mint { + /// Constructs a new [`Mint`] for an [`Asset`] of [`Quantity`] type. + pub fn asset_quantity(quantity: u32, asset_id: AssetId) -> Self { + Self { + object: quantity, + destination_id: asset_id, + } + } + } + + impl Mint { + /// Constructs a new [`Mint`] for an [`Asset`] of [`BigQuantity`] type. + pub fn asset_big_quantity(big_quantity: u128, asset_id: AssetId) -> Self { + Self { + object: big_quantity, + destination_id: asset_id, + } + } + } + + impl Mint { + /// Constructs a new [`Mint`] for an [`Asset`] of [`Fixed`] type. + pub fn asset_fixed(fixed: Fixed, asset_id: AssetId) -> Self { + Self { + object: fixed, + destination_id: asset_id, + } + } + } + + impl Mint> { + /// Constructs a new [`Mint`] for repetition count of [`Trigger`]. + pub fn trigger_repetitions(repetitions: u32, trigger_id: TriggerId) -> Self { + Self { + object: repetitions, + destination_id: trigger_id, + } + } + } + impl_display! { Mint where @@ -293,6 +668,26 @@ mod transparent { destination_id, } + impl_into_box! { + Mint | + Mint => AccountMintBox ==> MintBox::Account + } + + impl_into_box! { + Mint | + Mint | + Mint => AssetMintBox ==> MintBox::Asset + } + + impl_into_box! { + Mint | + Mint | + Mint | + Mint | + Mint | + Mint > => MintBox ==> InstructionBox::Mint + } + isi! { /// Generic instruction for a burn of an object to the identifiable destination. #[schema(bounds = "O: Into + IntoSchema, D: Identifiable, D::Id: IntoSchema")] @@ -304,6 +699,56 @@ mod transparent { } } + impl Burn { + /// Constructs a new [`Burn`] for a [`PublicKey`] for [`Account`]. + pub fn account_public_key(public_key: PublicKey, account_id: AccountId) -> Self { + Self { + object: public_key, + destination_id: account_id, + } + } + } + + impl Burn { + /// Constructs a new [`Burn`] for an [`Asset`] of [`Quantity`] type. + pub fn asset_quantity(quantity: u32, asset_id: AssetId) -> Self { + Self { + object: quantity, + destination_id: asset_id, + } + } + } + + impl Burn { + /// Constructs a new [`Burn`] for an [`Asset`] of [`BigQuantity`] type. + pub fn asset_big_quantity(big_quantity: u128, asset_id: AssetId) -> Self { + Self { + object: big_quantity, + destination_id: asset_id, + } + } + } + + impl Burn { + /// Constructs a new [`Burn`] for an [`Asset`] of [`Fixed`] type. + pub fn asset_fixed(fixed: Fixed, asset_id: AssetId) -> Self { + Self { + object: fixed, + destination_id: asset_id, + } + } + } + + impl Burn> { + /// Constructs a new [`Burn`] for repetition count of [`Trigger`]. + pub fn trigger_repetitions(repetitions: u32, trigger_id: TriggerId) -> Self { + Self { + object: repetitions, + destination_id: trigger_id, + } + } + } + impl_display! { Burn where @@ -316,6 +761,20 @@ mod transparent { destination_id, } + impl_into_box! { + Burn | + Burn | + Burn => AssetBurnBox ==> BurnBox::Asset + } + + impl_into_box! { + Burn | + Burn | + Burn | + Burn | + Burn > => BurnBox ==> InstructionBox::Burn + } + isi! { /// Generic instruction for a transfer of an object from the identifiable source to the identifiable destination. #[schema(bounds = "S: Identifiable, S::Id: IntoSchema, \ @@ -331,6 +790,65 @@ mod transparent { } } + impl Transfer { + /// Constructs a new [`Transfer`] for a [`Domain`]. + pub fn domain(from: AccountId, domain_id: DomainId, to: AccountId) -> Self { + Self { + source_id: from, + object: domain_id, + destination_id: to, + } + } + } + + impl Transfer { + /// Constructs a new [`Transfer`] for an [`AssetDefinition`]. + pub fn asset_definition( + from: AccountId, + asset_definition_id: AssetDefinitionId, + to: AccountId, + ) -> Self { + Self { + source_id: from, + object: asset_definition_id, + destination_id: to, + } + } + } + + impl Transfer { + /// Constructs a new [`Transfer`] for an [`Asset`] of [`Quantity`] type. + pub fn asset_quantity(asset_id: AssetId, quantity: u32, to: AccountId) -> Self { + Self { + source_id: asset_id, + object: quantity, + destination_id: to, + } + } + } + + impl Transfer { + /// Constructs a new [`Transfer`] for an [`Asset`] of [`BigQuantity`] type. + pub fn asset_big_quantity(asset_id: AssetId, big_quantity: u128, to: AccountId) -> Self { + Self { + source_id: asset_id, + object: big_quantity, + destination_id: to, + } + } + } + + impl Transfer { + /// Constructs a new [`Transfer`] for an [`Asset`] of [`Fixed`] type. + pub fn asset_fixed(asset_id: AssetId, fixed: Fixed, to: AccountId) -> Self { + Self { + source_id: asset_id, + object: fixed, + destination_id: to, + } + } + } + impl_display! { Transfer where @@ -346,9 +864,23 @@ mod transparent { destination_id, } + impl_into_box! { + Transfer | + Transfer | + Transfer => AssetTransferBox ==> TransferBox::Asset + } + + impl_into_box! { + Transfer | + Transfer | + Transfer | + Transfer | + Transfer => TransferBox ==> InstructionBox::Transfer + } + isi! { /// Utilitary instruction to fail execution and submit an error `message`. - #[derive(Display)] + #[derive(Constructor, Display)] #[display(fmt = "FAIL `{message}`")] #[serde(transparent)] #[repr(transparent)] @@ -358,6 +890,8 @@ mod transparent { } } + impl_into_box!(Fail ==> InstructionBox::Fail); + isi! { /// Generic instruction for granting permission to an entity. pub struct Grant> { @@ -368,6 +902,26 @@ mod transparent { } } + impl Grant { + /// Constructs a new [`Grant`] for a [`PermissionToken`]. + pub fn permission_token(permission_token: PermissionToken, to: AccountId) -> Self { + Self { + object: permission_token, + destination_id: to, + } + } + } + + impl Grant { + /// Constructs a new [`Grant`] for a [`Role`]. + pub fn role(role_id: RoleId, to: AccountId) -> Self { + Self { + object: role_id, + destination_id: to, + } + } + } + impl_display! { Grant where @@ -378,6 +932,11 @@ mod transparent { destination_id, } + impl_into_box! { + Grant | + Grant => GrantBox ==> InstructionBox::Grant + } + isi! { /// Generic instruction for revoking permission from an entity. pub struct Revoke> { @@ -388,6 +947,26 @@ mod transparent { } } + impl Revoke { + /// Constructs a new [`Revoke`] for a [`PermissionToken`]. + pub fn permission_token(permission_token: PermissionToken, from: AccountId) -> Self { + Self { + object: permission_token, + destination_id: from, + } + } + } + + impl Revoke { + /// Constructs a new [`Revoke`] for a [`Role`]. + pub fn role(role_id: RoleId, from: AccountId) -> Self { + Self { + object: role_id, + destination_id: from, + } + } + } + impl_display! { Revoke where @@ -398,9 +977,14 @@ mod transparent { destination_id, } + impl_into_box! { + Revoke | + Revoke => RevokeBox ==> InstructionBox::Revoke + } + isi! { /// Instruction to execute specified trigger - #[derive(Display)] + #[derive(Constructor, Display)] #[display(fmt = "EXECUTE `{trigger_id}`")] #[serde(transparent)] #[repr(transparent)] @@ -410,9 +994,11 @@ mod transparent { } } + impl_into_box!(ExecuteTrigger ==> InstructionBox::ExecuteTrigger); + isi! { /// Generic instruction for upgrading runtime objects. - #[derive(Display)] + #[derive(Constructor, Display)] #[display(fmt = "UPGRADE")] #[serde(transparent)] #[repr(transparent)] @@ -422,9 +1008,11 @@ mod transparent { } } + impl_into_box!(Upgrade ==> InstructionBox::Upgrade); + isi! { /// Instruction to print logs - #[derive(Display)] + #[derive(Constructor, Display)] #[display(fmt = "LOG({level}): {msg}")] pub struct Log { /// Message log level @@ -435,6 +1023,8 @@ mod transparent { pub msg: String, } } + + impl_into_box!(Log ==> InstructionBox::Log); } macro_rules! isi_box { @@ -473,32 +1063,6 @@ isi_box! { } } -impl SetKeyValueBox { - /// Constructs a new [`SetKeyValueBox`] for a [`Domain`] with the given `key` and `value`. - pub fn domain(domain_id: DomainId, key: Name, value: impl Into) -> Self { - Self::Domain(SetKeyValue::new(domain_id, key, value.into())) - } - - /// Constructs a new [`SetKeyValueBox`] for an [`Account`] with the given `key` and `value`. - pub fn account(account_id: AccountId, key: Name, value: impl Into) -> Self { - Self::Account(SetKeyValue::new(account_id, key, value.into())) - } - - /// Constructs a new [`SetKeyValueBox`] for an [`AssetDefinition`] with the given `key` and `value`. - pub fn asset_definition( - asset_definition_id: AssetDefinitionId, - key: Name, - value: impl Into, - ) -> Self { - Self::AssetDefinition(SetKeyValue::new(asset_definition_id, key, value.into())) - } - - /// Constructs a new [`SetKeyValueBox`] for an [`Asset`] with the given `key` and `value`. - pub fn asset(asset_id: AssetId, key: Name, value: impl Into) -> Self { - Self::Asset(SetKeyValue::new(asset_id, key, value.into())) - } -} - isi_box! { /// Enum with all supported [`RemoveKeyValue`] instructions. pub enum RemoveKeyValueBox { @@ -513,28 +1077,6 @@ isi_box! { } } -impl RemoveKeyValueBox { - /// Constructs a new [`RemoveKeyValueBox`] for a [`Domain`] with the given `key`. - pub fn domain(domain_id: DomainId, key: Name) -> Self { - Self::Domain(RemoveKeyValue::new(domain_id, key)) - } - - /// Constructs a new [`RemoveKeyValueBox`] for an [`Account`] with the given `key`. - pub fn account(account_id: AccountId, key: Name) -> Self { - Self::Account(RemoveKeyValue::new(account_id, key)) - } - - /// Constructs a new [`RemoveKeyValueBox`] for an [`AssetDefinition`] with the given `key`. - pub fn asset_definition(asset_definition_id: AssetDefinitionId, key: Name) -> Self { - Self::AssetDefinition(RemoveKeyValue::new(asset_definition_id, key)) - } - - /// Constructs a new [`RemoveKeyValueBox`] for an [`Asset`] with the given `key`. - pub fn asset(asset_id: AssetId, key: Name) -> Self { - Self::Asset(RemoveKeyValue::new(asset_id, key)) - } -} - isi_box! { /// Enum with all supported [`Register`] instructions. pub enum RegisterBox { @@ -555,43 +1097,6 @@ isi_box! { } } -impl RegisterBox { - /// Constructs a new [`RegisterBox`] for a [`Peer`]. - pub fn peer(new_peer: ::With) -> Self { - Self::Peer(Register::new(new_peer)) - } - - /// Constructs a new [`RegisterBox`] for a [`Domain`]. - pub fn domain(new_domain: ::With) -> Self { - Self::Domain(Register::new(new_domain)) - } - - /// Constructs a new [`RegisterBox`] for an [`Account`]. - pub fn account(new_account: ::With) -> Self { - Self::Account(Register::new(new_account)) - } - - /// Constructs a new [`RegisterBox`] for an [`AssetDefinition`]. - pub fn asset_definition(new_asset_definition: ::With) -> Self { - Self::AssetDefinition(Register::new(new_asset_definition)) - } - - /// Constructs a new [`RegisterBox`] for an [`Asset`]. - pub fn asset(new_asset: ::With) -> Self { - Self::Asset(Register::new(new_asset)) - } - - /// Constructs a new [`RegisterBox`] for a [`Role`]. - pub fn role(new_role: ::With) -> Self { - Self::Role(Register::new(new_role)) - } - - /// Constructs a new [`RegisterBox`] for a [`Trigger`]. - pub fn trigger(new_trigger: as Registered>::With) -> Self { - Self::Trigger(Register::new(new_trigger)) - } -} - isi_box! { /// Enum with all supported [`Unregister`] instructions. pub enum UnregisterBox { @@ -612,43 +1117,6 @@ isi_box! { } } -impl UnregisterBox { - /// Constructs a new [`UnregisterBox`] for a [`Peer`]. - pub fn peer(peer_id: PeerId) -> Self { - Self::Peer(Unregister::new(peer_id)) - } - - /// Constructs a new [`UnregisterBox`] for a [`Domain`]. - pub fn domain(domain_id: DomainId) -> Self { - Self::Domain(Unregister::new(domain_id)) - } - - /// Constructs a new [`UnregisterBox`] for an [`Account`]. - pub fn account(account_id: AccountId) -> Self { - Self::Account(Unregister::new(account_id)) - } - - /// Constructs a new [`UnregisterBox`] for an [`AssetDefinition`]. - pub fn asset_definition(asset_definition_id: AssetDefinitionId) -> Self { - Self::AssetDefinition(Unregister::new(asset_definition_id)) - } - - /// Constructs a new [`UnregisterBox`] for an [`Asset`]. - pub fn asset(asset_id: AssetId) -> Self { - Self::Asset(Unregister::new(asset_id)) - } - - /// Constructs a new [`UnregisterBox`] for a [`Role`]. - pub fn role(role_id: RoleId) -> Self { - Self::Role(Unregister::new(role_id)) - } - - /// Constructs a new [`UnregisterBox`] for a [`Trigger`]. - pub fn trigger(trigger_id: TriggerId) -> Self { - Self::Trigger(Unregister::new(trigger_id)) - } -} - isi_box! { /// Enum with all supported [`Mint`] instructions. pub enum MintBox { @@ -683,44 +1151,6 @@ isi_box! { } } -impl MintBox { - /// Constructs a new [`MintBox`] to mint [`PublicKey`] for [`Account`]. - pub fn account_public_key(public_key: PublicKey, account_id: AccountId) -> Self { - Self::Account(AccountMintBox::PublicKey(Mint::new(public_key, account_id))) - } - - /// Constructs a new [`MintBox`] to mint [`SignatureCheckCondition`] for [`Account`]. - pub fn account_signature_check_condition( - signature_check_condition: SignatureCheckCondition, - account_id: AccountId, - ) -> Self { - Self::Account(AccountMintBox::SignatureCheckCondition(Mint::new( - signature_check_condition, - account_id, - ))) - } - - /// Constructs a new [`MintBox`] to mint [`Asset`] of [`Quantity`] type. - pub fn asset_quantity(quantity: u32, asset_id: AssetId) -> Self { - Self::Asset(AssetMintBox::Quantity(Mint::new(quantity, asset_id))) - } - - /// Constructs a new [`MintBox`] to mint [`Asset`] of [`BigQuantity`] type. - pub fn asset_big_quantity(quantity: u128, asset_id: AssetId) -> Self { - Self::Asset(AssetMintBox::BigQuantity(Mint::new(quantity, asset_id))) - } - - /// Constructs a new [`MintBox`] to mint [`Asset`] of [`Fixed`] type. - pub fn asset_fixed(quantity: Fixed, asset_id: AssetId) -> Self { - Self::Asset(AssetMintBox::Fixed(Mint::new(quantity, asset_id))) - } - - /// Constructs a new [`MintBox`] to mint [`Trigger`] repetitions. - pub fn trigger_repetitions(repetitions: u32, trigger_id: TriggerId) -> Self { - Self::TriggerRepetitions(Mint::new(repetitions, trigger_id)) - } -} - isi_box! { /// Enum with all supported [`Burn`] instructions. pub enum BurnBox { @@ -745,33 +1175,6 @@ isi_box! { } } -impl BurnBox { - /// Constructs a new [`BurnBox`] to burn [`PublicKey`] for [`Account`]. - pub fn account_public_key(public_key: PublicKey, account_id: AccountId) -> Self { - Self::AccountPublicKey(Burn::new(public_key, account_id)) - } - - /// Constructs a new [`BurnBox`] to burn [`Asset`] of [`Quantity`] type. - pub fn asset_quantity(quantity: u32, asset_id: AssetId) -> Self { - Self::Asset(AssetBurnBox::Quantity(Burn::new(quantity, asset_id))) - } - - /// Constructs a new [`BurnBox`] to burn [`Asset`] of [`BigQuantity`] type. - pub fn asset_big_quantity(quantity: u128, asset_id: AssetId) -> Self { - Self::Asset(AssetBurnBox::BigQuantity(Burn::new(quantity, asset_id))) - } - - /// Constructs a new [`BurnBox`] to burn [`Asset`] of [`Fixed`] type. - pub fn asset_fixed(quantity: Fixed, asset_id: AssetId) -> Self { - Self::Asset(AssetBurnBox::Fixed(Burn::new(quantity, asset_id))) - } - - /// Constructs a new [`BurnBox`] to burn [`Trigger`] repetitions. - pub fn trigger_repetitions(repetitions: u32, trigger_id: TriggerId) -> Self { - Self::TriggerRepetitions(Burn::new(repetitions, trigger_id)) - } -} - isi_box! { /// Enum with all supported [`Transfer`] instructions. pub enum TransferBox { @@ -796,57 +1199,6 @@ isi_box! { } } -impl TransferBox { - /// Constructs a new [`TransferBox`] to transfer [`Domain`] to another [`Account`]. - pub fn domain(source_id: AccountId, domain_id: DomainId, destination_id: AccountId) -> Self { - Self::Domain(Transfer::new(source_id, domain_id, destination_id)) - } - - /// Constructs a new [`TransferBox`] to transfer [`AssetDefinition`] to another [`Account`]. - pub fn asset_definition( - source_id: AccountId, - asset_definition_id: AssetDefinitionId, - destination_id: AccountId, - ) -> Self { - Self::AssetDefinition(Transfer::new( - source_id, - asset_definition_id, - destination_id, - )) - } - - /// Constructs a new [`TransferBox`] to transfer [`Asset`] of [`Quantity`] type. - pub fn asset_quantity(source_id: AssetId, quantity: u32, destination_id: AccountId) -> Self { - Self::Asset(AssetTransferBox::Quantity(Transfer::new( - source_id, - quantity, - destination_id, - ))) - } - - /// Constructs a new [`TransferBox`] to transfer [`Asset`] of [`BigQuantity`] type. - pub fn asset_big_quantity( - source_id: AssetId, - quantity: u128, - destination_id: AccountId, - ) -> Self { - Self::Asset(AssetTransferBox::BigQuantity(Transfer::new( - source_id, - quantity, - destination_id, - ))) - } - - /// Constructs a new [`TransferBox`] to transfer [`Asset`] of [`Fixed`] type. - pub fn asset_fixed(source_id: AssetId, quantity: Fixed, destination_id: AccountId) -> Self { - Self::Asset(AssetTransferBox::Fixed(Transfer::new( - source_id, - quantity, - destination_id, - ))) - } -} - isi_box! { /// Enum with all supported [`Grant`] instructions. pub enum GrantBox { @@ -857,18 +1209,6 @@ isi_box! { } } -impl GrantBox { - /// Constructs a new [`GrantBox`] to grant [`PermissionToken`] to [`Account`]. - pub fn permission_token(permission_token: PermissionToken, account_id: AccountId) -> Self { - Self::PermissionToken(Grant::new(permission_token, account_id)) - } - - /// Constructs a new [`GrantBox`] to grant [`Role`] to [`Account`]. - pub fn role(role_id: RoleId, account_id: AccountId) -> Self { - Self::Role(Grant::new(role_id, account_id)) - } -} - isi_box! { /// Enum with all supported [`Revoke`] instructions. pub enum RevokeBox { @@ -879,18 +1219,6 @@ isi_box! { } } -impl RevokeBox { - /// Constructs a new [`RevokeBox`] to revoke [`PermissionToken`] from [`Account`]. - pub fn permission_token(permission_token: PermissionToken, account_id: AccountId) -> Self { - Self::PermissionToken(Revoke::new(permission_token, account_id)) - } - - /// Constructs a new [`RevokeBox`] to revoke [`Role`] from [`Account`]. - pub fn role(role_id: RoleId, account_id: AccountId) -> Self { - Self::Role(Revoke::new(role_id, account_id)) - } -} - pub mod error { //! Module containing errors that can occur during instruction evaluation diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 1b1764d4de8..0852f58d05f 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -75,34 +75,76 @@ pub mod trigger; pub mod visit; mod seal { - use crate::{isi::prelude::*, query::prelude::*}; + use crate::prelude::*; pub trait Sealed {} macro_rules! impl_sealed { - ($($ident:ident),+ $(,)?) => { $( - impl Sealed for $ident {} )+ + ($($ident:ident $(< $($generic:ident $(< $inner_generic:ident >)?),+ >)?),+ $(,)?) => { $( + impl Sealed for $ident $(< $($generic $(< $inner_generic >)?),+ >)? {} )+ }; } impl_sealed! { // Boxed instructions InstructionBox, - SetKeyValueBox, - RemoveKeyValueBox, - RegisterBox, - UnregisterBox, - MintBox, - BurnBox, - TransferBox, - GrantBox, - RevokeBox, + + SetKeyValue, + SetKeyValue, + SetKeyValue, + SetKeyValue, + + RemoveKeyValue, + RemoveKeyValue, + RemoveKeyValue, + RemoveKeyValue, + + Register, + Register, + Register, + Register, + Register, + Register, + Register >, + + Unregister, + Unregister, + Unregister, + Unregister, + Unregister, + Unregister, + Unregister >, + + Mint, + Mint, + Mint, + Mint, + Mint, + Mint >, + + Burn, + Burn, + Burn, + Burn, + Burn >, + + Transfer, + Transfer, + Transfer, + Transfer, + Transfer, + + Grant, + Grant, + + Revoke, + Revoke, + SetParameter, NewParameter, Upgrade, ExecuteTrigger, Log, - Fail, // Boxed queries diff --git a/data_model/src/visit.rs b/data_model/src/visit.rs index 884c0848ad7..9f82b19baae 100644 --- a/data_model/src/visit.rs +++ b/data_model/src/visit.rs @@ -60,7 +60,7 @@ pub trait Visit { visit_find_all_block_headers(&FindAllBlockHeaders), visit_find_all_blocks(&FindAllBlocks), visit_find_all_domains(&FindAllDomains), - visit_find_all_parammeters(&FindAllParameters), + visit_find_all_parameters(&FindAllParameters), visit_find_all_peers(&FindAllPeers), visit_find_permission_token_schema(&FindPermissionTokenSchema), visit_find_all_role_ids(&FindAllRoleIds), @@ -190,7 +190,7 @@ pub fn visit_query(visitor: &mut V, authority: &AccountId, qu visit_find_all_block_headers(FindAllBlockHeaders), visit_find_all_blocks(FindAllBlocks), visit_find_all_domains(FindAllDomains), - visit_find_all_parammeters(FindAllParameters), + visit_find_all_parameters(FindAllParameters), visit_find_all_peers(FindAllPeers), visit_find_permission_token_schema(FindPermissionTokenSchema), visit_find_all_role_ids(FindAllRoleIds), @@ -472,7 +472,7 @@ leaf_visitors! { visit_find_all_block_headers(&FindAllBlockHeaders), visit_find_all_blocks(&FindAllBlocks), visit_find_all_domains(&FindAllDomains), - visit_find_all_parammeters(&FindAllParameters), + visit_find_all_parameters(&FindAllParameters), visit_find_all_peers(&FindAllPeers), visit_find_permission_token_schema(&FindPermissionTokenSchema), visit_find_all_role_ids(&FindAllRoleIds), diff --git a/data_model/tests/data_model.rs b/data_model/tests/data_model.rs index 8fe4b98a7a0..c795f7590a4 100644 --- a/data_model/tests/data_model.rs +++ b/data_model/tests/data_model.rs @@ -2,7 +2,7 @@ use iroha_data_model::{prelude::*, ParseError}; #[test] fn transfer_isi_should_be_valid() { - let _instruction = TransferBox::asset_quantity( + let _instruction = Transfer::asset_quantity( "btc##seller@crypto".parse().expect("Valid"), 12_u32, "buyer@crypto".parse().expect("Valid"), diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index b6ce21dd8c2..84cd9964e49 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -287,7 +287,7 @@ impl RawGenesisBlockBuilder { let new_domain = Domain::new(domain_id.clone()).with_metadata(metadata); self.transaction .isi - .push(RegisterBox::domain(new_domain).into()); + .push(Register::domain(new_domain).into()); RawGenesisDomainBuilder { transaction: self.transaction, domain_id, @@ -322,7 +322,7 @@ impl RawGenesisDomainBuilder { let account_id = AccountId::new(account_name, self.domain_id.clone()); self.transaction .isi - .push(RegisterBox::account(Account::new(account_id, [])).into()); + .push(Register::account(Account::new(account_id, [])).into()); self } @@ -340,7 +340,7 @@ impl RawGenesisDomainBuilder { ) -> Self { let account_id = AccountId::new(account_name, self.domain_id.clone()); let register = - RegisterBox::account(Account::new(account_id, [public_key]).with_metadata(metadata)); + Register::account(Account::new(account_id, [public_key]).with_metadata(metadata)); self.transaction.isi.push(register.into()); self } @@ -356,7 +356,7 @@ impl RawGenesisDomainBuilder { }; self.transaction .isi - .push(RegisterBox::asset_definition(asset_definition).into()); + .push(Register::asset_definition(asset_definition).into()); self } } @@ -418,11 +418,11 @@ mod tests { let domain_id: DomainId = "wonderland".parse().unwrap(); assert_eq!( finished_genesis_block.transactions[0].isi[0], - RegisterBox::domain(Domain::new(domain_id.clone())).into() + Register::domain(Domain::new(domain_id.clone())).into() ); assert_eq!( finished_genesis_block.transactions[0].isi[1], - RegisterBox::account(Account::new( + Register::account(Account::new( AccountId::new("alice".parse().unwrap(), domain_id.clone()), [] )) @@ -430,7 +430,7 @@ mod tests { ); assert_eq!( finished_genesis_block.transactions[0].isi[2], - RegisterBox::account(Account::new( + Register::account(Account::new( AccountId::new("bob".parse().unwrap(), domain_id), [] )) @@ -441,11 +441,11 @@ mod tests { let domain_id: DomainId = "tulgey_wood".parse().unwrap(); assert_eq!( finished_genesis_block.transactions[0].isi[3], - RegisterBox::domain(Domain::new(domain_id.clone())).into() + Register::domain(Domain::new(domain_id.clone())).into() ); assert_eq!( finished_genesis_block.transactions[0].isi[4], - RegisterBox::account(Account::new( + Register::account(Account::new( AccountId::new("Cheshire_Cat".parse().unwrap(), domain_id), [] )) @@ -456,11 +456,11 @@ mod tests { let domain_id: DomainId = "meadow".parse().unwrap(); assert_eq!( finished_genesis_block.transactions[0].isi[5], - RegisterBox::domain(Domain::new(domain_id.clone())).into() + Register::domain(Domain::new(domain_id.clone())).into() ); assert_eq!( finished_genesis_block.transactions[0].isi[6], - RegisterBox::account(Account::new( + Register::account(Account::new( AccountId::new("Mad_Hatter".parse().unwrap(), domain_id), [public_key.parse().unwrap()], )) @@ -468,7 +468,7 @@ mod tests { ); assert_eq!( finished_genesis_block.transactions[0].isi[7], - RegisterBox::asset_definition(AssetDefinition::big_quantity( + Register::asset_definition(AssetDefinition::big_quantity( "hats#meadow".parse().unwrap() )) .into() diff --git a/smart_contract/executor/derive/src/default.rs b/smart_contract/executor/derive/src/default.rs index 472f16db5c2..c7efe702fb2 100644 --- a/smart_contract/executor/derive/src/default.rs +++ b/smart_contract/executor/derive/src/default.rs @@ -122,6 +122,7 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn2::DeriveInput) -> To "fn visit_transfer_domain(operation: &Transfer)", "fn visit_set_domain_key_value(operation: &SetKeyValue)", "fn visit_remove_domain_key_value(operation: &RemoveKeyValue)", + "fn visit_register_account(operation: &Register)", "fn visit_unregister_account(operation: &Unregister)", "fn visit_mint_account_public_key(operation: &Mint)", "fn visit_burn_account_public_key(operation: &Burn)", @@ -141,6 +142,7 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn2::DeriveInput) -> To "fn visit_transfer_asset_fixed(operation: &Transfer)", "fn visit_set_asset_key_value(operation: &SetKeyValue)", "fn visit_remove_asset_key_value(operation: &RemoveKeyValue)", + "fn visit_register_asset_definition(operation: &Register)", "fn visit_unregister_asset_definition(operation: &Unregister)", "fn visit_transfer_asset_definition(operation: &Transfer)", "fn visit_set_asset_definition_key_value(operation: &SetKeyValue)", @@ -151,6 +153,7 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn2::DeriveInput) -> To "fn visit_unregister_role(operation: &Unregister)", "fn visit_grant_account_role(operation: &Grant)", "fn visit_revoke_account_role(operation: &Revoke)", + "fn visit_register_trigger(operation: &Register>)", "fn visit_unregister_trigger(operation: &Unregister>)", "fn visit_mint_trigger_repetitions(operation: &Mint>)", "fn visit_burn_trigger_repetitions(operation: &Burn>)", @@ -158,6 +161,8 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn2::DeriveInput) -> To "fn visit_set_parameter(operation: &SetParameter)", "fn visit_new_parameter(operation: &NewParameter)", "fn visit_upgrade(operation: &Upgrade)", + "fn visit_log(operation: &Log)", + "fn visit_fail(operation: &Fail)", ] .into_iter() .map(|item| { diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index a09ddc11de4..5b70d96a884 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -7,8 +7,8 @@ use alloc::format; pub use account::{ visit_burn_account_public_key, visit_mint_account_public_key, - visit_mint_account_signature_check_condition, visit_remove_account_key_value, - visit_set_account_key_value, visit_unregister_account, + visit_mint_account_signature_check_condition, visit_register_account, + visit_remove_account_key_value, visit_set_account_key_value, visit_unregister_account, }; pub use asset::{ visit_burn_asset_big_quantity, visit_burn_asset_fixed, visit_burn_asset_quantity, @@ -18,14 +18,18 @@ pub use asset::{ visit_unregister_asset, }; pub use asset_definition::{ - visit_remove_asset_definition_key_value, visit_set_asset_definition_key_value, - visit_transfer_asset_definition, visit_unregister_asset_definition, + visit_register_asset_definition, visit_remove_asset_definition_key_value, + visit_set_asset_definition_key_value, visit_transfer_asset_definition, + visit_unregister_asset_definition, }; pub use domain::{ visit_register_domain, visit_remove_domain_key_value, visit_set_domain_key_value, visit_transfer_domain, visit_unregister_domain, }; pub use executor::visit_upgrade; +pub use fail::visit_fail; +use iroha_smart_contract::data_model::isi::InstructionBox; +pub use log::visit_log; pub use parameter::{visit_new_parameter, visit_set_parameter}; pub use peer::{visit_register_peer, visit_unregister_peer}; pub use permission_token::{visit_grant_account_permission, visit_revoke_account_permission}; @@ -34,7 +38,7 @@ pub use role::{ }; pub use trigger::{ visit_burn_trigger_repetitions, visit_execute_trigger, visit_mint_trigger_repetitions, - visit_unregister_trigger, + visit_register_trigger, visit_unregister_trigger, }; use crate::{permission, permission::Token as _, prelude::*}; @@ -86,41 +90,52 @@ pub fn visit_instruction( authority: &AccountId, isi: &InstructionBox, ) { - macro_rules! isi_executors { - ($( - $executor:ident($isi:ident) - ),+ $(,)?) => { - match isi { $( - InstructionBox::$isi(isi) => { - executor.$executor(authority, isi); - - if executor.verdict().is_ok() { - // TODO: Execution should be infallible after successful validation - if let Err(err) = isi.execute() { - executor.deny(err); - } - } - } - )+ } - }; - } - - isi_executors! { - visit_new_parameter(NewParameter), - visit_set_parameter(SetParameter), - visit_log(Log), - visit_execute_trigger(ExecuteTrigger), - visit_burn(Burn), - visit_fail(Fail), - visit_grant(Grant), - visit_mint(Mint), - visit_register(Register), - visit_remove_key_value(RemoveKeyValue), - visit_revoke(Revoke), - visit_set_key_value(SetKeyValue), - visit_transfer(Transfer), - visit_unregister(Unregister), - visit_upgrade(Upgrade), + match isi { + InstructionBox::NewParameter(isi) => { + executor.visit_new_parameter(authority, isi); + } + InstructionBox::SetParameter(isi) => { + executor.visit_set_parameter(authority, isi); + } + InstructionBox::Log(isi) => { + executor.visit_log(authority, isi); + } + InstructionBox::ExecuteTrigger(isi) => { + executor.visit_execute_trigger(authority, isi); + } + InstructionBox::Burn(isi) => { + executor.visit_burn(authority, isi); + } + InstructionBox::Fail(isi) => { + executor.visit_fail(authority, isi); + } + InstructionBox::Grant(isi) => { + executor.visit_grant(authority, isi); + } + InstructionBox::Mint(isi) => { + executor.visit_mint(authority, isi); + } + InstructionBox::Register(isi) => { + executor.visit_register(authority, isi); + } + InstructionBox::RemoveKeyValue(isi) => { + executor.visit_remove_key_value(authority, isi); + } + InstructionBox::Revoke(isi) => { + executor.visit_revoke(authority, isi); + } + InstructionBox::SetKeyValue(isi) => { + executor.visit_set_key_value(authority, isi); + } + InstructionBox::Transfer(isi) => { + executor.visit_transfer(authority, isi); + } + InstructionBox::Unregister(isi) => { + executor.visit_unregister(authority, isi); + } + InstructionBox::Upgrade(isi) => { + executor.visit_upgrade(authority, isi); + } } } @@ -128,23 +143,24 @@ pub mod peer { use super::*; pub fn visit_register_peer( - _executor: &mut V, + executor: &mut V, _authority: &AccountId, - _isi: &Register, + isi: &Register, ) { + execute!(executor, isi) } #[allow(clippy::needless_pass_by_value)] pub fn visit_unregister_peer( executor: &mut V, authority: &AccountId, - _isi: &Unregister, + isi: &Unregister, ) { if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } if tokens::peer::CanUnregisterAnyPeer.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't unregister peer"); @@ -157,10 +173,11 @@ pub mod domain { use super::*; pub fn visit_register_domain( - _executor: &mut V, + executor: &mut V, _authority: &AccountId, - _isi: &Register, + isi: &Register, ) { + execute!(executor, isi) } pub fn visit_unregister_domain( @@ -171,18 +188,18 @@ pub mod domain { let domain_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_domain_owner(domain_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_unregister_domain_token = tokens::domain::CanUnregisterDomain { domain_id: domain_id.clone(), }; if can_unregister_domain_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't unregister domain"); @@ -196,11 +213,11 @@ pub mod domain { let destination_id = isi.object(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_domain_owner(destination_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } @@ -215,18 +232,18 @@ pub mod domain { let domain_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_domain_owner(domain_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_set_key_value_in_domain_token = tokens::domain::CanSetKeyValueInDomain { domain_id: domain_id.clone(), }; if can_set_key_value_in_domain_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't set key value in domain metadata"); @@ -240,18 +257,18 @@ pub mod domain { let domain_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_domain_owner(domain_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_remove_key_value_in_domain_token = tokens::domain::CanRemoveKeyValueInDomain { domain_id: domain_id.clone(), }; if can_remove_key_value_in_domain_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't remove key value in domain metadata"); @@ -263,6 +280,14 @@ pub mod account { use super::*; + pub fn visit_register_account( + executor: &mut V, + _authority: &AccountId, + isi: &Register, + ) { + execute!(executor, isi) + } + pub fn visit_unregister_account( executor: &mut V, authority: &AccountId, @@ -271,18 +296,18 @@ pub mod account { let account_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_account_owner(account_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_unregister_user_account = tokens::account::CanUnregisterAccount { account_id: account_id.clone(), }; if can_unregister_user_account.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't unregister another account"); @@ -296,18 +321,18 @@ pub mod account { let account_id = isi.destination_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_account_owner(account_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_mint_user_public_keys = tokens::account::CanMintUserPublicKeys { account_id: account_id.clone(), }; if can_mint_user_public_keys.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't mint public keys of another account"); @@ -321,18 +346,18 @@ pub mod account { let account_id = isi.destination_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_account_owner(account_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_burn_user_public_keys = tokens::account::CanBurnUserPublicKeys { account_id: account_id.clone(), }; if can_burn_user_public_keys.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't burn public keys of another account"); @@ -346,11 +371,11 @@ pub mod account { let account_id = isi.destination_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_account_owner(account_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_mint_user_signature_check_conditions_token = @@ -358,7 +383,7 @@ pub mod account { account_id: account_id.clone(), }; if can_mint_user_signature_check_conditions_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -375,11 +400,11 @@ pub mod account { let account_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_account_owner(account_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_set_key_value_in_user_account_token = @@ -387,7 +412,7 @@ pub mod account { account_id: account_id.clone(), }; if can_set_key_value_in_user_account_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -404,11 +429,11 @@ pub mod account { let account_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_account_owner(account_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_remove_key_value_in_user_account_token = @@ -416,7 +441,7 @@ pub mod account { account_id: account_id.clone(), }; if can_remove_key_value_in_user_account_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -431,6 +456,14 @@ pub mod asset_definition { use super::*; + pub fn visit_register_asset_definition( + executor: &mut V, + _authority: &AccountId, + isi: &Register, + ) { + execute!(executor, isi); + } + pub fn visit_unregister_asset_definition( executor: &mut V, authority: &AccountId, @@ -439,11 +472,11 @@ pub mod asset_definition { let asset_definition_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_definition_owner(asset_definition_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_unregister_asset_definition_token = @@ -451,7 +484,7 @@ pub mod asset_definition { asset_definition_id: asset_definition_id.clone(), }; if can_unregister_asset_definition_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -469,16 +502,16 @@ pub mod asset_definition { let destination_id = isi.object(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_account_owner(source_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } match is_asset_definition_owner(destination_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } @@ -496,11 +529,11 @@ pub mod asset_definition { let asset_definition_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_definition_owner(asset_definition_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_set_key_value_in_asset_definition_token = @@ -508,7 +541,7 @@ pub mod asset_definition { asset_definition_id: asset_definition_id.clone(), }; if can_set_key_value_in_asset_definition_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -525,11 +558,11 @@ pub mod asset_definition { let asset_definition_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_definition_owner(asset_definition_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_remove_key_value_in_asset_definition_token = @@ -537,7 +570,7 @@ pub mod asset_definition { asset_definition_id: asset_definition_id.clone(), }; if can_remove_key_value_in_asset_definition_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -548,6 +581,8 @@ pub mod asset_definition { } pub mod asset { + use iroha_smart_contract::data_model::isi::Instruction; + use iroha_smart_contract_utils::Encode; use permission::{asset::is_asset_owner, asset_definition::is_asset_definition_owner}; use super::*; @@ -560,11 +595,11 @@ pub mod asset { let asset = isi.object(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_definition_owner(asset.id().definition_id(), authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_register_assets_with_definition_token = @@ -572,7 +607,7 @@ pub mod asset { asset_definition_id: asset.id().definition_id().clone(), }; if can_register_assets_with_definition_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -589,16 +624,16 @@ pub mod asset { let asset_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_owner(asset_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } match is_asset_definition_owner(asset_id.definition_id(), authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_unregister_assets_with_definition_token = @@ -606,36 +641,38 @@ pub mod asset { asset_definition_id: asset_id.definition_id().clone(), }; if can_unregister_assets_with_definition_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } let can_unregister_user_asset_token = tokens::asset::CanUnregisterUserAsset { asset_id: asset_id.clone(), }; if can_unregister_user_asset_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't unregister asset from another account"); } - fn validate_mint_asset( - executor: &mut V, - authority: &AccountId, - asset_id: &AssetId, - ) { + fn validate_mint_asset(executor: &mut V, authority: &AccountId, isi: &Mint) + where + V: Validate + ?Sized, + Q: Into, + Mint: Instruction + Encode + Clone, + { + let asset_id = isi.destination_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_definition_owner(asset_id.definition_id(), authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_mint_assets_with_definition_token = tokens::asset::CanMintAssetsWithDefinition { asset_definition_id: asset_id.definition_id().clone(), }; if can_mint_assets_with_definition_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -649,8 +686,7 @@ pub mod asset { authority: &AccountId, isi: &Mint, ) { - let asset_id = isi.destination_id(); - validate_mint_asset(executor, authority, asset_id); + validate_mint_asset(executor, authority, isi); } pub fn visit_mint_asset_big_quantity( @@ -658,8 +694,7 @@ pub mod asset { authority: &AccountId, isi: &Mint, ) { - let asset_id = isi.destination_id(); - validate_mint_asset(executor, authority, asset_id); + validate_mint_asset(executor, authority, isi); } pub fn visit_mint_asset_fixed( @@ -667,39 +702,40 @@ pub mod asset { authority: &AccountId, isi: &Mint, ) { - let asset_id = isi.destination_id(); - validate_mint_asset(executor, authority, asset_id); + validate_mint_asset(executor, authority, isi); } - fn validate_burn_asset( - executor: &mut V, - authority: &AccountId, - asset_id: &AssetId, - ) { + fn validate_burn_asset(executor: &mut V, authority: &AccountId, isi: &Burn) + where + V: Validate + ?Sized, + Q: Into, + Burn: Instruction + Encode + Clone, + { + let asset_id = isi.destination_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_owner(asset_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } match is_asset_definition_owner(asset_id.definition_id(), authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_burn_assets_with_definition_token = tokens::asset::CanBurnAssetsWithDefinition { asset_definition_id: asset_id.definition_id().clone(), }; if can_burn_assets_with_definition_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } let can_burn_user_asset_token = tokens::asset::CanBurnUserAsset { asset_id: asset_id.clone(), }; if can_burn_user_asset_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't burn assets from another account"); @@ -710,8 +746,7 @@ pub mod asset { authority: &AccountId, isi: &Burn, ) { - let asset_id = isi.destination_id(); - validate_burn_asset(executor, authority, asset_id); + validate_burn_asset(executor, authority, isi); } pub fn visit_burn_asset_big_quantity( @@ -719,8 +754,7 @@ pub mod asset { authority: &AccountId, isi: &Burn, ) { - let asset_id = isi.destination_id(); - validate_burn_asset(executor, authority, asset_id); + validate_burn_asset(executor, authority, isi); } pub fn visit_burn_asset_fixed( @@ -728,26 +762,30 @@ pub mod asset { authority: &AccountId, isi: &Burn, ) { - let asset_id = isi.destination_id(); - validate_burn_asset(executor, authority, asset_id); + validate_burn_asset(executor, authority, isi); } - fn validate_transfer_asset( + fn validate_transfer_asset( executor: &mut V, authority: &AccountId, - asset_id: &AssetId, - ) { + isi: &Transfer, + ) where + V: Validate + ?Sized, + Q: Into, + Transfer: Instruction + Encode + Clone, + { + let asset_id = isi.source_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_owner(asset_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } match is_asset_definition_owner(asset_id.definition_id(), authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_transfer_assets_with_definition_token = @@ -755,13 +793,13 @@ pub mod asset { asset_definition_id: asset_id.definition_id().clone(), }; if can_transfer_assets_with_definition_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } let can_transfer_user_asset_token = tokens::asset::CanTransferUserAsset { asset_id: asset_id.clone(), }; if can_transfer_user_asset_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't transfer assets of another account"); @@ -772,8 +810,7 @@ pub mod asset { authority: &AccountId, isi: &Transfer, ) { - let asset_id = isi.source_id(); - validate_transfer_asset(executor, authority, asset_id); + validate_transfer_asset(executor, authority, isi); } pub fn visit_transfer_asset_big_quantity( @@ -781,8 +818,7 @@ pub mod asset { authority: &AccountId, isi: &Transfer, ) { - let asset_id = isi.source_id(); - validate_transfer_asset(executor, authority, asset_id); + validate_transfer_asset(executor, authority, isi); } pub fn visit_transfer_asset_fixed( @@ -790,8 +826,7 @@ pub mod asset { authority: &AccountId, isi: &Transfer, ) { - let asset_id = isi.source_id(); - validate_transfer_asset(executor, authority, asset_id); + validate_transfer_asset(executor, authority, isi); } pub fn visit_set_asset_key_value( @@ -802,11 +837,11 @@ pub mod asset { let asset_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_owner(asset_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } @@ -814,7 +849,7 @@ pub mod asset { asset_id: asset_id.clone(), }; if can_set_key_value_in_user_asset_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -831,11 +866,11 @@ pub mod asset { let asset_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_asset_owner(asset_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_remove_key_value_in_user_asset_token = @@ -843,7 +878,7 @@ pub mod asset { asset_id: asset_id.clone(), }; if can_remove_key_value_in_user_asset_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -860,13 +895,13 @@ pub mod parameter { pub fn visit_new_parameter( executor: &mut V, authority: &AccountId, - _isi: &NewParameter, + isi: &NewParameter, ) { if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } if tokens::parameter::CanCreateParameters.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -879,13 +914,13 @@ pub mod parameter { pub fn visit_set_parameter( executor: &mut V, authority: &AccountId, - _isi: &SetParameter, + isi: &SetParameter, ) { if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } if tokens::parameter::CanSetParameters.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -934,6 +969,7 @@ pub mod role { } assert!(unknown_tokens.is_empty(), "Role contains unknown permission tokens: {unknown_tokens:?}"); + execute!($executor, $isi) }; } @@ -969,20 +1005,20 @@ pub mod role { ); } - pass!(executor); + execute!(executor, isi); } #[allow(clippy::needless_pass_by_value)] pub fn visit_unregister_role( executor: &mut V, authority: &AccountId, - _isi: &Unregister, + isi: &Unregister, ) { if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } if tokens::role::CanUnregisterAnyRole.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't unregister role"); @@ -1010,6 +1046,14 @@ pub mod trigger { use super::*; + pub fn visit_register_trigger( + executor: &mut V, + _authority: &AccountId, + isi: &Register>, + ) { + execute!(executor, isi) + } + pub fn visit_unregister_trigger( executor: &mut V, authority: &AccountId, @@ -1018,18 +1062,18 @@ pub mod trigger { let trigger_id = isi.object_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_trigger_owner(trigger_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_unregister_user_trigger_token = tokens::trigger::CanUnregisterUserTrigger { trigger_id: trigger_id.clone(), }; if can_unregister_user_trigger_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -1046,18 +1090,18 @@ pub mod trigger { let trigger_id = isi.destination_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_trigger_owner(trigger_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_mint_user_trigger_token = tokens::trigger::CanMintUserTrigger { trigger_id: trigger_id.clone(), }; if can_mint_user_trigger_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -1074,18 +1118,18 @@ pub mod trigger { let trigger_id = isi.destination_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_trigger_owner(trigger_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_mint_user_trigger_token = tokens::trigger::CanBurnUserTrigger { trigger_id: trigger_id.clone(), }; if can_mint_user_trigger_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!( @@ -1102,18 +1146,18 @@ pub mod trigger { let trigger_id = isi.trigger_id(); if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } match is_trigger_owner(trigger_id, authority) { Err(err) => deny!(executor, err), - Ok(true) => pass!(executor), + Ok(true) => execute!(executor, isi), Ok(false) => {} } let can_execute_trigger_token = tokens::trigger::CanExecuteUserTrigger { trigger_id: trigger_id.clone(), }; if can_execute_trigger_token.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't execute trigger owned by another account"); @@ -1124,14 +1168,14 @@ pub mod permission_token { use super::*; macro_rules! impl_validate { - ($executor:ident, $authority:ident, $self:ident, $method:ident) => { + ($executor:ident, $authority:ident, $isi:ident, $method:ident) => { // TODO: https://github.com/hyperledger/iroha/issues/4082 - let token = $self.object().clone(); + let token = $isi.object().clone(); macro_rules! visit_internal { ($token:ident) => { if is_genesis($executor) { - pass!($executor); + execute!($executor, $isi); } if let Err(error) = permission::ValidateGrantRevoke::$method( &$token, @@ -1141,7 +1185,7 @@ pub mod permission_token { deny!($executor, error); } - pass!($executor); + execute!($executor, $isi); }; } @@ -1178,19 +1222,35 @@ pub mod executor { pub fn visit_upgrade( executor: &mut V, authority: &AccountId, - _isi: &Upgrade, + isi: &Upgrade, ) { if is_genesis(executor) { - pass!(executor); + execute!(executor, isi); } if tokens::executor::CanUpgradeExecutor.is_owned_by(authority) { - pass!(executor); + execute!(executor, isi); } deny!(executor, "Can't upgrade executor"); } } +pub mod log { + use super::*; + + pub fn visit_log(executor: &mut V, _authority: &AccountId, isi: &Log) { + execute!(executor, isi) + } +} + +pub mod fail { + use super::*; + + pub fn visit_fail(executor: &mut V, _authority: &AccountId, isi: &Fail) { + execute!(executor, isi) + } +} + fn is_genesis(executor: &V) -> bool { executor.block_height() == 0 } diff --git a/smart_contract/executor/src/lib.rs b/smart_contract/executor/src/lib.rs index e3770f4fb98..ef953f78f14 100644 --- a/smart_contract/executor/src/lib.rs +++ b/smart_contract/executor/src/lib.rs @@ -131,20 +131,23 @@ mod host { } } -/// Shortcut for `return Ok(())`. +/// Execute instruction if verdict is [`Ok`], deny if execution failed and return. +/// +/// Convention is that you have no checks left if you decided to execute instruction. #[macro_export] -macro_rules! pass { - ($executor:ident) => {{ - #[cfg(debug_assertions)] - if let Err(_error) = $executor.verdict() { - unreachable!("Executor already denied"); +macro_rules! execute { + ($executor:ident, $isi:ident) => {{ + if $executor.verdict().is_ok() { + if let Err(err) = $isi.execute() { + $executor.deny(err); + } } return; }}; } -/// Shortcut for `return Err(ValidationFail)`. +/// Shortcut for setting verdict to [`Err`] and return. /// /// Supports [`format!`](alloc::fmt::format) syntax as well as any expression returning [`String`](alloc::string::String). #[macro_export] @@ -229,6 +232,6 @@ pub mod prelude { visit::Visit, ValidationFail, }, - deny, pass, PermissionTokenSchema, Validate, + deny, execute, PermissionTokenSchema, Validate, }; } diff --git a/smart_contract/src/lib.rs b/smart_contract/src/lib.rs index 86c6407a101..2bdd93706db 100644 --- a/smart_contract/src/lib.rs +++ b/smart_contract/src/lib.rs @@ -438,7 +438,7 @@ mod tests { fn get_test_instruction() -> InstructionBox { let new_account_id = "mad_hatter@wonderland".parse().expect("Valid"); - let register_isi = RegisterBox::account(Account::new(new_account_id, [])); + let register_isi = Register::account(Account::new(new_account_id, [])); register_isi.into() } diff --git a/smart_contract/utils/src/lib.rs b/smart_contract/utils/src/lib.rs index ec9f70a242e..5e0919b095a 100644 --- a/smart_contract/utils/src/lib.rs +++ b/smart_contract/utils/src/lib.rs @@ -7,7 +7,7 @@ extern crate alloc; use alloc::{boxed::Box, format, vec::Vec}; use core::ops::RangeFrom; -use parity_scale_codec::{DecodeAll, Encode}; +pub use parity_scale_codec::{DecodeAll, Encode}; pub mod debug; pub mod log; diff --git a/tools/kagami/src/genesis.rs b/tools/kagami/src/genesis.rs index 2fcde10db5f..6036e4723ab 100644 --- a/tools/kagami/src/genesis.rs +++ b/tools/kagami/src/genesis.rs @@ -4,7 +4,6 @@ use clap::{ArgGroup, Parser, Subcommand}; use iroha_config::{sumeragi::default::*, wasm::default::*, wsv::default::*}; use iroha_data_model::{ asset::AssetValueType, - isi::{MintBox, RegisterBox}, metadata::Limits, parameter::{default::*, ParametersBuilder}, prelude::AssetId, @@ -145,19 +144,19 @@ pub fn generate_default(executor: ExecutorMode) -> color_eyre::Result::new( - vec![MintBox::asset_quantity(1_u32, rose_id)], + vec![Mint::asset_quantity(1_u32, rose_id)], Repeats::Indefinitely, account_id, FilterBox::Data(DataEventFilter::BySome(DataEntityFilter::ByAccount(