From f90e2eed392ed1206285ecec3f43e97204d02029 Mon Sep 17 00:00:00 2001 From: Shunkichi Sato <49983831+s8sato@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:24:19 +0900 Subject: [PATCH] feat!: authenticate personal accounts by ID BREAKING CHANGE: - change `AccountId` from "name@domain" to "multihash@domain" - make account signatory single - make transaction signature single - remove query `FindAccountsByName` closes issue #2085 Signed-off-by: Shunkichi Sato <49983831+s8sato@users.noreply.github.com> --- .rustfmt.toml | 10 +- Cargo.lock | 17 + Cargo.toml | 3 + cli/src/lib.rs | 18 +- cli/src/samples.rs | 25 +- client/Cargo.toml | 1 + client/benches/torii.rs | 40 +- client/benches/tps/utils.rs | 34 +- client/examples/million_accounts_genesis.rs | 21 +- client/examples/register_1000_triggers.rs | 9 +- client/examples/tutorial.rs | 58 ++- client/src/client.rs | 8 +- client/src/config.rs | 6 +- client/src/config/user.rs | 8 +- client/src/config/user/boilerplate.rs | 10 +- client/src/lib.rs | 7 +- client/tests/integration/add_account.rs | 45 -- client/tests/integration/asset.rs | 253 ++++------ client/tests/integration/asset_propagation.rs | 7 +- client/tests/integration/burn_public_keys.rs | 110 ----- .../integration/domain_owner_permissions.rs | 83 +--- client/tests/integration/events/data.rs | 5 +- .../tests/integration/events/notification.rs | 5 +- .../multiple_blocks_created.rs | 7 +- .../extra_functional/offline_peers.rs | 3 +- .../extra_functional/restart_peer.rs | 3 +- .../extra_functional/unregister_peer.rs | 7 +- .../extra_functional/unstable_network.rs | 3 +- client/tests/integration/mod.rs | 11 - .../integration/multisignature_account.rs | 48 -- .../integration/multisignature_transaction.rs | 101 ---- client/tests/integration/non_mintable.rs | 7 +- client/tests/integration/pagination.rs | 17 +- client/tests/integration/permissions.rs | 39 +- client/tests/integration/queries/account.rs | 15 +- client/tests/integration/queries/asset.rs | 19 +- .../tests/integration/queries/query_errors.rs | 7 +- client/tests/integration/queries/role.rs | 3 +- client/tests/integration/roles.rs | 46 +- .../src/lib.rs | 4 +- .../executor_with_admin/src/lib.rs | 7 +- .../executor_with_migration_fail/src/lib.rs | 2 +- .../query_assets_and_save_cursor/src/lib.rs | 2 +- client/tests/integration/sorting.rs | 181 ++++--- client/tests/integration/status_response.rs | 4 +- client/tests/integration/transfer_asset.rs | 9 +- .../integration/triggers/by_call_trigger.rs | 23 +- .../integration/triggers/data_trigger.rs | 22 +- .../integration/triggers/event_trigger.rs | 5 +- .../integration/triggers/time_trigger.rs | 23 +- .../integration/triggers/trigger_rollback.rs | 3 +- client/tests/integration/tx_chain_id.rs | 34 +- client/tests/integration/tx_history.rs | 3 +- client/tests/integration/tx_rollback.rs | 3 +- client/tests/integration/upgrade.rs | 32 +- client_cli/README.md | 39 +- client_cli/pytests/README.md | 27 +- client_cli/pytests/common/consts.py | 4 +- client_cli/pytests/common/helpers.py | 4 +- .../json_isi_examples/unregister_asset.json | 4 +- client_cli/pytests/models/account.py | 11 +- .../pytests/src/client_cli/client_cli.py | 31 +- .../pytests/src/client_cli/configuration.py | 30 +- client_cli/pytests/src/client_cli/iroha.py | 2 +- client_cli/pytests/test/__init__.py | 1 - client_cli/pytests/test/accounts/conftest.py | 1 - .../accounts/test_accounts_query_filters.py | 20 +- .../test/accounts/test_register_accounts.py | 97 +--- .../test/assets/test_assets_query_filters.py | 2 +- .../pytests/test/assets/test_burn_assets.py | 4 +- .../pytests/test/assets/test_mint_assets.py | 15 +- .../assets/test_register_asset_definitions.py | 6 +- .../test/assets/test_transfer_assets.py | 24 +- .../test/assets/test_unregister_asset.py | 6 +- client_cli/pytests/test/conftest.py | 29 +- .../test/domains/test_register_domains.py | 8 +- .../test/triggers/test_register_trigger.py | 10 +- client_cli/src/main.rs | 63 +-- config/iroha_test_config.toml | 3 +- configs/client.template.toml | 2 +- configs/swarm/client.toml | 6 +- configs/swarm/executor.wasm | Bin 526638 -> 516776 bytes configs/swarm/genesis.json | 38 +- core/Cargo.toml | 2 + core/benches/blocks/apply_blocks.rs | 19 +- core/benches/blocks/apply_blocks_benchmark.rs | 2 +- core/benches/blocks/apply_blocks_oneshot.rs | 2 +- core/benches/blocks/common.rs | 27 +- core/benches/blocks/validate_blocks.rs | 15 +- core/benches/kura.rs | 17 +- core/benches/validation.rs | 78 ++- core/src/block.rs | 52 +- core/src/lib.rs | 7 +- core/src/queue.rs | 299 +++--------- core/src/smartcontracts/isi/account.rs | 117 +---- core/src/smartcontracts/isi/domain.rs | 7 +- core/src/smartcontracts/isi/mod.rs | 66 +-- core/src/smartcontracts/isi/query.rs | 47 +- core/src/smartcontracts/isi/world.rs | 2 +- core/src/smartcontracts/wasm.rs | 42 +- core/src/snapshot.rs | 4 +- core/src/state.rs | 11 +- core/src/sumeragi/main_loop.rs | 115 ++--- core/src/tx.rs | 13 +- core/test_network/Cargo.toml | 2 +- core/test_network/src/lib.rs | 40 +- .../ui_fail/transparent_api_private_field.rs | 4 +- .../transparent_api_private_field.stderr | 12 +- data_model/src/account.rs | 444 +++--------------- data_model/src/asset.rs | 110 +++-- data_model/src/block.rs | 7 +- data_model/src/events/data/filters.rs | 23 +- data_model/src/isi.rs | 74 +-- data_model/src/lib.rs | 5 +- data_model/src/query/mod.rs | 17 +- data_model/src/query/predicate.rs | 97 ++-- data_model/src/transaction.rs | 38 +- data_model/src/visit.rs | 17 - data_model/tests/data_model.rs | 26 +- docs/source/references/schema.json | 204 ++------ genesis/Cargo.toml | 1 + genesis/src/lib.rs | 105 +++-- schema/gen/src/lib.rs | 11 - smart_contract/executor/derive/src/default.rs | 3 - smart_contract/executor/derive/src/lib.rs | 2 +- smart_contract/executor/src/default.rs | 88 +--- smart_contract/src/lib.rs | 36 +- test_samples/Cargo.toml | 29 ++ test_samples/src/lib.rs | 43 ++ tools/kagami/Cargo.toml | 1 + tools/kagami/src/genesis.rs | 50 +- tools/kagami/src/main.rs | 11 +- tools/parity_scale_cli/Cargo.toml | 3 + tools/parity_scale_cli/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 + 141 files changed, 1466 insertions(+), 2972 deletions(-) delete mode 100644 client/tests/integration/add_account.rs delete mode 100644 client/tests/integration/burn_public_keys.rs delete mode 100644 client/tests/integration/multisignature_account.rs delete mode 100644 client/tests/integration/multisignature_transaction.rs create mode 100644 test_samples/Cargo.toml create mode 100644 test_samples/src/lib.rs diff --git a/.rustfmt.toml b/.rustfmt.toml index 0f32415c590..f9b0b3f3643 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,5 +1,5 @@ -newline_style="Unix" -use_field_init_shorthand=true -format_code_in_doc_comments = true # unstable (https://github.com/rust-lang/rustfmt/issues/3348) -group_imports="StdExternalCrate" # unstable (https://github.com/rust-lang/rustfmt/issues/5083) -imports_granularity="Crate" # unstable (https://github.com/rust-lang/rustfmt/issues/4991) +newline_style = "Unix" +use_field_init_shorthand = true +format_code_in_doc_comments = true # unstable (https://github.com/rust-lang/rustfmt/issues/3348) +group_imports = "StdExternalCrate" # unstable (https://github.com/rust-lang/rustfmt/issues/5083) +imports_granularity = "Crate" # unstable (https://github.com/rust-lang/rustfmt/issues/4991) diff --git a/Cargo.lock b/Cargo.lock index dff2ee23c08..54c8e2a54cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2855,6 +2855,7 @@ dependencies = [ "serde_with", "tempfile", "test_network", + "test_samples", "thiserror", "tokio", "tokio-tungstenite", @@ -2967,6 +2968,7 @@ dependencies = [ "serde_json", "storage", "tempfile", + "test_samples", "thiserror", "tokio", "uuid", @@ -3176,6 +3178,7 @@ dependencies = [ "once_cell", "serde", "serde_json", + "test_samples", "tracing", ] @@ -3681,6 +3684,7 @@ dependencies = [ "parity-scale-codec", "serde", "serde_json", + "test_samples", ] [[package]] @@ -4251,6 +4255,7 @@ dependencies = [ "parity-scale-codec", "serde", "serde_json", + "test_samples", ] [[package]] @@ -5711,10 +5716,22 @@ dependencies = [ "rand", "serde_json", "tempfile", + "test_samples", "tokio", "unique_port", ] +[[package]] +name = "test_samples" +version = "2.0.0-pre-rc.21" +dependencies = [ + "iroha_crypto", + "iroha_data_model", + "once_cell", + "serde", + "toml 0.8.12", +] + [[package]] name = "thiserror" version = "1.0.59" diff --git a/Cargo.toml b/Cargo.toml index 6625d6e2f9f..085f34fea5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,8 @@ iroha_executor_derive = { version = "=2.0.0-pre-rc.21", path = "smart_contract/e iroha_trigger_derive = { version = "=2.0.0-pre-rc.21", path = "smart_contract/trigger/derive" } test_network = { version = "=2.0.0-pre-rc.21", path = "core/test_network" } +test_samples = { version = "=2.0.0-pre-rc.21", path = "test_samples" } + proc-macro2 = "1.0.81" syn = { version = "2.0.60", default-features = false } quote = "1.0.36" @@ -228,6 +230,7 @@ members = [ "smart_contract/executor", "smart_contract/executor/derive", "telemetry", + "test_samples", "tools/kagami", "tools/kura_inspector", "tools/parity_scale_cli", diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 947f0a96321..e54aac98f66 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -210,10 +210,7 @@ impl Iroha { .wrap_err("Unable to start P2P-network")?; let (events_sender, _) = broadcast::channel(10000); - let world = World::with( - [genesis_domain(config.genesis.public_key().clone())], - config.sumeragi.trusted_peers.clone(), - ); + let world = World::with([genesis_domain()], config.sumeragi.trusted_peers.clone()); let kura = Kura::new(&config.kura)?; let kura_thread_handler = Kura::start(Arc::clone(&kura)); @@ -485,19 +482,18 @@ impl Iroha { } } -fn genesis_account(public_key: PublicKey) -> Account { - Account::new(iroha_genesis::GENESIS_ACCOUNT_ID.clone(), public_key) +fn genesis_account() -> Account { + Account::new(iroha_genesis::GENESIS_ACCOUNT_ID.clone()) .build(&iroha_genesis::GENESIS_ACCOUNT_ID) } -fn genesis_domain(public_key: PublicKey) -> Domain { +fn genesis_domain() -> Domain { let mut domain = Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()) .build(&iroha_genesis::GENESIS_ACCOUNT_ID); - domain.accounts.insert( - iroha_genesis::GENESIS_ACCOUNT_ID.clone(), - genesis_account(public_key), - ); + domain + .accounts + .insert(iroha_genesis::GENESIS_ACCOUNT_ID.clone(), genesis_account()); domain } diff --git a/cli/src/samples.rs b/cli/src/samples.rs index 26fc0acd300..fe45f22e104 100644 --- a/cli/src/samples.rs +++ b/cli/src/samples.rs @@ -62,18 +62,24 @@ pub fn get_trusted_peers(public_key: Option<&PublicKey>) -> HashSet { pub fn get_user_config( peers: &UniqueVec, chain_id: Option, - key_pair: Option, + peer_key_pair: Option, + genesis_key_pair: Option, ) -> UserConfig { let chain_id = chain_id.unwrap_or_else(|| ChainId::from("0")); - let (public_key, private_key) = key_pair.unwrap_or_else(KeyPair::random).into_parts(); - iroha_logger::info!(%public_key); + let (peer_public_key, peer_private_key) = + peer_key_pair.unwrap_or_else(KeyPair::random).into_parts(); + iroha_logger::info!(%peer_public_key); + let (genesis_public_key, genesis_private_key) = genesis_key_pair + .unwrap_or_else(KeyPair::random) + .into_parts(); + iroha_logger::info!(%genesis_public_key); let mut config = UserConfig::new(); config.chain_id.set(chain_id); - config.public_key.set(public_key.clone()); - config.private_key.set(private_key.clone()); + config.public_key.set(peer_public_key); + config.private_key.set(peer_private_key); config.network.address.set(DEFAULT_P2P_ADDR); config .chain_wide @@ -89,8 +95,8 @@ pub fn get_user_config( .network .block_gossip_period .set(HumanDuration(Duration::from_millis(500))); - config.genesis.private_key.set(private_key); - config.genesis.public_key.set(public_key); + config.genesis.private_key.set(genesis_private_key); + config.genesis.public_key.set(genesis_public_key); config.genesis.file.set("./genesis.json".into()); // There is no need in persistency in tests // If required to should be set explicitly not to overlap with other existing tests @@ -109,9 +115,10 @@ pub fn get_user_config( pub fn get_config( trusted_peers: &UniqueVec, chain_id: Option, - key_pair: Option, + peer_key_pair: Option, + genesis_key_pair: Option, ) -> Config { - get_user_config(trusted_peers, chain_id, key_pair) + get_user_config(trusted_peers, chain_id, peer_key_pair, genesis_key_pair) .unwrap_partial() .expect("config should build as all required fields were provided") .parse(CliContext { diff --git a/client/Cargo.toml b/client/Cargo.toml index cb1363174e2..f9f316fa0a9 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -56,6 +56,7 @@ iroha_logger = { workspace = true } iroha_telemetry = { workspace = true } iroha_torii_const = { workspace = true } iroha_version = { workspace = true, features = ["http"] } +test_samples = { workspace = true } attohttpc = { version = "0.28.0", default-features = false } eyre = { workspace = true } diff --git a/client/benches/torii.rs b/client/benches/torii.rs index bc3f5117cfe..a8bc5c97ab0 100644 --- a/client/benches/torii.rs +++ b/client/benches/torii.rs @@ -6,13 +6,13 @@ use criterion::{criterion_group, criterion_main, Criterion, Throughput}; use iroha::samples::{construct_executor, get_config}; use iroha_client::{ client::{asset, Client}, - crypto::KeyPair, data_model::prelude::*, }; use iroha_genesis::{GenesisNetwork, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; use iroha_version::Encode; use test_network::{get_chain_id, get_key_pair, Peer as TestPeer, PeerBuilder, TestRuntime}; +use test_samples::gen_account_in; use tokio::runtime::Runtime; const MINIMUM_SUCCESS_REQUEST_RATIO: f32 = 0.9; @@ -24,17 +24,15 @@ fn query_requests(criterion: &mut Criterion) { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let rt = Runtime::test(); let genesis = GenesisNetwork::new( RawGenesisBlockBuilder::default() .domain("wonderland".parse().expect("Valid")) - .account( - "alice".parse().expect("Valid"), - get_key_pair().public_key().clone(), - ) + .account(get_key_pair(test_network::Signatory::Alice).into_parts().0) .finish_domain() .executor_blob( construct_executor("../default_executor").expect("Failed to construct executor"), @@ -60,11 +58,10 @@ fn query_requests(criterion: &mut Criterion) { }); let mut group = criterion.benchmark_group("query-requests"); let domain_id: DomainId = "domain".parse().expect("Valid"); - let create_domain = Register::domain(Domain::new(domain_id.clone())); - let account_id = AccountId::new(domain_id.clone(), "account".parse().expect("Valid")); - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)); - let asset_definition_id = AssetDefinitionId::new(domain_id, "xor".parse().expect("Valid")); + let create_domain = Register::domain(Domain::new(domain_id)); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())); + let asset_definition_id: AssetDefinitionId = "xor#domain".parse().expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); let mint_asset = Mint::asset_numeric( @@ -73,7 +70,7 @@ fn query_requests(criterion: &mut Criterion) { ); let client_config = iroha_client::samples::get_client_config( get_chain_id(), - get_key_pair(), + get_key_pair(test_network::Signatory::Alice), format!("http://{}", peer.api_address).parse().unwrap(), ); @@ -132,15 +129,13 @@ fn instruction_submits(criterion: &mut Criterion) { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let genesis = GenesisNetwork::new( RawGenesisBlockBuilder::default() .domain("wonderland".parse().expect("Valid")) - .account( - "alice".parse().expect("Valid"), - configuration.common.key_pair.public_key().clone(), - ) + .account(configuration.common.key_pair.public_key().clone()) .finish_domain() .executor_blob( construct_executor("../default_executor").expect("Failed to construct executor"), @@ -158,14 +153,13 @@ fn instruction_submits(criterion: &mut Criterion) { rt.block_on(builder.start_with_peer(&mut peer)); let mut group = criterion.benchmark_group("instruction-requests"); let domain_id: DomainId = "domain".parse().expect("Valid"); - let create_domain: InstructionBox = Register::domain(Domain::new(domain_id.clone())).into(); - let account_id = AccountId::new(domain_id.clone(), "account".parse().expect("Valid")); - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)).into(); - let asset_definition_id = AssetDefinitionId::new(domain_id, "xor".parse().expect("Valid")); + let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())).into(); + let asset_definition_id: AssetDefinitionId = "xor#domain".parse().expect("Valid"); let client_config = iroha_client::samples::get_client_config( get_chain_id(), - get_key_pair(), + get_key_pair(test_network::Signatory::Alice), format!("http://{}", peer.api_address).parse().unwrap(), ); let iroha_client = Client::new(client_config); diff --git a/client/benches/tps/utils.rs b/client/benches/tps/utils.rs index ca63d75d89b..2295a639c77 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -1,4 +1,4 @@ -use std::{fmt, fs::File, io::BufReader, path::Path, str::FromStr as _, sync::mpsc, thread, time}; +use std::{fmt, fs::File, io::BufReader, path::Path, sync::mpsc, thread, time}; use eyre::{Result, WrapErr}; use iroha_client::{ @@ -8,10 +8,12 @@ use iroha_client::{ prelude::*, }, }; +use iroha_crypto::KeyPair; use iroha_data_model::events::pipeline::{BlockEventFilter, BlockStatus}; use nonzero_ext::nonzero; use serde::Deserialize; use test_network::*; +use test_samples::ALICE_ID; pub type Tps = f64; @@ -68,6 +70,7 @@ impl Config { config: self, client, name, + signatory: KeyPair::random().into_parts().0, }; unit.ready() }) @@ -136,6 +139,7 @@ struct MeasurerUnit { pub config: Config, pub client: Client, pub name: UnitName, + pub signatory: PublicKey, } type UnitName = u32; @@ -146,15 +150,10 @@ impl MeasurerUnit { /// Submit initial transactions for measurement fn ready(self) -> Result { - let keypair = iroha_client::crypto::KeyPair::random(); - - let account_id = account_id(self.name); - let asset_id = asset_id(self.name); - - let register_me = Register::account(Account::new(account_id, keypair.public_key().clone())); + let register_me = Register::account(Account::new(self.account_id())); self.client.submit_blocking(register_me)?; - let mint_a_rose = Mint::asset_numeric(1_u32, asset_id); + let mint_a_rose = Mint::asset_numeric(1_u32, self.asset_id()); self.client.submit_blocking(mint_a_rose)?; Ok(self) @@ -193,7 +192,7 @@ impl MeasurerUnit { let submitter = self.client.clone(); let interval_us_per_tx = self.config.interval_us_per_tx; let instructions = self.instructions(); - let alice_id = AccountId::from_str("alice@wonderland").expect("Failed to parse account id"); + let alice_id = ALICE_ID.clone(); let mut nonce = nonzero!(1_u32); @@ -231,17 +230,14 @@ impl MeasurerUnit { } fn mint(&self) -> InstructionBox { - Mint::asset_numeric(1_u32, asset_id(self.name)).into() + Mint::asset_numeric(1_u32, self.asset_id()).into() } -} -fn asset_id(account_name: UnitName) -> AssetId { - AssetId::new( - "rose#wonderland".parse().expect("Valid"), - account_id(account_name), - ) -} + fn account_id(&self) -> AccountId { + AccountId::new("wonderland".parse().expect("Valid"), self.signatory.clone()) + } -fn account_id(name: UnitName) -> AccountId { - format!("{name}@wonderland").parse().expect("Valid") + fn asset_id(&self) -> AssetId { + AssetId::new("rose#wonderland".parse().expect("Valid"), self.account_id()) + } } diff --git a/client/examples/million_accounts_genesis.rs b/client/examples/million_accounts_genesis.rs index 5a6b1dc6692..a44d682fa1e 100644 --- a/client/examples/million_accounts_genesis.rs +++ b/client/examples/million_accounts_genesis.rs @@ -16,14 +16,11 @@ use tokio::runtime::Runtime; fn generate_genesis(num_domains: u32) -> RawGenesisBlock { let mut builder = RawGenesisBlockBuilder::default(); - let key_pair = get_key_pair(); + let signatory_alice = get_key_pair(test_network::Signatory::Alice).into_parts().0; for i in 0_u32..num_domains { builder = builder .domain(format!("wonderland-{i}").parse().expect("Valid")) - .account( - format!("Alice-{i}").parse().expect("Valid"), - key_pair.public_key().clone(), - ) + .account(signatory_alice.clone()) .asset( format!("xor-{i}").parse().expect("Valid"), AssetValueType::Numeric(NumericSpec::default()), @@ -45,7 +42,8 @@ fn main_genesis() { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let rt = Runtime::test(); let genesis = GenesisNetwork::new( @@ -74,16 +72,9 @@ fn create_million_accounts_directly() { wait_for_genesis_committed(&vec![test_client.clone()], 0); for i in 0_u32..1_000_000_u32 { let domain_id: DomainId = format!("wonderland-{i}").parse().expect("Valid"); - let normal_account_id = AccountId::new( - domain_id.clone(), - format!("bob-{i}").parse().expect("Valid"), - ); + let normal_account_id = AccountId::new(domain_id.clone(), KeyPair::random().into_parts().0); let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); - let create_account = Register::account(Account::new( - normal_account_id.clone(), - KeyPair::random().into_parts().0, - )) - .into(); + let create_account = Register::account(Account::new(normal_account_id.clone())).into(); if test_client .submit_all([create_domain, create_account]) .is_err() diff --git a/client/examples/register_1000_triggers.rs b/client/examples/register_1000_triggers.rs index 6de1efd77bc..56ab3c01567 100644 --- a/client/examples/register_1000_triggers.rs +++ b/client/examples/register_1000_triggers.rs @@ -8,8 +8,8 @@ use iroha_data_model::trigger::TriggerId; use iroha_genesis::{GenesisNetwork, RawGenesisBlock, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; use test_network::{ - get_chain_id, wait_for_genesis_committed_with_max_retries, Peer as TestPeer, PeerBuilder, - TestClient, TestRuntime, + get_chain_id, get_key_pair, wait_for_genesis_committed_with_max_retries, Peer as TestPeer, + PeerBuilder, TestClient, TestRuntime, }; use tokio::runtime::Runtime; @@ -55,13 +55,14 @@ fn generate_genesis(num_triggers: u32) -> Result Result<(), Box> { - let mut peer = ::new().expect("Failed to create peer"); + let mut peer: TestPeer = ::new().expect("Failed to create peer"); let chain_id = get_chain_id(); let mut configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(peer.key_pair.clone()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); // Increase executor limits for large genesis diff --git a/client/examples/tutorial.rs b/client/examples/tutorial.rs index f4b36283ac6..0af57992466 100644 --- a/client/examples/tutorial.rs +++ b/client/examples/tutorial.rs @@ -76,13 +76,16 @@ fn domain_registration_test(config: Config) -> Result<(), Error> { fn account_definition_test() -> Result<(), Error> { // #region account_definition_comparison use iroha_client::data_model::prelude::AccountId; + use iroha_crypto::KeyPair; - // Create an `iroha_client::data_model::AccountId` instance - // with a DomainId instance and a Domain ID for an account - let longhand_account_id = AccountId::new("white_rabbit".parse()?, "looking_glass".parse()?); - let account_id: AccountId = "white_rabbit@looking_glass" + // Generate a new public key for a new account + let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by providing a DomainId instance and the public key + let longhand_account_id = AccountId::new("looking_glass".parse()?, public_key.clone()); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // Check that two ways to define an account match assert_eq!(account_id, longhand_account_id); @@ -109,19 +112,17 @@ fn account_registration_test(config: Config) -> Result<(), Error> { let iroha_client = Client::new(config); // #region register_account_create - // Create an AccountId instance by providing the account and domain name - let account_id: AccountId = "white_rabbit@looking_glass" - .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); - // #endregion register_account_create - - // TODO: consider getting a key from white_rabbit // Generate a new public key for a new account let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") + .parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); + // #endregion register_account_create // #region register_account_generate // Generate a new account - let create_account = Register::account(Account::new(account_id, public_key)); + let create_account = Register::account(Account::new(account_id)); // #endregion register_account_generate // #region register_account_prepare_tx @@ -151,6 +152,7 @@ fn asset_registration_test(config: Config) -> Result<(), Error> { numeric, AccountId, AssetDefinition, AssetDefinitionId, AssetId, Mint, Register, }, }; + use iroha_crypto::KeyPair; // #endregion register_asset_crates // Create an Iroha client @@ -171,10 +173,12 @@ fn asset_registration_test(config: Config) -> Result<(), Error> { iroha_client.submit(register_time)?; // #endregion register_asset_init_submit - // Create an account using the previously defined asset - let account_id: AccountId = "white_rabbit@looking_glass" + // Generate a new public key for a new account + let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #region register_asset_mint_submit // Create a MintBox using a previous asset and account @@ -205,8 +209,8 @@ fn asset_minting_test(config: Config) -> Result<(), Error> { // #region mint_asset_define_asset_account let roses = AssetDefinitionId::from_str("rose#wonderland") .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after"); - let alice: AccountId = "alice@wonderland".parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + let alice: AccountId = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #endregion mint_asset_define_asset_account // Mint the Asset instance @@ -222,11 +226,14 @@ fn asset_minting_test(config: Config) -> Result<(), Error> { // #region mint_asset_mint_alt // Mint the Asset instance (alternate syntax). - // The syntax is `asset_name#asset_domain#account_name@account_domain`, + // The syntax is `asset_name#asset_domain#account_signatory@account_domain`, // or `roses.to_string() + "#" + alice.to_string()`. // The `##` is a short-hand for the rose `which belongs to the same domain as the account // to which it belongs to. - let mint_roses_alt = Mint::asset_numeric(10u32, "rose##alice@wonderland".parse()?); + let alice_roses: AssetId = + "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + .parse()?; + let mint_roses_alt = Mint::asset_numeric(10u32, alice_roses); // #endregion mint_asset_mint_alt // #region mint_asset_submit_tx_alt @@ -256,8 +263,8 @@ fn asset_burning_test(config: Config) -> Result<(), Error> { // Define the instances of an Asset and Account let roses = AssetDefinitionId::from_str("rose#wonderland") .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after"); - let alice: AccountId = "alice@wonderland".parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + let alice: AccountId = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #endregion burn_asset_define_asset_account // #region burn_asset_burn @@ -273,11 +280,14 @@ fn asset_burning_test(config: Config) -> Result<(), Error> { // #region burn_asset_burn_alt // Burn the Asset instance (alternate syntax). - // The syntax is `asset_name#asset_domain#account_name@account_domain`, + // The syntax is `asset_name#asset_domain#account_signatory@account_domain`, // or `roses.to_string() + "#" + alice.to_string()`. // The `##` is a short-hand for the rose `which belongs to the same domain as the account // to which it belongs to. - let burn_roses_alt = Burn::asset_numeric(10u32, "rose##alice@wonderland".parse()?); + let alice_roses: AssetId = + "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + .parse()?; + let burn_roses_alt = Burn::asset_numeric(10u32, alice_roses); // #endregion burn_asset_burn_alt // #region burn_asset_submit_tx_alt diff --git a/client/src/client.rs b/client/src/client.rs index 2a4eed202d7..b725f9592aa 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -1575,6 +1575,7 @@ mod tests { use std::str::FromStr; use iroha_primitives::small::SmallStr; + use test_samples::gen_account_in; use super::*; use crate::config::{BasicAuth, Config, WebLogin}; @@ -1585,12 +1586,11 @@ mod tests { const ENCRYPTED_CREDENTIALS: &str = "bWFkX2hhdHRlcjppbG92ZXRlYQ=="; fn config_factory() -> Config { + let (account_id, key_pair) = gen_account_in("wonderland"); Config { chain_id: ChainId::from("0"), - key_pair: KeyPair::random(), - account_id: "alice@wonderland" - .parse() - .expect("This account ID should be valid"), + key_pair, + account_id, torii_api_url: "http://127.0.0.1:8080".parse().unwrap(), basic_auth: None, transaction_add_nonce: false, diff --git a/client/src/config.rs b/client/src/config.rs index 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..3ca231902fc 100644 --- a/client/tests/integration/extra_functional/offline_peers.rs +++ b/client/tests/integration/extra_functional/offline_peers.rs @@ -10,6 +10,7 @@ use iroha_config::parameters::actual::Root as Config; use iroha_crypto::KeyPair; use iroha_primitives::addr::socket_addr; use test_network::*; +use test_samples::ALICE_ID; use tokio::runtime::Runtime; #[test] @@ -25,7 +26,7 @@ fn genesis_block_is_committed_with_some_offline_peers() -> Result<()> { wait_for_genesis_committed(&network.clients(), 1); //When - let alice_id: AccountId = "alice@wonderland".parse()?; + let alice_id = ALICE_ID.clone(); let roses = "rose#wonderland".parse()?; let alice_has_roses = numeric!(13); diff --git a/client/tests/integration/extra_functional/restart_peer.rs b/client/tests/integration/extra_functional/restart_peer.rs index 3172e6d4492..5b1995ad2d2 100644 --- a/client/tests/integration/extra_functional/restart_peer.rs +++ b/client/tests/integration/extra_functional/restart_peer.rs @@ -8,11 +8,12 @@ use iroha_client::{ use iroha_config::parameters::actual::Root as Config; use rand::{seq::SliceRandom, thread_rng, Rng}; use test_network::*; +use test_samples::ALICE_ID; use tokio::runtime::Runtime; #[test] fn restarted_peer_should_have_the_same_asset_amount() -> Result<()> { - let account_id = AccountId::from_str("alice@wonderland").unwrap(); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").unwrap(); let quantity = numeric!(200); diff --git a/client/tests/integration/extra_functional/unregister_peer.rs b/client/tests/integration/extra_functional/unregister_peer.rs index f653e07890f..5742025cc01 100644 --- a/client/tests/integration/extra_functional/unregister_peer.rs +++ b/client/tests/integration/extra_functional/unregister_peer.rs @@ -3,7 +3,6 @@ use std::thread; use eyre::Result; use iroha_client::{ client::{self, QueryResult}, - crypto::KeyPair, data_model::{ parameter::{default::MAX_TRANSACTIONS_IN_BLOCK, ParametersBuilder}, prelude::*, @@ -11,6 +10,7 @@ use iroha_client::{ }; use iroha_config::parameters::actual::Root as Config; use test_network::*; +use test_samples::gen_account_in; // Note the test is marked as `unstable`, not the network. #[ignore = "ignore, more in #2851"] @@ -121,9 +121,8 @@ fn init() -> Result<( .add_parameter(MAX_TRANSACTIONS_IN_BLOCK, 1u32)? .into_set_parameters(); let create_domain = Register::domain(Domain::new("domain".parse()?)); - let account_id: AccountId = "account@domain".parse()?; - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())); let asset_definition_id: AssetDefinitionId = "xor#domain".parse()?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); diff --git a/client/tests/integration/extra_functional/unstable_network.rs b/client/tests/integration/extra_functional/unstable_network.rs index 836f263f45a..52b2b9ad851 100644 --- a/client/tests/integration/extra_functional/unstable_network.rs +++ b/client/tests/integration/extra_functional/unstable_network.rs @@ -8,6 +8,7 @@ use iroha_client::{ use iroha_config::parameters::actual::Root as Config; use rand::seq::SliceRandom; use test_network::*; +use test_samples::ALICE_ID; use tokio::runtime::Runtime; const MAX_TRANSACTIONS_IN_BLOCK: u32 = 5; @@ -75,7 +76,7 @@ fn unstable_network( let pipeline_time = Config::pipeline_time(); - let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id: AssetDefinitionId = "camomile#wonderland".parse().expect("Valid"); let register_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); diff --git a/client/tests/integration/mod.rs b/client/tests/integration/mod.rs index 98b17659895..37299969665 100644 --- a/client/tests/integration/mod.rs +++ b/client/tests/integration/mod.rs @@ -1,16 +1,9 @@ -use iroha_crypto::KeyPair; -use iroha_data_model::account::{Account, AccountId, NewAccount}; - -mod add_account; mod add_domain; mod asset; mod asset_propagation; -mod burn_public_keys; mod domain_owner_permissions; mod events; mod extra_functional; -mod multisignature_account; -mod multisignature_transaction; mod non_mintable; mod pagination; mod permissions; @@ -25,7 +18,3 @@ mod tx_chain_id; mod tx_history; mod tx_rollback; mod upgrade; - -fn new_account_with_random_public_key(account_id: AccountId) -> NewAccount { - Account::new(account_id, KeyPair::random().into_parts().0) -} diff --git a/client/tests/integration/multisignature_account.rs b/client/tests/integration/multisignature_account.rs deleted file mode 100644 index bdb290f0bc4..00000000000 --- a/client/tests/integration/multisignature_account.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::thread; - -use eyre::Result; -use iroha_client::{ - client::{self, Client, QueryResult}, - crypto::KeyPair, - data_model::prelude::*, -}; -use iroha_config::parameters::actual::Root as Config; -use test_network::*; - -#[test] -fn transaction_signed_by_new_signatory_of_account_should_pass() -> Result<()> { - let (_rt, peer, client) = ::new().with_port(10_605).start_with_runtime(); - wait_for_genesis_committed(&[client.clone()], 0); - let pipeline_time = Config::pipeline_time(); - - // Given - let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); - let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); - let create_asset = - Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); - let key_pair = KeyPair::random(); - let add_signatory = Mint::account_public_key(key_pair.public_key().clone(), account_id.clone()); - - let instructions: [InstructionBox; 2] = [create_asset.into(), add_signatory.into()]; - client.submit_all(instructions)?; - thread::sleep(pipeline_time * 2); - //When - let quantity = numeric!(200); - let mint_asset = Mint::asset_numeric( - quantity, - AssetId::new(asset_definition_id.clone(), account_id.clone()), - ); - Client::test_with_key(&peer.api_address, key_pair).submit_till( - mint_asset, - client::asset::by_account_id(account_id), - |result| { - let assets = result.collect::>>().expect("Valid"); - - assets.iter().any(|asset| { - asset.id().definition_id == asset_definition_id - && *asset.value() == AssetValue::Numeric(quantity) - }) - }, - )?; - Ok(()) -} diff --git a/client/tests/integration/multisignature_transaction.rs b/client/tests/integration/multisignature_transaction.rs deleted file mode 100644 index d319c4178f7..00000000000 --- a/client/tests/integration/multisignature_transaction.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::{str::FromStr as _, thread}; - -use eyre::Result; -use iroha_client::{ - client, - client::{Client, QueryResult}, - config::Config as ClientConfig, - crypto::KeyPair, - data_model::{ - parameter::{default::MAX_TRANSACTIONS_IN_BLOCK, ParametersBuilder}, - prelude::*, - }, -}; -use iroha_config::parameters::actual::Root as Config; -use test_network::*; - -#[allow(clippy::too_many_lines)] -#[test] -fn multisignature_transactions_should_be_accepted_after_fully_signed() -> Result<()> { - let (_rt, network, client) = Network::start_test_with_runtime(4, Some(10_945)); - wait_for_genesis_committed(&network.clients(), 0); - let pipeline_time = Config::pipeline_time(); - - client.submit_all_blocking( - ParametersBuilder::new() - .add_parameter(MAX_TRANSACTIONS_IN_BLOCK, 1u32)? - .into_set_parameters(), - )?; - - let alice_id = AccountId::from_str("alice@wonderland")?; - let alice_key_pair = get_key_pair(); - let key_pair_2 = KeyPair::random(); - let asset_definition_id = AssetDefinitionId::from_str("camomile#wonderland")?; - let create_asset = - Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); - let set_signature_condition = Mint::account_signature_check_condition( - SignatureCheckCondition::AllAccountSignaturesAnd( - vec![key_pair_2.public_key().clone()].into(), - ), - alice_id.clone(), - ); - - let mut client_config = ClientConfig::test(&network.genesis.api_address); - let client = Client::new(client_config.clone()); - let instructions: [InstructionBox; 2] = [create_asset.into(), set_signature_condition.into()]; - client.submit_all_blocking(instructions)?; - - //When - let quantity = numeric!(200); - let asset_id = AssetId::new(asset_definition_id, alice_id.clone()); - let mint_asset = Mint::asset_numeric(quantity, asset_id.clone()); - - client_config.account_id = alice_id.clone(); - client_config.key_pair = alice_key_pair; - let client = Client::new(client_config.clone()); - let instructions = [mint_asset.clone()]; - let transaction = client.build_transaction(instructions, UnlimitedMetadata::new()); - // The tx signed by the first account - let _ = client - .submit_transaction(&client.sign_transaction(transaction.clone())) - .expect_err("Transaction should not be added into the queue"); - - thread::sleep(pipeline_time); - - //Then - client_config.torii_api_url = format!( - "http://{}", - &network.peers.values().last().unwrap().api_address, - ) - .parse() - .unwrap(); - let client_1 = Client::new(client_config.clone()); - let request = client::asset::by_account_id(alice_id); - let assets = client_1 - .request(request.clone())? - .collect::>>()?; - assert_eq!( - assets.len(), - 2, // Alice has roses and cabbage from Genesis, but doesn't yet have camomile - "Multisignature transaction was committed before all required signatures were added" - ); - - client_config.key_pair = key_pair_2; - let client_2 = Client::new(client_config); - // The tx signed by the second account - client_2.submit_transaction(&client_2.sign_transaction(transaction))?; - - thread::sleep(pipeline_time); - - let assets = client_1 - .request(request)? - .collect::>>()?; - assert!(!assets.is_empty()); - let camomile_asset = assets - .iter() - .find(|asset| *asset.id() == asset_id) - .expect("Failed to find expected asset"); - assert_eq!(AssetValue::Numeric(quantity), *camomile_asset.value()); - - Ok(()) -} diff --git a/client/tests/integration/non_mintable.rs b/client/tests/integration/non_mintable.rs index e9652f29390..301ecb93b81 100644 --- a/client/tests/integration/non_mintable.rs +++ b/client/tests/integration/non_mintable.rs @@ -7,6 +7,7 @@ use iroha_client::{ }; use iroha_data_model::isi::InstructionBox; use test_network::*; +use test_samples::ALICE_ID; #[test] fn non_mintable_asset_can_be_minted_once_but_not_twice() -> Result<()> { @@ -14,7 +15,7 @@ fn non_mintable_asset_can_be_minted_once_but_not_twice() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = Register::asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).mintable_once(), @@ -62,7 +63,7 @@ fn non_mintable_asset_cannot_be_minted_if_registered_with_non_zero_value() -> Re wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset: InstructionBox = Register::asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).mintable_once(), @@ -99,7 +100,7 @@ fn non_mintable_asset_can_be_minted_if_registered_with_zero_value() -> Result<() wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = Register::asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).mintable_once(), diff --git a/client/tests/integration/pagination.rs b/client/tests/integration/pagination.rs index c8b4360ed28..176e3726e2e 100644 --- a/client/tests/integration/pagination.rs +++ b/client/tests/integration/pagination.rs @@ -16,12 +16,12 @@ fn limits_should_work() -> Result<()> { let vec = &client .build_query(asset::all_definitions()) .with_pagination(Pagination { - limit: Some(nonzero!(5_u32)), - start: Some(nonzero!(5_u64)), + limit: Some(nonzero!(7_u32)), + start: Some(nonzero!(1_u64)), }) .execute()? .collect::>>()?; - assert_eq!(vec.len(), 5); + assert_eq!(vec.len(), 7); Ok(()) } @@ -35,17 +35,18 @@ fn fetch_size_should_work() -> Result<()> { let iter = client .build_query(asset::all_definitions()) .with_pagination(Pagination { - limit: Some(nonzero!(20_u32)), - start: None, + limit: Some(nonzero!(7_u32)), + start: Some(nonzero!(1_u64)), }) - .with_fetch_size(FetchSize::new(Some(nonzero!(12_u32)))) + .with_fetch_size(FetchSize::new(Some(nonzero!(3_u32)))) .execute()?; - assert_eq!(iter.batch_len(), 12); + assert_eq!(iter.batch_len(), 3); Ok(()) } fn register_assets(client: &Client) -> Result<()> { - let register: Vec = ('a'..='z') + // FIXME transaction is rejected for more than a certain number of instructions + let register: Vec = ('a'..='j') .map(|c| c.to_string()) .map(|name| (name + "#wonderland").parse().expect("Valid")) .map(|asset_definition_id| { diff --git a/client/tests/integration/permissions.rs b/client/tests/integration/permissions.rs index 16a4c85140a..475066db77d 100644 --- a/client/tests/integration/permissions.rs +++ b/client/tests/integration/permissions.rs @@ -12,6 +12,7 @@ use iroha_data_model::{ use iroha_genesis::GenesisNetwork; use serde_json::json; use test_network::{PeerBuilder, *}; +use test_samples::{gen_account_in, ALICE_ID, BOB_ID}; #[test] fn genesis_transactions_are_validated() { @@ -22,7 +23,7 @@ fn genesis_transactions_are_validated() { let genesis = GenesisNetwork::test_with_instructions([Grant::permission( PermissionToken::new("InvalidToken".parse().unwrap(), &json!(null)), - AccountId::from_str("alice@wonderland").unwrap(), + ALICE_ID.clone(), ) .into()]); @@ -71,9 +72,9 @@ fn permissions_disallow_asset_transfer() { wait_for_genesis_committed(&[iroha_client.clone()], 0); // Given - let alice_id = "alice@wonderland".parse().expect("Valid"); - let bob_id: AccountId = "bob@wonderland".parse().expect("Valid"); - let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); + let alice_id = ALICE_ID.clone(); + let bob_id = BOB_ID.clone(); + let (mouse_id, _mouse_keypair) = gen_account_in("wonderland"); let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); @@ -124,9 +125,9 @@ fn permissions_disallow_asset_burn() { let (_rt, _peer, iroha_client) = ::new().with_port(10_735).start_with_runtime(); - let alice_id = "alice@wonderland".parse().expect("Valid"); - let bob_id: AccountId = "bob@wonderland".parse().expect("Valid"); - let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); + let alice_id = ALICE_ID.clone(); + let bob_id = BOB_ID.clone(); + let (mouse_id, _mouse_keypair) = gen_account_in("wonderland"); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); @@ -197,14 +198,13 @@ fn permissions_differ_not_only_by_names() { let (_rt, _not_drop, client) = ::new().with_port(10_745).start_with_runtime(); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); - let mouse_id: AccountId = "mouse@outfit".parse().expect("Valid"); - let mouse_keypair = KeyPair::random(); + let alice_id = ALICE_ID.clone(); + let (mouse_id, mouse_keypair) = gen_account_in("outfit"); // Registering mouse let outfit_domain: DomainId = "outfit".parse().unwrap(); let create_outfit_domain = Register::domain(Domain::new(outfit_domain.clone())); - let new_mouse_account = Account::new(mouse_id.clone(), mouse_keypair.public_key().clone()); + let new_mouse_account = Account::new(mouse_id.clone()); client .submit_all_blocking([ InstructionBox::from(create_outfit_domain), @@ -296,15 +296,14 @@ fn stored_vs_granted_token_payload() -> Result<()> { wait_for_genesis_committed(&[iroha_client.clone()], 0); // Given - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let alice_id = ALICE_ID.clone(); // Registering mouse and asset definition let asset_definition_id: AssetDefinitionId = "xor#wonderland".parse().expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::store(asset_definition_id.clone())); - let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); - let mouse_keypair = KeyPair::random(); - let new_mouse_account = Account::new(mouse_id.clone(), mouse_keypair.public_key().clone()); + let (mouse_id, mouse_keypair) = gen_account_in("wonderland"); + let new_mouse_account = Account::new(mouse_id.clone()); let instructions: [InstructionBox; 2] = [ Register::account(new_mouse_account).into(), create_asset.into(), @@ -319,7 +318,7 @@ fn stored_vs_granted_token_payload() -> Result<()> { PermissionToken::from_str_unchecked( "CanSetKeyValueInUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"xor#wonderland#mouse@wonderland\" }", + &*format!(r###"{{ "asset_id" : "xor#wonderland#{mouse_id}" }}"###), ), alice_id, ); @@ -347,13 +346,13 @@ fn permission_tokens_are_unified() { wait_for_genesis_committed(&[iroha_client.clone()], 0); // Given - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let alice_id = ALICE_ID.clone(); let allow_alice_to_transfer_rose_1 = Grant::permission( PermissionToken::from_str_unchecked( "CanTransferUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"rose#wonderland#alice@wonderland\" }", + &*format!(r###"{{ "asset_id" : "rose#wonderland#{alice_id}" }}"###), ), alice_id.clone(), ); @@ -362,7 +361,7 @@ fn permission_tokens_are_unified() { PermissionToken::from_str_unchecked( "CanTransferUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"rose##alice@wonderland\" }", + &*format!(r###"{{ "asset_id" : "rose##{alice_id}" }}"###), ), alice_id, ); @@ -381,7 +380,7 @@ fn associated_permission_tokens_removed_on_unregister() { let (_rt, _peer, iroha_client) = ::new().with_port(11_240).start_with_runtime(); wait_for_genesis_committed(&[iroha_client.clone()], 0); - let bob_id: AccountId = "bob@wonderland".parse().expect("Valid"); + let bob_id = BOB_ID.clone(); let kingdom_id: DomainId = "kingdom".parse().expect("Valid"); let kingdom = Domain::new(kingdom_id.clone()); diff --git a/client/tests/integration/queries/account.rs b/client/tests/integration/queries/account.rs index d8137dbc704..eb047c732cc 100644 --- a/client/tests/integration/queries/account.rs +++ b/client/tests/integration/queries/account.rs @@ -6,8 +6,7 @@ use iroha_client::{ data_model::prelude::*, }; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID}; #[test] fn find_accounts_with_asset() -> Result<()> { @@ -30,11 +29,11 @@ fn find_accounts_with_asset() -> Result<()> { )); let accounts: [AccountId; 5] = [ - "alice@wonderland".parse().expect("Valid"), - "mad_hatter@wonderland".parse().expect("Valid"), - "cheshire_cat@wonderland".parse().expect("Valid"), - "caterpillar@wonderland".parse().expect("Valid"), - "white_rabbit@wonderland".parse().expect("Valid"), + ALICE_ID.clone(), + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, ]; // Registering accounts @@ -42,7 +41,7 @@ fn find_accounts_with_asset() -> Result<()> { .iter() .skip(1) // Alice has already been registered in genesis .cloned() - .map(|account_id| Register::account(new_account_with_random_public_key(account_id))) + .map(|account_id| Register::account(Account::new(account_id))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; diff --git a/client/tests/integration/queries/asset.rs b/client/tests/integration/queries/asset.rs index 5ef6115078c..68ce747732f 100644 --- a/client/tests/integration/queries/asset.rs +++ b/client/tests/integration/queries/asset.rs @@ -1,7 +1,6 @@ use eyre::Result; use iroha_client::{ client::{Client, ClientQueryError}, - crypto::KeyPair, data_model::{ asset::AssetValue, isi::Instruction, @@ -10,6 +9,7 @@ use iroha_client::{ }, }; use test_network::*; +use test_samples::{gen_account_in, ALICE_ID}; #[test] #[allow(clippy::too_many_lines)] @@ -23,24 +23,19 @@ fn find_asset_total_quantity() -> Result<()> { test_client.submit_blocking(Register::domain(domain))?; let accounts: [AccountId; 5] = [ - "alice@wonderland".parse()?, - "mad_hatter@wonderland".parse()?, - "cheshire_cat@wonderland".parse()?, - "caterpillar@wonderland".parse()?, - "white_rabbit@looking_glass".parse()?, + ALICE_ID.clone(), + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("looking_glass").0, ]; - let keys = core::iter::repeat_with(KeyPair::random) - .take(accounts.len() - 1) - .collect::>(); - // Registering accounts let register_accounts = accounts .iter() .skip(1) // Alice has already been registered in genesis .cloned() - .zip(keys.iter().map(KeyPair::public_key).cloned()) - .map(|(account_id, public_key)| Register::account(Account::new(account_id, public_key))) + .map(|account_id| Register::account(Account::new(account_id))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; diff --git a/client/tests/integration/queries/query_errors.rs b/client/tests/integration/queries/query_errors.rs index 9a27cb7740c..5d8194fc043 100644 --- a/client/tests/integration/queries/query_errors.rs +++ b/client/tests/integration/queries/query_errors.rs @@ -1,5 +1,3 @@ -use std::str::FromStr; - use iroha_client::{ client::{self, ClientQueryError}, data_model::{ @@ -7,6 +5,7 @@ use iroha_client::{ query::error::{FindError, QueryExecutionFail}, }, }; +use test_samples::gen_account_in; #[test] fn non_existent_account_is_specific_error() { @@ -16,9 +15,7 @@ fn non_existent_account_is_specific_error() { // we cannot wait for genesis committment let err = client - .request(client::account::by_id( - AccountId::from_str("john_doe@regalia").unwrap(), - )) + .request(client::account::by_id(gen_account_in("regalia").0)) .expect_err("Should error"); match err { diff --git a/client/tests/integration/queries/role.rs b/client/tests/integration/queries/role.rs index d437b6f6926..76de3ef1681 100644 --- a/client/tests/integration/queries/role.rs +++ b/client/tests/integration/queries/role.rs @@ -7,6 +7,7 @@ use iroha_client::{ }; use serde_json::json; use test_network::*; +use test_samples::ALICE_ID; fn create_role_ids() -> [RoleId; 5] { [ @@ -123,7 +124,7 @@ fn find_roles_by_account_id() -> Result<()> { wait_for_genesis_committed(&[test_client.clone()], 0); let role_ids = create_role_ids(); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let alice_id = ALICE_ID.clone(); // Registering roles let register_roles = role_ids diff --git a/client/tests/integration/roles.rs b/client/tests/integration/roles.rs index 3a3bcd7aff6..5199da7d77f 100644 --- a/client/tests/integration/roles.rs +++ b/client/tests/integration/roles.rs @@ -3,14 +3,12 @@ use std::str::FromStr as _; use eyre::Result; use iroha_client::{ client::{self, QueryResult}, - crypto::KeyPair, data_model::prelude::*, }; use iroha_data_model::transaction::error::TransactionRejectionReason; use serde_json::json; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID}; #[test] fn register_empty_role() -> Result<()> { @@ -54,15 +52,11 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_700).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let alice_id = AccountId::from_str("alice@wonderland")?; - let mouse_id = AccountId::from_str("mouse@wonderland")?; + let alice_id = ALICE_ID.clone(); + let (mouse_id, mouse_keypair) = gen_account_in("wonderland"); // Registering Mouse - let mouse_key_pair = KeyPair::random(); - let register_mouse = Register::account(Account::new( - mouse_id.clone(), - mouse_key_pair.public_key().clone(), - )); + let register_mouse = Register::account(Account::new(mouse_id.clone())); test_client.submit_blocking(register_mouse)?; // Registering role @@ -83,7 +77,7 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { let grant_role = Grant::role(role_id.clone(), alice_id.clone()); let grant_role_tx = TransactionBuilder::new(chain_id, mouse_id.clone()) .with_instructions([grant_role]) - .sign(&mouse_key_pair); + .sign(&mouse_keypair); test_client.submit_transaction_blocking(&grant_role_tx)?; // Alice modifies Mouse's metadata @@ -109,11 +103,11 @@ fn unregistered_role_removed_from_account() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let role_id: RoleId = "root".parse().expect("Valid"); - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); - let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid"); + let alice_id = ALICE_ID.clone(); + let (mouse_id, _mouse_keypair) = gen_account_in("wonderland"); // Registering Mouse - let register_mouse = Register::account(new_account_with_random_public_key(mouse_id.clone())); + let register_mouse = Register::account(Account::new(mouse_id.clone())); test_client.submit_blocking(register_mouse)?; // Register root role @@ -154,7 +148,9 @@ fn role_with_invalid_permissions_is_not_accepted() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let role_id = RoleId::from_str("ACCESS_TO_ACCOUNT_METADATA")?; - let rose_asset_id = AssetId::from_str("rose##alice@wonderland")?; + let rose_asset_id: AssetId = format!("rose##{}", ALICE_ID.clone()) + .parse() + .expect("should be valid"); let role = Role::new(role_id).add_permission(PermissionToken::new( "CanSetKeyValueInAccount".parse()?, &json!({ "account_id": rose_asset_id }), @@ -185,13 +181,13 @@ fn role_permissions_unified() { let allow_alice_to_transfer_rose_1 = PermissionToken::from_str_unchecked( "CanTransferUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"rose#wonderland#alice@wonderland\" }", + "{ \"asset_id\" : \"rose#wonderland#ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland\" }", ); let allow_alice_to_transfer_rose_2 = PermissionToken::from_str_unchecked( "CanTransferUserAsset".parse().unwrap(), // NOTE: Introduced additional whitespaces in the serialized form - "{ \"asset_id\" : \"rose##alice@wonderland\" }", + "{ \"asset_id\" : \"rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland\" }", ); let role_id: RoleId = "role_id".parse().expect("Valid"); @@ -222,15 +218,11 @@ fn grant_revoke_role_permissions() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(11_245).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let alice_id = AccountId::from_str("alice@wonderland")?; - let mouse_id = AccountId::from_str("mouse@wonderland")?; + let alice_id = ALICE_ID.clone(); + let (mouse_id, mouse_keypair) = gen_account_in("wonderland"); // Registering Mouse - let mouse_key_pair = KeyPair::random(); - let register_mouse = Register::account(Account::new( - mouse_id.clone(), - mouse_key_pair.public_key().clone(), - )); + let register_mouse = Register::account(Account::new(mouse_id.clone())); test_client.submit_blocking(register_mouse)?; // Registering role @@ -248,7 +240,7 @@ fn grant_revoke_role_permissions() -> Result<()> { let grant_role = Grant::role(role_id.clone(), alice_id.clone()); let grant_role_tx = TransactionBuilder::new(chain_id.clone(), mouse_id.clone()) .with_instructions([grant_role]) - .sign(&mouse_key_pair); + .sign(&mouse_keypair); test_client.submit_transaction_blocking(&grant_role_tx)?; let set_key_value = SetKeyValue::account( @@ -275,7 +267,7 @@ fn grant_revoke_role_permissions() -> Result<()> { // Alice can modify Mouse's metadata after permission token is granted to role let grant_role_permission_tx = TransactionBuilder::new(chain_id.clone(), mouse_id.clone()) .with_instructions([grant_role_permission]) - .sign(&mouse_key_pair); + .sign(&mouse_keypair); test_client.submit_transaction_blocking(&grant_role_permission_tx)?; let found_permissions = test_client .request(FindPermissionTokensByAccountId::new(alice_id.clone()))? @@ -286,7 +278,7 @@ fn grant_revoke_role_permissions() -> Result<()> { // Alice can't modify Mouse's metadata after permission token is removed from role let revoke_role_permission_tx = TransactionBuilder::new(chain_id.clone(), mouse_id.clone()) .with_instructions([revoke_role_permission]) - .sign(&mouse_key_pair); + .sign(&mouse_keypair); test_client.submit_transaction_blocking(&revoke_role_permission_tx)?; let found_permissions = test_client .request(FindPermissionTokensByAccountId::new(alice_id.clone()))? diff --git a/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs b/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs index 1f62efe9689..4ae58d430b7 100644 --- a/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs +++ b/client/tests/integration/smartcontracts/create_nft_for_every_user_trigger/src/lib.rs @@ -38,7 +38,7 @@ fn main(_id: TriggerId, _owner: AccountId, _event: EventBox) { let mut metadata = Metadata::new(); let name = format!( "nft_for_{}_in_{}", - account.id().name(), + account.id().signatory(), account.id().domain_id() ) .parse() @@ -78,7 +78,7 @@ fn generate_new_nft_id(account_id: &AccountId) -> AssetDefinitionId { format!( "nft_number_{}_for_{}#{}", new_number, - account_id.name(), + account_id.signatory(), account_id.domain_id() ) .parse() diff --git a/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs b/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs index d861b6e13fd..ebbbe476a2a 100644 --- a/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs +++ b/client/tests/integration/smartcontracts/executor_with_admin/src/lib.rs @@ -1,5 +1,5 @@ -//! Runtime Executor which allows any instruction executed by `admin@admin` account. -//! If authority is not `admin@admin` then default validation is used as a backup. +//! Runtime Executor which allows any instruction executed by [admin](crate::integration::upgrade::ADMIN_ID) account. +//! If authority is not admin then default validation is used as a backup. #![no_std] @@ -22,7 +22,8 @@ struct Executor { } fn visit_instruction(executor: &mut Executor, authority: &AccountId, isi: &InstructionBox) { - if parse!("admin@admin" as AccountId) == *authority { + let admin_id = "ed012076E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC@admin"; // equals to integration::upgrade::ADMIN_ID + if *authority == parse!(AccountId, admin_id) { execute!(executor, isi); } diff --git a/client/tests/integration/smartcontracts/executor_with_migration_fail/src/lib.rs b/client/tests/integration/smartcontracts/executor_with_migration_fail/src/lib.rs index 1b04091ace8..0aaa7907707 100644 --- a/client/tests/integration/smartcontracts/executor_with_migration_fail/src/lib.rs +++ b/client/tests/integration/smartcontracts/executor_with_migration_fail/src/lib.rs @@ -28,7 +28,7 @@ pub fn migrate(_block_height: u64) -> MigrationResult { // Performing side-effects to check in the test that it won't be applied after failure // Registering a new domain (using ISI) - let domain_id = parse!("failed_migration_test_domain" as DomainId); + let domain_id = parse!(DomainId, "failed_migration_test_domain"); Register::domain(Domain::new(domain_id)) .execute() .map_err(|error| { diff --git a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs index dacc5b9789c..74ee7b4377d 100644 --- a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs +++ b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs @@ -46,7 +46,7 @@ fn main(owner: AccountId) { SetKeyValue::account( owner, - parse!("cursor" as Name), + parse!(Name, "cursor"), MetadataValueBox::String( serde_json::to_value(&asset_cursor.cursor) .dbg_expect("Failed to convert cursor to JSON") diff --git a/client/tests/integration/sorting.rs b/client/tests/integration/sorting.rs index bddfcd7ee39..f7651f382c0 100644 --- a/client/tests/integration/sorting.rs +++ b/client/tests/integration/sorting.rs @@ -12,121 +12,128 @@ use iroha_client::{ }, }, }; -use iroha_data_model::isi::InstructionBox; +use iroha_crypto::KeyPair; use nonzero_ext::nonzero; +use rand::{seq::SliceRandom, thread_rng}; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::ALICE_ID; #[test] +#[allow(clippy::cast_possible_truncation)] fn correct_pagination_assets_after_creating_new_one() { - let (_rt, _peer, test_client) = ::new().with_port(10_635).start_with_runtime(); - + // FIXME transaction is rejected for more than a certain number of instructions + const N_ASSETS: usize = 12; + // 0 < pagination.start < missing_idx < pagination.end < N_ASSETS + let missing_indices = vec![N_ASSETS / 2]; + let pagination = Pagination { + limit: Some(nonzero!(N_ASSETS as u32 / 3)), + start: Some(nonzero!(N_ASSETS as u64 / 3)), + }; + let xor_filter = PredicateBox::new(value::QueryOutputPredicate::Identifiable( + string::StringPredicate::starts_with("xor"), + )); let sort_by_metadata_key = Name::from_str("sort").expect("Valid"); + let sorting = Sorting::by_metadata_key(sort_by_metadata_key.clone()); + let account_id = ALICE_ID.clone(); - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let (_rt, _peer, test_client) = ::new().with_port(10_635).start_with_runtime(); + wait_for_genesis_committed(&[test_client.clone()], 0); - let mut assets = vec![]; - let mut instructions = vec![]; + let mut tester_assets = vec![]; + let mut register_asset_definitions = vec![]; + let mut register_assets = vec![]; - for i in 0..20_u32 { + let mut missing_tester_assets = vec![]; + let mut missing_register_asset_definitions = vec![]; + let mut missing_register_assets = vec![]; + + for i in 0..N_ASSETS { let asset_definition_id = AssetDefinitionId::from_str(&format!("xor{i}#wonderland")).expect("Valid"); let asset_definition = AssetDefinition::store(asset_definition_id.clone()); let mut asset_metadata = Metadata::new(); asset_metadata - .insert_with_limits(sort_by_metadata_key.clone(), i, MetadataLimits::new(10, 23)) + .insert_with_limits( + sort_by_metadata_key.clone(), + i as u32, + MetadataLimits::new(10, 23), + ) .expect("Valid"); let asset = Asset::new( AssetId::new(asset_definition_id, account_id.clone()), AssetValue::Store(asset_metadata), ); - assets.push(asset.clone()); - - let create_asset_definition: InstructionBox = - Register::asset_definition(asset_definition).into(); - let create_asset = Register::asset(asset).into(); - - instructions.push(create_asset_definition); - instructions.push(create_asset); + if missing_indices.contains(&i) { + missing_tester_assets.push(asset.clone()); + missing_register_asset_definitions.push(Register::asset_definition(asset_definition)); + missing_register_assets.push(Register::asset(asset)); + } else { + tester_assets.push(asset.clone()); + register_asset_definitions.push(Register::asset_definition(asset_definition)); + register_assets.push(Register::asset(asset)); + } } + register_asset_definitions.shuffle(&mut thread_rng()); + register_assets.shuffle(&mut thread_rng()); test_client - .submit_all_blocking(instructions) + .submit_all_blocking(register_asset_definitions) + .expect("Valid"); + test_client + .submit_all_blocking(register_assets) .expect("Valid"); - let sorting = Sorting::by_metadata_key(sort_by_metadata_key.clone()); - - let res = test_client - .build_query(client::asset::by_account_id(account_id.clone())) - .with_pagination(Pagination { - limit: Some(nonzero!(5_u32)), - start: None, - }) + let queried_assets = test_client + .build_query(client::asset::all()) + .with_filter(xor_filter.clone()) + .with_pagination(pagination) .with_sorting(sorting.clone()) .execute() .expect("Valid") .collect::>>() .expect("Valid"); - assert!(res + tester_assets .iter() - .map(|asset| &asset.id().definition_id.name) - .eq(assets - .iter() - .take(5) - .map(|asset| &asset.id().definition_id.name))); - - let new_asset_definition_id = AssetDefinitionId::from_str("xor20#wonderland").expect("Valid"); - let new_asset_definition = AssetDefinition::store(new_asset_definition_id.clone()); - let mut new_asset_metadata = Metadata::new(); - new_asset_metadata - .insert_with_limits( - sort_by_metadata_key, - numeric!(20), - MetadataLimits::new(10, 23), - ) - .expect("Valid"); - let new_asset = Asset::new( - AssetId::new(new_asset_definition_id, account_id.clone()), - AssetValue::Store(new_asset_metadata), - ); - - let create_asset_definition: InstructionBox = - Register::asset_definition(new_asset_definition).into(); - let create_asset = Register::asset(new_asset.clone()).into(); + .skip(N_ASSETS / 3) + .take(N_ASSETS / 3) + .zip(queried_assets) + .for_each(|(tester, queried)| assert_eq!(*tester, queried)); + for (i, missing_idx) in missing_indices.into_iter().enumerate() { + tester_assets.insert(missing_idx, missing_tester_assets[i].clone()); + } + test_client + .submit_all_blocking(missing_register_asset_definitions) + .expect("Valid"); test_client - .submit_all_blocking([create_asset_definition, create_asset]) + .submit_all_blocking(missing_register_assets) .expect("Valid"); - let res = test_client - .build_query(client::asset::by_account_id(account_id)) - .with_pagination(Pagination { - limit: Some(nonzero!(13_u32)), - start: Some(nonzero!(8_u64)), - }) + let queried_assets = test_client + .build_query(client::asset::all()) + .with_filter(xor_filter) + .with_pagination(pagination) .with_sorting(sorting) .execute() .expect("Valid") .collect::>>() .expect("Valid"); - assert!(res + tester_assets .iter() - .map(|asset| &asset.id().definition_id.name) - .eq(assets - .iter() - .skip(8) - .chain(core::iter::once(&new_asset)) - .map(|asset| &asset.id().definition_id.name))); + .skip(N_ASSETS / 3) + .take(N_ASSETS / 3) + .zip(queried_assets) + .for_each(|(tester, queried)| assert_eq!(*tester, queried)); } #[test] #[allow(clippy::too_many_lines)] fn correct_sorting_of_entities() { let (_rt, _peer, test_client) = ::new().with_port(10_640).start_with_runtime(); + wait_for_genesis_committed(&[test_client.clone()], 0); let sort_by_metadata_key = Name::from_str("test_sort").expect("Valid"); @@ -183,13 +190,23 @@ fn correct_sorting_of_entities() { // Test sorting accounts + let domain_name = "_neverland"; + let domain_id: DomainId = domain_name.parse().unwrap(); + test_client + .submit_blocking(Register::domain(Domain::new(domain_id.clone()))) + .expect("should be committed"); + let mut accounts = vec![]; let mut metadata_of_accounts = vec![]; let mut instructions = vec![]; let n = 10u32; + let mut public_keys = (0..n) + .map(|_| KeyPair::random().into_parts().0) + .collect::>(); + public_keys.sort_unstable(); for i in 0..n { - let account_id = AccountId::from_str(&format!("charlie{i}@wonderland")).expect("Valid"); + let account_id = AccountId::new(domain_id.clone(), public_keys[i as usize].clone()); let mut account_metadata = Metadata::new(); account_metadata .insert_with_limits( @@ -198,8 +215,7 @@ fn correct_sorting_of_entities() { MetadataLimits::new(10, 28), ) .expect("Valid"); - let account = new_account_with_random_public_key(account_id.clone()) - .with_metadata(account_metadata.clone()); + let account = Account::new(account_id.clone()).with_metadata(account_metadata.clone()); accounts.push(account_id); metadata_of_accounts.push(account_metadata); @@ -216,8 +232,8 @@ fn correct_sorting_of_entities() { .build_query(client::account::all()) .with_sorting(Sorting::by_metadata_key(sort_by_metadata_key.clone())) .with_filter(PredicateBox::new( - value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with( - "charlie", + value::QueryOutputPredicate::Identifiable(string::StringPredicate::ends_with( + domain_name, )), )) .execute() @@ -328,7 +344,15 @@ fn correct_sorting_of_entities() { #[test] fn sort_only_elements_which_have_sorting_key() -> Result<()> { + const TEST_DOMAIN: &str = "neverland"; + let (_rt, _peer, test_client) = ::new().with_port(10_680).start_with_runtime(); + wait_for_genesis_committed(&[test_client.clone()], 0); + + let domain_id: DomainId = TEST_DOMAIN.parse().unwrap(); + test_client + .submit_blocking(Register::domain(Domain::new(domain_id.clone()))) + .expect("should be committed"); let sort_by_metadata_key = Name::from_str("test_sort").expect("Valid"); @@ -341,10 +365,14 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { skip_set.insert(7); let n = 10u32; + let mut public_keys = (0..n) + .map(|_| KeyPair::random().into_parts().0) + .collect::>(); + public_keys.sort_unstable(); for i in 0..n { - let account_id = AccountId::from_str(&format!("charlie{i}@wonderland")).expect("Valid"); + let account_id = AccountId::new(domain_id.clone(), public_keys[i as usize].clone()); let account = if skip_set.contains(&i) { - let account = new_account_with_random_public_key(account_id.clone()); + let account = Account::new(account_id.clone()); accounts_b.push(account_id); account } else { @@ -356,8 +384,7 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { MetadataLimits::new(10, 28), ) .expect("Valid"); - let account = new_account_with_random_public_key(account_id.clone()) - .with_metadata(account_metadata); + let account = Account::new(account_id.clone()).with_metadata(account_metadata); accounts_a.push(account_id); account }; @@ -374,8 +401,8 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { .build_query(client::account::all()) .with_sorting(Sorting::by_metadata_key(sort_by_metadata_key)) .with_filter(PredicateBox::new( - value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with( - "charlie", + value::QueryOutputPredicate::Identifiable(string::StringPredicate::ends_with( + TEST_DOMAIN, )), )) .execute() diff --git a/client/tests/integration/status_response.rs b/client/tests/integration/status_response.rs index a0bf6c87d97..8209d46b5dd 100644 --- a/client/tests/integration/status_response.rs +++ b/client/tests/integration/status_response.rs @@ -4,6 +4,7 @@ use eyre::Result; use iroha_client::{data_model::prelude::*, samples::get_status_json}; use iroha_telemetry::metrics::Status; use test_network::*; +use test_samples::gen_account_in; fn status_eq_excluding_uptime_and_queue(lhs: &Status, rhs: &Status) -> bool { lhs.peers == rhs.peers @@ -29,8 +30,7 @@ fn json_and_scale_statuses_equality() -> Result<()> { let coins = ["xor", "btc", "eth", "doge"]; - let domain_id: DomainId = "test_domain".parse().expect("Should be valid"); - let account_id = AccountId::new(domain_id, "test_account".parse().expect("Should be valid")); + let (account_id, _account_keypair) = gen_account_in("domain"); for coin in coins { let asset_definition_id = AssetDefinitionId::from_str(&format!("{coin}#wonderland"))?; diff --git a/client/tests/integration/transfer_asset.rs b/client/tests/integration/transfer_asset.rs index 31c2750068f..f58d479e49c 100644 --- a/client/tests/integration/transfer_asset.rs +++ b/client/tests/integration/transfer_asset.rs @@ -2,7 +2,6 @@ use std::str::FromStr; use iroha_client::{ client::{self, QueryResult}, - crypto::KeyPair, data_model::{isi::Instruction, prelude::*, Registered}, }; use iroha_data_model::{ @@ -12,6 +11,7 @@ use iroha_data_model::{ name::Name, }; use test_network::*; +use test_samples::{gen_account_in, ALICE_ID}; #[test] // This test suite is also covered at the UI level in the iroha_client_cli tests @@ -135,12 +135,11 @@ fn simulate_transfer( } fn generate_two_ids() -> (AccountId, AccountId) { - let alice_id: AccountId = "alice@wonderland".parse().unwrap(); - let mouse_id: AccountId = "mouse@wonderland".parse().unwrap(); + let alice_id = ALICE_ID.clone(); + let (mouse_id, _mouse_keypair) = gen_account_in("wonderland"); (alice_id, mouse_id) } fn create_mouse(mouse_id: AccountId) -> Register { - let (mouse_public_key, _) = KeyPair::random().into_parts(); - Register::account(Account::new(mouse_id, mouse_public_key)) + Register::account(Account::new(mouse_id)) } diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index 37ccf66d12e..4c93ebacdc4 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -12,6 +12,7 @@ use iroha_client::{ use iroha_genesis::GenesisNetwork; use iroha_logger::info; use test_network::*; +use test_samples::ALICE_ID; const TRIGGER_NAME: &str = "mint_rose"; @@ -21,7 +22,7 @@ fn call_execute_trigger() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id); let prev_value = get_asset_value(&mut test_client, asset_id.clone()); @@ -45,7 +46,7 @@ fn execute_trigger_should_produce_event() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id: AccountId = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let instruction = Mint::asset_numeric(1u32, asset_id.clone()); @@ -81,7 +82,7 @@ fn infinite_recursion_should_produce_one_call_per_block() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id); let trigger_id = TriggerId::from_str(TRIGGER_NAME)?; let call_trigger = ExecuteTrigger::new(trigger_id); @@ -108,7 +109,7 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); // Registering trigger that should fail on execution @@ -164,7 +165,7 @@ fn trigger_should_not_be_executed_with_zero_repeats_count() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id = TriggerId::from_str("self_modifying_trigger")?; @@ -224,7 +225,7 @@ fn trigger_should_be_able_to_modify_its_own_repeats_count() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id = TriggerId::from_str("self_modifying_trigger")?; @@ -270,7 +271,7 @@ fn unregister_trigger() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_035).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); // Registering trigger let trigger_id = TriggerId::from_str("empty_trigger")?; @@ -346,7 +347,7 @@ fn trigger_in_genesis_using_base64() -> Result<()> { let engine = base64::engine::general_purpose::STANDARD; let wasm_base64 = serde_json::json!(base64::engine::Engine::encode(&engine, wasm)).to_string(); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let trigger_id = TriggerId::from_str("genesis_trigger")?; let trigger = Trigger::new( @@ -399,7 +400,7 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id_unregister = TriggerId::from_str("unregister_other_trigger")?; let trigger_id_to_be_unregistered = TriggerId::from_str("should_be_unregistered_trigger")?; @@ -459,7 +460,7 @@ fn trigger_burn_repetitions() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let trigger_id = TriggerId::from_str("trigger")?; @@ -494,7 +495,7 @@ fn unregistering_one_of_two_triggers_with_identical_wasm_should_not_cause_origin let (_rt, _peer, test_client) = ::new().with_port(11_105).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let first_trigger_id = TriggerId::from_str("mint_rose_1")?; let second_trigger_id = TriggerId::from_str("mint_rose_2")?; diff --git a/client/tests/integration/triggers/data_trigger.rs b/client/tests/integration/triggers/data_trigger.rs index 46f505a9f9f..3f070c2e4a2 100644 --- a/client/tests/integration/triggers/data_trigger.rs +++ b/client/tests/integration/triggers/data_trigger.rs @@ -2,15 +2,14 @@ use eyre::Result; use iroha_client::{client, data_model::prelude::*}; use iroha_data_model::asset::AssetValue; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID}; #[test] fn must_execute_both_triggers() -> Result<()> { let (_rt, _peer, test_client) = ::new().with_port(10_650).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); - let account_id: AccountId = "alice@wonderland".parse()?; + let account_id = ALICE_ID.clone(); let asset_definition_id = "rose#wonderland".parse()?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); @@ -39,8 +38,8 @@ fn must_execute_both_triggers() -> Result<()> { )); test_client.submit_blocking(register_trigger)?; - test_client.submit_blocking(Register::account(new_account_with_random_public_key( - "bunny@wonderland".parse()?, + test_client.submit_blocking(Register::account(Account::new( + gen_account_in("wonderland").0, )))?; test_client.submit_blocking(Register::domain(Domain::new("neverland".parse()?)))?; @@ -58,9 +57,8 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu let create_neverland_domain: InstructionBox = Register::domain(Domain::new("neverland".parse()?)).into(); - let account_id: AccountId = "sapporo@neverland".parse()?; - let create_sapporo_account = - Register::account(new_account_with_random_public_key(account_id.clone())).into(); + let (account_id, _account_keypair) = gen_account_in("neverland"); + let create_sapporo_account = Register::account(Account::new(account_id.clone())).into(); let asset_definition_id: AssetDefinitionId = "sakura#neverland".parse()?; let create_sakura_asset_definition = @@ -89,12 +87,12 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu )); test_client.submit_blocking(register_trigger)?; - test_client.submit_blocking(Register::account(new_account_with_random_public_key( - "asahi@wonderland".parse()?, + test_client.submit_blocking(Register::account(Account::new( + gen_account_in("wonderland").0, )))?; - test_client.submit_blocking(Register::account(new_account_with_random_public_key( - "asahi@neverland".parse()?, + test_client.submit_blocking(Register::account(Account::new( + gen_account_in("neverland").0, )))?; let new_value = get_asset_value(&test_client, asset_id); diff --git a/client/tests/integration/triggers/event_trigger.rs b/client/tests/integration/triggers/event_trigger.rs index 12a5dca633c..756a2ee6ac3 100644 --- a/client/tests/integration/triggers/event_trigger.rs +++ b/client/tests/integration/triggers/event_trigger.rs @@ -1,11 +1,10 @@ -use std::str::FromStr; - use eyre::Result; use iroha_client::{ client::{self, Client}, data_model::prelude::*, }; use test_network::*; +use test_samples::ALICE_ID; #[test] fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { @@ -13,7 +12,7 @@ fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse()?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let prev_value = get_asset_value(&mut test_client, asset_id.clone()); diff --git a/client/tests/integration/triggers/time_trigger.rs b/client/tests/integration/triggers/time_trigger.rs index 8a9bb9fb034..a7d161eb033 100644 --- a/client/tests/integration/triggers/time_trigger.rs +++ b/client/tests/integration/triggers/time_trigger.rs @@ -9,8 +9,7 @@ use iroha_config::parameters::defaults::chain_wide::DEFAULT_CONSENSUS_ESTIMATION use iroha_data_model::events::pipeline::{BlockEventFilter, BlockStatus}; use iroha_logger::info; use test_network::*; - -use crate::integration::new_account_with_random_public_key; +use test_samples::{gen_account_in, ALICE_ID}; fn curr_time() -> core::time::Duration { use std::time::SystemTime; @@ -47,7 +46,7 @@ fn time_trigger_execution_count_error_should_be_less_than_15_percent() -> Result // Start listening BEFORE submitting any transaction not to miss any block committed event let event_listener = get_block_committed_event_listener(&test_client)?; - let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_definition_id = "rose#wonderland".parse().expect("Valid"); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); @@ -107,7 +106,7 @@ fn change_asset_metadata_after_1_sec() -> Result<()> { let event_listener = get_block_committed_event_listener(&test_client)?; let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let account_id = ALICE_ID.clone(); let key = Name::from_str("petal")?; let schedule = TimeSchedule::starting_at(start_time + PERIOD); @@ -148,7 +147,7 @@ fn pre_commit_trigger_should_be_executed() -> Result<()> { wait_for_genesis_committed(&vec![test_client.clone()], 0); let asset_definition_id = "rose#wonderland".parse().expect("Valid"); - let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); + let account_id = ALICE_ID.clone(); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); let mut prev_value = get_asset_value(&mut test_client, asset_id.clone()); @@ -193,14 +192,14 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { let (_rt, _peer, mut test_client) = ::new().with_port(10_780).start_with_runtime(); wait_for_genesis_committed(&vec![test_client.clone()], 0); - let alice_id = "alice@wonderland".parse::().expect("Valid"); + let alice_id = ALICE_ID.clone(); let accounts: Vec = vec![ alice_id.clone(), - "mad_hatter@wonderland".parse().expect("Valid"), - "cheshire_cat@wonderland".parse().expect("Valid"), - "caterpillar@wonderland".parse().expect("Valid"), - "white_rabbit@wonderland".parse().expect("Valid"), + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, + gen_account_in("wonderland").0, ]; // Registering accounts @@ -208,7 +207,7 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { .iter() .skip(1) // Alice has already been registered in genesis .cloned() - .map(|account_id| Register::account(new_account_with_random_public_key(account_id))) + .map(|account_id| Register::account(Account::new(account_id))) .collect::>(); test_client.submit_all_blocking(register_accounts)?; @@ -255,7 +254,7 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> { // Checking results for account_id in accounts { let start_pattern = "nft_number_"; - let end_pattern = format!("_for_{}#{}", account_id.name, account_id.domain_id); + let end_pattern = format!("_for_{}#{}", account_id.signatory, account_id.domain_id); let assets = test_client .request(client::asset::by_account_id(account_id.clone()))? .collect::>>()?; diff --git a/client/tests/integration/triggers/trigger_rollback.rs b/client/tests/integration/triggers/trigger_rollback.rs index 6f61cae9835..1eb6188d916 100644 --- a/client/tests/integration/triggers/trigger_rollback.rs +++ b/client/tests/integration/triggers/trigger_rollback.rs @@ -6,6 +6,7 @@ use iroha_client::{ data_model::{prelude::*, query::asset::FindAllAssetsDefinitions, trigger::TriggerId}, }; use test_network::*; +use test_samples::ALICE_ID; #[test] fn failed_trigger_revert() -> Result<()> { @@ -14,7 +15,7 @@ fn failed_trigger_revert() -> Result<()> { //When let trigger_id = TriggerId::from_str("trigger")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); diff --git a/client/tests/integration/tx_chain_id.rs b/client/tests/integration/tx_chain_id.rs index 9e16a90a898..e439361c728 100644 --- a/client/tests/integration/tx_chain_id.rs +++ b/client/tests/integration/tx_chain_id.rs @@ -1,38 +1,28 @@ use std::str::FromStr; -use iroha_crypto::KeyPair; use iroha_data_model::prelude::*; use iroha_primitives::numeric::numeric; use test_network::*; - -use crate::integration::asset::asset_id_new; +use test_samples::gen_account_in; #[test] fn send_tx_with_different_chain_id() { let (_rt, _peer, test_client) = ::new().with_port(11_250).start_with_runtime(); wait_for_genesis_committed(&[test_client.clone()], 0); // Given - let sender_account_id = AccountId::from_str("sender@wonderland").unwrap(); - let sender_keypair = KeyPair::random(); - let receiver_account_id = AccountId::from_str("receiver@wonderland").unwrap(); - let receiver_keypair = KeyPair::random(); + let (sender_id, sender_keypair) = gen_account_in("wonderland"); + let (receiver_id, _receiver_keypair) = gen_account_in("wonderland"); let asset_definition_id = AssetDefinitionId::from_str("test_asset#wonderland").unwrap(); let to_transfer = numeric!(1); - let create_sender_account: InstructionBox = Register::account(Account::new( - sender_account_id.clone(), - sender_keypair.public_key().clone(), - )) - .into(); - let create_receiver_account: InstructionBox = Register::account(Account::new( - receiver_account_id.clone(), - receiver_keypair.public_key().clone(), - )) - .into(); + let create_sender_account: InstructionBox = + Register::account(Account::new(sender_id.clone())).into(); + let create_receiver_account: InstructionBox = + Register::account(Account::new(receiver_id.clone())).into(); let register_asset_definition: InstructionBox = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())).into(); let register_asset: InstructionBox = Register::asset(Asset::new( - AssetId::new(asset_definition_id.clone(), sender_account_id.clone()), + AssetId::new(asset_definition_id.clone(), sender_id.clone()), numeric!(10), )) .into(); @@ -48,14 +38,14 @@ fn send_tx_with_different_chain_id() { let chain_id_1 = ChainId::from("1"); let transfer_instruction = Transfer::asset_numeric( - asset_id_new("test_asset", "wonderland", sender_account_id.clone()), + AssetId::new("test_asset#wonderland".parse().unwrap(), sender_id.clone()), to_transfer, - receiver_account_id.clone(), + receiver_id.clone(), ); - let asset_transfer_tx_0 = TransactionBuilder::new(chain_id_0, sender_account_id.clone()) + let asset_transfer_tx_0 = TransactionBuilder::new(chain_id_0, sender_id.clone()) .with_instructions([transfer_instruction.clone()]) .sign(&sender_keypair); - let asset_transfer_tx_1 = TransactionBuilder::new(chain_id_1, sender_account_id.clone()) + let asset_transfer_tx_1 = TransactionBuilder::new(chain_id_1, sender_id.clone()) .with_instructions([transfer_instruction]) .sign(&sender_keypair); test_client diff --git a/client/tests/integration/tx_history.rs b/client/tests/integration/tx_history.rs index fb8b22ea604..19a57bd638e 100644 --- a/client/tests/integration/tx_history.rs +++ b/client/tests/integration/tx_history.rs @@ -8,6 +8,7 @@ use iroha_client::{ use iroha_config::parameters::actual::Root as Config; use nonzero_ext::nonzero; use test_network::*; +use test_samples::ALICE_ID; #[ignore = "ignore, more in #2851"] #[test] @@ -18,7 +19,7 @@ fn client_has_rejected_and_acepted_txs_should_return_tx_history() -> Result<()> let pipeline_time = Config::pipeline_time(); // Given - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); diff --git a/client/tests/integration/tx_rollback.rs b/client/tests/integration/tx_rollback.rs index 181e0242cd2..a5cc76ad759 100644 --- a/client/tests/integration/tx_rollback.rs +++ b/client/tests/integration/tx_rollback.rs @@ -6,6 +6,7 @@ use iroha_client::{ data_model::prelude::*, }; use test_network::*; +use test_samples::ALICE_ID; #[test] fn client_sends_transaction_with_invalid_instruction_should_not_see_any_changes() -> Result<()> { @@ -13,7 +14,7 @@ fn client_sends_transaction_with_invalid_instruction_should_not_see_any_changes( wait_for_genesis_committed(&[client.clone()], 0); //When - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland")?; let wrong_asset_definition_id = AssetDefinitionId::from_str("ksor#wonderland")?; let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id)); diff --git a/client/tests/integration/upgrade.rs b/client/tests/integration/upgrade.rs index 3d4106c79f5..817b9b4b6d4 100644 --- a/client/tests/integration/upgrade.rs +++ b/client/tests/integration/upgrade.rs @@ -3,35 +3,45 @@ use std::{path::Path, str::FromStr as _}; use eyre::Result; use iroha_client::{ client::{self, Client, QueryResult}, - crypto::KeyPair, data_model::prelude::*, }; +use iroha_crypto::{Algorithm, KeyPair, PrivateKey}; use iroha_logger::info; use serde_json::json; use test_network::*; +use test_samples::ALICE_ID; + +const ADMIN_ID: &str = + "ed012076E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC@admin"; +const ADMIN_SECRET: &str = "A4DE33BCA99A254ED6265D1F0FB69DFE42B77F89F6C2E478498E1831BF6D81F276E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC"; #[test] fn executor_upgrade_should_work() -> Result<()> { let chain_id = ChainId::from("0"); + let admin_id: AccountId = ADMIN_ID.parse().unwrap(); + let admin_keypair = KeyPair::new( + admin_id.signatory().clone(), + PrivateKey::from_hex(Algorithm::Ed25519, ADMIN_SECRET).unwrap(), + ) + .unwrap(); let (_rt, _peer, client) = ::new().with_port(10_795).start_with_runtime(); wait_for_genesis_committed(&vec![client.clone()], 0); // Register `admin` domain and account - let admin_domain = Domain::new("admin".parse()?); + let admin_domain = Domain::new(admin_id.domain_id().clone()); let register_admin_domain = Register::domain(admin_domain); client.submit_blocking(register_admin_domain)?; - let admin_id: AccountId = "admin@admin".parse()?; - let admin_keypair = KeyPair::random(); - let admin_account = Account::new(admin_id.clone(), admin_keypair.public_key().clone()); + let admin_account = Account::new(admin_id.clone()); let register_admin_account = Register::account(admin_account); client.submit_blocking(register_admin_account)?; // Check that admin isn't allowed to transfer alice's rose by default - let alice_rose: AssetId = "rose##alice@wonderland".parse()?; - let admin_rose: AccountId = "admin@admin".parse()?; - let transfer_alice_rose = Transfer::asset_numeric(alice_rose, 1u32, admin_rose); + let alice_rose: AssetId = format!("rose##{}", ALICE_ID.clone()) + .parse() + .expect("should be valid"); + let transfer_alice_rose = Transfer::asset_numeric(alice_rose, 1u32, admin_id.clone()); let transfer_rose_tx = TransactionBuilder::new(chain_id.clone(), admin_id.clone()) .with_instructions([transfer_alice_rose.clone()]) .sign(&admin_keypair); @@ -46,7 +56,7 @@ fn executor_upgrade_should_work() -> Result<()> { // Check that admin can transfer alice's rose now // Creating new transaction instead of cloning, because we need to update it's creation time - let transfer_rose_tx = TransactionBuilder::new(chain_id, admin_id) + let transfer_rose_tx = TransactionBuilder::new(chain_id, admin_id.clone()) .with_instructions([transfer_alice_rose]) .sign(&admin_keypair); client @@ -71,7 +81,7 @@ fn executor_upgrade_should_run_migration() -> Result<()> { .any(|id| id == &can_unregister_domain_token_id)); // Check that Alice has permission to unregister Wonderland - let alice_id: AccountId = "alice@wonderland".parse().unwrap(); + let alice_id = ALICE_ID.clone(); let alice_tokens = client .request(FindPermissionTokensByAccountId::new(alice_id.clone()))? .collect::>>() @@ -147,7 +157,7 @@ fn executor_upgrade_should_revoke_removed_permissions() -> Result<()> { .contains(&can_unregister_domain_token)); // Check that Alice has permission - let alice_id: AccountId = "alice@wonderland".parse()?; + let alice_id = ALICE_ID.clone(); assert!(client .request(FindPermissionTokensByAccountId::new(alice_id.clone()))? .collect::>>()? diff --git a/client_cli/README.md b/client_cli/README.md index 6cb5dfbc644..9cb7fdf4d29 100644 --- a/client_cli/README.md +++ b/client_cli/README.md @@ -60,20 +60,27 @@ Check the [Bash guide in Iroha Tutorial](https://hyperledger.github.io/iroha-2-d ```bash ./iroha_client_cli domain register --id="Soramitsu" -./iroha_client_cli account register --id="White Rabbit@Soramitsu" --key="" +./iroha_client_cli account register --id="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" ./iroha_client_cli asset register --id="XOR#Soramitsu" --value-type=Numeric -./iroha_client_cli asset mint --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 -./iroha_client_cli asset get --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" +./iroha_client_cli asset mint --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 +./iroha_client_cli asset get --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" ``` In this section we will show you how to use Iroha CLI Client to do the following: -- [Create new Domain](#create-new-domain) -- [Create new Account](#create-new-account) -- [Mint Asset to Account](#mint-asset-to-account) -- [Query Account Assets Quantity](#query-account-assets-quantity) -- [Execute WASM transaction](#execute-wasm-transaction) -- [Execute Multi-instruction Transactions](#execute-multi-instruction-instructions) +- [Iroha CLI Client](#iroha-cli-client) + - [Features](#features) + - [Installation](#installation) + - [Usage](#usage) + - [Options](#options) + - [Subcommands](#subcommands) + - [Examples](#examples) + - [Create new Domain](#create-new-domain) + - [Create new Account](#create-new-account) + - [Mint Asset to Account](#mint-asset-to-account) + - [Query Account Assets Quantity](#query-account-assets-quantity) + - [Execute WASM transaction](#execute-wasm-transaction) + - [Execute Multi-instruction Transactions](#execute-multi-instruction-transactions) ### Create new Domain @@ -89,12 +96,10 @@ Now you have a domain without any accounts. ### Create new Account -Let's create a new account. Like in the previous example, specify the entity type (`account`) and the command (`register`). Then define the account name as the value of the `id` argument. - -Additionally, you need to provide the `key` argument with the account's public key as a double-quoted multihash representation of the key. Providing an empty string also works (but is highly discouraged), while omitting the argument altogether will produce an error. +Let's create a new account. Like in the previous example, specify the entity type (`account`) and the command (`register`). Then define the value of the `id` argument in "signatory@domain" format, where signatory is the account's public key in multihash representation. ```bash -./iroha_client_cli account register --id="White Rabbit@Soramitsu" --key="" +./iroha_client_cli account register --id="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" ``` ### Mint Asset to Account @@ -107,10 +112,10 @@ Every asset has its own value spec. In this example, it is defined as `Numeric`, ```bash ./iroha_client_cli asset register --id="XOR#Soramitsu" --value-type=Numeric -./iroha_client_cli asset mint --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 +./iroha_client_cli asset mint --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 ``` -You created `XOR#Soramitsu`, an asset of type `Numeric`, and then gave `1010` units of this asset to the account `White Rabbit@Soramitsu`. +You created `XOR#Soramitsu`, an asset of type `Numeric`, and then gave `1010` units of this asset to the account `ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu`. ### Query Account Assets Quantity @@ -128,10 +133,10 @@ Let's use Get Account Assets Query as an example. To know how many units of a particular asset an account has, use `asset get` with the specified account and asset: ```bash -./iroha_client_cli asset get --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" +./iroha_client_cli asset get --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" ``` -This query returns the quantity of `XOR#Soramitsu` asset for the `White Rabbit@Soramitsu` account. +This query returns the quantity of `XOR#Soramitsu` asset for the `ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu` account. It's possible to filter based on either account, asset or domain id by using the filtering API provided by the Iroha client CLI. diff --git a/client_cli/pytests/README.md b/client_cli/pytests/README.md index f702ab4f6ab..6aefb69e900 100644 --- a/client_cli/pytests/README.md +++ b/client_cli/pytests/README.md @@ -4,14 +4,15 @@ This directory contains the `pytest` framework with test suites for the Iroha 2 For quick access to a topic that interests you, select one of the following: -- [Framework Structure](#framework-structure) -- [Iroha 2 Test Model](#iroha-2-test-model) -- [Using Test Suites](#using-test-suites) - - [Custom Test Environment with Docker Compose](#custom-test-environment-with-docker-compose) - - [Poetry Configuration](#poetry-configuration) - - [Tests Configuration](#tests-configuration) -- [Running Tests](#running-tests) -- [Viewing Test Reports](#viewing-test-reports) +- [Overview](#overview) + - [Framework Structure](#framework-structure) + - [Iroha 2 Test Model](#iroha-2-test-model) + - [Using Test Suites](#using-test-suites) + - [Custom Test Environment with Docker Compose](#custom-test-environment-with-docker-compose) + - [Poetry Configuration](#poetry-configuration) + - [Tests Configuration](#tests-configuration) + - [Running Tests](#running-tests) + - [Viewing Test Reports](#viewing-test-reports) ## Framework Structure @@ -105,20 +106,20 @@ To do so, perform the following steps: cargo build --bin iroha_client_cli ``` -3. Create a new directory, then copy the `iroha_client_cli` binary and its `config.json` configuration file into it: +3. Create a new directory, then copy the `iroha_client_cli` binary and its `client.toml` configuration file into it: ```shell # Create a new directory: - mkdir test_client + mkdir test # Copy the files: - cp configs/client/config.json test_client - cp target/debug/iroha_client_cli test_client + cp configs/swarm/client.toml test + cp target/debug/iroha_client_cli test ``` 4. Proceed with _Step 2_ of the [Using Test Suites](#using-test-suites) instructions. > [!NOTE] -> Don't forget to specify the path to the directory created for the `iroha_client_cli` binary and its `config.json` configuration file (see Step 3) in the `CLIENT_CLI_DIR` variable of the `.env` file. +> Don't forget to specify the path to the directory created for the `iroha_client_cli` binary and its `client.toml` configuration file (see Step 3) in the `CLIENT_CLI_DIR` variable of the `.env` file. > For details, see [Tests Configuration](#tests-configuration) below. ### Poetry Configuration diff --git a/client_cli/pytests/common/consts.py b/client_cli/pytests/common/consts.py index fadc52223ae..f1221c655b6 100644 --- a/client_cli/pytests/common/consts.py +++ b/client_cli/pytests/common/consts.py @@ -15,11 +15,11 @@ class Stderr(Enum): Enum for standard error messages. """ - CANNOT_BE_EMPTY = "cannot be empty\n\nFor more information, try '--help'.\n" + EMPTY = "mpty" REPETITION = "Repetition" TOO_LONG = "Name length violation" FAILED_TO_FIND_DOMAIN = "Failed to find domain" - INVALID_CHARACTER = "Invalid character" + INVALID_CHARACTER = "Failed to parse" INVALID_VALUE_TYPE = "should be either `Store` or `Numeric`" RESERVED_CHARACTER = ( "The `@` character is reserved for `account@domain` constructs," diff --git a/client_cli/pytests/common/helpers.py b/client_cli/pytests/common/helpers.py index 6145ea35557..aa55466ec9c 100644 --- a/client_cli/pytests/common/helpers.py +++ b/client_cli/pytests/common/helpers.py @@ -19,7 +19,7 @@ def extract_hash(stdout): """ Extracts a SHA-256 hash from the given string. - :param stdout: The string from which to extract the hash. + :param stdout: The string from which to extract the hash. :return: The extracted hash if found, otherwise None. """ if not isinstance(stdout, str) or not stdout.strip(): @@ -98,7 +98,7 @@ def generate_public_key(): encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw ) ).decode() - return public_key + return "ed0120" + public_key.upper() def generate_random_string(length, allowed_chars): diff --git a/client_cli/pytests/common/json_isi_examples/unregister_asset.json b/client_cli/pytests/common/json_isi_examples/unregister_asset.json index ac3d7f314f9..837355581fb 100644 --- a/client_cli/pytests/common/json_isi_examples/unregister_asset.json +++ b/client_cli/pytests/common/json_isi_examples/unregister_asset.json @@ -1,7 +1,7 @@ [{ "Unregister": { "Asset": { - "object_id": "rose#alice@wonderland" + "object_id": "rose#ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } -}] \ No newline at end of file +}] diff --git a/client_cli/pytests/models/account.py b/client_cli/pytests/models/account.py index 7f3acb01d67..7e15d614919 100644 --- a/client_cli/pytests/models/account.py +++ b/client_cli/pytests/models/account.py @@ -10,17 +10,14 @@ class Account: """ Account class represents an Iroha account. - :param name: The name of the account. - :type name: str + :param signatory: The signatory of the account. + :type signatory: str :param domain: The domain of the account. :type domain: str - :param public_key: The public key of the account. - :type public_key: str """ - name: str + signatory: str domain: str - public_key: str = "" def __repr__(self): - return f"{self.name}@{self.domain}" + return f"{self.signatory}@{self.domain}" diff --git a/client_cli/pytests/src/client_cli/client_cli.py b/client_cli/pytests/src/client_cli/client_cli.py index fd751c77f65..c78373fa2cf 100644 --- a/client_cli/pytests/src/client_cli/client_cli.py +++ b/client_cli/pytests/src/client_cli/client_cli.py @@ -134,22 +134,19 @@ def domain(self, domain: str): self.execute() return self - def account(self, account: str, domain: str, key: str): + def account(self, signatory: str, domain: str): """ - Executes the 'account' command for the given account, domain, and key. + Executes the 'account' command for the given signatory and domain. - :param account: The account to be queried. - :type account: str + :param signatory: The signatory of the account. + :type signatory: str :param domain: The domain of the account. :type domain: str - :param key: The key for the account. - :type key: str :return: The current ClientCli object. :rtype: ClientCli """ self.command.insert(2, "account") - self.command.append("--id=" + account + "@" + domain) - self.command.append("--key=ed0120" + key) + self.command.append("--id=" + signatory + "@" + domain) self.execute() return self @@ -172,11 +169,11 @@ def asset(self, asset_definition=None, account=None, value_of_value_type=None): "--asset-id=" + asset_definition.name + "#" - + account.domain + + asset_definition.domain + "#" - + account.name + + account.signatory + "@" - + asset_definition.domain + + account.domain ) self.command.append("--quantity=" + value_of_value_type) self.execute() @@ -204,11 +201,11 @@ def transfer(self, asset, source_account, target_account, quantity: str): "--asset-id=" + asset.name + "#" - + source_account.domain + + asset.domain + "#" - + source_account.name + + source_account.signatory + "@" - + asset.domain + + source_account.domain ) self.command.append("--quantity=" + quantity) self.execute() @@ -232,11 +229,11 @@ def burn(self, account, asset, quantity: str): "--asset-id=" + asset.name + "#" - + account.domain + + asset.domain + "#" - + account.name + + account.signatory + "@" - + asset.domain + + account.domain ) self.command.append("--quantity=" + quantity) self.execute() diff --git a/client_cli/pytests/src/client_cli/configuration.py b/client_cli/pytests/src/client_cli/configuration.py index 5e04cdfbecd..897eec066f8 100644 --- a/client_cli/pytests/src/client_cli/configuration.py +++ b/client_cli/pytests/src/client_cli/configuration.py @@ -122,42 +122,22 @@ def env(self): # return self._config['TORII_API_URL' return {**os.environ, **self._envs} - @property - def account_id(self): - """ - Get the ACCOUNT_ID configuration value. - - :return: The ACCOUNT_ID. - :rtype: str - """ - return self._config["account"]["id"] - - @property - def account_name(self): - """ - Get the account name from the ACCOUNT_ID configuration value. - - :return: The account name. - :rtype: str - """ - return self.account_id.split("@")[0] - @property def account_domain(self): """ - Get the account domain from the ACCOUNT_ID configuration value. + Get the ACCOUNT_DOMAIN configuration value. :return: The account domain. :rtype: str """ - return self.account_id.split("@")[1] + return self._config["account"]["domain_id"] @property - def public_key(self): + def account_signatory(self): """ Get the PUBLIC_KEY configuration value. - :return: The public key. + :return: The account signatory. :rtype: str """ - return self._config["account"]["public_key"].split("ed0120")[1] + return self._config["account"]["public_key"] diff --git a/client_cli/pytests/src/client_cli/iroha.py b/client_cli/pytests/src/client_cli/iroha.py index 8c35810c21a..7bfa0ba3af6 100644 --- a/client_cli/pytests/src/client_cli/iroha.py +++ b/client_cli/pytests/src/client_cli/iroha.py @@ -87,7 +87,7 @@ def assets(self) -> Dict[str, str]: Retrieve assets from the Iroha network and return them as a dictionary where the keys are asset ids and the values are the corresponding asset objects. - :return: Dictionary of assets. + :return: Dictionary of assets. :rtype: Dict[str, Any] """ self._execute_command("asset") diff --git a/client_cli/pytests/test/__init__.py b/client_cli/pytests/test/__init__.py index ab75290e556..1ca1a357dae 100644 --- a/client_cli/pytests/test/__init__.py +++ b/client_cli/pytests/test/__init__.py @@ -3,7 +3,6 @@ """ from .conftest import ( - GIVEN_127_length_name, GIVEN_128_length_name, GIVEN_129_length_name, GIVEN_currently_account_quantity_with_two_quantity_of_asset, diff --git a/client_cli/pytests/test/accounts/conftest.py b/client_cli/pytests/test/accounts/conftest.py index 7329c22d45f..f92932bfcb9 100644 --- a/client_cli/pytests/test/accounts/conftest.py +++ b/client_cli/pytests/test/accounts/conftest.py @@ -1,5 +1,4 @@ from test import ( - GIVEN_127_length_name, GIVEN_129_length_name, GIVEN_fake_name, GIVEN_key_with_invalid_character_in_key, diff --git a/client_cli/pytests/test/accounts/test_accounts_query_filters.py b/client_cli/pytests/test/accounts/test_accounts_query_filters.py index bb13afe52bf..6ff50159468 100644 --- a/client_cli/pytests/test/accounts/test_accounts_query_filters.py +++ b/client_cli/pytests/test/accounts/test_accounts_query_filters.py @@ -26,28 +26,10 @@ def condition(): client_cli.wait_for(condition) -def test_filter_by_account_name(GIVEN_registered_account): - def condition(): - name = GIVEN_registered_account.name - with allure.step(f'WHEN client_cli query accounts with name "{name}"'): - accounts = iroha.list_filter( - {"Identifiable": {"StartsWith": f"{name}@"}} - ).accounts() - with allure.step("THEN Iroha should return only accounts with this name"): - allure.attach( - json.dumps(accounts), - name="accounts", - attachment_type=allure.attachment_type.JSON, - ) - return accounts and all(account.startswith(name) for account in accounts) - - client_cli.wait_for(condition) - - def test_filter_by_account_id(GIVEN_registered_account): def condition(): account_id = ( - GIVEN_registered_account.name + "@" + GIVEN_registered_account.domain + GIVEN_registered_account.signatory + "@" + GIVEN_registered_account.domain ) with allure.step( f'WHEN client_cli query accounts with account id "{account_id}"' diff --git a/client_cli/pytests/test/accounts/test_register_accounts.py b/client_cli/pytests/test/accounts/test_register_accounts.py index 17b82efa853..4c0dec1b293 100644 --- a/client_cli/pytests/test/accounts/test_register_accounts.py +++ b/client_cli/pytests/test/accounts/test_register_accounts.py @@ -12,67 +12,39 @@ def story_account_register_account(): @allure.label("sdk_test_id", "register_account") -def test_register_account(GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key): +def test_register_account(GIVEN_public_key, GIVEN_registered_domain): with allure.step( - f'WHEN client_cli registers the account "{GIVEN_fake_name}" ' + f'WHEN client_cli registers the account "{GIVEN_public_key}" ' f'in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_public_key, domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, ) - registered = GIVEN_fake_name + "@" + GIVEN_registered_domain.name + registered = GIVEN_public_key + "@" + GIVEN_registered_domain.name with allure.step(f'THEN Iroha should have the "{registered}" account'): iroha.should(have.account(registered)) -@allure.label("sdk_test_id", "register_account_with_two_public_keys") -@pytest.mark.xfail(reason="TO DO") -def test_register_account_with_two_public_keys( - GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key -): - assert 0 - - -@allure.label("sdk_test_id", "register_account_with_empty_name") -def test_register_account_with_empty_name(GIVEN_registered_domain, GIVEN_public_key): - with allure.step( - f"WHEN client_cli tries to register an account with an empty name " - f'in the "{GIVEN_registered_domain.name}" domain' - ): - client_cli.register().account( - account="", domain=GIVEN_registered_domain.name, key=GIVEN_public_key - ) - with allure.step( - f'THEN сlient_cli should have the account error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) - - -@allure.label("sdk_test_id", "register_account_with_existing_name") -def test_register_account_with_existing_name( - GIVEN_registered_domain, GIVEN_public_key, GIVEN_registered_account +@allure.label("sdk_test_id", "register_account_with_existing_signatory") +def test_register_account_with_existing_signatory( + GIVEN_registered_domain, GIVEN_registered_account ): with allure.step( f"WHEN client_cli tries to register an account " - f'with the same name "{GIVEN_registered_domain.name}" ' + f'with the same signatory "{GIVEN_registered_account.signatory}" ' f'in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=GIVEN_registered_account.name, + signatory=GIVEN_registered_account.signatory, domain=GIVEN_registered_account.domain, - key=GIVEN_registered_account.public_key, ) - with allure.step( - f'THEN client_cli should have the account error: "{GIVEN_registered_domain.name}"' - ): + with allure.step("THEN client_cli should have the account error"): client_cli.should(have.error(Stderr.REPETITION.value)) @allure.label("sdk_test_id", "register_account_with_invalid_domain") def test_register_account_with_invalid_domain( - GIVEN_fake_name, GIVEN_not_existing_name, GIVEN_public_key, ): @@ -80,9 +52,8 @@ def test_register_account_with_invalid_domain( "WHEN client_cli tries to register an account with an invalid domain" ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_public_key, domain=GIVEN_not_existing_name, - key=GIVEN_public_key, ) with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.FAILED_TO_FIND_DOMAIN.value)) @@ -90,61 +61,19 @@ def test_register_account_with_invalid_domain( @allure.label("sdk_test_id", "register_account_with_invalid_character_in_key") def test_register_account_with_invalid_character_in_key( - GIVEN_fake_name, GIVEN_registered_domain, GIVEN_key_with_invalid_character_in_key + GIVEN_registered_domain, GIVEN_key_with_invalid_character_in_key ): with allure.step( "WHEN client_cli tries to register an account with invalid character in the key" ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_key_with_invalid_character_in_key, domain=GIVEN_registered_domain.name, - key=GIVEN_key_with_invalid_character_in_key, ) with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.INVALID_CHARACTER.value)) -@allure.label("sdk_test_id", "register_account_with_max_name") -def test_register_account_with_max_name( - GIVEN_127_length_name, GIVEN_registered_domain, GIVEN_public_key -): - with allure.step("WHEN client_cli register an account with the 127 length name"): - client_cli.register().account( - account=GIVEN_127_length_name, - domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, - ) - registered = GIVEN_127_length_name + "@" + GIVEN_registered_domain.name - with allure.step(f'THEN Iroha should have the "{registered}" account'): - iroha.should(have.account(registered)) - - -@allure.label("sdk_test_id", "register_account_with_special_characters") -@pytest.mark.xfail(reason="TO DO") -def test_register_account_with_special_characters( - GIVEN_registered_domain, GIVEN_public_key -): - assert 0 - - -@allure.label("sdk_test_id", "register_account_with_long_account_name") -def test_register_account_with_long_account_name( - GIVEN_registered_domain, GIVEN_129_length_name, GIVEN_public_key -): - with allure.step( - "WHEN client_cli tries to register an account with a name with 129 characters" - ): - client_cli.register().account( - account=GIVEN_129_length_name, - domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, - ) - with allure.step( - f'THEN client_cli should have the name error: "{Stderr.TOO_LONG}"' - ): - client_cli.should(have.error(Stderr.TOO_LONG.value)) - - @allure.label("sdk_test_id", "register_account_with_metadata") @pytest.mark.xfail(reason="TO DO") def test_register_account_with_metadata( diff --git a/client_cli/pytests/test/assets/test_assets_query_filters.py b/client_cli/pytests/test/assets/test_assets_query_filters.py index 1a44e6e8683..918a255079d 100644 --- a/client_cli/pytests/test/assets/test_assets_query_filters.py +++ b/client_cli/pytests/test/assets/test_assets_query_filters.py @@ -69,7 +69,7 @@ def condition(): asset_id = ( GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name + "##" - + GIVEN_currently_authorized_account.name + + GIVEN_currently_authorized_account.signatory + "@" + GIVEN_currently_authorized_account.domain ) diff --git a/client_cli/pytests/test/assets/test_burn_assets.py b/client_cli/pytests/test/assets/test_burn_assets.py index fdbc9a40864..b7d22f3ab3b 100644 --- a/client_cli/pytests/test/assets/test_burn_assets.py +++ b/client_cli/pytests/test/assets/test_burn_assets.py @@ -16,7 +16,7 @@ def test_burn_asset_for_account_in_same_domain( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} burns 1" + f"WHEN {GIVEN_currently_authorized_account.signatory} burns 1" f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" ): client_cli.burn( @@ -25,7 +25,7 @@ def test_burn_asset_for_account_in_same_domain( quantity="1", ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} " + f"THEN {GIVEN_currently_authorized_account.signatory} " f"has 1 of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" ): iroha.should( diff --git a/client_cli/pytests/test/assets/test_mint_assets.py b/client_cli/pytests/test/assets/test_mint_assets.py index 869049434e4..ef83e794df0 100644 --- a/client_cli/pytests/test/assets/test_mint_assets.py +++ b/client_cli/pytests/test/assets/test_mint_assets.py @@ -16,9 +16,9 @@ def test_mint_asset_for_account_in_same_domain( GIVEN_numeric_value, ): with allure.step( - f'WHEN client_cli mint the asset "{GIVEN_registered_asset_definition_with_numeric_value_type.name}" ' - f'for the "{GIVEN_registered_account.name}" ' - f'in the "{GIVEN_registered_asset_definition_with_numeric_value_type.domain}" domain' + f'WHEN client_cli mint "{GIVEN_numeric_value}" of ' + f'"{GIVEN_registered_asset_definition_with_numeric_value_type}" ' + f'for the "{GIVEN_registered_account}"' ): client_cli.mint().asset( account=GIVEN_registered_account, @@ -26,8 +26,9 @@ def test_mint_asset_for_account_in_same_domain( value_of_value_type=GIVEN_numeric_value, ) with allure.step( - f'THEN "{GIVEN_registered_account}" account ' - f'should have the "{GIVEN_registered_asset_definition_with_numeric_value_type}" asset definition' + f'THEN "{GIVEN_registered_account}" ' + f'should have the "{GIVEN_numeric_value}" of ' + f'"{GIVEN_registered_asset_definition_with_numeric_value_type}"' ): iroha.should( have.asset( @@ -59,8 +60,8 @@ def test_mint_asset_quantity_after_minting(GIVEN_minted_asset_quantity): expected_quantity = int(GIVEN_minted_asset_quantity.value) + 1 with allure.step( f'THEN "{GIVEN_minted_asset_quantity.account}" account ' - f'should have the "{expected_quantity}" asset definition ' - f"with updated quantity" + f'should have the "{GIVEN_minted_asset_quantity.definition}" asset ' + f'with updated quantity "{expected_quantity}"' ): iroha.should( have.asset_has_quantity( diff --git a/client_cli/pytests/test/assets/test_register_asset_definitions.py b/client_cli/pytests/test/assets/test_register_asset_definitions.py index 9110cd3bb16..03233ccc373 100644 --- a/client_cli/pytests/test/assets/test_register_asset_definitions.py +++ b/client_cli/pytests/test/assets/test_register_asset_definitions.py @@ -119,10 +119,8 @@ def test_register_asset_with_empty_name(GIVEN_registered_domain): client_cli.register().asset().definition( asset="", domain=GIVEN_registered_domain.name, value_type="Numeric" ) - with allure.step( - f'THEN сlient_cli should have the asset error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) + with allure.step(f'THEN сlient_cli should have the asset error: "{Stderr.EMPTY}"'): + client_cli.should(have.error(Stderr.EMPTY.value)) @allure.label("sdk_test_id", "register_asset_with_not_existing_domain") diff --git a/client_cli/pytests/test/assets/test_transfer_assets.py b/client_cli/pytests/test/assets/test_transfer_assets.py index f78a2da2daa..4e842185c58 100644 --- a/client_cli/pytests/test/assets/test_transfer_assets.py +++ b/client_cli/pytests/test/assets/test_transfer_assets.py @@ -18,9 +18,9 @@ def test_transfer_asset( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} transfers 1 Quantity" + f"WHEN {GIVEN_currently_authorized_account.signatory} transfers 1 Quantity" f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"to {GIVEN_registered_account.name}" + f"to {GIVEN_registered_account.signatory}" ): client_cli.transfer( asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, @@ -30,7 +30,7 @@ def test_transfer_asset( ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} has 1 Quantity " + f"THEN {GIVEN_currently_authorized_account.signatory} has 1 Quantity " f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" f"AND {GIVEN_registered_account} has 1 more Quantity" ): @@ -51,9 +51,9 @@ def test_transfer_with_insufficient_funds( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} attempts to transfer more than available Quantity" - f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"to {GIVEN_registered_account.name}" + f"WHEN {GIVEN_currently_authorized_account.signatory} attempts to transfer more than available " + f"Quantity of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + f"to {GIVEN_registered_account.signatory}" ): client_cli.transfer( asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, @@ -65,9 +65,9 @@ def test_transfer_with_insufficient_funds( ), ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} still has the original Quantity " + f"THEN {GIVEN_currently_authorized_account.signatory} still has the original Quantity " f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"AND {GIVEN_registered_account.name} does not receive any additional Quantity" + f"AND {GIVEN_registered_account.signatory} does not receive any additional Quantity" ): client_cli.should(have.error(Stderr.INSUFFICIENT_FUNDS.value)) @@ -82,8 +82,8 @@ def test_exchange_asset_through_intermediary( GIVEN_buyer_account_with_eth, ): # with allure.step(f'WHEN {GIVEN_intermediary_with_transfer_permission.name}' - # f'exchanges BTC from {GIVEN_seller_account_with_btc.name}' - # f'with ETH from {GIVEN_buyer_account_with_eth.name}'): + # f'exchanges BTC from {GIVEN_seller_account_with_btc.signatory}' + # f'with ETH from {GIVEN_buyer_account_with_eth.signatory}'): # client_cli.exchange_assets( # intermediary_account=GIVEN_intermediary_with_transfer_permission, # seller_account=GIVEN_seller_account_with_btc, @@ -91,8 +91,8 @@ def test_exchange_asset_through_intermediary( # btc_quantity="1", # eth_quantity="10") # - # with allure.step(f'THEN {GIVEN_seller_account_with_btc.name} receives ETH ' - # f'AND {GIVEN_buyer_account_with_eth.name} receives BTC'): + # with allure.step(f'THEN {GIVEN_seller_account_with_btc.signatory} receives ETH ' + # f'AND {GIVEN_buyer_account_with_eth.signatory} receives BTC'): # iroha.should(have.asset( # f'eth#{GIVEN_seller_account_with_btc.domain}', quantity="10")) # iroha.should(have.asset( diff --git a/client_cli/pytests/test/assets/test_unregister_asset.py b/client_cli/pytests/test/assets/test_unregister_asset.py index 997240e516a..bd48a79e0c2 100644 --- a/client_cli/pytests/test/assets/test_unregister_asset.py +++ b/client_cli/pytests/test/assets/test_unregister_asset.py @@ -12,7 +12,11 @@ def story_account_unregisters_asset(): @allure.label("sdk_test_id", "unregister_asset") @pytest.mark.parametrize( - "GIVEN_numeric_asset_for_account", ["alice@wonderland"], indirect=True + "GIVEN_numeric_asset_for_account", + [ + "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + ], + indirect=True, ) @pytest.mark.xfail(reason="wait for #4039") def test_unregister_asset( diff --git a/client_cli/pytests/test/conftest.py b/client_cli/pytests/test/conftest.py index fbfb96ee4b8..364456252ff 100644 --- a/client_cli/pytests/test/conftest.py +++ b/client_cli/pytests/test/conftest.py @@ -67,15 +67,12 @@ def GIVEN_registered_domain_with_uppercase_letter(GIVEN_registered_domain): @pytest.fixture() def GIVEN_registered_account(GIVEN_registered_domain, GIVEN_public_key): """Fixture to create an account.""" - name = fake_name() - account = Account( - name=name, domain=GIVEN_registered_domain.name, public_key=GIVEN_public_key - ) + account = Account(signatory=GIVEN_public_key, domain=GIVEN_registered_domain.name) with allure.step( - f'GIVEN the account "{name}" in the "{GIVEN_registered_domain.name}" domain' + f'GIVEN the account "{GIVEN_public_key}" in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=account.name, domain=account.domain, key=account.public_key + signatory=account.signatory, domain=account.domain ) return account @@ -84,12 +81,11 @@ def GIVEN_registered_account(GIVEN_registered_domain, GIVEN_public_key): def GIVEN_currently_authorized_account(): """Fixture to get the currently authorized account.""" account: Account = Account( - name=config.account_name, + signatory=config.account_signatory, domain=config.account_domain, - public_key=config.public_key, ) with allure.step( - f'GIVEN the currently authorized account "{account.name}" ' + f'GIVEN the currently authorized account "{account.signatory}" ' f'in the "{account.domain}" domain' ): return account @@ -106,7 +102,7 @@ def GIVEN_currently_account_quantity_with_two_quantity_of_asset( value_type=GIVEN_numeric_value_type, ) asset = Asset( - definition=asset_def, value="2", account=GIVEN_currently_authorized_account.name + definition=asset_def, value="2", account=GIVEN_currently_authorized_account ) name = fake_name() with allure.step( @@ -132,12 +128,14 @@ def GIVEN_numeric_asset_for_account( ): """Fixture to get an asset for a given account and domain with specified quantity.""" account, domain = request.param.split("@") - account = Account(name=account, domain=domain) + account = Account(signatory=account, domain=domain) asset_def = AssetDefinition( name=GIVEN_fake_asset_name, domain=domain, value_type=GIVEN_numeric_value_type ) - asset = Asset(definition=asset_def, value=GIVEN_numeric_value, account=account.name) + asset = Asset( + definition=asset_def, value=GIVEN_numeric_value, account=account.signatory + ) with allure.step( f'GIVEN the asset_definition "{asset_def.name}" ' f'in the "{domain}" domain' @@ -328,13 +326,6 @@ def GIVEN_129_length_name(): return ident -@pytest.fixture() -def GIVEN_127_length_name(): - ident = generate_random_string_without_reserved_chars(127) - with allure.step(f'GIVEN a name with 127 length "{ident}"'): - return ident - - @pytest.fixture() def GIVEN_string_with_reserved_character(): """Fixture to provide a random string with reserved characters.""" diff --git a/client_cli/pytests/test/domains/test_register_domains.py b/client_cli/pytests/test/domains/test_register_domains.py index d369aba3b89..7f01073934e 100644 --- a/client_cli/pytests/test/domains/test_register_domains.py +++ b/client_cli/pytests/test/domains/test_register_domains.py @@ -25,10 +25,8 @@ def test_register_empty_domain( ): with allure.step("WHEN client_cli registers an empty domain"): client_cli.register().domain("") - with allure.step( - f'THEN client_cli should have the domain error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) + with allure.step(f'THEN client_cli should have the domain error: "{Stderr.EMPTY}"'): + client_cli.should(have.error(Stderr.EMPTY.value)) @allure.label("sdk_test_id", "register_existing_domain") @@ -38,7 +36,7 @@ def test_register_existing_domain(GIVEN_registered_domain): ): client_cli.register().domain(GIVEN_registered_domain.name) with allure.step( - f'THEN client_cli should have the domain error: "{GIVEN_registered_domain.name}"' + f'THEN client_cli should have the domain error: "{GIVEN_registered_domain.name}"' ): client_cli.should(have.error(Stderr.REPETITION.value)) diff --git a/client_cli/pytests/test/triggers/test_register_trigger.py b/client_cli/pytests/test/triggers/test_register_trigger.py index e1c0e5169a2..87ee784b9e1 100644 --- a/client_cli/pytests/test/triggers/test_register_trigger.py +++ b/client_cli/pytests/test/triggers/test_register_trigger.py @@ -18,6 +18,12 @@ def test_register_trigger(GIVEN_currently_authorized_account): ): client_cli.register_trigger(GIVEN_currently_authorized_account) with allure.step( - "THEN Iroha should have the asset with nft_number_1_for_genesis##genesis@genesis" + "THEN Iroha should have the asset with nft_number_1_for_genesis##\ + ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B@genesis" ): - iroha.should(have.asset("nft_number_1_for_genesis##genesis@genesis")) + iroha.should( + have.asset( + "nft_number_1_for_genesis##\ + ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B@genesis" + ) + ) diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index 666f6902c91..1cfa5487f2d 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -504,9 +504,6 @@ mod account { pub enum Args { /// Register account Register(Register), - /// Set something in account - #[command(subcommand)] - Set(Set), /// List accounts #[command(subcommand)] List(List), @@ -520,7 +517,6 @@ mod account { fn run(self, context: &mut dyn RunContext) -> Result<()> { match_all!((self, context), { Args::Register, - Args::Set, Args::List, Args::Grant, Args::ListPermissions, @@ -534,74 +530,19 @@ mod account { /// Id of account in form `name@domain_name` #[arg(short, long)] pub id: AccountId, - /// Its public key - #[arg(short, long)] - pub key: PublicKey, #[command(flatten)] pub metadata: MetadataArgs, } impl RunArgs for Register { fn run(self, context: &mut dyn RunContext) -> Result<()> { - let Self { id, key, metadata } = self; - let create_account = - iroha_client::data_model::isi::Register::account(Account::new(id, key)); + let Self { id, metadata } = self; + let create_account = iroha_client::data_model::isi::Register::account(Account::new(id)); submit([create_account], metadata.load()?, context) .wrap_err("Failed to register account") } } - /// Set subcommand of account - #[derive(clap::Subcommand, Debug)] - pub enum Set { - /// Signature condition - SignatureCondition(SignatureCondition), - } - - impl RunArgs for Set { - fn run(self, context: &mut dyn RunContext) -> Result<()> { - match_all!((self, context), { Set::SignatureCondition }) - } - } - - #[derive(Debug, Clone)] - pub struct Signature(SignatureCheckCondition); - - impl FromStr for Signature { - type Err = Error; - fn from_str(s: &str) -> Result { - let err_msg = format!("Failed to open the signature condition file {}", &s); - let deser_err_msg = - format!("Failed to deserialize signature condition from file {}", &s); - let content = fs::read_to_string(s).wrap_err(err_msg)?; - let condition: SignatureCheckCondition = - json5::from_str(&content).wrap_err(deser_err_msg)?; - Ok(Self(condition)) - } - } - - /// Set accounts signature condition - #[derive(clap::Args, Debug)] - pub struct SignatureCondition { - /// Signature condition file - pub condition: Signature, - #[command(flatten)] - pub metadata: MetadataArgs, - } - - impl RunArgs for SignatureCondition { - fn run(self, context: &mut dyn RunContext) -> Result<()> { - let account_id = context.configuration().account_id.clone(); - let Self { - condition: Signature(condition), - metadata, - } = self; - let mint_box = Mint::account_signature_check_condition(condition, account_id); - submit([mint_box], metadata.load()?, context) - .wrap_err("Failed to set signature condition") - } - } - /// List accounts with this command #[derive(clap::Subcommand, Debug, Clone)] pub enum List { diff --git a/config/iroha_test_config.toml b/config/iroha_test_config.toml index 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..2523346be05312663bbee55fa3f38a055c88e3ee 100644 GIT binary patch literal 516776 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�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�v?PEg~wEx!ruC^l#=@FpUdtj6x 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�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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..6c9e5a2e7a0 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": "ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B@genesis", + "object": "rose#wonderland", + "destination_id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } }, { "Transfer": { "Domain": { - "source_id": "genesis@genesis", + "source_id": "ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B@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..84324e477fc 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -801,6 +801,7 @@ mod tests { use iroha_crypto::SignatureVerificationFail; use iroha_data_model::prelude::*; use iroha_genesis::{GENESIS_ACCOUNT_ID, GENESIS_DOMAIN_ID}; + use test_samples::gen_account_in; use super::*; use crate::{ @@ -826,10 +827,8 @@ mod tests { let chain_id = ChainId::from("0"); // Predefined world state - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let alice_keys = KeyPair::random(); - let account = - Account::new(alice_id.clone(), alice_keys.public_key().clone()).build(&alice_id); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&alice_id); assert!(domain.add_account(account).is_none()); @@ -848,7 +847,7 @@ mod tests { let transaction_limits = &state_block.transaction_executor().transaction_limits; let tx = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions([create_asset_definition]) - .sign(&alice_keys); + .sign(&alice_keypair); let tx = AcceptedTransaction::accept(tx, &chain_id, transaction_limits).expect("Valid"); // Creating a block of two identical transactions and validating it @@ -856,7 +855,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut state_block) - .sign(&alice_keys) + .sign(&alice_keypair) .unpack(|_| {}); // The first transaction should be confirmed @@ -883,10 +882,8 @@ mod tests { let chain_id = ChainId::from("0"); // Predefined world state - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let alice_keys = KeyPair::random(); - let account = - Account::new(alice_id.clone(), alice_keys.public_key().clone()).build(&alice_id); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&alice_id); assert!(domain.add_account(account).is_none()); @@ -905,7 +902,7 @@ mod tests { let transaction_limits = &state_block.transaction_executor().transaction_limits; let tx = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([create_asset_definition]) - .sign(&alice_keys); + .sign(&alice_keypair); let tx = AcceptedTransaction::accept(tx, &chain_id, transaction_limits).expect("Valid"); let fail_mint = Mint::asset_numeric( @@ -918,12 +915,12 @@ mod tests { let tx0 = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([fail_mint]) - .sign(&alice_keys); + .sign(&alice_keypair); let tx0 = AcceptedTransaction::accept(tx0, &chain_id, transaction_limits).expect("Valid"); let tx2 = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions([succeed_mint]) - .sign(&alice_keys); + .sign(&alice_keypair); let tx2 = AcceptedTransaction::accept(tx2, &chain_id, transaction_limits).expect("Valid"); // Creating a block of two identical transactions and validating it @@ -931,7 +928,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut state_block) - .sign(&alice_keys) + .sign(&alice_keypair) .unpack(|_| {}); // The first transaction should fail @@ -958,10 +955,8 @@ mod tests { let chain_id = ChainId::from("0"); // Predefined world state - let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let alice_keys = KeyPair::random(); - let account = - Account::new(alice_id.clone(), alice_keys.public_key().clone()).build(&alice_id); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&alice_id); assert!( @@ -987,12 +982,12 @@ mod tests { let instructions_accept: [InstructionBox; 2] = [create_domain.into(), create_asset.into()]; let tx_fail = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions(instructions_fail) - .sign(&alice_keys); + .sign(&alice_keypair); let tx_fail = AcceptedTransaction::accept(tx_fail, &chain_id, transaction_limits).expect("Valid"); let tx_accept = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions(instructions_accept) - .sign(&alice_keys); + .sign(&alice_keypair); let tx_accept = AcceptedTransaction::accept(tx_accept, &chain_id, transaction_limits).expect("Valid"); @@ -1001,7 +996,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut state_block) - .sign(&alice_keys) + .sign(&alice_keypair) .unpack(|_| {}); // The first transaction should be rejected @@ -1037,12 +1032,13 @@ mod tests { let genesis_correct_key = KeyPair::random(); let genesis_wrong_key = KeyPair::random(); let mut genesis_domain = Domain::new(GENESIS_DOMAIN_ID.clone()).build(&GENESIS_ACCOUNT_ID); - let genesis_account = Account::new( - GENESIS_ACCOUNT_ID.clone(), - genesis_wrong_key.public_key().clone(), - ) - .build(&GENESIS_ACCOUNT_ID); - assert!(genesis_domain.add_account(genesis_account).is_none(),); + let genesis_wrong_account_id: AccountId = + format!("{}@{}", genesis_wrong_key.public_key(), *GENESIS_DOMAIN_ID) + .parse() + .expect("should be valid"); + let genesis_wrong_account = + Account::new(genesis_wrong_account_id.clone()).build(&genesis_wrong_account_id); + assert!(genesis_domain.add_account(genesis_wrong_account).is_none(),); let world = World::with([genesis_domain], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); @@ -1057,7 +1053,7 @@ mod tests { // Create genesis transaction // Sign with `genesis_wrong_key` as peer which has incorrect genesis key pair - let tx = TransactionBuilder::new(chain_id.clone(), GENESIS_ACCOUNT_ID.clone()) + let tx = TransactionBuilder::new(chain_id.clone(), genesis_wrong_account_id.clone()) .with_instructions([isi]) .sign(&genesis_wrong_key); let tx = AcceptedTransaction::accept_genesis( diff --git a/core/src/lib.rs b/core/src/lib.rs index f9b25a0a831..5a3e1bb4be6 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -171,7 +171,8 @@ pub mod prelude { mod tests { use std::cmp::Ordering; - use iroha_data_model::{account::AccountId, role::RoleId}; + use iroha_data_model::role::RoleId; + use test_samples::gen_account_in; use crate::role::RoleIdWithOwner; @@ -179,8 +180,8 @@ mod tests { fn cmp_role_id_with_owner() { let role_id_a: RoleId = "a".parse().expect("failed to parse RoleId"); let role_id_b: RoleId = "b".parse().expect("failed to parse RoleId"); - let account_id_a: AccountId = "a@domain".parse().expect("failed to parse AccountId"); - let account_id_b: AccountId = "b@domain".parse().expect("failed to parse AccountId"); + let (account_id_a, _account_keypair_a) = gen_account_in("domain"); + let (account_id_b, _account_keypair_b) = gen_account_in("domain"); let mut role_ids_with_owner = Vec::new(); for account_id in [&account_id_a, &account_id_b] { diff --git a/core/src/queue.rs b/core/src/queue.rs index 51e3ad38a5b..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..f2389e9aace 100644 --- a/core/src/smartcontracts/isi/domain.rs +++ b/core/src/smartcontracts/isi/domain.rs @@ -50,14 +50,9 @@ pub mod isi { let account: Account = self.object.build(authority); let account_id = account.id().clone(); - account_id - .name - .validate_len(state_transaction.config.ident_length_limits) - .map_err(Error::from)?; - if account_id == *iroha_genesis::GENESIS_ACCOUNT_ID { return Err(InstructionExecutionError::InvariantViolation( - "Not allowed to register `genesis@genesis` account".to_owned(), + "Not allowed to register genesis account".to_owned(), )); } diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 4bc587aabee..9d18b92153d 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -107,26 +107,12 @@ impl Execute for MintBox { state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), Error> { match self { - Self::Account(isi) => isi.execute(authority, state_transaction), Self::Asset(isi) => isi.execute(authority, state_transaction), Self::TriggerRepetitions(isi) => isi.execute(authority, state_transaction), } } } -impl Execute for AccountMintBox { - fn execute( - self, - authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> std::prelude::v1::Result<(), Error> { - match self { - Self::PublicKey(isi) => isi.execute(authority, state_transaction), - Self::SignatureCheckCondition(isi) => isi.execute(authority, state_transaction), - } - } -} - impl Execute for BurnBox { #[iroha_logger::log(name = "burn", skip_all, fields(destination))] fn execute( @@ -135,7 +121,6 @@ impl Execute for BurnBox { state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), Error> { match self { - Self::AccountPublicKey(isi) => isi.execute(authority, state_transaction), Self::Asset(isi) => isi.execute(authority, state_transaction), Self::TriggerRepetitions(isi) => isi.execute(authority, state_transaction), } @@ -254,9 +239,9 @@ mod tests { use core::str::FromStr as _; use std::sync::Arc; - use iroha_crypto::KeyPair; use iroha_data_model::metadata::MetadataValueBox; - use iroha_genesis::GENESIS_ACCOUNT_ID; + use iroha_genesis::{GENESIS_ACCOUNT_ID, GENESIS_ACCOUNT_KEYPAIR}; + use test_samples::{gen_account_in, ALICE_ID}; use tokio::test; use super::*; @@ -272,18 +257,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(&GENESIS_ACCOUNT_ID, &mut state_transaction)?; + Register::account(Account::new(ALICE_ID.clone())) + .execute(&GENESIS_ACCOUNT_ID, &mut state_transaction)?; Register::asset_definition(AssetDefinition::store(asset_definition_id)) - .execute(&genesis_account_id, &mut state_transaction)?; + .execute(&GENESIS_ACCOUNT_ID, &mut state_transaction)?; state_transaction.apply(); state_block.commit(); Ok(state) @@ -293,9 +275,9 @@ mod tests { async fn asset_store() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let state = state_with_test_domains(&kura)?; - let mut staet_block = state.block(); - let mut state_transaction = staet_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let mut state_block = state.block(); + let mut state_transaction = state_block.transaction(); + let account_id = ALICE_ID.clone(); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); SetKeyValue::asset( @@ -326,7 +308,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); SetKeyValue::account( account_id.clone(), Name::from_str("Bytes")?, @@ -359,7 +341,7 @@ mod tests { let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); let definition_id = AssetDefinitionId::from_str("rose#wonderland")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); SetKeyValue::asset_definition( definition_id.clone(), Name::from_str("Bytes")?, @@ -390,7 +372,7 @@ mod tests { let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); let domain_id = DomainId::from_str("wonderland")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); SetKeyValue::domain( domain_id.clone(), Name::from_str("Bytes")?, @@ -420,7 +402,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let account_id = ALICE_ID.clone(); let trigger_id = TriggerId::from_str("test_trigger_id")?; assert!(matches!( @@ -439,13 +421,12 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; - let fake_account_id = AccountId::from_str("fake@wonderland")?; + let account_id = ALICE_ID.clone(); + let (fake_account_id, _fake_account_keypair) = gen_account_in("wonderland"); let trigger_id = TriggerId::from_str("test_trigger_id")?; // register fake account - let (public_key, _) = KeyPair::random().into_parts(); - let register_account = Register::account(Account::new(fake_account_id.clone(), public_key)); + let register_account = Register::account(Account::new(fake_account_id.clone())); register_account.execute(&account_id, &mut state_transaction)?; // register the trigger @@ -481,20 +462,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(GENESIS_ACCOUNT_ID.clone())); assert!(matches!( register_account .execute(&account_id, &mut state_transaction) @@ -512,10 +489,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()) .with_instructions(instructions) - .sign(&genesis_keys); + .sign(&GENESIS_ACCOUNT_KEYPAIR); let tx_limits = &state_block.transaction_executor().transaction_limits; assert!(matches!( AcceptedTransaction::accept(tx, &chain_id, tx_limits), diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index daf7faae917..ba8cc1843a2 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -72,13 +72,10 @@ impl ValidQueryRequest { query: SignedQuery, state_ro: &impl StateReadOnly, ) -> Result { - let account_has_public_key = state_ro - .world() - .map_account(query.authority(), |account| { - account.contains_signatory(query.signature().public_key()) - }) - .map_err(Error::from)?; - if !account_has_public_key { + if !query + .authority() + .signatory_matches(query.signature().public_key()) + { return Err(Error::Signature(String::from( "Signature public key doesn't correspond to the account.", )) @@ -155,7 +152,6 @@ impl ValidQuery for QueryBox { } FindAllAccounts, - FindAccountsByName, FindAccountsByDomainId, FindAccountsWithAsset, FindAllAssets, @@ -186,12 +182,12 @@ impl ValidQuery for QueryBox { mod tests { use std::str::FromStr as _; - use iroha_crypto::{Hash, HashOf, KeyPair}; + use iroha_crypto::{Hash, HashOf}; use iroha_data_model::{ metadata::MetadataValueBox, query::error::FindError, transaction::TransactionLimits, }; use iroha_primitives::unique_vec::UniqueVec; - use once_cell::sync::Lazy; + use test_samples::{gen_account_in, ALICE_ID, ALICE_KEYPAIR}; use tokio::test; use super::*; @@ -206,15 +202,10 @@ mod tests { PeersIds, }; - static ALICE_KEYS: Lazy = Lazy::new(KeyPair::random); - static ALICE_ID: Lazy = - Lazy::new(|| AccountId::from_str("alice@wonderland").expect("Valid")); - fn world_with_test_domains() -> World { let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&ALICE_ID); - let account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain.add_account(account).is_none()); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); assert!(domain @@ -227,8 +218,7 @@ mod tests { let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); let mut domain = Domain::new(DomainId::from_str("wonderland").expect("Valid")).build(&ALICE_ID); - let mut account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let mut account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain .add_asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).build(&ALICE_ID) @@ -260,7 +250,7 @@ mod tests { )?; let mut domain = Domain::new(DomainId::from_str("wonderland")?).build(&ALICE_ID); - let account = Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()) + let account = Account::new(ALICE_ID.clone()) .with_metadata(metadata) .build(&ALICE_ID); assert!(domain.add_account(account).is_none()); @@ -298,14 +288,14 @@ mod tests { let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions(instructions) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); AcceptedTransaction::accept(tx, &chain_id, &limits)? }; let invalid_tx = { let isi = Fail::new("fail".to_owned()); let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions([isi.clone(), isi]) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); AcceptedTransaction::accept(tx, &chain_id, &huge_limits)? }; @@ -315,7 +305,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let first_block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -327,7 +317,7 @@ mod tests { for _ in 1u64..blocks { let block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -461,7 +451,7 @@ mod tests { let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions(instructions) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); let tx_limits = &state_block.transaction_executor().transaction_limits; let va_tx = AcceptedTransaction::accept(tx, &chain_id, tx_limits)?; @@ -469,7 +459,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let vcb = BlockBuilder::new(vec![va_tx.clone()], topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -482,8 +472,8 @@ mod tests { let state_view = state.view(); let unapplied_tx = TransactionBuilder::new(chain_id, ALICE_ID.clone()) - .with_instructions([Unregister::account("account@domain".parse().unwrap())]) - .sign(&ALICE_KEYS); + .with_instructions([Unregister::account(gen_account_in("domain").0)]) + .sign(&ALICE_KEYPAIR); let wrong_hash = unapplied_tx.hash(); let not_found = FindTransactionByHash::new(wrong_hash).execute(&state_view); assert!(matches!( @@ -515,8 +505,7 @@ mod tests { let mut domain = Domain::new(DomainId::from_str("wonderland")?) .with_metadata(metadata) .build(&ALICE_ID); - let account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain.add_account(account).is_none()); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; assert!(domain diff --git a/core/src/smartcontracts/isi/world.rs b/core/src/smartcontracts/isi/world.rs index 19b56c01dd4..3eba2e10c3a 100644 --- a/core/src/smartcontracts/isi/world.rs +++ b/core/src/smartcontracts/isi/world.rs @@ -90,7 +90,7 @@ pub mod isi { if domain_id == *iroha_genesis::GENESIS_DOMAIN_ID { return Err(InstructionExecutionError::InvariantViolation( - "Not allowed to register `genesis` domain".to_owned(), + "Not allowed to register genesis domain".to_owned(), )); } diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index c10b17a6069..470321a972b 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -74,7 +74,7 @@ mod import { use super::super::*; - pub(crate) trait ExecuteOperations { + pub trait ExecuteOperations { /// Execute `query` on host #[codec::wrap_trait_fn] fn execute_query( @@ -1706,11 +1706,9 @@ impl GetExport for (&wasmtime::Instance, C) { #[cfg(test)] mod tests { - use std::str::FromStr as _; - - use iroha_crypto::KeyPair; use iroha_data_model::query::{sorting::Sorting, Pagination}; use parity_scale_codec::Encode; + use test_samples::gen_account_in; use tokio::test; use super::*; @@ -1721,8 +1719,7 @@ mod tests { fn world_with_test_account(authority: &AccountId) -> World { let domain_id = authority.domain_id.clone(); - let (public_key, _) = KeyPair::random().into_parts(); - let account = Account::new(authority.clone(), public_key).build(authority); + let account = Account::new(authority.clone()).build(authority); let mut domain = Domain::new(domain_id).build(authority); assert!(domain.add_account(account).is_none()); @@ -1775,17 +1772,14 @@ mod tests { #[test] async fn execute_instruction_exported() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { - let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = Register::account(Account::new( - new_authority, - KeyPair::random().into_parts().0, - )); + let (new_authority, _new_authority_keypair) = gen_account_in("wonderland"); + let register_isi = Register::account(Account::new(new_authority)); encode_hex(InstructionBox::from(register_isi)) }; @@ -1820,7 +1814,7 @@ mod tests { #[test] async fn execute_query_exported() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); @@ -1865,18 +1859,15 @@ mod tests { #[test] async fn instruction_limit_reached() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { - let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = Register::account(Account::new( - new_authority, - KeyPair::random().into_parts().0, - )); + let (new_authority, _new_authority_keypair) = gen_account_in("wonderland"); + let register_isi = Register::account(Account::new(new_authority)); encode_hex(InstructionBox::from(register_isi)) }; @@ -1918,17 +1909,14 @@ mod tests { #[test] async fn instructions_not_allowed() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { - let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); - let register_isi = Register::account(Account::new( - new_authority, - KeyPair::random().into_parts().0, - )); + let (new_authority, _new_authority_keypair) = gen_account_in("wonderland"); + let register_isi = Register::account(Account::new(new_authority)); encode_hex(InstructionBox::from(register_isi)) }; @@ -1970,7 +1958,7 @@ mod tests { #[test] async fn queries_not_allowed() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); @@ -2012,7 +2000,7 @@ mod tests { #[test] async fn trigger_related_func_is_not_linked_for_smart_contract() -> Result<(), Error> { - let authority = AccountId::from_str("alice@wonderland").expect("Valid"); + let (authority, _authority_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world_with_test_account(&authority), kura, query_handle); diff --git a/core/src/snapshot.rs b/core/src/snapshot.rs index c2b5f331be0..e046b49dba5 100644 --- a/core/src/snapshot.rs +++ b/core/src/snapshot.rs @@ -247,7 +247,6 @@ enum TryWriteError { mod tests { use std::{fs::File, io::Write}; - use iroha_crypto::KeyPair; use tempfile::tempdir; use tokio::test; @@ -255,11 +254,10 @@ mod tests { use crate::query::store::LiveQueryStore; fn state_factory() -> State { - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); State::new( - crate::queue::tests::world_with_test_domains([alice_key.public_key().clone()]), + crate::queue::tests::world_with_test_domains(), kura, query_handle, ) diff --git a/core/src/state.rs b/core/src/state.rs index 81ec0c79dec..d86a1dab212 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -1766,6 +1766,7 @@ pub(crate) mod deserialize { mod tests { use iroha_data_model::block::BlockPayload; use iroha_primitives::unique_vec::UniqueVec; + use test_samples::gen_account_in; use super::*; use crate::{ @@ -1844,14 +1845,14 @@ mod tests { #[test] fn role_account_range() { - let account_id: AccountId = "alice@wonderland".parse().unwrap(); + let (account_id, _account_keypair) = gen_account_in("wonderland"); let roles = [ RoleIdWithOwner::new(account_id.clone(), "1".parse().unwrap()), RoleIdWithOwner::new(account_id.clone(), "2".parse().unwrap()), - RoleIdWithOwner::new("bob@wonderland".parse().unwrap(), "3".parse().unwrap()), - RoleIdWithOwner::new("a@wonderland".parse().unwrap(), "4".parse().unwrap()), - RoleIdWithOwner::new("0@0".parse().unwrap(), "5".parse().unwrap()), - RoleIdWithOwner::new("1@1".parse().unwrap(), "6".parse().unwrap()), + RoleIdWithOwner::new(gen_account_in("wonderland").0, "3".parse().unwrap()), + RoleIdWithOwner::new(gen_account_in("wonderland").0, "4".parse().unwrap()), + RoleIdWithOwner::new(gen_account_in("0").0, "5".parse().unwrap()), + RoleIdWithOwner::new(gen_account_in("1").0, "6".parse().unwrap()), ]; let map = BTreeSet::from(roles); diff --git a/core/src/sumeragi/main_loop.rs b/core/src/sumeragi/main_loop.rs index 49e1c1db8b6..370dcbca0f0 100644 --- a/core/src/sumeragi/main_loop.rs +++ b/core/src/sumeragi/main_loop.rs @@ -1202,6 +1202,7 @@ fn handle_block_sync<'state, F: Fn(PipelineEventBox)>( #[cfg(test)] mod tests { use iroha_primitives::{unique_vec, unique_vec::UniqueVec}; + use test_samples::gen_account_in; use tokio::test; use super::*; @@ -1217,12 +1218,11 @@ mod tests { chain_id: &ChainId, topology: &Topology, leader_key_pair: &KeyPair, - tx_signer_key_pair: &KeyPair, - ) -> (State, Arc, SignedBlock) { + ) -> (State, Arc, SignedBlock, PublicKey) { // Predefined world state - let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); - let account = Account::new(alice_id.clone(), tx_signer_key_pair.public_key().clone()) - .build(&alice_id); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); + let genesis_public_key = alice_keypair.public_key().clone(); + let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = "wonderland".parse().expect("Valid"); let mut domain = Domain::new(domain_id).build(&alice_id); assert!(domain.add_account(account).is_none()); @@ -1239,7 +1239,7 @@ mod tests { // Making two transactions that have the same instruction let tx = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([fail_box]) - .sign(tx_signer_key_pair); + .sign(&alice_keypair); let tx = AcceptedTransaction::accept( tx, chain_id, @@ -1273,7 +1273,7 @@ mod tests { let tx1 = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([create_asset_definition1]) - .sign(tx_signer_key_pair); + .sign(&alice_keypair); let tx1 = AcceptedTransaction::accept( tx1, chain_id, @@ -1283,7 +1283,7 @@ mod tests { .expect("Valid"); let tx2 = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions([create_asset_definition2]) - .sign(tx_signer_key_pair); + .sign(&alice_keypair); let tx2 = AcceptedTransaction::accept( tx2, chain_id, @@ -1299,55 +1299,47 @@ mod tests { .unpack(|_| {}) }; - (state, kura, block.into()) + (state, kura, block.into(), genesis_public_key) } #[test] #[allow(clippy::redundant_clone)] async fn block_sync_invalid_block() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, _, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); // Malform block to make it invalid payload_mut(&mut block).commit_topology.clear(); - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!(result, Err((_, BlockSyncError::BlockNotValid(_))))) } #[test] async fn block_sync_invalid_soft_fork_block() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, kura, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); let mut state_block = state.block(); let committed_block = ValidBlock::validate( block.clone(), &topology, &chain_id, - tx_signer_key_pair.public_key(), + &genesis_public_key, &mut state_block, ) .unpack(|_| {}) @@ -1363,13 +1355,7 @@ mod tests { payload_mut(&mut block).commit_topology.clear(); payload_mut(&mut block).header.view_change_index = 1; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!( result, Err((_, BlockSyncError::SoftForkBlockNotValid(_))) @@ -1380,23 +1366,16 @@ mod tests { #[allow(clippy::redundant_clone)] async fn block_sync_not_proper_height() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let topology = Topology::new(UniqueVec::new()); let leader_key_pair = KeyPair::random(); - let (state, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, _, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); // Change block height payload_mut(&mut block).header.height = 42; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!( result, Err(( @@ -1413,44 +1392,36 @@ mod tests { #[allow(clippy::redundant_clone)] async fn block_sync_commit_block() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, _, block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let (state, _, block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!(result, Ok(BlockSyncOk::CommitBlock(_, _)))) } #[test] async fn block_sync_replace_top_block() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, kura, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); let mut state_block = state.block(); let committed_block = ValidBlock::validate( block.clone(), &topology, &chain_id, - tx_signer_key_pair.public_key(), + &genesis_public_key, &mut state_block, ) .unpack(|_| {}) @@ -1467,28 +1438,21 @@ mod tests { // Increase block view change index payload_mut(&mut block).header.view_change_index = 42; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!(result, Ok(BlockSyncOk::ReplaceTopBlock(_, _)))) } #[test] async fn block_sync_small_view_change_index() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let leader_key_pair = KeyPair::random(); let topology = Topology::new(unique_vec![PeerId::new( "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (state, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, kura, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); // Increase block view change index payload_mut(&mut block).header.view_change_index = 42; @@ -1498,7 +1462,7 @@ mod tests { block.clone(), &topology, &chain_id, - tx_signer_key_pair.public_key(), + &genesis_public_key, &mut state_block, ) .unpack(|_| {}) @@ -1514,13 +1478,7 @@ mod tests { // Decrease block view change index back payload_mut(&mut block).header.view_change_index = 0; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!( result, Err(( @@ -1537,25 +1495,18 @@ mod tests { #[allow(clippy::redundant_clone)] async fn block_sync_genesis_block_do_not_replace() { let chain_id = ChainId::from("0"); - let tx_signer_key_pair = KeyPair::random(); let topology = Topology::new(UniqueVec::new()); let leader_key_pair = KeyPair::random(); - let (state, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair, &tx_signer_key_pair); + let (state, _, mut block, genesis_public_key) = + create_data_for_test(&chain_id, &topology, &leader_key_pair); // Change block height and view change index // Soft-fork on genesis block is not possible payload_mut(&mut block).header.view_change_index = 42; payload_mut(&mut block).header.height = 1; - let result = handle_block_sync( - &chain_id, - tx_signer_key_pair.public_key(), - block, - &state, - &|_| {}, - ); + let result = handle_block_sync(&chain_id, &genesis_public_key, block, &state, &|_| {}); assert!(matches!( result, Err(( diff --git a/core/src/tx.rs b/core/src/tx.rs index 070ff3e7d03..368ac5d3184 100644 --- a/core/src/tx.rs +++ b/core/src/tx.rs @@ -63,14 +63,13 @@ impl AcceptedTransaction { })); } - for signature in tx.0.signatures() { - if signature.public_key() != genesis_public_key { - return Err(SignatureVerificationFail { - signature: signature.clone().into(), - reason: "Signature doesn't correspond to genesis public key".to_string(), - } - .into()); + let signature = tx.0.signature(); + if signature.public_key() != genesis_public_key { + return Err(SignatureVerificationFail { + signature: signature.clone().into(), + reason: "Signature doesn't correspond to genesis public key".to_string(), } + .into()); } Ok(Self(tx.0)) diff --git a/core/test_network/Cargo.toml b/core/test_network/Cargo.toml index 71e24f95f18..06888ac23dd 100644 --- a/core/test_network/Cargo.toml +++ b/core/test_network/Cargo.toml @@ -17,7 +17,7 @@ iroha_data_model = { workspace = true } iroha_primitives = { workspace = true } iroha_logger = { workspace = true } iroha_genesis = { workspace = true } - +test_samples = { workspace = true } eyre = { workspace = true } futures = { workspace = true, features = ["std", "async-await"] } diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 483332a6a0d..6fea0ba1a13 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -2,7 +2,7 @@ use core::{fmt::Debug, str::FromStr as _, time::Duration}; #[cfg(debug_assertions)] use std::sync::atomic::AtomicBool; -use std::{collections::BTreeMap, path::Path, sync::Arc, thread}; +use std::{collections::BTreeMap, ops::Deref, path::Path, sync::Arc, thread}; use eyre::Result; use futures::{prelude::*, stream::FuturesUnordered}; @@ -16,7 +16,7 @@ use iroha_config::parameters::actual::Root as Config; pub use iroha_core::state::StateReadOnly; use iroha_crypto::KeyPair; use iroha_data_model::{query::QueryOutputBox, ChainId}; -use iroha_genesis::{GenesisNetwork, RawGenesisBlockFile}; +use iroha_genesis::{GenesisNetwork, RawGenesisBlockFile, GENESIS_ACCOUNT_KEYPAIR}; use iroha_logger::{warn, InstrumentFutures}; use iroha_primitives::{ addr::{socket_addr, SocketAddr}, @@ -26,6 +26,7 @@ use iroha_primitives::{ use rand::{seq::IteratorRandom, thread_rng}; use serde_json::json; use tempfile::TempDir; +use test_samples::{ALICE_ID, ALICE_KEYPAIR, PEER_KEYPAIR}; use tokio::{ runtime::{self, Runtime}, task::{self, JoinHandle}, @@ -48,17 +49,22 @@ pub fn get_chain_id() -> ChainId { ChainId::from("0") } -/// Get a standardised key-pair from the hard-coded literals. -pub fn get_key_pair() -> KeyPair { - KeyPair::new( - iroha_crypto::PublicKey::from_str( - "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0", - ).unwrap(), - iroha_crypto::PrivateKey::from_hex( - iroha_crypto::Algorithm::Ed25519, - "9AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" - ).unwrap() - ).unwrap() +/// Get a key pair of a common signatory in the test network +pub fn get_key_pair(signatory: Signatory) -> KeyPair { + match signatory { + Signatory::Peer => &PEER_KEYPAIR, + Signatory::Genesis => &GENESIS_ACCOUNT_KEYPAIR, + Signatory::Alice => &ALICE_KEYPAIR, + } + .deref() + .clone() +} + +/// A common signatory in the test network +pub enum Signatory { + Peer, + Genesis, + Alice, } /// Trait used to differentiate a test instance of `genesis`. @@ -84,7 +90,6 @@ impl TestGenesis for GenesisNetwork { let rose_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("valid names"); - let alice_id = AccountId::from_str("alice@wonderland").expect("valid names"); let mint_rose_permission = PermissionToken::new( "CanMintAssetWithDefinition".parse().unwrap(), @@ -117,7 +122,7 @@ impl TestGenesis for GenesisNetwork { upgrade_executor_permission, ] { first_transaction - .append_instruction(Grant::permission(permission, alice_id.clone()).into()); + .append_instruction(Grant::permission(permission, ALICE_ID.clone()).into()); } for isi in extra_isi.into_iter() { @@ -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..3c37045350c 100644 --- a/data_model/src/account.rs +++ b/data_model/src/account.rs @@ -1,19 +1,13 @@ //! Structures, traits and impls related to `Account`s. #[cfg(not(feature = "std"))] -use alloc::{ - collections::{btree_map, btree_set}, - format, - string::String, - vec::Vec, -}; +use alloc::{collections::btree_map, format, string::String, vec::Vec}; use core::str::FromStr; #[cfg(feature = "std")] -use std::collections::{btree_map, btree_set}; +use std::collections::btree_map; use derive_more::{Constructor, DebugCustom, Display}; use getset::Getters; use iroha_data_model_derive::{model, IdEqOrdHash}; -use iroha_primitives::const_vec::ConstVec; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -27,27 +21,28 @@ use crate::{ }, domain::prelude::*, metadata::Metadata, - name::Name, HasMetadata, Identifiable, ParseError, PublicKey, Registered, }; /// API to work with collections of [`Id`] : [`Account`] mappings. pub type AccountsMap = btree_map::BTreeMap; -type Signatories = btree_set::BTreeSet; - #[model] mod model { use super::*; - /// Identification of an [`Account`]. Consists of Account name and Domain name. + /// Identification of [`Account`] by the combination of the [`PublicKey`] as its sole signatory and the [`Domain`](crate::domain::Domain) it belongs to. + /// TODO #4373 include multi-signatory use. /// /// # Examples /// /// ```rust /// use iroha_data_model::account::AccountId; /// - /// let id = "user@company".parse::().expect("Valid"); + /// let id: AccountId = + /// "ed0120BDF918243253B1E731FA096194C8928DA37C4D3226F97EEBD18CF5523D758D6C@domain" + /// .parse() + /// .expect("multihash@domain should be valid format"); /// ``` #[derive( DebugCustom, @@ -66,140 +61,63 @@ mod model { SerializeDisplay, IntoSchema, )] - #[display(fmt = "{name}@{domain_id}")] - #[debug(fmt = "{name}@{domain_id}")] + #[display(fmt = "{signatory}@{domain_id}")] + #[debug(fmt = "{signatory}@{domain_id}")] #[getset(get = "pub")] #[ffi_type] pub struct AccountId { - /// [`Account`]'s [`Domain`](`crate::domain::Domain`) id. + /// [`Domain`](crate::domain::Domain) that the [`Account`] belongs to. pub domain_id: DomainId, - /// [`Account`]'s name. - pub name: Name, + /// Sole signatory of the [`Account`]. + pub signatory: PublicKey, } /// Account entity is an authority which is used to execute `Iroha Special Instructions`. #[derive( - Debug, Display, Clone, IdEqOrdHash, Getters, Encode, Deserialize, Serialize, IntoSchema, + Debug, + Display, + Clone, + IdEqOrdHash, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, )] #[allow(clippy::multiple_inherent_impl)] #[display(fmt = "({id})")] // TODO: Add more? #[ffi_type] - #[serde(try_from = "candidate::Account")] pub struct Account { - /// An Identification of the [`Account`]. + /// Identification of the [`Account`]. pub id: AccountId, /// Assets in this [`Account`]. pub assets: AssetsMap, - /// [`Account`]'s signatories. - pub(super) signatories: Signatories, - /// Condition which checks if the account has the right signatures. - #[getset(get = "pub")] - pub signature_check_condition: SignatureCheckCondition, /// Metadata of this account as a key-value store. pub metadata: Metadata, } /// Builder which should be submitted in a transaction to create a new [`Account`] #[derive( - DebugCustom, Display, Clone, IdEqOrdHash, Encode, Serialize, Deserialize, IntoSchema, + DebugCustom, Display, Clone, IdEqOrdHash, Decode, Encode, Serialize, Deserialize, IntoSchema, )] #[display(fmt = "[{id}]")] - #[debug(fmt = "[{id:?}] {{ signatories: {signatories:?}, metadata: {metadata} }}")] + #[debug(fmt = "[{id:?}] {{ metadata: {metadata} }}")] #[ffi_type] - #[serde(try_from = "candidate::NewAccount")] pub struct NewAccount { /// Identification pub id: AccountId, - /// Signatories, i.e. signatures attached to this message. - /// Cannot be empty, guaranteed by constructors. - pub(super) signatories: Signatories, /// Metadata that should be submitted with the builder pub metadata: Metadata, } - - /// Condition which checks if the account has the right signatures. - #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - #[ffi_type(opaque)] - #[allow(clippy::enum_variant_names)] - pub enum SignatureCheckCondition { - #[display(fmt = "AnyAccountSignatureOr({_0:?})")] - AnyAccountSignatureOr(ConstVec), - #[display(fmt = "AllAccountSignaturesAnd({_0:?})")] - AllAccountSignaturesAnd(ConstVec), - } } -mod candidate { - //! Contains structs for deserialization checks - - use super::*; - - #[derive(Decode, Deserialize)] - /// [`Account`] candidate used for deserialization checks - pub struct Account { - id: AccountId, - assets: AssetsMap, - signatories: Signatories, - signature_check_condition: SignatureCheckCondition, - metadata: Metadata, - } - - impl TryFrom for super::Account { - type Error = &'static str; - - fn try_from(candidate: Account) -> Result { - check_signatories(&candidate.signatories)?; - - Ok(Self { - id: candidate.id, - assets: candidate.assets, - signatories: candidate.signatories, - signature_check_condition: candidate.signature_check_condition, - metadata: candidate.metadata, - }) - } - } - - /// [`NewAccount`] candidate used for deserialization checks - #[derive(Decode, Deserialize)] - pub struct NewAccount { - id: AccountId, - signatories: Signatories, - metadata: Metadata, - } - - impl TryFrom for super::NewAccount { - type Error = &'static str; - - fn try_from(candidate: NewAccount) -> Result { - check_signatories(&candidate.signatories)?; - - Ok(Self { - id: candidate.id, - signatories: candidate.signatories, - metadata: candidate.metadata, - }) - } - } - - fn check_signatories(signatories: &Signatories) -> Result<(), &'static str> { - if signatories.is_empty() { - return Err("Signatories cannot be empty"); - } - Ok(()) +impl AccountId { + /// Return `true` if the account signatory matches the given `public_key`. + #[inline] + #[cfg(feature = "transparent_api")] + pub fn signatory_matches(&self, public_key: &PublicKey) -> bool { + self.signatory() == public_key } } @@ -207,14 +125,14 @@ impl Account { /// Construct builder for [`Account`] identifiable by [`Id`] containing the given signatory. #[inline] #[must_use] - pub fn new(id: AccountId, signatory: PublicKey) -> ::With { - ::With::new(id, signatory) + pub fn new(id: AccountId) -> ::With { + ::With::new(id) } - /// Get an iterator over [`signatories`](PublicKey) of the `Account` + /// Return a reference to the `Account` signatory. #[inline] - pub fn signatories(&self) -> impl ExactSizeIterator { - self.signatories.iter() + pub fn signatory(&self) -> &PublicKey { + &self.id.signatory } /// Return a reference to the [`Asset`] corresponding to the asset id. @@ -228,12 +146,6 @@ impl Account { pub fn assets(&self) -> impl ExactSizeIterator { self.assets.values() } - - /// Return `true` if the `Account` contains the given signatory - #[inline] - pub fn contains_signatory(&self, signatory: &PublicKey) -> bool { - self.signatories.contains(signatory) - } } #[cfg(feature = "transparent_api")] @@ -249,67 +161,16 @@ impl Account { pub fn remove_asset(&mut self, asset_id: &AssetId) -> Option { self.assets.remove(asset_id) } - - /// Add [`signatory`](PublicKey) into the [`Account`]. - /// - /// If [`Account`] did not have this signatory present, `true` is returned. - /// If [`Account`] did have this signatory present, `false` is returned. - #[inline] - pub fn add_signatory(&mut self, signatory: PublicKey) -> bool { - self.signatories.insert(signatory) - } - - /// Remove a signatory from the [`Account`]. - /// - /// Does nothing and returns [`None`] if only one signature is left. - /// Otherwise returns whether the signatory was presented in the Account. - #[inline] - pub fn remove_signatory(&mut self, signatory: &PublicKey) -> Option { - if self.signatories.len() < 2 { - return None; - } - - Some(self.signatories.remove(signatory)) - } - - /// Checks whether the transaction contains all the signatures required by the - /// [`SignatureCheckCondition`] stored in this account. - #[must_use] - pub fn check_signature_check_condition( - &self, - transaction_signatories: &btree_set::BTreeSet, - ) -> bool { - self.signature_check_condition - .check(&self.signatories, transaction_signatories) - } -} - -impl Decode for Account { - fn decode( - input: &mut I, - ) -> Result { - let candidate = candidate::Account::decode(input)?; - Self::try_from(candidate).map_err(Into::into) - } } impl NewAccount { - fn new(id: AccountId, signatory: PublicKey) -> Self { + fn new(id: AccountId) -> Self { Self { id, - signatories: Signatories::from([signatory]), metadata: Metadata::default(), } } - /// Add signatory to account. - #[inline] - #[must_use] - pub fn add_signatory(mut self, signatory: PublicKey) -> Self { - self.signatories.insert(signatory); - self - } - /// Add [`Metadata`] to the account replacing any previously defined metadata #[inline] #[must_use] @@ -325,9 +186,7 @@ impl NewAccount { pub fn into_account(self) -> Account { Account { id: self.id, - signatories: self.signatories, assets: AssetsMap::default(), - signature_check_condition: SignatureCheckCondition::default(), metadata: self.metadata, } } @@ -339,15 +198,6 @@ impl HasMetadata for NewAccount { } } -impl Decode for NewAccount { - fn decode( - input: &mut I, - ) -> Result { - let candidate = candidate::NewAccount::decode(input)?; - Self::try_from(candidate).map_err(Into::into) - } -} - impl HasMetadata for Account { fn metadata(&self) -> &Metadata { &self.metadata @@ -358,207 +208,57 @@ impl Registered for Account { type With = NewAccount; } -/// Account Identification is represented by `name@domain_name` string. impl FromStr for AccountId { type Err = ParseError; - fn from_str(string: &str) -> Result { - let split = string.rsplit_once('@'); - match split { - Some(("", _)) => Err(ParseError { - reason: "`AccountId` cannot be empty", + fn from_str(s: &str) -> Result { + match s.rsplit_once('@') { + None => Err(ParseError { + reason: "Account ID should have format `signatory@domain`", }), - Some((name, domain_id)) if !name.is_empty() && !domain_id.is_empty() => Ok(AccountId { - name: name.parse()?, - domain_id: domain_id.parse()?, + Some(("", _)) => Err(ParseError { + reason: "Empty `signatory` part in `signatory@domain`", }), - _ => Err(ParseError { - reason: "`AccountId` should have format `name@domain_name`", + Some((_, "")) => Err(ParseError { + reason: "Empty `domain` part in `signatory@domain`", }), - } - } -} - -impl Default for SignatureCheckCondition { - fn default() -> Self { - Self::AnyAccountSignatureOr(ConstVec::new_empty()) - } -} - -impl SignatureCheckCondition { - /// Shorthand to create a [`SignatureCheckCondition::AnyAccountSignatureOr`] variant without additional allowed signatures. - #[inline] - pub fn any_account_signature() -> Self { - Self::AnyAccountSignatureOr(ConstVec::new_empty()) - } - - /// Shorthand to create a [`SignatureCheckCondition::AllAccountSignaturesAnd`] variant without additional required signatures. - #[inline] - pub fn all_account_signatures() -> Self { - Self::AllAccountSignaturesAnd(ConstVec::new_empty()) - } - - #[must_use] - #[cfg(feature = "transparent_api")] - fn check( - &self, - account_signatories: &btree_set::BTreeSet, - transaction_signatories: &btree_set::BTreeSet, - ) -> bool { - let result = match &self { - SignatureCheckCondition::AnyAccountSignatureOr(additional_allowed_signatures) => { - account_signatories - .iter() - .chain(additional_allowed_signatures.as_ref()) - .any(|allowed| transaction_signatories.contains(allowed)) - } - SignatureCheckCondition::AllAccountSignaturesAnd(additional_required_signatures) => { - account_signatories - .iter() - .chain(additional_required_signatures.as_ref()) - .all(|required_signature| transaction_signatories.contains(required_signature)) + Some((signatory_candidate, domain_id_candidate)) => { + let signatory = signatory_candidate.parse().map_err(|_| ParseError { + reason: "Failed to parse `signatory` part in `signatory@domain`. `signatory` should have multihash format e.g. `ed0120...`", + })?; + let domain_id = domain_id_candidate.parse().map_err(|_| ParseError { + reason: "Failed to parse `domain` part in `signatory@domain`", + })?; + Ok(Self::new(domain_id, signatory)) } - }; - - result + } } } /// The prelude re-exports most commonly used traits, structs and macros from this crate. pub mod prelude { - pub use super::{Account, AccountId, SignatureCheckCondition}; + pub use super::{Account, AccountId}; } #[cfg(test)] mod tests { - #[cfg(not(feature = "std"))] - use alloc::{vec, vec::Vec}; - use core::cmp::Ordering; - - use iroha_crypto::{KeyPair, PublicKey}; - - use super::{AccountId, SignatureCheckCondition}; - use crate::{domain::DomainId, name::Name}; - - fn make_key() -> PublicKey { - KeyPair::random().public_key().clone() - } - - fn check_signature_check_condition( - condition: &SignatureCheckCondition, - account_signatories: &[&PublicKey], - tx_signatories: &[&PublicKey], - result: bool, - ) { - let account_signatories = account_signatories.iter().copied().cloned().collect(); - let tx_signatories = tx_signatories.iter().copied().cloned().collect(); - - assert_eq!( - condition.check(&account_signatories, &tx_signatories,), - result - ); - } - - #[test] - fn signature_check_condition_default() { - let key1 = make_key(); - let key2 = make_key(); - let key3 = make_key(); - let condition = SignatureCheckCondition::default(); - - check_signature_check_condition(&condition, &[], &[], false); - check_signature_check_condition(&condition, &[&key1], &[], false); - check_signature_check_condition(&condition, &[], &[&key1], false); - check_signature_check_condition(&condition, &[&key1], &[&key1], true); - check_signature_check_condition(&condition, &[&key1], &[&key2], false); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key1], true); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key2], true); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key3], true); - } - - #[test] - fn signature_check_condition_all() { - let key1 = make_key(); - let key2 = make_key(); - let key3 = make_key(); - let condition = SignatureCheckCondition::all_account_signatures(); - - // technically, `\forall x \in \emptyset, check(x)` is true for any `check`, so this evaluate to true - // maybe not the logic we want? - check_signature_check_condition(&condition, &[], &[], true); - check_signature_check_condition(&condition, &[], &[&key1], true); - - check_signature_check_condition(&condition, &[&key1], &[], false); - check_signature_check_condition(&condition, &[&key1], &[&key1], true); - check_signature_check_condition(&condition, &[&key1], &[&key2], false); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key1], false); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key2], false); - check_signature_check_condition(&condition, &[&key1, &key2, &key3], &[&key3], false); - check_signature_check_condition(&condition, &[&key1, &key2], &[&key1, &key2, &key3], true); - check_signature_check_condition(&condition, &[&key1, &key2], &[&key1, &key2], true); - check_signature_check_condition(&condition, &[&key1, &key2], &[&key2, &key3], false); - } - - #[test] - fn signature_check_condition_any_or() { - let key1 = make_key(); - let key2 = make_key(); - let key3 = make_key(); - let condition = SignatureCheckCondition::AnyAccountSignatureOr(vec![key3.clone()].into()); - - check_signature_check_condition(&condition, &[], &[], false); - check_signature_check_condition(&condition, &[], &[&key3], true); - check_signature_check_condition(&condition, &[], &[&key2], false); - check_signature_check_condition(&condition, &[], &[&key1, &key2], false); - check_signature_check_condition(&condition, &[&key2], &[&key2], true); - check_signature_check_condition(&condition, &[&key2, &key3], &[&key2], true); - check_signature_check_condition(&condition, &[&key1, &key2], &[&key2], true); - } - - #[test] - fn signature_check_condition_all_and() { - let key1 = make_key(); - let key2 = make_key(); - let key3 = make_key(); - let condition = SignatureCheckCondition::AllAccountSignaturesAnd(vec![key3.clone()].into()); - - check_signature_check_condition(&condition, &[], &[], false); - check_signature_check_condition(&condition, &[], &[&key3], true); - check_signature_check_condition(&condition, &[&key1], &[&key3], false); - check_signature_check_condition(&condition, &[&key1], &[&key1, &key3], true); - check_signature_check_condition(&condition, &[&key2], &[&key1, &key3], false); - check_signature_check_condition(&condition, &[&key2], &[&key1, &key2, &key3], true); - } + use super::*; #[test] - fn cmp_account_id() { - let domain_id_a: DomainId = "a".parse().expect("failed to parse DomainId"); - let domain_id_b: DomainId = "b".parse().expect("failed to parse DomainId"); - let name_a: Name = "a".parse().expect("failed to parse Name"); - let name_b: Name = "b".parse().expect("failed to parse Name"); - - let mut account_ids = Vec::new(); - for name in [&name_a, &name_b] { - for domain_id in [&domain_id_a, &domain_id_b] { - account_ids.push(AccountId::new(domain_id.clone(), name.clone())); - } - } - - for account_id_1 in &account_ids { - for account_id_2 in &account_ids { - match ( - account_id_1.domain_id.cmp(&account_id_2.domain_id), - account_id_1.name.cmp(&account_id_2.name), - ) { - // `DomainId` take precedence in comparison - // if `DomainId`s are equal than comparison based on `Name`s - (Ordering::Equal, ordering) | (ordering, _) => assert_eq!( - account_id_1.cmp(account_id_2), - ordering, - "{account_id_1:?} and {account_id_2:?} are expected to be {ordering:?}" - ), - } - } - } + fn parse_account_id() { + const SIGNATORY: &str = + "ed0120EDF6D7B52C7032D03AEC696F2068BD53101528F3C7B6081BFF05A1662D7FC245"; + let _ok = format!("{SIGNATORY}@domain") + .parse::() + .expect("should be valid"); + let _err_empty_signatory = "@domain" + .parse::() + .expect_err("@domain should not be valid"); + let _err_empty_domain = format!("{SIGNATORY}@") + .parse::() + .expect_err("signatory@ should not be valid"); + let _err_violates_format = format!("{SIGNATORY}#domain") + .parse::() + .expect_err("signatory#domain should not be valid"); } } diff --git a/data_model/src/asset.rs b/data_model/src/asset.rs index 36e589a02ec..53fa815730e 100644 --- a/data_model/src/asset.rs +++ b/data_model/src/asset.rs @@ -382,19 +382,26 @@ impl> From for AssetValue { impl FromStr for AssetDefinitionId { type Err = ParseError; - fn from_str(string: &str) -> Result { - let mut split = string.split('#'); - match (split.next(), split.next(), split.next()) { - (Some(""), _, _) => Err(ParseError { - reason: "Asset Definition ID cannot be empty", + fn from_str(s: &str) -> Result { + match s.rsplit_once('#') { + None => Err(ParseError { + reason: "Asset Definition ID should have format `name#domain`", }), - (Some(name), Some(domain_id), None) if !domain_id.is_empty() => Ok(Self { - name: name.parse()?, - domain_id: domain_id.parse()?, + Some(("", _)) => Err(ParseError { + reason: "Empty `name` part in `name#domain`", }), - _ => Err(ParseError { - reason: "Asset Definition ID should have format `asset#domain`", + Some((_, "")) => Err(ParseError { + reason: "Empty `domain` part in `name#domain`", }), + Some((name_candidate, domain_id_candidate)) => { + let name = name_candidate.parse().map_err(|_| ParseError { + reason: "Failed to parse `name` part in `name#domain`", + })?; + let domain_id = domain_id_candidate.parse().map_err(|_| ParseError { + reason: "Failed to parse `domain` part in `name#domain`", + })?; + Ok(Self::new(domain_id, name)) + } } } } @@ -415,42 +422,26 @@ impl fmt::Debug for AssetId { } } -/// Asset Identification, represented by -/// `name#asset_domain#account_name@account_domain`. If the domains of -/// the asset and account match, the name can be shortened to -/// `asset##account@domain`. impl FromStr for AssetId { type Err = ParseError; - fn from_str(string: &str) -> Result { - if let Some((asset_definition_candidate, account_id_candidate)) = string.rsplit_once('#') { - let account_id: AccountId = account_id_candidate.parse() - .map_err(|_err| ParseError { - reason: "Failed to parse the `account_id` part of the `asset_id`. Please ensure that it has the form `account@domain`" - })?; - let definition_id = { - if let Ok(def_id) = asset_definition_candidate.parse() { - def_id - } else if let Some((name, "")) = asset_definition_candidate.rsplit_once('#') { - AssetDefinitionId::new( - account_id.domain_id.clone(), - name.parse().map_err(|_e| ParseError { - reason: "The `name` part of the `definition_id` part of the `asset_id` failed to parse as a valid `Name`. You might have forbidden characters like `#` or `@` in the first part." - })? - ) - } else { - return Err(ParseError { reason: "The `definition_id` part of the `asset_id` failed to parse. Ensure that you have it in the right format: `name#domain_of_asset#account_name@domain_of_account` or `name##account_name@domain_of_account` in case of same domain" }); - } - }; - Ok(Self { - definition_id, - account_id, - }) + fn from_str(s: &str) -> Result { + let (definition_id_candidate, account_id_candidate) = + s.rsplit_once('#').ok_or(ParseError { + reason: "Asset ID should have format `asset#domain#account@domain`, or `asset##account@domain` for the same domains", + })?; + let account_id = account_id_candidate.parse::().map_err(|_| ParseError { + reason: "Failed to parse `account@domain` part in `asset#domain#account@domain`. `account` should have multihash format e.g. `ed0120...`" + })?; + let domain_complement = if definition_id_candidate.ends_with('#') { + account_id.domain_id.name.as_ref() } else { - Err(ParseError { - reason: "The `AssetId` did not contain the `#` character. ", - }) - } + "" + }; + let definition_id = format!("{definition_id_candidate}{domain_complement}").parse().map_err(|_| ParseError { + reason: "Failed to parse `asset#domain` (or `asset#`) part in `asset#domain#account@domain` (or `asset##account@domain`)", + })?; + Ok(Self::new(definition_id, account_id)) } } @@ -494,9 +485,38 @@ pub mod prelude { #[cfg(test)] mod tests { use super::*; + + #[test] + fn parse_definition_id() { + let _ok = "asset#domain" + .parse::() + .expect("should be valid"); + let _err_empty_asset = "#domain" + .parse::() + .expect_err("#domain should not be valid"); + let _err_empty_domain = "asset#" + .parse::() + .expect_err("asset# should not be valid"); + let _err_violates_format = "asset@domain" + .parse::() + .expect_err("asset@domain should not be valid"); + } + #[test] - fn test_error_for_asset_id() { - let _invalid_asset_id = AssetId::from_str("store#alice@wonderland") - .expect_err("store#alice@wonderland should not be a valid AssetId"); + fn parse_asset_id() { + const SIGNATORY: &str = + "ed0120EDF6D7B52C7032D03AEC696F2068BD53101528F3C7B6081BFF05A1662D7FC245"; + let _account_id = format!("{SIGNATORY}@domain") + .parse::() + .expect("should be valid"); + let _ok = format!("asset#domain#{SIGNATORY}@domain") + .parse::() + .expect("should be valid"); + let _ok_short = format!("asset##{SIGNATORY}@domain") + .parse::() + .expect("should be valid"); + let _err = format!("asset#{SIGNATORY}@domain") + .parse::() + .expect_err("asset#signatory@domain should not be valid"); } } diff --git a/data_model/src/block.rs b/data_model/src/block.rs index b0f861757b9..91aca3ac52f 100644 --- a/data_model/src/block.rs +++ b/data_model/src/block.rs @@ -9,8 +9,6 @@ use alloc::{boxed::Box, format, string::String, vec::Vec}; use core::{fmt::Display, time::Duration}; use derive_more::Display; -#[cfg(all(feature = "std", feature = "transparent_api"))] -use iroha_crypto::KeyPair; use iroha_crypto::{HashOf, MerkleTree, SignaturesOf}; use iroha_data_model_derive::model; use iroha_macro::FromVariant; @@ -166,6 +164,7 @@ impl SignedBlock { /// Signatures of peers which approved this block. #[inline] + #[allow(private_interfaces)] pub fn signatures(&self) -> &SignaturesOf { let SignedBlock::V1(block) = self; &block.signatures @@ -188,8 +187,8 @@ impl SignedBlock { /// Add additional signatures to this block #[must_use] - #[cfg(feature = "transparent_api")] - pub fn sign(mut self, key_pair: &KeyPair) -> Self { + #[cfg(all(feature = "std", feature = "transparent_api"))] + pub fn sign(mut self, key_pair: &iroha_crypto::KeyPair) -> Self { let SignedBlock::V1(block) = &mut self; let signature = iroha_crypto::SignatureOf::new(key_pair, &block.payload); block.signatures.insert(signature); diff --git a/data_model/src/events/data/filters.rs b/data_model/src/events/data/filters.rs index 4ef246bf076..4fb6aee691e 100644 --- a/data_model/src/events/data/filters.rs +++ b/data_model/src/events/data/filters.rs @@ -740,10 +740,11 @@ pub mod prelude { TriggerEventFilter, }; } - #[cfg(test)] #[cfg(feature = "transparent_api")] mod tests { + use iroha_crypto::KeyPair; + use super::*; use crate::{ account::AccountsMap, @@ -753,12 +754,11 @@ mod tests { #[test] #[cfg(feature = "transparent_api")] fn entity_scope() { - let domain_name = "wonderland".parse().expect("Valid"); - let account_name = "alice".parse().expect("Valid"); - let asset_name = "rose".parse().expect("Valid"); - let domain_owner_id = "genesis@genesis".parse().expect("Valid"); + let domain_id: DomainId = "wonderland".parse().unwrap(); + let account_id = AccountId::new(domain_id.clone(), KeyPair::random().into_parts().0); + let asset_id: AssetId = format!("rose##{account_id}").parse().unwrap(); + let domain_owner_id = AccountId::new(domain_id.clone(), KeyPair::random().into_parts().0); - let domain_id = DomainId::new(domain_name); let domain = Domain { id: domain_id.clone(), accounts: AccountsMap::default(), @@ -768,16 +768,7 @@ mod tests { metadata: Metadata::default(), owned_by: domain_owner_id, }; - let account_id = AccountId::new(domain_id.clone(), account_name); - let account = Account::new( - account_id.clone(), - iroha_crypto::KeyPair::random().into_parts().0, - ) - .into_account(); - let asset_id = AssetId::new( - AssetDefinitionId::new(domain_id.clone(), asset_name), - account_id.clone(), - ); + let account = Account::new(account_id.clone()).into_account(); let asset = Asset::new(asset_id.clone(), 0_u32); // Create three events with three levels of nesting diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index 31aceca6dd6..d7c03236ba4 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -157,11 +157,8 @@ impl_instruction! { Unregister, Unregister, Unregister, - Mint, - Mint, Mint, Mint, - Burn, Burn, Burn, Transfer, @@ -646,29 +643,6 @@ mod transparent { } } - impl Mint { - /// Constructs a new [`Mint`] for a [`PublicKey`] for [`Account`]. - pub fn account_public_key(public_key: PublicKey, account_id: AccountId) -> Self { - Self { - object: public_key, - destination_id: account_id, - } - } - } - - impl Mint { - /// Constructs a new [`Mint`] for a [`SignatureCheckCondition`] for [`Account`]. - pub fn account_signature_check_condition( - signature_check_condition: SignatureCheckCondition, - account_id: AccountId, - ) -> Self { - Self { - object: signature_check_condition, - destination_id: account_id, - } - } - } - impl Mint { /// Constructs a new [`Mint`] for an [`Asset`] of [`Numeric`] type. pub fn asset_numeric(object: impl Into, asset_id: AssetId) -> Self { @@ -702,15 +676,6 @@ mod transparent { } impl_into_box! { - Mint | - Mint - => AccountMintBox => MintBox[Account], - => AccountMintBoxRef<'a> => MintBoxRef<'a>[Account] - } - - impl_into_box! { - Mint | - Mint | Mint | Mint => MintBox => InstructionBox[Mint], @@ -728,16 +693,6 @@ mod transparent { } } - impl Burn { - /// Constructs a new [`Burn`] for a [`PublicKey`] for [`Account`]. - pub fn account_public_key(public_key: PublicKey, account_id: AccountId) -> Self { - Self { - object: public_key, - destination_id: account_id, - } - } - } - impl Burn { /// Constructs a new [`Burn`] for an [`Asset`] of [`Numeric`] type. pub fn asset_numeric(object: impl Into, asset_id: AssetId) -> Self { @@ -771,7 +726,6 @@ mod transparent { } impl_into_box! { - Burn | Burn | Burn => BurnBox => InstructionBox[Burn], @@ -1172,9 +1126,6 @@ isi_box! { )] /// Enum with all supported [`Mint`] instructions. pub enum MintBox { - /// Mint for [`Account`]. - #[enum_ref(transparent)] - Account(AccountMintBox), /// Mint for [`Asset`]. Asset(Mint), /// Mint [`Trigger`] repetitions. @@ -1182,21 +1133,6 @@ isi_box! { } } -isi_box! { - #[strum_discriminants( - vis(pub(crate)), - name(AccountMintType), - derive(Encode), - )] - /// Enum with all supported [`Mint`] instructions related to [`Account`]. - pub enum AccountMintBox { - /// Mint [`PublicKey`]. - PublicKey(Mint), - /// Mint [`SignatureCheckCondition`]. - SignatureCheckCondition(Mint), - } -} - isi_box! { #[strum_discriminants( vis(pub(crate)), @@ -1205,8 +1141,6 @@ isi_box! { )] /// Enum with all supported [`Burn`] instructions. pub enum BurnBox { - /// Burn [`PublicKey`] for [`Account`]. - AccountPublicKey(Burn), /// Burn [`Asset`]. Asset(Burn), /// Burn [`Trigger`] repetitions. @@ -1584,9 +1518,9 @@ pub mod error { /// The prelude re-exports most commonly used traits, structs and macros from this crate. pub mod prelude { pub use super::{ - AccountMintBox, AssetTransferBox, Burn, BurnBox, ExecuteTrigger, Fail, Grant, GrantBox, - InstructionBox, Log, Mint, MintBox, NewParameter, Register, RegisterBox, RemoveKeyValue, - RemoveKeyValueBox, Revoke, RevokeBox, SetKeyValue, SetKeyValueBox, SetParameter, Transfer, - TransferBox, Unregister, UnregisterBox, Upgrade, + AssetTransferBox, Burn, BurnBox, ExecuteTrigger, Fail, Grant, GrantBox, InstructionBox, + Log, Mint, MintBox, NewParameter, Register, RegisterBox, RemoveKeyValue, RemoveKeyValueBox, + Revoke, RevokeBox, SetKeyValue, SetKeyValueBox, SetParameter, Transfer, TransferBox, + Unregister, UnregisterBox, Upgrade, }; } diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index a4be42ece8a..31b4c974b8b 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -97,12 +97,9 @@ mod seal { Unregister, Unregister, - Mint, - Mint, Mint, Mint, - Burn, Burn, Burn, @@ -131,7 +128,6 @@ mod seal { FindAllAccounts, FindAccountById, FindAccountKeyValueByIdAndKey, - FindAccountsByName, FindAccountsByDomainId, FindAccountsWithAsset, FindAllAssets, @@ -619,6 +615,7 @@ pub mod parameter { } #[model] +#[allow(clippy::redundant_pub_crate)] mod model { use super::*; diff --git a/data_model/src/query/mod.rs b/data_model/src/query/mod.rs index 9837f00905d..bd84908848d 100644 --- a/data_model/src/query/mod.rs +++ b/data_model/src/query/mod.rs @@ -152,7 +152,6 @@ mod model { FindAllAccounts(FindAllAccounts), FindAccountById(FindAccountById), FindAccountKeyValueByIdAndKey(FindAccountKeyValueByIdAndKey), - FindAccountsByName(FindAccountsByName), FindAccountsByDomainId(FindAccountsByDomainId), FindAccountsWithAsset(FindAccountsWithAsset), FindAllAssets(FindAllAssets), @@ -339,7 +338,6 @@ impl_query! { FindAllAccounts => Vec, FindAccountById => crate::account::Account, FindAccountKeyValueByIdAndKey => MetadataValueBox, - FindAccountsByName => Vec, FindAccountsByDomainId => Vec, FindAccountsWithAsset => Vec, FindAllAssets => Vec, @@ -668,19 +666,6 @@ pub mod account { pub key: Name, } - /// [`FindAccountsByName`] Iroha Query gets [`Account`]s name as input and - /// finds all [`Account`]s with this name. - #[derive(Display)] - #[display(fmt = "Find accounts with `{name}` name")] - #[repr(transparent)] - // SAFETY: `FindAccountsByName` has no trap representation in `EvaluatesTo` - #[ffi_type(unsafe {robust})] - pub struct FindAccountsByName { - /// `name` of accounts to find. - pub name: Name, - } - - /// [`FindAccountsByDomainId`] Iroha Query gets [`Domain`]s id as input and /// finds all [`Account`]s under this [`Domain`]. #[derive(Display)] @@ -710,7 +695,7 @@ pub mod account { pub mod prelude { pub use super::{ FindAccountById, FindAccountKeyValueByIdAndKey, FindAccountsByDomainId, - FindAccountsByName, FindAccountsWithAsset, FindAllAccounts, + FindAccountsWithAsset, FindAllAccounts, }; } } diff --git a/data_model/src/query/predicate.rs b/data_model/src/query/predicate.rs index 6b6ab4c3682..aee03ed9d89 100644 --- a/data_model/src/query/predicate.rs +++ b/data_model/src/query/predicate.rs @@ -614,6 +614,8 @@ pub mod string { use super::*; mod id_box { + use iroha_crypto::KeyPair; + use super::*; use crate::peer::PeerId; @@ -689,34 +691,32 @@ pub mod string { #[test] fn account_id() { - let id = IdBox::AccountId("alice@wonderland".parse().expect("Valid")); - assert!(StringPredicate::starts_with("alice@").applies(&id)); + let alice: PublicKey = KeyPair::random().into_parts().0; + let id = IdBox::AccountId(format!("{alice}@wonderland").parse().expect("Valid")); + assert!(StringPredicate::starts_with(&*format!("{alice}@")).applies(&id)); assert!(StringPredicate::ends_with("@wonderland").applies(&id)); - assert!(StringPredicate::is("alice@wonderland").applies(&id)); + assert!(StringPredicate::is(&*format!("{alice}@wonderland")).applies(&id)); // Should we also include a check into string // predicates? If the internal predicate starts with // whitespace, it can't possibly match any Id, but // there's no way to enforce this at both type level // and run-time. - assert!(!StringPredicate::starts_with(" alice@").applies(&id)); + assert!(!StringPredicate::starts_with(&*format!(" {alice}@")).applies(&id)); assert!(!StringPredicate::ends_with("@wonderland ").applies(&id)); - assert!(!StringPredicate::is("alice@@wonderland ").applies(&id)); + assert!(!StringPredicate::is(&*format!("{alice}@@wonderland ")).applies(&id)); assert!(!StringPredicate::contains("#").applies(&id)); - assert!(!StringPredicate::is("alice#wonderland").applies(&id)); + assert!(!StringPredicate::is(&*format!("{alice}#wonderland")).applies(&id)); } #[test] fn asset_id() { - let definition_id = "rose#wonderland".parse().expect("Valid"); - let account_id = "alice@wonderland".parse().expect("Valid"); - let id = IdBox::AssetId(crate::asset::AssetId { - definition_id, - account_id, - }); + let alice: PublicKey = KeyPair::random().into_parts().0; + let id = + IdBox::AssetId(format!("rose##{alice}@wonderland").parse().expect("Valid")); assert!(StringPredicate::starts_with("rose##").applies(&id)); - assert!(StringPredicate::ends_with("#alice@wonderland").applies(&id)); - assert!(StringPredicate::is("rose##alice@wonderland").applies(&id)); - assert!(StringPredicate::contains("#alice@").applies(&id)); + assert!(StringPredicate::ends_with(&*format!("#{alice}@wonderland")).applies(&id)); + assert!(StringPredicate::is(&*format!("rose##{alice}@wonderland")).applies(&id)); + assert!(StringPredicate::contains(&*format!("#{alice}@")).applies(&id)); } #[test] @@ -1237,98 +1237,95 @@ pub mod value { #[test] fn typing() { + let alice: PublicKey = KeyPair::random().into_parts().0; + let alice_id: AccountId = format!("{alice}@wonderland").parse().expect("Valid"); { let pred = QueryOutputPredicate::Identifiable(string::StringPredicate::is( - "alice@wonderland", + &*alice_id.to_string(), )); println!("{pred:?}"); - assert!(pred.applies(&QueryOutputBox::Id(IdBox::AccountId( - "alice@wonderland".parse().expect("Valid") - )))); + assert!(pred.applies(&QueryOutputBox::Id(IdBox::AccountId(alice_id.clone())))); assert!( pred.applies(&QueryOutputBox::Identifiable(IdentifiableBox::NewAccount( - Account::new( - "alice@wonderland".parse().expect("Valid"), - KeyPair::random().into_parts().0 - ) + Account::new(alice_id.clone()) ))) ); - assert!(!pred.applies( - &MetadataValueBox::from("alice".parse::().expect("Valid")).into() - )); + assert!(!pred.applies(&MetadataValueBox::from(alice_id.to_string()).into())); assert!(!pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { let pred = QueryOutputPredicate::Pass; println!("{pred:?}"); - assert!(pred.applies(&MetadataValueBox::from("alice@wonderland".to_owned()).into())); + assert!(pred.applies(&MetadataValueBox::from(alice_id.to_string()).into())); } { let pred = QueryOutputPredicate::TimeStamp(numerical::SemiInterval::starting(0)); println!("{pred:?}"); - assert!( - !pred.applies(&MetadataValueBox::from("alice@wonderland".to_owned()).into()) - ); + assert!(!pred.applies(&MetadataValueBox::from(alice_id.to_string()).into())); } { - let key_pair = iroha_crypto::KeyPair::random(); - let (public_key, _) = key_pair.into_parts(); - let pred = - QueryOutputPredicate::Display(string::StringPredicate::is("alice@wonderland")); + let pred = QueryOutputPredicate::Display(string::StringPredicate::is( + &*alice_id.to_string(), + )); println!("{pred:?}"); assert!( !pred.applies(&QueryOutputBox::Identifiable(IdentifiableBox::Peer(Peer { - id: PeerId::new(socket_addr!(127.0.0.1:123), public_key) + id: PeerId::new( + socket_addr!(127.0.0.1:123), + KeyPair::random().into_parts().0 + ) }))) ); } let pred = QueryOutputPredicate::Numerical(numerical::SemiRange::Numeric( (numeric!(0), numeric!(42)).into(), )); - assert!(!pred.applies(&MetadataValueBox::from("alice".to_owned()).into())); + assert!(!pred.applies(&MetadataValueBox::from(alice_id.to_string()).into())); assert!(pred.applies(&numeric!(41).into())); } #[test] fn container_vec() { + let wonderland: DomainId = "wonderland".parse().expect("Valid"); + let alice: PublicKey = KeyPair::random().into_parts().0; + let alice_id = AccountId::new(wonderland.clone(), alice.clone()); let list = QueryOutputBox::Vec(vec![ - QueryOutputBox::Identifiable( - Domain::new("alice".parse::().unwrap()).into(), - ), - QueryOutputBox::Id("alice@wonderland".parse::().unwrap().into()), - QueryOutputBox::Id("aliceee!".parse::().unwrap().into()), + QueryOutputBox::Identifiable(Domain::new(wonderland.clone()).into()), + QueryOutputBox::Id(alice_id.into()), + QueryOutputBox::Id(wonderland.clone().into()), ]); - let alice_pred = - QueryOutputPredicate::Display(string::StringPredicate::contains("alice")); + let wonderland_pred = + QueryOutputPredicate::Display(string::StringPredicate::contains("wonderland")); { - let pred = QueryOutputPredicate::any(alice_pred.clone()); + let pred = QueryOutputPredicate::any(wonderland_pred.clone()); println!("{pred:?}"); assert!(pred.applies(&list)); assert!(!pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { - let pred = QueryOutputPredicate::all(alice_pred.clone()); + let pred = QueryOutputPredicate::all(wonderland_pred.clone()); println!("{pred:?}"); assert!(pred.applies(&list)); assert!(pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { - let alice_id_pred = - QueryOutputPredicate::Identifiable(string::StringPredicate::contains("alice")); - let pred = QueryOutputPredicate::all(alice_id_pred); + let wonderland_id_pred = QueryOutputPredicate::Identifiable( + string::StringPredicate::contains("wonderland"), + ); + let pred = QueryOutputPredicate::all(wonderland_id_pred); println!("{pred:?}"); assert!(pred.applies(&list)); assert!(pred.applies(&QueryOutputBox::Vec(Vec::new()))); } - assert!(QueryOutputPredicate::at_index(0, alice_pred.clone()).applies(&list)); + assert!(QueryOutputPredicate::at_index(0, wonderland_pred.clone()).applies(&list)); - let idx_pred = QueryOutputPredicate::at_index(3, alice_pred); // Should be out of bounds. + let idx_pred = QueryOutputPredicate::at_index(3, wonderland_pred); // Should be out of bounds. println!("{idx_pred:?}"); assert!(!idx_pred.applies(&list)); } diff --git a/data_model/src/transaction.rs b/data_model/src/transaction.rs index 0a541f70d04..c1dbe0e1686 100644 --- a/data_model/src/transaction.rs +++ b/data_model/src/transaction.rs @@ -9,7 +9,7 @@ use core::{ }; use derive_more::{DebugCustom, Display}; -use iroha_crypto::SignaturesOf; +use iroha_crypto::SignatureOf; use iroha_data_model_derive::model; use iroha_macro::FromVariant; use iroha_schema::IntoSchema; @@ -100,6 +100,7 @@ mod model { /// Unique id of the blockchain. Used for simple replay attack protection. pub chain_id: ChainId, /// Account ID of transaction creator. + /// TODO dedup public keys in transaction #4410 pub authority: AccountId, /// Creation timestamp (unix time in milliseconds). pub creation_time_ms: u64, @@ -140,12 +141,12 @@ mod model { pub max_wasm_size_bytes: u64, } - /// Transaction that contains at least one signature + /// Transaction that contains a signature /// /// `Iroha` and its clients use [`Self`] to send transactions over the network. /// After a transaction is signed and before it can be processed any further, /// the transaction must be accepted by the `Iroha` peer. - /// The peer verifies the signatures and checks the limits. + /// The peer verifies the signature and checks the limits. #[version(version = 1, versioned_alias = "SignedTransaction")] #[derive( Debug, Display, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Serialize, IntoSchema, @@ -154,9 +155,9 @@ mod model { #[cfg_attr(feature = "std", display(fmt = "{}", "self.hash()"))] #[ffi_type] pub struct SignedTransactionV1 { - /// [`iroha_crypto::SignatureOf`]<[`TransactionPayload`]>. - pub(super) signatures: SignaturesOf, - /// [`Transaction`] payload. + /// Signature of [`Self::payload`]. + pub(super) signature: SignatureOf, + /// Payload of the transaction. pub(super) payload: TransactionPayload, } @@ -290,11 +291,12 @@ impl SignedTransaction { &tx.payload.chain_id } - /// Return transaction signatures + /// Return the transaction signature #[inline] - pub fn signatures(&self) -> &SignaturesOf { + #[allow(private_interfaces)] + pub fn signature(&self) -> &SignatureOf { let SignedTransaction::V1(tx) = self; - &tx.signatures + &tx.signature } /// Calculate transaction [`Hash`](`iroha_crypto::HashOf`). @@ -308,11 +310,11 @@ impl SignedTransaction { pub fn sign(self, key_pair: &iroha_crypto::KeyPair) -> SignedTransaction { let SignedTransaction::V1(mut tx) = self; let signature = iroha_crypto::SignatureOf::new(key_pair, &tx.payload); - tx.signatures.insert(signature); + tx.signature = signature; SignedTransactionV1 { payload: tx.payload, - signatures: tx.signatures, + signature: tx.signature, } .into() } @@ -346,13 +348,13 @@ mod candidate { #[derive(Decode, Deserialize)] struct SignedTransactionCandidate { - signatures: SignaturesOf, + signature: SignatureOf, payload: TransactionPayload, } impl SignedTransactionCandidate { fn validate(self) -> Result { - self.validate_signatures()?; + self.validate_signature()?; self.validate_instructions() } @@ -365,12 +367,12 @@ mod candidate { Ok(SignedTransactionV1 { payload: self.payload, - signatures: self.signatures, + signature: self.signature, }) } - fn validate_signatures(&self) -> Result<(), &'static str> { - self.signatures + fn validate_signature(&self) -> Result<(), &'static str> { + self.signature .verify(&self.payload) .map_err(|_| "Transaction contains invalid signatures") } @@ -741,11 +743,11 @@ mod http { /// Sign transaction with provided key pair. #[must_use] pub fn sign(self, key_pair: &iroha_crypto::KeyPair) -> SignedTransaction { - let signatures = SignaturesOf::new(key_pair, &self.payload); + let signature = SignatureOf::new(key_pair, &self.payload); SignedTransactionV1 { payload: self.payload, - signatures, + signature, } .into() } diff --git a/data_model/src/visit.rs b/data_model/src/visit.rs index f25fc3dc9e0..d216a8ee845 100644 --- a/data_model/src/visit.rs +++ b/data_model/src/visit.rs @@ -1,7 +1,6 @@ //! Visitor that visits every node in Iroha syntax tree #![allow(missing_docs, clippy::missing_errors_doc)] -use iroha_crypto::PublicKey; use iroha_primitives::numeric::Numeric; use crate::{isi::Log, prelude::*}; @@ -49,7 +48,6 @@ pub trait Visit { visit_find_account_by_id(&FindAccountById), visit_find_account_key_value_by_id_and_key(&FindAccountKeyValueByIdAndKey), visit_find_accounts_by_domain_id(&FindAccountsByDomainId), - visit_find_accounts_by_name(&FindAccountsByName), visit_find_accounts_with_asset(&FindAccountsWithAsset), visit_find_all_accounts(&FindAllAccounts), visit_find_all_active_trigger_ids(&FindAllActiveTriggerIds), @@ -108,12 +106,9 @@ pub trait Visit { // Visit MintBox visit_mint_asset_numeric(&Mint), - visit_mint_account_public_key(&Mint), - visit_mint_account_signature_check_condition(&Mint), visit_mint_trigger_repetitions(&Mint), // Visit BurnBox - visit_burn_account_public_key(&Burn), visit_burn_asset_numeric(&Burn), visit_burn_trigger_repetitions(&Burn), @@ -178,7 +173,6 @@ pub fn visit_query(visitor: &mut V, authority: &AccountId, qu visit_find_account_by_id(FindAccountById), visit_find_account_key_value_by_id_and_key(FindAccountKeyValueByIdAndKey), visit_find_accounts_by_domain_id(FindAccountsByDomainId), - visit_find_accounts_by_name(FindAccountsByName), visit_find_accounts_with_asset(FindAccountsWithAsset), visit_find_all_accounts(FindAllAccounts), visit_find_all_active_trigger_ids(FindAllActiveTriggerIds), @@ -304,12 +298,6 @@ pub fn visit_unregister( pub fn visit_mint(visitor: &mut V, authority: &AccountId, isi: &MintBox) { match isi { - MintBox::Account(mint_account) => match mint_account { - AccountMintBox::PublicKey(obj) => visitor.visit_mint_account_public_key(authority, obj), - AccountMintBox::SignatureCheckCondition(obj) => { - visitor.visit_mint_account_signature_check_condition(authority, obj) - } - }, MintBox::Asset(obj) => visitor.visit_mint_asset_numeric(authority, obj), MintBox::TriggerRepetitions(obj) => visitor.visit_mint_trigger_repetitions(authority, obj), } @@ -317,7 +305,6 @@ pub fn visit_mint(visitor: &mut V, authority: &AccountId, isi pub fn visit_burn(visitor: &mut V, authority: &AccountId, isi: &BurnBox) { match isi { - BurnBox::AccountPublicKey(obj) => visitor.visit_burn_account_public_key(authority, obj), BurnBox::Asset(obj) => visitor.visit_burn_asset_numeric(authority, obj), BurnBox::TriggerRepetitions(obj) => visitor.visit_burn_trigger_repetitions(authority, obj), } @@ -400,9 +387,6 @@ leaf_visitors! { // Instruction visitors visit_register_account(&Register), visit_unregister_account(&Unregister), - visit_mint_account_public_key(&Mint), - visit_burn_account_public_key(&Burn), - visit_mint_account_signature_check_condition(&Mint), visit_set_account_key_value(&SetKeyValue), visit_remove_account_key_value(&RemoveKeyValue), visit_register_asset(&Register), @@ -450,7 +434,6 @@ leaf_visitors! { visit_find_account_by_id(&FindAccountById), visit_find_account_key_value_by_id_and_key(&FindAccountKeyValueByIdAndKey), visit_find_accounts_by_domain_id(&FindAccountsByDomainId), - visit_find_accounts_by_name(&FindAccountsByName), visit_find_accounts_with_asset(&FindAccountsWithAsset), visit_find_all_accounts(&FindAllAccounts), visit_find_all_active_trigger_ids(&FindAllActiveTriggerIds), diff --git a/data_model/tests/data_model.rs b/data_model/tests/data_model.rs index 4e8a0a7a207..85963718589 100644 --- a/data_model/tests/data_model.rs +++ b/data_model/tests/data_model.rs @@ -1,25 +1,15 @@ -use iroha_data_model::{prelude::*, ParseError}; +use iroha_crypto::KeyPair; +use iroha_data_model::prelude::*; #[test] fn transfer_isi_should_be_valid() { let _instruction = Transfer::asset_numeric( - "btc##seller@crypto".parse().expect("Valid"), + format!("btc##{}@crypto", KeyPair::random().public_key()) + .parse() + .unwrap(), 12u32, - "buyer@crypto".parse().expect("Valid"), + format!("{}@crypto", KeyPair::random().public_key()) + .parse() + .unwrap(), ); } - -#[test] -fn account_id_parsing() -> Result<(), ParseError> { - // `AccountId` should have format `name@domain_name` - let account_normal: AccountId = "test@hello".parse()?; - assert_eq!(account_normal.name().as_ref(), "test"); - assert_eq!(account_normal.domain_id().name().as_ref(), "hello"); - - let account_empty: Result = "@hello".parse(); - assert!(account_empty.is_err()); - - let account_invalid: Result = "@".parse(); - assert!(account_invalid.is_err()); - Ok(()) -} diff --git a/docs/source/references/schema.json b/docs/source/references/schema.json index e9196b814df..6610b7a8f29 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -9,14 +9,6 @@ "name": "assets", "type": "SortedMap" }, - { - "name": "signatories", - "type": "SortedVec" - }, - { - "name": "signature_check_condition", - "type": "SignatureCheckCondition" - }, { "name": "metadata", "type": "Metadata" @@ -152,22 +144,8 @@ "type": "DomainId" }, { - "name": "name", - "type": "Name" - } - ] - }, - "AccountMintBox": { - "Enum": [ - { - "tag": "PublicKey", - "discriminant": 0, - "type": "Mint" - }, - { - "tag": "SignatureCheckCondition", - "discriminant": 1, - "type": "Mint" + "name": "signatory", + "type": "PublicKey" } ] }, @@ -713,18 +691,6 @@ } ] }, - "Burn": { - "Struct": [ - { - "name": "object", - "type": "PublicKey" - }, - { - "name": "destination_id", - "type": "AccountId" - } - ] - }, "Burn": { "Struct": [ { @@ -739,19 +705,14 @@ }, "BurnBox": { "Enum": [ - { - "tag": "AccountPublicKey", - "discriminant": 0, - "type": "Burn" - }, { "tag": "Asset", - "discriminant": 1, + "discriminant": 0, "type": "Burn" }, { "tag": "TriggerRepetitions", - "discriminant": 2, + "discriminant": 1, "type": "Burn" } ] @@ -1262,14 +1223,6 @@ } ] }, - "FindAccountsByName": { - "Struct": [ - { - "name": "name", - "type": "Name" - } - ] - }, "FindAccountsWithAsset": { "Struct": [ { @@ -2270,30 +2223,6 @@ } ] }, - "Mint": { - "Struct": [ - { - "name": "object", - "type": "PublicKey" - }, - { - "name": "destination_id", - "type": "AccountId" - } - ] - }, - "Mint": { - "Struct": [ - { - "name": "object", - "type": "SignatureCheckCondition" - }, - { - "name": "destination_id", - "type": "AccountId" - } - ] - }, "Mint": { "Struct": [ { @@ -2308,19 +2237,14 @@ }, "MintBox": { "Enum": [ - { - "tag": "Account", - "discriminant": 0, - "type": "AccountMintBox" - }, { "tag": "Asset", - "discriminant": 1, + "discriminant": 0, "type": "Mint" }, { "tag": "TriggerRepetitions", - "discriminant": 2, + "discriminant": 1, "type": "Mint" } ] @@ -2372,10 +2296,6 @@ "name": "id", "type": "AccountId" }, - { - "name": "signatories", - "type": "SortedVec" - }, { "name": "metadata", "type": "Metadata" @@ -2731,189 +2651,184 @@ "discriminant": 2, "type": "FindAccountKeyValueByIdAndKey" }, - { - "tag": "FindAccountsByName", - "discriminant": 3, - "type": "FindAccountsByName" - }, { "tag": "FindAccountsByDomainId", - "discriminant": 4, + "discriminant": 3, "type": "FindAccountsByDomainId" }, { "tag": "FindAccountsWithAsset", - "discriminant": 5, + "discriminant": 4, "type": "FindAccountsWithAsset" }, { "tag": "FindAllAssets", - "discriminant": 6, + "discriminant": 5, "type": "FindAllAssets" }, { "tag": "FindAllAssetsDefinitions", - "discriminant": 7, + "discriminant": 6, "type": "FindAllAssetsDefinitions" }, { "tag": "FindAssetById", - "discriminant": 8, + "discriminant": 7, "type": "FindAssetById" }, { "tag": "FindAssetDefinitionById", - "discriminant": 9, + "discriminant": 8, "type": "FindAssetDefinitionById" }, { "tag": "FindAssetsByName", - "discriminant": 10, + "discriminant": 9, "type": "FindAssetsByName" }, { "tag": "FindAssetsByAccountId", - "discriminant": 11, + "discriminant": 10, "type": "FindAssetsByAccountId" }, { "tag": "FindAssetsByAssetDefinitionId", - "discriminant": 12, + "discriminant": 11, "type": "FindAssetsByAssetDefinitionId" }, { "tag": "FindAssetsByDomainId", - "discriminant": 13, + "discriminant": 12, "type": "FindAssetsByDomainId" }, { "tag": "FindAssetsByDomainIdAndAssetDefinitionId", - "discriminant": 14, + "discriminant": 13, "type": "FindAssetsByDomainIdAndAssetDefinitionId" }, { "tag": "FindAssetQuantityById", - "discriminant": 15, + "discriminant": 14, "type": "FindAssetQuantityById" }, { "tag": "FindTotalAssetQuantityByAssetDefinitionId", - "discriminant": 16, + "discriminant": 15, "type": "FindTotalAssetQuantityByAssetDefinitionId" }, { "tag": "FindAssetKeyValueByIdAndKey", - "discriminant": 17, + "discriminant": 16, "type": "FindAssetKeyValueByIdAndKey" }, { "tag": "FindAssetDefinitionKeyValueByIdAndKey", - "discriminant": 18, + "discriminant": 17, "type": "FindAssetDefinitionKeyValueByIdAndKey" }, { "tag": "FindAllDomains", - "discriminant": 19, + "discriminant": 18, "type": "FindAllDomains" }, { "tag": "FindDomainById", - "discriminant": 20, + "discriminant": 19, "type": "FindDomainById" }, { "tag": "FindDomainKeyValueByIdAndKey", - "discriminant": 21, + "discriminant": 20, "type": "FindDomainKeyValueByIdAndKey" }, { "tag": "FindAllPeers", - "discriminant": 22, + "discriminant": 21, "type": "FindAllPeers" }, { "tag": "FindAllBlocks", - "discriminant": 23, + "discriminant": 22, "type": "FindAllBlocks" }, { "tag": "FindAllBlockHeaders", - "discriminant": 24, + "discriminant": 23, "type": "FindAllBlockHeaders" }, { "tag": "FindBlockHeaderByHash", - "discriminant": 25, + "discriminant": 24, "type": "FindBlockHeaderByHash" }, { "tag": "FindAllTransactions", - "discriminant": 26, + "discriminant": 25, "type": "FindAllTransactions" }, { "tag": "FindTransactionsByAccountId", - "discriminant": 27, + "discriminant": 26, "type": "FindTransactionsByAccountId" }, { "tag": "FindTransactionByHash", - "discriminant": 28, + "discriminant": 27, "type": "FindTransactionByHash" }, { "tag": "FindPermissionTokensByAccountId", - "discriminant": 29, + "discriminant": 28, "type": "FindPermissionTokensByAccountId" }, { "tag": "FindPermissionTokenSchema", - "discriminant": 30, + "discriminant": 29, "type": "FindPermissionTokenSchema" }, { "tag": "FindAllActiveTriggerIds", - "discriminant": 31, + "discriminant": 30, "type": "FindAllActiveTriggerIds" }, { "tag": "FindTriggerById", - "discriminant": 32, + "discriminant": 31, "type": "FindTriggerById" }, { "tag": "FindTriggerKeyValueByIdAndKey", - "discriminant": 33, + "discriminant": 32, "type": "FindTriggerKeyValueByIdAndKey" }, { "tag": "FindTriggersByDomainId", - "discriminant": 34, + "discriminant": 33, "type": "FindTriggersByDomainId" }, { "tag": "FindAllRoles", - "discriminant": 35, + "discriminant": 34, "type": "FindAllRoles" }, { "tag": "FindAllRoleIds", - "discriminant": 36, + "discriminant": 35, "type": "FindAllRoleIds" }, { "tag": "FindRoleByRoleId", - "discriminant": 37, + "discriminant": 36, "type": "FindRoleByRoleId" }, { "tag": "FindRolesByAccountId", - "discriminant": 38, + "discriminant": 37, "type": "FindRolesByAccountId" }, { "tag": "FindAllParameters", - "discriminant": 39, + "discriminant": 38, "type": "FindAllParameters" } ] @@ -3577,20 +3492,6 @@ } ] }, - "SignatureCheckCondition": { - "Enum": [ - { - "tag": "AnyAccountSignatureOr", - "discriminant": 0, - "type": "Vec" - }, - { - "tag": "AllAccountSignaturesAnd", - "discriminant": 1, - "type": "Vec" - } - ] - }, "SignatureOf": "Signature", "SignatureOf": "Signature", "SignatureOf": "Signature", @@ -3602,14 +3503,6 @@ } ] }, - "SignaturesOf": { - "Struct": [ - { - "name": "signatures", - "type": "SortedVec>" - } - ] - }, "SignedBlock": { "Enum": [ { @@ -3664,8 +3557,8 @@ "SignedTransactionV1": { "Struct": [ { - "name": "signatures", - "type": "SignaturesOf" + "name": "signature", + "type": "SignatureOf" }, { "name": "payload", @@ -3773,15 +3666,9 @@ "SortedVec": { "Vec": "PermissionToken" }, - "SortedVec": { - "Vec": "PublicKey" - }, "SortedVec>": { "Vec": "SignatureOf" }, - "SortedVec>": { - "Vec": "SignatureOf" - }, "String": "String", "StringPredicate": { "Enum": [ @@ -4429,9 +4316,6 @@ "Vec": { "Vec": "PeerId" }, - "Vec": { - "Vec": "PublicKey" - }, "Vec": { "Vec": "QueryOutputBox" }, diff --git a/genesis/Cargo.toml b/genesis/Cargo.toml index 75b8186cc7e..604c98045e6 100644 --- a/genesis/Cargo.toml +++ b/genesis/Cargo.toml @@ -24,3 +24,4 @@ eyre = { workspace = true } [dev-dependencies] iroha_crypto = { workspace = true, features = ["rand"] } +test_samples = { workspace = true } diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index 1fe4cfe92ab..773adad746d 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -6,6 +6,7 @@ use std::{ fs::File, io::BufReader, path::{Path, PathBuf}, + str::FromStr, }; use eyre::{eyre, Report, Result, WrapErr}; @@ -20,12 +21,25 @@ use iroha_data_model::{ use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -/// [`DomainId`] of the genesis account. -pub static GENESIS_DOMAIN_ID: Lazy = Lazy::new(|| "genesis".parse().expect("Valid")); - -/// [`AccountId`] of the genesis account. -pub static GENESIS_ACCOUNT_ID: Lazy = - Lazy::new(|| AccountId::new(GENESIS_DOMAIN_ID.clone(), "genesis".parse().expect("Valid"))); +/// [`DomainId`](iroha_data_model::domain::DomainId) of the genesis account. +pub static GENESIS_DOMAIN_ID: Lazy = Lazy::new(|| "genesis".parse().unwrap()); + +/// [`AccountId`](iroha_data_model::account::AccountId) of the genesis account. +pub static GENESIS_ACCOUNT_ID: Lazy = Lazy::new(|| { + AccountId::new( + GENESIS_DOMAIN_ID.clone(), + GENESIS_ACCOUNT_KEYPAIR.public_key().clone(), + ) +}); + +/// [`KeyPair`] of the genesis account. +pub static GENESIS_ACCOUNT_KEYPAIR: Lazy = Lazy::new(|| { + KeyPair::new( + iroha_crypto::PublicKey::from_str("ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B").unwrap(), + iroha_crypto::PrivateKey::from_hex(iroha_crypto::Algorithm::Ed25519, "DD61D7A2244A504E78BA80383DFCC0228E25CA131E5A6AF503F71632D23BD76AE2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B").unwrap(), + ) + .unwrap() +}); /// Genesis transaction #[derive(Debug, Clone)] @@ -310,31 +324,15 @@ impl RawGenesisDomainBuilder { } } - /// Add an account to this domain with random public key. - #[cfg(test)] - fn account_with_random_public_key(mut self, account_name: Name) -> Self { - let account_id = AccountId::new(self.domain_id.clone(), account_name); - self.transaction.isi.push( - Register::account(Account::new(account_id, KeyPair::random().into_parts().0)).into(), - ); - self - } - /// Add an account to this domain - pub fn account(self, account_name: Name, public_key: PublicKey) -> Self { - self.account_with_metadata(account_name, public_key, Metadata::default()) + pub fn account(self, signatory: PublicKey) -> Self { + self.account_with_metadata(signatory, Metadata::default()) } /// Add an account (having provided `metadata`) to this domain. - pub fn account_with_metadata( - mut self, - account_name: Name, - public_key: PublicKey, - metadata: Metadata, - ) -> Self { - let account_id = AccountId::new(self.domain_id.clone(), account_name); - let register = - Register::account(Account::new(account_id, public_key).with_metadata(metadata)); + pub fn account_with_metadata(mut self, signatory: PublicKey, metadata: Metadata) -> Self { + let account_id = AccountId::new(self.domain_id.clone(), signatory); + let register = Register::account(Account::new(account_id).with_metadata(metadata)); self.transaction.isi.push(register.into()); self } @@ -352,6 +350,8 @@ impl RawGenesisDomainBuilder { #[cfg(test)] mod tests { + use test_samples::{ALICE_KEYPAIR, BOB_KEYPAIR}; + use super::*; fn dummy_executor() -> Executor { @@ -367,7 +367,7 @@ mod tests { let _genesis_block = GenesisNetwork::new( RawGenesisBlockBuilder::default() .domain("wonderland".parse()?) - .account("alice".parse()?, alice_public_key) + .account(alice_public_key) .finish_domain() .executor_blob(dummy_executor()) .build(), @@ -379,19 +379,26 @@ mod tests { #[test] fn genesis_block_builder_example() { - let public_key = "ed0120204E9593C3FFAF4464A6189233811C297DD4CE73ABA167867E4FBD4F8C450ACB"; + let public_key: std::collections::HashMap<&'static str, PublicKey> = [ + ("alice", ALICE_KEYPAIR.public_key().clone()), + ("bob", BOB_KEYPAIR.public_key().clone()), + ("cheshire_cat", KeyPair::random().into_parts().0), + ("mad_hatter", KeyPair::random().into_parts().0), + ] + .into_iter() + .collect(); let mut genesis_builder = RawGenesisBlockBuilder::default(); genesis_builder = genesis_builder .domain("wonderland".parse().unwrap()) - .account_with_random_public_key("alice".parse().unwrap()) - .account_with_random_public_key("bob".parse().unwrap()) + .account(public_key["alice"].clone()) + .account(public_key["bob"].clone()) .finish_domain() .domain("tulgey_wood".parse().unwrap()) - .account_with_random_public_key("Cheshire_Cat".parse().unwrap()) + .account(public_key["cheshire_cat"].clone()) .finish_domain() .domain("meadow".parse().unwrap()) - .account("Mad_Hatter".parse().unwrap(), public_key.parse().unwrap()) + .account(public_key["mad_hatter"].clone()) .asset( "hats".parse().unwrap(), AssetValueType::Numeric(NumericSpec::default()), @@ -408,18 +415,18 @@ mod tests { ); assert_eq!( finished_genesis_block.transactions[0].isi[1], - Register::account(Account::new( - AccountId::new(domain_id.clone(), "alice".parse().unwrap()), - KeyPair::random().into_parts().0, - )) + Register::account(Account::new(AccountId::new( + domain_id.clone(), + public_key["alice"].clone() + ),)) .into() ); assert_eq!( finished_genesis_block.transactions[0].isi[2], - Register::account(Account::new( - AccountId::new(domain_id, "bob".parse().unwrap()), - KeyPair::random().into_parts().0, - )) + Register::account(Account::new(AccountId::new( + domain_id, + public_key["bob"].clone() + ),)) .into() ); } @@ -431,10 +438,10 @@ mod tests { ); assert_eq!( finished_genesis_block.transactions[0].isi[4], - Register::account(Account::new( - AccountId::new(domain_id, "Cheshire_Cat".parse().unwrap()), - KeyPair::random().into_parts().0, - )) + Register::account(Account::new(AccountId::new( + domain_id, + public_key["cheshire_cat"].clone() + ),)) .into() ); } @@ -446,10 +453,10 @@ mod tests { ); assert_eq!( finished_genesis_block.transactions[0].isi[6], - Register::account(Account::new( - AccountId::new(domain_id, "Mad_Hatter".parse().unwrap()), - public_key.parse().unwrap(), - )) + Register::account(Account::new(AccountId::new( + domain_id, + public_key["mad_hatter"].clone() + ),)) .into() ); assert_eq!( diff --git a/schema/gen/src/lib.rs b/schema/gen/src/lib.rs index e3c56afd90c..0a95dd31b40 100644 --- a/schema/gen/src/lib.rs +++ b/schema/gen/src/lib.rs @@ -63,7 +63,6 @@ types!( AccountEventFilter, AccountEventSet, AccountId, - AccountMintBox, AccountPermissionChanged, AccountRoleChanged, Action, @@ -91,9 +90,7 @@ types!( BTreeMap, BTreeMap, BTreeSet, - BTreeSet, BTreeSet>, - BTreeSet>, BatchedResponse, BatchedResponseV1, BlockEvent, @@ -109,14 +106,12 @@ types!( Box, Burn, Burn, - Burn, BurnBox, ChainId, ConfigurationEvent, ConfigurationEventFilter, ConfigurationEventSet, ConstString, - ConstVec, ConstVec, Container, DataEvent, @@ -145,7 +140,6 @@ types!( FindAccountById, FindAccountKeyValueByIdAndKey, FindAccountsByDomainId, - FindAccountsByName, FindAccountsWithAsset, FindAllAccounts, FindAllActiveTriggerIds, @@ -219,8 +213,6 @@ types!( MetadataValueBox, Mint, Mint, - Mint, - Mint, MintBox, MintabilityError, Mintable, @@ -317,14 +309,11 @@ types!( SetKeyValueBox, SetParameter, Signature, - SignatureCheckCondition, SignatureOf, SignatureOf, SignatureOf, SignatureWrapperOf, - SignatureWrapperOf, SignaturesOf, - SignaturesOf, SignedBlock, SignedBlockV1, SignedQuery, diff --git a/smart_contract/executor/derive/src/default.rs b/smart_contract/executor/derive/src/default.rs index cb2778b85e5..9723b534c20 100644 --- a/smart_contract/executor/derive/src/default.rs +++ b/smart_contract/executor/derive/src/default.rs @@ -125,9 +125,6 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn::DeriveInput) -> Tok "fn visit_remove_domain_key_value(operation: &RemoveKeyValue)", "fn visit_register_account(operation: &Register)", "fn visit_unregister_account(operation: &Unregister)", - "fn visit_mint_account_public_key(operation: &Mint)", - "fn visit_burn_account_public_key(operation: &Burn)", - "fn visit_mint_account_signature_check_condition(operation: &Mint)", "fn visit_set_account_key_value(operation: &SetKeyValue)", "fn visit_remove_account_key_value(operation: &RemoveKeyValue)", "fn visit_register_asset(operation: &Register)", diff --git a/smart_contract/executor/derive/src/lib.rs b/smart_contract/executor/derive/src/lib.rs index 713e099e810..7e605a9adb0 100644 --- a/smart_contract/executor/derive/src/lib.rs +++ b/smart_contract/executor/derive/src/lib.rs @@ -88,7 +88,7 @@ pub fn entrypoint(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// CanDoSomethingWithAsset { /// some_data: "some data".to_owned(), -/// asset_id: parse!("rose#wonderland" as AssetId), +/// asset_id: parse!(AssetId, "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland"), /// }.is_owned_by(&authority) /// } /// ``` diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index 79eff62ad71..584a9048253 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -6,9 +6,8 @@ pub mod tokens; use alloc::format; pub use account::{ - visit_burn_account_public_key, visit_mint_account_public_key, - visit_mint_account_signature_check_condition, visit_register_account, - visit_remove_account_key_value, visit_set_account_key_value, visit_unregister_account, + visit_register_account, visit_remove_account_key_value, visit_set_account_key_value, + visit_unregister_account, }; pub use asset::{ visit_burn_asset_numeric, visit_mint_asset_numeric, visit_register_asset, @@ -498,85 +497,6 @@ pub mod account { deny!(executor, "Can't unregister another account"); } - pub fn visit_mint_account_public_key( - executor: &mut V, - authority: &AccountId, - isi: &Mint, - ) { - let account_id = isi.destination_id(); - - if is_genesis(executor) { - execute!(executor, isi); - } - match is_account_owner(account_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_mint_user_public_keys = tokens::account::CanMintUserPublicKeys { - account_id: account_id.clone(), - }; - if can_mint_user_public_keys.is_owned_by(authority) { - execute!(executor, isi); - } - - deny!(executor, "Can't mint public keys of another account"); - } - - pub fn visit_burn_account_public_key( - executor: &mut V, - authority: &AccountId, - isi: &Burn, - ) { - let account_id = isi.destination_id(); - - if is_genesis(executor) { - execute!(executor, isi); - } - match is_account_owner(account_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_burn_user_public_keys = tokens::account::CanBurnUserPublicKeys { - account_id: account_id.clone(), - }; - if can_burn_user_public_keys.is_owned_by(authority) { - execute!(executor, isi); - } - - deny!(executor, "Can't burn public keys of another account"); - } - - pub fn visit_mint_account_signature_check_condition( - executor: &mut V, - authority: &AccountId, - isi: &Mint, - ) { - let account_id = isi.destination_id(); - - if is_genesis(executor) { - execute!(executor, isi); - } - match is_account_owner(account_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_mint_user_signature_check_conditions_token = - tokens::account::CanMintUserSignatureCheckConditions { - account_id: account_id.clone(), - }; - if can_mint_user_signature_check_conditions_token.is_owned_by(authority) { - execute!(executor, isi); - } - - deny!( - executor, - "Can't mint signature check conditions of another account" - ); - } - pub fn visit_set_account_key_value( executor: &mut V, authority: &AccountId, @@ -794,7 +714,7 @@ pub mod asset_definition { isi: &Transfer, ) { let source_id = isi.source_id(); - let destination_id = isi.object(); + let asset_definition_id = isi.object(); if is_genesis(executor) { execute!(executor, isi); @@ -804,7 +724,7 @@ pub mod asset_definition { Ok(true) => execute!(executor, isi), Ok(false) => {} } - match is_asset_definition_owner(destination_id, authority) { + match is_asset_definition_owner(asset_definition_id, authority) { Err(err) => deny!(executor, err), Ok(true) => execute!(executor, isi), Ok(false) => {} diff --git a/smart_contract/src/lib.rs b/smart_contract/src/lib.rs index b97c3800336..19cf0744920 100644 --- a/smart_contract/src/lib.rs +++ b/smart_contract/src/lib.rs @@ -74,27 +74,37 @@ pub fn stub_getrandom(_dest: &mut [u8]) -> Result<(), getrandom::Error> { unimplemented!("{ERROR_MESSAGE}") } -/// Macro to parse literal as a type. Panics if failed. +/// Returns the annotated type of value parsed from the given expression, or fails with [`dbg_expect`](debug::DebugExpectExt::dbg_expect) message. +/// Panics if the internal parsing fails. /// -/// # Example +/// # Examples /// -/// ```ignore -/// use iroha_smart_contract::{prelude::*, parse}; +/// ``` +/// use iroha_smart_contract::{parse, prelude::*}; /// -/// let account_id = parse!("alice@wonderland" as AccountId); +/// let from_literal = parse!(DomainId, "wonderland"); +/// let expr = "wonderland"; +/// // Although "expr" would be less informative in debug message +/// let from_expr = parse!(DomainId, expr); /// ``` #[macro_export] macro_rules! parse { - ($l:literal as _) => { + (_, $e:expr) => { compile_error!( "Don't use `_` as a type in this macro, \ otherwise panic message would be less informative" ) }; - ($l:literal as $t:ty) => { + ($t:ty, $e:expr) => { $crate::debug::DebugExpectExt::dbg_expect( - $l.parse::<$t>(), - concat!("Failed to parse `", $l, "` as `", stringify!($t), "`"), + $e.parse::<$t>(), + concat!( + "Failed to parse `", + stringify!($e), + "` as `", + stringify!($t), + "`" + ), ) }; } @@ -489,14 +499,12 @@ mod tests { const ISI_RESULT: Result<(), ValidationFail> = Ok(()); fn get_test_instruction() -> InstructionBox { - let new_asset_id = "tulip##alice@wonderland".parse().unwrap(); - let register_isi = Register::asset(Asset::new(new_asset_id, 1_u32)); - - register_isi.into() + let new_asset_id: AssetId = "tulip##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse().unwrap(); + Register::asset(Asset::new(new_asset_id, 1_u32)).into() } fn get_test_query() -> QueryBox { - let asset_id: AssetId = "rose##alice@wonderland".parse().expect("Valid"); + let asset_id: AssetId = "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse().unwrap(); FindAssetQuantityById::new(asset_id).into() } diff --git a/test_samples/Cargo.toml b/test_samples/Cargo.toml new file mode 100644 index 00000000000..276af31906f --- /dev/null +++ b/test_samples/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "test_samples" +edition.workspace = true +version.workspace = true +authors.workspace = true +description.workspace = true +repository.workspace = true +documentation.workspace = true +homepage.workspace = true +license.workspace = true +keywords.workspace = true +categories.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +iroha_crypto = { workspace = true } +iroha_data_model = { workspace = true } + +once_cell = { workspace = true } +serde = { workspace = true, features = ["derive"] } +toml = { workspace = true } + +[lints] +workspace = true + +[features] +default = ["rand"] +rand = ["iroha_crypto/rand"] diff --git a/test_samples/src/lib.rs b/test_samples/src/lib.rs new file mode 100644 index 00000000000..ae381e8fa12 --- /dev/null +++ b/test_samples/src/lib.rs @@ -0,0 +1,43 @@ +//! Utility crate for standardized and random signatories. + +use iroha_crypto::{Algorithm, KeyPair, PrivateKey}; +use iroha_data_model::prelude::AccountId; +use once_cell::sync::Lazy; + +/// Generate [`AccountId`](iroha_data_model::account::AccountId) in the given `domain`. +/// +/// # Panics +/// +/// Panics if the given `domain` is invalid as [`Name`](iroha_data_model::name::Name). +#[cfg(feature = "rand")] +pub fn gen_account_in(domain: impl core::fmt::Display) -> (AccountId, KeyPair) { + let key_pair = KeyPair::random(); + let account_id = format!("{}@{}", key_pair.public_key(), domain) + .parse() + .expect("domain name should be valid"); + (account_id, key_pair) +} + +macro_rules! static_signatory_ed25519 { + ( $kp:ident, $vk:expr, $sk:expr ) => { + /// A standardized [`KeyPair`](iroha_crypto::KeyPair). + pub static $kp: Lazy = Lazy::new(|| { + KeyPair::new( + $vk.parse().unwrap(), + PrivateKey::from_hex(Algorithm::Ed25519, $sk).unwrap(), + ) + .unwrap() + }); + }; + ( $id:ident, $dm:literal, $kp:ident, $vk:literal, $sk:literal ) => { + /// A standardized [`AccountId`](iroha_data_model::account::AccountId). + pub static $id: Lazy = + Lazy::new(|| format!("{}@{}", $kp.public_key(), $dm).parse().unwrap()); + + static_signatory_ed25519!($kp, $vk, $sk); + }; +} +static_signatory_ed25519!(PEER_KEYPAIR, "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0", "9AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"); +static_signatory_ed25519!(ALICE_ID, "wonderland", ALICE_KEYPAIR, "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03", "CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03"); +static_signatory_ed25519!(BOB_ID, "wonderland", BOB_KEYPAIR, "ed012004FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016", "AF3F96DEEF44348FEB516C057558972CEC4C75C4DB9C5B3AAC843668854BF82804FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016"); +static_signatory_ed25519!(CARPENTER_ID, "garden_of_live_flowers", CARPENTER_KEYPAIR, "ed0120E9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99", "B5DD003D106B273F3628A29E6087C31CE12C9F32223BE26DD1ADB85CEBB48E1DE9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99"); diff --git a/tools/kagami/Cargo.toml b/tools/kagami/Cargo.toml index 7a4d4145ecf..1f076c54b85 100644 --- a/tools/kagami/Cargo.toml +++ b/tools/kagami/Cargo.toml @@ -19,6 +19,7 @@ iroha_data_model = { workspace = true } iroha_schema_gen = { workspace = true } iroha_primitives = { workspace = true } iroha_genesis = { workspace = true } +test_samples = { workspace = true } color-eyre = { workspace = true } clap = { workspace = true, features = ["derive"] } diff --git a/tools/kagami/src/genesis.rs b/tools/kagami/src/genesis.rs index 1a7292df8b8..a89251fa8a6 100644 --- a/tools/kagami/src/genesis.rs +++ b/tools/kagami/src/genesis.rs @@ -12,8 +12,11 @@ use iroha_data_model::{ parameter::{default::*, ParametersBuilder}, prelude::AssetId, }; -use iroha_genesis::{executor_state, RawGenesisBlockBuilder, RawGenesisBlockFile}; +use iroha_genesis::{ + executor_state, RawGenesisBlockBuilder, RawGenesisBlockFile, GENESIS_ACCOUNT_ID, +}; use serde_json::json; +use test_samples::{gen_account_in, ALICE_ID, BOB_ID, CARPENTER_ID}; use super::*; @@ -81,19 +84,15 @@ pub fn generate_default( let mut genesis = builder .domain_with_metadata("wonderland".parse()?, meta.clone()) - .account_with_metadata( - "alice".parse()?, - crate::DEFAULT_PUBLIC_KEY.parse()?, - meta.clone(), - ) - .account_with_metadata("bob".parse()?, crate::DEFAULT_PUBLIC_KEY.parse()?, meta) + .account_with_metadata(ALICE_ID.signatory().clone(), meta.clone()) + .account_with_metadata(BOB_ID.signatory().clone(), meta) .asset( "rose".parse()?, AssetValueType::Numeric(NumericSpec::default()), ) .finish_domain() .domain("garden_of_live_flowers".parse()?) - .account("carpenter".parse()?, crate::DEFAULT_PUBLIC_KEY.parse()?) + .account(CARPENTER_ID.signatory().clone()) .asset( "cabbage".parse()?, AssetValueType::Numeric(NumericSpec::default()), @@ -101,33 +100,37 @@ pub fn generate_default( .finish_domain() .build(); - let alice_id = AccountId::from_str("alice@wonderland")?; let mint = Mint::asset_numeric( 13u32, - AssetId::new("rose#wonderland".parse()?, alice_id.clone()), + AssetId::new("rose#wonderland".parse()?, ALICE_ID.clone()), ); let mint_cabbage = Mint::asset_numeric( 44u32, - AssetId::new("cabbage#garden_of_live_flowers".parse()?, alice_id.clone()), + AssetId::new("cabbage#garden_of_live_flowers".parse()?, ALICE_ID.clone()), ); let grant_permission_to_set_parameters = Grant::permission( PermissionToken::new("CanSetParameters".parse()?, &json!(null)), - alice_id.clone(), + ALICE_ID.clone(), + ); + let transfer_rose_ownership = Transfer::asset_definition( + GENESIS_ACCOUNT_ID.clone(), + "rose#wonderland".parse()?, + ALICE_ID.clone(), ); - let transfer_domain_ownerhip = Transfer::domain( - "genesis@genesis".parse()?, + let transfer_wonderland_ownership = Transfer::domain( + GENESIS_ACCOUNT_ID.clone(), "wonderland".parse()?, - alice_id.clone(), + ALICE_ID.clone(), ); let register_user_metadata_access = Register::role( Role::new("ALICE_METADATA_ACCESS".parse()?) .add_permission(PermissionToken::new( "CanSetKeyValueInAccount".parse()?, - &json!({ "account_id": alice_id }), + &json!({ "account_id": ALICE_ID.clone() }), )) .add_permission(PermissionToken::new( "CanRemoveKeyValueInAccount".parse()?, - &json!({ "account_id": alice_id }), + &json!({ "account_id": ALICE_ID.clone() }), )), ) .into(); @@ -176,7 +179,8 @@ pub fn generate_default( for isi in [ mint.into(), mint_cabbage.into(), - transfer_domain_ownerhip.into(), + transfer_rose_ownership.into(), + transfer_wonderland_ownership.into(), grant_permission_to_set_parameters.into(), ] .into_iter() @@ -207,12 +211,10 @@ fn generate_synthetic( first_transaction .append_instruction(Register::domain(Domain::new(domain_id.clone())).into()); - for account in 0..accounts_per_domain { - let (public_key, _) = iroha_crypto::KeyPair::random().into_parts(); - let account_id: AccountId = format!("account_{account}@{domain_id}").parse()?; - first_transaction.append_instruction( - Register::account(Account::new(account_id.clone(), public_key)).into(), - ); + for _ in 0..accounts_per_domain { + let (account_id, _account_keypair) = gen_account_in(&domain_id); + first_transaction + .append_instruction(Register::account(Account::new(account_id.clone())).into()); } for asset in 0..assets_per_domain { diff --git a/tools/kagami/src/main.rs b/tools/kagami/src/main.rs index 16228701582..270e140994e 100644 --- a/tools/kagami/src/main.rs +++ b/tools/kagami/src/main.rs @@ -1,10 +1,7 @@ //! CLI for generating iroha sample configuration, genesis and //! cryptographic key pairs. To be used with all compliant Iroha //! installations. -use std::{ - io::{stdout, BufWriter, Write}, - str::FromStr as _, -}; +use std::io::{stdout, BufWriter, Write}; use clap::{Args as ClapArgs, Parser}; use color_eyre::eyre::WrapErr as _; @@ -17,12 +14,6 @@ mod schema; /// Outcome shorthand used throughout this crate pub(crate) type Outcome = color_eyre::Result<()>; -// The reason for hard-coding this default is to ensure that the -// algorithm is matched to the public key in Ed25519 format. If -// you need to change either, you should definitely change both. -const DEFAULT_PUBLIC_KEY: &str = - "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"; - fn main() -> Outcome { color_eyre::install()?; let args = Args::parse(); diff --git a/tools/parity_scale_cli/Cargo.toml b/tools/parity_scale_cli/Cargo.toml index 2b797c55a8f..aa8bf8d2c29 100644 --- a/tools/parity_scale_cli/Cargo.toml +++ b/tools/parity_scale_cli/Cargo.toml @@ -38,3 +38,6 @@ parity-scale-codec = { workspace = true } serde_json = { workspace = true, features = ["std"]} serde = { workspace = true } eyre = { workspace = true } + +[dev-dependencies] +test_samples = { workspace = true } diff --git a/tools/parity_scale_cli/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