diff --git a/core/src/query/store.rs b/core/src/query/store.rs index 7bb52d2313b..3cc4fdfdb8d 100644 --- a/core/src/query/store.rs +++ b/core/src/query/store.rs @@ -12,6 +12,7 @@ use iroha_data_model::{ asset::AssetValue, query::{ cursor::ForwardCursor, error::QueryExecutionFail, pagination::Pagination, sorting::Sorting, + QueryId, }, BatchedResponse, BatchedResponseV1, HasMetadata, IdentifiableBox, ValidationFail, Value, }; @@ -60,7 +61,7 @@ type LiveQuery = Batched>; /// Clients can handle their queries using [`LiveQueryStoreHandle`] #[derive(Debug)] pub struct LiveQueryStore { - queries: HashMap, + queries: HashMap, query_idle_time: Duration, } @@ -96,7 +97,8 @@ impl LiveQueryStore { "All handler to LiveQueryStore are dropped. Shutting down..."; let (insert_sender, mut insert_receiver) = mpsc::channel(1); - let (remove_sender, mut remove_receiver) = mpsc::channel::<(String, oneshot::Sender<_>)>(1); + let (remove_sender, mut remove_receiver) = + mpsc::channel::<(QueryId, oneshot::Sender<_>)>(1); let mut idle_interval = tokio::time::interval(self.query_idle_time); @@ -134,7 +136,7 @@ impl LiveQueryStore { } } - fn insert(&mut self, query_id: String, live_query: LiveQuery) { + fn insert(&mut self, query_id: QueryId, live_query: LiveQuery) { self.queries.insert(query_id, (live_query, Instant::now())); } @@ -147,10 +149,10 @@ impl LiveQueryStore { #[derive(Clone)] pub struct LiveQueryStoreHandle { /// Sender to insert a new query with specified id. - insert_sender: mpsc::Sender<(String, LiveQuery)>, + insert_sender: mpsc::Sender<(QueryId, LiveQuery)>, /// Sender to send a tuple of query id and another sender, which will be /// used by [`LiveQueryStore`] to write a response with optional live query. - remove_sender: mpsc::Sender<(String, oneshot::Sender>)>, + remove_sender: mpsc::Sender<(QueryId, oneshot::Sender>)>, } impl LiveQueryStoreHandle { @@ -197,13 +199,25 @@ impl LiveQueryStoreHandle { self.construct_query_response(query_id, cursor.cursor.map(NonZeroU64::get), live_query) } - fn insert(&self, query_id: String, live_query: LiveQuery) -> Result<()> { + /// Remove query from the storage if there is any. + /// + /// Returns `true` if query was removed, `false` otherwise. + /// + /// # Errors + /// + /// - Returns [`Error::ConnectionClosed`] if [`QueryService`] is dropped, + /// - Otherwise throws up query output handling errors. + pub fn forget_query(&self, query_id: QueryId) -> Result { + self.remove(query_id).map(|query_opt| query_opt.is_some()) + } + + fn insert(&self, query_id: QueryId, live_query: LiveQuery) -> Result<()> { self.insert_sender .blocking_send((query_id, live_query)) .map_err(|_| Error::ConnectionClosed) } - fn remove(&self, query_id: String) -> Result> { + fn remove(&self, query_id: QueryId) -> Result> { let (sender, receiver) = oneshot::channel(); self.remove_sender @@ -215,7 +229,7 @@ impl LiveQueryStoreHandle { fn construct_query_response( &self, - query_id: String, + query_id: QueryId, curr_cursor: Option, mut live_query: Batched>, ) -> Result> { diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index 8a38a178b6d..334d5c290e2 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -5,7 +5,7 @@ use std::num::NonZeroUsize; use error::*; -use import_traits::{ +use import::traits::{ ExecuteOperations as _, GetExecutorPayloads as _, SetPermissionTokenSchema as _, }; use iroha_config::{ @@ -18,7 +18,7 @@ use iroha_data_model::{ isi::InstructionExpr, permission::PermissionTokenSchema, prelude::*, - query::{QueryBox, QueryRequest, QueryWithParameters}, + query::{QueryBox, QueryId, QueryRequest, QueryWithParameters}, smart_contract::{ payloads::{self, Validate}, SmartContractQueryRequest, @@ -29,13 +29,16 @@ 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 state::{Wsv as _, WsvMut as _}; use wasmtime::{ Caller, Config, Engine, Linker, Module, Store, StoreLimits, StoreLimitsBuilder, TypedFunc, }; -use self::state::Authority; -use crate::{smartcontracts::Execute, wsv::WorldStateView, ValidQuery as _}; +use crate::{ + query::store::LiveQueryStoreHandle, + smartcontracts::{wasm::state::ValidateQueryOperation, Execute}, + wsv::WorldStateView, + ValidQuery as _, +}; /// Name of the exported memory const WASM_MEMORY: &str = "memory"; @@ -67,48 +70,48 @@ mod import { pub const EXECUTOR_VALIDATE_INSTRUCTION: &str = "_iroha_executor_validate_instruction"; pub const EXECUTOR_VALIDATE_QUERY: &str = "_iroha_executor_validate_query"; pub const EXECUTOR_MIGRATE: &str = "_iroha_executor_migrate"; -} -mod import_traits { - //! Traits which some [Runtime]s should implement to import functions from Iroha to WASM + pub mod traits { + //! Traits which some [Runtime]s should implement to import functions from Iroha to WASM - use iroha_data_model::{query::QueryBox, smart_contract::payloads::Validate}; + use iroha_data_model::{query::QueryBox, smart_contract::payloads::Validate}; - use super::*; + use super::super::*; - pub trait ExecuteOperations { - /// Execute `query` on host - #[codec::wrap_trait_fn] - fn execute_query( - query_request: SmartContractQueryRequest, - state: &S, - ) -> Result, ValidationFail>; - - /// Execute `instruction` on host - #[codec::wrap_trait_fn] - fn execute_instruction( - instruction: InstructionExpr, - state: &mut S, - ) -> Result<(), ValidationFail>; - } + pub trait ExecuteOperations { + /// Execute `query` on host + #[codec::wrap_trait_fn] + fn execute_query( + query_request: SmartContractQueryRequest, + state: &mut S, + ) -> Result, ValidationFail>; - pub trait GetExecutorPayloads { - #[codec::wrap_trait_fn] - fn get_migrate_payload(state: &S) -> payloads::Migrate; + /// Execute `instruction` on host + #[codec::wrap_trait_fn] + fn execute_instruction( + instruction: InstructionExpr, + state: &mut S, + ) -> Result<(), ValidationFail>; + } - #[codec::wrap_trait_fn] - fn get_validate_transaction_payload(state: &S) -> Validate; + pub trait GetExecutorPayloads { + #[codec::wrap_trait_fn] + fn get_migrate_payload(state: &S) -> payloads::Migrate; - #[codec::wrap_trait_fn] - fn get_validate_instruction_payload(state: &S) -> Validate; + #[codec::wrap_trait_fn] + fn get_validate_transaction_payload(state: &S) -> Validate; - #[codec::wrap_trait_fn] - fn get_validate_query_payload(state: &S) -> Validate; - } + #[codec::wrap_trait_fn] + fn get_validate_instruction_payload(state: &S) -> Validate; + + #[codec::wrap_trait_fn] + fn get_validate_query_payload(state: &S) -> Validate; + } - pub trait SetPermissionTokenSchema { - #[codec::wrap_trait_fn] - fn set_permission_token_schema(schema: PermissionTokenSchema, state: &mut S); + pub trait SetPermissionTokenSchema { + #[codec::wrap_trait_fn] + fn set_permission_token_schema(schema: PermissionTokenSchema, state: &mut S); + } } } @@ -119,9 +122,15 @@ pub mod error { /// `WebAssembly` execution error type #[derive(Debug, thiserror::Error, displaydoc::Display)] + #[ignore_extra_doc_attributes] pub enum Error { /// Runtime initialization failure Initialization(#[source] WasmtimeError), + /// Runtime finalization failure. + /// + /// Currently only [`crate::query::store::Error`] might fail in this case. + /// [`From`] is not implemented to force users to explicitly wrap this error. + Finalization(#[source] crate::query::store::Error), /// Failed to load module ModuleLoading(#[source] WasmtimeError), /// Module could not be instantiated @@ -270,6 +279,19 @@ fn create_config() -> Result { Ok(config) } +/// Remove all executed queries from the query storage. +fn forget_all_executed_queries( + query_handle: &LiveQueryStoreHandle, + executed_queries: impl IntoIterator, +) -> Result<()> { + for query_id in executed_queries { + let _ = query_handle + .forget_query(query_id) + .map_err(Error::Finalization)?; + } + Ok(()) +} + /// Limits checker for smartcontracts. #[derive(Copy, Clone)] struct LimitsExecutor { @@ -308,6 +330,10 @@ impl LimitsExecutor { pub mod state { //! All supported states for [`Runtime`](super::Runtime) + use std::collections::HashSet; + + use derive_more::Constructor; + use super::*; /// Construct [`StoreLimits`] from [`Configuration`] @@ -329,258 +355,211 @@ pub mod state { .build() } - /// Common data for states - pub struct Common<'wrld> { + /// State for most common operations. + /// Generic over concrete state kind and containing common data. + pub struct CommonState { pub(super) authority: AccountId, pub(super) store_limits: StoreLimits, - pub(super) wsv: &'wrld mut WorldStateView, /// Span inside of which all logs are recorded for this smart contract pub(super) log_span: Span, + pub(super) executed_queries: HashSet, + /// Concrete kind of state + pub(super) kind: K, } - impl<'wrld> Common<'wrld> { - /// Create new [`Common`] - pub fn new( - wsv: &'wrld mut WorldStateView, - authority: AccountId, - config: Configuration, - log_span: Span, - ) -> Self { + impl CommonState { + /// Create new [`OrdinaryState`] + pub fn new(authority: AccountId, config: Configuration, log_span: Span, kind: K) -> Self { Self { - wsv, authority, store_limits: store_limits_from_config(&config), log_span, + executed_queries: HashSet::new(), + kind, } } - } - - /// Trait to get span for logs. - /// - /// Used to implement [`log()`](Runtime::log) export. - pub trait LogSpan { - /// Get log span - fn log_span(&self) -> &Span; - } - - /// Trait to get mutable reference to limits - /// - /// Used to implement [`Runtime::create_store()`]. - pub trait LimitsMut { - /// Get mutable reference to store limits - fn limits_mut(&mut self) -> &mut StoreLimits; - } - - /// Trait to get authority account id - pub trait Authority { - /// Get authority account id - fn authority(&self) -> &AccountId; - } - - /// Trait to get an immutable reference to [`WorldStateView`] - pub trait Wsv { - /// Get immutable [`WorldStateView`] - fn wsv(&self) -> &WorldStateView; - } - - /// Trait to get mutable reference to [`WorldStateView`] - pub trait WsvMut { - /// Get mutable [`WorldStateView`] - fn wsv_mut(&mut self) -> &mut WorldStateView; - } - - /// Smart Contract execution state - pub struct SmartContract<'wrld> { - pub(super) common: Common<'wrld>, - /// Should be set for smart contract validation only. - pub(super) limits_executor: Option, - } - - impl LogSpan for SmartContract<'_> { - fn log_span(&self) -> &Span { - &self.common.log_span - } - } - - impl LimitsMut for SmartContract<'_> { - fn limits_mut(&mut self) -> &mut StoreLimits { - &mut self.common.store_limits - } - } - - impl Authority for SmartContract<'_> { - fn authority(&self) -> &AccountId { - &self.common.authority - } - } - - impl Wsv for SmartContract<'_> { - fn wsv(&self) -> &WorldStateView { - self.common.wsv - } - } - impl WsvMut for SmartContract<'_> { - fn wsv_mut(&mut self) -> &mut WorldStateView { - self.common.wsv + /// Take executed queries leaving an empty set + pub fn take_executed_queries(&mut self) -> HashSet { + std::mem::take(&mut self.executed_queries) } } - /// Trigger execution state - pub struct Trigger<'wrld> { - pub(super) common: Common<'wrld>, - /// Event which activated this trigger - pub(super) triggering_event: Event, - } - - impl LogSpan for Trigger<'_> { - fn log_span(&self) -> &Span { - &self.common.log_span - } + /// Trait to validate queries and instructions before execution. + pub trait ValidateQueryOperation { + /// Validate `query`. + /// + /// # Errors + /// + /// Returns error if query validation failed. + fn validate_query( + &self, + authority: &AccountId, + query: QueryBox, + ) -> Result<(), ValidationFail>; } - impl LimitsMut for Trigger<'_> { - fn limits_mut(&mut self) -> &mut StoreLimits { - &mut self.common.store_limits - } - } + pub mod kind { + //! Module with all kinds of state. - impl Authority for Trigger<'_> { - fn authority(&self) -> &AccountId { - &self.common.authority - } - } + use super::*; - impl Wsv for Trigger<'_> { - fn wsv(&self) -> &WorldStateView { - self.common.wsv + /// State kinds containing const reference to [`WorldStateView`] + #[derive(Constructor)] + pub struct WithWsv<'wrld, K> { + pub(in super::super) wsv: &'wrld WorldStateView, + pub(in super::super) sub_kind: K, } - } - impl WsvMut for Trigger<'_> { - fn wsv_mut(&mut self) -> &mut WorldStateView { - self.common.wsv + /// State kinds containing mutable reference to [`WorldStateView`] + #[derive(Constructor)] + pub struct WithMutWsv<'wrld, K> { + pub(in super::super) wsv: &'wrld mut WorldStateView, + pub(in super::super) sub_kind: K, } - } - - pub mod executor { - //! States related to *Executor* execution. - - use super::*; - /// Struct to encapsulate common state for `validate_transaction()` and - /// `validate_instruction()` entrypoints. + /// Trait to get immutable [`WorldStateView`] /// - /// *Mut* means that [`WorldStateView`] will be mutated. - pub struct ValidateMut<'wrld, T> { - pub(in super::super) common: Common<'wrld>, - pub(in super::super) to_validate: T, + /// Exists to write generic code for [`WithWsv`] and [`WithMutWsv`. + pub trait Wsv { + /// Get immutable [`WorldStateView`] + fn wsv(&self) -> &WorldStateView; } - impl LogSpan for ValidateMut<'_, T> { - fn log_span(&self) -> &Span { - &self.common.log_span + impl Wsv for WithWsv<'_, K> { + fn wsv(&self) -> &WorldStateView { + self.wsv } } - impl LimitsMut for ValidateMut<'_, T> { - fn limits_mut(&mut self) -> &mut StoreLimits { - &mut self.common.store_limits + impl Wsv for WithMutWsv<'_, K> { + fn wsv(&self) -> &WorldStateView { + self.wsv } } - impl Authority for ValidateMut<'_, T> { - fn authority(&self) -> &AccountId { - &self.common.authority + pub mod sub_kind { + //! Module with all real state kinds. + + use super::*; + + /// Smart Contract execution state kind + #[derive(Copy, Clone)] + pub struct SmartContract { + pub(in super::super::super) limits_executor: Option, } - } - impl Wsv for ValidateMut<'_, T> { - fn wsv(&self) -> &WorldStateView { - self.common.wsv + impl SmartContract { + /// Create new [`SmartContract`] + pub(in super::super::super) fn new( + limits_executor: Option, + ) -> Self { + Self { limits_executor } + } } - } - impl WsvMut for ValidateMut<'_, T> { - fn wsv_mut(&mut self) -> &mut WorldStateView { - self.common.wsv + /// Trigger execution state kind + #[derive(Constructor)] + pub struct Trigger { + /// Event which activated this trigger + pub(in super::super::super) triggering_event: Event, } - } - /// State for executing `validate_transaction()` entrypoint of executor - pub type ValidateTransaction<'wrld> = ValidateMut<'wrld, SignedTransaction>; + pub mod executor { + //! Stat kinds related to *Executor* execution. - /// State for executing `validate_instruction()` entrypoint of executor - pub type ValidateInstruction<'wrld> = ValidateMut<'wrld, InstructionExpr>; + use super::*; - /// State for executing `validate_query()` entrypoint of executor - /// - /// Does not implement [`WsvMut`] because it contains immutable reference to - /// [`WorldStateView`] since it shouldn't be changed during *query* validation. - pub struct ValidateQuery<'wrld> { - pub(in super::super) authority: AccountId, - pub(in super::super) store_limits: StoreLimits, - pub(in super::super) wsv: &'wrld WorldStateView, - pub(in super::super) log_span: Span, - pub(in super::super) query: QueryBox, - } + /// Struct to encapsulate common state kinds for `validate_*` entrypoints + #[derive(Constructor)] + pub struct Validate { + pub(in super::super::super::super) to_validate: T, + } - impl LogSpan for ValidateQuery<'_> { - fn log_span(&self) -> &Span { - &self.log_span - } - } + /// State kind for executing `validate_transaction()` entrypoint of executor + pub type ValidateTransaction = Validate; - impl LimitsMut for ValidateQuery<'_> { - fn limits_mut(&mut self) -> &mut StoreLimits { - &mut self.store_limits - } - } + /// State kind for executing `validate_query()` entrypoint of executor + pub type ValidateQuery = Validate; - impl Authority for ValidateQuery<'_> { - fn authority(&self) -> &AccountId { - &self.authority - } - } + /// State kind for executing `validate_instruction()` entrypoint of executor + pub type ValidateInstruction = Validate; - impl Wsv for ValidateQuery<'_> { - fn wsv(&self) -> &WorldStateView { - self.wsv + /// State kind for executing `migrate()` entrypoint of executor + #[derive(Copy, Clone)] + pub struct Migrate; } } + } - /// State for executing `migrate()` entrypoint of executor - pub struct Migrate<'wrld>(pub(in super::super) Common<'wrld>); + /// State for smart contract execution + pub type SmartContract<'wrld> = + CommonState>; - impl LimitsMut for Migrate<'_> { - fn limits_mut(&mut self) -> &mut StoreLimits { - &mut self.0.store_limits - } - } + /// state for trigger execution + pub type Trigger<'wrld> = CommonState>; - impl LogSpan for Migrate<'_> { - fn log_span(&self) -> &Span { - &self.0.log_span - } + impl ValidateQueryOperation for SmartContract<'_> { + fn validate_query( + &self, + authority: &AccountId, + query: QueryBox, + ) -> Result<(), ValidationFail> { + let wsv: &WorldStateView = self.kind.wsv; + wsv.executor().validate_query(wsv, authority, query) } + } - impl Authority for Migrate<'_> { - fn authority(&self) -> &AccountId { - &self.0.authority - } + impl ValidateQueryOperation for Trigger<'_> { + fn validate_query( + &self, + authority: &AccountId, + query: QueryBox, + ) -> Result<(), ValidationFail> { + let wsv: &WorldStateView = self.kind.wsv; + wsv.executor().validate_query(wsv, authority, query) } + } - impl Wsv for Migrate<'_> { - fn wsv(&self) -> &WorldStateView { - self.0.wsv - } - } + pub mod executor { + //! States for different executor entrypoints - impl WsvMut for Migrate<'_> { - fn wsv_mut(&mut self) -> &mut WorldStateView { - self.0.wsv - } + use super::*; + + /// State for executing `validate_transaction()` entrypoint + pub type ValidateTransaction<'wrld> = + CommonState>; + + /// State for executing `validate_query()` entrypoint + pub type ValidateQuery<'wrld> = + CommonState>; + + /// State for executing `validate_instruction()` entrypoint + pub type ValidateInstruction<'wrld> = + CommonState>; + + /// State for executing `migrate()` entrypoint + pub type Migrate<'wrld> = + CommonState>; + + macro_rules! blank_validate_operations { + (impl$(<$($generics_decl:tt),+>)? for $t:ident$(<$($generics:tt),+>)?) => { + impl$(<$($generics_decl),+>)? ValidateQueryOperation for $t$(<$($generics),+>)? { + fn validate_query( + &self, + _authority: &AccountId, + _query: QueryBox, + ) -> Result<(), ValidationFail> { + Ok(()) + } + } + }; } + + blank_validate_operations!(impl for ValidateTransaction<'_>); + blank_validate_operations!(impl for ValidateInstruction<'_>); + blank_validate_operations!(impl for ValidateQuery<'_>); + blank_validate_operations!(impl for Migrate<'_>); } } @@ -695,17 +674,20 @@ struct LogError(u8); /// It's required by `#[codec::wrap]` to parse well type WasmtimeError = wasmtime::Error; -impl Runtime { +impl Runtime> { /// Log the given string at the given log level /// /// # Errors /// /// If log level or string decoding fails #[codec::wrap] - pub fn log((log_level, msg): (u8, String), state: &S) -> Result<(), WasmtimeError> { + pub fn log( + (log_level, msg): (u8, String), + state: &state::CommonState, + ) -> Result<(), WasmtimeError> { const TARGET: &str = "WASM"; - let _span = state.log_span().enter(); + let _span = state.log_span.enter(); match LogLevel::from_repr(log_level) .ok_or(LogError(log_level)) .map_err(wasmtime::Error::from)? @@ -728,24 +710,24 @@ impl Runtime { } Ok(()) } -} -impl Runtime { - fn create_store(&self, state: S) -> Store { + fn create_store(&self, state: state::CommonState) -> Store> { let mut store = Store::new(&self.engine, state); - store.limiter(|s| s.limits_mut()); + store.limiter(|s| &mut s.store_limits); store .add_fuel(self.config.fuel_limit) .expect("Wasm Runtime config is malformed, this is a bug"); store } +} +impl Runtime> { fn execute_executor_validate_internal( &self, module: &wasmtime::Module, - state: S, + state: state::CommonState, validate_fn_name: &'static str, ) -> Result { let mut store = self.create_store(state); @@ -763,46 +745,71 @@ impl Runtime { let dealloc_fn = Self::get_typed_func(&instance, &mut store, import::SMART_CONTRACT_DEALLOC) .expect("Checked at instantiation step"); - codec::decode_with_length_prefix_from_memory(&memory, &dealloc_fn, &mut store, offset) - .map_err(Error::Decode) + let validation_res = + codec::decode_with_length_prefix_from_memory(&memory, &dealloc_fn, &mut store, offset) + .map_err(Error::Decode)?; + + let mut state = store.into_data(); + let executed_queries = state.take_executed_queries(); + forget_all_executed_queries(state.kind.wsv().query_handle(), executed_queries)?; + Ok(validation_res) } } -#[allow(clippy::needless_pass_by_value)] -impl Runtime { +impl Runtime> +where + state::CommonState: state::ValidateQueryOperation, +{ fn default_execute_query( query_request: SmartContractQueryRequest, - state: &S, + state: &mut state::CommonState, ) -> Result, ValidationFail> { iroha_logger::debug!(%query_request, "Executing"); - let wsv = state.wsv(); - match query_request.0 { QueryRequest::Query(QueryWithParameters { query, sorting, pagination, }) => { - wsv.executor() - .validate_query(wsv, state.authority(), query.clone())?; - let output = query.execute(wsv)?; - - wsv.query_handle().handle_query_output( - output, - NonZeroUsize::new(30_000).expect("30 000 is not zero"), - &sorting, - pagination, - ) + let batched = { + let wsv = &state.kind.wsv(); + state.validate_query(&state.authority, query.clone())?; + let output = query.execute(wsv)?; + + wsv.query_handle().handle_query_output( + output, + NonZeroUsize::new(30_000).expect("30 000 is not zero"), + &sorting, + pagination, + ) + }?; + match &batched { + BatchedResponse::V1(batched) => { + if let Some(query_id) = &batched.cursor.query_id { + state.executed_queries.insert(query_id.clone()); + } + } + } + Ok(batched) + } + QueryRequest::Cursor(cursor) => { + // In a normal situation we already have this `query_id` stored, + // so that's a protection from malicious smart contract + if let Some(query_id) = &cursor.query_id { + state.executed_queries.insert(query_id.clone()); + } + state.kind.wsv().query_handle().handle_query_cursor(cursor) } - QueryRequest::Cursor(cursor) => wsv.query_handle().handle_query_cursor(cursor), } .map_err(Into::into) } +} +impl<'wrld, K> Runtime>> { fn default_execute_instruction( instruction: InstructionExpr, - state: &mut S, + state: &mut state::CommonState>, ) -> Result<(), ValidationFail> { debug!(%instruction, "Executing"); @@ -810,8 +817,8 @@ impl Runtime { // There should be two steps validation and execution. First smart contract // is validated and then it's executed. Here it's validating in both steps. // Add a flag indicating whether smart contract is being validated or executed - let authority = state.authority().clone(); - let wsv = state.wsv_mut(); + let authority = state.authority.clone(); + let wsv: &mut WorldStateView = state.kind.wsv; wsv.executor() .clone() // Cloning executor is a cheap operation .validate_instruction(wsv, &authority, instruction) @@ -833,10 +840,12 @@ impl<'wrld> Runtime> { bytes: impl AsRef<[u8]>, ) -> Result<()> { let span = wasm_log_span!("Smart contract execution", %authority); - let state = state::SmartContract { - common: state::Common::new(wsv, authority, self.config, span), - limits_executor: None, - }; + let state = state::SmartContract::new( + authority, + self.config, + span, + state::kind::WithMutWsv::new(wsv, state::kind::sub_kind::SmartContract::new(None)), + ); self.execute_smart_contract_with_state(bytes, state) } @@ -856,10 +865,17 @@ impl<'wrld> Runtime> { max_instruction_count: u64, ) -> Result<()> { let span = wasm_log_span!("Smart contract validation", %authority); - let state = state::SmartContract { - common: state::Common::new(wsv, authority, self.config, span), - limits_executor: Some(LimitsExecutor::new(max_instruction_count)), - }; + let state = state::SmartContract::new( + authority, + self.config, + span, + state::kind::WithMutWsv::new( + wsv, + state::kind::sub_kind::SmartContract::new(Some(LimitsExecutor::new( + max_instruction_count, + ))), + ), + ); self.execute_smart_contract_with_state(bytes, state) } @@ -877,26 +893,28 @@ impl<'wrld> Runtime> { // NOTE: This function takes ownership of the pointer main_fn - .call(store, ()) - .map_err(ExportFnCallError::from) - .map_err(Into::into) + .call(&mut store, ()) + .map_err(ExportFnCallError::from)?; + let mut state = store.into_data(); + let executed_queries = state.take_executed_queries(); + forget_all_executed_queries(state.kind.wsv.query_handle(), executed_queries) } #[codec::wrap] fn get_smart_contract_payload(state: &state::SmartContract) -> payloads::SmartContract { payloads::SmartContract { - owner: state.authority().clone(), + owner: state.authority.clone(), } } } -impl<'wrld> import_traits::ExecuteOperations> +impl<'wrld> import::traits::ExecuteOperations> for Runtime> { #[codec::wrap] fn execute_query( query_request: SmartContractQueryRequest, - state: &state::SmartContract<'wrld>, + state: &mut state::SmartContract<'wrld>, ) -> Result, ValidationFail> { Self::default_execute_query(query_request, state) } @@ -906,7 +924,7 @@ impl<'wrld> import_traits::ExecuteOperations> instruction: InstructionExpr, state: &mut state::SmartContract<'wrld>, ) -> Result<(), ValidationFail> { - if let Some(limits_executor) = state.limits_executor.as_mut() { + if let Some(limits_executor) = state.kind.sub_kind.limits_executor.as_mut() { limits_executor.check_instruction_limits()?; } @@ -930,10 +948,12 @@ impl<'wrld> Runtime> { event: Event, ) -> Result<()> { let span = wasm_log_span!("Trigger execution", %id, %authority); - let state = state::Trigger { - common: state::Common::new(wsv, authority, self.config, span), - triggering_event: event, - }; + let state = state::Trigger::new( + authority, + self.config, + span, + state::kind::WithMutWsv::new(wsv, state::kind::sub_kind::Trigger::new(event)), + ); let mut store = self.create_store(state); let instance = self.instantiate_module(module, &mut store)?; @@ -942,27 +962,30 @@ impl<'wrld> Runtime> { // NOTE: This function takes ownership of the pointer main_fn - .call(store, ()) - .map_err(ExportFnCallError::from) - .map_err(Into::into) + .call(&mut store, ()) + .map_err(ExportFnCallError::from)?; + + let mut state = store.into_data(); + let executed_queries = state.take_executed_queries(); + forget_all_executed_queries(state.kind.wsv.query_handle(), executed_queries) } #[codec::wrap] fn get_trigger_payload(state: &state::Trigger) -> payloads::Trigger { payloads::Trigger { - owner: state.authority().clone(), - event: state.triggering_event.clone(), + owner: state.authority.clone(), + event: state.kind.sub_kind.triggering_event.clone(), } } } -impl<'wrld> import_traits::ExecuteOperations> +impl<'wrld> import::traits::ExecuteOperations> for Runtime> { #[codec::wrap] fn execute_query( query_request: SmartContractQueryRequest, - state: &state::Trigger<'wrld>, + state: &mut state::Trigger<'wrld>, ) -> Result, ValidationFail> { Self::default_execute_query(query_request, state) } @@ -982,49 +1005,31 @@ impl<'wrld> import_traits::ExecuteOperations> /// *Mut* means that [`WorldStateView`] will be mutated. trait ExecuteOperationsAsExecutorMut {} -impl import_traits::ExecuteOperations for R +impl<'wrld, R, K> + import::traits::ExecuteOperations>> for R where - R: ExecuteOperationsAsExecutorMut, - S: state::Wsv + state::WsvMut + state::Authority, + R: ExecuteOperationsAsExecutorMut>>, + state::CommonState>: state::ValidateQueryOperation, { #[codec::wrap] fn execute_query( query_request: SmartContractQueryRequest, - state: &S, + state: &mut state::CommonState>, ) -> Result, ValidationFail> { debug!(%query_request, "Executing as executor"); - let wsv = state.wsv(); - - match query_request.0 { - QueryRequest::Query(QueryWithParameters { - query, - sorting, - pagination, - }) => { - let output = query.execute(wsv)?; - - wsv.query_handle().handle_query_output( - output, - NonZeroUsize::new(30_000).expect("30 000 is not zero"), - &sorting, - pagination, - ) - } - QueryRequest::Cursor(cursor) => wsv.query_handle().handle_query_cursor(cursor), - } - .map_err(Into::into) + Runtime::default_execute_query(query_request, state) } #[codec::wrap] fn execute_instruction( instruction: InstructionExpr, - state: &mut S, + state: &mut state::CommonState>, ) -> Result<(), ValidationFail> { debug!(%instruction, "Executing as executor"); instruction - .execute(&state.authority().clone(), state.wsv_mut()) + .execute(&state.authority.clone(), state.kind.wsv) .map_err(Into::into) } } @@ -1038,7 +1043,7 @@ trait FakeSetPermissionTokenSchema { const ENTRYPOINT_FN_NAME: &'static str; } -impl import_traits::SetPermissionTokenSchema for R +impl import::traits::SetPermissionTokenSchema for R where R: FakeSetPermissionTokenSchema, { @@ -1071,10 +1076,15 @@ impl<'wrld> Runtime> { self.execute_executor_validate_internal( module, - state::executor::ValidateTransaction { - common: state::Common::new(wsv, authority.clone(), self.config, span), - to_validate: transaction, - }, + state::executor::ValidateTransaction::new( + authority.clone(), + self.config, + span, + state::kind::WithMutWsv::new( + wsv, + state::kind::sub_kind::executor::ValidateTransaction::new(transaction), + ), + ), import::EXECUTOR_VALIDATE_TRANSACTION, ) } @@ -1085,7 +1095,7 @@ impl<'wrld> ExecuteOperationsAsExecutorMut import_traits::GetExecutorPayloads> +impl<'wrld> import::traits::GetExecutorPayloads> for Runtime> { #[codec::wrap] @@ -1100,9 +1110,9 @@ impl<'wrld> import_traits::GetExecutorPayloads, ) -> Validate { Validate { - authority: state.authority().clone(), - block_height: state.wsv().height(), - to_validate: state.to_validate.clone(), + authority: state.authority.clone(), + block_height: state.kind.wsv.height(), + to_validate: state.kind.sub_kind.to_validate.clone(), } } @@ -1147,10 +1157,15 @@ impl<'wrld> Runtime> { self.execute_executor_validate_internal( module, - state::executor::ValidateInstruction { - common: state::Common::new(wsv, authority.clone(), self.config, span), - to_validate: instruction, - }, + state::executor::ValidateInstruction::new( + authority.clone(), + self.config, + span, + state::kind::WithMutWsv::new( + wsv, + state::kind::sub_kind::executor::ValidateInstruction::new(instruction), + ), + ), import::EXECUTOR_VALIDATE_INSTRUCTION, ) } @@ -1161,7 +1176,7 @@ impl<'wrld> ExecuteOperationsAsExecutorMut import_traits::GetExecutorPayloads> +impl<'wrld> import::traits::GetExecutorPayloads> for Runtime> { #[codec::wrap] @@ -1183,9 +1198,9 @@ impl<'wrld> import_traits::GetExecutorPayloads, ) -> Validate { Validate { - authority: state.authority().clone(), - block_height: state.wsv().height(), - to_validate: state.to_validate.clone(), + authority: state.authority.clone(), + block_height: state.kind.wsv.height(), + to_validate: state.kind.sub_kind.to_validate.clone(), } } @@ -1223,48 +1238,31 @@ impl<'wrld> Runtime> { self.execute_executor_validate_internal( module, - state::executor::ValidateQuery { - wsv, - authority: authority.clone(), - store_limits: state::store_limits_from_config(&self.config), - log_span: span, - query, - }, + state::executor::ValidateQuery::new( + authority.clone(), + self.config, + span, + state::kind::WithWsv::new( + wsv, + state::kind::sub_kind::executor::ValidateQuery::new(query), + ), + ), import::EXECUTOR_VALIDATE_QUERY, ) } } -impl<'wrld> import_traits::ExecuteOperations> +impl<'wrld> import::traits::ExecuteOperations> for Runtime> { #[codec::wrap] fn execute_query( query_request: SmartContractQueryRequest, - state: &state::executor::ValidateQuery<'wrld>, + state: &mut state::executor::ValidateQuery<'wrld>, ) -> Result, ValidationFail> { debug!(%query_request, "Executing as executor"); - let wsv = state.wsv(); - - match query_request.0 { - QueryRequest::Query(QueryWithParameters { - query, - sorting, - pagination, - }) => { - let output = query.execute(wsv)?; - - wsv.query_handle().handle_query_output( - output, - NonZeroUsize::new(30_000).expect("30 000 is not zero"), - &sorting, - pagination, - ) - } - QueryRequest::Cursor(cursor) => wsv.query_handle().handle_query_cursor(cursor), - } - .map_err(Into::into) + Runtime::default_execute_query(query_request, state) } #[codec::wrap] @@ -1276,7 +1274,7 @@ impl<'wrld> import_traits::ExecuteOperations import_traits::GetExecutorPayloads> +impl<'wrld> import::traits::GetExecutorPayloads> for Runtime> { #[codec::wrap] @@ -1303,9 +1301,9 @@ impl<'wrld> import_traits::GetExecutorPayloads, ) -> Validate { Validate { - authority: state.authority().clone(), - block_height: state.wsv().height(), - to_validate: state.query.clone(), + authority: state.authority.clone(), + block_height: state.kind.wsv.height(), + to_validate: state.kind.sub_kind.to_validate.clone(), } } } @@ -1332,12 +1330,12 @@ impl<'wrld> Runtime> { module: &wasmtime::Module, ) -> Result { let span = wasm_log_span!("Running migration"); - let state = state::executor::Migrate(state::Common::new( - wsv, + let state = state::executor::Migrate::new( authority.clone(), self.config, span, - )); + state::kind::WithMutWsv::new(wsv, state::kind::sub_kind::executor::Migrate), + ); let mut store = self.create_store(state); let instance = self.instantiate_module(module, &mut store)?; @@ -1372,13 +1370,13 @@ impl<'wrld> ExecuteOperationsAsExecutorMut> /// /// Panics with error message if called, because it should never be called from /// `migrate()` entrypoint. -impl<'wrld> import_traits::GetExecutorPayloads> +impl<'wrld> import::traits::GetExecutorPayloads> for Runtime> { #[codec::wrap] fn get_migrate_payload(state: &state::executor::Migrate<'wrld>) -> payloads::Migrate { payloads::Migrate { - block_height: state.wsv().height(), + block_height: state.kind.wsv.height(), } } @@ -1402,7 +1400,7 @@ impl<'wrld> import_traits::GetExecutorPayloads> } } -impl<'wrld> import_traits::SetPermissionTokenSchema> +impl<'wrld> import::traits::SetPermissionTokenSchema> for Runtime> { #[codec::wrap] @@ -1412,7 +1410,7 @@ impl<'wrld> import_traits::SetPermissionTokenSchema, + pub query_id: Option, /// Pointer to the next element in the result set pub cursor: Option, } impl ForwardCursor { /// Create a new cursor. - pub const fn new(query_id: Option, cursor: Option) -> Self { + pub const fn new(query_id: Option, cursor: Option) -> Self { Self { query_id, cursor } } } @@ -50,7 +51,7 @@ mod candidate { #[derive(Decode, Deserialize)] struct ForwardCursorCandidate { - query_id: Option, + query_id: Option, cursor: Option, } @@ -92,7 +93,7 @@ mod candidate { } } -impl From for Vec<(&'static str, String)> { +impl From for Vec<(&'static str, QueryId)> { fn from(cursor: ForwardCursor) -> Self { match (cursor.query_id, cursor.cursor) { (Some(query_id), Some(cursor)) => { diff --git a/data_model/src/query/mod.rs b/data_model/src/query/mod.rs index f9e8d266511..536f66f2c09 100644 --- a/data_model/src/query/mod.rs +++ b/data_model/src/query/mod.rs @@ -58,6 +58,9 @@ macro_rules! queries { }; } +/// Unique id of a query. +pub type QueryId = String; + /// Trait for typesafe query output pub trait Query: Into + seal::Sealed { /// Output type of query @@ -1568,6 +1571,6 @@ pub mod prelude { pub use super::{ account::prelude::*, asset::prelude::*, block::prelude::*, domain::prelude::*, peer::prelude::*, permission::prelude::*, role::prelude::*, transaction::*, - trigger::prelude::*, QueryBox, TransactionQueryOutput, + trigger::prelude::*, QueryBox, QueryId, TransactionQueryOutput, }; }