diff --git a/client/src/client.rs b/client/src/client.rs index c57fa73a3d9..bfee9800b76 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -14,6 +14,7 @@ use eyre::{eyre, Result, WrapErr}; use futures_util::StreamExt; use http_default::{AsyncWebSocketStream, WebSocketStream}; pub use iroha_config::client_api::ConfigDTO; +use iroha_data_model::query::QueryOutputBox; use iroha_logger::prelude::*; use iroha_telemetry::metrics::Status; use iroha_torii_const::uri as torii_uri; @@ -29,9 +30,8 @@ use crate::{ data_model::{ block::SignedBlock, isi::Instruction, - predicate::PredicateBox, prelude::*, - query::{Pagination, Query, Sorting}, + query::{predicate::PredicateBox, Pagination, Query, Sorting}, BatchedResponse, ChainId, ValidationFail, }, http::{Method as HttpMethod, RequestBuilder, Response, StatusCode}, @@ -81,13 +81,13 @@ impl Sign for SignedTransaction { impl QueryResponseHandler where - >::Error: Into, + >::Error: Into, { fn handle(&mut self, resp: &Response>) -> QueryResult { // Separate-compilation friendly response handling fn _handle_query_response_base( resp: &Response>, - ) -> QueryResult> { + ) -> QueryResult> { match resp.status() { StatusCode::OK => { let res = BatchedResponse::decode_all_versioned(resp.body()); @@ -127,12 +127,12 @@ where let (batch, cursor) = _handle_query_response_base(resp)?.into(); - let value = R::try_from(batch) + let output = R::try_from(batch) .map_err(Into::into) .wrap_err("Unexpected type")?; self.query_request.request = crate::data_model::query::QueryRequest::Cursor(cursor); - Ok(value) + Ok(output) } } @@ -215,12 +215,12 @@ impl From for eyre::Report { } /// Output of a query -pub trait QueryOutput: Into + TryFrom { +pub trait QueryOutput: Into + TryFrom { /// Type of the query output type Target: Clone; /// Construct query output from query response - fn new(value: Self, query_request: QueryResponseHandler) -> Self::Target; + fn new(output: Self, query_request: QueryResponseHandler) -> Self::Target; } /// Iterable query output @@ -244,7 +244,7 @@ impl ResultSet { impl Iterator for ResultSet where Vec: QueryOutput, - as TryFrom>::Error: Into, + as TryFrom>::Error: Into, { type Item = QueryResult; @@ -268,11 +268,11 @@ where Err(err) => return Some(Err(ClientQueryError::Other(err))), Ok(ok) => ok, }; - let value = match self.query_handler.handle(&response) { + let output = match self.query_handler.handle(&response) { Err(err) => return Some(Err(err)), Ok(ok) => ok, }; - self.iter = value; + self.iter = output; self.client_cursor = 0; } @@ -284,14 +284,14 @@ where impl QueryOutput for Vec where - Self: Into + TryFrom, + Self: Into + TryFrom, { type Target = ResultSet; - fn new(value: Self, query_handler: QueryResponseHandler) -> Self::Target { + fn new(output: Self, query_handler: QueryResponseHandler) -> Self::Target { ResultSet { query_handler, - iter: value, + iter: output, client_cursor: 0, } } @@ -302,22 +302,20 @@ macro_rules! impl_query_output { impl QueryOutput for $ident { type Target = Self; - fn new(value: Self, _query_handler: QueryResponseHandler) -> Self::Target { - value + fn new(output: Self, _query_handler: QueryResponseHandler) -> Self::Target { + output } } )+ }; } impl_query_output! { - bool, - crate::data_model::Value, crate::data_model::role::Role, crate::data_model::asset::Asset, crate::data_model::asset::AssetDefinition, crate::data_model::account::Account, crate::data_model::domain::Domain, crate::data_model::block::BlockHeader, - crate::data_model::query::MetadataValue, + crate::data_model::metadata::MetadataValueBox, crate::data_model::query::TransactionQueryOutput, crate::data_model::permission::PermissionTokenSchema, crate::data_model::trigger::Trigger, @@ -791,7 +789,7 @@ impl Client { fetch_size: FetchSize, ) -> (DefaultRequestBuilder, QueryResponseHandler) where - >::Error: Into, + >::Error: Into, { let query_builder = QueryBuilder::new(request, self.account_id.clone()).with_filter(filter); let request = self.sign_query(query_builder).encode_versioned(); @@ -826,15 +824,15 @@ impl Client { ) -> QueryResult<::Target> where R::Output: QueryOutput, - >::Error: Into, + >::Error: Into, { iroha_logger::trace!(?request, %pagination, ?sorting, ?filter); let (req, mut resp_handler) = self.prepare_query_request::(request, filter, pagination, sorting, fetch_size); let response = req.build()?.send()?; - let value = resp_handler.handle(&response)?; - let output = QueryOutput::new(value, resp_handler); + let output = resp_handler.handle(&response)?; + let output = QueryOutput::new(output, resp_handler); Ok(output) } @@ -847,7 +845,7 @@ impl Client { where R: Query + Debug, R::Output: QueryOutput, - >::Error: Into, + >::Error: Into, { self.build_query(request).execute() } @@ -865,7 +863,7 @@ impl Client { ) -> QueryResult where O: QueryOutput, - >::Error: Into, + >::Error: Into, { let request = QueryRequest { torii_url: self.torii_url.clone(), @@ -875,8 +873,8 @@ impl Client { let response = request.clone().assemble().build()?.send()?; let mut resp_handler = QueryResponseHandler::::new(request); - let value = resp_handler.handle(&response)?; - let output = O::new(value, resp_handler); + let output = resp_handler.handle(&response)?; + let output = O::new(output, resp_handler); Ok(output) } @@ -890,7 +888,7 @@ impl Client { where R: Query + Debug, R::Output: QueryOutput, - >::Error: Into, + >::Error: Into, { QueryRequestBuilder::new(self, request) } diff --git a/client/src/query_builder.rs b/client/src/query_builder.rs index e12b09f6769..4ccfe7c99db 100644 --- a/client/src/query_builder.rs +++ b/client/src/query_builder.rs @@ -1,12 +1,10 @@ use std::fmt::Debug; +use iroha_data_model::query::QueryOutputBox; + use crate::{ client::{Client, QueryOutput, QueryResult}, - data_model::{ - predicate::PredicateBox, - query::{sorting::Sorting, FetchSize, Pagination, Query}, - Value, - }, + data_model::query::{predicate::PredicateBox, sorting::Sorting, FetchSize, Pagination, Query}, }; pub struct QueryRequestBuilder<'a, R> { @@ -22,7 +20,7 @@ impl<'a, R> QueryRequestBuilder<'a, R> where R: Query + Debug, R::Output: QueryOutput, - >::Error: Into, + >::Error: Into, { pub(crate) fn new(client: &'a Client, request: R) -> Self { Self { diff --git a/client/tests/integration/burn_public_keys.rs b/client/tests/integration/burn_public_keys.rs index d86348da2d2..f99cb7bb933 100644 --- a/client/tests/integration/burn_public_keys.rs +++ b/client/tests/integration/burn_public_keys.rs @@ -3,6 +3,7 @@ use iroha_client::{ crypto::{HashOf, KeyPair, PublicKey}, data_model::{isi::Instruction, prelude::*}, }; +use iroha_data_model::query::TransactionQueryOutput; use test_network::*; fn submit( @@ -27,11 +28,8 @@ fn submit( (tx.hash(), client.submit_transaction_blocking(&tx)) } -fn get(client: &Client, hash: HashOf) -> TransactionValue { - *client - .request(transaction::by_hash(hash)) - .unwrap() - .transaction +fn get(client: &Client, hash: HashOf) -> TransactionQueryOutput { + client.request(transaction::by_hash(hash)).unwrap() } fn account_keys_count(client: &Client, account_id: AccountId) -> usize { @@ -95,7 +93,7 @@ fn public_keys_cannot_be_burned_to_nothing() { let committed_txn = get(&client, tx_hash); keys_count = charlie_keys_count(&client); assert_eq!(keys_count, 1); - assert!(committed_txn.error.is_none()); + assert!(committed_txn.as_ref().error.is_none()); let burn_the_last_key = burn(charlie_initial_keypair.public_key().clone()); @@ -108,5 +106,5 @@ fn public_keys_cannot_be_burned_to_nothing() { let committed_txn = get(&client, tx_hash); keys_count = charlie_keys_count(&client); assert_eq!(keys_count, 1); - assert!(committed_txn.error.is_some()); + assert!(committed_txn.as_ref().error.is_some()); } diff --git a/client/tests/integration/queries/asset.rs b/client/tests/integration/queries/asset.rs index c13c4b7b356..4a8c565fda6 100644 --- a/client/tests/integration/queries/asset.rs +++ b/client/tests/integration/queries/asset.rs @@ -146,7 +146,6 @@ fn test_total_quantity( ) -> Result<()> where T: Copy + Into, - Value: From, Mint: Instruction, Burn: Instruction, { diff --git a/client/tests/integration/queries/mod.rs b/client/tests/integration/queries/mod.rs index d4ed9a45cd6..4d85add4e5b 100644 --- a/client/tests/integration/queries/mod.rs +++ b/client/tests/integration/queries/mod.rs @@ -1,6 +1,6 @@ use std::str::FromStr as _; -use eyre::{bail, Result}; +use eyre::Result; use iroha_client::{ client::{self, ClientQueryError}, data_model::{ @@ -57,9 +57,7 @@ fn live_query_is_dropped_after_smart_contract_end() -> Result<()> { client.account_id.clone(), Name::from_str("cursor").unwrap(), ))?; - let Value::String(cursor) = metadata_value.0 else { - bail!("Expected `Value::String`, got {:?}", metadata_value.0); - }; + let cursor: String = metadata_value.try_into()?; let asset_cursor = serde_json::from_str::(&cursor)?; let err = client diff --git a/client/tests/integration/roles.rs b/client/tests/integration/roles.rs index a0a33516473..ddf03cc144d 100644 --- a/client/tests/integration/roles.rs +++ b/client/tests/integration/roles.rs @@ -89,7 +89,7 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { let set_key_value = SetKeyValue::account( mouse_id, Name::from_str("key").expect("Valid"), - Value::String("value".to_owned()), + "value".to_owned(), ); test_client.submit_blocking(set_key_value)?; diff --git a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs index 2ec5094f4d1..4b009d7f592 100644 --- a/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs +++ b/client/tests/integration/smartcontracts/query_assets_and_save_cursor/src/lib.rs @@ -10,7 +10,7 @@ extern crate alloc; use alloc::string::ToString as _; use core::num::NonZeroU32; -use iroha_smart_contract::{parse, prelude::*}; +use iroha_smart_contract::{data_model::metadata::MetadataValue, parse, prelude::*}; use lol_alloc::{FreeListAllocator, LockedAllocator}; #[global_allocator] @@ -31,7 +31,7 @@ fn main(owner: AccountId) { SetKeyValue::account( owner, parse!("cursor" as Name), - Value::String( + MetadataValue::String( serde_json::to_value(cursor) .dbg_expect("Failed to convert cursor to JSON") .to_string(), diff --git a/client/tests/integration/sorting.rs b/client/tests/integration/sorting.rs index 5483fcfbdca..f444db9146c 100644 --- a/client/tests/integration/sorting.rs +++ b/client/tests/integration/sorting.rs @@ -9,9 +9,11 @@ use iroha_client::{ client::{self, QueryResult}, data_model::{ account::Account, - predicate::{string, value, PredicateBox}, prelude::*, - query::{Pagination, Sorting}, + query::{ + predicate::{string, value, PredicateBox}, + Pagination, Sorting, + }, }, }; use iroha_data_model::isi::InstructionBox; @@ -30,17 +32,13 @@ fn correct_pagination_assets_after_creating_new_one() { let mut assets = vec![]; let mut instructions = vec![]; - for i in 0..20_u128 { + for i in 0..20_u32 { let asset_definition_id = AssetDefinitionId::from_str(&format!("xor{i}#wonderland")).expect("Valid"); let asset_definition = AssetDefinition::store(asset_definition_id.clone()); let mut asset_metadata = Metadata::new(); asset_metadata - .insert_with_limits( - sort_by_metadata_key.clone(), - i.to_value(), - MetadataLimits::new(10, 23), - ) + .insert_with_limits(sort_by_metadata_key.clone(), i, MetadataLimits::new(10, 23)) .expect("Valid"); let asset = Asset::new( AssetId::new(asset_definition_id, account_id.clone()), @@ -89,7 +87,7 @@ fn correct_pagination_assets_after_creating_new_one() { new_asset_metadata .insert_with_limits( sort_by_metadata_key, - 20_u128.to_value(), + numeric!(20), MetadataLimits::new(10, 23), ) .expect("Valid"); @@ -140,7 +138,7 @@ fn correct_sorting_of_entities() { let mut asset_definitions = vec![]; let mut metadata_of_assets = vec![]; let mut instructions = vec![]; - let n = 10u128; + let n = 10_u32; for i in 0..n { let asset_definition_id = AssetDefinitionId::from_str(&format!("xor_{i}#wonderland")).expect("Valid"); @@ -148,7 +146,7 @@ fn correct_sorting_of_entities() { asset_metadata .insert_with_limits( sort_by_metadata_key.clone(), - (n - i - 1).to_value(), + n - i - 1, MetadataLimits::new(10, 28), ) .expect("Valid"); @@ -169,9 +167,9 @@ fn correct_sorting_of_entities() { let res = test_client .build_query(client::asset::all_definitions()) .with_sorting(Sorting::by_metadata_key(sort_by_metadata_key.clone())) - .with_filter(PredicateBox::new(value::ValuePredicate::Identifiable( - string::StringPredicate::starts_with("xor_"), - ))) + .with_filter(PredicateBox::new( + value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with("xor_")), + )) .execute() .expect("Valid") .collect::>>() @@ -199,7 +197,7 @@ fn correct_sorting_of_entities() { account_metadata .insert_with_limits( sort_by_metadata_key.clone(), - (n - i - 1).to_value(), + n - i - 1, MetadataLimits::new(10, 28), ) .expect("Valid"); @@ -220,9 +218,11 @@ fn correct_sorting_of_entities() { let res = test_client .build_query(client::account::all()) .with_sorting(Sorting::by_metadata_key(sort_by_metadata_key.clone())) - .with_filter(PredicateBox::new(value::ValuePredicate::Identifiable( - string::StringPredicate::starts_with("charlie"), - ))) + .with_filter(PredicateBox::new( + value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with( + "charlie", + )), + )) .execute() .expect("Valid") .collect::>>() @@ -246,7 +246,7 @@ fn correct_sorting_of_entities() { domain_metadata .insert_with_limits( sort_by_metadata_key.clone(), - (n - i - 1).to_value(), + n - i - 1, MetadataLimits::new(10, 28), ) .expect("Valid"); @@ -266,9 +266,11 @@ fn correct_sorting_of_entities() { let res = test_client .build_query(client::domain::all()) .with_sorting(Sorting::by_metadata_key(sort_by_metadata_key.clone())) - .with_filter(PredicateBox::new(value::ValuePredicate::Identifiable( - string::StringPredicate::starts_with("neverland"), - ))) + .with_filter(PredicateBox::new( + value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with( + "neverland", + )), + )) .execute() .expect("Valid") .collect::>>() @@ -281,7 +283,7 @@ fn correct_sorting_of_entities() { .eq(metadata_of_domains.iter().rev())); // Naive test sorting of domains - let input = [(0i32, 1u128), (2, 0), (1, 2)]; + let input = [(0_i32, 1_u32), (2, 0), (1, 2)]; let mut domains = vec![]; let mut metadata_of_domains = vec![]; let mut instructions = vec![]; @@ -291,7 +293,7 @@ fn correct_sorting_of_entities() { domain_metadata .insert_with_limits( sort_by_metadata_key.clone(), - val.to_value(), + val, MetadataLimits::new(10, 28), ) .expect("Valid"); @@ -307,7 +309,7 @@ fn correct_sorting_of_entities() { .submit_all_blocking(instructions) .expect("Valid"); - let filter = PredicateBox::new(value::ValuePredicate::Identifiable( + let filter = PredicateBox::new(value::QueryOutputPredicate::Identifiable( string::StringPredicate::starts_with("neverland_"), )); let res = test_client @@ -353,7 +355,7 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { account_metadata .insert_with_limits( sort_by_metadata_key.clone(), - (n - i - 1).to_value(), + n - i - 1, MetadataLimits::new(10, 28), ) .expect("Valid"); @@ -374,9 +376,11 @@ fn sort_only_elements_which_have_sorting_key() -> Result<()> { let res = test_client .build_query(client::account::all()) .with_sorting(Sorting::by_metadata_key(sort_by_metadata_key)) - .with_filter(PredicateBox::new(value::ValuePredicate::Identifiable( - string::StringPredicate::starts_with("charlie"), - ))) + .with_filter(PredicateBox::new( + value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with( + "charlie", + )), + )) .execute() .wrap_err("Failed to submit request")? .collect::>>()?; diff --git a/client/tests/integration/transfer_asset.rs b/client/tests/integration/transfer_asset.rs index f277d6731a7..d13445c9ea3 100644 --- a/client/tests/integration/transfer_asset.rs +++ b/client/tests/integration/transfer_asset.rs @@ -83,7 +83,6 @@ fn simulate_transfer( port_number: u16, ) where T: std::fmt::Debug + Clone + Into, - Value: From, Mint: Instruction, Transfer: Instruction, { diff --git a/client/tests/integration/triggers/by_call_trigger.rs b/client/tests/integration/triggers/by_call_trigger.rs index f32b9a43af5..e00767959a8 100644 --- a/client/tests/integration/triggers/by_call_trigger.rs +++ b/client/tests/integration/triggers/by_call_trigger.rs @@ -24,7 +24,7 @@ fn call_execute_trigger() -> Result<()> { let asset_definition_id = "rose#wonderland".parse()?; let account_id = "alice@wonderland".parse()?; let asset_id = AssetId::new(asset_definition_id, account_id); - let prev_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_value = get_asset_value(&mut test_client, asset_id.clone()); let instruction = Mint::asset_numeric(1u32, asset_id.clone()); let register_trigger = build_register_trigger_isi(asset_id.clone(), vec![instruction.into()]); @@ -34,7 +34,7 @@ fn call_execute_trigger() -> Result<()> { let call_trigger = ExecuteTrigger::new(trigger_id); test_client.submit_blocking(call_trigger)?; - let new_value = get_asset_value(&mut test_client, asset_id)?; + let new_value = get_asset_value(&mut test_client, asset_id); assert_eq!(new_value, prev_value.checked_add(Numeric::ONE).unwrap()); Ok(()) @@ -85,7 +85,7 @@ fn infinite_recursion_should_produce_one_call_per_block() -> Result<()> { let asset_id = AssetId::new(asset_definition_id, account_id); let trigger_id = TriggerId::from_str(TRIGGER_NAME)?; let call_trigger = ExecuteTrigger::new(trigger_id); - let prev_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_value = get_asset_value(&mut test_client, asset_id.clone()); let instructions = vec![ Mint::asset_numeric(1u32, asset_id.clone()).into(), @@ -96,7 +96,7 @@ fn infinite_recursion_should_produce_one_call_per_block() -> Result<()> { test_client.submit_blocking(call_trigger)?; - let new_value = get_asset_value(&mut test_client, asset_id)?; + let new_value = get_asset_value(&mut test_client, asset_id); assert_eq!(new_value, prev_value.checked_add(Numeric::ONE).unwrap()); Ok(()) @@ -145,13 +145,13 @@ fn trigger_failure_should_not_cancel_other_triggers_execution() -> Result<()> { test_client.submit_blocking(register_trigger)?; // Saving current asset value - let prev_asset_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_asset_value = get_asset_value(&mut test_client, asset_id.clone()); // Executing bad trigger test_client.submit_blocking(ExecuteTrigger::new(bad_trigger_id))?; // Checking results - let new_asset_value = get_asset_value(&mut test_client, asset_id)?; + let new_asset_value = get_asset_value(&mut test_client, asset_id); assert_eq!( new_asset_value, prev_asset_value.checked_add(Numeric::ONE).unwrap() @@ -185,7 +185,7 @@ fn trigger_should_not_be_executed_with_zero_repeats_count() -> Result<()> { test_client.submit_blocking(register_trigger)?; // Saving current asset value - let prev_asset_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_asset_value = get_asset_value(&mut test_client, asset_id.clone()); // Executing trigger first time let execute_trigger = ExecuteTrigger::new(trigger_id.clone()); @@ -211,7 +211,7 @@ fn trigger_should_not_be_executed_with_zero_repeats_count() -> Result<()> { )); // Checking results - let new_asset_value = get_asset_value(&mut test_client, asset_id)?; + let new_asset_value = get_asset_value(&mut test_client, asset_id); assert_eq!( new_asset_value, prev_asset_value.checked_add(Numeric::ONE).unwrap() @@ -249,7 +249,7 @@ fn trigger_should_be_able_to_modify_its_own_repeats_count() -> Result<()> { test_client.submit_blocking(register_trigger)?; // Saving current asset value - let prev_asset_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_asset_value = get_asset_value(&mut test_client, asset_id.clone()); // Executing trigger first time let execute_trigger = ExecuteTrigger::new(trigger_id); @@ -259,7 +259,7 @@ fn trigger_should_be_able_to_modify_its_own_repeats_count() -> Result<()> { test_client.submit_blocking(execute_trigger)?; // Checking results - let new_asset_value = get_asset_value(&mut test_client, asset_id)?; + let new_asset_value = get_asset_value(&mut test_client, asset_id); assert_eq!( new_asset_value, prev_asset_value.checked_add(numeric!(2)).unwrap() @@ -378,14 +378,14 @@ fn trigger_in_genesis_using_base64() -> Result<()> { let asset_definition_id = "rose#wonderland".parse()?; let asset_id = AssetId::new(asset_definition_id, account_id); - let prev_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_value = get_asset_value(&mut test_client, asset_id.clone()); // Executing trigger let call_trigger = ExecuteTrigger::new(trigger_id); test_client.submit_blocking(call_trigger)?; // Checking result - let new_value = get_asset_value(&mut test_client, asset_id)?; + let new_value = get_asset_value(&mut test_client, asset_id); assert_eq!(new_value, prev_value.checked_add(Numeric::ONE).unwrap()); Ok(()) @@ -435,7 +435,7 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { test_client.submit_blocking(register_trigger)?; // Saving current asset value - let prev_asset_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_asset_value = get_asset_value(&mut test_client, asset_id.clone()); // Executing triggers let execute_trigger_unregister = ExecuteTrigger::new(trigger_id_unregister); @@ -447,7 +447,7 @@ fn trigger_should_be_able_to_modify_other_trigger() -> Result<()> { // Checking results // First trigger should cancel second one, so value should stay the same - let new_asset_value = get_asset_value(&mut test_client, asset_id)?; + let new_asset_value = get_asset_value(&mut test_client, asset_id); assert_eq!(new_asset_value, prev_asset_value); Ok(()) @@ -542,9 +542,14 @@ fn unregistering_one_of_two_triggers_with_identical_wasm_should_not_cause_origin Ok(()) } -fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Result { - let asset = client.request(client::asset::by_id(asset_id))?; - Ok(*TryAsRef::::try_as_ref(asset.value())?) +fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Numeric { + let asset = client.request(client::asset::by_id(asset_id)).unwrap(); + + let AssetValue::Numeric(val) = *asset.value() else { + panic!("Unexpected asset value"); + }; + + val } fn build_register_trigger_isi( diff --git a/client/tests/integration/triggers/data_trigger.rs b/client/tests/integration/triggers/data_trigger.rs index 26f0e325e8a..32f6ae92037 100644 --- a/client/tests/integration/triggers/data_trigger.rs +++ b/client/tests/integration/triggers/data_trigger.rs @@ -1,5 +1,6 @@ use eyre::Result; use iroha_client::{client, data_model::prelude::*}; +use iroha_data_model::asset::AssetValue; use test_network::*; use crate::integration::new_account_with_random_public_key; @@ -13,7 +14,7 @@ fn must_execute_both_triggers() -> Result<()> { let asset_definition_id = "rose#wonderland".parse()?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let prev_value = get_asset_value(&test_client, asset_id.clone())?; + let prev_value = get_asset_value(&test_client, asset_id.clone()); let instruction = Mint::asset_numeric(1u32, asset_id.clone()); let register_trigger = Register::trigger(Trigger::new( @@ -54,7 +55,7 @@ fn must_execute_both_triggers() -> Result<()> { )))?; test_client.submit_blocking(Register::domain(Domain::new("neverland".parse()?)))?; - let new_value = get_asset_value(&test_client, asset_id)?; + let new_value = get_asset_value(&test_client, asset_id); assert_eq!(new_value, prev_value.checked_add(numeric!(2)).unwrap()); Ok(()) @@ -86,7 +87,7 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu create_sakura_asset, ])?; - let prev_value = get_asset_value(&test_client, asset_id.clone())?; + let prev_value = get_asset_value(&test_client, asset_id.clone()); let register_trigger = Register::trigger(Trigger::new( "mint_sakura$neverland".parse()?, @@ -116,13 +117,18 @@ fn domain_scoped_trigger_must_be_executed_only_on_events_in_its_domain() -> Resu "asahi@neverland".parse()?, )))?; - let new_value = get_asset_value(&test_client, asset_id)?; + let new_value = get_asset_value(&test_client, asset_id); assert_eq!(new_value, prev_value.checked_add(Numeric::ONE).unwrap()); Ok(()) } -fn get_asset_value(client: &client::Client, asset_id: AssetId) -> Result { - let asset = client.request(client::asset::by_id(asset_id))?; - Ok(*TryAsRef::::try_as_ref(asset.value())?) +fn get_asset_value(client: &client::Client, asset_id: AssetId) -> Numeric { + let asset = client.request(client::asset::by_id(asset_id)).unwrap(); + + let AssetValue::Numeric(val) = *asset.value() else { + panic!("Expected u32 asset value") + }; + + val } diff --git a/client/tests/integration/triggers/event_trigger.rs b/client/tests/integration/triggers/event_trigger.rs index 89ed6b48ca5..c6250fdfe3e 100644 --- a/client/tests/integration/triggers/event_trigger.rs +++ b/client/tests/integration/triggers/event_trigger.rs @@ -15,7 +15,7 @@ fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { let asset_definition_id = "rose#wonderland".parse()?; let account_id = AccountId::from_str("alice@wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let prev_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_value = get_asset_value(&mut test_client, asset_id.clone()); let instruction = Mint::asset_numeric(1u32, asset_id.clone()); let register_trigger = Register::trigger(Trigger::new( @@ -45,13 +45,18 @@ fn test_mint_asset_when_new_asset_definition_created() -> Result<()> { Register::asset_definition(AssetDefinition::numeric(tea_definition_id)); test_client.submit_blocking(register_tea_definition)?; - let new_value = get_asset_value(&mut test_client, asset_id)?; + let new_value = get_asset_value(&mut test_client, asset_id); assert_eq!(new_value, prev_value.checked_add(Numeric::ONE).unwrap()); Ok(()) } -fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Result { - let asset = client.request(client::asset::by_id(asset_id))?; - Ok(*TryAsRef::::try_as_ref(asset.value())?) +fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Numeric { + let asset = client.request(client::asset::by_id(asset_id)).unwrap(); + + let AssetValue::Numeric(val) = *asset.value() else { + panic!("Unexpected asset value"); + }; + + val } diff --git a/client/tests/integration/triggers/time_trigger.rs b/client/tests/integration/triggers/time_trigger.rs index d6ec578e73b..819ea3cc5e0 100644 --- a/client/tests/integration/triggers/time_trigger.rs +++ b/client/tests/integration/triggers/time_trigger.rs @@ -42,7 +42,7 @@ fn time_trigger_execution_count_error_should_be_less_than_15_percent() -> Result let asset_definition_id = "rose#wonderland".parse().expect("Valid"); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let prev_value = get_asset_value(&mut test_client, asset_id.clone())?; + let prev_value = get_asset_value(&mut test_client, asset_id.clone()); let schedule = TimeSchedule::starting_at(start_time).with_period(PERIOD); let instruction = Mint::asset_numeric(1u32, asset_id.clone()); @@ -69,7 +69,7 @@ fn time_trigger_execution_count_error_should_be_less_than_15_percent() -> Result let finish_time = current_time(); let average_count = finish_time.saturating_sub(start_time).as_millis() / PERIOD.as_millis(); - let actual_value = get_asset_value(&mut test_client, asset_id)?; + let actual_value = get_asset_value(&mut test_client, asset_id); let expected_value = prev_value .checked_add(Numeric::new(average_count, 0)) .unwrap(); @@ -103,7 +103,7 @@ fn change_asset_metadata_after_1_sec() -> Result<()> { let schedule = TimeSchedule::starting_at(start_time + PERIOD); let instruction = - SetKeyValue::asset_definition(asset_definition_id.clone(), key.clone(), 3_u32.to_value()); + SetKeyValue::asset_definition(asset_definition_id.clone(), key.clone(), 3_u32); let register_trigger = Register::trigger(Trigger::new( "change_rose_metadata".parse().expect("Valid"), Action::new( @@ -122,13 +122,11 @@ fn change_asset_metadata_after_1_sec() -> Result<()> { usize::try_from(PERIOD.as_millis() / DEFAULT_CONSENSUS_ESTIMATION.as_millis() + 1)?, )?; - let value: Value = test_client - .request(FindAssetDefinitionKeyValueByIdAndKey { - id: asset_definition_id, - key, - })? - .into(); - assert_eq!(value, 3u32.to_value()); + let value = test_client.request(FindAssetDefinitionKeyValueByIdAndKey { + id: asset_definition_id, + key, + })?; + assert_eq!(value, numeric!(3_u32).into()); Ok(()) } @@ -144,7 +142,7 @@ fn pre_commit_trigger_should_be_executed() -> Result<()> { let account_id: AccountId = "alice@wonderland".parse().expect("Valid"); let asset_id = AssetId::new(asset_definition_id, account_id.clone()); - let mut prev_value = get_asset_value(&mut test_client, asset_id.clone())?; + let mut prev_value = get_asset_value(&mut test_client, asset_id.clone()); // Start listening BEFORE submitting any transaction not to miss any block committed event let event_listener = get_block_committed_event_listener(&test_client)?; @@ -162,7 +160,7 @@ fn pre_commit_trigger_should_be_executed() -> Result<()> { test_client.submit(register_trigger)?; for _ in event_listener.take(CHECKS_COUNT) { - let new_value = get_asset_value(&mut test_client, asset_id.clone())?; + let new_value = get_asset_value(&mut test_client, asset_id.clone()); assert_eq!(new_value, prev_value.checked_add(Numeric::ONE).unwrap()); prev_value = new_value; @@ -284,9 +282,14 @@ fn get_block_committed_event_listener( } /// Get asset numeric value -fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Result { - let asset = client.request(client::asset::by_id(asset_id))?; - Ok(*TryAsRef::::try_as_ref(asset.value())?) +fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Numeric { + let asset = client.request(client::asset::by_id(asset_id)).unwrap(); + + let AssetValue::Numeric(val) = *asset.value() else { + panic!("Unexpected asset value"); + }; + + val } /// Submit some sample ISIs to create new blocks diff --git a/client/tests/integration/tx_history.rs b/client/tests/integration/tx_history.rs index 3ace4e8806d..bfddd8270c4 100644 --- a/client/tests/integration/tx_history.rs +++ b/client/tests/integration/tx_history.rs @@ -64,11 +64,15 @@ fn client_has_rejected_and_acepted_txs_should_return_tx_history() -> Result<()> assert_eq!(transactions.len(), 50); let mut prev_creation_time = core::time::Duration::from_millis(0); - for tx in &transactions { - assert_eq!(tx.as_ref().authority(), &account_id); - //check sorted - assert!(tx.as_ref().creation_time() >= prev_creation_time); - prev_creation_time = tx.as_ref().creation_time(); - } + transactions + .iter() + .map(AsRef::as_ref) + .map(AsRef::as_ref) + .for_each(|tx| { + assert_eq!(tx.authority(), &account_id); + //check sorted + assert!(tx.creation_time() >= prev_creation_time); + prev_creation_time = tx.creation_time(); + }); Ok(()) } diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index 94250a822dd..c38e2e13025 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -15,9 +15,9 @@ use erased_serde::Serialize; use iroha_client::{ client::{Client, QueryResult}, config::Config, - data_model::prelude::*, + data_model::{metadata::MetadataValueBox, prelude::*}, }; -use iroha_primitives::addr::{Ipv4Addr, Ipv6Addr, SocketAddr}; +use iroha_primitives::addr::SocketAddr; /// Re-usable clap `--metadata ` (`-m`) argument. /// Should be combined with `#[command(flatten)]` attr. @@ -53,33 +53,29 @@ impl MetadataArgs { } } -/// Re-usable clap `--value ` (`-v`) argument. +/// Re-usable clap `--value ` (`-v`) argument. /// Should be combined with `#[command(flatten)]` attr. #[derive(clap::Args, Debug, Clone, PartialEq, Eq)] -pub struct ValueArg { - /// Wrapper around Value to accept possible values and fallback to json. +pub struct MetadataValueArg { + /// Wrapper around MetadataValue to accept possible values and fallback to json. + /// /// The following types are supported: /// Numbers: decimal with optional point /// Booleans: false/true - /// IPv4/IPv6: e.g. 127.0.0.1, ::1 - /// Iroha Public Key Multihash: e.g. ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0 /// JSON: e.g. {"Vec":[{"String":"a"},{"String":"b"}]} #[arg(short, long)] - value: Value, + value: MetadataValueBox, } -impl FromStr for ValueArg { +impl FromStr for MetadataValueArg { type Err = Error; fn from_str(s: &str) -> Result { s.parse::() - .map(Value::Bool) - .or_else(|_| s.parse::().map(Value::Ipv4Addr)) - .or_else(|_| s.parse::().map(Value::Ipv6Addr)) - .or_else(|_| s.parse::().map(Value::Numeric)) - .or_else(|_| s.parse::().map(Value::PublicKey)) - .or_else(|_| serde_json::from_str::(s).map_err(Into::into)) - .map(|value| ValueArg { value }) + .map(MetadataValueBox::Bool) + .or_else(|_| s.parse::().map(MetadataValueBox::Numeric)) + .or_else(|_| serde_json::from_str::(s).map_err(Into::into)) + .map(|value| MetadataValueArg { value }) } } @@ -234,7 +230,7 @@ fn submit( } mod filter { - use iroha_client::data_model::predicate::PredicateBox; + use iroha_client::data_model::query::predicate::PredicateBox; use super::*; @@ -450,7 +446,7 @@ mod domain { #[arg(short, long)] key: Name, #[command(flatten)] - value: ValueArg, + value: MetadataValueArg, } impl RunArgs for Set { @@ -458,7 +454,7 @@ mod domain { let Self { id, key, - value: ValueArg { value }, + value: MetadataValueArg { value }, } = self; let set_key_value = SetKeyValue::domain(id, key, value); submit([set_key_value], UnlimitedMetadata::new(), context) @@ -788,7 +784,7 @@ mod asset { } = self; let mint_asset = iroha_client::data_model::isi::Mint::asset_numeric(quantity, asset_id); submit([mint_asset], metadata.load()?, context) - .wrap_err("Failed to mint asset of type `NumericValue::U32`") + .wrap_err("Failed to mint asset of type `Numeric`") } } @@ -814,7 +810,7 @@ mod asset { } = self; let burn_asset = iroha_client::data_model::isi::Burn::asset_numeric(quantity, asset_id); submit([burn_asset], metadata.load()?, context) - .wrap_err("Failed to burn asset of type `NumericValue::U32`") + .wrap_err("Failed to burn asset of type `Numeric`") } } @@ -905,7 +901,7 @@ mod asset { #[clap(long)] pub key: Name, #[command(flatten)] - pub value: ValueArg, + pub value: MetadataValueArg, } impl RunArgs for SetKeyValue { @@ -913,7 +909,7 @@ mod asset { let Self { asset_id, key, - value: ValueArg { value }, + value: MetadataValueArg { value }, } = self; let set = iroha_client::data_model::isi::SetKeyValue::asset(asset_id, key, value); @@ -1100,58 +1096,35 @@ mod json { mod tests { use std::str::FromStr; - use iroha_client::data_model::Value; - use super::*; #[test] fn parse_value_arg_cases() { macro_rules! case { ($input:expr, $expected:expr) => { - let ValueArg { value } = - ValueArg::from_str($input).expect("should not fail with valid input"); + let MetadataValueArg { value } = + MetadataValueArg::from_str($input).expect("should not fail with valid input"); assert_eq!(value, $expected); }; } - // IPv4 address - case!( - "192.168.0.1", - Value::Ipv4Addr(Ipv4Addr::new([192, 168, 0, 1])) - ); - - // IPv6 address - case!( - "::1", - Value::Ipv6Addr(Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 1])) - ); - // Boolean values - case!("true", Value::Bool(true)); - case!("false", Value::Bool(false)); + case!("true", true.into()); + case!("false", false.into()); // Numeric values - case!("123", Value::Numeric(numeric!(123))); - case!("123.0", Value::Numeric(numeric!(123.0))); - - // Public Key - let public_key_str = - "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"; - case!( - public_key_str, - Value::PublicKey(PublicKey::from_str(public_key_str).unwrap()) - ); + case!("123", numeric!(123).into()); + case!("123.0", numeric!(123.0).into()); // JSON Value let json_str = r#"{"Vec":[{"String":"a"},{"String":"b"}]}"#; - let expected_json: Value = serde_json::from_str(json_str).unwrap(); - case!(json_str, expected_json); + case!(json_str, serde_json::from_str(json_str).unwrap()); } #[test] fn error_parse_invalid_value() { let invalid_str = "not_a_valid_value"; - let _invalid_value = ValueArg::from_str(invalid_str) + let _invalid_value = MetadataValueArg::from_str(invalid_str) .expect_err("Should fail invalid type from string but passed"); } } diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm index b6757881f8e..0221322fdec 100644 Binary files a/configs/swarm/executor.wasm and b/configs/swarm/executor.wasm differ diff --git a/core/src/query/store.rs b/core/src/query/store.rs index 6691ee24a48..d9df7ba4a7b 100644 --- a/core/src/query/store.rs +++ b/core/src/query/store.rs @@ -12,9 +12,9 @@ use iroha_data_model::{ asset::AssetValue, query::{ cursor::ForwardCursor, error::QueryExecutionFail, pagination::Pagination, sorting::Sorting, - FetchSize, QueryId, DEFAULT_FETCH_SIZE, MAX_FETCH_SIZE, + FetchSize, QueryId, QueryOutputBox, DEFAULT_FETCH_SIZE, MAX_FETCH_SIZE, }, - BatchedResponse, BatchedResponseV1, HasMetadata, IdentifiableBox, ValidationFail, Value, + BatchedResponse, BatchedResponseV1, HasMetadata, IdentifiableBox, ValidationFail, }; use iroha_logger::trace; use parity_scale_codec::{Decode, Encode}; @@ -25,7 +25,7 @@ use super::{ cursor::{Batch as _, Batched, UnknownCursor}, pagination::Paginate as _, }; -use crate::smartcontracts::query::LazyValue; +use crate::smartcontracts::query::LazyQueryOutput; /// Query service error. #[derive(Debug, thiserror::Error, Copy, Clone, Serialize, Deserialize, Encode, Decode)] @@ -61,7 +61,7 @@ impl From for ValidationFail { /// Result type for [`LiveQueryStore`] methods. pub type Result = std::result::Result; -type LiveQuery = Batched>; +type LiveQuery = Batched>; /// Service which stores queries which might be non fully consumed by a client. /// @@ -143,8 +143,11 @@ impl LiveQueryStore { } enum Message { - Insert(QueryId, Batched>), - Remove(QueryId, oneshot::Sender>>>), + Insert(QueryId, Batched>), + Remove( + QueryId, + oneshot::Sender>>>, + ), } /// Handle to interact with [`LiveQueryStore`]. @@ -162,18 +165,18 @@ impl LiveQueryStoreHandle { /// - Otherwise throws up query output handling errors. pub fn handle_query_output( &self, - query_output: LazyValue<'_>, + query_output: LazyQueryOutput<'_>, sorting: &Sorting, pagination: Pagination, fetch_size: FetchSize, - ) -> Result> { + ) -> Result> { match query_output { - LazyValue::Value(batch) => { + LazyQueryOutput::QueryOutput(batch) => { let cursor = ForwardCursor::default(); let result = BatchedResponseV1 { batch, cursor }; Ok(result.into()) } - LazyValue::Iter(iter) => { + LazyQueryOutput::Iter(iter) => { let fetch_size = fetch_size.fetch_size.unwrap_or(DEFAULT_FETCH_SIZE); if fetch_size > MAX_FETCH_SIZE { return Err(Error::FetchSizeTooBig); @@ -195,7 +198,10 @@ impl LiveQueryStoreHandle { /// /// - Returns [`Error::ConnectionClosed`] if [`LiveQueryStore`] is dropped, /// - Otherwise throws up query output handling errors. - pub fn handle_query_cursor(&self, cursor: ForwardCursor) -> Result> { + pub fn handle_query_cursor( + &self, + cursor: ForwardCursor, + ) -> Result> { let query_id = cursor.query_id.ok_or(UnknownCursor)?; let live_query = self.remove(query_id.clone())?.ok_or(UnknownCursor)?; @@ -236,8 +242,8 @@ impl LiveQueryStoreHandle { &self, query_id: QueryId, curr_cursor: Option, - mut live_query: Batched>, - ) -> Result> { + mut live_query: Batched>, + ) -> Result> { let (batch, next_cursor) = live_query.next_batch(curr_cursor)?; if !live_query.is_depleted() { @@ -245,7 +251,7 @@ impl LiveQueryStoreHandle { } let query_response = BatchedResponseV1 { - batch: Value::Vec(batch), + batch: QueryOutputBox::Vec(batch), cursor: ForwardCursor { query_id: Some(query_id), cursor: next_cursor, @@ -256,22 +262,25 @@ impl LiveQueryStoreHandle { } fn apply_sorting_and_pagination( - iter: impl Iterator, + iter: impl Iterator, sorting: &Sorting, pagination: Pagination, - ) -> Vec { + ) -> Vec { if let Some(key) = &sorting.sort_by_metadata_key { - let mut pairs: Vec<(Option, Value)> = iter + let mut pairs: Vec<(Option, QueryOutputBox)> = iter .map(|value| { let key = match &value { - Value::Identifiable(IdentifiableBox::Asset(asset)) => match asset.value() { - AssetValue::Store(store) => store.get(key).cloned(), - _ => None, - }, - Value::Identifiable(v) => TryInto::<&dyn HasMetadata>::try_into(v) + QueryOutputBox::Identifiable(IdentifiableBox::Asset(asset)) => { + match asset.value() { + AssetValue::Store(store) => store.get(key).cloned().map(Into::into), + _ => None, + } + } + QueryOutputBox::Identifiable(v) => TryInto::<&dyn HasMetadata>::try_into(v) .ok() .and_then(|has_metadata| has_metadata.metadata().get(key)) - .cloned(), + .cloned() + .map(Into::into), _ => None, }; (key, value) @@ -300,6 +309,8 @@ impl LiveQueryStoreHandle { mod tests { use std::num::NonZeroU32; + use iroha_data_model::metadata::MetadataValueBox; + use super::*; #[test] @@ -315,7 +326,9 @@ mod tests { }; let sorting = Sorting::default(); - let query_output = LazyValue::Iter(Box::new((0..100).map(|_| Value::Bool(false)))); + let query_output = LazyQueryOutput::Iter(Box::new( + (0..100).map(|_| MetadataValueBox::from(false).into()), + )); let mut counter = 0; @@ -323,7 +336,7 @@ mod tests { .handle_query_output(query_output, &sorting, pagination, fetch_size) .unwrap() .into(); - let Value::Vec(v) = batch else { + let QueryOutputBox::Vec(v) = batch else { panic!("not expected result") }; counter += v.len(); @@ -333,7 +346,7 @@ mod tests { break; }; let (batch, new_cursor) = batched.into(); - let Value::Vec(v) = batch else { + let QueryOutputBox::Vec(v) = batch else { panic!("not expected result") }; counter += v.len(); diff --git a/core/src/queue.rs b/core/src/queue.rs index 1d3245aa665..e6740741c46 100644 --- a/core/src/queue.rs +++ b/core/src/queue.rs @@ -246,20 +246,17 @@ impl Queue { }; let tx = entry.get(); - match self.check_tx(tx, wsv) { - Err(e) => { - let (_, tx) = entry.remove_entry(); - self.decrease_per_user_tx_count(tx.as_ref().authority()); - if let Error::Expired = e { - expired_transactions.push(tx); - } - continue; - } - Ok(()) => { - seen.push(hash); - return Some(tx.clone()); + if let Err(e) = self.check_tx(tx, wsv) { + let (_, tx) = entry.remove_entry(); + self.decrease_per_user_tx_count(tx.as_ref().authority()); + if let Error::Expired = e { + expired_transactions.push(tx); } + continue; } + + seen.push(hash); + return Some(tx.clone()); } } diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index e0512dc4fe8..a366153d46f 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -228,7 +228,7 @@ pub mod isi { wsv.emit_events(Some(AccountEvent::MetadataInserted(MetadataChanged { target_id: account_id.clone(), key: self.key.clone(), - value: Box::new(self.value), + value: self.value, }))); Ok(()) @@ -250,7 +250,7 @@ pub mod isi { wsv.emit_events(Some(AccountEvent::MetadataRemoved(MetadataChanged { target_id: account_id.clone(), key: self.key, - value: Box::new(value), + value, }))); Ok(()) @@ -483,9 +483,8 @@ pub mod query { use eyre::Result; use iroha_data_model::{ - account::Account, - permission::PermissionToken, - query::{error::QueryExecutionFail as Error, MetadataValue}, + account::Account, metadata::MetadataValueBox, permission::PermissionToken, + query::error::QueryExecutionFail as Error, }; use super::*; @@ -580,7 +579,7 @@ pub mod query { impl ValidQuery for FindAccountKeyValueByIdAndKey { #[metrics(+"find_account_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, wsv: &WorldStateView) -> Result { let id = &self.id; let key = &self.key; iroha_logger::trace!(%id, %key); diff --git a/core/src/smartcontracts/isi/asset.rs b/core/src/smartcontracts/isi/asset.rs index c4d6dc912b3..b1a8764ab3b 100644 --- a/core/src/smartcontracts/isi/asset.rs +++ b/core/src/smartcontracts/isi/asset.rs @@ -57,10 +57,10 @@ pub mod isi { let asset = wsv.asset_or_insert(asset_id.clone(), Metadata::new())?; { - let store: &mut Metadata = asset - .try_as_mut() - .map_err(eyre::Error::from) - .map_err(|e| Error::Conversion(e.to_string()))?; + let AssetValue::Store(store) = &mut asset.value else { + return Err(Error::Conversion("Expected store asset type".to_owned())); + }; + store.insert_with_limits( self.key.clone(), self.value.clone(), @@ -71,7 +71,7 @@ pub mod isi { wsv.emit_events(Some(AssetEvent::MetadataInserted(MetadataChanged { target_id: asset_id, key: self.key, - value: Box::new(self.value), + value: self.value, }))); Ok(()) @@ -91,10 +91,11 @@ pub mod isi { let value = { let asset = wsv.asset_mut(&asset_id)?; - let store: &mut Metadata = asset - .try_as_mut() - .map_err(eyre::Error::from) - .map_err(|e| Error::Conversion(e.to_string()))?; + + let AssetValue::Store(store) = &mut asset.value else { + return Err(Error::Conversion("Expected store asset type".to_owned())); + }; + store .remove(&self.key) .ok_or_else(|| FindError::MetadataKey(self.key.clone()))? @@ -103,7 +104,7 @@ pub mod isi { wsv.emit_events(Some(AssetEvent::MetadataRemoved(MetadataChanged { target_id: asset_id, key: self.key, - value: Box::new(value), + value, }))); Ok(()) @@ -158,10 +159,9 @@ pub mod isi { assert_can_mint(&asset_definition, wsv)?; let asset = wsv.asset_or_insert(asset_id.clone(), Numeric::ZERO)?; - let quantity: &mut Numeric = asset - .try_as_mut() - .map_err(eyre::Error::from) - .map_err(|e| Error::Conversion(e.to_string()))?; + let AssetValue::Numeric(quantity) = &mut asset.value else { + return Err(Error::Conversion("Expected numeric asset type".to_owned())); + }; *quantity = quantity .checked_add(self.object) .ok_or(MathError::Overflow)?; @@ -197,10 +197,9 @@ pub mod isi { .assets .get_mut(&asset_id) .ok_or_else(|| FindError::Asset(asset_id.clone()))?; - let quantity: &mut Numeric = asset - .try_as_mut() - .map_err(eyre::Error::from) - .map_err(|e| Error::Conversion(e.to_string()))?; + let AssetValue::Numeric(quantity) = &mut asset.value else { + return Err(Error::Conversion("Expected numeric asset type".to_owned())); + }; *quantity = quantity .checked_sub(self.object) .ok_or(MathError::NotEnoughQuantity)?; @@ -243,10 +242,9 @@ pub mod isi { .assets .get_mut(&source_id) .ok_or_else(|| FindError::Asset(source_id.clone()))?; - let quantity: &mut Numeric = asset - .try_as_mut() - .map_err(eyre::Error::from) - .map_err(|e| Error::Conversion(e.to_string()))?; + let AssetValue::Numeric(quantity) = &mut asset.value else { + return Err(Error::Conversion("Expected numeric asset type".to_owned())); + }; *quantity = quantity .checked_sub(self.object) .ok_or(MathError::NotEnoughQuantity)?; @@ -257,10 +255,9 @@ pub mod isi { let destination_asset = wsv.asset_or_insert(destination_id.clone(), Numeric::ZERO)?; { - let quantity: &mut Numeric = destination_asset - .try_as_mut() - .map_err(eyre::Error::from) - .map_err(|e| Error::Conversion(e.to_string()))?; + let AssetValue::Numeric(quantity) = &mut destination_asset.value else { + return Err(Error::Conversion("Expected numeric asset type".to_owned())); + }; *quantity = quantity .checked_add(self.object) .ok_or(MathError::Overflow)?; @@ -368,9 +365,8 @@ pub mod query { use eyre::Result; use iroha_data_model::{ asset::{Asset, AssetDefinition, AssetValue}, - query::{ - asset::FindAssetDefinitionById, error::QueryExecutionFail as Error, MetadataValue, - }, + metadata::MetadataValueBox, + query::{asset::FindAssetDefinitionById, error::QueryExecutionFail as Error}, }; use super::*; @@ -591,7 +587,7 @@ pub mod query { impl ValidQuery for FindAssetKeyValueByIdAndKey { #[metrics(+"find_asset_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, wsv: &WorldStateView) -> Result { let id = &self.id; let key = &self.key; let asset = wsv.asset(id).map_err(|asset_err| { @@ -602,11 +598,10 @@ pub mod query { } })?; iroha_logger::trace!(%id, %key); - let store: &Metadata = asset - .value - .try_as_ref() - .map_err(eyre::Error::from) - .map_err(|e| Error::Conversion(e.to_string()))?; + let AssetValue::Store(store) = &asset.value else { + return Err(Error::Conversion("expected store, found other".to_owned())); + }; + store .get(key) .ok_or_else(|| Error::Find(FindError::MetadataKey(key.clone()))) diff --git a/core/src/smartcontracts/isi/domain.rs b/core/src/smartcontracts/isi/domain.rs index 19528612664..7917bb07066 100644 --- a/core/src/smartcontracts/isi/domain.rs +++ b/core/src/smartcontracts/isi/domain.rs @@ -194,7 +194,7 @@ pub mod isi { MetadataChanged { target_id: asset_definition_id, key: self.key, - value: Box::new(self.value), + value: self.value, }, ))); @@ -220,7 +220,7 @@ pub mod isi { MetadataChanged { target_id: asset_definition_id, key: self.key, - value: Box::new(value), + value, }, ))); @@ -243,7 +243,7 @@ pub mod isi { wsv.emit_events(Some(DomainEvent::MetadataInserted(MetadataChanged { target_id: domain_id, key: self.key, - value: Box::new(self.value), + value: self.value, }))); Ok(()) @@ -264,7 +264,7 @@ pub mod isi { wsv.emit_events(Some(DomainEvent::MetadataRemoved(MetadataChanged { target_id: domain_id, key: self.key, - value: Box::new(value), + value, }))); Ok(()) @@ -303,8 +303,7 @@ pub mod isi { pub mod query { use eyre::Result; use iroha_data_model::{ - domain::Domain, - query::{error::QueryExecutionFail as Error, MetadataValue}, + domain::Domain, metadata::MetadataValueBox, query::error::QueryExecutionFail as Error, }; use super::*; @@ -330,7 +329,7 @@ pub mod query { impl ValidQuery for FindDomainKeyValueByIdAndKey { #[metrics(+"find_domain_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, wsv: &WorldStateView) -> Result { let id = &self.id; let key = &self.key; iroha_logger::trace!(%id, %key); @@ -342,7 +341,7 @@ pub mod query { impl ValidQuery for FindAssetDefinitionKeyValueByIdAndKey { #[metrics(+"find_asset_definition_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, wsv: &WorldStateView) -> Result { let id = &self.id; let key = &self.key; iroha_logger::trace!(%id, %key); diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 471c93b4eb6..35413b25766 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -203,6 +203,7 @@ mod tests { use std::sync::Arc; use iroha_crypto::KeyPair; + use iroha_data_model::metadata::MetadataValueBox; use tokio::test; use super::*; @@ -239,16 +240,16 @@ mod tests { ) .execute(&account_id, &mut wsv)?; let asset = wsv.asset(&asset_id)?; - let metadata: &Metadata = asset.try_as_ref()?; - let bytes = metadata - .get(&Name::from_str("Bytes").expect("Valid")) - .cloned(); + let AssetValue::Store(store) = &asset.value else { + panic!("expected store asset"); + }; + let bytes = store.get(&"Bytes".parse::().expect("Valid")).cloned(); assert_eq!( bytes, - Some(Value::Vec(vec![ - 1_u32.to_value(), - 2_u32.to_value(), - 3_u32.to_value(), + Some(MetadataValueBox::Vec(vec![ + 1_u32.into(), + 2_u32.into(), + 3_u32.into(), ])) ); Ok(()) @@ -273,10 +274,10 @@ mod tests { })?; assert_eq!( bytes, - Some(Value::Vec(vec![ - 1_u32.to_value(), - 2_u32.to_value(), - 3_u32.to_value(), + Some(MetadataValueBox::Vec(vec![ + 1_u32.into(), + 2_u32.into(), + 3_u32.into(), ])) ); Ok(()) @@ -301,10 +302,10 @@ mod tests { .cloned(); assert_eq!( bytes, - Some(Value::Vec(vec![ - 1_u32.to_value(), - 2_u32.to_value(), - 3_u32.to_value(), + Some(MetadataValueBox::Vec(vec![ + 1_u32.into(), + 2_u32.into(), + 3_u32.into(), ])) ); Ok(()) @@ -329,10 +330,10 @@ mod tests { .cloned(); assert_eq!( bytes, - Some(Value::Vec(vec![ - 1_u32.to_value(), - 2_u32.to_value(), - 3_u32.to_value(), + Some(MetadataValueBox::Vec(vec![ + 1_u32.into(), + 2_u32.into(), + 3_u32.into(), ])) ); Ok(()) diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index 0f8f8ab8a31..11414202d9d 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -1,7 +1,10 @@ //! Query functionality. The common error type is also defined here, //! alongside functions for converting them into HTTP responses. use eyre::Result; -use iroha_data_model::{prelude::*, query::error::QueryExecutionFail as Error}; +use iroha_data_model::{ + prelude::*, + query::{error::QueryExecutionFail as Error, QueryOutputBox}, +}; use parity_scale_codec::{Decode, Encode}; use crate::{prelude::ValidQuery, WorldStateView}; @@ -12,16 +15,16 @@ pub trait Lazy { type Lazy<'a>; } -/// Lazily evaluated equivalent of [`Value`] -pub enum LazyValue<'a> { - /// Concrete computed [`Value`] - Value(Value), - /// Iterator over a set of [`Value`]s - Iter(Box + 'a>), +/// Lazily evaluated equivalent of [`Query::Output`] +pub enum LazyQueryOutput<'a> { + /// Concrete computed [`Query::Output`] + QueryOutput(QueryOutputBox), + /// Iterator over a set of [`Query::Output`]s + Iter(Box + 'a>), } -impl Lazy for Value { - type Lazy<'a> = LazyValue<'a>; +impl Lazy for QueryOutputBox { + type Lazy<'a> = LazyQueryOutput<'a>; } impl Lazy for Vec { @@ -44,7 +47,7 @@ impl_lazy! { iroha_data_model::account::Account, iroha_data_model::domain::Domain, iroha_data_model::block::BlockHeader, - iroha_data_model::query::MetadataValue, + iroha_data_model::metadata::MetadataValueBox, iroha_data_model::query::TransactionQueryOutput, iroha_data_model::permission::PermissionTokenSchema, iroha_data_model::trigger::Trigger, @@ -83,13 +86,16 @@ impl ValidQueryRequest { /// /// # Errors /// Forwards `self.query.execute` error. - pub fn execute<'wsv>(&'wsv self, wsv: &'wsv WorldStateView) -> Result, Error> { - let value = self.0.query().execute(wsv)?; - - Ok(if let LazyValue::Iter(iter) = value { - LazyValue::Iter(Box::new(iter.filter(|val| self.0.filter().applies(val)))) + pub fn execute<'wsv>( + &'wsv self, + wsv: &'wsv WorldStateView, + ) -> Result, Error> { + let output = self.0.query().execute(wsv)?; + + Ok(if let LazyQueryOutput::Iter(iter) = output { + LazyQueryOutput::Iter(Box::new(iter.filter(|val| self.0.filter().applies(val)))) } else { - value + output }) // We're not handling the LimitedMetadata case, because @@ -103,14 +109,14 @@ impl ValidQueryRequest { } impl ValidQuery for QueryBox { - fn execute<'wsv>(&self, wsv: &'wsv WorldStateView) -> Result, Error> { + fn execute<'wsv>(&self, wsv: &'wsv WorldStateView) -> Result, Error> { iroha_logger::debug!(query=%self, "Executing"); macro_rules! match_all { ( non_iter: {$( $non_iter_query:ident ),+ $(,)?} $( $query:ident, )+ ) => { match self { $( - QueryBox::$non_iter_query(query) => query.execute(wsv).map(Value::from).map(LazyValue::Value), )+ $( - QueryBox::$query(query) => query.execute(wsv).map(|i| i.map(Value::from)).map(|iter| LazyValue::Iter(Box::new(iter))), )+ + QueryBox::$non_iter_query(query) => query.execute(wsv).map(QueryOutputBox::from).map(LazyQueryOutput::QueryOutput), )+ $( + QueryBox::$query(query) => query.execute(wsv).map(|i| i.map(QueryOutputBox::from)).map(|iter| LazyQueryOutput::Iter(Box::new(iter))), )+ } }; } @@ -168,7 +174,9 @@ mod tests { use std::str::FromStr as _; use iroha_crypto::{Hash, HashOf, KeyPair}; - use iroha_data_model::{query::error::FindError, transaction::TransactionLimits}; + use iroha_data_model::{ + metadata::MetadataValueBox, query::error::FindError, transaction::TransactionLimits, + }; use iroha_primitives::unique_vec::UniqueVec; use once_cell::sync::Lazy; use tokio::test; @@ -212,7 +220,7 @@ mod tests { store .insert_with_limits( Name::from_str("Bytes").expect("Valid"), - Value::Vec(vec![1_u32.to_value(), 2_u32.to_value(), 3_u32.to_value()]), + MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), MetadataLimits::new(10, 100), ) .unwrap(); @@ -228,7 +236,7 @@ mod tests { let mut metadata = Metadata::new(); metadata.insert_with_limits( Name::from_str("Bytes")?, - Value::Vec(vec![1_u32.to_value(), 2_u32.to_value(), 3_u32.to_value()]), + MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), MetadataLimits::new(10, 100), )?; @@ -319,8 +327,8 @@ mod tests { let bytes = FindAssetKeyValueByIdAndKey::new(asset_id, Name::from_str("Bytes")?).execute(&wsv)?; assert_eq!( - Value::Vec(vec![1_u32.to_value(), 2_u32.to_value(), 3_u32.to_value()]), - bytes.into(), + MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), + bytes, ); Ok(()) } @@ -334,8 +342,8 @@ mod tests { let bytes = FindAccountKeyValueByIdAndKey::new(ALICE_ID.clone(), Name::from_str("Bytes")?) .execute(&wsv)?; assert_eq!( - Value::Vec(vec![1_u32.to_value(), 2_u32.to_value(), 3_u32.to_value()]), - bytes.into(), + MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), + bytes, ); Ok(()) } @@ -447,7 +455,10 @@ mod tests { let found_accepted = FindTransactionByHash::new(va_tx.as_ref().hash()).execute(&wsv)?; if found_accepted.transaction.error.is_none() { - assert_eq!(va_tx.as_ref().hash(), found_accepted.as_ref().hash()) + assert_eq!( + va_tx.as_ref().hash(), + found_accepted.as_ref().as_ref().hash() + ) } Ok(()) } @@ -459,7 +470,7 @@ mod tests { let mut metadata = Metadata::new(); metadata.insert_with_limits( Name::from_str("Bytes")?, - Value::Vec(vec![1_u32.to_value(), 2_u32.to_value(), 3_u32.to_value()]), + MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), MetadataLimits::new(10, 100), )?; let mut domain = Domain::new(DomainId::from_str("wonderland")?) @@ -482,8 +493,8 @@ mod tests { let key = Name::from_str("Bytes")?; let bytes = FindDomainKeyValueByIdAndKey::new(domain_id, key).execute(&wsv)?; assert_eq!( - Value::Vec(vec![1_u32.to_value(), 2_u32.to_value(), 3_u32.to_value()]), - bytes.into(), + MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), + bytes, ); Ok(()) } diff --git a/core/src/smartcontracts/isi/triggers/mod.rs b/core/src/smartcontracts/isi/triggers/mod.rs index fd5459cacad..d90b5f65932 100644 --- a/core/src/smartcontracts/isi/triggers/mod.rs +++ b/core/src/smartcontracts/isi/triggers/mod.rs @@ -195,7 +195,8 @@ pub mod query { //! Queries associated to triggers. use iroha_data_model::{ events::TriggeringFilterBox, - query::{error::QueryExecutionFail as Error, MetadataValue}, + metadata::MetadataValueBox, + query::error::QueryExecutionFail as Error, trigger::{Trigger, TriggerId}, }; @@ -233,7 +234,7 @@ pub mod query { impl ValidQuery for FindTriggerKeyValueByIdAndKey { #[metrics(+"find_trigger_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, wsv: &WorldStateView) -> Result { let id = &self.id; let key = &self.key; iroha_logger::trace!(%id, %key); diff --git a/core/src/smartcontracts/isi/tx.rs b/core/src/smartcontracts/isi/tx.rs index 009b61484be..ac282bd37b3 100644 --- a/core/src/smartcontracts/isi/tx.rs +++ b/core/src/smartcontracts/isi/tx.rs @@ -74,7 +74,7 @@ impl ValidQuery for FindAllTransactions { .flat_map(BlockTransactionIter::new) .map(|tx| TransactionQueryOutput { block_hash: tx.block_hash(), - transaction: Box::new(tx.value()), + transaction: tx.value(), }), )) } @@ -94,7 +94,7 @@ impl ValidQuery for FindTransactionsByAccountId { .filter(move |tx| *tx.authority() == account_id) .map(|tx| TransactionQueryOutput { block_hash: tx.block_hash(), - transaction: Box::new(tx.value()), + transaction: tx.value(), }), )) } @@ -118,7 +118,6 @@ impl ValidQuery for FindTransactionByHash { transactions .find(|transaction| transaction.value.hash() == tx_hash) .cloned() - .map(Box::new) .map(|transaction| TransactionQueryOutput { block_hash, transaction, diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index c78bb327168..d3a1c0b374f 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -13,7 +13,7 @@ use iroha_data_model::{ isi::InstructionBox, permission::PermissionTokenSchema, prelude::*, - query::{QueryBox, QueryRequest, QueryWithParameters}, + query::{QueryBox, QueryId, QueryOutputBox, QueryRequest, QueryWithParameters}, smart_contract::payloads::{self, Validate}, BatchedResponse, Level as LogLevel, ValidationFail, }; @@ -78,7 +78,7 @@ mod import { fn execute_query( query_request: SmartContractQueryRequest, state: &mut S, - ) -> Result, ValidationFail>; + ) -> Result, ValidationFail>; /// Execute `instruction` on host #[codec::wrap_trait_fn] @@ -761,7 +761,7 @@ where fn default_execute_query( query_request: SmartContractQueryRequest, state: &mut state::CommonState, - ) -> Result, ValidationFail> { + ) -> Result, ValidationFail> { iroha_logger::debug!(%query_request, "Executing"); match query_request.0 { @@ -907,7 +907,7 @@ impl<'wrld> import::traits::ExecuteOperations> fn execute_query( query_request: SmartContractQueryRequest, state: &mut state::SmartContract<'wrld>, - ) -> Result, ValidationFail> { + ) -> Result, ValidationFail> { Self::default_execute_query(query_request, state) } @@ -979,7 +979,7 @@ impl<'wrld> import::traits::ExecuteOperations> fn execute_query( query_request: SmartContractQueryRequest, state: &mut state::Trigger<'wrld>, - ) -> Result, ValidationFail> { + ) -> Result, ValidationFail> { Self::default_execute_query(query_request, state) } @@ -1008,7 +1008,7 @@ where fn execute_query( query_request: SmartContractQueryRequest, state: &mut state::CommonState, S>, - ) -> Result, ValidationFail> { + ) -> Result, ValidationFail> { debug!(%query_request, "Executing as executor"); Runtime::default_execute_query(query_request, state) @@ -1246,7 +1246,7 @@ impl<'wrld> import::traits::ExecuteOperations, - ) -> Result, ValidationFail> { + ) -> Result, ValidationFail> { debug!(%query_request, "Executing as executor"); Runtime::default_execute_query(query_request, state) diff --git a/core/src/wsv.rs b/core/src/wsv.rs index 0d4c2b7248d..c736af16b22 100644 --- a/core/src/wsv.rs +++ b/core/src/wsv.rs @@ -14,7 +14,7 @@ use iroha_data_model::{ block::SignedBlock, events::notification::{TriggerCompletedEvent, TriggerCompletedOutcome}, isi::error::{InstructionExecutionError as Error, MathError}, - parameter::Parameter, + parameter::{Parameter, ParameterValueBox}, permission::PermissionTokenSchema, prelude::*, query::error::{FindError, QueryExecutionFail}, @@ -1067,7 +1067,7 @@ impl WorldStateView { } /// Query parameter and convert it to a proper type - pub fn query_param, P: core::hash::Hash + Eq + ?Sized>( + pub fn query_param, P: core::hash::Hash + Eq + ?Sized>( &self, param: &P, ) -> Option @@ -1078,7 +1078,7 @@ impl WorldStateView { .parameters .get(param) .as_ref() - .map(|param| &*param.val) + .map(|param| ¶m.val) .cloned() .and_then(|param_val| param_val.try_into().ok()) } diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 1c187480a74..128df3827b2 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -14,7 +14,7 @@ use iroha_client::{ }; use iroha_config::parameters::actual::Root as Config; use iroha_crypto::prelude::*; -use iroha_data_model::ChainId; +use iroha_data_model::{query::QueryOutputBox, ChainId}; use iroha_genesis::{GenesisNetwork, RawGenesisBlock}; use iroha_logger::InstrumentFutures; use iroha_primitives::{ @@ -687,7 +687,7 @@ pub trait TestClient: Sized { where R::Output: QueryOutput, ::Target: core::fmt::Debug, - >::Error: Into; + >::Error: Into; /// Submits instructions with polling /// @@ -702,7 +702,7 @@ pub trait TestClient: Sized { where R::Output: QueryOutput, ::Target: core::fmt::Debug, - >::Error: Into; + >::Error: Into; /// Polls request till predicate `f` is satisfied, with default period and max attempts. /// @@ -716,7 +716,7 @@ pub trait TestClient: Sized { where R::Output: QueryOutput, ::Target: core::fmt::Debug, - >::Error: Into; + >::Error: Into; /// Polls request till predicate `f` is satisfied with `period` and `max_attempts` supplied. /// @@ -732,7 +732,7 @@ pub trait TestClient: Sized { where R::Output: QueryOutput, ::Target: core::fmt::Debug, - >::Error: Into; + >::Error: Into; } impl TestRuntime for Runtime { @@ -829,7 +829,7 @@ impl TestClient for Client { where R::Output: QueryOutput, ::Target: core::fmt::Debug, - >::Error: Into, + >::Error: Into, { self.submit(instruction) .expect("Failed to submit instruction."); @@ -845,7 +845,7 @@ impl TestClient for Client { where R::Output: QueryOutput, ::Target: core::fmt::Debug, - >::Error: Into, + >::Error: Into, { self.submit_all(instructions) .expect("Failed to submit instruction."); @@ -862,7 +862,7 @@ impl TestClient for Client { where R::Output: QueryOutput, ::Target: core::fmt::Debug, - >::Error: Into, + >::Error: Into, { let mut query_result = None; for _ in 0..max_attempts { @@ -883,7 +883,7 @@ impl TestClient for Client { where R::Output: QueryOutput, ::Target: core::fmt::Debug, - >::Error: Into, + >::Error: Into, { self.poll_request_with_period(request, Config::pipeline_time() / 2, 10, f) } diff --git a/data_model/Cargo.toml b/data_model/Cargo.toml index 9b80ee2b6fc..db190cb0554 100644 --- a/data_model/Cargo.toml +++ b/data_model/Cargo.toml @@ -55,6 +55,8 @@ base64 = { workspace = true, features = ["alloc"] } once_cell = { workspace = true, optional = true } [dev-dependencies] +iroha_crypto = { workspace = true, features = ["rand"] } + trybuild = { workspace = true } criterion = { workspace = true } diff --git a/data_model/src/account.rs b/data_model/src/account.rs index 9226d54952e..0ac65442415 100644 --- a/data_model/src/account.rs +++ b/data_model/src/account.rs @@ -359,16 +359,6 @@ impl Registered for Account { type With = NewAccount; } -#[cfg(feature = "transparent_api")] -impl FromIterator for crate::Value { - fn from_iter>(iter: T) -> Self { - iter.into_iter() - .map(Into::into) - .collect::>() - .into() - } -} - /// Account Identification is represented by `name@domain_name` string. impl FromStr for AccountId { type Err = ParseError; diff --git a/data_model/src/asset.rs b/data_model/src/asset.rs index 1b3be4bd075..6bf4cf6447d 100644 --- a/data_model/src/asset.rs +++ b/data_model/src/asset.rs @@ -18,7 +18,7 @@ use serde_with::{DeserializeFromStr, SerializeDisplay}; pub use self::model::*; use crate::{ account::prelude::*, domain::prelude::*, ipfs::IpfsPath, metadata::Metadata, HasMetadata, - Identifiable, Name, ParseError, Registered, TryAsMut, TryAsRef, Value, + Identifiable, Name, ParseError, Registered, }; /// API to work with collections of [`Id`] : [`Asset`] mappings. @@ -34,6 +34,7 @@ pub type AssetTotalQuantityMap = btree_map::BTreeMap #[model] pub mod model { + use iroha_macro::FromVariant; use super::*; @@ -212,6 +213,7 @@ pub mod model { Eq, PartialOrd, Ord, + FromVariant, Decode, Encode, Deserialize, @@ -222,7 +224,11 @@ pub mod model { pub enum AssetValue { /// Asset's qualitative value. #[display(fmt = "{_0}")] - Numeric(Numeric), + Numeric( + #[skip_from] + #[skip_try_from] + Numeric, + ), /// Asset's key-value structured data. Store(Metadata), } @@ -366,51 +372,12 @@ impl AssetValue { } } -impl From for AssetValue { - fn from(value: Metadata) -> Self { - AssetValue::Store(value) - } -} - impl> From for AssetValue { fn from(value: T) -> Self { - AssetValue::Numeric(value.into()) + Self::Numeric(value.into()) } } -macro_rules! impl_try_as_for_asset_value { - ( $($variant:ident( $ty:ty ),)* ) => {$( - impl TryAsMut<$ty> for AssetValue { - type Error = crate::EnumTryAsError<$ty, AssetValueType>; - - fn try_as_mut(&mut self) -> Result<&mut $ty, Self::Error> { - if let AssetValue:: $variant (value) = self { - Ok(value) - } else { - Err(crate::EnumTryAsError::got(self.value_type())) - } - } - } - - impl TryAsRef<$ty> for AssetValue { - type Error = crate::EnumTryAsError<$ty, AssetValueType>; - - fn try_as_ref(&self) -> Result<& $ty, Self::Error> { - if let AssetValue:: $variant (value) = self { - Ok(value) - } else { - Err(crate::EnumTryAsError::got(self.value_type())) - } - } - } - )*} -} - -impl_try_as_for_asset_value! { - Numeric(Numeric), - Store(Metadata), -} - /// Asset Definition Identification is represented by `name#domain_name` string. impl FromStr for AssetDefinitionId { type Err = ParseError; @@ -508,30 +475,6 @@ impl HasMetadata for NewAssetDefinition { } } -impl TryAsMut for Asset -where - AssetValue: TryAsMut, -{ - type Error = >::Error; - - #[inline] - fn try_as_mut(&mut self) -> Result<&mut T, Self::Error> { - self.value.try_as_mut() - } -} - -impl TryAsRef for Asset -where - AssetValue: TryAsRef, -{ - type Error = >::Error; - - #[inline] - fn try_as_ref(&self) -> Result<&T, Self::Error> { - self.value.try_as_ref() - } -} - impl Registered for Asset { type With = Self; } @@ -540,24 +483,6 @@ impl Registered for AssetDefinition { type With = NewAssetDefinition; } -impl FromIterator for Value { - fn from_iter>(iter: T) -> Self { - iter.into_iter() - .map(Into::into) - .collect::>() - .into() - } -} - -impl FromIterator for Value { - fn from_iter>(iter: T) -> Self { - iter.into_iter() - .map(Into::into) - .collect::>() - .into() - } -} - /// The prelude re-exports most commonly used traits, structs and macros from this crate. pub mod prelude { pub use super::{ diff --git a/data_model/src/domain.rs b/data_model/src/domain.rs index 6eeff49dedb..c631996224d 100644 --- a/data_model/src/domain.rs +++ b/data_model/src/domain.rs @@ -257,15 +257,6 @@ impl Domain { } } -impl FromIterator for crate::Value { - fn from_iter>(iter: T) -> Self { - iter.into_iter() - .map(Into::into) - .collect::>() - .into() - } -} - /// The prelude re-exports most commonly used traits, structs and macros from this crate. pub mod prelude { pub use super::{Domain, DomainId}; diff --git a/data_model/src/events/data/events.rs b/data_model/src/events/data/events.rs index c5972675fad..2b079f8d2a1 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -36,6 +36,7 @@ macro_rules! data_event { #[model] pub mod model { use super::*; + use crate::metadata::MetadataValueBox; /// Generic [`MetadataChanged`] struct. /// Contains the changed metadata (`(key, value)` pair), either inserted or removed, which is determined by the wrapping event. @@ -58,7 +59,7 @@ pub mod model { pub struct MetadataChanged { pub target_id: ID, pub key: Name, - pub value: Box, + pub value: MetadataValueBox, } /// Event diff --git a/data_model/src/events/data/mod.rs b/data_model/src/events/data/mod.rs index 1c1ab8494f8..70d0cd0d66e 100644 --- a/data_model/src/events/data/mod.rs +++ b/data_model/src/events/data/mod.rs @@ -1,7 +1,7 @@ //! Data events. #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, format, string::String, vec::Vec}; +use alloc::{format, string::String, vec::Vec}; pub use events::DataEvent; pub use filters::DataEventFilter; diff --git a/data_model/src/integer.rs b/data_model/src/integer.rs deleted file mode 100644 index 465e7b0c174..00000000000 --- a/data_model/src/integer.rs +++ /dev/null @@ -1,268 +0,0 @@ -//! Tagged polymorphic numerical type. -//! -//! Special care is taken to work around limitations for wide-integer -//! types commonly used in Rust and in the code-base, -#[cfg(not(feature = "std"))] -use alloc::{ - format, - string::{String, ToString}, - vec::Vec, -}; -use core::{num::ParseIntError, str::FromStr}; - -use derive_more::From; -use serde::{de::Error, Deserializer, Serializer}; - -pub use self::model::*; -use super::{DebugCustom, Decode, Deserialize, Display, Encode, FromVariant, IntoSchema}; - -#[iroha_data_model_derive::model] -pub mod model { - use super::*; - - /// Enum for all supported numeric values - #[derive( - DebugCustom, - Display, - Copy, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - FromVariant, - Decode, - Encode, - IntoSchema, - )] - #[ffi_type] - pub enum Integer { - /// `u32` value - #[debug(fmt = "{_0}_u32")] - U32(u32), - /// `u64` value - #[debug(fmt = "{_0}_u64")] - U64(u64), - /// `u128` value - #[debug(fmt = "{_0}_u128")] - U128(u128), - } -} - -impl Integer { - /// Return `true` if value is zero - pub const fn is_zero_value(self) -> bool { - use Integer::*; - match self { - U32(value) => value == 0_u32, - U64(value) => value == 0_u64, - U128(value) => value == 0_u128, - } - } -} - -struct IntegerVisitor; - -#[derive(Deserialize)] -#[serde(field_identifier)] -enum Discriminants { - U32, - U64, - U128, -} - -impl FromStr for Integer { - type Err = ParseNumericError; - - fn from_str(s: &str) -> Result { - if s.contains('_') { - if s.ends_with("_u32") { - Ok(Integer::U32( - s.rsplit_once("_u32") - .ok_or(ParseNumericError::Format)? - .0 - .parse()?, - )) - } else if s.ends_with("_u64") { - Ok(Integer::U64( - s.rsplit_once("_u64") - .ok_or(ParseNumericError::Format)? - .0 - .parse()?, - )) - } else if s.ends_with("_u128") { - Ok(Integer::U128( - s.rsplit_once("_u128") - .ok_or(ParseNumericError::Format)? - .0 - .parse()?, - )) - } else { - Err(ParseNumericError::Format) - } - } else { - Err(ParseNumericError::Format) - } - } -} - -// serialize and deserialize numbers as string literals with tagged numbers inside -// U32(42) <-> "42_u32" -// U64(42) <-> "42_u64" -// U128(42) <-> "42_u128" - -impl serde::Serialize for Integer { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self:?}")) - } -} - -impl serde::de::Visitor<'_> for IntegerVisitor { - type Value = Integer; - - #[inline] - fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { - formatter.write_str("A quoted string containing a tagged number") - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - let parsed = v.parse::().map_err(|e| E::custom(e.to_string()))?; - - Ok(parsed) - } -} - -impl<'de> Deserialize<'de> for Integer { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(IntegerVisitor) - } -} - -/// Error emitted when a integer value failed to be parsed from a -/// string or a JSON literal. -#[derive(Clone, Debug, Display, From)] -#[allow(missing_docs)] -pub enum ParseNumericError { - #[display( - fmt = "A correctly formatted value was not found. Correct values are integer followed by an underscore and a type identifier, which is one of [`u32`, `u64`, `u128`]. Example:\"12_u32\"." - )] - Format, - #[from] - #[display(fmt = "Failed to parse value as an integer. {_0}")] - ParseInt(ParseIntError), -} - -#[cfg(feature = "std")] -impl std::error::Error for ParseNumericError {} - -// TODO: impl source correctly for conversions. - -#[cfg(test)] -mod tests { - #![allow(clippy::pedantic)] - use super::*; - - #[test] - fn tagged_quoted_inclusion_u128() { - let values = [ - 0_u128, - 1_u128, - (u32::MAX - 1_u32) as u128, - u32::MAX as u128, - (u64::MAX - 1_u64) as u128, - u64::MAX as u128, - u128::MAX - 1_u128, - u128::MAX, - ]; - for value in values { - let json = format!("\"{value}_u128\"",); - let val: Integer = serde_json::from_str(&json).expect("Invalid JSON"); - assert_eq!(val, Integer::U128(value)); - } - } - - #[test] - fn tagged_quoted_inclusion_u64() { - let values = [ - 0_u64, - 1_u64, - (u32::MAX - 1_u32) as u64, - u32::MAX as u64, - u64::MAX - 1_u64, - u64::MAX, - ]; - for value in values { - let json = format!("\"{value}_u64\"",); - let val: Integer = serde_json::from_str(&json).expect("Invalid JSON"); - assert_eq!(Integer::U64(value), val) - } - } - - #[test] - fn tagged_quoted_inclusion_u32() { - let values = [0_u32, 1_u32, (u32::MAX - 1_u32), u32::MAX]; - for value in values { - let json = format!("\"{value}_u32\"",); - let val: Integer = serde_json::from_str(&json).expect("Invalid JSON"); - assert_eq!(val, Integer::U32(value)); - } - } - - #[test] - fn serialize_is_quoted_u128() { - let value = Integer::U128(u128::MAX); - let string = serde_json::to_string(&value).unwrap(); - let expectation = format!("\"{}_u128\"", u128::MAX); - assert_eq!(string, expectation); - } - - #[test] - fn debug_and_from_str_invert_each_other() { - let values = [ - Integer::U32(0_u32), - Integer::U128(0_u128), - Integer::U128(u128::MAX), - Integer::U128((u64::MAX - 1) as u128), - ]; - for val in values { - let new_value: Integer = format!("{val:?}").parse().expect("Failed to parse"); - assert_eq!(new_value, val); - } - } - - fn as_u32(v: impl Into) -> String { - Integer::U32(v.into()).to_string() - } - - #[test] - #[should_panic] - /// We deny ambiguous deserialisation from strings. - fn display_from_str_integer_unsupported() { - assert_eq!( - Integer::from_str(&as_u32(0_u32)).unwrap(), - Integer::U128(0_u128) - ); - } - - #[test] - #[should_panic] - /// We deny ambiguous deserialisation from int literals - fn deserialize_int_literal_unsupported() { - serde_json::from_str::("0").unwrap(); - } - - #[test] - fn deserialize_without_prefix_fails() { - assert!(serde_json::from_str::("\"100\"").is_err()); - assert!(serde_json::from_str::("\"100.0\"").is_err()); - } -} diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index f7666d92567..85017971be4 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; use strum::EnumDiscriminants; pub use self::{model::*, transparent::*}; -use super::{prelude::*, Value}; +use super::{metadata::MetadataValueBox, prelude::*}; use crate::{seal, Level, Registered}; /// Marker trait designating instruction. @@ -281,13 +281,13 @@ mod transparent { /// Key. pub key: Name, /// Value. - pub value: Value, + pub value: MetadataValueBox, } } impl SetKeyValue { /// Constructs a new [`SetKeyValue`] for a [`Domain`] with the given `key` and `value`. - pub fn domain(domain_id: DomainId, key: Name, value: impl Into) -> Self { + pub fn domain(domain_id: DomainId, key: Name, value: impl Into) -> Self { Self { object_id: domain_id, key, @@ -298,7 +298,11 @@ mod transparent { impl SetKeyValue { /// Constructs a new [`SetKeyValue`] for an [`Account`] with the given `key` and `value`. - pub fn account(account_id: AccountId, key: Name, value: impl Into) -> Self { + pub fn account( + account_id: AccountId, + key: Name, + value: impl Into, + ) -> Self { Self { object_id: account_id, key, @@ -312,7 +316,7 @@ mod transparent { pub fn asset_definition( asset_definition_id: AssetDefinitionId, key: Name, - value: impl Into, + value: impl Into, ) -> Self { Self { object_id: asset_definition_id, @@ -324,7 +328,7 @@ mod transparent { impl SetKeyValue { /// Constructs a new [`SetKeyValue`] for an [`Asset`] with the given `key` and `value`. - pub fn asset(asset_id: AssetId, key: Name, value: impl Into) -> Self { + pub fn asset(asset_id: AssetId, key: Name, value: impl Into) -> Self { Self { object_id: asset_id, key, @@ -853,7 +857,7 @@ mod transparent { isi! { /// Generic instruction for granting permission to an entity. - pub struct Grant> { + pub struct Grant { /// Object to grant. pub object: O, /// Entity to which to grant this token. @@ -882,9 +886,7 @@ mod transparent { } impl_display! { - Grant - where - O: Into + Display, + Grant where O: Display, => "GRANT `{}` TO `{}`", object, @@ -900,7 +902,7 @@ mod transparent { isi! { /// Generic instruction for revoking permission from an entity. - pub struct Revoke> { + pub struct Revoke { /// Object to revoke. pub object: O, /// Entity which is being revoked this token from. @@ -929,9 +931,7 @@ mod transparent { } impl_display! { - Revoke - where - O: Into + Display, + Revoke where O: Display, => "REVOKE `{}` FROM `{}`", object, @@ -1214,7 +1214,7 @@ pub mod error { //! Module containing errors that can occur during instruction evaluation #[cfg(not(feature = "std"))] - use alloc::{boxed::Box, format, string::String, vec::Vec}; + use alloc::{format, string::String, vec::Vec}; use core::fmt::Debug; use derive_more::Display; @@ -1237,7 +1237,6 @@ pub mod error { use serde::{Deserialize, Serialize}; use super::*; - use crate::{asset::AssetDefinitionId, Value}; /// Instruction execution error type #[derive( @@ -1372,12 +1371,6 @@ pub mod error { pub enum TypeError { /// Asset Ids correspond to assets with different underlying types, {0} AssetValueType(#[cfg_attr(feature = "std", source)] Mismatch), - /// Value passed to the parameter doesn't have the right type, {0} - ParameterValueType(#[cfg_attr(feature = "std", source)] Box>), - /// AssetDefinition Ids don't match, {0} - AssetDefinitionId( - #[cfg_attr(feature = "std", source)] Box>, - ), /// Numeric asset value type was expected, received: {0} NumericAssetValueTypeExpected( #[skip_from] diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 3cec55f9c1c..5d227830432 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -15,40 +15,24 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -use core::{ - convert::AsRef, - fmt, - fmt::Debug, - ops::{ControlFlow, RangeInclusive}, - str::FromStr, -}; +use core::{fmt, fmt::Debug, ops::RangeInclusive, str::FromStr}; -use block::SignedBlock; -#[cfg(not(target_arch = "aarch64"))] -use derive_more::Into; -use derive_more::{AsRef, Constructor, DebugCustom, Deref, Display, From, FromStr}; +use derive_more::{Constructor, Display, From, FromStr}; use events::TriggeringFilterBox; use getset::Getters; -pub use integer::model::Integer; -use iroha_crypto::{HashOf, PublicKey}; -use iroha_data_model_derive::{ - model, EnumRef, IdEqOrdHash, PartiallyTaggedDeserialize, PartiallyTaggedSerialize, -}; -use iroha_macro::{error::ErrorTryFromEnum, FromVariant}; -use iroha_primitives::{ - numeric::Numeric, - small::{Array as SmallArray, SmallVec}, -}; +use iroha_crypto::PublicKey; +use iroha_data_model_derive::{model, EnumRef, IdEqOrdHash}; +use iroha_macro::FromVariant; use iroha_schema::IntoSchema; use iroha_version::{declare_versioned_with_scale, version_with_scale}; use parity_scale_codec::{Decode, Encode}; -use prelude::{Executable, SignedTransaction, TransactionQueryOutput}; +use prelude::Executable; use serde::{Deserialize, Serialize}; use serde_with::{DeserializeFromStr, SerializeDisplay}; use strum::FromRepr; pub use self::model::*; -use crate::{account::SignatureCheckCondition, name::Name}; +use crate::name::Name; pub mod account; pub mod asset; @@ -56,15 +40,12 @@ pub mod block; pub mod domain; pub mod events; pub mod executor; -pub mod integer; pub mod ipfs; pub mod isi; pub mod metadata; pub mod name; pub mod peer; pub mod permission; -#[cfg(feature = "http")] -pub mod predicate; pub mod query; pub mod role; pub mod smart_contract; @@ -197,26 +178,6 @@ pub struct ParseError { #[cfg(feature = "std")] impl std::error::Error for ParseError {} -#[allow(clippy::missing_errors_doc)] -/// [`AsMut`] but reference conversion can fail. -pub trait TryAsMut { - /// The type returned in the event of a conversion error. - type Error; - - /// Perform the conversion. - fn try_as_mut(&mut self) -> Result<&mut T, Self::Error>; -} - -#[allow(clippy::missing_errors_doc)] -/// Similar to [`AsRef`] but indicating that this reference conversion can fail. -pub trait TryAsRef { - /// The type returned in the event of a conversion error. - type Error; - - /// Perform the conversion. - fn try_as_ref(&self) -> Result<&T, Self::Error>; -} - /// Error which occurs when converting an enum reference to a variant reference #[derive(Debug, Clone, Copy)] #[repr(transparent)] @@ -239,7 +200,8 @@ impl fmt::Display for EnumTryAsError { } impl EnumTryAsError { - const fn got(got: GOT) -> Self { + #[allow(missing_docs)] + pub const fn got(got: GOT) -> Self { Self { expected: core::marker::PhantomData, got, @@ -255,6 +217,8 @@ pub mod parameter { use core::borrow::Borrow; + use iroha_primitives::numeric::Numeric; + pub use self::model::*; use super::*; use crate::isi::InstructionBox; @@ -281,6 +245,32 @@ pub mod parameter { pub mod model { use super::*; + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + FromVariant, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + #[ffi_type(local)] + pub enum ParameterValueBox { + TransactionLimits(transaction::TransactionLimits), + MetadataLimits(metadata::Limits), + LengthLimits(LengthLimits), + Numeric( + #[skip_from] + #[skip_try_from] + Numeric, + ), + } + /// Identification of a [`Parameter`]. #[derive( Debug, @@ -313,8 +303,8 @@ pub mod parameter { Debug, Display, Clone, + Constructor, IdEqOrdHash, - Getters, Decode, Encode, DeserializeFromStr, @@ -328,20 +318,63 @@ pub mod parameter { /// Unique [`Id`] of the [`Parameter`]. pub id: ParameterId, /// Current value of the [`Parameter`]. - #[getset(get = "pub")] - pub val: Box, + pub val: ParameterValueBox, } } - impl Parameter { - fn new(id: ParameterId, val: Value) -> Self { - Self { - id, - val: Box::new(val), + // TODO: Maybe derive + impl core::fmt::Display for ParameterValueBox { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::MetadataLimits(v) => core::fmt::Display::fmt(&v, f), + Self::TransactionLimits(v) => core::fmt::Display::fmt(&v, f), + Self::LengthLimits(v) => core::fmt::Display::fmt(&v, f), + Self::Numeric(v) => core::fmt::Display::fmt(&v, f), } } } + impl> From for ParameterValueBox { + fn from(value: T) -> Self { + Self::Numeric(value.into()) + } + } + + impl TryFrom for u32 { + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: ParameterValueBox) -> Result { + use iroha_macro::error::ErrorTryFromEnum; + + let ParameterValueBox::Numeric(numeric) = value else { + return Err(ErrorTryFromEnum::default()); + }; + + numeric.try_into().map_err(|_| ErrorTryFromEnum::default()) + } + } + + impl TryFrom for u64 { + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: ParameterValueBox) -> Result { + use iroha_macro::error::ErrorTryFromEnum; + + let ParameterValueBox::Numeric(numeric) = value else { + return Err(ErrorTryFromEnum::default()); + }; + + numeric.try_into().map_err(|_| ErrorTryFromEnum::default()) + } + } + + impl Parameter { + /// Current value of the [`Parameter`]. + pub fn val(&self) -> &ParameterValueBox { + &self.val + } + } + impl Borrow for ParameterId { fn borrow(&self) -> &str { self.name.borrow() @@ -380,7 +413,7 @@ pub mod parameter { reason: "Failed to parse the `val` part of the `Parameter` as `LengthLimits`. Invalid upper `u32` bound.", })?; - Value::LengthLimits(LengthLimits::new(lower, upper)) + LengthLimits::new(lower, upper).into() } // Shorthand for `TransactionLimits` "TL" => { @@ -396,10 +429,10 @@ pub mod parameter { reason: "Failed to parse the `val` part of the `Parameter` as `TransactionLimits`. `max_wasm_size_bytes` field should be a valid `u64`.", })?; - Value::TransactionLimits(transaction::TransactionLimits::new( + transaction::TransactionLimits::new( max_instr, max_wasm_size, - )) + ).into() } // Shorthand for `MetadataLimits` "ML" => { @@ -415,7 +448,7 @@ pub mod parameter { reason: "Failed to parse the `val` part of the `Parameter` as `MetadataLimits`. Invalid `u32` in `max_entry_len` field.", })?; - Value::MetadataLimits(metadata::Limits::new(lower, upper)) + metadata::Limits::new(lower, upper).into() } _ => return Err(ParseError { reason: @@ -424,10 +457,12 @@ pub mod parameter { }; Ok(Self::new(param_id, val)) } else { - let val = val_candidate.parse::().map_err(|_| ParseError { - reason: "Failed to parse the `val` part of the `Parameter` as `u64`.", + let val = val_candidate.parse::().map_err(|_| ParseError { + reason: + "Failed to parse the `val` part of the `Parameter` as `Numeric`.", })?; - Ok(Self::new(param_id, Value::Integer(Integer::U64(val)))) + + Ok(Self::new(param_id, val.into())) } } else { Err(ParseError { @@ -472,11 +507,11 @@ pub mod parameter { pub fn add_parameter( mut self, parameter_id: &str, - val: impl Into, + val: impl Into, ) -> Result { let parameter = Parameter { id: parameter_id.parse()?, - val: Box::new(val.into()), + val: val.into(), }; self.parameters.push(parameter); Ok(self) @@ -548,19 +583,19 @@ pub mod parameter { Parameter::new( ParameterId::from_str("TransactionLimits") .expect("Failed to parse `ParameterId`"), - Value::TransactionLimits(TransactionLimits::new(42, 24)), + TransactionLimits::new(42, 24).into(), ), Parameter::new( ParameterId::from_str("MetadataLimits").expect("Failed to parse `ParameterId`"), - Value::MetadataLimits(MetadataLimits::new(42, 24)), + MetadataLimits::new(42, 24).into(), ), Parameter::new( ParameterId::from_str("LengthLimits").expect("Failed to parse `ParameterId`"), - Value::LengthLimits(LengthLimits::new(24, 42)), + LengthLimits::new(24, 42).into(), ), Parameter::new( ParameterId::from_str("Int").expect("Failed to parse `ParameterId`"), - Value::Numeric(numeric!(42)), + numeric!(42).into(), ), ]; @@ -646,49 +681,6 @@ pub mod model { ParameterId(parameter::ParameterId), } - /// Sized container for constructors of all [`Identifiable`]s that can be registered via transaction - #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - EnumRef, - FromVariant, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - #[enum_ref(derive(Encode, FromVariant))] - #[ffi_type] - pub enum RegistrableBox { - /// [`Peer`](`peer::Peer`) variant. - #[display(fmt = "Peer {_0}")] - Peer(::With), - /// [`Domain`](`domain::Domain`) variant. - #[display(fmt = "Domain {_0}")] - Domain(::With), - /// [`Account`](`account::Account`) variant. - #[display(fmt = "Account {_0}")] - Account(::With), - /// [`AssetDefinition`](`asset::AssetDefinition`) variant. - #[display(fmt = "AssetDefinition {_0}")] - AssetDefinition(::With), - /// [`Asset`](`asset::Asset`) variant. - #[display(fmt = "Asset {_0}")] - Asset(::With), - /// [`Trigger`](`trigger::Trigger`) variant. - #[display(fmt = "Trigger {_0}")] - Trigger( as Registered>::With), - /// [`Role`](`role::Role`) variant. - #[display(fmt = "Role {_0}")] - Role(::With), - } - /// Sized container for all possible entities. #[derive( Debug, @@ -735,167 +727,6 @@ pub mod model { Parameter(parameter::Parameter), } - /// Sized container for all possible upgradable entities. - #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - EnumRef, - FromVariant, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - #[enum_ref(derive(Encode, FromVariant))] - // SAFETY: `UpgradableBox` has no trap representations in `executor::Executor` - #[ffi_type(unsafe {robust})] - #[serde(untagged)] // Unaffected by #3330, because stores binary data with no `u128` - #[repr(transparent)] - pub enum UpgradableBox { - /// [`Executor`](`executor::Executor`) variant. - #[display(fmt = "Executor")] - Executor(executor::Executor), - } - - /// Sized container for all possible values. - #[derive( - DebugCustom, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - FromVariant, - Decode, - Encode, - PartiallyTaggedDeserialize, - PartiallyTaggedSerialize, - IntoSchema, - )] - #[allow(clippy::enum_variant_names, missing_docs)] - #[ffi_type(opaque)] - pub enum Value { - Bool(bool), - String(String), - Name(Name), - Vec( - #[skip_from] - #[skip_try_from] - Vec, - ), - LimitedMetadata(metadata::Metadata), - MetadataLimits(metadata::Limits), - TransactionLimits(transaction::TransactionLimits), - LengthLimits(LengthLimits), - #[serde_partially_tagged(untagged)] - Id(IdBox), - #[serde_partially_tagged(untagged)] - Identifiable(IdentifiableBox), - PublicKey(PublicKey), - SignatureCheckCondition(SignatureCheckCondition), - TransactionQueryOutput(TransactionQueryOutput), - PermissionToken(permission::PermissionToken), - PermissionTokenSchema(permission::PermissionTokenSchema), - Hash(HashValue), - Block(SignedBlockWrapper), - BlockHeader(block::BlockHeader), - Ipv4Addr(iroha_primitives::addr::Ipv4Addr), - Ipv6Addr(iroha_primitives::addr::Ipv6Addr), - #[serde_partially_tagged(untagged)] - #[debug(fmt = "{_0:?}")] - Numeric(Numeric), - // Workaround to allow integer values in parameter - #[serde_partially_tagged(untagged)] - #[debug(fmt = "{_0:?}")] - Integer(Integer), - Executor(executor::Executor), - LogLevel(Level), - } - - /// Enum for all supported hash types - #[derive( - Debug, - Display, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - #[ffi_type] - pub enum HashValue { - /// Transaction hash - Transaction(HashOf), - /// Block hash - Block(HashOf), - } - - /// Cross-platform wrapper for [`SignedBlock`]. - #[cfg(not(target_arch = "aarch64"))] - #[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - AsRef, - Deref, - From, - Into, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - // SAFETY: SignedBlockWrapper has no trap representations in SignedBlock - #[schema(transparent)] - #[ffi_type(unsafe {robust})] - #[serde(transparent)] - #[repr(transparent)] - pub struct SignedBlockWrapper(SignedBlock); - - /// Cross-platform wrapper for `BlockValue`. - #[cfg(target_arch = "aarch64")] - #[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - AsRef, - Deref, - From, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - )] - #[schema(transparent)] - #[as_ref(forward)] - #[deref(forward)] - #[from(forward)] - // SAFETY: SignedBlockWrapper has no trap representations in Box - #[ffi_type(unsafe {robust})] - #[serde(transparent)] - #[repr(transparent)] - pub struct SignedBlockWrapper(pub(super) Box); - /// Limits of length of the identifiers (e.g. in [`domain::Domain`], [`account::Account`], [`asset::AssetDefinition`]) in number of chars #[derive( Debug, @@ -1061,32 +892,6 @@ macro_rules! impl_encode_as_identifiable_box { } } -macro_rules! impl_encode_as_registrable_box { - ($($ty:ty),+ $(,)?) => { $( - impl $ty { - /// [`Encode`] [`Self`] as [`RegistrableBox`]. - /// - /// Used to avoid an unnecessary clone - pub fn encode_as_registrable_box(&self) -> Vec { - RegistrableBoxRef::from(self).encode() - } - } )+ - } -} - -macro_rules! impl_encode_as_upgradable_box { - ($($ty:ty),+ $(,)?) => { $( - impl $ty { - /// [`Encode`] [`Self`] as [`UpgradableBox`]. - /// - /// Used to avoid an unnecessary clone - pub fn encode_as_upgradable_box(&self) -> Vec { - UpgradableBoxRef::from(self).encode() - } - } )+ - } -} - impl_encode_as_id_box! { peer::PeerId, domain::DomainId, @@ -1114,20 +919,6 @@ impl_encode_as_identifiable_box! { parameter::Parameter, } -impl_encode_as_registrable_box! { - peer::Peer, - domain::NewDomain, - account::NewAccount, - asset::NewAssetDefinition, - asset::Asset, - trigger::Trigger, - role::NewRole, -} - -impl_encode_as_upgradable_box! { - executor::Executor, -} - impl Decode for ChainId { fn decode( input: &mut I, @@ -1188,463 +979,6 @@ impl<'idbox> TryFrom<&'idbox IdentifiableBox> for &'idbox dyn HasMetadata { } } -/// Create a [`Vec`] containing the arguments, which should satisfy `Into` bound. -/// -/// Syntax is the same as in [`vec`](macro@vec) -#[macro_export] -macro_rules! val_vec { - () => { Vec::new() }; - ($elem:expr; $n:expr) => { vec![$crate::Value::from($elem); $n] }; - ($($x:expr),+ $(,)?) => { vec![$($crate::Value::from($x),)+] }; -} - -#[cfg(target_arch = "aarch64")] -impl From for SignedBlock { - fn from(block_value: SignedBlockWrapper) -> Self { - *block_value.0 - } -} - -impl fmt::Display for Value { - // TODO: Maybe derive - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Value::Bool(v) => fmt::Display::fmt(&v, f), - Value::String(v) => fmt::Display::fmt(&v, f), - Value::Name(v) => fmt::Display::fmt(&v, f), - Value::Vec(v) => { - // TODO: Remove so we can derive. - let list_of_display: Vec<_> = v.iter().map(ToString::to_string).collect(); - // this prints with quotation marks, which is fine 90% - // of the time, and helps delineate where a display of - // one value stops and another one begins. - write!(f, "{list_of_display:?}") - } - Value::LimitedMetadata(v) => fmt::Display::fmt(&v, f), - Value::Id(v) => fmt::Display::fmt(&v, f), - Value::Identifiable(v) => fmt::Display::fmt(&v, f), - Value::PublicKey(v) => fmt::Display::fmt(&v, f), - Value::SignatureCheckCondition(v) => fmt::Display::fmt(&v, f), - Value::TransactionQueryOutput(_) => write!(f, "TransactionQueryOutput"), - Value::PermissionToken(v) => fmt::Display::fmt(&v, f), - Value::PermissionTokenSchema(v) => fmt::Display::fmt(&v, f), - Value::Hash(v) => fmt::Display::fmt(&v, f), - Value::Block(v) => fmt::Display::fmt(&**v, f), - Value::BlockHeader(v) => fmt::Display::fmt(&v, f), - Value::Ipv4Addr(v) => fmt::Display::fmt(&v, f), - Value::Ipv6Addr(v) => fmt::Display::fmt(&v, f), - Value::Numeric(v) => fmt::Display::fmt(&v, f), - Value::Integer(v) => fmt::Display::fmt(&v, f), - Value::MetadataLimits(v) => fmt::Display::fmt(&v, f), - Value::TransactionLimits(v) => fmt::Display::fmt(&v, f), - Value::LengthLimits(v) => fmt::Display::fmt(&v, f), - Value::Executor(v) => write!(f, "Executor({} bytes)", v.wasm.as_ref().len()), - Value::LogLevel(v) => fmt::Display::fmt(&v, f), - } - } -} - -#[allow(clippy::len_without_is_empty)] -impl Value { - /// Number of underneath expressions. - pub fn len(&self) -> usize { - use Value::*; - - match self { - Id(_) - | PublicKey(_) - | Bool(_) - | Identifiable(_) - | String(_) - | Name(_) - | TransactionQueryOutput(_) - | PermissionToken(_) - | PermissionTokenSchema(_) - | Hash(_) - | Block(_) - | Ipv4Addr(_) - | Ipv6Addr(_) - | BlockHeader(_) - | MetadataLimits(_) - | TransactionLimits(_) - | LengthLimits(_) - | Numeric(_) - | Integer(_) - | Executor(_) - | LogLevel(_) - | SignatureCheckCondition(_) => 1_usize, - Vec(v) => v.iter().map(Self::len).sum::() + 1_usize, - LimitedMetadata(data) => data.nested_len() + 1_usize, - } - } -} - -impl From for Value { - fn from(block_value: SignedBlock) -> Self { - Value::Block(block_value.into()) - } -} - -impl From> for Value -where - A::Item: Into, -{ - fn from(sv: SmallVec) -> Self { - // This looks inefficient, but `Value` can only hold a - // heap-allocated `Vec` (it's recursive) and the vector - // conversions only do a heap allocation (if that). - let vec: Vec<_> = sv.into_vec(); - vec.into() - } -} - -// TODO: The following macros looks very similar. Try to generalize them under one macro -macro_rules! from_and_try_from_value_idbox { - ( $($variant:ident( $ty:ty ),)+ $(,)? ) => { $( - impl TryFrom for $ty { - type Error = ErrorTryFromEnum; - - fn try_from(value: Value) -> Result { - if let Value::Id(IdBox::$variant(id)) = value { - Ok(id) - } else { - Err(Self::Error::default()) - } - } - } - - impl From<$ty> for Value { - fn from(id: $ty) -> Self { - Value::Id(IdBox::$variant(id)) - } - })+ - }; -} - -macro_rules! from_and_try_from_value_identifiable { - ( $( $variant:ident( $ty:ty ), )+ $(,)? ) => { $( - impl TryFrom for $ty { - type Error = ErrorTryFromEnum; - - fn try_from(value: Value) -> Result { - if let Value::Identifiable(IdentifiableBox::$variant(id)) = value { - Ok(id) - } else { - Err(Self::Error::default()) - } - } - } - - impl From<$ty> for Value { - fn from(id: $ty) -> Self { - Value::Identifiable(IdentifiableBox::$variant(id)) - } - } )+ - }; -} - -macro_rules! from_and_try_from_and_try_as_value_hash { - ( $( $variant:ident($ty:ty)),+ $(,)? ) => { $( - impl TryFrom for $ty { - type Error = ErrorTryFromEnum; - - #[inline] - fn try_from(value: Value) -> Result { - if let Value::Hash(HashValue::$variant(value)) = value { - Ok(value) - } else { - Err(Self::Error::default()) - } - } - } - - impl From<$ty> for Value { - #[inline] - fn from(value: $ty) -> Self { - Value::Hash(HashValue::$variant(value)) - } - } - - impl TryAsMut<$ty> for HashValue { - type Error = crate::EnumTryAsError<$ty, HashValue>; - - #[inline] - fn try_as_mut(&mut self) -> Result<&mut $ty, Self::Error> { - if let HashValue::$variant (value) = self { - Ok(value) - } else { - Err(crate::EnumTryAsError::got(*self)) - } - } - } - - impl TryAsRef<$ty> for HashValue { - type Error = crate::EnumTryAsError<$ty, HashValue>; - - #[inline] - fn try_as_ref(&self) -> Result<& $ty, Self::Error> { - if let HashValue::$variant (value) = self { - Ok(value) - } else { - Err(crate::EnumTryAsError::got(*self)) - } - } - })+ - }; -} - -macro_rules! from_and_try_from_and_try_as_value_integer { - ( $( $variant:ident($ty:ty),)+ $(,)? ) => { $( - impl TryFrom for $ty { - type Error = ErrorTryFromEnum; - - #[inline] - fn try_from(value: Value) -> Result { - if let Value::Integer(Integer::$variant(value)) = value { - Ok(value) - } else { - Err(Self::Error::default()) - } - } - } - - impl From<$ty> for Value { - #[inline] - fn from(value: $ty) -> Self { - Value::Integer(Integer::$variant(value)) - } - } - - impl TryAsMut<$ty> for Integer { - type Error = crate::EnumTryAsError<$ty, Integer>; - - #[inline] - fn try_as_mut(&mut self) -> Result<&mut $ty, Self::Error> { - if let Integer:: $variant (value) = self { - Ok(value) - } else { - Err(crate::EnumTryAsError::got(*self)) - } - } - } - - impl TryAsRef<$ty> for Integer { - type Error = crate::EnumTryAsError<$ty, Integer>; - - #[inline] - fn try_as_ref(&self) -> Result<& $ty, Self::Error> { - if let Integer:: $variant (value) = self { - Ok(value) - } else { - Err(crate::EnumTryAsError::got(*self)) - } - } - })+ - }; -} - -from_and_try_from_value_idbox!( - PeerId(peer::PeerId), - DomainId(domain::DomainId), - AccountId(account::AccountId), - AssetId(asset::AssetId), - AssetDefinitionId(asset::AssetDefinitionId), - TriggerId(trigger::TriggerId), - RoleId(role::RoleId), - ParameterId(parameter::ParameterId), - // TODO: Should we wrap String with new type in order to convert like here? - //from_and_try_from_value_idbox!((DomainName(Name), ErrorValueTryFromDomainName),); -); - -from_and_try_from_value_identifiable!( - NewDomain(domain::NewDomain), - NewAccount(account::NewAccount), - NewAssetDefinition(asset::NewAssetDefinition), - NewRole(role::NewRole), - Peer(peer::Peer), - Domain(domain::Domain), - Account(account::Account), - AssetDefinition(asset::AssetDefinition), - Asset(asset::Asset), - Trigger(trigger::Trigger), - Role(role::Role), - Parameter(parameter::Parameter), -); - -from_and_try_from_and_try_as_value_hash! { - Transaction(HashOf), - Block(HashOf), -} - -from_and_try_from_and_try_as_value_integer! { - U32(u32), - U64(u64), - U128(u128), -} - -impl TryFrom for RegistrableBox { - type Error = ErrorTryFromEnum; - - fn try_from(source: Value) -> Result { - if let Value::Identifiable(identifiable) = source { - identifiable - .try_into() - .map_err(|_err| Self::Error::default()) - } else { - Err(Self::Error::default()) - } - } -} - -impl From for Value { - fn from(source: RegistrableBox) -> Self { - let identifiable = source.into(); - Value::Identifiable(identifiable) - } -} - -impl TryFrom for RegistrableBox { - type Error = ErrorTryFromEnum; - - fn try_from(source: IdentifiableBox) -> Result { - use IdentifiableBox::*; - - match source { - Peer(peer) => Ok(RegistrableBox::Peer(peer)), - NewDomain(domain) => Ok(RegistrableBox::Domain(domain)), - NewAccount(account) => Ok(RegistrableBox::Account(account)), - NewAssetDefinition(asset_definition) => { - Ok(RegistrableBox::AssetDefinition(asset_definition)) - } - NewRole(role) => Ok(RegistrableBox::Role(role)), - Asset(asset) => Ok(RegistrableBox::Asset(asset)), - Trigger(trigger) => Ok(RegistrableBox::Trigger(trigger)), - Domain(_) | Account(_) | AssetDefinition(_) | Role(_) | Parameter(_) => { - Err(Self::Error::default()) - } - } - } -} - -impl From for IdentifiableBox { - fn from(registrable: RegistrableBox) -> Self { - use RegistrableBox::*; - - match registrable { - Peer(peer) => IdentifiableBox::Peer(peer), - Domain(domain) => IdentifiableBox::NewDomain(domain), - Account(account) => IdentifiableBox::NewAccount(account), - AssetDefinition(asset_definition) => { - IdentifiableBox::NewAssetDefinition(asset_definition) - } - Role(role) => IdentifiableBox::NewRole(role), - Asset(asset) => IdentifiableBox::Asset(asset), - Trigger(trigger) => IdentifiableBox::Trigger(trigger), - } - } -} - -impl> From> for Value { - fn from(values: Vec) -> Value { - Value::Vec(values.into_iter().map(Into::into).collect()) - } -} - -impl TryFrom for Vec -where - Value: TryInto, -{ - type Error = ErrorTryFromEnum; - - fn try_from(value: Value) -> Result { - if let Value::Vec(vec) = value { - return vec - .into_iter() - .map(TryInto::try_into) - .collect::, _>>() - .map_err(|_e| Self::Error::default()); - } - - Err(Self::Error::default()) - } -} - -impl TryFrom for SignedBlock { - type Error = ErrorTryFromEnum; - - fn try_from(value: Value) -> Result { - if let Value::Block(block_value) = value { - return Ok(block_value.into()); - } - - Err(Self::Error::default()) - } -} - -impl TryFrom for SmallVec -where - Value: TryInto, -{ - type Error = ErrorTryFromEnum; - - fn try_from(value: Value) -> Result { - if let Value::Vec(vec) = value { - return vec - .into_iter() - .map(TryInto::try_into) - .collect::, _>>() - .map_err(|_e| Self::Error::default()); - } - Err(Self::Error::default()) - } -} - -impl TryFrom for UpgradableBox { - type Error = ErrorTryFromEnum; - - fn try_from(value: Value) -> Result { - match value { - Value::Executor(executor) => Ok(Self::Executor(executor)), - _ => Err(Self::Error::default()), - } - } -} - -/// Represent type which can be converted into [`Value`] infallibly. -/// This trait can be used when type inference can't properly inference desired type. -pub trait ToValue { - /// Convert [`Self`] into [`Value`]. - fn to_value(self) -> Value; -} - -/// Represent type which can be converted into `Value` with possibility of failure. -/// This trait can be used when type inference can't properly inference desired type. -pub trait TryToValue { - /// Type which represents conversation error. - type Error; - /// Try convert [`Self`] into [`Value`]. - /// - /// # Errors - /// Fail when it is not possible to convert [`Self`] into `Value` - fn try_to_value(self) -> Result; -} - -impl> ToValue for T { - #[inline] - fn to_value(self) -> Value { - self.into() - } -} - -impl> TryToValue for T { - type Error = T::Error; - - #[inline] - fn try_to_value(self) -> Result { - self.try_into() - } -} - /// Uniquely identifiable entity ([`Domain`], [`Account`], etc.). /// This trait should always be derived with [`IdEqOrdHash`] pub trait Identifiable: Ord + Eq { @@ -1670,7 +1004,7 @@ pub trait Registered: Identifiable { /// `Self`, but if you have a complex structure where most fields /// would be empty, to save space you create a builder for it, and /// set `With` to the builder's type. - type With: Into; + type With; } impl LengthLimits { @@ -1687,244 +1021,6 @@ impl From for RangeInclusive { } } -/// Trait for boolean-like values -/// -/// [`or`](`Self::or`) and [`and`](`Self::and`) must satisfy De Morgan's laws, commutativity and associativity -/// [`Not`](`core::ops::Not`) implementation should satisfy double negation elimintation. -/// -/// Short-circuiting behaviour for `and` and `or` can be controlled by returning -/// `ControlFlow::Break` when subsequent application of the same operation -/// won't change the end result, no matter what operands. -/// -/// When implementing, it's recommended to generate exhaustive tests with -/// [`test_conformity`](`Self::test_conformity`). -pub trait PredicateSymbol -where - Self: Sized + core::ops::Not, -{ - /// Conjunction (e.g. boolean and) - #[must_use] - fn and(self, other: Self) -> ControlFlow; - /// Disjunction (e.g. boolean or) - #[must_use] - fn or(self, other: Self) -> ControlFlow; - - #[doc(hidden)] - #[must_use] - fn unwrapped_and(self, other: Self) -> Self { - match self.and(other) { - ControlFlow::Continue(val) | ControlFlow::Break(val) => val, - } - } - - #[doc(hidden)] - #[must_use] - fn unwrapped_or(self, other: Self) -> Self { - match self.or(other) { - ControlFlow::Continue(val) | ControlFlow::Break(val) => val, - } - } - - /// Given a list of all possible values of a type implementing [`PredicateSymbol`] - /// which are different in predicate context, exhaustively tests for: - /// - commutativity of `and` and `or` - /// - associativity of `and` and `or` - /// - De Mornan duality of `and` and `or` - /// - double negation elimination - /// - /// # Examples - /// - /// ```rust - /// use iroha_data_model::PredicateSymbol; - /// - /// fn test() { - /// PredicateSymbol::test_conformity(vec![true, false]); - /// } - /// ``` - /// - fn test_conformity(values: Vec) - where - Self: PartialEq + Clone, - { - Self::test_conformity_with_eq(values, ::eq); - } - - /// Same as [`test_conformity`](`PredicateSymbol::test_conformity`), but - /// if type implementing [`PredicateSymbol`] carries some internal state - /// that isn't associative, one can provide custom `shallow_eq` function - /// that will be called instead of [`PartialEq::eq`] - /// - /// # Examples - /// - /// - /// ``` - /// use std::ops::ControlFlow; - /// use iroha_data_model::PredicateSymbol; - /// - /// #[derive(Clone, PartialEq)] - /// enum Check { - /// Good, - /// // Encapsulates reason for badness which - /// // doesn't behave associatively - /// // (but if we ignore it, Check as a whole does) - /// Bad(String), - /// } - /// - /// impl core::ops::Not for Check { - /// type Output = Self; - /// fn not(self) -> Self { - /// // ... - /// todo!() - /// } - /// } - /// - /// impl PredicateSymbol for Check { - /// fn and(self, other: Self) -> ControlFlow { - /// // ... - /// todo!() - /// } - /// - /// fn or(self, other: Self) -> ControlFlow { - /// // ... - /// todo!() - /// } - /// } - /// - /// fn shallow_eq(left: &Check, right: &Check) -> bool { - /// match (left, right) { - /// (Check::Good, Check::Good) | (Check::Bad(_), Check::Bad(_)) => true, - /// _ => false - /// } - /// } - /// - /// fn test() { - /// let good = Check::Good; - /// let bad = Check::Bad("example".to_owned()); - /// // Would fail some assertions, since derived PartialEq is "deep" - /// // PredicateSymbol::test_conformity(vec![good, bad]); - /// - /// // Works as expected - /// PredicateSymbol::test_conformity_with_eq(vec![good, bad], shallow_eq); - /// } - /// ``` - fn test_conformity_with_eq(values: Vec, shallow_eq: impl FnMut(&Self, &Self) -> bool) - where - Self: Clone, - { - let mut eq = shallow_eq; - let values = values - .into_iter() - .map(|val| move || val.clone()) - .collect::>(); - - let typ = core::any::type_name::(); - - for a in &values { - assert!( - eq(&a().not().not(), &a()), - "Double negation elimination doesn't hold for {typ}", - ); - } - - for a in &values { - for b in &values { - assert!( - eq( - &PredicateSymbol::unwrapped_and(a(), b()), - &PredicateSymbol::unwrapped_and(b(), a()) - ), - "Commutativity doesn't hold for `PredicateSymbol::and` implementation for {typ}" - ); - - assert!( - eq( - &PredicateSymbol::unwrapped_or(a(), b()), - &PredicateSymbol::unwrapped_or(b(), a()) - ), - "Commutativity doesn't hold for `PredicateSymbol::or` implementation for {typ}" - ); - - assert!( - eq( - &PredicateSymbol::unwrapped_or(!a(), !b()), - &!PredicateSymbol::unwrapped_and(a(), b()) - ), - "De Morgan's law doesn't hold for {typ}", - ); - - assert!( - eq( - &PredicateSymbol::unwrapped_and(!a(), !b()), - &!PredicateSymbol::unwrapped_or(a(), b()) - ), - "De Morgan's law doesn't hold for {typ}", - ); - } - } - - for a in &values { - for b in &values { - for c in &values { - assert!( - eq( - &PredicateSymbol::unwrapped_and( - PredicateSymbol::unwrapped_and(a(), b()), - c() - ), - &PredicateSymbol::unwrapped_and( - a(), - PredicateSymbol::unwrapped_and(b(), c()), - ), - ), - "Associativity doesn't hold for `PredicateSymbol::or` implementation for {typ}", - ); - - assert!( - eq( - &PredicateSymbol::unwrapped_or( - PredicateSymbol::unwrapped_or(a(), b()), - c() - ), - &PredicateSymbol::unwrapped_or( - a(), - PredicateSymbol::unwrapped_or(b(), c()), - ), - ), - "Associativity doesn't hold for `PredicateSymbol::and` implementation for {typ}", - ); - } - } - } - } -} - -impl PredicateSymbol for bool { - fn and(self, other: Self) -> ControlFlow { - if self && other { - ControlFlow::Continue(true) - } else { - ControlFlow::Break(false) - } - } - - fn or(self, other: Self) -> ControlFlow { - if self || other { - ControlFlow::Break(true) - } else { - ControlFlow::Continue(false) - } - } -} - -/// Trait for generic predicates. -pub trait PredicateTrait { - /// Type the predicate evaluates to. - type EvaluatesTo: PredicateSymbol; - - /// The result of applying the predicate to a value. - fn applies(&self, input: T) -> Self::EvaluatesTo; -} - /// Get the current system time as `Duration` since the unix epoch. #[cfg(feature = "std")] pub fn current_time() -> core::time::Duration { @@ -1996,8 +1092,6 @@ pub mod prelude { executor::prelude::*, isi::prelude::*, metadata::prelude::*, name::prelude::*, parameter::prelude::*, peer::prelude::*, permission::prelude::*, query::prelude::*, role::prelude::*, transaction::prelude::*, trigger::prelude::*, ChainId, EnumTryAsError, - HasMetadata, IdBox, Identifiable, IdentifiableBox, Integer, LengthLimits, PredicateTrait, - RegistrableBox, ToValue, TryAsMut, TryAsRef, TryToValue, UpgradableBox, ValidationFail, - Value, + HasMetadata, IdBox, Identifiable, IdentifiableBox, LengthLimits, ValidationFail, }; } diff --git a/data_model/src/metadata.rs b/data_model/src/metadata.rs index 5ddc6c595cd..871f14d9c51 100644 --- a/data_model/src/metadata.rs +++ b/data_model/src/metadata.rs @@ -1,27 +1,92 @@ //! Metadata: key-value pairs that can be attached to accounts, transactions and assets. #[cfg(not(feature = "std"))] -use alloc::{collections::btree_map, format, string::String, vec::Vec}; +use alloc::{ + collections::btree_map, + format, + string::{String, ToString}, + vec::Vec, +}; use core::borrow::Borrow; #[cfg(feature = "std")] use std::collections::btree_map; use derive_more::Display; use iroha_data_model_derive::model; +use iroha_macro::FromVariant; +use iroha_primitives::numeric::Numeric; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; pub use self::model::*; -use crate::{Name, Value}; +use crate::Name; + +/// A path slice, composed of [`Name`]s. +pub type Path = [Name]; /// Collection of parameters by their names. -pub type UnlimitedMetadata = btree_map::BTreeMap; +pub type UnlimitedMetadata = btree_map::BTreeMap; #[model] pub mod model { use super::*; + /// Collection of parameters by their names with checked insertion. + #[derive( + Debug, + Display, + Clone, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Deserialize, + Serialize, + Decode, + Encode, + IntoSchema, + )] + #[ffi_type(opaque)] + #[repr(transparent)] + #[serde(transparent)] + #[display(fmt = "Metadata")] + #[allow(clippy::multiple_inherent_impl)] + pub struct Metadata(pub(super) btree_map::BTreeMap); + + /// Metadata value + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + FromVariant, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + )] + #[ffi_type(opaque)] + #[allow(missing_docs)] + pub enum MetadataValueBox { + Bool(bool), + String(String), + Name(Name), + Bytes(Vec), + Numeric(Numeric), + LimitedMetadata(Metadata), + + Vec( + #[skip_from] + #[skip_try_from] + Vec, + ), + } + /// Limits for [`Metadata`]. #[derive( Debug, @@ -38,8 +103,8 @@ pub mod model { Serialize, IntoSchema, )] - #[display(fmt = "{capacity},{max_entry_len}_ML")] #[ffi_type] + #[display(fmt = "{capacity},{max_entry_len}_ML")] pub struct Limits { /// Maximum number of entries pub capacity: u32, @@ -47,82 +112,61 @@ pub mod model { pub max_entry_len: u32, } - /// Collection of parameters by their names with checked insertion. + /// Metadata related errors. #[derive( Debug, - Display, + displaydoc::Display, Clone, - Default, PartialEq, Eq, PartialOrd, Ord, + Decode, + Encode, Deserialize, Serialize, + IntoSchema, + )] + #[ffi_type(local)] + #[cfg_attr(feature = "std", derive(thiserror::Error))] + pub enum MetadataError { + /// Path specification empty + EmptyPath, + /// Metadata entry is too big + EntryTooBig(#[cfg_attr(feature = "std", source)] SizeError), + /// Metadata exceeds overall length limit + MaxCapacity(#[cfg_attr(feature = "std", source)] SizeError), + /// `{0}`: path segment not found, i.e. nothing was found at that key + MissingSegment(Name), + /// `{0}`: path segment not an instance of metadata + InvalidSegment(Name), + } + + /// Size limits exhaustion error + #[derive( + Debug, + Display, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, Decode, Encode, + Deserialize, + Serialize, IntoSchema, )] - #[allow(clippy::multiple_inherent_impl)] - #[display(fmt = "Metadata")] - #[ffi_type(opaque)] - #[serde(transparent)] - #[repr(transparent)] - pub struct Metadata(pub(super) btree_map::BTreeMap); -} - -/// Metadata related errors. -#[derive( - Debug, - displaydoc::Display, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, -)] -#[cfg_attr(feature = "std", derive(thiserror::Error))] -pub enum MetadataError { - /// Path specification empty - EmptyPath, - /// Metadata entry is too big - EntryTooBig(#[cfg_attr(feature = "std", source)] SizeError), - /// Metadata exceeds overall length limit - MaxCapacity(#[cfg_attr(feature = "std", source)] SizeError), - /// `{0}`: path segment not found, i.e. nothing was found at that key - MissingSegment(Name), - /// `{0}`: path segment not an instance of metadata - InvalidSegment(Name), -} - -/// Size limits exhaustion error -#[derive( - Debug, - Display, - Copy, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, -)] -#[display(fmt = "Limits are {limits}, while the actual value is {actual}")] -#[cfg_attr(feature = "std", derive(thiserror::Error))] -pub struct SizeError { - /// The limits that were set for this entry - limits: Limits, - /// The actual *entry* size in bytes - actual: u64, + #[ffi_type] + #[cfg_attr(feature = "std", derive(thiserror::Error))] + #[display(fmt = "Limits are {limits}, while the actual value is {actual}")] + pub struct SizeError { + /// The limits that were set for this entry + pub limits: Limits, + /// The actual *entry* size in bytes + pub actual: u64, + } } impl Limits { @@ -135,8 +179,106 @@ impl Limits { } } -/// A path slice, composed of [`Name`]s. -pub type Path = [Name]; +impl From for MetadataValueBox { + fn from(value: u32) -> Self { + Self::Numeric(value.into()) + } +} + +impl From for MetadataValueBox { + fn from(value: u64) -> Self { + Self::Numeric(value.into()) + } +} + +impl TryFrom for u32 { + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: MetadataValueBox) -> Result { + use iroha_macro::error::ErrorTryFromEnum; + + let MetadataValueBox::Numeric(numeric) = value else { + return Err(ErrorTryFromEnum::default()); + }; + + numeric.try_into().map_err(|_| ErrorTryFromEnum::default()) + } +} + +impl TryFrom for u64 { + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: MetadataValueBox) -> Result { + use iroha_macro::error::ErrorTryFromEnum; + + let MetadataValueBox::Numeric(numeric) = value else { + return Err(ErrorTryFromEnum::default()); + }; + + numeric.try_into().map_err(|_| ErrorTryFromEnum::default()) + } +} + +impl> From> for MetadataValueBox { + fn from(values: Vec) -> MetadataValueBox { + MetadataValueBox::Vec(values.into_iter().map(Into::into).collect()) + } +} + +impl TryFrom for Vec +where + MetadataValueBox: TryInto, +{ + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: MetadataValueBox) -> Result { + if let MetadataValueBox::Vec(vec) = value { + return vec + .into_iter() + .map(TryInto::try_into) + .collect::, _>>() + .map_err(|_e| Self::Error::default()); + } + + Err(Self::Error::default()) + } +} + +impl core::fmt::Display for MetadataValueBox { + // TODO: Maybe derive + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + MetadataValueBox::Bool(v) => core::fmt::Display::fmt(&v, f), + MetadataValueBox::String(v) => core::fmt::Display::fmt(&v, f), + MetadataValueBox::Name(v) => core::fmt::Display::fmt(&v, f), + MetadataValueBox::Numeric(v) => core::fmt::Display::fmt(&v, f), + MetadataValueBox::Bytes(v) => write!(f, "{v:?}"), + MetadataValueBox::Vec(v) => { + // TODO: Remove so we can derive. + let list_of_display: Vec<_> = v.iter().map(ToString::to_string).collect(); + // this prints with quotation marks, which is fine 90% + // of the time, and helps delineate where a display of + // one value stops and another one begins. + write!(f, "{list_of_display:?}") + } + MetadataValueBox::LimitedMetadata(v) => core::fmt::Display::fmt(&v, f), + } + } +} + +#[allow(clippy::len_without_is_empty)] +impl MetadataValueBox { + /// Number of underneath expressions. + fn len(&self) -> usize { + use MetadataValueBox::*; + + match self { + Bool(_) | String(_) | Name(_) | Bytes(_) | Numeric(_) => 1, + Vec(v) => v.iter().map(Self::len).sum::() + 1, + LimitedMetadata(data) => data.nested_len() + 1, + } + } +} impl Metadata { /// Constructor. @@ -155,12 +297,12 @@ impl Metadata { /// incorrect (if e.g. any of interior path segments are not /// [`Metadata`] instances return `None`. Else borrow the value /// corresponding to that path. - pub fn nested_get(&self, path: &Path) -> Option<&Value> { + pub fn nested_get(&self, path: &Path) -> Option<&MetadataValueBox> { let key = path.last()?; let mut map = &self.0; for k in path.iter().take(path.len() - 1) { map = match map.get(k)? { - Value::LimitedMetadata(data) => &data.0, + MetadataValueBox::LimitedMetadata(data) => &data.0, _ => return None, }; } @@ -173,13 +315,13 @@ impl Metadata { } /// Iterate over key/value pairs stored in the internal map. - pub fn iter(&self) -> impl ExactSizeIterator { + pub fn iter(&self) -> impl ExactSizeIterator { self.0.iter() } /// Get the `Some(&Value)` associated to `key`. Return `None` if not found. #[inline] - pub fn get(&self, key: &K) -> Option<&Value> + pub fn get(&self, key: &K) -> Option<&MetadataValueBox> where Name: Borrow, { @@ -204,9 +346,9 @@ impl Metadata { pub fn nested_insert_with_limits( &mut self, path: &Path, - value: Value, + value: impl Into, limits: Limits, - ) -> Result, MetadataError> { + ) -> Result, MetadataError> { if self.0.len() >= limits.capacity as usize { return Err(MetadataError::MaxCapacity(SizeError { limits, @@ -221,7 +363,7 @@ impl Metadata { .get_mut(k) .ok_or_else(|| MetadataError::MissingSegment(k.clone()))? { - Value::LimitedMetadata(data) => data, + MetadataValueBox::LimitedMetadata(data) => data, _ => return Err(MetadataError::InvalidSegment(k.clone())), }; } @@ -236,9 +378,11 @@ impl Metadata { pub fn insert_with_limits( &mut self, key: Name, - value: Value, + value: impl Into, limits: Limits, - ) -> Result, MetadataError> { + ) -> Result, MetadataError> { + let value = value.into(); + if self.0.len() >= limits.capacity as usize && !self.0.contains_key(&key) { return Err(MetadataError::MaxCapacity(SizeError { limits, @@ -256,7 +400,7 @@ impl Metadata { /// `Some(value)` at the key if the key was previously in the /// map, else `None`. #[inline] - pub fn remove(&mut self, key: &K) -> Option + pub fn remove(&mut self, key: &K) -> Option where Name: Borrow, { @@ -267,12 +411,12 @@ impl Metadata { /// malformed, or incorrect (if e.g. any of interior path segments /// are not [`Metadata`] instances) return `None`. Else return the /// owned value corresponding to that path. - pub fn nested_remove(&mut self, path: &Path) -> Option { + pub fn nested_remove(&mut self, path: &Path) -> Option { let key = path.last()?; let mut map = &mut self.0; for k in path.iter().take(path.len() - 1) { map = match map.get_mut(k)? { - Value::LimitedMetadata(data) => &mut data.0, + MetadataValueBox::LimitedMetadata(data) => &mut data.0, _ => return None, }; } @@ -280,7 +424,11 @@ impl Metadata { } } -fn check_size_limits(key: &Name, value: Value, limits: Limits) -> Result<(), MetadataError> { +fn check_size_limits( + key: &Name, + value: MetadataValueBox, + limits: Limits, +) -> Result<(), MetadataError> { let entry_bytes: Vec = (key, value).encode(); let byte_size = entry_bytes.len(); if byte_size > limits.max_entry_len as usize { @@ -305,8 +453,6 @@ mod tests { use alloc::{borrow::ToOwned as _, vec}; use core::str::FromStr as _; - use iroha_macro::FromVariant; - use super::*; use crate::ParseError; @@ -323,7 +469,7 @@ mod tests { let empty_path = vec![]; assert!(metadata.nested_get(&empty_path).is_none()); assert!(metadata - .nested_insert_with_limits(&empty_path, "0".to_owned().into(), Limits::new(12, 12)) + .nested_insert_with_limits(&empty_path, "0".to_owned(), Limits::new(12, 12)) .is_err()); #[cfg(feature = "transparent_api")] assert!(metadata.nested_remove(&empty_path).is_none()); @@ -336,12 +482,12 @@ mod tests { let limits = Limits::new(1024, 1024); // TODO: If we allow a `unsafe`, we could create the path. metadata - .insert_with_limits(Name::from_str("0")?, Metadata::new().into(), limits) + .insert_with_limits(Name::from_str("0")?, Metadata::new(), limits) .expect("Valid"); metadata .nested_insert_with_limits( &[Name::from_str("0")?, Name::from_str("1")?], - Metadata::new().into(), + Metadata::new(), limits, ) .expect("Valid"); @@ -351,11 +497,11 @@ mod tests { Name::from_str("2")?, ]; metadata - .nested_insert_with_limits(&path, "Hello World".to_owned().into(), limits) + .nested_insert_with_limits(&path, "Hello World".to_owned(), limits) .expect("Valid"); assert_eq!( *metadata.nested_get(&path).expect("Valid"), - Value::from("Hello World".to_owned()) + MetadataValueBox::from("Hello World".to_owned()) ); assert_eq!(metadata.nested_len(), 6); // Three nested path segments. metadata.nested_remove(&path); @@ -367,10 +513,10 @@ mod tests { fn non_existent_path_segment_fails() -> Result<(), TestError> { let mut metadata = Metadata::new(); let limits = Limits::new(10, 15); - metadata.insert_with_limits(Name::from_str("0")?, Metadata::new().into(), limits)?; + metadata.insert_with_limits(Name::from_str("0")?, Metadata::new(), limits)?; metadata.nested_insert_with_limits( &[Name::from_str("0")?, Name::from_str("1")?], - Metadata::new().into(), + Metadata::new(), limits, )?; let path = vec![ @@ -378,14 +524,14 @@ mod tests { Name::from_str("1")?, Name::from_str("2")?, ]; - metadata.nested_insert_with_limits(&path, "Hello World".to_owned().into(), limits)?; + metadata.nested_insert_with_limits(&path, "Hello World".to_owned(), limits)?; let bad_path = vec![ Name::from_str("0")?, Name::from_str("3")?, Name::from_str("2")?, ]; assert!(metadata - .nested_insert_with_limits(&bad_path, "Hello World".to_owned().into(), limits) + .nested_insert_with_limits(&bad_path, "Hello World".to_owned(), limits) .is_err()); assert!(metadata.nested_get(&bad_path).is_none()); #[cfg(feature = "transparent_api")] @@ -398,11 +544,11 @@ mod tests { let mut metadata = Metadata::new(); let limits = Limits::new(10, 14); // TODO: If we allow a `unsafe`, we could create the path. - metadata.insert_with_limits(Name::from_str("0")?, Metadata::new().into(), limits)?; + metadata.insert_with_limits(Name::from_str("0")?, Metadata::new(), limits)?; metadata .nested_insert_with_limits( &[Name::from_str("0")?, Name::from_str("1")?], - Metadata::new().into(), + Metadata::new(), limits, ) .expect("Valid"); @@ -412,7 +558,7 @@ mod tests { Name::from_str("2")?, ]; let failing_insert = - metadata.nested_insert_with_limits(&path, "Hello World".to_owned().into(), limits); + metadata.nested_insert_with_limits(&path, "Hello World".to_owned(), limits); assert!(failing_insert.is_err()); Ok(()) @@ -423,10 +569,10 @@ mod tests { let mut metadata = Metadata::new(); let limits = Limits::new(10, 5); assert!(metadata - .insert_with_limits(Name::from_str("1")?, "2".to_owned().into(), limits) + .insert_with_limits(Name::from_str("1")?, "2".to_owned(), limits) .is_ok()); assert!(metadata - .insert_with_limits(Name::from_str("1")?, "23456".to_owned().into(), limits) + .insert_with_limits(Name::from_str("1")?, "23456".to_owned(), limits) .is_err()); Ok(()) } @@ -437,16 +583,16 @@ mod tests { let mut metadata = Metadata::new(); let limits = Limits::new(2, 5); assert!(metadata - .insert_with_limits(Name::from_str("1")?, "0".to_owned().into(), limits) + .insert_with_limits(Name::from_str("1")?, "0".to_owned(), limits) .is_ok()); assert!(metadata - .insert_with_limits(Name::from_str("2")?, "0".to_owned().into(), limits) + .insert_with_limits(Name::from_str("2")?, "0".to_owned(), limits) .is_ok()); assert!(metadata - .insert_with_limits(Name::from_str("2")?, "1".to_owned().into(), limits) + .insert_with_limits(Name::from_str("2")?, "1".to_owned(), limits) .is_ok()); assert!(metadata - .insert_with_limits(Name::from_str("3")?, "0".to_owned().into(), limits) + .insert_with_limits(Name::from_str("3")?, "0".to_owned(), limits) .is_err()); Ok(()) } diff --git a/data_model/src/peer.rs b/data_model/src/peer.rs index f5da00a8bb5..695d61823f0 100644 --- a/data_model/src/peer.rs +++ b/data_model/src/peer.rs @@ -16,7 +16,7 @@ use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; pub use self::model::*; -use crate::{Identifiable, PublicKey, Registered, Value}; +use crate::{Identifiable, PublicKey, Registered}; #[model] pub mod model { @@ -113,15 +113,6 @@ impl Registered for Peer { type With = Self; } -impl FromIterator for Value { - fn from_iter>(iter: T) -> Self { - iter.into_iter() - .map(Into::into) - .collect::>() - .into() - } -} - /// The prelude re-exports most commonly used traits, structs and macros from this crate. pub mod prelude { pub use super::{Peer, PeerId}; diff --git a/data_model/src/query/mod.rs b/data_model/src/query/mod.rs index 8fe52246a37..9f3e402f8fd 100644 --- a/data_model/src/query/mod.rs +++ b/data_model/src/query/mod.rs @@ -4,9 +4,8 @@ #[cfg(not(feature = "std"))] use alloc::{ - boxed::Box, format, - string::{String, ToString as _}, + string::{String, ToString}, vec, vec::Vec, }; @@ -16,7 +15,7 @@ pub use cursor::ForwardCursor; use derive_more::{Constructor, Display}; use iroha_crypto::{PublicKey, SignatureOf}; use iroha_data_model_derive::{model, EnumRef}; -use iroha_macro::FromVariant; +use iroha_primitives::{numeric::Numeric, small::SmallVec}; use iroha_schema::IntoSchema; use iroha_version::prelude::*; pub use pagination::Pagination; @@ -31,15 +30,18 @@ use self::{ }; use crate::{ account::{Account, AccountId}, - block::SignedBlock, + block::{BlockHeader, SignedBlock}, events::TriggeringFilterBox, + metadata::MetadataValueBox, seal, transaction::{SignedTransaction, TransactionPayload, TransactionValue}, - Identifiable, Value, + IdBox, Identifiable, IdentifiableBox, }; pub mod cursor; pub mod pagination; +#[cfg(feature = "http")] +pub mod predicate; pub mod sorting; const FETCH_SIZE: &str = "fetch_size"; @@ -103,7 +105,7 @@ pub type QueryId = String; /// Trait for typesafe query output pub trait Query: Into + seal::Sealed { /// Output type of query - type Output: Into + TryFrom; + type Output: Into + TryFrom; /// [`Encode`] [`Self`] as [`QueryBox`]. /// @@ -115,6 +117,7 @@ pub trait Query: Into + seal::Sealed { pub mod model { use getset::Getters; use iroha_crypto::HashOf; + use iroha_macro::FromVariant; use strum::EnumDiscriminants; use super::*; @@ -191,47 +194,64 @@ pub mod model { FindAllParameters(FindAllParameters), } - /// Output of [`FindAllTransactions`] query + /// Sized container for all possible [`Query::Output`]s #[derive( Debug, Clone, - PartialOrd, - Ord, PartialEq, Eq, - Getters, + PartialOrd, + Ord, + FromVariant, Decode, Encode, Deserialize, Serialize, IntoSchema, )] - #[getset(get = "pub")] - #[ffi_type] - pub struct TransactionQueryOutput { - /// The hash of the block to which `tx` belongs to - pub block_hash: HashOf, - /// Transaction - #[getset(skip)] - pub transaction: Box, + #[allow(missing_docs)] + pub enum QueryOutputBox { + Id(IdBox), + Identifiable(IdentifiableBox), + Transaction(TransactionQueryOutput), + PermissionToken(crate::permission::PermissionToken), + PermissionTokenSchema(crate::permission::PermissionTokenSchema), + LimitedMetadata(MetadataValueBox), + Numeric(Numeric), + BlockHeader(BlockHeader), + Block(crate::block::SignedBlock), + + Vec( + #[skip_from] + #[skip_try_from] + Vec, + ), } - /// Type returned from [`Metadata`] queries + /// Output of [`FindAllTransactions`] query #[derive( Debug, Clone, - PartialEq, - Eq, PartialOrd, Ord, + PartialEq, + Eq, + Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, )] + #[getset(get = "pub")] #[ffi_type] - pub struct MetadataValue(pub Value); + pub struct TransactionQueryOutput { + /// The hash of the block to which `tx` belongs to + pub block_hash: HashOf, + /// Transaction + #[getset(skip)] + pub transaction: TransactionValue, + } /// Request type clients (like http clients or wasm) can send to a query endpoint. #[derive(Debug, Clone, Encode, Decode, Serialize, Deserialize)] @@ -260,6 +280,46 @@ pub mod model { } } +impl From for QueryOutputBox { + fn from(value: u32) -> Self { + Self::Numeric(value.into()) + } +} + +impl From for QueryOutputBox { + fn from(value: u64) -> Self { + Self::Numeric(value.into()) + } +} + +impl TryFrom for u32 { + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: QueryOutputBox) -> Result { + use iroha_macro::error::ErrorTryFromEnum; + + let QueryOutputBox::Numeric(numeric) = value else { + return Err(ErrorTryFromEnum::default()); + }; + + numeric.try_into().map_err(|_| ErrorTryFromEnum::default()) + } +} + +impl TryFrom for u64 { + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: QueryOutputBox) -> Result { + use iroha_macro::error::ErrorTryFromEnum; + + let QueryOutputBox::Numeric(numeric) = value else { + return Err(ErrorTryFromEnum::default()); + }; + + numeric.try_into().map_err(|_| ErrorTryFromEnum::default()) + } +} + macro_rules! impl_query { ($($ty:ty => $output:ty),+ $(,)?) => { $( impl Query for $ty { @@ -281,7 +341,7 @@ impl_query! { FindPermissionTokensByAccountId => Vec, FindAllAccounts => Vec, FindAccountById => crate::account::Account, - FindAccountKeyValueByIdAndKey => MetadataValue, + FindAccountKeyValueByIdAndKey => MetadataValueBox, FindAccountsByName => Vec, FindAccountsByDomainId => Vec, FindAccountsWithAsset => Vec, @@ -294,18 +354,18 @@ impl_query! { FindAssetsByAssetDefinitionId => Vec, FindAssetsByDomainId => Vec, FindAssetsByDomainIdAndAssetDefinitionId => Vec, - FindAssetQuantityById => crate::Numeric, - FindTotalAssetQuantityByAssetDefinitionId => crate::Numeric, - FindAssetKeyValueByIdAndKey => MetadataValue, - FindAssetDefinitionKeyValueByIdAndKey => MetadataValue, + FindAssetQuantityById => Numeric, + FindTotalAssetQuantityByAssetDefinitionId => Numeric, + FindAssetKeyValueByIdAndKey => MetadataValueBox, + FindAssetDefinitionKeyValueByIdAndKey => MetadataValueBox, FindAllDomains => Vec, FindDomainById => crate::domain::Domain, - FindDomainKeyValueByIdAndKey => MetadataValue, + FindDomainKeyValueByIdAndKey => MetadataValueBox, FindAllPeers => Vec, FindAllParameters => Vec, FindAllActiveTriggerIds => Vec, FindTriggerById => crate::trigger::Trigger, - FindTriggerKeyValueByIdAndKey => MetadataValue, + FindTriggerKeyValueByIdAndKey => MetadataValueBox, FindTriggersByDomainId => Vec>, FindAllTransactions => Vec, FindTransactionsByAccountId => Vec, @@ -316,30 +376,140 @@ impl_query! { } impl Query for QueryBox { - type Output = Value; + type Output = QueryOutputBox; fn encode_as_query_box(&self) -> Vec { self.encode() } } -impl From for Value { - #[inline] - fn from(source: MetadataValue) -> Self { - source.0 +impl core::fmt::Display for QueryOutputBox { + // TODO: Maybe derive + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + QueryOutputBox::Id(v) => core::fmt::Display::fmt(&v, f), + QueryOutputBox::Identifiable(v) => core::fmt::Display::fmt(&v, f), + QueryOutputBox::Transaction(_) => write!(f, "TransactionQueryOutput"), + QueryOutputBox::PermissionToken(v) => core::fmt::Display::fmt(&v, f), + QueryOutputBox::PermissionTokenSchema(v) => core::fmt::Display::fmt(&v, f), + QueryOutputBox::Block(v) => core::fmt::Display::fmt(&v, f), + QueryOutputBox::BlockHeader(v) => core::fmt::Display::fmt(&v, f), + QueryOutputBox::Numeric(v) => core::fmt::Display::fmt(&v, f), + QueryOutputBox::LimitedMetadata(v) => core::fmt::Display::fmt(&v, f), + + QueryOutputBox::Vec(v) => { + // TODO: Remove so we can derive. + let list_of_display: Vec<_> = v.iter().map(ToString::to_string).collect(); + // this prints with quotation marks, which is fine 90% + // of the time, and helps delineate where a display of + // one value stops and another one begins. + write!(f, "{list_of_display:?}") + } + } + } +} + +// TODO: The following macros looks very similar. Try to generalize them under one macro +macro_rules! from_and_try_from_value_idbox { + ( $($variant:ident( $ty:ty ),)+ $(,)? ) => { $( + impl TryFrom for $ty { + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: QueryOutputBox) -> Result { + if let QueryOutputBox::Id(IdBox::$variant(id)) = value { + Ok(id) + } else { + Err(Self::Error::default()) + } + } + } + + impl From<$ty> for QueryOutputBox { + fn from(id: $ty) -> Self { + QueryOutputBox::Id(IdBox::$variant(id)) + } + })+ + }; +} + +macro_rules! from_and_try_from_value_identifiable { + ( $( $variant:ident( $ty:ty ), )+ $(,)? ) => { $( + impl TryFrom for $ty { + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: QueryOutputBox) -> Result { + if let QueryOutputBox::Identifiable(IdentifiableBox::$variant(id)) = value { + Ok(id) + } else { + Err(Self::Error::default()) + } + } + } + + impl From<$ty> for QueryOutputBox { + fn from(id: $ty) -> Self { + QueryOutputBox::Identifiable(IdentifiableBox::$variant(id)) + } + } )+ + }; +} + +from_and_try_from_value_idbox!( + PeerId(crate::peer::PeerId), + DomainId(crate::domain::DomainId), + AccountId(crate::account::AccountId), + AssetId(crate::asset::AssetId), + AssetDefinitionId(crate::asset::AssetDefinitionId), + TriggerId(crate::trigger::TriggerId), + RoleId(crate::role::RoleId), + ParameterId(crate::parameter::ParameterId), + // TODO: Should we wrap String with new type in order to convert like here? + //from_and_try_from_value_idbox!((DomainName(Name), ErrorValueTryFromDomainName),); +); + +from_and_try_from_value_identifiable!( + NewDomain(crate::domain::NewDomain), + NewAccount(crate::account::NewAccount), + NewAssetDefinition(crate::asset::NewAssetDefinition), + NewRole(crate::role::NewRole), + Peer(crate::peer::Peer), + Domain(crate::domain::Domain), + Account(crate::account::Account), + AssetDefinition(crate::asset::AssetDefinition), + Asset(crate::asset::Asset), + Trigger(crate::trigger::Trigger), + Role(crate::role::Role), + Parameter(crate::parameter::Parameter), +); + +impl> From> for QueryOutputBox { + fn from(values: Vec) -> QueryOutputBox { + QueryOutputBox::Vec(values.into_iter().map(Into::into).collect()) } } -impl From for MetadataValue { - #[inline] - fn from(source: Value) -> Self { - Self(source) +impl TryFrom for Vec +where + QueryOutputBox: TryInto, +{ + type Error = iroha_macro::error::ErrorTryFromEnum; + + fn try_from(value: QueryOutputBox) -> Result { + if let QueryOutputBox::Vec(vec) = value { + return vec + .into_iter() + .map(TryInto::try_into) + .collect::, _>>() + .map_err(|_e| Self::Error::default()); + } + + Err(Self::Error::default()) } } -impl AsRef for TransactionQueryOutput { - fn as_ref(&self) -> &SignedTransaction { - &self.transaction.value +impl AsRef for TransactionQueryOutput { + fn as_ref(&self) -> &TransactionValue { + &self.transaction } } @@ -467,7 +637,7 @@ pub mod account { use derive_more::Display; use parity_scale_codec::Encode; - use super::{MetadataValue, Query, QueryType}; + use super::{MetadataValueBox, Query, QueryType}; use crate::prelude::*; queries! { @@ -489,7 +659,7 @@ pub mod account { pub id: AccountId, } - /// [`FindAccountKeyValueByIdAndKey`] Iroha Query finds a [`Value`] + /// [`FindAccountKeyValueByIdAndKey`] Iroha Query finds an [`MetadataValue`] /// of the key-value metadata pair in the specified account. #[derive(Display)] #[display(fmt = "Find metadata value with `{key}` key in `{id}` account")] @@ -560,7 +730,7 @@ pub mod asset { use iroha_primitives::numeric::Numeric; use parity_scale_codec::Encode; - use super::{MetadataValue, Query, QueryType}; + use super::{MetadataValueBox, Query, QueryType}; use crate::prelude::*; queries! { @@ -685,7 +855,7 @@ pub mod asset { pub id: AssetDefinitionId, } - /// [`FindAssetKeyValueByIdAndKey`] Iroha Query gets [`AssetId`] and key as input and finds [`Value`] + /// [`FindAssetKeyValueByIdAndKey`] Iroha Query gets [`AssetId`] and key as input and finds [`MetadataValue`] /// of the key-value pair stored in this asset. #[derive(Display)] #[display(fmt = "Find metadata value with `{key}` key in `{id}` asset")] @@ -697,7 +867,7 @@ pub mod asset { pub key: Name, } - /// [`FindAssetDefinitionKeyValueByIdAndKey`] Iroha Query gets [`AssetDefinitionId`] and key as input and finds [`Value`] + /// [`FindAssetDefinitionKeyValueByIdAndKey`] Iroha Query gets [`AssetDefinitionId`] and key as input and finds [`MetadataValue`] /// of the key-value pair stored in this asset definition. #[derive(Display)] #[display(fmt = "Find metadata value with `{key}` key in `{id}` asset definition")] @@ -733,7 +903,7 @@ pub mod domain { use derive_more::Display; use parity_scale_codec::Encode; - use super::{MetadataValue, Query, QueryType}; + use super::{MetadataValueBox, Query, QueryType}; use crate::prelude::*; queries! { @@ -754,7 +924,7 @@ pub mod domain { pub id: DomainId, } - /// [`FindDomainKeyValueByIdAndKey`] Iroha Query finds a [`Value`] of the key-value metadata pair + /// [`FindDomainKeyValueByIdAndKey`] Iroha Query finds a [`MetadataValue`] of the key-value metadata pair /// in the specified domain. #[derive(Display)] #[display(fmt = "Find metadata value with key `{key}` in `{id}` domain")] @@ -813,12 +983,12 @@ pub mod trigger { use derive_more::Display; use parity_scale_codec::Encode; - use super::{MetadataValue, Query, QueryType}; + use super::{MetadataValueBox, Query, QueryType}; use crate::{ domain::prelude::*, prelude::InstructionBox, trigger::{Trigger, TriggerId}, - Executable, Identifiable, Name, Value, + Executable, Identifiable, Name, }; queries! { @@ -933,14 +1103,13 @@ pub mod block { #![allow(clippy::missing_inline_in_public_items)] #[cfg(not(feature = "std"))] - use alloc::{boxed::Box, format, string::String, vec::Vec}; + use alloc::{format, string::String, vec::Vec}; use derive_more::Display; use iroha_crypto::HashOf; use parity_scale_codec::{Decode, Encode}; - use super::{Query, QueryType}; - use crate::block::SignedBlock; + use super::{Query, SignedBlock}; queries! { /// [`FindAllBlocks`] Iroha Query lists all blocks sorted by @@ -981,10 +1150,11 @@ pub mod http { use getset::Getters; use iroha_data_model_derive::model; + use predicate::PredicateBox; pub use self::model::*; use super::*; - use crate::{account::AccountId, predicate::PredicateBox}; + use crate::account::AccountId; declare_versioned_with_scale!(SignedQuery 1..2, Debug, Clone, iroha_macro::FromVariant, IntoSchema); @@ -1173,7 +1343,7 @@ pub mod error { pub use self::model::*; use super::*; - use crate::{block::SignedBlock, executor, permission, prelude::*}; + use crate::{executor, permission, prelude::*}; #[model] pub mod model { @@ -1270,11 +1440,11 @@ pub mod error { /// The prelude re-exports most commonly used traits, structs and macros from this crate. #[allow(ambiguous_glob_reexports)] pub mod prelude { - #[cfg(feature = "http")] - pub use super::http::*; pub use super::{ account::prelude::*, asset::prelude::*, block::prelude::*, domain::prelude::*, peer::prelude::*, permission::prelude::*, role::prelude::*, transaction::*, trigger::prelude::*, FetchSize, QueryBox, QueryId, TransactionQueryOutput, }; + #[cfg(feature = "http")] + pub use super::{http::*, predicate::PredicateTrait}; } diff --git a/data_model/src/predicate.rs b/data_model/src/query/predicate.rs similarity index 65% rename from data_model/src/predicate.rs rename to data_model/src/query/predicate.rs index 9469e1650ff..532243869d4 100644 --- a/data_model/src/predicate.rs +++ b/data_model/src/query/predicate.rs @@ -2,10 +2,254 @@ #[cfg(not(feature = "std"))] use alloc::vec; -use core::{fmt::Display, ops::Not}; +use core::{ + fmt::Display, + ops::{ControlFlow, Not}, +}; + +use iroha_data_model_derive::{PartiallyTaggedDeserialize, PartiallyTaggedSerialize}; use super::*; -use crate::{IdBox, Name, Value}; +use crate::{IdBox, Name}; + +/// Trait for boolean-like values +/// +/// [`or`](`Self::or`) and [`and`](`Self::and`) must satisfy De Morgan's laws, commutativity and associativity +/// [`Not`](`core::ops::Not`) implementation should satisfy double negation elimintation. +/// +/// Short-circuiting behaviour for `and` and `or` can be controlled by returning +/// `ControlFlow::Break` when subsequent application of the same operation +/// won't change the end result, no matter what operands. +/// +/// When implementing, it's recommended to generate exhaustive tests with +/// [`test_conformity`](`Self::test_conformity`). +pub trait PredicateSymbol +where + Self: Sized + core::ops::Not, +{ + /// Conjunction (e.g. boolean and) + #[must_use] + fn and(self, other: Self) -> ControlFlow; + /// Disjunction (e.g. boolean or) + #[must_use] + fn or(self, other: Self) -> ControlFlow; + + #[doc(hidden)] + #[must_use] + fn unwrapped_and(self, other: Self) -> Self { + match self.and(other) { + ControlFlow::Continue(val) | ControlFlow::Break(val) => val, + } + } + + #[doc(hidden)] + #[must_use] + fn unwrapped_or(self, other: Self) -> Self { + match self.or(other) { + ControlFlow::Continue(val) | ControlFlow::Break(val) => val, + } + } + + /// Given a list of all possible values of a type implementing [`PredicateSymbol`] + /// which are different in predicate context, exhaustively tests for: + /// - commutativity of `and` and `or` + /// - associativity of `and` and `or` + /// - De Mornan duality of `and` and `or` + /// - double negation elimination + /// + /// # Examples + /// + /// ``` + /// use iroha_data_model::query::predicate::PredicateSymbol; + /// + /// fn test() { + /// PredicateSymbol::test_conformity(vec![true, false]); + /// } + /// ``` + /// + fn test_conformity(values: Vec) + where + Self: PartialEq + Clone, + { + Self::test_conformity_with_eq(values, ::eq); + } + + /// Same as [`test_conformity`](`PredicateSymbol::test_conformity`), but + /// if type implementing [`PredicateSymbol`] carries some internal state + /// that isn't associative, one can provide custom `shallow_eq` function + /// that will be called instead of [`PartialEq::eq`] + /// + /// # Examples + /// + /// + /// ``` + /// use std::ops::ControlFlow; + /// + /// use iroha_data_model::query::predicate::PredicateSymbol; + /// + /// #[derive(Clone, PartialEq)] + /// enum Check { + /// Good, + /// // Encapsulates reason for badness which + /// // doesn't behave associatively + /// // (but if we ignore it, Check as a whole does) + /// Bad(String), + /// } + /// + /// impl core::ops::Not for Check { + /// type Output = Self; + /// fn not(self) -> Self { + /// // ... + /// todo!() + /// } + /// } + /// + /// impl PredicateSymbol for Check { + /// fn and(self, other: Self) -> ControlFlow { + /// // ... + /// todo!() + /// } + /// + /// fn or(self, other: Self) -> ControlFlow { + /// // ... + /// todo!() + /// } + /// } + /// + /// fn shallow_eq(left: &Check, right: &Check) -> bool { + /// match (left, right) { + /// (Check::Good, Check::Good) | (Check::Bad(_), Check::Bad(_)) => true, + /// _ => false + /// } + /// } + /// + /// fn test() { + /// let good = Check::Good; + /// let bad = Check::Bad("example".to_owned()); + /// // Would fail some assertions, since derived PartialEq is "deep" + /// // PredicateSymbol::test_conformity(vec![good, bad]); + /// + /// // Works as expected + /// PredicateSymbol::test_conformity_with_eq(vec![good, bad], shallow_eq); + /// } + /// ``` + fn test_conformity_with_eq(values: Vec, shallow_eq: impl FnMut(&Self, &Self) -> bool) + where + Self: Clone, + { + let mut eq = shallow_eq; + let values = values + .into_iter() + .map(|val| move || val.clone()) + .collect::>(); + + let typ = core::any::type_name::(); + + for a in &values { + assert!( + eq(&a().not().not(), &a()), + "Double negation elimination doesn't hold for {typ}", + ); + } + + for a in &values { + for b in &values { + assert!( + eq( + &PredicateSymbol::unwrapped_and(a(), b()), + &PredicateSymbol::unwrapped_and(b(), a()) + ), + "Commutativity doesn't hold for `PredicateSymbol::and` implementation for {typ}" + ); + + assert!( + eq( + &PredicateSymbol::unwrapped_or(a(), b()), + &PredicateSymbol::unwrapped_or(b(), a()) + ), + "Commutativity doesn't hold for `PredicateSymbol::or` implementation for {typ}" + ); + + assert!( + eq( + &PredicateSymbol::unwrapped_or(!a(), !b()), + &!PredicateSymbol::unwrapped_and(a(), b()) + ), + "De Morgan's law doesn't hold for {typ}", + ); + + assert!( + eq( + &PredicateSymbol::unwrapped_and(!a(), !b()), + &!PredicateSymbol::unwrapped_or(a(), b()) + ), + "De Morgan's law doesn't hold for {typ}", + ); + } + } + + for a in &values { + for b in &values { + for c in &values { + assert!( + eq( + &PredicateSymbol::unwrapped_and( + PredicateSymbol::unwrapped_and(a(), b()), + c() + ), + &PredicateSymbol::unwrapped_and( + a(), + PredicateSymbol::unwrapped_and(b(), c()), + ), + ), + "Associativity doesn't hold for `PredicateSymbol::or` implementation for {typ}", + ); + + assert!( + eq( + &PredicateSymbol::unwrapped_or( + PredicateSymbol::unwrapped_or(a(), b()), + c() + ), + &PredicateSymbol::unwrapped_or( + a(), + PredicateSymbol::unwrapped_or(b(), c()), + ), + ), + "Associativity doesn't hold for `PredicateSymbol::and` implementation for {typ}", + ); + } + } + } + } +} + +impl PredicateSymbol for bool { + fn and(self, other: Self) -> ControlFlow { + if self && other { + ControlFlow::Continue(true) + } else { + ControlFlow::Break(false) + } + } + + fn or(self, other: Self) -> ControlFlow { + if self || other { + ControlFlow::Break(true) + } else { + ControlFlow::Continue(false) + } + } +} + +/// Trait for generic predicates. +pub trait PredicateTrait { + /// Type the predicate evaluates to. + type EvaluatesTo: PredicateSymbol; + + /// The result of applying the predicate to a value. + fn applies(&self, input: T) -> Self::EvaluatesTo; +} mod nontrivial { use super::*; @@ -88,7 +332,7 @@ macro_rules! nontrivial { // couldn't find a way to do it without polluting everything // downstream with explicit lifetimes, since we would need to // store PhantomData here, and `Input`s are most often -// references (e.g. &Value). +// references (e.g. &QueryOutputBox). pub enum GenericPredicateBox

{ /// Logically `&&` the results of applying the predicates. And(NonTrivial), @@ -105,7 +349,7 @@ impl

Display for GenericPredicateBox

where P: Display, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { GenericPredicateBox::And(predicates) => { write!(f, "AND(")?; @@ -219,19 +463,19 @@ where } } -/// Predicate combinator for predicates operating on `Value` -pub type PredicateBox = GenericPredicateBox; +/// Predicate combinator for predicates operating on `QueryOutputBox` +pub type PredicateBox = GenericPredicateBox; impl Default for PredicateBox { fn default() -> Self { - PredicateBox::Raw(value::ValuePredicate::Pass) + PredicateBox::Raw(value::QueryOutputPredicate::Pass) } } #[cfg(test)] pub mod test { - use super::{value, PredicateBox}; - use crate::{PredicateSymbol, PredicateTrait as _, ToValue}; + use super::{value, PredicateBox, PredicateSymbol, PredicateTrait as _}; + use crate::metadata::MetadataValueBox; #[test] fn boolean_predicate_symbol_conformity() { @@ -240,10 +484,10 @@ pub mod test { #[test] fn pass() { - let t = PredicateBox::new(value::ValuePredicate::Pass); + let t = PredicateBox::new(value::QueryOutputPredicate::Pass); let f = t.clone().negate(); - let v_t = true.to_value(); - let v_f = false.to_value(); + let v_t = MetadataValueBox::from(true).into(); + let v_f = MetadataValueBox::from(false).into(); println!("t: {t:?}, f: {f:?}"); assert!(t.applies(&v_t)); @@ -254,9 +498,9 @@ pub mod test { #[test] fn truth_table() { - let t = PredicateBox::new(value::ValuePredicate::Pass); + let t = PredicateBox::new(value::QueryOutputPredicate::Pass); let f = t.clone().negate(); - let v = true.to_value(); + let v = MetadataValueBox::from(true).into(); assert!(!PredicateBox::and(t.clone(), f.clone()).applies(&v)); assert!(PredicateBox::and(t.clone(), t.clone()).applies(&v)); @@ -372,6 +616,7 @@ pub mod string { mod id_box { use super::*; + use crate::peer::PeerId; #[test] fn simple_name_wrappers() { @@ -496,7 +741,7 @@ pub mod string { #[test] fn peer_id() { let (public_key, _) = iroha_crypto::KeyPair::generate().into(); - let id = IdBox::PeerId(peer::PeerId::new(socket_addr!(127.0.0.1:123), public_key)); + let id = IdBox::PeerId(PeerId::new(socket_addr!(127.0.0.1:123), public_key)); assert!(StringPredicate::contains("123").applies(&id)); } } @@ -563,6 +808,7 @@ pub mod numerical { use iroha_primitives::numeric::Numeric; use super::*; + use crate::query::QueryOutputBox; /// A lower-inclusive range predicate. #[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] @@ -747,13 +993,13 @@ pub mod numerical { } } - impl PredicateTrait<&Value> for SemiRange { + impl PredicateTrait<&QueryOutputBox> for SemiRange { type EvaluatesTo = bool; #[inline] - fn applies(&self, input: &Value) -> Self::EvaluatesTo { + fn applies(&self, input: &QueryOutputBox) -> Self::EvaluatesTo { match input { - Value::Numeric(quantity) => match self { + QueryOutputBox::Numeric(quantity) => match self { SemiRange::Numeric(predicate) => predicate.applies(*quantity), }, _ => false, @@ -761,13 +1007,13 @@ pub mod numerical { } } - impl PredicateTrait<&Value> for Range { + impl PredicateTrait<&QueryOutputBox> for Range { type EvaluatesTo = bool; #[inline] - fn applies(&self, input: &Value) -> Self::EvaluatesTo { + fn applies(&self, input: &QueryOutputBox) -> Self::EvaluatesTo { match input { - Value::Numeric(quantity) => match self { + QueryOutputBox::Numeric(quantity) => match self { Range::Numeric(predicate) => predicate.applies(*quantity), }, _ => false, @@ -788,13 +1034,13 @@ pub mod numerical { let pred = SemiRange::Numeric((numeric!(1), numeric!(100)).into()); println!("semi_interval range predicate: {pred:?}"); - assert!(pred.applies(&Value::Numeric(numeric!(1)))); - assert!(!pred.applies(&Value::Numeric(numeric!(0)))); - assert!(pred.applies(&Value::Numeric(numeric!(99)))); - assert!(!pred.applies(&Value::Numeric(numeric!(100)))); - assert!(!pred.applies(&Value::Numeric(numeric!(0.99)))); - assert!(pred.applies(&Value::Numeric(numeric!(99.9999)))); - assert!(pred.applies(&Value::Numeric(numeric!(99.9_999_999_999)))); + assert!(pred.applies(&QueryOutputBox::Numeric(numeric!(1)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(0)))); + assert!(pred.applies(&QueryOutputBox::Numeric(numeric!(99)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(100)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(0.99)))); + assert!(pred.applies(&QueryOutputBox::Numeric(numeric!(99.9999)))); + assert!(pred.applies(&QueryOutputBox::Numeric(numeric!(99.9_999_999_999)))); } #[test] @@ -803,20 +1049,20 @@ pub mod numerical { let pred = Range::Numeric((numeric!(1), numeric!(100)).into()); println!("semi_interval range predicate: {pred:?}"); - assert!(pred.applies(&Value::Numeric(numeric!(1)))); - assert!(!pred.applies(&Value::Numeric(numeric!(0)))); - assert!(pred.applies(&Value::Numeric(numeric!(100)))); - assert!(!pred.applies(&Value::Numeric(numeric!(101)))); - assert!(!pred.applies(&Value::Numeric(numeric!(0.99)))); - assert!(pred.applies(&Value::Numeric(numeric!(99.9999)))); - assert!(!pred.applies(&Value::Numeric(numeric!(100.000000001)))); + assert!(pred.applies(&QueryOutputBox::Numeric(numeric!(1)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(0)))); + assert!(pred.applies(&QueryOutputBox::Numeric(numeric!(100)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(101)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(0.99)))); + assert!(pred.applies(&QueryOutputBox::Numeric(numeric!(99.9999)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(100.000000001)))); } { let pred = Range::Numeric((numeric!(127), numeric!(127)).into()); - assert!(pred.applies(&Value::Numeric(numeric!(127)))); - assert!(!pred.applies(&Value::Numeric(numeric!(126)))); - assert!(!pred.applies(&Value::Numeric(numeric!(128)))); + assert!(pred.applies(&QueryOutputBox::Numeric(numeric!(127)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(126)))); + assert!(!pred.applies(&QueryOutputBox::Numeric(numeric!(128)))); } } @@ -824,11 +1070,11 @@ pub mod numerical { fn invalid_types_false() { { let pred = SemiRange::Numeric(SemiInterval::ending(numeric!(100))); - assert!(!pred.applies(&Value::Vec(Vec::new()))); + assert!(!pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { let pred = Range::Numeric(Interval::ending(numeric!(100))); - assert!(!pred.applies(&Value::Vec(Vec::new()))); + assert!(!pred.applies(&QueryOutputBox::Vec(Vec::new()))); } } @@ -837,163 +1083,114 @@ pub mod numerical { { let pred = SemiRange::Numeric(SemiInterval::starting(Numeric::ZERO)); // Technically the maximum itself is never included in the semi range. - assert!(!pred.applies(&Numeric::MAX.to_value())); + assert!(!pred.applies(&Numeric::MAX.into())); } { let pred = SemiRange::Numeric(SemiInterval::ending(numeric!(100))); - assert!(pred.applies(&numeric!(1).to_value())); - assert!(pred.applies(&numeric!(99).to_value())); - assert!(!pred.applies(&numeric!(100).to_value())); + assert!(pred.applies(&numeric!(1).into())); + assert!(pred.applies(&numeric!(99).into())); + assert!(!pred.applies(&numeric!(100).into())); } { let pred = Range::Numeric(Interval::starting(Numeric::ZERO)); // Technically the maximum itself is included in the range. - assert!(pred.applies(&Numeric::MAX.to_value())); + assert!(pred.applies(&Numeric::MAX.into())); } { let pred = Range::Numeric(Interval::ending(numeric!(100))); - assert!(pred.applies(&numeric!(1).to_value())); - assert!(pred.applies(&numeric!(100).to_value())); - assert!(!pred.applies(&numeric!(101).to_value())); + assert!(pred.applies(&numeric!(1).into())); + assert!(pred.applies(&numeric!(100).into())); + assert!(!pred.applies(&numeric!(101).into())); } } } } pub mod value { - //! raw predicates applied to `Value`. + //! raw predicates applied to `QueryOutputBox`. use super::*; + use crate::query::QueryOutputBox; - /// A predicate designed for general processing of `Value`. + /// A predicate designed for general processing of `QueryOutputBox`. #[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] - pub enum ValuePredicate { + pub enum QueryOutputPredicate { /// Apply predicate to the [`Identifiable::Id`] and/or [`IdBox`]. Identifiable(string::StringPredicate), /// Apply predicate to the container. Container(Container), - /// Apply predicate to the [`::to_string`](ToString::to_string()) representation. + /// Apply predicate to the [`::to_string`](ToString::to_string()) representation. Display(string::StringPredicate), /// Apply predicate to the numerical value. Numerical(numerical::SemiRange), /// Timestamp (currently for [`SignedBlock`] only). TimeStamp(numerical::SemiInterval), - /// IpAddress enumerable by `u32` - Ipv4Addr(ip_addr::Ipv4Predicate), - /// IpAddress extended to use wide range `u128` enumerable addresses. - Ipv6Addr(ip_addr::Ipv6Predicate), /// Always return true. Pass, } - impl PredicateTrait<&Value> for ValuePredicate { + impl PredicateTrait<&QueryOutputBox> for QueryOutputPredicate { type EvaluatesTo = bool; - fn applies(&self, input: &Value) -> Self::EvaluatesTo { + fn applies(&self, input: &QueryOutputBox) -> Self::EvaluatesTo { // Large jump table. Do not inline. match self { - ValuePredicate::Identifiable(pred) => match input { - Value::String(s) => pred.applies(s), - Value::Name(n) => pred.applies(n), - Value::Id(id_box) => pred.applies(id_box), - Value::Identifiable(identifiable_box) => { + QueryOutputPredicate::Identifiable(pred) => match input { + QueryOutputBox::Id(id_box) => pred.applies(id_box), + QueryOutputBox::Identifiable(identifiable_box) => { pred.applies(&identifiable_box.id_box()) } _ => false, }, - ValuePredicate::Container(Container::Any(pred)) => match input { - Value::Vec(vec) => vec.iter().any(|val| pred.applies(val)), - Value::LimitedMetadata(map) => { - map.iter().map(|(_, val)| val).any(|val| pred.applies(val)) - } + QueryOutputPredicate::Container(Container::Any(pred)) => match input { + QueryOutputBox::Vec(vec) => vec.iter().any(|val| pred.applies(val)), _ => false, }, - ValuePredicate::Container(Container::All(pred)) => match input { - Value::Vec(vec) => vec.iter().all(|val| pred.applies(val)), - Value::LimitedMetadata(map) => { - map.iter().map(|(_, val)| val).all(|val| pred.applies(val)) - } + QueryOutputPredicate::Container(Container::All(pred)) => match input { + QueryOutputBox::Vec(vec) => vec.iter().all(|val| pred.applies(val)), _ => false, }, - ValuePredicate::Container(Container::AtIndex(AtIndex { + QueryOutputPredicate::Container(Container::AtIndex(AtIndex { index: idx, predicate: pred, })) => match input { - Value::Vec(vec) => vec + QueryOutputBox::Vec(vec) => vec .get(*idx as usize) // Safe, since this is only executed server-side and servers are 100% going to be 64-bit. .map_or(false, |val| pred.applies(val)), _ => false, }, - ValuePredicate::Container(Container::ValueOfKey(ValueOfKey { - key, - predicate: pred, - })) => { - match input { - Value::LimitedMetadata(map) => { - map.get(key).map_or(false, |val| pred.applies(val)) - } - _ => false, // TODO: Do we need more? + QueryOutputPredicate::Numerical(pred) => pred.applies(input), + QueryOutputPredicate::Display(pred) => pred.applies(&input.to_string()), + QueryOutputPredicate::TimeStamp(pred) => match input { + QueryOutputBox::Block(block) => { + pred.applies(block.header().timestamp().as_millis()) } - } - ValuePredicate::Container(Container::HasKey(key)) => match input { - Value::LimitedMetadata(map) => map.contains(key), _ => false, }, - ValuePredicate::Numerical(pred) => pred.applies(input), - ValuePredicate::Display(pred) => pred.applies(&input.to_string()), - ValuePredicate::TimeStamp(pred) => match input { - Value::Block(block) => pred.applies(block.header().timestamp().as_millis()), - _ => false, - }, - ValuePredicate::Ipv4Addr(pred) => match input { - Value::Ipv4Addr(addr) => pred.applies(*addr), - _ => false, - }, - ValuePredicate::Ipv6Addr(pred) => match input { - Value::Ipv6Addr(addr) => pred.applies(*addr), - _ => false, - }, - ValuePredicate::Pass => true, + QueryOutputPredicate::Pass => true, } } } - impl ValuePredicate { + impl QueryOutputPredicate { /// Construct [`Predicate::Container`] variant. #[inline] #[must_use] - pub fn any(pred: impl Into) -> Self { + pub fn any(pred: impl Into) -> Self { Self::Container(Container::Any(Box::new(pred.into()))) } /// Construct [`Predicate::Container`] variant. #[inline] #[must_use] - pub fn all(pred: impl Into) -> Self { + pub fn all(pred: impl Into) -> Self { Self::Container(Container::All(Box::new(pred.into()))) } /// Construct [`Predicate::Container`] variant. #[inline] #[must_use] - pub fn has_key(key: Name) -> Self { - Self::Container(Container::HasKey(key)) - } - - /// Construct [`Predicate::Container`] variant. - #[inline] - #[must_use] - pub fn value_of(key: Name, pred: impl Into) -> Self { - Self::Container(Container::ValueOfKey(ValueOfKey { - key, - predicate: Box::new(pred.into()), - })) - } - - /// Construct [`Predicate::Container`] variant. - #[inline] - #[must_use] - pub fn at_index(index: u32, pred: impl Into) -> Self { + pub fn at_index(index: u32, pred: impl Into) -> Self { Self::Container(Container::AtIndex(AtIndex { index, predicate: Box::new(pred.into()), @@ -1005,36 +1202,23 @@ pub mod value { #[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] pub struct AtIndex { index: u32, - predicate: Box, - } - - /// A predicate that targets the particular `key` of a collection. - #[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] - pub struct ValueOfKey { - key: Name, - predicate: Box, + predicate: Box, } /// Predicate that targets specific elements or groups; useful for - /// working with containers. Currently only - /// [`Metadata`](crate::metadata::Metadata) and [`Vec`] are - /// supported. + /// working with containers. Currently only [`Vec`] is supported. #[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] pub enum Container { /// Forward to [`Iterator::any`] - Any(Box), + Any(Box), /// Forward to [`Iterator::all`] - All(Box), + All(Box), /// Apply predicate to the [`Value`] element at the index. AtIndex(AtIndex), - /// Apply the predicate to the [`Value`] keyed by the index. - ValueOfKey(ValueOfKey), - /// Forward to [`Metadata::contains`](crate::metadata::Metadata::contains()). - HasKey(Name), } - impl From for PredicateBox { - fn from(value: ValuePredicate) -> Self { + impl From for PredicateBox { + fn from(value: QueryOutputPredicate) -> Self { PredicateBox::Raw(value) } } @@ -1042,271 +1226,112 @@ pub mod value { #[cfg(test)] mod test { use iroha_crypto::KeyPair; - use iroha_primitives::{ - addr::socket_addr, - numeric::{numeric, Numeric}, - }; - use peer::Peer; - use prelude::Metadata; + use iroha_primitives::{addr::socket_addr, numeric::numeric}; use super::*; - use crate::account::Account; + use crate::{ + account::{Account, AccountId}, + domain::{Domain, DomainId}, + metadata::{Metadata, MetadataValueBox}, + peer::{Peer, PeerId}, + }; #[test] fn typing() { { - let pred = - ValuePredicate::Identifiable(string::StringPredicate::is("alice@wonderland")); + let pred = QueryOutputPredicate::Identifiable(string::StringPredicate::is( + "alice@wonderland", + )); println!("{pred:?}"); - assert!(pred.applies(&Value::String("alice@wonderland".to_owned()))); - assert!(pred.applies(&Value::Id(IdBox::AccountId( + assert!(pred.applies(&QueryOutputBox::Id(IdBox::AccountId( "alice@wonderland".parse().expect("Valid") )))); assert!( - pred.applies(&Value::Identifiable(IdentifiableBox::NewAccount( + pred.applies(&QueryOutputBox::Identifiable(IdentifiableBox::NewAccount( Account::new( "alice@wonderland".parse().expect("Valid"), - KeyPair::generate().into_raw_parts().0, + KeyPair::generate().into_raw_parts().0 ) ))) ); - assert!(!pred.applies(&Value::Name("alice".parse().expect("Valid")))); - assert!(!pred.applies(&Value::Vec(Vec::new()))); + assert!(!pred.applies( + &MetadataValueBox::from("alice".parse::().expect("Valid")).into() + )); + assert!(!pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { - let pred = ValuePredicate::Pass; + let pred = QueryOutputPredicate::Pass; println!("{pred:?}"); - assert!(pred.applies(&Value::String("alice@wonderland".to_owned()))); + assert!(pred.applies(&MetadataValueBox::from("alice@wonderland".to_owned()).into())); } { - let pred = ValuePredicate::TimeStamp(numerical::SemiInterval::starting(0)); + let pred = QueryOutputPredicate::TimeStamp(numerical::SemiInterval::starting(0)); println!("{pred:?}"); - assert!(!pred.applies(&Value::String("alice@wonderland".to_owned()))); + assert!( + !pred.applies(&MetadataValueBox::from("alice@wonderland".to_owned()).into()) + ); } { let key_pair = iroha_crypto::KeyPair::generate(); let (public_key, _) = key_pair.into(); - let pred = ValuePredicate::Display(string::StringPredicate::is("alice@wonderland")); + let pred = + QueryOutputPredicate::Display(string::StringPredicate::is("alice@wonderland")); println!("{pred:?}"); assert!( - !pred.applies(&Value::Identifiable(IdentifiableBox::Peer(Peer { - id: peer::PeerId::new(socket_addr!(127.0.0.1:123), public_key) + !pred.applies(&QueryOutputBox::Identifiable(IdentifiableBox::Peer(Peer { + id: PeerId::new(socket_addr!(127.0.0.1:123), public_key) }))) ); } - let pred = ValuePredicate::Numerical(numerical::SemiRange::Numeric( + let pred = QueryOutputPredicate::Numerical(numerical::SemiRange::Numeric( (numeric!(0), numeric!(42)).into(), )); - assert!(!pred.applies(&Value::String("alice".to_owned()))); - assert!(pred.applies(&numeric!(41).to_value())); + assert!(!pred.applies(&MetadataValueBox::from("alice".to_owned()).into())); + assert!(pred.applies(&numeric!(41).into())); } #[test] fn container_vec() { - let list = Value::Vec(vec![ - Value::String("alice".to_owned()), - Value::Name("alice_at_wonderland".parse().expect("Valid")), - Value::String("aliceee!".to_owned()), + let list = QueryOutputBox::Vec(vec![ + QueryOutputBox::Identifiable( + Domain::new("alice".parse::().unwrap()).into(), + ), + QueryOutputBox::Id("alice@wonderland".parse::().unwrap().into()), + QueryOutputBox::Id("aliceee!".parse::().unwrap().into()), ]); - let meta = Value::LimitedMetadata(Metadata::default()); - let alice = Value::Name("alice".parse().expect("Valid")); - let alice_pred = ValuePredicate::Display(string::StringPredicate::contains("alice")); + let alice_pred = + QueryOutputPredicate::Display(string::StringPredicate::contains("alice")); { - let pred = ValuePredicate::any(alice_pred.clone()); + let pred = QueryOutputPredicate::any(alice_pred.clone()); println!("{pred:?}"); assert!(pred.applies(&list)); - assert!(!pred.applies(&Value::Vec(Vec::new()))); - assert!(!pred.applies(&meta)); - assert!(!pred.applies(&alice)); + assert!(!pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { - let pred = ValuePredicate::all(alice_pred.clone()); + let pred = QueryOutputPredicate::all(alice_pred.clone()); println!("{pred:?}"); assert!(pred.applies(&list)); - assert!(pred.applies(&Value::Vec(Vec::new()))); - assert!(pred.applies(&meta)); // Not bug. Same convention as std lib. - assert!(!pred.applies(&alice)); + assert!(pred.applies(&QueryOutputBox::Vec(Vec::new()))); } { let alice_id_pred = - ValuePredicate::Identifiable(string::StringPredicate::contains("alice")); - let pred = ValuePredicate::all(alice_id_pred); + QueryOutputPredicate::Identifiable(string::StringPredicate::contains("alice")); + let pred = QueryOutputPredicate::all(alice_id_pred); println!("{pred:?}"); assert!(pred.applies(&list)); - assert!(pred.applies(&Value::Vec(Vec::new()))); - assert!(pred.applies(&meta)); // Not bug. Same convention as std lib. - assert!(!pred.applies(&alice)); + assert!(pred.applies(&QueryOutputBox::Vec(Vec::new()))); } - assert!(ValuePredicate::at_index(0, alice_pred.clone()).applies(&list)); + assert!(QueryOutputPredicate::at_index(0, alice_pred.clone()).applies(&list)); - let idx_pred = ValuePredicate::at_index(3, alice_pred); // Should be out of bounds. + let idx_pred = QueryOutputPredicate::at_index(3, alice_pred); // Should be out of bounds. println!("{idx_pred:?}"); assert!(!idx_pred.applies(&list)); - assert!(!idx_pred.applies(&meta)); - assert!(!idx_pred.applies(&alice)); - - let has_key = ValuePredicate::has_key("alice".parse().expect("Valid")); - println!("{has_key:?}"); - assert!(!has_key.applies(&list)); - assert!(!has_key.applies(&meta)); - // TODO: case with non-empty meta - - let value_key = ValuePredicate::value_of("alice".parse().expect("Valid"), has_key); - println!("{value_key:?}"); - assert!(!value_key.applies(&list)); - assert!(!value_key.applies(&meta)); - // TODO: case with non-empty meta - } - } -} - -pub mod ip_addr { - //! Predicates for IP address processing. - use iroha_primitives::addr::{Ipv4Addr, Ipv6Addr}; - - use super::{numerical::Interval as Mask, *}; - - /// A Predicate containing independent octuplet masks to be - /// applied to all elements of an IP version 4 address. - #[derive( - Debug, Clone, Copy, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema, - )] - pub struct Ipv4Predicate([Mask; 4]); - - impl PredicateTrait for Ipv4Predicate { - type EvaluatesTo = bool; - - fn applies(&self, input: Ipv4Addr) -> Self::EvaluatesTo { - self.0 - .iter() - .copied() - .zip(input) - .all(|(myself, other)| myself.applies(other)) - } - } - - impl Ipv4Predicate { - /// Construct a new predicate. - pub fn new( - octet_0: impl Into>, - octet_1: impl Into>, - octet_2: impl Into>, - octet_3: impl Into>, - ) -> Self { - Self([ - octet_0.into(), - octet_1.into(), - octet_2.into(), - octet_3.into(), - ]) - } - } - - /// A Predicate containing independent _hexadecuplets_ (u16 - /// groups) masks to be applied to all elements of an IP version 6 - /// address. - #[derive( - Debug, Clone, Copy, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema, - )] - pub struct Ipv6Predicate([Mask; 8]); - - impl PredicateTrait for Ipv6Predicate { - type EvaluatesTo = bool; - - fn applies(&self, input: Ipv6Addr) -> Self::EvaluatesTo { - self.0 - .iter() - .copied() - .zip(input) - .all(|(myself, other)| myself.applies(other)) - } - } - - // Could do this with a macro, but it doesn't seem to shorten much. - #[allow(clippy::too_many_arguments)] - impl Ipv6Predicate { - /// Construct a new predicate. The 8 arguments must match a - /// mask that filters on all 8 of the _hexadecuplets_ (u16 - /// groups) in a normal IP version 6 address. - pub fn new( - hexadecuplet_0: impl Into>, - hexadecuplet_1: impl Into>, - hexadecuplet_2: impl Into>, - hexadecuplet_3: impl Into>, - hexadecuplet_4: impl Into>, - hexadecuplet_5: impl Into>, - hexadecuplet_6: impl Into>, - hexadecuplet_7: impl Into>, - ) -> Self { - Self([ - hexadecuplet_0.into(), - hexadecuplet_1.into(), - hexadecuplet_2.into(), - hexadecuplet_3.into(), - hexadecuplet_4.into(), - hexadecuplet_5.into(), - hexadecuplet_6.into(), - hexadecuplet_7.into(), - ]) - } - } - - #[cfg(test)] - mod test { - use super::*; - - #[test] - fn ipv4_filter_example() { - { - let pred = Ipv4Predicate::new(127, 0, 0, (0, 10)); - println!("{pred:?}"); - assert!(pred.applies(Ipv4Addr::from([127, 0, 0, 1]))); - assert!(pred.applies(Ipv4Addr::from([127, 0, 0, 3]))); - assert!(pred.applies(Ipv4Addr::from([127, 0, 0, 4]))); - assert!(pred.applies(Ipv4Addr::from([127, 0, 0, 10]))); - assert!(!pred.applies(Ipv4Addr::from([127, 0, 0, 11]))); - assert!(!pred.applies(Ipv4Addr::from([125, 0, 0, 1]))); - assert!(!pred.applies(Ipv4Addr::from([128, 0, 0, 1]))); - assert!(!pred.applies(Ipv4Addr::from([127, 1, 0, 1]))); - assert!(!pred.applies(Ipv4Addr::from([127, 0, 1, 1]))); - } - - { - let pred = Ipv4Predicate::new(Mask::starting(0), 0, 0, (0, 10)); - println!("{pred:?}"); - assert!(pred.applies(Ipv4Addr::from([0, 0, 0, 1]))); - assert!(pred.applies(Ipv4Addr::from([255, 0, 0, 1]))); - assert!(pred.applies(Ipv4Addr::from([127, 0, 0, 4]))); - assert!(pred.applies(Ipv4Addr::from([127, 0, 0, 10]))); - assert!(pred.applies(Ipv4Addr::from([128, 0, 0, 1]))); - assert!(pred.applies(Ipv4Addr::from([128, 0, 0, 1]))); - assert!(!pred.applies(Ipv4Addr::from([127, 0, 0, 11]))); - assert!(pred.applies(Ipv4Addr::from([126, 0, 0, 1]))); - assert!(pred.applies(Ipv4Addr::from([128, 0, 0, 1]))); - assert!(!pred.applies(Ipv4Addr::from([127, 1, 0, 1]))); - assert!(!pred.applies(Ipv4Addr::from([127, 0, 1, 1]))); - } - } - - #[test] - fn ipv6_filter_example() { - let pred = Ipv6Predicate::new(12700, 0, 0, (0, 10), 0, 0, 0, 0); - println!("{pred:?}"); - assert!(pred.applies(Ipv6Addr::from([12700, 0, 0, 1, 0, 0, 0, 0]))); - assert!(pred.applies(Ipv6Addr::from([12700, 0, 0, 3, 0, 0, 0, 0]))); - assert!(pred.applies(Ipv6Addr::from([12700, 0, 0, 4, 0, 0, 0, 0]))); - assert!(pred.applies(Ipv6Addr::from([12700, 0, 0, 10, 0, 0, 0, 0]))); - assert!(!pred.applies(Ipv6Addr::from([12700, 0, 0, 11, 0, 0, 0, 0]))); - assert!(!pred.applies(Ipv6Addr::from([12500, 0, 0, 1, 0, 0, 0, 0]))); - assert!(!pred.applies(Ipv6Addr::from([12800, 0, 0, 1, 0, 0, 0, 0]))); - assert!(!pred.applies(Ipv6Addr::from([12700, 1, 0, 1, 0, 0, 0, 0]))); - assert!(!pred.applies(Ipv6Addr::from([12700, 0, 1, 1, 0, 0, 0, 0]))); } } } diff --git a/docs/source/references/schema.json b/docs/source/references/schema.json index c4fc2994351..5f167627564 100644 --- a/docs/source/references/schema.json +++ b/docs/source/references/schema.json @@ -237,18 +237,6 @@ } ] }, - "Array, 8>": { - "Array": { - "type": "Interval", - "len": 8 - } - }, - "Array, 4>": { - "Array": { - "type": "Interval", - "len": 4 - } - }, "Array": { "Array": { "type": "u16", @@ -573,45 +561,24 @@ }, { "name": "predicate", - "type": "ValuePredicate" - } - ] - }, - "BatchedResponse": { - "Enum": [ - { - "tag": "V1", - "discriminant": 1, - "type": "BatchedResponseV1" + "type": "QueryOutputPredicate" } ] }, - "BatchedResponse>": { + "BatchedResponse": { "Enum": [ { "tag": "V1", "discriminant": 1, - "type": "BatchedResponseV1>" + "type": "BatchedResponseV1" } ] }, - "BatchedResponseV1": { + "BatchedResponseV1": { "Struct": [ { "name": "batch", - "type": "Value" - }, - { - "name": "cursor", - "type": "ForwardCursor" - } - ] - }, - "BatchedResponseV1>": { - "Struct": [ - { - "name": "batch", - "type": "Vec" + "type": "QueryOutputBox" }, { "name": "cursor", @@ -656,7 +623,7 @@ }, { "name": "commit_topology", - "type": "UniqueVec" + "type": "Vec" }, { "name": "transactions", @@ -763,27 +730,17 @@ { "tag": "Any", "discriminant": 0, - "type": "ValuePredicate" + "type": "QueryOutputPredicate" }, { "tag": "All", "discriminant": 1, - "type": "ValuePredicate" + "type": "QueryOutputPredicate" }, { "tag": "AtIndex", "discriminant": 2, "type": "AtIndex" - }, - { - "tag": "ValueOfKey", - "discriminant": 3, - "type": "ValueOfKey" - }, - { - "tag": "HasKey", - "discriminant": 4, - "type": "Name" } ] }, @@ -1770,27 +1727,27 @@ } ] }, - "GenericPredicateBox": { + "GenericPredicateBox": { "Enum": [ { "tag": "And", "discriminant": 0, - "type": "NonTrivial>" + "type": "NonTrivial>" }, { "tag": "Or", "discriminant": 1, - "type": "NonTrivial>" + "type": "NonTrivial>" }, { "tag": "Not", "discriminant": 2, - "type": "GenericPredicateBox" + "type": "GenericPredicateBox" }, { "tag": "Raw", "discriminant": 3, - "type": "ValuePredicate" + "type": "QueryOutputPredicate" } ] }, @@ -1836,20 +1793,6 @@ "HashOf>": "Hash", "HashOf": "Hash", "HashOf": "Hash", - "HashValue": { - "Enum": [ - { - "tag": "Transaction", - "discriminant": 0, - "type": "HashOf" - }, - { - "tag": "Block", - "discriminant": 1, - "type": "HashOf" - } - ] - }, "IdBox": { "Enum": [ { @@ -2196,49 +2139,6 @@ } ] }, - "Integer": { - "Enum": [ - { - "tag": "U32", - "discriminant": 0, - "type": "u32" - }, - { - "tag": "U64", - "discriminant": 1, - "type": "u64" - }, - { - "tag": "U128", - "discriminant": 2, - "type": "u128" - } - ] - }, - "Interval": { - "Struct": [ - { - "name": "start", - "type": "u16" - }, - { - "name": "limit", - "type": "u16" - } - ] - }, - "Interval": { - "Struct": [ - { - "name": "start", - "type": "u8" - }, - { - "name": "limit", - "type": "u8" - } - ] - }, "InvalidParameterError": { "Enum": [ { @@ -2254,9 +2154,7 @@ }, "IpfsPath": "String", "Ipv4Addr": "Array", - "Ipv4Predicate": "Array, 4>", "Ipv6Addr": "Array", - "Ipv6Predicate": "Array, 8>", "LengthLimits": { "Struct": [ { @@ -2353,7 +2251,7 @@ "MerkleTree": { "Vec": "HashOf" }, - "Metadata": "SortedMap", + "Metadata": "SortedMap", "MetadataChanged": { "Struct": [ { @@ -2366,7 +2264,7 @@ }, { "name": "value", - "type": "Value" + "type": "MetadataValueBox" } ] }, @@ -2382,7 +2280,7 @@ }, { "name": "value", - "type": "Value" + "type": "MetadataValueBox" } ] }, @@ -2398,7 +2296,7 @@ }, { "name": "value", - "type": "Value" + "type": "MetadataValueBox" } ] }, @@ -2414,7 +2312,7 @@ }, { "name": "value", - "type": "Value" + "type": "MetadataValueBox" } ] }, @@ -2446,6 +2344,45 @@ } ] }, + "MetadataValueBox": { + "Enum": [ + { + "tag": "Bool", + "discriminant": 0, + "type": "bool" + }, + { + "tag": "String", + "discriminant": 1, + "type": "String" + }, + { + "tag": "Name", + "discriminant": 2, + "type": "Name" + }, + { + "tag": "Bytes", + "discriminant": 3, + "type": "Vec" + }, + { + "tag": "Numeric", + "discriminant": 4, + "type": "Numeric" + }, + { + "tag": "LimitedMetadata", + "discriminant": 5, + "type": "Metadata" + }, + { + "tag": "Vec", + "discriminant": 6, + "type": "Vec" + } + ] + }, "Mint": { "Struct": [ { @@ -2541,18 +2478,6 @@ } ] }, - "Mismatch": { - "Struct": [ - { - "name": "expected", - "type": "AssetDefinitionId" - }, - { - "name": "actual", - "type": "AssetDefinitionId" - } - ] - }, "Mismatch": { "Struct": [ { @@ -2565,18 +2490,6 @@ } ] }, - "Mismatch": { - "Struct": [ - { - "name": "expected", - "type": "Value" - }, - { - "name": "actual", - "type": "Value" - } - ] - }, "Name": "String", "NewAccount": { "Struct": [ @@ -2650,7 +2563,7 @@ } ] }, - "NonTrivial>": "Vec>", + "NonTrivial>": "Vec>", "NonZero": "u32", "NonZero": "u64", "NotificationEvent": { @@ -2758,7 +2671,7 @@ }, { "name": "val", - "type": "Value" + "type": "ParameterValueBox" } ] }, @@ -2770,6 +2683,30 @@ } ] }, + "ParameterValueBox": { + "Enum": [ + { + "tag": "TransactionLimits", + "discriminant": 0, + "type": "TransactionLimits" + }, + { + "tag": "MetadataLimits", + "discriminant": 1, + "type": "Limits" + }, + { + "tag": "LengthLimits", + "discriminant": 2, + "type": "LengthLimits" + }, + { + "tag": "Numeric", + "discriminant": 3, + "type": "Numeric" + } + ] + }, "Peer": { "Struct": [ { @@ -3210,6 +3147,93 @@ } ] }, + "QueryOutputBox": { + "Enum": [ + { + "tag": "Id", + "discriminant": 0, + "type": "IdBox" + }, + { + "tag": "Identifiable", + "discriminant": 1, + "type": "IdentifiableBox" + }, + { + "tag": "Transaction", + "discriminant": 2, + "type": "TransactionQueryOutput" + }, + { + "tag": "PermissionToken", + "discriminant": 3, + "type": "PermissionToken" + }, + { + "tag": "PermissionTokenSchema", + "discriminant": 4, + "type": "PermissionTokenSchema" + }, + { + "tag": "LimitedMetadata", + "discriminant": 5, + "type": "MetadataValueBox" + }, + { + "tag": "Numeric", + "discriminant": 6, + "type": "Numeric" + }, + { + "tag": "BlockHeader", + "discriminant": 7, + "type": "BlockHeader" + }, + { + "tag": "Block", + "discriminant": 8, + "type": "SignedBlock" + }, + { + "tag": "Vec", + "discriminant": 9, + "type": "Vec" + } + ] + }, + "QueryOutputPredicate": { + "Enum": [ + { + "tag": "Identifiable", + "discriminant": 0, + "type": "StringPredicate" + }, + { + "tag": "Container", + "discriminant": 1, + "type": "Container" + }, + { + "tag": "Display", + "discriminant": 2, + "type": "StringPredicate" + }, + { + "tag": "Numerical", + "discriminant": 3, + "type": "SemiRange" + }, + { + "tag": "TimeStamp", + "discriminant": 4, + "type": "SemiInterval" + }, + { + "tag": "Pass", + "discriminant": 5 + } + ] + }, "QueryPayload": { "Struct": [ { @@ -3222,7 +3246,7 @@ }, { "name": "filter", - "type": "GenericPredicateBox" + "type": "GenericPredicateBox" } ] }, @@ -3333,45 +3357,6 @@ } ] }, - "RegistrableBox": { - "Enum": [ - { - "tag": "Peer", - "discriminant": 0, - "type": "Peer" - }, - { - "tag": "Domain", - "discriminant": 1, - "type": "NewDomain" - }, - { - "tag": "Account", - "discriminant": 2, - "type": "NewAccount" - }, - { - "tag": "AssetDefinition", - "discriminant": 3, - "type": "NewAssetDefinition" - }, - { - "tag": "Asset", - "discriminant": 4, - "type": "Asset" - }, - { - "tag": "Trigger", - "discriminant": 5, - "type": "Trigger" - }, - { - "tag": "Role", - "discriminant": 6, - "type": "NewRole" - } - ] - }, "RemoveKeyValue": { "Struct": [ { @@ -3631,7 +3616,7 @@ }, { "name": "value", - "type": "Value" + "type": "MetadataValueBox" } ] }, @@ -3647,7 +3632,7 @@ }, { "name": "value", - "type": "Value" + "type": "MetadataValueBox" } ] }, @@ -3663,7 +3648,7 @@ }, { "name": "value", - "type": "Value" + "type": "MetadataValueBox" } ] }, @@ -3679,7 +3664,7 @@ }, { "name": "value", - "type": "Value" + "type": "MetadataValueBox" } ] }, @@ -3914,10 +3899,10 @@ "value": "Asset" } }, - "SortedMap": { + "SortedMap": { "Map": { "key": "Name", - "value": "Value" + "value": "MetadataValueBox" } }, "SortedVec": { @@ -4031,7 +4016,7 @@ }, { "name": "metadata", - "type": "SortedMap" + "type": "SortedMap" } ] }, @@ -4347,29 +4332,18 @@ "discriminant": 0, "type": "Mismatch" }, - { - "tag": "ParameterValueType", - "discriminant": 1, - "type": "Mismatch" - }, - { - "tag": "AssetDefinitionId", - "discriminant": 2, - "type": "Mismatch" - }, { "tag": "NumericAssetValueTypeExpected", - "discriminant": 3, + "discriminant": 1, "type": "AssetValueType" }, { "tag": "StoreAssetValueTypeExpected", - "discriminant": 4, + "discriminant": 2, "type": "AssetValueType" } ] }, - "UniqueVec": "Vec", "Unregister": { "Struct": [ { @@ -4465,15 +4439,6 @@ } ] }, - "UpgradableBox": { - "Enum": [ - { - "tag": "Executor", - "discriminant": 0, - "type": "Executor" - } - ] - }, "Upgrade": { "Struct": [ { @@ -4509,194 +4474,18 @@ } ] }, - "Value": { - "Enum": [ - { - "tag": "Bool", - "discriminant": 0, - "type": "bool" - }, - { - "tag": "String", - "discriminant": 1, - "type": "String" - }, - { - "tag": "Name", - "discriminant": 2, - "type": "Name" - }, - { - "tag": "Vec", - "discriminant": 3, - "type": "Vec" - }, - { - "tag": "LimitedMetadata", - "discriminant": 4, - "type": "Metadata" - }, - { - "tag": "MetadataLimits", - "discriminant": 5, - "type": "Limits" - }, - { - "tag": "TransactionLimits", - "discriminant": 6, - "type": "TransactionLimits" - }, - { - "tag": "LengthLimits", - "discriminant": 7, - "type": "LengthLimits" - }, - { - "tag": "Id", - "discriminant": 8, - "type": "IdBox" - }, - { - "tag": "Identifiable", - "discriminant": 9, - "type": "IdentifiableBox" - }, - { - "tag": "PublicKey", - "discriminant": 10, - "type": "PublicKey" - }, - { - "tag": "SignatureCheckCondition", - "discriminant": 11, - "type": "SignatureCheckCondition" - }, - { - "tag": "TransactionQueryOutput", - "discriminant": 12, - "type": "TransactionQueryOutput" - }, - { - "tag": "PermissionToken", - "discriminant": 13, - "type": "PermissionToken" - }, - { - "tag": "PermissionTokenSchema", - "discriminant": 14, - "type": "PermissionTokenSchema" - }, - { - "tag": "Hash", - "discriminant": 15, - "type": "HashValue" - }, - { - "tag": "Block", - "discriminant": 16, - "type": "SignedBlock" - }, - { - "tag": "BlockHeader", - "discriminant": 17, - "type": "BlockHeader" - }, - { - "tag": "Ipv4Addr", - "discriminant": 18, - "type": "Ipv4Addr" - }, - { - "tag": "Ipv6Addr", - "discriminant": 19, - "type": "Ipv6Addr" - }, - { - "tag": "Numeric", - "discriminant": 20, - "type": "Numeric" - }, - { - "tag": "Integer", - "discriminant": 21, - "type": "Integer" - }, - { - "tag": "Executor", - "discriminant": 22, - "type": "Executor" - }, - { - "tag": "LogLevel", - "discriminant": 23, - "type": "Level" - } - ] - }, - "ValueOfKey": { - "Struct": [ - { - "name": "key", - "type": "Name" - }, - { - "name": "predicate", - "type": "ValuePredicate" - } - ] - }, - "ValuePredicate": { - "Enum": [ - { - "tag": "Identifiable", - "discriminant": 0, - "type": "StringPredicate" - }, - { - "tag": "Container", - "discriminant": 1, - "type": "Container" - }, - { - "tag": "Display", - "discriminant": 2, - "type": "StringPredicate" - }, - { - "tag": "Numerical", - "discriminant": 3, - "type": "SemiRange" - }, - { - "tag": "TimeStamp", - "discriminant": 4, - "type": "SemiInterval" - }, - { - "tag": "Ipv4Addr", - "discriminant": 5, - "type": "Ipv4Predicate" - }, - { - "tag": "Ipv6Addr", - "discriminant": 6, - "type": "Ipv6Predicate" - }, - { - "tag": "Pass", - "discriminant": 7 - } - ] - }, "Vec": { "Vec": "Event" }, - "Vec>": { - "Vec": "GenericPredicateBox" + "Vec>": { + "Vec": "GenericPredicateBox" }, "Vec": { "Vec": "InstructionBox" }, + "Vec": { + "Vec": "MetadataValueBox" + }, "Vec": { "Vec": "Name" }, @@ -4706,15 +4495,12 @@ "Vec": { "Vec": "PublicKey" }, - "Vec": { - "Vec": "SignedTransaction" + "Vec": { + "Vec": "QueryOutputBox" }, "Vec": { "Vec": "TransactionValue" }, - "Vec": { - "Vec": "Value" - }, "Vec>": { "Vec": "Vec" }, diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index afa9ad2242e..e6c9dcaffc1 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -29,6 +29,7 @@ pub static GENESIS_ACCOUNT_ID: Lazy = /// Genesis transaction #[derive(Debug, Clone)] +#[repr(transparent)] pub struct GenesisTransaction(pub SignedTransaction); /// [`GenesisNetwork`] contains initial transactions and genesis setup related parameters. diff --git a/primitives/derive/src/numeric.rs b/primitives/derive/src/numeric.rs index a0aaa2565e2..4c357174696 100644 --- a/primitives/derive/src/numeric.rs +++ b/primitives/derive/src/numeric.rs @@ -6,8 +6,17 @@ use quote::quote; pub fn numeric_impl(input: TokenStream) -> Result { let input = input.to_string(); - let numeric = ::iroha_numeric::Numeric::from_str(&input) + + let mut input = input.as_str(); + for pattern in ["_u8", "_u16", "_u32", "_u64", "_u128"] { + if let Some(stripped) = input.strip_suffix(pattern) { + input = stripped; + } + } + + let numeric = ::iroha_numeric::Numeric::from_str(input) .map_err(|err| error_message!("failed to parse numeric: {err}"))?; + let mantissa = numeric.mantissa(); let scale = numeric.scale(); diff --git a/primitives/numeric/src/lib.rs b/primitives/numeric/src/lib.rs index f21c0421c3e..f4118f93fef 100644 --- a/primitives/numeric/src/lib.rs +++ b/primitives/numeric/src/lib.rs @@ -212,6 +212,22 @@ impl From for Numeric { } } +impl TryFrom for u32 { + type Error = >::Error; + + fn try_from(value: Numeric) -> Result { + value.inner.try_into() + } +} + +impl TryFrom for u64 { + type Error = >::Error; + + fn try_from(value: Numeric) -> Result { + value.inner.try_into() + } +} + impl NumericSpec { /// Check if given numeric satisfy constrains /// diff --git a/primitives/src/unique_vec.rs b/primitives/src/unique_vec.rs index 728d920370e..2fa03d69ce0 100644 --- a/primitives/src/unique_vec.rs +++ b/primitives/src/unique_vec.rs @@ -44,6 +44,9 @@ macro_rules! unique_vec { Decode, IntoSchema, )] +#[repr(transparent)] +#[serde(transparent)] +#[schema(transparent)] pub struct UniqueVec(Vec); impl UniqueVec { diff --git a/schema/gen/src/lib.rs b/schema/gen/src/lib.rs index ab87b2e9fc2..4642b86ede0 100644 --- a/schema/gen/src/lib.rs +++ b/schema/gen/src/lib.rs @@ -4,7 +4,7 @@ use iroha_crypto::MerkleTree; use iroha_data_model::{ block::stream::{BlockMessage, BlockSubscriptionRequest}, - query::error::QueryExecutionFail, + query::QueryOutputBox, BatchedResponse, }; use iroha_genesis::RawGenesisBlock; @@ -17,9 +17,6 @@ macro_rules! types { macro_rules! map_all_schema_types { ($callback:ident) => {{ $( $callback!($t); )+ - - #[cfg(target_arch = "aarch64")] - $callback!(Box); }} } } @@ -41,22 +38,26 @@ pub fn build_schemas() -> MetaMap { } schemas! { - QueryExecutionFail, - BlockMessage, - BlockSubscriptionRequest, + // Transaction + SignedTransaction, + + // Query + response + SignedQuery, + BatchedResponse, + + // Event stream EventMessage, EventSubscriptionRequest, - BatchedResponse, - BatchedResponse>, - SignedQuery, - // Never referenced, but present in type signature. Like `PhantomData` - MerkleTree, - RegistrableBox, - UpgradableBox, + // Block stream + BlockMessage, + BlockSubscriptionRequest, // SDK devs want to know how to read serialized genesis block RawGenesisBlock, + + // Never referenced, but present in type signature. Like `PhantomData` + MerkleTree, } } @@ -92,23 +93,29 @@ types!( BTreeMap, BTreeMap, BTreeMap, - BTreeMap, + BTreeMap, BTreeSet, BTreeSet, - BatchedResponse, - BatchedResponse>, - BatchedResponseV1, - BatchedResponseV1>, + BTreeSet>, + BTreeSet>, + BatchedResponse, + BatchedResponseV1, BlockHeader, BlockMessage, + BlockPayload, BlockRejectionReason, BlockSubscriptionRequest, - Box>, - Box, - Box, + Box>, + Box, + Burn>, + Burn, + Burn, BurnBox, + ChainId, ConfigurationEvent, ConstString, + ConstVec, + ConstVec, Container, DataEntityFilter, DataEvent, @@ -118,6 +125,7 @@ types!( DomainEventFilter, DomainFilter, DomainId, + DomainOwnerChanged, Duration, Event, EventMessage, @@ -194,6 +202,8 @@ types!( FindTriggerKeyValueByIdAndKey, FindTriggersByDomainId, ForwardCursor, + Grant, + Grant, GrantBox, Hash, HashOf>, @@ -202,24 +212,35 @@ types!( IdBox, IdentifiableBox, InstructionBox, + InstructionEvaluationError, + InstructionExecutionError, InstructionExecutionFail, - Interval, - Interval, + InstructionType, + InvalidParameterError, IpfsPath, Ipv4Addr, - Ipv4Predicate, Ipv6Addr, - Ipv6Predicate, LengthLimits, + Level, + Log, + MathError, MerkleTree, Metadata, MetadataChanged, MetadataChanged, MetadataChanged, MetadataChanged, + MetadataError, MetadataLimits, + MetadataValueBox, + Mint>, + Mint, + Mint, + Mint, MintBox, + MintabilityError, Mintable, + Mismatch, Name, NewAccount, NewAssetDefinition, @@ -227,20 +248,26 @@ types!( NewParameter, NewRole, NonTrivial, + NonZeroU32, NonZeroU64, + NotificationEvent, NotificationEventFilter, - Integer, Numeric, + NumericSpec, + Option, Option, Option, Option, Option>>, Option>, Option, + Option, + Option, Option, Option, Option, Option, + Option, Option, Option, OriginFilter, @@ -252,6 +279,7 @@ types!( OriginFilter, Parameter, ParameterId, + ParameterValueBox, Peer, PeerEvent, PeerEventFilter, @@ -271,11 +299,26 @@ types!( PublicKey, QueryBox, QueryExecutionFail, + QueryOutputBox, + QueryOutputPredicate, QueryPayload, + Register, + Register, + Register, + Register, + Register, + Register, + Register>, RegisterBox, - RegistrableBox, + RemoveKeyValue, + RemoveKeyValue, + RemoveKeyValue, + RemoveKeyValue, RemoveKeyValueBox, Repeats, + RepetitionError, + Revoke, + Revoke, RevokeBox, Role, RoleEvent, @@ -285,23 +328,35 @@ types!( SemiInterval, SemiInterval, SemiRange, + SetKeyValue, + SetKeyValue, + SetKeyValue, + SetKeyValue, SetKeyValueBox, SetParameter, Signature, SignatureCheckCondition, + SignatureOf, SignatureOf, SignatureOf, + SignatureWrapperOf, SignatureWrapperOf, + SignaturesOf, SignaturesOf, SignedBlock, SignedBlockV1, - SignedBlockWrapper, SignedQuery, SignedQueryV1, SignedTransaction, SignedTransactionV1, + SizeError, + SocketAddr, + SocketAddrHost, + SocketAddrV4, + SocketAddrV6, String, StringPredicate, + StringWithJson, TimeEvent, TimeEventFilter, TimeInterval, @@ -312,9 +367,15 @@ types!( TransactionQueryOutput, TransactionRejectionReason, TransactionValue, + Transfer, + Transfer, + Transfer, + Transfer, TransferBox, Trigger, + TriggerCompletedEvent, TriggerCompletedEventFilter, + TriggerCompletedOutcome, TriggerCompletedOutcomeType, TriggerEvent, TriggerEventFilter, @@ -322,23 +383,29 @@ types!( TriggerId, TriggerNumberOfExecutionsChanged, TriggeringFilterBox, + TypeError, + UniqueVec, + Unregister, + Unregister, + Unregister, + Unregister, + Unregister, + Unregister, + Unregister>, UnregisterBox, - UpgradableBox, + Upgrade, ValidationFail, - Value, - ValueOfKey, - ValuePredicate, Vec, Vec, + Vec, + Vec, Vec, Vec, - Vec, - Vec, + Vec, + Vec, Vec, WasmExecutionFail, WasmSmartContract, - [Interval; 8], - [Interval; 4], [u16; 8], [u8; 32], [u8; 4], @@ -352,7 +419,7 @@ types!( #[cfg(test)] mod tests { - use core::num::NonZeroU64; + use core::num::{NonZeroU32, NonZeroU64}; use std::{ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, time::Duration, @@ -365,31 +432,42 @@ mod tests { block::{ error::BlockRejectionReason, stream::{BlockMessage, BlockSubscriptionRequest}, - BlockHeader, SignedBlock, SignedBlockV1, + BlockHeader, BlockPayload, SignedBlock, SignedBlockV1, }, domain::NewDomain, executor::Executor, ipfs::IpfsPath, - predicate::{ - ip_addr::{Ipv4Predicate, Ipv6Predicate}, - numerical::{Interval, SemiInterval, SemiRange}, - string::StringPredicate, - value::{AtIndex, Container, ValueOfKey, ValuePredicate}, - GenericPredicateBox, NonTrivial, PredicateBox, + isi::{ + error::{ + InstructionEvaluationError, InstructionExecutionError, InvalidParameterError, + MathError, MintabilityError, Mismatch, RepetitionError, TypeError, + }, + InstructionType, }, + metadata::{MetadataError, MetadataValueBox, SizeError}, + parameter::ParameterValueBox, + permission::StringWithJson, prelude::*, query::{ error::{FindError, QueryExecutionFail}, - ForwardCursor, + predicate::{ + numerical::{SemiInterval, SemiRange}, + string::StringPredicate, + value::{AtIndex, Container, QueryOutputPredicate}, + GenericPredicateBox, NonTrivial, PredicateBox, + }, + ForwardCursor, QueryOutputBox, }, transaction::{error::TransactionLimitError, SignedTransactionV1, TransactionLimits}, - BatchedResponse, BatchedResponseV1, SignedBlockWrapper, + BatchedResponse, BatchedResponseV1, Level, }; - use iroha_genesis::RawGenesisBlock; use iroha_primitives::{ - addr::{Ipv4Addr, Ipv6Addr}, + addr::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrHost, SocketAddrV4, SocketAddrV6}, + const_vec::ConstVec, conststr::ConstString, + unique_vec::UniqueVec, }; + use iroha_schema::Compact; use super::IntoSchema; @@ -412,7 +490,19 @@ mod tests { } }}; } + map_all_schema_types!(insert_into_test_map); + + insert_into_test_map!(Compact); + insert_into_test_map!(Compact); + + // NOTE: Coming from genesis + insert_into_test_map!(Vec); + insert_into_test_map!(iroha_genesis::GenesisTransactionBuilder); + insert_into_test_map!(iroha_genesis::RawGenesisBlock); + insert_into_test_map!(iroha_genesis::ExecutorMode); + insert_into_test_map!(iroha_genesis::ExecutorPath); + map } @@ -447,10 +537,8 @@ mod tests { #[test] fn no_extra_or_missing_schemas() { - let exceptions: HashSet<_> = RawGenesisBlock::schema() - .into_iter() - .map(|(type_id, _)| type_id) - .collect(); + // NOTE: Skipping Box until [this PR](https://github.com/paritytech/parity-scale-codec/pull/565) is merged + let exceptions: [core::any::TypeId; 1] = [core::any::TypeId::of::>()]; let schemas_types = super::build_schemas() .into_iter() diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index 373b4e9b854..be57fe5dc16 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -917,7 +917,9 @@ pub mod asset_definition { } pub mod asset { - use iroha_smart_contract::data_model::{isi::Instruction, metadata::Metadata}; + use iroha_smart_contract::data_model::{ + asset::AssetValue, isi::Instruction, metadata::Metadata, + }; use iroha_smart_contract_utils::Encode; use permission::{asset::is_asset_owner, asset_definition::is_asset_definition_owner}; @@ -992,7 +994,7 @@ pub mod asset { fn validate_mint_asset(executor: &mut V, authority: &AccountId, isi: &Mint) where V: Validate + ?Sized, - Q: Into, + Q: Into, Mint: Instruction + Encode, { let asset_id = isi.destination_id(); @@ -1034,7 +1036,7 @@ pub mod asset { fn validate_burn_asset(executor: &mut V, authority: &AccountId, isi: &Burn) where V: Validate + ?Sized, - Q: Into, + Q: Into, Burn: Instruction + Encode, { let asset_id = isi.destination_id(); @@ -1081,7 +1083,7 @@ pub mod asset { isi: &Transfer, ) where V: Validate + ?Sized, - Q: Into, + Q: Into, Transfer: Instruction + Encode, { let asset_id = isi.source_id(); diff --git a/smart_contract/src/lib.rs b/smart_contract/src/lib.rs index a3d6d281fea..b6663381a2b 100644 --- a/smart_contract/src/lib.rs +++ b/smart_contract/src/lib.rs @@ -11,12 +11,11 @@ use data_model::smart_contract::payloads; use data_model::{ isi::Instruction, prelude::*, - query::{cursor::ForwardCursor, sorting::Sorting, Pagination, Query}, + query::{cursor::ForwardCursor, sorting::Sorting, Pagination, Query, QueryOutputBox}, BatchedResponse, }; use derive_more::Display; pub use iroha_data_model as data_model; -use iroha_macro::error::ErrorTryFromEnum; pub use iroha_smart_contract_derive::main; pub use iroha_smart_contract_utils::{debug, error, info, log, warn}; use iroha_smart_contract_utils::{ @@ -181,7 +180,7 @@ impl ExecuteQueryOnHost for Q where Q: Query + Encode, Q::Output: DecodeAll, - >::Error: core::fmt::Debug, + >::Error: core::fmt::Debug, { type Output = Q::Output; @@ -227,7 +226,7 @@ impl QueryWithParameters<'_, Q> where Q: Query + Encode, Q::Output: DecodeAll, - >::Error: core::fmt::Debug, + >::Error: core::fmt::Debug, { /// Apply sorting to a query #[must_use] @@ -264,7 +263,7 @@ where // 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 { + let res: Result, ValidationFail> = unsafe { decode_with_length_prefix_from_raw(encode_and_execute( &QueryRequest::Query(self), host_execute_query, @@ -297,15 +296,15 @@ impl QueryOutputCursor { } } -impl QueryOutputCursor { - /// Same as [`into_inner()`](Self::into_inner) but collects all values of [`Value::Vec`] +impl QueryOutputCursor { + /// Same as [`into_inner()`](Self::into_inner) but collects all values of [`QueryOutputBox::Vec`] /// in case if there are some cached results left on the host side. /// /// # Errors /// /// May fail due to the same reasons [`QueryOutputCursorIterator`] can fail to iterate. - pub fn collect(self) -> Result>> { - let Value::Vec(v) = self.batch else { + pub fn collect(self) -> Result { + let QueryOutputBox::Vec(v) = self.batch else { return Ok(self.batch); }; @@ -317,12 +316,12 @@ impl QueryOutputCursor { cursor .into_iter() .collect::, _>>() - .map(Value::Vec) + .map(QueryOutputBox::Vec) } } -impl> IntoIterator for QueryOutputCursor> { - type Item = Result>>; +impl> IntoIterator for QueryOutputCursor> { + type Item = Result; type IntoIter = QueryOutputCursorIterator; fn into_iter(self) -> Self::IntoIter { @@ -346,8 +345,8 @@ pub struct QueryOutputCursorIterator { cursor: ForwardCursor, } -impl> QueryOutputCursorIterator { - fn next_batch(&self) -> Result>> { +impl> QueryOutputCursorIterator { + fn next_batch(&self) -> Result { #[cfg(not(test))] use host::execute_query as host_execute_query; #[cfg(test)] @@ -355,14 +354,14 @@ impl> QueryOutputCursorIterator { // 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 { + let res: Result, ValidationFail> = unsafe { decode_with_length_prefix_from_raw(encode_and_execute( &QueryRequest::::Cursor(&self.cursor), host_execute_query, )) }; let (value, cursor) = res?.into(); - let vec = Vec::::try_from(value)?; + let vec = Vec::::try_from(value).expect("Host returned unexpected output type"); Ok(Self { iter: vec.into_iter(), cursor, @@ -370,8 +369,8 @@ impl> QueryOutputCursorIterator { } } -impl> Iterator for QueryOutputCursorIterator { - type Item = Result>>; +impl> Iterator for QueryOutputCursorIterator { + type Item = Result; fn next(&mut self) -> Option { if let Some(item) = self.iter.next() { @@ -393,11 +392,9 @@ impl> Iterator for QueryOutputCursorIterator { /// Error iterating other query results. #[derive(Debug, Display, iroha_macro::FromVariant)] -pub enum QueryOutputCursorError { +pub enum QueryOutputCursorError { /// Validation error on the host side during next batch retrieval. Validation(ValidationFail), - /// Host returned unexpected output type. - Conversion(ErrorTryFromEnum), } /// Get payload for smart contract `main()` entrypoint. @@ -484,10 +481,11 @@ mod tests { } } - const QUERY_RESULT: Result, ValidationFail> = Ok(QueryOutputCursor { - batch: Value::Numeric(numeric!(1234)), - cursor: ForwardCursor::new(None, None), - }); + const QUERY_RESULT: Result, ValidationFail> = + Ok(QueryOutputCursor { + batch: QueryOutputBox::Numeric(numeric!(1234)), + cursor: ForwardCursor::new(None, None), + }); const ISI_RESULT: Result<(), ValidationFail> = Ok(()); fn get_test_instruction() -> InstructionBox { @@ -524,11 +522,12 @@ mod tests { let query = query_request.unwrap_query().0; assert_eq!(query, get_test_query()); - let response: Result, ValidationFail> = Ok(BatchedResponseV1::new( - QUERY_RESULT.unwrap().into_raw_parts().0, - ForwardCursor::new(None, None), - ) - .into()); + let response: Result, ValidationFail> = + Ok(BatchedResponseV1::new( + QUERY_RESULT.unwrap().into_raw_parts().0, + ForwardCursor::new(None, None), + ) + .into()); ManuallyDrop::new(encode_with_length_prefix(&response)).as_ptr() } diff --git a/tools/kagami/src/genesis.rs b/tools/kagami/src/genesis.rs index 87f29c7f4ce..cba35401af8 100644 --- a/tools/kagami/src/genesis.rs +++ b/tools/kagami/src/genesis.rs @@ -123,11 +123,7 @@ impl TryFrom for ExecutorMode { #[allow(clippy::too_many_lines)] pub fn generate_default(executor: ExecutorMode) -> color_eyre::Result { let mut meta = Metadata::new(); - meta.insert_with_limits( - "key".parse()?, - "value".to_owned().into(), - Limits::new(1024, 1024), - )?; + meta.insert_with_limits("key".parse()?, "value".to_owned(), Limits::new(1024, 1024))?; let mut genesis = RawGenesisBlockBuilder::default() .domain_with_metadata("wonderland".parse()?, meta.clone()) @@ -184,22 +180,43 @@ pub fn generate_default(executor: ExecutorMode) -> color_eyre::Result DumpDecodedMap { macro_rules! insert_into_map { ($t:ty) => {{ let type_id = <$t as iroha_schema::TypeId>::id(); + #[allow(trivial_casts)] map.insert(type_id, <$t as DumpDecoded>::dump_decoded as DumpDecodedPtr) }}; @@ -218,8 +230,8 @@ mod tests { let mut metadata = Metadata::new(); metadata .insert_with_limits( - "hat".parse().unwrap(), - Value::Name("white".parse().unwrap()), + "hat".parse().expect("Valid"), + "white".parse::().unwrap(), limits, ) .expect("Valid"); @@ -238,11 +250,7 @@ mod tests { let limits = MetadataLimits::new(256, 256); let mut metadata = Metadata::new(); metadata - .insert_with_limits( - "Is_Jabberwocky_alive".parse().expect("Valid"), - Value::Bool(true), - limits, - ) + .insert_with_limits("Is_Jabberwocky_alive".parse().expect("Valid"), true, limits) .expect("Valid"); let domain = Domain::new("wonderland".parse().expect("Valid")) .with_logo( diff --git a/torii/src/routing.rs b/torii/src/routing.rs index 54599a6fb82..02c1a9685c0 100644 --- a/torii/src/routing.rs +++ b/torii/src/routing.rs @@ -20,7 +20,7 @@ use iroha_data_model::{ }, prelude::*, query::{ - cursor::ForwardCursor, http, sorting::Sorting, Pagination, QueryRequest, + cursor::ForwardCursor, http, sorting::Sorting, Pagination, QueryOutputBox, QueryRequest, QueryWithParameters, }, BatchedResponse, @@ -104,7 +104,7 @@ pub async fn handle_queries( sumeragi: SumeragiHandle, query_request: http::ClientQueryRequest, -) -> Result>> { +) -> Result>> { let handle = task::spawn_blocking(move || match query_request.0 { QueryRequest::Query(QueryWithParameters { query: signed_query,