diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 0749c3bce2b..b84c0ebbea8 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -259,6 +259,7 @@ impl Iroha { let (events_sender, _) = broadcast::channel(10000); let world = World::with( [genesis_domain(config.genesis.public_key.clone())], + [genesis_account(config.genesis.public_key.clone())], config .sumeragi .trusted_peers @@ -554,14 +555,7 @@ fn genesis_account(public_key: PublicKey) -> Account { fn genesis_domain(public_key: PublicKey) -> Domain { let genesis_account = genesis_account(public_key); - let mut domain = - Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()).build(&genesis_account.id); - - domain - .accounts - .insert(genesis_account.id.clone(), genesis_account); - - domain + Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()).build(&genesis_account.id) } /// Error of [`read_config_and_genesis`] diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm index b20774feb8d..77897205ba0 100644 Binary files a/configs/swarm/executor.wasm and b/configs/swarm/executor.wasm differ diff --git a/core/benches/blocks/common.rs b/core/benches/blocks/common.rs index 7f658ac61a4..30ac642aee4 100644 --- a/core/benches/blocks/common.rs +++ b/core/benches/blocks/common.rs @@ -172,12 +172,16 @@ pub fn build_state(rt: &tokio::runtime::Handle, account_id: &AccountId) -> State let _guard = rt.enter(); LiveQueryStore::test().start() }; - let mut domain = Domain::new(account_id.domain().clone()).build(account_id); - domain.accounts.insert( - account_id.clone(), - Account::new(account_id.clone()).build(account_id), + let domain = Domain::new(account_id.domain().clone()).build(account_id); + let state = State::new( + World::with( + [domain], + [Account::new(account_id.clone()).build(account_id)], + UniqueVec::new(), + ), + kura, + query_handle, ); - let state = State::new(World::with([domain], UniqueVec::new()), kura, query_handle); { let mut state_block = state.block(); diff --git a/core/benches/validation.rs b/core/benches/validation.rs index 009e39fe00a..bb2820d1379 100644 --- a/core/benches/validation.rs +++ b/core/benches/validation.rs @@ -51,7 +51,6 @@ fn build_test_and_transient_state() -> State { 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()) }, kura, diff --git a/core/src/block.rs b/core/src/block.rs index aaa63fa433a..9e5f89c3945 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -1004,9 +1004,8 @@ mod tests { 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()); - let world = World::with([domain], UniqueVec::new()); + let domain = Domain::new(domain_id).build(&alice_id); + let world = World::with([domain], [account], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world, kura, query_handle); @@ -1061,9 +1060,8 @@ mod tests { 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()); - let world = World::with([domain], UniqueVec::new()); + let domain = Domain::new(domain_id).build(&alice_id); + let world = World::with([domain], [account], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world, kura, query_handle); @@ -1136,12 +1134,8 @@ mod tests { let (alice_id, alice_keypair) = gen_account_in("wonderland"); let account = Account::new(alice_id.clone()).build(&alice_id); let domain_id = DomainId::from_str("wonderland").expect("Valid"); - let mut domain = Domain::new(domain_id).build(&alice_id); - assert!( - domain.add_account(account).is_none(), - "{alice_id} already exist in the blockchain" - ); - let world = World::with([domain], UniqueVec::new()); + let domain = Domain::new(domain_id).build(&alice_id); + let world = World::with([domain], [account], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world, kura, query_handle); @@ -1219,12 +1213,11 @@ mod tests { GENESIS_DOMAIN_ID.clone(), genesis_wrong_key.public_key().clone(), ); - let mut genesis_domain = + let genesis_domain = Domain::new(GENESIS_DOMAIN_ID.clone()).build(&genesis_correct_account_id); let genesis_wrong_account = Account::new(genesis_wrong_account_id.clone()).build(&genesis_wrong_account_id); - assert!(genesis_domain.add_account(genesis_wrong_account).is_none(),); - let world = World::with([genesis_domain], UniqueVec::new()); + let world = World::with([genesis_domain], [genesis_wrong_account], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world, kura, query_handle); diff --git a/core/src/metrics.rs b/core/src/metrics.rs index f555e677ae4..379690e74a1 100644 --- a/core/src/metrics.rs +++ b/core/src/metrics.rs @@ -137,7 +137,12 @@ impl MetricsReporter { .accounts .get_metric_with_label_values(&[domain.id.name.as_ref()]) .wrap_err("Failed to compose domains")? - .set(domain.accounts.len() as u64); + .set( + state_view + .world() + .accounts_in_domain_iter(&domain.id) + .count() as u64, + ); } self.metrics.queue_size.set(self.queue.tx_len() as u64); diff --git a/core/src/queue.rs b/core/src/queue.rs index 1e343eb4033..658ffafe405 100644 --- a/core/src/queue.rs +++ b/core/src/queue.rs @@ -437,10 +437,9 @@ pub mod tests { pub fn world_with_test_domains() -> World { let domain_id = DomainId::from_str("wonderland").expect("Valid"); let (account_id, _account_keypair) = gen_account_in("wonderland"); - let mut domain = Domain::new(domain_id).build(&account_id); + let domain = Domain::new(domain_id).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()) + World::with([domain], [account], PeersIds::new()) } fn config_factory() -> Config { @@ -833,12 +832,10 @@ pub mod tests { let (bob_id, bob_keypair) = gen_account_in("wonderland"); let world = { let domain_id = DomainId::from_str("wonderland").expect("Valid"); - let mut domain = Domain::new(domain_id).build(&alice_id); + let 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()) + World::with([domain], [alice_account, bob_account], PeersIds::new()) }; let query_handle = LiveQueryStore::test().start(); let state = State::new(world, kura, query_handle); diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index 09afde491a1..bea9b9c375f 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -546,13 +546,7 @@ pub mod query { &self, state_ro: &'state impl StateReadOnly, ) -> Result + 'state>, Error> { - Ok(Box::new( - state_ro - .world() - .domains_iter() - .flat_map(|domain| domain.accounts.values()) - .cloned(), - )) + Ok(Box::new(state_ro.world().accounts_iter().cloned())) } } @@ -561,6 +555,10 @@ pub mod query { fn execute(&self, state_ro: &impl StateReadOnly) -> Result { let id = &self.id; iroha_logger::trace!(%id); + state_ro + .world() + .domain(id.domain()) + .map_err(|_| FindError::Domain(id.domain().clone()))?; state_ro .world() .map_account(id, Clone::clone) @@ -578,7 +576,7 @@ pub mod query { iroha_logger::trace!(%id); Ok(Box::new( - state_ro.world().domain(id)?.accounts.values().cloned(), + state_ro.world().accounts_in_domain_iter(id).cloned(), )) } } @@ -609,11 +607,8 @@ pub mod query { Ok(Box::new( state_ro .world() - .map_domain(&asset_definition_id.domain.clone(), move |domain| { - domain.accounts.values().filter(move |account| { - account.assets.contains_key(&asset_definition_id) - }) - })? + .accounts_iter() + .filter(move |account| account.assets.contains_key(&asset_definition_id)) .cloned(), )) } diff --git a/core/src/smartcontracts/isi/asset.rs b/core/src/smartcontracts/isi/asset.rs index 3c75c35cf35..38621f4ec5d 100644 --- a/core/src/smartcontracts/isi/asset.rs +++ b/core/src/smartcontracts/isi/asset.rs @@ -442,13 +442,8 @@ pub mod query { Ok(Box::new( state_ro .world() - .domains_iter() - .flat_map(|domain| { - domain - .accounts - .values() - .flat_map(|account| account.assets.values()) - }) + .accounts_iter() + .flat_map(|account| account.assets.values()) .cloned(), )) } @@ -507,18 +502,14 @@ pub mod query { Ok(Box::new( state_ro .world() - .domains_iter() - .flat_map(move |domain| { + .accounts_iter() + .flat_map(move |account| { let name = name.clone(); - domain.accounts.values().flat_map(move |account| { - let name = name.clone(); - - account - .assets - .values() - .filter(move |asset| asset.id().definition.name == name) - }) + account + .assets + .values() + .filter(move |asset| asset.id().definition.name == name) }) .cloned(), )) @@ -548,18 +539,14 @@ pub mod query { Ok(Box::new( state_ro .world() - .domains_iter() - .flat_map(move |domain| { + .accounts_iter() + .flat_map(move |account| { let id = id.clone(); - domain.accounts.values().flat_map(move |account| { - let id = id.clone(); - - account - .assets - .values() - .filter(move |asset| asset.id().definition == id) - }) + account + .assets + .values() + .filter(move |asset| asset.id().definition == id) }) .cloned(), )) @@ -577,9 +564,7 @@ pub mod query { Ok(Box::new( state_ro .world() - .domain(id)? - .accounts - .values() + .accounts_in_domain_iter(id) .flat_map(|account| account.assets.values()) .cloned(), )) @@ -601,9 +586,9 @@ pub mod query { .ok_or_else(|| FindError::AssetDefinition(asset_definition_id.clone()))?; iroha_logger::trace!(%domain_id, %asset_definition_id); Ok(Box::new( - domain - .accounts - .values() + state_ro + .world() + .accounts_in_domain_iter(&domain_id) .flat_map(move |account| { let domain_id = domain_id.clone(); let asset_definition_id = asset_definition_id.clone(); diff --git a/core/src/smartcontracts/isi/domain.rs b/core/src/smartcontracts/isi/domain.rs index 34efd4848b8..5dcad8e7b58 100644 --- a/core/src/smartcontracts/isi/domain.rs +++ b/core/src/smartcontracts/isi/domain.rs @@ -2,7 +2,6 @@ use eyre::Result; use iroha_data_model::{ - account::AccountsMap, asset::{AssetDefinitionsMap, AssetTotalQuantityMap}, prelude::*, query::error::FindError, @@ -19,7 +18,6 @@ impl Registrable for iroha_data_model::domain::NewDomain { fn build(self, authority: &AccountId) -> Self::Target { Self::Target { id: self.id, - accounts: AccountsMap::default(), asset_definitions: AssetDefinitionsMap::default(), asset_total_quantities: AssetTotalQuantityMap::default(), metadata: self.metadata, @@ -56,15 +54,18 @@ pub mod isi { )); } - let domain = state_transaction.world.domain_mut(&account_id.domain)?; - if domain.accounts.contains_key(&account_id) { + let _domain = state_transaction.world.domain_mut(&account_id.domain)?; + if state_transaction.world.account(&account_id).is_ok() { return Err(RepetitionError { instruction_type: InstructionType::Register, id: IdBox::AccountId(account_id), } .into()); } - domain.add_account(account.clone()); + state_transaction + .world + .accounts + .insert(account_id, account.clone()); state_transaction .world @@ -103,8 +104,8 @@ pub mod isi { if state_transaction .world - .domain_mut(&account_id.domain)? - .remove_account(&account_id) + .accounts + .remove(account_id.clone()) .is_none() { return Err(FindError::Account(account_id).into()); @@ -168,22 +169,20 @@ pub mod isi { let asset_definition_id = self.object; let mut assets_to_remove = Vec::new(); - for domain in state_transaction.world.domains_iter() { - for account in domain.accounts.values() { - assets_to_remove.extend( - account - .assets - .values() - .filter_map(|asset| { - if asset.id().definition == asset_definition_id { - return Some(asset.id()); - } - - None - }) - .cloned(), - ) - } + for (_, account) in state_transaction.world.accounts.iter() { + assets_to_remove.extend( + account + .assets + .values() + .filter_map(|asset| { + if asset.id().definition == asset_definition_id { + return Some(asset.id()); + } + + None + }) + .cloned(), + ) } let mut events = Vec::with_capacity(assets_to_remove.len() + 1); diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index e5746fdb1c3..72282ac66b4 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -259,7 +259,7 @@ mod tests { }; fn state_with_test_domains(kura: &Arc) -> Result { - let world = World::with([], PeersIds::new()); + let world = World::with([], [], PeersIds::new()); let query_handle = LiveQueryStore::test().start(); let state = State::new(world, kura.clone(), query_handle); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index 2d8a14982b1..a7f295cba25 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -313,12 +313,11 @@ mod tests { 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()).build(&ALICE_ID); - assert!(domain.add_account(account).is_none()); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); assert!(domain .add_asset_definition(AssetDefinition::numeric(asset_definition_id).build(&ALICE_ID)) .is_none()); - World::with([domain], PeersIds::new()) + World::with([domain], [account], PeersIds::new()) } fn world_with_test_asset_with_metadata() -> World { @@ -344,8 +343,7 @@ mod tests { let asset = Asset::new(asset_id, AssetValue::Store(store)); assert!(account.add_asset(asset).is_none()); - assert!(domain.add_account(account).is_none()); - World::with([domain], PeersIds::new()) + World::with([domain], [account], PeersIds::new()) } fn world_with_test_account_with_metadata() -> Result { @@ -360,12 +358,11 @@ mod tests { let account = Account::new(ALICE_ID.clone()) .with_metadata(metadata) .build(&ALICE_ID); - assert!(domain.add_account(account).is_none()); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland").expect("Valid"); assert!(domain .add_asset_definition(AssetDefinition::numeric(asset_definition_id).build(&ALICE_ID)) .is_none()); - Ok(World::with([domain], PeersIds::new())) + Ok(World::with([domain], [account], PeersIds::new())) } fn state_with_test_blocks_and_transactions( @@ -619,7 +616,6 @@ mod tests { .with_metadata(metadata) .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 .add_asset_definition( @@ -627,7 +623,11 @@ mod tests { ) .is_none()); let query_handle = LiveQueryStore::test().start(); - State::new(World::with([domain], PeersIds::new()), kura, query_handle) + State::new( + World::with([domain], [account], PeersIds::new()), + kura, + query_handle, + ) }; let domain_id = DomainId::from_str("wonderland")?; diff --git a/core/src/smartcontracts/isi/world.rs b/core/src/smartcontracts/isi/world.rs index 0839db60a3b..72b65dc194b 100644 --- a/core/src/smartcontracts/isi/world.rs +++ b/core/src/smartcontracts/isi/world.rs @@ -141,6 +141,15 @@ pub mod isi { .expect("should succeed") }); + let remove_accounts: Vec = state_transaction + .world + .accounts_in_domain_iter(&domain_id) + .map(|account| account.id().clone()) + .collect(); + for account in remove_accounts { + state_transaction.world.accounts.remove(account); + } + if state_transaction .world .domains diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index 716153983b5..9ab00dcbcf3 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -1719,10 +1719,9 @@ mod tests { fn world_with_test_account(authority: &AccountId) -> World { let domain_id = authority.domain.clone(); let account = Account::new(authority.clone()).build(authority); - let mut domain = Domain::new(domain_id).build(authority); - assert!(domain.add_account(account).is_none()); + let domain = Domain::new(domain_id).build(authority); - World::with([domain], PeersIds::new()) + World::with([domain], [account], PeersIds::new()) } fn memory_and_alloc(isi_hex: &str) -> String { diff --git a/core/src/state.rs b/core/src/state.rs index e8faf74a6b1..0c00cb5467b 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -31,7 +31,7 @@ use iroha_data_model::{ use iroha_logger::prelude::*; use iroha_primitives::{must_use::MustUse, numeric::Numeric, small::SmallVec}; use parking_lot::Mutex; -use range_bounds::RoleIdByAccountBounds; +use range_bounds::{AccountByDomainBounds, AsAccountIdDomainCompare, RoleIdByAccountBounds}; use serde::{ de::{DeserializeSeed, MapAccess, Visitor}, Deserializer, Serialize, @@ -75,6 +75,8 @@ pub struct World { pub(crate) trusted_peers_ids: Cell, /// Registered domains. pub(crate) domains: Storage, + /// Registered accounts. + pub(crate) accounts: Storage, /// Roles. [`Role`] pairs. pub(crate) roles: Storage, /// Permission tokens of an account. @@ -97,6 +99,8 @@ pub struct WorldBlock<'world> { pub(crate) trusted_peers_ids: CellBlock<'world, PeersIds>, /// Registered domains. pub(crate) domains: StorageBlock<'world, DomainId, Domain>, + /// Registered accounts. + pub(crate) accounts: StorageBlock<'world, AccountId, Account>, /// Roles. [`Role`] pairs. pub(crate) roles: StorageBlock<'world, RoleId, Role>, /// Permission tokens of an account. @@ -121,6 +125,8 @@ pub struct WorldTransaction<'block, 'world> { pub(crate) trusted_peers_ids: CellTransaction<'block, 'world, PeersIds>, /// Registered domains. pub(crate) domains: StorageTransaction<'block, 'world, DomainId, Domain>, + /// Registered accounts. + pub(crate) accounts: StorageTransaction<'block, 'world, AccountId, Account>, /// Roles. [`Role`] pairs. pub(crate) roles: StorageTransaction<'block, 'world, RoleId, Role>, /// Permission tokens of an account. @@ -153,6 +159,8 @@ pub struct WorldView<'world> { pub(crate) trusted_peers_ids: CellView<'world, PeersIds>, /// Registered domains. pub(crate) domains: StorageView<'world, DomainId, Domain>, + /// Registered accounts. + pub(crate) accounts: StorageView<'world, AccountId, Account>, /// Roles. [`Role`] pairs. pub(crate) roles: StorageView<'world, RoleId, Role>, /// Permission tokens of an account. @@ -268,17 +276,23 @@ impl World { } /// Creates a [`World`] with these [`Domain`]s and trusted [`PeerId`]s. - pub fn with(domains: D, trusted_peers_ids: PeersIds) -> Self + pub fn with(domains: D, accounts: A, trusted_peers_ids: PeersIds) -> Self where D: IntoIterator, + A: IntoIterator, { let domains = domains .into_iter() .map(|domain| (domain.id().clone(), domain)) .collect(); + let accounts = accounts + .into_iter() + .map(|account| (account.id().clone(), account)) + .collect(); World { trusted_peers_ids: Cell::new(trusted_peers_ids), domains, + accounts, ..World::new() } } @@ -289,6 +303,7 @@ impl World { parameters: self.parameters.block(), trusted_peers_ids: self.trusted_peers_ids.block(), domains: self.domains.block(), + accounts: self.accounts.block(), roles: self.roles.block(), account_permissions: self.account_permissions.block(), account_roles: self.account_roles.block(), @@ -305,6 +320,7 @@ impl World { parameters: self.parameters.block_and_revert(), trusted_peers_ids: self.trusted_peers_ids.block_and_revert(), domains: self.domains.block_and_revert(), + accounts: self.accounts.block_and_revert(), roles: self.roles.block_and_revert(), account_permissions: self.account_permissions.block_and_revert(), account_roles: self.account_roles.block_and_revert(), @@ -321,6 +337,7 @@ impl World { parameters: self.parameters.view(), trusted_peers_ids: self.trusted_peers_ids.view(), domains: self.domains.view(), + accounts: self.accounts.view(), roles: self.roles.view(), account_permissions: self.account_permissions.view(), account_roles: self.account_roles.view(), @@ -337,6 +354,7 @@ pub trait WorldReadOnly { fn parameters(&self) -> &Parameters; fn trusted_peers_ids(&self) -> &PeersIds; fn domains(&self) -> &impl StorageReadOnly; + fn accounts(&self) -> &impl StorageReadOnly; fn roles(&self) -> &impl StorageReadOnly; fn account_permissions(&self) -> &impl StorageReadOnly; fn account_roles(&self) -> &impl StorageReadOnly; @@ -378,6 +396,26 @@ pub trait WorldReadOnly { self.domains().iter().map(|(_, domain)| domain) } + /// Iterate accounts in domain + #[allow(clippy::type_complexity)] + fn accounts_in_domain_iter<'slf>( + &'slf self, + id: &DomainId, + ) -> core::iter::Map< + RangeIter<'slf, AccountId, Account>, + fn((&'slf AccountId, &'slf Account)) -> &'slf Account, + > { + self.accounts() + .range::(AccountByDomainBounds::new(id)) + .map(|(_, account)| account) + } + + /// Returns reference for domains map + #[inline] + fn accounts_iter(&self) -> impl Iterator { + self.accounts().iter().map(|(_, account)| account) + } + // Account-related methods /// Get `Account` and return reference to it. @@ -385,12 +423,9 @@ pub trait WorldReadOnly { /// # Errors /// Fails if there is no domain or account fn account(&self, id: &AccountId) -> Result<&Account, FindError> { - self.domain(&id.domain).and_then(|domain| { - domain - .accounts - .get(id) - .ok_or_else(|| FindError::Account(id.clone())) - }) + self.accounts() + .get(id) + .ok_or_else(|| FindError::Account(id.clone())) } /// Get `Account` and pass it to closure. @@ -402,9 +437,8 @@ pub trait WorldReadOnly { id: &AccountId, f: impl FnOnce(&'slf Account) -> T, ) -> Result { - let domain = self.domain(&id.domain)?; - let account = domain - .accounts + let account = self + .accounts() .get(id) .ok_or(FindError::Account(id.clone()))?; Ok(f(account)) @@ -578,6 +612,9 @@ macro_rules! impl_world_ro { fn domains(&self) -> &impl StorageReadOnly { &self.domains } + fn accounts(&self) -> &impl StorageReadOnly { + &self.accounts + } fn roles(&self) -> &impl StorageReadOnly { &self.roles } @@ -611,6 +648,7 @@ impl<'world> WorldBlock<'world> { parameters: self.parameters.transaction(), trusted_peers_ids: self.trusted_peers_ids.transaction(), domains: self.domains.transaction(), + accounts: self.accounts.transaction(), roles: self.roles.transaction(), account_permissions: self.account_permissions.transaction(), account_roles: self.account_roles.transaction(), @@ -633,6 +671,7 @@ impl<'world> WorldBlock<'world> { self.account_roles.commit(); self.account_permissions.commit(); self.roles.commit(); + self.accounts.commit(); self.domains.commit(); self.trusted_peers_ids.commit(); self.parameters.commit(); @@ -648,6 +687,7 @@ impl WorldTransaction<'_, '_> { self.account_roles.apply(); self.account_permissions.apply(); self.roles.apply(); + self.accounts.apply(); self.domains.apply(); self.trusted_peers_ids.apply(); self.parameters.apply(); @@ -671,12 +711,9 @@ impl WorldTransaction<'_, '_> { /// # Errors /// Fail if domain or account not found pub fn account_mut(&mut self, id: &AccountId) -> Result<&mut Account, FindError> { - self.domain_mut(&id.domain).and_then(move |domain| { - domain - .accounts - .get_mut(id) - .ok_or_else(|| FindError::Account(id.clone())) - }) + self.accounts + .get_mut(id) + .ok_or_else(|| FindError::Account(id.clone())) } /// Add [`permission`](Permission) to the [`Account`] if the account does not have this permission yet. @@ -746,11 +783,7 @@ impl WorldTransaction<'_, '_> { } let account_id = &asset_id.account; - let account_domain = self - .domains - .get_mut(&asset_id.account.domain) - .ok_or(FindError::Domain(asset_id.account.domain.clone()))?; - let account = account_domain + let account = self .accounts .get_mut(account_id) .ok_or(FindError::Account(account_id.clone()))?; @@ -1558,6 +1591,71 @@ mod range_bounds { key: RoleIdByAccount<'_>, trait: AsRoleIdByAccount } + + /// `DomainId` wrapper for fetching accounts beloning to a domain from the global store + #[derive(PartialEq, Eq, PartialOrd, Copy, Clone)] + pub struct AccountIdDomainCompare<'a> { + domain_id: &'a DomainId, + signatory: MinMaxExt<&'a PublicKey>, + } + + // Sorting needed to be flipped for the storage lookup to work. + impl Ord for AccountIdDomainCompare<'_> { + fn cmp(&self, other: &AccountIdDomainCompare<'_>) -> std::cmp::Ordering { + if self.domain_id == other.domain_id { + other.signatory.cmp(&self.signatory) + } else { + other.domain_id.cmp(self.domain_id) + } + } + } + + /// Bounds for range quired over accounts by domain + pub struct AccountByDomainBounds<'a> { + start: AccountIdDomainCompare<'a>, + end: AccountIdDomainCompare<'a>, + } + + impl<'a> AccountByDomainBounds<'a> { + /// Create range bounds for range quires over accounts by domain + pub fn new(domain_id: &'a DomainId) -> Self { + Self { + start: AccountIdDomainCompare { + domain_id, + signatory: MinMaxExt::Min, + }, + end: AccountIdDomainCompare { + domain_id, + signatory: MinMaxExt::Max, + }, + } + } + } + + impl<'a> RangeBounds for AccountByDomainBounds<'a> { + fn start_bound(&self) -> Bound<&(dyn AsAccountIdDomainCompare + 'a)> { + Bound::Excluded(&self.start) + } + + fn end_bound(&self) -> Bound<&(dyn AsAccountIdDomainCompare + 'a)> { + Bound::Excluded(&self.end) + } + } + + impl AsAccountIdDomainCompare for AccountId { + fn as_key(&self) -> AccountIdDomainCompare<'_> { + AccountIdDomainCompare { + domain_id: &self.domain, + signatory: (&self.signatory).into(), + } + } + } + + impl_as_dyn_key! { + target: AccountId, + key: AccountIdDomainCompare<'_>, + trait: AsAccountIdDomainCompare + } } pub(crate) mod deserialize { @@ -1654,6 +1752,7 @@ pub(crate) mod deserialize { let mut parameters = None; let mut trusted_peers_ids = None; let mut domains = None; + let mut accounts = None; let mut roles = None; let mut account_permissions = None; let mut account_roles = None; @@ -1672,6 +1771,9 @@ pub(crate) mod deserialize { "domains" => { domains = Some(map.next_value()?); } + "accounts" => { + accounts = Some(map.next_value()?); + } "roles" => { roles = Some(map.next_value()?); } @@ -1705,6 +1807,8 @@ pub(crate) mod deserialize { .ok_or_else(|| serde::de::Error::missing_field("trusted_peers_ids"))?, domains: domains .ok_or_else(|| serde::de::Error::missing_field("domains"))?, + accounts: accounts + .ok_or_else(|| serde::de::Error::missing_field("accounts"))?, roles: roles.ok_or_else(|| serde::de::Error::missing_field("roles"))?, account_permissions: account_permissions.ok_or_else(|| { serde::de::Error::missing_field("account_permissions") diff --git a/core/src/sumeragi/main_loop.rs b/core/src/sumeragi/main_loop.rs index 8f798cd2101..861bb556f66 100644 --- a/core/src/sumeragi/main_loop.rs +++ b/core/src/sumeragi/main_loop.rs @@ -1350,9 +1350,8 @@ mod tests { ); 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()); - let world = World::with([domain], topology.iter().cloned().collect()); + let domain = Domain::new(domain_id).build(&alice_id); + let world = World::with([domain], [account], topology.iter().cloned().collect()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let state = State::new(world, Arc::clone(&kura), query_handle); diff --git a/core/src/tx.rs b/core/src/tx.rs index a13b81ce40c..5c7bc266c35 100644 --- a/core/src/tx.rs +++ b/core/src/tx.rs @@ -19,10 +19,11 @@ use iroha_data_model::{ use iroha_genesis::GenesisTransaction; use iroha_logger::{debug, error}; use iroha_macro::FromVariant; +use storage::storage::StorageReadOnly; use crate::{ smartcontracts::wasm, - state::{StateBlock, StateTransaction, WorldReadOnly}, + state::{StateBlock, StateTransaction}, }; /// `AcceptedTransaction` — a transaction accepted by Iroha peer. @@ -212,17 +213,7 @@ impl TransactionExecutor { ) -> Result<(), TransactionRejectionReason> { let authority = tx.as_ref().authority(); - if !state_transaction - .world - .domain(&authority.domain) - .map_err(|_e| { - TransactionRejectionReason::AccountDoesNotExist(FindError::Domain( - authority.domain.clone(), - )) - })? - .accounts - .contains_key(authority) - { + if state_transaction.world.accounts.get(authority).is_none() { return Err(TransactionRejectionReason::AccountDoesNotExist( FindError::Account(authority.clone()), )); diff --git a/data_model/src/account.rs b/data_model/src/account.rs index 101581312da..0e99bf6d495 100644 --- a/data_model/src/account.rs +++ b/data_model/src/account.rs @@ -1,9 +1,7 @@ //! Structures, traits and impls related to `Account`s. #[cfg(not(feature = "std"))] -use alloc::{collections::btree_map, format, string::String, vec::Vec}; +use alloc::{format, string::String, vec::Vec}; use core::str::FromStr; -#[cfg(feature = "std")] -use std::collections::btree_map; use derive_more::{Constructor, DebugCustom, Display}; use getset::Getters; @@ -21,9 +19,6 @@ use crate::{ HasMetadata, Identifiable, ParseError, PublicKey, Registered, }; -/// API to work with collections of [`Id`] : [`Account`] mappings. -pub type AccountsMap = btree_map::BTreeMap; - #[model] mod model { use super::*; diff --git a/data_model/src/domain.rs b/data_model/src/domain.rs index 89df2d7bcfd..db01cd686c1 100644 --- a/data_model/src/domain.rs +++ b/data_model/src/domain.rs @@ -14,7 +14,6 @@ use serde_with::{DeserializeFromStr, SerializeDisplay}; pub use self::model::*; use crate::{ - account::{Account, AccountsMap}, asset::{AssetDefinition, AssetDefinitionsMap, AssetTotalQuantityMap}, ipfs::IpfsPath, metadata::Metadata, @@ -73,8 +72,6 @@ mod model { pub struct Domain { /// Identification of this [`Domain`]. pub id: DomainId, - /// [`Account`]s of the domain. - pub accounts: AccountsMap, /// [`Asset`](AssetDefinition)s defined of the `Domain`. pub asset_definitions: AssetDefinitionsMap, /// Total amount of [`Asset`]. @@ -158,12 +155,6 @@ impl Domain { } impl Domain { - /// Return a reference to the [`Account`] corresponding to the account id. - #[inline] - pub fn account(&self, account_id: &AccountId) -> Option<&Account> { - self.accounts.get(account_id) - } - /// Return a reference to the asset definition corresponding to the asset definition id #[inline] pub fn asset_definition( @@ -182,18 +173,6 @@ impl Domain { self.asset_total_quantities.get(asset_definition_id) } - /// Get an iterator over [`Account`]s of the `Domain` - #[inline] - pub fn accounts(&self) -> impl ExactSizeIterator { - self.accounts.values() - } - - /// Return `true` if the `Domain` contains [`Account`] - #[inline] - pub fn contains_account(&self, account_id: &AccountId) -> bool { - self.accounts.contains_key(account_id) - } - /// Get an iterator over asset definitions of the `Domain` #[inline] pub fn asset_definitions(&self) -> impl ExactSizeIterator { @@ -203,18 +182,6 @@ impl Domain { #[cfg(feature = "transparent_api")] impl Domain { - /// Add [`Account`] into the [`Domain`] returning previous account stored under the same id - #[inline] - pub fn add_account(&mut self, account: Account) -> Option { - self.accounts.insert(account.id().clone(), account) - } - - /// Remove account from the [`Domain`] and return it - #[inline] - pub fn remove_account(&mut self, account_id: &AccountId) -> Option { - self.accounts.remove(account_id) - } - /// Add asset definition into the [`Domain`] returning previous /// asset definition stored under the same id #[inline] diff --git a/data_model/src/events/data/filters.rs b/data_model/src/events/data/filters.rs index db34033eb82..86bb1a886f3 100644 --- a/data_model/src/events/data/filters.rs +++ b/data_model/src/events/data/filters.rs @@ -740,10 +740,7 @@ mod tests { use iroha_crypto::KeyPair; use super::*; - use crate::{ - account::AccountsMap, - asset::{AssetDefinitionsMap, AssetTotalQuantityMap}, - }; + use crate::asset::{AssetDefinitionsMap, AssetTotalQuantityMap}; #[test] #[cfg(feature = "transparent_api")] @@ -755,7 +752,6 @@ mod tests { let domain = Domain { id: domain_id.clone(), - accounts: AccountsMap::default(), asset_definitions: AssetDefinitionsMap::default(), asset_total_quantities: AssetTotalQuantityMap::default(), logo: None, diff --git a/docs/source/references/schema.json b/docs/source/references/schema.json index 3cb73c6c563..1b9ce3711d9 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -940,10 +940,6 @@ "name": "id", "type": "DomainId" }, - { - "name": "accounts", - "type": "SortedMap" - }, { "name": "asset_definitions", "type": "SortedMap" @@ -3705,12 +3701,6 @@ } ] }, - "SortedMap": { - "Map": { - "key": "AccountId", - "value": "Account" - } - }, "SortedMap": { "Map": { "key": "AssetDefinitionId",