From 6f39591892108fb886a1d1ee531627a4ea9060ae 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 | 16 +- 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 | 24 +- client/examples/register_1000_triggers.rs | 9 +- client/examples/tutorial.rs | 59 ++- 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 | 5 +- .../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 | 8 +- .../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/.gitignore | 2 + 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 | 12 +- client_cli/src/main.rs | 63 +-- config/iroha_test_config.toml | 3 +- configs/client.template.toml | 2 +- configs/swarm/client.toml | 6 +- configs/swarm/executor.wasm | Bin 526638 -> 516792 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 | 59 +-- core/src/lib.rs | 7 +- core/src/queue.rs | 299 +++--------- core/src/smartcontracts/isi/account.rs | 117 +---- core/src/smartcontracts/isi/domain.rs | 9 +- core/src/smartcontracts/isi/mod.rs | 69 +-- 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 | 15 +- core/test_network/Cargo.toml | 2 +- core/test_network/src/lib.rs | 38 +- .../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/name.rs | 2 +- data_model/src/query/mod.rs | 17 +- data_model/src/query/predicate.rs | 97 ++-- data_model/src/transaction.rs | 38 +- data_model/src/trigger.rs | 2 +- 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 | 93 ++-- hooks/pre-commit.sample | 28 +- schema/gen/src/lib.rs | 11 - scripts/tests/consistency.sh | 5 +- 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 | 56 +++ tools/kagami/Cargo.toml | 1 + tools/kagami/src/genesis.rs | 58 ++- tools/kagami/src/main.rs | 11 +- tools/parity_scale_cli/Cargo.toml | 3 + tools/parity_scale_cli/README.md | 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 | 13 +- torii/src/lib.rs | 2 +- torii/src/routing.rs | 1 + 146 files changed, 1520 insertions(+), 2985 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 client_cli/.gitignore 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..ab9f3b40689 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -486,18 +486,18 @@ impl Iroha { } fn genesis_account(public_key: PublicKey) -> Account { - Account::new(iroha_genesis::GENESIS_ACCOUNT_ID.clone(), public_key) - .build(&iroha_genesis::GENESIS_ACCOUNT_ID) + let genesis_account_id = AccountId::new(iroha_genesis::GENESIS_DOMAIN_ID.clone(), public_key); + Account::new(genesis_account_id.clone()).build(&genesis_account_id) } fn genesis_domain(public_key: PublicKey) -> Domain { - let mut domain = Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()) - .build(&iroha_genesis::GENESIS_ACCOUNT_ID); + let genesis_account = genesis_account(public_key); + let mut domain = + Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()).build(&genesis_account.id); - domain.accounts.insert( - iroha_genesis::GENESIS_ACCOUNT_ID.clone(), - genesis_account(public_key), - ); + domain + .accounts + .insert(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..afc4cb5d598 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -1,8 +1,9 @@ -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::{ client::Client, + crypto::KeyPair, data_model::{ parameter::{default::MAX_TRANSACTIONS_IN_BLOCK, ParametersBuilder}, prelude::*, @@ -12,6 +13,7 @@ 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..03114c82088 100644 --- a/client/examples/million_accounts_genesis.rs +++ b/client/examples/million_accounts_genesis.rs @@ -2,8 +2,7 @@ use std::{thread, time::Duration}; use iroha::samples::{construct_executor, get_config}; -use iroha_client::data_model::prelude::*; -use iroha_crypto::KeyPair; +use iroha_client::{crypto::KeyPair, data_model::prelude::*}; use iroha_data_model::isi::InstructionBox; use iroha_genesis::{GenesisNetwork, RawGenesisBlock, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; @@ -16,14 +15,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 +41,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 +71,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..a5194db6f3d 100644 --- a/client/examples/tutorial.rs +++ b/client/examples/tutorial.rs @@ -75,14 +75,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_client::{crypto::KeyPair, data_model::prelude::AccountId}; - // 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 +111,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 @@ -147,6 +147,7 @@ fn asset_registration_test(config: Config) -> Result<(), Error> { use iroha_client::{ client::Client, + crypto::KeyPair, data_model::prelude::{ numeric, AccountId, AssetDefinition, AssetDefinitionId, AssetId, Mint, Register, }, @@ -171,10 +172,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 +208,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 +225,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 +262,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 +279,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 e93b119a43b..70a8f1bab3a 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 = "8026409AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" + domain_id = "wonderland" + public_key = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" + private_key = "802640CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" [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..b6455eed100 100644 --- a/client/tests/integration/extra_functional/offline_peers.rs +++ b/client/tests/integration/extra_functional/offline_peers.rs @@ -1,15 +1,16 @@ use eyre::Result; use iroha_client::{ client::{self, Client, QueryResult}, + crypto::KeyPair, data_model::{ peer::{Peer as DataModelPeer, PeerId}, prelude::*, }, }; 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..f34d4f2eb57 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,9 @@ struct Executor { } fn visit_instruction(executor: &mut Executor, authority: &AccountId, isi: &InstructionBox) { - if parse!("admin@admin" as AccountId) == *authority { + // multihash equals to integration::upgrade::ADMIN_PUBLIC_KEY_MULTIHASH + let admin_id = "ed012076E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC@admin"; + 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..5e8fa205021 100644 --- a/client/tests/integration/sorting.rs +++ b/client/tests/integration/sorting.rs @@ -3,6 +3,7 @@ use std::{collections::HashSet, str::FromStr as _}; use eyre::{Result, WrapErr as _}; use iroha_client::{ client::{self, QueryResult}, + crypto::KeyPair, data_model::{ account::Account, prelude::*, @@ -12,121 +13,127 @@ use iroha_client::{ }, }, }; -use iroha_data_model::isi::InstructionBox; 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..4cc36f6cfbb 100644 --- a/client/tests/integration/upgrade.rs +++ b/client/tests/integration/upgrade.rs @@ -9,29 +9,41 @@ use iroha_client::{ use iroha_logger::info; use serde_json::json; use test_network::*; +use test_samples::ALICE_ID; + +const ADMIN_PUBLIC_KEY_MULTIHASH: &str = + "ed012076E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC"; +const ADMIN_PRIVATE_KEY_MULTIHASH: &str = "802640A4DE33BCA99A254ED6265D1F0FB69DFE42B77F89F6C2E478498E1831BF6D81F276E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC"; #[test] fn executor_upgrade_should_work() -> Result<()> { let chain_id = ChainId::from("0"); + let admin_id: AccountId = format!("{ADMIN_PUBLIC_KEY_MULTIHASH}@admin") + .parse() + .unwrap(); + let admin_keypair = KeyPair::new( + admin_id.signatory().clone(), + ADMIN_PRIVATE_KEY_MULTIHASH.parse().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 +58,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 +83,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 +159,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/.gitignore b/client_cli/.gitignore new file mode 100644 index 00000000000..d7b89fefd49 --- /dev/null +++ b/client_cli/.gitignore @@ -0,0 +1,2 @@ +peers_configs/ +isi_unregister_asset.json 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..2f4d2b7dc26 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 = "Empty" 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..b9164bbabe5 100644 --- a/client_cli/pytests/test/triggers/test_register_trigger.py +++ b/client_cli/pytests/test/triggers/test_register_trigger.py @@ -18,6 +18,14 @@ 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##\ + ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4@genesis" + # TODO use the same source as GENESIS_PUBLIC_KEY of peer ): - iroha.should(have.asset("nft_number_1_for_genesis##genesis@genesis")) + iroha.should( + have.asset( + "nft_number_1_for_genesis##\ + ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4@genesis" + # TODO use the same source as GENESIS_PUBLIC_KEY of peer + ) + ) 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 5279f76639c..f56d5dfe1bb 100644 --- a/config/iroha_test_config.toml +++ b/config/iroha_test_config.toml @@ -6,8 +6,8 @@ private_key = "802640282ED9F3CF92811C3818DBC4AE594ED59DC1A2F78E4241E31924E101D6B address = "127.0.0.1:1337" [genesis] -public_key = "ed01204CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" file = "./genesis.json" +public_key = "ed01204CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" private_key = "802640D748E18CE60CB30DEA3E73C9019B7AF45A8D465E3D71BCC9A5EF99A008205E534CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" [torii] @@ -31,4 +31,3 @@ public_key = "ed01208E351A70B6A603ED285D666B8D689B680865913BA03CE29FB7D13A166C4E [logger] format = "pretty" - diff --git a/configs/client.template.toml b/configs/client.template.toml index 7322b788374..858a4c1df8e 100644 --- a/configs/client.template.toml +++ b/configs/client.template.toml @@ -8,7 +8,7 @@ # password = [account] -# id = +# domain_id = # public_key = # private_key = diff --git a/configs/swarm/client.toml b/configs/swarm/client.toml index 750be32505b..f8fd0815237 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 = "8026409AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" +domain_id = "wonderland" +public_key = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" +private_key = "802640CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm index 9a8965b42ffac44c5e47cf11d88a9702c48963e0..dc5f312aef7c6b34ca6a444df5d7e747454263be 100644 GIT binary patch literal 516792 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

f8DY!jQ)tSgI5c_MlXj7Dquc}`r(Rc`@~7; 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∓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

xN!`k|Ywz9}-EqQiSwiklBM&jH(6R-& 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 Cq18$N literal 526638 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 ImQLgQ34z`J#v+M*@?fc($t+bPi4tj-_O z0RHXs2k4^Hjb+~-NNm>}zd^QBfx-L%9mxKq9MFMu;$0 zd1gS~T|4t`CjnzTx;p>+cf0|~Zmb&sUO|wLV<-Ndylu;<3GzYua~r)aQ0@Q*>Tla% zHuMab#6t^@5C8|Y51-X7hb=?f&K1c<6Wp1$v+ks^t8UQkc0B&eLGz4(}>-o?Q^xJ#k8o+i6;IwSz}fw6`MFva>6j$4*ufuW%RuoLxiht3aIuei0rT@{M4^L4|SU|_tl zSd4>@kB7Ikh={GbjkmCux5#g#BH&yRSf_4^aJ91Wau;4dIJH>CX7M|Nu!E1Qi!eqJ z7@7N9Z8C84+8~ItEwqO=IH8?IoWU75`i5@q1yyeA2wk%ZR@7h#4_#{vYW_FW>Q(~O zFub6u^x~?lPlO~z~up(0p7rX9$fPZEa*Yg@BjvP(9ORa!ojRc z?l)_~z;*mh9gLJiK|%86S@ZVV0~u}FmVue}<_8#~mb?Xk_)s$j?h26|AOHuk zZ=erRv65buE0_x-AMR&(VHb^zl$F$;}W0citEZct^0( z5V#|>6XYv}dxktYQ(W#z+dwdzUPwg6`D7cQpi!ypjpx`-R|y}&rzE^*w)6EOf~o@b z?8W=~{f+Uz?uXvUN&Du%^s)OG`-|*@WqMZ&FVX)%JT=OIT~9fO*jMR-{FR14*41Ys zP5B5AMy2D3zJ*}KWp_5%y*DW2GS>|J$z=sN?G;XVIsFohUojH-s8$={mx4xS@AgOB zrJw>QoC;sN0Ox$;@Dw-;$#Dd=>19~QkvDMliK}pFa&qLCKxM>z1s2#TtN`Lz6&sY{ zK{v=S3(vqXJE)3`4zTLv7<;=Jn{5^^_=e8Ua0jT+J3JKn3xk~r#l1pd&_A#~iCYar zaJa9qwRqyy_)zFj3wof#FX#dBq0pOT4Ibpjh2x+|5EmP|`bMt35o_<&7c7)y?PD!} z=sLa7HMn&?h*t52LahUsfeV43mX#J%9);RoQ2JN=m@DAgu}u#nZSkQGCulbQyyGSa zOHqn*`Mh{@vR%^}b@2_c1E)*CiCjS?ma#!erdvvGrST)1?zGEQxOaVE|5?$&_&{31JSuiwxX|#O zTfNU;t45VAyD{VZx^Zh|L-5&cCd z>~bpl2YHA5!*VigeVCuRL;Wt63!d-xTuSu#0#6gjJe?Lvr&Jfr_dai$BaPPR^R?SZ zDyh9G!fQv_@2wa#RlR5o2RF2(a?nOEuSjs*6H4+QrFX{S+;lz*ghC_HZc z>XI%0#ZTt+D9nB~{fWV_{P%aqm38k>$(IQ++}y3*rl)!Ss8fFYjm!1M)CxJlC6y<2 zcnZ3LdS+ivu}SD)A6Ycz=~Yv=6hD!#XGqNZa#`ce7)2AM%#`GnHtJ9k^y4D^z)}T9 zOPz;vCG7S@K8UzJp*s6RWqHfy#~Af8`1ZP&a7&!NUO%>cM2)7GaXfL#gf;P6h(j#4 zV?WA*ucVyy@uEhmc>ayITyu)j z@^pu$uBKa46BH+YROY5I$k_MgnUBh;?%d*>5%Om>QVx4G;GvDuOQ>T_% z+N(q{gvYVuX4d}jqAcWLy6+@a1n;0@)4FU~?`3rVgQcM$cbhU*3SIqkB%AT<-nMFL z*N@Vyw{F2nkqm|W0$n10XSA*9jbbI-5$_*V8&Xd{aU-jsioNlz^kr+~`R{D^-W)zL z&PrHi94}Aux_9cx1(f^ZBt5~i0Z-W58>eR7DSM9o;-T&!vm4!0o&U31;|^cEWgZ*4 zGcua*mtIO!cDw;8QxpS5;mh`_rW*x>5nX=n#p%=^3z^u=`+E9wUp+FuQ)11oeMarb zkrK6FU4<)!@sF4pyE9+EeQV;{()0X-QSKf(nv-FD{^*m&GJ~C;Z+)ppqxfG~nuMEA zHAwExD=fgf%UGQoddPuc*7>RKn2}+663xX(7{Rwdey&&foCEzqJ}4tXiR=#v{jb8v zUWr}&rO%||GB?8HJ(MpYn?IwxL`i$i=!gCwFRQ*;s-BrsYB$Q-_}qPDuhp3Fluin& zc9v;GC8zO)n%0A($9wwout~HS=LnbX*Pb>-gCFSG*%Z*v(btQ3PqSQC!$VHn+N3d) z_A)%wcP{xvkiIv8b(EGai}p|pW$g49=fg=}Lzr7=sRy0`8ol?FRSTFSUEwM$7B+Pa z9kh)|d_#5Q!z<}@jHAz;Ro?BBl=3HZTrQ@ks#Bm-&9bR8I(pQLBDHjirOp1iSaVPZt?)C+*v$5szcM>Qj7X)b8?I*mWoNh{~NsE zvST;8l4ZVZzL(~~(=+fGn(-o^^CeGB7jB~jiQhToeLJOY@FmnenYWCI`^}L4J>zC+ zl;M?HKa44DgdeMtW#9p8?}FkL^H4hUwVG$I>}Cbh@1aV?TlN?;huFF*=skH5WJx{~ zpJh5g+j{qmeXrwE^RVpWF0G&t^uQT^ucwqEw>m}el)zc{PTP&l)2#3z+`>g`~f7Qz$|4wJ1 zhrk4ST74E;XmWT?YeIEeC;f7Kg+uPC<9tSoiD^koy(;TFev+qPm-wA)b~?Q%-C12W@Jb-LnY@+Skm40U*4tVI zh#-x|mqWL1H^4rir0`#zz08F=)PGP#Bgek&Gxyu_LkaDS1f{L%9iw_(?3MkGuY28~ z*~de2L}k1;kWnz$k+J?X%B1TiI_skaUxKPAWrCz9(+TS8{4kcXs$A#kt}b&PT9KP6 zU$1=RwbqSID0+Y7D~jIt`}_HQ6zL-r8n>?$QZongn=oAv>MGIq>ew}Cbdy%t@Q@ni z!@zTWb?+*}R1DCks!4l-FDB3?Urjyxf~bl)RiuMv1)b;bS2EDyFbfVSV;5rZ-fq5m$vY3LVY8YSLJm)sz(JuYNq!5o^FOyEI5gMCh1C z)qsDihSK2NxQO#4S(ndKR`4GYE_?ZPK0M1==b_5~iXQKQRtxV_s+A5c`*Y6KY8w)a8uirzMJGY%@N|2c15PFnt*I!{b53elq3n7ms0|rujT3kRH{RQCTux!kZCN zkLns2uyQ{PHz_}?<+G>tB~y{c@aJ7`pTFnJl1ohJ@TQ&Y&^@i|@2NRn-qP(Az=s|& z9_<);IC8SYpfDlwoG&wmnqxp;yy}-pe=M0(&OB|X;%5dW-e)$&huu8JN6(|}oVs}S zk&_e9qYpFDE3vVRaTI+O%#n4b@^L#!bv2mINXxipf@L|T*YxNwg^$Zm%aboE zSXiC2l*Dgg8GXY#!dGz1KIF5k=8u;|^wItFm}(BA`{MU)I~TlNsd7`!+6g#v)qkmz zo~r+Pl9lyc2U~1acw4oE^>KvpMS2E?fg^<0dvkxyl@G!T-%*)N+Xi-BjX97!87(DA zEyng?Hlk7Wuk;SCzj_vRgFPpa zMGnEy>qB^NKwkQqKBEr#;TCu~?L59Eqy6HLB27LC5pf#dyB|_zC-i_v6W4eqQ5+sM&{&?n= zOBSEFkQmSOg)8Us=)&u`%4|=0s~$PpY8IUjRmnGH7Q$yPGtsV~1gy%^o>ciZ#=pWm zHmeq6j%DJbb4q#APD=6Fq13vLHuGc^I>!HHue%(3G*7raI_n^w{12$q}D zbK%5+hmapqcjcb>`hu=%@rlD5OP3d+fstR_CJs{ZFA!2hOPu(0W%f1d2-6-`&gA{1 zE{VdrgZxT8M6{;#6*n8-KFEvf2{U~0;Wes+Dw?vJQ`mDL@v7k`gnrmCs|y*8cHA+I z9=dm|FK%2Jqd${YO z{Z)#GFQtx?xYuNQHgle(Y5OSG;=Q0obT+k|=Pk1bs^q$Dz}MpxJ%zgr$P5=Wm>z}b zSWeuQJ6ThCvjb&CLc7OKK6JXaB_&mG{GB(`Ta=#1b2~k|!tX~9mDt`-9B1k^$+<@n z#kKp8%k=VQSwsYBrZVH1*S?oC3r_Cu)V3?==O=viLa_X?@RFvT8MM zvCK?PrF%MFLu$gb{F#`V__Jm|6_s@9FO}nFHhF!K6kWF$zpy+zC+d9rFiTX)OF8Ok zYfkz%ox2}QxqW=OyQjssek6?Q^x2$-FZ9>TJNZ;&)AO&gI(z8ge;;7rR~^U8q(5Ck zA3NQhzN_rLfpc8CX5h7HDj&T$Yxl9|(E%zVr+8EK*vxL2hu3nFzHV@;Z8}Y*$S`#O zh(jy04CVEh73p>g2WnToK=~7bmJFAAE+s__k+E}gB=31+)Hv*jMod zvWYo!gVrWAWFI=1GV{B}o4fA{Xonv{sibk6r+M)Nqvy^Hlp@KP@YGou)MulX$$uPR zJnq;*^D^Tw^&7KmEjbG!G^e6(p)}!WJ^dz|0cq3W`wOInOrG{VK9rd(j3Om$i$C3s zXzTRU_`5?(igrCZA|f)4MoZ|D7sN|^rX_w(btiaCoB0X3=eV31`z==zN?!5UZrb#m zb8%RD#s|hkWEkEzC(y~#r>JD6W&};h-?w((pJ!G|x!C(_=JCXP*)oq0jTUrBG!`Ss&jR?&g(yd&XRW(P;UH5cn!+NGnpG^`nqwt=ZlV{p5UG9WSIGJxkm$wv<2Mrr+1Q zdGRlvQ2kWybgwAbO*u&&*BNw&kj>g@pq8Ig?;wwod!a2a0fSad$mJNVUM&07saPG% zKI+5dt-L;mpSoi&e4#Ag^N8(04srBcrjtn|C8zgX{bh!WcL`m7d>${7>@J^8+FML* zWK8yc`e3j+_iPns1ZNPtN@0h_+=}g+(ZrMyhY?Z6SFr)IKUN}?#=Vul>0@|kd@?Vj zHnd)SF`0mzq|{_b=^^g#$~j~e!cvAm5>nj4NV&JMa~X(t{_Skr3TbTG=-&l6QxBMtEd#f7X6|$CV3g(~-P%W>a597RwubqgN>J%p$AM z!e0&zCvkLh9$X2z%lX=rWy*jcN+Ik-1$Jd6Y2-!;-K^3+@E;XeKdBVo(|h{cZmRL? z)01!qNokYFv7{VYQr0-eIi9rp=8NwgANc5?W#~Ijn4cc}87%TS+Qv-Y=OUF+(o)CE z`P5II-TSPcpRQrO)zch6ejzZj?u5{YYiH7P~=2&B@7dTg%Bo zGpVOQR$JbOZKOPKN-jn}r>8i^_8`oRA==D}cg3Y|wD_p2PV40$>YNk@a%{2X&7pH@ zOeai5*dvs`$yPortBUZgR3%}{JwXX?SZ zarR?cf_rZUHAiIL9vBe+5ywcIYc!8p&b<10>W)VJf;&wCE7d}o81v!#ya^s@$tI}& zl)H6bE6kN<$v=fY{l?35_?aGNX7|zQANA~Rv}s3_3B$M=I*Z=VUf4U#jTR_;EtiHb zYoab;bedDvjrl=rUkmNErT4Cn(fHxble9LOAKlRm%Vpe-3Q}nY;?b|t+CwT#Y)%vU z?%nnDDIE*NM~Xd3G^4ND6X$iLt_9GMvK&n4@vHLjC2x=yWcY~=Xb}69$UXn9S}oiD zy~$%1A^hW&@~nb!O|L81HH#7S+<2!fo(CDZzo8Tu_K0(!B5-ZEq}DVM{Ot}}R*TAq z^?J><_XL@bE*aP7FXd1q(hoNu(QFTe9SThQ+N_h?O_ik_sK(j`>xzF`q#Q=X#}@ul z$d$fp5B0%U#_3mHV;I`$*7&&Rp74(`8f3H8Ng0+C zvo$+gx%iZTs)C_1uVAd@+}*<-f)`&oQ#_$I&MBaxrsGC49hw-SqR(I-7#*kRQ;z7N zahcXv9ME74<*4mCm5vxx;^I?s2$7}P6DEFQDab)fk_Ako#l=wf+H?|An&c1HG~7SJ zr#r&fK$@TD|7H)7SbL*;bkBVnXX3AH4SswX=O|v+wihR(26_u^vyByD>h$+kSc9XP z7(a4-sThDi&!DorTq?Gowzz^NCP|~sL(_M-h$sFhYS1t_ucGK9)9q>xb+RCS<`I9T zs3*pwC#S5>5z3}k(?&1s#d8XbZi=rUA+Q~=MtjyjAV4ML`VPhTcer3*G4t$gcFxPK zoF9FC{?-Jm0v-F4%m-|24Hn~+lt$-DpQEv_5mvS~*(-EqrLk(-r&x6R(lm8gM#Tqs z@;Qny>iI}go> z^cm7;UW$}BvT&5Rw$SDAS-M+PFoo-<>%ks83Z`%y_dx^pT5%uXCg3*c10l~2Q+S8_ zilK%njN`tXz<(XL1brbxBiUdI=c1v{msB7<9rqd8Ne@#f!hK?&vx3cQ=o1fPkpT#{ z;=V`x)Bs2y?mH`)3BZg&-}v&Mu8IPnMO+|}?a2c=aHT~Ilp?Wv)|mySa0Lnwgua&w zdC4(2NVoCRq+01(4lPh*17)MF8tFy-mpfl2|;fgSEiPDJnu^2hy# zzb}Clube4@4u7FX{xdvsdC0RHI?RWj7|}2)c9G9K>+l7qI68-az871A4}H@+dNyOf ziQF1|Mw1?Q2~o`VbC59+c<>&U(YfJQRNR9MBf%3@WO=nomGfgBbWjVZ4R-|~Pj!7s zdkGmmz+fW@(E{O`jb2uYtMG%~j&}z!fiD)QaPXl$FOjy)j>by&=yC9gG1lR1rI~d6 zuiKzQT39*%BBvee;j1&z;m{!{%xd=jt=b&Z$CG?EtRR?Sy5FE>ewdL->Zch5|E%l~ z-@^*ajQ39hW}$<^s*KBJLLUR3G0b(`0Y(P$K6vtpzs?;AdSTM=9XOWjB;o6npY*(D zsx(pQ4B&HS32ZZa$Q$+;e9a|bfrXwniF}uHH1zKgD{y~^uZW`@`& zS{UzOOL1CQv}1yFGf}WPGt9*GpeDR@CiAjw@KXwAwES(ggbam#Pm)JV{eJCMw5VRD zGNSVa6y%~oDKv+wn7%xla98P_;U$uD>vwS}M{zHHZK0zQ-*e`96HQF?2bypDM}n_j zT(12p|BKOSl#xk-TlWPA+sgcf8?risVmc@V*Ih~lpO$CuQE3hxle48c|7`p<&Gq0t z_Qsa!lbS({cTapF>QOa^DY)LlbIa^wzjOAyirQD{3^nEH`k|pw>USdZM#u8ipBjUevX=-}bqEOU>_@SvWX~ z)Mofz!`s+25~Pdv`jPPQi8Wh=Um#0x>3I9$LyP+un-xT}GM;$$im!(L zWK$5C;cW7#?e$C2sy>CR-_qN73>lZu*KMv3-Fg;B<)BjUSzvLhG zMT$T3*&C$$Q_ed6WZB;^z+1zgWUQXA)IT4_LiMbY-rL33w}?3@`HgQ@HeFS2e?nt> z`z7m&`|8wj8Z61tU+zD69C6(YO+)du<2E{w4?A+J+?KF{_A)xZm6@*7J9x0;gRDB| z@s+nPYqOZ`h!Qk+7d5!fUaE~6tW-gVHc!2GKf13;JVb=2cULMcCP<_^&h7Nc74yXj z-S5AcB#LY7>G36sESS?JFVMb2)iD++m=T0slCoCD`*DSqR@{=|{LG=u;HM`H@$bTq zGqKBP_x@<*cCU6Ze2o4OfZ`$LqLPA*AO6zFFJZ)(N;C2NR)=!v&Hkaly>+(+9x&du z6d(|oh$RtmsXN+2N{1TScein`i{NwV#!2Ca@ys-=joJH^qWwdO_ZL^WdT}xi-6JES zaZGuCq|t#88)?RoDE+Xa@l zx1_{wqz=U_eO4`_o9;z7UyPDHU&A`~E0e*p@4Fsd(68avIl)i6PBpUyyuS6Bxy=kE z&m?m@^_(2FfNhHhTG5y0C5AL@NtB{%uk3Ow?fLVJc%#qZvNdR}h*nv7=Cs9xtWpez zDqRbz{rYYqbokFbr0*PVcJ?kMHyjc@Ov|+Zi>G~j;klly^gWXYSDEgnJqq|{a_W)3 z9XIv8*-TV+{X~a@M_S29$T#}k-pw=`(}iRa8ufjH10(6*DVG>28NPmg<-XALEv~eF zmQM~v&Gy-U}Sl-f5jLa^f)$@|@tUO#eoNibaS$*GK+B)jBMl;B4xqQKVotU+&{ zj>y8Um9fCFW`v6RWB^YEOY>aF!3>%wU$f{tFB^FdW}lj|(0;}^n;*xz8`E*xb@=&@ zG%;4u1vP&v(Hn6iM3(S*L@@U$DQ4RL)7yDRHL-OIJQNWr3L&)6QTzZwl8{DIy7UgA zgcbn*gkyO+o?`=vUh0atR&FV7MiEt zutgN!+tlYDTzLCPxy!*<+SFnd!TQDSde<#o`;8oLZ*8E+;uGIC@qbin-pYAu)#*?m z?^k-D+{3gzrYq13KT3Q-q_;b;c($olXEOQDcT;Z26b%yJRc^iIIqc{0T+DTbu%tHp z;D_&)!}STTw>k`15WnIq4)xc)8GqG%B-pSeN``7xv03`^;MgB&#C9fYHDQfs@;4d5 z)1?G__@G!~gYx7iyIOrc#~PQzxvooWMP*vQDp(8nLFY9dJ`FSUokyr?uiND1@uBs= z4gA^6W-;a2K^sQ&>!ckU^I0Pbz785M64w`Yxed({k8bl=PTL^IT41;*rRn*Fmw~^% z&MHh%YIXj0L(2J~(azMdV5L13>Mlk(vm@_^f^q*iX*xcnD5vowV74(XRM0gMx*-0= zGsTfCQh}4*=}i-FPv5X(Jy0XLbN><;+Oh3I zpZnomCqDkTvAm408dlktF(_N!yJL&qqN{dxZ)i{ZSt12c#?h^x2WwN zZfI6rTSdH4GGu3r#$AtM&GAt0&oWMBm7lw;4~TL1y%}X3q{#1D_JNWr=jm|%cGHlZ zR{_yFsQ<#!7Ro#N#ge1(o2z8-lv~LpR>8qya&)bI=1Ic#{I2q!2K9G`rY)`g{RWiP z-TnL3a`)^xx;sYiE6qS=n6unfC#a$#=<219X*LW({g14M_o+vdKV+OFc@gZC*Jal@ z#JSZ)ZUi&0dyh`rJ{lavcck3srMyAN}TxBKG^^1MB9Bh5d z56{dGJg=c1N#EASE_=&=V=h_s?cqKAba&^=lZQvDwWV&KICZmBak@?aNy9cR+On!O zp|5A-)YOj$mM)p03gt{4>wN!$gXadPHn&<7UVO~h6*s#5hr)$6X?f*agXF#v+j&EI z9jafebz6P_s`fVUM5;m|rz+nm_q0dr=#m4I>I+pj z?ozHg(#rE{!DooG?v=`Z5gniq0~STI`nUXUAU-BerI`Gx7k99wfY^7CU(t=Z8RiPy^}ZZ;3~ zJzsyst)*q)jEq@}!D@Mpl5K{8oc?~L5rR`cW8g{D(w}E0Om9iQ&?Ht4Yu|Lh)$HOw zv~7qf_mQDAESn9)%@oT>`Sn;Ii6yKk{c=8awZ(oyRebh60@&c9Nd?OAToC%-_o}q>jr;U(6*WFY?##Sg@glDKw_!F7^FZtZ=cZ2WA%t%A;Y3c0{n>7ZkwQh)xjuFz(&g6ZdR>I;%Q zlkMG;9_8pqCWqV*mMg!mBdPSN>^6~RAxY2gR(x+ZF8w^npfJ2psxU01bbnt{eRFG%-TdKKn>rPIR6EQ)pVSBMD!-h3 z+l*G(Ci|v*sBeCI@sW1POY1Kk*HU=(Uj2&Wpbl|QCC|`Uy;;T8*H|T~l{KLQx-!1- za*}P-$2DEANlZvCSo351NS(YRZMDSDnbK9xFT%g)X^E=1X;rTcu5XvBT9)_Q`z?bq zje68K)cLP^dFgfW`lc%K5>zW~;por7{eZFN|z=hWwidln+rj?q z=TwSZ2$$%r)i^tN;OPcsg-bJvr)CCr#g<)oy3uR0zLqzmMy_uW9&Bgn)!zGJ(>VVE ze&#BxC~!~f$2Fy&wv=5@QapFYa0yvzM@myO`TJOolUn-bx>LIkgs$Bu5t_^2;D+}( zV-vY{b9?@k3ZHnZ2zC246$kO+vR~Bz2 z5h9hc(|m8=l`nE5oEYjbSI*mAb7yqTDi!>3m#W@RSBw@$%wD1#my-2+rok8Co$kD2 zc7b2;%w4|a{)l_A+dloP5=T8=+}}eSdp&U+?|I-!XH?=QN$!4`rRs?xEFI4~ab4mW zZZo}vV_b21Ytp`wl8N65gVQ3!%9U5F47ls~-VbM7TJx=CQmg1*87pEv-juIs@K^UH zxEWtwDqmR?Ik?=d=lxK$vbLolWoPQ%>75Nu}YREe{uEkwj`@DgWSJAZWu^e&d z(*s?Zx(e3=>Ws?MdzBhj-@P-j?L?4SO3@EP9}$hn>cvHVyWN#_mQv2u5jb-BX>S+5 zE!h)#Qo7EcpbkC~tSZ8CRL!44*xp;>J-pC&C|4G(D zU)VRTRTKl~yT5d4s%hN3#yG7&Zcw!*@{Uvrch>eNso|iJi!}cHxp^`-SFH3iv*_7m zR$r~K+;BdjU}5jF^)A^N64$7z7P-Xr%UKm3Ozvha2TA!_{UB z6f}gZly#{+@6RW-GQ-xFt9XY`E!v@J;mhDAEY77fWDlPG>U|-mLcb(`m4S+Ai9G(` zXWj8aBf-*;fUjD1!$irl9a#^KTP%3=WV7>9g8yIVG()l;n@w( zxG9yBb{m!zy|5)d7{8F&yi#oHlr!_kamjd@-^a!kw)U<%y*ETGd-v@U!nxtY?Cfsk zA=fYKlT*BJaHwOZ6|icZ<13UC{>%d;_-4|frZvF zu?!^*rLiRmU-v2pxK!w|d$y>{jpMiPA5`C?ruU6_b+Z?gB_PTq==iSqJ=^u zOobpll~bHXEuPnF*+XH~uiibHl8NkG(7Z4@*zaCu8e7kuDQ}@);d8g^(=zex8&7`Z ztR+V6>`yEDmZ`#>G4*&fy;%mgM%rsHoyD0s=NRcWeTHE9vO3CD^xgM7L-ur|`G|5) z1u^WH_Wj?I>H<~_m5^k<8);>-mn}$GFEX(qU?-HWWnE`SHxUU)zBy z0_-_+TpyS}AIwLs{IJfE;4x!v23BOOi7E|#J$Vee`*A@pK=@dEdN}MH@@vJpw`o|v zK(GtA3~l`D-CqyDMujKv!E@dME{Q}UlPDx(5|ufGZ{=K)5HXPL=*7qO~9^Az-A_37S3w_2_PzODNn2f zrxCoCSi*!0K-sbi<lT)Qmld`K88}%6mVu8= z1Q}DYO(0_y_94iafR(brLG@~7gM-pF-3AAB>tP!l6s<39<_xPXQmf9kNU5&2MJl!3 z7Ae$G+l^42YTD7DGWD?&hN`s84k^+uJETTG*^NRas%MW>XsEp+RG^#fk^1bhN6K@? z9;r^_Rj;7pj9G=$=I&KUY2IChROWn#eNbUqIE+Ab$#V#Y%5uL$7F3mgIJh81>4?;% zy(3bRxsGh8B9A-rp@JN8oKufZJE3y)bV91J#0e?J4kx4*C!B6WrMSe|3aZ3F=R;5- zRy)fx?#aYw3djk^L=bf?^rnj3T90hOGV z2U2mq9-&adt@l9ct;+)`x6d9qP_^lMCPKv)=Gg<)+7{1jsMM}|B9(^oLJE!Qh16NB z7gA>RUTRR4-Sb)k6`8nqD^z1EyrrNL<9oY96?VWo0xGa)-s(tw`5@)B$_J^gJfA$M zxK8*WwKeQBr?h< zsPBGAL6KOip?ZpB4M63zgH-`lQy*&_Dkf3?c&L_4{E<>h_D3pdk3UjKkNjy+9W4yF z2$hjd08&Mn0Z0)Y2|#LSFknsz1tJyX8c2Z(s4x(zpVNW6q4Iefh*Xbu5K=sTK^0K# zR0JWVb1?|1oG(GHP~j|Nlc2f@XXip?vy~kLRn710EU0L3!7Qj|sKJR)$;1U;hAO5Z zSO68w{a~bC7K9+>Vi|%|OIpZHs8|k#6hO7|B4kdfga$#C;txK$sY4|6oKG@+%d29?vs^e<76jU5P!jak_ zbCA;Da_&QwQOiLJqmP5s#k`1Ks4Pq)kg7}KR19uhq!x;}Z=g~*!$m6LJ=X#%1nnrK4p>o08El9`s^C%- z6Doo)QEQ8X-ZB;;d|K=zMEB-cgzN*cbEqDN5Zy7357E3JP8=fn$v6p!;&0;S5Imj)(c3#7 zA$Ms!LhbYM2(drLBeecU0z&HG1ccI?60SmozLtQ{c{*V+L}p4NLglDLgvfP?DG-hC zBuYUf7E7vvC~TI55I8jnq3^yVguG9a3=rxjBgC~!MrfOzYypw>Sn^GXvagd15W=P) zboEHFfyi2%;sa6jY|5VyQO8pdnl4WDgGlP1icqvN^%X?W?$kdZdVWpyhRA8iyAM&5 z!z+V`xt(_yqU9}~Bt%MKK0--4e+@*)1U^E?Mm|Euhy1k=6(!RM5D~4?5E=^75E33v zLn!z%%^V@1KoX*#lOP%*-)aFuy*9xlM7&V}LOad$3W#(*=|&Lc%F;U_!d*z0hv@b( zJrE+BZU#cN&kYUgf`jhY1RR#QNyVC+UX9JD~^r~QZdNt(%=IBvv$T7=yogKhuAM(95JSHDZm zFw?NVrwve(_27S>mG}27P~!-0?C1Jr{_Ebf!+wni!NnTWa(}%8cjp`~5B<~xHJDhb zAgBwB{%Zwr?+)rqvF1YHM+(-=8&npi@sbn%sgt`dTL5m&NrqS=+v|ZQ?Vv#M-%b`f z@^~;@1FV4rqd|nvosC}~29ks+D1imzO@Qvapr|nm>m&%*ocoBN#4%Nn45~6gr&v%O z`OkZUO-Q(!y3wG;J198A+R`sG!m7{oz}Qw?Y!C-nCJVak^O8XmSuR#C3Hpm_q7wq@ zNJ0NZ@U2E9CxXtSf4%$F;GZu@`s;IX!O?;9^!LU*F4mO^tIdQ>dBG7xa6vP3Sh@># z%GCsWO%Nm|X~Gg$T?4RM64zAIU~aalrk*0G>J;X>=b|~=c(hLg&%W}aw*aHi{SS|@%*xGDuHk)n4=CGsK zNo*cF?e8Iv!Z{J3t4mO@K~A2jgn@*qreN;HNlOw$@qWGgJL7QLIIEVMfNF%;IIF%} zU}7;C;N*XO!IHq1+t@asHn6W3OdOr}FO`u0=UbeRu!!h9vH9W)=2n%El#*V!=)Z6G ze=Yy-TmHW+;r|+m#MBgCESfoaf6YQ;jGz`<*p~^Oz<@)(U>Pg##)b>P=3^I*^jyZ~ zV-SyBRM>KCdL9UDxxcgwE(}Bph&3RpK{SGB1#t?*RSwaz`n51nhT6Zv1x1`7T7cvR&yEqyx3>K0$V^*j@*dGRVyx1QXHjSMREU@1p0s>qAHE6s27Q`12-$BfP z5OV=TCxFlZ0R~UT>4RW`um#}(f(0TFgjMkaaNUYx*9OjR0jM>P6hwg1Z6PTziM7z= zMjCRmgy!);-Fbu#c{#&mIms{y8(9MsW9xFb5nK+(m`u|I%7SW0q8gGJdN_kcY2iFj kQyv<{MPt$kFI$u-fNhZtqZ5)j;9sL<;@CxD$jwaqABqOEZU6uP diff --git a/configs/swarm/genesis.json b/configs/swarm/genesis.json index e7126967f9e..91e1e0780eb 100644 --- a/configs/swarm/genesis.json +++ b/configs/swarm/genesis.json @@ -17,10 +17,7 @@ { "Register": { "Account": { - "id": "alice@wonderland", - "signatories": [ - "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" - ], + "id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland", "metadata": { "key": { "String": "value" @@ -32,10 +29,7 @@ { "Register": { "Account": { - "id": "bob@wonderland", - "signatories": [ - "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" - ], + "id": "ed012004FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016@wonderland", "metadata": { "key": { "String": "value" @@ -67,10 +61,7 @@ { "Register": { "Account": { - "id": "carpenter@garden_of_live_flowers", - "signatories": [ - "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" - ], + "id": "ed0120E9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99@garden_of_live_flowers", "metadata": {} } } @@ -90,7 +81,7 @@ "Mint": { "Asset": { "object": "13", - "destination_id": "rose##alice@wonderland" + "destination_id": "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } }, @@ -98,16 +89,25 @@ "Mint": { "Asset": { "object": "44", - "destination_id": "cabbage#garden_of_live_flowers#alice@wonderland" + "destination_id": "cabbage#garden_of_live_flowers#ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + } + } + }, + { + "Transfer": { + "AssetDefinition": { + "source_id": "ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4@genesis", + "object": "rose#wonderland", + "destination_id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } }, { "Transfer": { "Domain": { - "source_id": "genesis@genesis", + "source_id": "ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4@genesis", "object": "wonderland", - "destination_id": "alice@wonderland" + "destination_id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } }, @@ -118,7 +118,7 @@ "definition_id": "CanSetParameters", "payload": null }, - "destination_id": "alice@wonderland" + "destination_id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } }, @@ -172,13 +172,13 @@ { "definition_id": "CanRemoveKeyValueInAccount", "payload": { - "account_id": "alice@wonderland" + "account_id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } }, { "definition_id": "CanSetKeyValueInAccount", "payload": { - "account_id": "alice@wonderland" + "account_id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } ] diff --git a/core/Cargo.toml b/core/Cargo.toml index c351be1cb0c..46b8ff1cc1d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -68,6 +68,8 @@ uuid = { version = "1.8.0", features = ["v4"] } indexmap = "2.2.6" [dev-dependencies] +test_samples = { workspace = true } + criterion = { workspace = true } hex = { workspace = true } once_cell = { workspace = true } diff --git a/core/benches/blocks/apply_blocks.rs b/core/benches/blocks/apply_blocks.rs index bdf75e9e215..28daef9e720 100644 --- a/core/benches/blocks/apply_blocks.rs +++ b/core/benches/blocks/apply_blocks.rs @@ -1,6 +1,6 @@ use eyre::Result; use iroha_core::{block::CommittedBlock, prelude::*, state::State}; -use iroha_data_model::prelude::*; +use test_samples::gen_account_in; #[path = "./common.rs"] mod common; @@ -19,24 +19,23 @@ impl StateApplyBlocks { /// - Failed to parse [`AccountId`] /// - Failed to generate [`KeyPair`] /// - Failed to create instructions for block - pub fn setup(rt: &tokio::runtime::Handle) -> 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..7a7dc967399 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -800,7 +800,8 @@ mod tests { use iroha_crypto::SignatureVerificationFail; use iroha_data_model::prelude::*; - use iroha_genesis::{GENESIS_ACCOUNT_ID, GENESIS_DOMAIN_ID}; + use iroha_genesis::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 @@ -1036,13 +1031,19 @@ mod tests { // Predefined world state 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(), + let genesis_correct_account_id = AccountId::new( + GENESIS_DOMAIN_ID.clone(), + genesis_correct_key.public_key().clone(), + ); + let genesis_wrong_account_id = AccountId::new( + GENESIS_DOMAIN_ID.clone(), genesis_wrong_key.public_key().clone(), - ) - .build(&GENESIS_ACCOUNT_ID); - assert!(genesis_domain.add_account(genesis_account).is_none(),); + ); + let mut genesis_domain = + Domain::new(GENESIS_DOMAIN_ID.clone()).build(&genesis_correct_account_id); + 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 +1058,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..7029f57ff7f 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,25 @@ pub mod tests { } } - fn accepted_tx( - account_id: &str, - key: &KeyPair, + fn accepted_tx_by_someone(time_source: &TimeSource) -> AcceptedTransaction { + let (account_id, key_pair) = gen_account_in("wonderland"); + accepted_tx_by(account_id, &key_pair, time_source) + } + + fn accepted_tx_by( + account_id: AccountId, + key_pair: &KeyPair, time_source: &TimeSource, ) -> AcceptedTransaction { 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 +436,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 +455,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 +465,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_by_someone(&time_source), &state_view) .expect("Failed to push tx into queue"); } @@ -500,14 +473,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 +491,13 @@ pub mod tests { for _ in 0..capacity.get() { queue - .push( - accepted_tx("alice@wonderland", &key_pair, &time_source), - &state_view, - ) + .push(accepted_tx_by_someone(&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_by_someone(&time_source), &state_view), Err(Failure { err: Error::Full, .. @@ -543,84 +505,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 +524,7 @@ pub mod tests { ); for _ in 0..5 { queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx_by_someone(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(10)); } @@ -648,18 +535,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_by_someone(&time_source); let mut state_block = state.block(); state_block.transactions.insert(tx.as_ref().hash(), 1); state_block.commit(); @@ -678,18 +558,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_by_someone(&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 +580,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 +596,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_by_someone(&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_by_someone(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(101)); assert_eq!( @@ -751,10 +613,7 @@ pub mod tests { ); queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx_by_someone(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(210)); assert_eq!( @@ -770,24 +629,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_by_someone(&time_source), &state_view) .expect("Failed to push tx into queue"); let a = queue @@ -811,14 +662,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 +675,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 +722,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 +747,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_by_someone(&time_source); match queue_arc_clone.push(tx, &state.view()) { Ok(()) | Err(Failure { @@ -957,15 +796,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 +810,12 @@ pub mod tests { &time_source, ); - let tx = accepted_tx(alice_id, &alice_key, &time_source); + let tx = accepted_tx_by_someone(&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_by_someone(&time_source); time_handle.rewind(future_threshold * 2); assert!(matches!( @@ -997,22 +830,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 (alice_id, alice_keypair) = gen_account_in("wonderland"); + let (bob_id, bob_keypair) = gen_account_in("wonderland"); 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 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()) @@ -1035,14 +860,14 @@ pub mod tests { // First push by Alice should be fine queue .push( - accepted_tx("alice@wonderland", &alice_key_pair, &time_source), + accepted_tx_by(alice_id.clone(), &alice_keypair, &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), + accepted_tx_by(alice_id.clone(), &alice_keypair, &time_source), &state.view(), ); assert!( @@ -1059,7 +884,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), + accepted_tx_by(bob_id.clone(), &bob_keypair, &time_source), &state.view(), ) .expect("Failed to push tx into queue"); @@ -1081,14 +906,14 @@ pub mod tests { // After cleanup Alice and Bob pushes should work fine queue .push( - accepted_tx("alice@wonderland", &alice_key_pair, &time_source), + accepted_tx_by(alice_id, &alice_keypair, &time_source), &state.view(), ) .expect("Failed to push tx into queue"); queue .push( - accepted_tx("bob@wonderland", &bob_key_pair, &time_source), + accepted_tx_by(bob_id, &bob_keypair, &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..b3c2ac376c3 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 { + if *account_id.domain_id() == *iroha_genesis::GENESIS_DOMAIN_ID { return Err(InstructionExecutionError::InvariantViolation( - "Not allowed to register `genesis@genesis` account".to_owned(), + "Not allowed to register account in genesis domain".to_owned(), )); } diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 4bc587aabee..39cecd6988f 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,10 @@ 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, ALICE_ID, SAMPLE_GENESIS_ACCOUNT_ID, SAMPLE_GENESIS_ACCOUNT_KEYPAIR, + }; use tokio::test; use super::*; @@ -272,18 +258,15 @@ 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 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)) - .execute(&genesis_account_id, &mut state_transaction)?; + .execute(&SAMPLE_GENESIS_ACCOUNT_ID, &mut state_transaction)?; + Register::account(Account::new(ALICE_ID.clone())) + .execute(&SAMPLE_GENESIS_ACCOUNT_ID, &mut state_transaction)?; Register::asset_definition(AssetDefinition::store(asset_definition_id)) - .execute(&genesis_account_id, &mut state_transaction)?; + .execute(&SAMPLE_GENESIS_ACCOUNT_ID, &mut state_transaction)?; state_transaction.apply(); state_block.commit(); Ok(state) @@ -293,9 +276,9 @@ mod tests { async fn asset_store() -> Result<()> { let kura = Kura::blank_kura_for_testing(); 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 mut state_block = state.block(); + let mut state_transaction = state_block.transaction(); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); SetKeyValue::asset( @@ -326,7 +309,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 = ALICE_ID.clone(); SetKeyValue::account( account_id.clone(), Name::from_str("Bytes")?, @@ -359,7 +342,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 = ALICE_ID.clone(); SetKeyValue::asset_definition( definition_id.clone(), Name::from_str("Bytes")?, @@ -390,7 +373,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 = ALICE_ID.clone(); SetKeyValue::domain( domain_id.clone(), Name::from_str("Bytes")?, @@ -420,7 +403,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 = ALICE_ID.clone(); let trigger_id = TriggerId::from_str("test_trigger_id")?; assert!(matches!( @@ -439,13 +422,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 = ALICE_ID.clone(); + 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 @@ -481,20 +463,16 @@ mod tests { async fn not_allowed_to_register_genesis_domain_or_account() -> Result<()> { let kura = Kura::blank_kura_for_testing(); 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 mut state_block = state.block(); + let mut state_transaction = state_block.transaction(); + let account_id = ALICE_ID.clone(); 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(SAMPLE_GENESIS_ACCOUNT_ID.clone())); assert!(matches!( register_account .execute(&account_id, &mut state_transaction) @@ -512,10 +490,9 @@ mod tests { let state_block = state.block(); let instructions: [InstructionBox; 0] = []; - let genesis_keys = KeyPair::random(); - let tx = TransactionBuilder::new(chain_id.clone(), GENESIS_ACCOUNT_ID.clone()) + let tx = TransactionBuilder::new(chain_id.clone(), SAMPLE_GENESIS_ACCOUNT_ID.clone()) .with_instructions(instructions) - .sign(&genesis_keys); + .sign(&SAMPLE_GENESIS_ACCOUNT_KEYPAIR); let tx_limits = &state_block.transaction_executor().transaction_limits; assert!(matches!( AcceptedTransaction::accept(tx, &chain_id, tx_limits), 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..ac1e5986ccb 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)) @@ -95,7 +94,7 @@ impl AcceptedTransaction { })); } - if *iroha_genesis::GENESIS_ACCOUNT_ID == *tx.authority() { + if *iroha_genesis::GENESIS_DOMAIN_ID == *tx.authority().domain_id() { return Err(AcceptTransactionFail::UnexpectedGenesisAccountSignature); } 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 483332a6a0d..e7de53c116c 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}; @@ -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, SAMPLE_GENESIS_ACCOUNT_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 => &SAMPLE_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() { @@ -781,7 +786,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")); @@ -811,7 +817,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..9f83150cee5 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: r#"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/name.rs b/data_model/src/name.rs index e9b5f86221e..518ae2a230c 100644 --- a/data_model/src/name.rs +++ b/data_model/src/name.rs @@ -71,7 +71,7 @@ impl Name { if candidate.is_empty() { return Err(ParseError { - reason: "`Name` cannot be empty", + reason: "Empty `Name`", }); } if candidate.chars().any(char::is_whitespace) { 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/trigger.rs b/data_model/src/trigger.rs index b7af77ef05e..b32b9573bb7 100644 --- a/data_model/src/trigger.rs +++ b/data_model/src/trigger.rs @@ -106,7 +106,7 @@ impl FromStr for TriggerId { let mut split = s.split('$'); match (split.next(), split.next(), split.next()) { (Some(""), _, _) => Err(ParseError { - reason: "Trigger ID cannot be empty", + reason: "Empty `name` part in `name` or `name$domain_id`", }), (Some(name), None, _) => Ok(Self { name: Name::from_str(name)?, 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..0bbdae09468 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -20,12 +20,8 @@ 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()); /// Genesis transaction #[derive(Debug, Clone)] @@ -168,7 +164,11 @@ impl GenesisTransactionBuilder { /// Convert [`GenesisTransactionBuilder`] into [`SignedTransaction`] with signature. #[must_use] fn sign(self, chain_id: ChainId, genesis_key_pair: &KeyPair) -> SignedTransaction { - TransactionBuilder::new(chain_id, GENESIS_ACCOUNT_ID.clone()) + let genesis_account_id = AccountId::new( + GENESIS_DOMAIN_ID.clone(), + genesis_key_pair.public_key().clone(), + ); + TransactionBuilder::new(chain_id, genesis_account_id) .with_instructions(self.isi) .sign(genesis_key_pair) } @@ -310,31 +310,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 +336,8 @@ impl RawGenesisDomainBuilder { #[cfg(test)] mod tests { + use test_samples::{ALICE_KEYPAIR, BOB_KEYPAIR}; + use super::*; fn dummy_executor() -> Executor { @@ -367,7 +353,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 +365,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 +401,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 +424,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 +439,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/hooks/pre-commit.sample b/hooks/pre-commit.sample index 72addb9c6b5..d77824f2d1f 100755 --- a/hooks/pre-commit.sample +++ b/hooks/pre-commit.sample @@ -1,7 +1,25 @@ #!/bin/sh +# rustup default nightly-2024-04-18 set -e -cargo +nightly fmt --all -- --check -cargo +nightly lints clippy --workspace --benches --tests --examples --all-features -cargo run --bin kagami -- genesis >configs/peer/genesis.json -cargo run --bin kagami -- schema >docs/source/references/schema.json -git add configs/peer/genesis.json docs/source/references/schema.json +# format checks +cargo fmt --all -- --check +cd ./default_executor +cargo fmt --all -- --check +cd - +cd ./client/tests/integration/smartcontracts +cargo fmt --all -- --check +cd - +# update the default executor +cargo run --release --bin iroha_wasm_builder_cli -- build ./default_executor --optimize --outfile ./configs/swarm/executor.wasm +# update the default genesis, assuming the transaction authority is `test_samples::SAMPLE_GENESIS_ACCOUNT_ID` +GENESIS_PUBLIC_KEY=ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 cargo run --release --bin kagami -- genesis --executor-path-in-genesis ./executor.wasm > ./configs/swarm/genesis.json +# update schema +cargo run --release --bin kagami -- schema > ./docs/source/references/schema.json +# update docker compose files +cargo run --release --bin iroha_swarm -- -p 1 -s Iroha --force --config-dir ./configs/swarm --health-check --build . --outfile ./configs/swarm/docker-compose.single.yml +cargo run --release --bin iroha_swarm -- -p 4 -s Iroha --force --config-dir ./configs/swarm --health-check --build . --outfile ./configs/swarm/docker-compose.local.yml +cargo run --release --bin iroha_swarm -- -p 4 -s Iroha --force --config-dir ./configs/swarm --health-check --image hyperledger/iroha2:dev --outfile ./configs/swarm/docker-compose.yml +# lints +cargo lints clippy --workspace --benches --tests --examples --all-features +# stage updates +git add ./configs/swarm/executor.wasm ./configs/swarm/genesis.json ./docs/source/references/schema.json ./configs/swarm/docker-compose.single.yml ./configs/swarm/docker-compose.local.yml ./configs/swarm/docker-compose.yml 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/scripts/tests/consistency.sh b/scripts/tests/consistency.sh index ba3a34531f5..c55cbbfda10 100755 --- a/scripts/tests/consistency.sh +++ b/scripts/tests/consistency.sh @@ -3,8 +3,9 @@ set -e case $1 in "genesis") - cargo run --release --bin kagami -- genesis --executor-path-in-genesis ./executor.wasm | diff - configs/swarm/genesis.json || { - echo 'Please re-generate the genesis with `cargo run --release --bin kagami -- genesis --executor-path-in-genesis ./executor.wasm > configs/swarm/genesis.json`' + GENESIS_PUBLIC_KEY=ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 cargo run --release --bin kagami -- genesis --executor-path-in-genesis ./executor.wasm | diff - configs/swarm/genesis.json || { + echo 'Please re-generate the default genesis with `GENESIS_PUBLIC_KEY=ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 cargo run --release --bin kagami -- genesis --executor-path-in-genesis ./executor.wasm > configs/swarm/genesis.json`' + echo 'The assumption here is that the authority of the default genesis transaction is `test_samples::SAMPLE_GENESIS_ACCOUNT_ID`' exit 1 };; "schema") 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..668fb4d7116 --- /dev/null +++ b/test_samples/src/lib.rs @@ -0,0 +1,56 @@ +//! Utility crate for standardized and random signatories. + +use iroha_crypto::KeyPair; +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! declare_keypair { + ( $key_pair:ident, $public_key:expr, $private_key:expr ) => { + /// A standardized [`KeyPair`](iroha_crypto::KeyPair). + pub static $key_pair: Lazy = Lazy::new(|| { + KeyPair::new( + $public_key + .parse() + .expect(r#"public_key should be valid multihash e.g. "ed0120...""#), + $private_key + .parse() + .expect(r#"private_key should be valid multihash e.g. "802640...""#), + ) + .expect("public_key and private_key should be valid as a pair") + }); + }; +} + +macro_rules! declare_account_with_keypair { + ( $account_id:ident, $domain:literal, $key_pair:ident, $public_key:literal, $private_key:literal ) => { + /// A standardized [`AccountId`](iroha_data_model::account::AccountId). + pub static $account_id: Lazy = Lazy::new(|| { + format!("{}@{}", $key_pair.public_key(), $domain) + .parse() + .expect("domain and public_key should be valid as name and multihash, respectively") + }); + + declare_keypair!($key_pair, $public_key, $private_key); + }; +} + +declare_keypair!(PEER_KEYPAIR, "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0", "8026409AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"); + +declare_account_with_keypair!(ALICE_ID, "wonderland", ALICE_KEYPAIR, "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03", "802640CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03"); +declare_account_with_keypair!(BOB_ID, "wonderland", BOB_KEYPAIR, "ed012004FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016", "802640AF3F96DEEF44348FEB516C057558972CEC4C75C4DB9C5B3AAC843668854BF82804FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016"); +declare_account_with_keypair!(CARPENTER_ID, "garden_of_live_flowers", CARPENTER_KEYPAIR, "ed0120E9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99", "802640B5DD003D106B273F3628A29E6087C31CE12C9F32223BE26DD1ADB85CEBB48E1DE9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99"); +declare_account_with_keypair!(SAMPLE_GENESIS_ACCOUNT_ID, "genesis", SAMPLE_GENESIS_ACCOUNT_KEYPAIR, "ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4", "80264082B3BDE54AEBECA4146257DA0DE8D59D8E46D5FE34887DCD8072866792FCB3AD4164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4"); 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..db0db26b668 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_DOMAIN_ID, +}; use serde_json::json; +use test_samples::{gen_account_in, ALICE_ID, BOB_ID, CARPENTER_ID}; use super::*; @@ -76,24 +79,28 @@ impl RunArgs for Args { pub fn generate_default( builder: RawGenesisBlockBuilder, ) -> color_eyre::Result { + let genesis_account_id: AccountId = { + let multihash = + std::env::var("GENESIS_PUBLIC_KEY").wrap_err("GENESIS_PUBLIC_KEY should be set")?; + let domain = &*GENESIS_DOMAIN_ID; + format!("{multihash}@{domain}") + .parse() + .wrap_err("GENESIS_PUBLIC_KEY should be valid multihash")? + }; let mut meta = Metadata::new(); meta.insert_with_limits("key".parse()?, "value".to_owned(), Limits::new(1024, 1024))?; 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 +108,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 +187,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 +219,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/README.md b/tools/parity_scale_cli/README.md index 824bfbac4a2..4ca6aedee2e 100644 --- a/tools/parity_scale_cli/README.md +++ b/tools/parity_scale_cli/README.md @@ -63,6 +63,7 @@ Algorithm ## `scale-to-json` and `json-to-scale` + Both commands by default read data from `stdin` and print result to `stdout`. There are flags `--input` and `--output` which can be used to read/write from files instead. @@ -85,7 +86,7 @@ These commands require `--type` argument. If data type is not known, [`scale-to- * Encode the `NewAccount` data type from the `samples/account.json`: ```bash - ./target/debug/parity_scale_cli json-to-scale --input tools/parity_scale_cli/samples/domain.bin --output result.bin --type NewAccount + ./target/debug/parity_scale_cli json-to-scale --input tools/parity_scale_cli/samples/account.json --output result.bin --type NewAccount ``` ## `scale-to-rust` 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..331bacb6b8e 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::ALICE_ID; 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(ALICE_ID.clone()).with_metadata(metadata); decode_sample("account.bin", String::from("NewAccount"), &account); } @@ -364,17 +360,16 @@ mod tests { #[test] fn decode_trigger_sample() { - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let rose_definition_id = AssetDefinitionId::new( "wonderland".parse().expect("Valid"), "rose".parse().expect("Valid"), ); - let rose_id = AssetId::new(rose_definition_id, account_id.clone()); + let rose_id = AssetId::new(rose_definition_id, ALICE_ID.clone()); let trigger_id = "mint_rose".parse().expect("Valid"); let action = Action::new( vec![Mint::asset_numeric(1u32, rose_id)], Repeats::Indefinitely, - account_id, + ALICE_ID.clone(), DomainEventFilter::new().for_events(DomainEventSet::AnyAccount), ); let trigger = Trigger::new(trigger_id, action); 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