diff --git a/Cargo.lock b/Cargo.lock index 2d46b12b1e4..491bdd04fc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2049,6 +2049,7 @@ dependencies = [ name = "iroha_futures" version = "2.0.0-pre-rc.8" dependencies = [ + "iroha_config", "iroha_futures_derive", "iroha_logger", "rand 0.8.5", diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 72cb470c01b..9eb04e71f05 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -13,7 +13,10 @@ use std::{panic, path::PathBuf, sync::Arc}; use color_eyre::eyre::{eyre, Result, WrapErr}; use iroha_actor::{broker::*, prelude::*}; -use iroha_config::iroha::Configuration; +use iroha_config::{ + base::proxy::{LoadFromDisk, LoadFromEnv}, + iroha::{Configuration, ConfigurationProxy}, +}; use iroha_core::{ block_sync::{BlockSynchronizer, BlockSynchronizerTrait}, genesis::{GenesisNetwork, GenesisNetworkTrait, RawGenesisBlock}, @@ -124,11 +127,9 @@ where query_judge: QueryJudgeBoxed, ) -> Result { let broker = Broker::new(); - let mut config = match Configuration::from_path(&args.config_path) { - Ok(config) => config, - Err(_) => Configuration::default(), - }; - config.load_environment()?; + let mut proxy = ConfigurationProxy::from_path(&args.config_path)?; + proxy.load_environment()?; + let config = proxy.build()?; let telemetry = iroha_logger::init(&config.logger)?; iroha_logger::info!("Hyperledgerいろは2にようこそ!"); diff --git a/cli/src/samples.rs b/cli/src/samples.rs index fc5010715de..dbe4faa104a 100644 --- a/cli/src/samples.rs +++ b/cli/src/samples.rs @@ -3,15 +3,9 @@ use std::{collections::HashSet, str::FromStr}; use iroha_config::{ - block_sync::Configuration as BlockSyncConfiguration, - genesis::Configuration as GenesisConfiguration, - iroha::Configuration, - kura::Configuration as KuraConfiguration, - queue::Configuration as QueueConfiguration, - sumeragi::{Configuration as SumeragiConfiguration, TrustedPeers}, - torii::{Configuration as ToriiConfiguration, DEFAULT_TORII_P2P_ADDR}, - wasm::Configuration as WasmConfiguration, - wsv::Configuration as WsvConfiguration, + iroha::{Configuration, ConfigurationProxy}, + sumeragi::TrustedPeers, + torii::{uri::DEFAULT_API_URL, DEFAULT_TORII_P2P_ADDR, DEFAULT_TORII_TELEMETRY_URL}, }; use iroha_crypto::{KeyPair, PublicKey}; use iroha_data_model::peer::Id as PeerId; @@ -65,48 +59,49 @@ pub fn get_config(trusted_peers: HashSet, key_pair: Option) -> .into(), }; iroha_logger::info!(?public_key); - Configuration { - public_key: public_key.clone(), - private_key: private_key.clone(), - kura: KuraConfiguration { - init_mode: iroha_config::kura::Mode::Strict, - ..KuraConfiguration::default() - }, - sumeragi: SumeragiConfiguration { - key_pair: KeyPair::new(public_key.clone(), private_key.clone()) - .expect("Key pair mismatch"), - peer_id: PeerId::new(DEFAULT_TORII_P2P_ADDR, &public_key), - trusted_peers: TrustedPeers { + ConfigurationProxy { + public_key: Some(public_key.clone()), + private_key: Some(private_key.clone()), + sumeragi: Some(iroha_config::sumeragi::ConfigurationProxy { + key_pair: Some( + KeyPair::new(public_key.clone(), private_key.clone()).expect("Key pair mismatch"), + ), + peer_id: Some(PeerId::new(DEFAULT_TORII_P2P_ADDR, &public_key)), + trusted_peers: Some(TrustedPeers { peers: trusted_peers, - }, - gossip_period_ms: 500, - ..SumeragiConfiguration::default() - }, - torii: ToriiConfiguration { - max_transaction_size: 0x8000, - ..ToriiConfiguration::default() - }, - block_sync: BlockSyncConfiguration { - block_batch_size: 1, - gossip_period_ms: 5000, - ..BlockSyncConfiguration::default() - }, - queue: QueueConfiguration { - maximum_transactions_in_block: 2, - ..QueueConfiguration::default() - }, - genesis: GenesisConfiguration { - account_public_key: public_key, - account_private_key: Some(private_key), - ..GenesisConfiguration::default() - }, - wsv: WsvConfiguration { - wasm_runtime_config: WasmConfiguration { - fuel_limit: 10_000_000, - ..WasmConfiguration::default() - }, - ..WsvConfiguration::default() - }, - ..Configuration::default() + }), + ..iroha_config::sumeragi::ConfigurationProxy::default() + }), + torii: Some(iroha_config::torii::ConfigurationProxy { + p2p_addr: Some(DEFAULT_TORII_P2P_ADDR.to_owned()), + api_url: Some(DEFAULT_API_URL.to_owned()), + telemetry_url: Some(DEFAULT_TORII_TELEMETRY_URL.to_owned()), + max_transaction_size: Some(0x8000), + ..iroha_config::torii::ConfigurationProxy::default() + }), + block_sync: Some(iroha_config::block_sync::ConfigurationProxy { + block_batch_size: Some(1), + gossip_period_ms: Some(500), + ..iroha_config::block_sync::ConfigurationProxy::default() + }), + queue: Some(iroha_config::queue::ConfigurationProxy { + maximum_transactions_in_block: Some(2), + ..iroha_config::queue::ConfigurationProxy::default() + }), + genesis: Some(iroha_config::genesis::ConfigurationProxy { + account_private_key: Some(Some(private_key)), + account_public_key: Some(public_key), + ..iroha_config::genesis::ConfigurationProxy::default() + }), + wsv: Some(iroha_config::wsv::ConfigurationProxy { + wasm_runtime_config: Some(iroha_config::wasm::ConfigurationProxy { + fuel_limit: Some(10_000_000), + ..iroha_config::wasm::ConfigurationProxy::default() + }), + ..iroha_config::wsv::ConfigurationProxy::default() + }), + ..ConfigurationProxy::default() } + .build() + .expect("Iroha config should build as all required fields were provided") } diff --git a/client/src/client.rs b/client/src/client.rs index 878ed29bc72..793d4f8ad12 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -1353,7 +1353,11 @@ mod tests { #![allow(clippy::restriction)] use std::str::FromStr; - use iroha_config::client::{BasicAuth, WebLogin}; + use iroha_config::{ + base::proxy::Builder, + client::{BasicAuth, ConfigurationProxy, WebLogin}, + torii::{uri::DEFAULT_API_URL, DEFAULT_TORII_TELEMETRY_URL}, + }; use super::*; @@ -1366,12 +1370,21 @@ mod tests { fn txs_same_except_for_nonce_have_different_hashes() { let (public_key, private_key) = KeyPair::generate().unwrap().into(); - let cfg = Configuration { - public_key, - private_key, - add_transaction_nonce: true, - ..Configuration::default() - }; + let cfg = ConfigurationProxy { + public_key: Some(public_key), + private_key: Some(private_key), + account_id: Some( + "alice@wonderland" + .parse() + .expect("Account ID should not fail"), + ), + torii_api_url: Some(SmallStr::from_str(DEFAULT_API_URL)), + torii_telemetry_url: Some(SmallStr::from_str(DEFAULT_TORII_TELEMETRY_URL)), + add_transaction_nonce: Some(true), + ..ConfigurationProxy::default() + } + .build() + .expect("Client config should build as all required fields were provided"); let client = Client::new(&cfg).expect("Invalid client configuration"); let build_transaction = || { @@ -1395,10 +1408,28 @@ mod tests { password: SmallStr::from_str(PASSWORD), }; - let cfg = Configuration { - basic_auth: Some(basic_auth), - ..Configuration::default() - }; + let cfg = ConfigurationProxy { + public_key: Some( + "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" + .parse() + .expect("Public key not in mulithash format"), + ), + private_key: Some(iroha_crypto::PrivateKey::from_hex( + iroha_crypto::Algorithm::Ed25519, + "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" + ).expect("Private key not hex encoded")), + account_id: Some( + "alice@wonderland" + .parse() + .expect("Account ID should not fail"), + ), + torii_api_url: Some(SmallStr::from_str(DEFAULT_API_URL)), + torii_telemetry_url: Some(SmallStr::from_str(DEFAULT_TORII_TELEMETRY_URL)), + basic_auth: Some(Some(basic_auth)), + ..ConfigurationProxy::default() + } + .build() + .expect("Client config should build as all required fields were provided"); let client = Client::new(&cfg).expect("Invalid client configuration"); let value = client diff --git a/client/src/lib.rs b/client/src/lib.rs index 7b6aeeb82e5..68cf1d6180e 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,7 +1,5 @@ //! Crate contains iroha client which talks to iroha network via http -pub use iroha_config::client::Configuration; - /// Module with iroha client itself pub mod client; /// Module with general communication primitives like an HTTP request builder. @@ -10,21 +8,31 @@ mod http_default; /// Module containing sample configurations for tests and benchmarks. pub mod samples { - use iroha_config::torii::uri; + use iroha_config::{ + base::proxy::Builder, + client::{Configuration, ConfigurationProxy}, + torii::{uri::DEFAULT_API_URL, DEFAULT_TORII_TELEMETRY_URL}, + }; use iroha_crypto::KeyPair; - - use super::Configuration; + use iroha_primitives::small::SmallStr; /// Get sample client configuration. #[allow(clippy::expect_used)] pub fn get_client_config(key_pair: &KeyPair) -> Configuration { let (public_key, private_key) = key_pair.clone().into(); - Configuration { - public_key, - private_key, - account_id: "alice@wonderland".parse().expect("Should not fail."), - torii_api_url: iroha_primitives::small::SmallStr::from_str(uri::DEFAULT_API_URL), - ..Configuration::default() + ConfigurationProxy { + public_key: Some(public_key), + private_key: Some(private_key), + account_id: Some( + "alice@wonderland" + .parse() + .expect("Account ID should not fail"), + ), + torii_api_url: Some(SmallStr::from_str(DEFAULT_API_URL)), + torii_telemetry_url: Some(SmallStr::from_str(DEFAULT_TORII_TELEMETRY_URL)), + ..ConfigurationProxy::default() } + .build() + .expect("Client config should build as all required fields were provided") } } diff --git a/client/tests/integration/config.rs b/client/tests/integration/config.rs index e01ce3e2244..ec1d1668a7f 100644 --- a/client/tests/integration/config.rs +++ b/client/tests/integration/config.rs @@ -2,7 +2,7 @@ use test_network::*; -use super::Configuration; +use super::{Builder, Configuration, ConfigurationProxy}; #[test] fn get_config() { @@ -13,10 +13,16 @@ fn get_config() { let field = test_client.get_config_docs(&["torii"]).unwrap().unwrap(); assert!(field.contains("IROHA_TORII")); - let cfg: Configuration = - serde_json::from_value(test_client.get_config_value().unwrap()).unwrap(); let test = Configuration::test(); - assert_eq!(cfg.block_sync, test.block_sync); - assert_eq!(cfg.network, test.network); - assert_eq!(cfg.telemetry, test.telemetry); + let cfg_proxy: ConfigurationProxy = + serde_json::from_value(test_client.get_config_value().unwrap()).unwrap(); + assert_eq!( + cfg_proxy.block_sync.unwrap().build().unwrap(), + test.block_sync + ); + assert_eq!(cfg_proxy.network.unwrap().build().unwrap(), test.network); + assert_eq!( + cfg_proxy.telemetry.unwrap().build().unwrap(), + test.telemetry + ); } diff --git a/client/tests/integration/mod.rs b/client/tests/integration/mod.rs index cbae044a513..e90670e3f98 100644 --- a/client/tests/integration/mod.rs +++ b/client/tests/integration/mod.rs @@ -1,4 +1,7 @@ -pub use iroha_config::iroha::Configuration; +pub use iroha_config::{ + base::proxy::Builder, + iroha::{Configuration, ConfigurationProxy}, +}; mod add_account; mod add_domain; diff --git a/config/base/derive/src/documented.rs b/config/base/derive/src/documented.rs index 069e1ae00e4..74a83aee516 100644 --- a/config/base/derive/src/documented.rs +++ b/config/base/derive/src/documented.rs @@ -1,9 +1,9 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; -use syn::{Lit, LitStr, Meta}; +use syn::{parse_quote, Lit, LitStr, Meta, Path}; -use super::utils::StructWithFields; +use super::utils::{get_inner_type, StructWithFields}; pub fn impl_documented(ast: &StructWithFields) -> TokenStream { let name = &ast.ident; @@ -47,17 +47,30 @@ fn impl_get_doc_recursive(docs: Vec, ast: &StructWithFields) -> proc_mac .zip(docs) .map(|(field, documentation)| { let inner_thing = field.has_inner; + let option_thing = field.has_option; let ty = &field.ty; let ident = &field.ident; - if inner_thing { + let documented_trait: Path = parse_quote! { iroha_config_base::proxy::Documented }; + if inner_thing && option_thing { + let inner_ty = get_inner_type("Option", &field.ty); quote! { [stringify!(#ident)] => { let curr_doc = #documentation; - let inner_docs = <#ty as ::iroha_config_base::proxy::Documented>::get_inner_docs(); + let inner_docs = <#inner_ty as #documented_trait>::get_inner_docs(); let total_docs = format!("{}\n\nHas following fields:\n\n{}\n", curr_doc, inner_docs); Some(total_docs) }, - [stringify!(#ident), rest @ ..] => <#ty as ::iroha_config_base::proxy::Documented>::get_doc_recursive(rest)?, + [stringify!(#ident), rest @ ..] => <#inner_ty as #documented_trait>::get_doc_recursive(rest)?, + } + } else if inner_thing { + quote! { + [stringify!(#ident)] => { + let curr_doc = #documentation; + let inner_docs = <#ty as #documented_trait>::get_inner_docs(); + let total_docs = format!("{}\n\nHas following fields:\n\n{}\n", curr_doc, inner_docs); + Some(total_docs) + }, + [stringify!(#ident), rest @ ..] => <#ty as #documented_trait>::get_doc_recursive(rest)?, } } else { quote! { [stringify!(#ident)] => Some(#documentation.to_owned()), } @@ -84,10 +97,17 @@ fn impl_get_doc_recursive(docs: Vec, ast: &StructWithFields) -> proc_mac fn impl_get_inner_docs(docs: Vec, ast: &StructWithFields) -> proc_macro2::TokenStream { let inserts = ast.fields.iter().zip(docs).map(|(field, documentation)| { let inner_thing = field.has_inner; + let option_thing = field.has_option; let ty = &field.ty; let ident = &field.ident; - let doc = if inner_thing { - quote! { <#ty as ::iroha_config_base::proxy::Documented>::get_inner_docs().as_str() } + let documented_trait: Path = parse_quote! { ::iroha_config_base::proxy::Documented }; + let doc = if inner_thing && option_thing { + let inner_ty = get_inner_type("Option", &field.ty); + quote! { + <#inner_ty as #documented_trait>::get_inner_docs().as_str() + } + } else if inner_thing { + quote! { <#ty as #documented_trait>::get_inner_docs().as_str() } } else { quote! { #documentation.into() } }; @@ -114,8 +134,13 @@ fn impl_get_docs(docs: Vec, ast: &StructWithFields) -> proc_macro2::Toke let ident = &field.ident; let ty = &field.ty; let inner_thing = field.has_inner; - let doc = if inner_thing { - quote! { <#ty as ::iroha_config_base::proxy::Documented>::get_docs().into() } + let option_thing = field.has_option; + let documented_trait: Path = parse_quote! { iroha_config_base::proxy::Documented }; + let doc = if inner_thing && option_thing { + let inner_ty = get_inner_type("Option", &field.ty); + quote! { <#inner_ty as #documented_trait>::get_docs().into() } + } else if inner_thing { + quote! { <#ty as #documented_trait>::get_docs().into() } } else { quote! { #documentation.into() } }; @@ -153,9 +178,19 @@ fn impl_get_recursive(ast: &StructWithFields) -> proc_macro2::TokenStream { .iter() .map(|field | { let inner_thing = field.has_inner; + let option_thing = field.has_option; + // if ast.ident == "ConfigurationProxy" {println!("FIELD: {}, INNER: {}, OPTION: {}", field.ident, field.has_inner, field.has_option);} let ident = &field.ident; let l_value = &field.lvalue_read; - let inner_thing2 = if inner_thing { + let inner_thing2 = if inner_thing && option_thing { + let inner_ty = get_inner_type("Option", &field.ty); + let documented_trait: Path = parse_quote! { iroha_config_base::proxy::Documented }; + quote! { + [stringify!(#ident), rest @ ..] => { + <#inner_ty as #documented_trait>::get_recursive(#l_value.as_ref().expect("Should be instantiated"), rest)? + }, + } + } else if inner_thing { quote! { [stringify!(#ident), rest @ ..] => { #l_value.get_recursive(rest)? diff --git a/config/base/derive/src/proxy.rs b/config/base/derive/src/proxy.rs index e7b915bb118..04cde2182d5 100644 --- a/config/base/derive/src/proxy.rs +++ b/config/base/derive/src/proxy.rs @@ -14,16 +14,20 @@ pub fn impl_proxy(ast: StructWithFields) -> TokenStream { let disk_derive = quote! { ::iroha_config_base::derive::LoadFromDisk }; let builder_derive = quote! { ::iroha_config_base::derive::Builder }; let combine_derive = quote! { ::iroha_config_base::derive::Combine }; + let documented_derive = quote! { ::iroha_config_base::derive::Documented }; quote! { /// Proxy configuration structure to be used as an intermediate /// for config loading - #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, #builder_derive, #loadenv_derive, #disk_derive, - #combine_derive)] + #combine_derive, + #documented_derive + )] #[builder(parent = #parent_ty)] #proxy_struct + } .into() } @@ -154,13 +158,20 @@ fn gen_proxy_struct(mut ast: StructWithFields) -> StructWithFields { field.ty = parse_quote! { Option<#ty> }; + // Fields that already wrap an option should have a + // custom deserializer so that json `null` becomes + // `Some(None)` and not just `None` + if field.has_option { + let de_helper = stringify! { ::iroha_config_base::proxy::some_option }; + let serde_attr: syn::Attribute = + parse_quote! { #[serde(default, deserialize_with = #de_helper)] }; + field.attrs.push(serde_attr); + } field.has_option = true; - // Also remove `#[serde(default = ..)]` if present - // as it breaks proxy deserialization - utils::remove_attr(&mut field.attrs, "serde"); }); ast.ident = format_ident!("{}Proxy", ast.ident); - // Removing as `..Proxy` has its own doc + // Removing struct-level docs as `..Proxy` has its own doc, + // but not the field documentation as they stay the same utils::remove_attr(&mut ast.attrs, "doc"); ast } diff --git a/config/base/derive/src/utils.rs b/config/base/derive/src/utils.rs index 52b490e5ba2..af2e8f718ae 100644 --- a/config/base/derive/src/utils.rs +++ b/config/base/derive/src/utils.rs @@ -214,7 +214,6 @@ impl ToTokens for StructField { pub struct StructWithFields { pub attrs: Vec, pub env_prefix: String, - // pub parent_ty: Type, pub vis: syn::Visibility, _struct_token: Token![struct], pub ident: Ident, @@ -232,12 +231,6 @@ impl Parse for StructWithFields { .find_map(Result::ok) .map(|pref| pref.prefix.value()) .unwrap_or_default(); - // let parent_ty = attrs - // .iter() - // .map(Builder::::parse) - // .find_map(Result::ok) - // .map(|builder| builder.parent) - // .expect("???"); Ok(Self { attrs, vis: input.parse()?, diff --git a/config/base/derive/src/view.rs b/config/base/derive/src/view.rs index 759c9809194..f3ae0de2d53 100644 --- a/config/base/derive/src/view.rs +++ b/config/base/derive/src/view.rs @@ -12,7 +12,6 @@ pub fn impl_view(ast: StructWithFields) -> TokenStream { let original = original_struct(ast.clone()); let view = view_struct(ast); let impl_from = impl_from(&original, &view); - let impl_default = impl_default(&original, &view); let impl_has_view = impl_has_view(&original); let assertions = assertions(&view); let out = quote! { @@ -20,7 +19,6 @@ pub fn impl_view(ast: StructWithFields) -> TokenStream { #impl_has_view #view #impl_from - #impl_default #assertions }; out.into() @@ -46,8 +44,6 @@ mod gen { ast.attrs.push(syn::parse_quote!( #[doc = #view_doc] )); - // TODO: Remove `Default` from #[derive(..., Default, ...)] or #[derive(Default)] because we implement `Default` inside macro - // TODO: also add info with remove proxy ast.attrs .iter_mut() .filter(|attr| attr.path.is_ident("derive")) @@ -55,36 +51,31 @@ mod gen { let meta = attr .parse_meta() .expect("derive macro must be in one of the meta forms"); - match meta { - Meta::List(list) => { - let items: Vec = list - .nested - .into_iter() - .filter(|nested| { - if let NestedMeta::Meta(Meta::Path(path)) = nested { - // TODO: check here - if path.is_ident("Default") - || path.is_ident("Documented") - || path.is_ident("Proxy") - { - return false; - } + if let Meta::List(list) = meta { + let items: Vec = list + .nested + .into_iter() + .filter(|nested| { + if let NestedMeta::Meta(Meta::Path(path)) = nested { + // remove derives that are needed on the `Configuration` + // or `ConfigurationProxy`, but not needed on `ConfigruationView` + if path.is_ident("LoadFromEnv") + || path.is_ident("Builder") + || path.is_ident("Proxy") + { + return false; } - true - }) - .collect(); - *attr = syn::parse_quote!( - #[derive(#(#items),*)] - ) - } - Meta::Path(path) if path.is_ident("Default") => { - *attr = syn::parse_quote!( - #[derive()] - ) - } - _ => {} + } + true + }) + .collect(); + *attr = syn::parse_quote!( + #[derive(#(#items),*)] + ) } }); + // TODO: Find a way to make this more ergonomic. As `View` struct + // are formed inside a proc macro, we have to remove unrelated attributes from `Configuration` here. remove_attr_struct(&mut ast, "view"); remove_attr_struct(&mut ast, "config"); remove_attr_struct(&mut ast, "builder"); @@ -128,30 +119,6 @@ mod gen { } } - pub fn impl_default( - original: &StructWithFields, - view: &StructWithFields, - ) -> proc_macro2::TokenStream { - let StructWithFields { - ident: original_ident, - .. - } = original; - let StructWithFields { - generics, - ident: view_ident, - .. - } = view; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - quote! { - impl #impl_generics core::default::Default for #view_ident #ty_generics #where_clause { - fn default() -> Self { - core::convert::From::<_>::from(<#original_ident as core::default::Default>::default()) - } - } - } - } - pub fn impl_has_view(original: &StructWithFields) -> proc_macro2::TokenStream { let StructWithFields { generics, diff --git a/config/base/src/lib.rs b/config/base/src/lib.rs index 01e989e03b8..25c3f294fd5 100644 --- a/config/base/src/lib.rs +++ b/config/base/src/lib.rs @@ -2,7 +2,7 @@ #![allow(clippy::std_instead_of_core)] use std::{fmt::Debug, path::Path}; -use serde::{de::DeserializeOwned, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; use serde_json::Value; pub mod derive { @@ -329,4 +329,18 @@ pub mod proxy { /// Construct [`Self::ReturnValue`] from a proxy object. fn build(self) -> Self::ReturnValue; } + + /// Deserialization helper for proxy fields that wrap an `Option` + /// + /// # Errors + /// When deserialization of the field fails, e.g. it doesn't have + /// the `Option>` + #[allow(clippy::option_option)] + pub fn some_option<'de, T, D>(deserializer: D) -> Result>, D::Error> + where + T: Deserialize<'de>, + D: Deserializer<'de>, + { + Option::::deserialize(deserializer).map(Some) + } } diff --git a/config/src/block_sync.rs b/config/src/block_sync.rs index f3a483511e0..e45ecfeced9 100644 --- a/config/src/block_sync.rs +++ b/config/src/block_sync.rs @@ -12,7 +12,6 @@ const DEFAULT_ACTOR_CHANNEL_CAPACITY: u32 = 100; Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Documented, Proxy, LoadFromEnv, )] #[serde(rename_all = "UPPERCASE")] -#[serde(default)] #[config(env_prefix = "BLOCK_SYNC_")] pub struct Configuration { /// The period of time to wait between sending requests for the latest block. @@ -24,12 +23,12 @@ pub struct Configuration { pub actor_channel_capacity: u32, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { Self { - gossip_period_ms: DEFAULT_GOSSIP_PERIOD_MS, - block_batch_size: DEFAULT_BLOCK_BATCH_SIZE, - actor_channel_capacity: DEFAULT_ACTOR_CHANNEL_CAPACITY, + gossip_period_ms: Some(DEFAULT_GOSSIP_PERIOD_MS), + block_batch_size: Some(DEFAULT_BLOCK_BATCH_SIZE), + actor_channel_capacity: Some(DEFAULT_ACTOR_CHANNEL_CAPACITY), } } } diff --git a/config/src/client.rs b/config/src/client.rs index a00be406dbd..cd7ef3d2d39 100644 --- a/config/src/client.rs +++ b/config/src/client.rs @@ -1,18 +1,15 @@ //! Module for client-related configuration and structs #![allow(clippy::std_instead_of_core, clippy::std_instead_of_alloc)] -use std::{fmt, fs::File, io::BufReader, path::Path, str::FromStr}; +use std::str::FromStr; use derive_more::Display; -use eyre::{eyre, Result, WrapErr}; +use eyre::{eyre, Result}; use iroha_config_base::derive::{Documented, LoadFromEnv, Proxy}; use iroha_crypto::prelude::*; use iroha_data_model::{prelude::*, transaction}; use iroha_primitives::small::SmallStr; use serde::{Deserialize, Serialize}; -use crate::torii::uri; - -const DEFAULT_TORII_TELEMETRY_URL: &str = "127.0.0.1:8180"; const DEFAULT_TRANSACTION_TIME_TO_LIVE_MS: u64 = 100_000; const DEFAULT_TRANSACTION_STATUS_TIMEOUT_MS: u64 = 10_000; const DEFAULT_ADD_TRANSACTION_NONCE: bool = false; @@ -65,7 +62,6 @@ pub struct BasicAuth { /// `Configuration` provides an ability to define client parameters such as `TORII_URL`. #[derive(Debug, Clone, Deserialize, Serialize, Proxy, LoadFromEnv, Documented)] #[serde(rename_all = "UPPERCASE")] -#[serde(default)] #[config(env_prefix = "IROHA_")] pub struct Configuration { /// Public key of the user account. @@ -91,61 +87,22 @@ pub struct Configuration { pub add_transaction_nonce: bool, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { - let (public_key, private_key) = Self::placeholder_keypair().into(); - Self { - public_key, - private_key, - account_id: Self::placeholder_account(), - basic_auth: None, - torii_api_url: SmallStr::from_str(uri::DEFAULT_API_URL), - torii_telemetry_url: SmallStr::from_str(DEFAULT_TORII_TELEMETRY_URL), - transaction_time_to_live_ms: DEFAULT_TRANSACTION_TIME_TO_LIVE_MS, - transaction_status_timeout_ms: DEFAULT_TRANSACTION_STATUS_TIMEOUT_MS, - transaction_limits: TransactionLimits { + public_key: None, + private_key: None, + account_id: None, + basic_auth: Some(None), + torii_api_url: None, + torii_telemetry_url: None, + transaction_time_to_live_ms: Some(DEFAULT_TRANSACTION_TIME_TO_LIVE_MS), + transaction_status_timeout_ms: Some(DEFAULT_TRANSACTION_STATUS_TIMEOUT_MS), + transaction_limits: Some(TransactionLimits { max_instruction_number: transaction::DEFAULT_MAX_INSTRUCTION_NUMBER, max_wasm_size_bytes: transaction::DEFAULT_MAX_WASM_SIZE_BYTES, - }, - add_transaction_nonce: DEFAULT_ADD_TRANSACTION_NONCE, + }), + add_transaction_nonce: Some(DEFAULT_ADD_TRANSACTION_NONCE), } } } - -impl Configuration { - /// Key-pair used by default for demo purposes - #[allow(clippy::expect_used)] - fn placeholder_keypair() -> KeyPair { - let public_key = "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" - .parse() - .expect("Public key not in mulithash format"); - let private_key = PrivateKey::from_hex( - Algorithm::Ed25519, - "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" - ).expect("Private key not hex encoded"); - - KeyPair::new(public_key, private_key).expect("Key pair mismatch") - } - - /// Account ID used by default for demo purposes - #[allow(clippy::expect_used)] - fn placeholder_account() -> ::Id { - AccountId::from_str("alice@wonderland").expect("Account ID not valid") - } - - // TODO: Delete this after `LoadFromDisk` is implemented - /// This method will build `Configuration` from a json *pretty* formatted file (without `:` in - /// key names). - /// - /// # Panics - /// If configuration file present, but has incorrect format. - /// - /// # Errors - /// If system fails to find a file or read it's content. - pub fn from_path + fmt::Debug>(path: P) -> Result { - let file = File::open(path).wrap_err("Failed to open the config file")?; - let reader = BufReader::new(file); - serde_json::from_reader(reader).wrap_err("Failed to deserialize json from reader") - } -} diff --git a/config/src/genesis.rs b/config/src/genesis.rs index f23b46b09c2..c1c8182ab6e 100644 --- a/config/src/genesis.rs +++ b/config/src/genesis.rs @@ -2,7 +2,7 @@ #![allow(clippy::std_instead_of_core)] use iroha_config_base::derive::{view, Documented, LoadFromEnv, Proxy}; -use iroha_crypto::{KeyPair, PrivateKey, PublicKey}; +use iroha_crypto::{PrivateKey, PublicKey}; use serde::{Deserialize, Serialize}; const DEFAULT_WAIT_FOR_PEERS_RETRY_COUNT_LIMIT: u64 = 100; @@ -13,7 +13,6 @@ const DEFAULT_GENESIS_SUBMISSION_DELAY_MS: u64 = 1000; view! { /// Configuration of the genesis block and the process of its submission. #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Documented, Proxy, LoadFromEnv)] - #[serde(default)] #[serde(rename_all = "UPPERCASE")] #[config(env_prefix = "IROHA_GENESIS_")] pub struct Configuration { @@ -24,56 +23,23 @@ view! { #[view(ignore)] pub account_private_key: Option, /// The number of attempts to connect to peers while waiting for them to submit genesis. - #[serde(default = "default_wait_for_peers_retry_count_limit")] pub wait_for_peers_retry_count_limit: u64, /// The period in milliseconds in which to retry connecting to peers while waiting for them to submit genesis. - #[serde(default = "default_wait_for_peers_retry_period_ms")] pub wait_for_peers_retry_period_ms: u64, /// The delay before genesis block submission after minimum number of peers were discovered to be online. /// The delay between submissions, which is used to ensure that other peers had time to connect to each other. - #[serde(default = "default_genesis_submission_delay_ms")] pub genesis_submission_delay_ms: u64, } } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { - let (public_key, private_key) = Self::placeholder_keypair().into(); - Self { - account_public_key: public_key, - account_private_key: Some(private_key), - wait_for_peers_retry_count_limit: DEFAULT_WAIT_FOR_PEERS_RETRY_COUNT_LIMIT, - wait_for_peers_retry_period_ms: DEFAULT_WAIT_FOR_PEERS_RETRY_PERIOD_MS, - genesis_submission_delay_ms: DEFAULT_GENESIS_SUBMISSION_DELAY_MS, + account_public_key: None, + account_private_key: Some(None), + wait_for_peers_retry_count_limit: Some(DEFAULT_WAIT_FOR_PEERS_RETRY_COUNT_LIMIT), + wait_for_peers_retry_period_ms: Some(DEFAULT_WAIT_FOR_PEERS_RETRY_PERIOD_MS), + genesis_submission_delay_ms: Some(DEFAULT_GENESIS_SUBMISSION_DELAY_MS), } } } - -impl Configuration { - /// Key-pair used by default for demo purposes - #[allow(clippy::expect_used)] - fn placeholder_keypair() -> KeyPair { - let public_key = "ed01204cffd0ee429b1bdd36b3910ec570852b8bb63f18750341772fb46bc856c5caaf" - .parse() - .expect("Public key not in mulithash format"); - let private_key = PrivateKey::from_hex( - iroha_crypto::Algorithm::Ed25519, - "d748e18ce60cb30dea3e73c9019b7af45a8d465e3d71bcc9a5ef99a008205e534cffd0ee429b1bdd36b3910ec570852b8bb63f18750341772fb46bc856c5caaf" - ).expect("Private key not hex encoded"); - - KeyPair::new(public_key, private_key).expect("Key pair mismatch") - } -} - -const fn default_wait_for_peers_retry_count_limit() -> u64 { - DEFAULT_WAIT_FOR_PEERS_RETRY_COUNT_LIMIT -} - -const fn default_wait_for_peers_retry_period_ms() -> u64 { - DEFAULT_WAIT_FOR_PEERS_RETRY_PERIOD_MS -} - -const fn default_genesis_submission_delay_ms() -> u64 { - DEFAULT_GENESIS_SUBMISSION_DELAY_MS -} diff --git a/config/src/iroha.rs b/config/src/iroha.rs index c296b41fd20..8d7c10b97ab 100644 --- a/config/src/iroha.rs +++ b/config/src/iroha.rs @@ -3,7 +3,10 @@ use std::{fmt::Debug, fs::File, io::BufReader, path::Path}; use eyre::{Result, WrapErr}; -use iroha_config_base::derive::{view, Documented, LoadFromEnv, Proxy}; +use iroha_config_base::{ + derive::{view, Documented, Error as ConfigError, LoadFromEnv, Proxy}, + proxy::Builder, +}; use iroha_crypto::prelude::*; use serde::{Deserialize, Serialize}; @@ -12,8 +15,7 @@ use super::*; // Generate `ConfigurationView` without the private key view! { /// Configuration parameters for a peer - #[derive(Debug, Clone, Deserialize, Serialize, Proxy, LoadFromEnv, Documented)] - #[serde(default)] + #[derive(Debug, Clone, Deserialize, Serialize, Proxy, Documented, LoadFromEnv)] #[serde(rename_all = "UPPERCASE")] #[config(env_prefix = "IROHA_")] pub struct Configuration { @@ -60,26 +62,82 @@ view! { } } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { - let sumeragi_configuration = sumeragi::Configuration::default(); - let (public_key, private_key) = sumeragi_configuration.key_pair.clone().into(); - Self { - public_key, - private_key, - disable_panic_terminal_colors: bool::default(), - kura: kura::Configuration::default(), - sumeragi: sumeragi_configuration, - torii: torii::Configuration::default(), - block_sync: block_sync::Configuration::default(), - queue: queue::Configuration::default(), - logger: logger::Configuration::default(), - genesis: genesis::Configuration::default(), - wsv: wsv::Configuration::default(), - network: network::Configuration::default(), - telemetry: telemetry::Configuration::default(), + public_key: None, + private_key: None, + disable_panic_terminal_colors: Some(bool::default()), + kura: Some(kura::ConfigurationProxy::default()), + sumeragi: Some(sumeragi::ConfigurationProxy::default()), + torii: Some(torii::ConfigurationProxy::default()), + block_sync: Some(block_sync::ConfigurationProxy::default()), + queue: Some(queue::ConfigurationProxy::default()), + logger: Some(logger::ConfigurationProxy::default()), + genesis: Some(genesis::ConfigurationProxy::default()), + wsv: Some(wsv::ConfigurationProxy::default()), + network: Some(network::ConfigurationProxy::default()), + telemetry: Some(telemetry::ConfigurationProxy::default()), + } + } +} + +impl ConfigurationProxy { + /// Finalisation of Iroha config, i.e. in case where only some of mutually equivalent fields + /// were provided, instantiation of all the missing fields happens. + /// + /// # Errors + /// Bails early if none of the fields from the corresponding mutually equivalent set were + /// provided. + #[allow(clippy::expect_used, clippy::unwrap_in_result)] + pub fn finalize(&mut self) -> Result<()> { + if let Some(sumeragi_proxy) = &mut self.sumeragi { + // First, iroha public/private key and sumeragi keypair are interchangeable, but + // the user is allowed to provide only the former, and keypair is generated automatically + if let (Some(public_key), Some(private_key)) = (&self.public_key, &self.private_key) { + sumeragi_proxy.key_pair = + Some(KeyPair::new(public_key.clone(), private_key.clone())?); + } else { + eyre::bail!(ConfigError::ProxyBuildError( + "Neither Iroha public and private key nor sumeragi keypair supplied".to_owned() + )) + } + // Second, torii gateway and sumeragi peer id are interchangeable too + if let Some(torii_proxy) = &mut self.torii { + if let Some(peer_id) = &sumeragi_proxy.peer_id { + torii_proxy.p2p_addr = Some(peer_id.address.clone()); + } else { + sumeragi_proxy.peer_id = Some(iroha_data_model::peer::Id::new( + &torii_proxy.p2p_addr.clone().ok_or_else(|| { + eyre::eyre!("Torii `p2p_addr` field has `None` value") + })?, + &self.public_key.clone().expect( + "Iroha `public_key` should have been initialized above at the latest", + ), + )); + } + } + // Finally, if trusted peers were not supplied, we can fall back to inserting itself as + // the only trusted one + if sumeragi_proxy.trusted_peers.is_none() { + sumeragi_proxy.insert_self_as_trusted_peers() + } } + + Ok(()) + } + + /// Wrapper around for the topmost Iroha `ConfigurationProxy` + /// that performs finalisation prior to building. + /// + /// # Errors + /// - Finalisation fails + /// - Any of the inner fields had a `None` value when that + /// is not allowed by the defaults. + pub fn build(mut self) -> Result { + self.finalize()?; + ::build(self) + .wrap_err("Failed to build `Configuration` from `ConfigurationProxy`") } } diff --git a/config/src/kura.rs b/config/src/kura.rs index 74453f2a9dc..cf717ed6b77 100644 --- a/config/src/kura.rs +++ b/config/src/kura.rs @@ -16,26 +16,26 @@ const DEFAULT_ACTOR_CHANNEL_CAPACITY: u32 = 100; #[config(env_prefix = "KURA_")] pub struct Configuration { /// Initialization mode: `strict` or `fast`. - #[serde(default)] pub init_mode: Mode, /// Path to the existing block store folder or path to create new folder. - #[serde(default = "default_block_store_path")] pub block_store_path: String, /// Maximum number of blocks to write into a single storage file. - #[serde(default = "default_blocks_per_storage_file")] pub blocks_per_storage_file: NonZeroU64, /// Default buffer capacity of actor's MPSC channel. - #[serde(default = "default_actor_channel_capacity")] pub actor_channel_capacity: u32, } -impl Default for Configuration { +impl Default for ConfigurationProxy { + #[allow(clippy::expect_used)] fn default() -> Self { Self { - init_mode: Mode::default(), - block_store_path: default_block_store_path(), - blocks_per_storage_file: default_blocks_per_storage_file(), - actor_channel_capacity: default_actor_channel_capacity(), + init_mode: Some(Mode::default()), + block_store_path: Some(DEFAULT_BLOCK_STORE_PATH.to_owned()), + blocks_per_storage_file: Some( + NonZeroU64::new(DEFAULT_BLOCKS_PER_STORAGE_FILE) + .expect("BLOCKS_PER_STORAGE cannot be set to a non-positive value."), + ), + actor_channel_capacity: Some(DEFAULT_ACTOR_CHANNEL_CAPACITY), } } } @@ -54,20 +54,6 @@ impl Configuration { } } -fn default_block_store_path() -> String { - DEFAULT_BLOCK_STORE_PATH.to_owned() -} - -fn default_blocks_per_storage_file() -> NonZeroU64 { - #![allow(clippy::expect_used)] - NonZeroU64::new(DEFAULT_BLOCKS_PER_STORAGE_FILE) - .expect("BLOCKS_PER_STORAGE cannot be set to a non-positive value.") -} - -const fn default_actor_channel_capacity() -> u32 { - DEFAULT_ACTOR_CHANNEL_CAPACITY -} - /// Kura initialization mode. #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] diff --git a/config/src/logger.rs b/config/src/logger.rs index 98114fe2bb5..cbbed124bac 100644 --- a/config/src/logger.rs +++ b/config/src/logger.rs @@ -80,7 +80,6 @@ impl From for SyncLevel { /// 'Logger' configuration. #[derive(Clone, Deserialize, Serialize, Debug, Proxy, LoadFromEnv, Documented)] #[serde(rename_all = "UPPERCASE")] -#[serde(default)] pub struct Configuration { /// Maximum log level #[config(serde_as_str)] @@ -96,14 +95,14 @@ pub struct Configuration { pub terminal_colors: bool, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { Self { - max_log_level: SyncLevel::default(), - telemetry_capacity: TELEMETRY_CAPACITY, - compact_mode: DEFAULT_COMPACT_MODE, - log_file_path: None, - terminal_colors: DEFAULT_TERMINAL_COLORS, + max_log_level: Some(SyncLevel::default()), + telemetry_capacity: Some(TELEMETRY_CAPACITY), + compact_mode: Some(DEFAULT_COMPACT_MODE), + log_file_path: Some(None), + terminal_colors: Some(DEFAULT_TERMINAL_COLORS), } } } diff --git a/config/src/network.rs b/config/src/network.rs index b0db1a4f0cb..b76e82cec1b 100644 --- a/config/src/network.rs +++ b/config/src/network.rs @@ -9,7 +9,6 @@ const DEFAULT_ACTOR_CHANNEL_CAPACITY: u32 = 100; #[derive( Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Documented, Proxy, LoadFromEnv, )] -#[serde(default)] #[serde(rename_all = "UPPERCASE")] #[config(env_prefix = "IROHA_NETWORK_")] pub struct Configuration { @@ -17,10 +16,10 @@ pub struct Configuration { pub actor_channel_capacity: u32, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { Self { - actor_channel_capacity: DEFAULT_ACTOR_CHANNEL_CAPACITY, + actor_channel_capacity: Some(DEFAULT_ACTOR_CHANNEL_CAPACITY), } } } diff --git a/config/src/queue.rs b/config/src/queue.rs index 6e92852f922..78b02befb3c 100644 --- a/config/src/queue.rs +++ b/config/src/queue.rs @@ -14,7 +14,6 @@ const DEFAULT_FUTURE_THRESHOLD_MS: u64 = 1000; Copy, Clone, Deserialize, Serialize, Debug, Documented, Proxy, LoadFromEnv, PartialEq, Eq, )] #[serde(rename_all = "UPPERCASE")] -#[serde(default)] #[config(env_prefix = "QUEUE_")] pub struct Configuration { /// The upper limit of the number of transactions per block. @@ -27,13 +26,13 @@ pub struct Configuration { pub future_threshold_ms: u64, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { Self { - maximum_transactions_in_block: DEFAULT_MAXIMUM_TRANSACTIONS_IN_BLOCK, - maximum_transactions_in_queue: DEFAULT_MAXIMUM_TRANSACTIONS_IN_QUEUE, - transaction_time_to_live_ms: DEFAULT_TRANSACTION_TIME_TO_LIVE_MS, - future_threshold_ms: DEFAULT_FUTURE_THRESHOLD_MS, + maximum_transactions_in_block: Some(DEFAULT_MAXIMUM_TRANSACTIONS_IN_BLOCK), + maximum_transactions_in_queue: Some(DEFAULT_MAXIMUM_TRANSACTIONS_IN_QUEUE), + transaction_time_to_live_ms: Some(DEFAULT_TRANSACTION_TIME_TO_LIVE_MS), + future_threshold_ms: Some(DEFAULT_FUTURE_THRESHOLD_MS), } } } diff --git a/config/src/sumeragi.rs b/config/src/sumeragi.rs index 77098267dc4..73ebe8685f0 100644 --- a/config/src/sumeragi.rs +++ b/config/src/sumeragi.rs @@ -24,13 +24,12 @@ view! { /// `Sumeragi` configuration. /// [`struct@Configuration`] provides an ability to define parameters such as `BLOCK_TIME_MS` /// and a list of `TRUSTED_PEERS`. - #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Documented, Proxy, LoadFromEnv)] - #[serde(default)] + #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Proxy, Documented, LoadFromEnv)] #[serde(rename_all = "UPPERCASE")] #[config(env_prefix = "SUMERAGI_")] pub struct Configuration { /// The key pair consisting of a private and a public key. - #[serde(skip)] + //TODO: consider putting a `#[serde(skip)]` on the proxy struct here #[view(ignore)] pub key_pair: KeyPair, /// Current Peer Identification. @@ -54,63 +53,44 @@ view! { } } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { Self { - key_pair: Self::placeholder_keypair(), - peer_id: Self::placeholder_peer_id(), - trusted_peers: Self::placeholder_trusted_peers(), - block_time_ms: DEFAULT_BLOCK_TIME_MS, - commit_time_limit_ms: DEFAULT_COMMIT_TIME_LIMIT_MS, - tx_receipt_time_limit_ms: DEFAULT_TX_RECEIPT_TIME_LIMIT_MS, - transaction_limits: TransactionLimits { + key_pair: None, + peer_id: None, + trusted_peers: None, + block_time_ms: Some(DEFAULT_BLOCK_TIME_MS), + commit_time_limit_ms: Some(DEFAULT_COMMIT_TIME_LIMIT_MS), + tx_receipt_time_limit_ms: Some(DEFAULT_TX_RECEIPT_TIME_LIMIT_MS), + transaction_limits: Some(TransactionLimits { max_instruction_number: transaction::DEFAULT_MAX_INSTRUCTION_NUMBER, max_wasm_size_bytes: transaction::DEFAULT_MAX_WASM_SIZE_BYTES, - }, - actor_channel_capacity: DEFAULT_ACTOR_CHANNEL_CAPACITY, - gossip_batch_size: DEFAULT_GOSSIP_BATCH_SIZE, - gossip_period_ms: DEFAULT_GOSSIP_PERIOD_MS, + }), + actor_channel_capacity: Some(DEFAULT_ACTOR_CHANNEL_CAPACITY), + gossip_batch_size: Some(DEFAULT_GOSSIP_BATCH_SIZE), + gossip_period_ms: Some(DEFAULT_GOSSIP_PERIOD_MS), } } } - -impl Configuration { - /// Key-pair used by default for demo purposes - #[allow(clippy::expect_used)] - fn placeholder_keypair() -> KeyPair { - let public_key = "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b" - .parse() - .expect("Public key not in mulithash format"); - let private_key = PrivateKey::from_hex( - Algorithm::Ed25519, - "282ed9f3cf92811c3818dbc4ae594ed59dc1a2f78e4241e31924e101d6b1fb831c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b" - ).expect("Private key not hex encoded"); - - KeyPair::new(public_key, private_key).expect("Key pair mismatch") - } - - fn placeholder_peer_id() -> PeerId { - let (public_key, _) = Self::placeholder_keypair().into(); - - PeerId { - address: "127.0.0.1:1337".to_owned(), - public_key, - } - } - - fn placeholder_trusted_peers() -> TrustedPeers { +impl ConfigurationProxy { + /// To be used for proxy finalisation. Should only be + /// used if no peers present. + /// + /// # Panics + /// The [`peer_id`] field of [`Self`] + /// has not been initialized prior to calling this method. + pub fn insert_self_as_trusted_peers(&mut self) { let mut peers = HashSet::new(); - peers.insert(Self::placeholder_peer_id()); - TrustedPeers { peers } - } - - /// Set `trusted_peers` configuration parameter. Will overwrite - /// existing `trusted_peers` but does not check for duplication. - #[inline] - pub fn set_trusted_peers(&mut self, trusted_peers: Vec) { - self.trusted_peers.peers = trusted_peers.into_iter().collect(); + #[allow(clippy::expect_used)] + let peer_id = self + .peer_id + .clone() + .expect("`peer_id` field should be initialized"); + peers.insert(peer_id); + self.trusted_peers = Some(TrustedPeers { peers }); } - +} +impl Configuration { /// Time estimation from receiving a transaction to storing it in /// a block on all peers for the "sunny day" scenario. #[inline] diff --git a/config/src/telemetry.rs b/config/src/telemetry.rs index 012f25fa2ca..b557e840d8c 100644 --- a/config/src/telemetry.rs +++ b/config/src/telemetry.rs @@ -7,7 +7,6 @@ use url::Url; /// Configuration parameters container #[derive(Clone, Deserialize, Serialize, Debug, Proxy, LoadFromEnv, Documented, PartialEq, Eq)] #[serde(rename_all = "UPPERCASE")] -#[serde(default)] #[config(env_prefix = "TELEMETRY_")] pub struct Configuration { /// The node's name to be seen on the telemetry @@ -17,36 +16,26 @@ pub struct Configuration { #[config(serde_as_str)] pub url: Option, /// The minimum period of time in seconds to wait before reconnecting - #[serde(default = "default_min_retry_period")] pub min_retry_period: u64, /// The maximum exponent of 2 that is used for increasing delay between reconnections - #[serde(default = "default_max_retry_delay_exponent")] pub max_retry_delay_exponent: u8, /// The filepath that to write dev-telemetry to #[config(serde_as_str)] pub file: Option, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { Self { - name: None, - url: None, - min_retry_period: retry_period::DEFAULT_MIN_RETRY_PERIOD, - max_retry_delay_exponent: retry_period::DEFAULT_MAX_RETRY_DELAY_EXPONENT, - file: None, + name: Some(None), + url: Some(None), + min_retry_period: Some(retry_period::DEFAULT_MIN_RETRY_PERIOD), + max_retry_delay_exponent: Some(retry_period::DEFAULT_MAX_RETRY_DELAY_EXPONENT), + file: Some(None), } } } -const fn default_min_retry_period() -> u64 { - retry_period::DEFAULT_MIN_RETRY_PERIOD -} - -const fn default_max_retry_delay_exponent() -> u8 { - retry_period::DEFAULT_MAX_RETRY_DELAY_EXPONENT -} - /// `RetryPeriod` configuration pub mod retry_period { /// Default minimal retry period diff --git a/config/src/torii.rs b/config/src/torii.rs index bf981a5e620..c13994a625a 100644 --- a/config/src/torii.rs +++ b/config/src/torii.rs @@ -17,7 +17,6 @@ pub const DEFAULT_TORII_MAX_CONTENT_LENGTH: u32 = 2_u32.pow(12) * 4000; /// as well as `max_transaction_size`. #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Documented, Proxy, LoadFromEnv)] #[serde(rename_all = "UPPERCASE")] -#[serde(default)] #[config(env_prefix = "TORII_")] pub struct Configuration { /// Torii URL for p2p communication for consensus and block synchronization purposes. @@ -32,14 +31,14 @@ pub struct Configuration { pub max_content_len: u32, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { Self { - p2p_addr: DEFAULT_TORII_P2P_ADDR.to_owned(), - api_url: uri::DEFAULT_API_URL.to_owned(), - telemetry_url: DEFAULT_TORII_TELEMETRY_URL.to_owned(), - max_transaction_size: DEFAULT_TORII_MAX_TRANSACTION_SIZE, - max_content_len: DEFAULT_TORII_MAX_CONTENT_LENGTH, + p2p_addr: None, + api_url: None, + telemetry_url: None, + max_transaction_size: Some(DEFAULT_TORII_MAX_TRANSACTION_SIZE), + max_content_len: Some(DEFAULT_TORII_MAX_CONTENT_LENGTH), } } } diff --git a/config/src/wasm.rs b/config/src/wasm.rs index 4c2cbcbcd96..250216a7432 100644 --- a/config/src/wasm.rs +++ b/config/src/wasm.rs @@ -11,7 +11,7 @@ const DEFAULT_MAX_MEMORY: u32 = 500 * 2_u32.pow(20); // 500 MiB Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Documented, Proxy, LoadFromEnv, )] #[config(env_prefix = "WASM_")] -#[serde(rename_all = "UPPERCASE", default)] +#[serde(rename_all = "UPPERCASE")] pub struct Configuration { /// The fuel limit determines the maximum number of instructions that can be executed during the execution of a smart contract. /// Every WASM instruction costs approximately 1 unit of fuel. See @@ -21,11 +21,11 @@ pub struct Configuration { pub max_memory: u32, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { - Configuration { - fuel_limit: DEFAULT_FUEL_LIMIT, - max_memory: DEFAULT_MAX_MEMORY, + Self { + fuel_limit: Some(DEFAULT_FUEL_LIMIT), + max_memory: Some(DEFAULT_MAX_MEMORY), } } } diff --git a/config/src/wsv.rs b/config/src/wsv.rs index 4f5f2f2258d..473adc6138c 100644 --- a/config/src/wsv.rs +++ b/config/src/wsv.rs @@ -15,7 +15,7 @@ const DEFAULT_IDENT_LENGTH_LIMITS: LengthLimits = LengthLimits::new(1, 2_u32.pow Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Proxy, LoadFromEnv, Documented, )] #[config(env_prefix = "WSV_")] -#[serde(rename_all = "UPPERCASE", default)] +#[serde(rename_all = "UPPERCASE")] pub struct Configuration { /// [`MetadataLimits`] for every asset with store. pub asset_metadata_limits: MetadataLimits, @@ -28,18 +28,19 @@ pub struct Configuration { /// [`LengthLimits`] for the number of chars in identifiers that can be stored in the WSV. pub ident_length_limits: LengthLimits, /// WASM runtime configuration + #[config(inner)] pub wasm_runtime_config: wasm::Configuration, } -impl Default for Configuration { +impl Default for ConfigurationProxy { fn default() -> Self { - Configuration { - asset_metadata_limits: DEFAULT_METADATA_LIMITS, - asset_definition_metadata_limits: DEFAULT_METADATA_LIMITS, - account_metadata_limits: DEFAULT_METADATA_LIMITS, - domain_metadata_limits: DEFAULT_METADATA_LIMITS, - ident_length_limits: DEFAULT_IDENT_LENGTH_LIMITS, - wasm_runtime_config: wasm::Configuration::default(), + Self { + asset_metadata_limits: Some(DEFAULT_METADATA_LIMITS), + asset_definition_metadata_limits: Some(DEFAULT_METADATA_LIMITS), + account_metadata_limits: Some(DEFAULT_METADATA_LIMITS), + domain_metadata_limits: Some(DEFAULT_METADATA_LIMITS), + ident_length_limits: Some(DEFAULT_IDENT_LENGTH_LIMITS), + wasm_runtime_config: Some(wasm::ConfigurationProxy::default()), } } } diff --git a/core/src/genesis.rs b/core/src/genesis.rs index 92785c9f1df..eb3dd6ba6f7 100644 --- a/core/src/genesis.rs +++ b/core/src/genesis.rs @@ -421,9 +421,12 @@ impl RawGenesisDomainBuilder { #[cfg(test)] mod tests { + use iroha_config::{base::proxy::Builder, genesis::ConfigurationProxy}; + use super::*; #[test] + #[allow(clippy::expect_used)] fn load_default_genesis_block() -> Result<()> { let (public_key, private_key) = KeyPair::generate()?.into(); let tx_limits = TransactionLimits { @@ -436,7 +439,9 @@ mod tests { Some(&Configuration { account_public_key: public_key, account_private_key: Some(private_key), - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default genesis config should always build") }), &tx_limits, )?; diff --git a/core/src/queue.rs b/core/src/queue.rs index 960743dd270..6a6df0196b9 100644 --- a/core/src/queue.rs +++ b/core/src/queue.rs @@ -246,6 +246,7 @@ mod tests { time::{Duration, Instant}, }; + use iroha_config::{base::proxy::Builder, queue::ConfigurationProxy}; use iroha_data_model::prelude::*; use rand::Rng; @@ -299,7 +300,9 @@ mod tests { maximum_transactions_in_block: 2, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: 100, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); @@ -323,7 +326,9 @@ mod tests { maximum_transactions_in_block: 2, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: max_txs_in_queue, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); @@ -362,7 +367,9 @@ mod tests { maximum_transactions_in_block: 2, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: max_txs_in_queue, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); @@ -388,7 +395,9 @@ mod tests { maximum_transactions_in_block: 2, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: 100, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); @@ -436,7 +445,9 @@ mod tests { maximum_transactions_in_block: max_block_tx, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: 100, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); @@ -465,7 +476,9 @@ mod tests { maximum_transactions_in_block: max_block_tx, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: 100, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); @@ -486,7 +499,9 @@ mod tests { maximum_transactions_in_block: max_block_tx, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: 100, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, Arc::clone(&wsv), ); @@ -508,7 +523,9 @@ mod tests { maximum_transactions_in_block: max_block_tx, transaction_time_to_live_ms: 200, maximum_transactions_in_queue: 100, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); @@ -545,7 +562,9 @@ mod tests { maximum_transactions_in_block: 2, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: 100, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); @@ -580,7 +599,9 @@ mod tests { maximum_transactions_in_block: max_block_tx, transaction_time_to_live_ms: 100_000, maximum_transactions_in_queue: 100_000_000, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, )); @@ -644,7 +665,9 @@ mod tests { let queue = Queue::from_configuration( &Configuration { future_threshold_ms, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default queue config should always build") }, wsv, ); diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index c3c65333283..bf67a5b01ff 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -10,7 +10,10 @@ use anyhow::anyhow; use eyre::Context; -use iroha_config::wasm::Configuration; +use iroha_config::{ + base::proxy::Builder, + wasm::{Configuration, ConfigurationProxy}, +}; use iroha_data_model::{permission, prelude::*, ParseError}; use parity_scale_codec::{Decode, Encode}; use wasmtime::{ @@ -232,9 +235,12 @@ impl<'wrld> Runtime<'wrld> { /// # Errors /// /// If unable to construct runtime + #[allow(clippy::unwrap_in_result)] pub fn new() -> Result { let engine = Self::create_engine()?; - let config = Configuration::default(); + let config = ConfigurationProxy::default() + .build() + .expect("Wasm proxy always builds"); let linker = Self::create_linker(&engine)?; diff --git a/core/src/wsv.rs b/core/src/wsv.rs index 19ee9c1bccf..1e842ea284e 100644 --- a/core/src/wsv.rs +++ b/core/src/wsv.rs @@ -15,7 +15,10 @@ use dashmap::{ }; use eyre::Result; use getset::Getters; -use iroha_config::wsv::Configuration; +use iroha_config::{ + base::proxy::Builder, + wsv::{Configuration, ConfigurationProxy}, +}; use iroha_crypto::HashOf; use iroha_data_model::prelude::*; use iroha_logger::prelude::*; @@ -133,10 +136,14 @@ impl WorldStateView { /// Construct [`WorldStateView`] with given [`World`]. #[must_use] #[inline] + #[allow(clippy::expect_used)] pub fn new(world: World) -> Self { // Added to remain backward compatible with other code primary in tests let (events_sender, _) = broadcast::channel(1); - Self::from_configuration(Configuration::default(), world, events_sender) + let config = ConfigurationProxy::default() + .build() + .expect("Wsv proxy always builds"); + Self::from_configuration(config, world, events_sender) } /// Get `Account`'s `Asset`s diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 1bd164dd1f2..37d07eb4167 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -12,6 +12,7 @@ default = [] telemetry = ["iroha_futures_derive/telemetry"] [dependencies] +iroha_config = { version = "=2.0.0-pre-rc.8", path = "../config" } iroha_futures_derive = { version = "=2.0.0-pre-rc.8", path = "derive" } iroha_logger = { version = "=2.0.0-pre-rc.8", path = "../logger" } diff --git a/futures/tests/basic.rs b/futures/tests/basic.rs index f600e776af9..c3a17c6ba7a 100644 --- a/futures/tests/basic.rs +++ b/futures/tests/basic.rs @@ -2,8 +2,9 @@ use std::{thread, time::Duration}; +use iroha_config::base::proxy::Builder; use iroha_futures::FuturePollTelemetry; -use iroha_logger::Configuration; +use iroha_logger::ConfigurationProxy; use tokio::task; use tokio_stream::{wrappers::ReceiverStream, StreamExt}; @@ -33,9 +34,13 @@ async fn test_sleep() { Duration::from_nanos(80_000_000), ]; - let (_, telemetry_future) = iroha_logger::init(&Configuration::default()) - .unwrap() - .unwrap(); + let (_, telemetry_future) = iroha_logger::init( + &ConfigurationProxy::default() + .build() + .expect("Default logger config always builds"), + ) + .unwrap() + .unwrap(); assert_eq!(sleep(sleep_times.clone()).await, 10_i32); let telemetry = ReceiverStream::new(telemetry_future) .map(FuturePollTelemetry::try_from) diff --git a/logger/src/lib.rs b/logger/src/lib.rs index 3aa6fa0c7a2..82a1a0d3edd 100644 --- a/logger/src/lib.rs +++ b/logger/src/lib.rs @@ -19,7 +19,7 @@ use std::{ }; use color_eyre::{eyre::WrapErr, Report, Result}; -pub use iroha_config::logger::{Configuration, Level}; +pub use iroha_config::logger::{Configuration, ConfigurationProxy, Level}; pub use telemetry::{Telemetry, TelemetryFields, TelemetryLayer}; use tokio::sync::mpsc::Receiver; pub use tracing::{ diff --git a/logger/tests/setting_logger.rs b/logger/tests/setting_logger.rs index ac75325921f..da82ff950e3 100644 --- a/logger/tests/setting_logger.rs +++ b/logger/tests/setting_logger.rs @@ -1,11 +1,21 @@ #![allow(clippy::restriction, clippy::expect_used)] -use iroha_logger::{init, Configuration}; +use iroha_config::base::proxy::Builder; +use iroha_logger::{init, ConfigurationProxy}; #[tokio::test] async fn setting_logger_twice_fails() { - assert!(init(&Configuration::default()).is_ok()); - let second_init = init(&Configuration::default()); + assert!(init( + &ConfigurationProxy::default() + .build() + .expect("Default logger config always builds") + ) + .is_ok()); + let second_init = init( + &ConfigurationProxy::default() + .build() + .expect("Default logger config always builds"), + ); assert!(second_init.is_ok()); assert!(second_init.unwrap().is_none()); } diff --git a/logger/tests/telemetry.rs b/logger/tests/telemetry.rs index d896d154d62..50507412673 100644 --- a/logger/tests/telemetry.rs +++ b/logger/tests/telemetry.rs @@ -2,12 +2,15 @@ use std::time::Duration; -use iroha_logger::{info, init, Configuration, Telemetry, TelemetryFields}; +use iroha_config::base::proxy::Builder; +use iroha_logger::{info, init, ConfigurationProxy, Telemetry, TelemetryFields}; use tokio::time; #[tokio::test] async fn telemetry_separation_default() { - let (mut receiver, _) = init(&Configuration::default()).unwrap().unwrap(); + let (mut receiver, _) = init(&ConfigurationProxy::default().build().unwrap()) + .unwrap() + .unwrap(); info!(target: "telemetry::test", a = 2, c = true, d = "this won't be logged"); info!("This will be logged"); let telemetry = Telemetry { diff --git a/p2p/tests/integration/p2p.rs b/p2p/tests/integration/p2p.rs index 0028247cd22..5dd9642a1d7 100644 --- a/p2p/tests/integration/p2p.rs +++ b/p2p/tests/integration/p2p.rs @@ -11,8 +11,9 @@ use std::{ use futures::{prelude::*, stream::FuturesUnordered}; use iroha_actor::{broker::*, prelude::*}; +use iroha_config::base::proxy::Builder; use iroha_crypto::{KeyPair, PublicKey}; -use iroha_logger::{prelude::*, Configuration, Level}; +use iroha_logger::{prelude::*, Configuration, ConfigurationProxy, Level}; use iroha_p2p::{ network::{ConnectedPeers, GetConnectedPeers}, peer::PeerId, @@ -36,7 +37,9 @@ fn setup_logger() { let log_config = Configuration { max_log_level: Level::TRACE.into(), compact_mode: false, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default logger config failed to build. This is a programmer error") }; iroha_logger::init(&log_config).expect("Failed to start logger"); }) @@ -203,7 +206,9 @@ async fn multiple_networks() { let log_config = Configuration { max_log_level: Level::TRACE.into(), compact_mode: false, - ..Configuration::default() + ..ConfigurationProxy::default() + .build() + .expect("Default logger config should always build") }; // Can't use logger because it's failed to initialize. #[allow(clippy::print_stderr)] diff --git a/telemetry/src/ws.rs b/telemetry/src/ws.rs index f2b93d3fb31..9b82a07d508 100644 --- a/telemetry/src/ws.rs +++ b/telemetry/src/ws.rs @@ -275,6 +275,7 @@ mod tests { use eyre::{eyre, Result}; use futures::{Sink, StreamExt}; + use iroha_config::base::proxy::Builder; use iroha_logger::telemetry::{Telemetry, TelemetryFields}; use serde_json::{Map, Value}; use tokio::task::JoinHandle; @@ -575,7 +576,12 @@ mod tests { ($ident:ident, $future:ident) => { #[tokio::test] async fn $ident() { - iroha_logger::init(&iroha_logger::Configuration::default()).unwrap(); + iroha_logger::init( + &iroha_logger::ConfigurationProxy::default() + .build() + .expect("Default logger config should always build"), + ) + .unwrap(); let (suite, run_handle) = Suite::new(); $future(suite).await; run_handle.await.unwrap(); diff --git a/tools/kagami/src/main.rs b/tools/kagami/src/main.rs index 65925dd91b3..c34fe7e298a 100644 --- a/tools/kagami/src/main.rs +++ b/tools/kagami/src/main.rs @@ -10,7 +10,6 @@ use std::io::{stdout, BufWriter, Write}; use clap::{ArgGroup, StructOpt}; use color_eyre::eyre::WrapErr as _; -use iroha_config::iroha::Configuration; pub type Outcome = color_eyre::Result<()>; @@ -223,7 +222,7 @@ mod docs { use std::{fmt::Debug, io::Write}; use color_eyre::eyre::WrapErr as _; - use iroha_config::base::proxy::Documented; + use iroha_config::{base::proxy::Documented, iroha::ConfigurationProxy}; use serde_json::Value; use super::*; @@ -235,7 +234,7 @@ mod docs { impl RunArgs for Args { fn run(self, writer: &mut BufWriter) -> crate::Outcome { - Configuration::get_markdown(writer).wrap_err("Failed to generate documentation") + ConfigurationProxy::get_markdown(writer).wrap_err("Failed to generate documentation") } }