From 821465215145164ce1492d3785352157a41d51d2 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Nov 2020 16:03:00 +0900 Subject: [PATCH 01/10] Use ./app-desc.toml as default value of app_desc_path --- foundry/config/mod.rs | 9 +++++++-- foundry/run_node.rs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/foundry/config/mod.rs b/foundry/config/mod.rs index c87b8fe4dd..d686547120 100644 --- a/foundry/config/mod.rs +++ b/foundry/config/mod.rs @@ -40,8 +40,13 @@ pub struct Config { pub config: Option, // operating - #[conf(no_short, long = "app-desc-path", help = "Specify the app descriptor path.")] - pub app_desc_path: Option, + #[conf( + no_short, + long = "app-desc-path", + help = "Specify the app descriptor path.", + default = "\"./app-desc.toml\".to_string()" + )] + pub app_desc_path: String, #[conf( short = "i", diff --git a/foundry/run_node.rs b/foundry/run_node.rs index 5ca8298f72..52c269e659 100644 --- a/foundry/run_node.rs +++ b/foundry/run_node.rs @@ -234,7 +234,7 @@ pub fn run_node( let time_gap_params = config.create_time_gaps(); - let mut app_desc = AppDesc::from_str(&fs::read_to_string(config.app_desc_path.as_ref().unwrap()).unwrap()).unwrap(); + let mut app_desc = AppDesc::from_str(&fs::read_to_string(&config.app_desc_path).unwrap()).unwrap(); app_desc.merge_params(&module_arguments).expect("error in merge params"); let coordinator = Arc::new(Coordinator::from_app_desc(&app_desc).unwrap()); From 5101383700914ff20c6512bc33acfebd59348f49 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Nov 2020 16:31:35 +0900 Subject: [PATCH 02/10] Use better error message to parse app descriptor Before this commit, error messages were hard to read since Foundry prints a long call stack. --- foundry/run_node.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/foundry/run_node.rs b/foundry/run_node.rs index 52c269e659..9433eab2e8 100644 --- a/foundry/run_node.rs +++ b/foundry/run_node.rs @@ -234,8 +234,15 @@ pub fn run_node( let time_gap_params = config.create_time_gaps(); - let mut app_desc = AppDesc::from_str(&fs::read_to_string(&config.app_desc_path).unwrap()).unwrap(); - app_desc.merge_params(&module_arguments).expect("error in merge params"); + let mut app_desc = { + let app_desc_path = &config.app_desc_path; + let app_desc_string = fs::read_to_string(app_desc_path) + .map_err(|err| format!("Foundry failed to read an app desc at {}: {}", app_desc_path, err))?; + AppDesc::from_str(&app_desc_string).map_err(|err| format!("Foundry failed to parse app descriptor: {}", err))? + }; + app_desc + .merge_params(&module_arguments) + .map_err(|err| format!("Foundry failed to merge params you supplied into the app descriptor. {}", err))?; let coordinator = Arc::new(Coordinator::from_app_desc(&app_desc).unwrap()); let genesis = Genesis::new(app_desc.host.genesis, coordinator.as_ref()); From ba89c9a3f5e597d5b2b235f728fbdd99ef7da90d Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Mon, 9 Nov 2020 19:12:36 +0900 Subject: [PATCH 03/10] Extract app descriptor value types They will be used in the link descriptor. --- coordinator/src/app_desc.rs | 284 +------------------------------ coordinator/src/desc_common.rs | 295 +++++++++++++++++++++++++++++++++ coordinator/src/lib.rs | 2 + 3 files changed, 301 insertions(+), 280 deletions(-) create mode 100644 coordinator/src/desc_common.rs diff --git a/coordinator/src/app_desc.rs b/coordinator/src/app_desc.rs index 4c97fd38a6..4943081337 100644 --- a/coordinator/src/app_desc.rs +++ b/coordinator/src/app_desc.rs @@ -14,24 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::borrow::Borrow; -use std::collections::{BTreeMap, HashMap}; -use std::{fmt, fmt::Display, fmt::Formatter}; - -use primitives::H256; -use serde::de::{DeserializeOwned, DeserializeSeed, Error, Unexpected}; -use serde::de::{MapAccess, Visitor}; -use serde::{Deserialize, Deserializer}; - use self::deserialize::Hex; - use super::values::Value; +pub use crate::desc_common::{Constructor, GlobalName, LocalName, Namespaced, SimpleName}; pub use engine::Engine; pub use genesis::Genesis; -use once_cell::sync::Lazy; -use regex::Regex; +use primitives::H256; +use serde::Deserialize; +use std::collections::HashMap; use std::fmt::Debug; -use std::ops::{Deref, DerefMut}; pub use tendermint::TendermintParams; mod deserialize; @@ -41,97 +32,6 @@ pub(self) mod params; mod tendermint; pub(self) mod validator; -macro_rules! module_delim { - () => { - "/" - }; -} -macro_rules! namespace_delim { - () => { - "." - }; -} -macro_rules! first_word { - () => { - r"[A-Za-z][a-z0-9]*|[A-Z][A-Z0-9]*" - }; -} -macro_rules! trailing_word { - () => { - r"[a-z0-9]+|[A-Z0-9]+" - }; -} -macro_rules! ident { - () => { - concat!(first_word!(), "(-", trailing_word!(), ")*") - }; -} -macro_rules! simple_name { - () => { - concat!("^", ident!(), "$") - }; -} -macro_rules! local_name { - () => { - concat!("^", ident!(), "(", namespace_delim!(), ident!(), ")*$") - }; -} -macro_rules! global_name { - () => { - concat!("^", ident!(), module_delim!(), ident!(), "(", namespace_delim!(), ident!(), ")*$") - }; -} -macro_rules! impl_name { - ($name_type:ident, $pattern:ident, $expecting:tt) => { - #[derive(Hash, Eq, Ord, PartialOrd, PartialEq)] - pub struct $name_type(String); - - impl Deref for $name_type { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl Borrow for $name_type { - fn borrow(&self) -> &str { - &self.0 - } - } - - impl Debug for $name_type { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Debug::fmt(&self.0, f) - } - } - - impl Display for $name_type { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(&self.0, f) - } - } - - impl<'de> Deserialize<'de> for $name_type { - fn deserialize>(deserializer: D) -> Result { - deserializer - .deserialize_str(NameVisitor { - expecting: $expecting, - pattern: &*$pattern, - }) - .map($name_type) - } - } - }; -} - -pub const MODULE_DELIMITER: &str = module_delim!(); -pub const NAMESPACE_DELIMITER: &str = namespace_delim!(); - -static SIMPLE_NAME_RE: Lazy = Lazy::new(|| Regex::new(simple_name!()).unwrap()); -static LOCAL_NAME_RE: Lazy = Lazy::new(|| Regex::new(local_name!()).unwrap()); -static GLOBAL_NAME_RE: Lazy = Lazy::new(|| Regex::new(global_name!()).unwrap()); - #[derive(Deserialize, Debug)] #[serde(rename_all = "kebab-case")] pub struct AppDesc { @@ -197,182 +97,6 @@ pub struct HostSetup { pub genesis: Genesis, } -#[derive(Debug)] -pub struct Constructor { - pub name: String, - pub args: Value, -} - -struct NameVisitor { - expecting: &'static str, - pattern: &'static Regex, -} - -impl<'de> Visitor<'de> for NameVisitor { - type Value = String; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - write!(formatter, "{}", self.expecting) - } - - fn visit_str(self, v: &str) -> Result { - if !self.pattern.is_match(v) { - Err(E::invalid_value(Unexpected::Str(v), &self.expecting)) - } else { - Ok(v.to_owned()) - } - } -} - -impl_name!(SimpleName, SIMPLE_NAME_RE, "a kebab-cased identifier"); - -impl_name!(LocalName, LOCAL_NAME_RE, "a name consisting of identifiers separated by dots"); - -impl_name!(GlobalName, GLOBAL_NAME_RE, "a namespaced name qualified with module name"); - -impl GlobalName { - pub fn module(&self) -> &str { - let delimiter_index = self.0.find(MODULE_DELIMITER).expect("a module name followed by a module delimiter"); - &self.0[0..delimiter_index] - } - - pub fn name(&self) -> &str { - let delimiter_index = self.0.find(MODULE_DELIMITER).expect("a module name followed by a module delimiter"); - &self.0[delimiter_index + 1..] - } -} - -struct ConstructorVisitor; - -impl<'de> Deserialize<'de> for Constructor { - fn deserialize>(deserializer: D) -> Result { - impl<'de> Visitor<'de> for ConstructorVisitor { - type Value = Constructor; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str( - "a map with single key value pair that serves \ - as a specification of a constructor call", - ) - } - - fn visit_map>(self, mut map: M) -> Result { - match map.next_entry()? { - Some((name, args)) => match map.next_key::()? { - Some(_) => Err(Error::custom("Single constructor must be specified")), - None => Ok(Constructor { - name, - args, - }), - }, - None => Err(Error::custom("No constructor specified")), - } - } - } - deserializer.deserialize_map(ConstructorVisitor) - } -} - -pub struct Namespaced(BTreeMap); - -impl Debug for Namespaced { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Debug::fmt(&self.0, f) - } -} - -impl Default for Namespaced { - fn default() -> Self { - Namespaced(Default::default()) - } -} - -impl Deref for Namespaced { - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Namespaced { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -const NAMESPACE_PREFIX: char = '\\'; - -impl From> for BTreeMap { - fn from(from: Namespaced) -> Self { - from.0 - } -} - -struct NamespacedMapVisitor<'a, T: DeserializeOwned> { - prefix: String, - map: &'a mut BTreeMap, -} - -impl<'a, 'de, T: DeserializeOwned> Visitor<'de> for NamespacedMapVisitor<'a, T> { - type Value = (); - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map with a given type or a nested namespace as values") - } - - fn visit_map>(self, mut map: A) -> Result { - fn to_qualified<'s>(prefix: &'s str, key: &'s str) -> String { - if prefix.is_empty() { - key.to_owned() - } else { - String::with_capacity(prefix.len() + key.len() + 1) + prefix + NAMESPACE_DELIMITER + key - } - } - - while let Some(key) = map.next_key::()? { - if key.starts_with(NAMESPACE_PREFIX) { - let key_part = &key[1..]; - if !LOCAL_NAME_RE.is_match(key_part) { - return Err(A::Error::invalid_value(Unexpected::Str(&key), &"an @-prefixed qualified name")) - } - let prefix = to_qualified(&self.prefix, key_part); - map.next_value_seed(NamespacedMapVisitor { - prefix, - map: self.map, - })?; - } else { - if !LOCAL_NAME_RE.is_match(&key) { - return Err(A::Error::invalid_value(Unexpected::Str(&key), &"a qualified name")) - } - let qualified_key = to_qualified(&self.prefix, &key); - self.map.insert(qualified_key, map.next_value::()?); - } - } - Ok(()) - } -} - -impl<'de, T: DeserializeOwned + 'de> Deserialize<'de> for Namespaced { - fn deserialize>(deserializer: D) -> Result { - let mut map = BTreeMap::new(); - deserializer.deserialize_map(NamespacedMapVisitor { - prefix: String::new(), - map: &mut map, - })?; - Ok(Namespaced(map)) - } -} - -impl<'a, 'de, T: DeserializeOwned> DeserializeSeed<'de> for NamespacedMapVisitor<'a, T> { - type Value = (); - - fn deserialize>(self, deserializer: D) -> Result { - deserializer.deserialize_map(self)?; - Ok(()) - } -} - #[cfg(test)] mod tests { use crate::app_desc::AppDesc; diff --git a/coordinator/src/desc_common.rs b/coordinator/src/desc_common.rs new file mode 100644 index 0000000000..a809e66a11 --- /dev/null +++ b/coordinator/src/desc_common.rs @@ -0,0 +1,295 @@ +// Copyright 2020 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::values::Value; +use once_cell::sync::Lazy; +use regex::Regex; +use serde::de::{DeserializeOwned, DeserializeSeed, Error, Unexpected}; +use serde::de::{MapAccess, Visitor}; +use serde::{Deserialize, Deserializer}; +use std::borrow::Borrow; +use std::collections::BTreeMap; +use std::fmt::Debug; +use std::ops::{Deref, DerefMut}; +use std::{fmt, fmt::Display, fmt::Formatter}; + +macro_rules! module_delim { + () => { + "/" + }; +} +macro_rules! namespace_delim { + () => { + "." + }; +} +macro_rules! first_word { + () => { + r"[A-Za-z][a-z0-9]*|[A-Z][A-Z0-9]*" + }; +} +macro_rules! trailing_word { + () => { + r"[a-z0-9]+|[A-Z0-9]+" + }; +} +macro_rules! ident { + () => { + concat!(first_word!(), "(-", trailing_word!(), ")*") + }; +} +macro_rules! simple_name { + () => { + concat!("^", ident!(), "$") + }; +} +macro_rules! local_name { + () => { + concat!("^", ident!(), "(", namespace_delim!(), ident!(), ")*$") + }; +} +macro_rules! global_name { + () => { + concat!("^", ident!(), module_delim!(), ident!(), "(", namespace_delim!(), ident!(), ")*$") + }; +} + +pub const MODULE_DELIMITER: &str = module_delim!(); +pub const NAMESPACE_DELIMITER: &str = namespace_delim!(); + +pub static SIMPLE_NAME_RE: Lazy = Lazy::new(|| Regex::new(simple_name!()).unwrap()); +pub static LOCAL_NAME_RE: Lazy = Lazy::new(|| Regex::new(local_name!()).unwrap()); +pub static GLOBAL_NAME_RE: Lazy = Lazy::new(|| Regex::new(global_name!()).unwrap()); + +macro_rules! impl_name { + ($name_type:ident, $pattern:ident, $expecting:tt) => { + #[derive(Hash, Eq, Ord, PartialOrd, PartialEq)] + pub struct $name_type(String); + + impl Deref for $name_type { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl Borrow for $name_type { + fn borrow(&self) -> &str { + &self.0 + } + } + + impl Debug for $name_type { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } + } + + impl Display for $name_type { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } + } + + impl<'de> Deserialize<'de> for $name_type { + fn deserialize>(deserializer: D) -> Result { + deserializer + .deserialize_str(NameVisitor { + expecting: $expecting, + pattern: &*$pattern, + }) + .map($name_type) + } + } + }; +} + +impl_name!(SimpleName, SIMPLE_NAME_RE, "a kebab-cased identifier"); + +impl_name!(LocalName, LOCAL_NAME_RE, "a name consisting of identifiers separated by dots"); + +impl_name!(GlobalName, GLOBAL_NAME_RE, "a namespaced name qualified with module name"); + +impl GlobalName { + pub fn module(&self) -> &str { + let delimiter_index = self.0.find(MODULE_DELIMITER).expect("a module name followed by a module delimiter"); + &self.0[0..delimiter_index] + } + + pub fn name(&self) -> &str { + let delimiter_index = self.0.find(MODULE_DELIMITER).expect("a module name followed by a module delimiter"); + &self.0[delimiter_index + 1..] + } +} + +struct ConstructorVisitor; + +impl<'de> Deserialize<'de> for Constructor { + fn deserialize>(deserializer: D) -> Result { + impl<'de> Visitor<'de> for ConstructorVisitor { + type Value = Constructor; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str( + "a map with single key value pair that serves \ + as a specification of a constructor call", + ) + } + + fn visit_map>(self, mut map: M) -> Result { + match map.next_entry()? { + Some((name, args)) => match map.next_key::()? { + Some(_) => Err(Error::custom("Single constructor must be specified")), + None => Ok(Constructor { + name, + args, + }), + }, + None => Err(Error::custom("No constructor specified")), + } + } + } + deserializer.deserialize_map(ConstructorVisitor) + } +} + +pub struct Namespaced(BTreeMap); + +impl Debug for Namespaced { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl Default for Namespaced { + fn default() -> Self { + Namespaced(Default::default()) + } +} + +impl Deref for Namespaced { + type Target = BTreeMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Namespaced { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +const NAMESPACE_PREFIX: char = '\\'; + +impl From> for BTreeMap { + fn from(from: Namespaced) -> Self { + from.0 + } +} + +struct NamespacedMapVisitor<'a, T: DeserializeOwned> { + prefix: String, + map: &'a mut BTreeMap, +} + +impl<'a, 'de, T: DeserializeOwned> Visitor<'de> for NamespacedMapVisitor<'a, T> { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map with a given type or a nested namespace as values") + } + + fn visit_map>(self, mut map: A) -> Result { + fn to_qualified<'s>(prefix: &'s str, key: &'s str) -> String { + if prefix.is_empty() { + key.to_owned() + } else { + String::with_capacity(prefix.len() + key.len() + 1) + prefix + NAMESPACE_DELIMITER + key + } + } + + while let Some(key) = map.next_key::()? { + if key.starts_with(NAMESPACE_PREFIX) { + let key_part = &key[1..]; + if !LOCAL_NAME_RE.is_match(key_part) { + return Err(A::Error::invalid_value(Unexpected::Str(&key), &"an @-prefixed qualified name")) + } + let prefix = to_qualified(&self.prefix, key_part); + map.next_value_seed(NamespacedMapVisitor { + prefix, + map: self.map, + })?; + } else { + if !LOCAL_NAME_RE.is_match(&key) { + return Err(A::Error::invalid_value(Unexpected::Str(&key), &"a qualified name")) + } + let qualified_key = to_qualified(&self.prefix, &key); + self.map.insert(qualified_key, map.next_value::()?); + } + } + Ok(()) + } +} + +impl<'de, T: DeserializeOwned + 'de> Deserialize<'de> for Namespaced { + fn deserialize>(deserializer: D) -> Result { + let mut map = BTreeMap::new(); + deserializer.deserialize_map(NamespacedMapVisitor { + prefix: String::new(), + map: &mut map, + })?; + Ok(Namespaced(map)) + } +} + +impl<'a, 'de, T: DeserializeOwned> DeserializeSeed<'de> for NamespacedMapVisitor<'a, T> { + type Value = (); + + fn deserialize>(self, deserializer: D) -> Result { + deserializer.deserialize_map(self)?; + Ok(()) + } +} + +#[derive(Debug)] +pub struct Constructor { + pub name: String, + pub args: Value, +} + +struct NameVisitor { + expecting: &'static str, + pattern: &'static Regex, +} + +impl<'de> Visitor<'de> for NameVisitor { + type Value = String; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "{}", self.expecting) + } + + fn visit_str(self, v: &str) -> Result { + if !self.pattern.is_match(v) { + Err(E::invalid_value(Unexpected::Str(v), &self.expecting)) + } else { + Ok(v.to_owned()) + } + } +} diff --git a/coordinator/src/lib.rs b/coordinator/src/lib.rs index b225bc44df..feacf122a9 100644 --- a/coordinator/src/lib.rs +++ b/coordinator/src/lib.rs @@ -17,6 +17,8 @@ extern crate codechain_logger as clogger; extern crate log; +#[macro_use] +mod desc_common; pub mod app_desc; pub mod context; pub mod engine; From 276fc12eeb0975302c72708d2fba49d389bcf447 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Nov 2020 17:13:17 +0900 Subject: [PATCH 04/10] Create LinkDesc struct and read it from file Foundry is not using it. Following commits will use it. --- coordinator/src/lib.rs | 2 ++ coordinator/src/link_desc.rs | 52 ++++++++++++++++++++++++++++++++++++ foundry/config/mod.rs | 8 ++++++ foundry/run_node.rs | 9 ++++++- 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 coordinator/src/link_desc.rs diff --git a/coordinator/src/lib.rs b/coordinator/src/lib.rs index feacf122a9..975bb804c5 100644 --- a/coordinator/src/lib.rs +++ b/coordinator/src/lib.rs @@ -23,6 +23,7 @@ pub mod app_desc; pub mod context; pub mod engine; mod header; +mod link_desc; mod linkable; pub mod module; pub mod test_coordinator; @@ -35,6 +36,7 @@ pub use crate::app_desc::AppDesc; use crate::context::StorageAccess; use crate::engine::{BlockExecutor, ExecutionId, GraphQlHandlerProvider, Initializer, TxFilter}; pub use crate::header::Header; +pub use crate::link_desc::LinkDesc; use crate::module::{ HandleCrimes, HandleGraphQlRequest, InitConsensus, InitGenesis, SessionId, SortedTxs, Stateful, TxOwner, TxSorter, UpdateConsensus, diff --git a/coordinator/src/link_desc.rs b/coordinator/src/link_desc.rs new file mode 100644 index 0000000000..bb0d6c5573 --- /dev/null +++ b/coordinator/src/link_desc.rs @@ -0,0 +1,52 @@ +// Copyright 2020 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::values::Value; +use crate::desc_common::{Constructor, GlobalName, Namespaced, SimpleName}; +use serde::Deserialize; +use std::collections::HashMap; + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct LinkDesc { + /// The ID of the default `Sandboxer` to be used when no `Sandboxer` is specified for modules. + #[serde(default)] + pub default_sandboxer: String, + pub modules: HashMap, + #[serde(default)] + pub param_defaults: Namespaced, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct ModuleSetup { + #[serde(default)] + pub sandboxer: String, + #[serde(default)] + pub exports: Namespaced, + #[serde(default)] + pub imports: Namespaced, + #[serde(default)] + pub init_config: Value, +} + +#[allow(clippy::should_implement_trait)] +impl LinkDesc { + pub fn from_str(s: &str) -> anyhow::Result { + let link_desc: LinkDesc = toml::from_str(s)?; + Ok(link_desc) + } +} diff --git a/foundry/config/mod.rs b/foundry/config/mod.rs index d686547120..1286a88a98 100644 --- a/foundry/config/mod.rs +++ b/foundry/config/mod.rs @@ -48,6 +48,14 @@ pub struct Config { )] pub app_desc_path: String, + #[conf( + no_short, + long = "link-desc-path", + help = "Specify the link descriptor path.", + default = "\"./link-desc.toml\".to_string()" + )] + pub link_desc_path: String, + #[conf( short = "i", long = "instance-id", diff --git a/foundry/run_node.rs b/foundry/run_node.rs index 9433eab2e8..29e8500457 100644 --- a/foundry/run_node.rs +++ b/foundry/run_node.rs @@ -32,7 +32,7 @@ use ckeystore::accounts_dir::RootDiskDirectory; use ckeystore::KeyStore; use clogger::{EmailAlarm, LoggerConfig}; use cnetwork::{Filters, ManagingPeerdb, NetworkConfig, NetworkControl, NetworkService, RoutingTable, SocketAddr}; -use coordinator::{AppDesc, Coordinator}; +use coordinator::{AppDesc, Coordinator, LinkDesc}; use crossbeam::unbounded; use crossbeam_channel as crossbeam; use csync::snapshot::Service as SnapshotService; @@ -243,6 +243,13 @@ pub fn run_node( app_desc .merge_params(&module_arguments) .map_err(|err| format!("Foundry failed to merge params you supplied into the app descriptor. {}", err))?; + let _link_desc = { + let link_desc_path = &config.link_desc_path; + let link_desc_string = fs::read_to_string(link_desc_path) + .map_err(|err| format!("Foundry failed to read a link desc at {}: {}", link_desc_path, err))?; + LinkDesc::from_str(&link_desc_string) + .map_err(|err| format!("Foundry failed to parse link descriptor: {}", err))? + }; let coordinator = Arc::new(Coordinator::from_app_desc(&app_desc).unwrap()); let genesis = Genesis::new(app_desc.host.genesis, coordinator.as_ref()); From c691ac953d4fe84db546e8ea4b9d07d6ec774e3f Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 11 Nov 2020 17:12:37 +0900 Subject: [PATCH 05/10] Extract base parameter override code They will be used in the link descriptor code. --- coordinator/src/app_desc/params.rs | 168 +----------------------- coordinator/src/desc_common.rs | 2 + coordinator/src/desc_common/params.rs | 180 ++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 166 deletions(-) create mode 100644 coordinator/src/desc_common/params.rs diff --git a/coordinator/src/app_desc/params.rs b/coordinator/src/app_desc/params.rs index 8eda1ccb41..2320ce9223 100644 --- a/coordinator/src/app_desc/params.rs +++ b/coordinator/src/app_desc/params.rs @@ -14,45 +14,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::values::Value; -use crate::{ - app_desc::{AppDesc, HostSetup, ModuleSetup}, - values::TOMLValueDeserializer, -}; +use crate::app_desc::{AppDesc, HostSetup, ModuleSetup}; +use crate::desc_common::params::Merger; use anyhow::Context as _; -use handlebars::{no_escape, Context, Handlebars, TemplateRenderError}; use std::collections::BTreeMap; -/// You use Handlebars style template in app descriptor. -/// some-config = "{{other-variable}}" -/// By starting "@" you can express map or array using template. -/// some-config-array = "@[ {{variable1}}, {{variable2}} ]" -/// some-config-map = "@[ \"key\" = {{value-variable}} ]" -/// If you want to start a string value please use "@@" -/// some-config = "@@start from @" -struct Merger<'reg> { - registry: Handlebars<'reg>, - context: Context, -} - -#[allow(dead_code)] -impl<'reg> Merger<'reg> { - fn new(params: &BTreeMap) -> Merger<'reg> { - let mut registry = Handlebars::new(); - registry.register_escape_fn(no_escape); - registry.set_strict_mode(true); - - Merger { - registry, - context: Context::wraps(params).unwrap(), - } - } - - fn merge(&self, s: &str) -> Result { - self.registry.render_template_with_context(s, &self.context) - } -} - impl AppDesc { pub fn merge_params(&mut self, params: &BTreeMap) -> anyhow::Result<()> { let mut merged_params = self.param_defaults.clone(); @@ -94,133 +60,3 @@ impl HostSetup { Ok(()) } } - -impl Value { - fn merge_params(&mut self, merger: &Merger) -> anyhow::Result<()> { - match self { - Self::String(s) => { - if s.starts_with("@@") { - // Change @@ to @ and type is string - let merged = merger.merge(&s[1..])?; - *self = Value::String(merged); - } else if s.starts_with('@') { - // Remove @ and type is anything - let merged = merger.merge(&s[1..])?; - *self = TOMLValueDeserializer::deserialize(&merged)?; - } else { - // Type is string - let merged = merger.merge(s)?; - *self = Value::String(merged); - } - } - Self::List(list) => { - for v in list { - v.merge_params(merger)? - } - } - Self::Map(map) => { - for v in map.values_mut() { - v.merge_params(merger)? - } - } - _ => {} - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::Merger; - use crate::values::Value; - - #[test] - fn merge_into_string_value() { - let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); - let merger = Merger::new(¶ms); - let mut value = Value::String("=={{hello}}==".to_owned()); - value.merge_params(&merger).unwrap(); - assert_eq!(value, Value::String("==world==".to_owned())) - } - - #[test] - fn merge_into_non_string_value() { - let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); - let merger = Merger::new(¶ms); - let mut value = Value::Int(123); - value.merge_params(&merger).unwrap(); - assert_eq!(value, Value::Int(123)) - } - - #[test] - fn merge_into_value_in_map() { - let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); - let merger = Merger::new(¶ms); - let mut value = Value::Map( - vec![(String::from("1"), Value::Int(1)), (String::from("2"), Value::String(String::from("=={{hello}}==")))] - .into_iter() - .collect(), - ); - - value.merge_params(&merger).unwrap(); - - assert_eq!( - value, - Value::Map( - vec![(String::from("1"), Value::Int(1)), (String::from("2"), Value::String(String::from("==world==")))] - .into_iter() - .collect() - ) - ); - } - - #[test] - fn merge_into_value_in_list() { - let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); - let merger = Merger::new(¶ms); - let mut value = Value::List(vec![Value::Int(1), Value::String(String::from("=={{hello}}=="))]); - - value.merge_params(&merger).unwrap(); - - assert_eq!(value, Value::List(vec![Value::Int(1), Value::String(String::from("==world=="))])); - } - - #[test] - fn merge_at() { - let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); - let merger = Merger::new(¶ms); - let mut value = Value::String("@\"=={{hello}}==\"".to_owned()); - value.merge_params(&merger).unwrap(); - assert_eq!(value, Value::String("==world==".to_owned())) - } - - #[test] - fn merge_at_array() { - let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); - let merger = Merger::new(¶ms); - let mut value = Value::String("@[\"{{hello}}\", \"{{hello}}\"]".to_owned()); - value.merge_params(&merger).unwrap(); - assert_eq!(value, Value::List(vec![Value::String("world".to_owned()), Value::String("world".to_owned())])) - } - - #[test] - fn merge_at_map() { - let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); - let merger = Merger::new(¶ms); - let mut value = Value::String("@{ \"key\" = \"{{hello}}\" }".to_owned()); - value.merge_params(&merger).unwrap(); - assert_eq!( - value, - Value::Map(vec![("key".to_string(), Value::String("world".to_string())),].into_iter().collect()) - ) - } - - #[test] - fn merge_atat() { - let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); - let merger = Merger::new(¶ms); - let mut value = Value::String("@@=={{hello}}==".to_owned()); - value.merge_params(&merger).unwrap(); - assert_eq!(value, Value::String("@==world==".to_owned())) - } -} diff --git a/coordinator/src/desc_common.rs b/coordinator/src/desc_common.rs index a809e66a11..e532c171ba 100644 --- a/coordinator/src/desc_common.rs +++ b/coordinator/src/desc_common.rs @@ -26,6 +26,8 @@ use std::fmt::Debug; use std::ops::{Deref, DerefMut}; use std::{fmt, fmt::Display, fmt::Formatter}; +pub(crate) mod params; + macro_rules! module_delim { () => { "/" diff --git a/coordinator/src/desc_common/params.rs b/coordinator/src/desc_common/params.rs new file mode 100644 index 0000000000..90baedd687 --- /dev/null +++ b/coordinator/src/desc_common/params.rs @@ -0,0 +1,180 @@ +// Copyright 2020 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::values::TOMLValueDeserializer; +use crate::values::Value; +use handlebars::{no_escape, Context, Handlebars, TemplateRenderError}; +use std::collections::BTreeMap; + +/// You use Handlebars style template in app descriptor. +/// some-config = "{{other-variable}}" +/// By starting "@" you can express map or array using template. +/// some-config-array = "@[ {{variable1}}, {{variable2}} ]" +/// some-config-map = "@[ \"key\" = {{value-variable}} ]" +/// If you want to start a string value please use "@@" +/// some-config = "@@start from @" +pub struct Merger<'reg> { + registry: Handlebars<'reg>, + context: Context, +} + +#[allow(dead_code)] +impl<'reg> Merger<'reg> { + pub(crate) fn new(params: &BTreeMap) -> Merger<'reg> { + let mut registry = Handlebars::new(); + registry.register_escape_fn(no_escape); + registry.set_strict_mode(true); + + Merger { + registry, + context: Context::wraps(params).unwrap(), + } + } + + fn merge(&self, s: &str) -> Result { + self.registry.render_template_with_context(s, &self.context) + } +} + +impl Value { + pub(crate) fn merge_params(&mut self, merger: &Merger) -> anyhow::Result<()> { + match self { + Self::String(s) => { + if s.starts_with("@@") { + // Change @@ to @ and type is string + let merged = merger.merge(&s[1..])?; + *self = Value::String(merged); + } else if s.starts_with('@') { + // Remove @ and type is anything + let merged = merger.merge(&s[1..])?; + *self = TOMLValueDeserializer::deserialize(&merged)?; + } else { + // Type is string + let merged = merger.merge(s)?; + *self = Value::String(merged); + } + } + Self::List(list) => { + for v in list { + v.merge_params(merger)? + } + } + Self::Map(map) => { + for v in map.values_mut() { + v.merge_params(merger)? + } + } + _ => {} + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::Merger; + use crate::values::Value; + + #[test] + fn merge_into_string_value() { + let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); + let merger = Merger::new(¶ms); + let mut value = Value::String("=={{hello}}==".to_owned()); + value.merge_params(&merger).unwrap(); + assert_eq!(value, Value::String("==world==".to_owned())) + } + + #[test] + fn merge_into_non_string_value() { + let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); + let merger = Merger::new(¶ms); + let mut value = Value::Int(123); + value.merge_params(&merger).unwrap(); + assert_eq!(value, Value::Int(123)) + } + + #[test] + fn merge_into_value_in_map() { + let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); + let merger = Merger::new(¶ms); + let mut value = Value::Map( + vec![(String::from("1"), Value::Int(1)), (String::from("2"), Value::String(String::from("=={{hello}}==")))] + .into_iter() + .collect(), + ); + + value.merge_params(&merger).unwrap(); + + assert_eq!( + value, + Value::Map( + vec![(String::from("1"), Value::Int(1)), (String::from("2"), Value::String(String::from("==world==")))] + .into_iter() + .collect() + ) + ); + } + + #[test] + fn merge_into_value_in_list() { + let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); + let merger = Merger::new(¶ms); + let mut value = Value::List(vec![Value::Int(1), Value::String(String::from("=={{hello}}=="))]); + + value.merge_params(&merger).unwrap(); + + assert_eq!(value, Value::List(vec![Value::Int(1), Value::String(String::from("==world=="))])); + } + + #[test] + fn merge_at() { + let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); + let merger = Merger::new(¶ms); + let mut value = Value::String("@\"=={{hello}}==\"".to_owned()); + value.merge_params(&merger).unwrap(); + assert_eq!(value, Value::String("==world==".to_owned())) + } + + #[test] + fn merge_at_array() { + let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); + let merger = Merger::new(¶ms); + let mut value = Value::String("@[\"{{hello}}\", \"{{hello}}\"]".to_owned()); + value.merge_params(&merger).unwrap(); + assert_eq!(value, Value::List(vec![Value::String("world".to_owned()), Value::String("world".to_owned())])) + } + + #[test] + fn merge_at_map() { + let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); + let merger = Merger::new(¶ms); + let mut value = Value::String("@{ \"key\" = \"{{hello}}\" }".to_owned()); + value.merge_params(&merger).unwrap(); + assert_eq!( + value, + Value::Map(vec![("key".to_string(), Value::String("world".to_string())),].into_iter().collect()) + ) + } + + #[test] + fn merge_atat() { + let params = vec![("hello".to_owned(), "world".to_owned())].into_iter().collect(); + let merger = Merger::new(¶ms); + let mut value = Value::String("@@=={{hello}}==".to_owned()); + value.merge_params(&merger).unwrap(); + assert_eq!(value, Value::String("@==world==".to_owned())) + } +} From 629228f4845cc39b17f3d30cb49a4ef4a91e1359 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 11 Nov 2020 15:21:12 +0900 Subject: [PATCH 06/10] Override params in the link descriptor --- coordinator/src/app_desc/params.rs | 10 ------- coordinator/src/link_desc.rs | 2 ++ coordinator/src/link_desc/params.rs | 44 +++++++++++++++++++++++++++++ foundry/run_node.rs | 5 +++- 4 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 coordinator/src/link_desc/params.rs diff --git a/coordinator/src/app_desc/params.rs b/coordinator/src/app_desc/params.rs index 2320ce9223..e6ed4c70ac 100644 --- a/coordinator/src/app_desc/params.rs +++ b/coordinator/src/app_desc/params.rs @@ -37,10 +37,6 @@ impl AppDesc { impl ModuleSetup { fn merge_params(&mut self, merger: &Merger) -> anyhow::Result<()> { - for (export, cons) in self.exports.iter_mut() { - cons.args.merge_params(merger).with_context(|| format!("exports > {} = {}", export, cons.name))?; - } - self.init_config.merge_params(merger).context("init-config")?; self.genesis_config.merge_params(merger).context("genesis-config")?; Ok(()) } @@ -48,12 +44,6 @@ impl ModuleSetup { impl HostSetup { fn merge_params(&mut self, merger: &Merger) -> anyhow::Result<()> { - for (export, cons) in self.exports.iter_mut() { - cons.args.merge_params(merger).with_context(|| format!("host > exports > {} = {}", export, cons.name))?; - } - for (config, value) in self.init_config.iter_mut() { - value.merge_params(merger).with_context(|| format!("host > init-config > {}", config))?; - } for (config, value) in self.genesis_config.iter_mut() { value.merge_params(merger).with_context(|| format!("host > genesis-config > {}", config))?; } diff --git a/coordinator/src/link_desc.rs b/coordinator/src/link_desc.rs index bb0d6c5573..ec26eaf2a2 100644 --- a/coordinator/src/link_desc.rs +++ b/coordinator/src/link_desc.rs @@ -19,6 +19,8 @@ use crate::desc_common::{Constructor, GlobalName, Namespaced, SimpleName}; use serde::Deserialize; use std::collections::HashMap; +mod params; + #[derive(Deserialize, Debug)] #[serde(rename_all = "kebab-case")] pub struct LinkDesc { diff --git a/coordinator/src/link_desc/params.rs b/coordinator/src/link_desc/params.rs new file mode 100644 index 0000000000..6f8c6b788b --- /dev/null +++ b/coordinator/src/link_desc/params.rs @@ -0,0 +1,44 @@ +// Copyright 2020 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::ModuleSetup; +use crate::{desc_common::params::Merger, LinkDesc}; +use anyhow::Context as _; +use std::collections::BTreeMap; + +impl LinkDesc { + pub fn merge_params(&mut self, params: &BTreeMap) -> anyhow::Result<()> { + let mut merged_params = self.param_defaults.clone(); + merged_params.append(&mut params.clone()); + + let merger = Merger::new(&merged_params); + + for (name, setup) in self.modules.iter_mut() { + setup.merge_params(&merger).with_context(|| format!("module: {}", name))?; + } + Ok(()) + } +} + +impl ModuleSetup { + fn merge_params(&mut self, merger: &Merger) -> anyhow::Result<()> { + for (export, cons) in self.exports.iter_mut() { + cons.args.merge_params(merger).with_context(|| format!("exports > {} = {}", export, cons.name))?; + } + self.init_config.merge_params(merger).context("init-config")?; + Ok(()) + } +} diff --git a/foundry/run_node.rs b/foundry/run_node.rs index 29e8500457..bffb0859bb 100644 --- a/foundry/run_node.rs +++ b/foundry/run_node.rs @@ -243,13 +243,16 @@ pub fn run_node( app_desc .merge_params(&module_arguments) .map_err(|err| format!("Foundry failed to merge params you supplied into the app descriptor. {}", err))?; - let _link_desc = { + let mut link_desc = { let link_desc_path = &config.link_desc_path; let link_desc_string = fs::read_to_string(link_desc_path) .map_err(|err| format!("Foundry failed to read a link desc at {}: {}", link_desc_path, err))?; LinkDesc::from_str(&link_desc_string) .map_err(|err| format!("Foundry failed to parse link descriptor: {}", err))? }; + link_desc + .merge_params(&module_arguments) + .map_err(|err| format!("Foundry failed to merge params you supplied into the link descriptor. {}", err))?; let coordinator = Arc::new(Coordinator::from_app_desc(&app_desc).unwrap()); let genesis = Genesis::new(app_desc.host.genesis, coordinator.as_ref()); From 274d758b3eca1975a85a9bbf98c9e7c625cfc72f Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 11 Nov 2020 15:23:31 +0900 Subject: [PATCH 07/10] Validate the link descriptor --- coordinator/src/app_desc/validator.rs | 56 ------------------- coordinator/src/link_desc.rs | 3 + coordinator/src/link_desc/validator.rs | 76 ++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 56 deletions(-) create mode 100644 coordinator/src/link_desc/validator.rs diff --git a/coordinator/src/app_desc/validator.rs b/coordinator/src/app_desc/validator.rs index b895606699..571bd5658d 100644 --- a/coordinator/src/app_desc/validator.rs +++ b/coordinator/src/app_desc/validator.rs @@ -15,43 +15,15 @@ // along with this program. If not, see . use super::AppDesc; -use crate::app_desc::{GlobalName, Namespaced}; use anyhow::bail; impl AppDesc { pub fn validate(&self) -> anyhow::Result<()> { - self.sandboxer_specified()?; self.tx_owners_are_valid()?; - self.host_imports_are_valid()?; - self.module_imports_are_valid()?; Ok(()) } - fn sandboxer_specified(&self) -> anyhow::Result<()> { - if !self.default_sandboxer.is_empty() { - return Ok(()) - } - - let modules_without_sandboxer: Vec<_> = self - .modules - .iter() - .filter_map(|(module, setup)| { - if setup.sandboxer.is_empty() { - Some(&**module as &str) - } else { - None - } - }) - .collect(); - - if modules_without_sandboxer.is_empty() { - return Ok(()) - } - - bail!("No sandboxer is specified for modules: {}", modules_without_sandboxer.join(", ")) - } - fn tx_owners_are_valid(&self) -> anyhow::Result<()> { let invalid_owners: Vec<(&str, &str)> = self .transactions @@ -78,32 +50,4 @@ impl AppDesc { ) } } - - fn host_imports_are_valid(&self) -> anyhow::Result<()> { - self.imports_are_valid("The host", &self.host.imports) - } - - fn module_imports_are_valid(&self) -> anyhow::Result<()> { - for (module, setup) in self.modules.iter() { - self.imports_are_valid(&format!("A module, '{}'", module), &setup.imports)?; - } - Ok(()) - } - - fn imports_are_valid(&self, importer: &str, imports: &Namespaced) -> anyhow::Result<()> { - for (_to, from) in imports.iter() { - let module = from.module(); - match self.modules.get(from.module()) { - Some(setup) => { - let name = from.name(); - if !setup.exports.contains_key(name) { - bail!("{} imports non-existing service '{}' from '{}'", importer, name, module) - } - } - None => bail!("{} imports from non-existing module: {}", importer, module), - } - } - - Ok(()) - } } diff --git a/coordinator/src/link_desc.rs b/coordinator/src/link_desc.rs index ec26eaf2a2..17ccd74eba 100644 --- a/coordinator/src/link_desc.rs +++ b/coordinator/src/link_desc.rs @@ -20,6 +20,7 @@ use serde::Deserialize; use std::collections::HashMap; mod params; +mod validator; #[derive(Deserialize, Debug)] #[serde(rename_all = "kebab-case")] @@ -49,6 +50,8 @@ pub struct ModuleSetup { impl LinkDesc { pub fn from_str(s: &str) -> anyhow::Result { let link_desc: LinkDesc = toml::from_str(s)?; + link_desc.validate()?; + Ok(link_desc) } } diff --git a/coordinator/src/link_desc/validator.rs b/coordinator/src/link_desc/validator.rs new file mode 100644 index 0000000000..98bcb40677 --- /dev/null +++ b/coordinator/src/link_desc/validator.rs @@ -0,0 +1,76 @@ +// Copyright 2020 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::desc_common::GlobalName; +use crate::{app_desc::Namespaced, LinkDesc}; +use anyhow::bail; + +impl LinkDesc { + pub fn validate(&self) -> anyhow::Result<()> { + self.sandboxer_specified()?; + self.module_imports_are_valid()?; + + Ok(()) + } + + fn sandboxer_specified(&self) -> anyhow::Result<()> { + if !self.default_sandboxer.is_empty() { + return Ok(()) + } + + let modules_without_sandboxer: Vec<_> = self + .modules + .iter() + .filter_map(|(module, setup)| { + if setup.sandboxer.is_empty() { + Some(&**module as &str) + } else { + None + } + }) + .collect(); + + if modules_without_sandboxer.is_empty() { + return Ok(()) + } + + bail!("No sandboxer is specified for modules: {}", modules_without_sandboxer.join(", ")) + } + + fn module_imports_are_valid(&self) -> anyhow::Result<()> { + for (module, setup) in self.modules.iter() { + self.imports_are_valid(&format!("A module, '{}'", module), &setup.imports)?; + } + Ok(()) + } + + fn imports_are_valid(&self, importer: &str, imports: &Namespaced) -> anyhow::Result<()> { + for (_to, from) in imports.iter() { + let module = from.module(); + match self.modules.get(from.module()) { + Some(setup) => { + let name = from.name(); + if !setup.exports.contains_key(name) { + bail!("{} imports non-existing service '{}' from '{}'", importer, name, module) + } + } + None => bail!("{} imports from non-existing module: {}", importer, module), + } + } + + Ok(()) + } +} From d8af29d958c7d502a9e55055238b214f67c12b15 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 11 Nov 2020 15:33:03 +0900 Subject: [PATCH 08/10] Use the link descriptor in weaver --- coordinator/src/lib.rs | 4 +-- coordinator/src/link_desc.rs | 4 +++ coordinator/src/weaver.rs | 39 ++++++++++++++++++----------- foundry/run_node.rs | 2 +- timestamp/tests/integration_test.rs | 39 ++++++++++++++++++++++------- 5 files changed, 62 insertions(+), 26 deletions(-) diff --git a/coordinator/src/lib.rs b/coordinator/src/lib.rs index 975bb804c5..e8ad205a40 100644 --- a/coordinator/src/lib.rs +++ b/coordinator/src/lib.rs @@ -94,11 +94,11 @@ pub struct Coordinator { const SESSION_BITS_PER_SLOT: usize = mem::size_of::() * 8; impl Coordinator { - pub fn from_app_desc(app_desc: &AppDesc) -> anyhow::Result { + pub fn from_descs(app_desc: &AppDesc, link_desc: &LinkDesc) -> anyhow::Result { cmodule::init_modules(); let weaver = Weaver::new(); - let (sandboxes, mut services) = weaver.weave(app_desc)?; + let (sandboxes, mut services) = weaver.weave(app_desc, link_desc)?; // The order of stateful decides the assignment of substorage ids. It MUST be deterministic. services.stateful.lock().sort_by(|a, b| a.0.cmp(&b.0)); diff --git a/coordinator/src/link_desc.rs b/coordinator/src/link_desc.rs index 17ccd74eba..d9fb6939f5 100644 --- a/coordinator/src/link_desc.rs +++ b/coordinator/src/link_desc.rs @@ -54,4 +54,8 @@ impl LinkDesc { Ok(link_desc) } + + pub fn get(&self, module_name: &str) -> Option<&ModuleSetup> { + self.modules.get(module_name) + } } diff --git a/coordinator/src/weaver.rs b/coordinator/src/weaver.rs index be16bac2a2..3d9f5c4a03 100644 --- a/coordinator/src/weaver.rs +++ b/coordinator/src/weaver.rs @@ -25,7 +25,10 @@ use anyhow::{anyhow, bail, Context}; use cmodule::link::{best_linker, Port}; use cmodule::sandbox::{sandboxer, Sandbox}; -use crate::app_desc::{AppDesc, Constructor, GlobalName, HostSetup, ModuleSetup, Namespaced, SimpleName}; +use crate::{ + app_desc::{AppDesc, Constructor, GlobalName, ModuleSetup, Namespaced, SimpleName}, + link_desc::{self, LinkDesc}, +}; use crate::{Occurrences, Services}; use crate::{HOST_ID, SERVICES_FOR_HOST, TX_SERVICES_FOR_HOST}; @@ -59,11 +62,16 @@ impl Weaver { Self::default() } - pub(super) fn weave(mut self, app_desc: &AppDesc) -> anyhow::Result<(Vec>, Services)> { + pub(super) fn weave( + mut self, + app_desc: &AppDesc, + link_desc: &LinkDesc, + ) -> anyhow::Result<(Vec>, Services)> { self.modules.reserve(app_desc.modules.len()); - self.process_host(&app_desc.host)?; - self.process_modules(&app_desc)?; + let host_module = link_desc.get("host").ok_or_else(|| anyhow!("can't find host module in app descriptor"))?; + self.process_host(host_module)?; + self.process_modules(app_desc, link_desc)?; self.tx_owners = app_desc.transactions.iter().map(|(tx_type, module)| (tx_type.clone(), (**module).clone())).collect(); self.import_tx_services_for_modules(&app_desc.modules); @@ -76,9 +84,9 @@ impl Weaver { Ok((linkables, self.services.write().take().unwrap())) } - fn process_host(&mut self, setup: &HostSetup) -> anyhow::Result<()> { - let (exports, init_exports) = Self::process_exports(&setup.exports); - let imports = Self::process_imports(&setup.imports); + fn process_host(&mut self, link: &link_desc::ModuleSetup) -> anyhow::Result<()> { + let (exports, init_exports) = Self::process_exports(&link.exports); + let imports = Self::process_imports(&link.imports); let imports = RefCell::new(imports); let init_exports: Vec<(String, Vec)> = init_exports @@ -111,12 +119,15 @@ impl Weaver { Ok(()) } - fn process_modules(&mut self, app_desc: &AppDesc) -> anyhow::Result<()> { + fn process_modules(&mut self, app_desc: &AppDesc, link_desc: &LinkDesc) -> anyhow::Result<()> { for (name, setup) in app_desc.modules.iter() { - let sandboxer_id = if setup.sandboxer.is_empty() { - &app_desc.default_sandboxer + let link = + link_desc.get(name).ok_or_else(|| anyhow!("Failed to find module {} in the link descriptor", name))?; + + let sandboxer_id = if link.sandboxer.is_empty() { + &link_desc.default_sandboxer } else { - &setup.sandboxer + &link.sandboxer }; let sandboxer = sandboxer(sandboxer_id).ok_or_else(|| anyhow!("Sandboxer unknown: {}", sandboxer_id))?; // FIXME: assumes that path is not used to locate a module here. Fix this later when we @@ -126,9 +137,9 @@ impl Weaver { } else { format!("{:x}", &setup.hash.value) }; - let (exports, init_exports) = Self::process_exports(&setup.exports); - let imports = RefCell::new(Self::process_imports(&setup.imports)); - let linkable = RefCell::new(sandboxer.load(&path, &setup.init_config, &*init_exports)?); + let (exports, init_exports) = Self::process_exports(&link.exports); + let imports = RefCell::new(Self::process_imports(&link.imports)); + let linkable = RefCell::new(sandboxer.load(&path, &link.init_config, &*init_exports)?); self.modules.insert((*name).clone(), LinkInfo { linkable, diff --git a/foundry/run_node.rs b/foundry/run_node.rs index bffb0859bb..7ff732e805 100644 --- a/foundry/run_node.rs +++ b/foundry/run_node.rs @@ -253,7 +253,7 @@ pub fn run_node( link_desc .merge_params(&module_arguments) .map_err(|err| format!("Foundry failed to merge params you supplied into the link descriptor. {}", err))?; - let coordinator = Arc::new(Coordinator::from_app_desc(&app_desc).unwrap()); + let coordinator = Arc::new(Coordinator::from_descs(&app_desc, &link_desc).unwrap()); let genesis = Genesis::new(app_desc.host.genesis, coordinator.as_ref()); diff --git a/timestamp/tests/integration_test.rs b/timestamp/tests/integration_test.rs index 17aeb584b8..80ddfd2440 100644 --- a/timestamp/tests/integration_test.rs +++ b/timestamp/tests/integration_test.rs @@ -23,7 +23,7 @@ mod common; use ccrypto::blake256; use ckey::{Ed25519KeyPair, Generator, KeyPairTrait, Random}; use common::*; -use coordinator::module::SessionId; +use coordinator::{module::SessionId, LinkDesc}; use coordinator::{AppDesc, Coordinator}; use rand::prelude::*; use serde_json::Value; @@ -103,6 +103,14 @@ fn app_desc_path() -> &'static str { } } +fn link_desc_path() -> &'static str { + if std::path::Path::exists(std::path::Path::new("./link-desc.toml")) { + "./link-desc.toml" + } else { + "./timestamp/link-desc.toml" + } +} + fn app_desc() -> AppDesc { let app_desc = std::fs::read_to_string(app_desc_path()).unwrap(); let mut app_desc = AppDesc::from_str(&app_desc).unwrap(); @@ -115,9 +123,22 @@ fn app_desc() -> AppDesc { app_desc } +// We need clippy::let_and_return because of `feature = multi-process` +// When feature != "multi-process", code become let a = x; a +#[allow(clippy::let_and_return)] +fn link_desc() -> LinkDesc { + let link_desc = std::fs::read_to_string(link_desc_path()).unwrap(); + let link_desc = LinkDesc::from_str(&link_desc).unwrap(); + #[cfg(feature = "multi-process")] + { + link_desc.default_sandboxer = "multi-process".to_owned(); + } + link_desc +} + #[test] fn weave() { - let c = Coordinator::from_app_desc(&app_desc()).unwrap(); + let c = Coordinator::from_descs(&app_desc(), &link_desc()).unwrap(); assert_eq!(c.services().stateful.lock().len(), 2); assert_eq!(c.services().init_genesis.len(), 2); @@ -132,7 +153,7 @@ fn weave_conccurent() { let mut joins = Vec::new(); for _ in 0..n { joins.push(std::thread::spawn(|| { - let c = Coordinator::from_app_desc(&app_desc()).unwrap(); + let c = Coordinator::from_descs(&app_desc(), &link_desc()).unwrap(); assert_eq!(c.services().stateful.lock().len(), 2); assert_eq!(c.services().init_genesis.len(), 2); @@ -149,7 +170,7 @@ fn weave_conccurent() { #[test] fn simple1() { - let coordinator = Coordinator::from_app_desc(&app_desc()).unwrap(); + let coordinator = Coordinator::from_descs(&app_desc(), &link_desc()).unwrap(); set_empty_session(0, &coordinator); let services = Services::new(&coordinator); @@ -226,13 +247,13 @@ fn run_massive_token_exchange(id: SessionId, c: &Coordinator) { #[test] fn multiple() { - let coordinator = Coordinator::from_app_desc(&app_desc()).unwrap(); + let coordinator = Coordinator::from_descs(&app_desc(), &link_desc()).unwrap(); run_massive_token_exchange(0, &coordinator); } #[test] fn multiple_concurrent() { - let coordinator = Arc::new(Coordinator::from_app_desc(&app_desc()).unwrap()); + let coordinator = Arc::new(Coordinator::from_descs(&app_desc(), &link_desc()).unwrap()); let mut joins = Vec::new(); for i in 0..4 { let c = Arc::clone(&coordinator); @@ -245,7 +266,7 @@ fn multiple_concurrent() { #[test] fn query() { - let coordinator = Coordinator::from_app_desc(&app_desc()).unwrap(); + let coordinator = Coordinator::from_descs(&app_desc(), &link_desc()).unwrap(); set_empty_session(0, &coordinator); let services = Services::new(&coordinator); @@ -275,7 +296,7 @@ fn query() { #[test] fn query_tx() { - let coordinator = Coordinator::from_app_desc(&app_desc()).unwrap(); + let coordinator = Coordinator::from_descs(&app_desc(), &link_desc()).unwrap(); set_empty_session(0, &coordinator); let services = Services::new(&coordinator); @@ -293,7 +314,7 @@ fn query_tx() { #[test] fn query_concurrent() { - let coordinator = Coordinator::from_app_desc(&app_desc()).unwrap(); + let coordinator = Coordinator::from_descs(&app_desc(), &link_desc()).unwrap(); set_empty_session(0, &coordinator); let services = Services::new(&coordinator); From 75289438e670ded2136aafedd571b5b99e1403ad Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 11 Nov 2020 17:50:43 +0900 Subject: [PATCH 09/10] Remove unused configs in the app descriptor They are moved to the link descriptor. --- coordinator/src/app_desc.rs | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/coordinator/src/app_desc.rs b/coordinator/src/app_desc.rs index 4943081337..a45d9ee8db 100644 --- a/coordinator/src/app_desc.rs +++ b/coordinator/src/app_desc.rs @@ -37,9 +37,6 @@ pub(self) mod validator; pub struct AppDesc { // keyed with Name rather than module hash to allow for multiple instances of single module pub modules: HashMap, - /// The ID of the default `Sandboxer` to be used when no `Sandboxer` is specified for modules. - #[serde(default)] - pub default_sandboxer: String, #[serde(default)] pub host: HostSetup, #[serde(default)] @@ -62,20 +59,12 @@ impl AppDesc { #[serde(rename_all = "kebab-case")] pub struct ModuleSetup { pub hash: Hex, - #[serde(default)] - pub sandboxer: String, - #[serde(default)] - pub exports: Namespaced, - #[serde(default)] - pub imports: Namespaced, /// List of export names expected to hold the required services. /// Then the module will receive imports for `@tx//`s. /// It is mainly intended for modules providing `TxSorter` service. #[serde(default)] pub transactions: Vec, #[serde(default)] - pub init_config: Value, - #[serde(default)] pub genesis_config: Value, #[serde(default)] pub tags: HashMap, @@ -83,12 +72,6 @@ pub struct ModuleSetup { #[derive(Deserialize, Default, Debug)] pub struct HostSetup { - #[serde(default)] - pub exports: Namespaced, - #[serde(default)] - pub imports: Namespaced, - #[serde(default)] - pub init_config: Namespaced, #[serde(default)] pub genesis_config: Namespaced, #[serde(default)] @@ -110,26 +93,12 @@ mod tests { hash = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" transactions = ["has-seq"] -[modules.awesome-module.init-config] -test = 1 - [modules.awesome-module.init-config.test1] key1 = 1 key2 = "sdfsdaf" -[modules.awesome-module.exports] -init-genesis.init-genesis = {} -init-chain.init-chain = {} -update-chain.update-chain = {} - [host] -[host.imports] -a = "awesome-module/a.a" - -[host.imports."\\namespace"] -"b.b" = "asdfsdaf-asdf" - [transactions] great-tx = "awesome-module" From 0a98bcd7bd2ac1f66e783753ed30cc501b5dbc78 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 11 Nov 2020 19:09:31 +0900 Subject: [PATCH 10/10] Update config files --- demo/README.md | 8 ++--- demo/app-desc.toml | 58 --------------------------------- demo/link-desc.toml | 65 +++++++++++++++++++++++++++++++++++++ integration-test/src/lib.rs | 2 ++ timestamp/app-desc.toml | 52 ----------------------------- timestamp/link-desc.toml | 64 ++++++++++++++++++++++++++++++++++++ 6 files changed, 135 insertions(+), 114 deletions(-) create mode 100644 demo/link-desc.toml create mode 100644 timestamp/link-desc.toml diff --git a/demo/README.md b/demo/README.md index 9491565d60..f89cfaea54 100644 --- a/demo/README.md +++ b/demo/README.md @@ -7,13 +7,13 @@ 3. Run ``` -RUST_LOG=warn ./foundry --app-desc-path app-desc.toml --config config0.ini --db-path ./db0 +RUST_LOG=warn ./foundry --app-desc-path app-desc.toml --link-desc-path link-desc.toml --config config0.ini --db-path ./db0 -RUST_LOG=warn ./foundry --app-desc-path app-desc.toml --config config1.ini --db-path ./db1 +RUST_LOG=warn ./foundry --app-desc-path app-desc.toml --link-desc-path link-desc.toml --config config1.ini --db-path ./db1 -RUST_LOG=warn ./foundry --app-desc-path app-desc.toml --config config2.ini --db-path ./db2 +RUST_LOG=warn ./foundry --app-desc-path app-desc.toml --link-desc-path link-desc.toml --config config2.ini --db-path ./db2 -RUST_LOG=warn ./foundry --app-desc-path app-desc.toml --config config3.ini --db-path ./db3 +RUST_LOG=warn ./foundry --app-desc-path app-desc.toml --link-desc-path link-desc.toml --config config3.ini --db-path ./db3 ``` for each node. diff --git a/demo/app-desc.toml b/demo/app-desc.toml index 9286ace3e8..d87e3ed330 100644 --- a/demo/app-desc.toml +++ b/demo/app-desc.toml @@ -5,16 +5,6 @@ default-sandboxer = "single-process" [modules.module-account] hash = "a010000000012345678901234567890123456789012345678901234567890123" -[modules.module-account.exports] -stateful.stateful = {} -tx-owner.tx-owner = {} -account-manager.account-manager = {} -get-account-and-seq.get-account-and-seq = {} -handle-graphql-request.handle-graphql-request = {} - -[modules.module-account.init-config] -thread-pool-size = 16 - [modules.module-account.tags] previliged = true @@ -22,17 +12,6 @@ previliged = true hash = "a020000000012345678901234567890123456789012345678901234567890123" genesis-config = ["0a6902c51384a15d1062cac3a4e62c8d0c2eb02b4de7fa0a304ce4f88ea482d0", "0473f782c3aec053c37fe2bccefa9298dcf8ae3dc2262ae540a14a580ff773e6", "2502d5e6210679a19e45f3c0f93257e7a327baaf5f403f5ca1ab2685a9e1724e", "e909f311fd115ee412edcfcde88cc507370101f7635a67b9cb45390f1ccb4b5e"] -[modules.module-staking.exports] -init-genesis.init-genesis = {} -init-chain.init-chain = {} -update-chain.update-chain = {} - -[modules.module-staking.imports] -token-manager = "module-token/token-manager" - -[modules.module-staking.init-config] -thread-pool-size = 16 - [modules.module-staking.tags] previliged = true @@ -40,37 +19,12 @@ previliged = true hash = "a030000000012345678901234567890123456789012345678901234567890123" genesis-config = {} -[modules.module-stamp.exports] -tx-owner.tx-owner = {} -get-account-and-seq.get-account-and-seq = {} -init-genesis.init-genesis = {} - -[modules.module-stamp.imports] -account-manager = "module-account/account-manager" -token-manager = "module-token/token-manager" - -[modules.module-stamp.init-config] -thread-pool-size = 16 - [modules.module-stamp.tags] previliged = true [modules.module-token] hash = "a040000000012345678901234567890123456789012345678901234567890123" -[modules.module-token.exports] -tx-owner.tx-owner = {} -stateful.stateful = {} -token-manager.token-manager = {} -handle-graphql-request.handle-graphql-request = {} -get-account-and-seq.get-account-and-seq = {} - -[modules.module-token.imports] -account-manager = "module-account/account-manager" - -[modules.module-token.init-config] -thread-pool-size = 16 - [modules.module-token.tags] previliged = true @@ -78,24 +32,12 @@ previliged = true hash = "a050000000012345678901234567890123456789012345678901234567890123" transactions = ["get-account-and-seq"] -[modules.module-sorting.exports] -tx-sorter.tx-sorter = {} - -[modules.module-sorting.imports] -account-manager = "module-account/account-manager" - -[modules.module-sorting.init-config] -thread-pool-size = 16 - [modules.module-sorting.tags] previliged = true [modules.module-util] hash = "a060000000012345678901234567890123456789012345678901234567890123" -[modules.module-util.exports] -handle-graphql-request.handle-graphql-request = {} - [host] [host.engine] diff --git a/demo/link-desc.toml b/demo/link-desc.toml new file mode 100644 index 0000000000..cdd58835f5 --- /dev/null +++ b/demo/link-desc.toml @@ -0,0 +1,65 @@ +default-sandboxer = "single-process" + +[modules.module-account.exports] +stateful.stateful = {} +tx-owner.tx-owner = {} +account-manager.account-manager = {} +get-account-and-seq.get-account-and-seq = {} +handle-graphql-request.handle-graphql-request = {} + +[modules.module-account.init-config] +thread-pool-size = 16 + +[modules.module-staking.exports] +init-genesis.init-genesis = {} +init-chain.init-chain = {} +update-chain.update-chain = {} + +[modules.module-staking.imports] +token-manager = "module-token/token-manager" + +[modules.module-staking.init-config] +thread-pool-size = 16 + +[modules.module-stamp.exports] +tx-owner.tx-owner = {} +get-account-and-seq.get-account-and-seq = {} +init-genesis.init-genesis = {} + +[modules.module-stamp.imports] +account-manager = "module-account/account-manager" +token-manager = "module-token/token-manager" + +[modules.module-stamp.init-config] +thread-pool-size = 16 + +[modules.module-token.exports] +tx-owner.tx-owner = {} +stateful.stateful = {} +token-manager.token-manager = {} +handle-graphql-request.handle-graphql-request = {} +get-account-and-seq.get-account-and-seq = {} + +[modules.module-token.imports] +account-manager = "module-account/account-manager" + +[modules.module-token.init-config] +thread-pool-size = 16 + +[modules.module-sorting.exports] +tx-sorter.tx-sorter = {} + +[modules.module-sorting.imports] +account-manager = "module-account/account-manager" + +[modules.module-sorting.init-config] +thread-pool-size = 16 + +[modules.module-util.exports] +handle-graphql-request.handle-graphql-request = {} + +[modules.host] +exports = {} +imports = {} +init-config = {} + diff --git a/integration-test/src/lib.rs b/integration-test/src/lib.rs index 28e65e1eb2..21961246c3 100644 --- a/integration-test/src/lib.rs +++ b/integration-test/src/lib.rs @@ -40,6 +40,8 @@ pub fn run_node(port: u16) -> FoundryNode { .env("RUST_LOG", "warn") .arg("--app-desc-path") .arg("../timestamp/app-desc.toml") + .arg("--link-desc-path") + .arg("../timestamp/link-desc.toml") .arg("--config") .arg("config.tendermint-solo.ini") .arg("--graphql-port") diff --git a/timestamp/app-desc.toml b/timestamp/app-desc.toml index 65d62c1b34..824184f032 100644 --- a/timestamp/app-desc.toml +++ b/timestamp/app-desc.toml @@ -3,16 +3,6 @@ default-sandboxer = "single-process" [modules.module-account] hash = "a010000000012345678901234567890123456789012345678901234567890123" -[modules.module-account.exports] -stateful.stateful = {} -tx-owner.tx-owner = {} -account-manager.account-manager = {} -get-account-and-seq.get-account-and-seq = {} -handle-graphql-request.handle-graphql-request = {} - -[modules.module-account.init-config] -thread-pool-size = "@{{thread-pool-size}}" - [modules.module-account.tags] previliged = true @@ -20,17 +10,6 @@ previliged = true hash = "a020000000012345678901234567890123456789012345678901234567890123" genesis-config = ["0a6902c51384a15d1062cac3a4e62c8d0c2eb02b4de7fa0a304ce4f88ea482d0"] -[modules.module-staking.exports] -init-genesis.init-genesis = {} -init-chain.init-chain = {} -update-chain.update-chain = {} - -[modules.module-staking.imports] -token-manager = "module-token/token-manager" - -[modules.module-staking.init-config] -thread-pool-size = "@{{thread-pool-size}}" - [modules.module-staking.tags] previliged = true @@ -38,34 +17,12 @@ previliged = true hash = "a030000000012345678901234567890123456789012345678901234567890123" genesis-config = {} -[modules.module-stamp.exports] -tx-owner.tx-owner = {} -get-account-and-seq.get-account-and-seq = {} -init-genesis.init-genesis = {} - -[modules.module-stamp.imports] -account-manager = "module-account/account-manager" -token-manager = "module-token/token-manager" - -[modules.module-stamp.init-config] -thread-pool-size = "@{{thread-pool-size}}" - [modules.module-stamp.tags] previliged = true [modules.module-token] hash = "a040000000012345678901234567890123456789012345678901234567890123" -[modules.module-token.exports] -tx-owner.tx-owner = {} -stateful.stateful = {} -token-manager.token-manager = {} -handle-graphql-request.handle-graphql-request = {} -get-account-and-seq.get-account-and-seq = {} - -[modules.module-token.imports] -account-manager = "module-account/account-manager" - [modules.module-token.init-config] thread-pool-size = "@{{thread-pool-size}}" @@ -76,15 +33,6 @@ previliged = true hash = "a050000000012345678901234567890123456789012345678901234567890123" transactions = ["get-account-and-seq"] -[modules.module-sorting.exports] -tx-sorter.tx-sorter = {} - -[modules.module-sorting.imports] -account-manager = "module-account/account-manager" - -[modules.module-sorting.init-config] -thread-pool-size = "@{{thread-pool-size}}" - [modules.module-sorting.tags] previliged = true diff --git a/timestamp/link-desc.toml b/timestamp/link-desc.toml new file mode 100644 index 0000000000..05e32db630 --- /dev/null +++ b/timestamp/link-desc.toml @@ -0,0 +1,64 @@ +default-sandboxer = "single-process" + +[modules.module-account.exports] +stateful.stateful = {} +tx-owner.tx-owner = {} +account-manager.account-manager = {} +get-account-and-seq.get-account-and-seq = {} +handle-graphql-request.handle-graphql-request = {} + +[modules.module-account.init-config] +thread-pool-size = "@{{thread-pool-size}}" + +[modules.module-staking.exports] +init-genesis.init-genesis = {} +init-chain.init-chain = {} +update-chain.update-chain = {} + +[modules.module-staking.imports] +token-manager = "module-token/token-manager" + +[modules.module-staking.init-config] +thread-pool-size = "@{{thread-pool-size}}" + +[modules.module-stamp.exports] +tx-owner.tx-owner = {} +get-account-and-seq.get-account-and-seq = {} +init-genesis.init-genesis = {} + +[modules.module-stamp.imports] +account-manager = "module-account/account-manager" +token-manager = "module-token/token-manager" + +[modules.module-stamp.init-config] +thread-pool-size = "@{{thread-pool-size}}" + +[modules.module-token.exports] +tx-owner.tx-owner = {} +stateful.stateful = {} +token-manager.token-manager = {} +handle-graphql-request.handle-graphql-request = {} +get-account-and-seq.get-account-and-seq = {} + +[modules.module-token.imports] +account-manager = "module-account/account-manager" + +[modules.module-token.init-config] +thread-pool-size = "@{{thread-pool-size}}" + +[modules.module-sorting.exports] +tx-sorter.tx-sorter = {} + +[modules.module-sorting.imports] +account-manager = "module-account/account-manager" + +[modules.module-sorting.init-config] +thread-pool-size = "@{{thread-pool-size}}" + +[modules.host] +imports = {} +exports = {} +init-config = {} + +[param-defaults] +thread-pool-size = "16"