From d08dfcb6ed714eaf3e0649601afb9b552622dffc 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] [feature] #2085: Authenticate personal accounts by ID Signed-off-by: Shunkichi Sato <49983831+s8sato@users.noreply.github.com> --- .rustfmt.toml | 11 +- 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/http.rs | 29 +- 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 | 30 +- client_cli/README.md | 39 +- client_cli/pytests/README.md | 27 +- client_cli/pytests/common/consts.py | 4 +- client_cli/pytests/common/helpers.py | 4 +- .../json_isi_examples/unregister_asset.json | 4 +- client_cli/pytests/models/account.py | 11 +- .../pytests/src/client_cli/client_cli.py | 31 +- .../pytests/src/client_cli/configuration.py | 30 +- client_cli/pytests/src/client_cli/iroha.py | 2 +- client_cli/pytests/test/__init__.py | 1 - client_cli/pytests/test/accounts/conftest.py | 1 - .../accounts/test_accounts_query_filters.py | 20 +- .../test/accounts/test_register_accounts.py | 97 +--- .../test/assets/test_assets_query_filters.py | 2 +- .../pytests/test/assets/test_burn_assets.py | 4 +- .../pytests/test/assets/test_mint_assets.py | 15 +- .../assets/test_register_asset_definitions.py | 6 +- .../test/assets/test_transfer_assets.py | 24 +- .../test/assets/test_unregister_asset.py | 6 +- client_cli/pytests/test/conftest.py | 29 +- .../test/domains/test_register_domains.py | 8 +- .../test/triggers/test_register_trigger.py | 10 +- client_cli/src/main.rs | 63 +-- config/iroha_test_config.toml | 2 +- config/tests/fixtures.rs | 14 +- configs/client.template.toml | 2 +- configs/swarm/client.toml | 6 +- configs/swarm/executor.wasm | Bin 526777 -> 517109 bytes configs/swarm/genesis.json | 38 +- core/Cargo.toml | 2 + core/benches/blocks/apply_blocks.rs | 19 +- core/benches/blocks/apply_blocks_benchmark.rs | 2 +- core/benches/blocks/apply_blocks_oneshot.rs | 2 +- core/benches/blocks/common.rs | 27 +- core/benches/blocks/validate_blocks.rs | 15 +- core/benches/kura.rs | 17 +- core/benches/validation.rs | 78 ++- core/src/block.rs | 52 +- core/src/lib.rs | 7 +- core/src/queue.rs | 310 +++--------- core/src/smartcontracts/isi/account.rs | 117 +---- core/src/smartcontracts/isi/domain.rs | 7 +- core/src/smartcontracts/isi/mod.rs | 49 +- core/src/smartcontracts/isi/query.rs | 47 +- core/src/smartcontracts/isi/world.rs | 2 +- core/src/smartcontracts/wasm.rs | 43 +- 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 +- crypto/src/encryption/mod.rs | 6 +- data_model/derive/src/lib.rs | 45 +- .../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 | 154 +++--- 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 ++------ ffi/derive/src/lib.rs | 30 +- genesis/Cargo.toml | 1 + genesis/src/lib.rs | 105 +++-- primitives/src/unique_vec.rs | 19 +- schema/gen/src/lib.rs | 11 - smart_contract/derive/src/lib.rs | 1 - smart_contract/executor/derive/src/default.rs | 3 - smart_contract/executor/derive/src/lib.rs | 3 +- smart_contract/executor/src/default.rs | 88 +--- smart_contract/src/lib.rs | 36 +- telemetry/derive/src/lib.rs | 2 +- test_samples/Cargo.toml | 29 ++ test_samples/src/lib.rs | 43 ++ tools/kagami/Cargo.toml | 1 + tools/kagami/src/genesis.rs | 50 +- tools/kagami/src/main.rs | 11 +- tools/parity_scale_cli/Cargo.toml | 3 + tools/parity_scale_cli/samples/account.bin | Bin 64 -> 57 bytes tools/parity_scale_cli/samples/account.json | 5 +- tools/parity_scale_cli/samples/trigger.bin | Bin 76 -> 132 bytes tools/parity_scale_cli/samples/trigger.json | 4 +- tools/parity_scale_cli/src/main.rs | 11 +- torii/src/lib.rs | 2 +- torii/src/routing.rs | 1 + wasm_builder/src/lib.rs | 2 +- 149 files changed, 1560 insertions(+), 3072 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 2810f43da2d..bb9cf179f66 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,5 +1,6 @@ -newline_style="Unix" -use_field_init_shorthand=true -use_try_shorthand=true -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 +use_try_shorthand = true +group_imports = "StdExternalCrate" # unstable https://github.com/rust-lang/rustfmt/issues/5083 +imports_granularity = "Crate" # unstable https://github.com/rust-lang/rustfmt/issues/4991 +format_code_in_doc_comments = true # unstable https://github.com/rust-lang/rustfmt/issues/3348 diff --git a/Cargo.lock b/Cargo.lock index 54d744746bd..3b539236016 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2841,6 +2841,7 @@ dependencies = [ "serde_with", "tempfile", "test_network", + "test_samples", "thiserror", "tokio", "tokio-tungstenite", @@ -2953,6 +2954,7 @@ dependencies = [ "serde_json", "storage", "tempfile", + "test_samples", "thiserror", "tokio", "uuid", @@ -3162,6 +3164,7 @@ dependencies = [ "once_cell", "serde", "serde_json", + "test_samples", "tracing", ] @@ -3667,6 +3670,7 @@ dependencies = [ "parity-scale-codec", "serde", "serde_json", + "test_samples", ] [[package]] @@ -4236,6 +4240,7 @@ dependencies = [ "parity-scale-codec", "serde", "serde_json", + "test_samples", ] [[package]] @@ -5728,10 +5733,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 a7ab70ee433..6392fd812d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,8 @@ iroha_executor_derive = { version = "=2.0.0-pre-rc.21", path = "smart_contract/e iroha_trigger_derive = { version = "=2.0.0-pre-rc.21", path = "smart_contract/trigger/derive" } test_network = { version = "=2.0.0-pre-rc.21", path = "core/test_network" } +test_samples = { version = "=2.0.0-pre-rc.21", path = "test_samples" } + proc-macro2 = "1.0.81" syn = { version = "2.0.60", default-features = false } quote = "1.0.36" @@ -228,6 +230,7 @@ members = [ "smart_contract/executor", "smart_contract/executor/derive", "telemetry", + "test_samples", "tools/kagami", "tools/kura_inspector", "tools/parity_scale_cli", diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 947f0a96321..e54aac98f66 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -210,10 +210,7 @@ impl Iroha { .wrap_err("Unable to start P2P-network")?; let (events_sender, _) = broadcast::channel(10000); - let world = World::with( - [genesis_domain(config.genesis.public_key().clone())], - config.sumeragi.trusted_peers.clone(), - ); + let world = World::with([genesis_domain()], config.sumeragi.trusted_peers.clone()); let kura = Kura::new(&config.kura)?; let kura_thread_handler = Kura::start(Arc::clone(&kura)); @@ -485,19 +482,18 @@ impl Iroha { } } -fn genesis_account(public_key: PublicKey) -> Account { - Account::new(iroha_genesis::GENESIS_ACCOUNT_ID.clone(), public_key) +fn genesis_account() -> Account { + Account::new(iroha_genesis::GENESIS_ACCOUNT_ID.clone()) .build(&iroha_genesis::GENESIS_ACCOUNT_ID) } -fn genesis_domain(public_key: PublicKey) -> Domain { +fn genesis_domain() -> Domain { let mut domain = Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()) .build(&iroha_genesis::GENESIS_ACCOUNT_ID); - domain.accounts.insert( - iroha_genesis::GENESIS_ACCOUNT_ID.clone(), - genesis_account(public_key), - ); + domain + .accounts + .insert(iroha_genesis::GENESIS_ACCOUNT_ID.clone(), genesis_account()); domain } diff --git a/cli/src/samples.rs b/cli/src/samples.rs index 26fc0acd300..fe45f22e104 100644 --- a/cli/src/samples.rs +++ b/cli/src/samples.rs @@ -62,18 +62,24 @@ pub fn get_trusted_peers(public_key: Option<&PublicKey>) -> HashSet { pub fn get_user_config( peers: &UniqueVec, chain_id: Option, - key_pair: Option, + peer_key_pair: Option, + genesis_key_pair: Option, ) -> UserConfig { let chain_id = chain_id.unwrap_or_else(|| ChainId::from("0")); - let (public_key, private_key) = key_pair.unwrap_or_else(KeyPair::random).into_parts(); - iroha_logger::info!(%public_key); + let (peer_public_key, peer_private_key) = + peer_key_pair.unwrap_or_else(KeyPair::random).into_parts(); + iroha_logger::info!(%peer_public_key); + let (genesis_public_key, genesis_private_key) = genesis_key_pair + .unwrap_or_else(KeyPair::random) + .into_parts(); + iroha_logger::info!(%genesis_public_key); let mut config = UserConfig::new(); config.chain_id.set(chain_id); - config.public_key.set(public_key.clone()); - config.private_key.set(private_key.clone()); + config.public_key.set(peer_public_key); + config.private_key.set(peer_private_key); config.network.address.set(DEFAULT_P2P_ADDR); config .chain_wide @@ -89,8 +95,8 @@ pub fn get_user_config( .network .block_gossip_period .set(HumanDuration(Duration::from_millis(500))); - config.genesis.private_key.set(private_key); - config.genesis.public_key.set(public_key); + config.genesis.private_key.set(genesis_private_key); + config.genesis.public_key.set(genesis_public_key); config.genesis.file.set("./genesis.json".into()); // There is no need in persistency in tests // If required to should be set explicitly not to overlap with other existing tests @@ -109,9 +115,10 @@ pub fn get_user_config( pub fn get_config( trusted_peers: &UniqueVec, chain_id: Option, - key_pair: Option, + peer_key_pair: Option, + genesis_key_pair: Option, ) -> Config { - get_user_config(trusted_peers, chain_id, key_pair) + get_user_config(trusted_peers, chain_id, peer_key_pair, genesis_key_pair) .unwrap_partial() .expect("config should build as all required fields were provided") .parse(CliContext { diff --git a/client/Cargo.toml b/client/Cargo.toml index cb1363174e2..f9f316fa0a9 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -56,6 +56,7 @@ iroha_logger = { workspace = true } iroha_telemetry = { workspace = true } iroha_torii_const = { workspace = true } iroha_version = { workspace = true, features = ["http"] } +test_samples = { workspace = true } attohttpc = { version = "0.28.0", default-features = false } eyre = { workspace = true } diff --git a/client/benches/torii.rs b/client/benches/torii.rs index bc3f5117cfe..a8bc5c97ab0 100644 --- a/client/benches/torii.rs +++ b/client/benches/torii.rs @@ -6,13 +6,13 @@ use criterion::{criterion_group, criterion_main, Criterion, Throughput}; use iroha::samples::{construct_executor, get_config}; use iroha_client::{ client::{asset, Client}, - crypto::KeyPair, data_model::prelude::*, }; use iroha_genesis::{GenesisNetwork, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; use iroha_version::Encode; use test_network::{get_chain_id, get_key_pair, Peer as TestPeer, PeerBuilder, TestRuntime}; +use test_samples::gen_account_in; use tokio::runtime::Runtime; const MINIMUM_SUCCESS_REQUEST_RATIO: f32 = 0.9; @@ -24,17 +24,15 @@ fn query_requests(criterion: &mut Criterion) { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let rt = Runtime::test(); let genesis = GenesisNetwork::new( RawGenesisBlockBuilder::default() .domain("wonderland".parse().expect("Valid")) - .account( - "alice".parse().expect("Valid"), - get_key_pair().public_key().clone(), - ) + .account(get_key_pair(test_network::Signatory::Alice).into_parts().0) .finish_domain() .executor_blob( construct_executor("../default_executor").expect("Failed to construct executor"), @@ -60,11 +58,10 @@ fn query_requests(criterion: &mut Criterion) { }); let mut group = criterion.benchmark_group("query-requests"); let domain_id: DomainId = "domain".parse().expect("Valid"); - let create_domain = Register::domain(Domain::new(domain_id.clone())); - let account_id = AccountId::new(domain_id.clone(), "account".parse().expect("Valid")); - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)); - let asset_definition_id = AssetDefinitionId::new(domain_id, "xor".parse().expect("Valid")); + let create_domain = Register::domain(Domain::new(domain_id)); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())); + let asset_definition_id: AssetDefinitionId = "xor#domain".parse().expect("Valid"); let create_asset = Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); let mint_asset = Mint::asset_numeric( @@ -73,7 +70,7 @@ fn query_requests(criterion: &mut Criterion) { ); let client_config = iroha_client::samples::get_client_config( get_chain_id(), - get_key_pair(), + get_key_pair(test_network::Signatory::Alice), format!("http://{}", peer.api_address).parse().unwrap(), ); @@ -132,15 +129,13 @@ fn instruction_submits(criterion: &mut Criterion) { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let genesis = GenesisNetwork::new( RawGenesisBlockBuilder::default() .domain("wonderland".parse().expect("Valid")) - .account( - "alice".parse().expect("Valid"), - configuration.common.key_pair.public_key().clone(), - ) + .account(configuration.common.key_pair.public_key().clone()) .finish_domain() .executor_blob( construct_executor("../default_executor").expect("Failed to construct executor"), @@ -158,14 +153,13 @@ fn instruction_submits(criterion: &mut Criterion) { rt.block_on(builder.start_with_peer(&mut peer)); let mut group = criterion.benchmark_group("instruction-requests"); let domain_id: DomainId = "domain".parse().expect("Valid"); - let create_domain: InstructionBox = Register::domain(Domain::new(domain_id.clone())).into(); - let account_id = AccountId::new(domain_id.clone(), "account".parse().expect("Valid")); - let (public_key, _) = KeyPair::random().into_parts(); - let create_account = Register::account(Account::new(account_id.clone(), public_key)).into(); - let asset_definition_id = AssetDefinitionId::new(domain_id, "xor".parse().expect("Valid")); + let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); + let (account_id, _account_keypair) = gen_account_in("domain"); + let create_account = Register::account(Account::new(account_id.clone())).into(); + let asset_definition_id: AssetDefinitionId = "xor#domain".parse().expect("Valid"); let client_config = iroha_client::samples::get_client_config( get_chain_id(), - get_key_pair(), + get_key_pair(test_network::Signatory::Alice), format!("http://{}", peer.api_address).parse().unwrap(), ); let iroha_client = Client::new(client_config); diff --git a/client/benches/tps/utils.rs b/client/benches/tps/utils.rs index ca63d75d89b..2295a639c77 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -1,4 +1,4 @@ -use std::{fmt, fs::File, io::BufReader, path::Path, str::FromStr as _, sync::mpsc, thread, time}; +use std::{fmt, fs::File, io::BufReader, path::Path, sync::mpsc, thread, time}; use eyre::{Result, WrapErr}; use iroha_client::{ @@ -8,10 +8,12 @@ use iroha_client::{ prelude::*, }, }; +use iroha_crypto::KeyPair; use iroha_data_model::events::pipeline::{BlockEventFilter, BlockStatus}; use nonzero_ext::nonzero; use serde::Deserialize; use test_network::*; +use test_samples::ALICE_ID; pub type Tps = f64; @@ -68,6 +70,7 @@ impl Config { config: self, client, name, + signatory: KeyPair::random().into_parts().0, }; unit.ready() }) @@ -136,6 +139,7 @@ struct MeasurerUnit { pub config: Config, pub client: Client, pub name: UnitName, + pub signatory: PublicKey, } type UnitName = u32; @@ -146,15 +150,10 @@ impl MeasurerUnit { /// Submit initial transactions for measurement fn ready(self) -> Result { - let keypair = iroha_client::crypto::KeyPair::random(); - - let account_id = account_id(self.name); - let asset_id = asset_id(self.name); - - let register_me = Register::account(Account::new(account_id, keypair.public_key().clone())); + let register_me = Register::account(Account::new(self.account_id())); self.client.submit_blocking(register_me)?; - let mint_a_rose = Mint::asset_numeric(1_u32, asset_id); + let mint_a_rose = Mint::asset_numeric(1_u32, self.asset_id()); self.client.submit_blocking(mint_a_rose)?; Ok(self) @@ -193,7 +192,7 @@ impl MeasurerUnit { let submitter = self.client.clone(); let interval_us_per_tx = self.config.interval_us_per_tx; let instructions = self.instructions(); - let alice_id = AccountId::from_str("alice@wonderland").expect("Failed to parse account id"); + let alice_id = ALICE_ID.clone(); let mut nonce = nonzero!(1_u32); @@ -231,17 +230,14 @@ impl MeasurerUnit { } fn mint(&self) -> InstructionBox { - Mint::asset_numeric(1_u32, asset_id(self.name)).into() + Mint::asset_numeric(1_u32, self.asset_id()).into() } -} -fn asset_id(account_name: UnitName) -> AssetId { - AssetId::new( - "rose#wonderland".parse().expect("Valid"), - account_id(account_name), - ) -} + fn account_id(&self) -> AccountId { + AccountId::new("wonderland".parse().expect("Valid"), self.signatory.clone()) + } -fn account_id(name: UnitName) -> AccountId { - format!("{name}@wonderland").parse().expect("Valid") + fn asset_id(&self) -> AssetId { + AssetId::new("rose#wonderland".parse().expect("Valid"), self.account_id()) + } } diff --git a/client/examples/million_accounts_genesis.rs b/client/examples/million_accounts_genesis.rs index 5a6b1dc6692..a44d682fa1e 100644 --- a/client/examples/million_accounts_genesis.rs +++ b/client/examples/million_accounts_genesis.rs @@ -16,14 +16,11 @@ use tokio::runtime::Runtime; fn generate_genesis(num_domains: u32) -> RawGenesisBlock { let mut builder = RawGenesisBlockBuilder::default(); - let key_pair = get_key_pair(); + let signatory_alice = get_key_pair(test_network::Signatory::Alice).into_parts().0; for i in 0_u32..num_domains { builder = builder .domain(format!("wonderland-{i}").parse().expect("Valid")) - .account( - format!("Alice-{i}").parse().expect("Valid"), - key_pair.public_key().clone(), - ) + .account(signatory_alice.clone()) .asset( format!("xor-{i}").parse().expect("Valid"), AssetValueType::Numeric(NumericSpec::default()), @@ -45,7 +42,8 @@ fn main_genesis() { let configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(get_key_pair()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); let rt = Runtime::test(); let genesis = GenesisNetwork::new( @@ -74,16 +72,9 @@ fn create_million_accounts_directly() { wait_for_genesis_committed(&vec![test_client.clone()], 0); for i in 0_u32..1_000_000_u32 { let domain_id: DomainId = format!("wonderland-{i}").parse().expect("Valid"); - let normal_account_id = AccountId::new( - domain_id.clone(), - format!("bob-{i}").parse().expect("Valid"), - ); + let normal_account_id = AccountId::new(domain_id.clone(), KeyPair::random().into_parts().0); let create_domain: InstructionBox = Register::domain(Domain::new(domain_id)).into(); - let create_account = Register::account(Account::new( - normal_account_id.clone(), - KeyPair::random().into_parts().0, - )) - .into(); + let create_account = Register::account(Account::new(normal_account_id.clone())).into(); if test_client .submit_all([create_domain, create_account]) .is_err() diff --git a/client/examples/register_1000_triggers.rs b/client/examples/register_1000_triggers.rs index 6de1efd77bc..56ab3c01567 100644 --- a/client/examples/register_1000_triggers.rs +++ b/client/examples/register_1000_triggers.rs @@ -8,8 +8,8 @@ use iroha_data_model::trigger::TriggerId; use iroha_genesis::{GenesisNetwork, RawGenesisBlock, RawGenesisBlockBuilder}; use iroha_primitives::unique_vec; use test_network::{ - get_chain_id, wait_for_genesis_committed_with_max_retries, Peer as TestPeer, PeerBuilder, - TestClient, TestRuntime, + get_chain_id, get_key_pair, wait_for_genesis_committed_with_max_retries, Peer as TestPeer, + PeerBuilder, TestClient, TestRuntime, }; use tokio::runtime::Runtime; @@ -55,13 +55,14 @@ fn generate_genesis(num_triggers: u32) -> Result Result<(), Box> { - let mut peer = ::new().expect("Failed to create peer"); + let mut peer: TestPeer = ::new().expect("Failed to create peer"); let chain_id = get_chain_id(); let mut configuration = get_config( &unique_vec![peer.id.clone()], Some(chain_id.clone()), - Some(peer.key_pair.clone()), + Some(get_key_pair(test_network::Signatory::Peer)), + Some(get_key_pair(test_network::Signatory::Genesis)), ); // Increase executor limits for large genesis diff --git a/client/examples/tutorial.rs b/client/examples/tutorial.rs index f4b36283ac6..0af57992466 100644 --- a/client/examples/tutorial.rs +++ b/client/examples/tutorial.rs @@ -76,13 +76,16 @@ fn domain_registration_test(config: Config) -> Result<(), Error> { fn account_definition_test() -> Result<(), Error> { // #region account_definition_comparison use iroha_client::data_model::prelude::AccountId; + use iroha_crypto::KeyPair; - // Create an `iroha_client::data_model::AccountId` instance - // with a DomainId instance and a Domain ID for an account - let longhand_account_id = AccountId::new("white_rabbit".parse()?, "looking_glass".parse()?); - let account_id: AccountId = "white_rabbit@looking_glass" + // Generate a new public key for a new account + let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by providing a DomainId instance and the public key + let longhand_account_id = AccountId::new("looking_glass".parse()?, public_key.clone()); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // Check that two ways to define an account match assert_eq!(account_id, longhand_account_id); @@ -109,19 +112,17 @@ fn account_registration_test(config: Config) -> Result<(), Error> { let iroha_client = Client::new(config); // #region register_account_create - // Create an AccountId instance by providing the account and domain name - let account_id: AccountId = "white_rabbit@looking_glass" - .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); - // #endregion register_account_create - - // TODO: consider getting a key from white_rabbit // Generate a new public key for a new account let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") + .parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); + // #endregion register_account_create // #region register_account_generate // Generate a new account - let create_account = Register::account(Account::new(account_id, public_key)); + let create_account = Register::account(Account::new(account_id)); // #endregion register_account_generate // #region register_account_prepare_tx @@ -151,6 +152,7 @@ fn asset_registration_test(config: Config) -> Result<(), Error> { numeric, AccountId, AssetDefinition, AssetDefinitionId, AssetId, Mint, Register, }, }; + use iroha_crypto::KeyPair; // #endregion register_asset_crates // Create an Iroha client @@ -171,10 +173,12 @@ fn asset_registration_test(config: Config) -> Result<(), Error> { iroha_client.submit(register_time)?; // #endregion register_asset_init_submit - // Create an account using the previously defined asset - let account_id: AccountId = "white_rabbit@looking_glass" + // Generate a new public key for a new account + let (public_key, _) = KeyPair::random().into_parts(); + // Create an AccountId instance by parsing the serialized format "signatory@domain" + let account_id: AccountId = format!("{public_key}@looking_glass") .parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #region register_asset_mint_submit // Create a MintBox using a previous asset and account @@ -205,8 +209,8 @@ fn asset_minting_test(config: Config) -> Result<(), Error> { // #region mint_asset_define_asset_account let roses = AssetDefinitionId::from_str("rose#wonderland") .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after"); - let alice: AccountId = "alice@wonderland".parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + let alice: AccountId = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #endregion mint_asset_define_asset_account // Mint the Asset instance @@ -222,11 +226,14 @@ fn asset_minting_test(config: Config) -> Result<(), Error> { // #region mint_asset_mint_alt // Mint the Asset instance (alternate syntax). - // The syntax is `asset_name#asset_domain#account_name@account_domain`, + // The syntax is `asset_name#asset_domain#account_signatory@account_domain`, // or `roses.to_string() + "#" + alice.to_string()`. // The `##` is a short-hand for the rose `which belongs to the same domain as the account // to which it belongs to. - let mint_roses_alt = Mint::asset_numeric(10u32, "rose##alice@wonderland".parse()?); + let alice_roses: AssetId = + "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + .parse()?; + let mint_roses_alt = Mint::asset_numeric(10u32, alice_roses); // #endregion mint_asset_mint_alt // #region mint_asset_submit_tx_alt @@ -256,8 +263,8 @@ fn asset_burning_test(config: Config) -> Result<(), Error> { // Define the instances of an Asset and Account let roses = AssetDefinitionId::from_str("rose#wonderland") .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after"); - let alice: AccountId = "alice@wonderland".parse() - .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after"); + let alice: AccountId = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland".parse() + .expect("Valid, because before @ is a valid public key and after @ is a valid name i.e. a string with no spaces or forbidden chars"); // #endregion burn_asset_define_asset_account // #region burn_asset_burn @@ -273,11 +280,14 @@ fn asset_burning_test(config: Config) -> Result<(), Error> { // #region burn_asset_burn_alt // Burn the Asset instance (alternate syntax). - // The syntax is `asset_name#asset_domain#account_name@account_domain`, + // The syntax is `asset_name#asset_domain#account_signatory@account_domain`, // or `roses.to_string() + "#" + alice.to_string()`. // The `##` is a short-hand for the rose `which belongs to the same domain as the account // to which it belongs to. - let burn_roses_alt = Burn::asset_numeric(10u32, "rose##alice@wonderland".parse()?); + let alice_roses: AssetId = + "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + .parse()?; + let burn_roses_alt = Burn::asset_numeric(10u32, alice_roses); // #endregion burn_asset_burn_alt // #region burn_asset_submit_tx_alt diff --git a/client/src/client.rs b/client/src/client.rs index 2a4eed202d7..b725f9592aa 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -1575,6 +1575,7 @@ mod tests { use std::str::FromStr; use iroha_primitives::small::SmallStr; + use test_samples::gen_account_in; use super::*; use crate::config::{BasicAuth, Config, WebLogin}; @@ -1585,12 +1586,11 @@ mod tests { const ENCRYPTED_CREDENTIALS: &str = "bWFkX2hhdHRlcjppbG92ZXRlYQ=="; fn config_factory() -> Config { + let (account_id, key_pair) = gen_account_in("wonderland"); Config { chain_id: ChainId::from("0"), - key_pair: KeyPair::random(), - account_id: "alice@wonderland" - .parse() - .expect("This account ID should be valid"), + key_pair, + account_id, torii_api_url: "http://127.0.0.1:8080".parse().unwrap(), basic_auth: None, transaction_add_nonce: false, diff --git a/client/src/config.rs b/client/src/config.rs index 72bb909d8c7..7babfcc4a5d 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -109,9 +109,9 @@ mod tests { password = "ilovetea" [account] - id = "alice@wonderland" - public_key = "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" - private_key = { algorithm = "ed25519", payload = "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" } + domain_id = "wonderland" + public_key = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" + private_key = { algorithm = "ed25519", payload = "CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" } [transaction] time_to_live = 100_000 diff --git a/client/src/config/user.rs b/client/src/config/user.rs index 30a684e5bac..1ebb11e3a9c 100644 --- a/client/src/config/user.rs +++ b/client/src/config/user.rs @@ -8,7 +8,7 @@ pub use boilerplate::*; use eyre::{eyre, Context, Report}; use iroha_config::base::{Emitter, ErrorsCollection}; use iroha_crypto::{KeyPair, PrivateKey, PublicKey}; -use iroha_data_model::{account::AccountId, ChainId}; +use iroha_data_model::prelude::{AccountId, ChainId, DomainId}; use merge::Merge; use serde_with::DeserializeFromStr; use url::Url; @@ -67,7 +67,7 @@ impl Root { basic_auth, account: Account { - id: account_id, + domian_id, public_key, private_key, }, @@ -93,6 +93,8 @@ impl Root { )) } + let account_id = AccountId::new(domian_id, public_key.clone()); + let key_pair = KeyPair::new(public_key, private_key) .wrap_err("failed to construct a key pair") .map_or_else( @@ -121,7 +123,7 @@ impl Root { #[derive(Debug, Clone)] #[allow(missing_docs)] pub struct Account { - pub id: AccountId, + pub domian_id: DomainId, pub public_key: PublicKey, pub private_key: PrivateKey, } diff --git a/client/src/config/user/boilerplate.rs b/client/src/config/user/boilerplate.rs index 500b13afecb..635f9ebb153 100644 --- a/client/src/config/user/boilerplate.rs +++ b/client/src/config/user/boilerplate.rs @@ -9,7 +9,7 @@ use iroha_config::base::{ UserField, }; use iroha_crypto::{PrivateKey, PublicKey}; -use iroha_data_model::{account::AccountId, ChainId}; +use iroha_data_model::{domain::DomainId, ChainId}; use serde::Deserialize; use crate::config::{ @@ -89,7 +89,7 @@ impl UnwrapPartial for RootPartial { #[derive(Debug, Clone, Deserialize, Eq, PartialEq, Default, Merge)] #[serde(deny_unknown_fields, default)] pub struct AccountPartial { - pub id: UserField, + pub domain_id: UserField, pub public_key: UserField, pub private_key: UserField, } @@ -100,8 +100,8 @@ impl UnwrapPartial for AccountPartial { fn unwrap_partial(self) -> UnwrapPartialResult { let mut emitter = Emitter::new(); - if self.id.is_none() { - emitter.emit_missing_field("account.id"); + if self.domain_id.is_none() { + emitter.emit_missing_field("account.domain_id"); } if self.public_key.is_none() { emitter.emit_missing_field("account.public_key"); @@ -113,7 +113,7 @@ impl UnwrapPartial for AccountPartial { emitter.finish()?; Ok(Account { - id: self.id.get().unwrap(), + domian_id: self.domain_id.get().unwrap(), public_key: self.public_key.get().unwrap(), private_key: self.private_key.get().unwrap(), }) diff --git a/client/src/http.rs b/client/src/http.rs index 9547195da52..d974db4c4f1 100644 --- a/client/src/http.rs +++ b/client/src/http.rs @@ -95,10 +95,7 @@ pub mod ws { /// ```rust /// use eyre::{eyre, Result}; /// use iroha_client::http::{ - /// ws::conn_flow::{ - /// Events as FlowEvents, Init as FlowInit, - /// InitData, - /// }, + /// ws::conn_flow::{Events as FlowEvents, Init as FlowInit, InitData}, /// Method, RequestBuilder, /// }; /// @@ -109,7 +106,12 @@ pub mod ws { /// /// fn init(self) -> InitData { /// InitData::new( - /// R::new(Method::GET, "http://localhost:3000".parse().expect("`localhost` is a valid URL, port `3000` is sensible, `http` is supported")), + /// R::new( + /// Method::GET, + /// "http://localhost:3000".parse().expect( + /// "`localhost` is a valid URL, port `3000` is sensible, `http` is supported", + /// ), + /// ), /// vec![1, 2, 3], /// Events, /// ) @@ -139,39 +141,38 @@ pub mod ws { /// /// ```rust /// use eyre::Result; - /// use url::Url; /// use iroha_client::{ - /// data_model::prelude::EventBox, /// client::events_api::flow as events_api_flow, + /// data_model::prelude::EventBox, /// http::{ /// ws::conn_flow::{Events, Init, InitData}, - /// RequestBuilder, Method + /// Method, RequestBuilder, /// }, /// }; + /// use url::Url; /// /// // Some request builder /// struct MyBuilder; /// /// impl RequestBuilder for MyBuilder { /// fn new(_: Method, url: Url) -> Self { - /// todo!() + /// todo!() /// } /// - /// fn param, V: ?Sized + ToString>(self, _: K, _: &V) -> Self { - /// todo!() + /// fn param, V: ?Sized + ToString>(self, _: K, _: &V) -> Self { + /// todo!() /// } /// /// fn header, V: ?Sized + ToString>(self, _: N, _: &V) -> Self { - /// todo!() + /// todo!() /// } /// /// fn body(self, data: Vec) -> Self { - /// todo!() + /// todo!() /// } /// } /// /// impl MyBuilder { - /// /// fn connect(self) -> MyStream { /// /* ... */ /// MyStream {} 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..ff0f7913583 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 { + // crate::integration::upgrade::ADMIN_ID + if *authority == parse!(AccountId, "ed012076E5CA9698296AF9BE2CA45F525CB3BCFDEB7EE068BA56F973E9DD90564EF4FC@admin") { 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 32e169b25a4..b9890a5d681 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::>>() diff --git a/client_cli/README.md b/client_cli/README.md index 6cb5dfbc644..9cb7fdf4d29 100644 --- a/client_cli/README.md +++ b/client_cli/README.md @@ -60,20 +60,27 @@ Check the [Bash guide in Iroha Tutorial](https://hyperledger.github.io/iroha-2-d ```bash ./iroha_client_cli domain register --id="Soramitsu" -./iroha_client_cli account register --id="White Rabbit@Soramitsu" --key="" +./iroha_client_cli account register --id="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" ./iroha_client_cli asset register --id="XOR#Soramitsu" --value-type=Numeric -./iroha_client_cli asset mint --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 -./iroha_client_cli asset get --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" +./iroha_client_cli asset mint --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 +./iroha_client_cli asset get --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" ``` In this section we will show you how to use Iroha CLI Client to do the following: -- [Create new Domain](#create-new-domain) -- [Create new Account](#create-new-account) -- [Mint Asset to Account](#mint-asset-to-account) -- [Query Account Assets Quantity](#query-account-assets-quantity) -- [Execute WASM transaction](#execute-wasm-transaction) -- [Execute Multi-instruction Transactions](#execute-multi-instruction-instructions) +- [Iroha CLI Client](#iroha-cli-client) + - [Features](#features) + - [Installation](#installation) + - [Usage](#usage) + - [Options](#options) + - [Subcommands](#subcommands) + - [Examples](#examples) + - [Create new Domain](#create-new-domain) + - [Create new Account](#create-new-account) + - [Mint Asset to Account](#mint-asset-to-account) + - [Query Account Assets Quantity](#query-account-assets-quantity) + - [Execute WASM transaction](#execute-wasm-transaction) + - [Execute Multi-instruction Transactions](#execute-multi-instruction-transactions) ### Create new Domain @@ -89,12 +96,10 @@ Now you have a domain without any accounts. ### Create new Account -Let's create a new account. Like in the previous example, specify the entity type (`account`) and the command (`register`). Then define the account name as the value of the `id` argument. - -Additionally, you need to provide the `key` argument with the account's public key as a double-quoted multihash representation of the key. Providing an empty string also works (but is highly discouraged), while omitting the argument altogether will produce an error. +Let's create a new account. Like in the previous example, specify the entity type (`account`) and the command (`register`). Then define the value of the `id` argument in "signatory@domain" format, where signatory is the account's public key in multihash representation. ```bash -./iroha_client_cli account register --id="White Rabbit@Soramitsu" --key="" +./iroha_client_cli account register --id="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" ``` ### Mint Asset to Account @@ -107,10 +112,10 @@ Every asset has its own value spec. In this example, it is defined as `Numeric`, ```bash ./iroha_client_cli asset register --id="XOR#Soramitsu" --value-type=Numeric -./iroha_client_cli asset mint --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 +./iroha_client_cli asset mint --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" --quantity=1010 ``` -You created `XOR#Soramitsu`, an asset of type `Numeric`, and then gave `1010` units of this asset to the account `White Rabbit@Soramitsu`. +You created `XOR#Soramitsu`, an asset of type `Numeric`, and then gave `1010` units of this asset to the account `ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu`. ### Query Account Assets Quantity @@ -128,10 +133,10 @@ Let's use Get Account Assets Query as an example. To know how many units of a particular asset an account has, use `asset get` with the specified account and asset: ```bash -./iroha_client_cli asset get --account="White Rabbit@Soramitsu" --asset="XOR#Soramitsu" +./iroha_client_cli asset get --account="ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu" --asset="XOR#Soramitsu" ``` -This query returns the quantity of `XOR#Soramitsu` asset for the `White Rabbit@Soramitsu` account. +This query returns the quantity of `XOR#Soramitsu` asset for the `ed01204A3C5A6B77BBE439969F95F0AA4E01AE31EC45A0D68C131B2C622751FCC5E3B6@Soramitsu` account. It's possible to filter based on either account, asset or domain id by using the filtering API provided by the Iroha client CLI. diff --git a/client_cli/pytests/README.md b/client_cli/pytests/README.md index f702ab4f6ab..6aefb69e900 100644 --- a/client_cli/pytests/README.md +++ b/client_cli/pytests/README.md @@ -4,14 +4,15 @@ This directory contains the `pytest` framework with test suites for the Iroha 2 For quick access to a topic that interests you, select one of the following: -- [Framework Structure](#framework-structure) -- [Iroha 2 Test Model](#iroha-2-test-model) -- [Using Test Suites](#using-test-suites) - - [Custom Test Environment with Docker Compose](#custom-test-environment-with-docker-compose) - - [Poetry Configuration](#poetry-configuration) - - [Tests Configuration](#tests-configuration) -- [Running Tests](#running-tests) -- [Viewing Test Reports](#viewing-test-reports) +- [Overview](#overview) + - [Framework Structure](#framework-structure) + - [Iroha 2 Test Model](#iroha-2-test-model) + - [Using Test Suites](#using-test-suites) + - [Custom Test Environment with Docker Compose](#custom-test-environment-with-docker-compose) + - [Poetry Configuration](#poetry-configuration) + - [Tests Configuration](#tests-configuration) + - [Running Tests](#running-tests) + - [Viewing Test Reports](#viewing-test-reports) ## Framework Structure @@ -105,20 +106,20 @@ To do so, perform the following steps: cargo build --bin iroha_client_cli ``` -3. Create a new directory, then copy the `iroha_client_cli` binary and its `config.json` configuration file into it: +3. Create a new directory, then copy the `iroha_client_cli` binary and its `client.toml` configuration file into it: ```shell # Create a new directory: - mkdir test_client + mkdir test # Copy the files: - cp configs/client/config.json test_client - cp target/debug/iroha_client_cli test_client + cp configs/swarm/client.toml test + cp target/debug/iroha_client_cli test ``` 4. Proceed with _Step 2_ of the [Using Test Suites](#using-test-suites) instructions. > [!NOTE] -> Don't forget to specify the path to the directory created for the `iroha_client_cli` binary and its `config.json` configuration file (see Step 3) in the `CLIENT_CLI_DIR` variable of the `.env` file. +> Don't forget to specify the path to the directory created for the `iroha_client_cli` binary and its `client.toml` configuration file (see Step 3) in the `CLIENT_CLI_DIR` variable of the `.env` file. > For details, see [Tests Configuration](#tests-configuration) below. ### Poetry Configuration diff --git a/client_cli/pytests/common/consts.py b/client_cli/pytests/common/consts.py index fadc52223ae..f1221c655b6 100644 --- a/client_cli/pytests/common/consts.py +++ b/client_cli/pytests/common/consts.py @@ -15,11 +15,11 @@ class Stderr(Enum): Enum for standard error messages. """ - CANNOT_BE_EMPTY = "cannot be empty\n\nFor more information, try '--help'.\n" + EMPTY = "mpty" REPETITION = "Repetition" TOO_LONG = "Name length violation" FAILED_TO_FIND_DOMAIN = "Failed to find domain" - INVALID_CHARACTER = "Invalid character" + INVALID_CHARACTER = "Failed to parse" INVALID_VALUE_TYPE = "should be either `Store` or `Numeric`" RESERVED_CHARACTER = ( "The `@` character is reserved for `account@domain` constructs," diff --git a/client_cli/pytests/common/helpers.py b/client_cli/pytests/common/helpers.py index 6145ea35557..aa55466ec9c 100644 --- a/client_cli/pytests/common/helpers.py +++ b/client_cli/pytests/common/helpers.py @@ -19,7 +19,7 @@ def extract_hash(stdout): """ Extracts a SHA-256 hash from the given string. - :param stdout: The string from which to extract the hash. + :param stdout: The string from which to extract the hash. :return: The extracted hash if found, otherwise None. """ if not isinstance(stdout, str) or not stdout.strip(): @@ -98,7 +98,7 @@ def generate_public_key(): encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw ) ).decode() - return public_key + return "ed0120" + public_key.upper() def generate_random_string(length, allowed_chars): diff --git a/client_cli/pytests/common/json_isi_examples/unregister_asset.json b/client_cli/pytests/common/json_isi_examples/unregister_asset.json index ac3d7f314f9..837355581fb 100644 --- a/client_cli/pytests/common/json_isi_examples/unregister_asset.json +++ b/client_cli/pytests/common/json_isi_examples/unregister_asset.json @@ -1,7 +1,7 @@ [{ "Unregister": { "Asset": { - "object_id": "rose#alice@wonderland" + "object_id": "rose#ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } -}] \ No newline at end of file +}] diff --git a/client_cli/pytests/models/account.py b/client_cli/pytests/models/account.py index 7f3acb01d67..7e15d614919 100644 --- a/client_cli/pytests/models/account.py +++ b/client_cli/pytests/models/account.py @@ -10,17 +10,14 @@ class Account: """ Account class represents an Iroha account. - :param name: The name of the account. - :type name: str + :param signatory: The signatory of the account. + :type signatory: str :param domain: The domain of the account. :type domain: str - :param public_key: The public key of the account. - :type public_key: str """ - name: str + signatory: str domain: str - public_key: str = "" def __repr__(self): - return f"{self.name}@{self.domain}" + return f"{self.signatory}@{self.domain}" diff --git a/client_cli/pytests/src/client_cli/client_cli.py b/client_cli/pytests/src/client_cli/client_cli.py index fd751c77f65..c78373fa2cf 100644 --- a/client_cli/pytests/src/client_cli/client_cli.py +++ b/client_cli/pytests/src/client_cli/client_cli.py @@ -134,22 +134,19 @@ def domain(self, domain: str): self.execute() return self - def account(self, account: str, domain: str, key: str): + def account(self, signatory: str, domain: str): """ - Executes the 'account' command for the given account, domain, and key. + Executes the 'account' command for the given signatory and domain. - :param account: The account to be queried. - :type account: str + :param signatory: The signatory of the account. + :type signatory: str :param domain: The domain of the account. :type domain: str - :param key: The key for the account. - :type key: str :return: The current ClientCli object. :rtype: ClientCli """ self.command.insert(2, "account") - self.command.append("--id=" + account + "@" + domain) - self.command.append("--key=ed0120" + key) + self.command.append("--id=" + signatory + "@" + domain) self.execute() return self @@ -172,11 +169,11 @@ def asset(self, asset_definition=None, account=None, value_of_value_type=None): "--asset-id=" + asset_definition.name + "#" - + account.domain + + asset_definition.domain + "#" - + account.name + + account.signatory + "@" - + asset_definition.domain + + account.domain ) self.command.append("--quantity=" + value_of_value_type) self.execute() @@ -204,11 +201,11 @@ def transfer(self, asset, source_account, target_account, quantity: str): "--asset-id=" + asset.name + "#" - + source_account.domain + + asset.domain + "#" - + source_account.name + + source_account.signatory + "@" - + asset.domain + + source_account.domain ) self.command.append("--quantity=" + quantity) self.execute() @@ -232,11 +229,11 @@ def burn(self, account, asset, quantity: str): "--asset-id=" + asset.name + "#" - + account.domain + + asset.domain + "#" - + account.name + + account.signatory + "@" - + asset.domain + + account.domain ) self.command.append("--quantity=" + quantity) self.execute() diff --git a/client_cli/pytests/src/client_cli/configuration.py b/client_cli/pytests/src/client_cli/configuration.py index 5e04cdfbecd..897eec066f8 100644 --- a/client_cli/pytests/src/client_cli/configuration.py +++ b/client_cli/pytests/src/client_cli/configuration.py @@ -122,42 +122,22 @@ def env(self): # return self._config['TORII_API_URL' return {**os.environ, **self._envs} - @property - def account_id(self): - """ - Get the ACCOUNT_ID configuration value. - - :return: The ACCOUNT_ID. - :rtype: str - """ - return self._config["account"]["id"] - - @property - def account_name(self): - """ - Get the account name from the ACCOUNT_ID configuration value. - - :return: The account name. - :rtype: str - """ - return self.account_id.split("@")[0] - @property def account_domain(self): """ - Get the account domain from the ACCOUNT_ID configuration value. + Get the ACCOUNT_DOMAIN configuration value. :return: The account domain. :rtype: str """ - return self.account_id.split("@")[1] + return self._config["account"]["domain_id"] @property - def public_key(self): + def account_signatory(self): """ Get the PUBLIC_KEY configuration value. - :return: The public key. + :return: The account signatory. :rtype: str """ - return self._config["account"]["public_key"].split("ed0120")[1] + return self._config["account"]["public_key"] diff --git a/client_cli/pytests/src/client_cli/iroha.py b/client_cli/pytests/src/client_cli/iroha.py index 8c35810c21a..7bfa0ba3af6 100644 --- a/client_cli/pytests/src/client_cli/iroha.py +++ b/client_cli/pytests/src/client_cli/iroha.py @@ -87,7 +87,7 @@ def assets(self) -> Dict[str, str]: Retrieve assets from the Iroha network and return them as a dictionary where the keys are asset ids and the values are the corresponding asset objects. - :return: Dictionary of assets. + :return: Dictionary of assets. :rtype: Dict[str, Any] """ self._execute_command("asset") diff --git a/client_cli/pytests/test/__init__.py b/client_cli/pytests/test/__init__.py index ab75290e556..1ca1a357dae 100644 --- a/client_cli/pytests/test/__init__.py +++ b/client_cli/pytests/test/__init__.py @@ -3,7 +3,6 @@ """ from .conftest import ( - GIVEN_127_length_name, GIVEN_128_length_name, GIVEN_129_length_name, GIVEN_currently_account_quantity_with_two_quantity_of_asset, diff --git a/client_cli/pytests/test/accounts/conftest.py b/client_cli/pytests/test/accounts/conftest.py index 7329c22d45f..f92932bfcb9 100644 --- a/client_cli/pytests/test/accounts/conftest.py +++ b/client_cli/pytests/test/accounts/conftest.py @@ -1,5 +1,4 @@ from test import ( - GIVEN_127_length_name, GIVEN_129_length_name, GIVEN_fake_name, GIVEN_key_with_invalid_character_in_key, diff --git a/client_cli/pytests/test/accounts/test_accounts_query_filters.py b/client_cli/pytests/test/accounts/test_accounts_query_filters.py index bb13afe52bf..6ff50159468 100644 --- a/client_cli/pytests/test/accounts/test_accounts_query_filters.py +++ b/client_cli/pytests/test/accounts/test_accounts_query_filters.py @@ -26,28 +26,10 @@ def condition(): client_cli.wait_for(condition) -def test_filter_by_account_name(GIVEN_registered_account): - def condition(): - name = GIVEN_registered_account.name - with allure.step(f'WHEN client_cli query accounts with name "{name}"'): - accounts = iroha.list_filter( - {"Identifiable": {"StartsWith": f"{name}@"}} - ).accounts() - with allure.step("THEN Iroha should return only accounts with this name"): - allure.attach( - json.dumps(accounts), - name="accounts", - attachment_type=allure.attachment_type.JSON, - ) - return accounts and all(account.startswith(name) for account in accounts) - - client_cli.wait_for(condition) - - def test_filter_by_account_id(GIVEN_registered_account): def condition(): account_id = ( - GIVEN_registered_account.name + "@" + GIVEN_registered_account.domain + GIVEN_registered_account.signatory + "@" + GIVEN_registered_account.domain ) with allure.step( f'WHEN client_cli query accounts with account id "{account_id}"' diff --git a/client_cli/pytests/test/accounts/test_register_accounts.py b/client_cli/pytests/test/accounts/test_register_accounts.py index 17b82efa853..4c0dec1b293 100644 --- a/client_cli/pytests/test/accounts/test_register_accounts.py +++ b/client_cli/pytests/test/accounts/test_register_accounts.py @@ -12,67 +12,39 @@ def story_account_register_account(): @allure.label("sdk_test_id", "register_account") -def test_register_account(GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key): +def test_register_account(GIVEN_public_key, GIVEN_registered_domain): with allure.step( - f'WHEN client_cli registers the account "{GIVEN_fake_name}" ' + f'WHEN client_cli registers the account "{GIVEN_public_key}" ' f'in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_public_key, domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, ) - registered = GIVEN_fake_name + "@" + GIVEN_registered_domain.name + registered = GIVEN_public_key + "@" + GIVEN_registered_domain.name with allure.step(f'THEN Iroha should have the "{registered}" account'): iroha.should(have.account(registered)) -@allure.label("sdk_test_id", "register_account_with_two_public_keys") -@pytest.mark.xfail(reason="TO DO") -def test_register_account_with_two_public_keys( - GIVEN_fake_name, GIVEN_registered_domain, GIVEN_public_key -): - assert 0 - - -@allure.label("sdk_test_id", "register_account_with_empty_name") -def test_register_account_with_empty_name(GIVEN_registered_domain, GIVEN_public_key): - with allure.step( - f"WHEN client_cli tries to register an account with an empty name " - f'in the "{GIVEN_registered_domain.name}" domain' - ): - client_cli.register().account( - account="", domain=GIVEN_registered_domain.name, key=GIVEN_public_key - ) - with allure.step( - f'THEN сlient_cli should have the account error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) - - -@allure.label("sdk_test_id", "register_account_with_existing_name") -def test_register_account_with_existing_name( - GIVEN_registered_domain, GIVEN_public_key, GIVEN_registered_account +@allure.label("sdk_test_id", "register_account_with_existing_signatory") +def test_register_account_with_existing_signatory( + GIVEN_registered_domain, GIVEN_registered_account ): with allure.step( f"WHEN client_cli tries to register an account " - f'with the same name "{GIVEN_registered_domain.name}" ' + f'with the same signatory "{GIVEN_registered_account.signatory}" ' f'in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=GIVEN_registered_account.name, + signatory=GIVEN_registered_account.signatory, domain=GIVEN_registered_account.domain, - key=GIVEN_registered_account.public_key, ) - with allure.step( - f'THEN client_cli should have the account error: "{GIVEN_registered_domain.name}"' - ): + with allure.step("THEN client_cli should have the account error"): client_cli.should(have.error(Stderr.REPETITION.value)) @allure.label("sdk_test_id", "register_account_with_invalid_domain") def test_register_account_with_invalid_domain( - GIVEN_fake_name, GIVEN_not_existing_name, GIVEN_public_key, ): @@ -80,9 +52,8 @@ def test_register_account_with_invalid_domain( "WHEN client_cli tries to register an account with an invalid domain" ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_public_key, domain=GIVEN_not_existing_name, - key=GIVEN_public_key, ) with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.FAILED_TO_FIND_DOMAIN.value)) @@ -90,61 +61,19 @@ def test_register_account_with_invalid_domain( @allure.label("sdk_test_id", "register_account_with_invalid_character_in_key") def test_register_account_with_invalid_character_in_key( - GIVEN_fake_name, GIVEN_registered_domain, GIVEN_key_with_invalid_character_in_key + GIVEN_registered_domain, GIVEN_key_with_invalid_character_in_key ): with allure.step( "WHEN client_cli tries to register an account with invalid character in the key" ): client_cli.register().account( - account=GIVEN_fake_name, + signatory=GIVEN_key_with_invalid_character_in_key, domain=GIVEN_registered_domain.name, - key=GIVEN_key_with_invalid_character_in_key, ) with allure.step("THEN client_cli should have the error"): client_cli.should(have.error(Stderr.INVALID_CHARACTER.value)) -@allure.label("sdk_test_id", "register_account_with_max_name") -def test_register_account_with_max_name( - GIVEN_127_length_name, GIVEN_registered_domain, GIVEN_public_key -): - with allure.step("WHEN client_cli register an account with the 127 length name"): - client_cli.register().account( - account=GIVEN_127_length_name, - domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, - ) - registered = GIVEN_127_length_name + "@" + GIVEN_registered_domain.name - with allure.step(f'THEN Iroha should have the "{registered}" account'): - iroha.should(have.account(registered)) - - -@allure.label("sdk_test_id", "register_account_with_special_characters") -@pytest.mark.xfail(reason="TO DO") -def test_register_account_with_special_characters( - GIVEN_registered_domain, GIVEN_public_key -): - assert 0 - - -@allure.label("sdk_test_id", "register_account_with_long_account_name") -def test_register_account_with_long_account_name( - GIVEN_registered_domain, GIVEN_129_length_name, GIVEN_public_key -): - with allure.step( - "WHEN client_cli tries to register an account with a name with 129 characters" - ): - client_cli.register().account( - account=GIVEN_129_length_name, - domain=GIVEN_registered_domain.name, - key=GIVEN_public_key, - ) - with allure.step( - f'THEN client_cli should have the name error: "{Stderr.TOO_LONG}"' - ): - client_cli.should(have.error(Stderr.TOO_LONG.value)) - - @allure.label("sdk_test_id", "register_account_with_metadata") @pytest.mark.xfail(reason="TO DO") def test_register_account_with_metadata( diff --git a/client_cli/pytests/test/assets/test_assets_query_filters.py b/client_cli/pytests/test/assets/test_assets_query_filters.py index 1a44e6e8683..918a255079d 100644 --- a/client_cli/pytests/test/assets/test_assets_query_filters.py +++ b/client_cli/pytests/test/assets/test_assets_query_filters.py @@ -69,7 +69,7 @@ def condition(): asset_id = ( GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name + "##" - + GIVEN_currently_authorized_account.name + + GIVEN_currently_authorized_account.signatory + "@" + GIVEN_currently_authorized_account.domain ) diff --git a/client_cli/pytests/test/assets/test_burn_assets.py b/client_cli/pytests/test/assets/test_burn_assets.py index fdbc9a40864..b7d22f3ab3b 100644 --- a/client_cli/pytests/test/assets/test_burn_assets.py +++ b/client_cli/pytests/test/assets/test_burn_assets.py @@ -16,7 +16,7 @@ def test_burn_asset_for_account_in_same_domain( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} burns 1" + f"WHEN {GIVEN_currently_authorized_account.signatory} burns 1" f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" ): client_cli.burn( @@ -25,7 +25,7 @@ def test_burn_asset_for_account_in_same_domain( quantity="1", ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} " + f"THEN {GIVEN_currently_authorized_account.signatory} " f"has 1 of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" ): iroha.should( diff --git a/client_cli/pytests/test/assets/test_mint_assets.py b/client_cli/pytests/test/assets/test_mint_assets.py index 869049434e4..ef83e794df0 100644 --- a/client_cli/pytests/test/assets/test_mint_assets.py +++ b/client_cli/pytests/test/assets/test_mint_assets.py @@ -16,9 +16,9 @@ def test_mint_asset_for_account_in_same_domain( GIVEN_numeric_value, ): with allure.step( - f'WHEN client_cli mint the asset "{GIVEN_registered_asset_definition_with_numeric_value_type.name}" ' - f'for the "{GIVEN_registered_account.name}" ' - f'in the "{GIVEN_registered_asset_definition_with_numeric_value_type.domain}" domain' + f'WHEN client_cli mint "{GIVEN_numeric_value}" of ' + f'"{GIVEN_registered_asset_definition_with_numeric_value_type}" ' + f'for the "{GIVEN_registered_account}"' ): client_cli.mint().asset( account=GIVEN_registered_account, @@ -26,8 +26,9 @@ def test_mint_asset_for_account_in_same_domain( value_of_value_type=GIVEN_numeric_value, ) with allure.step( - f'THEN "{GIVEN_registered_account}" account ' - f'should have the "{GIVEN_registered_asset_definition_with_numeric_value_type}" asset definition' + f'THEN "{GIVEN_registered_account}" ' + f'should have the "{GIVEN_numeric_value}" of ' + f'"{GIVEN_registered_asset_definition_with_numeric_value_type}"' ): iroha.should( have.asset( @@ -59,8 +60,8 @@ def test_mint_asset_quantity_after_minting(GIVEN_minted_asset_quantity): expected_quantity = int(GIVEN_minted_asset_quantity.value) + 1 with allure.step( f'THEN "{GIVEN_minted_asset_quantity.account}" account ' - f'should have the "{expected_quantity}" asset definition ' - f"with updated quantity" + f'should have the "{GIVEN_minted_asset_quantity.definition}" asset ' + f'with updated quantity "{expected_quantity}"' ): iroha.should( have.asset_has_quantity( diff --git a/client_cli/pytests/test/assets/test_register_asset_definitions.py b/client_cli/pytests/test/assets/test_register_asset_definitions.py index 9110cd3bb16..03233ccc373 100644 --- a/client_cli/pytests/test/assets/test_register_asset_definitions.py +++ b/client_cli/pytests/test/assets/test_register_asset_definitions.py @@ -119,10 +119,8 @@ def test_register_asset_with_empty_name(GIVEN_registered_domain): client_cli.register().asset().definition( asset="", domain=GIVEN_registered_domain.name, value_type="Numeric" ) - with allure.step( - f'THEN сlient_cli should have the asset error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) + with allure.step(f'THEN сlient_cli should have the asset error: "{Stderr.EMPTY}"'): + client_cli.should(have.error(Stderr.EMPTY.value)) @allure.label("sdk_test_id", "register_asset_with_not_existing_domain") diff --git a/client_cli/pytests/test/assets/test_transfer_assets.py b/client_cli/pytests/test/assets/test_transfer_assets.py index f78a2da2daa..4e842185c58 100644 --- a/client_cli/pytests/test/assets/test_transfer_assets.py +++ b/client_cli/pytests/test/assets/test_transfer_assets.py @@ -18,9 +18,9 @@ def test_transfer_asset( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} transfers 1 Quantity" + f"WHEN {GIVEN_currently_authorized_account.signatory} transfers 1 Quantity" f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"to {GIVEN_registered_account.name}" + f"to {GIVEN_registered_account.signatory}" ): client_cli.transfer( asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, @@ -30,7 +30,7 @@ def test_transfer_asset( ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} has 1 Quantity " + f"THEN {GIVEN_currently_authorized_account.signatory} has 1 Quantity " f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" f"AND {GIVEN_registered_account} has 1 more Quantity" ): @@ -51,9 +51,9 @@ def test_transfer_with_insufficient_funds( GIVEN_currently_account_quantity_with_two_quantity_of_asset, ): with allure.step( - f"WHEN {GIVEN_currently_authorized_account.name} attempts to transfer more than available Quantity" - f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"to {GIVEN_registered_account.name}" + f"WHEN {GIVEN_currently_authorized_account.signatory} attempts to transfer more than available " + f"Quantity of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" + f"to {GIVEN_registered_account.signatory}" ): client_cli.transfer( asset=GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition, @@ -65,9 +65,9 @@ def test_transfer_with_insufficient_funds( ), ) with allure.step( - f"THEN {GIVEN_currently_authorized_account.name} still has the original Quantity " + f"THEN {GIVEN_currently_authorized_account.signatory} still has the original Quantity " f"of {GIVEN_currently_account_quantity_with_two_quantity_of_asset.definition.name}" - f"AND {GIVEN_registered_account.name} does not receive any additional Quantity" + f"AND {GIVEN_registered_account.signatory} does not receive any additional Quantity" ): client_cli.should(have.error(Stderr.INSUFFICIENT_FUNDS.value)) @@ -82,8 +82,8 @@ def test_exchange_asset_through_intermediary( GIVEN_buyer_account_with_eth, ): # with allure.step(f'WHEN {GIVEN_intermediary_with_transfer_permission.name}' - # f'exchanges BTC from {GIVEN_seller_account_with_btc.name}' - # f'with ETH from {GIVEN_buyer_account_with_eth.name}'): + # f'exchanges BTC from {GIVEN_seller_account_with_btc.signatory}' + # f'with ETH from {GIVEN_buyer_account_with_eth.signatory}'): # client_cli.exchange_assets( # intermediary_account=GIVEN_intermediary_with_transfer_permission, # seller_account=GIVEN_seller_account_with_btc, @@ -91,8 +91,8 @@ def test_exchange_asset_through_intermediary( # btc_quantity="1", # eth_quantity="10") # - # with allure.step(f'THEN {GIVEN_seller_account_with_btc.name} receives ETH ' - # f'AND {GIVEN_buyer_account_with_eth.name} receives BTC'): + # with allure.step(f'THEN {GIVEN_seller_account_with_btc.signatory} receives ETH ' + # f'AND {GIVEN_buyer_account_with_eth.signatory} receives BTC'): # iroha.should(have.asset( # f'eth#{GIVEN_seller_account_with_btc.domain}', quantity="10")) # iroha.should(have.asset( diff --git a/client_cli/pytests/test/assets/test_unregister_asset.py b/client_cli/pytests/test/assets/test_unregister_asset.py index 997240e516a..bd48a79e0c2 100644 --- a/client_cli/pytests/test/assets/test_unregister_asset.py +++ b/client_cli/pytests/test/assets/test_unregister_asset.py @@ -12,7 +12,11 @@ def story_account_unregisters_asset(): @allure.label("sdk_test_id", "unregister_asset") @pytest.mark.parametrize( - "GIVEN_numeric_asset_for_account", ["alice@wonderland"], indirect=True + "GIVEN_numeric_asset_for_account", + [ + "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + ], + indirect=True, ) @pytest.mark.xfail(reason="wait for #4039") def test_unregister_asset( diff --git a/client_cli/pytests/test/conftest.py b/client_cli/pytests/test/conftest.py index fbfb96ee4b8..364456252ff 100644 --- a/client_cli/pytests/test/conftest.py +++ b/client_cli/pytests/test/conftest.py @@ -67,15 +67,12 @@ def GIVEN_registered_domain_with_uppercase_letter(GIVEN_registered_domain): @pytest.fixture() def GIVEN_registered_account(GIVEN_registered_domain, GIVEN_public_key): """Fixture to create an account.""" - name = fake_name() - account = Account( - name=name, domain=GIVEN_registered_domain.name, public_key=GIVEN_public_key - ) + account = Account(signatory=GIVEN_public_key, domain=GIVEN_registered_domain.name) with allure.step( - f'GIVEN the account "{name}" in the "{GIVEN_registered_domain.name}" domain' + f'GIVEN the account "{GIVEN_public_key}" in the "{GIVEN_registered_domain.name}" domain' ): client_cli.register().account( - account=account.name, domain=account.domain, key=account.public_key + signatory=account.signatory, domain=account.domain ) return account @@ -84,12 +81,11 @@ def GIVEN_registered_account(GIVEN_registered_domain, GIVEN_public_key): def GIVEN_currently_authorized_account(): """Fixture to get the currently authorized account.""" account: Account = Account( - name=config.account_name, + signatory=config.account_signatory, domain=config.account_domain, - public_key=config.public_key, ) with allure.step( - f'GIVEN the currently authorized account "{account.name}" ' + f'GIVEN the currently authorized account "{account.signatory}" ' f'in the "{account.domain}" domain' ): return account @@ -106,7 +102,7 @@ def GIVEN_currently_account_quantity_with_two_quantity_of_asset( value_type=GIVEN_numeric_value_type, ) asset = Asset( - definition=asset_def, value="2", account=GIVEN_currently_authorized_account.name + definition=asset_def, value="2", account=GIVEN_currently_authorized_account ) name = fake_name() with allure.step( @@ -132,12 +128,14 @@ def GIVEN_numeric_asset_for_account( ): """Fixture to get an asset for a given account and domain with specified quantity.""" account, domain = request.param.split("@") - account = Account(name=account, domain=domain) + account = Account(signatory=account, domain=domain) asset_def = AssetDefinition( name=GIVEN_fake_asset_name, domain=domain, value_type=GIVEN_numeric_value_type ) - asset = Asset(definition=asset_def, value=GIVEN_numeric_value, account=account.name) + asset = Asset( + definition=asset_def, value=GIVEN_numeric_value, account=account.signatory + ) with allure.step( f'GIVEN the asset_definition "{asset_def.name}" ' f'in the "{domain}" domain' @@ -328,13 +326,6 @@ def GIVEN_129_length_name(): return ident -@pytest.fixture() -def GIVEN_127_length_name(): - ident = generate_random_string_without_reserved_chars(127) - with allure.step(f'GIVEN a name with 127 length "{ident}"'): - return ident - - @pytest.fixture() def GIVEN_string_with_reserved_character(): """Fixture to provide a random string with reserved characters.""" diff --git a/client_cli/pytests/test/domains/test_register_domains.py b/client_cli/pytests/test/domains/test_register_domains.py index d369aba3b89..7f01073934e 100644 --- a/client_cli/pytests/test/domains/test_register_domains.py +++ b/client_cli/pytests/test/domains/test_register_domains.py @@ -25,10 +25,8 @@ def test_register_empty_domain( ): with allure.step("WHEN client_cli registers an empty domain"): client_cli.register().domain("") - with allure.step( - f'THEN client_cli should have the domain error: "{Stderr.CANNOT_BE_EMPTY}"' - ): - client_cli.should(have.error(Stderr.CANNOT_BE_EMPTY.value)) + with allure.step(f'THEN client_cli should have the domain error: "{Stderr.EMPTY}"'): + client_cli.should(have.error(Stderr.EMPTY.value)) @allure.label("sdk_test_id", "register_existing_domain") @@ -38,7 +36,7 @@ def test_register_existing_domain(GIVEN_registered_domain): ): client_cli.register().domain(GIVEN_registered_domain.name) with allure.step( - f'THEN client_cli should have the domain error: "{GIVEN_registered_domain.name}"' + f'THEN client_cli should have the domain error: "{GIVEN_registered_domain.name}"' ): client_cli.should(have.error(Stderr.REPETITION.value)) diff --git a/client_cli/pytests/test/triggers/test_register_trigger.py b/client_cli/pytests/test/triggers/test_register_trigger.py index e1c0e5169a2..87ee784b9e1 100644 --- a/client_cli/pytests/test/triggers/test_register_trigger.py +++ b/client_cli/pytests/test/triggers/test_register_trigger.py @@ -18,6 +18,12 @@ def test_register_trigger(GIVEN_currently_authorized_account): ): client_cli.register_trigger(GIVEN_currently_authorized_account) with allure.step( - "THEN Iroha should have the asset with nft_number_1_for_genesis##genesis@genesis" + "THEN Iroha should have the asset with nft_number_1_for_genesis##\ + ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B@genesis" ): - iroha.should(have.asset("nft_number_1_for_genesis##genesis@genesis")) + iroha.should( + have.asset( + "nft_number_1_for_genesis##\ + ed0120E2ECD69DA5833EC10FB3DFAED83A07E5B9CBE9BC39484F0F7DDEC8B46253428B@genesis" + ) + ) diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index 666f6902c91..1cfa5487f2d 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -504,9 +504,6 @@ mod account { pub enum Args { /// Register account Register(Register), - /// Set something in account - #[command(subcommand)] - Set(Set), /// List accounts #[command(subcommand)] List(List), @@ -520,7 +517,6 @@ mod account { fn run(self, context: &mut dyn RunContext) -> Result<()> { match_all!((self, context), { Args::Register, - Args::Set, Args::List, Args::Grant, Args::ListPermissions, @@ -534,74 +530,19 @@ mod account { /// Id of account in form `name@domain_name` #[arg(short, long)] pub id: AccountId, - /// Its public key - #[arg(short, long)] - pub key: PublicKey, #[command(flatten)] pub metadata: MetadataArgs, } impl RunArgs for Register { fn run(self, context: &mut dyn RunContext) -> Result<()> { - let Self { id, key, metadata } = self; - let create_account = - iroha_client::data_model::isi::Register::account(Account::new(id, key)); + let Self { id, metadata } = self; + let create_account = iroha_client::data_model::isi::Register::account(Account::new(id)); submit([create_account], metadata.load()?, context) .wrap_err("Failed to register account") } } - /// Set subcommand of account - #[derive(clap::Subcommand, Debug)] - pub enum Set { - /// Signature condition - SignatureCondition(SignatureCondition), - } - - impl RunArgs for Set { - fn run(self, context: &mut dyn RunContext) -> Result<()> { - match_all!((self, context), { Set::SignatureCondition }) - } - } - - #[derive(Debug, Clone)] - pub struct Signature(SignatureCheckCondition); - - impl FromStr for Signature { - type Err = Error; - fn from_str(s: &str) -> Result { - let err_msg = format!("Failed to open the signature condition file {}", &s); - let deser_err_msg = - format!("Failed to deserialize signature condition from file {}", &s); - let content = fs::read_to_string(s).wrap_err(err_msg)?; - let condition: SignatureCheckCondition = - json5::from_str(&content).wrap_err(deser_err_msg)?; - Ok(Self(condition)) - } - } - - /// Set accounts signature condition - #[derive(clap::Args, Debug)] - pub struct SignatureCondition { - /// Signature condition file - pub condition: Signature, - #[command(flatten)] - pub metadata: MetadataArgs, - } - - impl RunArgs for SignatureCondition { - fn run(self, context: &mut dyn RunContext) -> Result<()> { - let account_id = context.configuration().account_id.clone(); - let Self { - condition: Signature(condition), - metadata, - } = self; - let mint_box = Mint::account_signature_check_condition(condition, account_id); - submit([mint_box], metadata.load()?, context) - .wrap_err("Failed to set signature condition") - } - } - /// List accounts with this command #[derive(clap::Subcommand, Debug, Clone)] pub enum List { diff --git a/config/iroha_test_config.toml b/config/iroha_test_config.toml index ae71d8b8223..04308426470 100644 --- a/config/iroha_test_config.toml +++ b/config/iroha_test_config.toml @@ -6,8 +6,8 @@ private_key = { algorithm = "ed25519", payload = "282ED9F3CF92811C3818DBC4AE594E address = "127.0.0.1:1337" [genesis] -public_key = "ed01204CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" file = "./genesis.json" +public_key = "ed01204CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" private_key = { algorithm = "ed25519", payload = "D748E18CE60CB30DEA3E73C9019B7AF45A8D465E3D71BCC9A5EF99A008205E534CFFD0EE429B1BDD36B3910EC570852B8BB63F18750341772FB46BC856C5CAAF" } [torii] diff --git a/config/tests/fixtures.rs b/config/tests/fixtures.rs index cfc930288fa..e88b2fdf155 100644 --- a/config/tests/fixtures.rs +++ b/config/tests/fixtures.rs @@ -65,7 +65,18 @@ fn minimal_config_snapshot() -> Result<()> { ), private_key: "[REDACTED PrivateKey]", }, - p2p_address: 127.0.0.1:1337, + peer_id: PeerId { + address: 127.0.0.1:1337, + public_key: PublicKey( + ed25519( + "ed01208BA62848CF767D72E7F7F4B9D2D7BA07FEE33760F79ABE5597A51520E292A0CB", + ), + ), + }, + }, + network: Network { + address: 127.0.0.1:1337, + idle_timeout: 60s, }, genesis: Partial { public_key: PublicKey( @@ -363,6 +374,7 @@ fn full_envs_set_is_consumed() -> Result<()> { block_gossip_period: None, transaction_gossip_max_size: None, transaction_gossip_period: None, + idle_timeout: None, }, logger: LoggerPartial { level: Some( diff --git a/configs/client.template.toml b/configs/client.template.toml index 3bad84abcc5..ab331866cbb 100644 --- a/configs/client.template.toml +++ b/configs/client.template.toml @@ -8,7 +8,7 @@ # password = [account] -# id = +# domain_id = # public_key = # private_key = { algorithm = "", payload = "" } diff --git a/configs/swarm/client.toml b/configs/swarm/client.toml index 55eed0b2f4a..06944ec77d4 100644 --- a/configs/swarm/client.toml +++ b/configs/swarm/client.toml @@ -6,6 +6,6 @@ web_login = "mad_hatter" password = "ilovetea" [account] -id = "alice@wonderland" -public_key = "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" -private_key = { algorithm = "ed25519", payload = "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" } +domain_id = "wonderland" +public_key = "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" +private_key = { algorithm = "ed25519", payload = "CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03" } diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm index cc4689ba58f2983a80403e0445e5a10ee1df3901..4a1b41fea1b72c9724e93903dd3102d187ba48cc 100644 GIT binary patch literal 517109 zcmeFa4V+z9S?|B!&dbc1Np{k-&0EVkr)fEXrUZo~)c&ut^QWaKNI|audawSQ+V*dm z6bfzOR)uC-0u2~2Xs!gTFiNoj3k+B=N`Zhy0u~HdHENYyK2@T(V#KP^Tjl@#J!|cK z_BnIT%p?tI%cqm}?6>vutY2t%7Xui4x1w*cwA;d41mEj} zEu-Y{f5Db|{y;;xRg(o=G!>0hUU9?q*Is$rH8)>#)oX9M=89Lm>c*RHc; z1(Ev}Uw8da2Z6gE&(po;wb$J6GkJ>UPhWG>6|cMUx>sC%Wp5Ezy!y(Yx$gQauMTQ1 zdvC&&`S$8-Zg}M@ZoH9}TyfL& zTd#T56*pdW?KQ8wGH*(J^-s~P_}@p5jRut0>%`KjtN z&ph%Jg#k&GGa3nMNi9i&y5;0eo*-00m1kw}U==BA?;n-+{!t4>>!1Ba6#=KeBx*!w zs2ZivUlJrqSWSXOl%7OU6xT+gs76ZC*Md-Y{M9N!L^|%0Ag(6iNEAm&Q~~x%$W_&Z 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%0A#I%k?!AgjV45xlSjKDLRi+|sNT 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�f&QGzu7$+xVw9Q$; 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�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 AD*ylh literal 526777 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%0A|SGIv5{ETR+X%a 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�eOP0ERO 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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 a17f9e759d1..25cee569dba 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -796,6 +796,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::{ @@ -821,10 +822,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()); @@ -843,7 +842,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 @@ -851,7 +850,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 @@ -878,10 +877,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()); @@ -900,7 +897,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( @@ -913,12 +910,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 @@ -926,7 +923,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 @@ -953,10 +950,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!( @@ -982,12 +977,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"); @@ -996,7 +991,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 @@ -1032,12 +1027,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(); @@ -1052,7 +1048,7 @@ mod tests { // Create genesis transaction // Sign with `genesis_wrong_key` as peer which has incorrect genesis key pair - let tx = TransactionBuilder::new(chain_id.clone(), GENESIS_ACCOUNT_ID.clone()) + let tx = TransactionBuilder::new(chain_id.clone(), genesis_wrong_account_id.clone()) .with_instructions([isi]) .sign(&genesis_wrong_key); let tx = AcceptedTransaction::accept_genesis( diff --git a/core/src/lib.rs b/core/src/lib.rs index f9b25a0a831..5a3e1bb4be6 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -171,7 +171,8 @@ pub mod prelude { mod tests { use std::cmp::Ordering; - use iroha_data_model::{account::AccountId, role::RoleId}; + use iroha_data_model::role::RoleId; + use test_samples::gen_account_in; use crate::role::RoleIdWithOwner; @@ -179,8 +180,8 @@ mod tests { fn cmp_role_id_with_owner() { let role_id_a: RoleId = "a".parse().expect("failed to parse RoleId"); let role_id_b: RoleId = "b".parse().expect("failed to parse RoleId"); - let account_id_a: AccountId = "a@domain".parse().expect("failed to parse AccountId"); - let account_id_b: AccountId = "b@domain".parse().expect("failed to parse AccountId"); + let (account_id_a, _account_keypair_a) = gen_account_in("domain"); + let (account_id_b, _account_keypair_b) = gen_account_in("domain"); let mut role_ids_with_owner = Vec::new(); for account_id in [&account_id_a, &account_id_b] { diff --git a/core/src/queue.rs b/core/src/queue.rs index 51e3ad38a5b..5d0695bbca7 100644 --- a/core/src/queue.rs +++ b/core/src/queue.rs @@ -22,24 +22,10 @@ use crate::{prelude::*, EventsSender}; impl AcceptedTransaction { // TODO: We should have another type of transaction like `CheckedTransaction` in the type system? - #[must_use] - fn check_signature_condition(&self, state_view: &StateView<'_>) -> bool { + fn is_signatory_consistent(&self) -> bool { let authority = self.as_ref().authority(); - - let transaction_signatories = self - .as_ref() - .signatures() - .iter() - .map(|signature| signature.public_key()) - .cloned() - .collect(); - - state_view - .world - .map_account(authority, |account| { - account.check_signature_check_condition(&transaction_signatories) - }) - .unwrap_or(false) + let signatory = self.as_ref().signature().public_key(); + authority.signatory_matches(signatory) } /// Check if [`self`] is committed or rejected. @@ -91,8 +77,8 @@ pub enum Error { MaximumTransactionsPerUser, /// The transaction is already in the queue IsInQueue, - /// Failure during signature condition execution - SignatureCondition, + /// Signatories in signature and payload mismatch + SignatoryInconsistent, } /// Failure that can pop up when pushing transaction into the queue @@ -181,8 +167,8 @@ impl Queue { Err(Error::Expired) } else if tx.is_in_blockchain(state_view) { Err(Error::InBlockchain) - } else if !tx.check_signature_condition(state_view) { - Err(Error::SignatureCondition) + } else if !tx.is_signatory_consistent() { + Err(Error::SignatoryInconsistent) } else { Ok(()) } @@ -396,6 +382,7 @@ pub mod tests { use iroha_data_model::{prelude::*, transaction::TransactionLimits}; use nonzero_ext::nonzero; use rand::Rng as _; + use test_samples::gen_account_in; use tokio::test; use super::*; @@ -423,24 +410,17 @@ pub mod tests { } } - fn accepted_tx( - account_id: &str, - key: &KeyPair, - time_source: &TimeSource, - ) -> AcceptedTransaction { + fn accepted_tx(time_source: &TimeSource) -> AcceptedTransaction { + let (account_id, key_pair) = gen_account_in("wonderland"); let chain_id = ChainId::from("0"); - let message = std::iter::repeat_with(rand::random::) .take(16) .collect(); let instructions = [Fail { message }]; - let tx = TransactionBuilder::new_with_time_source( - chain_id.clone(), - AccountId::from_str(account_id).expect("Valid"), - time_source, - ) - .with_instructions(instructions) - .sign(key); + let tx = + TransactionBuilder::new_with_time_source(chain_id.clone(), account_id, time_source) + .with_instructions(instructions) + .sign(&key_pair); let limits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, @@ -448,18 +428,11 @@ pub mod tests { AcceptedTransaction::accept(tx, &chain_id, &limits).expect("Failed to accept Transaction.") } - pub fn world_with_test_domains( - signatories: impl IntoIterator, - ) -> World { + pub fn world_with_test_domains() -> World { let domain_id = DomainId::from_str("wonderland").expect("Valid"); - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let (account_id, _account_keypair) = gen_account_in("wonderland"); let mut domain = Domain::new(domain_id).build(&account_id); - let mut signatories = signatories.into_iter(); - let mut account = Account::new(account_id.clone(), signatories.next().unwrap()); - for signatory in signatories { - account = account.add_signatory(signatory); - } - let account = account.build(&account_id); + let account = Account::new(account_id.clone()).build(&account_id); assert!(domain.add_account(account).is_none()); World::with([domain], PeersIds::new()) } @@ -474,14 +447,9 @@ pub mod tests { #[test] async fn push_tx() { - let key_pair = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([key_pair.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -489,10 +457,7 @@ pub mod tests { let queue = Queue::test(config_factory(), &time_source); queue - .push( - accepted_tx("alice@wonderland", &key_pair, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); } @@ -500,14 +465,9 @@ pub mod tests { async fn push_tx_overflow() { let capacity = nonzero!(10_usize); - let key_pair = KeyPair::random(); - let kura = Kura::blank_kura_for_testing(); + let kura: Arc = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([key_pair.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -523,19 +483,13 @@ pub mod tests { for _ in 0..capacity.get() { queue - .push( - accepted_tx("alice@wonderland", &key_pair, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(10)); } assert!(matches!( - queue.push( - accepted_tx("alice@wonderland", &key_pair, &time_source), - &state_view - ), + queue.push(accepted_tx(&time_source), &state_view), Err(Failure { err: Error::Full, .. @@ -543,84 +497,12 @@ pub mod tests { )); } - #[test] - async fn push_multisignature_tx() { - let chain_id = ChainId::from("0"); - - let key_pairs = [KeyPair::random(), KeyPair::random()]; - let kura = Kura::blank_kura_for_testing(); - let state = { - let domain_id = DomainId::from_str("wonderland").expect("Valid"); - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let mut domain = Domain::new(domain_id).build(&account_id); - let mut account = Account::new(account_id.clone(), key_pairs[0].public_key().clone()) - .add_signatory(key_pairs[1].public_key().clone()) - .build(&account_id); - account.signature_check_condition = SignatureCheckCondition::all_account_signatures(); - assert!(domain.add_account(account).is_none()); - let query_handle = LiveQueryStore::test().start(); - Arc::new(State::new( - World::with([domain], PeersIds::new()), - kura, - query_handle, - )) - }; - let state_view = state.view(); - - let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); - - let queue = Queue::test(config_factory(), &time_source); - let instructions: [InstructionBox; 0] = []; - let tx = TransactionBuilder::new_with_time_source( - chain_id.clone(), - "alice@wonderland".parse().expect("Valid"), - &time_source, - ) - .with_instructions(instructions); - let tx_limits = TransactionLimits { - max_instruction_number: 4096, - max_wasm_size_bytes: 0, - }; - let fully_signed_tx: AcceptedTransaction = { - let mut signed_tx = tx.clone().sign(&key_pairs[0]); - for key_pair in &key_pairs[1..] { - signed_tx = signed_tx.sign(key_pair); - } - AcceptedTransaction::accept(signed_tx, &chain_id, &tx_limits) - .expect("Failed to accept Transaction.") - }; - // Check that fully signed transaction passes signature check - assert!(fully_signed_tx.check_signature_condition(&state_view)); - - let get_tx = |key_pair| { - AcceptedTransaction::accept(tx.clone().sign(&key_pair), &chain_id, &tx_limits) - .expect("Failed to accept Transaction.") - }; - for key_pair in key_pairs { - let partially_signed_tx: AcceptedTransaction = get_tx(key_pair); - // Check that none of partially signed txs passes signature check - assert!(!partially_signed_tx.check_signature_condition(&state_view),); - assert!(matches!( - queue - .push(partially_signed_tx, &state_view) - .unwrap_err() - .err, - Error::SignatureCondition - )) - } - } - #[test] async fn get_available_txs() { let max_txs_in_block = 2; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -634,10 +516,7 @@ pub mod tests { ); for _ in 0..5 { queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(10)); } @@ -648,18 +527,11 @@ pub mod tests { #[test] async fn push_tx_already_in_blockchain() { - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - ); - + let state = State::new(world_with_test_domains(), kura, query_handle); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); - - let tx = accepted_tx("alice@wonderland", &alice_key, &time_source); + let tx = accepted_tx(&time_source); let mut state_block = state.block(); state_block.transactions.insert(tx.as_ref().hash(), 1); state_block.commit(); @@ -678,18 +550,11 @@ pub mod tests { #[test] async fn get_tx_drop_if_in_blockchain() { let max_txs_in_block = 2; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - ); - + let state = State::new(world_with_test_domains(), kura, query_handle); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); - - let tx = accepted_tx("alice@wonderland", &alice_key, &time_source); + let tx = accepted_tx(&time_source); let queue = Queue::test(config_factory(), &time_source); queue.push(tx.clone(), &state.view()).unwrap(); let mut state_block = state.block(); @@ -707,14 +572,9 @@ pub mod tests { #[test] async fn get_available_txs_with_timeout() { let max_txs_in_block = 6; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -728,19 +588,13 @@ pub mod tests { ); for _ in 0..(max_txs_in_block - 1) { queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(100)); } queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(101)); assert_eq!( @@ -751,10 +605,7 @@ pub mod tests { ); queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); time_handle.advance(Duration::from_millis(210)); assert_eq!( @@ -770,24 +621,16 @@ pub mod tests { #[test] async fn transactions_available_after_pop() { let max_txs_in_block = 2; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); let queue = Queue::test(config_factory(), &time_source); queue - .push( - accepted_tx("alice@wonderland", &alice_key, &time_source), - &state_view, - ) + .push(accepted_tx(&time_source), &state_view) .expect("Failed to push tx into queue"); let a = queue @@ -811,14 +654,10 @@ pub mod tests { let chain_id = ChainId::from("0"); let max_txs_in_block = 2; - let alice_key = KeyPair::random(); + let (alice_id, alice_keypair) = gen_account_in("wonderland"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -828,14 +667,11 @@ pub mod tests { let instructions = [Fail { message: "expired".to_owned(), }]; - let mut tx = TransactionBuilder::new_with_time_source( - chain_id.clone(), - AccountId::from_str("alice@wonderland").expect("Valid"), - &time_source, - ) - .with_instructions(instructions); + let mut tx = + TransactionBuilder::new_with_time_source(chain_id.clone(), alice_id, &time_source) + .with_instructions(instructions); tx.set_ttl(Duration::from_millis(TTL_MS)); - let tx = tx.sign(&alice_key); + let tx = tx.sign(&alice_keypair); let limits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, @@ -878,14 +714,9 @@ pub mod tests { #[test] async fn concurrent_stress_test() { let max_txs_in_block = 10; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -908,7 +739,7 @@ pub mod tests { // Spawn a thread where we push transactions thread::spawn(move || { while start_time.elapsed() < run_for { - let tx = accepted_tx("alice@wonderland", &alice_key, &time_source); + let tx = accepted_tx(&time_source); match queue_arc_clone.push(tx, &state.view()) { Ok(()) | Err(Failure { @@ -957,15 +788,9 @@ pub mod tests { async fn push_tx_in_future() { let future_threshold = Duration::from_secs(1); - let alice_id = "alice@wonderland"; - let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let state = Arc::new(State::new( - world_with_test_domains([alice_key.public_key().clone()]), - kura, - query_handle, - )); + let state = Arc::new(State::new(world_with_test_domains(), kura, query_handle)); let state_view = state.view(); let (time_handle, time_source) = TimeSource::new_mock(Duration::default()); @@ -977,12 +802,12 @@ pub mod tests { &time_source, ); - let tx = accepted_tx(alice_id, &alice_key, &time_source); + let tx = accepted_tx(&time_source); assert!(queue.push(tx.clone(), &state_view).is_ok()); // create the same tx but with timestamp in the future time_handle.advance(future_threshold * 2); - let tx = accepted_tx(alice_id, &alice_key, &time_source); + let tx = accepted_tx(&time_source); time_handle.rewind(future_threshold * 2); assert!(matches!( @@ -997,22 +822,14 @@ pub mod tests { #[test] async fn queue_throttling() { - let alice_key_pair = KeyPair::random(); - let bob_key_pair = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let world = { let domain_id = DomainId::from_str("wonderland").expect("Valid"); - let alice_account_id = AccountId::from_str("alice@wonderland").expect("Valid"); - let bob_account_id = AccountId::from_str("bob@wonderland").expect("Valid"); - let mut domain = Domain::new(domain_id).build(&alice_account_id); - let alice_account = Account::new( - alice_account_id.clone(), - alice_key_pair.public_key().clone(), - ) - .build(&alice_account_id); - let bob_account = - Account::new(bob_account_id.clone(), bob_key_pair.public_key().clone()) - .build(&bob_account_id); + let (alice_id, _alice_keypair) = gen_account_in("wonderland"); + let (bob_id, _bob_keypair) = gen_account_in("wonderland"); + let mut domain = Domain::new(domain_id).build(&alice_id); + let alice_account = Account::new(alice_id.clone()).build(&alice_id); + let bob_account = Account::new(bob_id.clone()).build(&bob_id); assert!(domain.add_account(alice_account).is_none()); assert!(domain.add_account(bob_account).is_none()); World::with([domain], PeersIds::new()) @@ -1034,17 +851,11 @@ pub mod tests { // First push by Alice should be fine queue - .push( - accepted_tx("alice@wonderland", &alice_key_pair, &time_source), - &state.view(), - ) + .push(accepted_tx(&time_source), &state.view()) .expect("Failed to push tx into queue"); // Second push by Alice excide limit and will be rejected - let result = queue.push( - accepted_tx("alice@wonderland", &alice_key_pair, &time_source), - &state.view(), - ); + let result = queue.push(accepted_tx(&time_source), &state.view()); assert!( matches!( result, @@ -1058,10 +869,7 @@ pub mod tests { // First push by Bob should be fine despite previous Alice error queue - .push( - accepted_tx("bob@wonderland", &bob_key_pair, &time_source), - &state.view(), - ) + .push(accepted_tx(&time_source), &state.view()) .expect("Failed to push tx into queue"); let transactions = queue.collect_transactions_for_block(&state.view(), 10); @@ -1080,17 +888,11 @@ pub mod tests { // After cleanup Alice and Bob pushes should work fine queue - .push( - accepted_tx("alice@wonderland", &alice_key_pair, &time_source), - &state.view(), - ) + .push(accepted_tx(&time_source), &state.view()) .expect("Failed to push tx into queue"); queue - .push( - accepted_tx("bob@wonderland", &bob_key_pair, &time_source), - &state.view(), - ) + .push(accepted_tx(&time_source), &state.view()) .expect("Failed to push tx into queue"); } } diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index 0661f8618e6..15484de7627 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -132,95 +132,6 @@ pub mod isi { } } - impl Execute for Mint { - #[metrics(+"mint_account_public_key")] - fn execute( - self, - _authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> Result<(), Error> { - let account_id = self.destination_id; - let public_key = self.object; - - state_transaction - .world - .account_mut(&account_id) - .map_err(Error::from) - .and_then(|account| { - if account.contains_signatory(&public_key) { - return Err(RepetitionError { - instruction_type: InstructionType::Mint, - id: account_id.clone().into(), - } - .into()); - } - - account.add_signatory(public_key); - Ok(()) - })?; - - state_transaction - .world - .emit_events(Some(AccountEvent::AuthenticationAdded(account_id.clone()))); - - Ok(()) - } - } - - impl Execute for Burn { - #[metrics(+"burn_account_public_key")] - fn execute( - self, - _authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> Result<(), Error> { - let account_id = self.destination_id; - let public_key = self.object; - - state_transaction.world.account_mut(&account_id) - .map_err(Error::from) - .and_then(|account| { - match account.remove_signatory(&public_key) { - None => Err(Error::InvariantViolation(String::from( - "Public keys cannot be burned to nothing, \ - if you want to delete the account, please use an unregister instruction", - ))), - Some(false) => Err(FindError::PublicKey(public_key).into()), - Some(true) => Ok(()) - } - })?; - - state_transaction - .world - .emit_events(Some(AccountEvent::AuthenticationRemoved(account_id))); - - Ok(()) - } - } - - impl Execute for Mint { - #[metrics(+"mint_account_signature_check_condition")] - fn execute( - self, - _authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> Result<(), Error> { - let account_id = self.destination_id; - let signature_check_condition = self.object; - - state_transaction - .world - .account_mut(&account_id)? - .signature_check_condition = signature_check_condition; - - state_transaction - .world - .emit_events(Some(AccountEvent::AuthenticationAdded(account_id.clone()))); - - Ok(()) - } - } - impl Execute for Transfer { fn execute( self, @@ -575,12 +486,13 @@ pub mod isi { #[cfg(test)] mod test { use iroha_data_model::{prelude::AssetDefinition, ParseError}; + use test_samples::gen_account_in; use crate::smartcontracts::isi::Registrable as _; #[test] fn cannot_forbid_minting_on_asset_mintable_infinitely() -> Result<(), ParseError> { - let authority = "alice@wonderland".parse()?; + let (authority, _authority_keypair) = gen_account_in("wonderland"); let mut definition = AssetDefinition::numeric("test#hello".parse()?).build(&authority); assert!(super::forbid_minting(&mut definition).is_err()); Ok(()) @@ -658,31 +570,6 @@ pub mod query { } } - impl ValidQuery for FindAccountsByName { - #[metrics(+"find_account_by_name")] - fn execute<'state>( - &self, - state_ro: &'state impl StateReadOnly, - ) -> Result + 'state>, Error> { - let name = self.name.clone(); - iroha_logger::trace!(%name); - Ok(Box::new( - state_ro - .world() - .domains_iter() - .flat_map(move |domain| { - let name = name.clone(); - - domain - .accounts - .values() - .filter(move |account| account.id().name == name) - }) - .cloned(), - )) - } - } - impl ValidQuery for FindAccountsByDomainId { #[metrics(+"find_accounts_by_domain_id")] fn execute<'state>( diff --git a/core/src/smartcontracts/isi/domain.rs b/core/src/smartcontracts/isi/domain.rs index 0ae2a44fde1..f2389e9aace 100644 --- a/core/src/smartcontracts/isi/domain.rs +++ b/core/src/smartcontracts/isi/domain.rs @@ -50,14 +50,9 @@ pub mod isi { let account: Account = self.object.build(authority); let account_id = account.id().clone(); - account_id - .name - .validate_len(state_transaction.config.ident_length_limits) - .map_err(Error::from)?; - if account_id == *iroha_genesis::GENESIS_ACCOUNT_ID { return Err(InstructionExecutionError::InvariantViolation( - "Not allowed to register `genesis@genesis` account".to_owned(), + "Not allowed to register genesis account".to_owned(), )); } diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 4bc587aabee..9cf125bcb8e 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -107,26 +107,12 @@ impl Execute for MintBox { state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), Error> { match self { - Self::Account(isi) => isi.execute(authority, state_transaction), Self::Asset(isi) => isi.execute(authority, state_transaction), Self::TriggerRepetitions(isi) => isi.execute(authority, state_transaction), } } } -impl Execute for AccountMintBox { - fn execute( - self, - authority: &AccountId, - state_transaction: &mut StateTransaction<'_, '_>, - ) -> std::prelude::v1::Result<(), Error> { - match self { - Self::PublicKey(isi) => isi.execute(authority, state_transaction), - Self::SignatureCheckCondition(isi) => isi.execute(authority, state_transaction), - } - } -} - impl Execute for BurnBox { #[iroha_logger::log(name = "burn", skip_all, fields(destination))] fn execute( @@ -135,7 +121,6 @@ impl Execute for BurnBox { state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), Error> { match self { - Self::AccountPublicKey(isi) => isi.execute(authority, state_transaction), Self::Asset(isi) => isi.execute(authority, state_transaction), Self::TriggerRepetitions(isi) => isi.execute(authority, state_transaction), } @@ -254,9 +239,9 @@ mod tests { use core::str::FromStr as _; use std::sync::Arc; - use iroha_crypto::KeyPair; use iroha_data_model::metadata::MetadataValueBox; use iroha_genesis::GENESIS_ACCOUNT_ID; + use test_samples::gen_account_in; use tokio::test; use super::*; @@ -272,15 +257,14 @@ mod tests { let world = World::with([], PeersIds::new()); let query_handle = LiveQueryStore::test().start(); let state = State::new(world, kura.clone(), query_handle); - let genesis_account_id = AccountId::from_str("genesis@genesis")?; - let account_id = AccountId::from_str("alice@wonderland")?; - let (public_key, _) = KeyPair::random().into_parts(); + let (genesis_account_id, _genesis_account_keypair) = gen_account_in("genesis"); + let (account_id, _account_keypair) = gen_account_in("wonderland"); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); Register::domain(Domain::new(DomainId::from_str("wonderland")?)) .execute(&genesis_account_id, &mut state_transaction)?; - Register::account(Account::new(account_id, public_key)) + Register::account(Account::new(account_id)) .execute(&genesis_account_id, &mut state_transaction)?; Register::asset_definition(AssetDefinition::store(asset_definition_id)) .execute(&genesis_account_id, &mut state_transaction)?; @@ -295,7 +279,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut staet_block = state.block(); let mut state_transaction = staet_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); SetKeyValue::asset( @@ -326,7 +310,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); SetKeyValue::account( account_id.clone(), Name::from_str("Bytes")?, @@ -359,7 +343,7 @@ mod tests { let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); let definition_id = AssetDefinitionId::from_str("rose#wonderland")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); SetKeyValue::asset_definition( definition_id.clone(), Name::from_str("Bytes")?, @@ -390,7 +374,7 @@ mod tests { let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); let domain_id = DomainId::from_str("wonderland")?; - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); SetKeyValue::domain( domain_id.clone(), Name::from_str("Bytes")?, @@ -420,7 +404,7 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); let trigger_id = TriggerId::from_str("test_trigger_id")?; assert!(matches!( @@ -439,13 +423,12 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut state_block = state.block(); let mut state_transaction = state_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; - let fake_account_id = AccountId::from_str("fake@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); + let (fake_account_id, _fake_account_keypair) = gen_account_in("wonderland"); let trigger_id = TriggerId::from_str("test_trigger_id")?; // register fake account - let (public_key, _) = KeyPair::random().into_parts(); - let register_account = Register::account(Account::new(fake_account_id.clone(), public_key)); + let register_account = Register::account(Account::new(fake_account_id.clone())); register_account.execute(&account_id, &mut state_transaction)?; // register the trigger @@ -483,18 +466,14 @@ mod tests { let state = state_with_test_domains(&kura)?; let mut staet_block = state.block(); let mut state_transaction = staet_block.transaction(); - let account_id = AccountId::from_str("alice@wonderland")?; + let (account_id, _account_keypair) = gen_account_in("wonderland"); assert!(matches!( Register::domain(Domain::new(DomainId::from_str("genesis")?)) .execute(&account_id, &mut state_transaction) .expect_err("Error expected"), Error::InvariantViolation(_) )); - let (public_key, _) = KeyPair::random().into_parts(); - let register_account = Register::account(Account::new( - AccountId::from_str("genesis@genesis")?, - public_key, - )); + let register_account = Register::account(Account::new(GENESIS_ACCOUNT_ID.clone())); assert!(matches!( register_account .execute(&account_id, &mut state_transaction) diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index daf7faae917..ba8cc1843a2 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -72,13 +72,10 @@ impl ValidQueryRequest { query: SignedQuery, state_ro: &impl StateReadOnly, ) -> Result { - let account_has_public_key = state_ro - .world() - .map_account(query.authority(), |account| { - account.contains_signatory(query.signature().public_key()) - }) - .map_err(Error::from)?; - if !account_has_public_key { + if !query + .authority() + .signatory_matches(query.signature().public_key()) + { return Err(Error::Signature(String::from( "Signature public key doesn't correspond to the account.", )) @@ -155,7 +152,6 @@ impl ValidQuery for QueryBox { } FindAllAccounts, - FindAccountsByName, FindAccountsByDomainId, FindAccountsWithAsset, FindAllAssets, @@ -186,12 +182,12 @@ impl ValidQuery for QueryBox { mod tests { use std::str::FromStr as _; - use iroha_crypto::{Hash, HashOf, KeyPair}; + use iroha_crypto::{Hash, HashOf}; use iroha_data_model::{ metadata::MetadataValueBox, query::error::FindError, transaction::TransactionLimits, }; use iroha_primitives::unique_vec::UniqueVec; - use once_cell::sync::Lazy; + use test_samples::{gen_account_in, ALICE_ID, ALICE_KEYPAIR}; use tokio::test; use super::*; @@ -206,15 +202,10 @@ mod tests { PeersIds, }; - static ALICE_KEYS: Lazy = Lazy::new(KeyPair::random); - static ALICE_ID: Lazy = - Lazy::new(|| AccountId::from_str("alice@wonderland").expect("Valid")); - fn world_with_test_domains() -> World { let domain_id = DomainId::from_str("wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&ALICE_ID); - let account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain.add_account(account).is_none()); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); assert!(domain @@ -227,8 +218,7 @@ mod tests { let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); let mut domain = Domain::new(DomainId::from_str("wonderland").expect("Valid")).build(&ALICE_ID); - let mut account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let mut account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain .add_asset_definition( AssetDefinition::numeric(asset_definition_id.clone()).build(&ALICE_ID) @@ -260,7 +250,7 @@ mod tests { )?; let mut domain = Domain::new(DomainId::from_str("wonderland")?).build(&ALICE_ID); - let account = Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()) + let account = Account::new(ALICE_ID.clone()) .with_metadata(metadata) .build(&ALICE_ID); assert!(domain.add_account(account).is_none()); @@ -298,14 +288,14 @@ mod tests { let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions(instructions) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); AcceptedTransaction::accept(tx, &chain_id, &limits)? }; let invalid_tx = { let isi = Fail::new("fail".to_owned()); let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions([isi.clone(), isi]) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); AcceptedTransaction::accept(tx, &chain_id, &huge_limits)? }; @@ -315,7 +305,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let first_block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -327,7 +317,7 @@ mod tests { for _ in 1u64..blocks { let block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -461,7 +451,7 @@ mod tests { let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions(instructions) - .sign(&ALICE_KEYS); + .sign(&ALICE_KEYPAIR); let tx_limits = &state_block.transaction_executor().transaction_limits; let va_tx = AcceptedTransaction::accept(tx, &chain_id, tx_limits)?; @@ -469,7 +459,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let vcb = BlockBuilder::new(vec![va_tx.clone()], topology.clone(), Vec::new()) .chain(0, &mut state_block) - .sign(&ALICE_KEYS) + .sign(&ALICE_KEYPAIR) .unpack(|_| {}) .commit(&topology) .unpack(|_| {}) @@ -482,8 +472,8 @@ mod tests { let state_view = state.view(); let unapplied_tx = TransactionBuilder::new(chain_id, ALICE_ID.clone()) - .with_instructions([Unregister::account("account@domain".parse().unwrap())]) - .sign(&ALICE_KEYS); + .with_instructions([Unregister::account(gen_account_in("domain").0)]) + .sign(&ALICE_KEYPAIR); let wrong_hash = unapplied_tx.hash(); let not_found = FindTransactionByHash::new(wrong_hash).execute(&state_view); assert!(matches!( @@ -515,8 +505,7 @@ mod tests { let mut domain = Domain::new(DomainId::from_str("wonderland")?) .with_metadata(metadata) .build(&ALICE_ID); - let account = - Account::new(ALICE_ID.clone(), ALICE_KEYS.public_key().clone()).build(&ALICE_ID); + let account = Account::new(ALICE_ID.clone()).build(&ALICE_ID); assert!(domain.add_account(account).is_none()); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; assert!(domain diff --git a/core/src/smartcontracts/isi/world.rs b/core/src/smartcontracts/isi/world.rs index 30af4f45561..ba982fc6416 100644 --- a/core/src/smartcontracts/isi/world.rs +++ b/core/src/smartcontracts/isi/world.rs @@ -89,7 +89,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 bb836227aee..aa62212a53b 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( @@ -253,7 +253,6 @@ pub(crate) struct SmartContractQueryRequest(pub QueryRequest); /// # Errors /// /// See [`Module::new`] -/// // TODO: Probably we can do some checks here such as searching for entrypoint function pub fn load_module(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result { Module::new(engine, bytes).map_err(Error::ModuleLoading) @@ -1704,11 +1703,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::*; @@ -1719,8 +1716,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()); @@ -1773,17 +1769,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)) }; @@ -1818,7 +1811,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); @@ -1863,18 +1856,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)) }; @@ -1916,17 +1906,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)) }; @@ -1968,7 +1955,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); @@ -2010,7 +1997,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 1f9a407638f..ad6d904e9d9 100644 --- a/core/src/tx.rs +++ b/core/src/tx.rs @@ -63,14 +63,13 @@ impl AcceptedTransaction { })); } - for signature in tx.0.signatures() { - if signature.public_key() != genesis_public_key { - return Err(SignatureVerificationFail { - signature: signature.clone().into(), - reason: "Signature doesn't correspond to genesis public key".to_string(), - } - .into()); + let signature = tx.0.signature(); + if signature.public_key() != genesis_public_key { + return Err(SignatureVerificationFail { + signature: signature.clone().into(), + reason: "Signature doesn't correspond to genesis public key".to_string(), } + .into()); } Ok(Self(tx.0)) diff --git a/core/test_network/Cargo.toml b/core/test_network/Cargo.toml index 71e24f95f18..06888ac23dd 100644 --- a/core/test_network/Cargo.toml +++ b/core/test_network/Cargo.toml @@ -17,7 +17,7 @@ iroha_data_model = { workspace = true } iroha_primitives = { workspace = true } iroha_logger = { workspace = true } iroha_genesis = { workspace = true } - +test_samples = { workspace = true } eyre = { workspace = true } futures = { workspace = true, features = ["std", "async-await"] } diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 1816fdff7d3..6b224ef80f8 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -2,7 +2,7 @@ use core::{fmt::Debug, str::FromStr as _, time::Duration}; #[cfg(debug_assertions)] use std::sync::atomic::AtomicBool; -use std::{collections::BTreeMap, path::Path, sync::Arc, thread}; +use std::{collections::BTreeMap, ops::Deref, path::Path, sync::Arc, thread}; use eyre::Result; use futures::{prelude::*, stream::FuturesUnordered}; @@ -16,7 +16,7 @@ use iroha_config::parameters::actual::Root as Config; pub use iroha_core::state::StateReadOnly; use iroha_crypto::KeyPair; use iroha_data_model::{query::QueryOutputBox, ChainId}; -use iroha_genesis::{GenesisNetwork, RawGenesisBlockFile}; +use iroha_genesis::{GenesisNetwork, RawGenesisBlockFile, GENESIS_ACCOUNT_KEYPAIR}; use iroha_logger::InstrumentFutures; use iroha_primitives::{ addr::{socket_addr, SocketAddr}, @@ -26,6 +26,7 @@ use iroha_primitives::{ use rand::{seq::IteratorRandom, thread_rng}; use serde_json::json; use tempfile::TempDir; +use test_samples::{ALICE_ID, ALICE_KEYPAIR, PEER_KEYPAIR}; use tokio::{ runtime::{self, Runtime}, task::{self, JoinHandle}, @@ -48,17 +49,22 @@ pub fn get_chain_id() -> ChainId { ChainId::from("0") } -/// Get a standardised key-pair from the hard-coded literals. -pub fn get_key_pair() -> KeyPair { - KeyPair::new( - iroha_crypto::PublicKey::from_str( - "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0", - ).unwrap(), - iroha_crypto::PrivateKey::from_hex( - iroha_crypto::Algorithm::Ed25519, - "9AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0" - ).unwrap() - ).unwrap() +/// Get a key pair of a common signatory in the test network +pub fn get_key_pair(signatory: Signatory) -> KeyPair { + match signatory { + Signatory::Peer => &PEER_KEYPAIR, + Signatory::Genesis => &GENESIS_ACCOUNT_KEYPAIR, + Signatory::Alice => &ALICE_KEYPAIR, + } + .deref() + .clone() +} + +/// A common signatory in the test network +pub enum Signatory { + Peer, + Genesis, + Alice, } /// Trait used to differentiate a test instance of `genesis`. @@ -84,7 +90,6 @@ impl TestGenesis for GenesisNetwork { let rose_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("valid names"); - let alice_id = AccountId::from_str("alice@wonderland").expect("valid names"); let mint_rose_permission = PermissionToken::new( "CanMintAssetWithDefinition".parse().unwrap(), @@ -117,7 +122,7 @@ impl TestGenesis for GenesisNetwork { upgrade_executor_permission, ] { first_transaction - .append_instruction(Grant::permission(permission, alice_id.clone()).into()); + .append_instruction(Grant::permission(permission, ALICE_ID.clone()).into()); } for isi in extra_isi.into_iter() { @@ -773,7 +778,8 @@ impl TestConfig for Config { let mut layer = iroha::samples::get_user_config( &UniqueVec::new(), Some(get_chain_id()), - Some(get_key_pair()), + Some(get_key_pair(Signatory::Peer)), + Some(get_key_pair(Signatory::Genesis)), ) .merge(RootPartial::from_env(&StdEnv).expect("test env variables should parse properly")); @@ -803,7 +809,7 @@ impl TestClientConfig for ClientConfig { fn test(api_address: &SocketAddr) -> Self { iroha_client::samples::get_client_config( get_chain_id(), - get_key_pair().clone(), + get_key_pair(Signatory::Alice), format!("http://{api_address}") .parse() .expect("should be valid url"), diff --git a/crypto/src/encryption/mod.rs b/crypto/src/encryption/mod.rs index 63be1eab7f2..bc200b7f5b3 100644 --- a/crypto/src/encryption/mod.rs +++ b/crypto/src/encryption/mod.rs @@ -65,13 +65,15 @@ fn random_bytes>() -> Result, Error> { /// # Usage /// /// ``` -/// use iroha_crypto::encryption::{SymmetricEncryptor, ChaCha20Poly1305}; +/// use iroha_crypto::encryption::{ChaCha20Poly1305, SymmetricEncryptor}; /// /// let key: Vec = (0..0x20).collect(); /// let encryptor = SymmetricEncryptor::::new_with_key(&key); /// let aad = b"Using ChaCha20Poly1305 to encrypt data"; /// let message = b"Hidden message"; -/// let ciphertext = encryptor.encrypt_easy(aad.as_ref(), message.as_ref()).unwrap(); +/// let ciphertext = encryptor +/// .encrypt_easy(aad.as_ref(), message.as_ref()) +/// .unwrap(); /// /// let res = encryptor.decrypt_easy(aad.as_ref(), ciphertext.as_slice()); /// assert_eq!(res.unwrap().as_slice(), message); diff --git a/data_model/derive/src/lib.rs b/data_model/derive/src/lib.rs index d98c1f6de1f..c6767957413 100644 --- a/data_model/derive/src/lib.rs +++ b/data_model/derive/src/lib.rs @@ -23,7 +23,7 @@ use proc_macro2::TokenStream; /// #[enum_ref(derive(Encode))] /// pub enum InnerEnum { /// A(u32), -/// B(i32) +/// B(i32), /// } /// /// #[derive(EnumRef)] @@ -51,7 +51,6 @@ use proc_macro2::TokenStream; /// } /// */ /// ``` -/// #[manyhow] #[proc_macro_derive(EnumRef, attributes(enum_ref))] pub fn enum_ref(input: TokenStream) -> Result { @@ -79,13 +78,13 @@ pub fn enum_ref(input: TokenStream) -> Result { /// #[model] /// mod model { /// pub struct DataModel1 { -/// pub item1: u32, -/// item2: u64 +/// pub item1: u32, +/// item2: u64, /// } /// /// pub(crate) struct DataModel2 { -/// pub item1: u32, -/// item2: u64 +/// pub item1: u32, +/// item2: u64, /// } /// } /// @@ -177,8 +176,8 @@ pub fn model_single(input: TokenStream) -> TokenStream { /// The common use-case: /// /// ``` +/// use iroha_data_model::{IdBox, Identifiable}; /// use iroha_data_model_derive::IdEqOrdHash; -/// use iroha_data_model::{Identifiable, IdBox}; /// /// #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] /// struct Id { @@ -237,8 +236,8 @@ pub fn model_single(input: TokenStream) -> TokenStream { /// Manual selection of the identifier field: /// /// ``` +/// use iroha_data_model::{IdBox, Identifiable}; /// use iroha_data_model_derive::IdEqOrdHash; -/// use iroha_data_model::{Identifiable, IdBox}; /// /// #[derive(Debug, IdEqOrdHash)] /// struct InnerStruct { @@ -275,7 +274,6 @@ pub fn model_single(input: TokenStream) -> TokenStream { /// name: u32, /// } /// ``` -/// #[manyhow] #[proc_macro_derive(IdEqOrdHash, attributes(id, opaque))] pub fn id_eq_ord_hash(input: TokenStream) -> TokenStream { @@ -292,8 +290,8 @@ pub fn id_eq_ord_hash(input: TokenStream) -> TokenStream { /// Derive `::serde::Serialize` trait for `enum` with possibility to avoid tags for selected variants /// /// ``` -/// use serde::Serialize; /// use iroha_data_model_derive::PartiallyTaggedSerialize; +/// use serde::Serialize; /// /// #[derive(PartiallyTaggedSerialize)] /// enum Outer { @@ -308,11 +306,13 @@ pub fn id_eq_ord_hash(input: TokenStream) -> TokenStream { /// } /// /// assert_eq!( -/// &serde_json::to_string(&Outer::Inner(Inner::B(42))).expect("Failed to serialize"), r#"{"B":42}"# +/// &serde_json::to_string(&Outer::Inner(Inner::B(42))).expect("Failed to serialize"), +/// r#"{"B":42}"# /// ); /// /// assert_eq!( -/// &serde_json::to_string(&Outer::A(42)).expect("Failed to serialize"), r#"{"A":42}"# +/// &serde_json::to_string(&Outer::A(42)).expect("Failed to serialize"), +/// r#"{"A":42}"# /// ); /// ``` #[manyhow] @@ -326,10 +326,11 @@ pub fn partially_tagged_serialize_derive(input: TokenStream) -> Result Result(r#"{"B":42}"#).expect("Failed to deserialize B"), Outer::Inner(Inner::B(42)) +/// serde_json::from_str::(r#"{"B":42}"#).expect("Failed to deserialize B"), +/// Outer::Inner(Inner::B(42)) /// ); /// /// assert_eq!( -/// serde_json::from_str::(r#"{"A":42}"#).expect("Failed to deserialize A"), Outer::A(42) +/// serde_json::from_str::(r#"{"A":42}"#).expect("Failed to deserialize A"), +/// Outer::A(42) /// ); /// ``` /// @@ -408,9 +411,8 @@ pub fn partially_tagged_deserialize_derive(input: TokenStream) -> Result Result 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 2e9a73504e3..d884e2e79c6 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 6421c164457..aee03ed9d89 100644 --- a/data_model/src/query/predicate.rs +++ b/data_model/src/query/predicate.rs @@ -66,7 +66,6 @@ where /// PredicateSymbol::test_conformity(vec![true, false]); /// } /// ``` - /// fn test_conformity(values: Vec) where Self: PartialEq + Clone, @@ -89,48 +88,48 @@ where /// /// #[derive(Clone, PartialEq)] /// enum Check { - /// Good, - /// // Encapsulates reason for badness which - /// // doesn't behave associatively - /// // (but if we ignore it, Check as a whole does) - /// Bad(String), + /// Good, + /// // Encapsulates reason for badness which + /// // doesn't behave associatively + /// // (but if we ignore it, Check as a whole does) + /// Bad(String), /// } /// /// impl core::ops::Not for Check { - /// type Output = Self; - /// fn not(self) -> Self { - /// // ... - /// todo!() - /// } + /// type Output = Self; + /// fn not(self) -> Self { + /// // ... + /// todo!() + /// } /// } /// /// impl PredicateSymbol for Check { - /// fn and(self, other: Self) -> ControlFlow { - /// // ... - /// todo!() - /// } + /// fn and(self, other: Self) -> ControlFlow { + /// // ... + /// todo!() + /// } /// - /// fn or(self, other: Self) -> ControlFlow { - /// // ... - /// todo!() - /// } + /// fn or(self, other: Self) -> ControlFlow { + /// // ... + /// todo!() + /// } /// } /// /// fn shallow_eq(left: &Check, right: &Check) -> bool { - /// match (left, right) { - /// (Check::Good, Check::Good) | (Check::Bad(_), Check::Bad(_)) => true, - /// _ => false - /// } + /// match (left, right) { + /// (Check::Good, Check::Good) | (Check::Bad(_), Check::Bad(_)) => true, + /// _ => false, + /// } /// } /// /// fn test() { - /// let good = Check::Good; - /// let bad = Check::Bad("example".to_owned()); - /// // Would fail some assertions, since derived PartialEq is "deep" - /// // PredicateSymbol::test_conformity(vec![good, bad]); + /// let good = Check::Good; + /// let bad = Check::Bad("example".to_owned()); + /// // Would fail some assertions, since derived PartialEq is "deep" + /// // PredicateSymbol::test_conformity(vec![good, bad]); /// - /// // Works as expected - /// PredicateSymbol::test_conformity_with_eq(vec![good, bad], shallow_eq); + /// // Works as expected + /// PredicateSymbol::test_conformity_with_eq(vec![good, bad], shallow_eq); /// } /// ``` fn test_conformity_with_eq(values: Vec, shallow_eq: impl FnMut(&Self, &Self) -> bool) @@ -615,6 +614,8 @@ pub mod string { use super::*; mod id_box { + use iroha_crypto::KeyPair; + use super::*; use crate::peer::PeerId; @@ -690,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] @@ -1238,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/ffi/derive/src/lib.rs b/ffi/derive/src/lib.rs index 195c15ec0aa..3166d2b6444 100644 --- a/ffi/derive/src/lib.rs +++ b/ffi/derive/src/lib.rs @@ -233,8 +233,7 @@ pub fn ffi_type_derive(input: TokenStream) -> TokenStream { /// /// // For a struct such as: /// #[iroha_ffi::ffi_export] -/// #[derive(iroha_ffi::FfiType)] -/// #[derive(Clone, Getters)] +/// #[derive(iroha_ffi::FfiType, Clone, Getters)] /// #[getset(get = "pub")] /// pub struct Foo { /// /// Id of the struct @@ -247,7 +246,10 @@ pub fn ffi_type_derive(input: TokenStream) -> TokenStream { /// impl Foo { /// /// Construct new type /// pub fn new(id: u8) -> Self { -/// Self {id, bar: Vec::new()} +/// Self { +/// id, +/// bar: Vec::new(), +/// } /// } /// /// Return bar /// pub fn bar(&self) -> &[u8] { @@ -392,17 +394,17 @@ pub fn ffi_export(attr: TokenStream, item: TokenStream) -> TokenStream { /// ```rust /// #[iroha_ffi::ffi_import] /// pub fn return_first_elem_from_arr(arr: [u8; 8]) -> u8 { -/// // The body of this function is replaced with something like the following: -/// // let mut store = Default::default(); -/// // let arr = iroha_ffi::FfiConvert::into_ffi(arr, &mut store); -/// // let output = MaybeUninit::uninit(); -/// // -/// // let call_res = __return_first_elem_from_arr(arr, output.as_mut_ptr()); -/// // if iroha_ffi::FfiReturn::Ok != call_res { -/// // panic!("Function call failed"); -/// // } -/// // -/// // iroha_ffi::FfiOutPtrRead::try_read_out(output.assume_init()).expect("Invalid type") +/// // The body of this function is replaced with something like the following: +/// // let mut store = Default::default(); +/// // let arr = iroha_ffi::FfiConvert::into_ffi(arr, &mut store); +/// // let output = MaybeUninit::uninit(); +/// // +/// // let call_res = __return_first_elem_from_arr(arr, output.as_mut_ptr()); +/// // if iroha_ffi::FfiReturn::Ok != call_res { +/// // panic!("Function call failed"); +/// // } +/// // +/// // iroha_ffi::FfiOutPtrRead::try_read_out(output.assume_init()).expect("Invalid type") /// } /// /// /* The following functions will be declared: 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/primitives/src/unique_vec.rs b/primitives/src/unique_vec.rs index 2fa03d69ce0..0eb1ea5aa26 100644 --- a/primitives/src/unique_vec.rs +++ b/primitives/src/unique_vec.rs @@ -150,8 +150,8 @@ impl<'de, T: PartialEq + Deserialize<'de>> UniqueVec { /// # Example /// /// ``` - /// use serde::{Deserialize, de::Error as _}; /// use iroha_primitives::unique_vec::UniqueVec; + /// use serde::{de::Error as _, Deserialize}; /// /// #[derive(Debug, PartialEq, Deserialize)] /// pub struct Config { @@ -160,10 +160,7 @@ impl<'de, T: PartialEq + Deserialize<'de>> UniqueVec { /// } /// /// let err = serde_json::from_str::(r#"{"numbers": [1, 2, 3, 2, 4, 5]}"#).unwrap_err(); - /// assert_eq!( - /// err.to_string(), - /// "Duplicated value at line 1 column 25", - /// ); + /// assert_eq!(err.to_string(), "Duplicated value at line 1 column 25",); /// ``` pub fn deserialize_failing_on_duplicates(deserializer: D) -> Result where @@ -189,8 +186,8 @@ impl<'de, T: Debug + PartialEq + Deserialize<'de>> UniqueVec { /// # Example /// /// ``` - /// use serde::{Deserialize, de::Error as _}; /// use iroha_primitives::unique_vec::UniqueVec; + /// use serde::{de::Error as _, Deserialize}; /// /// #[derive(Debug, PartialEq, Deserialize)] /// pub struct Config { @@ -198,7 +195,8 @@ impl<'de, T: Debug + PartialEq + Deserialize<'de>> UniqueVec { /// arrays: UniqueVec>, /// } /// - /// let err = serde_json::from_str::(r#"{"arrays": [[1, 2, 3], [9, 8], [1, 2, 3]]}"#).unwrap_err(); + /// let err = serde_json::from_str::(r#"{"arrays": [[1, 2, 3], [9, 8], [1, 2, 3]]}"#) + /// .unwrap_err(); /// assert_eq!( /// err.to_string(), /// "Duplicated value `[1, 2, 3]` at line 1 column 41", @@ -228,8 +226,8 @@ impl<'de, T: Display + PartialEq + Deserialize<'de>> UniqueVec { /// # Example /// /// ``` - /// use serde::{Deserialize, de::Error as _}; /// use iroha_primitives::unique_vec::UniqueVec; + /// use serde::{de::Error as _, Deserialize}; /// /// #[derive(Debug, PartialEq, Deserialize)] /// pub struct Config { @@ -238,10 +236,7 @@ impl<'de, T: Display + PartialEq + Deserialize<'de>> UniqueVec { /// } /// /// let err = serde_json::from_str::(r#"{"numbers": [1, 2, 3, 2, 4, 5]}"#).unwrap_err(); - /// assert_eq!( - /// err.to_string(), - /// "Duplicated value `2` at line 1 column 25", - /// ); + /// assert_eq!(err.to_string(), "Duplicated value `2` at line 1 column 25",); /// ``` pub fn display_deserialize_failing_on_duplicates(deserializer: D) -> Result where 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/derive/src/lib.rs b/smart_contract/derive/src/lib.rs index ca6b1789cd5..1e6a19e10b1 100644 --- a/smart_contract/derive/src/lib.rs +++ b/smart_contract/derive/src/lib.rs @@ -15,7 +15,6 @@ mod entrypoint; /// - If function has a return type /// /// # Examples -/// // `ignore` because this macro idiomatically should be imported from `iroha_wasm` crate. // /// Using without parameters: 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 59beda04b12..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) /// } /// ``` @@ -324,7 +324,6 @@ pub fn derive_visit(input: TokenStream) -> TokenStream { /// block_height: u64, /// host: smart_contract::Host, /// } -/// /// ``` #[manyhow] #[proc_macro_derive(ValidateEntrypoints, attributes(entrypoints))] 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/telemetry/derive/src/lib.rs b/telemetry/derive/src/lib.rs index 21f1d5bcbaf..ae41e9f6e5c 100644 --- a/telemetry/derive/src/lib.rs +++ b/telemetry/derive/src/lib.rs @@ -142,7 +142,7 @@ impl ToTokens for MetricSpec { /// # Examples /// /// ```rust -/// use iroha_core::state::{World, StateTransaction}; +/// use iroha_core::state::{StateTransaction, World}; /// use iroha_telemetry_derive::metrics; /// /// #[metrics(+"test_query", "another_test_query_without_timing")] diff --git a/test_samples/Cargo.toml b/test_samples/Cargo.toml new file mode 100644 index 00000000000..276af31906f --- /dev/null +++ b/test_samples/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "test_samples" +edition.workspace = true +version.workspace = true +authors.workspace = true +description.workspace = true +repository.workspace = true +documentation.workspace = true +homepage.workspace = true +license.workspace = true +keywords.workspace = true +categories.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +iroha_crypto = { workspace = true } +iroha_data_model = { workspace = true } + +once_cell = { workspace = true } +serde = { workspace = true, features = ["derive"] } +toml = { workspace = true } + +[lints] +workspace = true + +[features] +default = ["rand"] +rand = ["iroha_crypto/rand"] diff --git a/test_samples/src/lib.rs b/test_samples/src/lib.rs new file mode 100644 index 00000000000..ae381e8fa12 --- /dev/null +++ b/test_samples/src/lib.rs @@ -0,0 +1,43 @@ +//! Utility crate for standardized and random signatories. + +use iroha_crypto::{Algorithm, KeyPair, PrivateKey}; +use iroha_data_model::prelude::AccountId; +use once_cell::sync::Lazy; + +/// Generate [`AccountId`](iroha_data_model::account::AccountId) in the given `domain`. +/// +/// # Panics +/// +/// Panics if the given `domain` is invalid as [`Name`](iroha_data_model::name::Name). +#[cfg(feature = "rand")] +pub fn gen_account_in(domain: impl core::fmt::Display) -> (AccountId, KeyPair) { + let key_pair = KeyPair::random(); + let account_id = format!("{}@{}", key_pair.public_key(), domain) + .parse() + .expect("domain name should be valid"); + (account_id, key_pair) +} + +macro_rules! static_signatory_ed25519 { + ( $kp:ident, $vk:expr, $sk:expr ) => { + /// A standardized [`KeyPair`](iroha_crypto::KeyPair). + pub static $kp: Lazy = Lazy::new(|| { + KeyPair::new( + $vk.parse().unwrap(), + PrivateKey::from_hex(Algorithm::Ed25519, $sk).unwrap(), + ) + .unwrap() + }); + }; + ( $id:ident, $dm:literal, $kp:ident, $vk:literal, $sk:literal ) => { + /// A standardized [`AccountId`](iroha_data_model::account::AccountId). + pub static $id: Lazy = + Lazy::new(|| format!("{}@{}", $kp.public_key(), $dm).parse().unwrap()); + + static_signatory_ed25519!($kp, $vk, $sk); + }; +} +static_signatory_ed25519!(PEER_KEYPAIR, "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0", "9AC47ABF59B356E0BD7DCBBBB4DEC080E302156A48CA907E47CB6AEA1D32719E7233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"); +static_signatory_ed25519!(ALICE_ID, "wonderland", ALICE_KEYPAIR, "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03", "CCF31D85E3B32A4BEA59987CE0C78E3B8E2DB93881468AB2435FE45D5C9DCD53CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03"); +static_signatory_ed25519!(BOB_ID, "wonderland", BOB_KEYPAIR, "ed012004FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016", "AF3F96DEEF44348FEB516C057558972CEC4C75C4DB9C5B3AAC843668854BF82804FF5B81046DDCCF19E2E451C45DFB6F53759D4EB30FA2EFA807284D1CC33016"); +static_signatory_ed25519!(CARPENTER_ID, "garden_of_live_flowers", CARPENTER_KEYPAIR, "ed0120E9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99", "B5DD003D106B273F3628A29E6087C31CE12C9F32223BE26DD1ADB85CEBB48E1DE9F632D3034BAB6BB26D92AC8FD93EF878D9C5E69E01B61B4C47101884EE2F99"); diff --git a/tools/kagami/Cargo.toml b/tools/kagami/Cargo.toml index 7a4d4145ecf..1f076c54b85 100644 --- a/tools/kagami/Cargo.toml +++ b/tools/kagami/Cargo.toml @@ -19,6 +19,7 @@ iroha_data_model = { workspace = true } iroha_schema_gen = { workspace = true } iroha_primitives = { workspace = true } iroha_genesis = { workspace = true } +test_samples = { workspace = true } color-eyre = { workspace = true } clap = { workspace = true, features = ["derive"] } diff --git a/tools/kagami/src/genesis.rs b/tools/kagami/src/genesis.rs index 1a7292df8b8..a89251fa8a6 100644 --- a/tools/kagami/src/genesis.rs +++ b/tools/kagami/src/genesis.rs @@ -12,8 +12,11 @@ use iroha_data_model::{ parameter::{default::*, ParametersBuilder}, prelude::AssetId, }; -use iroha_genesis::{executor_state, RawGenesisBlockBuilder, RawGenesisBlockFile}; +use iroha_genesis::{ + executor_state, RawGenesisBlockBuilder, RawGenesisBlockFile, GENESIS_ACCOUNT_ID, +}; use serde_json::json; +use test_samples::{gen_account_in, ALICE_ID, BOB_ID, CARPENTER_ID}; use super::*; @@ -81,19 +84,15 @@ pub fn generate_default( let mut genesis = builder .domain_with_metadata("wonderland".parse()?, meta.clone()) - .account_with_metadata( - "alice".parse()?, - crate::DEFAULT_PUBLIC_KEY.parse()?, - meta.clone(), - ) - .account_with_metadata("bob".parse()?, crate::DEFAULT_PUBLIC_KEY.parse()?, meta) + .account_with_metadata(ALICE_ID.signatory().clone(), meta.clone()) + .account_with_metadata(BOB_ID.signatory().clone(), meta) .asset( "rose".parse()?, AssetValueType::Numeric(NumericSpec::default()), ) .finish_domain() .domain("garden_of_live_flowers".parse()?) - .account("carpenter".parse()?, crate::DEFAULT_PUBLIC_KEY.parse()?) + .account(CARPENTER_ID.signatory().clone()) .asset( "cabbage".parse()?, AssetValueType::Numeric(NumericSpec::default()), @@ -101,33 +100,37 @@ pub fn generate_default( .finish_domain() .build(); - let alice_id = AccountId::from_str("alice@wonderland")?; let mint = Mint::asset_numeric( 13u32, - AssetId::new("rose#wonderland".parse()?, alice_id.clone()), + AssetId::new("rose#wonderland".parse()?, ALICE_ID.clone()), ); let mint_cabbage = Mint::asset_numeric( 44u32, - AssetId::new("cabbage#garden_of_live_flowers".parse()?, alice_id.clone()), + AssetId::new("cabbage#garden_of_live_flowers".parse()?, ALICE_ID.clone()), ); let grant_permission_to_set_parameters = Grant::permission( PermissionToken::new("CanSetParameters".parse()?, &json!(null)), - alice_id.clone(), + ALICE_ID.clone(), + ); + let transfer_rose_ownership = Transfer::asset_definition( + GENESIS_ACCOUNT_ID.clone(), + "rose#wonderland".parse()?, + ALICE_ID.clone(), ); - let transfer_domain_ownerhip = Transfer::domain( - "genesis@genesis".parse()?, + let transfer_wonderland_ownership = Transfer::domain( + GENESIS_ACCOUNT_ID.clone(), "wonderland".parse()?, - alice_id.clone(), + ALICE_ID.clone(), ); let register_user_metadata_access = Register::role( Role::new("ALICE_METADATA_ACCESS".parse()?) .add_permission(PermissionToken::new( "CanSetKeyValueInAccount".parse()?, - &json!({ "account_id": alice_id }), + &json!({ "account_id": ALICE_ID.clone() }), )) .add_permission(PermissionToken::new( "CanRemoveKeyValueInAccount".parse()?, - &json!({ "account_id": alice_id }), + &json!({ "account_id": ALICE_ID.clone() }), )), ) .into(); @@ -176,7 +179,8 @@ pub fn generate_default( for isi in [ mint.into(), mint_cabbage.into(), - transfer_domain_ownerhip.into(), + transfer_rose_ownership.into(), + transfer_wonderland_ownership.into(), grant_permission_to_set_parameters.into(), ] .into_iter() @@ -207,12 +211,10 @@ fn generate_synthetic( first_transaction .append_instruction(Register::domain(Domain::new(domain_id.clone())).into()); - for account in 0..accounts_per_domain { - let (public_key, _) = iroha_crypto::KeyPair::random().into_parts(); - let account_id: AccountId = format!("account_{account}@{domain_id}").parse()?; - first_transaction.append_instruction( - Register::account(Account::new(account_id.clone(), public_key)).into(), - ); + for _ in 0..accounts_per_domain { + let (account_id, _account_keypair) = gen_account_in(&domain_id); + first_transaction + .append_instruction(Register::account(Account::new(account_id.clone())).into()); } for asset in 0..assets_per_domain { diff --git a/tools/kagami/src/main.rs b/tools/kagami/src/main.rs index 16228701582..270e140994e 100644 --- a/tools/kagami/src/main.rs +++ b/tools/kagami/src/main.rs @@ -1,10 +1,7 @@ //! CLI for generating iroha sample configuration, genesis and //! cryptographic key pairs. To be used with all compliant Iroha //! installations. -use std::{ - io::{stdout, BufWriter, Write}, - str::FromStr as _, -}; +use std::io::{stdout, BufWriter, Write}; use clap::{Args as ClapArgs, Parser}; use color_eyre::eyre::WrapErr as _; @@ -17,12 +14,6 @@ mod schema; /// Outcome shorthand used throughout this crate pub(crate) type Outcome = color_eyre::Result<()>; -// The reason for hard-coding this default is to ensure that the -// algorithm is matched to the public key in Ed25519 format. If -// you need to change either, you should definitely change both. -const DEFAULT_PUBLIC_KEY: &str = - "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"; - fn main() -> Outcome { color_eyre::install()?; let args = Args::parse(); diff --git a/tools/parity_scale_cli/Cargo.toml b/tools/parity_scale_cli/Cargo.toml index 2b797c55a8f..aa8bf8d2c29 100644 --- a/tools/parity_scale_cli/Cargo.toml +++ b/tools/parity_scale_cli/Cargo.toml @@ -38,3 +38,6 @@ parity-scale-codec = { workspace = true } serde_json = { workspace = true, features = ["std"]} serde = { workspace = true } eyre = { workspace = true } + +[dev-dependencies] +test_samples = { workspace = true } diff --git a/tools/parity_scale_cli/samples/account.bin b/tools/parity_scale_cli/samples/account.bin index c0ac2716337241b4188de397c53069ecccff7fff..b9ece2bea129b09d08ab75f268ff25124506eb1b 100644 GIT binary patch literal 57 zcmV-90LK3)cW-WFWpZp`Ze##}&VQtAoz8xwu_fZ;YujV$GY39(kgSq{YT@W%w*-p= P1Po|lbOIE2XlZn12nrdD literal 64 zcmdNW&(BLqEy_vEOA$%T$xKdVVQ6^!?fO=o0;3C7Z!+^0GWG@wh-!R3zKuirKkLFY U-TFhWEIb*BB}^ja8JQ)i09qXxvH$=8 diff --git a/tools/parity_scale_cli/samples/account.json b/tools/parity_scale_cli/samples/account.json index 7bb4321521b..c749623fb95 100644 --- a/tools/parity_scale_cli/samples/account.json +++ b/tools/parity_scale_cli/samples/account.json @@ -1,8 +1,5 @@ { - "id": "alice@wonderland", - "signatories": [ - "ed0120EDF6D7B52C7032D03AEC696F2068BD53101528F3C7B6081BFF05A1662D7FC245" - ], + "id": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland", "metadata": { "hat": { "Name": "white" diff --git a/tools/parity_scale_cli/samples/trigger.bin b/tools/parity_scale_cli/samples/trigger.bin index bf1bf3f4b9e0f3dd4531d14c81a9540770bbaa9e..35d40540b8b3e8c4e6bd6d2b9b8c6a8bffc9f2a3 100644 GIT binary patch literal 132 zcmZQj$<53wi7(18PGw+WVqjs=D9_JJNiE7r%u5jfi6ZeC8qU=($(ehuZplW~M~|~_ iC%-agw=12nW>RC;!xxF$S-P2#brV+2$i%?N00aQI|28lH literal 76 zcmZQj$<53wi7(18PGw+WVq{^^D9_JJNiE7r%u5jfi6Zeu5_2+>Q;}6bL>QSE7#V;7 E0H9YE^8f$< diff --git a/tools/parity_scale_cli/samples/trigger.json b/tools/parity_scale_cli/samples/trigger.json index bcc84114c88..6c74b0f5a53 100644 --- a/tools/parity_scale_cli/samples/trigger.json +++ b/tools/parity_scale_cli/samples/trigger.json @@ -7,14 +7,14 @@ "Mint": { "Asset": { "object": "1", - "destination_id": "rose##alice@wonderland" + "destination_id": "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" } } } ] }, "repeats": "Indefinitely", - "authority": "alice@wonderland", + "authority": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland", "filter": { "Data": { "Domain": { diff --git a/tools/parity_scale_cli/src/main.rs b/tools/parity_scale_cli/src/main.rs index abe04675b8f..b1dca086904 100644 --- a/tools/parity_scale_cli/src/main.rs +++ b/tools/parity_scale_cli/src/main.rs @@ -219,7 +219,6 @@ impl<'map> ScaleToRustDecoder<'map> { } /// Try to decode every type from `bytes` and print to `writer` - /// // TODO: Can be parallelized when there will be too many types fn decode_by_guess(&self, bytes: &[u8], writer: &mut W) -> Result<()> { let count = self @@ -322,6 +321,7 @@ mod tests { use std::str::FromStr as _; use iroha_data_model::{ipfs::IpfsPath, prelude::*}; + use test_samples::gen_account_in; use super::*; @@ -336,12 +336,7 @@ mod tests { limits, ) .expect("Valid"); - let signature = PublicKey::from_str( - "ed0120EDF6D7B52C7032D03AEC696F2068BD53101528F3C7B6081BFF05A1662D7FC245", - ) - .unwrap(); - let account = - Account::new("alice@wonderland".parse().unwrap(), signature).with_metadata(metadata); + let account = Account::new(gen_account_in("wonderland").0).with_metadata(metadata); decode_sample("account.bin", String::from("NewAccount"), &account); } @@ -365,7 +360,7 @@ mod tests { #[test] fn decode_trigger_sample() { - let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let (account_id, _account_keypair) = gen_account_in("wonderland"); let rose_definition_id = AssetDefinitionId::new( "wonderland".parse().expect("Valid"), "rose".parse().expect("Valid"), diff --git a/torii/src/lib.rs b/torii/src/lib.rs index 94d3b11ad79..c2445065125 100644 --- a/torii/src/lib.rs +++ b/torii/src/lib.rs @@ -341,7 +341,7 @@ impl Error { Config(_) | StatusSegmentNotFound(_) => StatusCode::NOT_FOUND, PushIntoQueue(err) => match **err { queue::Error::Full => StatusCode::INTERNAL_SERVER_ERROR, - queue::Error::SignatureCondition => StatusCode::UNAUTHORIZED, + queue::Error::SignatoryInconsistent => StatusCode::UNAUTHORIZED, _ => StatusCode::BAD_REQUEST, }, #[cfg(feature = "telemetry")] diff --git a/torii/src/routing.rs b/torii/src/routing.rs index edff834fed4..2d2aa7fcdd7 100644 --- a/torii/src/routing.rs +++ b/torii/src/routing.rs @@ -360,6 +360,7 @@ pub mod profiling { use super::*; /// Query params used to configure profile gathering + #[allow(clippy::unsafe_derive_deserialize)] #[derive(Serialize, Deserialize, Clone, Copy)] pub struct ProfileParams { /// How often to sample iroha diff --git a/wasm_builder/src/lib.rs b/wasm_builder/src/lib.rs index 8d56caa5f03..46b252ae012 100644 --- a/wasm_builder/src/lib.rs +++ b/wasm_builder/src/lib.rs @@ -21,8 +21,8 @@ const TOOLCHAIN: &str = "+nightly-2024-04-18"; /// # Example /// /// ```no_run -/// use iroha_wasm_builder::Builder; /// use eyre::Result; +/// use iroha_wasm_builder::Builder; /// /// fn main() -> Result<()> { /// let bytes = Builder::new("relative/path/to/smartcontract/")