From 1f1daa0d8d493aebfdc6ea375ba6ac3e57b25ada 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 | 12 +- 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 -> 516866 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 | 61 +-- 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/test_env.py | 5 +- 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 | 57 +++ tools/kagami/Cargo.toml | 1 + tools/kagami/src/genesis.rs | 68 +-- 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 + 147 files changed, 1534 insertions(+), 2992 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 80fd4b7afce..e0818920b8a 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]] @@ -5702,10 +5707,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.60" diff --git a/Cargo.toml b/Cargo.toml index 8554514d26c..d97c4feef61 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.63", 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..cbdeb2db807 100644 --- a/client/examples/register_1000_triggers.rs +++ b/client/examples/register_1000_triggers.rs @@ -8,9 +8,10 @@ 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 test_samples::gen_account_in; use tokio::runtime::Runtime; fn generate_genesis(num_triggers: u32) -> Result> { @@ -23,7 +24,7 @@ fn generate_genesis(num_triggers: u32) -> Result 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..480ff907c58283a475da26107e1934a4812bf950 100644 GIT binary patch literal 516866 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

    A@S)kX1D17|Nku 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 ImQLgQ34z`J#v+M*@?fc($t+bPi4tj-_O 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 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..34034559226 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,15 +955,13 @@ 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(), - "`alice@wonderland` already exist in the blockchain" + "{alice_id} already exist in the blockchain" ); let world = World::with([domain], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); @@ -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 2a81e8abb9c..cf47ea67c3c 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -108,26 +108,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( @@ -136,7 +122,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), } @@ -255,9 +240,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::*; @@ -273,18 +259,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) @@ -294,9 +277,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( @@ -327,7 +310,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); SetKeyValue::account( account_id.clone(), Name::from_str("Bytes")?, @@ -360,7 +343,7 @@ mod tests { let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); let definition_id = AssetDefinitionId::from_str("rose#wonderland")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); SetKeyValue::asset_definition( definition_id.clone(), Name::from_str("Bytes")?, @@ -391,7 +374,7 @@ mod tests { let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); let domain_id = DomainId::from_str("wonderland")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); SetKeyValue::domain( domain_id.clone(), Name::from_str("Bytes")?, @@ -421,7 +404,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let trigger_id = TriggerId::from_str("test_trigger_id")?; assert!(matches!( @@ -440,13 +423,12 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; - let fake_account_id = AccountId::from_str("fake@wonderland")?; + let account_id = 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 @@ -482,20 +464,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) @@ -513,10 +491,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 e3f78bd2699..f8adfbd34ee 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 881d60651af..57069f0eb4b 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -1770,6 +1770,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::{ @@ -1848,14 +1849,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 1001f732434..5505617e14e 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..a175705e418 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` +cargo run --release --bin kagami -- genesis --executor-path-in-genesis ./executor.wasm --genesis-public-key ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 > ./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/test_env.py b/scripts/test_env.py index 1e5b7144323..f14894617ca 100755 --- a/scripts/test_env.py +++ b/scripts/test_env.py @@ -100,7 +100,9 @@ def __init__(self, args: argparse.Namespace, nth: int): logging.info(f"Peer {self.name} generating key pair...") command = [self.out_dir / "kagami", "crypto", "-j"] - if args.peer_name_as_seed: + if nth == 0: + command.extend(["-s", "Iroha" + "genesis"]) + elif args.peer_name_as_seed: command.extend(["-s", self.name]) kagami = subprocess.run(command, capture_output=True) if kagami.returncode: @@ -109,7 +111,6 @@ def __init__(self, args: argparse.Namespace, nth: int): str_keypair = kagami.stdout # dict with `{ public_key: string, private_key: { algorithm: string, payload: string } }` self.key_pair = json.loads(str_keypair) - os.makedirs(self.peer_dir, exist_ok=True) config = { diff --git a/scripts/tests/consistency.sh b/scripts/tests/consistency.sh index ba3a34531f5..c0608a3dfe1 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`' + cargo run --release --bin kagami -- genesis --executor-path-in-genesis ./executor.wasm --genesis-public-key ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 | diff - configs/swarm/genesis.json || { + echo 'Please re-generate the default genesis with `cargo run --release --bin kagami -- genesis --executor-path-in-genesis ./executor.wasm --genesis-public-key ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 > ./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..1227e120e0e --- /dev/null +++ b/test_samples/src/lib.rs @@ -0,0 +1,57 @@ +//! 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"); +// kagami crypto --seed "Irohagenesis" +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..1902d8aecc7 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::*; @@ -22,6 +25,8 @@ pub struct Args { /// Specifies the `executor_file` that will be inserted into the genesis JSON as-is. #[clap(long, value_name = "PATH")] executor_path_in_genesis: PathBuf, + #[clap(long, value_name = "MULTI_HASH")] + genesis_public_key: PublicKey, #[clap(subcommand)] mode: Option, } @@ -55,17 +60,24 @@ impl RunArgs for Args { fn run(self, writer: &mut BufWriter) -> Outcome { let Self { executor_path_in_genesis, + genesis_public_key, mode, } = self; let builder = RawGenesisBlockBuilder::default().executor_file(executor_path_in_genesis); let genesis = match mode.unwrap_or_default() { - Mode::Default => generate_default(builder), + Mode::Default => generate_default(builder, genesis_public_key), Mode::Synthetic { domains, accounts_per_domain, assets_per_domain, - } => generate_synthetic(builder, domains, accounts_per_domain, assets_per_domain), + } => generate_synthetic( + builder, + genesis_public_key, + domains, + accounts_per_domain, + assets_per_domain, + ), }?; writeln!(writer, "{}", serde_json::to_string_pretty(&genesis)?) .wrap_err("failed to write serialized genesis to the buffer") @@ -75,25 +87,23 @@ impl RunArgs for Args { #[allow(clippy::too_many_lines)] pub fn generate_default( builder: RawGenesisBlockBuilder, + genesis_public_key: PublicKey, ) -> color_eyre::Result { + let genesis_account_id = AccountId::new(GENESIS_DOMAIN_ID.clone(), genesis_public_key); 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 +111,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_domain_ownerhip = Transfer::domain( - "genesis@genesis".parse()?, + let transfer_rose_ownership = Transfer::asset_definition( + genesis_account_id.clone(), + "rose#wonderland".parse()?, + ALICE_ID.clone(), + ); + 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 +190,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() @@ -191,12 +206,13 @@ pub fn generate_default( fn generate_synthetic( builder: RawGenesisBlockBuilder, + genesis_public_key: PublicKey, domains: u64, accounts_per_domain: u64, assets_per_domain: u64, ) -> color_eyre::Result { // Synthetic genesis is extension of default one - let mut genesis = generate_default(builder)?; + let mut genesis = generate_default(builder, genesis_public_key)?; let first_transaction = genesis .first_transaction_mut() @@ -207,12 +223,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