diff --git a/core/quang b/core/quang new file mode 100644 index 00000000000..0c85ddc7f6b --- /dev/null +++ b/core/quang @@ -0,0 +1,10 @@ +1. repositories: + - cbindgen(git@github.com:mozilla/cbindgen.git), + - iroha2(git@github.com:hyperledger/iroha.git) iroha2-stable branch + - iroha2-java(git@github.com:hyperledger/iroha-java.git) + +2. run iroha nodes and connect to it via iroha_client_cli and http app +3. run cbindgen on a small example: + - export a function from Rust + - generate C bindings with cbindgen + - link this C lib from C and Java diff --git a/core/src/kura.rs b/core/src/kura.rs index fd20f270592..93a6ba9e1df 100644 --- a/core/src/kura.rs +++ b/core/src/kura.rs @@ -319,17 +319,17 @@ impl Kura { } /// Put a block in kura's in memory block store. - pub fn store_block(&self, block: impl Into>) { - let block = block.into(); - self.block_data.lock().push((block.hash(), Some(block))); + pub fn store_block(&self, block: VersionedCommittedBlock) { + self.block_data + .lock() + .push((block.hash(), Some(Arc::new(block)))); } /// Replace the block in `Kura`'s in memory block store. - pub fn replace_top_block(&self, block: impl Into>) { - let block = block.into(); + pub fn replace_top_block(&self, block: VersionedCommittedBlock) { let mut data = self.block_data.lock(); data.pop(); - data.push((block.hash(), Some(block))); + data.push((block.hash(), Some(Arc::new(block)))); } } diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index 0f5033eb5b3..8f522d42876 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -479,53 +479,65 @@ pub mod query { }; use super::*; + use crate::smartcontracts::query::Lazy; impl ValidQuery for FindRolesByAccountId { #[metrics(+"find_roles_by_account_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let account_id = wsv .evaluate(&self.id) .wrap_err("Failed to evaluate account id") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%account_id, roles=?wsv.world.roles); - let roles = wsv.map_account(&account_id, |account| { - account.roles.iter().cloned().collect::>() - })?; - Ok(roles) + Ok(Box::new( + wsv.map_account(&account_id, |account| &account.roles)? + .iter() + .cloned(), + )) } } impl ValidQuery for FindPermissionTokensByAccountId { #[metrics(+"find_permission_tokens_by_account_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let account_id = wsv .evaluate(&self.id) .wrap_err("Failed to evaluate account id") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%account_id, accounts=?wsv.world.domains); - let tokens = wsv.map_account(&account_id, |account| { - wsv.account_permission_tokens(account) - })?; - Ok(tokens.into_iter().collect()) + Ok(Box::new( + wsv.account_permission_tokens(&account_id)?.cloned(), + )) } } impl ValidQuery for FindAllAccounts { #[metrics(+"find_all_accounts")] - fn execute(&self, wsv: &WorldStateView) -> Result { - let mut vec = Vec::new(); - for domain in wsv.domains().values() { - for account in domain.accounts.values() { - vec.push(account.clone()) - } - } - Ok(vec) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new( + wsv.domains() + .values() + .flat_map(|domain| domain.accounts.values()) + .cloned(), + )) } } impl ValidQuery for FindAccountById { #[metrics(+"find_account_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get id") @@ -537,39 +549,53 @@ pub mod query { impl ValidQuery for FindAccountsByName { #[metrics(+"find_account_by_name")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let name = wsv .evaluate(&self.name) .wrap_err("Failed to get account name") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%name); - let mut vec = Vec::new(); - for domain in wsv.domains().values() { - for account in domain.accounts.values() { - if account.id().name == name { - vec.push(account.clone()) - } - } - } - Ok(vec) + Ok(Box::new( + wsv.domains() + .values() + .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(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.domain_id) .wrap_err("Failed to get domain id") .map_err(|e| Error::Evaluate(e.to_string()))?; + iroha_logger::trace!(%id); - Ok(wsv.domain(&id)?.accounts.values().cloned().collect()) + Ok(Box::new(wsv.domain(&id)?.accounts.values().cloned())) } } impl ValidQuery for FindAccountKeyValueByIdAndKey { #[metrics(+"find_account_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get account id") @@ -586,29 +612,26 @@ pub mod query { impl ValidQuery for FindAccountsWithAsset { #[metrics(+"find_accounts_with_asset")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let asset_definition_id = wsv .evaluate(&self.asset_definition_id) .wrap_err("Failed to get asset id") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%asset_definition_id); - let domain_id = &asset_definition_id.domain_id; - - wsv.map_domain(domain_id, |domain| { - let found = domain - .accounts - .values() - .filter(|account| { + Ok(Box::new( + wsv.map_domain(&asset_definition_id.domain_id.clone(), move |domain| { + domain.accounts.values().filter(move |account| { let asset_id = AssetId::new(asset_definition_id.clone(), account.id().clone()); account.assets.get(&asset_id).is_some() }) - .cloned() - .collect(); - Ok(found) - }) - .map_err(Into::into) + })? + .cloned(), + )) } } } diff --git a/core/src/smartcontracts/isi/asset.rs b/core/src/smartcontracts/isi/asset.rs index d7ed6ba8aa6..e5ba4408b95 100644 --- a/core/src/smartcontracts/isi/asset.rs +++ b/core/src/smartcontracts/isi/asset.rs @@ -445,42 +445,49 @@ pub mod query { }; use super::*; + use crate::smartcontracts::query::Lazy; impl ValidQuery for FindAllAssets { #[metrics(+"find_all_assets")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv - .domains() - .values() - .map(|domain| { - domain - .accounts - .values() - .map(|account| account.assets.values()) - .flatten() - }) - .flatten() - .cloned() - .collect()) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new( + wsv.domains() + .values() + .flat_map(|domain| { + domain + .accounts + .values() + .flat_map(|account| account.assets.values()) + }) + .cloned(), + )) } } impl ValidQuery for FindAllAssetsDefinitions { #[metrics(+"find_all_asset_definitions")] - fn execute(&self, wsv: &WorldStateView) -> Result { - let mut vec = Vec::new(); - for domain in wsv.domains().values() { - for asset_definition in domain.asset_definitions.values() { - vec.push(asset_definition.clone()) - } - } - Ok(vec) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new( + wsv.domains() + .values() + .flat_map(|domain| domain.asset_definitions.values()) + .cloned(), + )) } } impl ValidQuery for FindAssetById { #[metrics(+"find_asset_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get asset id") @@ -498,7 +505,10 @@ pub mod query { impl ValidQuery for FindAssetDefinitionById { #[metrics(+"find_asset_defintion_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get asset definition id") @@ -512,81 +522,108 @@ pub mod query { impl ValidQuery for FindAssetsByName { #[metrics(+"find_assets_by_name")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let name = wsv .evaluate(&self.name) .wrap_err("Failed to get asset name") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%name); - let mut vec = Vec::new(); - for domain in wsv.domains().values() { - for account in domain.accounts.values() { - for asset in account.assets.values() { - if asset.id().definition_id.name == name { - vec.push(asset.clone()) - } - } - } - } - Ok(vec) + Ok(Box::new( + wsv.domains() + .values() + .flat_map(move |domain| { + let name = name.clone(); + + domain.accounts.values().flat_map(move |account| { + let name = name.clone(); + + account + .assets + .values() + .filter(move |asset| asset.id().definition_id.name == name) + }) + }) + .cloned(), + )) } } impl ValidQuery for FindAssetsByAccountId { #[metrics(+"find_assets_by_account_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.account_id) .wrap_err("Failed to get account id") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%id); - wsv.account_assets(&id).map_err(Into::into) + Ok(Box::new(wsv.account_assets(&id)?)) } } impl ValidQuery for FindAssetsByAssetDefinitionId { #[metrics(+"find_assets_by_asset_definition_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.asset_definition_id) .wrap_err("Failed to get asset definition id") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%id); - let mut vec = Vec::new(); - for domain in wsv.domains().values() { - for account in domain.accounts.values() { - for asset in account.assets.values() { - if asset.id().definition_id == id { - vec.push(asset.clone()) - } - } - } - } - Ok(vec) + Ok(Box::new( + wsv.domains() + .values() + .flat_map(move |domain| { + 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 == id) + }) + }) + .cloned(), + )) } } impl ValidQuery for FindAssetsByDomainId { #[metrics(+"find_assets_by_domain_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.domain_id) .wrap_err("Failed to get domain id") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%id); - let mut vec = Vec::new(); - for account in wsv.domain(&id)?.accounts.values() { - for asset in account.assets.values() { - vec.push(asset.clone()) - } - } - Ok(vec) + Ok(Box::new( + wsv.domain(&id)? + .accounts + .values() + .flat_map(|account| account.assets.values()) + .cloned(), + )) } } impl ValidQuery for FindAssetsByDomainIdAndAssetDefinitionId { #[metrics(+"find_assets_by_domain_id_and_asset_definition_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let domain_id = wsv .evaluate(&self.domain_id) .wrap_err("Failed to get domain id") @@ -601,23 +638,30 @@ pub mod query { .get(&asset_definition_id) .ok_or_else(|| FindError::AssetDefinition(asset_definition_id.clone()))?; iroha_logger::trace!(%domain_id, %asset_definition_id); - let mut assets = Vec::new(); - for account in domain.accounts.values() { - for asset in account.assets.values() { - if asset.id().account_id.domain_id == domain_id - && asset.id().definition_id == asset_definition_id - { - assets.push(asset.clone()) - } - } - } - Ok(assets) + Ok(Box::new( + domain + .accounts + .values() + .flat_map(move |account| { + let domain_id = domain_id.clone(); + let asset_definition_id = asset_definition_id.clone(); + + account.assets.values().filter(move |asset| { + asset.id().account_id.domain_id == domain_id + && asset.id().definition_id == asset_definition_id + }) + }) + .cloned(), + )) } } impl ValidQuery for FindAssetQuantityById { #[metrics(+"find_asset_quantity_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get asset id") @@ -641,7 +685,10 @@ pub mod query { impl ValidQuery for FindTotalAssetQuantityByAssetDefinitionId { #[metrics(+"find_total_asset_quantity_by_asset_definition_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get asset definition id") @@ -654,7 +701,10 @@ pub mod query { impl ValidQuery for FindAssetKeyValueByIdAndKey { #[metrics(+"find_asset_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get asset id") @@ -685,7 +735,10 @@ pub mod query { impl ValidQuery for IsAssetDefinitionOwner { #[metrics("is_asset_definition_owner")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let asset_definition_id = wsv .evaluate(&self.asset_definition_id) .wrap_err("Failed to get asset definition id") diff --git a/core/src/smartcontracts/isi/block.rs b/core/src/smartcontracts/isi/block.rs index 51bd8533c44..44b82f0193c 100644 --- a/core/src/smartcontracts/isi/block.rs +++ b/core/src/smartcontracts/isi/block.rs @@ -2,7 +2,6 @@ use eyre::{Result, WrapErr}; use iroha_crypto::HashOf; use iroha_data_model::{ - block::VersionedCommittedBlock, evaluate::ExpressionEvaluator, query::{ block::FindBlockHeaderByHash, @@ -12,34 +11,40 @@ use iroha_data_model::{ use iroha_telemetry::metrics; use super::*; +use crate::smartcontracts::query::Lazy; impl ValidQuery for FindAllBlocks { #[metrics(+"find_all_blocks")] - fn execute(&self, wsv: &WorldStateView) -> Result { - let blocks = wsv - .all_blocks() - .map(|block| VersionedCommittedBlock::clone(&block)) - .rev() - .collect(); - Ok(blocks) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, QueryExecutionFail> { + Ok(Box::new( + wsv.all_blocks().rev().map(|block| Clone::clone(&*block)), + )) } } impl ValidQuery for FindAllBlockHeaders { #[metrics(+"find_all_block_headers")] - fn execute(&self, wsv: &WorldStateView) -> Result { - let block_headers = wsv - .all_blocks() - .rev() - .map(|block| block.as_v1().header.clone()) - .collect(); - Ok(block_headers) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, QueryExecutionFail> { + Ok(Box::new( + wsv.all_blocks() + .rev() + .map(|block| block.as_v1().header.clone()), + )) } } impl ValidQuery for FindBlockHeaderByHash { #[metrics(+"find_block_header")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, QueryExecutionFail> { let hash = wsv .evaluate(&self.hash) .wrap_err("Failed to evaluate hash") diff --git a/core/src/smartcontracts/isi/domain.rs b/core/src/smartcontracts/isi/domain.rs index 177bca4419c..4483e83c1dc 100644 --- a/core/src/smartcontracts/isi/domain.rs +++ b/core/src/smartcontracts/isi/domain.rs @@ -291,17 +291,24 @@ pub mod query { use iroha_data_model::query::error::QueryExecutionFail as Error; use super::*; + use crate::smartcontracts::query::Lazy; impl ValidQuery for FindAllDomains { #[metrics(+"find_all_domains")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv.domains().values().cloned().collect()) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new(wsv.domains().values().cloned())) } } impl ValidQuery for FindDomainById { #[metrics(+"find_domain_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get domain id") @@ -313,7 +320,10 @@ pub mod query { impl ValidQuery for FindDomainKeyValueByIdAndKey { #[metrics(+"find_domain_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get domain id") @@ -323,16 +333,17 @@ pub mod query { .wrap_err("Failed to get key") .map_err(|e| Error::Evaluate(e.to_string()))?; iroha_logger::trace!(%id, %key); - wsv.map_domain(&id, |domain| { - Ok(domain.metadata.get(&key).map(Clone::clone)) - })? - .ok_or_else(|| FindError::MetadataKey(key).into()) + wsv.map_domain(&id, |domain| domain.metadata.get(&key).map(Clone::clone))? + .ok_or_else(|| FindError::MetadataKey(key).into()) } } impl ValidQuery for FindAssetDefinitionKeyValueByIdAndKey { #[metrics(+"find_asset_definition_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .wrap_err("Failed to get asset definition id") diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index cf592fcb904..215bf2835a4 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -11,6 +11,37 @@ use parity_scale_codec::{Decode, Encode}; use crate::{prelude::ValidQuery, WorldStateView}; +/// Represents lazy evaluated query output +pub trait Lazy { + /// Type of the lazy evaluated query output + type Lazy<'a>; +} + +impl Lazy for Vec { + type Lazy<'a> = Box + 'a>; +} + +macro_rules! impl_lazy { + ( $($ident:ty),+ $(,)? ) => { $( + impl Lazy for $ident { + type Lazy<'a> = Self; + } )+ + }; +} +impl_lazy! { + bool, + Value, + NumericValue, + iroha_data_model::role::Role, + iroha_data_model::asset::Asset, + iroha_data_model::asset::AssetDefinition, + iroha_data_model::account::Account, + iroha_data_model::domain::Domain, + iroha_data_model::block::BlockHeader, + iroha_data_model::transaction::TransactionQueryResult, + iroha_data_model::trigger::Trigger, +} + /// Query Request statefully validated on the Iroha node side. #[derive(Debug, Decode, Encode)] pub struct ValidQueryRequest(VersionedSignedQuery); @@ -54,59 +85,66 @@ impl ValidQueryRequest { } impl ValidQuery for QueryBox { - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { iroha_logger::debug!(query=%self, "Executing"); macro_rules! match_all { - ( $( $query:ident ),+ $(,)? ) => { + ( non_iter: {$( $non_iter_query:ident ),+ $(,)?} $( $query:ident, )+ ) => { match self { $( - QueryBox::$query(query) => query.execute(wsv).map(Into::into), )+ + QueryBox::$non_iter_query(query) => query.execute(wsv).map(Into::into), )+ $( + QueryBox::$query(query) => query.execute(wsv).map(|i| i.collect::>()).map(Into::into), )+ } }; } match_all! { + non_iter: { + FindAccountById, + FindAssetById, + FindAssetDefinitionById, + FindAssetQuantityById, + FindTotalAssetQuantityByAssetDefinitionId, + IsAssetDefinitionOwner, + FindDomainById, + FindDomainKeyValueByIdAndKey, + FindAssetKeyValueByIdAndKey, + FindAccountKeyValueByIdAndKey, + FindBlockHeaderByHash, + FindTransactionByHash, + DoesAccountHavePermissionToken, + FindAssetDefinitionKeyValueByIdAndKey, + FindTriggerById, + FindTriggerKeyValueByIdAndKey, + FindRoleByRoleId, + } + FindAllAccounts, - FindAccountById, FindAccountsByName, FindAccountsByDomainId, FindAccountsWithAsset, FindAllAssets, FindAllAssetsDefinitions, - FindAssetById, - FindAssetDefinitionById, FindAssetsByName, FindAssetsByAccountId, FindAssetsByAssetDefinitionId, FindAssetsByDomainId, FindAssetsByDomainIdAndAssetDefinitionId, - FindAssetQuantityById, - FindTotalAssetQuantityByAssetDefinitionId, - IsAssetDefinitionOwner, FindAllDomains, - FindDomainById, - FindDomainKeyValueByIdAndKey, FindAllPeers, - FindAssetKeyValueByIdAndKey, - FindAccountKeyValueByIdAndKey, FindAllBlocks, FindAllBlockHeaders, - FindBlockHeaderByHash, FindAllTransactions, FindTransactionsByAccountId, - FindTransactionByHash, FindPermissionTokensByAccountId, FindAllPermissionTokenDefinitions, - DoesAccountHavePermissionToken, - FindAssetDefinitionKeyValueByIdAndKey, FindAllActiveTriggerIds, - FindTriggerById, - FindTriggerKeyValueByIdAndKey, FindTriggersByDomainId, FindAllRoles, FindAllRoleIds, FindRolesByAccountId, - FindRoleByRoleId, FindAllParameters, } } @@ -300,8 +338,7 @@ mod tests { let num_blocks = 100; let wsv = wsv_with_test_blocks_and_transactions(num_blocks, 1, 1)?; - - let blocks = FindAllBlocks.execute(&wsv)?; + let blocks = FindAllBlocks.execute(&wsv)?.collect::>(); assert_eq!(blocks.len() as u64, num_blocks); assert!(blocks.windows(2).all(|wnd| wnd[0] >= wnd[1])); @@ -314,8 +351,7 @@ mod tests { let num_blocks = 100; let wsv = wsv_with_test_blocks_and_transactions(num_blocks, 1, 1)?; - - let block_headers = FindAllBlockHeaders.execute(&wsv)?; + let block_headers = FindAllBlockHeaders.execute(&wsv)?.collect::>(); assert_eq!(block_headers.len() as u64, num_blocks); assert!(block_headers.windows(2).all(|wnd| wnd[0] >= wnd[1])); @@ -346,8 +382,7 @@ mod tests { let num_blocks = 100; let wsv = wsv_with_test_blocks_and_transactions(num_blocks, 1, 1)?; - - let txs = FindAllTransactions.execute(&wsv)?; + let txs = FindAllTransactions.execute(&wsv)?.collect::>(); assert_eq!(txs.len() as u64, num_blocks * 2); assert_eq!( diff --git a/core/src/smartcontracts/isi/triggers/mod.rs b/core/src/smartcontracts/isi/triggers/mod.rs index 384a4ed076f..070ab518a4c 100644 --- a/core/src/smartcontracts/isi/triggers/mod.rs +++ b/core/src/smartcontracts/isi/triggers/mod.rs @@ -198,18 +198,24 @@ pub mod query { use iroha_data_model::query::error::QueryExecutionFail as Error; use super::*; - use crate::prelude::*; + use crate::{prelude::*, smartcontracts::query::Lazy}; impl ValidQuery for FindAllActiveTriggerIds { #[metrics(+"find_all_active_triggers")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv.triggers().ids()) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new(wsv.triggers().ids().cloned())) } } impl ValidQuery for FindTriggerById { #[metrics(+"find_trigger_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .map_err(|e| Error::Evaluate(format!("Failed to evaluate trigger id. {e}")))?; @@ -237,7 +243,10 @@ pub mod query { impl ValidQuery for FindTriggerKeyValueByIdAndKey { #[metrics(+"find_trigger_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let id = wsv .evaluate(&self.id) .map_err(|e| Error::Evaluate(format!("Failed to evaluate trigger id. {e}")))?; @@ -250,7 +259,7 @@ pub mod query { action .metadata() .get(&key) - .map(Clone::clone) + .cloned() .ok_or_else(|| FindError::MetadataKey(key.clone()).into()) }) .ok_or_else(|| Error::Find(Box::new(FindError::Trigger(id))))? @@ -259,14 +268,17 @@ pub mod query { impl ValidQuery for FindTriggersByDomainId { #[metrics(+"find_triggers_by_domain_id")] - fn execute(&self, wsv: &WorldStateView) -> eyre::Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> eyre::Result<::Lazy<'wsv>, Error> { let domain_id = wsv .evaluate(&self.domain_id) .map_err(|e| Error::Evaluate(format!("Failed to evaluate domain id. {e}")))?; - let triggers = wsv - .triggers() - .inspect_by_domain_id(&domain_id, |trigger_id, action| { + Ok(Box::new(wsv.triggers().inspect_by_domain_id( + &domain_id, + |trigger_id, action| { let Action { executable: loaded_executable, repeats, @@ -274,14 +286,14 @@ pub mod query { filter, metadata, } = action.clone_and_box(); + Trigger::new( trigger_id.clone(), Action::new(loaded_executable, repeats, authority, filter) .with_metadata(metadata), ) - }); - - Ok(triggers) + }, + ))) } } } diff --git a/core/src/smartcontracts/isi/triggers/set.rs b/core/src/smartcontracts/isi/triggers/set.rs index f30115715a1..ba0a26c50ea 100644 --- a/core/src/smartcontracts/isi/triggers/set.rs +++ b/core/src/smartcontracts/isi/triggers/set.rs @@ -202,14 +202,18 @@ impl Set { /// Get all contained trigger ids without a particular order #[inline] - pub fn ids(&self) -> Vec { - self.ids.keys().cloned().collect() + pub fn ids(&self) -> impl ExactSizeIterator { + self.ids.keys() } /// Apply `f` to triggers that belong to the given [`DomainId`] /// /// Return an empty list if [`Set`] doesn't contain any triggers belonging to [`DomainId`]. - pub fn inspect_by_domain_id(&self, domain_id: &DomainId, f: F) -> Vec + pub fn inspect_by_domain_id( + &self, + domain_id: &DomainId, + f: F, + ) -> impl ExactSizeIterator where F: Fn(&TriggerId, &dyn ActionTrait) -> R, { @@ -251,7 +255,8 @@ impl Set { Some(result) }) - .collect() + .collect::>() + .into_iter() } /// Apply `f` to the trigger identified by `id`. diff --git a/core/src/smartcontracts/isi/tx.rs b/core/src/smartcontracts/isi/tx.rs index 84196bee92c..760afcfc113 100644 --- a/core/src/smartcontracts/isi/tx.rs +++ b/core/src/smartcontracts/isi/tx.rs @@ -1,50 +1,156 @@ //! Query module provides [`Query`] Transaction related implementations. +use std::sync::Arc; + use eyre::{Result, WrapErr}; use iroha_crypto::HashOf; use iroha_data_model::{ + block::VersionedCommittedBlock, evaluate::ExpressionEvaluator, prelude::*, query::error::{FindError, QueryExecutionFail}, + transaction::{TransactionQueryResult, TransactionValue}, }; use iroha_telemetry::metrics; -use super::*; +use super::{query::Lazy, *}; + +pub(crate) struct BlockTransactionIter(Arc, usize); +pub(crate) struct BlockTransactionRef(Arc, usize); + +impl BlockTransactionIter { + fn new(block: Arc) -> Self { + Self(block, 0) + } +} + +impl Iterator for BlockTransactionIter { + type Item = BlockTransactionRef; + + fn next(&mut self) -> Option { + let block = self.0.as_v1(); + + if self.1 < block.rejected_transactions.len() { + return Some(BlockTransactionRef(Arc::clone(&self.0), self.1)); + } + + None + } +} + +impl BlockTransactionRef { + fn block_hash(&self) -> HashOf { + self.0.hash() + } + + fn authority(&self) -> &AccountId { + let block = self.0.as_v1(); + + if self.1 < block.transactions.len() { + return &block.transactions[self.1].payload().authority; + } + + &block.rejected_transactions[self.1] + .transaction + .payload() + .authority + } + fn value(&self) -> TransactionValue { + let block = self.0.as_v1(); + + if self.1 < block.transactions.len() { + return TransactionValue::Transaction(block.transactions[self.1].clone()); + } + + TransactionValue::RejectedTransaction(block.rejected_transactions[self.1].clone()) + } +} impl ValidQuery for FindAllTransactions { #[metrics(+"find_all_transactions")] - fn execute(&self, wsv: &WorldStateView) -> Result { - let mut txs = wsv.transaction_values(); - txs.reverse(); - Ok(txs) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, QueryExecutionFail> { + Ok(Box::new( + wsv.all_blocks() + .flat_map(BlockTransactionIter::new) + .map(|tx| TransactionQueryResult { + block_hash: tx.block_hash(), + transaction: tx.value(), + }), + )) } } impl ValidQuery for FindTransactionsByAccountId { #[metrics(+"find_transactions_by_account_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { - let id = wsv + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, QueryExecutionFail> { + let account_id = wsv .evaluate(&self.account_id) .wrap_err("Failed to get account id") .map_err(|e| QueryExecutionFail::Evaluate(e.to_string()))?; - iroha_logger::trace!(%id); - Ok(wsv.transactions_values_by_account_id(&id)) + + Ok(Box::new( + wsv.all_blocks() + .flat_map(BlockTransactionIter::new) + .filter(move |tx| *tx.authority() == account_id) + .map(|tx| TransactionQueryResult { + block_hash: tx.block_hash(), + transaction: tx.value(), + }), + )) } } impl ValidQuery for FindTransactionByHash { #[metrics(+"find_transaction_by_hash")] - fn execute(&self, wsv: &WorldStateView) -> Result { - let hash = wsv - .evaluate(&self.hash) - .wrap_err("Failed to get hash") - .map_err(|e| QueryExecutionFail::Evaluate(e.to_string()))?; - iroha_logger::trace!(%hash); - let hash = HashOf::from_untyped_unchecked(hash); - if !wsv.has_transaction(hash) { - return Err(FindError::Transaction(hash).into()); + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, QueryExecutionFail> { + let tx_hash = HashOf::from_untyped_unchecked( + wsv.evaluate(&self.hash) + .wrap_err("Failed to get hash") + .map_err(|e| QueryExecutionFail::Evaluate(e.to_string()))?, + ); + iroha_logger::trace!(%tx_hash); + if !wsv.has_transaction(tx_hash) { + return Err(FindError::Transaction(tx_hash).into()); }; - wsv.transaction_value_by_hash(&hash) - .ok_or_else(|| FindError::Transaction(hash).into()) + let block = wsv + .block_with_tx(&tx_hash) + .ok_or_else(|| FindError::Transaction(tx_hash))?; + + let block_hash = block.hash(); + let block = block.as_v1(); + + block + .transactions + .iter() + .find(|transaction| transaction.hash() == tx_hash) + .cloned() + .map(TransactionValue::Transaction) + .or_else(|| { + block + .rejected_transactions + .iter() + .find( + |RejectedTransaction { + transaction, + error: _, + }| transaction.hash() == tx_hash, + ) + .cloned() + .map(TransactionValue::RejectedTransaction) + }) + .map(|transaction| TransactionQueryResult { + block_hash, + transaction, + }) + .ok_or_else(|| FindError::Transaction(tx_hash).into()) } } diff --git a/core/src/smartcontracts/isi/world.rs b/core/src/smartcontracts/isi/world.rs index ebcc12b8fbd..4cae609c2ac 100644 --- a/core/src/smartcontracts/isi/world.rs +++ b/core/src/smartcontracts/isi/world.rs @@ -22,6 +22,7 @@ pub mod isi { use eyre::Result; use iroha_data_model::{ isi::error::{InvalidParameterError, RepetitionError}, + permission::PermissionTokenId, prelude::*, query::error::FindError, }; @@ -267,21 +268,24 @@ pub mod isi { /// Remove all tokens with specified definition id from all accounts in all domains fn remove_token_from_accounts( wsv: &mut WorldStateView, - target_definition_id: &::Id, + target_definition_id: &PermissionTokenId, ) -> Result<(), Error> { let mut accounts_with_token = std::collections::HashMap::new(); - for domain in wsv.domains().values() { - let account_ids = domain.accounts.values().map(|account| { - ( - account.id().clone(), - wsv.account_inherent_permission_tokens(account) - .filter(|token| token.definition_id == *target_definition_id) - .collect::>(), - ) - }); - - accounts_with_token.extend(account_ids); + let account_ids = wsv + .domains() + .values() + .flat_map(|domain| domain.accounts.values()) + .map(|account| &account.id); + + for account_id in account_ids { + accounts_with_token.insert( + account_id.clone(), + wsv.account_inherent_permission_tokens(account_id)? + .filter(|token| token.definition_id == *target_definition_id) + .cloned() + .collect::>(), + ); } let mut events = Vec::new(); @@ -413,31 +417,40 @@ pub mod query { }; use super::*; + use crate::smartcontracts::query::Lazy; impl ValidQuery for FindAllRoles { #[metrics(+"find_all_roles")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv.world.roles.values().cloned().collect()) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new(wsv.world.roles.values().cloned())) } } impl ValidQuery for FindAllRoleIds { #[metrics(+"find_all_role_ids")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new(wsv .world .roles .values() // To me, this should probably be a method, not a field. .map(Role::id) - .cloned() - .collect()) + .cloned())) } } impl ValidQuery for FindRoleByRoleId { #[metrics(+"find_role_by_role_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let role_id = wsv .evaluate(&self.id) .map_err(|e| Error::Evaluate(e.to_string()))?; @@ -452,40 +465,49 @@ pub mod query { impl ValidQuery for FindAllPeers { #[metrics("find_all_peers")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv.peers()) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new(wsv.peers().cloned().map(Peer::new))) } } impl ValidQuery for FindAllPermissionTokenDefinitions { #[metrics("find_all_token_ids")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv - .permission_token_definitions() - .values() - .cloned() - .collect()) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new( + wsv.permission_token_definitions().values().cloned(), + )) } } impl ValidQuery for FindAllParameters { #[metrics("find_all_parameters")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv.parameters()) + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { + Ok(Box::new(wsv.parameters().cloned())) } } impl ValidQuery for DoesAccountHavePermissionToken { #[metrics("does_account_have_permission")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, Error> { let authority = wsv .evaluate(&self.account_id) .map_err(|e| Error::Evaluate(e.to_string()))?; - wsv.map_account(&authority, |account| { - wsv.account_permission_tokens(account) - .contains(&self.permission_token) - }) + Ok(wsv + .account_permission_tokens(&authority)? + .any(|permission_token| *permission_token == self.permission_token)) } } } diff --git a/core/src/smartcontracts/mod.rs b/core/src/smartcontracts/mod.rs index 6580b1c766a..81992c6afdb 100644 --- a/core/src/smartcontracts/mod.rs +++ b/core/src/smartcontracts/mod.rs @@ -15,6 +15,7 @@ use iroha_data_model::{ }; pub use isi::*; +use self::query::Lazy; use crate::wsv::WorldStateView; /// Trait implementations should provide actions to apply changes on [`WorldStateView`]. @@ -27,7 +28,10 @@ pub trait Execute { } /// This trait should be implemented for all Iroha Queries. -pub trait ValidQuery: Query { +pub trait ValidQuery: Query +where + Self::Output: Lazy, +{ /// Execute query on the [`WorldStateView`]. /// Should not mutate [`WorldStateView`]! /// @@ -35,7 +39,10 @@ pub trait ValidQuery: Query { /// /// # Errors /// Concrete to each implementer - fn execute(&self, wsv: &WorldStateView) -> Result; + fn execute<'wsv>( + &self, + wsv: &'wsv WorldStateView, + ) -> Result<::Lazy<'wsv>, QueryExecutionFail>; } impl ExpressionEvaluator for WorldStateView { diff --git a/core/src/sumeragi/main_loop.rs b/core/src/sumeragi/main_loop.rs index 34cbf049b89..7396cda75f5 100644 --- a/core/src/sumeragi/main_loop.rs +++ b/core/src/sumeragi/main_loop.rs @@ -2,7 +2,7 @@ #![allow(clippy::cognitive_complexity)] use std::sync::mpsc; -use iroha_data_model::{block::*, transaction::error::TransactionRejectionReason}; +use iroha_data_model::{block::*, peer::PeerId, transaction::error::TransactionRejectionReason}; use iroha_p2p::UpdateTopology; use tracing::{span, Level}; @@ -291,17 +291,11 @@ impl Sumeragi { self.update_state::(block); } - fn update_topology(&mut self, committed_block: &VersionedCommittedBlock) { - let mut topology = Topology::new( - committed_block - .as_v1() - .header() - .committed_with_topology - .clone(), - ); + fn update_topology(&mut self, block_signees: &[PublicKey], peers: Vec) { + let mut topology = Topology::new(peers); topology.update_topology( - committed_block, + block_signees, self.wsv.peers_ids().iter().cloned().collect(), ); @@ -313,7 +307,7 @@ impl Sumeragi { &mut self, block: impl Into, ) { - let committed_block = Arc::new(block.into()); + let committed_block = block.into(); info!( addr=%self.peer_id.address, @@ -326,7 +320,7 @@ impl Sumeragi { Strategy::before_update_hook(self); self.wsv - .apply(committed_block.as_ref()) + .apply(&committed_block) .expect("Failed to apply block on WSV. Bailing."); let events_buffer = core::mem::take(&mut self.wsv.events_buffer); @@ -335,10 +329,23 @@ impl Sumeragi { // Parameters are updated before updating public copy of sumeragi self.update_params(); + let events: Vec<_> = (&committed_block).into(); + let topology = committed_block + .as_v1() + .header() + .committed_with_topology + .clone(); + let block_signees = committed_block + .signatures() + .map(|s| s.public_key()) + .cloned() + .collect::>(); + // https://github.com/hyperledger/iroha/issues/3396 // Kura should store the block only upon successful application to the internal WSV to avoid storing a corrupted block. // Public-facing WSV update should happen after that and be followed by `BlockCommited` event to prevent client access to uncommitted data. - Strategy::kura_store_block(&self.kura, Arc::clone(&committed_block)); + // TODO: Redundant clone + Strategy::kura_store_block(&self.kura, committed_block); // Update WSV copy that is public facing self.public_wsv_sender @@ -346,9 +353,9 @@ impl Sumeragi { // This sends "Block committed" event, so it should be done // AFTER public facing WSV update - self.send_events(committed_block.as_ref()); + self.send_events(events); - self.update_topology(committed_block.as_ref()); + self.update_topology(&block_signees, topology); self.cache_transaction() } @@ -1049,7 +1056,7 @@ trait ApplyBlockStrategy { fn before_update_hook(sumeragi: &mut Sumeragi); /// Operation to invoke in kura to store block. - fn kura_store_block(kura: &Kura, block: Arc); + fn kura_store_block(kura: &Kura, block: VersionedCommittedBlock); } /// Commit new block strategy. Used during normal consensus rounds. @@ -1065,7 +1072,7 @@ impl ApplyBlockStrategy for NewBlockStrategy { } #[inline] - fn kura_store_block(kura: &Kura, block: Arc) { + fn kura_store_block(kura: &Kura, block: VersionedCommittedBlock) { kura.store_block(block) } } @@ -1083,7 +1090,7 @@ impl ApplyBlockStrategy for ReplaceTopBlockStrategy { } #[inline] - fn kura_store_block(kura: &Kura, block: Arc) { + fn kura_store_block(kura: &Kura, block: VersionedCommittedBlock) { kura.replace_top_block(block) } } diff --git a/core/src/sumeragi/network_topology.rs b/core/src/sumeragi/network_topology.rs index d7a45eda694..9ed33183879 100644 --- a/core/src/sumeragi/network_topology.rs +++ b/core/src/sumeragi/network_topology.rs @@ -184,14 +184,8 @@ impl Topology { } /// Perform sequence of actions after block committed. - pub fn update_topology(&mut self, block: &VersionedCommittedBlock, new_peers: HashSet) { - self.lift_up_peers( - &block - .signatures() - .map(|s| s.public_key()) - .cloned() - .collect::>(), - ); + pub fn update_topology(&mut self, block_signees: &[PublicKey], new_peers: HashSet) { + self.lift_up_peers(block_signees); self.rotate_set_a(); self.update_peer_list(new_peers); } @@ -203,8 +197,13 @@ impl Topology { new_peers: HashSet, ) -> Self { let mut topology = Topology::new(block.as_v1().header().committed_with_topology.clone()); + let block_signees = block + .signatures() + .map(|s| s.public_key()) + .cloned() + .collect::>(); - topology.update_topology(block, new_peers); + topology.update_topology(&block_signees, new_peers); // Rotate all once for every view_change topology.rotate_all_n(view_change_index); diff --git a/core/src/wsv.rs b/core/src/wsv.rs index 26f87b2be92..89413536530 100644 --- a/core/src/wsv.rs +++ b/core/src/wsv.rs @@ -10,7 +10,6 @@ use std::{ borrow::Borrow, collections::{BTreeSet, HashMap}, - convert::Infallible, fmt::Debug, sync::Arc, time::Duration, @@ -28,7 +27,6 @@ use iroha_data_model::{ parameter::Parameter, prelude::*, query::error::{FindError, QueryExecutionFail}, - transaction::TransactionQueryResult, trigger::action::ActionTrait, }; use iroha_logger::prelude::*; @@ -157,39 +155,58 @@ impl WorldStateView { /// /// # Errors /// Fails if there is no domain or account - pub fn account_assets(&self, id: &AccountId) -> Result, QueryExecutionFail> { - self.map_account(id, |account| account.assets.values().cloned().collect()) + pub fn account_assets( + &self, + id: &AccountId, + ) -> Result + '_, QueryExecutionFail> { + self.map_account(id, |account| account.assets.values().cloned()) } /// Return a set of all permission tokens granted to this account. - pub fn account_permission_tokens(&self, account: &Account) -> BTreeSet { - let mut tokens: BTreeSet = - self.account_inherent_permission_tokens(account).collect(); + /// + /// # Errors + /// + /// - if there is no account + pub fn account_permission_tokens( + &self, + account_id: &AccountId, + ) -> Result, FindError> { + let account = self.account(account_id)?; + + let mut tokens = self + .account_inherent_permission_tokens(account_id)? + .collect::>(); + for role_id in &account.roles { if let Some(role) = self.world.roles.get(role_id) { - tokens.append(&mut role.permissions.clone()); + tokens.extend(role.permissions.iter()); } } - tokens + + Ok(tokens.into_iter()) } /// Return a set of permission tokens granted to this account not as part of any role. + /// + /// # Errors + /// + /// - if there is no account pub fn account_inherent_permission_tokens( &self, - account: &Account, - ) -> impl ExactSizeIterator { + account_id: &AccountId, + ) -> Result, FindError> { self.world .account_permission_tokens - .get(&account.id) - .map_or_else(Default::default, Clone::clone) - .into_iter() + .get(account_id) + .ok_or_else(|| FindError::Account(account_id.clone())) + .map(std::collections::BTreeSet::iter) } /// Return `true` if [`Account`] contains a permission token not associated with any role. #[inline] pub fn account_contains_inherent_permission( &self, - account: &::Id, + account: &AccountId, token: &PermissionToken, ) -> bool { self.world @@ -201,11 +218,7 @@ impl WorldStateView { /// Add [`permission`](PermissionToken) to the [`Account`] if the account does not have this permission yet. /// /// Return a Boolean value indicating whether or not the [`Account`] already had this permission. - pub fn add_account_permission( - &mut self, - account: &::Id, - token: PermissionToken, - ) -> bool { + pub fn add_account_permission(&mut self, account: &AccountId, token: PermissionToken) -> bool { // `match` here instead of `map_or_else` to avoid cloning token into each closure match self.world.account_permission_tokens.get_mut(account) { None => { @@ -228,7 +241,7 @@ impl WorldStateView { /// Return a Boolean value indicating whether the [`Account`] had this permission. pub fn remove_account_permission( &mut self, - account: &::Id, + account: &AccountId, token: &PermissionToken, ) -> bool { self.world @@ -453,7 +466,7 @@ impl WorldStateView { /// - No such [`Asset`] /// - The [`Account`] with which the [`Asset`] is associated doesn't exist. /// - The [`Domain`] with which the [`Account`] is associated doesn't exist. - pub fn asset(&self, id: &::Id) -> Result { + pub fn asset(&self, id: &AssetId) -> Result { self.map_account( &id.account_id, |account| -> Result { @@ -473,7 +486,7 @@ impl WorldStateView { #[allow(clippy::missing_panics_doc)] pub fn asset_or_insert( &mut self, - id: &::Id, + id: &AssetId, default_asset_value: impl Into, ) -> Result { if let Ok(asset) = self.asset(id) { @@ -550,7 +563,7 @@ impl WorldStateView { /// /// # Errors /// Fails if there is no domain - pub fn domain(&self, id: &::Id) -> Result<&Domain, FindError> { + pub fn domain<'wsv>(&'wsv self, id: &DomainId) -> Result<&'wsv Domain, FindError> { let domain = self .world .domains @@ -563,10 +576,7 @@ impl WorldStateView { /// /// # Errors /// Fails if there is no domain - pub fn domain_mut( - &mut self, - id: &::Id, - ) -> Result<&mut Domain, FindError> { + pub fn domain_mut(&mut self, id: &DomainId) -> Result<&mut Domain, FindError> { let domain = self .world .domains @@ -586,16 +596,13 @@ impl WorldStateView { /// # Errors /// Fails if there is no domain #[allow(clippy::panic_in_result_fn)] - pub fn map_domain( - &self, - id: &::Id, - f: impl FnOnce(&Domain) -> Result, + pub fn map_domain<'wsv, T>( + &'wsv self, + id: &DomainId, + f: impl FnOnce(&'wsv Domain) -> T, ) -> Result { let domain = self.domain(id)?; - let value = f(domain).map_or_else( - |_infallible| unreachable!("Returning `Infallible` should not be possible"), - |value| value, - ); + let value = f(domain); Ok(value) } @@ -679,10 +686,10 @@ impl WorldStateView { /// /// # Errors /// Fails if there is no domain or account - pub fn map_account( - &self, + pub fn map_account<'wsv, T>( + &'wsv self, id: &AccountId, - f: impl FnOnce(&Account) -> T, + f: impl FnOnce(&'wsv Account) -> T, ) -> Result { let domain = self.domain(&id.domain_id)?; let account = domain @@ -692,6 +699,15 @@ impl WorldStateView { Ok(f(account)) } + fn account(&self, id: &AccountId) -> Result<&Account, FindError> { + self.domain(&id.domain_id).and_then(|domain| { + domain + .accounts + .get(id) + .ok_or_else(|| FindError::Account(id.clone())) + }) + } + /// Get mutable reference to [`Account`] /// /// # Errors @@ -735,24 +751,13 @@ impl WorldStateView { } /// Get all `PeerId`s without an ability to modify them. - pub fn peers(&self) -> Vec { - let mut vec = self - .world - .trusted_peers_ids - .iter() - .map(|peer| Peer::new((*peer).clone())) - .collect::>(); - vec.sort(); - vec + pub fn peers(&self) -> impl ExactSizeIterator { + self.world.trusted_peers_ids.iter() } /// Get all `Parameter`s registered in the world. - pub fn parameters(&self) -> Vec { - self.world - .parameters - .iter() - .cloned() - .collect::>() + pub fn parameters(&self) -> impl ExactSizeIterator { + self.world.parameters.iter() } /// Query parameter and convert it to a proper type @@ -776,7 +781,7 @@ impl WorldStateView { /// - Asset definition entry not found pub fn asset_definition( &self, - asset_id: &::Id, + asset_id: &AssetDefinitionId, ) -> Result { self.domain(&asset_id.domain_id)? .asset_definitions @@ -791,7 +796,7 @@ impl WorldStateView { /// - Asset definition not found pub fn asset_total_amount( &self, - definition_id: &::Id, + definition_id: &AssetDefinitionId, ) -> Result { self.domain(&definition_id.domain_id)? .asset_total_quantities @@ -807,7 +812,7 @@ impl WorldStateView { /// - Overflow pub fn increase_asset_total_amount( &mut self, - definition_id: &::Id, + definition_id: &AssetDefinitionId, increment: I, ) -> Result<(), Error> where @@ -846,7 +851,7 @@ impl WorldStateView { /// - Not enough quantity pub fn decrease_asset_total_amount( &mut self, - definition_id: &::Id, + definition_id: &AssetDefinitionId, decrement: I, ) -> Result<(), Error> where @@ -878,116 +883,13 @@ impl WorldStateView { Ok(()) } - /// Get all transactions - pub fn transaction_values(&self) -> Vec { - let mut txs = self - .all_blocks() - .flat_map(|block| { - let block = block.as_v1(); - block - .rejected_transactions - .iter() - .cloned() - .map(|versioned_rejected_tx| TransactionQueryResult { - transaction: TransactionValue::RejectedTransaction(versioned_rejected_tx), - block_hash: block.hash(), - }) - .chain( - block - .transactions - .iter() - .cloned() - .map(VersionedSignedTransaction::from) - .map(|versioned_tx| TransactionQueryResult { - transaction: TransactionValue::Transaction(versioned_tx), - block_hash: block.hash(), - }), - ) - .collect::>() - }) - .collect::>(); - txs.sort(); - txs - } - /// Find a [`VersionedSignedTransaction`] by hash. - pub fn transaction_value_by_hash( + pub fn block_with_tx( &self, hash: &HashOf, - ) -> Option { + ) -> Option> { let height = *self.transactions.get(hash)?; - let block = self.kura.get_block_by_height(height)?; - let block_hash = block.as_v1().hash(); - block - .as_v1() - .rejected_transactions - .iter() - .find( - |RejectedTransaction { - transaction, - error: _, - }| transaction.hash() == *hash, - ) - .cloned() - .map(TransactionValue::RejectedTransaction) - .or_else(|| { - block - .as_v1() - .transactions - .iter() - .find(|e| e.hash() == *hash) - .cloned() - .map(VersionedSignedTransaction::from) - .map(TransactionValue::Transaction) - }) - .map(|tx| TransactionQueryResult { - transaction: tx, - block_hash, - }) - } - - /// Get committed and rejected transaction of the account. - pub fn transactions_values_by_account_id( - &self, - account_id: &AccountId, - ) -> Vec { - let mut transactions = self - .all_blocks() - .flat_map(|block_entry| { - let block = block_entry.as_v1(); - let block_hash = block.hash(); - - block - .rejected_transactions - .iter() - .filter( - |RejectedTransaction { - transaction, - error: _, - }| { - transaction.payload().authority == *account_id - }, - ) - .cloned() - .map(TransactionValue::RejectedTransaction) - .chain( - block - .transactions - .iter() - .filter(|tx| &tx.payload().authority == account_id) - .cloned() - .map(VersionedSignedTransaction::from) - .map(TransactionValue::Transaction), - ) - .map(|tx| TransactionQueryResult { - transaction: tx, - block_hash, - }) - .collect::>() - }) - .collect::>(); - transactions.sort(); - transactions + self.kura.get_block_by_height(height) } /// Get an immutable view of the `World`. diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 97e9b12f4ef..d62cfc0a81b 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -20,7 +20,7 @@ use iroha_config::{ sumeragi::Configuration as SumeragiConfiguration, torii::Configuration as ToriiConfiguration, }; -use iroha_core::prelude::*; +use iroha_core::{prelude::*, smartcontracts::query::Lazy}; use iroha_data_model::{peer::Peer as DataModelPeer, prelude::*}; use iroha_genesis::{GenesisNetwork, RawGenesisBlock}; use iroha_logger::{Configuration as LoggerConfiguration, InstrumentFutures}; @@ -717,7 +717,7 @@ pub trait TestClient: Sized { where R: ValidQuery + Into + Debug + Clone, >::Error: Into, - R::Output: Clone + Debug; + R::Output: Lazy + Clone + Debug; /// Submits instructions with polling /// @@ -732,7 +732,7 @@ pub trait TestClient: Sized { where R: ValidQuery + Into + Debug + Clone, >::Error: Into, - R::Output: Clone + Debug; + R::Output: Lazy + Clone + Debug; /// Polls request till predicate `f` is satisfied, with default period and max attempts. /// @@ -746,7 +746,7 @@ pub trait TestClient: Sized { where R: ValidQuery + Into + Debug + Clone, >::Error: Into, - R::Output: Clone + Debug; + R::Output: Lazy + Clone + Debug; /// Polls request till predicate `f` is satisfied with `period` and `max_attempts` supplied. /// @@ -762,7 +762,7 @@ pub trait TestClient: Sized { where R: ValidQuery + Into + Debug + Clone, >::Error: Into, - R::Output: Clone + Debug; + R::Output: Lazy + Clone + Debug; } impl TestRuntime for Runtime { @@ -857,7 +857,7 @@ impl TestClient for Client { where R: ValidQuery + Into + Debug + Clone, >::Error: Into, - R::Output: Clone + Debug, + R::Output: Lazy + Clone + Debug, { self.submit(instruction) .expect("Failed to submit instruction."); @@ -873,7 +873,7 @@ impl TestClient for Client { where R: ValidQuery + Into + Debug + Clone, >::Error: Into, - R::Output: Clone + Debug, + R::Output: Lazy + Clone + Debug, { self.submit_all(instructions) .expect("Failed to submit instruction."); @@ -890,7 +890,7 @@ impl TestClient for Client { where R: ValidQuery + Into + Debug + Clone, >::Error: Into, - R::Output: Clone + Debug, + R::Output: Lazy + Clone + Debug, { let mut query_result = None; for _ in 0..max_attempts { @@ -911,7 +911,7 @@ impl TestClient for Client { where R: ValidQuery + Into + Debug + Clone, >::Error: Into, - R::Output: Clone + Debug, + R::Output: Lazy + Clone + Debug, { self.poll_request_with_period(request, Configuration::pipeline_time() / 2, 10, f) } diff --git a/data_model/src/transaction.rs b/data_model/src/transaction.rs index 2b4120b221b..e37bdceb115 100644 --- a/data_model/src/transaction.rs +++ b/data_model/src/transaction.rs @@ -29,7 +29,7 @@ use crate::{ #[model] pub mod model { use super::*; - use crate::block::CommittedBlock; + use crate::block::VersionedCommittedBlock; /// Either ISI or Wasm binary #[derive( @@ -195,7 +195,7 @@ pub mod model { /// Transaction pub transaction: TransactionValue, /// The hash of the block to which `tx` belongs to - pub block_hash: HashOf, + pub block_hash: HashOf, } }