From d9f5eee30a1d0d24f1b7fb4fee9ee6c1d37978d6 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Sat, 12 May 2018 22:51:58 +0200 Subject: [PATCH] [enh] #68 add module crate --- module/Cargo.toml | 16 +++ module/lib.rs | 274 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 module/Cargo.toml create mode 100644 module/lib.rs diff --git a/module/Cargo.toml b/module/Cargo.toml new file mode 100644 index 00000000..8646dbde --- /dev/null +++ b/module/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "duniter-module" +version = "0.1.0" +authors = ["librelois <elois@duniter.org>"] +description = "Modules model for the Duniter project." +license = "AGPL-3.0" + +[lib] +path = "lib.rs" + +[dependencies] +duniter-crypto = { path = "../crypto" } +duniter-documents = { path = "../documents" } +serde = "1.0.24" +serde_derive = "1.0.24" +serde_json = "1.0.9" \ No newline at end of file diff --git a/module/lib.rs b/module/lib.rs new file mode 100644 index 00000000..17162bc7 --- /dev/null +++ b/module/lib.rs @@ -0,0 +1,274 @@ +// Copyright (C) 2018 The Duniter Project Developers. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Defined the few global types used by all modules, +//! as well as the DuniterModule trait that all modules must implement. + +#![cfg_attr(feature = "strict", deny(warnings))] +#![deny( + missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts, + trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces, + unused_qualifications +)] + +extern crate duniter_crypto; +extern crate serde; +extern crate serde_json; + +use duniter_crypto::keys::{KeyPair, PublicKey}; +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use std::fmt::Debug; +use std::sync::mpsc; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Store Currency +pub enum Currency { + /// Currency in string format + Str(String), + /// Currency in binary format + Bin([u8; 2]), +} + +impl ToString for Currency { + fn to_string(&self) -> String { + match *self { + Currency::Str(ref currency_str) => currency_str.clone(), + Currency::Bin(_) => panic!("Currency binary format is not implemented !"), + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +/// Store module identifier +pub enum ModuleId { + /// Module in static str format because module name must be know at compile time + Str(&'static str), + /// Module in binary format + Bin([u8; 2]), +} + +impl ToString for ModuleId { + fn to_string(&self) -> String { + match *self { + ModuleId::Str(module_id_str) => String::from(module_id_str), + ModuleId::Bin(_) => panic!("ModuleId binary format is not implemented !"), + } + } +} + +impl Serialize for ModuleId { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let module_id_string = match *self { + ModuleId::Str(module_id_str) => String::from(module_id_str), + ModuleId::Bin(_) => panic!("ModuleId binary format is not implemented !"), + }; + serializer.serialize_str(module_id_string.as_str()) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +/// Identifier of an inter-module request +pub struct ModuleReqId(pub u32); + +impl Serialize for ModuleReqId { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + serializer.serialize_str(&format!("{:x}", self.0)) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +/// Several modules can simultaneously send requests with the same identifier. +/// To identify each request in a unique way, we must therefore also take into account the identifier of the module performing the request. +pub struct ModuleReqFullId(pub ModuleId, pub ModuleReqId); + +impl ToString for ModuleReqFullId { + fn to_string(&self) -> String { + format!("{}-{}", self.0.to_string(), (self.1).0) + } +} + +/*impl Serialize for ModuleReqFullId { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + serializer.serialize_str(&format!("{}-{}", self.0.to_string(), (self.1).0)) + } +}*/ + +#[derive(Debug, Clone, PartialEq)] +/// Duniter configuration v1 +pub struct DuniterConfV1 { + /// Name of datas folder in ~/.config/durs/ + pub profile: String, + /// Currency + pub currency: Currency, + /// Duniter node unique identifier + pub my_node_id: u32, + /// Configuration of modules in json format (obtained from the conf.json file) + pub modules: serde_json::Value, +} + +impl Serialize for DuniterConfV1 { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("DuniterConfV1", 3)?; + + // Currency + state.serialize_field("currency", self.currency.to_string().as_str())?; + + // Node id + state.serialize_field("node_id", &format!("{:x}", self.my_node_id))?; + + // Modules + state.serialize_field("modules", &self.modules)?; + + // End + state.end() + } +} + +#[derive(Debug, Clone, PartialEq)] +/// Duniter node configuration +pub enum DuniterConf { + /// Duniter node configuration v1 + V1(DuniterConfV1), + /// Duniter node configuration v2 + V2(), +} + +impl DuniterConf { + /// Get profile + pub fn profile(&self) -> String { + match *self { + DuniterConf::V1(ref conf_v1) => conf_v1.profile.clone(), + _ => panic!("Fail to load duniter conf : conf version not supported !"), + } + } + /// Get currency + pub fn currency(&self) -> Currency { + match *self { + DuniterConf::V1(ref conf_v1) => conf_v1.currency.clone(), + _ => panic!("Fail to load duniter conf : conf version not supported !"), + } + } + /// Get node id + pub fn my_node_id(&self) -> u32 { + match *self { + DuniterConf::V1(ref conf_v1) => conf_v1.my_node_id, + _ => panic!("Fail to load duniter conf : conf version not supported !"), + } + } + /// Get modules conf + pub fn modules(&self) -> serde_json::Value { + match *self { + DuniterConf::V1(ref conf_v1) => conf_v1.modules.clone(), + _ => panic!("Fail to load duniter conf : conf version not supported !"), + } + } +} + +/// The different modules of Duniter-rs can exchange messages with the type of their choice, +/// provided that this type implements the ModuleMessage trait. +pub trait ModuleMessage: Debug + Clone {} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +/// Type returned by module initialization function +pub enum ModuleInitError { + /// Fail to load configuration + FailToLoadConf(), + /// Unknow error + UnknowError(), +} + +#[derive(Debug, Clone)] +/// Type sent by each module to the rooter during initialization +pub enum RooterThreadMessage<M: ModuleMessage> { + /// Channel on which the module listens + ModuleSender(mpsc::Sender<M>), + /// When the number of plugged modules is known, the rooter thread must be informed of the number of modules it must connect between them. + ModulesCount(usize), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +/// Indicates which keys the module needs to operate +pub enum RequiredKeys { + /// The module needs the member keypair (private key included). + MemberKeyPair(), + /// The module only needs the member public key. + MemberPublicKey(), + /// The module needs the network keypair (private key included). + NetworkKeyPair(), + /// The module only needs the network public key. + NetworkPublicKey(), + /// The module does not need any key + None(), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +/// Contains the keys the module needs +pub enum RequiredKeysContent<P: PublicKey, K: KeyPair> { + /// Contains the member keypair (private key included). + MemberKeyPair(Option<K>), + /// Contains the member public key. + MemberPublicKey(Option<P>), + /// Contains the network keypair (private key included). + NetworkKeyPair(K), + /// Contains the network public key. + NetworkPublicKey(P), + /// Does not contain any keys + None(), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +/// Defined the priority level of the module +pub enum ModulePriority { + /// This module is necessary for Duniter-Rs to work properly, impossible to disable it. + Essential(), + /// This module is recommended but it's not essential, it's enabled by default but can be disabled by the user. + Recommended(), + /// This module is disabled by default, it must be explicitly enabled by the user. + Optional(), +} + +/// All Duniter-rs modules must implement this trait. +pub trait DuniterModule<P: PublicKey, K: KeyPair, M: ModuleMessage> { + /// Returns the module identifier + fn id() -> ModuleId; + /// Returns the module priority + fn priority() -> ModulePriority; + /// Indicates which keys the module needs + fn ask_required_keys() -> RequiredKeys; + /// Provides the default module configuration + fn default_conf() -> serde_json::Value; + /// Launch the module + fn start( + soft_name: &str, + soft_version: &str, + keys: RequiredKeysContent<P, K>, + conf: &DuniterConf, + module_conf: &serde_json::Value, + main_sender: mpsc::Sender<RooterThreadMessage<M>>, + load_conf_only: bool, + ) -> Result<(), ModuleInitError>; +} -- GitLab