diff --git a/configs/peer/executor.wasm b/configs/peer/executor.wasm index 47647ccc0fd..53cf6d8683a 100644 Binary files a/configs/peer/executor.wasm and b/configs/peer/executor.wasm differ diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index cde7fe6a624..d4618e00961 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -16,17 +16,15 @@ use iroha_data_model::{ isi::InstructionBox, permission::PermissionTokenSchema, prelude::*, - query::{QueryBox, QueryId, QueryRequest, QueryWithParameters}, - smart_contract::{ - payloads::{self, Validate}, - SmartContractQueryRequest, - }, + query::{QueryBox, QueryRequest, QueryWithParameters}, + smart_contract::payloads::{self, Validate}, BatchedResponse, Level as LogLevel, ValidationFail, }; use iroha_logger::debug; // NOTE: Using error_span so that span info is logged on every event use iroha_logger::{error_span as wasm_log_span, prelude::tracing::Span}; use iroha_wasm_codec::{self as codec, WasmUsize}; +use parity_scale_codec::Decode; use wasmtime::{ Caller, Config, Engine, Linker, Module, Store, StoreLimits, StoreLimitsBuilder, TypedFunc, }; @@ -76,7 +74,7 @@ mod import { use super::super::*; - pub trait ExecuteOperations { + pub(crate) trait ExecuteOperations { /// Execute `query` on host #[codec::wrap_trait_fn] fn execute_query( @@ -245,6 +243,11 @@ pub mod error { /// [`Result`] type for this module pub type Result = core::result::Result; +#[cfg_attr(test, derive(parity_scale_codec::Encode))] +#[derive(Debug, derive_more::Display, Decode)] +#[repr(transparent)] +pub(crate) struct SmartContractQueryRequest(pub QueryRequest); + /// Create [`Module`] from bytes. /// /// # Errors @@ -1754,12 +1757,14 @@ mod tests { let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let mut wsv = WorldStateView::new(world_with_test_account(&authority), kura, query_handle); - let query_hex = encode_hex(SmartContractQueryRequest::query( - QueryBox::from(FindAccountById::new(authority.clone())), - Sorting::default(), - Pagination::default(), - FetchSize::default(), - )); + let query_hex = encode_hex(SmartContractQueryRequest(QueryRequest::Query( + QueryWithParameters::new( + FindAccountById::new(authority.clone()).into(), + Sorting::default(), + Pagination::default(), + FetchSize::default(), + ), + ))); let wat = format!( r#" diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index f550dc79f44..7c572323ea5 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -38,6 +38,8 @@ pub mod model { /// Note that [`InstructionBox`] is not a self-sufficient instruction, /// but just a wrapper to pass instructions back and forth. /// If you are a client SDK user then you likely don't need to use this type directly. + /// + /// The order of [`Self`] variants is guaranteed to correspond to the order of [`InstructionType`] variants #[derive( DebugCustom, Display, @@ -1690,15 +1692,16 @@ pub mod error { asset::AssetValueType, metadata, query::error::{FindError, QueryExecutionFail}, - IdBox, Value, + IdBox, }; #[model] pub mod model { use serde::{Deserialize, Serialize}; + use crate::{asset::AssetDefinitionId, Value}; + use super::*; - use crate::asset::AssetDefinitionId; /// Instruction execution error type #[derive( @@ -1964,6 +1967,7 @@ pub mod error { Self::Evaluate(InstructionEvaluationError::Type(err)) } } + impl From for MathError { fn from(err: FixedPointOperationError) -> Self { match err { diff --git a/data_model/src/query/mod.rs b/data_model/src/query/mod.rs index 6d96d52634f..3640fbd7db5 100644 --- a/data_model/src/query/mod.rs +++ b/data_model/src/query/mod.rs @@ -103,12 +103,16 @@ pub type QueryId = String; pub trait Query: Into + seal::Sealed { /// Output type of query type Output: Into + TryFrom; + + /// [`Encode`] [`Self`] as [`QueryBox`] + fn encode_as_query_box(&self) -> Vec; } #[model] pub mod model { use getset::Getters; use iroha_crypto::HashOf; + use strum::EnumDiscriminants; use super::*; use crate::{block::SignedBlock, permission::PermissionTokenId}; @@ -123,6 +127,7 @@ pub mod model { Eq, PartialOrd, Ord, + EnumDiscriminants, FromVariant, Decode, Encode, @@ -130,6 +135,12 @@ pub mod model { Serialize, IntoSchema, )] + #[strum_discriminants( + vis(pub(crate)), + name(QueryType), + derive(Encode), + allow(clippy::enum_variant_names) + )] #[ffi_type] #[allow(missing_docs)] pub enum QueryBox { @@ -248,6 +259,10 @@ impl From for MetadataValue { impl Query for QueryBox { type Output = Value; + + fn encode_as_query_box(&self) -> Vec { + self.encode() + } } impl TransactionQueryOutput { @@ -301,8 +316,9 @@ pub mod role { use alloc::{format, string::String, vec::Vec}; use derive_more::Display; + use parity_scale_codec::Encode; - use super::Query; + use super::{Query, QueryType}; use crate::prelude::*; queries! { @@ -345,18 +361,42 @@ pub mod role { impl Query for FindAllRoles { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllRoles.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAllRoleIds { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllRoleIds.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindRolesByAccountId { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindRolesByAccountId.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindRoleByRoleId { type Output = Role; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindRoleByRoleId.encode(); + self.encode_to(&mut output); + output + } } /// The prelude re-exports most commonly used traits, structs and macros from this module. @@ -372,8 +412,9 @@ pub mod permission { use alloc::{format, string::String, vec::Vec}; use derive_more::Display; + use parity_scale_codec::Encode; - use super::Query; + use super::{Query, QueryType}; use crate::{ permission::{self, PermissionTokenSchema}, prelude::*, @@ -400,10 +441,22 @@ pub mod permission { impl Query for FindPermissionTokenSchema { type Output = PermissionTokenSchema; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindPermissionTokenSchema.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindPermissionTokensByAccountId { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindPermissionTokensByAccountId.encode(); + self.encode_to(&mut output); + output + } } /// The prelude re-exports most commonly used traits, structs and macros from this module. @@ -419,8 +472,9 @@ pub mod account { use alloc::{format, string::String, vec::Vec}; use derive_more::Display; + use parity_scale_codec::Encode; - use super::{MetadataValue, Query}; + use super::{MetadataValue, Query, QueryType}; use crate::prelude::*; queries! { @@ -494,26 +548,62 @@ pub mod account { impl Query for FindAllAccounts { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllAccounts.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAccountById { type Output = Account; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAccountById.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAccountKeyValueByIdAndKey { type Output = MetadataValue; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAccountKeyValueByIdAndKey.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAccountsByName { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAccountsByName.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAccountsByDomainId { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAccountsByDomainId.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAccountsWithAsset { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAccountsWithAsset.encode(); + self.encode_to(&mut output); + output + } } /// The prelude re-exports most commonly used traits, structs and macros from this crate. @@ -534,10 +624,9 @@ pub mod asset { use alloc::{format, string::String, vec::Vec}; use derive_more::Display; - use iroha_data_model_derive::model; + use parity_scale_codec::Encode; - pub use self::model::*; - use super::{MetadataValue, Query}; + use super::{MetadataValue, Query, QueryType}; use crate::prelude::*; queries! { @@ -689,54 +778,132 @@ pub mod asset { } impl Query for FindAllAssets { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllAssets.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAllAssetsDefinitions { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllAssetsDefinitions.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetById { type Output = Asset; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetById.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetDefinitionById { type Output = AssetDefinition; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetDefinitionById.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetsByName { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetsByName.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetsByAccountId { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetsByAccountId.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetsByAssetDefinitionId { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetsByAssetDefinitionId.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetsByDomainId { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetsByDomainId.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetsByDomainIdAndAssetDefinitionId { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetsByDomainIdAndAssetDefinitionId.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetQuantityById { type Output = NumericValue; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetQuantityById.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindTotalAssetQuantityByAssetDefinitionId { type Output = NumericValue; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindTotalAssetQuantityByAssetDefinitionId.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetKeyValueByIdAndKey { type Output = MetadataValue; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetKeyValueByIdAndKey.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAssetDefinitionKeyValueByIdAndKey { type Output = MetadataValue; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAssetDefinitionKeyValueByIdAndKey.encode(); + self.encode_to(&mut output); + output + } } /// The prelude re-exports most commonly used traits, structs and macros from this crate. @@ -760,8 +927,9 @@ pub mod domain { use alloc::{format, string::String, vec::Vec}; use derive_more::Display; + use parity_scale_codec::Encode; - use super::{MetadataValue, Query}; + use super::{MetadataValue, Query, QueryType}; use crate::prelude::*; queries! { @@ -797,14 +965,32 @@ pub mod domain { impl Query for FindAllDomains { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllDomains.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindDomainById { type Output = Domain; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindDomainById.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindDomainKeyValueByIdAndKey { type Output = MetadataValue; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindDomainKeyValueByIdAndKey.encode(); + self.encode_to(&mut output); + output + } } /// The prelude re-exports most commonly used traits, structs and macros from this crate. @@ -820,8 +1006,9 @@ pub mod peer { use alloc::{format, string::String, vec::Vec}; use derive_more::Display; + use parity_scale_codec::Encode; - use super::Query; + use super::{Query, QueryType}; use crate::{parameter::Parameter, peer::Peer}; queries! { @@ -841,10 +1028,22 @@ pub mod peer { impl Query for FindAllPeers { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllPeers.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAllParameters { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllParameters.encode(); + self.encode_to(&mut output); + output + } } /// The prelude re-exports most commonly used traits, structs and macros from this crate. @@ -859,8 +1058,9 @@ pub mod trigger { use alloc::{format, string::String, vec::Vec}; use derive_more::Display; + use parity_scale_codec::Encode; - use super::{MetadataValue, Query}; + use super::{MetadataValue, Query, QueryType}; use crate::{ domain::prelude::*, events::TriggeringFilterBox, @@ -915,18 +1115,42 @@ pub mod trigger { impl Query for FindAllActiveTriggerIds { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllActiveTriggerIds.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindTriggerById { type Output = Trigger; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindTriggerById.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindTriggerKeyValueByIdAndKey { type Output = MetadataValue; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindTriggerKeyValueByIdAndKey.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindTriggersByDomainId { type Output = Vec>; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindTriggersByDomainId.encode(); + self.encode_to(&mut output); + output + } } pub mod prelude { @@ -948,8 +1172,9 @@ pub mod transaction { use derive_more::Display; use iroha_crypto::HashOf; + use parity_scale_codec::Encode; - use super::{Query, TransactionQueryOutput}; + use super::{Query, QueryType, TransactionQueryOutput}; use crate::{account::AccountId, prelude::Account, transaction::SignedTransaction}; queries! { @@ -986,14 +1211,32 @@ pub mod transaction { impl Query for FindAllTransactions { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllTransactions.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindTransactionsByAccountId { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindTransactionsByAccountId.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindTransactionByHash { type Output = TransactionQueryOutput; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindTransactionByHash.encode(); + self.encode_to(&mut output); + output + } } /// The prelude re-exports most commonly used traits, structs and macros from this crate. @@ -1012,8 +1255,9 @@ pub mod block { use derive_more::Display; use iroha_crypto::HashOf; + use parity_scale_codec::{Decode, Encode}; - use super::Query; + use super::{Query, QueryType}; use crate::block::{BlockHeader, SignedBlock}; queries! { @@ -1045,14 +1289,32 @@ pub mod block { impl Query for FindAllBlocks { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllBlocks.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindAllBlockHeaders { type Output = Vec; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindAllBlockHeaders.encode(); + self.encode_to(&mut output); + output + } } impl Query for FindBlockHeaderByHash { type Output = BlockHeader; + + fn encode_as_query_box(&self) -> Vec { + let mut output = QueryType::FindBlockHeaderByHash.encode(); + self.encode_to(&mut output); + output + } } /// The prelude re-exports most commonly used traits, structs and macros from this crate. @@ -1209,7 +1471,7 @@ pub mod http { impl QueryBuilder { /// Construct a new request with the `query`. - pub fn new(query: impl Into, authority: AccountId) -> Self { + pub fn new(query: impl Query, authority: AccountId) -> Self { Self { payload: QueryPayload { query: query.into(), diff --git a/data_model/src/smart_contract.rs b/data_model/src/smart_contract.rs index 6e363f07218..379da0585d9 100644 --- a/data_model/src/smart_contract.rs +++ b/data_model/src/smart_contract.rs @@ -1,16 +1,5 @@ //! This module contains data and structures related only to smart contract execution -use parity_scale_codec::{Decode, Encode}; - -pub use self::model::*; -use crate::{ - prelude::FetchSize, - query::{ - cursor::ForwardCursor, sorting::Sorting, Pagination, QueryBox, QueryRequest, - QueryWithParameters, - }, -}; - pub mod payloads { //! Payloads with function arguments for different entrypoints @@ -52,55 +41,3 @@ pub mod payloads { pub target: T, } } - -#[crate::model] -pub mod model { - use super::*; - - /// Request type for `execute_query()` function. - #[derive(Debug, derive_more::Display, Clone, Decode, Encode)] - pub struct SmartContractQueryRequest(pub QueryRequest); -} - -impl SmartContractQueryRequest { - /// Construct a new request containing query. - pub fn query( - query: QueryBox, - sorting: Sorting, - pagination: Pagination, - fetch_size: FetchSize, - ) -> Self { - Self(QueryRequest::Query(QueryWithParameters::new( - query, sorting, pagination, fetch_size, - ))) - } - - /// Construct a new request containing cursor. - pub fn cursor(cursor: ForwardCursor) -> Self { - Self(QueryRequest::Cursor(cursor)) - } - - /// Unwrap [`Self`] if it was previously constructed with [`query()`](Self::query). - /// - /// # Panics - /// - /// Panics if [`Self`] was constructed with [`cursor()`](Self::cursor). - pub fn unwrap_query(self) -> (QueryBox, Sorting, Pagination) { - match self.0 { - QueryRequest::Query(query) => (query.query, query.sorting, query.pagination), - QueryRequest::Cursor(_) => panic!("Expected query, got cursor"), - } - } - - /// Unwrap [`Self`] if it was previously constructed with [`cursor()`](Self::cursor). - /// - /// # Panics - /// - /// Panics if [`Self`] was constructed with [`query()`](Self::query). - pub fn unwrap_cursor(self) -> ForwardCursor { - match self.0 { - QueryRequest::Query(_) => panic!("Expected cursor, got query"), - QueryRequest::Cursor(cursor) => cursor, - } - } -} diff --git a/smart_contract/executor/derive/src/token.rs b/smart_contract/executor/derive/src/token.rs index a68c22ebcb4..e9c2b03f271 100644 --- a/smart_contract/executor/derive/src/token.rs +++ b/smart_contract/executor/derive/src/token.rs @@ -25,7 +25,7 @@ fn impl_token(ident: &syn2::Ident, generics: &syn2::Generics) -> proc_macro2::To fn is_owned_by(&self, account_id: &::iroha_executor::data_model::account::AccountId) -> bool { let account_tokens_cursor = ::iroha_executor::smart_contract::debug::DebugExpectExt::dbg_expect( ::iroha_executor::smart_contract::ExecuteQueryOnHost::execute( - ::iroha_executor::data_model::query::permission::FindPermissionTokensByAccountId::new( + &::iroha_executor::data_model::query::permission::FindPermissionTokensByAccountId::new( account_id.clone(), ) ), diff --git a/smart_contract/src/lib.rs b/smart_contract/src/lib.rs index ec0c52b20e9..3550bacdd41 100644 --- a/smart_contract/src/lib.rs +++ b/smart_contract/src/lib.rs @@ -12,7 +12,6 @@ use data_model::{ isi::Instruction, prelude::*, query::{cursor::ForwardCursor, sorting::Sorting, Pagination, Query}, - smart_contract::SmartContractQueryRequest, BatchedResponse, }; use derive_more::Display; @@ -97,44 +96,44 @@ impl ExecuteOnHost for I { } } +#[derive(Debug, Encode)] +enum QueryRequest<'a, Q> { + Query(QueryWithParameters<'a, Q>), + Cursor(&'a ForwardCursor), +} + /// Generic query request containing additional parameters. #[derive(Debug)] -pub struct QueryRequest { - query: Q, +pub struct QueryWithParameters<'a, Q> { + query: &'a Q, sorting: Sorting, pagination: Pagination, fetch_size: FetchSize, } -impl From> for SmartContractQueryRequest { - fn from(query_request: QueryRequest) -> Self { - SmartContractQueryRequest::query( - query_request.query.into(), - query_request.sorting, - query_request.pagination, - query_request.fetch_size, - ) +impl Encode for QueryWithParameters<'_, Q> { + fn encode(&self) -> Vec { + let mut output = self.query.encode_as_query_box(); + self.sorting.encode_to(&mut output); + self.pagination.encode_to(&mut output); + self.fetch_size.encode_to(&mut output); + output } } /// Implementing queries can be executed on the host -/// -/// TODO: `&self` should be enough pub trait ExecuteQueryOnHost: Sized { /// Query output type. type Output; - /// Type of [`QueryRequest`]. - type QueryRequest; - /// Apply sorting to a query - fn sort(self, sorting: Sorting) -> Self::QueryRequest; + fn sort(&self, sorting: Sorting) -> QueryWithParameters; /// Apply pagination to a query - fn paginate(self, pagination: Pagination) -> Self::QueryRequest; + fn paginate(&self, pagination: Pagination) -> QueryWithParameters; /// Set fetch size for a query. Default is [`DEFAULT_FETCH_SIZE`] - fn fetch_size(self, fetch_size: FetchSize) -> Self::QueryRequest; + fn fetch_size(&self, fetch_size: FetchSize) -> QueryWithParameters; /// Execute query on the host /// @@ -142,7 +141,7 @@ pub trait ExecuteQueryOnHost: Sized { /// /// - If query validation failed /// - If query execution failed - fn execute(self) -> Result, ValidationFail>; + fn execute(&self) -> Result, ValidationFail>; } impl ExecuteQueryOnHost for Q @@ -151,10 +150,9 @@ where >::Error: core::fmt::Debug, { type Output = Q::Output; - type QueryRequest = QueryRequest; - fn sort(self, sorting: Sorting) -> Self::QueryRequest { - QueryRequest { + fn sort(&self, sorting: Sorting) -> QueryWithParameters { + QueryWithParameters { query: self, sorting, pagination: Pagination::default(), @@ -162,8 +160,8 @@ where } } - fn paginate(self, pagination: Pagination) -> Self::QueryRequest { - QueryRequest { + fn paginate(&self, pagination: Pagination) -> QueryWithParameters { + QueryWithParameters { query: self, sorting: Sorting::default(), pagination, @@ -171,8 +169,8 @@ where } } - fn fetch_size(self, fetch_size: FetchSize) -> Self::QueryRequest { - QueryRequest { + fn fetch_size(&self, fetch_size: FetchSize) -> QueryWithParameters { + QueryWithParameters { query: self, sorting: Sorting::default(), pagination: Pagination::default(), @@ -180,8 +178,8 @@ where } } - fn execute(self) -> Result, ValidationFail> { - QueryRequest { + fn execute(&self) -> Result, ValidationFail> { + QueryWithParameters { query: self, sorting: Sorting::default(), pagination: Pagination::default(), @@ -191,49 +189,55 @@ where } } -impl ExecuteQueryOnHost for QueryRequest +impl QueryWithParameters<'_, Q> where Q::Output: DecodeAll, >::Error: core::fmt::Debug, { - type Output = Q::Output; - type QueryRequest = Self; - - fn sort(mut self, sorting: Sorting) -> Self { + /// Apply sorting to a query + #[must_use] + pub fn sort(mut self, sorting: Sorting) -> Self { self.sorting = sorting; self } - fn paginate(mut self, pagination: Pagination) -> Self { + /// Apply pagination to a query + #[must_use] + pub fn paginate(mut self, pagination: Pagination) -> Self { self.pagination = pagination; self } - fn fetch_size(mut self, fetch_size: FetchSize) -> Self::QueryRequest { + /// Set fetch size for a query. Default is [`DEFAULT_FETCH_SIZE`] + #[must_use] + pub fn fetch_size(mut self, fetch_size: FetchSize) -> Self { self.fetch_size = fetch_size; self } - #[allow(irrefutable_let_patterns)] - fn execute(self) -> Result, ValidationFail> { + /// Execute query on the host + /// + /// # Errors + /// + /// - If query validation failed + /// - If query execution failed + pub fn execute(self) -> Result, ValidationFail> { #[cfg(not(test))] use host::execute_query as host_execute_query; #[cfg(test)] use tests::_iroha_smart_contract_execute_query_mock as host_execute_query; - let wasm_query_request = SmartContractQueryRequest::from(self); - // Safety: - `host_execute_query` doesn't take ownership of it's pointer parameter // - ownership of the returned result is transferred into `_decode_from_raw` let res: Result, ValidationFail> = unsafe { decode_with_length_prefix_from_raw(encode_and_execute( - &wasm_query_request, + &QueryRequest::Query(self), host_execute_query, )) }; let (value, cursor) = res?.into(); - let typed_value = Self::Output::try_from(value).expect("Query output has incorrect type"); + let typed_value = Q::Output::try_from(value).expect("Query output has incorrect type"); Ok(QueryOutputCursor { batch: typed_value, cursor, @@ -312,20 +316,17 @@ pub struct QueryOutputCursorIterator { } impl> QueryOutputCursorIterator { - #[allow(irrefutable_let_patterns)] fn next_batch(&self) -> Result>> { #[cfg(not(test))] use host::execute_query as host_execute_query; #[cfg(test)] use tests::_iroha_smart_contract_execute_query_mock as host_execute_query; - let wasm_query_request = SmartContractQueryRequest::cursor(self.cursor.clone()); - // Safety: - `host_execute_query` doesn't take ownership of it's pointer parameter // - ownership of the returned result is transferred into `_decode_from_raw` let res: Result, ValidationFail> = unsafe { decode_with_length_prefix_from_raw(encode_and_execute( - &wasm_query_request, + &QueryRequest::::Cursor(&self.cursor), host_execute_query, )) }; @@ -424,10 +425,39 @@ mod tests { use data_model::{query::asset::FindAssetQuantityById, BatchedResponseV1}; use iroha_smart_contract_utils::encode_with_length_prefix; + use parity_scale_codec::Decode; use webassembly_test::webassembly_test; use super::*; + #[derive(Decode)] + struct QueryWithParameters { + query: Q, + sorting: Sorting, + pagination: Pagination, + #[allow(dead_code)] + fetch_size: FetchSize, + } + + #[derive(Decode)] + enum QueryRequest { + Query(QueryWithParameters), + Cursor(#[allow(unused_tuple_struct_fields)] ForwardCursor), + } + + #[derive(Decode)] + #[repr(transparent)] + struct SmartContractQueryRequest(pub QueryRequest); + + impl SmartContractQueryRequest { + fn unwrap_query(self) -> (QueryBox, Sorting, Pagination) { + match self.0 { + QueryRequest::Query(query) => (query.query, query.sorting, query.pagination), + QueryRequest::Cursor(_) => panic!("Expected query, got cursor"), + } + } + } + const QUERY_RESULT: Result, ValidationFail> = Ok(QueryOutputCursor { batch: Value::Numeric(NumericValue::U32(1234_u32)), cursor: ForwardCursor::new(None, None),