From 103ffc4a3ad686d954408b0b2398034dfd101f55 Mon Sep 17 00:00:00 2001 From: Shunkichi Sato <49983831+s8sato@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:24:19 +0900 Subject: [PATCH] feat!: authenticate personal accounts by ID BREAKING CHANGE: - change `AccountId` from "name@domain" to "multihash@domain" - make account signatory single - make transaction signature single - remove query `FindAccountsByName` closes issue #2085 Signed-off-by: Shunkichi Sato <49983831+s8sato@users.noreply.github.com> --- .rustfmt.toml | 10 +- Cargo.lock | 17 + Cargo.toml | 3 + cli/src/lib.rs | 18 +- cli/src/samples.rs | 25 +- client/Cargo.toml | 1 + client/benches/torii.rs | 40 +- client/benches/tps/utils.rs | 34 +- client/examples/million_accounts_genesis.rs | 21 +- client/examples/register_1000_triggers.rs | 9 +- client/examples/tutorial.rs | 58 ++- client/src/client.rs | 8 +- client/src/config.rs | 6 +- client/src/config/user.rs | 8 +- client/src/config/user/boilerplate.rs | 10 +- client/src/lib.rs | 7 +- client/tests/integration/add_account.rs | 45 -- client/tests/integration/asset.rs | 253 ++++------ client/tests/integration/asset_propagation.rs | 7 +- client/tests/integration/burn_public_keys.rs | 110 ----- .../integration/domain_owner_permissions.rs | 83 +--- client/tests/integration/events/data.rs | 5 +- .../tests/integration/events/notification.rs | 5 +- .../multiple_blocks_created.rs | 7 +- .../extra_functional/offline_peers.rs | 3 +- .../extra_functional/restart_peer.rs | 3 +- .../extra_functional/unregister_peer.rs | 7 +- .../extra_functional/unstable_network.rs | 3 +- client/tests/integration/mod.rs | 11 - .../integration/multisignature_account.rs | 48 -- .../integration/multisignature_transaction.rs | 101 ---- client/tests/integration/non_mintable.rs | 7 +- client/tests/integration/pagination.rs | 17 +- client/tests/integration/permissions.rs | 39 +- client/tests/integration/queries/account.rs | 15 +- client/tests/integration/queries/asset.rs | 19 +- .../tests/integration/queries/query_errors.rs | 7 +- client/tests/integration/queries/role.rs | 3 +- client/tests/integration/roles.rs | 46 +- .../src/lib.rs | 4 +- .../executor_with_admin/src/lib.rs | 7 +- .../executor_with_migration_fail/src/lib.rs | 2 +- .../query_assets_and_save_cursor/src/lib.rs | 2 +- client/tests/integration/sorting.rs | 181 ++++--- client/tests/integration/status_response.rs | 4 +- client/tests/integration/transfer_asset.rs | 9 +- .../integration/triggers/by_call_trigger.rs | 23 +- .../integration/triggers/data_trigger.rs | 22 +- .../integration/triggers/event_trigger.rs | 5 +- .../integration/triggers/time_trigger.rs | 23 +- .../integration/triggers/trigger_rollback.rs | 3 +- client/tests/integration/tx_chain_id.rs | 34 +- client/tests/integration/tx_history.rs | 3 +- client/tests/integration/tx_rollback.rs | 3 +- client/tests/integration/upgrade.rs | 32 +- client_cli/README.md | 39 +- client_cli/pytests/README.md | 27 +- client_cli/pytests/common/consts.py | 4 +- client_cli/pytests/common/helpers.py | 4 +- .../json_isi_examples/unregister_asset.json | 4 +- client_cli/pytests/models/account.py | 11 +- .../pytests/src/client_cli/client_cli.py | 31 +- .../pytests/src/client_cli/configuration.py | 30 +- client_cli/pytests/src/client_cli/iroha.py | 2 +- client_cli/pytests/test/__init__.py | 1 - client_cli/pytests/test/accounts/conftest.py | 1 - .../accounts/test_accounts_query_filters.py | 20 +- .../test/accounts/test_register_accounts.py | 97 +--- .../test/assets/test_assets_query_filters.py | 2 +- .../pytests/test/assets/test_burn_assets.py | 4 +- .../pytests/test/assets/test_mint_assets.py | 15 +- .../assets/test_register_asset_definitions.py | 6 +- .../test/assets/test_transfer_assets.py | 24 +- .../test/assets/test_unregister_asset.py | 6 +- client_cli/pytests/test/conftest.py | 29 +- .../test/domains/test_register_domains.py | 8 +- .../test/triggers/test_register_trigger.py | 10 +- client_cli/src/main.rs | 63 +-- config/iroha_test_config.toml | 2 +- configs/client.template.toml | 2 +- configs/swarm/client.toml | 6 +- configs/swarm/executor.wasm | Bin 526781 -> 517097 bytes configs/swarm/genesis.json | 38 +- core/Cargo.toml | 2 + core/benches/blocks/apply_blocks.rs | 19 +- core/benches/blocks/apply_blocks_benchmark.rs | 2 +- core/benches/blocks/apply_blocks_oneshot.rs | 2 +- core/benches/blocks/common.rs | 27 +- core/benches/blocks/validate_blocks.rs | 15 +- core/benches/kura.rs | 17 +- core/benches/validation.rs | 78 ++- core/src/block.rs | 52 +- core/src/lib.rs | 7 +- core/src/queue.rs | 310 +++--------- core/src/smartcontracts/isi/account.rs | 117 +---- core/src/smartcontracts/isi/domain.rs | 7 +- core/src/smartcontracts/isi/mod.rs | 49 +- core/src/smartcontracts/isi/query.rs | 47 +- core/src/smartcontracts/isi/world.rs | 2 +- core/src/smartcontracts/wasm.rs | 42 +- core/src/snapshot.rs | 4 +- core/src/state.rs | 11 +- core/src/sumeragi/main_loop.rs | 115 ++--- core/src/tx.rs | 13 +- core/test_network/Cargo.toml | 2 +- core/test_network/src/lib.rs | 40 +- .../ui_fail/transparent_api_private_field.rs | 4 +- .../transparent_api_private_field.stderr | 12 +- data_model/src/account.rs | 444 +++--------------- data_model/src/asset.rs | 110 +++-- data_model/src/block.rs | 7 +- data_model/src/events/data/filters.rs | 23 +- data_model/src/isi.rs | 74 +-- data_model/src/lib.rs | 5 +- data_model/src/query/mod.rs | 17 +- data_model/src/query/predicate.rs | 97 ++-- data_model/src/transaction.rs | 38 +- data_model/src/visit.rs | 17 - data_model/tests/data_model.rs | 26 +- docs/source/references/schema.json | 204 ++------ genesis/Cargo.toml | 1 + genesis/src/lib.rs | 105 +++-- schema/gen/src/lib.rs | 11 - smart_contract/executor/derive/src/default.rs | 3 - smart_contract/executor/derive/src/lib.rs | 2 +- smart_contract/executor/src/default.rs | 88 +--- smart_contract/src/lib.rs | 36 +- test_samples/Cargo.toml | 29 ++ test_samples/src/lib.rs | 43 ++ tools/kagami/Cargo.toml | 1 + tools/kagami/src/genesis.rs | 50 +- tools/kagami/src/main.rs | 11 +- tools/parity_scale_cli/Cargo.toml | 3 + tools/parity_scale_cli/samples/account.bin | Bin 64 -> 57 bytes tools/parity_scale_cli/samples/account.json | 5 +- tools/parity_scale_cli/samples/trigger.bin | Bin 76 -> 132 bytes tools/parity_scale_cli/samples/trigger.json | 4 +- tools/parity_scale_cli/src/main.rs | 10 +- torii/src/lib.rs | 2 +- torii/src/routing.rs | 1 + 140 files changed, 1450 insertions(+), 2975 deletions(-) delete mode 100644 client/tests/integration/add_account.rs delete mode 100644 client/tests/integration/burn_public_keys.rs delete mode 100644 client/tests/integration/multisignature_account.rs delete mode 100644 client/tests/integration/multisignature_transaction.rs create mode 100644 test_samples/Cargo.toml create mode 100644 test_samples/src/lib.rs diff --git a/.rustfmt.toml b/.rustfmt.toml index 0f32415c590..f9b0b3f3643 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,5 +1,5 @@ -newline_style="Unix" -use_field_init_shorthand=true -format_code_in_doc_comments = true # unstable (https://github.com/rust-lang/rustfmt/issues/3348) -group_imports="StdExternalCrate" # unstable (https://github.com/rust-lang/rustfmt/issues/5083) -imports_granularity="Crate" # unstable (https://github.com/rust-lang/rustfmt/issues/4991) +newline_style = "Unix" +use_field_init_shorthand = true +format_code_in_doc_comments = true # unstable (https://github.com/rust-lang/rustfmt/issues/3348) +group_imports = "StdExternalCrate" # unstable (https://github.com/rust-lang/rustfmt/issues/5083) +imports_granularity = "Crate" # unstable (https://github.com/rust-lang/rustfmt/issues/4991) diff --git a/Cargo.lock b/Cargo.lock index dff2ee23c08..54c8e2a54cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2855,6 +2855,7 @@ dependencies = [ "serde_with", "tempfile", "test_network", + "test_samples", "thiserror", "tokio", "tokio-tungstenite", @@ -2967,6 +2968,7 @@ dependencies = [ "serde_json", "storage", "tempfile", + "test_samples", "thiserror", "tokio", "uuid", @@ -3176,6 +3178,7 @@ dependencies = [ "once_cell", "serde", "serde_json", + "test_samples", "tracing", ] @@ -3681,6 +3684,7 @@ dependencies = [ "parity-scale-codec", "serde", "serde_json", + "test_samples", ] [[package]] @@ -4251,6 +4255,7 @@ dependencies = [ "parity-scale-codec", "serde", "serde_json", + "test_samples", ] [[package]] @@ -5711,10 +5716,22 @@ dependencies = [ "rand", "serde_json", "tempfile", + "test_samples", "tokio", "unique_port", ] +[[package]] +name = "test_samples" +version = "2.0.0-pre-rc.21" +dependencies = [ + "iroha_crypto", + "iroha_data_model", + "once_cell", + "serde", + "toml 0.8.12", +] + [[package]] name = "thiserror" version = "1.0.59" diff --git a/Cargo.toml b/Cargo.toml index 6625d6e2f9f..085f34fea5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,8 @@ iroha_executor_derive = { version = "=2.0.0-pre-rc.21", path = "smart_contract/e iroha_trigger_derive = { version = "=2.0.0-pre-rc.21", path = "smart_contract/trigger/derive" } test_network = { version = "=2.0.0-pre-rc.21", path = "core/test_network" } +test_samples = { version = "=2.0.0-pre-rc.21", path = "test_samples" } + proc-macro2 = "1.0.81" syn = { version = "2.0.60", default-features = false } quote = "1.0.36" @@ -228,6 +230,7 @@ members = [ "smart_contract/executor", "smart_contract/executor/derive", "telemetry", + "test_samples", "tools/kagami", "tools/kura_inspector", "tools/parity_scale_cli", diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 947f0a96321..e54aac98f66 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -210,10 +210,7 @@ impl Iroha { .wrap_err("Unable to start P2P-network")?; let (events_sender, _) = broadcast::channel(10000); - let world = World::with( - [genesis_domain(config.genesis.public_key().clone())], - config.sumeragi.trusted_peers.clone(), - ); + let world = World::with([genesis_domain()], config.sumeragi.trusted_peers.clone()); let kura = Kura::new(&config.kura)?; let kura_thread_handler = Kura::start(Arc::clone(&kura)); @@ -485,19 +482,18 @@ impl Iroha { } } -fn genesis_account(public_key: PublicKey) -> Account { - Account::new(iroha_genesis::GENESIS_ACCOUNT_ID.clone(), public_key) +fn genesis_account() -> Account { + Account::new(iroha_genesis::GENESIS_ACCOUNT_ID.clone()) .build(&iroha_genesis::GENESIS_ACCOUNT_ID) } -fn genesis_domain(public_key: PublicKey) -> Domain { +fn genesis_domain() -> Domain { let mut domain = Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()) .build(&iroha_genesis::GENESIS_ACCOUNT_ID); - domain.accounts.insert( - iroha_genesis::GENESIS_ACCOUNT_ID.clone(), - genesis_account(public_key), - ); + domain + .accounts + .insert(iroha_genesis::GENESIS_ACCOUNT_ID.clone(), genesis_account()); domain } diff --git a/cli/src/samples.rs b/cli/src/samples.rs index 26fc0acd300..fe45f22e104 100644 --- a/cli/src/samples.rs +++ b/cli/src/samples.rs @@ -62,18 +62,24 @@ pub fn get_trusted_peers(public_key: Option<&PublicKey>) -> HashSet { pub fn get_user_config( peers: &UniqueVec, chain_id: Option, - key_pair: Option, + peer_key_pair: Option, + genesis_key_pair: Option, ) -> UserConfig { let chain_id = chain_id.unwrap_or_else(|| ChainId::from("0")); - let (public_key, private_key) = key_pair.unwrap_or_else(KeyPair::random).into_parts(); - iroha_logger::info!(%public_key); + let (peer_public_key, peer_private_key) = + peer_key_pair.unwrap_or_else(KeyPair::random).into_parts(); + iroha_logger::info!(%peer_public_key); + let (genesis_public_key, genesis_private_key) = genesis_key_pair + .unwrap_or_else(KeyPair::random) + .into_parts(); + iroha_logger::info!(%genesis_public_key); let mut config = UserConfig::new(); config.chain_id.set(chain_id); - config.public_key.set(public_key.clone()); - config.private_key.set(private_key.clone()); + config.public_key.set(peer_public_key); + config.private_key.set(peer_private_key); config.network.address.set(DEFAULT_P2P_ADDR); config .chain_wide @@ -89,8 +95,8 @@ pub fn get_user_config( .network .block_gossip_period .set(HumanDuration(Duration::from_millis(500))); - config.genesis.private_key.set(private_key); - config.genesis.public_key.set(public_key); + config.genesis.private_key.set(genesis_private_key); + config.genesis.public_key.set(genesis_public_key); config.genesis.file.set("./genesis.json".into()); // There is no need in persistency in tests // If required to should be set explicitly not to overlap with other existing tests @@ -109,9 +115,10 @@ pub fn get_user_config( pub fn get_config( trusted_peers: &UniqueVec, chain_id: Option, - key_pair: Option, + peer_key_pair: Option, + genesis_key_pair: Option, ) -> Config { - get_user_config(trusted_peers, chain_id, key_pair) + get_user_config(trusted_peers, chain_id, peer_key_pair, genesis_key_pair) .unwrap_partial() .expect("config should build as all required fields were provided") .parse(CliContext { diff --git a/client/Cargo.toml b/client/Cargo.toml index cb1363174e2..f9f316fa0a9 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -56,6 +56,7 @@ iroha_logger = { workspace = true } iroha_telemetry = { workspace = true } iroha_torii_const = { workspace = true } iroha_version = { workspace = true, features = ["http"] } +test_samples = { workspace = true } attohttpc = { version = "0.28.0", default-features = false } eyre = { workspace = true } diff --git a/client/benches/torii.rs b/client/benches/torii.rs index bc3f5117cfe..a8bc5c97ab0 100644 --- a/client/benches/torii.rs +++ b/client/benches/torii.rs @@ -6,13 +6,13 @@ use criterion::{criterion_group, criterion_main, Criterion, Throughput}; use iroha::samples::{construct_executor, get_config}; use iroha_client::{ client::{asset, Client}, - crypto::KeyPair, data_model::prelude::*, }; use iroha_genesis::{GenesisNetwork, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; use iroha_version::Encode; use test_network::{get_chain_id, get_key_pair, Peer as TestPeer, PeerBuilder, TestRuntime}; +use test_samples::gen_account_in; use tokio::runtime::Runtime; const MINIMUM_SUCCESS_REQUEST_RATIO: f32 = 0.9; @@ -24,17 +24,15 @@ fn query_requests(criterion: &mut Criterion) { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let rt = Runtime::test(); let genesis = GenesisNetwork::new( RawGenesisBlockBuilder::default() .domain("wonderland".parse().expect("Valid")) - .account( - "alice".parse().expect("Valid"), - get_key_pair().public_key().clone(), - ) + .account(get_key_pair(test_network::Signatory::Alice).into_parts().0) .finish_domain() .executor_blob( construct_executor("../default_executor").expect("Failed to construct executor"), @@ -60,11 +58,10 @@ fn query_requests(criterion: &mut Criterion) { }); let mut group = criterion.benchmark_group("query-requests"); let domain_id: DomainId = "domain".parse().expect("Valid"); - let create_domain = Register::domain(Domain::new(domain_id.clone())); - let account_id = AccountId::new(domain_id.clone(), "account".parse().expect("Valid")); - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)); - let asset_definition_id = AssetDefinitionId::new(domain_id, "xor".parse().expect("Valid")); + let create_domain = Register::domain(Domain::new(domain_id)); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())); + let asset_definition_id: AssetDefinitionId = "xor#domain".parse().expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); let mint_asset = Mint::asset_numeric( @@ -73,7 +70,7 @@ fn query_requests(criterion: &mut Criterion) { ); let client_config = iroha_client::samples::get_client_config( get_chain_id(), - get_key_pair(), + get_key_pair(test_network::Signatory::Alice), format!("http://{}", peer.api_address).parse().unwrap(), ); @@ -132,15 +129,13 @@ fn instruction_submits(criterion: &mut Criterion) { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let genesis = GenesisNetwork::new( RawGenesisBlockBuilder::default() .domain("wonderland".parse().expect("Valid")) - .account( - "alice".parse().expect("Valid"), - configuration.common.key_pair.public_key().clone(), - ) + .account(configuration.common.key_pair.public_key().clone()) .finish_domain() .executor_blob( construct_executor("../default_executor").expect("Failed to construct executor"), @@ -158,14 +153,13 @@ 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: InstructionBox = Register::domain(Domain::new(domain_id.clone())).into(); - let account_id = AccountId::new(domain_id.clone(), "account".parse().expect("Valid")); - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)).into(); - let asset_definition_id = AssetDefinitionId::new(domain_id, "xor".parse().expect("Valid")); + let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())).into(); + let asset_definition_id: AssetDefinitionId = "xor#domain".parse().expect("Valid"); let client_config = iroha_client::samples::get_client_config( get_chain_id(), - get_key_pair(), + get_key_pair(test_network::Signatory::Alice), format!("http://{}", peer.api_address).parse().unwrap(), ); let iroha_client = Client::new(client_config); diff --git a/client/benches/tps/utils.rs b/client/benches/tps/utils.rs index ca63d75d89b..2295a639c77 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -1,4 +1,4 @@ -use std::{fmt, fs::File, io::BufReader, path::Path, str::FromStr as _, sync::mpsc, thread, time}; +use std::{fmt, fs::File, io::BufReader, path::Path, sync::mpsc, thread, time}; use eyre::{Result, WrapErr}; use iroha_client::{ @@ -8,10 +8,12 @@ use iroha_client::{ prelude::*, }, }; +use iroha_crypto::KeyPair; use iroha_data_model::events::pipeline::{BlockEventFilter, BlockStatus}; use nonzero_ext::nonzero; use serde::Deserialize; use test_network::*; +use test_samples::ALICE_ID; pub type Tps = f64; @@ -68,6 +70,7 @@ impl Config { config: self, client, name, + signatory: KeyPair::random().into_parts().0, }; unit.ready() }) @@ -136,6 +139,7 @@ struct MeasurerUnit { pub config: Config, pub client: Client, pub name: UnitName, + pub signatory: PublicKey, } type UnitName = u32; @@ -146,15 +150,10 @@ impl MeasurerUnit { /// Submit initial transactions for measurement fn ready(self) -> Result { - let keypair = iroha_client::crypto::KeyPair::random(); - - let account_id = account_id(self.name); - let asset_id = asset_id(self.name); - - let register_me = Register::account(Account::new(account_id, keypair.public_key().clone())); + let register_me = Register::account(Account::new(self.account_id())); self.client.submit_blocking(register_me)?; - let mint_a_rose = Mint::asset_numeric(1_u32, asset_id); + let mint_a_rose = Mint::asset_numeric(1_u32, self.asset_id()); self.client.submit_blocking(mint_a_rose)?; Ok(self) @@ -193,7 +192,7 @@ impl MeasurerUnit { let submitter = self.client.clone(); let interval_us_per_tx = self.config.interval_us_per_tx; let instructions = self.instructions(); - let alice_id = AccountId::from_str("alice@wonderland").expect("Failed to parse account id"); + let alice_id = ALICE_ID.clone(); let mut nonce = nonzero!(1_u32); @@ -231,17 +230,14 @@ impl MeasurerUnit { } fn mint(&self) -> InstructionBox { - Mint::asset_numeric(1_u32, asset_id(self.name)).into() + Mint::asset_numeric(1_u32, self.asset_id()).into() } -} -fn asset_id(account_name: UnitName) -> AssetId { - AssetId::new( - "rose#wonderland".parse().expect("Valid"), - account_id(account_name), - ) -} + fn account_id(&self) -> AccountId { + AccountId::new("wonderland".parse().expect("Valid"), self.signatory.clone()) + } -fn account_id(name: UnitName) -> AccountId { - format!("{name}@wonderland").parse().expect("Valid") + fn asset_id(&self) -> AssetId { + AssetId::new("rose#wonderland".parse().expect("Valid"), self.account_id()) + } } diff --git a/client/examples/million_accounts_genesis.rs b/client/examples/million_accounts_genesis.rs index 5a6b1dc6692..a44d682fa1e 100644 --- a/client/examples/million_accounts_genesis.rs +++ b/client/examples/million_accounts_genesis.rs @@ -16,14 +16,11 @@ use tokio::runtime::Runtime; fn generate_genesis(num_domains: u32) -> RawGenesisBlock { let mut builder = RawGenesisBlockBuilder::default(); - let key_pair = get_key_pair(); + let signatory_alice = get_key_pair(test_network::Signatory::Alice).into_parts().0; for i in 0_u32..num_domains { builder = builder .domain(format!("wonderland-{i}").parse().expect("Valid")) - .account( - format!("Alice-{i}").parse().expect("Valid"), - key_pair.public_key().clone(), - ) + .account(signatory_alice.clone()) .asset( format!("xor-{i}").parse().expect("Valid"), AssetValueType::Numeric(NumericSpec::default()), @@ -45,7 +42,8 @@ fn main_genesis() { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let rt = Runtime::test(); let genesis = GenesisNetwork::new( @@ -74,16 +72,9 @@ fn create_million_accounts_directly() { wait_for_genesis_committed(&vec![test_client.clone()], 0); for i in 0_u32..1_000_000_u32 { let domain_id: DomainId = format!("wonderland-{i}").parse().expect("Valid"); - let normal_account_id = AccountId::new( - domain_id.clone(), - format!("bob-{i}").parse().expect("Valid"), - ); + let normal_account_id = AccountId::new(domain_id.clone(), KeyPair::random().into_parts().0); let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); - let create_account = Register::account(Account::new( - normal_account_id.clone(), - KeyPair::random().into_parts().0, - )) - .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/register_1000_triggers.rs b/client/examples/register_1000_triggers.rs index 6de1efd77bc..56ab3c01567 100644 --- a/client/examples/register_1000_triggers.rs +++ b/client/examples/register_1000_triggers.rs @@ -8,8 +8,8 @@ use iroha_data_model::trigger::TriggerId; use iroha_genesis::{GenesisNetwork, RawGenesisBlock, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; use test_network::{ - get_chain_id, wait_for_genesis_committed_with_max_retries, Peer as TestPeer, PeerBuilder, - TestClient, TestRuntime, + get_chain_id, get_key_pair, wait_for_genesis_committed_with_max_retries, Peer as TestPeer, + PeerBuilder, TestClient, TestRuntime, }; use tokio::runtime::Runtime; @@ -55,13 +55,14 @@ fn generate_genesis(num_triggers: u32) -> Result Result<(), Box> { - let mut peer = ::new().expect("Failed to create peer"); + let mut peer: TestPeer = ::new().expect("Failed to create peer"); let chain_id = get_chain_id(); let mut configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(peer.key_pair.clone()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); // Increase executor limits for large genesis diff --git a/client/examples/tutorial.rs b/client/examples/tutorial.rs index f4b36283ac6..0af57992466 100644 --- a/client/examples/tutorial.rs +++ b/client/examples/tutorial.rs @@ -76,13 +76,16 @@ fn domain_registration_test(config: Config) -> Result<(), Error> { fn account_definition_test() -> Result<(), Error> { // #region account_definition_comparison use iroha_client::data_model::prelude::AccountId; + use iroha_crypto::KeyPair; - // Create an `iroha_client::data_model::AccountId` instance - // with a DomainId instance and a Domain ID for an account - let longhand_account_id = AccountId::new("white_rabbit".parse()?, "looking_glass".parse()?); - let account_id: AccountId = "white_rabbit@looking_glass" + // Generate a new public key for a new account + let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by providing a DomainId instance and the public key + let longhand_account_id = AccountId::new("looking_glass".parse()?, public_key.clone()); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // Check that two ways to define an account match assert_eq!(account_id, longhand_account_id); @@ -109,19 +112,17 @@ fn account_registration_test(config: Config) -> Result<(), Error> { let iroha_client = Client::new(config); // #region register_account_create - // Create an AccountId instance by providing the account and domain name - let account_id: AccountId = "white_rabbit@looking_glass" - .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); - // #endregion register_account_create - - // TODO: consider getting a key from white_rabbit // Generate a new public key for a new account let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") + .parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); + // #endregion register_account_create // #region register_account_generate // Generate a new account - let create_account = Register::account(Account::new(account_id, public_key)); + let create_account = Register::account(Account::new(account_id)); // #endregion register_account_generate // #region register_account_prepare_tx @@ -151,6 +152,7 @@ fn asset_registration_test(config: Config) -> Result<(), Error> { numeric, AccountId, AssetDefinition, AssetDefinitionId, AssetId, Mint, Register, }, }; + use iroha_crypto::KeyPair; // #endregion register_asset_crates // Create an Iroha client @@ -171,10 +173,12 @@ fn asset_registration_test(config: Config) -> Result<(), Error> { iroha_client.submit(register_time)?; // #endregion register_asset_init_submit - // Create an account using the previously defined asset - let account_id: AccountId = "white_rabbit@looking_glass" + // Generate a new public key for a new account + let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #region register_asset_mint_submit // Create a MintBox using a previous asset and account @@ -205,8 +209,8 @@ fn asset_minting_test(config: Config) -> Result<(), Error> { // #region mint_asset_define_asset_account let roses = AssetDefinitionId::from_str("rose#wonderland") .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after"); - let alice: AccountId = "alice@wonderland".parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + let alice: AccountId = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #endregion mint_asset_define_asset_account // Mint the Asset instance @@ -222,11 +226,14 @@ fn asset_minting_test(config: Config) -> Result<(), Error> { // #region mint_asset_mint_alt // Mint the Asset instance (alternate syntax). - // The syntax is `asset_name#asset_domain#account_name@account_domain`, + // The syntax is `asset_name#asset_domain#account_signatory@account_domain`, // 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 = Mint::asset_numeric(10u32, "rose##alice@wonderland".parse()?); + let alice_roses: AssetId = + "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + .parse()?; + let mint_roses_alt = Mint::asset_numeric(10u32, alice_roses); // #endregion mint_asset_mint_alt // #region mint_asset_submit_tx_alt @@ -256,8 +263,8 @@ fn asset_burning_test(config: Config) -> Result<(), Error> { // Define the instances of an Asset and Account let roses = AssetDefinitionId::from_str("rose#wonderland") .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after"); - let alice: AccountId = "alice@wonderland".parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + let alice: AccountId = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #endregion burn_asset_define_asset_account // #region burn_asset_burn @@ -273,11 +280,14 @@ fn asset_burning_test(config: Config) -> Result<(), Error> { // #region burn_asset_burn_alt // Burn the Asset instance (alternate syntax). - // The syntax is `asset_name#asset_domain#account_name@account_domain`, + // The syntax is `asset_name#asset_domain#account_signatory@account_domain`, // 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 = Burn::asset_numeric(10u32, "rose##alice@wonderland".parse()?); + let alice_roses: AssetId = + "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + .parse()?; + let burn_roses_alt = Burn::asset_numeric(10u32, alice_roses); // #endregion burn_asset_burn_alt // #region burn_asset_submit_tx_alt diff --git a/client/src/client.rs b/client/src/client.rs index 2a4eed202d7..b725f9592aa 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -1575,6 +1575,7 @@ mod tests { use std::str::FromStr; use iroha_primitives::small::SmallStr; + use test_samples::gen_account_in; use super::*; use crate::config::{BasicAuth, Config, WebLogin}; @@ -1585,12 +1586,11 @@ mod tests { const ENCRYPTED_CREDENTIALS: &str = "bWFkX2hhdHRlcjppbG92ZXRlYQ=="; fn config_factory() -> Config { + let (account_id, key_pair) = gen_account_in("wonderland"); Config { chain_id: ChainId::from("0"), - key_pair: KeyPair::random(), - account_id: "alice@wonderland" - .parse() - .expect("This account ID should be valid"), + key_pair, + account_id, torii_api_url: "http://127.0.0.1:8080".parse().unwrap(), basic_auth: None, transaction_add_nonce: false, diff --git a/client/src/config.rs b/client/src/config.rs index 72bb909d8c7..7babfcc4a5d 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -109,9 +109,9 @@ mod tests { password = "ilovetea" [account] - id = "alice@wonderland" - public_key = "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" - private_key = { algorithm = "ed25519", payload = "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" } + domain_id = "wonderland" + public_key = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" + private_key = { algorithm = "ed25519", payload = "CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" } [transaction] time_to_live = 100_000 diff --git a/client/src/config/user.rs b/client/src/config/user.rs index 30a684e5bac..1ebb11e3a9c 100644 --- a/client/src/config/user.rs +++ b/client/src/config/user.rs @@ -8,7 +8,7 @@ pub use boilerplate::*; use eyre::{eyre, Context, Report}; use iroha_config::base::{Emitter, ErrorsCollection}; use iroha_crypto::{KeyPair, PrivateKey, PublicKey}; -use iroha_data_model::{account::AccountId, ChainId}; +use iroha_data_model::prelude::{AccountId, ChainId, DomainId}; use merge::Merge; use serde_with::DeserializeFromStr; use url::Url; @@ -67,7 +67,7 @@ impl Root { basic_auth, account: Account { - id: account_id, + domian_id, public_key, private_key, }, @@ -93,6 +93,8 @@ impl Root { )) } + let account_id = AccountId::new(domian_id, public_key.clone()); + let key_pair = KeyPair::new(public_key, private_key) .wrap_err("failed to construct a key pair") .map_or_else( @@ -121,7 +123,7 @@ impl Root { #[derive(Debug, Clone)] #[allow(missing_docs)] pub struct Account { - pub id: AccountId, + pub domian_id: DomainId, pub public_key: PublicKey, pub private_key: PrivateKey, } diff --git a/client/src/config/user/boilerplate.rs b/client/src/config/user/boilerplate.rs index 500b13afecb..635f9ebb153 100644 --- a/client/src/config/user/boilerplate.rs +++ b/client/src/config/user/boilerplate.rs @@ -9,7 +9,7 @@ use iroha_config::base::{ UserField, }; use iroha_crypto::{PrivateKey, PublicKey}; -use iroha_data_model::{account::AccountId, ChainId}; +use iroha_data_model::{domain::DomainId, ChainId}; use serde::Deserialize; use crate::config::{ @@ -89,7 +89,7 @@ impl UnwrapPartial for RootPartial { #[derive(Debug, Clone, Deserialize, Eq, PartialEq, Default, Merge)] #[serde(deny_unknown_fields, default)] pub struct AccountPartial { - pub id: UserField, + pub domain_id: UserField, pub public_key: UserField, pub private_key: UserField, } @@ -100,8 +100,8 @@ impl UnwrapPartial for AccountPartial { fn unwrap_partial(self) -> UnwrapPartialResult { let mut emitter = Emitter::new(); - if self.id.is_none() { - emitter.emit_missing_field("account.id"); + if self.domain_id.is_none() { + emitter.emit_missing_field("account.domain_id"); } if self.public_key.is_none() { emitter.emit_missing_field("account.public_key"); @@ -113,7 +113,7 @@ impl UnwrapPartial for AccountPartial { emitter.finish()?; Ok(Account { - id: self.id.get().unwrap(), + domian_id: self.domain_id.get().unwrap(), public_key: self.public_key.get().unwrap(), private_key: self.private_key.get().unwrap(), }) diff --git a/client/src/lib.rs b/client/src/lib.rs index fc518a0cf6a..a551c23adc3 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -27,13 +27,14 @@ pub mod samples { /// Get sample client configuration. pub fn get_client_config(chain_id: ChainId, key_pair: KeyPair, torii_api_url: Url) -> Config { + let account_id = format!("{}@wonderland", key_pair.public_key()) + .parse() + .expect("should be valid"); Config { chain_id, key_pair, torii_api_url, - account_id: "alice@wonderland" - .parse() - .expect("This account ID should be valid"), + account_id, basic_auth: None, transaction_ttl: DEFAULT_TRANSACTION_TIME_TO_LIVE, transaction_status_timeout: DEFAULT_TRANSACTION_STATUS_TIMEOUT, diff --git a/client/tests/integration/add_account.rs b/client/tests/integration/add_account.rs deleted file mode 100644 index 58c5e35e3c9..00000000000 --- a/client/tests/integration/add_account.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::thread; - -use eyre::Result; -use iroha_client::{client, data_model::prelude::*}; -use iroha_config::parameters::actual::Root as Config; -use test_network::*; - -use crate::integration::new_account_with_random_public_key; - -#[test] -// This test suite is also covered at the UI level in the iroha_client_cli tests -// in test_register_accounts.py -fn client_add_account_with_name_length_more_than_limit_should_not_commit_transaction() -> Result<()> -{ - let (_rt, _peer, test_client) = ::new().with_port(10_505).start_with_runtime(); - wait_for_genesis_committed(&vec![test_client.clone()], 0); - - let pipeline_time = Config::pipeline_time(); - - let normal_account_id: AccountId = "bob@wonderland".parse().expect("Valid"); - let create_account = Register::account(new_account_with_random_public_key( - 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 = Register::account(new_account_with_random_public_key( - incorrect_account_id.clone(), - )); - test_client.submit(create_account)?; - - thread::sleep(pipeline_time * 2); - - assert!(test_client - .request(client::account::by_id(normal_account_id)) - .is_ok()); - assert!(test_client - .request(client::account::by_id(incorrect_account_id)) - .is_err()); - - Ok(()) -} diff --git a/client/tests/integration/asset.rs b/client/tests/integration/asset.rs index 34a102afd93..b6ca37c691e 100644 --- a/client/tests/integration/asset.rs +++ b/client/tests/integration/asset.rs @@ -3,7 +3,7 @@ use std::{str::FromStr as _, thread}; use eyre::Result; use iroha_client::{ client::{self, QueryResult}, - crypto::{KeyPair, PublicKey}, + crypto::KeyPair, data_model::prelude::*, }; use iroha_config::parameters::actual::Root as Config; @@ -14,6 +14,7 @@ use iroha_data_model::{ }; use serde_json::json; use test_network::*; +use test_samples::{gen_account_in, ALICE_ID, BOB_ID}; #[test] // This test is also covered at the UI level in the iroha_client_cli tests @@ -23,7 +24,7 @@ fn client_register_asset_should_add_asset_once_but_not_twice() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("test_asset#wonderland").expect("Valid"); let create_asset: InstructionBox = @@ -59,7 +60,7 @@ fn unregister_asset_should_remove_asset_from_account() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("test_asset#wonderland").expect("Valid"); let asset_id = AssetId::new(asset_definition_id.clone(), account_id.clone()); @@ -101,7 +102,7 @@ fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount() -> wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); @@ -132,7 +133,7 @@ fn client_add_big_asset_quantity_to_existing_asset_should_increase_asset_amount( wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); @@ -163,7 +164,7 @@ fn client_add_asset_with_decimal_should_increase_asset_amount() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let asset_definition = AssetDefinition::numeric(asset_definition_id.clone()); let create_asset = Register::asset_definition(asset_definition); @@ -262,135 +263,95 @@ fn find_rate_and_make_exchange_isi_should_succeed() { let (_rt, _peer, test_client) = ::new().with_port(10_675).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid."); - let seller_id: AccountId = "seller@company".parse().expect("Valid."); - let buyer_id: AccountId = "buyer@company".parse().expect("Valid."); - - let seller_btc: AssetId = "btc#crypto#seller@company".parse().expect("Valid."); - let buyer_eth: AssetId = "eth#crypto#buyer@company".parse().expect("Valid."); - - let seller_keypair = KeyPair::random(); - let buyer_keypair = KeyPair::random(); - - let register_account = |account_id: AccountId, signature: PublicKey| { - Register::account(Account::new(account_id, signature)) - }; + let (dex_id, _dex_keypair) = gen_account_in("exchange"); + let (seller_id, seller_keypair) = gen_account_in("company"); + let (buyer_id, buyer_keypair) = gen_account_in("company"); + let rate: AssetId = format!("btc/eth##{}", &dex_id) + .parse() + .expect("should be valid"); + let seller_btc: AssetId = format!("btc#crypto#{}", &seller_id) + .parse() + .expect("should be valid"); + let buyer_eth: AssetId = format!("eth#crypto#{}", &buyer_id) + .parse() + .expect("should be valid"); + let instructions: [InstructionBox; 12] = [ + register::domain("exchange").into(), + register::domain("company").into(), + register::domain("crypto").into(), + register::account(dex_id.clone()).into(), + register::account(seller_id.clone()).into(), + register::account(buyer_id.clone()).into(), + register::asset_definition_numeric("btc/eth#exchange").into(), + register::asset_definition_numeric("btc#crypto").into(), + register::asset_definition_numeric("eth#crypto").into(), + Mint::asset_numeric(20_u32, rate.clone()).into(), + Mint::asset_numeric(10_u32, seller_btc.clone()).into(), + Mint::asset_numeric(200_u32, buyer_eth.clone()).into(), + ]; + test_client + .submit_all_blocking(instructions) + .expect("transaction should be committed"); - let grant_alice_asset_transfer_permission = |asset_id: AssetId, owner_keypair: KeyPair| { - let allow_alice_to_transfer_asset = Grant::permission( + let alice_id = ALICE_ID.clone(); + let alice_can_transfer_asset = |asset_id: AssetId, owner_key_pair: KeyPair| { + let instruction = Grant::permission( PermissionToken::new( "CanTransferUserAsset".parse().unwrap(), &json!({ "asset_id": asset_id }), ), alice_id.clone(), ); - - let chain_id = ChainId::from("0"); - let grant_asset_transfer_tx = - TransactionBuilder::new(chain_id, asset_id.account_id().clone()) - .with_instructions([allow_alice_to_transfer_asset]) - .sign(&owner_keypair); + let transaction = + TransactionBuilder::new(ChainId::from("0"), asset_id.account_id().clone()) + .with_instructions([instruction]) + .sign(&owner_key_pair); test_client - .submit_transaction_blocking(&grant_asset_transfer_tx) - .expect(&format!( - "Failed to grant permission alice to transfer {asset_id}", - )); + .submit_transaction_blocking(&transaction) + .expect("transaction should be committed"); }; - - let buyer_account_id = account_id_new("buyer", "company"); - let seller_account_id = account_id_new("seller", "company"); - let asset_id = asset_id_new( - "btc2eth_rate", - "exchange", - account_id_new("dex", "exchange"), - ); - let instructions: [InstructionBox; 12] = [ - register::domain("exchange").into(), - register::domain("company").into(), - register::domain("crypto").into(), - register_account(seller_id, seller_keypair.public_key().clone()).into(), - register_account(buyer_id, buyer_keypair.public_key().clone()).into(), - register::account("dex", "exchange").into(), - register::asset_definition("btc", "crypto").into(), - register::asset_definition("eth", "crypto").into(), - register::asset_definition("btc2eth_rate", "exchange").into(), - Mint::asset_numeric( - 200u32, - asset_id_new("eth", "crypto", buyer_account_id.clone()), - ) - .into(), - Mint::asset_numeric( - 20u32, - asset_id_new("btc", "crypto", seller_account_id.clone()), - ) - .into(), - Mint::asset_numeric(20u32, asset_id.clone()).into(), - ]; - test_client - .submit_all_blocking(instructions) - .expect("Failed to prepare accounts."); - - grant_alice_asset_transfer_permission(seller_btc, seller_keypair); - grant_alice_asset_transfer_permission(buyer_eth, buyer_keypair); - - let to_transfer = test_client - .request(FindAssetQuantityById::new(asset_id)) - .expect("Failed to execute query to find asset quantity by id."); + alice_can_transfer_asset(seller_btc.clone(), seller_keypair); + alice_can_transfer_asset(buyer_eth.clone(), buyer_keypair); + + let assert_balance = |asset_id: AssetId, expected: Numeric| { + let got = test_client + .request(FindAssetQuantityById::new(asset_id)) + .expect("query should succeed"); + assert_eq!(got, expected); + }; + // before: seller has $BTC10 and buyer has $ETH200 + assert_balance(seller_btc.clone(), numeric!(10)); + assert_balance(buyer_eth.clone(), numeric!(200)); + + let rate: u32 = test_client + .request(FindAssetQuantityById::new(rate)) + .expect("query should succeed") + .try_into() + .expect("numeric should be u32 originally"); test_client .submit_all_blocking([ - Transfer::asset_numeric( - asset_id_new("btc", "crypto", seller_account_id.clone()), - to_transfer, - buyer_account_id.clone(), - ), - Transfer::asset_numeric( - asset_id_new("eth", "crypto", buyer_account_id), - to_transfer, - seller_account_id, - ), + Transfer::asset_numeric(seller_btc.clone(), 10_u32, buyer_id.clone()), + Transfer::asset_numeric(buyer_eth.clone(), 10_u32 * rate, seller_id.clone()), ]) - .expect("Failed to exchange eth for btc."); - - let expected_seller_eth = numeric!(20); - let expected_buyer_eth = numeric!(180); - let expected_buyer_btc = numeric!(20); - - let eth_quantity = test_client - .request(FindAssetQuantityById::new(asset_id_new( - "eth", - "crypto", - account_id_new("seller", "company"), - ))) - .expect("Failed to execute Iroha Query"); - assert_eq!(expected_seller_eth, eth_quantity); - - // For the btc amount we expect an error, as zero assets are purged from accounts - test_client - .request(FindAssetQuantityById::new(asset_id_new( - "btc", - "crypto", - account_id_new("seller", "company"), - ))) - .expect_err("Query must fail"); + .expect("transaction should be committed"); - let buyer_eth_quantity = test_client - .request(FindAssetQuantityById::new(asset_id_new( - "eth", - "crypto", - account_id_new("buyer", "company"), - ))) - .expect("Failed to execute Iroha Query"); - assert_eq!(expected_buyer_eth, buyer_eth_quantity); - - let buyer_btc_quantity = test_client - .request(FindAssetQuantityById::new(asset_id_new( - "btc", - "crypto", - account_id_new("buyer", "company"), - ))) - .expect("Failed to execute Iroha Query"); - assert_eq!(expected_buyer_btc, buyer_btc_quantity); + let assert_purged = |asset_id: AssetId| { + let _err = test_client + .request(FindAssetQuantityById::new(asset_id)) + .expect_err("query should fail, as zero assets are purged from accounts"); + }; + let seller_eth: AssetId = format!("eth#crypto#{}", &seller_id) + .parse() + .expect("should be valid"); + let buyer_btc: AssetId = format!("btc#crypto#{}", &buyer_id) + .parse() + .expect("should be valid"); + // after: seller has $ETH200 and buyer has $BTC10 + assert_purged(seller_btc); + assert_purged(buyer_eth); + assert_balance(seller_eth, numeric!(200)); + assert_balance(buyer_btc, numeric!(10)); } #[test] @@ -398,8 +359,8 @@ fn transfer_asset_definition() { let (_rt, _peer, test_client) = ::new().with_port(11_060).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid."); - let bob_id: AccountId = "bob@wonderland".parse().expect("Valid."); + let alice_id = ALICE_ID.clone(); + let bob_id = BOB_ID.clone(); let asset_definition_id: AssetDefinitionId = "asset#wonderland".parse().expect("Valid"); test_client @@ -432,8 +393,8 @@ fn fail_if_dont_satisfy_spec() { let (_rt, _peer, test_client) = ::new().with_port(11_125).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid."); - let bob_id: AccountId = "bob@wonderland".parse().expect("Valid."); + let alice_id = ALICE_ID.clone(); + let bob_id = BOB_ID.clone(); let asset_definition_id: AssetDefinitionId = "asset#wonderland".parse().expect("Valid"); let asset_id: AssetId = AssetId::new(asset_definition_id.clone(), alice_id.clone()); // Create asset definition which accepts only integers @@ -490,46 +451,20 @@ fn fail_if_dont_satisfy_spec() { } } -fn account_id_new(account_name: &str, account_domain: &str) -> AccountId { - AccountId::new( - account_domain.parse().expect("Valid"), - account_name.parse().expect("Valid"), - ) -} - -pub fn asset_id_new( - definition_name: &str, - definition_domain: &str, - account_id: AccountId, -) -> AssetId { - AssetId::new( - AssetDefinitionId::new( - definition_domain.parse().expect("Valid"), - definition_name.parse().expect("Valid"), - ), - account_id, - ) -} - mod register { use super::*; - use crate::integration::new_account_with_random_public_key; - pub fn domain(name: &str) -> Register { - Register::domain(Domain::new(DomainId::from_str(name).expect("Valid"))) + pub fn domain(id: &str) -> Register { + Register::domain(Domain::new(id.parse().expect("should parse to DomainId"))) } - pub fn account(account_name: &str, domain_name: &str) -> Register { - Register::account(new_account_with_random_public_key(AccountId::new( - domain_name.parse().expect("Valid"), - account_name.parse().expect("Valid"), - ))) + pub fn account(id: AccountId) -> Register { + Register::account(Account::new(id)) } - pub fn asset_definition(asset_name: &str, domain_name: &str) -> Register { - Register::asset_definition(AssetDefinition::numeric(AssetDefinitionId::new( - domain_name.parse().expect("Valid"), - asset_name.parse().expect("Valid"), - ))) + pub fn asset_definition_numeric(id: &str) -> Register { + Register::asset_definition(AssetDefinition::numeric( + id.parse().expect("should parse to AssetDefinitionId"), + )) } } diff --git a/client/tests/integration/asset_propagation.rs b/client/tests/integration/asset_propagation.rs index 48129d2f3e1..84396976c85 100644 --- a/client/tests/integration/asset_propagation.rs +++ b/client/tests/integration/asset_propagation.rs @@ -3,7 +3,6 @@ use std::{str::FromStr as _, thread}; use eyre::Result; use iroha_client::{ client::{self, QueryResult}, - crypto::KeyPair, data_model::{ parameter::{default::MAX_TRANSACTIONS_IN_BLOCK, ParametersBuilder}, prelude::*, @@ -11,6 +10,7 @@ use iroha_client::{ }; use iroha_config::parameters::actual::Root as Config; use test_network::*; +use test_samples::gen_account_in; #[test] // This test is also covered at the UI level in the iroha_client_cli tests @@ -30,9 +30,8 @@ fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount_on_a 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::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)).into(); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())).into(); let asset_definition_id = AssetDefinitionId::from_str("xor#domain")?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())).into(); diff --git a/client/tests/integration/burn_public_keys.rs b/client/tests/integration/burn_public_keys.rs deleted file mode 100644 index 8d245051aea..00000000000 --- a/client/tests/integration/burn_public_keys.rs +++ /dev/null @@ -1,110 +0,0 @@ -use iroha_client::{ - client::{account, transaction, Client}, - crypto::{HashOf, KeyPair, PublicKey}, - data_model::{isi::Instruction, prelude::*}, -}; -use iroha_data_model::query::TransactionQueryOutput; -use test_network::*; - -fn submit( - client: &Client, - instructions: impl IntoIterator, - submitter: Option<(AccountId, KeyPair)>, -) -> ( - HashOf, - eyre::Result>, -) { - let chain_id = ChainId::from("0"); - - let tx = if let Some((account_id, keypair)) = submitter { - TransactionBuilder::new(chain_id, account_id) - .with_instructions(instructions) - .sign(&keypair) - } else { - let tx = client.build_transaction(instructions, UnlimitedMetadata::default()); - client.sign_transaction(tx) - }; - - (tx.hash(), client.submit_transaction_blocking(&tx)) -} - -fn get(client: &Client, hash: HashOf) -> TransactionQueryOutput { - client.request(transaction::by_hash(hash)).unwrap() -} - -fn account_keys_count(client: &Client, account_id: AccountId) -> usize { - let account = client.request(account::by_id(account_id)).unwrap(); - let signatories = account.signatories(); - signatories.len() -} - -#[test] -fn public_keys_cannot_be_burned_to_nothing() { - const KEYS_COUNT: usize = 3; - let charlie_id: AccountId = "charlie@wonderland".parse().expect("Valid"); - let charlie_keys_count = |client: &Client| account_keys_count(client, charlie_id.clone()); - - let (_rt, _peer, client) = ::new().with_port(10_045).start_with_runtime(); - wait_for_genesis_committed(&vec![client.clone()], 0); - - let charlie_initial_keypair = KeyPair::random(); - let register_charlie = Register::account(Account::new( - charlie_id.clone(), - charlie_initial_keypair.public_key().clone(), - )); - - let (tx_hash, res) = submit(&client, [register_charlie], None); - res.unwrap(); - get(&client, tx_hash); - let mut keys_count = charlie_keys_count(&client); - assert_eq!(keys_count, 1); - - let mint_keys = (0..KEYS_COUNT - 1).map(|_| { - let (public_key, _) = KeyPair::random().into_parts(); - Mint::account_public_key(public_key, charlie_id.clone()) - }); - - let (tx_hash, res) = submit( - &client, - mint_keys, - Some((charlie_id.clone(), charlie_initial_keypair.clone())), - ); - res.unwrap(); - get(&client, tx_hash); - keys_count = charlie_keys_count(&client); - assert_eq!(keys_count, KEYS_COUNT); - - let charlie = client.request(account::by_id(charlie_id.clone())).unwrap(); - let mut keys = charlie.signatories(); - let burn = - |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()) - .cloned() - .map(burn); - - let (tx_hash, res) = submit( - &client, - burn_keys_leaving_one, - Some((charlie_id.clone(), charlie_initial_keypair.clone())), - ); - res.unwrap(); - let committed_txn = get(&client, tx_hash); - keys_count = charlie_keys_count(&client); - assert_eq!(keys_count, 1); - assert!(committed_txn.as_ref().error.is_none()); - - let burn_the_last_key = burn(charlie_initial_keypair.public_key().clone()); - - let (tx_hash, res) = submit( - &client, - std::iter::once(burn_the_last_key), - Some((charlie_id.clone(), charlie_initial_keypair)), - ); - assert!(res.is_err()); - let committed_txn = get(&client, tx_hash); - keys_count = charlie_keys_count(&client); - assert_eq!(keys_count, 1); - assert!(committed_txn.as_ref().error.is_some()); -} diff --git a/client/tests/integration/domain_owner_permissions.rs b/client/tests/integration/domain_owner_permissions.rs index af78eff12ac..0e64d8667c0 100644 --- a/client/tests/integration/domain_owner_permissions.rs +++ b/client/tests/integration/domain_owner_permissions.rs @@ -1,13 +1,9 @@ use eyre::Result; -use iroha_client::{ - crypto::KeyPair, - data_model::{account::SignatureCheckCondition, prelude::*}, -}; +use iroha_client::data_model::prelude::*; use iroha_data_model::transaction::error::TransactionRejectionReason; use serde_json::json; use test_network::*; - -use super::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID, BOB_ID}; #[test] fn domain_owner_domain_permissions() -> Result<()> { @@ -17,7 +13,7 @@ fn domain_owner_domain_permissions() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); let kingdom_id: DomainId = "kingdom".parse()?; - let bob_id: AccountId = "bob@kingdom".parse()?; + let (bob_id, bob_keypair) = gen_account_in("kingdom"); let coin_id: AssetDefinitionId = "coin#kingdom".parse()?; let coin = AssetDefinition::numeric(coin_id.clone()); @@ -25,8 +21,7 @@ fn domain_owner_domain_permissions() -> Result<()> { let kingdom = Domain::new(kingdom_id.clone()); test_client.submit_blocking(Register::domain(kingdom))?; - let bob_keypair = KeyPair::random(); - let bob = Account::new(bob_id.clone(), bob_keypair.public_key().clone()); + let bob = Account::new(bob_id.clone()); test_client.submit_blocking(Register::account(bob))?; // Asset definitions can't be registered by "bob@kingdom" by default @@ -88,36 +83,15 @@ fn domain_owner_account_permissions() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); let kingdom_id: DomainId = "kingdom".parse()?; - let mad_hatter_id: AccountId = "mad_hatter@kingdom".parse()?; + let (mad_hatter_id, _mad_hatter_keypair) = gen_account_in("kingdom"); // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id); test_client.submit_blocking(Register::domain(kingdom))?; - let mad_hatter_keypair = KeyPair::random(); - let mad_hatter = Account::new( - mad_hatter_id.clone(), - mad_hatter_keypair.public_key().clone(), - ); + let mad_hatter = Account::new(mad_hatter_id.clone()); 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::random(); - test_client.submit_blocking(Mint::account_public_key( - mad_hatter_new_keypair.public_key().clone(), - mad_hatter_id.clone(), - ))?; - 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(Mint::account_signature_check_condition( - SignatureCheckCondition::AnyAccountSignatureOr(Vec::new().into()), - mad_hatter_id.clone(), - ))?; - // 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()?; @@ -129,7 +103,7 @@ fn domain_owner_account_permissions() -> Result<()> { 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()?; + let bob_id = BOB_ID.clone(); let token = PermissionToken::new( "CanUnregisterAccount".parse().unwrap(), &json!({ "account_id": mad_hatter_id }), @@ -150,19 +124,18 @@ fn domain_owner_asset_definition_permissions() -> Result<()> { let chain_id = ChainId::from("0"); let kingdom_id: DomainId = "kingdom".parse()?; - let bob_id: AccountId = "bob@kingdom".parse()?; - let rabbit_id: AccountId = "rabbit@kingdom".parse()?; + let (bob_id, bob_keypair) = gen_account_in("kingdom"); + let (rabbit_id, _rabbit_keypair) = gen_account_in("kingdom"); let coin_id: AssetDefinitionId = "coin#kingdom".parse()?; // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id.clone()); test_client.submit_blocking(Register::domain(kingdom))?; - let bob_keypair = KeyPair::random(); - let bob = Account::new(bob_id.clone(), bob_keypair.public_key().clone()); + let bob = Account::new(bob_id.clone()); test_client.submit_blocking(Register::account(bob))?; - let rabbit = new_account_with_random_public_key(rabbit_id.clone()); + let rabbit = Account::new(rabbit_id.clone()); test_client.submit_blocking(Register::account(rabbit))?; // Grant permission to register asset definitions to "bob@kingdom" @@ -181,7 +154,7 @@ fn domain_owner_asset_definition_permissions() -> Result<()> { // check that "alice@wonderland" as owner of domain can transfer asset definitions in her domain test_client.submit_blocking(Transfer::asset_definition( - bob_id, + bob_id.clone(), coin_id.clone(), rabbit_id, ))?; @@ -197,7 +170,6 @@ fn domain_owner_asset_definition_permissions() -> Result<()> { 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()?; let token = PermissionToken::new( "CanUnregisterAssetDefinition".parse().unwrap(), &json!({ "asset_definition_id": coin_id }), @@ -218,9 +190,9 @@ fn domain_owner_asset_permissions() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(11_090).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let alice_id: AccountId = "alice@wonderland".parse()?; + let alice_id = ALICE_ID.clone(); let kingdom_id: DomainId = "kingdom".parse()?; - let bob_id: AccountId = "bob@kingdom".parse()?; + let (bob_id, bob_keypair) = gen_account_in("kingdom"); let coin_id: AssetDefinitionId = "coin#kingdom".parse()?; let store_id: AssetDefinitionId = "store#kingdom".parse()?; @@ -228,8 +200,7 @@ fn domain_owner_asset_permissions() -> Result<()> { let kingdom = Domain::new(kingdom_id.clone()); test_client.submit_blocking(Register::domain(kingdom))?; - let bob_keypair = KeyPair::random(); - let bob = Account::new(bob_id.clone(), bob_keypair.public_key().clone()); + let bob = Account::new(bob_id.clone()); test_client.submit_blocking(Register::account(bob))?; // Grant permission to register asset definitions to "bob@kingdom" @@ -264,12 +235,11 @@ fn domain_owner_asset_permissions() -> Result<()> { // 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); + let bob_store_id = AssetId::new(store_id, bob_id.clone()); 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()?; let token = PermissionToken::new( "CanUnregisterUserAsset".parse().unwrap(), &json!({ "asset_id": bob_store_id }), @@ -285,16 +255,15 @@ fn domain_owner_trigger_permissions() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(11_095).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let alice_id: AccountId = "alice@wonderland".parse()?; + let alice_id = ALICE_ID.clone(); let kingdom_id: DomainId = "kingdom".parse()?; - let bob_id: AccountId = "bob@kingdom".parse()?; + let (bob_id, _bob_keypair) = gen_account_in("kingdom"); // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id); test_client.submit_blocking(Register::domain(kingdom))?; - let bob_keypair = KeyPair::random(); - let bob = Account::new(bob_id.clone(), bob_keypair.public_key().clone()); + let bob = Account::new(bob_id.clone()); test_client.submit_blocking(Register::account(bob))?; let asset_definition_id = "rose#wonderland".parse()?; @@ -307,7 +276,7 @@ fn domain_owner_trigger_permissions() -> Result<()> { Action::new( trigger_instructions, Repeats::from(2_u32), - bob_id, + bob_id.clone(), ExecuteTriggerEventFilter::new().for_trigger(trigger_id.clone()), ), )); @@ -322,7 +291,6 @@ fn domain_owner_trigger_permissions() -> Result<()> { let _result = test_client.submit_blocking(execute_trigger)?; // check that "alice@wonderland" as owner of domain can grant and revoke trigger related permission tokens in her domain - let bob_id: AccountId = "bob@wonderland".parse()?; let token = PermissionToken::new( "CanUnregisterUserTrigger".parse().unwrap(), &json!({ "trigger_id": trigger_id }), @@ -342,16 +310,15 @@ fn domain_owner_transfer() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(11_100).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let alice_id: AccountId = "alice@wonderland".parse()?; + let alice_id = ALICE_ID.clone(); let kingdom_id: DomainId = "kingdom".parse()?; - let bob_id: AccountId = "bob@kingdom".parse()?; + let (bob_id, _bob_keypair) = gen_account_in("kingdom"); // "alice@wonderland" is owner of "kingdom" domain let kingdom = Domain::new(kingdom_id.clone()); test_client.submit_blocking(Register::domain(kingdom))?; - let bob_keypair = KeyPair::random(); - let bob = Account::new(bob_id.clone(), bob_keypair.public_key().clone()); + let bob = Account::new(bob_id.clone()); test_client.submit_blocking(Register::account(bob))?; let domain = test_client.request(FindDomainById::new(kingdom_id.clone()))?; @@ -365,8 +332,8 @@ fn domain_owner_transfer() -> Result<()> { )) .expect("Failed to submit transaction"); - let asset_definition = test_client.request(FindDomainById::new(kingdom_id))?; - assert_eq!(asset_definition.owned_by(), &bob_id); + let domain = test_client.request(FindDomainById::new(kingdom_id))?; + assert_eq!(domain.owned_by(), &bob_id); Ok(()) } diff --git a/client/tests/integration/events/data.rs b/client/tests/integration/events/data.rs index d77e303c9c9..1c6fdcfe0b8 100644 --- a/client/tests/integration/events/data.rs +++ b/client/tests/integration/events/data.rs @@ -5,6 +5,7 @@ use iroha_client::data_model::{prelude::*, transaction::WasmSmartContract}; use parity_scale_codec::Encode as _; use serde_json::json; use test_network::*; +use test_samples::{ALICE_ID, BOB_ID}; /// Return string containing exported memory, dummy allocator, and /// host function imports which you can embed into your wasm module. @@ -196,7 +197,7 @@ fn produce_multiple_events() -> Result<()> { init_receiver.recv()?; // Registering role - let alice_id = AccountId::from_str("alice@wonderland")?; + let alice_id = ALICE_ID.clone(); let role_id = RoleId::from_str("TEST_ROLE")?; let token_1 = PermissionToken::new( "CanRemoveKeyValueInAccount".parse()?, @@ -213,7 +214,7 @@ fn produce_multiple_events() -> Result<()> { client.submit_all_blocking(instructions)?; // Grants role to Bob - let bob_id = AccountId::from_str("bob@wonderland")?; + let bob_id = BOB_ID.clone(); let grant_role = Grant::role(role_id.clone(), bob_id.clone()); client.submit_blocking(grant_role)?; diff --git a/client/tests/integration/events/notification.rs b/client/tests/integration/events/notification.rs index c060d1e1e64..093fd213bc3 100644 --- a/client/tests/integration/events/notification.rs +++ b/client/tests/integration/events/notification.rs @@ -3,6 +3,7 @@ use std::{str::FromStr as _, sync::mpsc, thread, time::Duration}; use eyre::{eyre, Result, WrapErr}; use iroha_client::data_model::prelude::*; use test_network::*; +use test_samples::ALICE_ID; #[test] fn trigger_completion_success_should_produce_event() -> Result<()> { @@ -10,7 +11,7 @@ fn trigger_completion_success_should_produce_event() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id: AccountId = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id); let trigger_id = TriggerId::from_str("mint_rose")?; @@ -55,7 +56,7 @@ fn trigger_completion_failure_should_produce_event() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(11_055).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let account_id: AccountId = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let trigger_id = TriggerId::from_str("fail_box")?; let instruction = Fail::new("Fail box".to_owned()); diff --git a/client/tests/integration/extra_functional/multiple_blocks_created.rs b/client/tests/integration/extra_functional/multiple_blocks_created.rs index ee229fa1dfd..0c96b849824 100644 --- a/client/tests/integration/extra_functional/multiple_blocks_created.rs +++ b/client/tests/integration/extra_functional/multiple_blocks_created.rs @@ -3,7 +3,6 @@ use std::thread; use eyre::Result; use iroha_client::{ client::{self, Client, QueryResult}, - crypto::KeyPair, data_model::{ parameter::{default::MAX_TRANSACTIONS_IN_BLOCK, ParametersBuilder}, prelude::*, @@ -11,6 +10,7 @@ use iroha_client::{ }; use iroha_config::parameters::actual::Root as Config; use test_network::*; +use test_samples::gen_account_in; const N_BLOCKS: usize = 510; @@ -29,9 +29,8 @@ fn long_multiple_blocks_created() -> Result<()> { )?; let create_domain: InstructionBox = Register::domain(Domain::new("domain".parse()?)).into(); - let account_id: AccountId = "account@domain".parse()?; - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)).into(); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())).into(); let asset_definition_id: AssetDefinitionId = "xor#domain".parse()?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())).into(); diff --git a/client/tests/integration/extra_functional/offline_peers.rs b/client/tests/integration/extra_functional/offline_peers.rs index 988b0271acb..3ca231902fc 100644 --- a/client/tests/integration/extra_functional/offline_peers.rs +++ b/client/tests/integration/extra_functional/offline_peers.rs @@ -10,6 +10,7 @@ use iroha_config::parameters::actual::Root as Config; use iroha_crypto::KeyPair; use iroha_primitives::addr::socket_addr; use test_network::*; +use test_samples::ALICE_ID; use tokio::runtime::Runtime; #[test] @@ -25,7 +26,7 @@ fn genesis_block_is_committed_with_some_offline_peers() -> Result<()> { wait_for_genesis_committed(&network.clients(), 1); //When - let alice_id: AccountId = "alice@wonderland".parse()?; + let alice_id = ALICE_ID.clone(); let roses = "rose#wonderland".parse()?; let alice_has_roses = numeric!(13); diff --git a/client/tests/integration/extra_functional/restart_peer.rs b/client/tests/integration/extra_functional/restart_peer.rs index 3172e6d4492..5b1995ad2d2 100644 --- a/client/tests/integration/extra_functional/restart_peer.rs +++ b/client/tests/integration/extra_functional/restart_peer.rs @@ -8,11 +8,12 @@ use iroha_client::{ use iroha_config::parameters::actual::Root as Config; use rand::{seq::SliceRandom, thread_rng, Rng}; use test_network::*; +use test_samples::ALICE_ID; use tokio::runtime::Runtime; #[test] fn restarted_peer_should_have_the_same_asset_amount() -> Result<()> { - let account_id = AccountId::from_str("alice@wonderland").unwrap(); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").unwrap(); let quantity = numeric!(200); diff --git a/client/tests/integration/extra_functional/unregister_peer.rs b/client/tests/integration/extra_functional/unregister_peer.rs index f653e07890f..5742025cc01 100644 --- a/client/tests/integration/extra_functional/unregister_peer.rs +++ b/client/tests/integration/extra_functional/unregister_peer.rs @@ -3,7 +3,6 @@ use std::thread; use eyre::Result; use iroha_client::{ client::{self, QueryResult}, - crypto::KeyPair, data_model::{ parameter::{default::MAX_TRANSACTIONS_IN_BLOCK, ParametersBuilder}, prelude::*, @@ -11,6 +10,7 @@ use iroha_client::{ }; use iroha_config::parameters::actual::Root as Config; use test_network::*; +use test_samples::gen_account_in; // Note the test is marked as `unstable`, not the network. #[ignore = "ignore, more in #2851"] @@ -121,9 +121,8 @@ fn init() -> Result<( .add_parameter(MAX_TRANSACTIONS_IN_BLOCK, 1u32)? .into_set_parameters(); let create_domain = Register::domain(Domain::new("domain".parse()?)); - let account_id: AccountId = "account@domain".parse()?; - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())); let asset_definition_id: AssetDefinitionId = "xor#domain".parse()?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); diff --git a/client/tests/integration/extra_functional/unstable_network.rs b/client/tests/integration/extra_functional/unstable_network.rs index 836f263f45a..52b2b9ad851 100644 --- a/client/tests/integration/extra_functional/unstable_network.rs +++ b/client/tests/integration/extra_functional/unstable_network.rs @@ -8,6 +8,7 @@ use iroha_client::{ use iroha_config::parameters::actual::Root as Config; use rand::seq::SliceRandom; use test_network::*; +use test_samples::ALICE_ID; use tokio::runtime::Runtime; const MAX_TRANSACTIONS_IN_BLOCK: u32 = 5; @@ -75,7 +76,7 @@ fn unstable_network( let pipeline_time = Config::pipeline_time(); - let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id: AssetDefinitionId = "camomile#wonderland".parse().expect("Valid"); let register_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); diff --git a/client/tests/integration/mod.rs b/client/tests/integration/mod.rs index 98b17659895..37299969665 100644 --- a/client/tests/integration/mod.rs +++ b/client/tests/integration/mod.rs @@ -1,16 +1,9 @@ -use iroha_crypto::KeyPair; -use iroha_data_model::account::{Account, AccountId, NewAccount}; - -mod add_account; mod add_domain; mod asset; mod asset_propagation; -mod burn_public_keys; mod domain_owner_permissions; mod events; mod extra_functional; -mod multisignature_account; -mod multisignature_transaction; mod non_mintable; mod pagination; mod permissions; @@ -25,7 +18,3 @@ mod tx_chain_id; mod tx_history; mod tx_rollback; mod upgrade; - -fn new_account_with_random_public_key(account_id: AccountId) -> NewAccount { - Account::new(account_id, KeyPair::random().into_parts().0) -} diff --git a/client/tests/integration/multisignature_account.rs b/client/tests/integration/multisignature_account.rs deleted file mode 100644 index bdb290f0bc4..00000000000 --- a/client/tests/integration/multisignature_account.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::thread; - -use eyre::Result; -use iroha_client::{ - client::{self, Client, QueryResult}, - crypto::KeyPair, - data_model::prelude::*, -}; -use iroha_config::parameters::actual::Root as Config; -use test_network::*; - -#[test] -fn transaction_signed_by_new_signatory_of_account_should_pass() -> Result<()> { - let (_rt, peer, client) = ::new().with_port(10_605).start_with_runtime(); - wait_for_genesis_committed(&[client.clone()], 0); - let pipeline_time = Config::pipeline_time(); - - // Given - let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); - let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); - let create_asset = - Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); - let key_pair = KeyPair::random(); - 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 = numeric!(200); - let mint_asset = Mint::asset_numeric( - quantity, - AssetId::new(asset_definition_id.clone(), account_id.clone()), - ); - Client::test_with_key(&peer.api_address, key_pair).submit_till( - mint_asset, - client::asset::by_account_id(account_id), - |result| { - let assets = result.collect::>>().expect("Valid"); - - assets.iter().any(|asset| { - asset.id().definition_id == asset_definition_id - && *asset.value() == AssetValue::Numeric(quantity) - }) - }, - )?; - Ok(()) -} diff --git a/client/tests/integration/multisignature_transaction.rs b/client/tests/integration/multisignature_transaction.rs deleted file mode 100644 index d319c4178f7..00000000000 --- a/client/tests/integration/multisignature_transaction.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::{str::FromStr as _, thread}; - -use eyre::Result; -use iroha_client::{ - client, - client::{Client, QueryResult}, - config::Config as ClientConfig, - crypto::KeyPair, - data_model::{ - parameter::{default::MAX_TRANSACTIONS_IN_BLOCK, ParametersBuilder}, - prelude::*, - }, -}; -use iroha_config::parameters::actual::Root as Config; -use test_network::*; - -#[allow(clippy::too_many_lines)] -#[test] -fn multisignature_transactions_should_be_accepted_after_fully_signed() -> Result<()> { - let (_rt, network, client) = Network::start_test_with_runtime(4, Some(10_945)); - wait_for_genesis_committed(&network.clients(), 0); - let pipeline_time = Config::pipeline_time(); - - client.submit_all_blocking( - ParametersBuilder::new() - .add_parameter(MAX_TRANSACTIONS_IN_BLOCK, 1u32)? - .into_set_parameters(), - )?; - - let alice_id = AccountId::from_str("alice@wonderland")?; - let alice_key_pair = get_key_pair(); - let key_pair_2 = KeyPair::random(); - let asset_definition_id = AssetDefinitionId::from_str("camomile#wonderland")?; - let create_asset = - Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); - let set_signature_condition = Mint::account_signature_check_condition( - SignatureCheckCondition::AllAccountSignaturesAnd( - vec![key_pair_2.public_key().clone()].into(), - ), - alice_id.clone(), - ); - - let mut client_config = ClientConfig::test(&network.genesis.api_address); - let client = Client::new(client_config.clone()); - let instructions: [InstructionBox; 2] = [create_asset.into(), set_signature_condition.into()]; - client.submit_all_blocking(instructions)?; - - //When - let quantity = numeric!(200); - let asset_id = AssetId::new(asset_definition_id, alice_id.clone()); - let mint_asset = Mint::asset_numeric(quantity, asset_id.clone()); - - client_config.account_id = alice_id.clone(); - client_config.key_pair = alice_key_pair; - let client = Client::new(client_config.clone()); - let instructions = [mint_asset.clone()]; - let transaction = client.build_transaction(instructions, UnlimitedMetadata::new()); - // The tx signed by the first account - let _ = client - .submit_transaction(&client.sign_transaction(transaction.clone())) - .expect_err("Transaction should not be added into the queue"); - - thread::sleep(pipeline_time); - - //Then - client_config.torii_api_url = format!( - "http://{}", - &network.peers.values().last().unwrap().api_address, - ) - .parse() - .unwrap(); - let client_1 = Client::new(client_config.clone()); - let request = client::asset::by_account_id(alice_id); - let assets = client_1 - .request(request.clone())? - .collect::>>()?; - assert_eq!( - assets.len(), - 2, // Alice has roses and cabbage from Genesis, but doesn't yet have camomile - "Multisignature transaction was committed before all required signatures were added" - ); - - client_config.key_pair = key_pair_2; - let client_2 = Client::new(client_config); - // The tx signed by the second account - client_2.submit_transaction(&client_2.sign_transaction(transaction))?; - - thread::sleep(pipeline_time); - - let assets = client_1 - .request(request)? - .collect::>>()?; - assert!(!assets.is_empty()); - let camomile_asset = assets - .iter() - .find(|asset| *asset.id() == asset_id) - .expect("Failed to find expected asset"); - assert_eq!(AssetValue::Numeric(quantity), *camomile_asset.value()); - - Ok(()) -} diff --git a/client/tests/integration/non_mintable.rs b/client/tests/integration/non_mintable.rs index e9652f29390..301ecb93b81 100644 --- a/client/tests/integration/non_mintable.rs +++ b/client/tests/integration/non_mintable.rs @@ -7,6 +7,7 @@ use iroha_client::{ }; use iroha_data_model::isi::InstructionBox; use test_network::*; +use test_samples::ALICE_ID; #[test] fn non_mintable_asset_can_be_minted_once_but_not_twice() -> Result<()> { @@ -14,7 +15,7 @@ fn non_mintable_asset_can_be_minted_once_but_not_twice() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = Register::asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).mintable_once(), @@ -62,7 +63,7 @@ fn non_mintable_asset_cannot_be_minted_if_registered_with_non_zero_value() -> Re wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset: InstructionBox = Register::asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).mintable_once(), @@ -99,7 +100,7 @@ fn non_mintable_asset_can_be_minted_if_registered_with_zero_value() -> Result<() wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = Register::asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).mintable_once(), diff --git a/client/tests/integration/pagination.rs b/client/tests/integration/pagination.rs index c8b4360ed28..176e3726e2e 100644 --- a/client/tests/integration/pagination.rs +++ b/client/tests/integration/pagination.rs @@ -16,12 +16,12 @@ fn limits_should_work() -> Result<()> { let vec = &client .build_query(asset::all_definitions()) .with_pagination(Pagination { - limit: Some(nonzero!(5_u32)), - start: Some(nonzero!(5_u64)), + limit: Some(nonzero!(7_u32)), + start: Some(nonzero!(1_u64)), }) .execute()? .collect::>>()?; - assert_eq!(vec.len(), 5); + assert_eq!(vec.len(), 7); Ok(()) } @@ -35,17 +35,18 @@ fn fetch_size_should_work() -> Result<()> { let iter = client .build_query(asset::all_definitions()) .with_pagination(Pagination { - limit: Some(nonzero!(20_u32)), - start: None, + limit: Some(nonzero!(7_u32)), + start: Some(nonzero!(1_u64)), }) - .with_fetch_size(FetchSize::new(Some(nonzero!(12_u32)))) + .with_fetch_size(FetchSize::new(Some(nonzero!(3_u32)))) .execute()?; - assert_eq!(iter.batch_len(), 12); + assert_eq!(iter.batch_len(), 3); Ok(()) } fn register_assets(client: &Client) -> Result<()> { - let register: Vec = ('a'..='z') + // FIXME transaction is rejected for more than a certain number of instructions + let register: Vec = ('a'..='j') .map(|c| c.to_string()) .map(|name| (name + "#wonderland").parse().expect("Valid")) .map(|asset_definition_id| { diff --git a/client/tests/integration/permissions.rs b/client/tests/integration/permissions.rs index 16a4c85140a..475066db77d 100644 --- a/client/tests/integration/permissions.rs +++ b/client/tests/integration/permissions.rs @@ -12,6 +12,7 @@ use iroha_data_model::{ use iroha_genesis::GenesisNetwork; use serde_json::json; use test_network::{PeerBuilder, *}; +use test_samples::{gen_account_in, ALICE_ID, BOB_ID}; #[test] fn genesis_transactions_are_validated() { @@ -22,7 +23,7 @@ fn genesis_transactions_are_validated() { let genesis = GenesisNetwork::test_with_instructions([Grant::permission( PermissionToken::new("InvalidToken".parse().unwrap(), &json!(null)), - AccountId::from_str("alice@wonderland").unwrap(), + ALICE_ID.clone(), ) .into()]); @@ -71,9 +72,9 @@ fn permissions_disallow_asset_transfer() { wait_for_genesis_committed(&[iroha_client.clone()], 0); // Given - let alice_id = "alice@wonderland".parse().expect("Valid"); - let bob_id: AccountId = "bob@wonderland".parse().expect("Valid"); - let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); + let alice_id = ALICE_ID.clone(); + let bob_id = BOB_ID.clone(); + let (mouse_id, _mouse_keypair) = gen_account_in("wonderland"); let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); @@ -124,9 +125,9 @@ fn permissions_disallow_asset_burn() { let (_rt, _peer, iroha_client) = ::new().with_port(10_735).start_with_runtime(); - let alice_id = "alice@wonderland".parse().expect("Valid"); - let bob_id: AccountId = "bob@wonderland".parse().expect("Valid"); - let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); + let alice_id = ALICE_ID.clone(); + let bob_id = BOB_ID.clone(); + let (mouse_id, _mouse_keypair) = gen_account_in("wonderland"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); @@ -197,14 +198,13 @@ fn permissions_differ_not_only_by_names() { let (_rt, _not_drop, client) = ::new().with_port(10_745).start_with_runtime(); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); - let mouse_id: AccountId = "mouse@outfit".parse().expect("Valid"); - let mouse_keypair = KeyPair::random(); + let alice_id = ALICE_ID.clone(); + let (mouse_id, mouse_keypair) = gen_account_in("outfit"); // Registering mouse let outfit_domain: DomainId = "outfit".parse().unwrap(); let create_outfit_domain = Register::domain(Domain::new(outfit_domain.clone())); - let new_mouse_account = Account::new(mouse_id.clone(), mouse_keypair.public_key().clone()); + let new_mouse_account = Account::new(mouse_id.clone()); client .submit_all_blocking([ InstructionBox::from(create_outfit_domain), @@ -296,15 +296,14 @@ fn stored_vs_granted_token_payload() -> Result<()> { wait_for_genesis_committed(&[iroha_client.clone()], 0); // Given - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let alice_id = ALICE_ID.clone(); // Registering mouse and asset definition let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::store(asset_definition_id.clone())); - let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); - let mouse_keypair = KeyPair::random(); - let new_mouse_account = Account::new(mouse_id.clone(), mouse_keypair.public_key().clone()); + let (mouse_id, mouse_keypair) = gen_account_in("wonderland"); + let new_mouse_account = Account::new(mouse_id.clone()); let instructions: [InstructionBox; 2] = [ Register::account(new_mouse_account).into(), create_asset.into(), @@ -319,7 +318,7 @@ fn stored_vs_granted_token_payload() -> Result<()> { PermissionToken::from_str_unchecked( "CanSetKeyValueInUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"xor#wonderland#mouse@wonderland\" }", + &*format!(r###"{{ "asset_id" : "xor#wonderland#{mouse_id}" }}"###), ), alice_id, ); @@ -347,13 +346,13 @@ fn permission_tokens_are_unified() { wait_for_genesis_committed(&[iroha_client.clone()], 0); // Given - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let alice_id = ALICE_ID.clone(); let allow_alice_to_transfer_rose_1 = Grant::permission( PermissionToken::from_str_unchecked( "CanTransferUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"rose#wonderland#alice@wonderland\" }", + &*format!(r###"{{ "asset_id" : "rose#wonderland#{alice_id}" }}"###), ), alice_id.clone(), ); @@ -362,7 +361,7 @@ fn permission_tokens_are_unified() { PermissionToken::from_str_unchecked( "CanTransferUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"rose##alice@wonderland\" }", + &*format!(r###"{{ "asset_id" : "rose##{alice_id}" }}"###), ), alice_id, ); @@ -381,7 +380,7 @@ fn associated_permission_tokens_removed_on_unregister() { let (_rt, _peer, iroha_client) = ::new().with_port(11_240).start_with_runtime(); wait_for_genesis_committed(&[iroha_client.clone()], 0); - let bob_id: AccountId = "bob@wonderland".parse().expect("Valid"); + let bob_id = BOB_ID.clone(); let kingdom_id: DomainId = "kingdom".parse().expect("Valid"); let kingdom = Domain::new(kingdom_id.clone()); diff --git a/client/tests/integration/queries/account.rs b/client/tests/integration/queries/account.rs index d8137dbc704..eb047c732cc 100644 --- a/client/tests/integration/queries/account.rs +++ b/client/tests/integration/queries/account.rs @@ -6,8 +6,7 @@ use iroha_client::{ data_model::prelude::*, }; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID}; #[test] fn find_accounts_with_asset() -> Result<()> { @@ -30,11 +29,11 @@ fn find_accounts_with_asset() -> Result<()> { )); let accounts: [AccountId; 5] = [ - "alice@wonderland".parse().expect("Valid"), - "mad_hatter@wonderland".parse().expect("Valid"), - "cheshire_cat@wonderland".parse().expect("Valid"), - "caterpillar@wonderland".parse().expect("Valid"), - "white_rabbit@wonderland".parse().expect("Valid"), + ALICE_ID.clone(), + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, ]; // Registering accounts @@ -42,7 +41,7 @@ fn find_accounts_with_asset() -> Result<()> { .iter() .skip(1) // Alice has already been registered in genesis .cloned() - .map(|account_id| Register::account(new_account_with_random_public_key(account_id))) + .map(|account_id| Register::account(Account::new(account_id))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; diff --git a/client/tests/integration/queries/asset.rs b/client/tests/integration/queries/asset.rs index 5ef6115078c..68ce747732f 100644 --- a/client/tests/integration/queries/asset.rs +++ b/client/tests/integration/queries/asset.rs @@ -1,7 +1,6 @@ use eyre::Result; use iroha_client::{ client::{Client, ClientQueryError}, - crypto::KeyPair, data_model::{ asset::AssetValue, isi::Instruction, @@ -10,6 +9,7 @@ use iroha_client::{ }, }; use test_network::*; +use test_samples::{gen_account_in, ALICE_ID}; #[test] #[allow(clippy::too_many_lines)] @@ -23,24 +23,19 @@ fn find_asset_total_quantity() -> Result<()> { test_client.submit_blocking(Register::domain(domain))?; let accounts: [AccountId; 5] = [ - "alice@wonderland".parse()?, - "mad_hatter@wonderland".parse()?, - "cheshire_cat@wonderland".parse()?, - "caterpillar@wonderland".parse()?, - "white_rabbit@looking_glass".parse()?, + ALICE_ID.clone(), + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("looking_glass").0, ]; - let keys = core::iter::repeat_with(KeyPair::random) - .take(accounts.len() - 1) - .collect::>(); - // Registering accounts let register_accounts = accounts .iter() .skip(1) // Alice has already been registered in genesis .cloned() - .zip(keys.iter().map(KeyPair::public_key).cloned()) - .map(|(account_id, public_key)| Register::account(Account::new(account_id, public_key))) + .map(|account_id| Register::account(Account::new(account_id))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; diff --git a/client/tests/integration/queries/query_errors.rs b/client/tests/integration/queries/query_errors.rs index 9a27cb7740c..5d8194fc043 100644 --- a/client/tests/integration/queries/query_errors.rs +++ b/client/tests/integration/queries/query_errors.rs @@ -1,5 +1,3 @@ -use std::str::FromStr; - use iroha_client::{ client::{self, ClientQueryError}, data_model::{ @@ -7,6 +5,7 @@ use iroha_client::{ query::error::{FindError, QueryExecutionFail}, }, }; +use test_samples::gen_account_in; #[test] fn non_existent_account_is_specific_error() { @@ -16,9 +15,7 @@ fn non_existent_account_is_specific_error() { // we cannot wait for genesis committment let err = client - .request(client::account::by_id( - AccountId::from_str("john_doe@regalia").unwrap(), - )) + .request(client::account::by_id(gen_account_in("regalia").0)) .expect_err("Should error"); match err { diff --git a/client/tests/integration/queries/role.rs b/client/tests/integration/queries/role.rs index d437b6f6926..76de3ef1681 100644 --- a/client/tests/integration/queries/role.rs +++ b/client/tests/integration/queries/role.rs @@ -7,6 +7,7 @@ use iroha_client::{ }; use serde_json::json; use test_network::*; +use test_samples::ALICE_ID; fn create_role_ids() -> [RoleId; 5] { [ @@ -123,7 +124,7 @@ fn find_roles_by_account_id() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); let role_ids = create_role_ids(); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let alice_id = ALICE_ID.clone(); // Registering roles let register_roles = role_ids diff --git a/client/tests/integration/roles.rs b/client/tests/integration/roles.rs index 3a3bcd7aff6..5199da7d77f 100644 --- a/client/tests/integration/roles.rs +++ b/client/tests/integration/roles.rs @@ -3,14 +3,12 @@ use std::str::FromStr as _; use eyre::Result; use iroha_client::{ client::{self, QueryResult}, - crypto::KeyPair, data_model::prelude::*, }; use iroha_data_model::transaction::error::TransactionRejectionReason; use serde_json::json; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID}; #[test] fn register_empty_role() -> Result<()> { @@ -54,15 +52,11 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_700).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let alice_id = AccountId::from_str("alice@wonderland")?; - let mouse_id = AccountId::from_str("mouse@wonderland")?; + let alice_id = ALICE_ID.clone(); + let (mouse_id, mouse_keypair) = gen_account_in("wonderland"); // Registering Mouse - let mouse_key_pair = KeyPair::random(); - let register_mouse = Register::account(Account::new( - mouse_id.clone(), - mouse_key_pair.public_key().clone(), - )); + let register_mouse = Register::account(Account::new(mouse_id.clone())); test_client.submit_blocking(register_mouse)?; // Registering role @@ -83,7 +77,7 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { let grant_role = Grant::role(role_id.clone(), alice_id.clone()); let grant_role_tx = TransactionBuilder::new(chain_id, mouse_id.clone()) .with_instructions([grant_role]) - .sign(&mouse_key_pair); + .sign(&mouse_keypair); test_client.submit_transaction_blocking(&grant_role_tx)?; // Alice modifies Mouse's metadata @@ -109,11 +103,11 @@ fn unregistered_role_removed_from_account() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let role_id: RoleId = "root".parse().expect("Valid"); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); - let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); + let alice_id = ALICE_ID.clone(); + let (mouse_id, _mouse_keypair) = gen_account_in("wonderland"); // Registering Mouse - let register_mouse = Register::account(new_account_with_random_public_key(mouse_id.clone())); + let register_mouse = Register::account(Account::new(mouse_id.clone())); test_client.submit_blocking(register_mouse)?; // Register root role @@ -154,7 +148,9 @@ fn role_with_invalid_permissions_is_not_accepted() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let role_id = RoleId::from_str("ACCESS_TO_ACCOUNT_METADATA")?; - let rose_asset_id = AssetId::from_str("rose##alice@wonderland")?; + let rose_asset_id: AssetId = format!("rose##{}", ALICE_ID.clone()) + .parse() + .expect("should be valid"); let role = Role::new(role_id).add_permission(PermissionToken::new( "CanSetKeyValueInAccount".parse()?, &json!({ "account_id": rose_asset_id }), @@ -185,13 +181,13 @@ fn role_permissions_unified() { let allow_alice_to_transfer_rose_1 = PermissionToken::from_str_unchecked( "CanTransferUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"rose#wonderland#alice@wonderland\" }", + "{ \"asset_id\" : \"rose#wonderland#ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland\" }", ); let allow_alice_to_transfer_rose_2 = PermissionToken::from_str_unchecked( "CanTransferUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"rose##alice@wonderland\" }", + "{ \"asset_id\" : \"rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland\" }", ); let role_id: RoleId = "role_id".parse().expect("Valid"); @@ -222,15 +218,11 @@ fn grant_revoke_role_permissions() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(11_245).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let alice_id = AccountId::from_str("alice@wonderland")?; - let mouse_id = AccountId::from_str("mouse@wonderland")?; + let alice_id = ALICE_ID.clone(); + let (mouse_id, mouse_keypair) = gen_account_in("wonderland"); // Registering Mouse - let mouse_key_pair = KeyPair::random(); - let register_mouse = Register::account(Account::new( - mouse_id.clone(), - mouse_key_pair.public_key().clone(), - )); + let register_mouse = Register::account(Account::new(mouse_id.clone())); test_client.submit_blocking(register_mouse)?; // Registering role @@ -248,7 +240,7 @@ fn grant_revoke_role_permissions() -> Result<()> { let grant_role = Grant::role(role_id.clone(), alice_id.clone()); let grant_role_tx = TransactionBuilder::new(chain_id.clone(), mouse_id.clone()) .with_instructions([grant_role]) - .sign(&mouse_key_pair); + .sign(&mouse_keypair); test_client.submit_transaction_blocking(&grant_role_tx)?; let set_key_value = SetKeyValue::account( @@ -275,7 +267,7 @@ fn grant_revoke_role_permissions() -> Result<()> { // Alice can modify Mouse's metadata after permission token is granted to role let grant_role_permission_tx = TransactionBuilder::new(chain_id.clone(), mouse_id.clone()) .with_instructions([grant_role_permission]) - .sign(&mouse_key_pair); + .sign(&mouse_keypair); test_client.submit_transaction_blocking(&grant_role_permission_tx)?; let found_permissions = test_client .request(FindPermissionTokensByAccountId::new(alice_id.clone()))? @@ -286,7 +278,7 @@ fn grant_revoke_role_permissions() -> Result<()> { // Alice can't modify Mouse's metadata after permission token is removed from role let revoke_role_permission_tx = TransactionBuilder::new(chain_id.clone(), mouse_id.clone()) .with_instructions([revoke_role_permission]) - .sign(&mouse_key_pair); + .sign(&mouse_keypair); test_client.submit_transaction_blocking(&revoke_role_permission_tx)?; let found_permissions = test_client .request(FindPermissionTokensByAccountId::new(alice_id.clone()))? 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 1f62efe9689..4ae58d430b7 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 @@ -38,7 +38,7 @@ fn main(_id: TriggerId, _owner: AccountId, _event: EventBox) { let mut metadata = Metadata::new(); let name = format!( "nft_for_{}_in_{}", - account.id().name(), + account.id().signatory(), account.id().domain_id() ) .parse() @@ -78,7 +78,7 @@ fn generate_new_nft_id(account_id: &AccountId) -> AssetDefinitionId { format!( "nft_number_{}_for_{}#{}", new_number, - account_id.name(), + account_id.signatory(), account_id.domain_id() ) .parse() 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 d861b6e13fd..ebbbe476a2a 100644 --- a/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs +++ b/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs @@ -1,5 +1,5 @@ -//! Runtime Executor which allows any instruction executed by `admin@admin` account. -//! If authority is not `admin@admin` then default validation is used as a backup. +//! Runtime Executor which allows any instruction executed by [admin](crate::integration::upgrade::ADMIN_ID) account. +//! If authority is not admin then default validation is used as a backup. #![no_std] @@ -22,7 +22,8 @@ struct Executor { } fn visit_instruction(executor: &mut Executor, authority: &AccountId, isi: &InstructionBox) { - if parse!("admin@admin" as AccountId) == *authority { + let admin_id = "ed012076E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC@admin"; // equals to integration::upgrade::ADMIN_ID + if *authority == parse!(AccountId, admin_id) { execute!(executor, isi); } 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 1b04091ace8..0aaa7907707 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 @@ -28,7 +28,7 @@ pub fn migrate(_block_height: u64) -> MigrationResult { // Performing side-effects to check in the test that it won't be applied after failure // Registering a new domain (using ISI) - let domain_id = parse!("failed_migration_test_domain" as DomainId); + let domain_id = parse!(DomainId, "failed_migration_test_domain"); Register::domain(Domain::new(domain_id)) .execute() .map_err(|error| { 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 dacc5b9789c..74ee7b4377d 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 @@ -46,7 +46,7 @@ fn main(owner: AccountId) { SetKeyValue::account( owner, - parse!("cursor" as Name), + parse!(Name, "cursor"), MetadataValueBox::String( serde_json::to_value(&asset_cursor.cursor) .dbg_expect("Failed to convert cursor to JSON") diff --git a/client/tests/integration/sorting.rs b/client/tests/integration/sorting.rs index bddfcd7ee39..f7651f382c0 100644 --- a/client/tests/integration/sorting.rs +++ b/client/tests/integration/sorting.rs @@ -12,121 +12,128 @@ use iroha_client::{ }, }, }; -use iroha_data_model::isi::InstructionBox; +use iroha_crypto::KeyPair; use nonzero_ext::nonzero; +use rand::{seq::SliceRandom, thread_rng}; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::ALICE_ID; #[test] +#[allow(clippy::cast_possible_truncation)] fn correct_pagination_assets_after_creating_new_one() { - let (_rt, _peer, test_client) = ::new().with_port(10_635).start_with_runtime(); - + // FIXME transaction is rejected for more than a certain number of instructions + const N_ASSETS: usize = 12; + // 0 < pagination.start < missing_idx < pagination.end < N_ASSETS + let missing_indices = vec![N_ASSETS / 2]; + let pagination = Pagination { + limit: Some(nonzero!(N_ASSETS as u32 / 3)), + start: Some(nonzero!(N_ASSETS as u64 / 3)), + }; + let xor_filter = PredicateBox::new(value::QueryOutputPredicate::Identifiable( + string::StringPredicate::starts_with("xor"), + )); let sort_by_metadata_key = Name::from_str("sort").expect("Valid"); + let sorting = Sorting::by_metadata_key(sort_by_metadata_key.clone()); + let account_id = ALICE_ID.clone(); - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let (_rt, _peer, test_client) = ::new().with_port(10_635).start_with_runtime(); + wait_for_genesis_committed(&[test_client.clone()], 0); - let mut assets = vec![]; - let mut instructions = vec![]; + let mut tester_assets = vec![]; + let mut register_asset_definitions = vec![]; + let mut register_assets = vec![]; - for i in 0..20_u32 { + let mut missing_tester_assets = vec![]; + let mut missing_register_asset_definitions = vec![]; + let mut missing_register_assets = vec![]; + + for i in 0..N_ASSETS { let asset_definition_id = AssetDefinitionId::from_str(&format!("xor{i}#wonderland")).expect("Valid"); let asset_definition = AssetDefinition::store(asset_definition_id.clone()); let mut asset_metadata = Metadata::new(); asset_metadata - .insert_with_limits(sort_by_metadata_key.clone(), i, MetadataLimits::new(10, 23)) + .insert_with_limits( + sort_by_metadata_key.clone(), + i as u32, + MetadataLimits::new(10, 23), + ) .expect("Valid"); let asset = Asset::new( AssetId::new(asset_definition_id, account_id.clone()), AssetValue::Store(asset_metadata), ); - assets.push(asset.clone()); - - 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); + if missing_indices.contains(&i) { + missing_tester_assets.push(asset.clone()); + missing_register_asset_definitions.push(Register::asset_definition(asset_definition)); + missing_register_assets.push(Register::asset(asset)); + } else { + tester_assets.push(asset.clone()); + register_asset_definitions.push(Register::asset_definition(asset_definition)); + register_assets.push(Register::asset(asset)); + } } + register_asset_definitions.shuffle(&mut thread_rng()); + register_assets.shuffle(&mut thread_rng()); test_client - .submit_all_blocking(instructions) + .submit_all_blocking(register_asset_definitions) + .expect("Valid"); + test_client + .submit_all_blocking(register_assets) .expect("Valid"); - let sorting = Sorting::by_metadata_key(sort_by_metadata_key.clone()); - - let res = test_client - .build_query(client::asset::by_account_id(account_id.clone())) - .with_pagination(Pagination { - limit: Some(nonzero!(5_u32)), - start: None, - }) + let queried_assets = test_client + .build_query(client::asset::all()) + .with_filter(xor_filter.clone()) + .with_pagination(pagination) .with_sorting(sorting.clone()) .execute() .expect("Valid") .collect::>>() .expect("Valid"); - assert!(res + tester_assets .iter() - .map(|asset| &asset.id().definition_id.name) - .eq(assets - .iter() - .take(5) - .map(|asset| &asset.id().definition_id.name))); - - let new_asset_definition_id = AssetDefinitionId::from_str("xor20#wonderland").expect("Valid"); - let new_asset_definition = AssetDefinition::store(new_asset_definition_id.clone()); - let mut new_asset_metadata = Metadata::new(); - new_asset_metadata - .insert_with_limits( - sort_by_metadata_key, - numeric!(20), - MetadataLimits::new(10, 23), - ) - .expect("Valid"); - let new_asset = Asset::new( - AssetId::new(new_asset_definition_id, account_id.clone()), - AssetValue::Store(new_asset_metadata), - ); - - let create_asset_definition: InstructionBox = - Register::asset_definition(new_asset_definition).into(); - let create_asset = Register::asset(new_asset.clone()).into(); + .skip(N_ASSETS / 3) + .take(N_ASSETS / 3) + .zip(queried_assets) + .for_each(|(tester, queried)| assert_eq!(*tester, queried)); + for (i, missing_idx) in missing_indices.into_iter().enumerate() { + tester_assets.insert(missing_idx, missing_tester_assets[i].clone()); + } + test_client + .submit_all_blocking(missing_register_asset_definitions) + .expect("Valid"); test_client - .submit_all_blocking([create_asset_definition, create_asset]) + .submit_all_blocking(missing_register_assets) .expect("Valid"); - let res = test_client - .build_query(client::asset::by_account_id(account_id)) - .with_pagination(Pagination { - limit: Some(nonzero!(13_u32)), - start: Some(nonzero!(8_u64)), - }) + let queried_assets = test_client + .build_query(client::asset::all()) + .with_filter(xor_filter) + .with_pagination(pagination) .with_sorting(sorting) .execute() .expect("Valid") .collect::>>() .expect("Valid"); - assert!(res + tester_assets .iter() - .map(|asset| &asset.id().definition_id.name) - .eq(assets - .iter() - .skip(8) - .chain(core::iter::once(&new_asset)) - .map(|asset| &asset.id().definition_id.name))); + .skip(N_ASSETS / 3) + .take(N_ASSETS / 3) + .zip(queried_assets) + .for_each(|(tester, queried)| assert_eq!(*tester, queried)); } #[test] #[allow(clippy::too_many_lines)] fn correct_sorting_of_entities() { let (_rt, _peer, test_client) = ::new().with_port(10_640).start_with_runtime(); + wait_for_genesis_committed(&[test_client.clone()], 0); let sort_by_metadata_key = Name::from_str("test_sort").expect("Valid"); @@ -183,13 +190,23 @@ fn correct_sorting_of_entities() { // Test sorting accounts + let domain_name = "_neverland"; + let domain_id: DomainId = domain_name.parse().unwrap(); + test_client + .submit_blocking(Register::domain(Domain::new(domain_id.clone()))) + .expect("should be committed"); + let mut accounts = vec![]; let mut metadata_of_accounts = vec![]; let mut instructions = vec![]; let n = 10u32; + let mut public_keys = (0..n) + .map(|_| KeyPair::random().into_parts().0) + .collect::>(); + public_keys.sort_unstable(); for i in 0..n { - let account_id = AccountId::from_str(&format!("charlie{i}@wonderland")).expect("Valid"); + let account_id = AccountId::new(domain_id.clone(), public_keys[i as usize].clone()); let mut account_metadata = Metadata::new(); account_metadata .insert_with_limits( @@ -198,8 +215,7 @@ fn correct_sorting_of_entities() { MetadataLimits::new(10, 28), ) .expect("Valid"); - let account = new_account_with_random_public_key(account_id.clone()) - .with_metadata(account_metadata.clone()); + let account = Account::new(account_id.clone()).with_metadata(account_metadata.clone()); accounts.push(account_id); metadata_of_accounts.push(account_metadata); @@ -216,8 +232,8 @@ fn correct_sorting_of_entities() { .build_query(client::account::all()) .with_sorting(Sorting::by_metadata_key(sort_by_metadata_key.clone())) .with_filter(PredicateBox::new( - value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with( - "charlie", + value::QueryOutputPredicate::Identifiable(string::StringPredicate::ends_with( + domain_name, )), )) .execute() @@ -328,7 +344,15 @@ fn correct_sorting_of_entities() { #[test] fn sort_only_elements_which_have_sorting_key() -> Result<()> { + const TEST_DOMAIN: &str = "neverland"; + let (_rt, _peer, test_client) = ::new().with_port(10_680).start_with_runtime(); + wait_for_genesis_committed(&[test_client.clone()], 0); + + let domain_id: DomainId = TEST_DOMAIN.parse().unwrap(); + test_client + .submit_blocking(Register::domain(Domain::new(domain_id.clone()))) + .expect("should be committed"); let sort_by_metadata_key = Name::from_str("test_sort").expect("Valid"); @@ -341,10 +365,14 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { skip_set.insert(7); let n = 10u32; + let mut public_keys = (0..n) + .map(|_| KeyPair::random().into_parts().0) + .collect::>(); + public_keys.sort_unstable(); for i in 0..n { - let account_id = AccountId::from_str(&format!("charlie{i}@wonderland")).expect("Valid"); + let account_id = AccountId::new(domain_id.clone(), public_keys[i as usize].clone()); let account = if skip_set.contains(&i) { - let account = new_account_with_random_public_key(account_id.clone()); + let account = Account::new(account_id.clone()); accounts_b.push(account_id); account } else { @@ -356,8 +384,7 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { MetadataLimits::new(10, 28), ) .expect("Valid"); - let account = new_account_with_random_public_key(account_id.clone()) - .with_metadata(account_metadata); + let account = Account::new(account_id.clone()).with_metadata(account_metadata); accounts_a.push(account_id); account }; @@ -374,8 +401,8 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { .build_query(client::account::all()) .with_sorting(Sorting::by_metadata_key(sort_by_metadata_key)) .with_filter(PredicateBox::new( - value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with( - "charlie", + value::QueryOutputPredicate::Identifiable(string::StringPredicate::ends_with( + TEST_DOMAIN, )), )) .execute() diff --git a/client/tests/integration/status_response.rs b/client/tests/integration/status_response.rs index a0bf6c87d97..8209d46b5dd 100644 --- a/client/tests/integration/status_response.rs +++ b/client/tests/integration/status_response.rs @@ -4,6 +4,7 @@ use eyre::Result; use iroha_client::{data_model::prelude::*, samples::get_status_json}; use iroha_telemetry::metrics::Status; use test_network::*; +use test_samples::gen_account_in; fn status_eq_excluding_uptime_and_queue(lhs: &Status, rhs: &Status) -> bool { lhs.peers == rhs.peers @@ -29,8 +30,7 @@ fn json_and_scale_statuses_equality() -> Result<()> { let coins = ["xor", "btc", "eth", "doge"]; - let domain_id: DomainId = "test_domain".parse().expect("Should be valid"); - let account_id = AccountId::new(domain_id, "test_account".parse().expect("Should be valid")); + let (account_id, _account_keypair) = gen_account_in("domain"); for coin in coins { let asset_definition_id = AssetDefinitionId::from_str(&format!("{coin}#wonderland"))?; diff --git a/client/tests/integration/transfer_asset.rs b/client/tests/integration/transfer_asset.rs index 31c2750068f..f58d479e49c 100644 --- a/client/tests/integration/transfer_asset.rs +++ b/client/tests/integration/transfer_asset.rs @@ -2,7 +2,6 @@ use std::str::FromStr; use iroha_client::{ client::{self, QueryResult}, - crypto::KeyPair, data_model::{isi::Instruction, prelude::*, Registered}, }; use iroha_data_model::{ @@ -12,6 +11,7 @@ use iroha_data_model::{ name::Name, }; use test_network::*; +use test_samples::{gen_account_in, ALICE_ID}; #[test] // This test suite is also covered at the UI level in the iroha_client_cli tests @@ -135,12 +135,11 @@ fn simulate_transfer( } fn generate_two_ids() -> (AccountId, AccountId) { - let alice_id: AccountId = "alice@wonderland".parse().unwrap(); - let mouse_id: AccountId = "mouse@wonderland".parse().unwrap(); + let alice_id = ALICE_ID.clone(); + let (mouse_id, _mouse_keypair) = gen_account_in("wonderland"); (alice_id, mouse_id) } fn create_mouse(mouse_id: AccountId) -> Register { - let (mouse_public_key, _) = KeyPair::random().into_parts(); - Register::account(Account::new(mouse_id, mouse_public_key)) + Register::account(Account::new(mouse_id)) } diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index 37ccf66d12e..4c93ebacdc4 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -12,6 +12,7 @@ use iroha_client::{ use iroha_genesis::GenesisNetwork; use iroha_logger::info; use test_network::*; +use test_samples::ALICE_ID; const TRIGGER_NAME: &str = "mint_rose"; @@ -21,7 +22,7 @@ fn call_execute_trigger() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id); let prev_value = get_asset_value(&mut test_client, asset_id.clone()); @@ -45,7 +46,7 @@ fn execute_trigger_should_produce_event() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id: AccountId = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let instruction = Mint::asset_numeric(1u32, asset_id.clone()); @@ -81,7 +82,7 @@ fn infinite_recursion_should_produce_one_call_per_block() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id); let trigger_id = TriggerId::from_str(TRIGGER_NAME)?; let call_trigger = ExecuteTrigger::new(trigger_id); @@ -108,7 +109,7 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); // Registering trigger that should fail on execution @@ -164,7 +165,7 @@ fn trigger_should_not_be_executed_with_zero_repeats_count() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id = TriggerId::from_str("self_modifying_trigger")?; @@ -224,7 +225,7 @@ fn trigger_should_be_able_to_modify_its_own_repeats_count() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id = TriggerId::from_str("self_modifying_trigger")?; @@ -270,7 +271,7 @@ fn unregister_trigger() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_035).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); // Registering trigger let trigger_id = TriggerId::from_str("empty_trigger")?; @@ -346,7 +347,7 @@ fn trigger_in_genesis_using_base64() -> Result<()> { let engine = base64::engine::general_purpose::STANDARD; let wasm_base64 = serde_json::json!(base64::engine::Engine::encode(&engine, wasm)).to_string(); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let trigger_id = TriggerId::from_str("genesis_trigger")?; let trigger = Trigger::new( @@ -399,7 +400,7 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id_unregister = TriggerId::from_str("unregister_other_trigger")?; let trigger_id_to_be_unregistered = TriggerId::from_str("should_be_unregistered_trigger")?; @@ -459,7 +460,7 @@ fn trigger_burn_repetitions() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id = TriggerId::from_str("trigger")?; @@ -494,7 +495,7 @@ fn unregistering_one_of_two_triggers_with_identical_wasm_should_not_cause_origin let (_rt, _peer, test_client) = ::new().with_port(11_105).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let first_trigger_id = TriggerId::from_str("mint_rose_1")?; let second_trigger_id = TriggerId::from_str("mint_rose_2")?; diff --git a/client/tests/integration/triggers/data_trigger.rs b/client/tests/integration/triggers/data_trigger.rs index 46f505a9f9f..3f070c2e4a2 100644 --- a/client/tests/integration/triggers/data_trigger.rs +++ b/client/tests/integration/triggers/data_trigger.rs @@ -2,15 +2,14 @@ use eyre::Result; use iroha_client::{client, data_model::prelude::*}; use iroha_data_model::asset::AssetValue; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID}; #[test] fn must_execute_both_triggers() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_650).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let account_id: AccountId = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_definition_id = "rose#wonderland".parse()?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); @@ -39,8 +38,8 @@ fn must_execute_both_triggers() -> Result<()> { )); test_client.submit_blocking(register_trigger)?; - test_client.submit_blocking(Register::account(new_account_with_random_public_key( - "bunny@wonderland".parse()?, + test_client.submit_blocking(Register::account(Account::new( + gen_account_in("wonderland").0, )))?; test_client.submit_blocking(Register::domain(Domain::new("neverland".parse()?)))?; @@ -58,9 +57,8 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu let create_neverland_domain: InstructionBox = Register::domain(Domain::new("neverland".parse()?)).into(); - let account_id: AccountId = "sapporo@neverland".parse()?; - let create_sapporo_account = - Register::account(new_account_with_random_public_key(account_id.clone())).into(); + let (account_id, _account_keypair) = gen_account_in("neverland"); + 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 = @@ -89,12 +87,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(Register::account(new_account_with_random_public_key( - "asahi@wonderland".parse()?, + test_client.submit_blocking(Register::account(Account::new( + gen_account_in("wonderland").0, )))?; - test_client.submit_blocking(Register::account(new_account_with_random_public_key( - "asahi@neverland".parse()?, + test_client.submit_blocking(Register::account(Account::new( + gen_account_in("neverland").0, )))?; let new_value = get_asset_value(&test_client, asset_id); diff --git a/client/tests/integration/triggers/event_trigger.rs b/client/tests/integration/triggers/event_trigger.rs index 12a5dca633c..756a2ee6ac3 100644 --- a/client/tests/integration/triggers/event_trigger.rs +++ b/client/tests/integration/triggers/event_trigger.rs @@ -1,11 +1,10 @@ -use std::str::FromStr; - use eyre::Result; use iroha_client::{ client::{self, Client}, data_model::prelude::*, }; use test_network::*; +use test_samples::ALICE_ID; #[test] fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { @@ -13,7 +12,7 @@ fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let prev_value = get_asset_value(&mut test_client, asset_id.clone()); diff --git a/client/tests/integration/triggers/time_trigger.rs b/client/tests/integration/triggers/time_trigger.rs index 8a9bb9fb034..a7d161eb033 100644 --- a/client/tests/integration/triggers/time_trigger.rs +++ b/client/tests/integration/triggers/time_trigger.rs @@ -9,8 +9,7 @@ use iroha_config::parameters::defaults::chain_wide::DEFAULT_CONSENSUS_ESTIMATION use iroha_data_model::events::pipeline::{BlockEventFilter, BlockStatus}; use iroha_logger::info; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID}; fn curr_time() -> core::time::Duration { use std::time::SystemTime; @@ -47,7 +46,7 @@ fn time_trigger_execution_count_error_should_be_less_than_15_percent() -> 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 account_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = "rose#wonderland".parse().expect("Valid"); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); @@ -107,7 +106,7 @@ fn change_asset_metadata_after_1_sec() -> Result<()> { let event_listener = get_block_committed_event_listener(&test_client)?; let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let key = Name::from_str("petal")?; let schedule = TimeSchedule::starting_at(start_time + PERIOD); @@ -148,7 +147,7 @@ fn pre_commit_trigger_should_be_executed() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse().expect("Valid"); - let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let mut prev_value = get_asset_value(&mut test_client, asset_id.clone()); @@ -193,14 +192,14 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { let (_rt, _peer, mut test_client) = ::new().with_port(10_780).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let alice_id = "alice@wonderland".parse::().expect("Valid"); + let alice_id = ALICE_ID.clone(); let accounts: Vec = vec![ alice_id.clone(), - "mad_hatter@wonderland".parse().expect("Valid"), - "cheshire_cat@wonderland".parse().expect("Valid"), - "caterpillar@wonderland".parse().expect("Valid"), - "white_rabbit@wonderland".parse().expect("Valid"), + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, ]; // Registering accounts @@ -208,7 +207,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| Register::account(new_account_with_random_public_key(account_id))) + .map(|account_id| Register::account(Account::new(account_id))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; @@ -255,7 +254,7 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { // Checking results for account_id in accounts { let start_pattern = "nft_number_"; - let end_pattern = format!("_for_{}#{}", account_id.name, account_id.domain_id); + let end_pattern = format!("_for_{}#{}", account_id.signatory, account_id.domain_id); let assets = test_client .request(client::asset::by_account_id(account_id.clone()))? .collect::>>()?; diff --git a/client/tests/integration/triggers/trigger_rollback.rs b/client/tests/integration/triggers/trigger_rollback.rs index 6f61cae9835..1eb6188d916 100644 --- a/client/tests/integration/triggers/trigger_rollback.rs +++ b/client/tests/integration/triggers/trigger_rollback.rs @@ -6,6 +6,7 @@ use iroha_client::{ data_model::{prelude::*, query::asset::FindAllAssetsDefinitions, trigger::TriggerId}, }; use test_network::*; +use test_samples::ALICE_ID; #[test] fn failed_trigger_revert() -> Result<()> { @@ -14,7 +15,7 @@ fn failed_trigger_revert() -> Result<()> { //When let trigger_id = TriggerId::from_str("trigger")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); diff --git a/client/tests/integration/tx_chain_id.rs b/client/tests/integration/tx_chain_id.rs index 9e16a90a898..e439361c728 100644 --- a/client/tests/integration/tx_chain_id.rs +++ b/client/tests/integration/tx_chain_id.rs @@ -1,38 +1,28 @@ use std::str::FromStr; -use iroha_crypto::KeyPair; use iroha_data_model::prelude::*; use iroha_primitives::numeric::numeric; use test_network::*; - -use crate::integration::asset::asset_id_new; +use test_samples::gen_account_in; #[test] fn send_tx_with_different_chain_id() { let (_rt, _peer, test_client) = ::new().with_port(11_250).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let sender_account_id = AccountId::from_str("sender@wonderland").unwrap(); - let sender_keypair = KeyPair::random(); - let receiver_account_id = AccountId::from_str("receiver@wonderland").unwrap(); - let receiver_keypair = KeyPair::random(); + let (sender_id, sender_keypair) = gen_account_in("wonderland"); + let (receiver_id, _receiver_keypair) = gen_account_in("wonderland"); let asset_definition_id = AssetDefinitionId::from_str("test_asset#wonderland").unwrap(); let to_transfer = numeric!(1); - let create_sender_account: InstructionBox = Register::account(Account::new( - sender_account_id.clone(), - sender_keypair.public_key().clone(), - )) - .into(); - let create_receiver_account: InstructionBox = Register::account(Account::new( - receiver_account_id.clone(), - receiver_keypair.public_key().clone(), - )) - .into(); + let create_sender_account: InstructionBox = + Register::account(Account::new(sender_id.clone())).into(); + let create_receiver_account: InstructionBox = + Register::account(Account::new(receiver_id.clone())).into(); let register_asset_definition: InstructionBox = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())).into(); let register_asset: InstructionBox = Register::asset(Asset::new( - AssetId::new(asset_definition_id.clone(), sender_account_id.clone()), + AssetId::new(asset_definition_id.clone(), sender_id.clone()), numeric!(10), )) .into(); @@ -48,14 +38,14 @@ fn send_tx_with_different_chain_id() { let chain_id_1 = ChainId::from("1"); let transfer_instruction = Transfer::asset_numeric( - asset_id_new("test_asset", "wonderland", sender_account_id.clone()), + AssetId::new("test_asset#wonderland".parse().unwrap(), sender_id.clone()), to_transfer, - receiver_account_id.clone(), + receiver_id.clone(), ); - let asset_transfer_tx_0 = TransactionBuilder::new(chain_id_0, sender_account_id.clone()) + let asset_transfer_tx_0 = TransactionBuilder::new(chain_id_0, sender_id.clone()) .with_instructions([transfer_instruction.clone()]) .sign(&sender_keypair); - let asset_transfer_tx_1 = TransactionBuilder::new(chain_id_1, sender_account_id.clone()) + let asset_transfer_tx_1 = TransactionBuilder::new(chain_id_1, sender_id.clone()) .with_instructions([transfer_instruction]) .sign(&sender_keypair); test_client diff --git a/client/tests/integration/tx_history.rs b/client/tests/integration/tx_history.rs index fb8b22ea604..19a57bd638e 100644 --- a/client/tests/integration/tx_history.rs +++ b/client/tests/integration/tx_history.rs @@ -8,6 +8,7 @@ use iroha_client::{ use iroha_config::parameters::actual::Root as Config; use nonzero_ext::nonzero; use test_network::*; +use test_samples::ALICE_ID; #[ignore = "ignore, more in #2851"] #[test] @@ -18,7 +19,7 @@ fn client_has_rejected_and_acepted_txs_should_return_tx_history() -> Result<()> let pipeline_time = Config::pipeline_time(); // Given - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); diff --git a/client/tests/integration/tx_rollback.rs b/client/tests/integration/tx_rollback.rs index 181e0242cd2..a5cc76ad759 100644 --- a/client/tests/integration/tx_rollback.rs +++ b/client/tests/integration/tx_rollback.rs @@ -6,6 +6,7 @@ use iroha_client::{ data_model::prelude::*, }; use test_network::*; +use test_samples::ALICE_ID; #[test] fn client_sends_transaction_with_invalid_instruction_should_not_see_any_changes() -> Result<()> { @@ -13,7 +14,7 @@ fn client_sends_transaction_with_invalid_instruction_should_not_see_any_changes( wait_for_genesis_committed(&[client.clone()], 0); //When - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let wrong_asset_definition_id = AssetDefinitionId::from_str("ksor#wonderland")?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id)); diff --git a/client/tests/integration/upgrade.rs b/client/tests/integration/upgrade.rs index 3d4106c79f5..817b9b4b6d4 100644 --- a/client/tests/integration/upgrade.rs +++ b/client/tests/integration/upgrade.rs @@ -3,35 +3,45 @@ use std::{path::Path, str::FromStr as _}; use eyre::Result; use iroha_client::{ client::{self, Client, QueryResult}, - crypto::KeyPair, data_model::prelude::*, }; +use iroha_crypto::{Algorithm, KeyPair, PrivateKey}; use iroha_logger::info; use serde_json::json; use test_network::*; +use test_samples::ALICE_ID; + +const ADMIN_ID: &str = + "ed012076E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC@admin"; +const ADMIN_SECRET: &str = "A4DE33BCA99A254ED6265D1F0FB69DFE42B77F89F6C2E478498E1831BF6D81F276E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC"; #[test] fn executor_upgrade_should_work() -> Result<()> { let chain_id = ChainId::from("0"); + let admin_id: AccountId = ADMIN_ID.parse().unwrap(); + let admin_keypair = KeyPair::new( + admin_id.signatory().clone(), + PrivateKey::from_hex(Algorithm::Ed25519, ADMIN_SECRET).unwrap(), + ) + .unwrap(); let (_rt, _peer, client) = ::new().with_port(10_795).start_with_runtime(); wait_for_genesis_committed(&vec![client.clone()], 0); // Register `admin` domain and account - let admin_domain = Domain::new("admin".parse()?); + let admin_domain = Domain::new(admin_id.domain_id().clone()); 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::random(); - let admin_account = Account::new(admin_id.clone(), admin_keypair.public_key().clone()); + let admin_account = Account::new(admin_id.clone()); 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 = Transfer::asset_numeric(alice_rose, 1u32, admin_rose); + let alice_rose: AssetId = format!("rose##{}", ALICE_ID.clone()) + .parse() + .expect("should be valid"); + let transfer_alice_rose = Transfer::asset_numeric(alice_rose, 1u32, admin_id.clone()); let transfer_rose_tx = TransactionBuilder::new(chain_id.clone(), admin_id.clone()) .with_instructions([transfer_alice_rose.clone()]) .sign(&admin_keypair); @@ -46,7 +56,7 @@ fn executor_upgrade_should_work() -> Result<()> { // Check that admin can transfer alice's rose now // Creating new transaction instead of cloning, because we need to update it's creation time - let transfer_rose_tx = TransactionBuilder::new(chain_id, admin_id) + let transfer_rose_tx = TransactionBuilder::new(chain_id, admin_id.clone()) .with_instructions([transfer_alice_rose]) .sign(&admin_keypair); client @@ -71,7 +81,7 @@ fn executor_upgrade_should_run_migration() -> Result<()> { .any(|id| id == &can_unregister_domain_token_id)); // Check that Alice has permission to unregister Wonderland - let alice_id: AccountId = "alice@wonderland".parse().unwrap(); + let alice_id = ALICE_ID.clone(); let alice_tokens = client .request(FindPermissionTokensByAccountId::new(alice_id.clone()))? .collect::>>() @@ -147,7 +157,7 @@ fn executor_upgrade_should_revoke_removed_permissions() -> Result<()> { .contains(&can_unregister_domain_token)); // Check that Alice has permission - let alice_id: AccountId = "alice@wonderland".parse()?; + let alice_id = ALICE_ID.clone(); assert!(client .request(FindPermissionTokensByAccountId::new(alice_id.clone()))? .collect::>>()? diff --git a/client_cli/README.md b/client_cli/README.md index 6cb5dfbc644..9cb7fdf4d29 100644 --- a/client_cli/README.md +++ b/client_cli/README.md @@ -60,20 +60,27 @@ Check the [Bash guide in Iroha Tutorial](https://hyperledger.github.io/iroha-2-d ```bash ./iroha_client_cli domain register --id="Soramitsu" -./iroha_client_cli account register --id="White Rabbit@Soramitsu" --key="" +./iroha_client_cli account register --id="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" ./iroha_client_cli asset register --id="XOR#Soramitsu" --value-type=Numeric -./iroha_client_cli asset mint --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 -./iroha_client_cli asset get --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" +./iroha_client_cli asset mint --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 +./iroha_client_cli asset get --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" ``` In this section we will show you how to use Iroha CLI Client to do the following: -- [Create new Domain](#create-new-domain) -- [Create new Account](#create-new-account) -- [Mint Asset to Account](#mint-asset-to-account) -- [Query Account Assets Quantity](#query-account-assets-quantity) -- [Execute WASM transaction](#execute-wasm-transaction) -- [Execute Multi-instruction Transactions](#execute-multi-instruction-instructions) +- [Iroha CLI Client](#iroha-cli-client) + - [Features](#features) + - [Installation](#installation) + - [Usage](#usage) + - [Options](#options) + - [Subcommands](#subcommands) + - [Examples](#examples) + - [Create new Domain](#create-new-domain) + - [Create new Account](#create-new-account) + - [Mint Asset to Account](#mint-asset-to-account) + - [Query Account Assets Quantity](#query-account-assets-quantity) + - [Execute WASM transaction](#execute-wasm-transaction) + - [Execute Multi-instruction Transactions](#execute-multi-instruction-transactions) ### Create new Domain @@ -89,12 +96,10 @@ Now you have a domain without any accounts. ### Create new Account -Let's create a new account. Like in the previous example, specify the entity type (`account`) and the command (`register`). Then define the account name as the value of the `id` argument. - -Additionally, you need to provide the `key` argument with the account's public key as a double-quoted multihash representation of the key. Providing an empty string also works (but is highly discouraged), while omitting the argument altogether will produce an error. +Let's create a new account. Like in the previous example, specify the entity type (`account`) and the command (`register`). Then define the value of the `id` argument in "signatory@domain" format, where signatory is the account's public key in multihash representation. ```bash -./iroha_client_cli account register --id="White Rabbit@Soramitsu" --key="" +./iroha_client_cli account register --id="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" ``` ### Mint Asset to Account @@ -107,10 +112,10 @@ Every asset has its own value spec. In this example, it is defined as `Numeric`, ```bash ./iroha_client_cli asset register --id="XOR#Soramitsu" --value-type=Numeric -./iroha_client_cli asset mint --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 +./iroha_client_cli asset mint --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 ``` -You created `XOR#Soramitsu`, an asset of type `Numeric`, and then gave `1010` units of this asset to the account `White Rabbit@Soramitsu`. +You created `XOR#Soramitsu`, an asset of type `Numeric`, and then gave `1010` units of this asset to the account `ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu`. ### Query Account Assets Quantity @@ -128,10 +133,10 @@ Let's use Get Account Assets Query as an example. To know how many units of a particular asset an account has, use `asset get` with the specified account and asset: ```bash -./iroha_client_cli asset get --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" +./iroha_client_cli asset get --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" ``` -This query returns the quantity of `XOR#Soramitsu` asset for the `White Rabbit@Soramitsu` account. +This query returns the quantity of `XOR#Soramitsu` asset for the `ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu` account. It's possible to filter based on either account, asset or domain id by using the filtering API provided by the Iroha client CLI. diff --git a/client_cli/pytests/README.md b/client_cli/pytests/README.md index f702ab4f6ab..6aefb69e900 100644 --- a/client_cli/pytests/README.md +++ b/client_cli/pytests/README.md @@ -4,14 +4,15 @@ This directory contains the `pytest` framework with test suites for the Iroha 2 For quick access to a topic that interests you, select one of the following: -- [Framework Structure](#framework-structure) -- [Iroha 2 Test Model](#iroha-2-test-model) -- [Using Test Suites](#using-test-suites) - - [Custom Test Environment with Docker Compose](#custom-test-environment-with-docker-compose) - - [Poetry Configuration](#poetry-configuration) - - [Tests Configuration](#tests-configuration) -- [Running Tests](#running-tests) -- [Viewing Test Reports](#viewing-test-reports) +- [Overview](#overview) + - [Framework Structure](#framework-structure) + - [Iroha 2 Test Model](#iroha-2-test-model) + - [Using Test Suites](#using-test-suites) + - [Custom Test Environment with Docker Compose](#custom-test-environment-with-docker-compose) + - [Poetry Configuration](#poetry-configuration) + - [Tests Configuration](#tests-configuration) + - [Running Tests](#running-tests) + - [Viewing Test Reports](#viewing-test-reports) ## Framework Structure @@ -105,20 +106,20 @@ To do so, perform the following steps: cargo build --bin iroha_client_cli ``` -3. Create a new directory, then copy the `iroha_client_cli` binary and its `config.json` configuration file into it: +3. Create a new directory, then copy the `iroha_client_cli` binary and its `client.toml` configuration file into it: ```shell # Create a new directory: - mkdir test_client + mkdir test # Copy the files: - cp configs/client/config.json test_client - cp target/debug/iroha_client_cli test_client + cp configs/swarm/client.toml test + cp target/debug/iroha_client_cli test ``` 4. Proceed with _Step 2_ of the [Using Test Suites](#using-test-suites) instructions. > [!NOTE] -> Don't forget to specify the path to the directory created for the `iroha_client_cli` binary and its `config.json` configuration file (see Step 3) in the `CLIENT_CLI_DIR` variable of the `.env` file. +> Don't forget to specify the path to the directory created for the `iroha_client_cli` binary and its `client.toml` configuration file (see Step 3) in the `CLIENT_CLI_DIR` variable of the `.env` file. > For details, see [Tests Configuration](#tests-configuration) below. ### Poetry Configuration diff --git a/client_cli/pytests/common/consts.py b/client_cli/pytests/common/consts.py index fadc52223ae..f1221c655b6 100644 --- a/client_cli/pytests/common/consts.py +++ b/client_cli/pytests/common/consts.py @@ -15,11 +15,11 @@ class Stderr(Enum): Enum for standard error messages. """ - CANNOT_BE_EMPTY = "cannot be empty\n\nFor more information, try '--help'.\n" + EMPTY = "mpty" REPETITION = "Repetition" TOO_LONG = "Name length violation" FAILED_TO_FIND_DOMAIN = "Failed to find domain" - INVALID_CHARACTER = "Invalid character" + INVALID_CHARACTER = "Failed to parse" INVALID_VALUE_TYPE = "should be either `Store` or `Numeric`" RESERVED_CHARACTER = ( "The `@` character is reserved for `account@domain` constructs," diff --git a/client_cli/pytests/common/helpers.py b/client_cli/pytests/common/helpers.py index 6145ea35557..aa55466ec9c 100644 --- a/client_cli/pytests/common/helpers.py +++ b/client_cli/pytests/common/helpers.py @@ -19,7 +19,7 @@ def extract_hash(stdout): """ Extracts a SHA-256 hash from the given string. - :param stdout: The string from which to extract the hash. + :param stdout: The string from which to extract the hash. :return: The extracted hash if found, otherwise None. """ if not isinstance(stdout, str) or not stdout.strip(): @@ -98,7 +98,7 @@ def generate_public_key(): encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw ) ).decode() - return public_key + return "ed0120" + public_key.upper() def generate_random_string(length, allowed_chars): diff --git a/client_cli/pytests/common/json_isi_examples/unregister_asset.json b/client_cli/pytests/common/json_isi_examples/unregister_asset.json index ac3d7f314f9..837355581fb 100644 --- a/client_cli/pytests/common/json_isi_examples/unregister_asset.json +++ b/client_cli/pytests/common/json_isi_examples/unregister_asset.json @@ -1,7 +1,7 @@ [{ "Unregister": { "Asset": { - "object_id": "rose#alice@wonderland" + "object_id": "rose#ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } -}] \ No newline at end of file +}] diff --git a/client_cli/pytests/models/account.py b/client_cli/pytests/models/account.py index 7f3acb01d67..7e15d614919 100644 --- a/client_cli/pytests/models/account.py +++ b/client_cli/pytests/models/account.py @@ -10,17 +10,14 @@ class Account: """ Account class represents an Iroha account. - :param name: The name of the account. - :type name: str + :param signatory: The signatory of the account. + :type signatory: str :param domain: The domain of the account. :type domain: str - :param public_key: The public key of the account. - :type public_key: str """ - name: str + signatory: str domain: str - public_key: str = "" def __repr__(self): - return f"{self.name}@{self.domain}" + return f"{self.signatory}@{self.domain}" diff --git a/client_cli/pytests/src/client_cli/client_cli.py b/client_cli/pytests/src/client_cli/client_cli.py index fd751c77f65..c78373fa2cf 100644 --- a/client_cli/pytests/src/client_cli/client_cli.py +++ b/client_cli/pytests/src/client_cli/client_cli.py @@ -134,22 +134,19 @@ def domain(self, domain: str): self.execute() return self - def account(self, account: str, domain: str, key: str): + def account(self, signatory: str, domain: str): """ - Executes the 'account' command for the given account, domain, and key. + Executes the 'account' command for the given signatory and domain. - :param account: The account to be queried. - :type account: str + :param signatory: The signatory of the account. + :type signatory: str :param domain: The domain of the account. :type domain: str - :param key: The key for the account. - :type key: str :return: The current ClientCli object. :rtype: ClientCli """ self.command.insert(2, "account") - self.command.append("--id=" + account + "@" + domain) - self.command.append("--key=ed0120" + key) + self.command.append("--id=" + signatory + "@" + domain) self.execute() return self @@ -172,11 +169,11 @@ def asset(self, asset_definition=None, account=None, value_of_value_type=None): "--asset-id=" + asset_definition.name + "#" - + account.domain + + asset_definition.domain + "#" - + account.name + + account.signatory + "@" - + asset_definition.domain + + account.domain ) self.command.append("--quantity=" + value_of_value_type) self.execute() @@ -204,11 +201,11 @@ def transfer(self, asset, source_account, target_account, quantity: str): "--asset-id=" + asset.name + "#" - + source_account.domain + + asset.domain + "#" - + source_account.name + + source_account.signatory + "@" - + asset.domain + + source_account.domain ) self.command.append("--quantity=" + quantity) self.execute() @@ -232,11 +229,11 @@ def burn(self, account, asset, quantity: str): "--asset-id=" + asset.name + "#" - + account.domain + + asset.domain + "#" - + account.name + + account.signatory + "@" - + asset.domain + + account.domain ) self.command.append("--quantity=" + quantity) self.execute() diff --git a/client_cli/pytests/src/client_cli/configuration.py b/client_cli/pytests/src/client_cli/configuration.py index 5e04cdfbecd..897eec066f8 100644 --- a/client_cli/pytests/src/client_cli/configuration.py +++ b/client_cli/pytests/src/client_cli/configuration.py @@ -122,42 +122,22 @@ def env(self): # return self._config['TORII_API_URL' return {**os.environ, **self._envs} - @property - def account_id(self): - """ - Get the ACCOUNT_ID configuration value. - - :return: The ACCOUNT_ID. - :rtype: str - """ - return self._config["account"]["id"] - - @property - def account_name(self): - """ - Get the account name from the ACCOUNT_ID configuration value. - - :return: The account name. - :rtype: str - """ - return self.account_id.split("@")[0] - @property def account_domain(self): """ - Get the account domain from the ACCOUNT_ID configuration value. + Get the ACCOUNT_DOMAIN configuration value. :return: The account domain. :rtype: str """ - return self.account_id.split("@")[1] + return self._config["account"]["domain_id"] @property - def public_key(self): + def account_signatory(self): """ Get the PUBLIC_KEY configuration value. - :return: The public key. + :return: The account signatory. :rtype: str """ - return self._config["account"]["public_key"].split("ed0120")[1] + return self._config["account"]["public_key"] diff --git a/client_cli/pytests/src/client_cli/iroha.py b/client_cli/pytests/src/client_cli/iroha.py index 8c35810c21a..7bfa0ba3af6 100644 --- a/client_cli/pytests/src/client_cli/iroha.py +++ b/client_cli/pytests/src/client_cli/iroha.py @@ -87,7 +87,7 @@ def assets(self) -> Dict[str, str]: Retrieve assets from the Iroha network and return them as a dictionary where the keys are asset ids and the values are the corresponding asset objects. - :return: Dictionary of assets. + :return: Dictionary of assets. :rtype: Dict[str, Any] """ self._execute_command("asset") diff --git a/client_cli/pytests/test/__init__.py b/client_cli/pytests/test/__init__.py index ab75290e556..1ca1a357dae 100644 --- a/client_cli/pytests/test/__init__.py +++ b/client_cli/pytests/test/__init__.py @@ -3,7 +3,6 @@ """ from .conftest import ( - GIVEN_127_length_name, GIVEN_128_length_name, GIVEN_129_length_name, GIVEN_currently_account_quantity_with_two_quantity_of_asset, diff --git a/client_cli/pytests/test/accounts/conftest.py b/client_cli/pytests/test/accounts/conftest.py index 7329c22d45f..f92932bfcb9 100644 --- a/client_cli/pytests/test/accounts/conftest.py +++ b/client_cli/pytests/test/accounts/conftest.py @@ -1,5 +1,4 @@ from test import ( - GIVEN_127_length_name, GIVEN_129_length_name, GIVEN_fake_name, GIVEN_key_with_invalid_character_in_key, diff --git a/client_cli/pytests/test/accounts/test_accounts_query_filters.py b/client_cli/pytests/test/accounts/test_accounts_query_filters.py index bb13afe52bf..6ff50159468 100644 --- a/client_cli/pytests/test/accounts/test_accounts_query_filters.py +++ b/client_cli/pytests/test/accounts/test_accounts_query_filters.py @@ -26,28 +26,10 @@ def condition(): client_cli.wait_for(condition) -def test_filter_by_account_name(GIVEN_registered_account): - def condition(): - name = GIVEN_registered_account.name - with allure.step(f'WHEN client_cli query accounts with name "{name}"'): - accounts = iroha.list_filter( - {"Identifiable": {"StartsWith": f"{name}@"}} - ).accounts() - with allure.step("THEN Iroha should return only accounts with this name"): - allure.attach( - json.dumps(accounts), - name="accounts", - attachment_type=allure.attachment_type.JSON, - ) - return accounts and all(account.startswith(name) for account in accounts) - - client_cli.wait_for(condition) - - def test_filter_by_account_id(GIVEN_registered_account): def condition(): account_id = ( - GIVEN_registered_account.name + "@" + GIVEN_registered_account.domain + GIVEN_registered_account.signatory + "@" + GIVEN_registered_account.domain ) with allure.step( f'WHEN client_cli query accounts with account id "{account_id}"' diff --git a/client_cli/pytests/test/accounts/test_register_accounts.py b/client_cli/pytests/test/accounts/test_register_accounts.py index 17b82efa853..4c0dec1b293 100644 --- a/client_cli/pytests/test/accounts/test_register_accounts.py +++ b/client_cli/pytests/test/accounts/test_register_accounts.py @@ -12,67 +12,39 @@ def story_account_register_account(): @allure.label("sdk_test_id", "register_account") -def test_register_account(GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key): +def test_register_account(GIVEN_public_key, GIVEN_registered_domain): with allure.step( - f'WHEN client_cli registers the account "{GIVEN_fake_name}" ' + f'WHEN client_cli registers the account "{GIVEN_public_key}" ' f'in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_public_key, domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, ) - registered = GIVEN_fake_name + "@" + GIVEN_registered_domain.name + registered = GIVEN_public_key + "@" + GIVEN_registered_domain.name with allure.step(f'THEN Iroha should have the "{registered}" account'): iroha.should(have.account(registered)) -@allure.label("sdk_test_id", "register_account_with_two_public_keys") -@pytest.mark.xfail(reason="TO DO") -def test_register_account_with_two_public_keys( - GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key -): - assert 0 - - -@allure.label("sdk_test_id", "register_account_with_empty_name") -def test_register_account_with_empty_name(GIVEN_registered_domain, GIVEN_public_key): - with allure.step( - f"WHEN client_cli tries to register an account with an empty name " - f'in the "{GIVEN_registered_domain.name}" domain' - ): - client_cli.register().account( - account="", domain=GIVEN_registered_domain.name, key=GIVEN_public_key - ) - with allure.step( - f'THEN сlient_cli should have the account error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) - - -@allure.label("sdk_test_id", "register_account_with_existing_name") -def test_register_account_with_existing_name( - GIVEN_registered_domain, GIVEN_public_key, GIVEN_registered_account +@allure.label("sdk_test_id", "register_account_with_existing_signatory") +def test_register_account_with_existing_signatory( + GIVEN_registered_domain, GIVEN_registered_account ): with allure.step( f"WHEN client_cli tries to register an account " - f'with the same name "{GIVEN_registered_domain.name}" ' + f'with the same signatory "{GIVEN_registered_account.signatory}" ' f'in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=GIVEN_registered_account.name, + signatory=GIVEN_registered_account.signatory, domain=GIVEN_registered_account.domain, - key=GIVEN_registered_account.public_key, ) - with allure.step( - f'THEN client_cli should have the account error: "{GIVEN_registered_domain.name}"' - ): + with allure.step("THEN client_cli should have the account error"): client_cli.should(have.error(Stderr.REPETITION.value)) @allure.label("sdk_test_id", "register_account_with_invalid_domain") def test_register_account_with_invalid_domain( - GIVEN_fake_name, GIVEN_not_existing_name, GIVEN_public_key, ): @@ -80,9 +52,8 @@ def test_register_account_with_invalid_domain( "WHEN client_cli tries to register an account with an invalid domain" ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_public_key, domain=GIVEN_not_existing_name, - key=GIVEN_public_key, ) with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.FAILED_TO_FIND_DOMAIN.value)) @@ -90,61 +61,19 @@ def test_register_account_with_invalid_domain( @allure.label("sdk_test_id", "register_account_with_invalid_character_in_key") def test_register_account_with_invalid_character_in_key( - GIVEN_fake_name, GIVEN_registered_domain, GIVEN_key_with_invalid_character_in_key + GIVEN_registered_domain, GIVEN_key_with_invalid_character_in_key ): with allure.step( "WHEN client_cli tries to register an account with invalid character in the key" ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_key_with_invalid_character_in_key, domain=GIVEN_registered_domain.name, - key=GIVEN_key_with_invalid_character_in_key, ) with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.INVALID_CHARACTER.value)) -@allure.label("sdk_test_id", "register_account_with_max_name") -def test_register_account_with_max_name( - GIVEN_127_length_name, GIVEN_registered_domain, GIVEN_public_key -): - with allure.step("WHEN client_cli register an account with the 127 length name"): - client_cli.register().account( - account=GIVEN_127_length_name, - domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, - ) - registered = GIVEN_127_length_name + "@" + GIVEN_registered_domain.name - with allure.step(f'THEN Iroha should have the "{registered}" account'): - iroha.should(have.account(registered)) - - -@allure.label("sdk_test_id", "register_account_with_special_characters") -@pytest.mark.xfail(reason="TO DO") -def test_register_account_with_special_characters( - GIVEN_registered_domain, GIVEN_public_key -): - assert 0 - - -@allure.label("sdk_test_id", "register_account_with_long_account_name") -def test_register_account_with_long_account_name( - GIVEN_registered_domain, GIVEN_129_length_name, GIVEN_public_key -): - with allure.step( - "WHEN client_cli tries to register an account with a name with 129 characters" - ): - client_cli.register().account( - account=GIVEN_129_length_name, - domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, - ) - with allure.step( - f'THEN client_cli should have the name error: "{Stderr.TOO_LONG}"' - ): - client_cli.should(have.error(Stderr.TOO_LONG.value)) - - @allure.label("sdk_test_id", "register_account_with_metadata") @pytest.mark.xfail(reason="TO DO") def test_register_account_with_metadata( diff --git a/client_cli/pytests/test/assets/test_assets_query_filters.py b/client_cli/pytests/test/assets/test_assets_query_filters.py index 1a44e6e8683..918a255079d 100644 --- a/client_cli/pytests/test/assets/test_assets_query_filters.py +++ b/client_cli/pytests/test/assets/test_assets_query_filters.py @@ -69,7 +69,7 @@ def condition(): asset_id = ( GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name + "##" - + GIVEN_currently_authorized_account.name + + GIVEN_currently_authorized_account.signatory + "@" + GIVEN_currently_authorized_account.domain ) diff --git a/client_cli/pytests/test/assets/test_burn_assets.py b/client_cli/pytests/test/assets/test_burn_assets.py index fdbc9a40864..b7d22f3ab3b 100644 --- a/client_cli/pytests/test/assets/test_burn_assets.py +++ b/client_cli/pytests/test/assets/test_burn_assets.py @@ -16,7 +16,7 @@ def test_burn_asset_for_account_in_same_domain( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} burns 1" + f"WHEN {GIVEN_currently_authorized_account.signatory} burns 1" f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" ): client_cli.burn( @@ -25,7 +25,7 @@ def test_burn_asset_for_account_in_same_domain( quantity="1", ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} " + f"THEN {GIVEN_currently_authorized_account.signatory} " f"has 1 of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" ): iroha.should( diff --git a/client_cli/pytests/test/assets/test_mint_assets.py b/client_cli/pytests/test/assets/test_mint_assets.py index 869049434e4..ef83e794df0 100644 --- a/client_cli/pytests/test/assets/test_mint_assets.py +++ b/client_cli/pytests/test/assets/test_mint_assets.py @@ -16,9 +16,9 @@ def test_mint_asset_for_account_in_same_domain( GIVEN_numeric_value, ): with allure.step( - f'WHEN client_cli mint the asset "{GIVEN_registered_asset_definition_with_numeric_value_type.name}" ' - f'for the "{GIVEN_registered_account.name}" ' - f'in the "{GIVEN_registered_asset_definition_with_numeric_value_type.domain}" domain' + f'WHEN client_cli mint "{GIVEN_numeric_value}" of ' + f'"{GIVEN_registered_asset_definition_with_numeric_value_type}" ' + f'for the "{GIVEN_registered_account}"' ): client_cli.mint().asset( account=GIVEN_registered_account, @@ -26,8 +26,9 @@ def test_mint_asset_for_account_in_same_domain( value_of_value_type=GIVEN_numeric_value, ) with allure.step( - f'THEN "{GIVEN_registered_account}" account ' - f'should have the "{GIVEN_registered_asset_definition_with_numeric_value_type}" asset definition' + f'THEN "{GIVEN_registered_account}" ' + f'should have the "{GIVEN_numeric_value}" of ' + f'"{GIVEN_registered_asset_definition_with_numeric_value_type}"' ): iroha.should( have.asset( @@ -59,8 +60,8 @@ def test_mint_asset_quantity_after_minting(GIVEN_minted_asset_quantity): expected_quantity = int(GIVEN_minted_asset_quantity.value) + 1 with allure.step( f'THEN "{GIVEN_minted_asset_quantity.account}" account ' - f'should have the "{expected_quantity}" asset definition ' - f"with updated quantity" + f'should have the "{GIVEN_minted_asset_quantity.definition}" asset ' + f'with updated quantity "{expected_quantity}"' ): iroha.should( have.asset_has_quantity( diff --git a/client_cli/pytests/test/assets/test_register_asset_definitions.py b/client_cli/pytests/test/assets/test_register_asset_definitions.py index 9110cd3bb16..03233ccc373 100644 --- a/client_cli/pytests/test/assets/test_register_asset_definitions.py +++ b/client_cli/pytests/test/assets/test_register_asset_definitions.py @@ -119,10 +119,8 @@ def test_register_asset_with_empty_name(GIVEN_registered_domain): client_cli.register().asset().definition( asset="", domain=GIVEN_registered_domain.name, value_type="Numeric" ) - with allure.step( - f'THEN сlient_cli should have the asset error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) + with allure.step(f'THEN сlient_cli should have the asset error: "{Stderr.EMPTY}"'): + client_cli.should(have.error(Stderr.EMPTY.value)) @allure.label("sdk_test_id", "register_asset_with_not_existing_domain") diff --git a/client_cli/pytests/test/assets/test_transfer_assets.py b/client_cli/pytests/test/assets/test_transfer_assets.py index f78a2da2daa..4e842185c58 100644 --- a/client_cli/pytests/test/assets/test_transfer_assets.py +++ b/client_cli/pytests/test/assets/test_transfer_assets.py @@ -18,9 +18,9 @@ def test_transfer_asset( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} transfers 1 Quantity" + f"WHEN {GIVEN_currently_authorized_account.signatory} transfers 1 Quantity" f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"to {GIVEN_registered_account.name}" + f"to {GIVEN_registered_account.signatory}" ): client_cli.transfer( asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, @@ -30,7 +30,7 @@ def test_transfer_asset( ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} has 1 Quantity " + f"THEN {GIVEN_currently_authorized_account.signatory} has 1 Quantity " f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" f"AND {GIVEN_registered_account} has 1 more Quantity" ): @@ -51,9 +51,9 @@ def test_transfer_with_insufficient_funds( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} attempts to transfer more than available Quantity" - f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"to {GIVEN_registered_account.name}" + f"WHEN {GIVEN_currently_authorized_account.signatory} attempts to transfer more than available " + f"Quantity of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + f"to {GIVEN_registered_account.signatory}" ): client_cli.transfer( asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, @@ -65,9 +65,9 @@ def test_transfer_with_insufficient_funds( ), ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} still has the original Quantity " + f"THEN {GIVEN_currently_authorized_account.signatory} still has the original Quantity " f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"AND {GIVEN_registered_account.name} does not receive any additional Quantity" + f"AND {GIVEN_registered_account.signatory} does not receive any additional Quantity" ): client_cli.should(have.error(Stderr.INSUFFICIENT_FUNDS.value)) @@ -82,8 +82,8 @@ def test_exchange_asset_through_intermediary( GIVEN_buyer_account_with_eth, ): # with allure.step(f'WHEN {GIVEN_intermediary_with_transfer_permission.name}' - # f'exchanges BTC from {GIVEN_seller_account_with_btc.name}' - # f'with ETH from {GIVEN_buyer_account_with_eth.name}'): + # f'exchanges BTC from {GIVEN_seller_account_with_btc.signatory}' + # f'with ETH from {GIVEN_buyer_account_with_eth.signatory}'): # client_cli.exchange_assets( # intermediary_account=GIVEN_intermediary_with_transfer_permission, # seller_account=GIVEN_seller_account_with_btc, @@ -91,8 +91,8 @@ def test_exchange_asset_through_intermediary( # btc_quantity="1", # eth_quantity="10") # - # with allure.step(f'THEN {GIVEN_seller_account_with_btc.name} receives ETH ' - # f'AND {GIVEN_buyer_account_with_eth.name} receives BTC'): + # with allure.step(f'THEN {GIVEN_seller_account_with_btc.signatory} receives ETH ' + # f'AND {GIVEN_buyer_account_with_eth.signatory} receives BTC'): # iroha.should(have.asset( # f'eth#{GIVEN_seller_account_with_btc.domain}', quantity="10")) # iroha.should(have.asset( diff --git a/client_cli/pytests/test/assets/test_unregister_asset.py b/client_cli/pytests/test/assets/test_unregister_asset.py index 997240e516a..bd48a79e0c2 100644 --- a/client_cli/pytests/test/assets/test_unregister_asset.py +++ b/client_cli/pytests/test/assets/test_unregister_asset.py @@ -12,7 +12,11 @@ def story_account_unregisters_asset(): @allure.label("sdk_test_id", "unregister_asset") @pytest.mark.parametrize( - "GIVEN_numeric_asset_for_account", ["alice@wonderland"], indirect=True + "GIVEN_numeric_asset_for_account", + [ + "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + ], + indirect=True, ) @pytest.mark.xfail(reason="wait for #4039") def test_unregister_asset( diff --git a/client_cli/pytests/test/conftest.py b/client_cli/pytests/test/conftest.py index fbfb96ee4b8..364456252ff 100644 --- a/client_cli/pytests/test/conftest.py +++ b/client_cli/pytests/test/conftest.py @@ -67,15 +67,12 @@ def GIVEN_registered_domain_with_uppercase_letter(GIVEN_registered_domain): @pytest.fixture() def GIVEN_registered_account(GIVEN_registered_domain, GIVEN_public_key): """Fixture to create an account.""" - name = fake_name() - account = Account( - name=name, domain=GIVEN_registered_domain.name, public_key=GIVEN_public_key - ) + account = Account(signatory=GIVEN_public_key, domain=GIVEN_registered_domain.name) with allure.step( - f'GIVEN the account "{name}" in the "{GIVEN_registered_domain.name}" domain' + f'GIVEN the account "{GIVEN_public_key}" in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=account.name, domain=account.domain, key=account.public_key + signatory=account.signatory, domain=account.domain ) return account @@ -84,12 +81,11 @@ def GIVEN_registered_account(GIVEN_registered_domain, GIVEN_public_key): def GIVEN_currently_authorized_account(): """Fixture to get the currently authorized account.""" account: Account = Account( - name=config.account_name, + signatory=config.account_signatory, domain=config.account_domain, - public_key=config.public_key, ) with allure.step( - f'GIVEN the currently authorized account "{account.name}" ' + f'GIVEN the currently authorized account "{account.signatory}" ' f'in the "{account.domain}" domain' ): return account @@ -106,7 +102,7 @@ def GIVEN_currently_account_quantity_with_two_quantity_of_asset( value_type=GIVEN_numeric_value_type, ) asset = Asset( - definition=asset_def, value="2", account=GIVEN_currently_authorized_account.name + definition=asset_def, value="2", account=GIVEN_currently_authorized_account ) name = fake_name() with allure.step( @@ -132,12 +128,14 @@ def GIVEN_numeric_asset_for_account( ): """Fixture to get an asset for a given account and domain with specified quantity.""" account, domain = request.param.split("@") - account = Account(name=account, domain=domain) + account = Account(signatory=account, domain=domain) asset_def = AssetDefinition( name=GIVEN_fake_asset_name, domain=domain, value_type=GIVEN_numeric_value_type ) - asset = Asset(definition=asset_def, value=GIVEN_numeric_value, account=account.name) + asset = Asset( + definition=asset_def, value=GIVEN_numeric_value, account=account.signatory + ) with allure.step( f'GIVEN the asset_definition "{asset_def.name}" ' f'in the "{domain}" domain' @@ -328,13 +326,6 @@ def GIVEN_129_length_name(): return ident -@pytest.fixture() -def GIVEN_127_length_name(): - ident = generate_random_string_without_reserved_chars(127) - with allure.step(f'GIVEN a name with 127 length "{ident}"'): - return ident - - @pytest.fixture() def GIVEN_string_with_reserved_character(): """Fixture to provide a random string with reserved characters.""" diff --git a/client_cli/pytests/test/domains/test_register_domains.py b/client_cli/pytests/test/domains/test_register_domains.py index d369aba3b89..7f01073934e 100644 --- a/client_cli/pytests/test/domains/test_register_domains.py +++ b/client_cli/pytests/test/domains/test_register_domains.py @@ -25,10 +25,8 @@ def test_register_empty_domain( ): with allure.step("WHEN client_cli registers an empty domain"): client_cli.register().domain("") - with allure.step( - f'THEN client_cli should have the domain error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) + with allure.step(f'THEN client_cli should have the domain error: "{Stderr.EMPTY}"'): + client_cli.should(have.error(Stderr.EMPTY.value)) @allure.label("sdk_test_id", "register_existing_domain") @@ -38,7 +36,7 @@ def test_register_existing_domain(GIVEN_registered_domain): ): client_cli.register().domain(GIVEN_registered_domain.name) with allure.step( - f'THEN client_cli should have the domain error: "{GIVEN_registered_domain.name}"' + f'THEN client_cli should have the domain error: "{GIVEN_registered_domain.name}"' ): client_cli.should(have.error(Stderr.REPETITION.value)) diff --git a/client_cli/pytests/test/triggers/test_register_trigger.py b/client_cli/pytests/test/triggers/test_register_trigger.py index e1c0e5169a2..87ee784b9e1 100644 --- a/client_cli/pytests/test/triggers/test_register_trigger.py +++ b/client_cli/pytests/test/triggers/test_register_trigger.py @@ -18,6 +18,12 @@ def test_register_trigger(GIVEN_currently_authorized_account): ): client_cli.register_trigger(GIVEN_currently_authorized_account) with allure.step( - "THEN Iroha should have the asset with nft_number_1_for_genesis##genesis@genesis" + "THEN Iroha should have the asset with nft_number_1_for_genesis##\ + ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B@genesis" ): - iroha.should(have.asset("nft_number_1_for_genesis##genesis@genesis")) + iroha.should( + have.asset( + "nft_number_1_for_genesis##\ + ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B@genesis" + ) + ) diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index 666f6902c91..1cfa5487f2d 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -504,9 +504,6 @@ mod account { pub enum Args { /// Register account Register(Register), - /// Set something in account - #[command(subcommand)] - Set(Set), /// List accounts #[command(subcommand)] List(List), @@ -520,7 +517,6 @@ mod account { fn run(self, context: &mut dyn RunContext) -> Result<()> { match_all!((self, context), { Args::Register, - Args::Set, Args::List, Args::Grant, Args::ListPermissions, @@ -534,74 +530,19 @@ mod account { /// Id of account in form `name@domain_name` #[arg(short, long)] pub id: AccountId, - /// Its public key - #[arg(short, long)] - pub key: PublicKey, #[command(flatten)] pub metadata: MetadataArgs, } impl RunArgs for Register { fn run(self, context: &mut dyn RunContext) -> Result<()> { - let Self { id, key, metadata } = self; - let create_account = - iroha_client::data_model::isi::Register::account(Account::new(id, key)); + let Self { id, metadata } = self; + let create_account = iroha_client::data_model::isi::Register::account(Account::new(id)); submit([create_account], metadata.load()?, context) .wrap_err("Failed to register account") } } - /// Set subcommand of account - #[derive(clap::Subcommand, Debug)] - pub enum Set { - /// Signature condition - SignatureCondition(SignatureCondition), - } - - impl RunArgs for Set { - fn run(self, context: &mut dyn RunContext) -> Result<()> { - match_all!((self, context), { Set::SignatureCondition }) - } - } - - #[derive(Debug, Clone)] - pub struct Signature(SignatureCheckCondition); - - impl FromStr for Signature { - type Err = Error; - fn from_str(s: &str) -> Result { - let err_msg = format!("Failed to open the signature condition file {}", &s); - let deser_err_msg = - format!("Failed to deserialize signature condition from file {}", &s); - let content = fs::read_to_string(s).wrap_err(err_msg)?; - let condition: SignatureCheckCondition = - json5::from_str(&content).wrap_err(deser_err_msg)?; - Ok(Self(condition)) - } - } - - /// Set accounts signature condition - #[derive(clap::Args, Debug)] - pub struct SignatureCondition { - /// Signature condition file - pub condition: Signature, - #[command(flatten)] - pub metadata: MetadataArgs, - } - - impl RunArgs for SignatureCondition { - fn run(self, context: &mut dyn RunContext) -> Result<()> { - let account_id = context.configuration().account_id.clone(); - let Self { - condition: Signature(condition), - metadata, - } = self; - let mint_box = Mint::account_signature_check_condition(condition, account_id); - submit([mint_box], metadata.load()?, context) - .wrap_err("Failed to set signature condition") - } - } - /// List accounts with this command #[derive(clap::Subcommand, Debug, Clone)] pub enum List { diff --git a/config/iroha_test_config.toml b/config/iroha_test_config.toml index ae71d8b8223..04308426470 100644 --- a/config/iroha_test_config.toml +++ b/config/iroha_test_config.toml @@ -6,8 +6,8 @@ private_key = { algorithm = "ed25519", payload = "282ED9F3CF92811C3818DBC4AE594E address = "127.0.0.1:1337" [genesis] -public_key = "ed01204CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" file = "./genesis.json" +public_key = "ed01204CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" private_key = { algorithm = "ed25519", payload = "D748E18CE60CB30DEA3E73C9019B7AF45A8D465E3D71BCC9A5EF99A008205E534CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" } [torii] diff --git a/configs/client.template.toml b/configs/client.template.toml index 3bad84abcc5..ab331866cbb 100644 --- a/configs/client.template.toml +++ b/configs/client.template.toml @@ -8,7 +8,7 @@ # password = [account] -# id = +# domain_id = # public_key = # private_key = { algorithm = "", payload = "" } diff --git a/configs/swarm/client.toml b/configs/swarm/client.toml index 55eed0b2f4a..06944ec77d4 100644 --- a/configs/swarm/client.toml +++ b/configs/swarm/client.toml @@ -6,6 +6,6 @@ web_login = "mad_hatter" password = "ilovetea" [account] -id = "alice@wonderland" -public_key = "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" -private_key = { algorithm = "ed25519", payload = "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" } +domain_id = "wonderland" +public_key = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" +private_key = { algorithm = "ed25519", payload = "CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" } diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm index 55348c7b4cb483198f8426b9cce3bddb0050e6f1..1750403265ed2eafd24e4427c9b8dc96bea4a54f 100644 GIT binary patch literal 517097 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
# z-hKJq2j6}7jnht9Q4bndKk^QKbGN^H(^Yj+Ad$Z~+0iVjN^Qg=Z|u7Fd~*%cLLa)UptqxUTgCyFLPwi)2~Ly)u`WJ=)1 zxwTKNU8P%P3AzXl4)Jw-)kb~cpErZ>CWkOgEF?LsZ3Ibngc%*ShoVD9gz&xkbazBp zXNgmZFlMQ{5WxtxOP^Zmls+9NQC%0Zg;tI$hR#mwVO~3|&w9X+lMm|BQd|`Yj6f5R zfF%TFb&;?rRdSRMUc^K1_s4IvsWY;(lIk>Al!_n3;8 zavE+C4a4_K!!3sF6h=a>?uL=pS?W|8j#=td8Ztrb%1qly1m!IO;WaS&8pb;?b3w2a zb=k0(n1btQL?;a$-8W@)?{#$NW)jgIzj-xC_HkVa z{=Vss)((ND(0z*~PUYQ=mO7R0>nwFD-N!6-3h&xf1%N&W>Pm5+8;Yfrs@+0|El8Yk z9f7z3TC|%XoW`J?vFSJuEN9a$+3KjwUcI9Ql{mGdFoRB+H;jpnl;>mI2V2u4 zzzJJV1M;ZWwINRcKSno|jcy%BBMV7U$61vZ2vF=+(7ImNF zflaP&+PiNHBfi;v9BLE1TovBdeVF3Xp>|Ab?{TmieT@AGIvWsnIL4kPB7*ERI38}( z2S)`C71{@rBkQ1$&ipzeuFb|oD-pMdh@x1BqZpR`SYrmclabECO_1D5xe1VNAzdcr zT1hw7Mmo;w3A=XEZG9j(y8iO&NGBX%$%1mM8N;H9p+CYs% z1_1sfDTLan5&*){0RV*<24r1Zw!GXYCepDN^{KV|WV16nJJ#E2OT;o5VkxwB>$n$v z#m*CP)>4KIq}?M|X>($H?1x^uVN+xSYs;(Wj*VUQFO5^PCo(p5_oKLAjPZ@JxbgTo z-@kYA>ieEue@f$t{85bme-qtV_qoCAFhFDc=g<9m%bA<++Hgwaj{H&ij#fTydM6*m zZfyGIL(gnD?fgG{=aj~`^2a=1#qvRu#xvKCedmcgZ+Z9sYy2jAa=Do6L00m0U73|1 zrX%gPG@aq|m7keCw77FiORe!)D9)sW8Cp~N_|X+%wfgsFl2tlH(?KL))aE&_U9$crXn$Hzz0$X^q5w3Jos*^I7UKsiTg=M{kH#AM zWo+@1K<32te;nt?gNI=`$c)ONGb(E#%pz7nM18CcXSgUovPPvw3p0LD%lpDty1*(WPe=!9Nj9PFk-85Ot7L3S6S(Rvg>^PXVlVop zB7JKa&l8$Owfb0*GZhwLe+;`JR+(TJ;MLXjGI?Y$479mH=4i9b3GM4A=V# zTI3O3Ib>hi3?XLMp-!1^vWBe%R%q!vRB*;u7E(M;9iP&;I^!F@Z|gdhN%HH#t2 zY{BH}R5l9zGw~snh`cq6my^5N|HN<=xwQuqml`NAH6g||VJZ5JCVTP_yF}%=e3RNV zbMhBsgUh2yH8qKmhZ$J)NK>LV%fs9fyF5dxWk^T8RR?uxvXgxOBteJS>mPDav6GX< z2B2w}6ORQ8kKgduu5tpcVqx1GpIYN$;Zz@SP7{!CGMB+qY{jo5Z*!U7L%Ze( zU_UuEJVGJN^(>*QDFp*H2fvPy6c*o0$P$Pud0jDZmTEF?_@pT8*-Bz1dHRGmct62Gbe0BYfiHnAtmda|vR2CR%7Ylae`Ry>HVw<%9T7$mOe!Y=Jb+N5IZ z;Dkb1*3*K4v^7YfQ5;q^nQ=812Rxl1Ms6cZNm>UzHPbSBXw|fqYfVz}N)}Pbl|3fQ z2Lv{l#QK?r^RCGTZFAkAE^)A^26pbOHYi}@!F|sm)^|jV`{V!`&jAt{S^(lz0C`Yf zJFzCzRdlk&Q-}GJ!2Q)<_&&iAyw)LC50~4JYn;;XjJc%?Y1r|B0mvBH?ZB&-mt$r@ zK%odS>1a?aU)DI~ziqAu)R&JyWPk=@VL4-wod+8~SjY(1kmj5+4qdg3Q_HClo#%`l z+-@{3-=#KM9kM$Cr20rDiVmo5Qfy#G*v4vpn6X3C+xqV^ur|6Ve|R+6TvUj@%Wz6w zzhNBVAAQf}QEqpi%k@NSw89kf|gSAW@Vgob1K$goPVjk5!rB}7*Q z_#zN!AWPA{NM2K*x{*RIg^a}nGdc1p&m+NllP3H?KQNce8r>YSfe)3^ced5Vk}WXd z>EOWg0S~sxz6l_SkQ^Xwk>6rf^P{~e0-d-2VK8FDH33hyZpt9IK#YqIE zHdcOuHvmpUKmp1*ZEO1RK8TNKUEo0G!77HY^~9&Nrs{#{Si!JTn4GnvBn?(Fgui); z2AQEpio9Hc>b_*8$a%~oE&1)Wnfbh+`fPfS-&jW$3`_Z?_CZoaO8TbIki2S~NEZ}U z<#y0QXlsW3-;~^r8>FmRc2OR;$>vwjI-|3h;CiRaWf#LaQIN7$OTC@Z`38U~WGywO zE-%Pf&cj)j@v_saQjp+S)y`^IWWn;OA3SQPsJqDrj$m1{t|IZdRK7Oc(k!LCEk~j( zQ{GzHwPuZm#yX{DYlKNj(ShH!hnP0uh@TIrR+Ro+GcEkgHQ%mg4MZtCY0bXFo57f+ zaDx*l@j829UII z@l#bK%bBENOd_F#;V%*6Vs~g3$w^H`yOzW1lV$B}vKGfqv(y&GqFPk|oh6?brAR1{ z6}OlSF6&1zp2`kk*Iy>2tUGmgOUe%tMH<)YF}?vj#d2=xQ2klw&v4R+C5e`a$dEIF z>a~H}xF6@X&CnK{X+>!_8Y8q!A+3@Jr|PC$X&ITW&$x&rgHb|UFxptymD+W7<3tX+ z#AdyGz^Pn{X3-5KF*(vhOHRaw9G$G3qz5{CmRVY*rBATJYN?{E)VzJG0*7hUgpo5V z+%i3OG<)9|jREu+Yln>WSyHPD(=L)w)+}R4xJeVB+((`B)?6Yvx-9Su13j zv@#Sw7*k^`$kIX06|-p$KFZib6rb+=MG{(%HOW! zuHO1(_Nu7HsLgPSeeTp}NZ>BRF&8|nGL#)`Fxx-gPX?@XE27Q?6KS3OZBUv3+*)CnLE&=^kO7) z?{Lm9S)XZutItx}1)7y;jdeL+Yz?0mk#7m@Sy#c6=U5iB4~Z@KiK3>86E@zNc0bmuC(bn!@LjZ0VQb z-|)=_q7`AjD#E@56-J-}GiU0H6L70?f=9i`g>yA_&t!sifV{1PMzAE{RFXa~keE3z zh)voo#%wsmQ;@_wu{lkMD*fS*Dyz@vM?%c3x#yPLES)d&V=wwzz;arxQQaqLLq=Aj zwSIyWb6wuzv$h1hG=>g2wS2co-DDsaot9Lh2YoVeTqC~-=hJdI8w!TGB6`^U)9c~& zg7q47mJYtn@_btNJy}KdqGgKiQCGEnPCwjh1&*zuFAN3$v&J-0D+8Y@<oTd{>IiPW^Ikhc8KieI z0w9|?v3a@!Edi-qSS-*tQbqT<3%EqbV^cbg<7s}3HlV`j05Wk4{O9?>F!E`Bc5*k| zfx$3Y{UyO?t^26!z~E?mBnZ}l!Qo{4#7MFUQ!-D(qt=x}!7+w>^nm#=4+WpBS&-j@ z@Na`CpQv@HLxnEXEOXPe+^1uPiMPM!A*a26f($0E=^@Y`F$cdzp3E^|947OZ*2MV# zLpve`1;8$yU^#;?Jbq}c9@qr0T}G! zIxIt64m@Hjx=}cXsupvGMVD})7nZre5~P_*{wF`sj43>&8N?9Hw4sO}57u+YiKwYS zG016@lI}?#-i%JZUAD;H+9DhHqo2nKp(B)5smRk1NFb8vk*pg zpNQcSuLAJI!$x!nM)odN`wmiy!bH{K-A79DqII3>D!3LzVI_-{B*u|Ru{BcCE+=Dx z(U_~akd1hP~zRuul)1PTTa&N|5O6=0=p3U4rWuZQNudakW69W^kN#D3E}@5$I4)49e-an3!qr6t2eKjd zzA!>sy~v>^S-*-1a!-uNRKlzihhK;>O8Z=c8i#ItCqb=)j9{w!077>Q-zr8q!-gwj zN33?faSNle^wSPM3+kp>cA9$Qtm_2sLo0hA)Mah7RuWwbP{;j)6t=;@UA{1bE~v6u z0q~^b2~`U^X>x13h8hZm0o22te9R~q z$|;^#52YD0Cdeg#KCjzYMUu32J|kkX&*706|G1j65M3apQEbacEZ4{IgJDwAh!RK9 z2;YvBB~X1Gm(t#@cm7+w>}Fy-FY2huSv!Ks#erPk=?sXN6rpCoXhJ)wHzV(FdvrLVAi9 zj;2@(m{m2|oqXs3Tn*=_;m&_x0-3)wx~Nl9(REcIvum`WQ&hBGv6j_i+2~<1f&Lj# z8cN5~byh=0O2%Cq)m1sWYr=t=vZ8imkQGfkM&XebW08}D8oq`DVH4q0P@;Ul4J1bp zwY?alCsROyoY+(j)o5iN)}K#X0oY$xSAvXjVxW0`-H(kC)jPW~p_N_5%_>Qu=4l9- z0KoL7g=FU!kmsGpjYiug&hyEX^M$&eQF}D}Yqk}u7sKdcx>VzcKMNcdL*j&8+m3O~ z_D`&Jq%|dM5FiqKaVr`b$CpSc@iAohsWygfx|g5$hj9}qNqtb)!R_=6*$e>{!1-f< zo57mq)u*Co)*b3@cL7OCDR?>~6?}1T z(PmN^70qg_P#=1i;-gT%-sl8gxdWcL%hf{{sA}!p&{zDUBSo652~+cT zgAG%cDn!G^-le0!ULtT!;ZrTxTnbXl;TzpR0AyXFH76x~eso$9=!|ejq3jsQ!V^Rc zCT^d0gs}CbA#FR7tnt18f%%Dp;|}9oxGHrgShMwxF=L!+c|v zor>kwY9Ull*G1Gj!mtWM2s-qOTeaW-|dbYllo(79FAE8(hmzpcO_@ZV+M!m(lg3=sBYiy6n48~GjQ%nll^#R!wBJ9Pg3dphydKEUEsyU=0$zL<7APbwB zT*qaGLK5os!n01dBVBYA zS)vjD!Ynn|xGG`RFnS!|VUZoy?FH5UUtIu%@$8Tsv`|-yGhwova+JN)*egrX;LR!) z2T|1$0HAs#iH^RQFbogV`VUwnonTI+q=sH9q2ll`iRX3Ac`F2PCJh@K$UdtJw=rO+ zagK2pqAhX)Bhouu>SlOJS-VY6yzWO&Aa`j+q8UpyS{G||NkXS$2@=SIuYrVGxG*Sa z73XYE(veenc}3_3{`QnA;L^u5CCr%k`AK7gchxwV=l;Pw6|{jsOQ^UaB|zf{7p8iL zTaBZVlaO&D(0l)4wht1YMyK_>1HDl}dyE7+CyAw%_<$YJ9R!asjhP`-Oae_NTq$kZ zc(*OA@;4|Y?b;Rq&9;IeZ}dQqK9?SJtng$BBfSk<2*hOSg<`sXo}K>aDF_Iz9cid% zIp`^;Ix$;S;0TiALEWJsx4Ka=julKDthXPGm};e)&xje?VNQsn_Ql5A-fs zh?QV&l_U^QUJF-$l)KNI3y>-yBiW2BqCo(oFhwh_Nj5`CUry_c63O|(iPqxZ0!BVF zf(liooJ*_d&BieL)29}ZucRD79O+8+8sClCI32R^@2^ATk`FieiMy2%!jEv{5=5Q< z@7HkANV4tGJ3OT<*B$L*M}T%9CVowohtboio9>~Sn5CDqvSq263_6Y7-T|7B9NRW&Q6lG8PBtrrGus^84LktM;3VC68 zJ8ej-z@+>|*+SI&``5#y|3p5Iy%R2#y^+VtN%qDQw-cWrP#m%pv;`CMxJY5xs&4F4j7L%kq#*4B5%{+8DFO|361OVNJPoih#o zV|lj=F25ZTYADpfSeetEnCF_(ov^PC;}6$SAQZ4j{Z~pp^+hBQgn`a+X>%i7gV_lf z;U+^Uw_;;TOl3QE=d^$>_o*ntQn}Hw26v6hs(PgvYl+7!){-8Zv6gg-FHN9i?^>5{ z70>f|ekWWRceq$fwQ?Zwd(hJ4ASdO^OFXU_6C3)`Z_TW&7ih0nKCszR5Q%YaT>?Si zBhssk28j^hhh^Y?8}o&9%!e9@th|x`621shMuyiM88B)(GWepA0o5eZ&4z%8WM=ca z@>oq_vY;8836AHk@a8+CFhmY4bg`W*NDfnZ)qEM?2`Uy(WKTB2Ev@3xQqL_7Okb?^ z+x`5H+5Rpw=RPpkK4J$PpViO0xO}9xuUK=iR@YB|ZEyYb5iXCPs5W0eU9|=JsnkBE zpF$0;ga);mWMaIp(Hx$t?E?h^($J)Ac?A(asX)0y!9lf|`uT(+oeu?zYo;pfPslRf zhHA6*vw$eG{KN`EEokQGr(f~J1r2c;1r71s1P#;_G(`LqG(g+2pKiX98<8eSMJRD`i8%6+oORlhr?ELX zBU%5{rgPB_|G>wg;7Is{pQEH$3~|4QtqMP1E4xP27LBN78W9{catuGruIBmb0ALL% z09ZpFFYOuYU64G!ZPWGsXgn>sZtdF-6-M*s7;0(`7Y;SYb%jI0=Pih%P1v}vx#p-+ z%~7SA>v7F>yXM%tQFE1Ma|6js-?-~RWM6Oc_^QY64c`ZLR4MGJQrJO0T4vN#&gO`dr3)ZdprV(j{pQ@?xqxmdWOWam}Cygg_(x5vKwOR$@j{NSzYI2Iev zPHy|k55LZeJ7blPr0)ikFI~L)jWU}l$tyR%abCIE+(&|4U-H#U*1a5##xs-0{&42k z-%sDo&|V>D-DIpM#HuXt%$-=^`H37G^*c72?bv9RVuD zgX>uvB5{6r%bIss(!MA6^g8;Yvx>fR9ew9G`a(kuQ)sAR3Jo<(p`nH;T_F0>J(7ZS zkE8(!!o%UD#8s3;9G^$S2Z5K9LskiL`WyNK2Q9 zv~-C`OP4s(`fa4OneWqX<~!WYe4ni`^Zi9!+n=2C^48lBu~BmElWT5k8KxfzX=f!j zja{_5FpA9Ey5;3}-_LvnXNt6Nrbr8CinMU1NDKKyTF58TLOzie@`RD8S{PH5g)v207*mvmF+aS#{SOnhTkm{+64@6dtIvDl){j~qOiRwW zXyQU9>gmbbk6-uO7M*U76ZL;$QG3gIn{Gh%^(H_4&9^q;@s`QHk3=5KN#6SA_nt3F z!+ZFJS6(VN$H*Q@`=40U-gv|Lx0|Sa>e{j26`QzzB&3~{Jo&_r??M5aovi!qLoc+5 zpFNWHKe4EN;^htZnW%mC+uwQlqt@K}lF9L_R+^}N>)anb9c=9E1 z4U+S}bj|~Kg?yv}VOnzI8+Tq2O4i>0`>)^JBHH&z+W!byYes}mI|IYvPP_fAwA;T( zL71D|`_o_j5w&(8x#7aeZ6Eb&x;J_GshjQ~V%wbL!E;}J;-ge+QOKlwp^!=ULLrmx zML+FMtwn>C?q!CV!`+(fvu2q2OI8filNWz@-rZ(~`TgqKE^k@vnihSO)1r@dTJ+~@ z2w>Sh_GqZUJhBH}(wSkRY02|v{^GHgY0S($M>%uP(K7eQ3QrSPFj;X{=z zYC71dImD!zgE(1peaRVT+;5l-8B;aaz1h~oMPw#VLh?~&Tx7)lq6U~Tl{kBwk$ z6oun?Bf7f-%U3*q1bZY;N5#a$kM(q9TpOv?;(gd{(awY0b_le0SMQaDwzp}a?aeK; zd`b5%2(^wkMa#ZKT`|)=Sq^oY7L#EgKUni3eiqnPgKUg!ZppUC?t^T59tOiP+iu<2 z5tN-tedDxj%lC45F=_YB9n&$sMOWaa%%n1Zv0o9Ah+TCdR22V)ueFA)s>C6 zG48Oz1Fu)XY8qk9JZIT5T-96eY>cf~i8rBvkx`*pp2}R6%~sU*+Fot!jk~zy28v6R z$8wrT1izB**-@AsBL*R<@Fz_BW+&3uRm~2`K$D1Dxl)(_D^^DW0iw%sPB#!c6Zy|k zh%Cwg_c!pnOZ=+!&IDS4ePfHsii=(48n#}exMN9Z{t!Y??7IV}l}dE6D6R1$N`odG zoidL+is?yqnWBv{{S~8&FITv%2hsic4 z>m>DZc_;Bkq%_VXAn%e;aw$aYaZ+fJH(qUuJ-IAM?6Iu~^%u8Dy~$EHVreLNy<`$> z(f~n%1a>-9ES?QP>0qn$j8rlvx#i*sA=>qFx`~~QI1@=k zk~=*%LWHjTfEdj$d3_#gLEtdg&V znmI5S(<7LoL_tFO2!H2fJ$eT>P9@v;b*Bp!(h>DO!*vG1LG8h=tTa)Cqe7^S)iKV) zrI%3P;jEokgOzyf?C0YB3YO7E9dHq{B2^j#-Pl*;De#$G$a#bszDDlM;W#fG9!L)$ z@`6^IGK~(P+Ys5vgi2V(G`R!6Bun0I=PtX`WW1!5tkdB3l}qKRcv|{ZE|4V4r|AsI zOTybJbQ#VR?>Mn!)nuc{YV`SgT8Y~(n z28-mA`U05JvU(Hz(^av8+p8gR5vnrH0m3Sb+G`0!_JNhl?og%%3SL=?z%LzvDs)he zpKcbsf}EqNR;323PR%=PP@>Z(3a3wmWK5q-JkT*XoGAo9jin!SLLW%n%?d_db6!a! z@n>>P-ENXM?Rb-LmkL1R{k#!3poy}WrD*QmA-Ot!F5=GUC9tJ3?Q~jzeEcpB@%w6^ zlrU$;GgsASw9Jn^*^EL^369X2`y9>2U3y|(g&)n|E>|k<(wH@0H9krb;cs^btSL~& z^=qSnp`Z!c4)hpmVqW&??cxA=)@32&oq+Hd>;b+==@HcJ=CS$9^oYA|hAA%af?;^i zVx9!a;^S&1Mi%^(diR-t#BBpn&dDq~)Xf7r(gLlV5>Fv{iYy*=E+Q~+QHNpN!57@k zT}MmJhecQh9Sh6bL{l_}Jx&#+Sx|1oOO?Er8D80WO&n}pn}}d_wH!LEV$s}lu;6x- zY_z2Zo?v#0Uq_6mpvZyp-QWlbLGtPpNvEV+gUD}(rOF9S99>)Rl>cOoXe#TQm?CC{ zDn7MEIR(PVxsoPaaUvG3X)$U0Q?PYkGIY~7!uhbs-sDD36!u0P&3Rjkm~)db<1|EM)(KOUSc--Lfu@UPjr$ zs|8=9S3-peFds$zaEG*g;%syD$Mh4+OTI_1P0jgwp7RajRW1e@{EuoLoGKlYW2ZW) zsItUnqKj~hq;%)y?_DY0 zdAn3Pe$ZqbLx*aJcF@Bi^t+h01b+b;b1&KNE1@3qR{Uf&@}7Az zid;&3^;VziEK;Oy5eel}hufW*Tx31K4FL%f@A!Ol+E&P{MY`c6fbJ)Yh(Jtq6@A%doYrTbQo?uC9Md)Er(NR6r z>kCpZRQv%0U=Q%^WHQ-_nnF#K3!`UwPTl#OpP*W3^%E2Xt$u4p zUMKN@5)3H8fB=Kv=eu{+xwj>m9~ss>_0m13>eP?Dt9I?$wQJWdI8451rSX}Wn=i+7 zw#i?*R5OzuO;Muod?-Z-Ym_nz0+Ki7Z)M(=#y}^M+hyY}L_Zl{x9?Q6*}UF$`^(e! zBciGUc+7Aan0_Bj1t%>NNF%E<8!MNt!6 z;i**RM}&VUx5{B1A>?KD#{Dlcd*dbqredt_k-?B!@LVujjP12^Z&_^@4WFSUq7g@K zy#0D>d77CdN=^E``Mp-$)nI)HIkReAt*uI`B#gJI2Jghf>Han9Vt$G!25-N200=+tHhy66)Z`BLSvRhm#c@poRqX z*TGnt1t#XqV>ETzZ3lONI`#pY%!;~GP)F6o__Z#UI&hHr%y`Oe-!q}y7tup*El+Y8 z%YQ)=xj6g4kvayP+oRLD;`!)ppmoIjQl26>+(visr%NJvhKn$5+9&ks4}NNRk1oQL zFs6$z`4tw@bkYU1$&!mOGYx)?LGc7#go$7lk8O&a=rh-nkj;(~hTAX^pVYeW+!@(wf&iPHsox2-Y>T!tw_FvEs7Cg^C?_4yYaY-J~3#t49WJ$ z;v0GdR;_~?w$w$Kw*#JZZovTYkPMO$JM;)->SPHMk_M<=UV;3_FCI650!nQF#@8k1 zkn?Y00b|Kc7b0Hl&WJw1{LP~)*2nW7e+5@2-5wq{nyq(_f13dwe%5kc50b-dcbLwFhq+Af_7ZrP}n;`d2b zPzGojZ+U_|pa3wYFP`MYnW%!5ExU=t$r zqTC{ZLj=5bLyA$bGdrEBE9JIB^e&L3m*l6U+|8L8+)D`Hq=50RrNC3Gl70{P(X$K| z0i_w|oXO6Jd)_9hY=BrZSc-RafTW{?B7NPkhkUDs(5+^TOJlf!6;9iLlyNa%%`SDeS_dX?~is+Y3t1v8l-4v%1(0ivJ z<4e4P*v0S`mfbprJ+JEox$soynvnuU{L&u6h(7&+NHwOg|xiz!~)B% z{H;Es%DGPx?066dFNZ%xF!vxnuMU5Gl^%1j;J2rT1 zj~}jvlSJ|^AEcYIoTkd;qhUY~S=*J_A47LBapfNw*x)JZ-p3l!*w9s?!ebd-+ZIpssgIC3d4b7-zf?W0>jr~) z)eBW}*sI+3pBW=^0n^&kYfY6m0Wp*f#4RRL*ln+dOAfYY^0(EQjD#Qs9M&>b+c;{e z&9qZ*CDWG4Su3xqofj@#u7ypmk6hSix|i9xqh`}wOnYx889n4q9lQ;WQL5_k-0*rj zL-&#Mz(Ja*i6~mjRdpgbxk>dZEb+8cp!5)yc-&Jiz`K!iLrO&KpzCAm3+kmpYoQ>i zQQMYcET6M_3l=;6Hc^a(H(d-mr^aZEv}gu(Gbi)BmksTpFIID@nY5E90;ENHQTfUF zovLjB52fgK6BDgbxrQlfkCl({(Ch1F`#$Z$m6}H>exfRFP>s7HG?eCd)gEf3Bf=Y( z)`T|>6vx?K)k(K&`cO5p^r5jI8K!)?*PX;LBpF=|&j=^fDO=`KJJ9X!4kWj0u9Rp@ zr_9wmo!?AZDIna_W-Rk{NEUA7;?zZ6^q%~tHWMJ0f43JsmnSG>637^t+UIz-DwSi` zm+-u&J~PJ)_YOtgc^Z$-g4THFf?73r zU#AKFb3cf5;B=ci=ajDOOX~Q;G`?}p8Rwi^;)iYGoRM60%Wa=Y$*O=Jpu=4Bc$RmY z8Z+BqtC*S6aZRVEtJsV7#+}Df1?1YWX)o|tmtxqagU3E@<6aU{YEx!pvJ#WTsEwEn z2lFqWA^dFJ+3fk?0_Qm$VsG>zH}-l6*)oDouPsu9c!qYy0ode1B-vV@)t=H9v05#+ z#A^kacVo+o*Vr=J$@QwJjn6IGsNc_N+*Two;H1C@|dy#6~DET}IhbrfZM}rn|_2Y#jX+{5P&Ue-FUJ!vWseF0Rpx?GEMd z7+||6k*nF$U_TAWZN)Iic29{ro@FVS#||)=`69l1syV=S)ecWmtSa*CPK$Nmqti5M zjTS|uTKlQxrg3$%19^tO{Q5m8F+|}Pk z;I94#AK?w&S&!Tm=_|C2qZjEb^4CKsEQcfP5%uN8aD2sfhdo#MHwY}21~e91k*WGM zd^?IM2)pnaEIsXnfGSIq_#N{#M~*tZ#`uLz!1yYz8p>ml6WOYb8*pQr02ajLh^0ng zqpDtqW^K8}>eaV`v`dFfc}YfkAB=Q~+X1NGG$|jt1b1CXEB4AM#)Rq_CSrsau~$A% zL)Q$vLJxuUJ-n3uPx04xwcKmcik=5%HNIdgam)(h2Wiv5*%0%_K+1Y}=XG{O#KAJYfb--}TlqwlP&mXeYghJN=(7%xMR39FTvO|Geq!EP|v6lx*iA-cD%8h08&0ThE0iItg+4YL3U0@E9P zfPt&%o5~`hjC|{ByFM&4^C32v<5vcO&l|e>t7-97PEiYuh6tnw%*`|5tbrb4(Gyw{ zMAiLTZdKhF(pXtOv=`LNEIf-clr+9%4hv@+GiycpCtGa-HePH_;yULQnO-AXctUsh~X z7RoULDDZKX-FD<{#-Qs!tgj5T8A~Y2+J>chRe**(Ql&~4Nf?R|OkIIst)|A+j^04) zlgTWXLMCIN&{01tF{n*FM^zBl+V;Vxfpp!VlisgLkCc?sK4vLOOJCq9`Hsyrd z`)tZ#!s~Z2xK_XlPcti%Vn)--!%zj<0SZ?eXl~WML4}T)S6Y6nwPfzQ*{8nqt)0pLAs(0? z)XcXaA$uI&w#|KDp_b4D6LCv<^VuL_Hqh>#J7vVNn@XSxA~NQ$MQc!;r7 z7K%p$N70>}(1#D3tCvgyIP#6eA;)GSBSST6ro``zu9${k2C?TM4Y;G`f^D%!2Uu35 zMP^TAgqVlaUXU*p(B(bT2ruV7hX2zH8i7wjA z{jSk;jnqBo#^T`+JX!?xD@KG&f@r^_v$YA8aeprC>!^1FPtp{RS#(On(hIs+mYPio zZyQ~6_>~XWkP;j3Ls0B?3Za?$D+F8V(JcP{YL?A3(}%8G<+Nl*uc(clffH4B zu1||Cxt;5xUQZyiuI71~fj?QIM6O99Xo^NN!AWMnnShNWUU1Q6H!hYmQ2wiKwTy2$ zeCRVbL*P|xZ#mZC#sd%AvM58o>5WBt$0d!+1m|GW?#IU@Db^vniwSUGJ;Nk2Vf6&8 zfg7t8U^y#amU@_f%M+u{qHOR;HLepZ{aV6`Ri?8nVEXZGFT}3yp%?j#n!W#dxc@rt z^{|{})m3U(PWo1y|J3zP9fRiW#+%H_h zaK8j;sAw^KR_k%eMliRgMvDGDR!%kj*N)c+F)mFMON1Q7^U<^teE&NtyR+oB|-2pZOqQd zj8S+WaqKUmvoKIr_J>GN<8id9pF4Qjy{Ph7)e_8>zd%(Q4B&t#(O7WI4kL?Ekf|th zbziCG^a+EUfuG_%Ub-^hjoqAWhyQCz@R`A+nrr2-4rNz~k2?Hlim{#>)5_(CW@?0^ zPunE_)pz}AZO?_KMtuBVX=+4unz-iO&7-Cr-zWbuU!CXCaind4$okK(^+IOU9ySk5 zI>M%Q{>w58vfb=IlQ{uXH`$SID3k_8ZD?~Yw+)d^`|cPOwk3zKx!$Yaf?<|k6Q+38 z@~YeABA18}_l?WE7(QTvqXf384FqP14iRtV5NoX*zfFdzNTd>->vvf|vLW_BcCOI0 zr+5DgWqB$D#)Kk*o1pWw7dJ3oQ|NV-CIy*`#^jeF|y z40#%3fsCoT?p_efLUu)EA!!;>%3#rc1F+bE#4Xw=kvSeKBLlFI(pOMtR2`M*7xORAQ&u+WrQq57}EW%%k)1 z;dGfgY;j{S|D|iX?AYpIsAB$L7jfLW9RuP3hHt^ubY*aN7N+IMOf#j z5Kq8O=Ad&cB%>vKMd#=+M#J8fIYh)AXc%niv(QsC`>Br%NQdRz3R6Bxm=+jP;YBvY zvJgQVp5z~)2+ChD<3Q5RVM0H~^YC&VKC6`r*y)=9xk`t{AfQog%gJ`3p(}uB;2|&q zYT9ur5M!>U5&)v5s{ZkQ-OLORvg2M!@DFJF-6impEm|A% z+-#Q>Evuv$RSdgr1Ptm{BzGQn?jCJ12s)s}Si>KIwZ#gHFp?iFRxlp4-wDKS%VgcE zjs!#&&8f<{l~sWk2iInTT~Ztd7-}}DVnz$9y;N~F@fwlAy*;3ow}mFX9Q!!Uf3@zn zFNaQVYAtWA)et9uT*Q0n7}~(m+xo zURp$Kry?@8_8k`MUmLZ33WT5JAEl`n838TDF6@*1(_tA;Q?ZUu)MOB0 zXea9B?r)khi!m?`2^?(0OI6+Z`F1uQOt5CcR2m`U#%mfW6M3i9Vk^cu)=6x;S|y|T zW9rViX)V#x5CCgME^31(3QX+u_$xeJ3Y-NNJk@fit-ElYQ| zaj2dFrh|AUWiJvKK}xs4!a6M|?UD&i#+6VUg!uXOdofd>hvHW1ocL1=N&{)K{@jKO zR*Dr4Rgy)L46JCuhC>!P3xHn-YcIWnlx>pme4(_uFgJc0V$Eifw=KsgG=4yq4Ip$~ zlTtytNCb9PG2yU_V3g*;FKko2TR%<>Cl}O`h%WbH_bh;s4 zNV_q`+;SOHKtz|R3iOch=YHn?tCc^{0RUyoM)LI&1xyzQLBr7B@n2XmurWYI(>5Jw z)OVY+sdOaTP??*CJSr*|{M>nq)*({4R&T`6?uaHf1b}3f*g!odX^0Y?#&uw39k7&dk8jFVlXk#+VM=M3w15vf8EM zI4wbqA)zd~EIlWMnUOu4Ixm!p<-Jl<({fZcaJ8xe%O`Nij+P*1FcN^<3fAsl}+|z{x8?Q9^80}ax+kDGx zcCd?hDRWI}#%v10db;_RVtAif;mZnTxES8AwCPWgKVi19t33b^MNkS03zVhTIkqY6 z-WPEli-)<9ZXlIqqX9(1LATi_Z8c0RuH=qcSk>SFX;8go6Xk2uA*2;W!4brW8LlwU z_<OeZv(6m?o;32z< zUb_}W()s(LY}8$nERKgAct)*pEzXu|;0K{X9)XhlgNkFpH{X$@fu(atE+aUO*6B4_ z)*s;1$im!DIjcgzJ%lcq=sDdjJf~aKVK@>lumI_flxcUgwu^OKBJlAg&HWmb$oT-I*uA*BwHwzeA{rI})H{+ld%XIO#tFzu$* zXR^oZJ?=y>kY=)lIyVVYBZ&cT0drzGm|%}TI|)B5v=pk{*+cZf!08diV3m8qzF=Uj zCw74kb^Ig|fFjewoC4T_SoZwGTIdUj9$T9NIxoieq1zTV`+CO5$BX>+$2pU?>^~mg z2LN2KXlo@Hnq8u+lPzr>%wY6mmaTe$cD}>(`ysn&>0(r&07tbLI19@Y{BeGQWe z2N3BYKbY7JF*B?BHGy@i!xY7fr&!n)l&!ojEZRSCq#=5 zci%9{bCNNcW}uMEuwc!DRzOpg9jW8>PMDp1o3%L4$g^yt6lj$9+)-}4tJ@OLc83o1 zELUwTzIekCj4K0h+XnSQ{-S0-z=*-M4lv5@`g)*8UXkS=QsX5LFan_XiT9CN93L8L z6v!9|mP68P*{D1;B#2Qy92(NV(?C>vmiP?tVb8LJCA`tlYfky!4OC_#iGb{7l5(r3tS(89DSy#Y zvhcJeZ6=c&Qp-_-6h{e7W1GKkNe7M6DU|^$v-80GXn?YIO|peYVR~JCpCP1MzRhM* z5i%R)fr?am)uKoVHj}6^vJ=S+O71<6()a8sxUbv-o5y`u_oYQi8 zUN*b@AT54w{N7@EcKBei2|@P&RKzf<7v0&BgGFz)e(*q1%uXC6`n>XNm>oS>Y@BTn zy?%D3qR%OMYIgi!v2M0S^xE0giax98Nfo>vc)B=y+rDy3c^+H6C$Uv&~1ZRGzd#lmUinUqE3*A&3= z!j|=qKJ~FA|B5z%wp1aa zh<~Qo{L5U%O8$2KZGC7c%vTSqg2TV#NHvk7_$G;8QLJ+*-+<}B<0~VplvWMTB=iQ zxVZ-^vYaGJLw&Q(gu=zk1&iRavlwy8BDnl3M3~l{QehX0;gDHF2UmwQ&CzqEWDUxp zJM-U=_86eNp1@568o90lys{3nKG~KXy^B*@*zsd zLQ$J^6bm%Q%Z%bc=$hntg@CqZvZtH3ad1S3;s`84{RB^j;8PxaBm~cR@I(kc>A_%4l)n!1dd6Reczx1e+q|CkSC#OD zzczS1<*#bM$Nja&>jJOF8I2}jX-&kxW9kX|v%KPR#3wd*lLn6e*NZ zmksuLfX2ml#p(;+7gi6tfV~u}7`X(iAc_a99RtIZfAG$cM!W_R$@AoAz9n_1*2U-6SK6Vr)IagIh5N}Kc%4-XU0f< z915;SaS@(vb(RaiyP(6=-%ep_Mgc&Hqh^GEh(aZcdHbt+`b4IlmW(mxUDwUjYtZ0H z{7_ht{32lFzgveFLU$)JcG2avOMV`5!84 zfmHhzF3YA7DY~5P{_?(0J(iT?pcI7Kba3OHJKQ4$$`uomb|*pHWwf#o>x}RYXIIN! z-&sdxeBxa*(rYO^ebsF&RU|2Qjt`}i68z_y@mPU0Y~s~5SXgaqvZc2|9E^3)4BX}Q z3~OGejZfd?0mN^_Y?H-^d z2y6qEIo3mhi6aL&8xpWA4A~JxV7*lktsOF8#k16mxN1^LSz$`Fb?(LtAL22Whd);F zO-_+odh|lDO0*2d8xdDRJQBpx_{`IvbE-!S6EB7Fbv*uvDFBIx<*f4f1>(i?S~2|= zkH?-J$+b$~md`^2h=ZrvDk|EZp;LwoCy8H@App8ayebF?G|JT~M~xM#94?z)@(CJI zO<m_eQ$&ZqRUzUTMX(=StPBxn6(P0BM2L7= z5%T#p5+W8Af%>r+3K35!0)e1d79!3NQ66PeVc1(d4^sc~Vnq-d#HmeaKx2W-@Ot!9*<>o7>}HO;<1A6;W5D)o`=@(JXS#q zJXW)m=P}7rp2r#pfX7<;oyU|G_IlZ>2CM+L-wD7E7bkHSY( zyL8W6m~*x2%kLj%l=h&R8Da*92%~(ODBN}NR@hseH!1g6&+w+J7%6Cpz1K(Hk_l15>^FegzX4F~N~uYyUol z+c0dx_1=(&N0`G1C%uk`<(hzS(Hq?_X1gPs?v44E33TIx*Y$D_PxheX9e9CR?%{P9 zeG{JOO?bHILBBijakk3C)4kP&fWTCJKOu1*< z+Sglazf<1JtB3!ljC;Nkr2QU8$N77py)Z!U+g|V|^=CpE@c30cz5?*TuS zk{m2HR+CD!KD&w$Qk(rGq!mbXU(eGyQ%cat+K`DPPQE6!)G2WQNXFSKjIGrw$m*S6 zmx7*}GHpTBO6((Qx5{*E`j!BJqF#BcHdtq}f=D4_fprAblopsIpdPfq1OX9_1#$wS zR||{~5R0`yn}E11wSI>xR6(66-&pwX=R#CMRMOc}9U`l-gffdrW^#6q-!V)H^&nYj z!GPHCfixWsVnb>RE*67Heh~I5&&n`!RyU52{2B=_78^X)iFeD-7E`}`sWPFOm-c@b0qL!qrWi5B1PW0W9V@I@o z!u&sr@wPnlXg`HV&diC66*~~+`jaZlLY0N{T}y}Cu}c7bsDj{9K-+rzyYKYqwBbh^>1Oi*ope{5)&iw~q(oU|PnP$i z#!!THce}f$JRBmd<43w{%LhV)kwD&^DvyK+BL+5V$_GP)kp*&IIUgd7K-P6Pln;dn zBed!C@6cN0HGEjf z-{!C;xPkBle={L`9pM%Hy(olt5nj&UqY)^kLwGx3t$i*F;cbMq z^y!807Q$NjbVFF;BCFh@2w^crW8J7PwOwTWSa4rRQ~{U* zgr|s9&a-mR3M<_i5S7JaSF)mnwzl6FkAmG^@L>Nj{-yuHIr^^g>e@uAT3B z!fm)Bgr{NkxMK7+GWak60KR!$07DVftFLUWVN|i07=Q*Ft?@pAsr$Xy$kJJ3JI&4O zB9>JE+IPSYYpt$4OPR{qXfx)oyr`G^D5|r-+Y5@#Q1*l_v!`VLN9}wc` zC-|ju4<6U($lq^G!q70cjC43n_8v~^s7ViF>M9S)Y}X&I_u8}BPZsUj#!uc(6wjX; zwUcybAyvFbrBT;Qd7HB*Fw%m+nE(1c0xDiH9U3dv2=(^RN%L4Qo+4tsCu*0d*Sf;% z#iatoZL3_>yVz8{;xBdv3(z+c*6}o5A3;^*is`k@?z-+@sJqt^4ts^eZPLW&_YIQ) z2WMy1XB^tM!&B4fJF>efGnknk4!U+|>_|*&pf$KAxsqU`9OG>pZ|oTG#^H0`a^8NF zx3nDQ?J9d)&f8AjD4;-?trL7X!CE=O+co|HPybf(D+o5Nl_9i@joHvFfZzXT_(t)WtFuRE&{|NcjN;g_AwbH9Cm*axw zMo_-eK04q!UPja&W!BD`oq1~nwyYjbwuxiA)l=lw0{wMXuAun|dlRa!9jFUDRPbxa z3iofvtm4uLVi{fi7wi$_0SQRG1!SyDt9od8q@1xtyD&iF}MNB(K7~ zcCF-oGAXhNJKHP|ZxX+&w)@d-TSR3LfKJgODwBa)IloC9h^Mwn1#(R@!%<>Qb4l!Q z6?=kM6x`(rVn<^v`#;KcewrD)wmCV%3B)6tNOX>K-`a>cKg~u&A4DCZ(T-&W^TNlN zu}1n~hBdhmu%gidSXk8Q!KgWlI@OQTHt*Sfa99xZgQ%Fx^ZlSfx=;c&%_yiBd1o(N zW~dclbFdZbL~9Wa4q>_&+XqW8B#sa=v^QNq2Sa%TedJ8` z+;$ECP26?6mUPUx$9pSgpZUrkyrYKJ4D*5QFs_=VJGY$^mFuf?y`kg1)w7@f$EQD> zqK*rhd+p;m`2EtKfA#qkjRX15Z|{vr@7XBV_H#Cl_twnb{|o27CAAiA_V0vdR`)-* zj-#Hs_j7Ok88rG{PB&r{!f9Q|6X*miUd?SNL7OPf5_ zPuC{h`q|I@<3o?AaE1lD96La9<(({kJNE7PN>|g)R z6Lrd68ItyUt2Zqidb7w zKi+$BgzvJuS?x^Sjh4kbdLK4ze3+fV^gL_mrc6(V0o zQ_`ft;UqX{0>##{g%76Ve?=>Y{9SYSZ5QZ6q~;DlO+^{ZoBh*fb~;oRs{URzyShD@| z$sM+K=L#elKAFTok=XLRnrsWLy;$jM_kVNkBu3$=CsITa6d1~lfr@k&o@*pUFLaz~ z>}uIU4C&?Vls)wemw|rshE;Z$sorQFK1$<8onapW)d^g0WlME*60|jI6dzDk*Jw`|2 z>w_u{YlnKdZs6-f;p@Eewv>0xz}KVU>qE*rth}oRz8(u-k1Fq|@~#;8dO}|_3?s;u z55zx0ynAEdsbNLY&;1tE_65`hWSDj(9Uq-O%cp+lf{!+qln|cByHu!YIUO#s48tgl zSX1zXq8MG!DA26GYp)n$q&JNU{Pg)9I@sRfjK>z$TI^<;dhp@A;Df?vAmas{a(b`M zIgsHmU=1BzQ{&+|59^XlKR>{V+@qeYqRO&2p zxJP<7{KecNYb>=LJc4x}d>Ivp>(%&%M#|u(NPT6G$~+CRgeQ|=&ffx=NbgXd`fZxj zLc$g>HGA+$f$DUFmc!-zpDc;8#$d|O)O?tyP8p#b)-^P|!8-U=k@+_>SBT)>#vxAk za$-)cj+z|p5fjP{Qwoe_rWXml-Skp=!}+(elD^`!ljX=?H#%Ajvn^pWD7n8wF&tEw zPjB6uzd0~vqnE)FM0u|p9eKfw-^e-xP^v3_cu=BV{r5q6QAVH)t?G?awEegY?tAp2 zvp*K2dD_VywS|tc9>|Tc~f3q_(f+D zg)JS;Q#=l^29!4EwxhHWep>ybheI#)m0B=2!>^7GNm8fj?u5DK%3-YjMMX9#HSI{H9k$TKYCWJ~bO33r z9&P+#;ut-max;ZqtBSUf?ageK)%mRyg*^ zJn(p_q03q65`NLhKPOLp;W|~edmsR~nJmIhQ%(bJ&YnY4ellRdm>wn(LU7zLp{Th{ zQc23NG}={+s6Mwf)dx3bFFuvnYNiP$~AQ?MyTRlP{ghXcNX zwVPo$1Zes8lvv=nhy-7A9BR3aCuVz+I?89iD3bt*$+~pNg$W7t#F0$K_^0*6a!_K3 ztp#`(=ly?WFOcmrN7s4ueZ8Cg(Z#PU;P0&5+106z@{otlBwZXe{i`*|XyQJU@j$LE z7DJsz%<_+?BSQ)whQN6|w>|DZ)_wwSU4kh?ARV80dVGnF2Uvn7`Bx zgo!K}^-d^*DNpWlRq+z~Z$>gvy7`Qr47Ia*GFG1EX$g?0z595GU1K-Kx+ecNC|f&Y2L3z;7BP%|zUGoEX2#cP zw&E_x>ui>48Hq}4AP;rEsZ1edV+9$L7z|Wr4U~Z|uNoaDl}LzjgMX}w{!9emG7{Jr z9E5(6Q9C21d1o)(qxIP>2+r;*#iO3(n@eMRjVD#D@}+nt3cpjsMU)94C9V z53Z+J8yLi|_Hhiyp>AC<#uE~2aM>f*62zHK+iGF!)j-8oF#iQM>$Kot?6pvKiU0yA zoYU+#`XkKl#6MNJ!=@o;lE1?b_U~LzybTm)-714F@;Y%nOg>dFTg73I5u8ysqQoxcr)e4(1M5m1f>n3*>{w;#y zW(Zw26vNO%gTRW-*NdWXnCYP(*A}gDRxGuR2+H?7$D)5N;}&HH%i3^2tES^$HZ-fz zVxy$T%F|U*ocj@tj&RVY8ts>?lHDcv0%YPR!Rd4XFZ$CIB$WHT07~lWZ#8#n&eaCl znN7k~=LUCzyD~!d5(H>a!eO6K|#gdb+Vg0j+-L7I>$- z8u`10wB}o809BI7o3+2-ky;;ZUoER)5@ydPlah+b-$WNL!%gPN0D`TqlAB88ji12zfEh2ZOIiIWN%51z5$Tllqd@o$Xu zgWoj?I!t=8#Unh` z_NLUeq6zg-{)HD5U$g>hlgy=;aF%bW^wKV4v@tgCOgonBlqW$(7<^~ge}$E-Yb3kC z(_g8TlUtPl_BuaJQivf-Y-uhLTWqvlB)0e@$6;4ZSjLv=;0t9J>N447SWjdZMCbDG zm!QVCSF+2AozAm#DE*Y_)?9Wuc{x&oWGfuqA;j|XLDY;ARz75||JokVtvmzr9xc}& zYc`M+l(b^^=4pb__==GxyIBc7=)eSpAvH2{1tC(``$%#X*a1MkJwykVxr-}2)X5T8 zo>1euxFWZ#5?ADbl}Hm;OeKJrVu}MKTRXBP>Lme?=TTbG9aq)mP?faOLRx85(n^an zUkEU|`O5B>s_r}LODn<-X~p=Ljjl^dD;iRlkXDpoVDvHaOa{?)pf5GzHfcq~EczHs z8RVMC$O5OTX1`7pSJcLjOC)Ypxpl6RF0vtvyz;2J>y(mQAh4Wdv`J8TL{9{l6M7=- z9OG#M%h3{|as2@{d`L){3o9CU2rIgmSS{{y3w#y7Lqo_Wu*$ng^ofO(PxuhKM6G`Y@du$~uvSE$Q`bR3BbqjmD)R^ASfkx%r6!>v zx%fnC6E3>jn3}74p}kY!qLY|RxnT^@Y5Bal=!hjT(dO^Ts!)02ml)3Ys6*h<90lcs zmR4OD&Ur@;0J882o4caD9z8?^$<9)HATgD7go$ ziOlh%CiYfFQv8cRzEQGu!RHcE=DF=Ze8E;uNTl82_mVZbTzj9Gm9&S*wd%tt*NPRe z(-ueeqnA_8O!61?O0Ye7xlonsxSh#Ic&`Lo#*|GXL>)Eep8uNqMUBb>Db~a4HqkD^ zN)$#L<=JmAK#+_zx4qA0SuH~3w1VGxiiiTHRslzLV~NOO6>fTgIBRClhWAo&R(I5x zIExG$#aSfV2I4G(J&Lou{2=1&B2-3(?;+2^X~pJ)JRr)d3JhekmfP^tgA{?KA%|!c_pt;bIr z!0%Da!2d`=*MLkyi20+0wibr26^akl&k)nbxyVCWzK~BGJ|rihR?X$&-+n9!$gqUy2ioQ7et;&8 z0mce4H0Grt6I#;vNgI3T`T;sL2BgGc{k>O;A%TPsiuih>dR5F(Ka9~Iizosm3}_(a zPs^)<<{w+TDb0TaRjEt(M5*F68F5M?0fDQf<<9M+lFkJRZ`zizJw0WczMw?tB$SBH z87uK9D={z6?Eew}Q~uZauiZA;e@aoQ_7;t$dpVPh{Il`D9K#ONw4)YvH}*TXcezzc z3MYp1z+PwldeUdN-ER8MK&V1&L1j|AV?|I$FT6;r%M*aGCsP7bhmi&0C!&oKC!n3U zs9zoep+BgHW{iX2oD@-U(5Qg?W2*j~&7)@_))1lTJ}n>o?8lP+6l!fwVhzN8>?t5m z%Og=BSZrvJhG3dMrqWMx@aHkLWNIxrw;^mE3ny$a49jwE+?WsqHy(C}s|*LykildH zj-)*1i~E$9{!a2?1=MmfZ8j5Mnfj^|GyeOQDeY~-g@A4YC6~S9+zfW#DxZU|2Qu6$JQ_SrOWcug&z3ZJQw62@7ajpdIk^?3PQ^+^rMGz1&uQwmQs zO%RJcjVVY-?s;66bs5H@@N*<%Xg1})6B>=7Fuk5qrUfn1bpP1{8fd}m%S`0g@?V7Qe#O1# zUx6ncIKLK`hQuR2J3g5?A?vIm$54v?g;eLOeLjZCa8TTqwGtyrAc`&vp^ zp8uKB=XwM|v<*pFhOW|gm0WLYOSxvrbm%PYX4lK@EEW9dUYm{jW8ox{gKslLLa*C2DS;=c%lxw)>|3JkM5HxTXO!2&qs!Uf5gSK zxxOjYtJ>eMdkN~-oM0&M9Kj)iqwa;sYNbf8T>Z*Mr2+w_1E} zpslL|0~mtXDFsMtpqpV&ruwIS0Wa_&ka{PQmwR-;g0&^9w$YQ0%nG|QK{1`I%9eoTycK4nBMbdXv^u5|DVQ`EGJUBV z8x?ml)~j_BX@XS)(zGUGz&sfIp*t1*YCs@Wg*tUu}I+fcjqlIC< zbi9t?n8jH9k}LsO4I$MLkZ8N(ET>hbgjn}j+3<$aKpiJOG}L&7bRCXJd_ZoQJJbrq z6Ow26(2{E@=mTT3pr%7@XG=BIc<>Y4WbGC|!A;g~w-`9K+m-q!cDw7TM|QjGsgt|y zGAD^`NK8iA_@Ua30roSL&oVdX^{l_j|D;_ghQ}RV7d=*PwCqwcya4fP_mZ)q=hbxG zD9m^jbx@f6<~-rAa*|_w+V~|*o8HvrG;lr-=zc>SY!L%Jj`^$XHkmjI6mod9RKs#* zubBOqjuR_~3Hy>)n)zP`ArPsFb7(A+ke`e{%j`qK=-CfHCQTEsvn=xuXhvG*79qSb zUUuTFQ!}KdacQJ@3rY!yAMHG0EyI8i_CCYbRalF$lUS|A=%Bb3Llj5OW2uwcBta3s zA@qFTk*(kU-WH1iG9_P;5jan2g(S$^{-e%OTIo#Q=0P$Xqf|j&gi4=3W9Q}G-|`Cr zPmIJvlxHxH`}#0k3{isVCLUXzq>F^lz1kd|$j_5vwm=l8ZAg2RvCff6MWZKUm8kFK zd@Ts@h;{wf!_})~`9rQ_n{x-Xlw^0$opzxxE59`itFb$xe=|gFO+GNv;}kgaB$JiP zcb=}s{RlFCu1U*o3O}SbK}YNdaOrJ@v9#T&MA&0(P2N9J&ZQhCeE-PO(hLYODqL{N z^h8kD5%RbC%7kNEu23|1VO&^HoEy&H4bpnCO3yP;>)w*AG2w)9yNasW`8mgWV{0-m zpRg>XaYQ%dwn>ulGbu>AwX)zkDFZ*&SabmrN9hc%AvVdEnGMyycr&w$+1Odz zTSEGdohh^VaW0nHZZZa(eUX&@x|H<-JTUVu$~-N?Tt?#G2+`XDWr zZJWx_U;*LDst+q!&gWYU6i~(ZQEKXrF ztpJEw)QJFqgo8+%b`j5W%AL@C%z*&ZpLLLUp5-1KZ^#>&l8!+Z)SVq0x+;6fi6FNX zLsmXyCy%}Co)jt4rZDSk5LEpSjGGw)S0y71I?8!yM3Qz=vd*)X5PlF_Qm#`34N*l* zD1vsVA~0po%43!DO+Jo$9zVp=x$c1VD=(3-F>k_)68vzX1e$i(ZjAx+0xyj z&=z^ym*0qeYrh(sY2W3dw(Xox_gB}SIY&z9G+}b=voX$2<_@#sn9NtKoGjF z`y&cn$H5FjH*|kgp&M}YN9Z-(?FzjH4}XMi>T>2l4;T|D^DWH)zTrqIoD+0> zCc>-v+aAKNA-sye9}VFf2+Q-!kA(1bgys3=$`IZ~Se{?Dg|Ixm$n(n;AuLZX^8E7B z5Z+E$o?o_x@HWEo{IVs4w-A=+mzRX_3}JbGd2t9&6PD)}JkePD(ctV~bmWP~!t(SY z&o6kQv9LV7Xzv$KG!~v9EYB}^qOovJSe{?-L}Ov`8hL)f6OD!2gys1KPc#;0zGsP0 zmnRwvC!MELoMfar6~7prC7`bPCvjB708PVU?5x7$>#6EhF}q-+X|2x;GJFv*`q|{l zj3d;XBd`YslQO2ztIIB@ZoEMPL1ng&1{pp_Wt2cRbLjtH>-pP!aw!>+l&dkB+V zQhAi_Jd(PgRNx$y(!8NmPJC;}H;bUPm?ms00YE0q8F?4D3nr*W<6HB!+y*22W_{Sm z*^AqmL$OGR;tN>OXfif6c}~*=-}>0ZaKt8tX(YqO!kz!*vjsq#=P$L7v`L|@mo`dv zL8C|ltQv*3HUQLrH1%SZ)tsO(Itbo3zAs9$c5#KM)4*O}su>m40~LAj?p{MA)G!j# zq)uy8GTo9=ASeU?j62kHF_qC6)8YYU2ubJOOPLg7@fGD=c-7-(XxwGW2pvSFf)R;K zwm=pZb<@9<|3Ytrp!V-pGuj3c%Dp|Yj~bXnJsuek}})yd@}9!^gNL=0rNbaGWGI2 zmNJjX05^Y{qf-&^&HY!~sX)LE!LRuqbR zSeqtmiei6nidRJ5>tYg(fjwI9PbmYT+Kn9aNtqHhdOj5q^HIcx{k<+lU;uRkMPQ-3 z2XWdeQ%RoJi=wj>WcIg`+4fbC!>+|9FS)@y7D{eWNho=UO4jf1T}~w|17Bm6H1~Mn zB7hsEPV)+pbq6a%9Wq!U`+8pCsEB2sezVwT9F>(XR#_(T36v-SE#Nb7DL3J zHdL2=4^*KW$Owej4pLDX@DCW`0ouL>3~ONw1`Hd40t1GvK!KyhnD?OLkF#1wc#Rds zEqw~&EL=zzhV(iOT0t+Q>1wr#kPZV(NQVK2`_wqV#=(DLKRaxO^vyp2(mrHBy)cLZ zt}wPiT(i(H0aqO6+Sh<<KoDHj z0wW*@u4;i%dX>K3$SdA7f93~35G|tv5O_n1mjkhis#hKHs#hKHs#l#Ns$Sh_d8=Ms zDFlQ|6I?)GJ+EyLpx+#=)xt$kcR&DKHt{;5vs+>&`_&8`4x4SHW zW{~1eJp=={o3>zR^9D2kV-hSa005Rr18YLC^aPd`Won_$B>O8c{Q1nc;PcDpB%s&3nzkh23jI9o$f40|@8-ri|_~Rd}(?5#F zeqr`2kNy6e340Sx#9ZFM`Vq(qQEE@|BlaC?BgJ(RO&SmfO7vnIDG;@IyOK9l z{U#%l>W0BoTdP#H;-%I56})kSMa=PC_7=8qtJtQAKa)SEMN=Vxc)DzDwBXS5%Fpm0r=x ziz@1fn_pDXPxlKH>yL$t_s4fl@coLafj;3CO5M%yXKYnu8j>k zs9lJaa^l%KqEU$7BUsZZurqr#P8#UKnKA6pSQ=Ik2nM8*V2hN`q%pv-oGHR}Dipli z(Fs-D%6W3dFr3HXrKM_KXKw?HD)P6$?wr3>tRwQZicvXVt5}Egwb}rWima{TQPz`i zx=?|0Ix{&njFV8q?;@-VCz{Sf^ZfuN3;q1{x_CVI-uNQJR&;>PjithHmU- zQ+%S!rA^bokMB0ehIkVL>>b-gOVSeo8QEy-&svKDRaT@hAA`=-eBkT7A!-x3a`6i zh!^EHcVXjVP)!N4=!(_$-6dNXrfUhAy(}B{a;i@$L2GF@puRmQd$^IUvdl8oC3n3I zM4*=Ae%XG6%#G*SQ?1JPuzFbBfamgJ}fJg3O>)?SlO8c0RQn_0;P9Xp|RFPt}08 zW=di8nQeM&C|Y0#n-eRF+79+F*6XpayGf5XpjqPav)#+}cs=?h9(ST%;&D^AtH+zV zJw0BdE2?&|jd7VCH?SwdF@VE}W7Gq{db~8?`cx&RlZFbN}hz$l;)F$yR|i~y5A^ju=&o`z1=LHVCp{*HkwIf&tfI5Dd6>KrrAsQgqdC!gUl}2b5AEsLtZz zCTR*JX)fnmrAWrDH>3COSu$f6w|L{=jYWF?1acn8qD&qh&OM|GK z0#vRI*Vmea(5RlONlS$xJsW1oGMlxrhA`v%z}gH%2+g$MUw>7GaivaE$)uoW0?>@M z)l}j#KWL%-SA9g5a!K;z+TLV453%ZOE$*w%S7KGQuZrPy+_GddvU-&OybIa49uAtA zAjT;LL=W3nMVzr`#KDvCpM z-cPYymV^>ZG4~-^QJ+$a4irzZgEj_cH5hFQ3^yz$VwlB@YW6UOT-qj=-Fr0u*r(y- zb|7IB|CMi(!Iyg7EE+ohTVZPv+EEDY9Cb&Q{8bSXN zyERrN{Yyb+v`S9t3TzdR)1uUa<-V3QIz-q@bZRmSr%#j*V$*MkF{>DgeRrDHRqjID zRy&YrRxY$?<*eV%r<(4)iSvg@_>6eLD%+3zox_-Vuz~}~aI6@cRt|h*W{i(gz*AE5 z1)tseL!YTF*m^Fsh3Btl4v-La>wxjSW!KK?a<(6wIg(kfvyqyW`(D*+U_dIS>35em zJnMy8U*Y$)>w*>C#-mrlKKw0s)<|GW7GM*mKK)R_3TsUmWw`MX&ATYTslR^-!(bh@kv+0l3=Qwm$UlOJDP zf!TJha2oBK&X_`p))Ao$lqdky*MHbbxyY06x|0=<_v@_?S?dy3ZiD5yj`@$rL{ArV z@-od_%Y#c`xuYP(kvJA*`;nN3xjVMdWd@eJZI{DZ%6gBhFD)fz`ukbYsi2l!XIcyQ zZ&F^u?XgPLTW)tqj`K@Wu8YKq-wO4cLAl6OMfGw^)sT06u324%V`!utE^tLm3zeb4 zQf6Hr%Lx^W#OOyCwZ>dy;nvDgRm+JB-O;LBI`dF!ZueX+3#qtudw($=K8@R_JbW6r zCx&{U&=celzGe2_H0SE^dQ}Ye)Cj;jT){eK#bJu4TW-1X6=DcjTpHr$1?9?+Pi0Up zKqBseg)YI3%}$Y6tbDgNxUfW-3Svqqcqp-MC8F>QB|5weLUsJme(T)t4^o*V8l}d^>mp-5NVF`b?d3_RviGcy7e@xAgd2tC+c@^+E9l*`gNj+2*x{t(e;5e z0Trg`>9TdJ--=40uz-*xu+9Q%O#+h^P-hXCuz)CnKyCpM9DxxF$ZV8A+XAu~C4ehw z>apoia)AO#=f%#R!d2*g5Cnh^I&WG6@PF`mT$7OL0O5A}?lS}|RP(O*lnx(?g~rSr zS3nSG2#648ldiGs7pVTeOH}_dAho&3ZT7O7Oc zteolPS7kWoweuo~9I7$@Fi%{P+r4@-+2NX7)8Ai{<^0y<5b`cZ#-x|$@ikhEYj$dA z-*_Ib^$^h7xNT!qgcWguHDXtI9G& z7tGaVF)>rsh2wB_F`@Pur8 z^Iy?%EE>h*&h+D`Ol8bu3z;k*e5D*3fu1sG=&72~w1h{MoRDm(;J0RhRB%A9}NDIP>WT}KS(w26d80NI-4z4TRplx2*{6MX%aSb!|mV-l5yqZvpiz4X^@k7jq>j-hF z1j{_^))a~0fkD_syY|*5qb$Uyld`e5^LZJN7u*TVH6MB;h?wMee6n2@Mdu+O*I2vB zIwrl7amnnBfy9)F()HI);VNzBR)$rreB^Uatl7g@H7T~v(2#IG0|Vz=hzg^-CdxKa zJ7%Q1ml<-Ba}~}~?alw(QP%Ia*sP|z_v+Ou1U5KVhHOZX&X&5 zd-Knd_lmSFMsYLztRb z8D?r6yNY`^L?a@p_n4LT)bA;o1A&BwI(vL9 zKzlA!Bri|LXj+vtCLe`=U$+x(SEOUcAk^{((92Uuqo}OFx!pp#iyXl&>d;v0%g8kUhLIiGzKc>QLjO7q`M zHOy`((tHsn#;Ha9+(hsnY_*X8W4-grY6dhe6lOrRX`<|E@g8kqQ)2#Aiy*Jfrp7Ur z^N|AonUN;Ykj``^In}Ui(G2W$scqBzpUBA&SXLoD+~hnHKAv7hMQ6r{)Va0g`yz1Vm?y|2OHfU6xV2H0AWLHwhCU=_zoe zmk4%NO?P$^XorhjAO&jq7e$KpMF?ncf|_rjSy_F>R_Sy$0O(Cs6^KBw`6=q@dguKa z*BpC|NDkIoB{{@Svnh>lZfRkZ5m6Tyz}%2ll4q0i+C1u%&Do|R1D?9+Uj?ipVXJ}> zcMh4wcgnQeM9o*@KhrQ&f=-OY&OLvd{-egkX*nj-gJ<-Q_4DS28Yi+sqJsKILC zGXviyW#-2_p-4`V@SrUih{!mh7ZsSeb+56# zG=HW}L0;+BrJX94kmO&s37SFgO%2^<-nE;Srj~?&6sVh~ll)(+RELH#C}6)@^85P} zfugIN9lg7UY69aNntOKa^-~gT##k`c8_iaGXt*;nIz}ToK`kwL7P)S=b+=@_+2;Sw z<%8sGdd?~b?GAQn{%`dGO7W<#cvNPD>QT>nRBa&Ys~%M!i29mGH3p)-?omyPqPp`Q zcD_JUb>Xvvf1*En@DlK9L<|C}@L4a-AXWIEJZxYp{0$Eqj0%6#!v^3$euGZoZw-_u zKaK(&a;Bh0JkrBlO-1}Hm&}S*qRp=WV%ir;m4R6)-{6I|*mWEMa4f;CQ z80-`zMZ7P2ltAydSBZqgimt@eOS1^Lsri zbht$w@TkEKf2&7TNR#oPM|tmF(3@3TTmXjf3;j~H#@hw}(Dy?FwJZGgfvOdLhlg9% z7*-K-u(W)wTNo`_`4%lXxNeF@$T|r_g~P9$X8nGW4<|#NkzpktvL1K(4}>vHlrCZ6 zwl}@u;GApaESY!%6q5toS5xb}(Shvmc6i5LAgNCYUej{}gc5q}+a5KT>OL>98b(xm zzef#r;yV=;#`(J%7f)H@SE+FK z%}b(2>|m4_g9MElBuF25MFA8wdR_O zu3+75FcMhwvFgxb;bASIm-64W2Iy)AYep9tUXRjAKP7dtOU5O}v^6g@)QZFVa`a!^ zY&H6RojnUfg{Xwp>;-pnt}A?b^O*Fc(jd3)Eil{a!R4^&{_5fEimMm0vl}uO;gHL( zV8^|F5`$p>T(5mV%BG*H`#-h1LSHFYm;J@2~#okNIPis&aD_L96WFj${R~60=$|$$kuB8t?rIRgb#JsQS1^!K*4y zlHkad81U$Py+`1 zTpIWw(t2KW+ri69Kw ze-aRmAwXNX-Nq`vcj2p)VZ$=yWjb03%uLMa4S;ZzI*280va%gLnOpxmIz!M-K>~f+ z0?@!aQ&l`H(?dNHzH^YBoG-w~co`K$;Nd^BETNzP{6?bZpozPbLO?{&<0Ckk9 zC^GJ*bzZ?eNk1 z3sY$;qmyP-hRUoB>GG6O05e<#$lsy@GQxSMWI)mhD?$W-QH}nx4%5(Rn;L&>X#J4x zkY0ed-%`(Pe0;VSox5bxxv5?O-lY`(qLYp}+yMN>(8utH7TKHDBFoSsOBnY=FDO^b z7j)QDi`%oE$8Ir(>(>5=Qsrv#{O+qMu+T_I76xFy3a=Mto1WIdV{C%|C#hw%<_&%2 znr#J$zT|UB(O)$wuuQ$?`$<_1>Kps=>BYyBo=@L-5GAbULRLv}?s^_doS;#!&&}tX zathQ5{Un-dtK`>o)NDfnxKD803;o~TnNtoPBVS2i>dS)xm`igA?E zbmCqawzYM55T#qWPf@S=Qs5U34jJsf^^1>8kI#g0*`OCa{JOhTovbj@*M);>r&V0x zL@Bmd@(}eV198H@#M?qmqXS}Ufl4tjzWH>==*;{)d~G>b`lTqxkm}puonMS+-wGD$ zTBz21(XF`_Y8HcC^LD~DCvVLa%`Vkc^yFA?zr6l!aB4SbNOebRk0r)#K5p)fKdNK# zYu*~1t;c(7z;+z{mdecU46~beQ`CHv$Viyv3pdr0Q7Y#K66o!>uG#fXzWrs5aO%#~ z2&Vo{MsQM&Z*M~0bz{=97Y(50>soG_xSzaB!N@){tgYh&{I^M_2r7`KS_o6sjWkMTHZ2s&i8fQ~24uLyMpA9T%AVv;i~G=>=K$ zNK02h!E8nK(p@9{s`EY-c5qo~aRq_~@6&U$6XUI`-Zz@}zG<}^Vdppcqo+rMvU54| z2yd1Je3LP#GRQ_t^lObW&{dlz&*=N31S`>QvQ_&4fJ3wo%CKO4~`H2J_VMc-b zWRTiiOP-Y^+WjaQ`3X`#n)1(t87+_|LM`rO-RW4;ypceHzWix&{YcWZkCIU?krR4= z&2!5Io9C9`ler>2m9g%OQQzZy=a8lT=)X!g6v8$x8E?xBRdG)B=Kd0=aqIaCy8lK_ z1eO=Exk3N*)YLRzy`}F5h!qhF{zvZWV$Wt44~Wz0eoWnBvCWDa>dV>g*%fGTm8#nF zcu3W?*PYSU73;{aJ)1|H(Sd0?uMYP4jKkV^r;^3)&7;#PVr1b-RyaupdF>sY#}^vX zIt$|_ivVc}zpS%sfYWx3zTbd3^AbJ^%hywYhVX*odX@THD)xjO;3=yuaq3EH%R+aG z{9V=^-ga4?x~sWEqti?Q8`X#P%$n~hz{@1&Z}nS!P2tfgS##*&4~aFM?Z*qFpX%nK zT@iFJEF_RLgW;?p&`i#%ZyIrU1n3qGcnMjr0h=LaEl?dUq?qHQ6*SVxKcWQ#EJXj2 zO%$di3+>tHKX)ids}dBmFSubRos59w?v32O=oEnk+1ZM3mbIYuoMd~qSg_ESB3L9` z6df+WL;JRRYk&0OW3+GJ)v#=gXjpy*(2ohyoED}^v?^mneU#nO)yoqidl-S#>PARM zz3&RB;lIxV=i&A!g3dA5+4m2^A&}}FeTj;qIcKjDs)9TbhFsrwzvUhfEL?B^Au!4u z0ujyW#+b~Z+F&J;XuC@Z6GjZtQMG>c=C!ppXGC>TIUsF38J0U^Yh{|8+-#6GG%%0w z-~#Z_Bh>C+{?n`OjAb!ar+<3=sDKZnKwU)|2q$N*!O6j2Uk5!IkRcz61{Syr+Y(F1 z{pbdxMGIHYm!sNF4THSnw7q^jL?Yi=+k_`TCV*X5_ZQy*ZJza%X>)gAx{FOf3W!!U z3rY8FBx$(D9wJOavpUhjsMS>DTwmFa)_^hH72 zSkz;UCr0_WJ)44|+E!etg>-%VZir%g_99dXa_>71A5R~#Q{ADIF8-pO<;yAjp8j}f z|9pAT0xXFq@8xq^)90ctcAt!)y<>d7pJ))uzc0!!FbEtQqHSi%1sNm|U>%5ppP&R4 zdxoM{uc@**kC=G#C!qm-dC=A&586!oL;VEsT4cJdykw_cAl$t?W;J&lSB9-#G&rw zT#3rDtX-|@=AeVy-;{P%w1Hh=L%rW4tby9JkQFPsIcr=lqV*(R4`$yF!U*-^;B^)t zMGpFT%DA^!CZ@Y2kRMCUaDns?i&Q5rhYCCm~{H zI?78`W@?xf^;sAX7q16fZ){L9_&jUM%gEHbdGz73=tm^!fsTT0OGW1dabcKum~^*i z&s-#}W``^3M$?!S%><_nhtu!%^)EZ^*nA!qZe}nNTt==Mdg; zZuJ2Xt}Vj5{Iq7}x^+KBom)FhW6>7M(^OVbcGK9$DpJ=Lp;P47zN&njl2kE9qej_o zzWfXKAb4QIHc@!+mF__tbf53>kx4H6Z)@Ip2D|A95H9JfuNpi z=j^#1joOO+19Tt6EU2i0_ZYk_ij9dx{~i@W!?el}HeIwvlx5Gb0!_>;)7niXEB)|0 zSSQ+BE5LK=j*dwyBKUxd5@zR1KtEagH4pW8hxf*Yt(vwY`mMj|GMyP5OVR#&{ z^^96Hk*D%taB%w)!C5j3{6#&`DiO%EnhE_9=&7(MkVrW$N-r#yrzH?m)LQa;SqN@u zyoazg^cckt(d8z;FyG3Dh=H14Fe3IJh)?(UlY|IltKVyP33{Vm+BMk8FJh7uFZhl! zUrf|KgxXiY6WsI%n3Xw@ldnD3?@eQJk({=dgzXTpeEeLL5}wPL^yA8}=^kAr5VR&o$!$8fIUS&f!_aJXsRYBtnfD)(lyfX|#QYFq%0AANH|is;lj; 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 u7PA@{f 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 I5jR!vKQ+7;YXATM literal 526781 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

-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≫b%$%yaHXbiTKfz%@HQSINpNz05cL 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 Result { + pub fn setup(rt: &tokio::runtime::Handle) -> Self { let domains = 100; let accounts_per_domain = 1000; let assets_per_domain = 1000; - let account_id: AccountId = "alice@wonderland".parse()?; - let key_pair = KeyPair::random(); - let state = build_state(rt, &account_id, &key_pair); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let state = build_state(rt, &alice_id); let nth = 100; let instructions = [ - populate_state(domains, accounts_per_domain, assets_per_domain, &account_id), + populate_state(domains, accounts_per_domain, assets_per_domain, &alice_id), delete_every_nth(domains, accounts_per_domain, assets_per_domain, nth), restore_every_nth(domains, accounts_per_domain, assets_per_domain, nth), ]; let blocks = { // Create empty state because it will be changed during creation of block - let state = build_state(rt, &account_id, &key_pair); + let state = build_state(rt, &alice_id); instructions .into_iter() .map(|instructions| { @@ -44,8 +43,8 @@ impl StateApplyBlocks { let block = create_block( &mut state_block, instructions, - account_id.clone(), - &key_pair, + alice_id.clone(), + &alice_keypair, ); let _events = state_block.apply_without_execution(&block); state_block.commit(); @@ -54,7 +53,7 @@ impl StateApplyBlocks { .collect::>() }; - Ok(Self { state, blocks }) + Self { state, blocks } } /// Run benchmark body. diff --git a/core/benches/blocks/apply_blocks_benchmark.rs b/core/benches/blocks/apply_blocks_benchmark.rs index 93b1c56c5ef..27a4f381954 100644 --- a/core/benches/blocks/apply_blocks_benchmark.rs +++ b/core/benches/blocks/apply_blocks_benchmark.rs @@ -14,7 +14,7 @@ fn apply_blocks(c: &mut Criterion) { group.significance_level(0.1).sample_size(10); group.bench_function("apply_blocks", |b| { b.iter_batched_ref( - || StateApplyBlocks::setup(rt.handle()).expect("Failed to setup benchmark"), + || StateApplyBlocks::setup(rt.handle()), |bench| { StateApplyBlocks::measure(bench).expect("Failed to execute benchmark"); }, diff --git a/core/benches/blocks/apply_blocks_oneshot.rs b/core/benches/blocks/apply_blocks_oneshot.rs index db09e9d427f..6492d17e2c7 100644 --- a/core/benches/blocks/apply_blocks_oneshot.rs +++ b/core/benches/blocks/apply_blocks_oneshot.rs @@ -19,6 +19,6 @@ fn main() { iroha_logger::test_logger(); } iroha_logger::info!("Starting..."); - let bench = StateApplyBlocks::setup(rt.handle()).expect("Failed to setup benchmark"); + let bench = StateApplyBlocks::setup(rt.handle()); StateApplyBlocks::measure(&bench).expect("Failed to execute benchmark"); } diff --git a/core/benches/blocks/common.rs b/core/benches/blocks/common.rs index d88514f7c9f..996a40f29de 100644 --- a/core/benches/blocks/common.rs +++ b/core/benches/blocks/common.rs @@ -74,9 +74,9 @@ pub fn populate_state( owner_id.clone(), ); instructions.push(can_unregister_domain.into()); - for j in 0..accounts_per_domain { - let account_id = construct_account_id(j, domain_id.clone()); - let account = Account::new(account_id.clone(), KeyPair::random().into_parts().0); + for _ in 0..accounts_per_domain { + let account_id = generate_account_id(domain_id.clone()); + let account = Account::new(account_id.clone()); instructions.push(Register::account(account).into()); let can_unregister_account = Grant::permission( PermissionToken::new( @@ -118,7 +118,7 @@ pub fn delete_every_nth( } else { for j in 0..accounts_per_domain { if j % nth == 0 { - let account_id = construct_account_id(j, domain_id.clone()); + let account_id = generate_account_id(domain_id.clone()); instructions.push(Unregister::account(account_id.clone()).into()); } } @@ -148,8 +148,8 @@ pub fn restore_every_nth( } 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(), KeyPair::random().into_parts().0); + let account_id = generate_account_id(domain_id.clone()); + let account = Account::new(account_id.clone()); instructions.push(Register::account(account).into()); } } @@ -164,11 +164,7 @@ pub fn restore_every_nth( instructions } -pub fn build_state( - rt: &tokio::runtime::Handle, - account_id: &AccountId, - key_pair: &KeyPair, -) -> State { +pub fn build_state(rt: &tokio::runtime::Handle, account_id: &AccountId) -> State { let kura = iroha_core::kura::Kura::blank_kura_for_testing(); let query_handle = { let _guard = rt.enter(); @@ -177,7 +173,7 @@ pub fn build_state( let mut domain = Domain::new(account_id.domain_id.clone()).build(account_id); domain.accounts.insert( account_id.clone(), - Account::new(account_id.clone(), key_pair.public_key().clone()).build(account_id), + Account::new(account_id.clone()).build(account_id), ); let state = State::new(World::with([domain], UniqueVec::new()), kura, query_handle); @@ -209,11 +205,8 @@ fn construct_domain_id(i: usize) -> DomainId { DomainId::from_str(&format!("non_inlinable_domain_name_{i}")).unwrap() } -fn construct_account_id(i: usize, domain_id: DomainId) -> AccountId { - AccountId::new( - domain_id, - Name::from_str(&format!("non_inlinable_account_name_{i}")).unwrap(), - ) +fn generate_account_id(domain_id: DomainId) -> AccountId { + AccountId::new(domain_id, KeyPair::random().into_parts().0) } fn construct_asset_definition_id(i: usize, domain_id: DomainId) -> AssetDefinitionId { diff --git a/core/benches/blocks/validate_blocks.rs b/core/benches/blocks/validate_blocks.rs index ac6de7fa5d5..0d26e9ed407 100644 --- a/core/benches/blocks/validate_blocks.rs +++ b/core/benches/blocks/validate_blocks.rs @@ -1,5 +1,6 @@ use iroha_core::{prelude::*, state::State}; use iroha_data_model::{isi::InstructionBox, prelude::*}; +use test_samples::gen_account_in; #[path = "./common.rs"] mod common; @@ -16,7 +17,8 @@ pub struct StateValidateBlocks { impl StateValidateBlocks { /// Create [`State`] and blocks for benchmarking /// - /// # Errors + /// # Panics + /// /// - Failed to parse [`AccountId`] /// - Failed to generate [`KeyPair`] /// - Failed to create instructions for block @@ -24,13 +26,12 @@ impl StateValidateBlocks { let domains = 100; let accounts_per_domain = 1000; let assets_per_domain = 1000; - let account_id: AccountId = "alice@wonderland".parse().unwrap(); - let key_pair = KeyPair::random(); - let state = build_state(rt, &account_id, &key_pair); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let state = build_state(rt, &alice_id); let nth = 100; let instructions = [ - populate_state(domains, accounts_per_domain, assets_per_domain, &account_id), + populate_state(domains, accounts_per_domain, assets_per_domain, &alice_id), delete_every_nth(domains, accounts_per_domain, assets_per_domain, nth), restore_every_nth(domains, accounts_per_domain, assets_per_domain, nth), ] @@ -40,8 +41,8 @@ impl StateValidateBlocks { Self { state, instructions, - key_pair, - account_id, + key_pair: alice_keypair, + account_id: alice_id, } } diff --git a/core/benches/kura.rs b/core/benches/kura.rs index f9b53e25190..a0ba9fb1139 100644 --- a/core/benches/kura.rs +++ b/core/benches/kura.rs @@ -16,23 +16,20 @@ use iroha_core::{ use iroha_crypto::KeyPair; use iroha_data_model::{prelude::*, transaction::TransactionLimits}; use iroha_primitives::unique_vec::UniqueVec; +use test_samples::gen_account_in; use tokio::{fs, runtime::Runtime}; async fn measure_block_size_for_n_executors(n_executors: u32) { let chain_id = ChainId::from("0"); - let alice_id = AccountId::from_str("alice@test").expect("tested"); - let bob_id = AccountId::from_str("bob@test").expect("tested"); + let (alice_id, alice_keypair) = gen_account_in("test"); + let (bob_id, _bob_keypair) = gen_account_in("test"); let xor_id = AssetDefinitionId::from_str("xor#test").expect("tested"); - let alice_xor_id = AssetId::new(xor_id, alice_id); + let alice_xor_id = AssetId::new(xor_id, alice_id.clone()); let transfer = Transfer::asset_numeric(alice_xor_id, 10u32, bob_id); - let keypair = KeyPair::random(); - let tx = TransactionBuilder::new( - chain_id.clone(), - AccountId::from_str("alice@wonderland").expect("checked"), - ) - .with_instructions([transfer]) - .sign(&keypair); + let tx = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) + .with_instructions([transfer]) + .sign(&alice_keypair); let transaction_limits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, diff --git a/core/benches/validation.rs b/core/benches/validation.rs index d7e5459f090..da0917a18ef 100644 --- a/core/benches/validation.rs +++ b/core/benches/validation.rs @@ -1,7 +1,5 @@ #![allow(missing_docs)] -use std::str::FromStr as _; - use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use iroha_core::{ block::*, @@ -12,64 +10,46 @@ use iroha_core::{ sumeragi::network_topology::Topology, tx::TransactionExecutor, }; -use iroha_data_model::{isi::InstructionBox, prelude::*, transaction::TransactionLimits}; +use iroha_data_model::{ + account::AccountId, isi::InstructionBox, prelude::*, transaction::TransactionLimits, +}; use iroha_primitives::unique_vec::UniqueVec; +use once_cell::sync::Lazy; +use test_samples::gen_account_in; -const START_DOMAIN: &str = "start"; -const START_ACCOUNT: &str = "starter"; +static STARTER_DOMAIN: Lazy = Lazy::new(|| "start".parse().unwrap()); +static STARTER_KEYPAIR: Lazy = Lazy::new(|| KeyPair::random()); +static STARTER_ID: Lazy = + Lazy::new(|| AccountId::new(STARTER_DOMAIN.clone(), STARTER_KEYPAIR.public_key().clone())); const TRANSACTION_LIMITS: TransactionLimits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, }; -fn build_test_transaction(keys: &KeyPair, chain_id: ChainId) -> SignedTransaction { - let domain_name = "domain"; - let domain_id = DomainId::from_str(domain_name).expect("does not panic"); - let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); - let account_name = "account"; - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new( - AccountId::new( - domain_name.parse().expect("Valid"), - account_name.parse().expect("Valid"), - ), - public_key, - )) - .into(); - let asset_definition_id = AssetDefinitionId::new( - "xor".parse().expect("Valid"), - domain_name.parse().expect("Valid"), - ); +fn build_test_transaction(chain_id: ChainId) -> SignedTransaction { + let domain_id: DomainId = "domain".parse().unwrap(); + let create_domain: InstructionBox = Register::domain(Domain::new(domain_id.clone())).into(); + let create_account = Register::account(Account::new(gen_account_in(&domain_id).0)).into(); + let asset_definition_id = "xor#domain".parse().unwrap(); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id)).into(); let instructions = [create_domain, create_account, create_asset]; - TransactionBuilder::new( - chain_id, - AccountId::new( - START_DOMAIN.parse().expect("Valid"), - START_ACCOUNT.parse().expect("Valid"), - ), - ) - .with_instructions(instructions) - .sign(keys) + TransactionBuilder::new(chain_id, STARTER_ID.clone()) + .with_instructions(instructions) + .sign(&STARTER_KEYPAIR) } -fn build_test_and_transient_state(keys: KeyPair) -> State { +fn build_test_and_transient_state() -> State { let kura = iroha_core::kura::Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let (public_key, _) = keys.into_parts(); let state = State::new( { - let domain_id = DomainId::from_str(START_DOMAIN).expect("Valid"); - let account_id = AccountId::new( - domain_id.clone(), - Name::from_str(START_ACCOUNT).expect("Valid"), - ); - let mut domain = Domain::new(domain_id).build(&account_id); - let account = Account::new(account_id.clone(), public_key).build(&account_id); + let (account_id, _account_keypair) = gen_account_in(&*STARTER_DOMAIN); + let mut domain = Domain::new(STARTER_DOMAIN.clone()).build(&account_id); + let account = Account::new(account_id.clone()).build(&account_id); assert!(domain.add_account(account).is_none()); World::with([domain], UniqueVec::new()) }, @@ -85,7 +65,7 @@ fn build_test_and_transient_state(keys: KeyPair) -> State { let wasm = std::fs::read(&path_to_executor) .unwrap_or_else(|_| panic!("Failed to read file: {}", path_to_executor.display())); let executor = Executor::new(WasmSmartContract::from_compiled(wasm)); - let authority = "genesis@genesis".parse().expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("genesis"); Upgrade::new(executor) .execute(&authority, &mut state_transaction) .expect("Failed to load executor"); @@ -99,8 +79,7 @@ fn build_test_and_transient_state(keys: KeyPair) -> State { fn accept_transaction(criterion: &mut Criterion) { let chain_id = ChainId::from("0"); - let keys = KeyPair::random(); - let transaction = build_test_transaction(&keys, chain_id.clone()); + let transaction = build_test_transaction(chain_id.clone()); let mut success_count = 0; let mut failures_count = 0; let _ = criterion.bench_function("accept", |b| { @@ -117,8 +96,7 @@ fn accept_transaction(criterion: &mut Criterion) { fn sign_transaction(criterion: &mut Criterion) { let chain_id = ChainId::from("0"); - let keys = KeyPair::random(); - let transaction = build_test_transaction(&keys, chain_id); + let transaction = build_test_transaction(chain_id); let key_pair = KeyPair::random(); let mut count = 0; let _ = criterion.bench_function("sign", |b| { @@ -137,16 +115,15 @@ fn sign_transaction(criterion: &mut Criterion) { fn validate_transaction(criterion: &mut Criterion) { let chain_id = ChainId::from("0"); - let keys = KeyPair::random(); let transaction = AcceptedTransaction::accept( - build_test_transaction(&keys, chain_id.clone()), + build_test_transaction(chain_id.clone()), &chain_id, &TRANSACTION_LIMITS, ) .expect("Failed to accept transaction."); let mut success_count = 0; let mut failure_count = 0; - let state = build_test_and_transient_state(keys); + let state = build_test_and_transient_state(); let _ = criterion.bench_function("validate", move |b| { let transaction_executor = TransactionExecutor::new(TRANSACTION_LIMITS); b.iter(|| { @@ -163,9 +140,8 @@ fn validate_transaction(criterion: &mut Criterion) { fn sign_blocks(criterion: &mut Criterion) { let chain_id = ChainId::from("0"); - let keys = KeyPair::random(); let transaction = AcceptedTransaction::accept( - build_test_transaction(&keys, chain_id.clone()), + build_test_transaction(chain_id.clone()), &chain_id, &TRANSACTION_LIMITS, ) diff --git a/core/src/block.rs b/core/src/block.rs index 68df8771241..84324e477fc 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -801,6 +801,7 @@ mod tests { use iroha_crypto::SignatureVerificationFail; use iroha_data_model::prelude::*; use iroha_genesis::{GENESIS_ACCOUNT_ID, GENESIS_DOMAIN_ID}; + use test_samples::gen_account_in; use super::*; use crate::{ @@ -826,10 +827,8 @@ mod tests { let chain_id = ChainId::from("0"); // Predefined world state - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let alice_keys = KeyPair::random(); - let account = - Account::new(alice_id.clone(), alice_keys.public_key().clone()).build(&alice_id); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&alice_id); assert!(domain.add_account(account).is_none()); @@ -848,7 +847,7 @@ mod tests { let transaction_limits = &state_block.transaction_executor().transaction_limits; let tx = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions([create_asset_definition]) - .sign(&alice_keys); + .sign(&alice_keypair); let tx = AcceptedTransaction::accept(tx, &chain_id, transaction_limits).expect("Valid"); // Creating a block of two identical transactions and validating it @@ -856,7 +855,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut state_block) - .sign(&alice_keys) + .sign(&alice_keypair) .unpack(|_| {}); // The first transaction should be confirmed @@ -883,10 +882,8 @@ mod tests { let chain_id = ChainId::from("0"); // Predefined world state - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let alice_keys = KeyPair::random(); - let account = - Account::new(alice_id.clone(), alice_keys.public_key().clone()).build(&alice_id); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&alice_id); assert!(domain.add_account(account).is_none()); @@ -905,7 +902,7 @@ mod tests { let transaction_limits = &state_block.transaction_executor().transaction_limits; let tx = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([create_asset_definition]) - .sign(&alice_keys); + .sign(&alice_keypair); let tx = AcceptedTransaction::accept(tx, &chain_id, transaction_limits).expect("Valid"); let fail_mint = Mint::asset_numeric( @@ -918,12 +915,12 @@ mod tests { let tx0 = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([fail_mint]) - .sign(&alice_keys); + .sign(&alice_keypair); let tx0 = AcceptedTransaction::accept(tx0, &chain_id, transaction_limits).expect("Valid"); let tx2 = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions([succeed_mint]) - .sign(&alice_keys); + .sign(&alice_keypair); let tx2 = AcceptedTransaction::accept(tx2, &chain_id, transaction_limits).expect("Valid"); // Creating a block of two identical transactions and validating it @@ -931,7 +928,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut state_block) - .sign(&alice_keys) + .sign(&alice_keypair) .unpack(|_| {}); // The first transaction should fail @@ -958,10 +955,8 @@ mod tests { let chain_id = ChainId::from("0"); // Predefined world state - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let alice_keys = KeyPair::random(); - let account = - Account::new(alice_id.clone(), alice_keys.public_key().clone()).build(&alice_id); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&alice_id); assert!( @@ -987,12 +982,12 @@ mod tests { let instructions_accept: [InstructionBox; 2] = [create_domain.into(), create_asset.into()]; let tx_fail = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions(instructions_fail) - .sign(&alice_keys); + .sign(&alice_keypair); let tx_fail = AcceptedTransaction::accept(tx_fail, &chain_id, transaction_limits).expect("Valid"); let tx_accept = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions(instructions_accept) - .sign(&alice_keys); + .sign(&alice_keypair); let tx_accept = AcceptedTransaction::accept(tx_accept, &chain_id, transaction_limits).expect("Valid"); @@ -1001,7 +996,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut state_block) - .sign(&alice_keys) + .sign(&alice_keypair) .unpack(|_| {}); // The first transaction should be rejected @@ -1037,12 +1032,13 @@ mod tests { let genesis_correct_key = KeyPair::random(); let genesis_wrong_key = KeyPair::random(); let mut genesis_domain = Domain::new(GENESIS_DOMAIN_ID.clone()).build(&GENESIS_ACCOUNT_ID); - let genesis_account = Account::new( - GENESIS_ACCOUNT_ID.clone(), - genesis_wrong_key.public_key().clone(), - ) - .build(&GENESIS_ACCOUNT_ID); - assert!(genesis_domain.add_account(genesis_account).is_none(),); + let genesis_wrong_account_id: AccountId = + format!("{}@{}", genesis_wrong_key.public_key(), *GENESIS_DOMAIN_ID) + .parse() + .expect("should be valid"); + let genesis_wrong_account = + Account::new(genesis_wrong_account_id.clone()).build(&genesis_wrong_account_id); + assert!(genesis_domain.add_account(genesis_wrong_account).is_none(),); let world = World::with([genesis_domain], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); @@ -1057,7 +1053,7 @@ mod tests { // Create genesis transaction // Sign with `genesis_wrong_key` as peer which has incorrect genesis key pair - let tx = TransactionBuilder::new(chain_id.clone(), GENESIS_ACCOUNT_ID.clone()) + let tx = TransactionBuilder::new(chain_id.clone(), genesis_wrong_account_id.clone()) .with_instructions([isi]) .sign(&genesis_wrong_key); let tx = AcceptedTransaction::accept_genesis( diff --git a/core/src/lib.rs b/core/src/lib.rs index f9b25a0a831..5a3e1bb4be6 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -171,7 +171,8 @@ pub mod prelude { mod tests { use std::cmp::Ordering; - use iroha_data_model::{account::AccountId, role::RoleId}; + use iroha_data_model::role::RoleId; + use test_samples::gen_account_in; use crate::role::RoleIdWithOwner; @@ -179,8 +180,8 @@ mod tests { fn cmp_role_id_with_owner() { let role_id_a: RoleId = "a".parse().expect("failed to parse RoleId"); let role_id_b: RoleId = "b".parse().expect("failed to parse RoleId"); - let account_id_a: AccountId = "a@domain".parse().expect("failed to parse AccountId"); - let account_id_b: AccountId = "b@domain".parse().expect("failed to parse AccountId"); + let (account_id_a, _account_keypair_a) = gen_account_in("domain"); + let (account_id_b, _account_keypair_b) = gen_account_in("domain"); let mut role_ids_with_owner = Vec::new(); for account_id in [&account_id_a, &account_id_b] { diff --git a/core/src/queue.rs b/core/src/queue.rs index 51e3ad38a5b..5d0695bbca7 100644 --- a/core/src/queue.rs +++ b/core/src/queue.rs @@ -22,24 +22,10 @@ use crate::{prelude::*, EventsSender}; impl AcceptedTransaction { // TODO: We should have another type of transaction like `CheckedTransaction` in the type system? - #[must_use] - fn check_signature_condition(&self, state_view: &StateView<'_>) -> bool { + fn is_signatory_consistent(&self) -> bool { let authority = self.as_ref().authority(); - - let transaction_signatories = self - .as_ref() - .signatures() - .iter() - .map(|signature| signature.public_key()) - .cloned() - .collect(); - - state_view - .world - .map_account(authority, |account| { - account.check_signature_check_condition(&transaction_signatories) - }) - .unwrap_or(false) + let signatory = self.as_ref().signature().public_key(); + authority.signatory_matches(signatory) } /// Check if [`self`] is committed or rejected. @@ -91,8 +77,8 @@ pub enum Error { MaximumTransactionsPerUser, /// The transaction is already in the queue IsInQueue, - /// Failure during signature condition execution - SignatureCondition, + /// Signatories in signature and payload mismatch + SignatoryInconsistent, } /// Failure that can pop up when pushing transaction into the queue @@ -181,8 +167,8 @@ impl Queue { Err(Error::Expired) } else if tx.is_in_blockchain(state_view) { Err(Error::InBlockchain) - } else if !tx.check_signature_condition(state_view) { - Err(Error::SignatureCondition) + } else if !tx.is_signatory_consistent() { + Err(Error::SignatoryInconsistent) } else { Ok(()) } @@ -396,6 +382,7 @@ pub mod tests { use iroha_data_model::{prelude::*, transaction::TransactionLimits}; use nonzero_ext::nonzero; use rand::Rng as _; + use test_samples::gen_account_in; use tokio::test; use super::*; @@ -423,24 +410,17 @@ pub mod tests { } } - fn accepted_tx( - account_id: &str, - key: &KeyPair, - time_source: &TimeSource, - ) -> AcceptedTransaction { + fn accepted_tx(time_source: &TimeSource) -> AcceptedTransaction { + let (account_id, key_pair) = gen_account_in("wonderland"); let chain_id = ChainId::from("0"); - let message = std::iter::repeat_with(rand::random::) .take(16) .collect(); let instructions = [Fail { message }]; - let tx = TransactionBuilder::new_with_time_source( - chain_id.clone(), - AccountId::from_str(account_id).expect("Valid"), - time_source, - ) - .with_instructions(instructions) - .sign(key); + let tx = + TransactionBuilder::new_with_time_source(chain_id.clone(), account_id, time_source) + .with_instructions(instructions) + .sign(&key_pair); let limits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, @@ -448,18 +428,11 @@ pub mod tests { AcceptedTransaction::accept(tx, &chain_id, &limits).expect("Failed to accept Transaction.") } - pub fn world_with_test_domains( - signatories: impl IntoIterator, - ) -> World { + pub fn world_with_test_domains() -> World { let domain_id = DomainId::from_str("wonderland").expect("Valid"); - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let (account_id, _account_keypair) = gen_account_in("wonderland"); let mut domain = Domain::new(domain_id).build(&account_id); - let mut signatories = signatories.into_iter(); - let mut account = Account::new(account_id.clone(), signatories.next().unwrap()); - for signatory in signatories { - account = account.add_signatory(signatory); - } - let account = account.build(&account_id); + let account = Account::new(account_id.clone()).build(&account_id); assert!(domain.add_account(account).is_none()); World::with([domain], PeersIds::new()) } @@ -474,14 +447,9 @@ pub mod tests { #[test] async fn push_tx() { - let key_pair = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([key_pair.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -489,10 +457,7 @@ pub mod tests { let queue = Queue::test(config_factory(), &time_source); queue - .push( - accepted_tx("alice@wonderland", &key_pair, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); } @@ -500,14 +465,9 @@ pub mod tests { async fn push_tx_overflow() { let capacity = nonzero!(10_usize); - let key_pair = KeyPair::random(); - let kura = Kura::blank_kura_for_testing(); + let kura: Arc = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([key_pair.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -523,19 +483,13 @@ pub mod tests { for _ in 0..capacity.get() { queue - .push( - accepted_tx("alice@wonderland", &key_pair, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(10)); } assert!(matches!( - queue.push( - accepted_tx("alice@wonderland", &key_pair, &time_source), - &state_view - ), + queue.push(accepted_tx(&time_source), &state_view), Err(Failure { err: Error::Full, .. @@ -543,84 +497,12 @@ pub mod tests { )); } - #[test] - async fn push_multisignature_tx() { - let chain_id = ChainId::from("0"); - - let key_pairs = [KeyPair::random(), KeyPair::random()]; - let kura = Kura::blank_kura_for_testing(); - let state = { - let domain_id = DomainId::from_str("wonderland").expect("Valid"); - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let mut domain = Domain::new(domain_id).build(&account_id); - let mut account = Account::new(account_id.clone(), key_pairs[0].public_key().clone()) - .add_signatory(key_pairs[1].public_key().clone()) - .build(&account_id); - account.signature_check_condition = SignatureCheckCondition::all_account_signatures(); - assert!(domain.add_account(account).is_none()); - let query_handle = LiveQueryStore::test().start(); - Arc::new(State::new( - World::with([domain], PeersIds::new()), - kura, - query_handle, - )) - }; - let state_view = state.view(); - - let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); - - let queue = Queue::test(config_factory(), &time_source); - let instructions: [InstructionBox; 0] = []; - let tx = TransactionBuilder::new_with_time_source( - chain_id.clone(), - "alice@wonderland".parse().expect("Valid"), - &time_source, - ) - .with_instructions(instructions); - let tx_limits = TransactionLimits { - max_instruction_number: 4096, - max_wasm_size_bytes: 0, - }; - let fully_signed_tx: AcceptedTransaction = { - let mut signed_tx = tx.clone().sign(&key_pairs[0]); - for key_pair in &key_pairs[1..] { - signed_tx = signed_tx.sign(key_pair); - } - AcceptedTransaction::accept(signed_tx, &chain_id, &tx_limits) - .expect("Failed to accept Transaction.") - }; - // Check that fully signed transaction passes signature check - assert!(fully_signed_tx.check_signature_condition(&state_view)); - - let get_tx = |key_pair| { - AcceptedTransaction::accept(tx.clone().sign(&key_pair), &chain_id, &tx_limits) - .expect("Failed to accept Transaction.") - }; - for key_pair in key_pairs { - let partially_signed_tx: AcceptedTransaction = get_tx(key_pair); - // Check that none of partially signed txs passes signature check - assert!(!partially_signed_tx.check_signature_condition(&state_view),); - assert!(matches!( - queue - .push(partially_signed_tx, &state_view) - .unwrap_err() - .err, - Error::SignatureCondition - )) - } - } - #[test] async fn get_available_txs() { let max_txs_in_block = 2; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -634,10 +516,7 @@ pub mod tests { ); for _ in 0..5 { queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(10)); } @@ -648,18 +527,11 @@ pub mod tests { #[test] async fn push_tx_already_in_blockchain() { - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - ); - + let state = State::new(world_with_test_domains(), kura, query_handle); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); - - let tx = accepted_tx("alice@wonderland", &alice_key, &time_source); + let tx = accepted_tx(&time_source); let mut state_block = state.block(); state_block.transactions.insert(tx.as_ref().hash(), 1); state_block.commit(); @@ -678,18 +550,11 @@ pub mod tests { #[test] async fn get_tx_drop_if_in_blockchain() { let max_txs_in_block = 2; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - ); - + let state = State::new(world_with_test_domains(), kura, query_handle); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); - - let tx = accepted_tx("alice@wonderland", &alice_key, &time_source); + let tx = accepted_tx(&time_source); let queue = Queue::test(config_factory(), &time_source); queue.push(tx.clone(), &state.view()).unwrap(); let mut state_block = state.block(); @@ -707,14 +572,9 @@ pub mod tests { #[test] async fn get_available_txs_with_timeout() { let max_txs_in_block = 6; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -728,19 +588,13 @@ pub mod tests { ); for _ in 0..(max_txs_in_block - 1) { queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(100)); } queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(101)); assert_eq!( @@ -751,10 +605,7 @@ pub mod tests { ); queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(210)); assert_eq!( @@ -770,24 +621,16 @@ pub mod tests { #[test] async fn transactions_available_after_pop() { let max_txs_in_block = 2; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); let queue = Queue::test(config_factory(), &time_source); queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); let a = queue @@ -811,14 +654,10 @@ pub mod tests { let chain_id = ChainId::from("0"); let max_txs_in_block = 2; - let alice_key = KeyPair::random(); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -828,14 +667,11 @@ pub mod tests { let instructions = [Fail { message: "expired".to_owned(), }]; - let mut tx = TransactionBuilder::new_with_time_source( - chain_id.clone(), - AccountId::from_str("alice@wonderland").expect("Valid"), - &time_source, - ) - .with_instructions(instructions); + let mut tx = + TransactionBuilder::new_with_time_source(chain_id.clone(), alice_id, &time_source) + .with_instructions(instructions); tx.set_ttl(Duration::from_millis(TTL_MS)); - let tx = tx.sign(&alice_key); + let tx = tx.sign(&alice_keypair); let limits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, @@ -878,14 +714,9 @@ pub mod tests { #[test] async fn concurrent_stress_test() { let max_txs_in_block = 10; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -908,7 +739,7 @@ pub mod tests { // Spawn a thread where we push transactions thread::spawn(move || { while start_time.elapsed() < run_for { - let tx = accepted_tx("alice@wonderland", &alice_key, &time_source); + let tx = accepted_tx(&time_source); match queue_arc_clone.push(tx, &state.view()) { Ok(()) | Err(Failure { @@ -957,15 +788,9 @@ pub mod tests { async fn push_tx_in_future() { let future_threshold = Duration::from_secs(1); - let alice_id = "alice@wonderland"; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -977,12 +802,12 @@ pub mod tests { &time_source, ); - let tx = accepted_tx(alice_id, &alice_key, &time_source); + let tx = accepted_tx(&time_source); assert!(queue.push(tx.clone(), &state_view).is_ok()); // create the same tx but with timestamp in the future time_handle.advance(future_threshold * 2); - let tx = accepted_tx(alice_id, &alice_key, &time_source); + let tx = accepted_tx(&time_source); time_handle.rewind(future_threshold * 2); assert!(matches!( @@ -997,22 +822,14 @@ pub mod tests { #[test] async fn queue_throttling() { - let alice_key_pair = KeyPair::random(); - let bob_key_pair = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let world = { let domain_id = DomainId::from_str("wonderland").expect("Valid"); - let alice_account_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let bob_account_id = AccountId::from_str("bob@wonderland").expect("Valid"); - let mut domain = Domain::new(domain_id).build(&alice_account_id); - let alice_account = Account::new( - alice_account_id.clone(), - alice_key_pair.public_key().clone(), - ) - .build(&alice_account_id); - let bob_account = - Account::new(bob_account_id.clone(), bob_key_pair.public_key().clone()) - .build(&bob_account_id); + let (alice_id, _alice_keypair) = gen_account_in("wonderland"); + let (bob_id, _bob_keypair) = gen_account_in("wonderland"); + let mut domain = Domain::new(domain_id).build(&alice_id); + let alice_account = Account::new(alice_id.clone()).build(&alice_id); + let bob_account = Account::new(bob_id.clone()).build(&bob_id); assert!(domain.add_account(alice_account).is_none()); assert!(domain.add_account(bob_account).is_none()); World::with([domain], PeersIds::new()) @@ -1034,17 +851,11 @@ pub mod tests { // First push by Alice should be fine queue - .push( - accepted_tx("alice@wonderland", &alice_key_pair, &time_source), - &state.view(), - ) + .push(accepted_tx(&time_source), &state.view()) .expect("Failed to push tx into queue"); // Second push by Alice excide limit and will be rejected - let result = queue.push( - accepted_tx("alice@wonderland", &alice_key_pair, &time_source), - &state.view(), - ); + let result = queue.push(accepted_tx(&time_source), &state.view()); assert!( matches!( result, @@ -1058,10 +869,7 @@ pub mod tests { // First push by Bob should be fine despite previous Alice error queue - .push( - accepted_tx("bob@wonderland", &bob_key_pair, &time_source), - &state.view(), - ) + .push(accepted_tx(&time_source), &state.view()) .expect("Failed to push tx into queue"); let transactions = queue.collect_transactions_for_block(&state.view(), 10); @@ -1080,17 +888,11 @@ pub mod tests { // After cleanup Alice and Bob pushes should work fine queue - .push( - accepted_tx("alice@wonderland", &alice_key_pair, &time_source), - &state.view(), - ) + .push(accepted_tx(&time_source), &state.view()) .expect("Failed to push tx into queue"); queue - .push( - accepted_tx("bob@wonderland", &bob_key_pair, &time_source), - &state.view(), - ) + .push(accepted_tx(&time_source), &state.view()) .expect("Failed to push tx into queue"); } } diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index 0661f8618e6..15484de7627 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -132,95 +132,6 @@ pub mod isi { } } - impl Execute for Mint { - #[metrics(+"mint_account_public_key")] - fn execute( - self, - _authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> Result<(), Error> { - let account_id = self.destination_id; - let public_key = self.object; - - state_transaction - .world - .account_mut(&account_id) - .map_err(Error::from) - .and_then(|account| { - if account.contains_signatory(&public_key) { - return Err(RepetitionError { - instruction_type: InstructionType::Mint, - id: account_id.clone().into(), - } - .into()); - } - - account.add_signatory(public_key); - Ok(()) - })?; - - state_transaction - .world - .emit_events(Some(AccountEvent::AuthenticationAdded(account_id.clone()))); - - Ok(()) - } - } - - impl Execute for Burn { - #[metrics(+"burn_account_public_key")] - fn execute( - self, - _authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> Result<(), Error> { - let account_id = self.destination_id; - let public_key = self.object; - - state_transaction.world.account_mut(&account_id) - .map_err(Error::from) - .and_then(|account| { - match account.remove_signatory(&public_key) { - None => Err(Error::InvariantViolation(String::from( - "Public keys cannot be burned to nothing, \ - if you want to delete the account, please use an unregister instruction", - ))), - Some(false) => Err(FindError::PublicKey(public_key).into()), - Some(true) => Ok(()) - } - })?; - - state_transaction - .world - .emit_events(Some(AccountEvent::AuthenticationRemoved(account_id))); - - Ok(()) - } - } - - impl Execute for Mint { - #[metrics(+"mint_account_signature_check_condition")] - fn execute( - self, - _authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> Result<(), Error> { - let account_id = self.destination_id; - let signature_check_condition = self.object; - - state_transaction - .world - .account_mut(&account_id)? - .signature_check_condition = signature_check_condition; - - state_transaction - .world - .emit_events(Some(AccountEvent::AuthenticationAdded(account_id.clone()))); - - Ok(()) - } - } - impl Execute for Transfer { fn execute( self, @@ -575,12 +486,13 @@ pub mod isi { #[cfg(test)] mod test { use iroha_data_model::{prelude::AssetDefinition, ParseError}; + use test_samples::gen_account_in; use crate::smartcontracts::isi::Registrable as _; #[test] fn cannot_forbid_minting_on_asset_mintable_infinitely() -> Result<(), ParseError> { - let authority = "alice@wonderland".parse()?; + let (authority, _authority_keypair) = gen_account_in("wonderland"); let mut definition = AssetDefinition::numeric("test#hello".parse()?).build(&authority); assert!(super::forbid_minting(&mut definition).is_err()); Ok(()) @@ -658,31 +570,6 @@ pub mod query { } } - impl ValidQuery for FindAccountsByName { - #[metrics(+"find_account_by_name")] - fn execute<'state>( - &self, - state_ro: &'state impl StateReadOnly, - ) -> Result + 'state>, Error> { - let name = self.name.clone(); - iroha_logger::trace!(%name); - Ok(Box::new( - state_ro - .world() - .domains_iter() - .flat_map(move |domain| { - let name = name.clone(); - - domain - .accounts - .values() - .filter(move |account| account.id().name == name) - }) - .cloned(), - )) - } - } - impl ValidQuery for FindAccountsByDomainId { #[metrics(+"find_accounts_by_domain_id")] fn execute<'state>( diff --git a/core/src/smartcontracts/isi/domain.rs b/core/src/smartcontracts/isi/domain.rs index 0ae2a44fde1..f2389e9aace 100644 --- a/core/src/smartcontracts/isi/domain.rs +++ b/core/src/smartcontracts/isi/domain.rs @@ -50,14 +50,9 @@ pub mod isi { let account: Account = self.object.build(authority); let account_id = account.id().clone(); - account_id - .name - .validate_len(state_transaction.config.ident_length_limits) - .map_err(Error::from)?; - if account_id == *iroha_genesis::GENESIS_ACCOUNT_ID { return Err(InstructionExecutionError::InvariantViolation( - "Not allowed to register `genesis@genesis` account".to_owned(), + "Not allowed to register genesis account".to_owned(), )); } diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 4bc587aabee..9cf125bcb8e 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -107,26 +107,12 @@ impl Execute for MintBox { state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), Error> { match self { - Self::Account(isi) => isi.execute(authority, state_transaction), Self::Asset(isi) => isi.execute(authority, state_transaction), Self::TriggerRepetitions(isi) => isi.execute(authority, state_transaction), } } } -impl Execute for AccountMintBox { - fn execute( - self, - authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> std::prelude::v1::Result<(), Error> { - match self { - Self::PublicKey(isi) => isi.execute(authority, state_transaction), - Self::SignatureCheckCondition(isi) => isi.execute(authority, state_transaction), - } - } -} - impl Execute for BurnBox { #[iroha_logger::log(name = "burn", skip_all, fields(destination))] fn execute( @@ -135,7 +121,6 @@ impl Execute for BurnBox { state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), Error> { match self { - Self::AccountPublicKey(isi) => isi.execute(authority, state_transaction), Self::Asset(isi) => isi.execute(authority, state_transaction), Self::TriggerRepetitions(isi) => isi.execute(authority, state_transaction), } @@ -254,9 +239,9 @@ mod tests { use core::str::FromStr as _; use std::sync::Arc; - use iroha_crypto::KeyPair; use iroha_data_model::metadata::MetadataValueBox; use iroha_genesis::GENESIS_ACCOUNT_ID; + use test_samples::gen_account_in; use tokio::test; use super::*; @@ -272,15 +257,14 @@ mod tests { let world = World::with([], PeersIds::new()); let query_handle = LiveQueryStore::test().start(); let state = State::new(world, kura.clone(), query_handle); - let genesis_account_id = AccountId::from_str("genesis@genesis")?; - let account_id = AccountId::from_str("alice@wonderland")?; - let (public_key, _) = KeyPair::random().into_parts(); + let (genesis_account_id, _genesis_account_keypair) = gen_account_in("genesis"); + let (account_id, _account_keypair) = gen_account_in("wonderland"); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); Register::domain(Domain::new(DomainId::from_str("wonderland")?)) .execute(&genesis_account_id, &mut state_transaction)?; - Register::account(Account::new(account_id, public_key)) + Register::account(Account::new(account_id)) .execute(&genesis_account_id, &mut state_transaction)?; Register::asset_definition(AssetDefinition::store(asset_definition_id)) .execute(&genesis_account_id, &mut state_transaction)?; @@ -295,7 +279,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut staet_block = state.block(); let mut state_transaction = staet_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); SetKeyValue::asset( @@ -326,7 +310,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); SetKeyValue::account( account_id.clone(), Name::from_str("Bytes")?, @@ -359,7 +343,7 @@ mod tests { let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); let definition_id = AssetDefinitionId::from_str("rose#wonderland")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); SetKeyValue::asset_definition( definition_id.clone(), Name::from_str("Bytes")?, @@ -390,7 +374,7 @@ mod tests { let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); let domain_id = DomainId::from_str("wonderland")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); SetKeyValue::domain( domain_id.clone(), Name::from_str("Bytes")?, @@ -420,7 +404,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); let trigger_id = TriggerId::from_str("test_trigger_id")?; assert!(matches!( @@ -439,13 +423,12 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; - let fake_account_id = AccountId::from_str("fake@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); + let (fake_account_id, _fake_account_keypair) = gen_account_in("wonderland"); let trigger_id = TriggerId::from_str("test_trigger_id")?; // register fake account - let (public_key, _) = KeyPair::random().into_parts(); - let register_account = Register::account(Account::new(fake_account_id.clone(), public_key)); + let register_account = Register::account(Account::new(fake_account_id.clone())); register_account.execute(&account_id, &mut state_transaction)?; // register the trigger @@ -483,18 +466,14 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut staet_block = state.block(); let mut state_transaction = staet_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); assert!(matches!( Register::domain(Domain::new(DomainId::from_str("genesis")?)) .execute(&account_id, &mut state_transaction) .expect_err("Error expected"), Error::InvariantViolation(_) )); - let (public_key, _) = KeyPair::random().into_parts(); - let register_account = Register::account(Account::new( - AccountId::from_str("genesis@genesis")?, - public_key, - )); + let register_account = Register::account(Account::new(GENESIS_ACCOUNT_ID.clone())); assert!(matches!( register_account .execute(&account_id, &mut state_transaction) diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index daf7faae917..ba8cc1843a2 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -72,13 +72,10 @@ impl ValidQueryRequest { query: SignedQuery, state_ro: &impl StateReadOnly, ) -> Result { - let account_has_public_key = state_ro - .world() - .map_account(query.authority(), |account| { - account.contains_signatory(query.signature().public_key()) - }) - .map_err(Error::from)?; - if !account_has_public_key { + if !query + .authority() + .signatory_matches(query.signature().public_key()) + { return Err(Error::Signature(String::from( "Signature public key doesn't correspond to the account.", )) @@ -155,7 +152,6 @@ impl ValidQuery for QueryBox { } FindAllAccounts, - FindAccountsByName, FindAccountsByDomainId, FindAccountsWithAsset, FindAllAssets, @@ -186,12 +182,12 @@ impl ValidQuery for QueryBox { mod tests { use std::str::FromStr as _; - use iroha_crypto::{Hash, HashOf, KeyPair}; + use iroha_crypto::{Hash, HashOf}; use iroha_data_model::{ metadata::MetadataValueBox, query::error::FindError, transaction::TransactionLimits, }; use iroha_primitives::unique_vec::UniqueVec; - use once_cell::sync::Lazy; + use test_samples::{gen_account_in, ALICE_ID, ALICE_KEYPAIR}; use tokio::test; use super::*; @@ -206,15 +202,10 @@ mod tests { PeersIds, }; - static ALICE_KEYS: Lazy = Lazy::new(KeyPair::random); - static ALICE_ID: Lazy = - Lazy::new(|| AccountId::from_str("alice@wonderland").expect("Valid")); - fn world_with_test_domains() -> World { let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&ALICE_ID); - let account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain.add_account(account).is_none()); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); assert!(domain @@ -227,8 +218,7 @@ mod tests { let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); let mut domain = Domain::new(DomainId::from_str("wonderland").expect("Valid")).build(&ALICE_ID); - let mut account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let mut account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain .add_asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).build(&ALICE_ID) @@ -260,7 +250,7 @@ mod tests { )?; let mut domain = Domain::new(DomainId::from_str("wonderland")?).build(&ALICE_ID); - let account = Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()) + let account = Account::new(ALICE_ID.clone()) .with_metadata(metadata) .build(&ALICE_ID); assert!(domain.add_account(account).is_none()); @@ -298,14 +288,14 @@ mod tests { let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions(instructions) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); AcceptedTransaction::accept(tx, &chain_id, &limits)? }; let invalid_tx = { let isi = Fail::new("fail".to_owned()); let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions([isi.clone(), isi]) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); AcceptedTransaction::accept(tx, &chain_id, &huge_limits)? }; @@ -315,7 +305,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let first_block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -327,7 +317,7 @@ mod tests { for _ in 1u64..blocks { let block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -461,7 +451,7 @@ mod tests { let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions(instructions) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); let tx_limits = &state_block.transaction_executor().transaction_limits; let va_tx = AcceptedTransaction::accept(tx, &chain_id, tx_limits)?; @@ -469,7 +459,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let vcb = BlockBuilder::new(vec![va_tx.clone()], topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -482,8 +472,8 @@ mod tests { let state_view = state.view(); let unapplied_tx = TransactionBuilder::new(chain_id, ALICE_ID.clone()) - .with_instructions([Unregister::account("account@domain".parse().unwrap())]) - .sign(&ALICE_KEYS); + .with_instructions([Unregister::account(gen_account_in("domain").0)]) + .sign(&ALICE_KEYPAIR); let wrong_hash = unapplied_tx.hash(); let not_found = FindTransactionByHash::new(wrong_hash).execute(&state_view); assert!(matches!( @@ -515,8 +505,7 @@ mod tests { let mut domain = Domain::new(DomainId::from_str("wonderland")?) .with_metadata(metadata) .build(&ALICE_ID); - let account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain.add_account(account).is_none()); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; assert!(domain diff --git a/core/src/smartcontracts/isi/world.rs b/core/src/smartcontracts/isi/world.rs index 19b56c01dd4..3eba2e10c3a 100644 --- a/core/src/smartcontracts/isi/world.rs +++ b/core/src/smartcontracts/isi/world.rs @@ -90,7 +90,7 @@ pub mod isi { if domain_id == *iroha_genesis::GENESIS_DOMAIN_ID { return Err(InstructionExecutionError::InvariantViolation( - "Not allowed to register `genesis` domain".to_owned(), + "Not allowed to register genesis domain".to_owned(), )); } diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index c10b17a6069..470321a972b 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -74,7 +74,7 @@ mod import { use super::super::*; - pub(crate) trait ExecuteOperations { + pub trait ExecuteOperations { /// Execute `query` on host #[codec::wrap_trait_fn] fn execute_query( @@ -1706,11 +1706,9 @@ impl GetExport for (&wasmtime::Instance, C) { #[cfg(test)] mod tests { - use std::str::FromStr as _; - - use iroha_crypto::KeyPair; use iroha_data_model::query::{sorting::Sorting, Pagination}; use parity_scale_codec::Encode; + use test_samples::gen_account_in; use tokio::test; use super::*; @@ -1721,8 +1719,7 @@ mod tests { fn world_with_test_account(authority: &AccountId) -> World { let domain_id = authority.domain_id.clone(); - let (public_key, _) = KeyPair::random().into_parts(); - let account = Account::new(authority.clone(), public_key).build(authority); + let account = Account::new(authority.clone()).build(authority); let mut domain = Domain::new(domain_id).build(authority); assert!(domain.add_account(account).is_none()); @@ -1775,17 +1772,14 @@ mod tests { #[test] async fn execute_instruction_exported() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { - let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = Register::account(Account::new( - new_authority, - KeyPair::random().into_parts().0, - )); + let (new_authority, _new_authority_keypair) = gen_account_in("wonderland"); + let register_isi = Register::account(Account::new(new_authority)); encode_hex(InstructionBox::from(register_isi)) }; @@ -1820,7 +1814,7 @@ mod tests { #[test] async fn execute_query_exported() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); @@ -1865,18 +1859,15 @@ mod tests { #[test] async fn instruction_limit_reached() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { - let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = Register::account(Account::new( - new_authority, - KeyPair::random().into_parts().0, - )); + let (new_authority, _new_authority_keypair) = gen_account_in("wonderland"); + let register_isi = Register::account(Account::new(new_authority)); encode_hex(InstructionBox::from(register_isi)) }; @@ -1918,17 +1909,14 @@ mod tests { #[test] async fn instructions_not_allowed() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { - let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = Register::account(Account::new( - new_authority, - KeyPair::random().into_parts().0, - )); + let (new_authority, _new_authority_keypair) = gen_account_in("wonderland"); + let register_isi = Register::account(Account::new(new_authority)); encode_hex(InstructionBox::from(register_isi)) }; @@ -1970,7 +1958,7 @@ mod tests { #[test] async fn queries_not_allowed() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); @@ -2012,7 +2000,7 @@ mod tests { #[test] async fn trigger_related_func_is_not_linked_for_smart_contract() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); diff --git a/core/src/snapshot.rs b/core/src/snapshot.rs index c2b5f331be0..e046b49dba5 100644 --- a/core/src/snapshot.rs +++ b/core/src/snapshot.rs @@ -247,7 +247,6 @@ enum TryWriteError { mod tests { use std::{fs::File, io::Write}; - use iroha_crypto::KeyPair; use tempfile::tempdir; use tokio::test; @@ -255,11 +254,10 @@ mod tests { use crate::query::store::LiveQueryStore; fn state_factory() -> State { - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); State::new( - crate::queue::tests::world_with_test_domains([alice_key.public_key().clone()]), + crate::queue::tests::world_with_test_domains(), kura, query_handle, ) diff --git a/core/src/state.rs b/core/src/state.rs index 81ec0c79dec..d86a1dab212 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -1766,6 +1766,7 @@ pub(crate) mod deserialize { mod tests { use iroha_data_model::block::BlockPayload; use iroha_primitives::unique_vec::UniqueVec; + use test_samples::gen_account_in; use super::*; use crate::{ @@ -1844,14 +1845,14 @@ mod tests { #[test] fn role_account_range() { - let account_id: AccountId = "alice@wonderland".parse().unwrap(); + let (account_id, _account_keypair) = gen_account_in("wonderland"); let roles = [ RoleIdWithOwner::new(account_id.clone(), "1".parse().unwrap()), RoleIdWithOwner::new(account_id.clone(), "2".parse().unwrap()), - RoleIdWithOwner::new("bob@wonderland".parse().unwrap(), "3".parse().unwrap()), - RoleIdWithOwner::new("a@wonderland".parse().unwrap(), "4".parse().unwrap()), - RoleIdWithOwner::new("0@0".parse().unwrap(), "5".parse().unwrap()), - RoleIdWithOwner::new("1@1".parse().unwrap(), "6".parse().unwrap()), + RoleIdWithOwner::new(gen_account_in("wonderland").0, "3".parse().unwrap()), + RoleIdWithOwner::new(gen_account_in("wonderland").0, "4".parse().unwrap()), + RoleIdWithOwner::new(gen_account_in("0").0, "5".parse().unwrap()), + RoleIdWithOwner::new(gen_account_in("1").0, "6".parse().unwrap()), ]; let map = BTreeSet::from(roles); diff --git a/core/src/sumeragi/main_loop.rs b/core/src/sumeragi/main_loop.rs index 49e1c1db8b6..370dcbca0f0 100644 --- a/core/src/sumeragi/main_loop.rs +++ b/core/src/sumeragi/main_loop.rs @@ -1202,6 +1202,7 @@ fn handle_block_sync<'state, F: Fn(PipelineEventBox)>( #[cfg(test)] mod tests { use iroha_primitives::{unique_vec, unique_vec::UniqueVec}; + use test_samples::gen_account_in; use tokio::test; use super::*; @@ -1217,12 +1218,11 @@ mod tests { chain_id: &ChainId, topology: &Topology, leader_key_pair: &KeyPair, - tx_signer_key_pair: &KeyPair, - ) -> (State, Arc, SignedBlock) { + ) -> (State, Arc, SignedBlock, PublicKey) { // Predefined world state - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); - let account = Account::new(alice_id.clone(), tx_signer_key_pair.public_key().clone()) - .build(&alice_id); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let genesis_public_key = alice_keypair.public_key().clone(); + let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = "wonderland".parse().expect("Valid"); let mut domain = Domain::new(domain_id).build(&alice_id); assert!(domain.add_account(account).is_none()); @@ -1239,7 +1239,7 @@ mod tests { // Making two transactions that have the same instruction let tx = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([fail_box]) - .sign(tx_signer_key_pair); + .sign(&alice_keypair); let tx = AcceptedTransaction::accept( tx, chain_id, @@ -1273,7 +1273,7 @@ mod tests { let tx1 = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([create_asset_definition1]) - .sign(tx_signer_key_pair); + .sign(&alice_keypair); let tx1 = AcceptedTransaction::accept( tx1, chain_id, @@ -1283,7 +1283,7 @@ mod tests { .expect("Valid"); let tx2 = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions([create_asset_definition2]) - .sign(tx_signer_key_pair); + .sign(&alice_keypair); let tx2 = AcceptedTransaction::accept( tx2, chain_id, @@ -1299,55 +1299,47 @@ mod tests { .unpack(|_| {}) }; - (state, kura, block.into()) + (state, kura, block.into(), genesis_public_key) } #[test] #[allow(clippy::redundant_clone)] async fn block_sync_invalid_block() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, _, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); // Malform block to make it invalid payload_mut(&mut block).commit_topology.clear(); - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!(result, Err((_, BlockSyncError::BlockNotValid(_))))) } #[test] async fn block_sync_invalid_soft_fork_block() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, kura, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); let mut state_block = state.block(); let committed_block = ValidBlock::validate( block.clone(), &topology, &chain_id, - tx_signer_key_pair.public_key(), + &genesis_public_key, &mut state_block, ) .unpack(|_| {}) @@ -1363,13 +1355,7 @@ mod tests { payload_mut(&mut block).commit_topology.clear(); payload_mut(&mut block).header.view_change_index = 1; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!( result, Err((_, BlockSyncError::SoftForkBlockNotValid(_))) @@ -1380,23 +1366,16 @@ mod tests { #[allow(clippy::redundant_clone)] async fn block_sync_not_proper_height() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let topology = Topology::new(UniqueVec::new()); let leader_key_pair = KeyPair::random(); - let (state, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, _, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); // Change block height payload_mut(&mut block).header.height = 42; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!( result, Err(( @@ -1413,44 +1392,36 @@ mod tests { #[allow(clippy::redundant_clone)] async fn block_sync_commit_block() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, _, block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let (state, _, block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!(result, Ok(BlockSyncOk::CommitBlock(_, _)))) } #[test] async fn block_sync_replace_top_block() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, kura, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); let mut state_block = state.block(); let committed_block = ValidBlock::validate( block.clone(), &topology, &chain_id, - tx_signer_key_pair.public_key(), + &genesis_public_key, &mut state_block, ) .unpack(|_| {}) @@ -1467,28 +1438,21 @@ mod tests { // Increase block view change index payload_mut(&mut block).header.view_change_index = 42; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!(result, Ok(BlockSyncOk::ReplaceTopBlock(_, _)))) } #[test] async fn block_sync_small_view_change_index() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, kura, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); // Increase block view change index payload_mut(&mut block).header.view_change_index = 42; @@ -1498,7 +1462,7 @@ mod tests { block.clone(), &topology, &chain_id, - tx_signer_key_pair.public_key(), + &genesis_public_key, &mut state_block, ) .unpack(|_| {}) @@ -1514,13 +1478,7 @@ mod tests { // Decrease block view change index back payload_mut(&mut block).header.view_change_index = 0; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!( result, Err(( @@ -1537,25 +1495,18 @@ mod tests { #[allow(clippy::redundant_clone)] async fn block_sync_genesis_block_do_not_replace() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let topology = Topology::new(UniqueVec::new()); let leader_key_pair = KeyPair::random(); - let (state, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, _, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); // Change block height and view change index // Soft-fork on genesis block is not possible payload_mut(&mut block).header.view_change_index = 42; payload_mut(&mut block).header.height = 1; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!( result, Err(( diff --git a/core/src/tx.rs b/core/src/tx.rs index 070ff3e7d03..368ac5d3184 100644 --- a/core/src/tx.rs +++ b/core/src/tx.rs @@ -63,14 +63,13 @@ impl AcceptedTransaction { })); } - for signature in tx.0.signatures() { - if signature.public_key() != genesis_public_key { - return Err(SignatureVerificationFail { - signature: signature.clone().into(), - reason: "Signature doesn't correspond to genesis public key".to_string(), - } - .into()); + let signature = tx.0.signature(); + if signature.public_key() != genesis_public_key { + return Err(SignatureVerificationFail { + signature: signature.clone().into(), + reason: "Signature doesn't correspond to genesis public key".to_string(), } + .into()); } Ok(Self(tx.0)) diff --git a/core/test_network/Cargo.toml b/core/test_network/Cargo.toml index 71e24f95f18..06888ac23dd 100644 --- a/core/test_network/Cargo.toml +++ b/core/test_network/Cargo.toml @@ -17,7 +17,7 @@ iroha_data_model = { workspace = true } iroha_primitives = { workspace = true } iroha_logger = { workspace = true } iroha_genesis = { workspace = true } - +test_samples = { workspace = true } eyre = { workspace = true } futures = { workspace = true, features = ["std", "async-await"] } diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 1816fdff7d3..6b224ef80f8 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -2,7 +2,7 @@ use core::{fmt::Debug, str::FromStr as _, time::Duration}; #[cfg(debug_assertions)] use std::sync::atomic::AtomicBool; -use std::{collections::BTreeMap, path::Path, sync::Arc, thread}; +use std::{collections::BTreeMap, ops::Deref, path::Path, sync::Arc, thread}; use eyre::Result; use futures::{prelude::*, stream::FuturesUnordered}; @@ -16,7 +16,7 @@ use iroha_config::parameters::actual::Root as Config; pub use iroha_core::state::StateReadOnly; use iroha_crypto::KeyPair; use iroha_data_model::{query::QueryOutputBox, ChainId}; -use iroha_genesis::{GenesisNetwork, RawGenesisBlockFile}; +use iroha_genesis::{GenesisNetwork, RawGenesisBlockFile, GENESIS_ACCOUNT_KEYPAIR}; use iroha_logger::InstrumentFutures; use iroha_primitives::{ addr::{socket_addr, SocketAddr}, @@ -26,6 +26,7 @@ use iroha_primitives::{ use rand::{seq::IteratorRandom, thread_rng}; use serde_json::json; use tempfile::TempDir; +use test_samples::{ALICE_ID, ALICE_KEYPAIR, PEER_KEYPAIR}; use tokio::{ runtime::{self, Runtime}, task::{self, JoinHandle}, @@ -48,17 +49,22 @@ pub fn get_chain_id() -> ChainId { ChainId::from("0") } -/// Get a standardised key-pair from the hard-coded literals. -pub fn get_key_pair() -> KeyPair { - KeyPair::new( - iroha_crypto::PublicKey::from_str( - "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0", - ).unwrap(), - iroha_crypto::PrivateKey::from_hex( - iroha_crypto::Algorithm::Ed25519, - "9AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" - ).unwrap() - ).unwrap() +/// Get a key pair of a common signatory in the test network +pub fn get_key_pair(signatory: Signatory) -> KeyPair { + match signatory { + Signatory::Peer => &PEER_KEYPAIR, + Signatory::Genesis => &GENESIS_ACCOUNT_KEYPAIR, + Signatory::Alice => &ALICE_KEYPAIR, + } + .deref() + .clone() +} + +/// A common signatory in the test network +pub enum Signatory { + Peer, + Genesis, + Alice, } /// Trait used to differentiate a test instance of `genesis`. @@ -84,7 +90,6 @@ impl TestGenesis for GenesisNetwork { let rose_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("valid names"); - let alice_id = AccountId::from_str("alice@wonderland").expect("valid names"); let mint_rose_permission = PermissionToken::new( "CanMintAssetWithDefinition".parse().unwrap(), @@ -117,7 +122,7 @@ impl TestGenesis for GenesisNetwork { upgrade_executor_permission, ] { first_transaction - .append_instruction(Grant::permission(permission, alice_id.clone()).into()); + .append_instruction(Grant::permission(permission, ALICE_ID.clone()).into()); } for isi in extra_isi.into_iter() { @@ -773,7 +778,8 @@ impl TestConfig for Config { let mut layer = iroha::samples::get_user_config( &UniqueVec::new(), Some(get_chain_id()), - Some(get_key_pair()), + Some(get_key_pair(Signatory::Peer)), + Some(get_key_pair(Signatory::Genesis)), ) .merge(RootPartial::from_env(&StdEnv).expect("test env variables should parse properly")); @@ -803,7 +809,7 @@ impl TestClientConfig for ClientConfig { fn test(api_address: &SocketAddr) -> Self { iroha_client::samples::get_client_config( get_chain_id(), - get_key_pair().clone(), + get_key_pair(Signatory::Alice), format!("http://{api_address}") .parse() .expect("should be valid url"), diff --git a/data_model/derive/tests/ui_fail/transparent_api_private_field.rs b/data_model/derive/tests/ui_fail/transparent_api_private_field.rs index bdf02982028..72d0692dc6d 100644 --- a/data_model/derive/tests/ui_fail/transparent_api_private_field.rs +++ b/data_model/derive/tests/ui_fail/transparent_api_private_field.rs @@ -1,6 +1,6 @@ use iroha_data_model::account::AccountId; fn main() { - let account_id: AccountId = "alice@wonderland".parse().expect("Valid account id"); - println!("ID: {}", account_id.name); + let account_id: AccountId = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse().unwrap(); + println!("ID: {}", account_id.signatory); } diff --git a/data_model/derive/tests/ui_fail/transparent_api_private_field.stderr b/data_model/derive/tests/ui_fail/transparent_api_private_field.stderr index 884d5e959b1..16b358917e2 100644 --- a/data_model/derive/tests/ui_fail/transparent_api_private_field.stderr +++ b/data_model/derive/tests/ui_fail/transparent_api_private_field.stderr @@ -1,10 +1,10 @@ -error[E0616]: field `name` of struct `iroha_data_model::account::AccountId` is private +error[E0616]: field `signatory` of struct `iroha_data_model::account::AccountId` is private --> tests/ui_fail/transparent_api_private_field.rs:5:35 | -5 | println!("ID: {}", account_id.name); - | ^^^^ private field +5 | println!("ID: {}", account_id.signatory); + | ^^^^^^^^^ private field | -help: a method `name` also exists, call it with parentheses +help: a method `signatory` also exists, call it with parentheses | -5 | println!("ID: {}", account_id.name()); - | ++ +5 | println!("ID: {}", account_id.signatory()); + | ++ diff --git a/data_model/src/account.rs b/data_model/src/account.rs index fa0cd7fdcd4..3c37045350c 100644 --- a/data_model/src/account.rs +++ b/data_model/src/account.rs @@ -1,19 +1,13 @@ //! Structures, traits and impls related to `Account`s. #[cfg(not(feature = "std"))] -use alloc::{ - collections::{btree_map, btree_set}, - format, - string::String, - vec::Vec, -}; +use alloc::{collections::btree_map, format, string::String, vec::Vec}; use core::str::FromStr; #[cfg(feature = "std")] -use std::collections::{btree_map, btree_set}; +use std::collections::btree_map; use derive_more::{Constructor, DebugCustom, Display}; use getset::Getters; use iroha_data_model_derive::{model, IdEqOrdHash}; -use iroha_primitives::const_vec::ConstVec; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -27,27 +21,28 @@ use crate::{ }, domain::prelude::*, metadata::Metadata, - name::Name, HasMetadata, Identifiable, ParseError, PublicKey, Registered, }; /// API to work with collections of [`Id`] : [`Account`] mappings. pub type AccountsMap = btree_map::BTreeMap; -type Signatories = btree_set::BTreeSet; - #[model] mod model { use super::*; - /// Identification of an [`Account`]. Consists of Account name and Domain name. + /// Identification of [`Account`] by the combination of the [`PublicKey`] as its sole signatory and the [`Domain`](crate::domain::Domain) it belongs to. + /// TODO #4373 include multi-signatory use. /// /// # Examples /// /// ```rust /// use iroha_data_model::account::AccountId; /// - /// let id = "user@company".parse::().expect("Valid"); + /// let id: AccountId = + /// "ed0120BDF918243253B1E731FA096194C8928DA37C4D3226F97EEBD18CF5523D758D6C@domain" + /// .parse() + /// .expect("multihash@domain should be valid format"); /// ``` #[derive( DebugCustom, @@ -66,140 +61,63 @@ mod model { SerializeDisplay, IntoSchema, )] - #[display(fmt = "{name}@{domain_id}")] - #[debug(fmt = "{name}@{domain_id}")] + #[display(fmt = "{signatory}@{domain_id}")] + #[debug(fmt = "{signatory}@{domain_id}")] #[getset(get = "pub")] #[ffi_type] pub struct AccountId { - /// [`Account`]'s [`Domain`](`crate::domain::Domain`) id. + /// [`Domain`](crate::domain::Domain) that the [`Account`] belongs to. pub domain_id: DomainId, - /// [`Account`]'s name. - pub name: Name, + /// Sole signatory of the [`Account`]. + pub signatory: PublicKey, } /// Account entity is an authority which is used to execute `Iroha Special Instructions`. #[derive( - Debug, Display, Clone, IdEqOrdHash, Getters, Encode, Deserialize, Serialize, IntoSchema, + Debug, + Display, + Clone, + IdEqOrdHash, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, )] #[allow(clippy::multiple_inherent_impl)] #[display(fmt = "({id})")] // TODO: Add more? #[ffi_type] - #[serde(try_from = "candidate::Account")] pub struct Account { - /// An Identification of the [`Account`]. + /// Identification of the [`Account`]. pub id: AccountId, /// Assets in this [`Account`]. pub assets: AssetsMap, - /// [`Account`]'s signatories. - pub(super) signatories: Signatories, - /// Condition which checks if the account has the right signatures. - #[getset(get = "pub")] - pub signature_check_condition: SignatureCheckCondition, /// Metadata of this account as a key-value store. pub metadata: Metadata, } /// Builder which should be submitted in a transaction to create a new [`Account`] #[derive( - DebugCustom, Display, Clone, IdEqOrdHash, Encode, Serialize, Deserialize, IntoSchema, + DebugCustom, Display, Clone, IdEqOrdHash, Decode, Encode, Serialize, Deserialize, IntoSchema, )] #[display(fmt = "[{id}]")] - #[debug(fmt = "[{id:?}] {{ signatories: {signatories:?}, metadata: {metadata} }}")] + #[debug(fmt = "[{id:?}] {{ metadata: {metadata} }}")] #[ffi_type] - #[serde(try_from = "candidate::NewAccount")] pub struct NewAccount { /// Identification pub id: AccountId, - /// Signatories, i.e. signatures attached to this message. - /// Cannot be empty, guaranteed by constructors. - pub(super) signatories: Signatories, /// Metadata that should be submitted with the builder pub metadata: Metadata, } - - /// Condition which checks if the account has the right signatures. - #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - #[ffi_type(opaque)] - #[allow(clippy::enum_variant_names)] - pub enum SignatureCheckCondition { - #[display(fmt = "AnyAccountSignatureOr({_0:?})")] - AnyAccountSignatureOr(ConstVec), - #[display(fmt = "AllAccountSignaturesAnd({_0:?})")] - AllAccountSignaturesAnd(ConstVec), - } } -mod candidate { - //! Contains structs for deserialization checks - - use super::*; - - #[derive(Decode, Deserialize)] - /// [`Account`] candidate used for deserialization checks - pub struct Account { - id: AccountId, - assets: AssetsMap, - signatories: Signatories, - signature_check_condition: SignatureCheckCondition, - metadata: Metadata, - } - - impl TryFrom for super::Account { - type Error = &'static str; - - fn try_from(candidate: Account) -> Result { - check_signatories(&candidate.signatories)?; - - Ok(Self { - id: candidate.id, - assets: candidate.assets, - signatories: candidate.signatories, - signature_check_condition: candidate.signature_check_condition, - metadata: candidate.metadata, - }) - } - } - - /// [`NewAccount`] candidate used for deserialization checks - #[derive(Decode, Deserialize)] - pub struct NewAccount { - id: AccountId, - signatories: Signatories, - metadata: Metadata, - } - - impl TryFrom for super::NewAccount { - type Error = &'static str; - - fn try_from(candidate: NewAccount) -> Result { - check_signatories(&candidate.signatories)?; - - Ok(Self { - id: candidate.id, - signatories: candidate.signatories, - metadata: candidate.metadata, - }) - } - } - - fn check_signatories(signatories: &Signatories) -> Result<(), &'static str> { - if signatories.is_empty() { - return Err("Signatories cannot be empty"); - } - Ok(()) +impl AccountId { + /// Return `true` if the account signatory matches the given `public_key`. + #[inline] + #[cfg(feature = "transparent_api")] + pub fn signatory_matches(&self, public_key: &PublicKey) -> bool { + self.signatory() == public_key } } @@ -207,14 +125,14 @@ impl Account { /// Construct builder for [`Account`] identifiable by [`Id`] containing the given signatory. #[inline] #[must_use] - pub fn new(id: AccountId, signatory: PublicKey) -> ::With { - ::With::new(id, signatory) + pub fn new(id: AccountId) -> ::With { + ::With::new(id) } - /// Get an iterator over [`signatories`](PublicKey) of the `Account` + /// Return a reference to the `Account` signatory. #[inline] - pub fn signatories(&self) -> impl ExactSizeIterator { - self.signatories.iter() + pub fn signatory(&self) -> &PublicKey { + &self.id.signatory } /// Return a reference to the [`Asset`] corresponding to the asset id. @@ -228,12 +146,6 @@ impl Account { pub fn assets(&self) -> impl ExactSizeIterator { self.assets.values() } - - /// Return `true` if the `Account` contains the given signatory - #[inline] - pub fn contains_signatory(&self, signatory: &PublicKey) -> bool { - self.signatories.contains(signatory) - } } #[cfg(feature = "transparent_api")] @@ -249,67 +161,16 @@ impl Account { pub fn remove_asset(&mut self, asset_id: &AssetId) -> Option { self.assets.remove(asset_id) } - - /// Add [`signatory`](PublicKey) into the [`Account`]. - /// - /// If [`Account`] did not have this signatory present, `true` is returned. - /// If [`Account`] did have this signatory present, `false` is returned. - #[inline] - pub fn add_signatory(&mut self, signatory: PublicKey) -> bool { - self.signatories.insert(signatory) - } - - /// Remove a signatory from the [`Account`]. - /// - /// Does nothing and returns [`None`] if only one signature is left. - /// Otherwise returns whether the signatory was presented in the Account. - #[inline] - pub fn remove_signatory(&mut self, signatory: &PublicKey) -> Option { - if self.signatories.len() < 2 { - return None; - } - - Some(self.signatories.remove(signatory)) - } - - /// Checks whether the transaction contains all the signatures required by the - /// [`SignatureCheckCondition`] stored in this account. - #[must_use] - pub fn check_signature_check_condition( - &self, - transaction_signatories: &btree_set::BTreeSet, - ) -> bool { - self.signature_check_condition - .check(&self.signatories, transaction_signatories) - } -} - -impl Decode for Account { - fn decode( - input: &mut I, - ) -> Result { - let candidate = candidate::Account::decode(input)?; - Self::try_from(candidate).map_err(Into::into) - } } impl NewAccount { - fn new(id: AccountId, signatory: PublicKey) -> Self { + fn new(id: AccountId) -> Self { Self { id, - signatories: Signatories::from([signatory]), metadata: Metadata::default(), } } - /// Add signatory to account. - #[inline] - #[must_use] - pub fn add_signatory(mut self, signatory: PublicKey) -> Self { - self.signatories.insert(signatory); - self - } - /// Add [`Metadata`] to the account replacing any previously defined metadata #[inline] #[must_use] @@ -325,9 +186,7 @@ impl NewAccount { pub fn into_account(self) -> Account { Account { id: self.id, - signatories: self.signatories, assets: AssetsMap::default(), - signature_check_condition: SignatureCheckCondition::default(), metadata: self.metadata, } } @@ -339,15 +198,6 @@ impl HasMetadata for NewAccount { } } -impl Decode for NewAccount { - fn decode( - input: &mut I, - ) -> Result { - let candidate = candidate::NewAccount::decode(input)?; - Self::try_from(candidate).map_err(Into::into) - } -} - impl HasMetadata for Account { fn metadata(&self) -> &Metadata { &self.metadata @@ -358,207 +208,57 @@ impl Registered for Account { type With = NewAccount; } -/// Account Identification is represented by `name@domain_name` string. impl FromStr for AccountId { type Err = ParseError; - fn from_str(string: &str) -> Result { - let split = string.rsplit_once('@'); - match split { - Some(("", _)) => Err(ParseError { - reason: "`AccountId` cannot be empty", + fn from_str(s: &str) -> Result { + match s.rsplit_once('@') { + None => Err(ParseError { + reason: "Account ID should have format `signatory@domain`", }), - Some((name, domain_id)) if !name.is_empty() && !domain_id.is_empty() => Ok(AccountId { - name: name.parse()?, - domain_id: domain_id.parse()?, + Some(("", _)) => Err(ParseError { + reason: "Empty `signatory` part in `signatory@domain`", }), - _ => Err(ParseError { - reason: "`AccountId` should have format `name@domain_name`", + Some((_, "")) => Err(ParseError { + reason: "Empty `domain` part in `signatory@domain`", }), - } - } -} - -impl Default for SignatureCheckCondition { - fn default() -> Self { - Self::AnyAccountSignatureOr(ConstVec::new_empty()) - } -} - -impl SignatureCheckCondition { - /// Shorthand to create a [`SignatureCheckCondition::AnyAccountSignatureOr`] variant without additional allowed signatures. - #[inline] - pub fn any_account_signature() -> Self { - Self::AnyAccountSignatureOr(ConstVec::new_empty()) - } - - /// Shorthand to create a [`SignatureCheckCondition::AllAccountSignaturesAnd`] variant without additional required signatures. - #[inline] - pub fn all_account_signatures() -> Self { - Self::AllAccountSignaturesAnd(ConstVec::new_empty()) - } - - #[must_use] - #[cfg(feature = "transparent_api")] - fn check( - &self, - account_signatories: &btree_set::BTreeSet, - transaction_signatories: &btree_set::BTreeSet, - ) -> bool { - let result = match &self { - SignatureCheckCondition::AnyAccountSignatureOr(additional_allowed_signatures) => { - account_signatories - .iter() - .chain(additional_allowed_signatures.as_ref()) - .any(|allowed| transaction_signatories.contains(allowed)) - } - SignatureCheckCondition::AllAccountSignaturesAnd(additional_required_signatures) => { - account_signatories - .iter() - .chain(additional_required_signatures.as_ref()) - .all(|required_signature| transaction_signatories.contains(required_signature)) + Some((signatory_candidate, domain_id_candidate)) => { + let signatory = signatory_candidate.parse().map_err(|_| ParseError { + reason: "Failed to parse `signatory` part in `signatory@domain`. `signatory` should have multihash format e.g. `ed0120...`", + })?; + let domain_id = domain_id_candidate.parse().map_err(|_| ParseError { + reason: "Failed to parse `domain` part in `signatory@domain`", + })?; + Ok(Self::new(domain_id, signatory)) } - }; - - result + } } } /// The prelude re-exports most commonly used traits, structs and macros from this crate. pub mod prelude { - pub use super::{Account, AccountId, SignatureCheckCondition}; + pub use super::{Account, AccountId}; } #[cfg(test)] mod tests { - #[cfg(not(feature = "std"))] - use alloc::{vec, vec::Vec}; - use core::cmp::Ordering; - - use iroha_crypto::{KeyPair, PublicKey}; - - use super::{AccountId, SignatureCheckCondition}; - use crate::{domain::DomainId, name::Name}; - - fn make_key() -> PublicKey { - KeyPair::random().public_key().clone() - } - - fn check_signature_check_condition( - condition: &SignatureCheckCondition, - account_signatories: &[&PublicKey], - tx_signatories: &[&PublicKey], - result: bool, - ) { - let account_signatories = account_signatories.iter().copied().cloned().collect(); - let tx_signatories = tx_signatories.iter().copied().cloned().collect(); - - assert_eq!( - condition.check(&account_signatories, &tx_signatories,), - result - ); - } - - #[test] - fn signature_check_condition_default() { - let key1 = make_key(); - let key2 = make_key(); - let key3 = make_key(); - let condition = SignatureCheckCondition::default(); - - check_signature_check_condition(&condition, &[], &[], false); - check_signature_check_condition(&condition, &[&key1], &[], false); - check_signature_check_condition(&condition, &[], &[&key1], false); - check_signature_check_condition(&condition, &[&key1], &[&key1], true); - check_signature_check_condition(&condition, &[&key1], &[&key2], false); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key1], true); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key2], true); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key3], true); - } - - #[test] - fn signature_check_condition_all() { - let key1 = make_key(); - let key2 = make_key(); - let key3 = make_key(); - let condition = SignatureCheckCondition::all_account_signatures(); - - // technically, `\forall x \in \emptyset, check(x)` is true for any `check`, so this evaluate to true - // maybe not the logic we want? - check_signature_check_condition(&condition, &[], &[], true); - check_signature_check_condition(&condition, &[], &[&key1], true); - - check_signature_check_condition(&condition, &[&key1], &[], false); - check_signature_check_condition(&condition, &[&key1], &[&key1], true); - check_signature_check_condition(&condition, &[&key1], &[&key2], false); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key1], false); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key2], false); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key3], false); - check_signature_check_condition(&condition, &[&key1, &key2], &[&key1, &key2, &key3], true); - check_signature_check_condition(&condition, &[&key1, &key2], &[&key1, &key2], true); - check_signature_check_condition(&condition, &[&key1, &key2], &[&key2, &key3], false); - } - - #[test] - fn signature_check_condition_any_or() { - let key1 = make_key(); - let key2 = make_key(); - let key3 = make_key(); - let condition = SignatureCheckCondition::AnyAccountSignatureOr(vec![key3.clone()].into()); - - check_signature_check_condition(&condition, &[], &[], false); - check_signature_check_condition(&condition, &[], &[&key3], true); - check_signature_check_condition(&condition, &[], &[&key2], false); - check_signature_check_condition(&condition, &[], &[&key1, &key2], false); - check_signature_check_condition(&condition, &[&key2], &[&key2], true); - check_signature_check_condition(&condition, &[&key2, &key3], &[&key2], true); - check_signature_check_condition(&condition, &[&key1, &key2], &[&key2], true); - } - - #[test] - fn signature_check_condition_all_and() { - let key1 = make_key(); - let key2 = make_key(); - let key3 = make_key(); - let condition = SignatureCheckCondition::AllAccountSignaturesAnd(vec![key3.clone()].into()); - - check_signature_check_condition(&condition, &[], &[], false); - check_signature_check_condition(&condition, &[], &[&key3], true); - check_signature_check_condition(&condition, &[&key1], &[&key3], false); - check_signature_check_condition(&condition, &[&key1], &[&key1, &key3], true); - check_signature_check_condition(&condition, &[&key2], &[&key1, &key3], false); - check_signature_check_condition(&condition, &[&key2], &[&key1, &key2, &key3], true); - } + use super::*; #[test] - fn cmp_account_id() { - let domain_id_a: DomainId = "a".parse().expect("failed to parse DomainId"); - let domain_id_b: DomainId = "b".parse().expect("failed to parse DomainId"); - let name_a: Name = "a".parse().expect("failed to parse Name"); - let name_b: Name = "b".parse().expect("failed to parse Name"); - - let mut account_ids = Vec::new(); - for name in [&name_a, &name_b] { - for domain_id in [&domain_id_a, &domain_id_b] { - account_ids.push(AccountId::new(domain_id.clone(), name.clone())); - } - } - - for account_id_1 in &account_ids { - for account_id_2 in &account_ids { - match ( - account_id_1.domain_id.cmp(&account_id_2.domain_id), - account_id_1.name.cmp(&account_id_2.name), - ) { - // `DomainId` take precedence in comparison - // if `DomainId`s are equal than comparison based on `Name`s - (Ordering::Equal, ordering) | (ordering, _) => assert_eq!( - account_id_1.cmp(account_id_2), - ordering, - "{account_id_1:?} and {account_id_2:?} are expected to be {ordering:?}" - ), - } - } - } + fn parse_account_id() { + const SIGNATORY: &str = + "ed0120EDF6D7B52C7032D03AEC696F2068BD53101528F3C7B6081BFF05A1662D7FC245"; + let _ok = format!("{SIGNATORY}@domain") + .parse::() + .expect("should be valid"); + let _err_empty_signatory = "@domain" + .parse::() + .expect_err("@domain should not be valid"); + let _err_empty_domain = format!("{SIGNATORY}@") + .parse::() + .expect_err("signatory@ should not be valid"); + let _err_violates_format = format!("{SIGNATORY}#domain") + .parse::() + .expect_err("signatory#domain should not be valid"); } } diff --git a/data_model/src/asset.rs b/data_model/src/asset.rs index 36e589a02ec..53fa815730e 100644 --- a/data_model/src/asset.rs +++ b/data_model/src/asset.rs @@ -382,19 +382,26 @@ impl> From for AssetValue { impl FromStr for AssetDefinitionId { type Err = ParseError; - fn from_str(string: &str) -> Result { - let mut split = string.split('#'); - match (split.next(), split.next(), split.next()) { - (Some(""), _, _) => Err(ParseError { - reason: "Asset Definition ID cannot be empty", + fn from_str(s: &str) -> Result { + match s.rsplit_once('#') { + None => Err(ParseError { + reason: "Asset Definition ID should have format `name#domain`", }), - (Some(name), Some(domain_id), None) if !domain_id.is_empty() => Ok(Self { - name: name.parse()?, - domain_id: domain_id.parse()?, + Some(("", _)) => Err(ParseError { + reason: "Empty `name` part in `name#domain`", }), - _ => Err(ParseError { - reason: "Asset Definition ID should have format `asset#domain`", + Some((_, "")) => Err(ParseError { + reason: "Empty `domain` part in `name#domain`", }), + Some((name_candidate, domain_id_candidate)) => { + let name = name_candidate.parse().map_err(|_| ParseError { + reason: "Failed to parse `name` part in `name#domain`", + })?; + let domain_id = domain_id_candidate.parse().map_err(|_| ParseError { + reason: "Failed to parse `domain` part in `name#domain`", + })?; + Ok(Self::new(domain_id, name)) + } } } } @@ -415,42 +422,26 @@ impl fmt::Debug for AssetId { } } -/// Asset Identification, represented by -/// `name#asset_domain#account_name@account_domain`. If the domains of -/// the asset and account match, the name can be shortened to -/// `asset##account@domain`. impl FromStr for AssetId { type Err = ParseError; - fn from_str(string: &str) -> Result { - if let Some((asset_definition_candidate, account_id_candidate)) = string.rsplit_once('#') { - let account_id: AccountId = account_id_candidate.parse() - .map_err(|_err| ParseError { - reason: "Failed to parse the `account_id` part of the `asset_id`. Please ensure that it has the form `account@domain`" - })?; - let definition_id = { - if let Ok(def_id) = asset_definition_candidate.parse() { - def_id - } else if let Some((name, "")) = asset_definition_candidate.rsplit_once('#') { - AssetDefinitionId::new( - account_id.domain_id.clone(), - name.parse().map_err(|_e| ParseError { - reason: "The `name` part of the `definition_id` part of the `asset_id` failed to parse as a valid `Name`. You might have forbidden characters like `#` or `@` in the first part." - })? - ) - } else { - return Err(ParseError { reason: "The `definition_id` part of the `asset_id` failed to parse. Ensure that you have it in the right format: `name#domain_of_asset#account_name@domain_of_account` or `name##account_name@domain_of_account` in case of same domain" }); - } - }; - Ok(Self { - definition_id, - account_id, - }) + fn from_str(s: &str) -> Result { + let (definition_id_candidate, account_id_candidate) = + s.rsplit_once('#').ok_or(ParseError { + reason: "Asset ID should have format `asset#domain#account@domain`, or `asset##account@domain` for the same domains", + })?; + let account_id = account_id_candidate.parse::().map_err(|_| ParseError { + reason: "Failed to parse `account@domain` part in `asset#domain#account@domain`. `account` should have multihash format e.g. `ed0120...`" + })?; + let domain_complement = if definition_id_candidate.ends_with('#') { + account_id.domain_id.name.as_ref() } else { - Err(ParseError { - reason: "The `AssetId` did not contain the `#` character. ", - }) - } + "" + }; + let definition_id = format!("{definition_id_candidate}{domain_complement}").parse().map_err(|_| ParseError { + reason: "Failed to parse `asset#domain` (or `asset#`) part in `asset#domain#account@domain` (or `asset##account@domain`)", + })?; + Ok(Self::new(definition_id, account_id)) } } @@ -494,9 +485,38 @@ pub mod prelude { #[cfg(test)] mod tests { use super::*; + + #[test] + fn parse_definition_id() { + let _ok = "asset#domain" + .parse::() + .expect("should be valid"); + let _err_empty_asset = "#domain" + .parse::() + .expect_err("#domain should not be valid"); + let _err_empty_domain = "asset#" + .parse::() + .expect_err("asset# should not be valid"); + let _err_violates_format = "asset@domain" + .parse::() + .expect_err("asset@domain should not be valid"); + } + #[test] - fn test_error_for_asset_id() { - let _invalid_asset_id = AssetId::from_str("store#alice@wonderland") - .expect_err("store#alice@wonderland should not be a valid AssetId"); + fn parse_asset_id() { + const SIGNATORY: &str = + "ed0120EDF6D7B52C7032D03AEC696F2068BD53101528F3C7B6081BFF05A1662D7FC245"; + let _account_id = format!("{SIGNATORY}@domain") + .parse::() + .expect("should be valid"); + let _ok = format!("asset#domain#{SIGNATORY}@domain") + .parse::() + .expect("should be valid"); + let _ok_short = format!("asset##{SIGNATORY}@domain") + .parse::() + .expect("should be valid"); + let _err = format!("asset#{SIGNATORY}@domain") + .parse::() + .expect_err("asset#signatory@domain should not be valid"); } } diff --git a/data_model/src/block.rs b/data_model/src/block.rs index b0f861757b9..91aca3ac52f 100644 --- a/data_model/src/block.rs +++ b/data_model/src/block.rs @@ -9,8 +9,6 @@ use alloc::{boxed::Box, format, string::String, vec::Vec}; use core::{fmt::Display, time::Duration}; use derive_more::Display; -#[cfg(all(feature = "std", feature = "transparent_api"))] -use iroha_crypto::KeyPair; use iroha_crypto::{HashOf, MerkleTree, SignaturesOf}; use iroha_data_model_derive::model; use iroha_macro::FromVariant; @@ -166,6 +164,7 @@ impl SignedBlock { /// Signatures of peers which approved this block. #[inline] + #[allow(private_interfaces)] pub fn signatures(&self) -> &SignaturesOf { let SignedBlock::V1(block) = self; &block.signatures @@ -188,8 +187,8 @@ impl SignedBlock { /// Add additional signatures to this block #[must_use] - #[cfg(feature = "transparent_api")] - pub fn sign(mut self, key_pair: &KeyPair) -> Self { + #[cfg(all(feature = "std", feature = "transparent_api"))] + pub fn sign(mut self, key_pair: &iroha_crypto::KeyPair) -> Self { let SignedBlock::V1(block) = &mut self; let signature = iroha_crypto::SignatureOf::new(key_pair, &block.payload); block.signatures.insert(signature); diff --git a/data_model/src/events/data/filters.rs b/data_model/src/events/data/filters.rs index 4ef246bf076..4fb6aee691e 100644 --- a/data_model/src/events/data/filters.rs +++ b/data_model/src/events/data/filters.rs @@ -740,10 +740,11 @@ pub mod prelude { TriggerEventFilter, }; } - #[cfg(test)] #[cfg(feature = "transparent_api")] mod tests { + use iroha_crypto::KeyPair; + use super::*; use crate::{ account::AccountsMap, @@ -753,12 +754,11 @@ mod tests { #[test] #[cfg(feature = "transparent_api")] fn entity_scope() { - let domain_name = "wonderland".parse().expect("Valid"); - let account_name = "alice".parse().expect("Valid"); - let asset_name = "rose".parse().expect("Valid"); - let domain_owner_id = "genesis@genesis".parse().expect("Valid"); + let domain_id: DomainId = "wonderland".parse().unwrap(); + let account_id = AccountId::new(domain_id.clone(), KeyPair::random().into_parts().0); + let asset_id: AssetId = format!("rose##{account_id}").parse().unwrap(); + let domain_owner_id = AccountId::new(domain_id.clone(), KeyPair::random().into_parts().0); - let domain_id = DomainId::new(domain_name); let domain = Domain { id: domain_id.clone(), accounts: AccountsMap::default(), @@ -768,16 +768,7 @@ mod tests { metadata: Metadata::default(), owned_by: domain_owner_id, }; - let account_id = AccountId::new(domain_id.clone(), account_name); - let account = Account::new( - account_id.clone(), - iroha_crypto::KeyPair::random().into_parts().0, - ) - .into_account(); - let asset_id = AssetId::new( - AssetDefinitionId::new(domain_id.clone(), asset_name), - account_id.clone(), - ); + let account = Account::new(account_id.clone()).into_account(); let asset = Asset::new(asset_id.clone(), 0_u32); // Create three events with three levels of nesting diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index 31aceca6dd6..d7c03236ba4 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -157,11 +157,8 @@ impl_instruction! { Unregister, Unregister, Unregister, - Mint, - Mint, Mint, Mint, - Burn, Burn, Burn, Transfer, @@ -646,29 +643,6 @@ 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 [`Numeric`] type. pub fn asset_numeric(object: impl Into, asset_id: AssetId) -> Self { @@ -702,15 +676,6 @@ mod transparent { } impl_into_box! { - Mint | - Mint - => AccountMintBox => MintBox[Account], - => AccountMintBoxRef<'a> => MintBoxRef<'a>[Account] - } - - impl_into_box! { - Mint | - Mint | Mint | Mint => MintBox => InstructionBox[Mint], @@ -728,16 +693,6 @@ 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 [`Numeric`] type. pub fn asset_numeric(object: impl Into, asset_id: AssetId) -> Self { @@ -771,7 +726,6 @@ mod transparent { } impl_into_box! { - Burn | Burn | Burn => BurnBox => InstructionBox[Burn], @@ -1172,9 +1126,6 @@ isi_box! { )] /// Enum with all supported [`Mint`] instructions. pub enum MintBox { - /// Mint for [`Account`]. - #[enum_ref(transparent)] - Account(AccountMintBox), /// Mint for [`Asset`]. Asset(Mint), /// Mint [`Trigger`] repetitions. @@ -1182,21 +1133,6 @@ isi_box! { } } -isi_box! { - #[strum_discriminants( - vis(pub(crate)), - name(AccountMintType), - derive(Encode), - )] - /// Enum with all supported [`Mint`] instructions related to [`Account`]. - pub enum AccountMintBox { - /// Mint [`PublicKey`]. - PublicKey(Mint), - /// Mint [`SignatureCheckCondition`]. - SignatureCheckCondition(Mint), - } -} - isi_box! { #[strum_discriminants( vis(pub(crate)), @@ -1205,8 +1141,6 @@ isi_box! { )] /// Enum with all supported [`Burn`] instructions. pub enum BurnBox { - /// Burn [`PublicKey`] for [`Account`]. - AccountPublicKey(Burn), /// Burn [`Asset`]. Asset(Burn), /// Burn [`Trigger`] repetitions. @@ -1584,9 +1518,9 @@ pub mod error { /// The prelude re-exports most commonly used traits, structs and macros from this crate. pub mod prelude { pub use super::{ - AccountMintBox, AssetTransferBox, Burn, BurnBox, ExecuteTrigger, Fail, Grant, GrantBox, - InstructionBox, Log, Mint, MintBox, NewParameter, Register, RegisterBox, RemoveKeyValue, - RemoveKeyValueBox, Revoke, RevokeBox, SetKeyValue, SetKeyValueBox, SetParameter, Transfer, - TransferBox, Unregister, UnregisterBox, Upgrade, + AssetTransferBox, Burn, BurnBox, ExecuteTrigger, Fail, Grant, GrantBox, InstructionBox, + Log, Mint, MintBox, NewParameter, Register, RegisterBox, RemoveKeyValue, RemoveKeyValueBox, + Revoke, RevokeBox, SetKeyValue, SetKeyValueBox, SetParameter, Transfer, TransferBox, + Unregister, UnregisterBox, Upgrade, }; } diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index a4be42ece8a..31b4c974b8b 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -97,12 +97,9 @@ mod seal { Unregister, Unregister, - Mint, - Mint, Mint, Mint, - Burn, Burn, Burn, @@ -131,7 +128,6 @@ mod seal { FindAllAccounts, FindAccountById, FindAccountKeyValueByIdAndKey, - FindAccountsByName, FindAccountsByDomainId, FindAccountsWithAsset, FindAllAssets, @@ -619,6 +615,7 @@ pub mod parameter { } #[model] +#[allow(clippy::redundant_pub_crate)] mod model { use super::*; diff --git a/data_model/src/query/mod.rs b/data_model/src/query/mod.rs index 9837f00905d..bd84908848d 100644 --- a/data_model/src/query/mod.rs +++ b/data_model/src/query/mod.rs @@ -152,7 +152,6 @@ mod model { FindAllAccounts(FindAllAccounts), FindAccountById(FindAccountById), FindAccountKeyValueByIdAndKey(FindAccountKeyValueByIdAndKey), - FindAccountsByName(FindAccountsByName), FindAccountsByDomainId(FindAccountsByDomainId), FindAccountsWithAsset(FindAccountsWithAsset), FindAllAssets(FindAllAssets), @@ -339,7 +338,6 @@ impl_query! { FindAllAccounts => Vec, FindAccountById => crate::account::Account, FindAccountKeyValueByIdAndKey => MetadataValueBox, - FindAccountsByName => Vec, FindAccountsByDomainId => Vec, FindAccountsWithAsset => Vec, FindAllAssets => Vec, @@ -668,19 +666,6 @@ pub mod account { pub key: Name, } - /// [`FindAccountsByName`] Iroha Query gets [`Account`]s name as input and - /// finds all [`Account`]s with this name. - #[derive(Display)] - #[display(fmt = "Find accounts with `{name}` name")] - #[repr(transparent)] - // SAFETY: `FindAccountsByName` has no trap representation in `EvaluatesTo` - #[ffi_type(unsafe {robust})] - pub struct FindAccountsByName { - /// `name` of accounts to find. - pub name: Name, - } - - /// [`FindAccountsByDomainId`] Iroha Query gets [`Domain`]s id as input and /// finds all [`Account`]s under this [`Domain`]. #[derive(Display)] @@ -710,7 +695,7 @@ pub mod account { pub mod prelude { pub use super::{ FindAccountById, FindAccountKeyValueByIdAndKey, FindAccountsByDomainId, - FindAccountsByName, FindAccountsWithAsset, FindAllAccounts, + FindAccountsWithAsset, FindAllAccounts, }; } } diff --git a/data_model/src/query/predicate.rs b/data_model/src/query/predicate.rs index 6b6ab4c3682..aee03ed9d89 100644 --- a/data_model/src/query/predicate.rs +++ b/data_model/src/query/predicate.rs @@ -614,6 +614,8 @@ pub mod string { use super::*; mod id_box { + use iroha_crypto::KeyPair; + use super::*; use crate::peer::PeerId; @@ -689,34 +691,32 @@ pub mod string { #[test] fn account_id() { - let id = IdBox::AccountId("alice@wonderland".parse().expect("Valid")); - assert!(StringPredicate::starts_with("alice@").applies(&id)); + let alice: PublicKey = KeyPair::random().into_parts().0; + let id = IdBox::AccountId(format!("{alice}@wonderland").parse().expect("Valid")); + assert!(StringPredicate::starts_with(&*format!("{alice}@")).applies(&id)); assert!(StringPredicate::ends_with("@wonderland").applies(&id)); - assert!(StringPredicate::is("alice@wonderland").applies(&id)); + assert!(StringPredicate::is(&*format!("{alice}@wonderland")).applies(&id)); // Should we also include a check into string // predicates? If the internal predicate starts with // whitespace, it can't possibly match any Id, but // there's no way to enforce this at both type level // and run-time. - assert!(!StringPredicate::starts_with(" alice@").applies(&id)); + assert!(!StringPredicate::starts_with(&*format!(" {alice}@")).applies(&id)); assert!(!StringPredicate::ends_with("@wonderland ").applies(&id)); - assert!(!StringPredicate::is("alice@@wonderland ").applies(&id)); + assert!(!StringPredicate::is(&*format!("{alice}@@wonderland ")).applies(&id)); assert!(!StringPredicate::contains("#").applies(&id)); - assert!(!StringPredicate::is("alice#wonderland").applies(&id)); + assert!(!StringPredicate::is(&*format!("{alice}#wonderland")).applies(&id)); } #[test] fn asset_id() { - let definition_id = "rose#wonderland".parse().expect("Valid"); - let account_id = "alice@wonderland".parse().expect("Valid"); - let id = IdBox::AssetId(crate::asset::AssetId { - definition_id, - account_id, - }); + let alice: PublicKey = KeyPair::random().into_parts().0; + let id = + IdBox::AssetId(format!("rose##{alice}@wonderland").parse().expect("Valid")); assert!(StringPredicate::starts_with("rose##").applies(&id)); - assert!(StringPredicate::ends_with("#alice@wonderland").applies(&id)); - assert!(StringPredicate::is("rose##alice@wonderland").applies(&id)); - assert!(StringPredicate::contains("#alice@").applies(&id)); + assert!(StringPredicate::ends_with(&*format!("#{alice}@wonderland")).applies(&id)); + assert!(StringPredicate::is(&*format!("rose##{alice}@wonderland")).applies(&id)); + assert!(StringPredicate::contains(&*format!("#{alice}@")).applies(&id)); } #[test] @@ -1237,98 +1237,95 @@ pub mod value { #[test] fn typing() { + let alice: PublicKey = KeyPair::random().into_parts().0; + let alice_id: AccountId = format!("{alice}@wonderland").parse().expect("Valid"); { let pred = QueryOutputPredicate::Identifiable(string::StringPredicate::is( - "alice@wonderland", + &*alice_id.to_string(), )); println!("{pred:?}"); - assert!(pred.applies(&QueryOutputBox::Id(IdBox::AccountId( - "alice@wonderland".parse().expect("Valid") - )))); + assert!(pred.applies(&QueryOutputBox::Id(IdBox::AccountId(alice_id.clone())))); assert!( pred.applies(&QueryOutputBox::Identifiable(IdentifiableBox::NewAccount( - Account::new( - "alice@wonderland".parse().expect("Valid"), - KeyPair::random().into_parts().0 - ) + Account::new(alice_id.clone()) ))) ); - assert!(!pred.applies( - &MetadataValueBox::from("alice".parse::().expect("Valid")).into() - )); + assert!(!pred.applies(&MetadataValueBox::from(alice_id.to_string()).into())); assert!(!pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { let pred = QueryOutputPredicate::Pass; println!("{pred:?}"); - assert!(pred.applies(&MetadataValueBox::from("alice@wonderland".to_owned()).into())); + assert!(pred.applies(&MetadataValueBox::from(alice_id.to_string()).into())); } { let pred = QueryOutputPredicate::TimeStamp(numerical::SemiInterval::starting(0)); println!("{pred:?}"); - assert!( - !pred.applies(&MetadataValueBox::from("alice@wonderland".to_owned()).into()) - ); + assert!(!pred.applies(&MetadataValueBox::from(alice_id.to_string()).into())); } { - let key_pair = iroha_crypto::KeyPair::random(); - let (public_key, _) = key_pair.into_parts(); - let pred = - QueryOutputPredicate::Display(string::StringPredicate::is("alice@wonderland")); + let pred = QueryOutputPredicate::Display(string::StringPredicate::is( + &*alice_id.to_string(), + )); println!("{pred:?}"); assert!( !pred.applies(&QueryOutputBox::Identifiable(IdentifiableBox::Peer(Peer { - id: PeerId::new(socket_addr!(127.0.0.1:123), public_key) + id: PeerId::new( + socket_addr!(127.0.0.1:123), + KeyPair::random().into_parts().0 + ) }))) ); } let pred = QueryOutputPredicate::Numerical(numerical::SemiRange::Numeric( (numeric!(0), numeric!(42)).into(), )); - assert!(!pred.applies(&MetadataValueBox::from("alice".to_owned()).into())); + assert!(!pred.applies(&MetadataValueBox::from(alice_id.to_string()).into())); assert!(pred.applies(&numeric!(41).into())); } #[test] fn container_vec() { + let wonderland: DomainId = "wonderland".parse().expect("Valid"); + let alice: PublicKey = KeyPair::random().into_parts().0; + let alice_id = AccountId::new(wonderland.clone(), alice.clone()); let list = QueryOutputBox::Vec(vec![ - QueryOutputBox::Identifiable( - Domain::new("alice".parse::().unwrap()).into(), - ), - QueryOutputBox::Id("alice@wonderland".parse::().unwrap().into()), - QueryOutputBox::Id("aliceee!".parse::().unwrap().into()), + QueryOutputBox::Identifiable(Domain::new(wonderland.clone()).into()), + QueryOutputBox::Id(alice_id.into()), + QueryOutputBox::Id(wonderland.clone().into()), ]); - let alice_pred = - QueryOutputPredicate::Display(string::StringPredicate::contains("alice")); + let wonderland_pred = + QueryOutputPredicate::Display(string::StringPredicate::contains("wonderland")); { - let pred = QueryOutputPredicate::any(alice_pred.clone()); + let pred = QueryOutputPredicate::any(wonderland_pred.clone()); println!("{pred:?}"); assert!(pred.applies(&list)); assert!(!pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { - let pred = QueryOutputPredicate::all(alice_pred.clone()); + let pred = QueryOutputPredicate::all(wonderland_pred.clone()); println!("{pred:?}"); assert!(pred.applies(&list)); assert!(pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { - let alice_id_pred = - QueryOutputPredicate::Identifiable(string::StringPredicate::contains("alice")); - let pred = QueryOutputPredicate::all(alice_id_pred); + let wonderland_id_pred = QueryOutputPredicate::Identifiable( + string::StringPredicate::contains("wonderland"), + ); + let pred = QueryOutputPredicate::all(wonderland_id_pred); println!("{pred:?}"); assert!(pred.applies(&list)); assert!(pred.applies(&QueryOutputBox::Vec(Vec::new()))); } - assert!(QueryOutputPredicate::at_index(0, alice_pred.clone()).applies(&list)); + assert!(QueryOutputPredicate::at_index(0, wonderland_pred.clone()).applies(&list)); - let idx_pred = QueryOutputPredicate::at_index(3, alice_pred); // Should be out of bounds. + let idx_pred = QueryOutputPredicate::at_index(3, wonderland_pred); // Should be out of bounds. println!("{idx_pred:?}"); assert!(!idx_pred.applies(&list)); } diff --git a/data_model/src/transaction.rs b/data_model/src/transaction.rs index 0a541f70d04..c1dbe0e1686 100644 --- a/data_model/src/transaction.rs +++ b/data_model/src/transaction.rs @@ -9,7 +9,7 @@ use core::{ }; use derive_more::{DebugCustom, Display}; -use iroha_crypto::SignaturesOf; +use iroha_crypto::SignatureOf; use iroha_data_model_derive::model; use iroha_macro::FromVariant; use iroha_schema::IntoSchema; @@ -100,6 +100,7 @@ mod model { /// Unique id of the blockchain. Used for simple replay attack protection. pub chain_id: ChainId, /// Account ID of transaction creator. + /// TODO dedup public keys in transaction #4410 pub authority: AccountId, /// Creation timestamp (unix time in milliseconds). pub creation_time_ms: u64, @@ -140,12 +141,12 @@ mod model { pub max_wasm_size_bytes: u64, } - /// Transaction that contains at least one signature + /// Transaction that contains a signature /// /// `Iroha` and its clients use [`Self`] to send transactions over the network. /// After a transaction is signed and before it can be processed any further, /// the transaction must be accepted by the `Iroha` peer. - /// The peer verifies the signatures and checks the limits. + /// The peer verifies the signature and checks the limits. #[version(version = 1, versioned_alias = "SignedTransaction")] #[derive( Debug, Display, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Serialize, IntoSchema, @@ -154,9 +155,9 @@ mod model { #[cfg_attr(feature = "std", display(fmt = "{}", "self.hash()"))] #[ffi_type] pub struct SignedTransactionV1 { - /// [`iroha_crypto::SignatureOf`]<[`TransactionPayload`]>. - pub(super) signatures: SignaturesOf, - /// [`Transaction`] payload. + /// Signature of [`Self::payload`]. + pub(super) signature: SignatureOf, + /// Payload of the transaction. pub(super) payload: TransactionPayload, } @@ -290,11 +291,12 @@ impl SignedTransaction { &tx.payload.chain_id } - /// Return transaction signatures + /// Return the transaction signature #[inline] - pub fn signatures(&self) -> &SignaturesOf { + #[allow(private_interfaces)] + pub fn signature(&self) -> &SignatureOf { let SignedTransaction::V1(tx) = self; - &tx.signatures + &tx.signature } /// Calculate transaction [`Hash`](`iroha_crypto::HashOf`). @@ -308,11 +310,11 @@ impl SignedTransaction { pub fn sign(self, key_pair: &iroha_crypto::KeyPair) -> SignedTransaction { let SignedTransaction::V1(mut tx) = self; let signature = iroha_crypto::SignatureOf::new(key_pair, &tx.payload); - tx.signatures.insert(signature); + tx.signature = signature; SignedTransactionV1 { payload: tx.payload, - signatures: tx.signatures, + signature: tx.signature, } .into() } @@ -346,13 +348,13 @@ mod candidate { #[derive(Decode, Deserialize)] struct SignedTransactionCandidate { - signatures: SignaturesOf, + signature: SignatureOf, payload: TransactionPayload, } impl SignedTransactionCandidate { fn validate(self) -> Result { - self.validate_signatures()?; + self.validate_signature()?; self.validate_instructions() } @@ -365,12 +367,12 @@ mod candidate { Ok(SignedTransactionV1 { payload: self.payload, - signatures: self.signatures, + signature: self.signature, }) } - fn validate_signatures(&self) -> Result<(), &'static str> { - self.signatures + fn validate_signature(&self) -> Result<(), &'static str> { + self.signature .verify(&self.payload) .map_err(|_| "Transaction contains invalid signatures") } @@ -741,11 +743,11 @@ mod http { /// Sign transaction with provided key pair. #[must_use] pub fn sign(self, key_pair: &iroha_crypto::KeyPair) -> SignedTransaction { - let signatures = SignaturesOf::new(key_pair, &self.payload); + let signature = SignatureOf::new(key_pair, &self.payload); SignedTransactionV1 { payload: self.payload, - signatures, + signature, } .into() } diff --git a/data_model/src/visit.rs b/data_model/src/visit.rs index f25fc3dc9e0..d216a8ee845 100644 --- a/data_model/src/visit.rs +++ b/data_model/src/visit.rs @@ -1,7 +1,6 @@ //! Visitor that visits every node in Iroha syntax tree #![allow(missing_docs, clippy::missing_errors_doc)] -use iroha_crypto::PublicKey; use iroha_primitives::numeric::Numeric; use crate::{isi::Log, prelude::*}; @@ -49,7 +48,6 @@ pub trait Visit { visit_find_account_by_id(&FindAccountById), visit_find_account_key_value_by_id_and_key(&FindAccountKeyValueByIdAndKey), visit_find_accounts_by_domain_id(&FindAccountsByDomainId), - visit_find_accounts_by_name(&FindAccountsByName), visit_find_accounts_with_asset(&FindAccountsWithAsset), visit_find_all_accounts(&FindAllAccounts), visit_find_all_active_trigger_ids(&FindAllActiveTriggerIds), @@ -108,12 +106,9 @@ pub trait Visit { // Visit MintBox visit_mint_asset_numeric(&Mint), - visit_mint_account_public_key(&Mint), - visit_mint_account_signature_check_condition(&Mint), visit_mint_trigger_repetitions(&Mint), // Visit BurnBox - visit_burn_account_public_key(&Burn), visit_burn_asset_numeric(&Burn), visit_burn_trigger_repetitions(&Burn), @@ -178,7 +173,6 @@ pub fn visit_query(visitor: &mut V, authority: &AccountId, qu visit_find_account_by_id(FindAccountById), visit_find_account_key_value_by_id_and_key(FindAccountKeyValueByIdAndKey), visit_find_accounts_by_domain_id(FindAccountsByDomainId), - visit_find_accounts_by_name(FindAccountsByName), visit_find_accounts_with_asset(FindAccountsWithAsset), visit_find_all_accounts(FindAllAccounts), visit_find_all_active_trigger_ids(FindAllActiveTriggerIds), @@ -304,12 +298,6 @@ pub fn visit_unregister( pub fn visit_mint(visitor: &mut V, authority: &AccountId, isi: &MintBox) { match isi { - MintBox::Account(mint_account) => match mint_account { - AccountMintBox::PublicKey(obj) => visitor.visit_mint_account_public_key(authority, obj), - AccountMintBox::SignatureCheckCondition(obj) => { - visitor.visit_mint_account_signature_check_condition(authority, obj) - } - }, MintBox::Asset(obj) => visitor.visit_mint_asset_numeric(authority, obj), MintBox::TriggerRepetitions(obj) => visitor.visit_mint_trigger_repetitions(authority, obj), } @@ -317,7 +305,6 @@ pub fn visit_mint(visitor: &mut V, authority: &AccountId, isi pub fn visit_burn(visitor: &mut V, authority: &AccountId, isi: &BurnBox) { match isi { - BurnBox::AccountPublicKey(obj) => visitor.visit_burn_account_public_key(authority, obj), BurnBox::Asset(obj) => visitor.visit_burn_asset_numeric(authority, obj), BurnBox::TriggerRepetitions(obj) => visitor.visit_burn_trigger_repetitions(authority, obj), } @@ -400,9 +387,6 @@ leaf_visitors! { // Instruction visitors visit_register_account(&Register), visit_unregister_account(&Unregister), - visit_mint_account_public_key(&Mint), - visit_burn_account_public_key(&Burn), - visit_mint_account_signature_check_condition(&Mint), visit_set_account_key_value(&SetKeyValue), visit_remove_account_key_value(&RemoveKeyValue), visit_register_asset(&Register), @@ -450,7 +434,6 @@ leaf_visitors! { visit_find_account_by_id(&FindAccountById), visit_find_account_key_value_by_id_and_key(&FindAccountKeyValueByIdAndKey), visit_find_accounts_by_domain_id(&FindAccountsByDomainId), - visit_find_accounts_by_name(&FindAccountsByName), visit_find_accounts_with_asset(&FindAccountsWithAsset), visit_find_all_accounts(&FindAllAccounts), visit_find_all_active_trigger_ids(&FindAllActiveTriggerIds), diff --git a/data_model/tests/data_model.rs b/data_model/tests/data_model.rs index 4e8a0a7a207..85963718589 100644 --- a/data_model/tests/data_model.rs +++ b/data_model/tests/data_model.rs @@ -1,25 +1,15 @@ -use iroha_data_model::{prelude::*, ParseError}; +use iroha_crypto::KeyPair; +use iroha_data_model::prelude::*; #[test] fn transfer_isi_should_be_valid() { let _instruction = Transfer::asset_numeric( - "btc##seller@crypto".parse().expect("Valid"), + format!("btc##{}@crypto", KeyPair::random().public_key()) + .parse() + .unwrap(), 12u32, - "buyer@crypto".parse().expect("Valid"), + format!("{}@crypto", KeyPair::random().public_key()) + .parse() + .unwrap(), ); } - -#[test] -fn account_id_parsing() -> Result<(), ParseError> { - // `AccountId` should have format `name@domain_name` - let account_normal: AccountId = "test@hello".parse()?; - assert_eq!(account_normal.name().as_ref(), "test"); - assert_eq!(account_normal.domain_id().name().as_ref(), "hello"); - - let account_empty: Result = "@hello".parse(); - assert!(account_empty.is_err()); - - let account_invalid: Result = "@".parse(); - assert!(account_invalid.is_err()); - Ok(()) -} diff --git a/docs/source/references/schema.json b/docs/source/references/schema.json index e9196b814df..6610b7a8f29 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -9,14 +9,6 @@ "name": "assets", "type": "SortedMap" }, - { - "name": "signatories", - "type": "SortedVec" - }, - { - "name": "signature_check_condition", - "type": "SignatureCheckCondition" - }, { "name": "metadata", "type": "Metadata" @@ -152,22 +144,8 @@ "type": "DomainId" }, { - "name": "name", - "type": "Name" - } - ] - }, - "AccountMintBox": { - "Enum": [ - { - "tag": "PublicKey", - "discriminant": 0, - "type": "Mint" - }, - { - "tag": "SignatureCheckCondition", - "discriminant": 1, - "type": "Mint" + "name": "signatory", + "type": "PublicKey" } ] }, @@ -713,18 +691,6 @@ } ] }, - "Burn": { - "Struct": [ - { - "name": "object", - "type": "PublicKey" - }, - { - "name": "destination_id", - "type": "AccountId" - } - ] - }, "Burn": { "Struct": [ { @@ -739,19 +705,14 @@ }, "BurnBox": { "Enum": [ - { - "tag": "AccountPublicKey", - "discriminant": 0, - "type": "Burn" - }, { "tag": "Asset", - "discriminant": 1, + "discriminant": 0, "type": "Burn" }, { "tag": "TriggerRepetitions", - "discriminant": 2, + "discriminant": 1, "type": "Burn" } ] @@ -1262,14 +1223,6 @@ } ] }, - "FindAccountsByName": { - "Struct": [ - { - "name": "name", - "type": "Name" - } - ] - }, "FindAccountsWithAsset": { "Struct": [ { @@ -2270,30 +2223,6 @@ } ] }, - "Mint": { - "Struct": [ - { - "name": "object", - "type": "PublicKey" - }, - { - "name": "destination_id", - "type": "AccountId" - } - ] - }, - "Mint": { - "Struct": [ - { - "name": "object", - "type": "SignatureCheckCondition" - }, - { - "name": "destination_id", - "type": "AccountId" - } - ] - }, "Mint": { "Struct": [ { @@ -2308,19 +2237,14 @@ }, "MintBox": { "Enum": [ - { - "tag": "Account", - "discriminant": 0, - "type": "AccountMintBox" - }, { "tag": "Asset", - "discriminant": 1, + "discriminant": 0, "type": "Mint" }, { "tag": "TriggerRepetitions", - "discriminant": 2, + "discriminant": 1, "type": "Mint" } ] @@ -2372,10 +2296,6 @@ "name": "id", "type": "AccountId" }, - { - "name": "signatories", - "type": "SortedVec" - }, { "name": "metadata", "type": "Metadata" @@ -2731,189 +2651,184 @@ "discriminant": 2, "type": "FindAccountKeyValueByIdAndKey" }, - { - "tag": "FindAccountsByName", - "discriminant": 3, - "type": "FindAccountsByName" - }, { "tag": "FindAccountsByDomainId", - "discriminant": 4, + "discriminant": 3, "type": "FindAccountsByDomainId" }, { "tag": "FindAccountsWithAsset", - "discriminant": 5, + "discriminant": 4, "type": "FindAccountsWithAsset" }, { "tag": "FindAllAssets", - "discriminant": 6, + "discriminant": 5, "type": "FindAllAssets" }, { "tag": "FindAllAssetsDefinitions", - "discriminant": 7, + "discriminant": 6, "type": "FindAllAssetsDefinitions" }, { "tag": "FindAssetById", - "discriminant": 8, + "discriminant": 7, "type": "FindAssetById" }, { "tag": "FindAssetDefinitionById", - "discriminant": 9, + "discriminant": 8, "type": "FindAssetDefinitionById" }, { "tag": "FindAssetsByName", - "discriminant": 10, + "discriminant": 9, "type": "FindAssetsByName" }, { "tag": "FindAssetsByAccountId", - "discriminant": 11, + "discriminant": 10, "type": "FindAssetsByAccountId" }, { "tag": "FindAssetsByAssetDefinitionId", - "discriminant": 12, + "discriminant": 11, "type": "FindAssetsByAssetDefinitionId" }, { "tag": "FindAssetsByDomainId", - "discriminant": 13, + "discriminant": 12, "type": "FindAssetsByDomainId" }, { "tag": "FindAssetsByDomainIdAndAssetDefinitionId", - "discriminant": 14, + "discriminant": 13, "type": "FindAssetsByDomainIdAndAssetDefinitionId" }, { "tag": "FindAssetQuantityById", - "discriminant": 15, + "discriminant": 14, "type": "FindAssetQuantityById" }, { "tag": "FindTotalAssetQuantityByAssetDefinitionId", - "discriminant": 16, + "discriminant": 15, "type": "FindTotalAssetQuantityByAssetDefinitionId" }, { "tag": "FindAssetKeyValueByIdAndKey", - "discriminant": 17, + "discriminant": 16, "type": "FindAssetKeyValueByIdAndKey" }, { "tag": "FindAssetDefinitionKeyValueByIdAndKey", - "discriminant": 18, + "discriminant": 17, "type": "FindAssetDefinitionKeyValueByIdAndKey" }, { "tag": "FindAllDomains", - "discriminant": 19, + "discriminant": 18, "type": "FindAllDomains" }, { "tag": "FindDomainById", - "discriminant": 20, + "discriminant": 19, "type": "FindDomainById" }, { "tag": "FindDomainKeyValueByIdAndKey", - "discriminant": 21, + "discriminant": 20, "type": "FindDomainKeyValueByIdAndKey" }, { "tag": "FindAllPeers", - "discriminant": 22, + "discriminant": 21, "type": "FindAllPeers" }, { "tag": "FindAllBlocks", - "discriminant": 23, + "discriminant": 22, "type": "FindAllBlocks" }, { "tag": "FindAllBlockHeaders", - "discriminant": 24, + "discriminant": 23, "type": "FindAllBlockHeaders" }, { "tag": "FindBlockHeaderByHash", - "discriminant": 25, + "discriminant": 24, "type": "FindBlockHeaderByHash" }, { "tag": "FindAllTransactions", - "discriminant": 26, + "discriminant": 25, "type": "FindAllTransactions" }, { "tag": "FindTransactionsByAccountId", - "discriminant": 27, + "discriminant": 26, "type": "FindTransactionsByAccountId" }, { "tag": "FindTransactionByHash", - "discriminant": 28, + "discriminant": 27, "type": "FindTransactionByHash" }, { "tag": "FindPermissionTokensByAccountId", - "discriminant": 29, + "discriminant": 28, "type": "FindPermissionTokensByAccountId" }, { "tag": "FindPermissionTokenSchema", - "discriminant": 30, + "discriminant": 29, "type": "FindPermissionTokenSchema" }, { "tag": "FindAllActiveTriggerIds", - "discriminant": 31, + "discriminant": 30, "type": "FindAllActiveTriggerIds" }, { "tag": "FindTriggerById", - "discriminant": 32, + "discriminant": 31, "type": "FindTriggerById" }, { "tag": "FindTriggerKeyValueByIdAndKey", - "discriminant": 33, + "discriminant": 32, "type": "FindTriggerKeyValueByIdAndKey" }, { "tag": "FindTriggersByDomainId", - "discriminant": 34, + "discriminant": 33, "type": "FindTriggersByDomainId" }, { "tag": "FindAllRoles", - "discriminant": 35, + "discriminant": 34, "type": "FindAllRoles" }, { "tag": "FindAllRoleIds", - "discriminant": 36, + "discriminant": 35, "type": "FindAllRoleIds" }, { "tag": "FindRoleByRoleId", - "discriminant": 37, + "discriminant": 36, "type": "FindRoleByRoleId" }, { "tag": "FindRolesByAccountId", - "discriminant": 38, + "discriminant": 37, "type": "FindRolesByAccountId" }, { "tag": "FindAllParameters", - "discriminant": 39, + "discriminant": 38, "type": "FindAllParameters" } ] @@ -3577,20 +3492,6 @@ } ] }, - "SignatureCheckCondition": { - "Enum": [ - { - "tag": "AnyAccountSignatureOr", - "discriminant": 0, - "type": "Vec" - }, - { - "tag": "AllAccountSignaturesAnd", - "discriminant": 1, - "type": "Vec" - } - ] - }, "SignatureOf": "Signature", "SignatureOf": "Signature", "SignatureOf": "Signature", @@ -3602,14 +3503,6 @@ } ] }, - "SignaturesOf": { - "Struct": [ - { - "name": "signatures", - "type": "SortedVec>" - } - ] - }, "SignedBlock": { "Enum": [ { @@ -3664,8 +3557,8 @@ "SignedTransactionV1": { "Struct": [ { - "name": "signatures", - "type": "SignaturesOf" + "name": "signature", + "type": "SignatureOf" }, { "name": "payload", @@ -3773,15 +3666,9 @@ "SortedVec": { "Vec": "PermissionToken" }, - "SortedVec": { - "Vec": "PublicKey" - }, "SortedVec>": { "Vec": "SignatureOf" }, - "SortedVec>": { - "Vec": "SignatureOf" - }, "String": "String", "StringPredicate": { "Enum": [ @@ -4429,9 +4316,6 @@ "Vec": { "Vec": "PeerId" }, - "Vec": { - "Vec": "PublicKey" - }, "Vec": { "Vec": "QueryOutputBox" }, diff --git a/genesis/Cargo.toml b/genesis/Cargo.toml index 75b8186cc7e..604c98045e6 100644 --- a/genesis/Cargo.toml +++ b/genesis/Cargo.toml @@ -24,3 +24,4 @@ eyre = { workspace = true } [dev-dependencies] iroha_crypto = { workspace = true, features = ["rand"] } +test_samples = { workspace = true } diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index 1fe4cfe92ab..773adad746d 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -6,6 +6,7 @@ use std::{ fs::File, io::BufReader, path::{Path, PathBuf}, + str::FromStr, }; use eyre::{eyre, Report, Result, WrapErr}; @@ -20,12 +21,25 @@ use iroha_data_model::{ use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -/// [`DomainId`] of the genesis account. -pub static GENESIS_DOMAIN_ID: Lazy = Lazy::new(|| "genesis".parse().expect("Valid")); - -/// [`AccountId`] of the genesis account. -pub static GENESIS_ACCOUNT_ID: Lazy = - Lazy::new(|| AccountId::new(GENESIS_DOMAIN_ID.clone(), "genesis".parse().expect("Valid"))); +/// [`DomainId`](iroha_data_model::domain::DomainId) of the genesis account. +pub static GENESIS_DOMAIN_ID: Lazy = Lazy::new(|| "genesis".parse().unwrap()); + +/// [`AccountId`](iroha_data_model::account::AccountId) of the genesis account. +pub static GENESIS_ACCOUNT_ID: Lazy = Lazy::new(|| { + AccountId::new( + GENESIS_DOMAIN_ID.clone(), + GENESIS_ACCOUNT_KEYPAIR.public_key().clone(), + ) +}); + +/// [`KeyPair`] of the genesis account. +pub static GENESIS_ACCOUNT_KEYPAIR: Lazy = Lazy::new(|| { + KeyPair::new( + iroha_crypto::PublicKey::from_str("ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B").unwrap(), + iroha_crypto::PrivateKey::from_hex(iroha_crypto::Algorithm::Ed25519, "DD61D7A2244A504E78BA80383DFCC0228E25CA131E5A6AF503F71632D23BD76AE2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B").unwrap(), + ) + .unwrap() +}); /// Genesis transaction #[derive(Debug, Clone)] @@ -310,31 +324,15 @@ impl RawGenesisDomainBuilder { } } - /// Add an account to this domain with random public key. - #[cfg(test)] - fn account_with_random_public_key(mut self, account_name: Name) -> Self { - let account_id = AccountId::new(self.domain_id.clone(), account_name); - self.transaction.isi.push( - Register::account(Account::new(account_id, KeyPair::random().into_parts().0)).into(), - ); - self - } - /// Add an account to this domain - pub fn account(self, account_name: Name, public_key: PublicKey) -> Self { - self.account_with_metadata(account_name, public_key, Metadata::default()) + pub fn account(self, signatory: PublicKey) -> Self { + self.account_with_metadata(signatory, Metadata::default()) } /// Add an account (having provided `metadata`) to this domain. - pub fn account_with_metadata( - mut self, - account_name: Name, - public_key: PublicKey, - metadata: Metadata, - ) -> Self { - let account_id = AccountId::new(self.domain_id.clone(), account_name); - let register = - Register::account(Account::new(account_id, public_key).with_metadata(metadata)); + pub fn account_with_metadata(mut self, signatory: PublicKey, metadata: Metadata) -> Self { + let account_id = AccountId::new(self.domain_id.clone(), signatory); + let register = Register::account(Account::new(account_id).with_metadata(metadata)); self.transaction.isi.push(register.into()); self } @@ -352,6 +350,8 @@ impl RawGenesisDomainBuilder { #[cfg(test)] mod tests { + use test_samples::{ALICE_KEYPAIR, BOB_KEYPAIR}; + use super::*; fn dummy_executor() -> Executor { @@ -367,7 +367,7 @@ mod tests { let _genesis_block = GenesisNetwork::new( RawGenesisBlockBuilder::default() .domain("wonderland".parse()?) - .account("alice".parse()?, alice_public_key) + .account(alice_public_key) .finish_domain() .executor_blob(dummy_executor()) .build(), @@ -379,19 +379,26 @@ mod tests { #[test] fn genesis_block_builder_example() { - let public_key = "ed0120204E9593C3FFAF4464A6189233811C297DD4CE73ABA167867E4FBD4F8C450ACB"; + let public_key: std::collections::HashMap<&'static str, PublicKey> = [ + ("alice", ALICE_KEYPAIR.public_key().clone()), + ("bob", BOB_KEYPAIR.public_key().clone()), + ("cheshire_cat", KeyPair::random().into_parts().0), + ("mad_hatter", KeyPair::random().into_parts().0), + ] + .into_iter() + .collect(); let mut genesis_builder = RawGenesisBlockBuilder::default(); genesis_builder = genesis_builder .domain("wonderland".parse().unwrap()) - .account_with_random_public_key("alice".parse().unwrap()) - .account_with_random_public_key("bob".parse().unwrap()) + .account(public_key["alice"].clone()) + .account(public_key["bob"].clone()) .finish_domain() .domain("tulgey_wood".parse().unwrap()) - .account_with_random_public_key("Cheshire_Cat".parse().unwrap()) + .account(public_key["cheshire_cat"].clone()) .finish_domain() .domain("meadow".parse().unwrap()) - .account("Mad_Hatter".parse().unwrap(), public_key.parse().unwrap()) + .account(public_key["mad_hatter"].clone()) .asset( "hats".parse().unwrap(), AssetValueType::Numeric(NumericSpec::default()), @@ -408,18 +415,18 @@ mod tests { ); assert_eq!( finished_genesis_block.transactions[0].isi[1], - Register::account(Account::new( - AccountId::new(domain_id.clone(), "alice".parse().unwrap()), - KeyPair::random().into_parts().0, - )) + Register::account(Account::new(AccountId::new( + domain_id.clone(), + public_key["alice"].clone() + ),)) .into() ); assert_eq!( finished_genesis_block.transactions[0].isi[2], - Register::account(Account::new( - AccountId::new(domain_id, "bob".parse().unwrap()), - KeyPair::random().into_parts().0, - )) + Register::account(Account::new(AccountId::new( + domain_id, + public_key["bob"].clone() + ),)) .into() ); } @@ -431,10 +438,10 @@ mod tests { ); assert_eq!( finished_genesis_block.transactions[0].isi[4], - Register::account(Account::new( - AccountId::new(domain_id, "Cheshire_Cat".parse().unwrap()), - KeyPair::random().into_parts().0, - )) + Register::account(Account::new(AccountId::new( + domain_id, + public_key["cheshire_cat"].clone() + ),)) .into() ); } @@ -446,10 +453,10 @@ mod tests { ); assert_eq!( finished_genesis_block.transactions[0].isi[6], - Register::account(Account::new( - AccountId::new(domain_id, "Mad_Hatter".parse().unwrap()), - public_key.parse().unwrap(), - )) + Register::account(Account::new(AccountId::new( + domain_id, + public_key["mad_hatter"].clone() + ),)) .into() ); assert_eq!( diff --git a/schema/gen/src/lib.rs b/schema/gen/src/lib.rs index e3c56afd90c..0a95dd31b40 100644 --- a/schema/gen/src/lib.rs +++ b/schema/gen/src/lib.rs @@ -63,7 +63,6 @@ types!( AccountEventFilter, AccountEventSet, AccountId, - AccountMintBox, AccountPermissionChanged, AccountRoleChanged, Action, @@ -91,9 +90,7 @@ types!( BTreeMap, BTreeMap, BTreeSet, - BTreeSet, BTreeSet>, - BTreeSet>, BatchedResponse, BatchedResponseV1, BlockEvent, @@ -109,14 +106,12 @@ types!( Box, Burn, Burn, - Burn, BurnBox, ChainId, ConfigurationEvent, ConfigurationEventFilter, ConfigurationEventSet, ConstString, - ConstVec, ConstVec, Container, DataEvent, @@ -145,7 +140,6 @@ types!( FindAccountById, FindAccountKeyValueByIdAndKey, FindAccountsByDomainId, - FindAccountsByName, FindAccountsWithAsset, FindAllAccounts, FindAllActiveTriggerIds, @@ -219,8 +213,6 @@ types!( MetadataValueBox, Mint, Mint, - Mint, - Mint, MintBox, MintabilityError, Mintable, @@ -317,14 +309,11 @@ types!( SetKeyValueBox, SetParameter, Signature, - SignatureCheckCondition, SignatureOf, SignatureOf, SignatureOf, SignatureWrapperOf, - SignatureWrapperOf, SignaturesOf, - SignaturesOf, SignedBlock, SignedBlockV1, SignedQuery, diff --git a/smart_contract/executor/derive/src/default.rs b/smart_contract/executor/derive/src/default.rs index cb2778b85e5..9723b534c20 100644 --- a/smart_contract/executor/derive/src/default.rs +++ b/smart_contract/executor/derive/src/default.rs @@ -125,9 +125,6 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn::DeriveInput) -> Tok "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)", - "fn visit_mint_account_signature_check_condition(operation: &Mint)", "fn visit_set_account_key_value(operation: &SetKeyValue)", "fn visit_remove_account_key_value(operation: &RemoveKeyValue)", "fn visit_register_asset(operation: &Register)", diff --git a/smart_contract/executor/derive/src/lib.rs b/smart_contract/executor/derive/src/lib.rs index 713e099e810..7e605a9adb0 100644 --- a/smart_contract/executor/derive/src/lib.rs +++ b/smart_contract/executor/derive/src/lib.rs @@ -88,7 +88,7 @@ pub fn entrypoint(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// CanDoSomethingWithAsset { /// some_data: "some data".to_owned(), -/// asset_id: parse!("rose#wonderland" as AssetId), +/// asset_id: parse!(AssetId, "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland"), /// }.is_owned_by(&authority) /// } /// ``` diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index 79eff62ad71..584a9048253 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -6,9 +6,8 @@ pub mod tokens; use alloc::format; pub use account::{ - visit_burn_account_public_key, visit_mint_account_public_key, - visit_mint_account_signature_check_condition, visit_register_account, - visit_remove_account_key_value, visit_set_account_key_value, visit_unregister_account, + visit_register_account, visit_remove_account_key_value, visit_set_account_key_value, + visit_unregister_account, }; pub use asset::{ visit_burn_asset_numeric, visit_mint_asset_numeric, visit_register_asset, @@ -498,85 +497,6 @@ pub mod account { deny!(executor, "Can't unregister another account"); } - pub fn visit_mint_account_public_key( - executor: &mut V, - authority: &AccountId, - isi: &Mint, - ) { - let account_id = isi.destination_id(); - - if is_genesis(executor) { - execute!(executor, isi); - } - match is_account_owner(account_id, authority) { - Err(err) => deny!(executor, err), - 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) { - execute!(executor, isi); - } - - deny!(executor, "Can't mint public keys of another account"); - } - - pub fn visit_burn_account_public_key( - executor: &mut V, - authority: &AccountId, - isi: &Burn, - ) { - let account_id = isi.destination_id(); - - if is_genesis(executor) { - execute!(executor, isi); - } - match is_account_owner(account_id, authority) { - Err(err) => deny!(executor, err), - 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) { - execute!(executor, isi); - } - - deny!(executor, "Can't burn public keys of another account"); - } - - pub fn visit_mint_account_signature_check_condition( - executor: &mut V, - authority: &AccountId, - isi: &Mint, - ) { - let account_id = isi.destination_id(); - - if is_genesis(executor) { - execute!(executor, isi); - } - match is_account_owner(account_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_mint_user_signature_check_conditions_token = - tokens::account::CanMintUserSignatureCheckConditions { - account_id: account_id.clone(), - }; - if can_mint_user_signature_check_conditions_token.is_owned_by(authority) { - execute!(executor, isi); - } - - deny!( - executor, - "Can't mint signature check conditions of another account" - ); - } - pub fn visit_set_account_key_value( executor: &mut V, authority: &AccountId, @@ -794,7 +714,7 @@ pub mod asset_definition { isi: &Transfer, ) { let source_id = isi.source_id(); - let destination_id = isi.object(); + let asset_definition_id = isi.object(); if is_genesis(executor) { execute!(executor, isi); @@ -804,7 +724,7 @@ pub mod asset_definition { Ok(true) => execute!(executor, isi), Ok(false) => {} } - match is_asset_definition_owner(destination_id, authority) { + match is_asset_definition_owner(asset_definition_id, authority) { Err(err) => deny!(executor, err), Ok(true) => execute!(executor, isi), Ok(false) => {} diff --git a/smart_contract/src/lib.rs b/smart_contract/src/lib.rs index b97c3800336..19cf0744920 100644 --- a/smart_contract/src/lib.rs +++ b/smart_contract/src/lib.rs @@ -74,27 +74,37 @@ pub fn stub_getrandom(_dest: &mut [u8]) -> Result<(), getrandom::Error> { unimplemented!("{ERROR_MESSAGE}") } -/// Macro to parse literal as a type. Panics if failed. +/// Returns the annotated type of value parsed from the given expression, or fails with [`dbg_expect`](debug::DebugExpectExt::dbg_expect) message. +/// Panics if the internal parsing fails. /// -/// # Example +/// # Examples /// -/// ```ignore -/// use iroha_smart_contract::{prelude::*, parse}; +/// ``` +/// use iroha_smart_contract::{parse, prelude::*}; /// -/// let account_id = parse!("alice@wonderland" as AccountId); +/// let from_literal = parse!(DomainId, "wonderland"); +/// let expr = "wonderland"; +/// // Although "expr" would be less informative in debug message +/// let from_expr = parse!(DomainId, expr); /// ``` #[macro_export] macro_rules! parse { - ($l:literal as _) => { + (_, $e:expr) => { compile_error!( "Don't use `_` as a type in this macro, \ otherwise panic message would be less informative" ) }; - ($l:literal as $t:ty) => { + ($t:ty, $e:expr) => { $crate::debug::DebugExpectExt::dbg_expect( - $l.parse::<$t>(), - concat!("Failed to parse `", $l, "` as `", stringify!($t), "`"), + $e.parse::<$t>(), + concat!( + "Failed to parse `", + stringify!($e), + "` as `", + stringify!($t), + "`" + ), ) }; } @@ -489,14 +499,12 @@ mod tests { const ISI_RESULT: Result<(), ValidationFail> = Ok(()); fn get_test_instruction() -> InstructionBox { - let new_asset_id = "tulip##alice@wonderland".parse().unwrap(); - let register_isi = Register::asset(Asset::new(new_asset_id, 1_u32)); - - register_isi.into() + let new_asset_id: AssetId = "tulip##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse().unwrap(); + Register::asset(Asset::new(new_asset_id, 1_u32)).into() } fn get_test_query() -> QueryBox { - let asset_id: AssetId = "rose##alice@wonderland".parse().expect("Valid"); + let asset_id: AssetId = "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse().unwrap(); FindAssetQuantityById::new(asset_id).into() } diff --git a/test_samples/Cargo.toml b/test_samples/Cargo.toml new file mode 100644 index 00000000000..276af31906f --- /dev/null +++ b/test_samples/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "test_samples" +edition.workspace = true +version.workspace = true +authors.workspace = true +description.workspace = true +repository.workspace = true +documentation.workspace = true +homepage.workspace = true +license.workspace = true +keywords.workspace = true +categories.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +iroha_crypto = { workspace = true } +iroha_data_model = { workspace = true } + +once_cell = { workspace = true } +serde = { workspace = true, features = ["derive"] } +toml = { workspace = true } + +[lints] +workspace = true + +[features] +default = ["rand"] +rand = ["iroha_crypto/rand"] diff --git a/test_samples/src/lib.rs b/test_samples/src/lib.rs new file mode 100644 index 00000000000..ae381e8fa12 --- /dev/null +++ b/test_samples/src/lib.rs @@ -0,0 +1,43 @@ +//! Utility crate for standardized and random signatories. + +use iroha_crypto::{Algorithm, KeyPair, PrivateKey}; +use iroha_data_model::prelude::AccountId; +use once_cell::sync::Lazy; + +/// Generate [`AccountId`](iroha_data_model::account::AccountId) in the given `domain`. +/// +/// # Panics +/// +/// Panics if the given `domain` is invalid as [`Name`](iroha_data_model::name::Name). +#[cfg(feature = "rand")] +pub fn gen_account_in(domain: impl core::fmt::Display) -> (AccountId, KeyPair) { + let key_pair = KeyPair::random(); + let account_id = format!("{}@{}", key_pair.public_key(), domain) + .parse() + .expect("domain name should be valid"); + (account_id, key_pair) +} + +macro_rules! static_signatory_ed25519 { + ( $kp:ident, $vk:expr, $sk:expr ) => { + /// A standardized [`KeyPair`](iroha_crypto::KeyPair). + pub static $kp: Lazy = Lazy::new(|| { + KeyPair::new( + $vk.parse().unwrap(), + PrivateKey::from_hex(Algorithm::Ed25519, $sk).unwrap(), + ) + .unwrap() + }); + }; + ( $id:ident, $dm:literal, $kp:ident, $vk:literal, $sk:literal ) => { + /// A standardized [`AccountId`](iroha_data_model::account::AccountId). + pub static $id: Lazy = + Lazy::new(|| format!("{}@{}", $kp.public_key(), $dm).parse().unwrap()); + + static_signatory_ed25519!($kp, $vk, $sk); + }; +} +static_signatory_ed25519!(PEER_KEYPAIR, "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0", "9AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"); +static_signatory_ed25519!(ALICE_ID, "wonderland", ALICE_KEYPAIR, "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03", "CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03"); +static_signatory_ed25519!(BOB_ID, "wonderland", BOB_KEYPAIR, "ed012004FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016", "AF3F96DEEF44348FEB516C057558972CEC4C75C4DB9C5B3AAC843668854BF82804FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016"); +static_signatory_ed25519!(CARPENTER_ID, "garden_of_live_flowers", CARPENTER_KEYPAIR, "ed0120E9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99", "B5DD003D106B273F3628A29E6087C31CE12C9F32223BE26DD1ADB85CEBB48E1DE9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99"); diff --git a/tools/kagami/Cargo.toml b/tools/kagami/Cargo.toml index 7a4d4145ecf..1f076c54b85 100644 --- a/tools/kagami/Cargo.toml +++ b/tools/kagami/Cargo.toml @@ -19,6 +19,7 @@ iroha_data_model = { workspace = true } iroha_schema_gen = { workspace = true } iroha_primitives = { workspace = true } iroha_genesis = { workspace = true } +test_samples = { workspace = true } color-eyre = { workspace = true } clap = { workspace = true, features = ["derive"] } diff --git a/tools/kagami/src/genesis.rs b/tools/kagami/src/genesis.rs index 1a7292df8b8..a89251fa8a6 100644 --- a/tools/kagami/src/genesis.rs +++ b/tools/kagami/src/genesis.rs @@ -12,8 +12,11 @@ use iroha_data_model::{ parameter::{default::*, ParametersBuilder}, prelude::AssetId, }; -use iroha_genesis::{executor_state, RawGenesisBlockBuilder, RawGenesisBlockFile}; +use iroha_genesis::{ + executor_state, RawGenesisBlockBuilder, RawGenesisBlockFile, GENESIS_ACCOUNT_ID, +}; use serde_json::json; +use test_samples::{gen_account_in, ALICE_ID, BOB_ID, CARPENTER_ID}; use super::*; @@ -81,19 +84,15 @@ pub fn generate_default( let mut genesis = builder .domain_with_metadata("wonderland".parse()?, meta.clone()) - .account_with_metadata( - "alice".parse()?, - crate::DEFAULT_PUBLIC_KEY.parse()?, - meta.clone(), - ) - .account_with_metadata("bob".parse()?, crate::DEFAULT_PUBLIC_KEY.parse()?, meta) + .account_with_metadata(ALICE_ID.signatory().clone(), meta.clone()) + .account_with_metadata(BOB_ID.signatory().clone(), meta) .asset( "rose".parse()?, AssetValueType::Numeric(NumericSpec::default()), ) .finish_domain() .domain("garden_of_live_flowers".parse()?) - .account("carpenter".parse()?, crate::DEFAULT_PUBLIC_KEY.parse()?) + .account(CARPENTER_ID.signatory().clone()) .asset( "cabbage".parse()?, AssetValueType::Numeric(NumericSpec::default()), @@ -101,33 +100,37 @@ pub fn generate_default( .finish_domain() .build(); - let alice_id = AccountId::from_str("alice@wonderland")?; let mint = Mint::asset_numeric( 13u32, - AssetId::new("rose#wonderland".parse()?, alice_id.clone()), + AssetId::new("rose#wonderland".parse()?, ALICE_ID.clone()), ); let mint_cabbage = Mint::asset_numeric( 44u32, - AssetId::new("cabbage#garden_of_live_flowers".parse()?, alice_id.clone()), + AssetId::new("cabbage#garden_of_live_flowers".parse()?, ALICE_ID.clone()), ); let grant_permission_to_set_parameters = Grant::permission( PermissionToken::new("CanSetParameters".parse()?, &json!(null)), - alice_id.clone(), + ALICE_ID.clone(), + ); + let transfer_rose_ownership = Transfer::asset_definition( + GENESIS_ACCOUNT_ID.clone(), + "rose#wonderland".parse()?, + ALICE_ID.clone(), ); - let transfer_domain_ownerhip = Transfer::domain( - "genesis@genesis".parse()?, + let transfer_wonderland_ownership = Transfer::domain( + GENESIS_ACCOUNT_ID.clone(), "wonderland".parse()?, - alice_id.clone(), + ALICE_ID.clone(), ); let register_user_metadata_access = Register::role( Role::new("ALICE_METADATA_ACCESS".parse()?) .add_permission(PermissionToken::new( "CanSetKeyValueInAccount".parse()?, - &json!({ "account_id": alice_id }), + &json!({ "account_id": ALICE_ID.clone() }), )) .add_permission(PermissionToken::new( "CanRemoveKeyValueInAccount".parse()?, - &json!({ "account_id": alice_id }), + &json!({ "account_id": ALICE_ID.clone() }), )), ) .into(); @@ -176,7 +179,8 @@ pub fn generate_default( for isi in [ mint.into(), mint_cabbage.into(), - transfer_domain_ownerhip.into(), + transfer_rose_ownership.into(), + transfer_wonderland_ownership.into(), grant_permission_to_set_parameters.into(), ] .into_iter() @@ -207,12 +211,10 @@ fn generate_synthetic( first_transaction .append_instruction(Register::domain(Domain::new(domain_id.clone())).into()); - for account in 0..accounts_per_domain { - let (public_key, _) = iroha_crypto::KeyPair::random().into_parts(); - let account_id: AccountId = format!("account_{account}@{domain_id}").parse()?; - first_transaction.append_instruction( - Register::account(Account::new(account_id.clone(), public_key)).into(), - ); + for _ in 0..accounts_per_domain { + let (account_id, _account_keypair) = gen_account_in(&domain_id); + first_transaction + .append_instruction(Register::account(Account::new(account_id.clone())).into()); } for asset in 0..assets_per_domain { diff --git a/tools/kagami/src/main.rs b/tools/kagami/src/main.rs index 16228701582..270e140994e 100644 --- a/tools/kagami/src/main.rs +++ b/tools/kagami/src/main.rs @@ -1,10 +1,7 @@ //! CLI for generating iroha sample configuration, genesis and //! cryptographic key pairs. To be used with all compliant Iroha //! installations. -use std::{ - io::{stdout, BufWriter, Write}, - str::FromStr as _, -}; +use std::io::{stdout, BufWriter, Write}; use clap::{Args as ClapArgs, Parser}; use color_eyre::eyre::WrapErr as _; @@ -17,12 +14,6 @@ mod schema; /// Outcome shorthand used throughout this crate pub(crate) type Outcome = color_eyre::Result<()>; -// The reason for hard-coding this default is to ensure that the -// algorithm is matched to the public key in Ed25519 format. If -// you need to change either, you should definitely change both. -const DEFAULT_PUBLIC_KEY: &str = - "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"; - fn main() -> Outcome { color_eyre::install()?; let args = Args::parse(); diff --git a/tools/parity_scale_cli/Cargo.toml b/tools/parity_scale_cli/Cargo.toml index 2b797c55a8f..aa8bf8d2c29 100644 --- a/tools/parity_scale_cli/Cargo.toml +++ b/tools/parity_scale_cli/Cargo.toml @@ -38,3 +38,6 @@ parity-scale-codec = { workspace = true } serde_json = { workspace = true, features = ["std"]} serde = { workspace = true } eyre = { workspace = true } + +[dev-dependencies] +test_samples = { workspace = true } diff --git a/tools/parity_scale_cli/samples/account.bin b/tools/parity_scale_cli/samples/account.bin index c0ac2716337241b4188de397c53069ecccff7fff..b9ece2bea129b09d08ab75f268ff25124506eb1b 100644 GIT binary patch literal 57 zcmV-90LK3)cW-WFWpZp`Ze##}&VQtAoz8xwu_fZ;YujV$GY39(kgSq{YT@W%w*-p= P1Po|lbOIE2XlZn12nrdD literal 64 zcmdNW&(BLqEy_vEOA$%T$xKdVVQ6^!?fO=o0;3C7Z!+^0GWG@wh-!R3zKuirKkLFY U-TFhWEIb*BB}^ja8JQ)i09qXxvH$=8 diff --git a/tools/parity_scale_cli/samples/account.json b/tools/parity_scale_cli/samples/account.json index 7bb4321521b..c749623fb95 100644 --- a/tools/parity_scale_cli/samples/account.json +++ b/tools/parity_scale_cli/samples/account.json @@ -1,8 +1,5 @@ { - "id": "alice@wonderland", - "signatories": [ - "ed0120EDF6D7B52C7032D03AEC696F2068BD53101528F3C7B6081BFF05A1662D7FC245" - ], + "id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland", "metadata": { "hat": { "Name": "white" diff --git a/tools/parity_scale_cli/samples/trigger.bin b/tools/parity_scale_cli/samples/trigger.bin index bf1bf3f4b9e0f3dd4531d14c81a9540770bbaa9e..35d40540b8b3e8c4e6bd6d2b9b8c6a8bffc9f2a3 100644 GIT binary patch literal 132 zcmZQj$<53wi7(18PGw+WVqjs=D9_JJNiE7r%u5jfi6ZeC8qU=($(ehuZplW~M~|~_ iC%-agw=12nW>RC;!xxF$S-P2#brV+2$i%?N00aQI|28lH literal 76 zcmZQj$<53wi7(18PGw+WVq{^^D9_JJNiE7r%u5jfi6Zeu5_2+>Q;}6bL>QSE7#V;7 E0H9YE^8f$< diff --git a/tools/parity_scale_cli/samples/trigger.json b/tools/parity_scale_cli/samples/trigger.json index bcc84114c88..6c74b0f5a53 100644 --- a/tools/parity_scale_cli/samples/trigger.json +++ b/tools/parity_scale_cli/samples/trigger.json @@ -7,14 +7,14 @@ "Mint": { "Asset": { "object": "1", - "destination_id": "rose##alice@wonderland" + "destination_id": "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } } ] }, "repeats": "Indefinitely", - "authority": "alice@wonderland", + "authority": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland", "filter": { "Data": { "Domain": { diff --git a/tools/parity_scale_cli/src/main.rs b/tools/parity_scale_cli/src/main.rs index 3f14c549f84..b1dca086904 100644 --- a/tools/parity_scale_cli/src/main.rs +++ b/tools/parity_scale_cli/src/main.rs @@ -321,6 +321,7 @@ mod tests { use std::str::FromStr as _; use iroha_data_model::{ipfs::IpfsPath, prelude::*}; + use test_samples::gen_account_in; use super::*; @@ -335,12 +336,7 @@ mod tests { limits, ) .expect("Valid"); - let signature = PublicKey::from_str( - "ed0120EDF6D7B52C7032D03AEC696F2068BD53101528F3C7B6081BFF05A1662D7FC245", - ) - .unwrap(); - let account = - Account::new("alice@wonderland".parse().unwrap(), signature).with_metadata(metadata); + let account = Account::new(gen_account_in("wonderland").0).with_metadata(metadata); decode_sample("account.bin", String::from("NewAccount"), &account); } @@ -364,7 +360,7 @@ mod tests { #[test] fn decode_trigger_sample() { - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let (account_id, _account_keypair) = gen_account_in("wonderland"); let rose_definition_id = AssetDefinitionId::new( "wonderland".parse().expect("Valid"), "rose".parse().expect("Valid"), diff --git a/torii/src/lib.rs b/torii/src/lib.rs index 94d3b11ad79..c2445065125 100644 --- a/torii/src/lib.rs +++ b/torii/src/lib.rs @@ -341,7 +341,7 @@ impl Error { Config(_) | StatusSegmentNotFound(_) => StatusCode::NOT_FOUND, PushIntoQueue(err) => match **err { queue::Error::Full => StatusCode::INTERNAL_SERVER_ERROR, - queue::Error::SignatureCondition => StatusCode::UNAUTHORIZED, + queue::Error::SignatoryInconsistent => StatusCode::UNAUTHORIZED, _ => StatusCode::BAD_REQUEST, }, #[cfg(feature = "telemetry")] diff --git a/torii/src/routing.rs b/torii/src/routing.rs index edff834fed4..2d2aa7fcdd7 100644 --- a/torii/src/routing.rs +++ b/torii/src/routing.rs @@ -360,6 +360,7 @@ pub mod profiling { use super::*; /// Query params used to configure profile gathering + #[allow(clippy::unsafe_derive_deserialize)] #[derive(Serialize, Deserialize, Clone, Copy)] pub struct ProfileParams { /// How often to sample iroha